drivers/usbhost/usbhost_cdcecm.c: Converted CDC-ECM to lower half

Converted USB Host CDC-ECM to a lower half driver.
Removed existing full network driver callbacks and replaced with lower
half.

Signed-off-by: daniellizewski <daniellizewski@geotab.com>
This commit is contained in:
daniellizewski 2026-05-04 10:58:17 -04:00 committed by Lup Yuen Lee
parent 9b18160893
commit fe67544d5f
2 changed files with 173 additions and 323 deletions

View file

@ -16,7 +16,6 @@ CONFIG_ARCH_CHIP_STM32H563ZI=y
CONFIG_ARCH_CHIP_STM32H5=y
CONFIG_ARCH_INTERRUPTSTACK=2048
CONFIG_ARCH_STACKDUMP=y
CONFIG_ARMV8M_STACKCHECK_NONE=y
CONFIG_BOARD_LOOPSPERMSEC=9251
CONFIG_BUILTIN=y
CONFIG_DEBUG_ASSERTIONS=y
@ -31,6 +30,7 @@ CONFIG_HAVE_CXX=y
CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INIT_STACKSIZE=4096
CONFIG_LINE_MAX=64
CONFIG_NET=y
CONFIG_NETDB_DNSCLIENT=y
@ -56,6 +56,7 @@ CONFIG_READLINE_CMD_HISTORY=y
CONFIG_READLINE_TABCOMPLETION=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_LPWORK=y
CONFIG_SCHED_LPWORKSTACKSIZE=4096
CONFIG_SCHED_WAITPID=y
CONFIG_STACK_COLORATION=y
CONFIG_STM32H5_USART3=y

View file

