diff -urN -X /home/jgarzik/dontdiff /home/jgarzik/tmp/linux-2.4.11-pre6/Documentation/Configure.help linux_2_4/Documentation/Configure.help --- /home/jgarzik/tmp/linux-2.4.11-pre6/Documentation/Configure.help Tue Oct 9 00:03:40 2001 +++ linux_2_4/Documentation/Configure.help Tue Oct 9 03:39:02 2001 @@ -7637,6 +7637,53 @@ location). You also want to check out the PCMCIA-HOWTO, available from http://www.linuxdoc.org/docs.html#howto . +Hermes chipset 802.11b support (Orinoco/Prism2/Symbol cards) +CONFIG_HERMES + A driver for 802.11b wireless cards based based on the "Hermes" or + Intersil HFA384x (Prism 2) MAC controller. This includes the vast + majority of the PCMCIA 802.11b cards (which are nearly all rebadges) + - except for the Cisco/Aironet cards. Cards supported include the + Apple Airport (not a PCMCIA card), WavelanIEEE/Orinoco, + Cabletron/EnteraSys Roamabout, ELSA AirLancer, MELCO Buffalo, Avaya, + IBM High Rate Wireless, Farralon Syyline, Samsung MagicLAN, Netgear + MA401, LinkSys WPC-11, D-Link DWL-650, 3Com AirConnect, Intel + PRO/Wireless, and Symbol Spectrum24 High Rate amongst others. + + This option includes the guts of the driver, but in order to + actually use a card you will also need to enable support for PCMCIA + Hermes cards, PLX9052 based PCI adaptors or the Apple Airport below. + + You will also very likely also need the Wireless Tools in order to + configure your card and that /etc/pcmcia/wireless.opts works : + http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html + +Apple Airport support (built-in) +CONFIG_APPLE_AIRPORT + Enable support for the Apple Airport card (which is essentially a + Lucent Orinoco card with a non-standard interface) built into some + newer Apple Macintosh machines. + +Hermes 802.11b in PLX9052 based PCI adaptor support +CONFIG_PLX_HERMES + Enable support for PCMCIA cards supported by the "Hermes" (aka + orinoco_cs) driver when used in PLX9052 based PCI adaptors. These + adaptors are not a full PCMCIA controller but act as a more limited + PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that + 802.11b PCMCIA cards can be used in desktop machines. The Netgear + MA301 is such an adaptor. + + Support for these adaptors is so far still incomplete and buggy. + You have been warned. + +Hermes PCMCIA card support +CONFIG_PCMCIA_HERMES + Enable support for PCMCIA 802.11b cards using the Hermes or Intersil + HFA384x (Prism 2) chipset. To use your PC-cards, you will need + supporting software from David Hinds' pcmcia-cs package (see the + file Documentation/Changes for location). You also want to check out + the PCMCIA-HOWTO, available from + http://www.linuxdoc.org/docs.html#howto . + Hermes support (Orinoco/WavelanIEEE/PrismII/Symbol 802.11b cards) CONFIG_PCMCIA_HERMES A driver for "Hermes" chipset based PCMCIA wireless adaptors, such diff -urN -X /home/jgarzik/dontdiff /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/Config.in linux_2_4/drivers/net/wireless/Config.in --- /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/Config.in Wed Jun 20 14:13:18 2001 +++ linux_2_4/drivers/net/wireless/Config.in Tue Oct 9 03:42:46 2001 @@ -6,15 +6,21 @@ tristate ' Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards' CONFIG_AIRO fi +tristate ' Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)' CONFIG_HERMES + if [ "$CONFIG_ALL_PPC" = "y" ]; then - tristate ' Apple Airport support (built-in)' CONFIG_APPLE_AIRPORT + dep_tristate ' Apple Airport support (built-in)' CONFIG_APPLE_AIRPORT $CONFIG_HERMES +fi + +if [ "$CONFIG_PCI" = "y" ]; then + dep_tristate ' Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)' CONFIG_PLX_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL fi # If Pcmcia is compiled in, offer Pcmcia cards... if [ "$CONFIG_PCMCIA" != "n" ]; then comment 'Wireless Pcmcia cards support' - tristate ' Hermes support (Orinoco/WavelanIEEE/PrismII/Symbol 802.11b cards)' CONFIG_PCMCIA_HERMES + dep_tristate ' Hermes PCMCIA card support' CONFIG_PCMCIA_HERMES $CONFIG_HERMES tristate ' Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards' CONFIG_AIRO_CS fi diff -urN -X /home/jgarzik/dontdiff /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/Makefile linux_2_4/drivers/net/wireless/Makefile --- /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/Makefile Wed Jun 20 14:13:18 2001 +++ linux_2_4/drivers/net/wireless/Makefile Tue Oct 9 03:39:02 2001 @@ -14,8 +14,10 @@ # Things that need to export symbols export-objs := airo.o orinoco.o hermes.o -obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o orinoco.o hermes.o -obj-$(CONFIG_APPLE_AIRPORT) += airport.o orinoco.o hermes.o +obj-$(CONFIG_HERMES) += orinoco.o hermes.o +obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o +obj-$(CONFIG_APPLE_AIRPORT) += airport.o +obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o obj-$(CONFIG_AIRO) += airo.o obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o diff -urN -X /home/jgarzik/dontdiff /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/hermes.c linux_2_4/drivers/net/wireless/hermes.c --- /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/hermes.c Tue Oct 9 00:03:55 2001 +++ linux_2_4/drivers/net/wireless/hermes.c Tue Oct 9 03:48:40 2001 @@ -96,9 +96,9 @@ Callable from any context. */ -static int hermes_issue_cmd(hermes_t *hw, uint16_t cmd, uint16_t param0) +static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0) { - uint16_t reg; + u16 reg; /* First check that the command register is not busy */ reg = hermes_read_regn(hw, CMD); @@ -126,7 +126,7 @@ int hermes_reset(hermes_t *hw) { - uint16_t status, reg; + u16 status, reg; int err = 0; int k; @@ -210,11 +210,11 @@ * Returns: < 0 on internal error, 0 on success, > 0 on error returned by the firmware * * Callable from any context, but locking is your problem. */ -int hermes_docmd_wait(hermes_t *hw, uint16_t cmd, uint16_t parm0, hermes_response_t *resp) +int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp) { int err; int k; - uint16_t reg; + u16 reg; err = hermes_issue_cmd(hw, cmd, parm0); if (err) { @@ -263,12 +263,12 @@ return err; } -int hermes_allocate(hermes_t *hw, uint16_t size, uint16_t *fid) +int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) { int err = 0; hermes_response_t resp; int k; - uint16_t reg; + u16 reg; if ( (size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX) ) return -EINVAL; @@ -312,12 +312,12 @@ * from firmware * * Callable from any context */ -int hermes_bap_seek(hermes_t *hw, int bap, uint16_t id, uint16_t offset) +int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) { int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; int k; - uint16_t reg; + u16 reg; /* Paranoia.. */ if ( (offset > HERMES_BAP_OFFSET_MAX) || (offset % 2) ) @@ -368,7 +368,7 @@ * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware */ int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, - uint16_t id, uint16_t offset) + u16 id, u16 offset) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int err = 0; @@ -394,7 +394,7 @@ * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware */ int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, - uint16_t id, uint16_t offset) + u16 id, u16 offset) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int err = 0; @@ -421,16 +421,15 @@ * practice. * * Callable from user or bh context. */ -int hermes_read_ltv(hermes_t *hw, int bap, uint16_t rid, int buflen, - uint16_t *length, void *buf) +int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize, + u16 *length, void *buf) { int err = 0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; - uint16_t rlength, rtype; + u16 rlength, rtype; hermes_response_t resp; - int count; - if (buflen % 2) + if (bufsize % 2) return -EINVAL; err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, &resp); @@ -450,23 +449,21 @@ if (rtype != rid) printk(KERN_WARNING "hermes_read_ltv(): rid (0x%04x) does " "not match type (0x%04x)\n", rid, rtype); - if (HERMES_RECLEN_TO_BYTES(rlength) > buflen) + if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize) printk(KERN_WARNING "hermes @ 0x%x: Truncating LTV record from %d to %d bytes. " "(rid=0x%04x, len=0x%04x)\n", hw->iobase, - HERMES_RECLEN_TO_BYTES(rlength), buflen, rid, rlength); + HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); - /* For now we always read the whole buffer, the - lengths in the records seem to be wrong, frequently */ - count = buflen / 2; - - hermes_read_words(hw, dreg, buf, count); + /* FIXME: we should read the min of the requested length and + the actual record length */ + hermes_read_words(hw, dreg, buf, bufsize / 2); out: return err; } -int hermes_write_ltv(hermes_t *hw, int bap, uint16_t rid, - uint16_t length, const void *value) +int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, + u16 length, const void *value) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int err = 0; @@ -474,7 +471,7 @@ int count; DEBUG(3, "write_ltv(): bap=%d rid=0x%04x length=%d (value=0x%04x)\n", - bap, rid, length, * ((uint16_t *)value)); + bap, rid, length, * ((u16 *)value)); err = hermes_bap_seek(hw, bap, rid, 0); if (err) diff -urN -X /home/jgarzik/dontdiff /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/hermes.h linux_2_4/drivers/net/wireless/hermes.h --- /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/hermes.h Tue Oct 9 00:03:55 2001 +++ linux_2_4/drivers/net/wireless/hermes.h Tue Oct 9 03:55:33 2001 @@ -50,7 +50,6 @@ #define HERMES_FRAME_LEN_MAX (2304) #define HERMES_MAX_MULTICAST (16) #define HERMES_MAGIC (0x7d1f) -#define HERMES_SYMBOL_MAX_VER (14) /* * Hermes register offsets @@ -177,12 +176,12 @@ #define HERMES_RID_CNF_TX_KEY (0xfcb1) #define HERMES_RID_CNF_TICKTIME (0xfce0) -#define HERMES_RID_CNF_PRISM2_WEP_ON (0xfc28) -#define HERMES_RID_CNF_PRISM2_TX_KEY (0xfc23) -#define HERMES_RID_CNF_PRISM2_KEY0 (0xfc24) -#define HERMES_RID_CNF_PRISM2_KEY1 (0xfc25) -#define HERMES_RID_CNF_PRISM2_KEY2 (0xfc26) -#define HERMES_RID_CNF_PRISM2_KEY3 (0xfc27) +#define HERMES_RID_CNF_INTERSIL_WEP_ON (0xfc28) +#define HERMES_RID_CNF_INTERSIL_TX_KEY (0xfc23) +#define HERMES_RID_CNF_INTERSIL_KEY0 (0xfc24) +#define HERMES_RID_CNF_INTERSIL_KEY1 (0xfc25) +#define HERMES_RID_CNF_INTERSIL_KEY2 (0xfc26) +#define HERMES_RID_CNF_INTERSIL_KEY3 (0xfc27) #define HERMES_RID_CNF_SYMBOL_MANDATORY_BSSID (0xfc21) #define HERMES_RID_CNF_SYMBOL_AUTH_TYPE (0xfc2A) #define HERMES_RID_CNF_SYMBOL_BASIC_RATES (0xfc8A) @@ -212,11 +211,11 @@ typedef struct hermes_frame_desc { /* Hermes - i.e. little-endian byte-order */ - uint16_t status; - uint16_t res1, res2; - uint16_t q_info; - uint16_t res3, res4; - uint16_t tx_ctl; + u16 status; + u16 res1, res2; + u16 q_info; + u16 res3, res4; + u16 tx_ctl; } __attribute__ ((packed)) hermes_frame_desc_t; #define HERMES_RXSTAT_ERR (0x0003) @@ -239,21 +238,21 @@ typedef struct hermes { uint iobase; - uint16_t inten; /* Which interrupts should be enabled? */ + u16 inten; /* Which interrupts should be enabled? */ } hermes_t; typedef struct hermes_response { - uint16_t status, resp0, resp1, resp2; + u16 status, resp0, resp1, resp2; } hermes_response_t; /* "ID" structure - used for ESSID and station nickname */ typedef struct hermes_id { - uint16_t len; - uint16_t val[16]; + u16 len; + u16 val[16]; } __attribute__ ((packed)) hermes_id_t; typedef struct hermes_multicast { - uint8_t addr[HERMES_MAX_MULTICAST][ETH_ALEN]; + u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN]; } __attribute__ ((packed)) hermes_multicast_t; /* Register access convenience macros */ @@ -266,18 +265,18 @@ /* Function prototypes */ void hermes_struct_init(hermes_t *hw, uint io); int hermes_reset(hermes_t *hw); -int hermes_docmd_wait(hermes_t *hw, uint16_t cmd, uint16_t parm0, hermes_response_t *resp); -int hermes_allocate(hermes_t *hw, uint16_t size, uint16_t *fid); +int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp); +int hermes_allocate(hermes_t *hw, u16 size, u16 *fid); -int hermes_bap_seek(hermes_t *hw, int bap, uint16_t id, uint16_t offset); +int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset); int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, - uint16_t id, uint16_t offset); + u16 id, u16 offset); int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, - uint16_t id, uint16_t offset); -int hermes_read_ltv(hermes_t *hw, int bap, uint16_t rid, int buflen, - uint16_t *length, void *buf); -int hermes_write_ltv(hermes_t *hw, int bap, uint16_t rid, - uint16_t length, const void *value); + u16 id, u16 offset); +int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int buflen, + u16 *length, void *buf); +int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, + u16 length, const void *value); /* Inline functions */ @@ -286,13 +285,13 @@ return hermes_read_regn(hw, SWSUPPORT0) == HERMES_MAGIC; } -static inline void hermes_enable_interrupt(hermes_t *hw, uint16_t events) +static inline void hermes_enable_interrupt(hermes_t *hw, u16 events) { hw->inten |= events; hermes_write_regn(hw, INTEN, hw->inten); } -static inline void hermes_set_irqmask(hermes_t *hw, uint16_t events) +static inline void hermes_set_irqmask(hermes_t *hw, u16 events) { hw->inten = events; hermes_write_regn(hw, INTEN, events); @@ -326,9 +325,9 @@ #define HERMES_WRITE_RECORD(hw, bap, rid, buf) \ (hermes_write_ltv((hw),(bap),(rid),HERMES_BYTES_TO_RECLEN(sizeof(*buf)),(buf))) -static inline int hermes_read_wordrec(hermes_t *hw, int bap, uint16_t rid, uint16_t *word) +static inline int hermes_read_wordrec(hermes_t *hw, int bap, u16 rid, u16 *word) { - uint16_t rec; + u16 rec; int err; err = HERMES_READ_RECORD(hw, bap, rid, &rec); @@ -336,9 +335,9 @@ return err; } -static inline int hermes_write_wordrec(hermes_t *hw, int bap, uint16_t rid, uint16_t word) +static inline int hermes_write_wordrec(hermes_t *hw, int bap, u16 rid, u16 word) { - uint16_t rec = cpu_to_le16(word); + u16 rec = cpu_to_le16(word); return HERMES_WRITE_RECORD(hw, bap, rid, &rec); } diff -urN -X /home/jgarzik/dontdiff /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/orinoco.c linux_2_4/drivers/net/wireless/orinoco.c --- /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/orinoco.c Tue Oct 9 00:03:55 2001 +++ linux_2_4/drivers/net/wireless/orinoco.c Tue Oct 9 03:54:30 2001 @@ -1,4 +1,4 @@ -/* orinoco.c 0.08 - (formerly known as dldwd_cs.c and orinoco_cs.c) +/* orinoco.c 0.08a - (formerly known as dldwd_cs.c and orinoco_cs.c) * * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ @@ -44,33 +44,6 @@ * under either the MPL or the GPL. */ -/* Notes on locking: - * - * The basic principle of operation is that everything except the - * interrupt handler is serialized through a single spinlock in the - * dldwd_priv_t structure, using dldwd_lock() and - * dldwd_unlock() (which in turn use spin_lock_bh() and spin_unlock_bh()). - * - * The kernel's IRQ handling stuff ensures that the interrupt handler - * does not re-enter itself. The interrupt handler is written such - * that everything it does is safe without a lock: chiefly this means - * that the Rx path uses one of the Hermes chipset's BAPs while - * everything else uses the other. - * - * For the moment access to the device statistics from the interrupt - * handler is unsafe - we just put up with any resulting errors in the - * statisics. FIXME: This should probably be changed to store the - * stats in atomic types. - * - * EXCEPT that we don't want the irq handler running when we actually - * reset or shut down the card, because strange things might happen - * (probably the worst would be one packet of garbage, but you can't - * be too careful). For this we use __dldwd_stop_irqs() which will set - * a flag to disable the interrupt handler, and wait for any - * outstanding instances of the handler to complete. THIS WILL LOSE - * INTERRUPTS! so it shouldn't be used except for resets, when we - * don't care about that.*/ - /* * Tentative changelog... * @@ -180,9 +153,9 @@ * * v0.06e -> v0.06f - 14/8/2001 - David Gibson * o Wording fix to license - * o Added a 'use_alternate_encaps' module parameter for APs which need an oui of - * 00:00:00. We really need a better way of handling this, but the module flag - * is better than nothing for now. + * o Added a 'use_alternate_encaps' module parameter for APs which need an + * oui of 00:00:00. We really need a better way of handling this, but + * the module flag is better than nothing for now. * * v0.06f -> v0.07 - 20/8/2001 - David Gibson * o Removed BAP error retries from hermes_bap_seek(). For Tx we now @@ -190,32 +163,72 @@ * Rx path, but don't make as much noise about it. * o Firmware detection cleanups. * - * v0.07 -> v0.08 - 3/10/2001 - David Gibson + * v0.07 -> v0.07a - 1/10/3001 - Jean II + * o Add code to read Symbol firmware revision, inspired by latest code + * in Spectrum24 by Lee John Keyser-Allen - Thanks Lee ! + * o Thanks to Jared Valentine for "providing" me + * a 3Com card with a recent firmware, fill out Symbol firmware + * capabilities of latest rev (2.20), as well as older Symbol cards. + * o Disable Power Management in newer Symbol firmware, the API + * has changed (documentation needed). + * + * v0.07a -> v0.08 - 3/10/2001 - David Gibson * o Fixed a possible buffer overrun found by the Stanford checker (in * dldwd_ioctl_setiwencode()). Can only be called by root anyway, so not * a big problem. - * o Turned has_big_wep on for Intersil cards. That's not true for all of them - * but we should at least let the capable ones try. + * o Turned has_big_wep on for Intersil cards. That's not true for all of + * them but we should at least let the capable ones try. * o Wait for BUSY to clear at the beginning of hermes_bap_seek(). I * realised that my assumption that the driver's serialization * would prevent the BAP being busy on entry was possibly false, because * things other than seeks may make the BAP busy. * o Use "alternate" (oui 00:00:00) encapsulation by default. - * Setting use_old_encaps will mimic the old behaviour, but I - * think we will be able to eliminate this. - * o Don't try to make __initdata const (the version string). - * This can't work because of the way the __initdata sectioning - * works. + * Setting use_old_encaps will mimic the old behaviour, but I think we + * will be able to eliminate this. + * o Don't try to make __initdata const (the version string). This can't + * work because of the way the __initdata sectioning works. * o Added MODULE_LICENSE tags. * o Support for PLX (transparent PCMCIA->PCI brdge) cards. - * o Improved support for Symbol firmware - we can actually tell - * the version now. + * o Changed to using the new type-facist min/max. + * + * v0.08 -> v0.08a - 9/10/2001 - David Gibson + * o Inserted some missing acknowledgements/info into the Changelog. + * o Fixed some bugs in the normalisation of signel level reporting. + * o Fixed bad bug in WEP key handling on Intersil and Symbol firmware, + * which led to an instant crash on big-endian machines. * * TODO - Jean II * o inline functions (lots of candidate, need to reorder code) * o Test PrismII/Symbol cards & firmware versions * o Mini-PCI support (some people have reported success - JII) + * o Find and kill remaining Tx timeout problems */ +/* Notes on locking: + * + * The basic principle of operation is that everything except the + * interrupt handler is serialized through a single spinlock in the + * dldwd_priv_t structure, using dldwd_lock() and + * dldwd_unlock() (which in turn use spin_lock_bh() and spin_unlock_bh()). + * + * The kernel's IRQ handling stuff ensures that the interrupt handler + * does not re-enter itself. The interrupt handler is written such + * that everything it does is safe without a lock: chiefly this means + * that the Rx path uses one of the Hermes chipset's BAPs while + * everything else uses the other. + * + * Actually, the current updating of the statistics from the interrupt + * handler is unsafe. However all it can do is perturb the + * packet/byte counts slightly, so we just put up with it. We could + * fix this to use atomic types, but it's probably not worth it. + * + * The big exception is that that we don't want the irq handler + * running when we actually reset or shut down the card, because + * strange things might happen (probably the worst would be one packet + * of garbage, but you can't be too careful). For this we use + * __dldwd_stop_irqs() which will set a flag to disable the interrupt + * handler, and wait for any outstanding instances of the handler to + * complete. THIS WILL LOSE INTERRUPTS! so it shouldn't be used except + * for resets, where losing a few interrupts is acceptable. */ #include @@ -249,7 +262,7 @@ #include "hermes.h" #include "orinoco.h" -static char version[] __initdata = "orinoco.c 0.08 (David Gibson and others)"; +static char version[] __initdata = "orinoco.c 0.08a (David Gibson and others)"; MODULE_AUTHOR("David Gibson "); MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards"); MODULE_LICENSE("Dual MPL/GPL"); @@ -263,6 +276,8 @@ int use_old_encaps = 0; MODULE_PARM(use_old_encaps, "i"); +#define SYMBOL_MAX_VER_LEN (14) + const long channel_frequency[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 @@ -277,14 +292,14 @@ #define NUM_RATES (sizeof(rate_list) / sizeof(rate_list[0])) struct p80211_hdr { - uint16_t frame_ctl; - uint16_t duration_id; - uint8_t addr1[ETH_ALEN]; - uint8_t addr2[ETH_ALEN]; - uint8_t addr3[ETH_ALEN]; - uint16_t seq_ctl; - uint8_t addr4[ETH_ALEN]; - uint16_t data_len; + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; + u8 addr4[ETH_ALEN]; + u16 data_len; } __attribute__ ((packed)); /* Frame control field constants */ @@ -304,22 +319,20 @@ #define DLDWD_FTYPE_CTL 0x0004 #define DLDWD_FTYPE_DATA 0x0008 -#define __PACKED__ __attribute__ ((packed)) - struct p8022_hdr { - uint8_t dsap __PACKED__; - uint8_t ssap __PACKED__; - uint8_t ctrl __PACKED__; - uint8_t oui[3] __PACKED__; -}; + u8 dsap; + u8 ssap; + u8 ctrl; + u8 oui[3]; +} __attribute__ ((packed)); struct dldwd_frame_hdr { - hermes_frame_desc_t desc __PACKED__; - struct p80211_hdr p80211 __PACKED__; - struct ethhdr p8023 __PACKED__; - struct p8022_hdr p8022 __PACKED__; - uint16_t ethertype __PACKED__; -}; + hermes_frame_desc_t desc; + struct p80211_hdr p80211; + struct ethhdr p8023; + struct p8022_hdr p8022; + u16 ethertype; +} __attribute__ ((packed)); #define P8023_OFFSET (sizeof(hermes_frame_desc_t) + \ sizeof(struct p80211_hdr)) @@ -338,9 +351,8 @@ #define RX_EIO_RETRY 10 typedef struct dldwd_commsqual { - uint16_t qual, signal, noise; -} __PACKED__ dldwd_commsqual_t; - + u16 qual, signal, noise; +} __attribute__ ((packed)) dldwd_commsqual_t; /* * Function prototypes @@ -361,7 +373,7 @@ static int dldwd_hw_get_essid(dldwd_priv_t *priv, int *active, char buf[IW_ESSID_MAX_SIZE+1]); static long dldwd_hw_get_freq(dldwd_priv_t *priv); static int dldwd_hw_get_bitratelist(dldwd_priv_t *priv, int *numrates, - int32_t *rates, int max); + s32 *rates, int max); /* Interrupt handling routines */ static void __dldwd_ev_tick(dldwd_priv_t *priv, hermes_t *hw); @@ -431,7 +443,7 @@ } static inline void -__dldwd_start_irqs(dldwd_priv_t *priv, uint16_t irqmask) +__dldwd_start_irqs(dldwd_priv_t *priv, u16 irqmask) { hermes_t *hw = &priv->hw; @@ -577,8 +589,11 @@ /* Set up encryption */ if (priv->has_wep) { err = __dldwd_hw_setup_wep(priv); - if (err) + if (err) { + printk(KERN_ERR "%s: Error %d activating WEP.\n", + dev->name, err); goto out; + } } /* Set the desired ESSID */ @@ -691,6 +706,8 @@ int master_wep_flag; int auth_flag; + TRACE_ENTER(priv->ndev.name); + switch (priv->firmware_type) { case FIRMWARE_TYPE_LUCENT: /* Lucent style WEP */ if (priv->wep_on) { @@ -711,28 +728,34 @@ case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ master_wep_flag = 0; /* Off */ if (priv->wep_on) { - char keybuf[LARGE_KEY_SIZE+1]; - int keylen; +/* int keylen; */ int i; /* Fudge around firmware weirdness */ - keylen = priv->keys[priv->tx_key].len; +/* keylen = priv->keys[priv->tx_key].len; */ /* Write all 4 keys */ for(i = 0; i < MAX_KEYS; i++) { - memset(keybuf, 0, sizeof(keybuf)); - memcpy(keybuf, priv->keys[i].data, - priv->keys[i].len); + int keylen = le16_to_cpu(priv->keys[i].len); + + if (keylen > LARGE_KEY_SIZE) { + printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", + priv->ndev.name, i, keylen); + return -E2BIG; + } + + printk("About to write key %d, keylen=%d\n", + i, keylen); err = hermes_write_ltv(hw, USER_BAP, - HERMES_RID_CNF_PRISM2_KEY0 + i, + HERMES_RID_CNF_INTERSIL_KEY0 + i, HERMES_BYTES_TO_RECLEN(keylen), - keybuf); + priv->keys[i].data); if (err) return err; } /* Write the index of the key used in transmission */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PRISM2_TX_KEY, + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_INTERSIL_TX_KEY, priv->tx_key); if (err) return err; @@ -764,9 +787,10 @@ } /* Master WEP setting : on/off */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PRISM2_WEP_ON, master_wep_flag); + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_INTERSIL_WEP_ON, master_wep_flag); if (err) return err; + break; default: @@ -777,6 +801,8 @@ } } + TRACE_EXIT(priv->ndev.name); + return 0; } @@ -816,7 +842,7 @@ /* My guess is that the OWN_SSID should always be whatever * we set to the card, whereas CURRENT_SSID is the one that * may change... - Jean II */ - uint16_t rid; + u16 rid; *active = 1; @@ -855,7 +881,7 @@ hermes_t *hw = &priv->hw; int err = 0; - uint16_t channel; + u16 channel; long freq = 0; dldwd_lock(priv); @@ -883,7 +909,7 @@ } static int dldwd_hw_get_bitratelist(dldwd_priv_t *priv, int *numrates, - int32_t *rates, int max) + s32 *rates, int max) { hermes_t *hw = &priv->hw; hermes_id_t list; @@ -981,7 +1007,7 @@ hermes_t *hw = &priv->hw; struct net_device *dev = &priv->ndev; int count = IRQ_LOOP_MAX; - uint16_t evstat, events; + u16 evstat, events; static int old_time = 0, timecount = 0; /* Eugh, revolting hack for now */ if (test_and_set_bit(DLDWD_STATE_INIRQ, &priv->state)) @@ -1087,7 +1113,7 @@ struct iw_statistics *wstats = &priv->wstats; struct sk_buff *skb = NULL; int l = RX_EIO_RETRY; - uint16_t rxfid, status; + u16 rxfid, status; int length, data_len, data_off; char *p; struct dldwd_frame_hdr hdr; @@ -1262,7 +1288,7 @@ static void __dldwd_ev_alloc(dldwd_priv_t *priv, hermes_t *hw) { - uint16_t allocfid; + u16 allocfid; allocfid = hermes_read_regn(hw, ALLOCFID); DEBUG(3, "%s: Allocation complete FID=0x%04x\n", priv->ndev.name, allocfid); @@ -1280,9 +1306,9 @@ hermes_t *hw = &priv->hw; int err; struct sta_id { - uint16_t id, vendor, major, minor; - } __PACKED__ sta_id; - uint32_t firmver; + u16 id, vendor, major, minor; + } __attribute__ ((packed)) sta_id; + u32 firmver; /* Get the firmware version */ err = HERMES_READ_RECORD(hw, USER_BAP, @@ -1297,7 +1323,7 @@ le16_to_cpus(&sta_id.major); le16_to_cpus(&sta_id.minor); - firmver = ((uint32_t)sta_id.major << 16) | sta_id.minor; + firmver = ((u32)sta_id.major << 16) | sta_id.minor; printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n", dev->name, sta_id.id, sta_id.vendor, @@ -1335,37 +1361,33 @@ /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */ /* Intel MAC : 00:02:B3:* */ /* 3Com MAC : 00:50:DA:* */ - union symbol_sta_id { - char raw[HERMES_SYMBOL_MAX_VER]; - char string[HERMES_SYMBOL_MAX_VER + 1]; - } symbol_sta_id; + char tmp[SYMBOL_MAX_VER_LEN+1]; + memset(tmp, 0, sizeof(tmp)); /* Get the Symbol firmware version */ - err = HERMES_READ_RECORD(hw, USER_BAP, - HERMES_RID_SYMBOL_SECONDARY_VER, - &(symbol_sta_id.raw)); + err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SYMBOL_SECONDARY_VER, + SYMBOL_MAX_VER_LEN, NULL, &tmp); if (err) { - printk(KERN_WARNING "%s: Error %d reading Symbol firmware info. Wildly guessing capabilities...\n", + printk(KERN_WARNING + "%s: Error %d reading Symbol firmware info. Wildly guessing capabilities...\n", dev->name, err); firmver = 0; - symbol_sta_id.string[0] = '\0'; + tmp[0] = '\0'; } else { /* The firmware revision is a string, the format is * something like : "V2.20-01". * Quick and dirty parsing... - Jean II */ - firmver = ((symbol_sta_id.raw[1] - '0') << 16) - | ((symbol_sta_id.raw[3] - '0') << 12) - | ((symbol_sta_id.raw[4] - '0') << 8) - | ((symbol_sta_id.raw[6] - '0') << 4) - | (symbol_sta_id.raw[7] - '0'); + firmver = ((tmp[1] - '0') << 16) | ((tmp[3] - '0') << 12) + | ((tmp[4] - '0') << 8) | ((tmp[6] - '0') << 4) + | (tmp[7] - '0'); - symbol_sta_id.string[HERMES_SYMBOL_MAX_VER] = '\0'; + tmp[SYMBOL_MAX_VER_LEN] = '\0'; } printk(KERN_DEBUG "%s: Looks like a Symbol firmware " "version [%s] (parsing to %X)\n", dev->name, - symbol_sta_id.string, firmver); + tmp, firmver); priv->firmware_type = FIRMWARE_TYPE_SYMBOL; priv->tx_rate_ctrl = 0xF; /* 11 Mb/s auto */ @@ -1427,11 +1449,11 @@ hermes_t *hw = &priv->hw; int err = 0; hermes_id_t nickbuf; - uint16_t reclen; + u16 reclen; int len; TRACE_ENTER("dldwd"); - + dldwd_lock(priv); /* Do standard firmware reset */ @@ -1479,8 +1501,8 @@ dev->name); goto out; } - if ( nickbuf.len ) - len = min(IW_ESSID_MAX_SIZE, le16_to_cpu(nickbuf.len)); + if (nickbuf.len) + len = min_t(u16, IW_ESSID_MAX_SIZE, le16_to_cpu(nickbuf.len)); else len = min(IW_ESSID_MAX_SIZE, 2 * reclen); memcpy(priv->nick, &nickbuf.val, len); @@ -1612,9 +1634,9 @@ DEBUG(3, "%s: Global stats = %X-%X-%X\n", dev->name, cq.qual, cq.signal, cq.noise); - wstats->qual.qual = le16_to_cpu(cq.qual); - wstats->qual.level = le16_to_cpu(cq.signal); - wstats->qual.noise = le16_to_cpu(cq.noise); + wstats->qual.qual = (int)le16_to_cpu(cq.qual); + wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; + wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; wstats->qual.updated = 7; } @@ -1627,9 +1649,8 @@ } #ifdef WIRELESS_SPY -static inline void dldwd_spy_gather(struct net_device *dev, - u_char *mac, - dldwd_commsqual_t *cq) +static inline void dldwd_spy_gather(struct net_device *dev, u_char *mac, + int level, int noise) { dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv; int i; @@ -1638,9 +1659,9 @@ * source address with out list, and if match, get the stats... */ for (i = 0; i < priv->spy_number; i++) if (!memcmp(mac, priv->spy_address[i], ETH_ALEN)) { - priv->spy_stat[i].qual = cq->qual; - priv->spy_stat[i].level = cq->signal; - priv->spy_stat[i].noise = cq->noise; + priv->spy_stat[i].level = level - 0x95; + priv->spy_stat[i].noise = noise - 0x95; + priv->spy_stat[i].qual = level - noise; priv->spy_stat[i].updated = 7; } } @@ -1652,7 +1673,6 @@ struct dldwd_frame_hdr *hdr) { dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv; - dldwd_commsqual_t cq; /* Using spy support with lots of Rx packets, like in an * infrastructure (AP), will really slow down everything, because @@ -1662,31 +1682,18 @@ * Note that to get here, you need both WIRELESS_SPY * compiled in AND some addresses in the list !!! */ -#ifdef WIRELESS_EXT +#ifdef WIRELESS_SPY /* Note : gcc will optimise the whole section away if * WIRELESS_SPY is not defined... - Jean II */ - if ( -#ifdef WIRELESS_SPY - (priv->spy_number > 0) || -#endif - 0 ) - { - u_char *stats = (u_char *) &(hdr->desc.q_info); + if (priv->spy_number > 0) { + u8 *stats = (u8 *) &(hdr->desc.q_info); /* This code may look strange. Everywhere we are using 16 bit * ints except here. I've verified that these are are the * correct values. Please check on PPC - Jean II */ - cq.signal = stats[1]; /* High order byte */ - cq.noise = stats[0]; /* Low order byte */ - cq.qual = stats[0] - stats[1]; /* Better than nothing */ - - DEBUG(3, "%s: Packet stats = %X-%X-%X\n", dev->name, - cq.qual, cq.signal, cq.noise); -#ifdef WIRELESS_SPY - dldwd_spy_gather(dev, skb->mac.raw + ETH_ALEN, &cq); -#endif + dldwd_spy_gather(dev, skb->mac.raw + ETH_ALEN, (int)stats[1], (int)stats[0]); } -#endif /* WIRELESS_EXT */ +#endif /* WIRELESS_SPY */ } int @@ -1696,7 +1703,7 @@ struct net_device_stats *stats = &priv->stats; hermes_t *hw = &priv->hw; int err = 0; - uint16_t txfid = priv->txfid; + u16 txfid = priv->txfid; char *p; struct ethhdr *eh; int len, data_len, data_off; @@ -1976,7 +1983,7 @@ int setindex = priv->tx_key; int enable = priv->wep_on; int restricted = priv->wep_restrict; - uint16_t xlen = 0; + u16 xlen = 0; int err = 0; char keybuf[MAX_KEY_SIZE]; @@ -2064,7 +2071,7 @@ { dldwd_priv_t *priv = dev->priv; int index = (erq->flags & IW_ENCODE_INDEX) - 1; - uint16_t xlen = 0; + u16 xlen = 0; char keybuf[MAX_KEY_SIZE]; @@ -2114,7 +2121,7 @@ memset(&essidbuf, 0, sizeof(essidbuf)); - if (erq->flags) { + if (erq->flags) { if (erq->length > IW_ESSID_MAX_SIZE) return -E2BIG; @@ -2126,7 +2133,7 @@ dldwd_lock(priv); - memcpy(priv->desired_essid, essidbuf, IW_ESSID_MAX_SIZE+1); + memcpy(priv->desired_essid, essidbuf, sizeof(priv->desired_essid)); dldwd_unlock(priv); @@ -2240,7 +2247,7 @@ { dldwd_priv_t *priv = dev->priv; hermes_t *hw = &priv->hw; - uint16_t val; + u16 val; int err; dldwd_lock(priv); @@ -2326,7 +2333,7 @@ dldwd_priv_t *priv = dev->priv; hermes_t *hw = &priv->hw; int err = 0; - uint16_t val; + u16 val; dldwd_lock(priv); @@ -2435,7 +2442,7 @@ dldwd_priv_t *priv = dev->priv; hermes_t *hw = &priv->hw; int err = 0; - uint16_t val; + u16 val; int brate = 0; dldwd_lock(priv); @@ -2549,7 +2556,7 @@ dldwd_priv_t *priv = dev->priv; hermes_t *hw = &priv->hw; int err = 0; - uint16_t enable, period, timeout, mcast; + u16 enable, period, timeout, mcast; dldwd_lock(priv); @@ -2595,7 +2602,7 @@ dldwd_priv_t *priv = dev->priv; hermes_t *hw = &priv->hw; int err = 0; - uint16_t short_limit, long_limit, lifetime; + u16 short_limit, long_limit, lifetime; dldwd_lock(priv); @@ -3464,7 +3471,7 @@ } struct { - uint16_t rid; + u16 rid; char *name; int minlen, maxlen; int displaytype; @@ -3495,12 +3502,12 @@ RTCNFENTRY(KEYS, DISPLAY_BYTES), RTCNFENTRY(TX_KEY, DISPLAY_WORDS), RTCNFENTRY(TICKTIME, DISPLAY_WORDS), - RTCNFENTRY(PRISM2_TX_KEY, DISPLAY_WORDS), - RTCNFENTRY(PRISM2_KEY0, DISPLAY_BYTES), - RTCNFENTRY(PRISM2_KEY1, DISPLAY_BYTES), - RTCNFENTRY(PRISM2_KEY2, DISPLAY_BYTES), - RTCNFENTRY(PRISM2_KEY3, DISPLAY_BYTES), - RTCNFENTRY(PRISM2_WEP_ON, DISPLAY_WORDS), + RTCNFENTRY(INTERSIL_TX_KEY, DISPLAY_WORDS), + RTCNFENTRY(INTERSIL_KEY0, DISPLAY_BYTES), + RTCNFENTRY(INTERSIL_KEY1, DISPLAY_BYTES), + RTCNFENTRY(INTERSIL_KEY2, DISPLAY_BYTES), + RTCNFENTRY(INTERSIL_KEY3, DISPLAY_BYTES), + RTCNFENTRY(INTERSIL_WEP_ON, DISPLAY_WORDS), #undef RTCNFENTRY #define RTINFENTRY(name,type) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, type } RTINFENTRY(CHANNEL_LIST, DISPLAY_WORDS), @@ -3525,7 +3532,7 @@ char *buf; int total = 0, slop = 0; int i; - uint16_t length; + u16 length; int err; /* Hum, in this case hardware register are probably not readable... */ @@ -3536,12 +3543,12 @@ /* print out all the config RIDs */ for (i = 0; i < NUM_RIDS; i++) { - uint16_t rid = record_table[i].rid; + u16 rid = record_table[i].rid; int minlen = record_table[i].minlen; int maxlen = record_table[i].maxlen; int len; - uint8_t *val8; - uint16_t *val16; + u8 *val8; + u16 *val16; int j; val8 = kmalloc(maxlen + 2, GFP_KERNEL); @@ -3554,7 +3561,7 @@ DEBUG(0, "Error %d reading RID 0x%04x\n", err, rid); continue; } - val16 = (uint16_t *)val8; + val16 = (u16 *)val8; buf += sprintf(buf, "%-15s (0x%04x): length=%d (%d bytes)\tvalue=", record_table[i].name, rid, length, (length-1)*2); diff -urN -X /home/jgarzik/dontdiff /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/orinoco.h linux_2_4/drivers/net/wireless/orinoco.h --- /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/orinoco.h Tue Oct 9 00:03:55 2001 +++ linux_2_4/drivers/net/wireless/orinoco.h Tue Oct 9 03:39:02 2001 @@ -8,7 +8,7 @@ #define _ORINOCO_H /* To enable debug messages */ -//#define ORINOCO_DEBUG 3 +/* #define ORINOCO_DEBUG 3 */ #if (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10) #error "orinoco_cs requires Wireless extensions v10 or later." @@ -33,7 +33,7 @@ #define MAX_FRAME_SIZE 2304 typedef struct dldwd_key { - uint16_t len; + uint16_t len; /* always store little-endian */ char data[MAX_KEY_SIZE]; } __attribute__ ((packed)) dldwd_key_t; diff -urN -X /home/jgarzik/dontdiff /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/orinoco_cs.c linux_2_4/drivers/net/wireless/orinoco_cs.c --- /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/orinoco_cs.c Tue Oct 9 00:03:55 2001 +++ linux_2_4/drivers/net/wireless/orinoco_cs.c Tue Oct 9 03:39:35 2001 @@ -1,4 +1,4 @@ -/* orinoco_cs.c 0.08 - (formerly known as dldwd_cs.c) +/* orinoco_cs.c 0.08a - (formerly known as dldwd_cs.c) * * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ @@ -44,7 +44,7 @@ /*====================================================================*/ -static char version[] __initdata = "orinoco_cs.c 0.08 (David Gibson and others)"; +static char version[] __initdata = "orinoco_cs.c 0.08a (David Gibson and others)"; MODULE_AUTHOR("David Gibson "); MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards"); @@ -200,7 +200,7 @@ CardServices(AccessConfigurationRegister, link->handle, ®); default_cor = reg.Value; - DEBUG(2, "dldwd : dldwd_cs_cor_reset() : cor=0x%lX\n", default_cor); + DEBUG(2, "dldwd : dldwd_cs_cor_reset() : cor=0x%X\n", default_cor); /* Soft-Reset card */ reg.Action = CS_WRITE; diff -urN -X /home/jgarzik/dontdiff /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/orinoco_plx.c linux_2_4/drivers/net/wireless/orinoco_plx.c --- /home/jgarzik/tmp/linux-2.4.11-pre6/drivers/net/wireless/orinoco_plx.c Wed Dec 31 19:00:00 1969 +++ linux_2_4/drivers/net/wireless/orinoco_plx.c Tue Oct 9 03:39:40 2001 @@ -0,0 +1,306 @@ +/* orinoco_plx.c 0.01 + * + * Driver for Prism II devices which would usually be driven by orinoco_cs, + * but are connected to the PCI bus by a PLX9052. + * + * Specifically here we're talking about the SMC2602W (EZConnect + * Wireless PCI Adaptor) + * + * The actual driving is done by orinoco.c, this is just resource + * allocation stuff. The explanation below is courtesy of Ryan Niemi + * on the linux-wlan-ng list at + * http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html + * + * Copyright (C) 2001 Daniel Barlow + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. + +The PLX9052-based cards (WL11000 and several others) are a different +beast than the usual PCMCIA-based PRISM2 configuration expected by +wlan-ng. Here's the general details on how the WL11000 PCI adapter +works: + + - Two PCI I/O address spaces, one 0x80 long which contains the PLX9052 + registers, and one that's 0x40 long mapped to the PCMCIA slot I/O + address space. + + - One PCI memory address space, mapped to the PCMCIA memory space + (containing the CIS). + +After identifying the I/O and memory space, you can read through the +memory space to confirm the CIS's device ID or manufacturer ID to make +sure it's the expected card. Keep in mind that the PCMCIA spec specifies +the CIS as the lower 8 bits of each word read from the CIS, so to read the +bytes of the CIS, read every other byte (0,2,4,...). Passing that test, +you need to enable the I/O address space on the PCMCIA card via the PCMCIA +COR register. This is the first byte following the CIS. In my case +(which may not have any relation to what's on the PRISM2 cards), COR was +at offset 0x800 within the PCI memory space. Write 0x41 to the COR +register to enable I/O mode and to select level triggered interrupts. To +confirm you actually succeeded, read the COR register back and make sure +it actually got set to 0x41, incase you have an unexpected card inserted. + +Following that, you can treat the second PCI I/O address space (the one +that's not 0x80 in length) as the PCMCIA I/O space. + +Note that in the Eumitcom's source for their drivers, they register the +interrupt as edge triggered when registering it with the Windows kernel. I +don't recall how to register edge triggered on Linux (if it can be done at +all). But in some experimentation, I don't see much operational +difference between using either interrupt mode. Don't mess with the +interrupt mode in the COR register though, as the PLX9052 wants level +triggers with the way the serial EEPROM configures it on the WL11000. + +There's some other little quirks related to timing that I bumped into, but +I don't recall right now. Also, there's two variants of the WL11000 I've +seen, revision A1 and T2. These seem to differ slightly in the timings +configured in the wait-state generator in the PLX9052. There have also +been some comments from Eumitcom that cards shouldn't be hot swapped, +apparently due to risk of cooking the PLX9052. I'm unsure why they +believe this, as I can't see anything in the design that would really +cause a problem, except for crashing drivers not written to expect it. And +having developed drivers for the WL11000, I'd say it's quite tricky to +write code that will successfully deal with a hot unplug. Very odd things +happen on the I/O side of things. But anyway, be warned. Despite that, +I've hot-swapped a number of times during debugging and driver development +for various reasons (stuck WAIT# line after the radio card's firmware +locks up). + +Hope this is enough info for someone to add PLX9052 support to the wlan-ng +card. In the case of the WL11000, the PCI ID's are 0x1639/0x0200, with +matching subsystem ID's. Other PLX9052-based manufacturers other than +Eumitcom (or on cards other than the WL11000) may have different PCI ID's. + +If anyone needs any more specific info, let me know. I haven't had time +to implement support myself yet, and with the way things are going, might +not have time for a while.. + +---end of mail--- + + Bus 0, device 4, function 0: + Network controller: Unknown vendor Unknown device (rev 2). + Vendor id=1638. Device id=1100. + Medium devsel. Fast back-to-back capable. IRQ 10. + I/O at 0x1000 [0x1001]. + Non-prefetchable 32 bit memory at 0x40000000 [0x40000000]. + I/O at 0x10c0 [0x10c1]. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "hermes.h" +#include "orinoco.h" + +MODULE_AUTHOR("Daniel Barlow "); +MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge"); +MODULE_LICENSE("Dual MPL/GPL"); + +static dev_info_t dev_info = "orinoco_plx"; + +#define COR_OFFSET 0x3e0 /* COR attribute offset of Prism2 PC card */ +#define COR_VALUE 0x41 /* Enable PC card with interrupt in level trigger */ + +static int orinoco_plx_open(struct net_device *dev) +{ + dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv; + int err; + + netif_device_attach(dev); + + err = dldwd_reset(priv); + if (err) + printk(KERN_ERR "%s: dldwd_reset failed in orinoco_plx_open()", + dev->name); + else + netif_start_queue(dev); + + return err; +} + +static int orinoco_plx_stop(struct net_device *dev) +{ + dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv; + netif_stop_queue(dev); + dldwd_shutdown(priv); + return 0; +} + +static void +orinoco_plx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + dldwd_interrupt(irq, ((struct net_device *) dev_id)->priv, regs); +} + +static int orinoco_plx_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev; + unsigned long pccard_ioaddr; + int i; + int reg; + unsigned char *attr_mem; + dldwd_priv_t *priv; + + if ((i = pci_enable_device(pdev))) + return -EIO; + + /* Resource 2 is mapped to the PCMCIA space */ + attr_mem = ioremap(pci_resource_start(pdev, 2), 0x1000); + /* and 3 to the PCMCIA slot I/O address space */ + pccard_ioaddr = pci_resource_start(pdev, 3); + + /* Verify whether PC card is present */ + if (attr_mem[0] != 0x01 || attr_mem[2] != 0x03 || + attr_mem[4] != 0x00 || attr_mem[6] != 0x00 || + attr_mem[8] != 0xFF || attr_mem[10] != 0x17 || + attr_mem[12] != 0x04 || attr_mem[14] != 0x67) { + printk(KERN_ERR "orinoco_plx: The CIS value of Prism2 PC card is invalid.\n"); + return -EIO; + } + /* PCMCIA COR is the first byte following CIS: this write should + * enable I/O mode and select level-triggered interrupts */ + attr_mem[COR_OFFSET] = COR_VALUE; + reg = attr_mem[COR_OFFSET]; + /* assert(reg==COR_VALUE); doesn't work */ + iounmap(attr_mem); /* done with this now, it seems */ + if (!request_region(pccard_ioaddr, + pci_resource_len(pdev, 3), dev_info)) { + printk(KERN_ERR "orinoco_plx: I/O resource 0x%lx @ 0x%lx busy\n", + pci_resource_len(pdev, 3), pccard_ioaddr); + return -EBUSY; + } + if (!(priv = kmalloc(sizeof(*priv), GFP_KERNEL))) + return -ENOMEM; + memset(priv, 0, sizeof(*priv)); + dev = &priv->ndev; + + dldwd_setup(priv); /* XXX clean up if <0 */ + dev->irq = pdev->irq; + dev->base_addr = pccard_ioaddr; + dev->open = orinoco_plx_open; + dev->stop = orinoco_plx_stop; + priv->card_reset_handler = NULL; /* We have no reset handler */ + + printk(KERN_DEBUG + "Detected Orinoco/Prism2 PCI device at %s, mem:0x%lx, irq:%d, io addr:0x%lx\n", + pdev->slot_name, (long) attr_mem, pdev->irq, pccard_ioaddr); + + hermes_struct_init(&(priv->hw), dev->base_addr); /* XXX */ + dev->name[0] = '\0'; /* name defaults to ethX */ + register_netdev(dev); + request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name, + dev); + if (dldwd_proc_dev_init(priv) != 0) { + printk(KERN_ERR "%s: Failed to create /proc node\n", dev->name); + return -EIO; + } + + SET_MODULE_OWNER(dev); + priv->hw_ready = 1; + + /* if(reset_cor) dldwd_cs_cor_reset(priv); */ + return 0; /* succeeded */ +} + +static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + dldwd_priv_t *priv = dev->priv; + + if (!dev) + BUG(); + + dldwd_proc_dev_cleanup(priv); + free_irq(dev->irq, dev); + unregister_netdev(dev); + release_region(dev->base_addr, 0x40); + kfree(dev->priv); + pci_set_drvdata(pdev, NULL); +} + + +static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = { + {0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, orinoco_plx_pci_id_table); + +static struct pci_driver orinoco_plx_driver = { + name:"orinoco_plx", + id_table:orinoco_plx_pci_id_table, + probe:orinoco_plx_init_one, + remove:orinoco_plx_remove_one, + suspend:0, + resume:0 +}; + +static int __init orinoco_plx_init(void) +{ + return pci_module_init(&orinoco_plx_driver); +} + +extern void __exit orinoco_plx_exit(void) +{ + pci_unregister_driver(&orinoco_plx_driver); +} + +module_init(orinoco_plx_init); +module_exit(orinoco_plx_exit); + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */