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 sys_le16_to_cpu(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_context * const uds_ctx,struct net_buf * const buf)58 static void usbd_ep_ctrl_set_zlp(struct usbd_context *const uds_ctx,
59 struct net_buf *const buf)
60 {
61 struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
62 struct usb_device_descriptor *desc = uds_ctx->fs_desc;
63 size_t min_len = MIN(setup->wLength, buf->len);
64 uint8_t mps0 = 0;
65
66 switch (usbd_bus_speed(uds_ctx)) {
67 case USBD_SPEED_FS:
68 mps0 = desc->bMaxPacketSize0;
69 break;
70 case USBD_SPEED_HS:
71 mps0 = USB_CONTROL_EP_MPS;
72 break;
73 default:
74 __ASSERT(false, "Cannot determine bMaxPacketSize0 (unsupported speed)");
75 }
76
77 if (buf->len == 0) {
78 return;
79 }
80
81 /*
82 * Set ZLP flag when host asks for a bigger length and the
83 * last chunk is wMaxPacketSize long, to indicate the last
84 * packet.
85 */
86 if (setup->wLength > min_len && !(min_len % mps0)) {
87 /*
88 * Transfer length is less as requested by wLength and
89 * is multiple of wMaxPacketSize.
90 */
91 LOG_DBG("add ZLP, wLength %u buf length %zu",
92 setup->wLength, min_len);
93 udc_ep_buf_set_zlp(buf);
94
95 }
96 }
97
98 /*
99 * All the functions below are part of public USB device support API.
100 */
101
usbd_ep_ctrl_enqueue(struct usbd_context * const uds_ctx,struct net_buf * const buf)102 int usbd_ep_ctrl_enqueue(struct usbd_context *const uds_ctx,
103 struct net_buf *const buf)
104 {
105 struct udc_buf_info *bi;
106
107 bi = udc_get_buf_info(buf);
108 if (USB_EP_GET_IDX(bi->ep)) {
109 /* Not a control endpoint */
110 return -EINVAL;
111 }
112
113 if (USB_EP_DIR_IS_IN(bi->ep)) {
114 if (usbd_is_suspended(uds_ctx)) {
115 LOG_ERR("device is suspended");
116 return -EPERM;
117 }
118
119 usbd_ep_ctrl_set_zlp(uds_ctx, buf);
120 }
121
122 return udc_ep_enqueue(uds_ctx->dev, buf);
123 }
124
usbd_ep_buf_alloc(const struct usbd_class_data * const c_data,const uint8_t ep,const size_t size)125 struct net_buf *usbd_ep_buf_alloc(const struct usbd_class_data *const c_data,
126 const uint8_t ep, const size_t size)
127 {
128 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
129
130 return udc_ep_buf_alloc(uds_ctx->dev, ep, size);
131 }
132
usbd_ep_enqueue(const struct usbd_class_data * const c_data,struct net_buf * const buf)133 int usbd_ep_enqueue(const struct usbd_class_data *const c_data,
134 struct net_buf *const buf)
135 {
136 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
137 struct udc_buf_info *bi = udc_get_buf_info(buf);
138
139 if (USB_EP_DIR_IS_IN(bi->ep)) {
140 if (usbd_is_suspended(uds_ctx)) {
141 return -EPERM;
142 }
143 }
144
145 bi->owner = (void *)c_data;
146
147 return udc_ep_enqueue(uds_ctx->dev, buf);
148 }
149
usbd_ep_buf_free(struct usbd_context * const uds_ctx,struct net_buf * buf)150 int usbd_ep_buf_free(struct usbd_context *const uds_ctx, struct net_buf *buf)
151 {
152 return udc_ep_buf_free(uds_ctx->dev, buf);
153 }
154
usbd_ep_dequeue(struct usbd_context * const uds_ctx,const uint8_t ep)155 int usbd_ep_dequeue(struct usbd_context *const uds_ctx, const uint8_t ep)
156 {
157 return udc_ep_dequeue(uds_ctx->dev, ep);
158 }
159
usbd_ep_set_halt(struct usbd_context * const uds_ctx,const uint8_t ep)160 int usbd_ep_set_halt(struct usbd_context *const uds_ctx, const uint8_t ep)
161 {
162 struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data;
163 int ret;
164
165 ret = udc_ep_set_halt(uds_ctx->dev, ep);
166 if (ret) {
167 LOG_WRN("Set halt 0x%02x failed", ep);
168 return ret;
169 }
170
171 usbd_ep_bm_set(&ch9_data->ep_halt, ep);
172
173 return ret;
174 }
175
usbd_ep_clear_halt(struct usbd_context * const uds_ctx,const uint8_t ep)176 int usbd_ep_clear_halt(struct usbd_context *const uds_ctx, const uint8_t ep)
177 {
178 struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data;
179 int ret;
180
181 ret = udc_ep_clear_halt(uds_ctx->dev, ep);
182 if (ret) {
183 LOG_WRN("Clear halt 0x%02x failed", ep);
184 return ret;
185 }
186
187 usbd_ep_bm_clear(&ch9_data->ep_halt, ep);
188
189 return ret;
190 }
191
usbd_ep_is_halted(struct usbd_context * const uds_ctx,const uint8_t ep)192 bool usbd_ep_is_halted(struct usbd_context *const uds_ctx, const uint8_t ep)
193 {
194 struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data;
195
196 return usbd_ep_bm_is_set(&ch9_data->ep_halt, ep);
197 }
198