/**************************************************************************** * This file is part of the WLAN-Ethernut device driver. * * Copyright (c) 2004 by Michael Fischer. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * **************************************************************************** * Portions Copyright: * * Copyright (c) 2002 * M Warner Losh . All rights reserved. * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************** * History: * * 28.02.04 mifi First Version * If you like to see the original source. Take a look * in if_wi.c from FreeBSD. * 01.03.04 mifi Now we use a timeout in our Thread, and check if we lost * the card. In this case we try to Init it again. * 02.03.04 mifi Remove QuickHack for Networkname and WEP. * 03.03.04 mifi Use struct to config WLAN (WLAN_IOCTL_SET_CONFIG) * 04.03.04 mifi Add function to get WLAN_STATUS (WLAN_IOCTL_GET_STATUS) * 06.03.04 mifi Remove all @@MF later for monitor mode. * It does not work with my card, and I think we need a * special firmware for it. ****************************************************************************/ #define __WLANDRV_C__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*==========================================================*/ /* DEFINE: All Structures and Common Constants */ /*==========================================================*/ #if (_DEBUG >= 1) #define Debug(_x) if (bDebugState ) printf _x #define Debug2(_x) if (bDebugState > 1) printf _x #define panic printf #else #define Debug(_x) #define Debug2(_x) #define panic #endif #define WI_LOCK(sc) NutEventWait(&hDeviceSemaphore,0) #define WI_UNLOCK(sc) NutEventPost(&hDeviceSemaphore) #define ieee80211_new_state(_a,_b,_c) _a->ic_state=_b #define isset(_a,_b) (*_a & (1<<_b)) #define CSR_WRITE_2(_a,_b,_c) pcmcia_WriteReg(_b,_c) #define CSR_READ_2(_a,_b) pcmcia_ReadReg(_b) /* * Length information */ #define WLAN_MIN_ETHERNET_FRAME_LEN 60 #define WLAN_MAX_ETHERNET_FRAME_LEN 1514 #define WLAN_ETHERNET_HEADER_LEN 14 /* * Interrupt define */ #define WI_INTRS (WI_EV_RX | WI_EV_ALLOC | WI_EV_INFO) struct wi_card_ident { u_int16_t card_id; char *card_name; u_int8_t firm_type; }; struct ieee80211_frame_addr4 { u_int8_t i_fc[2]; u_int8_t i_dur[2]; u_int8_t i_addr1[IEEE80211_ADDR_LEN]; u_int8_t i_addr2[IEEE80211_ADDR_LEN]; u_int8_t i_addr3[IEEE80211_ADDR_LEN]; u_int8_t i_seq[2]; u_int8_t i_addr4[IEEE80211_ADDR_LEN]; }; /* * transmit/receive frame structure */ struct wi_frame { u_int16_t wi_status; /* 0x00 */ u_int16_t wi_rx_tstamp1; /* 0x02 */ u_int16_t wi_rx_tstamp0; /* 0x04 */ u_int8_t wi_rx_silence; /* 0x06 */ u_int8_t wi_rx_signal; /* 0x07 */ u_int8_t wi_rx_rate; /* 0x08 */ u_int8_t wi_rx_flow; /* 0x09 */ u_int8_t wi_tx_rtry; /* 0x0a *//* Prism2 AP Only */ u_int8_t wi_tx_rate; /* 0x0b *//* Prism2 AP Only */ u_int16_t wi_tx_ctl; /* 0x0c */ struct ieee80211_frame_addr4 wi_whdr; /* 0x0e */ u_int16_t wi_dat_len; /* 0x2c */ struct ether_header wi_ehdr; /* 0x2e */ }; /* * Station set identification (SSID). (0xFC02, 0xFC04) */ struct wi_ssid { u_int16_t wi_len; u_int8_t wi_ssid[32]; }; /* * Ethernet LLCS + SNAP header */ typedef struct { u_int16_t DSAPSSAP; u_int16_t Control; u_int16_t MustZero; u_int16_t Type; } LLCS_SNAP_HEADER; /*==========================================================*/ /* DEFINE: Prototypes */ /*==========================================================*/ /*==========================================================*/ /* DEFINE: Definition of all local Data */ /*==========================================================*/ static HANDLE hDeviceSemaphore; static BYTE bDebugState = 0; struct wi_card_ident wi_card_ident[] = { /* * CARD_ID CARD_NAME FIRM_TYPE */ #if (WLAN_SUPPORT_LUCENT >= 1) {WI_NIC_LUCENT_ID, WI_NIC_LUCENT_STR, WI_LUCENT}, {WI_NIC_SONY_ID, WI_NIC_SONY_STR, WI_LUCENT}, {WI_NIC_LUCENT_EMB_ID, WI_NIC_LUCENT_EMB_STR, WI_LUCENT}, #endif /* (WLAN_SUPPORT_LUCENT >= 1) */ #if (WLAN_SUPPORT_INTERSIL >= 1) {WI_NIC_EVB2_ID, WI_NIC_EVB2_STR, WI_INTERSIL}, {WI_NIC_HWB3763_ID, WI_NIC_HWB3763_STR, WI_INTERSIL}, {WI_NIC_HWB3163_ID, WI_NIC_HWB3163_STR, WI_INTERSIL}, {WI_NIC_HWB3163B_ID, WI_NIC_HWB3163B_STR, WI_INTERSIL}, {WI_NIC_EVB3_ID, WI_NIC_EVB3_STR, WI_INTERSIL}, {WI_NIC_HWB1153_ID, WI_NIC_HWB1153_STR, WI_INTERSIL}, {WI_NIC_P2_SST_ID, WI_NIC_P2_SST_STR, WI_INTERSIL}, {WI_NIC_EVB2_SST_ID, WI_NIC_EVB2_SST_STR, WI_INTERSIL}, {WI_NIC_3842_EVA_ID, WI_NIC_3842_EVA_STR, WI_INTERSIL}, {WI_NIC_3842_PCMCIA_AMD_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL}, {WI_NIC_3842_PCMCIA_SST_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL}, {WI_NIC_3842_PCMCIA_ATL_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL}, {WI_NIC_3842_PCMCIA_ATS_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL}, {WI_NIC_3842_MINI_AMD_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL}, {WI_NIC_3842_MINI_SST_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL}, {WI_NIC_3842_MINI_ATL_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL}, {WI_NIC_3842_MINI_ATS_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL}, {WI_NIC_3842_PCI_AMD_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL}, {WI_NIC_3842_PCI_SST_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL}, {WI_NIC_3842_PCI_ATS_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL}, {WI_NIC_3842_PCI_ATL_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL}, {WI_NIC_P3_PCMCIA_AMD_ID, WI_NIC_P3_PCMCIA_STR, WI_INTERSIL}, {WI_NIC_P3_PCMCIA_SST_ID, WI_NIC_P3_PCMCIA_STR, WI_INTERSIL}, {WI_NIC_P3_PCMCIA_ATL_ID, WI_NIC_P3_PCMCIA_STR, WI_INTERSIL}, {WI_NIC_P3_PCMCIA_ATS_ID, WI_NIC_P3_PCMCIA_STR, WI_INTERSIL}, {WI_NIC_P3_MINI_AMD_ID, WI_NIC_P3_MINI_STR, WI_INTERSIL}, {WI_NIC_P3_MINI_SST_ID, WI_NIC_P3_MINI_STR, WI_INTERSIL}, {WI_NIC_P3_MINI_ATL_ID, WI_NIC_P3_MINI_STR, WI_INTERSIL}, {WI_NIC_P3_MINI_ATS_ID, WI_NIC_P3_MINI_STR, WI_INTERSIL}, #endif /* (WLAN_SUPPORT_INTERSIL >= 1) */ {0, NULL, 0}, }; /*==========================================================*/ /* DEFINE: Definition of all local Procedures */ /*==========================================================*/ /************************************************************/ /* DELAY */ /* */ /* Delay in us. */ /************************************************************/ static void DELAY(u_int32_t delay_us) { delay_us = delay_us / 1000; delay_us++; NutSleep(delay_us); } /************************************************************/ /* DumpData */ /* */ /* Dump the Buffer to the Terminal */ /************************************************************/ /* Harald: Not always used, but not sure how. Changed fron static to global and changed name to avoid conflicts */ void DumpWlanData(BYTE * pBuffer, WORD wBufferSize) { u_int16_t x, MaxX = 16; u_int16_t y, MaxY; u_int16_t Size; if (bDebugState != 0) { Size = 0; MaxY = (wBufferSize / 16) + 1; for (y = 0; y < MaxY; y++) { Debug(("%04X: ", (y * 16))); for (x = 0; x < MaxX; x++) { Debug(("%02X ", pBuffer[Size])); Size++; if (Size >= wBufferSize) { Debug(("\r\n")); return; } } Debug(("\r\n")); } } } /************************************************************/ /* wi_cmd */ /************************************************************/ static int wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2) { unsigned long i; /* Harald: Changed from int to long. */ unsigned int s = 0; /* Oliver: Changed from signed to unsigned. */ static volatile int count = 0; if (sc->wi_gone) { return (ENODEV); } /* Harald: Removed this, it makes no sense */ //if (count > 0) { // panic("Hey partner, hold on there!"); //} count++; /* * wait for the busy bit to clear */ for (i = sc->wi_cmd_count; i > 0; i--) { /* 500ms */ if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) { break; } DELAY(1 * 1000); /* 1ms */ } if (i == 0) { Debug(("wi_cmd: busy bit won't clear.\n")); sc->wi_gone = 1; count--; return (ETIMEDOUT); } CSR_WRITE_2(sc, WI_PARAM0, val0); CSR_WRITE_2(sc, WI_PARAM1, val1); CSR_WRITE_2(sc, WI_PARAM2, val2); CSR_WRITE_2(sc, WI_COMMAND, cmd); if (cmd == WI_CMD_INI) { /* * XXX: should sleep here. */ /* Harald: Added UL specifiers, no idea how Michael's ICC accepted this */ DELAY(250UL * 1000UL); /* 250ms delay for init */ } for (i = 0; i < WI_TIMEOUT; i++) { /* * Wait for 'command complete' bit to be * set in the event status register. */ s = CSR_READ_2(sc, WI_EVENT_STAT); if (s & WI_EV_CMD) { /* * Ack the event and read result code. */ s = CSR_READ_2(sc, WI_STATUS); CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); if (s & WI_STAT_CMD_RESULT) { count--; return (EIO); } break; } DELAY(WI_DELAY); } count--; if (i == WI_TIMEOUT) { Debug(("timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s)); if (s == 0xffff) { sc->wi_gone = 1; } return (ETIMEDOUT); } return (0); } /************************************************************/ /* wi_stop */ /************************************************************/ void wi_stop(struct wi_softc *sc, int disable) { struct ieee80211com *ic = &sc->sc_ic; DELAY(100000); ieee80211_new_state(ic, IEEE80211_S_INIT, -1); if (sc->sc_enabled && !sc->wi_gone) { CSR_WRITE_2(sc, WI_INT_EN, 0); wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0); if (disable) { sc->sc_enabled = 0; } } else { if (sc->wi_gone && disable) { /* gone --> not enabled */ sc->sc_enabled = 0; } } sc->sc_tx_timer = 0; sc->sc_scan_timer = 0; } /************************************************************/ /* wi_seek_bap */ /************************************************************/ static int wi_seek_bap(struct wi_softc *sc, int id, int off) { long i; /* Harald: from int to long */ unsigned int status; /* Oliver: from signed to unsigned */ CSR_WRITE_2(sc, WI_SEL0, id); CSR_WRITE_2(sc, WI_OFF0, off); for (i = 0;; i++) { status = CSR_READ_2(sc, WI_OFF0); if ((status & WI_OFF_BUSY) == 0) { break; } if (i == WI_TIMEOUT) { Debug(("timeout in wi_seek to %x/%x\n", id, off)); sc->sc_bap_off = WI_OFF_ERR; /* invalidate */ if (status == 0xffff) { sc->wi_gone = 1; } return ETIMEDOUT; } DELAY(1); } if (status & WI_OFF_ERR) { Debug(("failed in wi_seek to %x/%x\n", id, off)); sc->sc_bap_off = WI_OFF_ERR; /* invalidate */ return EIO; } sc->sc_bap_id = id; sc->sc_bap_off = off; return 0; } /************************************************************/ /* wi_read_bap */ /************************************************************/ static int wi_read_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen) { u_int16_t *ptr; int i, error, cnt; if (buflen == 0) { return 0; } if (id != sc->sc_bap_id || off != sc->sc_bap_off) { if ((error = wi_seek_bap(sc, id, off)) != 0) { return error; } } cnt = (buflen + 1) / 2; ptr = (u_int16_t *) buf; for (i = 0; i < cnt; i++) { *ptr++ = CSR_READ_2(sc, WI_DATA0); } sc->sc_bap_off += cnt * 2; return 0; } /************************************************************/ /* wi_write_bap */ /************************************************************/ static int wi_write_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen) { u_int16_t *ptr; int i, error, cnt; if (buflen == 0) { return 0; } #ifdef WI_HERMES_AUTOINC_WAR again: #endif if (id != sc->sc_bap_id || off != sc->sc_bap_off) { if ((error = wi_seek_bap(sc, id, off)) != 0) { return error; } } cnt = (buflen + 1) / 2; ptr = (u_int16_t *) buf; for (i = 0; i < cnt; i++) { CSR_WRITE_2(sc, WI_DATA0, ptr[i]); } sc->sc_bap_off += cnt * 2; #ifdef WI_HERMES_AUTOINC_WAR /* * According to the comments in the HCF Light code, there is a bug * in the Hermes (or possibly in certain Hermes firmware revisions) * where the chip's internal autoincrement counter gets thrown off * during data writes: the autoincrement is missed, causing one * data word to be overwritten and subsequent words to be written to * the wrong memory locations. The end result is that we could end * up transmitting bogus frames without realizing it. The workaround * for this is to write a couple of extra guard words after the end * of the transfer, then attempt to read then back. If we fail to * locate the guard words where we expect them, we preform the * transfer over again. */ if ((sc->sc_flags & WI_FLAGS_BUG_AUTOINC) && (id & 0xf000) == 0) { CSR_WRITE_2(sc, WI_DATA0, 0x1234); CSR_WRITE_2(sc, WI_DATA0, 0x5678); wi_seek_bap(sc, id, sc->sc_bap_off); sc->sc_bap_off = WI_OFF_ERR; /* invalidate */ if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || CSR_READ_2(sc, WI_DATA0) != 0x5678) { Debug(("detect auto increment bug, try again\n")); goto again; } } #endif return 0; } /************************************************************/ /* wi_read_rid */ /************************************************************/ static int wi_read_rid(struct wi_softc *sc, int rid, void *buf, int *buflenp) { int error, len; u_int16_t ltbuf[2]; /* * Tell the NIC to enter record read mode. */ error = wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_READ, rid, 0, 0); if (error) { return error; } error = wi_read_bap(sc, rid, 0, ltbuf, sizeof(ltbuf)); if (error) { return error; } if (le16toh(ltbuf[1]) != (u_int16_t) rid) { Debug(("record read mismatch, rid=%x, got=%x\n", rid, le16toh(ltbuf[1]))); return EIO; } len = (le16toh(ltbuf[0]) - 1) * 2; /* already got rid */ if (*buflenp < len) { Debug(("record buffer is too small, rid=%x, size=%d, len=%d\n", rid, *buflenp, len)); return ENOSPC; } *buflenp = len; return wi_read_bap(sc, rid, sizeof(ltbuf), buf, len); } /************************************************************/ /* wi_write_rid */ /* */ /* Write a block of data to the Resource Identifier. */ /* */ /* Return: OK or Error cause. */ /************************************************************/ static int wi_write_rid(struct wi_softc *sc, int rid, void *buf, int buflen) { int error; u_int16_t ltbuf[2]; ltbuf[0] = htole16((buflen + 1) / 2 + 1); /* includes rid */ ltbuf[1] = htole16(rid); error = wi_write_bap(sc, rid, 0, ltbuf, sizeof(ltbuf)); if (error) { return error; } error = wi_write_bap(sc, rid, sizeof(ltbuf), buf, buflen); if (error) { return error; } return wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_WRITE, rid, 0, 0); } /************************************************************/ /* wi_write_val */ /* */ /* Write the value to the Resource Identifier. */ /* The function will write only a WORD */ /* */ /* Return: OK or Error cause. */ /************************************************************/ static int wi_write_val(struct wi_softc *sc, int rid, u_int16_t val) { val = htole16(val); return wi_write_rid(sc, rid, &val, sizeof(val)); } /************************************************************/ /* wi_write_txrate */ /************************************************************/ static int wi_write_txrate(struct wi_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; int i; u_int16_t rate; if (ic->ic_fixed_rate < 0) { rate = 0; /* auto */ } else { rate = (ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL) / 2; } /* * rate: 0, 1, 2, 5, 11 */ switch (sc->sc_firmware_type) { case WI_LUCENT: switch (rate) { case 0: /* auto == 11mbps auto */ rate = 3; break; /* * case 1, 2 map to 1, 2 */ case 5: /* 5.5Mbps -> 4 */ rate = 4; break; case 11: /* 11mbps -> 5 */ rate = 5; break; default: break; } break; default: /* * Choose a bit according to this table. * * * * bit | data rate * * ----+------------------- * * 0 | 1Mbps * * 1 | 2Mbps * * 2 | 5.5Mbps * * 3 | 11Mbps */ for (i = 8; i > 0; i >>= 1) { if ((int) rate >= i) { break; } } if (i == 0) { rate = 0xf; /* auto */ } else { rate = i; } break; } return wi_write_val(sc, WI_RID_TX_RATE, rate); } /************************************************************/ /* wi_reset */ /************************************************************/ static int wi_reset(struct wi_softc *sc) { //struct ieee80211com *ic = &sc->sc_ic; /* Harald: Not used */ int i; int error = 0; int tries; /* * Symbol firmware cannot be initialized more than once */ if (sc->sc_firmware_type == WI_SYMBOL && sc->sc_reset) { return (0); } if (sc->sc_firmware_type == WI_SYMBOL) { tries = 1; } else { tries = 3; } for (i = 0; i < tries; i++) { if ((error = wi_cmd(sc, WI_CMD_INI, 0, 0, 0)) == 0) { break; } DELAY(WI_DELAY * 1000); } sc->sc_reset = 1; if (i == tries) { Debug(("init failed\n")); return (error); } CSR_WRITE_2(sc, WI_INT_EN, 0); CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); /* * Calibrate timer */ wi_write_val(sc, WI_RID_TICK_TIME, 8); return (0); } /************************************************************/ /* WLANInterrupt */ /************************************************************/ static void WLANInterrupt(void *p) { NUTDEVICE *dev = (NUTDEVICE *) p; struct wi_softc *sc = (struct wi_softc *) dev->dev_dcb; if ((sc->wi_gone == 1) || (sc->sc_enabled == 0)) { CSR_WRITE_2(sc, WI_INT_EN, 0); CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); return; } /* * Disable interrupts. We will enable the interrupt * later in the RxThread. */ CSR_WRITE_2(sc, WI_INT_EN, 0); sc->EventStatus = CSR_READ_2(sc, WI_EVENT_STAT); NutEventPostFromIrq(&sc->InterruptEvent); } /************************************************************/ /* wi_tx_intr */ /************************************************************/ static void wi_tx_intr(struct wi_softc *sc) { // struct ieee80211com *ic = &sc->sc_ic; /* Harald: Not used. */ int fid, cur; if (sc->wi_gone) { return; } fid = CSR_READ_2(sc, WI_ALLOC_FID); CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); cur = sc->sc_txcur; if (sc->sc_txd[cur].d_fid != fid) { #if (WLAN_ENABLE_TX_FRAME_DUMP >= 1) Debug(("bad alloc %x != %x, cur %d nxt %d\n", fid, sc->sc_txd[cur].d_fid, cur, sc->sc_txnext)); #endif return; } sc->sc_tx_timer = 0; sc->sc_txd[cur].d_len = 0; sc->sc_txcur = cur = (cur + 1) % sc->sc_ntxbuf; if (sc->sc_txd[cur].d_len == 0) { //ifp->if_flags &= ~IFF_OACTIVE; } else { if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, sc->sc_txd[cur].d_fid, 0, 0)) { Debug(("xmit failed\n")); sc->sc_txd[cur].d_len = 0; } else { sc->sc_tx_timer = 5; //ifp->if_timer = 1; } } } /************************************************************/ /* wi_info_intr */ /************************************************************/ static void wi_info_intr(struct wi_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; //struct ifnet *ifp = &ic->ic_if; //int i, len, off; /* Harald: Not used */ int fid; u_int16_t ltbuf[2]; u_int16_t stat; //u_int32_t *ptr; /* Harald: Not used. */ fid = CSR_READ_2(sc, WI_INFO_FID); wi_read_bap(sc, fid, 0, ltbuf, sizeof(ltbuf)); switch (le16toh(ltbuf[1])) { case WI_INFO_LINK_STAT: wi_read_bap(sc, fid, sizeof(ltbuf), &stat, sizeof(stat)); #if (WLAN_ENABLE_LINK_STATUS >= 1) Debug(("wi_info_intr: LINK_STAT 0x%x\n", le16toh(stat))); #endif /* (WLAN_ENABLE_LINK_STATUS >= 1) */ switch (le16toh(stat)) { case WI_INFO_LINK_STAT_CONNECTED: sc->sc_flags &= ~WI_FLAGS_OUTRANGE; if (ic->ic_state == IEEE80211_S_RUN && ic->ic_opmode != IEEE80211_M_IBSS) { break; } /* * FALLTHROUGH */ case WI_INFO_LINK_STAT_AP_CHG: ieee80211_new_state(ic, IEEE80211_S_RUN, -1); break; case WI_INFO_LINK_STAT_AP_INR: sc->sc_flags &= ~WI_FLAGS_OUTRANGE; break; case WI_INFO_LINK_STAT_AP_OOR: if (sc->sc_firmware_type == WI_SYMBOL && sc->sc_scan_timer > 0) { if (wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_HOST_SCAN_RESULTS, 0, 0) != 0) { sc->sc_scan_timer = 0; } break; } if (ic->ic_opmode == IEEE80211_M_STA) { sc->sc_flags |= WI_FLAGS_OUTRANGE; } break; case WI_INFO_LINK_STAT_DISCONNECTED: case WI_INFO_LINK_STAT_ASSOC_FAILED: if (ic->ic_opmode == IEEE80211_M_STA) { ieee80211_new_state(ic, IEEE80211_S_INIT, -1); } break; } break; case WI_INFO_COUNTERS: #if 0 //@@MF later /* * I think we does not need the counter, * therefore it can be take longer to implement it. */ /* * some card versions have a larger stats structure */ len = min(le16toh(ltbuf[0]) - 1, sizeof(sc->sc_stats) / 4); ptr = (u_int32_t *) & sc->sc_stats; off = sizeof(ltbuf); for (i = 0; i < len; i++, off += 2, ptr++) { wi_read_bap(sc, fid, off, &stat, sizeof(stat)); #ifdef WI_HERMES_STATS_WAR if (stat & 0xf000) { stat = ~stat; } #endif *ptr += stat; } ifp->if_collisions = sc->sc_stats.wi_tx_single_retries + sc->sc_stats.wi_tx_multi_retries + sc->sc_stats.wi_tx_retry_limit; #endif /* later */ break; #if 0 //@@MF later case WI_INFO_SCAN_RESULTS: case WI_INFO_HOST_SCAN_RESULTS: wi_scan_result(sc, fid, le16toh(ltbuf[0])); break; #endif /* later */ default: Debug(("wi_info_intr: got fid %x type %x len %d\n", fid, le16toh(ltbuf[1]), le16toh(ltbuf[0]))); break; } CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); } /************************************************************/ /* wi_rx_intr */ /************************************************************/ static NETBUF *wi_rx_intr(struct wi_softc *sc) { // struct ieee80211com *ic = &sc->sc_ic; /* Harald: Not used */ struct wi_frame frmhdr; int fid, len, off; u_int16_t status; int error; NETBUF *nb = 0; LLCS_SNAP_HEADER *pLLCSSNAPHeader; ETHERHDR LABBuffer; ETHERHDR MAC802_3; u_int8_t *pFrameBuffer = 0; /* Harald: Missing init */ u_int8_t *pBytePointer; int copy_size = 0; /* Harald: Missing init */ int size; int IPFrame = 0; if (sc->wi_gone) { Debug(("wi_rx_intr gone\r\n")); return (0); } fid = CSR_READ_2(sc, WI_RX_FID); /* * First read in the frame header */ if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr))) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); Debug(("wi_rx_intr: read fid %x failed\n", fid)); return 0; } /* * Drop undecryptable or packets with receive errors here */ status = le16toh(frmhdr.wi_status); if (status & WI_STAT_ERRSTAT) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); Debug(("wi_rx_intr: fid %x error status %x\n", fid, status)); return 0; } /* * Catch the MAC 802.3 header */ memcpy(MAC802_3.ether_dhost, frmhdr.wi_ehdr.ether_dhost, IEEE80211_ADDR_LEN); memcpy(MAC802_3.ether_shost, frmhdr.wi_ehdr.ether_shost, IEEE80211_ADDR_LEN); /* * The ether_type comes later */ /* * Get the frame length, at this point I don't know * if it is a RFC1024 or RFC894 frame. But Ethernut need * an RFC894 frame! So we will alloc the NetBuffer later. */ len = le16toh(frmhdr.wi_dat_len); off = sizeof(frmhdr); /* * Sometimes the PRISM2.x returns bogusly large frames. Except * in monitor mode, just throw them away. */ if (len > WLAN_MAX_ETHERNET_FRAME_LEN) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); Debug(("wi_rx_intr: oversized packet\n")); return (0); } /* * First read the LookAheadBuffer */ error = wi_read_bap(sc, fid, off, &LABBuffer, sizeof(LABBuffer)); off += sizeof(LABBuffer); if (error == 0) { /* * Check if this is a 802.2 LLC 802.2 SNAP IP-Header RFC1024 */ pLLCSSNAPHeader = (LLCS_SNAP_HEADER *) & LABBuffer; if ((pLLCSSNAPHeader->DSAPSSAP == 0xAAAA) && (pLLCSSNAPHeader->Control == 0x0003) && (pLLCSSNAPHeader->MustZero == 0x0000)) { if ((pLLCSSNAPHeader->Type == 0x0608) || (pLLCSSNAPHeader->Type == 0x0008)) { #if (WLAN_ENABLE_RX_FRAME_DUMP >= 1) Debug(("RFC1024 IP-Frame\n")); #endif IPFrame = 1; MAC802_3.ether_type = pLLCSSNAPHeader->Type; /* * Now we can calculate the wFrameLength. * It is a RFC1042 Frame, and we must discard the: * DSAP, SSAP, cntl, and the org. code * 1 + 1 + 1 + 3 = 6 */ len -= 6; /* * The Ethernut need the dest and source address, * therefore add 12 again. */ len += 12; /* * The MAC can not handle odd counts, test it. * */ len += 1; len &= ~1; /* * Now alloc the NetBuffer for Ethernut. */ nb = NutNetBufAlloc(0, NBAF_DATALINK, len); if (nb != 0) { pFrameBuffer = nb->nb_dl.vp; /* * We must copy wFrameLength out of the MAC */ copy_size = len; /* * Copy the MAC 802.3 Header */ memcpy(pFrameBuffer, &MAC802_3, sizeof(MAC802_3)); pFrameBuffer += sizeof(MAC802_3); copy_size -= sizeof(MAC802_3); /* This was already in the WLANHeader */ /* * Now we have some data in our LABBuffer which we need */ pBytePointer = (BYTE *) & LABBuffer; /* * jump over the header */ pBytePointer += sizeof(LLCS_SNAP_HEADER); /* * and copy the rest */ size = sizeof(LABBuffer) - sizeof(LLCS_SNAP_HEADER); memcpy(pFrameBuffer, pBytePointer, size); /* * Change the pointer for the "global copy" */ pFrameBuffer += size; copy_size -= size; /* This was already in the LookAheadBuffer */ } else { IPFrame = 0; } } /* only ARP and IP */ } else { /* * Check if this is a RFX894 Frame */ if ((LABBuffer.ether_type == 0x0608) || (LABBuffer.ether_type == 0x0008)) { #if (WLAN_ENABLE_RX_FRAME_DUMP >= 1) Debug(("RFC894 IP-Frame\n")); #endif IPFrame = 1; /* * The MAC can not handle odd counts, test it. * */ len += 1; len &= ~1; /* * The FrameLength is now the correct length for an RFC894 Frame, * Now alloc the NetBuffer for Ethernut. */ nb = NutNetBufAlloc(0, NBAF_DATALINK, len); if (nb != 0) { pFrameBuffer = nb->nb_dl.vp; /* * We must copy wFrameLength out of the MAC */ copy_size = len; /* * The LookAheadBuffer is the MAC-Header */ memcpy(pFrameBuffer, &LABBuffer, sizeof(LABBuffer)); pFrameBuffer += sizeof(LABBuffer); copy_size -= sizeof(LABBuffer); /* This was our LookAheadBuffer */ } else { IPFrame = 0; } /* NutNetBufAlloc */ } /* RFC894 Frame */ } /* Error read LookAheadBuffer */ /* * Do the rest here, "global copy" */ if (IPFrame == 1) { error = wi_read_bap(sc, fid, off, pFrameBuffer, copy_size); if (error != 0) { NutNetBufFree(nb); nb = NULL; } else { #if (WLAN_ENABLE_RX_FRAME_DUMP >= 1) Debug(("RxFrame: %d\n", len)); DumpWlanData(nb->nb_dl.vp, nb->nb_dl.sz); #endif } } } /* * At least, send a ack for the event */ CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); return (nb); } /************************************************************/ /* RxThread */ /************************************************************/ THREAD(RxThread, arg) { NUTDEVICE *dev; IFNET *ifn; NETBUF *nb; struct wi_softc *sc; u_int16_t EventStatus; dev = arg; ifn = (IFNET *) dev->dev_icb; sc = (struct wi_softc *) dev->dev_dcb; for (;;) { /* * Wait for the arrival of new packets or check * the receiver every two second. */ NutEventWait(&sc->InterruptEvent, 2000); EventStatus = sc->EventStatus; /* * Clear the sc-EventStatus. If the next time the * EventStatus is 0, it was the timeout and not * an interrupt. */ sc->EventStatus = 0; if (EventStatus != 0) { if (EventStatus & WI_EV_RX) { WI_LOCK(sc); nb = wi_rx_intr(sc); WI_UNLOCK(sc); if (nb != 0) { (*ifn->if_recv) (dev, nb); } } if (EventStatus & WI_EV_ALLOC) { WI_LOCK(sc); wi_tx_intr(sc); WI_UNLOCK(sc); } if (EventStatus & WI_EV_INFO) { WI_LOCK(sc); wi_info_intr(sc); WI_UNLOCK(sc); } if (EventStatus & WI_EV_TX_EXC) { Debug(("WI_EV_TX_EXC\n")); } CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); } else { /* * It was the timeout, check if the card is alive */ if ((sc->sc_enabled == 1) && (sc->wi_gone == 1)) { /* * The card was enabled but we has lost the card, reinit... */ Debug(("Houston, we have a problem...\n")); sc->wi_gone = 0; wlandrv_Init(dev); } } /* (EventStatus != 0) */ } /* for */ } /************************************************************/ /* wi_alloc_fid */ /************************************************************/ static int wi_alloc_fid(struct wi_softc *sc, int len, int *idp) { unsigned long i; /* Harald: Changed from int to long */ /* Oliver: Changed from signed to unsigned */ if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) { Debug(("failed to allocate %d bytes on NIC\n", len)); return ENOMEM; } for (i = 0; i < WI_TIMEOUT; i++) { if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) { break; } if (i == WI_TIMEOUT) { Debug(("timeout in alloc\n")); return ETIMEDOUT; } DELAY(1); } *idp = CSR_READ_2(sc, WI_ALLOC_FID); CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); return 0; } /************************************************************/ /* wi_write_ssid */ /************************************************************/ static int wi_write_ssid(struct wi_softc *sc, int rid, u_int8_t * buf, int buflen) { struct wi_ssid ssid; if (buflen > IEEE80211_NWID_LEN) { return ENOBUFS; } memset(&ssid, 0, sizeof(ssid)); ssid.wi_len = htole16(buflen); memcpy(ssid.wi_ssid, buf, buflen); return wi_write_rid(sc, rid, &ssid, sizeof(ssid)); } /************************************************************/ /* wi_read_nicid */ /************************************************************/ static void wi_read_nicid(struct wi_softc *sc) { struct wi_card_ident *id; char *p; int len; u_int16_t ver[4]; /* * getting chip identity */ memset(ver, 0, sizeof(ver)); len = sizeof(ver); wi_read_rid(sc, WI_RID_CARD_ID, ver, &len); Debug(("using ")); sc->sc_firmware_type = WI_NOTYPE; for (id = wi_card_ident; id->card_name != NULL; id++) { if (le16toh(ver[0]) == id->card_id) { Debug(("%s", id->card_name)); sc->sc_firmware_type = id->firm_type; break; } } if (sc->sc_firmware_type == WI_NOTYPE) { if (le16toh(ver[0]) & 0x8000) { Debug(("Unknown PRISM2 chip")); sc->sc_firmware_type = WI_INTERSIL; } else { Debug(("Unknown Lucent chip")); sc->sc_firmware_type = WI_LUCENT; } } /* * get primary firmware version (Only Prism chips) */ if (sc->sc_firmware_type != WI_LUCENT) { memset(ver, 0, sizeof(ver)); len = sizeof(ver); wi_read_rid(sc, WI_RID_PRI_IDENTITY, ver, &len); sc->sc_pri_firmware_ver = le16toh(ver[2]) * 10000 + le16toh(ver[3]) * 100 + le16toh(ver[1]); } /* * get station firmware version */ memset(ver, 0, sizeof(ver)); len = sizeof(ver); wi_read_rid(sc, WI_RID_STA_IDENTITY, ver, &len); sc->sc_sta_firmware_ver = le16toh(ver[2]) * 10000 + le16toh(ver[3]) * 100 + le16toh(ver[1]); if (sc->sc_firmware_type == WI_INTERSIL && (sc->sc_sta_firmware_ver == 10102 || sc->sc_sta_firmware_ver == 20102)) { char ident[12]; memset(ident, 0, sizeof(ident)); len = sizeof(ident); /* * value should be the format like "V2.00-11" */ if (wi_read_rid(sc, WI_RID_SYMBOL_IDENTITY, ident, &len) == 0 && *(p = (char *) ident) >= 'A' && p[2] == '.' && p[5] == '-' && p[8] == '\0') { sc->sc_firmware_type = WI_SYMBOL; sc->sc_sta_firmware_ver = (p[1] - '0') * 10000 + (p[3] - '0') * 1000 + (p[4] - '0') * 100 + (p[6] - '0') * 10 + (p[7] - '0'); } } Debug(("\n")); Debug(("%s Firmware: ", sc->sc_firmware_type == WI_LUCENT ? "Lucent" : (sc->sc_firmware_type == WI_SYMBOL ? "Symbol" : "Intersil"))); if (sc->sc_firmware_type != WI_LUCENT) { /* XXX */ Debug(("Primary (%ld.%ld.%ld), ", sc->sc_pri_firmware_ver / 10000, (sc->sc_pri_firmware_ver % 10000) / 100, sc->sc_pri_firmware_ver % 100)); } Debug(("Station (%ld.%ld.%ld)\n", sc->sc_sta_firmware_ver / 10000, (sc->sc_sta_firmware_ver % 10000) / 100, sc->sc_sta_firmware_ver % 100)); } /************************************************************/ /* wi_write_wep */ /************************************************************/ static int wi_write_wep(struct wi_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; int error = 0; int i, keylen; u_int16_t val; struct wi_key wkey[IEEE80211_WEP_NKID]; switch (sc->sc_firmware_type) { #if (WLAN_SUPPORT_LUCENT >= 1) case WI_LUCENT: val = (ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0; error = wi_write_val(sc, WI_RID_ENCRYPTION, val); if (error) break; error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_wep_txkey); if (error) break; memset(wkey, 0, sizeof(wkey)); for (i = 0; i < IEEE80211_WEP_NKID; i++) { keylen = ic->ic_nw_keys[i].wk_len; wkey[i].wi_keylen = htole16(keylen); memcpy(wkey[i].wi_keydat, ic->ic_nw_keys[i].wk_key, keylen); } error = wi_write_rid(sc, WI_RID_DEFLT_CRYPT_KEYS, wkey, sizeof(wkey)); break; #endif /* (WLAN_SUPPORT_LUCENT >= 1) */ #if ((WLAN_SUPPORT_INTERSIL >= 1) || (WLAN_SUPPORT_SYMBOL >= 1)) case WI_INTERSIL: case WI_SYMBOL: if (ic->ic_flags & IEEE80211_F_WEPON) { /* * ONLY HWB3163 EVAL-CARD Firmware version * less than 0.8 variant2 * * If promiscuous mode disable, Prism2 chip * does not work with WEP . * It is under investigation for details. * (ichiro@netbsd.org) */ if (sc->sc_firmware_type == WI_INTERSIL && sc->sc_sta_firmware_ver < 802) { /* * firm ver < 0.8 variant 2 */ wi_write_val(sc, WI_RID_PROMISC, 1); } wi_write_val(sc, WI_RID_CNFAUTHMODE, sc->sc_cnfauthmode); val = PRIVACY_INVOKED | EXCLUDE_UNENCRYPTED; /* * Encryption firmware has a bug for HostAP mode. */ if (sc->sc_firmware_type == WI_INTERSIL && ic->ic_opmode == IEEE80211_M_HOSTAP) { val |= HOST_ENCRYPT; } } else { wi_write_val(sc, WI_RID_CNFAUTHMODE, IEEE80211_AUTH_OPEN); val = HOST_ENCRYPT | HOST_DECRYPT; } error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val); if (error) { break; } error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY, ic->ic_wep_txkey); if (error) { break; } /* * It seems that the firmware accept 104bit key only if * all the keys have 104bit length. We get the length of * the transmit key and use it for all other keys. * Perhaps we should use software WEP for such situation. */ keylen = ic->ic_nw_keys[ic->ic_wep_txkey].wk_len; if (keylen > IEEE80211_WEP_KEYLEN) { keylen = 13; /* 104bit keys */ } else { keylen = IEEE80211_WEP_KEYLEN; } for (i = 0; i < IEEE80211_WEP_NKID; i++) { error = wi_write_rid(sc, WI_RID_P2_CRYPT_KEY0 + i, ic->ic_nw_keys[i].wk_key, keylen); if (error) { break; } } break; #endif /* ((WLAN_SUPPORT_INTERSIL >= 1) || (WLAN_SUPPORT_SYMBOL >= 1)) */ } return error; } /*==========================================================*/ /* DEFINE: All code exported */ /*==========================================================*/ /************************************************************/ /* wlandrv_ProbeDevice */ /* */ /* Make a HW-Reset and test if a card is available. */ /* */ /* Return: ERROR, OK */ /************************************************************/ int wlandrv_ProbeDevice(void) { int error = -1; u_int16_t Value; /* * Set RESET */ RESET_EN_PORT |= RESET_BIT; RESET_PORT |= RESET_BIT; NutSleep(100); /* * Remove RESET */ RESET_PORT &= ~RESET_BIT; NutSleep(500); /* * Set the MAC into I/O mode */ pcmcia_WriteMem(WI_COR_OFFSET, WI_COR_VALUE); NutSleep(100); /* * Test if we can found a card */ pcmcia_WriteReg(WI_HFA384X_SWSUPPORT0_OFF, WI_PRISM2STA_MAGIC); Value = pcmcia_ReadReg(WI_HFA384X_SWSUPPORT0_OFF); if (Value == WI_PRISM2STA_MAGIC) { /* * We found a card :-) */ error = 0; } return (error); } /************************************************************/ /* wlandrv_Attach */ /* */ /* Catch all information about the card which */ /* could be found. And set some default parameter */ /* */ /* Return: ERROR, OK */ /************************************************************/ int wlandrv_Attach(device_t dev) { struct wi_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_rateset *rs; int i, nrates, buflen; u_int16_t val; u_int8_t ratebuf[2 + IEEE80211_RATE_SIZE]; //u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = { // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //}; /* Harald: This is unused */ int error; bDebugState = 2; sc->wi_cmd_count = 500; ic->ic_fixed_rate = -1; /* auto */ /* * Reset the NIC. */ if (wi_reset(sc) != 0) { return ENXIO; /* XXX */ } /* * Read the station address. * And do it twice. I've seen PRISM-based cards that return * an error when trying to read it the first time, which causes * the probe to fail. */ buflen = IEEE80211_ADDR_LEN; error = wi_read_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, &buflen); if (error != 0) { buflen = IEEE80211_ADDR_LEN; error = wi_read_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, &buflen); } if (error != 0) { Debug(("mac read failed %d\n", error)); return (error); } Debug(("802.11 address: %02X-%02X-%02X-%02X-%02X-%02X\n", ic->ic_myaddr[0], ic->ic_myaddr[1], ic->ic_myaddr[2], ic->ic_myaddr[3], ic->ic_myaddr[4], ic->ic_myaddr[5])); /* * Read NIC identification */ wi_read_nicid(sc); ic->ic_opmode = IEEE80211_M_STA; ic->ic_caps = IEEE80211_C_PMGT | IEEE80211_C_AHDEMO; ic->ic_state = IEEE80211_S_INIT; /* * Query the card for available channels. */ buflen = sizeof(val); if (wi_read_rid(sc, WI_RID_CHANNEL_LIST, &val, &buflen) != 0) { val = htole16(0x1fff); /* assume 1-13 */ } if (val == 0) { Debug(("wi_attach: no available channels listed!")); } ic->ChannelList = (val << 1); /* * Read the default channel from the NIC. This may vary * depending on the country where the NIC was purchased, so * we can't hard-code a default and expect it to work for * everyone. * * If no channel is specified, let the 802.11 code select. */ buflen = sizeof(val); if (wi_read_rid(sc, WI_RID_OWN_CHNL, &val, &buflen) == 0) { val = le16toh(val); i = (1 << val); if ((val > 16) || ((ic->ChannelList & i) == 0)) { Debug(("wi_attach: invalid own channel %d!", val)); } ic->ic_ibss_chan = val; } else { Debug(("WI_RID_OWN_CHNL failed, using first channel!\n")); ic->ic_ibss_chan = 1; } /* * Set flags based on firmware version. */ switch (sc->sc_firmware_type) { #if (WLAN_SUPPORT_LUCENT >= 1) case WI_LUCENT: sc->sc_ntxbuf = 1; sc->sc_flags |= WI_FLAGS_HAS_SYSSCALE; #ifdef WI_HERMES_AUTOINC_WAR /* * XXX: not confirmed, but never seen for recent firmware */ if (sc->sc_sta_firmware_ver < 40000) { sc->sc_flags |= WI_FLAGS_BUG_AUTOINC; } #endif if (sc->sc_sta_firmware_ver >= 60000) { sc->sc_flags |= WI_FLAGS_HAS_MOR; } if (sc->sc_sta_firmware_ver >= 60006) { ic->ic_caps |= IEEE80211_C_IBSS; ic->ic_caps |= IEEE80211_C_MONITOR; } sc->sc_ibss_port = htole16(1); sc->sc_min_rssi = WI_LUCENT_MIN_RSSI; sc->sc_max_rssi = WI_LUCENT_MAX_RSSI; sc->sc_dbm_offset = WI_LUCENT_DBM_OFFSET; break; #endif /* (WLAN_SUPPORT_LUCENT >= 1) */ #if (WLAN_SUPPORT_INTERSIL >= 1) case WI_INTERSIL: sc->sc_ntxbuf = WI_NTXBUF; sc->sc_flags |= WI_FLAGS_HAS_FRAGTHR; sc->sc_flags |= WI_FLAGS_HAS_ROAMING; sc->sc_flags |= WI_FLAGS_HAS_SYSSCALE; /* * Old firmware are slow, so give peace a chance. */ if (sc->sc_sta_firmware_ver < 10000) { sc->wi_cmd_count = 5000; } if (sc->sc_sta_firmware_ver > 10101) { sc->sc_flags |= WI_FLAGS_HAS_DBMADJUST; } if (sc->sc_sta_firmware_ver >= 800) { ic->ic_caps |= IEEE80211_C_IBSS; ic->ic_caps |= IEEE80211_C_MONITOR; } /* * version 0.8.3 and newer are the only ones that are known * to currently work. Earlier versions can be made to work, * at least according to the Linux driver. */ if (sc->sc_sta_firmware_ver >= 803) { ic->ic_caps |= IEEE80211_C_HOSTAP; } sc->sc_ibss_port = htole16(0); sc->sc_min_rssi = WI_PRISM_MIN_RSSI; sc->sc_max_rssi = WI_PRISM_MAX_RSSI; sc->sc_dbm_offset = WI_PRISM_DBM_OFFSET; break; #endif /* (WLAN_SUPPORT_INTERSIL >= 1) */ #if (WLAN_SUPPORT_SYMBOL >= 1) case WI_SYMBOL: sc->sc_ntxbuf = 1; sc->sc_flags |= WI_FLAGS_HAS_DIVERSITY; if (sc->sc_sta_firmware_ver >= 25000) { ic->ic_caps |= IEEE80211_C_IBSS; } sc->sc_ibss_port = htole16(4); sc->sc_min_rssi = WI_PRISM_MIN_RSSI; sc->sc_max_rssi = WI_PRISM_MAX_RSSI; sc->sc_dbm_offset = WI_PRISM_DBM_OFFSET; break; #endif /* (WLAN_SUPPORT_SYMBOL >= 1) */ } /* * Find out if we support WEP on this card. */ buflen = sizeof(val); if (wi_read_rid(sc, WI_RID_WEP_AVAIL, &val, &buflen) == 0 && val != htole16(0)) { ic->ic_caps |= IEEE80211_C_WEP; } /* * Find supported rates. */ buflen = sizeof(ratebuf); rs = &ic->ic_sup_rates[IEEE80211_MODE_11B]; if (wi_read_rid(sc, WI_RID_DATA_RATES, ratebuf, &buflen) == 0) { nrates = le16toh(*(u_int16_t *) ratebuf); if (nrates > IEEE80211_RATE_MAXSIZE) { nrates = IEEE80211_RATE_MAXSIZE; } rs->rs_nrates = 0; for (i = 0; i < nrates; i++) { if (ratebuf[2 + i]) { rs->rs_rates[rs->rs_nrates++] = ratebuf[2 + i]; } } } else { /* * XXX fallback on error? */ rs->rs_nrates = 0; } buflen = sizeof(val); if ((sc->sc_flags & WI_FLAGS_HAS_DBMADJUST) && wi_read_rid(sc, WI_RID_DBM_ADJUST, &val, &buflen) == 0) { sc->sc_dbm_offset = le16toh(val); } sc->sc_max_datalen = 2304; sc->sc_system_scale = 1; sc->sc_cnfauthmode = IEEE80211_AUTH_OPEN; sc->sc_roaming_mode = WI_DEFAULT_ROAMING; sc->sc_portnum = WI_DEFAULT_PORT; sc->sc_authtype = WI_DEFAULT_AUTHTYPE; /* * Init DeviceSemaphore */ NutEventPost(&hDeviceSemaphore); return (0); } /************************************************************/ /* wlandrv_Init */ /************************************************************/ void wlandrv_Init(device_t dev) { struct wi_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; int i; int error = 0, wasenabled; WI_LOCK(sc); if (sc->wi_gone) { WI_UNLOCK(sc); return; } if ((wasenabled = sc->sc_enabled)) { wi_stop(sc, 1); } wi_reset(sc); /* * common 802.11 configuration */ ic->ic_flags &= ~IEEE80211_F_IBSSON; sc->sc_flags &= ~WI_FLAGS_OUTRANGE; switch (ic->ic_opmode) { case IEEE80211_M_STA: wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_BSS); break; case IEEE80211_M_IBSS: wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_ibss_port); ic->ic_flags |= IEEE80211_F_IBSSON; break; case IEEE80211_M_AHDEMO: wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_ADHOC); break; case IEEE80211_M_HOSTAP: /* * For PRISM cards, override the empty SSID, because in * HostAP mode the controller will lock up otherwise. */ if (sc->sc_firmware_type == WI_INTERSIL && ic->ic_des_esslen == 0) { ic->ic_des_essid[0] = ' '; ic->ic_des_esslen = 1; } wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_HOSTAP); break; case IEEE80211_M_MONITOR: /* Harald: Should be handled. */ #if 0 // @@MF this mode is not working with my card if (sc->sc_firmware_type == WI_LUCENT) { wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_ADHOC); } error = wi_cmd(sc, WI_CMD_DEBUG | (WI_TEST_MONITOR << 8), 0, 0, 0); #endif break; } /* * Intersil interprets this RID as joining ESS even in IBSS mode */ if (sc->sc_firmware_type == WI_LUCENT && (ic->ic_flags & IEEE80211_F_IBSSON) && ic->ic_des_esslen > 0) { wi_write_val(sc, WI_RID_CREATE_IBSS, 1); } else { wi_write_val(sc, WI_RID_CREATE_IBSS, 0); } #if 0 //@@MF later wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval); #endif /* later */ wi_write_ssid(sc, WI_RID_DESIRED_SSID, ic->ic_des_essid, ic->ic_des_esslen); wi_write_val(sc, WI_RID_OWN_CHNL, ic->ic_ibss_chan); wi_write_ssid(sc, WI_RID_OWN_SSID, ic->ic_des_essid, ic->ic_des_esslen); wi_write_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, IEEE80211_ADDR_LEN); wi_write_val(sc, WI_RID_PM_ENABLED, (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); /* * not yet common 802.11 configuration */ wi_write_val(sc, WI_RID_MAX_DATALEN, sc->sc_max_datalen); #if 0 //@@MF later wi_write_val(sc, WI_RID_RTS_THRESH, ic->ic_rtsthreshold); if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR) { wi_write_val(sc, WI_RID_FRAG_THRESH, ic->ic_fragthreshold); } #endif /* later */ /* * driver specific 802.11 configuration */ if (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE) { wi_write_val(sc, WI_RID_SYSTEM_SCALE, sc->sc_system_scale); } if (sc->sc_flags & WI_FLAGS_HAS_ROAMING) { wi_write_val(sc, WI_RID_ROAMING_MODE, sc->sc_roaming_mode); } if (sc->sc_flags & WI_FLAGS_HAS_MOR) { wi_write_val(sc, WI_RID_MICROWAVE_OVEN, sc->sc_microwave_oven); } wi_write_txrate(sc); #if 0 //@@MF later /* * What is a nodename ??? :-( */ wi_write_ssid(sc, WI_RID_NODENAME, sc->sc_nodename, sc->sc_nodelen); #endif /* later */ #if 0 //@@MF later /* * Does we realy need HOSTAP on Ethernut?? */ if (ic->ic_opmode == IEEE80211_M_HOSTAP && sc->sc_firmware_type == WI_INTERSIL) { wi_write_val(sc, WI_RID_OWN_BEACON_INT, ic->ic_lintval); wi_write_val(sc, WI_RID_BASIC_RATE, 0x03); /* 1, 2 */ wi_write_val(sc, WI_RID_SUPPORT_RATE, 0x0f); /* 1, 2, 5.5, 11 */ wi_write_val(sc, WI_RID_DTIM_PERIOD, 1); } #endif /* later */ /* * Initialize promisc mode. * Being in the Host-AP mode causes a great * deal of pain if primisc mode is set. * Therefore we avoid confusing the firmware * and always reset promisc mode in Host-AP * mode. Host-AP sees all the packets anyway. */ if ((ic->ic_opmode != IEEE80211_M_HOSTAP) && (sc->PromiscuousMode != 0)) { wi_write_val(sc, WI_RID_PROMISC, 1); } else { wi_write_val(sc, WI_RID_PROMISC, 0); } /* * Configure WEP. */ if (ic->ic_caps & IEEE80211_C_WEP) { wi_write_wep(sc); } #if 0 //@@MF later /* * Set multicast filter. */ wi_write_multi(sc); #endif /* later */ /* * Allocate fids for the card */ if (sc->sc_firmware_type != WI_SYMBOL || !wasenabled) { sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame); if (sc->sc_firmware_type == WI_SYMBOL) { sc->sc_buflen = 1585; /* XXX */ } for (i = 0; i < sc->sc_ntxbuf; i++) { error = wi_alloc_fid(sc, sc->sc_buflen, &sc->sc_txd[i].d_fid); if (error) { Debug(("tx buffer allocation failed (error %u)\n", error)); goto out; } sc->sc_txd[i].d_len = 0; } } sc->sc_txcur = sc->sc_txnext = 0; /* * Enable desired port */ wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0); sc->sc_enabled = 1; if (ic->ic_opmode == IEEE80211_M_AHDEMO || ic->ic_opmode == IEEE80211_M_MONITOR || ic->ic_opmode == IEEE80211_M_HOSTAP) { ieee80211_new_state(ic, IEEE80211_S_RUN, -1); } /*************************************************************************/ /* Set interrupt and start RxThread */ /*************************************************************************/ if (sc->InterruptInitDone == 0) { sc->InterruptInitDone = 1; /* * Install WLAN interrupt */ PORTE |= 0x80; /* Enable PullUp */ error = NutRegisterIrqHandler(&sig_INTERRUPT7, WLANInterrupt, dev); if (error == FALSE) { EICR |= 0x80; // falling edge sbi(EIMSK, INT7); /* * Start the receiver thread. */ NutThreadCreate("rxi7", RxThread, dev, 640); } } /*************************************************************************/ /* * Enable interrupts */ CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); if (!wasenabled && ic->ic_opmode == IEEE80211_M_HOSTAP && sc->sc_firmware_type == WI_INTERSIL) { /* * XXX: some card need to be re-enabled for hostap */ wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0); wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0); } #if 0 //@@MF later /* * This is only for IEEE80211_M_STA */ if (ic->ic_opmode == IEEE80211_M_STA && ((ic->ic_flags & IEEE80211_F_DESBSSID) || ic->ic_des_chan != IEEE80211_CHAN_ANYC)) { memset(&join, 0, sizeof(join)); if (ic->ic_flags & IEEE80211_F_DESBSSID) IEEE80211_ADDR_COPY(&join.wi_bssid, ic->ic_des_bssid); if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) join.wi_chan = htole16(ieee80211_chan2ieee(ic, ic->ic_des_chan)); /* * Lucent firmware does not support the JOIN RID. */ if (sc->sc_firmware_type != WI_LUCENT) wi_write_rid(sc, WI_RID_JOIN_REQ, &join, sizeof(join)); } #endif /* later */ WI_UNLOCK(sc); return; out: if (error) { Debug(("interface not running\n")); wi_stop(sc, 1); } WI_UNLOCK(sc); Debug(("wi_init: return %d\n", error)); return; } /************************************************************/ /* wlandrv_PutPacket */ /************************************************************/ int wlandrv_PutPacket(NUTDEVICE * dev, NETBUF * nb) { int error = -1; struct wi_softc *sc = (struct wi_softc *) dev->dev_dcb; struct wi_frame frmhdr; LLCS_SNAP_HEADER LLCSSNAPHeader; ETHERHDR *pMAC8023Header; int cur, fid, off, len; if (sc->wi_gone) { Debug(("wlandrv_PutPacket gone\r\n")); return (-1); } /* * Calculate the number of bytes to be send. Do not * send packets larger than 1518 bytes. */ len = nb->nb_dl.sz + nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz; if (len > 1518) { return (-1); } else { WI_LOCK(sc); /* * This is a RFC894 Frame from Ethernut */ #if (WLAN_ENABLE_TX_FRAME_DUMP >= 1) Debug(("TxFrame: %d\n", len)); DumpWlanData(nb->nb_dl.vp, len); #endif /* * But we must send a RFC1024 Frame. * ETHERHDR is not part of an RFC1024 Frame */ len = len - sizeof(ETHERHDR); /* * Add RFC1024 Header */ len += sizeof(LLCS_SNAP_HEADER); /* * I do not know what I do here, therefore clear the header * and to hope for, the card will do the rest? */ memset(&frmhdr, 0, sizeof(frmhdr)); /* * Copy the Ethernut MAC 802.3 Dest/Source Address into the WLANHeader */ pMAC8023Header = (ETHERHDR *) nb->nb_dl.vp; frmhdr.wi_dat_len = len; memcpy(frmhdr.wi_ehdr.ether_dhost, pMAC8023Header->ether_dhost, IEEE80211_ADDR_LEN); memcpy(frmhdr.wi_ehdr.ether_shost, pMAC8023Header->ether_shost, IEEE80211_ADDR_LEN); frmhdr.wi_ehdr.ether_type = SWAP(len); /* * Prepare the RFC1024 Header */ LLCSSNAPHeader.DSAPSSAP = 0xAAAA; LLCSSNAPHeader.Control = 0x0003; LLCSSNAPHeader.MustZero = 0x0000; LLCSSNAPHeader.Type = pMAC8023Header->ether_type; /* * Get the TX-BUffer-ID */ cur = sc->sc_txcur; fid = sc->sc_txd[cur].d_fid; /* * And Copy the WLANHeader */ error = wi_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)); if (error == 0) { off = sizeof(frmhdr); /* * Now copy the RFC1024 Header */ error = wi_write_bap(sc, fid, off, &LLCSSNAPHeader, sizeof(LLCSSNAPHeader)); if (error == 0) { off += sizeof(LLCSSNAPHeader); /* * Copy the rest of the Ethernut Data */ if ((error == 0) && (nb->nb_nw.sz != 0)) { error = wi_write_bap(sc, fid, off, nb->nb_nw.vp, nb->nb_nw.sz); off += nb->nb_nw.sz; } if ((error == 0) && (nb->nb_tp.sz != 0)) { error = wi_write_bap(sc, fid, off, nb->nb_tp.vp, nb->nb_tp.sz); off += nb->nb_tp.sz; } if ((error == 0) && (nb->nb_ap.sz != 0)) { error = wi_write_bap(sc, fid, off, nb->nb_ap.vp, nb->nb_ap.sz); } if (error == 0) { //@@MF sc->sc_txd[cur].d_len = off; error = wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, fid, 0, 0); if (error != 0) { Debug(("xmit failed\n")); sc->sc_txd[cur].d_len = 0; } sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf; } } } WI_UNLOCK(sc); } return (error); } /************************************************************/ /* wlandrv_IOCTL */ /************************************************************/ int wlandrv_IOCTL(NUTDEVICE * dev, int req, void *conf) { int error = 0; int len; int i; u_int16_t DataBuffer[6]; struct wi_softc *sc = (struct wi_softc *) dev->dev_dcb; struct ieee80211com *ic = &sc->sc_ic; WLAN_CONFIG *pWLANConfig; WLAN_STATUS *pWLANStatus; u_int8_t BSSIDAddress[6]; if ((sc->wi_gone) || (conf == 0)) { error = -1; } else { switch (req) { /*****************************************************/ /* WLAN_IOCTL_GET_MAC_ADDRESS */ /*****************************************************/ case WLAN_IOCTL_GET_MAC_ADDRESS:{ memcpy(conf, ic->ic_myaddr, IEEE80211_ADDR_LEN); break; } /* WLAN_IOCTL_GET_MAC_ADDRESS */ /*****************************************************/ /* WLAN_IOCTL_SET_CONFIG */ /*****************************************************/ case WLAN_IOCTL_SET_CONFIG:{ pWLANConfig = (WLAN_CONFIG *) conf; if ((pWLANConfig != NULL) && (pWLANConfig->Size == sizeof(WLAN_CONFIG))) { WI_LOCK(sc); /* Stop WLAN controller */ wi_stop(sc, 1); /* Set mode */ switch (pWLANConfig->Mode) { case WLAN_MODE_STOP:{ WI_UNLOCK(sc); return (0); break; } case WLAN_MODE_STATION:{ ic->ic_opmode = IEEE80211_M_STA; break; } case WLAN_MODE_ADHOC:{ ic->ic_opmode = IEEE80211_M_IBSS; break; } default:{ error = -1; break; } } if (error == 0) { /* Set SSID */ len = strlen(pWLANConfig->Networkname); memcpy(ic->ic_des_essid, pWLANConfig->Networkname, len); ic->ic_des_esslen = len; /* Enable WEP */ switch (pWLANConfig->UseWEP) { case WLAN_USE_NO_WEP:{ ic->ic_flags &= ~IEEE80211_F_WEPON; break; } case WLAN_USE_64BIT_WEP: case WLAN_USE_128BIT_WEP:{ ic->ic_flags |= IEEE80211_F_WEPON; break; } default:{ error = -1; break; } } /* Set WEP tx key */ ic->ic_wep_txkey = pWLANConfig->UseWEPTxKey; /* Set WEP keys */ for (i = 0; i < WLAN_WEP_MAX_KEY_COUNT; i++) { ic->ic_nw_keys[i].wk_len = pWLANConfig->WEPKey[i].KeyLen; memcpy(ic->ic_nw_keys[i].wk_key, pWLANConfig->WEPKey[i].Key, WLAN_WEP_MAX_KEY_SIZE); } } WI_UNLOCK(sc); if (error == 0) { wlandrv_Init(dev); } } else { error = -1; } break; } /* WLAN_IOCTL_SET_CONFIG */ /*****************************************************/ /* WLAN_IOCTL_GET_STATUS */ /*****************************************************/ case WLAN_IOCTL_GET_STATUS:{ pWLANStatus = (WLAN_STATUS *) conf; if ((pWLANStatus != NULL) && (pWLANStatus->Size == sizeof(WLAN_STATUS))) { WI_LOCK(sc); memset(pWLANStatus, 0x00, sizeof(WLAN_STATUS)); pWLANStatus->Size = sizeof(WLAN_STATUS); /* * PortStatus */ memset(DataBuffer, 0, sizeof(DataBuffer)); len = sizeof(DataBuffer); error = wi_read_rid(sc, WI_RID_PORT_STAT, DataBuffer, &len); if (error == 0) { pWLANStatus->PortStatus = (u_int8_t) DataBuffer[0]; } /* * Current BSSID */ memset(BSSIDAddress, 0, sizeof(BSSIDAddress)); len = sizeof(BSSIDAddress); error = wi_read_rid(sc, WI_RID_CURRENT_BSSID, BSSIDAddress, &len); if (error == 0) { memcpy(pWLANStatus->BSSIDAddress, BSSIDAddress, IEEE80211_ADDR_LEN); } /* * Current Channel */ memset(DataBuffer, 0, sizeof(DataBuffer)); len = sizeof(DataBuffer); error = wi_read_rid(sc, WI_RID_CURRENT_CHAN, DataBuffer, &len); if (error == 0) { pWLANStatus->Channel = (u_int8_t) DataBuffer[0]; } /* * Current TxRate */ memset(DataBuffer, 0, sizeof(DataBuffer)); len = sizeof(DataBuffer); error = wi_read_rid(sc, WI_RID_CUR_TX_RATE, DataBuffer, &len); if (error == 0) { pWLANStatus->TxRate = (u_int8_t) DataBuffer[0]; } /* * Getting Quality */ memset(DataBuffer, 0, sizeof(DataBuffer)); len = sizeof(DataBuffer); error = wi_read_rid(sc, WI_RID_DBM_COMMS_QUAL, DataBuffer, &len); if (error == 0) { pWLANStatus->Quality = DataBuffer[0]; pWLANStatus->Signal = DataBuffer[1]; pWLANStatus->Noise = DataBuffer[2]; } WI_UNLOCK(sc); } else { error = -1; } break; } /* WLAN_IOCTL_GET_STATUS */ default:{ error = -1; break; } } /* switch */ } /* sc->wi_gone */ return (error); }