From e802c1532268eda422156a9ec89178be9bc05772 Mon Sep 17 00:00:00 2001 From: damien Date: Sat, 17 Sep 2005 12:41:05 +0000 Subject: o Add initial bits for IBSS support. o Allow association with APs that do not broadcast SSID (with hints from Nick Hudson and Hajimu Umemoto). o IFQ_DRV_PREPEND mbuf when h/w ring is full so it can be sent later. o Increment if_oerrors when appropriate. o Did some cleanup while I'm here. MFC after: 1 day --- sys/dev/iwi/if_iwi.c | 118 ++++++++++++++++++++++++++++++++++++++---------- sys/dev/iwi/if_iwireg.h | 13 ++++++ sys/dev/iwi/if_iwivar.h | 4 ++ 3 files changed, 110 insertions(+), 25 deletions(-) (limited to 'sys/dev') diff --git a/sys/dev/iwi/if_iwi.c b/sys/dev/iwi/if_iwi.c index ad33e11..420d2ae 100644 --- a/sys/dev/iwi/if_iwi.c +++ b/sys/dev/iwi/if_iwi.c @@ -121,6 +121,7 @@ static void iwi_media_status(struct ifnet *, struct ifmediareq *); static int iwi_newstate(struct ieee80211com *, enum ieee80211_state, int); static int iwi_wme_update(struct ieee80211com *); static uint16_t iwi_read_prom_word(struct iwi_softc *, uint8_t); +static int iwi_find_txnode(struct iwi_softc *, const uint8_t *); static void iwi_fix_channel(struct ieee80211com *, struct mbuf *); static void iwi_frame_intr(struct iwi_softc *, struct iwi_rx_data *, int, struct iwi_frame *); @@ -130,7 +131,7 @@ static void iwi_tx_intr(struct iwi_softc *, struct iwi_tx_ring *); static void iwi_intr(void *); static int iwi_cmd(struct iwi_softc *, uint8_t, void *, uint8_t, int); static int iwi_tx_start(struct ifnet *, struct mbuf *, - struct ieee80211_node *); + struct ieee80211_node *, int); static void iwi_start(struct ifnet *); static void iwi_watchdog(struct ifnet *); static int iwi_ioctl(struct ifnet *, u_long, caddr_t); @@ -316,7 +317,6 @@ iwi_attach(device_t dev) if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); goto fail; - return (ENOSPC); } ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); @@ -337,6 +337,7 @@ iwi_attach(device_t dev) /* set device capabilities */ ic->ic_caps = + IEEE80211_C_IBSS | /* IBSS mode supported */ IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ @@ -463,11 +464,11 @@ iwi_detach(device_t dev) iwi_free_firmware(sc); - if (ifp != NULL) + if (ifp != NULL) { bpfdetach(ifp); - ieee80211_ifdetach(ic); - if (ifp != NULL) + ieee80211_ifdetach(ic); if_free(ifp); + } iwi_free_cmd_ring(sc, &sc->cmdq); iwi_free_tx_ring(sc, &sc->txq[0]); @@ -922,6 +923,7 @@ iwi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) if (sc->flags & IWI_FLAG_SCANNING) break; + sc->nsta = 0; /* flush IBSS nodes */ ieee80211_node_table_reset(&ic->ic_scan); ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; sc->flags |= IWI_FLAG_SCANNING; @@ -1077,6 +1079,37 @@ iwi_read_prom_word(struct iwi_softc *sc, uint8_t addr) } /* + * This is only used for IBSS mode where the firmware expect an index to an + * internal node table instead of a destination address. + */ +static int +iwi_find_txnode(struct iwi_softc *sc, const uint8_t *macaddr) +{ + struct iwi_node node; + int i; + + for (i = 0; i < sc->nsta; i++) + if (IEEE80211_ADDR_EQ(sc->sta[i], macaddr)) + return i; /* already existing node */ + + if (i == IWI_MAX_NODE) + return -1; /* no place left in neighbor table */ + + /* save this new node in our softc table */ + IEEE80211_ADDR_COPY(sc->sta[i], macaddr); + sc->nsta = i; + + /* write node information into NIC memory */ + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.bssid, macaddr); + + CSR_WRITE_REGION_1(sc, IWI_CSR_NODE_BASE + i * sizeof node, + (uint8_t *)&node, sizeof node); + + return i; +} + +/* * XXX: Hack to set the current channel to the value advertised in beacons or * probe responses. Only used during AP detection. */ @@ -1436,41 +1469,38 @@ iwi_cmd(struct iwi_softc *sc, uint8_t type, void *data, uint8_t len, int async) } static int -iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) +iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni, + int ac) { struct iwi_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_key *k; const struct chanAccParams *cap; - struct iwi_tx_ring *txq; + struct iwi_tx_ring *txq = &sc->txq[ac]; struct iwi_tx_data *data; struct iwi_tx_desc *desc; struct mbuf *mnew; bus_dma_segment_t segs[IWI_MAX_NSEG]; - int error, nsegs, hdrlen, ac, i, noack = 0; + int error, nsegs, hdrlen, i, station = 0, noack = 0; wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) { hdrlen = sizeof (struct ieee80211_qosframe); - ac = M_WME_GETAC(m0); cap = &ic->ic_wme.wme_chanParams; noack = cap->cap_wmeParams[ac].wmep_noackPolicy; - } else { + } else hdrlen = sizeof (struct ieee80211_frame); - ac = WME_AC_BE; - } - txq = &sc->txq[ac]; - if (txq->queued >= IWI_TX_RING_COUNT - 4) { - /* - * There is no place left in this ring. Perhaps in 802.11e, - * we should try to fallback to a lowest priority ring? - */ - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - m_freem(m0); - return 0; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + station = iwi_find_txnode(sc, wh->i_addr1); + if (station == -1) { + m_freem(m0); + ieee80211_free_node(ni); + ifp->if_oerrors++; + return 0; + } } if (wh->i_fc[1] & IEEE80211_FC1_WEP) { @@ -1534,6 +1564,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni) desc->hdr.type = IWI_HDR_TYPE_DATA; desc->hdr.flags = IWI_HDR_FLAG_IRQ; + desc->station = station; desc->cmd = IWI_DATA_CMD_TX; desc->len = htole16(m0->m_pkthdr.len); desc->flags = 0; @@ -1583,6 +1614,7 @@ iwi_start(struct ifnet *ifp) struct mbuf *m0; struct ether_header *eh; struct ieee80211_node *ni; + int ac; IWI_LOCK(sc); @@ -1597,31 +1629,50 @@ iwi_start(struct ifnet *ifp) break; if (m0->m_len < sizeof (struct ether_header) && - (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL) + (m0 = m_pullup(m0, sizeof (struct ether_header))) == NULL) { + ifp->if_oerrors++; continue; - + } eh = mtod(m0, struct ether_header *); ni = ieee80211_find_txnode(ic, eh->ether_dhost); if (ni == NULL) { m_freem(m0); + ifp->if_oerrors++; continue; } + + /* classify mbuf so we can find which tx ring to use */ if (ieee80211_classify(ic, m0, ni) != 0) { m_freem(m0); + ieee80211_free_node(ni); + ifp->if_oerrors++; continue; } + + /* no QoS encapsulation for EAPOL frames */ + ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ? + M_WME_GETAC(m0) : WME_AC_BE; + + if (sc->txq[ac].queued > IWI_TX_RING_COUNT - 8) { + /* there is no place left in this ring */ + IFQ_DRV_PREPEND(&ifp->if_snd, m0); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + BPF_MTAP(ifp, m0); m0 = ieee80211_encap(ic, m0, ni); if (m0 == NULL) { ieee80211_free_node(ni); + ifp->if_oerrors++; continue; } if (ic->ic_rawbpf != NULL) bpf_mtap(ic->ic_rawbpf, m0); - if (iwi_tx_start(ifp, m0, ni) != 0) { + if (iwi_tx_start(ifp, m0, ni, ac) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; @@ -2148,6 +2199,22 @@ iwi_config(struct iwi_softc *sc) if (error != 0) return error; + /* if we have a desired ESSID, set it now */ + if (ic->ic_des_esslen != 0) { +#ifdef IWI_DEBUG + if (iwi_debug > 0) { + printf("Setting desired ESSID to "); + ieee80211_print_essid(ic->ic_des_essid, + ic->ic_des_esslen); + printf("\n"); + } +#endif + error = iwi_cmd(sc, IWI_CMD_SET_ESSID, ic->ic_des_essid, + ic->ic_des_esslen, 1); + if (error != 0) + return error; + } + data = htole32(arc4random()); DPRINTF(("Setting initialization vector to %u\n", le32toh(data))); error = iwi_cmd(sc, IWI_CMD_SET_IV, &data, sizeof data, 0); @@ -2201,7 +2268,8 @@ iwi_scan(struct iwi_softc *sc) int i, count; memset(&scan, 0, sizeof scan); - scan.type = IWI_SCAN_TYPE_BROADCAST; + scan.type = (ic->ic_des_esslen != 0) ? IWI_SCAN_TYPE_BDIRECTED : + IWI_SCAN_TYPE_BROADCAST; scan.dwelltime = htole16(sc->dwelltime); p = scan.channels; diff --git a/sys/dev/iwi/if_iwireg.h b/sys/dev/iwi/if_iwireg.h index 8c5e21c..fa42bc2 100644 --- a/sys/dev/iwi/if_iwireg.h +++ b/sys/dev/iwi/if_iwireg.h @@ -62,6 +62,7 @@ #define IWI_CSR_RX_BASE 0x0500 #define IWI_CSR_TABLE0_SIZE 0x0700 #define IWI_CSR_TABLE0_BASE 0x0704 +#define IWI_CSR_NODE_BASE 0x0c0c #define IWI_CSR_CMD_WIDX 0x0f80 #define IWI_CSR_TX1_WIDX 0x0f84 #define IWI_CSR_TX2_WIDX 0x0f88 @@ -269,6 +270,12 @@ struct iwi_cmd_desc { uint8_t data[120]; } __packed; +/* node information (IBSS) */ +struct iwi_node { + uint8_t bssid[IEEE80211_ADDR_LEN]; + uint8_t reserved[2]; +} __packed; + /* constants for 'mode' fields */ #define IWI_MODE_11A 0 #define IWI_MODE_11B 1 @@ -331,7 +338,9 @@ struct iwi_associate { struct iwi_scan { uint8_t type; #define IWI_SCAN_TYPE_PASSIVE 1 +#define IWI_SCAN_TYPE_DIRECTED 2 #define IWI_SCAN_TYPE_BROADCAST 3 +#define IWI_SCAN_TYPE_BDIRECTED 4 uint16_t dwelltime; uint8_t channels[54]; @@ -424,6 +433,10 @@ struct iwi_wme_params { #define CSR_WRITE_4(sc, reg, val) \ bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) +#define CSR_WRITE_REGION_1(sc, offset, datap, count) \ + bus_space_write_region_1((sc)->sc_st, (sc)->sc_sh, (offset), \ + (datap), (count)) + /* * indirect memory space access macros */ diff --git a/sys/dev/iwi/if_iwivar.h b/sys/dev/iwi/if_iwivar.h index ff9d9b3..1927bfa 100644 --- a/sys/dev/iwi/if_iwivar.h +++ b/sys/dev/iwi/if_iwivar.h @@ -130,6 +130,10 @@ struct iwi_softc { struct iwi_tx_ring txq[WME_NUM_AC]; struct iwi_rx_ring rxq; +#define IWI_MAX_NODE 32 + uint8_t sta[IWI_MAX_NODE][IEEE80211_ADDR_LEN]; + uint8_t nsta; + struct resource *irq; struct resource *mem; bus_space_tag_t sc_st; -- cgit v1.1