summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/wavelan.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/wireless/wavelan.c
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/net/wireless/wavelan.c')
-rw-r--r--drivers/net/wireless/wavelan.c4452
1 files changed, 4452 insertions, 0 deletions
diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c
new file mode 100644
index 000000000000..7a5e20a17890
--- /dev/null
+++ b/drivers/net/wireless/wavelan.c
@@ -0,0 +1,4452 @@
+/*
+ * WaveLAN ISA driver
+ *
+ * Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyright follows (also see the end of this file).
+ * See wavelan.p.h for details.
+ *
+ *
+ *
+ * AT&T GIS (nee NCR) WaveLAN card:
+ * An Ethernet-like radio transceiver
+ * controlled by an Intel 82586 coprocessor.
+ */
+
+#include "wavelan.p.h" /* Private header */
+
+/************************* MISC SUBROUTINES **************************/
+/*
+ * Subroutines which won't fit in one of the following category
+ * (WaveLAN modem or i82586)
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Translate irq number to PSA irq parameter
+ */
+static u8 wv_irq_to_psa(int irq)
+{
+ if (irq < 0 || irq >= NELS(irqvals))
+ return 0;
+
+ return irqvals[irq];
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Translate PSA irq parameter to irq number
+ */
+static int __init wv_psa_to_irq(u8 irqval)
+{
+ int irq;
+
+ for (irq = 0; irq < NELS(irqvals); irq++)
+ if (irqvals[irq] == irqval)
+ return irq;
+
+ return -1;
+}
+
+#ifdef STRUCT_CHECK
+/*------------------------------------------------------------------*/
+/*
+ * Sanity routine to verify the sizes of the various WaveLAN interface
+ * structures.
+ */
+static char *wv_struct_check(void)
+{
+#define SC(t,s,n) if (sizeof(t) != s) return(n);
+
+ SC(psa_t, PSA_SIZE, "psa_t");
+ SC(mmw_t, MMW_SIZE, "mmw_t");
+ SC(mmr_t, MMR_SIZE, "mmr_t");
+ SC(ha_t, HA_SIZE, "ha_t");
+
+#undef SC
+
+ return ((char *) NULL);
+} /* wv_struct_check */
+#endif /* STRUCT_CHECK */
+
+/********************* HOST ADAPTER SUBROUTINES *********************/
+/*
+ * Useful subroutines to manage the WaveLAN ISA interface
+ *
+ * One major difference with the PCMCIA hardware (except the port mapping)
+ * is that we have to keep the state of the Host Control Register
+ * because of the interrupt enable & bus size flags.
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read from card's Host Adaptor Status Register.
+ */
+static inline u16 hasr_read(unsigned long ioaddr)
+{
+ return (inw(HASR(ioaddr)));
+} /* hasr_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register.
+ */
+static inline void hacr_write(unsigned long ioaddr, u16 hacr)
+{
+ outw(hacr, HACR(ioaddr));
+} /* hacr_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register. Include a delay for
+ * those times when it is needed.
+ */
+static inline void hacr_write_slow(unsigned long ioaddr, u16 hacr)
+{
+ hacr_write(ioaddr, hacr);
+ /* delay might only be needed sometimes */
+ mdelay(1);
+} /* hacr_write_slow */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set the channel attention bit.
+ */
+static inline void set_chan_attn(unsigned long ioaddr, u16 hacr)
+{
+ hacr_write(ioaddr, hacr | HACR_CA);
+} /* set_chan_attn */
+
+/*------------------------------------------------------------------*/
+/*
+ * Reset, and then set host adaptor into default mode.
+ */
+static inline void wv_hacr_reset(unsigned long ioaddr)
+{
+ hacr_write_slow(ioaddr, HACR_RESET);
+ hacr_write(ioaddr, HACR_DEFAULT);
+} /* wv_hacr_reset */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set the I/O transfer over the ISA bus to 8-bit mode
+ */
+static inline void wv_16_off(unsigned long ioaddr, u16 hacr)
+{
+ hacr &= ~HACR_16BITS;
+ hacr_write(ioaddr, hacr);
+} /* wv_16_off */
+
+/*------------------------------------------------------------------*/
+/*
+ * Set the I/O transfer over the ISA bus to 8-bit mode
+ */
+static inline void wv_16_on(unsigned long ioaddr, u16 hacr)
+{
+ hacr |= HACR_16BITS;
+ hacr_write(ioaddr, hacr);
+} /* wv_16_on */
+
+/*------------------------------------------------------------------*/
+/*
+ * Disable interrupts on the WaveLAN hardware.
+ * (called by wv_82586_stop())
+ */
+static inline void wv_ints_off(struct net_device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+
+ lp->hacr &= ~HACR_INTRON;
+ hacr_write(ioaddr, lp->hacr);
+} /* wv_ints_off */
+
+/*------------------------------------------------------------------*/
+/*
+ * Enable interrupts on the WaveLAN hardware.
+ * (called by wv_hw_reset())
+ */
+static inline void wv_ints_on(struct net_device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+
+ lp->hacr |= HACR_INTRON;
+ hacr_write(ioaddr, lp->hacr);
+} /* wv_ints_on */
+
+/******************* MODEM MANAGEMENT SUBROUTINES *******************/
+/*
+ * Useful subroutines to manage the modem of the WaveLAN
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read the Parameter Storage Area from the WaveLAN card's memory
+ */
+/*
+ * Read bytes from the PSA.
+ */
+static void psa_read(unsigned long ioaddr, u16 hacr, int o, /* offset in PSA */
+ u8 * b, /* buffer to fill */
+ int n)
+{ /* size to read */
+ wv_16_off(ioaddr, hacr);
+
+ while (n-- > 0) {
+ outw(o, PIOR2(ioaddr));
+ o++;
+ *b++ = inb(PIOP2(ioaddr));
+ }
+
+ wv_16_on(ioaddr, hacr);
+} /* psa_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write the Parameter Storage Area to the WaveLAN card's memory.
+ */
+static void psa_write(unsigned long ioaddr, u16 hacr, int o, /* Offset in PSA */
+ u8 * b, /* Buffer in memory */
+ int n)
+{ /* Length of buffer */
+ int count = 0;
+
+ wv_16_off(ioaddr, hacr);
+
+ while (n-- > 0) {
+ outw(o, PIOR2(ioaddr));
+ o++;
+
+ outb(*b, PIOP2(ioaddr));
+ b++;
+
+ /* Wait for the memory to finish its write cycle */
+ count = 0;
+ while ((count++ < 100) &&
+ (hasr_read(ioaddr) & HASR_PSA_BUSY)) mdelay(1);
+ }
+
+ wv_16_on(ioaddr, hacr);
+} /* psa_write */
+
+#ifdef SET_PSA_CRC
+/*------------------------------------------------------------------*/
+/*
+ * Calculate the PSA CRC
+ * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
+ * NOTE: By specifying a length including the CRC position the
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
+ *
+ * The Windows drivers don't use the CRC, but the AP and the PtP tool
+ * depend on it.
+ */
+static inline u16 psa_crc(u8 * psa, /* The PSA */
+ int size)
+{ /* Number of short for CRC */
+ int byte_cnt; /* Loop on the PSA */
+ u16 crc_bytes = 0; /* Data in the PSA */
+ int bit_cnt; /* Loop on the bits of the short */
+
+ for (byte_cnt = 0; byte_cnt < size; byte_cnt++) {
+ crc_bytes ^= psa[byte_cnt]; /* Its an xor */
+
+ for (bit_cnt = 1; bit_cnt < 9; bit_cnt++) {
+ if (crc_bytes & 0x0001)
+ crc_bytes = (crc_bytes >> 1) ^ 0xA001;
+ else
+ crc_bytes >>= 1;
+ }
+ }
+
+ return crc_bytes;
+} /* psa_crc */
+#endif /* SET_PSA_CRC */
+
+/*------------------------------------------------------------------*/
+/*
+ * update the checksum field in the Wavelan's PSA
+ */
+static void update_psa_checksum(struct net_device * dev, unsigned long ioaddr, u16 hacr)
+{
+#ifdef SET_PSA_CRC
+ psa_t psa;
+ u16 crc;
+
+ /* read the parameter storage area */
+ psa_read(ioaddr, hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+ /* update the checksum */
+ crc = psa_crc((unsigned char *) &psa,
+ sizeof(psa) - sizeof(psa.psa_crc[0]) -
+ sizeof(psa.psa_crc[1])
+ - sizeof(psa.psa_crc_status));
+
+ psa.psa_crc[0] = crc & 0xFF;
+ psa.psa_crc[1] = (crc & 0xFF00) >> 8;
+
+ /* Write it ! */
+ psa_write(ioaddr, hacr, (char *) &psa.psa_crc - (char *) &psa,
+ (unsigned char *) &psa.psa_crc, 2);
+
+#ifdef DEBUG_IOCTL_INFO
+ printk(KERN_DEBUG "%s: update_psa_checksum(): crc = 0x%02x%02x\n",
+ dev->name, psa.psa_crc[0], psa.psa_crc[1]);
+
+ /* Check again (luxury !) */
+ crc = psa_crc((unsigned char *) &psa,
+ sizeof(psa) - sizeof(psa.psa_crc_status));
+
+ if (crc != 0)
+ printk(KERN_WARNING
+ "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n",
+ dev->name);
+#endif /* DEBUG_IOCTL_INFO */
+#endif /* SET_PSA_CRC */
+} /* update_psa_checksum */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write 1 byte to the MMC.
+ */
+static inline void mmc_out(unsigned long ioaddr, u16 o, u8 d)
+{
+ int count = 0;
+
+ /* Wait for MMC to go idle */
+ while ((count++ < 100) && (inw(HASR(ioaddr)) & HASR_MMC_BUSY))
+ udelay(10);
+
+ outw((u16) (((u16) d << 8) | (o << 1) | 1), MMCR(ioaddr));
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to write bytes to the Modem Management Controller.
+ * We start at the end because it is the way it should be!
+ */
+static inline void mmc_write(unsigned long ioaddr, u8 o, u8 * b, int n)
+{
+ o += n;
+ b += n;
+
+ while (n-- > 0)
+ mmc_out(ioaddr, --o, *(--b));
+} /* mmc_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read a byte from the MMC.
+ * Optimised version for 1 byte, avoid using memory.
+ */
+static inline u8 mmc_in(unsigned long ioaddr, u16 o)
+{
+ int count = 0;
+
+ while ((count++ < 100) && (inw(HASR(ioaddr)) & HASR_MMC_BUSY))
+ udelay(10);
+ outw(o << 1, MMCR(ioaddr));
+
+ while ((count++ < 100) && (inw(HASR(ioaddr)) & HASR_MMC_BUSY))
+ udelay(10);
+ return (u8) (inw(MMCR(ioaddr)) >> 8);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read bytes from the Modem Management Controller.
+ * The implementation is complicated by a lack of address lines,
+ * which prevents decoding of the low-order bit.
+ * (code has just been moved in the above function)
+ * We start at the end because it is the way it should be!
+ */
+static inline void mmc_read(unsigned long ioaddr, u8 o, u8 * b, int n)
+{
+ o += n;
+ b += n;
+
+ while (n-- > 0)
+ *(--b) = mmc_in(ioaddr, --o);
+} /* mmc_read */
+
+/*------------------------------------------------------------------*/
+/*
+ * Get the type of encryption available.
+ */
+static inline int mmc_encr(unsigned long ioaddr)
+{ /* I/O port of the card */
+ int temp;
+
+ temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail));
+ if ((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
+ return 0;
+ else
+ return temp;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wait for the frequency EEPROM to complete a command.
+ * I hope this one will be optimally inlined.
+ */
+static inline void fee_wait(unsigned long ioaddr, /* I/O port of the card */
+ int delay, /* Base delay to wait for */
+ int number)
+{ /* Number of time to wait */
+ int count = 0; /* Wait only a limited time */
+
+ while ((count++ < number) &&
+ (mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+ MMR_FEE_STATUS_BUSY)) udelay(delay);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the Frequency EEPROM (frequency select cards).
+ */
+static void fee_read(unsigned long ioaddr, /* I/O port of the card */
+ u16 o, /* destination offset */
+ u16 * b, /* data buffer */
+ int n)
+{ /* number of registers */
+ b += n; /* Position at the end of the area */
+
+ /* Write the address */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+ /* Loop on all buffer */
+ while (n-- > 0) {
+ /* Write the read command */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+ MMW_FEE_CTRL_READ);
+
+ /* Wait until EEPROM is ready (should be quick). */
+ fee_wait(ioaddr, 10, 100);
+
+ /* Read the value. */
+ *--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) |
+ mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
+ }
+}
+
+#ifdef WIRELESS_EXT /* if the wireless extension exists in the kernel */
+
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes from the Frequency EEPROM (frequency select cards).
+ * This is a bit complicated, because the frequency EEPROM has to
+ * be unprotected and the write enabled.
+ * Jean II
+ */
+static void fee_write(unsigned long ioaddr, /* I/O port of the card */
+ u16 o, /* destination offset */
+ u16 * b, /* data buffer */
+ int n)
+{ /* number of registers */
+ b += n; /* Position at the end of the area. */
+
+#ifdef EEPROM_IS_PROTECTED /* disabled */
+#ifdef DOESNT_SEEM_TO_WORK /* disabled */
+ /* Ask to read the protected register */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
+
+ fee_wait(ioaddr, 10, 100);
+
+ /* Read the protected register. */
+ printk("Protected 2: %02X-%02X\n",
+ mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)),
+ mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
+#endif /* DOESNT_SEEM_TO_WORK */
+
+ /* Enable protected register. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
+
+ fee_wait(ioaddr, 10, 100);
+
+ /* Unprotect area. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+#ifdef DOESNT_SEEM_TO_WORK /* disabled */
+ /* or use: */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
+#endif /* DOESNT_SEEM_TO_WORK */
+
+ fee_wait(ioaddr, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
+
+ /* Write enable. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
+
+ fee_wait(ioaddr, 10, 100);
+
+ /* Write the EEPROM address. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
+
+ /* Loop on all buffer */
+ while (n-- > 0) {
+ /* Write the value. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
+
+ /* Write the write command. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+ MMW_FEE_CTRL_WRITE);
+
+ /* WaveLAN documentation says to wait at least 10 ms for EEBUSY = 0 */
+ mdelay(10);
+ fee_wait(ioaddr, 10, 100);
+ }
+
+ /* Write disable. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
+
+ fee_wait(ioaddr, 10, 100);
+
+#ifdef EEPROM_IS_PROTECTED /* disabled */
+ /* Reprotect EEPROM. */
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00);
+ mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+
+ fee_wait(ioaddr, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
+}
+#endif /* WIRELESS_EXT */
+
+/************************ I82586 SUBROUTINES *************************/
+/*
+ * Useful subroutines to manage the Ethernet controller
+ */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the on-board RAM.
+ * Why does inlining this function make it fail?
+ */
+static /*inline */ void obram_read(unsigned long ioaddr,
+ u16 o, u8 * b, int n)
+{
+ outw(o, PIOR1(ioaddr));
+ insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes to the on-board RAM.
+ */
+static inline void obram_write(unsigned long ioaddr, u16 o, u8 * b, int n)
+{
+ outw(o, PIOR1(ioaddr));
+ outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Acknowledge the reading of the status issued by the i82586.
+ */
+static void wv_ack(struct net_device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ u16 scb_cs;
+ int i;
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+ scb_cs &= SCB_ST_INT;
+
+ if (scb_cs == 0)
+ return;
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--) {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(10);
+ }
+ udelay(100);
+
+#ifdef DEBUG_CONFIG_ERROR
+ if (i <= 0)
+ printk(KERN_INFO
+ "%s: wv_ack(): board not accepting command.\n",
+ dev->name);
+#endif
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Set channel attention bit and busy wait until command has
+ * completed, then acknowledge completion of the command.
+ */
+static inline int wv_synchronous_cmd(struct net_device * dev, const char *str)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long ioaddr = dev->base_addr;
+ u16 scb_cmd;
+ ach_t cb;
+ int i;
+
+ scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+ (unsigned char *) &scb_cmd, sizeof(scb_cmd));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--) {
+ obram_read(ioaddr, OFFSET_CU, (unsigned char *) &cb,
+ sizeof(cb));
+ if (cb.ac_status & AC_SFLD_C)
+ break;
+
+ udelay(10);
+ }
+ udelay(100);
+
+ if (i <= 0 || !(cb.ac_status & AC_SFLD_OK)) {
+#ifdef DEBUG_CONFIG_ERROR
+ printk(KERN_INFO "%s: %s failed; status = 0x%x\n",
+ dev->name, str, cb.ac_status);
+#endif
+#ifdef DEBUG_I82586_SHOW
+ wv_scb_show(ioaddr);
+#endif
+ return -1;
+ }
+
+ /* Ack the status */
+ wv_ack(dev);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Configuration commands completion interrupt.
+ * Check if done, and if OK.
+ */
+static inline int
+wv_config_complete(struct net_device * dev, unsigned long ioaddr, net_local * lp)
+{
+ unsigned short mcs_addr;
+ unsigned short status;
+ int ret;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name);
+#endif
+
+ mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t)
+ + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t);
+
+ /* Read the status of the last command (set mc list). */
+ obram_read(ioaddr, acoff(mcs_addr, ac_status),
+ (unsigned char *) &status, sizeof(status));
+
+ /* If not completed -> exit */
+ if ((status & AC_SFLD_C) == 0)
+ ret = 0; /* Not ready to be scrapped */
+ else {
+#ifdef DEBUG_CONFIG_ERROR
+ unsigned short cfg_addr;
+ unsigned short ias_addr;
+
+ /* Check mc_config command */
+ if ((status & AC_SFLD_OK) != AC_SFLD_OK)
+ printk(KERN_INFO
+ "%s: wv_config_complete(): set_multicast_address failed; status = 0x%x\n",
+ dev->name, status);
+
+ /* check ia-config command */
+ ias_addr = mcs_addr - sizeof(ac_ias_t);
+ obram_read(ioaddr, acoff(ias_addr, ac_status),
+ (unsigned char *) &status, sizeof(status));
+ if ((status & AC_SFLD_OK) != AC_SFLD_OK)
+ printk(KERN_INFO
+ "%s: wv_config_complete(): set_MAC_address failed; status = 0x%x\n",
+ dev->name, status);
+
+ /* Check config command. */
+ cfg_addr = ias_addr - sizeof(ac_cfg_t);
+ obram_read(ioaddr, acoff(cfg_addr, ac_status),
+ (unsigned char *) &status, sizeof(status));
+ if ((status & AC_SFLD_OK) != AC_SFLD_OK)
+ printk(KERN_INFO
+ "%s: wv_config_complete(): configure failed; status = 0x%x\n",
+ dev->name, status);
+#endif /* DEBUG_CONFIG_ERROR */
+
+ ret = 1; /* Ready to be scrapped */
+ }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name,
+ ret);
+#endif
+ return ret;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Command completion interrupt.
+ * Reclaim as many freed tx buffers as we can.
+ * (called in wavelan_interrupt()).
+ * Note : the spinlock is already grabbed for us.
+ */
+static int wv_complete(struct net_device * dev, unsigned long ioaddr, net_local * lp)
+{
+ int nreaped = 0;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name);
+#endif
+
+ /* Loop on all the transmit buffers */
+ while (lp->tx_first_in_use != I82586NULL) {
+ unsigned short tx_status;
+
+ /* Read the first transmit buffer */
+ obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status),
+ (unsigned char *) &tx_status,
+ sizeof(tx_status));
+
+ /* If not completed -> exit */
+ if ((tx_status & AC_SFLD_C) == 0)
+ break;
+
+ /* Hack for reconfiguration */
+ if (tx_status == 0xFFFF)
+ if (!wv_config_complete(dev, ioaddr, lp))
+ break; /* Not completed */
+
+ /* We now remove this buffer */
+ nreaped++;
+ --lp->tx_n_in_use;
+
+/*
+if (lp->tx_n_in_use > 0)
+ printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
+
+ /* Was it the last one? */
+ if (lp->tx_n_in_use <= 0)
+ lp->tx_first_in_use = I82586NULL;
+ else {
+ /* Next one in the chain */
+ lp->tx_first_in_use += TXBLOCKZ;
+ if (lp->tx_first_in_use >=
+ OFFSET_CU +
+ NTXBLOCKS * TXBLOCKZ) lp->tx_first_in_use -=
+ NTXBLOCKS * TXBLOCKZ;
+ }
+
+ /* Hack for reconfiguration */
+ if (tx_status == 0xFFFF)
+ continue;
+
+ /* Now, check status of the finished command */
+ if (tx_status & AC_SFLD_OK) {
+ int ncollisions;
+
+ lp->stats.tx_packets++;
+ ncollisions = tx_status & AC_SFLD_MAXCOL;
+ lp->stats.collisions += ncollisions;
+#ifdef DEBUG_TX_INFO
+ if (ncollisions > 0)
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx completed after %d collisions.\n",
+ dev->name, ncollisions);
+#endif
+ } else {
+ lp->stats.tx_errors++;
+ if (tx_status & AC_SFLD_S10) {
+ lp->stats.tx_carrier_errors++;
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx error: no CS.\n",
+ dev->name);
+#endif
+ }
+ if (tx_status & AC_SFLD_S9) {
+ lp->stats.tx_carrier_errors++;
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx error: lost CTS.\n",
+ dev->name);
+#endif
+ }
+ if (tx_status & AC_SFLD_S8) {
+ lp->stats.tx_fifo_errors++;
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx error: slow DMA.\n",
+ dev->name);
+#endif
+ }
+ if (tx_status & AC_SFLD_S6) {
+ lp->stats.tx_heartbeat_errors++;
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx error: heart beat.\n",
+ dev->name);
+#endif
+ }
+ if (tx_status & AC_SFLD_S5) {
+ lp->stats.tx_aborted_errors++;
+#ifdef DEBUG_TX_FAIL
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx error: too many collisions.\n",
+ dev->name);
+#endif
+ }
+ }
+
+#ifdef DEBUG_TX_INFO
+ printk(KERN_DEBUG
+ "%s: wv_complete(): tx completed, tx_status 0x%04x\n",
+ dev->name, tx_status);
+#endif
+ }
+
+#ifdef DEBUG_INTERRUPT_INFO
+ if (nreaped > 1)
+ printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n",
+ dev->name, nreaped);
+#endif
+
+ /*
+ * Inform upper layers.
+ */
+ if (lp->tx_n_in_use < NTXBLOCKS - 1) {
+ netif_wake_queue(dev);
+ }
+#ifdef DEBUG_INTERRUPT_TRACE
+ printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name);
+#endif
+ return nreaped;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Reconfigure the i82586, or at least ask for it.
+ * Because wv_82586_config uses a transmission buffer, we must do it
+ * when we are sure that there is one left, so we do it now
+ * or in wavelan_packet_xmit() (I can't find any better place,
+ * wavelan_interrupt is not an option), so you may experience
+ * delays sometimes.
+ */
+static inline void wv_82586_reconfig(struct net_device * dev)
+{
+ net_local *lp = (net_local *) dev->priv;
+ unsigned long flags;
+
+ /* Arm the flag, will be cleard in wv_82586_config() */
+ lp->reconfig_82586 = 1;
+
+ /* Check if we can do it now ! */
+ if((netif_running(dev)) && !(netif_queue_stopped(dev))) {
+ spin_lock_irqsave(&lp->spinlock, flags);
+ /* May fail */
+ wv_82586_config(dev);
+ spin_unlock_irqrestore(&lp->spinlock, flags);
+ }
+ else {
+#ifdef DEBUG_CONFIG_INFO
+ printk(KERN_DEBUG
+ "%s: wv_82586_reconfig(): delayed (state = %lX)\n",
+ dev->name, dev->state);
+#endif
+ }
+}
+
+/********************* DEBUG & INFO SUBROUTINES *********************/
+/*
+ * This routine is used in the code to show information for debugging.
+ * Most of the time, it dumps the contents of hardware structures.
+ */
+
+#ifdef DEBUG_PSA_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted contents of the Parameter Storage Area.
+ */
+static void wv_psa_show(psa_t * p)
+{
+ printk(KERN_DEBUG "##### WaveLAN PSA contents: #####\n");
+ printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
+ p->psa_io_base_addr_1,
+ p->psa_io_base_addr_2,
+ p->psa_io_base_addr_3, p->psa_io_base_addr_4);
+ printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
+ p->psa_rem_boot_addr_1,
+ p->psa_rem_boot_addr_2, p->psa_rem_boot_addr_3);
+ printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
+ printk("psa_int_req_no: %d\n", p->psa_int_req_no);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG
+ "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ p->psa_unused0[0], p->psa_unused0[1], p->psa_unused0[2],
+ p->psa_unused0[3], p->psa_unused0[4], p->psa_unused0[5],
+ p->psa_unused0[6]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG
+ "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ p->psa_univ_mac_addr[0], p->psa_univ_mac_addr[1],
+ p->psa_univ_mac_addr[2], p->psa_univ_mac_addr[3],
+ p->psa_univ_mac_addr[4], p->psa_univ_mac_addr[5]);
+ printk(KERN_DEBUG
+ "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ p->psa_local_mac_addr[0], p->psa_local_mac_addr[1],
+ p->psa_local_mac_addr[2], p->psa_local_mac_addr[3],
+ p->psa_local_mac_addr[4], p->psa_local_mac_addr[5]);
+ printk(KERN_DEBUG "psa_univ_local_sel: %d, ",
+ p->psa_univ_local_sel);
+ printk("psa_comp_number: %d, ", p->psa_comp_number);
+ printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
+ printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
+ p->psa_feature_select);
+ printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
+ printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
+ printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
+ printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0],
+ p->psa_nwid[1]);
+ printk("psa_nwid_select: %d\n", p->psa_nwid_select);
+ printk(KERN_DEBUG "psa_encryption_select: %d, ",
+ p->psa_encryption_select);
+ printk
+ ("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ p->psa_encryption_key[0], p->psa_encryption_key[1],
+ p->psa_encryption_key[2], p->psa_encryption_key[3],
+ p->psa_encryption_key[4], p->psa_encryption_key[5],
+ p->psa_encryption_key[6], p->psa_encryption_key[7]);
+ printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
+ printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
+ p->psa_call_code[0]);
+ printk
+ ("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ p->psa_call_code[0], p->psa_call_code[1], p->psa_call_code[2],
+ p->psa_call_code[3], p->psa_call_code[4], p->psa_call_code[5],
+ p->psa_call_code[6], p->psa_call_code[7]);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
+ p->psa_reserved[0],
+ p->psa_reserved[1], p->psa_reserved[2], p->psa_reserved[3]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
+ printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
+ printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
+} /* wv_psa_show */
+#endif /* DEBUG_PSA_SHOW */
+
+#ifdef DEBUG_MMC_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the Modem Management Controller.
+ * This function needs to be completed.
+ */
+static void wv_mmc_show(struct net_device * dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ net_local *lp = (net_local *) dev->priv;
+ mmr_t m;
+
+ /* Basic check */
+ if (hasr_read(ioaddr) & HASR_NO_CLK) {
+ printk(KERN_WARNING
+ "%s: wv_mmc_show: modem not connected\n",
+ dev->name);
+ return;
+ }
+
+ /* Read the mmc */
+ mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+ mmc_read(ioaddr, 0, (u8 *) & m, sizeof(m));
+ mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+#ifdef WIRELESS_EXT /* if wireless extension exists in the kernel */
+ /* Don't forget to update statistics */
+ lp->wstats.discard.nwid +=
+ (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+#endif /* WIRELESS_EXT */
+
+ 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",
+ m.mmr_unused0[0], m.mmr_unused0[1], m.mmr_unused0[2],
+ m.mmr_unused0[3], m.mmr_unused0[4], m.mmr_unused0[5],
+ m.mmr_unused0[6], m.mmr_unused0[7]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n",
+ m.mmr_des_avail, m.mmr_des_status);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
+ m.mmr_unused1[0],
+ m.mmr_unused1[1],
+ m.mmr_unused1[2], m.mmr_unused1[3], m.mmr_unused1[4]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
+ m.mmr_dce_status,
+ (m.
+ mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ?
+ "energy detected," : "",
+ (m.
+ mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
+ "loop test indicated," : "",
+ (m.
+ mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ?
+ "transmitter on," : "",
+ (m.
+ mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
+ "jabber timer expired," : "");
+ printk(KERN_DEBUG "Dsp ID: %02X\n", m.mmr_dsp_id);
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
+ m.mmr_unused2[0], m.mmr_unused2[1]);
+#endif /* DEBUG_SHOW_UNUSED */
+ printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
+ (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
+ (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+ printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
+ m.mmr_thr_pre_set & MMR_THR_PRE_SET,
+ (m.
+ mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" :
+ "below");
+ printk(KERN_DEBUG "signal_lvl: %d [%s], ",
+ m.mmr_signal_lvl & MMR_SIGNAL_LVL,
+ (m.
+ mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" :
+ "no new msg");
+ printk("silence_lvl: %d [%s], ",
+ m.mmr_silence_lvl & MMR_SILENCE_LVL,
+ (m.
+ mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" :
+ "no new update");
+ printk("sgnl_qual: 0x%x [%s]\n", m.mmr_sgnl_qual & MMR_SGNL_QUAL,
+ (m.
+ mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" :
+ "Antenna 0");
+#ifdef DEBUG_SHOW_UNUSED
+ printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
+#endif /* DEBUG_SHOW_UNUSED */
+} /* wv_mmc_show */
+#endif /* DEBUG_MMC_SHOW */
+
+#ifdef DEBUG_I82586_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the last block of the i82586 memory.
+ */
+static void wv_scb_show(unsigned long ioaddr)
+{
+ scb_t scb;
+
+ obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb,
+ sizeof(scb));
+
+ printk(KERN_DEBUG "##### WaveLAN system control block: #####\n");
+
+ printk(KERN_DEBUG "status: ");
+ printk("stat 0x%x[%s%s%s%s] ",
+ (scb.
+ scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA |
+ SCB_ST_RNR)) >> 12,
+ (scb.
+ scb_status & SCB_ST_CX) ? "command completion interrupt," :
+ "", (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
+ (scb.
+ scb_status & SCB_ST_CNA) ? "command unit not active," : "",
+ (scb.
+ scb_status & SCB_ST_RNR) ? "receiving unit not ready," :
+ "");
+ printk("cus 0x%x[%s%s%s] ", (scb.scb_status & SCB_ST_CUS) >> 8,
+ ((scb.scb_status & SCB_ST_CUS) ==
+ SCB_ST_CUS_IDLE) ? "idle" : "",
+ ((scb.scb_status & SCB_ST_CUS) ==
+ SCB_ST_CUS_SUSP) ? "suspended" : "",
+ ((scb.scb_status & SCB_ST_CUS) ==
+ SCB_ST_CUS_ACTV) ? "active" : "");
+ printk("rus 0x%x[%s%s%s%s]\n", (scb.scb_status & SCB_ST_RUS) >> 4,
+ ((scb.scb_status & SCB_ST_RUS) ==
+ SCB_ST_RUS_IDLE) ? "idle" : "",
+ ((scb.scb_status & SCB_ST_RUS) ==
+ SCB_ST_RUS_SUSP) ? "suspended" : "",
+ ((scb.scb_status & SCB_ST_RUS) ==
+ SCB_ST_RUS_NRES) ? "no resources" : "",
+ ((scb.scb_status & SCB_ST_RUS) ==
+ SCB_ST_RUS_RDY) ? "ready" : "");
+
+ printk(KERN_DEBUG "command: ");
+ printk("ack 0x%x[%s%s%s%s] ",
+ (scb.
+ scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR |
+ SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
+ (scb.
+ scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
+ (scb.
+ scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
+ (scb.
+