patch-2.4.6 linux/drivers/isdn/tpam/tpam_queues.c
Next file: linux/drivers/macintosh/mac_hid.c
Previous file: linux/drivers/isdn/tpam/tpam_nco.c
Back to the patch index
Back to the overall index
- Lines: 419
- Date:
Mon Jul 2 14:07:55 2001
- Orig file:
v2.4.5/linux/drivers/isdn/tpam/tpam_queues.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.5/linux/drivers/isdn/tpam/tpam_queues.c linux/drivers/isdn/tpam/tpam_queues.c
@@ -0,0 +1,418 @@
+/* $Id: tpam_queues.c,v 1.1.2.1 2001/06/05 19:45:37 kai Exp $
+ *
+ * Turbo PAM ISDN driver for Linux. (Kernel Driver)
+ *
+ * Copyright 2001 Stelian Pop <stelian.pop@fr.alcove.com>, Alcôve
+ *
+ * For all support questions please contact: <support@auvertech.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#include "tpam.h"
+
+/* Local function prototype */
+static int tpam_sendpacket(tpam_card *card, tpam_channel *channel);
+
+/*
+ * Queue a message to be send to the card when possible.
+ *
+ * card: the board
+ * skb: the sk_buff containing the message.
+ */
+void tpam_enqueue(tpam_card *card, struct sk_buff *skb) {
+
+ dprintk("TurboPAM(tpam_enqueue): card=%d\n", card->id);
+
+ /* queue the sk_buff on the board's send queue */
+ skb_queue_tail(&card->sendq, skb);
+
+ /* queue the board's send task struct for immediate treatment */
+ queue_task(&card->send_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+/*
+ * Queue a data message to be send to the card when possible.
+ *
+ * card: the board
+ * skb: the sk_buff containing the message and the data. This parameter
+ * can be NULL if we want just to trigger the send of queued
+ * messages.
+ */
+void tpam_enqueue_data(tpam_channel *channel, struct sk_buff *skb) {
+
+ dprintk("TurboPAM(tpam_enqueue_data): card=%d, channel=%d\n",
+ channel->card->id, channel->num);
+
+ /* if existant, queue the sk_buff on the channel's send queue */
+ if (skb)
+ skb_queue_tail(&channel->sendq, skb);
+
+ /* queue the channel's send task struct for immediate treatment */
+ queue_task(&channel->card->send_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+/*
+ * 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 */
+}
+
+/*
+ * Run the board's receive task queue, dispatching each message on the queue,
+ * to its treatment function.
+ *
+ * card: the board.
+ */
+void tpam_recv_tq(tpam_card *card) {
+ pci_mpb *p;
+ struct sk_buff *skb;
+
+ /* for each message on the receive queue... */
+ while ((skb = skb_dequeue(&card->recvq))) {
+
+ /* point to the pci_mpb block */
+ p = (pci_mpb *)(skb->data + sizeof(skb_header));
+
+ /* dispatch the message */
+ switch (p->messageID) {
+ case ID_ACreateNCOCnf:
+ tpam_recv_ACreateNCOCnf(card, skb);
+ break;
+ case ID_ADestroyNCOCnf:
+ tpam_recv_ADestroyNCOCnf(card, skb);
+ break;
+ case ID_CConnectCnf:
+ tpam_recv_CConnectCnf(card, skb);
+ break;
+ case ID_CConnectInd:
+ tpam_recv_CConnectInd(card, skb);
+ break;
+ case ID_CDisconnectInd:
+ tpam_recv_CDisconnectInd(card, skb);
+ break;
+ case ID_CDisconnectCnf:
+ tpam_recv_CDisconnectCnf(card, skb);
+ break;
+ case ID_U3DataInd:
+ tpam_recv_U3DataInd(card, skb);
+ break;
+ default:
+ dprintk("TurboPAM(tpam_recv_tq): "
+ "unknown messageID %d, card=%d\n",
+ p->messageID, card->id);
+ break;
+ }
+ /* free the sk_buff */
+ kfree_skb(skb);
+ }
+}
+
+/*
+ * Run the board's send task queue. If there is a message in the board's send
+ * queue, it gets sended. If not, it examines each channel (one at the time,
+ * using a round robin algorithm). For each channel, if there is a message
+ * in the channel's send queue, it gets sended. This function sends only one
+ * message, it does not consume all the queue.
+ */
+void tpam_send_tq(tpam_card *card) {
+ int i;
+
+ /* first, try to send a packet from the board's send queue */
+ if (tpam_sendpacket(card, NULL))
+ return;
+
+ /* then, try each channel, in a round-robin manner */
+ for (i=card->roundrobin; i<card->roundrobin+card->channels_used; i++) {
+ if (tpam_sendpacket(card,
+ &card->channels[i % card->channels_used])) {
+ card->roundrobin = (i + 1) % card->channels_used;
+ return;
+ }
+ }
+}
+
+/*
+ * 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;
+}
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)