blob: 056fd131c89c4039dfb690e47594aa3872ab3f68 [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 Brodowski855cdf12006-01-10 20:48:59 +010061static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s,
62 unsigned int function)
63{
64 struct pcmcia_device *p_dev = NULL;
65 unsigned long flags;
66
67 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
68 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
69 if (p_dev->func == function) {
70 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
71 return pcmcia_get_dev(p_dev);
72 }
73 }
74 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
75 return NULL;
76}
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070077
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070078/* backwards-compatible accessing of driver --- by name! */
79
Dominik Brodowski855cdf12006-01-10 20:48:59 +010080static struct pcmcia_driver *get_pcmcia_driver(dev_info_t *dev_info)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070081{
82 struct device_driver *drv;
83 struct pcmcia_driver *p_drv;
84
85 drv = driver_find((char *) dev_info, &pcmcia_bus_type);
86 if (!drv)
87 return NULL;
88
89 p_drv = container_of(drv, struct pcmcia_driver, drv);
90
91 return (p_drv);
92}
93
94
95#ifdef CONFIG_PROC_FS
96static struct proc_dir_entry *proc_pccard = NULL;
97
Alexey Dobriyane6be4a82009-09-21 17:03:55 -070098static int proc_read_drivers_callback(struct device_driver *driver, void *_m)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -070099{
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700100 struct seq_file *m = _m;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700101 struct pcmcia_driver *p_drv = container_of(driver,
102 struct pcmcia_driver, drv);
103
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700104 seq_printf(m, "%-24.24s 1 %d\n", p_drv->drv.name,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700105#ifdef CONFIG_MODULE_UNLOAD
106 (p_drv->owner) ? module_refcount(p_drv->owner) : 1
107#else
108 1
109#endif
110 );
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700111 return 0;
112}
113
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700114static int pccard_drivers_proc_show(struct seq_file *m, void *v)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700115{
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700116 return bus_for_each_drv(&pcmcia_bus_type, NULL,
117 m, proc_read_drivers_callback);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700118}
Alexey Dobriyane6be4a82009-09-21 17:03:55 -0700119
120static int pccard_drivers_proc_open(struct inode *inode, struct file *file)
121{
122 return single_open(file, pccard_drivers_proc_show, NULL);
123}
124
125static const struct file_operations pccard_drivers_proc_fops = {
126 .owner = THIS_MODULE,
127 .open = pccard_drivers_proc_open,
128 .read = seq_read,
129 .llseek = seq_lseek,
130 .release = single_release,
131};
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700132#endif
133
Dominik Brodowskic5023802008-06-19 19:02:52 +0200134
135#ifdef CONFIG_PCMCIA_PROBE
136
137static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
138{
139 int irq;
140 u32 mask;
141
142 irq = adj->resource.irq.IRQ;
143 if ((irq < 0) || (irq > 15))
Dominik Brodowski69ba4432008-08-03 12:10:53 +0200144 return -EINVAL;
Dominik Brodowskic5023802008-06-19 19:02:52 +0200145
146 if (adj->Action != REMOVE_MANAGED_RESOURCE)
147 return 0;
148
149 mask = 1 << irq;
150
151 if (!(s->irq_mask & mask))
152 return 0;
153
154 s->irq_mask &= ~mask;
155
156 return 0;
157}
158
159#else
160
161static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) {
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200162 return 0;
Dominik Brodowskic5023802008-06-19 19:02:52 +0200163}
164
165#endif
166
167static int pcmcia_adjust_resource_info(adjust_t *adj)
168{
169 struct pcmcia_socket *s;
Dominik Brodowskide6405e2008-08-03 10:47:59 +0200170 int ret = -ENOSYS;
Dominik Brodowskic5023802008-06-19 19:02:52 +0200171 unsigned long flags;
172
173 down_read(&pcmcia_socket_list_rwsem);
174 list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
175
176 if (adj->Resource == RES_IRQ)
177 ret = adjust_irq(s, adj);
178
179 else if (s->resource_ops->add_io) {
180 unsigned long begin, end;
181
182 /* you can't use the old interface if the new
183 * one was used before */
184 spin_lock_irqsave(&s->lock, flags);
185 if ((s->resource_setup_new) &&
186 !(s->resource_setup_old)) {
187 spin_unlock_irqrestore(&s->lock, flags);
188 continue;
189 } else if (!(s->resource_setup_old))
190 s->resource_setup_old = 1;
191 spin_unlock_irqrestore(&s->lock, flags);
192
193 switch (adj->Resource) {
194 case RES_MEMORY_RANGE:
195 begin = adj->resource.memory.Base;
196 end = adj->resource.memory.Base + adj->resource.memory.Size - 1;
197 if (s->resource_ops->add_mem)
198 ret =s->resource_ops->add_mem(s, adj->Action, begin, end);
199 case RES_IO_RANGE:
200 begin = adj->resource.io.BasePort;
201 end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1;
202 if (s->resource_ops->add_io)
203 ret = s->resource_ops->add_io(s, adj->Action, begin, end);
204 }
205 if (!ret) {
206 /* as there's no way we know this is the
207 * last call to adjust_resource_info, we
208 * always need to assume this is the latest
209 * one... */
210 spin_lock_irqsave(&s->lock, flags);
211 s->resource_setup_done = 1;
212 spin_unlock_irqrestore(&s->lock, flags);
213 }
214 }
215 }
216 up_read(&pcmcia_socket_list_rwsem);
217
218 return (ret);
219}
220
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200221/** pccard_get_status
222 *
223 * Get the current socket state bits. We don't support the latched
224 * SocketState yet: I haven't seen any point for it.
225 */
226
227static int pccard_get_status(struct pcmcia_socket *s,
228 struct pcmcia_device *p_dev,
229 cs_status_t *status)
230{
231 config_t *c;
232 int val;
233
234 s->ops->get_status(s, &val);
235 status->CardState = status->SocketState = 0;
236 status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0;
237 status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0;
238 status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0;
239 status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0;
240 if (s->state & SOCKET_SUSPEND)
241 status->CardState |= CS_EVENT_PM_SUSPEND;
242 if (!(s->state & SOCKET_PRESENT))
Dominik Brodowski3939c1ef2008-08-03 11:10:56 +0200243 return -ENODEV;
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200244
245 c = (p_dev) ? p_dev->function_config : NULL;
246
247 if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
248 (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
249 u_char reg;
250 if (c->CardValues & PRESENT_PIN_REPLACE) {
251 pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
252 status->CardState |=
253 (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0;
254 status->CardState |=
255 (reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0;
256 status->CardState |=
257 (reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0;
258 status->CardState |=
259 (reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0;
260 } else {
261 /* No PRR? Then assume we're always ready */
262 status->CardState |= CS_EVENT_READY_CHANGE;
263 }
264 if (c->CardValues & PRESENT_EXT_STATUS) {
265 pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
266 status->CardState |=
267 (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
268 }
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200269 return 0;
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200270 }
271 status->CardState |=
272 (val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0;
273 status->CardState |=
274 (val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0;
275 status->CardState |=
276 (val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0;
277 status->CardState |=
278 (val & SS_READY) ? CS_EVENT_READY_CHANGE : 0;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200279 return 0;
Dominik Brodowski4aeba012008-06-20 13:24:31 +0200280} /* pccard_get_status */
Dominik Brodowskic5023802008-06-19 19:02:52 +0200281
Roel Kluina0779322008-12-09 22:12:50 +0100282static int pccard_get_configuration_info(struct pcmcia_socket *s,
Dominik Brodowski64f34642008-08-02 17:00:46 +0200283 struct pcmcia_device *p_dev,
284 config_info_t *config)
285{
286 config_t *c;
287
288 if (!(s->state & SOCKET_PRESENT))
Dominik Brodowski3939c1ef2008-08-03 11:10:56 +0200289 return -ENODEV;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200290
291
292#ifdef CONFIG_CARDBUS
293 if (s->state & SOCKET_CARDBUS) {
294 memset(config, 0, sizeof(config_info_t));
295 config->Vcc = s->socket.Vcc;
296 config->Vpp1 = config->Vpp2 = s->socket.Vpp;
297 config->Option = s->cb_dev->subordinate->number;
298 if (s->state & SOCKET_CARDBUS_CONFIG) {
299 config->Attributes = CONF_VALID_CLIENT;
300 config->IntType = INT_CARDBUS;
301 config->AssignedIRQ = s->irq.AssignedIRQ;
302 if (config->AssignedIRQ)
303 config->Attributes |= CONF_ENABLE_IRQ;
304 if (s->io[0].res) {
305 config->BasePort1 = s->io[0].res->start;
306 config->NumPorts1 = s->io[0].res->end -
307 config->BasePort1 + 1;
308 }
309 }
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200310 return 0;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200311 }
312#endif
313
314 if (p_dev) {
315 c = p_dev->function_config;
316 config->Function = p_dev->func;
317 } else {
318 c = NULL;
319 config->Function = 0;
320 }
321
322 if ((c == NULL) || !(c->state & CONFIG_LOCKED)) {
323 config->Attributes = 0;
324 config->Vcc = s->socket.Vcc;
325 config->Vpp1 = config->Vpp2 = s->socket.Vpp;
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200326 return 0;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200327 }
328
329 config->Attributes = c->Attributes | CONF_VALID_CLIENT;
330 config->Vcc = s->socket.Vcc;
331 config->Vpp1 = config->Vpp2 = s->socket.Vpp;
332 config->IntType = c->IntType;
333 config->ConfigBase = c->ConfigBase;
334 config->Status = c->Status;
335 config->Pin = c->Pin;
336 config->Copy = c->Copy;
337 config->Option = c->Option;
338 config->ExtStatus = c->ExtStatus;
339 config->Present = config->CardValues = c->CardValues;
340 config->IRQAttributes = c->irq.Attributes;
341 config->AssignedIRQ = s->irq.AssignedIRQ;
342 config->BasePort1 = c->io.BasePort1;
343 config->NumPorts1 = c->io.NumPorts1;
344 config->Attributes1 = c->io.Attributes1;
345 config->BasePort2 = c->io.BasePort2;
346 config->NumPorts2 = c->io.NumPorts2;
347 config->Attributes2 = c->io.Attributes2;
348 config->IOAddrLines = c->io.IOAddrLines;
349
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200350 return 0;
Dominik Brodowski64f34642008-08-02 17:00:46 +0200351} /* pccard_get_configuration_info */
352
353
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700354/*======================================================================
355
356 These manage a ring buffer of events pending for one user process
357
358======================================================================*/
359
360
361static int queue_empty(user_info_t *user)
362{
363 return (user->event_head == user->event_tail);
364}
365
366static event_t get_queued_event(user_info_t *user)
367{
368 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
369 return user->event[user->event_tail];
370}
371
372static void queue_event(user_info_t *user, event_t event)
373{
374 user->event_head = (user->event_head+1) % MAX_EVENTS;
375 if (user->event_head == user->event_tail)
376 user->event_tail = (user->event_tail+1) % MAX_EVENTS;
377 user->event[user->event_head] = event;
378}
379
Dominik Brodowskidc109492005-06-27 16:28:50 -0700380void handle_event(struct pcmcia_socket *s, event_t event)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700381{
382 user_info_t *user;
383 for (user = s->user; user; user = user->next)
384 queue_event(user, event);
385 wake_up_interruptible(&s->queue);
386}
387
388
389/*======================================================================
390
391 bind_request() and bind_device() are merged by now. Register_client()
392 is called right at the end of bind_request(), during the driver's
393 ->attach() call. Individual descriptions:
394
395 bind_request() connects a socket to a particular client driver.
396 It looks up the specified device ID in the list of registered
397 drivers, binds it to the socket, and tries to create an instance
398 of the device. unbind_request() deletes a driver instance.
399
400 Bind_device() associates a device driver with a particular socket.
401 It is normally called by Driver Services after it has identified
402 a newly inserted card. An instance of that driver will then be
403 eligible to register as a client of this socket.
404
405 Register_client() uses the dev_info_t handle to match the
406 caller with a socket. The driver must have already been bound
407 to a socket with bind_device() -- in fact, bind_device()
408 allocates the client structure that will be used.
409
410======================================================================*/
411
Dominik Brodowskidc109492005-06-27 16:28:50 -0700412static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700413{
414 struct pcmcia_driver *p_drv;
415 struct pcmcia_device *p_dev;
416 int ret = 0;
417 unsigned long flags;
418
Dominik Brodowskidc109492005-06-27 16:28:50 -0700419 s = pcmcia_get_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700420 if (!s)
421 return -EINVAL;
422
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200423 pr_debug("bind_request(%d, '%s')\n", s->sock,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700424 (char *)bind_info->dev_info);
425
426 p_drv = get_pcmcia_driver(&bind_info->dev_info);
427 if (!p_drv) {
428 ret = -EINVAL;
429 goto err_put;
430 }
431
432 if (!try_module_get(p_drv->owner)) {
433 ret = -EINVAL;
434 goto err_put_driver;
435 }
436
437 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
438 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
439 if (p_dev->func == bind_info->function) {
440 if ((p_dev->dev.driver == &p_drv->drv)) {
441 if (p_dev->cardmgr) {
442 /* if there's already a device
443 * registered, and it was registered
444 * by userspace before, we need to
445 * return the "instance". */
446 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
Dominik Brodowskifd238232006-03-05 10:45:09 +0100447 bind_info->instance = p_dev;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700448 ret = -EBUSY;
449 goto err_put_module;
450 } else {
451 /* the correct driver managed to bind
452 * itself magically to the correct
453 * device. */
454 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
455 p_dev->cardmgr = p_drv;
456 ret = 0;
457 goto err_put_module;
458 }
459 } else if (!p_dev->dev.driver) {
460 /* there's already a device available where
461 * no device has been bound to yet. So we don't
462 * need to register a device! */
463 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
464 goto rescan;
465 }
466 }
467 }
468 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
469
470 p_dev = pcmcia_device_add(s, bind_info->function);
471 if (!p_dev) {
472 ret = -EIO;
473 goto err_put_module;
474 }
475
476rescan:
477 p_dev->cardmgr = p_drv;
478
479 /* if a driver is already running, we can abort */
480 if (p_dev->dev.driver)
481 goto err_put_module;
482
483 /*
484 * Prevent this racing with a card insertion.
485 */
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100486 mutex_lock(&s->skt_mutex);
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700487 ret = bus_rescan_devices(&pcmcia_bus_type);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100488 mutex_unlock(&s->skt_mutex);
Jeff Garzik4deb7c12006-10-20 14:44:23 -0700489 if (ret)
490 goto err_put_module;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700491
492 /* check whether the driver indeed matched. I don't care if this
493 * is racy or not, because it can only happen on cardmgr access
494 * paths...
495 */
496 if (!(p_dev->dev.driver == &p_drv->drv))
497 p_dev->cardmgr = NULL;
498
499 err_put_module:
500 module_put(p_drv->owner);
501 err_put_driver:
502 put_driver(&p_drv->drv);
503 err_put:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700504 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700505
506 return (ret);
507} /* bind_request */
508
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700509#ifdef CONFIG_CARDBUS
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700510
Dominik Brodowski33519dd2005-06-27 16:28:53 -0700511static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s)
512{
513 if (!s || !(s->state & SOCKET_CARDBUS))
514 return NULL;
515
516 return s->cb_dev->subordinate;
517}
518#endif
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700519
Dominik Brodowskidc109492005-06-27 16:28:50 -0700520static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700521{
522 dev_node_t *node;
523 struct pcmcia_device *p_dev;
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100524 struct pcmcia_driver *p_drv;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700525 unsigned long flags;
526 int ret = 0;
527
528#ifdef CONFIG_CARDBUS
529 /*
530 * Some unbelievably ugly code to associate the PCI cardbus
531 * device and its driver with the PCMCIA "bind" information.
532 */
533 {
534 struct pci_bus *bus;
535
Dominik Brodowskidc109492005-06-27 16:28:50 -0700536 bus = pcmcia_lookup_bus(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700537 if (bus) {
538 struct list_head *list;
539 struct pci_dev *dev = NULL;
540
541 list = bus->devices.next;
542 while (list != &bus->devices) {
543 struct pci_dev *pdev = pci_dev_b(list);
544 list = list->next;
545
546 if (first) {
547 dev = pdev;
548 break;
549 }
550
551 /* Try to handle "next" here some way? */
552 }
553 if (dev && dev->driver) {
554 strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN);
555 bind_info->major = 0;
556 bind_info->minor = 0;
557 bind_info->next = NULL;
558 return 0;
559 }
560 }
561 }
562#endif
563
564 spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
565 list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
566 if (p_dev->func == bind_info->function) {
567 p_dev = pcmcia_get_dev(p_dev);
568 if (!p_dev)
569 continue;
570 goto found;
571 }
572 }
573 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
574 return -ENODEV;
575
576 found:
577 spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
578
Dominik Brodowskie2d40962006-03-02 00:09:29 +0100579 p_drv = to_pcmcia_drv(p_dev->dev.driver);
580 if (p_drv && !p_dev->_locked) {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700581 ret = -EAGAIN;
582 goto err_put;
583 }
584
585 if (first)
Dominik Brodowskifd238232006-03-05 10:45:09 +0100586 node = p_dev->dev_node;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700587 else
Dominik Brodowskifd238232006-03-05 10:45:09 +0100588 for (node = p_dev->dev_node; node; node = node->next)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700589 if (node == bind_info->next)
590 break;
591 if (!node) {
592 ret = -ENODEV;
593 goto err_put;
594 }
595
596 strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN);
597 bind_info->major = node->major;
598 bind_info->minor = node->minor;
599 bind_info->next = node->next;
600
601 err_put:
602 pcmcia_put_dev(p_dev);
603 return (ret);
604} /* get_device_info */
605
606
607static int ds_open(struct inode *inode, struct file *file)
608{
609 socket_t i = iminor(inode);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700610 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700611 user_info_t *user;
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700612 static int warning_printed = 0;
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600613 int ret = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700614
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200615 pr_debug("ds_open(socket %d)\n", i);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700616
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600617 lock_kernel();
Dominik Brodowskidc109492005-06-27 16:28:50 -0700618 s = pcmcia_get_socket_by_nr(i);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600619 if (!s) {
620 ret = -ENODEV;
621 goto out;
622 }
Dominik Brodowskidc109492005-06-27 16:28:50 -0700623 s = pcmcia_get_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600624 if (!s) {
625 ret = -ENODEV;
626 goto out;
627 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700628
629 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700630 if (s->pcmcia_state.busy) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700631 pcmcia_put_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600632 ret = -EBUSY;
633 goto out;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700634 }
635 else
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700636 s->pcmcia_state.busy = 1;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700637 }
638
639 user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
640 if (!user) {
Dominik Brodowskidc109492005-06-27 16:28:50 -0700641 pcmcia_put_socket(s);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600642 ret = -ENOMEM;
643 goto out;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700644 }
645 user->event_tail = user->event_head = 0;
646 user->next = s->user;
647 user->user_magic = USER_MAGIC;
648 user->socket = s;
649 s->user = user;
650 file->private_data = user;
651
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700652 if (!warning_printed) {
653 printk(KERN_INFO "pcmcia: Detected deprecated PCMCIA ioctl "
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700654 "usage from process: %s.\n", current->comm);
Dominik Brodowskic352ec82005-09-13 01:25:03 -0700655 printk(KERN_INFO "pcmcia: This interface will soon be removed from "
656 "the kernel; please expect breakage unless you upgrade "
657 "to new tools.\n");
658 printk(KERN_INFO "pcmcia: see http://www.kernel.org/pub/linux/"
659 "utils/kernel/pcmcia/pcmcia.html for details.\n");
660 warning_printed = 1;
661 }
662
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700663 if (s->pcmcia_state.present)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700664 queue_event(user, CS_EVENT_CARD_INSERTION);
Jonathan Corbet0bec0bb2008-05-15 09:25:03 -0600665out:
666 unlock_kernel();
667 return ret;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700668} /* ds_open */
669
670/*====================================================================*/
671
672static int ds_release(struct inode *inode, struct file *file)
673{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700674 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700675 user_info_t *user, **link;
676
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200677 pr_debug("ds_release(socket %d)\n", iminor(inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700678
679 user = file->private_data;
680 if (CHECK_USER(user))
681 goto out;
682
683 s = user->socket;
684
685 /* Unlink user data structure */
686 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700687 s->pcmcia_state.busy = 0;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700688 }
689 file->private_data = NULL;
690 for (link = &s->user; *link; link = &(*link)->next)
691 if (*link == user) break;
692 if (link == NULL)
693 goto out;
694 *link = user->next;
695 user->user_magic = 0;
696 kfree(user);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700697 pcmcia_put_socket(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700698out:
699 return 0;
700} /* ds_release */
701
702/*====================================================================*/
703
704static ssize_t ds_read(struct file *file, char __user *buf,
705 size_t count, loff_t *ppos)
706{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700707 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700708 user_info_t *user;
709 int ret;
710
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200711 pr_debug("ds_read(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700712
713 if (count < 4)
714 return -EINVAL;
715
716 user = file->private_data;
717 if (CHECK_USER(user))
718 return -EIO;
719
720 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700721 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700722 return -EIO;
723
724 ret = wait_event_interruptible(s->queue, !queue_empty(user));
725 if (ret == 0)
726 ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;
727
728 return ret;
729} /* ds_read */
730
731/*====================================================================*/
732
733static ssize_t ds_write(struct file *file, const char __user *buf,
734 size_t count, loff_t *ppos)
735{
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200736 pr_debug("ds_write(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700737
738 if (count != 4)
739 return -EINVAL;
740 if ((file->f_flags & O_ACCMODE) == O_RDONLY)
741 return -EBADF;
742
743 return -EIO;
744} /* ds_write */
745
746/*====================================================================*/
747
748/* No kernel lock - fine */
749static u_int ds_poll(struct file *file, poll_table *wait)
750{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700751 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700752 user_info_t *user;
753
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200754 pr_debug("ds_poll(socket %d)\n", iminor(file->f_path.dentry->d_inode));
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700755
756 user = file->private_data;
757 if (CHECK_USER(user))
758 return POLLERR;
759 s = user->socket;
760 /*
761 * We don't check for a dead socket here since that
762 * will send cardmgr into an endless spin.
763 */
764 poll_wait(file, &s->queue, wait);
765 if (!queue_empty(user))
766 return POLLIN | POLLRDNORM;
767 return 0;
768} /* ds_poll */
769
770/*====================================================================*/
771
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700772static int ds_ioctl(struct inode * inode, struct file * file,
773 u_int cmd, u_long arg)
774{
Dominik Brodowskidc109492005-06-27 16:28:50 -0700775 struct pcmcia_socket *s;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700776 void __user *uarg = (char __user *)arg;
777 u_int size;
778 int ret, err;
779 ds_ioctl_arg_t *buf;
780 user_info_t *user;
781
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200782 pr_debug("ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700783
784 user = file->private_data;
785 if (CHECK_USER(user))
786 return -EIO;
787
788 s = user->socket;
Dominik Brodowskib5e43912005-06-27 16:28:50 -0700789 if (s->pcmcia_state.dead)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700790 return -EIO;
791
792 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
793 if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
794
795 /* Permission check */
796 if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
797 return -EPERM;
798
799 if (cmd & IOC_IN) {
800 if (!access_ok(VERIFY_READ, uarg, size)) {
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200801 pr_debug("ds_ioctl(): verify_read = %d\n", -EFAULT);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700802 return -EFAULT;
803 }
804 }
805 if (cmd & IOC_OUT) {
806 if (!access_ok(VERIFY_WRITE, uarg, size)) {
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200807 pr_debug("ds_ioctl(): verify_write = %d\n", -EFAULT);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700808 return -EFAULT;
809 }
810 }
811 buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL);
812 if (!buf)
813 return -ENOMEM;
814
815 err = ret = 0;
816
Dominik Brodowski93740742006-11-19 11:21:27 -0500817 if (cmd & IOC_IN) {
818 if (__copy_from_user((char *)buf, uarg, size)) {
819 err = -EFAULT;
820 goto free_out;
821 }
822 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700823
824 switch (cmd) {
825 case DS_ADJUST_RESOURCE_INFO:
826 ret = pcmcia_adjust_resource_info(&buf->adjust);
827 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700828 case DS_GET_CONFIGURATION_INFO:
829 if (buf->config.Function &&
Dominik Brodowskidc109492005-06-27 16:28:50 -0700830 (buf->config.Function >= s->functions))
Dominik Brodowski926c5402008-08-03 12:15:11 +0200831 ret = -EINVAL;
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100832 else {
833 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->config.Function);
Daniel Ritzf47ad212006-07-30 03:03:47 -0700834 ret = pccard_get_configuration_info(s, p_dev, &buf->config);
835 pcmcia_put_dev(p_dev);
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100836 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700837 break;
838 case DS_GET_FIRST_TUPLE:
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100839 mutex_lock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700840 pcmcia_validate_mem(s);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100841 mutex_unlock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700842 ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700843 break;
844 case DS_GET_NEXT_TUPLE:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700845 ret = pccard_get_next_tuple(s, BIND_FN_ALL, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700846 break;
847 case DS_GET_TUPLE_DATA:
848 buf->tuple.TupleData = buf->tuple_parse.data;
849 buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700850 ret = pccard_get_tuple_data(s, &buf->tuple);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700851 break;
852 case DS_PARSE_TUPLE:
853 buf->tuple.TupleData = buf->tuple_parse.data;
Dominik Brodowski2f3061e2008-08-31 15:50:33 +0200854 ret = pcmcia_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700855 break;
856 case DS_RESET_CARD:
Dominik Brodowski994917f2008-08-31 15:20:26 +0200857 ret = pcmcia_reset_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700858 break;
859 case DS_GET_STATUS:
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100860 if (buf->status.Function &&
861 (buf->status.Function >= s->functions))
Dominik Brodowski926c5402008-08-03 12:15:11 +0200862 ret = -EINVAL;
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100863 else {
864 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->status.Function);
Daniel Ritzf47ad212006-07-30 03:03:47 -0700865 ret = pccard_get_status(s, p_dev, &buf->status);
866 pcmcia_put_dev(p_dev);
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100867 }
868 break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700869 case DS_VALIDATE_CIS:
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100870 mutex_lock(&s->skt_mutex);
Dominik Brodowskidc109492005-06-27 16:28:50 -0700871 pcmcia_validate_mem(s);
Dominik Brodowski7fe908d2006-01-10 21:20:36 +0100872 mutex_unlock(&s->skt_mutex);
Dominik Brodowski84897fc2009-10-18 23:51:09 +0200873 ret = pccard_validate_cis(s, &buf->cisinfo.Chains);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700874 break;
875 case DS_SUSPEND_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700876 ret = pcmcia_suspend_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700877 break;
878 case DS_RESUME_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700879 ret = pcmcia_resume_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700880 break;
881 case DS_EJECT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700882 err = pcmcia_eject_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700883 break;
884 case DS_INSERT_CARD:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700885 err = pcmcia_insert_card(s);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700886 break;
887 case DS_ACCESS_CONFIGURATION_REGISTER:
888 if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
889 err = -EPERM;
890 goto free_out;
891 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100892
Dominik Brodowski926c5402008-08-03 12:15:11 +0200893 ret = -EINVAL;
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100894
895 if (!(buf->conf_reg.Function &&
896 (buf->conf_reg.Function >= s->functions))) {
897 struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->conf_reg.Function);
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700898 if (p_dev) {
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100899 ret = pcmcia_access_configuration_register(p_dev, &buf->conf_reg);
Benjamin Herrenschmidt73d58582006-05-15 09:43:53 -0700900 pcmcia_put_dev(p_dev);
901 }
Dominik Brodowski855cdf12006-01-10 20:48:59 +0100902 }
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700903 break;
904 case DS_GET_FIRST_REGION:
905 case DS_GET_NEXT_REGION:
906 case DS_BIND_MTD:
907 if (!capable(CAP_SYS_ADMIN)) {
908 err = -EPERM;
909 goto free_out;
910 } else {
Minchan Kima9c56952009-06-16 15:33:44 -0700911 printk_once(KERN_WARNING
912 "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n");
913 printk_once(KERN_WARNING "MTD handling any more.\n");
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700914 }
915 err = -EINVAL;
916 goto free_out;
917 break;
918 case DS_GET_FIRST_WINDOW:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700919 ret = pcmcia_get_window(s, &buf->win_info.handle, 0,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700920 &buf->win_info.window);
921 break;
922 case DS_GET_NEXT_WINDOW:
Dominik Brodowskidc109492005-06-27 16:28:50 -0700923 ret = pcmcia_get_window(s, &buf->win_info.handle,
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700924 buf->win_info.handle->index + 1, &buf->win_info.window);
925 break;
926 case DS_GET_MEM_PAGE:
927 ret = pcmcia_get_mem_page(buf->win_info.handle,
928 &buf->win_info.map);
929 break;
930 case DS_REPLACE_CIS:
Dominik Brodowski53efec92008-07-28 19:44:05 +0200931 ret = pcmcia_replace_cis(s, buf->cisdump.Data, buf->cisdump.Length);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700932 break;
933 case DS_BIND_REQUEST:
934 if (!capable(CAP_SYS_ADMIN)) {
935 err = -EPERM;
936 goto free_out;
937 }
938 err = bind_request(s, &buf->bind_info);
939 break;
940 case DS_GET_DEVICE_INFO:
941 err = get_device_info(s, &buf->bind_info, 1);
942 break;
943 case DS_GET_NEXT_DEVICE:
944 err = get_device_info(s, &buf->bind_info, 0);
945 break;
946 case DS_UNBIND_REQUEST:
947 err = 0;
948 break;
949 default:
950 err = -EINVAL;
951 }
952
Dominik Brodowski4c89e882008-08-03 10:07:45 +0200953 if ((err == 0) && (ret != 0)) {
Dominik Brodowskid50dbec2009-10-23 12:51:28 +0200954 pr_debug("ds_ioctl: ret = %d\n", ret);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700955 switch (ret) {
Dominik Brodowski610e2372008-08-03 11:58:53 +0200956 case -ENODEV:
957 case -EINVAL:
958 case -EBUSY:
959 case -ENOSYS:
960 err = ret;
961 break;
Dominik Brodowski610e2372008-08-03 11:58:53 +0200962 case -ENOMEM:
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700963 err = -ENOSPC; break;
Dominik Brodowski635d19b2008-08-03 11:47:29 +0200964 case -ENOSPC:
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700965 err = -ENODATA; break;
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700966 default:
967 err = -EIO; break;
968 }
969 }
970
971 if (cmd & IOC_OUT) {
972 if (__copy_to_user(uarg, (char *)buf, size))
973 err = -EFAULT;
974 }
975
976free_out:
977 kfree(buf);
978 return err;
979} /* ds_ioctl */
980
981/*====================================================================*/
982
Arjan van de Vend54b1fd2007-02-12 00:55:34 -0800983static const struct file_operations ds_fops = {
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700984 .owner = THIS_MODULE,
985 .open = ds_open,
986 .release = ds_release,
987 .ioctl = ds_ioctl,
988 .read = ds_read,
989 .write = ds_write,
990 .poll = ds_poll,
991};
992
993void __init pcmcia_setup_ioctl(void) {
994 int i;
995
996 /* Set up character device for user mode clients */
997 i = register_chrdev(0, "pcmcia", &ds_fops);
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -0700998 if (i < 0)
Dominik Brodowskie7a480d2005-06-27 16:28:47 -0700999 printk(KERN_NOTICE "unable to find a free device # for "
Dominik Brodowski1a8ceaf2005-06-27 16:29:00 -07001000 "Driver Services (error=%d)\n", i);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001001 else
1002 major_dev = i;
1003
1004#ifdef CONFIG_PROC_FS
akpm@linux-foundation.org97094dc2008-04-29 10:47:54 -07001005 proc_pccard = proc_mkdir("bus/pccard", NULL);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001006 if (proc_pccard)
Alexey Dobriyane6be4a82009-09-21 17:03:55 -07001007 proc_create("drivers", 0, proc_pccard, &pccard_drivers_proc_fops);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001008#endif
1009}
1010
1011
1012void __exit pcmcia_cleanup_ioctl(void) {
1013#ifdef CONFIG_PROC_FS
1014 if (proc_pccard) {
1015 remove_proc_entry("drivers", proc_pccard);
akpm@linux-foundation.org97094dc2008-04-29 10:47:54 -07001016 remove_proc_entry("bus/pccard", NULL);
Dominik Brodowskie7a480d2005-06-27 16:28:47 -07001017 }
1018#endif
1019 if (major_dev != -1)
1020 unregister_chrdev(major_dev, "pcmcia");
1021}