avm_cs.c 6.61 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $
 *
 * A PCMCIA client driver for AVM B1/M1/M2
 *
 * Copyright 1999 by Carsten Paeth <calle@calle.de>
 *
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/ptrace.h>
#include <linux/string.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/major.h>
#include <asm/io.h>
#include <asm/system.h>

#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>

#include <linux/skbuff.h>
#include <linux/capi.h>
#include <linux/b1lli.h>
#include <linux/b1pcmcia.h>

/*====================================================================*/

MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
MODULE_AUTHOR("Carsten Paeth");
MODULE_LICENSE("GPL");

/*====================================================================*/

/*
   The event() function is this driver's Card Services event handler.
   It will be called by Card Services when an appropriate card status
   event is received.  The config() and release() entry points are
   used to configure or release a socket, in response to card insertion
   and ejection events.  They are invoked from the skeleton event
   handler.
*/

50
static int avmcs_config(struct pcmcia_device *link);
51
static void avmcs_release(struct pcmcia_device *link);
Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55 56 57 58

/*
   The attach() and detach() entry points are used to create and destroy
   "instances" of the driver, where each instance represents everything
   needed to manage one actual PCMCIA card.
*/

59
static void avmcs_detach(struct pcmcia_device *p_dev);
Linus Torvalds's avatar
Linus Torvalds committed
60 61 62 63 64 65 66 67 68 69 70 71 72

/*======================================================================

    avmcs_attach() creates an "instance" of the driver, allocating
    local data structures for one device.  The device is registered
    with Card Services.

    The dev_link structure is initialized, but we don't actually
    configure the card at this point -- we wait until we receive a
    card insertion event.
    
======================================================================*/

73
static int avmcs_probe(struct pcmcia_device *p_dev)
Linus Torvalds's avatar
Linus Torvalds committed
74
{
75

Linus Torvalds's avatar
Linus Torvalds committed
76
    /* The io structure describes IO port mapping */
77 78
    p_dev->resource[0]->end = 16;
    p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
Linus Torvalds's avatar
Linus Torvalds committed
79 80

    /* General socket configuration */
81
    p_dev->config_flags |= CONF_ENABLE_IRQ;
82 83
    p_dev->config_index = 1;
    p_dev->config_regs = PRESENT_OPTION;
Linus Torvalds's avatar
Linus Torvalds committed
84

85
    return avmcs_config(p_dev);
Linus Torvalds's avatar
Linus Torvalds committed
86 87 88 89 90 91 92 93 94 95 96
} /* avmcs_attach */

/*======================================================================

    This deletes a driver "instance".  The device is de-registered
    with Card Services.  If it has been released, all local data
    structures are freed.  Otherwise, the structures will be freed
    when the device is released.

======================================================================*/

97
static void avmcs_detach(struct pcmcia_device *link)
Linus Torvalds's avatar
Linus Torvalds committed
98
{
99
	avmcs_release(link);
Linus Torvalds's avatar
Linus Torvalds committed
100 101 102 103 104 105 106 107 108 109
} /* avmcs_detach */

/*======================================================================

    avmcs_config() is scheduled to run after a CARD_INSERTION event
    is received, to configure the PCMCIA socket, and to make the
    ethernet device available to the system.
    
======================================================================*/

110 111
static int avmcs_configcheck(struct pcmcia_device *p_dev,
			     cistpl_cftable_entry_t *cf,
112
			     cistpl_cftable_entry_t *dflt,
113
			     unsigned int vcc,
114
			     void *priv_data)
Linus Torvalds's avatar
Linus Torvalds committed
115
{
116 117 118
	if (cf->io.nwin <= 0)
		return -ENODEV;

119 120 121
	p_dev->resource[0]->start = cf->io.win[0].base;
	p_dev->resource[0]->end = cf->io.win[0].len;
	return pcmcia_request_io(p_dev);
Linus Torvalds's avatar
Linus Torvalds committed
122 123
}

