avma1_cs.c 6.3 KB
Newer Older
L
Linus Torvalds 已提交
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 50
/*
 * PCMCIA client driver for AVM A1 / Fritz!PCMCIA
 *
 * Author       Carsten Paeth
 * Copyright    1998-2001 by Carsten Paeth <calle@calle.in-berlin.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/slab.h>
#include <linux/string.h>
#include <asm/io.h>
#include <asm/system.h>

#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#include "hisax_cfg.h"

MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for AVM A1/Fritz!PCMCIA cards");
MODULE_AUTHOR("Carsten Paeth");
MODULE_LICENSE("GPL");


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

/* Parameters that can be set with 'insmod' */

static int isdnprot = 2;

module_param(isdnprot, int, 0);

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

/*
   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.
*/

51
static int avma1cs_config(struct pcmcia_device *link) __devinit ;
52
static void avma1cs_release(struct pcmcia_device *link);
L
Linus Torvalds 已提交
53 54 55 56 57 58 59

/*
   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.
*/

60
static void avma1cs_detach(struct pcmcia_device *p_dev) __devexit ;
L
Linus Torvalds 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74


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

    avma1cs_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.
    
======================================================================*/

75
static int __devinit avma1cs_probe(struct pcmcia_device *p_dev)
L
Linus Torvalds 已提交
76
{
77
    dev_dbg(&p_dev->dev, "avma1cs_attach()\n");
L
Linus Torvalds 已提交
78 79

    /* General socket configuration */
80
    p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
81 82
    p_dev->config_index = 1;
    p_dev->config_regs = PRESENT_OPTION;
83

84
    return avma1cs_config(p_dev);
L
Linus Torvalds 已提交
85 86 87 88 89 90 91 92 93 94 95
} /* avma1cs_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.

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

96
static void __devexit avma1cs_detach(struct pcmcia_device *link)
L
Linus Torvalds 已提交
97
{
98
	dev_dbg(&link->dev, "avma1cs_detach(0x%p)\n", link);
99 100
	avma1cs_release(link);
	kfree(link->priv);
L
Linus Torvalds 已提交
101 102 103 104 105 106 107 108 109 110
} /* avma1cs_detach */

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

    avma1cs_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.
    
======================================================================*/

111
static int avma1cs_configcheck(struct pcmcia_device *p_dev, void *priv_data)
L
Linus Torvalds 已提交
112
{
113 114 115
	p_dev->resource[0]->end = 16;
	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
116
	p_dev->io_lines = 5;
117

118
	return pcmcia_request_io(p_dev);
L
Linus Torvalds 已提交
119 120 121
}


122
static int __devinit avma1cs_config(struct pcmcia_device *link)
L
Linus Torvalds 已提交
123
{
124
    int i = -1;
L
Linus Torvalds 已提交
125 126 127
    char devname[128];
    IsdnCard_t	icard;
    int busy = 0;
128

129
    dev_dbg(&link->dev, "avma1cs_config(0x%p)\n", link);
L
Linus Torvalds 已提交
130

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

135 136
    if (pcmcia_loop_config(link, avma1cs_configcheck, NULL))
	    return -ENODEV;
L
Linus Torvalds 已提交
137

138
    do {
L
Linus Torvalds 已提交
139 140 141
	/*
	 * allocate an interrupt line
	 */
142
	if (!link->irq) {
143
	    /* undo */
144
	    pcmcia_disable_device(link);
L
Linus Torvalds 已提交
145 146
	    break;
	}
147

L
Linus Torvalds 已提交
148 149 150
	/*
	 * configure the PCMCIA socket
	 */
151
	i = pcmcia_enable_device(link);
D
Dominik Brodowski 已提交
152
	if (i != 0) {
153
	    pcmcia_disable_device(link);
L
Linus Torvalds 已提交
154 155 156 157 158 159 160 161
	    break;
	}

    } while (0);

    /* If any step failed, release any partially configured state */
    if (i != 0) {
	avma1cs_release(link);
162
	return -ENODEV;
L
Linus Torvalds 已提交
163 164
    }

165
    icard.para[0] = link->irq;
166
    icard.para[1] = link->resource[0]->start;
L
Linus Torvalds 已提交
167 168 169 170 171
    icard.protocol = isdnprot;
    icard.typ = ISDN_CTYPE_A1_PCMCIA;
    
    i = hisax_init_pcmcia(link, &busy, &icard);
    if (i < 0) {
172 173 174
	printk(KERN_ERR "avma1_cs: failed to initialize AVM A1 "
			"PCMCIA %d at i/o %#x\n", i,
			(unsigned int) link->resource[0]->start);
L
Linus Torvalds 已提交
175
	avma1cs_release(link);
176
	return -ENODEV;
L
Linus Torvalds 已提交
177
    }
178
    link->priv = (void *) (unsigned long) i;
L
Linus Torvalds 已提交
179

180
    return 0;
L
Linus Torvalds 已提交
181 182 183 184 185 186 187 188 189 190
} /* avma1cs_config */

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

    After a card is removed, avma1cs_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.
    
======================================================================*/

191
static void avma1cs_release(struct pcmcia_device *link)
L
Linus Torvalds 已提交
192
{
193
	unsigned long minor = (unsigned long) link->priv;
L
Linus Torvalds 已提交
194

195
	dev_dbg(&link->dev, "avma1cs_release(0x%p)\n", link);
L
Linus Torvalds 已提交
196

197
	/* now unregister function with hisax */
198
	HiSax_closecard(minor);
L
Linus Torvalds 已提交
199

200
	pcmcia_disable_device(link);
L
Linus Torvalds 已提交
201 202 203
} /* avma1cs_release */


204 205 206 207 208 209 210
static struct pcmcia_device_id avma1cs_ids[] = {
	PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN A", 0x95d42008, 0xadc9d4bb),
	PCMCIA_DEVICE_PROD_ID12("ISDN", "CARD", 0x8d9761c8, 0x01c5aa7b),
	PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, avma1cs_ids);

L
Linus Torvalds 已提交
211 212
static struct pcmcia_driver avma1cs_driver = {
	.owner		= THIS_MODULE,
213
	.name		= "avma1_cs",
214
	.probe		= avma1cs_probe,
215
	.remove		= __devexit_p(avma1cs_detach),
216
	.id_table	= avma1cs_ids,
L
Linus Torvalds 已提交
217
};
218

L
Linus Torvalds 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232
/*====================================================================*/

static int __init init_avma1_cs(void)
{
	return(pcmcia_register_driver(&avma1cs_driver));
}

static void __exit exit_avma1_cs(void)
{
	pcmcia_unregister_driver(&avma1cs_driver);
}

module_init(init_avma1_cs);
module_exit(exit_avma1_cs);