forked from nuttx/nuttx-update
Add options to support all three host CDC/ACM protocols: Compliant, Reduced, and Bulk-Only
This commit is contained in:
parent
bd437bfb03
commit
46e5d0a419
4 changed files with 110 additions and 37 deletions
11
TODO
11
TODO
|
@ -1104,8 +1104,8 @@ o USB (drivers/usbdev, drivers/usbhost)
|
|||
are several unresolved issues that prevent the host driver
|
||||
from being usable:
|
||||
|
||||
- The driver works fine when configured for BULK-ONLY
|
||||
operation.
|
||||
- The driver works fine when configured for reduced or bulk-
|
||||
only protocol.
|
||||
- Testing has not been performed with the interrupt IN channel
|
||||
enabled (ie., I have not enabled FLOW control nor do I have
|
||||
a test case that used the interrupt IN channel). I can see
|
||||
|
@ -1125,8 +1125,13 @@ o USB (drivers/usbdev, drivers/usbhost)
|
|||
- When the cable connecting the STM3110E-EVAL device is
|
||||
removed, the software on the Olimex LPC1766STK crashes. The
|
||||
crash is due to corruption of the OHCI bDoneHead pointer.
|
||||
This might be an related to the use of 'cat /dev/ttyACM0'?
|
||||
|
||||
Most of these problems are unique to the Olimex LPC1766STK DCD; some are probably design problems in the CDC/ACM host driver. The bottom line is that the host CDC/ACM driver is immature and you should probably not use it unless you want to invest some effort.
|
||||
Most of these problems are unique to the Olimex LPC1766STK
|
||||
DCD; some are probably design problems in the CDC/ACM host
|
||||
driver. The bottom line is that the host CDC/ACM driver is
|
||||
immature and you should probably not use it unless you want
|
||||
to invest some effort.
|
||||
Status: Open
|
||||
Priority: Medium-Low unless you really need host CDC/ACM support.
|
||||
|
||||
|
|
|
@ -693,6 +693,7 @@ static int cdcacm_setconfig(FAR struct cdcacm_dev_s *priv, uint8_t config)
|
|||
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_EPINTINCONFIGFAIL), 0);
|
||||
goto errout;
|
||||
}
|
||||
|
||||
priv->epintin->priv = priv;
|
||||
|
||||
/* Configure the IN bulk endpoint */
|
||||
|
|
|
@ -96,18 +96,60 @@ config USBHOST_CDCACM
|
|||
|
||||
if USBHOST_CDCACM
|
||||
|
||||
choice
|
||||
prompt "CDC/ACM Protocol"
|
||||
default USBHOST_CDCACM_COMPLIANT if SERIAL_OFLOWCONTROL && !USBHOST_INT_DISABLE
|
||||
default USBHOST_CDCACM_REDUCED if !SERIAL_OFLOWCONTROL || USBHOST_INT_DISABLE
|
||||
|
||||
config USBHOST_CDCACM_COMPLIANT
|
||||
bool "CDC/ACM compliant"
|
||||
depends on SERIAL_OFLOWCONTROL && !USBHOST_INT_DISABLE
|
||||
---help---
|
||||
The device needs to implement the Abstract Control Model (ACM)
|
||||
subclass defined in the USB Communication Device Class (CDC)
|
||||
specification. This protocol requires two USB interfaces, a data
|
||||
interface which consists of two endpoints (bulk in + bulk out) and a
|
||||
control interface which consists of one interrupt in endpoint.
|
||||
Furthermore, on EP0 the device must implement the class-specific
|
||||
requests defined by the CDC specification. Because this USB protocol
|
||||
is compliant to the CDC/ACM standard on some other operating systems
|
||||
(e.g. Linux, MacOS) the device can work with in-box drivers.
|
||||
|
||||
config USBHOST_CDCACM_REDUCED
|
||||
bool "CDC/ACM reduced"
|
||||
---help---
|
||||
This mode uses the CDC data interface (bulk in + bulk out endpoint)
|
||||
only. No control interface (interrupt in endpoint) is required. The
|
||||
device needs to implement the CDC class-specific requests, so that
|
||||
is still possible to support baud rate settings and serial control/
|
||||
status lines. This mode is useful if the number of available
|
||||
endpoints is limited by hardware restrictions.
|
||||
|
||||
If the the CDC/ACM compliant protocol is selected, then the reduced
|
||||
protocol is supported for devices that provide not interrupt IN
|
||||
endpoint. This option is then most useful for testing purposes or
|
||||
if there are insufficient resources to support the compliant
|
||||
protocol. The interrupt IN control interface will not be used even
|
||||
if it is available in this case.
|
||||
|
||||
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.
|
||||
The device implements only the data interface (bulk in + bulk out
|
||||
endpoint). The device does not provide an additional control
|
||||
interface and does not need to implement any class-specific requests
|
||||
on EP0. Because bulk transfer mode provides flow control, there is
|
||||
no need to support serial control/status lines. The advantage of
|
||||
the bulk-only protocol is that the implementation efforts on the
|
||||
device side are reduced to a minimum.
|
||||
|
||||
By default, both the CDC/ACM compliant protocol and the bulk only
|
||||
By default, both the CDC/ACM compliant protocol and the reduced
|
||||
protocol are supported. This option is then most useful for testing
|
||||
purposes.
|
||||
purposes. The interrupt IN control interface will not be used even
|
||||
if it is available; nor will EP0 class requestes.
|
||||
|
||||
endchoice # CDC/ACM Protocol
|
||||
|
||||
config USBHOST_CDCACM_NTDELAY
|
||||
int "CDC/ACM notification polling interval (MSec)"
|
||||
|
|
|
@ -99,7 +99,6 @@
|
|||
# warning Asynchronous transfer support is required (CONFIG_USBHOST_ASYNCH)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_USBHOST_CDCACM_NTDELAY
|
||||
# define USBHOST_CDCACM_NTDELAY MSEC2TICK(CONFIG_USBHOST_CDCACM_NTDELAY)
|
||||
#else
|
||||
|
@ -118,6 +117,23 @@
|
|||
# define USBHOST_CDCACM_TXDELAY MSEC2TICK(200)
|
||||
#endif
|
||||
|
||||
/* Supported protocol */
|
||||
|
||||
#define HAVE_CLASS_REQUESTS 1
|
||||
#define HAVE_INTIN_ENDPOINT 1
|
||||
#define HAVE_CTRL_INTERFACE 1
|
||||
|
||||
#if defined(CONFIG_USBHOST_CDCACM_REDUCED)
|
||||
# undef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
# undef CONFIG_USBHOST_CDCACM_COMPLIANT
|
||||
# undef HAVE_INTIN_ENDPOINT
|
||||
#elif defined(CONFIG_USBHOST_CDCACM_BULKONLY)
|
||||
# undef CONFIG_USBHOST_CDCACM_COMPLIANT
|
||||
# undef HAVE_CLASS_REQUESTS
|
||||
# undef HAVE_INTIN_ENDPOINT
|
||||
# undef HAVE_CTRL_INTERFACE
|
||||
#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
|
||||
|
@ -174,9 +190,15 @@
|
|||
#define USBHOST_BULKIN_FOUND 0x02 /* Bulk IN interface found */
|
||||
#define USBHOST_BULKOUT_FOUND 0x04 /* Bulk OUT interface found */
|
||||
|
||||
#ifdef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#if defined(CONFIG_USBHOST_CDCACM_BULKONLY)
|
||||
# define USBHOST_MINFOUND 0x07 /* Minimum things needed */
|
||||
# define USBHOST_ALLFOUND 0x07 /* All configuration things */
|
||||
#elif defined(CONFIG_USBHOST_CDCACM_REDUCED)
|
||||
# define USBHOST_CTRLIF_FOUND 0x08 /* Control interface found */
|
||||
|
||||
# define USBHOST_MINFOUND 0x07 /* Minimum things needed */
|
||||
# define USBHOST_HAVE_CTRLIF 0x08 /* Needed for control interface */
|
||||
# define USBHOST_ALLFOUND 0x0f /* All configuration things */
|
||||
#else
|
||||
# define USBHOST_CTRLIF_FOUND 0x08 /* Control interface found */
|
||||
# define USBHOST_INTIN_FOUND 0x10 /* Interrupt IN interface found */
|
||||
|
@ -229,7 +251,7 @@ 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
|
||||
#ifdef HAVE_CTRL_INTERFACE
|
||||
uint8_t ctrlif; /* Control interface number */
|
||||
#endif
|
||||
uint8_t nbits; /* Number of bits (for line encoding) */
|
||||
|
@ -251,7 +273,7 @@ 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
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
usbhost_ep_t intin; /* Interrupt IN endpoint (optional) */
|
||||
#endif
|
||||
|
||||
|
@ -291,11 +313,13 @@ static inline void usbhost_mkdevname(FAR struct usbhost_cdcacm_s *priv,
|
|||
|
||||
/* CDC/ACM request helpers */
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_CTRL_INTERFACE
|
||||
static int usbhost_linecoding_send(FAR struct usbhost_cdcacm_s *priv);
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
static void usbhost_notification_work(FAR void *arg);
|
||||
static void usbhost_notification_callback(FAR void *arg, ssize_t nbytes);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* UART buffer data transfer */
|
||||
|
||||
|
@ -316,7 +340,7 @@ 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
|
||||
#ifdef HAVE_CTRL_INTERFACE
|
||||
static void usbhost_putle32(uint8_t *dest, uint32_t val);
|
||||
#endif
|
||||
|
||||
|
@ -624,7 +648,7 @@ static inline void usbhost_mkdevname(FAR struct usbhost_cdcacm_s *priv,
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_CTRL_INTERFACE
|
||||
static int usbhost_linecoding_send(FAR struct usbhost_cdcacm_s *priv)
|
||||
{
|
||||
FAR struct usbhost_hubport_s *hport;
|
||||
|
@ -682,7 +706,7 @@ static int usbhost_linecoding_send(FAR struct usbhost_cdcacm_s *priv)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
static void usbhost_notification_work(FAR void *arg)
|
||||
{
|
||||
FAR struct usbhost_cdcacm_s *priv;
|
||||
|
@ -773,7 +797,7 @@ static void usbhost_notification_work(FAR void *arg)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
static void usbhost_notification_callback(FAR void *arg, ssize_t nbytes)
|
||||
{
|
||||
FAR struct usbhost_cdcacm_s *priv = (FAR struct usbhost_cdcacm_s *)arg;
|
||||
|
@ -1137,9 +1161,9 @@ static void usbhost_rxdata_work(FAR void *arg)
|
|||
*/
|
||||
|
||||
#ifdef CONFIG_SERIAL_IFLOWCONTROL
|
||||
if (priv->rxena && priv->rts)
|
||||
if (priv->rxena && priv->rts && work_available(&priv->rxwork))
|
||||
#else
|
||||
if (priv->rxena)
|
||||
if (priv->rxena && work_available(&priv->rxwork))
|
||||
#endif
|
||||
{
|
||||
/* Schedule RX data reception work flow to occur after a delay.
|
||||
|
@ -1148,7 +1172,6 @@ static void usbhost_rxdata_work(FAR void *arg)
|
|||
* work when the upper layer demands more data.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(work_available(&priv->rxwork));
|
||||
ret = work_queue(LPWORK, &priv->rxwork, usbhost_rxdata_work, priv,
|
||||
USBHOST_CDCACM_RXDELAY);
|
||||
DEBUGASSERT(ret >= 0);
|
||||
|
@ -1213,7 +1236,7 @@ static void usbhost_destroy(FAR void *arg)
|
|||
DRVR_EPFREE(hport->drvr, priv->bulkin);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
if (priv->intin)
|
||||
{
|
||||
DRVR_EPFREE(hport->drvr, priv->intin);
|
||||
|
@ -1348,7 +1371,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv,
|
|||
found |= USBHOST_DATAIF_FOUND;
|
||||
currif = USBHOST_DATAIF_FOUND;
|
||||
}
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_CTRL_INTERFACE
|
||||
else if (ifdesc->classid == USB_CLASS_CDC &&
|
||||
(found & USBHOST_CTRLIF_FOUND) == 0)
|
||||
{
|
||||
|
@ -1447,7 +1470,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv,
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_CTRL_INTERFACE
|
||||
/* Check for an interrupt IN endpoint. */
|
||||
|
||||
else if (currif == USBHOST_CTRLIF_FOUND &&
|
||||
|
@ -1457,6 +1480,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv,
|
|||
|
||||
if (USB_ISEPIN(epdesc->addr))
|
||||
{
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
/* It is an IN interrupt endpoint. There should be only one
|
||||
* interrupt IN endpoint.
|
||||
*/
|
||||
|
@ -1487,6 +1511,9 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv,
|
|||
|
||||
uvdbg("Interrupt IN EP addr:%d mxpacketsize:%d\n",
|
||||
boutdesc.addr, boutdesc.mxpacketsize);
|
||||
#else
|
||||
found |= USBHOST_CTRLIF_FOUND;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -1545,7 +1572,7 @@ static int usbhost_cfgdesc(FAR struct usbhost_cdcacm_s *priv,
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
/* The control interface with interrupt IN endpoint is optional */
|
||||
|
||||
if ((found & USBHOST_HAVE_CTRLIF) == USBHOST_HAVE_CTRLIF)
|
||||
|
@ -1637,7 +1664,7 @@ static void usbhost_putle16(uint8_t *dest, uint16_t val)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_CTRL_INTERFACE
|
||||
static void usbhost_putle32(uint8_t *dest, uint32_t val)
|
||||
{
|
||||
/* Little endian means LS halfword first in byte stream */
|
||||
|
@ -1694,7 +1721,7 @@ static int usbhost_alloc_buffers(FAR struct usbhost_cdcacm_s *priv)
|
|||
goto errout;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
/* Allocate (optional) buffer for receiving line status data. */
|
||||
|
||||
if (priv->intin)
|
||||
|
@ -1973,7 +2000,7 @@ static int usbhost_connect(FAR struct usbhost_class_s *usbclass,
|
|||
goto errout;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_CTRL_INTERFACE
|
||||
/* Send the initial line encoding */
|
||||
|
||||
ret = usbhost_linecoding_send(priv);
|
||||
|
@ -1996,7 +2023,7 @@ static int usbhost_connect(FAR struct usbhost_class_s *usbclass,
|
|||
goto errout;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
/* Do we have an interrupt IN endpoint? */
|
||||
|
||||
if (priv->intin)
|
||||
|
@ -2061,13 +2088,13 @@ errout:
|
|||
static int usbhost_disconnected(struct usbhost_class_s *usbclass)
|
||||
{
|
||||
FAR struct usbhost_cdcacm_s *priv = (FAR struct usbhost_cdcacm_s *)usbclass;
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
FAR struct usbhost_hubport_s *hport;
|
||||
#endif
|
||||
irqstate_t flags;
|
||||
|
||||
DEBUGASSERT(priv != NULL);
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
DEBUGASSERT(priv->usbclass.hport != NULL);
|
||||
hport = priv->usbclass.hport;
|
||||
#endif
|
||||
|
@ -2079,7 +2106,7 @@ static int usbhost_disconnected(struct usbhost_class_s *usbclass)
|
|||
flags = irqsave();
|
||||
priv->disconnected = true;
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_INTIN_ENDPOINT
|
||||
/* Cancel any pending asynchronous I/O */
|
||||
|
||||
if (priv->intin)
|
||||
|
@ -2439,7 +2466,7 @@ static int usbhost_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_USBHOST_CDCACM_BULKONLY
|
||||
#ifdef HAVE_CTRL_INTERFACE
|
||||
/* Effect the changes immediately - note that we do not implement
|
||||
* TCSADRAIN / TCSAFLUSH
|
||||
*/
|
||||
|
@ -2500,10 +2527,9 @@ static void usbhost_rxint(FAR struct uart_dev_s *uartdev, bool enable)
|
|||
*/
|
||||
|
||||
#ifdef CONFIG_SERIAL_IFLOWCONTROL
|
||||
if (priv->rts)
|
||||
if (priv->rts && work_available(&priv->rxwork))
|
||||
#endif
|
||||
{
|
||||
DEBUGASSERT(work_available(&priv->rxwork));
|
||||
ret = work_queue(LPWORK, &priv->rxwork,
|
||||
usbhost_rxdata_work, priv, 0);
|
||||
DEBUGASSERT(ret >= 0);
|
||||
|
@ -2613,9 +2639,8 @@ static bool usbhost_rxflowcontrol(FAR struct uart_dev_s *uartdev,
|
|||
* disabled.
|
||||
*/
|
||||
|
||||
if (priv->rxena)
|
||||
if (priv->rxena && work_available(&priv->rxwork))
|
||||
{
|
||||
DEBUGASSERT(work_available(&priv->rxwork));
|
||||
ret = work_queue(LPWORK, &priv->rxwork,
|
||||
usbhost_rxdata_work, priv, 0);
|
||||
DEBUGASSERT(ret >= 0);
|
||||
|
|
Loading…
Reference in a new issue