diff --git a/drivers/usbhost/Kconfig b/drivers/usbhost/Kconfig index ae98c2e1fb..0c389b3b46 100644 --- a/drivers/usbhost/Kconfig +++ b/drivers/usbhost/Kconfig @@ -600,4 +600,96 @@ config USBHOST_TRACE_VERBOSE endif +config USBHOST_FT232R + bool "FTDI FT232R support" + default n + depends on !USBHOST_BULK_DISABLE && !USBHOST_INT_DISABLE + select SERIAL_REMOVABLE + ---help--- + Select this option to build in host support for FTDI FT232R + serial devices. + +if USBHOST_FT232R + +config USBHOST_FT232R_RXDELAY + int "RX poll delay (MSec)" + default 200 + ---help--- + When the CDC/ACM device is inactive, the host must poll it at this + rate in order to discover if it has serial data to send to us. + +config USBHOST_FT232R_TXDELAY + int "TX poll delay (MSec)" + default 200 + ---help--- + When the appellation is inactive, the host must poll it at this + rate in order to discover if it has serial data to send to the + device. + +config USBHOST_FT232R_NPREALLOC + int "Preallocated state" + default 0 + ---help--- + If this setting is zero, the FT232R driver will allocate + memory as needed for FT232R device state. If this value is non- + zero, then it provides a number of preallocated FT232R state + structures. This increases the static size of the code image, but + eliminates all, direct, run-time allocations by the driver. + +config USBHOST_FT232R_BAUD + int "Initialize FT232R BAUD" + default 115200 + +config USBHOST_FT232R_PARITY + int "Initialize FT232R parity" + default 0 + range 0 2 + ---help--- + Initialize FT232R parity. 0=None, 1=Odd, 2=Even. Default: None + +config USBHOST_FT232R_BITS + int "Initialize FT232R transfer size" + default 8 + range 7 8 + ---help--- + Initialize FT232R number of bits. Default: 8 + +config USBHOST_FT232R_2STOP + bool "FT232R use two stop bits" + default n + ---help--- + False = 1 stop bit, True = Two stop bits. Default: 1 stop bit + +config USBHOST_FT232R_HWFLOWCTRL + bool "Use FT232R RTS/CTS" + default n + ---help--- + Enables the FT232R to use its flow control signals + +config USBHOST_FT232R_RXBUFSIZE + int "Serial RX buffer size" + default 128 + ---help--- + This is the size of the serial buffer that will be used to hold + received data. + +config USBHOST_FT232R_TXBUFSIZE + int "Serial TX buffer size" + default 128 + ---help--- + This is the size of the serial buffer that will be used to hold + data waiting for transmission. + +config USBHOST_FT232R_LATENCY + int "Latency Timer" + default 16 + range 0 255 + ---help--- + A timeout for UART to USB packet reception. The FT232R will + release its buffer for sending to the USB host once this timeout + occurs or the buffer is full. Measured in ms. + +endif # USBHOST_FT232R + + endif # USBHOST diff --git a/drivers/usbhost/Make.defs b/drivers/usbhost/Make.defs index cf058a1696..332e83c10e 100644 --- a/drivers/usbhost/Make.defs +++ b/drivers/usbhost/Make.defs @@ -74,6 +74,10 @@ ifeq ($(CONFIG_USBHOST_MAX3421E),y) CSRCS += usbhost_max3421e.c endif +ifeq ($(CONFIG_USBHOST_FT232R),y) +CSRCS += usbhost_ft232r.c +endif + # HCD debug/trace logic ifeq ($(CONFIG_USBHOST_TRACE),y) diff --git a/drivers/usbhost/usbhost_ft232r.c b/drivers/usbhost/usbhost_ft232r.c new file mode 100644 index 0000000000..d85bfb8125 --- /dev/null +++ b/drivers/usbhost/usbhost_ft232r.c @@ -0,0 +1,2798 @@ +/**************************************************************************** + * drivers/usbhost/usbhost_ft232r.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_USBHOST_FT232R + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_USBHOST +# warning USB host support not enabled (CONFIG_USBHOST) +#endif + +#ifdef CONFIG_USBHOST_BULK_DISABLE +# warning USB bulk endpoint support is disabled (CONFIG_USBHOST_BULK_DISABLE) +#endif + +#ifdef CONFIG_USBHOST_INT_DISABLE +# warning USB interrupt endpoint support is disabled (CONFIG_USBHOST_INT_DISABLE) +#endif + +#if !defined(CONFIG_SCHED_WORKQUEUE) +# warning Worker thread support is required (CONFIG_SCHED_WORKQUEUE) +#else +# ifndef CONFIG_SCHED_HPWORK +# warning High priority work thread support is required (CONFIG_SCHED_HPWORK) +# endif +# ifndef CONFIG_SCHED_LPWORK +# warning Low priority work thread support is required (CONFIG_SCHED_LPWORK) +# endif +# if CONFIG_SCHED_LPNTHREADS < 2 +# warning Multiple low priority work threads recommended for performance (CONFIG_SCHED_LPNTHREADS > 1) +# endif +#endif + +#ifndef CONFIG_SERIAL_REMOVABLE +# warning Removable serial device support is required (CONFIG_SERIAL_REMOVABLE) +#endif + +#ifdef CONFIG_USBHOST_FT232R_RXDELAY +# define USBHOST_FT232R_RXDELAY MSEC2TICK(CONFIG_USBHOST_FT232R_RXDELAY) +#else +# define USBHOST_FT232R_RXDELAY MSEC2TICK(200) +#endif + +#ifdef CONFIG_USBHOST_FT232R_TXDELAY +# define USBHOST_FT232R_TXDELAY MSEC2TICK(CONFIG_USBHOST_FT232R_TXDELAY) +#else +# define USBHOST_FT232R_TXDELAY MSEC2TICK(200) +#endif + +/* If the create() method is called by the USB host device driver from an + * interrupt handler, then it will be unable to call kmm_malloc() in order to + * allocate a new class instance. If the create() method is called from the + * interrupt level, then class instances must be pre-allocated. + */ + +#ifndef CONFIG_USBHOST_FT232R_NPREALLOC +# define CONFIG_USBHOST_FT232R_NPREALLOC 0 +#endif + +#if CONFIG_USBHOST_FT232R_NPREALLOC > 32 +# error Currently limited to 32 devices /dev/ttyUSB[n] +#endif + +#ifndef CONFIG_USBHOST_FT232R_RXBUFSIZE +# define CONFIG_USBHOST_FT232R_RXBUFSIZE 128 +#endif + +#ifndef CONFIG_USBHOST_FT232R_TXBUFSIZE +# define CONFIG_USBHOST_FT232R_TXBUFSIZE 128 +#endif + +/* Initial line coding */ + +#ifndef CONFIG_USBHOST_FT232R_BAUD +# define CONFIG_USBHOST_FT232R_BAUD 115200 +#endif + +#ifndef CONFIG_USBHOST_FT232R_PARITY +# define CONFIG_USBHOST_FT232R_PARITY 0 +#endif + +#ifndef CONFIG_USBHOST_FT232R_BITS +# define CONFIG_USBHOST_FT232R_BITS 8 +#endif + +#ifndef CONFIG_USBHOST_FT232R_2STOP +# define CONFIG_USBHOST_FT232R_2STOP 0 +#endif + +#ifndef CONFIG_USBHOST_FT232R_LATENCY +# define CONFIG_USBHOST_FT232R_LATENCY 16 +#endif + +/* Driver support ***********************************************************/ + +/* This format is used to construct the /dev/sd[n] device driver path. It + * defined here so that it will be used consistently in all places. + */ + +#define DEV_FORMAT "/dev/ttyUSB%d" +#define DEV_NAMELEN 16 + +#define MAX_NOTIFICATION 32 + +/* Used in usbhost_connect() */ + +#define USBHOST_DATAIF_FOUND 0x01 /* Data interface found */ +#define USBHOST_BULKIN_FOUND 0x02 /* Bulk IN interface found */ +#define USBHOST_BULKOUT_FOUND 0x04 /* Bulk OUT interface found */ + +#define USBHOST_ALLFOUND 0x07 /* All configuration things */ + +#define USBHOST_MAX_CREFS INT16_MAX /* Max cref count before signed overflow */ + +/* Configuration options */ + +/* Special case baud rates */ + +#define USBHOST_FT232R_BAUD_2MHZ 2000000 +#define USBHOST_FT232R_BAUD_3MHZ 3000000 +#define USBHOST_FT232R_MAX_BAUD USBHOST_FT232R_BAUD_3MHZ + +/* FT232R Control Transfer Request Types */ + +#define USBHOST_FT232R_CTRLREQ_RESET 0x0 +#define USBHOST_FT232R_CTRLREQ_MODEMCTRL 0x1 +#define USBHOST_FT232R_CTRLREQ_SETFLOWCTRL 0x2 +#define USBHOST_FT232R_CTRLREQ_SETBAUD 0x3 +#define USBHOST_FT232R_CTRLREQ_SETDATA 0x4 +#define USBHOST_FT232R_CTRLREQ_GETMODEMSTAT 0x5 +#define USBHOST_FT232R_CTRLREQ_SETLATTIMER 0x9 +#define USBHOST_FT232R_CTRLREQ_GETLATTIMER 0xA + +#define USBHOST_FT232R_MODEMCTRL_VAL_DTR 0x1 +#define USBHOST_FT232R_MODEMCTRL_VAL_RTS 0x2 +#define USBHOST_FT232R_MODEMCTRL_VAL_DTR_EN 0x100 +#define USBHOST_FT232R_MODEMCTRL_VAL_RTS_EN 0x200 + +#define USBHOST_FT232R_SETDATA_NBIT_MASK 0xFF +#define USBHOST_FT232R_SETDATA_PARITY_MASK 0x7 +#define USBHOST_FT232R_SETDATA_PARITY_SHIFT 8 +#define USBHOST_FT232R_SETDATA_2STOP 0x1000 +#define USBHOST_FT232R_SETDATA_BREAK 0x4000 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure contains the internal, private state of the USB host ftdi. + */ + +struct usbhost_ft232r_s +{ + /* This is the externally visible portion of the state. The usbclass must + * the first element of the structure. It is then cast compatible with + * struct usbhost_ft232r_s. + */ + + struct usbhost_class_s usbclass; + + /* This is the standard of the lower-half serial interface. It is not + * the first element of the structure, but includes a pointer back to the + * the beginning of this structure. + */ + + struct uart_dev_s uartdev; + + /* The remainder of the fields are provide to the FTDI class */ + + volatile bool disconnected; /* TRUE: Device has been disconnected */ + bool stop2; /* True: 2 stop bits (for line coding) */ + bool txena; /* True: TX "interrupts" enabled */ + bool rxena; /* True: RX "interrupts" enabled */ +#ifdef CONFIG_SERIAL_IFLOWCONTROL + bool iflow; /* True: Input flow control (RTS) enabled */ + bool rts; /* True: Input flow control is in effect */ +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + bool oflow; /* True: Output flow control (CTS) enabled */ +#endif +#ifdef CONFIG_USBHOST_FT232R_HWFLOWCTRL + bool cts; /* True: Clear to send to FTDI chip */ +#endif + uint8_t minor; /* Minor number identifying the /dev/ttyUSB[n] device */ + uint8_t dataif; /* Data interface number */ + uint8_t nbits; /* Number of bits (for line encoding) */ + uint8_t parity; /* Parity (for line encoding) */ + uint16_t pktsize; /* Allocated size of transfer buffers */ + uint16_t nrxbytes; /* Number of bytes in the RX packet buffer */ + uint16_t rxndx; /* Index to the next byte in the RX packet buffer */ + int16_t crefs; /* Reference count on the driver instance */ + int16_t nbytes; /* The number of bytes actually transferred */ + sem_t exclsem; /* Used to maintain mutual exclusive access */ + struct work_s ntwork; /* For asynchronous notification work */ + struct work_s rxwork; /* For RX packet work */ + struct work_s txwork; /* For TX packet work */ + FAR uint8_t *ctrlreq; /* Allocated ctrl request structure */ + FAR uint8_t *inbuf; /* Allocated RX buffer for the Bulk IN endpoint */ + FAR uint8_t *outbuf; /* Allocated TX buffer for the Bulk OUT endpoint */ + uint32_t baud; /* Current baud for line coding */ + usbhost_ep_t bulkin; /* Bulk IN endpoint */ + usbhost_ep_t bulkout; /* Bulk OUT endpoint */ + + /* This is the serial data buffer */ + + char rxbuffer[CONFIG_USBHOST_FT232R_RXBUFSIZE]; + char txbuffer[CONFIG_USBHOST_FT232R_TXBUFSIZE]; +}; + +/* This is how struct usbhost_ft232r_s looks to the free list logic */ + +struct usbhost_freestate_s +{ + FAR struct usbhost_freestate_s *flink; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Semaphores */ + +static int usbhost_takesem(FAR sem_t *sem); +static void usbhost_forcetake(FAR sem_t *sem); +#define usbhost_givesem(s) nxsem_post(s); + +/* Memory allocation services */ + +static FAR struct usbhost_ft232r_s *usbhost_allocclass(void); +static void usbhost_freeclass(FAR struct usbhost_ft232r_s *usbclass); + +/* Device name management */ + +static int usbhost_devno_alloc(FAR struct usbhost_ft232r_s *priv); +static void usbhost_devno_free(FAR struct usbhost_ft232r_s *priv); +static inline void usbhost_mkdevname(FAR struct usbhost_ft232r_s *priv, + FAR char *devname); + +/* UART buffer data transfer */ + +static void usbhost_txdata_work(FAR void *arg); +static void usbhost_rxdata_work(FAR void *arg); + +/* Worker thread actions */ + +static void usbhost_destroy(FAR void *arg); + +/* Helpers for usbhost_connect() */ + +static int usbhost_cfgdesc(FAR struct usbhost_ft232r_s *priv, + FAR const uint8_t *configdesc, int desclen); + +/* (Little Endian) Data helpers */ + +static inline uint16_t usbhost_getle16(FAR const uint8_t *val); +static inline uint16_t usbhost_getbe16(FAR const uint8_t *val); +static inline void usbhost_putle16(FAR uint8_t *dest, uint16_t val); + +/* Transfer descriptor memory management */ + +static int usbhost_alloc_buffers(FAR struct usbhost_ft232r_s *priv); +static void usbhost_free_buffers(FAR struct usbhost_ft232r_s *priv); + +/* struct usbhost_registry_s methods */ + +static struct usbhost_class_s *usbhost_create( + FAR struct usbhost_hubport_s *hport, + FAR const struct usbhost_id_s *id); + +/* struct usbhost_class_s methods */ + +static int usbhost_connect(FAR struct usbhost_class_s *usbclass, + FAR const uint8_t *configdesc, int desclen); +static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass); + +/* Serial driver lower-half interfaces */ + +static int usbhost_setup(FAR struct uart_dev_s *uartdev); +static void usbhost_shutdown(FAR struct uart_dev_s *uartdev); +static int usbhost_attach(FAR struct uart_dev_s *uartdev); +static void usbhost_detach(FAR struct uart_dev_s *uartdev); +static int usbhost_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); +static void usbhost_rxint(FAR struct uart_dev_s *uartdev, bool enable); +static bool usbhost_rxavailable(FAR struct uart_dev_s *uartdev); +#ifdef CONFIG_SERIAL_IFLOWCONTROL +static bool usbhost_rxflowcontrol(FAR struct uart_dev_s *uartdev, + unsigned int nbuffered, bool upper); +#endif +static void usbhost_txint(FAR struct uart_dev_s *uartdev, bool enable); +static bool usbhost_txready(FAR struct uart_dev_s *uartdev); +static bool usbhost_txempty(FAR struct uart_dev_s *uartdev); + +/* FTDI control transfer helpers */ + +static int ft232r_ctrlxfer(FAR struct usbhost_ft232r_s *priv, uint8_t req, + uint16_t value, uint16_t index); +static int ft232r_reset(FAR struct usbhost_ft232r_s *priv, bool purgerxtx); +static int ft232r_modemctrl(FAR struct usbhost_ft232r_s *priv); +static int ft232r_setflowctrl(FAR struct usbhost_ft232r_s *priv); +static int ft232r_setdivisor(uint32_t *divisor, uint32_t baud); +static int ft232r_setbaud(FAR struct usbhost_ft232r_s *priv); +static int ft232r_setdata(FAR struct usbhost_ft232r_s *priv); +static int ft232r_setlat(FAR struct usbhost_ft232r_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This structure provides the registry entry ID information that will be + * used to associate the USB host FTDI class to a connected USB + * device. + */ + +static const struct usbhost_id_s g_id[4] = +{ + { + USB_CLASS_VENDOR_SPEC, /* base */ + 0xff, /* subclass */ + 0xff, /* proto */ + 0x0403, /* vid */ + 0x6001 /* pid */ + }, + { + USB_CLASS_VENDOR_SPEC, /* base */ + 0xff, /* subclass */ + 0xff, /* proto */ + 0x0403, /* vid */ + 0x6015 /* pid */ + } +}; + +/* This is the USB host FTDI class's registry entry */ + +static struct usbhost_registry_s g_ft232r = +{ + NULL, /* flink */ + usbhost_create, /* create */ + 2, /* nids */ + &g_id[0] /* id[] */ +}; + +/* Serial driver lower half interface */ + +static const struct uart_ops_s g_uart_ops = +{ + usbhost_setup, /* setup */ + usbhost_shutdown, /* shutdown */ + usbhost_attach, /* attach */ + usbhost_detach, /* detach */ + usbhost_ioctl, /* ioctl */ + NULL , /* receive */ + usbhost_rxint, /* rxinit */ + usbhost_rxavailable, /* rxavailable */ +#ifdef CONFIG_SERIAL_IFLOWCONTROL + usbhost_rxflowcontrol, /* rxflowcontrol */ +#endif + NULL, /* send */ + usbhost_txint, /* txinit */ + usbhost_txready, /* txready */ + usbhost_txempty /* txempty */ +}; + +/* This is an array of pre-allocated USB host FTDI class instances */ + +#if CONFIG_USBHOST_FT232R_NPREALLOC > 0 +static struct usbhost_ft232r_s g_prealloc[CONFIG_USBHOST_FT232R_NPREALLOC]; +#endif + +/* This is a list of free, pre-allocated USB host FTDI class instances */ + +#if CONFIG_USBHOST_FT232R_NPREALLOC > 0 +static FAR struct usbhost_freestate_s *g_freelist; +#endif + +/* This is a bitmap that is used to allocate device + * minor numbers /dev/ttyUSB[n]. + */ + +static uint32_t g_devinuse; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usbhost_takesem + * + * Description: + * This is just a wrapper to handle the annoying behavior of semaphore + * waits that return due to the receipt of a signal. + * + ****************************************************************************/ + +static int usbhost_takesem(FAR sem_t *sem) +{ + return nxsem_wait_uninterruptible(sem); +} + +/**************************************************************************** + * Name: usbhost_forcetake + * + * Description: + * This is just another wrapper but this one continues even if the thread + * is canceled. This must be done in certain conditions where were must + * continue in order to clean-up resources. + * + ****************************************************************************/ + +static void usbhost_forcetake(FAR sem_t *sem) +{ + int ret; + + do + { + ret = nxsem_wait_uninterruptible(sem); + + /* The only expected error would -ECANCELED meaning that the + * parent thread has been canceled. We have to continue and + * terminate the poll in this case. + */ + + DEBUGASSERT(ret == OK || ret == -ECANCELED); + } + while (ret < 0); +} + +/**************************************************************************** + * Name: usbhost_allocclass + * + * Description: + * This is really part of the logic that implements the create() method + * of struct usbhost_registry_s. This function allocates memory for one + * new class instance. + * + * Input Parameters: + * None + * + * Returned Value: + * On success, this function will return a non-NULL instance of struct + * usbhost_class_s. NULL is returned on failure; this function will + * will fail only if there are insufficient resources to create another + * USB host class instance. + * + ****************************************************************************/ + +#if CONFIG_USBHOST_FT232R_NPREALLOC > 0 +static FAR struct usbhost_ft232r_s *usbhost_allocclass(void) +{ + FAR struct usbhost_freestate_s *entry; + irqstate_t flags; + + /* We may be executing from an interrupt handler so we need to take one of + * our pre-allocated class instances from the free list. + */ + + flags = enter_critical_section(); + entry = g_freelist; + if (entry) + { + g_freelist = entry->flink; + } + + leave_critical_section(flags); + uinfo("Allocated: %p\n", entry); + return (FAR struct usbhost_ft232r_s *)entry; +} +#else +static FAR struct usbhost_ft232r_s *usbhost_allocclass(void) +{ + FAR struct usbhost_ft232r_s *priv; + + /* We are not executing from an interrupt handler so we can just call + * kmm_malloc() to get memory for the class instance. + */ + + DEBUGASSERT(!up_interrupt_context()); + priv = (FAR struct usbhost_ft232r_s *) + kmm_malloc(sizeof(struct usbhost_ft232r_s)); + + uinfo("Allocated: %p\n", priv); + return priv; +} +#endif + +/**************************************************************************** + * Name: usbhost_freeclass + * + * Description: + * Free a class instance previously allocated by usbhost_allocclass(). + * + * Input Parameters: + * usbclass - A reference to the class instance to be freed. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if CONFIG_USBHOST_FT232R_NPREALLOC > 0 +static void usbhost_freeclass(FAR struct usbhost_ft232r_s *usbclass) +{ + FAR struct usbhost_freestate_s *entry = + (FAR struct usbhost_freestate_s *)usbclass; + irqstate_t flags; + DEBUGASSERT(entry != NULL); + + uinfo("Freeing: %p\n", entry); + + /* Just put the pre-allocated class structure back on the freelist */ + + flags = enter_critical_section(); + entry->flink = g_freelist; + g_freelist = entry; + leave_critical_section(flags); +} +#else +static void usbhost_freeclass(FAR struct usbhost_ft232r_s *usbclass) +{ + DEBUGASSERT(usbclass != NULL); + + /* Free the class instance (calling kmm_free() in case we are executing + * from an interrupt handler. + */ + + uinfo("Freeing: %p\n", usbclass); + kmm_free(usbclass); +} +#endif + +/**************************************************************************** + * Name: usbhost_devno_alloc + * + * Description: + * Allocate a unique /dev/ttyACM[n] minor number in the range 0-31. + * + ****************************************************************************/ + +static int usbhost_devno_alloc(FAR struct usbhost_ft232r_s *priv) +{ + irqstate_t flags; + int devno; + + flags = enter_critical_section(); + for (devno = 0; devno < 32; devno++) + { + uint32_t bitno = 1 << devno; + if ((g_devinuse & bitno) == 0) + { + g_devinuse |= bitno; + priv->minor = devno; + leave_critical_section(flags); + return OK; + } + } + + leave_critical_section(flags); + return -EMFILE; +} + +/**************************************************************************** + * Name: usbhost_devno_free + * + * Description: + * Free a /dev/ttyACM[n] minor number so that it can be used. + * + ****************************************************************************/ + +static void usbhost_devno_free(FAR struct usbhost_ft232r_s *priv) +{ + int devno = priv->minor; + + if (devno >= 0 && devno < 32) + { + irqstate_t flags = enter_critical_section(); + g_devinuse &= ~(1 << devno); + leave_critical_section(flags); + } +} + +/**************************************************************************** + * Name: usbhost_mkdevname + * + * Description: + * Format a /dev/ttyACM[n] device name given a minor number. + * + ****************************************************************************/ + +static inline void usbhost_mkdevname(FAR struct usbhost_ft232r_s *priv, + FAR char *devname) +{ + snprintf(devname, DEV_NAMELEN, DEV_FORMAT, priv->minor); +} + +/**************************************************************************** + * Name: ft232r_ctrlxfer + * + * Description: + * Free a class instance previously allocated by usbhost_allocclass(). + * + * Input Parameters: + * priv - A reference to the USB host class instance. + * req - FTDI control transfer type. + * value - Value for control transfer. + * index - Index for control transfer. + * + * Returned Value: + * 0 on success. Negated errno on failure. + * + ****************************************************************************/ + +static int ft232r_ctrlxfer(FAR struct usbhost_ft232r_s *priv, uint8_t req, + uint16_t value, uint16_t index) +{ + FAR struct usbhost_hubport_s *hport; + FAR struct usb_ctrlreq_s *ctrlreq; + int ret; + + hport = priv->usbclass.hport; + DEBUGASSERT(hport); + + /* Initialize the control request */ + + ctrlreq = (FAR struct usb_ctrlreq_s *)priv->ctrlreq; + ctrlreq->type = USB_DIR_OUT | USB_REQ_TYPE_VENDOR | + USB_REQ_RECIPIENT_DEVICE; + ctrlreq->req = req; + + usbhost_putle16(ctrlreq->value, value); + usbhost_putle16(ctrlreq->index, index); + usbhost_putle16(ctrlreq->len, 0); + + /* And send the request */ + + ret = DRVR_CTRLOUT(hport->drvr, hport->ep0, ctrlreq, NULL); + if (ret < 0) + { + uerr("ERROR: DRVR_CTRLOUT failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: ft232r_reset + * + * Description: + * Resets the FT232. + * + * Input Parameters: + * priv - A reference to the USB host class instance. + * purgerxtx - True if the RX and TX buffers of the FTDI chip + * should be flushed. + * + * Returned Value: + * 0 on success. Negated errno on failure. + * + ****************************************************************************/ + +static int ft232r_reset(FAR struct usbhost_ft232r_s *priv, bool purgerxtx) +{ + int ret = ft232r_ctrlxfer(priv, USBHOST_FT232R_CTRLREQ_RESET, + purgerxtx, 0); + if (ret < 0) + { + uerr("ERROR: ft232r_ctrlxfer failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: ft232r_modemctrl + * + * Description: + * Sets RTS and DTR. + * + * Input Parameters: + * priv - A reference to the USB host class instance. + * + * Returned Value: + * 0 on success. Negated errno on failure. + * + ****************************************************************************/ + +static int ft232r_modemctrl(FAR struct usbhost_ft232r_s *priv) +{ + uint16_t value = USBHOST_FT232R_MODEMCTRL_VAL_RTS | + USBHOST_FT232R_MODEMCTRL_VAL_DTR | + USBHOST_FT232R_MODEMCTRL_VAL_RTS_EN | + USBHOST_FT232R_MODEMCTRL_VAL_DTR_EN; + + int ret = ft232r_ctrlxfer(priv, USBHOST_FT232R_CTRLREQ_MODEMCTRL, + value, 0); + if (ret < 0) + { + uerr("ERROR: ft232r_ctrlxfer failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: ft232r_setflowctrl + * + * Description: + * Enables/ disables hardware flow control. + * + * Input Parameters: + * priv - A reference to the USB host class instance. + * + * Returned Value: + * 0 on success. Negated errno on failure. + * + ****************************************************************************/ + +static int ft232r_setflowctrl(FAR struct usbhost_ft232r_s *priv) +{ + /* upper byte XOFF char, lower byte XON char */ + + uint16_t value = 0; + + /* Upper byte: flow control settings. + * 0th bit: RTS/CTS flow control. + * 1st bit: DTR/DSR flow control. + * 2nd bit: XON/XOFF flow control. + * Lower byte: 0 + */ + + uint16_t index = 0; + +#ifdef CONFIG_USBHOST_FT232R_HWFLOWCTRL + index = 0x100; +#endif + + int ret = ft232r_ctrlxfer(priv, USBHOST_FT232R_CTRLREQ_SETFLOWCTRL, + value, index); + if (ret < 0) + { + uerr("ERROR: ft232r_ctrlxfer failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: ft232r_setdivisor + * + * Description: + * Converts a baud rate to the corresponding FT232R divisor. Returns an + * error if the requested baud rate is not possible. + * + * Input Parameters: + * divisor - Where the calculated divisor will be written to. + * baud - The requested baud rate. + * Returned Value: + * 0 on success. Negated errno on failure. + * + ****************************************************************************/ + +static int ft232r_setdivisor(uint32_t *divisor, uint32_t baud) +{ + int ret = 0; + + /* Deal with special case baud rates: 3MHz and 2MHz */ + + if (baud == USBHOST_FT232R_BAUD_3MHZ) + { + *divisor = 0; + } + else if (baud == USBHOST_FT232R_BAUD_2MHZ) + { + *divisor = 1; + } + else if (baud > USBHOST_FT232R_BAUD_3MHZ / 2) + { + /* FT232 doesn't support fractional divisors between 0 and 2. */ + + ret = -EINVAL; + } + else + { + int divfrac[9] = {0, + 500, + 250, + 125, + 375, + 625, + 750, + 875, + 1000 + }; + + double frac = (double)USBHOST_FT232R_MAX_BAUD / (double)baud; + + *divisor = (uint32_t)frac; + int rem = (frac - *divisor) * 1000.0; + + if (rem > 0) + { + int i; + int j = 0; + int closest = rem; + + for (i = 1; i < 9; i++) + { + if (rem == divfrac[i]) + { + j = i; + break; + } + + if ((rem > divfrac[i] && (rem - divfrac[i]) < closest)) + { + j = i; + closest = rem - divfrac[i]; + } + else if (rem < divfrac[i] && (divfrac[i] - rem) < closest) + { + j = i; + closest = divfrac[i] - rem; + } + } + + /* Make sure the calculated baud is within 3% of the + * requested baud rate. + */ + + double err = (frac - + (double)(*divisor + (double)divfrac[j] / 1000.0)) + / frac; + if (err < -0.03 || err > 0.03) + { + ret = -ENOTSUP; + } + + if (j == 9) + { + *divisor += 1; + } + else + { + *divisor |= (j & 0x7) << 14; + } + } + } + + return ret; +} + +/**************************************************************************** + * Name: ft232r_setbaud + * + * Description: + * Sets the FT232 baud rate. + * + * Input Parameters: + * priv - A reference to the USB host class instance. + * + * Returned Value: + * 0 on success. Negated errno on failure. + * + ****************************************************************************/ + +static int ft232r_setbaud(FAR struct usbhost_ft232r_s *priv) +{ + /* Convert baud to FT232 divisor */ + + uint32_t divisor; + int ret = ft232r_setdivisor(&divisor, priv->baud); + if (ret < 0) + { + uerr("ERROR: ft232r_setdivisor failed: %d\n", ret); + } + else + { + ret = ft232r_ctrlxfer(priv, USBHOST_FT232R_CTRLREQ_SETBAUD, + divisor & 0xffff, divisor && 0x10000); + if (ret < 0) + { + uerr("ERROR: ft232r_ctrlxfer failed: %d\n", ret); + } + } + + return ret; +} + +/**************************************************************************** + * Name: ft232r_setdata + * + * Description: + * Sets the packet characteristics for the UART side of the FT232. + * + * Input Parameters: + * priv - A reference to the USB host class instance. + * + * Returned Value: + * 0 on success. Negated errno on failure. + * + ****************************************************************************/ + +static int ft232r_setdata(FAR struct usbhost_ft232r_s *priv) +{ + /* Build up line coding */ + + uint16_t linecoding = (priv->nbits & USBHOST_FT232R_SETDATA_NBIT_MASK) | + ((priv->parity & USBHOST_FT232R_SETDATA_PARITY_MASK) + << USBHOST_FT232R_SETDATA_PARITY_SHIFT); + if (priv->stop2) + { + linecoding |= CONFIG_USBHOST_FT232R_2STOP; + } + + int ret = ft232r_ctrlxfer(priv, USBHOST_FT232R_CTRLREQ_SETDATA, + linecoding, 0); + if (ret < 0) + { + uerr("ERROR: ft232r_ctrlxfer failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: ft232r_setlat + * + * Description: + * Sets the UART latency of the FT232. + * + * Input Parameters: + * priv - A reference to the USB host class instance. + * + * Returned Value: + * 0 on success. Negated errno on failure. + * + ****************************************************************************/ + +static int ft232r_setlat(FAR struct usbhost_ft232r_s *priv) +{ + int ret = ft232r_ctrlxfer(priv, USBHOST_FT232R_CTRLREQ_SETLATTIMER, + CONFIG_USBHOST_FT232R_LATENCY, 0); + if (ret < 0) + { + uerr("ERROR: ft232r_ctrlxfer failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * UART buffer data transfer + ****************************************************************************/ + +/**************************************************************************** + * Name: usbhost_txdata_work + * + * Description: + * Send more OUT data to the attached FTDI device. + * + * Input Parameters: + * arg - A reference to the FTDI class private data + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_txdata_work(FAR void *arg) +{ + FAR struct usbhost_ft232r_s *priv; + FAR struct usbhost_hubport_s *hport; + FAR struct uart_dev_s *uartdev; + FAR struct uart_buffer_s *txbuf; + ssize_t nwritten; + int txndx; + int txtail; + int ret; + + priv = (FAR struct usbhost_ft232r_s *)arg; + DEBUGASSERT(priv); + + hport = priv->usbclass.hport; + DEBUGASSERT(hport); + + uartdev = &priv->uartdev; + txbuf = &uartdev->xmit; + + /* Do nothing if TX transmission is disabled */ + + if (!priv->txena) + { + /* Terminate the work now *without* rescheduling */ + + return; + } + + /* Loop until The UART TX buffer is empty (or we become disconnected) */ + + txtail = txbuf->tail; + txndx = 0; + +#ifdef CONFIG_USBHOST_FT232R_HWFLOWCTRL + while (txtail != txbuf->head && priv->txena && + !priv->disconnected && priv->cts) +#else + while (txtail != txbuf->head && priv->txena && !priv->disconnected) +#endif + { + /* Copy data from the UART TX buffer until either 1) the UART TX + * buffer has been emptied, or 2) the Bulk OUT buffer is full. + */ + + txndx = 0; + while (txtail != txbuf->head && txndx < priv->pktsize) + { + /* Copy the next byte */ + + priv->outbuf[txndx] = txbuf->buffer[txtail]; + + /* Increment counters and indices */ + + txndx++; + if (++txtail >= txbuf->size) + { + txtail = 0; + } + } + + /* Save the updated tail pointer so that it cannot be sent again */ + + txbuf->tail = txtail; + + /* Bytes were removed from the TX buffer. Inform any waiters that + * there is space available in the TX buffer. + */ + + uart_datasent(uartdev); + + /* Send the filled TX buffer to the FTDI device */ + + nwritten = DRVR_TRANSFER(hport->drvr, priv->bulkout, + priv->outbuf, txndx); + if (nwritten < 0) + { + /* The most likely reason for a failure is that FTDI device + * NAK'ed our packet OR that the device has been disconnected. + * + * Just break out of the loop, rescheduling the work (unless + * the device is disconnected). + */ + + uerr("ERROR: DRVR_TRANSFER for packet failed: %d\n", + (int)nwritten); + break; + } + } + + /* We get here because: 1) the UART TX buffer is empty and there is + * nothing more to send, 2) the FTDI device was not ready to accept our + * data, or the device is no longer available. + * + * If the last packet sent was and even multiple of the packet size, then + * we need to send a zero length packet (ZLP). + */ + + if (txndx == priv->pktsize && !priv->disconnected) + { + /* Send the ZLP to the FTDI device */ + + nwritten = DRVR_TRANSFER(hport->drvr, priv->bulkout, + priv->outbuf, 0); + if (nwritten < 0) + { + /* The most likely reason for a failure is that FTDI device + * NAK'ed our packet. + */ + + uerr("ERROR: DRVR_TRANSFER for ZLP failed: %d\n", (int)nwritten); + } + } + + /* Check again if TX reception is enabled and that the device is still + * connected. These states could have changed since we started the + * transfer. + */ + + if (priv->txena && !priv->disconnected) + { + /* Schedule TX data work to occur after a delay. */ + + ret = work_queue(LPWORK, &priv->txwork, usbhost_txdata_work, priv, + USBHOST_FT232R_TXDELAY); + DEBUGASSERT(ret >= 0); + UNUSED(ret); + } +} + +/**************************************************************************** + * Name: usbhost_rxdata_work + * + * Description: + * Get more IN data from the attached FTDI device. + * + * Input Parameters: + * arg - A reference to the FTDI class private data + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_rxdata_work(FAR void *arg) +{ + FAR struct usbhost_ft232r_s *priv; + FAR struct usbhost_hubport_s *hport; + FAR struct uart_dev_s *uartdev; + FAR struct uart_buffer_s *rxbuf; + ssize_t nread; + int nxfrd; + int nexthead; + int rxndx; + int ret; + + priv = (FAR struct usbhost_ft232r_s *)arg; + DEBUGASSERT(priv); + + hport = priv->usbclass.hport; + DEBUGASSERT(hport); + + uartdev = &priv->uartdev; + rxbuf = &uartdev->recv; + + /* Get the index in the RX packet buffer where we will take the first + * byte of data. + */ + + rxndx = priv->rxndx; + nxfrd = 0; + + /* Get the index to the value of the UART RX buffer head AFTER the + * first character has been stored. We need to know this in order + * to test if the UART RX buffer is full. + */ + + nexthead = rxbuf->head + 1; + if (nexthead >= rxbuf->size) + { + nexthead = 0; + } + + /* Loop until either: + * + * 1. The UART RX buffer is full + * 2. There is no more data available from the FTDI device + * 3. RX rec + */ + +#ifdef CONFIG_SERIAL_IFLOWCONTROL + while (priv->rxena && priv->rts && !priv->disconnected) +#else + while (priv->rxena && !priv->disconnected) +#endif + { + /* Stop now if there is no room for another + * character in the RX buffer. + */ + + if (nexthead == rxbuf->tail) + { + /* Break out of the loop, rescheduling the work */ + + break; + } + + /* Do we have any buffer RX data to transfer? */ + + if (priv->nrxbytes < 1) + { + /* No.. Read more data from the FTDI device */ + + nread = DRVR_TRANSFER(hport->drvr, priv->bulkin, + priv->inbuf, priv->pktsize); + if (nread < 0) + { + /* The most likely reason for a failure is that the has no + * data available now and NAK'ed the IN token OR that the + * transfer was cancelled because the device was disconnected. + * + * Just break out of the loop, rescheduling the work (if the + * device was not disconnected. + */ + + uerr("ERROR: DRVR_TRANSFER for packet failed: %d\n", + (int)nread); + break; + } + + /* Save the number of bytes read. This might be zero if + * a Zero Length Packet (ZLP) is received. The ZLP is + * part of the bulk transfer protocol, but otherwise of + * no interest to us. Alternatively it can be 2 bytes of + * only FTDI status information. + */ + + priv->nrxbytes = (uint16_t)nread - 2; + rxndx = 0; + + /* When Hardware flow control is used, CTS is reported in the + * first byte of RX payload. + */ + +#ifdef CONFIG_USBHOST_FT232R_HWFLOWCTRL + if (nread >= 2) + { + priv->cts = priv->inbuf[0] & 0x10; + } +#endif + + /* Ignore ZLPs and RX of only FTDI status */ + + if (nread < 3) + { + continue; + } + } + + /* Transfer one byte from the RX packet buffer into UART RX buffer */ + + rxbuf->buffer[rxbuf->head] = priv->inbuf[rxndx + 2]; /* +2 to account for the two status byes */ + nxfrd++; + + /* Save the updated indices */ + + rxbuf->head = nexthead; + priv->rxndx = rxndx; + + /* Update the head point for for the next pass through the loop + * handling. If nexthead incremented to rxbuf->tail, then the + * RX buffer will and we will exit the loop at the top. + */ + + if (++nexthead >= rxbuf->size) + { + nexthead = 0; + } + + /* Increment the index in the USB IN packet buffer. If the + * index becomes equal to the number of bytes in the buffer, then + * we have consumed all of the RX data. + */ + + if (++rxndx >= priv->nrxbytes) + { + /* In that case set the number of bytes in the buffer to zero. + * This will force re-reading on the next time through the loop. + */ + + priv->nrxbytes = 0; + priv->rxndx = 0; + + /* Inform any waiters there there is new incoming data available. */ + + uart_datareceived(uartdev); + nxfrd = 0; + } + } + + /* We break out to here: 1) the UART RX buffer is full, 2) the FTDI + * device is not ready to provide us with more serial data, or 3) the + * device has been disconnected. + * + * Check if the device is still available: RX enabled, no RX flow + * control in effect, and that the device is not disconnected. These + * states could have changed since we started the transfer. + */ + +#ifdef CONFIG_SERIAL_IFLOWCONTROL + if (priv->rxena && priv->rts && work_available(&priv->rxwork) && + !priv->disconnected) +#else + if (priv->rxena && work_available(&priv->rxwork) && !priv->disconnected) +#endif + { + /* Schedule RX data reception work to occur after a delay. This will + * affect our responsive in certain cases. The delayed work, however, + * will be cancelled and replaced with immediate work when the upper + * layer demands more data. + */ + + ret = work_queue(LPWORK, &priv->rxwork, usbhost_rxdata_work, priv, + USBHOST_FT232R_RXDELAY); + DEBUGASSERT(ret >= 0); + UNUSED(ret); + } + + /* If any bytes were added to the buffer, inform any waiters there there + * is new incoming data available. + */ + + if (nxfrd) + { + uart_datareceived(uartdev); + } +} + +/**************************************************************************** + * Name: usbhost_destroy + * + * Description: + * The USB FTDI device has been disconnected and the reference count + * on the USB host class instance has gone to 1.. Time to destroy the USB + * host class instance. + * + * Input Parameters: + * arg - A reference to the class instance to be destroyed. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_destroy(FAR void *arg) +{ + FAR struct usbhost_ft232r_s *priv = (FAR struct usbhost_ft232r_s *)arg; + FAR struct usbhost_hubport_s *hport; + char devname[DEV_NAMELEN]; + + DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL); + hport = priv->usbclass.hport; + + uinfo("crefs: %d\n", priv->crefs); + + /* Unregister the serial lower half driver */ + + usbhost_mkdevname(priv, devname); + unregister_driver(devname); + + /* Release the device name used by this connection */ + + usbhost_devno_free(priv); + + /* Free the allocated endpoints */ + + if (priv->bulkout) + { + DRVR_EPFREE(hport->drvr, priv->bulkout); + } + + if (priv->bulkin) + { + DRVR_EPFREE(hport->drvr, priv->bulkin); + } + + /* Free any transfer buffers */ + + usbhost_free_buffers(priv); + + /* Destroy the semaphores */ + + nxsem_destroy(&priv->exclsem); + + /* Disconnect the USB host device */ + + DRVR_DISCONNECT(hport->drvr, hport); + + /* Free the function address assigned to this device */ + + usbhost_devaddr_destroy(hport, hport->funcaddr); + hport->funcaddr = 0; + + /* And free the class instance. */ + + usbhost_freeclass(priv); +} + +/**************************************************************************** + * Name: usbhost_cfgdesc + * + * Description: + * This function implements the connect() method of struct + * usbhost_class_s. This method is a callback into the class + * implementation. It is used to provide the device's configuration + * descriptor to the class so that the class may initialize properly + * + * Input Parameters: + * priv - The USB host class instance. + * configdesc - A pointer to a uint8_t buffer container the configuration + * descriptor. + * desclen - The length in bytes of the configuration descriptor. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ****************************************************************************/ + +static int usbhost_cfgdesc(FAR struct usbhost_ft232r_s *priv, + FAR const uint8_t *configdesc, int desclen) +{ + FAR struct usbhost_hubport_s *hport; + FAR struct usb_cfgdesc_s *cfgdesc; + FAR struct usb_desc_s *desc; + struct usbhost_epdesc_s bindesc; + struct usbhost_epdesc_s boutdesc; + struct usbhost_epdesc_s iindesc; + int remaining; + uint8_t found = 0; + uint8_t currif = 0; + int ret; + + DEBUGASSERT(priv != NULL && priv->usbclass.hport && + configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s)); + hport = priv->usbclass.hport; + + /* Keep the compiler from complaining about uninitialized variables */ + + memset(&bindesc, 0, sizeof(struct usbhost_epdesc_s)); + memset(&boutdesc, 0, sizeof(struct usbhost_epdesc_s)); + memset(&iindesc, 0, sizeof(struct usbhost_epdesc_s)); + + /* Verify that we were passed a configuration descriptor */ + + cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc; + if (cfgdesc->type != USB_DESC_TYPE_CONFIG) + { + return -EINVAL; + } + + /* Get the total length of the configuration descriptor (little endian). + * It might be a good check to get the number of interfaces here too. + */ + + remaining = (int)usbhost_getle16(cfgdesc->totallen); + + /* Skip to the next entry descriptor */ + + configdesc += cfgdesc->len; + remaining -= cfgdesc->len; + + /* Loop where there are more descriptors to examine */ + + while (remaining >= sizeof(struct usb_desc_s)) + { + /* What is the next descriptor? */ + + desc = (FAR struct usb_desc_s *)configdesc; + switch (desc->type) + { + /* Interface descriptor. The FTDI device may include two + * interfaces: + * + * 1) A data interface which consists of two endpoints (bulk in + + * bulk out) and + * 2) A control interface which consists of one interrupt in + * endpoint. + */ + + case USB_DESC_TYPE_INTERFACE: + { + FAR struct usb_ifdesc_s *ifdesc = + (FAR struct usb_ifdesc_s *)configdesc; + + uinfo("Interface descriptor: class: %d subclass: %d proto: %d\n", + ifdesc->classid, ifdesc->subclass, ifdesc->protocol); + DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC); + + /* Check for the FTDI data interface */ + + if (ifdesc->classid == USB_CLASS_VENDOR_SPEC && + (found & USBHOST_DATAIF_FOUND) == 0) + { + /* Save the data interface number and mark that the data + * interface found has been found. + */ + + priv->dataif = ifdesc->ifno; + found |= USBHOST_DATAIF_FOUND; + currif = USBHOST_DATAIF_FOUND; + } + else + { + /* Its something else */ + + currif = 0; + } + } + break; + + /* Endpoint descriptor. We expect two bulk endpoints, an IN and an + * OUT. + */ + + case USB_DESC_TYPE_ENDPOINT: + { + FAR struct usb_epdesc_s *epdesc = + (FAR struct usb_epdesc_s *)configdesc; + + uinfo("Endpoint descriptor: currif: %02x attr: %02x\n", + currif, epdesc->attr); + DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC); + + /* Check for a bulk endpoint. */ + + if (currif == USBHOST_DATAIF_FOUND && + (epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == + USB_EP_ATTR_XFER_BULK) + { + /* Yes.. it is a bulk endpoint. IN or OUT? */ + + if (USB_ISEPOUT(epdesc->addr)) + { + /* It is an OUT bulk endpoint. There should be only one + * bulk OUT endpoint. + */ + + if ((found & USBHOST_BULKOUT_FOUND) != 0) + { + /* Oops.. more than one endpoint. We don't know + * what to do with this. + */ + + return -EINVAL; + } + + found |= USBHOST_BULKOUT_FOUND; + + /* Save the bulk OUT endpoint information */ + + boutdesc.hport = hport; + boutdesc.addr = epdesc->addr & + USB_EP_ADDR_NUMBER_MASK; + boutdesc.in = false; + boutdesc.xfrtype = USB_EP_ATTR_XFER_BULK; + boutdesc.interval = epdesc->interval; + boutdesc.mxpacketsize = + usbhost_getle16(epdesc->mxpacketsize); + + uinfo("Bulk OUT EP addr:%d mxpacketsize:%d\n", + boutdesc.addr, boutdesc.mxpacketsize); + } + else + { + /* It is an IN bulk endpoint. There should be only one + * bulk IN endpoint. + */ + + if ((found & USBHOST_BULKIN_FOUND) != 0) + { + /* Oops.. more than one endpoint. We don't know + * what to do with this. + */ + + return -EINVAL; + } + + found |= USBHOST_BULKIN_FOUND; + + /* Save the bulk IN endpoint information */ + + bindesc.hport = hport; + bindesc.addr = epdesc->addr & + USB_EP_ADDR_NUMBER_MASK; + bindesc.in = 1; + bindesc.xfrtype = USB_EP_ATTR_XFER_BULK; + bindesc.interval = epdesc->interval; + bindesc.mxpacketsize = + usbhost_getle16(epdesc->mxpacketsize); + + uinfo("Bulk IN EP addr:%d mxpacketsize:%d\n", + bindesc.addr, bindesc.mxpacketsize); + } + } + } + break; + + /* Other descriptors are just ignored for now */ + + default: + break; + } + + /* If we found everything we need with this interface, then break out + * of the loop early. + */ + + if (found == USBHOST_ALLFOUND) + { + break; + } + + /* Increment the address of the next descriptor */ + + configdesc += desc->len; + remaining -= desc->len; + } + + /* Sanity checking... did we find all of things that we needed for the + * FT232R interface? + */ + + if (found != USBHOST_ALLFOUND) + { + uerr("ERROR: Found DATA IF:%s BULK IN:%s BULK OUT:%s\n", + (found & USBHOST_DATAIF_FOUND) != 0 ? "YES" : "NO", + (found & USBHOST_BULKIN_FOUND) != 0 ? "YES" : "NO", + (found & USBHOST_BULKOUT_FOUND) != 0 ? "YES" : "NO"); + return -EINVAL; + } + + /* We are good... Allocate the endpoints */ + + ret = DRVR_EPALLOC(hport->drvr, &boutdesc, &priv->bulkout); + if (ret < 0) + { + uerr("ERROR: Failed to allocate Bulk OUT endpoint\n"); + return ret; + } + + ret = DRVR_EPALLOC(hport->drvr, &bindesc, &priv->bulkin); + if (ret < 0) + { + uerr("ERROR: Failed to allocate Bulk IN endpoint\n"); + DRVR_EPFREE(hport->drvr, priv->bulkout); + return ret; + } + + uinfo("Endpoints allocated\n"); + return OK; +} + +/**************************************************************************** + * Name: usbhost_getle16 + * + * Description: + * Get a (possibly unaligned) 16-bit little endian value. + * + * Input Parameters: + * val - A pointer to the first byte of the little endian value. + * + * Returned Value: + * A uint16_t representing the whole 16-bit integer value + * + ****************************************************************************/ + +static inline uint16_t usbhost_getle16(FAR const uint8_t *val) +{ + return (uint16_t)val[1] << 8 | (uint16_t)val[0]; +} + +/**************************************************************************** + * Name: usbhost_getbe16 + * + * Description: + * Get a (possibly unaligned) 16-bit big endian value. + * + * Input Parameters: + * val - A pointer to the first byte of the big endian value. + * + * Returned Value: + * A uint16_t representing the whole 16-bit integer value + * + ****************************************************************************/ + +static inline uint16_t usbhost_getbe16(FAR const uint8_t *val) +{ + return (uint16_t)val[0] << 8 | (uint16_t)val[1]; +} + +/**************************************************************************** + * Name: usbhost_putle16 + * + * Description: + * Put a (possibly unaligned) 16-bit little endian value. + * + * Input Parameters: + * dest - A pointer to the first byte to save the little endian value. + * val - The 16-bit value to be saved. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_putle16(FAR uint8_t *dest, uint16_t val) +{ + dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */ + dest[1] = val >> 8; +} + +/**************************************************************************** + * Name: usbhost_alloc_buffers + * + * Description: + * Allocate transfer buffer memory. + * + * Input Parameters: + * priv - A reference to the class instance. + * + * Returned Value: + * On success, zero (OK) is returned. On failure, an negated errno value + * is returned to indicate the nature of the failure. + * + ****************************************************************************/ + +static int usbhost_alloc_buffers(FAR struct usbhost_ft232r_s *priv) +{ + FAR struct usbhost_hubport_s *hport; + size_t maxlen; + int ret; + + DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL); + hport = priv->usbclass.hport; + + /* Allocate memory for control requests */ + + ret = DRVR_ALLOC(hport->drvr, (FAR uint8_t **)&priv->ctrlreq, &maxlen); + if (ret < 0) + { + uerr("ERROR: DRVR_ALLOC of ctrlreq failed: %d\n", ret); + goto errout; + } + + DEBUGASSERT(maxlen >= sizeof(struct usb_ctrlreq_s)); + + /* Set the size of Bulk IN and OUT buffers to the max packet size */ + + priv->pktsize = (hport->speed == USB_SPEED_HIGH) ? 512 : 64; + + /* Allocate a RX buffer for Bulk IN transfers */ + + ret = DRVR_IOALLOC(hport->drvr, &priv->inbuf, priv->pktsize); + if (ret < 0) + { + uerr("ERROR: DRVR_IOALLOC of Bulk IN buffer failed: %d (%d bytes)\n", + ret, priv->pktsize); + goto errout; + } + + /* Allocate a TX buffer for Bulk IN transfers */ + + ret = DRVR_IOALLOC(hport->drvr, &priv->outbuf, priv->pktsize); + if (ret < 0) + { + uerr("ERROR: DRVR_IOALLOC of Bulk OUT buffer failed: %d (%d bytes)\n", + ret, priv->pktsize); + goto errout; + } + + return OK; + +errout: + usbhost_free_buffers(priv); + return ret; +} + +/**************************************************************************** + * Name: usbhost_free_buffers + * + * Description: + * Free transfer buffer memory. + * + * Input Parameters: + * priv - A reference to the class instance. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void usbhost_free_buffers(FAR struct usbhost_ft232r_s *priv) +{ + FAR struct usbhost_hubport_s *hport; + + DEBUGASSERT(priv != NULL && priv->usbclass.hport != NULL); + hport = priv->usbclass.hport; + + if (priv->ctrlreq) + { + DRVR_FREE(hport->drvr, priv->ctrlreq); + } + + if (priv->inbuf) + { + DRVR_IOFREE(hport->drvr, priv->inbuf); + } + + if (priv->outbuf) + { + DRVR_IOFREE(hport->drvr, priv->outbuf); + } + + priv->pktsize = 0; + priv->ctrlreq = NULL; + priv->inbuf = NULL; + priv->outbuf = NULL; +} + +/**************************************************************************** + * struct usbhost_registry_s methods + ****************************************************************************/ + +/**************************************************************************** + * Name: usbhost_create + * + * Description: + * This function implements the create() method of struct + * usbhost_registry_s. The create() method is a callback into the class + * implementation. It is used to (1) create a new instance of the USB + * host class state and to (2) bind a USB host driver "session" to the + * class instance. Use of this create() method will support environments + * where there may be multiple USB ports and multiple USB devices + * simultaneously connected. + * + * Input Parameters: + * hport - The hub port that manages the new class instance. + * id - In the case where the device supports multiple base classes, + * subclasses, or protocols, this specifies which to configure for. + * + * Returned Value: + * On success, this function will return a non-NULL instance of struct + * usbhost_class_s that can be used by the USB host driver to communicate + * with the USB host class. NULL is returned on failure; this function + * will fail only if the hport input parameter is NULL or if there are + * insufficient resources to create another USB host class instance. + * + ****************************************************************************/ + +static FAR struct usbhost_class_s * +usbhost_create(FAR struct usbhost_hubport_s *hport, + FAR const struct usbhost_id_s *id) +{ + FAR struct usbhost_ft232r_s *priv; + FAR struct uart_dev_s *uartdev; + + /* Allocate a USB host FTDI class instance */ + + priv = usbhost_allocclass(); + if (priv) + { + /* Initialize the allocated FTDI class instance */ + + memset(priv, 0, sizeof(struct usbhost_ft232r_s)); + + /* Assign a device number to this class instance */ + + if (usbhost_devno_alloc(priv) == OK) + { + /* Initialize class method function pointers */ + + priv->usbclass.hport = hport; + priv->usbclass.connect = usbhost_connect; + priv->usbclass.disconnected = usbhost_disconnected; + + /* The initial reference count is 1... + * One reference is held by the driver + */ + + priv->crefs = 1; + + /* Initialize semaphores + * (this works okay in the interrupt context) + */ + + nxsem_init(&priv->exclsem, 0, 1); + + /* Set up the serial lower-half interface */ + + uartdev = &priv->uartdev; + uartdev->recv.size = CONFIG_USBHOST_FT232R_RXBUFSIZE; + uartdev->recv.buffer = priv->rxbuffer; + uartdev->xmit.size = CONFIG_USBHOST_FT232R_TXBUFSIZE; + uartdev->xmit.buffer = priv->txbuffer; + uartdev->ops = &g_uart_ops; + uartdev->priv = priv; + + /* Set up the initial line status */ + + priv->baud = CONFIG_USBHOST_FT232R_BAUD; + priv->nbits = CONFIG_USBHOST_FT232R_BITS; + priv->parity = CONFIG_USBHOST_FT232R_PARITY; + priv->stop2 = CONFIG_USBHOST_FT232R_2STOP; + +#ifdef CONFIG_SERIAL_IFLOWCONTROL + priv->rts = true; +#endif + + /* Return the instance of the USB FTDI class */ + + return &priv->usbclass; + } + } + + /* An error occurred. Free the allocation and return NULL on all failures */ + + if (priv) + { + usbhost_freeclass(priv); + } + + return NULL; +} + +/**************************************************************************** + * struct usbhost_class_s methods + ****************************************************************************/ + +/**************************************************************************** + * Name: usbhost_connect + * + * Description: + * This function implements the connect() method of struct + * usbhost_class_s. This method is a callback into the class + * implementation. It is used to provide the device's configuration + * descriptor to the class so that the class may initialize properly + * + * Input Parameters: + * usbclass - The USB host class entry previously obtained from a call to + * create(). + * configdesc - A pointer to a uint8_t buffer container the configuration + * descriptor. + * desclen - The length in bytes of the configuration descriptor. + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * NOTE that the class instance remains valid upon return with a failure. + * It is the responsibility of the higher level enumeration logic to call + * CLASS_DISCONNECTED to free up the class driver resources. + * + * Assumptions: + * - This function will *not* be called from an interrupt handler. + * - If this function returns an error, the USB host controller driver + * must call to DISCONNECTED method to recover from the error + * + ****************************************************************************/ + +static int usbhost_connect(FAR struct usbhost_class_s *usbclass, + FAR const uint8_t *configdesc, int desclen) +{ + FAR struct usbhost_ft232r_s *priv = + (FAR struct usbhost_ft232r_s *)usbclass; + char devname[DEV_NAMELEN]; + int ret; + + DEBUGASSERT(priv != NULL && + configdesc != NULL && + desclen >= sizeof(struct usb_cfgdesc_s)); + + /* Get exclusive access to the device structure */ + + ret = usbhost_takesem(&priv->exclsem); + if (ret < 0) + { + return ret; + } + + /* Increment the reference count. This will prevent usbhost_destroy() from + * being called asynchronously if the device is removed. + */ + + priv->crefs++; + DEBUGASSERT(priv->crefs == 2); + + /* Parse the configuration descriptor to get the bulk I/O endpoints */ + + ret = usbhost_cfgdesc(priv, configdesc, desclen); + if (ret < 0) + { + uerr("ERROR: usbhost_cfgdesc() failed: %d\n", ret); + goto errout; + } + + /* Set aside a transfer buffer for exclusive use by the FTDI driver */ + + ret = usbhost_alloc_buffers(priv); + if (ret < 0) + { + uerr("ERROR: Failed to allocate transfer buffer\n"); + goto errout; + } + + /* Send the initial line encoding */ + + ret = ft232r_reset(priv, true); + if (ret < 0) + { + uerr("ERROR: ft232r_reset() failed: %d\n", ret); + } + + ret = ft232r_setflowctrl(priv); + if (ret < 0) + { + uerr("ERROR: ft232r_setflowctrl() failed: %d\n", ret); + } + + ret = ft232r_modemctrl(priv); + if (ret < 0) + { + uerr("ERROR: ft232r_modemctrl() failed: %d\n", ret); + } + + ret = ft232r_setbaud(priv); + if (ret < 0) + { + uerr("ERROR: ft232r_setbaud() failed: %d\n", ret); + } + + ret = ft232r_setdata(priv); + if (ret < 0) + { + uerr("ERROR: ft232r_setdata() failed: %d\n", ret); + } + + ret = ft232r_setlat(priv); + if (ret < 0) + { + uerr("ERROR: ft232r_setlat() failed: %d\n", ret); + } + + /* Register the lower half serial instance with the upper half serial + * driver. + */ + + usbhost_mkdevname(priv, devname); + uinfo("Register device: %s\n", devname); + + ret = uart_register(devname, &priv->uartdev); + if (ret < 0) + { + uerr("ERROR: uart_register() failed: %d\n", ret); + goto errout; + } + +errout: + /* Decrement the reference count. We incremented the reference count + * above so that usbhost_destroy() could not be called. We now have to + * be concerned about asynchronous modification of crefs because the + * serial driver has been registered. + */ + + DEBUGASSERT(priv->crefs >= 2); + priv->crefs--; + + /* Release the semaphore... there is a race condition here. + * Decrementing the reference count and releasing the semaphore + * allows usbhost_destroy() to execute (on the worker thread); + * the class driver instance could get destroyed before we are + * ready to handle it! + */ + + usbhost_givesem(&priv->exclsem); + return ret; +} + +/**************************************************************************** + * Name: usbhost_disconnected + * + * Description: + * This function implements the disconnected() method of struct + * usbhost_class_s. This method is a callback into the class + * implementation. It is used to inform the class that the USB device has + * been disconnected. + * + * Input Parameters: + * usbclass - The USB host class entry previously obtained from a call to + * create(). + * + * Returned Value: + * On success, zero (OK) is returned. On a failure, a negated errno value + * is returned indicating the nature of the failure + * + * Assumptions: + * This function may be called from an interrupt handler. + * + ****************************************************************************/ + +static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass) +{ + FAR struct usbhost_ft232r_s *priv = + (FAR struct usbhost_ft232r_s *)usbclass; + irqstate_t flags; + + DEBUGASSERT(priv != NULL); + + /* Set an indication to any users of the FTDI device that the device + * is no longer available. + */ + + flags = enter_critical_section(); + priv->disconnected = true; + + /* Let the upper half driver know that serial device is no longer + * connected. + */ + + uart_connected(&priv->uartdev, false); + + /* Now check the number of references on the class instance. If it is one, + * then we can free the class instance now. Otherwise, we will have to + * wait until the holders of the references free them by closing the + * serial driver. + */ + + uinfo("crefs: %d\n", priv->crefs); + if (priv->crefs == 1) + { + /* Destroy the class instance. If we are executing from an interrupt + * handler, then defer the destruction to the worker thread. + * Otherwise, destroy the instance now. + */ + + if (up_interrupt_context()) + { + /* Destroy the instance on the worker thread. */ + + uinfo("Queuing destruction: worker %p->%p\n", + priv->ntwork.worker, usbhost_destroy); + + DEBUGASSERT(work_available(&priv->ntwork)); + work_queue(HPWORK, &priv->ntwork, usbhost_destroy, priv, 0); + } + else + { + /* Do the work now */ + + usbhost_destroy(priv); + } + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Serial Lower-Half Interfaces + ****************************************************************************/ + +/**************************************************************************** + * Name: usbhost_setup + * + * Description: + * Configure the USART baud, bits, parity, etc. This method is called the + * first time that the serial port is opened. + * + ****************************************************************************/ + +static int usbhost_setup(FAR struct uart_dev_s *uartdev) +{ + FAR struct usbhost_ft232r_s *priv; + irqstate_t flags; + int ret; + + uinfo("Entry\n"); + DEBUGASSERT(uartdev && uartdev->priv); + priv = (FAR struct usbhost_ft232r_s *)uartdev->priv; + + /* Make sure that we have exclusive access to the private data structure */ + + DEBUGASSERT(priv->crefs > 0 && priv->crefs < USBHOST_MAX_CREFS); + ret = usbhost_takesem(&priv->exclsem); + if (ret < 0) + { + return ret; + } + + /* Check if the FTDI device is still connected. We need to disable + * interrupts momentarily to assure that there are no asynchronous + * isconnect events. + */ + + flags = enter_critical_section(); + if (priv->disconnected) + { + /* No... the block driver is no longer bound to the class. That means + * that the USB FTDI device is no longer connected. Refuse any + * further attempts to open the driver. + */ + + ret = -ENODEV; + } + else + { + /* Otherwise, just increment the reference count on the driver */ + + priv->crefs++; + ret = OK; + } + + leave_critical_section(flags); + usbhost_givesem(&priv->exclsem); + return ret; +} + +/**************************************************************************** + * Name: usbhost_shutdown + * + * Description: + * Disable the USART. This method is called when the serial + * port is closed + * + ****************************************************************************/ + +static void usbhost_shutdown(FAR struct uart_dev_s *uartdev) +{ + FAR struct usbhost_ft232r_s *priv; + irqstate_t flags; + + uinfo("Entry\n"); + DEBUGASSERT(uartdev && uartdev->priv); + priv = (FAR struct usbhost_ft232r_s *)uartdev->priv; + + /* Decrement the reference count on the block driver */ + + DEBUGASSERT(priv->crefs > 1); + usbhost_forcetake(&priv->exclsem); + priv->crefs--; + + /* Release the semaphore. The following operations when crefs == 1 are + * safe because we know that there is no outstanding open references to + * the block driver. + */ + + usbhost_givesem(&priv->exclsem); + + /* We need to disable interrupts momentarily to assure that there are + * no asynchronous disconnect events. + */ + + flags = enter_critical_section(); + + /* Check if the USB FTDI device is still connected. If the + * FTDI device is not connected and the reference count just + * decremented to one, then unregister the block driver and free + * the class instance. + */ + + if (priv->crefs <= 1 && priv->disconnected) + { + /* Destroy the class instance */ + + DEBUGASSERT(priv->crefs == 1); + usbhost_destroy(priv); + } + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: usbhost_attach + * + * Description: + * Configure the USART to operation in interrupt driven mode. This method + * is called when the serial port is opened. Normally, this is just after + * the setup() method is called, however, the serial console may operate in + * a non-interrupt driven mode during the boot phase. + * + * RX and TX interrupts are not enabled when by the attach method (unless + * the hardware supports multiple levels of interrupt enabling). The RX + * and TX interrupts are not enabled until the txint() and rxint() methods + * are called. + * + ****************************************************************************/ + +static int usbhost_attach(FAR struct uart_dev_s *uartdev) +{ + return OK; +} + +/**************************************************************************** + * Name: usbhost_detach + * + * Description: + * Detach USART interrupts. This method is called when the serial port is + * closed normally just before the shutdown method is called. The + * exception is the serial console which is never shutdown. + * + ****************************************************************************/ + +static void usbhost_detach(FAR struct uart_dev_s *uartdev) +{ +} + +/**************************************************************************** + * Name: usbhost_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + ****************************************************************************/ + +static int usbhost_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode; + FAR struct usbhost_ft232r_s *priv; + FAR struct uart_dev_s *uartdev; + int ret = 0; + + uinfo("Entry\n"); + DEBUGASSERT(filep && filep->f_inode); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + uartdev = (FAR struct uart_dev_s *)inode->i_private; + + DEBUGASSERT(uartdev && uartdev->priv); + priv = (FAR struct usbhost_ft232r_s *)uartdev->priv; + + /* Check if the FTDI device is still connected */ + + if (priv->disconnected) + { + /* No... the serial device has been disconnecgted. Refuse to process + * any ioctl commands. + */ + + ret = -ENODEV; + } + else + { + /* Process the IOCTL by command */ + + ret = usbhost_takesem(&priv->exclsem); + if (ret < 0) + { + return ret; + } + + switch (cmd) + { +#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT + case TIOCSERGSTRUCT: + { + FAR struct usbhost_ft232r_s *user = + (FAR struct usbhost_ft232r_s *)arg; + if (!user) + { + ret = -EINVAL; + } + else + { + memcpy(user, uartdev, sizeof(struct usbhost_ft232r_s)); + } + } + break; +#endif + +#ifdef CONFIG_SERIAL_TERMIOS + case TCGETS: + { + FAR struct termios *termiosp = (FAR struct termios *)arg; + + if (!termiosp) + { + ret = -EINVAL; + break; + } + + cfsetispeed(termiosp, priv->baud); + + termiosp->c_cflag = + ((priv->parity != 0) ? PARENB : 0) | + ((priv->parity == 1) ? PARODD : 0) | + ((priv->stop2) ? CSTOPB : 0) +#ifdef CONFIG_SERIAL_OFLOWCONTROL + | ((priv->oflow) ? CCTS_OFLOW : 0) +#endif +#ifdef CONFIG_SERIAL_IFLOWCONTROL + | ((priv->iflow) ? CRTS_IFLOW : 0) +#endif + ; + + switch (priv->nbits) + { + case 7: + termiosp->c_cflag |= CS7; + break; + + default: + case 8: + termiosp->c_cflag |= CS8; + break; + } + } + break; + + case TCSETS: + { + FAR struct termios *termiosp = (FAR struct termios *)arg; +#ifdef CONFIG_SERIAL_IFLOWCONTROL + bool iflow; +#endif + + if (!termiosp) + { + ret = -EINVAL; + break; + } + + if (termiosp->c_cflag & PARENB) + { + priv->parity = (termiosp->c_cflag & PARODD) ? 1 : 2; + } + else + { + priv->parity = 0; + } + + priv->stop2 = (termiosp->c_cflag & CSTOPB) != 0; +#ifdef CONFIG_SERIAL_OFLOWCONTROL + priv->oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0; +#endif +#ifdef CONFIG_SERIAL_IFLOWCONTROL + iflow = priv->iflow; + priv->iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0; +#endif + + switch (termiosp->c_cflag & CSIZE) + { + case CS7: + priv->nbits = 7; + break; + + default: + case CS8: + priv->nbits = 8; + break; + } + + /* Note that only cfgetispeed is used because we have knowledge + * that only one speed is supported. + */ + + priv->baud = cfgetispeed(termiosp); + +#ifdef CONFIG_SERIAL_IFLOWCONTROL + /* Set RTS if input flow control changed */ + + if (iflow != !priv->iflow) + { + priv->rts = true; + } +#endif + + ret = ft232r_setbaud(priv); + if (ret < 0) + { + uerr("ERROR: ft232r_setbaud failed: %d\n", ret); + } + + ret = ft232r_setdata(priv); + if (ret < 0) + { + uerr("ERROR: ft232r_setdata failed: %d\n", ret); + } + + ret = ft232r_setflowctrl(priv); + if (ret < 0) + { + uerr("ERROR: ft232r_setflowctrl failed: %d\n", ret); + } + } + + break; + +#endif /* CONFIG_SERIAL_TERMIOS */ + + default: + ret = -ENOTTY; + break; + } + + usbhost_givesem(&priv->exclsem); + } + + return ret; +} + +/**************************************************************************** + * Name: usbhost_rxint + * + * Description: + * Call to enable or disable RX interrupts + * + ****************************************************************************/ + +static void usbhost_rxint(FAR struct uart_dev_s *uartdev, bool enable) +{ + FAR struct usbhost_ft232r_s *priv; + int ret; + + DEBUGASSERT(uartdev && uartdev->priv); + priv = (FAR struct usbhost_ft232r_s *)uartdev->priv; + + /* Are we enabling or disabling RX reception? */ + + if (enable && !priv->rxena) + { + /* Cancel any pending, delayed RX data reception work */ + + work_cancel(LPWORK, &priv->rxwork); + + /* Restart immediate RX data reception work (unless RX flow control + * is in effect. + */ + +#ifdef CONFIG_SERIAL_IFLOWCONTROL + if (priv->rts) +#endif + { + ret = work_queue(LPWORK, &priv->rxwork, + usbhost_rxdata_work, priv, 0); + DEBUGASSERT(ret >= 0); + UNUSED(ret); + } + } + else if (!enable && priv->rxena) + { + /* Cancel any pending RX data reception work */ + + work_cancel(LPWORK, &priv->rxwork); + } + + /* Save the new RX enable state */ + + priv->rxena = enable; +} + +/**************************************************************************** + * Name: usbhost_rxavailable + * + * Description: + * Return true if the receive buffer is not empty + * + ****************************************************************************/ + +static bool usbhost_rxavailable(FAR struct uart_dev_s *uartdev) +{ + FAR struct usbhost_ft232r_s *priv; + + DEBUGASSERT(uartdev && uartdev->priv); + priv = (FAR struct usbhost_ft232r_s *)uartdev->priv; + return (priv->nrxbytes > 0); +} + +/**************************************************************************** + * Name: usbhost_rxflowcontrol + * + * Description: + * Called when Rx buffer is full (or exceeds configured watermark levels + * if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined). + * Return true if UART activated RX flow control to block more incoming + * data + * + * Input Parameters: + * uartdev - UART device instance + * nbuffered - the number of characters currently buffered + * (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is + * not defined the value will be 0 for an empty buffer or the + * defined buffer size for a full buffer) + * upper - true indicates the upper watermark was crossed where + * false indicates the lower watermark has been crossed + * + * Returned Value: + * true if RX flow control activated. + * + ****************************************************************************/ + +#ifdef CONFIG_SERIAL_IFLOWCONTROL +static bool usbhost_rxflowcontrol(FAR struct uart_dev_s *uartdev, + unsigned int nbuffered, bool upper) +{ + FAR struct usbhost_ft232r_s *priv; + bool newrts; + int ret; + + DEBUGASSERT(uartdev && uartdev->priv); + priv = (FAR struct usbhost_ft232r_s *)uartdev->priv + + /* Is RX flow control enabled? */ + + if (!priv->iflow) + { + /* Now.. make sure that RTS is set */ + + priv->rts = true; + return false; + } + + /* Are we enabling or disabling RX flow control? */ + + if (priv->rts && upper) + { + /* RX flow control is not in effect (RTS is true) but we have just + * crossed the upper threshold, meaning that we should now clear + * RTS. + */ + + priv->rts = false; + + /* Cancel any pending RX data reception work */ + + work_cancel(LPWORK, &priv->rxwork); + return true; + } + else if (!priv->rts && !upper) + { + /* RX flow control is in effect (RTS is false) and we have just + * crossed the lower threshold, meaning that we should now set + * RTS. + */ + + priv->rts = true; + + /* Restart RX data reception work flow unless RX reception is + * disabled. + */ + + if (priv->rxena && work_available(&priv->rxwork)) + { + ret = work_queue(LPWORK, &priv->rxwork, + usbhost_rxdata_work, priv, 0); + DEBUGASSERT(ret >= 0); + UNUSED(ret); + } + + return false; + } +} +#endif + +/**************************************************************************** + * Name: usbhost_txint + * + * Description: + * Call to enable or disable TX interrupts + * + ****************************************************************************/ + +static void usbhost_txint(FAR struct uart_dev_s *uartdev, bool enable) +{ + FAR struct usbhost_ft232r_s *priv; + int ret; + + DEBUGASSERT(uartdev && uartdev->priv); + priv = (FAR struct usbhost_ft232r_s *)uartdev->priv; + + /* Are we enabling or disabling TX transmission? */ + + if (enable && !priv->txena) + { + /* Cancel any pending, delayed TX data transmission work */ + + work_cancel(LPWORK, &priv->txwork); + + /* Restart immediate TX data transmission work */ + + ret = work_queue(LPWORK, &priv->txwork, + usbhost_txdata_work, priv, 0); + DEBUGASSERT(ret >= 0); + UNUSED(ret); + } + else if (!enable && priv->txena) + { + /* Cancel any pending TX data transmission work */ + + work_cancel(LPWORK, &priv->txwork); + } + + /* Save the new TX enable state */ + + priv->txena = enable; +} + +/**************************************************************************** + * Name: usbhost_txready + * + * Description: + * Return true if the transmit data register is not full. + * + ****************************************************************************/ + +static bool usbhost_txready(FAR struct uart_dev_s *uartdev) +{ + return true; +} + +/**************************************************************************** + * Name: usbhost_txempty + * + * Description: + * Return true if the transmit data buffer is empty + * + ****************************************************************************/ + +static bool usbhost_txempty(FAR struct uart_dev_s *uartdev) +{ + return true; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: usbhost_ft232r_initialize + * + * Description: + * Initialize the USB host FTDI. This function should be called + * by platform-specific code in order to initialize and register support + * for the FT232. + * + * Input Parameters: + * None + * + * Returned Value: + * On success this function will return zero (OK); A negated errno value + * will be returned on failure. + * + ****************************************************************************/ + +int usbhost_ft232r_initialize(void) +{ + /* If we have been configured to use pre-allocated FTDI class instances, + * then place all of the pre-allocated USB host FTDI class instances + * into a free list. + */ + +#if CONFIG_USBHOST_FT232R_NPREALLOC > 0 + FAR struct usbhost_freestate_s *entry; + int i; + + g_freelist = NULL; + for (i = 0; i < CONFIG_USBHOST_FT232R_NPREALLOC; i++) + { + entry = (FAR struct usbhost_freestate_s *)&g_prealloc[i]; + entry->flink = g_freelist; + g_freelist = entry; + } +#endif + + /* Advertise our availability to support (certain) FTDI devices */ + + return usbhost_registerclass(&g_ft232r); +} + +#endif /* CONFIG_USBHOST_FT232R */ diff --git a/include/nuttx/usb/usbhost.h b/include/nuttx/usb/usbhost.h index 7baf5d29d5..b1f06fd488 100644 --- a/include/nuttx/usb/usbhost.h +++ b/include/nuttx/usb/usbhost.h @@ -77,12 +77,12 @@ * Name: CLASS_CREATE * * Description: - * This macro will call the create() method of struct usbhost_registry_s. The create() - * method is a callback into the class implementation. It is used to (1) create - * a new instance of the USB host class state and to (2) bind a USB host driver - * "session" to the class instance. Use of this create() method will support - * environments where there may be multiple USB ports and multiple USB devices - * simultaneously connected. + * This macro will call the create() method of struct usbhost_registry_s. + * The create() method is a callback into the class implementation. It is used + * to (1) create a new instance of the USB host class state and to (2) bind a + * USB host driver "session" to the class instance. Use of this create() method + * will support environments where there may be multiple USB ports and multiple + * USB devices simultaneously connected. * * Input Parameters: * reg - The USB host class registry entry previously obtained from a call to @@ -112,14 +112,16 @@ * * Description: * This macro will call the connect() method of struct usbhost_class_s. This - * method is a callback into the devclass implementation. It is used to provide the - * device's configuration descriptor to the devclass so that the devclass may initialize - * properly + * method is a callback into the devclass implementation. It is used to provide + * the device's configuration descriptor to the devclass so that the devclass may + * initialize properly. * * Input Parameters: - * devclass - The USB host class entry previously obtained from a call to create(). - * configdesc - A pointer to a uint8_t buffer container the configuration descriptor. - * desclen - The length in bytes of the configuration descriptor. + * devclass - The USB host class entry previously obtained from a call + * to create(). + * configdesc - A pointer to a uint8_t buffer containing the configuration + * descriptor. + * desclen - The length in bytes of the configuration descriptor. * * Returned Value: * On success, zero (OK) is returned. On a failure, a negated errno value is @@ -163,7 +165,7 @@ #define CLASS_DISCONNECTED(devclass) ((devclass)->disconnected(devclass)) -/**************************************************************************** +/************************************************************************************ * Name: CONN_WAIT * * Description: @@ -186,7 +188,7 @@ * - Called from a single thread so no mutual exclusion is required. * - Never called from an interrupt handler. * - ****************************************************************************/ + ************************************************************************************/ #define CONN_WAIT(conn,hport) ((conn)->wait(conn,hport)) @@ -644,8 +646,8 @@ struct usbhost_id_s * connected to the USB port. */ -struct usbhost_hubport_s; /* Forward reference to the hub state structure */ -struct usbhost_class_s; /* Forward reference to the class state structure */ +struct usbhost_hubport_s; /* Forward reference to the hub state structure */ +struct usbhost_class_s; /* Forward reference to the class state structure */ struct usbhost_registry_s { /* This field is used to implement a singly-link registry structure. Because of @@ -730,9 +732,9 @@ struct usbhost_roothubport_s struct usbhost_class_s { - /* Class instances are associated with devices connected on one port on a - * hub and are represented by this structure. - */ + /* Class instances are associated with devices connected on one port on a + * hub and are represented by this structure. + */ FAR struct usbhost_hubport_s *hport; /* The port used by this class instance */ @@ -985,7 +987,7 @@ const struct usbhost_registry_s * usbhost_findclass(FAR const struct usbhost_id_s *id); #ifdef CONFIG_USBHOST_HUB - /**************************************************************************** + /*********************************************************************************** * Name: usbhost_hub_initialize * * Description: @@ -1000,13 +1002,13 @@ const struct usbhost_registry_s * * On success this function will return zero (OK); A negated errno value * will be returned on failure. * - ****************************************************************************/ + ************************************************************************************/ int usbhost_hub_initialize(void); #endif #ifdef CONFIG_USBHOST_MSC -/**************************************************************************** +/************************************************************************************ * Name: usbhost_msc_initialize * * Description: @@ -1021,13 +1023,13 @@ int usbhost_hub_initialize(void); * On success this function will return zero (OK); A negated errno value * will be returned on failure. * - ****************************************************************************/ + ************************************************************************************/ int usbhost_msc_initialize(void); #endif #ifdef CONFIG_USBHOST_CDCACM -/**************************************************************************** +/************************************************************************************ * Name: usbhost_cdcacm_initialize * * Description: @@ -1042,13 +1044,34 @@ int usbhost_msc_initialize(void); * On success this function will return zero (OK); A negated errno value * will be returned on failure. * - ****************************************************************************/ + ************************************************************************************/ int usbhost_cdcacm_initialize(void); #endif +#ifdef CONFIG_USBHOST_FT232R +/************************************************************************************ + * Name: usbhost_ft232r_initialize + * + * Description: + * Initialize the USB FT232R driver. This function should be called + * be platform-specific code in order to initialize and register support + * for the FT232R. + * + * Input Parameters: + * None + * + * Returned Value: + * On success this function will return zero (OK); A negated errno value + * will be returned on failure. + * + ************************************************************************************/ + +int usbhost_ft232r_initialize(void); +#endif + #ifdef CONFIG_USBHOST_HIDKBD -/**************************************************************************** +/************************************************************************************ * Name: usbhost_kbdinit * * Description: @@ -1063,13 +1086,13 @@ int usbhost_cdcacm_initialize(void); * On success this function will return zero (OK); A negated errno value * will be returned on failure. * - ****************************************************************************/ + ************************************************************************************/ int usbhost_kbdinit(void); #endif #ifdef CONFIG_USBHOST_HIDMOUSE -/**************************************************************************** +/************************************************************************************ * Name: usbhost_mouse_init * * Description: @@ -1084,13 +1107,13 @@ int usbhost_kbdinit(void); * On success this function will return zero (OK); A negated errno value * will be returned on failure. * - ****************************************************************************/ + ************************************************************************************/ int usbhost_mouse_init(void); #endif #ifdef CONFIG_USBHOST_XBOXCONTROLLER -/**************************************************************************** +/************************************************************************************ * Name: usbhost_xboxcontroller_init * * Description: @@ -1105,12 +1128,12 @@ int usbhost_mouse_init(void); * On success this function will return zero (OK); A negated errno value * will be returned on failure. * - ****************************************************************************/ + ************************************************************************************/ int usbhost_xboxcontroller_init(void); #endif -/**************************************************************************** +/************************************************************************************ * Name: usbhost_wlaninit * * Description: @@ -1125,11 +1148,11 @@ int usbhost_xboxcontroller_init(void); * On success this function will return zero (OK); A negated errno value * will be returned on failure. * - ****************************************************************************/ + ************************************************************************************/ int usbhost_wlaninit(void); -/**************************************************************************** +/************************************************************************************ * Name: usbhost_enumerate * * Description: @@ -1161,7 +1184,7 @@ int usbhost_wlaninit(void); * - Called from a single thread so no mutual exclusion is required. * - Never called from an interrupt handler. * - ****************************************************************************/ + ************************************************************************************/ int usbhost_enumerate(FAR struct usbhost_hubport_s *hub, FAR struct usbhost_class_s **devclass);