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