diff options
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r-- | drivers/pcmcia/Kconfig | 43 | ||||
-rw-r--r-- | drivers/pcmcia/Makefile | 3 | ||||
-rw-r--r-- | drivers/pcmcia/cistpl.c | 28 | ||||
-rw-r--r-- | drivers/pcmcia/cs.c | 1164 | ||||
-rw-r--r-- | drivers/pcmcia/cs_internal.h | 13 | ||||
-rw-r--r-- | drivers/pcmcia/ds.c | 1255 | ||||
-rw-r--r-- | drivers/pcmcia/ds_internal.h | 21 | ||||
-rw-r--r-- | drivers/pcmcia/i82365.c | 23 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_compat.c | 34 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_ioctl.c | 786 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_resource.c | 998 | ||||
-rw-r--r-- | drivers/pcmcia/rsrc_mgr.c | 11 | ||||
-rw-r--r-- | drivers/pcmcia/rsrc_nonstatic.c | 170 | ||||
-rw-r--r-- | drivers/pcmcia/socket_sysfs.c | 166 | ||||
-rw-r--r-- | drivers/pcmcia/yenta_socket.c | 6 |
15 files changed, 2677 insertions, 2044 deletions
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 14e4124e152..52ea3459436 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -14,8 +14,8 @@ config PCCARD Say Y here if you want to attach PCMCIA- or PC-cards to your Linux computer. These are credit-card size devices such as network cards, modems or hard drives often used with laptops computers. There are - actually two varieties of these cards: the older 16 bit PCMCIA cards - and the newer 32 bit CardBus cards. + actually two varieties of these cards: 16 bit PCMCIA and 32 bit + CardBus cards. To compile this driver as modules, choose M here: the module will be called pcmcia_core. @@ -42,22 +42,51 @@ config PCMCIA_DEBUG config PCMCIA tristate "16-bit PCMCIA support" + select CRC32 default y ---help--- This option enables support for 16-bit PCMCIA cards. Most older PC-cards are such 16-bit PCMCIA cards, so unless you know you're only using 32-bit CardBus cards, say Y or M here. - To use 16-bit PCMCIA cards, you will need supporting software from - David Hinds' pcmcia-cs package (see the file <file:Documentation/Changes> - for location). Please also read the PCMCIA-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. + To use 16-bit PCMCIA cards, you will need supporting software in + most cases. (see the file <file:Documentation/Changes> for + location and details). To compile this driver as modules, choose M here: the module will be called pcmcia. If unsure, say Y. +config PCMCIA_LOAD_CIS + bool "Load CIS updates from userspace (EXPERIMENTAL)" + depends on PCMCIA && EXPERIMENTAL + select FW_LOADER + default y + help + Some PCMCIA cards require an updated Card Information Structure (CIS) + to be loaded from userspace to work correctly. If you say Y here, + and your userspace is arranged correctly, this will be loaded + automatically using the in-kernel firmware loader and the hotplug + subsystem, instead of relying on cardmgr from pcmcia-cs to do so. + + If unsure, say Y. + +config PCMCIA_IOCTL + bool + depends on PCMCIA + default y + help + If you say Y here, the deprecated ioctl interface to the PCMCIA + subsystem will be built. It is needed by cardmgr and cardctl + (pcmcia-cs) to function properly. + + If you do not use the new pcmciautils package, and have a + yenta, Cirrus PD6729, i82092, i82365 or tcic compatible bridge, + you need to say Y here to be able to use 16-bit PCMCIA cards. + + If unsure, say Y. + config CARDBUS bool "32-bit CardBus support" depends on PCI @@ -77,8 +106,6 @@ comment "PC-card bridges" config YENTA tristate "CardBus yenta-compatible bridge support" - depends on PCI -#fixme: remove dependendcy on CARDBUS depends on CARDBUS select PCCARD_NONSTATIC ---help--- diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 50c29361bc5..ef694c74dfb 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -10,7 +10,8 @@ pcmcia_core-y += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o obj-$(CONFIG_PCCARD) += pcmcia_core.o -pcmcia-y += ds.o pcmcia_compat.o +pcmcia-y += ds.o pcmcia_compat.o pcmcia_resource.o +pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o obj-$(CONFIG_PCMCIA) += pcmcia.o obj-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index e29a6ddf2fd..dd7651ff5b4 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -89,8 +89,10 @@ static void __iomem * set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags) { pccard_mem_map *mem = &s->cis_mem; + int ret; + if (!(s->features & SS_CAP_STATIC_MAP) && mem->res == NULL) { - mem->res = find_mem_region(0, s->map_size, s->map_size, 0, s); + mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s); if (mem->res == NULL) { printk(KERN_NOTICE "cs: unable to map card memory!\n"); return NULL; @@ -99,7 +101,12 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag } mem->card_start = card_offset; mem->flags = flags; - s->ops->set_mem_map(s, mem); + ret = s->ops->set_mem_map(s, mem); + if (ret) { + iounmap(s->cis_virt); + return NULL; + } + if (s->features & SS_CAP_STATIC_MAP) { if (s->cis_virt) iounmap(s->cis_virt); @@ -119,13 +126,13 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag #define IS_ATTR 1 #define IS_INDIRECT 8 -int read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, +int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) { void __iomem *sys, *end; unsigned char *buf = ptr; - cs_dbg(s, 3, "read_cis_mem(%d, %#x, %u)\n", attr, addr, len); + cs_dbg(s, 3, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed @@ -182,14 +189,16 @@ int read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, *(u_char *)(ptr+2), *(u_char *)(ptr+3)); return 0; } +EXPORT_SYMBOL(pcmcia_read_cis_mem); + -void write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, +void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) { void __iomem *sys, *end; unsigned char *buf = ptr; - cs_dbg(s, 3, "write_cis_mem(%d, %#x, %u)\n", attr, addr, len); + cs_dbg(s, 3, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed @@ -239,6 +248,8 @@ void write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, } } } +EXPORT_SYMBOL(pcmcia_write_cis_mem); + /*====================================================================== @@ -274,7 +285,7 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, ret = read_cb_mem(s, attr, addr, len, ptr); else #endif - ret = read_cis_mem(s, attr, addr, len, ptr); + ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr); if (ret == 0) { /* Copy data into the cache */ @@ -348,7 +359,7 @@ int verify_cis_cache(struct pcmcia_socket *s) read_cb_mem(s, cis->attr, cis->addr, len, buf); else #endif - read_cis_mem(s, cis->attr, cis->addr, len, buf); + pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); if (memcmp(buf, cis->cache, len) != 0) { kfree(buf); @@ -381,6 +392,7 @@ int pcmcia_replace_cis(struct pcmcia_socket *s, cisdump_t *cis) memcpy(s->fake_cis, cis->Data, cis->Length); return CS_SUCCESS; } +EXPORT_SYMBOL(pcmcia_replace_cis); /*====================================================================== diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 48e4f04530d..e82859d3227 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -43,36 +43,11 @@ #include <pcmcia/ds.h> #include "cs_internal.h" -#ifdef CONFIG_PCI -#define PCI_OPT " [pci]" -#else -#define PCI_OPT "" -#endif -#ifdef CONFIG_CARDBUS -#define CB_OPT " [cardbus]" -#else -#define CB_OPT "" -#endif -#ifdef CONFIG_PM -#define PM_OPT " [pm]" -#else -#define PM_OPT "" -#endif -#if !defined(CONFIG_CARDBUS) && !defined(CONFIG_PCI) && !defined(CONFIG_PM) -#define OPTIONS " none" -#else -#define OPTIONS PCI_OPT CB_OPT PM_OPT -#endif - -static const char *release = "Linux Kernel Card Services"; -static const char *options = "options: " OPTIONS; - -/*====================================================================*/ /* Module parameters */ MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); -MODULE_DESCRIPTION("Linux Kernel Card Services\noptions:" OPTIONS); +MODULE_DESCRIPTION("Linux Kernel Card Services"); MODULE_LICENSE("GPL"); #define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444) @@ -89,9 +64,6 @@ INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */ /* Access speed for attribute memory windows */ INT_MODULE_PARM(cis_speed, 300); /* ns */ -/* Access speed for IO windows */ -INT_MODULE_PARM(io_speed, 0); /* ns */ - #ifdef DEBUG static int pc_debug; @@ -103,34 +75,26 @@ int cs_debug_level(int level) } #endif -/*====================================================================*/ socket_state_t dead_socket = { .csc_mask = SS_DETECT, }; +EXPORT_SYMBOL(dead_socket); /* List of all sockets, protected by a rwsem */ LIST_HEAD(pcmcia_socket_list); -DECLARE_RWSEM(pcmcia_socket_list_rwsem); EXPORT_SYMBOL(pcmcia_socket_list); -EXPORT_SYMBOL(pcmcia_socket_list_rwsem); - -#ifdef CONFIG_PCMCIA_PROBE -/* mask ofIRQs already reserved by other cards, we should avoid using them */ -static u8 pcmcia_used_irq[NR_IRQS]; -#endif +DECLARE_RWSEM(pcmcia_socket_list_rwsem); +EXPORT_SYMBOL(pcmcia_socket_list_rwsem); -/*==================================================================== - - Low-level PC Card interface drivers need to register with Card - Services using these calls. - -======================================================================*/ /** - * socket drivers are expected to use the following callbacks in their + * Low-level PCMCIA socket drivers need to register with the PCCard + * core using pcmcia_register_socket. + * + * socket drivers are expected to use the following callbacks in their * .drv struct: * - pcmcia_socket_dev_suspend * - pcmcia_socket_dev_resume @@ -230,8 +194,8 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) } /* try to obtain a socket number [yes, it gets ugly if we - * register more than 2^sizeof(unsigned int) pcmcia - * sockets... but the socket number is deprecated + * register more than 2^sizeof(unsigned int) pcmcia + * sockets... but the socket number is deprecated * anyways, so I don't care] */ down_write(&pcmcia_socket_list_rwsem); if (list_empty(&pcmcia_socket_list)) @@ -340,54 +304,49 @@ struct pcmcia_socket * pcmcia_get_socket_by_nr(unsigned int nr) EXPORT_SYMBOL(pcmcia_get_socket_by_nr); -/*====================================================================== - - socket_setup() and shutdown_socket() are called by the main event - handler when card insertion and removal events are received. - socket_setup() turns on socket power and resets the socket, in two stages. - shutdown_socket() unconfigures a socket and turns off socket power. - -======================================================================*/ - +/** + * socket_setup() and shutdown_socket() are called by the main event + * handler when card insertion and removal events are received. + * socket_setup() turns on socket power and resets the socket, in two stages. + * shutdown_socket() unconfigures a socket and turns off socket power. + */ static void shutdown_socket(struct pcmcia_socket *s) { - cs_dbg(s, 1, "shutdown_socket\n"); - - /* Blank out the socket state */ - s->socket = dead_socket; - s->ops->init(s); - s->ops->set_socket(s, &s->socket); - s->irq.AssignedIRQ = s->irq.Config = 0; - s->lock_count = 0; - destroy_cis_cache(s); + cs_dbg(s, 1, "shutdown_socket\n"); + + /* Blank out the socket state */ + s->socket = dead_socket; + s->ops->init(s); + s->ops->set_socket(s, &s->socket); + s->irq.AssignedIRQ = s->irq.Config = 0; + s->lock_count = 0; + destroy_cis_cache(s); #ifdef CONFIG_CARDBUS - cb_free(s); + cb_free(s); #endif - s->functions = 0; - if (s->config) { - kfree(s->config); - s->config = NULL; - } - - { - int status; - s->ops->get_status(s, &status); - if (status & SS_POWERON) { - printk(KERN_ERR "PCMCIA: socket %p: *** DANGER *** unable to remove socket power\n", s); + s->functions = 0; + if (s->config) { + kfree(s->config); + s->config = NULL; } - } -} /* shutdown_socket */ -/*====================================================================== + { + int status; + s->ops->get_status(s, &status); + if (status & SS_POWERON) { + printk(KERN_ERR "PCMCIA: socket %p: *** DANGER *** unable to remove socket power\n", s); + } + } +} /* shutdown_socket */ - The central event handler. Send_event() sends an event to the - 16-bit subsystem, which then calls the relevant device drivers. - Parse_events() interprets the event bits from - a card status change report. Do_shutdown() handles the high - priority stuff associated with a card removal. - -======================================================================*/ +/** + * The central event handler. Send_event() sends an event to the + * 16-bit subsystem, which then calls the relevant device drivers. + * Parse_events() interprets the event bits from + * a card status change report. Do_shutdown() handles the high + * priority stuff associated with a card removal. + */ /* NOTE: send_event needs to be called with skt->sem held. */ @@ -746,420 +705,9 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) wake_up(&s->thread_wait); } } /* pcmcia_parse_events */ +EXPORT_SYMBOL(pcmcia_parse_events); -/*====================================================================== - - Special stuff for managing IO windows, because they are scarce. - -======================================================================*/ - -static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base, - ioaddr_t num, u_int lines) -{ - int i; - kio_addr_t try, align; - - align = (*base) ? (lines ? 1<<lines : 0) : 1; - if (align && (align < num)) { - if (*base) { - cs_dbg(s, 0, "odd IO request: num %#x align %#lx\n", - num, align); - align = 0; - } else - while (align && (align < num)) align <<= 1; - } - if (*base & ~(align-1)) { - cs_dbg(s, 0, "odd IO request: base %#x align %#lx\n", - *base, align); - align = 0; - } - if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) { - *base = s->io_offset | (*base & 0x0fff); - return 0; - } - /* Check for an already-allocated window that must conflict with - what was asked for. It is a hack because it does not catch all - potential conflicts, just the most obvious ones. */ - for (i = 0; i < MAX_IO_WIN; i++) - if ((s->io[i].NumPorts != 0) && - ((s->io[i].BasePort & (align-1)) == *base)) - return 1; - for (i = 0; i < MAX_IO_WIN; i++) { - if (s->io[i].NumPorts == 0) { - s->io[i].res = find_io_region(*base, num, align, s); - if (s->io[i].res) { - s->io[i].Attributes = attr; - s->io[i].BasePort = *base = s->io[i].res->start; - s->io[i].NumPorts = s->io[i].InUse = num; - break; - } else - return 1; - } else if (s->io[i].Attributes != attr) - continue; - /* Try to extend top of window */ - try = s->io[i].BasePort + s->io[i].NumPorts; - if ((*base == 0) || (*base == try)) - if (adjust_io_region(s->io[i].res, s->io[i].res->start, - s->io[i].res->end + num, s) == 0) { - *base = try; - s->io[i].NumPorts += num; - s->io[i].InUse += num; - break; - } - /* Try to extend bottom of window */ - try = s->io[i].BasePort - num; - if ((*base == 0) || (*base == try)) - if (adjust_io_region(s->io[i].res, s->io[i].res->start - num, - s->io[i].res->end, s) == 0) { - s->io[i].BasePort = *base = try; - s->io[i].NumPorts += num; - s->io[i].InUse += num; - break; - } - } - return (i == MAX_IO_WIN); -} /* alloc_io_space */ - -static void release_io_space(struct pcmcia_socket *s, ioaddr_t base, - ioaddr_t num) -{ - int i; - - for (i = 0; i < MAX_IO_WIN; i++) { - if ((s->io[i].BasePort <= base) && - (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) { - s->io[i].InUse -= num; - /* Free the window if no one else is using it */ - if (s->io[i].InUse == 0) { - s->io[i].NumPorts = 0; - release_resource(s->io[i].res); - kfree(s->io[i].res); - s->io[i].res = NULL; - } - } - } -} - -/*====================================================================== - - Access_configuration_register() reads and writes configuration - registers in attribute memory. Memory window 0 is reserved for - this and the tuple reading services. - -======================================================================*/ - -int pccard_access_configuration_register(struct pcmcia_socket *s, - unsigned int function, - conf_reg_t *reg) -{ - config_t *c; - int addr; - u_char val; - - if (!s || !s->config) - return CS_NO_CARD; - - c = &s->config[function]; - - if (c == NULL) - return CS_NO_CARD; - - if (!(c->state & CONFIG_LOCKED)) - return CS_CONFIGURATION_LOCKED; - - addr = (c->ConfigBase + reg->Offset) >> 1; - - switch (reg->Action) { - case CS_READ: - read_cis_mem(s, 1, addr, 1, &val); - reg->Value = val; - break; - case CS_WRITE: - val = reg->Value; - write_cis_mem(s, 1, addr, 1, &val); - break; - default: - return CS_BAD_ARGS; - break; - } - return CS_SUCCESS; -} /* access_configuration_register */ -EXPORT_SYMBOL(pccard_access_configuration_register); - - -/*====================================================================*/ - -int pccard_get_configuration_info(struct pcmcia_socket *s, - unsigned int function, - config_info_t *config) -{ - config_t *c; - - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - - config->Function = function; - -#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->irq.AssignedIRQ; - if (config->AssignedIRQ) - config->Attributes |= CONF_ENABLE_IRQ; - config->BasePort1 = s->io[0].BasePort; - config->NumPorts1 = s->io[0].NumPorts; - } - return CS_SUCCESS; - } -#endif - - c = (s->config != NULL) ? &s->config[function] : NULL; - - if ((c == NULL) || !(c->state & CONFIG_LOCKED)) { - config->Attributes = 0; - config->Vcc = s->socket.Vcc; - config->Vpp1 = config->Vpp2 = s->socket.Vpp; - return CS_SUCCESS; - } - - /* !!! This is a hack !!! */ - memcpy(&config->Attributes, &c->Attributes, sizeof(config_t)); - config->Attributes |= CONF_VALID_CLIENT; - config->CardValues = c->CardValues; - config->IRQAttributes = c->irq.Attributes; - config->AssignedIRQ = s->irq.AssignedIRQ; - 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 CS_SUCCESS; -} /* get_configuration_info */ -EXPORT_SYMBOL(pccard_get_configuration_info); - -/*====================================================================== - - Return information about this version of Card Services. - -======================================================================*/ - -int pcmcia_get_card_services_info(servinfo_t *info) -{ - unsigned int socket_count = 0; - struct list_head *tmp; - info->Signature[0] = 'C'; - info->Signature[1] = 'S'; - down_read(&pcmcia_socket_list_rwsem); - list_for_each(tmp, &pcmcia_socket_list) - socket_count++; - up_read(&pcmcia_socket_list_rwsem); - info->Count = socket_count; - info->Revision = CS_RELEASE_CODE; - info->CSLevel = 0x0210; - info->VendorString = (char *)release; - return CS_SUCCESS; -} /* get_card_services_info */ - - -/*====================================================================*/ - -int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req) -{ - window_t *win; - int w; - - if (!s || !(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - for (w = idx; w < MAX_WIN; w++) - if (s->state & SOCKET_WIN_REQ(w)) break; - if (w == MAX_WIN) - return CS_NO_MORE_ITEMS; - win = &s->win[w]; - req->Base = win->ctl.res->start; - req->Size = win->ctl.res->end - win->ctl.res->start + 1; - req->AccessSpeed = win->ctl.speed; - req->Attributes = 0; - if (win->ctl.flags & MAP_ATTRIB) - req->Attributes |= WIN_MEMORY_TYPE_AM; - if (win->ctl.flags & MAP_ACTIVE) - req->Attributes |= WIN_ENABLE; - if (win->ctl.flags & MAP_16BIT) - req->Attributes |= WIN_DATA_WIDTH_16; - if (win->ctl.flags & MAP_USE_WAIT) - req->Attributes |= WIN_USE_WAIT; - *handle = win; - return CS_SUCCESS; -} /* get_window */ -EXPORT_SYMBOL(pcmcia_get_window); - -/*===================================================================== - - Return the PCI device associated with a card.. - -======================================================================*/ - -#ifdef CONFIG_CARDBUS - -struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s) -{ - if (!s || !(s->state & SOCKET_CARDBUS)) - return NULL; - - return s->cb_dev->subordinate; -} - -EXPORT_SYMBOL(pcmcia_lookup_bus); - -#endif - -/*====================================================================== - - Get the current socket state bits. We don't support the latched - SocketState yet: I haven't seen any point for it. - -======================================================================*/ - -int pccard_get_status(struct pcmcia_socket *s, unsigned int function, 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 CS_NO_CARD; - - c = (s->config != NULL) ? &s->config[function] : NULL; - if ((c != NULL) && (c->state & CONFIG_LOCKED) && - (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { - u_char reg; - if (c->Present & PRESENT_PIN_REPLACE) { - read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); - 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->Present & PRESENT_EXT_STATUS) { - read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); - status->CardState |= - (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; - } - return CS_SUCCESS; - } - 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 CS_SUCCESS; -} /* get_status */ -EXPORT_SYMBOL(pccard_get_status); - -/*====================================================================== - - Change the card address of an already open memory window. - -======================================================================*/ - -int pcmcia_get_mem_page(window_handle_t win, memreq_t *req) -{ - if ((win == NULL) || (win->magic != WINDOW_MAGIC)) - return CS_BAD_HANDLE; - req->Page = 0; - req->CardOffset = win->ctl.card_start; - return CS_SUCCESS; -} /* get_mem_page */ - -int pcmcia_map_mem_page(window_handle_t win, memreq_t *req) -{ - struct pcmcia_socket *s; - if ((win == NULL) || (win->magic != WINDOW_MAGIC)) - return CS_BAD_HANDLE; - if (req->Page != 0) - return CS_BAD_PAGE; - s = win->sock; - win->ctl.card_start = req->CardOffset; - if (s->ops->set_mem_map(s, &win->ctl) != 0) - return CS_BAD_OFFSET; - return CS_SUCCESS; -} /* map_mem_page */ - -/*====================================================================== - - Modify a locked socket configuration - -======================================================================*/ - -int pcmcia_modify_configuration(client_handle_t handle, - modconf_t *mod) -{ - struct pcmcia_socket *s; - config_t *c; - - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); c = CONFIG(handle); - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - if (!(c->state & CONFIG_LOCKED)) - return CS_CONFIGURATION_LOCKED; - - if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { - if (mod->Attributes & CONF_ENABLE_IRQ) { - c->Attributes |= CONF_ENABLE_IRQ; - s->socket.io_irq = s->irq.AssignedIRQ; - } else { - c->Attributes &= ~CONF_ENABLE_IRQ; - s->socket.io_irq = 0; - } - s->ops->set_socket(s, &s->socket); - } - - if (mod->Attributes & CONF_VCC_CHANGE_VALID) - return CS_BAD_VCC; - - /* We only allow changing Vpp1 and Vpp2 to the same value */ - if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) && - (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { - if (mod->Vpp1 != mod->Vpp2) - return CS_BAD_VPP; - c->Vpp1 = c->Vpp2 = s->socket.Vpp = mod->Vpp1; - if (s->ops->set_socket(s, &s->socket)) - return CS_BAD_VPP; - } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) || - (mod->Attributes & CONF_VPP2_CHANGE_VALID)) - return CS_BAD_VPP; - - return CS_SUCCESS; -} /* modify_configuration */ - /* register pcmcia_callback */ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) { @@ -1188,543 +736,16 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) } EXPORT_SYMBOL(pccard_register_pcmcia); -/*====================================================================*/ -int pcmcia_release_configuration(client_handle_t handle) -{ - pccard_io_map io = { 0, 0, 0, 0, 1 }; - struct pcmcia_socket *s; - int i; - - if (CHECK_HANDLE(handle) || - !(handle->state & CLIENT_CONFIG_LOCKED)) - return CS_BAD_HANDLE; - handle->state &= ~CLIENT_CONFIG_LOCKED; - s = SOCKET(handle); - -#ifdef CONFIG_CARDBUS - if (handle->state & CLIENT_CARDBUS) - return CS_SUCCESS; -#endif - - if (!(handle->state & CLIENT_STALE)) { - config_t *c = CONFIG(handle); - if (--(s->lock_count) == 0) { - s->socket.flags = SS_OUTPUT_ENA; /* Is this correct? */ - s->socket.Vpp = 0; - s->socket.io_irq = 0; - s->ops->set_socket(s, &s->socket); - } - if (c->state & CONFIG_IO_REQ) - for (i = 0; i < MAX_IO_WIN; i++) { - if (s->io[i].NumPorts == 0) - continue; - s->io[i].Config--; - if (s->io[i].Config != 0) - continue; - io.map = i; - s->ops->set_io_map(s, &io); - } - c->state &= ~CONFIG_LOCKED; - } - - return CS_SUCCESS; -} /* release_configuration */ - -/*====================================================================== - - Release_io() releases the I/O ranges allocated by a client. This - may be invoked some time after a card ejection has already dumped - the actual socket configuration, so if the client is "stale", we - don't bother checking the port ranges against the current socket - values. - -======================================================================*/ - -int pcmcia_release_io(client_handle_t handle, io_req_t *req) -{ - struct pcmcia_socket *s; - - if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ)) - return CS_BAD_HANDLE; - handle->state &= ~CLIENT_IO_REQ; - s = SOCKET(handle); - -#ifdef CONFIG_CARDBUS - if (handle->state & CLIENT_CARDBUS) - return CS_SUCCESS; -#endif - - if (!(handle->state & CLIENT_STALE)) { - config_t *c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if ((c->io.BasePort1 != req->BasePort1) || - (c->io.NumPorts1 != req->NumPorts1) || - (c->io.BasePort2 != req->BasePort2) || - (c->io.NumPorts2 != req->NumPorts2)) - return CS_BAD_ARGS; - c->state &= ~CONFIG_IO_REQ; - } - - release_io_space(s, req->BasePort1, req->NumPorts1); - if (req->NumPorts2) - release_io_space(s, req->BasePort2, req->NumPorts2); - - return CS_SUCCESS; -} /* release_io */ - -/*====================================================================*/ - -int pcmcia_release_irq(client_handle_t handle, irq_req_t *req) -{ - struct pcmcia_socket *s; - if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IRQ_REQ)) - return CS_BAD_HANDLE; - handle->state &= ~CLIENT_IRQ_REQ; - s = SOCKET(handle); - - if (!(handle->state & CLIENT_STALE)) { - config_t *c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if (c->irq.Attributes != req->Attributes) - return CS_BAD_ATTRIBUTE; - if (s->irq.AssignedIRQ != req->AssignedIRQ) - return CS_BAD_IRQ; - if (--s->irq.Config == 0) { - c->state &= ~CONFIG_IRQ_REQ; - s->irq.AssignedIRQ = 0; - } - } - - if (req->Attributes & IRQ_HANDLE_PRESENT) { - free_irq(req->AssignedIRQ, req->Instance); - } - -#ifdef CONFIG_PCMCIA_PROBE - pcmcia_used_irq[req->AssignedIRQ]--; -#endif - - return CS_SUCCESS; -} /* cs_release_irq */ - -/*====================================================================*/ - -int pcmcia_release_window(window_handle_t win) -{ - struct pcmcia_socket *s; - - if ((win == NULL) || (win->magic != WINDOW_MAGIC)) - return CS_BAD_HANDLE; - s = win->sock; - if (!(win->handle->state & CLIENT_WIN_REQ(win->index))) - return CS_BAD_HANDLE; - - /* Shut down memory window */ - win->ctl.flags &= ~MAP_ACTIVE; - s->ops->set_mem_map(s, &win->ctl); - s->state &= ~SOCKET_WIN_REQ(win->index); - - /* Release system memory */ - if (win->ctl.res) { - release_resource(win->ctl.res); - kfree(win->ctl.res); - win->ctl.res = NULL; - } - win->handle->state &= ~CLIENT_WIN_REQ(win->index); - - win->magic = 0; - - return CS_SUCCESS; -} /* release_window */ - -/*====================================================================*/ - -int pcmcia_request_configuration(client_handle_t handle, - config_req_t *req) -{ - int i; - u_int base; - struct pcmcia_socket *s; - config_t *c; - pccard_io_map iomap; - - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - -#ifdef CONFIG_CARDBUS - if (handle->state & CLIENT_CARDBUS) - return CS_UNSUPPORTED_MODE; -#endif - - if (req->IntType & INT_CARDBUS) - return CS_UNSUPPORTED_MODE; - c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - - /* Do power control. We don't allow changes in Vcc. */ - if (s->socket.Vcc != req->Vcc) - return CS_BAD_VCC; - if (req->Vpp1 != req->Vpp2) - return CS_BAD_VPP; - s->socket.Vpp = req->Vpp1; - if (s->ops->set_socket(s, &s->socket)) - return CS_BAD_VPP; - - c->Vcc = req->Vcc; c->Vpp1 = c->Vpp2 = req->Vpp1; - - /* Pick memory or I/O card, DMA mode, interrupt */ - c->IntType = req->IntType; - c->Attributes = req->Attributes; - if (req->IntType & INT_MEMORY_AND_IO) - s->socket.flags |= SS_IOCARD; - if (req->IntType & INT_ZOOMED_VIDEO) - s->socket.flags |= SS_ZVCARD | SS_IOCARD; - if (req->Attributes & CONF_ENABLE_DMA) - s->socket.flags |= SS_DMA_MODE; - if (req->Attributes & CONF_ENABLE_SPKR) - s->socket.flags |= SS_SPKR_ENA; - if (req->Attributes & CONF_ENABLE_IRQ) - s->socket.io_irq = s->irq.AssignedIRQ; - else - s->socket.io_irq = 0; - s->ops->set_socket(s, &s->socket); - s->lock_count++; - - /* Set up CIS configuration registers */ - base = c->ConfigBase = req->ConfigBase; - c->Present = c->CardValues = req->Present; - if (req->Present & PRESENT_COPY) { - c->Copy = req->Copy; - write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &c->Copy); - } - if (req->Present & PRESENT_OPTION) { - if (s->functions == 1) { - c->Option = req->ConfigIndex & COR_CONFIG_MASK; - } else { - c->Option = req->ConfigIndex & COR_MFC_CONFIG_MASK; - c->Option |= COR_FUNC_ENA|COR_IREQ_ENA; - if (req->Present & PRESENT_IOBASE_0) - c->Option |= COR_ADDR_DECODE; - } - if (c->state & CONFIG_IRQ_REQ) - if (!(c->irq.Attributes & IRQ_FORCED_PULSE)) - c->Option |= COR_LEVEL_REQ; - write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option); - mdelay(40); - } - if (req->Present & PRESENT_STATUS) { - c->Status = req->Status; - write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &c->Status); - } - if (req->Present & PRESENT_PIN_REPLACE) { - c->Pin = req->Pin; - write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &c->Pin); - } - if (req->Present & PRESENT_EXT_STATUS) { - c->ExtStatus = req->ExtStatus; - write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1, &c->ExtStatus); - } - if (req->Present & PRESENT_IOBASE_0) { - u_char b = c->io.BasePort1 & 0xff; - write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b); - b = (c->io.BasePort1 >> 8) & 0xff; - write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b); - } - if (req->Present & PRESENT_IOSIZE) { - u_char b = c->io.NumPorts1 + c->io.NumPorts2 - 1; - write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b); - } - - /* Configure I/O windows */ - if (c->state & CONFIG_IO_REQ) { - iomap.speed = io_speed; - for (i = 0; i < MAX_IO_WIN; i++) - if (s->io[i].NumPorts != 0) { - iomap.map = i; - iomap.flags = MAP_ACTIVE; - switch (s->io[i].Attributes & IO_DATA_PATH_WIDTH) { - case IO_DATA_PATH_WIDTH_16: - iomap.flags |= MAP_16BIT; break; - case IO_DATA_PATH_WIDTH_AUTO: - iomap.flags |= MAP_AUTOSZ; break; - default: - break; - } - iomap.start = s->io[i].BasePort; - iomap.stop = iomap.start + s->io[i].NumPorts - 1; - s->ops->set_io_map(s, &iomap); - s->io[i].Config++; - } - } - - c->state |= CONFIG_LOCKED; - handle->state |= CLIENT_CONFIG_LOCKED; - return CS_SUCCESS; -} /* request_configuration */ - -/*====================================================================== - - Request_io() reserves ranges of port addresses for a socket. - I have not implemented range sharing or alias addressing. - -======================================================================*/ - -int pcmcia_request_io(client_handle_t handle, io_req_t *req) -{ - struct pcmcia_socket *s; - config_t *c; - - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - - if (handle->state & CLIENT_CARDBUS) { -#ifdef CONFIG_CARDBUS - handle->state |= CLIENT_IO_REQ; - return CS_SUCCESS; -#else - return CS_UNSUPPORTED_FUNCTION; -#endif - } - - if (!req) - return CS_UNSUPPORTED_MODE; - c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if (c->state & CONFIG_IO_REQ) - return CS_IN_USE; - if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) - return CS_BAD_ATTRIBUTE; - if ((req->NumPorts2 > 0) && - (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) - return CS_BAD_ATTRIBUTE; - - if (alloc_io_space(s, req->Attributes1, &req->BasePort1, - req->NumPorts1, req->IOAddrLines)) - return CS_IN_USE; - - if (req->NumPorts2) { - if (alloc_io_space(s, req->Attributes2, &req->BasePort2, - req->NumPorts2, req->IOAddrLines)) { - release_io_space(s, req->BasePort1, req->NumPorts1); - return CS_IN_USE; - } - } - - c->io = *req; - c->state |= CONFIG_IO_REQ; - handle->state |= CLIENT_IO_REQ; - return CS_SUCCESS; -} /* request_io */ - -/*====================================================================== - - Request_irq() reserves an irq for this client. - - Also, since Linux only reserves irq's when they are actually - hooked, we don't guarantee that an irq will still be available - when the configuration is locked. Now that I think about it, - there might be a way to fix this using a dummy handler. - -======================================================================*/ - -#ifdef CONFIG_PCMCIA_PROBE -static irqreturn_t test_action(int cpl, void *dev_id, struct pt_regs *regs) -{ - return IRQ_NONE; -} -#endif - -int pcmcia_request_irq(client_handle_t handle, irq_req_t *req) -{ - struct pcmcia_socket *s; - config_t *c; - int ret = CS_IN_USE, irq = 0; - struct pcmcia_device *p_dev = handle_to_pdev(handle); - - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if (c->state & CONFIG_IRQ_REQ) - return CS_IN_USE; - -#ifdef CONFIG_PCMCIA_PROBE - if (s->irq.AssignedIRQ != 0) { - /* If the interrupt is already assigned, it must be the same */ - irq = s->irq.AssignedIRQ; - } else { - int try; - u32 mask = s->irq_mask; - void *data = NULL; - - for (try = 0; try < 64; try++) { - irq = try % 32; - - /* marked as available by driver, and not blocked by userspace? */ - if (!((mask >> irq) & 1)) - continue; - - /* avoid an IRQ which is already used by a PCMCIA card */ - if ((try < 32) && pcmcia_used_irq[irq]) - continue; - - /* register the correct driver, if possible, of check whether - * registering a dummy handle works, i.e. if the IRQ isn't - * marked as used by the kernel resource management core */ - ret = request_irq(irq, - (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action, - ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || - (s->functions > 1) || - (irq == s->pci_irq)) ? SA_SHIRQ : 0, - p_dev->dev.bus_id, - (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data); - if (!ret) { - if (!(req->Attributes & IRQ_HANDLE_PRESENT)) - free_irq(irq, data); - break; - } - } - } -#endif - if (ret) { - if (!s->pci_irq) - return ret; - irq = s->pci_irq; - } - - if (ret && req->Attributes & IRQ_HANDLE_PRESENT) { - if (request_irq(irq, req->Handler, - ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || - (s->functions > 1) || - (irq == s->pci_irq)) ? SA_SHIRQ : 0, - p_dev->dev.bus_id, req->Instance)) - return CS_IN_USE; - } - - c->irq.Attributes = req->Attributes; - s->irq.AssignedIRQ = req->AssignedIRQ = irq; - s->irq.Config++; - - c->state |= CONFIG_IRQ_REQ; - handle->state |= CLIENT_IRQ_REQ; - -#ifdef CONFIG_PCMCIA_PROBE - pcmcia_used_irq[irq]++; -#endif - - return CS_SUCCESS; -} /* pcmcia_request_irq */ - -/*====================================================================== - - Request_window() establishes a mapping between card memory space - and system memory space. - -======================================================================*/ - -int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle_t *wh) -{ - struct pcmcia_socket *s; - window_t *win; - u_long align; - int w; - - if (CHECK_HANDLE(*handle)) - return CS_BAD_HANDLE; - s = (*handle)->Socket; - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - if (req->Attributes & (WIN_PAGED | WIN_SHARED)) - return CS_BAD_ATTRIBUTE; - - /* Window size defaults to smallest available */ - if (req->Size == 0) - req->Size = s->map_size; - align = (((s->features & SS_CAP_MEM_ALIGN) || - (req->Attributes & WIN_STRICT_ALIGN)) ? - req->Size : s->map_size); - if (req->Size & (s->map_size-1)) - return CS_BAD_SIZE; - if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) || - (req->Base & (align-1))) - return CS_BAD_BASE; - if (req->Base) - align = 0; - - /* Allocate system memory window */ - for (w = 0; w < MAX_WIN; w++) - if (!(s->state & SOCKET_WIN_REQ(w))) break; - if (w == MAX_WIN) - return CS_OUT_OF_RESOURCE; - - win = &s->win[w]; - win->magic = WINDOW_MAGIC; - win->index = w; - win->handle = *handle; - win->sock = s; - - if (!(s->features & SS_CAP_STATIC_MAP)) { - win->ctl.res = find_mem_region(req->Base, req->Size, align, - (req->Attributes & WIN_MAP_BELOW_1MB), s); - if (!win->ctl.res) - return CS_IN_USE; - } - (*handle)->state |= CLIENT_WIN_REQ(w); - - /* Configure the socket controller */ - win->ctl.map = w+1; - win->ctl.flags = 0; - win->ctl.speed = req->AccessSpeed; - if (req->Attributes & WIN_MEMORY_TYPE) - win->ctl.flags |= MAP_ATTRIB; - if (req->Attributes & WIN_ENABLE) - win->ctl.flags |= MAP_ACTIVE; - if (req->Attributes & WIN_DATA_WIDTH_16) - win->ctl.flags |= MAP_16BIT; - if (req->Attributes & WIN_USE_WAIT) - win->ctl.flags |= MAP_USE_WAIT; - win->ctl.card_start = 0; - if (s->ops->set_mem_map(s, &win->ctl) != 0) - return CS_BAD_ARGS; - s->state |= SOCKET_WIN_REQ(w); - - /* Return window handle */ - if (s->features & SS_CAP_STATIC_MAP) { - req->Base = win->ctl.static_start; - } else { - req->Base = win->ctl.res->start; - } - *wh = win; - - return CS_SUCCESS; -} /* request_window */ - -/*====================================================================== - - I'm not sure which "reset" function this is supposed to use, - but for now, it uses the low-level interface's reset, not the - CIS register. - -======================================================================*/ +/* I'm not sure which "reset" function this is supposed to use, + * but for now, it uses the low-level interface's reset, not the + * CIS register. + */ int pccard_reset_card(struct pcmcia_socket *skt) { int ret; - + cs_dbg(skt, 1, "resetting socket\n"); down(&skt->skt_sem); @@ -1757,17 +778,14 @@ int pccard_reset_card(struct pcmcia_socket *skt) } /* reset_card */ EXPORT_SYMBOL(pccard_reset_card); -/*====================================================================== - - These shut down or wake up a socket. They are sort of user - initiated versions of the APM suspend and resume actions. - -======================================================================*/ +/* These shut down or wake up a socket. They are sort of user + * initiated versions of the APM suspend and resume actions. + */ int pcmcia_suspend_card(struct pcmcia_socket *skt) { int ret; - + cs_dbg(skt, 1, "suspending socket\n"); down(&skt->skt_sem); @@ -1786,6 +804,8 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt) return ret; } /* suspend_card */ +EXPORT_SYMBOL(pcmcia_suspend_card); + int pcmcia_resume_card(struct pcmcia_socket *skt) { @@ -1809,13 +829,10 @@ int pcmcia_resume_card(struct pcmcia_socket *skt) return ret; } /* resume_card */ +EXPORT_SYMBOL(pcmcia_resume_card); -/*====================================================================== - - These handle user requests to eject or insert a card. - -======================================================================*/ +/* These handle user requests to eject or insert a card. */ int pcmcia_eject_card(struct pcmcia_socket *skt) { int ret; @@ -1842,6 +859,8 @@ int pcmcia_eject_card(struct pcmcia_socket *skt) return ret; } /* eject_card */ +EXPORT_SYMBOL(pcmcia_eject_card); + int pcmcia_insert_card(struct pcmcia_socket *skt) { @@ -1865,37 +884,38 @@ int pcmcia_insert_card(struct pcmcia_socket *skt) return ret; } /* insert_card */ +EXPORT_SYMBOL(pcmcia_insert_card); -/*====================================================================== - OS-specific module glue goes here - -======================================================================*/ -/* in alpha order */ -EXPORT_SYMBOL(pcmcia_eject_card); -EXPORT_SYMBOL(pcmcia_get_card_services_info); -EXPORT_SYMBOL(pcmcia_get_mem_page); -EXPORT_SYMBOL(pcmcia_insert_card); -EXPORT_SYMBOL(pcmcia_map_mem_page); -EXPORT_SYMBOL(pcmcia_modify_configuration); -EXPORT_SYMBOL(pcmcia_release_configuration); -EXPORT_SYMBOL(pcmcia_release_io); -EXPORT_SYMBOL(pcmcia_release_irq); -EXPORT_SYMBOL(pcmcia_release_window); -EXPORT_SYMBOL(pcmcia_replace_cis); -EXPORT_SYMBOL(pcmcia_request_configuration); -EXPORT_SYMBOL(pcmcia_request_io); -EXPORT_SYMBOL(pcmcia_request_irq); -EXPORT_SYMBOL(pcmcia_request_window); -EXPORT_SYMBOL(pcmcia_resume_card); -EXPORT_SYMBOL(pcmcia_suspend_card); +static int pcmcia_socket_hotplug(struct class_device *dev, char **envp, + int num_envp, char *buffer, int buffer_size) +{ + struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev); + int i = 0, length = 0; + + if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, + &length, "SOCKET_NO=%u", s->sock)) + return -ENOMEM; + + envp[i] = NULL; + + return 0; +} + + +static struct completion pcmcia_unload; + +static void pcmcia_release_socket_class(struct class *data) +{ + complete(&pcmcia_unload); +} -EXPORT_SYMBOL(dead_socket); -EXPORT_SYMBOL(pcmcia_parse_events); struct class pcmcia_socket_class = { .name = "pcmcia_socket", + .hotplug = pcmcia_socket_hotplug, .release = pcmcia_release_socket, + .class_release = pcmcia_release_socket_class, }; EXPORT_SYMBOL(pcmcia_socket_class); @@ -1903,9 +923,8 @@ EXPORT_SYMBOL(pcmcia_socket_class); static int __init init_pcmcia_cs(void) { int ret; - printk(KERN_INFO "%s\n", release); - printk(KERN_INFO " %s\n", options); + init_completion(&pcmcia_unload); ret = class_register(&pcmcia_socket_class); if (ret) return (ret); @@ -1914,13 +933,12 @@ static int __init init_pcmcia_cs(void) static void __exit exit_pcmcia_cs(void) { - printk(KERN_INFO "unloading Kernel Card Services\n"); - class_interface_unregister(&pccard_sysfs_interface); - class_unregister(&pcmcia_socket_class); + class_interface_unregister(&pccard_sysfs_interface); + class_unregister(&pcmcia_socket_class); + + wait_for_completion(&pcmcia_unload); } subsys_initcall(init_pcmcia_cs); module_exit(exit_pcmcia_cs); -/*====================================================================*/ - diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 7933a7db49d..0b4c18edfa4 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -123,9 +123,9 @@ void cb_free(struct pcmcia_socket *s); int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len, void *ptr); /* In cistpl.c */ -int read_cis_mem(struct pcmcia_socket *s, int attr, +int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr); -void write_cis_mem(struct pcmcia_socket *s, int attr, +void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr); void release_cis_mem(struct pcmcia_socket *s); void destroy_cis_cache(struct pcmcia_socket *s); @@ -134,13 +134,12 @@ int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t /* In rsrc_mgr */ void pcmcia_validate_mem(struct pcmcia_socket *s); -struct resource *find_io_region(unsigned long base, int num, unsigned long align, +struct resource *pcmcia_find_io_region(unsigned long base, int num, unsigned long align, struct pcmcia_socket *s); -int adjust_io_region(struct resource *res, unsigned long r_start, +int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start, unsigned long r_end, struct pcmcia_socket *s); -struct resource *find_mem_region(u_long base, u_long num, u_long align, +struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, int low, struct pcmcia_socket *s); -int adjust_resource_info(client_handle_t handle, adjust_t *adj); void release_resource_db(struct pcmcia_socket *s); /* In socket_sysfs.c */ @@ -159,7 +158,7 @@ int pccard_access_configuration_register(struct pcmcia_socket *s, unsigned int f struct pcmcia_callback{ struct module *owner; int (*event) (struct pcmcia_socket *s, event_t event, int priority); - int (*resources_done) (struct pcmcia_socket *s); + void (*requery) (struct pcmcia_socket *s); }; int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 569e55feecf..cabddd49f6f 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -10,44 +10,29 @@ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * * (C) 1999 David A. Hinds - * (C) 2003 - 2004 Dominik Brodowski + * (C) 2003 - 2005 Dominik Brodowski */ #include <linux/config.h> +#include <linux/kernel.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/init.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/string.h> #include <linux/errno.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/fcntl.h> -#include <linux/sched.h> -#include <linux/smp_lock.h> -#include <linux/timer.h> -#include <linux/ioctl.h> -#include <linux/proc_fs.h> -#include <linux/poll.h> -#include <linux/pci.h> #include <linux/list.h> #include <linux/delay.h> -#include <linux/kref.h> #include <linux/workqueue.h> - -#include <asm/atomic.h> +#include <linux/crc32.h> +#include <linux/firmware.h> #define IN_CARD_SERVICES -#include <pcmcia/version.h> #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> #include <pcmcia/ss.h> #include "cs_internal.h" +#include "ds_internal.h" /*====================================================================*/ @@ -70,49 +55,9 @@ module_param_named(pc_debug, ds_pc_debug, int, 0644); #define ds_dbg(lvl, fmt, arg...) do { } while (0) #endif -/*====================================================================*/ +spinlock_t pcmcia_dev_list_lock; -/* 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_bus_socket *socket; -} user_info_t; - -/* Socket state information */ -struct pcmcia_bus_socket { - struct kref refcount; - struct pcmcia_callback callback; - int state; - user_info_t *user; - wait_queue_head_t queue; - struct pcmcia_socket *parent; - - /* the PCMCIA devices connected to this socket (normally one, more - * for multifunction devices: */ - struct list_head devices_list; - u8 device_count; /* the number of devices, used - * only internally and subject - * to incorrectness and change */ -}; -static spinlock_t pcmcia_dev_list_lock; - -#define DS_SOCKET_PRESENT 0x01 -#define DS_SOCKET_BUSY 0x02 -#define DS_SOCKET_REMOVAL_PENDING 0x10 -#define DS_SOCKET_DEAD 0x80 - -/*====================================================================*/ - -static int major_dev = -1; - -static int unbind_request(struct pcmcia_bus_socket *s); +static int unbind_request(struct pcmcia_socket *s); /*====================================================================*/ @@ -213,7 +158,7 @@ static const lookup_t service_table[] = { }; -int pcmcia_report_error(client_handle_t handle, error_info_t *err) +static int pcmcia_report_error(client_handle_t handle, error_info_t *err) { int i; char *serv; @@ -243,7 +188,6 @@ int pcmcia_report_error(client_handle_t handle, error_info_t *err) return CS_SUCCESS; } /* report_error */ -EXPORT_SYMBOL(pcmcia_report_error); /* end of code which was in cs.c before */ @@ -256,29 +200,101 @@ void cs_error(client_handle_t handle, int func, int ret) } EXPORT_SYMBOL(cs_error); -/*======================================================================*/ - -static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info); -static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr); -static void pcmcia_release_bus_socket(struct kref *refcount) +static void pcmcia_check_driver(struct pcmcia_driver *p_drv) { - struct pcmcia_bus_socket *s = container_of(refcount, struct pcmcia_bus_socket, refcount); - pcmcia_put_socket(s->parent); - kfree(s); + struct pcmcia_device_id *did = p_drv->id_table; + unsigned int i; + u32 hash; + + while (did && did->match_flags) { + for (i=0; i<4; i++) { + if (!did->prod_id[i]) + continue; + + hash = crc32(0, did->prod_id[i], strlen(did->prod_id[i])); + if (hash == did->prod_id_hash[i]) + continue; + + printk(KERN_DEBUG "pcmcia: %s: invalid hash for " + "product string \"%s\": is 0x%x, should " + "be 0x%x\n", p_drv->drv.name, did->prod_id[i], + did->prod_id_hash[i], hash); + printk(KERN_DEBUG "pcmcia: see " + "Documentation/pcmcia/devicetable.txt for " + "details\n"); + } + did++; + } + + return; } -static void pcmcia_put_bus_socket(struct pcmcia_bus_socket *s) + +#ifdef CONFIG_PCMCIA_LOAD_CIS + +/** + * pcmcia_load_firmware - load CIS from userspace if device-provided is broken + * @dev - the pcmcia device which needs a CIS override + * @filename - requested filename in /lib/firmware/cis/ + * + * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if + * the one provided by the card is broken. The firmware files reside in + * /lib/firmware/cis/ in userspace. + */ +static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) { - kref_put(&s->refcount, pcmcia_release_bus_socket); + struct pcmcia_socket *s = dev->socket; + const struct firmware *fw; + char path[20]; + int ret=-ENOMEM; + cisdump_t *cis; + + if (!filename) + return -EINVAL; + + ds_dbg(1, "trying to load firmware %s\n", filename); + + if (strlen(filename) > 14) + return -EINVAL; + + snprintf(path, 20, "%s", filename); + + if (request_firmware(&fw, path, &dev->dev) == 0) { + if (fw->size >= CISTPL_MAX_CIS_SIZE) + goto release; + + cis = kmalloc(sizeof(cisdump_t), GFP_KERNEL); + if (!cis) + goto release; + + memset(cis, 0, sizeof(cisdump_t)); + + cis->Length = fw->size + 1; + memcpy(cis->Data, fw->data, fw->size); + + if (!pcmcia_replace_cis(s, cis)) + ret = 0; + } + release: + release_firmware(fw); + + return (ret); } -static struct pcmcia_bus_socket *pcmcia_get_bus_socket(struct pcmcia_bus_socket *s) +#else /* !CONFIG_PCMCIA_LOAD_CIS */ + +static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) { - kref_get(&s->refcount); - return (s); + return -ENODEV; } +#endif + + +/*======================================================================*/ + + /** * pcmcia_register_driver - register a PCMCIA driver with the bus core * @@ -292,6 +308,8 @@ int pcmcia_register_driver(struct pcmcia_driver *driver) if (!driver) return -EINVAL; + pcmcia_check_driver(driver); + /* initialize common fields */ driver->drv.bus = &pcmcia_bus_type; driver->drv.owner = driver->owner; @@ -311,42 +329,10 @@ void pcmcia_unregister_driver(struct pcmcia_driver *driver) } EXPORT_SYMBOL(pcmcia_unregister_driver); -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry *proc_pccard = NULL; - -static int proc_read_drivers_callback(struct device_driver *driver, void *d) -{ - char **p = d; - struct pcmcia_driver *p_drv = container_of(driver, - struct pcmcia_driver, drv); - - *p += sprintf(*p, "%-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 - ); - d = (void *) p; - - return 0; -} - -static int proc_read_drivers(char *buf, char **start, off_t pos, - int count, int *eof, void *data) -{ - char *p = buf; - - bus_for_each_drv(&pcmcia_bus_type, NULL, - (void *) &p, proc_read_drivers_callback); - - return (p - buf); -} -#endif /* pcmcia_device handling */ -static struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev) +struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev) { struct device *tmp_dev; tmp_dev = get_device(&p_dev->dev); @@ -355,7 +341,7 @@ static struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev) return to_pcmcia_dev(tmp_dev); } -static void pcmcia_put_dev(struct pcmcia_device *p_dev) +void pcmcia_put_dev(struct pcmcia_device *p_dev) { if (p_dev) put_device(&p_dev->dev); @@ -365,7 +351,7 @@ static void pcmcia_release_dev(struct device *dev) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); ds_dbg(1, "releasing dev %p\n", p_dev); - pcmcia_put_bus_socket(p_dev->socket->pcmcia); + pcmcia_put_socket(p_dev->socket); kfree(p_dev); } @@ -500,34 +486,38 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) */ static DECLARE_MUTEX(device_add_lock); -static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, unsigned int function) +struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) { struct pcmcia_device *p_dev; unsigned long flags; - s = pcmcia_get_bus_socket(s); + s = pcmcia_get_socket(s); if (!s) return NULL; down(&device_add_lock); + /* max of 2 devices per card */ + if (s->device_count == 2) + goto err_put; + p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL); if (!p_dev) goto err_put; memset(p_dev, 0, sizeof(struct pcmcia_device)); - p_dev->socket = s->parent; + p_dev->socket = s; p_dev->device_no = (s->device_count++); p_dev->func = function; p_dev->dev.bus = &pcmcia_bus_type; - p_dev->dev.parent = s->parent->dev.dev; + p_dev->dev.parent = s->dev.dev; p_dev->dev.release = pcmcia_release_dev; sprintf (p_dev->dev.bus_id, "%d.%d", p_dev->socket->sock, p_dev->device_no); /* compat */ p_dev->client.client_magic = CLIENT_MAGIC; - p_dev->client.Socket = s->parent; + p_dev->client.Socket = s; p_dev->client.Function = function; p_dev->client.state = CLIENT_UNBOUND; @@ -536,6 +526,8 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns list_add_tail(&p_dev->socket_device_list, &s->devices_list); spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + pcmcia_device_query(p_dev); + if (device_register(&p_dev->dev)) { spin_lock_irqsave(&pcmcia_dev_list_lock, flags); list_del(&p_dev->socket_device_list); @@ -553,7 +545,7 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns s->device_count--; err_put: up(&device_add_lock); - pcmcia_put_bus_socket(s); + pcmcia_put_socket(s); return NULL; } @@ -584,23 +576,252 @@ static int pcmcia_card_add(struct pcmcia_socket *s) /* this doesn't handle multifunction devices on one pcmcia function * yet. */ for (i=0; i < no_funcs; i++) - pcmcia_device_add(s->pcmcia, i); + pcmcia_device_add(s, i); return (ret); } +static void pcmcia_delayed_add_pseudo_device(void *data) +{ + struct pcmcia_socket *s = data; + pcmcia_device_add(s, 0); + s->pcmcia_state.device_add_pending = 0; +} + +static inline void pcmcia_add_pseudo_device(struct pcmcia_socket *s) +{ + if (!s->pcmcia_state.device_add_pending) { + schedule_work(&s->device_add); + s->pcmcia_state.device_add_pending = 1; + } + return; +} + +static int pcmcia_requery(struct device *dev, void * _data) +{ + struct pcmcia_device *p_dev = to_pcmcia_dev(dev); + if (!p_dev->dev.driver) + pcmcia_device_query(p_dev); + + return 0; +} + +static void pcmcia_bus_rescan(struct pcmcia_socket *skt) +{ + int no_devices=0; + unsigned long flags; + + /* must be called with skt_sem held */ + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + if (list_empty(&skt->devices_list)) + no_devices=1; + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + /* if no devices were added for this socket yet because of + * missing resource information or other trouble, we need to + * do this now. */ + if (no_devices) { + int ret = pcmcia_card_add(skt); + if (ret) + return; + } + + /* some device information might have changed because of a CIS + * update or because we can finally read it correctly... so + * determine it again, overwriting old values if necessary. */ + bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery); + + /* we re-scan all devices, not just the ones connected to this + * socket. This does not matter, though. */ + bus_rescan_devices(&pcmcia_bus_type); +} + +static inline int pcmcia_devmatch(struct pcmcia_device *dev, + struct pcmcia_device_id *did) +{ + if (did->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID) { + if ((!dev->has_manf_id) || (dev->manf_id != did->manf_id)) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID) { + if ((!dev->has_card_id) || (dev->card_id != did->card_id)) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION) { + if (dev->func != did->function) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1) { + if (!dev->prod_id[0]) + return 0; + if (strcmp(did->prod_id[0], dev->prod_id[0])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2) { + if (!dev->prod_id[1]) + return 0; + if (strcmp(did->prod_id[1], dev->prod_id[1])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3) { + if (!dev->prod_id[2]) + return 0; + if (strcmp(did->prod_id[2], dev->prod_id[2])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4) { + if (!dev->prod_id[3]) + return 0; + if (strcmp(did->prod_id[3], dev->prod_id[3])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) { + /* handle pseudo multifunction devices: + * there are at most two pseudo multifunction devices. + * if we're matching against the first, schedule a + * call which will then check whether there are two + * pseudo devices, and if not, add the second one. + */ + if (dev->device_no == 0) + pcmcia_add_pseudo_device(dev->socket); + + if (dev->device_no != did->device_no) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { + if ((!dev->has_func_id) || (dev->func_id != did->func_id)) + return 0; + + /* if this is a pseudo-multi-function device, + * we need explicit matches */ + if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) + return 0; + if (dev->device_no) + return 0; + + /* also, FUNC_ID matching needs to be activated by userspace + * after it has re-checked that there is no possible module + * with a prod_id/manf_id/card_id match. + */ + if (!dev->allow_func_id_match) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) { + if (!dev->socket->fake_cis) + pcmcia_load_firmware(dev, did->cisfile); + + if (!dev->socket->fake_cis) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) { + int i; + for (i=0; i<4; i++) + if (dev->prod_id[i]) + return 0; + if (dev->has_manf_id || dev->has_card_id || dev->has_func_id) + return 0; + } + + dev->dev.driver_data = (void *) did; + + return 1; +} + + static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) { struct pcmcia_device * p_dev = to_pcmcia_dev(dev); struct pcmcia_driver * p_drv = to_pcmcia_drv(drv); + struct pcmcia_device_id *did = p_drv->id_table; /* matching by cardmgr */ if (p_dev->cardmgr == p_drv) return 1; + while (did && did->match_flags) { + if (pcmcia_devmatch(p_dev, did)) + return 1; + did++; + } + return 0; } +#ifdef CONFIG_HOTPLUG + +static int pcmcia_bus_hotplug(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct pcmcia_device *p_dev; + int i, length = 0; + u32 hash[4] = { 0, 0, 0, 0}; + + if (!dev) + return -ENODEV; + + p_dev = to_pcmcia_dev(dev); + + /* calculate hashes */ + for (i=0; i<4; i++) { + if (!p_dev->prod_id[i]) + continue; + hash[i] = crc32(0, p_dev->prod_id[i], strlen(p_dev->prod_id[i])); + } + + i = 0; + + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "SOCKET_NO=%u", + p_dev->socket->sock)) + return -ENOMEM; + + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEVICE_NO=%02X", + p_dev->device_no)) + return -ENOMEM; + + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X" + "pa%08Xpb%08Xpc%08Xpd%08X", + p_dev->has_manf_id ? p_dev->manf_id : 0, + p_dev->has_card_id ? p_dev->card_id : 0, + p_dev->has_func_id ? p_dev->func_id : 0, + p_dev->func, + p_dev->device_no, + hash[0], + hash[1], + hash[2], + hash[3])) + return -ENOMEM; + + envp[i] = NULL; + + return 0; +} + +#else + +static int pcmcia_bus_hotplug(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + return -ENODEV; +} + +#endif + /************************ per-device sysfs output ***************************/ #define pcmcia_device_attr(field, test, format) \ @@ -626,6 +847,43 @@ pcmcia_device_stringattr(prod_id2, prod_id[1]); pcmcia_device_stringattr(prod_id3, prod_id[2]); pcmcia_device_stringattr(prod_id4, prod_id[3]); +static ssize_t modalias_show(struct device *dev, char *buf) +{ + struct pcmcia_device *p_dev = to_pcmcia_dev(dev); + int i; + u32 hash[4] = { 0, 0, 0, 0}; + + /* calculate hashes */ + for (i=0; i<4; i++) { + if (!p_dev->prod_id[i]) + continue; + hash[i] = crc32(0,p_dev->prod_id[i],strlen(p_dev->prod_id[i])); + } + return sprintf(buf, "pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X" + "pa%08Xpb%08Xpc%08Xpd%08X\n", + p_dev->has_manf_id ? p_dev->manf_id : 0, + p_dev->has_card_id ? p_dev->card_id : 0, + p_dev->has_func_id ? p_dev->func_id : 0, + p_dev->func, p_dev->device_no, + hash[0], hash[1], hash[2], hash[3]); +} + +static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct pcmcia_device *p_dev = to_pcmcia_dev(dev); + if (!count) + return -EINVAL; + + down(&p_dev->socket->skt_sem); + p_dev->allow_func_id_match = 1; + up(&p_dev->socket->skt_sem); + + bus_rescan_devices(&pcmcia_bus_type); + + return count; +} + static struct device_attribute pcmcia_dev_attrs[] = { __ATTR(function, 0444, func_show, NULL), __ATTR_RO(func_id), @@ -635,46 +893,14 @@ static struct device_attribute pcmcia_dev_attrs[] = { __ATTR_RO(prod_id2), __ATTR_RO(prod_id3), __ATTR_RO(prod_id4), + __ATTR_RO(modalias), + __ATTR(allow_func_id_match, 0200, NULL, pcmcia_store_allow_func_id_match), __ATTR_NULL, }; /*====================================================================== - 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; -} - -static void handle_event(struct pcmcia_bus_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); -} - - -/*====================================================================== - The card status event handler. ======================================================================*/ @@ -706,21 +932,13 @@ static int send_event_callback(struct device *dev, void * _data) static int send_event(struct pcmcia_socket *s, event_t event, int priority) { - int ret = 0; struct send_event_data private; - struct pcmcia_bus_socket *skt = pcmcia_get_bus_socket(s->pcmcia); - - if (!skt) - return 0; private.skt = s; private.event = event; private.priority = priority; - ret = bus_for_each_dev(&pcmcia_bus_type, NULL, &private, send_event_callback); - - pcmcia_put_bus_socket(skt); - return ret; + return bus_for_each_dev(&pcmcia_bus_type, NULL, &private, send_event_callback); } /* send_event */ @@ -731,25 +949,25 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) { - struct pcmcia_bus_socket *s = skt->pcmcia; + struct pcmcia_socket *s = pcmcia_get_socket(skt); int ret = 0; ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n", - event, priority, s); + event, priority, skt); switch (event) { case CS_EVENT_CARD_REMOVAL: - s->state &= ~DS_SOCKET_PRESENT; + s->pcmcia_state.present = 0; send_event(skt, event, priority); - unbind_request(s); - handle_event(s, event); + unbind_request(skt); + handle_event(skt, event); break; case CS_EVENT_CARD_INSERTION: - s->state |= DS_SOCKET_PRESENT; + s->pcmcia_state.present = 1; pcmcia_card_add(skt); - handle_event(s, event); + handle_event(skt, event); break; case CS_EVENT_EJECTION_REQUEST: @@ -757,137 +975,22 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) break; default: - handle_event(s, event); + handle_event(skt, event); send_event(skt, event, priority); break; } + pcmcia_put_socket(s); + return 0; } /* ds_event */ -/*====================================================================== - - 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_bus_socket *s, bind_info_t *bind_info) -{ - struct pcmcia_driver *p_drv; - struct pcmcia_device *p_dev; - int ret = 0; - unsigned long flags; - - s = pcmcia_get_bus_socket(s); - if (!s) - return -EINVAL; - - ds_dbg(2, "bind_request(%d, '%s')\n", s->parent->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; - } - - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - 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". */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - bind_info->instance = p_dev->instance; - ret = -EBUSY; - goto err_put_module; - } else { - /* the correct driver managed to bind - * itself magically to the correct - * device. */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - 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! */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - goto rescan; - } - } - } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - - p_dev = pcmcia_device_add(s, bind_info->function); - if (!p_dev) { - ret = -EIO; - goto err_put_module; - } - -rescan: - p_dev->cardmgr = p_drv; - - pcmcia_device_query(p_dev); - - /* - * Prevent this racing with a card insertion. - */ - down(&s->parent->skt_sem); - bus_rescan_devices(&pcmcia_bus_type); - up(&s->parent->skt_sem); - - /* 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_bus_socket(s); - - return (ret); -} /* bind_request */ - int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) { client_t *client = NULL; - struct pcmcia_socket *s; - struct pcmcia_bus_socket *skt = NULL; + struct pcmcia_socket *s = NULL; struct pcmcia_device *p_dev = NULL; /* Look for unbound client with matching dev_info */ @@ -898,14 +1001,11 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) if (s->state & SOCKET_CARDBUS) continue; - skt = s->pcmcia; - if (!skt) - continue; - skt = pcmcia_get_bus_socket(skt); - if (!skt) + s = pcmcia_get_socket(s); + if (!s) continue; spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - list_for_each_entry(p_dev, &skt->devices_list, socket_device_list) { + list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { struct pcmcia_driver *p_drv; p_dev = pcmcia_get_dev(p_dev); if (!p_dev) @@ -924,14 +1024,14 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) pcmcia_put_dev(p_dev); } spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - pcmcia_put_bus_socket(skt); + pcmcia_put_socket(s); } found: up_read(&pcmcia_socket_list_rwsem); if (!p_dev || !client) return -ENODEV; - pcmcia_put_bus_socket(skt); /* safe, as we already hold a reference from bind_device */ + pcmcia_put_socket(s); /* safe, as we already hold a reference from bind_device */ *handle = client; client->state &= ~CLIENT_UNBOUND; @@ -978,106 +1078,15 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) EXPORT_SYMBOL(pcmcia_register_client); -/*====================================================================*/ - -extern struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s); - -static int get_device_info(struct pcmcia_bus_socket *s, bind_info_t *bind_info, int first) -{ - dev_node_t *node; - struct pcmcia_device *p_dev; - unsigned long flags; - 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->parent); - 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 - - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - 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; - } - } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - return -ENODEV; - - found: - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - - if ((!p_dev->instance) || - (p_dev->instance->state & DEV_CONFIG_PENDING)) { - ret = -EAGAIN; - goto err_put; - } - - if (first) - node = p_dev->instance->dev; - else - for (node = p_dev->instance->dev; node; node = node->next) - if (node == bind_info->next) - break; - if (!node) { - ret = -ENODEV; - goto err_put; - } - - strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN); - bind_info->major = node->major; - bind_info->minor = node->minor; - bind_info->next = node->next; - - err_put: - pcmcia_put_dev(p_dev); - return (ret); -} /* get_device_info */ - -/*====================================================================*/ - /* unbind _all_ devices attached to a given pcmcia_bus_socket. The * drivers have been called with EVENT_CARD_REMOVAL before. */ -static int unbind_request(struct pcmcia_bus_socket *s) +static int unbind_request(struct pcmcia_socket *s) { struct pcmcia_device *p_dev; unsigned long flags; - ds_dbg(2, "unbind_request(%d)\n", s->parent->sock); + ds_dbg(2, "unbind_request(%d)\n", s->sock); s->device_count = 0; @@ -1133,433 +1142,58 @@ int pcmcia_deregister_client(client_handle_t handle) } /* deregister_client */ EXPORT_SYMBOL(pcmcia_deregister_client); - -/*====================================================================== - - The user-mode PC Card device interface - -======================================================================*/ - -static int ds_open(struct inode *inode, struct file *file) -{ - socket_t i = iminor(inode); - struct pcmcia_bus_socket *s; - user_info_t *user; - - ds_dbg(0, "ds_open(socket %d)\n", i); - - s = get_socket_info_by_nr(i); - if (!s) - return -ENODEV; - s = pcmcia_get_bus_socket(s); - if (!s) - return -ENODEV; - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - if (s->state & DS_SOCKET_BUSY) { - pcmcia_put_bus_socket(s); - return -EBUSY; - } - else - s->state |= DS_SOCKET_BUSY; - } - - user = kmalloc(sizeof(user_info_t), GFP_KERNEL); - if (!user) { - pcmcia_put_bus_socket(s); - return -ENOMEM; - } - 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 (s->state & DS_SOCKET_PRESENT) - queue_event(user, CS_EVENT_CARD_INSERTION); - return 0; -} /* ds_open */ - -/*====================================================================*/ - -static int ds_release(struct inode *inode, struct file *file) -{ - struct pcmcia_bus_socket *s; - user_info_t *user, **link; - - ds_dbg(0, "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->state &= ~DS_SOCKET_BUSY; - } - 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_bus_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_bus_socket *s; - user_info_t *user; - int ret; - - ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_dentry->d_inode)); - - if (count < 4) - return -EINVAL; - - user = file->private_data; - if (CHECK_USER(user)) - return -EIO; - - s = user->socket; - if (s->state & DS_SOCKET_DEAD) - return -EIO; - - 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) -{ - ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_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_bus_socket *s; - user_info_t *user; - - ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_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 */ - -/*====================================================================*/ - -extern int pcmcia_adjust_resource_info(adjust_t *adj); - -static int ds_ioctl(struct inode * inode, struct file * file, - u_int cmd, u_long arg) -{ - struct pcmcia_bus_socket *s; - void __user *uarg = (char __user *)arg; - u_int size; - int ret, err; - ds_ioctl_arg_t *buf; - user_info_t *user; - - ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg); - - user = file->private_data; - if (CHECK_USER(user)) - return -EIO; - - s = user->socket; - if (s->state & DS_SOCKET_DEAD) - return -EIO; - - 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)) { - ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT); - return -EFAULT; - } - } - if (cmd & IOC_OUT) { - if (!access_ok(VERIFY_WRITE, uarg, size)) { - ds_dbg(3, "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) __copy_from_user((char *)buf, uarg, size); - - switch (cmd) { - case DS_ADJUST_RESOURCE_INFO: - ret = pcmcia_adjust_resource_info(&buf->adjust); - break; - case DS_GET_CARD_SERVICES_INFO: - ret = pcmcia_get_card_services_info(&buf->servinfo); - break; - case DS_GET_CONFIGURATION_INFO: - if (buf->config.Function && - (buf->config.Function >= s->parent->functions)) - ret = CS_BAD_ARGS; - else - ret = pccard_get_configuration_info(s->parent, - buf->config.Function, &buf->config); - break; - case DS_GET_FIRST_TUPLE: - down(&s->parent->skt_sem); - pcmcia_validate_mem(s->parent); - up(&s->parent->skt_sem); - ret = pccard_get_first_tuple(s->parent, BIND_FN_ALL, &buf->tuple); - break; - case DS_GET_NEXT_TUPLE: - ret = pccard_get_next_tuple(s->parent, 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->parent, &buf->tuple); - break; - case DS_PARSE_TUPLE: - buf->tuple.TupleData = buf->tuple_parse.data; - ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse); - break; - case DS_RESET_CARD: - ret = pccard_reset_card(s->parent); - break; - case DS_GET_STATUS: - if (buf->status.Function && - (buf->status.Function >= s->parent->functions)) - ret = CS_BAD_ARGS; - else - ret = pccard_get_status(s->parent, buf->status.Function, &buf->status); - break; - case DS_VALIDATE_CIS: - down(&s->parent->skt_sem); - pcmcia_validate_mem(s->parent); - up(&s->parent->skt_sem); - ret = pccard_validate_cis(s->parent, BIND_FN_ALL, &buf->cisinfo); - break; - case DS_SUSPEND_CARD: - ret = pcmcia_suspend_card(s->parent); - break; - case DS_RESUME_CARD: - ret = pcmcia_resume_card(s->parent); - break; - case DS_EJECT_CARD: - err = pcmcia_eject_card(s->parent); - break; - case DS_INSERT_CARD: - err = pcmcia_insert_card(s->parent); - break; - case DS_ACCESS_CONFIGURATION_REGISTER: - if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) { - err = -EPERM; - goto free_out; - } - if (buf->conf_reg.Function && - (buf->conf_reg.Function >= s->parent->functions)) - ret = CS_BAD_ARGS; - else - ret = pccard_access_configuration_register(s->parent, - buf->conf_reg.Function, &buf->conf_reg); - 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 { - static int printed = 0; - if (!printed) { - printk(KERN_WARNING "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n"); - printk(KERN_WARNING "MTD handling any more.\n"); - printed++; - } - } - err = -EINVAL; - goto free_out; - break; - case DS_GET_FIRST_WINDOW: - ret = pcmcia_get_window(s->parent, &buf->win_info.handle, 0, - &buf->win_info.window); - break; - case DS_GET_NEXT_WINDOW: - ret = pcmcia_get_window(s->parent, &buf->win_info.handle, - buf->win_info.handle->index + 1, &buf->win_info.window); - break; - case DS_GET_MEM_PAGE: - ret = pcmcia_get_mem_page(buf->win_info.handle, - &buf->win_info.map); - break; - case DS_REPLACE_CIS: - ret = pcmcia_replace_cis(s->parent, &buf->cisdump); - 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 != CS_SUCCESS)) { - ds_dbg(2, "ds_ioctl: ret = %d\n", ret); - switch (ret) { - case CS_BAD_SOCKET: case CS_NO_CARD: - err = -ENODEV; break; - case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ: - case CS_BAD_TUPLE: - err = -EINVAL; break; - case CS_IN_USE: - err = -EBUSY; break; - case CS_OUT_OF_RESOURCE: - err = -ENOSPC; break; - case CS_NO_MORE_ITEMS: - err = -ENODATA; break; - case CS_UNSUPPORTED_FUNCTION: - err = -ENOSYS; 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 struct file_operations ds_fops = { - .owner = THIS_MODULE, - .open = ds_open, - .release = ds_release, - .ioctl = ds_ioctl, - .read = ds_read, - .write = ds_write, - .poll = ds_poll, +static struct pcmcia_callback pcmcia_bus_callback = { + .owner = THIS_MODULE, + .event = ds_event, + .requery = pcmcia_bus_rescan, }; static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev) { struct pcmcia_socket *socket = class_get_devdata(class_dev); - struct pcmcia_bus_socket *s; int ret; - s = kmalloc(sizeof(struct pcmcia_bus_socket), GFP_KERNEL); - if(!s) - return -ENOMEM; - memset(s, 0, sizeof(struct pcmcia_bus_socket)); - - /* get reference to parent socket */ - s->parent = pcmcia_get_socket(socket); - if (!s->parent) { + socket = pcmcia_get_socket(socket); + if (!socket) { printk(KERN_ERR "PCMCIA obtaining reference to socket %p failed\n", socket); - kfree (s); return -ENODEV; } - kref_init(&s->refcount); - /* * Ugly. But we want to wait for the socket threads to have started up. * We really should let the drivers themselves drive some of this.. */ msleep(250); - init_waitqueue_head(&s->queue); - INIT_LIST_HEAD(&s->devices_list); - - /* Set up hotline to Card Services */ - s->callback.owner = THIS_MODULE; - s->callback.event = &ds_event; - s->callback.resources_done = &pcmcia_card_add; - socket->pcmcia = s; +#ifdef CONFIG_PCMCIA_IOCTL + init_waitqueue_head(&socket->queue); +#endif + INIT_LIST_HEAD(&socket->devices_list); + INIT_WORK(&socket->device_add, pcmcia_delayed_add_pseudo_device, socket); + memset(&socket->pcmcia_state, 0, sizeof(u8)); + socket->device_count = 0; - ret = pccard_register_pcmcia(socket, &s->callback); + ret = pccard_register_pcmcia(socket, &pcmcia_bus_callback); if (ret) { printk(KERN_ERR "PCMCIA registration PCCard core failed for socket %p\n", socket); - pcmcia_put_bus_socket(s); - socket->pcmcia = NULL; + pcmcia_put_socket(socket); return (ret); } return 0; } - static void pcmcia_bus_remove_socket(struct class_device *class_dev) { struct pcmcia_socket *socket = class_get_devdata(class_dev); - if (!socket || !socket->pcmcia) + if (!socket) return; + socket->pcmcia_state.dead = 1; pccard_register_pcmcia(socket, NULL); - socket->pcmcia->state |= DS_SOCKET_DEAD; - pcmcia_put_bus_socket(socket->pcmcia); - socket->pcmcia = NULL; + pcmcia_put_socket(socket); return; } @@ -1575,34 +1209,20 @@ static struct class_interface pcmcia_bus_interface = { struct bus_type pcmcia_bus_type = { .name = "pcmcia", + .hotplug = pcmcia_bus_hotplug, .match = pcmcia_bus_match, .dev_attrs = pcmcia_dev_attrs, }; -EXPORT_SYMBOL(pcmcia_bus_type); static int __init init_pcmcia_bus(void) { - int i; - spin_lock_init(&pcmcia_dev_list_lock); bus_register(&pcmcia_bus_type); class_interface_register(&pcmcia_bus_interface); - /* 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("pccard", proc_bus); - if (proc_pccard) - create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL); -#endif + pcmcia_setup_ioctl(); return 0; } @@ -1612,48 +1232,13 @@ fs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that static void __exit exit_pcmcia_bus(void) { - class_interface_unregister(&pcmcia_bus_interface); + pcmcia_cleanup_ioctl(); -#ifdef CONFIG_PROC_FS - if (proc_pccard) { - remove_proc_entry("drivers", proc_pccard); - remove_proc_entry("pccard", proc_bus); - } -#endif - if (major_dev != -1) - unregister_chrdev(major_dev, "pcmcia"); + class_interface_unregister(&pcmcia_bus_interface); bus_unregister(&pcmcia_bus_type); } module_exit(exit_pcmcia_bus); - -/* helpers for backwards-compatible functions */ - -static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr) -{ - struct pcmcia_socket * s = pcmcia_get_socket_by_nr(nr); - if (s && s->pcmcia) - return s->pcmcia; - else - 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); -} - MODULE_ALIAS("ds"); diff --git a/drivers/pcmcia/ds_internal.h b/drivers/pcmcia/ds_internal.h new file mode 100644 index 00000000000..d359bd25a51 --- /dev/null +++ b/drivers/pcmcia/ds_internal.h @@ -0,0 +1,21 @@ +/* ds_internal.h - internal header for 16-bit PCMCIA devices management */ + +extern spinlock_t pcmcia_dev_list_lock; +extern struct bus_type pcmcia_bus_type; + +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); + +#ifdef CONFIG_PCMCIA_IOCTL +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 +static inline void __init pcmcia_setup_ioctl(void) { return; } +static inline void __init 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 CS_SUCCESS; } +#endif diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index 90a335a5d9f..d72f9a35c8b 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -669,11 +669,13 @@ static int __init is_alive(u_short sock) if ((stat & I365_CS_DETECT) && (stat & I365_CS_POWERON) && (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) && (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(0)) && - (check_region(start, stop-start+1) != 0) && - ((start & 0xfeef) != 0x02e8)) - return 1; - else - return 0; + ((start & 0xfeef) != 0x02e8)) { + if (!request_region(start, stop-start+1, "i82365")) + return 1; + release_region(start, stop-start+1); + } + + return 0; } /*====================================================================*/ @@ -696,7 +698,13 @@ static void __init add_pcic(int ns, int type) struct i82365_socket *t = &socket[sockets-ns]; base = sockets-ns; - if (t->ioaddr > 0) request_region(t->ioaddr, 2, "i82365"); + if (t->ioaddr > 0) { + if (!request_region(t->ioaddr, 2, "i82365")) { + printk(KERN_ERR "i82365: IO region conflict at %#lx, not available\n", + t->ioaddr); + return; + } + } if (base == 0) printk("\n"); printk(KERN_INFO " %s", pcic[type].name); @@ -803,7 +811,7 @@ static void __init isa_probe(void) } #endif - if (check_region(i365_base, 2) != 0) { + if (!request_region(i365_base, 2, "i82365")) { if (sockets == 0) printk("port conflict at %#lx\n", i365_base); return; @@ -1441,6 +1449,7 @@ static void __exit exit_i82365(void) i365_set(i, I365_CSCINT, 0); release_region(socket[i].ioaddr, 2); } + release_region(i365_base, 2); #ifdef CONFIG_PNP if (i82365_pnpdev) pnp_disable_dev(i82365_pnpdev); diff --git a/drivers/pcmcia/pcmcia_compat.c b/drivers/pcmcia/pcmcia_compat.c index 68b80084f83..1cc83317e7e 100644 --- a/drivers/pcmcia/pcmcia_compat.c +++ b/drivers/pcmcia/pcmcia_compat.c @@ -74,19 +74,6 @@ int pcmcia_validate_cis(client_handle_t handle, cisinfo_t *info) } EXPORT_SYMBOL(pcmcia_validate_cis); -int pcmcia_get_configuration_info(client_handle_t handle, - config_info_t *config) -{ - struct pcmcia_socket *s; - - if ((CHECK_HANDLE(handle)) || !config) - return CS_BAD_HANDLE; - s = SOCKET(handle); - if (!s) - return CS_BAD_HANDLE; - return pccard_get_configuration_info(s, handle->Function, config); -} -EXPORT_SYMBOL(pcmcia_get_configuration_info); int pcmcia_reset_card(client_handle_t handle, client_req_t *req) { @@ -102,24 +89,3 @@ int pcmcia_reset_card(client_handle_t handle, client_req_t *req) } EXPORT_SYMBOL(pcmcia_reset_card); -int pcmcia_get_status(client_handle_t handle, cs_status_t *status) -{ - struct pcmcia_socket *s; - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - return pccard_get_status(s, handle->Function, status); -} -EXPORT_SYMBOL(pcmcia_get_status); - -int pcmcia_access_configuration_register(client_handle_t handle, - conf_reg_t *reg) -{ - struct pcmcia_socket *s; - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - return pccard_access_configuration_register(s, handle->Function, reg); -} -EXPORT_SYMBOL(pcmcia_access_configuration_register); - diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c new file mode 100644 index 00000000000..b883bc151ed --- /dev/null +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -0,0 +1,786 @@ +/* + * 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/config.h> +#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/workqueue.h> + +#define IN_CARD_SERVICES +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> +#include <pcmcia/ss.h> + +#include "cs_internal.h" +#include "ds_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; + + +#ifdef DEBUG +extern int ds_pc_debug; +#define cs_socket_name(skt) ((skt)->dev.class_id) + +#define ds_dbg(lvl, fmt, arg...) do { \ + if (ds_pc_debug >= lvl) \ + printk(KERN_DEBUG "ds: " fmt , ## arg); \ +} while (0) +#else +#define ds_dbg(lvl, fmt, arg...) do { } while (0) +#endif + +static const char *release = "Linux Kernel Card Services"; + +/** pcmcia_get_card_services_info + * + * Return information about this version of Card Services + */ +static int pcmcia_get_card_services_info(servinfo_t *info) +{ + unsigned int socket_count = 0; + struct list_head *tmp; + info->Signature[0] = 'C'; + info->Signature[1] = 'S'; + down_read(&pcmcia_socket_list_rwsem); + list_for_each(tmp, &pcmcia_socket_list) + socket_count++; + up_read(&pcmcia_socket_list_rwsem); + info->Count = socket_count; + info->Revision = CS_RELEASE_CODE; + info->CSLevel = 0x0210; + info->VendorString = (char *)release; + return CS_SUCCESS; +} /* get_card_services_info */ + + +/* 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 = NULL; + +static int proc_read_drivers_callback(struct device_driver *driver, void *d) +{ + char **p = d; + struct pcmcia_driver *p_drv = container_of(driver, + struct pcmcia_driver, drv); + + *p += sprintf(*p, "%-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 + ); + d = (void *) p; + + return 0; +} + +static int proc_read_drivers(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + char *p = buf; + + bus_for_each_drv(&pcmcia_bus_type, NULL, + (void *) &p, proc_read_drivers_callback); + + return (p - buf); +} +#endif + +/*====================================================================== + + 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; + unsigned long flags; + + s = pcmcia_get_socket(s); + if (!s) + return -EINVAL; + + ds_dbg(2, "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; + } + + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + 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". */ + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + bind_info->instance = p_dev->instance; + ret = -EBUSY; + goto err_put_module; + } else { + /* the correct driver managed to bind + * itself magically to the correct + * device. */ + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + 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! */ + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + goto rescan; + } + } + } + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + 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. + */ + down(&s->skt_sem); + bus_rescan_devices(&pcmcia_bus_type); + up(&s->skt_sem); + + /* 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) +{ + dev_node_t *node; + struct pcmcia_device *p_dev; + unsigned long flags; + 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 + + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + 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; + } + } + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + return -ENODEV; + + found: + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + if ((!p_dev->instance) || + (p_dev->instance->state & DEV_CONFIG_PENDING)) { + ret = -EAGAIN; + goto err_put; + } + + if (first) + node = p_dev->instance->dev; + else + for (node = p_dev->instance->dev; node; node = node->next) + if (node == bind_info->next) + break; + if (!node) { + ret = -ENODEV; + goto err_put; + } + + strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN); + bind_info->major = node->major; + bind_info->minor = node->minor; + bind_info->next = node->next; + + 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; + + ds_dbg(0, "ds_open(socket %d)\n", i); + + s = pcmcia_get_socket_by_nr(i); + if (!s) + return -ENODEV; + s = pcmcia_get_socket(s); + if (!s) + return -ENODEV; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + if (s->pcmcia_state.busy) { + pcmcia_put_socket(s); + return -EBUSY; + } + else + s->pcmcia_state.busy = 1; + } + + user = kmalloc(sizeof(user_info_t), GFP_KERNEL); + if (!user) { + pcmcia_put_socket(s); + return -ENOMEM; + } + 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 (s->pcmcia_state.present) + queue_event(user, CS_EVENT_CARD_INSERTION); + return 0; +} /* ds_open */ + +/*====================================================================*/ + +static int ds_release(struct inode *inode, struct file *file) +{ + struct pcmcia_socket *s; + user_info_t *user, **link; + + ds_dbg(0, "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; + + ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_dentry->d_inode)); + + if (count < 4) + return -EINVAL; + + user = file->private_data; + if (CHECK_USER(user)) + return -EIO; + + s = user->socket; + if (s->pcmcia_state.dead) + return -EIO; + + 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) +{ + ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_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; + + ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_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 */ + +/*====================================================================*/ + +extern int pcmcia_adjust_resource_info(adjust_t *adj); + +static int ds_ioctl(struct inode * inode, 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; + + ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg); + + user = file->private_data; + if (CHECK_USER(user)) + return -EIO; + + s = user->socket; + if (s->pcmcia_state.dead) + return -EIO; + + 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)) { + ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT); + return -EFAULT; + } + } + if (cmd & IOC_OUT) { + if (!access_ok(VERIFY_WRITE, uarg, size)) { + ds_dbg(3, "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) __copy_from_user((char *)buf, uarg, size); + + switch (cmd) { + case DS_ADJUST_RESOURCE_INFO: + ret = pcmcia_adjust_resource_info(&buf->adjust); + break; + case DS_GET_CARD_SERVICES_INFO: + ret = pcmcia_get_card_services_info(&buf->servinfo); + break; + case DS_GET_CONFIGURATION_INFO: + if (buf->config.Function && + (buf->config.Function >= s->functions)) + ret = CS_BAD_ARGS; + else + ret = pccard_get_configuration_info(s, + buf->config.Function, &buf->config); + break; + case DS_GET_FIRST_TUPLE: + down(&s->skt_sem); + pcmcia_validate_mem(s); + up(&s->skt_sem); + 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 = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse); + break; + case DS_RESET_CARD: + ret = pccard_reset_card(s); + break; + case DS_GET_STATUS: + if (buf->status.Function && + (buf->status.Function >= s->functions)) + ret = CS_BAD_ARGS; + else + ret = pccard_get_status(s, buf->status.Function, &buf->status); + break; + case DS_VALIDATE_CIS: + down(&s->skt_sem); + pcmcia_validate_mem(s); + up(&s->skt_sem); + ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo); + break; + case DS_SUSPEND_CARD: + ret = pcmcia_suspend_card(s); + break; + case DS_RESUME_CARD: + ret = pcmcia_resume_card(s); + break; + case DS_EJECT_CARD: + err = pcmcia_eject_card(s); + break; + case DS_INSERT_CARD: + err = pcmcia_insert_card(s); + break; + case DS_ACCESS_CONFIGURATION_REGISTER: + if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) { + err = -EPERM; + goto free_out; + } + if (buf->conf_reg.Function && + (buf->conf_reg.Function >= s->functions)) + ret = CS_BAD_ARGS; + else + ret = pccard_access_configuration_register(s, + buf->conf_reg.Function, &buf->conf_reg); + 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 { + static int printed = 0; + if (!printed) { + printk(KERN_WARNING "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n"); + printk(KERN_WARNING "MTD handling any more.\n"); + printed++; + } + } + err = -EINVAL; + goto free_out; + break; + case DS_GET_FIRST_WINDOW: + ret = pcmcia_get_window(s, &buf->win_info.handle, 0, + &buf->win_info.window); + break; + case DS_GET_NEXT_WINDOW: + ret = pcmcia_get_window(s, &buf->win_info.handle, + buf->win_info.handle->index + 1, &buf->win_info.window); + break; + case DS_GET_MEM_PAGE: + ret = pcmcia_get_mem_page(buf->win_info.handle, + &buf->win_info.map); + break; + case DS_REPLACE_CIS: + ret = pcmcia_replace_cis(s, &buf->cisdump); + 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 != CS_SUCCESS)) { + ds_dbg(2, "ds_ioctl: ret = %d\n", ret); + switch (ret) { + case CS_BAD_SOCKET: case CS_NO_CARD: + err = -ENODEV; break; + case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ: + case CS_BAD_TUPLE: + err = -EINVAL; break; + case CS_IN_USE: + err = -EBUSY; break; + case CS_OUT_OF_RESOURCE: + err = -ENOSPC; break; + case CS_NO_MORE_ITEMS: + err = -ENODATA; break; + case CS_UNSUPPORTED_FUNCTION: + err = -ENOSYS; 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 struct file_operations ds_fops = { + .owner = THIS_MODULE, + .open = ds_open, + .release = ds_release, + .ioctl = ds_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("pccard", proc_bus); + if (proc_pccard) + create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL); +#endif +} + + +void __exit pcmcia_cleanup_ioctl(void) { +#ifdef CONFIG_PROC_FS + if (proc_pccard) { + remove_proc_entry("drivers", proc_pccard); + remove_proc_entry("pccard", proc_bus); + } +#endif + if (major_dev != -1) + unregister_chrdev(major_dev, "pcmcia"); +} diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c new file mode 100644 index 00000000000..c01dc6bf152 --- /dev/null +++ b/drivers/pcmcia/pcmcia_resource.c @@ -0,0 +1,998 @@ +/* + * PCMCIA 16-bit resource management functions + * + * 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. + * + * Copyright (C) 1999 David A. Hinds + * Copyright (C) 2004-2005 Dominik Brodowski + * + * 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. + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/device.h> + +#define IN_CARD_SERVICES +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/ss.h> +#include <pcmcia/cs.h> +#include <pcmcia/bulkmem.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ds.h> + +#include "cs_internal.h" +#include "ds_internal.h" + + +/* Access speed for IO windows */ +static int io_speed = 0; +module_param(io_speed, int, 0444); + + +#ifdef CONFIG_PCMCIA_PROBE +/* mask of IRQs already reserved by other cards, we should avoid using them */ +static u8 pcmcia_used_irq[NR_IRQS]; +#endif + + +#ifdef DEBUG +extern int ds_pc_debug; +#define cs_socket_name(skt) ((skt)->dev.class_id) + +#define ds_dbg(skt, lvl, fmt, arg...) do { \ + if (ds_pc_debug >= lvl) \ + printk(KERN_DEBUG "pcmcia_resource: %s: " fmt, \ + cs_socket_name(skt) , ## arg); \ +} while (0) +#else +#define ds_dbg(lvl, fmt, arg...) do { } while (0) +#endif + + + +/** alloc_io_space + * + * Special stuff for managing IO windows, because they are scarce + */ + +static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base, + ioaddr_t num, u_int lines) +{ + int i; + kio_addr_t try, align; + + align = (*base) ? (lines ? 1<<lines : 0) : 1; + if (align && (align < num)) { + if (*base) { + ds_dbg(s, 0, "odd IO request: num %#x align %#lx\n", + num, align); + align = 0; + } else + while (align && (align < num)) align <<= 1; + } + if (*base & ~(align-1)) { + ds_dbg(s, 0, "odd IO request: base %#x align %#lx\n", + *base, align); + align = 0; + } + if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) { + *base = s->io_offset | (*base & 0x0fff); + s->io[0].Attributes = attr; + return 0; + } + /* Check for an already-allocated window that must conflict with + * what was asked for. It is a hack because it does not catch all + * potential conflicts, just the most obvious ones. + */ + for (i = 0; i < MAX_IO_WIN; i++) + if ((s->io[i].NumPorts != 0) && + ((s->io[i].BasePort & (align-1)) == *base)) + return 1; + for (i = 0; i < MAX_IO_WIN; i++) { + if (s->io[i].NumPorts == 0) { + s->io[i].res = pcmcia_find_io_region(*base, num, align, s); + if (s->io[i].res) { + s->io[i].Attributes = attr; + s->io[i].BasePort = *base = s->io[i].res->start; + s->io[i].NumPorts = s->io[i].InUse = num; + break; + } else + return 1; + } else if (s->io[i].Attributes != attr) + continue; + /* Try to extend top of window */ + try = s->io[i].BasePort + s->io[i].NumPorts; + if ((*base == 0) || (*base == try)) + if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start, + s->io[i].res->end + num, s) == 0) { + *base = try; + s->io[i].NumPorts += num; + s->io[i].InUse += num; + break; + } + /* Try to extend bottom of window */ + try = s->io[i].BasePort - num; + if ((*base == 0) || (*base == try)) + if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start - num, + s->io[i].res->end, s) == 0) { + s->io[i].BasePort = *base = try; + s->io[i].NumPorts += num; + s->io[i].InUse += num; + break; + } + } + return (i == MAX_IO_WIN); +} /* alloc_io_space */ + + +static void release_io_space(struct pcmcia_socket *s, ioaddr_t base, + ioaddr_t num) +{ + int i; + + for (i = 0; i < MAX_IO_WIN; i++) { + if ((s->io[i].BasePort <= base) && + (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) { + s->io[i].InUse -= num; + /* Free the window if no one else is using it */ + if (s->io[i].InUse == 0) { + s->io[i].NumPorts = 0; + release_resource(s->io[i].res); + kfree(s->io[i].res); + s->io[i].res = NULL; + } + } + } +} /* release_io_space */ + + +/** pccard_access_configuration_register + * + * Access_configuration_register() reads and writes configuration + * registers in attribute memory. Memory window 0 is reserved for + * this and the tuple reading services. + */ + +int pccard_access_configuration_register(struct pcmcia_socket *s, + unsigned int function, + conf_reg_t *reg) +{ + config_t *c; + int addr; + u_char val; + + if (!s || !s->config) + return CS_NO_CARD; + + c = &s->config[function]; + + if (c == NULL) + return CS_NO_CARD; + + if (!(c->state & CONFIG_LOCKED)) + return CS_CONFIGURATION_LOCKED; + + addr = (c->ConfigBase + reg->Offset) >> 1; + + switch (reg->Action) { + case CS_READ: + pcmcia_read_cis_mem(s, 1, addr, 1, &val); + reg->Value = val; + break; + case CS_WRITE: + val = reg->Value; + pcmcia_write_cis_mem(s, 1, addr, 1, &val); + break; + default: + return CS_BAD_ARGS; + break; + } + return CS_SUCCESS; +} /* pccard_access_configuration_register */ + +int pcmcia_access_configuration_register(client_handle_t handle, + conf_reg_t *reg) +{ + struct pcmcia_socket *s; + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + return pccard_access_configuration_register(s, handle->Function, reg); +} +EXPORT_SYMBOL(pcmcia_access_configuration_register); + + + +int pccard_get_configuration_info(struct pcmcia_socket *s, + unsigned int function, + config_info_t *config) +{ + config_t *c; + + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + + config->Function = function; + +#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->irq.AssignedIRQ; + if (config->AssignedIRQ) + config->Attributes |= CONF_ENABLE_IRQ; + config->BasePort1 = s->io[0].BasePort; + config->NumPorts1 = s->io[0].NumPorts; + } + return CS_SUCCESS; + } +#endif + + c = (s->config != NULL) ? &s->config[function] : NULL; + + if ((c == NULL) || !(c->state & CONFIG_LOCKED)) { + config->Attributes = 0; + config->Vcc = s->socket.Vcc; + config->Vpp1 = config->Vpp2 = s->socket.Vpp; + return CS_SUCCESS; + } + + /* !!! This is a hack !!! */ + memcpy(&config->Attributes, &c->Attributes, sizeof(config_t)); + config->Attributes |= CONF_VALID_CLIENT; + config->CardValues = c->CardValues; + config->IRQAttributes = c->irq.Attributes; + config->AssignedIRQ = s->irq.AssignedIRQ; + 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 CS_SUCCESS; +} /* pccard_get_configuration_info */ + +int pcmcia_get_configuration_info(client_handle_t handle, + config_info_t *config) +{ + struct pcmcia_socket *s; + + if ((CHECK_HANDLE(handle)) || !config) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!s) + return CS_BAD_HANDLE; + return pccard_get_configuration_info(s, handle->Function, config); +} +EXPORT_SYMBOL(pcmcia_get_configuration_info); + + +/** pcmcia_get_window + */ +int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, + int idx, win_req_t *req) +{ + window_t *win; + int w; + + if (!s || !(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + for (w = idx; w < MAX_WIN; w++) + if (s->state & SOCKET_WIN_REQ(w)) + break; + if (w == MAX_WIN) + return CS_NO_MORE_ITEMS; + win = &s->win[w]; + req->Base = win->ctl.res->start; + req->Size = win->ctl.res->end - win->ctl.res->start + 1; + req->AccessSpeed = win->ctl.speed; + req->Attributes = 0; + if (win->ctl.flags & MAP_ATTRIB) + req->Attributes |= WIN_MEMORY_TYPE_AM; + if (win->ctl.flags & MAP_ACTIVE) + req->Attributes |= WIN_ENABLE; + if (win->ctl.flags & MAP_16BIT) + req->Attributes |= WIN_DATA_WIDTH_16; + if (win->ctl.flags & MAP_USE_WAIT) + req->Attributes |= WIN_USE_WAIT; + *handle = win; + return CS_SUCCESS; +} /* pcmcia_get_window */ +EXPORT_SYMBOL(pcmcia_get_window); + + +/** 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. + */ + +int pccard_get_status(struct pcmcia_socket *s, unsigned int function, + 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 CS_NO_CARD; + + c = (s->config != NULL) ? &s->config[function] : NULL; + if ((c != NULL) && (c->state & CONFIG_LOCKED) && + (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { + u_char reg; + if (c->Present & PRESENT_PIN_REPLACE) { + pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); + 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->Present & PRESENT_EXT_STATUS) { + pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); + status->CardState |= + (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; + } + return CS_SUCCESS; + } + 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 CS_SUCCESS; +} /* pccard_get_status */ + +int pcmcia_get_status(client_handle_t handle, cs_status_t *status) +{ + struct pcmcia_socket *s; + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + return pccard_get_status(s, handle->Function, status); +} +EXPORT_SYMBOL(pcmcia_get_status); + + + +/** pcmcia_get_mem_page + * + * Change the card address of an already open memory window. + */ +int pcmcia_get_mem_page(window_handle_t win, memreq_t *req) +{ + if ((win == NULL) || (win->magic != WINDOW_MAGIC)) + return CS_BAD_HANDLE; + req->Page = 0; + req->CardOffset = win->ctl.card_start; + return CS_SUCCESS; +} /* pcmcia_get_mem_page */ +EXPORT_SYMBOL(pcmcia_get_mem_page); + + +int pcmcia_map_mem_page(window_handle_t win, memreq_t *req) +{ + struct pcmcia_socket *s; + if ((win == NULL) || (win->magic != WINDOW_MAGIC)) + return CS_BAD_HANDLE; + if (req->Page != 0) + return CS_BAD_PAGE; + s = win->sock; + win->ctl.card_start = req->CardOffset; + if (s->ops->set_mem_map(s, &win->ctl) != 0) + return CS_BAD_OFFSET; + return CS_SUCCESS; +} /* pcmcia_map_mem_page */ +EXPORT_SYMBOL(pcmcia_map_mem_page); + + +/** pcmcia_modify_configuration + * + * Modify a locked socket configuration + */ +int pcmcia_modify_configuration(client_handle_t handle, + modconf_t *mod) +{ + struct pcmcia_socket *s; + config_t *c; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + c = CONFIG(handle); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + if (!(c->state & CONFIG_LOCKED)) + return CS_CONFIGURATION_LOCKED; + + if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { + if (mod->Attributes & CONF_ENABLE_IRQ) { + c->Attributes |= CONF_ENABLE_IRQ; + s->socket.io_irq = s->irq.AssignedIRQ; + } else { + c->Attributes &= ~CONF_ENABLE_IRQ; + s->socket.io_irq = 0; + } + s->ops->set_socket(s, &s->socket); + } + + if (mod->Attributes & CONF_VCC_CHANGE_VALID) + return CS_BAD_VCC; + + /* We only allow changing Vpp1 and Vpp2 to the same value */ + if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) && + (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { + if (mod->Vpp1 != mod->Vpp2) + return CS_BAD_VPP; + c->Vpp1 = c->Vpp2 = s->socket.Vpp = mod->Vpp1; + if (s->ops->set_socket(s, &s->socket)) + return CS_BAD_VPP; + } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) || + (mod->Attributes & CONF_VPP2_CHANGE_VALID)) + return CS_BAD_VPP; + + return CS_SUCCESS; +} /* modify_configuration */ +EXPORT_SYMBOL(pcmcia_modify_configuration); + + +int pcmcia_release_configuration(client_handle_t handle) +{ + pccard_io_map io = { 0, 0, 0, 0, 1 }; + struct pcmcia_socket *s; + int i; + + if (CHECK_HANDLE(handle) || + !(handle->state & CLIENT_CONFIG_LOCKED)) + return CS_BAD_HANDLE; + handle->state &= ~CLIENT_CONFIG_LOCKED; + s = SOCKET(handle); + +#ifdef CONFIG_CARDBUS + if (handle->state & CLIENT_CARDBUS) + return CS_SUCCESS; +#endif + + if (!(handle->state & CLIENT_STALE)) { + config_t *c = CONFIG(handle); + if (--(s->lock_count) == 0) { + s->socket.flags = SS_OUTPUT_ENA; /* Is this correct? */ + s->socket.Vpp = 0; + s->socket.io_irq = 0; + s->ops->set_socket(s, &s->socket); + } + if (c->state & CONFIG_IO_REQ) + for (i = 0; i < MAX_IO_WIN; i++) { + if (s->io[i].NumPorts == 0) + continue; + s->io[i].Config--; + if (s->io[i].Config != 0) + continue; + io.map = i; + s->ops->set_io_map(s, &io); + } + c->state &= ~CONFIG_LOCKED; + } + + return CS_SUCCESS; +} /* pcmcia_release_configuration */ +EXPORT_SYMBOL(pcmcia_release_configuration); + + +/** pcmcia_release_io + * + * Release_io() releases the I/O ranges allocated by a client. This + * may be invoked some time after a card ejection has already dumped + * the actual socket configuration, so if the client is "stale", we + * don't bother checking the port ranges against the current socket + * values. + */ +int pcmcia_release_io(client_handle_t handle, io_req_t *req) +{ + struct pcmcia_socket *s; + + if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ)) + return CS_BAD_HANDLE; + handle->state &= ~CLIENT_IO_REQ; + s = SOCKET(handle); + +#ifdef CONFIG_CARDBUS + if (handle->state & CLIENT_CARDBUS) + return CS_SUCCESS; +#endif + + if (!(handle->state & CLIENT_STALE)) { + config_t *c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + if ((c->io.BasePort1 != req->BasePort1) || + (c->io.NumPorts1 != req->NumPorts1) || + (c->io.BasePort2 != req->BasePort2) || + (c->io.NumPorts2 != req->NumPorts2)) + return CS_BAD_ARGS; + c->state &= ~CONFIG_IO_REQ; + } + + release_io_space(s, req->BasePort1, req->NumPorts1); + if (req->NumPorts2) + release_io_space(s, req->BasePort2, req->NumPorts2); + + return CS_SUCCESS; +} /* pcmcia_release_io */ +EXPORT_SYMBOL(pcmcia_release_io); + + +int pcmcia_release_irq(client_handle_t handle, irq_req_t *req) +{ + struct pcmcia_socket *s; + if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IRQ_REQ)) + return CS_BAD_HANDLE; + handle->state &= ~CLIENT_IRQ_REQ; + s = SOCKET(handle); + + if (!(handle->state & CLIENT_STALE)) { + config_t *c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + if (c->irq.Attributes != req->Attributes) + return CS_BAD_ATTRIBUTE; + if (s->irq.AssignedIRQ != req->AssignedIRQ) + return CS_BAD_IRQ; + if (--s->irq.Config == 0) { + c->state &= ~CONFIG_IRQ_REQ; + s->irq.AssignedIRQ = 0; + } + } + + if (req->Attributes & IRQ_HANDLE_PRESENT) { + free_irq(req->AssignedIRQ, req->Instance); + } + +#ifdef CONFIG_PCMCIA_PROBE + pcmcia_used_irq[req->AssignedIRQ]--; +#endif + + return CS_SUCCESS; +} /* pcmcia_release_irq */ +EXPORT_SYMBOL(pcmcia_release_irq); + + +int pcmcia_release_window(window_handle_t win) +{ + struct pcmcia_socket *s; + + if ((win == NULL) || (win->magic != WINDOW_MAGIC)) + return CS_BAD_HANDLE; + s = win->sock; + if (!(win->handle->state & CLIENT_WIN_REQ(win->index))) + return CS_BAD_HANDLE; + + /* Shut down memory window */ + win->ctl.flags &= ~MAP_ACTIVE; + s->ops->set_mem_map(s, &win->ctl); + s->state &= ~SOCKET_WIN_REQ(win->index); + + /* Release system memory */ + if (win->ctl.res) { + release_resource(win->ctl.res); + kfree(win->ctl.res); + win->ctl.res = NULL; + } + win->handle->state &= ~CLIENT_WIN_REQ(win->index); + + win->magic = 0; + + return CS_SUCCESS; +} /* pcmcia_release_window */ +EXPORT_SYMBOL(pcmcia_release_window); + + +int pcmcia_request_configuration(client_handle_t handle, + config_req_t *req) +{ + int i; + u_int base; + struct pcmcia_socket *s; + config_t *c; + pccard_io_map iomap; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + +#ifdef CONFIG_CARDBUS + if (handle->state & CLIENT_CARDBUS) + return CS_UNSUPPORTED_MODE; +#endif + + if (req->IntType & INT_CARDBUS) + return CS_UNSUPPORTED_MODE; + c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + + /* Do power control. We don't allow changes in Vcc. */ + if (s->socket.Vcc != req->Vcc) + return CS_BAD_VCC; + if (req->Vpp1 != req->Vpp2) + return CS_BAD_VPP; + s->socket.Vpp = req->Vpp1; + if (s->ops->set_socket(s, &s->socket)) + return CS_BAD_VPP; + + c->Vcc = req->Vcc; c->Vpp1 = c->Vpp2 = req->Vpp1; + + /* Pick memory or I/O card, DMA mode, interrupt */ + c->IntType = req->IntType; + c->Attributes = req->Attributes; + if (req->IntType & INT_MEMORY_AND_IO) + s->socket.flags |= SS_IOCARD; + if (req->IntType & INT_ZOOMED_VIDEO) + s->socket.flags |= SS_ZVCARD | SS_IOCARD; + if (req->Attributes & CONF_ENABLE_DMA) + s->socket.flags |= SS_DMA_MODE; + if (req->Attributes & CONF_ENABLE_SPKR) + s->socket.flags |= SS_SPKR_ENA; + if (req->Attributes & CONF_ENABLE_IRQ) + s->socket.io_irq = s->irq.AssignedIRQ; + else + s->socket.io_irq = 0; + s->ops->set_socket(s, &s->socket); + s->lock_count++; + + /* Set up CIS configuration registers */ + base = c->ConfigBase = req->ConfigBase; + c->Present = c->CardValues = req->Present; + if (req->Present & PRESENT_COPY) { + c->Copy = req->Copy; + pcmcia_write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &c->Copy); + } + if (req->Present & PRESENT_OPTION) { + if (s->functions == 1) { + c->Option = req->ConfigIndex & COR_CONFIG_MASK; + } else { + c->Option = req->ConfigIndex & COR_MFC_CONFIG_MASK; + c->Option |= COR_FUNC_ENA|COR_IREQ_ENA; + if (req->Present & PRESENT_IOBASE_0) + c->Option |= COR_ADDR_DECODE; + } + if (c->state & CONFIG_IRQ_REQ) + if (!(c->irq.Attributes & IRQ_FORCED_PULSE)) + c->Option |= COR_LEVEL_REQ; + pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option); + mdelay(40); + } + if (req->Present & PRESENT_STATUS) { + c->Status = req->Status; + pcmcia_write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &c->Status); + } + if (req->Present & PRESENT_PIN_REPLACE) { + c->Pin = req->Pin; + pcmcia_write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &c->Pin); + } + if (req->Present & PRESENT_EXT_STATUS) { + c->ExtStatus = req->ExtStatus; + pcmcia_write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1, &c->ExtStatus); + } + if (req->Present & PRESENT_IOBASE_0) { + u_char b = c->io.BasePort1 & 0xff; + pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b); + b = (c->io.BasePort1 >> 8) & 0xff; + pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b); + } + if (req->Present & PRESENT_IOSIZE) { + u_char b = c->io.NumPorts1 + c->io.NumPorts2 - 1; + pcmcia_write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b); + } + + /* Configure I/O windows */ + if (c->state & CONFIG_IO_REQ) { + iomap.speed = io_speed; + for (i = 0; i < MAX_IO_WIN; i++) + if (s->io[i].NumPorts != 0) { + iomap.map = i; + iomap.flags = MAP_ACTIVE; + switch (s->io[i].Attributes & IO_DATA_PATH_WIDTH) { + case IO_DATA_PATH_WIDTH_16: + iomap.flags |= MAP_16BIT; break; + case IO_DATA_PATH_WIDTH_AUTO: + iomap.flags |= MAP_AUTOSZ; break; + default: + break; + } + iomap.start = s->io[i].BasePort; + iomap.stop = iomap.start + s->io[i].NumPorts - 1; + s->ops->set_io_map(s, &iomap); + s->io[i].Config++; + } + } + + c->state |= CONFIG_LOCKED; + handle->state |= CLIENT_CONFIG_LOCKED; + return CS_SUCCESS; +} /* pcmcia_request_configuration */ +EXPORT_SYMBOL(pcmcia_request_configuration); + + +/** pcmcia_request_io + * + * Request_io() reserves ranges of port addresses for a socket. + * I have not implemented range sharing or alias addressing. + */ +int pcmcia_request_io(client_handle_t handle, io_req_t *req) +{ + struct pcmcia_socket *s; + config_t *c; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + + if (handle->state & CLIENT_CARDBUS) { +#ifdef CONFIG_CARDBUS + handle->state |= CLIENT_IO_REQ; + return CS_SUCCESS; +#else + return CS_UNSUPPORTED_FUNCTION; +#endif + } + + if (!req) + return CS_UNSUPPORTED_MODE; + c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + if (c->state & CONFIG_IO_REQ) + return CS_IN_USE; + if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) + return CS_BAD_ATTRIBUTE; + if ((req->NumPorts2 > 0) && + (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) + return CS_BAD_ATTRIBUTE; + + if (alloc_io_space(s, req->Attributes1, &req->BasePort1, + req->NumPorts1, req->IOAddrLines)) + return CS_IN_USE; + + if (req->NumPorts2) { + if (alloc_io_space(s, req->Attributes2, &req->BasePort2, + req->NumPorts2, req->IOAddrLines)) { + release_io_space(s, req->BasePort1, req->NumPorts1); + return CS_IN_USE; + } + } + + c->io = *req; + c->state |= CONFIG_IO_REQ; + handle->state |= CLIENT_IO_REQ; + return CS_SUCCESS; +} /* pcmcia_request_io */ +EXPORT_SYMBOL(pcmcia_request_io); + + +/** pcmcia_request_irq + * + * Request_irq() reserves an irq for this client. + * + * Also, since Linux only reserves irq's when they are actually + * hooked, we don't guarantee that an irq will still be available + * when the configuration is locked. Now that I think about it, + * there might be a way to fix this using a dummy handler. + */ + +#ifdef CONFIG_PCMCIA_PROBE +static irqreturn_t test_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + return IRQ_NONE; +} +#endif + +int pcmcia_request_irq(client_handle_t handle, irq_req_t *req) +{ + struct pcmcia_socket *s; + config_t *c; + int ret = CS_IN_USE, irq = 0; + struct pcmcia_device *p_dev = handle_to_pdev(handle); + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + if (c->state & CONFIG_IRQ_REQ) + return CS_IN_USE; + +#ifdef CONFIG_PCMCIA_PROBE + if (s->irq.AssignedIRQ != 0) { + /* If the interrupt is already assigned, it must be the same */ + irq = s->irq.AssignedIRQ; + } else { + int try; + u32 mask = s->irq_mask; + void *data = NULL; + + for (try = 0; try < 64; try++) { + irq = try % 32; + + /* marked as available by driver, and not blocked by userspace? */ + if (!((mask >> irq) & 1)) + continue; + + /* avoid an IRQ which is already used by a PCMCIA card */ + if ((try < 32) && pcmcia_used_irq[irq]) + continue; + + /* register the correct driver, if possible, of check whether + * registering a dummy handle works, i.e. if the IRQ isn't + * marked as used by the kernel resource management core */ + ret = request_irq(irq, + (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action, + ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || + (s->functions > 1) || + (irq == s->pci_irq)) ? SA_SHIRQ : 0, + p_dev->dev.bus_id, + (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data); + if (!ret) { + if (!(req->Attributes & IRQ_HANDLE_PRESENT)) + free_irq(irq, data); + break; + } + } + } +#endif + if (ret) { + if (!s->pci_irq) + return ret; + irq = s->pci_irq; + } + + if (ret && req->Attributes & IRQ_HANDLE_PRESENT) { + if (request_irq(irq, req->Handler, + ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || + (s->functions > 1) || + (irq == s->pci_irq)) ? SA_SHIRQ : 0, + p_dev->dev.bus_id, req->Instance)) + return CS_IN_USE; + } + + c->irq.Attributes = req->Attributes; + s->irq.AssignedIRQ = req->AssignedIRQ = irq; + s->irq.Config++; + + c->state |= CONFIG_IRQ_REQ; + handle->state |= CLIENT_IRQ_REQ; + +#ifdef CONFIG_PCMCIA_PROBE + pcmcia_used_irq[irq]++; +#endif + + return CS_SUCCESS; +} /* pcmcia_request_irq */ +EXPORT_SYMBOL(pcmcia_request_irq); + + +/** pcmcia_request_window + * + * Request_window() establishes a mapping between card memory space + * and system memory space. + */ +int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle_t *wh) +{ + struct pcmcia_socket *s; + window_t *win; + u_long align; + int w; + + if (CHECK_HANDLE(*handle)) + return CS_BAD_HANDLE; + s = (*handle)->Socket; + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + if (req->Attributes & (WIN_PAGED | WIN_SHARED)) + return CS_BAD_ATTRIBUTE; + + /* Window size defaults to smallest available */ + if (req->Size == 0) + req->Size = s->map_size; + align = (((s->features & SS_CAP_MEM_ALIGN) || + (req->Attributes & WIN_STRICT_ALIGN)) ? + req->Size : s->map_size); + if (req->Size & (s->map_size-1)) + return CS_BAD_SIZE; + if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) || + (req->Base & (align-1))) + return CS_BAD_BASE; + if (req->Base) + align = 0; + + /* Allocate system memory window */ + for (w = 0; w < MAX_WIN; w++) + if (!(s->state & SOCKET_WIN_REQ(w))) break; + if (w == MAX_WIN) + return CS_OUT_OF_RESOURCE; + + win = &s->win[w]; + win->magic = WINDOW_MAGIC; + win->index = w; + win->handle = *handle; + win->sock = s; + + if (!(s->features & SS_CAP_STATIC_MAP)) { + win->ctl.res = pcmcia_find_mem_region(req->Base, req->Size, align, + (req->Attributes & WIN_MAP_BELOW_1MB), s); + if (!win->ctl.res) + return CS_IN_USE; + } + (*handle)->state |= CLIENT_WIN_REQ(w); + + /* Configure the socket controller */ + win->ctl.map = w+1; + win->ctl.flags = 0; + win->ctl.speed = req->AccessSpeed; + if (req->Attributes & WIN_MEMORY_TYPE) + win->ctl.flags |= MAP_ATTRIB; + if (req->Attributes & WIN_ENABLE) + win->ctl.flags |= MAP_ACTIVE; + if (req->Attributes & WIN_DATA_WIDTH_16) + win->ctl.flags |= MAP_16BIT; + if (req->Attributes & WIN_USE_WAIT) + win->ctl.flags |= MAP_USE_WAIT; + win->ctl.card_start = 0; + if (s->ops->set_mem_map(s, &win->ctl) != 0) + return CS_BAD_ARGS; + s->state |= SOCKET_WIN_REQ(w); + + /* Return window handle */ + if (s->features & SS_CAP_STATIC_MAP) { + req->Base = win->ctl.static_start; + } else { + req->Base = win->ctl.res->start; + } + *wh = win; + + return CS_SUCCESS; +} /* pcmcia_request_window */ +EXPORT_SYMBOL(pcmcia_request_window); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index b6843f8d300..0668384ebc8 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -72,7 +72,7 @@ int pcmcia_adjust_resource_info(adjust_t *adj) /* you can't use the old interface if the new * one was used before */ spin_lock_irqsave(&s->lock, flags); - if ((s->resource_setup_done) && + if ((s->resource_setup_new) && !(s->resource_setup_old)) { spin_unlock_irqrestore(&s->lock, flags); continue; @@ -105,29 +105,32 @@ void pcmcia_validate_mem(struct pcmcia_socket *s) } EXPORT_SYMBOL(pcmcia_validate_mem); -int adjust_io_region(struct resource *res, unsigned long r_start, +int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start, unsigned long r_end, struct pcmcia_socket *s) { if (s->resource_ops->adjust_io_region) return s->resource_ops->adjust_io_region(res, r_start, r_end, s); return -ENOMEM; } +EXPORT_SYMBOL(pcmcia_adjust_io_region); -struct resource *find_io_region(unsigned long base, int num, +struct resource *pcmcia_find_io_region(unsigned long base, int num, unsigned long align, struct pcmcia_socket *s) { if (s->resource_ops->find_io) return s->resource_ops->find_io(base, num, align, s); return NULL; } +EXPORT_SYMBOL(pcmcia_find_io_region); -struct resource *find_mem_region(u_long base, u_long num, u_long align, +struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, int low, struct pcmcia_socket *s) { if (s->resource_ops->find_mem) return s->resource_ops->find_mem(base, num, align, low, s); return NULL; } +EXPORT_SYMBOL(pcmcia_find_mem_region); void release_resource_db(struct pcmcia_socket *s) { diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 5876bab7c14..c42455d20eb 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -372,6 +372,9 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) base, base+num-1); bad = fail = 0; step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff); + /* don't allow too large steps */ + if (step > 0x800000) + step = 0x800000; /* cis_readable wants to map 2x map_size */ if (step < 2 * s->map_size) step = 2 * s->map_size; @@ -465,8 +468,7 @@ static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { mm = *m; - if (do_mem_probe(mm.base, mm.num, s)) - break; + do_mem_probe(mm.base, mm.num, s); } } @@ -601,7 +603,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star ======================================================================*/ -struct resource *nonstatic_find_io_region(unsigned long base, int num, +static struct resource *nonstatic_find_io_region(unsigned long base, int num, unsigned long align, struct pcmcia_socket *s) { struct resource *res = make_resource(0, num, IORESOURCE_IO, s->dev.class_id); @@ -635,8 +637,8 @@ struct resource *nonstatic_find_io_region(unsigned long base, int num, return res; } -struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long align, - int low, struct pcmcia_socket *s) +static struct resource * nonstatic_find_mem_region(u_long base, u_long num, + u_long align, int low, struct pcmcia_socket *s) { struct resource *res = make_resource(0, num, IORESOURCE_MEM, s->dev.class_id); struct socket_data *s_data = s->resource_data; @@ -683,27 +685,23 @@ struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long alig } -static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) +static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) { - u_long base, num; struct socket_data *data = s->resource_data; - int ret; - - base = adj->resource.memory.Base; - num = adj->resource.memory.Size; - if ((num == 0) || (base+num-1 < base)) - return CS_BAD_SIZE; + unsigned long size = end - start + 1; + int ret = 0; - ret = CS_SUCCESS; + if (end <= start) + return -EINVAL; down(&rsrc_sem); - switch (adj->Action) { + switch (action) { case ADD_MANAGED_RESOURCE: - ret = add_interval(&data->mem_db, base, num); + ret = add_interval(&data->mem_db, start, size); break; case REMOVE_MANAGED_RESOURCE: - ret = sub_interval(&data->mem_db, base, num); - if (ret == CS_SUCCESS) { + ret = sub_interval(&data->mem_db, start, size); + if (!ret) { struct pcmcia_socket *socket; down_read(&pcmcia_socket_list_rwsem); list_for_each_entry(socket, &pcmcia_socket_list, socket_list) @@ -712,7 +710,7 @@ static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) } break; default: - ret = CS_UNSUPPORTED_FUNCTION; + ret = -EINVAL; } up(&rsrc_sem); @@ -720,36 +718,35 @@ static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) } -static int adjust_io(struct pcmcia_socket *s, adjust_t *adj) +static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) { struct socket_data *data = s->resource_data; - kio_addr_t base, num; - int ret = CS_SUCCESS; + unsigned long size = end - start + 1; + int ret = 0; - base = adj->resource.io.BasePort; - num = adj->resource.io.NumPorts; - if ((base < 0) || (base > 0xffff)) - return CS_BAD_BASE; - if ((num <= 0) || (base+num > 0x10000) || (base+num <= base)) - return CS_BAD_SIZE; + if (end <= start) + return -EINVAL; + + if (end > IO_SPACE_LIMIT) + return -EINVAL; down(&rsrc_sem); - switch (adj->Action) { + switch (action) { case ADD_MANAGED_RESOURCE: - if (add_interval(&data->io_db, base, num) != 0) { - ret = CS_IN_USE; + if (add_interval(&data->io_db, start, size) != 0) { + ret = -EBUSY; break; } #ifdef CONFIG_PCMCIA_PROBE if (probe_io) - do_io_probe(s, base, num); + do_io_probe(s, start, size); #endif break; case REMOVE_MANAGED_RESOURCE: - sub_interval(&data->io_db, base, num); + sub_interval(&data->io_db, start, size); break; default: - ret = CS_UNSUPPORTED_FUNCTION; + ret = -EINVAL; break; } up(&rsrc_sem); @@ -760,15 +757,82 @@ static int adjust_io(struct pcmcia_socket *s, adjust_t *adj) static int nonstatic_adjust_resource_info(struct pcmcia_socket *s, adjust_t *adj) { + unsigned long end; + switch (adj->Resource) { case RES_MEMORY_RANGE: - return adjust_memory(s, adj); + end = adj->resource.memory.Base + adj->resource.memory.Size - 1; + return adjust_memory(s, adj->Action, adj->resource.memory.Base, end); case RES_IO_RANGE: - return adjust_io(s, adj); + end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1; + return adjust_io(s, adj->Action, adj->resource.io.BasePort, end); } return CS_UNSUPPORTED_FUNCTION; } +#ifdef CONFIG_PCI +static int nonstatic_autoadd_resources(struct pcmcia_socket *s) +{ + struct resource *res; + int i, done = 0; + + if (!s->cb_dev || !s->cb_dev->bus) + return -ENODEV; + +#if defined(CONFIG_X86) || defined(CONFIG_X86_64) + /* If this is the root bus, the risk of hitting + * some strange system devices which aren't protected + * by either ACPI resource tables or properly requested + * resources is too big. Therefore, don't do auto-adding + * of resources at the moment. + */ + if (s->cb_dev->bus->number == 0) + return -EINVAL; +#endif + + for (i=0; i < PCI_BUS_NUM_RESOURCES; i++) { + res = s->cb_dev->bus->resource[i]; + if (!res) + continue; + + if (res->flags & IORESOURCE_IO) { + if (res == &ioport_resource) + continue; + printk(KERN_INFO "pcmcia: parent PCI bridge I/O window: 0x%lx - 0x%lx\n", + res->start, res->end); + if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end)) + done |= IORESOURCE_IO; + + } + + if (res->flags & IORESOURCE_MEM) { + if (res == &iomem_resource) + continue; + printk(KERN_INFO "pcmcia: parent PCI bridge Memory window: 0x%lx - 0x%lx\n", + res->start, res->end); + if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end)) + done |= IORESOURCE_MEM; + } + } + + /* if we got at least one of IO, and one of MEM, we can be glad and + * activate the PCMCIA subsystem */ + if (done & (IORESOURCE_MEM | IORESOURCE_IO)) + s->resource_setup_done = 1; + + return 0; +} + +#else + +static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s) +{ + return -ENODEV; +} + +#endif + + static int nonstatic_init(struct pcmcia_socket *s) { struct socket_data *data; @@ -783,6 +847,8 @@ static int nonstatic_init(struct pcmcia_socket *s) s->resource_data = (void *) data; + nonstatic_autoadd_resources(s); + return 0; } @@ -845,17 +911,16 @@ static ssize_t store_io_db(struct class_device *class_dev, const char *buf, size { struct pcmcia_socket *s = class_get_devdata(class_dev); unsigned long start_addr, end_addr; - unsigned int add = 1; - adjust_t adj; + unsigned int add = ADD_MANAGED_RESOURCE; ssize_t ret = 0; ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); if (ret != 2) { ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); - add = 0; + add = REMOVE_MANAGED_RESOURCE; if (ret != 2) { ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr); - add = 1; + add = ADD_MANAGED_RESOURCE; if (ret != 2) return -EINVAL; } @@ -863,12 +928,9 @@ static ssize_t store_io_db(struct class_device *class_dev, const char *buf, size if (end_addr <= start_addr) return -EINVAL; - adj.Action = add ? ADD_MANAGED_RESOURCE : REMOVE_MANAGED_RESOURCE; - adj.Resource = RES_IO_RANGE; - adj.resource.io.BasePort = start_addr; - adj.resource.io.NumPorts = end_addr - start_addr + 1; - - ret = adjust_io(s, &adj); + ret = adjust_io(s, add, start_addr, end_addr); + if (!ret) + s->resource_setup_new = 1; return ret ? ret : count; } @@ -901,17 +963,16 @@ static ssize_t store_mem_db(struct class_device *class_dev, const char *buf, siz { struct pcmcia_socket *s = class_get_devdata(class_dev); unsigned long start_addr, end_addr; - unsigned int add = 1; - adjust_t adj; + unsigned int add = ADD_MANAGED_RESOURCE; ssize_t ret = 0; ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); if (ret != 2) { ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); - add = 0; + add = REMOVE_MANAGED_RESOURCE; if (ret != 2) { ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr); - add = 1; + add = ADD_MANAGED_RESOURCE; if (ret != 2) return -EINVAL; } @@ -919,12 +980,9 @@ static ssize_t store_mem_db(struct class_device *class_dev, const char *buf, siz if (end_addr <= start_addr) return -EINVAL; - adj.Action = add ? ADD_MANAGED_RESOURCE : REMOVE_MANAGED_RESOURCE; - adj.Resource = RES_MEMORY_RANGE; - adj.resource.memory.Base = start_addr; - adj.resource.memory.Size = end_addr - start_addr + 1; - - ret = adjust_memory(s, &adj); + ret = adjust_memory(s, add, start_addr, end_addr); + if (!ret) + s->resource_setup_new = 1; return ret ? ret : count; } diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index 8eed0393821..fcef54c1c2d 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -163,28 +163,164 @@ static ssize_t pccard_store_resource(struct class_device *dev, const char *buf, return -EINVAL; spin_lock_irqsave(&s->lock, flags); - if (!s->resource_setup_done) { + if (!s->resource_setup_done) s->resource_setup_done = 1; - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); + + down(&s->skt_sem); + if ((s->callback) && + (s->state & SOCKET_PRESENT) && + !(s->state & SOCKET_CARDBUS)) { + if (try_module_get(s->callback->owner)) { + s->callback->requery(s); + module_put(s->callback->owner); + } + } + up(&s->skt_sem); + + return count; +} +static CLASS_DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource); + + +static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, loff_t off, size_t count) +{ + tuple_t tuple; + int status, i; + loff_t pointer = 0; + ssize_t ret = 0; + u_char *tuplebuffer; + u_char *tempbuffer; + + tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL); + if (!tuplebuffer) + return -ENOMEM; + + tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL); + if (!tempbuffer) { + ret = -ENOMEM; + goto free_tuple; + } + + memset(&tuple, 0, sizeof(tuple_t)); + + tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON; + tuple.DesiredTuple = RETURN_FIRST_TUPLE; + tuple.TupleOffset = 0; + + status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple); + while (!status) { + tuple.TupleData = tuplebuffer; + tuple.TupleDataMax = 255; + memset(tuplebuffer, 0, sizeof(u_char) * 255); + status = pccard_get_tuple_data(s, &tuple); + if (status) + break; + + if (off < (pointer + 2 + tuple.TupleDataLen)) { + tempbuffer[0] = tuple.TupleCode & 0xff; + tempbuffer[1] = tuple.TupleLink & 0xff; + for (i = 0; i < tuple.TupleDataLen; i++) + tempbuffer[i + 2] = tuplebuffer[i] & 0xff; + + for (i = 0; i < (2 + tuple.TupleDataLen); i++) { + if (((i + pointer) >= off) && + (i + pointer) < (off + count)) { + buf[ret] = tempbuffer[i]; + ret++; + } + } + } + + pointer += 2 + tuple.TupleDataLen; + + if (pointer >= (off + count)) + break; + + if (tuple.TupleCode == CISTPL_END) + break; + status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple); + } + + kfree(tempbuffer); + free_tuple: + kfree(tuplebuffer); + + return (ret); +} + +static ssize_t pccard_show_cis(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned int size = 0x200; + + if (off >= size) + count = 0; + else { + struct pcmcia_socket *s; + cisinfo_t cisinfo; + + if (off + count > size) + count = size - off; + + s = to_socket(container_of(kobj, struct class_device, kobj)); + + if (!(s->state & SOCKET_PRESENT)) + return -ENODEV; + if (pccard_validate_cis(s, BIND_FN_ALL, &cisinfo)) + return -EIO; + if (!cisinfo.Chains) + return -ENODATA; + + count = pccard_extract_cis(s, buf, off, count); + } + + return (count); +} + +static ssize_t pccard_store_cis(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + struct pcmcia_socket *s = to_socket(container_of(kobj, struct class_device, kobj)); + cisdump_t *cis; + ssize_t ret = count; + + if (off) + return -EINVAL; + + if (count >= 0x200) + return -EINVAL; + + if (!(s->state & SOCKET_PRESENT)) + return -ENODEV; + + cis = kmalloc(sizeof(cisdump_t), GFP_KERNEL); + if (!cis) + return -ENOMEM; + memset(cis, 0, sizeof(cisdump_t)); + + cis->Length = count + 1; + memcpy(cis->Data, buf, count); + + if (pcmcia_replace_cis(s, cis)) + ret = -EIO; + + kfree(cis); + + if (!ret) { down(&s->skt_sem); - if ((s->callback) && - (s->state & SOCKET_PRESENT) && + if ((s->callback) && (s->state & SOCKET_PRESENT) && !(s->state & SOCKET_CARDBUS)) { if (try_module_get(s->callback->owner)) { - s->callback->resources_done(s); + s->callback->requery(s); module_put(s->callback->owner); } } up(&s->skt_sem); - - return count; } - spin_unlock_irqrestore(&s->lock, flags); - return count; + + return (ret); } -static CLASS_DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource); static struct class_device_attribute *pccard_socket_attributes[] = { @@ -199,6 +335,13 @@ static struct class_device_attribute *pccard_socket_attributes[] = { NULL, }; +static struct bin_attribute pccard_cis_attr = { + .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR, .owner = THIS_MODULE}, + .size = 0x200, + .read = pccard_show_cis, + .write = pccard_store_cis, +}; + static int __devinit pccard_sysfs_add_socket(struct class_device *class_dev) { struct class_device_attribute **attr; @@ -209,6 +352,8 @@ static int __devinit pccard_sysfs_add_socket(struct class_device *class_dev) if (ret) break; } + if (!ret) + ret = sysfs_create_bin_file(&class_dev->kobj, &pccard_cis_attr); return ret; } @@ -217,6 +362,7 @@ static void __devexit pccard_sysfs_remove_socket(struct class_device *class_dev) { struct class_device_attribute **attr; + sysfs_remove_bin_file(&class_dev->kobj, &pccard_cis_attr); for (attr = pccard_socket_attributes; *attr; attr++) class_device_remove_file(class_dev, *attr); } diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index bee05362fd2..02b23abc2df 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -549,6 +549,11 @@ static void yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned typ unsigned offset; unsigned mask; + res = socket->dev->resource + PCI_BRIDGE_RESOURCES + nr; + /* Already allocated? */ + if (res->parent) + return 0; + /* The granularity of the memory limit is 4kB, on IO it's 4 bytes */ mask = ~0xfff; if (type & IORESOURCE_IO) @@ -556,7 +561,6 @@ static void yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned typ offset = 0x1c + 8*nr; bus = socket->dev->subordinate; - res = socket->dev->resource + PCI_BRIDGE_RESOURCES + nr; res->name = bus->name; res->flags = type; res->start = 0; |