Date: Fri, 6 Jul 2007 03:23:29 GMT From: Andrew Thompson <thompsa@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 122988 for review Message-ID: <200707060323.l663NTF3017798@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=122988 Change 122988 by thompsa@thompsa_heff on 2007/07/06 03:23:28 Add in a large chunk of ipw changes by Sam, I am currently testing and refining this. Affected files ... .. //depot/projects/wifi/sys/dev/ipw/if_ipw.c#19 edit .. //depot/projects/wifi/sys/dev/ipw/if_ipwreg.h#3 edit .. //depot/projects/wifi/sys/dev/ipw/if_ipwvar.h#5 edit Differences ... ==== //depot/projects/wifi/sys/dev/ipw/if_ipw.c#19 (text+ko) ==== @@ -1,8 +1,7 @@ -/* $FreeBSD: src/sys/dev/ipw/if_ipw.c,v 1.28 2007/06/29 02:43:13 kevlo Exp $ */ - /*- * Copyright (c) 2004-2006 * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. + * Copyright (c) 2006 Sam Leffler, Errno Consulting * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,7 +27,7 @@ */ #include <sys/cdefs.h> -__FBSDID("$FreeBSD: src/sys/dev/ipw/if_ipw.c,v 1.28 2007/06/29 02:43:13 kevlo Exp $"); +__FBSDID("$FreeBSD: src/sys/dev/ipw/if_ipw.c,v 1.18 2006/03/13 20:05:32 damien Exp $"); /*- * Intel(R) PRO/Wireless 2100 MiniPCI driver @@ -78,10 +77,11 @@ #include <dev/ipw/if_ipwreg.h> #include <dev/ipw/if_ipwvar.h> +#define IPW_DEBUG #ifdef IPW_DEBUG #define DPRINTF(x) do { if (ipw_debug > 0) printf x; } while (0) #define DPRINTFN(n, x) do { if (ipw_debug >= (n)) printf x; } while (0) -int ipw_debug = 0; +int ipw_debug = 4; SYSCTL_INT(_debug, OID_AUTO, ipw, CTLFLAG_RW, &ipw_debug, 0, "ipw debug level"); #else #define DPRINTF(x) @@ -109,40 +109,66 @@ static int ipw_media_change(struct ifnet *); static void ipw_media_status(struct ifnet *, struct ifmediareq *); static int ipw_newstate(struct ieee80211com *, enum ieee80211_state, int); -static uint16_t ipw_read_prom_word(struct ipw_softc *, uint8_t); -static void ipw_command_intr(struct ipw_softc *, struct ipw_soft_buf *); -static void ipw_newstate_intr(struct ipw_softc *, struct ipw_soft_buf *); -static void ipw_data_intr(struct ipw_softc *, struct ipw_status *, +static void ipw_rx_cmd_intr(struct ipw_softc *, struct ipw_soft_buf *); +static void ipw_rx_newstate_intr(struct ipw_softc *, struct ipw_soft_buf *); +static void ipw_rx_data_intr(struct ipw_softc *, struct ipw_status *, struct ipw_soft_bd *, struct ipw_soft_buf *); static void ipw_rx_intr(struct ipw_softc *); static void ipw_release_sbd(struct ipw_softc *, struct ipw_soft_bd *); static void ipw_tx_intr(struct ipw_softc *); +static const char *ipw_cmdname(int); static void ipw_intr(void *); static void ipw_dma_map_addr(void *, bus_dma_segment_t *, int, int); -static int ipw_cmd(struct ipw_softc *, uint32_t, void *, uint32_t); +static int ipw_cmd(struct ipw_softc *, uint32_t, const void *, uint32_t); static int ipw_tx_start(struct ifnet *, struct mbuf *, struct ieee80211_node *); +static void ipw_start_locked(struct ifnet *); static void ipw_start(struct ifnet *); -static void ipw_watchdog(struct ifnet *); +static void ipw_watchdog(void *); static int ipw_ioctl(struct ifnet *, u_long, caddr_t); static void ipw_stop_master(struct ipw_softc *); static int ipw_reset(struct ipw_softc *); -static int ipw_load_ucode(struct ipw_softc *, const char *, int); -static int ipw_load_firmware(struct ipw_softc *, const char *, int); +static int ipw_enable(struct ipw_softc *); +static int ipw_disable(struct ipw_softc *); static int ipw_config(struct ipw_softc *); -static void ipw_init_task(void *, int); +static void ipw_restart(void *, int); +static int ipw_scan(struct ipw_softc *); +static void ipw_scanstart(void *, int); +static void ipw_assoc_lost(void *, int); +static void ipw_assoc(void *, int); +static int ipw_auth_and_assoc(struct ipw_softc *); +static int ipw_disassociate(struct ipw_softc *); +static void ipw_down(void *, int); static void ipw_init(void *); +static void ipw_init_locked(struct ipw_softc *, int); +static void ipw_stop_locked(struct ipw_softc *); static void ipw_stop(void *); static int ipw_sysctl_stats(SYSCTL_HANDLER_ARGS); +static int ipw_getrfkill(struct ipw_softc *); +static void ipw_radio_on(void *, int); +static void ipw_radio_off(void *, int); static int ipw_sysctl_radio(SYSCTL_HANDLER_ARGS); +static int ipw_get_firmware(struct ipw_softc *); +static void ipw_put_firmware(struct ipw_softc *); +static int ipw_load_ucode(struct ipw_softc *, const struct ipw_fw *); +static int ipw_load_firmware(struct ipw_softc *, const struct ipw_fw *); static uint32_t ipw_read_table1(struct ipw_softc *, uint32_t); static void ipw_write_table1(struct ipw_softc *, uint32_t, uint32_t); +#if 0 static int ipw_read_table2(struct ipw_softc *, uint32_t, void *, uint32_t *); static void ipw_read_mem_1(struct ipw_softc *, bus_size_t, uint8_t *, bus_size_t); +#endif static void ipw_write_mem_1(struct ipw_softc *, bus_size_t, const uint8_t *, bus_size_t); +static uint16_t ipw_read_prom_word(struct ipw_softc *, uint8_t); +static void ipw_sysctlattach(struct ipw_softc *); +static void ipw_scan_start(struct ieee80211com *); +static void ipw_scan_end(struct ieee80211com *); +static void ipw_set_channel(struct ieee80211com *); +static void ipw_scan_curchan(struct ieee80211com *, unsigned long maxdwell); +static void ipw_scan_mindwell(struct ieee80211com *); static int ipw_probe(device_t); static int ipw_attach(device_t); @@ -172,6 +198,22 @@ static devclass_t ipw_devclass; DRIVER_MODULE(ipw, pci, ipw_driver, ipw_devclass, 0, 0); +DRIVER_MODULE(ipw, cardbus, ipw_driver, ipw_devclass, 0, 0); + +/* + * NB.: This models the only instance of async locking in ipw_init_locked + * and must be kept in sync. + */ +#define IPW_LOCK_DECL int __waslocked = 0 +#define IPW_LOCK(sc) do { \ + if (!(__waslocked = mtx_owned(&(sc)->sc_mtx))) \ + mtx_lock(&sc->sc_mtx); \ +} while (0) +#define IPW_UNLOCK(sc) do { \ + if (!__waslocked) \ + mtx_unlock(&sc->sc_mtx); \ +} while (0) +#define IPW_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) static int ipw_probe(device_t dev) @@ -206,7 +248,25 @@ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); - TASK_INIT(&sc->sc_init_task, 0, ipw_init_task, sc); +#if __FreeBSD_version >= 700000 + sc->sc_tq = taskqueue_create("ipw_taskq", M_NOWAIT, + taskqueue_thread_enqueue, &sc->sc_tq); + taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", + device_get_nameunit(dev)); +#else + sc->sc_tq = taskqueue_create("ipw_taskq", M_NOWAIT, + taskqueue_thread_enqueue, &sc->sc_tq, &sc->sc_tqproc); + taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", + device_get_nameunit(dev)); +#endif + TASK_INIT(&sc->sc_radiontask, 0, ipw_radio_on, sc); + TASK_INIT(&sc->sc_radiofftask, 0, ipw_radio_off, sc); + TASK_INIT(&sc->sc_scanstarttask,0, ipw_scanstart, sc); + TASK_INIT(&sc->sc_assoclosttask,0, ipw_assoc_lost, sc); + TASK_INIT(&sc->sc_assoctask, 0, ipw_assoc, sc); + TASK_INIT(&sc->sc_downtask, 0, ipw_down, sc); + TASK_INIT(&sc->sc_restarttask, 0, ipw_restart, sc); + callout_init_mtx(&sc->sc_wdtimer, &sc->sc_mtx, 0); if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { device_printf(dev, "chip is in D%d power mode " @@ -260,7 +320,6 @@ ifp->if_init = ipw_init; ifp->if_ioctl = ipw_ioctl; ifp->if_start = ipw_start; - ifp->if_watchdog = ipw_watchdog; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); @@ -271,11 +330,12 @@ ic->ic_state = IEEE80211_S_INIT; /* 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 */ + ic->ic_caps = IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_PMGT /* power save supported */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_WPA /* 802.11i supported */ + ; /* read MAC address from EEPROM */ val = ipw_read_prom_word(sc, IPW_EEPROM_MAC + 0); @@ -303,7 +363,7 @@ /* check support for radio transmitter switch in EEPROM */ if (!(ipw_read_prom_word(sc, IPW_EEPROM_RADIO) & 8)) - sc->flags |= IPW_FLAG_HAS_RADIO_SWITCH; + sc->flags |= IPW_FLAG_HAS_RFSWITCH; ieee80211_ifattach(ic); /* override state transition machine */ @@ -311,8 +371,14 @@ ic->ic_newstate = ipw_newstate; ieee80211_media_init(ic, ipw_media_change, ipw_media_status); + ic->ic_scan_start = ipw_scan_start; + ic->ic_scan_end = ipw_scan_end; + ic->ic_set_channel = ipw_set_channel; + ic->ic_scan_curchan = ipw_scan_curchan; + ic->ic_scan_mindwell = ipw_scan_mindwell; + bpfattach2(ifp, DLT_IEEE802_11_RADIO, - sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap), + sizeof (struct ieee80211_frame) + sizeof (sc->sc_txtap), &sc->sc_drvbpf); sc->sc_rxtap_len = sizeof sc->sc_rxtap; @@ -323,26 +389,8 @@ sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(IPW_TX_RADIOTAP_PRESENT); - /* - * Add a few sysctl knobs. - */ - sc->dwelltime = 100; - - SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "radio", - CTLTYPE_INT | CTLFLAG_RD, sc, 0, ipw_sysctl_radio, "I", - "radio transmitter switch state (0=off, 1=on)"); + ipw_sysctlattach(sc); - SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "stats", - CTLTYPE_OPAQUE | CTLFLAG_RD, sc, 0, ipw_sysctl_stats, "S", - "statistics"); - - SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "dwell", - CTLFLAG_RW, &sc->dwelltime, 0, - "channel dwell time (ms) for AP/station scanning"); - /* * Hook our interrupt after all initialization is complete. */ @@ -370,6 +418,8 @@ struct ifnet *ifp = ic->ic_ifp; ipw_stop(sc); + callout_drain(&sc->sc_wdtimer); + ipw_put_firmware(sc); if (ifp != NULL) { bpfdetach(ifp); @@ -389,10 +439,7 @@ if (ifp != NULL) if_free(ifp); - if (sc->sc_firmware != NULL) { - firmware_put(sc->sc_firmware, FIRMWARE_UNLOAD); - sc->sc_firmware = NULL; - } + taskqueue_free(sc->sc_tq); mtx_destroy(&sc->sc_mtx); @@ -692,6 +739,7 @@ struct ipw_softc *sc = device_get_softc(dev); ipw_stop(sc); + ipw_put_firmware(sc); /* ??? XXX */ return 0; } @@ -711,18 +759,19 @@ { struct ipw_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ic.ic_ifp; + IPW_LOCK_DECL; - mtx_lock(&sc->sc_mtx); + IPW_LOCK(sc); pci_write_config(dev, 0x41, 0, 1); if (ifp->if_flags & IFF_UP) { - ifp->if_init(ifp->if_softc); + ipw_init_locked(sc, 0); if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ifp->if_start(ifp); + ipw_start_locked(ifp); } - mtx_unlock(&sc->sc_mtx); + IPW_UNLOCK(sc); return 0; } @@ -732,20 +781,30 @@ { struct ipw_softc *sc = ifp->if_softc; int error; + IPW_LOCK_DECL; - mtx_lock(&sc->sc_mtx); - + IPW_LOCK(sc); error = ieee80211_media_change(ifp); - if (error != ENETRESET) { - mtx_unlock(&sc->sc_mtx); - return error; + if (error == ENETRESET) { + if ((ifp->if_flags & IFF_UP) && + (ifp->if_drv_flags & IFF_DRV_RUNNING)) + ipw_init_locked(sc, 0); + error = 0; } + IPW_UNLOCK(sc); - if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) - ipw_init(sc); + return error; +} - mtx_unlock(&sc->sc_mtx); - +static int +ipw_cvtrate(int ipwrate) +{ + switch (ipwrate) { + case IPW_RATE_DS1: return 2; + case IPW_RATE_DS2: return 4; + case IPW_RATE_DS5: return 11; + case IPW_RATE_DS11: return 22; + } return 0; } @@ -756,20 +815,9 @@ static void ipw_media_status(struct ifnet *ifp, struct ifmediareq *imr) { -#define N(a) (sizeof (a) / sizeof (a[0])) struct ipw_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; - static const struct { - uint32_t val; - int rate; - } rates[] = { - { IPW_RATE_DS1, 2 }, - { IPW_RATE_DS2, 4 }, - { IPW_RATE_DS5, 11 }, - { IPW_RATE_DS11, 22 }, - }; - uint32_t val; - int rate, i; + int rate; imr->ifm_status = IFM_AVALID; imr->ifm_active = IFM_IEEE80211; @@ -777,33 +825,13 @@ imr->ifm_status |= IFM_ACTIVE; /* read current transmission rate from adapter */ - val = ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf; - - /* convert ipw rate to 802.11 rate */ - for (i = 0; i < N(rates) && rates[i].val != val; i++); - rate = (i < N(rates)) ? rates[i].rate : 0; - - imr->ifm_active |= IFM_IEEE80211_11B; + rate = ipw_cvtrate(ipw_read_table1(sc, IPW_INFO_CURRENT_TX_RATE) & 0xf); imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B); - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - break; - case IEEE80211_M_IBSS: + if (ic->ic_opmode == IEEE80211_M_IBSS) imr->ifm_active |= IFM_IEEE80211_IBSS; - break; - - case IEEE80211_M_MONITOR: + else if (ic->ic_opmode == IEEE80211_M_MONITOR) imr->ifm_active |= IFM_IEEE80211_MONITOR; - break; - - case IEEE80211_M_AHDEMO: - case IEEE80211_M_HOSTAP: - case IEEE80211_M_WDS: - /* should not get there */ - break; - } -#undef N } static int @@ -811,99 +839,55 @@ { struct ifnet *ifp = ic->ic_ifp; struct ipw_softc *sc = ifp->if_softc; - uint8_t macaddr[IEEE80211_ADDR_LEN]; - uint32_t len; + + DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, + ieee80211_state_name[ic->ic_state], + ieee80211_state_name[nstate], sc->flags)); switch (nstate) { + case IEEE80211_S_AUTH: + taskqueue_enqueue(sc->sc_tq, &sc->sc_assoctask); + break; + case IEEE80211_S_RUN: - DELAY(200); /* firmware needs a short delay here */ + if (ic->ic_opmode == IEEE80211_M_IBSS) { + /* + * XXX when joining an ibss network we are called + * with a SCAN -> RUN transition on scan complete. + * Use that to call ipw_auth_and_assoc. On completing + * the join we are then called again with an + * AUTH -> RUN transition and we want to do nothing. + * This is all totally bogus and needs to be redone. + */ + if (ic->ic_state == IEEE80211_S_SCAN) + taskqueue_enqueue(sc->sc_tq, &sc->sc_assoctask); + } + /* XXX way wrong */ + return sc->sc_newstate(ic, nstate, + IEEE80211_FC0_SUBTYPE_ASSOC_RESP); + break; - len = IEEE80211_ADDR_LEN; - ipw_read_table2(sc, IPW_INFO_CURRENT_BSSID, macaddr, &len); - -#if 0 - ni = ieee80211_find_node(&ic->ic_scan, macaddr); - if (ni == NULL) - break; - - ieee80211_ref_node(ni); - ieee80211_sta_join(ic, ni); - ieee80211_node_authorize(ni); - - if (ic->ic_opmode == IEEE80211_M_STA) - ieee80211_notify_node_join(ic, ni, 1); -#endif + case IEEE80211_S_ASSOC: break; case IEEE80211_S_INIT: - case IEEE80211_S_SCAN: - case IEEE80211_S_AUTH: - case IEEE80211_S_ASSOC: + /* + * NB: don't try to do this if ipw_stop_master has + * shutdown the firmware and disabled interrupts. + */ + if (ic->ic_state == IEEE80211_S_RUN && + (sc->flags & IPW_FLAG_FW_INITED)) + taskqueue_enqueue(sc->sc_tq, &sc->sc_downtask); break; - } - ic->ic_state = nstate; - - return 0; -} - -/* - * Read 16 bits at address 'addr' from the serial EEPROM. - */ -static uint16_t -ipw_read_prom_word(struct ipw_softc *sc, uint8_t addr) -{ - uint32_t tmp; - uint16_t val; - int n; - - /* clock C once before the first command */ - IPW_EEPROM_CTL(sc, 0); - IPW_EEPROM_CTL(sc, IPW_EEPROM_S); - IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C); - IPW_EEPROM_CTL(sc, IPW_EEPROM_S); - - /* write start bit (1) */ - IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D); - IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D | IPW_EEPROM_C); - - /* write READ opcode (10) */ - IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D); - IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_D | IPW_EEPROM_C); - IPW_EEPROM_CTL(sc, IPW_EEPROM_S); - IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C); - - /* write address A7-A0 */ - for (n = 7; n >= 0; n--) { - IPW_EEPROM_CTL(sc, IPW_EEPROM_S | - (((addr >> n) & 1) << IPW_EEPROM_SHIFT_D)); - IPW_EEPROM_CTL(sc, IPW_EEPROM_S | - (((addr >> n) & 1) << IPW_EEPROM_SHIFT_D) | IPW_EEPROM_C); + default: + break; } - - IPW_EEPROM_CTL(sc, IPW_EEPROM_S); - - /* read data Q15-Q0 */ - val = 0; - for (n = 15; n >= 0; n--) { - IPW_EEPROM_CTL(sc, IPW_EEPROM_S | IPW_EEPROM_C); - IPW_EEPROM_CTL(sc, IPW_EEPROM_S); - tmp = MEM_READ_4(sc, IPW_MEM_EEPROM_CTL); - val |= ((tmp & IPW_EEPROM_Q) >> IPW_EEPROM_SHIFT_Q) << n; - } - - IPW_EEPROM_CTL(sc, 0); - - /* clear Chip Select and clock C */ - IPW_EEPROM_CTL(sc, IPW_EEPROM_S); - IPW_EEPROM_CTL(sc, 0); - IPW_EEPROM_CTL(sc, IPW_EEPROM_C); - - return le16toh(val); + return sc->sc_newstate(ic, nstate, arg); } static void -ipw_command_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) +ipw_rx_cmd_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) { struct ipw_cmd *cmd; @@ -911,16 +895,18 @@ cmd = mtod(sbuf->m, struct ipw_cmd *); - DPRINTFN(2, ("cmd ack'ed (%u, %u, %u, %u, %u)\n", le32toh(cmd->type), - le32toh(cmd->subtype), le32toh(cmd->seq), le32toh(cmd->len), - le32toh(cmd->status))); + DPRINTFN(9, ("%s(%s subtype %u seq %u len %u status %u)\n", + __func__, ipw_cmdname(le32toh(cmd->type)), le32toh(cmd->subtype), + le32toh(cmd->seq), le32toh(cmd->len), le32toh(cmd->status))); - wakeup(sc); + sc->flags &= ~IPW_FLAG_BUSY; + wakeup_one(&sc->cmd); } static void -ipw_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) +ipw_rx_newstate_intr(struct ipw_softc *sc, struct ipw_soft_buf *sbuf) { +#define IEEESTATE(ic) ieee80211_state_name[ic->ic_state] struct ieee80211com *ic = &sc->sc_ic; uint32_t state; @@ -928,43 +914,106 @@ state = le32toh(*mtod(sbuf->m, uint32_t *)); - DPRINTFN(2, ("entering state %u\n", state)); + switch (state) { + case IPW_STATE_INITIALIZED: + case IPW_STATE_DISABLED: + break; - switch (state) { case IPW_STATE_ASSOCIATED: - ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + DPRINTFN(2, ("Association succeeded (%s flags 0x%x)\n", + IEEESTATE(ic), sc->flags)); + sc->flags |= IPW_FLAG_ASSOCIATED; + /* XXX suppress state change in case the fw auto-associates */ + if (ic->ic_state != IEEE80211_S_ASSOC) { + DPRINTF(("Unexpected association (state %u)\n", + ic->ic_state)); + } else + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); break; case IPW_STATE_SCANNING: - /* don't leave run state on background scan */ - if (ic->ic_state != IEEE80211_S_RUN) - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - - ic->ic_flags |= IEEE80211_F_SCAN; + DPRINTFN(3, ("Scanning (%s flags 0x%x)\n", + IEEESTATE(ic), sc->flags)); + /* + * NB: Check driver state for association on assoc + * loss as the firmware will immediately start to + * scan and we would treat it as a beacon miss if + * we checked the 802.11 layer state. + */ + if (sc->flags & IPW_FLAG_ASSOCIATED) + ieee80211_beacon_miss(ic); break; case IPW_STATE_SCAN_COMPLETE: - ieee80211_notify_scan_done(ic); - ic->ic_flags &= ~IEEE80211_F_SCAN; + DPRINTFN(3, ("Scan complete (%s flags 0x%x)\n", + IEEESTATE(ic), sc->flags)); + /* + * XXX For some reason scan requests generate scan + * started + scan done events before any traffic is + * received (e.g. probe response frames). We work + * around this by marking the HACK flag and skipping + * the first scan complete event. This works ok + * because the adapter scans only 2.4G channels so + * doing an extra pass doesn't take long. + */ + if (sc->flags & IPW_FLAG_HACK) { + sc->flags &= ~IPW_FLAG_HACK; + break; + } + sc->sc_scan_timer = 0; + sc->flags &= ~IPW_FLAG_SCANNING; + ieee80211_scan_done(ic); break; case IPW_STATE_ASSOCIATION_LOST: - ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + DPRINTFN(2, ("Association lost (%s flags 0x%x)\n", + IEEESTATE(ic), sc->flags)); + sc->flags &= ~IPW_FLAG_ASSOCIATED; + taskqueue_enqueue(sc->sc_tq, &sc->sc_assoclosttask); break; - case IPW_STATE_RADIO_DISABLED: - ic->ic_ifp->if_flags &= ~IFF_UP; - ipw_stop(sc); + case IPW_STATE_RADIO_OFF: + DPRINTFN(2, ("Radio off (%s flags 0x%x)\n", + IEEESTATE(ic), sc->flags)); + taskqueue_enqueue(sc->sc_tq, &sc->sc_radiofftask); + break; + default: + DPRINTFN(2, ("%s: state %u %s flags 0x%x\n", + __func__, state, IEEESTATE(ic), sc->flags)); break; } +#undef IEEESTATE } /* + * Set driver state for current channel. + */ +static void +ipw_setcurchan(struct ipw_softc *sc, int chan) +{ + struct ieee80211com *ic = &sc->sc_ic; + + if (chan == sc->curchan) + return; + + ic->ic_curchan = ieee80211_find_channel(ic, + ieee80211_ieee2mhz(chan, 0), + IEEE80211_CHAN_B); + sc->curchan = chan; + + sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq = + htole16(ic->ic_curchan->ic_freq); + sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags = + htole16(ic->ic_curchan->ic_flags); +} + +/* * XXX: Hack to set the current channel to the value advertised in beacons or * probe responses. Only used during AP detection. + * XXX value comes from DSPARMS ie which is wrong for off-channel rx's */ static void -ipw_fix_channel(struct ieee80211com *ic, struct mbuf *m) +ipw_fix_channel(struct ipw_softc *sc, struct mbuf *m) { struct ieee80211_frame *wh; uint8_t subtype; @@ -990,16 +1039,14 @@ #if IEEE80211_CHAN_MAX < 255 if (frm[2] <= IEEE80211_CHAN_MAX) #endif - ic->ic_bsschan = ieee80211_find_channel(ic, - ieee80211_ieee2mhz(frm[2], 0), - IEEE80211_CHAN_B); + ipw_setcurchan(sc, frm[2]); frm += frm[1] + 2; } } static void -ipw_data_intr(struct ipw_softc *sc, struct ipw_status *status, +ipw_rx_data_intr(struct ipw_softc *sc, struct ipw_status *status, struct ipw_soft_bd *sbd, struct ipw_soft_buf *sbuf) { struct ieee80211com *ic = &sc->sc_ic; @@ -1008,15 +1055,25 @@ struct ieee80211_frame *wh; struct ieee80211_node *ni; bus_addr_t physaddr; - int error; + int error, framelen; + IPW_LOCK_DECL; + + framelen = le32toh(status->len); + if (framelen < IEEE80211_MIN_LEN || framelen > MCLBYTES) { + /* + * XXX >MCLBYTES is bogus as it means the h/w dma'd + * out of bounds; need to figure out how to limit + * frame size in the firmware + */ + /* XXX stat */ + DPRINTF(("drop rx frame len=%u rssi=%u\n", + framelen, status->rssi)); + return; + } DPRINTFN(5, ("received frame len=%u, rssi=%u\n", le32toh(status->len), status->rssi)); - if (le32toh(status->len) < sizeof (struct ieee80211_frame_min) || - le32toh(status->len) > MCLBYTES) - return; - /* * Try to allocate a new mbuf for this ring element and load it before * processing the current mbuf. If the ring element cannot be loaded, @@ -1062,22 +1119,21 @@ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = le32toh(status->len); - if (bpf_peers_present(sc->sc_drvbpf)) { + /* XXX */ + if (sc->flags & IPW_FLAG_SCANNING) + ipw_fix_channel(sc, m); + + if (sc->sc_drvbpf != NULL) { struct ipw_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; - tap->wr_antsignal = status->rssi; - tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); - tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); + tap->wr_antsignal = status->rssi + IPW_RSSI_TO_DBM; bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); } - if (ic->ic_state == IEEE80211_S_SCAN) - ipw_fix_channel(ic, m); - wh = mtod(m, struct ieee80211_frame *); - mtx_unlock(&sc->sc_mtx); + IPW_UNLOCK(sc); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); /* send the frame to the 802.11 layer */ @@ -1085,7 +1141,7 @@ /* node is no longer needed */ ieee80211_free_node(ni); - mtx_lock(&sc->sc_mtx); + IPW_LOCK(sc); bus_dmamap_sync(sc->rbd_dmat, sc->rbd_map, BUS_DMASYNC_PREWRITE); } @@ -1093,6 +1149,7 @@ static void ipw_rx_intr(struct ipw_softc *sc) { + struct ieee80211com *ic = &sc->sc_ic; struct ipw_status *status; struct ipw_soft_bd *sbd; struct ipw_soft_buf *sbuf; @@ -1112,31 +1169,33 @@ switch (le16toh(status->code) & 0xf) { case IPW_STATUS_CODE_COMMAND: - ipw_command_intr(sc, sbuf); + ipw_rx_cmd_intr(sc, sbuf); break; case IPW_STATUS_CODE_NEWSTATE: - ipw_newstate_intr(sc, sbuf); + ipw_rx_newstate_intr(sc, sbuf); break; case IPW_STATUS_CODE_DATA_802_3: case IPW_STATUS_CODE_DATA_802_11: - ipw_data_intr(sc, status, sbd, sbuf); + ipw_rx_data_intr(sc, status, sbd, sbuf); break; case IPW_STATUS_CODE_NOTIFICATION: - DPRINTFN(2, ("received notification\n")); + DPRINTFN(2, ("notification status, len %u flags 0x%x\n", + le32toh(status->len), status->flags)); + if (ic->ic_state == IEEE80211_S_AUTH) { + /* XXX assume auth notification */ + ieee80211_node_authorize(ic->ic_bss); + ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); + } break; default: - device_printf(sc->sc_dev, "unknown status code %u\n", + device_printf(sc->sc_dev, "unexpected status code %u\n", le16toh(status->code)); + break; } - - /* firmware was killed, stop processing received frames */ - if (!(sc->flags & IPW_FLAG_FW_INITED)) - return; - sbd->bd->flags = 0; } @@ -1174,12 +1233,8 @@ bus_dmamap_unload(sc->txbuf_dmat, sbuf->map); SLIST_INSERT_HEAD(&sc->free_sbuf, sbuf, next); - if (sbuf->m->m_flags & M_TXCB) - ieee80211_process_callback(sbuf->ni, sbuf->m, 0/*XXX*/); m_freem(sbuf->m); ieee80211_free_node(sbuf->ni); - - sc->sc_tx_timer = 0; break; } @@ -1211,8 +1266,10 @@ /* remember what the firmware has processed */ sc->txold = (r == 0) ? IPW_NTBD - 1 : r - 1; + sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ipw_start(ifp); + + ipw_start_locked(ifp); } static void @@ -1222,36 +1279,39 @@ uint32_t r; mtx_lock(&sc->sc_mtx); + r = CSR_READ_4(sc, IPW_CSR_INTR); + if (r != 0 && r != 0xffffffff) { + /* acknowledge all interrupts */ + CSR_WRITE_4(sc, IPW_CSR_INTR, r); - if ((r = CSR_READ_4(sc, IPW_CSR_INTR)) == 0 || r == 0xffffffff) { - mtx_unlock(&sc->sc_mtx); - return; - } + DPRINTFN(9, ("%s: 0x%x\n", __func__, r)); - /* disable interrupts */ - CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, 0); - - /* acknowledge all interrupts */ - CSR_WRITE_4(sc, IPW_CSR_INTR, r); - - if (r & (IPW_INTR_FATAL_ERROR | IPW_INTR_PARITY_ERROR)) { - device_printf(sc->sc_dev, "firmware error\n"); - taskqueue_enqueue_fast(taskqueue_fast, &sc->sc_init_task); - r = 0; /* don't process more interrupts */ + if (r & IPW_INTR_FATAL_ERROR) { + device_printf(sc->sc_dev, "firmware error\n"); + taskqueue_enqueue(sc->sc_tq, &sc->sc_restarttask); + r = 0; /* NB: don't do anything else */ + } + if (r & IPW_INTR_FW_INIT_DONE) { + wakeup_one(sc); + r &= ~IPW_INTR_FW_INIT_DONE; + } + if (r & IPW_INTR_RX_TRANSFER) { + ipw_rx_intr(sc); + r &= ~IPW_INTR_RX_TRANSFER; + } + if (r & IPW_INTR_TX_TRANSFER) { + ipw_tx_intr(sc); + r &= ~IPW_INTR_TX_TRANSFER; + } + if (r & IPW_INTR_PARITY_ERROR) { + /* XXX rate limit */ + device_printf(sc->sc_dev, "parity error\n"); + r &= ~IPW_INTR_PARITY_ERROR; + } + if (r != 0) + device_printf(sc->sc_dev, + "%s: 0x%x unserviced\n", __func__, r); } - - if (r & IPW_INTR_FW_INIT_DONE) - wakeup(sc); - - if (r & IPW_INTR_RX_TRANSFER) - ipw_rx_intr(sc); - - if (r & IPW_INTR_TX_TRANSFER) - ipw_tx_intr(sc); - - /* re-enable interrupts */ - CSR_WRITE_4(sc, IPW_CSR_INTR_MASK, IPW_INTR_MASK); - mtx_unlock(&sc->sc_mtx); } @@ -1266,26 +1326,87 @@ *(bus_addr_t *)arg = segs[0].ds_addr; } +static const char * +ipw_cmdname(int cmd) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const struct { + int cmd; + const char *name; + } cmds[] = { + { IPW_CMD_ADD_MULTICAST, "ADD_MULTICAST" }, + { IPW_CMD_BROADCAST_SCAN, "BROADCAST_SCAN" }, + { IPW_CMD_DISABLE, "DISABLE" }, + { IPW_CMD_DISABLE_PHY, "DISABLE_PHY" }, + { IPW_CMD_DISASSOCIATE, "DISASSOCIATE" }, + { IPW_CMD_ENABLE, "ENABLE" }, + { IPW_CMD_PREPARE_POWER_DOWN, "PREPARE_POWER_DOWN" }, + { IPW_CMD_SET_BASIC_TX_RATES, "SET_BASIC_TX_RATES" }, + { IPW_CMD_SET_BEACON_INTERVAL, "SET_BEACON_INTERVAL" }, + { IPW_CMD_SET_CHANNEL, "SET_CHANNEL" }, + { IPW_CMD_SET_CONFIGURATION, "SET_CONFIGURATION" }, + { IPW_CMD_SET_DESIRED_BSSID, "SET_DESIRED_BSSID" }, + { IPW_CMD_SET_ESSID, "SET_ESSID" }, + { IPW_CMD_SET_FRAG_THRESHOLD, "SET_FRAG_THRESHOLD" }, + { IPW_CMD_SET_LONG_RETRY, "SET_LONG_RETRY" }, + { IPW_CMD_SET_MAC_ADDRESS, "SET_MAC_ADDRESS" }, + { IPW_CMD_SET_MANDATORY_BSSID, "SET_MANDATORY_BSSID" }, + { IPW_CMD_SET_MODE, "SET_MODE" }, + { IPW_CMD_SET_MSDU_TX_RATES, "SET_MSDU_TX_RATES" }, + { IPW_CMD_SET_POWER_MODE, "SET_POWER_MODE" }, + { IPW_CMD_SET_RTS_THRESHOLD, "SET_RTS_THRESHOLD" }, + { IPW_CMD_SET_SCAN_DWELL_TIME, "SET_SCAN_DWELL_TIME" }, + { IPW_CMD_SET_SCAN_OPTIONS, "SET_SCAN_OPTIONS" }, + { IPW_CMD_SET_SECURITY_INFO, "SET_SECURITY_INFO" }, + { IPW_CMD_SET_SHORT_RETRY, "SET_SHORT_RETRY" }, + { IPW_CMD_SET_TX_POWER_INDEX, "SET_TX_POWER_INDEX" }, + { IPW_CMD_SET_TX_RATES, "SET_TX_RATES" }, + { IPW_CMD_SET_WEP_FLAGS, "SET_WEP_FLAGS" }, + { IPW_CMD_SET_WEP_KEY, "SET_WEP_KEY" }, + { IPW_CMD_SET_WEP_KEY_INDEX, "SET_WEP_KEY_INDEX" }, + { IPW_CMD_SET_WPA_IE, "SET_WPA_IE" }, + + }; + static char buf[12]; + int i; + + for (i = 0; i < N(cmds); i++) + if (cmds[i].cmd == cmd) + return cmds[i].name; + snprintf(buf, sizeof(buf), "%u", cmd); + return buf; +#undef N +} + /* * Send a command to the firmware and wait for the acknowledgement. */ static int -ipw_cmd(struct ipw_softc *sc, uint32_t type, void *data, uint32_t len) +ipw_cmd(struct ipw_softc *sc, uint32_t cmd, const void *data, uint32_t len) { struct ipw_soft_bd *sbd; bus_addr_t physaddr; int error; + if (sc->flags & IPW_FLAG_BUSY) { + device_printf(sc->sc_dev, "%s: %s not sent, busy\n", + __func__, ipw_cmdname(cmd)); + return EAGAIN; + } + sc->flags |= IPW_FLAG_BUSY; + sbd = &sc->stbd_list[sc->txcur]; error = bus_dmamap_load(sc->cmd_dmat, sc->cmd_map, &sc->cmd, sizeof (struct ipw_cmd), ipw_dma_map_addr, &physaddr, 0); if (error != 0) { - device_printf(sc->sc_dev, "could not map command DMA memory\n"); + device_printf(sc->sc_dev, >>> TRUNCATED FOR MAIL (1000 lines) <<<
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200707060323.l663NTF3017798>