diff options
author | Rémi Denis-Courmont <remi.denis-courmont@nokia.com> | 2008-12-17 18:49:09 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-12-17 18:49:09 -0500 |
commit | 9732d523212060c44a4723332bdc5ee429eeabc3 (patch) | |
tree | d549b68cf1f84d3bbeed2301b64aa8b93fb4c94e /drivers/usb | |
parent | 893873f3965ae0173a9315534bec7628162d2243 (diff) |
Phonet: USB CDC Phonet function for gadget framework
This implements the Nokia vendor-specific communication device class
function to exchange Phonet messages over USB. This function is already
found in the "PC suite" USB profile of (non-Linux) Nokia handsets.
Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/f_phonet.c | 621 | ||||
-rw-r--r-- | drivers/usb/gadget/u_phonet.h | 21 |
2 files changed, 642 insertions, 0 deletions
diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c new file mode 100644 index 000000000000..d8fc9b32fe36 --- /dev/null +++ b/drivers/usb/gadget/f_phonet.c | |||
@@ -0,0 +1,621 @@ | |||
1 | /* | ||
2 | * f_phonet.c -- USB CDC Phonet function | ||
3 | * | ||
4 | * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved. | ||
5 | * | ||
6 | * Author: Rémi Denis-Courmont | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20 | * 02110-1301 USA | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/device.h> | ||
25 | |||
26 | #include <linux/netdevice.h> | ||
27 | #include <linux/if_ether.h> | ||
28 | #include <linux/if_phonet.h> | ||
29 | #include <linux/if_arp.h> | ||
30 | |||
31 | #include <linux/usb/ch9.h> | ||
32 | #include <linux/usb/cdc.h> | ||
33 | #include <linux/usb/composite.h> | ||
34 | |||
35 | #include "u_phonet.h" | ||
36 | |||
37 | #define PN_MEDIA_USB 0x1B | ||
38 | |||
39 | /*-------------------------------------------------------------------------*/ | ||
40 | |||
41 | struct phonet_port { | ||
42 | struct f_phonet *usb; | ||
43 | spinlock_t lock; | ||
44 | }; | ||
45 | |||
46 | struct f_phonet { | ||
47 | struct usb_function function; | ||
48 | struct net_device *dev; | ||
49 | struct usb_ep *in_ep, *out_ep; | ||
50 | |||
51 | struct usb_request *in_req; | ||
52 | struct usb_request *out_reqv[0]; | ||
53 | }; | ||
54 | |||
55 | static int phonet_rxq_size = 2; | ||
56 | |||
57 | static inline struct f_phonet *func_to_pn(struct usb_function *f) | ||
58 | { | ||
59 | return container_of(f, struct f_phonet, function); | ||
60 | } | ||
61 | |||
62 | /*-------------------------------------------------------------------------*/ | ||
63 | |||
64 | #define USB_CDC_SUBCLASS_PHONET 0xfe | ||
65 | #define USB_CDC_PHONET_TYPE 0xab | ||
66 | |||
67 | static struct usb_interface_descriptor | ||
68 | pn_control_intf_desc = { | ||
69 | .bLength = sizeof pn_control_intf_desc, | ||
70 | .bDescriptorType = USB_DT_INTERFACE, | ||
71 | |||
72 | /* .bInterfaceNumber = DYNAMIC, */ | ||
73 | .bInterfaceClass = USB_CLASS_COMM, | ||
74 | .bInterfaceSubClass = USB_CDC_SUBCLASS_PHONET, | ||
75 | }; | ||
76 | |||
77 | static const struct usb_cdc_header_desc | ||
78 | pn_header_desc = { | ||
79 | .bLength = sizeof pn_header_desc, | ||
80 | .bDescriptorType = USB_DT_CS_INTERFACE, | ||
81 | .bDescriptorSubType = USB_CDC_HEADER_TYPE, | ||
82 | .bcdCDC = __constant_cpu_to_le16(0x0110), | ||
83 | }; | ||
84 | |||
85 | static const struct usb_cdc_header_desc | ||
86 | pn_phonet_desc = { | ||
87 | .bLength = sizeof pn_phonet_desc, | ||
88 | .bDescriptorType = USB_DT_CS_INTERFACE, | ||
89 | .bDescriptorSubType = USB_CDC_PHONET_TYPE, | ||
90 | .bcdCDC = __constant_cpu_to_le16(0x1505), /* ??? */ | ||
91 | }; | ||
92 | |||
93 | static struct usb_cdc_union_desc | ||
94 | pn_union_desc = { | ||
95 | .bLength = sizeof pn_union_desc, | ||
96 | .bDescriptorType = USB_DT_CS_INTERFACE, | ||
97 | .bDescriptorSubType = USB_CDC_UNION_TYPE, | ||
98 | |||
99 | /* .bMasterInterface0 = DYNAMIC, */ | ||
100 | /* .bSlaveInterface0 = DYNAMIC, */ | ||
101 | }; | ||
102 | |||
103 | static struct usb_interface_descriptor | ||
104 | pn_data_nop_intf_desc = { | ||
105 | .bLength = sizeof pn_data_nop_intf_desc, | ||
106 | .bDescriptorType = USB_DT_INTERFACE, | ||
107 | |||
108 | /* .bInterfaceNumber = DYNAMIC, */ | ||
109 | .bAlternateSetting = 0, | ||
110 | .bNumEndpoints = 0, | ||
111 | .bInterfaceClass = USB_CLASS_CDC_DATA, | ||
112 | }; | ||
113 | |||
114 | static struct usb_interface_descriptor | ||
115 | pn_data_intf_desc = { | ||
116 | .bLength = sizeof pn_data_intf_desc, | ||
117 | .bDescriptorType = USB_DT_INTERFACE, | ||
118 | |||
119 | /* .bInterfaceNumber = DYNAMIC, */ | ||
120 | .bAlternateSetting = 1, | ||
121 | .bNumEndpoints = 2, | ||
122 | .bInterfaceClass = USB_CLASS_CDC_DATA, | ||
123 | }; | ||
124 | |||
125 | static struct usb_endpoint_descriptor | ||
126 | pn_fs_sink_desc = { | ||
127 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
128 | .bDescriptorType = USB_DT_ENDPOINT, | ||
129 | |||
130 | .bEndpointAddress = USB_DIR_OUT, | ||
131 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
132 | }; | ||
133 | |||
134 | static struct usb_endpoint_descriptor | ||
135 | pn_hs_sink_desc = { | ||
136 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
137 | .bDescriptorType = USB_DT_ENDPOINT, | ||
138 | |||
139 | .bEndpointAddress = USB_DIR_OUT, | ||
140 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
141 | .wMaxPacketSize = __constant_cpu_to_le16(512), | ||
142 | }; | ||
143 | |||
144 | static struct usb_endpoint_descriptor | ||
145 | pn_fs_source_desc = { | ||
146 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
147 | .bDescriptorType = USB_DT_ENDPOINT, | ||
148 | |||
149 | .bEndpointAddress = USB_DIR_IN, | ||
150 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
151 | }; | ||
152 | |||
153 | static struct usb_endpoint_descriptor | ||
154 | pn_hs_source_desc = { | ||
155 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
156 | .bDescriptorType = USB_DT_ENDPOINT, | ||
157 | |||
158 | .bEndpointAddress = USB_DIR_IN, | ||
159 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
160 | .wMaxPacketSize = __constant_cpu_to_le16(512), | ||
161 | }; | ||
162 | |||
163 | static struct usb_descriptor_header *fs_pn_function[] = { | ||
164 | (struct usb_descriptor_header *) &pn_control_intf_desc, | ||
165 | (struct usb_descriptor_header *) &pn_header_desc, | ||
166 | (struct usb_descriptor_header *) &pn_phonet_desc, | ||
167 | (struct usb_descriptor_header *) &pn_union_desc, | ||
168 | (struct usb_descriptor_header *) &pn_data_nop_intf_desc, | ||
169 | (struct usb_descriptor_header *) &pn_data_intf_desc, | ||
170 | (struct usb_descriptor_header *) &pn_fs_sink_desc, | ||
171 | (struct usb_descriptor_header *) &pn_fs_source_desc, | ||
172 | NULL, | ||
173 | }; | ||
174 | |||
175 | static struct usb_descriptor_header *hs_pn_function[] = { | ||
176 | (struct usb_descriptor_header *) &pn_control_intf_desc, | ||
177 | (struct usb_descriptor_header *) &pn_header_desc, | ||
178 | (struct usb_descriptor_header *) &pn_phonet_desc, | ||
179 | (struct usb_descriptor_header *) &pn_union_desc, | ||
180 | (struct usb_descriptor_header *) &pn_data_nop_intf_desc, | ||
181 | (struct usb_descriptor_header *) &pn_data_intf_desc, | ||
182 | (struct usb_descriptor_header *) &pn_hs_sink_desc, | ||
183 | (struct usb_descriptor_header *) &pn_hs_source_desc, | ||
184 | NULL, | ||
185 | }; | ||
186 | |||
187 | /*-------------------------------------------------------------------------*/ | ||
188 | |||
189 | static int pn_net_open(struct net_device *dev) | ||
190 | { | ||
191 | if (netif_carrier_ok(dev)) | ||
192 | netif_wake_queue(dev); | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int pn_net_close(struct net_device *dev) | ||
197 | { | ||
198 | netif_stop_queue(dev); | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static void pn_tx_complete(struct usb_ep *ep, struct usb_request *req) | ||
203 | { | ||
204 | struct f_phonet *fp = ep->driver_data; | ||
205 | struct net_device *dev = fp->dev; | ||
206 | struct sk_buff *skb = req->context; | ||
207 | |||
208 | switch (req->status) { | ||
209 | case 0: | ||
210 | dev->stats.tx_packets++; | ||
211 | dev->stats.tx_bytes += skb->len; | ||
212 | break; | ||
213 | |||
214 | case -ESHUTDOWN: /* disconnected */ | ||
215 | case -ECONNRESET: /* disabled */ | ||
216 | dev->stats.tx_aborted_errors++; | ||
217 | default: | ||
218 | dev->stats.tx_errors++; | ||
219 | } | ||
220 | |||
221 | dev_kfree_skb_any(skb); | ||
222 | if (netif_carrier_ok(dev)) | ||
223 | netif_wake_queue(dev); | ||
224 | } | ||
225 | |||
226 | static int pn_net_xmit(struct sk_buff *skb, struct net_device *dev) | ||
227 | { | ||
228 | struct phonet_port *port = netdev_priv(dev); | ||
229 | struct f_phonet *fp; | ||
230 | struct usb_request *req; | ||
231 | unsigned long flags; | ||
232 | |||
233 | if (skb->protocol != htons(ETH_P_PHONET)) | ||
234 | goto out; | ||
235 | |||
236 | spin_lock_irqsave(&port->lock, flags); | ||
237 | fp = port->usb; | ||
238 | if (unlikely(!fp)) /* race with carrier loss */ | ||
239 | goto out_unlock; | ||
240 | |||
241 | req = fp->in_req; | ||
242 | req->buf = skb->data; | ||
243 | req->length = skb->len; | ||
244 | req->complete = pn_tx_complete; | ||
245 | req->zero = 1; | ||
246 | req->context = skb; | ||
247 | |||
248 | if (unlikely(usb_ep_queue(fp->in_ep, req, GFP_ATOMIC))) | ||
249 | goto out_unlock; | ||
250 | |||
251 | netif_stop_queue(dev); | ||
252 | skb = NULL; | ||
253 | |||
254 | out_unlock: | ||
255 | spin_unlock_irqrestore(&port->lock, flags); | ||
256 | out: | ||
257 | if (unlikely(skb)) { | ||
258 | dev_kfree_skb_any(skb); | ||
259 | dev->stats.tx_dropped++; | ||
260 | } | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int pn_net_mtu(struct net_device *dev, int new_mtu) | ||
265 | { | ||
266 | struct phonet_port *port = netdev_priv(dev); | ||
267 | unsigned long flags; | ||
268 | int err = -EBUSY; | ||
269 | |||
270 | if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU)) | ||
271 | return -EINVAL; | ||
272 | |||
273 | spin_lock_irqsave(&port->lock, flags); | ||
274 | if (!netif_carrier_ok(dev)) { | ||
275 | dev->mtu = new_mtu; | ||
276 | err = 0; | ||
277 | } | ||
278 | spin_unlock_irqrestore(&port->lock, flags); | ||
279 | return err; | ||
280 | } | ||
281 | |||
282 | static void pn_net_setup(struct net_device *dev) | ||
283 | { | ||
284 | dev->features = 0; | ||
285 | dev->type = ARPHRD_PHONET; | ||
286 | dev->flags = IFF_POINTOPOINT | IFF_NOARP; | ||
287 | dev->mtu = PHONET_DEV_MTU; | ||
288 | dev->hard_header_len = 1; | ||
289 | dev->dev_addr[0] = PN_MEDIA_USB; | ||
290 | dev->addr_len = 1; | ||
291 | dev->tx_queue_len = 1; | ||
292 | |||
293 | dev->destructor = free_netdev; | ||
294 | dev->header_ops = &phonet_header_ops; | ||
295 | dev->open = pn_net_open; | ||
296 | dev->stop = pn_net_close; | ||
297 | dev->hard_start_xmit = pn_net_xmit; /* mandatory */ | ||
298 | dev->change_mtu = pn_net_mtu; | ||
299 | } | ||
300 | |||
301 | /*-------------------------------------------------------------------------*/ | ||
302 | |||
303 | /* | ||
304 | * Queue buffer for data from the host | ||
305 | */ | ||
306 | static int | ||
307 | pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags) | ||
308 | { | ||
309 | struct sk_buff *skb; | ||
310 | const size_t size = fp->dev->mtu; | ||
311 | int err; | ||
312 | |||
313 | skb = alloc_skb(size, gfp_flags); | ||
314 | if (!skb) | ||
315 | return -ENOMEM; | ||
316 | |||
317 | req->buf = skb->data; | ||
318 | req->length = size; | ||
319 | req->context = skb; | ||
320 | |||
321 | err = usb_ep_queue(fp->out_ep, req, gfp_flags); | ||
322 | if (unlikely(err)) | ||
323 | dev_kfree_skb_any(skb); | ||
324 | return err; | ||
325 | } | ||
326 | |||
327 | static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) | ||
328 | { | ||
329 | struct f_phonet *fp = ep->driver_data; | ||
330 | struct net_device *dev = fp->dev; | ||
331 | struct sk_buff *skb = req->context; | ||
332 | int status = req->status; | ||
333 | |||
334 | switch (status) { | ||
335 | case 0: | ||
336 | if (unlikely(!netif_running(dev))) | ||
337 | break; | ||
338 | if (unlikely(req->actual < 1)) | ||
339 | break; | ||
340 | skb_put(skb, req->actual); | ||
341 | skb->protocol = htons(ETH_P_PHONET); | ||
342 | skb_reset_mac_header(skb); | ||
343 | __skb_pull(skb, 1); | ||
344 | skb->dev = dev; | ||
345 | dev->stats.rx_packets++; | ||
346 | dev->stats.rx_bytes += skb->len; | ||
347 | |||
348 | netif_rx(skb); | ||
349 | skb = NULL; | ||
350 | break; | ||
351 | |||
352 | /* Do not resubmit in these cases: */ | ||
353 | case -ESHUTDOWN: /* disconnect */ | ||
354 | case -ECONNABORTED: /* hw reset */ | ||
355 | case -ECONNRESET: /* dequeued (unlink or netif down) */ | ||
356 | req = NULL; | ||
357 | break; | ||
358 | |||
359 | /* Do resubmit in these cases: */ | ||
360 | case -EOVERFLOW: /* request buffer overflow */ | ||
361 | dev->stats.rx_over_errors++; | ||
362 | default: | ||
363 | dev->stats.rx_errors++; | ||
364 | break; | ||
365 | } | ||
366 | |||
367 | if (skb) | ||
368 | dev_kfree_skb_any(skb); | ||
369 | if (req) | ||
370 | pn_rx_submit(fp, req, GFP_ATOMIC); | ||
371 | } | ||
372 | |||
373 | /*-------------------------------------------------------------------------*/ | ||
374 | |||
375 | static void __pn_reset(struct usb_function *f) | ||
376 | { | ||
377 | struct f_phonet *fp = func_to_pn(f); | ||
378 | struct net_device *dev = fp->dev; | ||
379 | struct phonet_port *port = netdev_priv(dev); | ||
380 | |||
381 | netif_carrier_off(dev); | ||
382 | netif_stop_queue(dev); | ||
383 | port->usb = NULL; | ||
384 | |||
385 | usb_ep_disable(fp->out_ep); | ||
386 | usb_ep_disable(fp->in_ep); | ||
387 | } | ||
388 | |||
389 | static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt) | ||
390 | { | ||
391 | struct f_phonet *fp = func_to_pn(f); | ||
392 | struct usb_gadget *gadget = fp->function.config->cdev->gadget; | ||
393 | |||
394 | if (intf == pn_control_intf_desc.bInterfaceNumber) | ||
395 | /* control interface, no altsetting */ | ||
396 | return (alt > 0) ? -EINVAL : 0; | ||
397 | |||
398 | if (intf == pn_data_intf_desc.bInterfaceNumber) { | ||
399 | struct net_device *dev = fp->dev; | ||
400 | struct phonet_port *port = netdev_priv(dev); | ||
401 | |||
402 | /* data intf (0: inactive, 1: active) */ | ||
403 | if (alt > 1) | ||
404 | return -EINVAL; | ||
405 | |||
406 | spin_lock(&port->lock); | ||
407 | __pn_reset(f); | ||
408 | if (alt == 1) { | ||
409 | struct usb_endpoint_descriptor *out, *in; | ||
410 | int i; | ||
411 | |||
412 | out = ep_choose(gadget, | ||
413 | &pn_hs_sink_desc, | ||
414 | &pn_fs_sink_desc); | ||
415 | in = ep_choose(gadget, | ||
416 | &pn_hs_source_desc, | ||
417 | &pn_fs_source_desc); | ||
418 | usb_ep_enable(fp->out_ep, out); | ||
419 | usb_ep_enable(fp->in_ep, in); | ||
420 | |||
421 | port->usb = fp; | ||
422 | fp->out_ep->driver_data = fp; | ||
423 | fp->in_ep->driver_data = fp; | ||
424 | |||
425 | netif_carrier_on(dev); | ||
426 | if (netif_running(dev)) | ||
427 | netif_wake_queue(dev); | ||
428 | for (i = 0; i < phonet_rxq_size; i++) | ||
429 | pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC); | ||
430 | } | ||
431 | spin_unlock(&port->lock); | ||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | return -EINVAL; | ||
436 | } | ||
437 | |||
438 | static int pn_get_alt(struct usb_function *f, unsigned intf) | ||
439 | { | ||
440 | struct f_phonet *fp = func_to_pn(f); | ||
441 | |||
442 | if (intf == pn_control_intf_desc.bInterfaceNumber) | ||
443 | return 0; | ||
444 | |||
445 | if (intf == pn_data_intf_desc.bInterfaceNumber) { | ||
446 | struct phonet_port *port = netdev_priv(fp->dev); | ||
447 | u8 alt; | ||
448 | |||
449 | spin_lock(&port->lock); | ||
450 | alt = port->usb != NULL; | ||
451 | spin_unlock(&port->lock); | ||
452 | return alt; | ||
453 | } | ||
454 | |||
455 | return -EINVAL; | ||
456 | } | ||
457 | |||
458 | static void pn_disconnect(struct usb_function *f) | ||
459 | { | ||
460 | struct f_phonet *fp = func_to_pn(f); | ||
461 | struct phonet_port *port = netdev_priv(fp->dev); | ||
462 | unsigned long flags; | ||
463 | |||
464 | /* remain disabled until set_alt */ | ||
465 | spin_lock_irqsave(&port->lock, flags); | ||
466 | __pn_reset(f); | ||
467 | spin_unlock_irqrestore(&port->lock, flags); | ||
468 | } | ||
469 | |||
470 | /*-------------------------------------------------------------------------*/ | ||
471 | |||
472 | static __init | ||
473 | int pn_bind(struct usb_configuration *c, struct usb_function *f) | ||
474 | { | ||
475 | struct usb_composite_dev *cdev = c->cdev; | ||
476 | struct usb_gadget *gadget = cdev->gadget; | ||
477 | struct f_phonet *fp = func_to_pn(f); | ||
478 | struct usb_ep *ep; | ||
479 | int status, i; | ||
480 | |||
481 | /* Reserve interface IDs */ | ||
482 | status = usb_interface_id(c, f); | ||
483 | if (status < 0) | ||
484 | goto err; | ||
485 | pn_control_intf_desc.bInterfaceNumber = status; | ||
486 | pn_union_desc.bMasterInterface0 = status; | ||
487 | |||
488 | status = usb_interface_id(c, f); | ||
489 | if (status < 0) | ||
490 | goto err; | ||
491 | pn_data_nop_intf_desc.bInterfaceNumber = status; | ||
492 | pn_data_intf_desc.bInterfaceNumber = status; | ||
493 | pn_union_desc.bSlaveInterface0 = status; | ||
494 | |||
495 | /* Reserve endpoints */ | ||
496 | status = -ENODEV; | ||
497 | ep = usb_ep_autoconfig(gadget, &pn_fs_sink_desc); | ||
498 | if (!ep) | ||
499 | goto err; | ||
500 | fp->out_ep = ep; | ||
501 | ep->driver_data = fp; /* Claim */ | ||
502 | |||
503 | ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc); | ||
504 | if (!ep) | ||
505 | goto err; | ||
506 | fp->in_ep = ep; | ||
507 | ep->driver_data = fp; /* Claim */ | ||
508 | |||
509 | pn_hs_sink_desc.bEndpointAddress = | ||
510 | pn_fs_sink_desc.bEndpointAddress; | ||
511 | pn_hs_source_desc.bEndpointAddress = | ||
512 | pn_fs_source_desc.bEndpointAddress; | ||
513 | |||
514 | /* Do not try to bind Phonet twice... */ | ||
515 | fp->function.descriptors = fs_pn_function; | ||
516 | fp->function.hs_descriptors = hs_pn_function; | ||
517 | |||
518 | /* Incoming USB requests */ | ||
519 | status = -ENOMEM; | ||
520 | for (i = 0; i < phonet_rxq_size; i++) { | ||
521 | struct usb_request *req; | ||
522 | |||
523 | req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL); | ||
524 | if (!req) | ||
525 | goto err; | ||
526 | |||
527 | req->complete = pn_rx_complete; | ||
528 | fp->out_reqv[i] = req; | ||
529 | } | ||
530 | |||
531 | /* Outgoing USB requests */ | ||
532 | fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL); | ||
533 | if (!fp->in_req) | ||
534 | goto err; | ||
535 | |||
536 | INFO(cdev, "USB CDC Phonet function\n"); | ||
537 | INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name, | ||
538 | fp->out_ep->name, fp->in_ep->name); | ||
539 | return 0; | ||
540 | |||
541 | err: | ||
542 | if (fp->out_ep) | ||
543 | fp->out_ep->driver_data = NULL; | ||
544 | if (fp->in_ep) | ||
545 | fp->in_ep->driver_data = NULL; | ||
546 | ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n"); | ||
547 | return status; | ||
548 | } | ||
549 | |||
550 | static void | ||
551 | pn_unbind(struct usb_configuration *c, struct usb_function *f) | ||
552 | { | ||
553 | struct f_phonet *fp = func_to_pn(f); | ||
554 | int i; | ||
555 | |||
556 | /* We are already disconnected */ | ||
557 | if (fp->in_req) | ||
558 | usb_ep_free_request(fp->in_ep, fp->in_req); | ||
559 | for (i = 0; i < phonet_rxq_size; i++) | ||
560 | if (fp->out_reqv[i]) | ||
561 | usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); | ||
562 | |||
563 | kfree(fp); | ||
564 | } | ||
565 | |||
566 | /*-------------------------------------------------------------------------*/ | ||
567 | |||
568 | static struct net_device *dev; | ||
569 | |||
570 | int __init phonet_bind_config(struct usb_configuration *c) | ||
571 | { | ||
572 | struct f_phonet *fp; | ||
573 | int err; | ||
574 | |||
575 | fp = kzalloc(sizeof(*fp), GFP_KERNEL); | ||
576 | if (!fp) | ||
577 | return -ENOMEM; | ||
578 | |||
579 | fp->dev = dev; | ||
580 | fp->function.name = "phonet"; | ||
581 | fp->function.bind = pn_bind; | ||
582 | fp->function.unbind = pn_unbind; | ||
583 | fp->function.set_alt = pn_set_alt; | ||
584 | fp->function.get_alt = pn_get_alt; | ||
585 | fp->function.disable = pn_disconnect; | ||
586 | |||
587 | err = usb_add_function(c, &fp->function); | ||
588 | if (err) | ||
589 | kfree(fp); | ||
590 | return err; | ||
591 | } | ||
592 | |||
593 | int __init gphonet_setup(struct usb_gadget *gadget) | ||
594 | { | ||
595 | struct phonet_port *port; | ||
596 | int err; | ||
597 | |||
598 | /* Create net device */ | ||
599 | BUG_ON(dev); | ||
600 | dev = alloc_netdev(sizeof(*port) | ||
601 | + (phonet_rxq_size * sizeof(struct usb_request *)), | ||
602 | "upnlink%d", pn_net_setup); | ||
603 | if (!dev) | ||
604 | return -ENOMEM; | ||
605 | |||
606 | port = netdev_priv(dev); | ||
607 | spin_lock_init(&port->lock); | ||
608 | netif_carrier_off(dev); | ||
609 | netif_stop_queue(dev); | ||
610 | SET_NETDEV_DEV(dev, &gadget->dev); | ||
611 | |||
612 | err = register_netdev(dev); | ||
613 | if (err) | ||
614 | free_netdev(dev); | ||
615 | return err; | ||
616 | } | ||
617 | |||
618 | void gphonet_cleanup(void) | ||
619 | { | ||
620 | unregister_netdev(dev); | ||
621 | } | ||
diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/u_phonet.h new file mode 100644 index 000000000000..09a75259b6cd --- /dev/null +++ b/drivers/usb/gadget/u_phonet.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * u_phonet.h - interface to Phonet | ||
3 | * | ||
4 | * Copyright (C) 2007-2008 by Nokia Corporation | ||
5 | * | ||
6 | * This software is distributed under the terms of the GNU General | ||
7 | * Public License ("GPL") as published by the Free Software Foundation, | ||
8 | * either version 2 of that License or (at your option) any later version. | ||
9 | */ | ||
10 | |||
11 | #ifndef __U_PHONET_H | ||
12 | #define __U_PHONET_H | ||
13 | |||
14 | #include <linux/usb/composite.h> | ||
15 | #include <linux/usb/cdc.h> | ||
16 | |||
17 | int gphonet_setup(struct usb_gadget *gadget); | ||
18 | int phonet_bind_config(struct usb_configuration *c); | ||
19 | void gphonet_cleanup(void); | ||
20 | |||
21 | #endif /* __U_PHONET_H */ | ||