Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: t1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $ |
2 | * | |
3 | * Module for AVM T1 HEMA-card. | |
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/skbuff.h> | |
15 | #include <linux/delay.h> | |
16 | #include <linux/mm.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/ioport.h> | |
19 | #include <linux/capi.h> | |
20 | #include <linux/netdevice.h> | |
21 | #include <linux/kernelcapi.h> | |
22 | #include <linux/init.h> | |
23 | #include <linux/pci.h> | |
24 | #include <asm/io.h> | |
25 | #include <linux/isdn/capicmd.h> | |
26 | #include <linux/isdn/capiutil.h> | |
27 | #include <linux/isdn/capilli.h> | |
28 | #include "avmcard.h" | |
29 | ||
30 | /* ------------------------------------------------------------- */ | |
31 | ||
32 | static char *revision = "$Revision: 1.1.2.3 $"; | |
33 | ||
34 | /* ------------------------------------------------------------- */ | |
35 | ||
36 | MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 HEMA ISA card"); | |
37 | MODULE_AUTHOR("Carsten Paeth"); | |
38 | MODULE_LICENSE("GPL"); | |
39 | ||
40 | /* ------------------------------------------------------------- */ | |
41 | ||
42 | static int hema_irq_table[16] = | |
43 | {0, | |
44 | 0, | |
45 | 0, | |
46 | 0x80, /* irq 3 */ | |
47 | 0, | |
48 | 0x90, /* irq 5 */ | |
49 | 0, | |
50 | 0xA0, /* irq 7 */ | |
51 | 0, | |
52 | 0xB0, /* irq 9 */ | |
53 | 0xC0, /* irq 10 */ | |
54 | 0xD0, /* irq 11 */ | |
55 | 0xE0, /* irq 12 */ | |
56 | 0, | |
57 | 0, | |
58 | 0xF0, /* irq 15 */ | |
59 | }; | |
60 | ||
61 | static int t1_detectandinit(unsigned int base, unsigned irq, int cardnr) | |
62 | { | |
63 | unsigned char cregs[8]; | |
64 | unsigned char reverse_cardnr; | |
65 | unsigned char dummy; | |
66 | int i; | |
67 | ||
68 | reverse_cardnr = ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1) | |
69 | | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3); | |
70 | cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf); | |
71 | cregs[1] = 0x00; /* fast & slow link connected to CON1 */ | |
72 | cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */ | |
73 | cregs[3] = 0; | |
74 | cregs[4] = 0x11; /* zero wait state */ | |
75 | cregs[5] = hema_irq_table[irq & 0xf]; | |
76 | cregs[6] = 0; | |
77 | cregs[7] = 0; | |
78 | ||
79 | /* | |
80 | * no one else should use the ISA bus in this moment, | |
81 | * but no function there to prevent this :-( | |
82 | * save_flags(flags); cli(); | |
83 | */ | |
84 | ||
85 | /* board reset */ | |
86 | t1outp(base, T1_RESETBOARD, 0xf); | |
87 | mdelay(100); | |
88 | dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */ | |
89 | ||
90 | /* write config */ | |
91 | dummy = (base >> 4) & 0xff; | |
92 | for (i=1;i<=0xf;i++) t1outp(base, i, dummy); | |
93 | t1outp(base, HEMA_PAL_ID & 0xf, dummy); | |
94 | t1outp(base, HEMA_PAL_ID >> 4, cregs[0]); | |
95 | for(i=1;i<7;i++) t1outp(base, 0, cregs[i]); | |
96 | t1outp(base, ((base >> 4)) & 0x3, cregs[7]); | |
97 | /* restore_flags(flags); */ | |
98 | ||
99 | mdelay(100); | |
100 | t1outp(base, T1_FASTLINK+T1_RESETLINK, 0); | |
101 | t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0); | |
102 | mdelay(10); | |
103 | t1outp(base, T1_FASTLINK+T1_RESETLINK, 1); | |
104 | t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1); | |
105 | mdelay(100); | |
106 | t1outp(base, T1_FASTLINK+T1_RESETLINK, 0); | |
107 | t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0); | |
108 | mdelay(10); | |
109 | t1outp(base, T1_FASTLINK+T1_ANALYSE, 0); | |
110 | mdelay(5); | |
111 | t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0); | |
112 | ||
113 | if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */ | |
114 | return 1; | |
115 | if (t1inp(base, T1_FASTLINK+T1_INSTAT) != 0x0) /* rx empty */ | |
116 | return 2; | |
117 | if (t1inp(base, T1_FASTLINK+T1_IRQENABLE) != 0x0) | |
118 | return 3; | |
119 | if ((t1inp(base, T1_FASTLINK+T1_FIFOSTAT) & 0xf0) != 0x70) | |
120 | return 4; | |
121 | if ((t1inp(base, T1_FASTLINK+T1_IRQMASTER) & 0x0e) != 0) | |
122 | return 5; | |
123 | if ((t1inp(base, T1_FASTLINK+T1_IDENT) & 0x7d) != 1) | |
124 | return 6; | |
125 | if (t1inp(base, T1_SLOWLINK+T1_OUTSTAT) != 0x1) /* tx empty */ | |
126 | return 7; | |
127 | if ((t1inp(base, T1_SLOWLINK+T1_IRQMASTER) & 0x0e) != 0) | |
128 | return 8; | |
129 | if ((t1inp(base, T1_SLOWLINK+T1_IDENT) & 0x7d) != 0) | |
130 | return 9; | |
131 | return 0; | |
132 | } | |
133 | ||
7d12e780 | 134 | static irqreturn_t t1isa_interrupt(int interrupt, void *devptr) |
1da177e4 LT |
135 | { |
136 | avmcard *card = devptr; | |
137 | avmctrl_info *cinfo = &card->ctrlinfo[0]; | |
138 | struct capi_ctr *ctrl = &cinfo->capi_ctrl; | |
139 | unsigned char b1cmd; | |
140 | struct sk_buff *skb; | |
141 | ||
142 | unsigned ApplId; | |
143 | unsigned MsgLen; | |
144 | unsigned DataB3Len; | |
145 | unsigned NCCI; | |
146 | unsigned WindowSize; | |
147 | unsigned long flags; | |
148 | ||
149 | spin_lock_irqsave(&card->lock, flags); | |
150 | ||
151 | while (b1_rx_full(card->port)) { | |
152 | ||
153 | b1cmd = b1_get_byte(card->port); | |
154 | ||
155 | switch (b1cmd) { | |
156 | ||
157 | case RECEIVE_DATA_B3_IND: | |
158 | ||
159 | ApplId = (unsigned) b1_get_word(card->port); | |
160 | MsgLen = t1_get_slice(card->port, card->msgbuf); | |
161 | DataB3Len = t1_get_slice(card->port, card->databuf); | |
162 | spin_unlock_irqrestore(&card->lock, flags); | |
163 | ||
164 | if (MsgLen < 30) { /* not CAPI 64Bit */ | |
165 | memset(card->msgbuf+MsgLen, 0, 30-MsgLen); | |
166 | MsgLen = 30; | |
167 | CAPIMSG_SETLEN(card->msgbuf, 30); | |
168 | } | |
169 | if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) { | |
170 | printk(KERN_ERR "%s: incoming packet dropped\n", | |
171 | card->name); | |
172 | } else { | |
173 | memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); | |
174 | memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); | |
175 | capi_ctr_handle_message(ctrl, ApplId, skb); | |
176 | } | |
177 | break; | |
178 | ||
179 | case RECEIVE_MESSAGE: | |
180 | ||
181 | ApplId = (unsigned) b1_get_word(card->port); | |
182 | MsgLen = t1_get_slice(card->port, card->msgbuf); | |
183 | spin_unlock_irqrestore(&card->lock, flags); | |
184 | if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { | |
185 | printk(KERN_ERR "%s: incoming packet dropped\n", | |
186 | card->name); | |
187 | } else { | |
188 | memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); | |
189 | if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3) | |
190 | capilib_data_b3_conf(&cinfo->ncci_head, ApplId, | |
191 | CAPIMSG_NCCI(skb->data), | |
192 | CAPIMSG_MSGID(skb->data)); | |
193 | ||
194 | capi_ctr_handle_message(ctrl, ApplId, skb); | |
195 | } | |
196 | break; | |
197 | ||
198 | case RECEIVE_NEW_NCCI: | |
199 | ||
200 | ApplId = b1_get_word(card->port); | |
201 | NCCI = b1_get_word(card->port); | |
202 | WindowSize = b1_get_word(card->port); | |
203 | spin_unlock_irqrestore(&card->lock, flags); | |
204 | ||
205 | capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize); | |
206 | ||
207 | break; | |
208 | ||
209 | case RECEIVE_FREE_NCCI: | |
210 | ||
211 | ApplId = b1_get_word(card->port); | |
212 | NCCI = b1_get_word(card->port); | |
213 | spin_unlock_irqrestore(&card->lock, flags); | |
214 | ||
215 | if (NCCI != 0xffffffff) | |
216 | capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI); | |
217 | ||
218 | break; | |
219 | ||
220 | case RECEIVE_START: | |
221 | b1_put_byte(card->port, SEND_POLLACK); | |
222 | spin_unlock_irqrestore(&card->lock, flags); | |
223 | capi_ctr_resume_output(ctrl); | |
224 | break; | |
225 | ||
226 | case RECEIVE_STOP: | |
227 | spin_unlock_irqrestore(&card->lock, flags); | |
228 | capi_ctr_suspend_output(ctrl); | |
229 | break; | |
230 | ||
231 | case RECEIVE_INIT: | |
232 | ||
233 | cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf); | |
234 | spin_unlock_irqrestore(&card->lock, flags); | |
235 | b1_parse_version(cinfo); | |
236 | printk(KERN_INFO "%s: %s-card (%s) now active\n", | |
237 | card->name, | |
238 | cinfo->version[VER_CARDTYPE], | |
239 | cinfo->version[VER_DRIVER]); | |
240 | capi_ctr_ready(ctrl); | |
241 | break; | |
242 | ||
243 | case RECEIVE_TASK_READY: | |
244 | ApplId = (unsigned) b1_get_word(card->port); | |
245 | MsgLen = t1_get_slice(card->port, card->msgbuf); | |
246 | spin_unlock_irqrestore(&card->lock, flags); | |
247 | card->msgbuf[MsgLen] = 0; | |
248 | while ( MsgLen > 0 | |
249 | && ( card->msgbuf[MsgLen-1] == '\n' | |
250 | || card->msgbuf[MsgLen-1] == '\r')) { | |
251 | card->msgbuf[MsgLen-1] = 0; | |
252 | MsgLen--; | |
253 | } | |
254 | printk(KERN_INFO "%s: task %d \"%s\" ready.\n", | |
255 | card->name, ApplId, card->msgbuf); | |
256 | break; | |
257 | ||
258 | case RECEIVE_DEBUGMSG: | |
259 | MsgLen = t1_get_slice(card->port, card->msgbuf); | |
260 | spin_unlock_irqrestore(&card->lock, flags); | |
261 | card->msgbuf[MsgLen] = 0; | |
262 | while ( MsgLen > 0 | |
263 | && ( card->msgbuf[MsgLen-1] == '\n' | |
264 | || card->msgbuf[MsgLen-1] == '\r')) { | |
265 | card->msgbuf[MsgLen-1] = 0; | |
266 | MsgLen--; | |
267 | } | |
268 | printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); | |
269 | break; | |
270 | ||
271 | ||
272 | case 0xff: | |
273 | spin_unlock_irqrestore(&card->lock, flags); | |
274 | printk(KERN_ERR "%s: card reseted ?\n", card->name); | |
275 | return IRQ_HANDLED; | |
276 | default: | |
277 | spin_unlock_irqrestore(&card->lock, flags); | |
278 | printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", | |
279 | card->name, b1cmd); | |
280 | return IRQ_NONE; | |
281 | } | |
282 | } | |
283 | return IRQ_HANDLED; | |
284 | } | |
285 | ||
286 | /* ------------------------------------------------------------- */ | |
287 | ||
288 | static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) | |
289 | { | |
290 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
291 | avmcard *card = cinfo->card; | |
292 | unsigned int port = card->port; | |
293 | unsigned long flags; | |
294 | int retval; | |
295 | ||
296 | t1_disable_irq(port); | |
297 | b1_reset(port); | |
298 | ||
299 | if ((retval = b1_load_t4file(card, &data->firmware))) { | |
300 | b1_reset(port); | |
301 | printk(KERN_ERR "%s: failed to load t4file!!\n", | |
302 | card->name); | |
303 | return retval; | |
304 | } | |
305 | ||
306 | if (data->configuration.len > 0 && data->configuration.data) { | |
307 | if ((retval = b1_load_config(card, &data->configuration))) { | |
308 | b1_reset(port); | |
309 | printk(KERN_ERR "%s: failed to load config!!\n", | |
310 | card->name); | |
311 | return retval; | |
312 | } | |
313 | } | |
314 | ||
315 | if (!b1_loaded(card)) { | |
316 | printk(KERN_ERR "%s: failed to load t4file.\n", card->name); | |
317 | return -EIO; | |
318 | } | |
319 | ||
320 | spin_lock_irqsave(&card->lock, flags); | |
321 | b1_setinterrupt(port, card->irq, card->cardtype); | |
322 | b1_put_byte(port, SEND_INIT); | |
323 | b1_put_word(port, CAPI_MAXAPPL); | |
324 | b1_put_word(port, AVM_NCCI_PER_CHANNEL*30); | |
325 | b1_put_word(port, ctrl->cnr - 1); | |
326 | spin_unlock_irqrestore(&card->lock, flags); | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
08e51533 | 331 | static void t1isa_reset_ctr(struct capi_ctr *ctrl) |
1da177e4 LT |
332 | { |
333 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
334 | avmcard *card = cinfo->card; | |
335 | unsigned int port = card->port; | |
336 | ||
337 | t1_disable_irq(port); | |
338 | b1_reset(port); | |
339 | b1_reset(port); | |
340 | ||
341 | memset(cinfo->version, 0, sizeof(cinfo->version)); | |
342 | capilib_release(&cinfo->ncci_head); | |
343 | capi_ctr_reseted(ctrl); | |
344 | } | |
345 | ||
346 | static void t1isa_remove(struct pci_dev *pdev) | |
347 | { | |
348 | avmctrl_info *cinfo = pci_get_drvdata(pdev); | |
349 | avmcard *card; | |
350 | ||
351 | if (!cinfo) | |
352 | return; | |
353 | ||
354 | card = cinfo->card; | |
355 | ||
356 | t1_disable_irq(card->port); | |
357 | b1_reset(card->port); | |
358 | b1_reset(card->port); | |
359 | t1_reset(card->port); | |
360 | ||
361 | detach_capi_ctr(&cinfo->capi_ctrl); | |
362 | free_irq(card->irq, card); | |
363 | release_region(card->port, AVMB1_PORTLEN); | |
364 | b1_free_card(card); | |
365 | } | |
366 | ||
367 | /* ------------------------------------------------------------- */ | |
368 | ||
369 | static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); | |
370 | static char *t1isa_procinfo(struct capi_ctr *ctrl); | |
371 | ||
372 | static int t1isa_probe(struct pci_dev *pdev, int cardnr) | |
373 | { | |
374 | avmctrl_info *cinfo; | |
375 | avmcard *card; | |
376 | int retval; | |
377 | ||
378 | card = b1_alloc_card(1); | |
379 | if (!card) { | |
380 | printk(KERN_WARNING "t1isa: no memory.\n"); | |
381 | retval = -ENOMEM; | |
382 | goto err; | |
383 | } | |
384 | ||
385 | cinfo = card->ctrlinfo; | |
386 | card->port = pci_resource_start(pdev, 0); | |
387 | card->irq = pdev->irq; | |
388 | card->cardtype = avm_t1isa; | |
389 | card->cardnr = cardnr; | |
390 | sprintf(card->name, "t1isa-%x", card->port); | |
391 | ||
392 | if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) { | |
393 | printk(KERN_WARNING "t1isa: invalid port 0x%x.\n", card->port); | |
394 | retval = -EINVAL; | |
395 | goto err_free; | |
396 | } | |
397 | if (hema_irq_table[card->irq & 0xf] == 0) { | |
398 | printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq); | |
399 | retval = -EINVAL; | |
400 | goto err_free; | |
401 | } | |
402 | if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { | |
403 | printk(KERN_INFO "t1isa: ports 0x%03x-0x%03x in use.\n", | |
404 | card->port, card->port + AVMB1_PORTLEN); | |
405 | retval = -EBUSY; | |
406 | goto err_free; | |
407 | } | |
408 | retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card); | |
409 | if (retval) { | |
410 | printk(KERN_INFO "t1isa: unable to get IRQ %d.\n", card->irq); | |
411 | retval = -EBUSY; | |
412 | goto err_release_region; | |
413 | } | |
414 | ||
415 | if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) { | |
416 | printk(KERN_INFO "t1isa: NO card at 0x%x (%d)\n", | |
417 | card->port, retval); | |
418 | retval = -ENODEV; | |
419 | goto err_free_irq; | |
420 | } | |
421 | t1_disable_irq(card->port); | |
422 | b1_reset(card->port); | |
423 | ||
424 | cinfo->capi_ctrl.owner = THIS_MODULE; | |
425 | cinfo->capi_ctrl.driver_name = "t1isa"; | |
426 | cinfo->capi_ctrl.driverdata = cinfo; | |
427 | cinfo->capi_ctrl.register_appl = b1_register_appl; | |
428 | cinfo->capi_ctrl.release_appl = b1_release_appl; | |
429 | cinfo->capi_ctrl.send_message = t1isa_send_message; | |
430 | cinfo->capi_ctrl.load_firmware = t1isa_load_firmware; | |
431 | cinfo->capi_ctrl.reset_ctr = t1isa_reset_ctr; | |
432 | cinfo->capi_ctrl.procinfo = t1isa_procinfo; | |
433 | cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc; | |
434 | strcpy(cinfo->capi_ctrl.name, card->name); | |
435 | ||
436 | retval = attach_capi_ctr(&cinfo->capi_ctrl); | |
437 | if (retval) { | |
438 | printk(KERN_INFO "t1isa: attach controller failed.\n"); | |
439 | goto err_free_irq; | |
440 | } | |
441 | ||
442 | printk(KERN_INFO "t1isa: AVM T1 ISA at i/o %#x, irq %d, card %d\n", | |
443 | card->port, card->irq, card->cardnr); | |
444 | ||
445 | pci_set_drvdata(pdev, cinfo); | |
446 | return 0; | |
447 | ||
448 | err_free_irq: | |
449 | free_irq(card->irq, card); | |
450 | err_release_region: | |
451 | release_region(card->port, AVMB1_PORTLEN); | |
452 | err_free: | |
453 | b1_free_card(card); | |
454 | err: | |
455 | return retval; | |
456 | } | |
457 | ||
458 | static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) | |
459 | { | |
460 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
461 | avmcard *card = cinfo->card; | |
462 | unsigned int port = card->port; | |
463 | unsigned long flags; | |
464 | u16 len = CAPIMSG_LEN(skb->data); | |
465 | u8 cmd = CAPIMSG_COMMAND(skb->data); | |
466 | u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); | |
467 | u16 dlen, retval; | |
468 | ||
469 | if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { | |
470 | retval = capilib_data_b3_req(&cinfo->ncci_head, | |
471 | CAPIMSG_APPID(skb->data), | |
472 | CAPIMSG_NCCI(skb->data), | |
473 | CAPIMSG_MSGID(skb->data)); | |
474 | if (retval != CAPI_NOERROR) | |
475 | return retval; | |
476 | ||
477 | dlen = CAPIMSG_DATALEN(skb->data); | |
478 | ||
479 | spin_lock_irqsave(&card->lock, flags); | |
480 | b1_put_byte(port, SEND_DATA_B3_REQ); | |
481 | t1_put_slice(port, skb->data, len); | |
482 | t1_put_slice(port, skb->data + len, dlen); | |
483 | spin_unlock_irqrestore(&card->lock, flags); | |
484 | } else { | |
485 | ||
486 | spin_lock_irqsave(&card->lock, flags); | |
487 | b1_put_byte(port, SEND_MESSAGE); | |
488 | t1_put_slice(port, skb->data, len); | |
489 | spin_unlock_irqrestore(&card->lock, flags); | |
490 | } | |
491 | ||
492 | dev_kfree_skb_any(skb); | |
493 | return CAPI_NOERROR; | |
494 | } | |
495 | /* ------------------------------------------------------------- */ | |
496 | ||
497 | static char *t1isa_procinfo(struct capi_ctr *ctrl) | |
498 | { | |
499 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
500 | ||
501 | if (!cinfo) | |
502 | return ""; | |
503 | sprintf(cinfo->infobuf, "%s %s 0x%x %d %d", | |
504 | cinfo->cardname[0] ? cinfo->cardname : "-", | |
505 | cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", | |
506 | cinfo->card ? cinfo->card->port : 0x0, | |
507 | cinfo->card ? cinfo->card->irq : 0, | |
508 | cinfo->card ? cinfo->card->cardnr : 0 | |
509 | ); | |
510 | return cinfo->infobuf; | |
511 | } | |
512 | ||
513 | ||
514 | /* ------------------------------------------------------------- */ | |
515 | ||
516 | #define MAX_CARDS 4 | |
517 | static struct pci_dev isa_dev[MAX_CARDS]; | |
518 | static int io[MAX_CARDS]; | |
519 | static int irq[MAX_CARDS]; | |
520 | static int cardnr[MAX_CARDS]; | |
521 | ||
8d3b33f6 RR |
522 | module_param_array(io, int, NULL, 0); |
523 | module_param_array(irq, int, NULL, 0); | |
524 | module_param_array(cardnr, int, NULL, 0); | |
1da177e4 LT |
525 | MODULE_PARM_DESC(io, "I/O base address(es)"); |
526 | MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); | |
527 | MODULE_PARM_DESC(cardnr, "Card number(s) (as jumpered)"); | |
528 | ||
529 | static int t1isa_add_card(struct capi_driver *driver, capicardparams *data) | |
530 | { | |
531 | int i; | |
532 | ||
533 | for (i = 0; i < MAX_CARDS; i++) { | |
534 | if (isa_dev[i].resource[0].start) | |
535 | continue; | |
536 | ||
537 | isa_dev[i].resource[0].start = data->port; | |
538 | isa_dev[i].irq = data->irq; | |
539 | ||
540 | if (t1isa_probe(&isa_dev[i], data->cardnr) == 0) | |
541 | return 0; | |
542 | } | |
543 | return -ENODEV; | |
544 | } | |
545 | ||
546 | static struct capi_driver capi_driver_t1isa = { | |
547 | .name = "t1isa", | |
548 | .revision = "1.0", | |
549 | .add_card = t1isa_add_card, | |
550 | }; | |
551 | ||
552 | static int __init t1isa_init(void) | |
553 | { | |
554 | char rev[32]; | |
555 | char *p; | |
556 | int i; | |
557 | ||
558 | if ((p = strchr(revision, ':')) != 0 && p[1]) { | |
559 | strlcpy(rev, p + 2, 32); | |
560 | if ((p = strchr(rev, '$')) != 0 && p > rev) | |
561 | *(p-1) = 0; | |
562 | } else | |
563 | strcpy(rev, "1.0"); | |
564 | ||
565 | for (i = 0; i < MAX_CARDS; i++) { | |
566 | if (!io[i]) | |
567 | break; | |
568 | ||
569 | isa_dev[i].resource[0].start = io[i]; | |
570 | isa_dev[i].irq = irq[i]; | |
571 | ||
572 | if (t1isa_probe(&isa_dev[i], cardnr[i]) != 0) | |
573 | return -ENODEV; | |
574 | } | |
575 | ||
576 | strlcpy(capi_driver_t1isa.revision, rev, 32); | |
577 | register_capi_driver(&capi_driver_t1isa); | |
578 | printk(KERN_INFO "t1isa: revision %s\n", rev); | |
579 | ||
580 | return 0; | |
581 | } | |
582 | ||
583 | static void __exit t1isa_exit(void) | |
584 | { | |
585 | int i; | |
586 | ||
587 | for (i = 0; i < MAX_CARDS; i++) { | |
588 | if (!io[i]) | |
589 | break; | |
590 | ||
591 | t1isa_remove(&isa_dev[i]); | |
592 | } | |
593 | } | |
594 | ||
595 | module_init(t1isa_init); | |
596 | module_exit(t1isa_exit); |