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