Index: head/sys/dev/bwi/if_bwi.c =================================================================== --- head/sys/dev/bwi/if_bwi.c (revision 193072) +++ head/sys/dev/bwi/if_bwi.c (revision 193073) @@ -1,4045 +1,4058 @@ /* * Copyright (c) 2007 The DragonFly Project. All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Sepherosa Ziehau * * 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 DragonFly Project 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 HOLDERS 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. * * $DragonFly: src/sys/dev/netif/bwi/if_bwi.c,v 1.19 2008/02/15 11:15:38 sephe Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_bwi.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif #include #include #include #include #include #include #include #include struct bwi_clock_freq { u_int clkfreq_min; u_int clkfreq_max; }; struct bwi_myaddr_bssid { uint8_t myaddr[IEEE80211_ADDR_LEN]; uint8_t bssid[IEEE80211_ADDR_LEN]; } __packed; static struct ieee80211vap *bwi_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, int, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void bwi_vap_delete(struct ieee80211vap *); static void bwi_init(void *); static int bwi_ioctl(struct ifnet *, u_long, caddr_t); static void bwi_start(struct ifnet *); static void bwi_start_locked(struct ifnet *); static int bwi_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void bwi_watchdog(struct ifnet *); static void bwi_scan_start(struct ieee80211com *); static void bwi_set_channel(struct ieee80211com *); static void bwi_scan_end(struct ieee80211com *); static int bwi_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void bwi_updateslot(struct ifnet *); static struct ieee80211_node *bwi_node_alloc(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); static void bwi_newassoc(struct ieee80211_node *, int); static int bwi_media_change(struct ifnet *); static void bwi_calibrate(void *); static int bwi_calc_rssi(struct bwi_softc *, const struct bwi_rxbuf_hdr *); static int bwi_calc_noise(struct bwi_softc *); static __inline uint8_t bwi_ofdm_plcp2rate(const uint32_t *); static __inline uint8_t bwi_ds_plcp2rate(const struct ieee80211_ds_plcp_hdr *); static void bwi_rx_radiotap(struct bwi_softc *, struct mbuf *, struct bwi_rxbuf_hdr *, const void *, int, int, int); static void bwi_restart(void *, int); static void bwi_init_statechg(struct bwi_softc *, int); static void bwi_stop(struct bwi_softc *, int); static void bwi_stop_locked(struct bwi_softc *, int); static int bwi_newbuf(struct bwi_softc *, int, int); static int bwi_encap(struct bwi_softc *, int, struct mbuf *, struct ieee80211_node *); static int bwi_encap_raw(struct bwi_softc *, int, struct mbuf *, struct ieee80211_node *, const struct ieee80211_bpf_params *); static void bwi_init_rxdesc_ring32(struct bwi_softc *, uint32_t, bus_addr_t, int, int); static void bwi_reset_rx_ring32(struct bwi_softc *, uint32_t); static int bwi_init_tx_ring32(struct bwi_softc *, int); static int bwi_init_rx_ring32(struct bwi_softc *); static int bwi_init_txstats32(struct bwi_softc *); static void bwi_free_tx_ring32(struct bwi_softc *, int); static void bwi_free_rx_ring32(struct bwi_softc *); static void bwi_free_txstats32(struct bwi_softc *); static void bwi_setup_rx_desc32(struct bwi_softc *, int, bus_addr_t, int); static void bwi_setup_tx_desc32(struct bwi_softc *, struct bwi_ring_data *, int, bus_addr_t, int); static int bwi_rxeof32(struct bwi_softc *); static void bwi_start_tx32(struct bwi_softc *, uint32_t, int); static void bwi_txeof_status32(struct bwi_softc *); static int bwi_init_tx_ring64(struct bwi_softc *, int); static int bwi_init_rx_ring64(struct bwi_softc *); static int bwi_init_txstats64(struct bwi_softc *); static void bwi_free_tx_ring64(struct bwi_softc *, int); static void bwi_free_rx_ring64(struct bwi_softc *); static void bwi_free_txstats64(struct bwi_softc *); static void bwi_setup_rx_desc64(struct bwi_softc *, int, bus_addr_t, int); static void bwi_setup_tx_desc64(struct bwi_softc *, struct bwi_ring_data *, int, bus_addr_t, int); static int bwi_rxeof64(struct bwi_softc *); static void bwi_start_tx64(struct bwi_softc *, uint32_t, int); static void bwi_txeof_status64(struct bwi_softc *); static int bwi_rxeof(struct bwi_softc *, int); static void _bwi_txeof(struct bwi_softc *, uint16_t, int, int); static void bwi_txeof(struct bwi_softc *); static void bwi_txeof_status(struct bwi_softc *, int); static void bwi_enable_intrs(struct bwi_softc *, uint32_t); static void bwi_disable_intrs(struct bwi_softc *, uint32_t); static int bwi_dma_alloc(struct bwi_softc *); static void bwi_dma_free(struct bwi_softc *); static int bwi_dma_ring_alloc(struct bwi_softc *, bus_dma_tag_t, struct bwi_ring_data *, bus_size_t, uint32_t); static int bwi_dma_mbuf_create(struct bwi_softc *); static void bwi_dma_mbuf_destroy(struct bwi_softc *, int, int); static int bwi_dma_txstats_alloc(struct bwi_softc *, uint32_t, bus_size_t); static void bwi_dma_txstats_free(struct bwi_softc *); static void bwi_dma_ring_addr(void *, bus_dma_segment_t *, int, int); static void bwi_dma_buf_addr(void *, bus_dma_segment_t *, int, bus_size_t, int); static void bwi_power_on(struct bwi_softc *, int); static int bwi_power_off(struct bwi_softc *, int); static int bwi_set_clock_mode(struct bwi_softc *, enum bwi_clock_mode); static int bwi_set_clock_delay(struct bwi_softc *); static void bwi_get_clock_freq(struct bwi_softc *, struct bwi_clock_freq *); static int bwi_get_pwron_delay(struct bwi_softc *sc); static void bwi_set_addr_filter(struct bwi_softc *, uint16_t, const uint8_t *); static void bwi_set_bssid(struct bwi_softc *, const uint8_t *); static void bwi_get_card_flags(struct bwi_softc *); static void bwi_get_eaddr(struct bwi_softc *, uint16_t, uint8_t *); static int bwi_bus_attach(struct bwi_softc *); static int bwi_bbp_attach(struct bwi_softc *); static int bwi_bbp_power_on(struct bwi_softc *, enum bwi_clock_mode); static void bwi_bbp_power_off(struct bwi_softc *); static const char *bwi_regwin_name(const struct bwi_regwin *); static uint32_t bwi_regwin_disable_bits(struct bwi_softc *); static void bwi_regwin_info(struct bwi_softc *, uint16_t *, uint8_t *); static int bwi_regwin_select(struct bwi_softc *, int); static void bwi_led_attach(struct bwi_softc *); static void bwi_led_newstate(struct bwi_softc *, enum ieee80211_state); static void bwi_led_event(struct bwi_softc *, int); static void bwi_led_blink_start(struct bwi_softc *, int, int); static void bwi_led_blink_next(void *); static void bwi_led_blink_end(void *); static const struct { uint16_t did_min; uint16_t did_max; uint16_t bbp_id; } bwi_bbpid_map[] = { { 0x4301, 0x4301, 0x4301 }, { 0x4305, 0x4307, 0x4307 }, { 0x4403, 0x4403, 0x4402 }, { 0x4610, 0x4615, 0x4610 }, { 0x4710, 0x4715, 0x4710 }, { 0x4720, 0x4725, 0x4309 } }; static const struct { uint16_t bbp_id; int nregwin; } bwi_regwin_count[] = { { 0x4301, 5 }, { 0x4306, 6 }, { 0x4307, 5 }, { 0x4310, 8 }, { 0x4401, 3 }, { 0x4402, 3 }, { 0x4610, 9 }, { 0x4704, 9 }, { 0x4710, 9 }, { 0x5365, 7 } }; #define CLKSRC(src) \ [BWI_CLKSRC_ ## src] = { \ .freq_min = BWI_CLKSRC_ ##src## _FMIN, \ .freq_max = BWI_CLKSRC_ ##src## _FMAX \ } static const struct { u_int freq_min; u_int freq_max; } bwi_clkfreq[BWI_CLKSRC_MAX] = { CLKSRC(LP_OSC), CLKSRC(CS_OSC), CLKSRC(PCI) }; #undef CLKSRC #define VENDOR_LED_ACT(vendor) \ { \ .vid = PCI_VENDOR_##vendor, \ .led_act = { BWI_VENDOR_LED_ACT_##vendor } \ } static const struct { #define PCI_VENDOR_COMPAQ 0x0e11 #define PCI_VENDOR_LINKSYS 0x1737 uint16_t vid; uint8_t led_act[BWI_LED_MAX]; } bwi_vendor_led_act[] = { VENDOR_LED_ACT(COMPAQ), VENDOR_LED_ACT(LINKSYS) #undef PCI_VENDOR_LINKSYS #undef PCI_VENDOR_COMPAQ }; static const uint8_t bwi_default_led_act[BWI_LED_MAX] = { BWI_VENDOR_LED_ACT_DEFAULT }; #undef VENDOR_LED_ACT static const struct { int on_dur; int off_dur; } bwi_led_duration[109] = { [0] = { 400, 100 }, [2] = { 150, 75 }, [4] = { 90, 45 }, [11] = { 66, 34 }, [12] = { 53, 26 }, [18] = { 42, 21 }, [22] = { 35, 17 }, [24] = { 32, 16 }, [36] = { 21, 10 }, [48] = { 16, 8 }, [72] = { 11, 5 }, [96] = { 9, 4 }, [108] = { 7, 3 } }; #ifdef BWI_DEBUG #ifdef BWI_DEBUG_VERBOSE static uint32_t bwi_debug = BWI_DBG_ATTACH | BWI_DBG_INIT | BWI_DBG_TXPOWER; #else static uint32_t bwi_debug; #endif TUNABLE_INT("hw.bwi.debug", (int *)&bwi_debug); #endif /* BWI_DEBUG */ static const uint8_t bwi_zero_addr[IEEE80211_ADDR_LEN]; uint16_t bwi_read_sprom(struct bwi_softc *sc, uint16_t ofs) { return CSR_READ_2(sc, ofs + BWI_SPROM_START); } static __inline void bwi_setup_desc32(struct bwi_softc *sc, struct bwi_desc32 *desc_array, int ndesc, int desc_idx, bus_addr_t paddr, int buf_len, int tx) { struct bwi_desc32 *desc = &desc_array[desc_idx]; uint32_t ctrl, addr, addr_hi, addr_lo; addr_lo = __SHIFTOUT(paddr, BWI_DESC32_A_ADDR_MASK); addr_hi = __SHIFTOUT(paddr, BWI_DESC32_A_FUNC_MASK); addr = __SHIFTIN(addr_lo, BWI_DESC32_A_ADDR_MASK) | __SHIFTIN(BWI_DESC32_A_FUNC_TXRX, BWI_DESC32_A_FUNC_MASK); ctrl = __SHIFTIN(buf_len, BWI_DESC32_C_BUFLEN_MASK) | __SHIFTIN(addr_hi, BWI_DESC32_C_ADDRHI_MASK); if (desc_idx == ndesc - 1) ctrl |= BWI_DESC32_C_EOR; if (tx) { /* XXX */ ctrl |= BWI_DESC32_C_FRAME_START | BWI_DESC32_C_FRAME_END | BWI_DESC32_C_INTR; } desc->addr = htole32(addr); desc->ctrl = htole32(ctrl); } int bwi_attach(struct bwi_softc *sc) { struct ieee80211com *ic; device_t dev = sc->sc_dev; struct ifnet *ifp; struct bwi_mac *mac; struct bwi_phy *phy; int i, error; uint8_t bands; uint8_t macaddr[IEEE80211_ADDR_LEN]; BWI_LOCK_INIT(sc); /* * Initialize taskq and various tasks */ sc->sc_tq = taskqueue_create("bwi_taskq", M_NOWAIT | M_ZERO, taskqueue_thread_enqueue, &sc->sc_tq); taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(dev)); TASK_INIT(&sc->sc_restart_task, 0, bwi_restart, sc); callout_init_mtx(&sc->sc_calib_ch, &sc->sc_mtx, 0); /* * Initialize sysctl variables */ sc->sc_fw_version = BWI_FW_VERSION3; sc->sc_led_idle = (2350 * hz) / 1000; sc->sc_led_blink = 1; sc->sc_txpwr_calib = 1; #ifdef BWI_DEBUG sc->sc_debug = bwi_debug; #endif bwi_power_on(sc, 1); error = bwi_bbp_attach(sc); if (error) goto fail; error = bwi_bbp_power_on(sc, BWI_CLOCK_MODE_FAST); if (error) goto fail; if (BWI_REGWIN_EXIST(&sc->sc_com_regwin)) { error = bwi_set_clock_delay(sc); if (error) goto fail; error = bwi_set_clock_mode(sc, BWI_CLOCK_MODE_FAST); if (error) goto fail; error = bwi_get_pwron_delay(sc); if (error) goto fail; } error = bwi_bus_attach(sc); if (error) goto fail; bwi_get_card_flags(sc); bwi_led_attach(sc); for (i = 0; i < sc->sc_nmac; ++i) { struct bwi_regwin *old; mac = &sc->sc_mac[i]; error = bwi_regwin_switch(sc, &mac->mac_regwin, &old); if (error) goto fail; error = bwi_mac_lateattach(mac); if (error) goto fail; error = bwi_regwin_switch(sc, old, NULL); if (error) goto fail; } /* * XXX First MAC is known to exist * TODO2 */ mac = &sc->sc_mac[0]; phy = &mac->mac_phy; bwi_bbp_power_off(sc); error = bwi_dma_alloc(sc); if (error) goto fail; ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not if_alloc()\n"); error = ENOSPC; goto fail; } ic = ifp->if_l2com; /* set these up early for if_printf use */ if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = bwi_init; ifp->if_ioctl = bwi_ioctl; ifp->if_start = bwi_start; ifp->if_watchdog = bwi_watchdog; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); /* * Setup ratesets, phytype, channels and get MAC address */ bands = 0; if (phy->phy_mode == IEEE80211_MODE_11B || phy->phy_mode == IEEE80211_MODE_11G) { setbit(&bands, IEEE80211_MODE_11B); if (phy->phy_mode == IEEE80211_MODE_11B) { ic->ic_phytype = IEEE80211_T_DS; } else { ic->ic_phytype = IEEE80211_T_OFDM; setbit(&bands, IEEE80211_MODE_11G); } bwi_get_eaddr(sc, BWI_SPROM_11BG_EADDR, macaddr); if (IEEE80211_IS_MULTICAST(macaddr)) { bwi_get_eaddr(sc, BWI_SPROM_11A_EADDR, macaddr); if (IEEE80211_IS_MULTICAST(macaddr)) { device_printf(dev, "invalid MAC address: %6D\n", macaddr, ":"); } } } else if (phy->phy_mode == IEEE80211_MODE_11A) { /* TODO:11A */ setbit(&bands, IEEE80211_MODE_11A); error = ENXIO; goto fail; } else { panic("unknown phymode %d\n", phy->phy_mode); } /* Get locale */ sc->sc_locale = __SHIFTOUT(bwi_read_sprom(sc, BWI_SPROM_CARD_INFO), BWI_SPROM_CARD_INFO_LOCALE); DPRINTF(sc, BWI_DBG_ATTACH, "locale: %d\n", sc->sc_locale); /* XXX use locale */ ieee80211_init_channels(ic, NULL, &bands); ic->ic_ifp = ifp; ic->ic_caps = IEEE80211_C_STA | IEEE80211_C_SHSLOT | IEEE80211_C_SHPREAMBLE | IEEE80211_C_WPA | IEEE80211_C_BGSCAN | IEEE80211_C_MONITOR; ic->ic_opmode = IEEE80211_M_STA; ieee80211_ifattach(ic, macaddr); ic->ic_headroom = sizeof(struct bwi_txbuf_hdr); /* override default methods */ ic->ic_vap_create = bwi_vap_create; ic->ic_vap_delete = bwi_vap_delete; ic->ic_raw_xmit = bwi_raw_xmit; ic->ic_updateslot = bwi_updateslot; ic->ic_node_alloc = bwi_node_alloc; ic->ic_scan_start = bwi_scan_start; ic->ic_scan_end = bwi_scan_end; ic->ic_set_channel = bwi_set_channel; sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); ieee80211_radiotap_attach(ic, &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), BWI_TX_RADIOTAP_PRESENT, &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), BWI_RX_RADIOTAP_PRESENT); /* * Add sysctl nodes */ SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fw_version", CTLFLAG_RD, &sc->sc_fw_version, 0, "Firmware version"); SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "led_idle", CTLFLAG_RW, &sc->sc_led_idle, 0, "# ticks before LED enters idle state"); SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "led_blink", CTLFLAG_RW, &sc->sc_led_blink, 0, "Allow LED to blink"); SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "txpwr_calib", CTLFLAG_RW, &sc->sc_txpwr_calib, 0, "Enable software TX power calibration"); #ifdef BWI_DEBUG SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, 0, "Debug flags"); #endif if (bootverbose) ieee80211_announce(ic); return (0); fail: BWI_LOCK_DESTROY(sc); return (error); } int bwi_detach(struct bwi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int i; bwi_stop(sc, 1); callout_drain(&sc->sc_calib_ch); ieee80211_ifdetach(ic); for (i = 0; i < sc->sc_nmac; ++i) bwi_mac_detach(&sc->sc_mac[i]); bwi_dma_free(sc); if_free(ifp); taskqueue_free(sc->sc_tq); BWI_LOCK_DESTROY(sc); return (0); } static struct ieee80211vap * bwi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct bwi_vap *bvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; bvp = (struct bwi_vap *) malloc(sizeof(struct bwi_vap), M_80211_VAP, M_WAITOK | M_ZERO); if (bvp == NULL) return NULL; vap = &bvp->bv_vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override default methods */ bvp->bv_newstate = vap->iv_newstate; vap->iv_newstate = bwi_newstate; #if 0 vap->iv_update_beacon = bwi_beacon_update; #endif ieee80211_amrr_init(&bvp->bv_amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 500 /*ms*/); /* complete setup */ ieee80211_vap_attach(vap, bwi_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return vap; } static void bwi_vap_delete(struct ieee80211vap *vap) { struct bwi_vap *bvp = BWI_VAP(vap); ieee80211_amrr_cleanup(&bvp->bv_amrr); ieee80211_vap_detach(vap); free(bvp, M_80211_VAP); } void bwi_suspend(struct bwi_softc *sc) { bwi_stop(sc, 1); } void bwi_resume(struct bwi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; if (ifp->if_flags & IFF_UP) bwi_init(sc); } int bwi_shutdown(struct bwi_softc *sc) { bwi_stop(sc, 1); return 0; } static void bwi_power_on(struct bwi_softc *sc, int with_pll) { uint32_t gpio_in, gpio_out, gpio_en; uint16_t status; gpio_in = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_IN, 4); if (gpio_in & BWI_PCIM_GPIO_PWR_ON) goto back; gpio_out = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, 4); gpio_en = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, 4); gpio_out |= BWI_PCIM_GPIO_PWR_ON; gpio_en |= BWI_PCIM_GPIO_PWR_ON; if (with_pll) { /* Turn off PLL first */ gpio_out |= BWI_PCIM_GPIO_PLL_PWR_OFF; gpio_en |= BWI_PCIM_GPIO_PLL_PWR_OFF; } pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, gpio_out, 4); pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, gpio_en, 4); DELAY(1000); if (with_pll) { /* Turn on PLL */ gpio_out &= ~BWI_PCIM_GPIO_PLL_PWR_OFF; pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, gpio_out, 4); DELAY(5000); } back: /* Clear "Signaled Target Abort" */ status = pci_read_config(sc->sc_dev, PCIR_STATUS, 2); status &= ~PCIM_STATUS_STABORT; pci_write_config(sc->sc_dev, PCIR_STATUS, status, 2); } static int bwi_power_off(struct bwi_softc *sc, int with_pll) { uint32_t gpio_out, gpio_en; pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_IN, 4); /* dummy read */ gpio_out = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, 4); gpio_en = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, 4); gpio_out &= ~BWI_PCIM_GPIO_PWR_ON; gpio_en |= BWI_PCIM_GPIO_PWR_ON; if (with_pll) { gpio_out |= BWI_PCIM_GPIO_PLL_PWR_OFF; gpio_en |= BWI_PCIM_GPIO_PLL_PWR_OFF; } pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, gpio_out, 4); pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, gpio_en, 4); return 0; } int bwi_regwin_switch(struct bwi_softc *sc, struct bwi_regwin *rw, struct bwi_regwin **old_rw) { int error; if (old_rw != NULL) *old_rw = NULL; if (!BWI_REGWIN_EXIST(rw)) return EINVAL; if (sc->sc_cur_regwin != rw) { error = bwi_regwin_select(sc, rw->rw_id); if (error) { device_printf(sc->sc_dev, "can't select regwin %d\n", rw->rw_id); return error; } } if (old_rw != NULL) *old_rw = sc->sc_cur_regwin; sc->sc_cur_regwin = rw; return 0; } static int bwi_regwin_select(struct bwi_softc *sc, int id) { uint32_t win = BWI_PCIM_REGWIN(id); int i; #define RETRY_MAX 50 for (i = 0; i < RETRY_MAX; ++i) { pci_write_config(sc->sc_dev, BWI_PCIR_SEL_REGWIN, win, 4); if (pci_read_config(sc->sc_dev, BWI_PCIR_SEL_REGWIN, 4) == win) return 0; DELAY(10); } #undef RETRY_MAX return ENXIO; } static void bwi_regwin_info(struct bwi_softc *sc, uint16_t *type, uint8_t *rev) { uint32_t val; val = CSR_READ_4(sc, BWI_ID_HI); *type = BWI_ID_HI_REGWIN_TYPE(val); *rev = BWI_ID_HI_REGWIN_REV(val); DPRINTF(sc, BWI_DBG_ATTACH, "regwin: type 0x%03x, rev %d, " "vendor 0x%04x\n", *type, *rev, __SHIFTOUT(val, BWI_ID_HI_REGWIN_VENDOR_MASK)); } static int bwi_bbp_attach(struct bwi_softc *sc) { #define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) uint16_t bbp_id, rw_type; uint8_t rw_rev; uint32_t info; int error, nregwin, i; /* * Get 0th regwin information * NOTE: 0th regwin should exist */ error = bwi_regwin_select(sc, 0); if (error) { device_printf(sc->sc_dev, "can't select regwin 0\n"); return error; } bwi_regwin_info(sc, &rw_type, &rw_rev); /* * Find out BBP id */ bbp_id = 0; info = 0; if (rw_type == BWI_REGWIN_T_COM) { info = CSR_READ_4(sc, BWI_INFO); bbp_id = __SHIFTOUT(info, BWI_INFO_BBPID_MASK); BWI_CREATE_REGWIN(&sc->sc_com_regwin, 0, rw_type, rw_rev); sc->sc_cap = CSR_READ_4(sc, BWI_CAPABILITY); } else { for (i = 0; i < N(bwi_bbpid_map); ++i) { if (sc->sc_pci_did >= bwi_bbpid_map[i].did_min && sc->sc_pci_did <= bwi_bbpid_map[i].did_max) { bbp_id = bwi_bbpid_map[i].bbp_id; break; } } if (bbp_id == 0) { device_printf(sc->sc_dev, "no BBP id for device id " "0x%04x\n", sc->sc_pci_did); return ENXIO; } info = __SHIFTIN(sc->sc_pci_revid, BWI_INFO_BBPREV_MASK) | __SHIFTIN(0, BWI_INFO_BBPPKG_MASK); } /* * Find out number of regwins */ nregwin = 0; if (rw_type == BWI_REGWIN_T_COM && rw_rev >= 4) { nregwin = __SHIFTOUT(info, BWI_INFO_NREGWIN_MASK); } else { for (i = 0; i < N(bwi_regwin_count); ++i) { if (bwi_regwin_count[i].bbp_id == bbp_id) { nregwin = bwi_regwin_count[i].nregwin; break; } } if (nregwin == 0) { device_printf(sc->sc_dev, "no number of win for " "BBP id 0x%04x\n", bbp_id); return ENXIO; } } /* Record BBP id/rev for later using */ sc->sc_bbp_id = bbp_id; sc->sc_bbp_rev = __SHIFTOUT(info, BWI_INFO_BBPREV_MASK); sc->sc_bbp_pkg = __SHIFTOUT(info, BWI_INFO_BBPPKG_MASK); device_printf(sc->sc_dev, "BBP: id 0x%04x, rev 0x%x, pkg %d\n", sc->sc_bbp_id, sc->sc_bbp_rev, sc->sc_bbp_pkg); DPRINTF(sc, BWI_DBG_ATTACH, "nregwin %d, cap 0x%08x\n", nregwin, sc->sc_cap); /* * Create rest of the regwins */ /* Don't re-create common regwin, if it is already created */ i = BWI_REGWIN_EXIST(&sc->sc_com_regwin) ? 1 : 0; for (; i < nregwin; ++i) { /* * Get regwin information */ error = bwi_regwin_select(sc, i); if (error) { device_printf(sc->sc_dev, "can't select regwin %d\n", i); return error; } bwi_regwin_info(sc, &rw_type, &rw_rev); /* * Try attach: * 1) Bus (PCI/PCIE) regwin * 2) MAC regwin * Ignore rest types of regwin */ if (rw_type == BWI_REGWIN_T_BUSPCI || rw_type == BWI_REGWIN_T_BUSPCIE) { if (BWI_REGWIN_EXIST(&sc->sc_bus_regwin)) { device_printf(sc->sc_dev, "bus regwin already exists\n"); } else { BWI_CREATE_REGWIN(&sc->sc_bus_regwin, i, rw_type, rw_rev); } } else if (rw_type == BWI_REGWIN_T_MAC) { /* XXX ignore return value */ bwi_mac_attach(sc, i, rw_rev); } } /* At least one MAC shold exist */ if (!BWI_REGWIN_EXIST(&sc->sc_mac[0].mac_regwin)) { device_printf(sc->sc_dev, "no MAC was found\n"); return ENXIO; } KASSERT(sc->sc_nmac > 0, ("no mac's")); /* Bus regwin must exist */ if (!BWI_REGWIN_EXIST(&sc->sc_bus_regwin)) { device_printf(sc->sc_dev, "no bus regwin was found\n"); return ENXIO; } /* Start with first MAC */ error = bwi_regwin_switch(sc, &sc->sc_mac[0].mac_regwin, NULL); if (error) return error; return 0; #undef N } int bwi_bus_init(struct bwi_softc *sc, struct bwi_mac *mac) { struct bwi_regwin *old, *bus; uint32_t val; int error; bus = &sc->sc_bus_regwin; KASSERT(sc->sc_cur_regwin == &mac->mac_regwin, ("not cur regwin")); /* * Tell bus to generate requested interrupts */ if (bus->rw_rev < 6 && bus->rw_type == BWI_REGWIN_T_BUSPCI) { /* * NOTE: Read BWI_FLAGS from MAC regwin */ val = CSR_READ_4(sc, BWI_FLAGS); error = bwi_regwin_switch(sc, bus, &old); if (error) return error; CSR_SETBITS_4(sc, BWI_INTRVEC, (val & BWI_FLAGS_INTR_MASK)); } else { uint32_t mac_mask; mac_mask = 1 << mac->mac_id; error = bwi_regwin_switch(sc, bus, &old); if (error) return error; val = pci_read_config(sc->sc_dev, BWI_PCIR_INTCTL, 4); val |= mac_mask << 8; pci_write_config(sc->sc_dev, BWI_PCIR_INTCTL, val, 4); } if (sc->sc_flags & BWI_F_BUS_INITED) goto back; if (bus->rw_type == BWI_REGWIN_T_BUSPCI) { /* * Enable prefetch and burst */ CSR_SETBITS_4(sc, BWI_BUS_CONFIG, BWI_BUS_CONFIG_PREFETCH | BWI_BUS_CONFIG_BURST); if (bus->rw_rev < 5) { struct bwi_regwin *com = &sc->sc_com_regwin; /* * Configure timeouts for bus operation */ /* * Set service timeout and request timeout */ CSR_SETBITS_4(sc, BWI_CONF_LO, __SHIFTIN(BWI_CONF_LO_SERVTO, BWI_CONF_LO_SERVTO_MASK) | __SHIFTIN(BWI_CONF_LO_REQTO, BWI_CONF_LO_REQTO_MASK)); /* * If there is common regwin, we switch to that regwin * and switch back to bus regwin once we have done. */ if (BWI_REGWIN_EXIST(com)) { error = bwi_regwin_switch(sc, com, NULL); if (error) return error; } /* Let bus know what we have changed */ CSR_WRITE_4(sc, BWI_BUS_ADDR, BWI_BUS_ADDR_MAGIC); CSR_READ_4(sc, BWI_BUS_ADDR); /* Flush */ CSR_WRITE_4(sc, BWI_BUS_DATA, 0); CSR_READ_4(sc, BWI_BUS_DATA); /* Flush */ if (BWI_REGWIN_EXIST(com)) { error = bwi_regwin_switch(sc, bus, NULL); if (error) return error; } } else if (bus->rw_rev >= 11) { /* * Enable memory read multiple */ CSR_SETBITS_4(sc, BWI_BUS_CONFIG, BWI_BUS_CONFIG_MRM); } } else { /* TODO:PCIE */ } sc->sc_flags |= BWI_F_BUS_INITED; back: return bwi_regwin_switch(sc, old, NULL); } static void bwi_get_card_flags(struct bwi_softc *sc) { #define PCI_VENDOR_APPLE 0x106b #define PCI_VENDOR_DELL 0x1028 sc->sc_card_flags = bwi_read_sprom(sc, BWI_SPROM_CARD_FLAGS); if (sc->sc_card_flags == 0xffff) sc->sc_card_flags = 0; if (sc->sc_pci_subvid == PCI_VENDOR_DELL && sc->sc_bbp_id == BWI_BBPID_BCM4301 && sc->sc_pci_revid == 0x74) sc->sc_card_flags |= BWI_CARD_F_BT_COEXIST; if (sc->sc_pci_subvid == PCI_VENDOR_APPLE && sc->sc_pci_subdid == 0x4e && /* XXX */ sc->sc_pci_revid > 0x40) sc->sc_card_flags |= BWI_CARD_F_PA_GPIO9; DPRINTF(sc, BWI_DBG_ATTACH, "card flags 0x%04x\n", sc->sc_card_flags); #undef PCI_VENDOR_DELL #undef PCI_VENDOR_APPLE } static void bwi_get_eaddr(struct bwi_softc *sc, uint16_t eaddr_ofs, uint8_t *eaddr) { int i; for (i = 0; i < 3; ++i) { *((uint16_t *)eaddr + i) = htobe16(bwi_read_sprom(sc, eaddr_ofs + 2 * i)); } } static void bwi_get_clock_freq(struct bwi_softc *sc, struct bwi_clock_freq *freq) { struct bwi_regwin *com; uint32_t val; u_int div; int src; bzero(freq, sizeof(*freq)); com = &sc->sc_com_regwin; KASSERT(BWI_REGWIN_EXIST(com), ("regwin does not exist")); KASSERT(sc->sc_cur_regwin == com, ("wrong regwin")); KASSERT(sc->sc_cap & BWI_CAP_CLKMODE, ("wrong clock mode")); /* * Calculate clock frequency */ src = -1; div = 0; if (com->rw_rev < 6) { val = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, 4); if (val & BWI_PCIM_GPIO_OUT_CLKSRC) { src = BWI_CLKSRC_PCI; div = 64; } else { src = BWI_CLKSRC_CS_OSC; div = 32; } } else if (com->rw_rev < 10) { val = CSR_READ_4(sc, BWI_CLOCK_CTRL); src = __SHIFTOUT(val, BWI_CLOCK_CTRL_CLKSRC); if (src == BWI_CLKSRC_LP_OSC) { div = 1; } else { div = (__SHIFTOUT(val, BWI_CLOCK_CTRL_FDIV) + 1) << 2; /* Unknown source */ if (src >= BWI_CLKSRC_MAX) src = BWI_CLKSRC_CS_OSC; } } else { val = CSR_READ_4(sc, BWI_CLOCK_INFO); src = BWI_CLKSRC_CS_OSC; div = (__SHIFTOUT(val, BWI_CLOCK_INFO_FDIV) + 1) << 2; } KASSERT(src >= 0 && src < BWI_CLKSRC_MAX, ("bad src %d", src)); KASSERT(div != 0, ("div zero")); DPRINTF(sc, BWI_DBG_ATTACH, "clksrc %s\n", src == BWI_CLKSRC_PCI ? "PCI" : (src == BWI_CLKSRC_LP_OSC ? "LP_OSC" : "CS_OSC")); freq->clkfreq_min = bwi_clkfreq[src].freq_min / div; freq->clkfreq_max = bwi_clkfreq[src].freq_max / div; DPRINTF(sc, BWI_DBG_ATTACH, "clkfreq min %u, max %u\n", freq->clkfreq_min, freq->clkfreq_max); } static int bwi_set_clock_mode(struct bwi_softc *sc, enum bwi_clock_mode clk_mode) { struct bwi_regwin *old, *com; uint32_t clk_ctrl, clk_src; int error, pwr_off = 0; com = &sc->sc_com_regwin; if (!BWI_REGWIN_EXIST(com)) return 0; if (com->rw_rev >= 10 || com->rw_rev < 6) return 0; /* * For common regwin whose rev is [6, 10), the chip * must be capable to change clock mode. */ if ((sc->sc_cap & BWI_CAP_CLKMODE) == 0) return 0; error = bwi_regwin_switch(sc, com, &old); if (error) return error; if (clk_mode == BWI_CLOCK_MODE_FAST) bwi_power_on(sc, 0); /* Don't turn on PLL */ clk_ctrl = CSR_READ_4(sc, BWI_CLOCK_CTRL); clk_src = __SHIFTOUT(clk_ctrl, BWI_CLOCK_CTRL_CLKSRC); switch (clk_mode) { case BWI_CLOCK_MODE_FAST: clk_ctrl &= ~BWI_CLOCK_CTRL_SLOW; clk_ctrl |= BWI_CLOCK_CTRL_IGNPLL; break; case BWI_CLOCK_MODE_SLOW: clk_ctrl |= BWI_CLOCK_CTRL_SLOW; break; case BWI_CLOCK_MODE_DYN: clk_ctrl &= ~(BWI_CLOCK_CTRL_SLOW | BWI_CLOCK_CTRL_IGNPLL | BWI_CLOCK_CTRL_NODYN); if (clk_src != BWI_CLKSRC_CS_OSC) { clk_ctrl |= BWI_CLOCK_CTRL_NODYN; pwr_off = 1; } break; } CSR_WRITE_4(sc, BWI_CLOCK_CTRL, clk_ctrl); if (pwr_off) bwi_power_off(sc, 0); /* Leave PLL as it is */ return bwi_regwin_switch(sc, old, NULL); } static int bwi_set_clock_delay(struct bwi_softc *sc) { struct bwi_regwin *old, *com; int error; com = &sc->sc_com_regwin; if (!BWI_REGWIN_EXIST(com)) return 0; error = bwi_regwin_switch(sc, com, &old); if (error) return error; if (sc->sc_bbp_id == BWI_BBPID_BCM4321) { if (sc->sc_bbp_rev == 0) CSR_WRITE_4(sc, BWI_CONTROL, BWI_CONTROL_MAGIC0); else if (sc->sc_bbp_rev == 1) CSR_WRITE_4(sc, BWI_CONTROL, BWI_CONTROL_MAGIC1); } if (sc->sc_cap & BWI_CAP_CLKMODE) { if (com->rw_rev >= 10) { CSR_FILT_SETBITS_4(sc, BWI_CLOCK_INFO, 0xffff, 0x40000); } else { struct bwi_clock_freq freq; bwi_get_clock_freq(sc, &freq); CSR_WRITE_4(sc, BWI_PLL_ON_DELAY, howmany(freq.clkfreq_max * 150, 1000000)); CSR_WRITE_4(sc, BWI_FREQ_SEL_DELAY, howmany(freq.clkfreq_max * 15, 1000000)); } } return bwi_regwin_switch(sc, old, NULL); } static void bwi_init(void *xsc) { struct bwi_softc *sc = xsc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; BWI_LOCK(sc); bwi_init_statechg(sc, 1); BWI_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vap's */ } static void bwi_init_statechg(struct bwi_softc *sc, int statechg) { struct ifnet *ifp = sc->sc_ifp; struct bwi_mac *mac; int error; bwi_stop_locked(sc, statechg); bwi_bbp_power_on(sc, BWI_CLOCK_MODE_FAST); /* TODO: 2 MAC */ mac = &sc->sc_mac[0]; error = bwi_regwin_switch(sc, &mac->mac_regwin, NULL); if (error) { if_printf(ifp, "%s: error %d on regwin switch\n", __func__, error); goto bad; } error = bwi_mac_init(mac); if (error) { if_printf(ifp, "%s: error %d on MAC init\n", __func__, error); goto bad; } bwi_bbp_power_on(sc, BWI_CLOCK_MODE_DYN); bwi_set_bssid(sc, bwi_zero_addr); /* Clear BSSID */ bwi_set_addr_filter(sc, BWI_ADDR_FILTER_MYADDR, IF_LLADDR(ifp)); bwi_mac_reset_hwkeys(mac); if ((mac->mac_flags & BWI_MAC_F_HAS_TXSTATS) == 0) { int i; #define NRETRY 1000 /* * Drain any possible pending TX status */ for (i = 0; i < NRETRY; ++i) { if ((CSR_READ_4(sc, BWI_TXSTATUS0) & BWI_TXSTATUS0_VALID) == 0) break; CSR_READ_4(sc, BWI_TXSTATUS1); } if (i == NRETRY) if_printf(ifp, "%s: can't drain TX status\n", __func__); #undef NRETRY } if (mac->mac_phy.phy_mode == IEEE80211_MODE_11G) bwi_mac_updateslot(mac, 1); /* Start MAC */ error = bwi_mac_start(mac); if (error) { if_printf(ifp, "%s: error %d starting MAC\n", __func__, error); goto bad; } /* Clear stop flag before enabling interrupt */ sc->sc_flags &= ~BWI_F_STOP; ifp->if_drv_flags |= IFF_DRV_RUNNING; /* Enable intrs */ bwi_enable_intrs(sc, BWI_INIT_INTRS); return; bad: bwi_stop_locked(sc, 1); } static int bwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { #define IS_RUNNING(ifp) \ ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) struct bwi_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: BWI_LOCK(sc); if (IS_RUNNING(ifp)) { struct bwi_mac *mac; int promisc = -1; KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; if ((ifp->if_flags & IFF_PROMISC) && (sc->sc_flags & BWI_F_PROMISC) == 0) { promisc = 1; sc->sc_flags |= BWI_F_PROMISC; } else if ((ifp->if_flags & IFF_PROMISC) == 0 && (sc->sc_flags & BWI_F_PROMISC)) { promisc = 0; sc->sc_flags &= ~BWI_F_PROMISC; } if (promisc >= 0) bwi_mac_set_promisc(mac, promisc); } if (ifp->if_flags & IFF_UP) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { bwi_init_statechg(sc, 1); startall = 1; } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) bwi_stop_locked(sc, 1); } BWI_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; #undef IS_RUNNING } static void bwi_start(struct ifnet *ifp) { struct bwi_softc *sc = ifp->if_softc; BWI_LOCK(sc); bwi_start_locked(ifp); BWI_UNLOCK(sc); } static void bwi_start_locked(struct ifnet *ifp) { struct bwi_softc *sc = ifp->if_softc; struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct ieee80211_key *k; struct mbuf *m; int trans, idx; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; trans = 0; idx = tbd->tbd_idx; while (tbd->tbd_buf[idx].tb_mbuf == NULL) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); /* XXX: LOCK */ if (m == NULL) break; ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { ieee80211_free_node(ni); m_freem(m); ifp->if_oerrors++; continue; } } wh = NULL; /* Catch any invalid use */ if (bwi_encap(sc, idx, m, ni) != 0) { /* 'm' is freed in bwi_encap() if we reach here */ if (ni != NULL) ieee80211_free_node(ni); ifp->if_oerrors++; continue; } trans = 1; tbd->tbd_used++; idx = (idx + 1) % BWI_TX_NDESC; ifp->if_opackets++; if (tbd->tbd_used + BWI_TX_NSPRDESC >= BWI_TX_NDESC) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } } tbd->tbd_idx = idx; if (trans) ifp->if_timer = 5; } static int bwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct bwi_softc *sc = ifp->if_softc; /* XXX wme? */ struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; int idx, error; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { ieee80211_free_node(ni); m_freem(m); return ENETDOWN; } BWI_LOCK(sc); idx = tbd->tbd_idx; KASSERT(tbd->tbd_buf[idx].tb_mbuf == NULL, ("slot %d not empty", idx)); if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ error = bwi_encap(sc, idx, m, ni); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ error = bwi_encap_raw(sc, idx, m, ni, params); } if (error == 0) { ifp->if_opackets++; if (++tbd->tbd_used + BWI_TX_NSPRDESC >= BWI_TX_NDESC) ifp->if_drv_flags |= IFF_DRV_OACTIVE; tbd->tbd_idx = (idx + 1) % BWI_TX_NDESC; ifp->if_timer = 5; } else { /* NB: m is reclaimed on encap failure */ ieee80211_free_node(ni); ifp->if_oerrors++; } BWI_UNLOCK(sc); return error; } static void bwi_watchdog(struct ifnet *ifp) { struct bwi_softc *sc = ifp->if_softc; BWI_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING)) { if_printf(ifp, "watchdog timeout\n"); ifp->if_oerrors++; taskqueue_enqueue(sc->sc_tq, &sc->sc_restart_task); } BWI_UNLOCK(sc); } static void bwi_stop(struct bwi_softc *sc, int statechg) { BWI_LOCK(sc); bwi_stop_locked(sc, statechg); BWI_UNLOCK(sc); } static void bwi_stop_locked(struct bwi_softc *sc, int statechg) { struct ifnet *ifp = sc->sc_ifp; struct bwi_mac *mac; int i, error, pwr_off = 0; BWI_ASSERT_LOCKED(sc); callout_stop(&sc->sc_calib_ch); callout_stop(&sc->sc_led_blink_ch); sc->sc_led_blinking = 0; sc->sc_flags |= BWI_F_STOP; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; bwi_disable_intrs(sc, BWI_ALL_INTRS); CSR_READ_4(sc, BWI_MAC_INTR_MASK); bwi_mac_stop(mac); } for (i = 0; i < sc->sc_nmac; ++i) { struct bwi_regwin *old_rw; mac = &sc->sc_mac[i]; if ((mac->mac_flags & BWI_MAC_F_INITED) == 0) continue; error = bwi_regwin_switch(sc, &mac->mac_regwin, &old_rw); if (error) continue; bwi_mac_shutdown(mac); pwr_off = 1; bwi_regwin_switch(sc, old_rw, NULL); } if (pwr_off) bwi_bbp_power_off(sc); sc->sc_tx_timer = 0; ifp->if_timer = 0; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); } void bwi_intr(void *xsc) { struct bwi_softc *sc = xsc; struct ifnet *ifp = sc->sc_ifp; struct bwi_mac *mac; uint32_t intr_status; uint32_t txrx_intr_status[BWI_TXRX_NRING]; int i, txrx_error, tx = 0, rx_data = -1; BWI_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (sc->sc_flags & BWI_F_STOP)) { BWI_UNLOCK(sc); return; } /* * Get interrupt status */ intr_status = CSR_READ_4(sc, BWI_MAC_INTR_STATUS); if (intr_status == 0xffffffff) { /* Not for us */ BWI_UNLOCK(sc); return; } DPRINTF(sc, BWI_DBG_INTR, "intr status 0x%08x\n", intr_status); intr_status &= CSR_READ_4(sc, BWI_MAC_INTR_MASK); if (intr_status == 0) { /* Nothing is interesting */ BWI_UNLOCK(sc); return; } KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; txrx_error = 0; DPRINTF(sc, BWI_DBG_INTR, "%s\n", "TX/RX intr"); for (i = 0; i < BWI_TXRX_NRING; ++i) { uint32_t mask; if (BWI_TXRX_IS_RX(i)) mask = BWI_TXRX_RX_INTRS; else mask = BWI_TXRX_TX_INTRS; txrx_intr_status[i] = CSR_READ_4(sc, BWI_TXRX_INTR_STATUS(i)) & mask; _DPRINTF(sc, BWI_DBG_INTR, ", %d 0x%08x", i, txrx_intr_status[i]); if (txrx_intr_status[i] & BWI_TXRX_INTR_ERROR) { if_printf(ifp, "%s: intr fatal TX/RX (%d) error 0x%08x\n", __func__, i, txrx_intr_status[i]); txrx_error = 1; } } _DPRINTF(sc, BWI_DBG_INTR, "%s\n", ""); /* * Acknowledge interrupt */ CSR_WRITE_4(sc, BWI_MAC_INTR_STATUS, intr_status); for (i = 0; i < BWI_TXRX_NRING; ++i) CSR_WRITE_4(sc, BWI_TXRX_INTR_STATUS(i), txrx_intr_status[i]); /* Disable all interrupts */ bwi_disable_intrs(sc, BWI_ALL_INTRS); if (intr_status & BWI_INTR_PHY_TXERR) { if (mac->mac_flags & BWI_MAC_F_PHYE_RESET) { if_printf(ifp, "%s: intr PHY TX error\n", __func__); taskqueue_enqueue(sc->sc_tq, &sc->sc_restart_task); BWI_UNLOCK(sc); return; } } if (txrx_error) { /* TODO: reset device */ } if (intr_status & BWI_INTR_TBTT) bwi_mac_config_ps(mac); if (intr_status & BWI_INTR_EO_ATIM) if_printf(ifp, "EO_ATIM\n"); if (intr_status & BWI_INTR_PMQ) { for (;;) { if ((CSR_READ_4(sc, BWI_MAC_PS_STATUS) & 0x8) == 0) break; } CSR_WRITE_2(sc, BWI_MAC_PS_STATUS, 0x2); } if (intr_status & BWI_INTR_NOISE) if_printf(ifp, "intr noise\n"); if (txrx_intr_status[0] & BWI_TXRX_INTR_RX) { rx_data = sc->sc_rxeof(sc); if (sc->sc_flags & BWI_F_STOP) { BWI_UNLOCK(sc); return; } } if (txrx_intr_status[3] & BWI_TXRX_INTR_RX) { sc->sc_txeof_status(sc); tx = 1; } if (intr_status & BWI_INTR_TX_DONE) { bwi_txeof(sc); tx = 1; } /* Re-enable interrupts */ bwi_enable_intrs(sc, BWI_INIT_INTRS); if (sc->sc_blink_led != NULL && sc->sc_led_blink) { int evt = BWI_LED_EVENT_NONE; if (tx && rx_data > 0) { if (sc->sc_rx_rate > sc->sc_tx_rate) evt = BWI_LED_EVENT_RX; else evt = BWI_LED_EVENT_TX; } else if (tx) { evt = BWI_LED_EVENT_TX; } else if (rx_data > 0) { evt = BWI_LED_EVENT_RX; } else if (rx_data == 0) { evt = BWI_LED_EVENT_POLL; } if (evt != BWI_LED_EVENT_NONE) bwi_led_event(sc, evt); } BWI_UNLOCK(sc); } static void bwi_scan_start(struct ieee80211com *ic) { struct bwi_softc *sc = ic->ic_ifp->if_softc; BWI_LOCK(sc); /* Enable MAC beacon promiscuity */ CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_PASS_BCN); BWI_UNLOCK(sc); } static void bwi_set_channel(struct ieee80211com *ic) { struct bwi_softc *sc = ic->ic_ifp->if_softc; struct ieee80211_channel *c = ic->ic_curchan; struct bwi_mac *mac; BWI_LOCK(sc); KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; bwi_rf_set_chan(mac, ieee80211_chan2ieee(ic, c), 0); sc->sc_rates = ieee80211_get_ratetable(c); /* * Setup radio tap channel freq and flags */ sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq = htole16(c->ic_freq); sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags = htole16(c->ic_flags & 0xffff); BWI_UNLOCK(sc); } static void bwi_scan_end(struct ieee80211com *ic) { struct bwi_softc *sc = ic->ic_ifp->if_softc; BWI_LOCK(sc); CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_PASS_BCN); BWI_UNLOCK(sc); } static int bwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct bwi_vap *bvp = BWI_VAP(vap); struct ifnet *ifp = vap->iv_ic->ic_ifp; struct bwi_softc *sc = ifp->if_softc; struct bwi_mac *mac; struct ieee80211_node *ni; int error; BWI_LOCK(sc); callout_stop(&sc->sc_calib_ch); if (nstate == IEEE80211_S_INIT) sc->sc_txpwrcb_type = BWI_TXPWR_INIT; bwi_led_newstate(sc, nstate); error = bvp->bv_newstate(vap, nstate, arg); if (error != 0) goto back; if (vap->iv_opmode == IEEE80211_M_MONITOR) { /* Nothing to do */ } else if (nstate == IEEE80211_S_RUN) { ni = vap->iv_bss; bwi_set_bssid(sc, vap->iv_bss->ni_bssid); KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; /* Initial TX power calibration */ bwi_mac_calibrate_txpower(mac, BWI_TXPWR_INIT); #ifdef notyet sc->sc_txpwrcb_type = BWI_TXPWR_FORCE; #else sc->sc_txpwrcb_type = BWI_TXPWR_CALIB; #endif if (vap->iv_opmode == IEEE80211_M_STA) { /* fake a join to init the tx rate */ bwi_newassoc(ni, 1); } callout_reset(&sc->sc_calib_ch, hz, bwi_calibrate, sc); } else { bwi_set_bssid(sc, bwi_zero_addr); } back: BWI_UNLOCK(sc); return error; } /* ARGUSED */ static struct ieee80211_node * bwi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct bwi_node *bn; bn = malloc(sizeof(struct bwi_node), M_80211_NODE, M_NOWAIT | M_ZERO); return bn != NULL ? &bn->ni : NULL; } static void bwi_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&BWI_VAP(vap)->bv_amrr, &BWI_NODE(ni)->amn, ni); } static int bwi_media_change(struct ifnet *ifp) { int error = ieee80211_media_change(ifp); /* NB: only the fixed rate can change and that doesn't need a reset */ return (error == ENETRESET ? 0 : error); } static int bwi_dma_alloc(struct bwi_softc *sc) { int error, i, has_txstats; bus_addr_t lowaddr = 0; bus_size_t tx_ring_sz, rx_ring_sz, desc_sz = 0; uint32_t txrx_ctrl_step = 0; has_txstats = 0; for (i = 0; i < sc->sc_nmac; ++i) { if (sc->sc_mac[i].mac_flags & BWI_MAC_F_HAS_TXSTATS) { has_txstats = 1; break; } } switch (sc->sc_bus_space) { case BWI_BUS_SPACE_30BIT: case BWI_BUS_SPACE_32BIT: if (sc->sc_bus_space == BWI_BUS_SPACE_30BIT) lowaddr = BWI_BUS_SPACE_MAXADDR; else lowaddr = BUS_SPACE_MAXADDR_32BIT; desc_sz = sizeof(struct bwi_desc32); txrx_ctrl_step = 0x20; sc->sc_init_tx_ring = bwi_init_tx_ring32; sc->sc_free_tx_ring = bwi_free_tx_ring32; sc->sc_init_rx_ring = bwi_init_rx_ring32; sc->sc_free_rx_ring = bwi_free_rx_ring32; sc->sc_setup_rxdesc = bwi_setup_rx_desc32; sc->sc_setup_txdesc = bwi_setup_tx_desc32; sc->sc_rxeof = bwi_rxeof32; sc->sc_start_tx = bwi_start_tx32; if (has_txstats) { sc->sc_init_txstats = bwi_init_txstats32; sc->sc_free_txstats = bwi_free_txstats32; sc->sc_txeof_status = bwi_txeof_status32; } break; case BWI_BUS_SPACE_64BIT: lowaddr = BUS_SPACE_MAXADDR; /* XXX */ desc_sz = sizeof(struct bwi_desc64); txrx_ctrl_step = 0x40; sc->sc_init_tx_ring = bwi_init_tx_ring64; sc->sc_free_tx_ring = bwi_free_tx_ring64; sc->sc_init_rx_ring = bwi_init_rx_ring64; sc->sc_free_rx_ring = bwi_free_rx_ring64; sc->sc_setup_rxdesc = bwi_setup_rx_desc64; sc->sc_setup_txdesc = bwi_setup_tx_desc64; sc->sc_rxeof = bwi_rxeof64; sc->sc_start_tx = bwi_start_tx64; if (has_txstats) { sc->sc_init_txstats = bwi_init_txstats64; sc->sc_free_txstats = bwi_free_txstats64; sc->sc_txeof_status = bwi_txeof_status64; } break; } KASSERT(lowaddr != 0, ("lowaddr zero")); KASSERT(desc_sz != 0, ("desc_sz zero")); KASSERT(txrx_ctrl_step != 0, ("txrx_ctrl_step zero")); tx_ring_sz = roundup(desc_sz * BWI_TX_NDESC, BWI_RING_ALIGN); rx_ring_sz = roundup(desc_sz * BWI_RX_NDESC, BWI_RING_ALIGN); /* * Create top level DMA tag */ error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ BWI_ALIGN, 0, /* alignment, bounds */ lowaddr, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, /* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->sc_parent_dtag); if (error) { device_printf(sc->sc_dev, "can't create parent DMA tag\n"); return error; } #define TXRX_CTRL(idx) (BWI_TXRX_CTRL_BASE + (idx) * txrx_ctrl_step) /* * Create TX ring DMA stuffs */ error = bus_dma_tag_create(sc->sc_parent_dtag, BWI_RING_ALIGN, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, tx_ring_sz, 1, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_txring_dtag); if (error) { device_printf(sc->sc_dev, "can't create TX ring DMA tag\n"); return error; } for (i = 0; i < BWI_TX_NRING; ++i) { error = bwi_dma_ring_alloc(sc, sc->sc_txring_dtag, &sc->sc_tx_rdata[i], tx_ring_sz, TXRX_CTRL(i)); if (error) { device_printf(sc->sc_dev, "%dth TX ring " "DMA alloc failed\n", i); return error; } } /* * Create RX ring DMA stuffs */ error = bus_dma_tag_create(sc->sc_parent_dtag, BWI_RING_ALIGN, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, rx_ring_sz, 1, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_rxring_dtag); if (error) { device_printf(sc->sc_dev, "can't create RX ring DMA tag\n"); return error; } error = bwi_dma_ring_alloc(sc, sc->sc_rxring_dtag, &sc->sc_rx_rdata, rx_ring_sz, TXRX_CTRL(0)); if (error) { device_printf(sc->sc_dev, "RX ring DMA alloc failed\n"); return error; } if (has_txstats) { error = bwi_dma_txstats_alloc(sc, TXRX_CTRL(3), desc_sz); if (error) { device_printf(sc->sc_dev, "TX stats DMA alloc failed\n"); return error; } } #undef TXRX_CTRL return bwi_dma_mbuf_create(sc); } static void bwi_dma_free(struct bwi_softc *sc) { if (sc->sc_txring_dtag != NULL) { int i; for (i = 0; i < BWI_TX_NRING; ++i) { struct bwi_ring_data *rd = &sc->sc_tx_rdata[i]; if (rd->rdata_desc != NULL) { bus_dmamap_unload(sc->sc_txring_dtag, rd->rdata_dmap); bus_dmamem_free(sc->sc_txring_dtag, rd->rdata_desc, rd->rdata_dmap); } } bus_dma_tag_destroy(sc->sc_txring_dtag); } if (sc->sc_rxring_dtag != NULL) { struct bwi_ring_data *rd = &sc->sc_rx_rdata; if (rd->rdata_desc != NULL) { bus_dmamap_unload(sc->sc_rxring_dtag, rd->rdata_dmap); bus_dmamem_free(sc->sc_rxring_dtag, rd->rdata_desc, rd->rdata_dmap); } bus_dma_tag_destroy(sc->sc_rxring_dtag); } bwi_dma_txstats_free(sc); bwi_dma_mbuf_destroy(sc, BWI_TX_NRING, 1); if (sc->sc_parent_dtag != NULL) bus_dma_tag_destroy(sc->sc_parent_dtag); } static int bwi_dma_ring_alloc(struct bwi_softc *sc, bus_dma_tag_t dtag, struct bwi_ring_data *rd, bus_size_t size, uint32_t txrx_ctrl) { int error; error = bus_dmamem_alloc(dtag, &rd->rdata_desc, BUS_DMA_WAITOK | BUS_DMA_ZERO, &rd->rdata_dmap); if (error) { device_printf(sc->sc_dev, "can't allocate DMA mem\n"); return error; } error = bus_dmamap_load(dtag, rd->rdata_dmap, rd->rdata_desc, size, bwi_dma_ring_addr, &rd->rdata_paddr, BUS_DMA_NOWAIT); if (error) { device_printf(sc->sc_dev, "can't load DMA mem\n"); bus_dmamem_free(dtag, rd->rdata_desc, rd->rdata_dmap); rd->rdata_desc = NULL; return error; } rd->rdata_txrx_ctrl = txrx_ctrl; return 0; } static int bwi_dma_txstats_alloc(struct bwi_softc *sc, uint32_t ctrl_base, bus_size_t desc_sz) { struct bwi_txstats_data *st; bus_size_t dma_size; int error; st = malloc(sizeof(*st), M_DEVBUF, M_NOWAIT | M_ZERO); if (st == NULL) { device_printf(sc->sc_dev, "can't allocate txstats data\n"); return ENOMEM; } sc->sc_txstats = st; /* * Create TX stats descriptor DMA stuffs */ dma_size = roundup(desc_sz * BWI_TXSTATS_NDESC, BWI_RING_ALIGN); error = bus_dma_tag_create(sc->sc_parent_dtag, BWI_RING_ALIGN, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, dma_size, 1, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW, NULL, NULL, &st->stats_ring_dtag); if (error) { device_printf(sc->sc_dev, "can't create txstats ring " "DMA tag\n"); return error; } error = bus_dmamem_alloc(st->stats_ring_dtag, &st->stats_ring, BUS_DMA_WAITOK | BUS_DMA_ZERO, &st->stats_ring_dmap); if (error) { device_printf(sc->sc_dev, "can't allocate txstats ring " "DMA mem\n"); bus_dma_tag_destroy(st->stats_ring_dtag); st->stats_ring_dtag = NULL; return error; } error = bus_dmamap_load(st->stats_ring_dtag, st->stats_ring_dmap, st->stats_ring, dma_size, bwi_dma_ring_addr, &st->stats_ring_paddr, BUS_DMA_NOWAIT); if (error) { device_printf(sc->sc_dev, "can't load txstats ring DMA mem\n"); bus_dmamem_free(st->stats_ring_dtag, st->stats_ring, st->stats_ring_dmap); bus_dma_tag_destroy(st->stats_ring_dtag); st->stats_ring_dtag = NULL; return error; } /* * Create TX stats DMA stuffs */ dma_size = roundup(sizeof(struct bwi_txstats) * BWI_TXSTATS_NDESC, BWI_ALIGN); error = bus_dma_tag_create(sc->sc_parent_dtag, BWI_ALIGN, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, dma_size, 1, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW, NULL, NULL, &st->stats_dtag); if (error) { device_printf(sc->sc_dev, "can't create txstats DMA tag\n"); return error; } error = bus_dmamem_alloc(st->stats_dtag, (void **)&st->stats, BUS_DMA_WAITOK | BUS_DMA_ZERO, &st->stats_dmap); if (error) { device_printf(sc->sc_dev, "can't allocate txstats DMA mem\n"); bus_dma_tag_destroy(st->stats_dtag); st->stats_dtag = NULL; return error; } error = bus_dmamap_load(st->stats_dtag, st->stats_dmap, st->stats, dma_size, bwi_dma_ring_addr, &st->stats_paddr, BUS_DMA_NOWAIT); if (error) { device_printf(sc->sc_dev, "can't load txstats DMA mem\n"); bus_dmamem_free(st->stats_dtag, st->stats, st->stats_dmap); bus_dma_tag_destroy(st->stats_dtag); st->stats_dtag = NULL; return error; } st->stats_ctrl_base = ctrl_base; return 0; } static void bwi_dma_txstats_free(struct bwi_softc *sc) { struct bwi_txstats_data *st; if (sc->sc_txstats == NULL) return; st = sc->sc_txstats; if (st->stats_ring_dtag != NULL) { bus_dmamap_unload(st->stats_ring_dtag, st->stats_ring_dmap); bus_dmamem_free(st->stats_ring_dtag, st->stats_ring, st->stats_ring_dmap); bus_dma_tag_destroy(st->stats_ring_dtag); } if (st->stats_dtag != NULL) { bus_dmamap_unload(st->stats_dtag, st->stats_dmap); bus_dmamem_free(st->stats_dtag, st->stats, st->stats_dmap); bus_dma_tag_destroy(st->stats_dtag); } free(st, M_DEVBUF); } static void bwi_dma_ring_addr(void *arg, bus_dma_segment_t *seg, int nseg, int error) { KASSERT(nseg == 1, ("too many segments\n")); *((bus_addr_t *)arg) = seg->ds_addr; } static int bwi_dma_mbuf_create(struct bwi_softc *sc) { struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; int i, j, k, ntx, error; /* * Create TX/RX mbuf DMA tag */ error = bus_dma_tag_create(sc->sc_parent_dtag, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, BUS_SPACE_MAXSIZE_32BIT, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_buf_dtag); if (error) { device_printf(sc->sc_dev, "can't create mbuf DMA tag\n"); return error; } ntx = 0; /* * Create TX mbuf DMA map */ for (i = 0; i < BWI_TX_NRING; ++i) { struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[i]; for (j = 0; j < BWI_TX_NDESC; ++j) { error = bus_dmamap_create(sc->sc_buf_dtag, 0, &tbd->tbd_buf[j].tb_dmap); if (error) { device_printf(sc->sc_dev, "can't create " "%dth tbd, %dth DMA map\n", i, j); ntx = i; for (k = 0; k < j; ++k) { bus_dmamap_destroy(sc->sc_buf_dtag, tbd->tbd_buf[k].tb_dmap); } goto fail; } } } ntx = BWI_TX_NRING; /* * Create RX mbuf DMA map and a spare DMA map */ error = bus_dmamap_create(sc->sc_buf_dtag, 0, &rbd->rbd_tmp_dmap); if (error) { device_printf(sc->sc_dev, "can't create spare RX buf DMA map\n"); goto fail; } for (j = 0; j < BWI_RX_NDESC; ++j) { error = bus_dmamap_create(sc->sc_buf_dtag, 0, &rbd->rbd_buf[j].rb_dmap); if (error) { device_printf(sc->sc_dev, "can't create %dth " "RX buf DMA map\n", j); for (k = 0; k < j; ++k) { bus_dmamap_destroy(sc->sc_buf_dtag, rbd->rbd_buf[j].rb_dmap); } bus_dmamap_destroy(sc->sc_buf_dtag, rbd->rbd_tmp_dmap); goto fail; } } return 0; fail: bwi_dma_mbuf_destroy(sc, ntx, 0); return error; } static void bwi_dma_mbuf_destroy(struct bwi_softc *sc, int ntx, int nrx) { int i, j; if (sc->sc_buf_dtag == NULL) return; for (i = 0; i < ntx; ++i) { struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[i]; for (j = 0; j < BWI_TX_NDESC; ++j) { struct bwi_txbuf *tb = &tbd->tbd_buf[j]; if (tb->tb_mbuf != NULL) { bus_dmamap_unload(sc->sc_buf_dtag, tb->tb_dmap); m_freem(tb->tb_mbuf); } if (tb->tb_ni != NULL) ieee80211_free_node(tb->tb_ni); bus_dmamap_destroy(sc->sc_buf_dtag, tb->tb_dmap); } } if (nrx) { struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; bus_dmamap_destroy(sc->sc_buf_dtag, rbd->rbd_tmp_dmap); for (j = 0; j < BWI_RX_NDESC; ++j) { struct bwi_rxbuf *rb = &rbd->rbd_buf[j]; if (rb->rb_mbuf != NULL) { bus_dmamap_unload(sc->sc_buf_dtag, rb->rb_dmap); m_freem(rb->rb_mbuf); } bus_dmamap_destroy(sc->sc_buf_dtag, rb->rb_dmap); } } bus_dma_tag_destroy(sc->sc_buf_dtag); sc->sc_buf_dtag = NULL; } static void bwi_enable_intrs(struct bwi_softc *sc, uint32_t enable_intrs) { CSR_SETBITS_4(sc, BWI_MAC_INTR_MASK, enable_intrs); } static void bwi_disable_intrs(struct bwi_softc *sc, uint32_t disable_intrs) { CSR_CLRBITS_4(sc, BWI_MAC_INTR_MASK, disable_intrs); } static int bwi_init_tx_ring32(struct bwi_softc *sc, int ring_idx) { struct bwi_ring_data *rd; struct bwi_txbuf_data *tbd; uint32_t val, addr_hi, addr_lo; KASSERT(ring_idx < BWI_TX_NRING, ("ring_idx %d", ring_idx)); rd = &sc->sc_tx_rdata[ring_idx]; tbd = &sc->sc_tx_bdata[ring_idx]; tbd->tbd_idx = 0; tbd->tbd_used = 0; bzero(rd->rdata_desc, sizeof(struct bwi_desc32) * BWI_TX_NDESC); bus_dmamap_sync(sc->sc_txring_dtag, rd->rdata_dmap, BUS_DMASYNC_PREWRITE); addr_lo = __SHIFTOUT(rd->rdata_paddr, BWI_TXRX32_RINGINFO_ADDR_MASK); addr_hi = __SHIFTOUT(rd->rdata_paddr, BWI_TXRX32_RINGINFO_FUNC_MASK); val = __SHIFTIN(addr_lo, BWI_TXRX32_RINGINFO_ADDR_MASK) | __SHIFTIN(BWI_TXRX32_RINGINFO_FUNC_TXRX, BWI_TXRX32_RINGINFO_FUNC_MASK); CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_RINGINFO, val); val = __SHIFTIN(addr_hi, BWI_TXRX32_CTRL_ADDRHI_MASK) | BWI_TXRX32_CTRL_ENABLE; CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_CTRL, val); return 0; } static void bwi_init_rxdesc_ring32(struct bwi_softc *sc, uint32_t ctrl_base, bus_addr_t paddr, int hdr_size, int ndesc) { uint32_t val, addr_hi, addr_lo; addr_lo = __SHIFTOUT(paddr, BWI_TXRX32_RINGINFO_ADDR_MASK); addr_hi = __SHIFTOUT(paddr, BWI_TXRX32_RINGINFO_FUNC_MASK); val = __SHIFTIN(addr_lo, BWI_TXRX32_RINGINFO_ADDR_MASK) | __SHIFTIN(BWI_TXRX32_RINGINFO_FUNC_TXRX, BWI_TXRX32_RINGINFO_FUNC_MASK); CSR_WRITE_4(sc, ctrl_base + BWI_RX32_RINGINFO, val); val = __SHIFTIN(hdr_size, BWI_RX32_CTRL_HDRSZ_MASK) | __SHIFTIN(addr_hi, BWI_TXRX32_CTRL_ADDRHI_MASK) | BWI_TXRX32_CTRL_ENABLE; CSR_WRITE_4(sc, ctrl_base + BWI_RX32_CTRL, val); CSR_WRITE_4(sc, ctrl_base + BWI_RX32_INDEX, (ndesc - 1) * sizeof(struct bwi_desc32)); } static int bwi_init_rx_ring32(struct bwi_softc *sc) { struct bwi_ring_data *rd = &sc->sc_rx_rdata; int i, error; sc->sc_rx_bdata.rbd_idx = 0; for (i = 0; i < BWI_RX_NDESC; ++i) { error = bwi_newbuf(sc, i, 1); if (error) { device_printf(sc->sc_dev, "can't allocate %dth RX buffer\n", i); return error; } } bus_dmamap_sync(sc->sc_rxring_dtag, rd->rdata_dmap, BUS_DMASYNC_PREWRITE); bwi_init_rxdesc_ring32(sc, rd->rdata_txrx_ctrl, rd->rdata_paddr, sizeof(struct bwi_rxbuf_hdr), BWI_RX_NDESC); return 0; } static int bwi_init_txstats32(struct bwi_softc *sc) { struct bwi_txstats_data *st = sc->sc_txstats; bus_addr_t stats_paddr; int i; bzero(st->stats, BWI_TXSTATS_NDESC * sizeof(struct bwi_txstats)); bus_dmamap_sync(st->stats_dtag, st->stats_dmap, BUS_DMASYNC_PREWRITE); st->stats_idx = 0; stats_paddr = st->stats_paddr; for (i = 0; i < BWI_TXSTATS_NDESC; ++i) { bwi_setup_desc32(sc, st->stats_ring, BWI_TXSTATS_NDESC, i, stats_paddr, sizeof(struct bwi_txstats), 0); stats_paddr += sizeof(struct bwi_txstats); } bus_dmamap_sync(st->stats_ring_dtag, st->stats_ring_dmap, BUS_DMASYNC_PREWRITE); bwi_init_rxdesc_ring32(sc, st->stats_ctrl_base, st->stats_ring_paddr, 0, BWI_TXSTATS_NDESC); return 0; } static void bwi_setup_rx_desc32(struct bwi_softc *sc, int buf_idx, bus_addr_t paddr, int buf_len) { struct bwi_ring_data *rd = &sc->sc_rx_rdata; KASSERT(buf_idx < BWI_RX_NDESC, ("buf_idx %d", buf_idx)); bwi_setup_desc32(sc, rd->rdata_desc, BWI_RX_NDESC, buf_idx, paddr, buf_len, 0); } static void bwi_setup_tx_desc32(struct bwi_softc *sc, struct bwi_ring_data *rd, int buf_idx, bus_addr_t paddr, int buf_len) { KASSERT(buf_idx < BWI_TX_NDESC, ("buf_idx %d", buf_idx)); bwi_setup_desc32(sc, rd->rdata_desc, BWI_TX_NDESC, buf_idx, paddr, buf_len, 1); } static int bwi_init_tx_ring64(struct bwi_softc *sc, int ring_idx) { /* TODO:64 */ return EOPNOTSUPP; } static int bwi_init_rx_ring64(struct bwi_softc *sc) { /* TODO:64 */ return EOPNOTSUPP; } static int bwi_init_txstats64(struct bwi_softc *sc) { /* TODO:64 */ return EOPNOTSUPP; } static void bwi_setup_rx_desc64(struct bwi_softc *sc, int buf_idx, bus_addr_t paddr, int buf_len) { /* TODO:64 */ } static void bwi_setup_tx_desc64(struct bwi_softc *sc, struct bwi_ring_data *rd, int buf_idx, bus_addr_t paddr, int buf_len) { /* TODO:64 */ } static void bwi_dma_buf_addr(void *arg, bus_dma_segment_t *seg, int nseg, bus_size_t mapsz __unused, int error) { if (!error) { KASSERT(nseg == 1, ("too many segments(%d)\n", nseg)); *((bus_addr_t *)arg) = seg->ds_addr; } } static int bwi_newbuf(struct bwi_softc *sc, int buf_idx, int init) { struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; struct bwi_rxbuf *rxbuf = &rbd->rbd_buf[buf_idx]; struct bwi_rxbuf_hdr *hdr; bus_dmamap_t map; bus_addr_t paddr; struct mbuf *m; int error; KASSERT(buf_idx < BWI_RX_NDESC, ("buf_idx %d", buf_idx)); m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { error = ENOBUFS; /* * If the NIC is up and running, we need to: * - Clear RX buffer's header. * - Restore RX descriptor settings. */ if (init) return error; else goto back; } m->m_len = m->m_pkthdr.len = MCLBYTES; /* * Try to load RX buf into temporary DMA map */ error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, rbd->rbd_tmp_dmap, m, bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); if (error) { m_freem(m); /* * See the comment above */ if (init) return error; else goto back; } if (!init) bus_dmamap_unload(sc->sc_buf_dtag, rxbuf->rb_dmap); rxbuf->rb_mbuf = m; rxbuf->rb_paddr = paddr; /* * Swap RX buf's DMA map with the loaded temporary one */ map = rxbuf->rb_dmap; rxbuf->rb_dmap = rbd->rbd_tmp_dmap; rbd->rbd_tmp_dmap = map; back: /* * Clear RX buf header */ hdr = mtod(rxbuf->rb_mbuf, struct bwi_rxbuf_hdr *); bzero(hdr, sizeof(*hdr)); bus_dmamap_sync(sc->sc_buf_dtag, rxbuf->rb_dmap, BUS_DMASYNC_PREWRITE); /* * Setup RX buf descriptor */ sc->sc_setup_rxdesc(sc, buf_idx, rxbuf->rb_paddr, rxbuf->rb_mbuf->m_len - sizeof(*hdr)); return error; } static void bwi_set_addr_filter(struct bwi_softc *sc, uint16_t addr_ofs, const uint8_t *addr) { int i; CSR_WRITE_2(sc, BWI_ADDR_FILTER_CTRL, BWI_ADDR_FILTER_CTRL_SET | addr_ofs); for (i = 0; i < (IEEE80211_ADDR_LEN / 2); ++i) { uint16_t addr_val; addr_val = (uint16_t)addr[i * 2] | (((uint16_t)addr[(i * 2) + 1]) << 8); CSR_WRITE_2(sc, BWI_ADDR_FILTER_DATA, addr_val); } } static int bwi_rxeof(struct bwi_softc *sc, int end_idx) { struct bwi_ring_data *rd = &sc->sc_rx_rdata; struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int idx, rx_data = 0; idx = rbd->rbd_idx; while (idx != end_idx) { struct bwi_rxbuf *rb = &rbd->rbd_buf[idx]; struct bwi_rxbuf_hdr *hdr; struct ieee80211_frame_min *wh; struct ieee80211_node *ni; struct mbuf *m; const void *plcp; uint16_t flags2; int buflen, wh_ofs, hdr_extra, rssi, noise, type, rate; m = rb->rb_mbuf; bus_dmamap_sync(sc->sc_buf_dtag, rb->rb_dmap, BUS_DMASYNC_POSTREAD); if (bwi_newbuf(sc, idx, 0)) { ifp->if_ierrors++; goto next; } hdr = mtod(m, struct bwi_rxbuf_hdr *); flags2 = le16toh(hdr->rxh_flags2); hdr_extra = 0; if (flags2 & BWI_RXH_F2_TYPE2FRAME) hdr_extra = 2; wh_ofs = hdr_extra + 6; /* XXX magic number */ buflen = le16toh(hdr->rxh_buflen); if (buflen < BWI_FRAME_MIN_LEN(wh_ofs)) { if_printf(ifp, "%s: zero length data, hdr_extra %d\n", __func__, hdr_extra); ifp->if_ierrors++; m_freem(m); goto next; } plcp = ((const uint8_t *)(hdr + 1) + hdr_extra); rssi = bwi_calc_rssi(sc, hdr); noise = bwi_calc_noise(sc); m->m_pkthdr.rcvif = ifp; m->m_len = m->m_pkthdr.len = buflen + sizeof(*hdr); m_adj(m, sizeof(*hdr) + wh_ofs); if (htole16(hdr->rxh_flags1) & BWI_RXH_F1_OFDM) rate = bwi_ofdm_plcp2rate(plcp); else rate = bwi_ds_plcp2rate(plcp); /* RX radio tap */ if (ieee80211_radiotap_active(ic)) bwi_rx_radiotap(sc, m, hdr, plcp, rate, rssi, noise); m_adj(m, -IEEE80211_CRC_LEN); BWI_UNLOCK(sc); wh = mtod(m, struct ieee80211_frame_min *); ni = ieee80211_find_rxnode(ic, wh); if (ni != NULL) { type = ieee80211_input(ni, m, rssi - noise, noise); ieee80211_free_node(ni); } else type = ieee80211_input_all(ic, m, rssi - noise, noise); if (type == IEEE80211_FC0_TYPE_DATA) { rx_data = 1; sc->sc_rx_rate = rate; } BWI_LOCK(sc); next: idx = (idx + 1) % BWI_RX_NDESC; if (sc->sc_flags & BWI_F_STOP) { /* * Take the fast lane, don't do * any damage to softc */ return -1; } } rbd->rbd_idx = idx; bus_dmamap_sync(sc->sc_rxring_dtag, rd->rdata_dmap, BUS_DMASYNC_PREWRITE); return rx_data; } static int bwi_rxeof32(struct bwi_softc *sc) { uint32_t val, rx_ctrl; int end_idx, rx_data; rx_ctrl = sc->sc_rx_rdata.rdata_txrx_ctrl; val = CSR_READ_4(sc, rx_ctrl + BWI_RX32_STATUS); end_idx = __SHIFTOUT(val, BWI_RX32_STATUS_INDEX_MASK) / sizeof(struct bwi_desc32); rx_data = bwi_rxeof(sc, end_idx); if (rx_data >= 0) { CSR_WRITE_4(sc, rx_ctrl + BWI_RX32_INDEX, end_idx * sizeof(struct bwi_desc32)); } return rx_data; } static int bwi_rxeof64(struct bwi_softc *sc) { /* TODO:64 */ return 0; } static void bwi_reset_rx_ring32(struct bwi_softc *sc, uint32_t rx_ctrl) { int i; CSR_WRITE_4(sc, rx_ctrl + BWI_RX32_CTRL, 0); #define NRETRY 10 for (i = 0; i < NRETRY; ++i) { uint32_t status; status = CSR_READ_4(sc, rx_ctrl + BWI_RX32_STATUS); if (__SHIFTOUT(status, BWI_RX32_STATUS_STATE_MASK) == BWI_RX32_STATUS_STATE_DISABLED) break; DELAY(1000); } if (i == NRETRY) device_printf(sc->sc_dev, "reset rx ring timedout\n"); #undef NRETRY CSR_WRITE_4(sc, rx_ctrl + BWI_RX32_RINGINFO, 0); } static void bwi_free_txstats32(struct bwi_softc *sc) { bwi_reset_rx_ring32(sc, sc->sc_txstats->stats_ctrl_base); } static void bwi_free_rx_ring32(struct bwi_softc *sc) { struct bwi_ring_data *rd = &sc->sc_rx_rdata; struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; int i; bwi_reset_rx_ring32(sc, rd->rdata_txrx_ctrl); for (i = 0; i < BWI_RX_NDESC; ++i) { struct bwi_rxbuf *rb = &rbd->rbd_buf[i]; if (rb->rb_mbuf != NULL) { bus_dmamap_unload(sc->sc_buf_dtag, rb->rb_dmap); m_freem(rb->rb_mbuf); rb->rb_mbuf = NULL; } } } static void bwi_free_tx_ring32(struct bwi_softc *sc, int ring_idx) { struct bwi_ring_data *rd; struct bwi_txbuf_data *tbd; struct ifnet *ifp = sc->sc_ifp; uint32_t state, val; int i; KASSERT(ring_idx < BWI_TX_NRING, ("ring_idx %d", ring_idx)); rd = &sc->sc_tx_rdata[ring_idx]; tbd = &sc->sc_tx_bdata[ring_idx]; #define NRETRY 10 for (i = 0; i < NRETRY; ++i) { val = CSR_READ_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_STATUS); state = __SHIFTOUT(val, BWI_TX32_STATUS_STATE_MASK); if (state == BWI_TX32_STATUS_STATE_DISABLED || state == BWI_TX32_STATUS_STATE_IDLE || state == BWI_TX32_STATUS_STATE_STOPPED) break; DELAY(1000); } if (i == NRETRY) { if_printf(ifp, "%s: wait for TX ring(%d) stable timed out\n", __func__, ring_idx); } CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_CTRL, 0); for (i = 0; i < NRETRY; ++i) { val = CSR_READ_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_STATUS); state = __SHIFTOUT(val, BWI_TX32_STATUS_STATE_MASK); if (state == BWI_TX32_STATUS_STATE_DISABLED) break; DELAY(1000); } if (i == NRETRY) if_printf(ifp, "%s: reset TX ring (%d) timed out\n", __func__, ring_idx); #undef NRETRY DELAY(1000); CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_RINGINFO, 0); for (i = 0; i < BWI_TX_NDESC; ++i) { struct bwi_txbuf *tb = &tbd->tbd_buf[i]; if (tb->tb_mbuf != NULL) { bus_dmamap_unload(sc->sc_buf_dtag, tb->tb_dmap); m_freem(tb->tb_mbuf); tb->tb_mbuf = NULL; } if (tb->tb_ni != NULL) { ieee80211_free_node(tb->tb_ni); tb->tb_ni = NULL; } } } static void bwi_free_txstats64(struct bwi_softc *sc) { /* TODO:64 */ } static void bwi_free_rx_ring64(struct bwi_softc *sc) { /* TODO:64 */ } static void bwi_free_tx_ring64(struct bwi_softc *sc, int ring_idx) { /* TODO:64 */ } /* XXX does not belong here */ #define IEEE80211_OFDM_PLCP_RATE_MASK __BITS(3, 0) #define IEEE80211_OFDM_PLCP_LEN_MASK __BITS(16, 5) static __inline void bwi_ofdm_plcp_header(uint32_t *plcp0, int pkt_len, uint8_t rate) { uint32_t plcp; plcp = __SHIFTIN(ieee80211_rate2plcp(rate, IEEE80211_T_OFDM), IEEE80211_OFDM_PLCP_RATE_MASK) | __SHIFTIN(pkt_len, IEEE80211_OFDM_PLCP_LEN_MASK); *plcp0 = htole32(plcp); } static __inline void bwi_ds_plcp_header(struct ieee80211_ds_plcp_hdr *plcp, int pkt_len, uint8_t rate) { int len, service, pkt_bitlen; pkt_bitlen = pkt_len * NBBY; len = howmany(pkt_bitlen * 2, rate); service = IEEE80211_PLCP_SERVICE_LOCKED; if (rate == (11 * 2)) { int pkt_bitlen1; /* * PLCP service field needs to be adjusted, * if TX rate is 11Mbytes/s */ pkt_bitlen1 = len * 11; if (pkt_bitlen1 - pkt_bitlen >= NBBY) service |= IEEE80211_PLCP_SERVICE_LENEXT7; } plcp->i_signal = ieee80211_rate2plcp(rate, IEEE80211_T_CCK); plcp->i_service = service; plcp->i_length = htole16(len); /* NOTE: do NOT touch i_crc */ } static __inline void bwi_plcp_header(const struct ieee80211_rate_table *rt, void *plcp, int pkt_len, uint8_t rate) { enum ieee80211_phytype modtype; /* * Assume caller has zeroed 'plcp' */ modtype = ieee80211_rate2phytype(rt, rate); if (modtype == IEEE80211_T_OFDM) bwi_ofdm_plcp_header(plcp, pkt_len, rate); else if (modtype == IEEE80211_T_DS) bwi_ds_plcp_header(plcp, pkt_len, rate); else panic("unsupport modulation type %u\n", modtype); } static int bwi_encap(struct bwi_softc *sc, int idx, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct bwi_ring_data *rd = &sc->sc_tx_rdata[BWI_TX_DATA_RING]; struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; struct bwi_txbuf *tb = &tbd->tbd_buf[idx]; struct bwi_mac *mac; struct bwi_txbuf_hdr *hdr; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp; uint8_t rate, rate_fb; uint32_t mac_ctrl; uint16_t phy_ctrl; bus_addr_t paddr; int type, ismcast, pkt_len, error, rix; #if 0 const uint8_t *p; int i; #endif KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); /* Get 802.11 frame len before prepending TX header */ pkt_len = m->m_pkthdr.len + IEEE80211_CRC_LEN; /* * Find TX rate */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (type != IEEE80211_FC0_TYPE_DATA || (m->m_flags & M_EAPOL)) { rate = rate_fb = tp->mgmtrate; } else if (ismcast) { rate = rate_fb = tp->mcastrate; } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { rate = rate_fb = tp->ucastrate; } else { rix = ieee80211_amrr_choose(ni, &BWI_NODE(ni)->amn); rate = ni->ni_txrate; if (rix > 0) { rate_fb = ni->ni_rates.rs_rates[rix-1] & IEEE80211_RATE_VAL; } else { rate_fb = rate; } } tb->tb_rate[0] = rate; tb->tb_rate[1] = rate_fb; sc->sc_tx_rate = rate; /* * TX radio tap */ if (ieee80211_radiotap_active_vap(vap)) { sc->sc_tx_th.wt_flags = 0; if (wh->i_fc[1] & IEEE80211_FC1_WEP) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_DS && (ic->ic_flags & IEEE80211_F_SHPREAMBLE) && rate != (1 * 2)) { sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; } sc->sc_tx_th.wt_rate = rate; ieee80211_radiotap_tx(vap, m); } /* * Setup the embedded TX header */ M_PREPEND(m, sizeof(*hdr), M_DONTWAIT); if (m == NULL) { if_printf(ifp, "%s: prepend TX header failed\n", __func__); return ENOBUFS; } hdr = mtod(m, struct bwi_txbuf_hdr *); bzero(hdr, sizeof(*hdr)); bcopy(wh->i_fc, hdr->txh_fc, sizeof(hdr->txh_fc)); bcopy(wh->i_addr1, hdr->txh_addr1, sizeof(hdr->txh_addr1)); if (!ismcast) { uint16_t dur; dur = ieee80211_ack_duration(sc->sc_rates, rate, ic->ic_flags & ~IEEE80211_F_SHPREAMBLE); hdr->txh_fb_duration = htole16(dur); } hdr->txh_id = __SHIFTIN(BWI_TX_DATA_RING, BWI_TXH_ID_RING_MASK) | __SHIFTIN(idx, BWI_TXH_ID_IDX_MASK); bwi_plcp_header(sc->sc_rates, hdr->txh_plcp, pkt_len, rate); bwi_plcp_header(sc->sc_rates, hdr->txh_fb_plcp, pkt_len, rate_fb); phy_ctrl = __SHIFTIN(mac->mac_rf.rf_ant_mode, BWI_TXH_PHY_C_ANTMODE_MASK); if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) phy_ctrl |= BWI_TXH_PHY_C_OFDM; else if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && rate != (2 * 1)) phy_ctrl |= BWI_TXH_PHY_C_SHPREAMBLE; mac_ctrl = BWI_TXH_MAC_C_HWSEQ | BWI_TXH_MAC_C_FIRST_FRAG; if (!ismcast) mac_ctrl |= BWI_TXH_MAC_C_ACK; if (ieee80211_rate2phytype(sc->sc_rates, rate_fb) == IEEE80211_T_OFDM) mac_ctrl |= BWI_TXH_MAC_C_FB_OFDM; hdr->txh_mac_ctrl = htole32(mac_ctrl); hdr->txh_phy_ctrl = htole16(phy_ctrl); /* Catch any further usage */ hdr = NULL; wh = NULL; /* DMA load */ error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); if (error && error != EFBIG) { if_printf(ifp, "%s: can't load TX buffer (1) %d\n", __func__, error); goto back; } if (error) { /* error == EFBIG */ struct mbuf *m_new; m_new = m_defrag(m, M_DONTWAIT); if (m_new == NULL) { if_printf(ifp, "%s: can't defrag TX buffer\n", __func__); error = ENOBUFS; goto back; } else { m = m_new; } error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); if (error) { if_printf(ifp, "%s: can't load TX buffer (2) %d\n", __func__, error); goto back; } } error = 0; bus_dmamap_sync(sc->sc_buf_dtag, tb->tb_dmap, BUS_DMASYNC_PREWRITE); tb->tb_mbuf = m; tb->tb_ni = ni; #if 0 p = mtod(m, const uint8_t *); for (i = 0; i < m->m_pkthdr.len; ++i) { if (i != 0 && i % 8 == 0) printf("\n"); printf("%02x ", p[i]); } printf("\n"); #endif DPRINTF(sc, BWI_DBG_TX, "idx %d, pkt_len %d, buflen %d\n", idx, pkt_len, m->m_pkthdr.len); /* Setup TX descriptor */ sc->sc_setup_txdesc(sc, rd, idx, paddr, m->m_pkthdr.len); bus_dmamap_sync(sc->sc_txring_dtag, rd->rdata_dmap, BUS_DMASYNC_PREWRITE); /* Kick start */ sc->sc_start_tx(sc, rd->rdata_txrx_ctrl, idx); back: if (error) m_freem(m); return error; } static int bwi_encap_raw(struct bwi_softc *sc, int idx, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; struct bwi_ring_data *rd = &sc->sc_tx_rdata[BWI_TX_DATA_RING]; struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; struct bwi_txbuf *tb = &tbd->tbd_buf[idx]; struct bwi_mac *mac; struct bwi_txbuf_hdr *hdr; struct ieee80211_frame *wh; uint8_t rate, rate_fb; uint32_t mac_ctrl; uint16_t phy_ctrl; bus_addr_t paddr; int ismcast, pkt_len, error; KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; wh = mtod(m, struct ieee80211_frame *); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); /* Get 802.11 frame len before prepending TX header */ pkt_len = m->m_pkthdr.len + IEEE80211_CRC_LEN; /* * Find TX rate */ rate = params->ibp_rate0; - rate_fb = (params->ibp_try1 != 0) ? - params->ibp_rate1 : params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) { + /* XXX fall back to mcast/mgmt rate? */ + m_freem(m0); + return EINVAL; + } + if (params->ibp_try1 != 0) { + rate_fb = params->ibp_rate1; + if (!ieee80211_isratevalid(ic->ic_rt, rate_fb)) { + /* XXX fall back to rate0? */ + m_freem(m0); + return EINVAL; + } + } else + rate_fb = rate; tb->tb_rate[0] = rate; tb->tb_rate[1] = rate_fb; sc->sc_tx_rate = rate; /* * TX radio tap */ if (ieee80211_radiotap_active_vap(vap)) { sc->sc_tx_th.wt_flags = 0; /* XXX IEEE80211_BPF_CRYPTO */ if (wh->i_fc[1] & IEEE80211_FC1_WEP) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; sc->sc_tx_th.wt_rate = rate; ieee80211_radiotap_tx(vap, m); } /* * Setup the embedded TX header */ M_PREPEND(m, sizeof(*hdr), M_DONTWAIT); if (m == NULL) { if_printf(ifp, "%s: prepend TX header failed\n", __func__); return ENOBUFS; } hdr = mtod(m, struct bwi_txbuf_hdr *); bzero(hdr, sizeof(*hdr)); bcopy(wh->i_fc, hdr->txh_fc, sizeof(hdr->txh_fc)); bcopy(wh->i_addr1, hdr->txh_addr1, sizeof(hdr->txh_addr1)); mac_ctrl = BWI_TXH_MAC_C_HWSEQ | BWI_TXH_MAC_C_FIRST_FRAG; if (!ismcast && (params->ibp_flags & IEEE80211_BPF_NOACK) == 0) { uint16_t dur; dur = ieee80211_ack_duration(sc->sc_rates, rate_fb, 0); hdr->txh_fb_duration = htole16(dur); mac_ctrl |= BWI_TXH_MAC_C_ACK; } hdr->txh_id = __SHIFTIN(BWI_TX_DATA_RING, BWI_TXH_ID_RING_MASK) | __SHIFTIN(idx, BWI_TXH_ID_IDX_MASK); bwi_plcp_header(sc->sc_rates, hdr->txh_plcp, pkt_len, rate); bwi_plcp_header(sc->sc_rates, hdr->txh_fb_plcp, pkt_len, rate_fb); phy_ctrl = __SHIFTIN(mac->mac_rf.rf_ant_mode, BWI_TXH_PHY_C_ANTMODE_MASK); if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) { phy_ctrl |= BWI_TXH_PHY_C_OFDM; mac_ctrl |= BWI_TXH_MAC_C_FB_OFDM; } else if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) phy_ctrl |= BWI_TXH_PHY_C_SHPREAMBLE; hdr->txh_mac_ctrl = htole32(mac_ctrl); hdr->txh_phy_ctrl = htole16(phy_ctrl); /* Catch any further usage */ hdr = NULL; wh = NULL; /* DMA load */ error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0) { struct mbuf *m_new; if (error != EFBIG) { if_printf(ifp, "%s: can't load TX buffer (1) %d\n", __func__, error); goto back; } m_new = m_defrag(m, M_DONTWAIT); if (m_new == NULL) { if_printf(ifp, "%s: can't defrag TX buffer\n", __func__); error = ENOBUFS; goto back; } m = m_new; error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); if (error) { if_printf(ifp, "%s: can't load TX buffer (2) %d\n", __func__, error); goto back; } } bus_dmamap_sync(sc->sc_buf_dtag, tb->tb_dmap, BUS_DMASYNC_PREWRITE); tb->tb_mbuf = m; tb->tb_ni = ni; DPRINTF(sc, BWI_DBG_TX, "idx %d, pkt_len %d, buflen %d\n", idx, pkt_len, m->m_pkthdr.len); /* Setup TX descriptor */ sc->sc_setup_txdesc(sc, rd, idx, paddr, m->m_pkthdr.len); bus_dmamap_sync(sc->sc_txring_dtag, rd->rdata_dmap, BUS_DMASYNC_PREWRITE); /* Kick start */ sc->sc_start_tx(sc, rd->rdata_txrx_ctrl, idx); back: if (error) m_freem(m); return error; } static void bwi_start_tx32(struct bwi_softc *sc, uint32_t tx_ctrl, int idx) { idx = (idx + 1) % BWI_TX_NDESC; CSR_WRITE_4(sc, tx_ctrl + BWI_TX32_INDEX, idx * sizeof(struct bwi_desc32)); } static void bwi_start_tx64(struct bwi_softc *sc, uint32_t tx_ctrl, int idx) { /* TODO:64 */ } static void bwi_txeof_status32(struct bwi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t val, ctrl_base; int end_idx; ctrl_base = sc->sc_txstats->stats_ctrl_base; val = CSR_READ_4(sc, ctrl_base + BWI_RX32_STATUS); end_idx = __SHIFTOUT(val, BWI_RX32_STATUS_INDEX_MASK) / sizeof(struct bwi_desc32); bwi_txeof_status(sc, end_idx); CSR_WRITE_4(sc, ctrl_base + BWI_RX32_INDEX, end_idx * sizeof(struct bwi_desc32)); if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) ifp->if_start(ifp); } static void bwi_txeof_status64(struct bwi_softc *sc) { /* TODO:64 */ } static void _bwi_txeof(struct bwi_softc *sc, uint16_t tx_id, int acked, int data_txcnt) { struct ifnet *ifp = sc->sc_ifp; struct bwi_txbuf_data *tbd; struct bwi_txbuf *tb; int ring_idx, buf_idx; struct ieee80211_node *ni; if (tx_id == 0) { if_printf(ifp, "%s: zero tx id\n", __func__); return; } ring_idx = __SHIFTOUT(tx_id, BWI_TXH_ID_RING_MASK); buf_idx = __SHIFTOUT(tx_id, BWI_TXH_ID_IDX_MASK); KASSERT(ring_idx == BWI_TX_DATA_RING, ("ring_idx %d", ring_idx)); KASSERT(buf_idx < BWI_TX_NDESC, ("buf_idx %d", buf_idx)); tbd = &sc->sc_tx_bdata[ring_idx]; KASSERT(tbd->tbd_used > 0, ("tbd_used %d", tbd->tbd_used)); tbd->tbd_used--; tb = &tbd->tbd_buf[buf_idx]; DPRINTF(sc, BWI_DBG_TXEOF, "txeof idx %d, " "acked %d, data_txcnt %d, ni %p\n", buf_idx, acked, data_txcnt, tb->tb_ni); bus_dmamap_unload(sc->sc_buf_dtag, tb->tb_dmap); ni = tb->tb_ni; if (tb->tb_ni != NULL) { struct bwi_node *bn = (struct bwi_node *) tb->tb_ni; const struct bwi_txbuf_hdr *hdr = mtod(tb->tb_mbuf, const struct bwi_txbuf_hdr *); /* NB: update rate control only for unicast frames */ if (hdr->txh_mac_ctrl & htole32(BWI_TXH_MAC_C_ACK)) { /* * Feed back 'acked and data_txcnt'. Note that the * generic AMRR code only understands one tx rate * and the estimator doesn't handle real retry counts * well so to avoid over-aggressive downshifting we * treat any number of retries as "1". */ ieee80211_amrr_tx_complete(&bn->amn, acked, data_txcnt > 1); } /* * Do any tx complete callback. Note this must * be done before releasing the node reference. */ if (tb->tb_mbuf->m_flags & M_TXCB) ieee80211_process_callback(ni, tb->tb_mbuf, !acked); ieee80211_free_node(tb->tb_ni); tb->tb_ni = NULL; } m_freem(tb->tb_mbuf); tb->tb_mbuf = NULL; if (tbd->tbd_used == 0) ifp->if_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static void bwi_txeof_status(struct bwi_softc *sc, int end_idx) { struct bwi_txstats_data *st = sc->sc_txstats; int idx; bus_dmamap_sync(st->stats_dtag, st->stats_dmap, BUS_DMASYNC_POSTREAD); idx = st->stats_idx; while (idx != end_idx) { const struct bwi_txstats *stats = &st->stats[idx]; if ((stats->txs_flags & BWI_TXS_F_PENDING) == 0) { int data_txcnt; data_txcnt = __SHIFTOUT(stats->txs_txcnt, BWI_TXS_TXCNT_DATA); _bwi_txeof(sc, le16toh(stats->txs_id), stats->txs_flags & BWI_TXS_F_ACKED, data_txcnt); } idx = (idx + 1) % BWI_TXSTATS_NDESC; } st->stats_idx = idx; } static void bwi_txeof(struct bwi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; for (;;) { uint32_t tx_status0, tx_status1; uint16_t tx_id; int data_txcnt; tx_status0 = CSR_READ_4(sc, BWI_TXSTATUS0); if ((tx_status0 & BWI_TXSTATUS0_VALID) == 0) break; tx_status1 = CSR_READ_4(sc, BWI_TXSTATUS1); tx_id = __SHIFTOUT(tx_status0, BWI_TXSTATUS0_TXID_MASK); data_txcnt = __SHIFTOUT(tx_status0, BWI_TXSTATUS0_DATA_TXCNT_MASK); if (tx_status0 & (BWI_TXSTATUS0_AMPDU | BWI_TXSTATUS0_PENDING)) continue; _bwi_txeof(sc, le16toh(tx_id), tx_status0 & BWI_TXSTATUS0_ACKED, data_txcnt); } if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) ifp->if_start(ifp); } static int bwi_bbp_power_on(struct bwi_softc *sc, enum bwi_clock_mode clk_mode) { bwi_power_on(sc, 1); return bwi_set_clock_mode(sc, clk_mode); } static void bwi_bbp_power_off(struct bwi_softc *sc) { bwi_set_clock_mode(sc, BWI_CLOCK_MODE_SLOW); bwi_power_off(sc, 1); } static int bwi_get_pwron_delay(struct bwi_softc *sc) { struct bwi_regwin *com, *old; struct bwi_clock_freq freq; uint32_t val; int error; com = &sc->sc_com_regwin; KASSERT(BWI_REGWIN_EXIST(com), ("no regwin")); if ((sc->sc_cap & BWI_CAP_CLKMODE) == 0) return 0; error = bwi_regwin_switch(sc, com, &old); if (error) return error; bwi_get_clock_freq(sc, &freq); val = CSR_READ_4(sc, BWI_PLL_ON_DELAY); sc->sc_pwron_delay = howmany((val + 2) * 1000000, freq.clkfreq_min); DPRINTF(sc, BWI_DBG_ATTACH, "power on delay %u\n", sc->sc_pwron_delay); return bwi_regwin_switch(sc, old, NULL); } static int bwi_bus_attach(struct bwi_softc *sc) { struct bwi_regwin *bus, *old; int error; bus = &sc->sc_bus_regwin; error = bwi_regwin_switch(sc, bus, &old); if (error) return error; if (!bwi_regwin_is_enabled(sc, bus)) bwi_regwin_enable(sc, bus, 0); /* Disable interripts */ CSR_WRITE_4(sc, BWI_INTRVEC, 0); return bwi_regwin_switch(sc, old, NULL); } static const char * bwi_regwin_name(const struct bwi_regwin *rw) { switch (rw->rw_type) { case BWI_REGWIN_T_COM: return "COM"; case BWI_REGWIN_T_BUSPCI: return "PCI"; case BWI_REGWIN_T_MAC: return "MAC"; case BWI_REGWIN_T_BUSPCIE: return "PCIE"; } panic("unknown regwin type 0x%04x\n", rw->rw_type); return NULL; } static uint32_t bwi_regwin_disable_bits(struct bwi_softc *sc) { uint32_t busrev; /* XXX cache this */ busrev = __SHIFTOUT(CSR_READ_4(sc, BWI_ID_LO), BWI_ID_LO_BUSREV_MASK); DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT | BWI_DBG_MISC, "bus rev %u\n", busrev); if (busrev == BWI_BUSREV_0) return BWI_STATE_LO_DISABLE1; else if (busrev == BWI_BUSREV_1) return BWI_STATE_LO_DISABLE2; else return (BWI_STATE_LO_DISABLE1 | BWI_STATE_LO_DISABLE2); } int bwi_regwin_is_enabled(struct bwi_softc *sc, struct bwi_regwin *rw) { uint32_t val, disable_bits; disable_bits = bwi_regwin_disable_bits(sc); val = CSR_READ_4(sc, BWI_STATE_LO); if ((val & (BWI_STATE_LO_CLOCK | BWI_STATE_LO_RESET | disable_bits)) == BWI_STATE_LO_CLOCK) { DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT, "%s is enabled\n", bwi_regwin_name(rw)); return 1; } else { DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT, "%s is disabled\n", bwi_regwin_name(rw)); return 0; } } void bwi_regwin_disable(struct bwi_softc *sc, struct bwi_regwin *rw, uint32_t flags) { uint32_t state_lo, disable_bits; int i; state_lo = CSR_READ_4(sc, BWI_STATE_LO); /* * If current regwin is in 'reset' state, it was already disabled. */ if (state_lo & BWI_STATE_LO_RESET) { DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT, "%s was already disabled\n", bwi_regwin_name(rw)); return; } disable_bits = bwi_regwin_disable_bits(sc); /* * Disable normal clock */ state_lo = BWI_STATE_LO_CLOCK | disable_bits; CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); /* * Wait until normal clock is disabled */ #define NRETRY 1000 for (i = 0; i < NRETRY; ++i) { state_lo = CSR_READ_4(sc, BWI_STATE_LO); if (state_lo & disable_bits) break; DELAY(10); } if (i == NRETRY) { device_printf(sc->sc_dev, "%s disable clock timeout\n", bwi_regwin_name(rw)); } for (i = 0; i < NRETRY; ++i) { uint32_t state_hi; state_hi = CSR_READ_4(sc, BWI_STATE_HI); if ((state_hi & BWI_STATE_HI_BUSY) == 0) break; DELAY(10); } if (i == NRETRY) { device_printf(sc->sc_dev, "%s wait BUSY unset timeout\n", bwi_regwin_name(rw)); } #undef NRETRY /* * Reset and disable regwin with gated clock */ state_lo = BWI_STATE_LO_RESET | disable_bits | BWI_STATE_LO_CLOCK | BWI_STATE_LO_GATED_CLOCK | __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); /* Flush pending bus write */ CSR_READ_4(sc, BWI_STATE_LO); DELAY(1); /* Reset and disable regwin */ state_lo = BWI_STATE_LO_RESET | disable_bits | __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); /* Flush pending bus write */ CSR_READ_4(sc, BWI_STATE_LO); DELAY(1); } void bwi_regwin_enable(struct bwi_softc *sc, struct bwi_regwin *rw, uint32_t flags) { uint32_t state_lo, state_hi, imstate; bwi_regwin_disable(sc, rw, flags); /* Reset regwin with gated clock */ state_lo = BWI_STATE_LO_RESET | BWI_STATE_LO_CLOCK | BWI_STATE_LO_GATED_CLOCK | __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); /* Flush pending bus write */ CSR_READ_4(sc, BWI_STATE_LO); DELAY(1); state_hi = CSR_READ_4(sc, BWI_STATE_HI); if (state_hi & BWI_STATE_HI_SERROR) CSR_WRITE_4(sc, BWI_STATE_HI, 0); imstate = CSR_READ_4(sc, BWI_IMSTATE); if (imstate & (BWI_IMSTATE_INBAND_ERR | BWI_IMSTATE_TIMEOUT)) { imstate &= ~(BWI_IMSTATE_INBAND_ERR | BWI_IMSTATE_TIMEOUT); CSR_WRITE_4(sc, BWI_IMSTATE, imstate); } /* Enable regwin with gated clock */ state_lo = BWI_STATE_LO_CLOCK | BWI_STATE_LO_GATED_CLOCK | __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); /* Flush pending bus write */ CSR_READ_4(sc, BWI_STATE_LO); DELAY(1); /* Enable regwin with normal clock */ state_lo = BWI_STATE_LO_CLOCK | __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); /* Flush pending bus write */ CSR_READ_4(sc, BWI_STATE_LO); DELAY(1); } static void bwi_set_bssid(struct bwi_softc *sc, const uint8_t *bssid) { struct ifnet *ifp = sc->sc_ifp; struct bwi_mac *mac; struct bwi_myaddr_bssid buf; const uint8_t *p; uint32_t val; int n, i; KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; bwi_set_addr_filter(sc, BWI_ADDR_FILTER_BSSID, bssid); bcopy(IF_LLADDR(ifp), buf.myaddr, sizeof(buf.myaddr)); bcopy(bssid, buf.bssid, sizeof(buf.bssid)); n = sizeof(buf) / sizeof(val); p = (const uint8_t *)&buf; for (i = 0; i < n; ++i) { int j; val = 0; for (j = 0; j < sizeof(val); ++j) val |= ((uint32_t)(*p++)) << (j * 8); TMPLT_WRITE_4(mac, 0x20 + (i * sizeof(val)), val); } } static void bwi_updateslot(struct ifnet *ifp) { struct bwi_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct bwi_mac *mac; BWI_LOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { DPRINTF(sc, BWI_DBG_80211, "%s\n", __func__); KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; bwi_mac_updateslot(mac, (ic->ic_flags & IEEE80211_F_SHSLOT)); } BWI_UNLOCK(sc); } static void bwi_calibrate(void *xsc) { struct bwi_softc *sc = xsc; #ifdef INVARIANTS struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; #endif struct bwi_mac *mac; BWI_ASSERT_LOCKED(sc); KASSERT(ic->ic_opmode != IEEE80211_M_MONITOR, ("opmode %d", ic->ic_opmode)); KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; bwi_mac_calibrate_txpower(mac, sc->sc_txpwrcb_type); sc->sc_txpwrcb_type = BWI_TXPWR_CALIB; /* XXX 15 seconds */ callout_reset(&sc->sc_calib_ch, hz * 15, bwi_calibrate, sc); } static int bwi_calc_rssi(struct bwi_softc *sc, const struct bwi_rxbuf_hdr *hdr) { struct bwi_mac *mac; KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; return bwi_rf_calc_rssi(mac, hdr); } static int bwi_calc_noise(struct bwi_softc *sc) { struct bwi_mac *mac; KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, ("current regwin type %d", sc->sc_cur_regwin->rw_type)); mac = (struct bwi_mac *)sc->sc_cur_regwin; return bwi_rf_calc_noise(mac); } static __inline uint8_t bwi_ofdm_plcp2rate(const uint32_t *plcp0) { uint32_t plcp; uint8_t plcp_rate; plcp = le32toh(*plcp0); plcp_rate = __SHIFTOUT(plcp, IEEE80211_OFDM_PLCP_RATE_MASK); return ieee80211_plcp2rate(plcp_rate, IEEE80211_T_OFDM); } static __inline uint8_t bwi_ds_plcp2rate(const struct ieee80211_ds_plcp_hdr *hdr) { return ieee80211_plcp2rate(hdr->i_signal, IEEE80211_T_DS); } static void bwi_rx_radiotap(struct bwi_softc *sc, struct mbuf *m, struct bwi_rxbuf_hdr *hdr, const void *plcp, int rate, int rssi, int noise) { const struct ieee80211_frame_min *wh; sc->sc_rx_th.wr_flags = IEEE80211_RADIOTAP_F_FCS; if (htole16(hdr->rxh_flags1) & BWI_RXH_F1_SHPREAMBLE) sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; wh = mtod(m, const struct ieee80211_frame_min *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_WEP; sc->sc_rx_th.wr_tsf = hdr->rxh_tsf; /* No endian convertion */ sc->sc_rx_th.wr_rate = rate; sc->sc_rx_th.wr_antsignal = rssi; sc->sc_rx_th.wr_antnoise = noise; } static void bwi_led_attach(struct bwi_softc *sc) { const uint8_t *led_act = NULL; uint16_t gpio, val[BWI_LED_MAX]; int i; #define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) for (i = 0; i < N(bwi_vendor_led_act); ++i) { if (sc->sc_pci_subvid == bwi_vendor_led_act[i].vid) { led_act = bwi_vendor_led_act[i].led_act; break; } } if (led_act == NULL) led_act = bwi_default_led_act; #undef N gpio = bwi_read_sprom(sc, BWI_SPROM_GPIO01); val[0] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_0); val[1] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_1); gpio = bwi_read_sprom(sc, BWI_SPROM_GPIO23); val[2] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_2); val[3] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_3); for (i = 0; i < BWI_LED_MAX; ++i) { struct bwi_led *led = &sc->sc_leds[i]; if (val[i] == 0xff) { led->l_act = led_act[i]; } else { if (val[i] & BWI_LED_ACT_LOW) led->l_flags |= BWI_LED_F_ACTLOW; led->l_act = __SHIFTOUT(val[i], BWI_LED_ACT_MASK); } led->l_mask = (1 << i); if (led->l_act == BWI_LED_ACT_BLINK_SLOW || led->l_act == BWI_LED_ACT_BLINK_POLL || led->l_act == BWI_LED_ACT_BLINK) { led->l_flags |= BWI_LED_F_BLINK; if (led->l_act == BWI_LED_ACT_BLINK_POLL) led->l_flags |= BWI_LED_F_POLLABLE; else if (led->l_act == BWI_LED_ACT_BLINK_SLOW) led->l_flags |= BWI_LED_F_SLOW; if (sc->sc_blink_led == NULL) { sc->sc_blink_led = led; if (led->l_flags & BWI_LED_F_SLOW) BWI_LED_SLOWDOWN(sc->sc_led_idle); } } DPRINTF(sc, BWI_DBG_LED | BWI_DBG_ATTACH, "%dth led, act %d, lowact %d\n", i, led->l_act, led->l_flags & BWI_LED_F_ACTLOW); } callout_init(&sc->sc_led_blink_ch, CALLOUT_MPSAFE); } static __inline uint16_t bwi_led_onoff(const struct bwi_led *led, uint16_t val, int on) { if (led->l_flags & BWI_LED_F_ACTLOW) on = !on; if (on) val |= led->l_mask; else val &= ~led->l_mask; return val; } static void bwi_led_newstate(struct bwi_softc *sc, enum ieee80211_state nstate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t val; int i; if (nstate == IEEE80211_S_INIT) { callout_stop(&sc->sc_led_blink_ch); sc->sc_led_blinking = 0; } if ((ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; val = CSR_READ_2(sc, BWI_MAC_GPIO_CTRL); for (i = 0; i < BWI_LED_MAX; ++i) { struct bwi_led *led = &sc->sc_leds[i]; int on; if (led->l_act == BWI_LED_ACT_UNKN || led->l_act == BWI_LED_ACT_NULL) continue; if ((led->l_flags & BWI_LED_F_BLINK) && nstate != IEEE80211_S_INIT) continue; switch (led->l_act) { case BWI_LED_ACT_ON: /* Always on */ on = 1; break; case BWI_LED_ACT_OFF: /* Always off */ case BWI_LED_ACT_5GHZ: /* TODO: 11A */ on = 0; break; default: on = 1; switch (nstate) { case IEEE80211_S_INIT: on = 0; break; case IEEE80211_S_RUN: if (led->l_act == BWI_LED_ACT_11G && ic->ic_curmode != IEEE80211_MODE_11G) on = 0; break; default: if (led->l_act == BWI_LED_ACT_ASSOC) on = 0; break; } break; } val = bwi_led_onoff(led, val, on); } CSR_WRITE_2(sc, BWI_MAC_GPIO_CTRL, val); } static void bwi_led_event(struct bwi_softc *sc, int event) { struct bwi_led *led = sc->sc_blink_led; int rate; if (event == BWI_LED_EVENT_POLL) { if ((led->l_flags & BWI_LED_F_POLLABLE) == 0) return; if (ticks - sc->sc_led_ticks < sc->sc_led_idle) return; } sc->sc_led_ticks = ticks; if (sc->sc_led_blinking) return; switch (event) { case BWI_LED_EVENT_RX: rate = sc->sc_rx_rate; break; case BWI_LED_EVENT_TX: rate = sc->sc_tx_rate; break; case BWI_LED_EVENT_POLL: rate = 0; break; default: panic("unknown LED event %d\n", event); break; } bwi_led_blink_start(sc, bwi_led_duration[rate].on_dur, bwi_led_duration[rate].off_dur); } static void bwi_led_blink_start(struct bwi_softc *sc, int on_dur, int off_dur) { struct bwi_led *led = sc->sc_blink_led; uint16_t val; val = CSR_READ_2(sc, BWI_MAC_GPIO_CTRL); val = bwi_led_onoff(led, val, 1); CSR_WRITE_2(sc, BWI_MAC_GPIO_CTRL, val); if (led->l_flags & BWI_LED_F_SLOW) { BWI_LED_SLOWDOWN(on_dur); BWI_LED_SLOWDOWN(off_dur); } sc->sc_led_blinking = 1; sc->sc_led_blink_offdur = off_dur; callout_reset(&sc->sc_led_blink_ch, on_dur, bwi_led_blink_next, sc); } static void bwi_led_blink_next(void *xsc) { struct bwi_softc *sc = xsc; uint16_t val; val = CSR_READ_2(sc, BWI_MAC_GPIO_CTRL); val = bwi_led_onoff(sc->sc_blink_led, val, 0); CSR_WRITE_2(sc, BWI_MAC_GPIO_CTRL, val); callout_reset(&sc->sc_led_blink_ch, sc->sc_led_blink_offdur, bwi_led_blink_end, sc); } static void bwi_led_blink_end(void *xsc) { struct bwi_softc *sc = xsc; sc->sc_led_blinking = 0; } static void bwi_restart(void *xsc, int pending) { struct bwi_softc *sc = xsc; struct ifnet *ifp = sc->sc_ifp; if_printf(ifp, "%s begin, help!\n", __func__); BWI_LOCK(sc); bwi_init_statechg(xsc, 0); BWI_UNLOCK(sc); #if 0 bwi_start_locked(ifp); #endif } Index: head/sys/dev/iwn/if_iwn.c =================================================================== --- head/sys/dev/iwn/if_iwn.c (revision 193072) +++ head/sys/dev/iwn/if_iwn.c (revision 193073) @@ -1,4487 +1,4493 @@ /*- * Copyright (c) 2007 * Damien Bergamini * Copyright (c) 2008 * Benjamin Close * Copyright (c) 2008 Sam Leffler, Errno Consulting * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Driver for Intel Wireless WiFi Link 4965AGN 802.11 network adapters. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int iwn_probe(device_t); static int iwn_attach(device_t); static int iwn_detach(device_t); static int iwn_cleanup(device_t); static struct ieee80211vap *iwn_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void iwn_vap_delete(struct ieee80211vap *); static int iwn_shutdown(device_t); static int iwn_suspend(device_t); static int iwn_resume(device_t); static int iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *, void **, bus_size_t, bus_size_t, int); static void iwn_dma_contig_free(struct iwn_dma_info *); int iwn_alloc_shared(struct iwn_softc *); void iwn_free_shared(struct iwn_softc *); int iwn_alloc_kw(struct iwn_softc *); void iwn_free_kw(struct iwn_softc *); int iwn_alloc_fwmem(struct iwn_softc *); void iwn_free_fwmem(struct iwn_softc *); struct iwn_rbuf *iwn_alloc_rbuf(struct iwn_softc *); void iwn_free_rbuf(void *, void *); int iwn_alloc_rpool(struct iwn_softc *); void iwn_free_rpool(struct iwn_softc *); int iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); void iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); void iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); int iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *, int); void iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); void iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); static struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); void iwn_newassoc(struct ieee80211_node *, int); int iwn_media_change(struct ifnet *); int iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); void iwn_mem_lock(struct iwn_softc *); void iwn_mem_unlock(struct iwn_softc *); uint32_t iwn_mem_read(struct iwn_softc *, uint32_t); void iwn_mem_write(struct iwn_softc *, uint32_t, uint32_t); void iwn_mem_write_region_4(struct iwn_softc *, uint32_t, const uint32_t *, int); int iwn_eeprom_lock(struct iwn_softc *); void iwn_eeprom_unlock(struct iwn_softc *); int iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int); int iwn_transfer_microcode(struct iwn_softc *, const uint8_t *, int); int iwn_transfer_firmware(struct iwn_softc *); int iwn_load_firmware(struct iwn_softc *); void iwn_unload_firmware(struct iwn_softc *); static void iwn_timer_timeout(void *); static void iwn_calib_reset(struct iwn_softc *); void iwn_ampdu_rx_start(struct iwn_softc *, struct iwn_rx_desc *); void iwn_rx_intr(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *); void iwn_tx_intr(struct iwn_softc *, struct iwn_rx_desc *); void iwn_cmd_intr(struct iwn_softc *, struct iwn_rx_desc *); void iwn_notif_intr(struct iwn_softc *); void iwn_intr(void *); void iwn_read_eeprom(struct iwn_softc *, uint8_t macaddr[IEEE80211_ADDR_LEN]); static void iwn_read_eeprom_channels(struct iwn_softc *); void iwn_print_power_group(struct iwn_softc *, int); uint8_t iwn_plcp_signal(int); int iwn_tx_data(struct iwn_softc *, struct mbuf *, struct ieee80211_node *, struct iwn_tx_ring *); void iwn_start(struct ifnet *); void iwn_start_locked(struct ifnet *); static int iwn_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void iwn_watchdog(struct iwn_softc *); int iwn_ioctl(struct ifnet *, u_long, caddr_t); int iwn_cmd(struct iwn_softc *, int, const void *, int, int); int iwn_set_link_quality(struct iwn_softc *, uint8_t, const struct ieee80211_channel *, int); int iwn_set_key(struct ieee80211com *, struct ieee80211_node *, const struct ieee80211_key *); int iwn_wme_update(struct ieee80211com *); void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t); int iwn_set_critical_temp(struct iwn_softc *); void iwn_enable_tsf(struct iwn_softc *, struct ieee80211_node *); void iwn_power_calibration(struct iwn_softc *, int); int iwn_set_txpower(struct iwn_softc *, struct ieee80211_channel *, int); int8_t iwn_get_rssi(struct iwn_softc *, const struct iwn_rx_stat *); int iwn_get_noise(const struct iwn_rx_general_stats *); int iwn_get_temperature(struct iwn_softc *); int iwn_init_sensitivity(struct iwn_softc *); void iwn_compute_differential_gain(struct iwn_softc *, const struct iwn_rx_general_stats *); void iwn_tune_sensitivity(struct iwn_softc *, const struct iwn_rx_stats *); int iwn_send_sensitivity(struct iwn_softc *); int iwn_auth(struct iwn_softc *, struct ieee80211vap *); int iwn_run(struct iwn_softc *, struct ieee80211vap *); int iwn_scan(struct iwn_softc *); int iwn_config(struct iwn_softc *); void iwn_post_alive(struct iwn_softc *); void iwn_stop_master(struct iwn_softc *); int iwn_reset(struct iwn_softc *); void iwn_hw_config(struct iwn_softc *); void iwn_init_locked(struct iwn_softc *); void iwn_init(void *); void iwn_stop_locked(struct iwn_softc *); void iwn_stop(struct iwn_softc *); static void iwn_scan_start(struct ieee80211com *); static void iwn_scan_end(struct ieee80211com *); static void iwn_set_channel(struct ieee80211com *); static void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long); static void iwn_scan_mindwell(struct ieee80211_scan_state *); static void iwn_hwreset(void *, int); static void iwn_radioon(void *, int); static void iwn_radiooff(void *, int); static void iwn_sysctlattach(struct iwn_softc *); #define IWN_DEBUG #ifdef IWN_DEBUG enum { IWN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ IWN_DEBUG_RECV = 0x00000002, /* basic recv operation */ IWN_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ IWN_DEBUG_TXPOW = 0x00000008, /* tx power processing */ IWN_DEBUG_RESET = 0x00000010, /* reset processing */ IWN_DEBUG_OPS = 0x00000020, /* iwn_ops processing */ IWN_DEBUG_BEACON = 0x00000040, /* beacon handling */ IWN_DEBUG_WATCHDOG = 0x00000080, /* watchdog timeout */ IWN_DEBUG_INTR = 0x00000100, /* ISR */ IWN_DEBUG_CALIBRATE = 0x00000200, /* periodic calibration */ IWN_DEBUG_NODE = 0x00000400, /* node management */ IWN_DEBUG_LED = 0x00000800, /* led management */ IWN_DEBUG_CMD = 0x00001000, /* cmd submission */ IWN_DEBUG_FATAL = 0x80000000, /* fatal errors */ IWN_DEBUG_ANY = 0xffffffff }; #define DPRINTF(sc, m, fmt, ...) do { \ if (sc->sc_debug & (m)) \ printf(fmt, __VA_ARGS__); \ } while (0) static const char *iwn_intr_str(uint8_t); #else #define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0) #endif struct iwn_ident { uint16_t vendor; uint16_t device; const char *name; }; static const struct iwn_ident iwn_ident_table [] = { { 0x8086, 0x4229, "Intel(R) PRO/Wireless 4965BGN" }, { 0x8086, 0x422D, "Intel(R) PRO/Wireless 4965BGN" }, { 0x8086, 0x4230, "Intel(R) PRO/Wireless 4965BGN" }, { 0x8086, 0x4233, "Intel(R) PRO/Wireless 4965BGN" }, { 0, 0, NULL } }; static int iwn_probe(device_t dev) { const struct iwn_ident *ident; for (ident = iwn_ident_table; ident->name != NULL; ident++) { if (pci_get_vendor(dev) == ident->vendor && pci_get_device(dev) == ident->device) { device_set_desc(dev, ident->name); return 0; } } return ENXIO; } static int iwn_attach(device_t dev) { struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; int i, error, result; uint8_t macaddr[IEEE80211_ADDR_LEN]; sc->sc_dev = dev; /* XXX */ if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { device_printf(dev, "chip is in D%d power mode " "-- setting to D0\n", pci_get_powerstate(dev)); pci_set_powerstate(dev, PCI_POWERSTATE_D0); } /* clear device specific PCI configuration register 0x41 */ pci_write_config(dev, 0x41, 0, 1); /* enable bus-mastering */ pci_enable_busmaster(dev); sc->mem_rid= PCIR_BAR(0); sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem == NULL ) { device_printf(dev, "could not allocate memory resources\n"); error = ENOMEM; return error; } sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); sc->irq_rid = 0; if ((result = pci_msi_count(dev)) == 1 && pci_alloc_msi(dev, &result) == 0) sc->irq_rid = 1; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->irq == NULL) { device_printf(dev, "could not allocate interrupt resource\n"); error = ENOMEM; return error; } IWN_LOCK_INIT(sc); callout_init_mtx(&sc->sc_timer_to, &sc->sc_mtx, 0); TASK_INIT(&sc->sc_reinit_task, 0, iwn_hwreset, sc ); TASK_INIT(&sc->sc_radioon_task, 0, iwn_radioon, sc ); TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radiooff, sc ); /* * Put adapter into a known state. */ error = iwn_reset(sc); if (error != 0) { device_printf(dev, "could not reset adapter, error %d\n", error); goto fail; } /* * Allocate DMA memory for firmware transfers. */ error = iwn_alloc_fwmem(sc); if (error != 0) { device_printf(dev, "could not allocate firmware memory, error %d\n", error); goto fail; } /* * Allocate a "keep warm" page. */ error = iwn_alloc_kw(sc); if (error != 0) { device_printf(dev, "could not allocate keep-warm page, error %d\n", error); goto fail; } /* * Allocate shared area (communication area). */ error = iwn_alloc_shared(sc); if (error != 0) { device_printf(dev, "could not allocate shared area, error %d\n", error); goto fail; } /* * Allocate Tx rings. */ for (i = 0; i < IWN_NTXQUEUES; i++) { error = iwn_alloc_tx_ring(sc, &sc->txq[i], i); if (error != 0) { device_printf(dev, "could not allocate Tx ring %d, error %d\n", i, error); goto fail; } } error = iwn_alloc_rx_ring(sc, &sc->rxq); if (error != 0 ){ device_printf(dev, "could not allocate Rx ring, error %d\n", error); goto fail; } ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not allocate ifnet structure\n"); goto fail; } ic = ifp->if_l2com; ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WPA | IEEE80211_C_SHPREAMBLE /* short preamble supported */ #if 0 | IEEE80211_C_BGSCAN /* background scanning */ | IEEE80211_C_IBSS /* ibss/adhoc mode */ #endif | IEEE80211_C_WME /* WME */ ; #if 0 /* XXX disable until HT channel setup works */ ic->ic_htcaps = IEEE80211_HTCAP_SMPS_ENA /* SM PS mode enabled */ | IEEE80211_HTCAP_CHWIDTH40 /* 40MHz channel width */ | IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */ | IEEE80211_HTCAP_SHORTGI40 /* short GI in 40MHz */ | IEEE80211_HTCAP_RXSTBC_2STREAM/* 1-2 spatial streams */ | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ /* s/w capabilities */ | IEEE80211_HTC_HT /* HT operation */ | IEEE80211_HTC_AMPDU /* tx A-MPDU */ | IEEE80211_HTC_AMSDU /* tx A-MSDU */ ; #endif /* read supported channels and MAC address from EEPROM */ iwn_read_eeprom(sc, macaddr); if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = iwn_init; ifp->if_ioctl = iwn_ioctl; ifp->if_start = iwn_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ieee80211_ifattach(ic, macaddr); ic->ic_vap_create = iwn_vap_create; ic->ic_vap_delete = iwn_vap_delete; ic->ic_raw_xmit = iwn_raw_xmit; ic->ic_node_alloc = iwn_node_alloc; ic->ic_newassoc = iwn_newassoc; ic->ic_wme.wme_update = iwn_wme_update; ic->ic_scan_start = iwn_scan_start; ic->ic_scan_end = iwn_scan_end; ic->ic_set_channel = iwn_set_channel; ic->ic_scan_curchan = iwn_scan_curchan; ic->ic_scan_mindwell = iwn_scan_mindwell; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), IWN_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), IWN_RX_RADIOTAP_PRESENT); iwn_sysctlattach(sc); /* * Hook our interrupt after all initialization is complete. */ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, iwn_intr, sc, &sc->sc_ih); if (error != 0) { device_printf(dev, "could not set up interrupt, error %d\n", error); goto fail; } ieee80211_announce(ic); return 0; fail: iwn_cleanup(dev); return error; } static int iwn_detach(device_t dev) { iwn_cleanup(dev); return 0; } /* * Cleanup any device resources that were allocated */ int iwn_cleanup(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int i; ieee80211_draintask(ic, &sc->sc_reinit_task); ieee80211_draintask(ic, &sc->sc_radioon_task); ieee80211_draintask(ic, &sc->sc_radiooff_task); if (ifp != NULL) { iwn_stop(sc); callout_drain(&sc->sc_timer_to); ieee80211_ifdetach(ic); } iwn_unload_firmware(sc); iwn_free_rx_ring(sc, &sc->rxq); for (i = 0; i < IWN_NTXQUEUES; i++) iwn_free_tx_ring(sc, &sc->txq[i]); iwn_free_kw(sc); iwn_free_fwmem(sc); if (sc->irq != NULL) { bus_teardown_intr(dev, sc->irq, sc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); if (sc->irq_rid == 1) pci_release_msi(dev); } if (sc->mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); if (ifp != NULL) if_free(ifp); IWN_LOCK_DESTROY(sc); return 0; } static struct ieee80211vap * iwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct iwn_vap *ivp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (ivp == NULL) return NULL; vap = &ivp->iv_vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); vap->iv_bmissthreshold = 10; /* override default */ /* override with driver methods */ ivp->iv_newstate = vap->iv_newstate; vap->iv_newstate = iwn_newstate; ieee80211_amrr_init(&ivp->iv_amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 500 /*ms*/); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return vap; } static void iwn_vap_delete(struct ieee80211vap *vap) { struct iwn_vap *ivp = IWN_VAP(vap); ieee80211_amrr_cleanup(&ivp->iv_amrr); ieee80211_vap_detach(vap); free(ivp, M_80211_VAP); } static int iwn_shutdown(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); iwn_stop(sc); return 0; } static int iwn_suspend(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); iwn_stop(sc); return 0; } static int iwn_resume(device_t dev) { struct iwn_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; pci_write_config(dev, 0x41, 0, 1); if (ifp->if_flags & IFF_UP) iwn_init(sc); return 0; } static void iwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); *(bus_addr_t *)arg = segs[0].ds_addr; } static int iwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma, void **kvap, bus_size_t size, bus_size_t alignment, int flags) { int error, lalignment, i; /* * FreeBSD can't guarrenty 16k alignment at the moment (11/2007) so * we allocate an extra 12k with 4k alignement and walk through * it trying to find where the alignment is. It's a nasty fix for * a bigger problem. */ DPRINTF(sc, IWN_DEBUG_RESET, "Size: %zd - alignment %zd\n", size, alignment); if (alignment == 0x4000) { size += 12*1024; lalignment = 4096; DPRINTF(sc, IWN_DEBUG_RESET, "%s\n", "Attempting to find a 16k boundary"); } else lalignment = alignment; dma->size = size; dma->tag = NULL; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), lalignment, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, flags, NULL, NULL, &dma->tag); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dma_tag_create failed, error %d\n", __func__, error); goto fail; } error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, flags | BUS_DMA_ZERO, &dma->map); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamem_alloc failed, error %d\n", __func__, error); goto fail; } if (alignment == 0x4000) { for (i = 0; i < 3 && (((uintptr_t)dma->vaddr) & 0x3fff); i++) { DPRINTF(sc, IWN_DEBUG_RESET, "%s\n", "Memory Unaligned, shifting pointer by 4k"); dma->vaddr += 4096; size -= 4096; } if ((((uintptr_t)dma->vaddr ) & (alignment-1))) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: failed to align memory, vaddr %p, align %zd\n", __func__, dma->vaddr, alignment); error = ENOMEM; goto fail; } } error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, iwn_dma_map_addr, &dma->paddr, flags); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); goto fail; } if (kvap != NULL) *kvap = dma->vaddr; return 0; fail: iwn_dma_contig_free(dma); return error; } static void iwn_dma_contig_free(struct iwn_dma_info *dma) { if (dma->tag != NULL) { if (dma->map != NULL) { if (dma->paddr == 0) { bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(dma->tag, dma->map); } bus_dmamem_free(dma->tag, &dma->vaddr, dma->map); } bus_dma_tag_destroy(dma->tag); } } int iwn_alloc_shared(struct iwn_softc *sc) { /* must be aligned on a 1KB boundary */ return iwn_dma_contig_alloc(sc, &sc->shared_dma, (void **)&sc->shared, sizeof (struct iwn_shared), 1024, BUS_DMA_NOWAIT); } void iwn_free_shared(struct iwn_softc *sc) { iwn_dma_contig_free(&sc->shared_dma); } int iwn_alloc_kw(struct iwn_softc *sc) { /* must be aligned on a 4k boundary */ return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, PAGE_SIZE, PAGE_SIZE, BUS_DMA_NOWAIT); } void iwn_free_kw(struct iwn_softc *sc) { iwn_dma_contig_free(&sc->kw_dma); } int iwn_alloc_fwmem(struct iwn_softc *sc) { /* allocate enough contiguous space to store text and data */ return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, IWN_FW_MAIN_TEXT_MAXSZ + IWN_FW_MAIN_DATA_MAXSZ, 16, BUS_DMA_NOWAIT); } void iwn_free_fwmem(struct iwn_softc *sc) { iwn_dma_contig_free(&sc->fw_dma); } int iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) { int i, error; ring->cur = 0; error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, IWN_RX_RING_COUNT * sizeof (uint32_t), IWN_RING_DMA_ALIGN, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate rx ring DMA memory, error %d\n", __func__, error); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dma_tag_create_failed, error %d\n", __func__, error); goto fail; } /* * Setup Rx buffers. */ for (i = 0; i < IWN_RX_RING_COUNT; i++) { struct iwn_rx_data *data = &ring->data[i]; struct mbuf *m; bus_addr_t paddr; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamap_create failed, error %d\n", __func__, error); goto fail; } m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (m == NULL) { device_printf(sc->sc_dev, "%s: could not allocate rx mbuf\n", __func__); error = ENOMEM; goto fail; } /* map page */ error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m, caddr_t), MJUMPAGESIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); m_freem(m); error = ENOMEM; /* XXX unique code */ goto fail; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); data->m = m; /* Rx buffers are aligned on a 256-byte boundary */ ring->desc[i] = htole32(paddr >> 8); } bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); return 0; fail: iwn_free_rx_ring(sc, ring); return error; } void iwn_reset_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) { int ntries; iwn_mem_lock(sc); IWN_WRITE(sc, IWN_RX_CONFIG, 0); for (ntries = 0; ntries < 100; ntries++) { if (IWN_READ(sc, IWN_RX_STATUS) & IWN_RX_IDLE) break; DELAY(10); } #ifdef IWN_DEBUG if (ntries == 100) DPRINTF(sc, IWN_DEBUG_ANY, "%s\n", "timeout resetting Rx ring"); #endif iwn_mem_unlock(sc); ring->cur = 0; } void iwn_free_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) { int i; iwn_dma_contig_free(&ring->desc_dma); for (i = 0; i < IWN_RX_RING_COUNT; i++) if (ring->data[i].m != NULL) m_freem(ring->data[i].m); } int iwn_alloc_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring, int qid) { bus_size_t size; int i, error; ring->qid = qid; ring->queued = 0; ring->cur = 0; size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_desc); error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, size, IWN_RING_DMA_ALIGN, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate tx ring DMA memory, error %d\n", __func__, error); goto fail; } size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_cmd); error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, size, 4, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate tx cmd DMA memory, error %d\n", __func__, error); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, IWN_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dma_tag_create_failed, error %d\n", __func__, error); goto fail; } for (i = 0; i < IWN_TX_RING_COUNT; i++) { struct iwn_tx_data *data = &ring->data[i]; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "%s: bus_dmamap_create failed, error %d\n", __func__, error); goto fail; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); } return 0; fail: iwn_free_tx_ring(sc, ring); return error; } void iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) { uint32_t tmp; int i, ntries; iwn_mem_lock(sc); IWN_WRITE(sc, IWN_TX_CONFIG(ring->qid), 0); for (ntries = 0; ntries < 20; ntries++) { tmp = IWN_READ(sc, IWN_TX_STATUS); if ((tmp & IWN_TX_IDLE(ring->qid)) == IWN_TX_IDLE(ring->qid)) break; DELAY(10); } #ifdef IWN_DEBUG if (ntries == 20) DPRINTF(sc, IWN_DEBUG_RESET, "%s: timeout resetting Tx ring %d\n", __func__, ring->qid); #endif iwn_mem_unlock(sc); for (i = 0; i < IWN_TX_RING_COUNT; i++) { struct iwn_tx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } } ring->queued = 0; ring->cur = 0; } void iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) { int i; iwn_dma_contig_free(&ring->desc_dma); iwn_dma_contig_free(&ring->cmd_dma); if (ring->data != NULL) { for (i = 0; i < IWN_TX_RING_COUNT; i++) { struct iwn_tx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); } } } } struct ieee80211_node * iwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { return malloc(sizeof (struct iwn_node), M_80211_NODE,M_NOWAIT | M_ZERO); } void iwn_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&IWN_VAP(vap)->iv_amrr, &IWN_NODE(ni)->amn, ni); } int iwn_media_change(struct ifnet *ifp) { int error = ieee80211_media_change(ifp); /* NB: only the fixed rate can change and that doesn't need a reset */ return (error == ENETRESET ? 0 : error); } int iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct iwn_vap *ivp = IWN_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct iwn_softc *sc = ic->ic_ifp->if_softc; int error; DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); IWN_LOCK(sc); callout_stop(&sc->sc_timer_to); if (nstate == IEEE80211_S_AUTH && vap->iv_state != IEEE80211_S_AUTH) { /* !AUTH -> AUTH requires adapter config */ error = iwn_auth(sc, vap); } if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) { /* * !RUN -> RUN requires setting the association id * which is done with a firmware cmd. We also defer * starting the timers until that work is done. */ error = iwn_run(sc, vap); } if (nstate == IEEE80211_S_RUN) { /* * RUN -> RUN transition; just restart the timers. */ iwn_calib_reset(sc); } IWN_UNLOCK(sc); IEEE80211_LOCK(ic); return ivp->iv_newstate(vap, nstate, arg); } /* * Grab exclusive access to NIC memory. */ void iwn_mem_lock(struct iwn_softc *sc) { uint32_t tmp; int ntries; tmp = IWN_READ(sc, IWN_GPIO_CTL); IWN_WRITE(sc, IWN_GPIO_CTL, tmp | IWN_GPIO_MAC); /* spin until we actually get the lock */ for (ntries = 0; ntries < 1000; ntries++) { if ((IWN_READ(sc, IWN_GPIO_CTL) & (IWN_GPIO_CLOCK | IWN_GPIO_SLEEP)) == IWN_GPIO_CLOCK) break; DELAY(10); } if (ntries == 1000) device_printf(sc->sc_dev, "%s: could not lock memory\n", __func__); } /* * Release lock on NIC memory. */ void iwn_mem_unlock(struct iwn_softc *sc) { uint32_t tmp = IWN_READ(sc, IWN_GPIO_CTL); IWN_WRITE(sc, IWN_GPIO_CTL, tmp & ~IWN_GPIO_MAC); } uint32_t iwn_mem_read(struct iwn_softc *sc, uint32_t addr) { IWN_WRITE(sc, IWN_READ_MEM_ADDR, IWN_MEM_4 | addr); return IWN_READ(sc, IWN_READ_MEM_DATA); } void iwn_mem_write(struct iwn_softc *sc, uint32_t addr, uint32_t data) { IWN_WRITE(sc, IWN_WRITE_MEM_ADDR, IWN_MEM_4 | addr); IWN_WRITE(sc, IWN_WRITE_MEM_DATA, data); } void iwn_mem_write_region_4(struct iwn_softc *sc, uint32_t addr, const uint32_t *data, int wlen) { for (; wlen > 0; wlen--, data++, addr += 4) iwn_mem_write(sc, addr, *data); } int iwn_eeprom_lock(struct iwn_softc *sc) { uint32_t tmp; int ntries; tmp = IWN_READ(sc, IWN_HWCONFIG); IWN_WRITE(sc, IWN_HWCONFIG, tmp | IWN_HW_EEPROM_LOCKED); /* spin until we actually get the lock */ for (ntries = 0; ntries < 100; ntries++) { if (IWN_READ(sc, IWN_HWCONFIG) & IWN_HW_EEPROM_LOCKED) return 0; DELAY(10); } return ETIMEDOUT; } void iwn_eeprom_unlock(struct iwn_softc *sc) { uint32_t tmp = IWN_READ(sc, IWN_HWCONFIG); IWN_WRITE(sc, IWN_HWCONFIG, tmp & ~IWN_HW_EEPROM_LOCKED); } /* * Read `len' bytes from the EEPROM. We access the EEPROM through the MAC * instead of using the traditional bit-bang method. */ int iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int len) { uint8_t *out = data; uint32_t val; int ntries, tmp; iwn_mem_lock(sc); for (; len > 0; len -= 2, addr++) { IWN_WRITE(sc, IWN_EEPROM_CTL, addr << 2); tmp = IWN_READ(sc, IWN_EEPROM_CTL); IWN_WRITE(sc, IWN_EEPROM_CTL, tmp & ~IWN_EEPROM_MSK ); for (ntries = 0; ntries < 10; ntries++) { if ((val = IWN_READ(sc, IWN_EEPROM_CTL)) & IWN_EEPROM_READY) break; DELAY(5); } if (ntries == 10) { device_printf(sc->sc_dev,"could not read EEPROM\n"); return ETIMEDOUT; } *out++ = val >> 16; if (len > 1) *out++ = val >> 24; } iwn_mem_unlock(sc); return 0; } /* * The firmware boot code is small and is intended to be copied directly into * the NIC internal memory. */ int iwn_transfer_microcode(struct iwn_softc *sc, const uint8_t *ucode, int size) { int ntries; size /= sizeof (uint32_t); iwn_mem_lock(sc); /* copy microcode image into NIC memory */ iwn_mem_write_region_4(sc, IWN_MEM_UCODE_BASE, (const uint32_t *)ucode, size); iwn_mem_write(sc, IWN_MEM_UCODE_SRC, 0); iwn_mem_write(sc, IWN_MEM_UCODE_DST, IWN_FW_TEXT); iwn_mem_write(sc, IWN_MEM_UCODE_SIZE, size); /* run microcode */ iwn_mem_write(sc, IWN_MEM_UCODE_CTL, IWN_UC_RUN); /* wait for transfer to complete */ for (ntries = 0; ntries < 1000; ntries++) { if (!(iwn_mem_read(sc, IWN_MEM_UCODE_CTL) & IWN_UC_RUN)) break; DELAY(10); } if (ntries == 1000) { iwn_mem_unlock(sc); device_printf(sc->sc_dev, "%s: could not load boot firmware\n", __func__); return ETIMEDOUT; } iwn_mem_write(sc, IWN_MEM_UCODE_CTL, IWN_UC_ENABLE); iwn_mem_unlock(sc); return 0; } int iwn_load_firmware(struct iwn_softc *sc) { int error; KASSERT(sc->fw_fp == NULL, ("firmware already loaded")); IWN_UNLOCK(sc); /* load firmware image from disk */ sc->fw_fp = firmware_get("iwnfw"); if (sc->fw_fp == NULL) { device_printf(sc->sc_dev, "%s: could not load firmare image \"iwnfw\"\n", __func__); error = EINVAL; } else error = 0; IWN_LOCK(sc); return error; } int iwn_transfer_firmware(struct iwn_softc *sc) { struct iwn_dma_info *dma = &sc->fw_dma; const struct iwn_firmware_hdr *hdr; const uint8_t *init_text, *init_data, *main_text, *main_data; const uint8_t *boot_text; uint32_t init_textsz, init_datasz, main_textsz, main_datasz; uint32_t boot_textsz; int error = 0; const struct firmware *fp = sc->fw_fp; /* extract firmware header information */ if (fp->datasize < sizeof (struct iwn_firmware_hdr)) { device_printf(sc->sc_dev, "%s: truncated firmware header: %zu bytes, expecting %zu\n", __func__, fp->datasize, sizeof (struct iwn_firmware_hdr)); error = EINVAL; goto fail; } hdr = (const struct iwn_firmware_hdr *)fp->data; main_textsz = le32toh(hdr->main_textsz); main_datasz = le32toh(hdr->main_datasz); init_textsz = le32toh(hdr->init_textsz); init_datasz = le32toh(hdr->init_datasz); boot_textsz = le32toh(hdr->boot_textsz); /* sanity-check firmware segments sizes */ if (main_textsz > IWN_FW_MAIN_TEXT_MAXSZ || main_datasz > IWN_FW_MAIN_DATA_MAXSZ || init_textsz > IWN_FW_INIT_TEXT_MAXSZ || init_datasz > IWN_FW_INIT_DATA_MAXSZ || boot_textsz > IWN_FW_BOOT_TEXT_MAXSZ || (boot_textsz & 3) != 0) { device_printf(sc->sc_dev, "%s: invalid firmware header, main [%d,%d], init [%d,%d] " "boot %d\n", __func__, main_textsz, main_datasz, init_textsz, init_datasz, boot_textsz); error = EINVAL; goto fail; } /* check that all firmware segments are present */ if (fp->datasize < sizeof (struct iwn_firmware_hdr) + main_textsz + main_datasz + init_textsz + init_datasz + boot_textsz) { device_printf(sc->sc_dev, "%s: firmware file too short: " "%zu bytes, main [%d, %d], init [%d,%d] boot %d\n", __func__, fp->datasize, main_textsz, main_datasz, init_textsz, init_datasz, boot_textsz); error = EINVAL; goto fail; } /* get pointers to firmware segments */ main_text = (const uint8_t *)(hdr + 1); main_data = main_text + main_textsz; init_text = main_data + main_datasz; init_data = init_text + init_textsz; boot_text = init_data + init_datasz; /* copy initialization images into pre-allocated DMA-safe memory */ memcpy(dma->vaddr, init_data, init_datasz); memcpy(dma->vaddr + IWN_FW_INIT_DATA_MAXSZ, init_text, init_textsz); /* tell adapter where to find initialization images */ iwn_mem_lock(sc); iwn_mem_write(sc, IWN_MEM_DATA_BASE, dma->paddr >> 4); iwn_mem_write(sc, IWN_MEM_DATA_SIZE, init_datasz); iwn_mem_write(sc, IWN_MEM_TEXT_BASE, (dma->paddr + IWN_FW_INIT_DATA_MAXSZ) >> 4); iwn_mem_write(sc, IWN_MEM_TEXT_SIZE, init_textsz); iwn_mem_unlock(sc); /* load firmware boot code */ error = iwn_transfer_microcode(sc, boot_text, boot_textsz); if (error != 0) { device_printf(sc->sc_dev, "%s: could not load boot firmware, error %d\n", __func__, error); goto fail; } /* now press "execute" ;-) */ IWN_WRITE(sc, IWN_RESET, 0); /* wait at most one second for first alive notification */ error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz); if (error != 0) { /* this isn't what was supposed to happen.. */ device_printf(sc->sc_dev, "%s: timeout waiting for first alive notice, error %d\n", __func__, error); goto fail; } /* copy runtime images into pre-allocated DMA-safe memory */ memcpy(dma->vaddr, main_data, main_datasz); memcpy(dma->vaddr + IWN_FW_MAIN_DATA_MAXSZ, main_text, main_textsz); /* tell adapter where to find runtime images */ iwn_mem_lock(sc); iwn_mem_write(sc, IWN_MEM_DATA_BASE, dma->paddr >> 4); iwn_mem_write(sc, IWN_MEM_DATA_SIZE, main_datasz); iwn_mem_write(sc, IWN_MEM_TEXT_BASE, (dma->paddr + IWN_FW_MAIN_DATA_MAXSZ) >> 4); iwn_mem_write(sc, IWN_MEM_TEXT_SIZE, IWN_FW_UPDATED | main_textsz); iwn_mem_unlock(sc); /* wait at most one second for second alive notification */ error = msleep(sc, &sc->sc_mtx, PCATCH, "iwninit", hz); if (error != 0) { /* this isn't what was supposed to happen.. */ device_printf(sc->sc_dev, "%s: timeout waiting for second alive notice, error %d\n", __func__, error); goto fail; } return 0; fail: return error; } void iwn_unload_firmware(struct iwn_softc *sc) { if (sc->fw_fp != NULL) { firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); sc->fw_fp = NULL; } } static void iwn_timer_timeout(void *arg) { struct iwn_softc *sc = arg; IWN_LOCK_ASSERT(sc); if (sc->calib_cnt && --sc->calib_cnt == 0) { DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n", "send statistics request"); (void) iwn_cmd(sc, IWN_CMD_GET_STATISTICS, NULL, 0, 1); sc->calib_cnt = 60; /* do calibration every 60s */ } iwn_watchdog(sc); /* NB: piggyback tx watchdog */ callout_reset(&sc->sc_timer_to, hz, iwn_timer_timeout, sc); } static void iwn_calib_reset(struct iwn_softc *sc) { callout_reset(&sc->sc_timer_to, hz, iwn_timer_timeout, sc); sc->calib_cnt = 60; /* do calibration every 60s */ } void iwn_ampdu_rx_start(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct iwn_rx_stat *stat; DPRINTF(sc, IWN_DEBUG_RECV, "%s\n", "received AMPDU stats"); /* save Rx statistics, they will be used on IWN_AMPDU_RX_DONE */ stat = (struct iwn_rx_stat *)(desc + 1); memcpy(&sc->last_rx_stat, stat, sizeof (*stat)); sc->last_rx_valid = 1; } static __inline int maprate(int iwnrate) { switch (iwnrate) { /* CCK rates */ case 10: return 2; case 20: return 4; case 55: return 11; case 110: return 22; /* OFDM rates */ case 0xd: return 12; case 0xf: return 18; case 0x5: return 24; case 0x7: return 36; case 0x9: return 48; case 0xb: return 72; case 0x1: return 96; case 0x3: return 108; /* XXX MCS */ } /* unknown rate: should not happen */ return 0; } void iwn_rx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct iwn_rx_data *data) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_rx_ring *ring = &sc->rxq; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *m, *mnew; struct iwn_rx_stat *stat; caddr_t head; uint32_t *tail; int8_t rssi, nf; int len, error; bus_addr_t paddr; if (desc->type == IWN_AMPDU_RX_DONE) { /* check for prior AMPDU_RX_START */ if (!sc->last_rx_valid) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: missing AMPDU_RX_START\n", __func__); ifp->if_ierrors++; return; } sc->last_rx_valid = 0; stat = &sc->last_rx_stat; } else stat = (struct iwn_rx_stat *)(desc + 1); if (stat->cfg_phy_len > IWN_STAT_MAXLEN) { device_printf(sc->sc_dev, "%s: invalid rx statistic header, len %d\n", __func__, stat->cfg_phy_len); ifp->if_ierrors++; return; } if (desc->type == IWN_AMPDU_RX_DONE) { struct iwn_rx_ampdu *ampdu = (struct iwn_rx_ampdu *)(desc + 1); head = (caddr_t)(ampdu + 1); len = le16toh(ampdu->len); } else { head = (caddr_t)(stat + 1) + stat->cfg_phy_len; len = le16toh(stat->len); } /* discard Rx frames with bad CRC early */ tail = (uint32_t *)(head + len); if ((le32toh(*tail) & IWN_RX_NOERROR) != IWN_RX_NOERROR) { DPRINTF(sc, IWN_DEBUG_RECV, "%s: rx flags error %x\n", __func__, le32toh(*tail)); ifp->if_ierrors++; return; } if (len < sizeof (struct ieee80211_frame)) { DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n", __func__, len); ifp->if_ierrors++; return; } /* XXX don't need mbuf, just dma buffer */ mnew = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (mnew == NULL) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n", __func__); ifp->if_ierrors++; return; } error = bus_dmamap_load(ring->data_dmat, data->map, mtod(mnew, caddr_t), MJUMPAGESIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); m_freem(mnew); ifp->if_ierrors++; return; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); /* finalize mbuf and swap in new one */ m = data->m; m->m_pkthdr.rcvif = ifp; m->m_data = head; m->m_pkthdr.len = m->m_len = len; data->m = mnew; /* update Rx descriptor */ ring->desc[ring->cur] = htole32(paddr >> 8); rssi = iwn_get_rssi(sc, stat); /* grab a reference to the source node */ wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN && (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95; if (ieee80211_radiotap_active(ic)) { struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_tsft = htole64(stat->tstamp); tap->wr_flags = 0; if (stat->flags & htole16(IWN_CONFIG_SHPREAMBLE)) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; tap->wr_rate = maprate(stat->rate); tap->wr_dbm_antsignal = rssi; tap->wr_dbm_antnoise = nf; } IWN_UNLOCK(sc); /* send the frame to the 802.11 layer */ if (ni != NULL) { (void) ieee80211_input(ni, m, rssi - nf, nf); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi - nf, nf); IWN_LOCK(sc); } void iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct iwn_calib_state *calib = &sc->calib; struct iwn_stats *stats = (struct iwn_stats *)(desc + 1); /* beacon stats are meaningful only when associated and not scanning */ if (vap->iv_state != IEEE80211_S_RUN || (ic->ic_flags & IEEE80211_F_SCAN)) return; DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: cmd %d\n", __func__, desc->type); iwn_calib_reset(sc); /* test if temperature has changed */ if (stats->general.temp != sc->rawtemp) { int temp; sc->rawtemp = stats->general.temp; temp = iwn_get_temperature(sc); DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d\n", __func__, temp); /* update Tx power if need be */ iwn_power_calibration(sc, temp); } if (desc->type != IWN_BEACON_STATISTICS) return; /* reply to a statistics request */ sc->noise = iwn_get_noise(&stats->rx.general); DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise); /* test that RSSI and noise are present in stats report */ if (stats->rx.general.flags != htole32(1)) { DPRINTF(sc, IWN_DEBUG_ANY, "%s\n", "received statistics without RSSI"); return; } if (calib->state == IWN_CALIB_STATE_ASSOC) iwn_compute_differential_gain(sc, &stats->rx.general); else if (calib->state == IWN_CALIB_STATE_RUN) iwn_tune_sensitivity(sc, &stats->rx); } void iwn_tx_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct ifnet *ifp = sc->sc_ifp; struct iwn_tx_ring *ring = &sc->txq[desc->qid & 0xf]; struct iwn_tx_data *data = &ring->data[desc->idx]; struct iwn_tx_stat *stat = (struct iwn_tx_stat *)(desc + 1); struct iwn_node *wn = IWN_NODE(data->ni); struct mbuf *m; struct ieee80211_node *ni; uint32_t status; KASSERT(data->ni != NULL, ("no node")); DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", __func__, desc->qid, desc->idx, stat->ntries, stat->nkill, stat->rate, le16toh(stat->duration), le32toh(stat->status)); /* * Update rate control statistics for the node. */ status = le32toh(stat->status) & 0xff; if (status & 0x80) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: status 0x%x\n", __func__, le32toh(stat->status)); ifp->if_oerrors++; ieee80211_amrr_tx_complete(&wn->amn, IEEE80211_AMRR_FAILURE, stat->ntries); } else { ieee80211_amrr_tx_complete(&wn->amn, IEEE80211_AMRR_SUCCESS, stat->ntries); } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m = data->m, data->m = NULL; ni = data->ni, data->ni = NULL; if (m->m_flags & M_TXCB) { /* * Channels marked for "radar" require traffic to be received * to unlock before we can transmit. Until traffic is seen * any attempt to transmit is returned immediately with status * set to IWN_TX_FAIL_TX_LOCKED. Unfortunately this can easily * happen on first authenticate after scanning. To workaround * this we ignore a failure of this sort in AUTH state so the * 802.11 layer will fall back to using a timeout to wait for * the AUTH reply. This allows the firmware time to see * traffic so a subsequent retry of AUTH succeeds. It's * unclear why the firmware does not maintain state for * channels recently visited as this would allow immediate * use of the channel after a scan (where we see traffic). */ if (status == IWN_TX_FAIL_TX_LOCKED && ni->ni_vap->iv_state == IEEE80211_S_AUTH) ieee80211_process_callback(ni, m, 0); else ieee80211_process_callback(ni, m, (status & IWN_TX_FAIL) != 0); } m_freem(m); ieee80211_free_node(ni); ring->queued--; sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; iwn_start_locked(ifp); } void iwn_cmd_intr(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_data *data; if ((desc->qid & 0xf) != 4) return; /* not a command ack */ data = &ring->data[desc->idx]; /* if the command was mapped in a mbuf, free it */ if (data->m != NULL) { bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } wakeup(&ring->cmd[desc->idx]); } void iwn_notif_intr(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint16_t hw; hw = le16toh(sc->shared->closed_count) & 0xfff; while (sc->rxq.cur != hw) { struct iwn_rx_data *data = &sc->rxq.data[sc->rxq.cur]; struct iwn_rx_desc *desc = (void *)data->m->m_ext.ext_buf; DPRINTF(sc, IWN_DEBUG_RECV, "%s: qid %x idx %d flags %x type %d(%s) len %d\n", __func__, desc->qid, desc->idx, desc->flags, desc->type, iwn_intr_str(desc->type), le16toh(desc->len)); if (!(desc->qid & 0x80)) /* reply to a command */ iwn_cmd_intr(sc, desc); switch (desc->type) { case IWN_RX_DONE: case IWN_AMPDU_RX_DONE: iwn_rx_intr(sc, desc, data); break; case IWN_AMPDU_RX_START: iwn_ampdu_rx_start(sc, desc); break; case IWN_TX_DONE: /* a 802.11 frame has been transmitted */ iwn_tx_intr(sc, desc); break; case IWN_RX_STATISTICS: case IWN_BEACON_STATISTICS: iwn_rx_statistics(sc, desc); break; case IWN_BEACON_MISSED: { struct iwn_beacon_missed *miss = (struct iwn_beacon_missed *)(desc + 1); int misses = le32toh(miss->consecutive); /* XXX not sure why we're notified w/ zero */ if (misses == 0) break; DPRINTF(sc, IWN_DEBUG_STATE, "%s: beacons missed %d/%d\n", __func__, misses, le32toh(miss->total)); /* * If more than 5 consecutive beacons are missed, * reinitialize the sensitivity state machine. */ if (vap->iv_state == IEEE80211_S_RUN && misses > 5) (void) iwn_init_sensitivity(sc); if (misses >= vap->iv_bmissthreshold) ieee80211_beacon_miss(ic); break; } case IWN_UC_READY: { struct iwn_ucode_info *uc = (struct iwn_ucode_info *)(desc + 1); /* the microcontroller is ready */ DPRINTF(sc, IWN_DEBUG_RESET, "microcode alive notification version=%d.%d " "subtype=%x alive=%x\n", uc->major, uc->minor, uc->subtype, le32toh(uc->valid)); if (le32toh(uc->valid) != 1) { device_printf(sc->sc_dev, "microcontroller initialization failed"); break; } if (uc->subtype == IWN_UCODE_INIT) { /* save microcontroller's report */ memcpy(&sc->ucode_info, uc, sizeof (*uc)); } break; } case IWN_STATE_CHANGED: { uint32_t *status = (uint32_t *)(desc + 1); /* * State change allows hardware switch change to be * noted. However, we handle this in iwn_intr as we * get both the enable/disble intr. */ DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n", le32toh(*status)); break; } case IWN_START_SCAN: { struct iwn_start_scan *scan = (struct iwn_start_scan *)(desc + 1); DPRINTF(sc, IWN_DEBUG_ANY, "%s: scanning channel %d status %x\n", __func__, scan->chan, le32toh(scan->status)); break; } case IWN_STOP_SCAN: { struct iwn_stop_scan *scan = (struct iwn_stop_scan *)(desc + 1); DPRINTF(sc, IWN_DEBUG_STATE, "scan finished nchan=%d status=%d chan=%d\n", scan->nchan, scan->status, scan->chan); ieee80211_scan_next(vap); break; } } sc->rxq.cur = (sc->rxq.cur + 1) % IWN_RX_RING_COUNT; } /* tell the firmware what we have processed */ hw = (hw == 0) ? IWN_RX_RING_COUNT - 1 : hw - 1; IWN_WRITE(sc, IWN_RX_WIDX, hw & ~7); } static void iwn_rftoggle_intr(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t tmp = IWN_READ(sc, IWN_GPIO_CTL); IWN_LOCK_ASSERT(sc); device_printf(sc->sc_dev, "RF switch: radio %s\n", (tmp & IWN_GPIO_RF_ENABLED) ? "enabled" : "disabled"); if (tmp & IWN_GPIO_RF_ENABLED) ieee80211_runtask(ic, &sc->sc_radioon_task); else ieee80211_runtask(ic, &sc->sc_radiooff_task); } static void iwn_error_intr(struct iwn_softc *sc, uint32_t r1, uint32_t r2) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); IWN_LOCK_ASSERT(sc); device_printf(sc->sc_dev, "error, INTR=%b STATUS=0x%x\n", r1, IWN_INTR_BITS, r2); if (vap != NULL) ieee80211_cancel_scan(vap); ieee80211_runtask(ic, &sc->sc_reinit_task); } void iwn_intr(void *arg) { struct iwn_softc *sc = arg; uint32_t r1, r2; IWN_LOCK(sc); /* disable interrupts */ IWN_WRITE(sc, IWN_MASK, 0); r1 = IWN_READ(sc, IWN_INTR); r2 = IWN_READ(sc, IWN_INTR_STATUS); if (r1 == 0 && r2 == 0) { IWN_WRITE(sc, IWN_MASK, IWN_INTR_MASK); goto done; /* not for us */ } if (r1 == 0xffffffff) goto done; /* hardware gone */ /* ack interrupts */ IWN_WRITE(sc, IWN_INTR, r1); IWN_WRITE(sc, IWN_INTR_STATUS, r2); DPRINTF(sc, IWN_DEBUG_INTR, "interrupt reg1=%x reg2=%x\n", r1, r2); if (r1 & IWN_RF_TOGGLED) iwn_rftoggle_intr(sc); if (r1 & IWN_CT_REACHED) device_printf(sc->sc_dev, "critical temperature reached!\n"); if (r1 & (IWN_SW_ERROR | IWN_HW_ERROR)) { iwn_error_intr(sc, r1, r2); goto done; } if ((r1 & (IWN_RX_INTR | IWN_SW_RX_INTR)) || (r2 & IWN_RX_STATUS_INTR)) iwn_notif_intr(sc); if (r1 & IWN_ALIVE_INTR) wakeup(sc); /* re-enable interrupts */ IWN_WRITE(sc, IWN_MASK, IWN_INTR_MASK); done: IWN_UNLOCK(sc); } uint8_t iwn_plcp_signal(int rate) { switch (rate) { /* CCK rates (returned values are device-dependent) */ case 2: return 10; case 4: return 20; case 11: return 55; case 22: return 110; /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ /* R1-R4, (u)ral is R4-R1 */ case 12: return 0xd; case 18: return 0xf; case 24: return 0x5; case 36: return 0x7; case 48: return 0x9; case 72: return 0xb; case 96: return 0x1; case 108: return 0x3; case 120: return 0x3; } /* unknown rate (should not get there) */ return 0; } /* determine if a given rate is CCK or OFDM */ #define IWN_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) int iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, struct iwn_tx_ring *ring) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = sc->sc_ifp; const struct ieee80211_txparam *tp; struct iwn_tx_desc *desc; struct iwn_tx_data *data; struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct ieee80211_frame *wh; struct ieee80211_key *k; bus_addr_t paddr; uint32_t flags; uint16_t timeout; uint8_t type; u_int hdrlen; struct mbuf *mnew; int rate, error, pad, nsegs, i, ismcast, id; bus_dma_segment_t segs[IWN_MAX_SCATTER]; IWN_LOCK_ASSERT(sc); wh = mtod(m0, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); hdrlen = ieee80211_anyhdrsize(wh); /* pick a tx rate */ /* XXX ni_chan */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (type == IEEE80211_FC0_TYPE_MGT) rate = tp->mgmtrate; else if (ismcast) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else { (void) ieee80211_amrr_choose(ni, &IWN_NODE(ni)->amn); rate = ni->ni_txrate; } if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } else k = NULL; if (ieee80211_radiotap_active_vap(vap)) { struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; if (k != NULL) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; ieee80211_radiotap_tx(vap, m0); } flags = IWN_TX_AUTO_SEQ; /* XXX honor ACM */ if (!ismcast) flags |= IWN_TX_NEED_ACK; if (ismcast || type != IEEE80211_FC0_TYPE_DATA) id = IWN_ID_BROADCAST; else id = IWN_ID_BSS; /* check if RTS/CTS or CTS-to-self protection must be used */ if (!ismcast) { /* multicast frames are not sent at OFDM rates in 802.11b/g */ if (m0->m_pkthdr.len+IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && IWN_RATE_IS_OFDM(rate)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; } } if (type == IEEE80211_FC0_TYPE_MGT) { uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; /* tell h/w to set timestamp in probe responses */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= IWN_TX_INSERT_TSTAMP; if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) timeout = htole16(3); else timeout = htole16(2); } else timeout = htole16(0); if (hdrlen & 3) { /* first segment's length must be a multiple of 4 */ flags |= IWN_TX_NEED_PADDING; pad = 4 - (hdrlen & 3); } else pad = 0; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; cmd = &ring->cmd[ring->cur]; cmd->code = IWN_CMD_TX_DATA; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; tx = (struct iwn_cmd_data *)cmd->data; /* NB: no need to bzero tx, all fields are reinitialized here */ tx->id = id; tx->flags = htole32(flags); tx->len = htole16(m0->m_pkthdr.len); tx->rate = iwn_plcp_signal(rate); tx->rts_ntries = 60; /* XXX? */ tx->data_ntries = 15; /* XXX? */ tx->lifetime = htole32(IWN_LIFETIME_INFINITE); tx->timeout = timeout; if (k != NULL) { /* XXX fill in */; } else tx->security = 0; /* XXX alternate between Ant A and Ant B ? */ tx->rflags = IWN_RFLAG_ANT_B; if (tx->id == IWN_ID_BROADCAST) { tx->ridx = IWN_MAX_TX_RETRIES - 1; if (!IWN_RATE_IS_OFDM(rate)) tx->rflags |= IWN_RFLAG_CCK; } else { tx->ridx = 0; /* tell adapter to ignore rflags */ tx->flags |= htole32(IWN_TX_USE_NODE_RATE); } /* copy and trim IEEE802.11 header */ memcpy((uint8_t *)(tx + 1), wh, hdrlen); m_adj(m0, hdrlen); error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { if (error == EFBIG) { /* too many fragments, linearize */ mnew = m_collapse(m0, M_DONTWAIT, IWN_MAX_SCATTER); if (mnew == NULL) { IWN_UNLOCK(sc); device_printf(sc->sc_dev, "%s: could not defrag mbuf\n", __func__); m_freem(m0); return ENOBUFS; } m0 = mnew; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); } if (error != 0) { IWN_UNLOCK(sc); device_printf(sc->sc_dev, "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", __func__, error); m_freem(m0); return error; } } data->m = m0; data->ni = ni; DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", __func__, ring->qid, ring->cur, m0->m_pkthdr.len, nsegs); paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); tx->loaddr = htole32(paddr + 4 + offsetof(struct iwn_cmd_data, ntries)); tx->hiaddr = 0; /* limit to 32-bit physical addresses */ /* first scatter/gather segment is used by the tx data command */ IWN_SET_DESC_NSEGS(desc, 1 + nsegs); IWN_SET_DESC_SEG(desc, 0, paddr, 4 + sizeof (*tx) + hdrlen + pad); for (i = 1; i <= nsegs; i++) { IWN_SET_DESC_SEG(desc, i, segs[i - 1].ds_addr, segs[i - 1].ds_len); } sc->shared->len[ring->qid][ring->cur] = htole16(hdrlen + m0->m_pkthdr.len + 8); if (ring->cur < IWN_TX_WINDOW) sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = htole16(hdrlen + m0->m_pkthdr.len + 8); ring->queued++; /* kick Tx ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); ifp->if_opackets++; sc->sc_tx_timer = 5; return 0; } void iwn_start(struct ifnet *ifp) { struct iwn_softc *sc = ifp->if_softc; IWN_LOCK(sc); iwn_start_locked(ifp); IWN_UNLOCK(sc); } void iwn_start_locked(struct ifnet *ifp) { struct iwn_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct iwn_tx_ring *txq; struct mbuf *m; int pri; IWN_LOCK_ASSERT(sc); for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; pri = M_WME_GETAC(m); txq = &sc->txq[pri]; if (txq->queued >= IWN_TX_RING_COUNT - 8) { /* XXX not right */ /* ring is nearly full, stop flow */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; } if (iwn_tx_data(sc, m, ni, txq) != 0) { ifp->if_oerrors++; ieee80211_free_node(ni); break; } } } static int iwn_tx_handoff(struct iwn_softc *sc, struct iwn_tx_ring *ring, struct iwn_tx_cmd *cmd, struct iwn_cmd_data *tx, struct ieee80211_node *ni, struct mbuf *m0, u_int hdrlen, int pad) { struct ifnet *ifp = sc->sc_ifp; struct iwn_tx_desc *desc; struct iwn_tx_data *data; bus_addr_t paddr; struct mbuf *mnew; int error, nsegs, i; bus_dma_segment_t segs[IWN_MAX_SCATTER]; /* copy and trim IEEE802.11 header */ memcpy((uint8_t *)(tx + 1), mtod(m0, uint8_t *), hdrlen); m_adj(m0, hdrlen); desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { if (error == EFBIG) { /* too many fragments, linearize */ mnew = m_collapse(m0, M_DONTWAIT, IWN_MAX_SCATTER); if (mnew == NULL) { IWN_UNLOCK(sc); device_printf(sc->sc_dev, "%s: could not defrag mbuf\n", __func__); m_freem(m0); return ENOBUFS; } m0 = mnew; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); } if (error != 0) { IWN_UNLOCK(sc); device_printf(sc->sc_dev, "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", __func__, error); m_freem(m0); return error; } } data->m = m0; data->ni = ni; DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", __func__, ring->qid, ring->cur, m0->m_pkthdr.len, nsegs); paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); tx->loaddr = htole32(paddr + 4 + offsetof(struct iwn_cmd_data, ntries)); tx->hiaddr = 0; /* limit to 32-bit physical addresses */ /* first scatter/gather segment is used by the tx data command */ IWN_SET_DESC_NSEGS(desc, 1 + nsegs); IWN_SET_DESC_SEG(desc, 0, paddr, 4 + sizeof (*tx) + hdrlen + pad); for (i = 1; i <= nsegs; i++) { IWN_SET_DESC_SEG(desc, i, segs[i - 1].ds_addr, segs[i - 1].ds_len); } sc->shared->len[ring->qid][ring->cur] = htole16(hdrlen + m0->m_pkthdr.len + 8); if (ring->cur < IWN_TX_WINDOW) sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = htole16(hdrlen + m0->m_pkthdr.len + 8); ring->queued++; /* kick Tx ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); ifp->if_opackets++; sc->sc_tx_timer = 5; return 0; } static int iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, struct iwn_tx_ring *ring, const struct ieee80211_bpf_params *params) { struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct ieee80211_frame *wh; uint32_t flags; uint8_t type, subtype; u_int hdrlen; int rate, pad; IWN_LOCK_ASSERT(sc); wh = mtod(m0, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; hdrlen = ieee80211_anyhdrsize(wh); flags = IWN_TX_AUTO_SEQ; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= IWN_TX_NEED_ACK; if (params->ibp_flags & IEEE80211_BPF_RTS) flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; if (params->ibp_flags & IEEE80211_BPF_CTS) flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP; if (type == IEEE80211_FC0_TYPE_MGT && subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) { /* tell h/w to set timestamp in probe responses */ flags |= IWN_TX_INSERT_TSTAMP; } if (hdrlen & 3) { /* first segment's length must be a multiple of 4 */ flags |= IWN_TX_NEED_PADDING; pad = 4 - (hdrlen & 3); } else pad = 0; /* pick a tx rate */ rate = params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) { + /* XXX fall back to mcast/mgmt rate? */ + m_freem(m0); + return EINVAL; + } if (ieee80211_radiotap_active_vap(vap)) { struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; ieee80211_radiotap_tx(vap, m0); } cmd = &ring->cmd[ring->cur]; cmd->code = IWN_CMD_TX_DATA; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; tx = (struct iwn_cmd_data *)cmd->data; /* NB: no need to bzero tx, all fields are reinitialized here */ tx->id = IWN_ID_BROADCAST; tx->flags = htole32(flags); tx->len = htole16(m0->m_pkthdr.len); tx->rate = iwn_plcp_signal(rate); tx->rts_ntries = params->ibp_try1; /* XXX? */ tx->data_ntries = params->ibp_try0; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); /* XXX use try count? */ if (type == IEEE80211_FC0_TYPE_MGT) { if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) tx->timeout = htole16(3); else tx->timeout = htole16(2); } else tx->timeout = htole16(0); tx->security = 0; /* XXX alternate between Ant A and Ant B ? */ tx->rflags = IWN_RFLAG_ANT_B; /* XXX params->ibp_pri >> 2 */ tx->ridx = IWN_MAX_TX_RETRIES - 1; if (!IWN_RATE_IS_OFDM(rate)) tx->rflags |= IWN_RFLAG_CCK; return iwn_tx_handoff(sc, ring, cmd, tx, ni, m0, hdrlen, pad); } static int iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; struct iwn_tx_ring *txq; int error; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { ieee80211_free_node(ni); m_freem(m); return ENETDOWN; } IWN_LOCK(sc); if (params == NULL) txq = &sc->txq[M_WME_GETAC(m)]; else txq = &sc->txq[params->ibp_pri & 3]; if (txq->queued >= IWN_TX_RING_COUNT - 8) { /* XXX not right */ /* ring is nearly full, stop flow */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; } if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ error = iwn_tx_data(sc, m, ni, txq); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ error = iwn_tx_data_raw(sc, m, ni, txq, params); } if (error != 0) { /* NB: m is reclaimed on tx failure */ ieee80211_free_node(ni); ifp->if_oerrors++; } IWN_UNLOCK(sc); return error; } static void iwn_watchdog(struct iwn_softc *sc) { if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; if_printf(ifp, "device timeout\n"); ieee80211_runtask(ic, &sc->sc_reinit_task); } } int iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct iwn_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: IWN_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { iwn_init_locked(sc); startall = 1; } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) iwn_stop_locked(sc); } IWN_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; } void iwn_read_eeprom(struct iwn_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) { char domain[4]; uint16_t val; int i, error; if ((error = iwn_eeprom_lock(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not lock EEPROM, error %d\n", __func__, error); return; } /* read and print regulatory domain */ iwn_read_prom_data(sc, IWN_EEPROM_DOMAIN, domain, 4); device_printf(sc->sc_dev,"Reg Domain: %.4s", domain); /* read and print MAC address */ iwn_read_prom_data(sc, IWN_EEPROM_MAC, macaddr, 6); printf(", address %6D\n", macaddr, ":"); /* read the list of authorized channels */ iwn_read_eeprom_channels(sc); /* read maximum allowed Tx power for 2GHz and 5GHz bands */ iwn_read_prom_data(sc, IWN_EEPROM_MAXPOW, &val, 2); sc->maxpwr2GHz = val & 0xff; sc->maxpwr5GHz = val >> 8; /* check that EEPROM values are correct */ if (sc->maxpwr5GHz < 20 || sc->maxpwr5GHz > 50) sc->maxpwr5GHz = 38; if (sc->maxpwr2GHz < 20 || sc->maxpwr2GHz > 50) sc->maxpwr2GHz = 38; DPRINTF(sc, IWN_DEBUG_RESET, "maxpwr 2GHz=%d 5GHz=%d\n", sc->maxpwr2GHz, sc->maxpwr5GHz); /* read voltage at which samples were taken */ iwn_read_prom_data(sc, IWN_EEPROM_VOLTAGE, &val, 2); sc->eeprom_voltage = (int16_t)le16toh(val); DPRINTF(sc, IWN_DEBUG_RESET, "voltage=%d (in 0.3V)\n", sc->eeprom_voltage); /* read power groups */ iwn_read_prom_data(sc, IWN_EEPROM_BANDS, sc->bands, sizeof sc->bands); #ifdef IWN_DEBUG if (sc->sc_debug & IWN_DEBUG_ANY) { for (i = 0; i < IWN_NBANDS; i++) iwn_print_power_group(sc, i); } #endif iwn_eeprom_unlock(sc); } struct iwn_chan_band { uint32_t addr; /* offset in EEPROM */ uint32_t flags; /* net80211 flags */ uint8_t nchan; #define IWN_MAX_CHAN_PER_BAND 14 uint8_t chan[IWN_MAX_CHAN_PER_BAND]; }; static void iwn_read_eeprom_band(struct iwn_softc *sc, const struct iwn_chan_band *band) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; struct ieee80211_channel *c; int i, chan, flags; iwn_read_prom_data(sc, band->addr, channels, band->nchan * sizeof (struct iwn_eeprom_chan)); for (i = 0; i < band->nchan; i++) { if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID)) { DPRINTF(sc, IWN_DEBUG_RESET, "skip chan %d flags 0x%x maxpwr %d\n", band->chan[i], channels[i].flags, channels[i].maxpwr); continue; } chan = band->chan[i]; /* translate EEPROM flags to net80211 */ flags = 0; if ((channels[i].flags & IWN_EEPROM_CHAN_ACTIVE) == 0) flags |= IEEE80211_CHAN_PASSIVE; if ((channels[i].flags & IWN_EEPROM_CHAN_IBSS) == 0) flags |= IEEE80211_CHAN_NOADHOC; if (channels[i].flags & IWN_EEPROM_CHAN_RADAR) { flags |= IEEE80211_CHAN_DFS; /* XXX apparently IBSS may still be marked */ flags |= IEEE80211_CHAN_NOADHOC; } DPRINTF(sc, IWN_DEBUG_RESET, "add chan %d flags 0x%x maxpwr %d\n", chan, channels[i].flags, channels[i].maxpwr); c = &ic->ic_channels[ic->ic_nchans++]; c->ic_ieee = chan; c->ic_freq = ieee80211_ieee2mhz(chan, band->flags); c->ic_maxregpower = channels[i].maxpwr; c->ic_maxpower = 2*c->ic_maxregpower; if (band->flags & IEEE80211_CHAN_2GHZ) { /* G =>'s B is supported */ c->ic_flags = IEEE80211_CHAN_B | flags; c = &ic->ic_channels[ic->ic_nchans++]; c[0] = c[-1]; c->ic_flags = IEEE80211_CHAN_G | flags; } else { /* 5GHz band */ c->ic_flags = IEEE80211_CHAN_A | flags; } /* XXX no constraints on using HT20 */ /* add HT20, HT40 added separately */ c = &ic->ic_channels[ic->ic_nchans++]; c[0] = c[-1]; c->ic_flags |= IEEE80211_CHAN_HT20; /* XXX NARROW =>'s 1/2 and 1/4 width? */ } } static void iwn_read_eeprom_ht40(struct iwn_softc *sc, const struct iwn_chan_band *band) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_eeprom_chan channels[IWN_MAX_CHAN_PER_BAND]; struct ieee80211_channel *c, *cent, *extc; int i; iwn_read_prom_data(sc, band->addr, channels, band->nchan * sizeof (struct iwn_eeprom_chan)); for (i = 0; i < band->nchan; i++) { if (!(channels[i].flags & IWN_EEPROM_CHAN_VALID) || !(channels[i].flags & IWN_EEPROM_CHAN_WIDE)) { DPRINTF(sc, IWN_DEBUG_RESET, "skip chan %d flags 0x%x maxpwr %d\n", band->chan[i], channels[i].flags, channels[i].maxpwr); continue; } /* * Each entry defines an HT40 channel pair; find the * center channel, then the extension channel above. */ cent = ieee80211_find_channel_byieee(ic, band->chan[i], band->flags & ~IEEE80211_CHAN_HT); if (cent == NULL) { /* XXX shouldn't happen */ device_printf(sc->sc_dev, "%s: no entry for channel %d\n", __func__, band->chan[i]); continue; } extc = ieee80211_find_channel(ic, cent->ic_freq+20, band->flags & ~IEEE80211_CHAN_HT); if (extc == NULL) { DPRINTF(sc, IWN_DEBUG_RESET, "skip chan %d, extension channel not found\n", band->chan[i]); continue; } DPRINTF(sc, IWN_DEBUG_RESET, "add ht40 chan %d flags 0x%x maxpwr %d\n", band->chan[i], channels[i].flags, channels[i].maxpwr); c = &ic->ic_channels[ic->ic_nchans++]; c[0] = cent[0]; c->ic_extieee = extc->ic_ieee; c->ic_flags &= ~IEEE80211_CHAN_HT; c->ic_flags |= IEEE80211_CHAN_HT40U; c = &ic->ic_channels[ic->ic_nchans++]; c[0] = extc[0]; c->ic_extieee = cent->ic_ieee; c->ic_flags &= ~IEEE80211_CHAN_HT; c->ic_flags |= IEEE80211_CHAN_HT40D; } } static void iwn_read_eeprom_channels(struct iwn_softc *sc) { #define N(a) (sizeof(a)/sizeof(a[0])) static const struct iwn_chan_band iwn_bands[] = { { IWN_EEPROM_BAND1, IEEE80211_CHAN_G, 14, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } }, { IWN_EEPROM_BAND2, IEEE80211_CHAN_A, 13, { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 } }, { IWN_EEPROM_BAND3, IEEE80211_CHAN_A, 12, { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 } }, { IWN_EEPROM_BAND4, IEEE80211_CHAN_A, 11, { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 } }, { IWN_EEPROM_BAND5, IEEE80211_CHAN_A, 6, { 145, 149, 153, 157, 161, 165 } }, { IWN_EEPROM_BAND6, IEEE80211_CHAN_G | IEEE80211_CHAN_HT40, 7, { 1, 2, 3, 4, 5, 6, 7 } }, { IWN_EEPROM_BAND7, IEEE80211_CHAN_A | IEEE80211_CHAN_HT40, 11, { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 } } }; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int i; /* read the list of authorized channels */ for (i = 0; i < N(iwn_bands)-2; i++) iwn_read_eeprom_band(sc, &iwn_bands[i]); for (; i < N(iwn_bands); i++) iwn_read_eeprom_ht40(sc, &iwn_bands[i]); ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); #undef N } #ifdef IWN_DEBUG void iwn_print_power_group(struct iwn_softc *sc, int i) { struct iwn_eeprom_band *band = &sc->bands[i]; struct iwn_eeprom_chan_samples *chans = band->chans; int j, c; printf("===band %d===\n", i); printf("chan lo=%d, chan hi=%d\n", band->lo, band->hi); printf("chan1 num=%d\n", chans[0].num); for (c = 0; c < IWN_NTXCHAINS; c++) { for (j = 0; j < IWN_NSAMPLES; j++) { printf("chain %d, sample %d: temp=%d gain=%d " "power=%d pa_det=%d\n", c, j, chans[0].samples[c][j].temp, chans[0].samples[c][j].gain, chans[0].samples[c][j].power, chans[0].samples[c][j].pa_det); } } printf("chan2 num=%d\n", chans[1].num); for (c = 0; c < IWN_NTXCHAINS; c++) { for (j = 0; j < IWN_NSAMPLES; j++) { printf("chain %d, sample %d: temp=%d gain=%d " "power=%d pa_det=%d\n", c, j, chans[1].samples[c][j].temp, chans[1].samples[c][j].gain, chans[1].samples[c][j].power, chans[1].samples[c][j].pa_det); } } } #endif /* * Send a command to the firmware. */ int iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) { struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_desc *desc; struct iwn_tx_cmd *cmd; bus_addr_t paddr; IWN_LOCK_ASSERT(sc); KASSERT(size <= sizeof cmd->data, ("Command too big")); desc = &ring->desc[ring->cur]; cmd = &ring->cmd[ring->cur]; cmd->code = code; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; memcpy(cmd->data, buf, size); paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); IWN_SET_DESC_NSEGS(desc, 1); IWN_SET_DESC_SEG(desc, 0, paddr, 4 + size); sc->shared->len[ring->qid][ring->cur] = htole16(8); if (ring->cur < IWN_TX_WINDOW) { sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = htole16(8); } DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n", __func__, iwn_intr_str(cmd->code), cmd->code, cmd->flags, cmd->qid, cmd->idx); /* kick cmd ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); return async ? 0 : msleep(cmd, &sc->sc_mtx, PCATCH, "iwncmd", hz); } static const uint8_t iwn_ridx_to_plcp[] = { 10, 20, 55, 110, /* CCK */ 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, 0x3 /* OFDM R1-R4 */ }; static const uint8_t iwn_siso_mcs_to_plcp[] = { 0, 0, 0, 0, /* CCK */ 0, 0, 1, 2, 3, 4, 5, 6, 7 /* HT */ }; static const uint8_t iwn_mimo_mcs_to_plcp[] = { 0, 0, 0, 0, /* CCK */ 8, 8, 9, 10, 11, 12, 13, 14, 15 /* HT */ }; static const uint8_t iwn_prev_ridx[] = { /* NB: allow fallback from CCK11 to OFDM9 and from OFDM6 to CCK5 */ 0, 0, 1, 5, /* CCK */ 2, 4, 3, 6, 7, 8, 9, 10, 10 /* OFDM */ }; /* * Configure hardware link parameters for the specified * node operating on the specified channel. */ int iwn_set_link_quality(struct iwn_softc *sc, uint8_t id, const struct ieee80211_channel *c, int async) { struct iwn_cmd_link_quality lq; int i, ridx; memset(&lq, 0, sizeof(lq)); lq.id = id; if (IEEE80211_IS_CHAN_HT(c)) { lq.mimo = 1; lq.ssmask = 0x1; } else lq.ssmask = 0x2; if (id == IWN_ID_BSS) ridx = IWN_RATE_OFDM54; else if (IEEE80211_IS_CHAN_A(c)) ridx = IWN_RATE_OFDM6; else ridx = IWN_RATE_CCK1; for (i = 0; i < IWN_MAX_TX_RETRIES; i++) { /* XXX toggle antenna for retry patterns */ if (IEEE80211_IS_CHAN_HT40(c)) { lq.table[i].rate = iwn_mimo_mcs_to_plcp[ridx] | IWN_RATE_MCS; lq.table[i].rflags = IWN_RFLAG_HT | IWN_RFLAG_HT40 | IWN_RFLAG_ANT_A; /* XXX shortGI */ } else if (IEEE80211_IS_CHAN_HT(c)) { lq.table[i].rate = iwn_siso_mcs_to_plcp[ridx] | IWN_RATE_MCS; lq.table[i].rflags = IWN_RFLAG_HT | IWN_RFLAG_ANT_A; /* XXX shortGI */ } else { lq.table[i].rate = iwn_ridx_to_plcp[ridx]; if (ridx <= IWN_RATE_CCK11) lq.table[i].rflags = IWN_RFLAG_CCK; lq.table[i].rflags |= IWN_RFLAG_ANT_B; } ridx = iwn_prev_ridx[ridx]; } lq.dsmask = 0x3; lq.ampdu_disable = 3; lq.ampdu_limit = htole16(4000); #ifdef IWN_DEBUG if (sc->sc_debug & IWN_DEBUG_STATE) { printf("%s: set link quality for node %d, mimo %d ssmask %d\n", __func__, id, lq.mimo, lq.ssmask); printf("%s:", __func__); for (i = 0; i < IWN_MAX_TX_RETRIES; i++) printf(" %d:%x", lq.table[i].rate, lq.table[i].rflags); printf("\n"); } #endif return iwn_cmd(sc, IWN_CMD_TX_LINK_QUALITY, &lq, sizeof(lq), async); } #if 0 /* * Install a pairwise key into the hardware. */ int iwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, const struct ieee80211_key *k) { struct iwn_softc *sc = ic->ic_softc; struct iwn_node_info node; if (k->k_flags & IEEE80211_KEY_GROUP) return 0; memset(&node, 0, sizeof node); switch (k->k_cipher) { case IEEE80211_CIPHER_CCMP: node.security = htole16(IWN_CIPHER_CCMP); memcpy(node.key, k->k_key, k->k_len); break; default: return 0; } node.id = IWN_ID_BSS; IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); node.control = IWN_NODE_UPDATE; node.flags = IWN_FLAG_SET_KEY; return iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); } #endif int iwn_wme_update(struct ieee80211com *ic) { #define IWN_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ #define IWN_TXOP_TO_US(v) (v<<5) struct iwn_softc *sc = ic->ic_ifp->if_softc; struct iwn_edca_params cmd; int i; memset(&cmd, 0, sizeof cmd); cmd.flags = htole32(IWN_EDCA_UPDATE); for (i = 0; i < WME_NUM_AC; i++) { const struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[i]; cmd.ac[i].aifsn = wmep->wmep_aifsn; cmd.ac[i].cwmin = htole16(IWN_EXP2(wmep->wmep_logcwmin)); cmd.ac[i].cwmax = htole16(IWN_EXP2(wmep->wmep_logcwmax)); cmd.ac[i].txoplimit = htole16(IWN_TXOP_TO_US(wmep->wmep_txopLimit)); } IWN_LOCK(sc); (void) iwn_cmd(sc, IWN_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1 /*async*/); IWN_UNLOCK(sc); return 0; #undef IWN_TXOP_TO_US #undef IWN_EXP2 } void iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) { struct iwn_cmd_led led; led.which = which; led.unit = htole32(100000); /* on/off in unit of 100ms */ led.off = off; led.on = on; (void) iwn_cmd(sc, IWN_CMD_SET_LED, &led, sizeof led, 1); } /* * Set the critical temperature at which the firmware will automatically stop * the radio transmitter. */ int iwn_set_critical_temp(struct iwn_softc *sc) { struct iwn_ucode_info *uc = &sc->ucode_info; struct iwn_critical_temp crit; uint32_t r1, r2, r3, temp; r1 = le32toh(uc->temp[0].chan20MHz); r2 = le32toh(uc->temp[1].chan20MHz); r3 = le32toh(uc->temp[2].chan20MHz); /* inverse function of iwn_get_temperature() */ temp = r2 + (IWN_CTOK(110) * (r3 - r1)) / 259; IWN_WRITE(sc, IWN_UCODE_CLR, IWN_CTEMP_STOP_RF); memset(&crit, 0, sizeof crit); crit.tempR = htole32(temp); DPRINTF(sc, IWN_DEBUG_RESET, "setting critical temp to %u\n", temp); return iwn_cmd(sc, IWN_CMD_SET_CRITICAL_TEMP, &crit, sizeof crit, 0); } void iwn_enable_tsf(struct iwn_softc *sc, struct ieee80211_node *ni) { struct iwn_cmd_tsf tsf; uint64_t val, mod; memset(&tsf, 0, sizeof tsf); memcpy(&tsf.tstamp, ni->ni_tstamp.data, sizeof (uint64_t)); tsf.bintval = htole16(ni->ni_intval); tsf.lintval = htole16(10); /* XXX all wrong */ /* compute remaining time until next beacon */ val = (uint64_t)ni->ni_intval * 1024; /* msecs -> usecs */ DPRINTF(sc, IWN_DEBUG_ANY, "%s: val = %ju %s\n", __func__, val, val == 0 ? "correcting" : ""); if (val == 0) val = 1; mod = le64toh(tsf.tstamp) % val; tsf.binitval = htole32((uint32_t)(val - mod)); DPRINTF(sc, IWN_DEBUG_RESET, "TSF bintval=%u tstamp=%ju, init=%u\n", ni->ni_intval, le64toh(tsf.tstamp), (uint32_t)(val - mod)); if (iwn_cmd(sc, IWN_CMD_TSF, &tsf, sizeof tsf, 1) != 0) device_printf(sc->sc_dev, "%s: could not enable TSF\n", __func__); } void iwn_power_calibration(struct iwn_softc *sc, int temp) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; #if 0 KASSERT(ic->ic_state == IEEE80211_S_RUN, ("not running")); #endif DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: temperature %d->%d\n", __func__, sc->temp, temp); /* adjust Tx power if need be (delta >= 3°C) */ if (abs(temp - sc->temp) < 3) return; sc->temp = temp; DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: set Tx power for channel %d\n", __func__, ieee80211_chan2ieee(ic, ic->ic_bsschan)); if (iwn_set_txpower(sc, ic->ic_bsschan, 1) != 0) { /* just warn, too bad for the automatic calibration... */ device_printf(sc->sc_dev, "%s: could not adjust Tx power\n", __func__); } } /* * Set Tx power for a given channel (each rate has its own power settings). * This function takes into account the regulatory information from EEPROM, * the current temperature and the current voltage. */ int iwn_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, int async) { /* fixed-point arithmetic division using a n-bit fractional part */ #define fdivround(a, b, n) \ ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) /* linear interpolation */ #define interpolate(x, x1, y1, x2, y2, n) \ ((y1) + fdivround(((int)(x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) static const int tdiv[IWN_NATTEN_GROUPS] = { 9, 8, 8, 8, 6 }; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_ucode_info *uc = &sc->ucode_info; struct iwn_cmd_txpower cmd; struct iwn_eeprom_chan_samples *chans; const uint8_t *rf_gain, *dsp_gain; int32_t vdiff, tdiff; int i, c, grp, maxpwr; u_int chan; /* get channel number */ chan = ieee80211_chan2ieee(ic, ch); memset(&cmd, 0, sizeof cmd); cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1; cmd.chan = chan; if (IEEE80211_IS_CHAN_5GHZ(ch)) { maxpwr = sc->maxpwr5GHz; rf_gain = iwn_rf_gain_5ghz; dsp_gain = iwn_dsp_gain_5ghz; } else { maxpwr = sc->maxpwr2GHz; rf_gain = iwn_rf_gain_2ghz; dsp_gain = iwn_dsp_gain_2ghz; } /* compute voltage compensation */ vdiff = ((int32_t)le32toh(uc->volt) - sc->eeprom_voltage) / 7; if (vdiff > 0) vdiff *= 2; if (abs(vdiff) > 2) vdiff = 0; DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: voltage compensation=%d (UCODE=%d, EEPROM=%d)\n", __func__, vdiff, le32toh(uc->volt), sc->eeprom_voltage); /* get channel's attenuation group */ if (chan <= 20) /* 1-20 */ grp = 4; else if (chan <= 43) /* 34-43 */ grp = 0; else if (chan <= 70) /* 44-70 */ grp = 1; else if (chan <= 124) /* 71-124 */ grp = 2; else /* 125-200 */ grp = 3; DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: chan %d, attenuation group=%d\n", __func__, chan, grp); /* get channel's sub-band */ for (i = 0; i < IWN_NBANDS; i++) if (sc->bands[i].lo != 0 && sc->bands[i].lo <= chan && chan <= sc->bands[i].hi) break; chans = sc->bands[i].chans; DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: chan %d sub-band=%d\n", __func__, chan, i); for (c = 0; c < IWN_NTXCHAINS; c++) { uint8_t power, gain, temp; int maxchpwr, pwr, ridx, idx; power = interpolate(chan, chans[0].num, chans[0].samples[c][1].power, chans[1].num, chans[1].samples[c][1].power, 1); gain = interpolate(chan, chans[0].num, chans[0].samples[c][1].gain, chans[1].num, chans[1].samples[c][1].gain, 1); temp = interpolate(chan, chans[0].num, chans[0].samples[c][1].temp, chans[1].num, chans[1].samples[c][1].temp, 1); DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: Tx chain %d: power=%d gain=%d temp=%d\n", __func__, c, power, gain, temp); /* compute temperature compensation */ tdiff = ((sc->temp - temp) * 2) / tdiv[grp]; DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: temperature compensation=%d (current=%d, EEPROM=%d)\n", __func__, tdiff, sc->temp, temp); for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) { maxchpwr = ch->ic_maxpower; if ((ridx / 8) & 1) { /* MIMO: decrease Tx power (-3dB) */ maxchpwr -= 6; } pwr = maxpwr - 10; /* decrease power for highest OFDM rates */ if ((ridx % 8) == 5) /* 48Mbit/s */ pwr -= 5; else if ((ridx % 8) == 6) /* 54Mbit/s */ pwr -= 7; else if ((ridx % 8) == 7) /* 60Mbit/s */ pwr -= 10; if (pwr > maxchpwr) pwr = maxchpwr; idx = gain - (pwr - power) - tdiff - vdiff; if ((ridx / 8) & 1) /* MIMO */ idx += (int32_t)le32toh(uc->atten[grp][c]); if (cmd.band == 0) idx += 9; /* 5GHz */ if (ridx == IWN_RIDX_MAX) idx += 5; /* CCK */ /* make sure idx stays in a valid range */ if (idx < 0) idx = 0; else if (idx > IWN_MAX_PWR_INDEX) idx = IWN_MAX_PWR_INDEX; DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: Tx chain %d, rate idx %d: power=%d\n", __func__, c, ridx, idx); cmd.power[ridx].rf_gain[c] = rf_gain[idx]; cmd.power[ridx].dsp_gain[c] = dsp_gain[idx]; } } DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_TXPOW, "%s: set tx power for chan %d\n", __func__, chan); return iwn_cmd(sc, IWN_CMD_TXPOWER, &cmd, sizeof cmd, async); #undef interpolate #undef fdivround } /* * Get the best (maximum) RSSI among the * connected antennas and convert to dBm. */ int8_t iwn_get_rssi(struct iwn_softc *sc, const struct iwn_rx_stat *stat) { int mask, agc, rssi; mask = (le16toh(stat->antenna) >> 4) & 0x7; agc = (le16toh(stat->agc) >> 7) & 0x7f; rssi = 0; #if 0 if (mask & (1 << 0)) /* Ant A */ rssi = max(rssi, stat->rssi[0]); if (mask & (1 << 1)) /* Ant B */ rssi = max(rssi, stat->rssi[2]); if (mask & (1 << 2)) /* Ant C */ rssi = max(rssi, stat->rssi[4]); #else rssi = max(rssi, stat->rssi[0]); rssi = max(rssi, stat->rssi[2]); rssi = max(rssi, stat->rssi[4]); #endif DPRINTF(sc, IWN_DEBUG_RECV, "%s: agc %d mask 0x%x rssi %d %d %d " "result %d\n", __func__, agc, mask, stat->rssi[0], stat->rssi[2], stat->rssi[4], rssi - agc - IWN_RSSI_TO_DBM); return rssi - agc - IWN_RSSI_TO_DBM; } /* * Get the average noise among Rx antennas (in dBm). */ int iwn_get_noise(const struct iwn_rx_general_stats *stats) { int i, total, nbant, noise; total = nbant = 0; for (i = 0; i < 3; i++) { noise = le32toh(stats->noise[i]) & 0xff; if (noise != 0) { total += noise; nbant++; } } /* there should be at least one antenna but check anyway */ return (nbant == 0) ? -127 : (total / nbant) - 107; } /* * Read temperature (in degC) from the on-board thermal sensor. */ int iwn_get_temperature(struct iwn_softc *sc) { struct iwn_ucode_info *uc = &sc->ucode_info; int32_t r1, r2, r3, r4, temp; r1 = le32toh(uc->temp[0].chan20MHz); r2 = le32toh(uc->temp[1].chan20MHz); r3 = le32toh(uc->temp[2].chan20MHz); r4 = le32toh(sc->rawtemp); if (r1 == r3) /* prevents division by 0 (should not happen) */ return 0; /* sign-extend 23-bit R4 value to 32-bit */ r4 = (r4 << 8) >> 8; /* compute temperature */ temp = (259 * (r4 - r2)) / (r3 - r1); temp = (temp * 97) / 100 + 8; return IWN_KTOC(temp); } /* * Initialize sensitivity calibration state machine. */ int iwn_init_sensitivity(struct iwn_softc *sc) { struct iwn_calib_state *calib = &sc->calib; struct iwn_phy_calib_cmd cmd; int error; /* reset calibration state */ memset(calib, 0, sizeof (*calib)); calib->state = IWN_CALIB_STATE_INIT; calib->cck_state = IWN_CCK_STATE_HIFA; /* initial values taken from the reference driver */ calib->corr_ofdm_x1 = 105; calib->corr_ofdm_mrc_x1 = 220; calib->corr_ofdm_x4 = 90; calib->corr_ofdm_mrc_x4 = 170; calib->corr_cck_x4 = 125; calib->corr_cck_mrc_x4 = 200; calib->energy_cck = 100; /* write initial sensitivity values */ error = iwn_send_sensitivity(sc); if (error != 0) return error; memset(&cmd, 0, sizeof cmd); cmd.code = IWN_SET_DIFF_GAIN; /* differential gains initially set to 0 for all 3 antennas */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: calibrate phy\n", __func__); return iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1); } /* * Collect noise and RSSI statistics for the first 20 beacons received * after association and use them to determine connected antennas and * set differential gains. */ void iwn_compute_differential_gain(struct iwn_softc *sc, const struct iwn_rx_general_stats *stats) { struct iwn_calib_state *calib = &sc->calib; struct iwn_phy_calib_cmd cmd; int i, val; /* accumulate RSSI and noise for all 3 antennas */ for (i = 0; i < 3; i++) { calib->rssi[i] += le32toh(stats->rssi[i]) & 0xff; calib->noise[i] += le32toh(stats->noise[i]) & 0xff; } /* we update differential gain only once after 20 beacons */ if (++calib->nbeacons < 20) return; /* determine antenna with highest average RSSI */ val = max(calib->rssi[0], calib->rssi[1]); val = max(calib->rssi[2], val); /* determine which antennas are connected */ sc->antmsk = 0; for (i = 0; i < 3; i++) if (val - calib->rssi[i] <= 15 * 20) sc->antmsk |= 1 << i; /* if neither Ant A and Ant B are connected.. */ if ((sc->antmsk & (1 << 0 | 1 << 1)) == 0) sc->antmsk |= 1 << 1; /* ..mark Ant B as connected! */ /* get minimal noise among connected antennas */ val = INT_MAX; /* ok, there's at least one */ for (i = 0; i < 3; i++) if (sc->antmsk & (1 << i)) val = min(calib->noise[i], val); memset(&cmd, 0, sizeof cmd); cmd.code = IWN_SET_DIFF_GAIN; /* set differential gains for connected antennas */ for (i = 0; i < 3; i++) { if (sc->antmsk & (1 << i)) { cmd.gain[i] = (calib->noise[i] - val) / 30; /* limit differential gain to 3 */ cmd.gain[i] = min(cmd.gain[i], 3); cmd.gain[i] |= IWN_GAIN_SET; } } DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: set differential gains Ant A/B/C: %x/%x/%x (%x)\n", __func__,cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->antmsk); if (iwn_cmd(sc, IWN_PHY_CALIB, &cmd, sizeof cmd, 1) == 0) calib->state = IWN_CALIB_STATE_RUN; } /* * Tune RF Rx sensitivity based on the number of false alarms detected * during the last beacon period. */ void iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) { #define inc_clip(val, inc, max) \ if ((val) < (max)) { \ if ((val) < (max) - (inc)) \ (val) += (inc); \ else \ (val) = (max); \ needs_update = 1; \ } #define dec_clip(val, dec, min) \ if ((val) > (min)) { \ if ((val) > (min) + (dec)) \ (val) -= (dec); \ else \ (val) = (min); \ needs_update = 1; \ } struct iwn_calib_state *calib = &sc->calib; uint32_t val, rxena, fa; uint32_t energy[3], energy_min; uint8_t noise[3], noise_ref; int i, needs_update = 0; /* check that we've been enabled long enough */ if ((rxena = le32toh(stats->general.load)) == 0) return; /* compute number of false alarms since last call for OFDM */ fa = le32toh(stats->ofdm.bad_plcp) - calib->bad_plcp_ofdm; fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm; fa *= 200 * 1024; /* 200TU */ /* save counters values for next call */ calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp); calib->fa_ofdm = le32toh(stats->ofdm.fa); if (fa > 50 * rxena) { /* high false alarm count, decrease sensitivity */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: OFDM high false alarm count: %u\n", __func__, fa); inc_clip(calib->corr_ofdm_x1, 1, 140); inc_clip(calib->corr_ofdm_mrc_x1, 1, 270); inc_clip(calib->corr_ofdm_x4, 1, 120); inc_clip(calib->corr_ofdm_mrc_x4, 1, 210); } else if (fa < 5 * rxena) { /* low false alarm count, increase sensitivity */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: OFDM low false alarm count: %u\n", __func__, fa); dec_clip(calib->corr_ofdm_x1, 1, 105); dec_clip(calib->corr_ofdm_mrc_x1, 1, 220); dec_clip(calib->corr_ofdm_x4, 1, 85); dec_clip(calib->corr_ofdm_mrc_x4, 1, 170); } /* compute maximum noise among 3 antennas */ for (i = 0; i < 3; i++) noise[i] = (le32toh(stats->general.noise[i]) >> 8) & 0xff; val = max(noise[0], noise[1]); val = max(noise[2], val); /* insert it into our samples table */ calib->noise_samples[calib->cur_noise_sample] = val; calib->cur_noise_sample = (calib->cur_noise_sample + 1) % 20; /* compute maximum noise among last 20 samples */ noise_ref = calib->noise_samples[0]; for (i = 1; i < 20; i++) noise_ref = max(noise_ref, calib->noise_samples[i]); /* compute maximum energy among 3 antennas */ for (i = 0; i < 3; i++) energy[i] = le32toh(stats->general.energy[i]); val = min(energy[0], energy[1]); val = min(energy[2], val); /* insert it into our samples table */ calib->energy_samples[calib->cur_energy_sample] = val; calib->cur_energy_sample = (calib->cur_energy_sample + 1) % 10; /* compute minimum energy among last 10 samples */ energy_min = calib->energy_samples[0]; for (i = 1; i < 10; i++) energy_min = max(energy_min, calib->energy_samples[i]); energy_min += 6; /* compute number of false alarms since last call for CCK */ fa = le32toh(stats->cck.bad_plcp) - calib->bad_plcp_cck; fa += le32toh(stats->cck.fa) - calib->fa_cck; fa *= 200 * 1024; /* 200TU */ /* save counters values for next call */ calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp); calib->fa_cck = le32toh(stats->cck.fa); if (fa > 50 * rxena) { /* high false alarm count, decrease sensitivity */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: CCK high false alarm count: %u\n", __func__, fa); calib->cck_state = IWN_CCK_STATE_HIFA; calib->low_fa = 0; if (calib->corr_cck_x4 > 160) { calib->noise_ref = noise_ref; if (calib->energy_cck > 2) dec_clip(calib->energy_cck, 2, energy_min); } if (calib->corr_cck_x4 < 160) { calib->corr_cck_x4 = 161; needs_update = 1; } else inc_clip(calib->corr_cck_x4, 3, 200); inc_clip(calib->corr_cck_mrc_x4, 3, 400); } else if (fa < 5 * rxena) { /* low false alarm count, increase sensitivity */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: CCK low false alarm count: %u\n", __func__, fa); calib->cck_state = IWN_CCK_STATE_LOFA; calib->low_fa++; if (calib->cck_state != 0 && ((calib->noise_ref - noise_ref) > 2 || calib->low_fa > 100)) { inc_clip(calib->energy_cck, 2, 97); dec_clip(calib->corr_cck_x4, 3, 125); dec_clip(calib->corr_cck_mrc_x4, 3, 200); } } else { /* not worth to increase or decrease sensitivity */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: CCK normal false alarm count: %u\n", __func__, fa); calib->low_fa = 0; calib->noise_ref = noise_ref; if (calib->cck_state == IWN_CCK_STATE_HIFA) { /* previous interval had many false alarms */ dec_clip(calib->energy_cck, 8, energy_min); } calib->cck_state = IWN_CCK_STATE_INIT; } if (needs_update) (void)iwn_send_sensitivity(sc); #undef dec_clip #undef inc_clip } int iwn_send_sensitivity(struct iwn_softc *sc) { struct iwn_calib_state *calib = &sc->calib; struct iwn_sensitivity_cmd cmd; memset(&cmd, 0, sizeof cmd); cmd.which = IWN_SENSITIVITY_WORKTBL; /* OFDM modulation */ cmd.corr_ofdm_x1 = htole16(calib->corr_ofdm_x1); cmd.corr_ofdm_mrc_x1 = htole16(calib->corr_ofdm_mrc_x1); cmd.corr_ofdm_x4 = htole16(calib->corr_ofdm_x4); cmd.corr_ofdm_mrc_x4 = htole16(calib->corr_ofdm_mrc_x4); cmd.energy_ofdm = htole16(100); cmd.energy_ofdm_th = htole16(62); /* CCK modulation */ cmd.corr_cck_x4 = htole16(calib->corr_cck_x4); cmd.corr_cck_mrc_x4 = htole16(calib->corr_cck_mrc_x4); cmd.energy_cck = htole16(calib->energy_cck); /* Barker modulation: use default values */ cmd.corr_barker = htole16(190); cmd.corr_barker_mrc = htole16(390); DPRINTF(sc, IWN_DEBUG_RESET, "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__, calib->corr_ofdm_x1, calib->corr_ofdm_mrc_x1, calib->corr_ofdm_x4, calib->corr_ofdm_mrc_x4, calib->corr_cck_x4, calib->corr_cck_mrc_x4, calib->energy_cck); return iwn_cmd(sc, IWN_SENSITIVITY, &cmd, sizeof cmd, 1); } int iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni = vap->iv_bss; struct iwn_node_info node; int error; sc->calib.state = IWN_CALIB_STATE_INIT; /* update adapter's configuration */ sc->config.associd = 0; IEEE80211_ADDR_COPY(sc->config.bssid, ni->ni_bssid); sc->config.chan = htole16(ieee80211_chan2ieee(ic, ni->ni_chan)); sc->config.flags = htole32(IWN_CONFIG_TSF); if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) sc->config.flags |= htole32(IWN_CONFIG_AUTO | IWN_CONFIG_24GHZ); if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { sc->config.cck_mask = 0; sc->config.ofdm_mask = 0x15; } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { sc->config.cck_mask = 0x03; sc->config.ofdm_mask = 0; } else { /* XXX assume 802.11b/g */ sc->config.cck_mask = 0x0f; sc->config.ofdm_mask = 0x15; } if (ic->ic_flags & IEEE80211_F_SHSLOT) sc->config.flags |= htole32(IWN_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) sc->config.flags |= htole32(IWN_CONFIG_SHPREAMBLE); sc->config.filter &= ~htole32(IWN_FILTER_BSS); DPRINTF(sc, IWN_DEBUG_STATE, "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", __func__, le16toh(sc->config.chan), sc->config.mode, le32toh(sc->config.flags), sc->config.cck_mask, sc->config.ofdm_mask, sc->config.ht_single_mask, sc->config.ht_dual_mask, le16toh(sc->config.rxchain), sc->config.myaddr, ":", sc->config.wlap, ":", sc->config.bssid, ":", le16toh(sc->config.associd), le32toh(sc->config.filter)); error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, sizeof (struct iwn_config), 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not configure, error %d\n", __func__, error); return error; } sc->sc_curchan = ic->ic_curchan; /* configuration has changed, set Tx power accordingly */ error = iwn_set_txpower(sc, ni->ni_chan, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set Tx power, error %d\n", __func__, error); return error; } /* * Reconfiguring clears the adapter's nodes table so we must * add the broadcast node again. */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr); node.id = IWN_ID_BROADCAST; DPRINTF(sc, IWN_DEBUG_STATE, "%s: add broadcast node\n", __func__); error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not add broadcast node, error %d\n", __func__, error); return error; } error = iwn_set_link_quality(sc, node.id, ic->ic_curchan, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not setup MRR for broadcast node, error %d\n", __func__, error); return error; } return 0; } /* * Configure the adapter for associated state. */ int iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) { #define MS(v,x) (((v) & x) >> x##_S) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni = vap->iv_bss; struct iwn_node_info node; int error, maxrxampdu, ampdudensity; sc->calib.state = IWN_CALIB_STATE_INIT; if (ic->ic_opmode == IEEE80211_M_MONITOR) { /* link LED blinks while monitoring */ iwn_set_led(sc, IWN_LED_LINK, 5, 5); return 0; } iwn_enable_tsf(sc, ni); /* update adapter's configuration */ sc->config.associd = htole16(IEEE80211_AID(ni->ni_associd)); /* short preamble/slot time are negotiated when associating */ sc->config.flags &= ~htole32(IWN_CONFIG_SHPREAMBLE | IWN_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHSLOT) sc->config.flags |= htole32(IWN_CONFIG_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) sc->config.flags |= htole32(IWN_CONFIG_SHPREAMBLE); if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { sc->config.flags &= ~htole32(IWN_CONFIG_HT); if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) sc->config.flags |= htole32(IWN_CONFIG_HT40U); else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) sc->config.flags |= htole32(IWN_CONFIG_HT40D); else sc->config.flags |= htole32(IWN_CONFIG_HT20); sc->config.rxchain = htole16( (3 << IWN_RXCHAIN_VALID_S) | (3 << IWN_RXCHAIN_MIMO_CNT_S) | (1 << IWN_RXCHAIN_CNT_S) | IWN_RXCHAIN_MIMO_FORCE); maxrxampdu = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); ampdudensity = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); } else maxrxampdu = ampdudensity = 0; sc->config.filter |= htole32(IWN_FILTER_BSS); DPRINTF(sc, IWN_DEBUG_STATE, "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", __func__, le16toh(sc->config.chan), sc->config.mode, le32toh(sc->config.flags), sc->config.cck_mask, sc->config.ofdm_mask, sc->config.ht_single_mask, sc->config.ht_dual_mask, le16toh(sc->config.rxchain), sc->config.myaddr, ":", sc->config.wlap, ":", sc->config.bssid, ":", le16toh(sc->config.associd), le32toh(sc->config.filter)); error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, sizeof (struct iwn_config), 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not update configuration, error %d\n", __func__, error); return error; } sc->sc_curchan = ni->ni_chan; /* configuration has changed, set Tx power accordingly */ error = iwn_set_txpower(sc, ni->ni_chan, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set Tx power, error %d\n", __func__, error); return error; } /* add BSS node */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); node.id = IWN_ID_BSS; node.htflags = htole32( (maxrxampdu << IWN_MAXRXAMPDU_S) | (ampdudensity << IWN_MPDUDENSITY_S)); DPRINTF(sc, IWN_DEBUG_STATE, "%s: add BSS node, id %d htflags 0x%x\n", __func__, node.id, le32toh(node.htflags)); error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) { device_printf(sc->sc_dev,"could not add BSS node\n"); return error; } error = iwn_set_link_quality(sc, node.id, ni->ni_chan, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not setup MRR for node %d, error %d\n", __func__, node.id, error); return error; } error = iwn_init_sensitivity(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set sensitivity, error %d\n", __func__, error); return error; } /* start/restart periodic calibration timer */ sc->calib.state = IWN_CALIB_STATE_ASSOC; iwn_calib_reset(sc); /* link LED always on while associated */ iwn_set_led(sc, IWN_LED_LINK, 0, 1); return 0; #undef MS } /* * Send a scan request to the firmware. Since this command is huge, we map it * into a mbuf instead of using the pre-allocated set of commands. */ int iwn_scan(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_scan_state *ss = ic->ic_scan; /*XXX*/ struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_desc *desc; struct iwn_tx_data *data; struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct iwn_scan_hdr *hdr; struct iwn_scan_essid *essid; struct iwn_scan_chan *chan; struct ieee80211_frame *wh; struct ieee80211_rateset *rs; struct ieee80211_channel *c; enum ieee80211_phymode mode; uint8_t *frm; int pktlen, error, nrates; bus_addr_t physaddr; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; /* XXX malloc */ data->m = m_getcl(M_DONTWAIT, MT_DATA, 0); if (data->m == NULL) { device_printf(sc->sc_dev, "%s: could not allocate mbuf for scan command\n", __func__); return ENOMEM; } cmd = mtod(data->m, struct iwn_tx_cmd *); cmd->code = IWN_CMD_SCAN; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; hdr = (struct iwn_scan_hdr *)cmd->data; memset(hdr, 0, sizeof (struct iwn_scan_hdr)); /* XXX use scan state */ /* * Move to the next channel if no packets are received within 5 msecs * after sending the probe request (this helps to reduce the duration * of active scans). */ hdr->quiet = htole16(5); /* timeout in milliseconds */ hdr->plcp_threshold = htole16(1); /* min # of packets */ /* select Ant B and Ant C for scanning */ hdr->rxchain = htole16(0x3e1 | (7 << IWN_RXCHAIN_VALID_S)); tx = (struct iwn_cmd_data *)(hdr + 1); memset(tx, 0, sizeof (struct iwn_cmd_data)); tx->flags = htole32(IWN_TX_AUTO_SEQ | 0x200); /* XXX */ tx->id = IWN_ID_BROADCAST; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); tx->rflags = IWN_RFLAG_ANT_B; if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { hdr->crc_threshold = htole16(1); /* send probe requests at 6Mbps */ tx->rate = iwn_ridx_to_plcp[IWN_RATE_OFDM6]; } else { hdr->flags = htole32(IWN_CONFIG_24GHZ | IWN_CONFIG_AUTO); /* send probe requests at 1Mbps */ tx->rate = iwn_ridx_to_plcp[IWN_RATE_CCK1]; tx->rflags |= IWN_RFLAG_CCK; } essid = (struct iwn_scan_essid *)(tx + 1); memset(essid, 0, 4 * sizeof (struct iwn_scan_essid)); essid[0].id = IEEE80211_ELEMID_SSID; essid[0].len = ss->ss_ssid[0].len; memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); /* * Build a probe request frame. Most of the following code is a * copy & paste of what is done in net80211. */ wh = (struct ieee80211_frame *)&essid[4]; wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp)); IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); *(u_int16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ *(u_int16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ frm = (uint8_t *)(wh + 1); /* add SSID IE */ *frm++ = IEEE80211_ELEMID_SSID; *frm++ = ss->ss_ssid[0].len; memcpy(frm, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); frm += ss->ss_ssid[0].len; mode = ieee80211_chan2mode(ic->ic_curchan); rs = &ic->ic_sup_rates[mode]; /* add supported rates IE */ *frm++ = IEEE80211_ELEMID_RATES; nrates = rs->rs_nrates; if (nrates > IEEE80211_RATE_SIZE) nrates = IEEE80211_RATE_SIZE; *frm++ = nrates; memcpy(frm, rs->rs_rates, nrates); frm += nrates; /* add supported xrates IE */ if (rs->rs_nrates > IEEE80211_RATE_SIZE) { nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; *frm++ = IEEE80211_ELEMID_XRATES; *frm++ = (uint8_t)nrates; memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); frm += nrates; } /* setup length of probe request */ tx->len = htole16(frm - (uint8_t *)wh); c = ic->ic_curchan; chan = (struct iwn_scan_chan *)frm; chan->chan = ieee80211_chan2ieee(ic, c); chan->flags = 0; if ((c->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { chan->flags |= IWN_CHAN_ACTIVE; if (ss->ss_nssid > 0) chan->flags |= IWN_CHAN_DIRECT; } chan->dsp_gain = 0x6e; if (IEEE80211_IS_CHAN_5GHZ(c)) { chan->rf_gain = 0x3b; chan->active = htole16(10); chan->passive = htole16(110); } else { chan->rf_gain = 0x28; chan->active = htole16(20); chan->passive = htole16(120); } DPRINTF(sc, IWN_DEBUG_STATE, "%s: chan %u flags 0x%x rf_gain 0x%x " "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__, chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain, chan->active, chan->passive); hdr->nchan++; chan++; frm += sizeof (struct iwn_scan_chan); hdr->len = htole16(frm - (uint8_t *)hdr); pktlen = frm - (uint8_t *)cmd; error = bus_dmamap_load(ring->data_dmat, data->map, cmd, pktlen, iwn_dma_map_addr, &physaddr, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: could not map scan command, error %d\n", __func__, error); m_freem(data->m); data->m = NULL; return error; } IWN_SET_DESC_NSEGS(desc, 1); IWN_SET_DESC_SEG(desc, 0, physaddr, pktlen); sc->shared->len[ring->qid][ring->cur] = htole16(8); if (ring->cur < IWN_TX_WINDOW) sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = htole16(8); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); /* kick cmd ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); return 0; /* will be notified async. of failure/success */ } int iwn_config(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct iwn_power power; struct iwn_bluetooth bluetooth; struct iwn_node_info node; int error; /* set power mode */ memset(&power, 0, sizeof power); power.flags = htole16(IWN_POWER_CAM | 0x8); DPRINTF(sc, IWN_DEBUG_RESET, "%s: set power mode\n", __func__); error = iwn_cmd(sc, IWN_CMD_SET_POWER_MODE, &power, sizeof power, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set power mode, error %d\n", __func__, error); return error; } /* configure bluetooth coexistence */ memset(&bluetooth, 0, sizeof bluetooth); bluetooth.flags = 3; bluetooth.lead = 0xaa; bluetooth.kill = 1; DPRINTF(sc, IWN_DEBUG_RESET, "%s: config bluetooth coexistence\n", __func__); error = iwn_cmd(sc, IWN_CMD_BLUETOOTH, &bluetooth, sizeof bluetooth, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not configure bluetooth coexistence, error %d\n", __func__, error); return error; } /* configure adapter */ memset(&sc->config, 0, sizeof (struct iwn_config)); IEEE80211_ADDR_COPY(sc->config.myaddr, IF_LLADDR(ifp)); IEEE80211_ADDR_COPY(sc->config.wlap, IF_LLADDR(ifp)); /* set default channel */ sc->config.chan = htole16(ieee80211_chan2ieee(ic, ic->ic_curchan)); sc->config.flags = htole32(IWN_CONFIG_TSF); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) sc->config.flags |= htole32(IWN_CONFIG_AUTO | IWN_CONFIG_24GHZ); sc->config.filter = 0; switch (ic->ic_opmode) { case IEEE80211_M_STA: sc->config.mode = IWN_MODE_STA; sc->config.filter |= htole32(IWN_FILTER_MULTICAST); break; case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: sc->config.mode = IWN_MODE_IBSS; break; case IEEE80211_M_HOSTAP: sc->config.mode = IWN_MODE_HOSTAP; break; case IEEE80211_M_MONITOR: sc->config.mode = IWN_MODE_MONITOR; sc->config.filter |= htole32(IWN_FILTER_MULTICAST | IWN_FILTER_CTL | IWN_FILTER_PROMISC); break; default: break; } sc->config.cck_mask = 0x0f; /* not yet negotiated */ sc->config.ofdm_mask = 0xff; /* not yet negotiated */ sc->config.ht_single_mask = 0xff; sc->config.ht_dual_mask = 0xff; sc->config.rxchain = htole16(0x2800 | (7 << IWN_RXCHAIN_VALID_S)); DPRINTF(sc, IWN_DEBUG_STATE, "%s: config chan %d mode %d flags 0x%x cck 0x%x ofdm 0x%x " "ht_single 0x%x ht_dual 0x%x rxchain 0x%x " "myaddr %6D wlap %6D bssid %6D associd %d filter 0x%x\n", __func__, le16toh(sc->config.chan), sc->config.mode, le32toh(sc->config.flags), sc->config.cck_mask, sc->config.ofdm_mask, sc->config.ht_single_mask, sc->config.ht_dual_mask, le16toh(sc->config.rxchain), sc->config.myaddr, ":", sc->config.wlap, ":", sc->config.bssid, ":", le16toh(sc->config.associd), le32toh(sc->config.filter)); error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->config, sizeof (struct iwn_config), 0); if (error != 0) { device_printf(sc->sc_dev, "%s: configure command failed, error %d\n", __func__, error); return error; } sc->sc_curchan = ic->ic_curchan; /* configuration has changed, set Tx power accordingly */ error = iwn_set_txpower(sc, ic->ic_curchan, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set Tx power, error %d\n", __func__, error); return error; } /* add broadcast node */ memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ic->ic_ifp->if_broadcastaddr); node.id = IWN_ID_BROADCAST; node.rate = iwn_plcp_signal(2); DPRINTF(sc, IWN_DEBUG_RESET, "%s: add broadcast node\n", __func__); error = iwn_cmd(sc, IWN_CMD_ADD_NODE, &node, sizeof node, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not add broadcast node, error %d\n", __func__, error); return error; } error = iwn_set_link_quality(sc, node.id, ic->ic_curchan, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: could not setup MRR for node %d, error %d\n", __func__, node.id, error); return error; } error = iwn_set_critical_temp(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set critical temperature, error %d\n", __func__, error); return error; } return 0; } /* * Do post-alive initialization of the NIC (after firmware upload). */ void iwn_post_alive(struct iwn_softc *sc) { uint32_t base; uint16_t offset; int qid; iwn_mem_lock(sc); /* clear SRAM */ base = iwn_mem_read(sc, IWN_SRAM_BASE); for (offset = 0x380; offset < 0x520; offset += 4) { IWN_WRITE(sc, IWN_MEM_WADDR, base + offset); IWN_WRITE(sc, IWN_MEM_WDATA, 0); } /* shared area is aligned on a 1K boundary */ iwn_mem_write(sc, IWN_SRAM_BASE, sc->shared_dma.paddr >> 10); iwn_mem_write(sc, IWN_SELECT_QCHAIN, 0); for (qid = 0; qid < IWN_NTXQUEUES; qid++) { iwn_mem_write(sc, IWN_QUEUE_RIDX(qid), 0); IWN_WRITE(sc, IWN_TX_WIDX, qid << 8 | 0); /* set sched. window size */ IWN_WRITE(sc, IWN_MEM_WADDR, base + IWN_QUEUE_OFFSET(qid)); IWN_WRITE(sc, IWN_MEM_WDATA, 64); /* set sched. frame limit */ IWN_WRITE(sc, IWN_MEM_WADDR, base + IWN_QUEUE_OFFSET(qid) + 4); IWN_WRITE(sc, IWN_MEM_WDATA, 10 << 16); } /* enable interrupts for all 16 queues */ iwn_mem_write(sc, IWN_QUEUE_INTR_MASK, 0xffff); /* identify active Tx rings (0-7) */ iwn_mem_write(sc, IWN_TX_ACTIVE, 0xff); /* mark Tx rings (4 EDCA + cmd + 2 HCCA) as active */ for (qid = 0; qid < 7; qid++) { iwn_mem_write(sc, IWN_TXQ_STATUS(qid), IWN_TXQ_STATUS_ACTIVE | qid << 1); } iwn_mem_unlock(sc); } void iwn_stop_master(struct iwn_softc *sc) { uint32_t tmp; int ntries; tmp = IWN_READ(sc, IWN_RESET); IWN_WRITE(sc, IWN_RESET, tmp | IWN_STOP_MASTER); tmp = IWN_READ(sc, IWN_GPIO_CTL); if ((tmp & IWN_GPIO_PWR_STATUS) == IWN_GPIO_PWR_SLEEP) return; /* already asleep */ for (ntries = 0; ntries < 100; ntries++) { if (IWN_READ(sc, IWN_RESET) & IWN_MASTER_DISABLED) break; DELAY(10); } if (ntries == 100) device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__); } int iwn_reset(struct iwn_softc *sc) { uint32_t tmp; int ntries; /* clear any pending interrupts */ IWN_WRITE(sc, IWN_INTR, 0xffffffff); tmp = IWN_READ(sc, IWN_CHICKEN); IWN_WRITE(sc, IWN_CHICKEN, tmp | IWN_CHICKEN_DISLOS); tmp = IWN_READ(sc, IWN_GPIO_CTL); IWN_WRITE(sc, IWN_GPIO_CTL, tmp | IWN_GPIO_INIT); /* wait for clock stabilization */ for (ntries = 0; ntries < 1000; ntries++) { if (IWN_READ(sc, IWN_GPIO_CTL) & IWN_GPIO_CLOCK) break; DELAY(10); } if (ntries == 1000) { device_printf(sc->sc_dev, "%s: timeout waiting for clock stabilization\n", __func__); return ETIMEDOUT; } return 0; } void iwn_hw_config(struct iwn_softc *sc) { uint32_t tmp, hw; /* enable interrupts mitigation */ IWN_WRITE(sc, IWN_INTR_MIT, 512 / 32); /* voodoo from the reference driver */ tmp = pci_read_config(sc->sc_dev, PCIR_REVID,1); if ((tmp & 0x80) && (tmp & 0x7f) < 8) { /* enable "no snoop" field */ tmp = pci_read_config(sc->sc_dev, 0xe8, 1); tmp &= ~IWN_DIS_NOSNOOP; /* clear device specific PCI configuration register 0x41 */ pci_write_config(sc->sc_dev, 0xe8, tmp, 1); } /* disable L1 entry to work around a hardware bug */ tmp = pci_read_config(sc->sc_dev, 0xf0, 1); tmp &= ~IWN_ENA_L1; pci_write_config(sc->sc_dev, 0xf0, tmp, 1 ); hw = IWN_READ(sc, IWN_HWCONFIG); IWN_WRITE(sc, IWN_HWCONFIG, hw | 0x310); iwn_mem_lock(sc); tmp = iwn_mem_read(sc, IWN_MEM_POWER); iwn_mem_write(sc, IWN_MEM_POWER, tmp | IWN_POWER_RESET); DELAY(5); tmp = iwn_mem_read(sc, IWN_MEM_POWER); iwn_mem_write(sc, IWN_MEM_POWER, tmp & ~IWN_POWER_RESET); iwn_mem_unlock(sc); } void iwn_init_locked(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; int error, qid; IWN_LOCK_ASSERT(sc); /* load the firmware */ if (sc->fw_fp == NULL && (error = iwn_load_firmware(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not load firmware, error %d\n", __func__, error); return; } error = iwn_reset(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not reset adapter, error %d\n", __func__, error); return; } iwn_mem_lock(sc); iwn_mem_read(sc, IWN_CLOCK_CTL); iwn_mem_write(sc, IWN_CLOCK_CTL, 0xa00); iwn_mem_read(sc, IWN_CLOCK_CTL); iwn_mem_unlock(sc); DELAY(20); iwn_mem_lock(sc); tmp = iwn_mem_read(sc, IWN_MEM_PCIDEV); iwn_mem_write(sc, IWN_MEM_PCIDEV, tmp | 0x800); iwn_mem_unlock(sc); iwn_mem_lock(sc); tmp = iwn_mem_read(sc, IWN_MEM_POWER); iwn_mem_write(sc, IWN_MEM_POWER, tmp & ~0x03000000); iwn_mem_unlock(sc); iwn_hw_config(sc); /* init Rx ring */ iwn_mem_lock(sc); IWN_WRITE(sc, IWN_RX_CONFIG, 0); IWN_WRITE(sc, IWN_RX_WIDX, 0); /* Rx ring is aligned on a 256-byte boundary */ IWN_WRITE(sc, IWN_RX_BASE, sc->rxq.desc_dma.paddr >> 8); /* shared area is aligned on a 16-byte boundary */ IWN_WRITE(sc, IWN_RW_WIDX_PTR, (sc->shared_dma.paddr + offsetof(struct iwn_shared, closed_count)) >> 4); IWN_WRITE(sc, IWN_RX_CONFIG, 0x80601000); iwn_mem_unlock(sc); IWN_WRITE(sc, IWN_RX_WIDX, (IWN_RX_RING_COUNT - 1) & ~7); iwn_mem_lock(sc); iwn_mem_write(sc, IWN_TX_ACTIVE, 0); /* set physical address of "keep warm" page */ IWN_WRITE(sc, IWN_KW_BASE, sc->kw_dma.paddr >> 4); /* init Tx rings */ for (qid = 0; qid < IWN_NTXQUEUES; qid++) { struct iwn_tx_ring *txq = &sc->txq[qid]; IWN_WRITE(sc, IWN_TX_BASE(qid), txq->desc_dma.paddr >> 8); IWN_WRITE(sc, IWN_TX_CONFIG(qid), 0x80000008); } iwn_mem_unlock(sc); /* clear "radio off" and "disable command" bits (reversed logic) */ IWN_WRITE(sc, IWN_UCODE_CLR, IWN_RADIO_OFF); IWN_WRITE(sc, IWN_UCODE_CLR, IWN_DISABLE_CMD); /* clear any pending interrupts */ IWN_WRITE(sc, IWN_INTR, 0xffffffff); /* enable interrupts */ IWN_WRITE(sc, IWN_MASK, IWN_INTR_MASK); /* not sure why/if this is necessary... */ IWN_WRITE(sc, IWN_UCODE_CLR, IWN_RADIO_OFF); IWN_WRITE(sc, IWN_UCODE_CLR, IWN_RADIO_OFF); /* check that the radio is not disabled by RF switch */ if (!(IWN_READ(sc, IWN_GPIO_CTL) & IWN_GPIO_RF_ENABLED)) { device_printf(sc->sc_dev, "radio is disabled by hardware switch\n"); return; } error = iwn_transfer_firmware(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not load firmware, error %d\n", __func__, error); return; } /* firmware has notified us that it is alive.. */ iwn_post_alive(sc); /* ..do post alive initialization */ sc->rawtemp = sc->ucode_info.temp[3].chan20MHz; sc->temp = iwn_get_temperature(sc); DPRINTF(sc, IWN_DEBUG_RESET, "%s: temperature=%d\n", __func__, sc->temp); error = iwn_config(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not configure device, error %d\n", __func__, error); return; } ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; } void iwn_init(void *arg) { struct iwn_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; IWN_LOCK(sc); iwn_init_locked(sc); IWN_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); } void iwn_stop_locked(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; int i; IWN_LOCK_ASSERT(sc); IWN_WRITE(sc, IWN_RESET, IWN_NEVO_RESET); sc->sc_tx_timer = 0; callout_stop(&sc->sc_timer_to); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); /* disable interrupts */ IWN_WRITE(sc, IWN_MASK, 0); IWN_WRITE(sc, IWN_INTR, 0xffffffff); IWN_WRITE(sc, IWN_INTR_STATUS, 0xffffffff); /* reset all Tx rings */ for (i = 0; i < IWN_NTXQUEUES; i++) iwn_reset_tx_ring(sc, &sc->txq[i]); /* reset Rx ring */ iwn_reset_rx_ring(sc, &sc->rxq); iwn_mem_lock(sc); iwn_mem_write(sc, IWN_MEM_CLOCK2, 0x200); iwn_mem_unlock(sc); DELAY(5); iwn_stop_master(sc); tmp = IWN_READ(sc, IWN_RESET); IWN_WRITE(sc, IWN_RESET, tmp | IWN_SW_RESET); } void iwn_stop(struct iwn_softc *sc) { IWN_LOCK(sc); iwn_stop_locked(sc); IWN_UNLOCK(sc); } /* * Callback from net80211 to start a scan. */ static void iwn_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; IWN_LOCK(sc); /* make the link LED blink while we're scanning */ iwn_set_led(sc, IWN_LED_LINK, 20, 2); IWN_UNLOCK(sc); } /* * Callback from net80211 to terminate a scan. */ static void iwn_scan_end(struct ieee80211com *ic) { /* ignore */ } /* * Callback from net80211 to force a channel change. */ static void iwn_set_channel(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; struct ieee80211vap *vap; const struct ieee80211_channel *c = ic->ic_curchan; int error; vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ IWN_LOCK(sc); if (c != sc->sc_curchan) { sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); error = iwn_config(sc); if (error != 0) { DPRINTF(sc, IWN_DEBUG_STATE, "%s: set chan failed, cancel scan\n", __func__); //XXX Handle failed scan correctly ieee80211_cancel_scan(vap); } } IWN_UNLOCK(sc); } /* * Callback from net80211 to start scanning of the current channel. */ static void iwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { struct ieee80211vap *vap = ss->ss_vap; struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc; int error; IWN_LOCK(sc); error = iwn_scan(sc); IWN_UNLOCK(sc); if (error != 0) ieee80211_cancel_scan(vap); } /* * Callback from net80211 to handle the minimum dwell time being met. * The intent is to terminate the scan but we just let the firmware * notify us when it's finished as we have no safe way to abort it. */ static void iwn_scan_mindwell(struct ieee80211_scan_state *ss) { /* NB: don't try to abort scan; wait for firmware to finish */ } static void iwn_hwreset(void *arg0, int pending) { struct iwn_softc *sc = arg0; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; iwn_init(sc); ieee80211_notify_radio(ic, 1); } static void iwn_radioon(void *arg0, int pending) { struct iwn_softc *sc = arg0; iwn_init(sc); } static void iwn_radiooff(void *arg0, int pending) { struct iwn_softc *sc = arg0; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; IWN_LOCK(sc); ieee80211_notify_radio(ic, 0); iwn_stop_locked(sc); IWN_UNLOCK(sc); } static void iwn_sysctlattach(struct iwn_softc *sc) { struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); #ifdef IWN_DEBUG sc->sc_debug = 0; SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs"); #endif } #ifdef IWN_DEBUG static const char * iwn_intr_str(uint8_t cmd) { switch (cmd) { /* Notifications */ case IWN_UC_READY: return "UC_READY"; case IWN_ADD_NODE_DONE: return "ADD_NODE_DONE"; case IWN_TX_DONE: return "TX_DONE"; case IWN_START_SCAN: return "START_SCAN"; case IWN_STOP_SCAN: return "STOP_SCAN"; case IWN_RX_STATISTICS: return "RX_STATS"; case IWN_BEACON_STATISTICS: return "BEACON_STATS"; case IWN_STATE_CHANGED: return "STATE_CHANGED"; case IWN_BEACON_MISSED: return "BEACON_MISSED"; case IWN_AMPDU_RX_START: return "AMPDU_RX_START"; case IWN_AMPDU_RX_DONE: return "AMPDU_RX_DONE"; case IWN_RX_DONE: return "RX_DONE"; /* Command Notifications */ case IWN_CMD_CONFIGURE: return "IWN_CMD_CONFIGURE"; case IWN_CMD_ASSOCIATE: return "IWN_CMD_ASSOCIATE"; case IWN_CMD_EDCA_PARAMS: return "IWN_CMD_EDCA_PARAMS"; case IWN_CMD_TSF: return "IWN_CMD_TSF"; case IWN_CMD_TX_LINK_QUALITY: return "IWN_CMD_TX_LINK_QUALITY"; case IWN_CMD_SET_LED: return "IWN_CMD_SET_LED"; case IWN_CMD_SET_POWER_MODE: return "IWN_CMD_SET_POWER_MODE"; case IWN_CMD_SCAN: return "IWN_CMD_SCAN"; case IWN_CMD_TXPOWER: return "IWN_CMD_TXPOWER"; case IWN_CMD_BLUETOOTH: return "IWN_CMD_BLUETOOTH"; case IWN_CMD_SET_CRITICAL_TEMP: return "IWN_CMD_SET_CRITICAL_TEMP"; case IWN_SENSITIVITY: return "IWN_SENSITIVITY"; case IWN_PHY_CALIB: return "IWN_PHY_CALIB"; } return "UNKNOWN INTR NOTIF/CMD"; } #endif /* IWN_DEBUG */ static device_method_t iwn_methods[] = { /* Device interface */ DEVMETHOD(device_probe, iwn_probe), DEVMETHOD(device_attach, iwn_attach), DEVMETHOD(device_detach, iwn_detach), DEVMETHOD(device_shutdown, iwn_shutdown), DEVMETHOD(device_suspend, iwn_suspend), DEVMETHOD(device_resume, iwn_resume), { 0, 0 } }; static driver_t iwn_driver = { "iwn", iwn_methods, sizeof (struct iwn_softc) }; static devclass_t iwn_devclass; DRIVER_MODULE(iwn, pci, iwn_driver, iwn_devclass, 0, 0); MODULE_DEPEND(iwn, pci, 1, 1, 1); MODULE_DEPEND(iwn, firmware, 1, 1, 1); MODULE_DEPEND(iwn, wlan, 1, 1, 1); MODULE_DEPEND(iwn, wlan_amrr, 1, 1, 1); Index: head/sys/dev/ral/rt2560.c =================================================================== --- head/sys/dev/ral/rt2560.c (revision 193072) +++ head/sys/dev/ral/rt2560.c (revision 193073) @@ -1,2835 +1,2835 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2005, 2006 * Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2560 chipset driver * http://www.ralinktech.com/ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RT2560_RSSI(sc, rssi) \ ((rssi) > (RT2560_NOISE_FLOOR + (sc)->rssi_corr) ? \ ((rssi) - RT2560_NOISE_FLOOR - (sc)->rssi_corr) : 0) #define RAL_DEBUG #ifdef RAL_DEBUG #define DPRINTF(sc, fmt, ...) do { \ if (sc->sc_debug > 0) \ printf(fmt, __VA_ARGS__); \ } while (0) #define DPRINTFN(sc, n, fmt, ...) do { \ if (sc->sc_debug >= (n)) \ printf(fmt, __VA_ARGS__); \ } while (0) #else #define DPRINTF(sc, fmt, ...) #define DPRINTFN(sc, n, fmt, ...) #endif static struct ieee80211vap *rt2560_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void rt2560_vap_delete(struct ieee80211vap *); static void rt2560_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int rt2560_alloc_tx_ring(struct rt2560_softc *, struct rt2560_tx_ring *, int); static void rt2560_reset_tx_ring(struct rt2560_softc *, struct rt2560_tx_ring *); static void rt2560_free_tx_ring(struct rt2560_softc *, struct rt2560_tx_ring *); static int rt2560_alloc_rx_ring(struct rt2560_softc *, struct rt2560_rx_ring *, int); static void rt2560_reset_rx_ring(struct rt2560_softc *, struct rt2560_rx_ring *); static void rt2560_free_rx_ring(struct rt2560_softc *, struct rt2560_rx_ring *); static struct ieee80211_node *rt2560_node_alloc(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); static void rt2560_newassoc(struct ieee80211_node *, int); static int rt2560_newstate(struct ieee80211vap *, enum ieee80211_state, int); static uint16_t rt2560_eeprom_read(struct rt2560_softc *, uint8_t); static void rt2560_encryption_intr(struct rt2560_softc *); static void rt2560_tx_intr(struct rt2560_softc *); static void rt2560_prio_intr(struct rt2560_softc *); static void rt2560_decryption_intr(struct rt2560_softc *); static void rt2560_rx_intr(struct rt2560_softc *); static void rt2560_beacon_update(struct ieee80211vap *, int item); static void rt2560_beacon_expire(struct rt2560_softc *); static void rt2560_wakeup_expire(struct rt2560_softc *); static void rt2560_scan_start(struct ieee80211com *); static void rt2560_scan_end(struct ieee80211com *); static void rt2560_set_channel(struct ieee80211com *); static void rt2560_setup_tx_desc(struct rt2560_softc *, struct rt2560_tx_desc *, uint32_t, int, int, int, bus_addr_t); static int rt2560_tx_bcn(struct rt2560_softc *, struct mbuf *, struct ieee80211_node *); static int rt2560_tx_mgt(struct rt2560_softc *, struct mbuf *, struct ieee80211_node *); static int rt2560_tx_data(struct rt2560_softc *, struct mbuf *, struct ieee80211_node *); static void rt2560_start_locked(struct ifnet *); static void rt2560_start(struct ifnet *); static void rt2560_watchdog(void *); static int rt2560_ioctl(struct ifnet *, u_long, caddr_t); static void rt2560_bbp_write(struct rt2560_softc *, uint8_t, uint8_t); static uint8_t rt2560_bbp_read(struct rt2560_softc *, uint8_t); static void rt2560_rf_write(struct rt2560_softc *, uint8_t, uint32_t); static void rt2560_set_chan(struct rt2560_softc *, struct ieee80211_channel *); #if 0 static void rt2560_disable_rf_tune(struct rt2560_softc *); #endif static void rt2560_enable_tsf_sync(struct rt2560_softc *); static void rt2560_enable_tsf(struct rt2560_softc *); static void rt2560_update_plcp(struct rt2560_softc *); static void rt2560_update_slot(struct ifnet *); static void rt2560_set_basicrates(struct rt2560_softc *); static void rt2560_update_led(struct rt2560_softc *, int, int); static void rt2560_set_bssid(struct rt2560_softc *, const uint8_t *); static void rt2560_set_macaddr(struct rt2560_softc *, uint8_t *); static void rt2560_get_macaddr(struct rt2560_softc *, uint8_t *); static void rt2560_update_promisc(struct ifnet *); static const char *rt2560_get_rf(int); static void rt2560_read_config(struct rt2560_softc *); static int rt2560_bbp_init(struct rt2560_softc *); static void rt2560_set_txantenna(struct rt2560_softc *, int); static void rt2560_set_rxantenna(struct rt2560_softc *, int); static void rt2560_init_locked(struct rt2560_softc *); static void rt2560_init(void *); static void rt2560_stop_locked(struct rt2560_softc *); static int rt2560_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static const struct { uint32_t reg; uint32_t val; } rt2560_def_mac[] = { RT2560_DEF_MAC }; static const struct { uint8_t reg; uint8_t val; } rt2560_def_bbp[] = { RT2560_DEF_BBP }; static const uint32_t rt2560_rf2522_r2[] = RT2560_RF2522_R2; static const uint32_t rt2560_rf2523_r2[] = RT2560_RF2523_R2; static const uint32_t rt2560_rf2524_r2[] = RT2560_RF2524_R2; static const uint32_t rt2560_rf2525_r2[] = RT2560_RF2525_R2; static const uint32_t rt2560_rf2525_hi_r2[] = RT2560_RF2525_HI_R2; static const uint32_t rt2560_rf2525e_r2[] = RT2560_RF2525E_R2; static const uint32_t rt2560_rf2526_r2[] = RT2560_RF2526_R2; static const uint32_t rt2560_rf2526_hi_r2[] = RT2560_RF2526_HI_R2; static const struct { uint8_t chan; uint32_t r1, r2, r4; } rt2560_rf5222[] = { RT2560_RF5222 }; int rt2560_attach(device_t dev, int id) { struct rt2560_softc *sc = device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; int error; uint8_t bands; uint8_t macaddr[IEEE80211_ADDR_LEN]; sc->sc_dev = dev; mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0); /* retrieve RT2560 rev. no */ sc->asic_rev = RAL_READ(sc, RT2560_CSR0); /* retrieve RF rev. no and various other things from EEPROM */ rt2560_read_config(sc); device_printf(dev, "MAC/BBP RT2560 (rev 0x%02x), RF %s\n", sc->asic_rev, rt2560_get_rf(sc->rf_rev)); /* * Allocate Tx and Rx rings. */ error = rt2560_alloc_tx_ring(sc, &sc->txq, RT2560_TX_RING_COUNT); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Tx ring\n"); goto fail1; } error = rt2560_alloc_tx_ring(sc, &sc->atimq, RT2560_ATIM_RING_COUNT); if (error != 0) { device_printf(sc->sc_dev, "could not allocate ATIM ring\n"); goto fail2; } error = rt2560_alloc_tx_ring(sc, &sc->prioq, RT2560_PRIO_RING_COUNT); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Prio ring\n"); goto fail3; } error = rt2560_alloc_tx_ring(sc, &sc->bcnq, RT2560_BEACON_RING_COUNT); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Beacon ring\n"); goto fail4; } error = rt2560_alloc_rx_ring(sc, &sc->rxq, RT2560_RX_RING_COUNT); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Rx ring\n"); goto fail5; } ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(sc->sc_dev, "can not if_alloc()\n"); goto fail6; } ic = ifp->if_l2com; /* retrieve MAC address */ rt2560_get_macaddr(sc, macaddr); ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = rt2560_init; ifp->if_ioctl = rt2560_ioctl; ifp->if_start = rt2560_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; ic->ic_opmode = IEEE80211_M_STA; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode */ | IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ | IEEE80211_C_HOSTAP /* hostap mode */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_AHDEMO /* adhoc demo mode */ | IEEE80211_C_WDS /* 4-address traffic works */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ #ifdef notyet | IEEE80211_C_TXFRAG /* handle tx frags */ #endif ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); if (sc->rf_rev == RT2560_RF_5222) setbit(&bands, IEEE80211_MODE_11A); ieee80211_init_channels(ic, NULL, &bands); ieee80211_ifattach(ic, macaddr); ic->ic_newassoc = rt2560_newassoc; ic->ic_raw_xmit = rt2560_raw_xmit; ic->ic_updateslot = rt2560_update_slot; ic->ic_update_promisc = rt2560_update_promisc; ic->ic_node_alloc = rt2560_node_alloc; ic->ic_scan_start = rt2560_scan_start; ic->ic_scan_end = rt2560_scan_end; ic->ic_set_channel = rt2560_set_channel; ic->ic_vap_create = rt2560_vap_create; ic->ic_vap_delete = rt2560_vap_delete; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), RT2560_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), RT2560_RX_RADIOTAP_PRESENT); /* * Add a few sysctl knobs. */ #ifdef RAL_DEBUG SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, 0, "debug msgs"); #endif SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "txantenna", CTLFLAG_RW, &sc->tx_ant, 0, "tx antenna (0=auto)"); SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rxantenna", CTLFLAG_RW, &sc->rx_ant, 0, "rx antenna (0=auto)"); if (bootverbose) ieee80211_announce(ic); return 0; fail6: rt2560_free_rx_ring(sc, &sc->rxq); fail5: rt2560_free_tx_ring(sc, &sc->bcnq); fail4: rt2560_free_tx_ring(sc, &sc->prioq); fail3: rt2560_free_tx_ring(sc, &sc->atimq); fail2: rt2560_free_tx_ring(sc, &sc->txq); fail1: mtx_destroy(&sc->sc_mtx); return ENXIO; } int rt2560_detach(void *xsc) { struct rt2560_softc *sc = xsc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; rt2560_stop(sc); ieee80211_ifdetach(ic); rt2560_free_tx_ring(sc, &sc->txq); rt2560_free_tx_ring(sc, &sc->atimq); rt2560_free_tx_ring(sc, &sc->prioq); rt2560_free_tx_ring(sc, &sc->bcnq); rt2560_free_rx_ring(sc, &sc->rxq); if_free(ifp); mtx_destroy(&sc->sc_mtx); return 0; } static struct ieee80211vap * rt2560_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ifnet *ifp = ic->ic_ifp; struct rt2560_vap *rvp; struct ieee80211vap *vap; switch (opmode) { case IEEE80211_M_STA: case IEEE80211_M_IBSS: case IEEE80211_M_AHDEMO: case IEEE80211_M_MONITOR: case IEEE80211_M_HOSTAP: if (!TAILQ_EMPTY(&ic->ic_vaps)) { if_printf(ifp, "only 1 vap supported\n"); return NULL; } if (opmode == IEEE80211_M_STA) flags |= IEEE80211_CLONE_NOBEACONS; break; case IEEE80211_M_WDS: if (TAILQ_EMPTY(&ic->ic_vaps) || ic->ic_opmode != IEEE80211_M_HOSTAP) { if_printf(ifp, "wds only supported in ap mode\n"); return NULL; } /* * Silently remove any request for a unique * bssid; WDS vap's always share the local * mac address. */ flags &= ~IEEE80211_CLONE_BSSID; break; default: if_printf(ifp, "unknown opmode %d\n", opmode); return NULL; } rvp = (struct rt2560_vap *) malloc(sizeof(struct rt2560_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (rvp == NULL) return NULL; vap = &rvp->ral_vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); /* override state transition machine */ rvp->ral_newstate = vap->iv_newstate; vap->iv_newstate = rt2560_newstate; vap->iv_update_beacon = rt2560_beacon_update; ieee80211_amrr_init(&rvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 500 /* ms */); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); if (TAILQ_FIRST(&ic->ic_vaps) == vap) ic->ic_opmode = opmode; return vap; } static void rt2560_vap_delete(struct ieee80211vap *vap) { struct rt2560_vap *rvp = RT2560_VAP(vap); ieee80211_amrr_cleanup(&rvp->amrr); ieee80211_vap_detach(vap); free(rvp, M_80211_VAP); } void rt2560_resume(void *xsc) { struct rt2560_softc *sc = xsc; struct ifnet *ifp = sc->sc_ifp; if (ifp->if_flags & IFF_UP) rt2560_init(sc); } static void rt2560_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { if (error != 0) return; KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); *(bus_addr_t *)arg = segs[0].ds_addr; } static int rt2560_alloc_tx_ring(struct rt2560_softc *sc, struct rt2560_tx_ring *ring, int count) { int i, error; ring->count = count; ring->queued = 0; ring->cur = ring->next = 0; ring->cur_encrypt = ring->next_encrypt = 0; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, count * RT2560_TX_DESC_SIZE, 1, count * RT2560_TX_DESC_SIZE, 0, NULL, NULL, &ring->desc_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create desc DMA tag\n"); goto fail; } error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); if (error != 0) { device_printf(sc->sc_dev, "could not allocate DMA memory\n"); goto fail; } error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, count * RT2560_TX_DESC_SIZE, rt2560_dma_map_addr, &ring->physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not load desc DMA map\n"); goto fail; } ring->data = malloc(count * sizeof (struct rt2560_tx_data), M_DEVBUF, M_NOWAIT | M_ZERO); if (ring->data == NULL) { device_printf(sc->sc_dev, "could not allocate soft data\n"); error = ENOMEM; goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, RT2560_MAX_SCATTER, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create data DMA tag\n"); goto fail; } for (i = 0; i < count; i++) { error = bus_dmamap_create(ring->data_dmat, 0, &ring->data[i].map); if (error != 0) { device_printf(sc->sc_dev, "could not create DMA map\n"); goto fail; } } return 0; fail: rt2560_free_tx_ring(sc, ring); return error; } static void rt2560_reset_tx_ring(struct rt2560_softc *sc, struct rt2560_tx_ring *ring) { struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; int i; for (i = 0; i < ring->count; i++) { desc = &ring->desc[i]; data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } desc->flags = 0; } bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE); ring->queued = 0; ring->cur = ring->next = 0; ring->cur_encrypt = ring->next_encrypt = 0; } static void rt2560_free_tx_ring(struct rt2560_softc *sc, struct rt2560_tx_ring *ring) { struct rt2560_tx_data *data; int i; if (ring->desc != NULL) { bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->desc_dmat, ring->desc_map); bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); } if (ring->desc_dmat != NULL) bus_dma_tag_destroy(ring->desc_dmat); if (ring->data != NULL) { for (i = 0; i < ring->count; i++) { data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); } if (data->ni != NULL) ieee80211_free_node(data->ni); if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } free(ring->data, M_DEVBUF); } if (ring->data_dmat != NULL) bus_dma_tag_destroy(ring->data_dmat); } static int rt2560_alloc_rx_ring(struct rt2560_softc *sc, struct rt2560_rx_ring *ring, int count) { struct rt2560_rx_desc *desc; struct rt2560_rx_data *data; bus_addr_t physaddr; int i, error; ring->count = count; ring->cur = ring->next = 0; ring->cur_decrypt = 0; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, count * RT2560_RX_DESC_SIZE, 1, count * RT2560_RX_DESC_SIZE, 0, NULL, NULL, &ring->desc_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create desc DMA tag\n"); goto fail; } error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); if (error != 0) { device_printf(sc->sc_dev, "could not allocate DMA memory\n"); goto fail; } error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc, count * RT2560_RX_DESC_SIZE, rt2560_dma_map_addr, &ring->physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not load desc DMA map\n"); goto fail; } ring->data = malloc(count * sizeof (struct rt2560_rx_data), M_DEVBUF, M_NOWAIT | M_ZERO); if (ring->data == NULL) { device_printf(sc->sc_dev, "could not allocate soft data\n"); error = ENOMEM; goto fail; } /* * Pre-allocate Rx buffers and populate Rx ring. */ error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create data DMA tag\n"); goto fail; } for (i = 0; i < count; i++) { desc = &sc->rxq.desc[i]; data = &sc->rxq.data[i]; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "could not create DMA map\n"); goto fail; } data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (data->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } error = bus_dmamap_load(ring->data_dmat, data->map, mtod(data->m, void *), MCLBYTES, rt2560_dma_map_addr, &physaddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not load rx buf DMA map"); goto fail; } desc->flags = htole32(RT2560_RX_BUSY); desc->physaddr = htole32(physaddr); } bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE); return 0; fail: rt2560_free_rx_ring(sc, ring); return error; } static void rt2560_reset_rx_ring(struct rt2560_softc *sc, struct rt2560_rx_ring *ring) { int i; for (i = 0; i < ring->count; i++) { ring->desc[i].flags = htole32(RT2560_RX_BUSY); ring->data[i].drop = 0; } bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE); ring->cur = ring->next = 0; ring->cur_decrypt = 0; } static void rt2560_free_rx_ring(struct rt2560_softc *sc, struct rt2560_rx_ring *ring) { struct rt2560_rx_data *data; int i; if (ring->desc != NULL) { bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->desc_dmat, ring->desc_map); bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map); } if (ring->desc_dmat != NULL) bus_dma_tag_destroy(ring->desc_dmat); if (ring->data != NULL) { for (i = 0; i < ring->count; i++) { data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); } if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } free(ring->data, M_DEVBUF); } if (ring->data_dmat != NULL) bus_dma_tag_destroy(ring->data_dmat); } static struct ieee80211_node * rt2560_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct rt2560_node *rn; rn = malloc(sizeof (struct rt2560_node), M_80211_NODE, M_NOWAIT | M_ZERO); return (rn != NULL) ? &rn->ni : NULL; } static void rt2560_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&RT2560_VAP(vap)->amrr, &RT2560_NODE(ni)->amrr, ni); } static int rt2560_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct rt2560_vap *rvp = RT2560_VAP(vap); struct ifnet *ifp = vap->iv_ic->ic_ifp; struct rt2560_softc *sc = ifp->if_softc; int error; if (nstate == IEEE80211_S_INIT && vap->iv_state == IEEE80211_S_RUN) { /* abort TSF synchronization */ RAL_WRITE(sc, RT2560_CSR14, 0); /* turn association led off */ rt2560_update_led(sc, 0, 0); } error = rvp->ral_newstate(vap, nstate, arg); if (error == 0 && nstate == IEEE80211_S_RUN) { struct ieee80211_node *ni = vap->iv_bss; struct mbuf *m; if (vap->iv_opmode != IEEE80211_M_MONITOR) { rt2560_update_plcp(sc); rt2560_set_basicrates(sc); rt2560_set_bssid(sc, ni->ni_bssid); } if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS) { m = ieee80211_beacon_alloc(ni, &rvp->ral_bo); if (m == NULL) { if_printf(ifp, "could not allocate beacon\n"); return ENOBUFS; } ieee80211_ref_node(ni); error = rt2560_tx_bcn(sc, m, ni); if (error != 0) return error; } /* turn assocation led on */ rt2560_update_led(sc, 1, 0); if (vap->iv_opmode != IEEE80211_M_MONITOR) rt2560_enable_tsf_sync(sc); else rt2560_enable_tsf(sc); } return error; } /* * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46 or * 93C66). */ static uint16_t rt2560_eeprom_read(struct rt2560_softc *sc, uint8_t addr) { uint32_t tmp; uint16_t val; int n; /* clock C once before the first command */ RT2560_EEPROM_CTL(sc, 0); RT2560_EEPROM_CTL(sc, RT2560_S); RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_C); RT2560_EEPROM_CTL(sc, RT2560_S); /* write start bit (1) */ RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_D); RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_D | RT2560_C); /* write READ opcode (10) */ RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_D); RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_D | RT2560_C); RT2560_EEPROM_CTL(sc, RT2560_S); RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_C); /* write address (A5-A0 or A7-A0) */ n = (RAL_READ(sc, RT2560_CSR21) & RT2560_93C46) ? 5 : 7; for (; n >= 0; n--) { RT2560_EEPROM_CTL(sc, RT2560_S | (((addr >> n) & 1) << RT2560_SHIFT_D)); RT2560_EEPROM_CTL(sc, RT2560_S | (((addr >> n) & 1) << RT2560_SHIFT_D) | RT2560_C); } RT2560_EEPROM_CTL(sc, RT2560_S); /* read data Q15-Q0 */ val = 0; for (n = 15; n >= 0; n--) { RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_C); tmp = RAL_READ(sc, RT2560_CSR21); val |= ((tmp & RT2560_Q) >> RT2560_SHIFT_Q) << n; RT2560_EEPROM_CTL(sc, RT2560_S); } RT2560_EEPROM_CTL(sc, 0); /* clear Chip Select and clock C */ RT2560_EEPROM_CTL(sc, RT2560_S); RT2560_EEPROM_CTL(sc, 0); RT2560_EEPROM_CTL(sc, RT2560_C); return val; } /* * Some frames were processed by the hardware cipher engine and are ready for * transmission. */ static void rt2560_encryption_intr(struct rt2560_softc *sc) { struct rt2560_tx_desc *desc; int hw; /* retrieve last descriptor index processed by cipher engine */ hw = RAL_READ(sc, RT2560_SECCSR1) - sc->txq.physaddr; hw /= RT2560_TX_DESC_SIZE; bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map, BUS_DMASYNC_POSTREAD); while (sc->txq.next_encrypt != hw) { if (sc->txq.next_encrypt == sc->txq.cur_encrypt) { printf("hw encrypt %d, cur_encrypt %d\n", hw, sc->txq.cur_encrypt); break; } desc = &sc->txq.desc[sc->txq.next_encrypt]; if ((le32toh(desc->flags) & RT2560_TX_BUSY) || (le32toh(desc->flags) & RT2560_TX_CIPHER_BUSY)) break; /* for TKIP, swap eiv field to fix a bug in ASIC */ if ((le32toh(desc->flags) & RT2560_TX_CIPHER_MASK) == RT2560_TX_CIPHER_TKIP) desc->eiv = bswap32(desc->eiv); /* mark the frame ready for transmission */ desc->flags |= htole32(RT2560_TX_VALID); desc->flags |= htole32(RT2560_TX_BUSY); DPRINTFN(sc, 15, "encryption done idx=%u\n", sc->txq.next_encrypt); sc->txq.next_encrypt = (sc->txq.next_encrypt + 1) % RT2560_TX_RING_COUNT; } bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map, BUS_DMASYNC_PREWRITE); /* kick Tx */ RAL_WRITE(sc, RT2560_TXCSR0, RT2560_KICK_TX); } static void rt2560_tx_intr(struct rt2560_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; struct rt2560_node *rn; struct mbuf *m; uint32_t flags; int retrycnt; bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map, BUS_DMASYNC_POSTREAD); for (;;) { desc = &sc->txq.desc[sc->txq.next]; data = &sc->txq.data[sc->txq.next]; flags = le32toh(desc->flags); if ((flags & RT2560_TX_BUSY) || (flags & RT2560_TX_CIPHER_BUSY) || !(flags & RT2560_TX_VALID)) break; rn = (struct rt2560_node *)data->ni; m = data->m; switch (flags & RT2560_TX_RESULT_MASK) { case RT2560_TX_SUCCESS: DPRINTFN(sc, 10, "%s\n", "data frame sent successfully"); if (data->rix != IEEE80211_FIXED_RATE_NONE) ieee80211_amrr_tx_complete(&rn->amrr, IEEE80211_AMRR_SUCCESS, 0); ifp->if_opackets++; break; case RT2560_TX_SUCCESS_RETRY: retrycnt = RT2560_TX_RETRYCNT(flags); DPRINTFN(sc, 9, "data frame sent after %u retries\n", retrycnt); if (data->rix != IEEE80211_FIXED_RATE_NONE) ieee80211_amrr_tx_complete(&rn->amrr, IEEE80211_AMRR_SUCCESS, retrycnt); ifp->if_opackets++; break; case RT2560_TX_FAIL_RETRY: retrycnt = RT2560_TX_RETRYCNT(flags); DPRINTFN(sc, 9, "data frame failed after %d retries\n", retrycnt); if (data->rix != IEEE80211_FIXED_RATE_NONE) ieee80211_amrr_tx_complete(&rn->amrr, IEEE80211_AMRR_FAILURE, retrycnt); ifp->if_oerrors++; break; case RT2560_TX_FAIL_INVALID: case RT2560_TX_FAIL_OTHER: default: device_printf(sc->sc_dev, "sending data frame failed " "0x%08x\n", flags); ifp->if_oerrors++; } bus_dmamap_sync(sc->txq.data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->txq.data_dmat, data->map); m_freem(m); data->m = NULL; ieee80211_free_node(data->ni); data->ni = NULL; /* descriptor is no longer valid */ desc->flags &= ~htole32(RT2560_TX_VALID); DPRINTFN(sc, 15, "tx done idx=%u\n", sc->txq.next); sc->txq.queued--; sc->txq.next = (sc->txq.next + 1) % RT2560_TX_RING_COUNT; } bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map, BUS_DMASYNC_PREWRITE); if (sc->prioq.queued == 0 && sc->txq.queued == 0) sc->sc_tx_timer = 0; if (sc->txq.queued < RT2560_TX_RING_COUNT - 1) { sc->sc_flags &= ~RT2560_F_DATA_OACTIVE; if ((sc->sc_flags & (RT2560_F_DATA_OACTIVE | RT2560_F_PRIO_OACTIVE)) == 0) ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; rt2560_start_locked(ifp); } } static void rt2560_prio_intr(struct rt2560_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; struct ieee80211_node *ni; struct mbuf *m; int flags; bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map, BUS_DMASYNC_POSTREAD); for (;;) { desc = &sc->prioq.desc[sc->prioq.next]; data = &sc->prioq.data[sc->prioq.next]; flags = le32toh(desc->flags); if ((flags & RT2560_TX_BUSY) || (flags & RT2560_TX_VALID) == 0) break; switch (flags & RT2560_TX_RESULT_MASK) { case RT2560_TX_SUCCESS: DPRINTFN(sc, 10, "%s\n", "mgt frame sent successfully"); break; case RT2560_TX_SUCCESS_RETRY: DPRINTFN(sc, 9, "mgt frame sent after %u retries\n", (flags >> 5) & 0x7); break; case RT2560_TX_FAIL_RETRY: DPRINTFN(sc, 9, "%s\n", "sending mgt frame failed (too much retries)"); break; case RT2560_TX_FAIL_INVALID: case RT2560_TX_FAIL_OTHER: default: device_printf(sc->sc_dev, "sending mgt frame failed " "0x%08x\n", flags); break; } bus_dmamap_sync(sc->prioq.data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->prioq.data_dmat, data->map); m = data->m; data->m = NULL; ni = data->ni; data->ni = NULL; /* descriptor is no longer valid */ desc->flags &= ~htole32(RT2560_TX_VALID); DPRINTFN(sc, 15, "prio done idx=%u\n", sc->prioq.next); sc->prioq.queued--; sc->prioq.next = (sc->prioq.next + 1) % RT2560_PRIO_RING_COUNT; if (m->m_flags & M_TXCB) ieee80211_process_callback(ni, m, (flags & RT2560_TX_RESULT_MASK) &~ (RT2560_TX_SUCCESS | RT2560_TX_SUCCESS_RETRY)); m_freem(m); ieee80211_free_node(ni); } bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map, BUS_DMASYNC_PREWRITE); if (sc->prioq.queued == 0 && sc->txq.queued == 0) sc->sc_tx_timer = 0; if (sc->prioq.queued < RT2560_PRIO_RING_COUNT) { sc->sc_flags &= ~RT2560_F_PRIO_OACTIVE; if ((sc->sc_flags & (RT2560_F_DATA_OACTIVE | RT2560_F_PRIO_OACTIVE)) == 0) ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; rt2560_start_locked(ifp); } } /* * Some frames were processed by the hardware cipher engine and are ready for * handoff to the IEEE802.11 layer. */ static void rt2560_decryption_intr(struct rt2560_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct rt2560_rx_desc *desc; struct rt2560_rx_data *data; bus_addr_t physaddr; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *mnew, *m; int hw, error; int8_t rssi, nf; /* retrieve last decriptor index processed by cipher engine */ hw = RAL_READ(sc, RT2560_SECCSR0) - sc->rxq.physaddr; hw /= RT2560_RX_DESC_SIZE; bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map, BUS_DMASYNC_POSTREAD); for (; sc->rxq.cur_decrypt != hw;) { desc = &sc->rxq.desc[sc->rxq.cur_decrypt]; data = &sc->rxq.data[sc->rxq.cur_decrypt]; if ((le32toh(desc->flags) & RT2560_RX_BUSY) || (le32toh(desc->flags) & RT2560_RX_CIPHER_BUSY)) break; if (data->drop) { ifp->if_ierrors++; goto skip; } if ((le32toh(desc->flags) & RT2560_RX_CIPHER_MASK) != 0 && (le32toh(desc->flags) & RT2560_RX_ICV_ERROR)) { ifp->if_ierrors++; goto skip; } /* * 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, drop the received packet and reuse the old * mbuf. In the unlikely case that the old mbuf can't be * reloaded either, explicitly panic. */ mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { ifp->if_ierrors++; goto skip; } bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rxq.data_dmat, data->map); error = bus_dmamap_load(sc->rxq.data_dmat, data->map, mtod(mnew, void *), MCLBYTES, rt2560_dma_map_addr, &physaddr, 0); if (error != 0) { m_freem(mnew); /* try to reload the old mbuf */ error = bus_dmamap_load(sc->rxq.data_dmat, data->map, mtod(data->m, void *), MCLBYTES, rt2560_dma_map_addr, &physaddr, 0); if (error != 0) { /* very unlikely that it will fail... */ panic("%s: could not load old rx mbuf", device_get_name(sc->sc_dev)); } ifp->if_ierrors++; goto skip; } /* * New mbuf successfully loaded, update Rx ring and continue * processing. */ m = data->m; data->m = mnew; desc->physaddr = htole32(physaddr); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff; rssi = RT2560_RSSI(sc, desc->rssi); nf = RT2560_NOISE_FLOOR; if (ieee80211_radiotap_active(ic)) { struct rt2560_rx_radiotap_header *tap = &sc->sc_rxtap; uint32_t tsf_lo, tsf_hi; /* get timestamp (low and high 32 bits) */ tsf_hi = RAL_READ(sc, RT2560_CSR17); tsf_lo = RAL_READ(sc, RT2560_CSR16); tap->wr_tsf = htole64(((uint64_t)tsf_hi << 32) | tsf_lo); tap->wr_flags = 0; tap->wr_rate = ieee80211_plcp2rate(desc->rate, (desc->flags & htole32(RT2560_RX_OFDM)) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_antenna = sc->rx_ant; tap->wr_antsignal = nf + rssi; tap->wr_antnoise = nf; } sc->sc_flags |= RT2560_F_INPUT_RUNNING; RAL_UNLOCK(sc); wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); if (ni != NULL) { (void) ieee80211_input(ni, m, rssi, nf); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi, nf); RAL_LOCK(sc); sc->sc_flags &= ~RT2560_F_INPUT_RUNNING; skip: desc->flags = htole32(RT2560_RX_BUSY); DPRINTFN(sc, 15, "decryption done idx=%u\n", sc->rxq.cur_decrypt); sc->rxq.cur_decrypt = (sc->rxq.cur_decrypt + 1) % RT2560_RX_RING_COUNT; } bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map, BUS_DMASYNC_PREWRITE); } /* * Some frames were received. Pass them to the hardware cipher engine before * sending them to the 802.11 layer. */ static void rt2560_rx_intr(struct rt2560_softc *sc) { struct rt2560_rx_desc *desc; struct rt2560_rx_data *data; bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map, BUS_DMASYNC_POSTREAD); for (;;) { desc = &sc->rxq.desc[sc->rxq.cur]; data = &sc->rxq.data[sc->rxq.cur]; if ((le32toh(desc->flags) & RT2560_RX_BUSY) || (le32toh(desc->flags) & RT2560_RX_CIPHER_BUSY)) break; data->drop = 0; if ((le32toh(desc->flags) & RT2560_RX_PHY_ERROR) || (le32toh(desc->flags) & RT2560_RX_CRC_ERROR)) { /* * This should not happen since we did not request * to receive those frames when we filled RXCSR0. */ DPRINTFN(sc, 5, "PHY or CRC error flags 0x%08x\n", le32toh(desc->flags)); data->drop = 1; } if (((le32toh(desc->flags) >> 16) & 0xfff) > MCLBYTES) { DPRINTFN(sc, 5, "%s\n", "bad length"); data->drop = 1; } /* mark the frame for decryption */ desc->flags |= htole32(RT2560_RX_CIPHER_BUSY); DPRINTFN(sc, 15, "rx done idx=%u\n", sc->rxq.cur); sc->rxq.cur = (sc->rxq.cur + 1) % RT2560_RX_RING_COUNT; } bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map, BUS_DMASYNC_PREWRITE); /* kick decrypt */ RAL_WRITE(sc, RT2560_SECCSR0, RT2560_KICK_DECRYPT); } static void rt2560_beacon_update(struct ieee80211vap *vap, int item) { struct rt2560_vap *rvp = RT2560_VAP(vap); struct ieee80211_beacon_offsets *bo = &rvp->ral_bo; setbit(bo->bo_flags, item); } /* * This function is called periodically in IBSS mode when a new beacon must be * sent out. */ static void rt2560_beacon_expire(struct rt2560_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct rt2560_vap *rvp = RT2560_VAP(vap); struct rt2560_tx_data *data; if (ic->ic_opmode != IEEE80211_M_IBSS && ic->ic_opmode != IEEE80211_M_HOSTAP) return; data = &sc->bcnq.data[sc->bcnq.next]; /* * Don't send beacon if bsschan isn't set */ if (data->ni == NULL) return; bus_dmamap_sync(sc->bcnq.data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->bcnq.data_dmat, data->map); /* XXX 1 =>'s mcast frames which means all PS sta's will wakeup! */ ieee80211_beacon_update(data->ni, &rvp->ral_bo, data->m, 1); rt2560_tx_bcn(sc, data->m, data->ni); DPRINTFN(sc, 15, "%s", "beacon expired\n"); sc->bcnq.next = (sc->bcnq.next + 1) % RT2560_BEACON_RING_COUNT; } /* ARGSUSED */ static void rt2560_wakeup_expire(struct rt2560_softc *sc) { DPRINTFN(sc, 2, "%s", "wakeup expired\n"); } void rt2560_intr(void *arg) { struct rt2560_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; uint32_t r; RAL_LOCK(sc); /* disable interrupts */ RAL_WRITE(sc, RT2560_CSR8, 0xffffffff); /* don't re-enable interrupts if we're shutting down */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { RAL_UNLOCK(sc); return; } r = RAL_READ(sc, RT2560_CSR7); RAL_WRITE(sc, RT2560_CSR7, r); if (r & RT2560_BEACON_EXPIRE) rt2560_beacon_expire(sc); if (r & RT2560_WAKEUP_EXPIRE) rt2560_wakeup_expire(sc); if (r & RT2560_ENCRYPTION_DONE) rt2560_encryption_intr(sc); if (r & RT2560_TX_DONE) rt2560_tx_intr(sc); if (r & RT2560_PRIO_DONE) rt2560_prio_intr(sc); if (r & RT2560_DECRYPTION_DONE) rt2560_decryption_intr(sc); if (r & RT2560_RX_DONE) { rt2560_rx_intr(sc); rt2560_encryption_intr(sc); } /* re-enable interrupts */ RAL_WRITE(sc, RT2560_CSR8, RT2560_INTR_MASK); RAL_UNLOCK(sc); } #define RAL_SIFS 10 /* us */ #define RT2560_TXRX_TURNAROUND 10 /* us */ static uint8_t rt2560_plcp_signal(int rate) { switch (rate) { /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return 0xb; case 18: return 0xf; case 24: return 0xa; case 36: return 0xe; case 48: return 0x9; case 72: return 0xd; case 96: return 0x8; case 108: return 0xc; /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return 0x0; case 4: return 0x1; case 11: return 0x2; case 22: return 0x3; } return 0xff; /* XXX unsupported/unknown rate */ } static void rt2560_setup_tx_desc(struct rt2560_softc *sc, struct rt2560_tx_desc *desc, uint32_t flags, int len, int rate, int encrypt, bus_addr_t physaddr) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t plcp_length; int remainder; desc->flags = htole32(flags); desc->flags |= htole32(len << 16); desc->physaddr = htole32(physaddr); desc->wme = htole16( RT2560_AIFSN(2) | RT2560_LOGCWMIN(3) | RT2560_LOGCWMAX(8)); /* setup PLCP fields */ desc->plcp_signal = rt2560_plcp_signal(rate); desc->plcp_service = 4; len += IEEE80211_CRC_LEN; if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) { desc->flags |= htole32(RT2560_TX_OFDM); plcp_length = len & 0xfff; desc->plcp_length_hi = plcp_length >> 6; desc->plcp_length_lo = plcp_length & 0x3f; } else { plcp_length = (16 * len + rate - 1) / rate; if (rate == 22) { remainder = (16 * len) % 22; if (remainder != 0 && remainder < 7) desc->plcp_service |= RT2560_PLCP_LENGEXT; } desc->plcp_length_hi = plcp_length >> 8; desc->plcp_length_lo = plcp_length & 0xff; if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) desc->plcp_signal |= 0x08; } if (!encrypt) desc->flags |= htole32(RT2560_TX_VALID); desc->flags |= encrypt ? htole32(RT2560_TX_CIPHER_BUSY) : htole32(RT2560_TX_BUSY); } static int rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; bus_dma_segment_t segs[RT2560_MAX_SCATTER]; int nsegs, rate, error; desc = &sc->bcnq.desc[sc->bcnq.cur]; data = &sc->bcnq.data[sc->bcnq.cur]; /* XXX maybe a separate beacon rate? */ rate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].mgmtrate; error = bus_dmamap_load_mbuf_sg(sc->bcnq.data_dmat, data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } if (ieee80211_radiotap_active_vap(vap)) { struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_antenna = sc->tx_ant; ieee80211_radiotap_tx(vap, m0); } data->m = m0; data->ni = ni; rt2560_setup_tx_desc(sc, desc, RT2560_TX_IFS_NEWBACKOFF | RT2560_TX_TIMESTAMP, m0->m_pkthdr.len, rate, 0, segs->ds_addr); DPRINTFN(sc, 10, "sending beacon frame len=%u idx=%u rate=%u\n", m0->m_pkthdr.len, sc->bcnq.cur, rate); bus_dmamap_sync(sc->bcnq.data_dmat, data->map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->bcnq.desc_dmat, sc->bcnq.desc_map, BUS_DMASYNC_PREWRITE); sc->bcnq.cur = (sc->bcnq.cur + 1) % RT2560_BEACON_RING_COUNT; return 0; } static int rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; struct ieee80211_frame *wh; struct ieee80211_key *k; bus_dma_segment_t segs[RT2560_MAX_SCATTER]; uint16_t dur; uint32_t flags = 0; int nsegs, rate, error; desc = &sc->prioq.desc[sc->prioq.cur]; data = &sc->prioq.data[sc->prioq.cur]; rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate; wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } } error = bus_dmamap_load_mbuf_sg(sc->prioq.data_dmat, data->map, m0, segs, &nsegs, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } if (ieee80211_radiotap_active_vap(vap)) { struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_antenna = sc->tx_ant; ieee80211_radiotap_tx(vap, m0); } data->m = m0; data->ni = ni; /* management frames are not taken into account for amrr */ data->rix = IEEE80211_FIXED_RATE_NONE; wh = mtod(m0, struct ieee80211_frame *); if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RT2560_TX_ACK; dur = ieee80211_ack_duration(ic->ic_rt, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); *(uint16_t *)wh->i_dur = htole16(dur); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT && (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= RT2560_TX_TIMESTAMP; } rt2560_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate, 0, segs->ds_addr); bus_dmamap_sync(sc->prioq.data_dmat, data->map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map, BUS_DMASYNC_PREWRITE); DPRINTFN(sc, 10, "sending mgt frame len=%u idx=%u rate=%u\n", m0->m_pkthdr.len, sc->prioq.cur, rate); /* kick prio */ sc->prioq.queued++; sc->prioq.cur = (sc->prioq.cur + 1) % RT2560_PRIO_RING_COUNT; RAL_WRITE(sc, RT2560_TXCSR0, RT2560_KICK_PRIO); return 0; } static int rt2560_sendprot(struct rt2560_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) { struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_frame *wh; struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; struct mbuf *mprot; int protrate, ackrate, pktlen, flags, isshort, error; uint16_t dur; bus_dma_segment_t segs[RT2560_MAX_SCATTER]; int nsegs; KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, ("protection %d", prot)); wh = mtod(m, const struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; protrate = ieee80211_ctl_rate(ic->ic_rt, rate); ackrate = ieee80211_ack_rate(ic->ic_rt, rate); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort) + ieee80211_ack_duration(ic->ic_rt, rate, isshort); flags = RT2560_TX_MORE_FRAG; if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort); flags |= RT2560_TX_ACK; mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else { mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { /* XXX stat + msg */ return ENOBUFS; } desc = &sc->txq.desc[sc->txq.cur_encrypt]; data = &sc->txq.data[sc->txq.cur_encrypt]; error = bus_dmamap_load_mbuf_sg(sc->txq.data_dmat, data->map, mprot, segs, &nsegs, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(mprot); return error; } data->m = mprot; data->ni = ieee80211_ref_node(ni); /* ctl frames are not taken into account for amrr */ data->rix = IEEE80211_FIXED_RATE_NONE; rt2560_setup_tx_desc(sc, desc, flags, mprot->m_pkthdr.len, protrate, 1, segs->ds_addr); bus_dmamap_sync(sc->txq.data_dmat, data->map, BUS_DMASYNC_PREWRITE); sc->txq.queued++; sc->txq.cur_encrypt = (sc->txq.cur_encrypt + 1) % RT2560_TX_RING_COUNT; return 0; } static int rt2560_tx_raw(struct rt2560_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; bus_dma_segment_t segs[RT2560_MAX_SCATTER]; uint32_t flags; int nsegs, rate, error; desc = &sc->prioq.desc[sc->prioq.cur]; data = &sc->prioq.data[sc->prioq.cur]; - rate = params->ibp_rate0 & IEEE80211_RATE_VAL; - /* XXX validate */ - if (rate == 0) { + rate = params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) { /* XXX fall back to mcast/mgmt rate? */ m_freem(m0); return EINVAL; } flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= RT2560_TX_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { error = rt2560_sendprot(sc, m0, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); if (error) { m_freem(m0); return error; } flags |= RT2560_TX_LONG_RETRY | RT2560_TX_IFS_SIFS; } error = bus_dmamap_load_mbuf_sg(sc->prioq.data_dmat, data->map, m0, segs, &nsegs, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } if (ieee80211_radiotap_active_vap(vap)) { struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_antenna = sc->tx_ant; ieee80211_radiotap_tx(ni->ni_vap, m0); } data->m = m0; data->ni = ni; /* XXX need to setup descriptor ourself */ rt2560_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate, (params->ibp_flags & IEEE80211_BPF_CRYPTO) != 0, segs->ds_addr); bus_dmamap_sync(sc->prioq.data_dmat, data->map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map, BUS_DMASYNC_PREWRITE); DPRINTFN(sc, 10, "sending raw frame len=%u idx=%u rate=%u\n", m0->m_pkthdr.len, sc->prioq.cur, rate); /* kick prio */ sc->prioq.queued++; sc->prioq.cur = (sc->prioq.cur + 1) % RT2560_PRIO_RING_COUNT; RAL_WRITE(sc, RT2560_TXCSR0, RT2560_KICK_PRIO); return 0; } static int rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct rt2560_tx_desc *desc; struct rt2560_tx_data *data; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp; struct ieee80211_key *k; struct mbuf *mnew; bus_dma_segment_t segs[RT2560_MAX_SCATTER]; uint16_t dur; uint32_t flags; int nsegs, rate, error; wh = mtod(m0, struct ieee80211_frame *); tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { rate = tp->mcastrate; } else if (m0->m_flags & M_EAPOL) { rate = tp->mgmtrate; } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { rate = tp->ucastrate; } else { (void) ieee80211_amrr_choose(ni, &RT2560_NODE(ni)->amrr); rate = ni->ni_txrate; } if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } flags = 0; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { int prot = IEEE80211_PROT_NONE; if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) prot = IEEE80211_PROT_RTSCTS; else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) prot = ic->ic_protmode; if (prot != IEEE80211_PROT_NONE) { error = rt2560_sendprot(sc, m0, ni, prot, rate); if (error) { m_freem(m0); return error; } flags |= RT2560_TX_LONG_RETRY | RT2560_TX_IFS_SIFS; } } data = &sc->txq.data[sc->txq.cur_encrypt]; desc = &sc->txq.desc[sc->txq.cur_encrypt]; error = bus_dmamap_load_mbuf_sg(sc->txq.data_dmat, data->map, m0, segs, &nsegs, 0); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } if (error != 0) { mnew = m_defrag(m0, M_DONTWAIT); if (mnew == NULL) { device_printf(sc->sc_dev, "could not defragment mbuf\n"); m_freem(m0); return ENOBUFS; } m0 = mnew; error = bus_dmamap_load_mbuf_sg(sc->txq.data_dmat, data->map, m0, segs, &nsegs, 0); if (error != 0) { device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", error); m_freem(m0); return error; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (ieee80211_radiotap_active_vap(vap)) { struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; tap->wt_antenna = sc->tx_ant; ieee80211_radiotap_tx(vap, m0); } data->m = m0; data->ni = ni; /* remember link conditions for rate adaptation algorithm */ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) { data->rix = ni->ni_txrate; /* XXX probably need last rssi value and not avg */ data->rssi = ic->ic_node_getrssi(ni); } else data->rix = IEEE80211_FIXED_RATE_NONE; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RT2560_TX_ACK; dur = ieee80211_ack_duration(ic->ic_rt, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); *(uint16_t *)wh->i_dur = htole16(dur); } rt2560_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate, 1, segs->ds_addr); bus_dmamap_sync(sc->txq.data_dmat, data->map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map, BUS_DMASYNC_PREWRITE); DPRINTFN(sc, 10, "sending data frame len=%u idx=%u rate=%u\n", m0->m_pkthdr.len, sc->txq.cur_encrypt, rate); /* kick encrypt */ sc->txq.queued++; sc->txq.cur_encrypt = (sc->txq.cur_encrypt + 1) % RT2560_TX_RING_COUNT; RAL_WRITE(sc, RT2560_SECCSR1, RT2560_KICK_ENCRYPT); return 0; } static void rt2560_start_locked(struct ifnet *ifp) { struct rt2560_softc *sc = ifp->if_softc; struct mbuf *m; struct ieee80211_node *ni; RAL_LOCK_ASSERT(sc); for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; if (sc->txq.queued >= RT2560_TX_RING_COUNT - 1) { IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; sc->sc_flags |= RT2560_F_DATA_OACTIVE; break; } ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; if (rt2560_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; } sc->sc_tx_timer = 5; } } static void rt2560_start(struct ifnet *ifp) { struct rt2560_softc *sc = ifp->if_softc; RAL_LOCK(sc); rt2560_start_locked(ifp); RAL_UNLOCK(sc); } static void rt2560_watchdog(void *arg) { struct rt2560_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; RAL_LOCK_ASSERT(sc); KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running")); if (sc->sc_invalid) /* card ejected */ return; rt2560_encryption_intr(sc); rt2560_tx_intr(sc); if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); rt2560_init_locked(sc); ifp->if_oerrors++; /* NB: callout is reset in rt2560_init() */ return; } callout_reset(&sc->watchdog_ch, hz, rt2560_watchdog, sc); } static int rt2560_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct rt2560_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: RAL_LOCK(sc); if (ifp->if_flags & IFF_UP) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { rt2560_init_locked(sc); startall = 1; } else rt2560_update_promisc(ifp); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) rt2560_stop_locked(sc); } RAL_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; } static void rt2560_bbp_write(struct rt2560_softc *sc, uint8_t reg, uint8_t val) { uint32_t tmp; int ntries; for (ntries = 0; ntries < 100; ntries++) { if (!(RAL_READ(sc, RT2560_BBPCSR) & RT2560_BBP_BUSY)) break; DELAY(1); } if (ntries == 100) { device_printf(sc->sc_dev, "could not write to BBP\n"); return; } tmp = RT2560_BBP_WRITE | RT2560_BBP_BUSY | reg << 8 | val; RAL_WRITE(sc, RT2560_BBPCSR, tmp); DPRINTFN(sc, 15, "BBP R%u <- 0x%02x\n", reg, val); } static uint8_t rt2560_bbp_read(struct rt2560_softc *sc, uint8_t reg) { uint32_t val; int ntries; for (ntries = 0; ntries < 100; ntries++) { if (!(RAL_READ(sc, RT2560_BBPCSR) & RT2560_BBP_BUSY)) break; DELAY(1); } if (ntries == 100) { device_printf(sc->sc_dev, "could not read from BBP\n"); return 0; } val = RT2560_BBP_BUSY | reg << 8; RAL_WRITE(sc, RT2560_BBPCSR, val); for (ntries = 0; ntries < 100; ntries++) { val = RAL_READ(sc, RT2560_BBPCSR); if (!(val & RT2560_BBP_BUSY)) return val & 0xff; DELAY(1); } device_printf(sc->sc_dev, "could not read from BBP\n"); return 0; } static void rt2560_rf_write(struct rt2560_softc *sc, uint8_t reg, uint32_t val) { uint32_t tmp; int ntries; for (ntries = 0; ntries < 100; ntries++) { if (!(RAL_READ(sc, RT2560_RFCSR) & RT2560_RF_BUSY)) break; DELAY(1); } if (ntries == 100) { device_printf(sc->sc_dev, "could not write to RF\n"); return; } tmp = RT2560_RF_BUSY | RT2560_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3); RAL_WRITE(sc, RT2560_RFCSR, tmp); /* remember last written value in sc */ sc->rf_regs[reg] = val; DPRINTFN(sc, 15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff); } static void rt2560_set_chan(struct rt2560_softc *sc, struct ieee80211_channel *c) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint8_t power, tmp; u_int i, chan; chan = ieee80211_chan2ieee(ic, c); KASSERT(chan != 0 && chan != IEEE80211_CHAN_ANY, ("chan 0x%x", chan)); if (IEEE80211_IS_CHAN_2GHZ(c)) power = min(sc->txpow[chan - 1], 31); else power = 31; /* adjust txpower using ifconfig settings */ power -= (100 - ic->ic_txpowlimit) / 8; DPRINTFN(sc, 2, "setting channel to %u, txpower to %u\n", chan, power); switch (sc->rf_rev) { case RT2560_RF_2522: rt2560_rf_write(sc, RAL_RF1, 0x00814); rt2560_rf_write(sc, RAL_RF2, rt2560_rf2522_r2[chan - 1]); rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x00040); break; case RT2560_RF_2523: rt2560_rf_write(sc, RAL_RF1, 0x08804); rt2560_rf_write(sc, RAL_RF2, rt2560_rf2523_r2[chan - 1]); rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x38044); rt2560_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RT2560_RF_2524: rt2560_rf_write(sc, RAL_RF1, 0x0c808); rt2560_rf_write(sc, RAL_RF2, rt2560_rf2524_r2[chan - 1]); rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x00040); rt2560_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RT2560_RF_2525: rt2560_rf_write(sc, RAL_RF1, 0x08808); rt2560_rf_write(sc, RAL_RF2, rt2560_rf2525_hi_r2[chan - 1]); rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x18044); rt2560_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); rt2560_rf_write(sc, RAL_RF1, 0x08808); rt2560_rf_write(sc, RAL_RF2, rt2560_rf2525_r2[chan - 1]); rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x18044); rt2560_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RT2560_RF_2525E: rt2560_rf_write(sc, RAL_RF1, 0x08808); rt2560_rf_write(sc, RAL_RF2, rt2560_rf2525e_r2[chan - 1]); rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x18044); rt2560_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); break; case RT2560_RF_2526: rt2560_rf_write(sc, RAL_RF2, rt2560_rf2526_hi_r2[chan - 1]); rt2560_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); rt2560_rf_write(sc, RAL_RF1, 0x08804); rt2560_rf_write(sc, RAL_RF2, rt2560_rf2526_r2[chan - 1]); rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x18044); rt2560_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); break; /* dual-band RF */ case RT2560_RF_5222: for (i = 0; rt2560_rf5222[i].chan != chan; i++); rt2560_rf_write(sc, RAL_RF1, rt2560_rf5222[i].r1); rt2560_rf_write(sc, RAL_RF2, rt2560_rf5222[i].r2); rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x00040); rt2560_rf_write(sc, RAL_RF4, rt2560_rf5222[i].r4); break; default: printf("unknown ral rev=%d\n", sc->rf_rev); } /* XXX */ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { /* set Japan filter bit for channel 14 */ tmp = rt2560_bbp_read(sc, 70); tmp &= ~RT2560_JAPAN_FILTER; if (chan == 14) tmp |= RT2560_JAPAN_FILTER; rt2560_bbp_write(sc, 70, tmp); /* clear CRC errors */ RAL_READ(sc, RT2560_CNT0); } } static void rt2560_set_channel(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct rt2560_softc *sc = ifp->if_softc; RAL_LOCK(sc); rt2560_set_chan(sc, ic->ic_curchan); RAL_UNLOCK(sc); } #if 0 /* * Disable RF auto-tuning. */ static void rt2560_disable_rf_tune(struct rt2560_softc *sc) { uint32_t tmp; if (sc->rf_rev != RT2560_RF_2523) { tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; rt2560_rf_write(sc, RAL_RF1, tmp); } tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; rt2560_rf_write(sc, RAL_RF3, tmp); DPRINTFN(sc, 2, "%s", "disabling RF autotune\n"); } #endif /* * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF * synchronization. */ static void rt2560_enable_tsf_sync(struct rt2560_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint16_t logcwmin, preload; uint32_t tmp; /* first, disable TSF synchronization */ RAL_WRITE(sc, RT2560_CSR14, 0); tmp = 16 * vap->iv_bss->ni_intval; RAL_WRITE(sc, RT2560_CSR12, tmp); RAL_WRITE(sc, RT2560_CSR13, 0); logcwmin = 5; preload = (vap->iv_opmode == IEEE80211_M_STA) ? 384 : 1024; tmp = logcwmin << 16 | preload; RAL_WRITE(sc, RT2560_BCNOCSR, tmp); /* finally, enable TSF synchronization */ tmp = RT2560_ENABLE_TSF | RT2560_ENABLE_TBCN; if (ic->ic_opmode == IEEE80211_M_STA) tmp |= RT2560_ENABLE_TSF_SYNC(1); else tmp |= RT2560_ENABLE_TSF_SYNC(2) | RT2560_ENABLE_BEACON_GENERATOR; RAL_WRITE(sc, RT2560_CSR14, tmp); DPRINTF(sc, "%s", "enabling TSF synchronization\n"); } static void rt2560_enable_tsf(struct rt2560_softc *sc) { RAL_WRITE(sc, RT2560_CSR14, 0); RAL_WRITE(sc, RT2560_CSR14, RT2560_ENABLE_TSF_SYNC(2) | RT2560_ENABLE_TSF); } static void rt2560_update_plcp(struct rt2560_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* no short preamble for 1Mbps */ RAL_WRITE(sc, RT2560_PLCP1MCSR, 0x00700400); if (!(ic->ic_flags & IEEE80211_F_SHPREAMBLE)) { /* values taken from the reference driver */ RAL_WRITE(sc, RT2560_PLCP2MCSR, 0x00380401); RAL_WRITE(sc, RT2560_PLCP5p5MCSR, 0x00150402); RAL_WRITE(sc, RT2560_PLCP11MCSR, 0x000b8403); } else { /* same values as above or'ed 0x8 */ RAL_WRITE(sc, RT2560_PLCP2MCSR, 0x00380409); RAL_WRITE(sc, RT2560_PLCP5p5MCSR, 0x0015040a); RAL_WRITE(sc, RT2560_PLCP11MCSR, 0x000b840b); } DPRINTF(sc, "updating PLCP for %s preamble\n", (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long"); } /* * This function can be called by ieee80211_set_shortslottime(). Refer to * IEEE Std 802.11-1999 pp. 85 to know how these values are computed. */ static void rt2560_update_slot(struct ifnet *ifp) { struct rt2560_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; uint8_t slottime; uint16_t tx_sifs, tx_pifs, tx_difs, eifs; uint32_t tmp; #ifndef FORCE_SLOTTIME slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; #else /* * Setting slot time according to "short slot time" capability * in beacon/probe_resp seems to cause problem to acknowledge * certain AP's data frames transimitted at CCK/DS rates: the * problematic AP keeps retransmitting data frames, probably * because MAC level acks are not received by hardware. * So we cheat a little bit here by claiming we are capable of * "short slot time" but setting hardware slot time to the normal * slot time. ral(4) does not seem to have trouble to receive * frames transmitted using short slot time even if hardware * slot time is set to normal slot time. If we didn't use this * trick, we would have to claim that short slot time is not * supported; this would give relative poor RX performance * (-1Mb~-2Mb lower) and the _whole_ BSS would stop using short * slot time. */ slottime = 20; #endif /* update the MAC slot boundaries */ tx_sifs = RAL_SIFS - RT2560_TXRX_TURNAROUND; tx_pifs = tx_sifs + slottime; tx_difs = tx_sifs + 2 * slottime; eifs = (ic->ic_curmode == IEEE80211_MODE_11B) ? 364 : 60; tmp = RAL_READ(sc, RT2560_CSR11); tmp = (tmp & ~0x1f00) | slottime << 8; RAL_WRITE(sc, RT2560_CSR11, tmp); tmp = tx_pifs << 16 | tx_sifs; RAL_WRITE(sc, RT2560_CSR18, tmp); tmp = eifs << 16 | tx_difs; RAL_WRITE(sc, RT2560_CSR19, tmp); DPRINTF(sc, "setting slottime to %uus\n", slottime); } static void rt2560_set_basicrates(struct rt2560_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* update basic rate set */ if (ic->ic_curmode == IEEE80211_MODE_11B) { /* 11b basic rates: 1, 2Mbps */ RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x3); } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) { /* 11a basic rates: 6, 12, 24Mbps */ RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x150); } else { /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x15f); } } static void rt2560_update_led(struct rt2560_softc *sc, int led1, int led2) { uint32_t tmp; /* set ON period to 70ms and OFF period to 30ms */ tmp = led1 << 16 | led2 << 17 | 70 << 8 | 30; RAL_WRITE(sc, RT2560_LEDCSR, tmp); } static void rt2560_set_bssid(struct rt2560_softc *sc, const uint8_t *bssid) { uint32_t tmp; tmp = bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24; RAL_WRITE(sc, RT2560_CSR5, tmp); tmp = bssid[4] | bssid[5] << 8; RAL_WRITE(sc, RT2560_CSR6, tmp); DPRINTF(sc, "setting BSSID to %6D\n", bssid, ":"); } static void rt2560_set_macaddr(struct rt2560_softc *sc, uint8_t *addr) { uint32_t tmp; tmp = addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24; RAL_WRITE(sc, RT2560_CSR3, tmp); tmp = addr[4] | addr[5] << 8; RAL_WRITE(sc, RT2560_CSR4, tmp); DPRINTF(sc, "setting MAC address to %6D\n", addr, ":"); } static void rt2560_get_macaddr(struct rt2560_softc *sc, uint8_t *addr) { uint32_t tmp; tmp = RAL_READ(sc, RT2560_CSR3); addr[0] = tmp & 0xff; addr[1] = (tmp >> 8) & 0xff; addr[2] = (tmp >> 16) & 0xff; addr[3] = (tmp >> 24); tmp = RAL_READ(sc, RT2560_CSR4); addr[4] = tmp & 0xff; addr[5] = (tmp >> 8) & 0xff; } static void rt2560_update_promisc(struct ifnet *ifp) { struct rt2560_softc *sc = ifp->if_softc; uint32_t tmp; tmp = RAL_READ(sc, RT2560_RXCSR0); tmp &= ~RT2560_DROP_NOT_TO_ME; if (!(ifp->if_flags & IFF_PROMISC)) tmp |= RT2560_DROP_NOT_TO_ME; RAL_WRITE(sc, RT2560_RXCSR0, tmp); DPRINTF(sc, "%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ? "entering" : "leaving"); } static const char * rt2560_get_rf(int rev) { switch (rev) { case RT2560_RF_2522: return "RT2522"; case RT2560_RF_2523: return "RT2523"; case RT2560_RF_2524: return "RT2524"; case RT2560_RF_2525: return "RT2525"; case RT2560_RF_2525E: return "RT2525e"; case RT2560_RF_2526: return "RT2526"; case RT2560_RF_5222: return "RT5222"; default: return "unknown"; } } static void rt2560_read_config(struct rt2560_softc *sc) { uint16_t val; int i; val = rt2560_eeprom_read(sc, RT2560_EEPROM_CONFIG0); sc->rf_rev = (val >> 11) & 0x7; sc->hw_radio = (val >> 10) & 0x1; sc->led_mode = (val >> 6) & 0x7; sc->rx_ant = (val >> 4) & 0x3; sc->tx_ant = (val >> 2) & 0x3; sc->nb_ant = val & 0x3; /* read default values for BBP registers */ for (i = 0; i < 16; i++) { val = rt2560_eeprom_read(sc, RT2560_EEPROM_BBP_BASE + i); if (val == 0 || val == 0xffff) continue; sc->bbp_prom[i].reg = val >> 8; sc->bbp_prom[i].val = val & 0xff; } /* read Tx power for all b/g channels */ for (i = 0; i < 14 / 2; i++) { val = rt2560_eeprom_read(sc, RT2560_EEPROM_TXPOWER + i); sc->txpow[i * 2] = val & 0xff; sc->txpow[i * 2 + 1] = val >> 8; } for (i = 0; i < 14; ++i) { if (sc->txpow[i] > 31) sc->txpow[i] = 24; } val = rt2560_eeprom_read(sc, RT2560_EEPROM_CALIBRATE); if ((val & 0xff) == 0xff) sc->rssi_corr = RT2560_DEFAULT_RSSI_CORR; else sc->rssi_corr = val & 0xff; DPRINTF(sc, "rssi correction %d, calibrate 0x%02x\n", sc->rssi_corr, val); } static void rt2560_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct rt2560_softc *sc = ifp->if_softc; /* abort TSF synchronization */ RAL_WRITE(sc, RT2560_CSR14, 0); rt2560_set_bssid(sc, ifp->if_broadcastaddr); } static void rt2560_scan_end(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct rt2560_softc *sc = ifp->if_softc; struct ieee80211vap *vap = ic->ic_scan->ss_vap; rt2560_enable_tsf_sync(sc); /* XXX keep local copy */ rt2560_set_bssid(sc, vap->iv_bss->ni_bssid); } static int rt2560_bbp_init(struct rt2560_softc *sc) { #define N(a) (sizeof (a) / sizeof ((a)[0])) int i, ntries; /* wait for BBP to be ready */ for (ntries = 0; ntries < 100; ntries++) { if (rt2560_bbp_read(sc, RT2560_BBP_VERSION) != 0) break; DELAY(1); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for BBP\n"); return EIO; } /* initialize BBP registers to default values */ for (i = 0; i < N(rt2560_def_bbp); i++) { rt2560_bbp_write(sc, rt2560_def_bbp[i].reg, rt2560_def_bbp[i].val); } /* initialize BBP registers to values stored in EEPROM */ for (i = 0; i < 16; i++) { if (sc->bbp_prom[i].reg == 0 && sc->bbp_prom[i].val == 0) break; rt2560_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); } rt2560_bbp_write(sc, 17, 0x48); /* XXX restore bbp17 */ return 0; #undef N } static void rt2560_set_txantenna(struct rt2560_softc *sc, int antenna) { uint32_t tmp; uint8_t tx; tx = rt2560_bbp_read(sc, RT2560_BBP_TX) & ~RT2560_BBP_ANTMASK; if (antenna == 1) tx |= RT2560_BBP_ANTA; else if (antenna == 2) tx |= RT2560_BBP_ANTB; else tx |= RT2560_BBP_DIVERSITY; /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ if (sc->rf_rev == RT2560_RF_2525E || sc->rf_rev == RT2560_RF_2526 || sc->rf_rev == RT2560_RF_5222) tx |= RT2560_BBP_FLIPIQ; rt2560_bbp_write(sc, RT2560_BBP_TX, tx); /* update values for CCK and OFDM in BBPCSR1 */ tmp = RAL_READ(sc, RT2560_BBPCSR1) & ~0x00070007; tmp |= (tx & 0x7) << 16 | (tx & 0x7); RAL_WRITE(sc, RT2560_BBPCSR1, tmp); } static void rt2560_set_rxantenna(struct rt2560_softc *sc, int antenna) { uint8_t rx; rx = rt2560_bbp_read(sc, RT2560_BBP_RX) & ~RT2560_BBP_ANTMASK; if (antenna == 1) rx |= RT2560_BBP_ANTA; else if (antenna == 2) rx |= RT2560_BBP_ANTB; else rx |= RT2560_BBP_DIVERSITY; /* need to force no I/Q flip for RF 2525e and 2526 */ if (sc->rf_rev == RT2560_RF_2525E || sc->rf_rev == RT2560_RF_2526) rx &= ~RT2560_BBP_FLIPIQ; rt2560_bbp_write(sc, RT2560_BBP_RX, rx); } static void rt2560_init_locked(struct rt2560_softc *sc) { #define N(a) (sizeof (a) / sizeof ((a)[0])) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t tmp; int i; RAL_LOCK_ASSERT(sc); rt2560_stop_locked(sc); /* setup tx rings */ tmp = RT2560_PRIO_RING_COUNT << 24 | RT2560_ATIM_RING_COUNT << 16 | RT2560_TX_RING_COUNT << 8 | RT2560_TX_DESC_SIZE; /* rings must be initialized in this exact order */ RAL_WRITE(sc, RT2560_TXCSR2, tmp); RAL_WRITE(sc, RT2560_TXCSR3, sc->txq.physaddr); RAL_WRITE(sc, RT2560_TXCSR5, sc->prioq.physaddr); RAL_WRITE(sc, RT2560_TXCSR4, sc->atimq.physaddr); RAL_WRITE(sc, RT2560_TXCSR6, sc->bcnq.physaddr); /* setup rx ring */ tmp = RT2560_RX_RING_COUNT << 8 | RT2560_RX_DESC_SIZE; RAL_WRITE(sc, RT2560_RXCSR1, tmp); RAL_WRITE(sc, RT2560_RXCSR2, sc->rxq.physaddr); /* initialize MAC registers to default values */ for (i = 0; i < N(rt2560_def_mac); i++) RAL_WRITE(sc, rt2560_def_mac[i].reg, rt2560_def_mac[i].val); rt2560_set_macaddr(sc, IF_LLADDR(ifp)); /* set basic rate set (will be updated later) */ RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x153); rt2560_update_slot(ifp); rt2560_update_plcp(sc); rt2560_update_led(sc, 0, 0); RAL_WRITE(sc, RT2560_CSR1, RT2560_RESET_ASIC); RAL_WRITE(sc, RT2560_CSR1, RT2560_HOST_READY); if (rt2560_bbp_init(sc) != 0) { rt2560_stop(sc); RAL_UNLOCK(sc); return; } rt2560_set_txantenna(sc, sc->tx_ant); rt2560_set_rxantenna(sc, sc->rx_ant); /* set default BSS channel */ rt2560_set_chan(sc, ic->ic_curchan); /* kick Rx */ tmp = RT2560_DROP_PHY_ERROR | RT2560_DROP_CRC_ERROR; if (ic->ic_opmode != IEEE80211_M_MONITOR) { tmp |= RT2560_DROP_CTL | RT2560_DROP_VERSION_ERROR; if (ic->ic_opmode != IEEE80211_M_HOSTAP) tmp |= RT2560_DROP_TODS; if (!(ifp->if_flags & IFF_PROMISC)) tmp |= RT2560_DROP_NOT_TO_ME; } RAL_WRITE(sc, RT2560_RXCSR0, tmp); /* clear old FCS and Rx FIFO errors */ RAL_READ(sc, RT2560_CNT0); RAL_READ(sc, RT2560_CNT4); /* clear any pending interrupts */ RAL_WRITE(sc, RT2560_CSR7, 0xffffffff); /* enable interrupts */ RAL_WRITE(sc, RT2560_CSR8, RT2560_INTR_MASK); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; callout_reset(&sc->watchdog_ch, hz, rt2560_watchdog, sc); #undef N } static void rt2560_init(void *priv) { struct rt2560_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; RAL_LOCK(sc); rt2560_init_locked(sc); RAL_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vap's */ } static void rt2560_stop_locked(struct rt2560_softc *sc) { struct ifnet *ifp = sc->sc_ifp; volatile int *flags = &sc->sc_flags; RAL_LOCK_ASSERT(sc); while (*flags & RT2560_F_INPUT_RUNNING) msleep(sc, &sc->sc_mtx, 0, "ralrunning", hz/10); callout_stop(&sc->watchdog_ch); sc->sc_tx_timer = 0; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); /* abort Tx */ RAL_WRITE(sc, RT2560_TXCSR0, RT2560_ABORT_TX); /* disable Rx */ RAL_WRITE(sc, RT2560_RXCSR0, RT2560_DISABLE_RX); /* reset ASIC (imply reset BBP) */ RAL_WRITE(sc, RT2560_CSR1, RT2560_RESET_ASIC); RAL_WRITE(sc, RT2560_CSR1, 0); /* disable interrupts */ RAL_WRITE(sc, RT2560_CSR8, 0xffffffff); /* reset Tx and Rx rings */ rt2560_reset_tx_ring(sc, &sc->txq); rt2560_reset_tx_ring(sc, &sc->atimq); rt2560_reset_tx_ring(sc, &sc->prioq); rt2560_reset_tx_ring(sc, &sc->bcnq); rt2560_reset_rx_ring(sc, &sc->rxq); } sc->sc_flags &= ~(RT2560_F_PRIO_OACTIVE | RT2560_F_DATA_OACTIVE); } void rt2560_stop(void *arg) { struct rt2560_softc *sc = arg; RAL_LOCK(sc); rt2560_stop_locked(sc); RAL_UNLOCK(sc); } static int rt2560_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct rt2560_softc *sc = ifp->if_softc; RAL_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { RAL_UNLOCK(sc); m_freem(m); ieee80211_free_node(ni); return ENETDOWN; } if (sc->prioq.queued >= RT2560_PRIO_RING_COUNT) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; sc->sc_flags |= RT2560_F_PRIO_OACTIVE; RAL_UNLOCK(sc); m_freem(m); ieee80211_free_node(ni); return ENOBUFS; /* XXX */ } ifp->if_opackets++; if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ if (rt2560_tx_mgt(sc, m, ni) != 0) goto bad; } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ if (rt2560_tx_raw(sc, m, ni, params)) goto bad; } sc->sc_tx_timer = 5; RAL_UNLOCK(sc); return 0; bad: ifp->if_oerrors++; ieee80211_free_node(ni); RAL_UNLOCK(sc); return EIO; /* XXX */ } Index: head/sys/dev/usb/wlan/if_rum.c =================================================================== --- head/sys/dev/usb/wlan/if_rum.c (revision 193072) +++ head/sys/dev/usb/wlan/if_rum.c (revision 193073) @@ -1,2363 +1,2363 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2005-2007 Damien Bergamini * Copyright (c) 2006 Niall O'Higgins * Copyright (c) 2007-2008 Hans Petter Selasky * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2501USB/RT2601USB chipset driver * http://www.ralinktech.com.tw/ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #include #include #include #include #define USB_DEBUG_VAR rum_debug #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #include #if USB_DEBUG static int rum_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum"); SYSCTL_INT(_hw_usb_rum, OID_AUTO, debug, CTLFLAG_RW, &rum_debug, 0, "Debug level"); #endif static const struct usb_device_id rum_devs[] = { { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM) }, { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2) }, { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3) }, { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4) }, { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700) }, { USB_VP(USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO) }, { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1) }, { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2) }, { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A) }, { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3) }, { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC) }, { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR) }, { USB_VP(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2) }, { USB_VP(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL) }, { USB_VP(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX) }, { USB_VP(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F) }, { USB_VP(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573) }, { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1) }, { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340) }, { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111) }, { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110) }, { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS) }, { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS) }, { USB_VP(USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573) }, { USB_VP(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573) }, { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB) }, { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP) }, { USB_VP(USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G) }, { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP) }, { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP) }, { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1) }, { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2) }, { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3) }, { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4) }, { USB_VP(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573) }, { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP) }, { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2) }, { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM) }, { USB_VP(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573) }, { USB_VP(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2) }, { USB_VP(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_3) }, { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573) }, { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2) }, { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671) }, { USB_VP(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2) }, { USB_VP(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172) }, { USB_VP(USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573) }, { USB_VP(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573) }, }; MODULE_DEPEND(rum, wlan, 1, 1, 1); MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1); MODULE_DEPEND(rum, usb, 1, 1, 1); static device_probe_t rum_match; static device_attach_t rum_attach; static device_detach_t rum_detach; static usb_callback_t rum_bulk_read_callback; static usb_callback_t rum_bulk_write_callback; static usb_error_t rum_do_request(struct rum_softc *sc, struct usb_device_request *req, void *data); static struct ieee80211vap *rum_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void rum_vap_delete(struct ieee80211vap *); static void rum_tx_free(struct rum_tx_data *, int); static void rum_setup_tx_list(struct rum_softc *); static void rum_unsetup_tx_list(struct rum_softc *); static int rum_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void rum_setup_tx_desc(struct rum_softc *, struct rum_tx_desc *, uint32_t, uint16_t, int, int); static int rum_tx_mgt(struct rum_softc *, struct mbuf *, struct ieee80211_node *); static int rum_tx_raw(struct rum_softc *, struct mbuf *, struct ieee80211_node *, const struct ieee80211_bpf_params *); static int rum_tx_data(struct rum_softc *, struct mbuf *, struct ieee80211_node *); static void rum_start(struct ifnet *); static int rum_ioctl(struct ifnet *, u_long, caddr_t); static void rum_eeprom_read(struct rum_softc *, uint16_t, void *, int); static uint32_t rum_read(struct rum_softc *, uint16_t); static void rum_read_multi(struct rum_softc *, uint16_t, void *, int); static usb_error_t rum_write(struct rum_softc *, uint16_t, uint32_t); static usb_error_t rum_write_multi(struct rum_softc *, uint16_t, void *, size_t); static void rum_bbp_write(struct rum_softc *, uint8_t, uint8_t); static uint8_t rum_bbp_read(struct rum_softc *, uint8_t); static void rum_rf_write(struct rum_softc *, uint8_t, uint32_t); static void rum_select_antenna(struct rum_softc *); static void rum_enable_mrr(struct rum_softc *); static void rum_set_txpreamble(struct rum_softc *); static void rum_set_basicrates(struct rum_softc *); static void rum_select_band(struct rum_softc *, struct ieee80211_channel *); static void rum_set_chan(struct rum_softc *, struct ieee80211_channel *); static void rum_enable_tsf_sync(struct rum_softc *); static void rum_enable_tsf(struct rum_softc *); static void rum_update_slot(struct ifnet *); static void rum_set_bssid(struct rum_softc *, const uint8_t *); static void rum_set_macaddr(struct rum_softc *, const uint8_t *); static void rum_update_promisc(struct ifnet *); static void rum_setpromisc(struct rum_softc *); static const char *rum_get_rf(int); static void rum_read_eeprom(struct rum_softc *); static int rum_bbp_init(struct rum_softc *); static void rum_init_locked(struct rum_softc *); static void rum_init(void *); static void rum_stop(struct rum_softc *); static void rum_load_microcode(struct rum_softc *, const uint8_t *, size_t); static int rum_prepare_beacon(struct rum_softc *, struct ieee80211vap *); static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static struct ieee80211_node *rum_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static void rum_newassoc(struct ieee80211_node *, int); static void rum_scan_start(struct ieee80211com *); static void rum_scan_end(struct ieee80211com *); static void rum_set_channel(struct ieee80211com *); static int rum_get_rssi(struct rum_softc *, uint8_t); static void rum_amrr_start(struct rum_softc *, struct ieee80211_node *); static void rum_amrr_timeout(void *); static void rum_amrr_task(void *, int); static int rum_pause(struct rum_softc *, int); static const struct { uint32_t reg; uint32_t val; } rum_def_mac[] = { { RT2573_TXRX_CSR0, 0x025fb032 }, { RT2573_TXRX_CSR1, 0x9eaa9eaf }, { RT2573_TXRX_CSR2, 0x8a8b8c8d }, { RT2573_TXRX_CSR3, 0x00858687 }, { RT2573_TXRX_CSR7, 0x2e31353b }, { RT2573_TXRX_CSR8, 0x2a2a2a2c }, { RT2573_TXRX_CSR15, 0x0000000f }, { RT2573_MAC_CSR6, 0x00000fff }, { RT2573_MAC_CSR8, 0x016c030a }, { RT2573_MAC_CSR10, 0x00000718 }, { RT2573_MAC_CSR12, 0x00000004 }, { RT2573_MAC_CSR13, 0x00007f00 }, { RT2573_SEC_CSR0, 0x00000000 }, { RT2573_SEC_CSR1, 0x00000000 }, { RT2573_SEC_CSR5, 0x00000000 }, { RT2573_PHY_CSR1, 0x000023b0 }, { RT2573_PHY_CSR5, 0x00040a06 }, { RT2573_PHY_CSR6, 0x00080606 }, { RT2573_PHY_CSR7, 0x00000408 }, { RT2573_AIFSN_CSR, 0x00002273 }, { RT2573_CWMIN_CSR, 0x00002344 }, { RT2573_CWMAX_CSR, 0x000034aa } }; static const struct { uint8_t reg; uint8_t val; } rum_def_bbp[] = { { 3, 0x80 }, { 15, 0x30 }, { 17, 0x20 }, { 21, 0xc8 }, { 22, 0x38 }, { 23, 0x06 }, { 24, 0xfe }, { 25, 0x0a }, { 26, 0x0d }, { 32, 0x0b }, { 34, 0x12 }, { 37, 0x07 }, { 39, 0xf8 }, { 41, 0x60 }, { 53, 0x10 }, { 54, 0x18 }, { 60, 0x10 }, { 61, 0x04 }, { 62, 0x04 }, { 75, 0xfe }, { 86, 0xfe }, { 88, 0xfe }, { 90, 0x0f }, { 99, 0x00 }, { 102, 0x16 }, { 107, 0x04 } }; static const struct rfprog { uint8_t chan; uint32_t r1, r2, r3, r4; } rum_rf5226[] = { { 1, 0x00b03, 0x001e1, 0x1a014, 0x30282 }, { 2, 0x00b03, 0x001e1, 0x1a014, 0x30287 }, { 3, 0x00b03, 0x001e2, 0x1a014, 0x30282 }, { 4, 0x00b03, 0x001e2, 0x1a014, 0x30287 }, { 5, 0x00b03, 0x001e3, 0x1a014, 0x30282 }, { 6, 0x00b03, 0x001e3, 0x1a014, 0x30287 }, { 7, 0x00b03, 0x001e4, 0x1a014, 0x30282 }, { 8, 0x00b03, 0x001e4, 0x1a014, 0x30287 }, { 9, 0x00b03, 0x001e5, 0x1a014, 0x30282 }, { 10, 0x00b03, 0x001e5, 0x1a014, 0x30287 }, { 11, 0x00b03, 0x001e6, 0x1a014, 0x30282 }, { 12, 0x00b03, 0x001e6, 0x1a014, 0x30287 }, { 13, 0x00b03, 0x001e7, 0x1a014, 0x30282 }, { 14, 0x00b03, 0x001e8, 0x1a014, 0x30284 }, { 34, 0x00b03, 0x20266, 0x36014, 0x30282 }, { 38, 0x00b03, 0x20267, 0x36014, 0x30284 }, { 42, 0x00b03, 0x20268, 0x36014, 0x30286 }, { 46, 0x00b03, 0x20269, 0x36014, 0x30288 }, { 36, 0x00b03, 0x00266, 0x26014, 0x30288 }, { 40, 0x00b03, 0x00268, 0x26014, 0x30280 }, { 44, 0x00b03, 0x00269, 0x26014, 0x30282 }, { 48, 0x00b03, 0x0026a, 0x26014, 0x30284 }, { 52, 0x00b03, 0x0026b, 0x26014, 0x30286 }, { 56, 0x00b03, 0x0026c, 0x26014, 0x30288 }, { 60, 0x00b03, 0x0026e, 0x26014, 0x30280 }, { 64, 0x00b03, 0x0026f, 0x26014, 0x30282 }, { 100, 0x00b03, 0x0028a, 0x2e014, 0x30280 }, { 104, 0x00b03, 0x0028b, 0x2e014, 0x30282 }, { 108, 0x00b03, 0x0028c, 0x2e014, 0x30284 }, { 112, 0x00b03, 0x0028d, 0x2e014, 0x30286 }, { 116, 0x00b03, 0x0028e, 0x2e014, 0x30288 }, { 120, 0x00b03, 0x002a0, 0x2e014, 0x30280 }, { 124, 0x00b03, 0x002a1, 0x2e014, 0x30282 }, { 128, 0x00b03, 0x002a2, 0x2e014, 0x30284 }, { 132, 0x00b03, 0x002a3, 0x2e014, 0x30286 }, { 136, 0x00b03, 0x002a4, 0x2e014, 0x30288 }, { 140, 0x00b03, 0x002a6, 0x2e014, 0x30280 }, { 149, 0x00b03, 0x002a8, 0x2e014, 0x30287 }, { 153, 0x00b03, 0x002a9, 0x2e014, 0x30289 }, { 157, 0x00b03, 0x002ab, 0x2e014, 0x30281 }, { 161, 0x00b03, 0x002ac, 0x2e014, 0x30283 }, { 165, 0x00b03, 0x002ad, 0x2e014, 0x30285 } }, rum_rf5225[] = { { 1, 0x00b33, 0x011e1, 0x1a014, 0x30282 }, { 2, 0x00b33, 0x011e1, 0x1a014, 0x30287 }, { 3, 0x00b33, 0x011e2, 0x1a014, 0x30282 }, { 4, 0x00b33, 0x011e2, 0x1a014, 0x30287 }, { 5, 0x00b33, 0x011e3, 0x1a014, 0x30282 }, { 6, 0x00b33, 0x011e3, 0x1a014, 0x30287 }, { 7, 0x00b33, 0x011e4, 0x1a014, 0x30282 }, { 8, 0x00b33, 0x011e4, 0x1a014, 0x30287 }, { 9, 0x00b33, 0x011e5, 0x1a014, 0x30282 }, { 10, 0x00b33, 0x011e5, 0x1a014, 0x30287 }, { 11, 0x00b33, 0x011e6, 0x1a014, 0x30282 }, { 12, 0x00b33, 0x011e6, 0x1a014, 0x30287 }, { 13, 0x00b33, 0x011e7, 0x1a014, 0x30282 }, { 14, 0x00b33, 0x011e8, 0x1a014, 0x30284 }, { 34, 0x00b33, 0x01266, 0x26014, 0x30282 }, { 38, 0x00b33, 0x01267, 0x26014, 0x30284 }, { 42, 0x00b33, 0x01268, 0x26014, 0x30286 }, { 46, 0x00b33, 0x01269, 0x26014, 0x30288 }, { 36, 0x00b33, 0x01266, 0x26014, 0x30288 }, { 40, 0x00b33, 0x01268, 0x26014, 0x30280 }, { 44, 0x00b33, 0x01269, 0x26014, 0x30282 }, { 48, 0x00b33, 0x0126a, 0x26014, 0x30284 }, { 52, 0x00b33, 0x0126b, 0x26014, 0x30286 }, { 56, 0x00b33, 0x0126c, 0x26014, 0x30288 }, { 60, 0x00b33, 0x0126e, 0x26014, 0x30280 }, { 64, 0x00b33, 0x0126f, 0x26014, 0x30282 }, { 100, 0x00b33, 0x0128a, 0x2e014, 0x30280 }, { 104, 0x00b33, 0x0128b, 0x2e014, 0x30282 }, { 108, 0x00b33, 0x0128c, 0x2e014, 0x30284 }, { 112, 0x00b33, 0x0128d, 0x2e014, 0x30286 }, { 116, 0x00b33, 0x0128e, 0x2e014, 0x30288 }, { 120, 0x00b33, 0x012a0, 0x2e014, 0x30280 }, { 124, 0x00b33, 0x012a1, 0x2e014, 0x30282 }, { 128, 0x00b33, 0x012a2, 0x2e014, 0x30284 }, { 132, 0x00b33, 0x012a3, 0x2e014, 0x30286 }, { 136, 0x00b33, 0x012a4, 0x2e014, 0x30288 }, { 140, 0x00b33, 0x012a6, 0x2e014, 0x30280 }, { 149, 0x00b33, 0x012a8, 0x2e014, 0x30287 }, { 153, 0x00b33, 0x012a9, 0x2e014, 0x30289 }, { 157, 0x00b33, 0x012ab, 0x2e014, 0x30281 }, { 161, 0x00b33, 0x012ac, 0x2e014, 0x30283 }, { 165, 0x00b33, 0x012ad, 0x2e014, 0x30285 } }; static const struct usb_config rum_config[RUM_N_TRANSFER] = { [RUM_BULK_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = (MCLBYTES + RT2573_TX_DESC_SIZE + 8), .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = rum_bulk_write_callback, .timeout = 5000, /* ms */ }, [RUM_BULK_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = (MCLBYTES + RT2573_RX_DESC_SIZE), .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = rum_bulk_read_callback, }, }; static int rum_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != 0) return (ENXIO); if (uaa->info.bIfaceIndex != RT2573_IFACE_INDEX) return (ENXIO); return (usb2_lookup_id_by_uaa(rum_devs, sizeof(rum_devs), uaa)); } static int rum_attach(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); struct rum_softc *sc = device_get_softc(self); struct ieee80211com *ic; struct ifnet *ifp; uint8_t iface_index, bands; uint32_t tmp; int error, ntries; device_set_usb2_desc(self); sc->sc_udev = uaa->device; sc->sc_dev = self; mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF); iface_index = RT2573_IFACE_INDEX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, rum_config, RUM_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(self, "could not allocate USB transfers, " "err=%s\n", usb2_errstr(error)); goto detach; } RUM_LOCK(sc); /* retrieve RT2573 rev. no */ for (ntries = 0; ntries < 100; ntries++) { if ((tmp = rum_read(sc, RT2573_MAC_CSR0)) != 0) break; if (rum_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for chip to settle\n"); RUM_UNLOCK(sc); goto detach; } /* retrieve MAC address and various other things from EEPROM */ rum_read_eeprom(sc); device_printf(sc->sc_dev, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n", tmp, rum_get_rf(sc->rf_rev)); rum_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode)); RUM_UNLOCK(sc); ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(sc->sc_dev, "can not if_alloc()\n"); goto detach; } ic = ifp->if_l2com; ifp->if_softc = sc; if_initname(ifp, "rum", device_get_unit(sc->sc_dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = rum_init; ifp->if_ioctl = rum_ioctl; ifp->if_start = rum_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_HOSTAP /* HostAp mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* bg scanning supported */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) setbit(&bands, IEEE80211_MODE_11A); ieee80211_init_channels(ic, NULL, &bands); ieee80211_ifattach(ic, sc->sc_bssid); ic->ic_update_promisc = rum_update_promisc; ic->ic_newassoc = rum_newassoc; ic->ic_raw_xmit = rum_raw_xmit; ic->ic_node_alloc = rum_node_alloc; ic->ic_scan_start = rum_scan_start; ic->ic_scan_end = rum_scan_end; ic->ic_set_channel = rum_set_channel; ic->ic_vap_create = rum_vap_create; ic->ic_vap_delete = rum_vap_delete; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), RT2573_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), RT2573_RX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); return (0); detach: rum_detach(self); return (ENXIO); /* failure */ } static int rum_detach(device_t self) { struct rum_softc *sc = device_get_softc(self); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic; /* stop all USB transfers */ usb2_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER); /* free TX list, if any */ RUM_LOCK(sc); rum_unsetup_tx_list(sc); RUM_UNLOCK(sc); if (ifp) { ic = ifp->if_l2com; ieee80211_ifdetach(ic); if_free(ifp); } mtx_destroy(&sc->sc_mtx); return (0); } static usb_error_t rum_do_request(struct rum_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; while (ntries--) { err = usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 250 /* ms */); if (err == 0) break; DPRINTFN(1, "Control request failed, %s (retrying)\n", usb2_errstr(err)); if (rum_pause(sc, hz / 100)) break; } return (err); } static struct ieee80211vap * rum_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct rum_softc *sc = ic->ic_ifp->if_softc; struct rum_vap *rvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; rvp = (struct rum_vap *) malloc(sizeof(struct rum_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (rvp == NULL) return NULL; vap = &rvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ rvp->newstate = vap->iv_newstate; vap->iv_newstate = rum_newstate; usb2_callout_init_mtx(&rvp->amrr_ch, &sc->sc_mtx, 0); TASK_INIT(&rvp->amrr_task, 0, rum_amrr_task, rvp); ieee80211_amrr_init(&rvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return vap; } static void rum_vap_delete(struct ieee80211vap *vap) { struct rum_vap *rvp = RUM_VAP(vap); struct ieee80211com *ic = vap->iv_ic; usb2_callout_drain(&rvp->amrr_ch); ieee80211_draintask(ic, &rvp->amrr_task); ieee80211_amrr_cleanup(&rvp->amrr); ieee80211_vap_detach(vap); free(rvp, M_80211_VAP); } static void rum_tx_free(struct rum_tx_data *data, int txerr) { struct rum_softc *sc = data->sc; if (data->m != NULL) { if (data->m->m_flags & M_TXCB) ieee80211_process_callback(data->ni, data->m, txerr ? ETIMEDOUT : 0); m_freem(data->m); data->m = NULL; ieee80211_free_node(data->ni); data->ni = NULL; } STAILQ_INSERT_TAIL(&sc->tx_free, data, next); sc->tx_nfree++; } static void rum_setup_tx_list(struct rum_softc *sc) { struct rum_tx_data *data; int i; sc->tx_nfree = 0; STAILQ_INIT(&sc->tx_q); STAILQ_INIT(&sc->tx_free); for (i = 0; i < RUM_TX_LIST_COUNT; i++) { data = &sc->tx_data[i]; data->sc = sc; STAILQ_INSERT_TAIL(&sc->tx_free, data, next); sc->tx_nfree++; } } static void rum_unsetup_tx_list(struct rum_softc *sc) { struct rum_tx_data *data; int i; /* make sure any subsequent use of the queues will fail */ sc->tx_nfree = 0; STAILQ_INIT(&sc->tx_q); STAILQ_INIT(&sc->tx_free); /* free up all node references and mbufs */ for (i = 0; i < RUM_TX_LIST_COUNT; i++) { data = &sc->tx_data[i]; if (data->m != NULL) { m_freem(data->m); data->m = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } } static int rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct rum_vap *rvp = RUM_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct rum_softc *sc = ic->ic_ifp->if_softc; const struct ieee80211_txparam *tp; enum ieee80211_state ostate; struct ieee80211_node *ni; uint32_t tmp; ostate = vap->iv_state; DPRINTF("%s -> %s\n", ieee80211_state_name[ostate], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); RUM_LOCK(sc); usb2_callout_stop(&rvp->amrr_ch); switch (nstate) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_RUN) { /* abort TSF synchronization */ tmp = rum_read(sc, RT2573_TXRX_CSR9); rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); } break; case IEEE80211_S_RUN: ni = vap->iv_bss; if (vap->iv_opmode != IEEE80211_M_MONITOR) { rum_update_slot(ic->ic_ifp); rum_enable_mrr(sc); rum_set_txpreamble(sc); rum_set_basicrates(sc); IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); rum_set_bssid(sc, sc->sc_bssid); } if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS) rum_prepare_beacon(sc, vap); if (vap->iv_opmode != IEEE80211_M_MONITOR) rum_enable_tsf_sync(sc); else rum_enable_tsf(sc); /* enable automatic rate adaptation */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) rum_amrr_start(sc, ni); break; default: break; } RUM_UNLOCK(sc); IEEE80211_LOCK(ic); return (rvp->newstate(vap, nstate, arg)); } static void rum_bulk_write_callback(struct usb_xfer *xfer) { struct rum_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap; struct rum_tx_data *data; struct mbuf *m; unsigned int len; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen); /* free resources */ data = xfer->priv_fifo; rum_tx_free(data, 0); xfer->priv_fifo = NULL; ifp->if_opackets++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&sc->tx_q); if (data) { STAILQ_REMOVE_HEAD(&sc->tx_q, next); m = data->m; if (m->m_pkthdr.len > (MCLBYTES + RT2573_TX_DESC_SIZE)) { DPRINTFN(0, "data overflow, %u bytes\n", m->m_pkthdr.len); m->m_pkthdr.len = (MCLBYTES + RT2573_TX_DESC_SIZE); } usb2_copy_in(xfer->frbuffers, 0, &data->desc, RT2573_TX_DESC_SIZE); usb2_m_copy_in(xfer->frbuffers, RT2573_TX_DESC_SIZE, m, 0, m->m_pkthdr.len); vap = data->ni->ni_vap; if (ieee80211_radiotap_active_vap(vap)) { struct rum_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = data->rate; tap->wt_antenna = sc->tx_ant; ieee80211_radiotap_tx(vap, m); } /* align end on a 4-bytes boundary */ len = (RT2573_TX_DESC_SIZE + m->m_pkthdr.len + 3) & ~3; if ((len % 64) == 0) len += 4; DPRINTFN(11, "sending frame len=%u xferlen=%u\n", m->m_pkthdr.len, len); xfer->frlengths[0] = len; xfer->priv_fifo = data; usb2_start_hardware(xfer); } break; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); ifp->if_oerrors++; data = xfer->priv_fifo; if (data != NULL) { rum_tx_free(data, xfer->error); xfer->priv_fifo = NULL; } if (xfer->error == USB_ERR_STALLED) { /* try to clear stall first */ xfer->flags.stall_pipe = 1; goto tr_setup; } if (xfer->error == USB_ERR_TIMEOUT) device_printf(sc->sc_dev, "device timeout\n"); break; } } static void rum_bulk_read_callback(struct usb_xfer *xfer) { struct rum_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni; struct mbuf *m = NULL; uint32_t flags; uint8_t rssi = 0; unsigned int len; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); len = xfer->actlen; if (len < RT2573_RX_DESC_SIZE + IEEE80211_MIN_LEN) { DPRINTF("%s: xfer too short %d\n", device_get_nameunit(sc->sc_dev), len); ifp->if_ierrors++; goto tr_setup; } len -= RT2573_RX_DESC_SIZE; usb2_copy_out(xfer->frbuffers, 0, &sc->sc_rx_desc, RT2573_RX_DESC_SIZE); rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi); flags = le32toh(sc->sc_rx_desc.flags); if (flags & RT2573_RX_CRC_ERROR) { /* * This should not happen since we did not * request to receive those frames when we * filled RUM_TXRX_CSR2: */ DPRINTFN(5, "PHY or CRC error\n"); ifp->if_ierrors++; goto tr_setup; } m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF("could not allocate mbuf\n"); ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, RT2573_RX_DESC_SIZE, mtod(m, uint8_t *), len); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; if (ieee80211_radiotap_active(ic)) { struct rum_rx_radiotap_header *tap = &sc->sc_rxtap; /* XXX read tsf */ tap->wr_flags = 0; tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, (flags & RT2573_RX_OFDM) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_antsignal = RT2573_NOISE_FLOOR + rssi; tap->wr_antnoise = RT2573_NOISE_FLOOR; tap->wr_antenna = sc->rx_ant; } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "ieee80211_input" here, and not some lines up! */ if (m) { RUM_UNLOCK(sc); ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { (void) ieee80211_input(ni, m, rssi, RT2573_NOISE_FLOOR); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi, RT2573_NOISE_FLOOR); RUM_LOCK(sc); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ xfer->flags.stall_pipe = 1; goto tr_setup; } return; } } static uint8_t rum_plcp_signal(int rate) { switch (rate) { /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return 0xb; case 18: return 0xf; case 24: return 0xa; case 36: return 0xe; case 48: return 0x9; case 72: return 0xd; case 96: return 0x8; case 108: return 0xc; /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return 0x0; case 4: return 0x1; case 11: return 0x2; case 22: return 0x3; } return 0xff; /* XXX unsupported/unknown rate */ } static void rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc, uint32_t flags, uint16_t xflags, int len, int rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t plcp_length; int remainder; desc->flags = htole32(flags); desc->flags |= htole32(RT2573_TX_VALID); desc->flags |= htole32(len << 16); desc->xflags = htole16(xflags); desc->wme = htole16(RT2573_QID(0) | RT2573_AIFSN(2) | RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10)); /* setup PLCP fields */ desc->plcp_signal = rum_plcp_signal(rate); desc->plcp_service = 4; len += IEEE80211_CRC_LEN; if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) { desc->flags |= htole32(RT2573_TX_OFDM); plcp_length = len & 0xfff; desc->plcp_length_hi = plcp_length >> 6; desc->plcp_length_lo = plcp_length & 0x3f; } else { plcp_length = (16 * len + rate - 1) / rate; if (rate == 22) { remainder = (16 * len) % 22; if (remainder != 0 && remainder < 7) desc->plcp_service |= RT2573_PLCP_LENGEXT; } desc->plcp_length_hi = plcp_length >> 8; desc->plcp_length_lo = plcp_length & 0xff; if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) desc->plcp_signal |= 0x08; } } static int rum_sendprot(struct rum_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) { struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_frame *wh; struct rum_tx_data *data; struct mbuf *mprot; int protrate, ackrate, pktlen, flags, isshort; uint16_t dur; RUM_LOCK_ASSERT(sc, MA_OWNED); KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, ("protection %d", prot)); wh = mtod(m, const struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; protrate = ieee80211_ctl_rate(ic->ic_rt, rate); ackrate = ieee80211_ack_rate(ic->ic_rt, rate); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort); + ieee80211_ack_duration(ic->ic_rt, rate, isshort); flags = RT2573_TX_MORE_FRAG; if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort); flags |= RT2573_TX_NEED_ACK; mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else { mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { /* XXX stat + msg */ return (ENOBUFS); } data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; data->m = mprot; data->ni = ieee80211_ref_node(ni); data->rate = protrate; rum_setup_tx_desc(sc, &data->desc, flags, 0, mprot->m_pkthdr.len, protrate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); return 0; } static int rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct rum_tx_data *data; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; RUM_LOCK_ASSERT(sc, MA_OWNED); data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } wh = mtod(m0, struct ieee80211_frame *); } tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RT2573_TX_NEED_ACK; dur = ieee80211_ack_duration(ic->ic_rt, tp->mgmtrate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); *(uint16_t *)wh->i_dur = htole16(dur); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) flags |= RT2573_TX_TIMESTAMP; } data->m = m0; data->ni = ni; data->rate = tp->mgmtrate; rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, tp->mgmtrate); DPRINTFN(10, "sending mgt frame len=%d rate=%d\n", m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); return (0); } static int rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { + struct ieee80211com *ic = ni->ni_ic; struct rum_tx_data *data; uint32_t flags; int rate, error; RUM_LOCK_ASSERT(sc, MA_OWNED); KASSERT(params != NULL, ("no raw xmit params")); - rate = params->ibp_rate0 & IEEE80211_RATE_VAL; - /* XXX validate */ - if (rate == 0) { + rate = params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) { m_freem(m0); return EINVAL; } flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= RT2573_TX_NEED_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { error = rum_sendprot(sc, m0, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); if (error || sc->tx_nfree == 0) { m_freem(m0); return ENOBUFS; } flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; } data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; data->m = m0; data->ni = ni; data->rate = rate; /* XXX need to setup descriptor ourself */ rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, rate); DPRINTFN(10, "sending raw frame len=%u rate=%u\n", m0->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); return 0; } static int rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct rum_tx_data *data; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; int error, rate; RUM_LOCK_ASSERT(sc, MA_OWNED); wh = mtod(m0, struct ieee80211_frame *); tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = ni->ni_txrate; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { int prot = IEEE80211_PROT_NONE; if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) prot = IEEE80211_PROT_RTSCTS; else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) prot = ic->ic_protmode; if (prot != IEEE80211_PROT_NONE) { error = rum_sendprot(sc, m0, ni, prot, rate); if (error || sc->tx_nfree == 0) { m_freem(m0); return ENOBUFS; } flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; } } data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; data->m = m0; data->ni = ni; data->rate = rate; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RT2573_TX_NEED_ACK; flags |= RT2573_TX_MORE_FRAG; dur = ieee80211_ack_duration(ic->ic_rt, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); *(uint16_t *)wh->i_dur = htole16(dur); } rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, rate); DPRINTFN(10, "sending frame len=%d rate=%d\n", m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]); return 0; } static void rum_start(struct ifnet *ifp) { struct rum_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct mbuf *m; RUM_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { RUM_UNLOCK(sc); return; } for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; if (sc->tx_nfree < RUM_TX_MINFREE) { IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; if (rum_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; } } RUM_UNLOCK(sc); } static int rum_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct rum_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: RUM_LOCK(sc); if (ifp->if_flags & IFF_UP) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { rum_init_locked(sc); startall = 1; } else rum_setpromisc(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) rum_stop(sc); } RUM_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; default: error = EINVAL; break; } return error; } static void rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2573_READ_EEPROM; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); error = rum_do_request(sc, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not read EEPROM: %s\n", usb2_errstr(error)); } } static uint32_t rum_read(struct rum_softc *sc, uint16_t reg) { uint32_t val; rum_read_multi(sc, reg, &val, sizeof val); return le32toh(val); } static void rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RT2573_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); error = rum_do_request(sc, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not multi read MAC register: %s\n", usb2_errstr(error)); } } static usb_error_t rum_write(struct rum_softc *sc, uint16_t reg, uint32_t val) { uint32_t tmp = htole32(val); return (rum_write_multi(sc, reg, &tmp, sizeof tmp)); } static usb_error_t rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2573_WRITE_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); error = rum_do_request(sc, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not multi write MAC register: %s\n", usb2_errstr(error)); } return (error); } static void rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val) { uint32_t tmp; int ntries; DPRINTFN(2, "reg=0x%08x\n", reg); for (ntries = 0; ntries < 100; ntries++) { if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY)) break; if (rum_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "could not write to BBP\n"); return; } tmp = RT2573_BBP_BUSY | (reg & 0x7f) << 8 | val; rum_write(sc, RT2573_PHY_CSR3, tmp); } static uint8_t rum_bbp_read(struct rum_softc *sc, uint8_t reg) { uint32_t val; int ntries; DPRINTFN(2, "reg=0x%08x\n", reg); for (ntries = 0; ntries < 100; ntries++) { if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY)) break; if (rum_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "could not read BBP\n"); return 0; } val = RT2573_BBP_BUSY | RT2573_BBP_READ | reg << 8; rum_write(sc, RT2573_PHY_CSR3, val); for (ntries = 0; ntries < 100; ntries++) { val = rum_read(sc, RT2573_PHY_CSR3); if (!(val & RT2573_BBP_BUSY)) return val & 0xff; if (rum_pause(sc, hz / 100)) break; } device_printf(sc->sc_dev, "could not read BBP\n"); return 0; } static void rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val) { uint32_t tmp; int ntries; for (ntries = 0; ntries < 100; ntries++) { if (!(rum_read(sc, RT2573_PHY_CSR4) & RT2573_RF_BUSY)) break; if (rum_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "could not write to RF\n"); return; } tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | (val & 0xfffff) << 2 | (reg & 3); rum_write(sc, RT2573_PHY_CSR4, tmp); /* remember last written value in sc */ sc->rf_regs[reg] = val; DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 3, val & 0xfffff); } static void rum_select_antenna(struct rum_softc *sc) { uint8_t bbp4, bbp77; uint32_t tmp; bbp4 = rum_bbp_read(sc, 4); bbp77 = rum_bbp_read(sc, 77); /* TBD */ /* make sure Rx is disabled before switching antenna */ tmp = rum_read(sc, RT2573_TXRX_CSR0); rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); rum_bbp_write(sc, 4, bbp4); rum_bbp_write(sc, 77, bbp77); rum_write(sc, RT2573_TXRX_CSR0, tmp); } /* * Enable multi-rate retries for frames sent at OFDM rates. * In 802.11b/g mode, allow fallback to CCK rates. */ static void rum_enable_mrr(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t tmp; tmp = rum_read(sc, RT2573_TXRX_CSR4); tmp &= ~RT2573_MRR_CCK_FALLBACK; if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) tmp |= RT2573_MRR_CCK_FALLBACK; tmp |= RT2573_MRR_ENABLED; rum_write(sc, RT2573_TXRX_CSR4, tmp); } static void rum_set_txpreamble(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t tmp; tmp = rum_read(sc, RT2573_TXRX_CSR4); tmp &= ~RT2573_SHORT_PREAMBLE; if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) tmp |= RT2573_SHORT_PREAMBLE; rum_write(sc, RT2573_TXRX_CSR4, tmp); } static void rum_set_basicrates(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; /* update basic rate set */ if (ic->ic_curmode == IEEE80211_MODE_11B) { /* 11b basic rates: 1, 2Mbps */ rum_write(sc, RT2573_TXRX_CSR5, 0x3); } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) { /* 11a basic rates: 6, 12, 24Mbps */ rum_write(sc, RT2573_TXRX_CSR5, 0x150); } else { /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ rum_write(sc, RT2573_TXRX_CSR5, 0xf); } } /* * Reprogram MAC/BBP to switch to a new band. Values taken from the reference * driver. */ static void rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c) { uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104; uint32_t tmp; /* update all BBP registers that depend on the band */ bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c; bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48; if (IEEE80211_IS_CHAN_5GHZ(c)) { bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c; bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10; } if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10; } sc->bbp17 = bbp17; rum_bbp_write(sc, 17, bbp17); rum_bbp_write(sc, 96, bbp96); rum_bbp_write(sc, 104, bbp104); if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { rum_bbp_write(sc, 75, 0x80); rum_bbp_write(sc, 86, 0x80); rum_bbp_write(sc, 88, 0x80); } rum_bbp_write(sc, 35, bbp35); rum_bbp_write(sc, 97, bbp97); rum_bbp_write(sc, 98, bbp98); tmp = rum_read(sc, RT2573_PHY_CSR0); tmp &= ~(RT2573_PA_PE_2GHZ | RT2573_PA_PE_5GHZ); if (IEEE80211_IS_CHAN_2GHZ(c)) tmp |= RT2573_PA_PE_2GHZ; else tmp |= RT2573_PA_PE_5GHZ; rum_write(sc, RT2573_PHY_CSR0, tmp); } static void rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; const struct rfprog *rfprog; uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT; int8_t power; int i, chan; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) return; /* select the appropriate RF settings based on what EEPROM says */ rfprog = (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527) ? rum_rf5225 : rum_rf5226; /* find the settings for this channel (we know it exists) */ for (i = 0; rfprog[i].chan != chan; i++); power = sc->txpow[i]; if (power < 0) { bbp94 += power; power = 0; } else if (power > 31) { bbp94 += power - 31; power = 31; } /* * If we are switching from the 2GHz band to the 5GHz band or * vice-versa, BBP registers need to be reprogrammed. */ if (c->ic_flags != ic->ic_curchan->ic_flags) { rum_select_band(sc, c); rum_select_antenna(sc); } ic->ic_curchan = c; rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7 | 1); rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); rum_pause(sc, hz / 100); /* enable smart mode for MIMO-capable RFs */ bbp3 = rum_bbp_read(sc, 3); bbp3 &= ~RT2573_SMART_MODE; if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527) bbp3 |= RT2573_SMART_MODE; rum_bbp_write(sc, 3, bbp3); if (bbp94 != RT2573_BBPR94_DEFAULT) rum_bbp_write(sc, 94, bbp94); /* give the chip some extra time to do the switchover */ rum_pause(sc, hz / 100); } /* * Enable TSF synchronization and tell h/w to start sending beacons for IBSS * and HostAP operating modes. */ static void rum_enable_tsf_sync(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t tmp; if (vap->iv_opmode != IEEE80211_M_STA) { /* * Change default 16ms TBTT adjustment to 8ms. * Must be done before enabling beacon generation. */ rum_write(sc, RT2573_TXRX_CSR10, 1 << 12 | 8); } tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000; /* set beacon interval (in 1/16ms unit) */ tmp |= vap->iv_bss->ni_intval * 16; tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT; if (vap->iv_opmode == IEEE80211_M_STA) tmp |= RT2573_TSF_MODE(1); else tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON; rum_write(sc, RT2573_TXRX_CSR9, tmp); } static void rum_enable_tsf(struct rum_softc *sc) { rum_write(sc, RT2573_TXRX_CSR9, (rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000) | RT2573_TSF_TICKING | RT2573_TSF_MODE(2)); } static void rum_update_slot(struct ifnet *ifp) { struct rum_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; uint8_t slottime; uint32_t tmp; slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; tmp = rum_read(sc, RT2573_MAC_CSR9); tmp = (tmp & ~0xff) | slottime; rum_write(sc, RT2573_MAC_CSR9, tmp); DPRINTF("setting slot time to %uus\n", slottime); } static void rum_set_bssid(struct rum_softc *sc, const uint8_t *bssid) { uint32_t tmp; tmp = bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24; rum_write(sc, RT2573_MAC_CSR4, tmp); tmp = bssid[4] | bssid[5] << 8 | RT2573_ONE_BSSID << 16; rum_write(sc, RT2573_MAC_CSR5, tmp); } static void rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr) { uint32_t tmp; tmp = addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24; rum_write(sc, RT2573_MAC_CSR2, tmp); tmp = addr[4] | addr[5] << 8 | 0xff << 16; rum_write(sc, RT2573_MAC_CSR3, tmp); } static void rum_setpromisc(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; tmp = rum_read(sc, RT2573_TXRX_CSR0); tmp &= ~RT2573_DROP_NOT_TO_ME; if (!(ifp->if_flags & IFF_PROMISC)) tmp |= RT2573_DROP_NOT_TO_ME; rum_write(sc, RT2573_TXRX_CSR0, tmp); DPRINTF("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ? "entering" : "leaving"); } static void rum_update_promisc(struct ifnet *ifp) { struct rum_softc *sc = ifp->if_softc; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; RUM_LOCK(sc); rum_setpromisc(sc); RUM_UNLOCK(sc); } static const char * rum_get_rf(int rev) { switch (rev) { case RT2573_RF_2527: return "RT2527 (MIMO XR)"; case RT2573_RF_2528: return "RT2528"; case RT2573_RF_5225: return "RT5225 (MIMO XR)"; case RT2573_RF_5226: return "RT5226"; default: return "unknown"; } } static void rum_read_eeprom(struct rum_softc *sc) { uint16_t val; #ifdef RUM_DEBUG int i; #endif /* read MAC address */ rum_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_bssid, 6); rum_eeprom_read(sc, RT2573_EEPROM_ANTENNA, &val, 2); val = le16toh(val); sc->rf_rev = (val >> 11) & 0x1f; sc->hw_radio = (val >> 10) & 0x1; sc->rx_ant = (val >> 4) & 0x3; sc->tx_ant = (val >> 2) & 0x3; sc->nb_ant = val & 0x3; DPRINTF("RF revision=%d\n", sc->rf_rev); rum_eeprom_read(sc, RT2573_EEPROM_CONFIG2, &val, 2); val = le16toh(val); sc->ext_5ghz_lna = (val >> 6) & 0x1; sc->ext_2ghz_lna = (val >> 4) & 0x1; DPRINTF("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n", sc->ext_2ghz_lna, sc->ext_5ghz_lna); rum_eeprom_read(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET, &val, 2); val = le16toh(val); if ((val & 0xff) != 0xff) sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ /* Only [-10, 10] is valid */ if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10) sc->rssi_2ghz_corr = 0; rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2); val = le16toh(val); if ((val & 0xff) != 0xff) sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ /* Only [-10, 10] is valid */ if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10) sc->rssi_5ghz_corr = 0; if (sc->ext_2ghz_lna) sc->rssi_2ghz_corr -= 14; if (sc->ext_5ghz_lna) sc->rssi_5ghz_corr -= 14; DPRINTF("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n", sc->rssi_2ghz_corr, sc->rssi_5ghz_corr); rum_eeprom_read(sc, RT2573_EEPROM_FREQ_OFFSET, &val, 2); val = le16toh(val); if ((val & 0xff) != 0xff) sc->rffreq = val & 0xff; DPRINTF("RF freq=%d\n", sc->rffreq); /* read Tx power for all a/b/g channels */ rum_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->txpow, 14); /* XXX default Tx power for 802.11a channels */ memset(sc->txpow + 14, 24, sizeof (sc->txpow) - 14); #ifdef RUM_DEBUG for (i = 0; i < 14; i++) DPRINTF("Channel=%d Tx power=%d\n", i + 1, sc->txpow[i]); #endif /* read default values for BBP registers */ rum_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); #ifdef RUM_DEBUG for (i = 0; i < 14; i++) { if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) continue; DPRINTF("BBP R%d=%02x\n", sc->bbp_prom[i].reg, sc->bbp_prom[i].val); } #endif } static int rum_bbp_init(struct rum_softc *sc) { #define N(a) (sizeof (a) / sizeof ((a)[0])) int i, ntries; /* wait for BBP to be ready */ for (ntries = 0; ntries < 100; ntries++) { const uint8_t val = rum_bbp_read(sc, 0); if (val != 0 && val != 0xff) break; if (rum_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for BBP\n"); return EIO; } /* initialize BBP registers to default values */ for (i = 0; i < N(rum_def_bbp); i++) rum_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val); /* write vendor-specific BBP values (from EEPROM) */ for (i = 0; i < 16; i++) { if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) continue; rum_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); } return 0; #undef N } static void rum_init_locked(struct rum_softc *sc) { #define N(a) (sizeof (a) / sizeof ((a)[0])) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t tmp; usb_error_t error; int i, ntries; RUM_LOCK_ASSERT(sc, MA_OWNED); rum_stop(sc); /* initialize MAC registers to default values */ for (i = 0; i < N(rum_def_mac); i++) rum_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val); /* set host ready */ rum_write(sc, RT2573_MAC_CSR1, 3); rum_write(sc, RT2573_MAC_CSR1, 0); /* wait for BBP/RF to wakeup */ for (ntries = 0; ntries < 100; ntries++) { if (rum_read(sc, RT2573_MAC_CSR12) & 8) break; rum_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */ if (rum_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for BBP/RF to wakeup\n"); goto fail; } if ((error = rum_bbp_init(sc)) != 0) goto fail; /* select default channel */ rum_select_band(sc, ic->ic_curchan); rum_select_antenna(sc); rum_set_chan(sc, ic->ic_curchan); /* clear STA registers */ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); rum_set_macaddr(sc, IF_LLADDR(ifp)); /* initialize ASIC */ rum_write(sc, RT2573_MAC_CSR1, 4); /* * Allocate Tx and Rx xfer queues. */ rum_setup_tx_list(sc); /* update Rx filter */ tmp = rum_read(sc, RT2573_TXRX_CSR0) & 0xffff; tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR; if (ic->ic_opmode != IEEE80211_M_MONITOR) { tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR | RT2573_DROP_ACKCTS; if (ic->ic_opmode != IEEE80211_M_HOSTAP) tmp |= RT2573_DROP_TODS; if (!(ifp->if_flags & IFF_PROMISC)) tmp |= RT2573_DROP_NOT_TO_ME; } rum_write(sc, RT2573_TXRX_CSR0, tmp); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; usb2_transfer_set_stall(sc->sc_xfer[RUM_BULK_WR]); usb2_transfer_start(sc->sc_xfer[RUM_BULK_RD]); return; fail: rum_stop(sc); #undef N } static void rum_init(void *priv) { struct rum_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; RUM_LOCK(sc); rum_init_locked(sc); RUM_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vap's */ } static void rum_stop(struct rum_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; RUM_LOCK_ASSERT(sc, MA_OWNED); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); RUM_UNLOCK(sc); /* * Drain the USB transfers, if not already drained: */ usb2_transfer_drain(sc->sc_xfer[RUM_BULK_WR]); usb2_transfer_drain(sc->sc_xfer[RUM_BULK_RD]); RUM_LOCK(sc); rum_unsetup_tx_list(sc); /* disable Rx */ tmp = rum_read(sc, RT2573_TXRX_CSR0); rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); /* reset ASIC */ rum_write(sc, RT2573_MAC_CSR1, 3); rum_write(sc, RT2573_MAC_CSR1, 0); } static void rum_load_microcode(struct rum_softc *sc, const uint8_t *ucode, size_t size) { struct usb_device_request req; uint16_t reg = RT2573_MCU_CODE_BASE; usb_error_t err; /* copy firmware image into NIC */ for (; size >= 4; reg += 4, ucode += 4, size -= 4) { err = rum_write(sc, reg, UGETDW(ucode)); if (err) { /* firmware already loaded ? */ device_printf(sc->sc_dev, "Firmware load " "failure! (ignored)\n"); break; } } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RT2573_MCU_CNTL; USETW(req.wValue, RT2573_MCU_RUN); USETW(req.wIndex, 0); USETW(req.wLength, 0); err = rum_do_request(sc, &req, NULL); if (err != 0) { device_printf(sc->sc_dev, "could not run firmware: %s\n", usb2_errstr(err)); } /* give the chip some time to boot */ rum_pause(sc, hz / 8); } static int rum_prepare_beacon(struct rum_softc *sc, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_txparam *tp; struct rum_tx_desc desc; struct mbuf *m0; m0 = ieee80211_beacon_alloc(vap->iv_bss, &RUM_VAP(vap)->bo); if (m0 == NULL) { return ENOBUFS; } tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; rum_setup_tx_desc(sc, &desc, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ, m0->m_pkthdr.len, tp->mgmtrate); /* copy the first 24 bytes of Tx descriptor into NIC memory */ rum_write_multi(sc, RT2573_HW_BEACON_BASE0, (uint8_t *)&desc, 24); /* copy beacon header and payload into NIC memory */ rum_write_multi(sc, RT2573_HW_BEACON_BASE0 + 24, mtod(m0, uint8_t *), m0->m_pkthdr.len); m_freem(m0); return 0; } static int rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ifnet *ifp = ni->ni_ic->ic_ifp; struct rum_softc *sc = ifp->if_softc; RUM_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { RUM_UNLOCK(sc); m_freem(m); ieee80211_free_node(ni); return ENETDOWN; } if (sc->tx_nfree < RUM_TX_MINFREE) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; RUM_UNLOCK(sc); m_freem(m); ieee80211_free_node(ni); return EIO; } ifp->if_opackets++; if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ if (rum_tx_mgt(sc, m, ni) != 0) goto bad; } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ if (rum_tx_raw(sc, m, ni, params) != 0) goto bad; } RUM_UNLOCK(sc); return 0; bad: ifp->if_oerrors++; RUM_UNLOCK(sc); ieee80211_free_node(ni); return EIO; } static void rum_amrr_start(struct rum_softc *sc, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct rum_vap *rvp = RUM_VAP(vap); /* clear statistic registers (STA_CSR0 to STA_CSR5) */ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); ieee80211_amrr_node_init(&rvp->amrr, &RUM_NODE(ni)->amn, ni); usb2_callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, rvp); } static void rum_amrr_timeout(void *arg) { struct rum_vap *rvp = arg; struct ieee80211vap *vap = &rvp->vap; struct ieee80211com *ic = vap->iv_ic; ieee80211_runtask(ic, &rvp->amrr_task); } static void rum_amrr_task(void *arg, int pending) { struct rum_vap *rvp = arg; struct ieee80211vap *vap = &rvp->vap; struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct rum_softc *sc = ifp->if_softc; struct ieee80211_node *ni = vap->iv_bss; int ok, fail; RUM_LOCK(sc); /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof(sc->sta)); ok = (le32toh(sc->sta[4]) >> 16) + /* TX ok w/o retry */ (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ retry */ fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */ ieee80211_amrr_tx_update(&RUM_NODE(ni)->amn, ok+fail, ok, (le32toh(sc->sta[5]) & 0xffff) + fail); (void) ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn); ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */ usb2_callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, rvp); RUM_UNLOCK(sc); } /* ARGUSED */ static struct ieee80211_node * rum_node_alloc(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct rum_node *rn; rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO); return rn != NULL ? &rn->ni : NULL; } static void rum_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni); } static void rum_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct rum_softc *sc = ifp->if_softc; uint32_t tmp; RUM_LOCK(sc); /* abort TSF synchronization */ tmp = rum_read(sc, RT2573_TXRX_CSR9); rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff); rum_set_bssid(sc, ifp->if_broadcastaddr); RUM_UNLOCK(sc); } static void rum_scan_end(struct ieee80211com *ic) { struct rum_softc *sc = ic->ic_ifp->if_softc; RUM_LOCK(sc); rum_enable_tsf_sync(sc); rum_set_bssid(sc, sc->sc_bssid); RUM_UNLOCK(sc); } static void rum_set_channel(struct ieee80211com *ic) { struct rum_softc *sc = ic->ic_ifp->if_softc; RUM_LOCK(sc); rum_set_chan(sc, ic->ic_curchan); RUM_UNLOCK(sc); } static int rum_get_rssi(struct rum_softc *sc, uint8_t raw) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int lna, agc, rssi; lna = (raw >> 5) & 0x3; agc = raw & 0x1f; if (lna == 0) { /* * No RSSI mapping * * NB: Since RSSI is relative to noise floor, -1 is * adequate for caller to know error happened. */ return -1; } rssi = (2 * agc) - RT2573_NOISE_FLOOR; if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { rssi += sc->rssi_2ghz_corr; if (lna == 1) rssi -= 64; else if (lna == 2) rssi -= 74; else if (lna == 3) rssi -= 90; } else { rssi += sc->rssi_5ghz_corr; if (!sc->ext_5ghz_lna && lna != 1) rssi += 4; if (lna == 1) rssi -= 64; else if (lna == 2) rssi -= 86; else if (lna == 3) rssi -= 100; } return rssi; } static int rum_pause(struct rum_softc *sc, int timeout) { usb2_pause_mtx(&sc->sc_mtx, timeout); return (0); } static device_method_t rum_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rum_match), DEVMETHOD(device_attach, rum_attach), DEVMETHOD(device_detach, rum_detach), { 0, 0 } }; static driver_t rum_driver = { .name = "rum", .methods = rum_methods, .size = sizeof(struct rum_softc), }; static devclass_t rum_devclass; DRIVER_MODULE(rum, uhub, rum_driver, rum_devclass, NULL, 0); Index: head/sys/dev/usb/wlan/if_ural.c =================================================================== --- head/sys/dev/usb/wlan/if_ural.c (revision 193072) +++ head/sys/dev/usb/wlan/if_ural.c (revision 193073) @@ -1,2285 +1,2285 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2005, 2006 * Damien Bergamini * * Copyright (c) 2006, 2008 * Hans Petter Selasky * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /*- * Ralink Technology RT2500USB chipset driver * http://www.ralinktech.com/ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #include #include #include #include #define USB_DEBUG_VAR ural_debug #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #if USB_DEBUG static int ural_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural"); SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RW, &ural_debug, 0, "Debug level"); #endif #define URAL_RSSI(rssi) \ ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0) /* various supported device vendors/products */ static const struct usb_device_id ural_devs[] = { { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G) }, { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_RALINK_RT2570) }, { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050) }, { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7051) }, { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_HU200TS) }, { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54G) }, { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GP) }, { USB_VP(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU) }, { USB_VP(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122) }, { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GN54G) }, { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG) }, { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254) }, { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54) }, { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI) }, { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB) }, { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI) }, { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570) }, { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2) }, { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3) }, { USB_VP(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_NV902) }, { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570) }, { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2) }, { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3) }, { USB_VP(USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G) }, { USB_VP(USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG) }, { USB_VP(USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R) }, { USB_VP(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2570) }, { USB_VP(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570) }, { USB_VP(USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570) }, }; static usb_callback_t ural_bulk_read_callback; static usb_callback_t ural_bulk_write_callback; static usb_error_t ural_do_request(struct ural_softc *sc, struct usb_device_request *req, void *data); static struct ieee80211vap *ural_vap_create(struct ieee80211com *, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]); static void ural_vap_delete(struct ieee80211vap *); static void ural_tx_free(struct ural_tx_data *, int); static void ural_setup_tx_list(struct ural_softc *); static void ural_unsetup_tx_list(struct ural_softc *); static int ural_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void ural_setup_tx_desc(struct ural_softc *, struct ural_tx_desc *, uint32_t, int, int); static int ural_tx_bcn(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static int ural_tx_mgt(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static int ural_tx_data(struct ural_softc *, struct mbuf *, struct ieee80211_node *); static void ural_start(struct ifnet *); static int ural_ioctl(struct ifnet *, u_long, caddr_t); static void ural_set_testmode(struct ural_softc *); static void ural_eeprom_read(struct ural_softc *, uint16_t, void *, int); static uint16_t ural_read(struct ural_softc *, uint16_t); static void ural_read_multi(struct ural_softc *, uint16_t, void *, int); static void ural_write(struct ural_softc *, uint16_t, uint16_t); static void ural_write_multi(struct ural_softc *, uint16_t, void *, int) __unused; static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t); static uint8_t ural_bbp_read(struct ural_softc *, uint8_t); static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t); static struct ieee80211_node *ural_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static void ural_newassoc(struct ieee80211_node *, int); static void ural_scan_start(struct ieee80211com *); static void ural_scan_end(struct ieee80211com *); static void ural_set_channel(struct ieee80211com *); static void ural_set_chan(struct ural_softc *, struct ieee80211_channel *); static void ural_disable_rf_tune(struct ural_softc *); static void ural_enable_tsf_sync(struct ural_softc *); static void ural_enable_tsf(struct ural_softc *); static void ural_update_slot(struct ifnet *); static void ural_set_txpreamble(struct ural_softc *); static void ural_set_basicrates(struct ural_softc *, const struct ieee80211_channel *); static void ural_set_bssid(struct ural_softc *, const uint8_t *); static void ural_set_macaddr(struct ural_softc *, uint8_t *); static void ural_update_promisc(struct ifnet *); static void ural_setpromisc(struct ural_softc *); static const char *ural_get_rf(int); static void ural_read_eeprom(struct ural_softc *); static int ural_bbp_init(struct ural_softc *); static void ural_set_txantenna(struct ural_softc *, int); static void ural_set_rxantenna(struct ural_softc *, int); static void ural_init_locked(struct ural_softc *); static void ural_init(void *); static void ural_stop(struct ural_softc *); static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void ural_amrr_start(struct ural_softc *, struct ieee80211_node *); static void ural_amrr_timeout(void *); static void ural_amrr_task(void *, int); static int ural_pause(struct ural_softc *sc, int timeout); /* * Default values for MAC registers; values taken from the reference driver. */ static const struct { uint16_t reg; uint16_t val; } ural_def_mac[] = { { RAL_TXRX_CSR5, 0x8c8d }, { RAL_TXRX_CSR6, 0x8b8a }, { RAL_TXRX_CSR7, 0x8687 }, { RAL_TXRX_CSR8, 0x0085 }, { RAL_MAC_CSR13, 0x1111 }, { RAL_MAC_CSR14, 0x1e11 }, { RAL_TXRX_CSR21, 0xe78f }, { RAL_MAC_CSR9, 0xff1d }, { RAL_MAC_CSR11, 0x0002 }, { RAL_MAC_CSR22, 0x0053 }, { RAL_MAC_CSR15, 0x0000 }, { RAL_MAC_CSR8, RAL_FRAME_SIZE }, { RAL_TXRX_CSR19, 0x0000 }, { RAL_TXRX_CSR18, 0x005a }, { RAL_PHY_CSR2, 0x0000 }, { RAL_TXRX_CSR0, 0x1ec0 }, { RAL_PHY_CSR4, 0x000f } }; /* * Default values for BBP registers; values taken from the reference driver. */ static const struct { uint8_t reg; uint8_t val; } ural_def_bbp[] = { { 3, 0x02 }, { 4, 0x19 }, { 14, 0x1c }, { 15, 0x30 }, { 16, 0xac }, { 17, 0x48 }, { 18, 0x18 }, { 19, 0xff }, { 20, 0x1e }, { 21, 0x08 }, { 22, 0x08 }, { 23, 0x08 }, { 24, 0x80 }, { 25, 0x50 }, { 26, 0x08 }, { 27, 0x23 }, { 30, 0x10 }, { 31, 0x2b }, { 32, 0xb9 }, { 34, 0x12 }, { 35, 0x50 }, { 39, 0xc4 }, { 40, 0x02 }, { 41, 0x60 }, { 53, 0x10 }, { 54, 0x18 }, { 56, 0x08 }, { 57, 0x10 }, { 58, 0x08 }, { 61, 0x60 }, { 62, 0x10 }, { 75, 0xff } }; /* * Default values for RF register R2 indexed by channel numbers. */ static const uint32_t ural_rf2522_r2[] = { 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814, 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e }; static const uint32_t ural_rf2523_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const uint32_t ural_rf2524_r2[] = { 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 }; static const uint32_t ural_rf2525_r2[] = { 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d, 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346 }; static const uint32_t ural_rf2525_hi_r2[] = { 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345, 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e }; static const uint32_t ural_rf2525e_r2[] = { 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463, 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b }; static const uint32_t ural_rf2526_hi_r2[] = { 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d, 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241 }; static const uint32_t ural_rf2526_r2[] = { 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229, 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d }; /* * For dual-band RF, RF registers R1 and R4 also depend on channel number; * values taken from the reference driver. */ static const struct { uint8_t chan; uint32_t r1; uint32_t r2; uint32_t r4; } ural_rf5222[] = { { 1, 0x08808, 0x0044d, 0x00282 }, { 2, 0x08808, 0x0044e, 0x00282 }, { 3, 0x08808, 0x0044f, 0x00282 }, { 4, 0x08808, 0x00460, 0x00282 }, { 5, 0x08808, 0x00461, 0x00282 }, { 6, 0x08808, 0x00462, 0x00282 }, { 7, 0x08808, 0x00463, 0x00282 }, { 8, 0x08808, 0x00464, 0x00282 }, { 9, 0x08808, 0x00465, 0x00282 }, { 10, 0x08808, 0x00466, 0x00282 }, { 11, 0x08808, 0x00467, 0x00282 }, { 12, 0x08808, 0x00468, 0x00282 }, { 13, 0x08808, 0x00469, 0x00282 }, { 14, 0x08808, 0x0046b, 0x00286 }, { 36, 0x08804, 0x06225, 0x00287 }, { 40, 0x08804, 0x06226, 0x00287 }, { 44, 0x08804, 0x06227, 0x00287 }, { 48, 0x08804, 0x06228, 0x00287 }, { 52, 0x08804, 0x06229, 0x00287 }, { 56, 0x08804, 0x0622a, 0x00287 }, { 60, 0x08804, 0x0622b, 0x00287 }, { 64, 0x08804, 0x0622c, 0x00287 }, { 100, 0x08804, 0x02200, 0x00283 }, { 104, 0x08804, 0x02201, 0x00283 }, { 108, 0x08804, 0x02202, 0x00283 }, { 112, 0x08804, 0x02203, 0x00283 }, { 116, 0x08804, 0x02204, 0x00283 }, { 120, 0x08804, 0x02205, 0x00283 }, { 124, 0x08804, 0x02206, 0x00283 }, { 128, 0x08804, 0x02207, 0x00283 }, { 132, 0x08804, 0x02208, 0x00283 }, { 136, 0x08804, 0x02209, 0x00283 }, { 140, 0x08804, 0x0220a, 0x00283 }, { 149, 0x08808, 0x02429, 0x00281 }, { 153, 0x08808, 0x0242b, 0x00281 }, { 157, 0x08808, 0x0242d, 0x00281 }, { 161, 0x08808, 0x0242f, 0x00281 } }; static const struct usb_config ural_config[URAL_N_TRANSFER] = { [URAL_BULK_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE + 4), .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = ural_bulk_write_callback, .timeout = 5000, /* ms */ }, [URAL_BULK_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = (RAL_FRAME_SIZE + RAL_RX_DESC_SIZE), .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = ural_bulk_read_callback, }, }; static device_probe_t ural_match; static device_attach_t ural_attach; static device_detach_t ural_detach; static device_method_t ural_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ural_match), DEVMETHOD(device_attach, ural_attach), DEVMETHOD(device_detach, ural_detach), { 0, 0 } }; static driver_t ural_driver = { .name = "ural", .methods = ural_methods, .size = sizeof(struct ural_softc), }; static devclass_t ural_devclass; DRIVER_MODULE(ural, uhub, ural_driver, ural_devclass, NULL, 0); MODULE_DEPEND(ural, usb, 1, 1, 1); MODULE_DEPEND(ural, wlan, 1, 1, 1); MODULE_DEPEND(ural, wlan_amrr, 1, 1, 1); static int ural_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != 0) return (ENXIO); if (uaa->info.bIfaceIndex != RAL_IFACE_INDEX) return (ENXIO); return (usb2_lookup_id_by_uaa(ural_devs, sizeof(ural_devs), uaa)); } static int ural_attach(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); struct ural_softc *sc = device_get_softc(self); struct ifnet *ifp; struct ieee80211com *ic; uint8_t iface_index, bands; int error; device_set_usb2_desc(self); sc->sc_udev = uaa->device; sc->sc_dev = self; mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF); iface_index = RAL_IFACE_INDEX; error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, ural_config, URAL_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(self, "could not allocate USB transfers, " "err=%s\n", usb2_errstr(error)); goto detach; } RAL_LOCK(sc); /* retrieve RT2570 rev. no */ sc->asic_rev = ural_read(sc, RAL_MAC_CSR0); /* retrieve MAC address and various other things from EEPROM */ ural_read_eeprom(sc); RAL_UNLOCK(sc); device_printf(self, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n", sc->asic_rev, ural_get_rf(sc->rf_rev)); ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(sc->sc_dev, "can not if_alloc()\n"); goto detach; } ic = ifp->if_l2com; ifp->if_softc = sc; if_initname(ifp, "ural", device_get_unit(sc->sc_dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = ural_init; ifp->if_ioctl = ural_ioctl; ifp->if_start = ural_start; IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; IFQ_SET_READY(&ifp->if_snd); ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_HOSTAP /* HostAp mode supported */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_BGSCAN /* bg scanning supported */ | IEEE80211_C_WPA /* 802.11i */ ; bands = 0; setbit(&bands, IEEE80211_MODE_11B); setbit(&bands, IEEE80211_MODE_11G); if (sc->rf_rev == RAL_RF_5222) setbit(&bands, IEEE80211_MODE_11A); ieee80211_init_channels(ic, NULL, &bands); ieee80211_ifattach(ic, sc->sc_bssid); ic->ic_update_promisc = ural_update_promisc; ic->ic_newassoc = ural_newassoc; ic->ic_raw_xmit = ural_raw_xmit; ic->ic_node_alloc = ural_node_alloc; ic->ic_scan_start = ural_scan_start; ic->ic_scan_end = ural_scan_end; ic->ic_set_channel = ural_set_channel; ic->ic_vap_create = ural_vap_create; ic->ic_vap_delete = ural_vap_delete; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), RAL_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), RAL_RX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); return (0); detach: ural_detach(self); return (ENXIO); /* failure */ } static int ural_detach(device_t self) { struct ural_softc *sc = device_get_softc(self); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic; /* stop all USB transfers */ usb2_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER); /* free TX list, if any */ RAL_LOCK(sc); ural_unsetup_tx_list(sc); RAL_UNLOCK(sc); if (ifp) { ic = ifp->if_l2com; ieee80211_ifdetach(ic); if_free(ifp); } mtx_destroy(&sc->sc_mtx); return (0); } static usb_error_t ural_do_request(struct ural_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; while (ntries--) { err = usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 250 /* ms */); if (err == 0) break; DPRINTFN(1, "Control request failed, %s (retrying)\n", usb2_errstr(err)); if (ural_pause(sc, hz / 100)) break; } return (err); } static struct ieee80211vap * ural_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, int opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct ural_softc *sc = ic->ic_ifp->if_softc; struct ural_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; uvp = (struct ural_vap *) malloc(sizeof(struct ural_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (uvp == NULL) return NULL; vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = ural_newstate; usb2_callout_init_mtx(&uvp->amrr_ch, &sc->sc_mtx, 0); TASK_INIT(&uvp->amrr_task, 0, ural_amrr_task, uvp); ieee80211_amrr_init(&uvp->amrr, vap, IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD, 1000 /* 1 sec */); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return vap; } static void ural_vap_delete(struct ieee80211vap *vap) { struct ural_vap *uvp = URAL_VAP(vap); struct ieee80211com *ic = vap->iv_ic; usb2_callout_drain(&uvp->amrr_ch); ieee80211_draintask(ic, &uvp->amrr_task); ieee80211_amrr_cleanup(&uvp->amrr); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static void ural_tx_free(struct ural_tx_data *data, int txerr) { struct ural_softc *sc = data->sc; if (data->m != NULL) { if (data->m->m_flags & M_TXCB) ieee80211_process_callback(data->ni, data->m, txerr ? ETIMEDOUT : 0); m_freem(data->m); data->m = NULL; ieee80211_free_node(data->ni); data->ni = NULL; } STAILQ_INSERT_TAIL(&sc->tx_free, data, next); sc->tx_nfree++; } static void ural_setup_tx_list(struct ural_softc *sc) { struct ural_tx_data *data; int i; sc->tx_nfree = 0; STAILQ_INIT(&sc->tx_q); STAILQ_INIT(&sc->tx_free); for (i = 0; i < RAL_TX_LIST_COUNT; i++) { data = &sc->tx_data[i]; data->sc = sc; STAILQ_INSERT_TAIL(&sc->tx_free, data, next); sc->tx_nfree++; } } static void ural_unsetup_tx_list(struct ural_softc *sc) { struct ural_tx_data *data; int i; /* make sure any subsequent use of the queues will fail */ sc->tx_nfree = 0; STAILQ_INIT(&sc->tx_q); STAILQ_INIT(&sc->tx_free); /* free up all node references and mbufs */ for (i = 0; i < RAL_TX_LIST_COUNT; i++) { data = &sc->tx_data[i]; if (data->m != NULL) { m_freem(data->m); data->m = NULL; } if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; } } } static int ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct ural_vap *uvp = URAL_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ural_softc *sc = ic->ic_ifp->if_softc; const struct ieee80211_txparam *tp; struct ieee80211_node *ni; struct mbuf *m; DPRINTF("%s -> %s\n", ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); RAL_LOCK(sc); usb2_callout_stop(&uvp->amrr_ch); switch (nstate) { case IEEE80211_S_INIT: if (vap->iv_state == IEEE80211_S_RUN) { /* abort TSF synchronization */ ural_write(sc, RAL_TXRX_CSR19, 0); /* force tx led to stop blinking */ ural_write(sc, RAL_MAC_CSR20, 0); } break; case IEEE80211_S_RUN: ni = vap->iv_bss; if (vap->iv_opmode != IEEE80211_M_MONITOR) { ural_update_slot(ic->ic_ifp); ural_set_txpreamble(sc); ural_set_basicrates(sc, ic->ic_bsschan); IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); ural_set_bssid(sc, sc->sc_bssid); } if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS) { m = ieee80211_beacon_alloc(ni, &uvp->bo); if (m == NULL) { device_printf(sc->sc_dev, "could not allocate beacon\n"); RAL_UNLOCK(sc); IEEE80211_LOCK(ic); return (-1); } ieee80211_ref_node(ni); if (ural_tx_bcn(sc, m, ni) != 0) { device_printf(sc->sc_dev, "could not send beacon\n"); RAL_UNLOCK(sc); IEEE80211_LOCK(ic); return (-1); } } /* make tx led blink on tx (controlled by ASIC) */ ural_write(sc, RAL_MAC_CSR20, 1); if (vap->iv_opmode != IEEE80211_M_MONITOR) ural_enable_tsf_sync(sc); else ural_enable_tsf(sc); /* enable automatic rate adaptation */ /* XXX should use ic_bsschan but not valid until after newstate call below */ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) ural_amrr_start(sc, ni); break; default: break; } RAL_UNLOCK(sc); IEEE80211_LOCK(ic); return (uvp->newstate(vap, nstate, arg)); } static void ural_bulk_write_callback(struct usb_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap; struct ural_tx_data *data; struct mbuf *m; unsigned int len; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen); /* free resources */ data = xfer->priv_fifo; ural_tx_free(data, 0); xfer->priv_fifo = NULL; ifp->if_opackets++; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&sc->tx_q); if (data) { STAILQ_REMOVE_HEAD(&sc->tx_q, next); m = data->m; if (m->m_pkthdr.len > (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE)) { DPRINTFN(0, "data overflow, %u bytes\n", m->m_pkthdr.len); m->m_pkthdr.len = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE); } usb2_copy_in(xfer->frbuffers, 0, &data->desc, RAL_TX_DESC_SIZE); usb2_m_copy_in(xfer->frbuffers, RAL_TX_DESC_SIZE, m, 0, m->m_pkthdr.len); vap = data->ni->ni_vap; if (ieee80211_radiotap_active_vap(vap)) { struct ural_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = data->rate; tap->wt_antenna = sc->tx_ant; ieee80211_radiotap_tx(vap, m); } /* xfer length needs to be a multiple of two! */ len = (RAL_TX_DESC_SIZE + m->m_pkthdr.len + 1) & ~1; if ((len % 64) == 0) len += 2; DPRINTFN(11, "sending frame len=%u xferlen=%u\n", m->m_pkthdr.len, len); xfer->frlengths[0] = len; xfer->priv_fifo = data; usb2_start_hardware(xfer); } break; default: /* Error */ DPRINTFN(11, "transfer error, %s\n", usb2_errstr(xfer->error)); ifp->if_oerrors++; data = xfer->priv_fifo; if (data != NULL) { ural_tx_free(data, xfer->error); xfer->priv_fifo = NULL; } if (xfer->error == USB_ERR_STALLED) { /* try to clear stall first */ xfer->flags.stall_pipe = 1; goto tr_setup; } if (xfer->error == USB_ERR_TIMEOUT) device_printf(sc->sc_dev, "device timeout\n"); break; } } static void ural_bulk_read_callback(struct usb_xfer *xfer) { struct ural_softc *sc = xfer->priv_sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_node *ni; struct mbuf *m = NULL; uint32_t flags; int8_t rssi = 0, nf = 0; unsigned int len; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen); len = xfer->actlen; if (len < RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN) { DPRINTF("%s: xfer too short %d\n", device_get_nameunit(sc->sc_dev), len); ifp->if_ierrors++; goto tr_setup; } len -= RAL_RX_DESC_SIZE; /* rx descriptor is located at the end */ usb2_copy_out(xfer->frbuffers, len, &sc->sc_rx_desc, RAL_RX_DESC_SIZE); rssi = URAL_RSSI(sc->sc_rx_desc.rssi); nf = RAL_NOISE_FLOOR; flags = le32toh(sc->sc_rx_desc.flags); if (flags & (RAL_RX_PHY_ERROR | RAL_RX_CRC_ERROR)) { /* * This should not happen since we did not * request to receive those frames when we * filled RAL_TXRX_CSR2: */ DPRINTFN(5, "PHY or CRC error\n"); ifp->if_ierrors++; goto tr_setup; } m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF("could not allocate mbuf\n"); ifp->if_ierrors++; goto tr_setup; } usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *), len); /* finalize mbuf */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; if (ieee80211_radiotap_active(ic)) { struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; /* XXX set once */ tap->wr_flags = 0; tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, (flags & RAL_RX_OFDM) ? IEEE80211_T_OFDM : IEEE80211_T_CCK); tap->wr_antenna = sc->rx_ant; tap->wr_antsignal = nf + rssi; tap->wr_antnoise = nf; } /* Strip trailing 802.11 MAC FCS. */ m_adj(m, -IEEE80211_CRC_LEN); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: xfer->frlengths[0] = xfer->max_data_length; usb2_start_hardware(xfer); /* * At the end of a USB callback it is always safe to unlock * the private mutex of a device! That is why we do the * "ieee80211_input" here, and not some lines up! */ if (m) { RAL_UNLOCK(sc); ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); if (ni != NULL) { (void) ieee80211_input(ni, m, rssi, nf); ieee80211_free_node(ni); } else (void) ieee80211_input_all(ic, m, rssi, nf); RAL_LOCK(sc); } return; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* try to clear stall first */ xfer->flags.stall_pipe = 1; goto tr_setup; } return; } } static uint8_t ural_plcp_signal(int rate) { switch (rate) { /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ case 12: return 0xb; case 18: return 0xf; case 24: return 0xa; case 36: return 0xe; case 48: return 0x9; case 72: return 0xd; case 96: return 0x8; case 108: return 0xc; /* CCK rates (NB: not IEEE std, device-specific) */ case 2: return 0x0; case 4: return 0x1; case 11: return 0x2; case 22: return 0x3; } return 0xff; /* XXX unsupported/unknown rate */ } static void ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc, uint32_t flags, int len, int rate) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t plcp_length; int remainder; desc->flags = htole32(flags); desc->flags |= htole32(RAL_TX_NEWSEQ); desc->flags |= htole32(len << 16); desc->wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5)); desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame))); /* setup PLCP fields */ desc->plcp_signal = ural_plcp_signal(rate); desc->plcp_service = 4; len += IEEE80211_CRC_LEN; if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) { desc->flags |= htole32(RAL_TX_OFDM); plcp_length = len & 0xfff; desc->plcp_length_hi = plcp_length >> 6; desc->plcp_length_lo = plcp_length & 0x3f; } else { plcp_length = (16 * len + rate - 1) / rate; if (rate == 22) { remainder = (16 * len) % 22; if (remainder != 0 && remainder < 7) desc->plcp_service |= RAL_PLCP_LENGEXT; } desc->plcp_length_hi = plcp_length >> 8; desc->plcp_length_lo = plcp_length & 0xff; if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) desc->plcp_signal |= 0x08; } desc->iv = 0; desc->eiv = 0; } #define RAL_TX_TIMEOUT 5000 static int ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = sc->sc_ifp; const struct ieee80211_txparam *tp; struct ural_tx_data *data; if (sc->tx_nfree == 0) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; m_freem(m0); ieee80211_free_node(ni); return EIO; } data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; data->m = m0; data->ni = ni; data->rate = tp->mgmtrate; ural_setup_tx_desc(sc, &data->desc, RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, m0->m_pkthdr.len, tp->mgmtrate); DPRINTFN(10, "sending beacon frame len=%u rate=%u\n", m0->m_pkthdr.len, tp->mgmtrate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); return (0); } static int ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_txparam *tp; struct ural_tx_data *data; struct ieee80211_frame *wh; struct ieee80211_key *k; uint32_t flags; uint16_t dur; RAL_LOCK_ASSERT(sc, MA_OWNED); data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } wh = mtod(m0, struct ieee80211_frame *); } data->m = m0; data->ni = ni; data->rate = tp->mgmtrate; flags = 0; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RAL_TX_ACK; dur = ieee80211_ack_duration(ic->ic_rt, tp->mgmtrate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); *(uint16_t *)wh->i_dur = htole16(dur); /* tell hardware to add timestamp for probe responses */ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT && (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= RAL_TX_TIMESTAMP; } ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, tp->mgmtrate); DPRINTFN(10, "sending mgt frame len=%u rate=%u\n", m0->m_pkthdr.len, tp->mgmtrate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); return 0; } static int ural_sendprot(struct ural_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) { struct ieee80211com *ic = ni->ni_ic; const struct ieee80211_frame *wh; struct ural_tx_data *data; struct mbuf *mprot; int protrate, ackrate, pktlen, flags, isshort; uint16_t dur; KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY, ("protection %d", prot)); wh = mtod(m, const struct ieee80211_frame *); pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN; protrate = ieee80211_ctl_rate(ic->ic_rt, rate); ackrate = ieee80211_ack_rate(ic->ic_rt, rate); isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0; dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort); + ieee80211_ack_duration(ic->ic_rt, rate, isshort); flags = RAL_TX_RETRY(7); if (prot == IEEE80211_PROT_RTSCTS) { /* NB: CTS is the same size as an ACK */ dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort); flags |= RAL_TX_ACK; mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur); } else { mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { /* XXX stat + msg */ return ENOBUFS; } data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; data->m = mprot; data->ni = ieee80211_ref_node(ni); data->rate = protrate; ural_setup_tx_desc(sc, &data->desc, flags, mprot->m_pkthdr.len, protrate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); return 0; } static int ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { + struct ieee80211com *ic = ni->ni_ic; struct ural_tx_data *data; uint32_t flags; int error; int rate; RAL_LOCK_ASSERT(sc, MA_OWNED); KASSERT(params != NULL, ("no raw xmit params")); - rate = params->ibp_rate0 & IEEE80211_RATE_VAL; - /* XXX validate */ - if (rate == 0) { + rate = params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) { m_freem(m0); return EINVAL; } flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= RAL_TX_ACK; if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { error = ural_sendprot(sc, m0, ni, params->ibp_flags & IEEE80211_BPF_RTS ? IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, rate); if (error || sc->tx_nfree == 0) { m_freem(m0); return ENOBUFS; } flags |= RAL_TX_IFS_SIFS; } data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; data->m = m0; data->ni = ni; data->rate = rate; /* XXX need to setup descriptor ourself */ ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); DPRINTFN(10, "sending raw frame len=%u rate=%u\n", m0->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); return 0; } static int ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ural_tx_data *data; struct ieee80211_frame *wh; const struct ieee80211_txparam *tp; struct ieee80211_key *k; uint32_t flags = 0; uint16_t dur; int error, rate; RAL_LOCK_ASSERT(sc, MA_OWNED); wh = mtod(m0, struct ieee80211_frame *); tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = ni->ni_txrate; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; } /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { int prot = IEEE80211_PROT_NONE; if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) prot = IEEE80211_PROT_RTSCTS; else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) prot = ic->ic_protmode; if (prot != IEEE80211_PROT_NONE) { error = ural_sendprot(sc, m0, ni, prot, rate); if (error || sc->tx_nfree == 0) { m_freem(m0); return ENOBUFS; } flags |= RAL_TX_IFS_SIFS; } } data = STAILQ_FIRST(&sc->tx_free); STAILQ_REMOVE_HEAD(&sc->tx_free, next); sc->tx_nfree--; data->m = m0; data->ni = ni; data->rate = rate; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= RAL_TX_ACK; flags |= RAL_TX_RETRY(7); dur = ieee80211_ack_duration(ic->ic_rt, rate, ic->ic_flags & IEEE80211_F_SHPREAMBLE); *(uint16_t *)wh->i_dur = htole16(dur); } ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); DPRINTFN(10, "sending data frame len=%u rate=%u\n", m0->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->tx_q, data, next); usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]); return 0; } static void ural_start(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct mbuf *m; RAL_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { RAL_UNLOCK(sc); return; } for (;;) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; if (sc->tx_nfree < RAL_TX_MINFREE) { IFQ_DRV_PREPEND(&ifp->if_snd, m); ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; if (ural_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); ifp->if_oerrors++; break; } } RAL_UNLOCK(sc); } static int ural_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ural_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0; switch (cmd) { case SIOCSIFFLAGS: RAL_LOCK(sc); if (ifp->if_flags & IFF_UP) { if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { ural_init_locked(sc); startall = 1; } else ural_setpromisc(sc); } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) ural_stop(sc); } RAL_UNLOCK(sc); if (startall) ieee80211_start_all(ic); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; default: error = ether_ioctl(ifp, cmd, data); break; } return error; } static void ural_set_testmode(struct ural_softc *sc) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_VENDOR_REQUEST; USETW(req.wValue, 4); USETW(req.wIndex, 1); USETW(req.wLength, 0); error = ural_do_request(sc, &req, NULL); if (error != 0) { device_printf(sc->sc_dev, "could not set test mode: %s\n", usb2_errstr(error)); } } static void ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_EEPROM; USETW(req.wValue, 0); USETW(req.wIndex, addr); USETW(req.wLength, len); error = ural_do_request(sc, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not read EEPROM: %s\n", usb2_errstr(error)); } } static uint16_t ural_read(struct ural_softc *sc, uint16_t reg) { struct usb_device_request req; usb_error_t error; uint16_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, sizeof (uint16_t)); error = ural_do_request(sc, &req, &val); if (error != 0) { device_printf(sc->sc_dev, "could not read MAC register: %s\n", usb2_errstr(error)); return 0; } return le16toh(val); } static void ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = RAL_READ_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); error = ural_do_request(sc, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not read MAC register: %s\n", usb2_errstr(error)); } } static void ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MAC; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); error = ural_do_request(sc, &req, NULL); if (error != 0) { device_printf(sc->sc_dev, "could not write MAC register: %s\n", usb2_errstr(error)); } } static void ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) { struct usb_device_request req; usb_error_t error; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = RAL_WRITE_MULTI_MAC; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, len); error = ural_do_request(sc, &req, buf); if (error != 0) { device_printf(sc->sc_dev, "could not write MAC register: %s\n", usb2_errstr(error)); } } static void ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val) { uint16_t tmp; int ntries; for (ntries = 0; ntries < 100; ntries++) { if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) break; if (ural_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "could not write to BBP\n"); return; } tmp = reg << 8 | val; ural_write(sc, RAL_PHY_CSR7, tmp); } static uint8_t ural_bbp_read(struct ural_softc *sc, uint8_t reg) { uint16_t val; int ntries; val = RAL_BBP_WRITE | reg << 8; ural_write(sc, RAL_PHY_CSR7, val); for (ntries = 0; ntries < 100; ntries++) { if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) break; if (ural_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "could not read BBP\n"); return 0; } return ural_read(sc, RAL_PHY_CSR7) & 0xff; } static void ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) { uint32_t tmp; int ntries; for (ntries = 0; ntries < 100; ntries++) { if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY)) break; if (ural_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "could not write to RF\n"); return; } tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3); ural_write(sc, RAL_PHY_CSR9, tmp & 0xffff); ural_write(sc, RAL_PHY_CSR10, tmp >> 16); /* remember last written value in sc */ sc->rf_regs[reg] = val; DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff); } /* ARGUSED */ static struct ieee80211_node * ural_node_alloc(struct ieee80211vap *vap __unused, const uint8_t mac[IEEE80211_ADDR_LEN] __unused) { struct ural_node *un; un = malloc(sizeof(struct ural_node), M_80211_NODE, M_NOWAIT | M_ZERO); return un != NULL ? &un->ni : NULL; } static void ural_newassoc(struct ieee80211_node *ni, int isnew) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni); } static void ural_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct ural_softc *sc = ifp->if_softc; RAL_LOCK(sc); ural_write(sc, RAL_TXRX_CSR19, 0); ural_set_bssid(sc, ifp->if_broadcastaddr); RAL_UNLOCK(sc); } static void ural_scan_end(struct ieee80211com *ic) { struct ural_softc *sc = ic->ic_ifp->if_softc; RAL_LOCK(sc); ural_enable_tsf_sync(sc); ural_set_bssid(sc, sc->sc_bssid); RAL_UNLOCK(sc); } static void ural_set_channel(struct ieee80211com *ic) { struct ural_softc *sc = ic->ic_ifp->if_softc; RAL_LOCK(sc); ural_set_chan(sc, ic->ic_curchan); RAL_UNLOCK(sc); } static void ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint8_t power, tmp; int i, chan; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) return; if (IEEE80211_IS_CHAN_2GHZ(c)) power = min(sc->txpow[chan - 1], 31); else power = 31; /* adjust txpower using ifconfig settings */ power -= (100 - ic->ic_txpowlimit) / 8; DPRINTFN(2, "setting channel to %u, txpower to %u\n", chan, power); switch (sc->rf_rev) { case RAL_RF_2522: ural_rf_write(sc, RAL_RF1, 0x00814); ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); break; case RAL_RF_2523: ural_rf_write(sc, RAL_RF1, 0x08804); ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2524: ural_rf_write(sc, RAL_RF1, 0x0c808); ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525: ural_rf_write(sc, RAL_RF1, 0x08808); ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); ural_rf_write(sc, RAL_RF1, 0x08808); ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); break; case RAL_RF_2525E: ural_rf_write(sc, RAL_RF1, 0x08808); ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); break; case RAL_RF_2526: ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); ural_rf_write(sc, RAL_RF1, 0x08804); ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); break; /* dual-band RF */ case RAL_RF_5222: for (i = 0; ural_rf5222[i].chan != chan; i++); ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); break; } if (ic->ic_opmode != IEEE80211_M_MONITOR && (ic->ic_flags & IEEE80211_F_SCAN) == 0) { /* set Japan filter bit for channel 14 */ tmp = ural_bbp_read(sc, 70); tmp &= ~RAL_JAPAN_FILTER; if (chan == 14) tmp |= RAL_JAPAN_FILTER; ural_bbp_write(sc, 70, tmp); /* clear CRC errors */ ural_read(sc, RAL_STA_CSR0); ural_pause(sc, hz / 100); ural_disable_rf_tune(sc); } /* XXX doesn't belong here */ /* update basic rate set */ ural_set_basicrates(sc, c); /* give the hardware some time to do the switchover */ ural_pause(sc, hz / 100); } /* * Disable RF auto-tuning. */ static void ural_disable_rf_tune(struct ural_softc *sc) { uint32_t tmp; if (sc->rf_rev != RAL_RF_2523) { tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; ural_rf_write(sc, RAL_RF1, tmp); } tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; ural_rf_write(sc, RAL_RF3, tmp); DPRINTFN(2, "disabling RF autotune\n"); } /* * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF * synchronization. */ static void ural_enable_tsf_sync(struct ural_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint16_t logcwmin, preload, tmp; /* first, disable TSF synchronization */ ural_write(sc, RAL_TXRX_CSR19, 0); tmp = (16 * vap->iv_bss->ni_intval) << 4; ural_write(sc, RAL_TXRX_CSR18, tmp); logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; tmp = logcwmin << 12 | preload; ural_write(sc, RAL_TXRX_CSR20, tmp); /* finally, enable TSF synchronization */ tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; if (ic->ic_opmode == IEEE80211_M_STA) tmp |= RAL_ENABLE_TSF_SYNC(1); else tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; ural_write(sc, RAL_TXRX_CSR19, tmp); DPRINTF("enabling TSF synchronization\n"); } static void ural_enable_tsf(struct ural_softc *sc) { /* first, disable TSF synchronization */ ural_write(sc, RAL_TXRX_CSR19, 0); ural_write(sc, RAL_TXRX_CSR19, RAL_ENABLE_TSF | RAL_ENABLE_TSF_SYNC(2)); } #define RAL_RXTX_TURNAROUND 5 /* us */ static void ural_update_slot(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; uint16_t slottime, sifs, eifs; slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; /* * These settings may sound a bit inconsistent but this is what the * reference driver does. */ if (ic->ic_curmode == IEEE80211_MODE_11B) { sifs = 16 - RAL_RXTX_TURNAROUND; eifs = 364; } else { sifs = 10 - RAL_RXTX_TURNAROUND; eifs = 64; } ural_write(sc, RAL_MAC_CSR10, slottime); ural_write(sc, RAL_MAC_CSR11, sifs); ural_write(sc, RAL_MAC_CSR12, eifs); } static void ural_set_txpreamble(struct ural_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t tmp; tmp = ural_read(sc, RAL_TXRX_CSR10); tmp &= ~RAL_SHORT_PREAMBLE; if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) tmp |= RAL_SHORT_PREAMBLE; ural_write(sc, RAL_TXRX_CSR10, tmp); } static void ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c) { /* XXX wrong, take from rate set */ /* update basic rate set */ if (IEEE80211_IS_CHAN_5GHZ(c)) { /* 11a basic rates: 6, 12, 24Mbps */ ural_write(sc, RAL_TXRX_CSR11, 0x150); } else if (IEEE80211_IS_CHAN_ANYG(c)) { /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ ural_write(sc, RAL_TXRX_CSR11, 0x15f); } else { /* 11b basic rates: 1, 2Mbps */ ural_write(sc, RAL_TXRX_CSR11, 0x3); } } static void ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid) { uint16_t tmp; tmp = bssid[0] | bssid[1] << 8; ural_write(sc, RAL_MAC_CSR5, tmp); tmp = bssid[2] | bssid[3] << 8; ural_write(sc, RAL_MAC_CSR6, tmp); tmp = bssid[4] | bssid[5] << 8; ural_write(sc, RAL_MAC_CSR7, tmp); DPRINTF("setting BSSID to %6D\n", bssid, ":"); } static void ural_set_macaddr(struct ural_softc *sc, uint8_t *addr) { uint16_t tmp; tmp = addr[0] | addr[1] << 8; ural_write(sc, RAL_MAC_CSR2, tmp); tmp = addr[2] | addr[3] << 8; ural_write(sc, RAL_MAC_CSR3, tmp); tmp = addr[4] | addr[5] << 8; ural_write(sc, RAL_MAC_CSR4, tmp); DPRINTF("setting MAC address to %6D\n", addr, ":"); } static void ural_setpromisc(struct ural_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t tmp; tmp = ural_read(sc, RAL_TXRX_CSR2); tmp &= ~RAL_DROP_NOT_TO_ME; if (!(ifp->if_flags & IFF_PROMISC)) tmp |= RAL_DROP_NOT_TO_ME; ural_write(sc, RAL_TXRX_CSR2, tmp); DPRINTF("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ? "entering" : "leaving"); } static void ural_update_promisc(struct ifnet *ifp) { struct ural_softc *sc = ifp->if_softc; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; RAL_LOCK(sc); ural_setpromisc(sc); RAL_UNLOCK(sc); } static const char * ural_get_rf(int rev) { switch (rev) { case RAL_RF_2522: return "RT2522"; case RAL_RF_2523: return "RT2523"; case RAL_RF_2524: return "RT2524"; case RAL_RF_2525: return "RT2525"; case RAL_RF_2525E: return "RT2525e"; case RAL_RF_2526: return "RT2526"; case RAL_RF_5222: return "RT5222"; default: return "unknown"; } } static void ural_read_eeprom(struct ural_softc *sc) { uint16_t val; ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); val = le16toh(val); sc->rf_rev = (val >> 11) & 0x7; sc->hw_radio = (val >> 10) & 0x1; sc->led_mode = (val >> 6) & 0x7; sc->rx_ant = (val >> 4) & 0x3; sc->tx_ant = (val >> 2) & 0x3; sc->nb_ant = val & 0x3; /* read MAC address */ ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, sc->sc_bssid, 6); /* read default values for BBP registers */ ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); /* read Tx power for all b/g channels */ ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14); } static int ural_bbp_init(struct ural_softc *sc) { #define N(a) (sizeof (a) / sizeof ((a)[0])) int i, ntries; /* wait for BBP to be ready */ for (ntries = 0; ntries < 100; ntries++) { if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0) break; if (ural_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for BBP\n"); return EIO; } /* initialize BBP registers to default values */ for (i = 0; i < N(ural_def_bbp); i++) ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val); #if 0 /* initialize BBP registers to values stored in EEPROM */ for (i = 0; i < 16; i++) { if (sc->bbp_prom[i].reg == 0xff) continue; ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); } #endif return 0; #undef N } static void ural_set_txantenna(struct ural_softc *sc, int antenna) { uint16_t tmp; uint8_t tx; tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK; if (antenna == 1) tx |= RAL_BBP_ANTA; else if (antenna == 2) tx |= RAL_BBP_ANTB; else tx |= RAL_BBP_DIVERSITY; /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 || sc->rf_rev == RAL_RF_5222) tx |= RAL_BBP_FLIPIQ; ural_bbp_write(sc, RAL_BBP_TX, tx); /* update values in PHY_CSR5 and PHY_CSR6 */ tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7; ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7; ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); } static void ural_set_rxantenna(struct ural_softc *sc, int antenna) { uint8_t rx; rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK; if (antenna == 1) rx |= RAL_BBP_ANTA; else if (antenna == 2) rx |= RAL_BBP_ANTB; else rx |= RAL_BBP_DIVERSITY; /* need to force no I/Q flip for RF 2525e and 2526 */ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526) rx &= ~RAL_BBP_FLIPIQ; ural_bbp_write(sc, RAL_BBP_RX, rx); } static void ural_init_locked(struct ural_softc *sc) { #define N(a) (sizeof (a) / sizeof ((a)[0])) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint16_t tmp; int i, ntries; RAL_LOCK_ASSERT(sc, MA_OWNED); ural_set_testmode(sc); ural_write(sc, 0x308, 0x00f0); /* XXX magic */ ural_stop(sc); /* initialize MAC registers to default values */ for (i = 0; i < N(ural_def_mac); i++) ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val); /* wait for BBP and RF to wake up (this can take a long time!) */ for (ntries = 0; ntries < 100; ntries++) { tmp = ural_read(sc, RAL_MAC_CSR17); if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == (RAL_BBP_AWAKE | RAL_RF_AWAKE)) break; if (ural_pause(sc, hz / 100)) break; } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for BBP/RF to wakeup\n"); goto fail; } /* we're ready! */ ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); /* set basic rate set (will be updated later) */ ural_write(sc, RAL_TXRX_CSR11, 0x15f); if (ural_bbp_init(sc) != 0) goto fail; ural_set_chan(sc, ic->ic_curchan); /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); ural_set_txantenna(sc, sc->tx_ant); ural_set_rxantenna(sc, sc->rx_ant); ural_set_macaddr(sc, IF_LLADDR(ifp)); /* * Allocate Tx and Rx xfer queues. */ ural_setup_tx_list(sc); /* kick Rx */ tmp = RAL_DROP_PHY | RAL_DROP_CRC; if (ic->ic_opmode != IEEE80211_M_MONITOR) { tmp |= RAL_DROP_CTL | RAL_DROP_BAD_VERSION; if (ic->ic_opmode != IEEE80211_M_HOSTAP) tmp |= RAL_DROP_TODS; if (!(ifp->if_flags & IFF_PROMISC)) tmp |= RAL_DROP_NOT_TO_ME; } ural_write(sc, RAL_TXRX_CSR2, tmp); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; usb2_transfer_set_stall(sc->sc_xfer[URAL_BULK_WR]); usb2_transfer_start(sc->sc_xfer[URAL_BULK_RD]); return; fail: ural_stop(sc); #undef N } static void ural_init(void *priv) { struct ural_softc *sc = priv; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; RAL_LOCK(sc); ural_init_locked(sc); RAL_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); /* start all vap's */ } static void ural_stop(struct ural_softc *sc) { struct ifnet *ifp = sc->sc_ifp; RAL_LOCK_ASSERT(sc, MA_OWNED); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); /* * Drain all the transfers, if not already drained: */ RAL_UNLOCK(sc); usb2_transfer_drain(sc->sc_xfer[URAL_BULK_WR]); usb2_transfer_drain(sc->sc_xfer[URAL_BULK_RD]); RAL_LOCK(sc); ural_unsetup_tx_list(sc); /* disable Rx */ ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); /* reset ASIC and BBP (but won't reset MAC registers!) */ ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); /* wait a little */ ural_pause(sc, hz / 10); ural_write(sc, RAL_MAC_CSR1, 0); /* wait a little */ ural_pause(sc, hz / 10); } static int ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct ural_softc *sc = ifp->if_softc; RAL_LOCK(sc); /* prevent management frames from being sent if we're not ready */ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { RAL_UNLOCK(sc); m_freem(m); ieee80211_free_node(ni); return ENETDOWN; } if (sc->tx_nfree < RAL_TX_MINFREE) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; RAL_UNLOCK(sc); m_freem(m); ieee80211_free_node(ni); return EIO; } ifp->if_opackets++; if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ if (ural_tx_mgt(sc, m, ni) != 0) goto bad; } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ if (ural_tx_raw(sc, m, ni, params) != 0) goto bad; } RAL_UNLOCK(sc); return 0; bad: ifp->if_oerrors++; RAL_UNLOCK(sc); ieee80211_free_node(ni); return EIO; /* XXX */ } static void ural_amrr_start(struct ural_softc *sc, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ural_vap *uvp = URAL_VAP(vap); /* clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); ieee80211_amrr_node_init(&uvp->amrr, &URAL_NODE(ni)->amn, ni); usb2_callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, uvp); } static void ural_amrr_timeout(void *arg) { struct ural_vap *uvp = arg; struct ieee80211vap *vap = &uvp->vap; struct ieee80211com *ic = vap->iv_ic; ieee80211_runtask(ic, &uvp->amrr_task); } static void ural_amrr_task(void *arg, int pending) { struct ural_vap *uvp = arg; struct ieee80211vap *vap = &uvp->vap; struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct ural_softc *sc = ifp->if_softc; struct ieee80211_node *ni = vap->iv_bss; int ok, fail; RAL_LOCK(sc); /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof(sc->sta)); ok = sc->sta[7] + /* TX ok w/o retry */ sc->sta[8]; /* TX ok w/ retry */ fail = sc->sta[9]; /* TX retry-fail count */ ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn, ok+fail, ok, sc->sta[8] + fail); (void) ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn); ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */ usb2_callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, uvp); RAL_UNLOCK(sc); } static int ural_pause(struct ural_softc *sc, int timeout) { usb2_pause_mtx(&sc->sc_mtx, timeout); return (0); }