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