[PATCH 2.4] wavelan_cs update (v23)

Jean Tourrilhes (jt@bougret.hpl.hp.com)
Tue, 6 Aug 2002 11:16:40 -0700


Hi Marcelo,

The wavelan_cs driver currently in your kernel is completely
non functional :
o Oops on SMP
o lockup on CPUs faster than about 300 MHz
o not conform to current net API
This patch re-synch the driver with the version in the Pcmcia
package. This patch has been in 2.5.X since 2.5.3-pre2 and was already
in 2.4.19-acX. I also fixed dev->trans_start.
This is a big patch, but it has proven safe in both Pcmcia
package, 2.5.X and 2.4.19-acX. And I've tested it intensively on
2.4.19.
Please apply...

Jean

P.S. : I don't see the point of keeping broken drivers in the kernel,
so if you don't accept the patch, please remove this driver from your
kernel. That will force people to use the Pcmcia package.

---------------------------------------------------------------

diff -u -p -r linux/drivers/net/pcmcia-buggy/wavelan.h linux/drivers/net/pcmcia/wavelan.h
--- linux/drivers/net/pcmcia-buggy/wavelan.h Tue Aug 6 10:35:51 2002
+++ linux/drivers/net/pcmcia/wavelan.h Tue Aug 6 10:37:05 2002
@@ -88,6 +88,7 @@ const short channel_bands[] = { 0x30, 0x
*/
const int fixed_bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };

+
/*************************** PC INTERFACE ****************************/

/* WaveLAN host interface definitions */
@@ -316,6 +317,7 @@ struct mmw_t
/* Calculate offset of a field in the above structure */
#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)

+
/*
* Modem Management Controller (MMC) read structure.
*/
@@ -372,6 +374,7 @@ struct mmr_t

/* Calculate offset of a field in the above structure */
#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+

/* Make the two above structures one */
typedef union mm_t
diff -u -p -r linux/drivers/net/pcmcia-buggy/wavelan_cs.c linux/drivers/net/pcmcia/wavelan_cs.c
--- linux/drivers/net/pcmcia-buggy/wavelan_cs.c Tue Aug 6 10:35:51 2002
+++ linux/drivers/net/pcmcia/wavelan_cs.c Tue Aug 6 10:37:05 2002
@@ -41,7 +41,8 @@
* Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000
* - reorganize kmallocs in wavelan_attach, checking all for failure
* and releasing the previous allocations if one fails
- *
+ * <No longer true, we reverted to the version in the Pcmcia package
+ * which was correct in the first place - Jean II>
*
****************************************************************************
* Copyright 1995
@@ -72,6 +73,34 @@

