diff options
Diffstat (limited to 'linux/pcmcia-cs/modules/cs.c')
-rw-r--r-- | linux/pcmcia-cs/modules/cs.c | 2399 |
1 files changed, 2399 insertions, 0 deletions
diff --git a/linux/pcmcia-cs/modules/cs.c b/linux/pcmcia-cs/modules/cs.c new file mode 100644 index 00000000..99ee192f --- /dev/null +++ b/linux/pcmcia-cs/modules/cs.c @@ -0,0 +1,2399 @@ +/*====================================================================== + + PCMCIA Card Services -- core services + + cs.c 1.287 2004/04/09 03:54:25 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + 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. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL"), in + which case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/config.h> +#include <linux/string.h> +#include <linux/major.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/proc_fs.h> +#include <linux/pm.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <asm/system.h> +#include <asm/irq.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/bus_ops.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 " [apm]" +#else +#define PM_OPT "" +#endif +#ifdef CONFIG_PNP_BIOS +#define PNP_OPT " [pnp]" +#else +#define PNP_OPT "" +#endif +#if !defined(CONFIG_CARDBUS) && !defined(CONFIG_PCI) && \ + !defined(CONFIG_PM) && !defined(CONFIG_PNP_BIOS) +#define OPTIONS " none" +#else +#define OPTIONS PCI_OPT CB_OPT PM_OPT PNP_OPT +#endif + +static const char *release = "Linux PCMCIA Card Services " CS_RELEASE; +#ifdef UTS_RELEASE +static const char *kernel = "kernel build: " UTS_RELEASE " " UTS_VERSION; +#endif +static const char *options = "options: " OPTIONS; + +/*====================================================================*/ + +/* Module parameters */ + +MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); +MODULE_DESCRIPTION("Linux PCMCIA Card Services " CS_RELEASE + "\n options:" OPTIONS); +MODULE_LICENSE("Dual MPL/GPL"); + +#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i") + +INT_MODULE_PARM(setup_delay, HZ/20); /* ticks */ +INT_MODULE_PARM(resume_delay, HZ/5); /* ticks */ +INT_MODULE_PARM(shutdown_delay, HZ/40); /* ticks */ +INT_MODULE_PARM(vcc_settle, HZ*4/10); /* ticks */ +INT_MODULE_PARM(reset_time, 10); /* usecs */ +INT_MODULE_PARM(unreset_delay, HZ/10); /* ticks */ +INT_MODULE_PARM(unreset_check, HZ/10); /* ticks */ +INT_MODULE_PARM(unreset_limit, 50); /* 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 */ + +/* Optional features */ +#ifdef CONFIG_PM +INT_MODULE_PARM(do_apm, 1); +#endif +#ifdef CONFIG_PNP_BIOS +INT_MODULE_PARM(do_pnp, 1); +#endif + +#ifdef PCMCIA_DEBUG +int pc_debug=PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +static const char *version = +"cs.c 1.287 2004/04/09 03:54:25 (David Hinds)"; +#endif + +/*====================================================================*/ + +static socket_state_t dead_socket = { + 0, SS_DETECT, 0, 0, 0 +}; + +/* Table of sockets */ +socket_t sockets = 0; +socket_info_t *socket_table[MAX_SOCK]; + +#ifdef HAS_PROC_BUS +struct proc_dir_entry *proc_pccard = NULL; +#endif + +/*====================================================================*/ + +/* String tables for error messages */ + +typedef struct lookup_t { + int key; + char *msg; +} lookup_t; + +static const lookup_t error_table[] = { + { CS_SUCCESS, "Operation succeeded" }, + { CS_BAD_ADAPTER, "Bad adapter" }, + { CS_BAD_ATTRIBUTE, "Bad attribute", }, + { CS_BAD_BASE, "Bad base address" }, + { CS_BAD_EDC, "Bad EDC" }, + { CS_BAD_IRQ, "Bad IRQ" }, + { CS_BAD_OFFSET, "Bad offset" }, + { CS_BAD_PAGE, "Bad page number" }, + { CS_READ_FAILURE, "Read failure" }, + { CS_BAD_SIZE, "Bad size" }, + { CS_BAD_SOCKET, "Bad socket" }, + { CS_BAD_TYPE, "Bad type" }, + { CS_BAD_VCC, "Bad Vcc" }, + { CS_BAD_VPP, "Bad Vpp" }, + { CS_BAD_WINDOW, "Bad window" }, + { CS_WRITE_FAILURE, "Write failure" }, + { CS_NO_CARD, "No card present" }, + { CS_UNSUPPORTED_FUNCTION, "Usupported function" }, + { CS_UNSUPPORTED_MODE, "Unsupported mode" }, + { CS_BAD_SPEED, "Bad speed" }, + { CS_BUSY, "Resource busy" }, + { CS_GENERAL_FAILURE, "General failure" }, + { CS_WRITE_PROTECTED, "Write protected" }, + { CS_BAD_ARG_LENGTH, "Bad argument length" }, + { CS_BAD_ARGS, "Bad arguments" }, + { CS_CONFIGURATION_LOCKED, "Configuration locked" }, + { CS_IN_USE, "Resource in use" }, + { CS_NO_MORE_ITEMS, "No more items" }, + { CS_OUT_OF_RESOURCE, "Out of resource" }, + { CS_BAD_HANDLE, "Bad handle" }, + { CS_BAD_TUPLE, "Bad CIS tuple" } +}; +#define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t)) + +static const lookup_t service_table[] = { + { AccessConfigurationRegister, "AccessConfigurationRegister" }, + { AddSocketServices, "AddSocketServices" }, + { AdjustResourceInfo, "AdjustResourceInfo" }, + { CheckEraseQueue, "CheckEraseQueue" }, + { CloseMemory, "CloseMemory" }, + { DeregisterClient, "DeregisterClient" }, + { DeregisterEraseQueue, "DeregisterEraseQueue" }, + { GetCardServicesInfo, "GetCardServicesInfo" }, + { GetClientInfo, "GetClientInfo" }, + { GetConfigurationInfo, "GetConfigurationInfo" }, + { GetEventMask, "GetEventMask" }, + { GetFirstClient, "GetFirstClient" }, + { GetFirstRegion, "GetFirstRegion" }, + { GetFirstTuple, "GetFirstTuple" }, + { GetNextClient, "GetNextClient" }, + { GetNextRegion, "GetNextRegion" }, + { GetNextTuple, "GetNextTuple" }, + { GetStatus, "GetStatus" }, + { GetTupleData, "GetTupleData" }, + { MapMemPage, "MapMemPage" }, + { ModifyConfiguration, "ModifyConfiguration" }, + { ModifyWindow, "ModifyWindow" }, + { OpenMemory, "OpenMemory" }, + { ParseTuple, "ParseTuple" }, + { ReadMemory, "ReadMemory" }, + { RegisterClient, "RegisterClient" }, + { RegisterEraseQueue, "RegisterEraseQueue" }, + { RegisterMTD, "RegisterMTD" }, + { ReleaseConfiguration, "ReleaseConfiguration" }, + { ReleaseIO, "ReleaseIO" }, + { ReleaseIRQ, "ReleaseIRQ" }, + { ReleaseWindow, "ReleaseWindow" }, + { RequestConfiguration, "RequestConfiguration" }, + { RequestIO, "RequestIO" }, + { RequestIRQ, "RequestIRQ" }, + { RequestSocketMask, "RequestSocketMask" }, + { RequestWindow, "RequestWindow" }, + { ResetCard, "ResetCard" }, + { SetEventMask, "SetEventMask" }, + { ValidateCIS, "ValidateCIS" }, + { WriteMemory, "WriteMemory" }, + { BindDevice, "BindDevice" }, + { BindMTD, "BindMTD" }, + { ReportError, "ReportError" }, + { SuspendCard, "SuspendCard" }, + { ResumeCard, "ResumeCard" }, + { EjectCard, "EjectCard" }, + { InsertCard, "InsertCard" }, + { ReplaceCIS, "ReplaceCIS" } +}; +#define SERVICE_COUNT (sizeof(service_table)/sizeof(lookup_t)) + +/*====================================================================== + + Reset a socket to the default state + +======================================================================*/ + +static void init_socket(socket_info_t *s) +{ + int i; + pccard_io_map io = { 0, 0, 0, 0, 1 }; + pccard_mem_map mem = { 0, 0, 0, 0, 0, 0 }; + + mem.sys_stop = s->cap.map_size; + s->socket = dead_socket; + s->ss_entry(s->sock, SS_SetSocket, &s->socket); + for (i = 0; i < 2; i++) { + io.map = i; + s->ss_entry(s->sock, SS_SetIOMap, &io); + } + for (i = 0; i < 5; i++) { + mem.map = i; + s->ss_entry(s->sock, SS_SetMemMap, &mem); + } +} + +/*====================================================================*/ + +#if defined(HAS_PROC_BUS) && defined(PCMCIA_DEBUG) +static int proc_read_clients(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + socket_info_t *s = data; + client_handle_t c; + char *p = buf; + + for (c = s->clients; c; c = c->next) + p += sprintf(p, "fn %x: '%s' [attr 0x%04x] [state 0x%04x]\n", + c->Function, c->dev_info, c->Attributes, c->state); + return (p - buf); +} +#endif + +/*====================================================================== + + Low-level PC Card interface drivers need to register with Card + Services using these calls. + +======================================================================*/ + +static void setup_socket(u_long i); +static void shutdown_socket(u_long i); +static void reset_socket(u_long i); +static void unreset_socket(u_long i); +static void parse_events(void *info, u_int events); + +int register_ss_entry(int nsock, ss_entry_t ss_entry) +{ + int i, ns; + socket_info_t *s; + + DEBUG(0, "cs: register_ss_entry(%d, 0x%p)\n", nsock, ss_entry); + + for (ns = 0; ns < nsock; ns++) { + s = kmalloc(sizeof(struct socket_info_t), GFP_KERNEL); + if (!s) { + printk(KERN_NOTICE "cs: memory allocation failure!\n"); + return (!ns); + } + memset(s, 0, sizeof(socket_info_t)); + + s->ss_entry = ss_entry; + s->sock = ns; + s->setup.data = sockets; + s->setup.function = &setup_socket; + s->shutdown.data = sockets; + s->shutdown.function = &shutdown_socket; + /* base address = 0, map = 0 */ + s->cis_mem.flags = 0; + s->cis_mem.speed = cis_speed; + s->erase_busy.next = s->erase_busy.prev = &s->erase_busy; + spin_lock_init(&s->lock); + + for (i = 0; i < sockets; i++) + if (socket_table[i] == NULL) break; + socket_table[i] = s; + if (i == sockets) sockets++; + + init_socket(s); + ss_entry(ns, SS_InquireSocket, &s->cap); +#ifdef HAS_PROC_BUS + if (proc_pccard) { + char name[3]; + sprintf(name, "%02d", i); + s->proc = proc_mkdir(name, proc_pccard); + if (s->proc) + ss_entry(ns, SS_ProcSetup, s->proc); +#ifdef PCMCIA_DEBUG + if (s->proc) + create_proc_read_entry("clients", 0, s->proc, + proc_read_clients, s); +#endif + } +#endif + } + + return 0; +} /* register_ss_entry */ + +/*====================================================================*/ + +void unregister_ss_entry(ss_entry_t ss_entry) +{ + int i, j; + socket_info_t *s = NULL; + client_t *client; + +#ifdef HAS_PROC_BUS + for (i = 0; i < sockets; i++) { + s = socket_table[i]; + if (s->ss_entry != ss_entry) continue; + if (proc_pccard) { + char name[3]; + sprintf(name, "%02d", i); +#ifdef PCMCIA_DEBUG + remove_proc_entry("clients", s->proc); +#endif + remove_proc_entry(name, proc_pccard); + } + } +#endif + + for (;;) { + for (i = 0; i < sockets; i++) { + s = socket_table[i]; + if (s->ss_entry == ss_entry) break; + } + if (i == sockets) + break; + shutdown_socket(i); + release_cis_mem(s); + while (s->clients) { + client = s->clients; + s->clients = s->clients->next; + kfree(client); + } + s->ss_entry = NULL; + kfree(s); + socket_table[i] = NULL; + for (j = i; j < sockets-1; j++) + socket_table[j] = socket_table[j+1]; + sockets--; + } + +} /* unregister_ss_entry */ + +/*====================================================================== + + Shutdown_Socket() and setup_socket() are scheduled using add_timer + calls by the main event handler when card insertion and removal + events are received. Shutdown_Socket() unconfigures a socket and + turns off socket power. Setup_socket() turns on socket power + and resets the socket, in two stages. + +======================================================================*/ + +static void free_regions(memory_handle_t *list) +{ + memory_handle_t tmp; + while (*list != NULL) { + tmp = *list; + *list = tmp->info.next; + tmp->region_magic = 0; + kfree(tmp); + } +} + +static int send_event(socket_info_t *s, event_t event, int priority); + +static void shutdown_socket(u_long i) +{ + socket_info_t *s = socket_table[i]; + client_t **c; + + DEBUG(1, "cs: shutdown_socket(%ld)\n", i); + + /* Blank out the socket state */ + s->state &= SOCKET_PRESENT|SOCKET_SETUP_PENDING; + init_socket(s); + s->irq.AssignedIRQ = s->irq.Config = 0; + s->lock_count = 0; + s->cis_used = 0; + if (s->fake_cis) { + kfree(s->fake_cis); + s->fake_cis = NULL; + } +#ifdef CONFIG_CARDBUS + cb_release_cis_mem(s); + cb_free(s); +#endif + s->functions = 0; + if (s->config) { + kfree(s->config); + s->config = NULL; + } + for (c = &s->clients; *c; ) { + if ((*c)->state & CLIENT_UNBOUND) { + client_t *d = *c; + *c = (*c)->next; + kfree(d); + } else { + c = &((*c)->next); + } + } + free_regions(&s->a_region); + free_regions(&s->c_region); +} /* shutdown_socket */ + +static void setup_socket(u_long i) +{ + int val; + socket_info_t *s = socket_table[i]; + + s->ss_entry(s->sock, SS_GetStatus, &val); + if (val & SS_PENDING) { + /* Does the socket need more time? */ + DEBUG(2, "cs: setup_socket(%ld): status pending\n", i); + if (++s->setup_timeout > 100) { + printk(KERN_NOTICE "cs: socket %ld voltage interrogation" + " timed out\n", i); + } else { + mod_timer(&s->setup, jiffies + HZ/10); + } + } else if (val & SS_DETECT) { + DEBUG(1, "cs: setup_socket(%ld): applying power\n", i); + s->state |= SOCKET_PRESENT; + s->socket.flags = 0; + if (val & SS_3VCARD) + s->socket.Vcc = s->socket.Vpp = 33; + else if (!(val & SS_XVCARD)) + s->socket.Vcc = s->socket.Vpp = 50; + else { + printk(KERN_NOTICE "cs: socket %ld: unsupported " + "voltage key\n", i); + s->socket.Vcc = 0; + } + if (val & SS_CARDBUS) { + s->state |= SOCKET_CARDBUS; +#ifndef CONFIG_CARDBUS + printk(KERN_NOTICE "cs: unsupported card type detected!\n"); +#endif + } + s->ss_entry(s->sock, SS_SetSocket, &s->socket); + s->setup.function = &reset_socket; + mod_timer(&s->setup, jiffies + vcc_settle); + } else + DEBUG(0, "cs: setup_socket(%ld): no card!\n", i); +} /* setup_socket */ + +/*====================================================================== + + Reset_socket() and unreset_socket() handle hard resets. Resets + have several causes: card insertion, a call to reset_socket, or + recovery from a suspend/resume cycle. Unreset_socket() sends + a CS event that matches the cause of the reset. + +======================================================================*/ + +static void reset_socket(u_long i) +{ + socket_info_t *s = socket_table[i]; + + DEBUG(1, "cs: resetting socket %ld\n", i); + s->socket.flags |= SS_OUTPUT_ENA | SS_RESET; + s->ss_entry(s->sock, SS_SetSocket, &s->socket); + udelay((long)reset_time); + s->socket.flags &= ~SS_RESET; + s->ss_entry(s->sock, SS_SetSocket, &s->socket); + s->setup_timeout = 0; + s->setup.function = &unreset_socket; + mod_timer(&s->setup, jiffies + unreset_delay); +} /* reset_socket */ + +#define EVENT_MASK \ +(SOCKET_SETUP_PENDING|SOCKET_SUSPEND|SOCKET_RESET_PENDING) + +static void unreset_socket(u_long i) +{ + socket_info_t *s = socket_table[i]; + int val; + + s->ss_entry(s->sock, SS_GetStatus, &val); + if (val & SS_READY) { + DEBUG(1, "cs: reset done on socket %ld\n", i); + if (s->state & SOCKET_SUSPEND) { + s->state &= ~EVENT_MASK; + if (verify_cis_cache(s) != 0) + parse_events(s, SS_DETECT); + else + send_event(s, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); + } else if (s->state & SOCKET_SETUP_PENDING) { +#ifdef CONFIG_CARDBUS + if (s->state & SOCKET_CARDBUS) + cb_alloc(s); +#endif + send_event(s, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); + s->state &= ~SOCKET_SETUP_PENDING; + } else { + send_event(s, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW); + if (s->reset_handle) { + s->reset_handle->event_callback_args.info = NULL; + EVENT(s->reset_handle, CS_EVENT_RESET_COMPLETE, + CS_EVENT_PRI_LOW); + s->state &= ~EVENT_MASK; + } + } + } else { + DEBUG(2, "cs: socket %ld not ready yet\n", i); + if (++s->setup_timeout > unreset_limit) { + printk(KERN_NOTICE "cs: socket %ld timed out during" + " reset\n", i); + s->state &= ~EVENT_MASK; + } else { + mod_timer(&s->setup, jiffies + unreset_check); + } + } +} /* unreset_socket */ + +/*====================================================================== + + The central event handler. Send_event() sends an event to all + valid clients. Parse_events() interprets the event bits from + a card status change report. Do_shotdown() handles the high + priority stuff associated with a card removal. + +======================================================================*/ + +static int send_event(socket_info_t *s, event_t event, int priority) +{ + client_t *client = s->clients; + int ret; + DEBUG(1, "cs: send_event(sock %d, event %d, pri %d)\n", + s->sock, event, priority); + ret = 0; + for (; client; client = client->next) { + if (client->state & (CLIENT_UNBOUND|CLIENT_STALE)) + continue; + if (client->EventMask & event) { + ret = EVENT(client, event, priority); + if (ret != 0) + return ret; + } + } + return ret; +} /* send_event */ + +static void do_shutdown(socket_info_t *s) +{ + client_t *client; + if (s->state & SOCKET_SHUTDOWN_PENDING) + return; + s->state |= SOCKET_SHUTDOWN_PENDING; + send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); + for (client = s->clients; client; client = client->next) + if (!(client->Attributes & INFO_MASTER_CLIENT)) + client->state |= CLIENT_STALE; + if (s->state & (SOCKET_SETUP_PENDING|SOCKET_RESET_PENDING)) { + DEBUG(0, "cs: flushing pending setup\n"); + del_timer(&s->setup); + s->state &= ~EVENT_MASK; + } + mod_timer(&s->shutdown, jiffies + shutdown_delay); + s->state &= ~SOCKET_PRESENT; +} + +static void parse_events(void *info, u_int events) +{ + socket_info_t *s = info; + if (events & SS_DETECT) { + int status; + u_long flags; + spin_lock_irqsave(&s->lock, flags); + s->ss_entry(s->sock, SS_GetStatus, &status); + if ((s->state & SOCKET_PRESENT) && + (!(s->state & SOCKET_SUSPEND) || + !(status & SS_DETECT))) + do_shutdown(s); + if (status & SS_DETECT) { + if (s->state & SOCKET_SETUP_PENDING) { + del_timer(&s->setup); + DEBUG(1, "cs: delaying pending setup\n"); + } + s->state |= SOCKET_SETUP_PENDING; + s->setup.function = &setup_socket; + s->setup_timeout = 0; + if (s->state & SOCKET_SUSPEND) + s->setup.expires = jiffies + resume_delay; + else + s->setup.expires = jiffies + setup_delay; + add_timer(&s->setup); + } + spin_unlock_irqrestore(&s->lock, flags); + } + if (events & SS_BATDEAD) + send_event(s, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW); + if (events & SS_BATWARN) + send_event(s, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW); + if (events & SS_READY) { + if (!(s->state & SOCKET_RESET_PENDING)) + send_event(s, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); + else DEBUG(1, "cs: ready change during reset\n"); + } +} /* parse_events */ + +/*====================================================================== + + Another event handler, for power management events. + + This does not comply with the latest PC Card spec for handling + power management events. + +======================================================================*/ + +#ifdef CONFIG_PM +#if (LINUX_VERSION_CODE < VERSION(2,3,43)) +static int handle_pm_event(apm_event_t rqst) +#else +static int handle_pm_event(struct pm_dev *dev, pm_request_t rqst, + void *data) +#endif +{ + int i, stat; + socket_info_t *s; + static int down = 0; + + /* <linux/pm.h> hides a hack so this works with old APM support */ + switch (rqst) { + case PM_SUSPEND: + DEBUG(1, "cs: received suspend notification\n"); + if (down) { + printk(KERN_DEBUG "cs: received extra suspend event\n"); + break; + } + down = 1; + for (i = 0; i < sockets; i++) { + s = socket_table[i]; + if ((s->state & SOCKET_PRESENT) && + !(s->state & SOCKET_SUSPEND)){ + send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); + s->ss_entry(s->sock, SS_SetSocket, &dead_socket); + s->state |= SOCKET_SUSPEND; + } + } + break; + case PM_RESUME: + DEBUG(1, "cs: received resume notification\n"); + if (!down) { + printk(KERN_DEBUG "cs: received bogus resume event\n"); + break; + } + down = 0; + for (i = 0; i < sockets; i++) { + s = socket_table[i]; + /* Do this just to reinitialize the socket */ + init_socket(s); + s->ss_entry(s->sock, SS_GetStatus, &stat); + /* If there was or is a card here, we need to do something + about it... but parse_events will sort it all out. */ + if ((s->state & SOCKET_PRESENT) || (stat & SS_DETECT)) + parse_events(s, SS_DETECT); + } + break; + } + return 0; +} /* handle_pm_event */ +#endif + +/*====================================================================== + + Special stuff for managing IO windows, because they are scarce. + +======================================================================*/ + +static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base, + ioaddr_t num, u_int lines, char *name) +{ + int i; + ioaddr_t try, align; + + align = (*base) ? (lines ? 1<<lines : 0) : 1; + if (align && (align < num)) { + if (*base) { + DEBUG(0, "odd IO request: num %04x align %04x\n", + num, align); + align = 0; + } else + while (align && (align < num)) align <<= 1; + } + if (*base & ~(align-1)) { + DEBUG(0, "odd IO request: base %04x align %04x\n", + *base, align); + while (*base & ~(align-1)) align <<= 1; + } + /* 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) { + if (find_io_region(base, num, align, name) == 0) { + s->io[i].Attributes = attr; + s->io[i].BasePort = *base; + 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 (find_io_region(&try, num, 0, name) == 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 (find_io_region(&try, num, 0, name) == 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(socket_info_t *s, ioaddr_t base, + ioaddr_t num) +{ + int i; + release_region(base, num); + 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; + } + } +} + +/*====================================================================== + + Access_configuration_register() reads and writes configuration + registers in attribute memory. Memory window 0 is reserved for + this and the tuple reading services. + +======================================================================*/ + +static int access_configuration_register(client_handle_t handle, + conf_reg_t *reg) +{ + socket_info_t *s; + config_t *c; + int addr; + u_char val; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + if (handle->Function == BIND_FN_ALL) { + if (reg->Function >= s->functions) + return CS_BAD_ARGS; + c = &s->config[reg->Function]; + } else + c = CONFIG(handle); + 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 */ + +/*====================================================================== + + 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. + +======================================================================*/ + +static int bind_device(bind_req_t *req) +{ + client_t *client; + socket_info_t *s; + + if (CHECK_SOCKET(req->Socket)) + return CS_BAD_SOCKET; + s = SOCKET(req); + + client = (client_t *)kmalloc(sizeof(client_t), GFP_KERNEL); + if (!client) return CS_OUT_OF_RESOURCE; + memset(client, '\0', sizeof(client_t)); + client->client_magic = CLIENT_MAGIC; + strncpy(client->dev_info, (char *)req->dev_info, DEV_NAME_LEN); + client->Socket = req->Socket; + client->Function = req->Function; + client->state = CLIENT_UNBOUND; + client->erase_busy.next = &client->erase_busy; + client->erase_busy.prev = &client->erase_busy; + init_waitqueue_head(&client->mtd_req); + client->next = s->clients; + s->clients = client; + DEBUG(1, "cs: bind_device(): client 0x%p, sock %d, dev %s\n", + client, client->Socket, client->dev_info); + return CS_SUCCESS; +} /* bind_device */ + +/*====================================================================== + + Bind_mtd() associates a device driver with a particular memory + region. It is normally called by Driver Services after it has + identified a memory device type. An instance of the corresponding + driver will then be able to register to control this region. + +======================================================================*/ + +static int bind_mtd(mtd_bind_t *req) +{ + socket_info_t *s; + memory_handle_t region; + + if (CHECK_SOCKET(req->Socket)) + return CS_BAD_SOCKET; + s = SOCKET(req); + + if (req->Attributes & REGION_TYPE_AM) + region = s->a_region; + else + region = s->c_region; + + while (region) { + if (region->info.CardOffset == req->CardOffset) break; + region = region->info.next; + } + if (!region || (region->mtd != NULL)) + return CS_BAD_OFFSET; + strncpy(region->dev_info, (char *)req->dev_info, DEV_NAME_LEN); + + DEBUG(1, "cs: bind_mtd(): attr 0x%x, offset 0x%x, dev %s\n", + req->Attributes, req->CardOffset, (char *)req->dev_info); + return CS_SUCCESS; +} /* bind_mtd */ + +/*====================================================================*/ + +static int deregister_client(client_handle_t handle) +{ + client_t **client; + socket_info_t *s; + memory_handle_t region; + u_long flags; + int i, sn; + + DEBUG(1, "cs: deregister_client(%p)\n", handle); + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + if (handle->state & + (CLIENT_IRQ_REQ|CLIENT_IO_REQ|CLIENT_CONFIG_LOCKED)) + return CS_IN_USE; + for (i = 0; i < MAX_WIN; i++) + if (handle->state & CLIENT_WIN_REQ(i)) + return CS_IN_USE; + + /* Disconnect all MTD links */ + s = SOCKET(handle); + if (handle->mtd_count) { + for (region = s->a_region; region; region = region->info.next) + if (region->mtd == handle) region->mtd = NULL; + for (region = s->c_region; region; region = region->info.next) + if (region->mtd == handle) region->mtd = NULL; + } + + sn = handle->Socket; s = socket_table[sn]; + + if ((handle->state & CLIENT_STALE) || + (handle->Attributes & INFO_MASTER_CLIENT)) { + spin_lock_irqsave(&s->lock, flags); + client = &s->clients; + while ((*client) && ((*client) != handle)) + client = &(*client)->next; + if (*client == NULL) { + spin_unlock_irqrestore(&s->lock, flags); + return CS_BAD_HANDLE; + } + *client = handle->next; + handle->client_magic = 0; + kfree(handle); + spin_unlock_irqrestore(&s->lock, flags); + } else { + handle->state = CLIENT_UNBOUND; + handle->mtd_count = 0; + handle->event_handler = NULL; + } + + if (--s->real_clients == 0) + s->ss_entry(sn, SS_RegisterCallback, NULL); + + return CS_SUCCESS; +} /* deregister_client */ + +/*====================================================================*/ + +static int get_configuration_info(client_handle_t handle, + config_info_t *config) +{ + socket_info_t *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->Function == BIND_FN_ALL) { + if (config->Function && (config->Function >= s->functions)) + return CS_BAD_ARGS; + } else + config->Function = handle->Function; + +#ifdef CONFIG_CARDBUS + if (s->state & SOCKET_CARDBUS) { + u_char fn = config->Function; + memset(config, 0, sizeof(config_info_t)); + config->Function = fn; + config->Vcc = s->socket.Vcc; + config->Vpp1 = config->Vpp2 = s->socket.Vpp; + config->Option = s->cap.cardbus; + config->IntType = INT_CARDBUS; + /* This is a nasty hack */ + pcibios_read_config_dword(s->cap.cardbus, 0, 0, &config->ConfigBase); + if (s->cb_config) { + config->Attributes = CONF_VALID_CLIENT; + 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[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 */ + +/*====================================================================== + + Return information about this version of Card Services. + +======================================================================*/ + +static int get_card_services_info(servinfo_t *info) +{ + info->Signature[0] = 'C'; + info->Signature[1] = 'S'; + info->Count = sockets; + info->Revision = CS_RELEASE_CODE; + info->CSLevel = 0x0210; + info->VendorString = (char *)release; + return CS_SUCCESS; +} /* get_card_services_info */ + +/*====================================================================== + + Note that get_first_client() *does* recognize the Socket field + in the request structure. + +======================================================================*/ + +static int get_first_client(client_handle_t *handle, client_req_t *req) +{ + socket_t s; + if (req->Attributes & CLIENT_THIS_SOCKET) + s = req->Socket; + else + s = 0; + if (CHECK_SOCKET(req->Socket)) + return CS_BAD_SOCKET; + if (socket_table[s]->clients == NULL) + return CS_NO_MORE_ITEMS; + *handle = socket_table[s]->clients; + return CS_SUCCESS; +} /* get_first_client */ + +/*====================================================================*/ + +static int get_next_client(client_handle_t *handle, client_req_t *req) +{ + socket_info_t *s; + if ((handle == NULL) || CHECK_HANDLE(*handle)) + return CS_BAD_HANDLE; + if ((*handle)->next == NULL) { + if (req->Attributes & CLIENT_THIS_SOCKET) + return CS_NO_MORE_ITEMS; + s = SOCKET(*handle); + if (s->clients == NULL) + return CS_NO_MORE_ITEMS; + *handle = s->clients; + } else + *handle = (*handle)->next; + return CS_SUCCESS; +} /* get_next_client */ + +/*====================================================================*/ + +static int get_window(window_handle_t *handle, int idx, win_req_t *req) +{ + socket_info_t *s; + window_t *win; + int w; + + if (idx == 0) + s = SOCKET((client_handle_t)*handle); + else + s = (*handle)->sock; + if (!(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.sys_start; + req->Size = win->ctl.sys_stop - win->ctl.sys_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 */ + +static int get_first_window(client_handle_t *handle, win_req_t *req) +{ + if ((handle == NULL) || CHECK_HANDLE(*handle)) + return CS_BAD_HANDLE; + return get_window((window_handle_t *)handle, 0, req); +} + +static int get_next_window(window_handle_t *win, win_req_t *req) +{ + if ((win == NULL) || ((*win)->magic != WINDOW_MAGIC)) + return CS_BAD_HANDLE; + return get_window(win, (*win)->index+1, req); +} + +/*====================================================================== + + Get the current socket state bits. We don't support the latched + SocketState yet: I haven't seen any point for it. + +======================================================================*/ + +static int cs_get_status(client_handle_t handle, cs_status_t *status) +{ + socket_info_t *s; + config_t *c; + int val; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + s->ss_entry(s->sock, SS_GetStatus, &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; + if (s->state & SOCKET_SETUP_PENDING) + status->CardState |= CS_EVENT_CARD_INSERTION; + + /* Get info from the PRR, if necessary */ + if (handle->Function == BIND_FN_ALL) { + if (status->Function && (status->Function >= s->functions)) + return CS_BAD_ARGS; + c = (s->config != NULL) ? &s->config[status->Function] : NULL; + } else + c = CONFIG(handle); + 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; +} /* cs_get_status */ + +/*====================================================================== + + Change the card address of an already open memory window. + +======================================================================*/ + +static int 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 */ + +static int map_mem_page(window_handle_t win, memreq_t *req) +{ + socket_info_t *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->ss_entry(s->sock, SS_SetMemMap, &win->ctl) != 0) + return CS_BAD_OFFSET; + return CS_SUCCESS; +} /* map_mem_page */ + +/*====================================================================== + + Modify a locked socket configuration + +======================================================================*/ + +static int modify_configuration(client_handle_t handle, + modconf_t *mod) +{ + socket_info_t *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->ss_entry(s->sock, SS_SetSocket, &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->ss_entry(s->sock, SS_SetSocket, &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 */ + +/*====================================================================== + + Modify the attributes of a window returned by RequestWindow. + +======================================================================*/ + +static int modify_window(window_handle_t win, modwin_t *req) +{ + if ((win == NULL) || (win->magic != WINDOW_MAGIC)) + return CS_BAD_HANDLE; + + win->ctl.flags &= ~(MAP_ATTRIB|MAP_ACTIVE); + 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.speed = req->AccessSpeed; + win->sock->ss_entry(win->sock->sock, SS_SetMemMap, &win->ctl); + + return CS_SUCCESS; +} /* modify_window */ + +/*====================================================================== + + 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 register_client(client_handle_t *handle, client_reg_t *req) +{ + client_t *client; + socket_info_t *s; + socket_t ns; + + /* Look for unbound client with matching dev_info */ + client = NULL; + for (ns = 0; ns < sockets; ns++) { + client = socket_table[ns]->clients; + while (client != NULL) { + if ((strcmp(client->dev_info, (char *)req->dev_info) == 0) + && (client->state & CLIENT_UNBOUND)) break; + client = client->next; + } + if (client != NULL) break; + } + if (client == NULL) + return CS_OUT_OF_RESOURCE; + + s = socket_table[ns]; + if (++s->real_clients == 1) { + ss_callback_t call; + int status; + call.handler = &parse_events; + call.info = s; + s->ss_entry(ns, SS_RegisterCallback, &call); + s->ss_entry(ns, SS_GetStatus, &status); + if ((status & SS_DETECT) && + !(s->state & SOCKET_SETUP_PENDING)) { + s->state |= SOCKET_SETUP_PENDING; + setup_socket(ns); + } + } + + *handle = client; + client->state &= ~CLIENT_UNBOUND; + client->Socket = ns; + client->Attributes = req->Attributes; + client->EventMask = req->EventMask; + client->event_handler = req->event_handler; + client->event_callback_args = req->event_callback_args; + client->event_callback_args.client_handle = client; + client->event_callback_args.bus = s->cap.bus; + + if (s->state & SOCKET_CARDBUS) + client->state |= CLIENT_CARDBUS; + + if ((!(s->state & SOCKET_CARDBUS)) && (s->functions == 0) && + (client->Function != BIND_FN_ALL)) { + cistpl_longlink_mfc_t mfc; + if (read_tuple(client, CISTPL_LONGLINK_MFC, &mfc) + == CS_SUCCESS) + s->functions = mfc.nfn; + else + s->functions = 1; + s->config = kmalloc(sizeof(config_t) * s->functions, + GFP_KERNEL); + memset(s->config, 0, sizeof(config_t) * s->functions); + } + + DEBUG(1, "cs: register_client(): client 0x%p, sock %d, dev %s\n", + client, client->Socket, client->dev_info); + if (client->EventMask & CS_EVENT_REGISTRATION_COMPLETE) + EVENT(client, CS_EVENT_REGISTRATION_COMPLETE, CS_EVENT_PRI_LOW); + if ((socket_table[ns]->state & SOCKET_PRESENT) && + !(socket_table[ns]->state & SOCKET_SETUP_PENDING)) { + if (client->EventMask & CS_EVENT_CARD_INSERTION) + EVENT(client, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); + else + client->PendingEvents |= CS_EVENT_CARD_INSERTION; + } + return CS_SUCCESS; +} /* register_client */ + +/*====================================================================*/ + +static int release_configuration(client_handle_t handle, + config_req_t *req) +{ + socket_info_t *s; + pccard_io_map io = { 0, 0, 0, 0, 1 }; + 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) { + cb_disable(s); + s->lock_count = 0; + 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; + s->socket.Vpp = 0; + s->socket.io_irq = 0; + s->ss_entry(s->sock, SS_SetSocket, &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->ss_entry(s->sock, SS_SetIOMap, &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. + +======================================================================*/ + +static int release_io(client_handle_t handle, io_req_t *req) +{ + socket_info_t *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) { + cb_release(s); + 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 */ + +/*====================================================================*/ + +static int cs_release_irq(client_handle_t handle, irq_req_t *req) +{ + socket_info_t *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) { + bus_free_irq(s->cap.bus, req->AssignedIRQ, req->Instance); + } + +#ifdef CONFIG_ISA + if (req->AssignedIRQ != s->cap.pci_irq) + undo_irq(req->Attributes, req->AssignedIRQ); +#endif + + return CS_SUCCESS; +} /* cs_release_irq */ + +/*====================================================================*/ + +static int release_window(window_handle_t win) +{ + socket_info_t *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->ss_entry(s->sock, SS_SetMemMap, &win->ctl); + s->state &= ~SOCKET_WIN_REQ(win->index); + + /* Release system memory */ + release_mem_region(win->base, win->size); + win->handle->state &= ~CLIENT_WIN_REQ(win->index); + + win->magic = 0; + + return CS_SUCCESS; +} /* release_window */ + +/*====================================================================*/ + +static int request_configuration(client_handle_t handle, + config_req_t *req) +{ + int i; + u_int base; + socket_info_t *s; + config_t *c; + pccard_io_map iomap; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + +#ifdef CONFIG_CARDBUS + if (handle->state & CLIENT_CARDBUS) { + if (!(req->IntType & INT_CARDBUS)) + return CS_UNSUPPORTED_MODE; + if (s->lock_count != 0) + return CS_CONFIGURATION_LOCKED; + cb_enable(s); + handle->state |= CLIENT_CONFIG_LOCKED; + s->lock_count++; + return CS_SUCCESS; + } +#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) + printk(KERN_DEBUG "cs: ignoring requested Vcc\n"); + if (req->Vpp1 != req->Vpp2) + return CS_BAD_VPP; + s->socket.Vpp = req->Vpp1; + if (s->ss_entry(s->sock, SS_SetSocket, &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; + 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->ss_entry(s->sock, SS_SetSocket, &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->ss_entry(s->sock, SS_SetIOMap, &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. + +======================================================================*/ + +static int request_io(client_handle_t handle, io_req_t *req) +{ + socket_info_t *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 + int ret = cb_config(s); + if (ret == CS_SUCCESS) + handle->state |= CLIENT_IO_REQ; + return ret; +#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, + handle->dev_info)) + return CS_IN_USE; + + if (req->NumPorts2) { + if (alloc_io_space(s, req->Attributes2, &req->BasePort2, + req->NumPorts2, req->IOAddrLines, + handle->dev_info)) { + 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. + +======================================================================*/ + +static int cs_request_irq(client_handle_t handle, irq_req_t *req) +{ + socket_info_t *s; + config_t *c; + int ret = CS_IN_USE, irq = 0; + + 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_ISA + if (s->irq.AssignedIRQ != 0) { + /* If the interrupt is already assigned, it must match */ + irq = s->irq.AssignedIRQ; + if (req->IRQInfo1 & IRQ_INFO2_VALID) { + u_int mask = req->IRQInfo2 & s->cap.irq_mask; + ret = ((mask >> irq) & 1) ? 0 : CS_BAD_ARGS; + } else + ret = ((req->IRQInfo1&IRQ_MASK) == irq) ? 0 : CS_BAD_ARGS; + } else { + if (req->IRQInfo1 & IRQ_INFO2_VALID) { + u_int try, mask = req->IRQInfo2 & s->cap.irq_mask; + for (try = 0; try < 2; try++) { + for (irq = 0; irq < 16; irq++) + if ((mask >> irq) & 1) { + ret = try_irq(req->Attributes, irq, try); + if (ret == 0) break; + } + if (ret == 0) break; + } + } else { + irq = req->IRQInfo1 & IRQ_MASK; + ret = try_irq(req->Attributes, irq, 1); + } + } +#endif + if (ret != 0) { + if (!s->cap.pci_irq) + return ret; + irq = s->cap.pci_irq; + } + + if (req->Attributes & IRQ_HANDLE_PRESENT) { + if (bus_request_irq(s->cap.bus, irq, req->Handler, + ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || + (s->functions > 1) || + (irq == s->cap.pci_irq)) ? SA_SHIRQ : 0, + handle->dev_info, 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; + return CS_SUCCESS; +} /* cs_request_irq */ + +/*====================================================================== + + Request_window() establishes a mapping between card memory space + and system memory space. + +======================================================================*/ + +static int request_window(client_handle_t *handle, win_req_t *req) +{ + socket_info_t *s; + window_t *win; + u_long align; + int w; + + if (CHECK_HANDLE(*handle)) + return CS_BAD_HANDLE; + s = SOCKET(*handle); + 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->cap.map_size; + align = (((s->cap.features & SS_CAP_MEM_ALIGN) || + (req->Attributes & WIN_STRICT_ALIGN)) ? + req->Size : s->cap.map_size); + if (req->Size & (s->cap.map_size-1)) + return CS_BAD_SIZE; + if ((req->Base && (s->cap.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; + win->base = req->Base; + win->size = req->Size; + + if (!(s->cap.features & SS_CAP_STATIC_MAP) && + find_mem_region(&win->base, win->size, align, + (req->Attributes & WIN_MAP_BELOW_1MB) || + !(s->cap.features & SS_CAP_PAGE_REGS), + (*handle)->dev_info)) + 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.sys_start = win->base; + win->ctl.sys_stop = win->base + win->size-1; + win->ctl.card_start = 0; + if (s->ss_entry(s->sock, SS_SetMemMap, &win->ctl) != 0) + return CS_BAD_ARGS; + s->state |= SOCKET_WIN_REQ(w); + + /* Return window handle */ + req->Base = win->ctl.sys_start; + *handle = (client_handle_t)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. + +======================================================================*/ + +static int reset_card(client_handle_t handle, client_req_t *req) +{ + int i, ret; + socket_info_t *s; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + if (s->state & SOCKET_RESET_PENDING) + return CS_IN_USE; + s->state |= SOCKET_RESET_PENDING; + + ret = send_event(s, CS_EVENT_RESET_REQUEST, CS_EVENT_PRI_LOW); + if (ret != 0) { + s->state &= ~SOCKET_RESET_PENDING; + handle->event_callback_args.info = (void *)(u_long)ret; + EVENT(handle, CS_EVENT_RESET_COMPLETE, CS_EVENT_PRI_LOW); + } else { + DEBUG(1, "cs: resetting socket %d\n", i); + send_event(s, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW); + s->reset_handle = handle; + reset_socket(i); + } + return CS_SUCCESS; +} /* reset_card */ + +/*====================================================================== + + These shut down or wake up a socket. They are sort of user + initiated versions of the APM suspend and resume actions. + +======================================================================*/ + +static int suspend_card(client_handle_t handle, client_req_t *req) +{ + int i; + socket_info_t *s; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + if (s->state & SOCKET_SUSPEND) + return CS_IN_USE; + + DEBUG(1, "cs: suspending socket %d\n", i); + send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); + s->ss_entry(s->sock, SS_SetSocket, &dead_socket); + s->state |= SOCKET_SUSPEND; + + return CS_SUCCESS; +} /* suspend_card */ + +static int resume_card(client_handle_t handle, client_req_t *req) +{ + int i; + socket_info_t *s; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + if (!(s->state & SOCKET_SUSPEND)) + return CS_IN_USE; + + DEBUG(1, "cs: waking up socket %d\n", i); + setup_socket(i); + + return CS_SUCCESS; +} /* resume_card */ + +/*====================================================================== + + These handle user requests to eject or insert a card. + +======================================================================*/ + +static int eject_card(client_handle_t handle, client_req_t *req) +{ + int i, ret; + socket_info_t *s; + u_long flags; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + + DEBUG(1, "cs: user eject request on socket %d\n", i); + + ret = send_event(s, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW); + if (ret != 0) + return ret; + + spin_lock_irqsave(&s->lock, flags); + do_shutdown(s); + spin_unlock_irqrestore(&s->lock, flags); + + return CS_SUCCESS; + +} /* eject_card */ + +static int insert_card(client_handle_t handle, client_req_t *req) +{ + int i, status; + socket_info_t *s; + u_long flags; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + i = handle->Socket; s = socket_table[i]; + if (s->state & SOCKET_PRESENT) + return CS_IN_USE; + + DEBUG(1, "cs: user insert request on socket %d\n", i); + + spin_lock_irqsave(&s->lock, flags); + if (!(s->state & SOCKET_SETUP_PENDING)) { + s->state |= SOCKET_SETUP_PENDING; + spin_unlock_irqrestore(&s->lock, flags); + s->ss_entry(i, SS_GetStatus, &status); + if (status & SS_DETECT) + setup_socket(i); + else { + s->state &= ~SOCKET_SETUP_PENDING; + return CS_NO_CARD; + } + } else + spin_unlock_irqrestore(&s->lock, flags); + + return CS_SUCCESS; +} /* insert_card */ + +/*====================================================================== + + Maybe this should send a CS_EVENT_CARD_INSERTION event if we + haven't sent one to this client yet? + +======================================================================*/ + +static int set_event_mask(client_handle_t handle, eventmask_t *mask) +{ + u_int events, bit; + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + if (handle->Attributes & CONF_EVENT_MASK_VALID) + return CS_BAD_SOCKET; + handle->EventMask = mask->EventMask; + events = handle->PendingEvents & handle->EventMask; + handle->PendingEvents -= events; + while (events != 0) { + bit = ((events ^ (events-1)) + 1) >> 1; + EVENT(handle, bit, CS_EVENT_PRI_LOW); + events -= bit; + } + return CS_SUCCESS; +} /* set_event_mask */ + +/*====================================================================*/ + +static int report_error(client_handle_t handle, error_info_t *err) +{ + int i; + char *serv; + + if (CHECK_HANDLE(handle)) + printk(KERN_NOTICE); + else + printk(KERN_NOTICE "%s: ", handle->dev_info); + + for (i = 0; i < SERVICE_COUNT; i++) + if (service_table[i].key == err->func) break; + if (i < SERVICE_COUNT) + serv = service_table[i].msg; + else + serv = "Unknown service number"; + + for (i = 0; i < ERROR_COUNT; i++) + if (error_table[i].key == err->retcode) break; + if (i < ERROR_COUNT) + printk("%s: %s\n", serv, error_table[i].msg); + else + printk("%s: Unknown error code %#x\n", serv, err->retcode); + + return CS_SUCCESS; +} /* report_error */ + +/*====================================================================*/ + +int CardServices(int func, void *a1, void *a2, void *a3) +{ + +#ifdef PCMCIA_DEBUG + if (pc_debug > 2) { + int i; + for (i = 0; i < SERVICE_COUNT; i++) + if (service_table[i].key == func) break; + if (i < SERVICE_COUNT) + printk(KERN_DEBUG "cs: CardServices(%s, 0x%p, 0x%p)\n", + service_table[i].msg, a1, a2); + else + printk(KERN_DEBUG "cs: CardServices(Unknown func %d, " + "0x%p, 0x%p)\n", func, a1, a2); + } +#endif + switch (func) { + case AccessConfigurationRegister: + return access_configuration_register(a1, a2); break; + case AdjustResourceInfo: + return adjust_resource_info(a1, a2); break; + case CheckEraseQueue: + return check_erase_queue(a1); break; + case CloseMemory: + return close_memory(a1); break; + case CopyMemory: + return copy_memory(a1, a2); break; + case DeregisterClient: + return deregister_client(a1); break; + case DeregisterEraseQueue: + return deregister_erase_queue(a1); break; + case GetFirstClient: + return get_first_client(a1, a2); break; + case GetCardServicesInfo: + return get_card_services_info(a1); break; + case GetConfigurationInfo: + return get_configuration_info(a1, a2); break; + case GetNextClient: + return get_next_client(a1, a2); break; + case GetFirstRegion: + return get_first_region(a1, a2); break; + case GetFirstTuple: + return get_first_tuple(a1, a2); break; + case GetNextRegion: + return get_next_region(a1, a2); break; + case GetNextTuple: + return get_next_tuple(a1, a2); break; + case GetStatus: + return cs_get_status(a1, a2); break; + case GetTupleData: + return get_tuple_data(a1, a2); break; + case MapMemPage: + return map_mem_page(a1, a2); break; + case ModifyConfiguration: + return modify_configuration(a1, a2); break; + case ModifyWindow: + return modify_window(a1, a2); break; + case OpenMemory: + return open_memory(a1, a2); + case ParseTuple: + return parse_tuple(a1, a2, a3); break; + case ReadMemory: + return read_memory(a1, a2, a3); break; + case RegisterClient: + return register_client(a1, a2); break; + case RegisterEraseQueue: + return register_erase_queue(a1, a2); break; + case RegisterMTD: + return register_mtd(a1, a2); break; + case ReleaseConfiguration: + return release_configuration(a1, a2); break; + case ReleaseIO: + return release_io(a1, a2); break; + case ReleaseIRQ: + return cs_release_irq(a1, a2); break; + case ReleaseWindow: + return release_window(a1); break; + case RequestConfiguration: + return request_configuration(a1, a2); break; + case RequestIO: + return request_io(a1, a2); break; + case RequestIRQ: + return cs_request_irq(a1, a2); break; + case RequestWindow: + return request_window(a1, a2); break; + case ResetCard: + return reset_card(a1, a2); break; + case SetEventMask: + return set_event_mask(a1, a2); break; + case ValidateCIS: + return validate_cis(a1, a2); break; + case WriteMemory: + return write_memory(a1, a2, a3); break; + case BindDevice: + return bind_device(a1); break; + case BindMTD: + return bind_mtd(a1); break; + case ReportError: + return report_error(a1, a2); break; + case SuspendCard: + return suspend_card(a1, a2); break; + case ResumeCard: + return resume_card(a1, a2); break; + case EjectCard: + return eject_card(a1, a2); break; + case InsertCard: + return insert_card(a1, a2); break; + case ReplaceCIS: + return replace_cis(a1, a2); break; + case GetFirstWindow: + return get_first_window(a1, a2); break; + case GetNextWindow: + return get_next_window(a1, a2); break; + case GetMemPage: + return get_mem_page(a1, a2); break; + default: + return CS_UNSUPPORTED_FUNCTION; break; + } + +} /* CardServices */ + +/*====================================================================== + + OS-specific module glue goes here + +======================================================================*/ + +#include <linux/pci.h> + +#if (LINUX_VERSION_CODE <= VERSION(2,1,17)) + +#undef CONFIG_MODVERSIONS +static struct symbol_table cs_symtab = { +#include <linux/symtab_begin.h> +#undef X +#define X(sym) { (void *)&sym, SYMBOL_NAME_STR(sym) } + X(register_ss_entry), + X(unregister_ss_entry), + X(CardServices), + X(MTDHelperEntry), +#ifdef HAS_PROC_BUS + X(proc_pccard), +#endif +#ifndef HAVE_MEMRESERVE + X(request_mem_region), + X(release_mem_region), +#endif +#ifdef CONFIG_PNP_BIOS + X(check_pnp_irq), +#endif +#ifdef CONFIG_PCI + X(pci_irq_mask), + X(pci_devices), + X(pci_root), + X(pci_find_slot), + X(pci_find_class), + X(pci_enable_device), + X(pci_set_power_state), +#endif +#include <linux/symtab_end.h> +}; + +#else + +EXPORT_SYMBOL(register_ss_entry); +EXPORT_SYMBOL(unregister_ss_entry); +EXPORT_SYMBOL(CardServices); +EXPORT_SYMBOL(MTDHelperEntry); +#ifdef HAS_PROC_BUS +EXPORT_SYMBOL(proc_pccard); +#endif +#ifndef HAVE_MEMRESERVE +EXPORT_SYMBOL(request_mem_region); +EXPORT_SYMBOL(release_mem_region); +#endif +#ifdef CONFIG_PNP_BIOS +EXPORT_SYMBOL(check_pnp_irq); +#endif +#ifdef CONFIG_PCI +EXPORT_SYMBOL(pci_irq_mask); +#if (LINUX_VERSION_CODE < VERSION(2,3,24)) +EXPORT_SYMBOL(pci_enable_device); +EXPORT_SYMBOL(pci_set_power_state); +#endif +#endif + +#endif + +static int __init init_pcmcia_cs(void) +{ + printk(KERN_INFO "%s\n", release); +#ifdef UTS_RELEASE + printk(KERN_INFO " %s\n", kernel); +#endif + printk(KERN_INFO " %s\n", options); + DEBUG(0, "%s\n", version); +#ifdef CONFIG_PM + if (do_apm) + pm_register(PM_SYS_DEV, PM_SYS_PCMCIA, handle_pm_event); +#endif +#ifdef CONFIG_PCI + pci_fixup_init(); +#endif +#ifdef CONFIG_PNP_BIOS + if (do_pnp) { + pnp_bios_init(); + pnp_proc_init(); + pnp_rsrc_init(); + } +#endif + register_symtab(&cs_symtab); +#ifdef HAS_PROC_BUS + proc_pccard = proc_mkdir("pccard", proc_bus); +#ifdef CONFIG_PNP_BIOS + if (proc_pccard) { + create_proc_read_entry("ioport", 0, proc_pccard, + proc_read_io, NULL); + create_proc_read_entry("irq", 0, proc_pccard, + proc_read_irq, NULL); + } +#endif +#ifndef HAVE_MEMRESERVE + if (proc_pccard) + create_proc_read_entry("memory", 0, proc_pccard, + proc_read_mem, NULL); +#endif +#endif + return 0; +} + +static void __exit exit_pcmcia_cs(void) +{ + printk(KERN_INFO "unloading PCMCIA Card Services\n"); +#ifdef HAS_PROC_BUS + if (proc_pccard) { +#ifdef CONFIG_PNP_BIOS + remove_proc_entry("ioport", proc_pccard); + remove_proc_entry("irq", proc_pccard); +#endif +#ifndef HAVE_MEMRESERVE + remove_proc_entry("memory", proc_pccard); +#endif + remove_proc_entry("pccard", proc_bus); + } +#endif +#ifdef CONFIG_PM + if (do_apm) + pm_unregister_all(handle_pm_event); +#endif +#ifdef CONFIG_PCI + pci_fixup_done(); +#endif +#ifdef CONFIG_PNP_BIOS + if (do_pnp) { + pnp_proc_done(); + pnp_rsrc_done(); + } +#endif + release_resource_db(); +} + +module_init(init_pcmcia_cs); +module_exit(exit_pcmcia_cs); |