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 };