diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/usb/storage | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/usb/storage')
-rw-r--r-- | drivers/usb/storage/libusual.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c new file mode 100644 index 00000000000..fe3ffe1459b --- /dev/null +++ b/drivers/usb/storage/libusual.c | |||
@@ -0,0 +1,243 @@ | |||
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 | |||
19 | struct mod_status { | ||
20 | unsigned long fls; | ||
21 | }; | ||
22 | |||
23 | static struct mod_status stat[3]; | ||
24 | static DEFINE_SPINLOCK(usu_lock); | ||
25 | |||
26 | /* | ||
27 | */ | ||
28 | #define USB_US_DEFAULT_BIAS USB_US_TYPE_STOR | ||
29 | static atomic_t usu_bias = ATOMIC_INIT(USB_US_DEFAULT_BIAS); | ||
30 | |||
31 | #define BIAS_NAME_SIZE (sizeof("usb-storage")) | ||
32 | static const char *bias_names[3] = { "none", "usb-storage", "ub" }; | ||
33 | |||
34 | static DEFINE_MUTEX(usu_probe_mutex); | ||
35 | static DECLARE_COMPLETION(usu_end_notify); | ||
36 | static atomic_t total_threads = ATOMIC_INIT(0); | ||
37 | |||
38 | static int usu_probe_thread(void *arg); | ||
39 | |||
40 | /* | ||
41 | * @type: the module type as an integer | ||
42 | */ | ||
43 | void 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 | } | ||
55 | EXPORT_SYMBOL_GPL(usb_usual_set_present); | ||
56 | |||
57 | void 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 | } | ||
69 | EXPORT_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 | */ | ||
75 | int 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 | } | ||
90 | EXPORT_SYMBOL_GPL(usb_usual_check_type); | ||
91 | |||
92 | /* | ||
93 | */ | ||
94 | static 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 | |||
130 | static void usu_disconnect(struct usb_interface *intf) | ||
131 | { | ||
132 | ; /* We should not be here. */ | ||
133 | } | ||
134 | |||
135 | static 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 | */ | ||
151 | static 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 | */ | ||
178 | static 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 | |||
188 | static 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 | */ | ||
206 | static 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 | |||
231 | static int usu_get_bias(char *buffer, struct kernel_param *kp) | ||
232 | { | ||
233 | return strlen(strcpy(buffer, bias_names[atomic_read(&usu_bias)])); | ||
234 | } | ||
235 | |||
236 | module_init(usb_usual_init); | ||
237 | module_exit(usb_usual_exit); | ||
238 | |||
239 | module_param_call(bias, usu_set_bias, usu_get_bias, NULL, S_IRUGO|S_IWUSR); | ||
240 | __MODULE_PARM_TYPE(bias, "string"); | ||
241 | MODULE_PARM_DESC(bias, "Bias to usb-storage or ub"); | ||
242 | |||
243 | MODULE_LICENSE("GPL"); | ||