@ -46,16 +46,12 @@
#include <nuttx/fs/fs.h>
#include <nuttx/wqueue.h>
#include <nuttx/signal.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/netdev_lowerhalf.h>
#include <nuttx/usb/cdc.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbhost.h>
#ifdef CONFIG_NET_PKT
#include <nuttx/net/pkt.h>
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@ -147,7 +143,6 @@ struct usbhost_cdcecm_s
mutex_t lock; /* Used to maintain mutual exclusive access */
struct work_s ntwork; /* Notification work */
struct work_s bulk_rxwork;
struct work_s txpollwork;
struct work_s destroywork;
int16_t nnbytes; /* Number of bytes received in notification */
int16_t bulkinbytes;
@ -167,11 +162,11 @@ struct usbhost_cdcecm_s
uint8_t macstridx; /* MAC address string index */
uint8_t macaddr[6]; /* Device MAC address */
/* Network device members */
/* Network device lowerhalf members */
bool registered; /* true if the device was registered */
netpkt_queue_t rx_queue; /* RX packet queue for lowerhalf */
bool bifup; /* true:ifup false:ifdown */
struct net_driver_s netdev; /* Interface understood by the network */
struct netdev_lowerhalf_s dev; /* lower half network interface */
};
/****************************************************************************
@ -220,17 +215,18 @@ 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);
/* NuttX network callback functions */
/* Lowerhalf network device operations */
static int cdcecm_ifup(struct net_driver_s *dev);
static int cdcecm_ifdown(struct net_driver_s *dev);
static int cdcecm_txavail(struct net_driver_s *dev);
/* Network support functions */
static void cdcecm_receive(struct usbhost_cdcecm_s *priv);
static int cdcecm_txpoll(struct net_driver_s *dev);
static int cdcecm_net_ifup(FAR struct netdev_lowerhalf_s *dev);
static int cdcecm_net_ifdown(FAR struct netdev_lowerhalf_s *dev);
static int cdcecm_net_transmit(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt);
static FAR netpkt_t *cdcecm_net_receive(FAR struct netdev_lowerhalf_s *dev);
#ifdef CONFIG_NETDEV_IOCTL
static int cdcecm_net_ioctl(FAR struct netdev_lowerhalf_s *dev,
int cmd, unsigned long arg);
#endif
static void cdcecm_net_reclaim(FAR struct netdev_lowerhalf_s *dev);
/****************************************************************************
* Private Data
@ -269,6 +265,20 @@ static struct usbhost_registry_s g_cdcecm =
g_cdcecm_id /* id[] */
};
/* Netdev operations */
static const struct netdev_ops_s g_cdcecm_netdev_ops =
{
.ifup = cdcecm_net_ifup,
.ifdown = cdcecm_net_ifdown,
.transmit = cdcecm_net_transmit,
.receive = cdcecm_net_receive,
#ifdef CONFIG_NETDEV_IOCTL
.ioctl = cdcecm_net_ioctl,
#endif
.reclaim = cdcecm_net_reclaim,
};
/****************************************************************************
* Private Functions
****************************************************************************/
@ -402,19 +412,11 @@ static void usbhost_bulkin_work(FAR void *arg)
{
struct usbhost_cdcecm_s *priv;
struct usbhost_hubport_s *hport;
struct iob_s *iob = NULL;
FAR netpkt_t *pkt = NULL;
priv = (struct usbhost_cdcecm_s *)arg;
DEBUGASSERT(priv);
/* We need net_lock and need priv->mutex.
* Since the TX path (cdcecm_txpoll) gets net_lock then priv->lock,
* we need to follow the same order to prevent a deadlock if there
* is more than 1 LPWORK thread
*/
net_lock();
nxmutex_lock(&priv->lock);
hport = priv->usbclass.hport;
@ -423,13 +425,10 @@ static void usbhost_bulkin_work(FAR void *arg)
if (priv->disconnected || !priv->bifup)
{
nxmutex_unlock(&priv->lock);
net_unlock();
return;
}
/* Copy incoming data into IOB buffer and
* start a new RX as soon as possible
*/
/* Process received data into netpkt and queue for upper half */
if (priv->bulkinbytes > 0)
{
@ -439,50 +438,47 @@ static void usbhost_bulkin_work(FAR void *arg)
/* ECM sends raw Ethernet frames - no additional framing */
if (priv->bulkinbytes <= iob_navail(true) * CONFIG_IOB_BUFSIZE)
pkt = netpkt_alloc(&priv->dev, NETPKT_RX);
if (pkt != NULL)
{
iob = iob_tryalloc(true);
if (iob != NULL)
int ret = netpkt_copyin(&priv->dev, pkt,
priv->bulkinbuf,
priv->bulkinbytes, 0);
if (ret < 0)
{
iob_reserve(iob, CONFIG_NET_LL_GUARDSIZE);
int ret = iob_trycopyin(iob, priv->bulkinbuf,
priv->bulkinbytes,
-NET_LL_HDRLEN(&priv->netdev), true);
if (ret != priv->bulkinbytes)
netpkt_free(&priv->dev, pkt, NETPKT_RX);
pkt = NULL;
}
else
{
ret = netpkt_tryadd_queue(pkt, &priv->rx_queue);
if (ret != 0)
{
iob_free_chain(iob);
iob = NULL;
netpkt_free(&priv->dev, pkt, NETPKT_RX);
pkt = NULL;
}
}
}
if (iob == NULL)
{
NETDEV_RXDROPPED(&priv->netdev);
}
else
{
priv->netdev.d_iob = iob;
priv->netdev.d_len = priv->bulkinbytes;
}
}
else if (priv->bulkinbytes < 0 && priv->bulkinbytes != -EAGAIN)
{
uerr("Bulk IN error: %d\n", priv->bulkinbytes);
}
/* Restart async RX */
DRVR_ASYNCH(hport->drvr, priv->bulkin,
(uint8_t *)priv->bulkinbuf, CONFIG_USBHOST_CDCECM_RXBUFSIZE,
usbhost_bulkin_callback, priv);
if (iob != NULL)
{
cdcecm_receive(priv);
netdev_iob_release(&priv->netdev);
}
nxmutex_unlock(&priv->lock);
net_unlock();
/* Notify upper half after unlocking to avoid deadlock */
if (pkt != NULL)
{
netdev_lower_rxready(&priv->dev);
}
}
/****************************************************************************
@ -497,9 +493,6 @@ static void usbhost_bulkin_work(FAR void *arg)
* Returned Value:
* None
*
* Assumptions:
* Probably called from an interrupt handler.
*
****************************************************************************/
static void usbhost_notification_work(FAR void *arg)
@ -541,24 +534,28 @@ static void usbhost_notification_work(FAR void *arg)
*/
if (inmsg->value[0] &&
!IFF_IS_RUNNING(priv->netdev.d_flags))
!IFF_IS_RUNNING(priv->dev.netdev.d_flags))
{
uinfo("Network connected\n");
netdev_carrier_on(&priv->netdev);
netdev_lower_carrier_on(&priv->dev);
}
else if (!inmsg->value[0] &&
IFF_IS_RUNNING(priv->netdev.d_flags))
IFF_IS_RUNNING(priv->dev.netdev.d_flags))
{
uinfo("Network disconnected\n");
netdev_carrier_off(&priv->netdev);
netdev_lower_carrier_off(&priv->dev);
}
break;
case ECM_SPEED_CHANGE:
/* Connection speed changed - informational only */
/* Connection speed changed - informational only.
* Disabled print since some adapters send this very
* frequently even if there is no change
*/
/* uinfo("Speed change notification\n"); */
uinfo("Speed change notification\n");
break;
default:
@ -573,7 +570,7 @@ static void usbhost_notification_work(FAR void *arg)
ret = DRVR_ASYNCH(hport->drvr, priv->intin,
(FAR uint8_t *)priv->notification,
SIZEOF_NOTIFICATION_S(0),
priv->maxintsize,
usbhost_notification_callback,
priv);
if (ret < 0)
@ -699,11 +696,7 @@ static void usbhost_destroy(FAR void *arg)
/* Unregister the network device */
if (priv->registered)
{
netdev_unregister(&priv->netdev);
priv->registered = false;
}
netdev_lower_unregister(&priv->dev);
/* Cancel any pending work */
@ -711,8 +704,6 @@ static void usbhost_destroy(FAR void *arg)
work_cancel(LPWORK, &priv->bulk_rxwork);
work_cancel(LPWORK, &priv->txpollwork);
/* Free the endpoints */
if (priv->intin)
@ -1168,7 +1159,7 @@ static int usbhost_parse_mac_string(FAR const uint8_t * strdesc,
static int usbhost_get_mac_address(FAR struct usbhost_cdcecm_s * priv)
{
int ret;
uint8_t strbuf[64];
uint8_t *strbuf;
if (priv->macstridx == 0)
{
@ -1176,16 +1167,24 @@ static int usbhost_get_mac_address(FAR struct usbhost_cdcecm_s * priv)
return -ENOENT;
}
ret = DRVR_IOALLOC(priv->usbclass.hport->drvr, &strbuf, 64);
if (ret < 0)
{
uerr("ERROR: DRVR_IOALLOC of notification buf failed: %d (%d bytes)\n",
ret, priv->maxintsize);
return ret;
}
ret = usbhost_ctrl_cmd(priv,
USB_REQ_DIR_IN | USB_REQ_TYPE_STANDARD |
USB_REQ_RECIPIENT_DEVICE,
USB_REQ_GETDESCRIPTOR,
(USB_DESC_TYPE_STRING << 8) | priv->macstridx,
0x0409, strbuf, sizeof(strbuf));
0x0409, strbuf, 64);
if (ret < 0)
{
uerr("Failed to get MAC string descriptor: %d\n", ret);
return ret;
goto errout;
}
/* Parse MAC address from Unicode string */
@ -1194,14 +1193,18 @@ static int usbhost_get_mac_address(FAR struct usbhost_cdcecm_s * priv)
if (ret < 0)
{
uerr("Failed to parse MAC address: %d\n", ret);
return ret;
goto errout;
}
uinfo("Device MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
priv->macaddr[0], priv->macaddr[1], priv->macaddr[2],
priv->macaddr[3], priv->macaddr[4], priv->macaddr[5]);
return OK;
ret = OK;
errout:
(void)DRVR_IOFREE(priv->usbclass.hport->drvr, strbuf);
return ret;
}
/****************************************************************************
@ -1282,6 +1285,22 @@ static inline int usbhost_devinit(FAR struct usbhost_cdcecm_s *priv)
priv->macaddr[5] = rand() & 0xff;
}
/* Setup and register the lowerhalf network device */
FAR struct netdev_lowerhalf_s *dev = &priv->dev;
memset(dev, 0, sizeof(*dev));
memcpy(dev->netdev.d_mac.ether.ether_addr_octet,
priv->macaddr, sizeof(priv->macaddr));
dev->ops = &g_cdcecm_netdev_ops;
dev->quota[NETPKT_TX] = 1;
dev->quota[NETPKT_RX] = 1;
dev->rxtype = NETDEV_RX_WORK;
dev->priority = LPWORK;
netdev_lower_register(dev, NET_LL_ETHERNET);
if (priv->intin)
{
/* Begin monitoring of message available events */
@ -1289,7 +1308,7 @@ static inline int usbhost_devinit(FAR struct usbhost_cdcecm_s *priv)
uinfo("Start notification monitoring\n");
ret = DRVR_ASYNCH(hport->drvr, priv->intin,
(FAR uint8_t *)priv->notification,
SIZEOF_NOTIFICATION_S(0),
priv->maxintsize,
usbhost_notification_callback,
priv);
if (ret < 0)
@ -1298,21 +1317,6 @@ static inline int usbhost_devinit(FAR struct usbhost_cdcecm_s *priv)
}
}
/* Setup the network interface */
memset(&priv->netdev, 0, sizeof(struct net_driver_s));
priv->netdev.d_ifup = cdcecm_ifup;
priv->netdev.d_ifdown = cdcecm_ifdown;
priv->netdev.d_txavail = cdcecm_txavail;
priv->netdev.d_private = priv;
memcpy(priv->netdev.d_mac.ether.ether_addr_octet,
priv->macaddr, sizeof(priv->macaddr));
/* Register the network device */
netdev_register(&priv->netdev, NET_LL_ETHERNET);
priv->registered = true;
/* Check if we successfully initialized. We now have to be concerned
* about asynchronous modification of crefs because the character
* driver has been registered.
@ -1343,8 +1347,9 @@ static inline int usbhost_devinit(FAR struct usbhost_cdcecm_s *priv)
uinfo("Successfully initialized\n");
priv->crefs--;
nxmutex_unlock(&priv->lock);
}
nxmutex_unlock(&priv->lock);
}
return ret;
@ -1741,203 +1746,124 @@ static int usbhost_disconnected(struct usbhost_class_s *usbclass)
}
/****************************************************************************
* Name: cdcecm_transmit
* Name: cdcecm_net_transmit
*
* Description:
* Start hardware transmission. Called either from the txdone interrupt
* handling or from watchdog based polling.
* Lowerhalf transmit callback. Copies packet data to USB bulk OUT buffer
* and performs a blocking USB transfer.
*
* Input Parameters:
* priv - Reference to the driver state structure
* dev - Reference to the lowerhalf driver structure
* pkt - The packet to send (driver takes ownership and must free)
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int cdcecm_transmit(struct usbhost_cdcecm_s *priv)
static int cdcecm_net_transmit(FAR struct netdev_lowerhalf_s *dev,
FAR netpkt_t *pkt)
{
struct usbhost_hubport_s *hport;
FAR struct usbhost_cdcecm_s *priv;
FAR struct usbhost_hubport_s *hport;
unsigned int datalen;
ssize_t ret;
priv = container_of(dev, struct usbhost_cdcecm_s, dev);
hport = priv->usbclass.hport;
uinfo("transmit packet: %d bytes\n", priv->netdev.d_len);
datalen = netpkt_getdatalen(dev, pkt);
uinfo("transmit packet: %u bytes\n", datalen);
/* Increment statistics */
NETDEV_TXPACKETS(&priv->netdev);
/* For ECM, we send raw Ethernet frames - no additional framing needed.
* Copy the frame to the TX buffer and send it.
*/
if (priv->netdev.d_len > CONFIG_USBHOST_CDCECM_TXBUFSIZE)
if (datalen > CONFIG_USBHOST_CDCECM_TXBUFSIZE)
{
NETDEV_TXERRORS(&priv->netdev);
netpkt_free(dev, pkt, NETPKT_TX);
return -EMSGSIZE;
}
iob_copyout(priv->bulkoutbuf, priv->netdev.d_iob, priv->netdev.d_len,
-NET_LL_HDRLEN(&priv->netdev));
cdcecm_print_packet("ECM TX Packet:",
priv->bulkoutbuf, priv->netdev.d_len);
/* Copy packet data to USB bulk out buffer.
* ECM sends raw Ethernet frames - no additional framing needed.
*/
ret = netpkt_copyout(dev, priv->bulkoutbuf, pkt, datalen, 0);
netpkt_free(dev, pkt, NETPKT_TX);
ret = DRVR_TRANSFER(hport->drvr, priv->bulkout, priv->bulkoutbuf,
priv->netdev.d_len);
if (ret < 0)
{
uerr("transfer returned error: %d\n", ret);
return ret;
}
NETDEV_TXDONE(&priv->netdev);
cdcecm_print_packet("ECM TX Packet:", priv->bulkoutbuf, datalen);
ret = DRVR_TRANSFER(hport->drvr, priv->bulkout,
priv->bulkoutbuf, datalen);
if (ret < 0)
{
uerr("ERROR: DRVR_TRANSFER failed: %zd\n", ret);
return ret;
}
netdev_lower_txdone(dev);
return OK;
}
/****************************************************************************
* Name: cdcecm_receive
* Name: cdcecm_net_receive
*
* Description:
* Handle a received packet.
* Lowerhalf receive callback. Dequeues a received packet from the RX
* queue. Called by upper half after netdev_lower_rxready() notification.
*
* Input Parameters:
* priv - Reference to the driver state structure
* dev - Reference to the lowerhalf driver structure
*
* Returned Value:
* OK on success; a negated errno on failure
* A netpkt containing the received packet, or NULL if no more packets.
*
* Assumptions:
* The network is locked.
****************************************************************************/
static void cdcecm_receive(struct usbhost_cdcecm_s *priv)
static FAR netpkt_t *cdcecm_net_receive(FAR struct netdev_lowerhalf_s *dev)
{
FAR struct eth_hdr_s * ethhdr;
FAR struct usbhost_cdcecm_s *priv;
NETDEV_RXPACKETS(&priv->netdev);
/* Validate frame length */
if (priv->netdev.d_len < ETH_HDRLEN ||
priv->netdev.d_len > priv->maxsegment)
{
NETDEV_RXERRORS(&priv->netdev);
nwarn("Invalid frame length: %zu\n", priv->netdev.d_len);
return;
}
ethhdr = (struct eth_hdr_s *)
&priv->netdev.d_iob->io_data[CONFIG_NET_LL_GUARDSIZE -
NET_LL_HDRLEN(&priv->netdev)];
/* Check the Ethertype */
#ifdef CONFIG_NET_IPv4
if (ethhdr->type == HTONS(ETHTYPE_IP))
{
NETDEV_RXIPV4(&priv->netdev);
ipv4_input(&priv->netdev);
if (priv->netdev.d_len > 0)
{
cdcecm_transmit(priv);
}
}
else
#endif
#ifdef CONFIG_NET_IPv6
if (ethhdr->type == HTONS(ETHTYPE_IP6))
{
NETDEV_RXIPV6(&priv->netdev);
ipv6_input(&priv->netdev);
if (priv->netdev.d_len > 0)
{
cdcecm_transmit(priv);
}
}
else
#endif
#ifdef CONFIG_NET_ARP
if (ethhdr->type == HTONS(ETHTYPE_ARP))
{
NETDEV_RXARP(&priv->netdev);
arp_input(&priv->netdev);
if (priv->netdev.d_len > 0)
{
cdcecm_transmit(priv);
}
}
else
#endif
#ifdef CONFIG_NET_PKT
if (1)
{
/* Forward raw packet to packet socket */
pkt_input(&priv->netdev);
}
else
#endif
{
NETDEV_RXDROPPED(&priv->netdev);
}
priv = container_of(dev, struct usbhost_cdcecm_s, dev);
return netpkt_remove_queue(&priv->rx_queue);
}
/****************************************************************************
* Name: cdcecm_txpoll
* Name: cdcecm_net_ioctl
*
* Description:
* The transmitter is available, check if the network has any outgoing
* packets ready to send. This is a callback from devif_poll().
* devif_poll() may be called:
*
* 1. When the preceding TX packet send is complete,
* 2. When the preceding TX packet send timesout and the interface is reset
* 3. During normal TX polling
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* The network is locked.
* Lowerhalf ioctl callback.
*
****************************************************************************/
static int cdcecm_txpoll(struct net_driver_s *dev)
#ifdef CONFIG_NETDEV_IOCTL
static int cdcecm_net_ioctl(FAR struct netdev_lowerhalf_s *dev, int cmd,
unsigned long arg)
{
struct usbhost_cdcecm_s *priv = (struct usbhost_cdcecm_s *)
dev->d_private;
return -ENOTTY;
}
#endif
/* If the polling resulted in data that should be sent out on the network,
* the field d_len is set to a value > 0.
*/
/****************************************************************************
* Name: cdcecm_net_reclaim
*
* Description:
* Lowerhalf reclaim callback. TX is synchronous via DRVR_TRANSFER
* so there are no queued TX packets to reclaim.
*
****************************************************************************/
nxmutex_lock(&priv->lock);
/* Send the packet */
cdcecm_transmit(priv);
nxmutex_unlock(&priv->lock);
return 0;
static void cdcecm_net_reclaim(FAR struct netdev_lowerhalf_s *dev)
{
}
/****************************************************************************
* Name: cdcecm_ifup
*
* Description:
* NuttX Callback: Bring up the ECM interface when an IP address is
* provided
* NuttX Callback: Bring up the ECM interface
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
@ -1949,27 +1875,16 @@ static int cdcecm_txpoll(struct net_driver_s *dev)
*
****************************************************************************/
static int cdcecm_ifup(struct net_driver_s *dev)
static int cdcecm_net_ifup(struct netdev_lowerhalf_s *dev)
{
struct usbhost_cdcecm_s *priv = (struct usbhost_cdcecm_s *)
dev->d_private;
FAR struct usbhost_cdcecm_s *priv;
priv = container_of(dev, struct usbhost_cdcecm_s, dev);
nxmutex_lock(&priv->lock);
struct usbhost_hubport_s *hport = priv->usbclass.hport;
int ret;
#ifdef CONFIG_NET_IPv4
ninfo("Bringing up: %d.%d.%d.%d\n",
(int)(dev->d_ipaddr & 0xff), (int)((dev->d_ipaddr >> 8) & 0xff),
(int)((dev->d_ipaddr >> 16) & 0xff), (int)(dev->d_ipaddr >> 24));
#endif
#ifdef CONFIG_NET_IPv6
ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2],
dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5],
dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
#endif
/* Activate the data interface by setting alternate setting 1 */
ret = usbhost_setinterface(priv, priv->dataif, 1);
@ -2025,18 +1940,17 @@ static int cdcecm_ifup(struct net_driver_s *dev)
*
****************************************************************************/
static int cdcecm_ifdown(struct net_driver_s *dev)
static int cdcecm_net_ifdown(struct netdev_lowerhalf_s *dev)
{
struct usbhost_cdcecm_s *priv = (struct usbhost_cdcecm_s *)
dev->d_private;
struct usbhost_cdcecm_s *priv;
priv = container_of(dev, struct usbhost_cdcecm_s, dev);
nxmutex_lock(&priv->lock);
/* Deactivate the data interface */
usbhost_setinterface(priv, priv->dataif, 0);
/* Mark the device "down" */
priv->bifup = false;
nxmutex_unlock(&priv->lock);
@ -2044,71 +1958,6 @@ static int cdcecm_ifdown(struct net_driver_s *dev)
return OK;
}
/****************************************************************************
* Name: cdcecm_txavail_work
*
* Description:
* Driver callback invoked when new TX data is available. This is a
* stimulus perform an out-of-cycle poll and, thereby, reduce the TX
* latency.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Called in normal user mode
*
****************************************************************************/
static void cdcecm_txavail_work(void *arg)
{
struct usbhost_cdcecm_s *priv = (struct usbhost_cdcecm_s *)arg;
net_lock();
if (priv->bifup)
{
devif_poll(&priv->netdev, cdcecm_txpoll);
}
net_unlock();
}
/****************************************************************************
* Name: cdcecm_txavail
*
* Description:
* Driver callback invoked when new TX data is available. This is a
* stimulus perform an out-of-cycle poll and, thereby, reduce the TX
* latency.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Called from the network stack with the network locked.
*
****************************************************************************/
static int cdcecm_txavail(struct net_driver_s *dev)
{
struct usbhost_cdcecm_s *priv = (struct usbhost_cdcecm_s *)
dev->d_private;
if (work_available(&priv->txpollwork))
{
work_queue(LPWORK, &priv->txpollwork, cdcecm_txavail_work, priv, 0);
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/