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 | ||
b8868e45 | 20 | #include <linux/kthread.h> |
8e336a72 | 21 | #include <linux/export.h> |
7aaacb43 | 22 | |
0f79847c | 23 | #include "usbip_common.h" |
05a1f28e TH |
24 | |
25 | static int event_handler(struct usbip_device *ud) | |
26 | { | |
b8868e45 | 27 | usbip_dbg_eh("enter\n"); |
05a1f28e TH |
28 | |
29 | /* | |
30 | * Events are handled by only this thread. | |
31 | */ | |
b8868e45 BM |
32 | while (usbip_event_happened(ud)) { |
33 | usbip_dbg_eh("pending event %lx\n", ud->event); | |
05a1f28e TH |
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); | |
05a1f28e | 41 | ud->event &= ~USBIP_EH_SHUTDOWN; |
05a1f28e TH |
42 | } |
43 | ||
05a1f28e TH |
44 | /* Reset the device. */ |
45 | if (ud->event & USBIP_EH_RESET) { | |
46 | ud->eh_ops.reset(ud); | |
05a1f28e | 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); | |
05a1f28e | 53 | ud->event &= ~USBIP_EH_UNUSABLE; |
05a1f28e TH |
54 | } |
55 | ||
584c5b7c MV |
56 | /* Stop the error handler. */ |
57 | if (ud->event & USBIP_EH_BYE) | |
58 | return -1; | |
05a1f28e TH |
59 | } |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
9720b4bc | 64 | static int event_handler_loop(void *data) |
05a1f28e | 65 | { |
9720b4bc | 66 | struct usbip_device *ud = data; |
05a1f28e | 67 | |
9720b4bc AB |
68 | while (!kthread_should_stop()) { |
69 | wait_event_interruptible(ud->eh_waitq, | |
0f79847c | 70 | usbip_event_happened(ud) || |
71 | kthread_should_stop()); | |
9720b4bc | 72 | usbip_dbg_eh("wakeup\n"); |
05a1f28e TH |
73 | |
74 | if (event_handler(ud) < 0) | |
75 | break; | |
05a1f28e | 76 | } |
0f79847c | 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)) { | |
ff451370 | 88 | pr_warn("Unable to start control thread\n"); |
9720b4bc | 89 | return PTR_ERR(ud->eh); |
b8868e45 | 90 | } |
0f79847c | 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 | { | |
dcf14779 HY |
108 | unsigned long flags; |
109 | ||
110 | spin_lock_irqsave(&ud->lock, flags); | |
05a1f28e | 111 | ud->event |= event; |
05a1f28e | 112 | wake_up(&ud->eh_waitq); |
dcf14779 | 113 | spin_unlock_irqrestore(&ud->lock, flags); |
05a1f28e TH |
114 | } |
115 | EXPORT_SYMBOL_GPL(usbip_event_add); | |
116 | ||
b8868e45 | 117 | int usbip_event_happened(struct usbip_device *ud) |
05a1f28e | 118 | { |
b8868e45 | 119 | int happened = 0; |
05a1f28e TH |
120 | |
121 | spin_lock(&ud->lock); | |
05a1f28e | 122 | if (ud->event != 0) |
b8868e45 | 123 | happened = 1; |
05a1f28e TH |
124 | spin_unlock(&ud->lock); |
125 | ||
b8868e45 | 126 | return happened; |
05a1f28e | 127 | } |
b8868e45 | 128 | EXPORT_SYMBOL_GPL(usbip_event_happened); |