/*------------------------------------------------------------------*/
/*
+ * Wrapper for disabling interrupts.
+ * (note : inline, so optimised away)
+ */
+static inline void
+wv_splhi(net_local * lp,
+ unsigned long * pflags)
+{
+ spin_lock_irqsave(&lp->spinlock, *pflags);
+ /* Note : above does the cli(); itself */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for re-enabling interrupts.
+ */
+static inline void
+wv_splx(net_local * lp,
+ unsigned long * pflags)
+{
+ spin_unlock_irqrestore(&lp->spinlock, *pflags);
+
+ /* Note : enabling interrupts on the hardware is done in wv_ru_start()
+ * via : outb(OP1_INT_ENABLE, LCCR(base));
+ */
+}
+
+/*------------------------------------------------------------------*/
+/*
* Wrapper for reporting error to cardservices
*/
static void cs_error(client_handle_t handle, int func, int ret)
@@ -103,7 +132,7 @@ wv_structuct_check(void)

/******************* MODEM MANAGEMENT SUBROUTINES *******************/
/*
- * Usefull subroutines to manage the modem of the wavelan
+ * Useful subroutines to manage the modem of the wavelan
*/

/*------------------------------------------------------------------*/
@@ -138,7 +167,7 @@ hacr_write_slow(u_long base,
{
hacr_write(base, hacr);
/* delay might only be needed sometimes */
- mdelay(1L);
+ mdelay(1);
} /* hacr_write_slow */

/*------------------------------------------------------------------*/
@@ -569,18 +598,18 @@ void wv_nwid_filter(unsigned char mode,
#endif

/* Disable interrupts & save flags */
- spin_lock_irqsave (&lp->lock, flags);
+ wv_splhi(lp, &flags);

m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00;
mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1);

- /* ReEnable interrupts & restore flags */
- spin_unlock_irqrestore (&lp->lock, flags);
-
if(mode==NWID_PROMISC)
lp->cell_search=1;
else
- lp->cell_search=0;
+ lp->cell_search=0;
+
+ /* ReEnable interrupts & restore flags */
+ wv_splx(lp, &flags);
}

/* Find a record in the WavePoint table matching a given NWID */
@@ -737,7 +766,7 @@ void wv_roam_handover(wavepoint_history
ioaddr_t base = lp->dev->base_addr;
mm_t m;
unsigned long flags;
-
+
if(wavepoint==lp->curr_point) /* Sanity check... */
{
wv_nwid_filter(!NWID_PROMISC,lp);
@@ -749,16 +778,16 @@ void wv_roam_handover(wavepoint_history
#endif

/* Disable interrupts & save flags */
- spin_lock_irqsave(&lp->lock, flags);
-
+ wv_splhi(lp, &flags);
+
m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF;
m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8;

mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2);

/* ReEnable interrupts & restore flags */
- spin_unlock_irqrestore (&lp->lock, flags);
-
+ wv_splx(lp, &flags);
+
wv_nwid_filter(!NWID_PROMISC,lp);
lp->curr_point=wavepoint;
}
@@ -775,6 +804,11 @@ static inline void wl_roam_gather(device
wavepoint_history *wavepoint=NULL; /* WavePoint table entry */
net_local *lp=(net_local *)dev->priv; /* Device info */

+#ifdef I_NEED_THIS_FEATURE
+ /* Some people don't need this, some other may need it */
+ nwid=nwid^ntohs(beacon->domain_id);
+#endif
+
#if WAVELAN_ROAMING_DEBUG > 1
printk(KERN_DEBUG "WaveLAN: beacon, dev %s:\n",dev->name);
printk(KERN_DEBUG "Domain: %.4X NWID: %.4X SigQual=%d\n",ntohs(beacon->domain_id),nwid,sigqual);
@@ -832,7 +866,9 @@ static inline int WAVELAN_BEACON(unsigne
/*------------------------------------------------------------------*/
/*
* Routine to synchronously send a command to the i82593 chip.
- * Should be called with interrupts enabled.
+ * Should be called with interrupts disabled.
+ * (called by wv_packet_write(), wv_ru_stop(), wv_ru_start(),
+ * wv_82593_config() & wv_diag())
*/
static int
wv_82593_cmd(device * dev,
@@ -841,74 +877,98 @@ wv_82593_cmd(device * dev,
int result)
{
ioaddr_t base = dev->base_addr;
- net_local * lp = (net_local *)dev->priv;
int status;
+ int wait_completed;
long spin;
- u_long flags;

/* Spin until the chip finishes executing its current command (if any) */
+ spin = 1000;
do
{
- spin_lock_irqsave (&lp->lock, flags);
+ /* Time calibration of the loop */
+ udelay(10);
+
+ /* Read the interrupt register */
outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
status = inb(LCSR(base));
- spin_unlock_irqrestore (&lp->lock, flags);
}
- while((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE);
+ while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0));

- /* We are waiting for command completion */
- wv_wait_completed = TRUE;
+ /* If the interrupt hasn't be posted */
+ if(spin <= 0)
+ {
+#ifdef DEBUG_INTERRUPT_ERROR
+ printk(KERN_INFO "wv_82593_cmd: %s timeout (previous command), status 0x%02x\n",
+ str, status);
+#endif
+ return(FALSE);
+ }

/* Issue the command to the controller */
outb(cmd, LCCR(base));

- /* If we don't have to check the result of the command */
+ /* If we don't have to check the result of the command
+ * Note : this mean that the irq handler will deal with that */
if(result == SR0_NO_RESULT)
- {
- wv_wait_completed = FALSE;
- return(TRUE);
- }
+ return(TRUE);

- /* Busy wait while the LAN controller executes the command.
- * Note : wv_wait_completed should be volatile */
- spin = 0;
- while(wv_wait_completed && (spin++ < 1000))
- udelay(10);
+ /* We are waiting for command completion */
+ wait_completed = TRUE;

- /* If the interrupt handler hasn't be called */
- if(wv_wait_completed)
+ /* Busy wait while the LAN controller executes the command. */
+ spin = 1000;
+ do
{
- outb(OP0_NOP, LCCR(base));
+ /* Time calibration of the loop */
+ udelay(10);
+
+ /* Read the interrupt register */
+ outb(CR0_STATUS_0 | OP0_NOP, LCCR(base));
status = inb(LCSR(base));
- if(status & SR0_INTERRUPT)
+
+ /* Check if there was an interrupt posted */
+ if((status & SR0_INTERRUPT))
{
- /* There was an interrupt : call the interrupt handler */
-#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_WARNING "wv_82593_cmd: interrupt handler not installed or interrupt disabled\n");
-#endif
+ /* Acknowledge the interrupt */
+ outb(CR0_INT_ACK | OP0_NOP, LCCR(base));

- wavelan_interrupt(dev->irq, (void *) dev,
- (struct pt_regs *) NULL);
+ /* Check if interrupt is a command completion */
+ if(((status & SR0_BOTH_RX_TX) != SR0_BOTH_RX_TX) &&
+ ((status & SR0_BOTH_RX_TX) != 0x0) &&
+ !(status & SR0_RECEPTION))
+ {
+ /* Signal command completion */
+ wait_completed = FALSE;
+ }
+ else
+ {
+ /* Note : Rx interrupts will be handled later, because we can
+ * handle multiple Rx packets at once */
+#ifdef DEBUG_INTERRUPT_INFO
+ printk(KERN_INFO "wv_82593_cmd: not our interrupt\n");
+#endif
+ }
}
- else
- {
- wv_wait_completed = 0; /* XXX */
+ }
+ while(wait_completed && (spin-- > 0));
+
+ /* If the interrupt hasn't be posted */
+ if(wait_completed)
+ {
#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO "wv_82593_cmd: %s timeout, status0 0x%02x\n",
- str, status);
+ printk(KERN_INFO "wv_82593_cmd: %s timeout, status 0x%02x\n",
+ str, status);
#endif
- /* We probably should reset the controller here */
- return(FALSE);
- }
+ return(FALSE);
}

- /* Check the return code provided by the interrupt handler against
+ /* Check the return code returned by the card (see above) against
* the expected return code provided by the caller */
- if((lp->status & SR0_EVENT_MASK) != result)
+ if((status & SR0_EVENT_MASK) != result)
{
#ifdef DEBUG_INTERRUPT_ERROR
- printk(KERN_INFO "wv_82593_cmd: %s failed, status0 = 0x%x\n",
- str, lp->status);
+ printk(KERN_INFO "wv_82593_cmd: %s failed, status = 0x%x\n",
+ str, status);
#endif
return(FALSE);
}
@@ -924,14 +984,16 @@ wv_82593_cmd(device * dev,
static inline int
wv_diag(device * dev)
{
+ int ret = FALSE;
+
if(wv_82593_cmd(dev, "wv_diag(): diagnose",
OP0_DIAGNOSE, SR0_DIAGNOSE_PASSED))
- return(TRUE);
+ ret = TRUE;

#ifdef DEBUG_CONFIG_ERROR
printk(KERN_INFO "wavelan_cs: i82593 Self Test failed!\n");
#endif
- return(FALSE);
+ return(ret);
} /* wv_diag */

/*------------------------------------------------------------------*/
@@ -951,15 +1013,6 @@ read_ringbuf(device * dev,
int chunk_len;
char * buf_ptr = buf;

-#ifdef OLDIES
- /* After having check skb_put (net/core/skbuff.c) in the kernel, it seem
- * quite safe to remove this... */
-
- /* If buf is NULL, just increment the ring buffer pointer */
- if(buf == NULL)
- return((ring_ptr - RX_BASE + len) % RX_SIZE + RX_BASE);
-#endif
-
/* Get all the buffer */
while(len > 0)
{
@@ -990,70 +1043,32 @@ read_ringbuf(device * dev,
* wavelan_interrupt is not an option...), so you may experience
* some delay sometime...
*/
-static inline void wv_82593_reconfig (device * dev)
+static inline void
+wv_82593_reconfig(device * dev)
{
- net_local *lp = (net_local *) dev->priv;
- dev_link_t *link = ((net_local *) dev->priv)->link;
+ net_local * lp = (net_local *)dev->priv;
+ dev_link_t * link = ((net_local *) dev->priv)->link;
+ unsigned long flags;
+
+ /* Arm the flag, will be cleard in wv_82593_config() */
+ lp->reconfig_82593 = TRUE;

- /* Check if we can do it now ! */
- if (!(link->open)) {
- lp->reconfig_82593 = TRUE;
+ /* Check if we can do it now ! */
+ if((link->open) && (netif_running(dev)) && !(netif_queue_stopped(dev)))
+ {
+ wv_splhi(lp, &flags); /* Disable interrupts */
+ wv_82593_config(dev);
+ wv_splx(lp, &flags); /* Re-enable interrupts */
+ }
+ else
+ {
#ifdef DEBUG_IOCTL_INFO
- printk (KERN_DEBUG "%s: wv_82593_reconfig(): delayed (link = %d)\n",
- dev->name, link->open);
+ printk(KERN_DEBUG
+ "%s: wv_82593_reconfig(): delayed (state = %lX, link = %d)\n",
+ dev->name, dev->state, link->open);
#endif
- } else {
- netif_stop_queue (dev);
-
- lp->reconfig_82593 = FALSE;
- wv_82593_config (dev);
- netif_wake_queue (dev);
- }
-}
-
-#ifdef OLDIES
-/*------------------------------------------------------------------*/
-/*
- * Dumps the current i82593 receive buffer to the console.
- */
-static void wavelan_dump(device *dev)
-{
- ioaddr_t base = dev->base_addr;
- int i, c;
-
- /* disable receiver so we can use channel 1 */
- outb(OP0_RCV_DISABLE, LCCR(base));
-
- /* reset receive DMA pointer */
- hacr_write_slow(base, HACR_PWR_STAT | HACR_RX_DMA_RESET);
- hacr_write(base, HACR_DEFAULT);
-
- /* dump into receive buffer */
- wv_82593_cmd(dev, "wavelan_dump(): dump", CR0_CHNL|OP0_DUMP, SR0_DUMP_DONE);
-
- /* set read pointer to start of receive buffer */
- outb(0, PIORL(base));
- outb(0, PIORH(base));
-
- printk(KERN_DEBUG "wavelan_cs: dump:\n");
- printk(KERN_DEBUG " 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
- for(i = 0; i < 73; i++){
- if((i % 16) == 0) {
- printk("\n0x%02x:", i);
- if (!i) {
- printk(" ");
- continue;
- }
}
- c = inb(PIOP(base));
- printk("%02x ", c);
- }
- printk("\n");
-
- /* enable the receiver again */
- wv_ru_start(dev);
}
-#endif

/********************* DEBUG & INFO SUBROUTINES *********************/
/*
@@ -1171,6 +1186,8 @@ wv_mmc_show(device * dev)
return;
}

+ wv_splhi(lp, &flags);
+
/* Read the mmc */
mmc_out(base, mmwoff(0, mmw_freeze), 1);
mmc_read(base, 0, (u_char *)&m, sizeof(m));
@@ -1181,6 +1198,8 @@ wv_mmc_show(device * dev)
lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
#endif /* WIRELESS_EXT */

+ wv_splx(lp, &flags);
+
printk(KERN_DEBUG "##### wavelan modem status registers: #####\n");
#ifdef DEBUG_SHOW_UNUSED
printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
@@ -1265,6 +1284,7 @@ static void
wv_dev_show(device * dev)
{
printk(KERN_DEBUG "dev:");
+ printk(" state=%lX,", dev->state);
printk(" trans_start=%ld,", dev->trans_start);
printk(" flags=0x%x,", dev->flags);
printk("\n");
@@ -1892,7 +1912,7 @@ wavelan_ioctl(struct net_device * dev, /
#endif

/* Disable interrupts & save flags */
- spin_lock_irqsave (&lp->lock, flags);
+ wv_splhi(lp, &flags);

/* Look what is the request */
switch(cmd)
@@ -1968,7 +1988,7 @@ wavelan_ioctl(struct net_device * dev, /

case SIOCGIWFREQ:
/* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
- * (does it work for everybody XXX - especially old cards...) */
+ * (does it work for everybody ? - especially old cards...) */
if(!(mmc_in(base, mmroff(0, mmr_fee_status)) &
(MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
{
@@ -2239,15 +2259,17 @@ wavelan_ioctl(struct net_device * dev, /
{
struct iw_range range;

- /* Set the length (very important for backward compatibility) */
- wrq->u.data.length = sizeof(struct iw_range);
+ /* Set the length (very important for backward compatibility) */
+ wrq->u.data.length = sizeof(struct iw_range);

- /* Set all the info we don't care or don't know about to zero */
- memset(&range, 0, sizeof(range));
+ /* Set all the info we don't care or don't know about to zero */
+ memset(&range, 0, sizeof(range));

- /* Set the Wireless Extension versions */
- range.we_version_compiled = WIRELESS_EXT;
- range.we_version_source = 9; /* Nothing for us in v10 and v11 */
+#if WIRELESS_EXT > 10
+ /* Set the Wireless Extension versions */
+ range.we_version_compiled = WIRELESS_EXT;
+ range.we_version_source = 9; /* Nothing for us in v10 and v11 */
+#endif /* WIRELESS_EXT > 10 */

/* Set information in the range struct */
range.throughput = 1.4 * 1000 * 1000; /* don't argue on this ! */
@@ -2517,7 +2539,7 @@ wavelan_ioctl(struct net_device * dev, /
}

/* ReEnable interrupts & restore flags */
- spin_unlock_irqrestore (&lp->lock, flags);
+ wv_splx(lp, &flags);

#ifdef DEBUG_IOCTL_TRACE
printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
@@ -2543,11 +2565,8 @@ wavelan_get_wireless_stats(device * dev)
printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name);
#endif

- if (lp == NULL) /* XXX will this ever occur? */
- return NULL;
-
/* Disable interrupts & save flags */
- spin_lock_irqsave (&lp->lock, flags);
+ wv_splhi(lp, &flags);

wstats = &lp->wstats;

@@ -2573,7 +2592,7 @@ wavelan_get_wireless_stats(device * dev)
wstats->discard.misc = 0L;

/* ReEnable interrupts & restore flags */
- spin_unlock_irqrestore (&lp->lock, flags);
+ wv_splx(lp, &flags);

#ifdef DEBUG_IOCTL_TRACE
printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name);
@@ -2692,12 +2711,6 @@ wv_packet_read(device * dev,
skb->protocol = eth_type_trans(skb, dev);

#ifdef DEBUG_RX_INFO
- /* Another glitch : Due to the way the GET_PACKET macro is written,
- * we are not sure to have the same thing in skb->data. On the other
- * hand, skb->mac.raw is not defined everywhere...
- * For versions between 1.2.13 and those where skb->mac.raw appear,
- * I don't have a clue...
- */
wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
#endif /* DEBUG_RX_INFO */

@@ -2731,9 +2744,7 @@ wv_packet_read(device * dev,
wl_roam_gather(dev, skb->data, stats);
#endif /* WAVELAN_ROAMING */

- /* Spying stuff */
#ifdef WIRELESS_SPY
- /* Same as above */
wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats);
#endif /* WIRELESS_SPY */
#ifdef HISTOGRAM
@@ -2766,6 +2777,7 @@ wv_packet_read(device * dev,
* called to do the actual transfer of the card's data including the
* ethernet header into a packet consisting of an sk_buff chain.
* (called by wavelan_interrupt())
+ * Note : the spinlock is already grabbed for us and irq are disabled.
*/
static inline void
wv_packet_rcv(device * dev)
@@ -2916,7 +2928,7 @@ wv_packet_write(device * dev,
printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length);
#endif

- spin_lock_irqsave (&lp->lock, flags);
+ wv_splhi(lp, &flags);

/* Check if we need some padding */
if(clen < ETH_ZLEN)
@@ -2943,18 +2955,13 @@ wv_packet_write(device * dev,
wv_82593_cmd(dev, "wv_packet_write(): transmit",
OP0_TRANSMIT, SR0_NO_RESULT);

+ /* Make sure the watchdog will keep quiet for a while */
+ dev->trans_start = jiffies;
+
/* Keep stats up to date */
lp->stats.tx_bytes += length;

- /* If watchdog not already active, activate it... */
- if (!timer_pending(&lp->watchdog))
- {
- /* set timer to expire in WATCHDOG_JIFFIES */
- lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
- add_timer(&lp->watchdog);
- }
-
- spin_unlock_irqrestore (&lp->lock, flags);
+ wv_splx(lp, &flags);

#ifdef DEBUG_TX_INFO
wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write");
@@ -2963,56 +2970,57 @@ wv_packet_write(device * dev,
#ifdef DEBUG_TX_TRACE
printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
#endif
-
- netif_start_queue (dev);
}

/*------------------------------------------------------------------*/
/*
* This routine is called when we want to send a packet (NET3 callback)
- * In this routine, we check if the hardware is ready to accept
+ * In this routine, we check if the harware is ready to accept
* the packet. We also prevent reentrance. Then, we call the function
* to send the packet...
*/
-static int wavelan_packet_xmit (struct sk_buff *skb,
- device * dev)
+static int
+wavelan_packet_xmit(struct sk_buff * skb,
+ device * dev)
{
- net_local *lp = (net_local *) dev->priv;
+ net_local * lp = (net_local *)dev->priv;
+ unsigned long flags;

#ifdef DEBUG_TX_TRACE
- printk (KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
- (unsigned) skb);
+ printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
+ (unsigned) skb);
#endif

- /*
- * For ethernet, fill in the header.
- */
+ /*
+ * Block a timer-based transmit from overlapping a previous transmit.
+ * In other words, prevent reentering this routine.
+ */
+ netif_stop_queue(dev);

- netif_stop_queue (dev);
+ /* If somebody has asked to reconfigure the controller,
+ * we can do it now */
+ if(lp->reconfig_82593)
+ {
+ wv_splhi(lp, &flags); /* Disable interrupts */
+ wv_82593_config(dev);
+ wv_splx(lp, &flags); /* Re-enable interrupts */
+ /* Note : the configure procedure was totally synchronous,
+ * so the Tx buffer is now free */
+ }

- /*
- * Block a timer-based transmit from overlapping a previous transmit.
- * In other words, prevent reentering this routine.
- */
- if (1) {
- /* If somebody has asked to reconfigure the controller, we can do it now */
- if (lp->reconfig_82593) {
- lp->reconfig_82593 = FALSE;
- wv_82593_config (dev);
- }
#ifdef DEBUG_TX_ERROR
- if (skb->next)
- printk (KERN_INFO "skb has next\n");
+ if (skb->next)
+ printk(KERN_INFO "skb has next\n");
#endif

- wv_packet_write (dev, skb->data, skb->len);
- }
- dev_kfree_skb (skb);
+ wv_packet_write(dev, skb->data, skb->len);
+
+ dev_kfree_skb(skb);

#ifdef DEBUG_TX_TRACE
- printk (KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
+ printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
#endif
- return (0);
+ return(0);
}

/********************** HARDWARE CONFIGURATION **********************/
@@ -3165,7 +3173,7 @@ wv_mmc_init(device * dev)
*/

/* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
- * (does it work for everybody XXX - especially old cards...) */
+ * (does it work for everybody ? - especially old cards...) */
/* Note : WFREQSEL verify that it is able to read from EEprom
* a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID
* is 0xA (Xilinx version) or 0xB (Ariadne version).
@@ -3223,7 +3231,7 @@ static int
wv_ru_stop(device * dev)
{
ioaddr_t base = dev->base_addr;
- net_local *lp = (net_local *) dev->priv;
+ net_local * lp = (net_local *) dev->priv;
unsigned long flags;
int status;
int spin;
@@ -3232,35 +3240,35 @@ wv_ru_stop(device * dev)
printk(KERN_DEBUG "%s: ->wv_ru_stop()\n", dev->name);
#endif

+ wv_splhi(lp, &flags);
+
/* First, send the LAN controller a stop receive command */
wv_82593_cmd(dev, "wv_graceful_shutdown(): stop-rcv",
OP0_STOP_RCV, SR0_NO_RESULT);

/* Then, spin until the receive unit goes idle */
- spin = 0;
+ spin = 300;
do
{
udelay(10);
- spin_lock_irqsave (&lp->lock, flags);
outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
status = inb(LCSR(base));
- spin_unlock_irqrestore (&lp->lock, flags);
}
- while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin++ < 300));
+ while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE) && (spin-- > 0));

/* Now, spin until the chip finishes executing its current command */
do
{
udelay(10);
- spin_lock_irqsave (&lp->lock, flags);
outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
status = inb(LCSR(base));
- spin_unlock_irqrestore (&lp->lock, flags);
}
- while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin++ < 300));
+ while(((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE) && (spin-- > 0));
+
+ wv_splx(lp, &flags);

/* If there was a problem */
- if(spin > 300)
+ if(spin <= 0)
{
#ifdef DEBUG_CONFIG_ERROR
printk(KERN_INFO "%s: wv_ru_stop(): The chip doesn't want to stop...\n",
@@ -3287,6 +3295,7 @@ wv_ru_start(device * dev)
{
ioaddr_t base = dev->base_addr;
net_local * lp = (net_local *) dev->priv;
+ unsigned long flags;

#ifdef DEBUG_CONFIG_TRACE
printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
@@ -3300,6 +3309,8 @@ wv_ru_start(device * dev)
if(!wv_ru_stop(dev))
return FALSE;

+ wv_splhi(lp, &flags);
+
/* Now we know that no command is being executed. */

/* Set the receive frame pointer and stop pointer */
@@ -3309,8 +3320,17 @@ wv_ru_start(device * dev)
/* Reset ring management. This sets the receive frame pointer to 1 */
outb(OP1_RESET_RING_MNGMT, LCCR(base));

+#if 0
+ /* XXX the i82593 manual page 6-4 seems to indicate that the stop register
+ should be set as below */
+ /* outb(CR1_STOP_REG_UPDATE|((RX_SIZE - 0x40)>> RX_SIZE_SHIFT),LCCR(base));*/
+#elif 0
+ /* but I set it 0 instead */
+ lp->stop = 0;
+#else
/* but I set it to 3 bytes per packet less than 8K */
lp->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
+#endif
outb(CR1_STOP_REG_UPDATE | (lp->stop >> RX_SIZE_SHIFT), LCCR(base));
outb(OP1_INT_ENABLE, LCCR(base));
outb(OP1_SWIT_TO_PORT_0, LCCR(base));
@@ -3326,17 +3346,15 @@ wv_ru_start(device * dev)
#ifdef DEBUG_I82593_SHOW
{
int status;
- unsigned long flags;
- int i = 0;
+ int opri;
+ int spin = 10000;

/* spin until the chip starts receiving */
do
{
- spin_lock_irqsave (&lp->lock, flags);
outb(OP0_NOP | CR0_STATUS_3, LCCR(base));
status = inb(LCSR(base));
- spin_unlock_irqrestore (&lp->lock, flags);
- if(i++ > 10000)
+ if(spin-- <= 0)
break;
}
while(((status & SR3_RCV_STATE_MASK) != SR3_RCV_ACTIVE) &&
@@ -3345,6 +3363,9 @@ wv_ru_start(device * dev)
(status & SR3_RCV_STATE_MASK), i);
}
#endif
+
+ wv_splx(lp, &flags);
+
#ifdef DEBUG_CONFIG_TRACE
printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
#endif
@@ -3363,6 +3384,7 @@ wv_82593_config(device * dev)
ioaddr_t base = dev->base_addr;
net_local * lp = (net_local *) dev->priv;
struct i82593_conf_block cfblk;
+ int ret = TRUE;

#ifdef DEBUG_CONFIG_TRACE
printk(KERN_DEBUG "%s: ->wv_82593_config()\n", dev->name);
@@ -3457,7 +3479,7 @@ wv_82593_config(device * dev)
hacr_write(base, HACR_DEFAULT);
if(!wv_82593_cmd(dev, "wv_82593_config(): configure",
OP0_CONFIGURE, SR0_CONFIGURE_DONE))
- return(FALSE);
+ ret = FALSE;

/* Initialize adapter's ethernet MAC address */
outb(TX_BASE & 0xff, PIORL(base));
@@ -3471,7 +3493,7 @@ wv_82593_config(device * dev)
hacr_write(base, HACR_DEFAULT);
if(!wv_82593_cmd(dev, "wv_82593_config(): ia-setup",
OP0_IA_SETUP, SR0_IA_SETUP_DONE))
- return(FALSE);
+ ret = FALSE;

#ifdef WAVELAN_ROAMING
/* If roaming is enabled, join the "Beacon Request" multicast group... */
@@ -3508,14 +3530,17 @@ wv_82593_config(device * dev)
hacr_write(base, HACR_DEFAULT);
if(!wv_82593_cmd(dev, "wv_82593_config(): mc-setup",
OP0_MC_SETUP, SR0_MC_SETUP_DONE))
- return(FALSE);
+ ret = FALSE;
lp->mc_count = dev->mc_count; /* remember to avoid repeated reset */
}

+ /* Job done, clear the flag */
+ lp->reconfig_82593 = FALSE;
+
#ifdef DEBUG_CONFIG_TRACE
printk(KERN_DEBUG "%s: <-wv_82593_config()\n", dev->name);
#endif
- return(TRUE);
+ return(ret);
}

/*------------------------------------------------------------------*/
@@ -3594,6 +3619,8 @@ wv_hw_config(device * dev)
{
net_local * lp = (net_local *) dev->priv;
ioaddr_t base = dev->base_addr;
+ unsigned long flags;
+ int ret = FALSE;

#ifdef DEBUG_CONFIG_TRACE
printk(KERN_DEBUG "%s: ->wv_hw_config()\n", dev->name);
@@ -3612,50 +3639,78 @@ wv_hw_config(device * dev)
if(wv_pcmcia_reset(dev) == FALSE)
return FALSE;

- /* Power UP the module + reset the modem + reset host adapter
- * (in fact, reset DMA channels) */
- hacr_write_slow(base, HACR_RESET);
- hacr_write(base, HACR_DEFAULT);
+ /* Disable interrupts */
+ wv_splhi(lp, &flags);

- /* Check if the module has been powered up... */
- if(hasr_read(base) & HASR_NO_CLK)
+ /* Disguised goto ;-) */
+ do
{
+ /* Power UP the module + reset the modem + reset host adapter
+ * (in fact, reset DMA channels) */
+ hacr_write_slow(base, HACR_RESET);
+ hacr_write(base, HACR_DEFAULT);
+
+ /* Check if the module has been powered up... */
+ if(hasr_read(base) & HASR_NO_CLK)
+ {
#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_WARNING "%s: wv_hw_config(): modem not connected or not a wavelan card\n",
- dev->name);
+ printk(KERN_WARNING "%s: wv_hw_config(): modem not connected or not a wavelan card\n",
+ dev->name);
#endif
- return FALSE;
- }
+ break;
+ }

- /* initialize the modem */
- if(wv_mmc_init(dev) == FALSE)
- return FALSE;
+ /* initialize the modem */
+ if(wv_mmc_init(dev) == FALSE)
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_WARNING "%s: wv_hw_config(): Can't configure the modem\n",
+ dev->name);
+#endif
+ break;
+ }

- /* reset the LAN controller (i82593) */
- outb(OP0_RESET, LCCR(base));
- mdelay(1); /* A bit crude ! */
-
- /* Initialize the LAN controller */
- if((wv_82593_config(dev) == FALSE) ||
- (wv_diag(dev) == FALSE))
- {
+ /* reset the LAN controller (i82593) */
+ outb(OP0_RESET, LCCR(base));
+ mdelay(1); /* A bit crude ! */
+
+ /* Initialize the LAN controller */
+ if(wv_82593_config(dev) == FALSE)
+ {
#ifdef DEBUG_CONFIG_ERRORS
- printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n", dev->name);
+ printk(KERN_INFO "%s: wv_hw_config(): i82593 init failed\n",
+ dev->name);
#endif
- return FALSE;
- }
+ break;
+ }

- /*
- * insert code for loopback test here
- */
+ /* Diagnostic */
+ if(wv_diag(dev) == FALSE)
+ {
+#ifdef DEBUG_CONFIG_ERRORS
+ printk(KERN_INFO "%s: wv_hw_config(): i82593 diagnostic failed\n",
+ dev->name);
+#endif
+ break;
+ }

- /* The device is now configured */
- lp->configured = 1;
+ /*
+ * insert code for loopback test here
+ */
+
+ /* The device is now configured */
+ lp->configured = 1;
+ ret = TRUE;
+ }
+ while(0);
+
+ /* Re-enable interrupts */
+ wv_splx(lp, &flags);

#ifdef DEBUG_CONFIG_TRACE
printk(KERN_DEBUG "%s: <-wv_hw_config()\n", dev->name);
#endif
- return TRUE;
+ return(ret);
}

/*------------------------------------------------------------------*/
@@ -3675,10 +3730,6 @@ wv_hw_reset(device * dev)
printk(KERN_DEBUG "%s: ->wv_hw_reset()\n", dev->name);
#endif

- /* If watchdog was activated, kill it ! */
- if (timer_pending(&lp->watchdog))
- del_timer(&lp->watchdog);
-
lp->nresets++;
lp->configured = 0;

@@ -3786,13 +3837,13 @@ wv_pcmcia_config(dev_link_t * link)
}

/*
- * Allocate a 4K memory window. Note that the dev_link_t
+ * Allocate a small memory window. Note that the dev_link_t
* structure provides space for one window handle -- if your
* device needs several windows, you'll need to keep track of
* the handles in your private data structure, link->priv.
*/
req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
- req.Base = 0; req.Size = 0x1000;
+ req.Base = req.Size = 0;
req.AccessSpeed = mem_speed;
link->win = (window_handle_t)link->handle;
i = CardServices(RequestWindow, &link->win, &req);
@@ -3803,7 +3854,7 @@ wv_pcmcia_config(dev_link_t * link)
}

dev->rmem_start = dev->mem_start =
- (u_long)ioremap(req.Base, 0x1000);
+ (u_long)ioremap(req.Base, req.Size);
dev->rmem_end = dev->mem_end = dev->mem_start + req.Size;

mem.CardOffset = 0; mem.Page = 0;
@@ -3817,7 +3868,7 @@ wv_pcmcia_config(dev_link_t * link)
/* Feed device with this info... */
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
- netif_start_queue (dev);
+ netif_start_queue(dev);

#ifdef DEBUG_CONFIG_INFO
printk(KERN_DEBUG "wv_pcmcia_config: MEMSTART 0x%x IRQ %d IOPORT 0x%x\n",
@@ -3843,7 +3894,7 @@ wv_pcmcia_config(dev_link_t * link)
return FALSE;
}

- /* XXX Could you explain me this, Dave ? */
+ strcpy(((net_local *) dev->priv)->node.dev_name, dev->name);
link->dev = &((net_local *) dev->priv)->node;

#ifdef DEBUG_CONFIG_TRACE
@@ -3887,7 +3938,7 @@ wv_pcmcia_release(u_long arg) /* Address
CardServices(ReleaseIO, link->handle, &link->io);
CardServices(ReleaseIRQ, link->handle, &link->irq);

- link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING | DEV_STALE_CONFIG);
+ link->state &= ~(DEV_CONFIG | DEV_STALE_CONFIG);

#ifdef DEBUG_CONFIG_TRACE
printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name);
@@ -3896,7 +3947,7 @@ wv_pcmcia_release(u_long arg) /* Address

/*------------------------------------------------------------------*/
/*
- * Sometimes, netwave_detach can't be performed following a call from
+ * Sometimes, wavelan_detach can't be performed following a call from
* cardmgr (device still open, pcmcia_release not done) and the device
* is put in a STALE_LINK state and remains in memory.
*
@@ -3970,7 +4021,19 @@ wavelan_interrupt(int irq,
lp = (net_local *) dev->priv;
base = dev->base_addr;

- spin_lock (&lp->lock);
+#ifdef DEBUG_INTERRUPT_INFO
+ /* Check state of our spinlock (it should be cleared) */
+ if(spin_is_locked(&lp->spinlock))
+ printk(KERN_DEBUG
+ "%s: wavelan_interrupt(): spinlock is already locked !!!\n",
+ dev->name);
+#endif
+
+ /* Prevent reentrancy. We need to do that because we may have
+ * multiple interrupt handler running concurently.
+ * It is safe because wv_splhi() disable interrupts before aquiring
+ * the spinlock. */
+ spin_lock(&lp->spinlock);

/* Treat all pending interrupts */
while(1)
@@ -4015,8 +4078,6 @@ wavelan_interrupt(int irq,
break;
}

- lp->status = status0; /* Save current status (for commands) */
-
/* ----------------- RECEIVING PACKET ----------------- */
/*
* When the wavelan signal the reception of a new packet,
@@ -4054,22 +4115,6 @@ wavelan_interrupt(int irq,
* Most likely : transmission done
*/

- /* If we are already waiting elsewhere for the command to complete */
- if(wv_wait_completed)
- {
-#ifdef DEBUG_INTERRUPT_INFO
- printk(KERN_DEBUG "%s: wv_interrupt(): command completed\n",
- dev->name);
-#endif
-
- /* Signal command completion */
- wv_wait_completed = 0;
-
- /* Acknowledge the interrupt */
- outb(CR0_INT_ACK | OP0_NOP, LCCR(base));
- continue;
- }
-
/* If a transmission has been done */
if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE ||
(status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE ||
@@ -4081,10 +4126,6 @@ wavelan_interrupt(int irq,
dev->name);
#endif

- /* If watchdog was activated, kill it ! */
- if(timer_pending(&lp->watchdog))
- del_timer(&lp->watchdog);
-
/* Get transmission status */
tx_status = inb(LCSR(base));
tx_status |= (inb(LCSR(base)) << 8);
@@ -4174,7 +4215,7 @@ wavelan_interrupt(int irq,
lp->stats.collisions += (tx_status & TX_NCOL_MASK);
lp->stats.tx_packets++;

- netif_wake_queue (dev);
+ netif_wake_queue(dev);
outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */
}
else /* if interrupt = transmit done or retransmit done */
@@ -4185,9 +4226,9 @@ wavelan_interrupt(int irq,
#endif
outb(CR0_INT_ACK | OP0_NOP, LCCR(base)); /* Acknowledge the interrupt */
}
- }
+ } /* while(1) */

- spin_unlock_irq (&lp->lock);
+ spin_unlock(&lp->spinlock);

#ifdef DEBUG_INTERRUPT_TRACE
printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
@@ -4196,30 +4237,23 @@ wavelan_interrupt(int irq,

/*------------------------------------------------------------------*/
/*
- * Watchdog : when we start a transmission, we set a timer in the
- * kernel. If the transmission complete, this timer is disabled. If
- * it expire, it try to unlock the hardware.
+ * Watchdog: when we start a transmission, a timer is set for us in the
+ * kernel. If the transmission completes, this timer is disabled. If
+ * the timer expires, we are called and we try to unlock the hardware.
*
- * Note : this watchdog doesn't work on the same principle as the
- * watchdog in the ISA driver. I make it this way because the overhead
- * of add_timer() and del_timer() is nothing and that it avoid calling
- * the watchdog, saving some CPU... If you want to apply the same
- * watchdog to the ISA driver, you should be a bit carefull, because
- * of the many transmit buffers...
- * This watchdog is also move clever, it try to abort the current
- * command before reseting everything...
+ * Note : This watchdog is move clever than the one in the ISA driver,
+ * because it try to abort the current command before reseting
+ * everything...
+ * On the other hand, it's a bit simpler, because we don't have to
+ * deal with the multiple Tx buffers...
*/
static void
-wavelan_watchdog(u_long a)
+wavelan_watchdog(device * dev)
{
- device * dev;
- net_local * lp;
- ioaddr_t base;
- int spin;
-
- dev = (device *) a;
- base = dev->base_addr;
- lp = (net_local *) dev->priv;
+ net_local * lp = (net_local *) dev->priv;
+ ioaddr_t base = dev->base_addr;
+ unsigned long flags;
+ int aborted = FALSE;

#ifdef DEBUG_INTERRUPT_TRACE
printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
@@ -4230,21 +4264,21 @@ wavelan_watchdog(u_long a)
dev->name);
#endif

- /* We are waiting for command completion */
- wv_wait_completed = TRUE;
+ wv_splhi(lp, &flags);

/* Ask to abort the current command */
outb(OP0_ABORT, LCCR(base));

- /* Busy wait while the LAN controller executes the command.
- * Note : wv_wait_completed should be volatile */
- spin = 0;
- while(wv_wait_completed && (spin++ < 250))
- udelay(10);
-
- /* If the interrupt handler hasn't be called or invalid status */
- if((wv_wait_completed) ||
- ((lp->status & SR0_EVENT_MASK) != SR0_EXECUTION_ABORTED))
+ /* Wait for the end of the command (a bit hackish) */
+ if(wv_82593_cmd(dev, "wavelan_watchdog(): abort",
+ OP0_NOP | CR0_STATUS_3, SR0_EXECUTION_ABORTED))
+ aborted = TRUE;
+
+ /* Release spinlock here so that wv_hw_reset() can grab it */
+ wv_splx(lp, &flags);
+
+ /* Check if we were successful in aborting it */
+ if(!aborted)
{
/* It seem that it wasn't enough */
#ifdef DEBUG_INTERRUPT_ERROR
@@ -4269,7 +4303,7 @@ wavelan_watchdog(u_long a)
#endif

/* We are no more waiting for something... */
- netif_start_queue (dev);
+ netif_wake_queue(dev);

#ifdef DEBUG_INTERRUPT_TRACE
printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
@@ -4322,7 +4356,7 @@ wavelan_open(device * dev)
return FALSE;
if(!wv_ru_start(dev))
wv_hw_reset(dev); /* If problem : reset */
- netif_start_queue (dev);
+ netif_start_queue(dev);

/* Mark the device as used */
link->open++;
@@ -4348,7 +4382,6 @@ static int
wavelan_close(device * dev)
{
dev_link_t * link = ((net_local *) dev->priv)->link;
- net_local * lp = (net_local *)dev->priv;
ioaddr_t base = dev->base_addr;

#ifdef DEBUG_CALLBACK_TRACE
@@ -4356,8 +4389,6 @@ wavelan_close(device * dev)
(unsigned int) dev);
#endif

- netif_stop_queue (dev);
-
/* If the device isn't open, then nothing to do */
if(!link->open)
{
@@ -4373,17 +4404,13 @@ wavelan_close(device * dev)
wv_roam_cleanup(dev);
#endif /* WAVELAN_ROAMING */

- /* If watchdog was activated, kill it ! */
- if(timer_pending(&lp->watchdog))
- del_timer(&lp->watchdog);
-
link->open--;
MOD_DEC_USE_COUNT;

/* If the card is still present */
- if (netif_device_present(dev))
+ if(netif_running(dev))
{
- netif_stop_queue (dev);
+ netif_stop_queue(dev);

/* Stop receiving new messages and wait end of transmission */
wv_ru_stop(dev);
@@ -4404,21 +4431,6 @@ wavelan_close(device * dev)

/*------------------------------------------------------------------*/
/*
- * We never need to do anything when a wavelan device is "initialized"
- * by the net software, because we only register already-found cards.
- */
-static int
-wavelan_init(device * dev)
-{
-#ifdef DEBUG_CALLBACK_TRACE
- printk(KERN_DEBUG "<>wavelan_init()\n");
-#endif
-
- return(0);
-}
-
-/*------------------------------------------------------------------*/
-/*
* wavelan_attach() creates an "instance" of the driver, allocating
* local data structures for one device (one interface). The device
* is registered with Card Services.
@@ -4445,24 +4457,8 @@ wavelan_attach(void)

/* Initialize the dev_link_t structure */
link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
- if (!link)
- return NULL;
-
- /* Allocate the generic data structure */
- dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
- if (!dev)
- goto fail_alloc_dev;
-
- /* Allocate the wavelan-specific data structure. */
- lp = (net_local *) kmalloc(sizeof(net_local), GFP_KERNEL);
- if (!lp)
- goto fail_alloc_dev_priv;
-
- memset(lp, 0, sizeof(net_local));
+ if (!link) return NULL;
memset(link, 0, sizeof(struct dev_link_t));
- memset(dev, 0, sizeof(struct net_device));
-
- dev->priv = lp;

/* Unused for the Wavelan */
link->release.function = &wv_pcmcia_release;
@@ -4492,18 +4488,35 @@ wavelan_attach(void)
link->next = dev_list;
dev_list = link;

+ /* Allocate the generic data structure */
+ dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+ if (!dev) {
+ kfree(link);
+ return NULL;
+ }
+ memset(dev, 0x00, sizeof(struct net_device));
link->priv = link->irq.Instance = dev;

+ /* Allocate the wavelan-specific data structure. */
+ dev->priv = lp = (net_local *) kmalloc(sizeof(net_local), GFP_KERNEL);
+ if (!lp) {
+ kfree(link);
+ kfree(dev);
+ return NULL;
+ }
+ memset(lp, 0x00, sizeof(net_local));
+
/* Init specific data */
- wv_wait_completed = 0;
- lp->status = FALSE;
lp->configured = 0;
lp->reconfig_82593 = FALSE;
lp->nresets = 0;
+ /* Multicast stuff */
+ lp->promiscuous = 0;
+ lp->allmulticast = 0;
+ lp->mc_count = 0;

- /* Set the watchdog timer */
- lp->watchdog.function = wavelan_watchdog;
- lp->watchdog.data = (unsigned long) dev;
+ /* Init spinlock */
+ spin_lock_init(&lp->spinlock);

/* back links */
lp->link = link;
@@ -4513,7 +4526,6 @@ wavelan_attach(void)
ether_setup(dev);

/* wavelan NET3 callbacks */
- dev->init = &wavelan_init;
dev->open = &wavelan_open;
dev->stop = &wavelan_close;
dev->hard_start_xmit = &wavelan_packet_xmit;
@@ -4523,14 +4535,16 @@ wavelan_attach(void)
dev->set_mac_address = &wavelan_set_mac_address;
#endif /* SET_MAC_ADDRESS */

+ /* Set the watchdog timer */
+ dev->tx_timeout = &wavelan_watchdog;
+ dev->watchdog_timeo = WATCHDOG_JIFFIES;
+
#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */
dev->do_ioctl = wavelan_ioctl; /* wireless extensions */
dev->get_wireless_stats = wavelan_get_wireless_stats;
#endif

/* Other specific data */
- strcpy(dev->name, ((net_local *)dev->priv)->node.dev_name);
- netif_start_queue (dev);
dev->mtu = WAVELAN_MTU;

/* Register with Card Services */
@@ -4562,12 +4576,6 @@ wavelan_attach(void)
#endif

return link;
-
-fail_alloc_dev_priv:
- kfree(dev);
-fail_alloc_dev:
- kfree(link);
- return NULL;
}

/*------------------------------------------------------------------*/
@@ -4698,7 +4706,7 @@ wavelan_event(event_t event, /* The ev
if(link->state & DEV_CONFIG)
{
/* Accept no more transmissions */
- netif_device_detach(dev);
+ netif_device_detach(dev);

/* Release the card */
wv_pcmcia_release((u_long) link);
@@ -4720,7 +4728,7 @@ wavelan_event(event_t event, /* The ev
* obliged to close nicely the wavelan here. David, could you
* close the device before suspending them ? And, by the way,
* could you, on resume, add a "route add -net ..." after the
- * ifconfig up XXX Thanks... */
+ * ifconfig up ? Thanks... */

/* Stop receiving new messages and wait end of transmission */
wv_ru_stop(dev);
@@ -4735,8 +4743,7 @@ wavelan_event(event_t event, /* The ev
if(link->state & DEV_CONFIG)
{
if(link->open)
- netif_device_detach(dev);
-
+ netif_device_detach(dev);
CardServices(ReleaseConfiguration, link->handle);
}
break;
@@ -4748,7 +4755,7 @@ wavelan_event(event_t event, /* The ev
if(link->state & DEV_CONFIG)
{
CardServices(RequestConfiguration, link->handle, &link->conf);
- if(link->open) /* If RESET -> True, If RESUME -> False XXX */
+ if(link->open) /* If RESET -> True, If RESUME -> False ? */
{
wv_hw_reset(dev);
netif_device_attach(dev);
@@ -4838,4 +4845,5 @@ exit_wavelan_cs(void)

module_init(init_wavelan_cs);
module_exit(exit_wavelan_cs);
-MODULE_LICENSE("Dual BSD/GPL");
+
+/* Note : Modules parameters are in wavelan_cs.h - Jean II */
diff -u -p -r linux/drivers/net/pcmcia-buggy/wavelan_cs.h linux/drivers/net/pcmcia/wavelan_cs.h
--- linux/drivers/net/pcmcia-buggy/wavelan_cs.h Tue Aug 6 10:35:51 2002
+++ linux/drivers/net/pcmcia/wavelan_cs.h Tue Aug 6 10:37:05 2002
@@ -34,6 +34,25 @@
* I try to maintain a web page with the Wireless LAN Howto at :
* http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Wavelan.html
*
+ * SMP
+ * ---
+ * We now are SMP compliant (I eventually fixed the remaining bugs).
+ * The driver has been tested on a dual P6-150 and survived my usual
+ * set of torture tests.
+ * Anyway, I spent enough time chasing interrupt re-entrancy during
+ * errors or reconfigure, and I designed the locked/unlocked sections
+ * of the driver with great care, and with the recent addition of
+ * the spinlock (thanks to the new API), we should be quite close to
+ * the truth.
+ * The SMP/IRQ locking is quite coarse and conservative (i.e. not fast),
+ * but better safe than sorry (especially at 2 Mb/s ;-).
+ *
+ * I have also looked into disabling only our interrupt on the card
+ * (via HACR) instead of all interrupts in the processor (via cli),
+ * so that other driver are not impacted, and it look like it's
+ * possible, but it's very tricky to do right (full of races). As
+ * the gain would be mostly for SMP systems, it can wait...
+ *
* Debugging and options
* ---------------------
* You will find below a set of '#define" allowing a very fine control
@@ -122,7 +141,7 @@
* Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work.
* Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
* correctly 2.00 cards (2.4 GHz with frequency selection).
- * David Hinds <dhinds@pcmcia.sourceforge.org> integrated the whole in his
+ * David Hinds <dahinds@users.sourceforge.net> integrated the whole in his
* Pcmcia package (+ bug corrections).
*
* I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
@@ -158,8 +177,8 @@
*
* This software was originally developed under Linux 1.2.3
* (Slackware 2.0 distribution).
- * And then under Linux 2.0.x (Debian 1.1 - pcmcia 2.8.18-23) with
- * HP OmniBook 4000 & 5500.
+ * And then under Linux 2.0.x (Debian 1.1 -> 2.2 - pcmcia 2.8.18+)
+ * with an HP OmniBook 4000 and then a 5500.
*
* It is based on other device drivers and information either written
* or supplied by:
@@ -174,7 +193,7 @@
* Matthew Geier (matthew@cs.su.oz.au),
* Remo di Giovanni (remo@cs.su.oz.au),
* Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
- * David Hinds <dhinds@pcmcia.sourceforge.org>,
+ * David Hinds <dahinds@users.sourceforge.net>,
* Jan Hoogendoorn (c/o marteijn@lucent.com),
* Bruce Janson <bruce@cs.usyd.edu.au>,
* Anthony D. Joseph <adj@lcs.mit.edu>,
@@ -349,6 +368,37 @@
* - Fix check for root permission (break instead of exit)
* - New nwid & encoding setting (Wireless Extension 9)
*
+ * Changes made for release in 3.1.12 :
+ * ----------------------------------
+ * - reworked wv_82593_cmd to avoid using the IRQ handler and doing
+ * ugly things with interrupts.
+ * - Add IRQ protection in 82593_config/ru_start/ru_stop/watchdog
+ * - Update to new network API (softnet - 2.3.43) :
+ * o replace dev->tbusy (David + me)
+ * o replace dev->tstart (David + me)
+ * o remove dev->interrupt (David)
+ * o add SMP locking via spinlock in splxx (me)
+ * o add spinlock in interrupt handler (me)
+ * o use kernel watchdog instead of ours (me)
+ * o verify that all the changes make sense and work (me)
+ * - Re-sync kernel/pcmcia versions (not much actually)
+ * - A few other cleanups (David & me)...
+ *
+ * Changes made for release in 3.1.22 :
+ * ----------------------------------
+ * - Check that SMP works, remove annoying log message
+ *
+ * Changes made for release in 3.1.24 :
+ * ----------------------------------
+ * - Fix unfrequent card lockup when watchdog was reseting the hardware :
+ * o control first busy loop in wv_82593_cmd()
+ * o Extend spinlock protection in wv_hw_config()
+ *
+ * Changes made for release in 3.2.1 :
+ * ---------------------------------
+ * - Set dev->trans_start to avoid filling the logs
+ * (and generating useless abort commands)
+ *
* Wishes & dreams:
* ----------------
* - Cleanup and integrate the roaming code
@@ -368,6 +418,7 @@
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
+#include <linux/spinlock.h>
#include <linux/in.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
@@ -384,7 +435,7 @@

#ifdef CONFIG_NET_PCMCIA_RADIO
#include <linux/wireless.h> /* Wireless extensions */
-#endif /* CONFIG_NET_PCMCIA_RADIO */
+#endif

/* Pcmcia headers that we need */
#include <pcmcia/cs_types.h>
@@ -437,7 +488,7 @@
#undef DEBUG_RX_INFO /* Header of the transmitted packet */
#undef DEBUG_RX_FAIL /* Normal failure conditions */
#define DEBUG_RX_ERROR /* Unexpected conditions */
-#undef DEBUG_PACKET_DUMP /* Dump packet on the screen */
+#undef DEBUG_PACKET_DUMP 32 /* Dump packet on the screen */
#undef DEBUG_IOCTL_TRACE /* Misc call by Linux */
#undef DEBUG_IOCTL_INFO /* Various debug info */
#define DEBUG_IOCTL_ERROR /* What's going wrong */
@@ -452,7 +503,7 @@
/************************ CONSTANTS & MACROS ************************/

#ifdef DEBUG_VERSION_SHOW
-static const char *version = "wavelan_cs.c : v21 (wireless extensions) 18/10/99\n";
+static const char *version = "wavelan_cs.c : v23 (SMP + wireless extensions) 20/12/00\n";
#endif

/* Watchdog temporisation */
@@ -557,9 +608,9 @@ typedef u_char mac_addr[WAVELAN_ADDR_SI
*/
struct net_local
{
- spinlock_t lock;
dev_node_t node; /* ???? What is this stuff ???? */
device * dev; /* Reverse link... */
+ spinlock_t spinlock; /* Serialize access to the hardware (SMP) */
dev_link_t * link; /* pcmcia structure */
en_stats stats; /* Ethernet interface statistics */
int nresets; /* Number of hw resets */
@@ -568,9 +619,7 @@ struct net_local
u_char promiscuous; /* Promiscuous mode */
u_char allmulticast; /* All Multicast mode */
int mc_count; /* Number of multicast addresses */
- timer_list watchdog; /* To avoid blocking state */

- u_char status; /* Current i82593 status */
int stop; /* Current i82593 Stop Hit Register */
int rfp; /* Last DMA machine receive pointer */
int overrunning; /* Receiver overrun flag */
@@ -617,8 +666,14 @@ void wv_roam_cleanup(struct net_device *
#endif /* WAVELAN_ROAMING */

/* ----------------------- MISC SUBROUTINES ------------------------ */
+static inline void
+ wv_splhi(net_local *, /* Disable interrupts */
+ unsigned long *); /* flags */
+static inline void
+ wv_splx(net_local *, /* ReEnable interrupts */
+ unsigned long *); /* flags */
static void
- cs_error(client_handle_t, /* Report error to cardmgr */
+ cs_error(client_handle_t, /* Report error to cardmgr */
int,
int);
/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
@@ -722,16 +777,15 @@ static void
wv_flush_stale_links(void); /* "detach" all possible devices */
/* ---------------------- INTERRUPT HANDLING ---------------------- */
static void
-wavelan_interrupt(int, /* Interrupt handler */
- void *,
- struct pt_regs *);
+ wavelan_interrupt(int, /* Interrupt handler */
+ void *,
+ struct pt_regs *);
static void
- wavelan_watchdog(u_long); /* Transmission watchdog */
+ wavelan_watchdog(device *); /* Transmission watchdog */
/* ------------------- CONFIGURATION CALLBACKS ------------------- */
static int
wavelan_open(device *), /* Open the device */
- wavelan_close(device *), /* Close the device */
- wavelan_init(device *); /* Do nothing */
+ wavelan_close(device *); /* Close the device */
static dev_link_t *
wavelan_attach(void); /* Create a new device */
static void
@@ -744,11 +798,7 @@ static int
/**************************** VARIABLES ****************************/

static dev_info_t dev_info = "wavelan_cs";
-static dev_link_t *dev_list; /* Linked list of devices */
-
-/* WARNING : the following variable MUST be volatile
- * It is used by wv_82593_cmd to syncronise with wavelan_interrupt */
-static volatile int wv_wait_completed;
+static dev_link_t *dev_list = NULL; /* Linked list of devices */

/*
* Parameters that can be set with 'insmod'
@@ -761,7 +811,7 @@ static int irq_mask = 0xdeb8;
static int irq_list[4] = { -1 };

/* Shared memory speed, in ns */
-static int mem_speed;
+static int mem_speed = 0;

/* New module interface */
MODULE_PARM(irq_mask, "i");
@@ -770,9 +820,12 @@ MODULE_PARM(mem_speed, "i");

#ifdef WAVELAN_ROAMING /* Conditional compile, see above in options */
/* Enable roaming mode ? No ! Please keep this to 0 */
-static int do_roaming;
+static int do_roaming = 0;
MODULE_PARM(do_roaming, "i");
#endif /* WAVELAN_ROAMING */
+
+/* My modifications and rewrite were GPL only - Jean II */
+MODULE_LICENSE("GPL");

#endif /* WAVELAN_CS_H */

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/