Commit 5716d415 authored by Dominik Brodowski's avatar Dominik Brodowski

pcmcia: remove obsolete ioctl

Signed-off-by: default avatarDominik Brodowski <linux@dominikbrodowski.net>
parent b37fa16e
......@@ -116,29 +116,6 @@ Who: Mauro Carvalho Chehab <mchehab@infradead.org>
---------------------------
What: PCMCIA control ioctl (needed for pcmcia-cs [cardmgr, cardctl])
When: 2.6.35/2.6.36
Files: drivers/pcmcia/: pcmcia_ioctl.c
Why: With the 16-bit PCMCIA subsystem now behaving (almost) like a
normal hotpluggable bus, and with it using the default kernel
infrastructure (hotplug, driver core, sysfs) keeping the PCMCIA
control ioctl needed by cardmgr and cardctl from pcmcia-cs is
unnecessary and potentially harmful (it does not provide for
proper locking), and makes further cleanups and integration of the
PCMCIA subsystem into the Linux kernel device driver model more
difficult. The features provided by cardmgr and cardctl are either
handled by the kernel itself now or are available in the new
pcmciautils package available at
http://kernel.org/pub/linux/utils/kernel/pcmcia/
For all architectures except ARM, the associated config symbol
has been removed from kernel 2.6.34; for ARM, it will be likely
be removed from kernel 2.6.35. The actual code will then likely
be removed from kernel 2.6.36.
Who: Dominik Brodowski <linux@dominikbrodowski.net>
---------------------------
What: sys_sysctl
When: September 2010
Option: CONFIG_SYSCTL_SYSCALL
......
......@@ -7,7 +7,6 @@ pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o
obj-$(CONFIG_PCCARD) += pcmcia_core.o
pcmcia-y += ds.o pcmcia_resource.o cistpl.o pcmcia_cis.o
pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o
obj-$(CONFIG_PCMCIA) += pcmcia.o
pcmcia_rsrc-y += rsrc_mgr.o
......
......@@ -60,14 +60,6 @@ struct pccard_resource_ops {
struct resource* (*find_mem) (unsigned long base, unsigned long num,
unsigned long align, int low,
struct pcmcia_socket *s);
int (*add_io) (struct pcmcia_socket *s,
unsigned int action,
unsigned long r_start,
unsigned long r_end);
int (*add_mem) (struct pcmcia_socket *s,
unsigned int action,
unsigned long r_start,
unsigned long r_end);
int (*init) (struct pcmcia_socket *s);
void (*exit) (struct pcmcia_socket *s);
};
......@@ -146,6 +138,8 @@ void pcmcia_put_socket(struct pcmcia_socket *skt);
/* ds.c */
extern struct bus_type pcmcia_bus_type;
struct pcmcia_device;
/* pcmcia_resource.c */
extern int pcmcia_release_configuration(struct pcmcia_device *p_dev);
extern int pcmcia_validate_mem(struct pcmcia_socket *s);
......@@ -188,34 +182,4 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function,
int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple);
#ifdef CONFIG_PCMCIA_IOCTL
/* ds.c */
extern struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev);
extern void pcmcia_put_dev(struct pcmcia_device *p_dev);
struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s,
unsigned int function);
/* pcmcia_ioctl.c */
extern void __init pcmcia_setup_ioctl(void);
extern void __exit pcmcia_cleanup_ioctl(void);
extern void handle_event(struct pcmcia_socket *s, event_t event);
extern int handle_request(struct pcmcia_socket *s, event_t event);
#else /* CONFIG_PCMCIA_IOCTL */
static inline void __init pcmcia_setup_ioctl(void) { return; }
static inline void __exit pcmcia_cleanup_ioctl(void) { return; }
static inline void handle_event(struct pcmcia_socket *s, event_t event)
{
return;
}
static inline int handle_request(struct pcmcia_socket *s, event_t event)
{
return 0;
}
#endif /* CONFIG_PCMCIA_IOCTL */
#endif /* _LINUX_CS_INTERNAL_H */
......@@ -213,7 +213,7 @@ EXPORT_SYMBOL(pcmcia_unregister_driver);
/* pcmcia_device handling */
struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev)
static struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev)
{
struct device *tmp_dev;
tmp_dev = get_device(&p_dev->dev);
......@@ -222,7 +222,7 @@ struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev)
return to_pcmcia_dev(tmp_dev);
}
void pcmcia_put_dev(struct pcmcia_device *p_dev)
static void pcmcia_put_dev(struct pcmcia_device *p_dev)
{
if (p_dev)
put_device(&p_dev->dev);
......@@ -477,7 +477,8 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
}
struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
static struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s,
unsigned int function)
{
struct pcmcia_device *p_dev, *tmp_dev;
int i;
......@@ -885,14 +886,6 @@ static int pcmcia_bus_match(struct device *dev, struct device_driver *drv)
}
mutex_unlock(&p_drv->dynids.lock);
#ifdef CONFIG_PCMCIA_IOCTL
/* matching by cardmgr */
if (p_dev->cardmgr == p_drv) {
dev_dbg(dev, "cardmgr matched to %s\n", drv->name);
return 1;
}
#endif
while (did && did->match_flags) {
dev_dbg(dev, "trying to match to %s\n", drv->name);
if (pcmcia_devmatch(p_dev, did)) {
......@@ -1245,7 +1238,6 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
case CS_EVENT_CARD_REMOVAL:
atomic_set(&skt->present, 0);
pcmcia_card_remove(skt, NULL);
handle_event(skt, event);
mutex_lock(&s->ops_mutex);
destroy_cis_cache(s);
pcmcia_cleanup_irq(s);
......@@ -1259,7 +1251,6 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
destroy_cis_cache(s); /* to be on the safe side... */
mutex_unlock(&s->ops_mutex);
pcmcia_card_add(skt);
handle_event(skt, event);
break;
case CS_EVENT_EJECTION_REQUEST:
......@@ -1280,14 +1271,12 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
ds_event(skt, CS_EVENT_CARD_INSERTION,
CS_EVENT_PRI_LOW);
}
handle_event(skt, event);
break;
case CS_EVENT_PM_SUSPEND:
case CS_EVENT_RESET_PHYSICAL:
case CS_EVENT_CARD_RESET:
default:
handle_event(skt, event);
break;
}
......@@ -1350,9 +1339,6 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev,
return ret;
}
#ifdef CONFIG_PCMCIA_IOCTL
init_waitqueue_head(&socket->queue);
#endif
INIT_LIST_HEAD(&socket->devices_list);
memset(&socket->pcmcia_state, 0, sizeof(u8));
socket->device_count = 0;
......@@ -1429,8 +1415,6 @@ static int __init init_pcmcia_bus(void)
return ret;
}
pcmcia_setup_ioctl();
return 0;
}
fs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that
......@@ -1439,8 +1423,6 @@ fs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that
static void __exit exit_pcmcia_bus(void)
{
pcmcia_cleanup_ioctl();
class_interface_unregister(&pcmcia_bus_interface);
bus_unregister(&pcmcia_bus_type);
......
/*
* pcmcia_ioctl.c -- ioctl interface for cardmgr and cardctl
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* (C) 1999 David A. Hinds
* (C) 2003 - 2004 Dominik Brodowski
*/
/*
* This file will go away soon.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/proc_fs.h>
#include <linux/poll.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/smp_lock.h>
#include <linux/workqueue.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include <pcmcia/ss.h>
#include "cs_internal.h"
static int major_dev = -1;
/* Device user information */
#define MAX_EVENTS 32
#define USER_MAGIC 0x7ea4
#define CHECK_USER(u) \
(((u) == NULL) || ((u)->user_magic != USER_MAGIC))
typedef struct user_info_t {
u_int user_magic;
int event_head, event_tail;
event_t event[MAX_EVENTS];
struct user_info_t *next;
struct pcmcia_socket *socket;
} user_info_t;
static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s,
unsigned int function)
{
struct pcmcia_device *p_dev = NULL;
mutex_lock(&s->ops_mutex);
list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
if (p_dev->func == function) {
mutex_unlock(&s->ops_mutex);
return pcmcia_get_dev(p_dev);
}
}
mutex_unlock(&s->ops_mutex);
return NULL;
}
/* backwards-compatible accessing of driver --- by name! */
static struct pcmcia_driver *get_pcmcia_driver(dev_info_t *dev_info)
{
struct device_driver *drv;
struct pcmcia_driver *p_drv;
drv = driver_find((char *) dev_info, &pcmcia_bus_type);
if (!drv)
return NULL;
p_drv = container_of(drv, struct pcmcia_driver, drv);
return p_drv;
}
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_pccard;
static int proc_read_drivers_callback(struct device_driver *driver, void *_m)
{
struct seq_file *m = _m;
struct pcmcia_driver *p_drv = container_of(driver,
struct pcmcia_driver, drv);
seq_printf(m, "%-24.24s 1 %d\n", p_drv->drv.name,
#ifdef CONFIG_MODULE_UNLOAD
(p_drv->owner) ? module_refcount(p_drv->owner) : 1
#else
1
#endif
);
return 0;
}
static int pccard_drivers_proc_show(struct seq_file *m, void *v)
{
return bus_for_each_drv(&pcmcia_bus_type, NULL,
m, proc_read_drivers_callback);
}
static int pccard_drivers_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, pccard_drivers_proc_show, NULL);
}
static const struct file_operations pccard_drivers_proc_fops = {
.owner = THIS_MODULE,
.open = pccard_drivers_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif
#ifdef CONFIG_PCMCIA_PROBE
static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
{
int irq;
u32 mask;
irq = adj->resource.irq.IRQ;
if ((irq < 0) || (irq > 15))
return -EINVAL;
if (adj->Action != REMOVE_MANAGED_RESOURCE)
return 0;
mask = 1 << irq;
if (!(s->irq_mask & mask))
return 0;
s->irq_mask &= ~mask;
return 0;
}
#else
static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj)
{
return 0;
}
#endif
static int pcmcia_adjust_resource_info(adjust_t *adj)
{
struct pcmcia_socket *s;
int ret = -ENOSYS;
down_read(&pcmcia_socket_list_rwsem);
list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
if (adj->Resource == RES_IRQ)
ret = adjust_irq(s, adj);
else if (s->resource_ops->add_io) {
unsigned long begin, end;
/* you can't use the old interface if the new
* one was used before */
mutex_lock(&s->ops_mutex);
if ((s->resource_setup_new) &&
!(s->resource_setup_old)) {
mutex_unlock(&s->ops_mutex);
continue;
} else if (!(s->resource_setup_old))
s->resource_setup_old = 1;
switch (adj->Resource) {
case RES_MEMORY_RANGE:
begin = adj->resource.memory.Base;
end = adj->resource.memory.Base + adj->resource.memory.Size - 1;
if (s->resource_ops->add_mem)
ret = s->resource_ops->add_mem(s, adj->Action, begin, end);
case RES_IO_RANGE:
begin = adj->resource.io.BasePort;
end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1;
if (s->resource_ops->add_io)
ret = s->resource_ops->add_io(s, adj->Action, begin, end);
}
if (!ret) {
/* as there's no way we know this is the
* last call to adjust_resource_info, we
* always need to assume this is the latest
* one... */
s->resource_setup_done = 1;
}
mutex_unlock(&s->ops_mutex);
}
}
up_read(&pcmcia_socket_list_rwsem);
return ret;
}
/** pcmcia_get_window
*/
static int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *wh_out,
window_handle_t wh, win_req_t *req)
{
pccard_mem_map *win;
window_handle_t w;
wh--;
if (!s || !(s->state & SOCKET_PRESENT))
return -ENODEV;
if (wh >= MAX_WIN)
return -EINVAL;
for (w = wh; w < MAX_WIN; w++)
if (s->state & SOCKET_WIN_REQ(w))
break;
if (w == MAX_WIN)
return -EINVAL;
win = &s->win[w];
req->Base = win->res->start;
req->Size = win->res->end - win->res->start + 1;
req->AccessSpeed = win->speed;
req->Attributes = 0;
if (win->flags & MAP_ATTRIB)
req->Attributes |= WIN_MEMORY_TYPE_AM;
if (win->flags & MAP_ACTIVE)
req->Attributes |= WIN_ENABLE;
if (win->flags & MAP_16BIT)
req->Attributes |= WIN_DATA_WIDTH_16;
if (win->flags & MAP_USE_WAIT)
req->Attributes |= WIN_USE_WAIT;
*wh_out = w + 1;
return 0;
} /* pcmcia_get_window */
/** pcmcia_get_mem_page
*
* Change the card address of an already open memory window.
*/
static int pcmcia_get_mem_page(struct pcmcia_socket *skt, window_handle_t wh,
memreq_t *req)
{
wh--;
if (wh >= MAX_WIN)
return -EINVAL;
req->Page = 0;
req->CardOffset = skt->win[wh].card_start;
return 0;
} /* pcmcia_get_mem_page */
/** pccard_get_status
*
* Get the current socket state bits. We don't support the latched
* SocketState yet: I haven't seen any point for it.
*/
static int pccard_get_status(struct pcmcia_socket *s,
struct pcmcia_device *p_dev,
cs_status_t *status)
{
config_t *c;
int val;
s->ops->get_status(s, &val);
status->CardState = status->SocketState = 0;
status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0;
status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0;
status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0;
status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0;
if (s->state & SOCKET_SUSPEND)
status->CardState |= CS_EVENT_PM_SUSPEND;
if (!(s->state & SOCKET_PRESENT))
return -ENODEV;
c = (p_dev) ? p_dev->function_config : NULL;
if ((c != NULL) && (c->state & CONFIG_LOCKED) &&
(c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) {
u_char reg;
if (c->CardValues & PRESENT_PIN_REPLACE) {
mutex_lock(&s->ops_mutex);
pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, &reg);
mutex_unlock(&s->ops_mutex);
status->CardState |=
(reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0;
status->CardState |=
(reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0;
status->CardState |=
(reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0;
status->CardState |=
(reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0;
} else {
/* No PRR? Then assume we're always ready */
status->CardState |= CS_EVENT_READY_CHANGE;
}
if (c->CardValues & PRESENT_EXT_STATUS) {
mutex_lock(&s->ops_mutex);
pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, &reg);
mutex_unlock(&s->ops_mutex);
status->CardState |=
(reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
}
return 0;
}
status->CardState |=
(val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0;
status->CardState |=
(val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0;
status->CardState |=
(val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0;
status->CardState |=
(val & SS_READY) ? CS_EVENT_READY_CHANGE : 0;
return 0;
} /* pccard_get_status */
static int pccard_get_configuration_info(struct pcmcia_socket *s,
struct pcmcia_device *p_dev,
config_info_t *config)
{
config_t *c;
if (!(s->state & SOCKET_PRESENT))
return -ENODEV;
#ifdef CONFIG_CARDBUS
if (s->state & SOCKET_CARDBUS) {
memset(config, 0, sizeof(config_info_t));
config->Vcc = s->socket.Vcc;
config->Vpp1 = config->Vpp2 = s->socket.Vpp;
config->Option = s->cb_dev->subordinate->number;
if (s->state & SOCKET_CARDBUS_CONFIG) {
config->Attributes = CONF_VALID_CLIENT;
config->IntType = INT_CARDBUS;
config->AssignedIRQ = s->pcmcia_irq;
if (config->AssignedIRQ)
config->Attributes |= CONF_ENABLE_IRQ;
if (s->io[0].res) {
config->BasePort1 = s->io[0].res->start;
config->NumPorts1 = s->io[0].res->end -
config->BasePort1 + 1;
}
}
return 0;
}
#endif
if (p_dev) {
c = p_dev->function_config;
config->Function = p_dev->func;
} else {
c = NULL;
config->Function = 0;
}
if ((c == NULL) || !(c->state & CONFIG_LOCKED)) {
config->Attributes = 0;
config->Vcc = s->socket.Vcc;
config->Vpp1 = config->Vpp2 = s->socket.Vpp;
return 0;
}
config->Attributes = c->Attributes | CONF_VALID_CLIENT;
config->Vcc = s->socket.Vcc;
config->Vpp1 = config->Vpp2 = s->socket.Vpp;
config->IntType = c->IntType;
config->ConfigBase = c->ConfigBase;
config->Status = c->Status;
config->Pin = c->Pin;
config->Copy = c->Copy;
config->Option = c->Option;
config->ExtStatus = c->ExtStatus;
config->Present = config->CardValues = c->CardValues;
config->IRQAttributes = c->irq.Attributes;
config->AssignedIRQ = s->pcmcia_irq;
config->BasePort1 = c->io.BasePort1;
config->NumPorts1 = c->io.NumPorts1;
config->Attributes1 = c->io.Attributes1;
config->BasePort2 = c->io.BasePort2;
config->NumPorts2 = c->io.NumPorts2;
config->Attributes2 = c->io.Attributes2;
config->IOAddrLines = c->io.IOAddrLines;
return 0;
} /* pccard_get_configuration_info */
/*======================================================================
These manage a ring buffer of events pending for one user process
======================================================================*/
static int queue_empty(user_info_t *user)
{
return (user->event_head == user->event_tail);
}
static event_t get_queued_event(user_info_t *user)
{
user->event_tail = (user->event_tail+1) % MAX_EVENTS;
return user->event[user->event_tail];
}
static void queue_event(user_info_t *user, event_t event)
{
user->event_head = (user->event_head+1) % MAX_EVENTS;
if (user->event_head == user->event_tail)
user->event_tail = (user->event_tail+1) % MAX_EVENTS;
user->event[user->event_head] = event;
}
void handle_event(struct pcmcia_socket *s, event_t event)
{
user_info_t *user;
for (user = s->user; user; user = user->next)
queue_event(user, event);
wake_up_interruptible(&s->queue);
}
/*======================================================================
bind_request() and bind_device() are merged by now. Register_client()
is called right at the end of bind_request(), during the driver's
->attach() call. Individual descriptions:
bind_request() connects a socket to a particular client driver.
It looks up the specified device ID in the list of registered
drivers, binds it to the socket, and tries to create an instance
of the device. unbind_request() deletes a driver instance.
Bind_device() associates a device driver with a particular socket.
It is normally called by Driver Services after it has identified
a newly inserted card. An instance of that driver will then be
eligible to register as a client of this socket.
Register_client() uses the dev_info_t handle to match the
caller with a socket. The driver must have already been bound
to a socket with bind_device() -- in fact, bind_device()
allocates the client structure that will be used.
======================================================================*/
static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
{
struct pcmcia_driver *p_drv;
struct pcmcia_device *p_dev;
int ret = 0;
s = pcmcia_get_socket(s);
if (!s)
return -EINVAL;
pr_debug("bind_request(%d, '%s')\n", s->sock,
(char *)bind_info->dev_info);
p_drv = get_pcmcia_driver(&bind_info->dev_info);
if (!p_drv) {
ret = -EINVAL;
goto err_put;
}
if (!try_module_get(p_drv->owner)) {
ret = -EINVAL;
goto err_put_driver;
}
mutex_lock(&s->ops_mutex);
list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
if (p_dev->func == bind_info->function) {
if ((p_dev->dev.driver == &p_drv->drv)) {
if (p_dev->cardmgr) {
/* if there's already a device
* registered, and it was registered
* by userspace before, we need to
* return the "instance". */
mutex_unlock(&s->ops_mutex);
bind_info->instance = p_dev;
ret = -EBUSY;
goto err_put_module;
} else {
/* the correct driver managed to bind
* itself magically to the correct
* device. */
mutex_unlock(&s->ops_mutex);
p_dev->cardmgr = p_drv;
ret = 0;
goto err_put_module;
}
} else if (!p_dev->dev.driver) {
/* there's already a device available where
* no device has been bound to yet. So we don't
* need to register a device! */
mutex_unlock(&s->ops_mutex);
goto rescan;
}
}
}
mutex_unlock(&s->ops_mutex);
p_dev = pcmcia_device_add(s, bind_info->function);
if (!p_dev) {
ret = -EIO;
goto err_put_module;
}
rescan:
p_dev->cardmgr = p_drv;
/* if a driver is already running, we can abort */
if (p_dev->dev.driver)
goto err_put_module;
/*
* Prevent this racing with a card insertion.
*/
mutex_lock(&s->skt_mutex);
ret = bus_rescan_devices(&pcmcia_bus_type);
mutex_unlock(&s->skt_mutex);
if (ret)
goto err_put_module;
/* check whether the driver indeed matched. I don't care if this
* is racy or not, because it can only happen on cardmgr access
* paths...
*/
if (!(p_dev->dev.driver == &p_drv->drv))
p_dev->cardmgr = NULL;
err_put_module:
module_put(p_drv->owner);
err_put_driver:
put_driver(&p_drv->drv);
err_put:
pcmcia_put_socket(s);
return ret;
} /* bind_request */
#ifdef CONFIG_CARDBUS
static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s)
{
if (!s || !(s->state & SOCKET_CARDBUS))
return NULL;
return s->cb_dev->subordinate;
}
#endif
static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first)
{
struct pcmcia_device *p_dev;
struct pcmcia_driver *p_drv;
int ret = 0;
#ifdef CONFIG_CARDBUS
/*
* Some unbelievably ugly code to associate the PCI cardbus
* device and its driver with the PCMCIA "bind" information.
*/
{
struct pci_bus *bus;
bus = pcmcia_lookup_bus(s);
if (bus) {
struct list_head *list;
struct pci_dev *dev = NULL;
list = bus->devices.next;
while (list != &bus->devices) {
struct pci_dev *pdev = pci_dev_b(list);
list = list->next;
if (first) {
dev = pdev;
break;
}
/* Try to handle "next" here some way? */
}
if (dev && dev->driver) {
strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN);
bind_info->major = 0;
bind_info->minor = 0;
bind_info->next = NULL;
return 0;
}
}
}
#endif
mutex_lock(&s->ops_mutex);
list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
if (p_dev->func == bind_info->function) {
p_dev = pcmcia_get_dev(p_dev);
if (!p_dev)
continue;
goto found;
}
}
mutex_unlock(&s->ops_mutex);
return -ENODEV;
found:
mutex_unlock(&s->ops_mutex);
p_drv = to_pcmcia_drv(p_dev->dev.driver);
if (p_drv && !p_dev->_locked) {
ret = -EAGAIN;
goto err_put;
}
if (!first) {
ret = -ENODEV;
goto err_put;
}
strlcpy(bind_info->name, dev_name(&p_dev->dev), DEV_NAME_LEN);
bind_info->next = NULL;
err_put:
pcmcia_put_dev(p_dev);
return ret;
} /* get_device_info */
static int ds_open(struct inode *inode, struct file *file)
{
socket_t i = iminor(inode);
struct pcmcia_socket *s;
user_info_t *user;
static int warning_printed;
int ret = 0;
pr_debug("ds_open(socket %d)\n", i);
lock_kernel();
s = pcmcia_get_socket_by_nr(i);
if (!s) {
ret = -ENODEV;
goto out;
}
s = pcmcia_get_socket(s);
if (!s) {
ret = -ENODEV;
goto out;
}
if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
if (s->pcmcia_state.busy) {
pcmcia_put_socket(s);
ret = -EBUSY;
goto out;
}
else
s->pcmcia_state.busy = 1;
}
user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
if (!user) {
pcmcia_put_socket(s);
ret = -ENOMEM;
goto out;
}
user->event_tail = user->event_head = 0;
user->next = s->user;
user->user_magic = USER_MAGIC;
user->socket = s;
s->user = user;
file->private_data = user;
if (!warning_printed) {
printk(KERN_INFO "pcmcia: Detected deprecated PCMCIA ioctl "
"usage from process: %s.\n", current->comm);
printk(KERN_INFO "pcmcia: This interface will soon be removed from "
"the kernel; please expect breakage unless you upgrade "
"to new tools.\n");
printk(KERN_INFO "pcmcia: see http://www.kernel.org/pub/linux/"
"utils/kernel/pcmcia/pcmcia.html for details.\n");
warning_printed = 1;
}
if (atomic_read(&s->present))
queue_event(user, CS_EVENT_CARD_INSERTION);
out:
unlock_kernel();
return ret;
} /* ds_open */
/*====================================================================*/
static int ds_release(struct inode *inode, struct file *file)
{
struct pcmcia_socket *s;
user_info_t *user, **link;
pr_debug("ds_release(socket %d)\n", iminor(inode));
user = file->private_data;
if (CHECK_USER(user))
goto out;
s = user->socket;
/* Unlink user data structure */
if ((file->f_flags & O_ACCMODE) != O_RDONLY)
s->pcmcia_state.busy = 0;
file->private_data = NULL;
for (link = &s->user; *link; link = &(*link)->next)
if (*link == user)
break;
if (link == NULL)
goto out;
*link = user->next;
user->user_magic = 0;
kfree(user);
pcmcia_put_socket(s);
out:
return 0;
} /* ds_release */
/*====================================================================*/
static ssize_t ds_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct pcmcia_socket *s;
user_info_t *user;
int ret;
pr_debug("ds_read(socket %d)\n", iminor(file->f_path.dentry->d_inode));
if (count < 4)
return -EINVAL;
user = file->private_data;
if (CHECK_USER(user))
return -EIO;
s = user->socket;
ret = wait_event_interruptible(s->queue, !queue_empty(user));
if (ret == 0)
ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4;
return ret;
} /* ds_read */
/*====================================================================*/
static ssize_t ds_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
pr_debug("ds_write(socket %d)\n", iminor(file->f_path.dentry->d_inode));
if (count != 4)
return -EINVAL;
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return -EBADF;
return -EIO;
} /* ds_write */
/*====================================================================*/
/* No kernel lock - fine */
static u_int ds_poll(struct file *file, poll_table *wait)
{
struct pcmcia_socket *s;
user_info_t *user;
pr_debug("ds_poll(socket %d)\n", iminor(file->f_path.dentry->d_inode));
user = file->private_data;
if (CHECK_USER(user))
return POLLERR;
s = user->socket;
/*
* We don't check for a dead socket here since that
* will send cardmgr into an endless spin.
*/
poll_wait(file, &s->queue, wait);
if (!queue_empty(user))
return POLLIN | POLLRDNORM;
return 0;
} /* ds_poll */
/*====================================================================*/
static int ds_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct pcmcia_socket *s;
void __user *uarg = (char __user *)arg;
u_int size;
int ret, err;
ds_ioctl_arg_t *buf;
user_info_t *user;
pr_debug("ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg);
user = file->private_data;
if (CHECK_USER(user))
return -EIO;
s = user->socket;
size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
if (size > sizeof(ds_ioctl_arg_t))
return -EINVAL;
/* Permission check */
if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN))
return -EPERM;
if (cmd & IOC_IN) {
if (!access_ok(VERIFY_READ, uarg, size)) {
pr_debug("ds_ioctl(): verify_read = %d\n", -EFAULT);
return -EFAULT;
}
}
if (cmd & IOC_OUT) {
if (!access_ok(VERIFY_WRITE, uarg, size)) {
pr_debug("ds_ioctl(): verify_write = %d\n", -EFAULT);
return -EFAULT;
}
}
buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL);
if (!buf)
return -ENOMEM;
err = ret = 0;
if (cmd & IOC_IN) {
if (__copy_from_user((char *)buf, uarg, size)) {
err = -EFAULT;
goto free_out;
}
}
switch (cmd) {
case DS_ADJUST_RESOURCE_INFO:
ret = pcmcia_adjust_resource_info(&buf->adjust);
break;
case DS_GET_CONFIGURATION_INFO:
if (buf->config.Function &&
(buf->config.Function >= s->functions))
ret = -EINVAL;
else {
struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->config.Function);
ret = pccard_get_configuration_info(s, p_dev, &buf->config);
pcmcia_put_dev(p_dev);
}
break;
case DS_GET_FIRST_TUPLE:
mutex_lock(&s->skt_mutex);
pcmcia_validate_mem(s);
mutex_unlock(&s->skt_mutex);
ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple);
break;
case DS_GET_NEXT_TUPLE:
ret = pccard_get_next_tuple(s, BIND_FN_ALL, &buf->tuple);
break;
case DS_GET_TUPLE_DATA:
buf->tuple.TupleData = buf->tuple_parse.data;
buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data);
ret = pccard_get_tuple_data(s, &buf->tuple);
break;
case DS_PARSE_TUPLE:
buf->tuple.TupleData = buf->tuple_parse.data;
ret = pcmcia_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
break;
case DS_RESET_CARD:
ret = pcmcia_reset_card(s);
break;
case DS_GET_STATUS:
if (buf->status.Function &&
(buf->status.Function >= s->functions))
ret = -EINVAL;
else {
struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->status.Function);
ret = pccard_get_status(s, p_dev, &buf->status);
pcmcia_put_dev(p_dev);
}
break;
case DS_VALIDATE_CIS:
mutex_lock(&s->skt_mutex);
pcmcia_validate_mem(s);
mutex_unlock(&s->skt_mutex);
ret = pccard_validate_cis(s, &buf->cisinfo.Chains);
break;
case DS_SUSPEND_CARD:
pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND);
break;
case DS_RESUME_CARD:
pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME);
break;
case DS_EJECT_CARD:
pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT);
break;
case DS_INSERT_CARD:
pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT);
break;
case DS_ACCESS_CONFIGURATION_REGISTER:
if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
err = -EPERM;
goto free_out;
}
ret = -EINVAL;
if (!(buf->conf_reg.Function &&
(buf->conf_reg.Function >= s->functions))) {
struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->conf_reg.Function);
if (p_dev) {
ret = pcmcia_access_configuration_register(p_dev, &buf->conf_reg);
pcmcia_put_dev(p_dev);
}
}
break;
case DS_GET_FIRST_REGION:
case DS_GET_NEXT_REGION:
case DS_BIND_MTD:
if (!capable(CAP_SYS_ADMIN)) {
err = -EPERM;
goto free_out;
} else {
printk_once(KERN_WARNING
"2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n");
printk_once(KERN_WARNING "MTD handling any more.\n");
}
err = -EINVAL;
goto free_out;
break;
case DS_GET_FIRST_WINDOW:
ret = pcmcia_get_window(s, &buf->win_info.handle, 1,
&buf->win_info.window);
break;
case DS_GET_NEXT_WINDOW:
ret = pcmcia_get_window(s, &buf->win_info.handle,
buf->win_info.handle + 1, &buf->win_info.window);
break;
case DS_GET_MEM_PAGE:
ret = pcmcia_get_mem_page(s, buf->win_info.handle,
&buf->win_info.map);
break;
case DS_REPLACE_CIS:
ret = pcmcia_replace_cis(s, buf->cisdump.Data, buf->cisdump.Length);
break;
case DS_BIND_REQUEST:
if (!capable(CAP_SYS_ADMIN)) {
err = -EPERM;
goto free_out;
}
err = bind_request(s, &buf->bind_info);
break;
case DS_GET_DEVICE_INFO:
err = get_device_info(s, &buf->bind_info, 1);
break;
case DS_GET_NEXT_DEVICE:
err = get_device_info(s, &buf->bind_info, 0);
break;
case DS_UNBIND_REQUEST:
err = 0;
break;
default:
err = -EINVAL;
}
if ((err == 0) && (ret != 0)) {
pr_debug("ds_ioctl: ret = %d\n", ret);
switch (ret) {
case -ENODEV:
case -EINVAL:
case -EBUSY:
case -ENOSYS:
err = ret;
break;
case -ENOMEM:
err = -ENOSPC; break;
case -ENOSPC:
err = -ENODATA; break;
default:
err = -EIO; break;
}
}
if (cmd & IOC_OUT) {
if (__copy_to_user(uarg, (char *)buf, size))
err = -EFAULT;
}
free_out:
kfree(buf);
return err;
} /* ds_ioctl */
static long ds_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
lock_kernel();
ret = ds_ioctl(file, cmd, arg);
unlock_kernel();
return ret;
}
/*====================================================================*/
static const struct file_operations ds_fops = {
.owner = THIS_MODULE,
.open = ds_open,
.release = ds_release,
.unlocked_ioctl = ds_unlocked_ioctl,
.read = ds_read,
.write = ds_write,
.poll = ds_poll,
};
void __init pcmcia_setup_ioctl(void)
{
int i;
/* Set up character device for user mode clients */
i = register_chrdev(0, "pcmcia", &ds_fops);
if (i < 0)
printk(KERN_NOTICE "unable to find a free device # for "
"Driver Services (error=%d)\n", i);
else
major_dev = i;
#ifdef CONFIG_PROC_FS
proc_pccard = proc_mkdir("bus/pccard", NULL);
if (proc_pccard)
proc_create("drivers", 0, proc_pccard, &pccard_drivers_proc_fops);
#endif
}
void __exit pcmcia_cleanup_ioctl(void)
{
#ifdef CONFIG_PROC_FS
if (proc_pccard) {
remove_proc_entry("drivers", proc_pccard);
remove_proc_entry("bus/pccard", NULL);
}
#endif
if (major_dev != -1)
unregister_chrdev(major_dev, "pcmcia");
}
......@@ -164,8 +164,6 @@ struct pccard_resource_ops pccard_iodyn_ops = {
.validate_mem = NULL,
.find_io = iodyn_find_io,
.find_mem = NULL,
.add_io = NULL,
.add_mem = NULL,
.init = static_init,
.exit = NULL,
};
......
......@@ -62,8 +62,6 @@ struct pccard_resource_ops pccard_static_ops = {
.validate_mem = NULL,
.find_io = static_find_io,
.find_mem = NULL,
.add_io = NULL,
.add_mem = NULL,
.init = static_init,
.exit = NULL,
};
......
......@@ -1055,8 +1055,6 @@ struct pccard_resource_ops pccard_nonstatic_ops = {
.validate_mem = pcmcia_nonstatic_validate_mem,
.find_io = nonstatic_find_io,
.find_mem = nonstatic_find_mem_region,
.add_io = adjust_io,
.add_mem = adjust_memory,
.init = nonstatic_init,
.exit = nonstatic_release_resource_db,
};
......
......@@ -117,11 +117,6 @@ struct pcmcia_device {
u64 dma_mask;
struct device dev;
#ifdef CONFIG_PCMCIA_IOCTL
/* device driver wanted by cardmgr */
struct pcmcia_driver *cardmgr;
#endif
/* data private to drivers */
void *priv;
};
......@@ -211,208 +206,4 @@ void pcmcia_disable_device(struct pcmcia_device *p_dev);
#endif /* __KERNEL__ */
/* Below, there are only definitions which are used by
* - the PCMCIA ioctl
* - deprecated PCMCIA userspace tools only
*
* here be dragons ... here be dragons ... here be dragons ... here be drag
*/
#if defined(CONFIG_PCMCIA_IOCTL) || !defined(__KERNEL__)
#if defined(__arm__) || defined(__mips__) || defined(__avr32__) || \
defined(__bfin__)
/* This (ioaddr_t) is exposed to userspace & hence cannot be changed. */
typedef u_int ioaddr_t;
#else
typedef u_short ioaddr_t;
#endif
/* for AdjustResourceInfo */
typedef struct adjust_t {
u_int Action;
u_int Resource;
u_int Attributes;
union {
struct memory {
u_long Base;
u_long Size;
} memory;
struct io {
ioaddr_t BasePort;
ioaddr_t NumPorts;
u_int IOAddrLines;
} io;
struct irq {
u_int IRQ;
} irq;
} resource;
} adjust_t;
/* Action field */
#define REMOVE_MANAGED_RESOURCE 1
#define ADD_MANAGED_RESOURCE 2
#define GET_FIRST_MANAGED_RESOURCE 3
#define GET_NEXT_MANAGED_RESOURCE 4
/* Resource field */
#define RES_MEMORY_RANGE 1
#define RES_IO_RANGE 2
#define RES_IRQ 3
/* Attribute field */
#define RES_IRQ_TYPE 0x03
#define RES_IRQ_TYPE_EXCLUSIVE 0
#define RES_IRQ_TYPE_TIME 1
#define RES_IRQ_TYPE_DYNAMIC 2
#define RES_IRQ_CSC 0x04
#define RES_SHARED 0x08
#define RES_RESERVED 0x10
#define RES_ALLOCATED 0x20
#define RES_REMOVED 0x40
typedef struct tuple_parse_t {
tuple_t tuple;
cisdata_t data[255];
cisparse_t parse;
} tuple_parse_t;
typedef struct win_info_t {
window_handle_t handle;
win_req_t window;
memreq_t map;
} win_info_t;
typedef struct bind_info_t {
dev_info_t dev_info;
u_char function;
struct pcmcia_device *instance;
char name[DEV_NAME_LEN];
u_short major, minor;
void *next;
} bind_info_t;
typedef struct mtd_info_t {
dev_info_t dev_info;
u_int Attributes;
u_int CardOffset;
} mtd_info_t;
typedef struct region_info_t {
u_int Attributes;
u_int CardOffset;
u_int RegionSize;
u_int AccessSpeed;
u_int BlockSize;
u_int PartMultiple;
u_char JedecMfr, JedecInfo;
memory_handle_t next;
} region_info_t;
#define REGION_TYPE 0x0001
#define REGION_TYPE_CM 0x0000
#define REGION_TYPE_AM 0x0001
#define REGION_PREFETCH 0x0008
#define REGION_CACHEABLE 0x0010
#define REGION_BAR_MASK 0xe000
#define REGION_BAR_SHIFT 13
/* For ReplaceCIS */
typedef struct cisdump_t {
u_int Length;
cisdata_t Data[CISTPL_MAX_CIS_SIZE];
} cisdump_t;
/* for GetConfigurationInfo */
typedef struct config_info_t {
u_char Function;
u_int Attributes;
u_int Vcc, Vpp1, Vpp2;
u_int IntType;
u_int ConfigBase;
u_char Status, Pin, Copy, Option, ExtStatus;
u_int Present;
u_int CardValues;
u_int AssignedIRQ;
u_int IRQAttributes;
ioaddr_t BasePort1;
ioaddr_t NumPorts1;
u_int Attributes1;
ioaddr_t BasePort2;
ioaddr_t NumPorts2;
u_int Attributes2;
u_int IOAddrLines;
} config_info_t;
/* For ValidateCIS */
typedef struct cisinfo_t {
u_int Chains;
} cisinfo_t;
typedef struct cs_status_t {
u_char Function;
event_t CardState;
event_t SocketState;
} cs_status_t;
typedef union ds_ioctl_arg_t {
adjust_t adjust;
config_info_t config;
tuple_t tuple;
tuple_parse_t tuple_parse;
client_req_t client_req;
cs_status_t status;
conf_reg_t conf_reg;
cisinfo_t cisinfo;
region_info_t region;
bind_info_t bind_info;
mtd_info_t mtd_info;
win_info_t win_info;
cisdump_t cisdump;
} ds_ioctl_arg_t;
#define DS_ADJUST_RESOURCE_INFO _IOWR('d', 2, adjust_t)
#define DS_GET_CONFIGURATION_INFO _IOWR('d', 3, config_info_t)
#define DS_GET_FIRST_TUPLE _IOWR('d', 4, tuple_t)
#define DS_GET_NEXT_TUPLE _IOWR('d', 5, tuple_t)
#define DS_GET_TUPLE_DATA _IOWR('d', 6, tuple_parse_t)
#define DS_PARSE_TUPLE _IOWR('d', 7, tuple_parse_t)
#define DS_RESET_CARD _IO ('d', 8)
#define DS_GET_STATUS _IOWR('d', 9, cs_status_t)
#define DS_ACCESS_CONFIGURATION_REGISTER _IOWR('d', 10, conf_reg_t)
#define DS_VALIDATE_CIS _IOR ('d', 11, cisinfo_t)
#define DS_SUSPEND_CARD _IO ('d', 12)
#define DS_RESUME_CARD _IO ('d', 13)
#define DS_EJECT_CARD _IO ('d', 14)
#define DS_INSERT_CARD _IO ('d', 15)
#define DS_GET_FIRST_REGION _IOWR('d', 16, region_info_t)
#define DS_GET_NEXT_REGION _IOWR('d', 17, region_info_t)
#define DS_REPLACE_CIS _IOWR('d', 18, cisdump_t)
#define DS_GET_FIRST_WINDOW _IOR ('d', 19, win_info_t)
#define DS_GET_NEXT_WINDOW _IOWR('d', 20, win_info_t)
#define DS_GET_MEM_PAGE _IOWR('d', 21, win_info_t)
#define DS_BIND_REQUEST _IOWR('d', 60, bind_info_t)
#define DS_GET_DEVICE_INFO _IOWR('d', 61, bind_info_t)
#define DS_GET_NEXT_DEVICE _IOWR('d', 62, bind_info_t)
#define DS_UNBIND_REQUEST _IOW ('d', 63, bind_info_t)
#define DS_BIND_MTD _IOWR('d', 64, mtd_info_t)
/* used in userspace only */
#define CS_IN_USE 0x1e
#define INFO_MASTER_CLIENT 0x01
#define INFO_IO_CLIENT 0x02
#define INFO_MTD_CLIENT 0x04
#define INFO_MEM_CLIENT 0x08
#define MAX_NUM_CLIENTS 3
#define INFO_CARD_SHARE 0x10
#define INFO_CARD_EXCL 0x20
#endif /* !defined(__KERNEL__) || defined(CONFIG_PCMCIA_IOCTL) */
#endif /* _LINUX_DS_H */
......@@ -220,12 +220,10 @@ struct pcmcia_socket {
/* 16-bit state: */
struct {
/* "master" ioctl is used */
u8 busy:1;
/* the PCMCIA card consists of two pseudo devices */
u8 has_pfc:1;
u8 reserved:6;
u8 reserved:7;
} pcmcia_state;
/* non-zero if PCMCIA card is present */
......@@ -234,10 +232,6 @@ struct pcmcia_socket {
/* IRQ to be used by PCMCIA devices. May not be IRQ 0. */
unsigned int pcmcia_irq;
#ifdef CONFIG_PCMCIA_IOCTL
struct user_info_t *user;
wait_queue_head_t queue;
#endif /* CONFIG_PCMCIA_IOCTL */
#endif /* CONFIG_PCMCIA */
/* socket device */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment