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