diff options
Diffstat (limited to 'drivers/isdn/hardware/avm/t1isa.c')
-rw-r--r-- | drivers/isdn/hardware/avm/t1isa.c | 596 |
1 files changed, 596 insertions, 0 deletions
diff --git a/drivers/isdn/hardware/avm/t1isa.c b/drivers/isdn/hardware/avm/t1isa.c new file mode 100644 index 000000000000..cb9d9cee2a64 --- /dev/null +++ b/drivers/isdn/hardware/avm/t1isa.c | |||
@@ -0,0 +1,596 @@ | |||
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 | |||
134 | static irqreturn_t t1isa_interrupt(int interrupt, void *devptr, struct pt_regs *regs) | ||
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 | |||
331 | void t1isa_reset_ctr(struct capi_ctr *ctrl) | ||
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 | |||
522 | MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i"); | ||
523 | MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i"); | ||
524 | MODULE_PARM(cardnr, "1-" __MODULE_STRING(MAX_CARDS) "i"); | ||
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); | ||