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; | |
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 | ||
75 | static 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)) { | |
b8868e45 | 81 | usbip_dbg_eh("signal catched!\n"); |
05a1f28e TH |
82 | break; |
83 | } | |
84 | ||
85 | if (event_handler(ud) < 0) | |
86 | break; | |
87 | ||
b8868e45 BM |
88 | wait_event_interruptible(ud->eh_waitq, |
89 | usbip_event_happened(ud)); | |
90 | usbip_dbg_eh("wakeup\n"); | |
05a1f28e TH |
91 | } |
92 | } | |
93 | ||
b8868e45 | 94 | int usbip_start_eh(struct usbip_device *ud) |
05a1f28e TH |
95 | { |
96 | struct usbip_task *eh = &ud->eh; | |
b8868e45 | 97 | struct task_struct *th; |
05a1f28e 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 | ||
b8868e45 BM |
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 | } | |
05a1f28e TH |
110 | |
111 | wait_for_completion(&eh->thread_done); | |
b8868e45 | 112 | return 0; |
05a1f28e TH |
113 | } |
114 | EXPORT_SYMBOL_GPL(usbip_start_eh); | |
115 | ||
116 | void usbip_stop_eh(struct usbip_device *ud) | |
117 | { | |
118 | struct usbip_task *eh = &ud->eh; | |
119 | ||
d01f42a2 EL |
120 | if (eh->thread == current) |
121 | return; /* do not wait for myself */ | |
122 | ||
05a1f28e | 123 | wait_for_completion(&eh->thread_done); |
b8868e45 | 124 | usbip_dbg_eh("usbip_eh has finished\n"); |
05a1f28e TH |
125 | } |
126 | EXPORT_SYMBOL_GPL(usbip_stop_eh); | |
127 | ||
128 | void usbip_event_add(struct usbip_device *ud, unsigned long event) | |
129 | { | |
130 | spin_lock(&ud->lock); | |
131 | ||
132 | ud->event |= event; | |
133 | ||
134 | wake_up(&ud->eh_waitq); | |
135 | ||
136 | spin_unlock(&ud->lock); | |
137 | } | |
138 | EXPORT_SYMBOL_GPL(usbip_event_add); | |
139 | ||
b8868e45 | 140 | int usbip_event_happened(struct usbip_device *ud) |
05a1f28e | 141 | { |
b8868e45 | 142 | int happened = 0; |
05a1f28e TH |
143 | |
144 | spin_lock(&ud->lock); | |
145 | ||
146 | if (ud->event != 0) | |
b8868e45 | 147 | happened = 1; |
05a1f28e TH |
148 | |
149 | spin_unlock(&ud->lock); | |
150 | ||
b8868e45 | 151 | return happened; |
05a1f28e | 152 | } |
b8868e45 | 153 | EXPORT_SYMBOL_GPL(usbip_event_happened); |