blob: 138396ef5be9c38886a6c9a856dff081b681a636 [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
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070021#include <linux/kernel.h>
Dominik Brodowski3b659fb2005-06-27 16:28:51 -070022#include <linux/module.h>
23#include <linux/init.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070024#include <linux/major.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070025#include <linux/errno.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070026#include <linux/ioctl.h>
27#include <linux/proc_fs.h>
28#include <linux/poll.h>
29#include <linux/pci.h>
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -060030#include <linux/smp_lock.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;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070063
64#define ds_dbg(lvl, fmt, arg...) do { \
65 if (ds_pc_debug >= lvl) \
66 printk(KERN_DEBUG "ds: " fmt , ## arg); \
67} while (0)
68#else
69#define ds_dbg(lvl, fmt, arg...) do { } while (0)
70#endif
71
Dominik Brodowski855cdf12006-01-10 20:48:59 +010072static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s,
73 unsigned int function)
74{
75 struct pcmcia_device *p_dev = NULL;
76 unsigned long flags;
77
78 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
79 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
80 if (p_dev->func == function) {
81 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
82 return pcmcia_get_dev(p_dev);
83 }
84 }
85 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
86 return NULL;
87}
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070088
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070089/* backwards-compatible accessing of driver --- by name! */
90
Dominik Brodowski855cdf12006-01-10 20:48:59 +010091static struct pcmcia_driver *get_pcmcia_driver(dev_info_t *dev_info)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070092{
93 struct device_driver *drv;
94 struct pcmcia_driver *p_drv;
95
96 drv = driver_find((char *) dev_info, &pcmcia_bus_type);
97 if (!drv)
98 return NULL;
99
100 p_drv = container_of(drv, struct pcmcia_driver, drv);
101
102 return (p_drv);
103}
104
105
106#ifdef CONFIG_PROC_FS
107static struct proc_dir_entry *proc_pccard = NULL;
108
109static int proc_read_drivers_callback(struct device_driver *driver, void *d)
110{
111 char **p = d;
112 struct pcmcia_driver *p_drv = container_of(driver,
113 struct pcmcia_driver, drv);
114
115 *p += sprintf(*p, "%-24.24s 1 %d\n", p_drv->drv.name,
116#ifdef CONFIG_MODULE_UNLOAD
117 (p_drv->owner) ? module_refcount(p_drv->owner) : 1
118#else
119 1
120#endif
121 );
122 d = (void *) p;
123
124 return 0;
125}
126
127static int proc_read_drivers(char *buf, char **start, off_t pos,
128 int count, int *eof, void *data)
129{
130 char *p = buf;
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700131 int rc;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700132
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700133 rc = bus_for_each_drv(&pcmcia_bus_type, NULL,
134 (void *) &p, proc_read_drivers_callback);
135 if (rc < 0)
136 return rc;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700137
138 return (p - buf);
139}
140#endif
141
142/*======================================================================
143
144 These manage a ring buffer of events pending for one user process
145
146======================================================================*/
147
148
149static int queue_empty(user_info_t *user)
150{
151 return (user->event_head == user->event_tail);
152}
153
154static event_t get_queued_event(user_info_t *user)
155{
156 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
157 return user->event[user->event_tail];
158}
159
160static void queue_event(user_info_t *user, event_t event)
161{
162 user->event_head = (user->event_head+1) % MAX_EVENTS;
163 if (user->event_head == user->event_tail)
164 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
165 user->event[user->event_head] = event;
166}
167
Dominik Brodowskidc109492005-06-27 16:28:50 -0700168void handle_event(struct pcmcia_socket *s, event_t event)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700169{
170 user_info_t *user;
171 for (user = s->user; user; user = user->next)
172 queue_event(user, event);
173 wake_up_interruptible(&s->queue);
174}
175
176
177/*======================================================================
178
179 bind_request() and bind_device() are merged by now. Register_client()
180 is called right at the end of bind_request(), during the driver's
181 ->attach() call. Individual descriptions:
182
183 bind_request() connects a socket to a particular client driver.
184 It looks up the specified device ID in the list of registered
185 drivers, binds it to the socket, and tries to create an instance
186 of the device. unbind_request() deletes a driver instance.
187
188 Bind_device() associates a device driver with a particular socket.
189 It is normally called by Driver Services after it has identified
190 a newly inserted card. An instance of that driver will then be
191 eligible to register as a client of this socket.
192
193 Register_client() uses the dev_info_t handle to match the
194 caller with a socket. The driver must have already been bound
195 to a socket with bind_device() -- in fact, bind_device()
196 allocates the client structure that will be used.
197
198======================================================================*/
199
Dominik Brodowskidc109492005-06-27 16:28:50 -0700200static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700201{
202 struct pcmcia_driver *p_drv;
203 struct pcmcia_device *p_dev;
204 int ret = 0;
205 unsigned long flags;
206
Dominik Brodowskidc109492005-06-27 16:28:50 -0700207 s = pcmcia_get_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700208 if (!s)
209 return -EINVAL;
210
Dominik Brodowskidc109492005-06-27 16:28:50 -0700211 ds_dbg(2, "bind_request(%d, '%s')\n", s->sock,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700212 (char *)bind_info->dev_info);
213
214 p_drv = get_pcmcia_driver(&bind_info->dev_info);
215 if (!p_drv) {
216 ret = -EINVAL;
217 goto err_put;
218 }
219
220 if (!try_module_get(p_drv->owner)) {
221 ret = -EINVAL;
222 goto err_put_driver;
223 }
224
225 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
226 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
227 if (p_dev->func == bind_info->function) {
228 if ((p_dev->dev.driver == &p_drv->drv)) {
229 if (p_dev->cardmgr) {
230 /* if there's already a device
231 * registered, and it was registered
232 * by userspace before, we need to
233 * return the "instance". */
234 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
Dominik Brodowskifd238232006-03-05 10:45:09 +0100235 bind_info->instance = p_dev;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700236 ret = -EBUSY;
237 goto err_put_module;
238 } else {
239 /* the correct driver managed to bind
240 * itself magically to the correct
241 * device. */
242 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
243 p_dev->cardmgr = p_drv;
244 ret = 0;
245 goto err_put_module;
246 }
247 } else if (!p_dev->dev.driver) {
248 /* there's already a device available where
249 * no device has been bound to yet. So we don't
250 * need to register a device! */
251 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
252 goto rescan;
253 }
254 }
255 }
256 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
257
258 p_dev = pcmcia_device_add(s, bind_info->function);
259 if (!p_dev) {
260 ret = -EIO;
261 goto err_put_module;
262 }
263
264rescan:
265 p_dev->cardmgr = p_drv;
266
267 /* if a driver is already running, we can abort */
268 if (p_dev->dev.driver)
269 goto err_put_module;
270
271 /*
272 * Prevent this racing with a card insertion.
273 */
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100274 mutex_lock(&s->skt_mutex);
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700275 ret = bus_rescan_devices(&pcmcia_bus_type);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100276 mutex_unlock(&s->skt_mutex);
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700277 if (ret)
278 goto err_put_module;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700279
280 /* check whether the driver indeed matched. I don't care if this
281 * is racy or not, because it can only happen on cardmgr access
282 * paths...
283 */
284 if (!(p_dev->dev.driver == &p_drv->drv))
285 p_dev->cardmgr = NULL;
286
287 err_put_module:
288 module_put(p_drv->owner);
289 err_put_driver:
290 put_driver(&p_drv->drv);
291 err_put:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700292 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700293
294 return (ret);
295} /* bind_request */
296
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700297#ifdef CONFIG_CARDBUS
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700298
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700299static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s)
300{
301 if (!s || !(s->state & SOCKET_CARDBUS))
302 return NULL;
303
304 return s->cb_dev->subordinate;
305}
306#endif
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700307
Dominik Brodowskidc109492005-06-27 16:28:50 -0700308static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700309{
310 dev_node_t *node;
311 struct pcmcia_device *p_dev;
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100312 struct pcmcia_driver *p_drv;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700313 unsigned long flags;
314 int ret = 0;
315
316#ifdef CONFIG_CARDBUS
317 /*
318 * Some unbelievably ugly code to associate the PCI cardbus
319 * device and its driver with the PCMCIA "bind" information.
320 */
321 {
322 struct pci_bus *bus;
323
Dominik Brodowskidc109492005-06-27 16:28:50 -0700324 bus = pcmcia_lookup_bus(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700325 if (bus) {
326 struct list_head *list;
327 struct pci_dev *dev = NULL;
328
329 list = bus->devices.next;
330 while (list != &bus->devices) {
331 struct pci_dev *pdev = pci_dev_b(list);
332 list = list->next;
333
334 if (first) {
335 dev = pdev;
336 break;
337 }
338
339 /* Try to handle "next" here some way? */
340 }
341 if (dev && dev->driver) {
342 strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN);
343 bind_info->major = 0;
344 bind_info->minor = 0;
345 bind_info->next = NULL;
346 return 0;
347 }
348 }
349 }
350#endif
351
352 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
353 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
354 if (p_dev->func == bind_info->function) {
355 p_dev = pcmcia_get_dev(p_dev);
356 if (!p_dev)
357 continue;
358 goto found;
359 }
360 }
361 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
362 return -ENODEV;
363
364 found:
365 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
366
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100367 p_drv = to_pcmcia_drv(p_dev->dev.driver);
368 if (p_drv && !p_dev->_locked) {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700369 ret = -EAGAIN;
370 goto err_put;
371 }
372
373 if (first)
Dominik Brodowskifd238232006-03-05 10:45:09 +0100374 node = p_dev->dev_node;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700375 else
Dominik Brodowskifd238232006-03-05 10:45:09 +0100376 for (node = p_dev->dev_node; node; node = node->next)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700377 if (node == bind_info->next)
378 break;
379 if (!node) {
380 ret = -ENODEV;
381 goto err_put;
382 }
383
384 strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN);
385 bind_info->major = node->major;
386 bind_info->minor = node->minor;
387 bind_info->next = node->next;
388
389 err_put:
390 pcmcia_put_dev(p_dev);
391 return (ret);
392} /* get_device_info */
393
394
395static int ds_open(struct inode *inode, struct file *file)
396{
397 socket_t i = iminor(inode);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700398 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700399 user_info_t *user;
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700400 static int warning_printed = 0;
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600401 int ret = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700402
403 ds_dbg(0, "ds_open(socket %d)\n", i);
404
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600405 lock_kernel();
Dominik Brodowskidc109492005-06-27 16:28:50 -0700406 s = pcmcia_get_socket_by_nr(i);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600407 if (!s) {
408 ret = -ENODEV;
409 goto out;
410 }
Dominik Brodowskidc109492005-06-27 16:28:50 -0700411 s = pcmcia_get_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600412 if (!s) {
413 ret = -ENODEV;
414 goto out;
415 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700416
417 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700418 if (s->pcmcia_state.busy) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700419 pcmcia_put_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600420 ret = -EBUSY;
421 goto out;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700422 }
423 else
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700424 s->pcmcia_state.busy = 1;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700425 }
426
427 user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
428 if (!user) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700429 pcmcia_put_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600430 ret = -ENOMEM;
431 goto out;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700432 }
433 user->event_tail = user->event_head = 0;
434 user->next = s->user;
435 user->user_magic = USER_MAGIC;
436 user->socket = s;
437 s->user = user;
438 file->private_data = user;
439
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700440 if (!warning_printed) {
441 printk(KERN_INFO "pcmcia: Detected deprecated PCMCIA ioctl "
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700442 "usage from process: %s.\n", current->comm);
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700443 printk(KERN_INFO "pcmcia: This interface will soon be removed from "
444 "the kernel; please expect breakage unless you upgrade "
445 "to new tools.\n");
446 printk(KERN_INFO "pcmcia: see http://www.kernel.org/pub/linux/"
447 "utils/kernel/pcmcia/pcmcia.html for details.\n");
448 warning_printed = 1;
449 }
450
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700451 if (s->pcmcia_state.present)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700452 queue_event(user, CS_EVENT_CARD_INSERTION);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600453out:
454 unlock_kernel();
455 return ret;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700456} /* ds_open */
457
458/*====================================================================*/
459
460static int ds_release(struct inode *inode, struct file *file)
461{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700462 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700463 user_info_t *user, **link;
464
465 ds_dbg(0, "ds_release(socket %d)\n", iminor(inode));
466
467 user = file->private_data;
468 if (CHECK_USER(user))
469 goto out;
470
471 s = user->socket;
472
473 /* Unlink user data structure */
474 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700475 s->pcmcia_state.busy = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700476 }
477 file->private_data = NULL;
478 for (link = &s->user; *link; link = &(*link)->next)
479 if (*link == user) break;
480 if (link == NULL)
481 goto out;
482 *link = user->next;
483 user->user_magic = 0;
484 kfree(user);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700485 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700486out:
487 return 0;
488} /* ds_release */
489
490/*====================================================================*/
491
492static ssize_t ds_read(struct file *file, char __user *buf,
493 size_t count, loff_t *ppos)
494{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700495 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700496 user_info_t *user;
497 int ret;
498
Josef Sipek40fad042006-12-08 02:37:29 -0800499 ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700500
501 if (count < 4)
502 return -EINVAL;
503
504 user = file->private_data;
505 if (CHECK_USER(user))
506 return -EIO;
507
508 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700509 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700510 return -EIO;
511
512 ret = wait_event_interruptible(s->queue, !queue_empty(user));
513 if (ret == 0)
514 ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;
515
516 return ret;
517} /* ds_read */
518
519/*====================================================================*/
520
521static ssize_t ds_write(struct file *file, const char __user *buf,
522 size_t count, loff_t *ppos)
523{
Josef Sipek40fad042006-12-08 02:37:29 -0800524 ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700525
526 if (count != 4)
527 return -EINVAL;
528 if ((file->f_flags & O_ACCMODE) == O_RDONLY)
529 return -EBADF;
530
531 return -EIO;
532} /* ds_write */
533
534/*====================================================================*/
535
536/* No kernel lock - fine */
537static u_int ds_poll(struct file *file, poll_table *wait)
538{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700539 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700540 user_info_t *user;
541
Josef Sipek40fad042006-12-08 02:37:29 -0800542 ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700543
544 user = file->private_data;
545 if (CHECK_USER(user))
546 return POLLERR;
547 s = user->socket;
548 /*
549 * We don't check for a dead socket here since that
550 * will send cardmgr into an endless spin.
551 */
552 poll_wait(file, &s->queue, wait);
553 if (!queue_empty(user))
554 return POLLIN | POLLRDNORM;
555 return 0;
556} /* ds_poll */
557
558/*====================================================================*/
559
560extern int pcmcia_adjust_resource_info(adjust_t *adj);
561
562static int ds_ioctl(struct inode * inode, struct file * file,
563 u_int cmd, u_long arg)
564{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700565 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700566 void __user *uarg = (char __user *)arg;
567 u_int size;
568 int ret, err;
569 ds_ioctl_arg_t *buf;
570 user_info_t *user;
571
572 ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
573
574 user = file->private_data;
575 if (CHECK_USER(user))
576 return -EIO;
577
578 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700579 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700580 return -EIO;
581
582 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
583 if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
584
585 /* Permission check */
586 if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
587 return -EPERM;
588
589 if (cmd & IOC_IN) {
590 if (!access_ok(VERIFY_READ, uarg, size)) {
591 ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT);
592 return -EFAULT;
593 }
594 }
595 if (cmd & IOC_OUT) {
596 if (!access_ok(VERIFY_WRITE, uarg, size)) {
597 ds_dbg(3, "ds_ioctl(): verify_write = %d\n", -EFAULT);
598 return -EFAULT;
599 }
600 }
601 buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL);
602 if (!buf)
603 return -ENOMEM;
604
605 err = ret = 0;
606
Dominik Brodowski93740742006-11-19 11:21:27 -0500607 if (cmd & IOC_IN) {
608 if (__copy_from_user((char *)buf, uarg, size)) {
609 err = -EFAULT;
610 goto free_out;
611 }
612 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700613
614 switch (cmd) {
615 case DS_ADJUST_RESOURCE_INFO:
616 ret = pcmcia_adjust_resource_info(&buf->adjust);
617 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700618 case DS_GET_CONFIGURATION_INFO:
619 if (buf->config.Function &&
Dominik Brodowskidc109492005-06-27 16:28:50 -0700620 (buf->config.Function >= s->functions))
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700621 ret = CS_BAD_ARGS;
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100622 else {
623 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->config.Function);
Daniel Ritzf47ad212006-07-30 03:03:47 -0700624 ret = pccard_get_configuration_info(s, p_dev, &buf->config);
625 pcmcia_put_dev(p_dev);
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100626 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700627 break;
628 case DS_GET_FIRST_TUPLE:
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100629 mutex_lock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700630 pcmcia_validate_mem(s);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100631 mutex_unlock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700632 ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700633 break;
634 case DS_GET_NEXT_TUPLE:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700635 ret = pccard_get_next_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700636 break;
637 case DS_GET_TUPLE_DATA:
638 buf->tuple.TupleData = buf->tuple_parse.data;
639 buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700640 ret = pccard_get_tuple_data(s, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700641 break;
642 case DS_PARSE_TUPLE:
643 buf->tuple.TupleData = buf->tuple_parse.data;
644 ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
645 break;
646 case DS_RESET_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700647 ret = pccard_reset_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700648 break;
649 case DS_GET_STATUS:
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100650 if (buf->status.Function &&
651 (buf->status.Function >= s->functions))
652 ret = CS_BAD_ARGS;
653 else {
654 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->status.Function);
Daniel Ritzf47ad212006-07-30 03:03:47 -0700655 ret = pccard_get_status(s, p_dev, &buf->status);
656 pcmcia_put_dev(p_dev);
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100657 }
658 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700659 case DS_VALIDATE_CIS:
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100660 mutex_lock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700661 pcmcia_validate_mem(s);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100662 mutex_unlock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700663 ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700664 break;
665 case DS_SUSPEND_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700666 ret = pcmcia_suspend_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700667 break;
668 case DS_RESUME_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700669 ret = pcmcia_resume_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700670 break;
671 case DS_EJECT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700672 err = pcmcia_eject_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700673 break;
674 case DS_INSERT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700675 err = pcmcia_insert_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700676 break;
677 case DS_ACCESS_CONFIGURATION_REGISTER:
678 if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
679 err = -EPERM;
680 goto free_out;
681 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100682
683 ret = CS_BAD_ARGS;
684
685 if (!(buf->conf_reg.Function &&
686 (buf->conf_reg.Function >= s->functions))) {
687 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->conf_reg.Function);
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700688 if (p_dev) {
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100689 ret = pcmcia_access_configuration_register(p_dev, &buf->conf_reg);
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700690 pcmcia_put_dev(p_dev);
691 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100692 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700693 break;
694 case DS_GET_FIRST_REGION:
695 case DS_GET_NEXT_REGION:
696 case DS_BIND_MTD:
697 if (!capable(CAP_SYS_ADMIN)) {
698 err = -EPERM;
699 goto free_out;
700 } else {
701 static int printed = 0;
702 if (!printed) {
703 printk(KERN_WARNING "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n");
704 printk(KERN_WARNING "MTD handling any more.\n");
705 printed++;
706 }
707 }
708 err = -EINVAL;
709 goto free_out;
710 break;
711 case DS_GET_FIRST_WINDOW:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700712 ret = pcmcia_get_window(s, &buf->win_info.handle, 0,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700713 &buf->win_info.window);
714 break;
715 case DS_GET_NEXT_WINDOW:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700716 ret = pcmcia_get_window(s, &buf->win_info.handle,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700717 buf->win_info.handle->index + 1, &buf->win_info.window);
718 break;
719 case DS_GET_MEM_PAGE:
720 ret = pcmcia_get_mem_page(buf->win_info.handle,
721 &buf->win_info.map);
722 break;
723 case DS_REPLACE_CIS:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700724 ret = pcmcia_replace_cis(s, &buf->cisdump);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700725 break;
726 case DS_BIND_REQUEST:
727 if (!capable(CAP_SYS_ADMIN)) {
728 err = -EPERM;
729 goto free_out;
730 }
731 err = bind_request(s, &buf->bind_info);
732 break;
733 case DS_GET_DEVICE_INFO:
734 err = get_device_info(s, &buf->bind_info, 1);
735 break;
736 case DS_GET_NEXT_DEVICE:
737 err = get_device_info(s, &buf->bind_info, 0);
738 break;
739 case DS_UNBIND_REQUEST:
740 err = 0;
741 break;
742 default:
743 err = -EINVAL;
744 }
745
746 if ((err == 0) && (ret != CS_SUCCESS)) {
747 ds_dbg(2, "ds_ioctl: ret = %d\n", ret);
748 switch (ret) {
749 case CS_BAD_SOCKET: case CS_NO_CARD:
750 err = -ENODEV; break;
751 case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ:
752 case CS_BAD_TUPLE:
753 err = -EINVAL; break;
754 case CS_IN_USE:
755 err = -EBUSY; break;
756 case CS_OUT_OF_RESOURCE:
757 err = -ENOSPC; break;
758 case CS_NO_MORE_ITEMS:
759 err = -ENODATA; break;
760 case CS_UNSUPPORTED_FUNCTION:
761 err = -ENOSYS; break;
762 default:
763 err = -EIO; break;
764 }
765 }
766
767 if (cmd & IOC_OUT) {
768 if (__copy_to_user(uarg, (char *)buf, size))
769 err = -EFAULT;
770 }
771
772free_out:
773 kfree(buf);
774 return err;
775} /* ds_ioctl */
776
777/*====================================================================*/
778
Arjan van de Vend54b1fd2007-02-12 00:55:34 -0800779static const struct file_operations ds_fops = {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700780 .owner = THIS_MODULE,
781 .open = ds_open,
782 .release = ds_release,
783 .ioctl = ds_ioctl,
784 .read = ds_read,
785 .write = ds_write,
786 .poll = ds_poll,
787};
788
789void __init pcmcia_setup_ioctl(void) {
790 int i;
791
792 /* Set up character device for user mode clients */
793 i = register_chrdev(0, "pcmcia", &ds_fops);
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -0700794 if (i < 0)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700795 printk(KERN_NOTICE "unable to find a free device # for "
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -0700796 "Driver Services (error=%d)\n", i);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700797 else
798 major_dev = i;
799
800#ifdef CONFIG_PROC_FS
akpm@linux-foundation.org97094dc2008-04-29 10:47:54 -0700801 proc_pccard = proc_mkdir("bus/pccard", NULL);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700802 if (proc_pccard)
803 create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
804#endif
805}
806
807
808void __exit pcmcia_cleanup_ioctl(void) {
809#ifdef CONFIG_PROC_FS
810 if (proc_pccard) {
811 remove_proc_entry("drivers", proc_pccard);
akpm@linux-foundation.org97094dc2008-04-29 10:47:54 -0700812 remove_proc_entry("bus/pccard", NULL);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700813 }
814#endif
815 if (major_dev != -1)
816 unregister_chrdev(major_dev, "pcmcia");
817}