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