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
This commit is contained in:
Damien Bergamini 2005-09-17 12:41:05 +00:00
parent c1706c47b5
commit 0b74a28506
3 changed files with 110 additions and 25 deletions

View File

@ -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;
@ -1076,6 +1078,37 @@ iwi_read_prom_word(struct iwi_softc *sc, uint8_t addr)
return be16toh(val);
}
/*
* 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;

View File

@ -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
*/

View File

@ -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;