* drivers/usbdev/cdcecm.c
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*
****************************************************************************/
* [CDCECM1.2] Universal Serial Bus - Communications Class - Subclass
* Specification for Ethernet Control Model Devices - Rev 1.2
*/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <arpa/inet.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/irq.h>
#include <nuttx/wqueue.h>
#include <nuttx/semaphore.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/netdev.h>
#include <nuttx/usb/usbdev.h>
#include <nuttx/usb/cdc.h>
#include <nuttx/usb/usbdev_trace.h>
#ifdef CONFIG_NET_PKT
# include <nuttx/net/pkt.h>
#endif
#ifdef CONFIG_BOARD_USBDEV_SERIALSTR
#include <nuttx/board.h>
#endif
#include "cdcecm.h"
#ifdef CONFIG_NET_CDCECM
* Pre-processor Definitions
****************************************************************************/
#if !defined(CONFIG_SCHED_WORKQUEUE)
# error Work queue support is required in this configuration (CONFIG_SCHED_WORKQUEUE)
#endif
* will be the same as HPWORK. NOTE: Use of the high priority work queue will
* have a negative impact on interrupt handling latency and overall system
* performance. This should be avoided.
*/
#define ETHWORK LPWORK
* that will be supported.
*/
#ifndef CONFIG_CDCECM_NINTERFACES
# define CONFIG_CDCECM_NINTERFACES 1
#endif
#define CDCECM_TXTIMEOUT (60*CLK_TCK)
#define BUF ((FAR struct eth_hdr_s *)self->dev.d_buf)
* Private Types
****************************************************************************/
* hardware interface
*/
struct cdcecm_driver_s
{
struct usbdevclass_driver_s usbdev;
struct usbdev_devinfo_s devinfo;
FAR struct usbdev_req_s *ctrlreq;
FAR struct usbdev_ep_s *epint;
FAR struct usbdev_ep_s *epbulkin;
FAR struct usbdev_ep_s *epbulkout;
uint8_t config;
uint16_t pktbuf[(CONFIG_NET_ETH_PKTSIZE +
CONFIG_NET_GUARDSIZE + 1) / 2];
struct usbdev_req_s *rdreq;
bool rxpending;
struct usbdev_req_s *wrreq;
sem_t wrreq_idle;
bool txdone;
bool bifup;
struct work_s irqwork;
* to the work queue */
struct work_s pollwork;
* the work queue */
struct net_driver_s dev;
* network */
};
* Private Function Prototypes
****************************************************************************/
static int cdcecm_transmit(FAR struct cdcecm_driver_s *priv);
static int cdcecm_txpoll(FAR struct net_driver_s *dev);
static void cdcecm_reply(struct cdcecm_driver_s *priv);
static void cdcecm_receive(FAR struct cdcecm_driver_s *priv);
static void cdcecm_txdone(FAR struct cdcecm_driver_s *priv);
static void cdcecm_interrupt_work(FAR void *arg);
static int cdcecm_ifup(FAR struct net_driver_s *dev);
static int cdcecm_ifdown(FAR struct net_driver_s *dev);
static void cdcecm_txavail_work(FAR void *arg);
static int cdcecm_txavail(FAR struct net_driver_s *dev);
#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
static int cdcecm_addmac(FAR struct net_driver_s *dev,
FAR const uint8_t *mac);
#ifdef CONFIG_NET_MCASTGROUP
static int cdcecm_rmmac(FAR struct net_driver_s *dev,
FAR const uint8_t *mac);
#endif
#endif
#ifdef CONFIG_NETDEV_IOCTL
static int cdcecm_ioctl(FAR struct net_driver_s *dev, int cmd,
unsigned long arg);
#endif
static int cdcecm_bind(FAR struct usbdevclass_driver_s *driver,
FAR struct usbdev_s *dev);
static void cdcecm_unbind(FAR struct usbdevclass_driver_s *driver,
FAR struct usbdev_s *dev);
static int cdcecm_setup(FAR struct usbdevclass_driver_s *driver,
FAR struct usbdev_s *dev, FAR const struct usb_ctrlreq_s *ctrl,
FAR uint8_t *dataout, size_t outlen);
static void cdcecm_disconnect(FAR struct usbdevclass_driver_s *driver,
FAR struct usbdev_s *dev);
static void cdcecm_ep0incomplete(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *req);
static void cdcecm_rdcomplete(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *req);
static void cdcecm_wrcomplete(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *req);
static int cdcecm_mkepdesc(int epidx,
FAR struct usb_epdesc_s *epdesc,
FAR struct usbdev_devinfo_s *devinfo,
uint8_t speed);
* Private Data
****************************************************************************/
static const struct usbdevclass_driverops_s g_usbdevops =
{
cdcecm_bind,
cdcecm_unbind,
cdcecm_setup,
cdcecm_disconnect,
NULL,
NULL
};
#ifndef CONFIG_CDCECM_COMPOSITE
static const struct usb_devdesc_s g_devdesc =
{
USB_SIZEOF_DEVDESC,
USB_DESC_TYPE_DEVICE,
{
LSBYTE(0x0200),
MSBYTE(0x0200)
},
USB_CLASS_CDC,
CDC_SUBCLASS_ECM,
CDC_PROTO_NONE,
CONFIG_CDCECM_EP0MAXPACKET,
{
LSBYTE(CONFIG_CDCECM_VENDORID),
MSBYTE(CONFIG_CDCECM_VENDORID)
},
{
LSBYTE(CONFIG_CDCECM_PRODUCTID),
MSBYTE(CONFIG_CDCECM_PRODUCTID)
},
{
LSBYTE(CDCECM_VERSIONNO),
MSBYTE(CDCECM_VERSIONNO)
},
CDCECM_MANUFACTURERSTRID,
CDCECM_PRODUCTSTRID,
CDCECM_SERIALSTRID,
CDCECM_NCONFIGS
};
#endif
* Private Functions
****************************************************************************/
* Name: cdcecm_transmit
*
* Description:
* Start hardware transmission. Called either from the txdone interrupt
* handling or from watchdog based polling.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int cdcecm_transmit(FAR struct cdcecm_driver_s *self)
{
* becomes available.
*/
while (nxsem_wait(&self->wrreq_idle) != OK)
{
}
NETDEV_TXPACKETS(&self->dev);
memcpy(self->wrreq->buf, self->dev.d_buf, self->dev.d_len);
self->wrreq->len = self->dev.d_len;
return EP_SUBMIT(self->epbulkin, self->wrreq);
}
* Name: cdcecm_txpoll
*
* 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 times out 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.
*
****************************************************************************/
static int cdcecm_txpoll(FAR struct net_driver_s *dev)
{
FAR struct cdcecm_driver_s *priv =
(FAR struct cdcecm_driver_s *)dev->d_private;
cdcecm_transmit(priv);
* not, return a non-zero value to terminate the poll.
*/
return 1;
}
* Name: cdcecm_reply
*
* Description:
* After a packet has been received and dispatched to the network, it
* may return with an outgoing packet. This function checks for that
* case and performs the transmission if necessary.
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static void cdcecm_reply(struct cdcecm_driver_s *priv)
{
* network, the field d_len will set to a value > 0.
*/
if (priv->dev.d_len > 0)
{
cdcecm_transmit(priv);
}
}
* Name: cdcecm_receive
*
* Description:
* An interrupt was received indicating the availability of a new RX packet
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static void cdcecm_receive(FAR struct cdcecm_driver_s *self)
{
* configuration.
*/
* amount of data in self->dev.d_len
*/
memcpy(self->dev.d_buf, self->rdreq->buf, self->rdreq->xfrd);
self->dev.d_len = self->rdreq->xfrd;
#ifdef CONFIG_NET_PKT
pkt_input(&self->dev);
#endif
#ifdef CONFIG_NET_IPv4
if (BUF->type == HTONS(ETHTYPE_IP))
{
ninfo("IPv4 frame\n");
NETDEV_RXIPV4(&self->dev);
ipv4_input(&self->dev);
cdcecm_reply(self);
}
else
#endif
#ifdef CONFIG_NET_IPv6
if (BUF->type == HTONS(ETHTYPE_IP6))
{
ninfo("IPv6 frame\n");
NETDEV_RXIPV6(&self->dev);
ipv6_input(&self->dev);
cdcecm_reply(self);
}
else
#endif
#ifdef CONFIG_NET_ARP
if (BUF->type == HTONS(ETHTYPE_ARP))
{
arp_input(&self->dev);
NETDEV_RXARP(&self->dev);
* sent out on the network, d_len field will set to a value > 0.
*/
if (self->dev.d_len > 0)
{
cdcecm_transmit(self);
}
}
else
#endif
{
NETDEV_RXDROPPED(&self->dev);
}
}
* Name: cdcecm_txdone
*
* Description:
* An interrupt was received indicating that the last TX packet(s) is done
*
* Input Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static void cdcecm_txdone(FAR struct cdcecm_driver_s *priv)
{
NETDEV_TXDONE(&priv->dev);
devif_poll(&priv->dev, cdcecm_txpoll);
}
* Name: cdcecm_interrupt_work
*
* Description:
* Perform interrupt related work from the worker thread
*
* Input Parameters:
* arg - The argument passed when work_queue() was called.
*
* Returned Value:
* OK on success
*
* Assumptions:
* Runs on a worker thread.
*
****************************************************************************/
static void cdcecm_interrupt_work(FAR void *arg)
{
FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)arg;
* NOTE: Serialization is only required in the case where the driver work
* is performed on an LP worker thread and where more than one LP worker
* thread has been configured.
*/
net_lock();
if (self->rxpending)
{
cdcecm_receive(self);
self->rxpending = false;
EP_SUBMIT(self->epbulkout, self->rdreq);
}
* cdcecm_txdone. This may disable further Tx interrupts if there
* are no pending transmissions.
*/
if (self->txdone)
{
self->txdone = false;
cdcecm_txdone(self);
}
net_unlock();
}
* Name: cdcecm_ifup
*
* Description:
* NuttX Callback: Bring up the Ethernet interface when an IP address is
* provided
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int cdcecm_ifup(FAR struct net_driver_s *dev)
{
FAR struct cdcecm_driver_s *priv =
(FAR struct cdcecm_driver_s *)dev->d_private;
#ifdef CONFIG_NET_IPv4
ninfo("Bringing up: %u.%u.%u.%u\n",
ip4_addr1(dev->d_ipaddr), ip4_addr2(dev->d_ipaddr),
ip4_addr3(dev->d_ipaddr), ip4_addr4(dev->d_ipaddr));
#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
priv->bifup = true;
return OK;
}
* Name: cdcecm_ifdown
*
* Description:
* NuttX Callback: Stop the interface.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int cdcecm_ifdown(FAR struct net_driver_s *dev)
{
FAR struct cdcecm_driver_s *priv =
(FAR struct cdcecm_driver_s *)dev->d_private;
* a known configuration that will guarantee the cdcecm_ifup() always
* successfully brings the interface back up.
*/
priv->bifup = false;
return OK;
}
* Name: cdcecm_txavail_work
*
* Description:
* Perform an out-of-cycle poll on the worker thread.
*
* Input Parameters:
* arg - Reference to the NuttX driver state structure (cast to void*)
*
* Returned Value:
* None
*
* Assumptions:
* Runs on a work queue thread.
*
****************************************************************************/
static void cdcecm_txavail_work(FAR void *arg)
{
FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)arg;
* NOTE: Serialization is only required in the case where the driver work
* is performed on an LP worker thread and where more than one LP worker
* thread has been configured.
*/
net_lock();
if (self->bifup)
{
devif_poll(&self->dev, 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:
* The network is locked.
*
****************************************************************************/
static int cdcecm_txavail(FAR struct net_driver_s *dev)
{
FAR struct cdcecm_driver_s *priv =
(FAR struct cdcecm_driver_s *)dev->d_private;
* pending interrupt actions and we will have to ignore the Tx
* availability action.
*/
if (work_available(&priv->pollwork))
{
work_queue(ETHWORK, &priv->pollwork, cdcecm_txavail_work, priv, 0);
}
return OK;
}
* Name: cdcecm_addmac
*
* Description:
* NuttX Callback: Add the specified MAC address to the hardware multicast
* address filtering
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
* mac - The MAC address to be added
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
#if defined(CONFIG_NET_MCASTGROUP) || defined(CONFIG_NET_ICMPv6)
static int cdcecm_addmac(FAR struct net_driver_s *dev,
FAR const uint8_t *mac)
{
FAR struct cdcecm_driver_s *priv =
(FAR struct cdcecm_driver_s *)dev->d_private;
UNUSED(priv);
return OK;
}
#endif
* Name: cdcecm_rmmac
*
* Description:
* NuttX Callback: Remove the specified MAC address from the hardware
* multicast address filtering
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
* mac - The MAC address to be removed
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
#ifdef CONFIG_NET_MCASTGROUP
static int cdcecm_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac)
{
FAR struct cdcecm_driver_s *priv =
(FAR struct cdcecm_driver_s *)dev->d_private;
UNUSED(priv);
return OK;
}
#endif
* Name: cdcecm_ioctl
*
* Description:
* Handle network IOCTL commands directed to this device.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
* cmd - The IOCTL command
* arg - The argument for the IOCTL command
*
* Returned Value:
* OK on success; Negated errno on failure.
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
#ifdef CONFIG_NETDEV_IOCTL
static int cdcecm_ioctl(FAR struct net_driver_s *dev, int cmd,
unsigned long arg)
{
switch (cmd)
{
default:
nerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
return -ENOTTY;
}
return OK;
}
#endif
* USB Device Class Helpers
****************************************************************************/
* Name: cdcecm_ep0incomplete
*
* Description:
* Handle completion of EP0 control operations
*
****************************************************************************/
static void cdcecm_ep0incomplete(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *req)
{
if (req->result || req->xfrd != req->len)
{
uerr("result: %hd, xfrd: %zu\n", req->result, req->xfrd);
}
}
* Name: cdcecm_rdcomplete
*
* Description:
* Handle completion of read request on the bulk OUT endpoint.
*
****************************************************************************/
static void cdcecm_rdcomplete(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *req)
{
FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)ep->priv;
uinfo("buf: %p, flags 0x%hhx, len %zu, xfrd %zu, result %hd\n",
req->buf, req->flags, req->len, req->xfrd, req->result);
switch (req->result)
{
case 0:
{
DEBUGASSERT(!self->rxpending);
self->rxpending = true;
work_queue(ETHWORK, &self->irqwork,
cdcecm_interrupt_work, self, 0);
}
break;
case -ESHUTDOWN:
break;
default:
{
uerr("req->result: %hd\n", req->result);
EP_SUBMIT(self->epbulkout, self->rdreq);
}
break;
}
}
* Name: cdcecm_wrcomplete
*
* Description:
* Handle completion of write request. This function probably executes
* in the context of an interrupt handler.
*
****************************************************************************/
static void cdcecm_wrcomplete(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *req)
{
FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)ep->priv;
int rc;
uinfo("buf: %p, flags 0x%hhx, len %zu, xfrd %zu, result %hd\n",
req->buf, req->flags, req->len, req->xfrd, req->result);
* transmissions again.
*/
rc = nxsem_post(&self->wrreq_idle);
if (rc != OK)
{
nerr("nxsem_post failed! rc: %d\n", rc);
}
self->txdone = true;
work_queue(ETHWORK, &self->irqwork, cdcecm_interrupt_work, self, 0);
}
* Name: cdcecm_resetconfig
*
* Description:
* Mark the device as not configured and disable all endpoints.
*
****************************************************************************/
static void cdcecm_resetconfig(FAR struct cdcecm_driver_s *self)
{
if (self->config != CDCECM_CONFIGID_NONE)
{
self->config = CDCECM_CONFIGID_NONE;
self->dev.d_ifdown(&self->dev);
* transfers.
*/
EP_DISABLE(self->epint);
EP_DISABLE(self->epbulkin);
EP_DISABLE(self->epbulkout);
}
}
* Name: cdcecm_setconfig
*
* Set the device configuration by allocating and configuring endpoints and
* by allocating and queue read and write requests.
*
****************************************************************************/
static int cdcecm_setconfig(FAR struct cdcecm_driver_s *self, uint8_t config)
{
struct usb_ss_epdesc_s epdesc;
int ret = OK;
if (config == self->config)
{
return OK;
}
cdcecm_resetconfig(self);
if (config == CDCECM_CONFIGID_NONE)
{
return OK;
}
if (config != CDCECM_CONFIGID)
{
return -EINVAL;
}
cdcecm_mkepdesc(CDCECM_EP_INTIN_IDX,
&epdesc.epdesc, &self->devinfo, self->usbdev.speed);
ret = EP_CONFIGURE(self->epint, &epdesc.epdesc, false);
if (ret < 0)
{
goto error;
}
self->epint->priv = self;
cdcecm_mkepdesc(CDCECM_EP_BULKIN_IDX,
&epdesc.epdesc, &self->devinfo, self->usbdev.speed);
ret = EP_CONFIGURE(self->epbulkin, &epdesc.epdesc, false);
if (ret < 0)
{
goto error;
}
self->epbulkin->priv = self;
cdcecm_mkepdesc(CDCECM_EP_BULKOUT_IDX,
&epdesc.epdesc, &self->devinfo, self->usbdev.speed);
ret = EP_CONFIGURE(self->epbulkout, &epdesc.epdesc, true);
if (ret < 0)
{
goto error;
}
self->epbulkout->priv = self;
DEBUGASSERT(!self->rxpending);
self->rdreq->callback = cdcecm_rdcomplete,
ret = EP_SUBMIT(self->epbulkout, self->rdreq);
if (ret != OK)
{
uerr("EP_SUBMIT failed. ret %d\n", ret);
goto error;
}
self->config = config;
memcpy(self->dev.d_mac.ether.ether_addr_octet,
"\x00\xe0\xde\xad\xbe\xef", IFHWADDRLEN);
if (self->dev.d_ifup(&self->dev) == OK)
{
self->dev.d_flags |= IFF_UP;
}
return OK;
error:
cdcecm_resetconfig(self);
return ret;
}
* Name: cdcecm_setinterface
*
****************************************************************************/
static int cdcecm_setinterface(FAR struct cdcecm_driver_s *self,
uint16_t interface, uint16_t altsetting)
{
netdev_carrier_on(&self->dev);
uinfo("interface: %hu, altsetting: %hu\n", interface, altsetting);
return OK;
}
* Name: cdcecm_mkstrdesc
*
* Description:
* Construct a string descriptor
*
****************************************************************************/
static int cdcecm_mkstrdesc(uint8_t id, FAR struct usb_strdesc_s *strdesc)
{
FAR uint8_t *data = (FAR uint8_t *)(strdesc + 1);
FAR const char *str;
int len;
int ndata;
int i;
switch (id)
{
#ifndef CONFIG_CDCECM_COMPOSITE
case 0:
{
strdesc->len = 4;
strdesc->type = USB_DESC_TYPE_STRING;
data[0] = LSBYTE(CDCECM_STR_LANGUAGE);
data[1] = MSBYTE(CDCECM_STR_LANGUAGE);
return 4;
}
case CDCECM_MANUFACTURERSTRID:
str = CONFIG_CDCECM_VENDORSTR;
break;
case CDCECM_PRODUCTSTRID:
str = CONFIG_CDCECM_PRODUCTSTR;
break;
case CDCECM_SERIALSTRID:
#ifdef CONFIG_BOARD_USBDEV_SERIALSTR
str = board_usbdev_serialstr();
#else
str = "0";
#endif
break;
case CDCECM_CONFIGSTRID:
str = "Default";
break;
#endif
case CDCECM_MACSTRID:
str = "020000112233";
break;
default:
uwarn("Unknown string descriptor index: %d\n", id);
return -EINVAL;
}
* conversion below will only handle 7-bit en-us ascii
*/
len = strlen(str);
if (len > (CDCECM_MAXSTRLEN / 2))
{
len = (CDCECM_MAXSTRLEN / 2);
}
for (i = 0, ndata = 0; i < len; i++, ndata += 2)
{
data[ndata] = str[i];
data[ndata + 1] = 0;
}
strdesc->len = ndata + 2;
strdesc->type = USB_DESC_TYPE_STRING;
return strdesc->len;
}
* Name: cdcecm_mkepcompdesc
*
* Description:
* Construct the endpoint companion descriptor
*
****************************************************************************/
#ifdef CONFIG_USBDEV_SUPERSPEED
static void cdcecm_mkepcompdesc(int epidx,
FAR struct usb_ss_epcompdesc_s *epcompdesc)
{
switch (epidx)
{
case CDCECM_EP_INTIN_IDX:
{
epcompdesc->len = USB_SIZEOF_SS_EPCOMPDESC;
epcompdesc->type = USB_DESC_TYPE_ENDPOINT_COMPANION;
if (CONFIG_CDCECM_EPINTIN_MAXBURST >= USB_SS_INT_EP_MAXBURST)
{
epcompdesc->mxburst = USB_SS_INT_EP_MAXBURST - 1;
}
else
{
epcompdesc->mxburst = CONFIG_CDCECM_EPINTIN_MAXBURST;
}
epcompdesc->attr = 0;
epcompdesc->wbytes[0] = LSBYTE((epcompdesc->mxburst + 1) *
CONFIG_CDCECM_EPINTIN_SSSIZE);
epcompdesc->wbytes[1] = MSBYTE((epcompdesc->mxburst + 1) *
CONFIG_CDCECM_EPINTIN_SSSIZE);
}
break;
case CDCECM_EP_BULKOUT_IDX:
{
epcompdesc->len = USB_SIZEOF_SS_EPCOMPDESC;
epcompdesc->type = USB_DESC_TYPE_ENDPOINT_COMPANION;
if (CONFIG_CDCECM_EPBULKOUT_MAXBURST >= USB_SS_BULK_EP_MAXBURST)
{
epcompdesc->mxburst = USB_SS_BULK_EP_MAXBURST - 1;
}
else
{
epcompdesc->mxburst = CONFIG_CDCECM_EPBULKOUT_MAXBURST;
}
if (CONFIG_CDCECM_EPBULKOUT_MAXSTREAM > USB_SS_BULK_EP_MAXSTREAM)
{
epcompdesc->attr = USB_SS_BULK_EP_MAXSTREAM;
}
else
{
epcompdesc->attr = CONFIG_CDCECM_EPBULKOUT_MAXSTREAM;
}
epcompdesc->wbytes[0] = 0;
epcompdesc->wbytes[1] = 0;
}
break;
case CDCECM_EP_BULKIN_IDX:
{
epcompdesc->len = USB_SIZEOF_SS_EPCOMPDESC;
epcompdesc->type = USB_DESC_TYPE_ENDPOINT_COMPANION;
if (CONFIG_CDCECM_EPBULKIN_MAXBURST >= USB_SS_BULK_EP_MAXBURST)
{
epcompdesc->mxburst = USB_SS_BULK_EP_MAXBURST - 1;
}
else
{
epcompdesc->mxburst = CONFIG_CDCECM_EPBULKIN_MAXBURST;
}
if (CONFIG_CDCECM_EPBULKIN_MAXSTREAM > USB_SS_BULK_EP_MAXSTREAM)
{
epcompdesc->attr = USB_SS_BULK_EP_MAXSTREAM;
}
else
{
epcompdesc->attr = CONFIG_CDCECM_EPBULKIN_MAXSTREAM;
}
epcompdesc->wbytes[0] = 0;
epcompdesc->wbytes[1] = 0;
}
break;
default:
break;
}
}
#endif
* Name: cdcecm_mkepdesc
*
* Description:
* Construct the endpoint descriptor
*
****************************************************************************/
static int cdcecm_mkepdesc(int epidx,
FAR struct usb_epdesc_s *epdesc,
FAR struct usbdev_devinfo_s *devinfo,
uint8_t speed)
{
uint16_t intin_mxpktsz = CONFIG_CDCECM_EPINTIN_FSSIZE;
uint16_t bulkout_mxpktsz = CONFIG_CDCECM_EPBULKOUT_FSSIZE;
uint16_t bulkin_mxpktsz = CONFIG_CDCECM_EPBULKIN_FSSIZE;
int len = sizeof(struct usb_epdesc_s);
#ifdef CONFIG_USBDEV_SUPERSPEED
if (speed == USB_SPEED_SUPER ||
speed == USB_SPEED_SUPER_PLUS ||
speed == USB_SPEED_UNKNOWN)
{
intin_mxpktsz = CONFIG_CDCECM_EPINTIN_SSSIZE;
bulkout_mxpktsz = CONFIG_CDCECM_EPBULKOUT_SSSIZE;
bulkin_mxpktsz = CONFIG_CDCECM_EPBULKIN_SSSIZE;
len += sizeof(struct usb_ss_epcompdesc_s);
}
else
#endif
#ifdef CONFIG_USBDEV_DUALSPEED
if (speed == USB_SPEED_HIGH)
{
intin_mxpktsz = CONFIG_CDCECM_EPINTIN_HSSIZE;
bulkout_mxpktsz = CONFIG_CDCECM_EPBULKOUT_HSSIZE;
bulkin_mxpktsz = CONFIG_CDCECM_EPBULKIN_HSSIZE;
}
#else
UNUSED(speed);
#endif
if (epdesc == NULL)
{
return len;
}
epdesc->len = USB_SIZEOF_EPDESC;
epdesc->type = USB_DESC_TYPE_ENDPOINT;
switch (epidx)
{
case CDCECM_EP_INTIN_IDX:
{
epdesc->addr = USB_DIR_IN |
devinfo->epno[CDCECM_EP_INTIN_IDX];
epdesc->attr = USB_EP_ATTR_XFER_INT;
epdesc->mxpacketsize[0] = LSBYTE(intin_mxpktsz);
epdesc->mxpacketsize[1] = MSBYTE(intin_mxpktsz);
epdesc->interval = 5;
}
break;
case CDCECM_EP_BULKIN_IDX:
{
epdesc->addr = USB_DIR_IN |
devinfo->epno[CDCECM_EP_BULKIN_IDX];
epdesc->attr = USB_EP_ATTR_XFER_BULK;
epdesc->mxpacketsize[0] = LSBYTE(bulkin_mxpktsz);
epdesc->mxpacketsize[1] = MSBYTE(bulkin_mxpktsz);
epdesc->interval = 0;
}
break;
case CDCECM_EP_BULKOUT_IDX:
{
epdesc->addr = USB_DIR_OUT |
devinfo->epno[CDCECM_EP_BULKOUT_IDX];
epdesc->attr = USB_EP_ATTR_XFER_BULK;
epdesc->mxpacketsize[0] = LSBYTE(bulkout_mxpktsz);
epdesc->mxpacketsize[1] = MSBYTE(bulkout_mxpktsz);
epdesc->interval = 0;
}
break;
default:
DEBUGPANIC();
}
#ifdef CONFIG_USBDEV_SUPERSPEED
if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS)
{
epdesc++;
cdcecm_mkepcompdesc(epidx, (FAR struct usb_ss_epcompdesc_s *)epdesc);
}
#endif
return len;
}
* Name: cdcecm_mkcfgdesc
*
* Description:
* Construct the config descriptor
*
****************************************************************************/
static int16_t cdcecm_mkcfgdesc(FAR uint8_t *desc,
FAR struct usbdev_devinfo_s *devinfo,
uint8_t speed, uint8_t type)
{
#ifndef CONFIG_CDCECM_COMPOSITE
FAR struct usb_cfgdesc_s *cfgdesc = NULL;
#endif
int16_t len = 0;
int ret;
if (type == USB_DESC_TYPE_OTHERSPEEDCONFIG && speed < USB_SPEED_SUPER)
{
speed = speed == USB_SPEED_HIGH ? USB_SPEED_FULL : USB_SPEED_HIGH;
}
#ifndef CONFIG_CDCECM_COMPOSITE
if (desc)
{
cfgdesc = (FAR struct usb_cfgdesc_s *)desc;
cfgdesc->len = USB_SIZEOF_CFGDESC;
cfgdesc->type = type;
cfgdesc->ninterfaces = CDCECM_NINTERFACES;
cfgdesc->cfgvalue = CDCECM_CONFIGID;
cfgdesc->icfg = devinfo->strbase + CDCECM_CONFIGSTRID;
cfgdesc->attr = USB_CONFIG_ATTR_ONE | CDCECM_SELFPOWERED |
CDCECM_REMOTEWAKEUP;
cfgdesc->mxpower = (CONFIG_USBDEV_MAXPOWER + 1) / 2;
desc += USB_SIZEOF_CFGDESC;
}
len += USB_SIZEOF_CFGDESC;
#elif defined(CONFIG_COMPOSITE_IAD)
if (desc)
{
FAR struct usb_iaddesc_s *iaddesc = (FAR struct usb_iaddesc_s *)desc;
iaddesc->len = USB_SIZEOF_IADDESC;
iaddesc->type = USB_DESC_TYPE_INTERFACEASSOCIATION;
iaddesc->firstif = devinfo->ifnobase;
iaddesc->nifs = devinfo->ninterfaces;
iaddesc->classid = USB_CLASS_CDC;
iaddesc->subclass = CDC_SUBCLASS_ECM;
iaddesc->protocol = CDC_PROTO_NONE;
iaddesc->ifunction = 0;
desc += USB_SIZEOF_IADDESC;
}
len += USB_SIZEOF_IADDESC;
#endif
if (desc)
{
FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)desc;
ifdesc->len = USB_SIZEOF_IFDESC;
ifdesc->type = USB_DESC_TYPE_INTERFACE;
ifdesc->ifno = devinfo->ifnobase;
ifdesc->alt = 0;
ifdesc->neps = 1;
ifdesc->classid = USB_CLASS_CDC;
ifdesc->subclass = CDC_SUBCLASS_ECM;
ifdesc->protocol = CDC_PROTO_NONE;
ifdesc->iif = 0;
desc += USB_SIZEOF_IFDESC;
}
len += USB_SIZEOF_IFDESC;
if (desc)
{
FAR struct cdc_hdr_funcdesc_s *hdrdesc;
hdrdesc = (FAR struct cdc_hdr_funcdesc_s *)desc;
hdrdesc->size = SIZEOF_HDR_FUNCDESC;
hdrdesc->type = USB_DESC_TYPE_CSINTERFACE;
hdrdesc->subtype = CDC_DSUBTYPE_HDR;
hdrdesc->cdc[0] = LSBYTE(0x0110);
hdrdesc->cdc[1] = MSBYTE(0x0110);
desc += SIZEOF_HDR_FUNCDESC;
}
len += SIZEOF_HDR_FUNCDESC;
if (desc)
{
FAR struct cdc_union_funcdesc_s *uniondesc;
uniondesc = (FAR struct cdc_union_funcdesc_s *)desc;
uniondesc->size = SIZEOF_UNION_FUNCDESC(1);
uniondesc->type = USB_DESC_TYPE_CSINTERFACE;
uniondesc->subtype = CDC_DSUBTYPE_UNION;
uniondesc->master = devinfo->ifnobase;
uniondesc->slave[0] = devinfo->ifnobase + 1;
desc += SIZEOF_UNION_FUNCDESC(1);
}
len += SIZEOF_UNION_FUNCDESC(1);
if (desc)
{
FAR struct cdc_ecm_funcdesc_s *ecmdesc;
ecmdesc = (FAR struct cdc_ecm_funcdesc_s *)desc;
ecmdesc->size = SIZEOF_ECM_FUNCDESC;
ecmdesc->type = USB_DESC_TYPE_CSINTERFACE;
ecmdesc->subtype = CDC_DSUBTYPE_ECM;
ecmdesc->mac = devinfo->strbase + CDCECM_MACSTRID;
ecmdesc->stats[0] = 0;
ecmdesc->stats[1] = 0;
ecmdesc->stats[2] = 0;
ecmdesc->stats[3] = 0;
ecmdesc->maxseg[0] = LSBYTE(CONFIG_NET_ETH_PKTSIZE);
ecmdesc->maxseg[1] = MSBYTE(CONFIG_NET_ETH_PKTSIZE);
ecmdesc->nmcflts[0] = LSBYTE(0);
ecmdesc->nmcflts[1] = MSBYTE(0);
ecmdesc->npwrflts = 0;
desc += SIZEOF_ECM_FUNCDESC;
}
len += SIZEOF_ECM_FUNCDESC;
ret = cdcecm_mkepdesc(CDCECM_EP_INTIN_IDX,
(FAR struct usb_epdesc_s *)desc,
devinfo, speed);
if (desc)
{
desc += ret;
}
len += ret;
if (desc)
{
FAR struct usb_ifdesc_s *ifdesc;
ifdesc = (FAR struct usb_ifdesc_s *)desc;
ifdesc->len = USB_SIZEOF_IFDESC;
ifdesc->type = USB_DESC_TYPE_INTERFACE;
ifdesc->ifno = devinfo->ifnobase + 1;
ifdesc->alt = 0;
ifdesc->neps = 0;
ifdesc->classid = USB_CLASS_CDC_DATA;
ifdesc->subclass = CDC_SUBCLASS_ECM;
ifdesc->protocol = CDC_PROTO_NONE;
ifdesc->iif = 0;
desc += USB_SIZEOF_IFDESC;
}
len += USB_SIZEOF_IFDESC;
if (desc)
{
FAR struct usb_ifdesc_s *ifdesc;
ifdesc = (FAR struct usb_ifdesc_s *)desc;
ifdesc->len = USB_SIZEOF_IFDESC;
ifdesc->type = USB_DESC_TYPE_INTERFACE;
ifdesc->ifno = devinfo->ifnobase + 1;
ifdesc->alt = 1;
ifdesc->neps = 2;
ifdesc->classid = USB_CLASS_CDC_DATA;
ifdesc->subclass = CDC_SUBCLASS_ECM;
ifdesc->protocol = CDC_PROTO_NONE;
ifdesc->iif = 0;
desc += USB_SIZEOF_IFDESC;
}
len += USB_SIZEOF_IFDESC;
ret = cdcecm_mkepdesc(CDCECM_EP_BULKIN_IDX,
(FAR struct usb_epdesc_s *)desc,
devinfo, speed);
if (desc)
{
desc += ret;
}
len += ret;
ret = cdcecm_mkepdesc(CDCECM_EP_BULKOUT_IDX,
(FAR struct usb_epdesc_s *)desc,
devinfo, speed);
if (desc)
{
desc += ret;
}
len += ret;
#ifndef CONFIG_CDCECM_COMPOSITE
if (cfgdesc)
{
cfgdesc->totallen[0] = LSBYTE(len);
cfgdesc->totallen[1] = MSBYTE(len);
}
#endif
DEBUGASSERT(len <= CDCECM_MXDESCLEN);
return len;
}
* Name: cdcecm_getdescriptor
*
* Description:
* Copy the USB CDC-ECM Device USB Descriptor of a given Type and a given
* Index into the provided Descriptor Buffer.
*
* Input Parameter:
* drvr - The USB Device Fuzzer Driver instance.
* type - The Type of USB Descriptor requested.
* index - The Index of the USB Descriptor requested.
* desc - The USB Descriptor is copied into this buffer, which must be at
* least CDCECM_MXDESCLEN bytes wide.
*
* Returned Value:
* The size in bytes of the requested USB Descriptor or a negated errno in
* case of failure.
*
****************************************************************************/
static int cdcecm_getdescriptor(FAR struct cdcecm_driver_s *self,
uint8_t type, uint8_t index, FAR void *desc)
{
uinfo("type: 0x%02hhx, index: 0x%02hhx\n", type, index);
switch (type)
{
#ifndef CONFIG_CDCECM_COMPOSITE
case USB_DESC_TYPE_DEVICE:
{
return usbdev_copy_devdesc(desc, &g_devdesc, self->usbdev.speed);
}
break;
#endif
#ifdef CONFIG_USBDEV_DUALSPEED
case USB_DESC_TYPE_OTHERSPEEDCONFIG:
#endif
case USB_DESC_TYPE_CONFIG:
{
return cdcecm_mkcfgdesc((FAR uint8_t *)desc, &self->devinfo,
self->usbdev.speed, type);
}
break;
case USB_DESC_TYPE_STRING:
{
return cdcecm_mkstrdesc(index, (FAR struct usb_strdesc_s *)desc);
}
break;
default:
uwarn("Unsupported descriptor type: 0x%02hhx\n", type);
break;
}
return -ENOTSUP;
}
* USB Device Class Methods
****************************************************************************/
* Name: cdcecm_bind
*
* Description:
* Invoked when the driver is bound to an USB device
*
****************************************************************************/
static int cdcecm_bind(FAR struct usbdevclass_driver_s *driver,
FAR struct usbdev_s *dev)
{
FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)driver;
int ret = OK;
uinfo("\n");
#ifndef CONFIG_CDCECM_COMPOSITE
dev->ep0->priv = self;
#endif
self->ctrlreq = usbdev_allocreq(dev->ep0, CDCECM_MXDESCLEN);
if (self->ctrlreq == NULL)
{
ret = -ENOMEM;
goto error;
}
self->ctrlreq->callback = cdcecm_ep0incomplete;
self->epint = DEV_ALLOCEP(dev,
USB_DIR_IN |
self->devinfo.epno[CDCECM_EP_INTIN_IDX],
true, USB_EP_ATTR_XFER_INT);
self->epbulkin = DEV_ALLOCEP(dev,
USB_DIR_IN |
self->devinfo.epno[CDCECM_EP_BULKIN_IDX],
true, USB_EP_ATTR_XFER_BULK);
self->epbulkout = DEV_ALLOCEP(dev,
USB_DIR_OUT |
self->devinfo.epno[CDCECM_EP_BULKOUT_IDX],
false, USB_EP_ATTR_XFER_BULK);
if (!self->epint || !self->epbulkin || !self->epbulkout)
{
uerr("Failed to allocate endpoints!\n");
ret = -ENODEV;
goto error;
}
self->epint->priv = self;
self->epbulkin->priv = self;
self->epbulkout->priv = self;
self->rdreq = usbdev_allocreq(self->epbulkout,
CONFIG_NET_ETH_PKTSIZE + CONFIG_NET_GUARDSIZE);
if (self->rdreq == NULL)
{
uerr("Out of memory\n");
ret = -ENOMEM;
goto error;
}
self->rdreq->callback = cdcecm_rdcomplete;
self->wrreq = usbdev_allocreq(self->epbulkin,
CONFIG_NET_ETH_PKTSIZE + CONFIG_NET_GUARDSIZE);
if (self->wrreq == NULL)
{
uerr("Out of memory\n");
ret = -ENOMEM;
goto error;
}
self->wrreq->callback = cdcecm_wrcomplete;
ret = nxsem_init(&self->wrreq_idle, 0, 1);
if (ret != OK)
{
uerr("nxsem_init failed. ret: %d\n", ret);
goto error;
}
self->txdone = false;
self->dev.d_len = 0;
#ifndef CONFIG_CDCECM_COMPOSITE
#ifdef CONFIG_USBDEV_SELFPOWERED
DEV_SETSELFPOWERED(dev);
#endif
* part of a composite device)
*/
DEV_CONNECT(dev);
#endif
return OK;
error:
uerr("cdcecm_bind failed! ret: %d\n", ret);
cdcecm_unbind(driver, dev);
return ret;
}
static void cdcecm_unbind(FAR struct usbdevclass_driver_s *driver,
FAR struct usbdev_s *dev)
{
FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)driver;
#ifdef CONFIG_DEBUG_FEATURES
if (!driver || !dev)
{
usbtrace(TRACE_CLSERROR(USBSER_TRACEERR_INVALIDARG), 0);
return;
}
#endif
* we were terminated gracefully, then the configuration should
* already have been reset. If not, then calling cdcacm_resetconfig
* should cause the endpoints to immediately terminate all
* transfers and return the requests to us (with result == -ESHUTDOWN)
*/
cdcecm_resetconfig(self);
up_mdelay(50);
if (self->epint)
{
DEV_FREEEP(dev, self->epint);
self->epint = NULL;
}
if (self->ctrlreq != NULL)
{
usbdev_freereq(dev->ep0, self->ctrlreq);
self->ctrlreq = NULL;
}
* been returned to the free list at this time -- we don't check)
*/
if (self->rdreq != NULL)
{
usbdev_freereq(self->epbulkout, self->rdreq);
self->rdreq = NULL;
}
if (self->epbulkout)
{
DEV_FREEEP(dev, self->epbulkout);
self->epbulkout = NULL;
}
* of them)
*/
if (self->wrreq != NULL)
{
usbdev_freereq(self->epbulkin, self->wrreq);
self->wrreq = NULL;
}
if (self->epbulkin)
{
DEV_FREEEP(dev, self->epbulkin);
self->epbulkin = NULL;
}
self->dev.d_len = 0;
}
static int cdcecm_setup(FAR struct usbdevclass_driver_s *driver,
FAR struct usbdev_s *dev,
FAR const struct usb_ctrlreq_s *ctrl,
FAR uint8_t *dataout,
size_t outlen)
{
FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)driver;
uint16_t value = GETUINT16(ctrl->value);
uint16_t index = GETUINT16(ctrl->index);
uint16_t len = GETUINT16(ctrl->len);
int ret = -EOPNOTSUPP;
uinfo("\n");
if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD)
{
switch (ctrl->req)
{
case USB_REQ_GETDESCRIPTOR:
{
uint8_t descindex = ctrl->value[0];
uint8_t desctype = ctrl->value[1];
self->usbdev.speed = dev->speed;
ret = cdcecm_getdescriptor(self, desctype, descindex,
self->ctrlreq->buf);
}
break;
case USB_REQ_SETCONFIGURATION:
ret = cdcecm_setconfig(self, value);
break;
case USB_REQ_SETINTERFACE:
ret = cdcecm_setinterface(self, index, value);
break;
default:
uwarn("Unsupported standard req: 0x%02hhx\n", ctrl->req);
break;
}
}
else if ((ctrl->type & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS)
{
switch (ctrl->req)
{
case ECM_SET_PACKET_FILTER:
* specific request, but it is still ok to always operate in
* promiscuous mode and rely on the host to do the filtering.
* This is especially true for our case:
* A simulated point-to-point connection.
*/
uinfo("ECM_SET_PACKET_FILTER wValue: 0x%04hx, wIndex: 0x%04hx\n",
GETUINT16(ctrl->value), GETUINT16(ctrl->index));
ret = OK;
break;
default:
uwarn("Unsupported class req: 0x%02hhx\n", ctrl->req);
break;
}
}
else
{
uwarn("Unsupported type: 0x%02hhx\n", ctrl->type);
}
if (ret >= 0)
{
FAR struct usbdev_req_s *ctrlreq = self->ctrlreq;
ctrlreq->len = MIN(len, ret);
ctrlreq->flags = USBDEV_REQFLAGS_NULLPKT;
#ifndef CONFIG_CDCECM_COMPOSITE
ret = EP_SUBMIT(dev->ep0, ctrlreq);
uinfo("EP_SUBMIT ret: %d\n", ret);
#else
ret = composite_ep0submit(driver, dev, ctrlreq, ctrl);
#endif
if (ret < 0)
{
ctrlreq->result = OK;
cdcecm_ep0incomplete(dev->ep0, ctrlreq);
}
}
return ret;
}
static void cdcecm_disconnect(FAR struct usbdevclass_driver_s *driver,
FAR struct usbdev_s *dev)
{
uinfo("\n");
}
* Name: cdcecm_classobject
*
* Description:
* Register USB CDC/ECM and return the class object.
*
* Returned Value:
* A pointer to the allocated class object (NULL on failure).
*
****************************************************************************/
static int cdcecm_classobject(int minor,
FAR struct usbdev_devinfo_s *devinfo,
FAR struct usbdevclass_driver_s **classdev)
{
FAR struct cdcecm_driver_s *self;
int ret;
self = kmm_zalloc(sizeof(struct cdcecm_driver_s));
if (!self)
{
nerr("Out of memory!\n");
return -ENOMEM;
}
self->dev.d_buf = (FAR uint8_t *)self->pktbuf;
self->dev.d_ifup = cdcecm_ifup;
self->dev.d_ifdown = cdcecm_ifdown;
self->dev.d_txavail = cdcecm_txavail;
#ifdef CONFIG_NET_MCASTGROUP
self->dev.d_addmac = cdcecm_addmac;
self->dev.d_rmmac = cdcecm_rmmac;
#endif
#ifdef CONFIG_NETDEV_IOCTL
self->dev.d_ioctl = cdcecm_ioctl;
#endif
self->dev.d_private = self;
#if defined(CONFIG_USBDEV_SUPERSPEED)
self->usbdev.speed = USB_SPEED_SUPER;
#elif defined(CONFIG_USBDEV_DUALSPEED)
self->usbdev.speed = USB_SPEED_HIGH;
#else
self->usbdev.speed = USB_SPEED_FULL;
#endif
self->usbdev.ops = &g_usbdevops;
memcpy(&self->devinfo, devinfo, sizeof(struct usbdev_devinfo_s));
* the device and/or calling cdcecm_ifdown().
*/
cdcecm_ifdown(&self->dev);
* priv->dev.d_mac.ether.ether_addr_octet
* Applies only if the Ethernet MAC has its own internal address.
*/
memcpy(self->dev.d_mac.ether.ether_addr_octet,
"\x00\xe0\xde\xad\xbe\xef", IFHWADDRLEN);
ret = netdev_register(&self->dev, NET_LL_ETHERNET);
if (ret < 0)
{
nerr("netdev_register failed. ret: %d\n", ret);
}
*classdev = (FAR struct usbdevclass_driver_s *)self;
return ret;
}
* Name: cdcecm_uninitialize
*
* Description:
* Un-initialize the USB CDC/ECM class driver. This function is used
* internally by the USB composite driver to uninitialize the CDC/ECM
* driver. This same interface is available (with an untyped input
* parameter) when the CDC/ECM driver is used standalone.
*
* Input Parameters:
* There is one parameter, it differs in typing depending upon whether the
* CDC/ECM driver is an internal part of a composite device, or a
* standalone USB driver:
*
* classdev - The class object returned by cdcecm_classobject()
*
* Returned Value:
* None
*
****************************************************************************/
void cdcecm_uninitialize(FAR struct usbdevclass_driver_s *classdev)
{
FAR struct cdcecm_driver_s *self = (FAR struct cdcecm_driver_s *)classdev;
int ret;
ret = netdev_unregister(&self->dev);
if (ret < 0)
{
nerr("ERROR: netdev_unregister failed. ret: %d\n", ret);
}
#ifndef CONFIG_CDCECM_COMPOSITE
usbdev_unregister(&self->usbdev);
#endif
kmm_free(self);
}
* Public Functions
****************************************************************************/
* Name: cdcecm_initialize
*
* Description:
* Register CDC/ECM USB device interface. Register the corresponding
* network driver to NuttX and bring up the network.
*
* Input Parameters:
* minor - Device minor number.
* handle - An optional opaque reference to the CDC/ECM class object that
* may subsequently be used with cdcecm_uninitialize().
*
* Returned Value:
* Zero (OK) means that the driver was successfully registered. On any
* failure, a negated errno value is returned.
*
****************************************************************************/
#ifndef CONFIG_CDCECM_COMPOSITE
int cdcecm_initialize(int minor, FAR void **handle)
{
FAR struct usbdevclass_driver_s *drvr = NULL;
struct usbdev_devinfo_s devinfo;
int ret;
memset(&devinfo, 0, sizeof(struct usbdev_devinfo_s));
devinfo.ninterfaces = CDCECM_NINTERFACES;
devinfo.nstrings = CDCECM_NSTRIDS;
devinfo.nendpoints = CDCECM_NUM_EPS;
devinfo.epno[CDCECM_EP_INTIN_IDX] = CONFIG_CDCECM_EPINTIN;
devinfo.epno[CDCECM_EP_BULKIN_IDX] = CONFIG_CDCECM_EPBULKIN;
devinfo.epno[CDCECM_EP_BULKOUT_IDX] = CONFIG_CDCECM_EPBULKOUT;
ret = cdcecm_classobject(minor, &devinfo, &drvr);
if (ret == OK)
{
ret = usbdev_register(drvr);
if (ret < 0)
{
uinfo("usbdev_register failed. ret %d\n", ret);
}
}
if (handle)
{
*handle = (FAR void *)drvr;
}
return ret;
}
#endif
* Name: cdcecm_get_composite_devdesc
*
* Description:
* Helper function to fill in some constants into the composite
* configuration struct.
*
* Input Parameters:
* dev - Pointer to the configuration struct we should fill
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_CDCECM_COMPOSITE
void cdcecm_get_composite_devdesc(struct composite_devdesc_s *dev)
{
memset(dev, 0, sizeof(struct composite_devdesc_s));
*
* classobject() and uninitialize() must be provided by board-specific
* logic
*/
dev->mkconfdesc = cdcecm_mkcfgdesc;
dev->mkstrdesc = cdcecm_mkstrdesc;
dev->classobject = cdcecm_classobject;
dev->uninitialize = cdcecm_uninitialize;
dev->nconfigs = CDCECM_NCONFIGS;
dev->configid = CDCECM_CONFIGID;
dev->cfgdescsize = cdcecm_mkcfgdesc(NULL, NULL, USB_SPEED_UNKNOWN, 0);
*
* ifnobase must be provided by board-specific logic
*/
dev->devinfo.ninterfaces = CDCECM_NINTERFACES;
*
* strbase must be provided by board-specific logic
*/
dev->devinfo.nstrings = CDCECM_NSTRIDS + 1;
*
* Endpoint numbers must be provided by board-specific logic.
*/
dev->devinfo.nendpoints = CDCECM_NUM_EPS;
}
#endif
#endif