linux/drivers/staging/usbip/usbip_event.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2003-2008 Takahiro Hirofuchi
   3 *
   4 * This is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 * This is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write to the Free Software
  16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17 * USA.
  18 */
  19
  20#include "usbip_common.h"
  21#include <linux/kthread.h>
  22
  23static int event_handler(struct usbip_device *ud)
  24{
  25        usbip_dbg_eh("enter\n");
  26
  27        /*
  28         * Events are handled by only this thread.
  29         */
  30        while (usbip_event_happened(ud)) {
  31                usbip_dbg_eh("pending event %lx\n", ud->event);
  32
  33                /*
  34                 * NOTE: shutdown must come first.
  35                 * Shutdown the device.
  36                 */
  37                if (ud->event & USBIP_EH_SHUTDOWN) {
  38                        ud->eh_ops.shutdown(ud);
  39
  40                        ud->event &= ~USBIP_EH_SHUTDOWN;
  41
  42                        break;
  43                }
  44
  45                /* Stop the error handler. */
  46                if (ud->event & USBIP_EH_BYE)
  47                        return -1;
  48
  49                /* Reset the device. */
  50                if (ud->event & USBIP_EH_RESET) {
  51                        ud->eh_ops.reset(ud);
  52
  53                        ud->event &= ~USBIP_EH_RESET;
  54
  55                        break;
  56                }
  57
  58                /* Mark the device as unusable. */
  59                if (ud->event & USBIP_EH_UNUSABLE) {
  60                        ud->eh_ops.unusable(ud);
  61
  62                        ud->event &= ~USBIP_EH_UNUSABLE;
  63
  64                        break;
  65                }
  66
  67                /* NOTREACHED */
  68                printk(KERN_ERR "%s: unknown event\n", __func__);
  69                return -1;
  70        }
  71
  72        return 0;
  73}
  74
  75static void event_handler_loop(struct usbip_task *ut)
  76{
  77        struct usbip_device *ud = container_of(ut, struct usbip_device, eh);
  78
  79        while (1) {
  80                if (signal_pending(current)) {
  81                        usbip_dbg_eh("signal catched!\n");
  82                        break;
  83                }
  84
  85                if (event_handler(ud) < 0)
  86                        break;
  87
  88                wait_event_interruptible(ud->eh_waitq,
  89                                        usbip_event_happened(ud));
  90                usbip_dbg_eh("wakeup\n");
  91        }
  92}
  93
  94int usbip_start_eh(struct usbip_device *ud)
  95{
  96        struct usbip_task *eh = &ud->eh;
  97        struct task_struct *th;
  98
  99        init_waitqueue_head(&ud->eh_waitq);
 100        ud->event = 0;
 101
 102        usbip_task_init(eh, "usbip_eh", event_handler_loop);
 103
 104        th = kthread_run(usbip_thread, (void *)eh, "usbip");
 105        if (IS_ERR(th)) {
 106                printk(KERN_WARNING
 107                        "Unable to start control thread\n");
 108                return PTR_ERR(th);
 109        }
 110
 111        wait_for_completion(&eh->thread_done);
 112        return 0;
 113}
 114EXPORT_SYMBOL_GPL(usbip_start_eh);
 115
 116void usbip_stop_eh(struct usbip_device *ud)
 117{
 118        struct usbip_task *eh = &ud->eh;
 119
 120        wait_for_completion(&eh->thread_done);
 121        usbip_dbg_eh("usbip_eh has finished\n");
 122}
 123EXPORT_SYMBOL_GPL(usbip_stop_eh);
 124
 125void usbip_event_add(struct usbip_device *ud, unsigned long event)
 126{
 127        spin_lock(&ud->lock);
 128
 129        ud->event |= event;
 130
 131        wake_up(&ud->eh_waitq);
 132
 133        spin_unlock(&ud->lock);
 134}
 135EXPORT_SYMBOL_GPL(usbip_event_add);
 136
 137int usbip_event_happened(struct usbip_device *ud)
 138{
 139        int happened = 0;
 140
 141        spin_lock(&ud->lock);
 142
 143        if (ud->event != 0)
 144                happened = 1;
 145
 146        spin_unlock(&ud->lock);
 147
 148        return happened;
 149}
 150EXPORT_SYMBOL_GPL(usbip_event_happened);
 151