📄 wavelan_cs.c
字号:
/* * Wavelan Pcmcia driver * * Jean II - HPLB '96 * * Reorganisation and extension of the driver. * Original copyright follow. See wavelan_cs.h for details. * * This code is derived from Anthony D. Joseph's code and all the changes here * are also under the original copyright below. * * This code supports version 2.00 of WaveLAN/PCMCIA cards (2.4GHz), and * can work on Linux 2.0.36 with support of David Hinds' PCMCIA Card Services * * Joe Finney (joe@comp.lancs.ac.uk) at Lancaster University in UK added * critical code in the routine to initialize the Modem Management Controller. * * Thanks to Alan Cox and Bruce Janson for their advice. * * -- Yunzhou Li (scip4166@nus.sg) *#ifdef WAVELAN_ROAMING * Roaming support added 07/22/98 by Justin Seger (jseger@media.mit.edu) * based on patch by Joe Finney from Lancaster University.#endif * * Lucent (formerly AT&T GIS, formerly NCR) WaveLAN PCMCIA card: An * Ethernet-like radio transceiver controlled by an Intel 82593 coprocessor. * * A non-shared memory PCMCIA ethernet driver for linux * * ISA version modified to support PCMCIA by Anthony Joseph (adj@lcs.mit.edu) * * * Joseph O'Sullivan & John Langford (josullvn@cs.cmu.edu & jcl@cs.cmu.edu) * * Apr 2 '98 made changes to bring the i82593 control/int handling in line * with offical specs... * **************************************************************************** * Copyright 1995 * Anthony D. Joseph * Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this program * for any purpose and without fee is hereby granted, provided * that this copyright and permission notice appear on all copies * and supporting documentation, the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * program without specific prior permission, and notice be given * in supporting documentation that copying and distribution is * by permission of M.I.T. M.I.T. makes no representations about * the suitability of this software for any purpose. It is pro- * vided "as is" without express or implied warranty. **************************************************************************** * */#include "wavelan_cs.h" /* Private header *//************************* MISC SUBROUTINES **************************//* * Subroutines which won't fit in one of the following category * (wavelan modem or i82593) *//*------------------------------------------------------------------*//* * Wrapper for disabling interrupts. * (note : inline, so optimised away) */static inline voidwv_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 voidwv_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){ error_info_t err = { func, ret }; CardServices(ReportError, handle, &err);}#ifdef STRUCT_CHECK/*------------------------------------------------------------------*//* * Sanity routine to verify the sizes of the various WaveLAN interface * structures. */static char *wv_structuct_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");#undef SC return((char *) NULL);} /* wv_structuct_check */#endif /* STRUCT_CHECK *//******************* MODEM MANAGEMENT SUBROUTINES *******************//* * Useful subroutines to manage the modem of the wavelan *//*------------------------------------------------------------------*//* * Read from card's Host Adaptor Status Register. */static inline u_charhasr_read(u_long base){ return(inb(HASR(base)));} /* hasr_read *//*------------------------------------------------------------------*//* * Write to card's Host Adapter Command Register. */static inline voidhacr_write(u_long base, u_char hacr){ outb(hacr, HACR(base));} /* hacr_write *//*------------------------------------------------------------------*//* * Write to card's Host Adapter Command Register. Include a delay for * those times when it is needed. */static inline voidhacr_write_slow(u_long base, u_char hacr){ hacr_write(base, hacr); /* delay might only be needed sometimes */ mdelay(1);} /* hacr_write_slow *//*------------------------------------------------------------------*//* * Read the Parameter Storage Area from the WaveLAN card's memory */static voidpsa_read(device * dev, int o, /* offset in PSA */ u_char * b, /* buffer to fill */ int n) /* size to read */{ u_char * ptr = ((u_char *)dev->mem_start) + PSA_ADDR + (o << 1); while(n-- > 0) { *b++ = readb(ptr); /* Due to a lack of address decode pins, the WaveLAN PCMCIA card * only supports reading even memory addresses. That means the * increment here MUST be two. * Because of that, we can't use memcpy_fromio()... */ ptr += 2; }} /* psa_read *//*------------------------------------------------------------------*//* * Write the Paramter Storage Area to the WaveLAN card's memory */static voidpsa_write(device * dev, int o, /* Offset in psa */ u_char * b, /* Buffer in memory */ int n) /* Length of buffer */{ u_char * ptr = ((u_char *) dev->mem_start) + PSA_ADDR + (o << 1); int count = 0; ioaddr_t base = dev->base_addr; /* As there seem to have no flag PSA_BUSY as in the ISA model, we are * oblige to verify this address to know when the PSA is ready... */ volatile u_char * verify = ((u_char *) dev->mem_start) + PSA_ADDR + (psaoff(0, psa_comp_number) << 1); /* Authorize writting to PSA */ hacr_write(base, HACR_PWR_STAT | HACR_ROM_WEN); while(n-- > 0) { /* write to PSA */ writeb(*b++, ptr); ptr += 2; /* I don't have the spec, so I don't know what the correct * sequence to write is. This hack seem to work for me... */ count = 0; while((readb(verify) != PSA_COMP_PCMCIA_915) && (count++ < 100)) mdelay(1); } /* Put the host interface back in standard state */ hacr_write(base, HACR_DEFAULT);} /* 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 u_shortpsa_crc(unsigned char * psa, /* The PSA */ int size) /* Number of short for CRC */{ int byte_cnt; /* Loop on the PSA */ u_short 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 voidupdate_psa_checksum(device * dev){#ifdef SET_PSA_CRC psa_t psa; u_short crc; /* read the parameter storage area */ psa_read(dev, 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(dev, (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 voidmmc_out(u_long base, u_short o, u_char d){ /* Wait for MMC to go idle */ while(inb(HASR(base)) & HASR_MMI_BUSY) ; outb((u_char)((o << 1) | MMR_MMI_WR), MMR(base)); outb(d, MMD(base));}/*------------------------------------------------------------------*//* * Routine to write bytes to the Modem Management Controller. * We start by the end because it is the way it should be ! */static inline voidmmc_write(u_long base, u_char o, u_char * b, int n){ o += n; b += n; while(n-- > 0 ) mmc_out(base, --o, *(--b));} /* mmc_write *//*------------------------------------------------------------------*//* * Read 1 byte from the MMC. * Optimised version for 1 byte, avoid using memory... */static inline u_charmmc_in(u_long base, u_short o){ while(inb(HASR(base)) & HASR_MMI_BUSY) ; outb(o << 1, MMR(base)); /* Set the read address */ outb(0, MMD(base)); /* Required dummy write */ while(inb(HASR(base)) & HASR_MMI_BUSY) ; return (u_char) (inb(MMD(base))); /* Now do the actual read */}/*------------------------------------------------------------------*//* * 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 by the end because it is the way it should be ! */static inline voidmmc_read(u_long base, u_char o, u_char * b, int n){ o += n; b += n; while(n-- > 0) *(--b) = mmc_in(base, --o);} /* mmc_read *//*------------------------------------------------------------------*//* * Get the type of encryption available... */static inline intmmc_encr(u_long base) /* i/o port of the card */{ int temp; temp = mmc_in(base, 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 voidfee_wait(u_long base, /* 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(base, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY)) udelay(delay);}/*------------------------------------------------------------------*//* * Read bytes from the Frequency EEprom (frequency select cards). */static voidfee_read(u_long base, /* i/o port of the card */ u_short o, /* destination offset */ u_short * b, /* data buffer */ int n) /* number of registers */{ b += n; /* Position at the end of the area */ /* Write the address */ mmc_out(base, mmwoff(0, mmw_fee_addr), o + n - 1); /* Loop on all buffer */ while(n-- > 0) { /* Write the read command */ mmc_out(base, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ); /* Wait until EEprom is ready (should be quick !) */ fee_wait(base, 10, 100); /* Read the value */ *--b = ((mmc_in(base, mmroff(0, mmr_fee_data_h)) << 8) | mmc_in(base, mmroff(0, mmr_fee_data_l))); }}#ifdef WIRELESS_EXT /* If wireless extension exist 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 voidfee_write(u_long base, /* i/o port of the card */ u_short o, /* destination offset */ u_short * b, /* data buffer */ int n) /* number of registers */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -