• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/drivers/isdn/hisax/
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#include <asm/system.h>
22
23#include <pcmcia/cs.h>
24#include <pcmcia/cistpl.h>
25#include <pcmcia/ds.h>
26#include "hisax_cfg.h"
27
28MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for AVM A1/Fritz!PCMCIA cards");
29MODULE_AUTHOR("Carsten Paeth");
30MODULE_LICENSE("GPL");
31
32
33/*====================================================================*/
34
35/* Parameters that can be set with 'insmod' */
36
37static int isdnprot = 2;
38
39module_param(isdnprot, int, 0);
40
41/*====================================================================*/
42
43/*
44   The event() function is this driver's Card Services event handler.
45   It will be called by Card Services when an appropriate card status
46   event is received.  The config() and release() entry points are
47   used to configure or release a socket, in response to card insertion
48   and ejection events.  They are invoked from the skeleton event
49   handler.
50*/
51
52static int avma1cs_config(struct pcmcia_device *link) __devinit ;
53static void avma1cs_release(struct pcmcia_device *link);
54
55/*
56   The attach() and detach() entry points are used to create and destroy
57   "instances" of the driver, where each instance represents everything
58   needed to manage one actual PCMCIA card.
59*/
60
61static void avma1cs_detach(struct pcmcia_device *p_dev) __devexit ;
62
63
64/*======================================================================
65
66    avma1cs_attach() creates an "instance" of the driver, allocating
67    local data structures for one device.  The device is registered
68    with Card Services.
69
70    The dev_link structure is initialized, but we don't actually
71    configure the card at this point -- we wait until we receive a
72    card insertion event.
73
74======================================================================*/
75
76static int __devinit avma1cs_probe(struct pcmcia_device *p_dev)
77{
78    dev_dbg(&p_dev->dev, "avma1cs_attach()\n");
79
80    /* The io structure describes IO port mapping */
81    p_dev->resource[0]->end = 16;
82    p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
83    p_dev->resource[1]->end = 16;
84    p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
85
86    /* General socket configuration */
87    p_dev->conf.Attributes = CONF_ENABLE_IRQ;
88    p_dev->conf.IntType = INT_MEMORY_AND_IO;
89    p_dev->conf.ConfigIndex = 1;
90    p_dev->conf.Present = PRESENT_OPTION;
91
92    return avma1cs_config(p_dev);
93} /* avma1cs_attach */
94
95/*======================================================================
96
97    This deletes a driver "instance".  The device is de-registered
98    with Card Services.  If it has been released, all local data
99    structures are freed.  Otherwise, the structures will be freed
100    when the device is released.
101
102======================================================================*/
103
104static void __devexit avma1cs_detach(struct pcmcia_device *link)
105{
106	dev_dbg(&link->dev, "avma1cs_detach(0x%p)\n", link);
107	avma1cs_release(link);
108	kfree(link->priv);
109} /* avma1cs_detach */
110
111/*======================================================================
112
113    avma1cs_config() is scheduled to run after a CARD_INSERTION event
114    is received, to configure the PCMCIA socket, and to make the
115    ethernet device available to the system.
116
117======================================================================*/
118
119static int avma1cs_configcheck(struct pcmcia_device *p_dev,
120			       cistpl_cftable_entry_t *cf,
121			       cistpl_cftable_entry_t *dflt,
122			       unsigned int vcc,
123			       void *priv_data)
124{
125	if (cf->io.nwin <= 0)
126		return -ENODEV;
127
128	p_dev->resource[0]->start = cf->io.win[0].base;
129	p_dev->resource[0]->end = cf->io.win[0].len;
130	p_dev->io_lines = 5;
131	return pcmcia_request_io(p_dev);
132}
133
134
135static int __devinit avma1cs_config(struct pcmcia_device *link)
136{
137    int i = -1;
138    char devname[128];
139    IsdnCard_t	icard;
140    int busy = 0;
141
142    dev_dbg(&link->dev, "avma1cs_config(0x%p)\n", link);
143
144    devname[0] = 0;
145    if (link->prod_id[1])
146	    strlcpy(devname, link->prod_id[1], sizeof(devname));
147
148    if (pcmcia_loop_config(link, avma1cs_configcheck, NULL))
149	    return -ENODEV;
150
151    do {
152	/*
153	 * allocate an interrupt line
154	 */
155	if (!link->irq) {
156	    /* undo */
157	    pcmcia_disable_device(link);
158	    break;
159	}
160
161	/*
162	 * configure the PCMCIA socket
163	 */
164	i = pcmcia_request_configuration(link, &link->conf);
165	if (i != 0) {
166	    pcmcia_disable_device(link);
167	    break;
168	}
169
170    } while (0);
171
172    /* If any step failed, release any partially configured state */
173    if (i != 0) {
174	avma1cs_release(link);
175	return -ENODEV;
176    }
177
178    printk(KERN_NOTICE "avma1_cs: checking at i/o %#x, irq %d\n",
179		(unsigned int) link->resource[0]->start, link->irq);
180
181    icard.para[0] = link->irq;
182    icard.para[1] = link->resource[0]->start;
183    icard.protocol = isdnprot;
184    icard.typ = ISDN_CTYPE_A1_PCMCIA;
185
186    i = hisax_init_pcmcia(link, &busy, &icard);
187    if (i < 0) {
188	printk(KERN_ERR "avma1_cs: failed to initialize AVM A1 "
189			"PCMCIA %d at i/o %#x\n", i,
190			(unsigned int) link->resource[0]->start);
191	avma1cs_release(link);
192	return -ENODEV;
193    }
194    link->priv = (void *) (unsigned long) i;
195
196    return 0;
197} /* avma1cs_config */
198
199/*======================================================================
200
201    After a card is removed, avma1cs_release() will unregister the net
202    device, and release the PCMCIA configuration.  If the device is
203    still open, this will be postponed until it is closed.
204
205======================================================================*/
206
207static void avma1cs_release(struct pcmcia_device *link)
208{
209	unsigned long minor = (unsigned long) link->priv;
210
211	dev_dbg(&link->dev, "avma1cs_release(0x%p)\n", link);
212
213	/* now unregister function with hisax */
214	HiSax_closecard(minor);
215
216	pcmcia_disable_device(link);
217} /* avma1cs_release */
218
219
220static struct pcmcia_device_id avma1cs_ids[] = {
221	PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN A", 0x95d42008, 0xadc9d4bb),
222	PCMCIA_DEVICE_PROD_ID12("ISDN", "CARD", 0x8d9761c8, 0x01c5aa7b),
223	PCMCIA_DEVICE_NULL
224};
225MODULE_DEVICE_TABLE(pcmcia, avma1cs_ids);
226
227static struct pcmcia_driver avma1cs_driver = {
228	.owner		= THIS_MODULE,
229	.drv		= {
230		.name	= "avma1_cs",
231	},
232	.probe		= avma1cs_probe,
233	.remove		= __devexit_p(avma1cs_detach),
234	.id_table	= avma1cs_ids,
235};
236
237/*====================================================================*/
238
239static int __init init_avma1_cs(void)
240{
241	return(pcmcia_register_driver(&avma1cs_driver));
242}
243
244static void __exit exit_avma1_cs(void)
245{
246	pcmcia_unregister_driver(&avma1cs_driver);
247}
248
249module_init(init_avma1_cs);
250module_exit(exit_avma1_cs);
251