aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/isdn/hisax/avma1_cs.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/hisax/avma1_cs.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/isdn/hisax/avma1_cs.c')
-rw-r--r--drivers/isdn/hisax/avma1_cs.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c
new file mode 100644
index 000000000000..663a0bf703b7
--- /dev/null
+++ b/drivers/isdn/hisax/avma1_cs.c
@@ -0,0 +1,527 @@
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/sched.h>
18#include <linux/ptrace.h>
19#include <linux/slab.h>
20#include <linux/string.h>
21#include <asm/io.h>
22#include <asm/system.h>
23
24#include <pcmcia/version.h>
25#include <pcmcia/cs_types.h>
26#include <pcmcia/cs.h>
27#include <pcmcia/cistpl.h>
28#include <pcmcia/ds.h>
29#include "hisax_cfg.h"
30
31MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for AVM A1/Fritz!PCMCIA cards");
32MODULE_AUTHOR("Carsten Paeth");
33MODULE_LICENSE("GPL");
34
35/*
36 All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
37 you do not define PCMCIA_DEBUG at all, all the debug code will be
38 left out. If you compile with PCMCIA_DEBUG=0, the debug code will
39 be present but disabled -- but it can then be enabled for specific
40 modules at load time with a 'pc_debug=#' option to insmod.
41*/
42#ifdef PCMCIA_DEBUG
43static int pc_debug = PCMCIA_DEBUG;
44module_param(pc_debug, int, 0);
45#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
46static char *version =
47"avma1_cs.c 1.00 1998/01/23 10:00:00 (Carsten Paeth)";
48#else
49#define DEBUG(n, args...)
50#endif
51
52/*====================================================================*/
53
54/* Parameters that can be set with 'insmod' */
55
56static int isdnprot = 2;
57
58module_param(isdnprot, int, 0);
59
60/*====================================================================*/
61
62/*
63 The event() function is this driver's Card Services event handler.
64 It will be called by Card Services when an appropriate card status
65 event is received. The config() and release() entry points are
66 used to configure or release a socket, in response to card insertion
67 and ejection events. They are invoked from the skeleton event
68 handler.
69*/
70
71static void avma1cs_config(dev_link_t *link);
72static void avma1cs_release(dev_link_t *link);
73static int avma1cs_event(event_t event, int priority,
74 event_callback_args_t *args);
75
76/*
77 The attach() and detach() entry points are used to create and destroy
78 "instances" of the driver, where each instance represents everything
79 needed to manage one actual PCMCIA card.
80*/
81
82static dev_link_t *avma1cs_attach(void);
83static void avma1cs_detach(dev_link_t *);
84
85/*
86 The dev_info variable is the "key" that is used to match up this
87 device driver with appropriate cards, through the card configuration
88 database.
89*/
90
91static dev_info_t dev_info = "avma1_cs";
92
93/*
94 A linked list of "instances" of the skeleton device. Each actual
95 PCMCIA card corresponds to one device instance, and is described
96 by one dev_link_t structure (defined in ds.h).
97
98 You may not want to use a linked list for this -- for example, the
99 memory card driver uses an array of dev_link_t pointers, where minor
100 device numbers are used to derive the corresponding array index.
101*/
102
103static dev_link_t *dev_list = NULL;
104
105/*
106 A dev_link_t structure has fields for most things that are needed
107 to keep track of a socket, but there will usually be some device
108 specific information that also needs to be kept track of. The
109 'priv' pointer in a dev_link_t structure can be used to point to
110 a device-specific private data structure, like this.
111
112 A driver needs to provide a dev_node_t structure for each device
113 on a card. In some cases, there is only one device per card (for
114 example, ethernet cards, modems). In other cases, there may be
115 many actual or logical devices (SCSI adapters, memory cards with
116 multiple partitions). The dev_node_t structures need to be kept
117 in a linked list starting at the 'dev' field of a dev_link_t
118 structure. We allocate them in the card's private data structure,
119 because they generally can't be allocated dynamically.
120*/
121
122typedef struct local_info_t {
123 dev_node_t node;
124} local_info_t;
125
126/*======================================================================
127
128 avma1cs_attach() creates an "instance" of the driver, allocating
129 local data structures for one device. The device is registered
130 with Card Services.
131
132 The dev_link structure is initialized, but we don't actually
133 configure the card at this point -- we wait until we receive a
134 card insertion event.
135
136======================================================================*/
137
138static dev_link_t *avma1cs_attach(void)
139{
140 client_reg_t client_reg;
141 dev_link_t *link;
142 local_info_t *local;
143 int ret;
144
145 DEBUG(0, "avma1cs_attach()\n");
146
147 /* Initialize the dev_link_t structure */
148 link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
149 if (!link)
150 return NULL;
151 memset(link, 0, sizeof(struct dev_link_t));
152
153 /* Allocate space for private device-specific data */
154 local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
155 if (!local) {
156 kfree(link);
157 return NULL;
158 }
159 memset(local, 0, sizeof(local_info_t));
160 link->priv = local;
161
162 /* The io structure describes IO port mapping */
163 link->io.NumPorts1 = 16;
164 link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
165 link->io.NumPorts2 = 16;
166 link->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
167 link->io.IOAddrLines = 5;
168
169 /* Interrupt setup */
170 link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
171 link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
172
173 link->irq.IRQInfo1 = IRQ_LEVEL_ID;
174
175 /* General socket configuration */
176 link->conf.Attributes = CONF_ENABLE_IRQ;
177 link->conf.Vcc = 50;
178 link->conf.IntType = INT_MEMORY_AND_IO;
179 link->conf.ConfigIndex = 1;
180 link->conf.Present = PRESENT_OPTION;
181
182 /* Register with Card Services */
183 link->next = dev_list;
184 dev_list = link;
185 client_reg.dev_info = &dev_info;
186 client_reg.EventMask =
187 CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
188 CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
189 CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
190 client_reg.event_handler = &avma1cs_event;
191 client_reg.Version = 0x0210;
192 client_reg.event_callback_args.client_data = link;
193 ret = pcmcia_register_client(&link->handle, &client_reg);
194 if (ret != 0) {
195 cs_error(link->handle, RegisterClient, ret);
196 avma1cs_detach(link);
197 return NULL;
198 }
199
200 return link;
201} /* avma1cs_attach */
202
203/*======================================================================
204
205 This deletes a driver "instance". The device is de-registered
206 with Card Services. If it has been released, all local data
207 structures are freed. Otherwise, the structures will be freed
208 when the device is released.
209
210======================================================================*/
211
212static void avma1cs_detach(dev_link_t *link)
213{
214 dev_link_t **linkp;
215
216 DEBUG(0, "avma1cs_detach(0x%p)\n", link);
217
218 /* Locate device structure */
219 for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
220 if (*linkp == link) break;
221 if (*linkp == NULL)
222 return;
223
224 /*
225 If the device is currently configured and active, we won't
226 actually delete it yet. Instead, it is marked so that when
227 the release() function is called, that will trigger a proper
228 detach().
229 */
230 if (link->state & DEV_CONFIG) {
231#ifdef PCMCIA_DEBUG
232 printk(KERN_DEBUG "avma1_cs: detach postponed, '%s' "
233 "still locked\n", link->dev->dev_name);
234#endif
235 link->state |= DEV_STALE_LINK;
236 return;
237 }
238
239 /* Break the link with Card Services */
240 if (link->handle)
241 pcmcia_deregister_client(link->handle);
242
243 /* Unlink device structure, free pieces */
244 *linkp = link->next;
245 if (link->priv) {
246 kfree(link->priv);
247 }
248 kfree(link);
249
250} /* avma1cs_detach */
251
252/*======================================================================
253
254 avma1cs_config() is scheduled to run after a CARD_INSERTION event
255 is received, to configure the PCMCIA socket, and to make the
256 ethernet device available to the system.
257
258======================================================================*/
259
260static int get_tuple(client_handle_t handle, tuple_t *tuple,
261 cisparse_t *parse)
262{
263 int i = pcmcia_get_tuple_data(handle, tuple);
264 if (i != CS_SUCCESS) return i;
265 return pcmcia_parse_tuple(handle, tuple, parse);
266}
267
268static int first_tuple(client_handle_t handle, tuple_t *tuple,
269 cisparse_t *parse)
270{
271 int i = pcmcia_get_first_tuple(handle, tuple);
272 if (i != CS_SUCCESS) return i;
273 return get_tuple(handle, tuple, parse);
274}
275
276static int next_tuple(client_handle_t handle, tuple_t *tuple,
277 cisparse_t *parse)
278{
279 int i = pcmcia_get_next_tuple(handle, tuple);
280 if (i != CS_SUCCESS) return i;
281 return get_tuple(handle, tuple, parse);
282}
283
284static void avma1cs_config(dev_link_t *link)
285{
286 client_handle_t handle;
287 tuple_t tuple;
288 cisparse_t parse;
289 cistpl_cftable_entry_t *cf = &parse.cftable_entry;
290 local_info_t *dev;
291 int i;
292 u_char buf[64];
293 char devname[128];
294 IsdnCard_t icard;
295 int busy = 0;
296
297 handle = link->handle;
298 dev = link->priv;
299
300 DEBUG(0, "avma1cs_config(0x%p)\n", link);
301
302 /*
303 This reads the card's CONFIG tuple to find its configuration
304 registers.
305 */
306 do {
307 tuple.DesiredTuple = CISTPL_CONFIG;
308 i = pcmcia_get_first_tuple(handle, &tuple);
309 if (i != CS_SUCCESS) break;
310 tuple.TupleData = buf;
311 tuple.TupleDataMax = 64;
312 tuple.TupleOffset = 0;
313 i = pcmcia_get_tuple_data(handle, &tuple);
314 if (i != CS_SUCCESS) break;
315 i = pcmcia_parse_tuple(handle, &tuple, &parse);
316 if (i != CS_SUCCESS) break;
317 link->conf.ConfigBase = parse.config.base;
318 } while (0);
319 if (i != CS_SUCCESS) {
320 cs_error(link->handle, ParseTuple, i);
321 link->state &= ~DEV_CONFIG_PENDING;
322 return;
323 }
324
325 /* Configure card */
326 link->state |= DEV_CONFIG;
327
328 do {
329
330 tuple.Attributes = 0;
331 tuple.TupleData = buf;
332 tuple.TupleDataMax = 254;
333 tuple.TupleOffset = 0;
334 tuple.DesiredTuple = CISTPL_VERS_1;
335
336 devname[0] = 0;
337 if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) {
338 strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1],
339 sizeof(devname));
340 }
341 /*
342 * find IO port
343 */
344 tuple.TupleData = (cisdata_t *)buf;
345 tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
346 tuple.Attributes = 0;
347 tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
348 i = first_tuple(handle, &tuple, &parse);
349 while (i == CS_SUCCESS) {
350 if (cf->io.nwin > 0) {
351 link->conf.ConfigIndex = cf->index;
352 link->io.BasePort1 = cf->io.win[0].base;
353 link->io.NumPorts1 = cf->io.win[0].len;
354 link->io.NumPorts2 = 0;
355 printk(KERN_INFO "avma1_cs: testing i/o %#x-%#x\n",
356 link->io.BasePort1,
357 link->io.BasePort1+link->io.NumPorts1 - 1);
358 i = pcmcia_request_io(link->handle, &link->io);
359 if (i == CS_SUCCESS) goto found_port;
360 }
361 i = next_tuple(handle, &tuple, &parse);
362 }
363
364found_port:
365 if (i != CS_SUCCESS) {
366 cs_error(link->handle, RequestIO, i);
367 break;
368 }
369
370 /*
371 * allocate an interrupt line
372 */
373 i = pcmcia_request_irq(link->handle, &link->irq);
374 if (i != CS_SUCCESS) {
375 cs_error(link->handle, RequestIRQ, i);
376 pcmcia_release_io(link->handle, &link->io);
377 break;
378 }
379
380 /*
381 * configure the PCMCIA socket
382 */
383 i = pcmcia_request_configuration(link->handle, &link->conf);
384 if (i != CS_SUCCESS) {
385 cs_error(link->handle, RequestConfiguration, i);
386 pcmcia_release_io(link->handle, &link->io);
387 pcmcia_release_irq(link->handle, &link->irq);
388 break;
389 }
390
391 } while (0);
392
393 /* At this point, the dev_node_t structure(s) should be
394 initialized and arranged in a linked list at link->dev. */
395
396 strcpy(dev->node.dev_name, "A1");
397 dev->node.major = 45;
398 dev->node.minor = 0;
399 link->dev = &dev->node;
400
401 link->state &= ~DEV_CONFIG_PENDING;
402 /* If any step failed, release any partially configured state */
403 if (i != 0) {
404 avma1cs_release(link);
405 return;
406 }
407
408 printk(KERN_NOTICE "avma1_cs: checking at i/o %#x, irq %d\n",
409 link->io.BasePort1, link->irq.AssignedIRQ);
410
411 icard.para[0] = link->irq.AssignedIRQ;
412 icard.para[1] = link->io.BasePort1;
413 icard.protocol = isdnprot;
414 icard.typ = ISDN_CTYPE_A1_PCMCIA;
415
416 i = hisax_init_pcmcia(link, &busy, &icard);
417 if (i < 0) {
418 printk(KERN_ERR "avma1_cs: failed to initialize AVM A1 PCMCIA %d at i/o %#x\n", i, link->io.BasePort1);
419 avma1cs_release(link);
420 return;
421 }
422 dev->node.minor = i;
423
424} /* avma1cs_config */
425
426/*======================================================================
427
428 After a card is removed, avma1cs_release() will unregister the net
429 device, and release the PCMCIA configuration. If the device is
430 still open, this will be postponed until it is closed.
431
432======================================================================*/
433
434static void avma1cs_release(dev_link_t *link)
435{
436 local_info_t *local = link->priv;
437
438 DEBUG(0, "avma1cs_release(0x%p)\n", link);
439
440 /* no unregister function with hisax */
441 HiSax_closecard(local->node.minor);
442
443 /* Unlink the device chain */
444 link->dev = NULL;
445
446 /* Don't bother checking to see if these succeed or not */
447 pcmcia_release_configuration(link->handle);
448 pcmcia_release_io(link->handle, &link->io);
449 pcmcia_release_irq(link->handle, &link->irq);
450 link->state &= ~DEV_CONFIG;
451
452 if (link->state & DEV_STALE_LINK)
453 avma1cs_detach(link);
454} /* avma1cs_release */
455
456/*======================================================================
457
458 The card status event handler. Mostly, this schedules other
459 stuff to run after an event is received. A CARD_REMOVAL event
460 also sets some flags to discourage the net drivers from trying
461 to talk to the card any more.
462
463 When a CARD_REMOVAL event is received, we immediately set a flag
464 to block future accesses to this device. All the functions that
465 actually access the device should check this flag to make sure
466 the card is still present.
467
468======================================================================*/
469
470static int avma1cs_event(event_t event, int priority,
471 event_callback_args_t *args)
472{
473 dev_link_t *link = args->client_data;
474
475 DEBUG(1, "avma1cs_event(0x%06x)\n", event);
476
477 switch (event) {
478 case CS_EVENT_CARD_REMOVAL:
479 if (link->state & DEV_CONFIG)
480 avma1cs_release(link);
481 break;
482 case CS_EVENT_CARD_INSERTION:
483 link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
484 avma1cs_config(link);
485 break;
486 case CS_EVENT_PM_SUSPEND:
487 link->state |= DEV_SUSPEND;
488 /* Fall through... */
489 case CS_EVENT_RESET_PHYSICAL:
490 if (link->state & DEV_CONFIG)
491 pcmcia_release_configuration(link->handle);
492 break;
493 case CS_EVENT_PM_RESUME:
494 link->state &= ~DEV_SUSPEND;
495 /* Fall through... */
496 case CS_EVENT_CARD_RESET:
497 if (link->state & DEV_CONFIG)
498 pcmcia_request_configuration(link->handle, &link->conf);
499 break;
500 }
501 return 0;
502} /* avma1cs_event */
503
504static struct pcmcia_driver avma1cs_driver = {
505 .owner = THIS_MODULE,
506 .drv = {
507 .name = "avma1_cs",
508 },
509 .attach = avma1cs_attach,
510 .detach = avma1cs_detach,
511};
512
513/*====================================================================*/
514
515static int __init init_avma1_cs(void)
516{
517 return(pcmcia_register_driver(&avma1cs_driver));
518}
519
520static void __exit exit_avma1_cs(void)
521{
522 pcmcia_unregister_driver(&avma1cs_driver);
523 BUG_ON(dev_list != NULL);
524}
525
526module_init(init_avma1_cs);
527module_exit(exit_avma1_cs);