From 214aeeff57e883ea084ad6d934d7f9a0eddfbc64 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 7 May 2015 11:16:03 -0600 Subject: [PATCH] USB host CDC/ACM: Add a bulk-only mode mostly for test, fix two bad assertions. Correct some loop termination logic in RX data receipt --- drivers/usbhost/Kconfig | 13 +++++ drivers/usbhost/usbhost_cdcacm.c | 98 ++++++++++++++++++++++++++------ 2 files changed, 93 insertions(+), 18 deletions(-) diff --git a/drivers/usbhost/Kconfig b/drivers/usbhost/Kconfig index b068020165..71706f48e9 100644 --- a/drivers/usbhost/Kconfig +++ b/drivers/usbhost/Kconfig @@ -96,6 +96,19 @@ config USBHOST_CDCACM if USBHOST_CDCACM +config USBHOST_CDCACM_BULKONLY + bool "Bulk only protocol" + default n + ---help--- + Support only the CDC/ACM data interface (bulk in + bulk out). This + works with devices that have no additional control interface and + even if the device provides the control interface, it will not be + used. + + By default, both the CDC/ACM compliant protocol and the bulk only + protocol are supported. This option is then most useful for testing + purposes. + config USBHOST_CDCACM_NTDELAY int "CDC/ACM notification polling interval (MSec)" default 400 diff --git a/drivers/usbhost/usbhost_cdcacm.c b/drivers/usbhost/usbhost_cdcacm.c index 36373aafa2..c1f1a53f6c 100644 --- a/drivers/usbhost/usbhost_cdcacm.c +++ b/drivers/usbhost/usbhost_cdcacm.c @@ -163,24 +163,30 @@ * defined here so that it will be used consistently in all places. */ -#define DEV_FORMAT "/dev/ttyACM%d" -#define DEV_NAMELEN 16 +#define DEV_FORMAT "/dev/ttyACM%d" +#define DEV_NAMELEN 16 -#define MAX_NOTIFICATION 32 +#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_CTRLIF_FOUND 0x08 /* Control interface found */ -#define USBHOST_INTIN_FOUND 0x10 /* Interrupt IN interface found */ +#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_MINFOUND 0x07 /* Minimum things needed */ -#define USBHOST_HAVE_CTRLIF 0x18 /* Needed for control interface */ -#define USBHOST_ALLFOUND 0x1f /* All configuration things */ +#ifdef CONFIG_USBHOST_CDCACM_BULKONLY +# define USBHOST_MINFOUND 0x07 /* Minimum things needed */ +# define USBHOST_ALLFOUND 0x07 /* All configuration things */ +#else +# define USBHOST_CTRLIF_FOUND 0x08 /* Control interface found */ +# define USBHOST_INTIN_FOUND 0x10 /* Interrupt IN interface found */ -#define USBHOST_MAX_CREFS INT16_MAX /* Max cref count before signed overflow */ +# define USBHOST_MINFOUND 0x07 /* Minimum things needed */ +# define USBHOST_HAVE_CTRLIF 0x18 /* Needed for control interface */ +# define USBHOST_ALLFOUND 0x1f /* All configuration things */ +#endif + +#define USBHOST_MAX_CREFS INT16_MAX /* Max cref count before signed overflow */ /**************************************************************************** * Private Types @@ -223,7 +229,9 @@ struct usbhost_cdcacm_s #endif uint8_t minor; /* Minor number identifying the /dev/ttyACM[n] device */ uint8_t dataif; /* Data interface number */ +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY uint8_t ctrlif; /* Control interface number */ +#endif uint8_t nbits; /* Number of bits (for line encoding) */ uint8_t parity; /* Parity (for line encoding) */ uint16_t pktsize; /* Allocated size of transfer buffers */ @@ -243,7 +251,9 @@ struct usbhost_cdcacm_s uint32_t baud; /* Current baud for line coding */ usbhost_ep_t bulkin; /* Bulk IN endpoint */ usbhost_ep_t bulkout; /* Bulk OUT endpoint */ +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY usbhost_ep_t intin; /* Interrupt IN endpoint (optional) */ +#endif /* This is the serial data buffer */ @@ -281,9 +291,11 @@ static inline void usbhost_mkdevname(FAR struct usbhost_cdcacm_s *priv, /* CDC/ACM request helpers */ +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY static int usbhost_linecoding_send(FAR struct usbhost_cdcacm_s *priv); static void usbhost_notification_work(FAR void *arg); static void usbhost_notification_callback(FAR void *arg, ssize_t nbytes); +#endif /* UART buffer data transfer */ @@ -304,7 +316,9 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv, static inline uint16_t usbhost_getle16(const uint8_t *val); static inline uint16_t usbhost_getbe16(const uint8_t *val); static inline void usbhost_putle16(uint8_t *dest, uint16_t val); +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY static void usbhost_putle32(uint8_t *dest, uint32_t val); +#endif /* Transfer descriptor memory management */ @@ -610,6 +624,7 @@ static inline void usbhost_mkdevname(FAR struct usbhost_cdcacm_s *priv, * ****************************************************************************/ +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY static int usbhost_linecoding_send(FAR struct usbhost_cdcacm_s *priv) { FAR struct usbhost_hubport_s *hport; @@ -648,6 +663,7 @@ static int usbhost_linecoding_send(FAR struct usbhost_cdcacm_s *priv) return ret; } +#endif /**************************************************************************** * Name: usbhost_notification_work @@ -666,6 +682,7 @@ static int usbhost_linecoding_send(FAR struct usbhost_cdcacm_s *priv) * ****************************************************************************/ +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY static void usbhost_notification_work(FAR void *arg) { FAR struct usbhost_cdcacm_s *priv; @@ -735,6 +752,7 @@ static void usbhost_notification_work(FAR void *arg) } } } +#endif /**************************************************************************** * Name: usbhost_notification_callback @@ -755,6 +773,7 @@ static void usbhost_notification_work(FAR void *arg) * ****************************************************************************/ +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY static void usbhost_notification_callback(FAR void *arg, ssize_t nbytes) { FAR struct usbhost_cdcacm_s *priv = (FAR struct usbhost_cdcacm_s *)arg; @@ -775,7 +794,7 @@ static void usbhost_notification_callback(FAR void *arg, ssize_t nbytes) * polling. */ - DEBUGASSERT(nbytes >= -INT16_MIN && nbytes <= INT16_MAX); + DEBUGASSERT(nbytes >= INT16_MIN && nbytes <= INT16_MAX); priv->nbytes = (int16_t)nbytes; if (nbytes < 0) @@ -815,6 +834,7 @@ static void usbhost_notification_callback(FAR void *arg, ssize_t nbytes) } } } +#endif /************************************************************************************ * UART buffer data transfer @@ -972,10 +992,10 @@ static void usbhost_rxdata_work(FAR void *arg) int rxndx; int ret; - priv = (FAR struct usbhost_cdcacm_s *)arg; + priv = (FAR struct usbhost_cdcacm_s *)arg; DEBUGASSERT(priv); - hport = priv->usbclass.hport; + hport = priv->usbclass.hport; DEBUGASSERT(hport); uartdev = &priv->uartdev; @@ -1050,7 +1070,13 @@ static void usbhost_rxdata_work(FAR void *arg) break; } - DEBUGASSERT(nread > 0 && nread <= priv->pktsize); + /* 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. + */ + + DEBUGASSERT(nread <= priv->pktsize); priv->nrxbytes = (uint16_t)nread; rxndx = 0; @@ -1072,13 +1098,28 @@ static void usbhost_rxdata_work(FAR void *arg) rxbuf->head = nexthead; priv->rxndx = rxndx; - /* Update the indices for the next pass through the loop */ + /* 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. + */ - rxndx++; 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. 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. + */ + + if (++rxndx >= priv->nrxbytes) + { + priv->nrxbytes = 0; + } } /* We break out to here if either: 1) the UART RX buffer is full or, @@ -1166,10 +1207,12 @@ static void usbhost_destroy(FAR void *arg) DRVR_EPFREE(hport->drvr, priv->bulkin); } +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY if (priv->intin) { DRVR_EPFREE(hport->drvr, priv->intin); } +#endif /* Free any transfer buffers */ @@ -1299,6 +1342,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv, found |= USBHOST_DATAIF_FOUND; currif = USBHOST_DATAIF_FOUND; } +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY else if (ifdesc->classid == USB_CLASS_CDC && (found & USBHOST_CTRLIF_FOUND) == 0) { @@ -1309,6 +1353,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv, priv->ctrlif = ifdesc->ifno; currif = USBHOST_CTRLIF_FOUND; } +#endif else { /* Its something else */ @@ -1397,6 +1442,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv, } } +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY /* Check for an interrupt IN endpoint. */ else if (currif == USBHOST_CTRLIF_FOUND && @@ -1438,6 +1484,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv, boutdesc.addr, boutdesc.mxpacketsize); } } +#endif } break; @@ -1493,6 +1540,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv, return ret; } +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY /* The control interface with interrupt IN endpoint is optional */ if ((found & USBHOST_HAVE_CTRLIF) == USBHOST_HAVE_CTRLIF) @@ -1504,6 +1552,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv, priv->intin = NULL; } } +#endif uvdbg("Endpoints allocated\n"); return OK; @@ -1583,6 +1632,7 @@ static void usbhost_putle16(uint8_t *dest, uint16_t val) * ****************************************************************************/ +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY static void usbhost_putle32(uint8_t *dest, uint32_t val) { /* Little endian means LS halfword first in byte stream */ @@ -1590,6 +1640,7 @@ static void usbhost_putle32(uint8_t *dest, uint32_t val) usbhost_putle16(dest, (uint16_t)(val & 0xffff)); usbhost_putle16(dest+2, (uint16_t)(val >> 16)); } +#endif /**************************************************************************** * Name: usbhost_alloc_buffers @@ -1638,6 +1689,7 @@ static int usbhost_alloc_buffers(FAR struct usbhost_cdcacm_s *priv) goto errout; } +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY /* Allocate (optional) buffer for receiving line status data. */ if (priv->intin) @@ -1650,6 +1702,7 @@ static int usbhost_alloc_buffers(FAR struct usbhost_cdcacm_s *priv) goto errout; } } +#endif /* Set the size of Bulk IN and OUT buffers to the max packet size */ @@ -1915,6 +1968,7 @@ static int usbhost_connect(FAR struct usbhost_class_s *usbclass, goto errout; } +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY /* Send the initial line encoding */ ret = usbhost_linecoding_send(priv); @@ -1922,11 +1976,14 @@ static int usbhost_connect(FAR struct usbhost_class_s *usbclass, { udbg("usbhost_linecoding_send() failed: %d\n", ret); } +#endif /* Register the lower half serial instance with the upper half serial * driver */ usbhost_mkdevname(priv, devname); + uvdbg("Register device: %s\n", devname); + ret = uart_register(devname, &priv->uartdev); if (ret < 0) { @@ -1934,12 +1991,14 @@ static int usbhost_connect(FAR struct usbhost_class_s *usbclass, goto errout; } +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY /* Do we have an interrupt IN endpoint? */ if (priv->intin) { /* Begin monitoring of port status change events */ + uvdbg("Start notification monitoring\n"); ret = DRVR_ASYNCH(hport->drvr, priv->intin, (FAR uint8_t *)priv->notification, MAX_NOTIFICATION, usbhost_notification_callback, @@ -1949,6 +2008,7 @@ static int usbhost_connect(FAR struct usbhost_class_s *usbclass, udbg("ERROR: DRVR_ASYNCH failed: %d\n", ret); } } +#endif errout: /* Decrement the reference count. We incremented the reference count @@ -2354,11 +2414,13 @@ static int usbhost_ioctl(FAR struct file *filep, int cmd, unsigned long arg) } #endif +#ifndef CONFIG_USBHOST_CDCACM_BULKONLY /* Effect the changes immediately - note that we do not implement * TCSADRAIN / TCSAFLUSH */ ret = usbhost_linecoding_send(priv); +#endif } break; #endif /* CONFIG_SERIAL_TERMIOS */