Commit | Line | Data |
---|---|---|
a00828e9 PZ |
1 | /* |
2 | * libusual | |
3 | * | |
4 | * The libusual contains the table of devices common for ub and usb-storage. | |
5 | */ | |
6 | #include <linux/kernel.h> | |
7 | #include <linux/module.h> | |
8 | #include <linux/usb.h> | |
9 | #include <linux/usb_usual.h> | |
10 | #include <linux/vmalloc.h> | |
0e3c8c26 | 11 | #include <linux/kthread.h> |
a00828e9 PZ |
12 | |
13 | /* | |
14 | */ | |
15 | #define USU_MOD_FL_THREAD 1 /* Thread is running */ | |
16 | #define USU_MOD_FL_PRESENT 2 /* The module is loaded */ | |
17 | ||
18 | struct mod_status { | |
19 | unsigned long fls; | |
20 | }; | |
21 | ||
22 | static struct mod_status stat[3]; | |
23 | static DEFINE_SPINLOCK(usu_lock); | |
24 | ||
25 | /* | |
26 | */ | |
27 | #define USB_US_DEFAULT_BIAS USB_US_TYPE_STOR | |
5ba35bd8 | 28 | static atomic_t usu_bias = ATOMIC_INIT(USB_US_DEFAULT_BIAS); |
a00828e9 PZ |
29 | |
30 | #define BIAS_NAME_SIZE (sizeof("usb-storage")) | |
a00828e9 PZ |
31 | static const char *bias_names[3] = { "none", "usb-storage", "ub" }; |
32 | ||
04fc8bbc | 33 | static struct semaphore usu_init_notify; |
a00828e9 PZ |
34 | static DECLARE_COMPLETION(usu_end_notify); |
35 | static atomic_t total_threads = ATOMIC_INIT(0); | |
36 | ||
37 | static int usu_probe_thread(void *arg); | |
a00828e9 PZ |
38 | |
39 | /* | |
40 | * The table. | |
41 | */ | |
42 | #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ | |
43 | vendorName, productName,useProtocol, useTransport, \ | |
44 | initFunction, flags) \ | |
45 | { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax), \ | |
46 | .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } | |
47 | ||
48 | #define USUAL_DEV(useProto, useTrans, useType) \ | |
49 | { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \ | |
50 | .driver_info = ((useType)<<24) } | |
51 | ||
52 | struct usb_device_id storage_usb_ids [] = { | |
53 | # include "unusual_devs.h" | |
54 | { } /* Terminating entry */ | |
55 | }; | |
56 | ||
57 | #undef USUAL_DEV | |
58 | #undef UNUSUAL_DEV | |
59 | ||
60 | MODULE_DEVICE_TABLE(usb, storage_usb_ids); | |
61 | EXPORT_SYMBOL_GPL(storage_usb_ids); | |
62 | ||
63 | /* | |
64 | * @type: the module type as an integer | |
65 | */ | |
66 | void usb_usual_set_present(int type) | |
67 | { | |
68 | struct mod_status *st; | |
69 | unsigned long flags; | |
70 | ||
71 | if (type <= 0 || type >= 3) | |
72 | return; | |
73 | st = &stat[type]; | |
74 | spin_lock_irqsave(&usu_lock, flags); | |
75 | st->fls |= USU_MOD_FL_PRESENT; | |
76 | spin_unlock_irqrestore(&usu_lock, flags); | |
77 | } | |
78 | EXPORT_SYMBOL_GPL(usb_usual_set_present); | |
79 | ||
80 | void usb_usual_clear_present(int type) | |
81 | { | |
82 | struct mod_status *st; | |
83 | unsigned long flags; | |
84 | ||
85 | if (type <= 0 || type >= 3) | |
86 | return; | |
87 | st = &stat[type]; | |
88 | spin_lock_irqsave(&usu_lock, flags); | |
89 | st->fls &= ~USU_MOD_FL_PRESENT; | |
90 | spin_unlock_irqrestore(&usu_lock, flags); | |
91 | } | |
92 | EXPORT_SYMBOL_GPL(usb_usual_clear_present); | |
93 | ||
94 | /* | |
95 | * Match the calling driver type against the table. | |
96 | * Returns: 0 if the device matches. | |
97 | */ | |
98 | int usb_usual_check_type(const struct usb_device_id *id, int caller_type) | |
99 | { | |
100 | int id_type = USB_US_TYPE(id->driver_info); | |
101 | ||
102 | if (caller_type <= 0 || caller_type >= 3) | |
103 | return -EINVAL; | |
104 | ||
105 | /* Drivers grab fixed assignment devices */ | |
106 | if (id_type == caller_type) | |
107 | return 0; | |
108 | /* Drivers grab devices biased to them */ | |
5ba35bd8 | 109 | if (id_type == USB_US_TYPE_NONE && caller_type == atomic_read(&usu_bias)) |
a00828e9 PZ |
110 | return 0; |
111 | return -ENODEV; | |
112 | } | |
113 | EXPORT_SYMBOL_GPL(usb_usual_check_type); | |
114 | ||
115 | /* | |
116 | */ | |
117 | static int usu_probe(struct usb_interface *intf, | |
118 | const struct usb_device_id *id) | |
119 | { | |
64e35d92 | 120 | int rc; |
6d453b9e | 121 | unsigned long type; |
0e3c8c26 | 122 | struct task_struct* task; |
a00828e9 PZ |
123 | unsigned long flags; |
124 | ||
125 | type = USB_US_TYPE(id->driver_info); | |
126 | if (type == 0) | |
5ba35bd8 | 127 | type = atomic_read(&usu_bias); |
a00828e9 PZ |
128 | |
129 | spin_lock_irqsave(&usu_lock, flags); | |
130 | if ((stat[type].fls & (USU_MOD_FL_THREAD|USU_MOD_FL_PRESENT)) != 0) { | |
131 | spin_unlock_irqrestore(&usu_lock, flags); | |
132 | return -ENXIO; | |
133 | } | |
134 | stat[type].fls |= USU_MOD_FL_THREAD; | |
135 | spin_unlock_irqrestore(&usu_lock, flags); | |
136 | ||
0e3c8c26 MD |
137 | task = kthread_run(usu_probe_thread, (void*)type, "libusual_%d", type); |
138 | if (IS_ERR(task)) { | |
64e35d92 | 139 | rc = PTR_ERR(task); |
a00828e9 PZ |
140 | printk(KERN_WARNING "libusual: " |
141 | "Unable to start the thread for %s: %d\n", | |
142 | bias_names[type], rc); | |
143 | spin_lock_irqsave(&usu_lock, flags); | |
144 | stat[type].fls &= ~USU_MOD_FL_THREAD; | |
145 | spin_unlock_irqrestore(&usu_lock, flags); | |
146 | return rc; /* Not being -ENXIO causes a message printed */ | |
147 | } | |
148 | atomic_inc(&total_threads); | |
149 | ||
150 | return -ENXIO; | |
151 | } | |
152 | ||
153 | static void usu_disconnect(struct usb_interface *intf) | |
154 | { | |
155 | ; /* We should not be here. */ | |
156 | } | |
157 | ||
158 | static struct usb_driver usu_driver = { | |
a00828e9 PZ |
159 | .name = "libusual", |
160 | .probe = usu_probe, | |
161 | .disconnect = usu_disconnect, | |
162 | .id_table = storage_usb_ids, | |
163 | }; | |
164 | ||
165 | /* | |
166 | * A whole new thread for a purpose of request_module seems quite stupid. | |
167 | * The request_module forks once inside again. However, if we attempt | |
168 | * to load a storage module from our own modprobe thread, that module | |
169 | * references our symbols, which cannot be resolved until our module is | |
170 | * initialized. I wish there was a way to wait for the end of initialization. | |
171 | * The module notifier reports MODULE_STATE_COMING only. | |
172 | * So, we wait until module->init ends as the next best thing. | |
173 | */ | |
174 | static int usu_probe_thread(void *arg) | |
175 | { | |
176 | int type = (unsigned long) arg; | |
177 | struct mod_status *st = &stat[type]; | |
178 | int rc; | |
179 | unsigned long flags; | |
180 | ||
a00828e9 PZ |
181 | /* A completion does not work here because it's counted. */ |
182 | down(&usu_init_notify); | |
183 | up(&usu_init_notify); | |
184 | ||
185 | rc = request_module(bias_names[type]); | |
186 | spin_lock_irqsave(&usu_lock, flags); | |
187 | if (rc == 0 && (st->fls & USU_MOD_FL_PRESENT) == 0) { | |
188 | /* | |
189 | * This should not happen, but let us keep tabs on it. | |
190 | */ | |
191 | printk(KERN_NOTICE "libusual: " | |
192 | "modprobe for %s succeeded, but module is not present\n", | |
193 | bias_names[type]); | |
194 | } | |
195 | st->fls &= ~USU_MOD_FL_THREAD; | |
196 | spin_unlock_irqrestore(&usu_lock, flags); | |
197 | ||
198 | complete_and_exit(&usu_end_notify, 0); | |
199 | } | |
200 | ||
201 | /* | |
202 | */ | |
203 | static int __init usb_usual_init(void) | |
204 | { | |
205 | int rc; | |
206 | ||
04fc8bbc CH |
207 | sema_init(&usu_init_notify, 0); |
208 | ||
a00828e9 PZ |
209 | rc = usb_register(&usu_driver); |
210 | up(&usu_init_notify); | |
211 | return rc; | |
212 | } | |
213 | ||
214 | static void __exit usb_usual_exit(void) | |
215 | { | |
216 | /* | |
217 | * We do not check for any drivers present, because | |
218 | * they keep us pinned with symbol references. | |
219 | */ | |
220 | ||
221 | usb_deregister(&usu_driver); | |
222 | ||
223 | while (atomic_read(&total_threads) > 0) { | |
224 | wait_for_completion(&usu_end_notify); | |
225 | atomic_dec(&total_threads); | |
226 | } | |
227 | } | |
228 | ||
229 | /* | |
230 | * Validate and accept the bias parameter. | |
a00828e9 | 231 | */ |
5ba35bd8 | 232 | static int usu_set_bias(const char *bias_s, struct kernel_param *kp) |
a00828e9 PZ |
233 | { |
234 | int i; | |
5ba35bd8 | 235 | int len; |
a00828e9 PZ |
236 | int bias_n = 0; |
237 | ||
5ba35bd8 PZ |
238 | len = strlen(bias_s); |
239 | if (len == 0) | |
240 | return -EDOM; | |
241 | if (bias_s[len-1] == '\n') | |
242 | --len; | |
243 | ||
244 | for (i = 1; i < 3; i++) { | |
245 | if (strncmp(bias_s, bias_names[i], len) == 0) { | |
246 | bias_n = i; | |
247 | break; | |
a00828e9 PZ |
248 | } |
249 | } | |
5ba35bd8 PZ |
250 | if (bias_n == 0) |
251 | return -EINVAL; | |
252 | ||
253 | atomic_set(&usu_bias, bias_n); | |
254 | return 0; | |
255 | } | |
256 | ||
257 | static int usu_get_bias(char *buffer, struct kernel_param *kp) | |
258 | { | |
259 | return strlen(strcpy(buffer, bias_names[atomic_read(&usu_bias)])); | |
a00828e9 PZ |
260 | } |
261 | ||
262 | module_init(usb_usual_init); | |
263 | module_exit(usb_usual_exit); | |
264 | ||
5ba35bd8 PZ |
265 | module_param_call(bias, usu_set_bias, usu_get_bias, NULL, S_IRUGO|S_IWUSR); |
266 | __MODULE_PARM_TYPE(bias, "string"); | |
a00828e9 PZ |
267 | MODULE_PARM_DESC(bias, "Bias to usb-storage or ub"); |
268 | ||
269 | MODULE_LICENSE("GPL"); |