]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* $Id: c4.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ |
475be4d8 | 2 | * |
1da177e4 | 3 | * Module for AVM C4 & C2 card. |
475be4d8 | 4 | * |
1da177e4 LT |
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 | ||
1da177e4 LT |
12 | #include <linux/module.h> |
13 | #include <linux/kernel.h> | |
9a58a80a AD |
14 | #include <linux/proc_fs.h> |
15 | #include <linux/seq_file.h> | |
1da177e4 LT |
16 | #include <linux/skbuff.h> |
17 | #include <linux/delay.h> | |
18 | #include <linux/mm.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/ioport.h> | |
21 | #include <linux/pci.h> | |
22 | #include <linux/capi.h> | |
23 | #include <linux/kernelcapi.h> | |
24 | #include <linux/init.h> | |
5a0e3ad6 | 25 | #include <linux/gfp.h> |
1da177e4 | 26 | #include <asm/io.h> |
7c0f6ba6 | 27 | #include <linux/uaccess.h> |
1da177e4 LT |
28 | #include <linux/netdevice.h> |
29 | #include <linux/isdn/capicmd.h> | |
30 | #include <linux/isdn/capiutil.h> | |
31 | #include <linux/isdn/capilli.h> | |
32 | #include "avmcard.h" | |
33 | ||
26fb5c58 RD |
34 | #undef AVM_C4_DEBUG |
35 | #undef AVM_C4_POLLDEBUG | |
1da177e4 LT |
36 | |
37 | /* ------------------------------------------------------------- */ | |
38 | ||
39 | static char *revision = "$Revision: 1.1.2.2 $"; | |
40 | ||
41 | /* ------------------------------------------------------------- */ | |
42 | ||
90ab5ee9 | 43 | static bool suppress_pollack; |
1da177e4 | 44 | |
65f96417 | 45 | static const struct pci_device_id c4_pci_tbl[] = { |
1da177e4 LT |
46 | { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, 0, 0, (unsigned long)4 }, |
47 | { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C2, 0, 0, (unsigned long)2 }, | |
48 | { } /* Terminating entry */ | |
49 | }; | |
50 | ||
51 | MODULE_DEVICE_TABLE(pci, c4_pci_tbl); | |
52 | MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM C2/C4 cards"); | |
53 | MODULE_AUTHOR("Carsten Paeth"); | |
54 | MODULE_LICENSE("GPL"); | |
8d3b33f6 | 55 | module_param(suppress_pollack, bool, 0); |
1da177e4 LT |
56 | |
57 | /* ------------------------------------------------------------- */ | |
58 | ||
59 | static void c4_dispatch_tx(avmcard *card); | |
60 | ||
61 | /* ------------------------------------------------------------- */ | |
62 | ||
63 | #define DC21285_DRAM_A0MR 0x40000000 | |
64 | #define DC21285_DRAM_A1MR 0x40004000 | |
65 | #define DC21285_DRAM_A2MR 0x40008000 | |
66 | #define DC21285_DRAM_A3MR 0x4000C000 | |
67 | ||
68 | #define CAS_OFFSET 0x88 | |
69 | ||
70 | #define DC21285_ARMCSR_BASE 0x42000000 | |
71 | ||
72 | #define PCI_OUT_INT_STATUS 0x30 | |
73 | #define PCI_OUT_INT_MASK 0x34 | |
74 | #define MAILBOX_0 0x50 | |
75 | #define MAILBOX_1 0x54 | |
76 | #define MAILBOX_2 0x58 | |
77 | #define MAILBOX_3 0x5C | |
78 | #define DOORBELL 0x60 | |
79 | #define DOORBELL_SETUP 0x64 | |
80 | ||
81 | #define CHAN_1_CONTROL 0x90 | |
82 | #define CHAN_2_CONTROL 0xB0 | |
83 | #define DRAM_TIMING 0x10C | |
84 | #define DRAM_ADDR_SIZE_0 0x110 | |
85 | #define DRAM_ADDR_SIZE_1 0x114 | |
86 | #define DRAM_ADDR_SIZE_2 0x118 | |
87 | #define DRAM_ADDR_SIZE_3 0x11C | |
88 | #define SA_CONTROL 0x13C | |
89 | #define XBUS_CYCLE 0x148 | |
90 | #define XBUS_STROBE 0x14C | |
91 | #define DBELL_PCI_MASK 0x150 | |
92 | #define DBELL_SA_MASK 0x154 | |
93 | ||
94 | #define SDRAM_SIZE 0x1000000 | |
95 | ||
96 | /* ------------------------------------------------------------- */ | |
97 | ||
98 | #define MBOX_PEEK_POKE MAILBOX_0 | |
99 | ||
100 | #define DBELL_ADDR 0x01 | |
101 | #define DBELL_DATA 0x02 | |
102 | #define DBELL_RNWR 0x40 | |
103 | #define DBELL_INIT 0x80 | |
104 | ||
105 | /* ------------------------------------------------------------- */ | |
106 | ||
107 | #define MBOX_UP_ADDR MAILBOX_0 | |
108 | #define MBOX_UP_LEN MAILBOX_1 | |
109 | #define MBOX_DOWN_ADDR MAILBOX_2 | |
110 | #define MBOX_DOWN_LEN MAILBOX_3 | |
111 | ||
112 | #define DBELL_UP_HOST 0x00000100 | |
113 | #define DBELL_UP_ARM 0x00000200 | |
114 | #define DBELL_DOWN_HOST 0x00000400 | |
115 | #define DBELL_DOWN_ARM 0x00000800 | |
116 | #define DBELL_RESET_HOST 0x40000000 | |
117 | #define DBELL_RESET_ARM 0x80000000 | |
118 | ||
119 | /* ------------------------------------------------------------- */ | |
120 | ||
121 | #define DRAM_TIMING_DEF 0x001A01A5 | |
122 | #define DRAM_AD_SZ_DEF0 0x00000045 | |
123 | #define DRAM_AD_SZ_NULL 0x00000000 | |
124 | ||
125 | #define SA_CTL_ALLRIGHT 0x64AA0271 | |
126 | ||
127 | #define INIT_XBUS_CYCLE 0x100016DB | |
128 | #define INIT_XBUS_STROBE 0xF1F1F1F1 | |
129 | ||
130 | /* ------------------------------------------------------------- */ | |
131 | ||
475be4d8 JP |
132 | #define RESET_TIMEOUT (15 * HZ) /* 15 sec */ |
133 | #define PEEK_POKE_TIMEOUT (HZ / 10) /* 0.1 sec */ | |
1da177e4 LT |
134 | |
135 | /* ------------------------------------------------------------- */ | |
136 | ||
137 | #define c4outmeml(addr, value) writel(value, addr) | |
138 | #define c4inmeml(addr) readl(addr) | |
139 | #define c4outmemw(addr, value) writew(value, addr) | |
140 | #define c4inmemw(addr) readw(addr) | |
141 | #define c4outmemb(addr, value) writeb(value, addr) | |
142 | #define c4inmemb(addr) readb(addr) | |
143 | ||
144 | /* ------------------------------------------------------------- */ | |
145 | ||
146 | static inline int wait_for_doorbell(avmcard *card, unsigned long t) | |
147 | { | |
148 | unsigned long stop; | |
149 | ||
150 | stop = jiffies + t; | |
475be4d8 | 151 | while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) { |
1da177e4 LT |
152 | if (!time_before(jiffies, stop)) |
153 | return -1; | |
154 | mb(); | |
155 | } | |
156 | return 0; | |
157 | } | |
158 | ||
159 | static int c4_poke(avmcard *card, unsigned long off, unsigned long value) | |
160 | { | |
161 | ||
475be4d8 | 162 | if (wait_for_doorbell(card, HZ / 10) < 0) |
1da177e4 | 163 | return -1; |
1da177e4 | 164 | |
475be4d8 JP |
165 | c4outmeml(card->mbase + MBOX_PEEK_POKE, off); |
166 | c4outmeml(card->mbase + DOORBELL, DBELL_ADDR); | |
167 | ||
168 | if (wait_for_doorbell(card, HZ / 10) < 0) | |
1da177e4 LT |
169 | return -1; |
170 | ||
475be4d8 JP |
171 | c4outmeml(card->mbase + MBOX_PEEK_POKE, value); |
172 | c4outmeml(card->mbase + DOORBELL, DBELL_DATA | DBELL_ADDR); | |
1da177e4 LT |
173 | |
174 | return 0; | |
175 | } | |
176 | ||
177 | static int c4_peek(avmcard *card, unsigned long off, unsigned long *valuep) | |
178 | { | |
475be4d8 | 179 | if (wait_for_doorbell(card, HZ / 10) < 0) |
1da177e4 LT |
180 | return -1; |
181 | ||
475be4d8 JP |
182 | c4outmeml(card->mbase + MBOX_PEEK_POKE, off); |
183 | c4outmeml(card->mbase + DOORBELL, DBELL_RNWR | DBELL_ADDR); | |
1da177e4 | 184 | |
475be4d8 | 185 | if (wait_for_doorbell(card, HZ / 10) < 0) |
1da177e4 LT |
186 | return -1; |
187 | ||
475be4d8 | 188 | *valuep = c4inmeml(card->mbase + MBOX_PEEK_POKE); |
1da177e4 LT |
189 | |
190 | return 0; | |
191 | } | |
192 | ||
193 | /* ------------------------------------------------------------- */ | |
194 | ||
475be4d8 | 195 | static int c4_load_t4file(avmcard *card, capiloaddatapart *t4file) |
1da177e4 LT |
196 | { |
197 | u32 val; | |
198 | unsigned char *dp; | |
199 | u_int left; | |
200 | u32 loadoff = 0; | |
201 | ||
202 | dp = t4file->data; | |
203 | left = t4file->len; | |
204 | while (left >= sizeof(u32)) { | |
475be4d8 | 205 | if (t4file->user) { |
1da177e4 LT |
206 | if (copy_from_user(&val, dp, sizeof(val))) |
207 | return -EFAULT; | |
208 | } else { | |
209 | memcpy(&val, dp, sizeof(val)); | |
210 | } | |
211 | if (c4_poke(card, loadoff, val)) { | |
212 | printk(KERN_ERR "%s: corrupted firmware file ?\n", | |
475be4d8 | 213 | card->name); |
1da177e4 LT |
214 | return -EIO; |
215 | } | |
216 | left -= sizeof(u32); | |
217 | dp += sizeof(u32); | |
218 | loadoff += sizeof(u32); | |
219 | } | |
220 | if (left) { | |
221 | val = 0; | |
222 | if (t4file->user) { | |
223 | if (copy_from_user(&val, dp, left)) | |
224 | return -EFAULT; | |
225 | } else { | |
226 | memcpy(&val, dp, left); | |
227 | } | |
228 | if (c4_poke(card, loadoff, val)) { | |
229 | printk(KERN_ERR "%s: corrupted firmware file ?\n", | |
475be4d8 | 230 | card->name); |
1da177e4 LT |
231 | return -EIO; |
232 | } | |
233 | } | |
234 | return 0; | |
235 | } | |
236 | ||
237 | /* ------------------------------------------------------------- */ | |
238 | ||
239 | static inline void _put_byte(void **pp, u8 val) | |
240 | { | |
241 | u8 *s = *pp; | |
242 | *s++ = val; | |
243 | *pp = s; | |
244 | } | |
245 | ||
246 | static inline void _put_word(void **pp, u32 val) | |
247 | { | |
248 | u8 *s = *pp; | |
249 | *s++ = val & 0xff; | |
250 | *s++ = (val >> 8) & 0xff; | |
251 | *s++ = (val >> 16) & 0xff; | |
252 | *s++ = (val >> 24) & 0xff; | |
253 | *pp = s; | |
254 | } | |
255 | ||
256 | static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len) | |
257 | { | |
258 | unsigned i = len; | |
259 | _put_word(pp, i); | |
260 | while (i-- > 0) | |
261 | _put_byte(pp, *dp++); | |
262 | } | |
263 | ||
264 | static inline u8 _get_byte(void **pp) | |
265 | { | |
266 | u8 *s = *pp; | |
267 | u8 val; | |
268 | val = *s++; | |
269 | *pp = s; | |
270 | return val; | |
271 | } | |
272 | ||
273 | static inline u32 _get_word(void **pp) | |
274 | { | |
275 | u8 *s = *pp; | |
276 | u32 val; | |
277 | val = *s++; | |
278 | val |= (*s++ << 8); | |
279 | val |= (*s++ << 16); | |
280 | val |= (*s++ << 24); | |
281 | *pp = s; | |
282 | return val; | |
283 | } | |
284 | ||
285 | static inline u32 _get_slice(void **pp, unsigned char *dp) | |
286 | { | |
287 | unsigned int len, i; | |
288 | ||
289 | len = i = _get_word(pp); | |
290 | while (i-- > 0) *dp++ = _get_byte(pp); | |
291 | return len; | |
292 | } | |
293 | ||
294 | /* ------------------------------------------------------------- */ | |
295 | ||
296 | static void c4_reset(avmcard *card) | |
297 | { | |
298 | unsigned long stop; | |
299 | ||
475be4d8 | 300 | c4outmeml(card->mbase + DOORBELL, DBELL_RESET_ARM); |
1da177e4 | 301 | |
475be4d8 JP |
302 | stop = jiffies + HZ * 10; |
303 | while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) { | |
1da177e4 LT |
304 | if (!time_before(jiffies, stop)) |
305 | return; | |
475be4d8 | 306 | c4outmeml(card->mbase + DOORBELL, DBELL_ADDR); |
1da177e4 LT |
307 | mb(); |
308 | } | |
309 | ||
310 | c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0); | |
311 | c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0); | |
312 | } | |
313 | ||
314 | /* ------------------------------------------------------------- */ | |
315 | ||
316 | static int c4_detect(avmcard *card) | |
317 | { | |
318 | unsigned long stop, dummy; | |
319 | ||
475be4d8 JP |
320 | c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x0c); |
321 | if (c4inmeml(card->mbase + PCI_OUT_INT_MASK) != 0x0c) | |
1da177e4 LT |
322 | return 1; |
323 | ||
475be4d8 | 324 | c4outmeml(card->mbase + DOORBELL, DBELL_RESET_ARM); |
1da177e4 | 325 | |
475be4d8 JP |
326 | stop = jiffies + HZ * 10; |
327 | while (c4inmeml(card->mbase + DOORBELL) != 0xffffffff) { | |
1da177e4 LT |
328 | if (!time_before(jiffies, stop)) |
329 | return 2; | |
475be4d8 | 330 | c4outmeml(card->mbase + DOORBELL, DBELL_ADDR); |
1da177e4 LT |
331 | mb(); |
332 | } | |
333 | ||
334 | c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0); | |
335 | c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0); | |
336 | ||
475be4d8 JP |
337 | c4outmeml(card->mbase + MAILBOX_0, 0x55aa55aa); |
338 | if (c4inmeml(card->mbase + MAILBOX_0) != 0x55aa55aa) return 3; | |
1da177e4 | 339 | |
475be4d8 JP |
340 | c4outmeml(card->mbase + MAILBOX_0, 0xaa55aa55); |
341 | if (c4inmeml(card->mbase + MAILBOX_0) != 0xaa55aa55) return 4; | |
1da177e4 | 342 | |
475be4d8 JP |
343 | if (c4_poke(card, DC21285_ARMCSR_BASE + DBELL_SA_MASK, 0)) return 5; |
344 | if (c4_poke(card, DC21285_ARMCSR_BASE + DBELL_PCI_MASK, 0)) return 6; | |
345 | if (c4_poke(card, DC21285_ARMCSR_BASE + SA_CONTROL, SA_CTL_ALLRIGHT)) | |
1da177e4 | 346 | return 7; |
475be4d8 | 347 | if (c4_poke(card, DC21285_ARMCSR_BASE + XBUS_CYCLE, INIT_XBUS_CYCLE)) |
1da177e4 | 348 | return 8; |
475be4d8 | 349 | if (c4_poke(card, DC21285_ARMCSR_BASE + XBUS_STROBE, INIT_XBUS_STROBE)) |
1da177e4 | 350 | return 8; |
475be4d8 | 351 | if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_TIMING, 0)) return 9; |
1da177e4 | 352 | |
475be4d8 | 353 | mdelay(1); |
1da177e4 LT |
354 | |
355 | if (c4_peek(card, DC21285_DRAM_A0MR, &dummy)) return 10; | |
356 | if (c4_peek(card, DC21285_DRAM_A1MR, &dummy)) return 11; | |
357 | if (c4_peek(card, DC21285_DRAM_A2MR, &dummy)) return 12; | |
358 | if (c4_peek(card, DC21285_DRAM_A3MR, &dummy)) return 13; | |
359 | ||
475be4d8 JP |
360 | if (c4_poke(card, DC21285_DRAM_A0MR + CAS_OFFSET, 0)) return 14; |
361 | if (c4_poke(card, DC21285_DRAM_A1MR + CAS_OFFSET, 0)) return 15; | |
362 | if (c4_poke(card, DC21285_DRAM_A2MR + CAS_OFFSET, 0)) return 16; | |
363 | if (c4_poke(card, DC21285_DRAM_A3MR + CAS_OFFSET, 0)) return 17; | |
1da177e4 | 364 | |
475be4d8 | 365 | mdelay(1); |
1da177e4 | 366 | |
475be4d8 | 367 | if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_TIMING, DRAM_TIMING_DEF)) |
1da177e4 LT |
368 | return 18; |
369 | ||
475be4d8 | 370 | if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_0, DRAM_AD_SZ_DEF0)) |
1da177e4 | 371 | return 19; |
475be4d8 | 372 | if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_1, DRAM_AD_SZ_NULL)) |
1da177e4 | 373 | return 20; |
475be4d8 | 374 | if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_2, DRAM_AD_SZ_NULL)) |
1da177e4 | 375 | return 21; |
475be4d8 | 376 | if (c4_poke(card, DC21285_ARMCSR_BASE + DRAM_ADDR_SIZE_3, DRAM_AD_SZ_NULL)) |
1da177e4 LT |
377 | return 22; |
378 | ||
379 | /* Transputer test */ | |
475be4d8 JP |
380 | |
381 | if (c4_poke(card, 0x000000, 0x11111111) | |
1da177e4 | 382 | || c4_poke(card, 0x400000, 0x22222222) |
475be4d8 JP |
383 | || c4_poke(card, 0x800000, 0x33333333) |
384 | || c4_poke(card, 0xC00000, 0x44444444)) | |
1da177e4 LT |
385 | return 23; |
386 | ||
475be4d8 | 387 | if (c4_peek(card, 0x000000, &dummy) || dummy != 0x11111111 |
1da177e4 | 388 | || c4_peek(card, 0x400000, &dummy) || dummy != 0x22222222 |
475be4d8 JP |
389 | || c4_peek(card, 0x800000, &dummy) || dummy != 0x33333333 |
390 | || c4_peek(card, 0xC00000, &dummy) || dummy != 0x44444444) | |
1da177e4 LT |
391 | return 24; |
392 | ||
475be4d8 | 393 | if (c4_poke(card, 0x000000, 0x55555555) |
1da177e4 | 394 | || c4_poke(card, 0x400000, 0x66666666) |
475be4d8 JP |
395 | || c4_poke(card, 0x800000, 0x77777777) |
396 | || c4_poke(card, 0xC00000, 0x88888888)) | |
1da177e4 LT |
397 | return 25; |
398 | ||
475be4d8 | 399 | if (c4_peek(card, 0x000000, &dummy) || dummy != 0x55555555 |
1da177e4 | 400 | || c4_peek(card, 0x400000, &dummy) || dummy != 0x66666666 |
475be4d8 JP |
401 | || c4_peek(card, 0x800000, &dummy) || dummy != 0x77777777 |
402 | || c4_peek(card, 0xC00000, &dummy) || dummy != 0x88888888) | |
1da177e4 LT |
403 | return 26; |
404 | ||
405 | return 0; | |
406 | } | |
407 | ||
408 | /* ------------------------------------------------------------- */ | |
409 | ||
410 | static void c4_dispatch_tx(avmcard *card) | |
411 | { | |
412 | avmcard_dmainfo *dma = card->dma; | |
413 | struct sk_buff *skb; | |
414 | u8 cmd, subcmd; | |
415 | u16 len; | |
416 | u32 txlen; | |
417 | void *p; | |
418 | ||
419 | ||
420 | if (card->csr & DBELL_DOWN_ARM) { /* tx busy */ | |
421 | return; | |
422 | } | |
423 | ||
424 | skb = skb_dequeue(&dma->send_queue); | |
425 | if (!skb) { | |
26fb5c58 | 426 | #ifdef AVM_C4_DEBUG |
1da177e4 LT |
427 | printk(KERN_DEBUG "%s: tx underrun\n", card->name); |
428 | #endif | |
429 | return; | |
430 | } | |
431 | ||
432 | len = CAPIMSG_LEN(skb->data); | |
433 | ||
434 | if (len) { | |
435 | cmd = CAPIMSG_COMMAND(skb->data); | |
436 | subcmd = CAPIMSG_SUBCOMMAND(skb->data); | |
437 | ||
438 | p = dma->sendbuf.dmabuf; | |
439 | ||
440 | if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { | |
441 | u16 dlen = CAPIMSG_DATALEN(skb->data); | |
442 | _put_byte(&p, SEND_DATA_B3_REQ); | |
443 | _put_slice(&p, skb->data, len); | |
444 | _put_slice(&p, skb->data + len, dlen); | |
445 | } else { | |
446 | _put_byte(&p, SEND_MESSAGE); | |
447 | _put_slice(&p, skb->data, len); | |
448 | } | |
449 | txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf; | |
26fb5c58 | 450 | #ifdef AVM_C4_DEBUG |
1da177e4 LT |
451 | printk(KERN_DEBUG "%s: tx put msg len=%d\n", card->name, txlen); |
452 | #endif | |
453 | } else { | |
475be4d8 | 454 | txlen = skb->len - 2; |
26fb5c58 | 455 | #ifdef AVM_C4_POLLDEBUG |
1da177e4 LT |
456 | if (skb->data[2] == SEND_POLLACK) |
457 | printk(KERN_INFO "%s: ack to c4\n", card->name); | |
458 | #endif | |
26fb5c58 | 459 | #ifdef AVM_C4_DEBUG |
1da177e4 | 460 | printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n", |
475be4d8 | 461 | card->name, skb->data[2], txlen); |
1da177e4 | 462 | #endif |
d626f62b ACM |
463 | skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf, |
464 | skb->len - 2); | |
1da177e4 LT |
465 | } |
466 | txlen = (txlen + 3) & ~3; | |
467 | ||
475be4d8 JP |
468 | c4outmeml(card->mbase + MBOX_DOWN_ADDR, dma->sendbuf.dmaaddr); |
469 | c4outmeml(card->mbase + MBOX_DOWN_LEN, txlen); | |
1da177e4 LT |
470 | |
471 | card->csr |= DBELL_DOWN_ARM; | |
472 | ||
475be4d8 | 473 | c4outmeml(card->mbase + DOORBELL, DBELL_DOWN_ARM); |
1da177e4 LT |
474 | |
475 | dev_kfree_skb_any(skb); | |
476 | } | |
477 | ||
478 | /* ------------------------------------------------------------- */ | |
479 | ||
480 | static void queue_pollack(avmcard *card) | |
481 | { | |
482 | struct sk_buff *skb; | |
483 | void *p; | |
484 | ||
485 | skb = alloc_skb(3, GFP_ATOMIC); | |
486 | if (!skb) { | |
487 | printk(KERN_CRIT "%s: no memory, lost poll ack\n", | |
475be4d8 | 488 | card->name); |
1da177e4 LT |
489 | return; |
490 | } | |
491 | p = skb->data; | |
492 | _put_byte(&p, 0); | |
493 | _put_byte(&p, 0); | |
494 | _put_byte(&p, SEND_POLLACK); | |
495 | skb_put(skb, (u8 *)p - (u8 *)skb->data); | |
496 | ||
497 | skb_queue_tail(&card->dma->send_queue, skb); | |
498 | c4_dispatch_tx(card); | |
499 | } | |
500 | ||
501 | /* ------------------------------------------------------------- */ | |
502 | ||
503 | static void c4_handle_rx(avmcard *card) | |
504 | { | |
505 | avmcard_dmainfo *dma = card->dma; | |
506 | struct capi_ctr *ctrl; | |
507 | avmctrl_info *cinfo; | |
508 | struct sk_buff *skb; | |
509 | void *p = dma->recvbuf.dmabuf; | |
510 | u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize; | |
511 | u8 b1cmd = _get_byte(&p); | |
512 | u32 cidx; | |
513 | ||
514 | ||
26fb5c58 | 515 | #ifdef AVM_C4_DEBUG |
1da177e4 | 516 | printk(KERN_DEBUG "%s: rx 0x%x len=%lu\n", card->name, |
475be4d8 | 517 | b1cmd, (unsigned long)dma->recvlen); |
1da177e4 | 518 | #endif |
475be4d8 | 519 | |
1da177e4 LT |
520 | switch (b1cmd) { |
521 | case RECEIVE_DATA_B3_IND: | |
522 | ||
523 | ApplId = (unsigned) _get_word(&p); | |
524 | MsgLen = _get_slice(&p, card->msgbuf); | |
525 | DataB3Len = _get_slice(&p, card->databuf); | |
526 | cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr; | |
527 | if (cidx >= card->nlogcontr) cidx = 0; | |
528 | ctrl = &card->ctrlinfo[cidx].capi_ctrl; | |
529 | ||
530 | if (MsgLen < 30) { /* not CAPI 64Bit */ | |
475be4d8 | 531 | memset(card->msgbuf + MsgLen, 0, 30 - MsgLen); |
1da177e4 LT |
532 | MsgLen = 30; |
533 | CAPIMSG_SETLEN(card->msgbuf, 30); | |
534 | } | |
475be4d8 | 535 | if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) { |
1da177e4 | 536 | printk(KERN_ERR "%s: incoming packet dropped\n", |
475be4d8 | 537 | card->name); |
1da177e4 | 538 | } else { |
59ae1d12 JB |
539 | skb_put_data(skb, card->msgbuf, MsgLen); |
540 | skb_put_data(skb, card->databuf, DataB3Len); | |
1da177e4 LT |
541 | capi_ctr_handle_message(ctrl, ApplId, skb); |
542 | } | |
543 | break; | |
544 | ||
545 | case RECEIVE_MESSAGE: | |
546 | ||
547 | ApplId = (unsigned) _get_word(&p); | |
548 | MsgLen = _get_slice(&p, card->msgbuf); | |
549 | cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr; | |
550 | if (cidx >= card->nlogcontr) cidx = 0; | |
551 | cinfo = &card->ctrlinfo[cidx]; | |
552 | ctrl = &card->ctrlinfo[cidx].capi_ctrl; | |
553 | ||
554 | if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { | |
555 | printk(KERN_ERR "%s: incoming packet dropped\n", | |
475be4d8 | 556 | card->name); |
1da177e4 | 557 | } else { |
59ae1d12 | 558 | skb_put_data(skb, card->msgbuf, MsgLen); |
1da177e4 LT |
559 | if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) |
560 | capilib_data_b3_conf(&cinfo->ncci_head, ApplId, | |
561 | CAPIMSG_NCCI(skb->data), | |
562 | CAPIMSG_MSGID(skb->data)); | |
563 | ||
564 | capi_ctr_handle_message(ctrl, ApplId, skb); | |
565 | } | |
566 | break; | |
567 | ||
568 | case RECEIVE_NEW_NCCI: | |
569 | ||
570 | ApplId = _get_word(&p); | |
571 | NCCI = _get_word(&p); | |
572 | WindowSize = _get_word(&p); | |
475be4d8 | 573 | cidx = (NCCI & 0x7f) - card->cardnr; |
1da177e4 LT |
574 | if (cidx >= card->nlogcontr) cidx = 0; |
575 | ||
576 | capilib_new_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI, WindowSize); | |
577 | ||
578 | break; | |
579 | ||
580 | case RECEIVE_FREE_NCCI: | |
581 | ||
582 | ApplId = _get_word(&p); | |
583 | NCCI = _get_word(&p); | |
584 | ||
585 | if (NCCI != 0xffffffff) { | |
475be4d8 | 586 | cidx = (NCCI & 0x7f) - card->cardnr; |
1da177e4 LT |
587 | if (cidx >= card->nlogcontr) cidx = 0; |
588 | capilib_free_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI); | |
589 | } | |
590 | break; | |
591 | ||
592 | case RECEIVE_START: | |
26fb5c58 | 593 | #ifdef AVM_C4_POLLDEBUG |
1da177e4 LT |
594 | printk(KERN_INFO "%s: poll from c4\n", card->name); |
595 | #endif | |
596 | if (!suppress_pollack) | |
597 | queue_pollack(card); | |
475be4d8 | 598 | for (cidx = 0; cidx < card->nr_controllers; cidx++) { |
1da177e4 LT |
599 | ctrl = &card->ctrlinfo[cidx].capi_ctrl; |
600 | capi_ctr_resume_output(ctrl); | |
601 | } | |
602 | break; | |
603 | ||
604 | case RECEIVE_STOP: | |
475be4d8 | 605 | for (cidx = 0; cidx < card->nr_controllers; cidx++) { |
1da177e4 LT |
606 | ctrl = &card->ctrlinfo[cidx].capi_ctrl; |
607 | capi_ctr_suspend_output(ctrl); | |
608 | } | |
609 | break; | |
610 | ||
611 | case RECEIVE_INIT: | |
612 | ||
475be4d8 | 613 | cidx = card->nlogcontr; |
1da177e4 LT |
614 | if (cidx >= card->nr_controllers) { |
615 | printk(KERN_ERR "%s: card with %d controllers ??\n", | |
475be4d8 | 616 | card->name, cidx + 1); |
1da177e4 LT |
617 | break; |
618 | } | |
475be4d8 JP |
619 | card->nlogcontr++; |
620 | cinfo = &card->ctrlinfo[cidx]; | |
1da177e4 LT |
621 | ctrl = &cinfo->capi_ctrl; |
622 | cinfo->versionlen = _get_slice(&p, cinfo->versionbuf); | |
623 | b1_parse_version(cinfo); | |
624 | printk(KERN_INFO "%s: %s-card (%s) now active\n", | |
625 | card->name, | |
626 | cinfo->version[VER_CARDTYPE], | |
627 | cinfo->version[VER_DRIVER]); | |
628 | capi_ctr_ready(&cinfo->capi_ctrl); | |
629 | break; | |
630 | ||
631 | case RECEIVE_TASK_READY: | |
632 | ApplId = (unsigned) _get_word(&p); | |
633 | MsgLen = _get_slice(&p, card->msgbuf); | |
634 | card->msgbuf[MsgLen] = 0; | |
475be4d8 JP |
635 | while (MsgLen > 0 |
636 | && (card->msgbuf[MsgLen - 1] == '\n' | |
637 | || card->msgbuf[MsgLen - 1] == '\r')) { | |
638 | card->msgbuf[MsgLen - 1] = 0; | |
1da177e4 LT |
639 | MsgLen--; |
640 | } | |
641 | printk(KERN_INFO "%s: task %d \"%s\" ready.\n", | |
475be4d8 | 642 | card->name, ApplId, card->msgbuf); |
1da177e4 LT |
643 | break; |
644 | ||
645 | case RECEIVE_DEBUGMSG: | |
646 | MsgLen = _get_slice(&p, card->msgbuf); | |
647 | card->msgbuf[MsgLen] = 0; | |
475be4d8 JP |
648 | while (MsgLen > 0 |
649 | && (card->msgbuf[MsgLen - 1] == '\n' | |
650 | || card->msgbuf[MsgLen - 1] == '\r')) { | |
651 | card->msgbuf[MsgLen - 1] = 0; | |
1da177e4 LT |
652 | MsgLen--; |
653 | } | |
654 | printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); | |
655 | break; | |
656 | ||
657 | default: | |
658 | printk(KERN_ERR "%s: c4_interrupt: 0x%x ???\n", | |
475be4d8 | 659 | card->name, b1cmd); |
1da177e4 LT |
660 | return; |
661 | } | |
662 | } | |
663 | ||
664 | /* ------------------------------------------------------------- */ | |
665 | ||
666 | static irqreturn_t c4_handle_interrupt(avmcard *card) | |
667 | { | |
668 | unsigned long flags; | |
669 | u32 status; | |
670 | ||
671 | spin_lock_irqsave(&card->lock, flags); | |
475be4d8 | 672 | status = c4inmeml(card->mbase + DOORBELL); |
1da177e4 LT |
673 | |
674 | if (status & DBELL_RESET_HOST) { | |
675 | u_int i; | |
475be4d8 | 676 | c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x0c); |
1da177e4 LT |
677 | spin_unlock_irqrestore(&card->lock, flags); |
678 | if (card->nlogcontr == 0) | |
679 | return IRQ_HANDLED; | |
680 | printk(KERN_ERR "%s: unexpected reset\n", card->name); | |
475be4d8 | 681 | for (i = 0; i < card->nr_controllers; i++) { |
1da177e4 LT |
682 | avmctrl_info *cinfo = &card->ctrlinfo[i]; |
683 | memset(cinfo->version, 0, sizeof(cinfo->version)); | |
eac141de | 684 | spin_lock_irqsave(&card->lock, flags); |
1da177e4 | 685 | capilib_release(&cinfo->ncci_head); |
eac141de | 686 | spin_unlock_irqrestore(&card->lock, flags); |
4e329972 | 687 | capi_ctr_down(&cinfo->capi_ctrl); |
1da177e4 LT |
688 | } |
689 | card->nlogcontr = 0; | |
690 | return IRQ_HANDLED; | |
691 | } | |
692 | ||
693 | status &= (DBELL_UP_HOST | DBELL_DOWN_HOST); | |
694 | if (!status) { | |
695 | spin_unlock_irqrestore(&card->lock, flags); | |
696 | return IRQ_HANDLED; | |
697 | } | |
475be4d8 | 698 | c4outmeml(card->mbase + DOORBELL, status); |
1da177e4 LT |
699 | |
700 | if ((status & DBELL_UP_HOST) != 0) { | |
475be4d8 JP |
701 | card->dma->recvlen = c4inmeml(card->mbase + MBOX_UP_LEN); |
702 | c4outmeml(card->mbase + MBOX_UP_LEN, 0); | |
1da177e4 LT |
703 | c4_handle_rx(card); |
704 | card->dma->recvlen = 0; | |
475be4d8 JP |
705 | c4outmeml(card->mbase + MBOX_UP_LEN, card->dma->recvbuf.size); |
706 | c4outmeml(card->mbase + DOORBELL, DBELL_UP_ARM); | |
1da177e4 LT |
707 | } |
708 | ||
709 | if ((status & DBELL_DOWN_HOST) != 0) { | |
710 | card->csr &= ~DBELL_DOWN_ARM; | |
475be4d8 | 711 | c4_dispatch_tx(card); |
1da177e4 | 712 | } else if (card->csr & DBELL_DOWN_HOST) { |
475be4d8 JP |
713 | if (c4inmeml(card->mbase + MBOX_DOWN_LEN) == 0) { |
714 | card->csr &= ~DBELL_DOWN_ARM; | |
1da177e4 LT |
715 | c4_dispatch_tx(card); |
716 | } | |
717 | } | |
718 | spin_unlock_irqrestore(&card->lock, flags); | |
719 | return IRQ_HANDLED; | |
720 | } | |
721 | ||
7d12e780 | 722 | static irqreturn_t c4_interrupt(int interrupt, void *devptr) |
1da177e4 LT |
723 | { |
724 | avmcard *card = devptr; | |
725 | ||
726 | return c4_handle_interrupt(card); | |
727 | } | |
728 | ||
729 | /* ------------------------------------------------------------- */ | |
730 | ||
731 | static void c4_send_init(avmcard *card) | |
732 | { | |
733 | struct sk_buff *skb; | |
734 | void *p; | |
1ccfd633 | 735 | unsigned long flags; |
1da177e4 LT |
736 | |
737 | skb = alloc_skb(15, GFP_ATOMIC); | |
738 | if (!skb) { | |
739 | printk(KERN_CRIT "%s: no memory, lost register appl.\n", | |
475be4d8 | 740 | card->name); |
1da177e4 LT |
741 | return; |
742 | } | |
743 | p = skb->data; | |
744 | _put_byte(&p, 0); | |
745 | _put_byte(&p, 0); | |
746 | _put_byte(&p, SEND_INIT); | |
747 | _put_word(&p, CAPI_MAXAPPL); | |
475be4d8 | 748 | _put_word(&p, AVM_NCCI_PER_CHANNEL * 30); |
1da177e4 LT |
749 | _put_word(&p, card->cardnr - 1); |
750 | skb_put(skb, (u8 *)p - (u8 *)skb->data); | |
751 | ||
752 | skb_queue_tail(&card->dma->send_queue, skb); | |
1ccfd633 | 753 | spin_lock_irqsave(&card->lock, flags); |
1da177e4 | 754 | c4_dispatch_tx(card); |
1ccfd633 | 755 | spin_unlock_irqrestore(&card->lock, flags); |
1da177e4 LT |
756 | } |
757 | ||
758 | static int queue_sendconfigword(avmcard *card, u32 val) | |
759 | { | |
760 | struct sk_buff *skb; | |
1ccfd633 | 761 | unsigned long flags; |
1da177e4 LT |
762 | void *p; |
763 | ||
475be4d8 | 764 | skb = alloc_skb(3 + 4, GFP_ATOMIC); |
1da177e4 LT |
765 | if (!skb) { |
766 | printk(KERN_CRIT "%s: no memory, send config\n", | |
475be4d8 | 767 | card->name); |
1da177e4 LT |
768 | return -ENOMEM; |
769 | } | |
770 | p = skb->data; | |
771 | _put_byte(&p, 0); | |
772 | _put_byte(&p, 0); | |
773 | _put_byte(&p, SEND_CONFIG); | |
774 | _put_word(&p, val); | |
775 | skb_put(skb, (u8 *)p - (u8 *)skb->data); | |
776 | ||
777 | skb_queue_tail(&card->dma->send_queue, skb); | |
1ccfd633 | 778 | spin_lock_irqsave(&card->lock, flags); |
1da177e4 | 779 | c4_dispatch_tx(card); |
1ccfd633 | 780 | spin_unlock_irqrestore(&card->lock, flags); |
1da177e4 LT |
781 | return 0; |
782 | } | |
783 | ||
784 | static int queue_sendconfig(avmcard *card, char cval[4]) | |
785 | { | |
786 | struct sk_buff *skb; | |
787 | unsigned long flags; | |
788 | void *p; | |
789 | ||
475be4d8 | 790 | skb = alloc_skb(3 + 4, GFP_ATOMIC); |
1da177e4 LT |
791 | if (!skb) { |
792 | printk(KERN_CRIT "%s: no memory, send config\n", | |
475be4d8 | 793 | card->name); |
1da177e4 LT |
794 | return -ENOMEM; |
795 | } | |
796 | p = skb->data; | |
797 | _put_byte(&p, 0); | |
798 | _put_byte(&p, 0); | |
799 | _put_byte(&p, SEND_CONFIG); | |
800 | _put_byte(&p, cval[0]); | |
801 | _put_byte(&p, cval[1]); | |
802 | _put_byte(&p, cval[2]); | |
803 | _put_byte(&p, cval[3]); | |
804 | skb_put(skb, (u8 *)p - (u8 *)skb->data); | |
805 | ||
806 | skb_queue_tail(&card->dma->send_queue, skb); | |
475be4d8 | 807 | |
1da177e4 LT |
808 | spin_lock_irqsave(&card->lock, flags); |
809 | c4_dispatch_tx(card); | |
810 | spin_unlock_irqrestore(&card->lock, flags); | |
811 | return 0; | |
812 | } | |
813 | ||
475be4d8 | 814 | static int c4_send_config(avmcard *card, capiloaddatapart *config) |
1da177e4 LT |
815 | { |
816 | u8 val[4]; | |
817 | unsigned char *dp; | |
818 | u_int left; | |
819 | int retval; | |
475be4d8 | 820 | |
1da177e4 LT |
821 | if ((retval = queue_sendconfigword(card, 1)) != 0) |
822 | return retval; | |
823 | if ((retval = queue_sendconfigword(card, config->len)) != 0) | |
824 | return retval; | |
825 | ||
826 | dp = config->data; | |
827 | left = config->len; | |
828 | while (left >= sizeof(u32)) { | |
475be4d8 | 829 | if (config->user) { |
1da177e4 LT |
830 | if (copy_from_user(val, dp, sizeof(val))) |
831 | return -EFAULT; | |
832 | } else { | |
833 | memcpy(val, dp, sizeof(val)); | |
834 | } | |
835 | if ((retval = queue_sendconfig(card, val)) != 0) | |
836 | return retval; | |
837 | left -= sizeof(val); | |
838 | dp += sizeof(val); | |
839 | } | |
840 | if (left) { | |
841 | memset(val, 0, sizeof(val)); | |
842 | if (config->user) { | |
843 | if (copy_from_user(&val, dp, left)) | |
844 | return -EFAULT; | |
845 | } else { | |
846 | memcpy(&val, dp, left); | |
847 | } | |
848 | if ((retval = queue_sendconfig(card, val)) != 0) | |
849 | return retval; | |
850 | } | |
851 | ||
852 | return 0; | |
853 | } | |
854 | ||
855 | static int c4_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) | |
856 | { | |
857 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
858 | avmcard *card = cinfo->card; | |
859 | int retval; | |
860 | ||
861 | if ((retval = c4_load_t4file(card, &data->firmware))) { | |
862 | printk(KERN_ERR "%s: failed to load t4file!!\n", | |
475be4d8 | 863 | card->name); |
1da177e4 LT |
864 | c4_reset(card); |
865 | return retval; | |
866 | } | |
867 | ||
868 | card->csr = 0; | |
475be4d8 JP |
869 | c4outmeml(card->mbase + MBOX_UP_LEN, 0); |
870 | c4outmeml(card->mbase + MBOX_DOWN_LEN, 0); | |
871 | c4outmeml(card->mbase + DOORBELL, DBELL_INIT); | |
1da177e4 | 872 | mdelay(1); |
475be4d8 JP |
873 | c4outmeml(card->mbase + DOORBELL, |
874 | DBELL_UP_HOST | DBELL_DOWN_HOST | DBELL_RESET_HOST); | |
1da177e4 | 875 | |
475be4d8 | 876 | c4outmeml(card->mbase + PCI_OUT_INT_MASK, 0x08); |
1da177e4 LT |
877 | |
878 | card->dma->recvlen = 0; | |
475be4d8 JP |
879 | c4outmeml(card->mbase + MBOX_UP_ADDR, card->dma->recvbuf.dmaaddr); |
880 | c4outmeml(card->mbase + MBOX_UP_LEN, card->dma->recvbuf.size); | |
881 | c4outmeml(card->mbase + DOORBELL, DBELL_UP_ARM); | |
1da177e4 LT |
882 | |
883 | if (data->configuration.len > 0 && data->configuration.data) { | |
884 | retval = c4_send_config(card, &data->configuration); | |
885 | if (retval) { | |
886 | printk(KERN_ERR "%s: failed to set config!!\n", | |
475be4d8 | 887 | card->name); |
1da177e4 LT |
888 | c4_reset(card); |
889 | return retval; | |
890 | } | |
891 | } | |
892 | ||
475be4d8 | 893 | c4_send_init(card); |
1da177e4 LT |
894 | |
895 | return 0; | |
896 | } | |
897 | ||
898 | ||
08e51533 | 899 | static void c4_reset_ctr(struct capi_ctr *ctrl) |
1da177e4 LT |
900 | { |
901 | avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card; | |
902 | avmctrl_info *cinfo; | |
903 | u_int i; | |
904 | unsigned long flags; | |
905 | ||
906 | spin_lock_irqsave(&card->lock, flags); | |
907 | ||
475be4d8 | 908 | c4_reset(card); |
1da177e4 LT |
909 | |
910 | spin_unlock_irqrestore(&card->lock, flags); | |
911 | ||
475be4d8 | 912 | for (i = 0; i < card->nr_controllers; i++) { |
1da177e4 LT |
913 | cinfo = &card->ctrlinfo[i]; |
914 | memset(cinfo->version, 0, sizeof(cinfo->version)); | |
4e329972 | 915 | capi_ctr_down(&cinfo->capi_ctrl); |
1da177e4 LT |
916 | } |
917 | card->nlogcontr = 0; | |
918 | } | |
919 | ||
920 | static void c4_remove(struct pci_dev *pdev) | |
921 | { | |
922 | avmcard *card = pci_get_drvdata(pdev); | |
923 | avmctrl_info *cinfo; | |
924 | u_int i; | |
925 | ||
926 | if (!card) | |
927 | return; | |
928 | ||
475be4d8 | 929 | c4_reset(card); |
1da177e4 | 930 | |
475be4d8 | 931 | for (i = 0; i < card->nr_controllers; i++) { |
1da177e4 LT |
932 | cinfo = &card->ctrlinfo[i]; |
933 | detach_capi_ctr(&cinfo->capi_ctrl); | |
934 | } | |
935 | ||
936 | free_irq(card->irq, card); | |
937 | iounmap(card->mbase); | |
938 | release_region(card->port, AVMB1_PORTLEN); | |
475be4d8 JP |
939 | avmcard_dma_free(card->dma); |
940 | pci_set_drvdata(pdev, NULL); | |
1da177e4 LT |
941 | b1_free_card(card); |
942 | } | |
943 | ||
944 | /* ------------------------------------------------------------- */ | |
945 | ||
946 | ||
08e51533 | 947 | static void c4_register_appl(struct capi_ctr *ctrl, |
475be4d8 JP |
948 | u16 appl, |
949 | capi_register_params *rp) | |
1da177e4 LT |
950 | { |
951 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
952 | avmcard *card = cinfo->card; | |
953 | struct sk_buff *skb; | |
954 | int want = rp->level3cnt; | |
955 | unsigned long flags; | |
956 | int nconn; | |
957 | void *p; | |
958 | ||
959 | if (ctrl->cnr == card->cardnr) { | |
960 | ||
961 | if (want > 0) nconn = want; | |
962 | else nconn = ctrl->profile.nbchannel * 4 * -want; | |
963 | if (nconn == 0) nconn = ctrl->profile.nbchannel * 4; | |
964 | ||
965 | skb = alloc_skb(23, GFP_ATOMIC); | |
966 | if (!skb) { | |
967 | printk(KERN_CRIT "%s: no memory, lost register appl.\n", | |
475be4d8 | 968 | card->name); |
1da177e4 LT |
969 | return; |
970 | } | |
971 | p = skb->data; | |
972 | _put_byte(&p, 0); | |
973 | _put_byte(&p, 0); | |
974 | _put_byte(&p, SEND_REGISTER); | |
975 | _put_word(&p, appl); | |
475be4d8 | 976 | _put_word(&p, 1024 * (nconn + 1)); |
1da177e4 LT |
977 | _put_word(&p, nconn); |
978 | _put_word(&p, rp->datablkcnt); | |
979 | _put_word(&p, rp->datablklen); | |
980 | skb_put(skb, (u8 *)p - (u8 *)skb->data); | |
981 | ||
982 | skb_queue_tail(&card->dma->send_queue, skb); | |
475be4d8 | 983 | |
1da177e4 LT |
984 | spin_lock_irqsave(&card->lock, flags); |
985 | c4_dispatch_tx(card); | |
986 | spin_unlock_irqrestore(&card->lock, flags); | |
987 | } | |
988 | } | |
989 | ||
990 | /* ------------------------------------------------------------- */ | |
991 | ||
08e51533 | 992 | static void c4_release_appl(struct capi_ctr *ctrl, u16 appl) |
1da177e4 LT |
993 | { |
994 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
995 | avmcard *card = cinfo->card; | |
996 | unsigned long flags; | |
997 | struct sk_buff *skb; | |
998 | void *p; | |
999 | ||
1ccfd633 | 1000 | spin_lock_irqsave(&card->lock, flags); |
1da177e4 | 1001 | capilib_release_appl(&cinfo->ncci_head, appl); |
1ccfd633 | 1002 | spin_unlock_irqrestore(&card->lock, flags); |
1da177e4 LT |
1003 | |
1004 | if (ctrl->cnr == card->cardnr) { | |
1005 | skb = alloc_skb(7, GFP_ATOMIC); | |
1006 | if (!skb) { | |
1007 | printk(KERN_CRIT "%s: no memory, lost release appl.\n", | |
475be4d8 | 1008 | card->name); |
1da177e4 LT |
1009 | return; |
1010 | } | |
1011 | p = skb->data; | |
1012 | _put_byte(&p, 0); | |
1013 | _put_byte(&p, 0); | |
1014 | _put_byte(&p, SEND_RELEASE); | |
1015 | _put_word(&p, appl); | |
1016 | ||
1017 | skb_put(skb, (u8 *)p - (u8 *)skb->data); | |
1018 | skb_queue_tail(&card->dma->send_queue, skb); | |
1019 | spin_lock_irqsave(&card->lock, flags); | |
1020 | c4_dispatch_tx(card); | |
1021 | spin_unlock_irqrestore(&card->lock, flags); | |
1022 | } | |
1023 | } | |
1024 | ||
1025 | /* ------------------------------------------------------------- */ | |
1026 | ||
1027 | ||
1028 | static u16 c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) | |
1029 | { | |
1030 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
1031 | avmcard *card = cinfo->card; | |
1032 | u16 retval = CAPI_NOERROR; | |
1033 | unsigned long flags; | |
1034 | ||
1ccfd633 KK |
1035 | spin_lock_irqsave(&card->lock, flags); |
1036 | if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { | |
1da177e4 LT |
1037 | retval = capilib_data_b3_req(&cinfo->ncci_head, |
1038 | CAPIMSG_APPID(skb->data), | |
1039 | CAPIMSG_NCCI(skb->data), | |
1040 | CAPIMSG_MSGID(skb->data)); | |
1041 | } | |
1042 | if (retval == CAPI_NOERROR) { | |
1043 | skb_queue_tail(&card->dma->send_queue, skb); | |
1da177e4 | 1044 | c4_dispatch_tx(card); |
1da177e4 | 1045 | } |
1ccfd633 | 1046 | spin_unlock_irqrestore(&card->lock, flags); |
1da177e4 LT |
1047 | return retval; |
1048 | } | |
1049 | ||
1050 | /* ------------------------------------------------------------- */ | |
1051 | ||
1052 | static char *c4_procinfo(struct capi_ctr *ctrl) | |
1053 | { | |
1054 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); | |
1055 | ||
1056 | if (!cinfo) | |
1057 | return ""; | |
1058 | sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx", | |
1059 | cinfo->cardname[0] ? cinfo->cardname : "-", | |
1060 | cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", | |
1061 | cinfo->card ? cinfo->card->port : 0x0, | |
1062 | cinfo->card ? cinfo->card->irq : 0, | |
1063 | cinfo->card ? cinfo->card->membase : 0 | |
1064 | ); | |
1065 | return cinfo->infobuf; | |
1066 | } | |
1067 | ||
9a58a80a | 1068 | static int c4_proc_show(struct seq_file *m, void *v) |
1da177e4 | 1069 | { |
9a58a80a | 1070 | struct capi_ctr *ctrl = m->private; |
1da177e4 LT |
1071 | avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); |
1072 | avmcard *card = cinfo->card; | |
1073 | u8 flag; | |
1da177e4 LT |
1074 | char *s; |
1075 | ||
9a58a80a AD |
1076 | seq_printf(m, "%-16s %s\n", "name", card->name); |
1077 | seq_printf(m, "%-16s 0x%x\n", "io", card->port); | |
1078 | seq_printf(m, "%-16s %d\n", "irq", card->irq); | |
1079 | seq_printf(m, "%-16s 0x%lx\n", "membase", card->membase); | |
1da177e4 LT |
1080 | switch (card->cardtype) { |
1081 | case avm_b1isa: s = "B1 ISA"; break; | |
1082 | case avm_b1pci: s = "B1 PCI"; break; | |
1083 | case avm_b1pcmcia: s = "B1 PCMCIA"; break; | |
1084 | case avm_m1: s = "M1"; break; | |
1085 | case avm_m2: s = "M2"; break; | |
1086 | case avm_t1isa: s = "T1 ISA (HEMA)"; break; | |
1087 | case avm_t1pci: s = "T1 PCI"; break; | |
1088 | case avm_c4: s = "C4"; break; | |
1089 | case avm_c2: s = "C2"; break; | |
1090 | default: s = "???"; break; | |
1091 | } | |
9a58a80a | 1092 | seq_printf(m, "%-16s %s\n", "type", s); |
8e44b29d | 1093 | if ((s = cinfo->version[VER_DRIVER]) != NULL) |
9a58a80a | 1094 | seq_printf(m, "%-16s %s\n", "ver_driver", s); |
8e44b29d | 1095 | if ((s = cinfo->version[VER_CARDTYPE]) != NULL) |
9a58a80a | 1096 | seq_printf(m, "%-16s %s\n", "ver_cardtype", s); |
8e44b29d | 1097 | if ((s = cinfo->version[VER_SERIAL]) != NULL) |
9a58a80a | 1098 | seq_printf(m, "%-16s %s\n", "ver_serial", s); |
1da177e4 LT |
1099 | |
1100 | if (card->cardtype != avm_m1) { | |
475be4d8 JP |
1101 | flag = ((u8 *)(ctrl->profile.manu))[3]; |
1102 | if (flag) | |
9a58a80a | 1103 | seq_printf(m, "%-16s%s%s%s%s%s%s%s\n", |
475be4d8 JP |
1104 | "protocol", |
1105 | (flag & 0x01) ? " DSS1" : "", | |
1106 | (flag & 0x02) ? " CT1" : "", | |
1107 | (flag & 0x04) ? " VN3" : "", | |
1108 | (flag & 0x08) ? " NI1" : "", | |
1109 | (flag & 0x10) ? " AUSTEL" : "", | |
1110 | (flag & 0x20) ? " ESS" : "", | |
1111 | (flag & 0x40) ? " 1TR6" : "" | |
1112 | ); | |
1da177e4 LT |
1113 | } |
1114 | if (card->cardtype != avm_m1) { | |
475be4d8 | 1115 | flag = ((u8 *)(ctrl->profile.manu))[5]; |
1da177e4 | 1116 | if (flag) |
9a58a80a | 1117 | seq_printf(m, "%-16s%s%s%s%s\n", |
475be4d8 JP |
1118 | "linetype", |
1119 | (flag & 0x01) ? " point to point" : "", | |
1120 | (flag & 0x02) ? " point to multipoint" : "", | |
1121 | (flag & 0x08) ? " leased line without D-channel" : "", | |
1122 | (flag & 0x04) ? " leased line with D-channel" : "" | |
1123 | ); | |
1da177e4 | 1124 | } |
9a58a80a AD |
1125 | seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname); |
1126 | ||
1127 | return 0; | |
1da177e4 LT |
1128 | } |
1129 | ||
9a58a80a AD |
1130 | static int c4_proc_open(struct inode *inode, struct file *file) |
1131 | { | |
d9dda78b | 1132 | return single_open(file, c4_proc_show, PDE_DATA(inode)); |
9a58a80a AD |
1133 | } |
1134 | ||
1135 | static const struct file_operations c4_proc_fops = { | |
1136 | .owner = THIS_MODULE, | |
1137 | .open = c4_proc_open, | |
1138 | .read = seq_read, | |
1139 | .llseek = seq_lseek, | |
1140 | .release = single_release, | |
1141 | }; | |
1142 | ||
1da177e4 LT |
1143 | /* ------------------------------------------------------------- */ |
1144 | ||
1145 | static int c4_add_card(struct capicardparams *p, struct pci_dev *dev, | |
1146 | int nr_controllers) | |
1147 | { | |
1148 | avmcard *card; | |
1149 | avmctrl_info *cinfo; | |
1150 | int retval; | |
1151 | int i; | |
1152 | ||
1153 | card = b1_alloc_card(nr_controllers); | |
1154 | if (!card) { | |
1155 | printk(KERN_WARNING "c4: no memory.\n"); | |
1156 | retval = -ENOMEM; | |
1157 | goto err; | |
1158 | } | |
475be4d8 | 1159 | card->dma = avmcard_dma_alloc("c4", dev, 2048 + 128, 2048 + 128); |
1da177e4 LT |
1160 | if (!card->dma) { |
1161 | printk(KERN_WARNING "c4: no memory.\n"); | |
1162 | retval = -ENOMEM; | |
1163 | goto err_free; | |
1164 | } | |
1165 | ||
1166 | sprintf(card->name, "c%d-%x", nr_controllers, p->port); | |
1167 | card->port = p->port; | |
1168 | card->irq = p->irq; | |
1169 | card->membase = p->membase; | |
1170 | card->cardtype = (nr_controllers == 4) ? avm_c4 : avm_c2; | |
1171 | ||
1172 | if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { | |
1173 | printk(KERN_WARNING "c4: ports 0x%03x-0x%03x in use.\n", | |
1174 | card->port, card->port + AVMB1_PORTLEN); | |
1175 | retval = -EBUSY; | |
1176 | goto err_free_dma; | |
1177 | } | |
1178 | ||
1179 | card->mbase = ioremap(card->membase, 128); | |
8e44b29d | 1180 | if (card->mbase == NULL) { |
1da177e4 LT |
1181 | printk(KERN_NOTICE "c4: can't remap memory at 0x%lx\n", |
1182 | card->membase); | |
1183 | retval = -EIO; | |
1184 | goto err_release_region; | |
1185 | } | |
1186 | ||
1187 | retval = c4_detect(card); | |
1188 | if (retval != 0) { | |
1189 | printk(KERN_NOTICE "c4: NO card at 0x%x error(%d)\n", | |
1190 | card->port, retval); | |
1191 | retval = -EIO; | |
1192 | goto err_unmap; | |
1193 | } | |
1194 | c4_reset(card); | |
1195 | ||
9ba02bec | 1196 | retval = request_irq(card->irq, c4_interrupt, IRQF_SHARED, card->name, card); |
1da177e4 | 1197 | if (retval) { |
475be4d8 | 1198 | printk(KERN_ERR "c4: unable to get IRQ %d.\n", card->irq); |
1da177e4 LT |
1199 | retval = -EBUSY; |
1200 | goto err_unmap; | |
1201 | } | |
1202 | ||
475be4d8 | 1203 | for (i = 0; i < nr_controllers; i++) { |
1da177e4 LT |
1204 | cinfo = &card->ctrlinfo[i]; |
1205 | cinfo->capi_ctrl.owner = THIS_MODULE; | |
1206 | cinfo->capi_ctrl.driver_name = "c4"; | |
1207 | cinfo->capi_ctrl.driverdata = cinfo; | |
1208 | cinfo->capi_ctrl.register_appl = c4_register_appl; | |
1209 | cinfo->capi_ctrl.release_appl = c4_release_appl; | |
1210 | cinfo->capi_ctrl.send_message = c4_send_message; | |
1211 | cinfo->capi_ctrl.load_firmware = c4_load_firmware; | |
1212 | cinfo->capi_ctrl.reset_ctr = c4_reset_ctr; | |
1213 | cinfo->capi_ctrl.procinfo = c4_procinfo; | |
9a58a80a | 1214 | cinfo->capi_ctrl.proc_fops = &c4_proc_fops; |
1da177e4 LT |
1215 | strcpy(cinfo->capi_ctrl.name, card->name); |
1216 | ||
1217 | retval = attach_capi_ctr(&cinfo->capi_ctrl); | |
1218 | if (retval) { | |
1219 | printk(KERN_ERR "c4: attach controller failed (%d).\n", i); | |
1220 | for (i--; i >= 0; i--) { | |
1221 | cinfo = &card->ctrlinfo[i]; | |
1222 | detach_capi_ctr(&cinfo->capi_ctrl); | |
1223 | } | |
1224 | goto err_free_irq; | |
1225 | } | |
1226 | if (i == 0) | |
1227 | card->cardnr = cinfo->capi_ctrl.cnr; | |
1228 | } | |
1229 | ||
1230 | printk(KERN_INFO "c4: AVM C%d at i/o %#x, irq %d, mem %#lx\n", | |
1231 | nr_controllers, card->port, card->irq, | |
1232 | card->membase); | |
1233 | pci_set_drvdata(dev, card); | |
1234 | return 0; | |
1235 | ||
475be4d8 | 1236 | err_free_irq: |
1da177e4 | 1237 | free_irq(card->irq, card); |
475be4d8 | 1238 | err_unmap: |
1da177e4 | 1239 | iounmap(card->mbase); |
475be4d8 | 1240 | err_release_region: |
1da177e4 | 1241 | release_region(card->port, AVMB1_PORTLEN); |
475be4d8 | 1242 | err_free_dma: |
1da177e4 | 1243 | avmcard_dma_free(card->dma); |
475be4d8 | 1244 | err_free: |
1da177e4 | 1245 | b1_free_card(card); |
475be4d8 | 1246 | err: |
1da177e4 LT |
1247 | return retval; |
1248 | } | |
1249 | ||
1250 | /* ------------------------------------------------------------- */ | |
1251 | ||
ed5a84cd | 1252 | static int c4_probe(struct pci_dev *dev, const struct pci_device_id *ent) |
1da177e4 LT |
1253 | { |
1254 | int nr = ent->driver_data; | |
1255 | int retval = 0; | |
1256 | struct capicardparams param; | |
1257 | ||
1258 | if (pci_enable_device(dev) < 0) { | |
1259 | printk(KERN_ERR "c4: failed to enable AVM-C%d\n", nr); | |
1260 | return -ENODEV; | |
1261 | } | |
1262 | pci_set_master(dev); | |
1263 | ||
1264 | param.port = pci_resource_start(dev, 1); | |
1265 | param.irq = dev->irq; | |
1266 | param.membase = pci_resource_start(dev, 0); | |
475be4d8 | 1267 | |
1da177e4 LT |
1268 | printk(KERN_INFO "c4: PCI BIOS reports AVM-C%d at i/o %#x, irq %d, mem %#x\n", |
1269 | nr, param.port, param.irq, param.membase); | |
475be4d8 | 1270 | |
1da177e4 LT |
1271 | retval = c4_add_card(¶m, dev, nr); |
1272 | if (retval != 0) { | |
1273 | printk(KERN_ERR "c4: no AVM-C%d at i/o %#x, irq %d detected, mem %#x\n", | |
1274 | nr, param.port, param.irq, param.membase); | |
b69bcd9d | 1275 | pci_disable_device(dev); |
1da177e4 LT |
1276 | return -ENODEV; |
1277 | } | |
1278 | return 0; | |
1279 | } | |
1280 | ||
1281 | static struct pci_driver c4_pci_driver = { | |
475be4d8 JP |
1282 | .name = "c4", |
1283 | .id_table = c4_pci_tbl, | |
1284 | .probe = c4_probe, | |
1285 | .remove = c4_remove, | |
1da177e4 LT |
1286 | }; |
1287 | ||
1288 | static struct capi_driver capi_driver_c2 = { | |
1289 | .name = "c2", | |
1290 | .revision = "1.0", | |
1291 | }; | |
1292 | ||
1293 | static struct capi_driver capi_driver_c4 = { | |
1294 | .name = "c4", | |
1295 | .revision = "1.0", | |
1296 | }; | |
1297 | ||
1298 | static int __init c4_init(void) | |
1299 | { | |
1300 | char *p; | |
1301 | char rev[32]; | |
1302 | int err; | |
1303 | ||
8e44b29d | 1304 | if ((p = strchr(revision, ':')) != NULL && p[1]) { |
1da177e4 | 1305 | strlcpy(rev, p + 2, 32); |
8e44b29d | 1306 | if ((p = strchr(rev, '$')) != NULL && p > rev) |
475be4d8 | 1307 | *(p - 1) = 0; |
1da177e4 LT |
1308 | } else |
1309 | strcpy(rev, "1.0"); | |
1310 | ||
1311 | err = pci_register_driver(&c4_pci_driver); | |
1312 | if (!err) { | |
1313 | strlcpy(capi_driver_c2.revision, rev, 32); | |
1314 | register_capi_driver(&capi_driver_c2); | |
1315 | strlcpy(capi_driver_c4.revision, rev, 32); | |
1316 | register_capi_driver(&capi_driver_c4); | |
1317 | printk(KERN_INFO "c4: revision %s\n", rev); | |
1318 | } | |
1319 | return err; | |
1320 | } | |
1321 | ||
1322 | static void __exit c4_exit(void) | |
1323 | { | |
1324 | unregister_capi_driver(&capi_driver_c2); | |
1325 | unregister_capi_driver(&capi_driver_c4); | |
1326 | pci_unregister_driver(&c4_pci_driver); | |
1327 | } | |
1328 | ||
1329 | module_init(c4_init); | |
1330 | module_exit(c4_exit); |