summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordamien <damien@FreeBSD.org>2005-09-17 12:41:05 +0000
committerdamien <damien@FreeBSD.org>2005-09-17 12:41:05 +0000
commite802c1532268eda422156a9ec89178be9bc05772 (patch)
treeccbc2ebe5598d157aec0baefa23d4c52744f3384
parent609346c61e2ba426a72d6b6a06d65e3e88898494 (diff)
downloadFreeBSD-src-e802c1532268eda422156a9ec89178be9bc05772.zip
FreeBSD-src-e802c1532268eda422156a9ec89178be9bc05772.tar.gz
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
-rw-r--r--sys/dev/iwi/if_iwi.c118
-rw-r--r--sys/dev/iwi/if_iwireg.h13
-rw-r--r--sys/dev/iwi/if_iwivar.h4
3 files changed, 110 insertions, 25 deletions
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;
OpenPOWER on IntegriCloud