/* * Initializes and registers a new TurboPAM card. * * dev: the PCI device * num: the board number * * Return: 0 if OK, <0 if error */ static int __devinit tpam_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) { tpam_card *card, *c; int i; /* allocate memory for the board structure */ if (!(card = (tpam_card *)kmalloc(sizeof(tpam_card), GFP_KERNEL))) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "kmalloc failed!\n"); return -ENOMEM; } memset((char *)card, 0, sizeof(tpam_card)); card->irq = dev->irq; card->lock = SPIN_LOCK_UNLOCKED; sprintf(card->interface.id, "%s%d", id, cards_num); /* request interrupt */ if (request_irq(card->irq, &tpam_irq, SA_INTERRUPT | SA_SHIRQ, card->interface.id, card)) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "could not request irq %d\n", card->irq); kfree(card); return -EIO; } /* remap board memory */ if (!(card->bar0 = (unsigned long) ioremap(pci_resource_start(dev, 0), 0x800000))) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "unable to remap bar0\n"); free_irq(card->irq, card); kfree(card); return -EIO; } /* reset the board */ readl(card->bar0 + TPAM_RESETPAM_REGISTER); /* initialisation magic :-( */ copy_to_pam_dword(card, (void *)0x01800008, 0x00000030); copy_to_pam_dword(card, (void *)0x01800010, 0x00000030); copy_to_pam_dword(card, (void *)0x01800014, 0x42240822); copy_to_pam_dword(card, (void *)0x01800018, 0x07114000); copy_to_pam_dword(card, (void *)0x0180001c, 0x00000400); copy_to_pam_dword(card, (void *)0x01840070, 0x00000010); /* fill the ISDN link layer structure */ card->interface.channels = TPAM_NBCHANNEL; card->interface.maxbufsize = TPAM_MAXBUFSIZE; card->interface.features = ISDN_FEATURE_P_EURO | ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_MODEM | ISDN_FEATURE_L3_TRANS; card->interface.hl_hdrlen = 0; card->interface.command = tpam_command; card->interface.writebuf_skb = tpam_writebuf_skb; card->interface.writecmd = NULL; card->interface.readstat = NULL; /* register wrt the ISDN link layer */ if (!register_isdn(&card->interface)) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "unable to register %s\n", card->interface.id); free_irq(card->irq, card); iounmap((void *)card->bar0); kfree(card); return -EIO; } card->id = card->interface.channels; /* initialize all channels */ for (i = 0; i < TPAM_NBCHANNEL; ++i) { card->channels[i].num = i; card->channels[i].card = card; card->channels[i].ncoid = TPAM_NCOID_INVALID; card->channels[i].hdlc = 0; card->channels[i].realhdlc = 0; card->channels[i].hdlcshift = 0; skb_queue_head_init(&card->channels[i].sendq); } /* initialize the rest of board structure */ card->channels_used = 0; card->channels_tested = 0; card->running = 0; card->busy = 0; card->roundrobin = 0; card->loopmode = 0; skb_queue_head_init(&card->sendq); skb_queue_head_init(&card->recvq); card->recv_tq.routine = (void *) (void *) tpam_recv_tq; card->recv_tq.data = card; card->send_tq.routine = (void *) (void *) tpam_send_tq; card->send_tq.data = card; /* add the board at the end of the list of boards */ card->next = NULL; if (cards) { c = cards; while (c->next) c = c->next; c->next = card; } else cards = card; ++cards_num; pci_set_drvdata(dev, card); return 0; }
/* * IRQ handler. * * If a message comes from the board we read it, construct a sk_buff containing * the message and we queue the sk_buff on the board's receive queue, and we * trigger the execution of the board's receive task queue. * * If a message ack comes from the board we can go on and send a new message, * so we trigger the execution of the board's send task queue. * * irq: the irq number * dev_id: the registered board to the irq * regs: not used. */ void tpam_irq(int irq, void *dev_id, struct pt_regs *regs) { tpam_card *card = (tpam_card *)dev_id; u32 ackupload, uploadptr; u32 waiting_too_long; u32 hpic; struct sk_buff *skb; pci_mpb mpb; skb_header *skbh; dprintk("TurboPAM(tpam_irq): IRQ received, card=%d\n", card->id); /* grab the board lock */ spin_lock(&card->lock); /* get the message type */ ackupload = copy_from_pam_dword(card, (void *)TPAM_ACKUPLOAD_REGISTER); /* acknowledge the interrupt */ copy_to_pam_dword(card, (void *)TPAM_INTERRUPTACK_REGISTER, 0); readl(card->bar0 + TPAM_HINTACK_REGISTER); if (!ackupload) { /* it is a new message from the board */ dprintk("TurboPAM(tpam_irq): message received, card=%d\n", card->id); /* get the upload pointer */ uploadptr = copy_from_pam_dword(card, (void *)TPAM_UPLOADPTR_REGISTER); /* get the beginning of the message (pci_mpb part) */ copy_from_pam(card, &mpb, (void *)uploadptr, sizeof(pci_mpb)); /* allocate the sk_buff */ if (!(skb = alloc_skb(sizeof(skb_header) + sizeof(pci_mpb) + mpb.actualBlockTLVSize + mpb.actualDataSize, GFP_ATOMIC))) { printk(KERN_ERR "TurboPAM(tpam_irq): " "alloc_skb failed\n"); spin_unlock(&card->lock); return; } /* build the skb_header */ skbh = (skb_header *)skb_put(skb, sizeof(skb_header)); skbh->size = sizeof(pci_mpb) + mpb.actualBlockTLVSize; skbh->data_size = mpb.actualDataSize; skbh->ack = 0; skbh->ack_size = 0; /* copy the pci_mpb into the sk_buff */ memcpy(skb_put(skb, sizeof(pci_mpb)), &mpb, sizeof(pci_mpb)); /* copy the TLV block into the sk_buff */ copy_from_pam(card, skb_put(skb, mpb.actualBlockTLVSize), (void *)uploadptr + sizeof(pci_mpb), mpb.actualBlockTLVSize); /* if existent, copy the data block into the sk_buff */ if (mpb.actualDataSize) copy_from_pam(card, skb_put(skb, mpb.actualDataSize), (void *)uploadptr + sizeof(pci_mpb) + 4096, mpb.actualDataSize); /* wait for the board to become ready */ waiting_too_long = 0; do { hpic = readl(card->bar0 + TPAM_HPIC_REGISTER); if (waiting_too_long++ > 0xfffffff) { spin_unlock(&card->lock); printk(KERN_ERR "TurboPAM(tpam_irq): " "waiting too long...\n"); return; } } while (hpic & 0x00000002); /* acknowledge the message */ copy_to_pam_dword(card, (void *)TPAM_ACKDOWNLOAD_REGISTER, 0xffffffff); readl(card->bar0 + TPAM_DSPINT_REGISTER); /* release the board lock */ spin_unlock(&card->lock); if (mpb.messageID == ID_U3ReadyToReceiveInd) { /* this message needs immediate treatment */ tpam_recv_U3ReadyToReceiveInd(card, skb); kfree_skb(skb); } else { /* put the message in the receive queue */ skb_queue_tail(&card->recvq, skb); queue_task(&card->recv_tq, &tq_immediate); mark_bh(IMMEDIATE_BH); } return; } else { /* it is a ack from the board */ dprintk("TurboPAM(tpam_irq): message acknowledged, card=%d\n", card->id); /* board is not busy anymore */ card->busy = 0; /* release the lock */ spin_unlock(&card->lock); /* schedule the send queue for execution */ queue_task(&card->send_tq, &tq_immediate); mark_bh(IMMEDIATE_BH); return; } /* not reached */ }
/* * Launch the board's firmware. This function must be called after the * firmware was loaded into the board's memory using TPAM_CMD_DSPLOAD * IOCTL commands. After launching the firmware, this function creates * the NCOs and waits for their creation. * * card: the board * * Return: 0 if OK, <0 on errors. */ static int tpam_command_ioctl_dsprun(tpam_card *card) { u32 signature = 0, timeout, i; isdn_ctrl ctrl; struct sk_buff *skb; dprintk("TurboPAM(tpam_command_ioctl_dsprun): card=%d\n", card->id); /* board must _not_ be running */ if (card->running) return -EBUSY; /* reset the board */ spin_lock_irq(&card->lock); copy_to_pam_dword(card, (void *)TPAM_MAGICNUMBER_REGISTER, 0xdeadface); readl(card->bar0 + TPAM_DSPINT_REGISTER); readl(card->bar0 + TPAM_HINTACK_REGISTER); spin_unlock_irq(&card->lock); /* wait for the board signature */ timeout = jiffies + SIGNATURE_TIMEOUT; while (time_before(jiffies, timeout)) { spin_lock_irq(&card->lock); signature = copy_from_pam_dword(card, (void *)TPAM_MAGICNUMBER_REGISTER); spin_unlock_irq(&card->lock); if (signature == TPAM_MAGICNUMBER) break; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(2); } /* signature not present -> board not started */ if (signature != TPAM_MAGICNUMBER) { printk(KERN_ERR "TurboPAM(tpam_command_ioctl_dsprun): " "card=%d, signature 0x%lx, expected 0x%lx\n", card->id, (unsigned long)signature, (unsigned long)TPAM_MAGICNUMBER); printk(KERN_ERR "TurboPAM(tpam_command_ioctl_dsprun): " "card=%d, firmware not started\n", card->id); return -EIO; } /* the firmware is started */ printk(KERN_INFO "TurboPAM: card=%d, firmware started\n", card->id); /* init the CRC routines */ init_CRC(); /* create all the NCOs */ for (i = 0; i < TPAM_NBCHANNEL; ++i) if ((skb = build_ACreateNCOReq(""))) tpam_enqueue(card, skb); /* wait for NCO creation confirmation */ timeout = jiffies + NCOCREATE_TIMEOUT; while (time_before(jiffies, timeout)) { if (card->channels_tested == TPAM_NBCHANNEL) break; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(2); } card->running = 1; if (card->channels_tested != TPAM_NBCHANNEL) printk(KERN_ERR "TurboPAM(tpam_command_ioctl_dsprun): " "card=%d, tried to init %d channels, " "got reply from only %d channels\n", card->id, TPAM_NBCHANNEL, card->channels_tested); /* if all the channels were not initialized, signal to the ISDN * link layer that fact that some channels are not usable */ if (card->channels_used != TPAM_NBCHANNEL) for (i = card->channels_used; i < TPAM_NBCHANNEL; ++i) { ctrl.driver = card->id; ctrl.command = ISDN_STAT_DISCH; ctrl.arg = i; ctrl.parm.num[0] = 0; (* card->interface.statcallb)(&ctrl); } printk(KERN_INFO "TurboPAM: card=%d, ready, %d channels available\n", card->id, card->channels_used); /* let's rock ! */ ctrl.driver = card->id; ctrl.command = ISDN_STAT_RUN; ctrl.arg = 0; tpam_statcallb(card, ctrl); return 0; }
/* * Try to send a packet from the board's send queue or from the channel's * send queue. * * card: the board. * channel: the channel (if NULL, the packet will be taken from the * board's send queue. If not, it will be taken from the * channel's send queue. * * Return: 0 if tpam_send_tq must try another card/channel combination * (meaning that no packet has been send), 1 if no more packets * can be send at that time (a packet has been send or the card is * still busy from a previous send). */ static int tpam_sendpacket(tpam_card *card, tpam_channel *channel) { struct sk_buff *skb; u32 hpic; u32 downloadptr; skb_header *skbh; u32 waiting_too_long; dprintk("TurboPAM(tpam_sendpacket), card=%d, channel=%d\n", card->id, channel ? channel->num : -1); if (channel) { /* dequeue a packet from the channel's send queue */ if (!(skb = skb_dequeue(&channel->sendq))) { dprintk("TurboPAM(tpam_sendpacket): " "card=%d, channel=%d, no packet\n", card->id, channel->num); return 0; } /* if the channel is not ready to receive, requeue the packet * and return 0 to give a chance to another channel */ if (!channel->readytoreceive) { dprintk("TurboPAM(tpam_sendpacket): " "card=%d, channel=%d, channel not ready\n", card->id, channel->num); skb_queue_head(&channel->sendq, skb); return 0; } /* grab the board lock */ spin_lock_irq(&card->lock); /* if the board is busy, requeue the packet and return 1 since * there is no need to try another channel */ if (card->busy) { dprintk("TurboPAM(tpam_sendpacket): " "card=%d, channel=%d, card busy\n", card->id, channel->num); skb_queue_head(&channel->sendq, skb); spin_unlock_irq(&card->lock); return 1; } } else { /* dequeue a packet from the board's send queue */ if (!(skb = skb_dequeue(&card->sendq))) { dprintk("TurboPAM(tpam_sendpacket): " "card=%d, no packet\n", card->id); return 0; } /* grab the board lock */ spin_lock_irq(&card->lock); /* if the board is busy, requeue the packet and return 1 since * there is no need to try another channel */ if (card->busy) { dprintk("TurboPAM(tpam_sendpacket): " "card=%d, card busy\n", card->id); skb_queue_head(&card->sendq, skb); spin_unlock_irq(&card->lock); return 1; } } /* wait for the board to become ready */ waiting_too_long = 0; do { hpic = readl(card->bar0 + TPAM_HPIC_REGISTER); if (waiting_too_long++ > 0xfffffff) { spin_unlock_irq(&card->lock); printk(KERN_ERR "TurboPAM(tpam_sendpacket): " "waiting too long...\n"); return 1; } } while (hpic & 0x00000002); skbh = (skb_header *)skb->data; dprintk("TurboPAM(tpam_sendpacket): " "card=%d, card ready, sending %d/%d bytes\n", card->id, skbh->size, skbh->data_size); /* get the board's download pointer */ downloadptr = copy_from_pam_dword(card, (void *)TPAM_DOWNLOADPTR_REGISTER); /* copy the packet to the board at the downloadptr location */ copy_to_pam(card, (void *)downloadptr, skb->data + sizeof(skb_header), skbh->size); if (skbh->data_size) /* if there is some data in the packet, copy it too */ copy_to_pam(card, (void *)downloadptr + sizeof(pci_mpb) + 4096, skb->data + sizeof(skb_header) + skbh->size, skbh->data_size); /* card will become busy right now */ card->busy = 1; /* interrupt the board */ copy_to_pam_dword(card, (void *)TPAM_ACKDOWNLOAD_REGISTER, 0); readl(card->bar0 + TPAM_DSPINT_REGISTER); /* release the lock */ spin_unlock_irq(&card->lock); /* if a data ack was requested by the ISDN link layer, send it now */ if (skbh->ack) { isdn_ctrl ctrl; ctrl.driver = card->id; ctrl.command = ISDN_STAT_BSENT; ctrl.arg = channel->num; ctrl.parm.length = skbh->ack_size; (* card->interface.statcallb)(&ctrl); } /* free the sk_buff */ kfree_skb(skb); return 1; }