diff options
author | Pete Zaitcev <zaitcev@redhat.com> | 2005-10-22 23:15:09 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-01-04 16:48:31 -0500 |
commit | a00828e9ac62caed7b830d631914d7748817ccd1 (patch) | |
tree | 2fed4c66762fa4f54945413b4027ff5837ad0633 /drivers/usb/storage/libusual.c | |
parent | 1c50c317e2e7f15427149cbc216a63366468710e (diff) |
[PATCH] USB: drivers/usb/storage/libusual
This patch adds a shim driver libusual, which routes devices between
usb-storage and ub according to the common table, based on unusual_devs.h.
The help and example syntax is in Kconfig.
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/storage/libusual.c')
-rw-r--r-- | drivers/usb/storage/libusual.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c new file mode 100644 index 000000000000..61f73d8a2c0f --- /dev/null +++ b/drivers/usb/storage/libusual.c | |||
@@ -0,0 +1,266 @@ | |||
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 | |||
12 | /* | ||
13 | */ | ||
14 | #define USU_MOD_FL_THREAD 1 /* Thread is running */ | ||
15 | #define USU_MOD_FL_PRESENT 2 /* The module is loaded */ | ||
16 | |||
17 | struct mod_status { | ||
18 | unsigned long fls; | ||
19 | }; | ||
20 | |||
21 | static struct mod_status stat[3]; | ||
22 | static DEFINE_SPINLOCK(usu_lock); | ||
23 | |||
24 | /* | ||
25 | */ | ||
26 | #define USB_US_DEFAULT_BIAS USB_US_TYPE_STOR | ||
27 | |||
28 | #define BIAS_NAME_SIZE (sizeof("usb-storage")) | ||
29 | static char bias[BIAS_NAME_SIZE]; | ||
30 | static int usb_usual_bias; | ||
31 | static const char *bias_names[3] = { "none", "usb-storage", "ub" }; | ||
32 | |||
33 | static DECLARE_MUTEX_LOCKED(usu_init_notify); | ||
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); | ||
38 | static int parse_bias(const char *bias_s); | ||
39 | |||
40 | /* | ||
41 | * The table. | ||
42 | */ | ||
43 | #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ | ||
44 | vendorName, productName,useProtocol, useTransport, \ | ||
45 | initFunction, flags) \ | ||
46 | { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax), \ | ||
47 | .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } | ||
48 | |||
49 | #define USUAL_DEV(useProto, useTrans, useType) \ | ||
50 | { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \ | ||
51 | .driver_info = ((useType)<<24) } | ||
52 | |||
53 | struct usb_device_id storage_usb_ids [] = { | ||
54 | # include "unusual_devs.h" | ||
55 | { } /* Terminating entry */ | ||
56 | }; | ||
57 | |||
58 | #undef USUAL_DEV | ||
59 | #undef UNUSUAL_DEV | ||
60 | |||
61 | MODULE_DEVICE_TABLE(usb, storage_usb_ids); | ||
62 | EXPORT_SYMBOL_GPL(storage_usb_ids); | ||
63 | |||
64 | /* | ||
65 | * @type: the module type as an integer | ||
66 | */ | ||
67 | void usb_usual_set_present(int type) | ||
68 | { | ||
69 | struct mod_status *st; | ||
70 | unsigned long flags; | ||
71 | |||
72 | if (type <= 0 || type >= 3) | ||
73 | return; | ||
74 | st = &stat[type]; | ||
75 | spin_lock_irqsave(&usu_lock, flags); | ||
76 | st->fls |= USU_MOD_FL_PRESENT; | ||
77 | spin_unlock_irqrestore(&usu_lock, flags); | ||
78 | } | ||
79 | EXPORT_SYMBOL_GPL(usb_usual_set_present); | ||
80 | |||
81 | void usb_usual_clear_present(int type) | ||
82 | { | ||
83 | struct mod_status *st; | ||
84 | unsigned long flags; | ||
85 | |||
86 | if (type <= 0 || type >= 3) | ||
87 | return; | ||
88 | st = &stat[type]; | ||
89 | spin_lock_irqsave(&usu_lock, flags); | ||
90 | st->fls &= ~USU_MOD_FL_PRESENT; | ||
91 | spin_unlock_irqrestore(&usu_lock, flags); | ||
92 | } | ||
93 | EXPORT_SYMBOL_GPL(usb_usual_clear_present); | ||
94 | |||
95 | /* | ||
96 | * Match the calling driver type against the table. | ||
97 | * Returns: 0 if the device matches. | ||
98 | */ | ||
99 | int usb_usual_check_type(const struct usb_device_id *id, int caller_type) | ||
100 | { | ||
101 | int id_type = USB_US_TYPE(id->driver_info); | ||
102 | |||
103 | if (caller_type <= 0 || caller_type >= 3) | ||
104 | return -EINVAL; | ||
105 | |||
106 | /* Drivers grab fixed assignment devices */ | ||
107 | if (id_type == caller_type) | ||
108 | return 0; | ||
109 | /* Drivers grab devices biased to them */ | ||
110 | if (id_type == USB_US_TYPE_NONE && caller_type == usb_usual_bias) | ||
111 | return 0; | ||
112 | return -ENODEV; | ||
113 | } | ||
114 | EXPORT_SYMBOL_GPL(usb_usual_check_type); | ||
115 | |||
116 | /* | ||
117 | */ | ||
118 | static int usu_probe(struct usb_interface *intf, | ||
119 | const struct usb_device_id *id) | ||
120 | { | ||
121 | int type; | ||
122 | int rc; | ||
123 | unsigned long flags; | ||
124 | |||
125 | type = USB_US_TYPE(id->driver_info); | ||
126 | if (type == 0) | ||
127 | type = usb_usual_bias; | ||
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 | |||
137 | rc = kernel_thread(usu_probe_thread, (void*)type, CLONE_VM); | ||
138 | if (rc < 0) { | ||
139 | printk(KERN_WARNING "libusual: " | ||
140 | "Unable to start the thread for %s: %d\n", | ||
141 | bias_names[type], rc); | ||
142 | spin_lock_irqsave(&usu_lock, flags); | ||
143 | stat[type].fls &= ~USU_MOD_FL_THREAD; | ||
144 | spin_unlock_irqrestore(&usu_lock, flags); | ||
145 | return rc; /* Not being -ENXIO causes a message printed */ | ||
146 | } | ||
147 | atomic_inc(&total_threads); | ||
148 | |||
149 | return -ENXIO; | ||
150 | } | ||
151 | |||
152 | static void usu_disconnect(struct usb_interface *intf) | ||
153 | { | ||
154 | ; /* We should not be here. */ | ||
155 | } | ||
156 | |||
157 | static struct usb_driver usu_driver = { | ||
158 | .owner = THIS_MODULE, | ||
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 | |||
181 | daemonize("libusual_%d", type); /* "usb-storage" is kinda too long */ | ||
182 | |||
183 | /* A completion does not work here because it's counted. */ | ||
184 | down(&usu_init_notify); | ||
185 | up(&usu_init_notify); | ||
186 | |||
187 | rc = request_module(bias_names[type]); | ||
188 | spin_lock_irqsave(&usu_lock, flags); | ||
189 | if (rc == 0 && (st->fls & USU_MOD_FL_PRESENT) == 0) { | ||
190 | /* | ||
191 | * This should not happen, but let us keep tabs on it. | ||
192 | */ | ||
193 | printk(KERN_NOTICE "libusual: " | ||
194 | "modprobe for %s succeeded, but module is not present\n", | ||
195 | bias_names[type]); | ||
196 | } | ||
197 | st->fls &= ~USU_MOD_FL_THREAD; | ||
198 | spin_unlock_irqrestore(&usu_lock, flags); | ||
199 | |||
200 | complete_and_exit(&usu_end_notify, 0); | ||
201 | } | ||
202 | |||
203 | /* | ||
204 | */ | ||
205 | static int __init usb_usual_init(void) | ||
206 | { | ||
207 | int rc; | ||
208 | |||
209 | bias[BIAS_NAME_SIZE-1] = 0; | ||
210 | usb_usual_bias = parse_bias(bias); | ||
211 | |||
212 | rc = usb_register(&usu_driver); | ||
213 | up(&usu_init_notify); | ||
214 | return rc; | ||
215 | } | ||
216 | |||
217 | static void __exit usb_usual_exit(void) | ||
218 | { | ||
219 | /* | ||
220 | * We do not check for any drivers present, because | ||
221 | * they keep us pinned with symbol references. | ||
222 | */ | ||
223 | |||
224 | usb_deregister(&usu_driver); | ||
225 | |||
226 | while (atomic_read(&total_threads) > 0) { | ||
227 | wait_for_completion(&usu_end_notify); | ||
228 | atomic_dec(&total_threads); | ||
229 | } | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * Validate and accept the bias parameter. | ||
234 | * Maybe make an sysfs method later. XXX | ||
235 | */ | ||
236 | static int parse_bias(const char *bias_s) | ||
237 | { | ||
238 | int i; | ||
239 | int bias_n = 0; | ||
240 | |||
241 | if (bias_s[0] == 0 || bias_s[0] == ' ') { | ||
242 | bias_n = USB_US_DEFAULT_BIAS; | ||
243 | } else { | ||
244 | for (i = 1; i < 3; i++) { | ||
245 | if (strcmp(bias_s, bias_names[i]) == 0) { | ||
246 | bias_n = i; | ||
247 | break; | ||
248 | } | ||
249 | } | ||
250 | if (bias_n == 0) { | ||
251 | bias_n = USB_US_DEFAULT_BIAS; | ||
252 | printk(KERN_INFO | ||
253 | "libusual: unknown bias \"%s\", using \"%s\"\n", | ||
254 | bias_s, bias_names[bias_n]); | ||
255 | } | ||
256 | } | ||
257 | return bias_n; | ||
258 | } | ||
259 | |||
260 | module_init(usb_usual_init); | ||
261 | module_exit(usb_usual_exit); | ||
262 | |||
263 | module_param_string(bias, bias, BIAS_NAME_SIZE, S_IRUGO|S_IWUSR); | ||
264 | MODULE_PARM_DESC(bias, "Bias to usb-storage or ub"); | ||
265 | |||
266 | MODULE_LICENSE("GPL"); | ||