18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * The USB Monitor, inspired by Dave Harding's USBMon. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * mon_main.c: Main file, module initiation and exit, registrations, etc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2005 Pete Zaitcev (zaitcev@redhat.com) 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/usb.h> 138c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/notifier.h> 168c2ecf20Sopenharmony_ci#include <linux/mutex.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "usb_mon.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic void mon_stop(struct mon_bus *mbus); 228c2ecf20Sopenharmony_cistatic void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus); 238c2ecf20Sopenharmony_cistatic void mon_bus_drop(struct kref *r); 248c2ecf20Sopenharmony_cistatic void mon_bus_init(struct usb_bus *ubus); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciDEFINE_MUTEX(mon_lock); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct mon_bus mon_bus0; /* Pseudo bus meaning "all buses" */ 298c2ecf20Sopenharmony_cistatic LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci * Link a reader into the bus. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * This must be called with mon_lock taken because of mbus->ref. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_civoid mon_reader_add(struct mon_bus *mbus, struct mon_reader *r) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci unsigned long flags; 398c2ecf20Sopenharmony_ci struct list_head *p; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci spin_lock_irqsave(&mbus->lock, flags); 428c2ecf20Sopenharmony_ci if (mbus->nreaders == 0) { 438c2ecf20Sopenharmony_ci if (mbus == &mon_bus0) { 448c2ecf20Sopenharmony_ci list_for_each (p, &mon_buses) { 458c2ecf20Sopenharmony_ci struct mon_bus *m1; 468c2ecf20Sopenharmony_ci m1 = list_entry(p, struct mon_bus, bus_link); 478c2ecf20Sopenharmony_ci m1->u_bus->monitored = 1; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci } else { 508c2ecf20Sopenharmony_ci mbus->u_bus->monitored = 1; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci mbus->nreaders++; 548c2ecf20Sopenharmony_ci list_add_tail(&r->r_link, &mbus->r_list); 558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mbus->lock, flags); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci kref_get(&mbus->ref); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Unlink reader from the bus. 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * This is called with mon_lock taken, so we can decrement mbus->ref. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_civoid mon_reader_del(struct mon_bus *mbus, struct mon_reader *r) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci unsigned long flags; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci spin_lock_irqsave(&mbus->lock, flags); 708c2ecf20Sopenharmony_ci list_del(&r->r_link); 718c2ecf20Sopenharmony_ci --mbus->nreaders; 728c2ecf20Sopenharmony_ci if (mbus->nreaders == 0) 738c2ecf20Sopenharmony_ci mon_stop(mbus); 748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mbus->lock, flags); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci kref_put(&mbus->ref, mon_bus_drop); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_cistatic void mon_bus_submit(struct mon_bus *mbus, struct urb *urb) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci unsigned long flags; 848c2ecf20Sopenharmony_ci struct list_head *pos; 858c2ecf20Sopenharmony_ci struct mon_reader *r; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci spin_lock_irqsave(&mbus->lock, flags); 888c2ecf20Sopenharmony_ci mbus->cnt_events++; 898c2ecf20Sopenharmony_ci list_for_each (pos, &mbus->r_list) { 908c2ecf20Sopenharmony_ci r = list_entry(pos, struct mon_reader, r_link); 918c2ecf20Sopenharmony_ci r->rnf_submit(r->r_data, urb); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mbus->lock, flags); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void mon_submit(struct usb_bus *ubus, struct urb *urb) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct mon_bus *mbus; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci mbus = ubus->mon_bus; 1018c2ecf20Sopenharmony_ci if (mbus != NULL) 1028c2ecf20Sopenharmony_ci mon_bus_submit(mbus, urb); 1038c2ecf20Sopenharmony_ci mon_bus_submit(&mon_bus0, urb); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_cistatic void mon_bus_submit_error(struct mon_bus *mbus, struct urb *urb, int error) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci unsigned long flags; 1118c2ecf20Sopenharmony_ci struct list_head *pos; 1128c2ecf20Sopenharmony_ci struct mon_reader *r; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci spin_lock_irqsave(&mbus->lock, flags); 1158c2ecf20Sopenharmony_ci mbus->cnt_events++; 1168c2ecf20Sopenharmony_ci list_for_each (pos, &mbus->r_list) { 1178c2ecf20Sopenharmony_ci r = list_entry(pos, struct mon_reader, r_link); 1188c2ecf20Sopenharmony_ci r->rnf_error(r->r_data, urb, error); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mbus->lock, flags); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct mon_bus *mbus; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci mbus = ubus->mon_bus; 1288c2ecf20Sopenharmony_ci if (mbus != NULL) 1298c2ecf20Sopenharmony_ci mon_bus_submit_error(mbus, urb, error); 1308c2ecf20Sopenharmony_ci mon_bus_submit_error(&mon_bus0, urb, error); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_cistatic void mon_bus_complete(struct mon_bus *mbus, struct urb *urb, int status) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci unsigned long flags; 1388c2ecf20Sopenharmony_ci struct list_head *pos; 1398c2ecf20Sopenharmony_ci struct mon_reader *r; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spin_lock_irqsave(&mbus->lock, flags); 1428c2ecf20Sopenharmony_ci mbus->cnt_events++; 1438c2ecf20Sopenharmony_ci list_for_each (pos, &mbus->r_list) { 1448c2ecf20Sopenharmony_ci r = list_entry(pos, struct mon_reader, r_link); 1458c2ecf20Sopenharmony_ci r->rnf_complete(r->r_data, urb, status); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mbus->lock, flags); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void mon_complete(struct usb_bus *ubus, struct urb *urb, int status) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct mon_bus *mbus; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci mbus = ubus->mon_bus; 1558c2ecf20Sopenharmony_ci if (mbus != NULL) 1568c2ecf20Sopenharmony_ci mon_bus_complete(mbus, urb, status); 1578c2ecf20Sopenharmony_ci mon_bus_complete(&mon_bus0, urb, status); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* int (*unlink_urb) (struct urb *urb, int status); */ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* 1638c2ecf20Sopenharmony_ci * Stop monitoring. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_cistatic void mon_stop(struct mon_bus *mbus) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct usb_bus *ubus; 1688c2ecf20Sopenharmony_ci struct list_head *p; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (mbus == &mon_bus0) { 1718c2ecf20Sopenharmony_ci list_for_each (p, &mon_buses) { 1728c2ecf20Sopenharmony_ci mbus = list_entry(p, struct mon_bus, bus_link); 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * We do not change nreaders here, so rely on mon_lock. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci if (mbus->nreaders == 0 && (ubus = mbus->u_bus) != NULL) 1778c2ecf20Sopenharmony_ci ubus->monitored = 0; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } else { 1808c2ecf20Sopenharmony_ci /* 1818c2ecf20Sopenharmony_ci * A stop can be called for a dissolved mon_bus in case of 1828c2ecf20Sopenharmony_ci * a reader staying across an rmmod foo_hcd, so test ->u_bus. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci if (mon_bus0.nreaders == 0 && (ubus = mbus->u_bus) != NULL) { 1858c2ecf20Sopenharmony_ci ubus->monitored = 0; 1868c2ecf20Sopenharmony_ci mb(); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* 1928c2ecf20Sopenharmony_ci * Add a USB bus (usually by a modprobe foo-hcd) 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * This does not return an error code because the core cannot care less 1958c2ecf20Sopenharmony_ci * if monitoring is not established. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistatic void mon_bus_add(struct usb_bus *ubus) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci mon_bus_init(ubus); 2008c2ecf20Sopenharmony_ci mutex_lock(&mon_lock); 2018c2ecf20Sopenharmony_ci if (mon_bus0.nreaders != 0) 2028c2ecf20Sopenharmony_ci ubus->monitored = 1; 2038c2ecf20Sopenharmony_ci mutex_unlock(&mon_lock); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * Remove a USB bus (either from rmmod foo-hcd or from a hot-remove event). 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_cistatic void mon_bus_remove(struct usb_bus *ubus) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct mon_bus *mbus = ubus->mon_bus; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci mutex_lock(&mon_lock); 2148c2ecf20Sopenharmony_ci list_del(&mbus->bus_link); 2158c2ecf20Sopenharmony_ci if (mbus->text_inited) 2168c2ecf20Sopenharmony_ci mon_text_del(mbus); 2178c2ecf20Sopenharmony_ci if (mbus->bin_inited) 2188c2ecf20Sopenharmony_ci mon_bin_del(mbus); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci mon_dissolve(mbus, ubus); 2218c2ecf20Sopenharmony_ci kref_put(&mbus->ref, mon_bus_drop); 2228c2ecf20Sopenharmony_ci mutex_unlock(&mon_lock); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int mon_notify(struct notifier_block *self, unsigned long action, 2268c2ecf20Sopenharmony_ci void *dev) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci switch (action) { 2298c2ecf20Sopenharmony_ci case USB_BUS_ADD: 2308c2ecf20Sopenharmony_ci mon_bus_add(dev); 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci case USB_BUS_REMOVE: 2338c2ecf20Sopenharmony_ci mon_bus_remove(dev); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci return NOTIFY_OK; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic struct notifier_block mon_nb = { 2398c2ecf20Sopenharmony_ci .notifier_call = mon_notify, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci * Ops 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_cistatic const struct usb_mon_operations mon_ops_0 = { 2468c2ecf20Sopenharmony_ci .urb_submit = mon_submit, 2478c2ecf20Sopenharmony_ci .urb_submit_error = mon_submit_error, 2488c2ecf20Sopenharmony_ci .urb_complete = mon_complete, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* 2528c2ecf20Sopenharmony_ci * Tear usb_bus and mon_bus apart. 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_cistatic void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (ubus->monitored) { 2588c2ecf20Sopenharmony_ci ubus->monitored = 0; 2598c2ecf20Sopenharmony_ci mb(); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ubus->mon_bus = NULL; 2638c2ecf20Sopenharmony_ci mbus->u_bus = NULL; 2648c2ecf20Sopenharmony_ci mb(); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* We want synchronize_irq() here, but that needs an argument. */ 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_cistatic void mon_bus_drop(struct kref *r) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct mon_bus *mbus = container_of(r, struct mon_bus, ref); 2748c2ecf20Sopenharmony_ci kfree(mbus); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* 2788c2ecf20Sopenharmony_ci * Initialize a bus for us: 2798c2ecf20Sopenharmony_ci * - allocate mon_bus 2808c2ecf20Sopenharmony_ci * - refcount USB bus struct 2818c2ecf20Sopenharmony_ci * - link 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_cistatic void mon_bus_init(struct usb_bus *ubus) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct mon_bus *mbus; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL); 2888c2ecf20Sopenharmony_ci if (mbus == NULL) 2898c2ecf20Sopenharmony_ci goto err_alloc; 2908c2ecf20Sopenharmony_ci kref_init(&mbus->ref); 2918c2ecf20Sopenharmony_ci spin_lock_init(&mbus->lock); 2928c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mbus->r_list); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* 2958c2ecf20Sopenharmony_ci * We don't need to take a reference to ubus, because we receive 2968c2ecf20Sopenharmony_ci * a notification if the bus is about to be removed. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci mbus->u_bus = ubus; 2998c2ecf20Sopenharmony_ci ubus->mon_bus = mbus; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci mbus->text_inited = mon_text_add(mbus, ubus); 3028c2ecf20Sopenharmony_ci mbus->bin_inited = mon_bin_add(mbus, ubus); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci mutex_lock(&mon_lock); 3058c2ecf20Sopenharmony_ci list_add_tail(&mbus->bus_link, &mon_buses); 3068c2ecf20Sopenharmony_ci mutex_unlock(&mon_lock); 3078c2ecf20Sopenharmony_ci return; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cierr_alloc: 3108c2ecf20Sopenharmony_ci return; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic void mon_bus0_init(void) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct mon_bus *mbus = &mon_bus0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci kref_init(&mbus->ref); 3188c2ecf20Sopenharmony_ci spin_lock_init(&mbus->lock); 3198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mbus->r_list); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci mbus->text_inited = mon_text_add(mbus, NULL); 3228c2ecf20Sopenharmony_ci mbus->bin_inited = mon_bin_add(mbus, NULL); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* 3268c2ecf20Sopenharmony_ci * Search a USB bus by number. Notice that USB bus numbers start from one, 3278c2ecf20Sopenharmony_ci * which we may later use to identify "all" with zero. 3288c2ecf20Sopenharmony_ci * 3298c2ecf20Sopenharmony_ci * This function must be called with mon_lock held. 3308c2ecf20Sopenharmony_ci * 3318c2ecf20Sopenharmony_ci * This is obviously inefficient and may be revised in the future. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_cistruct mon_bus *mon_bus_lookup(unsigned int num) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct list_head *p; 3368c2ecf20Sopenharmony_ci struct mon_bus *mbus; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (num == 0) { 3398c2ecf20Sopenharmony_ci return &mon_bus0; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci list_for_each (p, &mon_buses) { 3428c2ecf20Sopenharmony_ci mbus = list_entry(p, struct mon_bus, bus_link); 3438c2ecf20Sopenharmony_ci if (mbus->u_bus->busnum == num) { 3448c2ecf20Sopenharmony_ci return mbus; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci return NULL; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int __init mon_init(void) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct usb_bus *ubus; 3538c2ecf20Sopenharmony_ci int rc, id; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if ((rc = mon_text_init()) != 0) 3568c2ecf20Sopenharmony_ci goto err_text; 3578c2ecf20Sopenharmony_ci if ((rc = mon_bin_init()) != 0) 3588c2ecf20Sopenharmony_ci goto err_bin; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci mon_bus0_init(); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (usb_mon_register(&mon_ops_0) != 0) { 3638c2ecf20Sopenharmony_ci printk(KERN_NOTICE TAG ": unable to register with the core\n"); 3648c2ecf20Sopenharmony_ci rc = -ENODEV; 3658c2ecf20Sopenharmony_ci goto err_reg; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci // MOD_INC_USE_COUNT(which_module?); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci mutex_lock(&usb_bus_idr_lock); 3708c2ecf20Sopenharmony_ci idr_for_each_entry(&usb_bus_idr, ubus, id) 3718c2ecf20Sopenharmony_ci mon_bus_init(ubus); 3728c2ecf20Sopenharmony_ci usb_register_notify(&mon_nb); 3738c2ecf20Sopenharmony_ci mutex_unlock(&usb_bus_idr_lock); 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cierr_reg: 3778c2ecf20Sopenharmony_ci mon_bin_exit(); 3788c2ecf20Sopenharmony_cierr_bin: 3798c2ecf20Sopenharmony_ci mon_text_exit(); 3808c2ecf20Sopenharmony_cierr_text: 3818c2ecf20Sopenharmony_ci return rc; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic void __exit mon_exit(void) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct mon_bus *mbus; 3878c2ecf20Sopenharmony_ci struct list_head *p; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci usb_unregister_notify(&mon_nb); 3908c2ecf20Sopenharmony_ci usb_mon_deregister(); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci mutex_lock(&mon_lock); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci while (!list_empty(&mon_buses)) { 3958c2ecf20Sopenharmony_ci p = mon_buses.next; 3968c2ecf20Sopenharmony_ci mbus = list_entry(p, struct mon_bus, bus_link); 3978c2ecf20Sopenharmony_ci list_del(p); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (mbus->text_inited) 4008c2ecf20Sopenharmony_ci mon_text_del(mbus); 4018c2ecf20Sopenharmony_ci if (mbus->bin_inited) 4028c2ecf20Sopenharmony_ci mon_bin_del(mbus); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * This never happens, because the open/close paths in 4068c2ecf20Sopenharmony_ci * file level maintain module use counters and so rmmod fails 4078c2ecf20Sopenharmony_ci * before reaching here. However, better be safe... 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci if (mbus->nreaders) { 4108c2ecf20Sopenharmony_ci printk(KERN_ERR TAG 4118c2ecf20Sopenharmony_ci ": Outstanding opens (%d) on usb%d, leaking...\n", 4128c2ecf20Sopenharmony_ci mbus->nreaders, mbus->u_bus->busnum); 4138c2ecf20Sopenharmony_ci kref_get(&mbus->ref); /* Force leak */ 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci mon_dissolve(mbus, mbus->u_bus); 4178c2ecf20Sopenharmony_ci kref_put(&mbus->ref, mon_bus_drop); 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci mbus = &mon_bus0; 4218c2ecf20Sopenharmony_ci if (mbus->text_inited) 4228c2ecf20Sopenharmony_ci mon_text_del(mbus); 4238c2ecf20Sopenharmony_ci if (mbus->bin_inited) 4248c2ecf20Sopenharmony_ci mon_bin_del(mbus); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci mutex_unlock(&mon_lock); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci mon_text_exit(); 4298c2ecf20Sopenharmony_ci mon_bin_exit(); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cimodule_init(mon_init); 4338c2ecf20Sopenharmony_cimodule_exit(mon_exit); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 436