linux/drivers/isdn/hisax/avma1_cs.c
<<
>>
Prefs
   1/*
   2 * PCMCIA client driver for AVM A1 / Fritz!PCMCIA
   3 *
   4 * Author       Carsten Paeth
   5 * Copyright    1998-2001 by Carsten Paeth <calle@calle.in-berlin.de>
   6 *
   7 * This software may be used and distributed according to the terms
   8 * of the GNU General Public License, incorporated herein by reference.
   9 *
  10 */
  11
  12#include <linux/module.h>
  13
  14
  15#include <linux/kernel.h>
  16#include <linux/init.h>
  17#include <linux/ptrace.h>
  18#include <linux/slab.h>
  19#include <linux/string.h>
  20#include <asm/io.h>
  21
  22#include <pcmcia/cistpl.h>
  23#include <pcmcia/ds.h>
  24#include "hisax_cfg.h"
  25
  26MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for AVM A1/Fritz!PCMCIA cards");
  27MODULE_AUTHOR("Carsten Paeth");
  28MODULE_LICENSE("GPL");
  29
  30
  31/*====================================================================*/
  32
  33/* Parameters that can be set with 'insmod' */
  34
  35static int isdnprot = 2;
  36
  37module_param(isdnprot, int, 0);
  38
  39/*====================================================================*/
  40
  41static int avma1cs_config(struct pcmcia_device *link);
  42static void avma1cs_release(struct pcmcia_device *link);
  43static void avma1cs_detach(struct pcmcia_device *p_dev);
  44
  45static int avma1cs_probe(struct pcmcia_device *p_dev)
  46{
  47        dev_dbg(&p_dev->dev, "avma1cs_attach()\n");
  48
  49        /* General socket configuration */
  50        p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
  51        p_dev->config_index = 1;
  52        p_dev->config_regs = PRESENT_OPTION;
  53
  54        return avma1cs_config(p_dev);
  55} /* avma1cs_attach */
  56
  57static void avma1cs_detach(struct pcmcia_device *link)
  58{
  59        dev_dbg(&link->dev, "avma1cs_detach(0x%p)\n", link);
  60        avma1cs_release(link);
  61        kfree(link->priv);
  62} /* avma1cs_detach */
  63
  64static int avma1cs_configcheck(struct pcmcia_device *p_dev, void *priv_data)
  65{
  66        p_dev->resource[0]->end = 16;
  67        p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
  68        p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
  69        p_dev->io_lines = 5;
  70
  71        return pcmcia_request_io(p_dev);
  72}
  73
  74
  75static int avma1cs_config(struct pcmcia_device *link)
  76{
  77        int i = -1;
  78        char devname[128];
  79        IsdnCard_t      icard;
  80        int busy = 0;
  81
  82        dev_dbg(&link->dev, "avma1cs_config(0x%p)\n", link);
  83
  84        devname[0] = 0;
  85        if (link->prod_id[1])
  86                strlcpy(devname, link->prod_id[1], sizeof(devname));
  87
  88        if (pcmcia_loop_config(link, avma1cs_configcheck, NULL))
  89                return -ENODEV;
  90
  91        do {
  92                /*
  93                 * allocate an interrupt line
  94                 */
  95                if (!link->irq) {
  96                        /* undo */
  97                        pcmcia_disable_device(link);
  98                        break;
  99                }
 100
 101                /*
 102                 * configure the PCMCIA socket
 103                 */
 104                i = pcmcia_enable_device(link);
 105                if (i != 0) {
 106                        pcmcia_disable_device(link);
 107                        break;
 108                }
 109
 110        } while (0);
 111
 112        /* If any step failed, release any partially configured state */
 113        if (i != 0) {
 114                avma1cs_release(link);
 115                return -ENODEV;
 116        }
 117
 118        icard.para[0] = link->irq;
 119        icard.para[1] = link->resource[0]->start;
 120        icard.protocol = isdnprot;
 121        icard.typ = ISDN_CTYPE_A1_PCMCIA;
 122
 123        i = hisax_init_pcmcia(link, &busy, &icard);
 124        if (i < 0) {
 125                printk(KERN_ERR "avma1_cs: failed to initialize AVM A1 "
 126                       "PCMCIA %d at i/o %#x\n", i,
 127                       (unsigned int) link->resource[0]->start);
 128                avma1cs_release(link);
 129                return -ENODEV;
 130        }
 131        link->priv = (void *) (unsigned long) i;
 132
 133        return 0;
 134} /* avma1cs_config */
 135
 136static void avma1cs_release(struct pcmcia_device *link)
 137{
 138        unsigned long minor = (unsigned long) link->priv;
 139
 140        dev_dbg(&link->dev, "avma1cs_release(0x%p)\n", link);
 141
 142        /* now unregister function with hisax */
 143        HiSax_closecard(minor);
 144
 145        pcmcia_disable_device(link);
 146} /* avma1cs_release */
 147
 148static const struct pcmcia_device_id avma1cs_ids[] = {
 149        PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN A", 0x95d42008, 0xadc9d4bb),
 150        PCMCIA_DEVICE_PROD_ID12("ISDN", "CARD", 0x8d9761c8, 0x01c5aa7b),
 151        PCMCIA_DEVICE_NULL
 152};
 153MODULE_DEVICE_TABLE(pcmcia, avma1cs_ids);
 154
 155static struct pcmcia_driver avma1cs_driver = {
 156        .owner          = THIS_MODULE,
 157        .name           = "avma1_cs",
 158        .probe          = avma1cs_probe,
 159        .remove         = avma1cs_detach,
 160        .id_table       = avma1cs_ids,
 161};
 162module_pcmcia_driver(avma1cs_driver);
 163