diff options
Diffstat (limited to 'drivers/memstick/core/memstick.c')
-rw-r--r-- | drivers/memstick/core/memstick.c | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c new file mode 100644 index 0000000000..bba467fe4b --- /dev/null +++ b/drivers/memstick/core/memstick.c | |||
@@ -0,0 +1,614 @@ | |||
1 | /* | ||
2 | * Sony MemoryStick support | ||
3 | * | ||
4 | * Copyright (C) 2007 Alex Dubov <oakad@yahoo.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * Special thanks to Carlos Corbacho for providing various MemoryStick cards | ||
11 | * that made this driver possible. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/memstick.h> | ||
16 | #include <linux/idr.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/delay.h> | ||
19 | |||
20 | #define DRIVER_NAME "memstick" | ||
21 | #define DRIVER_VERSION "0.2" | ||
22 | |||
23 | static unsigned int cmd_retries = 3; | ||
24 | module_param(cmd_retries, uint, 0644); | ||
25 | |||
26 | static struct workqueue_struct *workqueue; | ||
27 | static DEFINE_IDR(memstick_host_idr); | ||
28 | static DEFINE_SPINLOCK(memstick_host_lock); | ||
29 | |||
30 | static int memstick_dev_match(struct memstick_dev *card, | ||
31 | struct memstick_device_id *id) | ||
32 | { | ||
33 | if (id->match_flags & MEMSTICK_MATCH_ALL) { | ||
34 | if ((id->type == card->id.type) | ||
35 | && (id->category == card->id.category) | ||
36 | && (id->class == card->id.class)) | ||
37 | return 1; | ||
38 | } | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int memstick_bus_match(struct device *dev, struct device_driver *drv) | ||
44 | { | ||
45 | struct memstick_dev *card = container_of(dev, struct memstick_dev, | ||
46 | dev); | ||
47 | struct memstick_driver *ms_drv = container_of(drv, | ||
48 | struct memstick_driver, | ||
49 | driver); | ||
50 | struct memstick_device_id *ids = ms_drv->id_table; | ||
51 | |||
52 | if (ids) { | ||
53 | while (ids->match_flags) { | ||
54 | if (memstick_dev_match(card, ids)) | ||
55 | return 1; | ||
56 | ++ids; | ||
57 | } | ||
58 | } | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int memstick_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
63 | { | ||
64 | struct memstick_dev *card = container_of(dev, struct memstick_dev, | ||
65 | dev); | ||
66 | |||
67 | if (add_uevent_var(env, "MEMSTICK_TYPE=%02X", card->id.type)) | ||
68 | return -ENOMEM; | ||
69 | |||
70 | if (add_uevent_var(env, "MEMSTICK_CATEGORY=%02X", card->id.category)) | ||
71 | return -ENOMEM; | ||
72 | |||
73 | if (add_uevent_var(env, "MEMSTICK_CLASS=%02X", card->id.class)) | ||
74 | return -ENOMEM; | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int memstick_device_probe(struct device *dev) | ||
80 | { | ||
81 | struct memstick_dev *card = container_of(dev, struct memstick_dev, | ||
82 | dev); | ||
83 | struct memstick_driver *drv = container_of(dev->driver, | ||
84 | struct memstick_driver, | ||
85 | driver); | ||
86 | int rc = -ENODEV; | ||
87 | |||
88 | if (dev->driver && drv->probe) { | ||
89 | rc = drv->probe(card); | ||
90 | if (!rc) | ||
91 | get_device(dev); | ||
92 | } | ||
93 | return rc; | ||
94 | } | ||
95 | |||
96 | static int memstick_device_remove(struct device *dev) | ||
97 | { | ||
98 | struct memstick_dev *card = container_of(dev, struct memstick_dev, | ||
99 | dev); | ||
100 | struct memstick_driver *drv = container_of(dev->driver, | ||
101 | struct memstick_driver, | ||
102 | driver); | ||
103 | |||
104 | if (dev->driver && drv->remove) { | ||
105 | drv->remove(card); | ||
106 | card->dev.driver = NULL; | ||
107 | } | ||
108 | |||
109 | put_device(dev); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | #ifdef CONFIG_PM | ||
114 | |||
115 | static int memstick_device_suspend(struct device *dev, pm_message_t state) | ||
116 | { | ||
117 | struct memstick_dev *card = container_of(dev, struct memstick_dev, | ||
118 | dev); | ||
119 | struct memstick_driver *drv = container_of(dev->driver, | ||
120 | struct memstick_driver, | ||
121 | driver); | ||
122 | |||
123 | if (dev->driver && drv->suspend) | ||
124 | return drv->suspend(card, state); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int memstick_device_resume(struct device *dev) | ||
129 | { | ||
130 | struct memstick_dev *card = container_of(dev, struct memstick_dev, | ||
131 | dev); | ||
132 | struct memstick_driver *drv = container_of(dev->driver, | ||
133 | struct memstick_driver, | ||
134 | driver); | ||
135 | |||
136 | if (dev->driver && drv->resume) | ||
137 | return drv->resume(card); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | #else | ||
142 | |||
143 | #define memstick_device_suspend NULL | ||
144 | #define memstick_device_resume NULL | ||
145 | |||
146 | #endif /* CONFIG_PM */ | ||
147 | |||
148 | #define MEMSTICK_ATTR(name, format) \ | ||
149 | static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ | ||
150 | char *buf) \ | ||
151 | { \ | ||
152 | struct memstick_dev *card = container_of(dev, struct memstick_dev, \ | ||
153 | dev); \ | ||
154 | return sprintf(buf, format, card->id.name); \ | ||
155 | } | ||
156 | |||
157 | MEMSTICK_ATTR(type, "%02X"); | ||
158 | MEMSTICK_ATTR(category, "%02X"); | ||
159 | MEMSTICK_ATTR(class, "%02X"); | ||
160 | |||
161 | #define MEMSTICK_ATTR_RO(name) __ATTR(name, S_IRUGO, name##_show, NULL) | ||
162 | |||
163 | static struct device_attribute memstick_dev_attrs[] = { | ||
164 | MEMSTICK_ATTR_RO(type), | ||
165 | MEMSTICK_ATTR_RO(category), | ||
166 | MEMSTICK_ATTR_RO(class), | ||
167 | __ATTR_NULL | ||
168 | }; | ||
169 | |||
170 | static struct bus_type memstick_bus_type = { | ||
171 | .name = "memstick", | ||
172 | .dev_attrs = memstick_dev_attrs, | ||
173 | .match = memstick_bus_match, | ||
174 | .uevent = memstick_uevent, | ||
175 | .probe = memstick_device_probe, | ||
176 | .remove = memstick_device_remove, | ||
177 | .suspend = memstick_device_suspend, | ||
178 | .resume = memstick_device_resume | ||
179 | }; | ||
180 | |||
181 | static void memstick_free(struct class_device *cdev) | ||
182 | { | ||
183 | struct memstick_host *host = container_of(cdev, struct memstick_host, | ||
184 | cdev); | ||
185 | kfree(host); | ||
186 | } | ||
187 | |||
188 | static struct class memstick_host_class = { | ||
189 | .name = "memstick_host", | ||
190 | .release = memstick_free | ||
191 | }; | ||
192 | |||
193 | static void memstick_free_card(struct device *dev) | ||
194 | { | ||
195 | struct memstick_dev *card = container_of(dev, struct memstick_dev, | ||
196 | dev); | ||
197 | kfree(card); | ||
198 | } | ||
199 | |||
200 | static int memstick_dummy_check(struct memstick_dev *card) | ||
201 | { | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * memstick_detect_change - schedule media detection on memstick host | ||
207 | * @host - host to use | ||
208 | */ | ||
209 | void memstick_detect_change(struct memstick_host *host) | ||
210 | { | ||
211 | queue_work(workqueue, &host->media_checker); | ||
212 | } | ||
213 | EXPORT_SYMBOL(memstick_detect_change); | ||
214 | |||
215 | /** | ||
216 | * memstick_next_req - called by host driver to obtain next request to process | ||
217 | * @host - host to use | ||
218 | * @mrq - pointer to stick the request to | ||
219 | * | ||
220 | * Host calls this function from idle state (*mrq == NULL) or after finishing | ||
221 | * previous request (*mrq should point to it). If previous request was | ||
222 | * unsuccessful, it is retried for predetermined number of times. Return value | ||
223 | * of 0 means that new request was assigned to the host. | ||
224 | */ | ||
225 | int memstick_next_req(struct memstick_host *host, struct memstick_request **mrq) | ||
226 | { | ||
227 | int rc = -ENXIO; | ||
228 | |||
229 | if ((*mrq) && (*mrq)->error && host->retries) { | ||
230 | (*mrq)->error = rc; | ||
231 | host->retries--; | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | if (host->card && host->card->next_request) | ||
236 | rc = host->card->next_request(host->card, mrq); | ||
237 | |||
238 | if (!rc) | ||
239 | host->retries = cmd_retries; | ||
240 | else | ||
241 | *mrq = NULL; | ||
242 | |||
243 | return rc; | ||
244 | } | ||
245 | EXPORT_SYMBOL(memstick_next_req); | ||
246 | |||
247 | /** | ||
248 | * memstick_new_req - notify the host that some requests are pending | ||
249 | * @host - host to use | ||
250 | */ | ||
251 | void memstick_new_req(struct memstick_host *host) | ||
252 | { | ||
253 | host->retries = cmd_retries; | ||
254 | host->request(host); | ||
255 | } | ||
256 | EXPORT_SYMBOL(memstick_new_req); | ||
257 | |||
258 | /** | ||
259 | * memstick_init_req_sg - set request fields needed for bulk data transfer | ||
260 | * @mrq - request to use | ||
261 | * @tpc - memstick Transport Protocol Command | ||
262 | * @sg - TPC argument | ||
263 | */ | ||
264 | void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc, | ||
265 | struct scatterlist *sg) | ||
266 | { | ||
267 | mrq->tpc = tpc; | ||
268 | if (tpc & 8) | ||
269 | mrq->data_dir = WRITE; | ||
270 | else | ||
271 | mrq->data_dir = READ; | ||
272 | |||
273 | mrq->sg = *sg; | ||
274 | mrq->io_type = MEMSTICK_IO_SG; | ||
275 | |||
276 | if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD) | ||
277 | mrq->need_card_int = 1; | ||
278 | else | ||
279 | mrq->need_card_int = 0; | ||
280 | |||
281 | mrq->get_int_reg = 0; | ||
282 | } | ||
283 | EXPORT_SYMBOL(memstick_init_req_sg); | ||
284 | |||
285 | /** | ||
286 | * memstick_init_req - set request fields needed for short data transfer | ||
287 | * @mrq - request to use | ||
288 | * @tpc - memstick Transport Protocol Command | ||
289 | * @buf - TPC argument buffer | ||
290 | * @length - TPC argument size | ||
291 | * | ||
292 | * The intended use of this function (transfer of data items several bytes | ||
293 | * in size) allows us to just copy the value between request structure and | ||
294 | * user supplied buffer. | ||
295 | */ | ||
296 | void memstick_init_req(struct memstick_request *mrq, unsigned char tpc, | ||
297 | void *buf, size_t length) | ||
298 | { | ||
299 | mrq->tpc = tpc; | ||
300 | if (tpc & 8) | ||
301 | mrq->data_dir = WRITE; | ||
302 | else | ||
303 | mrq->data_dir = READ; | ||
304 | |||
305 | mrq->data_len = length > sizeof(mrq->data) ? sizeof(mrq->data) : length; | ||
306 | if (mrq->data_dir == WRITE) | ||
307 | memcpy(mrq->data, buf, mrq->data_len); | ||
308 | |||
309 | mrq->io_type = MEMSTICK_IO_VAL; | ||
310 | |||
311 | if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD) | ||
312 | mrq->need_card_int = 1; | ||
313 | else | ||
314 | mrq->need_card_int = 0; | ||
315 | |||
316 | mrq->get_int_reg = 0; | ||
317 | } | ||
318 | EXPORT_SYMBOL(memstick_init_req); | ||
319 | |||
320 | /* | ||
321 | * Functions prefixed with "h_" are protocol callbacks. They can be called from | ||
322 | * interrupt context. Return value of 0 means that request processing is still | ||
323 | * ongoing, while special error value of -EAGAIN means that current request is | ||
324 | * finished (and request processor should come back some time later). | ||
325 | */ | ||
326 | |||
327 | static int h_memstick_read_dev_id(struct memstick_dev *card, | ||
328 | struct memstick_request **mrq) | ||
329 | { | ||
330 | struct ms_id_register id_reg; | ||
331 | |||
332 | if (!(*mrq)) { | ||
333 | memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, NULL, | ||
334 | sizeof(struct ms_id_register)); | ||
335 | *mrq = &card->current_mrq; | ||
336 | return 0; | ||
337 | } else { | ||
338 | if (!(*mrq)->error) { | ||
339 | memcpy(&id_reg, (*mrq)->data, sizeof(id_reg)); | ||
340 | card->id.match_flags = MEMSTICK_MATCH_ALL; | ||
341 | card->id.type = id_reg.type; | ||
342 | card->id.category = id_reg.category; | ||
343 | card->id.class = id_reg.class; | ||
344 | } | ||
345 | complete(&card->mrq_complete); | ||
346 | return -EAGAIN; | ||
347 | } | ||
348 | } | ||
349 | |||
350 | static int h_memstick_set_rw_addr(struct memstick_dev *card, | ||
351 | struct memstick_request **mrq) | ||
352 | { | ||
353 | if (!(*mrq)) { | ||
354 | memstick_init_req(&card->current_mrq, MS_TPC_SET_RW_REG_ADRS, | ||
355 | (char *)&card->reg_addr, | ||
356 | sizeof(card->reg_addr)); | ||
357 | *mrq = &card->current_mrq; | ||
358 | return 0; | ||
359 | } else { | ||
360 | complete(&card->mrq_complete); | ||
361 | return -EAGAIN; | ||
362 | } | ||
363 | } | ||
364 | |||
365 | /** | ||
366 | * memstick_set_rw_addr - issue SET_RW_REG_ADDR request and wait for it to | ||
367 | * complete | ||
368 | * @card - media device to use | ||
369 | */ | ||
370 | int memstick_set_rw_addr(struct memstick_dev *card) | ||
371 | { | ||
372 | card->next_request = h_memstick_set_rw_addr; | ||
373 | memstick_new_req(card->host); | ||
374 | wait_for_completion(&card->mrq_complete); | ||
375 | |||
376 | return card->current_mrq.error; | ||
377 | } | ||
378 | EXPORT_SYMBOL(memstick_set_rw_addr); | ||
379 | |||
380 | static struct memstick_dev *memstick_alloc_card(struct memstick_host *host) | ||
381 | { | ||
382 | struct memstick_dev *card = kzalloc(sizeof(struct memstick_dev), | ||
383 | GFP_KERNEL); | ||
384 | struct memstick_dev *old_card = host->card; | ||
385 | struct ms_id_register id_reg; | ||
386 | |||
387 | if (card) { | ||
388 | card->host = host; | ||
389 | snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), | ||
390 | "%s", host->cdev.class_id); | ||
391 | card->dev.parent = host->cdev.dev; | ||
392 | card->dev.bus = &memstick_bus_type; | ||
393 | card->dev.release = memstick_free_card; | ||
394 | card->check = memstick_dummy_check; | ||
395 | |||
396 | card->reg_addr.r_offset = offsetof(struct ms_register, id); | ||
397 | card->reg_addr.r_length = sizeof(id_reg); | ||
398 | card->reg_addr.w_offset = offsetof(struct ms_register, id); | ||
399 | card->reg_addr.w_length = sizeof(id_reg); | ||
400 | |||
401 | init_completion(&card->mrq_complete); | ||
402 | |||
403 | host->card = card; | ||
404 | if (memstick_set_rw_addr(card)) | ||
405 | goto err_out; | ||
406 | |||
407 | card->next_request = h_memstick_read_dev_id; | ||
408 | memstick_new_req(host); | ||
409 | wait_for_completion(&card->mrq_complete); | ||
410 | |||
411 | if (card->current_mrq.error) | ||
412 | goto err_out; | ||
413 | } | ||
414 | host->card = old_card; | ||
415 | return card; | ||
416 | err_out: | ||
417 | host->card = old_card; | ||
418 | kfree(card); | ||
419 | return NULL; | ||
420 | } | ||
421 | |||
422 | static void memstick_power_on(struct memstick_host *host) | ||
423 | { | ||
424 | host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON); | ||
425 | host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL); | ||
426 | msleep(1); | ||
427 | } | ||
428 | |||
429 | static void memstick_check(struct work_struct *work) | ||
430 | { | ||
431 | struct memstick_host *host = container_of(work, struct memstick_host, | ||
432 | media_checker); | ||
433 | struct memstick_dev *card; | ||
434 | |||
435 | dev_dbg(host->cdev.dev, "memstick_check started\n"); | ||
436 | mutex_lock(&host->lock); | ||
437 | if (!host->card) | ||
438 | memstick_power_on(host); | ||
439 | |||
440 | card = memstick_alloc_card(host); | ||
441 | |||
442 | if (!card) { | ||
443 | if (host->card) { | ||
444 | device_unregister(&host->card->dev); | ||
445 | host->card = NULL; | ||
446 | } | ||
447 | } else { | ||
448 | dev_dbg(host->cdev.dev, "new card %02x, %02x, %02x\n", | ||
449 | card->id.type, card->id.category, card->id.class); | ||
450 | if (host->card) { | ||
451 | if (memstick_set_rw_addr(host->card) | ||
452 | || !memstick_dev_match(host->card, &card->id) | ||
453 | || !(host->card->check(host->card))) { | ||
454 | device_unregister(&host->card->dev); | ||
455 | host->card = NULL; | ||
456 | } | ||
457 | } | ||
458 | |||
459 | if (!host->card) { | ||
460 | host->card = card; | ||
461 | if (device_register(&card->dev)) { | ||
462 | kfree(host->card); | ||
463 | host->card = NULL; | ||
464 | } | ||
465 | } else | ||
466 | kfree(card); | ||
467 | } | ||
468 | |||
469 | if (!host->card) | ||
470 | host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); | ||
471 | |||
472 | mutex_unlock(&host->lock); | ||
473 | dev_dbg(host->cdev.dev, "memstick_check finished\n"); | ||
474 | } | ||
475 | |||
476 | /** | ||
477 | * memstick_alloc_host - allocate a memstick_host structure | ||
478 | * @extra: size of the user private data to allocate | ||
479 | * @dev: parent device of the host | ||
480 | */ | ||
481 | struct memstick_host *memstick_alloc_host(unsigned int extra, | ||
482 | struct device *dev) | ||
483 | { | ||
484 | struct memstick_host *host; | ||
485 | |||
486 | host = kzalloc(sizeof(struct memstick_host) + extra, GFP_KERNEL); | ||
487 | if (host) { | ||
488 | mutex_init(&host->lock); | ||
489 | INIT_WORK(&host->media_checker, memstick_check); | ||
490 | host->cdev.class = &memstick_host_class; | ||
491 | host->cdev.dev = dev; | ||
492 | class_device_initialize(&host->cdev); | ||
493 | } | ||
494 | return host; | ||
495 | } | ||
496 | EXPORT_SYMBOL(memstick_alloc_host); | ||
497 | |||
498 | /** | ||
499 | * memstick_add_host - start request processing on memstick host | ||
500 | * @host - host to use | ||
501 | */ | ||
502 | int memstick_add_host(struct memstick_host *host) | ||
503 | { | ||
504 | int rc; | ||
505 | |||
506 | if (!idr_pre_get(&memstick_host_idr, GFP_KERNEL)) | ||
507 | return -ENOMEM; | ||
508 | |||
509 | spin_lock(&memstick_host_lock); | ||
510 | rc = idr_get_new(&memstick_host_idr, host, &host->id); | ||
511 | spin_unlock(&memstick_host_lock); | ||
512 | if (rc) | ||
513 | return rc; | ||
514 | |||
515 | snprintf(host->cdev.class_id, BUS_ID_SIZE, | ||
516 | "memstick%u", host->id); | ||
517 | |||
518 | rc = class_device_add(&host->cdev); | ||
519 | if (rc) { | ||
520 | spin_lock(&memstick_host_lock); | ||
521 | idr_remove(&memstick_host_idr, host->id); | ||
522 | spin_unlock(&memstick_host_lock); | ||
523 | return rc; | ||
524 | } | ||
525 | |||
526 | host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); | ||
527 | memstick_detect_change(host); | ||
528 | return 0; | ||
529 | } | ||
530 | EXPORT_SYMBOL(memstick_add_host); | ||
531 | |||
532 | /** | ||
533 | * memstick_remove_host - stop request processing on memstick host | ||
534 | * @host - host to use | ||
535 | */ | ||
536 | void memstick_remove_host(struct memstick_host *host) | ||
537 | { | ||
538 | flush_workqueue(workqueue); | ||
539 | mutex_lock(&host->lock); | ||
540 | if (host->card) | ||
541 | device_unregister(&host->card->dev); | ||
542 | host->card = NULL; | ||
543 | host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); | ||
544 | mutex_unlock(&host->lock); | ||
545 | |||
546 | spin_lock(&memstick_host_lock); | ||
547 | idr_remove(&memstick_host_idr, host->id); | ||
548 | spin_unlock(&memstick_host_lock); | ||
549 | class_device_del(&host->cdev); | ||
550 | } | ||
551 | EXPORT_SYMBOL(memstick_remove_host); | ||
552 | |||
553 | /** | ||
554 | * memstick_free_host - free memstick host | ||
555 | * @host - host to use | ||
556 | */ | ||
557 | void memstick_free_host(struct memstick_host *host) | ||
558 | { | ||
559 | mutex_destroy(&host->lock); | ||
560 | class_device_put(&host->cdev); | ||
561 | } | ||
562 | EXPORT_SYMBOL(memstick_free_host); | ||
563 | |||
564 | int memstick_register_driver(struct memstick_driver *drv) | ||
565 | { | ||
566 | drv->driver.bus = &memstick_bus_type; | ||
567 | |||
568 | return driver_register(&drv->driver); | ||
569 | } | ||
570 | EXPORT_SYMBOL(memstick_register_driver); | ||
571 | |||
572 | void memstick_unregister_driver(struct memstick_driver *drv) | ||
573 | { | ||
574 | driver_unregister(&drv->driver); | ||
575 | } | ||
576 | EXPORT_SYMBOL(memstick_unregister_driver); | ||
577 | |||
578 | |||
579 | static int __init memstick_init(void) | ||
580 | { | ||
581 | int rc; | ||
582 | |||
583 | workqueue = create_freezeable_workqueue("kmemstick"); | ||
584 | if (!workqueue) | ||
585 | return -ENOMEM; | ||
586 | |||
587 | rc = bus_register(&memstick_bus_type); | ||
588 | if (!rc) | ||
589 | rc = class_register(&memstick_host_class); | ||
590 | |||
591 | if (!rc) | ||
592 | return 0; | ||
593 | |||
594 | bus_unregister(&memstick_bus_type); | ||
595 | destroy_workqueue(workqueue); | ||
596 | |||
597 | return rc; | ||
598 | } | ||
599 | |||
600 | static void __exit memstick_exit(void) | ||
601 | { | ||
602 | class_unregister(&memstick_host_class); | ||
603 | bus_unregister(&memstick_bus_type); | ||
604 | destroy_workqueue(workqueue); | ||
605 | idr_destroy(&memstick_host_idr); | ||
606 | } | ||
607 | |||
608 | module_init(memstick_init); | ||
609 | module_exit(memstick_exit); | ||
610 | |||
611 | MODULE_AUTHOR("Alex Dubov"); | ||
612 | MODULE_LICENSE("GPL"); | ||
613 | MODULE_DESCRIPTION("Sony MemoryStick core driver"); | ||
614 | MODULE_VERSION(DRIVER_VERSION); | ||