From dcad369d060d4f10b1d1d27e42c374ba5a484b41 Mon Sep 17 00:00:00 2001 From: sakumisu <1203593632@qq.com> Date: Fri, 2 Feb 2024 22:50:21 +0800 Subject: [PATCH] add ftdi host --- class/vendor/usbh_ftdi.c | 311 +++++++++++++++++++++++++++++++++++++++ class/vendor/usbh_ftdi.h | 105 +++++++++++++ 2 files changed, 416 insertions(+) create mode 100644 class/vendor/usbh_ftdi.c create mode 100644 class/vendor/usbh_ftdi.h diff --git a/class/vendor/usbh_ftdi.c b/class/vendor/usbh_ftdi.c new file mode 100644 index 00000000..baf9516d --- /dev/null +++ b/class/vendor/usbh_ftdi.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2022, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbh_core.h" +#include "usbh_ftdi.h" + +#define DEV_FORMAT "/dev/ttyUSB%d" + +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_ftdi_buf[32]; + +#define CONFIG_USBHOST_MAX_FTDI_CLASS 4 + +static struct usbh_ftdi g_ftdi_class[CONFIG_USBHOST_MAX_FTDI_CLASS]; +static uint32_t g_devinuse = 0; + +static struct usbh_ftdi *usbh_ftdi_class_alloc(void) +{ + int devno; + + for (devno = 0; devno < CONFIG_USBHOST_MAX_FTDI_CLASS; devno++) { + if ((g_devinuse & (1 << devno)) == 0) { + g_devinuse |= (1 << devno); + memset(&g_ftdi_class[devno], 0, sizeof(struct usbh_ftdi)); + g_ftdi_class[devno].minor = devno; + return &g_ftdi_class[devno]; + } + } + return NULL; +} + +static void usbh_ftdi_class_free(struct usbh_ftdi *ftdi_class) +{ + int devno = ftdi_class->minor; + + if (devno >= 0 && devno < 32) { + g_devinuse &= ~(1 << devno); + } + memset(ftdi_class, 0, sizeof(struct usbh_ftdi)); +} + +static void usbh_ftdi_caculate_baudrate(uint32_t *itdf_divisor, uint32_t actual_baudrate) +{ +#define FTDI_USB_CLK 48000000 + int baudrate; + uint8_t frac[] = { 0, 8, 4, 2, 6, 10, 12, 14 }; + + if (actual_baudrate == 2000000) { + *itdf_divisor = 0x01; + } else if (actual_baudrate == 3000000) { + *itdf_divisor = 0x00; + } else { + baudrate = actual_baudrate; + if (baudrate > 100000 && baudrate < 12000000) { + baudrate = (baudrate / 100000) + 100000; + } + int divisor = FTDI_USB_CLK / baudrate; + int frac_bits = 0; + for (int i = 0; i < sizeof(frac) / sizeof(frac[0]); i++) { + if ((divisor & 0xF) == frac[i]) { + frac_bits = i; + break; + } + } + divisor >>= 4; + divisor &= 0x3FFF; + *itdf_divisor = (divisor << 14) | (frac_bits << 8); + } +} + +int usbh_ftdi_reset(struct usbh_ftdi *ftdi_class) +{ + struct usb_setup_packet *setup = ftdi_class->hport->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = SIO_RESET_REQUEST; + setup->wValue = 0; + setup->wIndex = ftdi_class->intf; + setup->wLength = 0; + + return usbh_control_transfer(ftdi_class->hport, setup, NULL); +} + +int usbh_ftdi_set_modem(struct usbh_ftdi *ftdi_class, uint16_t value) +{ + struct usb_setup_packet *setup = ftdi_class->hport->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = SIO_SET_MODEM_CTRL_REQUEST; + setup->wValue = value; + setup->wIndex = ftdi_class->intf; + setup->wLength = 0; + + return usbh_control_transfer(ftdi_class->hport, setup, NULL); +} + +int usbh_ftdi_set_baudrate(struct usbh_ftdi *ftdi_class, uint32_t baudrate) +{ + struct usb_setup_packet *setup = ftdi_class->hport->setup; + uint32_t itdf_divisor; + uint16_t value; + uint8_t baudrate_high; + + usbh_ftdi_caculate_baudrate(&itdf_divisor, baudrate); + value = itdf_divisor & 0xFFFF; + baudrate_high = (itdf_divisor >> 16) & 0xff; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = SIO_SET_BAUDRATE_REQUEST; + setup->wValue = value; + setup->wIndex = (baudrate_high << 8) | ftdi_class->intf; + setup->wLength = 0; + + return usbh_control_transfer(ftdi_class->hport, setup, NULL); +} + +int usbh_ftdi_set_data(struct usbh_ftdi *ftdi_class, uint8_t databits, uint8_t parity, uint8_t stopbits, uint8_t isbreak) +{ + /** + * D0-D7 databits BITS_7=7, BITS_8=8 + * D8-D10 parity NONE=0, ODD=1, EVEN=2, MARK=3, SPACE=4 + * D11-D12 STOP_BIT_1=0, STOP_BIT_15=1, STOP_BIT_2=2 + * D14 BREAK_OFF=0, BREAK_ON=1 + **/ + + uint16_t value = (databits & 0x0F) | ((parity & 0x03) << 8) | ((stopbits & 0x03) << 11) | ((isbreak & 0x01) << 14); + + struct usb_setup_packet *setup = ftdi_class->hport->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = SIO_SET_DATA_REQUEST; + setup->wValue = value; + setup->wIndex = ftdi_class->intf; + setup->wLength = 0; + + return usbh_control_transfer(ftdi_class->hport, setup, NULL); +} + +int usbh_ftdi_set_latency_timer(struct usbh_ftdi *ftdi_class, uint16_t value) +{ + struct usb_setup_packet *setup = ftdi_class->hport->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = SIO_SET_LATENCY_TIMER_REQUEST; + setup->wValue = value; + setup->wIndex = ftdi_class->intf; + setup->wLength = 0; + + return usbh_control_transfer(ftdi_class->hport, setup, NULL); +} + +int usbh_ftdi_set_flow_ctrl(struct usbh_ftdi *ftdi_class, uint16_t value) +{ + struct usb_setup_packet *setup = ftdi_class->hport->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = SIO_SET_FLOW_CTRL_REQUEST; + setup->wValue = value; + setup->wIndex = ftdi_class->intf; + setup->wLength = 0; + + return usbh_control_transfer(ftdi_class->hport, setup, NULL); +} + +int usbh_ftdi_read_modem_status(struct usbh_ftdi *ftdi_class) +{ + struct usb_setup_packet *setup = ftdi_class->hport->setup; + int ret; + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = SIO_POLL_MODEM_STATUS_REQUEST; + setup->wValue = 0x0000; + setup->wIndex = ftdi_class->intf; + setup->wLength = 2; + + ret = usbh_control_transfer(ftdi_class->hport, setup, g_ftdi_buf); + if (ret < 0) { + return ret; + } + memcpy(ftdi_class->modem_status, g_ftdi_buf, 2); + return ret; +} + +static int usbh_ftdi_connect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usb_endpoint_descriptor *ep_desc; + int ret; + + struct usbh_ftdi *ftdi_class = usbh_ftdi_class_alloc(); + if (ftdi_class == NULL) { + USB_LOG_ERR("Fail to alloc ftdi_class\r\n"); + return -USB_ERR_NOMEM; + } + + ftdi_class->hport = hport; + ftdi_class->intf = intf; + + hport->config.intf[intf].priv = ftdi_class; + + usbh_ftdi_reset(ftdi_class); + usbh_ftdi_set_flow_ctrl(ftdi_class, SIO_DISABLE_FLOW_CTRL); + usbh_ftdi_set_latency_timer(ftdi_class, 0x10); + usbh_ftdi_set_modem(ftdi_class, SIO_SET_RTS_LOW); + usbh_ftdi_set_modem(ftdi_class, SIO_SET_DTR_HIGH); + usbh_ftdi_read_modem_status(ftdi_class); + printf("modem status:%02x:%02x\r\n", ftdi_class->modem_status[0], ftdi_class->modem_status[1]); + + for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) { + ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc; + + if (ep_desc->bEndpointAddress & 0x80) { + USBH_EP_INIT(ftdi_class->bulkin, ep_desc); + } else { + USBH_EP_INIT(ftdi_class->bulkout, ep_desc); + } + } + + snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, ftdi_class->minor); + + USB_LOG_INFO("Register FTDI Class:%s\r\n", hport->config.intf[intf].devname); + + usbh_ftdi_run(ftdi_class); + return ret; +} + +static int usbh_ftdi_disconnect(struct usbh_hubport *hport, uint8_t intf) +{ + int ret = 0; + + struct usbh_ftdi *ftdi_class = (struct usbh_ftdi *)hport->config.intf[intf].priv; + + if (ftdi_class) { + if (ftdi_class->bulkin) { + usbh_kill_urb(&ftdi_class->bulkin_urb); + } + + if (ftdi_class->bulkout) { + usbh_kill_urb(&ftdi_class->bulkout_urb); + } + + if (hport->config.intf[intf].devname[0] != '\0') { + USB_LOG_INFO("Unregister FTDI Class:%s\r\n", hport->config.intf[intf].devname); + usbh_ftdi_stop(ftdi_class); + } + + usbh_ftdi_class_free(ftdi_class); + } + + return ret; +} + +int usbh_ftdi_bulk_in_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout) +{ + int ret; + struct usbh_urb *urb = &ftdi_class->bulkin_urb; + + usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkin, buffer, buflen, timeout, NULL, NULL); + ret = usbh_submit_urb(urb); + if (ret == 0) { + ret = urb->actual_length; + } + return ret; +} + +int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout) +{ + int ret; + struct usbh_urb *urb = &ftdi_class->bulkout_urb; + + usbh_bulk_urb_fill(urb, ftdi_class->hport, ftdi_class->bulkout, buffer, buflen, timeout, NULL, NULL); + ret = usbh_submit_urb(urb); + if (ret == 0) { + ret = urb->actual_length; + } + return ret; +} + +__WEAK void usbh_ftdi_run(struct usbh_ftdi *ftdi_class) +{ +} + +__WEAK void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class) +{ +} + +const struct usbh_class_driver ftdi_class_driver = { + .driver_name = "ftdi", + .connect = usbh_ftdi_connect, + .disconnect = usbh_ftdi_disconnect +}; + +CLASS_INFO_DEFINE const struct usbh_class_info ftdi1_class_info = { + .match_flags = USB_CLASS_MATCH_VENDOR | USB_CLASS_MATCH_PRODUCT | USB_CLASS_MATCH_INTF_CLASS, + .class = 0xff, + .subclass = 0xff, + .protocol = 0xff, + .vid = 0x0403, + .pid = 0x6001, + .class_driver = &ftdi_class_driver +}; + +CLASS_INFO_DEFINE const struct usbh_class_info ftdi2_class_info = { + .match_flags = USB_CLASS_MATCH_VENDOR | USB_CLASS_MATCH_PRODUCT | USB_CLASS_MATCH_INTF_CLASS, + .class = 0xff, + .subclass = 0xff, + .protocol = 0xff, + .vid = 0x0403, + .pid = 0x6010, + .class_driver = &ftdi_class_driver +}; \ No newline at end of file diff --git a/class/vendor/usbh_ftdi.h b/class/vendor/usbh_ftdi.h new file mode 100644 index 00000000..dfffa606 --- /dev/null +++ b/class/vendor/usbh_ftdi.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USBH_FTDI_H +#define USBH_FTDI_H + +/* Requests */ +#define SIO_RESET_REQUEST 0x00 /* Reset the port */ +#define SIO_SET_MODEM_CTRL_REQUEST 0x01 /* Set the modem control register */ +#define SIO_SET_FLOW_CTRL_REQUEST 0x02 /* Set flow control register */ +#define SIO_SET_BAUDRATE_REQUEST 0x03 /* Set baud rate */ +#define SIO_SET_DATA_REQUEST 0x04 /* Set the data characteristics of the port */ +#define SIO_POLL_MODEM_STATUS_REQUEST 0x05 +#define SIO_SET_EVENT_CHAR_REQUEST 0x06 +#define SIO_SET_ERROR_CHAR_REQUEST 0x07 +#define SIO_SET_LATENCY_TIMER_REQUEST 0x09 +#define SIO_GET_LATENCY_TIMER_REQUEST 0x0A +#define SIO_SET_BITMODE_REQUEST 0x0B +#define SIO_READ_PINS_REQUEST 0x0C +#define SIO_READ_EEPROM_REQUEST 0x90 +#define SIO_WRITE_EEPROM_REQUEST 0x91 +#define SIO_ERASE_EEPROM_REQUEST 0x92 + +#define SIO_DISABLE_FLOW_CTRL 0x0 +#define SIO_RTS_CTS_HS (0x1 << 8) +#define SIO_DTR_DSR_HS (0x2 << 8) +#define SIO_XON_XOFF_HS (0x4 << 8) + +#define SIO_SET_DTR_MASK 0x1 +#define SIO_SET_DTR_HIGH (1 | (SIO_SET_DTR_MASK << 8)) +#define SIO_SET_DTR_LOW (0 | (SIO_SET_DTR_MASK << 8)) +#define SIO_SET_RTS_MASK 0x2 +#define SIO_SET_RTS_HIGH (2 | (SIO_SET_RTS_MASK << 8)) +#define SIO_SET_RTS_LOW (0 | (SIO_SET_RTS_MASK << 8)) + +#define SIO_RTS_CTS_HS (0x1 << 8) + +struct usbh_ftdi { + struct usbh_hubport *hport; + struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */ + struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */ + struct usbh_urb bulkout_urb; + struct usbh_urb bulkin_urb; + + uint8_t intf; + uint8_t minor; + uint8_t modem_status[2]; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int usbh_ftdi_reset(struct usbh_ftdi *ftdi_class); +/** + * @brief set modem + * + * @param [in] value SIO_SET_DTR_HIGH or SIO_SET_DTR_LOW or SIO_SET_RTS_HIGH or SIO_SET_RTS_LOW + */ +int usbh_ftdi_set_modem(struct usbh_ftdi *ftdi_class, uint16_t value); +/** + * @brief set baudrate + * + * @param [in] baudrate less than 3M + */ +int usbh_ftdi_set_baudrate(struct usbh_ftdi *ftdi_class, uint32_t baudrate); +/** + * @brief set data + * + * @param [in] databits BITS_7=7, BITS_8=8 + * @param [in] parity NONE=0, ODD=1, EVEN=2, MARK=3, SPACE=4 + * @param [in] stopbits STOP_BIT_1=0, STOP_BIT_15=1, STOP_BIT_2=2 + * @param [in] isbreak BREAK_OFF=0, BREAK_ON=1 + */ +int usbh_ftdi_set_data(struct usbh_ftdi *ftdi_class, uint8_t databits, + uint8_t parity, + uint8_t stopbits, + uint8_t isbreak); +int usbh_ftdi_set_flow_ctrl(struct usbh_ftdi *ftdi_class, uint16_t value); + +/** + * @brief start a bulk in transfer + * + * @param [in] buffer buffer[0] and buffer[1] is modem status + * @param [in] buflen should be 64 or 512 + */ +int usbh_ftdi_bulk_in_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout); +/** + * @brief start a bulk out transfer + * + * @param [in] buffer + * @param [in] buflen + */ +int usbh_ftdi_bulk_out_transfer(struct usbh_ftdi *ftdi_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout); + +void usbh_ftdi_run(struct usbh_ftdi *ftdi_class); +void usbh_ftdi_stop(struct usbh_ftdi *ftdi_class); + +#ifdef __cplusplus +} +#endif + +#endif /* USBH_FTDI_H */