blob: 99563134ac0fc79aa18b62f03410c9403ef666e9 [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
Dominik Brodowski7d16b652008-08-02 21:02:01 +020061#ifdef CONFIG_PCMCIA_DEBUG
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070062extern 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))
Dominik Brodowski69ba4432008-08-03 12:10:53 +0200152 return -EINVAL;
Dominik Brodowskic5023802008-06-19 19:02:52 +0200153
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) {
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200170 return 0;
Dominik Brodowskic5023802008-06-19 19:02:52 +0200171}
172
173#endif
174
175static int pcmcia_adjust_resource_info(adjust_t *adj)
176{
177 struct pcmcia_socket *s;
Dominik Brodowskide6405e2008-08-03 10:47:59 +0200178 int ret = -ENOSYS;
Dominik Brodowskic5023802008-06-19 19:02:52 +0200179 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))
Dominik Brodowski3939c1ef2008-08-03 11:10:56 +0200251 return -ENODEV;
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200252
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 }
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200277 return 0;
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200278 }
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;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200287 return 0;
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200288} /* pccard_get_status */
Dominik Brodowskic5023802008-06-19 19:02:52 +0200289
Dominik Brodowski64f34642008-08-02 17:00:46 +0200290int pccard_get_configuration_info(struct pcmcia_socket *s,
291 struct pcmcia_device *p_dev,
292 config_info_t *config)
293{
294 config_t *c;
295
296 if (!(s->state & SOCKET_PRESENT))
Dominik Brodowski3939c1ef2008-08-03 11:10:56 +0200297 return -ENODEV;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200298
299
300#ifdef CONFIG_CARDBUS
301 if (s->state & SOCKET_CARDBUS) {
302 memset(config, 0, sizeof(config_info_t));
303 config->Vcc = s->socket.Vcc;
304 config->Vpp1 = config->Vpp2 = s->socket.Vpp;
305 config->Option = s->cb_dev->subordinate->number;
306 if (s->state & SOCKET_CARDBUS_CONFIG) {
307 config->Attributes = CONF_VALID_CLIENT;
308 config->IntType = INT_CARDBUS;
309 config->AssignedIRQ = s->irq.AssignedIRQ;
310 if (config->AssignedIRQ)
311 config->Attributes |= CONF_ENABLE_IRQ;
312 if (s->io[0].res) {
313 config->BasePort1 = s->io[0].res->start;
314 config->NumPorts1 = s->io[0].res->end -
315 config->BasePort1 + 1;
316 }
317 }
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200318 return 0;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200319 }
320#endif
321
322 if (p_dev) {
323 c = p_dev->function_config;
324 config->Function = p_dev->func;
325 } else {
326 c = NULL;
327 config->Function = 0;
328 }
329
330 if ((c == NULL) || !(c->state & CONFIG_LOCKED)) {
331 config->Attributes = 0;
332 config->Vcc = s->socket.Vcc;
333 config->Vpp1 = config->Vpp2 = s->socket.Vpp;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200334 return 0;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200335 }
336
337 config->Attributes = c->Attributes | CONF_VALID_CLIENT;
338 config->Vcc = s->socket.Vcc;
339 config->Vpp1 = config->Vpp2 = s->socket.Vpp;
340 config->IntType = c->IntType;
341 config->ConfigBase = c->ConfigBase;
342 config->Status = c->Status;
343 config->Pin = c->Pin;
344 config->Copy = c->Copy;
345 config->Option = c->Option;
346 config->ExtStatus = c->ExtStatus;
347 config->Present = config->CardValues = c->CardValues;
348 config->IRQAttributes = c->irq.Attributes;
349 config->AssignedIRQ = s->irq.AssignedIRQ;
350 config->BasePort1 = c->io.BasePort1;
351 config->NumPorts1 = c->io.NumPorts1;
352 config->Attributes1 = c->io.Attributes1;
353 config->BasePort2 = c->io.BasePort2;
354 config->NumPorts2 = c->io.NumPorts2;
355 config->Attributes2 = c->io.Attributes2;
356 config->IOAddrLines = c->io.IOAddrLines;
357
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200358 return 0;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200359} /* pccard_get_configuration_info */
360
361
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700362/*======================================================================
363
364 These manage a ring buffer of events pending for one user process
365
366======================================================================*/
367
368
369static int queue_empty(user_info_t *user)
370{
371 return (user->event_head == user->event_tail);
372}
373
374static event_t get_queued_event(user_info_t *user)
375{
376 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
377 return user->event[user->event_tail];
378}
379
380static void queue_event(user_info_t *user, event_t event)
381{
382 user->event_head = (user->event_head+1) % MAX_EVENTS;
383 if (user->event_head == user->event_tail)
384 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
385 user->event[user->event_head] = event;
386}
387
Dominik Brodowskidc109492005-06-27 16:28:50 -0700388void handle_event(struct pcmcia_socket *s, event_t event)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700389{
390 user_info_t *user;
391 for (user = s->user; user; user = user->next)
392 queue_event(user, event);
393 wake_up_interruptible(&s->queue);
394}
395
396
397/*======================================================================
398
399 bind_request() and bind_device() are merged by now. Register_client()
400 is called right at the end of bind_request(), during the driver's
401 ->attach() call. Individual descriptions:
402
403 bind_request() connects a socket to a particular client driver.
404 It looks up the specified device ID in the list of registered
405 drivers, binds it to the socket, and tries to create an instance
406 of the device. unbind_request() deletes a driver instance.
407
408 Bind_device() associates a device driver with a particular socket.
409 It is normally called by Driver Services after it has identified
410 a newly inserted card. An instance of that driver will then be
411 eligible to register as a client of this socket.
412
413 Register_client() uses the dev_info_t handle to match the
414 caller with a socket. The driver must have already been bound
415 to a socket with bind_device() -- in fact, bind_device()
416 allocates the client structure that will be used.
417
418======================================================================*/
419
Dominik Brodowskidc109492005-06-27 16:28:50 -0700420static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700421{
422 struct pcmcia_driver *p_drv;
423 struct pcmcia_device *p_dev;
424 int ret = 0;
425 unsigned long flags;
426
Dominik Brodowskidc109492005-06-27 16:28:50 -0700427 s = pcmcia_get_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700428 if (!s)
429 return -EINVAL;
430
Dominik Brodowskidc109492005-06-27 16:28:50 -0700431 ds_dbg(2, "bind_request(%d, '%s')\n", s->sock,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700432 (char *)bind_info->dev_info);
433
434 p_drv = get_pcmcia_driver(&bind_info->dev_info);
435 if (!p_drv) {
436 ret = -EINVAL;
437 goto err_put;
438 }
439
440 if (!try_module_get(p_drv->owner)) {
441 ret = -EINVAL;
442 goto err_put_driver;
443 }
444
445 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
446 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
447 if (p_dev->func == bind_info->function) {
448 if ((p_dev->dev.driver == &p_drv->drv)) {
449 if (p_dev->cardmgr) {
450 /* if there's already a device
451 * registered, and it was registered
452 * by userspace before, we need to
453 * return the "instance". */
454 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
Dominik Brodowskifd238232006-03-05 10:45:09 +0100455 bind_info->instance = p_dev;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700456 ret = -EBUSY;
457 goto err_put_module;
458 } else {
459 /* the correct driver managed to bind
460 * itself magically to the correct
461 * device. */
462 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
463 p_dev->cardmgr = p_drv;
464 ret = 0;
465 goto err_put_module;
466 }
467 } else if (!p_dev->dev.driver) {
468 /* there's already a device available where
469 * no device has been bound to yet. So we don't
470 * need to register a device! */
471 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
472 goto rescan;
473 }
474 }
475 }
476 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
477
478 p_dev = pcmcia_device_add(s, bind_info->function);
479 if (!p_dev) {
480 ret = -EIO;
481 goto err_put_module;
482 }
483
484rescan:
485 p_dev->cardmgr = p_drv;
486
487 /* if a driver is already running, we can abort */
488 if (p_dev->dev.driver)
489 goto err_put_module;
490
491 /*
492 * Prevent this racing with a card insertion.
493 */
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100494 mutex_lock(&s->skt_mutex);
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700495 ret = bus_rescan_devices(&pcmcia_bus_type);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100496 mutex_unlock(&s->skt_mutex);
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700497 if (ret)
498 goto err_put_module;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700499
500 /* check whether the driver indeed matched. I don't care if this
501 * is racy or not, because it can only happen on cardmgr access
502 * paths...
503 */
504 if (!(p_dev->dev.driver == &p_drv->drv))
505 p_dev->cardmgr = NULL;
506
507 err_put_module:
508 module_put(p_drv->owner);
509 err_put_driver:
510 put_driver(&p_drv->drv);
511 err_put:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700512 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700513
514 return (ret);
515} /* bind_request */
516
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700517#ifdef CONFIG_CARDBUS
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700518
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700519static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s)
520{
521 if (!s || !(s->state & SOCKET_CARDBUS))
522 return NULL;
523
524 return s->cb_dev->subordinate;
525}
526#endif
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700527
Dominik Brodowskidc109492005-06-27 16:28:50 -0700528static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700529{
530 dev_node_t *node;
531 struct pcmcia_device *p_dev;
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100532 struct pcmcia_driver *p_drv;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700533 unsigned long flags;
534 int ret = 0;
535
536#ifdef CONFIG_CARDBUS
537 /*
538 * Some unbelievably ugly code to associate the PCI cardbus
539 * device and its driver with the PCMCIA "bind" information.
540 */
541 {
542 struct pci_bus *bus;
543
Dominik Brodowskidc109492005-06-27 16:28:50 -0700544 bus = pcmcia_lookup_bus(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700545 if (bus) {
546 struct list_head *list;
547 struct pci_dev *dev = NULL;
548
549 list = bus->devices.next;
550 while (list != &bus->devices) {
551 struct pci_dev *pdev = pci_dev_b(list);
552 list = list->next;
553
554 if (first) {
555 dev = pdev;
556 break;
557 }
558
559 /* Try to handle "next" here some way? */
560 }
561 if (dev && dev->driver) {
562 strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN);
563 bind_info->major = 0;
564 bind_info->minor = 0;
565 bind_info->next = NULL;
566 return 0;
567 }
568 }
569 }
570#endif
571
572 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
573 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
574 if (p_dev->func == bind_info->function) {
575 p_dev = pcmcia_get_dev(p_dev);
576 if (!p_dev)
577 continue;
578 goto found;
579 }
580 }
581 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
582 return -ENODEV;
583
584 found:
585 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
586
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100587 p_drv = to_pcmcia_drv(p_dev->dev.driver);
588 if (p_drv && !p_dev->_locked) {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700589 ret = -EAGAIN;
590 goto err_put;
591 }
592
593 if (first)
Dominik Brodowskifd238232006-03-05 10:45:09 +0100594 node = p_dev->dev_node;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700595 else
Dominik Brodowskifd238232006-03-05 10:45:09 +0100596 for (node = p_dev->dev_node; node; node = node->next)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700597 if (node == bind_info->next)
598 break;
599 if (!node) {
600 ret = -ENODEV;
601 goto err_put;
602 }
603
604 strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN);
605 bind_info->major = node->major;
606 bind_info->minor = node->minor;
607 bind_info->next = node->next;
608
609 err_put:
610 pcmcia_put_dev(p_dev);
611 return (ret);
612} /* get_device_info */
613
614
615static int ds_open(struct inode *inode, struct file *file)
616{
617 socket_t i = iminor(inode);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700618 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700619 user_info_t *user;
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700620 static int warning_printed = 0;
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600621 int ret = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700622
623 ds_dbg(0, "ds_open(socket %d)\n", i);
624
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600625 lock_kernel();
Dominik Brodowskidc109492005-06-27 16:28:50 -0700626 s = pcmcia_get_socket_by_nr(i);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600627 if (!s) {
628 ret = -ENODEV;
629 goto out;
630 }
Dominik Brodowskidc109492005-06-27 16:28:50 -0700631 s = pcmcia_get_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600632 if (!s) {
633 ret = -ENODEV;
634 goto out;
635 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700636
637 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700638 if (s->pcmcia_state.busy) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700639 pcmcia_put_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600640 ret = -EBUSY;
641 goto out;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700642 }
643 else
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700644 s->pcmcia_state.busy = 1;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700645 }
646
647 user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
648 if (!user) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700649 pcmcia_put_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600650 ret = -ENOMEM;
651 goto out;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700652 }
653 user->event_tail = user->event_head = 0;
654 user->next = s->user;
655 user->user_magic = USER_MAGIC;
656 user->socket = s;
657 s->user = user;
658 file->private_data = user;
659
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700660 if (!warning_printed) {
661 printk(KERN_INFO "pcmcia: Detected deprecated PCMCIA ioctl "
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700662 "usage from process: %s.\n", current->comm);
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700663 printk(KERN_INFO "pcmcia: This interface will soon be removed from "
664 "the kernel; please expect breakage unless you upgrade "
665 "to new tools.\n");
666 printk(KERN_INFO "pcmcia: see http://www.kernel.org/pub/linux/"
667 "utils/kernel/pcmcia/pcmcia.html for details.\n");
668 warning_printed = 1;
669 }
670
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700671 if (s->pcmcia_state.present)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700672 queue_event(user, CS_EVENT_CARD_INSERTION);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600673out:
674 unlock_kernel();
675 return ret;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700676} /* ds_open */
677
678/*====================================================================*/
679
680static int ds_release(struct inode *inode, struct file *file)
681{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700682 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700683 user_info_t *user, **link;
684
685 ds_dbg(0, "ds_release(socket %d)\n", iminor(inode));
686
687 user = file->private_data;
688 if (CHECK_USER(user))
689 goto out;
690
691 s = user->socket;
692
693 /* Unlink user data structure */
694 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700695 s->pcmcia_state.busy = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700696 }
697 file->private_data = NULL;
698 for (link = &s->user; *link; link = &(*link)->next)
699 if (*link == user) break;
700 if (link == NULL)
701 goto out;
702 *link = user->next;
703 user->user_magic = 0;
704 kfree(user);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700705 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700706out:
707 return 0;
708} /* ds_release */
709
710/*====================================================================*/
711
712static ssize_t ds_read(struct file *file, char __user *buf,
713 size_t count, loff_t *ppos)
714{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700715 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700716 user_info_t *user;
717 int ret;
718
Josef Sipek40fad042006-12-08 02:37:29 -0800719 ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700720
721 if (count < 4)
722 return -EINVAL;
723
724 user = file->private_data;
725 if (CHECK_USER(user))
726 return -EIO;
727
728 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700729 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700730 return -EIO;
731
732 ret = wait_event_interruptible(s->queue, !queue_empty(user));
733 if (ret == 0)
734 ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;
735
736 return ret;
737} /* ds_read */
738
739/*====================================================================*/
740
741static ssize_t ds_write(struct file *file, const char __user *buf,
742 size_t count, loff_t *ppos)
743{
Josef Sipek40fad042006-12-08 02:37:29 -0800744 ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700745
746 if (count != 4)
747 return -EINVAL;
748 if ((file->f_flags & O_ACCMODE) == O_RDONLY)
749 return -EBADF;
750
751 return -EIO;
752} /* ds_write */
753
754/*====================================================================*/
755
756/* No kernel lock - fine */
757static u_int ds_poll(struct file *file, poll_table *wait)
758{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700759 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700760 user_info_t *user;
761
Josef Sipek40fad042006-12-08 02:37:29 -0800762 ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700763
764 user = file->private_data;
765 if (CHECK_USER(user))
766 return POLLERR;
767 s = user->socket;
768 /*
769 * We don't check for a dead socket here since that
770 * will send cardmgr into an endless spin.
771 */
772 poll_wait(file, &s->queue, wait);
773 if (!queue_empty(user))
774 return POLLIN | POLLRDNORM;
775 return 0;
776} /* ds_poll */
777
778/*====================================================================*/
779
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700780static int ds_ioctl(struct inode * inode, struct file * file,
781 u_int cmd, u_long arg)
782{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700783 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700784 void __user *uarg = (char __user *)arg;
785 u_int size;
786 int ret, err;
787 ds_ioctl_arg_t *buf;
788 user_info_t *user;
789
790 ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
791
792 user = file->private_data;
793 if (CHECK_USER(user))
794 return -EIO;
795
796 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700797 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700798 return -EIO;
799
800 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
801 if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
802
803 /* Permission check */
804 if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
805 return -EPERM;
806
807 if (cmd & IOC_IN) {
808 if (!access_ok(VERIFY_READ, uarg, size)) {
809 ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT);
810 return -EFAULT;
811 }
812 }
813 if (cmd & IOC_OUT) {
814 if (!access_ok(VERIFY_WRITE, uarg, size)) {
815 ds_dbg(3, "ds_ioctl(): verify_write = %d\n", -EFAULT);
816 return -EFAULT;
817 }
818 }
819 buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL);
820 if (!buf)
821 return -ENOMEM;
822
823 err = ret = 0;
824
Dominik Brodowski93740742006-11-19 11:21:27 -0500825 if (cmd & IOC_IN) {
826 if (__copy_from_user((char *)buf, uarg, size)) {
827 err = -EFAULT;
828 goto free_out;
829 }
830 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700831
832 switch (cmd) {
833 case DS_ADJUST_RESOURCE_INFO:
834 ret = pcmcia_adjust_resource_info(&buf->adjust);
835 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700836 case DS_GET_CONFIGURATION_INFO:
837 if (buf->config.Function &&
Dominik Brodowskidc109492005-06-27 16:28:50 -0700838 (buf->config.Function >= s->functions))
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700839 ret = CS_BAD_ARGS;
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100840 else {
841 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->config.Function);
Daniel Ritzf47ad212006-07-30 03:03:47 -0700842 ret = pccard_get_configuration_info(s, p_dev, &buf->config);
843 pcmcia_put_dev(p_dev);
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100844 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700845 break;
846 case DS_GET_FIRST_TUPLE:
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100847 mutex_lock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700848 pcmcia_validate_mem(s);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100849 mutex_unlock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700850 ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700851 break;
852 case DS_GET_NEXT_TUPLE:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700853 ret = pccard_get_next_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700854 break;
855 case DS_GET_TUPLE_DATA:
856 buf->tuple.TupleData = buf->tuple_parse.data;
857 buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700858 ret = pccard_get_tuple_data(s, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700859 break;
860 case DS_PARSE_TUPLE:
861 buf->tuple.TupleData = buf->tuple_parse.data;
862 ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
863 break;
864 case DS_RESET_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700865 ret = pccard_reset_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700866 break;
867 case DS_GET_STATUS:
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100868 if (buf->status.Function &&
869 (buf->status.Function >= s->functions))
870 ret = CS_BAD_ARGS;
871 else {
872 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->status.Function);
Daniel Ritzf47ad212006-07-30 03:03:47 -0700873 ret = pccard_get_status(s, p_dev, &buf->status);
874 pcmcia_put_dev(p_dev);
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100875 }
876 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700877 case DS_VALIDATE_CIS:
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100878 mutex_lock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700879 pcmcia_validate_mem(s);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100880 mutex_unlock(&s->skt_mutex);
Dominik Brodowskic5081d52008-06-19 20:12:34 +0200881 ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo.Chains);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700882 break;
883 case DS_SUSPEND_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700884 ret = pcmcia_suspend_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700885 break;
886 case DS_RESUME_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700887 ret = pcmcia_resume_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700888 break;
889 case DS_EJECT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700890 err = pcmcia_eject_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700891 break;
892 case DS_INSERT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700893 err = pcmcia_insert_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700894 break;
895 case DS_ACCESS_CONFIGURATION_REGISTER:
896 if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
897 err = -EPERM;
898 goto free_out;
899 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100900
901 ret = CS_BAD_ARGS;
902
903 if (!(buf->conf_reg.Function &&
904 (buf->conf_reg.Function >= s->functions))) {
905 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->conf_reg.Function);
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700906 if (p_dev) {
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100907 ret = pcmcia_access_configuration_register(p_dev, &buf->conf_reg);
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700908 pcmcia_put_dev(p_dev);
909 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100910 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700911 break;
912 case DS_GET_FIRST_REGION:
913 case DS_GET_NEXT_REGION:
914 case DS_BIND_MTD:
915 if (!capable(CAP_SYS_ADMIN)) {
916 err = -EPERM;
917 goto free_out;
918 } else {
919 static int printed = 0;
920 if (!printed) {
921 printk(KERN_WARNING "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n");
922 printk(KERN_WARNING "MTD handling any more.\n");
923 printed++;
924 }
925 }
926 err = -EINVAL;
927 goto free_out;
928 break;
929 case DS_GET_FIRST_WINDOW:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700930 ret = pcmcia_get_window(s, &buf->win_info.handle, 0,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700931 &buf->win_info.window);
932 break;
933 case DS_GET_NEXT_WINDOW:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700934 ret = pcmcia_get_window(s, &buf->win_info.handle,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700935 buf->win_info.handle->index + 1, &buf->win_info.window);
936 break;
937 case DS_GET_MEM_PAGE:
938 ret = pcmcia_get_mem_page(buf->win_info.handle,
939 &buf->win_info.map);
940 break;
941 case DS_REPLACE_CIS:
Dominik Brodowski53efec92008-07-28 19:44:05 +0200942 ret = pcmcia_replace_cis(s, buf->cisdump.Data, buf->cisdump.Length);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700943 break;
944 case DS_BIND_REQUEST:
945 if (!capable(CAP_SYS_ADMIN)) {
946 err = -EPERM;
947 goto free_out;
948 }
949 err = bind_request(s, &buf->bind_info);
950 break;
951 case DS_GET_DEVICE_INFO:
952 err = get_device_info(s, &buf->bind_info, 1);
953 break;
954 case DS_GET_NEXT_DEVICE:
955 err = get_device_info(s, &buf->bind_info, 0);
956 break;
957 case DS_UNBIND_REQUEST:
958 err = 0;
959 break;
960 default:
961 err = -EINVAL;
962 }
963
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200964 if ((err == 0) && (ret != 0)) {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700965 ds_dbg(2, "ds_ioctl: ret = %d\n", ret);
966 switch (ret) {
Dominik Brodowski610e2372008-08-03 11:58:53 +0200967 case -ENODEV:
968 case -EINVAL:
969 case -EBUSY:
970 case -ENOSYS:
971 err = ret;
972 break;
Dominik Brodowski69ba4432008-08-03 12:10:53 +0200973 case CS_BAD_ARGS: case CS_BAD_TUPLE:
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700974 err = -EINVAL; break;
Dominik Brodowski610e2372008-08-03 11:58:53 +0200975 case -ENOMEM:
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700976 err = -ENOSPC; break;
Dominik Brodowski635d19b2008-08-03 11:47:29 +0200977 case -ENOSPC:
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700978 err = -ENODATA; break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700979 default:
980 err = -EIO; break;
981 }
982 }
983
984 if (cmd & IOC_OUT) {
985 if (__copy_to_user(uarg, (char *)buf, size))
986 err = -EFAULT;
987 }
988
989free_out:
990 kfree(buf);
991 return err;
992} /* ds_ioctl */
993
994/*====================================================================*/
995
Arjan van de Vend54b1fd2007-02-12 00:55:34 -0800996static const struct file_operations ds_fops = {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700997 .owner = THIS_MODULE,
998 .open = ds_open,
999 .release = ds_release,
1000 .ioctl = ds_ioctl,
1001 .read = ds_read,
1002 .write = ds_write,
1003 .poll = ds_poll,
1004};
1005
1006void __init pcmcia_setup_ioctl(void) {
1007 int i;
1008
1009 /* Set up character device for user mode clients */
1010 i = register_chrdev(0, "pcmcia", &ds_fops);
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -07001011 if (i < 0)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001012 printk(KERN_NOTICE "unable to find a free device # for "
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -07001013 "Driver Services (error=%d)\n", i);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001014 else
1015 major_dev = i;
1016
1017#ifdef CONFIG_PROC_FS
akpm@linux-foundation.org97094dc2008-04-29 10:47:54 -07001018 proc_pccard = proc_mkdir("bus/pccard", NULL);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001019 if (proc_pccard)
1020 create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
1021#endif
1022}
1023
1024
1025void __exit pcmcia_cleanup_ioctl(void) {
1026#ifdef CONFIG_PROC_FS
1027 if (proc_pccard) {
1028 remove_proc_entry("drivers", proc_pccard);
akpm@linux-foundation.org97094dc2008-04-29 10:47:54 -07001029 remove_proc_entry("bus/pccard", NULL);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001030 }
1031#endif
1032 if (major_dev != -1)
1033 unregister_chrdev(major_dev, "pcmcia");
1034}