| Start/ | End/ | |||
| True | False | - | Line | Source |
| 1 | /* | |||
| 2 | * The USB Monitor, inspired by Dave Harding's USBMon. | |||
| 3 | * | |||
| 4 | * mon_main.c: Main file, module initiation and exit, registrations, etc. | |||
| 5 | * | |||
| 6 | * Copyright (C) 2005 Pete Zaitcev (zaitcev@redhat.com) | |||
| 7 | */ | |||
| 8 | ||||
| 9 | #include <linux/kernel.h> | |||
| 10 | #include <linux/module.h> | |||
| 11 | #include <linux/usb.h> | |||
| 12 | #include <linux/debugfs.h> | |||
| 13 | #include <linux/smp_lock.h> | |||
| 14 | #include <linux/notifier.h> | |||
| 15 | ||||
| 16 | #include "usb_mon.h" | |||
| 17 | #include "../core/hcd.h" | |||
| 18 | ||||
| 19 | static void mon_submit(struct usb_bus *ubus, struct urb *urb); | |||
| 20 | static void mon_complete(struct usb_bus *ubus, struct urb *urb); | |||
| 21 | static void mon_stop(struct mon_bus *mbus); | |||
| 22 | static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus); | |||
| 23 | static void mon_bus_drop(struct kref *r); | |||
| 24 | static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus); | |||
| 25 | ||||
| 26 | DECLARE_MUTEX(mon_lock); | |||
| 27 | ||||
| 28 | static struct dentry *mon_dir; /* /dbg/usbmon */ | |||
| 29 | static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ | |||
| 30 | ||||
| 31 | /* | |||
| 32 | * Link a reader into the bus. | |||
| 33 | * | |||
| 34 | * This must be called with mon_lock taken because of mbus->ref. | |||
| 35 | */ | |||
| 0 | 0 | - | 36 | void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r) |
| 37 | { | |||
| 38 | unsigned long flags; | |||
| 39 | struct usb_bus *ubus; | |||
| 40 | ||||
| 41 | spin_lock_irqsave(&mbus->lock, flags); | |||
| 41 | do | |||
| 41 | do | |||
| 0 | 0 | - | 41 | do-while (0) |
| 0 | 0 | - | 41 | do-while (0) |
| 41 | do | |||
| 41 | do | |||
| 0 | 0 | - | 41 | do-while (0) |
| 0 | 0 | - | 41 | do-while (0) |
| 0 | 0 | - | 41 | do-while (0) |
| 0 | 0 | - | 42 | if (mbus->nreaders == 0) { |
| 43 | ubus = mbus->u_bus; | |||
| 0 | 0 | - | 44 | if (ubus->monitored) { |
| 45 | /* | |||
| 46 | * Something is really broken, refuse to go on and | |||
| 47 | * possibly corrupt ops pointers or worse. | |||
| 48 | */ | |||
| 49 | printk(KERN_ERR TAG ": bus %d is already monitored\n", | |||
| 50 | ubus->busnum); | |||
| 51 | spin_unlock_irqrestore(&mbus->lock, flags); | |||
| 51 | do | |||
| 51 | do | |||
| 0 | 0 | - | 51 | do-while (0) |
| 0 | 0 | - | 51 | do-while (0) |
| 0 | 0 | - | 51 | do-while (0) |
| 0 | - | 52 | return; | |
| 53 | } | |||
| 54 | ubus->monitored = 1; | |||
| 55 | } | |||
| 56 | mbus->nreaders++; | |||
| 57 | list_add_tail(&r->r_link, &mbus->r_list); | |||
| 58 | spin_unlock_irqrestore(&mbus->lock, flags); | |||
| 58 | do | |||
| 58 | do | |||
| 0 | 0 | - | 58 | do-while (0) |
| 0 | 0 | - | 58 | do-while (0) |
| 0 | 0 | - | 58 | do-while (0) |
| 59 | ||||
| 60 | kref_get(&mbus->ref); | |||
| 61 | } | |||
| 62 | ||||
| 63 | /* | |||
| 64 | * Unlink reader from the bus. | |||
| 65 | * | |||
| 66 | * This is called with mon_lock taken, so we can decrement mbus->ref. | |||
| 67 | */ | |||
| 0 | 0 | - | 68 | void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r) |
| 69 | { | |||
| 70 | unsigned long flags; | |||
| 71 | ||||
| 72 | spin_lock_irqsave(&mbus->lock, flags); | |||
| 72 | do | |||
| 72 | do | |||
| 0 | 0 | - | 72 | do-while (0) |
| 0 | 0 | - | 72 | do-while (0) |
| 72 | do | |||
| 72 | do | |||
| 0 | 0 | - | 72 | do-while (0) |
| 0 | 0 | - | 72 | do-while (0) |
| 0 | 0 | - | 72 | do-while (0) |
| 73 | list_del(&r->r_link); | |||
| 74 | --mbus->nreaders; | |||
| 0 | 0 | - | 75 | if (mbus->nreaders == 0) |
| 76 | mon_stop(mbus); | |||
| 77 | spin_unlock_irqrestore(&mbus->lock, flags); | |||
| 77 | do | |||
| 77 | do | |||
| 0 | 0 | - | 77 | do-while (0) |
| 0 | 0 | - | 77 | do-while (0) |
| 0 | 0 | - | 77 | do-while (0) |
| 78 | ||||
| 79 | kref_put(&mbus->ref, mon_bus_drop); | |||
| 80 | } | |||
| 81 | ||||
| 82 | /* | |||
| 83 | */ | |||
| 0 | 0 | - | 84 | static void mon_submit(struct usb_bus *ubus, struct urb *urb) |
| 85 | { | |||
| 86 | struct mon_bus *mbus; | |||
| 87 | unsigned long flags; | |||
| 88 | struct list_head *pos; | |||
| 89 | struct mon_reader *r; | |||
| 90 | ||||
| 91 | mbus = ubus->mon_bus; | |||
| 0 | 0 | - | 92 | if (mbus == NULL) |
| 0 | - | 93 | goto out_unlocked; | |
| 94 | ||||
| 95 | spin_lock_irqsave(&mbus->lock, flags); | |||
| 95 | do | |||
| 95 | do | |||
| 0 | 0 | - | 95 | do-while (0) |
| 0 | 0 | - | 95 | do-while (0) |
| 95 | do | |||
| 95 | do | |||
| 0 | 0 | - | 95 | do-while (0) |
| 0 | 0 | - | 95 | do-while (0) |
| 0 | 0 | - | 95 | do-while (0) |
| 0 | 0 | - | 96 | if (mbus->nreaders == 0) |
| 0 | - | 97 | goto out_locked; | |
| 98 | ||||
| 0 | 0 | - | 99 | list_for_each (pos, &mbus->r_list) { |
| 100 | r = list_entry(pos, struct mon_reader, r_link); | |||
| 101 | r->rnf_submit(r->r_data, urb); | |||
| 102 | } | |||
| 103 | ||||
| 104 | spin_unlock_irqrestore(&mbus->lock, flags); | |||
| 104 | do | |||
| 104 | do | |||
| 0 | 0 | - | 104 | do-while (0) |
| 0 | 0 | - | 104 | do-while (0) |
| 0 | 0 | - | 104 | do-while (0) |
| 0 | - | 105 | return; | |
| 106 | ||||
| 107 | out_locked: | |||
| 108 | spin_unlock_irqrestore(&mbus->lock, flags); | |||
| 108 | do | |||
| 108 | do | |||
| 0 | 0 | - | 108 | do-while (0) |
| 0 | 0 | - | 108 | do-while (0) |
| 0 | 0 | - | 108 | do-while (0) |
| 109 | out_unlocked: | |||
| 0 | - | 110 | return; | |
| 111 | } | |||
| 112 | ||||
| 113 | /* | |||
| 114 | */ | |||
| 0 | 0 | - | 115 | static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int err) |
| 116 | { | |||
| 117 | struct mon_bus *mbus; | |||
| 118 | ||||
| 119 | mbus = ubus->mon_bus; | |||
| 0 | 0 | - | 120 | if (mbus == NULL) |
| 0 | - | 121 | goto out_unlocked; | |
| 122 | ||||
| 123 | /* | |||
| 124 | * XXX Capture the error code and the 'E' event. | |||
| 125 | */ | |||
| 126 | ||||
| 0 | - | 127 | return; | |
| 128 | ||||
| 129 | out_unlocked: | |||
| 0 | - | 130 | return; | |
| 131 | } | |||
| 132 | ||||
| 133 | /* | |||
| 134 | */ | |||
| 0 | 0 | - | 135 | static void mon_complete(struct usb_bus *ubus, struct urb *urb) |
| 136 | { | |||
| 137 | struct mon_bus *mbus; | |||
| 138 | unsigned long flags; | |||
| 139 | struct list_head *pos; | |||
| 140 | struct mon_reader *r; | |||
| 141 | ||||
| 142 | mbus = ubus->mon_bus; | |||
| 0 | 0 | - | 143 | if (mbus == NULL) { |
| 144 | /* | |||
| 145 | * This should not happen. | |||
| 146 | * At this point we do not even know the bus number... | |||
| 147 | */ | |||
| 148 | printk(KERN_ERR TAG ": Null mon bus in URB, pipe 0x%x\n", | |||
| 149 | urb->pipe); | |||
| 0 | - | 150 | return; | |
| 151 | } | |||
| 152 | ||||
| 153 | spin_lock_irqsave(&mbus->lock, flags); | |||
| 153 | do | |||
| 153 | do | |||
| 0 | 0 | - | 153 | do-while (0) |
| 0 | 0 | - | 153 | do-while (0) |
| 153 | do | |||
| 153 | do | |||
| 0 | 0 | - | 153 | do-while (0) |
| 0 | 0 | - | 153 | do-while (0) |
| 0 | 0 | - | 153 | do-while (0) |
| 0 | 0 | - | 154 | list_for_each (pos, &mbus->r_list) { |
| 155 | r = list_entry(pos, struct mon_reader, r_link); | |||
| 156 | r->rnf_complete(r->r_data, urb); | |||
| 157 | } | |||
| 158 | spin_unlock_irqrestore(&mbus->lock, flags); | |||
| 158 | do | |||
| 158 | do | |||
| 0 | 0 | - | 158 | do-while (0) |
| 0 | 0 | - | 158 | do-while (0) |
| 0 | 0 | - | 158 | do-while (0) |
| 159 | } | |||
| 160 | ||||
| 161 | /* int (*unlink_urb) (struct urb *urb, int status); */ | |||
| 162 | ||||
| 163 | /* | |||
| 164 | * Stop monitoring. | |||
| 165 | * Obviously this must be well locked, so no need to play with mb's. | |||
| 166 | */ | |||
| 0 | 0 | - | 167 | static void mon_stop(struct mon_bus *mbus) |
| 168 | { | |||
| 169 | struct usb_bus *ubus = mbus->u_bus; | |||
| 170 | ||||
| 171 | /* | |||
| 172 | * A stop can be called for a dissolved mon_bus in case of | |||
| 173 | * a reader staying across an rmmod foo_hcd. | |||
| 174 | */ | |||
| 0 | 0 | - | 175 | if (ubus != NULL) { |
| 176 | ubus->monitored = 0; | |||
| 177 | mb(); | |||
| 178 | } | |||
| 179 | } | |||
| 180 | ||||
| 181 | /* | |||
| 182 | * Add a USB bus (usually by a modprobe foo-hcd) | |||
| 183 | * | |||
| 184 | * This does not return an error code because the core cannot care less | |||
| 185 | * if monitoring is not established. | |||
| 186 | */ | |||
| 24 | 24 | 187 | static void mon_bus_add(struct usb_bus *ubus) | |
| 188 | { | |||
| 189 | mon_bus_init(mon_dir, ubus); | |||
| 190 | } | |||
| 191 | ||||
| 192 | /* | |||
| 193 | * Remove a USB bus (either from rmmod foo-hcd or from a hot-remove event). | |||
| 194 | */ | |||
| 0 | 0 | - | 195 | static void mon_bus_remove(struct usb_bus *ubus) |
| 196 | { | |||
| 197 | struct mon_bus *mbus = ubus->mon_bus; | |||
| 198 | ||||
| 199 | down(&mon_lock); | |||
| 200 | list_del(&mbus->bus_link); | |||
| 201 | debugfs_remove(mbus->dent_t); | |||
| 202 | debugfs_remove(mbus->dent_s); | |||
| 203 | ||||
| 204 | mon_dissolve(mbus, ubus); | |||
| 205 | kref_put(&mbus->ref, mon_bus_drop); | |||
| 206 | up(&mon_lock); | |||
| 207 | } | |||
| 208 | ||||
| 62 | 0 | 209 | static int mon_notify(struct notifier_block *self, unsigned long action, | |
| 210 | void *dev) | |||
| 211 | { | |||
| 212 | switch (action) { | |||
| 24 | 213 | case USB_BUS_ADD: | ||
| 214 | mon_bus_add(dev); | |||
| 24 | 215 | break; | ||
| 0 | - | 216 | case USB_BUS_REMOVE: | |
| 217 | mon_bus_remove(dev); | |||
| 218 | } | |||
| 62 | 219 | return NOTIFY_OK; | ||
| 220 | } | |||
| 221 | ||||
| 222 | static struct notifier_block mon_nb = { | |||
| 223 | .notifier_call = mon_notify, | |||
| 224 | }; | |||
| 225 | ||||
| 226 | /* | |||
| 227 | * Ops | |||
| 228 | */ | |||
| 229 | static struct usb_mon_operations mon_ops_0 = { | |||
| 230 | .urb_submit = mon_submit, | |||
| 231 | .urb_submit_error = mon_submit_error, | |||
| 232 | .urb_complete = mon_complete, | |||
| 233 | }; | |||
| 234 | ||||
| 235 | /* | |||
| 236 | * Tear usb_bus and mon_bus apart. | |||
| 237 | */ | |||
| 0 | 0 | - | 238 | static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) |
| 239 | { | |||
| 240 | ||||
| 241 | /* | |||
| 242 | * Never happens, but... | |||
| 243 | */ | |||
| 0 | 0 | - | 244 | if (ubus->monitored) { |
| 245 | printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n", | |||
| 246 | ubus->busnum); | |||
| 247 | ubus->monitored = 0; | |||
| 248 | mb(); | |||
| 249 | } | |||
| 250 | ||||
| 251 | ubus->mon_bus = NULL; | |||
| 252 | mbus->u_bus = NULL; | |||
| 253 | mb(); | |||
| 254 | // usb_bus_put(ubus); | |||
| 255 | } | |||
| 256 | ||||
| 257 | /* | |||
| 258 | */ | |||
| 0 | 0 | - | 259 | static void mon_bus_drop(struct kref *r) |
| 260 | { | |||
| 261 | struct mon_bus *mbus = container_of(r, struct mon_bus, ref); | |||
| 262 | kfree(mbus); | |||
| 263 | } | |||
| 264 | ||||
| 265 | /* | |||
| 266 | * Initialize a bus for us: | |||
| 267 | * - allocate mon_bus | |||
| 268 | * - refcount USB bus struct | |||
| 269 | * - link | |||
| 270 | */ | |||
| 24 | 0 | 271 | static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus) | |
| 272 | { | |||
| 273 | struct dentry *d; | |||
| 274 | struct mon_bus *mbus; | |||
| 275 | enum { NAMESZ = 10 }; | |||
| 276 | char name[NAMESZ]; | |||
| 277 | int rc; | |||
| 278 | ||||
| 0 | 24 | - | 279 | if ((mbus = kmalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL) |
| 0 | - | 280 | goto err_alloc; | |
| 281 | memset(mbus, 0, sizeof(struct mon_bus)); | |||
| 282 | kref_init(&mbus->ref); | |||
| 283 | spin_lock_init(&mbus->lock); | |||
| 0 | 24 | - | 283 | do-while (0) |
| 284 | INIT_LIST_HEAD(&mbus->r_list); | |||
| 285 | ||||
| 286 | /* | |||
| 287 | * This usb_bus_get here is superfluous, because we receive | |||
| 288 | * a notification if usb_bus is about to be removed. | |||
| 289 | */ | |||
| 290 | // usb_bus_get(ubus); | |||
| 291 | mbus->u_bus = ubus; | |||
| 292 | ubus->mon_bus = mbus; | |||
| 293 | ||||
| 294 | rc = snprintf(name, NAMESZ, "%dt", ubus->busnum); | |||
| 0 | 24 | - | 295 | if (rc <= 0 || rc >= NAMESZ) |
| 0 | - | 295 | T || _ | |
| 0 | - | 295 | F || T | |
| 24 | 295 | F || F | ||
| 0 | - | 296 | goto err_print_t; | |
| 297 | d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_text); | |||
| 0 | 24 | - | 298 | if (d == NULL) |
| 0 | - | 299 | goto err_create_t; | |
| 300 | mbus->dent_t = d; | |||
| 301 | ||||
| 302 | rc = snprintf(name, NAMESZ, "%ds", ubus->busnum); | |||
| 0 | 24 | - | 303 | if (rc <= 0 || rc >= NAMESZ) |
| 0 | - | 303 | T || _ | |
| 0 | - | 303 | F || T | |
| 24 | 303 | F || F | ||
| 0 | - | 304 | goto err_print_s; | |
| 305 | d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_stat); | |||
| 0 | 24 | - | 306 | if (d == NULL) |
| 0 | - | 307 | goto err_create_s; | |
| 308 | mbus->dent_s = d; | |||
| 309 | ||||
| 310 | down(&mon_lock); | |||
| 311 | list_add_tail(&mbus->bus_link, &mon_buses); | |||
| 312 | up(&mon_lock); | |||
| 24 | 313 | return; | ||
| 314 | ||||
| 315 | err_create_s: | |||
| 316 | err_print_s: | |||
| 317 | debugfs_remove(mbus->dent_t); | |||
| 318 | err_create_t: | |||
| 319 | err_print_t: | |||
| 320 | kfree(mbus); | |||
| 321 | err_alloc: | |||
| 0 | - | 322 | return; | |
| 323 | } | |||
| 324 | ||||
| 6 | 0 | 325 | static int __init mon_init(void) | |
| 326 | { | |||
| 327 | struct usb_bus *ubus; | |||
| 328 | struct dentry *mondir; | |||
| 329 | ||||
| 330 | mondir = debugfs_create_dir("usbmon", NULL); | |||
| 0 | 6 | - | 331 | if (IS_ERR(mondir)) { |
| 332 | printk(KERN_NOTICE TAG ": debugfs is not available\n"); | |||
| 0 | - | 333 | return -ENODEV; | |
| 334 | } | |||
| 0 | 6 | - | 335 | if (mondir == NULL) { |
| 336 | printk(KERN_NOTICE TAG ": unable to create usbmon directory\n"); | |||
| 0 | - | 337 | return -ENODEV; | |
| 338 | } | |||
| 339 | mon_dir = mondir; | |||
| 340 | ||||
| 0 | 6 | - | 341 | if (usb_mon_register(&mon_ops_0) != 0) { |
| 342 | printk(KERN_NOTICE TAG ": unable to register with the core\n"); | |||
| 343 | debugfs_remove(mondir); | |||
| 0 | - | 344 | return -ENODEV; | |
| 345 | } | |||
| 346 | // MOD_INC_USE_COUNT(which_module?); | |||
| 347 | ||||
| 348 | usb_register_notify(&mon_nb); | |||
| 349 | ||||
| 350 | down(&usb_bus_list_lock); | |||
| 0 | 6 | - | 351 | list_for_each_entry (ubus, &usb_bus_list, bus_list) { |
| 352 | mon_bus_init(mondir, ubus); | |||
| 353 | } | |||
| 354 | up(&usb_bus_list_lock); | |||
| 6 | 355 | return 0; | ||
| 356 | } | |||
| 357 | ||||
| 0 | 0 | - | 358 | static void __exit mon_exit(void) |
| 359 | { | |||
| 360 | struct mon_bus *mbus; | |||
| 361 | struct list_head *p; | |||
| 362 | ||||
| 363 | usb_unregister_notify(&mon_nb); | |||
| 364 | usb_mon_deregister(); | |||
| 365 | ||||
| 366 | down(&mon_lock); | |||
| 0 | 0 | - | 367 | while (!list_empty(&mon_buses)) { |
| 368 | p = mon_buses.next; | |||
| 369 | mbus = list_entry(p, struct mon_bus, bus_link); | |||
| 370 | list_del(p); | |||
| 371 | ||||
| 372 | debugfs_remove(mbus->dent_t); | |||
| 373 | debugfs_remove(mbus->dent_s); | |||
| 374 | ||||
| 375 | /* | |||
| 376 | * This never happens, because the open/close paths in | |||
| 377 | * file level maintain module use counters and so rmmod fails | |||
| 378 | * before reaching here. However, better be safe... | |||
| 379 | */ | |||
| 0 | 0 | - | 380 | if (mbus->nreaders) { |
| 381 | printk(KERN_ERR TAG | |||
| 382 | ": Outstanding opens (%d) on usb%d, leaking...\n", | |||
| 383 | mbus->nreaders, mbus->u_bus->busnum); | |||
| 384 | atomic_set(&mbus->ref.refcount, 2); /* Force leak */ | |||
| 385 | } | |||
| 386 | ||||
| 387 | mon_dissolve(mbus, mbus->u_bus); | |||
| 388 | kref_put(&mbus->ref, mon_bus_drop); | |||
| 389 | } | |||
| 390 | up(&mon_lock); | |||
| 391 | ||||
| 392 | debugfs_remove(mon_dir); | |||
| 393 | } | |||
| 394 | ||||
| 395 | module_init(mon_init); | |||
| 396 | module_exit(mon_exit); | |||
| 397 | ||||
| 398 | MODULE_LICENSE("GPL"); | |||
| ***TER 13% (21/166) of SOURCE FILE mon_main.c | ||||