124
static int avmcs_config(struct pcmcia_device *link)
Linus Torvalds's avatar
Linus Torvalds committed
125
{
126
    int i = -1;
Linus Torvalds's avatar
Linus Torvalds committed
127 128 129
    char devname[128];
    int cardtype;
    int (*addcard)(unsigned int port, unsigned irq);
130

131 132 133
    devname[0] = 0;
    if (link->prod_id[1])
	    strlcpy(devname, link->prod_id[1], sizeof(devname));
134

135 136 137 138 139
    /*
     * find IO port
     */
    if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
	    return -ENODEV;
140

141
    do {
142
	if (!link->irq) {
143
	    /* undo */
144
	    pcmcia_disable_device(link);
Linus Torvalds's avatar
Linus Torvalds committed
145 146
	    break;
	}
147

Linus Torvalds's avatar
Linus Torvalds committed
148 149 150
	/*
         * configure the PCMCIA socket
	  */
151
	i = pcmcia_enable_device(link);
152
	if (i != 0) {
153
	    pcmcia_disable_device(link);
Linus Torvalds's avatar
Linus Torvalds committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
	    break;
	}

    } while (0);

    if (devname[0]) {
	char *s = strrchr(devname, ' ');
	if (!s)
	   s = devname;
	else s++;
        if (strcmp("M1", s) == 0) {
           cardtype = AVM_CARDTYPE_M1;
        } else if (strcmp("M2", s) == 0) {
           cardtype = AVM_CARDTYPE_M2;
	} else {
           cardtype = AVM_CARDTYPE_B1;
	}
171
    } else
Linus Torvalds's avatar
Linus Torvalds committed
172
        cardtype = AVM_CARDTYPE_B1;
173

Linus Torvalds's avatar
Linus Torvalds committed
174 175 176
    /* If any step failed, release any partially configured state */
    if (i != 0) {
	avmcs_release(link);
177
	return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
178 179 180 181 182 183 184 185 186
    }


    switch (cardtype) {
        case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
        case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
	default:
        case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
    }
187 188 189 190
    if ((i = (*addcard)(link->resource[0]->start, link->irq)) < 0) {
	    dev_err(&link->dev,
		    "avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n",
		    (unsigned int) link->resource[0]->start, link->irq);
191 192
	    avmcs_release(link);
	    return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
193
    }
194
    return 0;
Linus Torvalds's avatar
Linus Torvalds committed
195 196 197 198 199 200 201 202 203 204 205

} /* avmcs_config */

/*======================================================================

    After a card is removed, avmcs_release() will unregister the net
    device, and release the PCMCIA configuration.  If the device is
    still open, this will be postponed until it is closed.
    
======================================================================*/

206
static void avmcs_release(struct pcmcia_device *link)
Linus Torvalds's avatar
Linus Torvalds committed
207
{
208
	b1pcmcia_delcard(link->resource[0]->start, link->irq);
209
	pcmcia_disable_device(link);
Linus Torvalds's avatar
Linus Torvalds committed
210 211 212
} /* avmcs_release */


213 214 215 216 217 218 219 220
static struct pcmcia_device_id avmcs_ids[] = {
	PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
	PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
	PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
	PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);

Linus Torvalds's avatar
Linus Torvalds committed
221 222 223 224 225
static struct pcmcia_driver avmcs_driver = {
	.owner	= THIS_MODULE,
	.drv	= {
		.name	= "avm_cs",
	},
226
	.probe = avmcs_probe,
227
	.remove	= avmcs_detach,
228
	.id_table = avmcs_ids,
Linus Torvalds's avatar
Linus Torvalds committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242
};

static int __init avmcs_init(void)
{
	return pcmcia_register_driver(&avmcs_driver);
}

static void __exit avmcs_exit(void)
{
	pcmcia_unregister_driver(&avmcs_driver);
}

module_init(avmcs_init);
module_exit(avmcs_exit);