📄 usbnet.c
字号:
struct usb_interface *control; struct usb_interface *data;};static struct usb_driver usbnet_driver;/* * probes control interface, claims data interface, collects the bulk * endpoints, activates data interface (if needed), maybe sets MTU. * all pure cdc, except for certain firmware workarounds. */static int generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf){ u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *d; struct cdc_state *info = (void *) &dev->data; int status; int rndis; if (sizeof dev->data < sizeof *info) return -EDOM; /* expect strict spec conformance for the descriptors, but * cope with firmware which stores them in the wrong place */ if (len == 0 && dev->udev->actconfig->extralen) { /* Motorola SB4100 (and others: Brad Hards says it's * from a Broadcom design) put CDC descriptors here */ buf = dev->udev->actconfig->extra; len = dev->udev->actconfig->extralen; if (len) dev_dbg (&intf->dev, "CDC descriptors on config\n"); } /* this assumes that if there's a non-RNDIS vendor variant * of cdc-acm, it'll fail RNDIS requests cleanly. */ rndis = (intf->cur_altsetting->desc.bInterfaceProtocol == 0xff); memset (info, 0, sizeof *info); info->control = intf; while (len > 3) { if (buf [1] != USB_DT_CS_INTERFACE) goto next_desc; /* use bDescriptorSubType to identify the CDC descriptors. * We expect devices with CDC header and union descriptors. * For CDC Ethernet we need the ethernet descriptor. * For RNDIS, ignore two (pointless) CDC modem descriptors * in favor of a complicated OID-based RPC scheme doing what * CDC Ethernet achieves with a simple descriptor. */ switch (buf [2]) { case 0x00: /* Header, mostly useless */ if (info->header) { dev_dbg (&intf->dev, "extra CDC header\n"); goto bad_desc; } info->header = (void *) buf; if (info->header->bLength != sizeof *info->header) { dev_dbg (&intf->dev, "CDC header len %u\n", info->header->bLength); goto bad_desc; } break; case 0x06: /* Union (groups interfaces) */ if (info->u) { dev_dbg (&intf->dev, "extra CDC union\n"); goto bad_desc; } info->u = (void *) buf; if (info->u->bLength != sizeof *info->u) { dev_dbg (&intf->dev, "CDC union len %u\n", info->u->bLength); goto bad_desc; } /* we need a master/control interface (what we're * probed with) and a slave/data interface; union * descriptors sort this all out. */ info->control = usb_ifnum_to_if(dev->udev, info->u->bMasterInterface0); info->data = usb_ifnum_to_if(dev->udev, info->u->bSlaveInterface0); if (!info->control || !info->data) { dev_dbg (&intf->dev, "master #%u/%p slave #%u/%p\n", info->u->bMasterInterface0, info->control, info->u->bSlaveInterface0, info->data); goto bad_desc; } if (info->control != intf) { dev_dbg (&intf->dev, "bogus CDC Union\n"); /* Ambit USB Cable Modem (and maybe others) * interchanges master and slave interface. */ if (info->data == intf) { info->data = info->control; info->control = intf; } else goto bad_desc; } /* a data interface altsetting does the real i/o */ d = &info->data->cur_altsetting->desc; if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { dev_dbg (&intf->dev, "slave class %u\n", d->bInterfaceClass); goto bad_desc; } break; case 0x0F: /* Ethernet Networking */ if (info->ether) { dev_dbg (&intf->dev, "extra CDC ether\n"); goto bad_desc; } info->ether = (void *) buf; if (info->ether->bLength != sizeof *info->ether) { dev_dbg (&intf->dev, "CDC ether len %u\n", info->u->bLength); goto bad_desc; } dev->net->mtu = le16_to_cpup ( &info->ether->wMaxSegmentSize) - ETH_HLEN; /* because of Zaurus, we may be ignoring the host * side link address we were given. */ break; }next_desc: len -= buf [0]; /* bLength */ buf += buf [0]; } if (!info->header || !info->u || (!rndis && !info->ether)) { dev_dbg (&intf->dev, "missing cdc %s%s%sdescriptor\n", info->header ? "" : "header ", info->u ? "" : "union ", info->ether ? "" : "ether "); goto bad_desc; } /* claim data interface and set it up ... with side effects. * network traffic can't flow until an altsetting is enabled. */ status = usb_driver_claim_interface (&usbnet_driver, info->data, dev); if (status < 0) return status; status = get_endpoints (dev, info->data); if (status < 0) { /* ensure immediate exit from usbnet_disconnect */ usb_set_intfdata(info->data, NULL); usb_driver_release_interface (&usbnet_driver, info->data); return status; } return 0;bad_desc: dev_info (&dev->udev->dev, "bad CDC descriptors\n"); return -ENODEV;}static void cdc_unbind (struct usbnet *dev, struct usb_interface *intf){ struct cdc_state *info = (void *) &dev->data; /* disconnect master --> disconnect slave */ if (intf == info->control && info->data) { /* ensure immediate exit from usbnet_disconnect */ usb_set_intfdata(info->data, NULL); usb_driver_release_interface (&usbnet_driver, info->data); info->data = NULL; } /* and vice versa (just in case) */ else if (intf == info->data && info->control) { /* ensure immediate exit from usbnet_disconnect */ usb_set_intfdata(info->control, NULL); usb_driver_release_interface (&usbnet_driver, info->control); info->control = NULL; }}#endif /* NEED_GENERIC_CDC */#ifdef CONFIG_USB_CDCETHER#define HAVE_HARDWARE/*------------------------------------------------------------------------- * * Communications Device Class, Ethernet Control model * * Takes two interfaces. The DATA interface is inactive till an altsetting * is selected. Configuration data includes class descriptors. * * This should interop with whatever the 2.4 "CDCEther.c" driver * (by Brad Hards) talked with. * *-------------------------------------------------------------------------*/#include <linux/ctype.h>static u8 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 inline intget_ethernet_addr (struct usbnet *dev, struct ether_desc *e){ int tmp, i; unsigned char buf [13]; tmp = usb_string (dev->udev, e->iMACAddress, buf, sizeof buf); if (tmp != 12) { dev_dbg (&dev->udev->dev, "bad MAC string %d fetch, %d\n", e->iMACAddress, tmp); if (tmp >= 0) tmp = -EINVAL; return tmp; } for (i = tmp = 0; i < 6; i++, tmp += 2) dev->net->dev_addr [i] = (nibble (buf [tmp]) << 4) + nibble (buf [tmp + 1]); return 0;}static int cdc_bind (struct usbnet *dev, struct usb_interface *intf){ int status; struct cdc_state *info = (void *) &dev->data; status = generic_cdc_bind (dev, intf); if (status < 0) return status; status = get_ethernet_addr (dev, info->ether); if (status < 0) { usb_set_intfdata(info->data, NULL); usb_driver_release_interface (&usbnet_driver, info->data); return status; } /* FIXME cdc-ether has some multicast code too, though it complains * in routine cases. info->ether describes the multicast support. */ return 0;}static const struct driver_info cdc_info = { .description = "CDC Ethernet Device", .flags = FLAG_ETHER, // .check_connect = cdc_check_connect, .bind = cdc_bind, .unbind = cdc_unbind,};#endif /* CONFIG_USB_CDCETHER */#ifdef CONFIG_USB_EPSON2888#define HAVE_HARDWARE/*------------------------------------------------------------------------- * * EPSON USB clients * * This is the same idea as Linux PDAs (below) except the firmware in the * device might not be Tux-powered. Epson provides reference firmware that * implements this interface. Product developers can reuse or modify that * code, such as by using their own product and vendor codes. * * Support was from Juro Bystricky <bystricky.juro@erd.epson.com> * *-------------------------------------------------------------------------*/static const struct driver_info epson2888_info = { .description = "Epson USB Device", .check_connect = always_connected, .in = 4, .out = 3,};#endif /* CONFIG_USB_EPSON2888 */#ifdef CONFIG_USB_GENESYS#define HAVE_HARDWARE/*------------------------------------------------------------------------- * * GeneSys GL620USB-A (www.genesyslogic.com.tw) * * ... should partially interop with the Win32 driver for this hardware * The GeneSys docs imply there's some NDIS issue motivating this framing. * * Some info from GeneSys: * - GL620USB-A is full duplex; GL620USB is only half duplex for bulk. * (Some cables, like the BAFO-100c, use the half duplex version.) * - For the full duplex model, the low bit of the version code says * which side is which ("left/right"). * - For the half duplex type, a control/interrupt handshake settles * the transfer direction. (That's disabled here, partially coded.) * A control URB would block until other side writes an interrupt. * * Original code from Jiun-Jie Huang <huangjj@genesyslogic.com.tw> * and merged into "usbnet" by Stanislav Brabec <utx@penguin.cz>. * *-------------------------------------------------------------------------*/// control msg write command#define GENELINK_CONNECT_WRITE 0xF0// interrupt pipe index#define GENELINK_INTERRUPT_PIPE 0x03// interrupt read buffer size#define INTERRUPT_BUFSIZE 0x08// interrupt pipe interval value#define GENELINK_INTERRUPT_INTERVAL 0x10// max transmit packet number per transmit#define GL_MAX_TRANSMIT_PACKETS 32// max packet length#define GL_MAX_PACKET_LEN 1514// max receive buffer size #define GL_RCV_BUF_SIZE \ (((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4)struct gl_packet { u32 packet_length; char packet_data [1];};struct gl_header { u32 packet_count; struct gl_packet packets;};#ifdef GENELINK_ACK// FIXME: this code is incomplete, not debugged; it doesn't// handle interrupts correctly. interrupts should be generic// code like all other device I/O, anyway.struct gl_priv { struct urb *irq_urb; char irq_buf [INTERRUPT_BUFSIZE];};static inline int gl_control_write (struct usbnet *dev, u8 request, u16 value){ int retval; retval = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0), request, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, value, 0, // index 0, // data buffer 0, // size CONTROL_TIMEOUT_JIFFIES); return retval;}static void gl_interrupt_complete (struct urb *urb, struct pt_regs *regs){ int status = urb->status; switch (status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", __FUNCTION__, status); return; default: dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); } status = usb_submit_urb (urb, GFP_ATOMIC); if (status) err ("%s - usb_submit_urb failed with result %d", __FUNCTION__, status);}static int gl_interrupt_read (struct usbnet *dev){ struct gl_priv *priv = dev->priv_data; int retval; // issue usb interrupt read if (priv && priv->irq_urb) { // submit urb if ((retval = usb_submit_urb (priv->irq_urb, GFP_KERNEL)) != 0) dbg ("gl_interrupt_read: submit fail - %X...", retval); else dbg ("gl_interrupt_read: submit success..."); } return 0;}// check whether another side is connectedstatic int genelink_check_connect (struct usbnet *dev){ int retval; dbg ("genelink_check_connect..."); // detect whether another side is connected if ((retval = gl_control_write (dev, GENELINK_CONNECT_WRITE, 0)) != 0) { dbg ("%s: genelink_check_connect write fail - %X", dev->net->name, retval); return retval; } // usb interrupt read to ack another side if ((retval = gl_interrupt_read (dev)) != 0) { dbg ("%s: genelink_check_connect read fail - %X", dev->net->name, retval); return retval; } dbg ("%s: genelink_check_connect read success", dev->net->name); return 0;}// allocate and initialize the private data for genelinkstatic int genelink_init (struct usbnet *dev){ struct gl_priv *priv; // allocate the private data structure if ((priv = kmalloc (sizeof *priv, GFP_KERNEL)) == 0) { dbg ("%s: cannot allocate private data per device", dev->net->name); return -ENOMEM; } // allocate irq urb if ((priv->irq_urb = usb_alloc_urb (0, GFP_KERNEL)) == 0) { dbg ("%s: cannot allocate private irq urb per device", dev->net->name); kfree (priv); return -ENOMEM; } // fill irq urb usb_fill_int_urb (priv->irq_urb, dev->udev, usb_rcvintpipe (dev->udev, GENELINK_INTERRUPT_PIPE), priv->irq_buf, INTERRUPT_BUFSIZE, gl_interrupt_complete, 0, GENELINK_INTERRUPT_INTERVAL); // set private data pointer dev->priv_data = priv;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -