blob: 56b625d171ae4ea8a959cd92d76cb13e602e3133 [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>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070022#include <linux/kernel.h>
Dominik Brodowski3b659fb2005-06-27 16:28:51 -070023#include <linux/module.h>
24#include <linux/init.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070025#include <linux/major.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070026#include <linux/errno.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070027#include <linux/ioctl.h>
28#include <linux/proc_fs.h>
29#include <linux/poll.h>
30#include <linux/pci.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070031#include <linux/workqueue.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070032
33#define IN_CARD_SERVICES
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070034#include <pcmcia/cs_types.h>
35#include <pcmcia/cs.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070036#include <pcmcia/cistpl.h>
37#include <pcmcia/ds.h>
38#include <pcmcia/ss.h>
39
40#include "cs_internal.h"
41#include "ds_internal.h"
42
43static int major_dev = -1;
44
45
46/* Device user information */
47#define MAX_EVENTS 32
48#define USER_MAGIC 0x7ea4
49#define CHECK_USER(u) \
50 (((u) == NULL) || ((u)->user_magic != USER_MAGIC))
51
52typedef struct user_info_t {
53 u_int user_magic;
54 int event_head, event_tail;
55 event_t event[MAX_EVENTS];
56 struct user_info_t *next;
Dominik Brodowskidc109492005-06-27 16:28:50 -070057 struct pcmcia_socket *socket;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070058} user_info_t;
59
60
61#ifdef DEBUG
62extern int ds_pc_debug;
63#define cs_socket_name(skt) ((skt)->dev.class_id)
64
65#define ds_dbg(lvl, fmt, arg...) do { \
66 if (ds_pc_debug >= lvl) \
67 printk(KERN_DEBUG "ds: " fmt , ## arg); \
68} while (0)
69#else
70#define ds_dbg(lvl, fmt, arg...) do { } while (0)
71#endif
72
Dominik Brodowski855cdf12006-01-10 20:48:59 +010073static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s,
74 unsigned int function)
75{
76 struct pcmcia_device *p_dev = NULL;
77 unsigned long flags;
78
79 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
80 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
81 if (p_dev->func == function) {
82 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
83 return pcmcia_get_dev(p_dev);
84 }
85 }
86 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
87 return NULL;
88}
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070089
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070090/* backwards-compatible accessing of driver --- by name! */
91
Dominik Brodowski855cdf12006-01-10 20:48:59 +010092static struct pcmcia_driver *get_pcmcia_driver(dev_info_t *dev_info)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070093{
94 struct device_driver *drv;
95 struct pcmcia_driver *p_drv;
96
97 drv = driver_find((char *) dev_info, &pcmcia_bus_type);
98 if (!drv)
99 return NULL;
100
101 p_drv = container_of(drv, struct pcmcia_driver, drv);
102
103 return (p_drv);
104}
105
106
107#ifdef CONFIG_PROC_FS
108static struct proc_dir_entry *proc_pccard = NULL;
109
110static int proc_read_drivers_callback(struct device_driver *driver, void *d)
111{
112 char **p = d;
113 struct pcmcia_driver *p_drv = container_of(driver,
114 struct pcmcia_driver, drv);
115
116 *p += sprintf(*p, "%-24.24s 1 %d\n", p_drv->drv.name,
117#ifdef CONFIG_MODULE_UNLOAD
118 (p_drv->owner) ? module_refcount(p_drv->owner) : 1
119#else
120 1
121#endif
122 );
123 d = (void *) p;
124
125 return 0;
126}
127
128static int proc_read_drivers(char *buf, char **start, off_t pos,
129 int count, int *eof, void *data)
130{
131 char *p = buf;
132
133 bus_for_each_drv(&pcmcia_bus_type, NULL,
134 (void *) &p, proc_read_drivers_callback);
135
136 return (p - buf);
137}
138#endif
139
140/*======================================================================
141
142 These manage a ring buffer of events pending for one user process
143
144======================================================================*/
145
146
147static int queue_empty(user_info_t *user)
148{
149 return (user->event_head == user->event_tail);
150}
151
152static event_t get_queued_event(user_info_t *user)
153{
154 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
155 return user->event[user->event_tail];
156}
157
158static void queue_event(user_info_t *user, event_t event)
159{
160 user->event_head = (user->event_head+1) % MAX_EVENTS;
161 if (user->event_head == user->event_tail)
162 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
163 user->event[user->event_head] = event;
164}
165
Dominik Brodowskidc109492005-06-27 16:28:50 -0700166void handle_event(struct pcmcia_socket *s, event_t event)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700167{
168 user_info_t *user;
169 for (user = s->user; user; user = user->next)
170 queue_event(user, event);
171 wake_up_interruptible(&s->queue);
172}
173
174
175/*======================================================================
176
177 bind_request() and bind_device() are merged by now. Register_client()
178 is called right at the end of bind_request(), during the driver's
179 ->attach() call. Individual descriptions:
180
181 bind_request() connects a socket to a particular client driver.
182 It looks up the specified device ID in the list of registered
183 drivers, binds it to the socket, and tries to create an instance
184 of the device. unbind_request() deletes a driver instance.
185
186 Bind_device() associates a device driver with a particular socket.
187 It is normally called by Driver Services after it has identified
188 a newly inserted card. An instance of that driver will then be
189 eligible to register as a client of this socket.
190
191 Register_client() uses the dev_info_t handle to match the
192 caller with a socket. The driver must have already been bound
193 to a socket with bind_device() -- in fact, bind_device()
194 allocates the client structure that will be used.
195
196======================================================================*/
197
Dominik Brodowskidc109492005-06-27 16:28:50 -0700198static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700199{
200 struct pcmcia_driver *p_drv;
201 struct pcmcia_device *p_dev;
202 int ret = 0;
203 unsigned long flags;
204
Dominik Brodowskidc109492005-06-27 16:28:50 -0700205 s = pcmcia_get_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700206 if (!s)
207 return -EINVAL;
208
Dominik Brodowskidc109492005-06-27 16:28:50 -0700209 ds_dbg(2, "bind_request(%d, '%s')\n", s->sock,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700210 (char *)bind_info->dev_info);
211
212 p_drv = get_pcmcia_driver(&bind_info->dev_info);
213 if (!p_drv) {
214 ret = -EINVAL;
215 goto err_put;
216 }
217
218 if (!try_module_get(p_drv->owner)) {
219 ret = -EINVAL;
220 goto err_put_driver;
221 }
222
223 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
224 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
225 if (p_dev->func == bind_info->function) {
226 if ((p_dev->dev.driver == &p_drv->drv)) {
227 if (p_dev->cardmgr) {
228 /* if there's already a device
229 * registered, and it was registered
230 * by userspace before, we need to
231 * return the "instance". */
232 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
233 bind_info->instance = p_dev->instance;
234 ret = -EBUSY;
235 goto err_put_module;
236 } else {
237 /* the correct driver managed to bind
238 * itself magically to the correct
239 * device. */
240 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
241 p_dev->cardmgr = p_drv;
242 ret = 0;
243 goto err_put_module;
244 }
245 } else if (!p_dev->dev.driver) {
246 /* there's already a device available where
247 * no device has been bound to yet. So we don't
248 * need to register a device! */
249 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
250 goto rescan;
251 }
252 }
253 }
254 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
255
256 p_dev = pcmcia_device_add(s, bind_info->function);
257 if (!p_dev) {
258 ret = -EIO;
259 goto err_put_module;
260 }
261
262rescan:
263 p_dev->cardmgr = p_drv;
264
265 /* if a driver is already running, we can abort */
266 if (p_dev->dev.driver)
267 goto err_put_module;
268
269 /*
270 * Prevent this racing with a card insertion.
271 */
Dominik Brodowskidc109492005-06-27 16:28:50 -0700272 down(&s->skt_sem);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700273 bus_rescan_devices(&pcmcia_bus_type);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700274 up(&s->skt_sem);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700275
276 /* check whether the driver indeed matched. I don't care if this
277 * is racy or not, because it can only happen on cardmgr access
278 * paths...
279 */
280 if (!(p_dev->dev.driver == &p_drv->drv))
281 p_dev->cardmgr = NULL;
282
283 err_put_module:
284 module_put(p_drv->owner);
285 err_put_driver:
286 put_driver(&p_drv->drv);
287 err_put:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700288 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700289
290 return (ret);
291} /* bind_request */
292
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700293#ifdef CONFIG_CARDBUS
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700294
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700295static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s)
296{
297 if (!s || !(s->state & SOCKET_CARDBUS))
298 return NULL;
299
300 return s->cb_dev->subordinate;
301}
302#endif
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700303
Dominik Brodowskidc109492005-06-27 16:28:50 -0700304static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700305{
306 dev_node_t *node;
307 struct pcmcia_device *p_dev;
308 unsigned long flags;
309 int ret = 0;
310
311#ifdef CONFIG_CARDBUS
312 /*
313 * Some unbelievably ugly code to associate the PCI cardbus
314 * device and its driver with the PCMCIA "bind" information.
315 */
316 {
317 struct pci_bus *bus;
318
Dominik Brodowskidc109492005-06-27 16:28:50 -0700319 bus = pcmcia_lookup_bus(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700320 if (bus) {
321 struct list_head *list;
322 struct pci_dev *dev = NULL;
323
324 list = bus->devices.next;
325 while (list != &bus->devices) {
326 struct pci_dev *pdev = pci_dev_b(list);
327 list = list->next;
328
329 if (first) {
330 dev = pdev;
331 break;
332 }
333
334 /* Try to handle "next" here some way? */
335 }
336 if (dev && dev->driver) {
337 strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN);
338 bind_info->major = 0;
339 bind_info->minor = 0;
340 bind_info->next = NULL;
341 return 0;
342 }
343 }
344 }
345#endif
346
347 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
348 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
349 if (p_dev->func == bind_info->function) {
350 p_dev = pcmcia_get_dev(p_dev);
351 if (!p_dev)
352 continue;
353 goto found;
354 }
355 }
356 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
357 return -ENODEV;
358
359 found:
360 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
361
362 if ((!p_dev->instance) ||
363 (p_dev->instance->state & DEV_CONFIG_PENDING)) {
364 ret = -EAGAIN;
365 goto err_put;
366 }
367
368 if (first)
369 node = p_dev->instance->dev;
370 else
371 for (node = p_dev->instance->dev; node; node = node->next)
372 if (node == bind_info->next)
373 break;
374 if (!node) {
375 ret = -ENODEV;
376 goto err_put;
377 }
378
379 strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN);
380 bind_info->major = node->major;
381 bind_info->minor = node->minor;
382 bind_info->next = node->next;
383
384 err_put:
385 pcmcia_put_dev(p_dev);
386 return (ret);
387} /* get_device_info */
388
389
390static int ds_open(struct inode *inode, struct file *file)
391{
392 socket_t i = iminor(inode);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700393 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700394 user_info_t *user;
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700395 static int warning_printed = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700396
397 ds_dbg(0, "ds_open(socket %d)\n", i);
398
Dominik Brodowskidc109492005-06-27 16:28:50 -0700399 s = pcmcia_get_socket_by_nr(i);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700400 if (!s)
401 return -ENODEV;
Dominik Brodowskidc109492005-06-27 16:28:50 -0700402 s = pcmcia_get_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700403 if (!s)
404 return -ENODEV;
405
406 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700407 if (s->pcmcia_state.busy) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700408 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700409 return -EBUSY;
410 }
411 else
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700412 s->pcmcia_state.busy = 1;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700413 }
414
415 user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
416 if (!user) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700417 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700418 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
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700427 if (!warning_printed) {
428 printk(KERN_INFO "pcmcia: Detected deprecated PCMCIA ioctl "
429 "usage.\n");
430 printk(KERN_INFO "pcmcia: This interface will soon be removed from "
431 "the kernel; please expect breakage unless you upgrade "
432 "to new tools.\n");
433 printk(KERN_INFO "pcmcia: see http://www.kernel.org/pub/linux/"
434 "utils/kernel/pcmcia/pcmcia.html for details.\n");
435 warning_printed = 1;
436 }
437
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700438 if (s->pcmcia_state.present)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700439 queue_event(user, CS_EVENT_CARD_INSERTION);
440 return 0;
441} /* ds_open */
442
443/*====================================================================*/
444
445static int ds_release(struct inode *inode, struct file *file)
446{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700447 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700448 user_info_t *user, **link;
449
450 ds_dbg(0, "ds_release(socket %d)\n", iminor(inode));
451
452 user = file->private_data;
453 if (CHECK_USER(user))
454 goto out;
455
456 s = user->socket;
457
458 /* Unlink user data structure */
459 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700460 s->pcmcia_state.busy = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700461 }
462 file->private_data = NULL;
463 for (link = &s->user; *link; link = &(*link)->next)
464 if (*link == user) break;
465 if (link == NULL)
466 goto out;
467 *link = user->next;
468 user->user_magic = 0;
469 kfree(user);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700470 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700471out:
472 return 0;
473} /* ds_release */
474
475/*====================================================================*/
476
477static ssize_t ds_read(struct file *file, char __user *buf,
478 size_t count, loff_t *ppos)
479{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700480 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700481 user_info_t *user;
482 int ret;
483
484 ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_dentry->d_inode));
485
486 if (count < 4)
487 return -EINVAL;
488
489 user = file->private_data;
490 if (CHECK_USER(user))
491 return -EIO;
492
493 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700494 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700495 return -EIO;
496
497 ret = wait_event_interruptible(s->queue, !queue_empty(user));
498 if (ret == 0)
499 ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;
500
501 return ret;
502} /* ds_read */
503
504/*====================================================================*/
505
506static ssize_t ds_write(struct file *file, const char __user *buf,
507 size_t count, loff_t *ppos)
508{
509 ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_dentry->d_inode));
510
511 if (count != 4)
512 return -EINVAL;
513 if ((file->f_flags & O_ACCMODE) == O_RDONLY)
514 return -EBADF;
515
516 return -EIO;
517} /* ds_write */
518
519/*====================================================================*/
520
521/* No kernel lock - fine */
522static u_int ds_poll(struct file *file, poll_table *wait)
523{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700524 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700525 user_info_t *user;
526
527 ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_dentry->d_inode));
528
529 user = file->private_data;
530 if (CHECK_USER(user))
531 return POLLERR;
532 s = user->socket;
533 /*
534 * We don't check for a dead socket here since that
535 * will send cardmgr into an endless spin.
536 */
537 poll_wait(file, &s->queue, wait);
538 if (!queue_empty(user))
539 return POLLIN | POLLRDNORM;
540 return 0;
541} /* ds_poll */
542
543/*====================================================================*/
544
545extern int pcmcia_adjust_resource_info(adjust_t *adj);
546
547static int ds_ioctl(struct inode * inode, struct file * file,
548 u_int cmd, u_long arg)
549{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700550 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700551 void __user *uarg = (char __user *)arg;
552 u_int size;
553 int ret, err;
554 ds_ioctl_arg_t *buf;
555 user_info_t *user;
556
557 ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
558
559 user = file->private_data;
560 if (CHECK_USER(user))
561 return -EIO;
562
563 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700564 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700565 return -EIO;
566
567 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
568 if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
569
570 /* Permission check */
571 if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
572 return -EPERM;
573
574 if (cmd & IOC_IN) {
575 if (!access_ok(VERIFY_READ, uarg, size)) {
576 ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT);
577 return -EFAULT;
578 }
579 }
580 if (cmd & IOC_OUT) {
581 if (!access_ok(VERIFY_WRITE, uarg, size)) {
582 ds_dbg(3, "ds_ioctl(): verify_write = %d\n", -EFAULT);
583 return -EFAULT;
584 }
585 }
586 buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL);
587 if (!buf)
588 return -ENOMEM;
589
590 err = ret = 0;
591
592 if (cmd & IOC_IN) __copy_from_user((char *)buf, uarg, size);
593
594 switch (cmd) {
595 case DS_ADJUST_RESOURCE_INFO:
596 ret = pcmcia_adjust_resource_info(&buf->adjust);
597 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700598 case DS_GET_CONFIGURATION_INFO:
599 if (buf->config.Function &&
Dominik Brodowskidc109492005-06-27 16:28:50 -0700600 (buf->config.Function >= s->functions))
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700601 ret = CS_BAD_ARGS;
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100602 else {
603 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->config.Function);
604 ret = pccard_get_configuration_info(s, p_dev, &buf->config);
605 pcmcia_put_dev(p_dev);
606 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700607 break;
608 case DS_GET_FIRST_TUPLE:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700609 down(&s->skt_sem);
610 pcmcia_validate_mem(s);
611 up(&s->skt_sem);
612 ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700613 break;
614 case DS_GET_NEXT_TUPLE:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700615 ret = pccard_get_next_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700616 break;
617 case DS_GET_TUPLE_DATA:
618 buf->tuple.TupleData = buf->tuple_parse.data;
619 buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700620 ret = pccard_get_tuple_data(s, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700621 break;
622 case DS_PARSE_TUPLE:
623 buf->tuple.TupleData = buf->tuple_parse.data;
624 ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
625 break;
626 case DS_RESET_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700627 ret = pccard_reset_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700628 break;
629 case DS_GET_STATUS:
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100630 if (buf->status.Function &&
631 (buf->status.Function >= s->functions))
632 ret = CS_BAD_ARGS;
633 else {
634 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->status.Function);
635 ret = pccard_get_status(s, p_dev, &buf->status);
636 pcmcia_put_dev(p_dev);
637 }
638 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700639 case DS_VALIDATE_CIS:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700640 down(&s->skt_sem);
641 pcmcia_validate_mem(s);
642 up(&s->skt_sem);
643 ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700644 break;
645 case DS_SUSPEND_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700646 ret = pcmcia_suspend_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700647 break;
648 case DS_RESUME_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700649 ret = pcmcia_resume_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700650 break;
651 case DS_EJECT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700652 err = pcmcia_eject_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700653 break;
654 case DS_INSERT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700655 err = pcmcia_insert_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700656 break;
657 case DS_ACCESS_CONFIGURATION_REGISTER:
658 if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
659 err = -EPERM;
660 goto free_out;
661 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100662
663 ret = CS_BAD_ARGS;
664
665 if (!(buf->conf_reg.Function &&
666 (buf->conf_reg.Function >= s->functions))) {
667 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->conf_reg.Function);
668 if (p_dev)
669 ret = pcmcia_access_configuration_register(p_dev, &buf->conf_reg);
670 pcmcia_put_dev(p_dev);
671 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700672 break;
673 case DS_GET_FIRST_REGION:
674 case DS_GET_NEXT_REGION:
675 case DS_BIND_MTD:
676 if (!capable(CAP_SYS_ADMIN)) {
677 err = -EPERM;
678 goto free_out;
679 } else {
680 static int printed = 0;
681 if (!printed) {
682 printk(KERN_WARNING "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n");
683 printk(KERN_WARNING "MTD handling any more.\n");
684 printed++;
685 }
686 }
687 err = -EINVAL;
688 goto free_out;
689 break;
690 case DS_GET_FIRST_WINDOW:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700691 ret = pcmcia_get_window(s, &buf->win_info.handle, 0,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700692 &buf->win_info.window);
693 break;
694 case DS_GET_NEXT_WINDOW:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700695 ret = pcmcia_get_window(s, &buf->win_info.handle,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700696 buf->win_info.handle->index + 1, &buf->win_info.window);
697 break;
698 case DS_GET_MEM_PAGE:
699 ret = pcmcia_get_mem_page(buf->win_info.handle,
700 &buf->win_info.map);
701 break;
702 case DS_REPLACE_CIS:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700703 ret = pcmcia_replace_cis(s, &buf->cisdump);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700704 break;
705 case DS_BIND_REQUEST:
706 if (!capable(CAP_SYS_ADMIN)) {
707 err = -EPERM;
708 goto free_out;
709 }
710 err = bind_request(s, &buf->bind_info);
711 break;
712 case DS_GET_DEVICE_INFO:
713 err = get_device_info(s, &buf->bind_info, 1);
714 break;
715 case DS_GET_NEXT_DEVICE:
716 err = get_device_info(s, &buf->bind_info, 0);
717 break;
718 case DS_UNBIND_REQUEST:
719 err = 0;
720 break;
721 default:
722 err = -EINVAL;
723 }
724
725 if ((err == 0) && (ret != CS_SUCCESS)) {
726 ds_dbg(2, "ds_ioctl: ret = %d\n", ret);
727 switch (ret) {
728 case CS_BAD_SOCKET: case CS_NO_CARD:
729 err = -ENODEV; break;
730 case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ:
731 case CS_BAD_TUPLE:
732 err = -EINVAL; break;
733 case CS_IN_USE:
734 err = -EBUSY; break;
735 case CS_OUT_OF_RESOURCE:
736 err = -ENOSPC; break;
737 case CS_NO_MORE_ITEMS:
738 err = -ENODATA; break;
739 case CS_UNSUPPORTED_FUNCTION:
740 err = -ENOSYS; break;
741 default:
742 err = -EIO; break;
743 }
744 }
745
746 if (cmd & IOC_OUT) {
747 if (__copy_to_user(uarg, (char *)buf, size))
748 err = -EFAULT;
749 }
750
751free_out:
752 kfree(buf);
753 return err;
754} /* ds_ioctl */
755
756/*====================================================================*/
757
758static struct file_operations ds_fops = {
759 .owner = THIS_MODULE,
760 .open = ds_open,
761 .release = ds_release,
762 .ioctl = ds_ioctl,
763 .read = ds_read,
764 .write = ds_write,
765 .poll = ds_poll,
766};
767
768void __init pcmcia_setup_ioctl(void) {
769 int i;
770
771 /* Set up character device for user mode clients */
772 i = register_chrdev(0, "pcmcia", &ds_fops);
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -0700773 if (i < 0)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700774 printk(KERN_NOTICE "unable to find a free device # for "
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -0700775 "Driver Services (error=%d)\n", i);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700776 else
777 major_dev = i;
778
779#ifdef CONFIG_PROC_FS
780 proc_pccard = proc_mkdir("pccard", proc_bus);
781 if (proc_pccard)
782 create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
783#endif
784}
785
786
787void __exit pcmcia_cleanup_ioctl(void) {
788#ifdef CONFIG_PROC_FS
789 if (proc_pccard) {
790 remove_proc_entry("drivers", proc_pccard);
791 remove_proc_entry("pccard", proc_bus);
792 }
793#endif
794 if (major_dev != -1)
795 unregister_chrdev(major_dev, "pcmcia");
796}