1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/sys/util.h>
8 #include <zephyr/drivers/usb/udc.h>
9 #include <zephyr/usb/usbd.h>
10 
11 #include "usbd_device.h"
12 #include "usbd_class.h"
13 #include "usbd_ch9.h"
14 #include "usbd_desc.h"
15 #include "usbd_endpoint.h"
16 
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(usbd_ep, CONFIG_USBD_LOG_LEVEL);
19 
usbd_ep_enable(const struct device * dev,const struct usb_ep_descriptor * const ed,uint32_t * const ep_bm)20 int usbd_ep_enable(const struct device *dev,
21 		   const struct usb_ep_descriptor *const ed,
22 		   uint32_t *const ep_bm)
23 {
24 	int ret;
25 
26 	ret = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes,
27 			    ed->wMaxPacketSize, ed->bInterval);
28 	if (ret == 0) {
29 		usbd_ep_bm_set(ep_bm, ed->bEndpointAddress);
30 	}
31 
32 	return ret;
33 }
34 
usbd_ep_disable(const struct device * dev,const uint8_t ep,uint32_t * const ep_bm)35 int usbd_ep_disable(const struct device *dev,
36 		    const uint8_t ep,
37 		    uint32_t *const ep_bm)
38 {
39 	int ret;
40 
41 	ret = udc_ep_disable(dev, ep);
42 	if (ret) {
43 		return ret;
44 	}
45 
46 	usbd_ep_bm_clear(ep_bm, ep);
47 
48 	ret = udc_ep_dequeue(dev, ep);
49 	if (ret) {
50 		return ret;
51 	}
52 
53 	k_yield();
54 
55 	return ret;
56 }
57 
usbd_ep_ctrl_set_zlp(struct usbd_contex * const uds_ctx,struct net_buf * const buf)58 static void usbd_ep_ctrl_set_zlp(struct usbd_contex *const uds_ctx,
59 				 struct net_buf *const buf)
60 {
61 	struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
62 	size_t min_len = MIN(setup->wLength, buf->len);
63 
64 	if (buf->len == 0) {
65 		return;
66 	}
67 
68 	/*
69 	 * Set ZLP flag when host asks for a bigger length and the
70 	 * last chunk is wMaxPacketSize long, to indicate the last
71 	 * packet.
72 	 */
73 	if (setup->wLength > min_len && !(min_len % USB_CONTROL_EP_MPS)) {
74 		/*
75 		 * Transfer length is less as requested by wLength and
76 		 * is multiple of wMaxPacketSize.
77 		 */
78 		LOG_DBG("add ZLP, wLength %u buf length %zu",
79 			setup->wLength, min_len);
80 		udc_ep_buf_set_zlp(buf);
81 
82 	}
83 }
84 
85 /*
86  * All the functions below are part of public USB device support API.
87  */
88 
usbd_ep_ctrl_buf_alloc(struct usbd_contex * const uds_ctx,const uint8_t ep,const size_t size)89 struct net_buf *usbd_ep_ctrl_buf_alloc(struct usbd_contex *const uds_ctx,
90 				       const uint8_t ep, const size_t size)
91 {
92 	if (USB_EP_GET_IDX(ep)) {
93 		/* Not a control endpoint */
94 		return NULL;
95 	}
96 
97 	return udc_ep_buf_alloc(uds_ctx->dev, ep, size);
98 }
99 
usbd_ep_ctrl_enqueue(struct usbd_contex * const uds_ctx,struct net_buf * const buf)100 int usbd_ep_ctrl_enqueue(struct usbd_contex *const uds_ctx,
101 			 struct net_buf *const buf)
102 {
103 	struct udc_buf_info *bi;
104 
105 	bi = udc_get_buf_info(buf);
106 	if (USB_EP_GET_IDX(bi->ep)) {
107 		/* Not a control endpoint */
108 		return -EINVAL;
109 	}
110 
111 	if (USB_EP_DIR_IS_IN(bi->ep)) {
112 		if (usbd_is_suspended(uds_ctx)) {
113 			LOG_ERR("device is suspended");
114 			return -EPERM;
115 		}
116 
117 		usbd_ep_ctrl_set_zlp(uds_ctx, buf);
118 	}
119 
120 	return udc_ep_enqueue(uds_ctx->dev, buf);
121 }
122 
usbd_ep_buf_alloc(const struct usbd_class_node * const c_nd,const uint8_t ep,const size_t size)123 struct net_buf *usbd_ep_buf_alloc(const struct usbd_class_node *const c_nd,
124 				  const uint8_t ep, const size_t size)
125 {
126 	struct usbd_contex *uds_ctx = c_nd->data->uds_ctx;
127 
128 	return udc_ep_buf_alloc(uds_ctx->dev, ep, size);
129 }
130 
usbd_ep_enqueue(const struct usbd_class_node * const c_nd,struct net_buf * const buf)131 int usbd_ep_enqueue(const struct usbd_class_node *const c_nd,
132 		    struct net_buf *const buf)
133 {
134 	struct usbd_contex *uds_ctx = c_nd->data->uds_ctx;
135 	struct udc_buf_info *bi = udc_get_buf_info(buf);
136 
137 	if (USB_EP_DIR_IS_IN(bi->ep)) {
138 		if (usbd_is_suspended(uds_ctx)) {
139 			return -EPERM;
140 		}
141 	}
142 
143 	bi->owner = (void *)c_nd;
144 
145 	return udc_ep_enqueue(uds_ctx->dev, buf);
146 }
147 
usbd_ep_buf_free(struct usbd_contex * const uds_ctx,struct net_buf * buf)148 int usbd_ep_buf_free(struct usbd_contex *const uds_ctx, struct net_buf *buf)
149 {
150 	return udc_ep_buf_free(uds_ctx->dev, buf);
151 }
152 
usbd_ep_dequeue(struct usbd_contex * const uds_ctx,const uint8_t ep)153 int usbd_ep_dequeue(struct usbd_contex *const uds_ctx, const uint8_t ep)
154 {
155 	return udc_ep_dequeue(uds_ctx->dev, ep);
156 }
157 
usbd_ep_set_halt(struct usbd_contex * const uds_ctx,const uint8_t ep)158 int usbd_ep_set_halt(struct usbd_contex *const uds_ctx, const uint8_t ep)
159 {
160 	struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data;
161 	int ret;
162 
163 	ret = udc_ep_set_halt(uds_ctx->dev, ep);
164 	if (ret) {
165 		LOG_WRN("Set halt 0x%02x failed", ep);
166 		return ret;
167 	}
168 
169 	usbd_ep_bm_set(&ch9_data->ep_halt, ep);
170 
171 	return ret;
172 }
173 
usbd_ep_clear_halt(struct usbd_contex * const uds_ctx,const uint8_t ep)174 int usbd_ep_clear_halt(struct usbd_contex *const uds_ctx, const uint8_t ep)
175 {
176 	struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data;
177 	int ret;
178 
179 	ret = udc_ep_clear_halt(uds_ctx->dev, ep);
180 	if (ret) {
181 		LOG_WRN("Clear halt 0x%02x failed", ep);
182 		return ret;
183 	}
184 
185 	usbd_ep_bm_clear(&ch9_data->ep_halt, ep);
186 
187 	return ret;
188 }
189 
usbd_ep_is_halted(struct usbd_contex * const uds_ctx,const uint8_t ep)190 bool usbd_ep_is_halted(struct usbd_contex *const uds_ctx, const uint8_t ep)
191 {
192 	struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data;
193 
194 	return usbd_ep_bm_is_set(&ch9_data->ep_halt, ep);
195 }
196