blob: 0c504dc49acf554095eb64b72dc8c2991b6c7fd6 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai 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/sched.h>
16#include <linux/ptrace.h>
17#include <linux/slab.h>
18#include <linux/string.h>
19#include <linux/tty.h>
20#include <linux/serial.h>
21#include <linux/major.h>
22#include <asm/io.h>
23#include <asm/system.h>
24
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include <pcmcia/cs_types.h>
26#include <pcmcia/cs.h>
27#include <pcmcia/cistpl.h>
28#include <pcmcia/ciscode.h>
29#include <pcmcia/ds.h>
30#include <pcmcia/cisreg.h>
31
32#include <linux/skbuff.h>
33#include <linux/capi.h>
34#include <linux/b1lli.h>
35#include <linux/b1pcmcia.h>
36
37/*====================================================================*/
38
39MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
40MODULE_AUTHOR("Carsten Paeth");
41MODULE_LICENSE("GPL");
42
43/*====================================================================*/
44
45/*
46 The event() function is this driver's Card Services event handler.
47 It will be called by Card Services when an appropriate card status
48 event is received. The config() and release() entry points are
49 used to configure or release a socket, in response to card insertion
50 and ejection events. They are invoked from the skeleton event
51 handler.
52*/
53
54static void avmcs_config(dev_link_t *link);
55static void avmcs_release(dev_link_t *link);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57/*
58 The attach() and detach() entry points are used to create and destroy
59 "instances" of the driver, where each instance represents everything
60 needed to manage one actual PCMCIA card.
61*/
62
Dominik Brodowskicc3b4862005-11-14 21:23:14 +010063static void avmcs_detach(struct pcmcia_device *p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
65/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 A linked list of "instances" of the skeleton device. Each actual
67 PCMCIA card corresponds to one device instance, and is described
68 by one dev_link_t structure (defined in ds.h).
69
70 You may not want to use a linked list for this -- for example, the
71 memory card driver uses an array of dev_link_t pointers, where minor
72 device numbers are used to derive the corresponding array index.
73*/
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 A driver needs to provide a dev_node_t structure for each device
77 on a card. In some cases, there is only one device per card (for
78 example, ethernet cards, modems). In other cases, there may be
79 many actual or logical devices (SCSI adapters, memory cards with
80 multiple partitions). The dev_node_t structures need to be kept
81 in a linked list starting at the 'dev' field of a dev_link_t
82 structure. We allocate them in the card's private data structure,
83 because they generally can't be allocated dynamically.
84*/
85
86typedef struct local_info_t {
87 dev_node_t node;
88} local_info_t;
89
90/*======================================================================
91
92 avmcs_attach() creates an "instance" of the driver, allocating
93 local data structures for one device. The device is registered
94 with Card Services.
95
96 The dev_link structure is initialized, but we don't actually
97 configure the card at this point -- we wait until we receive a
98 card insertion event.
99
100======================================================================*/
101
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100102static int avmcs_attach(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 dev_link_t *link;
105 local_info_t *local;
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 /* Initialize the dev_link_t structure */
108 link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
109 if (!link)
110 goto err;
111 memset(link, 0, sizeof(struct dev_link_t));
112
113 /* The io structure describes IO port mapping */
114 link->io.NumPorts1 = 16;
115 link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
116 link->io.NumPorts2 = 0;
117
118 /* Interrupt setup */
119 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
120 link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
121
122 link->irq.IRQInfo1 = IRQ_LEVEL_ID;
123
124 /* General socket configuration */
125 link->conf.Attributes = CONF_ENABLE_IRQ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 link->conf.IntType = INT_MEMORY_AND_IO;
127 link->conf.ConfigIndex = 1;
128 link->conf.Present = PRESENT_OPTION;
129
130 /* Allocate space for private device-specific data */
131 local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
132 if (!local)
133 goto err_kfree;
134 memset(local, 0, sizeof(local_info_t));
135 link->priv = local;
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100136
137 link->handle = p_dev;
138 p_dev->instance = link;
139
140 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
141 avmcs_config(link);
142
143 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
145 err_kfree:
146 kfree(link);
147 err:
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100148 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149} /* avmcs_attach */
150
151/*======================================================================
152
153 This deletes a driver "instance". The device is de-registered
154 with Card Services. If it has been released, all local data
155 structures are freed. Otherwise, the structures will be freed
156 when the device is released.
157
158======================================================================*/
159
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100160static void avmcs_detach(struct pcmcia_device *p_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161{
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100162 dev_link_t *link = dev_to_instance(p_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100164 if (link->state & DEV_CONFIG)
165 avmcs_release(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166
Jesper Juhl3c7208f2005-11-07 01:01:29 -0800167 kfree(link->priv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 kfree(link);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169} /* avmcs_detach */
170
171/*======================================================================
172
173 avmcs_config() is scheduled to run after a CARD_INSERTION event
174 is received, to configure the PCMCIA socket, and to make the
175 ethernet device available to the system.
176
177======================================================================*/
178
179static int get_tuple(client_handle_t handle, tuple_t *tuple,
180 cisparse_t *parse)
181{
182 int i = pcmcia_get_tuple_data(handle, tuple);
183 if (i != CS_SUCCESS) return i;
184 return pcmcia_parse_tuple(handle, tuple, parse);
185}
186
187static int first_tuple(client_handle_t handle, tuple_t *tuple,
188 cisparse_t *parse)
189{
190 int i = pcmcia_get_first_tuple(handle, tuple);
191 if (i != CS_SUCCESS) return i;
192 return get_tuple(handle, tuple, parse);
193}
194
195static int next_tuple(client_handle_t handle, tuple_t *tuple,
196 cisparse_t *parse)
197{
198 int i = pcmcia_get_next_tuple(handle, tuple);
199 if (i != CS_SUCCESS) return i;
200 return get_tuple(handle, tuple, parse);
201}
202
203static void avmcs_config(dev_link_t *link)
204{
205 client_handle_t handle;
206 tuple_t tuple;
207 cisparse_t parse;
208 cistpl_cftable_entry_t *cf = &parse.cftable_entry;
209 local_info_t *dev;
210 int i;
211 u_char buf[64];
212 char devname[128];
213 int cardtype;
214 int (*addcard)(unsigned int port, unsigned irq);
215
216 handle = link->handle;
217 dev = link->priv;
218
219 /*
220 This reads the card's CONFIG tuple to find its configuration
221 registers.
222 */
223 do {
224 tuple.DesiredTuple = CISTPL_CONFIG;
225 i = pcmcia_get_first_tuple(handle, &tuple);
226 if (i != CS_SUCCESS) break;
227 tuple.TupleData = buf;
228 tuple.TupleDataMax = 64;
229 tuple.TupleOffset = 0;
230 i = pcmcia_get_tuple_data(handle, &tuple);
231 if (i != CS_SUCCESS) break;
232 i = pcmcia_parse_tuple(handle, &tuple, &parse);
233 if (i != CS_SUCCESS) break;
234 link->conf.ConfigBase = parse.config.base;
235 } while (0);
236 if (i != CS_SUCCESS) {
237 cs_error(link->handle, ParseTuple, i);
238 link->state &= ~DEV_CONFIG_PENDING;
239 return;
240 }
241
242 /* Configure card */
243 link->state |= DEV_CONFIG;
244
245 do {
246
247 tuple.Attributes = 0;
248 tuple.TupleData = buf;
249 tuple.TupleDataMax = 254;
250 tuple.TupleOffset = 0;
251 tuple.DesiredTuple = CISTPL_VERS_1;
252
253 devname[0] = 0;
254 if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) {
255 strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1],
256 sizeof(devname));
257 }
258 /*
259 * find IO port
260 */
261 tuple.TupleData = (cisdata_t *)buf;
262 tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
263 tuple.Attributes = 0;
264 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
265 i = first_tuple(handle, &tuple, &parse);
266 while (i == CS_SUCCESS) {
267 if (cf->io.nwin > 0) {
268 link->conf.ConfigIndex = cf->index;
269 link->io.BasePort1 = cf->io.win[0].base;
270 link->io.NumPorts1 = cf->io.win[0].len;
271 link->io.NumPorts2 = 0;
272 printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
273 link->io.BasePort1,
274 link->io.BasePort1+link->io.NumPorts1-1);
275 i = pcmcia_request_io(link->handle, &link->io);
276 if (i == CS_SUCCESS) goto found_port;
277 }
278 i = next_tuple(handle, &tuple, &parse);
279 }
280
281found_port:
282 if (i != CS_SUCCESS) {
283 cs_error(link->handle, RequestIO, i);
284 break;
285 }
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100286
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 /*
288 * allocate an interrupt line
289 */
290 i = pcmcia_request_irq(link->handle, &link->irq);
291 if (i != CS_SUCCESS) {
292 cs_error(link->handle, RequestIRQ, i);
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100293 /* undo */
294 pcmcia_disable_device(link->handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 break;
296 }
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100297
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 /*
299 * configure the PCMCIA socket
300 */
301 i = pcmcia_request_configuration(link->handle, &link->conf);
302 if (i != CS_SUCCESS) {
303 cs_error(link->handle, RequestConfiguration, i);
Dominik Brodowski50db3fd2006-01-15 10:05:19 +0100304 pcmcia_disable_device(link->handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 break;
306 }
307
308 } while (0);
309
310 /* At this point, the dev_node_t structure(s) should be
311 initialized and arranged in a linked list at link->dev. */
312
313 if (devname[0]) {
314 char *s = strrchr(devname, ' ');
315 if (!s)
316 s = devname;
317 else s++;
318 strcpy(dev->node.dev_name, s);
319 if (strcmp("M1", s) == 0) {
320 cardtype = AVM_CARDTYPE_M1;
321 } else if (strcmp("M2", s) == 0) {
322 cardtype = AVM_CARDTYPE_M2;
323 } else {
324 cardtype = AVM_CARDTYPE_B1;
325 }
326 } else {
327 strcpy(dev->node.dev_name, "b1");
328 cardtype = AVM_CARDTYPE_B1;
329 }
330
331 dev->node.major = 64;
332 dev->node.minor = 0;
333 link->dev = &dev->node;
334
335 link->state &= ~DEV_CONFIG_PENDING;
336 /* If any step failed, release any partially configured state */
337 if (i != 0) {
338 avmcs_release(link);
339 return;
340 }
341
342
343 switch (cardtype) {
344 case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
345 case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
346 default:
347 case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
348 }
349 if ((i = (*addcard)(link->io.BasePort1, link->irq.AssignedIRQ)) < 0) {
350 printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n",
351 dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ);
352 avmcs_release(link);
353 return;
354 }
355 dev->node.minor = i;
356
357} /* avmcs_config */
358
359/*======================================================================
360
361 After a card is removed, avmcs_release() will unregister the net
362 device, and release the PCMCIA configuration. If the device is
363 still open, this will be postponed until it is closed.
364
365======================================================================*/
366
367static void avmcs_release(dev_link_t *link)
368{
Dominik Brodowski5f2a71f2006-01-15 09:32:39 +0100369 b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ);
370 pcmcia_disable_device(link->handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371} /* avmcs_release */
372
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
Dominik Brodowskia13bcf02005-06-27 16:28:35 -0700374static struct pcmcia_device_id avmcs_ids[] = {
375 PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
376 PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
377 PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
378 PCMCIA_DEVICE_NULL
379};
380MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
381
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382static struct pcmcia_driver avmcs_driver = {
383 .owner = THIS_MODULE,
384 .drv = {
385 .name = "avm_cs",
386 },
Dominik Brodowskif8cfa612005-11-14 21:25:51 +0100387 .probe = avmcs_attach,
Dominik Brodowskicc3b4862005-11-14 21:23:14 +0100388 .remove = avmcs_detach,
Dominik Brodowskia13bcf02005-06-27 16:28:35 -0700389 .id_table = avmcs_ids,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390};
391
392static int __init avmcs_init(void)
393{
394 return pcmcia_register_driver(&avmcs_driver);
395}
396
397static void __exit avmcs_exit(void)
398{
399 pcmcia_unregister_driver(&avmcs_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400}
401
402module_init(avmcs_init);
403module_exit(avmcs_exit);