1 /*
2 * Copyright (c) 2024, sakumisu
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "usbh_core.h"
7 #include "usbh_cp210x.h"
8
9 #define DEV_FORMAT "/dev/ttyUSB%d"
10
11 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cp210x_buf[USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
12
13 #define CONFIG_USBHOST_MAX_CP210X_CLASS 1
14
15 static struct usbh_cp210x g_cp210x_class[CONFIG_USBHOST_MAX_CP210X_CLASS];
16 static uint32_t g_devinuse = 0;
17
usbh_cp210x_class_alloc(void)18 static struct usbh_cp210x *usbh_cp210x_class_alloc(void)
19 {
20 uint8_t devno;
21
22 for (devno = 0; devno < CONFIG_USBHOST_MAX_CP210X_CLASS; devno++) {
23 if ((g_devinuse & (1U << devno)) == 0) {
24 g_devinuse |= (1U << devno);
25 memset(&g_cp210x_class[devno], 0, sizeof(struct usbh_cp210x));
26 g_cp210x_class[devno].minor = devno;
27 return &g_cp210x_class[devno];
28 }
29 }
30 return NULL;
31 }
32
usbh_cp210x_class_free(struct usbh_cp210x * cp210x_class)33 static void usbh_cp210x_class_free(struct usbh_cp210x *cp210x_class)
34 {
35 uint8_t devno = cp210x_class->minor;
36
37 if (devno < 32) {
38 g_devinuse &= ~(1U << devno);
39 }
40 memset(cp210x_class, 0, sizeof(struct usbh_cp210x));
41 }
42
usbh_cp210x_enable(struct usbh_cp210x * cp210x_class)43 static int usbh_cp210x_enable(struct usbh_cp210x *cp210x_class)
44 {
45 struct usb_setup_packet *setup;
46
47 if (!cp210x_class || !cp210x_class->hport) {
48 return -USB_ERR_INVAL;
49 }
50 setup = cp210x_class->hport->setup;
51
52 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
53 setup->bRequest = CP210X_IFC_ENABLE;
54 setup->wValue = 1;
55 setup->wIndex = cp210x_class->intf;
56 setup->wLength = 0;
57
58 return usbh_control_transfer(cp210x_class->hport, setup, NULL);
59 }
60
usbh_cp210x_set_flow(struct usbh_cp210x * cp210x_class)61 static int usbh_cp210x_set_flow(struct usbh_cp210x *cp210x_class)
62 {
63 struct usb_setup_packet *setup;
64
65 if (!cp210x_class || !cp210x_class->hport) {
66 return -USB_ERR_INVAL;
67 }
68 setup = cp210x_class->hport->setup;
69
70 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
71 setup->bRequest = CP210X_SET_FLOW;
72 setup->wValue = 0;
73 setup->wIndex = cp210x_class->intf;
74 setup->wLength = 16;
75
76 memset(g_cp210x_buf, 0, 16);
77 g_cp210x_buf[13] = 0x20;
78 return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
79 }
80
usbh_cp210x_set_chars(struct usbh_cp210x * cp210x_class)81 static int usbh_cp210x_set_chars(struct usbh_cp210x *cp210x_class)
82 {
83 struct usb_setup_packet *setup;
84
85 if (!cp210x_class || !cp210x_class->hport) {
86 return -USB_ERR_INVAL;
87 }
88 setup = cp210x_class->hport->setup;
89
90 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
91 setup->bRequest = CP210X_SET_CHARS;
92 setup->wValue = 0;
93 setup->wIndex = cp210x_class->intf;
94 setup->wLength = 6;
95
96 memset(g_cp210x_buf, 0, 6);
97 g_cp210x_buf[0] = 0x80;
98 g_cp210x_buf[4] = 0x88;
99 g_cp210x_buf[5] = 0x28;
100 return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
101 }
102
usbh_cp210x_set_baudrate(struct usbh_cp210x * cp210x_class,uint32_t baudrate)103 static int usbh_cp210x_set_baudrate(struct usbh_cp210x *cp210x_class, uint32_t baudrate)
104 {
105 struct usb_setup_packet *setup;
106
107 if (!cp210x_class || !cp210x_class->hport) {
108 return -USB_ERR_INVAL;
109 }
110 setup = cp210x_class->hport->setup;
111
112 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
113 setup->bRequest = CP210X_SET_BAUDRATE;
114 setup->wValue = 0;
115 setup->wIndex = cp210x_class->intf;
116 setup->wLength = 4;
117
118 memcpy(g_cp210x_buf, (uint8_t *)&baudrate, 4);
119 return usbh_control_transfer(cp210x_class->hport, setup, g_cp210x_buf);
120 }
121
usbh_cp210x_set_data_format(struct usbh_cp210x * cp210x_class,uint8_t databits,uint8_t parity,uint8_t stopbits)122 static int usbh_cp210x_set_data_format(struct usbh_cp210x *cp210x_class, uint8_t databits, uint8_t parity, uint8_t stopbits)
123 {
124 struct usb_setup_packet *setup;
125 uint16_t value;
126
127 if (!cp210x_class || !cp210x_class->hport) {
128 return -USB_ERR_INVAL;
129 }
130 setup = cp210x_class->hport->setup;
131
132 value = ((databits & 0x0F) << 8) | ((parity & 0x0f) << 4) | ((stopbits & 0x03) << 0);
133
134 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
135 setup->bRequest = CP210X_SET_LINE_CTL;
136 setup->wValue = value;
137 setup->wIndex = cp210x_class->intf;
138 setup->wLength = 0;
139
140 return usbh_control_transfer(cp210x_class->hport, setup, NULL);
141 }
142
usbh_cp210x_set_mhs(struct usbh_cp210x * cp210x_class,uint8_t dtr,uint8_t rts,uint8_t dtr_mask,uint8_t rts_mask)143 static int usbh_cp210x_set_mhs(struct usbh_cp210x *cp210x_class, uint8_t dtr, uint8_t rts, uint8_t dtr_mask, uint8_t rts_mask)
144 {
145 struct usb_setup_packet *setup;
146 uint16_t value;
147
148 if (!cp210x_class || !cp210x_class->hport) {
149 return -USB_ERR_INVAL;
150 }
151 setup = cp210x_class->hport->setup;
152
153 value = ((dtr & 0x01) << 0) | ((rts & 0x01) << 1) | ((dtr_mask & 0x01) << 8) | ((rts_mask & 0x01) << 9);
154
155 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
156 setup->bRequest = CP210X_SET_MHS;
157 setup->wValue = value;
158 setup->wIndex = cp210x_class->intf;
159 setup->wLength = 0;
160
161 return usbh_control_transfer(cp210x_class->hport, setup, NULL);
162 }
163
usbh_cp210x_set_line_coding(struct usbh_cp210x * cp210x_class,struct cdc_line_coding * line_coding)164 int usbh_cp210x_set_line_coding(struct usbh_cp210x *cp210x_class, struct cdc_line_coding *line_coding)
165 {
166 memcpy((uint8_t *)&cp210x_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
167 usbh_cp210x_set_baudrate(cp210x_class, line_coding->dwDTERate);
168 return usbh_cp210x_set_data_format(cp210x_class, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat);
169 }
170
usbh_cp210x_get_line_coding(struct usbh_cp210x * cp210x_class,struct cdc_line_coding * line_coding)171 int usbh_cp210x_get_line_coding(struct usbh_cp210x *cp210x_class, struct cdc_line_coding *line_coding)
172 {
173 memcpy(line_coding, (uint8_t *)&cp210x_class->line_coding, sizeof(struct cdc_line_coding));
174 return 0;
175 }
176
usbh_cp210x_set_line_state(struct usbh_cp210x * cp210x_class,bool dtr,bool rts)177 int usbh_cp210x_set_line_state(struct usbh_cp210x *cp210x_class, bool dtr, bool rts)
178 {
179 return usbh_cp210x_set_mhs(cp210x_class, dtr, rts, 1, 1);
180 }
181
usbh_cp210x_connect(struct usbh_hubport * hport,uint8_t intf)182 static int usbh_cp210x_connect(struct usbh_hubport *hport, uint8_t intf)
183 {
184 struct usb_endpoint_descriptor *ep_desc;
185 int ret = 0;
186
187 struct usbh_cp210x *cp210x_class = usbh_cp210x_class_alloc();
188 if (cp210x_class == NULL) {
189 USB_LOG_ERR("Fail to alloc cp210x_class\r\n");
190 return -USB_ERR_NOMEM;
191 }
192
193 cp210x_class->hport = hport;
194 cp210x_class->intf = intf;
195
196 hport->config.intf[intf].priv = cp210x_class;
197
198 usbh_cp210x_enable(cp210x_class);
199 usbh_cp210x_set_flow(cp210x_class);
200 usbh_cp210x_set_chars(cp210x_class);
201
202 for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
203 ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
204
205 if (ep_desc->bEndpointAddress & 0x80) {
206 USBH_EP_INIT(cp210x_class->bulkin, ep_desc);
207 } else {
208 USBH_EP_INIT(cp210x_class->bulkout, ep_desc);
209 }
210 }
211
212 snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, cp210x_class->minor);
213
214 USB_LOG_INFO("Register CP210X Class:%s\r\n", hport->config.intf[intf].devname);
215
216 #if 0
217 USB_LOG_INFO("Test cp2102 rx and tx and rx for 5 times, baudrate is 115200\r\n");
218
219 struct cdc_line_coding linecoding;
220 uint8_t count = 5;
221
222 linecoding.dwDTERate = 115200;
223 linecoding.bDataBits = 8;
224 linecoding.bParityType = 0;
225 linecoding.bCharFormat = 0;
226 usbh_cp210x_set_line_coding(cp210x_class, &linecoding);
227 usbh_cp210x_set_line_state(cp210x_class, true, false);
228
229 memset(g_cp210x_buf, 'a', sizeof(g_cp210x_buf));
230 ret = usbh_cp210x_bulk_out_transfer(cp210x_class, g_cp210x_buf, sizeof(g_cp210x_buf), 0xfffffff);
231 USB_LOG_RAW("out ret:%d\r\n", ret);
232 while (count--) {
233 ret = usbh_cp210x_bulk_in_transfer(cp210x_class, g_cp210x_buf, sizeof(g_cp210x_buf), 0xfffffff);
234 USB_LOG_RAW("in ret:%d\r\n", ret);
235 if (ret > 0) {
236 for (uint32_t i = 0; i < ret; i++) {
237 USB_LOG_RAW("%02x ", g_cp210x_buf[i]);
238 }
239 USB_LOG_RAW("\r\n");
240 }
241 }
242 #endif
243 usbh_cp210x_run(cp210x_class);
244 return ret;
245 }
246
usbh_cp210x_disconnect(struct usbh_hubport * hport,uint8_t intf)247 static int usbh_cp210x_disconnect(struct usbh_hubport *hport, uint8_t intf)
248 {
249 int ret = 0;
250
251 struct usbh_cp210x *cp210x_class = (struct usbh_cp210x *)hport->config.intf[intf].priv;
252
253 if (cp210x_class) {
254 if (cp210x_class->bulkin) {
255 usbh_kill_urb(&cp210x_class->bulkin_urb);
256 }
257
258 if (cp210x_class->bulkout) {
259 usbh_kill_urb(&cp210x_class->bulkout_urb);
260 }
261
262 if (hport->config.intf[intf].devname[0] != '\0') {
263 usb_osal_thread_schedule_other();
264 USB_LOG_INFO("Unregister CP210X Class:%s\r\n", hport->config.intf[intf].devname);
265 usbh_cp210x_stop(cp210x_class);
266 }
267
268 usbh_cp210x_class_free(cp210x_class);
269 }
270
271 return ret;
272 }
273
usbh_cp210x_bulk_in_transfer(struct usbh_cp210x * cp210x_class,uint8_t * buffer,uint32_t buflen,uint32_t timeout)274 int usbh_cp210x_bulk_in_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
275 {
276 int ret;
277 struct usbh_urb *urb = &cp210x_class->bulkin_urb;
278
279 usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkin, buffer, buflen, timeout, NULL, NULL);
280 ret = usbh_submit_urb(urb);
281 if (ret == 0) {
282 ret = urb->actual_length;
283 }
284 return ret;
285 }
286
usbh_cp210x_bulk_out_transfer(struct usbh_cp210x * cp210x_class,uint8_t * buffer,uint32_t buflen,uint32_t timeout)287 int usbh_cp210x_bulk_out_transfer(struct usbh_cp210x *cp210x_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
288 {
289 int ret;
290 struct usbh_urb *urb = &cp210x_class->bulkout_urb;
291
292 usbh_bulk_urb_fill(urb, cp210x_class->hport, cp210x_class->bulkout, buffer, buflen, timeout, NULL, NULL);
293 ret = usbh_submit_urb(urb);
294 if (ret == 0) {
295 ret = urb->actual_length;
296 }
297 return ret;
298 }
299
usbh_cp210x_run(struct usbh_cp210x * cp210x_class)300 __WEAK void usbh_cp210x_run(struct usbh_cp210x *cp210x_class)
301 {
302 (void)cp210x_class;
303 }
304
usbh_cp210x_stop(struct usbh_cp210x * cp210x_class)305 __WEAK void usbh_cp210x_stop(struct usbh_cp210x *cp210x_class)
306 {
307 (void)cp210x_class;
308 }
309
310 static const uint16_t cp210x_id_table[][2] = {
311 { 0x10C4, 0xEA60 },
312 { 0, 0 },
313 };
314
315 const struct usbh_class_driver cp210x_class_driver = {
316 .driver_name = "cp210x",
317 .connect = usbh_cp210x_connect,
318 .disconnect = usbh_cp210x_disconnect
319 };
320
321 CLASS_INFO_DEFINE const struct usbh_class_info cp210x_class_info = {
322 .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
323 .bInterfaceClass = 0xff,
324 .bInterfaceSubClass = 0x00,
325 .bInterfaceProtocol = 0x00,
326 .id_table = cp210x_id_table,
327 .class_driver = &cp210x_class_driver
328 };