--- zzzz-none-000/linux-2.4.17/drivers/isdn/tpam/tpam_queues.c 2001-12-21 17:41:54.000000000 +0000 +++ sangam-fb-322/linux-2.4.17/drivers/isdn/tpam/tpam_queues.c 2004-11-24 13:23:00.000000000 +0000 @@ -1,406 +1,406 @@ -/* $Id: tpam_queues.c,v 1.1.2.1 2001/11/20 14:19:37 kai Exp $ - * - * Turbo PAM ISDN driver for Linux. (Kernel Driver) - * - * Copyright 2001 Stelian Pop , Alcôve - * - * This software may be used and distributed according to the terms - * of the GNU General Public License, incorporated herein by reference. - * - * For all support questions please contact: - * - */ - -#include -#include -#include -#include -#include - -#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; iroundrobin+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; -} - +/* $Id: tpam_queues.c,v 1.1.1.1 2003/06/23 22:18:27 jharrell Exp $ + * + * Turbo PAM ISDN driver for Linux. (Kernel Driver) + * + * Copyright 2001 Stelian Pop , Alcôve + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * For all support questions please contact: + * + */ + +#include +#include +#include +#include +#include + +#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; iroundrobin+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; +} +