18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Host Side support for RNDIS Networking Links 48c2ecf20Sopenharmony_ci * Copyright (C) 2005 by David Brownell 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 88c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 98c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 108c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/mii.h> 138c2ecf20Sopenharmony_ci#include <linux/usb.h> 148c2ecf20Sopenharmony_ci#include <linux/usb/cdc.h> 158c2ecf20Sopenharmony_ci#include <linux/usb/usbnet.h> 168c2ecf20Sopenharmony_ci#include <linux/usb/rndis_host.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * RNDIS is NDIS remoted over USB. It's a MSFT variant of CDC ACM ... of 218c2ecf20Sopenharmony_ci * course ACM was intended for modems, not Ethernet links! USB's standard 228c2ecf20Sopenharmony_ci * for Ethernet links is "CDC Ethernet", which is significantly simpler. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * NOTE that Microsoft's "RNDIS 1.0" specification is incomplete. Issues 258c2ecf20Sopenharmony_ci * include: 268c2ecf20Sopenharmony_ci * - Power management in particular relies on information that's scattered 278c2ecf20Sopenharmony_ci * through other documentation, and which is incomplete or incorrect even 288c2ecf20Sopenharmony_ci * there. 298c2ecf20Sopenharmony_ci * - There are various undocumented protocol requirements, such as the 308c2ecf20Sopenharmony_ci * need to send unused garbage in control-OUT messages. 318c2ecf20Sopenharmony_ci * - In some cases, MS-Windows will emit undocumented requests; this 328c2ecf20Sopenharmony_ci * matters more to peripheral implementations than host ones. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * Moreover there's a no-open-specs variant of RNDIS called "ActiveSync". 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in 378c2ecf20Sopenharmony_ci * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and 388c2ecf20Sopenharmony_ci * currently rare) "Ethernet Emulation Model" (EEM). 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * RNDIS notifications from device: command completion; "reverse" 438c2ecf20Sopenharmony_ci * keepalives; etc 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_civoid rndis_status(struct usbnet *dev, struct urb *urb) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "rndis status urb, len %d stat %d\n", 488c2ecf20Sopenharmony_ci urb->actual_length, urb->status); 498c2ecf20Sopenharmony_ci // FIXME for keepalives, respond immediately (asynchronously) 508c2ecf20Sopenharmony_ci // if not an RNDIS status, do like cdc_status(dev,urb) does 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_status); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * RNDIS indicate messages. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_cistatic void rndis_msg_indicate(struct usbnet *dev, struct rndis_indicate *msg, 588c2ecf20Sopenharmony_ci int buflen) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct cdc_state *info = (void *)&dev->data; 618c2ecf20Sopenharmony_ci struct device *udev = &info->control->dev; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (dev->driver_info->indication) { 648c2ecf20Sopenharmony_ci dev->driver_info->indication(dev, msg, buflen); 658c2ecf20Sopenharmony_ci } else { 668c2ecf20Sopenharmony_ci u32 status = le32_to_cpu(msg->status); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci switch (status) { 698c2ecf20Sopenharmony_ci case RNDIS_STATUS_MEDIA_CONNECT: 708c2ecf20Sopenharmony_ci dev_info(udev, "rndis media connect\n"); 718c2ecf20Sopenharmony_ci break; 728c2ecf20Sopenharmony_ci case RNDIS_STATUS_MEDIA_DISCONNECT: 738c2ecf20Sopenharmony_ci dev_info(udev, "rndis media disconnect\n"); 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci default: 768c2ecf20Sopenharmony_ci dev_info(udev, "rndis indication: 0x%08x\n", status); 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * RPC done RNDIS-style. Caller guarantees: 838c2ecf20Sopenharmony_ci * - message is properly byteswapped 848c2ecf20Sopenharmony_ci * - there's no other request pending 858c2ecf20Sopenharmony_ci * - buf can hold up to 1KB response (required by RNDIS spec) 868c2ecf20Sopenharmony_ci * On return, the first few entries are already byteswapped. 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * Call context is likely probe(), before interface name is known, 898c2ecf20Sopenharmony_ci * which is why we won't try to use it in the diagnostics. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ciint rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct cdc_state *info = (void *) &dev->data; 948c2ecf20Sopenharmony_ci struct usb_cdc_notification notification; 958c2ecf20Sopenharmony_ci int master_ifnum; 968c2ecf20Sopenharmony_ci int retval; 978c2ecf20Sopenharmony_ci int partial; 988c2ecf20Sopenharmony_ci unsigned count; 998c2ecf20Sopenharmony_ci u32 xid = 0, msg_len, request_id, msg_type, rsp, 1008c2ecf20Sopenharmony_ci status; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* REVISIT when this gets called from contexts other than probe() or 1038c2ecf20Sopenharmony_ci * disconnect(): either serialize, or dispatch responses on xid 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci msg_type = le32_to_cpu(buf->msg_type); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Issue the request; xid is unique, don't bother byteswapping it */ 1098c2ecf20Sopenharmony_ci if (likely(msg_type != RNDIS_MSG_HALT && msg_type != RNDIS_MSG_RESET)) { 1108c2ecf20Sopenharmony_ci xid = dev->xid++; 1118c2ecf20Sopenharmony_ci if (!xid) 1128c2ecf20Sopenharmony_ci xid = dev->xid++; 1138c2ecf20Sopenharmony_ci buf->request_id = (__force __le32) xid; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci master_ifnum = info->control->cur_altsetting->desc.bInterfaceNumber; 1168c2ecf20Sopenharmony_ci retval = usb_control_msg(dev->udev, 1178c2ecf20Sopenharmony_ci usb_sndctrlpipe(dev->udev, 0), 1188c2ecf20Sopenharmony_ci USB_CDC_SEND_ENCAPSULATED_COMMAND, 1198c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE, 1208c2ecf20Sopenharmony_ci 0, master_ifnum, 1218c2ecf20Sopenharmony_ci buf, le32_to_cpu(buf->msg_len), 1228c2ecf20Sopenharmony_ci RNDIS_CONTROL_TIMEOUT_MS); 1238c2ecf20Sopenharmony_ci if (unlikely(retval < 0 || xid == 0)) 1248c2ecf20Sopenharmony_ci return retval; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Some devices don't respond on the control channel until 1278c2ecf20Sopenharmony_ci * polled on the status channel, so do that first. */ 1288c2ecf20Sopenharmony_ci if (dev->driver_info->data & RNDIS_DRIVER_DATA_POLL_STATUS) { 1298c2ecf20Sopenharmony_ci retval = usb_interrupt_msg( 1308c2ecf20Sopenharmony_ci dev->udev, 1318c2ecf20Sopenharmony_ci usb_rcvintpipe(dev->udev, 1328c2ecf20Sopenharmony_ci dev->status->desc.bEndpointAddress), 1338c2ecf20Sopenharmony_ci ¬ification, sizeof(notification), &partial, 1348c2ecf20Sopenharmony_ci RNDIS_CONTROL_TIMEOUT_MS); 1358c2ecf20Sopenharmony_ci if (unlikely(retval < 0)) 1368c2ecf20Sopenharmony_ci return retval; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Poll the control channel; the request probably completed immediately */ 1408c2ecf20Sopenharmony_ci rsp = le32_to_cpu(buf->msg_type) | RNDIS_MSG_COMPLETION; 1418c2ecf20Sopenharmony_ci for (count = 0; count < 10; count++) { 1428c2ecf20Sopenharmony_ci memset(buf, 0, CONTROL_BUFFER_SIZE); 1438c2ecf20Sopenharmony_ci retval = usb_control_msg(dev->udev, 1448c2ecf20Sopenharmony_ci usb_rcvctrlpipe(dev->udev, 0), 1458c2ecf20Sopenharmony_ci USB_CDC_GET_ENCAPSULATED_RESPONSE, 1468c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 1478c2ecf20Sopenharmony_ci 0, master_ifnum, 1488c2ecf20Sopenharmony_ci buf, buflen, 1498c2ecf20Sopenharmony_ci RNDIS_CONTROL_TIMEOUT_MS); 1508c2ecf20Sopenharmony_ci if (likely(retval >= 8)) { 1518c2ecf20Sopenharmony_ci msg_type = le32_to_cpu(buf->msg_type); 1528c2ecf20Sopenharmony_ci msg_len = le32_to_cpu(buf->msg_len); 1538c2ecf20Sopenharmony_ci status = le32_to_cpu(buf->status); 1548c2ecf20Sopenharmony_ci request_id = (__force u32) buf->request_id; 1558c2ecf20Sopenharmony_ci if (likely(msg_type == rsp)) { 1568c2ecf20Sopenharmony_ci if (likely(request_id == xid)) { 1578c2ecf20Sopenharmony_ci if (unlikely(rsp == RNDIS_MSG_RESET_C)) 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci if (likely(RNDIS_STATUS_SUCCESS == 1608c2ecf20Sopenharmony_ci status)) 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci dev_dbg(&info->control->dev, 1638c2ecf20Sopenharmony_ci "rndis reply status %08x\n", 1648c2ecf20Sopenharmony_ci status); 1658c2ecf20Sopenharmony_ci return -EL3RST; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci dev_dbg(&info->control->dev, 1688c2ecf20Sopenharmony_ci "rndis reply id %d expected %d\n", 1698c2ecf20Sopenharmony_ci request_id, xid); 1708c2ecf20Sopenharmony_ci /* then likely retry */ 1718c2ecf20Sopenharmony_ci } else switch (msg_type) { 1728c2ecf20Sopenharmony_ci case RNDIS_MSG_INDICATE: /* fault/event */ 1738c2ecf20Sopenharmony_ci rndis_msg_indicate(dev, (void *)buf, buflen); 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci case RNDIS_MSG_KEEPALIVE: { /* ping */ 1768c2ecf20Sopenharmony_ci struct rndis_keepalive_c *msg = (void *)buf; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci msg->msg_type = cpu_to_le32(RNDIS_MSG_KEEPALIVE_C); 1798c2ecf20Sopenharmony_ci msg->msg_len = cpu_to_le32(sizeof *msg); 1808c2ecf20Sopenharmony_ci msg->status = cpu_to_le32(RNDIS_STATUS_SUCCESS); 1818c2ecf20Sopenharmony_ci retval = usb_control_msg(dev->udev, 1828c2ecf20Sopenharmony_ci usb_sndctrlpipe(dev->udev, 0), 1838c2ecf20Sopenharmony_ci USB_CDC_SEND_ENCAPSULATED_COMMAND, 1848c2ecf20Sopenharmony_ci USB_TYPE_CLASS | USB_RECIP_INTERFACE, 1858c2ecf20Sopenharmony_ci 0, master_ifnum, 1868c2ecf20Sopenharmony_ci msg, sizeof *msg, 1878c2ecf20Sopenharmony_ci RNDIS_CONTROL_TIMEOUT_MS); 1888c2ecf20Sopenharmony_ci if (unlikely(retval < 0)) 1898c2ecf20Sopenharmony_ci dev_dbg(&info->control->dev, 1908c2ecf20Sopenharmony_ci "rndis keepalive err %d\n", 1918c2ecf20Sopenharmony_ci retval); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci default: 1958c2ecf20Sopenharmony_ci dev_dbg(&info->control->dev, 1968c2ecf20Sopenharmony_ci "unexpected rndis msg %08x len %d\n", 1978c2ecf20Sopenharmony_ci le32_to_cpu(buf->msg_type), msg_len); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci } else { 2008c2ecf20Sopenharmony_ci /* device probably issued a protocol stall; ignore */ 2018c2ecf20Sopenharmony_ci dev_dbg(&info->control->dev, 2028c2ecf20Sopenharmony_ci "rndis response error, code %d\n", retval); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci msleep(40); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci dev_dbg(&info->control->dev, "rndis response timeout\n"); 2078c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_command); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* 2128c2ecf20Sopenharmony_ci * rndis_query: 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * Performs a query for @oid along with 0 or more bytes of payload as 2158c2ecf20Sopenharmony_ci * specified by @in_len. If @reply_len is not set to -1 then the reply 2168c2ecf20Sopenharmony_ci * length is checked against this value, resulting in an error if it 2178c2ecf20Sopenharmony_ci * doesn't match. 2188c2ecf20Sopenharmony_ci * 2198c2ecf20Sopenharmony_ci * NOTE: Adding a payload exactly or greater than the size of the expected 2208c2ecf20Sopenharmony_ci * response payload is an evident requirement MSFT added for ActiveSync. 2218c2ecf20Sopenharmony_ci * 2228c2ecf20Sopenharmony_ci * The only exception is for OIDs that return a variably sized response, 2238c2ecf20Sopenharmony_ci * in which case no payload should be added. This undocumented (and 2248c2ecf20Sopenharmony_ci * nonsensical!) issue was found by sniffing protocol requests from the 2258c2ecf20Sopenharmony_ci * ActiveSync 4.1 Windows driver. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_cistatic int rndis_query(struct usbnet *dev, struct usb_interface *intf, 2288c2ecf20Sopenharmony_ci void *buf, u32 oid, u32 in_len, 2298c2ecf20Sopenharmony_ci void **reply, int *reply_len) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci int retval; 2328c2ecf20Sopenharmony_ci union { 2338c2ecf20Sopenharmony_ci void *buf; 2348c2ecf20Sopenharmony_ci struct rndis_msg_hdr *header; 2358c2ecf20Sopenharmony_ci struct rndis_query *get; 2368c2ecf20Sopenharmony_ci struct rndis_query_c *get_c; 2378c2ecf20Sopenharmony_ci } u; 2388c2ecf20Sopenharmony_ci u32 off, len; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci u.buf = buf; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci memset(u.get, 0, sizeof *u.get + in_len); 2438c2ecf20Sopenharmony_ci u.get->msg_type = cpu_to_le32(RNDIS_MSG_QUERY); 2448c2ecf20Sopenharmony_ci u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len); 2458c2ecf20Sopenharmony_ci u.get->oid = cpu_to_le32(oid); 2468c2ecf20Sopenharmony_ci u.get->len = cpu_to_le32(in_len); 2478c2ecf20Sopenharmony_ci u.get->offset = cpu_to_le32(20); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); 2508c2ecf20Sopenharmony_ci if (unlikely(retval < 0)) { 2518c2ecf20Sopenharmony_ci dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n", 2528c2ecf20Sopenharmony_ci oid, retval); 2538c2ecf20Sopenharmony_ci return retval; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci off = le32_to_cpu(u.get_c->offset); 2578c2ecf20Sopenharmony_ci len = le32_to_cpu(u.get_c->len); 2588c2ecf20Sopenharmony_ci if (unlikely((off > CONTROL_BUFFER_SIZE - 8) || 2598c2ecf20Sopenharmony_ci (len > CONTROL_BUFFER_SIZE - 8 - off))) 2608c2ecf20Sopenharmony_ci goto response_error; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (*reply_len != -1 && len != *reply_len) 2638c2ecf20Sopenharmony_ci goto response_error; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci *reply = (unsigned char *) &u.get_c->request_id + off; 2668c2ecf20Sopenharmony_ci *reply_len = len; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return retval; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ciresponse_error: 2718c2ecf20Sopenharmony_ci dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) " 2728c2ecf20Sopenharmony_ci "invalid response - off %d len %d\n", 2738c2ecf20Sopenharmony_ci oid, off, len); 2748c2ecf20Sopenharmony_ci return -EDOM; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* same as usbnet_netdev_ops but MTU change not allowed */ 2788c2ecf20Sopenharmony_cistatic const struct net_device_ops rndis_netdev_ops = { 2798c2ecf20Sopenharmony_ci .ndo_open = usbnet_open, 2808c2ecf20Sopenharmony_ci .ndo_stop = usbnet_stop, 2818c2ecf20Sopenharmony_ci .ndo_start_xmit = usbnet_start_xmit, 2828c2ecf20Sopenharmony_ci .ndo_tx_timeout = usbnet_tx_timeout, 2838c2ecf20Sopenharmony_ci .ndo_get_stats64 = usbnet_get_stats64, 2848c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 2858c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ciint 2898c2ecf20Sopenharmony_cigeneric_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci int retval; 2928c2ecf20Sopenharmony_ci struct net_device *net = dev->net; 2938c2ecf20Sopenharmony_ci struct cdc_state *info = (void *) &dev->data; 2948c2ecf20Sopenharmony_ci union { 2958c2ecf20Sopenharmony_ci void *buf; 2968c2ecf20Sopenharmony_ci struct rndis_msg_hdr *header; 2978c2ecf20Sopenharmony_ci struct rndis_init *init; 2988c2ecf20Sopenharmony_ci struct rndis_init_c *init_c; 2998c2ecf20Sopenharmony_ci struct rndis_query *get; 3008c2ecf20Sopenharmony_ci struct rndis_query_c *get_c; 3018c2ecf20Sopenharmony_ci struct rndis_set *set; 3028c2ecf20Sopenharmony_ci struct rndis_set_c *set_c; 3038c2ecf20Sopenharmony_ci struct rndis_halt *halt; 3048c2ecf20Sopenharmony_ci } u; 3058c2ecf20Sopenharmony_ci u32 tmp; 3068c2ecf20Sopenharmony_ci __le32 phym_unspec, *phym; 3078c2ecf20Sopenharmony_ci int reply_len; 3088c2ecf20Sopenharmony_ci unsigned char *bp; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* we can't rely on i/o from stack working, or stack allocation */ 3118c2ecf20Sopenharmony_ci u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); 3128c2ecf20Sopenharmony_ci if (!u.buf) 3138c2ecf20Sopenharmony_ci return -ENOMEM; 3148c2ecf20Sopenharmony_ci retval = usbnet_generic_cdc_bind(dev, intf); 3158c2ecf20Sopenharmony_ci if (retval < 0) 3168c2ecf20Sopenharmony_ci goto fail; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci u.init->msg_type = cpu_to_le32(RNDIS_MSG_INIT); 3198c2ecf20Sopenharmony_ci u.init->msg_len = cpu_to_le32(sizeof *u.init); 3208c2ecf20Sopenharmony_ci u.init->major_version = cpu_to_le32(1); 3218c2ecf20Sopenharmony_ci u.init->minor_version = cpu_to_le32(0); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* max transfer (in spec) is 0x4000 at full speed, but for 3248c2ecf20Sopenharmony_ci * TX we'll stick to one Ethernet packet plus RNDIS framing. 3258c2ecf20Sopenharmony_ci * For RX we handle drivers that zero-pad to end-of-packet. 3268c2ecf20Sopenharmony_ci * Don't let userspace change these settings. 3278c2ecf20Sopenharmony_ci * 3288c2ecf20Sopenharmony_ci * NOTE: there still seems to be wierdness here, as if we need 3298c2ecf20Sopenharmony_ci * to do some more things to make sure WinCE targets accept this. 3308c2ecf20Sopenharmony_ci * They default to jumbograms of 8KB or 16KB, which is absurd 3318c2ecf20Sopenharmony_ci * for such low data rates and which is also more than Linux 3328c2ecf20Sopenharmony_ci * can usually expect to allocate for SKB data... 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_ci net->hard_header_len += sizeof (struct rndis_data_hdr); 3358c2ecf20Sopenharmony_ci dev->hard_mtu = net->mtu + net->hard_header_len; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci dev->maxpacket = usb_maxpacket(dev->udev, dev->out, 1); 3388c2ecf20Sopenharmony_ci if (dev->maxpacket == 0) { 3398c2ecf20Sopenharmony_ci netif_dbg(dev, probe, dev->net, 3408c2ecf20Sopenharmony_ci "dev->maxpacket can't be 0\n"); 3418c2ecf20Sopenharmony_ci retval = -EINVAL; 3428c2ecf20Sopenharmony_ci goto fail_and_release; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1); 3468c2ecf20Sopenharmony_ci dev->rx_urb_size &= ~(dev->maxpacket - 1); 3478c2ecf20Sopenharmony_ci u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci net->netdev_ops = &rndis_netdev_ops; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); 3528c2ecf20Sopenharmony_ci if (unlikely(retval < 0)) { 3538c2ecf20Sopenharmony_ci /* it might not even be an RNDIS device!! */ 3548c2ecf20Sopenharmony_ci dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); 3558c2ecf20Sopenharmony_ci goto fail_and_release; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci tmp = le32_to_cpu(u.init_c->max_transfer_size); 3588c2ecf20Sopenharmony_ci if (tmp < dev->hard_mtu) { 3598c2ecf20Sopenharmony_ci if (tmp <= net->hard_header_len) { 3608c2ecf20Sopenharmony_ci dev_err(&intf->dev, 3618c2ecf20Sopenharmony_ci "dev can't take %u byte packets (max %u)\n", 3628c2ecf20Sopenharmony_ci dev->hard_mtu, tmp); 3638c2ecf20Sopenharmony_ci retval = -EINVAL; 3648c2ecf20Sopenharmony_ci goto halt_fail_and_release; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci dev_warn(&intf->dev, 3678c2ecf20Sopenharmony_ci "dev can't take %u byte packets (max %u), " 3688c2ecf20Sopenharmony_ci "adjusting MTU to %u\n", 3698c2ecf20Sopenharmony_ci dev->hard_mtu, tmp, tmp - net->hard_header_len); 3708c2ecf20Sopenharmony_ci dev->hard_mtu = tmp; 3718c2ecf20Sopenharmony_ci net->mtu = dev->hard_mtu - net->hard_header_len; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* REVISIT: peripheral "alignment" request is ignored ... */ 3758c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, 3768c2ecf20Sopenharmony_ci "hard mtu %u (%u from dev), rx buflen %zu, align %d\n", 3778c2ecf20Sopenharmony_ci dev->hard_mtu, tmp, dev->rx_urb_size, 3788c2ecf20Sopenharmony_ci 1 << le32_to_cpu(u.init_c->packet_alignment)); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* module has some device initialization code needs to be done right 3818c2ecf20Sopenharmony_ci * after RNDIS_INIT */ 3828c2ecf20Sopenharmony_ci if (dev->driver_info->early_init && 3838c2ecf20Sopenharmony_ci dev->driver_info->early_init(dev) != 0) 3848c2ecf20Sopenharmony_ci goto halt_fail_and_release; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Check physical medium */ 3878c2ecf20Sopenharmony_ci phym = NULL; 3888c2ecf20Sopenharmony_ci reply_len = sizeof *phym; 3898c2ecf20Sopenharmony_ci retval = rndis_query(dev, intf, u.buf, 3908c2ecf20Sopenharmony_ci RNDIS_OID_GEN_PHYSICAL_MEDIUM, 3918c2ecf20Sopenharmony_ci reply_len, (void **)&phym, &reply_len); 3928c2ecf20Sopenharmony_ci if (retval != 0 || !phym) { 3938c2ecf20Sopenharmony_ci /* OID is optional so don't fail here. */ 3948c2ecf20Sopenharmony_ci phym_unspec = cpu_to_le32(RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED); 3958c2ecf20Sopenharmony_ci phym = &phym_unspec; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci if ((flags & FLAG_RNDIS_PHYM_WIRELESS) && 3988c2ecf20Sopenharmony_ci le32_to_cpup(phym) != RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) { 3998c2ecf20Sopenharmony_ci netif_dbg(dev, probe, dev->net, 4008c2ecf20Sopenharmony_ci "driver requires wireless physical medium, but device is not\n"); 4018c2ecf20Sopenharmony_ci retval = -ENODEV; 4028c2ecf20Sopenharmony_ci goto halt_fail_and_release; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci if ((flags & FLAG_RNDIS_PHYM_NOT_WIRELESS) && 4058c2ecf20Sopenharmony_ci le32_to_cpup(phym) == RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN) { 4068c2ecf20Sopenharmony_ci netif_dbg(dev, probe, dev->net, 4078c2ecf20Sopenharmony_ci "driver requires non-wireless physical medium, but device is wireless.\n"); 4088c2ecf20Sopenharmony_ci retval = -ENODEV; 4098c2ecf20Sopenharmony_ci goto halt_fail_and_release; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* Get designated host ethernet address */ 4138c2ecf20Sopenharmony_ci reply_len = ETH_ALEN; 4148c2ecf20Sopenharmony_ci retval = rndis_query(dev, intf, u.buf, 4158c2ecf20Sopenharmony_ci RNDIS_OID_802_3_PERMANENT_ADDRESS, 4168c2ecf20Sopenharmony_ci 48, (void **) &bp, &reply_len); 4178c2ecf20Sopenharmony_ci if (unlikely(retval< 0)) { 4188c2ecf20Sopenharmony_ci dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); 4198c2ecf20Sopenharmony_ci goto halt_fail_and_release; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (bp[0] & 0x02) 4238c2ecf20Sopenharmony_ci eth_hw_addr_random(net); 4248c2ecf20Sopenharmony_ci else 4258c2ecf20Sopenharmony_ci ether_addr_copy(net->dev_addr, bp); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* set a nonzero filter to enable data transfers */ 4288c2ecf20Sopenharmony_ci memset(u.set, 0, sizeof *u.set); 4298c2ecf20Sopenharmony_ci u.set->msg_type = cpu_to_le32(RNDIS_MSG_SET); 4308c2ecf20Sopenharmony_ci u.set->msg_len = cpu_to_le32(4 + sizeof *u.set); 4318c2ecf20Sopenharmony_ci u.set->oid = cpu_to_le32(RNDIS_OID_GEN_CURRENT_PACKET_FILTER); 4328c2ecf20Sopenharmony_ci u.set->len = cpu_to_le32(4); 4338c2ecf20Sopenharmony_ci u.set->offset = cpu_to_le32((sizeof *u.set) - 8); 4348c2ecf20Sopenharmony_ci *(__le32 *)(u.buf + sizeof *u.set) = cpu_to_le32(RNDIS_DEFAULT_FILTER); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); 4378c2ecf20Sopenharmony_ci if (unlikely(retval < 0)) { 4388c2ecf20Sopenharmony_ci dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); 4398c2ecf20Sopenharmony_ci goto halt_fail_and_release; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci retval = 0; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci kfree(u.buf); 4458c2ecf20Sopenharmony_ci return retval; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cihalt_fail_and_release: 4488c2ecf20Sopenharmony_ci memset(u.halt, 0, sizeof *u.halt); 4498c2ecf20Sopenharmony_ci u.halt->msg_type = cpu_to_le32(RNDIS_MSG_HALT); 4508c2ecf20Sopenharmony_ci u.halt->msg_len = cpu_to_le32(sizeof *u.halt); 4518c2ecf20Sopenharmony_ci (void) rndis_command(dev, (void *)u.halt, CONTROL_BUFFER_SIZE); 4528c2ecf20Sopenharmony_cifail_and_release: 4538c2ecf20Sopenharmony_ci usb_set_intfdata(info->data, NULL); 4548c2ecf20Sopenharmony_ci usb_driver_release_interface(driver_of(intf), info->data); 4558c2ecf20Sopenharmony_ci info->data = NULL; 4568c2ecf20Sopenharmony_cifail: 4578c2ecf20Sopenharmony_ci kfree(u.buf); 4588c2ecf20Sopenharmony_ci return retval; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(generic_rndis_bind); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int rndis_bind(struct usbnet *dev, struct usb_interface *intf) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci return generic_rndis_bind(dev, intf, FLAG_RNDIS_PHYM_NOT_WIRELESS); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_civoid rndis_unbind(struct usbnet *dev, struct usb_interface *intf) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct rndis_halt *halt; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* try to clear any rndis state/activity (no i/o from stack!) */ 4728c2ecf20Sopenharmony_ci halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); 4738c2ecf20Sopenharmony_ci if (halt) { 4748c2ecf20Sopenharmony_ci halt->msg_type = cpu_to_le32(RNDIS_MSG_HALT); 4758c2ecf20Sopenharmony_ci halt->msg_len = cpu_to_le32(sizeof *halt); 4768c2ecf20Sopenharmony_ci (void) rndis_command(dev, (void *)halt, CONTROL_BUFFER_SIZE); 4778c2ecf20Sopenharmony_ci kfree(halt); 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci usbnet_cdc_unbind(dev, intf); 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_unbind); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci/* 4858c2ecf20Sopenharmony_ci * DATA -- host must not write zlps 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ciint rndis_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci /* This check is no longer done by usbnet */ 4908c2ecf20Sopenharmony_ci if (skb->len < dev->net->hard_header_len) 4918c2ecf20Sopenharmony_ci return 0; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* peripheral may have batched packets to us... */ 4948c2ecf20Sopenharmony_ci while (likely(skb->len)) { 4958c2ecf20Sopenharmony_ci struct rndis_data_hdr *hdr = (void *)skb->data; 4968c2ecf20Sopenharmony_ci struct sk_buff *skb2; 4978c2ecf20Sopenharmony_ci u32 msg_type, msg_len, data_offset, data_len; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci msg_type = le32_to_cpu(hdr->msg_type); 5008c2ecf20Sopenharmony_ci msg_len = le32_to_cpu(hdr->msg_len); 5018c2ecf20Sopenharmony_ci data_offset = le32_to_cpu(hdr->data_offset); 5028c2ecf20Sopenharmony_ci data_len = le32_to_cpu(hdr->data_len); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* don't choke if we see oob, per-packet data, etc */ 5058c2ecf20Sopenharmony_ci if (unlikely(msg_type != RNDIS_MSG_PACKET || skb->len < msg_len 5068c2ecf20Sopenharmony_ci || (data_offset + data_len + 8) > msg_len)) { 5078c2ecf20Sopenharmony_ci dev->net->stats.rx_frame_errors++; 5088c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "bad rndis message %d/%d/%d/%d, len %d\n", 5098c2ecf20Sopenharmony_ci le32_to_cpu(hdr->msg_type), 5108c2ecf20Sopenharmony_ci msg_len, data_offset, data_len, skb->len); 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci skb_pull(skb, 8 + data_offset); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* at most one packet left? */ 5168c2ecf20Sopenharmony_ci if (likely((data_len - skb->len) <= sizeof *hdr)) { 5178c2ecf20Sopenharmony_ci skb_trim(skb, data_len); 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* try to return all the packets in the batch */ 5228c2ecf20Sopenharmony_ci skb2 = skb_clone(skb, GFP_ATOMIC); 5238c2ecf20Sopenharmony_ci if (unlikely(!skb2)) 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci skb_pull(skb, msg_len - sizeof *hdr); 5268c2ecf20Sopenharmony_ci skb_trim(skb2, data_len); 5278c2ecf20Sopenharmony_ci usbnet_skb_return(dev, skb2); 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* caller will usbnet_skb_return the remaining packet */ 5318c2ecf20Sopenharmony_ci return 1; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_rx_fixup); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistruct sk_buff * 5368c2ecf20Sopenharmony_cirndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct rndis_data_hdr *hdr; 5398c2ecf20Sopenharmony_ci struct sk_buff *skb2; 5408c2ecf20Sopenharmony_ci unsigned len = skb->len; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (likely(!skb_cloned(skb))) { 5438c2ecf20Sopenharmony_ci int room = skb_headroom(skb); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* enough head room as-is? */ 5468c2ecf20Sopenharmony_ci if (unlikely((sizeof *hdr) <= room)) 5478c2ecf20Sopenharmony_ci goto fill; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* enough room, but needs to be readjusted? */ 5508c2ecf20Sopenharmony_ci room += skb_tailroom(skb); 5518c2ecf20Sopenharmony_ci if (likely((sizeof *hdr) <= room)) { 5528c2ecf20Sopenharmony_ci skb->data = memmove(skb->head + sizeof *hdr, 5538c2ecf20Sopenharmony_ci skb->data, len); 5548c2ecf20Sopenharmony_ci skb_set_tail_pointer(skb, len); 5558c2ecf20Sopenharmony_ci goto fill; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* create a new skb, with the correct size (and tailpad) */ 5608c2ecf20Sopenharmony_ci skb2 = skb_copy_expand(skb, sizeof *hdr, 1, flags); 5618c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 5628c2ecf20Sopenharmony_ci if (unlikely(!skb2)) 5638c2ecf20Sopenharmony_ci return skb2; 5648c2ecf20Sopenharmony_ci skb = skb2; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* fill out the RNDIS header. we won't bother trying to batch 5678c2ecf20Sopenharmony_ci * packets; Linux minimizes wasted bandwidth through tx queues. 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_cifill: 5708c2ecf20Sopenharmony_ci hdr = __skb_push(skb, sizeof *hdr); 5718c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof *hdr); 5728c2ecf20Sopenharmony_ci hdr->msg_type = cpu_to_le32(RNDIS_MSG_PACKET); 5738c2ecf20Sopenharmony_ci hdr->msg_len = cpu_to_le32(skb->len); 5748c2ecf20Sopenharmony_ci hdr->data_offset = cpu_to_le32(sizeof(*hdr) - 8); 5758c2ecf20Sopenharmony_ci hdr->data_len = cpu_to_le32(len); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* FIXME make the last packet always be short ... */ 5788c2ecf20Sopenharmony_ci return skb; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rndis_tx_fixup); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic const struct driver_info rndis_info = { 5848c2ecf20Sopenharmony_ci .description = "RNDIS device", 5858c2ecf20Sopenharmony_ci .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, 5868c2ecf20Sopenharmony_ci .bind = rndis_bind, 5878c2ecf20Sopenharmony_ci .unbind = rndis_unbind, 5888c2ecf20Sopenharmony_ci .status = rndis_status, 5898c2ecf20Sopenharmony_ci .rx_fixup = rndis_rx_fixup, 5908c2ecf20Sopenharmony_ci .tx_fixup = rndis_tx_fixup, 5918c2ecf20Sopenharmony_ci}; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic const struct driver_info rndis_poll_status_info = { 5948c2ecf20Sopenharmony_ci .description = "RNDIS device (poll status before control)", 5958c2ecf20Sopenharmony_ci .flags = FLAG_ETHER | FLAG_POINTTOPOINT | FLAG_FRAMING_RN | FLAG_NO_SETINT, 5968c2ecf20Sopenharmony_ci .data = RNDIS_DRIVER_DATA_POLL_STATUS, 5978c2ecf20Sopenharmony_ci .bind = rndis_bind, 5988c2ecf20Sopenharmony_ci .unbind = rndis_unbind, 5998c2ecf20Sopenharmony_ci .status = rndis_status, 6008c2ecf20Sopenharmony_ci .rx_fixup = rndis_rx_fixup, 6018c2ecf20Sopenharmony_ci .tx_fixup = rndis_tx_fixup, 6028c2ecf20Sopenharmony_ci}; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic const struct usb_device_id products [] = { 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci /* 2Wire HomePortal 1000SW */ 6098c2ecf20Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1630, 0x0042, 6108c2ecf20Sopenharmony_ci USB_CLASS_COMM, 2 /* ACM */, 0x0ff), 6118c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &rndis_poll_status_info, 6128c2ecf20Sopenharmony_ci}, { 6138c2ecf20Sopenharmony_ci /* Hytera Communications DMR radios' "Radio to PC Network" */ 6148c2ecf20Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(0x238b, 6158c2ecf20Sopenharmony_ci USB_CLASS_COMM, 2 /* ACM */, 0x0ff), 6168c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&rndis_info, 6178c2ecf20Sopenharmony_ci}, { 6188c2ecf20Sopenharmony_ci /* RNDIS is MSFT's un-official variant of CDC ACM */ 6198c2ecf20Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), 6208c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &rndis_info, 6218c2ecf20Sopenharmony_ci}, { 6228c2ecf20Sopenharmony_ci /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ 6238c2ecf20Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), 6248c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &rndis_poll_status_info, 6258c2ecf20Sopenharmony_ci}, { 6268c2ecf20Sopenharmony_ci /* RNDIS for tethering */ 6278c2ecf20Sopenharmony_ci#ifndef CONFIG_DRIVERS_HDF_USB_NET_ADAPTER 6288c2ecf20Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_WIRELESS_CONTROLLER, 1, 3), 6298c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &rndis_info, 6308c2ecf20Sopenharmony_ci#endif 6318c2ecf20Sopenharmony_ci}, { 6328c2ecf20Sopenharmony_ci /* Novatel Verizon USB730L */ 6338c2ecf20Sopenharmony_ci USB_INTERFACE_INFO(USB_CLASS_MISC, 4, 1), 6348c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &rndis_info, 6358c2ecf20Sopenharmony_ci}, 6368c2ecf20Sopenharmony_ci { }, // END 6378c2ecf20Sopenharmony_ci}; 6388c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic struct usb_driver rndis_driver = { 6418c2ecf20Sopenharmony_ci .name = "rndis_host", 6428c2ecf20Sopenharmony_ci .id_table = products, 6438c2ecf20Sopenharmony_ci .probe = usbnet_probe, 6448c2ecf20Sopenharmony_ci .disconnect = usbnet_disconnect, 6458c2ecf20Sopenharmony_ci .suspend = usbnet_suspend, 6468c2ecf20Sopenharmony_ci .resume = usbnet_resume, 6478c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 6488c2ecf20Sopenharmony_ci}; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cimodule_usb_driver(rndis_driver); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 6538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("USB Host side RNDIS driver"); 6548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 655