aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/isdn/act2000/act2000_isa.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/act2000/act2000_isa.c
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/isdn/act2000/act2000_isa.c')
-rw-r--r--drivers/isdn/act2000/act2000_isa.c449
1 files changed, 449 insertions, 0 deletions
diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c
new file mode 100644
index 00000000000..bc98d77c5ec
--- /dev/null
+++ b/drivers/isdn/act2000/act2000_isa.c
@@ -0,0 +1,449 @@
1/* $Id: act2000_isa.c,v 1.11.6.3 2001/09/23 22:24:32 kai Exp $
2 *
3 * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
4 *
5 * Author Fritz Elfert
6 * Copyright by Fritz Elfert <fritz@isdn4linux.de>
7 *
8 * This software may be used and distributed according to the terms
9 * of the GNU General Public License, incorporated herein by reference.
10 *
11 * Thanks to Friedemann Baitinger and IBM Germany
12 *
13 */
14
15#include "act2000.h"
16#include "act2000_isa.h"
17#include "capi.h"
18
19static act2000_card *irq2card_map[16];
20
21/*
22 * Reset Controller, then try to read the Card's signature.
23 + Return:
24 * 1 = Signature found.
25 * 0 = Signature not found.
26 */
27static int
28act2000_isa_reset(unsigned short portbase)
29{
30 unsigned char reg;
31 int i;
32 int found;
33 int serial = 0;
34
35 found = 0;
36 if ((reg = inb(portbase + ISA_COR)) != 0xff) {
37 outb(reg | ISA_COR_RESET, portbase + ISA_COR);
38 mdelay(10);
39 outb(reg, portbase + ISA_COR);
40 mdelay(10);
41
42 for (i = 0; i < 16; i++) {
43 if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL)
44 serial |= 0x10000;
45 serial >>= 1;
46 }
47 if (serial == ISA_SER_ID)
48 found++;
49 }
50 return found;
51}
52
53int
54act2000_isa_detect(unsigned short portbase)
55{
56 int ret = 0;
57
58 if (request_region(portbase, ACT2000_PORTLEN, "act2000isa")) {
59 ret = act2000_isa_reset(portbase);
60 release_region(portbase, ISA_REGION);
61 }
62 return ret;
63}
64
65static irqreturn_t
66act2000_isa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
67{
68 act2000_card *card = irq2card_map[irq];
69 u_char istatus;
70
71 if (!card) {
72 printk(KERN_WARNING
73 "act2000: Spurious interrupt!\n");
74 return IRQ_NONE;
75 }
76 istatus = (inb(ISA_PORT_ISR) & 0x07);
77 if (istatus & ISA_ISR_OUT) {
78 /* RX fifo has data */
79 istatus &= ISA_ISR_OUT_MASK;
80 outb(0, ISA_PORT_SIS);
81 act2000_isa_receive(card);
82 outb(ISA_SIS_INT, ISA_PORT_SIS);
83 }
84 if (istatus & ISA_ISR_ERR) {
85 /* Error Interrupt */
86 istatus &= ISA_ISR_ERR_MASK;
87 printk(KERN_WARNING "act2000: errIRQ\n");
88 }
89 if (istatus)
90 printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", irq, istatus);
91 return IRQ_HANDLED;
92}
93
94static void
95act2000_isa_select_irq(act2000_card * card)
96{
97 unsigned char reg;
98
99 reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR;
100 switch (card->irq) {
101 case 3:
102 reg = ISA_COR_IRQ03;
103 break;
104 case 5:
105 reg = ISA_COR_IRQ05;
106 break;
107 case 7:
108 reg = ISA_COR_IRQ07;
109 break;
110 case 10:
111 reg = ISA_COR_IRQ10;
112 break;
113 case 11:
114 reg = ISA_COR_IRQ11;
115 break;
116 case 12:
117 reg = ISA_COR_IRQ12;
118 break;
119 case 15:
120 reg = ISA_COR_IRQ15;
121 break;
122 }
123 outb(reg, ISA_PORT_COR);
124}
125
126static void
127act2000_isa_enable_irq(act2000_card * card)
128{
129 act2000_isa_select_irq(card);
130 /* Enable READ irq */
131 outb(ISA_SIS_INT, ISA_PORT_SIS);
132}
133
134/*
135 * Install interrupt handler, enable irq on card.
136 * If irq is -1, choose next free irq, else irq is given explicitely.
137 */
138int
139act2000_isa_config_irq(act2000_card * card, short irq)
140{
141 if (card->flags & ACT2000_FLAGS_IVALID) {
142 free_irq(card->irq, NULL);
143 irq2card_map[card->irq] = NULL;
144 }
145 card->flags &= ~ACT2000_FLAGS_IVALID;
146 outb(ISA_COR_IRQOFF, ISA_PORT_COR);
147 if (!irq)
148 return 0;
149
150 if (!request_irq(irq, &act2000_isa_interrupt, 0, card->regname, NULL)) {
151 card->irq = irq;
152 irq2card_map[card->irq] = card;
153 card->flags |= ACT2000_FLAGS_IVALID;
154 printk(KERN_WARNING
155 "act2000: Could not request irq %d\n",irq);
156 return -EBUSY;
157 } else {
158 act2000_isa_select_irq(card);
159 /* Disable READ and WRITE irq */
160 outb(0, ISA_PORT_SIS);
161 outb(0, ISA_PORT_SOS);
162 }
163 return 0;
164}
165
166int
167act2000_isa_config_port(act2000_card * card, unsigned short portbase)
168{
169 if (card->flags & ACT2000_FLAGS_PVALID) {
170 release_region(card->port, ISA_REGION);
171 card->flags &= ~ACT2000_FLAGS_PVALID;
172 }
173 if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL)
174 return -EBUSY;
175 else {
176 card->port = portbase;
177 card->flags |= ACT2000_FLAGS_PVALID;
178 return 0;
179 }
180}
181
182/*
183 * Release ressources, used by an adaptor.
184 */
185void
186act2000_isa_release(act2000_card * card)
187{
188 unsigned long flags;
189
190 spin_lock_irqsave(&card->lock, flags);
191 if (card->flags & ACT2000_FLAGS_IVALID) {
192 free_irq(card->irq, NULL);
193 irq2card_map[card->irq] = NULL;
194 }
195 card->flags &= ~ACT2000_FLAGS_IVALID;
196 if (card->flags & ACT2000_FLAGS_PVALID)
197 release_region(card->port, ISA_REGION);
198 card->flags &= ~ACT2000_FLAGS_PVALID;
199 spin_unlock_irqrestore(&card->lock, flags);
200}
201
202static int
203act2000_isa_writeb(act2000_card * card, u_char data)
204{
205 u_char timeout = 40;
206
207 while (timeout) {
208 if (inb(ISA_PORT_SOS) & ISA_SOS_READY) {
209 outb(data, ISA_PORT_SDO);
210 return 0;
211 } else {
212 timeout--;
213 udelay(10);
214 }
215 }
216 return 1;
217}
218
219static int
220act2000_isa_readb(act2000_card * card, u_char * data)
221{
222 u_char timeout = 40;
223
224 while (timeout) {
225 if (inb(ISA_PORT_SIS) & ISA_SIS_READY) {
226 *data = inb(ISA_PORT_SDI);
227 return 0;
228 } else {
229 timeout--;
230 udelay(10);
231 }
232 }
233 return 1;
234}
235
236void
237act2000_isa_receive(act2000_card *card)
238{
239 u_char c;
240
241 if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0)
242 return;
243 while (!act2000_isa_readb(card, &c)) {
244 if (card->idat.isa.rcvidx < 8) {
245 card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c;
246 if (card->idat.isa.rcvidx == 8) {
247 int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr);
248
249 if (valid) {
250 card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len;
251 card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen);
252 if (card->idat.isa.rcvskb == NULL) {
253 card->idat.isa.rcvignore = 1;
254 printk(KERN_WARNING
255 "act2000_isa_receive: no memory\n");
256 test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
257 return;
258 }
259 memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8);
260 card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8);
261 } else {
262 card->idat.isa.rcvidx = 0;
263 printk(KERN_WARNING
264 "act2000_isa_receive: Invalid CAPI msg\n");
265 {
266 int i; __u8 *p; __u8 *c; __u8 tmp[30];
267 for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++)
268 c += sprintf(c, "%02x ", *(p++));
269 printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp);
270 }
271 }
272 }
273 } else {
274 if (!card->idat.isa.rcvignore)
275 *card->idat.isa.rcvptr++ = c;
276 if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) {
277 if (!card->idat.isa.rcvignore) {
278 skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb);
279 act2000_schedule_rx(card);
280 }
281 card->idat.isa.rcvidx = 0;
282 card->idat.isa.rcvlen = 8;
283 card->idat.isa.rcvignore = 0;
284 card->idat.isa.rcvskb = NULL;
285 card->idat.isa.rcvptr = card->idat.isa.rcvhdr;
286 }
287 }
288 }
289 if (!(card->flags & ACT2000_FLAGS_IVALID)) {
290 /* In polling mode, schedule myself */
291 if ((card->idat.isa.rcvidx) &&
292 (card->idat.isa.rcvignore ||
293 (card->idat.isa.rcvidx < card->idat.isa.rcvlen)))
294 act2000_schedule_poll(card);
295 }
296 test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
297}
298
299void
300act2000_isa_send(act2000_card * card)
301{
302 unsigned long flags;
303 struct sk_buff *skb;
304 actcapi_msg *msg;
305 int l;
306
307 if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0)
308 return;
309 while (1) {
310 spin_lock_irqsave(&card->lock, flags);
311 if (!(card->sbuf)) {
312 if ((card->sbuf = skb_dequeue(&card->sndq))) {
313 card->ack_msg = card->sbuf->data;
314 msg = (actcapi_msg *)card->sbuf->data;
315 if ((msg->hdr.cmd.cmd == 0x86) &&
316 (msg->hdr.cmd.subcmd == 0) ) {
317 /* Save flags in message */
318 card->need_b3ack = msg->msg.data_b3_req.flags;
319 msg->msg.data_b3_req.flags = 0;
320 }
321 }
322 }
323 spin_unlock_irqrestore(&card->lock, flags);
324 if (!(card->sbuf)) {
325 /* No more data to send */
326 test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
327 return;
328 }
329 skb = card->sbuf;
330 l = 0;
331 while (skb->len) {
332 if (act2000_isa_writeb(card, *(skb->data))) {
333 /* Fifo is full, but more data to send */
334 test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
335 /* Schedule myself */
336 act2000_schedule_tx(card);
337 return;
338 }
339 skb_pull(skb, 1);
340 l++;
341 }
342 msg = (actcapi_msg *)card->ack_msg;
343 if ((msg->hdr.cmd.cmd == 0x86) &&
344 (msg->hdr.cmd.subcmd == 0) ) {
345 /*
346 * If it's user data, reset data-ptr
347 * and put skb into ackq.
348 */
349 skb->data = card->ack_msg;
350 /* Restore flags in message */
351 msg->msg.data_b3_req.flags = card->need_b3ack;
352 skb_queue_tail(&card->ackq, skb);
353 } else
354 dev_kfree_skb(skb);
355 card->sbuf = NULL;
356 }
357}
358
359/*
360 * Get firmware ID, check for 'ISDN' signature.
361 */
362static int
363act2000_isa_getid(act2000_card * card)
364{
365
366 act2000_fwid fid;
367 u_char *p = (u_char *) & fid;
368 int count = 0;
369
370 while (1) {
371 if (count > 510)
372 return -EPROTO;
373 if (act2000_isa_readb(card, p++))
374 break;
375 count++;
376 }
377 if (count <= 20) {
378 printk(KERN_WARNING "act2000: No Firmware-ID!\n");
379 return -ETIME;
380 }
381 *p = '\0';
382 fid.revlen[0] = '\0';
383 if (strcmp(fid.isdn, "ISDN")) {
384 printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n");
385 return -EPROTO;
386 }
387 if ((p = strchr(fid.revision, '\n')))
388 *p = '\0';
389 printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision);
390 if (card->flags & ACT2000_FLAGS_IVALID) {
391 printk(KERN_DEBUG "Enabling Interrupts ...\n");
392 act2000_isa_enable_irq(card);
393 }
394 return 0;
395}
396
397/*
398 * Download microcode into card, check Firmware signature.
399 */
400int
401act2000_isa_download(act2000_card * card, act2000_ddef __user * cb)
402{
403 unsigned int length;
404 int l;
405 int c;
406 long timeout;
407 u_char *b;
408 u_char __user *p;
409 u_char *buf;
410 act2000_ddef cblock;
411
412 if (!act2000_isa_reset(card->port))
413 return -ENXIO;
414 msleep_interruptible(500);
415 if (copy_from_user(&cblock, cb, sizeof(cblock)))
416 return -EFAULT;
417 length = cblock.length;
418 p = cblock.buffer;
419 if (!access_ok(VERIFY_READ, p, length))
420 return -EFAULT;
421 buf = (u_char *) kmalloc(1024, GFP_KERNEL);
422 if (!buf)
423 return -ENOMEM;
424 timeout = 0;
425 while (length) {
426 l = (length > 1024) ? 1024 : length;
427 c = 0;
428 b = buf;
429 if (copy_from_user(buf, p, l)) {
430 kfree(buf);
431 return -EFAULT;
432 }
433 while (c < l) {
434 if (act2000_isa_writeb(card, *b++)) {
435 printk(KERN_WARNING
436 "act2000: loader timed out"
437 " len=%d c=%d\n", length, c);
438 kfree(buf);
439 return -ETIME;
440 }
441 c++;
442 }
443 length -= l;
444 p += l;
445 }
446 kfree(buf);
447 msleep_interruptible(500);
448 return (act2000_isa_getid(card));
449}