blob: 419f97fc9a625befbd2805ec1b0693a4bb1af6b1 [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
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070033#include <pcmcia/cs_types.h>
34#include <pcmcia/cs.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070035#include <pcmcia/cistpl.h>
Dominik Brodowski4aeba012008-06-20 13:24:31 +020036#include <pcmcia/cisreg.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070037#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
Dominik Brodowskic5023802008-06-19 19:02:52 +0200142
143#ifdef CONFIG_PCMCIA_PROBE
144
145static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
146{
147 int irq;
148 u32 mask;
149
150 irq = adj->resource.irq.IRQ;
151 if ((irq < 0) || (irq > 15))
152 return CS_BAD_IRQ;
153
154 if (adj->Action != REMOVE_MANAGED_RESOURCE)
155 return 0;
156
157 mask = 1 << irq;
158
159 if (!(s->irq_mask & mask))
160 return 0;
161
162 s->irq_mask &= ~mask;
163
164 return 0;
165}
166
167#else
168
169static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) {
170 return CS_SUCCESS;
171}
172
173#endif
174
175static int pcmcia_adjust_resource_info(adjust_t *adj)
176{
177 struct pcmcia_socket *s;
178 int ret = CS_UNSUPPORTED_FUNCTION;
179 unsigned long flags;
180
181 down_read(&pcmcia_socket_list_rwsem);
182 list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
183
184 if (adj->Resource == RES_IRQ)
185 ret = adjust_irq(s, adj);
186
187 else if (s->resource_ops->add_io) {
188 unsigned long begin, end;
189
190 /* you can't use the old interface if the new
191 * one was used before */
192 spin_lock_irqsave(&s->lock, flags);
193 if ((s->resource_setup_new) &&
194 !(s->resource_setup_old)) {
195 spin_unlock_irqrestore(&s->lock, flags);
196 continue;
197 } else if (!(s->resource_setup_old))
198 s->resource_setup_old = 1;
199 spin_unlock_irqrestore(&s->lock, flags);
200
201 switch (adj->Resource) {
202 case RES_MEMORY_RANGE:
203 begin = adj->resource.memory.Base;
204 end = adj->resource.memory.Base + adj->resource.memory.Size - 1;
205 if (s->resource_ops->add_mem)
206 ret =s->resource_ops->add_mem(s, adj->Action, begin, end);
207 case RES_IO_RANGE:
208 begin = adj->resource.io.BasePort;
209 end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1;
210 if (s->resource_ops->add_io)
211 ret = s->resource_ops->add_io(s, adj->Action, begin, end);
212 }
213 if (!ret) {
214 /* as there's no way we know this is the
215 * last call to adjust_resource_info, we
216 * always need to assume this is the latest
217 * one... */
218 spin_lock_irqsave(&s->lock, flags);
219 s->resource_setup_done = 1;
220 spin_unlock_irqrestore(&s->lock, flags);
221 }
222 }
223 }
224 up_read(&pcmcia_socket_list_rwsem);
225
226 return (ret);
227}
228
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200229/** pccard_get_status
230 *
231 * Get the current socket state bits. We don't support the latched
232 * SocketState yet: I haven't seen any point for it.
233 */
234
235static int pccard_get_status(struct pcmcia_socket *s,
236 struct pcmcia_device *p_dev,
237 cs_status_t *status)
238{
239 config_t *c;
240 int val;
241
242 s->ops->get_status(s, &val);
243 status->CardState = status->SocketState = 0;
244 status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0;
245 status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0;
246 status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0;
247 status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0;
248 if (s->state & SOCKET_SUSPEND)
249 status->CardState |= CS_EVENT_PM_SUSPEND;
250 if (!(s->state & SOCKET_PRESENT))
251 return CS_NO_CARD;
252
253 c = (p_dev) ? p_dev->function_config : NULL;
254
255 if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
256 (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
257 u_char reg;
258 if (c->CardValues & PRESENT_PIN_REPLACE) {
259 pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
260 status->CardState |=
261 (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0;
262 status->CardState |=
263 (reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0;
264 status->CardState |=
265 (reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0;
266 status->CardState |=
267 (reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0;
268 } else {
269 /* No PRR? Then assume we're always ready */
270 status->CardState |= CS_EVENT_READY_CHANGE;
271 }
272 if (c->CardValues & PRESENT_EXT_STATUS) {
273 pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
274 status->CardState |=
275 (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
276 }
277 return CS_SUCCESS;
278 }
279 status->CardState |=
280 (val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0;
281 status->CardState |=
282 (val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0;
283 status->CardState |=
284 (val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0;
285 status->CardState |=
286 (val & SS_READY) ? CS_EVENT_READY_CHANGE : 0;
287 return CS_SUCCESS;
288} /* pccard_get_status */
Dominik Brodowskic5023802008-06-19 19:02:52 +0200289
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700290/*======================================================================
291
292 These manage a ring buffer of events pending for one user process
293
294======================================================================*/
295
296
297static int queue_empty(user_info_t *user)
298{
299 return (user->event_head == user->event_tail);
300}
301
302static event_t get_queued_event(user_info_t *user)
303{
304 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
305 return user->event[user->event_tail];
306}
307
308static void queue_event(user_info_t *user, event_t event)
309{
310 user->event_head = (user->event_head+1) % MAX_EVENTS;
311 if (user->event_head == user->event_tail)
312 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
313 user->event[user->event_head] = event;
314}
315
Dominik Brodowskidc109492005-06-27 16:28:50 -0700316void handle_event(struct pcmcia_socket *s, event_t event)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700317{
318 user_info_t *user;
319 for (user = s->user; user; user = user->next)
320 queue_event(user, event);
321 wake_up_interruptible(&s->queue);
322}
323
324
325/*======================================================================
326
327 bind_request() and bind_device() are merged by now. Register_client()
328 is called right at the end of bind_request(), during the driver's
329 ->attach() call. Individual descriptions:
330
331 bind_request() connects a socket to a particular client driver.
332 It looks up the specified device ID in the list of registered
333 drivers, binds it to the socket, and tries to create an instance
334 of the device. unbind_request() deletes a driver instance.
335
336 Bind_device() associates a device driver with a particular socket.
337 It is normally called by Driver Services after it has identified
338 a newly inserted card. An instance of that driver will then be
339 eligible to register as a client of this socket.
340
341 Register_client() uses the dev_info_t handle to match the
342 caller with a socket. The driver must have already been bound
343 to a socket with bind_device() -- in fact, bind_device()
344 allocates the client structure that will be used.
345
346======================================================================*/
347
Dominik Brodowskidc109492005-06-27 16:28:50 -0700348static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700349{
350 struct pcmcia_driver *p_drv;
351 struct pcmcia_device *p_dev;
352 int ret = 0;
353 unsigned long flags;
354
Dominik Brodowskidc109492005-06-27 16:28:50 -0700355 s = pcmcia_get_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700356 if (!s)
357 return -EINVAL;
358
Dominik Brodowskidc109492005-06-27 16:28:50 -0700359 ds_dbg(2, "bind_request(%d, '%s')\n", s->sock,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700360 (char *)bind_info->dev_info);
361
362 p_drv = get_pcmcia_driver(&bind_info->dev_info);
363 if (!p_drv) {
364 ret = -EINVAL;
365 goto err_put;
366 }
367
368 if (!try_module_get(p_drv->owner)) {
369 ret = -EINVAL;
370 goto err_put_driver;
371 }
372
373 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
374 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
375 if (p_dev->func == bind_info->function) {
376 if ((p_dev->dev.driver == &p_drv->drv)) {
377 if (p_dev->cardmgr) {
378 /* if there's already a device
379 * registered, and it was registered
380 * by userspace before, we need to
381 * return the "instance". */
382 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
Dominik Brodowskifd238232006-03-05 10:45:09 +0100383 bind_info->instance = p_dev;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700384 ret = -EBUSY;
385 goto err_put_module;
386 } else {
387 /* the correct driver managed to bind
388 * itself magically to the correct
389 * device. */
390 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
391 p_dev->cardmgr = p_drv;
392 ret = 0;
393 goto err_put_module;
394 }
395 } else if (!p_dev->dev.driver) {
396 /* there's already a device available where
397 * no device has been bound to yet. So we don't
398 * need to register a device! */
399 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
400 goto rescan;
401 }
402 }
403 }
404 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
405
406 p_dev = pcmcia_device_add(s, bind_info->function);
407 if (!p_dev) {
408 ret = -EIO;
409 goto err_put_module;
410 }
411
412rescan:
413 p_dev->cardmgr = p_drv;
414
415 /* if a driver is already running, we can abort */
416 if (p_dev->dev.driver)
417 goto err_put_module;
418
419 /*
420 * Prevent this racing with a card insertion.
421 */
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100422 mutex_lock(&s->skt_mutex);
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700423 ret = bus_rescan_devices(&pcmcia_bus_type);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100424 mutex_unlock(&s->skt_mutex);
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700425 if (ret)
426 goto err_put_module;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700427
428 /* check whether the driver indeed matched. I don't care if this
429 * is racy or not, because it can only happen on cardmgr access
430 * paths...
431 */
432 if (!(p_dev->dev.driver == &p_drv->drv))
433 p_dev->cardmgr = NULL;
434
435 err_put_module:
436 module_put(p_drv->owner);
437 err_put_driver:
438 put_driver(&p_drv->drv);
439 err_put:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700440 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700441
442 return (ret);
443} /* bind_request */
444
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700445#ifdef CONFIG_CARDBUS
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700446
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700447static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s)
448{
449 if (!s || !(s->state & SOCKET_CARDBUS))
450 return NULL;
451
452 return s->cb_dev->subordinate;
453}
454#endif
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700455
Dominik Brodowskidc109492005-06-27 16:28:50 -0700456static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700457{
458 dev_node_t *node;
459 struct pcmcia_device *p_dev;
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100460 struct pcmcia_driver *p_drv;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700461 unsigned long flags;
462 int ret = 0;
463
464#ifdef CONFIG_CARDBUS
465 /*
466 * Some unbelievably ugly code to associate the PCI cardbus
467 * device and its driver with the PCMCIA "bind" information.
468 */
469 {
470 struct pci_bus *bus;
471
Dominik Brodowskidc109492005-06-27 16:28:50 -0700472 bus = pcmcia_lookup_bus(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700473 if (bus) {
474 struct list_head *list;
475 struct pci_dev *dev = NULL;
476
477 list = bus->devices.next;
478 while (list != &bus->devices) {
479 struct pci_dev *pdev = pci_dev_b(list);
480 list = list->next;
481
482 if (first) {
483 dev = pdev;
484 break;
485 }
486
487 /* Try to handle "next" here some way? */
488 }
489 if (dev && dev->driver) {
490 strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN);
491 bind_info->major = 0;
492 bind_info->minor = 0;
493 bind_info->next = NULL;
494 return 0;
495 }
496 }
497 }
498#endif
499
500 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
501 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
502 if (p_dev->func == bind_info->function) {
503 p_dev = pcmcia_get_dev(p_dev);
504 if (!p_dev)
505 continue;
506 goto found;
507 }
508 }
509 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
510 return -ENODEV;
511
512 found:
513 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
514
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100515 p_drv = to_pcmcia_drv(p_dev->dev.driver);
516 if (p_drv && !p_dev->_locked) {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700517 ret = -EAGAIN;
518 goto err_put;
519 }
520
521 if (first)
Dominik Brodowskifd238232006-03-05 10:45:09 +0100522 node = p_dev->dev_node;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700523 else
Dominik Brodowskifd238232006-03-05 10:45:09 +0100524 for (node = p_dev->dev_node; node; node = node->next)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700525 if (node == bind_info->next)
526 break;
527 if (!node) {
528 ret = -ENODEV;
529 goto err_put;
530 }
531
532 strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN);
533 bind_info->major = node->major;
534 bind_info->minor = node->minor;
535 bind_info->next = node->next;
536
537 err_put:
538 pcmcia_put_dev(p_dev);
539 return (ret);
540} /* get_device_info */
541
542
543static int ds_open(struct inode *inode, struct file *file)
544{
545 socket_t i = iminor(inode);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700546 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700547 user_info_t *user;
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700548 static int warning_printed = 0;
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600549 int ret = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700550
551 ds_dbg(0, "ds_open(socket %d)\n", i);
552
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600553 lock_kernel();
Dominik Brodowskidc109492005-06-27 16:28:50 -0700554 s = pcmcia_get_socket_by_nr(i);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600555 if (!s) {
556 ret = -ENODEV;
557 goto out;
558 }
Dominik Brodowskidc109492005-06-27 16:28:50 -0700559 s = pcmcia_get_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600560 if (!s) {
561 ret = -ENODEV;
562 goto out;
563 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700564
565 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700566 if (s->pcmcia_state.busy) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700567 pcmcia_put_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600568 ret = -EBUSY;
569 goto out;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700570 }
571 else
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700572 s->pcmcia_state.busy = 1;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700573 }
574
575 user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
576 if (!user) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700577 pcmcia_put_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600578 ret = -ENOMEM;
579 goto out;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700580 }
581 user->event_tail = user->event_head = 0;
582 user->next = s->user;
583 user->user_magic = USER_MAGIC;
584 user->socket = s;
585 s->user = user;
586 file->private_data = user;
587
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700588 if (!warning_printed) {
589 printk(KERN_INFO "pcmcia: Detected deprecated PCMCIA ioctl "
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700590 "usage from process: %s.\n", current->comm);
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700591 printk(KERN_INFO "pcmcia: This interface will soon be removed from "
592 "the kernel; please expect breakage unless you upgrade "
593 "to new tools.\n");
594 printk(KERN_INFO "pcmcia: see http://www.kernel.org/pub/linux/"
595 "utils/kernel/pcmcia/pcmcia.html for details.\n");
596 warning_printed = 1;
597 }
598
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700599 if (s->pcmcia_state.present)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700600 queue_event(user, CS_EVENT_CARD_INSERTION);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600601out:
602 unlock_kernel();
603 return ret;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700604} /* ds_open */
605
606/*====================================================================*/
607
608static int ds_release(struct inode *inode, struct file *file)
609{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700610 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700611 user_info_t *user, **link;
612
613 ds_dbg(0, "ds_release(socket %d)\n", iminor(inode));
614
615 user = file->private_data;
616 if (CHECK_USER(user))
617 goto out;
618
619 s = user->socket;
620
621 /* Unlink user data structure */
622 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700623 s->pcmcia_state.busy = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700624 }
625 file->private_data = NULL;
626 for (link = &s->user; *link; link = &(*link)->next)
627 if (*link == user) break;
628 if (link == NULL)
629 goto out;
630 *link = user->next;
631 user->user_magic = 0;
632 kfree(user);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700633 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700634out:
635 return 0;
636} /* ds_release */
637
638/*====================================================================*/
639
640static ssize_t ds_read(struct file *file, char __user *buf,
641 size_t count, loff_t *ppos)
642{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700643 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700644 user_info_t *user;
645 int ret;
646
Josef Sipek40fad042006-12-08 02:37:29 -0800647 ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700648
649 if (count < 4)
650 return -EINVAL;
651
652 user = file->private_data;
653 if (CHECK_USER(user))
654 return -EIO;
655
656 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700657 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700658 return -EIO;
659
660 ret = wait_event_interruptible(s->queue, !queue_empty(user));
661 if (ret == 0)
662 ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;
663
664 return ret;
665} /* ds_read */
666
667/*====================================================================*/
668
669static ssize_t ds_write(struct file *file, const char __user *buf,
670 size_t count, loff_t *ppos)
671{
Josef Sipek40fad042006-12-08 02:37:29 -0800672 ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700673
674 if (count != 4)
675 return -EINVAL;
676 if ((file->f_flags & O_ACCMODE) == O_RDONLY)
677 return -EBADF;
678
679 return -EIO;
680} /* ds_write */
681
682/*====================================================================*/
683
684/* No kernel lock - fine */
685static u_int ds_poll(struct file *file, poll_table *wait)
686{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700687 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700688 user_info_t *user;
689
Josef Sipek40fad042006-12-08 02:37:29 -0800690 ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700691
692 user = file->private_data;
693 if (CHECK_USER(user))
694 return POLLERR;
695 s = user->socket;
696 /*
697 * We don't check for a dead socket here since that
698 * will send cardmgr into an endless spin.
699 */
700 poll_wait(file, &s->queue, wait);
701 if (!queue_empty(user))
702 return POLLIN | POLLRDNORM;
703 return 0;
704} /* ds_poll */
705
706/*====================================================================*/
707
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700708static int ds_ioctl(struct inode * inode, struct file * file,
709 u_int cmd, u_long arg)
710{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700711 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700712 void __user *uarg = (char __user *)arg;
713 u_int size;
714 int ret, err;
715 ds_ioctl_arg_t *buf;
716 user_info_t *user;
717
718 ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
719
720 user = file->private_data;
721 if (CHECK_USER(user))
722 return -EIO;
723
724 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700725 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700726 return -EIO;
727
728 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
729 if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
730
731 /* Permission check */
732 if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
733 return -EPERM;
734
735 if (cmd & IOC_IN) {
736 if (!access_ok(VERIFY_READ, uarg, size)) {
737 ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT);
738 return -EFAULT;
739 }
740 }
741 if (cmd & IOC_OUT) {
742 if (!access_ok(VERIFY_WRITE, uarg, size)) {
743 ds_dbg(3, "ds_ioctl(): verify_write = %d\n", -EFAULT);
744 return -EFAULT;
745 }
746 }
747 buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL);
748 if (!buf)
749 return -ENOMEM;
750
751 err = ret = 0;
752
Dominik Brodowski93740742006-11-19 11:21:27 -0500753 if (cmd & IOC_IN) {
754 if (__copy_from_user((char *)buf, uarg, size)) {
755 err = -EFAULT;
756 goto free_out;
757 }
758 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700759
760 switch (cmd) {
761 case DS_ADJUST_RESOURCE_INFO:
762 ret = pcmcia_adjust_resource_info(&buf->adjust);
763 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700764 case DS_GET_CONFIGURATION_INFO:
765 if (buf->config.Function &&
Dominik Brodowskidc109492005-06-27 16:28:50 -0700766 (buf->config.Function >= s->functions))
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700767 ret = CS_BAD_ARGS;
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100768 else {
769 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->config.Function);
Daniel Ritzf47ad212006-07-30 03:03:47 -0700770 ret = pccard_get_configuration_info(s, p_dev, &buf->config);
771 pcmcia_put_dev(p_dev);
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100772 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700773 break;
774 case DS_GET_FIRST_TUPLE:
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100775 mutex_lock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700776 pcmcia_validate_mem(s);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100777 mutex_unlock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700778 ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700779 break;
780 case DS_GET_NEXT_TUPLE:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700781 ret = pccard_get_next_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700782 break;
783 case DS_GET_TUPLE_DATA:
784 buf->tuple.TupleData = buf->tuple_parse.data;
785 buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700786 ret = pccard_get_tuple_data(s, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700787 break;
788 case DS_PARSE_TUPLE:
789 buf->tuple.TupleData = buf->tuple_parse.data;
790 ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
791 break;
792 case DS_RESET_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700793 ret = pccard_reset_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700794 break;
795 case DS_GET_STATUS:
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100796 if (buf->status.Function &&
797 (buf->status.Function >= s->functions))
798 ret = CS_BAD_ARGS;
799 else {
800 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->status.Function);
Daniel Ritzf47ad212006-07-30 03:03:47 -0700801 ret = pccard_get_status(s, p_dev, &buf->status);
802 pcmcia_put_dev(p_dev);
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100803 }
804 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700805 case DS_VALIDATE_CIS:
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100806 mutex_lock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700807 pcmcia_validate_mem(s);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100808 mutex_unlock(&s->skt_mutex);
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200809 ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo.Chains);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700810 break;
811 case DS_SUSPEND_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700812 ret = pcmcia_suspend_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700813 break;
814 case DS_RESUME_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700815 ret = pcmcia_resume_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700816 break;
817 case DS_EJECT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700818 err = pcmcia_eject_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700819 break;
820 case DS_INSERT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700821 err = pcmcia_insert_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700822 break;
823 case DS_ACCESS_CONFIGURATION_REGISTER:
824 if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
825 err = -EPERM;
826 goto free_out;
827 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100828
829 ret = CS_BAD_ARGS;
830
831 if (!(buf->conf_reg.Function &&
832 (buf->conf_reg.Function >= s->functions))) {
833 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->conf_reg.Function);
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700834 if (p_dev) {
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100835 ret = pcmcia_access_configuration_register(p_dev, &buf->conf_reg);
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700836 pcmcia_put_dev(p_dev);
837 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100838 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700839 break;
840 case DS_GET_FIRST_REGION:
841 case DS_GET_NEXT_REGION:
842 case DS_BIND_MTD:
843 if (!capable(CAP_SYS_ADMIN)) {
844 err = -EPERM;
845 goto free_out;
846 } else {
847 static int printed = 0;
848 if (!printed) {
849 printk(KERN_WARNING "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n");
850 printk(KERN_WARNING "MTD handling any more.\n");
851 printed++;
852 }
853 }
854 err = -EINVAL;
855 goto free_out;
856 break;
857 case DS_GET_FIRST_WINDOW:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700858 ret = pcmcia_get_window(s, &buf->win_info.handle, 0,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700859 &buf->win_info.window);
860 break;
861 case DS_GET_NEXT_WINDOW:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700862 ret = pcmcia_get_window(s, &buf->win_info.handle,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700863 buf->win_info.handle->index + 1, &buf->win_info.window);
864 break;
865 case DS_GET_MEM_PAGE:
866 ret = pcmcia_get_mem_page(buf->win_info.handle,
867 &buf->win_info.map);
868 break;
869 case DS_REPLACE_CIS:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700870 ret = pcmcia_replace_cis(s, &buf->cisdump);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700871 break;
872 case DS_BIND_REQUEST:
873 if (!capable(CAP_SYS_ADMIN)) {
874 err = -EPERM;
875 goto free_out;
876 }
877 err = bind_request(s, &buf->bind_info);
878 break;
879 case DS_GET_DEVICE_INFO:
880 err = get_device_info(s, &buf->bind_info, 1);
881 break;
882 case DS_GET_NEXT_DEVICE:
883 err = get_device_info(s, &buf->bind_info, 0);
884 break;
885 case DS_UNBIND_REQUEST:
886 err = 0;
887 break;
888 default:
889 err = -EINVAL;
890 }
891
892 if ((err == 0) && (ret != CS_SUCCESS)) {
893 ds_dbg(2, "ds_ioctl: ret = %d\n", ret);
894 switch (ret) {
895 case CS_BAD_SOCKET: case CS_NO_CARD:
896 err = -ENODEV; break;
897 case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ:
898 case CS_BAD_TUPLE:
899 err = -EINVAL; break;
900 case CS_IN_USE:
901 err = -EBUSY; break;
902 case CS_OUT_OF_RESOURCE:
903 err = -ENOSPC; break;
904 case CS_NO_MORE_ITEMS:
905 err = -ENODATA; break;
906 case CS_UNSUPPORTED_FUNCTION:
907 err = -ENOSYS; break;
908 default:
909 err = -EIO; break;
910 }
911 }
912
913 if (cmd & IOC_OUT) {
914 if (__copy_to_user(uarg, (char *)buf, size))
915 err = -EFAULT;
916 }
917
918free_out:
919 kfree(buf);
920 return err;
921} /* ds_ioctl */
922
923/*====================================================================*/
924
Arjan van de Vend54b1fd2007-02-12 00:55:34 -0800925static const struct file_operations ds_fops = {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700926 .owner = THIS_MODULE,
927 .open = ds_open,
928 .release = ds_release,
929 .ioctl = ds_ioctl,
930 .read = ds_read,
931 .write = ds_write,
932 .poll = ds_poll,
933};
934
935void __init pcmcia_setup_ioctl(void) {
936 int i;
937
938 /* Set up character device for user mode clients */
939 i = register_chrdev(0, "pcmcia", &ds_fops);
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -0700940 if (i < 0)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700941 printk(KERN_NOTICE "unable to find a free device # for "
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -0700942 "Driver Services (error=%d)\n", i);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700943 else
944 major_dev = i;
945
946#ifdef CONFIG_PROC_FS
akpm@linux-foundation.org97094dc2008-04-29 10:47:54 -0700947 proc_pccard = proc_mkdir("bus/pccard", NULL);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700948 if (proc_pccard)
949 create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
950#endif
951}
952
953
954void __exit pcmcia_cleanup_ioctl(void) {
955#ifdef CONFIG_PROC_FS
956 if (proc_pccard) {
957 remove_proc_entry("drivers", proc_pccard);
akpm@linux-foundation.org97094dc2008-04-29 10:47:54 -0700958 remove_proc_entry("bus/pccard", NULL);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700959 }
960#endif
961 if (major_dev != -1)
962 unregister_chrdev(major_dev, "pcmcia");
963}