vserver 2.0 rc7
[linux-2.6.git] / drivers / isdn / act2000 / act2000_isa.c
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
19 static 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  */
27 static int
28 act2000_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
53 int
54 act2000_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
65 static irqreturn_t
66 act2000_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
94 static void
95 act2000_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
126 static void
127 act2000_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  */
138 int
139 act2000_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
166 int
167 act2000_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  */
185 void
186 act2000_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
202 static int
203 act2000_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
219 static int
220 act2000_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
236 void
237 act2000_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
299 void
300 act2000_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  */
362 static int
363 act2000_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  */
400 int
401 act2000_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 }