blob: 30cf71d2ee235ce57cbef4d32600f63dd7b8f324 [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>
Alexey Dobriyane6be4a82009-09-21 17:03:55 -070030#include <linux/seq_file.h>
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -060031#include <linux/smp_lock.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070032#include <linux/workqueue.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070033
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>
Dominik Brodowski4aeba012008-06-20 13:24:31 +020037#include <pcmcia/cisreg.h>
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070038#include <pcmcia/ds.h>
39#include <pcmcia/ss.h>
40
41#include "cs_internal.h"
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070042
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
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700109static int proc_read_drivers_callback(struct device_driver *driver, void *_m)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700110{
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700111 struct seq_file *m = _m;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700112 struct pcmcia_driver *p_drv = container_of(driver,
113 struct pcmcia_driver, drv);
114
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700115 seq_printf(m, "%-24.24s 1 %d\n", p_drv->drv.name,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700116#ifdef CONFIG_MODULE_UNLOAD
117 (p_drv->owner) ? module_refcount(p_drv->owner) : 1
118#else
119 1
120#endif
121 );
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700122 return 0;
123}
124
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700125static int pccard_drivers_proc_show(struct seq_file *m, void *v)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700126{
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700127 return bus_for_each_drv(&pcmcia_bus_type, NULL,
128 m, proc_read_drivers_callback);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700129}
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700130
131static int pccard_drivers_proc_open(struct inode *inode, struct file *file)
132{
133 return single_open(file, pccard_drivers_proc_show, NULL);
134}
135
136static const struct file_operations pccard_drivers_proc_fops = {
137 .owner = THIS_MODULE,
138 .open = pccard_drivers_proc_open,
139 .read = seq_read,
140 .llseek = seq_lseek,
141 .release = single_release,
142};
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700143#endif
144
Dominik Brodowskic5023802008-06-19 19:02:52 +0200145
146#ifdef CONFIG_PCMCIA_PROBE
147
148static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
149{
150 int irq;
151 u32 mask;
152
153 irq = adj->resource.irq.IRQ;
154 if ((irq < 0) || (irq > 15))
Dominik Brodowski69ba4432008-08-03 12:10:53 +0200155 return -EINVAL;
Dominik Brodowskic5023802008-06-19 19:02:52 +0200156
157 if (adj->Action != REMOVE_MANAGED_RESOURCE)
158 return 0;
159
160 mask = 1 << irq;
161
162 if (!(s->irq_mask & mask))
163 return 0;
164
165 s->irq_mask &= ~mask;
166
167 return 0;
168}
169
170#else
171
172static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) {
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200173 return 0;
Dominik Brodowskic5023802008-06-19 19:02:52 +0200174}
175
176#endif
177
178static int pcmcia_adjust_resource_info(adjust_t *adj)
179{
180 struct pcmcia_socket *s;
Dominik Brodowskide6405e2008-08-03 10:47:59 +0200181 int ret = -ENOSYS;
Dominik Brodowskic5023802008-06-19 19:02:52 +0200182 unsigned long flags;
183
184 down_read(&pcmcia_socket_list_rwsem);
185 list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
186
187 if (adj->Resource == RES_IRQ)
188 ret = adjust_irq(s, adj);
189
190 else if (s->resource_ops->add_io) {
191 unsigned long begin, end;
192
193 /* you can't use the old interface if the new
194 * one was used before */
195 spin_lock_irqsave(&s->lock, flags);
196 if ((s->resource_setup_new) &&
197 !(s->resource_setup_old)) {
198 spin_unlock_irqrestore(&s->lock, flags);
199 continue;
200 } else if (!(s->resource_setup_old))
201 s->resource_setup_old = 1;
202 spin_unlock_irqrestore(&s->lock, flags);
203
204 switch (adj->Resource) {
205 case RES_MEMORY_RANGE:
206 begin = adj->resource.memory.Base;
207 end = adj->resource.memory.Base + adj->resource.memory.Size - 1;
208 if (s->resource_ops->add_mem)
209 ret =s->resource_ops->add_mem(s, adj->Action, begin, end);
210 case RES_IO_RANGE:
211 begin = adj->resource.io.BasePort;
212 end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1;
213 if (s->resource_ops->add_io)
214 ret = s->resource_ops->add_io(s, adj->Action, begin, end);
215 }
216 if (!ret) {
217 /* as there's no way we know this is the
218 * last call to adjust_resource_info, we
219 * always need to assume this is the latest
220 * one... */
221 spin_lock_irqsave(&s->lock, flags);
222 s->resource_setup_done = 1;
223 spin_unlock_irqrestore(&s->lock, flags);
224 }
225 }
226 }
227 up_read(&pcmcia_socket_list_rwsem);
228
229 return (ret);
230}
231
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200232/** pccard_get_status
233 *
234 * Get the current socket state bits. We don't support the latched
235 * SocketState yet: I haven't seen any point for it.
236 */
237
238static int pccard_get_status(struct pcmcia_socket *s,
239 struct pcmcia_device *p_dev,
240 cs_status_t *status)
241{
242 config_t *c;
243 int val;
244
245 s->ops->get_status(s, &val);
246 status->CardState = status->SocketState = 0;
247 status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0;
248 status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0;
249 status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0;
250 status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0;
251 if (s->state & SOCKET_SUSPEND)
252 status->CardState |= CS_EVENT_PM_SUSPEND;
253 if (!(s->state & SOCKET_PRESENT))
Dominik Brodowski3939c1ef2008-08-03 11:10:56 +0200254 return -ENODEV;
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200255
256 c = (p_dev) ? p_dev->function_config : NULL;
257
258 if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
259 (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
260 u_char reg;
261 if (c->CardValues & PRESENT_PIN_REPLACE) {
262 pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
263 status->CardState |=
264 (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0;
265 status->CardState |=
266 (reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0;
267 status->CardState |=
268 (reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0;
269 status->CardState |=
270 (reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0;
271 } else {
272 /* No PRR? Then assume we're always ready */
273 status->CardState |= CS_EVENT_READY_CHANGE;
274 }
275 if (c->CardValues & PRESENT_EXT_STATUS) {
276 pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
277 status->CardState |=
278 (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
279 }
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200280 return 0;
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200281 }
282 status->CardState |=
283 (val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0;
284 status->CardState |=
285 (val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0;
286 status->CardState |=
287 (val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0;
288 status->CardState |=
289 (val & SS_READY) ? CS_EVENT_READY_CHANGE : 0;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200290 return 0;
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200291} /* pccard_get_status */
Dominik Brodowskic5023802008-06-19 19:02:52 +0200292
Roel Kluina0779322008-12-09 22:12:50 +0100293static int pccard_get_configuration_info(struct pcmcia_socket *s,
Dominik Brodowski64f34642008-08-02 17:00:46 +0200294 struct pcmcia_device *p_dev,
295 config_info_t *config)
296{
297 config_t *c;
298
299 if (!(s->state & SOCKET_PRESENT))
Dominik Brodowski3939c1ef2008-08-03 11:10:56 +0200300 return -ENODEV;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200301
302
303#ifdef CONFIG_CARDBUS
304 if (s->state & SOCKET_CARDBUS) {
305 memset(config, 0, sizeof(config_info_t));
306 config->Vcc = s->socket.Vcc;
307 config->Vpp1 = config->Vpp2 = s->socket.Vpp;
308 config->Option = s->cb_dev->subordinate->number;
309 if (s->state & SOCKET_CARDBUS_CONFIG) {
310 config->Attributes = CONF_VALID_CLIENT;
311 config->IntType = INT_CARDBUS;
312 config->AssignedIRQ = s->irq.AssignedIRQ;
313 if (config->AssignedIRQ)
314 config->Attributes |= CONF_ENABLE_IRQ;
315 if (s->io[0].res) {
316 config->BasePort1 = s->io[0].res->start;
317 config->NumPorts1 = s->io[0].res->end -
318 config->BasePort1 + 1;
319 }
320 }
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200321 return 0;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200322 }
323#endif
324
325 if (p_dev) {
326 c = p_dev->function_config;
327 config->Function = p_dev->func;
328 } else {
329 c = NULL;
330 config->Function = 0;
331 }
332
333 if ((c == NULL) || !(c->state & CONFIG_LOCKED)) {
334 config->Attributes = 0;
335 config->Vcc = s->socket.Vcc;
336 config->Vpp1 = config->Vpp2 = s->socket.Vpp;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200337 return 0;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200338 }
339
340 config->Attributes = c->Attributes | CONF_VALID_CLIENT;
341 config->Vcc = s->socket.Vcc;
342 config->Vpp1 = config->Vpp2 = s->socket.Vpp;
343 config->IntType = c->IntType;
344 config->ConfigBase = c->ConfigBase;
345 config->Status = c->Status;
346 config->Pin = c->Pin;
347 config->Copy = c->Copy;
348 config->Option = c->Option;
349 config->ExtStatus = c->ExtStatus;
350 config->Present = config->CardValues = c->CardValues;
351 config->IRQAttributes = c->irq.Attributes;
352 config->AssignedIRQ = s->irq.AssignedIRQ;
353 config->BasePort1 = c->io.BasePort1;
354 config->NumPorts1 = c->io.NumPorts1;
355 config->Attributes1 = c->io.Attributes1;
356 config->BasePort2 = c->io.BasePort2;
357 config->NumPorts2 = c->io.NumPorts2;
358 config->Attributes2 = c->io.Attributes2;
359 config->IOAddrLines = c->io.IOAddrLines;
360
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200361 return 0;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200362} /* pccard_get_configuration_info */
363
364
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700365/*======================================================================
366
367 These manage a ring buffer of events pending for one user process
368
369======================================================================*/
370
371
372static int queue_empty(user_info_t *user)
373{
374 return (user->event_head == user->event_tail);
375}
376
377static event_t get_queued_event(user_info_t *user)
378{
379 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
380 return user->event[user->event_tail];
381}
382
383static void queue_event(user_info_t *user, event_t event)
384{
385 user->event_head = (user->event_head+1) % MAX_EVENTS;
386 if (user->event_head == user->event_tail)
387 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
388 user->event[user->event_head] = event;
389}
390
Dominik Brodowskidc109492005-06-27 16:28:50 -0700391void handle_event(struct pcmcia_socket *s, event_t event)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700392{
393 user_info_t *user;
394 for (user = s->user; user; user = user->next)
395 queue_event(user, event);
396 wake_up_interruptible(&s->queue);
397}
398
399
400/*======================================================================
401
402 bind_request() and bind_device() are merged by now. Register_client()
403 is called right at the end of bind_request(), during the driver's
404 ->attach() call. Individual descriptions:
405
406 bind_request() connects a socket to a particular client driver.
407 It looks up the specified device ID in the list of registered
408 drivers, binds it to the socket, and tries to create an instance
409 of the device. unbind_request() deletes a driver instance.
410
411 Bind_device() associates a device driver with a particular socket.
412 It is normally called by Driver Services after it has identified
413 a newly inserted card. An instance of that driver will then be
414 eligible to register as a client of this socket.
415
416 Register_client() uses the dev_info_t handle to match the
417 caller with a socket. The driver must have already been bound
418 to a socket with bind_device() -- in fact, bind_device()
419 allocates the client structure that will be used.
420
421======================================================================*/
422
Dominik Brodowskidc109492005-06-27 16:28:50 -0700423static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700424{
425 struct pcmcia_driver *p_drv;
426 struct pcmcia_device *p_dev;
427 int ret = 0;
428 unsigned long flags;
429
Dominik Brodowskidc109492005-06-27 16:28:50 -0700430 s = pcmcia_get_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700431 if (!s)
432 return -EINVAL;
433
Dominik Brodowskidc109492005-06-27 16:28:50 -0700434 ds_dbg(2, "bind_request(%d, '%s')\n", s->sock,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700435 (char *)bind_info->dev_info);
436
437 p_drv = get_pcmcia_driver(&bind_info->dev_info);
438 if (!p_drv) {
439 ret = -EINVAL;
440 goto err_put;
441 }
442
443 if (!try_module_get(p_drv->owner)) {
444 ret = -EINVAL;
445 goto err_put_driver;
446 }
447
448 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
449 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
450 if (p_dev->func == bind_info->function) {
451 if ((p_dev->dev.driver == &p_drv->drv)) {
452 if (p_dev->cardmgr) {
453 /* if there's already a device
454 * registered, and it was registered
455 * by userspace before, we need to
456 * return the "instance". */
457 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
Dominik Brodowskifd238232006-03-05 10:45:09 +0100458 bind_info->instance = p_dev;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700459 ret = -EBUSY;
460 goto err_put_module;
461 } else {
462 /* the correct driver managed to bind
463 * itself magically to the correct
464 * device. */
465 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
466 p_dev->cardmgr = p_drv;
467 ret = 0;
468 goto err_put_module;
469 }
470 } else if (!p_dev->dev.driver) {
471 /* there's already a device available where
472 * no device has been bound to yet. So we don't
473 * need to register a device! */
474 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
475 goto rescan;
476 }
477 }
478 }
479 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
480
481 p_dev = pcmcia_device_add(s, bind_info->function);
482 if (!p_dev) {
483 ret = -EIO;
484 goto err_put_module;
485 }
486
487rescan:
488 p_dev->cardmgr = p_drv;
489
490 /* if a driver is already running, we can abort */
491 if (p_dev->dev.driver)
492 goto err_put_module;
493
494 /*
495 * Prevent this racing with a card insertion.
496 */
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100497 mutex_lock(&s->skt_mutex);
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700498 ret = bus_rescan_devices(&pcmcia_bus_type);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100499 mutex_unlock(&s->skt_mutex);
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700500 if (ret)
501 goto err_put_module;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700502
503 /* check whether the driver indeed matched. I don't care if this
504 * is racy or not, because it can only happen on cardmgr access
505 * paths...
506 */
507 if (!(p_dev->dev.driver == &p_drv->drv))
508 p_dev->cardmgr = NULL;
509
510 err_put_module:
511 module_put(p_drv->owner);
512 err_put_driver:
513 put_driver(&p_drv->drv);
514 err_put:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700515 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700516
517 return (ret);
518} /* bind_request */
519
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700520#ifdef CONFIG_CARDBUS
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700521
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700522static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s)
523{
524 if (!s || !(s->state & SOCKET_CARDBUS))
525 return NULL;
526
527 return s->cb_dev->subordinate;
528}
529#endif
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700530
Dominik Brodowskidc109492005-06-27 16:28:50 -0700531static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700532{
533 dev_node_t *node;
534 struct pcmcia_device *p_dev;
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100535 struct pcmcia_driver *p_drv;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700536 unsigned long flags;
537 int ret = 0;
538
539#ifdef CONFIG_CARDBUS
540 /*
541 * Some unbelievably ugly code to associate the PCI cardbus
542 * device and its driver with the PCMCIA "bind" information.
543 */
544 {
545 struct pci_bus *bus;
546
Dominik Brodowskidc109492005-06-27 16:28:50 -0700547 bus = pcmcia_lookup_bus(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700548 if (bus) {
549 struct list_head *list;
550 struct pci_dev *dev = NULL;
551
552 list = bus->devices.next;
553 while (list != &bus->devices) {
554 struct pci_dev *pdev = pci_dev_b(list);
555 list = list->next;
556
557 if (first) {
558 dev = pdev;
559 break;
560 }
561
562 /* Try to handle "next" here some way? */
563 }
564 if (dev && dev->driver) {
565 strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN);
566 bind_info->major = 0;
567 bind_info->minor = 0;
568 bind_info->next = NULL;
569 return 0;
570 }
571 }
572 }
573#endif
574
575 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
576 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
577 if (p_dev->func == bind_info->function) {
578 p_dev = pcmcia_get_dev(p_dev);
579 if (!p_dev)
580 continue;
581 goto found;
582 }
583 }
584 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
585 return -ENODEV;
586
587 found:
588 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
589
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100590 p_drv = to_pcmcia_drv(p_dev->dev.driver);
591 if (p_drv && !p_dev->_locked) {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700592 ret = -EAGAIN;
593 goto err_put;
594 }
595
596 if (first)
Dominik Brodowskifd238232006-03-05 10:45:09 +0100597 node = p_dev->dev_node;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700598 else
Dominik Brodowskifd238232006-03-05 10:45:09 +0100599 for (node = p_dev->dev_node; node; node = node->next)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700600 if (node == bind_info->next)
601 break;
602 if (!node) {
603 ret = -ENODEV;
604 goto err_put;
605 }
606
607 strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN);
608 bind_info->major = node->major;
609 bind_info->minor = node->minor;
610 bind_info->next = node->next;
611
612 err_put:
613 pcmcia_put_dev(p_dev);
614 return (ret);
615} /* get_device_info */
616
617
618static int ds_open(struct inode *inode, struct file *file)
619{
620 socket_t i = iminor(inode);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700621 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700622 user_info_t *user;
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700623 static int warning_printed = 0;
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600624 int ret = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700625
626 ds_dbg(0, "ds_open(socket %d)\n", i);
627
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600628 lock_kernel();
Dominik Brodowskidc109492005-06-27 16:28:50 -0700629 s = pcmcia_get_socket_by_nr(i);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600630 if (!s) {
631 ret = -ENODEV;
632 goto out;
633 }
Dominik Brodowskidc109492005-06-27 16:28:50 -0700634 s = pcmcia_get_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600635 if (!s) {
636 ret = -ENODEV;
637 goto out;
638 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700639
640 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700641 if (s->pcmcia_state.busy) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700642 pcmcia_put_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600643 ret = -EBUSY;
644 goto out;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700645 }
646 else
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700647 s->pcmcia_state.busy = 1;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700648 }
649
650 user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
651 if (!user) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700652 pcmcia_put_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600653 ret = -ENOMEM;
654 goto out;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700655 }
656 user->event_tail = user->event_head = 0;
657 user->next = s->user;
658 user->user_magic = USER_MAGIC;
659 user->socket = s;
660 s->user = user;
661 file->private_data = user;
662
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700663 if (!warning_printed) {
664 printk(KERN_INFO "pcmcia: Detected deprecated PCMCIA ioctl "
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700665 "usage from process: %s.\n", current->comm);
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700666 printk(KERN_INFO "pcmcia: This interface will soon be removed from "
667 "the kernel; please expect breakage unless you upgrade "
668 "to new tools.\n");
669 printk(KERN_INFO "pcmcia: see http://www.kernel.org/pub/linux/"
670 "utils/kernel/pcmcia/pcmcia.html for details.\n");
671 warning_printed = 1;
672 }
673
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700674 if (s->pcmcia_state.present)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700675 queue_event(user, CS_EVENT_CARD_INSERTION);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600676out:
677 unlock_kernel();
678 return ret;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700679} /* ds_open */
680
681/*====================================================================*/
682
683static int ds_release(struct inode *inode, struct file *file)
684{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700685 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700686 user_info_t *user, **link;
687
688 ds_dbg(0, "ds_release(socket %d)\n", iminor(inode));
689
690 user = file->private_data;
691 if (CHECK_USER(user))
692 goto out;
693
694 s = user->socket;
695
696 /* Unlink user data structure */
697 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700698 s->pcmcia_state.busy = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700699 }
700 file->private_data = NULL;
701 for (link = &s->user; *link; link = &(*link)->next)
702 if (*link == user) break;
703 if (link == NULL)
704 goto out;
705 *link = user->next;
706 user->user_magic = 0;
707 kfree(user);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700708 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700709out:
710 return 0;
711} /* ds_release */
712
713/*====================================================================*/
714
715static ssize_t ds_read(struct file *file, char __user *buf,
716 size_t count, loff_t *ppos)
717{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700718 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700719 user_info_t *user;
720 int ret;
721
Josef Sipek40fad042006-12-08 02:37:29 -0800722 ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700723
724 if (count < 4)
725 return -EINVAL;
726
727 user = file->private_data;
728 if (CHECK_USER(user))
729 return -EIO;
730
731 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700732 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700733 return -EIO;
734
735 ret = wait_event_interruptible(s->queue, !queue_empty(user));
736 if (ret == 0)
737 ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;
738
739 return ret;
740} /* ds_read */
741
742/*====================================================================*/
743
744static ssize_t ds_write(struct file *file, const char __user *buf,
745 size_t count, loff_t *ppos)
746{
Josef Sipek40fad042006-12-08 02:37:29 -0800747 ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700748
749 if (count != 4)
750 return -EINVAL;
751 if ((file->f_flags & O_ACCMODE) == O_RDONLY)
752 return -EBADF;
753
754 return -EIO;
755} /* ds_write */
756
757/*====================================================================*/
758
759/* No kernel lock - fine */
760static u_int ds_poll(struct file *file, poll_table *wait)
761{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700762 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700763 user_info_t *user;
764
Josef Sipek40fad042006-12-08 02:37:29 -0800765 ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700766
767 user = file->private_data;
768 if (CHECK_USER(user))
769 return POLLERR;
770 s = user->socket;
771 /*
772 * We don't check for a dead socket here since that
773 * will send cardmgr into an endless spin.
774 */
775 poll_wait(file, &s->queue, wait);
776 if (!queue_empty(user))
777 return POLLIN | POLLRDNORM;
778 return 0;
779} /* ds_poll */
780
781/*====================================================================*/
782
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700783static int ds_ioctl(struct inode * inode, struct file * file,
784 u_int cmd, u_long arg)
785{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700786 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700787 void __user *uarg = (char __user *)arg;
788 u_int size;
789 int ret, err;
790 ds_ioctl_arg_t *buf;
791 user_info_t *user;
792
793 ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
794
795 user = file->private_data;
796 if (CHECK_USER(user))
797 return -EIO;
798
799 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700800 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700801 return -EIO;
802
803 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
804 if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
805
806 /* Permission check */
807 if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
808 return -EPERM;
809
810 if (cmd & IOC_IN) {
811 if (!access_ok(VERIFY_READ, uarg, size)) {
812 ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT);
813 return -EFAULT;
814 }
815 }
816 if (cmd & IOC_OUT) {
817 if (!access_ok(VERIFY_WRITE, uarg, size)) {
818 ds_dbg(3, "ds_ioctl(): verify_write = %d\n", -EFAULT);
819 return -EFAULT;
820 }
821 }
822 buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL);
823 if (!buf)
824 return -ENOMEM;
825
826 err = ret = 0;
827
Dominik Brodowski93740742006-11-19 11:21:27 -0500828 if (cmd & IOC_IN) {
829 if (__copy_from_user((char *)buf, uarg, size)) {
830 err = -EFAULT;
831 goto free_out;
832 }
833 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700834
835 switch (cmd) {
836 case DS_ADJUST_RESOURCE_INFO:
837 ret = pcmcia_adjust_resource_info(&buf->adjust);
838 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700839 case DS_GET_CONFIGURATION_INFO:
840 if (buf->config.Function &&
Dominik Brodowskidc109492005-06-27 16:28:50 -0700841 (buf->config.Function >= s->functions))
Dominik Brodowski926c5402008-08-03 12:15:11 +0200842 ret = -EINVAL;
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100843 else {
844 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->config.Function);
Daniel Ritzf47ad212006-07-30 03:03:47 -0700845 ret = pccard_get_configuration_info(s, p_dev, &buf->config);
846 pcmcia_put_dev(p_dev);
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100847 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700848 break;
849 case DS_GET_FIRST_TUPLE:
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100850 mutex_lock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700851 pcmcia_validate_mem(s);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100852 mutex_unlock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700853 ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700854 break;
855 case DS_GET_NEXT_TUPLE:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700856 ret = pccard_get_next_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700857 break;
858 case DS_GET_TUPLE_DATA:
859 buf->tuple.TupleData = buf->tuple_parse.data;
860 buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700861 ret = pccard_get_tuple_data(s, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700862 break;
863 case DS_PARSE_TUPLE:
864 buf->tuple.TupleData = buf->tuple_parse.data;
Dominik Brodowski2f3061e2008-08-31 15:50:33 +0200865 ret = pcmcia_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700866 break;
867 case DS_RESET_CARD:
Dominik Brodowski994917f2008-08-31 15:20:26 +0200868 ret = pcmcia_reset_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700869 break;
870 case DS_GET_STATUS:
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100871 if (buf->status.Function &&
872 (buf->status.Function >= s->functions))
Dominik Brodowski926c5402008-08-03 12:15:11 +0200873 ret = -EINVAL;
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100874 else {
875 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->status.Function);
Daniel Ritzf47ad212006-07-30 03:03:47 -0700876 ret = pccard_get_status(s, p_dev, &buf->status);
877 pcmcia_put_dev(p_dev);
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100878 }
879 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700880 case DS_VALIDATE_CIS:
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100881 mutex_lock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700882 pcmcia_validate_mem(s);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100883 mutex_unlock(&s->skt_mutex);
Dominik Brodowski84897fc2009-10-18 23:51:09 +0200884 ret = pccard_validate_cis(s, &buf->cisinfo.Chains);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700885 break;
886 case DS_SUSPEND_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700887 ret = pcmcia_suspend_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700888 break;
889 case DS_RESUME_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700890 ret = pcmcia_resume_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700891 break;
892 case DS_EJECT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700893 err = pcmcia_eject_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700894 break;
895 case DS_INSERT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700896 err = pcmcia_insert_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700897 break;
898 case DS_ACCESS_CONFIGURATION_REGISTER:
899 if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
900 err = -EPERM;
901 goto free_out;
902 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100903
Dominik Brodowski926c5402008-08-03 12:15:11 +0200904 ret = -EINVAL;
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100905
906 if (!(buf->conf_reg.Function &&
907 (buf->conf_reg.Function >= s->functions))) {
908 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->conf_reg.Function);
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700909 if (p_dev) {
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100910 ret = pcmcia_access_configuration_register(p_dev, &buf->conf_reg);
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700911 pcmcia_put_dev(p_dev);
912 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100913 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700914 break;
915 case DS_GET_FIRST_REGION:
916 case DS_GET_NEXT_REGION:
917 case DS_BIND_MTD:
918 if (!capable(CAP_SYS_ADMIN)) {
919 err = -EPERM;
920 goto free_out;
921 } else {
Minchan Kima9c56952009-06-16 15:33:44 -0700922 printk_once(KERN_WARNING
923 "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n");
924 printk_once(KERN_WARNING "MTD handling any more.\n");
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700925 }
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 Brodowski610e2372008-08-03 11:58:53 +0200973 case -ENOMEM:
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700974 err = -ENOSPC; break;
Dominik Brodowski635d19b2008-08-03 11:47:29 +0200975 case -ENOSPC:
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700976 err = -ENODATA; break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700977 default:
978 err = -EIO; break;
979 }
980 }
981
982 if (cmd & IOC_OUT) {
983 if (__copy_to_user(uarg, (char *)buf, size))
984 err = -EFAULT;
985 }
986
987free_out:
988 kfree(buf);
989 return err;
990} /* ds_ioctl */
991
992/*====================================================================*/
993
Arjan van de Vend54b1fd2007-02-12 00:55:34 -0800994static const struct file_operations ds_fops = {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700995 .owner = THIS_MODULE,
996 .open = ds_open,
997 .release = ds_release,
998 .ioctl = ds_ioctl,
999 .read = ds_read,
1000 .write = ds_write,
1001 .poll = ds_poll,
1002};
1003
1004void __init pcmcia_setup_ioctl(void) {
1005 int i;
1006
1007 /* Set up character device for user mode clients */
1008 i = register_chrdev(0, "pcmcia", &ds_fops);
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -07001009 if (i < 0)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001010 printk(KERN_NOTICE "unable to find a free device # for "
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -07001011 "Driver Services (error=%d)\n", i);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001012 else
1013 major_dev = i;
1014
1015#ifdef CONFIG_PROC_FS
akpm@linux-foundation.org97094dc2008-04-29 10:47:54 -07001016 proc_pccard = proc_mkdir("bus/pccard", NULL);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001017 if (proc_pccard)
Alexey Dobriyane6be4a82009-09-21 17:03:55 -07001018 proc_create("drivers", 0, proc_pccard, &pccard_drivers_proc_fops);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001019#endif
1020}
1021
1022
1023void __exit pcmcia_cleanup_ioctl(void) {
1024#ifdef CONFIG_PROC_FS
1025 if (proc_pccard) {
1026 remove_proc_entry("drivers", proc_pccard);
akpm@linux-foundation.org97094dc2008-04-29 10:47:54 -07001027 remove_proc_entry("bus/pccard", NULL);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001028 }
1029#endif
1030 if (major_dev != -1)
1031 unregister_chrdev(major_dev, "pcmcia");
1032}