Commit | Line | Data |
---|---|---|
45fe3b8e DB |
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 | |
b97503ff | 7 | * Copyright (C) 2009 Samsung Electronics |
54b8360f | 8 | * Author: Michal Nazarewicz (mina86@mina86.com) |
45fe3b8e DB |
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. | |
45fe3b8e DB |
14 | */ |
15 | ||
16 | /* #define VERBOSE_DEBUG */ | |
17 | ||
5a0e3ad6 | 18 | #include <linux/slab.h> |
45fe3b8e DB |
19 | #include <linux/kernel.h> |
20 | #include <linux/device.h> | |
21 | #include <linux/etherdevice.h> | |
22 | ||
60063497 | 23 | #include <linux/atomic.h> |
45fe3b8e DB |
24 | |
25 | #include "u_ether.h" | |
26 | #include "rndis.h" | |
27 | ||
6fa3eb70 S |
28 | #define F_RNDIS_LOG "USB_RNDIS" |
29 | ||
30 | #define f_rndis_debug 0 | |
45fe3b8e DB |
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 | ||
6fa3eb70 S |
73 | static unsigned int rndis_dl_max_pkt_per_xfer = 3; |
74 | module_param(rndis_dl_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR); | |
75 | MODULE_PARM_DESC(rndis_dl_max_pkt_per_xfer, | |
76 | "Maximum packets per transfer for DL aggregation"); | |
77 | ||
78 | static unsigned int rndis_ul_max_pkt_per_xfer = 1; | |
79 | module_param(rndis_ul_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR); | |
80 | MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer, | |
81 | "Maximum packets per transfer for UL aggregation"); | |
82 | ||
45fe3b8e DB |
83 | struct f_rndis { |
84 | struct gether port; | |
85 | u8 ctrl_id, data_id; | |
86 | u8 ethaddr[ETH_ALEN]; | |
1fbfeff9 BG |
87 | u32 vendorID; |
88 | const char *manufacturer; | |
45fe3b8e DB |
89 | int config; |
90 | ||
45fe3b8e | 91 | struct usb_ep *notify; |
45fe3b8e DB |
92 | struct usb_request *notify_req; |
93 | atomic_t notify_count; | |
94 | }; | |
95 | ||
96 | static inline struct f_rndis *func_to_rndis(struct usb_function *f) | |
97 | { | |
98 | return container_of(f, struct f_rndis, port.func); | |
99 | } | |
100 | ||
101 | /* peak (theoretical) bulk transfer rate in bits-per-second */ | |
102 | static unsigned int bitrate(struct usb_gadget *g) | |
103 | { | |
04617db7 PZ |
104 | if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) |
105 | return 13 * 1024 * 8 * 1000 * 8; | |
106 | else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) | |
45fe3b8e DB |
107 | return 13 * 512 * 8 * 1000 * 8; |
108 | else | |
04617db7 | 109 | return 19 * 64 * 1 * 1000 * 8; |
45fe3b8e DB |
110 | } |
111 | ||
112 | /*-------------------------------------------------------------------------*/ | |
113 | ||
114 | /* | |
115 | */ | |
116 | ||
bcb2f99c | 117 | #define RNDIS_STATUS_INTERVAL_MS 32 |
45fe3b8e DB |
118 | #define STATUS_BYTECOUNT 8 /* 8 bytes data */ |
119 | ||
120 | ||
121 | /* interface descriptor: */ | |
122 | ||
28824b18 | 123 | static struct usb_interface_descriptor rndis_control_intf = { |
45fe3b8e DB |
124 | .bLength = sizeof rndis_control_intf, |
125 | .bDescriptorType = USB_DT_INTERFACE, | |
126 | ||
127 | /* .bInterfaceNumber = DYNAMIC */ | |
128 | /* status endpoint is optional; this could be patched later */ | |
129 | .bNumEndpoints = 1, | |
130 | .bInterfaceClass = USB_CLASS_COMM, | |
131 | .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, | |
132 | .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, | |
133 | /* .iInterface = DYNAMIC */ | |
134 | }; | |
135 | ||
28824b18 | 136 | static struct usb_cdc_header_desc header_desc = { |
45fe3b8e DB |
137 | .bLength = sizeof header_desc, |
138 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
139 | .bDescriptorSubType = USB_CDC_HEADER_TYPE, | |
140 | ||
551509d2 | 141 | .bcdCDC = cpu_to_le16(0x0110), |
45fe3b8e DB |
142 | }; |
143 | ||
28824b18 | 144 | static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { |
45fe3b8e DB |
145 | .bLength = sizeof call_mgmt_descriptor, |
146 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
147 | .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, | |
148 | ||
149 | .bmCapabilities = 0x00, | |
150 | .bDataInterface = 0x01, | |
151 | }; | |
152 | ||
28824b18 | 153 | static struct usb_cdc_acm_descriptor rndis_acm_descriptor = { |
b97503ff | 154 | .bLength = sizeof rndis_acm_descriptor, |
45fe3b8e DB |
155 | .bDescriptorType = USB_DT_CS_INTERFACE, |
156 | .bDescriptorSubType = USB_CDC_ACM_TYPE, | |
157 | ||
158 | .bmCapabilities = 0x00, | |
159 | }; | |
160 | ||
28824b18 | 161 | static struct usb_cdc_union_desc rndis_union_desc = { |
45fe3b8e DB |
162 | .bLength = sizeof(rndis_union_desc), |
163 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
164 | .bDescriptorSubType = USB_CDC_UNION_TYPE, | |
165 | /* .bMasterInterface0 = DYNAMIC */ | |
166 | /* .bSlaveInterface0 = DYNAMIC */ | |
167 | }; | |
168 | ||
169 | /* the data interface has two bulk endpoints */ | |
170 | ||
28824b18 | 171 | static struct usb_interface_descriptor rndis_data_intf = { |
45fe3b8e DB |
172 | .bLength = sizeof rndis_data_intf, |
173 | .bDescriptorType = USB_DT_INTERFACE, | |
174 | ||
175 | /* .bInterfaceNumber = DYNAMIC */ | |
45fe3b8e DB |
176 | .bNumEndpoints = 2, |
177 | .bInterfaceClass = USB_CLASS_CDC_DATA, | |
178 | .bInterfaceSubClass = 0, | |
179 | .bInterfaceProtocol = 0, | |
180 | /* .iInterface = DYNAMIC */ | |
181 | }; | |
182 | ||
b97503ff MN |
183 | |
184 | static struct usb_interface_assoc_descriptor | |
185 | rndis_iad_descriptor = { | |
186 | .bLength = sizeof rndis_iad_descriptor, | |
187 | .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, | |
188 | ||
189 | .bFirstInterface = 0, /* XXX, hardcoded */ | |
190 | .bInterfaceCount = 2, // control + data | |
191 | .bFunctionClass = USB_CLASS_COMM, | |
192 | .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET, | |
193 | .bFunctionProtocol = USB_CDC_PROTO_NONE, | |
194 | /* .iFunction = DYNAMIC */ | |
195 | }; | |
196 | ||
45fe3b8e DB |
197 | /* full speed support: */ |
198 | ||
28824b18 | 199 | static struct usb_endpoint_descriptor fs_notify_desc = { |
45fe3b8e DB |
200 | .bLength = USB_DT_ENDPOINT_SIZE, |
201 | .bDescriptorType = USB_DT_ENDPOINT, | |
202 | ||
203 | .bEndpointAddress = USB_DIR_IN, | |
204 | .bmAttributes = USB_ENDPOINT_XFER_INT, | |
551509d2 | 205 | .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), |
bcb2f99c | 206 | .bInterval = RNDIS_STATUS_INTERVAL_MS, |
45fe3b8e DB |
207 | }; |
208 | ||
28824b18 | 209 | static struct usb_endpoint_descriptor fs_in_desc = { |
45fe3b8e DB |
210 | .bLength = USB_DT_ENDPOINT_SIZE, |
211 | .bDescriptorType = USB_DT_ENDPOINT, | |
212 | ||
213 | .bEndpointAddress = USB_DIR_IN, | |
214 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | |
215 | }; | |
216 | ||
28824b18 | 217 | static struct usb_endpoint_descriptor fs_out_desc = { |
45fe3b8e DB |
218 | .bLength = USB_DT_ENDPOINT_SIZE, |
219 | .bDescriptorType = USB_DT_ENDPOINT, | |
220 | ||
221 | .bEndpointAddress = USB_DIR_OUT, | |
222 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | |
223 | }; | |
224 | ||
28824b18 | 225 | static struct usb_descriptor_header *eth_fs_function[] = { |
b97503ff | 226 | (struct usb_descriptor_header *) &rndis_iad_descriptor, |
04617db7 | 227 | |
45fe3b8e DB |
228 | /* control interface matches ACM, not Ethernet */ |
229 | (struct usb_descriptor_header *) &rndis_control_intf, | |
230 | (struct usb_descriptor_header *) &header_desc, | |
231 | (struct usb_descriptor_header *) &call_mgmt_descriptor, | |
b97503ff | 232 | (struct usb_descriptor_header *) &rndis_acm_descriptor, |
45fe3b8e DB |
233 | (struct usb_descriptor_header *) &rndis_union_desc, |
234 | (struct usb_descriptor_header *) &fs_notify_desc, | |
04617db7 | 235 | |
45fe3b8e DB |
236 | /* data interface has no altsetting */ |
237 | (struct usb_descriptor_header *) &rndis_data_intf, | |
238 | (struct usb_descriptor_header *) &fs_in_desc, | |
239 | (struct usb_descriptor_header *) &fs_out_desc, | |
240 | NULL, | |
241 | }; | |
242 | ||
243 | /* high speed support: */ | |
244 | ||
28824b18 | 245 | static struct usb_endpoint_descriptor hs_notify_desc = { |
45fe3b8e DB |
246 | .bLength = USB_DT_ENDPOINT_SIZE, |
247 | .bDescriptorType = USB_DT_ENDPOINT, | |
248 | ||
249 | .bEndpointAddress = USB_DIR_IN, | |
250 | .bmAttributes = USB_ENDPOINT_XFER_INT, | |
551509d2 | 251 | .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), |
bcb2f99c | 252 | .bInterval = USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS) |
45fe3b8e | 253 | }; |
04617db7 | 254 | |
28824b18 | 255 | static struct usb_endpoint_descriptor hs_in_desc = { |
45fe3b8e DB |
256 | .bLength = USB_DT_ENDPOINT_SIZE, |
257 | .bDescriptorType = USB_DT_ENDPOINT, | |
258 | ||
259 | .bEndpointAddress = USB_DIR_IN, | |
260 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | |
551509d2 | 261 | .wMaxPacketSize = cpu_to_le16(512), |
45fe3b8e DB |
262 | }; |
263 | ||
28824b18 | 264 | static struct usb_endpoint_descriptor hs_out_desc = { |
45fe3b8e DB |
265 | .bLength = USB_DT_ENDPOINT_SIZE, |
266 | .bDescriptorType = USB_DT_ENDPOINT, | |
267 | ||
268 | .bEndpointAddress = USB_DIR_OUT, | |
269 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | |
551509d2 | 270 | .wMaxPacketSize = cpu_to_le16(512), |
45fe3b8e DB |
271 | }; |
272 | ||
28824b18 | 273 | static struct usb_descriptor_header *eth_hs_function[] = { |
b97503ff | 274 | (struct usb_descriptor_header *) &rndis_iad_descriptor, |
04617db7 | 275 | |
45fe3b8e DB |
276 | /* control interface matches ACM, not Ethernet */ |
277 | (struct usb_descriptor_header *) &rndis_control_intf, | |
278 | (struct usb_descriptor_header *) &header_desc, | |
279 | (struct usb_descriptor_header *) &call_mgmt_descriptor, | |
b97503ff | 280 | (struct usb_descriptor_header *) &rndis_acm_descriptor, |
45fe3b8e DB |
281 | (struct usb_descriptor_header *) &rndis_union_desc, |
282 | (struct usb_descriptor_header *) &hs_notify_desc, | |
04617db7 | 283 | |
45fe3b8e DB |
284 | /* data interface has no altsetting */ |
285 | (struct usb_descriptor_header *) &rndis_data_intf, | |
286 | (struct usb_descriptor_header *) &hs_in_desc, | |
287 | (struct usb_descriptor_header *) &hs_out_desc, | |
288 | NULL, | |
289 | }; | |
290 | ||
04617db7 PZ |
291 | /* super speed support: */ |
292 | ||
293 | static struct usb_endpoint_descriptor ss_notify_desc = { | |
294 | .bLength = USB_DT_ENDPOINT_SIZE, | |
295 | .bDescriptorType = USB_DT_ENDPOINT, | |
296 | ||
297 | .bEndpointAddress = USB_DIR_IN, | |
298 | .bmAttributes = USB_ENDPOINT_XFER_INT, | |
299 | .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT), | |
bcb2f99c | 300 | .bInterval = USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS) |
04617db7 PZ |
301 | }; |
302 | ||
303 | static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = { | |
304 | .bLength = sizeof ss_intr_comp_desc, | |
305 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | |
306 | ||
307 | /* the following 3 values can be tweaked if necessary */ | |
308 | /* .bMaxBurst = 0, */ | |
309 | /* .bmAttributes = 0, */ | |
310 | .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT), | |
311 | }; | |
312 | ||
313 | static struct usb_endpoint_descriptor ss_in_desc = { | |
314 | .bLength = USB_DT_ENDPOINT_SIZE, | |
315 | .bDescriptorType = USB_DT_ENDPOINT, | |
316 | ||
317 | .bEndpointAddress = USB_DIR_IN, | |
318 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | |
319 | .wMaxPacketSize = cpu_to_le16(1024), | |
320 | }; | |
321 | ||
322 | static struct usb_endpoint_descriptor ss_out_desc = { | |
323 | .bLength = USB_DT_ENDPOINT_SIZE, | |
324 | .bDescriptorType = USB_DT_ENDPOINT, | |
325 | ||
326 | .bEndpointAddress = USB_DIR_OUT, | |
327 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | |
328 | .wMaxPacketSize = cpu_to_le16(1024), | |
329 | }; | |
330 | ||
331 | static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = { | |
332 | .bLength = sizeof ss_bulk_comp_desc, | |
333 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | |
334 | ||
335 | /* the following 2 values can be tweaked if necessary */ | |
336 | /* .bMaxBurst = 0, */ | |
337 | /* .bmAttributes = 0, */ | |
338 | }; | |
339 | ||
340 | static struct usb_descriptor_header *eth_ss_function[] = { | |
341 | (struct usb_descriptor_header *) &rndis_iad_descriptor, | |
342 | ||
343 | /* control interface matches ACM, not Ethernet */ | |
344 | (struct usb_descriptor_header *) &rndis_control_intf, | |
345 | (struct usb_descriptor_header *) &header_desc, | |
346 | (struct usb_descriptor_header *) &call_mgmt_descriptor, | |
347 | (struct usb_descriptor_header *) &rndis_acm_descriptor, | |
348 | (struct usb_descriptor_header *) &rndis_union_desc, | |
349 | (struct usb_descriptor_header *) &ss_notify_desc, | |
350 | (struct usb_descriptor_header *) &ss_intr_comp_desc, | |
351 | ||
352 | /* data interface has no altsetting */ | |
353 | (struct usb_descriptor_header *) &rndis_data_intf, | |
354 | (struct usb_descriptor_header *) &ss_in_desc, | |
355 | (struct usb_descriptor_header *) &ss_bulk_comp_desc, | |
356 | (struct usb_descriptor_header *) &ss_out_desc, | |
357 | (struct usb_descriptor_header *) &ss_bulk_comp_desc, | |
358 | NULL, | |
359 | }; | |
360 | ||
45fe3b8e DB |
361 | /* string descriptors: */ |
362 | ||
363 | static struct usb_string rndis_string_defs[] = { | |
364 | [0].s = "RNDIS Communications Control", | |
365 | [1].s = "RNDIS Ethernet Data", | |
b97503ff | 366 | [2].s = "RNDIS", |
45fe3b8e DB |
367 | { } /* end of list */ |
368 | }; | |
369 | ||
370 | static struct usb_gadget_strings rndis_string_table = { | |
371 | .language = 0x0409, /* en-us */ | |
372 | .strings = rndis_string_defs, | |
373 | }; | |
374 | ||
375 | static struct usb_gadget_strings *rndis_strings[] = { | |
376 | &rndis_string_table, | |
377 | NULL, | |
378 | }; | |
379 | ||
6fa3eb70 S |
380 | u32 rndis_test_last_resp_id = 0; |
381 | ||
45fe3b8e DB |
382 | /*-------------------------------------------------------------------------*/ |
383 | ||
9b39e9dd BN |
384 | static struct sk_buff *rndis_add_header(struct gether *port, |
385 | struct sk_buff *skb) | |
45fe3b8e | 386 | { |
9b39e9dd | 387 | struct sk_buff *skb2; |
6fa3eb70 S |
388 | struct rndis_packet_msg_type *header = NULL; |
389 | struct f_rndis *rndis = func_to_rndis(&port->func); | |
390 | ||
391 | if (rndis->port.multi_pkt_xfer) { | |
392 | if (port->header) { | |
393 | header = port->header; | |
394 | memset(header, 0, sizeof(*header)); | |
395 | header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET); | |
396 | header->MessageLength = cpu_to_le32(skb->len + | |
397 | sizeof(*header)); | |
398 | header->DataOffset = cpu_to_le32(36); | |
399 | header->DataLength = cpu_to_le32(skb->len); | |
400 | pr_debug("MessageLength:%d DataLength:%d\n", | |
401 | header->MessageLength, | |
402 | header->DataLength); | |
403 | return skb; | |
404 | } else { | |
405 | pr_err("RNDIS header is NULL.\n"); | |
406 | return NULL; | |
407 | } | |
408 | } else { | |
409 | skb2 = skb_realloc_headroom(skb, | |
410 | sizeof(struct rndis_packet_msg_type)); | |
411 | if (skb2) | |
412 | rndis_add_hdr(skb2); | |
413 | ||
414 | dev_kfree_skb(skb); | |
415 | return skb2; | |
416 | } | |
45fe3b8e DB |
417 | } |
418 | ||
419 | static void rndis_response_available(void *_rndis) | |
420 | { | |
421 | struct f_rndis *rndis = _rndis; | |
422 | struct usb_request *req = rndis->notify_req; | |
423 | struct usb_composite_dev *cdev = rndis->port.func.config->cdev; | |
424 | __le32 *data = req->buf; | |
425 | int status; | |
426 | ||
ff349505 | 427 | if (atomic_inc_return(&rndis->notify_count) != 1) |
45fe3b8e DB |
428 | return; |
429 | ||
430 | /* Send RNDIS RESPONSE_AVAILABLE notification; a | |
431 | * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too | |
432 | * | |
433 | * This is the only notification defined by RNDIS. | |
434 | */ | |
435 | data[0] = cpu_to_le32(1); | |
436 | data[1] = cpu_to_le32(0); | |
437 | ||
438 | status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); | |
439 | if (status) { | |
440 | atomic_dec(&rndis->notify_count); | |
441 | DBG(cdev, "notify/0 --> %d\n", status); | |
442 | } | |
443 | } | |
444 | ||
445 | static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req) | |
446 | { | |
447 | struct f_rndis *rndis = req->context; | |
6fa3eb70 | 448 | struct usb_composite_dev *cdev; |
45fe3b8e DB |
449 | int status = req->status; |
450 | ||
6fa3eb70 S |
451 | if (!rndis->port.func.config || !rndis->port.func.config->cdev) |
452 | return; | |
453 | else | |
454 | cdev = rndis->port.func.config->cdev; | |
455 | ||
45fe3b8e DB |
456 | /* after TX: |
457 | * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control) | |
458 | * - RNDIS_RESPONSE_AVAILABLE (status/irq) | |
459 | */ | |
460 | switch (status) { | |
461 | case -ECONNRESET: | |
462 | case -ESHUTDOWN: | |
463 | /* connection gone */ | |
464 | atomic_set(&rndis->notify_count, 0); | |
465 | break; | |
466 | default: | |
467 | DBG(cdev, "RNDIS %s response error %d, %d/%d\n", | |
468 | ep->name, status, | |
469 | req->actual, req->length); | |
470 | /* FALLTHROUGH */ | |
471 | case 0: | |
472 | if (ep != rndis->notify) | |
473 | break; | |
474 | ||
475 | /* handle multiple pending RNDIS_RESPONSE_AVAILABLE | |
476 | * notifications by resending until we're done | |
477 | */ | |
478 | if (atomic_dec_and_test(&rndis->notify_count)) | |
479 | break; | |
480 | status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC); | |
481 | if (status) { | |
482 | atomic_dec(&rndis->notify_count); | |
483 | DBG(cdev, "notify/1 --> %d\n", status); | |
484 | } | |
485 | break; | |
486 | } | |
487 | } | |
488 | ||
489 | static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) | |
490 | { | |
491 | struct f_rndis *rndis = req->context; | |
6fa3eb70 | 492 | struct usb_composite_dev *cdev; |
45fe3b8e | 493 | int status; |
6fa3eb70 S |
494 | rndis_init_msg_type *buf; |
495 | ||
496 | if (!rndis->port.func.config || !rndis->port.func.config->cdev) | |
497 | return; | |
498 | else | |
499 | cdev = rndis->port.func.config->cdev; | |
45fe3b8e DB |
500 | |
501 | /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ | |
502 | // spin_lock(&dev->lock); | |
503 | status = rndis_msg_parser(rndis->config, (u8 *) req->buf); | |
504 | if (status < 0) | |
967baed4 | 505 | pr_err("RNDIS command error %d, %d/%d\n", |
45fe3b8e | 506 | status, req->actual, req->length); |
6fa3eb70 S |
507 | |
508 | buf = (rndis_init_msg_type *)req->buf; | |
509 | ||
510 | if (buf->MessageType == RNDIS_MSG_INIT) { | |
511 | if (buf->MaxTransferSize > 2048){ | |
512 | rndis->port.multi_pkt_xfer = 1; | |
513 | rndis->port.dl_max_transfer_len = buf->MaxTransferSize; | |
514 | }else{ | |
515 | rndis->port.multi_pkt_xfer = 0; | |
516 | } | |
517 | DBG(cdev, "%s: MaxTransferSize: %d : Multi_pkt_txr: %s\n", | |
518 | __func__, buf->MaxTransferSize, | |
519 | rndis->port.multi_pkt_xfer ? "enabled" : | |
520 | "disabled"); | |
521 | if (rndis_dl_max_pkt_per_xfer <= 1) | |
522 | rndis->port.multi_pkt_xfer = 0; | |
523 | } | |
45fe3b8e DB |
524 | // spin_unlock(&dev->lock); |
525 | } | |
526 | ||
527 | static int | |
528 | rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) | |
529 | { | |
530 | struct f_rndis *rndis = func_to_rndis(f); | |
531 | struct usb_composite_dev *cdev = f->config->cdev; | |
532 | struct usb_request *req = cdev->req; | |
533 | int value = -EOPNOTSUPP; | |
534 | u16 w_index = le16_to_cpu(ctrl->wIndex); | |
535 | u16 w_value = le16_to_cpu(ctrl->wValue); | |
536 | u16 w_length = le16_to_cpu(ctrl->wLength); | |
537 | ||
538 | /* composite driver infrastructure handles everything except | |
539 | * CDC class messages; interface activation uses set_alt(). | |
540 | */ | |
6fa3eb70 S |
541 | |
542 | if (f_rndis_debug){ | |
543 | pr_debug("rndis_setup , ctrl->bRequestType is 0x%x, ctrl->bRequest is 0x%x, w_index is 0x%x, w_value is 0x%x\n" , | |
544 | ctrl->bRequestType , ctrl->bRequest, w_index, w_value) ; | |
545 | } | |
546 | ||
45fe3b8e DB |
547 | switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { |
548 | ||
549 | /* RNDIS uses the CDC command encapsulation mechanism to implement | |
550 | * an RPC scheme, with much getting/setting of attributes by OID. | |
551 | */ | |
552 | case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | |
553 | | USB_CDC_SEND_ENCAPSULATED_COMMAND: | |
472b9127 | 554 | if (w_value || w_index != rndis->ctrl_id) |
45fe3b8e DB |
555 | goto invalid; |
556 | /* read the request; process it later */ | |
557 | value = w_length; | |
558 | req->complete = rndis_command_complete; | |
559 | req->context = rndis; | |
560 | /* later, rndis_response_available() sends a notification */ | |
561 | break; | |
562 | ||
563 | case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | |
564 | | USB_CDC_GET_ENCAPSULATED_RESPONSE: | |
565 | if (w_value || w_index != rndis->ctrl_id) | |
566 | goto invalid; | |
567 | else { | |
568 | u8 *buf; | |
569 | u32 n; | |
6fa3eb70 S |
570 | u32 MsgType, MsgLength, MsgID; |
571 | __le32 *tmp; | |
45fe3b8e DB |
572 | |
573 | /* return the result */ | |
574 | buf = rndis_get_next_response(rndis->config, &n); | |
575 | if (buf) { | |
576 | memcpy(req->buf, buf, n); | |
577 | req->complete = rndis_response_complete; | |
f1356172 | 578 | req->context = rndis; |
6fa3eb70 S |
579 | tmp = (__le32 *)buf; |
580 | MsgType = get_unaligned_le32(tmp++); | |
581 | MsgLength = get_unaligned_le32(tmp++); | |
582 | MsgID = get_unaligned_le32(tmp++); | |
583 | ||
45fe3b8e DB |
584 | rndis_free_response(rndis->config, buf); |
585 | value = n; | |
6fa3eb70 S |
586 | |
587 | if (f_rndis_debug){ | |
588 | pr_debug("rndis_setup, rndis response MsgLength %d, msg type is 0x%x, RequestID is 0x%x\n", MsgLength, MsgType, MsgID); | |
589 | } | |
590 | rndis_test_last_resp_id = MsgID ; | |
45fe3b8e DB |
591 | } |
592 | /* else stalls ... spec says to avoid that */ | |
593 | } | |
594 | break; | |
595 | ||
596 | default: | |
597 | invalid: | |
6fa3eb70 S |
598 | pr_err("invalid control req%02x.%02x v%04x i%04x l%d\n", |
599 | ctrl->bRequestType, ctrl->bRequest, | |
600 | w_value, w_index, w_length); | |
601 | ||
45fe3b8e DB |
602 | VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", |
603 | ctrl->bRequestType, ctrl->bRequest, | |
604 | w_value, w_index, w_length); | |
605 | } | |
606 | ||
607 | /* respond with data transfer or status phase? */ | |
608 | if (value >= 0) { | |
609 | DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n", | |
610 | ctrl->bRequestType, ctrl->bRequest, | |
611 | w_value, w_index, w_length); | |
090b9011 | 612 | req->zero = (value < w_length); |
45fe3b8e DB |
613 | req->length = value; |
614 | value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); | |
615 | if (value < 0) | |
616 | ERROR(cdev, "rndis response on err %d\n", value); | |
617 | } | |
618 | ||
619 | /* device either stalls (value < 0) or reports success */ | |
620 | return value; | |
621 | } | |
622 | ||
623 | ||
624 | static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) | |
625 | { | |
626 | struct f_rndis *rndis = func_to_rndis(f); | |
627 | struct usb_composite_dev *cdev = f->config->cdev; | |
628 | ||
629 | /* we know alt == 0 */ | |
6fa3eb70 S |
630 | printk( F_RNDIS_LOG "rndis_set_alt - interface : %d, rndis ctrl id : %d, rndis data id : %d\n" , |
631 | intf, rndis->ctrl_id, rndis->data_id); | |
45fe3b8e DB |
632 | |
633 | if (intf == rndis->ctrl_id) { | |
634 | if (rndis->notify->driver_data) { | |
635 | VDBG(cdev, "reset rndis control %d\n", intf); | |
6fa3eb70 | 636 | printk( F_RNDIS_LOG "reset rndis control %d\n", intf); |
45fe3b8e | 637 | usb_ep_disable(rndis->notify); |
ea2a1df7 TB |
638 | } |
639 | if (!rndis->notify->desc) { | |
45fe3b8e | 640 | VDBG(cdev, "init rndis ctrl %d\n", intf); |
6fa3eb70 | 641 | printk( F_RNDIS_LOG "init rndis ctrl %d\n", intf); |
ea2a1df7 TB |
642 | if (config_ep_by_speed(cdev->gadget, f, rndis->notify)) |
643 | goto fail; | |
45fe3b8e | 644 | } |
72c973dd | 645 | usb_ep_enable(rndis->notify); |
45fe3b8e DB |
646 | rndis->notify->driver_data = rndis; |
647 | ||
648 | } else if (intf == rndis->data_id) { | |
649 | struct net_device *net; | |
650 | ||
651 | if (rndis->port.in_ep->driver_data) { | |
652 | DBG(cdev, "reset rndis\n"); | |
6fa3eb70 | 653 | printk( F_RNDIS_LOG "reset rndis\n"); |
45fe3b8e | 654 | gether_disconnect(&rndis->port); |
830d1b18 MM |
655 | } |
656 | ||
ea2a1df7 | 657 | if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) { |
45fe3b8e | 658 | DBG(cdev, "init rndis\n"); |
6fa3eb70 | 659 | printk( F_RNDIS_LOG "init rndis\n"); |
ea2a1df7 TB |
660 | if (config_ep_by_speed(cdev->gadget, f, |
661 | rndis->port.in_ep) || | |
662 | config_ep_by_speed(cdev->gadget, f, | |
663 | rndis->port.out_ep)) { | |
664 | rndis->port.in_ep->desc = NULL; | |
665 | rndis->port.out_ep->desc = NULL; | |
666 | goto fail; | |
667 | } | |
45fe3b8e DB |
668 | } |
669 | ||
670 | /* Avoid ZLPs; they can be troublesome. */ | |
671 | rndis->port.is_zlp_ok = false; | |
672 | ||
673 | /* RNDIS should be in the "RNDIS uninitialized" state, | |
674 | * either never activated or after rndis_uninit(). | |
675 | * | |
676 | * We don't want data to flow here until a nonzero packet | |
677 | * filter is set, at which point it enters "RNDIS data | |
678 | * initialized" state ... but we do want the endpoints | |
679 | * to be activated. It's a strange little state. | |
680 | * | |
681 | * REVISIT the RNDIS gadget code has done this wrong for a | |
682 | * very long time. We need another call to the link layer | |
683 | * code -- gether_updown(...bool) maybe -- to do it right. | |
684 | */ | |
685 | rndis->port.cdc_filter = 0; | |
686 | ||
687 | DBG(cdev, "RNDIS RX/TX early activation ... \n"); | |
688 | net = gether_connect(&rndis->port); | |
689 | if (IS_ERR(net)) | |
690 | return PTR_ERR(net); | |
691 | ||
692 | rndis_set_param_dev(rndis->config, net, | |
693 | &rndis->port.cdc_filter); | |
694 | } else | |
695 | goto fail; | |
696 | ||
697 | return 0; | |
698 | fail: | |
699 | return -EINVAL; | |
700 | } | |
701 | ||
702 | static void rndis_disable(struct usb_function *f) | |
703 | { | |
704 | struct f_rndis *rndis = func_to_rndis(f); | |
705 | struct usb_composite_dev *cdev = f->config->cdev; | |
706 | ||
707 | if (!rndis->notify->driver_data) | |
708 | return; | |
709 | ||
710 | DBG(cdev, "rndis deactivated\n"); | |
6fa3eb70 | 711 | printk( F_RNDIS_LOG "rndis deactivated\n"); |
45fe3b8e DB |
712 | |
713 | rndis_uninit(rndis->config); | |
714 | gether_disconnect(&rndis->port); | |
715 | ||
716 | usb_ep_disable(rndis->notify); | |
717 | rndis->notify->driver_data = NULL; | |
718 | } | |
719 | ||
720 | /*-------------------------------------------------------------------------*/ | |
721 | ||
722 | /* | |
723 | * This isn't quite the same mechanism as CDC Ethernet, since the | |
724 | * notification scheme passes less data, but the same set of link | |
725 | * states must be tested. A key difference is that altsettings are | |
726 | * not used to tell whether the link should send packets or not. | |
727 | */ | |
728 | ||
729 | static void rndis_open(struct gether *geth) | |
730 | { | |
731 | struct f_rndis *rndis = func_to_rndis(&geth->func); | |
732 | struct usb_composite_dev *cdev = geth->func.config->cdev; | |
733 | ||
734 | DBG(cdev, "%s\n", __func__); | |
6fa3eb70 | 735 | printk( F_RNDIS_LOG "%s\n", __func__); |
45fe3b8e | 736 | |
17c51b6c | 737 | rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, |
45fe3b8e DB |
738 | bitrate(cdev->gadget) / 100); |
739 | rndis_signal_connect(rndis->config); | |
740 | } | |
741 | ||
742 | static void rndis_close(struct gether *geth) | |
743 | { | |
744 | struct f_rndis *rndis = func_to_rndis(&geth->func); | |
745 | ||
746 | DBG(geth->func.config->cdev, "%s\n", __func__); | |
6fa3eb70 | 747 | printk( F_RNDIS_LOG "%s\n", __func__); |
45fe3b8e | 748 | |
17c51b6c | 749 | rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); |
45fe3b8e DB |
750 | rndis_signal_disconnect(rndis->config); |
751 | } | |
752 | ||
753 | /*-------------------------------------------------------------------------*/ | |
754 | ||
755 | /* ethernet function driver setup/binding */ | |
756 | ||
28824b18 | 757 | static int |
45fe3b8e DB |
758 | rndis_bind(struct usb_configuration *c, struct usb_function *f) |
759 | { | |
760 | struct usb_composite_dev *cdev = c->cdev; | |
761 | struct f_rndis *rndis = func_to_rndis(f); | |
762 | int status; | |
763 | struct usb_ep *ep; | |
764 | ||
6fa3eb70 S |
765 | printk( F_RNDIS_LOG \ |
766 | "%s: rndis_bind begin \n", \ | |
767 | __func__); | |
768 | ||
45fe3b8e DB |
769 | /* allocate instance-specific interface IDs */ |
770 | status = usb_interface_id(c, f); | |
771 | if (status < 0) | |
772 | goto fail; | |
773 | rndis->ctrl_id = status; | |
b97503ff | 774 | rndis_iad_descriptor.bFirstInterface = status; |
45fe3b8e DB |
775 | |
776 | rndis_control_intf.bInterfaceNumber = status; | |
777 | rndis_union_desc.bMasterInterface0 = status; | |
778 | ||
779 | status = usb_interface_id(c, f); | |
780 | if (status < 0) | |
781 | goto fail; | |
782 | rndis->data_id = status; | |
783 | ||
784 | rndis_data_intf.bInterfaceNumber = status; | |
785 | rndis_union_desc.bSlaveInterface0 = status; | |
786 | ||
787 | status = -ENODEV; | |
788 | ||
789 | /* allocate instance-specific endpoints */ | |
790 | ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); | |
791 | if (!ep) | |
792 | goto fail; | |
793 | rndis->port.in_ep = ep; | |
794 | ep->driver_data = cdev; /* claim */ | |
795 | ||
796 | ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); | |
797 | if (!ep) | |
798 | goto fail; | |
799 | rndis->port.out_ep = ep; | |
800 | ep->driver_data = cdev; /* claim */ | |
801 | ||
802 | /* NOTE: a status/notification endpoint is, strictly speaking, | |
803 | * optional. We don't treat it that way though! It's simpler, | |
804 | * and some newer profiles don't treat it as optional. | |
805 | */ | |
806 | ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); | |
807 | if (!ep) | |
808 | goto fail; | |
809 | rndis->notify = ep; | |
810 | ep->driver_data = cdev; /* claim */ | |
811 | ||
812 | status = -ENOMEM; | |
813 | ||
814 | /* allocate notification request and buffer */ | |
815 | rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); | |
816 | if (!rndis->notify_req) | |
817 | goto fail; | |
818 | rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); | |
819 | if (!rndis->notify_req->buf) | |
820 | goto fail; | |
821 | rndis->notify_req->length = STATUS_BYTECOUNT; | |
822 | rndis->notify_req->context = rndis; | |
823 | rndis->notify_req->complete = rndis_response_complete; | |
824 | ||
45fe3b8e DB |
825 | /* support all relevant hardware speeds... we expect that when |
826 | * hardware is dual speed, all bulk-capable endpoints work at | |
827 | * both speeds | |
828 | */ | |
10287bae SAS |
829 | hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress; |
830 | hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress; | |
831 | hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress; | |
45fe3b8e | 832 | |
10287bae SAS |
833 | ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress; |
834 | ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress; | |
835 | ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress; | |
836 | ||
837 | status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function, | |
838 | eth_ss_function); | |
839 | if (status) | |
840 | goto fail; | |
04617db7 | 841 | |
45fe3b8e DB |
842 | rndis->port.open = rndis_open; |
843 | rndis->port.close = rndis_close; | |
844 | ||
845 | status = rndis_register(rndis_response_available, rndis); | |
846 | if (status < 0) | |
847 | goto fail; | |
848 | rndis->config = status; | |
849 | ||
17c51b6c | 850 | rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); |
45fe3b8e | 851 | rndis_set_host_mac(rndis->config, rndis->ethaddr); |
6fa3eb70 | 852 | rndis_set_max_pkt_xfer(rndis->config, rndis_ul_max_pkt_per_xfer); |
45fe3b8e | 853 | |
1fbfeff9 BG |
854 | if (rndis->manufacturer && rndis->vendorID && |
855 | rndis_set_param_vendor(rndis->config, rndis->vendorID, | |
856 | rndis->manufacturer)) | |
857 | goto fail; | |
45fe3b8e DB |
858 | |
859 | /* NOTE: all that is done without knowing or caring about | |
860 | * the network link ... which is unavailable to this code | |
861 | * until we're activated via set_alt(). | |
862 | */ | |
863 | ||
6fa3eb70 S |
864 | printk( F_RNDIS_LOG |
865 | "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", | |
866 | gadget_is_superspeed(c->cdev->gadget) ? "super" : | |
867 | gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", | |
868 | rndis->port.in_ep->name, rndis->port.out_ep->name, | |
869 | rndis->notify->name); | |
870 | ||
45fe3b8e | 871 | DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n", |
04617db7 | 872 | gadget_is_superspeed(c->cdev->gadget) ? "super" : |
45fe3b8e DB |
873 | gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", |
874 | rndis->port.in_ep->name, rndis->port.out_ep->name, | |
875 | rndis->notify->name); | |
876 | return 0; | |
877 | ||
878 | fail: | |
10287bae | 879 | usb_free_all_descriptors(f); |
45fe3b8e DB |
880 | |
881 | if (rndis->notify_req) { | |
882 | kfree(rndis->notify_req->buf); | |
883 | usb_ep_free_request(rndis->notify, rndis->notify_req); | |
884 | } | |
885 | ||
886 | /* we might as well release our claims on endpoints */ | |
887 | if (rndis->notify) | |
888 | rndis->notify->driver_data = NULL; | |
e79cc615 | 889 | if (rndis->port.out_ep) |
45fe3b8e | 890 | rndis->port.out_ep->driver_data = NULL; |
e79cc615 | 891 | if (rndis->port.in_ep) |
45fe3b8e DB |
892 | rndis->port.in_ep->driver_data = NULL; |
893 | ||
894 | ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); | |
895 | ||
896 | return status; | |
897 | } | |
898 | ||
899 | static void | |
900 | rndis_unbind(struct usb_configuration *c, struct usb_function *f) | |
901 | { | |
902 | struct f_rndis *rndis = func_to_rndis(f); | |
903 | ||
6fa3eb70 S |
904 | printk( F_RNDIS_LOG \ |
905 | "%s: rndis_unbind \n", \ | |
906 | __func__); | |
907 | ||
908 | ||
45fe3b8e DB |
909 | rndis_deregister(rndis->config); |
910 | rndis_exit(); | |
911 | ||
1616e99d | 912 | rndis_string_defs[0].id = 0; |
10287bae | 913 | usb_free_all_descriptors(f); |
45fe3b8e DB |
914 | |
915 | kfree(rndis->notify_req->buf); | |
916 | usb_ep_free_request(rndis->notify, rndis->notify_req); | |
917 | ||
918 | kfree(rndis); | |
919 | } | |
920 | ||
921 | /* Some controllers can't support RNDIS ... */ | |
922 | static inline bool can_support_rndis(struct usb_configuration *c) | |
923 | { | |
45fe3b8e DB |
924 | /* everything else is *presumably* fine */ |
925 | return true; | |
926 | } | |
927 | ||
28824b18 | 928 | int |
1fbfeff9 | 929 | rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], |
d6a01439 | 930 | u32 vendorID, const char *manufacturer, struct eth_dev *dev) |
45fe3b8e DB |
931 | { |
932 | struct f_rndis *rndis; | |
933 | int status; | |
934 | ||
935 | if (!can_support_rndis(c) || !ethaddr) | |
936 | return -EINVAL; | |
937 | ||
6fa3eb70 S |
938 | /* setup RNDIS itself */ |
939 | status = rndis_init(); | |
940 | if (status < 0) | |
941 | return status; | |
45fe3b8e | 942 | |
6fa3eb70 | 943 | if (rndis_string_defs[0].id == 0) { |
1616e99d SAS |
944 | status = usb_string_ids_tab(c->cdev, rndis_string_defs); |
945 | if (status) | |
45fe3b8e | 946 | return status; |
45fe3b8e | 947 | |
1616e99d SAS |
948 | rndis_control_intf.iInterface = rndis_string_defs[0].id; |
949 | rndis_data_intf.iInterface = rndis_string_defs[1].id; | |
950 | rndis_iad_descriptor.iFunction = rndis_string_defs[2].id; | |
45fe3b8e DB |
951 | } |
952 | ||
953 | /* allocate and initialize one new instance */ | |
954 | status = -ENOMEM; | |
955 | rndis = kzalloc(sizeof *rndis, GFP_KERNEL); | |
956 | if (!rndis) | |
957 | goto fail; | |
958 | ||
959 | memcpy(rndis->ethaddr, ethaddr, ETH_ALEN); | |
1fbfeff9 BG |
960 | rndis->vendorID = vendorID; |
961 | rndis->manufacturer = manufacturer; | |
45fe3b8e | 962 | |
d6a01439 | 963 | rndis->port.ioport = dev; |
45fe3b8e DB |
964 | /* RNDIS activates when the host changes this filter */ |
965 | rndis->port.cdc_filter = 0; | |
966 | ||
967 | /* RNDIS has special (and complex) framing */ | |
968 | rndis->port.header_len = sizeof(struct rndis_packet_msg_type); | |
969 | rndis->port.wrap = rndis_add_header; | |
970 | rndis->port.unwrap = rndis_rm_hdr; | |
6fa3eb70 S |
971 | rndis->port.ul_max_pkts_per_xfer = rndis_ul_max_pkt_per_xfer; |
972 | rndis->port.dl_max_pkts_per_xfer = rndis_dl_max_pkt_per_xfer; | |
45fe3b8e DB |
973 | |
974 | rndis->port.func.name = "rndis"; | |
975 | rndis->port.func.strings = rndis_strings; | |
976 | /* descriptors are per-instance copies */ | |
977 | rndis->port.func.bind = rndis_bind; | |
978 | rndis->port.func.unbind = rndis_unbind; | |
979 | rndis->port.func.set_alt = rndis_set_alt; | |
980 | rndis->port.func.setup = rndis_setup; | |
981 | rndis->port.func.disable = rndis_disable; | |
982 | ||
983 | status = usb_add_function(c, &rndis->port.func); | |
984 | if (status) { | |
985 | kfree(rndis); | |
986 | fail: | |
987 | rndis_exit(); | |
988 | } | |
6fa3eb70 S |
989 | |
990 | printk( F_RNDIS_LOG \ | |
991 | "%s: rndis_bind_config_vendor done, status is %d \n", \ | |
992 | __func__, status); | |
45fe3b8e DB |
993 | return status; |
994 | } |