diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_rndis.c')
| -rw-r--r-- | drivers/usb/gadget/function/f_rndis.c | 1035 |
1 files changed, 1035 insertions, 0 deletions
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c new file mode 100644 index 000000000000..ddb09dc6d1f2 --- /dev/null +++ b/drivers/usb/gadget/function/f_rndis.c | |||
| @@ -0,0 +1,1035 @@ | |||
| 1 | /* | ||
| 2 | * f_rndis.c -- RNDIS link function driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2003-2005,2008 David Brownell | ||
| 5 | * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger | ||
| 6 | * Copyright (C) 2008 Nokia Corporation | ||
| 7 | * Copyright (C) 2009 Samsung Electronics | ||
| 8 | * Author: Michal Nazarewicz (mina86@mina86.com) | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; either version 2 of the License, or | ||
| 13 | * (at your option) any later version. | ||
| 14 | */ | ||
| 15 | |||
| 16 | /* #define VERBOSE_DEBUG */ | ||
| 17 | |||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/kernel.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/device.h> | ||
| 22 | #include <linux/etherdevice.h> | ||
| 23 | |||
| 24 | #include <linux/atomic.h> | ||
| 25 | |||
| 26 | #include "u_ether.h" | ||
| 27 | #include "u_ether_configfs.h" | ||
| 28 | #include "u_rndis.h" | ||
| 29 | #include "rndis.h" | ||
| 30 | #include "configfs.h" | ||
| 31 | |||
| 32 | /* | ||
| 33 | * This function is an RNDIS Ethernet port -- a Microsoft protocol that's | ||
| 34 | * been promoted instead of the standard CDC Ethernet. The published RNDIS | ||
| 35 | * spec is ambiguous, incomplete, and needlessly complex. Variants such as | ||
| 36 | * ActiveSync have even worse status in terms of specification. | ||
| 37 | * | ||
| 38 | * In short: it's a protocol controlled by (and for) Microsoft, not for an | ||
| 39 | * Open ecosystem or markets. Linux supports it *only* because Microsoft | ||
| 40 | * doesn't support the CDC Ethernet standard. | ||
| 41 | * | ||
| 42 | * The RNDIS data transfer model is complex, with multiple Ethernet packets | ||
| 43 | * per USB message, and out of band data. The control model is built around | ||
| 44 | * what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM | ||
| 45 | * (modem, not Ethernet) veneer, with those ACM descriptors being entirely | ||
| 46 | * useless (they're ignored). RNDIS expects to be the only function in its | ||
| 47 | * configuration, so it's no real help if you need composite devices; and | ||
| 48 | * it expects to be the first configuration too. | ||
| 49 | * | ||
| 50 | * There is a single technical advantage of RNDIS over CDC Ethernet, if you | ||
| 51 | * discount the fluff that its RPC can be made to deliver: it doesn't need | ||
| 52 | * a NOP altsetting for the data interface. That lets it work on some of the | ||
| 53 | * "so smart it's stupid" hardware which takes over configuration changes | ||
| 54 | * from the software, and adds restrictions like "no altsettings". | ||
| 55 | * | ||
| 56 | * Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and | ||
| 57 | * have all sorts of contrary-to-specification oddities that can prevent | ||
| 58 | * them from working sanely. Since bugfixes (or accurate specs, letting | ||
| 59 | * Linux work around those bugs) are unlikely to ever come from MSFT, you | ||
| 60 | * may want to avoid using RNDIS on purely operational grounds. | ||
| 61 | * | ||
| 62 | * Omissions from the RNDIS 1.0 specification include: | ||
| 63 | * | ||
| 64 | * - Power management ... references data that's scattered around lots | ||
| 65 | * of other documentation, which is incorrect/incomplete there too. | ||
| 66 | * | ||
| 67 | * - There are various undocumented protocol requirements, like the need | ||
| 68 | * to send garbage in some control-OUT messages. | ||
| 69 | * | ||
| 70 | * - MS-Windows drivers sometimes emit undocumented requests. | ||
| 71 | */ | ||
| 72 | |||
| 73 | struct f_rndis { | ||
| 74 | struct gether port; | ||
| 75 | u8 ctrl_id, data_id; | ||
| 76 | u8 ethaddr[ETH_ALEN]; | ||
| 77 | u32 vendorID; | ||
| 78 | const char *manufacturer; | ||
| 79 | int config; | ||
| 80 | |||
| 81 | struct usb_ep *notify; | ||
| 82 | struct usb_request *notify_req; | ||
| 83 | atomic_t notify_count; | ||
| 84 | }; | ||
| 85 | |||
| 86 | static inline struct f_rndis *func_to_rndis(struct usb_function *f) | ||
| 87 | { | ||
| 88 | return container_of(f, struct f_rndis, port.func); | ||
| 89 | } | ||
| 90 | |||
| 91 | /* peak (theoretical) bulk transfer rate in bits-per-second */ | ||
| 92 | static unsigned int bitrate(struct usb_gadget *g) | ||
| 93 | { | ||
| 94 | if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) | ||
| 95 | return 13 * 1024 * 8 * 1000 * 8; | ||
| 96 | else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) | ||
| 97 | return 13 * 512 * 8 * 1000 * 8; | ||
| 98 | else | ||
| 99 | return 19 * 64 * 1 * 1000 * 8; | ||
| 100 | } | ||
| 101 | |||
| 102 | /*-------------------------------------------------------------------------*/ | ||
| 103 | |||
| 104 | /* | ||
| 105 | */ | ||
| 106 | |||
| 107 | #define RNDIS_STATUS_INTERVAL_MS 32 | ||
| 108 | #define STATUS_BYTECOUNT 8 /* 8 bytes data */ | ||
| 109 | |||
| 110 | |||
| 111 | /* interface descriptor: */ | ||
| 112 | |||
| 113 | static struct usb_interface_descriptor rndis_control_intf = { | ||
| 114 | .bLength = sizeof rndis_control_intf, | ||
| 115 | .bDescriptorType = USB_DT_INTERFACE, | ||
| 116 | |||
| 117 | /* .bInterfaceNumber = DYNAMIC */ | ||
| 118 | /* status endpoint is optional; this could be patched later */ | ||
| 119 | .bNumEndpoints = 1, | ||
| 120 | .bInterfaceClass = USB_CLASS_COMM, | ||
| 121 | .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, | ||
| 122 | .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, | ||
| 123 | /* .iInterface = DYNAMIC */ | ||
| 124 | }; | ||
| 125 | |||
| 126 | static struct usb_cdc_header_desc header_desc = { | ||
| 127 | .bLength = sizeof header_desc, | ||
| 128 | .bDescriptorType = USB_DT_CS_INTERFACE, | ||
| 129 | .bDescriptorSubType = USB_CDC_HEADER_TYPE, | ||
| 130 | |||
| 131 | .bcdCDC = cpu_to_le16(0x0110), | ||
| 132 | }; | ||
| 133 | |||
| 134 | static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { | ||
| 135 | .bLength = sizeof call_mgmt_descriptor, | ||
| 136 | .bDescriptorType = USB_DT_CS_INTERFACE, | ||
| 137 | .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, | ||
| 138 | |||
| 139 | .bmCapabilities = 0x00, | ||
| 140 | .bDataInterface = 0x01, | ||
| 141 | }; | ||
| 142 | |||
| 143 | static struct usb_cdc_acm_descriptor rndis_acm_descriptor = { | ||
| 144 | .bLength = sizeof rndis_acm_descriptor, | ||
| 145 | .bDescriptorType = USB_DT_CS_INTERFACE, | ||
| 146 | .bDescriptorSubType = USB_CDC_ACM_TYPE, | ||
| 147 | |||
| 148 | .bmCapabilities = 0x00, | ||
| 149 | }; | ||
| 150 | |||
| 151 | static struct usb_cdc_union_desc rndis_union_desc = { | ||
| 152 | .bLength = sizeof(rndis_union_desc), | ||
| 153 | .bDescriptorType = USB_DT_CS_INTERFACE, | ||
| 154 | .bDescriptorSubType = USB_CDC_UNION_TYPE, | ||
| 155 | /* .bMasterInterface0 = DYNAMIC */ | ||
| 156 | /* .bSlaveInterface0 = DYNAMIC */ | ||
| 157 | }; | ||
| 158 | |||
| 159 | /* the data interface has two bulk endpoints */ | ||
| 160 | |||
| 161 | static struct usb_interface_descriptor rndis_data_intf = { | ||
| 162 | .bLength = sizeof rndis_data_intf, | ||
| 163 | .bDescriptorType = USB_DT_INTERFACE, | ||
| 164 | |||
| 165 | /* .bInterfaceNumber = DYNAMIC */ | ||
| 166 | .bNumEndpoints = 2, | ||
| 167 | .bInterfaceClass = USB_CLASS_CDC_DATA, | ||
| 168 | .bInterfaceSubClass = 0, | ||
| 169 | .bInterfaceProtocol = 0, | ||
| 170 | /* .iInterface = DYNAMIC */ | ||
| 171 | }; | ||
| 172 | |||
| 173 | |||
| 174 | static struct usb_interface_assoc_descriptor | ||
| 175 | rndis_iad_descriptor = { | ||
| 176 | .bLength = sizeof rndis_iad_descriptor, | ||
| 177 | .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, | ||
| 178 | |||
| 179 | .bFirstInterface = 0, /* XXX, hardcoded */ | ||
| 180 | .bInterfaceCount = 2, // control + data | ||
| 181 | .bFunctionClass = USB_CLASS_COMM, | ||
| 182 | .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, | ||
| 183 | .bFunctionProtocol = USB_CDC_PROTO_NONE, | ||
| 184 | /* .iFunction = DYNAMIC */ | ||
| 185 | }; | ||
| 186 | |||
| 187 | /* full speed support: */ | ||
| 188 | |||
| 189 | static struct usb_endpoint_descriptor fs_notify_desc = { | ||
| 190 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
| 191 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 192 | |||
| 193 | .bEndpointAddress = USB_DIR_IN, | ||
| 194 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
| 195 | .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), | ||
| 196 | .bInterval = RNDIS_STATUS_INTERVAL_MS, | ||
| 197 | }; | ||
| 198 | |||
| 199 | static struct usb_endpoint_descriptor fs_in_desc = { | ||
| 200 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
| 201 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 202 | |||
| 203 | .bEndpointAddress = USB_DIR_IN, | ||
| 204 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
| 205 | }; | ||
| 206 | |||
| 207 | static struct usb_endpoint_descriptor fs_out_desc = { | ||
| 208 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
| 209 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 210 | |||
| 211 | .bEndpointAddress = USB_DIR_OUT, | ||
| 212 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
| 213 | }; | ||
| 214 | |||
| 215 | static struct usb_descriptor_header *eth_fs_function[] = { | ||
| 216 | (struct usb_descriptor_header *) &rndis_iad_descriptor, | ||
| 217 | |||
| 218 | /* control interface matches ACM, not Ethernet */ | ||
| 219 | (struct usb_descriptor_header *) &rndis_control_intf, | ||
| 220 | (struct usb_descriptor_header *) &header_desc, | ||
| 221 | (struct usb_descriptor_header *) &call_mgmt_descriptor, | ||
| 222 | (struct usb_descriptor_header *) &rndis_acm_descriptor, | ||
| 223 | (struct usb_descriptor_header *) &rndis_union_desc, | ||
| 224 | (struct usb_descriptor_header *) &fs_notify_desc, | ||
| 225 | |||
| 226 | /* data interface has no altsetting */ | ||
| 227 | (struct usb_descriptor_header *) &rndis_data_intf, | ||
| 228 | (struct usb_descriptor_header *) &fs_in_desc, | ||
| 229 | (struct usb_descriptor_header *) &fs_out_desc, | ||
| 230 | NULL, | ||
| 231 | }; | ||
| 232 | |||
| 233 | /* high speed support: */ | ||
| 234 | |||
| 235 | static struct usb_endpoint_descriptor hs_notify_desc = { | ||
| 236 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
| 237 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 238 | |||
| 239 | .bEndpointAddress = USB_DIR_IN, | ||
| 240 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
| 241 | .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), | ||
| 242 | .bInterval = USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS) | ||
| 243 | }; | ||
| 244 | |||
| 245 | static struct usb_endpoint_descriptor hs_in_desc = { | ||
| 246 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
| 247 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 248 | |||
| 249 | .bEndpointAddress = USB_DIR_IN, | ||
| 250 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
| 251 | .wMaxPacketSize = cpu_to_le16(512), | ||
| 252 | }; | ||
| 253 | |||
| 254 | static struct usb_endpoint_descriptor hs_out_desc = { | ||
| 255 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
| 256 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 257 | |||
| 258 | .bEndpointAddress = USB_DIR_OUT, | ||
| 259 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
| 260 | .wMaxPacketSize = cpu_to_le16(512), | ||
| 261 | }; | ||
| 262 | |||
| 263 | static struct usb_descriptor_header *eth_hs_function[] = { | ||
| 264 | (struct usb_descriptor_header *) &rndis_iad_descriptor, | ||
| 265 | |||
| 266 | /* control interface matches ACM, not Ethernet */ | ||
| 267 | (struct usb_descriptor_header *) &rndis_control_intf, | ||
| 268 | (struct usb_descriptor_header *) &header_desc, | ||
| 269 | (struct usb_descriptor_header *) &call_mgmt_descriptor, | ||
| 270 | (struct usb_descriptor_header *) &rndis_acm_descriptor, | ||
| 271 | (struct usb_descriptor_header *) &rndis_union_desc, | ||
| 272 | (struct usb_descriptor_header *) &hs_notify_desc, | ||
| 273 | |||
| 274 | /* data interface has no altsetting */ | ||
| 275 | (struct usb_descriptor_header *) &rndis_data_intf, | ||
| 276 | (struct usb_descriptor_header *) &hs_in_desc, | ||
| 277 | (struct usb_descriptor_header *) &hs_out_desc, | ||
| 278 | NULL, | ||
| 279 | }; | ||
| 280 | |||
| 281 | /* super speed support: */ | ||
| 282 | |||
| 283 | static struct usb_endpoint_descriptor ss_notify_desc = { | ||
| 284 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
| 285 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 286 | |||
| 287 | .bEndpointAddress = USB_DIR_IN, | ||
| 288 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
| 289 | .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), | ||
| 290 | .bInterval = USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS) | ||
| 291 | }; | ||
| 292 | |||
| 293 | static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = { | ||
| 294 | .bLength = sizeof ss_intr_comp_desc, | ||
| 295 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | ||
| 296 | |||
| 297 | /* the following 3 values can be tweaked if necessary */ | ||
| 298 | /* .bMaxBurst = 0, */ | ||
| 299 | /* .bmAttributes = 0, */ | ||
| 300 | .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT), | ||
| 301 | }; | ||
| 302 | |||
| 303 | static struct usb_endpoint_descriptor ss_in_desc = { | ||
| 304 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
| 305 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 306 | |||
| 307 | .bEndpointAddress = USB_DIR_IN, | ||
| 308 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
| 309 | .wMaxPacketSize = cpu_to_le16(1024), | ||
| 310 | }; | ||
| 311 | |||
| 312 | static struct usb_endpoint_descriptor ss_out_desc = { | ||
| 313 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
| 314 | .bDescriptorType = USB_DT_ENDPOINT, | ||
| 315 | |||
| 316 | .bEndpointAddress = USB_DIR_OUT, | ||
| 317 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
| 318 | .wMaxPacketSize = cpu_to_le16(1024), | ||
| 319 | }; | ||
| 320 | |||
| 321 | static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = { | ||
| 322 | .bLength = sizeof ss_bulk_comp_desc, | ||
| 323 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | ||
| 324 | |||
| 325 | /* the following 2 values can be tweaked if necessary */ | ||
| 326 | /* .bMaxBurst = 0, */ | ||
| 327 | /* .bmAttributes = 0, */ | ||
| 328 | }; | ||
| 329 | |||
| 330 | static struct usb_descriptor_header *eth_ss_function[] = { | ||
| 331 | (struct usb_descriptor_header *) &rndis_iad_descriptor, | ||
| 332 | |||
| 333 | /* control interface matches ACM, not Ethernet */ | ||
| 334 | (struct usb_descriptor_header *) &rndis_control_intf, | ||
| 335 | (struct usb_descriptor_header *) &header_desc, | ||
| 336 | (struct usb_descriptor_header *) &call_mgmt_descriptor, | ||
| 337 | (struct usb_descriptor_header *) &rndis_acm_descriptor, | ||
| 338 | (struct usb_descriptor_header *) &rndis_union_desc, | ||
| 339 | (struct usb_descriptor_header *) &ss_notify_desc, | ||
| 340 | (struct usb_descriptor_header *) &ss_intr_comp_desc, | ||
| 341 | |||
| 342 | /* data interface has no altsetting */ | ||
| 343 | (struct usb_descriptor_header *) &rndis_data_intf, | ||
| 344 | (struct usb_descriptor_header *) &ss_in_desc, | ||
| 345 | (struct usb_descriptor_header *) &ss_bulk_comp_desc, | ||
| 346 | (struct usb_descriptor_header *) &ss_out_desc, | ||
| 347 | (struct usb_descriptor_header *) &ss_bulk_comp_desc, | ||
| 348 | NULL, | ||
| 349 | }; | ||
| 350 | |||
| 351 | /* string descriptors: */ | ||
| 352 | |||
| 353 | static struct usb_string rndis_string_defs[] = { | ||
| 354 | [0].s = "RNDIS Communications Control", | ||
| 355 | [1].s = "RNDIS Ethernet Data", | ||
| 356 | [2].s = "RNDIS", | ||
| 357 | { } /* end of list */ | ||
| 358 | }; | ||
| 359 | |||
| 360 | static struct usb_gadget_strings rndis_string_table = { | ||
| 361 | .language = 0x0409, /* en-us */ | ||
| 362 | .strings = rndis_string_defs, | ||
| 363 | }; | ||
| 364 | |||
| 365 | static struct usb_gadget_strings *rndis_strings[] = { | ||
| 366 | &rndis_string_table, | ||
| 367 | NULL, | ||
| 368 | }; | ||
| 369 | |||
| 370 | /*-------------------------------------------------------------------------*/ | ||
| 371 | |||
| 372 | static struct sk_buff *rndis_add_header(struct gether *port, | ||
| 373 | struct sk_buff *skb) | ||
| 374 | { | ||
| 375 | struct sk_buff *skb2; | ||
| 376 | |||
| 377 | skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type)); | ||
| 378 | if (skb2) | ||
| 379 | rndis_add_hdr(skb2); | ||
| 380 | |||
| 381 | dev_kfree_skb(skb); | ||
| 382 | return skb2; | ||
| 383 | } | ||
| 384 | |||
| 385 | static void rndis_response_available(void *_rndis) | ||
| 386 | { | ||
| 387 | struct f_rndis *rndis = _rndis; | ||
| 388 | struct usb_request *req = rndis->notify_req; | ||
| 389 | struct usb_composite_dev *cdev = rndis->port.func.config->cdev; | ||
| 390 | __le32 *data = req->buf; | ||
| 391 | int status; | ||
| 392 | |||
| 393 | if (atomic_inc_return(&rndis->notify_count) != 1) | ||
| 394 | return; | ||
| 395 | |||
| 396 | /* Send RNDIS RESPONSE_AVAILABLE notification; a | ||
| 397 | * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too | ||
| 398 | * | ||
| 399 | * This is the only notification defined by RNDIS. | ||
| 400 | */ | ||
| 401 | data[0] = cpu_to_le32(1); | ||
| 402 | data[1] = cpu_to_le32(0); | ||
| 403 | |||
| 404 | status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); | ||
| 405 | if (status) { | ||
| 406 | atomic_dec(&rndis->notify_count); | ||
| 407 | DBG(cdev, "notify/0 --> %d\n", status); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) | ||
| 412 | { | ||
| 413 | struct f_rndis *rndis = req->context; | ||
| 414 | struct usb_composite_dev *cdev = rndis->port.func.config->cdev; | ||
| 415 | int status = req->status; | ||
| 416 | |||
| 417 | /* after TX: | ||
| 418 | * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) | ||
| 419 | * - RNDIS_RESPONSE_AVAILABLE (status/irq) | ||
| 420 | */ | ||
| 421 | switch (status) { | ||
| 422 | case -ECONNRESET: | ||
| 423 | case -ESHUTDOWN: | ||
| 424 | /* connection gone */ | ||
| 425 | atomic_set(&rndis->notify_count, 0); | ||
| 426 | break; | ||
| 427 | default: | ||
| 428 | DBG(cdev, "RNDIS %s response error %d, %d/%d\n", | ||
| 429 | ep->name, status, | ||
| 430 | req->actual, req->length); | ||
| 431 | /* FALLTHROUGH */ | ||
| 432 | case 0: | ||
| 433 | if (ep != rndis->notify) | ||
| 434 | break; | ||
| 435 | |||
| 436 | /* handle multiple pending RNDIS_RESPONSE_AVAILABLE | ||
| 437 | * notifications by resending until we're done | ||
| 438 | */ | ||
| 439 | if (atomic_dec_and_test(&rndis->notify_count)) | ||
| 440 | break; | ||
| 441 | status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); | ||
| 442 | if (status) { | ||
| 443 | atomic_dec(&rndis->notify_count); | ||
| 444 | DBG(cdev, "notify/1 --> %d\n", status); | ||
| 445 | } | ||
| 446 | break; | ||
| 447 | } | ||
| 448 | } | ||
| 449 | |||
| 450 | static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) | ||
| 451 | { | ||
| 452 | struct f_rndis *rndis = req->context; | ||
| 453 | int status; | ||
| 454 | |||
| 455 | /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ | ||
| 456 | // spin_lock(&dev->lock); | ||
| 457 | status = rndis_msg_parser(rndis->config, (u8 *) req->buf); | ||
| 458 | if (status < 0) | ||
| 459 | pr_err("RNDIS command error %d, %d/%d\n", | ||
| 460 | status, req->actual, req->length); | ||
| 461 | // spin_unlock(&dev->lock); | ||
| 462 | } | ||
| 463 | |||
| 464 | static int | ||
| 465 | rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) | ||
| 466 | { | ||
| 467 | struct f_rndis *rndis = func_to_rndis(f); | ||
| 468 | struct usb_composite_dev *cdev = f->config->cdev; | ||
| 469 | struct usb_request *req = cdev->req; | ||
| 470 | int value = -EOPNOTSUPP; | ||
| 471 | u16 w_index = le16_to_cpu(ctrl->wIndex); | ||
| 472 | u16 w_value = le16_to_cpu(ctrl->wValue); | ||
| 473 | u16 w_length = le16_to_cpu(ctrl->wLength); | ||
| 474 | |||
| 475 | /* composite driver infrastructure handles everything except | ||
| 476 | * CDC class messages; interface activation uses set_alt(). | ||
| 477 | */ | ||
| 478 | switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { | ||
| 479 | |||
| 480 | /* RNDIS uses the CDC command encapsulation mechanism to implement | ||
| 481 | * an RPC scheme, with much getting/setting of attributes by OID. | ||
| 482 | */ | ||
| 483 | case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | ||
| 484 | | USB_CDC_SEND_ENCAPSULATED_COMMAND: | ||
| 485 | if (w_value || w_index != rndis->ctrl_id) | ||
| 486 | goto invalid; | ||
| 487 | /* read the request; process it later */ | ||
| 488 | value = w_length; | ||
| 489 | req->complete = rndis_command_complete; | ||
| 490 | req->context = rndis; | ||
| 491 | /* later, rndis_response_available() sends a notification */ | ||
| 492 | break; | ||
| 493 | |||
| 494 | case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | ||
| 495 | | USB_CDC_GET_ENCAPSULATED_RESPONSE: | ||
| 496 | if (w_value || w_index != rndis->ctrl_id) | ||
| 497 | goto invalid; | ||
| 498 | else { | ||
| 499 | u8 *buf; | ||
| 500 | u32 n; | ||
| 501 | |||
| 502 | /* return the result */ | ||
| 503 | buf = rndis_get_next_response(rndis->config, &n); | ||
| 504 | if (buf) { | ||
| 505 | memcpy(req->buf, buf, n); | ||
| 506 | req->complete = rndis_response_complete; | ||
| 507 | req->context = rndis; | ||
| 508 | rndis_free_response(rndis->config, buf); | ||
| 509 | value = n; | ||
| 510 | } | ||
| 511 | /* else stalls ... spec says to avoid that */ | ||
| 512 | } | ||
| 513 | break; | ||
| 514 | |||
| 515 | default: | ||
| 516 | invalid: | ||
| 517 | VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", | ||
| 518 | ctrl->bRequestType, ctrl->bRequest, | ||
| 519 | w_value, w_index, w_length); | ||
| 520 | } | ||
| 521 | |||
| 522 | /* respond with data transfer or status phase? */ | ||
| 523 | if (value >= 0) { | ||
| 524 | DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n", | ||
| 525 | ctrl->bRequestType, ctrl->bRequest, | ||
| 526 | w_value, w_index, w_length); | ||
| 527 | req->zero = (value < w_length); | ||
| 528 | req->length = value; | ||
| 529 | value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); | ||
| 530 | if (value < 0) | ||
| 531 | ERROR(cdev, "rndis response on err %d\n", value); | ||
| 532 | } | ||
| 533 | |||
| 534 | /* device either stalls (value < 0) or reports success */ | ||
| 535 | return value; | ||
| 536 | } | ||
| 537 | |||
| 538 | |||
| 539 | static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) | ||
| 540 | { | ||
| 541 | struct f_rndis *rndis = func_to_rndis(f); | ||
| 542 | struct usb_composite_dev *cdev = f->config->cdev; | ||
| 543 | |||
| 544 | /* we know alt == 0 */ | ||
| 545 | |||
| 546 | if (intf == rndis->ctrl_id) { | ||
| 547 | if (rndis->notify->driver_data) { | ||
| 548 | VDBG(cdev, "reset rndis control %d\n", intf); | ||
| 549 | usb_ep_disable(rndis->notify); | ||
| 550 | } | ||
| 551 | if (!rndis->notify->desc) { | ||
| 552 | VDBG(cdev, "init rndis ctrl %d\n", intf); | ||
| 553 | if (config_ep_by_speed(cdev->gadget, f, rndis->notify)) | ||
| 554 | goto fail; | ||
| 555 | } | ||
| 556 | usb_ep_enable(rndis->notify); | ||
| 557 | rndis->notify->driver_data = rndis; | ||
| 558 | |||
| 559 | } else if (intf == rndis->data_id) { | ||
| 560 | struct net_device *net; | ||
| 561 | |||
| 562 | if (rndis->port.in_ep->driver_data) { | ||
| 563 | DBG(cdev, "reset rndis\n"); | ||
| 564 | gether_disconnect(&rndis->port); | ||
| 565 | } | ||
| 566 | |||
| 567 | if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) { | ||
| 568 | DBG(cdev, "init rndis\n"); | ||
| 569 | if (config_ep_by_speed(cdev->gadget, f, | ||
| 570 | rndis->port.in_ep) || | ||
| 571 | config_ep_by_speed(cdev->gadget, f, | ||
| 572 | rndis->port.out_ep)) { | ||
| 573 | rndis->port.in_ep->desc = NULL; | ||
| 574 | rndis->port.out_ep->desc = NULL; | ||
| 575 | goto fail; | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | /* Avoid ZLPs; they can be troublesome. */ | ||
| 580 | rndis->port.is_zlp_ok = false; | ||
| 581 | |||
| 582 | /* RNDIS should be in the "RNDIS uninitialized" state, | ||
| 583 | * either never activated or after rndis_uninit(). | ||
| 584 | * | ||
| 585 | * We don't want data to flow here until a nonzero packet | ||
| 586 | * filter is set, at which point it enters "RNDIS data | ||
| 587 | * initialized" state ... but we do want the endpoints | ||
| 588 | * to be activated. It's a strange little state. | ||
| 589 | * | ||
| 590 | * REVISIT the RNDIS gadget code has done this wrong for a | ||
| 591 | * very long time. We need another call to the link layer | ||
| 592 | * code -- gether_updown(...bool) maybe -- to do it right. | ||
| 593 | */ | ||
| 594 | rndis->port.cdc_filter = 0; | ||
| 595 | |||
| 596 | DBG(cdev, "RNDIS RX/TX early activation ... \n"); | ||
| 597 | net = gether_connect(&rndis->port); | ||
| 598 | if (IS_ERR(net)) | ||
| 599 | return PTR_ERR(net); | ||
| 600 | |||
| 601 | rndis_set_param_dev(rndis->config, net, | ||
| 602 | &rndis->port.cdc_filter); | ||
| 603 | } else | ||
| 604 | goto fail; | ||
| 605 | |||
| 606 | return 0; | ||
| 607 | fail: | ||
| 608 | return -EINVAL; | ||
| 609 | } | ||
| 610 | |||
| 611 | static void rndis_disable(struct usb_function *f) | ||
| 612 | { | ||
| 613 | struct f_rndis *rndis = func_to_rndis(f); | ||
| 614 | struct usb_composite_dev *cdev = f->config->cdev; | ||
| 615 | |||
| 616 | if (!rndis->notify->driver_data) | ||
| 617 | return; | ||
| 618 | |||
| 619 | DBG(cdev, "rndis deactivated\n"); | ||
| 620 | |||
| 621 | rndis_uninit(rndis->config); | ||
| 622 | gether_disconnect(&rndis->port); | ||
| 623 | |||
| 624 | usb_ep_disable(rndis->notify); | ||
| 625 | rndis->notify->driver_data = NULL; | ||
| 626 | } | ||
| 627 | |||
| 628 | /*-------------------------------------------------------------------------*/ | ||
| 629 | |||
| 630 | /* | ||
| 631 | * This isn't quite the same mechanism as CDC Ethernet, since the | ||
| 632 | * notification scheme passes less data, but the same set of link | ||
| 633 | * states must be tested. A key difference is that altsettings are | ||
| 634 | * not used to tell whether the link should send packets or not. | ||
| 635 | */ | ||
| 636 | |||
| 637 | static void rndis_open(struct gether *geth) | ||
| 638 | { | ||
| 639 | struct f_rndis *rndis = func_to_rndis(&geth->func); | ||
| 640 | struct usb_composite_dev *cdev = geth->func.config->cdev; | ||
| 641 | |||
| 642 | DBG(cdev, "%s\n", __func__); | ||
| 643 | |||
| 644 | rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, | ||
| 645 | bitrate(cdev->gadget) / 100); | ||
| 646 | rndis_signal_connect(rndis->config); | ||
| 647 | } | ||
| 648 | |||
| 649 | static void rndis_close(struct gether *geth) | ||
| 650 | { | ||
| 651 | struct f_rndis *rndis = func_to_rndis(&geth->func); | ||
| 652 | |||
| 653 | DBG(geth->func.config->cdev, "%s\n", __func__); | ||
| 654 | |||
| 655 | rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); | ||
| 656 | rndis_signal_disconnect(rndis->config); | ||
| 657 | } | ||
| 658 | |||
| 659 | /*-------------------------------------------------------------------------*/ | ||
| 660 | |||
| 661 | /* Some controllers can't support RNDIS ... */ | ||
| 662 | static inline bool can_support_rndis(struct usb_configuration *c) | ||
| 663 | { | ||
| 664 | /* everything else is *presumably* fine */ | ||
| 665 | return true; | ||
| 666 | } | ||
| 667 | |||
| 668 | /* ethernet function driver setup/binding */ | ||
| 669 | |||
| 670 | static int | ||
| 671 | rndis_bind(struct usb_configuration *c, struct usb_function *f) | ||
| 672 | { | ||
| 673 | struct usb_composite_dev *cdev = c->cdev; | ||
| 674 | struct f_rndis *rndis = func_to_rndis(f); | ||
| 675 | struct usb_string *us; | ||
| 676 | int status; | ||
| 677 | struct usb_ep *ep; | ||
| 678 | |||
| 679 | struct f_rndis_opts *rndis_opts; | ||
| 680 | |||
| 681 | if (!can_support_rndis(c)) | ||
| 682 | return -EINVAL; | ||
| 683 | |||
| 684 | rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst); | ||
| 685 | |||
| 686 | if (cdev->use_os_string) { | ||
| 687 | f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), | ||
| 688 | GFP_KERNEL); | ||
| 689 | if (!f->os_desc_table) | ||
| 690 | return -ENOMEM; | ||
| 691 | f->os_desc_n = 1; | ||
| 692 | f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc; | ||
| 693 | } | ||
| 694 | |||
| 695 | /* | ||
| 696 | * in drivers/usb/gadget/configfs.c:configfs_composite_bind() | ||
| 697 | * configurations are bound in sequence with list_for_each_entry, | ||
| 698 | * in each configuration its functions are bound in sequence | ||
| 699 | * with list_for_each_entry, so we assume no race condition | ||
| 700 | * with regard to rndis_opts->bound access | ||
| 701 | */ | ||
| 702 | if (!rndis_opts->bound) { | ||
| 703 | gether_set_gadget(rndis_opts->net, cdev->gadget); | ||
| 704 | status = gether_register_netdev(rndis_opts->net); | ||
| 705 | if (status) | ||
| 706 | goto fail; | ||
| 707 | rndis_opts->bound = true; | ||
| 708 | } | ||
| 709 | |||
| 710 | us = usb_gstrings_attach(cdev, rndis_strings, | ||
| 711 | ARRAY_SIZE(rndis_string_defs)); | ||
| 712 | if (IS_ERR(us)) { | ||
| 713 | status = PTR_ERR(us); | ||
| 714 | goto fail; | ||
| 715 | } | ||
| 716 | rndis_control_intf.iInterface = us[0].id; | ||
| 717 | rndis_data_intf.iInterface = us[1].id; | ||
| 718 | rndis_iad_descriptor.iFunction = us[2].id; | ||
| 719 | |||
| 720 | /* allocate instance-specific interface IDs */ | ||
| 721 | status = usb_interface_id(c, f); | ||
| 722 | if (status < 0) | ||
| 723 | goto fail; | ||
| 724 | rndis->ctrl_id = status; | ||
| 725 | rndis_iad_descriptor.bFirstInterface = status; | ||
| 726 | |||
| 727 | rndis_control_intf.bInterfaceNumber = status; | ||
| 728 | rndis_union_desc.bMasterInterface0 = status; | ||
| 729 | |||
| 730 | if (cdev->use_os_string) | ||
| 731 | f->os_desc_table[0].if_id = | ||
| 732 | rndis_iad_descriptor.bFirstInterface; | ||
| 733 | |||
| 734 | status = usb_interface_id(c, f); | ||
| 735 | if (status < 0) | ||
| 736 | goto fail; | ||
| 737 | rndis->data_id = status; | ||
| 738 | |||
| 739 | rndis_data_intf.bInterfaceNumber = status; | ||
| 740 | rndis_union_desc.bSlaveInterface0 = status; | ||
| 741 | |||
| 742 | status = -ENODEV; | ||
| 743 | |||
| 744 | /* allocate instance-specific endpoints */ | ||
| 745 | ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); | ||
| 746 | if (!ep) | ||
| 747 | goto fail; | ||
| 748 | rndis->port.in_ep = ep; | ||
| 749 | ep->driver_data = cdev; /* claim */ | ||
| 750 | |||
| 751 | ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); | ||
| 752 | if (!ep) | ||
| 753 | goto fail; | ||
| 754 | rndis->port.out_ep = ep; | ||
| 755 | ep->driver_data = cdev; /* claim */ | ||
| 756 | |||
| 757 | /* NOTE: a status/notification endpoint is, strictly speaking, | ||
| 758 | * optional. We don't treat it that way though! It's simpler, | ||
| 759 | * and some newer profiles don't treat it as optional. | ||
| 760 | */ | ||
| 761 | ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); | ||
| 762 | if (!ep) | ||
| 763 | goto fail; | ||
| 764 | rndis->notify = ep; | ||
| 765 | ep->driver_data = cdev; /* claim */ | ||
| 766 | |||
| 767 | status = -ENOMEM; | ||
| 768 | |||
| 769 | /* allocate notification request and buffer */ | ||
| 770 | rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); | ||
| 771 | if (!rndis->notify_req) | ||
| 772 | goto fail; | ||
| 773 | rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); | ||
| 774 | if (!rndis->notify_req->buf) | ||
| 775 | goto fail; | ||
| 776 | rndis->notify_req->length = STATUS_BYTECOUNT; | ||
| 777 | rndis->notify_req->context = rndis; | ||
| 778 | rndis->notify_req->complete = rndis_response_complete; | ||
| 779 | |||
| 780 | /* support all relevant hardware speeds... we expect that when | ||
| 781 | * hardware is dual speed, all bulk-capable endpoints work at | ||
| 782 | * both speeds | ||
| 783 | */ | ||
| 784 | hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress; | ||
| 785 | hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress; | ||
| 786 | hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress; | ||
| 787 | |||
| 788 | ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress; | ||
| 789 | ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress; | ||
| 790 | ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress; | ||
| 791 | |||
| 792 | status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function, | ||
| 793 | eth_ss_function); | ||
| 794 | if (status) | ||
| 795 | goto fail; | ||
| 796 | |||
| 797 | rndis->port.open = rndis_open; | ||
| 798 | rndis->port.close = rndis_close; | ||
| 799 | |||
| 800 | rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); | ||
| 801 | rndis_set_host_mac(rndis->config, rndis->ethaddr); | ||
| 802 | |||
| 803 | if (rndis->manufacturer && rndis->vendorID && | ||
| 804 | rndis_set_param_vendor(rndis->config, rndis->vendorID, | ||
| 805 | rndis->manufacturer)) | ||
| 806 | goto fail; | ||
| 807 | |||
| 808 | /* NOTE: all that is done without knowing or caring about | ||
| 809 | * the network link ... which is unavailable to this code | ||
| 810 | * until we're activated via set_alt(). | ||
| 811 | */ | ||
| 812 | |||
| 813 | DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", | ||
| 814 | gadget_is_superspeed(c->cdev->gadget) ? "super" : | ||
| 815 | gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", | ||
| 816 | rndis->port.in_ep->name, rndis->port.out_ep->name, | ||
| 817 | rndis->notify->name); | ||
| 818 | return 0; | ||
| 819 | |||
| 820 | fail: | ||
| 821 | kfree(f->os_desc_table); | ||
| 822 | f->os_desc_n = 0; | ||
| 823 | usb_free_all_descriptors(f); | ||
| 824 | |||
| 825 | if (rndis->notify_req) { | ||
| 826 | kfree(rndis->notify_req->buf); | ||
| 827 | usb_ep_free_request(rndis->notify, rndis->notify_req); | ||
| 828 | } | ||
| 829 | |||
| 830 | /* we might as well release our claims on endpoints */ | ||
| 831 | if (rndis->notify) | ||
| 832 | rndis->notify->driver_data = NULL; | ||
| 833 | if (rndis->port.out_ep) | ||
| 834 | rndis->port.out_ep->driver_data = NULL; | ||
| 835 | if (rndis->port.in_ep) | ||
| 836 | rndis->port.in_ep->driver_data = NULL; | ||
| 837 | |||
| 838 | ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); | ||
| 839 | |||
| 840 | return status; | ||
| 841 | } | ||
| 842 | |||
| 843 | void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) | ||
| 844 | { | ||
| 845 | struct f_rndis_opts *opts; | ||
| 846 | |||
| 847 | opts = container_of(f, struct f_rndis_opts, func_inst); | ||
| 848 | if (opts->bound) | ||
| 849 | gether_cleanup(netdev_priv(opts->net)); | ||
| 850 | else | ||
| 851 | free_netdev(opts->net); | ||
| 852 | opts->borrowed_net = opts->bound = true; | ||
| 853 | opts->net = net; | ||
| 854 | } | ||
| 855 | EXPORT_SYMBOL_GPL(rndis_borrow_net); | ||
| 856 | |||
| 857 | static inline struct f_rndis_opts *to_f_rndis_opts(struct config_item *item) | ||
| 858 | { | ||
| 859 | return container_of(to_config_group(item), struct f_rndis_opts, | ||
| 860 | func_inst.group); | ||
| 861 | } | ||
| 862 | |||
| 863 | /* f_rndis_item_ops */ | ||
| 864 | USB_ETHERNET_CONFIGFS_ITEM(rndis); | ||
| 865 | |||
| 866 | /* f_rndis_opts_dev_addr */ | ||
| 867 | USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(rndis); | ||
| 868 | |||
| 869 | /* f_rndis_opts_host_addr */ | ||
| 870 | USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(rndis); | ||
| 871 | |||
| 872 | /* f_rndis_opts_qmult */ | ||
| 873 | USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis); | ||
| 874 | |||
| 875 | /* f_rndis_opts_ifname */ | ||
| 876 | USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis); | ||
| 877 | |||
| 878 | static struct configfs_attribute *rndis_attrs[] = { | ||
| 879 | &f_rndis_opts_dev_addr.attr, | ||
| 880 | &f_rndis_opts_host_addr.attr, | ||
| 881 | &f_rndis_opts_qmult.attr, | ||
| 882 | &f_rndis_opts_ifname.attr, | ||
| 883 | NULL, | ||
| 884 | }; | ||
| 885 | |||
| 886 | static struct config_item_type rndis_func_type = { | ||
| 887 | .ct_item_ops = &rndis_item_ops, | ||
| 888 | .ct_attrs = rndis_attrs, | ||
| 889 | .ct_owner = THIS_MODULE, | ||
| 890 | }; | ||
| 891 | |||
| 892 | static void rndis_free_inst(struct usb_function_instance *f) | ||
| 893 | { | ||
| 894 | struct f_rndis_opts *opts; | ||
| 895 | |||
| 896 | opts = container_of(f, struct f_rndis_opts, func_inst); | ||
| 897 | if (!opts->borrowed_net) { | ||
| 898 | if (opts->bound) | ||
| 899 | gether_cleanup(netdev_priv(opts->net)); | ||
| 900 | else | ||
| 901 | free_netdev(opts->net); | ||
| 902 | } | ||
| 903 | |||
| 904 | kfree(opts->rndis_os_desc.group.default_groups); /* single VLA chunk */ | ||
| 905 | kfree(opts); | ||
| 906 | } | ||
| 907 | |||
| 908 | static struct usb_function_instance *rndis_alloc_inst(void) | ||
| 909 | { | ||
| 910 | struct f_rndis_opts *opts; | ||
| 911 | struct usb_os_desc *descs[1]; | ||
| 912 | char *names[1]; | ||
| 913 | |||
| 914 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); | ||
| 915 | if (!opts) | ||
| 916 | return ERR_PTR(-ENOMEM); | ||
| 917 | opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id; | ||
| 918 | |||
| 919 | mutex_init(&opts->lock); | ||
| 920 | opts->func_inst.free_func_inst = rndis_free_inst; | ||
| 921 | opts->net = gether_setup_default(); | ||
| 922 | if (IS_ERR(opts->net)) { | ||
| 923 | struct net_device *net = opts->net; | ||
| 924 | kfree(opts); | ||
| 925 | return ERR_CAST(net); | ||
| 926 | } | ||
| 927 | INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop); | ||
| 928 | |||
| 929 | descs[0] = &opts->rndis_os_desc; | ||
| 930 | names[0] = "rndis"; | ||
| 931 | usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, | ||
| 932 | names, THIS_MODULE); | ||
| 933 | config_group_init_type_name(&opts->func_inst.group, "", | ||
| 934 | &rndis_func_type); | ||
| 935 | |||
| 936 | return &opts->func_inst; | ||
| 937 | } | ||
| 938 | |||
| 939 | static void rndis_free(struct usb_function *f) | ||
| 940 | { | ||
| 941 | struct f_rndis *rndis; | ||
| 942 | struct f_rndis_opts *opts; | ||
| 943 | |||
| 944 | rndis = func_to_rndis(f); | ||
| 945 | rndis_deregister(rndis->config); | ||
| 946 | opts = container_of(f->fi, struct f_rndis_opts, func_inst); | ||
| 947 | kfree(rndis); | ||
| 948 | mutex_lock(&opts->lock); | ||
| 949 | opts->refcnt--; | ||
| 950 | mutex_unlock(&opts->lock); | ||
| 951 | } | ||
| 952 | |||
| 953 | static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) | ||
| 954 | { | ||
| 955 | struct f_rndis *rndis = func_to_rndis(f); | ||
| 956 | |||
| 957 | kfree(f->os_desc_table); | ||
| 958 | f->os_desc_n = 0; | ||
| 959 | usb_free_all_descriptors(f); | ||
| 960 | |||
| 961 | kfree(rndis->notify_req->buf); | ||
| 962 | usb_ep_free_request(rndis->notify, rndis->notify_req); | ||
| 963 | } | ||
| 964 | |||
| 965 | static struct usb_function *rndis_alloc(struct usb_function_instance *fi) | ||
| 966 | { | ||
| 967 | struct f_rndis *rndis; | ||
| 968 | struct f_rndis_opts *opts; | ||
| 969 | int status; | ||
| 970 | |||
| 971 | /* allocate and initialize one new instance */ | ||
| 972 | rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); | ||
| 973 | if (!rndis) | ||
| 974 | return ERR_PTR(-ENOMEM); | ||
| 975 | |||
| 976 | opts = container_of(fi, struct f_rndis_opts, func_inst); | ||
| 977 | mutex_lock(&opts->lock); | ||
| 978 | opts->refcnt++; | ||
| 979 | |||
| 980 | gether_get_host_addr_u8(opts->net, rndis->ethaddr); | ||
| 981 | rndis->vendorID = opts->vendor_id; | ||
| 982 | rndis->manufacturer = opts->manufacturer; | ||
| 983 | |||
| 984 | rndis->port.ioport = netdev_priv(opts->net); | ||
| 985 | mutex_unlock(&opts->lock); | ||
| 986 | /* RNDIS activates when the host changes this filter */ | ||
| 987 | rndis->port.cdc_filter = 0; | ||
| 988 | |||
| 989 | /* RNDIS has special (and complex) framing */ | ||
| 990 | rndis->port.header_len = sizeof(struct rndis_packet_msg_type); | ||
| 991 | rndis->port.wrap = rndis_add_header; | ||
| 992 | rndis->port.unwrap = rndis_rm_hdr; | ||
| 993 | |||
| 994 | rndis->port.func.name = "rndis"; | ||
| 995 | /* descriptors are per-instance copies */ | ||
| 996 | rndis->port.func.bind = rndis_bind; | ||
| 997 | rndis->port.func.unbind = rndis_unbind; | ||
| 998 | rndis->port.func.set_alt = rndis_set_alt; | ||
| 999 | rndis->port.func.setup = rndis_setup; | ||
| 1000 | rndis->port.func.disable = rndis_disable; | ||
| 1001 | rndis->port.func.free_func = rndis_free; | ||
| 1002 | |||
| 1003 | status = rndis_register(rndis_response_available, rndis); | ||
| 1004 | if (status < 0) { | ||
| 1005 | kfree(rndis); | ||
| 1006 | return ERR_PTR(status); | ||
| 1007 | } | ||
| 1008 | rndis->config = status; | ||
| 1009 | |||
| 1010 | return &rndis->port.func; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc); | ||
| 1014 | |||
| 1015 | static int __init rndis_mod_init(void) | ||
| 1016 | { | ||
| 1017 | int ret; | ||
| 1018 | |||
| 1019 | ret = rndis_init(); | ||
| 1020 | if (ret) | ||
| 1021 | return ret; | ||
| 1022 | |||
| 1023 | return usb_function_register(&rndisusb_func); | ||
| 1024 | } | ||
| 1025 | module_init(rndis_mod_init); | ||
| 1026 | |||
| 1027 | static void __exit rndis_mod_exit(void) | ||
| 1028 | { | ||
| 1029 | usb_function_unregister(&rndisusb_func); | ||
| 1030 | rndis_exit(); | ||
| 1031 | } | ||
| 1032 | module_exit(rndis_mod_exit); | ||
| 1033 | |||
| 1034 | MODULE_LICENSE("GPL"); | ||
| 1035 | MODULE_AUTHOR("David Brownell"); | ||
