162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Host Side support for RNDIS Networking Links 462306a36Sopenharmony_ci * Copyright (C) 2005 by David Brownell 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/netdevice.h> 862306a36Sopenharmony_ci#include <linux/etherdevice.h> 962306a36Sopenharmony_ci#include <linux/ethtool.h> 1062306a36Sopenharmony_ci#include <linux/workqueue.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/mii.h> 1362306a36Sopenharmony_ci#include <linux/usb.h> 1462306a36Sopenharmony_ci#include <linux/usb/cdc.h> 1562306a36Sopenharmony_ci#include <linux/usb/usbnet.h> 1662306a36Sopenharmony_ci#include <linux/usb/rndis_host.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * RNDIS is NDIS remoted over USB. It's a MSFT variant of CDC ACM ... of 2162306a36Sopenharmony_ci * course ACM was intended for modems, not Ethernet links! USB's standard 2262306a36Sopenharmony_ci * for Ethernet links is "CDC Ethernet", which is significantly simpler. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * NOTE that Microsoft's "RNDIS 1.0" specification is incomplete. Issues 2562306a36Sopenharmony_ci * include: 2662306a36Sopenharmony_ci * - Power management in particular relies on information that's scattered 2762306a36Sopenharmony_ci * through other documentation, and which is incomplete or incorrect even 2862306a36Sopenharmony_ci * there. 2962306a36Sopenharmony_ci * - There are various undocumented protocol requirements, such as the 3062306a36Sopenharmony_ci * need to send unused garbage in control-OUT messages. 3162306a36Sopenharmony_ci * - In some cases, MS-Windows will emit undocumented requests; this 3262306a36Sopenharmony_ci * matters more to peripheral implementations than host ones. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Moreover there's a no-open-specs variant of RNDIS called "ActiveSync". 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in 3762306a36Sopenharmony_ci * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and 3862306a36Sopenharmony_ci * currently rare) "Ethernet Emulation Model" (EEM). 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * RNDIS notifications from device: command completion; "reverse" 4362306a36Sopenharmony_ci * keepalives; etc 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_civoid rndis_status(struct usbnet *dev, struct urb *urb) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci netdev_dbg(dev->net, "rndis status urb, len %d stat %d\n", 4862306a36Sopenharmony_ci urb->actual_length, urb->status); 4962306a36Sopenharmony_ci // FIXME for keepalives, respond immediately (asynchronously) 5062306a36Sopenharmony_ci // if not an RNDIS status, do like cdc_status(dev,urb) does 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_status); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * RNDIS indicate messages. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cistatic void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg, 5862306a36Sopenharmony_ci int buflen) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct cdc_state *info = (void *)&dev->data; 6162306a36Sopenharmony_ci struct device *udev = &info->control->dev; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (dev->driver_info->indication) { 6462306a36Sopenharmony_ci dev->driver_info->indication(dev, msg, buflen); 6562306a36Sopenharmony_ci } else { 6662306a36Sopenharmony_ci u32 status = le32_to_cpu(msg->status); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci switch (status) { 6962306a36Sopenharmony_ci case RNDIS_STATUS_MEDIA_CONNECT: 7062306a36Sopenharmony_ci dev_info(udev, "rndis media connect\n"); 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci case RNDIS_STATUS_MEDIA_DISCONNECT: 7362306a36Sopenharmony_ci dev_info(udev, "rndis media disconnect\n"); 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci default: 7662306a36Sopenharmony_ci dev_info(udev, "rndis indication: 0x%08x\n", status); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * RPC done RNDIS-style. Caller guarantees: 8362306a36Sopenharmony_ci * - message is properly byteswapped 8462306a36Sopenharmony_ci * - there's no other request pending 8562306a36Sopenharmony_ci * - buf can hold up to 1KB response (required by RNDIS spec) 8662306a36Sopenharmony_ci * On return, the first few entries are already byteswapped. 8762306a36Sopenharmony_ci * 8862306a36Sopenharmony_ci * Call context is likely probe(), before interface name is known, 8962306a36Sopenharmony_ci * which is why we won't try to use it in the diagnostics. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ciint rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct cdc_state *info = (void *) &dev->data; 9462306a36Sopenharmony_ci struct usb_cdc_notification notification; 9562306a36Sopenharmony_ci int master_ifnum; 9662306a36Sopenharmony_ci int retval; 9762306a36Sopenharmony_ci int partial; 9862306a36Sopenharmony_ci unsigned count; 9962306a36Sopenharmony_ci u32 xid = 0, msg_len, request_id, msg_type, rsp, 10062306a36Sopenharmony_ci status; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* REVISIT when this gets called from contexts other than probe() or 10362306a36Sopenharmony_ci * disconnect(): either serialize, or dispatch responses on xid 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci msg_type = le32_to_cpu(buf->msg_type); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Issue the request; xid is unique, don't bother byteswapping it */ 10962306a36Sopenharmony_ci if (likely(msg_type != RNDIS_MSG_HALT && msg_type != RNDIS_MSG_RESET)) { 11062306a36Sopenharmony_ci xid = dev->xid++; 11162306a36Sopenharmony_ci if (!xid) 11262306a36Sopenharmony_ci xid = dev->xid++; 11362306a36Sopenharmony_ci buf->request_id = (__force __le32) xid; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci master_ifnum = info->control->cur_altsetting->desc.bInterfaceNumber; 11662306a36Sopenharmony_ci retval = usb_control_msg(dev->udev, 11762306a36Sopenharmony_ci usb_sndctrlpipe(dev->udev, 0), 11862306a36Sopenharmony_ci USB_CDC_SEND_ENCAPSULATED_COMMAND, 11962306a36Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE, 12062306a36Sopenharmony_ci 0, master_ifnum, 12162306a36Sopenharmony_ci buf, le32_to_cpu(buf->msg_len), 12262306a36Sopenharmony_ci RNDIS_CONTROL_TIMEOUT_MS); 12362306a36Sopenharmony_ci if (unlikely(retval < 0 || xid == 0)) 12462306a36Sopenharmony_ci return retval; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Some devices don't respond on the control channel until 12762306a36Sopenharmony_ci * polled on the status channel, so do that first. */ 12862306a36Sopenharmony_ci if (dev->driver_info->data & RNDIS_DRIVER_DATA_POLL_STATUS) { 12962306a36Sopenharmony_ci retval = usb_interrupt_msg( 13062306a36Sopenharmony_ci dev->udev, 13162306a36Sopenharmony_ci usb_rcvintpipe(dev->udev, 13262306a36Sopenharmony_ci dev->status->desc.bEndpointAddress), 13362306a36Sopenharmony_ci ¬ification, sizeof(notification), &partial, 13462306a36Sopenharmony_ci RNDIS_CONTROL_TIMEOUT_MS); 13562306a36Sopenharmony_ci if (unlikely(retval < 0)) 13662306a36Sopenharmony_ci return retval; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Poll the control channel; the request probably completed immediately */ 14062306a36Sopenharmony_ci rsp = le32_to_cpu(buf->msg_type) | RNDIS_MSG_COMPLETION; 14162306a36Sopenharmony_ci for (count = 0; count < 10; count++) { 14262306a36Sopenharmony_ci memset(buf, 0, CONTROL_BUFFER_SIZE); 14362306a36Sopenharmony_ci retval = usb_control_msg(dev->udev, 14462306a36Sopenharmony_ci usb_rcvctrlpipe(dev->udev, 0), 14562306a36Sopenharmony_ci USB_CDC_GET_ENCAPSULATED_RESPONSE, 14662306a36Sopenharmony_ci USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 14762306a36Sopenharmony_ci 0, master_ifnum, 14862306a36Sopenharmony_ci buf, buflen, 14962306a36Sopenharmony_ci RNDIS_CONTROL_TIMEOUT_MS); 15062306a36Sopenharmony_ci if (likely(retval >= 8)) { 15162306a36Sopenharmony_ci msg_type = le32_to_cpu(buf->msg_type); 15262306a36Sopenharmony_ci msg_len = le32_to_cpu(buf->msg_len); 15362306a36Sopenharmony_ci status = le32_to_cpu(buf->status); 15462306a36Sopenharmony_ci request_id = (__force u32) buf->request_id; 15562306a36Sopenharmony_ci if (likely(msg_type == rsp)) { 15662306a36Sopenharmony_ci if (likely(request_id == xid)) { 15762306a36Sopenharmony_ci if (unlikely(rsp == RNDIS_MSG_RESET_C)) 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci if (likely(RNDIS_STATUS_SUCCESS == 16062306a36Sopenharmony_ci status)) 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci dev_dbg(&info->control->dev, 16362306a36Sopenharmony_ci "rndis reply status %08x\n", 16462306a36Sopenharmony_ci status); 16562306a36Sopenharmony_ci return -EL3RST; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci dev_dbg(&info->control->dev, 16862306a36Sopenharmony_ci "rndis reply id %d expected %d\n", 16962306a36Sopenharmony_ci request_id, xid); 17062306a36Sopenharmony_ci /* then likely retry */ 17162306a36Sopenharmony_ci } else switch (msg_type) { 17262306a36Sopenharmony_ci case RNDIS_MSG_INDICATE: /* fault/event */ 17362306a36Sopenharmony_ci rndis_msg_indicate(dev, (void *)buf, buflen); 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci case RNDIS_MSG_KEEPALIVE: { /* ping */ 17662306a36Sopenharmony_ci struct rndis_keepalive_c *msg = (void *)buf; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci msg->msg_type = cpu_to_le32(RNDIS_MSG_KEEPALIVE_C); 17962306a36Sopenharmony_ci msg->msg_len = cpu_to_le32(sizeof *msg); 18062306a36Sopenharmony_ci msg->status = cpu_to_le32(RNDIS_STATUS_SUCCESS); 18162306a36Sopenharmony_ci retval = usb_control_msg(dev->udev, 18262306a36Sopenharmony_ci usb_sndctrlpipe(dev->udev, 0), 18362306a36Sopenharmony_ci USB_CDC_SEND_ENCAPSULATED_COMMAND, 18462306a36Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE, 18562306a36Sopenharmony_ci 0, master_ifnum, 18662306a36Sopenharmony_ci msg, sizeof *msg, 18762306a36Sopenharmony_ci RNDIS_CONTROL_TIMEOUT_MS); 18862306a36Sopenharmony_ci if (unlikely(retval < 0)) 18962306a36Sopenharmony_ci dev_dbg(&info->control->dev, 19062306a36Sopenharmony_ci "rndis keepalive err %d\n", 19162306a36Sopenharmony_ci retval); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci default: 19562306a36Sopenharmony_ci dev_dbg(&info->control->dev, 19662306a36Sopenharmony_ci "unexpected rndis msg %08x len %d\n", 19762306a36Sopenharmony_ci le32_to_cpu(buf->msg_type), msg_len); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci /* device probably issued a protocol stall; ignore */ 20162306a36Sopenharmony_ci dev_dbg(&info->control->dev, 20262306a36Sopenharmony_ci "rndis response error, code %d\n", retval); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci msleep(40); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci dev_dbg(&info->control->dev, "rndis response timeout\n"); 20762306a36Sopenharmony_ci return -ETIMEDOUT; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_command); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci * rndis_query: 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * Performs a query for @oid along with 0 or more bytes of payload as 21562306a36Sopenharmony_ci * specified by @in_len. If @reply_len is not set to -1 then the reply 21662306a36Sopenharmony_ci * length is checked against this value, resulting in an error if it 21762306a36Sopenharmony_ci * doesn't match. 21862306a36Sopenharmony_ci * 21962306a36Sopenharmony_ci * NOTE: Adding a payload exactly or greater than the size of the expected 22062306a36Sopenharmony_ci * response payload is an evident requirement MSFT added for ActiveSync. 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * The only exception is for OIDs that return a variably sized response, 22362306a36Sopenharmony_ci * in which case no payload should be added. This undocumented (and 22462306a36Sopenharmony_ci * nonsensical!) issue was found by sniffing protocol requests from the 22562306a36Sopenharmony_ci * ActiveSync 4.1 Windows driver. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_cistatic int rndis_query(struct usbnet *dev, struct usb_interface *intf, 22862306a36Sopenharmony_ci void *buf, u32 oid, u32 in_len, 22962306a36Sopenharmony_ci void **reply, int *reply_len) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int retval; 23262306a36Sopenharmony_ci union { 23362306a36Sopenharmony_ci void *buf; 23462306a36Sopenharmony_ci struct rndis_msg_hdr *header; 23562306a36Sopenharmony_ci struct rndis_query *get; 23662306a36Sopenharmony_ci struct rndis_query_c *get_c; 23762306a36Sopenharmony_ci } u; 23862306a36Sopenharmony_ci u32 off, len; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci u.buf = buf; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci memset(u.get, 0, sizeof *u.get + in_len); 24362306a36Sopenharmony_ci u.get->msg_type = cpu_to_le32(RNDIS_MSG_QUERY); 24462306a36Sopenharmony_ci u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len); 24562306a36Sopenharmony_ci u.get->oid = cpu_to_le32(oid); 24662306a36Sopenharmony_ci u.get->len = cpu_to_le32(in_len); 24762306a36Sopenharmony_ci u.get->offset = cpu_to_le32(20); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); 25062306a36Sopenharmony_ci if (unlikely(retval < 0)) { 25162306a36Sopenharmony_ci dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n", 25262306a36Sopenharmony_ci oid, retval); 25362306a36Sopenharmony_ci return retval; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci off = le32_to_cpu(u.get_c->offset); 25762306a36Sopenharmony_ci len = le32_to_cpu(u.get_c->len); 25862306a36Sopenharmony_ci if (unlikely((off > CONTROL_BUFFER_SIZE - 8) || 25962306a36Sopenharmony_ci (len > CONTROL_BUFFER_SIZE - 8 - off))) 26062306a36Sopenharmony_ci goto response_error; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (*reply_len != -1 && len != *reply_len) 26362306a36Sopenharmony_ci goto response_error; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci *reply = (unsigned char *) &u.get_c->request_id + off; 26662306a36Sopenharmony_ci *reply_len = len; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return retval; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ciresponse_error: 27162306a36Sopenharmony_ci dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) " 27262306a36Sopenharmony_ci "invalid response - off %d len %d\n", 27362306a36Sopenharmony_ci oid, off, len); 27462306a36Sopenharmony_ci return -EDOM; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* same as usbnet_netdev_ops but MTU change not allowed */ 27862306a36Sopenharmony_cistatic const struct net_device_ops rndis_netdev_ops = { 27962306a36Sopenharmony_ci .ndo_open = usbnet_open, 28062306a36Sopenharmony_ci .ndo_stop = usbnet_stop, 28162306a36Sopenharmony_ci .ndo_start_xmit = usbnet_start_xmit, 28262306a36Sopenharmony_ci .ndo_tx_timeout = usbnet_tx_timeout, 28362306a36Sopenharmony_ci .ndo_get_stats64 = dev_get_tstats64, 28462306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 28562306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciint 28962306a36Sopenharmony_cigeneric_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci int retval; 29262306a36Sopenharmony_ci struct net_device *net = dev->net; 29362306a36Sopenharmony_ci struct cdc_state *info = (void *) &dev->data; 29462306a36Sopenharmony_ci union { 29562306a36Sopenharmony_ci void *buf; 29662306a36Sopenharmony_ci struct rndis_msg_hdr *header; 29762306a36Sopenharmony_ci struct rndis_init *init; 29862306a36Sopenharmony_ci struct rndis_init_c *init_c; 29962306a36Sopenharmony_ci struct rndis_query *get; 30062306a36Sopenharmony_ci struct rndis_query_c *get_c; 30162306a36Sopenharmony_ci struct rndis_set *set; 30262306a36Sopenharmony_ci struct rndis_set_c *set_c; 30362306a36Sopenharmony_ci struct rndis_halt *halt; 30462306a36Sopenharmony_ci } u; 30562306a36Sopenharmony_ci u32 tmp; 30662306a36Sopenharmony_ci __le32 phym_unspec, *phym; 30762306a36Sopenharmony_ci int reply_len; 30862306a36Sopenharmony_ci unsigned char *bp; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* we can't rely on i/o from stack working, or stack allocation */ 31162306a36Sopenharmony_ci u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); 31262306a36Sopenharmony_ci if (!u.buf) 31362306a36Sopenharmony_ci return -ENOMEM; 31462306a36Sopenharmony_ci retval = usbnet_generic_cdc_bind(dev, intf); 31562306a36Sopenharmony_ci if (retval < 0) 31662306a36Sopenharmony_ci goto fail; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci u.init->msg_type = cpu_to_le32(RNDIS_MSG_INIT); 31962306a36Sopenharmony_ci u.init->msg_len = cpu_to_le32(sizeof *u.init); 32062306a36Sopenharmony_ci u.init->major_version = cpu_to_le32(1); 32162306a36Sopenharmony_ci u.init->minor_version = cpu_to_le32(0); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* max transfer (in spec) is 0x4000 at full speed, but for 32462306a36Sopenharmony_ci * TX we'll stick to one Ethernet packet plus RNDIS framing. 32562306a36Sopenharmony_ci * For RX we handle drivers that zero-pad to end-of-packet. 32662306a36Sopenharmony_ci * Don't let userspace change these settings. 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * NOTE: there still seems to be weirdness here, as if we need 32962306a36Sopenharmony_ci * to do some more things to make sure WinCE targets accept this. 33062306a36Sopenharmony_ci * They default to jumbograms of 8KB or 16KB, which is absurd 33162306a36Sopenharmony_ci * for such low data rates and which is also more than Linux 33262306a36Sopenharmony_ci * can usually expect to allocate for SKB data... 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci net->hard_header_len += sizeof (struct rndis_data_hdr); 33562306a36Sopenharmony_ci dev->hard_mtu = net->mtu + net->hard_header_len; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci dev->maxpacket = usb_maxpacket(dev->udev, dev->out); 33862306a36Sopenharmony_ci if (dev->maxpacket == 0) { 33962306a36Sopenharmony_ci netif_dbg(dev, probe, dev->net, 34062306a36Sopenharmony_ci "dev->maxpacket can't be 0\n"); 34162306a36Sopenharmony_ci retval = -EINVAL; 34262306a36Sopenharmony_ci goto fail_and_release; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1); 34662306a36Sopenharmony_ci dev->rx_urb_size &= ~(dev->maxpacket - 1); 34762306a36Sopenharmony_ci u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci net->netdev_ops = &rndis_netdev_ops; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); 35262306a36Sopenharmony_ci if (unlikely(retval < 0)) { 35362306a36Sopenharmony_ci /* it might not even be an RNDIS device!! */ 35462306a36Sopenharmony_ci dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); 35562306a36Sopenharmony_ci goto fail_and_release; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci tmp = le32_to_cpu(u.init_c->max_transfer_size); 35862306a36Sopenharmony_ci if (tmp < dev->hard_mtu) { 35962306a36Sopenharmony_ci if (tmp <= net->hard_header_len) { 36062306a36Sopenharmony_ci dev_err(&intf->dev, 36162306a36Sopenharmony_ci "dev can't take %u byte packets (max %u)\n", 36262306a36Sopenharmony_ci dev->hard_mtu, tmp); 36362306a36Sopenharmony_ci retval = -EINVAL; 36462306a36Sopenharmony_ci goto halt_fail_and_release; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci dev_warn(&intf->dev, 36762306a36Sopenharmony_ci "dev can't take %u byte packets (max %u), " 36862306a36Sopenharmony_ci "adjusting MTU to %u\n", 36962306a36Sopenharmony_ci dev->hard_mtu, tmp, tmp - net->hard_header_len); 37062306a36Sopenharmony_ci dev->hard_mtu = tmp; 37162306a36Sopenharmony_ci net->mtu = dev->hard_mtu - net->hard_header_len; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* REVISIT: peripheral "alignment" request is ignored ... */ 37562306a36Sopenharmony_ci dev_dbg(&intf->dev, 37662306a36Sopenharmony_ci "hard mtu %u (%u from dev), rx buflen %zu, align %d\n", 37762306a36Sopenharmony_ci dev->hard_mtu, tmp, dev->rx_urb_size, 37862306a36Sopenharmony_ci 1 << le32_to_cpu(u.init_c->packet_alignment)); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* module has some device initialization code needs to be done right 38162306a36Sopenharmony_ci * after RNDIS_INIT */ 38262306a36Sopenharmony_ci if (dev->driver_info->early_init && 38362306a36Sopenharmony_ci dev->driver_info->early_init(dev) != 0) 38462306a36Sopenharmony_ci goto halt_fail_and_release; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Check physical medium */ 38762306a36Sopenharmony_ci phym = NULL; 38862306a36Sopenharmony_ci reply_len = sizeof *phym; 38962306a36Sopenharmony_ci retval = rndis_query(dev, intf, u.buf, 39062306a36Sopenharmony_ci RNDIS_OID_GEN_PHYSICAL_MEDIUM, 39162306a36Sopenharmony_ci reply_len, (void **)&phym, &reply_len); 39262306a36Sopenharmony_ci if (retval != 0 || !phym) { 39362306a36Sopenharmony_ci /* OID is optional so don't fail here. */ 39462306a36Sopenharmony_ci phym_unspec = cpu_to_le32(RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED); 39562306a36Sopenharmony_ci phym = &phym_unspec; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci if ((flags & FLAG_RNDIS_PHYM_WIRELESS) && 39862306a36Sopenharmony_ci le32_to_cpup(phym) != RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) { 39962306a36Sopenharmony_ci netif_dbg(dev, probe, dev->net, 40062306a36Sopenharmony_ci "driver requires wireless physical medium, but device is not\n"); 40162306a36Sopenharmony_ci retval = -ENODEV; 40262306a36Sopenharmony_ci goto halt_fail_and_release; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci if ((flags & FLAG_RNDIS_PHYM_NOT_WIRELESS) && 40562306a36Sopenharmony_ci le32_to_cpup(phym) == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) { 40662306a36Sopenharmony_ci netif_dbg(dev, probe, dev->net, 40762306a36Sopenharmony_ci "driver requires non-wireless physical medium, but device is wireless.\n"); 40862306a36Sopenharmony_ci retval = -ENODEV; 40962306a36Sopenharmony_ci goto halt_fail_and_release; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Get designated host ethernet address */ 41362306a36Sopenharmony_ci reply_len = ETH_ALEN; 41462306a36Sopenharmony_ci retval = rndis_query(dev, intf, u.buf, 41562306a36Sopenharmony_ci RNDIS_OID_802_3_PERMANENT_ADDRESS, 41662306a36Sopenharmony_ci 48, (void **) &bp, &reply_len); 41762306a36Sopenharmony_ci if (unlikely(retval< 0)) { 41862306a36Sopenharmony_ci dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); 41962306a36Sopenharmony_ci goto halt_fail_and_release; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci eth_hw_addr_set(net, bp); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* set a nonzero filter to enable data transfers */ 42562306a36Sopenharmony_ci memset(u.set, 0, sizeof *u.set); 42662306a36Sopenharmony_ci u.set->msg_type = cpu_to_le32(RNDIS_MSG_SET); 42762306a36Sopenharmony_ci u.set->msg_len = cpu_to_le32(4 + sizeof *u.set); 42862306a36Sopenharmony_ci u.set->oid = cpu_to_le32(RNDIS_OID_GEN_CURRENT_PACKET_FILTER); 42962306a36Sopenharmony_ci u.set->len = cpu_to_le32(4); 43062306a36Sopenharmony_ci u.set->offset = cpu_to_le32((sizeof *u.set) - 8); 43162306a36Sopenharmony_ci *(__le32 *)(u.buf + sizeof *u.set) = cpu_to_le32(RNDIS_DEFAULT_FILTER); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); 43462306a36Sopenharmony_ci if (unlikely(retval < 0)) { 43562306a36Sopenharmony_ci dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); 43662306a36Sopenharmony_ci goto halt_fail_and_release; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci retval = 0; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci kfree(u.buf); 44262306a36Sopenharmony_ci return retval; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cihalt_fail_and_release: 44562306a36Sopenharmony_ci memset(u.halt, 0, sizeof *u.halt); 44662306a36Sopenharmony_ci u.halt->msg_type = cpu_to_le32(RNDIS_MSG_HALT); 44762306a36Sopenharmony_ci u.halt->msg_len = cpu_to_le32(sizeof *u.halt); 44862306a36Sopenharmony_ci (void) rndis_command(dev, (void *)u.halt, CONTROL_BUFFER_SIZE); 44962306a36Sopenharmony_cifail_and_release: 45062306a36Sopenharmony_ci usb_set_intfdata(info->data, NULL); 45162306a36Sopenharmony_ci usb_driver_release_interface(driver_of(intf), info->data); 45262306a36Sopenharmony_ci info->data = NULL; 45362306a36Sopenharmony_cifail: 45462306a36Sopenharmony_ci kfree(u.buf); 45562306a36Sopenharmony_ci return retval; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(generic_rndis_bind); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int rndis_bind(struct usbnet *dev, struct usb_interface *intf) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci return generic_rndis_bind(dev, intf, FLAG_RNDIS_PHYM_NOT_WIRELESS); 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int zte_rndis_bind(struct usbnet *dev, struct usb_interface *intf) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci int status = rndis_bind(dev, intf); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (!status && (dev->net->dev_addr[0] & 0x02)) 46962306a36Sopenharmony_ci eth_hw_addr_random(dev->net); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return status; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_civoid rndis_unbind(struct usbnet *dev, struct usb_interface *intf) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct rndis_halt *halt; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* try to clear any rndis state/activity (no i/o from stack!) */ 47962306a36Sopenharmony_ci halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); 48062306a36Sopenharmony_ci if (halt) { 48162306a36Sopenharmony_ci halt->msg_type = cpu_to_le32(RNDIS_MSG_HALT); 48262306a36Sopenharmony_ci halt->msg_len = cpu_to_le32(sizeof *halt); 48362306a36Sopenharmony_ci (void) rndis_command(dev, (void *)halt, CONTROL_BUFFER_SIZE); 48462306a36Sopenharmony_ci kfree(halt); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci usbnet_cdc_unbind(dev, intf); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_unbind); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/* 49262306a36Sopenharmony_ci * DATA -- host must not write zlps 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ciint rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci bool dst_mac_fixup; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* This check is no longer done by usbnet */ 49962306a36Sopenharmony_ci if (skb->len < dev->net->hard_header_len) 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci dst_mac_fixup = !!(dev->driver_info->data & RNDIS_DRIVER_DATA_DST_MAC_FIXUP); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* peripheral may have batched packets to us... */ 50562306a36Sopenharmony_ci while (likely(skb->len)) { 50662306a36Sopenharmony_ci struct rndis_data_hdr *hdr = (void *)skb->data; 50762306a36Sopenharmony_ci struct sk_buff *skb2; 50862306a36Sopenharmony_ci u32 msg_type, msg_len, data_offset, data_len; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci msg_type = le32_to_cpu(hdr->msg_type); 51162306a36Sopenharmony_ci msg_len = le32_to_cpu(hdr->msg_len); 51262306a36Sopenharmony_ci data_offset = le32_to_cpu(hdr->data_offset); 51362306a36Sopenharmony_ci data_len = le32_to_cpu(hdr->data_len); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* don't choke if we see oob, per-packet data, etc */ 51662306a36Sopenharmony_ci if (unlikely(msg_type != RNDIS_MSG_PACKET || skb->len < msg_len 51762306a36Sopenharmony_ci || (data_offset + data_len + 8) > msg_len)) { 51862306a36Sopenharmony_ci dev->net->stats.rx_frame_errors++; 51962306a36Sopenharmony_ci netdev_dbg(dev->net, "bad rndis message %d/%d/%d/%d, len %d\n", 52062306a36Sopenharmony_ci le32_to_cpu(hdr->msg_type), 52162306a36Sopenharmony_ci msg_len, data_offset, data_len, skb->len); 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci skb_pull(skb, 8 + data_offset); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* at most one packet left? */ 52762306a36Sopenharmony_ci if (likely((data_len - skb->len) <= sizeof *hdr)) { 52862306a36Sopenharmony_ci skb_trim(skb, data_len); 52962306a36Sopenharmony_ci break; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* try to return all the packets in the batch */ 53362306a36Sopenharmony_ci skb2 = skb_clone(skb, GFP_ATOMIC); 53462306a36Sopenharmony_ci if (unlikely(!skb2)) 53562306a36Sopenharmony_ci break; 53662306a36Sopenharmony_ci skb_pull(skb, msg_len - sizeof *hdr); 53762306a36Sopenharmony_ci skb_trim(skb2, data_len); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (unlikely(dst_mac_fixup)) 54062306a36Sopenharmony_ci usbnet_cdc_zte_rx_fixup(dev, skb2); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci usbnet_skb_return(dev, skb2); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* caller will usbnet_skb_return the remaining packet */ 54662306a36Sopenharmony_ci if (unlikely(dst_mac_fixup)) 54762306a36Sopenharmony_ci usbnet_cdc_zte_rx_fixup(dev, skb); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return 1; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_rx_fixup); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistruct sk_buff * 55462306a36Sopenharmony_cirndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct rndis_data_hdr *hdr; 55762306a36Sopenharmony_ci struct sk_buff *skb2; 55862306a36Sopenharmony_ci unsigned len = skb->len; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (likely(!skb_cloned(skb))) { 56162306a36Sopenharmony_ci int room = skb_headroom(skb); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* enough head room as-is? */ 56462306a36Sopenharmony_ci if (unlikely((sizeof *hdr) <= room)) 56562306a36Sopenharmony_ci goto fill; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* enough room, but needs to be readjusted? */ 56862306a36Sopenharmony_ci room += skb_tailroom(skb); 56962306a36Sopenharmony_ci if (likely((sizeof *hdr) <= room)) { 57062306a36Sopenharmony_ci skb->data = memmove(skb->head + sizeof *hdr, 57162306a36Sopenharmony_ci skb->data, len); 57262306a36Sopenharmony_ci skb_set_tail_pointer(skb, len); 57362306a36Sopenharmony_ci goto fill; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* create a new skb, with the correct size (and tailpad) */ 57862306a36Sopenharmony_ci skb2 = skb_copy_expand(skb, sizeof *hdr, 1, flags); 57962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 58062306a36Sopenharmony_ci if (unlikely(!skb2)) 58162306a36Sopenharmony_ci return skb2; 58262306a36Sopenharmony_ci skb = skb2; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* fill out the RNDIS header. we won't bother trying to batch 58562306a36Sopenharmony_ci * packets; Linux minimizes wasted bandwidth through tx queues. 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_cifill: 58862306a36Sopenharmony_ci hdr = __skb_push(skb, sizeof *hdr); 58962306a36Sopenharmony_ci memset(hdr, 0, sizeof *hdr); 59062306a36Sopenharmony_ci hdr->msg_type = cpu_to_le32(RNDIS_MSG_PACKET); 59162306a36Sopenharmony_ci hdr->msg_len = cpu_to_le32(skb->len); 59262306a36Sopenharmony_ci hdr->data_offset = cpu_to_le32(sizeof(*hdr) - 8); 59362306a36Sopenharmony_ci hdr->data_len = cpu_to_le32(len); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* FIXME make the last packet always be short ... */ 59662306a36Sopenharmony_ci return skb; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_tx_fixup); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic const struct driver_info rndis_info = { 60262306a36Sopenharmony_ci .description = "RNDIS device", 60362306a36Sopenharmony_ci .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, 60462306a36Sopenharmony_ci .bind = rndis_bind, 60562306a36Sopenharmony_ci .unbind = rndis_unbind, 60662306a36Sopenharmony_ci .status = rndis_status, 60762306a36Sopenharmony_ci .rx_fixup = rndis_rx_fixup, 60862306a36Sopenharmony_ci .tx_fixup = rndis_tx_fixup, 60962306a36Sopenharmony_ci}; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic const struct driver_info rndis_poll_status_info = { 61262306a36Sopenharmony_ci .description = "RNDIS device (poll status before control)", 61362306a36Sopenharmony_ci .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, 61462306a36Sopenharmony_ci .data = RNDIS_DRIVER_DATA_POLL_STATUS, 61562306a36Sopenharmony_ci .bind = rndis_bind, 61662306a36Sopenharmony_ci .unbind = rndis_unbind, 61762306a36Sopenharmony_ci .status = rndis_status, 61862306a36Sopenharmony_ci .rx_fixup = rndis_rx_fixup, 61962306a36Sopenharmony_ci .tx_fixup = rndis_tx_fixup, 62062306a36Sopenharmony_ci}; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic const struct driver_info zte_rndis_info = { 62362306a36Sopenharmony_ci .description = "ZTE RNDIS device", 62462306a36Sopenharmony_ci .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, 62562306a36Sopenharmony_ci .data = RNDIS_DRIVER_DATA_DST_MAC_FIXUP, 62662306a36Sopenharmony_ci .bind = zte_rndis_bind, 62762306a36Sopenharmony_ci .unbind = rndis_unbind, 62862306a36Sopenharmony_ci .status = rndis_status, 62962306a36Sopenharmony_ci .rx_fixup = rndis_rx_fixup, 63062306a36Sopenharmony_ci .tx_fixup = rndis_tx_fixup, 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic const struct usb_device_id products [] = { 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci /* 2Wire HomePortal 1000SW */ 63862306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1630, 0x0042, 63962306a36Sopenharmony_ci USB_CLASS_COMM, 2 /* ACM */, 0x0ff), 64062306a36Sopenharmony_ci .driver_info = (unsigned long) &rndis_poll_status_info, 64162306a36Sopenharmony_ci}, { 64262306a36Sopenharmony_ci /* Hytera Communications DMR radios' "Radio to PC Network" */ 64362306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(0x238b, 64462306a36Sopenharmony_ci USB_CLASS_COMM, 2 /* ACM */, 0x0ff), 64562306a36Sopenharmony_ci .driver_info = (unsigned long)&rndis_info, 64662306a36Sopenharmony_ci}, { 64762306a36Sopenharmony_ci /* ZTE WWAN modules */ 64862306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(0x19d2, 64962306a36Sopenharmony_ci USB_CLASS_WIRELESS_CONTROLLER, 1, 3), 65062306a36Sopenharmony_ci .driver_info = (unsigned long)&zte_rndis_info, 65162306a36Sopenharmony_ci}, { 65262306a36Sopenharmony_ci /* ZTE WWAN modules, ACM flavour */ 65362306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(0x19d2, 65462306a36Sopenharmony_ci USB_CLASS_COMM, 2 /* ACM */, 0x0ff), 65562306a36Sopenharmony_ci .driver_info = (unsigned long)&zte_rndis_info, 65662306a36Sopenharmony_ci}, { 65762306a36Sopenharmony_ci /* RNDIS is MSFT's un-official variant of CDC ACM */ 65862306a36Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), 65962306a36Sopenharmony_ci .driver_info = (unsigned long) &rndis_info, 66062306a36Sopenharmony_ci}, { 66162306a36Sopenharmony_ci /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ 66262306a36Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), 66362306a36Sopenharmony_ci .driver_info = (unsigned long) &rndis_poll_status_info, 66462306a36Sopenharmony_ci}, { 66562306a36Sopenharmony_ci /* RNDIS for tethering */ 66662306a36Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3), 66762306a36Sopenharmony_ci .driver_info = (unsigned long) &rndis_info, 66862306a36Sopenharmony_ci}, { 66962306a36Sopenharmony_ci /* Novatel Verizon USB730L */ 67062306a36Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_MISC, 4, 1), 67162306a36Sopenharmony_ci .driver_info = (unsigned long) &rndis_info, 67262306a36Sopenharmony_ci}, 67362306a36Sopenharmony_ci { }, // END 67462306a36Sopenharmony_ci}; 67562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic struct usb_driver rndis_driver = { 67862306a36Sopenharmony_ci .name = "rndis_host", 67962306a36Sopenharmony_ci .id_table = products, 68062306a36Sopenharmony_ci .probe = usbnet_probe, 68162306a36Sopenharmony_ci .disconnect = usbnet_disconnect, 68262306a36Sopenharmony_ci .suspend = usbnet_suspend, 68362306a36Sopenharmony_ci .resume = usbnet_resume, 68462306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 68562306a36Sopenharmony_ci}; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cimodule_usb_driver(rndis_driver); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 69062306a36Sopenharmony_ciMODULE_DESCRIPTION("USB Host side RNDIS driver"); 69162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 692