linux/drivers/isdn/hisax/teles_cs.c
<<
>>
Prefs
   1/* $Id: teles_cs.c,v 1.1.2.2 2004/01/25 15:07:06 keil Exp $ */
   2/*======================================================================
   3
   4    A teles S0 PCMCIA client driver
   5
   6    Based on skeleton by David Hinds, dhinds@allegro.stanford.edu
   7    Written by Christof Petig, christof.petig@wtal.de
   8    
   9    Also inspired by ELSA PCMCIA driver 
  10    by Klaus Lichtenwalder <Lichtenwalder@ACM.org>
  11    
  12    Extentions to new hisax_pcmcia by Karsten Keil
  13
  14    minor changes to be compatible with kernel 2.4.x
  15    by Jan.Schubert@GMX.li
  16
  17======================================================================*/
  18
  19#include <linux/kernel.h>
  20#include <linux/module.h>
  21#include <linux/init.h>
  22#include <linux/ptrace.h>
  23#include <linux/slab.h>
  24#include <linux/string.h>
  25#include <linux/timer.h>
  26#include <linux/ioport.h>
  27#include <asm/io.h>
  28#include <asm/system.h>
  29
  30#include <pcmcia/cs_types.h>
  31#include <pcmcia/cs.h>
  32#include <pcmcia/cistpl.h>
  33#include <pcmcia/cisreg.h>
  34#include <pcmcia/ds.h>
  35#include "hisax_cfg.h"
  36
  37MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
  38MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
  39MODULE_LICENSE("GPL");
  40
  41/*
  42   All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
  43   you do not define PCMCIA_DEBUG at all, all the debug code will be
  44   left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
  45   be present but disabled -- but it can then be enabled for specific
  46   modules at load time with a 'pc_debug=#' option to insmod.
  47*/
  48
  49#ifdef PCMCIA_DEBUG
  50static int pc_debug = PCMCIA_DEBUG;
  51module_param(pc_debug, int, 0);
  52#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
  53static char *version =
  54"teles_cs.c 2.10 2002/07/30 22:23:34 kkeil";
  55#else
  56#define DEBUG(n, args...)
  57#endif
  58
  59/*====================================================================*/
  60
  61/* Parameters that can be set with 'insmod' */
  62
  63static int protocol = 2;        /* EURO-ISDN Default */
  64module_param(protocol, int, 0);
  65
  66/*====================================================================*/
  67
  68/*
  69   The event() function is this driver's Card Services event handler.
  70   It will be called by Card Services when an appropriate card status
  71   event is received.  The config() and release() entry points are
  72   used to configure or release a socket, in response to card insertion
  73   and ejection events.  They are invoked from the teles_cs event
  74   handler.
  75*/
  76
  77static int teles_cs_config(struct pcmcia_device *link);
  78static void teles_cs_release(struct pcmcia_device *link);
  79
  80/*
  81   The attach() and detach() entry points are used to create and destroy
  82   "instances" of the driver, where each instance represents everything
  83   needed to manage one actual PCMCIA card.
  84*/
  85
  86static void teles_detach(struct pcmcia_device *p_dev);
  87
  88/*
  89   A linked list of "instances" of the teles_cs device.  Each actual
  90   PCMCIA card corresponds to one device instance, and is described
  91   by one struct pcmcia_device structure (defined in ds.h).
  92
  93   You may not want to use a linked list for this -- for example, the
  94   memory card driver uses an array of struct pcmcia_device pointers, where minor
  95   device numbers are used to derive the corresponding array index.
  96*/
  97
  98/*
  99   A driver needs to provide a dev_node_t structure for each device
 100   on a card.  In some cases, there is only one device per card (for
 101   example, ethernet cards, modems).  In other cases, there may be
 102   many actual or logical devices (SCSI adapters, memory cards with
 103   multiple partitions).  The dev_node_t structures need to be kept
 104   in a linked list starting at the 'dev' field of a struct pcmcia_device
 105   structure.  We allocate them in the card's private data structure,
 106   because they generally shouldn't be allocated dynamically.
 107   In this case, we also provide a flag to indicate if a device is
 108   "stopped" due to a power management event, or card ejection.  The
 109   device IO routines can use a flag like this to throttle IO to a
 110   card that is not ready to accept it.
 111*/
 112
 113typedef struct local_info_t {
 114        struct pcmcia_device    *p_dev;
 115    dev_node_t          node;
 116    int                 busy;
 117    int                 cardnr;
 118} local_info_t;
 119
 120/*======================================================================
 121
 122    teles_attach() creates an "instance" of the driver, allocatingx
 123    local data structures for one device.  The device is registered
 124    with Card Services.
 125
 126    The dev_link structure is initialized, but we don't actually
 127    configure the card at this point -- we wait until we receive a
 128    card insertion event.
 129
 130======================================================================*/
 131
 132static int teles_probe(struct pcmcia_device *link)
 133{
 134    local_info_t *local;
 135
 136    DEBUG(0, "teles_attach()\n");
 137
 138    /* Allocate space for private device-specific data */
 139    local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
 140    if (!local) return -ENOMEM;
 141    local->cardnr = -1;
 142
 143    local->p_dev = link;
 144    link->priv = local;
 145
 146    /* Interrupt setup */
 147    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
 148    link->irq.IRQInfo1 = IRQ_LEVEL_ID|IRQ_SHARE_ID;
 149    link->irq.Handler = NULL;
 150
 151    /*
 152      General socket configuration defaults can go here.  In this
 153      client, we assume very little, and rely on the CIS for almost
 154      everything.  In most clients, many details (i.e., number, sizes,
 155      and attributes of IO windows) are fixed by the nature of the
 156      device, and can be hard-wired here.
 157    */
 158    link->io.NumPorts1 = 96;
 159    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
 160    link->io.IOAddrLines = 5;
 161
 162    link->conf.Attributes = CONF_ENABLE_IRQ;
 163    link->conf.IntType = INT_MEMORY_AND_IO;
 164
 165    return teles_cs_config(link);
 166} /* teles_attach */
 167
 168/*======================================================================
 169
 170    This deletes a driver "instance".  The device is de-registered
 171    with Card Services.  If it has been released, all local data
 172    structures are freed.  Otherwise, the structures will be freed
 173    when the device is released.
 174
 175======================================================================*/
 176
 177static void teles_detach(struct pcmcia_device *link)
 178{
 179        local_info_t *info = link->priv;
 180
 181        DEBUG(0, "teles_detach(0x%p)\n", link);
 182
 183        info->busy = 1;
 184        teles_cs_release(link);
 185
 186        kfree(info);
 187} /* teles_detach */
 188
 189/*======================================================================
 190
 191    teles_cs_config() is scheduled to run after a CARD_INSERTION event
 192    is received, to configure the PCMCIA socket, and to make the
 193    device available to the system.
 194
 195======================================================================*/
 196
 197static int teles_cs_configcheck(struct pcmcia_device *p_dev,
 198                                cistpl_cftable_entry_t *cf,
 199                                cistpl_cftable_entry_t *dflt,
 200                                unsigned int vcc,
 201                                void *priv_data)
 202{
 203        int j;
 204
 205        if ((cf->io.nwin > 0) && cf->io.win[0].base) {
 206                printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
 207                p_dev->io.BasePort1 = cf->io.win[0].base;
 208                if (!pcmcia_request_io(p_dev, &p_dev->io))
 209                        return 0;
 210        } else {
 211                printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
 212                for (j = 0x2f0; j > 0x100; j -= 0x10) {
 213                        p_dev->io.BasePort1 = j;
 214                        if (!pcmcia_request_io(p_dev, &p_dev->io))
 215                                return 0;
 216                }
 217        }
 218        return -ENODEV;
 219}
 220
 221static int teles_cs_config(struct pcmcia_device *link)
 222{
 223    local_info_t *dev;
 224    int i, last_fn;
 225    IsdnCard_t icard;
 226
 227    DEBUG(0, "teles_config(0x%p)\n", link);
 228    dev = link->priv;
 229
 230    i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
 231    if (i != 0) {
 232        last_fn = RequestIO;
 233        goto cs_failed;
 234    }
 235
 236    i = pcmcia_request_irq(link, &link->irq);
 237    if (i != 0) {
 238        link->irq.AssignedIRQ = 0;
 239        last_fn = RequestIRQ;
 240        goto cs_failed;
 241    }
 242
 243    i = pcmcia_request_configuration(link, &link->conf);
 244    if (i != 0) {
 245      last_fn = RequestConfiguration;
 246      goto cs_failed;
 247    }
 248
 249    /* At this point, the dev_node_t structure(s) should be
 250       initialized and arranged in a linked list at link->dev. *//*  */
 251    sprintf(dev->node.dev_name, "teles");
 252    dev->node.major = dev->node.minor = 0x0;
 253
 254    link->dev_node = &dev->node;
 255
 256    /* Finally, report what we've done */
 257    printk(KERN_INFO "%s: index 0x%02x:",
 258           dev->node.dev_name, link->conf.ConfigIndex);
 259    if (link->conf.Attributes & CONF_ENABLE_IRQ)
 260        printk(", irq %d", link->irq.AssignedIRQ);
 261    if (link->io.NumPorts1)
 262        printk(", io 0x%04x-0x%04x", link->io.BasePort1,
 263               link->io.BasePort1+link->io.NumPorts1-1);
 264    if (link->io.NumPorts2)
 265        printk(" & 0x%04x-0x%04x", link->io.BasePort2,
 266               link->io.BasePort2+link->io.NumPorts2-1);
 267    printk("\n");
 268
 269    icard.para[0] = link->irq.AssignedIRQ;
 270    icard.para[1] = link->io.BasePort1;
 271    icard.protocol = protocol;
 272    icard.typ = ISDN_CTYPE_TELESPCMCIA;
 273    
 274    i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
 275    if (i < 0) {
 276        printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
 277                i, link->io.BasePort1);
 278        teles_cs_release(link);
 279        return -ENODEV;
 280    }
 281
 282    ((local_info_t*)link->priv)->cardnr = i;
 283    return 0;
 284
 285cs_failed:
 286    cs_error(link, last_fn, i);
 287    teles_cs_release(link);
 288    return -ENODEV;
 289} /* teles_cs_config */
 290
 291/*======================================================================
 292
 293    After a card is removed, teles_cs_release() will unregister the net
 294    device, and release the PCMCIA configuration.  If the device is
 295    still open, this will be postponed until it is closed.
 296
 297======================================================================*/
 298
 299static void teles_cs_release(struct pcmcia_device *link)
 300{
 301    local_info_t *local = link->priv;
 302
 303    DEBUG(0, "teles_cs_release(0x%p)\n", link);
 304
 305    if (local) {
 306        if (local->cardnr >= 0) {
 307            /* no unregister function with hisax */
 308            HiSax_closecard(local->cardnr);
 309        }
 310    }
 311
 312    pcmcia_disable_device(link);
 313} /* teles_cs_release */
 314
 315static int teles_suspend(struct pcmcia_device *link)
 316{
 317        local_info_t *dev = link->priv;
 318
 319        dev->busy = 1;
 320
 321        return 0;
 322}
 323
 324static int teles_resume(struct pcmcia_device *link)
 325{
 326        local_info_t *dev = link->priv;
 327
 328        dev->busy = 0;
 329
 330        return 0;
 331}
 332
 333
 334static struct pcmcia_device_id teles_ids[] = {
 335        PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
 336        PCMCIA_DEVICE_NULL,
 337};
 338MODULE_DEVICE_TABLE(pcmcia, teles_ids);
 339
 340static struct pcmcia_driver teles_cs_driver = {
 341        .owner          = THIS_MODULE,
 342        .drv            = {
 343                .name   = "teles_cs",
 344        },
 345        .probe          = teles_probe,
 346        .remove         = teles_detach,
 347        .id_table       = teles_ids,
 348        .suspend        = teles_suspend,
 349        .resume         = teles_resume,
 350};
 351
 352static int __init init_teles_cs(void)
 353{
 354        return pcmcia_register_driver(&teles_cs_driver);
 355}
 356
 357static void __exit exit_teles_cs(void)
 358{
 359        pcmcia_unregister_driver(&teles_cs_driver);
 360}
 361
 362module_init(init_teles_cs);
 363module_exit(exit_teles_cs);
 364