blob: e8d2c95db1c90193d8c472e4a681d756cf2542a1 [file] [log] [blame]
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001/*
2 * pcmcia_ioctl.c -- ioctl interface for cardmgr and cardctl
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * The initial developer of the original code is David A. Hinds
9 * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
10 * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
11 *
12 * (C) 1999 David A. Hinds
13 * (C) 2003 - 2004 Dominik Brodowski
14 */
15
16/*
17 * This file will go away soon.
18 */
19
20
21#include <linux/config.h>
22#include <linux/module.h>
23#include <linux/moduleparam.h>
24#include <linux/init.h>
25#include <linux/kernel.h>
26#include <linux/major.h>
27#include <linux/string.h>
28#include <linux/errno.h>
29#include <linux/slab.h>
30#include <linux/mm.h>
31#include <linux/fcntl.h>
32#include <linux/sched.h>
33#include <linux/smp_lock.h>
34#include <linux/timer.h>
35#include <linux/ioctl.h>
36#include <linux/proc_fs.h>
37#include <linux/poll.h>
38#include <linux/pci.h>
39#include <linux/list.h>
40#include <linux/delay.h>
41#include <linux/kref.h>
42#include <linux/workqueue.h>
43#include <linux/crc32.h>
44
45#include <asm/atomic.h>
46
47#define IN_CARD_SERVICES
48#include <pcmcia/version.h>
49#include <pcmcia/cs_types.h>
50#include <pcmcia/cs.h>
51#include <pcmcia/bulkmem.h>
52#include <pcmcia/cistpl.h>
53#include <pcmcia/ds.h>
54#include <pcmcia/ss.h>
55
56#include "cs_internal.h"
57#include "ds_internal.h"
58
59static int major_dev = -1;
60
61
62/* Device user information */
63#define MAX_EVENTS 32
64#define USER_MAGIC 0x7ea4
65#define CHECK_USER(u) \
66 (((u) == NULL) || ((u)->user_magic != USER_MAGIC))
67
68typedef struct user_info_t {
69 u_int user_magic;
70 int event_head, event_tail;
71 event_t event[MAX_EVENTS];
72 struct user_info_t *next;
73 struct pcmcia_bus_socket *socket;
74} user_info_t;
75
76
77#ifdef DEBUG
78extern int ds_pc_debug;
79#define cs_socket_name(skt) ((skt)->dev.class_id)
80
81#define ds_dbg(lvl, fmt, arg...) do { \
82 if (ds_pc_debug >= lvl) \
83 printk(KERN_DEBUG "ds: " fmt , ## arg); \
84} while (0)
85#else
86#define ds_dbg(lvl, fmt, arg...) do { } while (0)
87#endif
88
89
90static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr)
91{
92 struct pcmcia_socket * s = pcmcia_get_socket_by_nr(nr);
93 if (s && s->pcmcia)
94 return s->pcmcia;
95 else
96 return NULL;
97}
98
99/* backwards-compatible accessing of driver --- by name! */
100
101static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info)
102{
103 struct device_driver *drv;
104 struct pcmcia_driver *p_drv;
105
106 drv = driver_find((char *) dev_info, &pcmcia_bus_type);
107 if (!drv)
108 return NULL;
109
110 p_drv = container_of(drv, struct pcmcia_driver, drv);
111
112 return (p_drv);
113}
114
115
116#ifdef CONFIG_PROC_FS
117static struct proc_dir_entry *proc_pccard = NULL;
118
119static int proc_read_drivers_callback(struct device_driver *driver, void *d)
120{
121 char **p = d;
122 struct pcmcia_driver *p_drv = container_of(driver,
123 struct pcmcia_driver, drv);
124
125 *p += sprintf(*p, "%-24.24s 1 %d\n", p_drv->drv.name,
126#ifdef CONFIG_MODULE_UNLOAD
127 (p_drv->owner) ? module_refcount(p_drv->owner) : 1
128#else
129 1
130#endif
131 );
132 d = (void *) p;
133
134 return 0;
135}
136
137static int proc_read_drivers(char *buf, char **start, off_t pos,
138 int count, int *eof, void *data)
139{
140 char *p = buf;
141
142 bus_for_each_drv(&pcmcia_bus_type, NULL,
143 (void *) &p, proc_read_drivers_callback);
144
145 return (p - buf);
146}
147#endif
148
149/*======================================================================
150
151 These manage a ring buffer of events pending for one user process
152
153======================================================================*/
154
155
156static int queue_empty(user_info_t *user)
157{
158 return (user->event_head == user->event_tail);
159}
160
161static event_t get_queued_event(user_info_t *user)
162{
163 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
164 return user->event[user->event_tail];
165}
166
167static void queue_event(user_info_t *user, event_t event)
168{
169 user->event_head = (user->event_head+1) % MAX_EVENTS;
170 if (user->event_head == user->event_tail)
171 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
172 user->event[user->event_head] = event;
173}
174
175void handle_event(struct pcmcia_bus_socket *s, event_t event)
176{
177 user_info_t *user;
178 for (user = s->user; user; user = user->next)
179 queue_event(user, event);
180 wake_up_interruptible(&s->queue);
181}
182
183
184/*======================================================================
185
186 bind_request() and bind_device() are merged by now. Register_client()
187 is called right at the end of bind_request(), during the driver's
188 ->attach() call. Individual descriptions:
189
190 bind_request() connects a socket to a particular client driver.
191 It looks up the specified device ID in the list of registered
192 drivers, binds it to the socket, and tries to create an instance
193 of the device. unbind_request() deletes a driver instance.
194
195 Bind_device() associates a device driver with a particular socket.
196 It is normally called by Driver Services after it has identified
197 a newly inserted card. An instance of that driver will then be
198 eligible to register as a client of this socket.
199
200 Register_client() uses the dev_info_t handle to match the
201 caller with a socket. The driver must have already been bound
202 to a socket with bind_device() -- in fact, bind_device()
203 allocates the client structure that will be used.
204
205======================================================================*/
206
207static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info)
208{
209 struct pcmcia_driver *p_drv;
210 struct pcmcia_device *p_dev;
211 int ret = 0;
212 unsigned long flags;
213
214 s = pcmcia_get_bus_socket(s);
215 if (!s)
216 return -EINVAL;
217
218 ds_dbg(2, "bind_request(%d, '%s')\n", s->parent->sock,
219 (char *)bind_info->dev_info);
220
221 p_drv = get_pcmcia_driver(&bind_info->dev_info);
222 if (!p_drv) {
223 ret = -EINVAL;
224 goto err_put;
225 }
226
227 if (!try_module_get(p_drv->owner)) {
228 ret = -EINVAL;
229 goto err_put_driver;
230 }
231
232 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
233 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
234 if (p_dev->func == bind_info->function) {
235 if ((p_dev->dev.driver == &p_drv->drv)) {
236 if (p_dev->cardmgr) {
237 /* if there's already a device
238 * registered, and it was registered
239 * by userspace before, we need to
240 * return the "instance". */
241 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
242 bind_info->instance = p_dev->instance;
243 ret = -EBUSY;
244 goto err_put_module;
245 } else {
246 /* the correct driver managed to bind
247 * itself magically to the correct
248 * device. */
249 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
250 p_dev->cardmgr = p_drv;
251 ret = 0;
252 goto err_put_module;
253 }
254 } else if (!p_dev->dev.driver) {
255 /* there's already a device available where
256 * no device has been bound to yet. So we don't
257 * need to register a device! */
258 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
259 goto rescan;
260 }
261 }
262 }
263 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
264
265 p_dev = pcmcia_device_add(s, bind_info->function);
266 if (!p_dev) {
267 ret = -EIO;
268 goto err_put_module;
269 }
270
271rescan:
272 p_dev->cardmgr = p_drv;
273
274 /* if a driver is already running, we can abort */
275 if (p_dev->dev.driver)
276 goto err_put_module;
277
278 /*
279 * Prevent this racing with a card insertion.
280 */
281 down(&s->parent->skt_sem);
282 bus_rescan_devices(&pcmcia_bus_type);
283 up(&s->parent->skt_sem);
284
285 /* check whether the driver indeed matched. I don't care if this
286 * is racy or not, because it can only happen on cardmgr access
287 * paths...
288 */
289 if (!(p_dev->dev.driver == &p_drv->drv))
290 p_dev->cardmgr = NULL;
291
292 err_put_module:
293 module_put(p_drv->owner);
294 err_put_driver:
295 put_driver(&p_drv->drv);
296 err_put:
297 pcmcia_put_bus_socket(s);
298
299 return (ret);
300} /* bind_request */
301
302
303extern struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s);
304
305static int get_device_info(struct pcmcia_bus_socket *s, bind_info_t *bind_info, int first)
306{
307 dev_node_t *node;
308 struct pcmcia_device *p_dev;
309 unsigned long flags;
310 int ret = 0;
311
312#ifdef CONFIG_CARDBUS
313 /*
314 * Some unbelievably ugly code to associate the PCI cardbus
315 * device and its driver with the PCMCIA "bind" information.
316 */
317 {
318 struct pci_bus *bus;
319
320 bus = pcmcia_lookup_bus(s->parent);
321 if (bus) {
322 struct list_head *list;
323 struct pci_dev *dev = NULL;
324
325 list = bus->devices.next;
326 while (list != &bus->devices) {
327 struct pci_dev *pdev = pci_dev_b(list);
328 list = list->next;
329
330 if (first) {
331 dev = pdev;
332 break;
333 }
334
335 /* Try to handle "next" here some way? */
336 }
337 if (dev && dev->driver) {
338 strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN);
339 bind_info->major = 0;
340 bind_info->minor = 0;
341 bind_info->next = NULL;
342 return 0;
343 }
344 }
345 }
346#endif
347
348 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
349 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
350 if (p_dev->func == bind_info->function) {
351 p_dev = pcmcia_get_dev(p_dev);
352 if (!p_dev)
353 continue;
354 goto found;
355 }
356 }
357 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
358 return -ENODEV;
359
360 found:
361 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
362
363 if ((!p_dev->instance) ||
364 (p_dev->instance->state & DEV_CONFIG_PENDING)) {
365 ret = -EAGAIN;
366 goto err_put;
367 }
368
369 if (first)
370 node = p_dev->instance->dev;
371 else
372 for (node = p_dev->instance->dev; node; node = node->next)
373 if (node == bind_info->next)
374 break;
375 if (!node) {
376 ret = -ENODEV;
377 goto err_put;
378 }
379
380 strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN);
381 bind_info->major = node->major;
382 bind_info->minor = node->minor;
383 bind_info->next = node->next;
384
385 err_put:
386 pcmcia_put_dev(p_dev);
387 return (ret);
388} /* get_device_info */
389
390
391static int ds_open(struct inode *inode, struct file *file)
392{
393 socket_t i = iminor(inode);
394 struct pcmcia_bus_socket *s;
395 user_info_t *user;
396
397 ds_dbg(0, "ds_open(socket %d)\n", i);
398
399 s = get_socket_info_by_nr(i);
400 if (!s)
401 return -ENODEV;
402 s = pcmcia_get_bus_socket(s);
403 if (!s)
404 return -ENODEV;
405
406 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
407 if (s->state & DS_SOCKET_BUSY) {
408 pcmcia_put_bus_socket(s);
409 return -EBUSY;
410 }
411 else
412 s->state |= DS_SOCKET_BUSY;
413 }
414
415 user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
416 if (!user) {
417 pcmcia_put_bus_socket(s);
418 return -ENOMEM;
419 }
420 user->event_tail = user->event_head = 0;
421 user->next = s->user;
422 user->user_magic = USER_MAGIC;
423 user->socket = s;
424 s->user = user;
425 file->private_data = user;
426
427 if (s->state & DS_SOCKET_PRESENT)
428 queue_event(user, CS_EVENT_CARD_INSERTION);
429 return 0;
430} /* ds_open */
431
432/*====================================================================*/
433
434static int ds_release(struct inode *inode, struct file *file)
435{
436 struct pcmcia_bus_socket *s;
437 user_info_t *user, **link;
438
439 ds_dbg(0, "ds_release(socket %d)\n", iminor(inode));
440
441 user = file->private_data;
442 if (CHECK_USER(user))
443 goto out;
444
445 s = user->socket;
446
447 /* Unlink user data structure */
448 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
449 s->state &= ~DS_SOCKET_BUSY;
450 }
451 file->private_data = NULL;
452 for (link = &s->user; *link; link = &(*link)->next)
453 if (*link == user) break;
454 if (link == NULL)
455 goto out;
456 *link = user->next;
457 user->user_magic = 0;
458 kfree(user);
459 pcmcia_put_bus_socket(s);
460out:
461 return 0;
462} /* ds_release */
463
464/*====================================================================*/
465
466static ssize_t ds_read(struct file *file, char __user *buf,
467 size_t count, loff_t *ppos)
468{
469 struct pcmcia_bus_socket *s;
470 user_info_t *user;
471 int ret;
472
473 ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_dentry->d_inode));
474
475 if (count < 4)
476 return -EINVAL;
477
478 user = file->private_data;
479 if (CHECK_USER(user))
480 return -EIO;
481
482 s = user->socket;
483 if (s->state & DS_SOCKET_DEAD)
484 return -EIO;
485
486 ret = wait_event_interruptible(s->queue, !queue_empty(user));
487 if (ret == 0)
488 ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;
489
490 return ret;
491} /* ds_read */
492
493/*====================================================================*/
494
495static ssize_t ds_write(struct file *file, const char __user *buf,
496 size_t count, loff_t *ppos)
497{
498 ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_dentry->d_inode));
499
500 if (count != 4)
501 return -EINVAL;
502 if ((file->f_flags & O_ACCMODE) == O_RDONLY)
503 return -EBADF;
504
505 return -EIO;
506} /* ds_write */
507
508/*====================================================================*/
509
510/* No kernel lock - fine */
511static u_int ds_poll(struct file *file, poll_table *wait)
512{
513 struct pcmcia_bus_socket *s;
514 user_info_t *user;
515
516 ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_dentry->d_inode));
517
518 user = file->private_data;
519 if (CHECK_USER(user))
520 return POLLERR;
521 s = user->socket;
522 /*
523 * We don't check for a dead socket here since that
524 * will send cardmgr into an endless spin.
525 */
526 poll_wait(file, &s->queue, wait);
527 if (!queue_empty(user))
528 return POLLIN | POLLRDNORM;
529 return 0;
530} /* ds_poll */
531
532/*====================================================================*/
533
534extern int pcmcia_adjust_resource_info(adjust_t *adj);
535
536static int ds_ioctl(struct inode * inode, struct file * file,
537 u_int cmd, u_long arg)
538{
539 struct pcmcia_bus_socket *s;
540 void __user *uarg = (char __user *)arg;
541 u_int size;
542 int ret, err;
543 ds_ioctl_arg_t *buf;
544 user_info_t *user;
545
546 ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
547
548 user = file->private_data;
549 if (CHECK_USER(user))
550 return -EIO;
551
552 s = user->socket;
553 if (s->state & DS_SOCKET_DEAD)
554 return -EIO;
555
556 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
557 if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
558
559 /* Permission check */
560 if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
561 return -EPERM;
562
563 if (cmd & IOC_IN) {
564 if (!access_ok(VERIFY_READ, uarg, size)) {
565 ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT);
566 return -EFAULT;
567 }
568 }
569 if (cmd & IOC_OUT) {
570 if (!access_ok(VERIFY_WRITE, uarg, size)) {
571 ds_dbg(3, "ds_ioctl(): verify_write = %d\n", -EFAULT);
572 return -EFAULT;
573 }
574 }
575 buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL);
576 if (!buf)
577 return -ENOMEM;
578
579 err = ret = 0;
580
581 if (cmd & IOC_IN) __copy_from_user((char *)buf, uarg, size);
582
583 switch (cmd) {
584 case DS_ADJUST_RESOURCE_INFO:
585 ret = pcmcia_adjust_resource_info(&buf->adjust);
586 break;
587 case DS_GET_CARD_SERVICES_INFO:
588 ret = pcmcia_get_card_services_info(&buf->servinfo);
589 break;
590 case DS_GET_CONFIGURATION_INFO:
591 if (buf->config.Function &&
592 (buf->config.Function >= s->parent->functions))
593 ret = CS_BAD_ARGS;
594 else
595 ret = pccard_get_configuration_info(s->parent,
596 buf->config.Function, &buf->config);
597 break;
598 case DS_GET_FIRST_TUPLE:
599 down(&s->parent->skt_sem);
600 pcmcia_validate_mem(s->parent);
601 up(&s->parent->skt_sem);
602 ret = pccard_get_first_tuple(s->parent, BIND_FN_ALL, &buf->tuple);
603 break;
604 case DS_GET_NEXT_TUPLE:
605 ret = pccard_get_next_tuple(s->parent, BIND_FN_ALL, &buf->tuple);
606 break;
607 case DS_GET_TUPLE_DATA:
608 buf->tuple.TupleData = buf->tuple_parse.data;
609 buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data);
610 ret = pccard_get_tuple_data(s->parent, &buf->tuple);
611 break;
612 case DS_PARSE_TUPLE:
613 buf->tuple.TupleData = buf->tuple_parse.data;
614 ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
615 break;
616 case DS_RESET_CARD:
617 ret = pccard_reset_card(s->parent);
618 break;
619 case DS_GET_STATUS:
620 if (buf->status.Function &&
621 (buf->status.Function >= s->parent->functions))
622 ret = CS_BAD_ARGS;
623 else
624 ret = pccard_get_status(s->parent, buf->status.Function, &buf->status);
625 break;
626 case DS_VALIDATE_CIS:
627 down(&s->parent->skt_sem);
628 pcmcia_validate_mem(s->parent);
629 up(&s->parent->skt_sem);
630 ret = pccard_validate_cis(s->parent, BIND_FN_ALL, &buf->cisinfo);
631 break;
632 case DS_SUSPEND_CARD:
633 ret = pcmcia_suspend_card(s->parent);
634 break;
635 case DS_RESUME_CARD:
636 ret = pcmcia_resume_card(s->parent);
637 break;
638 case DS_EJECT_CARD:
639 err = pcmcia_eject_card(s->parent);
640 break;
641 case DS_INSERT_CARD:
642 err = pcmcia_insert_card(s->parent);
643 break;
644 case DS_ACCESS_CONFIGURATION_REGISTER:
645 if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
646 err = -EPERM;
647 goto free_out;
648 }
649 if (buf->conf_reg.Function &&
650 (buf->conf_reg.Function >= s->parent->functions))
651 ret = CS_BAD_ARGS;
652 else
653 ret = pccard_access_configuration_register(s->parent,
654 buf->conf_reg.Function, &buf->conf_reg);
655 break;
656 case DS_GET_FIRST_REGION:
657 case DS_GET_NEXT_REGION:
658 case DS_BIND_MTD:
659 if (!capable(CAP_SYS_ADMIN)) {
660 err = -EPERM;
661 goto free_out;
662 } else {
663 static int printed = 0;
664 if (!printed) {
665 printk(KERN_WARNING "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n");
666 printk(KERN_WARNING "MTD handling any more.\n");
667 printed++;
668 }
669 }
670 err = -EINVAL;
671 goto free_out;
672 break;
673 case DS_GET_FIRST_WINDOW:
674 ret = pcmcia_get_window(s->parent, &buf->win_info.handle, 0,
675 &buf->win_info.window);
676 break;
677 case DS_GET_NEXT_WINDOW:
678 ret = pcmcia_get_window(s->parent, &buf->win_info.handle,
679 buf->win_info.handle->index + 1, &buf->win_info.window);
680 break;
681 case DS_GET_MEM_PAGE:
682 ret = pcmcia_get_mem_page(buf->win_info.handle,
683 &buf->win_info.map);
684 break;
685 case DS_REPLACE_CIS:
686 ret = pcmcia_replace_cis(s->parent, &buf->cisdump);
687 break;
688 case DS_BIND_REQUEST:
689 if (!capable(CAP_SYS_ADMIN)) {
690 err = -EPERM;
691 goto free_out;
692 }
693 err = bind_request(s, &buf->bind_info);
694 break;
695 case DS_GET_DEVICE_INFO:
696 err = get_device_info(s, &buf->bind_info, 1);
697 break;
698 case DS_GET_NEXT_DEVICE:
699 err = get_device_info(s, &buf->bind_info, 0);
700 break;
701 case DS_UNBIND_REQUEST:
702 err = 0;
703 break;
704 default:
705 err = -EINVAL;
706 }
707
708 if ((err == 0) && (ret != CS_SUCCESS)) {
709 ds_dbg(2, "ds_ioctl: ret = %d\n", ret);
710 switch (ret) {
711 case CS_BAD_SOCKET: case CS_NO_CARD:
712 err = -ENODEV; break;
713 case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ:
714 case CS_BAD_TUPLE:
715 err = -EINVAL; break;
716 case CS_IN_USE:
717 err = -EBUSY; break;
718 case CS_OUT_OF_RESOURCE:
719 err = -ENOSPC; break;
720 case CS_NO_MORE_ITEMS:
721 err = -ENODATA; break;
722 case CS_UNSUPPORTED_FUNCTION:
723 err = -ENOSYS; break;
724 default:
725 err = -EIO; break;
726 }
727 }
728
729 if (cmd & IOC_OUT) {
730 if (__copy_to_user(uarg, (char *)buf, size))
731 err = -EFAULT;
732 }
733
734free_out:
735 kfree(buf);
736 return err;
737} /* ds_ioctl */
738
739/*====================================================================*/
740
741static struct file_operations ds_fops = {
742 .owner = THIS_MODULE,
743 .open = ds_open,
744 .release = ds_release,
745 .ioctl = ds_ioctl,
746 .read = ds_read,
747 .write = ds_write,
748 .poll = ds_poll,
749};
750
751void __init pcmcia_setup_ioctl(void) {
752 int i;
753
754 /* Set up character device for user mode clients */
755 i = register_chrdev(0, "pcmcia", &ds_fops);
756 if (i == -EBUSY)
757 printk(KERN_NOTICE "unable to find a free device # for "
758 "Driver Services\n");
759 else
760 major_dev = i;
761
762#ifdef CONFIG_PROC_FS
763 proc_pccard = proc_mkdir("pccard", proc_bus);
764 if (proc_pccard)
765 create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
766#endif
767}
768
769
770void __exit pcmcia_cleanup_ioctl(void) {
771#ifdef CONFIG_PROC_FS
772 if (proc_pccard) {
773 remove_proc_entry("drivers", proc_pccard);
774 remove_proc_entry("pccard", proc_bus);
775 }
776#endif
777 if (major_dev != -1)
778 unregister_chrdev(major_dev, "pcmcia");
779}