Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(321)

Side by Side Diff: drivers/net/usb/gobi/qcusbnet.c

Issue 6539018: CHROMIUM: Add gobi driver. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/kernel.git@master
Patch Set: Remove config changes. Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « drivers/net/usb/gobi/qcusbnet.h ('k') | drivers/net/usb/gobi/qmi.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /* qcusbnet.c - gobi network device
2 * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
3
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301, USA.
17 */
18
19 #include "structs.h"
20 #include "qmidevice.h"
21 #include "qmi.h"
22 #include "qcusbnet.h"
23
24 #define DRIVER_VERSION "1.0.110"
25 #define DRIVER_AUTHOR "Qualcomm Innovation Center"
26 #define DRIVER_DESC "QCUSBNet2k"
27
28 int qcusbnet_debug;
29 static struct class *devclass;
30
31 int qc_suspend(struct usb_interface *iface, pm_message_t event)
32 {
33 struct usbnet *usbnet;
34 struct qcusbnet *dev;
35
36 if (!iface)
37 return -ENOMEM;
38
39 usbnet = usb_get_intfdata(iface);
40
41 if (!usbnet || !usbnet->net) {
42 DBG("failed to get netdevice\n");
43 return -ENXIO;
44 }
45
46 dev = (struct qcusbnet *)usbnet->data[0];
47 if (!dev) {
48 DBG("failed to get QMIDevice\n");
49 return -ENXIO;
50 }
51
52 if (!(event.event & PM_EVENT_AUTO)) {
53 DBG("device suspended to power level %d\n",
54 event.event);
55 qc_setdown(dev, DOWN_DRIVER_SUSPENDED);
56 } else {
57 DBG("device autosuspend\n");
58 }
59
60 if (event.event & PM_EVENT_SUSPEND) {
61 qc_stopread(dev);
62 usbnet->udev->reset_resume = 0;
63 iface->dev.power.power_state.event = event.event;
64 } else {
65 usbnet->udev->reset_resume = 1;
66 }
67
68 return usbnet_suspend(iface, event);
69 }
70
71 static int qc_resume(struct usb_interface *iface)
72 {
73 struct usbnet *usbnet;
74 struct qcusbnet *dev;
75 int ret;
76 int oldstate;
77
78 if (iface == 0)
79 return -ENOMEM;
80
81 usbnet = usb_get_intfdata(iface);
82
83 if (!usbnet || !usbnet->net) {
84 DBG("failed to get netdevice\n");
85 return -ENXIO;
86 }
87
88 dev = (struct qcusbnet *)usbnet->data[0];
89 if (!dev) {
90 DBG("failed to get QMIDevice\n");
91 return -ENXIO;
92 }
93
94 oldstate = iface->dev.power.power_state.event;
95 iface->dev.power.power_state.event = PM_EVENT_ON;
96 DBG("resuming from power mode %d\n", oldstate);
97
98 if (oldstate & PM_EVENT_SUSPEND) {
99 qc_cleardown(dev, DOWN_DRIVER_SUSPENDED);
100
101 ret = usbnet_resume(iface);
102 if (ret) {
103 DBG("usbnet_resume error %d\n", ret);
104 return ret;
105 }
106
107 ret = qc_startread(dev);
108 if (ret) {
109 DBG("qc_startread error %d\n", ret);
110 return ret;
111 }
112
113 complete(&dev->worker.work);
114 } else {
115 DBG("nothing to resume\n");
116 return 0;
117 }
118
119 return ret;
120 }
121
122 static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
123 {
124 int numends;
125 int i;
126 struct usb_host_endpoint *endpoint = NULL;
127 struct usb_host_endpoint *in = NULL;
128 struct usb_host_endpoint *out = NULL;
129
130 if (iface->num_altsetting != 1) {
131 DBG("invalid num_altsetting %u\n", iface->num_altsetting);
132 return -EINVAL;
133 }
134
135 if (iface->cur_altsetting->desc.bInterfaceNumber != 0) {
136 DBG("invalid interface %d\n",
137 iface->cur_altsetting->desc.bInterfaceNumber);
138 return -EINVAL;
139 }
140
141 numends = iface->cur_altsetting->desc.bNumEndpoints;
142 for (i = 0; i < numends; i++) {
143 endpoint = iface->cur_altsetting->endpoint + i;
144 if (!endpoint) {
145 DBG("invalid endpoint %u\n", i);
146 return -EINVAL;
147 }
148
149 if (usb_endpoint_dir_in(&endpoint->desc)
150 && !usb_endpoint_xfer_int(&endpoint->desc)) {
151 in = endpoint;
152 } else if (!usb_endpoint_dir_out(&endpoint->desc)) {
153 out = endpoint;
154 }
155 }
156
157 if (!in || !out) {
158 DBG("invalid endpoints\n");
159 return -EINVAL;
160 }
161
162 if (usb_set_interface(usbnet->udev,
163 iface->cur_altsetting->desc.bInterfaceNumber, 0)) {
164 DBG("unable to set interface\n");
165 return -EINVAL;
166 }
167
168 usbnet->in = usb_rcvbulkpipe(usbnet->udev, in->desc.bEndpointAddress & U SB_ENDPOINT_NUMBER_MASK);
169 usbnet->out = usb_sndbulkpipe(usbnet->udev, out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
170
171 DBG("in %x, out %x\n",
172 in->desc.bEndpointAddress,
173 out->desc.bEndpointAddress);
174
175 return 0;
176 }
177
178 static void qcnet_unbind(struct usbnet *usbnet, struct usb_interface *iface)
179 {
180 struct qcusbnet *dev = (struct qcusbnet *)usbnet->data[0];
181
182 netif_carrier_off(usbnet->net);
183 qc_deregister(dev);
184
185 kfree(usbnet->net->netdev_ops);
186 usbnet->net->netdev_ops = NULL;
187
188 kfree(dev);
189 }
190
191 static void qcnet_urbhook(struct urb *urb)
192 {
193 unsigned long flags;
194 struct worker *worker = urb->context;
195 if (!worker) {
196 DBG("bad context\n");
197 return;
198 }
199
200 if (urb->status) {
201 DBG("urb finished with error %d\n", urb->status);
202 }
203
204 spin_lock_irqsave(&worker->active_lock, flags);
205 worker->active = ERR_PTR(-EAGAIN);
206 spin_unlock_irqrestore(&worker->active_lock, flags);
207 /* XXX-fix race against qcnet_stop()? */
208 complete(&worker->work);
209 usb_free_urb(urb);
210 }
211
212 static void qcnet_txtimeout(struct net_device *netdev)
213 {
214 struct list_head *node, *tmp;
215 struct qcusbnet *dev;
216 struct worker *worker;
217 struct urbreq *req;
218 unsigned long activeflags, listflags;
219 struct usbnet *usbnet = netdev_priv(netdev);
220
221 if (!usbnet || !usbnet->net) {
222 DBG("failed to get usbnet device\n");
223 return;
224 }
225
226 dev = (struct qcusbnet *)usbnet->data[0];
227 if (!dev) {
228 DBG("failed to get QMIDevice\n");
229 return;
230 }
231 worker = &dev->worker;
232
233 DBG("\n");
234
235 spin_lock_irqsave(&worker->active_lock, activeflags);
236 if (worker->active)
237 usb_kill_urb(worker->active);
238 spin_unlock_irqrestore(&worker->active_lock, activeflags);
239
240 spin_lock_irqsave(&worker->urbs_lock, listflags);
241 list_for_each_safe(node, tmp, &worker->urbs) {
242 req = list_entry(node, struct urbreq, node);
243 usb_free_urb(req->urb);
244 list_del(&req->node);
245 kfree(req);
246 }
247 spin_unlock_irqrestore(&worker->urbs_lock, listflags);
248
249 complete(&worker->work);
250 }
251
252 static int qcnet_worker(void *arg)
253 {
254 struct list_head *node, *tmp;
255 unsigned long activeflags, listflags;
256 struct urbreq *req;
257 int status;
258 struct usb_device *usbdev;
259 struct worker *worker = arg;
260 if (!worker) {
261 DBG("passed null pointer\n");
262 return -EINVAL;
263 }
264
265 usbdev = interface_to_usbdev(worker->iface);
266
267 DBG("traffic thread started\n");
268
269 while (!kthread_should_stop()) {
270 wait_for_completion_interruptible(&worker->work);
271
272 if (kthread_should_stop()) {
273 spin_lock_irqsave(&worker->active_lock, activeflags);
274 if (worker->active) {
275 usb_kill_urb(worker->active);
276 }
277 spin_unlock_irqrestore(&worker->active_lock, activeflags );
278
279 spin_lock_irqsave(&worker->urbs_lock, listflags);
280 list_for_each_safe(node, tmp, &worker->urbs) {
281 req = list_entry(node, struct urbreq, node);
282 usb_free_urb(req->urb);
283 list_del(&req->node);
284 kfree(req);
285 }
286 spin_unlock_irqrestore(&worker->urbs_lock, listflags);
287
288 break;
289 }
290
291 spin_lock_irqsave(&worker->active_lock, activeflags);
292 if (IS_ERR(worker->active) && PTR_ERR(worker->active) == -EAGAIN ) {
293 worker->active = NULL;
294 spin_unlock_irqrestore(&worker->active_lock, activeflags );
295 usb_autopm_put_interface(worker->iface);
296 spin_lock_irqsave(&worker->active_lock, activeflags);
297 }
298
299 if (worker->active) {
300 spin_unlock_irqrestore(&worker->active_lock, activeflags );
301 continue;
302 }
303
304 spin_lock_irqsave(&worker->urbs_lock, listflags);
305 if (list_empty(&worker->urbs)) {
306 spin_unlock_irqrestore(&worker->urbs_lock, listflags);
307 spin_unlock_irqrestore(&worker->active_lock, activeflags );
308 continue;
309 }
310
311 req = list_first_entry(&worker->urbs, struct urbreq, node);
312 list_del(&req->node);
313 spin_unlock_irqrestore(&worker->urbs_lock, listflags);
314
315 worker->active = req->urb;
316 spin_unlock_irqrestore(&worker->active_lock, activeflags);
317
318 status = usb_autopm_get_interface(worker->iface);
319 if (status < 0) {
320 DBG("unable to autoresume interface: %d\n", status);
321 if (status == -EPERM) {
322 qc_suspend(worker->iface, PMSG_SUSPEND);
323 }
324
325 spin_lock_irqsave(&worker->urbs_lock, listflags);
326 list_add(&req->node, &worker->urbs);
327 spin_unlock_irqrestore(&worker->urbs_lock, listflags);
328
329 spin_lock_irqsave(&worker->active_lock, activeflags);
330 worker->active = NULL;
331 spin_unlock_irqrestore(&worker->active_lock, activeflags );
332
333 continue;
334 }
335
336 status = usb_submit_urb(worker->active, GFP_KERNEL);
337 if (status < 0) {
338 DBG("Failed to submit URB: %d. Packet dropped\n", statu s);
339 spin_lock_irqsave(&worker->active_lock, activeflags);
340 usb_free_urb(worker->active);
341 worker->active = NULL;
342 spin_unlock_irqrestore(&worker->active_lock, activeflags );
343 usb_autopm_put_interface(worker->iface);
344 complete(&worker->work);
345 }
346
347 kfree(req);
348 }
349
350 DBG("traffic thread exiting\n");
351 worker->thread = NULL;
352 return 0;
353 }
354
355 static int qcnet_startxmit(struct sk_buff *skb, struct net_device *netdev)
356 {
357 unsigned long listflags;
358 struct qcusbnet *dev;
359 struct worker *worker;
360 struct urbreq *req;
361 void *data;
362 struct usbnet *usbnet = netdev_priv(netdev);
363
364 DBG("\n");
365
366 if (!usbnet || !usbnet->net) {
367 DBG("failed to get usbnet device\n");
368 return NETDEV_TX_BUSY;
369 }
370
371 dev = (struct qcusbnet *)usbnet->data[0];
372 if (!dev) {
373 DBG("failed to get QMIDevice\n");
374 return NETDEV_TX_BUSY;
375 }
376 worker = &dev->worker;
377
378 if (qc_isdown(dev, DOWN_DRIVER_SUSPENDED)) {
379 DBG("device is suspended\n");
380 dump_stack();
381 return NETDEV_TX_BUSY;
382 }
383
384 req = kmalloc(sizeof(*req), GFP_ATOMIC);
385 if (!req) {
386 DBG("unable to allocate URBList memory\n");
387 return NETDEV_TX_BUSY;
388 }
389
390 req->urb = usb_alloc_urb(0, GFP_ATOMIC);
391
392 if (!req->urb) {
393 kfree(req);
394 DBG("unable to allocate URB\n");
395 return NETDEV_TX_BUSY;
396 }
397
398 data = kmalloc(skb->len, GFP_ATOMIC);
399 if (!data) {
400 usb_free_urb(req->urb);
401 kfree(req);
402 DBG("unable to allocate URB data\n");
403 return NETDEV_TX_BUSY;
404 }
405 memcpy(data, skb->data, skb->len);
406
407 usb_fill_bulk_urb(req->urb, dev->usbnet->udev, dev->usbnet->out,
408 data, skb->len, qcnet_urbhook, worker);
409
410 spin_lock_irqsave(&worker->urbs_lock, listflags);
411 list_add_tail(&req->node, &worker->urbs);
412 spin_unlock_irqrestore(&worker->urbs_lock, listflags);
413
414 complete(&worker->work);
415
416 netdev->trans_start = jiffies;
417 dev_kfree_skb_any(skb);
418
419 return NETDEV_TX_OK;
420 }
421
422 static int qcnet_open(struct net_device *netdev)
423 {
424 int status = 0;
425 struct qcusbnet *dev;
426 struct usbnet *usbnet = netdev_priv(netdev);
427
428 if (!usbnet) {
429 DBG("failed to get usbnet device\n");
430 return -ENXIO;
431 }
432
433 dev = (struct qcusbnet *)usbnet->data[0];
434 if (!dev) {
435 DBG("failed to get QMIDevice\n");
436 return -ENXIO;
437 }
438
439 DBG("\n");
440
441 dev->worker.iface = dev->iface;
442 INIT_LIST_HEAD(&dev->worker.urbs);
443 dev->worker.active = NULL;
444 spin_lock_init(&dev->worker.urbs_lock);
445 spin_lock_init(&dev->worker.active_lock);
446 init_completion(&dev->worker.work);
447
448 dev->worker.thread = kthread_run(qcnet_worker, &dev->worker, "qcnet_work er");
449 if (IS_ERR(dev->worker.thread)) {
450 DBG("AutoPM thread creation error\n");
451 return PTR_ERR(dev->worker.thread);
452 }
453
454 qc_cleardown(dev, DOWN_NET_IFACE_STOPPED);
455 if (dev->open) {
456 status = dev->open(netdev);
457 if (status == 0) {
458 usb_autopm_put_interface(dev->iface);
459 }
460 } else {
461 DBG("no USBNetOpen defined\n");
462 }
463
464 return status;
465 }
466
467 int qcnet_stop(struct net_device *netdev)
468 {
469 struct qcusbnet *dev;
470 struct usbnet *usbnet = netdev_priv(netdev);
471
472 if (!usbnet || !usbnet->net) {
473 DBG("failed to get netdevice\n");
474 return -ENXIO;
475 }
476
477 dev = (struct qcusbnet *)usbnet->data[0];
478 if (!dev) {
479 DBG("failed to get QMIDevice\n");
480 return -ENXIO;
481 }
482
483 qc_setdown(dev, DOWN_NET_IFACE_STOPPED);
484 complete(&dev->worker.work);
485 kthread_stop(dev->worker.thread);
486 DBG("thread stopped\n");
487
488 if (dev->stop != NULL)
489 return dev->stop(netdev);
490 return 0;
491 }
492
493 static const struct driver_info qc_netinfo = {
494 .description = "QCUSBNet Ethernet Device",
495 .flags = FLAG_ETHER,
496 .bind = qcnet_bind,
497 .unbind = qcnet_unbind,
498 .data = 0,
499 };
500
501 #define MKVIDPID(v, p) \
502 { \
503 USB_DEVICE(v, p), \
504 .driver_info = (unsigned long)&qc_netinfo, \
505 }
506
507 static const struct usb_device_id qc_vidpids[] = {
508 MKVIDPID(0x05c6, 0x9215), /* Acer Gobi 2000 */
509 MKVIDPID(0x05c6, 0x9265), /* Asus Gobi 2000 */
510 MKVIDPID(0x16d8, 0x8002), /* CMOTech Gobi 2000 */
511 MKVIDPID(0x413c, 0x8186), /* Dell Gobi 2000 */
512 MKVIDPID(0x1410, 0xa010), /* Entourage Gobi 2000 */
513 MKVIDPID(0x1410, 0xa011), /* Entourage Gobi 2000 */
514 MKVIDPID(0x1410, 0xa012), /* Entourage Gobi 2000 */
515 MKVIDPID(0x1410, 0xa013), /* Entourage Gobi 2000 */
516 MKVIDPID(0x03f0, 0x251d), /* HP Gobi 2000 */
517 MKVIDPID(0x05c6, 0x9205), /* Lenovo Gobi 2000 */
518 MKVIDPID(0x05c6, 0x920b), /* Generic Gobi 2000 */
519 MKVIDPID(0x04da, 0x250f), /* Panasonic Gobi 2000 */
520 MKVIDPID(0x05c6, 0x9245), /* Samsung Gobi 2000 */
521 MKVIDPID(0x1199, 0x9001), /* Sierra Wireless Gobi 2000 */
522 MKVIDPID(0x1199, 0x9002), /* Sierra Wireless Gobi 2000 */
523 MKVIDPID(0x1199, 0x9003), /* Sierra Wireless Gobi 2000 */
524 MKVIDPID(0x1199, 0x9004), /* Sierra Wireless Gobi 2000 */
525 MKVIDPID(0x1199, 0x9005), /* Sierra Wireless Gobi 2000 */
526 MKVIDPID(0x1199, 0x9006), /* Sierra Wireless Gobi 2000 */
527 MKVIDPID(0x1199, 0x9007), /* Sierra Wireless Gobi 2000 */
528 MKVIDPID(0x1199, 0x9008), /* Sierra Wireless Gobi 2000 */
529 MKVIDPID(0x1199, 0x9009), /* Sierra Wireless Gobi 2000 */
530 MKVIDPID(0x1199, 0x900a), /* Sierra Wireless Gobi 2000 */
531 MKVIDPID(0x05c6, 0x9225), /* Sony Gobi 2000 */
532 MKVIDPID(0x05c6, 0x9235), /* Top Global Gobi 2000 */
533 MKVIDPID(0x05c6, 0x9275), /* iRex Technologies Gobi 2000 */
534 { }
535 };
536
537 MODULE_DEVICE_TABLE(usb, qc_vidpids);
538
539 int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids )
540 {
541 int status;
542 struct usbnet *usbnet;
543 struct qcusbnet *dev;
544 struct net_device_ops *netdevops;
545
546 status = usbnet_probe(iface, vidpids);
547 if (status < 0) {
548 DBG("usbnet_probe failed %d\n", status);
549 return status;
550 }
551
552 usbnet = usb_get_intfdata(iface);
553
554 if (!usbnet || !usbnet->net) {
555 DBG("failed to get netdevice\n");
556 return -ENXIO;
557 }
558
559 dev = kmalloc(sizeof(struct qcusbnet), GFP_KERNEL);
560 if (!dev) {
561 DBG("failed to allocate device buffers\n");
562 return -ENOMEM;
563 }
564
565 usbnet->data[0] = (unsigned long)dev;
566
567 dev->usbnet = usbnet;
568
569 netdevops = kmalloc(sizeof(struct net_device_ops), GFP_KERNEL);
570 if (!netdevops) {
571 DBG("failed to allocate net device ops\n");
572 return -ENOMEM;
573 }
574 memcpy(netdevops, usbnet->net->netdev_ops, sizeof(struct net_device_ops) );
575
576 dev->open = netdevops->ndo_open;
577 netdevops->ndo_open = qcnet_open;
578 dev->stop = netdevops->ndo_stop;
579 netdevops->ndo_stop = qcnet_stop;
580 netdevops->ndo_start_xmit = qcnet_startxmit;
581 netdevops->ndo_tx_timeout = qcnet_txtimeout;
582
583 usbnet->net->netdev_ops = netdevops;
584
585 memset(&(dev->usbnet->net->stats), 0, sizeof(struct net_device_stats));
586
587 dev->iface = iface;
588 memset(&(dev->meid), '0', 14);
589
590 DBG("Mac Address: %pM\n", dev->usbnet->net->dev_addr);
591
592 dev->valid = false;
593 memset(&dev->qmi, 0, sizeof(struct qmidev));
594
595 dev->qmi.devclass = devclass;
596
597 INIT_LIST_HEAD(&dev->qmi.clients);
598 init_completion(&dev->worker.work);
599 spin_lock_init(&dev->qmi.clients_lock);
600
601 dev->down = 0;
602 qc_setdown(dev, DOWN_NO_NDIS_CONNECTION);
603 qc_setdown(dev, DOWN_NET_IFACE_STOPPED);
604
605 status = qc_register(dev);
606 if (status) {
607 qc_deregister(dev);
608 }
609
610 return status;
611 }
612 EXPORT_SYMBOL_GPL(qcnet_probe);
613
614 static struct usb_driver qcusbnet = {
615 .name = "QCUSBNet2k",
616 .id_table = qc_vidpids,
617 .probe = qcnet_probe,
618 .disconnect = usbnet_disconnect,
619 .suspend = qc_suspend,
620 .resume = qc_resume,
621 .supports_autosuspend = true,
622 };
623
624 static int __init modinit(void)
625 {
626 devclass = class_create(THIS_MODULE, "QCQMI");
627 if (IS_ERR(devclass)) {
628 DBG("error at class_create %ld\n", PTR_ERR(devclass));
629 return -ENOMEM;
630 }
631 printk(KERN_INFO "%s: %s\n", DRIVER_DESC, DRIVER_VERSION);
632 return usb_register(&qcusbnet);
633 }
634 module_init(modinit);
635
636 static void __exit modexit(void)
637 {
638 usb_deregister(&qcusbnet);
639 class_destroy(devclass);
640 }
641 module_exit(modexit);
642
643 MODULE_VERSION(DRIVER_VERSION);
644 MODULE_AUTHOR(DRIVER_AUTHOR);
645 MODULE_DESCRIPTION(DRIVER_DESC);
646 MODULE_LICENSE("Dual BSD/GPL");
647
648 module_param(qcusbnet_debug, bool, S_IRUGO | S_IWUSR);
649 MODULE_PARM_DESC(qcusbnet_debug, "Debugging enabled or not");
OLDNEW
« no previous file with comments | « drivers/net/usb/gobi/qcusbnet.h ('k') | drivers/net/usb/gobi/qmi.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698