OLD | NEW |
1 /* qcusbnet.c - gobi network device | 1 /* qcusbnet.c - gobi network device |
2 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. | 2 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. |
3 | 3 |
4 * This program is free software; you can redistribute it and/or modify | 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 | 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. | 6 * only version 2 as published by the Free Software Foundation. |
7 | 7 |
8 * This program is distributed in the hope that it will be useful, | 8 * This program is distributed in the hope that it will be useful, |
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
61 if (entry == key) { | 61 if (entry == key) { |
62 kref_get(&entry->refcount); | 62 kref_get(&entry->refcount); |
63 mutex_unlock(&qcusbnet_lock); | 63 mutex_unlock(&qcusbnet_lock); |
64 return entry; | 64 return entry; |
65 } | 65 } |
66 } | 66 } |
67 mutex_unlock(&qcusbnet_lock); | 67 mutex_unlock(&qcusbnet_lock); |
68 return NULL; | 68 return NULL; |
69 } | 69 } |
70 | 70 |
| 71 static void wake_worker(struct worker *worker) |
| 72 { |
| 73 atomic_inc(&worker->work_count); |
| 74 wake_up(&worker->waitq); |
| 75 } |
| 76 |
71 int qc_suspend(struct usb_interface *iface, pm_message_t event) | 77 int qc_suspend(struct usb_interface *iface, pm_message_t event) |
72 { | 78 { |
73 struct usbnet *usbnet; | 79 struct usbnet *usbnet; |
74 struct qcusbnet *dev; | 80 struct qcusbnet *dev; |
75 | 81 |
76 if (!iface) | 82 if (!iface) |
77 return -ENOMEM; | 83 return -ENOMEM; |
78 | 84 |
79 usbnet = usb_get_intfdata(iface); | 85 usbnet = usb_get_intfdata(iface); |
80 | 86 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 DBG("usbnet_resume error %d\n", ret); | 149 DBG("usbnet_resume error %d\n", ret); |
144 return ret; | 150 return ret; |
145 } | 151 } |
146 | 152 |
147 ret = qc_startread(dev); | 153 ret = qc_startread(dev); |
148 if (ret) { | 154 if (ret) { |
149 DBG("qc_startread error %d\n", ret); | 155 DBG("qc_startread error %d\n", ret); |
150 return ret; | 156 return ret; |
151 } | 157 } |
152 | 158 |
153 » » complete(&dev->worker.work); | 159 » » wake_worker(&dev->worker); |
154 } else { | 160 } else { |
155 DBG("nothing to resume\n"); | 161 DBG("nothing to resume\n"); |
156 return 0; | 162 return 0; |
157 } | 163 } |
158 | 164 |
159 return ret; | 165 return ret; |
160 } | 166 } |
161 | 167 |
162 static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface) | 168 static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface) |
163 { | 169 { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
239 } | 245 } |
240 | 246 |
241 if (urb->status) { | 247 if (urb->status) { |
242 DBG("urb finished with error %d\n", urb->status); | 248 DBG("urb finished with error %d\n", urb->status); |
243 } | 249 } |
244 | 250 |
245 spin_lock_irqsave(&worker->active_lock, flags); | 251 spin_lock_irqsave(&worker->active_lock, flags); |
246 worker->active = ERR_PTR(-EAGAIN); | 252 worker->active = ERR_PTR(-EAGAIN); |
247 spin_unlock_irqrestore(&worker->active_lock, flags); | 253 spin_unlock_irqrestore(&worker->active_lock, flags); |
248 /* XXX-fix race against qcnet_stop()? */ | 254 /* XXX-fix race against qcnet_stop()? */ |
249 » complete(&worker->work); | 255 » wake_worker(worker); |
250 usb_free_urb(urb); | 256 usb_free_urb(urb); |
251 } | 257 } |
252 | 258 |
253 static void qcnet_txtimeout(struct net_device *netdev) | 259 static void qcnet_txtimeout(struct net_device *netdev) |
254 { | 260 { |
255 struct list_head *node, *tmp; | 261 struct list_head *node, *tmp; |
256 struct qcusbnet *dev; | 262 struct qcusbnet *dev; |
257 struct worker *worker; | 263 struct worker *worker; |
258 struct urbreq *req; | 264 struct urbreq *req; |
259 unsigned long activeflags, listflags; | 265 unsigned long activeflags, listflags; |
(...skipping 20 matching lines...) Expand all Loading... |
280 | 286 |
281 spin_lock_irqsave(&worker->urbs_lock, listflags); | 287 spin_lock_irqsave(&worker->urbs_lock, listflags); |
282 list_for_each_safe(node, tmp, &worker->urbs) { | 288 list_for_each_safe(node, tmp, &worker->urbs) { |
283 req = list_entry(node, struct urbreq, node); | 289 req = list_entry(node, struct urbreq, node); |
284 usb_free_urb(req->urb); | 290 usb_free_urb(req->urb); |
285 list_del(&req->node); | 291 list_del(&req->node); |
286 kfree(req); | 292 kfree(req); |
287 } | 293 } |
288 spin_unlock_irqrestore(&worker->urbs_lock, listflags); | 294 spin_unlock_irqrestore(&worker->urbs_lock, listflags); |
289 | 295 |
290 » complete(&worker->work); | 296 » wake_worker(worker); |
| 297 } |
| 298 |
| 299 static int worker_should_wake(struct worker *worker) |
| 300 { |
| 301 » if (kthread_should_stop()) |
| 302 » » return 1; |
| 303 » /* This is safe only because we are the only place that decrements this |
| 304 » * counter. */ |
| 305 » if (atomic_read(&worker->work_count)) |
| 306 » » return 1; |
| 307 » return 0; |
291 } | 308 } |
292 | 309 |
293 static int qcnet_worker(void *arg) | 310 static int qcnet_worker(void *arg) |
294 { | 311 { |
295 struct list_head *node, *tmp; | 312 struct list_head *node, *tmp; |
296 unsigned long activeflags, listflags; | 313 unsigned long activeflags, listflags; |
297 struct urbreq *req; | 314 struct urbreq *req; |
298 int status; | 315 int status; |
299 struct usb_device *usbdev; | 316 struct usb_device *usbdev; |
300 struct worker *worker = arg; | 317 struct worker *worker = arg; |
301 if (!worker) { | 318 if (!worker) { |
302 DBG("passed null pointer\n"); | 319 DBG("passed null pointer\n"); |
303 return -EINVAL; | 320 return -EINVAL; |
304 } | 321 } |
305 | 322 |
306 usbdev = interface_to_usbdev(worker->iface); | 323 usbdev = interface_to_usbdev(worker->iface); |
307 | 324 |
308 DBG("traffic thread started\n"); | 325 DBG("traffic thread started\n"); |
309 | 326 |
310 » while (!worker->exit && !kthread_should_stop()) { | 327 » while (!kthread_should_stop()) { |
311 » » wait_for_completion_interruptible(&worker->work); | 328 » » wait_event(worker->waitq, worker_should_wake(worker)); |
312 | 329 |
313 » » if (worker->exit || kthread_should_stop()) { | 330 » » if (kthread_should_stop()) |
314 » » » spin_lock_irqsave(&worker->active_lock, activeflags); | |
315 » » » if (worker->active) { | |
316 » » » » usb_kill_urb(worker->active); | |
317 » » » } | |
318 » » » spin_unlock_irqrestore(&worker->active_lock, activeflags
); | |
319 | |
320 » » » spin_lock_irqsave(&worker->urbs_lock, listflags); | |
321 » » » list_for_each_safe(node, tmp, &worker->urbs) { | |
322 » » » » req = list_entry(node, struct urbreq, node); | |
323 » » » » usb_free_urb(req->urb); | |
324 » » » » list_del(&req->node); | |
325 » » » » kfree(req); | |
326 » » » } | |
327 » » » spin_unlock_irqrestore(&worker->urbs_lock, listflags); | |
328 | |
329 break; | 331 break; |
330 » » } | 332 » » atomic_dec(&worker->work_count); |
331 | 333 |
332 spin_lock_irqsave(&worker->active_lock, activeflags); | 334 spin_lock_irqsave(&worker->active_lock, activeflags); |
333 if (IS_ERR(worker->active) && PTR_ERR(worker->active) == -EAGAIN
) { | 335 if (IS_ERR(worker->active) && PTR_ERR(worker->active) == -EAGAIN
) { |
334 worker->active = NULL; | 336 worker->active = NULL; |
335 spin_unlock_irqrestore(&worker->active_lock, activeflags
); | 337 spin_unlock_irqrestore(&worker->active_lock, activeflags
); |
336 usb_autopm_put_interface(worker->iface); | 338 usb_autopm_put_interface(worker->iface); |
337 spin_lock_irqsave(&worker->active_lock, activeflags); | 339 spin_lock_irqsave(&worker->active_lock, activeflags); |
338 } | 340 } |
339 | 341 |
340 if (worker->active) { | 342 if (worker->active) { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
375 } | 377 } |
376 | 378 |
377 status = usb_submit_urb(worker->active, GFP_KERNEL); | 379 status = usb_submit_urb(worker->active, GFP_KERNEL); |
378 if (status < 0) { | 380 if (status < 0) { |
379 DBG("Failed to submit URB: %d. Packet dropped\n", statu
s); | 381 DBG("Failed to submit URB: %d. Packet dropped\n", statu
s); |
380 spin_lock_irqsave(&worker->active_lock, activeflags); | 382 spin_lock_irqsave(&worker->active_lock, activeflags); |
381 usb_free_urb(worker->active); | 383 usb_free_urb(worker->active); |
382 worker->active = NULL; | 384 worker->active = NULL; |
383 spin_unlock_irqrestore(&worker->active_lock, activeflags
); | 385 spin_unlock_irqrestore(&worker->active_lock, activeflags
); |
384 usb_autopm_put_interface(worker->iface); | 386 usb_autopm_put_interface(worker->iface); |
385 » » » complete(&worker->work); | 387 » » » wake_worker(worker); |
386 } | 388 } |
387 | 389 |
388 kfree(req); | 390 kfree(req); |
389 } | 391 } |
390 | 392 |
| 393 spin_lock_irqsave(&worker->active_lock, activeflags); |
| 394 if (worker->active) { |
| 395 usb_kill_urb(worker->active); |
| 396 } |
| 397 spin_unlock_irqrestore(&worker->active_lock, activeflags); |
| 398 |
| 399 spin_lock_irqsave(&worker->urbs_lock, listflags); |
| 400 list_for_each_safe(node, tmp, &worker->urbs) { |
| 401 req = list_entry(node, struct urbreq, node); |
| 402 usb_free_urb(req->urb); |
| 403 list_del(&req->node); |
| 404 kfree(req); |
| 405 } |
| 406 spin_unlock_irqrestore(&worker->urbs_lock, listflags); |
| 407 |
391 DBG("traffic thread exiting\n"); | 408 DBG("traffic thread exiting\n"); |
392 worker->thread = NULL; | 409 worker->thread = NULL; |
393 return 0; | 410 return 0; |
394 } | 411 } |
395 | 412 |
396 static int qcnet_startxmit(struct sk_buff *skb, struct net_device *netdev) | 413 static int qcnet_startxmit(struct sk_buff *skb, struct net_device *netdev) |
397 { | 414 { |
398 unsigned long listflags; | 415 unsigned long listflags; |
399 struct qcusbnet *dev; | 416 struct qcusbnet *dev; |
400 struct worker *worker; | 417 struct worker *worker; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
445 } | 462 } |
446 memcpy(data, skb->data, skb->len); | 463 memcpy(data, skb->data, skb->len); |
447 | 464 |
448 usb_fill_bulk_urb(req->urb, dev->usbnet->udev, dev->usbnet->out, | 465 usb_fill_bulk_urb(req->urb, dev->usbnet->udev, dev->usbnet->out, |
449 data, skb->len, qcnet_urbhook, worker); | 466 data, skb->len, qcnet_urbhook, worker); |
450 | 467 |
451 spin_lock_irqsave(&worker->urbs_lock, listflags); | 468 spin_lock_irqsave(&worker->urbs_lock, listflags); |
452 list_add_tail(&req->node, &worker->urbs); | 469 list_add_tail(&req->node, &worker->urbs); |
453 spin_unlock_irqrestore(&worker->urbs_lock, listflags); | 470 spin_unlock_irqrestore(&worker->urbs_lock, listflags); |
454 | 471 |
455 » complete(&worker->work); | 472 » wake_worker(worker); |
456 | 473 |
457 netdev->trans_start = jiffies; | 474 netdev->trans_start = jiffies; |
458 dev_kfree_skb_any(skb); | 475 dev_kfree_skb_any(skb); |
459 | 476 |
460 return NETDEV_TX_OK; | 477 return NETDEV_TX_OK; |
461 } | 478 } |
462 | 479 |
463 static int qcnet_open(struct net_device *netdev) | 480 static int qcnet_open(struct net_device *netdev) |
464 { | 481 { |
465 int status = 0; | 482 int status = 0; |
(...skipping 11 matching lines...) Expand all Loading... |
477 return -ENXIO; | 494 return -ENXIO; |
478 } | 495 } |
479 | 496 |
480 DBG("\n"); | 497 DBG("\n"); |
481 | 498 |
482 dev->worker.iface = dev->iface; | 499 dev->worker.iface = dev->iface; |
483 INIT_LIST_HEAD(&dev->worker.urbs); | 500 INIT_LIST_HEAD(&dev->worker.urbs); |
484 dev->worker.active = NULL; | 501 dev->worker.active = NULL; |
485 spin_lock_init(&dev->worker.urbs_lock); | 502 spin_lock_init(&dev->worker.urbs_lock); |
486 spin_lock_init(&dev->worker.active_lock); | 503 spin_lock_init(&dev->worker.active_lock); |
487 » init_completion(&dev->worker.work); | 504 » atomic_set(&dev->worker.work_count, 0); |
| 505 » init_waitqueue_head(&dev->worker.waitq); |
488 | 506 |
489 dev->worker.exit = 0; | |
490 dev->worker.thread = kthread_run(qcnet_worker, &dev->worker, "qcnet_work
er"); | 507 dev->worker.thread = kthread_run(qcnet_worker, &dev->worker, "qcnet_work
er"); |
491 if (IS_ERR(dev->worker.thread)) { | 508 if (IS_ERR(dev->worker.thread)) { |
492 DBG("AutoPM thread creation error\n"); | 509 DBG("AutoPM thread creation error\n"); |
493 return PTR_ERR(dev->worker.thread); | 510 return PTR_ERR(dev->worker.thread); |
494 } | 511 } |
495 | 512 |
496 qc_cleardown(dev, DOWN_NET_IFACE_STOPPED); | 513 qc_cleardown(dev, DOWN_NET_IFACE_STOPPED); |
497 if (dev->open) { | 514 if (dev->open) { |
498 status = dev->open(netdev); | 515 status = dev->open(netdev); |
499 if (status == 0) { | 516 if (status == 0) { |
(...skipping 16 matching lines...) Expand all Loading... |
516 return -ENXIO; | 533 return -ENXIO; |
517 } | 534 } |
518 | 535 |
519 dev = (struct qcusbnet *)usbnet->data[0]; | 536 dev = (struct qcusbnet *)usbnet->data[0]; |
520 if (!dev) { | 537 if (!dev) { |
521 DBG("failed to get QMIDevice\n"); | 538 DBG("failed to get QMIDevice\n"); |
522 return -ENXIO; | 539 return -ENXIO; |
523 } | 540 } |
524 | 541 |
525 qc_setdown(dev, DOWN_NET_IFACE_STOPPED); | 542 qc_setdown(dev, DOWN_NET_IFACE_STOPPED); |
526 dev->worker.exit = 1; | |
527 complete(&dev->worker.work); | |
528 kthread_stop(dev->worker.thread); | 543 kthread_stop(dev->worker.thread); |
529 DBG("thread stopped\n"); | 544 DBG("thread stopped\n"); |
530 | 545 |
531 if (dev->stop != NULL) | 546 if (dev->stop != NULL) |
532 return dev->stop(netdev); | 547 return dev->stop(netdev); |
533 return 0; | 548 return 0; |
534 } | 549 } |
535 | 550 |
536 static const struct driver_info qc_netinfo = { | 551 static const struct driver_info qc_netinfo = { |
537 .description = "QCUSBNet Ethernet Device", | 552 .description = "QCUSBNet Ethernet Device", |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
646 memset(&(dev->meid), '0', 14); | 661 memset(&(dev->meid), '0', 14); |
647 | 662 |
648 dev->valid = false; | 663 dev->valid = false; |
649 memset(&dev->qmi, 0, sizeof(dev->qmi)); | 664 memset(&dev->qmi, 0, sizeof(dev->qmi)); |
650 | 665 |
651 dev->qmi.devclass = devclass; | 666 dev->qmi.devclass = devclass; |
652 | 667 |
653 kref_init(&dev->refcount); | 668 kref_init(&dev->refcount); |
654 INIT_LIST_HEAD(&dev->node); | 669 INIT_LIST_HEAD(&dev->node); |
655 INIT_LIST_HEAD(&dev->qmi.clients); | 670 INIT_LIST_HEAD(&dev->qmi.clients); |
656 » init_completion(&dev->worker.work); | 671 » atomic_set(&dev->worker.work_count, 0); |
| 672 » init_waitqueue_head(&dev->worker.waitq); |
657 spin_lock_init(&dev->qmi.clients_lock); | 673 spin_lock_init(&dev->qmi.clients_lock); |
658 | 674 |
659 dev->down = 0; | 675 dev->down = 0; |
660 qc_setdown(dev, DOWN_NO_NDIS_CONNECTION); | 676 qc_setdown(dev, DOWN_NO_NDIS_CONNECTION); |
661 qc_setdown(dev, DOWN_NET_IFACE_STOPPED); | 677 qc_setdown(dev, DOWN_NET_IFACE_STOPPED); |
662 | 678 |
663 status = qc_register(dev); | 679 status = qc_register(dev); |
664 if (status) { | 680 if (status) { |
665 qc_deregister(dev); | 681 qc_deregister(dev); |
666 } else { | 682 } else { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
710 } | 726 } |
711 module_exit(modexit); | 727 module_exit(modexit); |
712 | 728 |
713 MODULE_VERSION(DRIVER_VERSION); | 729 MODULE_VERSION(DRIVER_VERSION); |
714 MODULE_AUTHOR(DRIVER_AUTHOR); | 730 MODULE_AUTHOR(DRIVER_AUTHOR); |
715 MODULE_DESCRIPTION(DRIVER_DESC); | 731 MODULE_DESCRIPTION(DRIVER_DESC); |
716 MODULE_LICENSE("Dual BSD/GPL"); | 732 MODULE_LICENSE("Dual BSD/GPL"); |
717 | 733 |
718 module_param(qcusbnet_debug, bool, S_IRUGO | S_IWUSR); | 734 module_param(qcusbnet_debug, bool, S_IRUGO | S_IWUSR); |
719 MODULE_PARM_DESC(qcusbnet_debug, "Debugging enabled or not"); | 735 MODULE_PARM_DESC(qcusbnet_debug, "Debugging enabled or not"); |
OLD | NEW |