1 /*
2  * Copyright (c) 2024, sakumisu
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "usbh_core.h"
7 #include "usbh_ftdi.h"
8 
9 #define DEV_FORMAT "/dev/ttyUSB%d"
10 
11 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_ftdi_buf[USB_ALIGN_UP(64, CONFIG_USB_ALIGN_SIZE)];
12 
13 #define CONFIG_USBHOST_MAX_FTDI_CLASS 1
14 
15 static struct usbh_ftdi g_ftdi_class[CONFIG_USBHOST_MAX_FTDI_CLASS];
16 static uint32_t g_devinuse = 0;
17 
18 static const char *ftdi_chip_name[] = {
19     [SIO] = "SIO", /* the serial part of FT8U100AX */
20     [FT232A] = "FT232A",
21     [FT232B] = "FT232B",
22     [FT2232C] = "FT2232C/D",
23     [FT232R] = "FT232R",
24     [FT232H] = "FT232H",
25     [FT2232H] = "FT2232H",
26     [FT4232H] = "FT4232H",
27     [FT4232HA] = "FT4232HA",
28     [FT232HP] = "FT232HP",
29     [FT233HP] = "FT233HP",
30     [FT2232HP] = "FT2232HP",
31     [FT2233HP] = "FT2233HP",
32     [FT4232HP] = "FT4232HP",
33     [FT4233HP] = "FT4233HP",
34     [FTX] = "FT-X",
35 };
36 
usbh_ftdi_class_alloc(void)37 static struct usbh_ftdi *usbh_ftdi_class_alloc(void)
38 {
39     uint8_t devno;
40 
41     for (devno = 0; devno < CONFIG_USBHOST_MAX_FTDI_CLASS; devno++) {
42         if ((g_devinuse & (1U << devno)) == 0) {
43             g_devinuse |= (1U << devno);
44             memset(&g_ftdi_class[devno], 0, sizeof(struct usbh_ftdi));
45             g_ftdi_class[devno].minor = devno;
46             return &g_ftdi_class[devno];
47         }
48     }
49     return NULL;
50 }
51 
usbh_ftdi_class_free(struct usbh_ftdi * ftdi_class)52 static void usbh_ftdi_class_free(struct usbh_ftdi *ftdi_class)
53 {
54     uint8_t devno = ftdi_class->minor;
55 
56     if (devno < 32) {
57         g_devinuse &= ~(1U << devno);
58     }
59     memset(ftdi_class, 0, sizeof(struct usbh_ftdi));
60 }
61 
62 /*
63  * Divide positive or negative dividend by positive or negative divisor
64  * and round to closest integer. Result is undefined for negative
65  * divisors if the dividend variable type is unsigned and for negative
66  * dividends if the divisor variable type is unsigned.
67  */
68 #define DIV_ROUND_CLOSEST(x, divisor) (       \
69     {                                         \
70         typeof(x) __x = x;                    \
71         typeof(divisor) __d = divisor;        \
72         (((typeof(x))-1) > 0 ||               \
73          ((typeof(divisor))-1) > 0 ||         \
74          (((__x) > 0) == ((__d) > 0))) ?      \
75             (((__x) + ((__d) / 2)) / (__d)) : \
76             (((__x) - ((__d) / 2)) / (__d));  \
77     })
78 
ftdi_232bm_baud_base_to_divisor(uint32_t baud,int base)79 static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, int base)
80 {
81     static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
82     uint32_t divisor;
83     /* divisor shifted 3 bits to the left */
84     int divisor3 = DIV_ROUND_CLOSEST(base, 2 * baud);
85     divisor = divisor3 >> 3;
86     divisor |= (uint32_t)divfrac[divisor3 & 0x7] << 14;
87     /* Deal with special cases for highest baud rates. */
88     if (divisor == 1) /* 1.0 */
89         divisor = 0;
90     else if (divisor == 0x4001) /* 1.5 */
91         divisor = 1;
92     return divisor;
93 }
94 
ftdi_232bm_baud_to_divisor(uint32_t baud)95 static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud)
96 {
97     return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
98 }
99 
ftdi_2232h_baud_base_to_divisor(uint32_t baud,int base)100 static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, int base)
101 {
102     static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
103     uint32_t divisor;
104     int divisor3;
105 
106     /* hi-speed baud rate is 10-bit sampling instead of 16-bit */
107     divisor3 = DIV_ROUND_CLOSEST(8 * base, 10 * baud);
108 
109     divisor = divisor3 >> 3;
110     divisor |= (uint32_t)divfrac[divisor3 & 0x7] << 14;
111     /* Deal with special cases for highest baud rates. */
112     if (divisor == 1) /* 1.0 */
113         divisor = 0;
114     else if (divisor == 0x4001) /* 1.5 */
115         divisor = 1;
116     /*
117 	 * Set this bit to turn off a divide by 2.5 on baud rate generator
118 	 * This enables baud rates up to 12Mbaud but cannot reach below 1200
119 	 * baud with this bit set
120 	 */
121     divisor |= 0x00020000;
122     return divisor;
123 }
124 
ftdi_2232h_baud_to_divisor(uint32_t baud)125 static uint32_t ftdi_2232h_baud_to_divisor(uint32_t baud)
126 {
127     return ftdi_2232h_baud_base_to_divisor(baud, 120000000);
128 }
129 
usbh_ftdi_reset(struct usbh_ftdi * ftdi_class)130 int usbh_ftdi_reset(struct usbh_ftdi *ftdi_class)
131 {
132     struct usb_setup_packet *setup;
133 
134     if (!ftdi_class || !ftdi_class->hport) {
135         return -USB_ERR_INVAL;
136     }
137     setup = ftdi_class->hport->setup;
138 
139     setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
140     setup->bRequest = SIO_RESET_REQUEST;
141     setup->wValue = 0;
142     setup->wIndex = ftdi_class->intf;
143     setup->wLength = 0;
144 
145     return usbh_control_transfer(ftdi_class->hport, setup, NULL);
146 }
147 
usbh_ftdi_set_modem(struct usbh_ftdi * ftdi_class,uint16_t value)148 static int usbh_ftdi_set_modem(struct usbh_ftdi *ftdi_class, uint16_t value)
149 {
150     struct usb_setup_packet *setup;
151 
152     if (!ftdi_class || !ftdi_class->hport) {
153         return -USB_ERR_INVAL;
154     }
155     setup = ftdi_class->hport->setup;
156 
157     setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
158     setup->bRequest = SIO_SET_MODEM_CTRL_REQUEST;
159     setup->wValue = value;
160     setup->wIndex = ftdi_class->intf;
161     setup->wLength = 0;
162 
163     return usbh_control_transfer(ftdi_class->hport, setup, NULL);
164 }
165 
usbh_ftdi_set_baudrate(struct usbh_ftdi * ftdi_class,uint32_t baudrate)166 static int usbh_ftdi_set_baudrate(struct usbh_ftdi *ftdi_class, uint32_t baudrate)
167 {
168     struct usb_setup_packet *setup;
169     uint32_t div_value;
170     uint16_t value;
171     uint8_t baudrate_high;
172 
173     if (!ftdi_class || !ftdi_class->hport) {
174         return -USB_ERR_INVAL;
175     }
176     setup = ftdi_class->hport->setup;
177 
178     switch (ftdi_class->chip_type) {
179         case FT232B:
180         case FT2232C:
181         case FT232R:
182             if (baudrate > 3000000) {
183                 return -USB_ERR_INVAL;
184             }
185             div_value = ftdi_232bm_baud_to_divisor(baudrate);
186             break;
187         default:
188             if ((baudrate <= 12000000) && (baudrate >= 1200)) {
189                 div_value = ftdi_2232h_baud_to_divisor(baudrate);
190             } else {
191                 return -USB_ERR_INVAL;
192             }
193             break;
194     }
195 
196     value = div_value & 0xFFFF;
197     baudrate_high = (div_value >> 16) & 0xff;
198 
199     setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
200     setup->bRequest = SIO_SET_BAUDRATE_REQUEST;
201     setup->wValue = value;
202     setup->wIndex = (baudrate_high << 8) | ftdi_class->intf;
203     setup->wLength = 0;
204 
205     return usbh_control_transfer(ftdi_class->hport, setup, NULL);
206 }
207 
usbh_ftdi_set_data_format(struct usbh_ftdi * ftdi_class,uint8_t databits,uint8_t parity,uint8_t stopbits,uint8_t isbreak)208 static int usbh_ftdi_set_data_format(struct usbh_ftdi *ftdi_class, uint8_t databits, uint8_t parity, uint8_t stopbits, uint8_t isbreak)
209 {
210     /**
211      * D0-D7 databits  BITS_7=7, BITS_8=8
212      * D8-D10 parity  NONE=0, ODD=1, EVEN=2, MARK=3, SPACE=4
213      * D11-D12 		STOP_BIT_1=0, STOP_BIT_15=1, STOP_BIT_2=2
214      * D14  		BREAK_OFF=0, BREAK_ON=1
215      **/
216     struct usb_setup_packet *setup;
217     uint16_t value;
218 
219     if (!ftdi_class || !ftdi_class->hport) {
220         return -USB_ERR_INVAL;
221     }
222     setup = ftdi_class->hport->setup;
223 
224     value = ((isbreak & 0x01) << 14) | ((stopbits & 0x03) << 11) | ((parity & 0x0f) << 8) | (databits & 0x0f);
225 
226     setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
227     setup->bRequest = SIO_SET_DATA_REQUEST;
228     setup->wValue = value;
229     setup->wIndex = ftdi_class->intf;
230     setup->wLength = 0;
231 
232     return usbh_control_transfer(ftdi_class->hport, setup, NULL);
233 }
234 
usbh_ftdi_set_latency_timer(struct usbh_ftdi * ftdi_class,uint16_t value)235 static int usbh_ftdi_set_latency_timer(struct usbh_ftdi *ftdi_class, uint16_t value)
236 {
237     struct usb_setup_packet *setup;
238 
239     if (!ftdi_class || !ftdi_class->hport) {
240         return -USB_ERR_INVAL;
241     }
242     setup = ftdi_class->hport->setup;
243 
244     setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
245     setup->bRequest = SIO_SET_LATENCY_TIMER_REQUEST;
246     setup->wValue = value;
247     setup->wIndex = ftdi_class->intf;
248     setup->wLength = 0;
249 
250     return usbh_control_transfer(ftdi_class->hport, setup, NULL);
251 }
252 
usbh_ftdi_set_flow_ctrl(struct usbh_ftdi * ftdi_class,uint16_t value)253 static int usbh_ftdi_set_flow_ctrl(struct usbh_ftdi *ftdi_class, uint16_t value)
254 {
255     struct usb_setup_packet *setup;
256 
257     if (!ftdi_class || !ftdi_class->hport) {
258         return -USB_ERR_INVAL;
259     }
260     setup = ftdi_class->hport->setup;
261 
262     setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
263     setup->bRequest = SIO_SET_FLOW_CTRL_REQUEST;
264     setup->wValue = value;
265     setup->wIndex = ftdi_class->intf;
266     setup->wLength = 0;
267 
268     return usbh_control_transfer(ftdi_class->hport, setup, NULL);
269 }
270 
usbh_ftdi_read_modem_status(struct usbh_ftdi * ftdi_class)271 static int usbh_ftdi_read_modem_status(struct usbh_ftdi *ftdi_class)
272 {
273     struct usb_setup_packet *setup;
274     int ret;
275 
276     if (!ftdi_class || !ftdi_class->hport) {
277         return -USB_ERR_INVAL;
278     }
279     setup = ftdi_class->hport->setup;
280 
281     setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
282     setup->bRequest = SIO_POLL_MODEM_STATUS_REQUEST;
283     setup->wValue = 0x0000;
284     setup->wIndex = ftdi_class->intf;
285     setup->wLength = 2;
286 
287     ret = usbh_control_transfer(ftdi_class->hport, setup, g_ftdi_buf);
288     if (ret < 0) {
289         return ret;
290     }
291     memcpy(ftdi_class->modem_status, g_ftdi_buf, 2);
292     return ret;
293 }
294 
usbh_ftdi_set_line_coding(struct usbh_ftdi * ftdi_class,struct cdc_line_coding * line_coding)295 int usbh_ftdi_set_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding)
296 {
297     memcpy((uint8_t *)&ftdi_class->line_coding, line_coding, sizeof(struct cdc_line_coding));
298 
299     int ret = usbh_ftdi_set_baudrate(ftdi_class, line_coding->dwDTERate);
300     if (ret < 0) {
301         return ret;
302     }
303     return usbh_ftdi_set_data_format(ftdi_class, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat, 0);
304 }
305 
usbh_ftdi_get_line_coding(struct usbh_ftdi * ftdi_class,struct cdc_line_coding * line_coding)306 int usbh_ftdi_get_line_coding(struct usbh_ftdi *ftdi_class, struct cdc_line_coding *line_coding)
307 {
308     memcpy(line_coding, (uint8_t *)&ftdi_class->line_coding, sizeof(struct cdc_line_coding));
309     return 0;
310 }
311 
usbh_ftdi_set_line_state(struct usbh_ftdi * ftdi_class,bool dtr,bool rts)312 int usbh_ftdi_set_line_state(struct usbh_ftdi *ftdi_class, bool dtr, bool rts)
313 {
314     int ret;
315 
316     if (dtr) {
317         usbh_ftdi_set_modem(ftdi_class, SIO_SET_DTR_HIGH);
318     } else {
319         usbh_ftdi_set_modem(ftdi_class, SIO_SET_DTR_LOW);
320     }
321 
322     if (rts) {
323         ret = usbh_ftdi_set_modem(ftdi_class, SIO_SET_RTS_HIGH);
324     } else {
325         ret = usbh_ftdi_set_modem(ftdi_class, SIO_SET_RTS_LOW);
326     }
327 
328     return ret;
329 }
330 
usbh_ftdi_connect(struct usbh_hubport * hport,uint8_t intf)331 static int usbh_ftdi_connect(struct usbh_hubport *hport, uint8_t intf)
332 {
333     struct usb_endpoint_descriptor *ep_desc;
334     int ret = 0;
335     uint16_t version;
336 
337     struct usbh_ftdi *ftdi_class = usbh_ftdi_class_alloc();
338     if (ftdi_class == NULL) {
339         USB_LOG_ERR("Fail to alloc ftdi_class\r\n");
340         return -USB_ERR_NOMEM;
341     }
342 
343     ftdi_class->hport = hport;
344     ftdi_class->intf = intf;
345 
346     hport->config.intf[intf].priv = ftdi_class;
347 
348     version = hport->device_desc.bcdDevice;
349 
350     switch (version) {
351         case 0x400:
352             ftdi_class->chip_type = FT232B;
353             break;
354         case 0x500:
355             ftdi_class->chip_type = FT2232C;
356             break;
357         case 0x600:
358             ftdi_class->chip_type = FT232R;
359             break;
360         case 0x700:
361             ftdi_class->chip_type = FT2232H;
362             break;
363         case 0x800:
364             ftdi_class->chip_type = FT4232H;
365             break;
366         case 0x900:
367             ftdi_class->chip_type = FT232H;
368             break;
369 
370         default:
371             USB_LOG_ERR("Unknown FTDI chip version:%04x\r\n", version);
372             return -USB_ERR_NOTSUPP;
373     }
374 
375     USB_LOG_INFO("FTDI chip name:%s\r\n", ftdi_chip_name[ftdi_class->chip_type]);
376 
377     usbh_ftdi_reset(ftdi_class);
378     usbh_ftdi_set_flow_ctrl(ftdi_class, SIO_DISABLE_FLOW_CTRL);
379     usbh_ftdi_set_latency_timer(ftdi_class, 0x10);
380     usbh_ftdi_read_modem_status(ftdi_class);
381     USB_LOG_INFO("modem status:%02x:%02x\r\n", ftdi_class->modem_status[0], ftdi_class->modem_status[1]);
382 
383     for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
384         ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
385 
386         if (ep_desc->bEndpointAddress & 0x80) {
387             USBH_EP_INIT(ftdi_class->bulkin, ep_desc);
388         } else {
389             USBH_EP_INIT(ftdi_class->bulkout, ep_desc);
390         }
391     }
392 
393     snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, ftdi_class->minor);
394 
395     USB_LOG_INFO("Register FTDI Class:%s\r\n", hport->config.intf[intf].devname);
396 
397 #if 0
398     USB_LOG_INFO("Test ftdi rx and tx and rx for 5 times, baudrate is 115200\r\n");
399 
400     struct cdc_line_coding linecoding;
401     uint8_t count = 5;
402 
403     linecoding.dwDTERate = 115200;
404     linecoding.bDataBits = 8;
405     linecoding.bParityType = 0;
406     linecoding.bCharFormat = 0;
407     usbh_ftdi_set_line_coding(ftdi_class, &linecoding);
408     usbh_ftdi_set_line_state(ftdi_class, true, false);
409 
410     memset(g_ftdi_buf, 'a', sizeof(g_ftdi_buf));
411     ret = usbh_ftdi_bulk_out_transfer(ftdi_class, g_ftdi_buf, sizeof(g_ftdi_buf), 0xfffffff);
412     USB_LOG_RAW("out ret:%d\r\n", ret);
413     while (count--) {
414         ret = usbh_ftdi_bulk_in_transfer(ftdi_class, g_ftdi_buf, sizeof(g_ftdi_buf), 0xfffffff);
415         USB_LOG_RAW("in ret:%d\r\n", ret);
416         if (ret > 0) {
417             for (uint32_t i = 0; i < ret; i++) {
418                 USB_LOG_RAW("%02x ", g_ftdi_buf[i]);
419             }
420         }
421         USB_LOG_RAW("\r\n");
422     }
423 #endif
424     usbh_ftdi_run(ftdi_class);
425     return ret;
426 }
427 
usbh_ftdi_disconnect(struct usbh_hubport * hport,uint8_t intf)428 static int usbh_ftdi_disconnect(struct usbh_hubport *hport, uint8_t intf)
429 {
430     int ret = 0;
431 
432     struct usbh_ftdi *ftdi_class = (struct usbh_ftdi *)hport->config.intf[intf].priv;
433 
434     if (ftdi_class) {
435         if (ftdi_class->bulkin) {
436             usbh_kill_urb(&ftdi_class->bulkin_urb);
437         }
438 
439         if (ftdi_class->bulkout) {
440             usbh_kill_urb(&ftdi_class->bulkout_urb);
441         }
442 
443         if (hport->config.intf[intf].devname[0] != '\0') {
444             usb_osal_thread_schedule_other();
445             USB_LOG_INFO("Unregister FTDI Class:%s\r\n", hport->config.intf[intf].devname);
446             usbh_ftdi_stop(ftdi_class);
447         }
448 
449         usbh_ftdi_class_free(ftdi_class);
450     }
451 
452     return ret;
453 }
454 
usbh_ftdi_bulk_in_transfer(struct usbh_ftdi * ftdi_class,uint8_t * buffer,uint32_t buflen,uint32_t timeout)455 int usbh_ftdi_bulk_in_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
456 {
457     int ret;
458     struct usbh_urb *urb = &ftdi_class->bulkin_urb;
459 
460     usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkin, buffer, buflen, timeout, NULL, NULL);
461     ret = usbh_submit_urb(urb);
462     if (ret == 0) {
463         ret = urb->actual_length;
464     }
465     return ret;
466 }
467 
usbh_ftdi_bulk_out_transfer(struct usbh_ftdi * ftdi_class,uint8_t * buffer,uint32_t buflen,uint32_t timeout)468 int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
469 {
470     int ret;
471     struct usbh_urb *urb = &ftdi_class->bulkout_urb;
472 
473     usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkout, buffer, buflen, timeout, NULL, NULL);
474     ret = usbh_submit_urb(urb);
475     if (ret == 0) {
476         ret = urb->actual_length;
477     }
478     return ret;
479 }
480 
usbh_ftdi_run(struct usbh_ftdi * ftdi_class)481 __WEAK void usbh_ftdi_run(struct usbh_ftdi *ftdi_class)
482 {
483     (void)ftdi_class;
484 }
485 
usbh_ftdi_stop(struct usbh_ftdi * ftdi_class)486 __WEAK void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class)
487 {
488     (void)ftdi_class;
489 }
490 
491 static const uint16_t ftdi_id_table[][2] = {
492     { 0x0403, 0x6001 },
493     { 0x0403, 0x6010 },
494     { 0, 0 },
495 };
496 
497 const struct usbh_class_driver ftdi_class_driver = {
498     .driver_name = "ftdi",
499     .connect = usbh_ftdi_connect,
500     .disconnect = usbh_ftdi_disconnect
501 };
502 
503 CLASS_INFO_DEFINE const struct usbh_class_info ftdi_class_info = {
504     .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
505     .bInterfaceClass = 0xff,
506     .bInterfaceSubClass = 0x00,
507     .bInterfaceProtocol = 0x00,
508     .id_table = ftdi_id_table,
509     .class_driver = &ftdi_class_driver
510 };