diff options
Diffstat (limited to 'drivers/usb/usbip/usbip_event.c')
-rw-r--r-- | drivers/usb/usbip/usbip_event.c | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c new file mode 100644 index 000000000000..64933b993d7a --- /dev/null +++ b/drivers/usb/usbip/usbip_event.c | |||
@@ -0,0 +1,128 @@ | |||
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 <linux/kthread.h> | ||
21 | #include <linux/export.h> | ||
22 | |||
23 | #include "usbip_common.h" | ||
24 | |||
25 | static int event_handler(struct usbip_device *ud) | ||
26 | { | ||
27 | usbip_dbg_eh("enter\n"); | ||
28 | |||
29 | /* | ||
30 | * Events are handled by only this thread. | ||
31 | */ | ||
32 | while (usbip_event_happened(ud)) { | ||
33 | usbip_dbg_eh("pending event %lx\n", ud->event); | ||
34 | |||
35 | /* | ||
36 | * NOTE: shutdown must come first. | ||
37 | * Shutdown the device. | ||
38 | */ | ||
39 | if (ud->event & USBIP_EH_SHUTDOWN) { | ||
40 | ud->eh_ops.shutdown(ud); | ||
41 | ud->event &= ~USBIP_EH_SHUTDOWN; | ||
42 | } | ||
43 | |||
44 | /* Reset the device. */ | ||
45 | if (ud->event & USBIP_EH_RESET) { | ||
46 | ud->eh_ops.reset(ud); | ||
47 | ud->event &= ~USBIP_EH_RESET; | ||
48 | } | ||
49 | |||
50 | /* Mark the device as unusable. */ | ||
51 | if (ud->event & USBIP_EH_UNUSABLE) { | ||
52 | ud->eh_ops.unusable(ud); | ||
53 | ud->event &= ~USBIP_EH_UNUSABLE; | ||
54 | } | ||
55 | |||
56 | /* Stop the error handler. */ | ||
57 | if (ud->event & USBIP_EH_BYE) | ||
58 | return -1; | ||
59 | } | ||
60 | |||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static int event_handler_loop(void *data) | ||
65 | { | ||
66 | struct usbip_device *ud = data; | ||
67 | |||
68 | while (!kthread_should_stop()) { | ||
69 | wait_event_interruptible(ud->eh_waitq, | ||
70 | usbip_event_happened(ud) || | ||
71 | kthread_should_stop()); | ||
72 | usbip_dbg_eh("wakeup\n"); | ||
73 | |||
74 | if (event_handler(ud) < 0) | ||
75 | break; | ||
76 | } | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | int usbip_start_eh(struct usbip_device *ud) | ||
82 | { | ||
83 | init_waitqueue_head(&ud->eh_waitq); | ||
84 | ud->event = 0; | ||
85 | |||
86 | ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh"); | ||
87 | if (IS_ERR(ud->eh)) { | ||
88 | pr_warn("Unable to start control thread\n"); | ||
89 | return PTR_ERR(ud->eh); | ||
90 | } | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | EXPORT_SYMBOL_GPL(usbip_start_eh); | ||
95 | |||
96 | void usbip_stop_eh(struct usbip_device *ud) | ||
97 | { | ||
98 | if (ud->eh == current) | ||
99 | return; /* do not wait for myself */ | ||
100 | |||
101 | kthread_stop(ud->eh); | ||
102 | usbip_dbg_eh("usbip_eh has finished\n"); | ||
103 | } | ||
104 | EXPORT_SYMBOL_GPL(usbip_stop_eh); | ||
105 | |||
106 | void usbip_event_add(struct usbip_device *ud, unsigned long event) | ||
107 | { | ||
108 | unsigned long flags; | ||
109 | |||
110 | spin_lock_irqsave(&ud->lock, flags); | ||
111 | ud->event |= event; | ||
112 | wake_up(&ud->eh_waitq); | ||
113 | spin_unlock_irqrestore(&ud->lock, flags); | ||
114 | } | ||
115 | EXPORT_SYMBOL_GPL(usbip_event_add); | ||
116 | |||
117 | int usbip_event_happened(struct usbip_device *ud) | ||
118 | { | ||
119 | int happened = 0; | ||
120 | |||
121 | spin_lock(&ud->lock); | ||
122 | if (ud->event != 0) | ||
123 | happened = 1; | ||
124 | spin_unlock(&ud->lock); | ||
125 | |||
126 | return happened; | ||
127 | } | ||
128 | EXPORT_SYMBOL_GPL(usbip_event_happened); | ||