avm_cs.c revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * A PCMCIA client driver for AVM B1/M1/M2
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright 1999 by Carsten Paeth <calle@calle.de>
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This software may be used and distributed according to the terms
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * of the GNU General Public License, incorporated herein by reference.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sched.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ptrace.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serial.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/major.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/system.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/version.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/cs_types.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/cs.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/cistpl.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/ciscode.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/ds.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <pcmcia/cisreg.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/skbuff.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/capi.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/b1lli.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/b1pcmcia.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*====================================================================*/
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Carsten Paeth");
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*====================================================================*/
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   The event() function is this driver's Card Services event handler.
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   It will be called by Card Services when an appropriate card status
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   event is received.  The config() and release() entry points are
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   used to configure or release a socket, in response to card insertion
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   and ejection events.  They are invoked from the skeleton event
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   handler.
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void avmcs_config(dev_link_t *link);
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void avmcs_release(dev_link_t *link);
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int avmcs_event(event_t event, int priority,
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			  event_callback_args_t *args);
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   The attach() and detach() entry points are used to create and destroy
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   "instances" of the driver, where each instance represents everything
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   needed to manage one actual PCMCIA card.
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic dev_link_t *avmcs_attach(void);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void avmcs_detach(dev_link_t *);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   The dev_info variable is the "key" that is used to match up this
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   device driver with appropriate cards, through the card configuration
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   database.
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic dev_info_t dev_info = "avm_cs";
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   A linked list of "instances" of the skeleton device.  Each actual
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   PCMCIA card corresponds to one device instance, and is described
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   by one dev_link_t structure (defined in ds.h).
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   You may not want to use a linked list for this -- for example, the
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   memory card driver uses an array of dev_link_t pointers, where minor
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   device numbers are used to derive the corresponding array index.
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic dev_link_t *dev_list = NULL;
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   A dev_link_t structure has fields for most things that are needed
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   to keep track of a socket, but there will usually be some device
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   specific information that also needs to be kept track of.  The
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   'priv' pointer in a dev_link_t structure can be used to point to
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   a device-specific private data structure, like this.
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   A driver needs to provide a dev_node_t structure for each device
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   on a card.  In some cases, there is only one device per card (for
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   example, ethernet cards, modems).  In other cases, there may be
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   many actual or logical devices (SCSI adapters, memory cards with
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   multiple partitions).  The dev_node_t structures need to be kept
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   in a linked list starting at the 'dev' field of a dev_link_t
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   structure.  We allocate them in the card's private data structure,
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   because they generally can't be allocated dynamically.
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldstypedef struct local_info_t {
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev_node_t	node;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} local_info_t;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*======================================================================
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    avmcs_attach() creates an "instance" of the driver, allocating
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    local data structures for one device.  The device is registered
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    with Card Services.
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    The dev_link structure is initialized, but we don't actually
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    configure the card at this point -- we wait until we receive a
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    card insertion event.
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds======================================================================*/
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic dev_link_t *avmcs_attach(void)
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    client_reg_t client_reg;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev_link_t *link;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    local_info_t *local;
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int ret;
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Initialize the dev_link_t structure */
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (!link)
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        goto err;
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    memset(link, 0, sizeof(struct dev_link_t));
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* The io structure describes IO port mapping */
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->io.NumPorts1 = 16;
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->io.NumPorts2 = 0;
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Interrupt setup */
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* General socket configuration */
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->conf.Attributes = CONF_ENABLE_IRQ;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->conf.Vcc = 50;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->conf.IntType = INT_MEMORY_AND_IO;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->conf.ConfigIndex = 1;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->conf.Present = PRESENT_OPTION;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Allocate space for private device-specific data */
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (!local)
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        goto err_kfree;
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    memset(local, 0, sizeof(local_info_t));
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->priv = local;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Register with Card Services */
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->next = dev_list;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev_list = link;
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    client_reg.dev_info = &dev_info;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    client_reg.EventMask =
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    client_reg.event_handler = &avmcs_event;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    client_reg.Version = 0x0210;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    client_reg.event_callback_args.client_data = link;
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ret = pcmcia_register_client(&link->handle, &client_reg);
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (ret != 0) {
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cs_error(link->handle, RegisterClient, ret);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	avmcs_detach(link);
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto err;
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return link;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err_kfree:
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    kfree(link);
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err:
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return NULL;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} /* avmcs_attach */
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*======================================================================
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    This deletes a driver "instance".  The device is de-registered
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    with Card Services.  If it has been released, all local data
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    structures are freed.  Otherwise, the structures will be freed
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    when the device is released.
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds======================================================================*/
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void avmcs_detach(dev_link_t *link)
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev_link_t **linkp;
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Locate device structure */
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (*linkp == link) break;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (*linkp == NULL)
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /*
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       If the device is currently configured and active, we won't
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       actually delete it yet.  Instead, it is marked so that when
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       the release() function is called, that will trigger a proper
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       detach().
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    */
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (link->state & DEV_CONFIG) {
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	link->state |= DEV_STALE_LINK;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Break the link with Card Services */
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (link->handle)
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pcmcia_deregister_client(link->handle);
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Unlink device structure, free pieces */
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *linkp = link->next;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (link->priv) {
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(link->priv);
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    kfree(link);
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} /* avmcs_detach */
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*======================================================================
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    avmcs_config() is scheduled to run after a CARD_INSERTION event
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    is received, to configure the PCMCIA socket, and to make the
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ethernet device available to the system.
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds======================================================================*/
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_tuple(client_handle_t handle, tuple_t *tuple,
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     cisparse_t *parse)
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int i = pcmcia_get_tuple_data(handle, tuple);
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (i != CS_SUCCESS) return i;
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return pcmcia_parse_tuple(handle, tuple, parse);
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int first_tuple(client_handle_t handle, tuple_t *tuple,
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     cisparse_t *parse)
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int i = pcmcia_get_first_tuple(handle, tuple);
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (i != CS_SUCCESS) return i;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return get_tuple(handle, tuple, parse);
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int next_tuple(client_handle_t handle, tuple_t *tuple,
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		     cisparse_t *parse)
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int i = pcmcia_get_next_tuple(handle, tuple);
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (i != CS_SUCCESS) return i;
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return get_tuple(handle, tuple, parse);
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void avmcs_config(dev_link_t *link)
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    client_handle_t handle;
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tuple_t tuple;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cisparse_t parse;
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    local_info_t *dev;
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int i;
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    u_char buf[64];
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    char devname[128];
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int cardtype;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int (*addcard)(unsigned int port, unsigned irq);
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    handle = link->handle;
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev = link->priv;
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /*
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       This reads the card's CONFIG tuple to find its configuration
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       registers.
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    */
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    do {
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.DesiredTuple = CISTPL_CONFIG;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = pcmcia_get_first_tuple(handle, &tuple);
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i != CS_SUCCESS) break;
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.TupleData = buf;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.TupleDataMax = 64;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.TupleOffset = 0;
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = pcmcia_get_tuple_data(handle, &tuple);
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i != CS_SUCCESS) break;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = pcmcia_parse_tuple(handle, &tuple, &parse);
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i != CS_SUCCESS) break;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	link->conf.ConfigBase = parse.config.base;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } while (0);
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (i != CS_SUCCESS) {
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cs_error(link->handle, ParseTuple, i);
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	link->state &= ~DEV_CONFIG_PENDING;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Configure card */
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->state |= DEV_CONFIG;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    do {
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.Attributes = 0;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.TupleData = buf;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.TupleDataMax = 254;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.TupleOffset = 0;
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.DesiredTuple = CISTPL_VERS_1;
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	devname[0] = 0;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) {
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1],
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sizeof(devname));
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         * find IO port
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         */
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.TupleData = (cisdata_t *)buf;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.Attributes = 0;
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = first_tuple(handle, &tuple, &parse);
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (i == CS_SUCCESS) {
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    if (cf->io.nwin > 0) {
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		link->conf.ConfigIndex = cf->index;
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		link->io.BasePort1 = cf->io.win[0].base;
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		link->io.NumPorts1 = cf->io.win[0].len;
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		link->io.NumPorts2 = 0;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			link->io.BasePort1,
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		        link->io.BasePort1+link->io.NumPorts1-1);
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		i = pcmcia_request_io(link->handle, &link->io);
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (i == CS_SUCCESS) goto found_port;
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    }
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    i = next_tuple(handle, &tuple, &parse);
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsfound_port:
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i != CS_SUCCESS) {
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    cs_error(link->handle, RequestIO, i);
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    break;
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * allocate an interrupt line
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = pcmcia_request_irq(link->handle, &link->irq);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i != CS_SUCCESS) {
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    cs_error(link->handle, RequestIRQ, i);
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    pcmcia_release_io(link->handle, &link->io);
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    break;
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         * configure the PCMCIA socket
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  */
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = pcmcia_request_configuration(link->handle, &link->conf);
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (i != CS_SUCCESS) {
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    cs_error(link->handle, RequestConfiguration, i);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    pcmcia_release_io(link->handle, &link->io);
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    pcmcia_release_irq(link->handle, &link->irq);
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    break;
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } while (0);
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* At this point, the dev_node_t structure(s) should be
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds       initialized and arranged in a linked list at link->dev. */
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (devname[0]) {
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *s = strrchr(devname, ' ');
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!s)
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   s = devname;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else s++;
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	strcpy(dev->node.dev_name, s);
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if (strcmp("M1", s) == 0) {
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds           cardtype = AVM_CARDTYPE_M1;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        } else if (strcmp("M2", s) == 0) {
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds           cardtype = AVM_CARDTYPE_M2;
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds           cardtype = AVM_CARDTYPE_B1;
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        strcpy(dev->node.dev_name, "b1");
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        cardtype = AVM_CARDTYPE_B1;
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->node.major = 64;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->node.minor = 0;
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->dev = &dev->node;
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->state &= ~DEV_CONFIG_PENDING;
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* If any step failed, release any partially configured state */
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (i != 0) {
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	avmcs_release(link);
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    switch (cardtype) {
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if ((i = (*addcard)(link->io.BasePort1, link->irq.AssignedIRQ)) < 0) {
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n",
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ);
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	avmcs_release(link);
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->node.minor = i;
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} /* avmcs_config */
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*======================================================================
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    After a card is removed, avmcs_release() will unregister the net
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    device, and release the PCMCIA configuration.  If the device is
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    still open, this will be postponed until it is closed.
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds======================================================================*/
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void avmcs_release(dev_link_t *link)
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ);
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Unlink the device chain */
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->dev = NULL;
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* Don't bother checking to see if these succeed or not */
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    pcmcia_release_configuration(link->handle);
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    pcmcia_release_io(link->handle, &link->io);
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    pcmcia_release_irq(link->handle, &link->irq);
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    link->state &= ~DEV_CONFIG;
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (link->state & DEV_STALE_LINK)
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	avmcs_detach(link);
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} /* avmcs_release */
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*======================================================================
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    The card status event handler.  Mostly, this schedules other
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    stuff to run after an event is received.  A CARD_REMOVAL event
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    also sets some flags to discourage the net drivers from trying
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    to talk to the card any more.
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    When a CARD_REMOVAL event is received, we immediately set a flag
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    to block future accesses to this device.  All the functions that
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    actually access the device should check this flag to make sure
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    the card is still present.
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds======================================================================*/
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int avmcs_event(event_t event, int priority,
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			  event_callback_args_t *args)
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev_link_t *link = args->client_data;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    switch (event) {
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case CS_EVENT_CARD_REMOVAL:
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	link->state &= ~DEV_PRESENT;
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (link->state & DEV_CONFIG)
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		avmcs_release(link);
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case CS_EVENT_CARD_INSERTION:
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	avmcs_config(link);
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case CS_EVENT_PM_SUSPEND:
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	link->state |= DEV_SUSPEND;
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Fall through... */
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case CS_EVENT_RESET_PHYSICAL:
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (link->state & DEV_CONFIG)
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    pcmcia_release_configuration(link->handle);
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case CS_EVENT_PM_RESUME:
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	link->state &= ~DEV_SUSPEND;
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Fall through... */
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case CS_EVENT_CARD_RESET:
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (link->state & DEV_CONFIG)
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    pcmcia_request_configuration(link->handle, &link->conf);
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} /* avmcs_event */
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pcmcia_driver avmcs_driver = {
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner	= THIS_MODULE,
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.drv	= {
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.name	= "avm_cs",
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.attach	= avmcs_attach,
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.detach	= avmcs_detach,
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init avmcs_init(void)
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return pcmcia_register_driver(&avmcs_driver);
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit avmcs_exit(void)
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pcmcia_unregister_driver(&avmcs_driver);
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	BUG_ON(dev_list != NULL);
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(avmcs_init);
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(avmcs_exit);
511