Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * The USB Monitor, inspired by Dave Harding's USBMon. | |
3 | * | |
4 | * mon_main.c: Main file, module initiation and exit, registrations, etc. | |
da5ca008 PZ |
5 | * |
6 | * Copyright (C) 2005 Pete Zaitcev (zaitcev@redhat.com) | |
1da177e4 LT |
7 | */ |
8 | ||
9 | #include <linux/kernel.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/usb.h> | |
27729aad | 12 | #include <linux/usb/hcd.h> |
5a0e3ad6 | 13 | #include <linux/slab.h> |
72adaa96 | 14 | #include <linux/notifier.h> |
4186ecf8 | 15 | #include <linux/mutex.h> |
1da177e4 LT |
16 | |
17 | #include "usb_mon.h" | |
27729aad | 18 | |
1da177e4 | 19 | |
1da177e4 LT |
20 | static void mon_stop(struct mon_bus *mbus); |
21 | static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus); | |
22 | static void mon_bus_drop(struct kref *r); | |
6f23ee1f | 23 | static void mon_bus_init(struct usb_bus *ubus); |
1da177e4 | 24 | |
4186ecf8 | 25 | DEFINE_MUTEX(mon_lock); |
1da177e4 | 26 | |
ecb658d3 | 27 | struct mon_bus mon_bus0; /* Pseudo bus meaning "all buses" */ |
1da177e4 LT |
28 | static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ |
29 | ||
30 | /* | |
31 | * Link a reader into the bus. | |
32 | * | |
33 | * This must be called with mon_lock taken because of mbus->ref. | |
34 | */ | |
35 | void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r) | |
36 | { | |
37 | unsigned long flags; | |
ecb658d3 | 38 | struct list_head *p; |
1da177e4 LT |
39 | |
40 | spin_lock_irqsave(&mbus->lock, flags); | |
41 | if (mbus->nreaders == 0) { | |
ecb658d3 PZ |
42 | if (mbus == &mon_bus0) { |
43 | list_for_each (p, &mon_buses) { | |
44 | struct mon_bus *m1; | |
45 | m1 = list_entry(p, struct mon_bus, bus_link); | |
46 | m1->u_bus->monitored = 1; | |
47 | } | |
48 | } else { | |
49 | mbus->u_bus->monitored = 1; | |
1da177e4 | 50 | } |
1da177e4 LT |
51 | } |
52 | mbus->nreaders++; | |
53 | list_add_tail(&r->r_link, &mbus->r_list); | |
54 | spin_unlock_irqrestore(&mbus->lock, flags); | |
55 | ||
56 | kref_get(&mbus->ref); | |
57 | } | |
58 | ||
59 | /* | |
60 | * Unlink reader from the bus. | |
61 | * | |
62 | * This is called with mon_lock taken, so we can decrement mbus->ref. | |
63 | */ | |
64 | void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r) | |
65 | { | |
66 | unsigned long flags; | |
67 | ||
68 | spin_lock_irqsave(&mbus->lock, flags); | |
69 | list_del(&r->r_link); | |
70 | --mbus->nreaders; | |
71 | if (mbus->nreaders == 0) | |
72 | mon_stop(mbus); | |
73 | spin_unlock_irqrestore(&mbus->lock, flags); | |
74 | ||
75 | kref_put(&mbus->ref, mon_bus_drop); | |
76 | } | |
77 | ||
78 | /* | |
79 | */ | |
ecb658d3 | 80 | static void mon_bus_submit(struct mon_bus *mbus, struct urb *urb) |
1da177e4 | 81 | { |
1da177e4 LT |
82 | unsigned long flags; |
83 | struct list_head *pos; | |
84 | struct mon_reader *r; | |
85 | ||
1da177e4 | 86 | spin_lock_irqsave(&mbus->lock, flags); |
5b1c674d | 87 | mbus->cnt_events++; |
1da177e4 LT |
88 | list_for_each (pos, &mbus->r_list) { |
89 | r = list_entry(pos, struct mon_reader, r_link); | |
90 | r->rnf_submit(r->r_data, urb); | |
91 | } | |
1da177e4 | 92 | spin_unlock_irqrestore(&mbus->lock, flags); |
ecb658d3 | 93 | } |
1da177e4 | 94 | |
ecb658d3 PZ |
95 | static void mon_submit(struct usb_bus *ubus, struct urb *urb) |
96 | { | |
97 | struct mon_bus *mbus; | |
98 | ||
d4950d5d GKH |
99 | mbus = ubus->mon_bus; |
100 | if (mbus != NULL) | |
ecb658d3 PZ |
101 | mon_bus_submit(mbus, urb); |
102 | mon_bus_submit(&mon_bus0, urb); | |
1da177e4 LT |
103 | } |
104 | ||
105 | /* | |
106 | */ | |
ecb658d3 | 107 | static void mon_bus_submit_error(struct mon_bus *mbus, struct urb *urb, int error) |
1da177e4 | 108 | { |
12e72fea PZ |
109 | unsigned long flags; |
110 | struct list_head *pos; | |
111 | struct mon_reader *r; | |
1da177e4 | 112 | |
12e72fea | 113 | spin_lock_irqsave(&mbus->lock, flags); |
12e72fea PZ |
114 | mbus->cnt_events++; |
115 | list_for_each (pos, &mbus->r_list) { | |
116 | r = list_entry(pos, struct mon_reader, r_link); | |
117 | r->rnf_error(r->r_data, urb, error); | |
118 | } | |
12e72fea | 119 | spin_unlock_irqrestore(&mbus->lock, flags); |
ecb658d3 | 120 | } |
1da177e4 | 121 | |
ecb658d3 PZ |
122 | static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error) |
123 | { | |
124 | struct mon_bus *mbus; | |
125 | ||
d4950d5d GKH |
126 | mbus = ubus->mon_bus; |
127 | if (mbus != NULL) | |
ecb658d3 PZ |
128 | mon_bus_submit_error(mbus, urb, error); |
129 | mon_bus_submit_error(&mon_bus0, urb, error); | |
1da177e4 LT |
130 | } |
131 | ||
132 | /* | |
133 | */ | |
454459b0 | 134 | static void mon_bus_complete(struct mon_bus *mbus, struct urb *urb, int status) |
1da177e4 | 135 | { |
1da177e4 LT |
136 | unsigned long flags; |
137 | struct list_head *pos; | |
138 | struct mon_reader *r; | |
139 | ||
ecb658d3 PZ |
140 | spin_lock_irqsave(&mbus->lock, flags); |
141 | mbus->cnt_events++; | |
142 | list_for_each (pos, &mbus->r_list) { | |
143 | r = list_entry(pos, struct mon_reader, r_link); | |
9347d51c | 144 | r->rnf_complete(r->r_data, urb, status); |
ecb658d3 PZ |
145 | } |
146 | spin_unlock_irqrestore(&mbus->lock, flags); | |
147 | } | |
148 | ||
9347d51c | 149 | static void mon_complete(struct usb_bus *ubus, struct urb *urb, int status) |
ecb658d3 PZ |
150 | { |
151 | struct mon_bus *mbus; | |
152 | ||
d4950d5d GKH |
153 | mbus = ubus->mon_bus; |
154 | if (mbus != NULL) | |
9347d51c AS |
155 | mon_bus_complete(mbus, urb, status); |
156 | mon_bus_complete(&mon_bus0, urb, status); | |
1da177e4 LT |
157 | } |
158 | ||
159 | /* int (*unlink_urb) (struct urb *urb, int status); */ | |
160 | ||
161 | /* | |
162 | * Stop monitoring. | |
1da177e4 LT |
163 | */ |
164 | static void mon_stop(struct mon_bus *mbus) | |
165 | { | |
c36d54ab | 166 | struct usb_bus *ubus; |
ecb658d3 | 167 | struct list_head *p; |
1da177e4 | 168 | |
ecb658d3 PZ |
169 | if (mbus == &mon_bus0) { |
170 | list_for_each (p, &mon_buses) { | |
171 | mbus = list_entry(p, struct mon_bus, bus_link); | |
172 | /* | |
173 | * We do not change nreaders here, so rely on mon_lock. | |
174 | */ | |
175 | if (mbus->nreaders == 0 && (ubus = mbus->u_bus) != NULL) | |
176 | ubus->monitored = 0; | |
177 | } | |
178 | } else { | |
179 | /* | |
180 | * A stop can be called for a dissolved mon_bus in case of | |
181 | * a reader staying across an rmmod foo_hcd, so test ->u_bus. | |
182 | */ | |
183 | if (mon_bus0.nreaders == 0 && (ubus = mbus->u_bus) != NULL) { | |
184 | ubus->monitored = 0; | |
185 | mb(); | |
186 | } | |
1da177e4 LT |
187 | } |
188 | } | |
189 | ||
190 | /* | |
191 | * Add a USB bus (usually by a modprobe foo-hcd) | |
192 | * | |
193 | * This does not return an error code because the core cannot care less | |
194 | * if monitoring is not established. | |
195 | */ | |
196 | static void mon_bus_add(struct usb_bus *ubus) | |
197 | { | |
6f23ee1f | 198 | mon_bus_init(ubus); |
ecb658d3 PZ |
199 | mutex_lock(&mon_lock); |
200 | if (mon_bus0.nreaders != 0) | |
201 | ubus->monitored = 1; | |
202 | mutex_unlock(&mon_lock); | |
1da177e4 LT |
203 | } |
204 | ||
205 | /* | |
206 | * Remove a USB bus (either from rmmod foo-hcd or from a hot-remove event). | |
207 | */ | |
208 | static void mon_bus_remove(struct usb_bus *ubus) | |
209 | { | |
210 | struct mon_bus *mbus = ubus->mon_bus; | |
211 | ||
4186ecf8 | 212 | mutex_lock(&mon_lock); |
1da177e4 | 213 | list_del(&mbus->bus_link); |
6f23ee1f PZ |
214 | if (mbus->text_inited) |
215 | mon_text_del(mbus); | |
ce7cd137 PZ |
216 | if (mbus->bin_inited) |
217 | mon_bin_del(mbus); | |
1da177e4 LT |
218 | |
219 | mon_dissolve(mbus, ubus); | |
220 | kref_put(&mbus->ref, mon_bus_drop); | |
4186ecf8 | 221 | mutex_unlock(&mon_lock); |
1da177e4 LT |
222 | } |
223 | ||
72adaa96 GKH |
224 | static int mon_notify(struct notifier_block *self, unsigned long action, |
225 | void *dev) | |
226 | { | |
227 | switch (action) { | |
228 | case USB_BUS_ADD: | |
229 | mon_bus_add(dev); | |
230 | break; | |
231 | case USB_BUS_REMOVE: | |
232 | mon_bus_remove(dev); | |
233 | } | |
234 | return NOTIFY_OK; | |
235 | } | |
236 | ||
237 | static struct notifier_block mon_nb = { | |
238 | .notifier_call = mon_notify, | |
239 | }; | |
240 | ||
1da177e4 LT |
241 | /* |
242 | * Ops | |
243 | */ | |
6fb8ac81 | 244 | static const struct usb_mon_operations mon_ops_0 = { |
1da177e4 LT |
245 | .urb_submit = mon_submit, |
246 | .urb_submit_error = mon_submit_error, | |
247 | .urb_complete = mon_complete, | |
1da177e4 LT |
248 | }; |
249 | ||
250 | /* | |
251 | * Tear usb_bus and mon_bus apart. | |
252 | */ | |
253 | static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) | |
254 | { | |
255 | ||
1da177e4 | 256 | if (ubus->monitored) { |
1da177e4 LT |
257 | ubus->monitored = 0; |
258 | mb(); | |
259 | } | |
260 | ||
261 | ubus->mon_bus = NULL; | |
262 | mbus->u_bus = NULL; | |
263 | mb(); | |
ecb658d3 PZ |
264 | |
265 | /* We want synchronize_irq() here, but that needs an argument. */ | |
1da177e4 LT |
266 | } |
267 | ||
268 | /* | |
269 | */ | |
270 | static void mon_bus_drop(struct kref *r) | |
271 | { | |
272 | struct mon_bus *mbus = container_of(r, struct mon_bus, ref); | |
273 | kfree(mbus); | |
274 | } | |
275 | ||
276 | /* | |
277 | * Initialize a bus for us: | |
278 | * - allocate mon_bus | |
279 | * - refcount USB bus struct | |
280 | * - link | |
281 | */ | |
6f23ee1f | 282 | static void mon_bus_init(struct usb_bus *ubus) |
1da177e4 | 283 | { |
1da177e4 | 284 | struct mon_bus *mbus; |
1da177e4 | 285 | |
d4950d5d GKH |
286 | mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL); |
287 | if (mbus == NULL) | |
1da177e4 | 288 | goto err_alloc; |
1da177e4 LT |
289 | kref_init(&mbus->ref); |
290 | spin_lock_init(&mbus->lock); | |
291 | INIT_LIST_HEAD(&mbus->r_list); | |
292 | ||
293 | /* | |
17200583 AS |
294 | * We don't need to take a reference to ubus, because we receive |
295 | * a notification if the bus is about to be removed. | |
1da177e4 | 296 | */ |
1da177e4 LT |
297 | mbus->u_bus = ubus; |
298 | ubus->mon_bus = mbus; | |
299 | ||
ce7cd137 PZ |
300 | mbus->text_inited = mon_text_add(mbus, ubus); |
301 | mbus->bin_inited = mon_bin_add(mbus, ubus); | |
1da177e4 | 302 | |
4186ecf8 | 303 | mutex_lock(&mon_lock); |
1da177e4 | 304 | list_add_tail(&mbus->bus_link, &mon_buses); |
4186ecf8 | 305 | mutex_unlock(&mon_lock); |
1da177e4 LT |
306 | return; |
307 | ||
1da177e4 LT |
308 | err_alloc: |
309 | return; | |
310 | } | |
311 | ||
ecb658d3 PZ |
312 | static void mon_bus0_init(void) |
313 | { | |
314 | struct mon_bus *mbus = &mon_bus0; | |
315 | ||
316 | kref_init(&mbus->ref); | |
317 | spin_lock_init(&mbus->lock); | |
318 | INIT_LIST_HEAD(&mbus->r_list); | |
319 | ||
ce7cd137 PZ |
320 | mbus->text_inited = mon_text_add(mbus, NULL); |
321 | mbus->bin_inited = mon_bin_add(mbus, NULL); | |
ecb658d3 PZ |
322 | } |
323 | ||
6f23ee1f PZ |
324 | /* |
325 | * Search a USB bus by number. Notice that USB bus numbers start from one, | |
326 | * which we may later use to identify "all" with zero. | |
327 | * | |
328 | * This function must be called with mon_lock held. | |
329 | * | |
330 | * This is obviously inefficient and may be revised in the future. | |
331 | */ | |
332 | struct mon_bus *mon_bus_lookup(unsigned int num) | |
333 | { | |
334 | struct list_head *p; | |
335 | struct mon_bus *mbus; | |
336 | ||
ecb658d3 PZ |
337 | if (num == 0) { |
338 | return &mon_bus0; | |
339 | } | |
6f23ee1f PZ |
340 | list_for_each (p, &mon_buses) { |
341 | mbus = list_entry(p, struct mon_bus, bus_link); | |
342 | if (mbus->u_bus->busnum == num) { | |
343 | return mbus; | |
344 | } | |
345 | } | |
346 | return NULL; | |
347 | } | |
348 | ||
1da177e4 LT |
349 | static int __init mon_init(void) |
350 | { | |
351 | struct usb_bus *ubus; | |
5363de75 | 352 | int rc, id; |
1da177e4 | 353 | |
6f23ee1f PZ |
354 | if ((rc = mon_text_init()) != 0) |
355 | goto err_text; | |
356 | if ((rc = mon_bin_init()) != 0) | |
357 | goto err_bin; | |
1da177e4 | 358 | |
ecb658d3 PZ |
359 | mon_bus0_init(); |
360 | ||
1da177e4 LT |
361 | if (usb_mon_register(&mon_ops_0) != 0) { |
362 | printk(KERN_NOTICE TAG ": unable to register with the core\n"); | |
6f23ee1f PZ |
363 | rc = -ENODEV; |
364 | goto err_reg; | |
1da177e4 LT |
365 | } |
366 | // MOD_INC_USE_COUNT(which_module?); | |
367 | ||
a4b5d606 | 368 | mutex_lock(&usb_bus_idr_lock); |
5363de75 | 369 | idr_for_each_entry(&usb_bus_idr, ubus, id) |
6f23ee1f | 370 | mon_bus_init(ubus); |
bb4e3b5a | 371 | usb_register_notify(&mon_nb); |
a4b5d606 | 372 | mutex_unlock(&usb_bus_idr_lock); |
1da177e4 | 373 | return 0; |
6f23ee1f PZ |
374 | |
375 | err_reg: | |
376 | mon_bin_exit(); | |
377 | err_bin: | |
378 | mon_text_exit(); | |
379 | err_text: | |
380 | return rc; | |
1da177e4 LT |
381 | } |
382 | ||
383 | static void __exit mon_exit(void) | |
384 | { | |
385 | struct mon_bus *mbus; | |
386 | struct list_head *p; | |
387 | ||
72adaa96 | 388 | usb_unregister_notify(&mon_nb); |
1da177e4 LT |
389 | usb_mon_deregister(); |
390 | ||
4186ecf8 | 391 | mutex_lock(&mon_lock); |
ecb658d3 | 392 | |
1da177e4 LT |
393 | while (!list_empty(&mon_buses)) { |
394 | p = mon_buses.next; | |
395 | mbus = list_entry(p, struct mon_bus, bus_link); | |
396 | list_del(p); | |
397 | ||
6f23ee1f PZ |
398 | if (mbus->text_inited) |
399 | mon_text_del(mbus); | |
ce7cd137 PZ |
400 | if (mbus->bin_inited) |
401 | mon_bin_del(mbus); | |
1da177e4 LT |
402 | |
403 | /* | |
404 | * This never happens, because the open/close paths in | |
405 | * file level maintain module use counters and so rmmod fails | |
406 | * before reaching here. However, better be safe... | |
407 | */ | |
408 | if (mbus->nreaders) { | |
409 | printk(KERN_ERR TAG | |
410 | ": Outstanding opens (%d) on usb%d, leaking...\n", | |
411 | mbus->nreaders, mbus->u_bus->busnum); | |
412 | atomic_set(&mbus->ref.refcount, 2); /* Force leak */ | |
413 | } | |
414 | ||
415 | mon_dissolve(mbus, mbus->u_bus); | |
416 | kref_put(&mbus->ref, mon_bus_drop); | |
417 | } | |
ecb658d3 PZ |
418 | |
419 | mbus = &mon_bus0; | |
420 | if (mbus->text_inited) | |
421 | mon_text_del(mbus); | |
ce7cd137 PZ |
422 | if (mbus->bin_inited) |
423 | mon_bin_del(mbus); | |
ecb658d3 | 424 | |
4186ecf8 | 425 | mutex_unlock(&mon_lock); |
1da177e4 | 426 | |
6f23ee1f PZ |
427 | mon_text_exit(); |
428 | mon_bin_exit(); | |
1da177e4 LT |
429 | } |
430 | ||
431 | module_init(mon_init); | |
432 | module_exit(mon_exit); | |
433 | ||
434 | MODULE_LICENSE("GPL"); |