Commit | Line | Data |
---|---|---|
05a1f28e TH |
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" | |
b8868e45 | 21 | #include <linux/kthread.h> |
05a1f28e TH |
22 | |
23 | static int event_handler(struct usbip_device *ud) | |
24 | { | |
b8868e45 | 25 | usbip_dbg_eh("enter\n"); |
05a1f28e TH |
26 | |
27 | /* | |
28 | * Events are handled by only this thread. | |
29 | */ | |
b8868e45 BM |
30 | while (usbip_event_happened(ud)) { |
31 | usbip_dbg_eh("pending event %lx\n", ud->event); | |
05a1f28e TH |
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; | |
05a1f28e TH |
41 | } |
42 | ||
05a1f28e TH |
43 | /* Reset the device. */ |
44 | if (ud->event & USBIP_EH_RESET) { | |
45 | ud->eh_ops.reset(ud); | |
46 | ||
47 | ud->event &= ~USBIP_EH_RESET; | |
05a1f28e TH |
48 | } |
49 | ||
50 | /* Mark the device as unusable. */ | |
51 | if (ud->event & USBIP_EH_UNUSABLE) { | |
52 | ud->eh_ops.unusable(ud); | |
53 | ||
54 | ud->event &= ~USBIP_EH_UNUSABLE; | |
05a1f28e TH |
55 | } |
56 | ||
584c5b7c MV |
57 | /* Stop the error handler. */ |
58 | if (ud->event & USBIP_EH_BYE) | |
59 | return -1; | |
05a1f28e TH |
60 | } |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
9720b4bc | 65 | static int event_handler_loop(void *data) |
05a1f28e | 66 | { |
9720b4bc | 67 | struct usbip_device *ud = data; |
05a1f28e | 68 | |
9720b4bc AB |
69 | while (!kthread_should_stop()) { |
70 | wait_event_interruptible(ud->eh_waitq, | |
71 | usbip_event_happened(ud) || | |
72 | kthread_should_stop()); | |
73 | usbip_dbg_eh("wakeup\n"); | |
05a1f28e TH |
74 | |
75 | if (event_handler(ud) < 0) | |
76 | break; | |
05a1f28e | 77 | } |
9720b4bc | 78 | return 0; |
05a1f28e TH |
79 | } |
80 | ||
b8868e45 | 81 | int usbip_start_eh(struct usbip_device *ud) |
05a1f28e | 82 | { |
05a1f28e TH |
83 | init_waitqueue_head(&ud->eh_waitq); |
84 | ud->event = 0; | |
85 | ||
9720b4bc AB |
86 | ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh"); |
87 | if (IS_ERR(ud->eh)) { | |
b8868e45 BM |
88 | printk(KERN_WARNING |
89 | "Unable to start control thread\n"); | |
9720b4bc | 90 | return PTR_ERR(ud->eh); |
b8868e45 | 91 | } |
b8868e45 | 92 | return 0; |
05a1f28e TH |
93 | } |
94 | EXPORT_SYMBOL_GPL(usbip_start_eh); | |
95 | ||
96 | void usbip_stop_eh(struct usbip_device *ud) | |
97 | { | |
9720b4bc | 98 | if (ud->eh == current) |
d01f42a2 EL |
99 | return; /* do not wait for myself */ |
100 | ||
9720b4bc | 101 | kthread_stop(ud->eh); |
b8868e45 | 102 | usbip_dbg_eh("usbip_eh has finished\n"); |
05a1f28e TH |
103 | } |
104 | EXPORT_SYMBOL_GPL(usbip_stop_eh); | |
105 | ||
106 | void usbip_event_add(struct usbip_device *ud, unsigned long event) | |
107 | { | |
108 | spin_lock(&ud->lock); | |
109 | ||
110 | ud->event |= event; | |
111 | ||
112 | wake_up(&ud->eh_waitq); | |
113 | ||
114 | spin_unlock(&ud->lock); | |
115 | } | |
116 | EXPORT_SYMBOL_GPL(usbip_event_add); | |
117 | ||
b8868e45 | 118 | int usbip_event_happened(struct usbip_device *ud) |
05a1f28e | 119 | { |
b8868e45 | 120 | int happened = 0; |
05a1f28e TH |
121 | |
122 | spin_lock(&ud->lock); | |
123 | ||
124 | if (ud->event != 0) | |
b8868e45 | 125 | happened = 1; |
05a1f28e TH |
126 | |
127 | spin_unlock(&ud->lock); | |
128 | ||
b8868e45 | 129 | return happened; |
05a1f28e | 130 | } |
b8868e45 | 131 | EXPORT_SYMBOL_GPL(usbip_event_happened); |