📄 ether.c
字号:
if (dest [0] & 0x01) {
u16 type;
/* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
* SET_ETHERNET_MULTICAST_FILTERS requests
*/
if (memcmp (dest, net->broadcast, ETH_ALEN) == 0)
type = USB_CDC_PACKET_TYPE_BROADCAST;
else
type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
if (!(dev->cdc_filter & type)) {
dev_kfree_skb_any (skb);
return 0;
}
}
/* ignores USB_CDC_PACKET_TYPE_DIRECTED */
}
spin_lock_irqsave (&dev->lock, flags);
req = container_of (dev->tx_reqs.next, struct usb_request, list);
list_del (&req->list);
if (list_empty (&dev->tx_reqs))
netif_stop_queue (net);
spin_unlock_irqrestore (&dev->lock, flags);
/* no buffer copies needed, unless the network stack did it
* or the hardware can't use skb buffers.
* or there's not enough space for any RNDIS headers we need
*/
if (rndis_active(dev)) {
struct sk_buff *skb_rndis;
skb_rndis = skb_realloc_headroom (skb,
sizeof (struct rndis_packet_msg_type));
if (!skb_rndis)
goto drop;
dev_kfree_skb_any (skb);
skb = skb_rndis;
rndis_add_hdr (skb);
length = skb->len;
}
req->buf = skb->data;
req->context = skb;
req->complete = tx_complete;
/* use zlp framing on tx for strict CDC-Ether conformance,
* though any robust network rx path ignores extra padding.
* and some hardware doesn't like to write zlps.
*/
req->zero = 1;
if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0)
length++;
req->length = length;
#ifdef CONFIG_USB_GADGET_DUALSPEED
/* throttle highspeed IRQ rate back slightly */
req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
? ((atomic_read (&dev->tx_qlen) % TX_DELAY) != 0)
: 0;
#endif
retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);
switch (retval) {
default:
DEBUG (dev, "tx queue err %d\n", retval);
break;
case 0:
net->trans_start = jiffies;
atomic_inc (&dev->tx_qlen);
}
if (retval) {
drop:
dev->stats.tx_dropped++;
dev_kfree_skb_any (skb);
spin_lock_irqsave (&dev->lock, flags);
if (list_empty (&dev->tx_reqs))
netif_start_queue (net);
list_add (&req->list, &dev->tx_reqs);
spin_unlock_irqrestore (&dev->lock, flags);
}
return 0;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_ETH_RNDIS
/* The interrupt endpoint is used in RNDIS to notify the host when messages
* other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT
* messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even
* REMOTE_NDIS_KEEPALIVE_MSG.
*
* The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and
* normally just one notification will be queued.
*/
static struct usb_request *eth_req_alloc (struct usb_ep *, unsigned, unsigned);
static void eth_req_free (struct usb_ep *ep, struct usb_request *req);
static void
rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
{
struct eth_dev *dev = ep->driver_data;
if (req->status || req->actual != req->length)
DEBUG (dev,
"rndis control ack complete --> %d, %d/%d\n",
req->status, req->actual, req->length);
req->context = NULL;
if (req != dev->stat_req)
eth_req_free(ep, req);
}
static int rndis_control_ack (struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
u32 length;
struct usb_request *resp = dev->stat_req;
/* in case RNDIS calls this after disconnect */
if (!dev->status) {
DEBUG (dev, "status ENODEV\n");
return -ENODEV;
}
/* in case queue length > 1 */
if (resp->context) {
resp = eth_req_alloc (dev->status_ep, 8, GFP_ATOMIC);
if (!resp)
return -ENOMEM;
}
/* Send RNDIS RESPONSE_AVAILABLE notification;
* USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too
*/
resp->length = 8;
resp->complete = rndis_control_ack_complete;
resp->context = dev;
*((__le32 *) resp->buf) = __constant_cpu_to_le32 (1);
*((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0);
length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC);
if (length < 0) {
resp->status = 0;
rndis_control_ack_complete (dev->status_ep, resp);
}
return 0;
}
#else
#define rndis_control_ack NULL
#endif /* RNDIS */
static void eth_start (struct eth_dev *dev, unsigned gfp_flags)
{
DEBUG (dev, "%s\n", __FUNCTION__);
/* fill the rx queue */
rx_fill (dev, gfp_flags);
/* and open the tx floodgates */
atomic_set (&dev->tx_qlen, 0);
netif_wake_queue (dev->net);
if (rndis_active(dev)) {
rndis_set_param_medium (dev->rndis_config,
NDIS_MEDIUM_802_3,
BITRATE(dev->gadget)/100);
(void) rndis_signal_connect (dev->rndis_config);
}
}
static int eth_open (struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
DEBUG (dev, "%s\n", __FUNCTION__);
if (netif_carrier_ok (dev->net))
eth_start (dev, GFP_KERNEL);
return 0;
}
static int eth_stop (struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
VDEBUG (dev, "%s\n", __FUNCTION__);
netif_stop_queue (net);
DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
dev->stats.rx_packets, dev->stats.tx_packets,
dev->stats.rx_errors, dev->stats.tx_errors
);
/* ensure there are no more active requests */
if (dev->config) {
usb_ep_disable (dev->in_ep);
usb_ep_disable (dev->out_ep);
if (netif_carrier_ok (dev->net)) {
DEBUG (dev, "host still using in/out endpoints\n");
// FIXME idiom may leave toggle wrong here
usb_ep_enable (dev->in_ep, dev->in);
usb_ep_enable (dev->out_ep, dev->out);
}
if (dev->status_ep) {
usb_ep_disable (dev->status_ep);
usb_ep_enable (dev->status_ep, dev->status);
}
}
if (rndis_active(dev)) {
rndis_set_param_medium (dev->rndis_config,
NDIS_MEDIUM_802_3, 0);
(void) rndis_signal_disconnect (dev->rndis_config);
}
return 0;
}
/*-------------------------------------------------------------------------*/
static struct usb_request *
eth_req_alloc (struct usb_ep *ep, unsigned size, unsigned gfp_flags)
{
struct usb_request *req;
req = usb_ep_alloc_request (ep, gfp_flags);
if (!req)
return NULL;
req->buf = kmalloc (size, gfp_flags);
if (!req->buf) {
usb_ep_free_request (ep, req);
req = NULL;
}
return req;
}
static void
eth_req_free (struct usb_ep *ep, struct usb_request *req)
{
kfree (req->buf);
usb_ep_free_request (ep, req);
}
static void
eth_unbind (struct usb_gadget *gadget)
{
struct eth_dev *dev = get_gadget_data (gadget);
DEBUG (dev, "unbind\n");
rndis_deregister (dev->rndis_config);
rndis_exit ();
/* we've already been disconnected ... no i/o is active */
if (dev->req) {
eth_req_free (gadget->ep0, dev->req);
dev->req = NULL;
}
if (dev->stat_req) {
eth_req_free (dev->status_ep, dev->stat_req);
dev->stat_req = NULL;
}
unregister_netdev (dev->net);
free_netdev(dev->net);
/* assuming we used keventd, it must quiesce too */
flush_scheduled_work ();
set_gadget_data (gadget, NULL);
}
static u8 __init nibble (unsigned char c)
{
if (likely (isdigit (c)))
return c - '0';
c = toupper (c);
if (likely (isxdigit (c)))
return 10 + c - 'A';
return 0;
}
static void __init get_ether_addr (const char *str, u8 *dev_addr)
{
if (str) {
unsigned i;
for (i = 0; i < 6; i++) {
unsigned char num;
if((*str == '.') || (*str == ':'))
str++;
num = nibble(*str++) << 4;
num |= (nibble(*str++));
dev_addr [i] = num;
}
if (is_valid_ether_addr (dev_addr))
return;
}
random_ether_addr(dev_addr);
}
static int __init
eth_bind (struct usb_gadget *gadget)
{
struct eth_dev *dev;
struct net_device *net;
u8 cdc = 1, zlp = 1, rndis = 1;
struct usb_ep *in_ep, *out_ep, *status_ep = NULL;
int status = -ENOMEM;
int gcnum;
/* these flags are only ever cleared; compiler take note */
#ifndef DEV_CONFIG_CDC
cdc = 0;
#endif
#ifndef CONFIG_USB_ETH_RNDIS
rndis = 0;
#endif
/* Because most host side USB stacks handle CDC Ethernet, that
* standard protocol is _strongly_ preferred for interop purposes.
* (By everyone except Microsoft.)
*/
if (gadget_is_pxa (gadget)) {
/* pxa doesn't support altsettings */
cdc = 0;
} else if (gadget_is_sh(gadget)) {
/* sh doesn't support multiple interfaces or configs */
cdc = 0;
rndis = 0;
} else if (gadget_is_sa1100 (gadget)) {
/* hardware can't write zlps */
zlp = 0;
/* sa1100 CAN do CDC, without status endpoint ... we use
* non-CDC to be compatible with ARM Linux-2.4 "usb-eth".
*/
cdc = 0;
}
gcnum = usb_gadget_controller_number (gadget);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16 (0x0200 + gcnum);
else {
/* can't assume CDC works. don't want to default to
* anything less functional on CDC-capable hardware,
* so we fail in this case.
*/
dev_err (&gadget->dev,
"controller '%s' not recognized\n",
gadget->name);
return -ENODEV;
}
snprintf (manufacturer, sizeof manufacturer, "%s %s/%s",
system_utsname.sysname, system_utsname.release,
gadget->name);
/* If there's an RNDIS configuration, that's what Windows wants to
* be using ... so use these product IDs here and in the "linux.inf"
* needed to install MSFT drivers. Current Linux kernels will use
* the second configuration if it's CDC Ethernet, and need some help
* to choose the right configuration otherwise.
*/
if (rndis) {
device_desc.idVendor =
__constant_cpu_to_le16(RNDIS_VENDOR_NUM);
device_desc.idProduct =
__constant_cpu_to_le16(RNDIS_PRODUCT_NUM);
snprintf (product_desc, sizeof product_desc,
"RNDIS/%s", driver_desc);
/* CDC subset ... recognized by Linux since 2.4.10, but Windows
* drivers aren't widely available.
*/
} else if (!cdc) {
device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
device_desc.idVendor =
__constant_cpu_to_le16(SIMPLE_VENDOR_NUM);
device_desc.idProduct =
__constant_cpu_to_le16(SIMPLE_PRODUCT_NUM);
}
/* support optional vendor/distro customization */
if (idVendor) {
if (!idProduct) {
dev_err (&gadget->dev, "idVendor needs idProduct!\n");
return -ENODEV;
}
device_desc.idVendor = cpu_to_le16(idVendor);
device_desc.idProduct = cpu_to_le16(idProduct);
if (bcdDevice)
device_desc.bcdDevice = cpu_to_le16(bcdDevice);
}
if (iManufacturer)
strlcpy (manufacturer, iManufacturer, sizeof manufacturer);
if (iProduct)
strlcpy (product_desc, iProduct, sizeof product_desc);
/* all we really need is bulk IN/OUT */
usb_ep_autoconfig_reset (gadget);
in_ep = usb_ep_autoconfig (gadget, &fs_source_desc);
if (!in_ep) {
autoconf_fail:
dev_err (&gadget->dev,
"can't autoconfigure on %s\n",
gadget->name);
return -ENODEV;
}
in_ep->driver_data = in_ep; /* claim */
out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
if (!out_ep)
goto autoconf_fail;
out_ep->driver_data = out_ep; /* claim */
#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
/* CDC Ethernet control interface doesn't require a status endpoint.
* Since some hosts expect one, try to allocate one anyway.
*/
if (cdc || rndis) {
status_ep = usb_ep_autoconfig (gadget, &fs_status_desc);
if (status_ep) {
status_ep->driver_data = status_ep; /* claim */
} else if (rndis) {
dev_err (&gadget->dev,
"can't run RNDIS on %s\n",
gadget->name);
return -ENODEV;
#ifdef DEV_CONFIG_CDC
/* pxa25x only does CDC subset; often used with RNDIS */
} else if (cdc) {
control_intf.bNumEndpoints = 0;
/* FIXME remove endpoint from descriptor list */
#endif
}
}
#endif
/* one config: cdc, else minimal subset */
if (!cdc) {
eth_config.bNumInterfaces = 1;
eth_config.iConfiguration = STRING_SUBSET;
fs_subset_descriptors();
hs_subset_descriptors();
}
/* For now RNDIS is always a second config */
if (rndis)
device_desc.bNumConfigurations = 2;
#ifdef CONFIG_USB_GADGET_DUALSPEED
if (rndis)
dev_qualifier.bNumConfigurations = 2
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -