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); | |
1da177e4 | 183 | if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { |
feea6d4d | 184 | spin_unlock_irqrestore(&card->lock, flags); |
1da177e4 LT |
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)); | |
feea6d4d | 193 | spin_unlock_irqrestore(&card->lock, flags); |
1da177e4 LT |
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); | |
1da177e4 | 203 | capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize); |
feea6d4d | 204 | spin_unlock_irqrestore(&card->lock, flags); |
1da177e4 LT |
205 | break; |
206 | ||
207 | case RECEIVE_FREE_NCCI: | |
208 | ||
209 | ApplId = b1_get_word(card->port); | |
210 | NCCI = b1_get_word(card->port); | |
1da177e4 LT |
211 | if (NCCI != 0xffffffff) |
212 | capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI); | |
feea6d4d | 213 | spin_unlock_irqrestore(&card->lock, flags); |
1da177e4 LT |
214 | break; |
215 | ||
216 | case RECEIVE_START: | |
217 | b1_put_byte(card->port, SEND_POLLACK); | |
218 | spin_unlock_irqrestore(&card->lock, flags); | |
219 | capi_ctr_resume_output(ctrl); | |
220 | break; | |
221 | ||
222 | case RECEIVE_STOP: | |
223 | spin_unlock_irqrestore(&card->lock, flags); | |
224 | capi_ctr_suspend_output(ctrl); | |
225 | break; | |
226 | ||
227 | case RECEIVE_INIT: | |
228 | ||
229 | cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf); | |
230 | spin_unlock_irqrestore(&card->lock, flags); | |
231 | b1_parse_version(cinfo); | |
232 | printk(KERN_INFO "%s: %s-card (%s) now active\n", | |
233 | card->name, | |
234 | cinfo->version[VER_CARDTYPE], | |
235 | cinfo->version[VER_DRIVER]); | |
236 | capi_ctr_ready(ctrl); | |
237 | break; | |
238 | ||
239 | case RECEIVE_TASK_READY: | |
240 | ApplId = (unsigned) b1_get_word(card->port); | |
241 | MsgLen = t1_get_slice(card->port, card->msgbuf); | |
242 | spin_unlock_irqrestore(&card->lock, flags); | |
243 | card->msgbuf[MsgLen] = 0; | |
244 | while ( MsgLen > 0 | |
245 | && ( card->msgbuf[MsgLen-1] == '\n' | |
246 | || card->msgbuf[MsgLen-1] == '\r')) { | |
247 | card->msgbuf[MsgLen-1] = 0; | |
248 | MsgLen--; | |
249 | } | |
250 | printk(KERN_INFO "%s: task %d \"%s\" ready.\n", | |
251 | card->name, ApplId, card->msgbuf); | |
252 | break; | |
253 | ||
254 | case RECEIVE_DEBUGMSG: | |
255 | MsgLen = t1_get_slice(card->port, card->msgbuf); | |
256 | spin_unlock_irqrestore(&card->lock, flags); | |
257 | card->msgbuf[MsgLen] = 0; | |
258 | while ( MsgLen > 0 | |
259 | && ( card->msgbuf[MsgLen-1] == '\n' | |
260 | || card->msgbuf[MsgLen-1] == '\r')) { | |
261 | card->msgbuf[MsgLen-1] = 0; | |
262 | MsgLen--; | |
263 | } | |
264 | printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); | |
265 | break; | |
266 | ||
267 | ||
268 | case 0xff: | |
269 | spin_unlock_irqrestore(&card->lock, flags); | |
270 | printk(KERN_ERR "%s: card reseted ?\n", card->name); | |
271 | return IRQ_HANDLED; | |
272 | default: | |
273 | spin_unlock_irqrestore(&card->lock, flags); | |
274 | printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", | |
275 | card->name, b1cmd); | |
276 | return IRQ_NONE; | |
277 | } | |
278 | } | |
279 | return IRQ_HANDLED; | |
280 | } | |
281 | ||
282 | /* ------------------------------------------------------------- */ | |
283 | ||
284 | static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) | |
285 | { | |
286 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
287 | avmcard *card = cinfo->card; | |
288 | unsigned int port = card->port; | |
289 | unsigned long flags; | |
290 | int retval; | |
291 | ||
292 | t1_disable_irq(port); | |
293 | b1_reset(port); | |
294 | ||
295 | if ((retval = b1_load_t4file(card, &data->firmware))) { | |
296 | b1_reset(port); | |
297 | printk(KERN_ERR "%s: failed to load t4file!!\n", | |
298 | card->name); | |
299 | return retval; | |
300 | } | |
301 | ||
302 | if (data->configuration.len > 0 && data->configuration.data) { | |
303 | if ((retval = b1_load_config(card, &data->configuration))) { | |
304 | b1_reset(port); | |
305 | printk(KERN_ERR "%s: failed to load config!!\n", | |
306 | card->name); | |
307 | return retval; | |
308 | } | |
309 | } | |
310 | ||
311 | if (!b1_loaded(card)) { | |
312 | printk(KERN_ERR "%s: failed to load t4file.\n", card->name); | |
313 | return -EIO; | |
314 | } | |
315 | ||
316 | spin_lock_irqsave(&card->lock, flags); | |
317 | b1_setinterrupt(port, card->irq, card->cardtype); | |
318 | b1_put_byte(port, SEND_INIT); | |
319 | b1_put_word(port, CAPI_MAXAPPL); | |
320 | b1_put_word(port, AVM_NCCI_PER_CHANNEL*30); | |
321 | b1_put_word(port, ctrl->cnr - 1); | |
322 | spin_unlock_irqrestore(&card->lock, flags); | |
323 | ||
324 | return 0; | |
325 | } | |
326 | ||
08e51533 | 327 | static void t1isa_reset_ctr(struct capi_ctr *ctrl) |
1da177e4 LT |
328 | { |
329 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
330 | avmcard *card = cinfo->card; | |
331 | unsigned int port = card->port; | |
feea6d4d | 332 | unsigned long flags; |
1da177e4 LT |
333 | |
334 | t1_disable_irq(port); | |
335 | b1_reset(port); | |
336 | b1_reset(port); | |
337 | ||
338 | memset(cinfo->version, 0, sizeof(cinfo->version)); | |
feea6d4d | 339 | spin_lock_irqsave(&card->lock, flags); |
1da177e4 | 340 | capilib_release(&cinfo->ncci_head); |
feea6d4d | 341 | spin_unlock_irqrestore(&card->lock, flags); |
4e329972 | 342 | capi_ctr_down(ctrl); |
1da177e4 LT |
343 | } |
344 | ||
345 | static void t1isa_remove(struct pci_dev *pdev) | |
346 | { | |
347 | avmctrl_info *cinfo = pci_get_drvdata(pdev); | |
348 | avmcard *card; | |
349 | ||
350 | if (!cinfo) | |
351 | return; | |
352 | ||
353 | card = cinfo->card; | |
354 | ||
355 | t1_disable_irq(card->port); | |
356 | b1_reset(card->port); | |
357 | b1_reset(card->port); | |
358 | t1_reset(card->port); | |
359 | ||
360 | detach_capi_ctr(&cinfo->capi_ctrl); | |
361 | free_irq(card->irq, card); | |
362 | release_region(card->port, AVMB1_PORTLEN); | |
363 | b1_free_card(card); | |
364 | } | |
365 | ||
366 | /* ------------------------------------------------------------- */ | |
367 | ||
368 | static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); | |
369 | static char *t1isa_procinfo(struct capi_ctr *ctrl); | |
370 | ||
371 | static int t1isa_probe(struct pci_dev *pdev, int cardnr) | |
372 | { | |
373 | avmctrl_info *cinfo; | |
374 | avmcard *card; | |
375 | int retval; | |
376 | ||
377 | card = b1_alloc_card(1); | |
378 | if (!card) { | |
379 | printk(KERN_WARNING "t1isa: no memory.\n"); | |
380 | retval = -ENOMEM; | |
381 | goto err; | |
382 | } | |
383 | ||
384 | cinfo = card->ctrlinfo; | |
385 | card->port = pci_resource_start(pdev, 0); | |
386 | card->irq = pdev->irq; | |
387 | card->cardtype = avm_t1isa; | |
388 | card->cardnr = cardnr; | |
389 | sprintf(card->name, "t1isa-%x", card->port); | |
390 | ||
391 | if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) { | |
392 | printk(KERN_WARNING "t1isa: invalid port 0x%x.\n", card->port); | |
393 | retval = -EINVAL; | |
394 | goto err_free; | |
395 | } | |
396 | if (hema_irq_table[card->irq & 0xf] == 0) { | |
397 | printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq); | |
398 | retval = -EINVAL; | |
399 | goto err_free; | |
400 | } | |
401 | if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { | |
402 | printk(KERN_INFO "t1isa: ports 0x%03x-0x%03x in use.\n", | |
403 | card->port, card->port + AVMB1_PORTLEN); | |
404 | retval = -EBUSY; | |
405 | goto err_free; | |
406 | } | |
407 | retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card); | |
408 | if (retval) { | |
409 | printk(KERN_INFO "t1isa: unable to get IRQ %d.\n", card->irq); | |
410 | retval = -EBUSY; | |
411 | goto err_release_region; | |
412 | } | |
413 | ||
414 | if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) { | |
415 | printk(KERN_INFO "t1isa: NO card at 0x%x (%d)\n", | |
416 | card->port, retval); | |
417 | retval = -ENODEV; | |
418 | goto err_free_irq; | |
419 | } | |
420 | t1_disable_irq(card->port); | |
421 | b1_reset(card->port); | |
422 | ||
423 | cinfo->capi_ctrl.owner = THIS_MODULE; | |
424 | cinfo->capi_ctrl.driver_name = "t1isa"; | |
425 | cinfo->capi_ctrl.driverdata = cinfo; | |
426 | cinfo->capi_ctrl.register_appl = b1_register_appl; | |
427 | cinfo->capi_ctrl.release_appl = b1_release_appl; | |
428 | cinfo->capi_ctrl.send_message = t1isa_send_message; | |
429 | cinfo->capi_ctrl.load_firmware = t1isa_load_firmware; | |
430 | cinfo->capi_ctrl.reset_ctr = t1isa_reset_ctr; | |
431 | cinfo->capi_ctrl.procinfo = t1isa_procinfo; | |
432 | cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc; | |
433 | strcpy(cinfo->capi_ctrl.name, card->name); | |
434 | ||
435 | retval = attach_capi_ctr(&cinfo->capi_ctrl); | |
436 | if (retval) { | |
437 | printk(KERN_INFO "t1isa: attach controller failed.\n"); | |
438 | goto err_free_irq; | |
439 | } | |
440 | ||
441 | printk(KERN_INFO "t1isa: AVM T1 ISA at i/o %#x, irq %d, card %d\n", | |
442 | card->port, card->irq, card->cardnr); | |
443 | ||
444 | pci_set_drvdata(pdev, cinfo); | |
445 | return 0; | |
446 | ||
447 | err_free_irq: | |
448 | free_irq(card->irq, card); | |
449 | err_release_region: | |
450 | release_region(card->port, AVMB1_PORTLEN); | |
451 | err_free: | |
452 | b1_free_card(card); | |
453 | err: | |
454 | return retval; | |
455 | } | |
456 | ||
457 | static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) | |
458 | { | |
459 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
460 | avmcard *card = cinfo->card; | |
461 | unsigned int port = card->port; | |
462 | unsigned long flags; | |
463 | u16 len = CAPIMSG_LEN(skb->data); | |
464 | u8 cmd = CAPIMSG_COMMAND(skb->data); | |
465 | u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); | |
466 | u16 dlen, retval; | |
467 | ||
feea6d4d | 468 | spin_lock_irqsave(&card->lock, flags); |
1da177e4 LT |
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)); | |
feea6d4d KK |
474 | if (retval != CAPI_NOERROR) { |
475 | spin_unlock_irqrestore(&card->lock, flags); | |
1da177e4 | 476 | return retval; |
feea6d4d | 477 | } |
1da177e4 LT |
478 | dlen = CAPIMSG_DATALEN(skb->data); |
479 | ||
1da177e4 LT |
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); | |
1da177e4 | 483 | } else { |
1da177e4 LT |
484 | b1_put_byte(port, SEND_MESSAGE); |
485 | t1_put_slice(port, skb->data, len); | |
1da177e4 | 486 | } |
feea6d4d | 487 | spin_unlock_irqrestore(&card->lock, flags); |
1da177e4 LT |
488 | dev_kfree_skb_any(skb); |
489 | return CAPI_NOERROR; | |
490 | } | |
491 | /* ------------------------------------------------------------- */ | |
492 | ||
493 | static char *t1isa_procinfo(struct capi_ctr *ctrl) | |
494 | { | |
495 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
496 | ||
497 | if (!cinfo) | |
498 | return ""; | |
499 | sprintf(cinfo->infobuf, "%s %s 0x%x %d %d", | |
500 | cinfo->cardname[0] ? cinfo->cardname : "-", | |
501 | cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", | |
502 | cinfo->card ? cinfo->card->port : 0x0, | |
503 | cinfo->card ? cinfo->card->irq : 0, | |
504 | cinfo->card ? cinfo->card->cardnr : 0 | |
505 | ); | |
506 | return cinfo->infobuf; | |
507 | } | |
508 | ||
509 | ||
510 | /* ------------------------------------------------------------- */ | |
511 | ||
512 | #define MAX_CARDS 4 | |
513 | static struct pci_dev isa_dev[MAX_CARDS]; | |
514 | static int io[MAX_CARDS]; | |
515 | static int irq[MAX_CARDS]; | |
516 | static int cardnr[MAX_CARDS]; | |
517 | ||
8d3b33f6 RR |
518 | module_param_array(io, int, NULL, 0); |
519 | module_param_array(irq, int, NULL, 0); | |
520 | module_param_array(cardnr, int, NULL, 0); | |
1da177e4 LT |
521 | MODULE_PARM_DESC(io, "I/O base address(es)"); |
522 | MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); | |
523 | MODULE_PARM_DESC(cardnr, "Card number(s) (as jumpered)"); | |
524 | ||
525 | static int t1isa_add_card(struct capi_driver *driver, capicardparams *data) | |
526 | { | |
527 | int i; | |
528 | ||
529 | for (i = 0; i < MAX_CARDS; i++) { | |
530 | if (isa_dev[i].resource[0].start) | |
531 | continue; | |
532 | ||
533 | isa_dev[i].resource[0].start = data->port; | |
534 | isa_dev[i].irq = data->irq; | |
535 | ||
536 | if (t1isa_probe(&isa_dev[i], data->cardnr) == 0) | |
537 | return 0; | |
538 | } | |
539 | return -ENODEV; | |
540 | } | |
541 | ||
542 | static struct capi_driver capi_driver_t1isa = { | |
543 | .name = "t1isa", | |
544 | .revision = "1.0", | |
545 | .add_card = t1isa_add_card, | |
546 | }; | |
547 | ||
548 | static int __init t1isa_init(void) | |
549 | { | |
550 | char rev[32]; | |
551 | char *p; | |
552 | int i; | |
553 | ||
8e44b29d | 554 | if ((p = strchr(revision, ':')) != NULL && p[1]) { |
1da177e4 | 555 | strlcpy(rev, p + 2, 32); |
8e44b29d | 556 | if ((p = strchr(rev, '$')) != NULL && p > rev) |
1da177e4 LT |
557 | *(p-1) = 0; |
558 | } else | |
559 | strcpy(rev, "1.0"); | |
560 | ||
561 | for (i = 0; i < MAX_CARDS; i++) { | |
562 | if (!io[i]) | |
563 | break; | |
564 | ||
565 | isa_dev[i].resource[0].start = io[i]; | |
566 | isa_dev[i].irq = irq[i]; | |
567 | ||
568 | if (t1isa_probe(&isa_dev[i], cardnr[i]) != 0) | |
569 | return -ENODEV; | |
570 | } | |
571 | ||
572 | strlcpy(capi_driver_t1isa.revision, rev, 32); | |
573 | register_capi_driver(&capi_driver_t1isa); | |
574 | printk(KERN_INFO "t1isa: revision %s\n", rev); | |
575 | ||
576 | return 0; | |
577 | } | |
578 | ||
579 | static void __exit t1isa_exit(void) | |
580 | { | |
581 | int i; | |
582 | ||
ba6d14af | 583 | unregister_capi_driver(&capi_driver_t1isa); |
1da177e4 LT |
584 | for (i = 0; i < MAX_CARDS; i++) { |
585 | if (!io[i]) | |
586 | break; | |
587 | ||
588 | t1isa_remove(&isa_dev[i]); | |
589 | } | |
590 | } | |
591 | ||
592 | module_init(t1isa_init); | |
593 | module_exit(t1isa_exit); |