1/* $Id: avm_cs.c,v 1.1.1.1 2007/08/03 18:52:34 Exp $
2 *
3 * A PCMCIA client driver for AVM B1/M1/M2
4 *
5 * Copyright 1999 by Carsten Paeth <calle@calle.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#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/ptrace.h>
16#include <linux/slab.h>
17#include <linux/string.h>
18#include <linux/tty.h>
19#include <linux/serial.h>
20#include <linux/major.h>
21#include <asm/io.h>
22#include <asm/system.h>
23
24#include <pcmcia/cs_types.h>
25#include <pcmcia/cs.h>
26#include <pcmcia/cistpl.h>
27#include <pcmcia/ciscode.h>
28#include <pcmcia/ds.h>
29#include <pcmcia/cisreg.h>
30
31#include <linux/skbuff.h>
32#include <linux/capi.h>
33#include <linux/b1lli.h>
34#include <linux/b1pcmcia.h>
35
36/*====================================================================*/
37
38MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
39MODULE_AUTHOR("Carsten Paeth");
40MODULE_LICENSE("GPL");
41
42/*====================================================================*/
43
44/*
45   The event() function is this driver's Card Services event handler.
46   It will be called by Card Services when an appropriate card status
47   event is received.  The config() and release() entry points are
48   used to configure or release a socket, in response to card insertion
49   and ejection events.  They are invoked from the skeleton event
50   handler.
51*/
52
53static int avmcs_config(struct pcmcia_device *link);
54static void avmcs_release(struct pcmcia_device *link);
55
56/*
57   The attach() and detach() entry points are used to create and destroy
58   "instances" of the driver, where each instance represents everything
59   needed to manage one actual PCMCIA card.
60*/
61
62static void avmcs_detach(struct pcmcia_device *p_dev);
63
64/*
65   A linked list of "instances" of the skeleton device.  Each actual
66   PCMCIA card corresponds to one device instance, and is described
67   by one struct pcmcia_device structure (defined in ds.h).
68
69   You may not want to use a linked list for this -- for example, the
70   memory card driver uses an array of struct pcmcia_device pointers, where minor
71   device numbers are used to derive the corresponding array index.
72*/
73
74/*
75   A driver needs to provide a dev_node_t structure for each device
76   on a card.  In some cases, there is only one device per card (for
77   example, ethernet cards, modems).  In other cases, there may be
78   many actual or logical devices (SCSI adapters, memory cards with
79   multiple partitions).  The dev_node_t structures need to be kept
80   in a linked list starting at the 'dev' field of a struct pcmcia_device
81   structure.  We allocate them in the card's private data structure,
82   because they generally can't be allocated dynamically.
83*/
84
85typedef struct local_info_t {
86    dev_node_t	node;
87} local_info_t;
88
89/*======================================================================
90
91    avmcs_attach() creates an "instance" of the driver, allocating
92    local data structures for one device.  The device is registered
93    with Card Services.
94
95    The dev_link structure is initialized, but we don't actually
96    configure the card at this point -- we wait until we receive a
97    card insertion event.
98
99======================================================================*/
100
101static int avmcs_probe(struct pcmcia_device *p_dev)
102{
103    local_info_t *local;
104
105    /* The io structure describes IO port mapping */
106    p_dev->io.NumPorts1 = 16;
107    p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
108    p_dev->io.NumPorts2 = 0;
109
110    /* Interrupt setup */
111    p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
112    p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
113
114    p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
115
116    /* General socket configuration */
117    p_dev->conf.Attributes = CONF_ENABLE_IRQ;
118    p_dev->conf.IntType = INT_MEMORY_AND_IO;
119    p_dev->conf.ConfigIndex = 1;
120    p_dev->conf.Present = PRESENT_OPTION;
121
122    /* Allocate space for private device-specific data */
123    local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
124    if (!local)
125        goto err;
126    p_dev->priv = local;
127
128    return avmcs_config(p_dev);
129
130 err:
131    return -ENOMEM;
132} /* avmcs_attach */
133
134/*======================================================================
135
136    This deletes a driver "instance".  The device is de-registered
137    with Card Services.  If it has been released, all local data
138    structures are freed.  Otherwise, the structures will be freed
139    when the device is released.
140
141======================================================================*/
142
143static void avmcs_detach(struct pcmcia_device *link)
144{
145	avmcs_release(link);
146	kfree(link->priv);
147} /* avmcs_detach */
148
149/*======================================================================
150
151    avmcs_config() is scheduled to run after a CARD_INSERTION event
152    is received, to configure the PCMCIA socket, and to make the
153    ethernet device available to the system.
154
155======================================================================*/
156
157static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
158		     cisparse_t *parse)
159{
160    int i = pcmcia_get_tuple_data(handle, tuple);
161    if (i != CS_SUCCESS) return i;
162    return pcmcia_parse_tuple(handle, tuple, parse);
163}
164
165static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
166		     cisparse_t *parse)
167{
168    int i = pcmcia_get_first_tuple(handle, tuple);
169    if (i != CS_SUCCESS) return i;
170    return get_tuple(handle, tuple, parse);
171}
172
173static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
174		     cisparse_t *parse)
175{
176    int i = pcmcia_get_next_tuple(handle, tuple);
177    if (i != CS_SUCCESS) return i;
178    return get_tuple(handle, tuple, parse);
179}
180
181static int avmcs_config(struct pcmcia_device *link)
182{
183    tuple_t tuple;
184    cisparse_t parse;
185    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
186    local_info_t *dev;
187    int i;
188    u_char buf[64];
189    char devname[128];
190    int cardtype;
191    int (*addcard)(unsigned int port, unsigned irq);
192
193    dev = link->priv;
194
195    do {
196	devname[0] = 0;
197	if (link->prod_id[1])
198		strlcpy(devname, link->prod_id[1], sizeof(devname));
199
200	/*
201         * find IO port
202         */
203	tuple.TupleData = (cisdata_t *)buf;
204	tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
205	tuple.Attributes = 0;
206	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
207	i = first_tuple(link, &tuple, &parse);
208	while (i == CS_SUCCESS) {
209	    if (cf->io.nwin > 0) {
210		link->conf.ConfigIndex = cf->index;
211		link->io.BasePort1 = cf->io.win[0].base;
212		link->io.NumPorts1 = cf->io.win[0].len;
213		link->io.NumPorts2 = 0;
214                printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
215			link->io.BasePort1,
216		        link->io.BasePort1+link->io.NumPorts1-1);
217		i = pcmcia_request_io(link, &link->io);
218		if (i == CS_SUCCESS) goto found_port;
219	    }
220	    i = next_tuple(link, &tuple, &parse);
221	}
222
223found_port:
224	if (i != CS_SUCCESS) {
225	    cs_error(link, RequestIO, i);
226	    break;
227	}
228
229	/*
230	 * allocate an interrupt line
231	 */
232	i = pcmcia_request_irq(link, &link->irq);
233	if (i != CS_SUCCESS) {
234	    cs_error(link, RequestIRQ, i);
235	    /* undo */
236	    pcmcia_disable_device(link);
237	    break;
238	}
239
240	/*
241         * configure the PCMCIA socket
242	  */
243	i = pcmcia_request_configuration(link, &link->conf);
244	if (i != CS_SUCCESS) {
245	    cs_error(link, RequestConfiguration, i);
246	    pcmcia_disable_device(link);
247	    break;
248	}
249
250    } while (0);
251
252    /* At this point, the dev_node_t structure(s) should be
253       initialized and arranged in a linked list at link->dev. */
254
255    if (devname[0]) {
256	char *s = strrchr(devname, ' ');
257	if (!s)
258	   s = devname;
259	else s++;
260	strcpy(dev->node.dev_name, s);
261        if (strcmp("M1", s) == 0) {
262           cardtype = AVM_CARDTYPE_M1;
263        } else if (strcmp("M2", s) == 0) {
264           cardtype = AVM_CARDTYPE_M2;
265	} else {
266           cardtype = AVM_CARDTYPE_B1;
267	}
268    } else {
269        strcpy(dev->node.dev_name, "b1");
270        cardtype = AVM_CARDTYPE_B1;
271    }
272
273    dev->node.major = 64;
274    dev->node.minor = 0;
275    link->dev_node = &dev->node;
276
277    /* If any step failed, release any partially configured state */
278    if (i != 0) {
279	avmcs_release(link);
280	return -ENODEV;
281    }
282
283
284    switch (cardtype) {
285        case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
286        case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
287	default:
288        case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
289    }
290    if ((i = (*addcard)(link->io.BasePort1, link->irq.AssignedIRQ)) < 0) {
291        printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n",
292		dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ);
293	avmcs_release(link);
294	return -ENODEV;
295    }
296    dev->node.minor = i;
297    return 0;
298
299} /* avmcs_config */
300
301/*======================================================================
302
303    After a card is removed, avmcs_release() will unregister the net
304    device, and release the PCMCIA configuration.  If the device is
305    still open, this will be postponed until it is closed.
306
307======================================================================*/
308
309static void avmcs_release(struct pcmcia_device *link)
310{
311	b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ);
312	pcmcia_disable_device(link);
313} /* avmcs_release */
314
315
316static struct pcmcia_device_id avmcs_ids[] = {
317	PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
318	PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
319	PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
320	PCMCIA_DEVICE_NULL
321};
322MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
323
324static struct pcmcia_driver avmcs_driver = {
325	.owner	= THIS_MODULE,
326	.drv	= {
327		.name	= "avm_cs",
328	},
329	.probe = avmcs_probe,
330	.remove	= avmcs_detach,
331	.id_table = avmcs_ids,
332};
333
334static int __init avmcs_init(void)
335{
336	return pcmcia_register_driver(&avmcs_driver);
337}
338
339static void __exit avmcs_exit(void)
340{
341	pcmcia_unregister_driver(&avmcs_driver);
342}
343
344module_init(avmcs_init);
345module_exit(avmcs_exit);
346