patch-2.2.3 linux/drivers/net/cosa.c
Next file: linux/drivers/net/cosa.h
Previous file: linux/drivers/net/ariadne.c
Back to the patch index
Back to the overall index
- Lines: 421
- Date:
Sun Mar 7 15:47:46 1999
- Orig file:
v2.2.2/linux/drivers/net/cosa.c
- Orig date:
Mon Dec 28 15:00:52 1998
diff -u --recursive --new-file v2.2.2/linux/drivers/net/cosa.c linux/drivers/net/cosa.c
@@ -1,4 +1,4 @@
-/* $Id: cosa.c,v 1.11 1998/12/24 23:44:23 kas Exp $ */
+/* $Id: cosa.c,v 1.21 1999/02/06 19:49:18 kas Exp $ */
/*
* Copyright (C) 1995-1997 Jan "Yenya" Kasprzak <kas@fi.muni.cz>
@@ -27,7 +27,7 @@
* Masaryk University (http://www.ics.muni.cz/). The hardware is
* developed by Jiri Novotny <novotny@ics.muni.cz>. More information
* and the photo of both cards is available at
- * http://www.kozakmartin.cz/cosa.html. The card documentation, firmwares
+ * http://www.pavoucek.cz/cosa.html. The card documentation, firmwares
* and other goods can be downloaded from ftp://ftp.ics.muni.cz/pub/cosa/.
* For Linux-specific utilities, see below in the "Software info" section.
* If you want to order the card, contact Jiri Novotny.
@@ -141,11 +141,13 @@
unsigned int datareg, statusreg; /* I/O ports */
unsigned short irq, dma; /* IRQ and DMA number */
unsigned short startaddr; /* Firmware start address */
+ unsigned short busmaster; /* Use busmastering? */
int nchannels; /* # of channels on this card */
int driver_status; /* For communicating with firware */
int firmware_status; /* Downloaded, reseted, etc. */
int rxbitmap, txbitmap; /* Bitmap of channels who are willing to send/receive data */
int rxtx; /* RX or TX in progress? */
+ int enabled;
int usage; /* usage count */
int txchan, txsize, rxsize;
struct channel_data *rxchan;
@@ -331,9 +333,8 @@
static int get_wait_data(struct cosa_data *cosa);
static int put_wait_data(struct cosa_data *cosa, int data);
static int puthexnumber(struct cosa_data *cosa, int number);
-static void put_driver_status_common(struct cosa_data *cosa, int nolock);
-#define put_driver_status(x) put_driver_status_common((x), 0)
-#define put_driver_status_nolock(x) put_driver_status_common((x), 1)
+static void put_driver_status(struct cosa_data *cosa);
+static void put_driver_status_nolock(struct cosa_data *cosa);
/* Interrupt handling */
static void cosa_interrupt(int irq, void *cosa, struct pt_regs *regs);
@@ -357,7 +358,7 @@
#endif
{
int i;
- printk(KERN_INFO "cosa v1.03 (c) 1997-8 Jan Kasprzak <kas@fi.muni.cz>\n");
+ printk(KERN_INFO "cosa v1.04 (c) 1997-8 Jan Kasprzak <kas@fi.muni.cz>\n");
#ifdef __SMP__
printk(KERN_INFO "cosa: SMP found. Please mail any success/failure reports to the author.\n");
#endif
@@ -630,9 +631,14 @@
if (dev->tbusy) {
if (time_before(jiffies, dev->trans_start+2*HZ))
return 1; /* Two seconds timeout */
+ if (test_bit(RXBIT, &chan->cosa->rxtx)) {
+ chan->stats.rx_errors++;
+ chan->stats.rx_missed_errors++;
+ } else {
+ chan->stats.tx_errors++;
+ chan->stats.tx_aborted_errors++;
+ }
cosa_kick(chan->cosa);
- chan->stats.tx_errors++;
- chan->stats.tx_aborted_errors++;
if (chan->tx_skb) {
dev_kfree_skb(chan->tx_skb);
chan->tx_skb = 0;
@@ -659,6 +665,14 @@
d->tbusy = 1;
cosa_disable_rx(chan);
spin_lock_irqsave(&chan->cosa->lock, flags);
+ if (chan->rx_skb) {
+ kfree_skb(chan->rx_skb);
+ chan->rx_skb = 0;
+ }
+ if (chan->tx_skb) {
+ kfree_skb(chan->tx_skb);
+ chan->tx_skb = 0;
+ }
chan->usage=0;
chan->cosa->usage--;
MOD_DEC_USE_COUNT;
@@ -1128,6 +1142,17 @@
return nr_cards;
case COSAIONRCHANS:
return cosa->nchannels;
+ case COSAIOBMSET:
+ if (!suser())
+ return -EACCES;
+ if (is_8bit(cosa))
+ return -EINVAL;
+ if (arg != COSA_BM_OFF && arg != COSA_BM_ON)
+ return -EINVAL;
+ cosa->busmaster = arg;
+ return 0;
+ case COSAIOBMGET:
+ return cosa->busmaster;
}
return -ENOIOCTLCMD;
}
@@ -1208,37 +1233,67 @@
return 0;
}
-static void put_driver_status_common(struct cosa_data *cosa, int nolock)
+static void put_driver_status(struct cosa_data *cosa)
{
unsigned flags=0;
int status;
- if (!nolock)
- spin_lock_irqsave(&cosa->lock, flags);
+ spin_lock_irqsave(&cosa->lock, flags);
status = (cosa->rxbitmap ? DRIVER_RX_READY : 0)
| (cosa->txbitmap ? DRIVER_TX_READY : 0)
| (cosa->txbitmap? ~(cosa->txbitmap<<DRIVER_TXMAP_SHIFT)
&DRIVER_TXMAP_MASK : 0);
- if (!cosa->rxtx || nolock) {
-#ifdef DEBUG_IO
- debug_data_cmd(cosa, status);
-#endif
- cosa_putdata8(cosa, status);
+ if (!cosa->rxtx) {
if (cosa->rxbitmap|cosa->txbitmap) {
- cosa_putstatus(cosa, SR_RX_INT_ENA);
+ if (!cosa->enabled) {
+ cosa_putstatus(cosa, SR_RX_INT_ENA);
#ifdef DEBUG_IO
- debug_status_out(cosa, SR_RX_INT_ENA);
+ debug_status_out(cosa, SR_RX_INT_ENA);
#endif
- } else {
+ cosa->enabled = 1;
+ }
+ } else if (cosa->enabled) {
+ cosa->enabled = 0;
cosa_putstatus(cosa, 0);
#ifdef DEBUG_IO
debug_status_out(cosa, 0);
#endif
}
+ cosa_putdata8(cosa, status);
+#ifdef DEBUG_IO
+ debug_data_cmd(cosa, status);
+#endif
}
- if (!nolock)
- spin_unlock_irqrestore(&cosa->lock, flags);
+ spin_unlock_irqrestore(&cosa->lock, flags);
+}
+
+static void put_driver_status_nolock(struct cosa_data *cosa)
+{
+ int status;
+
+ status = (cosa->rxbitmap ? DRIVER_RX_READY : 0)
+ | (cosa->txbitmap ? DRIVER_TX_READY : 0)
+ | (cosa->txbitmap? ~(cosa->txbitmap<<DRIVER_TXMAP_SHIFT)
+ &DRIVER_TXMAP_MASK : 0);
+
+ if (cosa->rxbitmap|cosa->txbitmap) {
+ cosa_putstatus(cosa, SR_RX_INT_ENA);
+#ifdef DEBUG_IO
+ debug_status_out(cosa, SR_RX_INT_ENA);
+#endif
+ cosa->enabled = 1;
+ } else {
+ cosa_putstatus(cosa, 0);
+#ifdef DEBUG_IO
+ debug_status_out(cosa, 0);
+#endif
+ cosa->enabled = 0;
+ }
+ cosa_putdata8(cosa, status);
+#ifdef DEBUG_IO
+ debug_data_cmd(cosa, status);
+#endif
}
/*
@@ -1249,9 +1304,14 @@
static void cosa_kick(struct cosa_data *cosa)
{
unsigned flags, flags1;
+ char *s = "Unknown";
- printk(KERN_INFO "%s: DMA timeout - restarting.\n", cosa->name);
+ if (test_bit(RXBIT, &cosa->rxtx))
+ s = "RX";
+ if (test_bit(TXBIT, &cosa->rxtx))
+ s = "TX";
+ printk(KERN_INFO "%s: %s DMA timeout - restarting.\n", cosa->name, s);
spin_lock_irqsave(&cosa->lock, flags);
cosa->rxtx = 0;
@@ -1261,6 +1321,13 @@
release_dma_lock(flags1);
/* FIXME: Anything else? */
+ udelay(100);
+ cosa_putstatus(cosa, 0);
+ udelay(100);
+ (void) cosa_getdata8(cosa);
+ udelay(100);
+ cosa_putdata8(cosa, 0);
+ udelay(100);
put_driver_status_nolock(cosa);
spin_unlock_irqrestore(&cosa->lock, flags);
}
@@ -1556,6 +1623,10 @@
* COSA status byte. I have moved the rx/tx/eot interrupt handling into
* separate functions to make it more readable. These functions are inline,
* so there should be no overhead of function call.
+ *
+ * In the COSA bus-master mode, we need to tell the card the address of a
+ * buffer. Unfortunately, COSA may be too slow for us, so we must busy-wait.
+ * It's time to use the bottom half :-(
*/
/*
@@ -1606,13 +1677,13 @@
if (is_8bit(cosa)) {
if (!test_bit(IRQBIT, &cosa->rxtx)) {
+ cosa_putstatus(cosa, SR_TX_INT_ENA);
cosa_putdata8(cosa, ((cosa->txchan << 5) & 0xe0)|
((cosa->txsize >> 8) & 0x1f));
- cosa_putstatus(cosa, SR_TX_INT_ENA);
#ifdef DEBUG_IO
+ debug_status_out(cosa, SR_TX_INT_ENA);
debug_data_out(cosa, ((cosa->txchan << 5) & 0xe0)|
((cosa->txsize >> 8) & 0x1f));
- debug_status_out(cosa, SR_TX_INT_ENA);
debug_data_in(cosa, cosa_getdata8(cosa));
#else
cosa_getdata8(cosa);
@@ -1630,27 +1701,57 @@
#endif
}
} else {
+ cosa_putstatus(cosa, SR_TX_INT_ENA);
cosa_putdata16(cosa, ((cosa->txchan<<13) & 0xe000)
| (cosa->txsize & 0x1fff));
- cosa_getdata16(cosa);
#ifdef DEBUG_IO
- debug_status_out(cosa, ((cosa->txchan<<13) & 0xe000)
+ debug_status_out(cosa, SR_TX_INT_ENA);
+ debug_data_out(cosa, ((cosa->txchan<<13) & 0xe000)
| (cosa->txsize & 0x1fff));
- debug_data_in(cosa, cosa_getdata16(cosa));
+ debug_data_in(cosa, cosa_getdata8(cosa));
+ debug_status_out(cosa, 0);
#else
- cosa_getdata16(cosa);
+ cosa_getdata8(cosa);
#endif
+ cosa_putstatus(cosa, 0);
}
- /* start the DMA */
- flags1 = claim_dma_lock();
- disable_dma(cosa->dma);
- clear_dma_ff(cosa->dma);
- set_dma_mode(cosa->dma, DMA_MODE_WRITE);
- set_dma_addr(cosa->dma, virt_to_bus(cosa->txbuf));
- set_dma_count(cosa->dma, cosa->txsize);
- enable_dma(cosa->dma);
- release_dma_lock(flags1);
+ if (cosa->busmaster) {
+ unsigned long addr = virt_to_bus(cosa->txbuf);
+ int count=0;
+ printk(KERN_INFO "busmaster IRQ\n");
+ while (!(cosa_getstatus(cosa)&SR_TX_RDY)) {
+ count++;
+ udelay(10);
+ if (count > 1000) break;
+ }
+ printk(KERN_INFO "status %x\n", cosa_getstatus(cosa));
+ printk(KERN_INFO "ready after %d loops\n", count);
+ cosa_putdata16(cosa, (addr >> 16)&0xffff);
+
+ count = 0;
+ while (!(cosa_getstatus(cosa)&SR_TX_RDY)) {
+ count++;
+ if (count > 1000) break;
+ udelay(10);
+ }
+ printk(KERN_INFO "ready after %d loops\n", count);
+ cosa_putdata16(cosa, addr &0xffff);
+ flags1 = claim_dma_lock();
+ set_dma_mode(cosa->dma, DMA_MODE_CASCADE);
+ enable_dma(cosa->dma);
+ release_dma_lock(flags1);
+ } else {
+ /* start the DMA */
+ flags1 = claim_dma_lock();
+ disable_dma(cosa->dma);
+ clear_dma_ff(cosa->dma);
+ set_dma_mode(cosa->dma, DMA_MODE_WRITE);
+ set_dma_addr(cosa->dma, virt_to_bus(cosa->txbuf));
+ set_dma_count(cosa->dma, cosa->txsize);
+ enable_dma(cosa->dma);
+ release_dma_lock(flags1);
+ }
cosa_putstatus(cosa, SR_TX_DMA_ENA|SR_USR_INT_ENA);
#ifdef DEBUG_IO
debug_status_out(cosa, SR_TX_DMA_ENA|SR_USR_INT_ENA);
@@ -1671,21 +1772,16 @@
if (is_8bit(cosa)) {
if (!test_bit(IRQBIT, &cosa->rxtx)) {
set_bit(IRQBIT, &cosa->rxtx);
- cosa_putstatus(cosa, 0);
cosa->rxsize = cosa_getdata8(cosa) <<8;
#ifdef DEBUG_IO
- debug_status_out(cosa, 0);
debug_data_in(cosa, cosa->rxsize >> 8);
#endif
- put_driver_status_nolock(cosa);
spin_unlock_irqrestore(&cosa->lock, flags);
return;
} else {
clear_bit(IRQBIT, &cosa->rxtx);
- cosa_putstatus(cosa, 0);
cosa->rxsize |= cosa_getdata8(cosa) & 0xff;
#ifdef DEBUG_IO
- debug_status_out(cosa, 0);
debug_data_in(cosa, cosa->rxsize & 0xff);
#endif
#if 0
@@ -1695,12 +1791,8 @@
}
} else {
cosa->rxsize = cosa_getdata16(cosa);
- cosa_putstatus(cosa, 0);
- cosa_putdata8(cosa, DRIVER_RX_READY);
#ifdef DEBUG_IO
debug_data_in(cosa, cosa->rxsize);
- debug_status_out(cosa, 0);
- debug_cmd_out(cosa, DRIVER_RX_READY);
#endif
#if 0
printk(KERN_INFO "cosa%d: receive rxsize = (0x%04x).\n",
@@ -1725,10 +1817,7 @@
reject: /* Reject the packet */
printk(KERN_INFO "cosa%d: rejecting packet on channel %d\n",
cosa->num, cosa->rxchan->num);
- /* FIXME: This works for COSA only (not SRP) */
- cosa->rxtx = 0;
- put_driver_status(cosa);
- return;
+ cosa->rxbuf = cosa->bouncebuf;
}
/* start the DMA */
@@ -1746,8 +1835,12 @@
release_dma_lock(flags);
spin_lock_irqsave(&cosa->lock, flags);
cosa_putstatus(cosa, SR_RX_DMA_ENA|SR_USR_INT_ENA);
+ if (!is_8bit(cosa) && (status & SR_TX_RDY))
+ cosa_putdata8(cosa, DRIVER_RX_READY);
#ifdef DEBUG_IO
debug_status_out(cosa, SR_RX_DMA_ENA|SR_USR_INT_ENA);
+ if (!is_8bit(cosa) && (status & SR_TX_RDY))
+ debug_data_cmd(cosa, DRIVER_RX_READY);
#endif
spin_unlock_irqrestore(&cosa->lock, flags);
}
@@ -1756,11 +1849,12 @@
{
unsigned long flags, flags1;
spin_lock_irqsave(&cosa->lock, flags);
+ flags1 = claim_dma_lock();
+ disable_dma(cosa->dma);
+ clear_dma_ff(cosa->dma);
+ release_dma_lock(flags1);
if (test_bit(TXBIT, &cosa->rxtx)) {
struct channel_data *chan = cosa->chan+cosa->txchan;
-#ifdef DEBUG_IRQS
- printk(KERN_INFO "cosa%d: end of transfer.\n", cosa->num);
-#endif
if (chan->tx_done)
if (chan->tx_done(chan, cosa->txsize))
clear_bit(chan->num, &cosa->txbitmap);
@@ -1775,6 +1869,9 @@
printk("\n");
}
#endif
+ /* Packet for unknown channel? */
+ if (cosa->rxbuf == cosa->bouncebuf)
+ goto out;
if (!cosa_dma_able(cosa->rxchan, cosa->rxbuf, cosa->rxsize))
memcpy(cosa->rxbuf, cosa->bouncebuf, cosa->rxsize);
if (cosa->rxchan->rx_done)
@@ -1786,12 +1883,11 @@
}
/*
* Clear the RXBIT, TXBIT and IRQBIT (the latest should be
- * cleared anyway).
+ * cleared anyway). We should do it as soon as possible
+ * so that we can tell the COSA we are done and to give it a time
+ * for recovery.
*/
- flags1 = claim_dma_lock();
- disable_dma(cosa->dma);
- clear_dma_ff(cosa->dma);
- release_dma_lock(flags1);
+out:
cosa->rxtx = 0;
put_driver_status_nolock(cosa);
spin_unlock_irqrestore(&cosa->lock, flags);
@@ -1799,7 +1895,7 @@
static void cosa_interrupt(int irq, void *cosa_, struct pt_regs *regs)
{
- int status;
+ unsigned status;
int count = 0;
struct cosa_data *cosa = cosa_;
again:
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)