112e36acbSWarner Losh /* 212e36acbSWarner Losh * Copyright (c) 2007 The DragonFly Project. All rights reserved. 312e36acbSWarner Losh * 412e36acbSWarner Losh * This code is derived from software contributed to The DragonFly Project 512e36acbSWarner Losh * by Sepherosa Ziehau <sepherosa@gmail.com> 612e36acbSWarner Losh * 712e36acbSWarner Losh * Redistribution and use in source and binary forms, with or without 812e36acbSWarner Losh * modification, are permitted provided that the following conditions 912e36acbSWarner Losh * are met: 1012e36acbSWarner Losh * 1112e36acbSWarner Losh * 1. Redistributions of source code must retain the above copyright 1212e36acbSWarner Losh * notice, this list of conditions and the following disclaimer. 1312e36acbSWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 1412e36acbSWarner Losh * notice, this list of conditions and the following disclaimer in 1512e36acbSWarner Losh * the documentation and/or other materials provided with the 1612e36acbSWarner Losh * distribution. 1712e36acbSWarner Losh * 3. Neither the name of The DragonFly Project nor the names of its 1812e36acbSWarner Losh * contributors may be used to endorse or promote products derived 1912e36acbSWarner Losh * from this software without specific, prior written permission. 2012e36acbSWarner Losh * 2112e36acbSWarner Losh * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2212e36acbSWarner Losh * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2312e36acbSWarner Losh * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2412e36acbSWarner Losh * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2512e36acbSWarner Losh * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2612e36acbSWarner Losh * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 2712e36acbSWarner Losh * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2812e36acbSWarner Losh * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2912e36acbSWarner Losh * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3012e36acbSWarner Losh * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 3112e36acbSWarner Losh * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3212e36acbSWarner Losh * SUCH DAMAGE. 3312e36acbSWarner Losh * 3412e36acbSWarner Losh * $DragonFly: src/sys/dev/netif/bwi/if_bwi.c,v 1.19 2008/02/15 11:15:38 sephe Exp $ 3512e36acbSWarner Losh */ 3612e36acbSWarner Losh 3712e36acbSWarner Losh #include <sys/cdefs.h> 3812e36acbSWarner Losh __FBSDID("$FreeBSD$"); 3912e36acbSWarner Losh 4012e36acbSWarner Losh #include "opt_inet.h" 4112e36acbSWarner Losh #include "opt_bwi.h" 4260807f5bSAdrian Chadd #include "opt_wlan.h" 4312e36acbSWarner Losh 4412e36acbSWarner Losh #include <sys/param.h> 4512e36acbSWarner Losh #include <sys/endian.h> 4612e36acbSWarner Losh #include <sys/kernel.h> 4712e36acbSWarner Losh #include <sys/bus.h> 4812e36acbSWarner Losh #include <sys/malloc.h> 4912e36acbSWarner Losh #include <sys/proc.h> 5012e36acbSWarner Losh #include <sys/rman.h> 5112e36acbSWarner Losh #include <sys/socket.h> 5212e36acbSWarner Losh #include <sys/sockio.h> 5312e36acbSWarner Losh #include <sys/sysctl.h> 5412e36acbSWarner Losh #include <sys/systm.h> 5512e36acbSWarner Losh #include <sys/taskqueue.h> 5612e36acbSWarner Losh 5712e36acbSWarner Losh #include <net/if.h> 5876039bc8SGleb Smirnoff #include <net/if_var.h> 5912e36acbSWarner Losh #include <net/if_dl.h> 6012e36acbSWarner Losh #include <net/if_media.h> 6112e36acbSWarner Losh #include <net/if_types.h> 6212e36acbSWarner Losh #include <net/if_arp.h> 6312e36acbSWarner Losh #include <net/ethernet.h> 6412e36acbSWarner Losh #include <net/if_llc.h> 6512e36acbSWarner Losh 6612e36acbSWarner Losh #include <net80211/ieee80211_var.h> 6712e36acbSWarner Losh #include <net80211/ieee80211_radiotap.h> 6812e36acbSWarner Losh #include <net80211/ieee80211_regdomain.h> 6912e36acbSWarner Losh #include <net80211/ieee80211_phy.h> 70b6108616SRui Paulo #include <net80211/ieee80211_ratectl.h> 7112e36acbSWarner Losh 7212e36acbSWarner Losh #include <net/bpf.h> 7312e36acbSWarner Losh 7412e36acbSWarner Losh #ifdef INET 7512e36acbSWarner Losh #include <netinet/in.h> 7612e36acbSWarner Losh #include <netinet/if_ether.h> 7712e36acbSWarner Losh #endif 7812e36acbSWarner Losh 7912e36acbSWarner Losh #include <machine/bus.h> 8012e36acbSWarner Losh 8112e36acbSWarner Losh #include <dev/pci/pcivar.h> 8212e36acbSWarner Losh #include <dev/pci/pcireg.h> 8312e36acbSWarner Losh 8412e36acbSWarner Losh #include <dev/bwi/bitops.h> 8512e36acbSWarner Losh #include <dev/bwi/if_bwireg.h> 8612e36acbSWarner Losh #include <dev/bwi/if_bwivar.h> 8712e36acbSWarner Losh #include <dev/bwi/bwimac.h> 8812e36acbSWarner Losh #include <dev/bwi/bwirf.h> 8912e36acbSWarner Losh 9012e36acbSWarner Losh struct bwi_clock_freq { 9112e36acbSWarner Losh u_int clkfreq_min; 9212e36acbSWarner Losh u_int clkfreq_max; 9312e36acbSWarner Losh }; 9412e36acbSWarner Losh 9512e36acbSWarner Losh struct bwi_myaddr_bssid { 9612e36acbSWarner Losh uint8_t myaddr[IEEE80211_ADDR_LEN]; 9712e36acbSWarner Losh uint8_t bssid[IEEE80211_ADDR_LEN]; 9812e36acbSWarner Losh } __packed; 9912e36acbSWarner Losh 10012e36acbSWarner Losh static struct ieee80211vap *bwi_vap_create(struct ieee80211com *, 101fcd9500fSBernhard Schmidt const char [IFNAMSIZ], int, enum ieee80211_opmode, int, 10212e36acbSWarner Losh const uint8_t [IEEE80211_ADDR_LEN], 10312e36acbSWarner Losh const uint8_t [IEEE80211_ADDR_LEN]); 10412e36acbSWarner Losh static void bwi_vap_delete(struct ieee80211vap *); 10512e36acbSWarner Losh static void bwi_init(void *); 10612e36acbSWarner Losh static int bwi_ioctl(struct ifnet *, u_long, caddr_t); 10712e36acbSWarner Losh static void bwi_start(struct ifnet *); 10812e36acbSWarner Losh static void bwi_start_locked(struct ifnet *); 10912e36acbSWarner Losh static int bwi_raw_xmit(struct ieee80211_node *, struct mbuf *, 11012e36acbSWarner Losh const struct ieee80211_bpf_params *); 11158fbe5abSJohn Baldwin static void bwi_watchdog(void *); 11212e36acbSWarner Losh static void bwi_scan_start(struct ieee80211com *); 11312e36acbSWarner Losh static void bwi_set_channel(struct ieee80211com *); 11412e36acbSWarner Losh static void bwi_scan_end(struct ieee80211com *); 11512e36acbSWarner Losh static int bwi_newstate(struct ieee80211vap *, enum ieee80211_state, int); 116272f6adeSGleb Smirnoff static void bwi_updateslot(struct ieee80211com *); 11712e36acbSWarner Losh static int bwi_media_change(struct ifnet *); 11812e36acbSWarner Losh 11912e36acbSWarner Losh static void bwi_calibrate(void *); 12012e36acbSWarner Losh 12112e36acbSWarner Losh static int bwi_calc_rssi(struct bwi_softc *, const struct bwi_rxbuf_hdr *); 12212e36acbSWarner Losh static int bwi_calc_noise(struct bwi_softc *); 123fcd9500fSBernhard Schmidt static __inline uint8_t bwi_plcp2rate(uint32_t, enum ieee80211_phytype); 1245463c4a4SSam Leffler static void bwi_rx_radiotap(struct bwi_softc *, struct mbuf *, 12512e36acbSWarner Losh struct bwi_rxbuf_hdr *, const void *, int, int, int); 12612e36acbSWarner Losh 12712e36acbSWarner Losh static void bwi_restart(void *, int); 12812e36acbSWarner Losh static void bwi_init_statechg(struct bwi_softc *, int); 12912e36acbSWarner Losh static void bwi_stop(struct bwi_softc *, int); 13012e36acbSWarner Losh static void bwi_stop_locked(struct bwi_softc *, int); 13112e36acbSWarner Losh static int bwi_newbuf(struct bwi_softc *, int, int); 13212e36acbSWarner Losh static int bwi_encap(struct bwi_softc *, int, struct mbuf *, 13312e36acbSWarner Losh struct ieee80211_node *); 13412e36acbSWarner Losh static int bwi_encap_raw(struct bwi_softc *, int, struct mbuf *, 13512e36acbSWarner Losh struct ieee80211_node *, 13612e36acbSWarner Losh const struct ieee80211_bpf_params *); 13712e36acbSWarner Losh 13812e36acbSWarner Losh static void bwi_init_rxdesc_ring32(struct bwi_softc *, uint32_t, 13912e36acbSWarner Losh bus_addr_t, int, int); 14012e36acbSWarner Losh static void bwi_reset_rx_ring32(struct bwi_softc *, uint32_t); 14112e36acbSWarner Losh 14212e36acbSWarner Losh static int bwi_init_tx_ring32(struct bwi_softc *, int); 14312e36acbSWarner Losh static int bwi_init_rx_ring32(struct bwi_softc *); 14412e36acbSWarner Losh static int bwi_init_txstats32(struct bwi_softc *); 14512e36acbSWarner Losh static void bwi_free_tx_ring32(struct bwi_softc *, int); 14612e36acbSWarner Losh static void bwi_free_rx_ring32(struct bwi_softc *); 14712e36acbSWarner Losh static void bwi_free_txstats32(struct bwi_softc *); 14812e36acbSWarner Losh static void bwi_setup_rx_desc32(struct bwi_softc *, int, bus_addr_t, int); 14912e36acbSWarner Losh static void bwi_setup_tx_desc32(struct bwi_softc *, struct bwi_ring_data *, 15012e36acbSWarner Losh int, bus_addr_t, int); 15112e36acbSWarner Losh static int bwi_rxeof32(struct bwi_softc *); 15212e36acbSWarner Losh static void bwi_start_tx32(struct bwi_softc *, uint32_t, int); 15312e36acbSWarner Losh static void bwi_txeof_status32(struct bwi_softc *); 15412e36acbSWarner Losh 15512e36acbSWarner Losh static int bwi_init_tx_ring64(struct bwi_softc *, int); 15612e36acbSWarner Losh static int bwi_init_rx_ring64(struct bwi_softc *); 15712e36acbSWarner Losh static int bwi_init_txstats64(struct bwi_softc *); 15812e36acbSWarner Losh static void bwi_free_tx_ring64(struct bwi_softc *, int); 15912e36acbSWarner Losh static void bwi_free_rx_ring64(struct bwi_softc *); 16012e36acbSWarner Losh static void bwi_free_txstats64(struct bwi_softc *); 16112e36acbSWarner Losh static void bwi_setup_rx_desc64(struct bwi_softc *, int, bus_addr_t, int); 16212e36acbSWarner Losh static void bwi_setup_tx_desc64(struct bwi_softc *, struct bwi_ring_data *, 16312e36acbSWarner Losh int, bus_addr_t, int); 16412e36acbSWarner Losh static int bwi_rxeof64(struct bwi_softc *); 16512e36acbSWarner Losh static void bwi_start_tx64(struct bwi_softc *, uint32_t, int); 16612e36acbSWarner Losh static void bwi_txeof_status64(struct bwi_softc *); 16712e36acbSWarner Losh 16812e36acbSWarner Losh static int bwi_rxeof(struct bwi_softc *, int); 16912e36acbSWarner Losh static void _bwi_txeof(struct bwi_softc *, uint16_t, int, int); 17012e36acbSWarner Losh static void bwi_txeof(struct bwi_softc *); 17112e36acbSWarner Losh static void bwi_txeof_status(struct bwi_softc *, int); 17212e36acbSWarner Losh static void bwi_enable_intrs(struct bwi_softc *, uint32_t); 17312e36acbSWarner Losh static void bwi_disable_intrs(struct bwi_softc *, uint32_t); 17412e36acbSWarner Losh 17512e36acbSWarner Losh static int bwi_dma_alloc(struct bwi_softc *); 17612e36acbSWarner Losh static void bwi_dma_free(struct bwi_softc *); 17712e36acbSWarner Losh static int bwi_dma_ring_alloc(struct bwi_softc *, bus_dma_tag_t, 17812e36acbSWarner Losh struct bwi_ring_data *, bus_size_t, 17912e36acbSWarner Losh uint32_t); 18012e36acbSWarner Losh static int bwi_dma_mbuf_create(struct bwi_softc *); 18112e36acbSWarner Losh static void bwi_dma_mbuf_destroy(struct bwi_softc *, int, int); 18212e36acbSWarner Losh static int bwi_dma_txstats_alloc(struct bwi_softc *, uint32_t, bus_size_t); 18312e36acbSWarner Losh static void bwi_dma_txstats_free(struct bwi_softc *); 18412e36acbSWarner Losh static void bwi_dma_ring_addr(void *, bus_dma_segment_t *, int, int); 18512e36acbSWarner Losh static void bwi_dma_buf_addr(void *, bus_dma_segment_t *, int, 18612e36acbSWarner Losh bus_size_t, int); 18712e36acbSWarner Losh 18812e36acbSWarner Losh static void bwi_power_on(struct bwi_softc *, int); 18912e36acbSWarner Losh static int bwi_power_off(struct bwi_softc *, int); 19012e36acbSWarner Losh static int bwi_set_clock_mode(struct bwi_softc *, enum bwi_clock_mode); 19112e36acbSWarner Losh static int bwi_set_clock_delay(struct bwi_softc *); 19212e36acbSWarner Losh static void bwi_get_clock_freq(struct bwi_softc *, struct bwi_clock_freq *); 19312e36acbSWarner Losh static int bwi_get_pwron_delay(struct bwi_softc *sc); 19412e36acbSWarner Losh static void bwi_set_addr_filter(struct bwi_softc *, uint16_t, 19512e36acbSWarner Losh const uint8_t *); 19612e36acbSWarner Losh static void bwi_set_bssid(struct bwi_softc *, const uint8_t *); 19712e36acbSWarner Losh 19812e36acbSWarner Losh static void bwi_get_card_flags(struct bwi_softc *); 19912e36acbSWarner Losh static void bwi_get_eaddr(struct bwi_softc *, uint16_t, uint8_t *); 20012e36acbSWarner Losh 20112e36acbSWarner Losh static int bwi_bus_attach(struct bwi_softc *); 20212e36acbSWarner Losh static int bwi_bbp_attach(struct bwi_softc *); 20312e36acbSWarner Losh static int bwi_bbp_power_on(struct bwi_softc *, enum bwi_clock_mode); 20412e36acbSWarner Losh static void bwi_bbp_power_off(struct bwi_softc *); 20512e36acbSWarner Losh 20612e36acbSWarner Losh static const char *bwi_regwin_name(const struct bwi_regwin *); 20712e36acbSWarner Losh static uint32_t bwi_regwin_disable_bits(struct bwi_softc *); 20812e36acbSWarner Losh static void bwi_regwin_info(struct bwi_softc *, uint16_t *, uint8_t *); 20912e36acbSWarner Losh static int bwi_regwin_select(struct bwi_softc *, int); 21012e36acbSWarner Losh 21112e36acbSWarner Losh static void bwi_led_attach(struct bwi_softc *); 21212e36acbSWarner Losh static void bwi_led_newstate(struct bwi_softc *, enum ieee80211_state); 21312e36acbSWarner Losh static void bwi_led_event(struct bwi_softc *, int); 21412e36acbSWarner Losh static void bwi_led_blink_start(struct bwi_softc *, int, int); 21512e36acbSWarner Losh static void bwi_led_blink_next(void *); 21612e36acbSWarner Losh static void bwi_led_blink_end(void *); 21712e36acbSWarner Losh 21812e36acbSWarner Losh static const struct { 21912e36acbSWarner Losh uint16_t did_min; 22012e36acbSWarner Losh uint16_t did_max; 22112e36acbSWarner Losh uint16_t bbp_id; 22212e36acbSWarner Losh } bwi_bbpid_map[] = { 22312e36acbSWarner Losh { 0x4301, 0x4301, 0x4301 }, 22412e36acbSWarner Losh { 0x4305, 0x4307, 0x4307 }, 225e0082150SAdrian Chadd { 0x4402, 0x4403, 0x4402 }, 22612e36acbSWarner Losh { 0x4610, 0x4615, 0x4610 }, 22712e36acbSWarner Losh { 0x4710, 0x4715, 0x4710 }, 22812e36acbSWarner Losh { 0x4720, 0x4725, 0x4309 } 22912e36acbSWarner Losh }; 23012e36acbSWarner Losh 23112e36acbSWarner Losh static const struct { 23212e36acbSWarner Losh uint16_t bbp_id; 23312e36acbSWarner Losh int nregwin; 23412e36acbSWarner Losh } bwi_regwin_count[] = { 23512e36acbSWarner Losh { 0x4301, 5 }, 23612e36acbSWarner Losh { 0x4306, 6 }, 23712e36acbSWarner Losh { 0x4307, 5 }, 23812e36acbSWarner Losh { 0x4310, 8 }, 23912e36acbSWarner Losh { 0x4401, 3 }, 24012e36acbSWarner Losh { 0x4402, 3 }, 24112e36acbSWarner Losh { 0x4610, 9 }, 24212e36acbSWarner Losh { 0x4704, 9 }, 24312e36acbSWarner Losh { 0x4710, 9 }, 24412e36acbSWarner Losh { 0x5365, 7 } 24512e36acbSWarner Losh }; 24612e36acbSWarner Losh 24712e36acbSWarner Losh #define CLKSRC(src) \ 24812e36acbSWarner Losh [BWI_CLKSRC_ ## src] = { \ 24912e36acbSWarner Losh .freq_min = BWI_CLKSRC_ ##src## _FMIN, \ 25012e36acbSWarner Losh .freq_max = BWI_CLKSRC_ ##src## _FMAX \ 25112e36acbSWarner Losh } 25212e36acbSWarner Losh 25312e36acbSWarner Losh static const struct { 25412e36acbSWarner Losh u_int freq_min; 25512e36acbSWarner Losh u_int freq_max; 25612e36acbSWarner Losh } bwi_clkfreq[BWI_CLKSRC_MAX] = { 25712e36acbSWarner Losh CLKSRC(LP_OSC), 25812e36acbSWarner Losh CLKSRC(CS_OSC), 25912e36acbSWarner Losh CLKSRC(PCI) 26012e36acbSWarner Losh }; 26112e36acbSWarner Losh 26212e36acbSWarner Losh #undef CLKSRC 26312e36acbSWarner Losh 26412e36acbSWarner Losh #define VENDOR_LED_ACT(vendor) \ 26512e36acbSWarner Losh { \ 26612e36acbSWarner Losh .vid = PCI_VENDOR_##vendor, \ 26712e36acbSWarner Losh .led_act = { BWI_VENDOR_LED_ACT_##vendor } \ 26812e36acbSWarner Losh } 26912e36acbSWarner Losh 27012e36acbSWarner Losh static const struct { 27112e36acbSWarner Losh #define PCI_VENDOR_COMPAQ 0x0e11 27212e36acbSWarner Losh #define PCI_VENDOR_LINKSYS 0x1737 27312e36acbSWarner Losh uint16_t vid; 27412e36acbSWarner Losh uint8_t led_act[BWI_LED_MAX]; 27512e36acbSWarner Losh } bwi_vendor_led_act[] = { 27612e36acbSWarner Losh VENDOR_LED_ACT(COMPAQ), 27712e36acbSWarner Losh VENDOR_LED_ACT(LINKSYS) 27812e36acbSWarner Losh #undef PCI_VENDOR_LINKSYS 27912e36acbSWarner Losh #undef PCI_VENDOR_COMPAQ 28012e36acbSWarner Losh }; 28112e36acbSWarner Losh 28212e36acbSWarner Losh static const uint8_t bwi_default_led_act[BWI_LED_MAX] = 28312e36acbSWarner Losh { BWI_VENDOR_LED_ACT_DEFAULT }; 28412e36acbSWarner Losh 28512e36acbSWarner Losh #undef VENDOR_LED_ACT 28612e36acbSWarner Losh 28712e36acbSWarner Losh static const struct { 28812e36acbSWarner Losh int on_dur; 28912e36acbSWarner Losh int off_dur; 29012e36acbSWarner Losh } bwi_led_duration[109] = { 29112e36acbSWarner Losh [0] = { 400, 100 }, 29212e36acbSWarner Losh [2] = { 150, 75 }, 29312e36acbSWarner Losh [4] = { 90, 45 }, 29412e36acbSWarner Losh [11] = { 66, 34 }, 29512e36acbSWarner Losh [12] = { 53, 26 }, 29612e36acbSWarner Losh [18] = { 42, 21 }, 29712e36acbSWarner Losh [22] = { 35, 17 }, 29812e36acbSWarner Losh [24] = { 32, 16 }, 29912e36acbSWarner Losh [36] = { 21, 10 }, 30012e36acbSWarner Losh [48] = { 16, 8 }, 30112e36acbSWarner Losh [72] = { 11, 5 }, 30212e36acbSWarner Losh [96] = { 9, 4 }, 30312e36acbSWarner Losh [108] = { 7, 3 } 30412e36acbSWarner Losh }; 30512e36acbSWarner Losh 30612e36acbSWarner Losh #ifdef BWI_DEBUG 30712e36acbSWarner Losh #ifdef BWI_DEBUG_VERBOSE 30812e36acbSWarner Losh static uint32_t bwi_debug = BWI_DBG_ATTACH | BWI_DBG_INIT | BWI_DBG_TXPOWER; 30912e36acbSWarner Losh #else 31012e36acbSWarner Losh static uint32_t bwi_debug; 31112e36acbSWarner Losh #endif 31212e36acbSWarner Losh TUNABLE_INT("hw.bwi.debug", (int *)&bwi_debug); 31312e36acbSWarner Losh #endif /* BWI_DEBUG */ 31412e36acbSWarner Losh 31512e36acbSWarner Losh static const uint8_t bwi_zero_addr[IEEE80211_ADDR_LEN]; 31612e36acbSWarner Losh 31712e36acbSWarner Losh uint16_t 31812e36acbSWarner Losh bwi_read_sprom(struct bwi_softc *sc, uint16_t ofs) 31912e36acbSWarner Losh { 32012e36acbSWarner Losh return CSR_READ_2(sc, ofs + BWI_SPROM_START); 32112e36acbSWarner Losh } 32212e36acbSWarner Losh 32312e36acbSWarner Losh static __inline void 32412e36acbSWarner Losh bwi_setup_desc32(struct bwi_softc *sc, struct bwi_desc32 *desc_array, 32512e36acbSWarner Losh int ndesc, int desc_idx, bus_addr_t paddr, int buf_len, 32612e36acbSWarner Losh int tx) 32712e36acbSWarner Losh { 32812e36acbSWarner Losh struct bwi_desc32 *desc = &desc_array[desc_idx]; 32912e36acbSWarner Losh uint32_t ctrl, addr, addr_hi, addr_lo; 33012e36acbSWarner Losh 33112e36acbSWarner Losh addr_lo = __SHIFTOUT(paddr, BWI_DESC32_A_ADDR_MASK); 33212e36acbSWarner Losh addr_hi = __SHIFTOUT(paddr, BWI_DESC32_A_FUNC_MASK); 33312e36acbSWarner Losh 33412e36acbSWarner Losh addr = __SHIFTIN(addr_lo, BWI_DESC32_A_ADDR_MASK) | 33512e36acbSWarner Losh __SHIFTIN(BWI_DESC32_A_FUNC_TXRX, BWI_DESC32_A_FUNC_MASK); 33612e36acbSWarner Losh 33712e36acbSWarner Losh ctrl = __SHIFTIN(buf_len, BWI_DESC32_C_BUFLEN_MASK) | 33812e36acbSWarner Losh __SHIFTIN(addr_hi, BWI_DESC32_C_ADDRHI_MASK); 33912e36acbSWarner Losh if (desc_idx == ndesc - 1) 34012e36acbSWarner Losh ctrl |= BWI_DESC32_C_EOR; 34112e36acbSWarner Losh if (tx) { 34212e36acbSWarner Losh /* XXX */ 34312e36acbSWarner Losh ctrl |= BWI_DESC32_C_FRAME_START | 34412e36acbSWarner Losh BWI_DESC32_C_FRAME_END | 34512e36acbSWarner Losh BWI_DESC32_C_INTR; 34612e36acbSWarner Losh } 34712e36acbSWarner Losh 34812e36acbSWarner Losh desc->addr = htole32(addr); 34912e36acbSWarner Losh desc->ctrl = htole32(ctrl); 35012e36acbSWarner Losh } 35112e36acbSWarner Losh 35212e36acbSWarner Losh int 35312e36acbSWarner Losh bwi_attach(struct bwi_softc *sc) 35412e36acbSWarner Losh { 35512e36acbSWarner Losh struct ieee80211com *ic; 35612e36acbSWarner Losh device_t dev = sc->sc_dev; 35712e36acbSWarner Losh struct ifnet *ifp; 35812e36acbSWarner Losh struct bwi_mac *mac; 35912e36acbSWarner Losh struct bwi_phy *phy; 36012e36acbSWarner Losh int i, error; 36112e36acbSWarner Losh uint8_t bands; 36212e36acbSWarner Losh uint8_t macaddr[IEEE80211_ADDR_LEN]; 36312e36acbSWarner Losh 36412e36acbSWarner Losh BWI_LOCK_INIT(sc); 36512e36acbSWarner Losh 36612e36acbSWarner Losh /* 36712e36acbSWarner Losh * Initialize taskq and various tasks 36812e36acbSWarner Losh */ 36912e36acbSWarner Losh sc->sc_tq = taskqueue_create("bwi_taskq", M_NOWAIT | M_ZERO, 37012e36acbSWarner Losh taskqueue_thread_enqueue, &sc->sc_tq); 37112e36acbSWarner Losh taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", 37212e36acbSWarner Losh device_get_nameunit(dev)); 37312e36acbSWarner Losh TASK_INIT(&sc->sc_restart_task, 0, bwi_restart, sc); 37412e36acbSWarner Losh 37512e36acbSWarner Losh callout_init_mtx(&sc->sc_calib_ch, &sc->sc_mtx, 0); 37612e36acbSWarner Losh 37712e36acbSWarner Losh /* 37812e36acbSWarner Losh * Initialize sysctl variables 37912e36acbSWarner Losh */ 38012e36acbSWarner Losh sc->sc_fw_version = BWI_FW_VERSION3; 38112e36acbSWarner Losh sc->sc_led_idle = (2350 * hz) / 1000; 38212e36acbSWarner Losh sc->sc_led_blink = 1; 38312e36acbSWarner Losh sc->sc_txpwr_calib = 1; 38412e36acbSWarner Losh #ifdef BWI_DEBUG 38512e36acbSWarner Losh sc->sc_debug = bwi_debug; 38612e36acbSWarner Losh #endif 38712e36acbSWarner Losh bwi_power_on(sc, 1); 38812e36acbSWarner Losh 38912e36acbSWarner Losh error = bwi_bbp_attach(sc); 39012e36acbSWarner Losh if (error) 39112e36acbSWarner Losh goto fail; 39212e36acbSWarner Losh 39312e36acbSWarner Losh error = bwi_bbp_power_on(sc, BWI_CLOCK_MODE_FAST); 39412e36acbSWarner Losh if (error) 39512e36acbSWarner Losh goto fail; 39612e36acbSWarner Losh 39712e36acbSWarner Losh if (BWI_REGWIN_EXIST(&sc->sc_com_regwin)) { 39812e36acbSWarner Losh error = bwi_set_clock_delay(sc); 39912e36acbSWarner Losh if (error) 40012e36acbSWarner Losh goto fail; 40112e36acbSWarner Losh 40212e36acbSWarner Losh error = bwi_set_clock_mode(sc, BWI_CLOCK_MODE_FAST); 40312e36acbSWarner Losh if (error) 40412e36acbSWarner Losh goto fail; 40512e36acbSWarner Losh 40612e36acbSWarner Losh error = bwi_get_pwron_delay(sc); 40712e36acbSWarner Losh if (error) 40812e36acbSWarner Losh goto fail; 40912e36acbSWarner Losh } 41012e36acbSWarner Losh 41112e36acbSWarner Losh error = bwi_bus_attach(sc); 41212e36acbSWarner Losh if (error) 41312e36acbSWarner Losh goto fail; 41412e36acbSWarner Losh 41512e36acbSWarner Losh bwi_get_card_flags(sc); 41612e36acbSWarner Losh 41712e36acbSWarner Losh bwi_led_attach(sc); 41812e36acbSWarner Losh 41912e36acbSWarner Losh for (i = 0; i < sc->sc_nmac; ++i) { 42012e36acbSWarner Losh struct bwi_regwin *old; 42112e36acbSWarner Losh 42212e36acbSWarner Losh mac = &sc->sc_mac[i]; 42312e36acbSWarner Losh error = bwi_regwin_switch(sc, &mac->mac_regwin, &old); 42412e36acbSWarner Losh if (error) 42512e36acbSWarner Losh goto fail; 42612e36acbSWarner Losh 42712e36acbSWarner Losh error = bwi_mac_lateattach(mac); 42812e36acbSWarner Losh if (error) 42912e36acbSWarner Losh goto fail; 43012e36acbSWarner Losh 43112e36acbSWarner Losh error = bwi_regwin_switch(sc, old, NULL); 43212e36acbSWarner Losh if (error) 43312e36acbSWarner Losh goto fail; 43412e36acbSWarner Losh } 43512e36acbSWarner Losh 43612e36acbSWarner Losh /* 43712e36acbSWarner Losh * XXX First MAC is known to exist 43812e36acbSWarner Losh * TODO2 43912e36acbSWarner Losh */ 44012e36acbSWarner Losh mac = &sc->sc_mac[0]; 44112e36acbSWarner Losh phy = &mac->mac_phy; 44212e36acbSWarner Losh 44312e36acbSWarner Losh bwi_bbp_power_off(sc); 44412e36acbSWarner Losh 44512e36acbSWarner Losh error = bwi_dma_alloc(sc); 44612e36acbSWarner Losh if (error) 44712e36acbSWarner Losh goto fail; 44812e36acbSWarner Losh 449c803f24bSGleb Smirnoff error = bwi_mac_fw_alloc(mac); 450c803f24bSGleb Smirnoff if (error) 451c803f24bSGleb Smirnoff goto fail; 452c803f24bSGleb Smirnoff 45312e36acbSWarner Losh ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 45412e36acbSWarner Losh if (ifp == NULL) { 45512e36acbSWarner Losh device_printf(dev, "can not if_alloc()\n"); 45612e36acbSWarner Losh error = ENOSPC; 45712e36acbSWarner Losh goto fail; 45812e36acbSWarner Losh } 45912e36acbSWarner Losh ic = ifp->if_l2com; 46012e36acbSWarner Losh 46112e36acbSWarner Losh /* set these up early for if_printf use */ 46212e36acbSWarner Losh if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 46312e36acbSWarner Losh 46412e36acbSWarner Losh ifp->if_softc = sc; 46512e36acbSWarner Losh ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 46612e36acbSWarner Losh ifp->if_init = bwi_init; 46712e36acbSWarner Losh ifp->if_ioctl = bwi_ioctl; 46812e36acbSWarner Losh ifp->if_start = bwi_start; 469e50d35e6SMaxim Sobolev IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 470e50d35e6SMaxim Sobolev ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 47112e36acbSWarner Losh IFQ_SET_READY(&ifp->if_snd); 47258fbe5abSJohn Baldwin callout_init_mtx(&sc->sc_watchdog_timer, &sc->sc_mtx, 0); 47312e36acbSWarner Losh 47412e36acbSWarner Losh /* 47512e36acbSWarner Losh * Setup ratesets, phytype, channels and get MAC address 47612e36acbSWarner Losh */ 47712e36acbSWarner Losh bands = 0; 47812e36acbSWarner Losh if (phy->phy_mode == IEEE80211_MODE_11B || 47912e36acbSWarner Losh phy->phy_mode == IEEE80211_MODE_11G) { 48012e36acbSWarner Losh setbit(&bands, IEEE80211_MODE_11B); 48112e36acbSWarner Losh if (phy->phy_mode == IEEE80211_MODE_11B) { 48212e36acbSWarner Losh ic->ic_phytype = IEEE80211_T_DS; 48312e36acbSWarner Losh } else { 48412e36acbSWarner Losh ic->ic_phytype = IEEE80211_T_OFDM; 48512e36acbSWarner Losh setbit(&bands, IEEE80211_MODE_11G); 48612e36acbSWarner Losh } 48712e36acbSWarner Losh 48812e36acbSWarner Losh bwi_get_eaddr(sc, BWI_SPROM_11BG_EADDR, macaddr); 48912e36acbSWarner Losh if (IEEE80211_IS_MULTICAST(macaddr)) { 49012e36acbSWarner Losh bwi_get_eaddr(sc, BWI_SPROM_11A_EADDR, macaddr); 49112e36acbSWarner Losh if (IEEE80211_IS_MULTICAST(macaddr)) { 49212e36acbSWarner Losh device_printf(dev, 49312e36acbSWarner Losh "invalid MAC address: %6D\n", 49412e36acbSWarner Losh macaddr, ":"); 49512e36acbSWarner Losh } 49612e36acbSWarner Losh } 49712e36acbSWarner Losh } else if (phy->phy_mode == IEEE80211_MODE_11A) { 49812e36acbSWarner Losh /* TODO:11A */ 49912e36acbSWarner Losh setbit(&bands, IEEE80211_MODE_11A); 50012e36acbSWarner Losh error = ENXIO; 50112e36acbSWarner Losh goto fail; 50212e36acbSWarner Losh } else { 50312e36acbSWarner Losh panic("unknown phymode %d\n", phy->phy_mode); 50412e36acbSWarner Losh } 50512e36acbSWarner Losh 50612e36acbSWarner Losh /* Get locale */ 50712e36acbSWarner Losh sc->sc_locale = __SHIFTOUT(bwi_read_sprom(sc, BWI_SPROM_CARD_INFO), 50812e36acbSWarner Losh BWI_SPROM_CARD_INFO_LOCALE); 50912e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "locale: %d\n", sc->sc_locale); 51012e36acbSWarner Losh /* XXX use locale */ 51112e36acbSWarner Losh ieee80211_init_channels(ic, NULL, &bands); 51212e36acbSWarner Losh 51312e36acbSWarner Losh ic->ic_ifp = ifp; 51459686fe9SGleb Smirnoff ic->ic_softc = sc; 515c8550c02SGleb Smirnoff ic->ic_name = device_get_nameunit(dev); 51612e36acbSWarner Losh ic->ic_caps = IEEE80211_C_STA | 51712e36acbSWarner Losh IEEE80211_C_SHSLOT | 51812e36acbSWarner Losh IEEE80211_C_SHPREAMBLE | 51912e36acbSWarner Losh IEEE80211_C_WPA | 52012e36acbSWarner Losh IEEE80211_C_BGSCAN | 521a7c6aabdSBernhard Schmidt IEEE80211_C_MONITOR; 52212e36acbSWarner Losh ic->ic_opmode = IEEE80211_M_STA; 52312e36acbSWarner Losh ieee80211_ifattach(ic, macaddr); 52412e36acbSWarner Losh 52512e36acbSWarner Losh ic->ic_headroom = sizeof(struct bwi_txbuf_hdr); 52612e36acbSWarner Losh 52712e36acbSWarner Losh /* override default methods */ 52812e36acbSWarner Losh ic->ic_vap_create = bwi_vap_create; 52912e36acbSWarner Losh ic->ic_vap_delete = bwi_vap_delete; 53012e36acbSWarner Losh ic->ic_raw_xmit = bwi_raw_xmit; 53112e36acbSWarner Losh ic->ic_updateslot = bwi_updateslot; 53212e36acbSWarner Losh ic->ic_scan_start = bwi_scan_start; 53312e36acbSWarner Losh ic->ic_scan_end = bwi_scan_end; 53412e36acbSWarner Losh ic->ic_set_channel = bwi_set_channel; 53512e36acbSWarner Losh 53612e36acbSWarner Losh sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan); 53712e36acbSWarner Losh 5385463c4a4SSam Leffler ieee80211_radiotap_attach(ic, 5395463c4a4SSam Leffler &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th), 5405463c4a4SSam Leffler BWI_TX_RADIOTAP_PRESENT, 5415463c4a4SSam Leffler &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th), 5425463c4a4SSam Leffler BWI_RX_RADIOTAP_PRESENT); 54312e36acbSWarner Losh 54412e36acbSWarner Losh /* 54512e36acbSWarner Losh * Add sysctl nodes 54612e36acbSWarner Losh */ 5476dc7dc9aSMatthew D Fleming SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 54812e36acbSWarner Losh SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 54912e36acbSWarner Losh "fw_version", CTLFLAG_RD, &sc->sc_fw_version, 0, 55012e36acbSWarner Losh "Firmware version"); 5516dc7dc9aSMatthew D Fleming SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 55212e36acbSWarner Losh SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 55312e36acbSWarner Losh "led_idle", CTLFLAG_RW, &sc->sc_led_idle, 0, 55412e36acbSWarner Losh "# ticks before LED enters idle state"); 55512e36acbSWarner Losh SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 55612e36acbSWarner Losh SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 55712e36acbSWarner Losh "led_blink", CTLFLAG_RW, &sc->sc_led_blink, 0, 55812e36acbSWarner Losh "Allow LED to blink"); 55912e36acbSWarner Losh SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 56012e36acbSWarner Losh SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 56112e36acbSWarner Losh "txpwr_calib", CTLFLAG_RW, &sc->sc_txpwr_calib, 0, 56212e36acbSWarner Losh "Enable software TX power calibration"); 56312e36acbSWarner Losh #ifdef BWI_DEBUG 56412e36acbSWarner Losh SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 56512e36acbSWarner Losh SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 56612e36acbSWarner Losh "debug", CTLFLAG_RW, &sc->sc_debug, 0, "Debug flags"); 56712e36acbSWarner Losh #endif 56812e36acbSWarner Losh if (bootverbose) 56912e36acbSWarner Losh ieee80211_announce(ic); 57012e36acbSWarner Losh 57112e36acbSWarner Losh return (0); 57212e36acbSWarner Losh fail: 57312e36acbSWarner Losh BWI_LOCK_DESTROY(sc); 57412e36acbSWarner Losh return (error); 57512e36acbSWarner Losh } 57612e36acbSWarner Losh 57712e36acbSWarner Losh int 57812e36acbSWarner Losh bwi_detach(struct bwi_softc *sc) 57912e36acbSWarner Losh { 58012e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 58112e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 58212e36acbSWarner Losh int i; 58312e36acbSWarner Losh 58412e36acbSWarner Losh bwi_stop(sc, 1); 5857389f7dbSWarner Losh callout_drain(&sc->sc_led_blink_ch); 58612e36acbSWarner Losh callout_drain(&sc->sc_calib_ch); 58758fbe5abSJohn Baldwin callout_drain(&sc->sc_watchdog_timer); 58812e36acbSWarner Losh ieee80211_ifdetach(ic); 58912e36acbSWarner Losh 59012e36acbSWarner Losh for (i = 0; i < sc->sc_nmac; ++i) 59112e36acbSWarner Losh bwi_mac_detach(&sc->sc_mac[i]); 59212e36acbSWarner Losh bwi_dma_free(sc); 59312e36acbSWarner Losh if_free(ifp); 59412e36acbSWarner Losh taskqueue_free(sc->sc_tq); 59512e36acbSWarner Losh 59612e36acbSWarner Losh BWI_LOCK_DESTROY(sc); 59712e36acbSWarner Losh 59812e36acbSWarner Losh return (0); 59912e36acbSWarner Losh } 60012e36acbSWarner Losh 60112e36acbSWarner Losh static struct ieee80211vap * 602fcd9500fSBernhard Schmidt bwi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 603fcd9500fSBernhard Schmidt enum ieee80211_opmode opmode, int flags, 60412e36acbSWarner Losh const uint8_t bssid[IEEE80211_ADDR_LEN], 60512e36acbSWarner Losh const uint8_t mac[IEEE80211_ADDR_LEN]) 60612e36acbSWarner Losh { 60712e36acbSWarner Losh struct bwi_vap *bvp; 60812e36acbSWarner Losh struct ieee80211vap *vap; 60912e36acbSWarner Losh 61012e36acbSWarner Losh if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 61112e36acbSWarner Losh return NULL; 61212e36acbSWarner Losh bvp = (struct bwi_vap *) malloc(sizeof(struct bwi_vap), 61312e36acbSWarner Losh M_80211_VAP, M_WAITOK | M_ZERO); 61412e36acbSWarner Losh if (bvp == NULL) 61512e36acbSWarner Losh return NULL; 61612e36acbSWarner Losh vap = &bvp->bv_vap; 61712e36acbSWarner Losh /* enable s/w bmiss handling for sta mode */ 61812e36acbSWarner Losh ieee80211_vap_setup(ic, vap, name, unit, opmode, 61912e36acbSWarner Losh flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); 62012e36acbSWarner Losh 62112e36acbSWarner Losh /* override default methods */ 62212e36acbSWarner Losh bvp->bv_newstate = vap->iv_newstate; 62312e36acbSWarner Losh vap->iv_newstate = bwi_newstate; 62412e36acbSWarner Losh #if 0 62512e36acbSWarner Losh vap->iv_update_beacon = bwi_beacon_update; 62612e36acbSWarner Losh #endif 627b6108616SRui Paulo ieee80211_ratectl_init(vap); 62812e36acbSWarner Losh 62912e36acbSWarner Losh /* complete setup */ 63012e36acbSWarner Losh ieee80211_vap_attach(vap, bwi_media_change, ieee80211_media_status); 63112e36acbSWarner Losh ic->ic_opmode = opmode; 63212e36acbSWarner Losh return vap; 63312e36acbSWarner Losh } 63412e36acbSWarner Losh 63512e36acbSWarner Losh static void 63612e36acbSWarner Losh bwi_vap_delete(struct ieee80211vap *vap) 63712e36acbSWarner Losh { 63812e36acbSWarner Losh struct bwi_vap *bvp = BWI_VAP(vap); 63912e36acbSWarner Losh 640b6108616SRui Paulo ieee80211_ratectl_deinit(vap); 64112e36acbSWarner Losh ieee80211_vap_detach(vap); 64212e36acbSWarner Losh free(bvp, M_80211_VAP); 64312e36acbSWarner Losh } 64412e36acbSWarner Losh 64512e36acbSWarner Losh void 64612e36acbSWarner Losh bwi_suspend(struct bwi_softc *sc) 64712e36acbSWarner Losh { 64812e36acbSWarner Losh bwi_stop(sc, 1); 64912e36acbSWarner Losh } 65012e36acbSWarner Losh 65112e36acbSWarner Losh void 65212e36acbSWarner Losh bwi_resume(struct bwi_softc *sc) 65312e36acbSWarner Losh { 65412e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 65512e36acbSWarner Losh 65612e36acbSWarner Losh if (ifp->if_flags & IFF_UP) 65712e36acbSWarner Losh bwi_init(sc); 65812e36acbSWarner Losh } 65912e36acbSWarner Losh 66012e36acbSWarner Losh int 66112e36acbSWarner Losh bwi_shutdown(struct bwi_softc *sc) 66212e36acbSWarner Losh { 66312e36acbSWarner Losh bwi_stop(sc, 1); 66412e36acbSWarner Losh return 0; 66512e36acbSWarner Losh } 66612e36acbSWarner Losh 66712e36acbSWarner Losh static void 66812e36acbSWarner Losh bwi_power_on(struct bwi_softc *sc, int with_pll) 66912e36acbSWarner Losh { 67012e36acbSWarner Losh uint32_t gpio_in, gpio_out, gpio_en; 67112e36acbSWarner Losh uint16_t status; 67212e36acbSWarner Losh 67312e36acbSWarner Losh gpio_in = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_IN, 4); 67412e36acbSWarner Losh if (gpio_in & BWI_PCIM_GPIO_PWR_ON) 67512e36acbSWarner Losh goto back; 67612e36acbSWarner Losh 67712e36acbSWarner Losh gpio_out = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, 4); 67812e36acbSWarner Losh gpio_en = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, 4); 67912e36acbSWarner Losh 68012e36acbSWarner Losh gpio_out |= BWI_PCIM_GPIO_PWR_ON; 68112e36acbSWarner Losh gpio_en |= BWI_PCIM_GPIO_PWR_ON; 68212e36acbSWarner Losh if (with_pll) { 68312e36acbSWarner Losh /* Turn off PLL first */ 68412e36acbSWarner Losh gpio_out |= BWI_PCIM_GPIO_PLL_PWR_OFF; 68512e36acbSWarner Losh gpio_en |= BWI_PCIM_GPIO_PLL_PWR_OFF; 68612e36acbSWarner Losh } 68712e36acbSWarner Losh 68812e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, gpio_out, 4); 68912e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, gpio_en, 4); 69012e36acbSWarner Losh DELAY(1000); 69112e36acbSWarner Losh 69212e36acbSWarner Losh if (with_pll) { 69312e36acbSWarner Losh /* Turn on PLL */ 69412e36acbSWarner Losh gpio_out &= ~BWI_PCIM_GPIO_PLL_PWR_OFF; 69512e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, gpio_out, 4); 69612e36acbSWarner Losh DELAY(5000); 69712e36acbSWarner Losh } 69812e36acbSWarner Losh 69912e36acbSWarner Losh back: 70012e36acbSWarner Losh /* Clear "Signaled Target Abort" */ 70112e36acbSWarner Losh status = pci_read_config(sc->sc_dev, PCIR_STATUS, 2); 70212e36acbSWarner Losh status &= ~PCIM_STATUS_STABORT; 70312e36acbSWarner Losh pci_write_config(sc->sc_dev, PCIR_STATUS, status, 2); 70412e36acbSWarner Losh } 70512e36acbSWarner Losh 70612e36acbSWarner Losh static int 70712e36acbSWarner Losh bwi_power_off(struct bwi_softc *sc, int with_pll) 70812e36acbSWarner Losh { 70912e36acbSWarner Losh uint32_t gpio_out, gpio_en; 71012e36acbSWarner Losh 71112e36acbSWarner Losh pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_IN, 4); /* dummy read */ 71212e36acbSWarner Losh gpio_out = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, 4); 71312e36acbSWarner Losh gpio_en = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, 4); 71412e36acbSWarner Losh 71512e36acbSWarner Losh gpio_out &= ~BWI_PCIM_GPIO_PWR_ON; 71612e36acbSWarner Losh gpio_en |= BWI_PCIM_GPIO_PWR_ON; 71712e36acbSWarner Losh if (with_pll) { 71812e36acbSWarner Losh gpio_out |= BWI_PCIM_GPIO_PLL_PWR_OFF; 71912e36acbSWarner Losh gpio_en |= BWI_PCIM_GPIO_PLL_PWR_OFF; 72012e36acbSWarner Losh } 72112e36acbSWarner Losh 72212e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, gpio_out, 4); 72312e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_GPIO_ENABLE, gpio_en, 4); 72412e36acbSWarner Losh return 0; 72512e36acbSWarner Losh } 72612e36acbSWarner Losh 72712e36acbSWarner Losh int 72812e36acbSWarner Losh bwi_regwin_switch(struct bwi_softc *sc, struct bwi_regwin *rw, 72912e36acbSWarner Losh struct bwi_regwin **old_rw) 73012e36acbSWarner Losh { 73112e36acbSWarner Losh int error; 73212e36acbSWarner Losh 73312e36acbSWarner Losh if (old_rw != NULL) 73412e36acbSWarner Losh *old_rw = NULL; 73512e36acbSWarner Losh 73612e36acbSWarner Losh if (!BWI_REGWIN_EXIST(rw)) 73712e36acbSWarner Losh return EINVAL; 73812e36acbSWarner Losh 73912e36acbSWarner Losh if (sc->sc_cur_regwin != rw) { 74012e36acbSWarner Losh error = bwi_regwin_select(sc, rw->rw_id); 74112e36acbSWarner Losh if (error) { 74212e36acbSWarner Losh device_printf(sc->sc_dev, "can't select regwin %d\n", 74312e36acbSWarner Losh rw->rw_id); 74412e36acbSWarner Losh return error; 74512e36acbSWarner Losh } 74612e36acbSWarner Losh } 74712e36acbSWarner Losh 74812e36acbSWarner Losh if (old_rw != NULL) 74912e36acbSWarner Losh *old_rw = sc->sc_cur_regwin; 75012e36acbSWarner Losh sc->sc_cur_regwin = rw; 75112e36acbSWarner Losh return 0; 75212e36acbSWarner Losh } 75312e36acbSWarner Losh 75412e36acbSWarner Losh static int 75512e36acbSWarner Losh bwi_regwin_select(struct bwi_softc *sc, int id) 75612e36acbSWarner Losh { 75712e36acbSWarner Losh uint32_t win = BWI_PCIM_REGWIN(id); 75812e36acbSWarner Losh int i; 75912e36acbSWarner Losh 76012e36acbSWarner Losh #define RETRY_MAX 50 76112e36acbSWarner Losh for (i = 0; i < RETRY_MAX; ++i) { 76212e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_SEL_REGWIN, win, 4); 76312e36acbSWarner Losh if (pci_read_config(sc->sc_dev, BWI_PCIR_SEL_REGWIN, 4) == win) 76412e36acbSWarner Losh return 0; 76512e36acbSWarner Losh DELAY(10); 76612e36acbSWarner Losh } 76712e36acbSWarner Losh #undef RETRY_MAX 76812e36acbSWarner Losh 76912e36acbSWarner Losh return ENXIO; 77012e36acbSWarner Losh } 77112e36acbSWarner Losh 77212e36acbSWarner Losh static void 77312e36acbSWarner Losh bwi_regwin_info(struct bwi_softc *sc, uint16_t *type, uint8_t *rev) 77412e36acbSWarner Losh { 77512e36acbSWarner Losh uint32_t val; 77612e36acbSWarner Losh 77712e36acbSWarner Losh val = CSR_READ_4(sc, BWI_ID_HI); 77812e36acbSWarner Losh *type = BWI_ID_HI_REGWIN_TYPE(val); 77912e36acbSWarner Losh *rev = BWI_ID_HI_REGWIN_REV(val); 78012e36acbSWarner Losh 78112e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "regwin: type 0x%03x, rev %d, " 78212e36acbSWarner Losh "vendor 0x%04x\n", *type, *rev, 78312e36acbSWarner Losh __SHIFTOUT(val, BWI_ID_HI_REGWIN_VENDOR_MASK)); 78412e36acbSWarner Losh } 78512e36acbSWarner Losh 78612e36acbSWarner Losh static int 78712e36acbSWarner Losh bwi_bbp_attach(struct bwi_softc *sc) 78812e36acbSWarner Losh { 78912e36acbSWarner Losh #define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) 79012e36acbSWarner Losh uint16_t bbp_id, rw_type; 79112e36acbSWarner Losh uint8_t rw_rev; 79212e36acbSWarner Losh uint32_t info; 79312e36acbSWarner Losh int error, nregwin, i; 79412e36acbSWarner Losh 79512e36acbSWarner Losh /* 79612e36acbSWarner Losh * Get 0th regwin information 79712e36acbSWarner Losh * NOTE: 0th regwin should exist 79812e36acbSWarner Losh */ 79912e36acbSWarner Losh error = bwi_regwin_select(sc, 0); 80012e36acbSWarner Losh if (error) { 80112e36acbSWarner Losh device_printf(sc->sc_dev, "can't select regwin 0\n"); 80212e36acbSWarner Losh return error; 80312e36acbSWarner Losh } 80412e36acbSWarner Losh bwi_regwin_info(sc, &rw_type, &rw_rev); 80512e36acbSWarner Losh 80612e36acbSWarner Losh /* 80712e36acbSWarner Losh * Find out BBP id 80812e36acbSWarner Losh */ 80912e36acbSWarner Losh bbp_id = 0; 81012e36acbSWarner Losh info = 0; 81112e36acbSWarner Losh if (rw_type == BWI_REGWIN_T_COM) { 81212e36acbSWarner Losh info = CSR_READ_4(sc, BWI_INFO); 81312e36acbSWarner Losh bbp_id = __SHIFTOUT(info, BWI_INFO_BBPID_MASK); 81412e36acbSWarner Losh 81512e36acbSWarner Losh BWI_CREATE_REGWIN(&sc->sc_com_regwin, 0, rw_type, rw_rev); 81612e36acbSWarner Losh 81712e36acbSWarner Losh sc->sc_cap = CSR_READ_4(sc, BWI_CAPABILITY); 81812e36acbSWarner Losh } else { 81912e36acbSWarner Losh for (i = 0; i < N(bwi_bbpid_map); ++i) { 82012e36acbSWarner Losh if (sc->sc_pci_did >= bwi_bbpid_map[i].did_min && 82112e36acbSWarner Losh sc->sc_pci_did <= bwi_bbpid_map[i].did_max) { 82212e36acbSWarner Losh bbp_id = bwi_bbpid_map[i].bbp_id; 82312e36acbSWarner Losh break; 82412e36acbSWarner Losh } 82512e36acbSWarner Losh } 82612e36acbSWarner Losh if (bbp_id == 0) { 82712e36acbSWarner Losh device_printf(sc->sc_dev, "no BBP id for device id " 82812e36acbSWarner Losh "0x%04x\n", sc->sc_pci_did); 82912e36acbSWarner Losh return ENXIO; 83012e36acbSWarner Losh } 83112e36acbSWarner Losh 83212e36acbSWarner Losh info = __SHIFTIN(sc->sc_pci_revid, BWI_INFO_BBPREV_MASK) | 83312e36acbSWarner Losh __SHIFTIN(0, BWI_INFO_BBPPKG_MASK); 83412e36acbSWarner Losh } 83512e36acbSWarner Losh 83612e36acbSWarner Losh /* 83712e36acbSWarner Losh * Find out number of regwins 83812e36acbSWarner Losh */ 83912e36acbSWarner Losh nregwin = 0; 84012e36acbSWarner Losh if (rw_type == BWI_REGWIN_T_COM && rw_rev >= 4) { 84112e36acbSWarner Losh nregwin = __SHIFTOUT(info, BWI_INFO_NREGWIN_MASK); 84212e36acbSWarner Losh } else { 84312e36acbSWarner Losh for (i = 0; i < N(bwi_regwin_count); ++i) { 84412e36acbSWarner Losh if (bwi_regwin_count[i].bbp_id == bbp_id) { 84512e36acbSWarner Losh nregwin = bwi_regwin_count[i].nregwin; 84612e36acbSWarner Losh break; 84712e36acbSWarner Losh } 84812e36acbSWarner Losh } 84912e36acbSWarner Losh if (nregwin == 0) { 85012e36acbSWarner Losh device_printf(sc->sc_dev, "no number of win for " 85112e36acbSWarner Losh "BBP id 0x%04x\n", bbp_id); 85212e36acbSWarner Losh return ENXIO; 85312e36acbSWarner Losh } 85412e36acbSWarner Losh } 85512e36acbSWarner Losh 85612e36acbSWarner Losh /* Record BBP id/rev for later using */ 85712e36acbSWarner Losh sc->sc_bbp_id = bbp_id; 85812e36acbSWarner Losh sc->sc_bbp_rev = __SHIFTOUT(info, BWI_INFO_BBPREV_MASK); 85912e36acbSWarner Losh sc->sc_bbp_pkg = __SHIFTOUT(info, BWI_INFO_BBPPKG_MASK); 86012e36acbSWarner Losh device_printf(sc->sc_dev, "BBP: id 0x%04x, rev 0x%x, pkg %d\n", 86112e36acbSWarner Losh sc->sc_bbp_id, sc->sc_bbp_rev, sc->sc_bbp_pkg); 86212e36acbSWarner Losh 86312e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "nregwin %d, cap 0x%08x\n", 86412e36acbSWarner Losh nregwin, sc->sc_cap); 86512e36acbSWarner Losh 86612e36acbSWarner Losh /* 86712e36acbSWarner Losh * Create rest of the regwins 86812e36acbSWarner Losh */ 86912e36acbSWarner Losh 87012e36acbSWarner Losh /* Don't re-create common regwin, if it is already created */ 87112e36acbSWarner Losh i = BWI_REGWIN_EXIST(&sc->sc_com_regwin) ? 1 : 0; 87212e36acbSWarner Losh 87312e36acbSWarner Losh for (; i < nregwin; ++i) { 87412e36acbSWarner Losh /* 87512e36acbSWarner Losh * Get regwin information 87612e36acbSWarner Losh */ 87712e36acbSWarner Losh error = bwi_regwin_select(sc, i); 87812e36acbSWarner Losh if (error) { 87912e36acbSWarner Losh device_printf(sc->sc_dev, 88012e36acbSWarner Losh "can't select regwin %d\n", i); 88112e36acbSWarner Losh return error; 88212e36acbSWarner Losh } 88312e36acbSWarner Losh bwi_regwin_info(sc, &rw_type, &rw_rev); 88412e36acbSWarner Losh 88512e36acbSWarner Losh /* 88612e36acbSWarner Losh * Try attach: 88712e36acbSWarner Losh * 1) Bus (PCI/PCIE) regwin 88812e36acbSWarner Losh * 2) MAC regwin 88912e36acbSWarner Losh * Ignore rest types of regwin 89012e36acbSWarner Losh */ 89112e36acbSWarner Losh if (rw_type == BWI_REGWIN_T_BUSPCI || 89212e36acbSWarner Losh rw_type == BWI_REGWIN_T_BUSPCIE) { 89312e36acbSWarner Losh if (BWI_REGWIN_EXIST(&sc->sc_bus_regwin)) { 89412e36acbSWarner Losh device_printf(sc->sc_dev, 89512e36acbSWarner Losh "bus regwin already exists\n"); 89612e36acbSWarner Losh } else { 89712e36acbSWarner Losh BWI_CREATE_REGWIN(&sc->sc_bus_regwin, i, 89812e36acbSWarner Losh rw_type, rw_rev); 89912e36acbSWarner Losh } 90012e36acbSWarner Losh } else if (rw_type == BWI_REGWIN_T_MAC) { 90112e36acbSWarner Losh /* XXX ignore return value */ 90212e36acbSWarner Losh bwi_mac_attach(sc, i, rw_rev); 90312e36acbSWarner Losh } 90412e36acbSWarner Losh } 90512e36acbSWarner Losh 90612e36acbSWarner Losh /* At least one MAC shold exist */ 90712e36acbSWarner Losh if (!BWI_REGWIN_EXIST(&sc->sc_mac[0].mac_regwin)) { 90812e36acbSWarner Losh device_printf(sc->sc_dev, "no MAC was found\n"); 90912e36acbSWarner Losh return ENXIO; 91012e36acbSWarner Losh } 91112e36acbSWarner Losh KASSERT(sc->sc_nmac > 0, ("no mac's")); 91212e36acbSWarner Losh 91312e36acbSWarner Losh /* Bus regwin must exist */ 91412e36acbSWarner Losh if (!BWI_REGWIN_EXIST(&sc->sc_bus_regwin)) { 91512e36acbSWarner Losh device_printf(sc->sc_dev, "no bus regwin was found\n"); 91612e36acbSWarner Losh return ENXIO; 91712e36acbSWarner Losh } 91812e36acbSWarner Losh 91912e36acbSWarner Losh /* Start with first MAC */ 92012e36acbSWarner Losh error = bwi_regwin_switch(sc, &sc->sc_mac[0].mac_regwin, NULL); 92112e36acbSWarner Losh if (error) 92212e36acbSWarner Losh return error; 92312e36acbSWarner Losh 92412e36acbSWarner Losh return 0; 92512e36acbSWarner Losh #undef N 92612e36acbSWarner Losh } 92712e36acbSWarner Losh 92812e36acbSWarner Losh int 92912e36acbSWarner Losh bwi_bus_init(struct bwi_softc *sc, struct bwi_mac *mac) 93012e36acbSWarner Losh { 93112e36acbSWarner Losh struct bwi_regwin *old, *bus; 93212e36acbSWarner Losh uint32_t val; 93312e36acbSWarner Losh int error; 93412e36acbSWarner Losh 93512e36acbSWarner Losh bus = &sc->sc_bus_regwin; 93612e36acbSWarner Losh KASSERT(sc->sc_cur_regwin == &mac->mac_regwin, ("not cur regwin")); 93712e36acbSWarner Losh 93812e36acbSWarner Losh /* 93912e36acbSWarner Losh * Tell bus to generate requested interrupts 94012e36acbSWarner Losh */ 94112e36acbSWarner Losh if (bus->rw_rev < 6 && bus->rw_type == BWI_REGWIN_T_BUSPCI) { 94212e36acbSWarner Losh /* 94312e36acbSWarner Losh * NOTE: Read BWI_FLAGS from MAC regwin 94412e36acbSWarner Losh */ 94512e36acbSWarner Losh val = CSR_READ_4(sc, BWI_FLAGS); 94612e36acbSWarner Losh 94712e36acbSWarner Losh error = bwi_regwin_switch(sc, bus, &old); 94812e36acbSWarner Losh if (error) 94912e36acbSWarner Losh return error; 95012e36acbSWarner Losh 95112e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_INTRVEC, (val & BWI_FLAGS_INTR_MASK)); 95212e36acbSWarner Losh } else { 95312e36acbSWarner Losh uint32_t mac_mask; 95412e36acbSWarner Losh 95512e36acbSWarner Losh mac_mask = 1 << mac->mac_id; 95612e36acbSWarner Losh 95712e36acbSWarner Losh error = bwi_regwin_switch(sc, bus, &old); 95812e36acbSWarner Losh if (error) 95912e36acbSWarner Losh return error; 96012e36acbSWarner Losh 96112e36acbSWarner Losh val = pci_read_config(sc->sc_dev, BWI_PCIR_INTCTL, 4); 96212e36acbSWarner Losh val |= mac_mask << 8; 96312e36acbSWarner Losh pci_write_config(sc->sc_dev, BWI_PCIR_INTCTL, val, 4); 96412e36acbSWarner Losh } 96512e36acbSWarner Losh 96612e36acbSWarner Losh if (sc->sc_flags & BWI_F_BUS_INITED) 96712e36acbSWarner Losh goto back; 96812e36acbSWarner Losh 96912e36acbSWarner Losh if (bus->rw_type == BWI_REGWIN_T_BUSPCI) { 97012e36acbSWarner Losh /* 97112e36acbSWarner Losh * Enable prefetch and burst 97212e36acbSWarner Losh */ 97312e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_BUS_CONFIG, 97412e36acbSWarner Losh BWI_BUS_CONFIG_PREFETCH | BWI_BUS_CONFIG_BURST); 97512e36acbSWarner Losh 97612e36acbSWarner Losh if (bus->rw_rev < 5) { 97712e36acbSWarner Losh struct bwi_regwin *com = &sc->sc_com_regwin; 97812e36acbSWarner Losh 97912e36acbSWarner Losh /* 98012e36acbSWarner Losh * Configure timeouts for bus operation 98112e36acbSWarner Losh */ 98212e36acbSWarner Losh 98312e36acbSWarner Losh /* 98412e36acbSWarner Losh * Set service timeout and request timeout 98512e36acbSWarner Losh */ 98612e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_CONF_LO, 98712e36acbSWarner Losh __SHIFTIN(BWI_CONF_LO_SERVTO, BWI_CONF_LO_SERVTO_MASK) | 98812e36acbSWarner Losh __SHIFTIN(BWI_CONF_LO_REQTO, BWI_CONF_LO_REQTO_MASK)); 98912e36acbSWarner Losh 99012e36acbSWarner Losh /* 99112e36acbSWarner Losh * If there is common regwin, we switch to that regwin 99212e36acbSWarner Losh * and switch back to bus regwin once we have done. 99312e36acbSWarner Losh */ 99412e36acbSWarner Losh if (BWI_REGWIN_EXIST(com)) { 99512e36acbSWarner Losh error = bwi_regwin_switch(sc, com, NULL); 99612e36acbSWarner Losh if (error) 99712e36acbSWarner Losh return error; 99812e36acbSWarner Losh } 99912e36acbSWarner Losh 100012e36acbSWarner Losh /* Let bus know what we have changed */ 100112e36acbSWarner Losh CSR_WRITE_4(sc, BWI_BUS_ADDR, BWI_BUS_ADDR_MAGIC); 100212e36acbSWarner Losh CSR_READ_4(sc, BWI_BUS_ADDR); /* Flush */ 100312e36acbSWarner Losh CSR_WRITE_4(sc, BWI_BUS_DATA, 0); 100412e36acbSWarner Losh CSR_READ_4(sc, BWI_BUS_DATA); /* Flush */ 100512e36acbSWarner Losh 100612e36acbSWarner Losh if (BWI_REGWIN_EXIST(com)) { 100712e36acbSWarner Losh error = bwi_regwin_switch(sc, bus, NULL); 100812e36acbSWarner Losh if (error) 100912e36acbSWarner Losh return error; 101012e36acbSWarner Losh } 101112e36acbSWarner Losh } else if (bus->rw_rev >= 11) { 101212e36acbSWarner Losh /* 101312e36acbSWarner Losh * Enable memory read multiple 101412e36acbSWarner Losh */ 101512e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_BUS_CONFIG, BWI_BUS_CONFIG_MRM); 101612e36acbSWarner Losh } 101712e36acbSWarner Losh } else { 101812e36acbSWarner Losh /* TODO:PCIE */ 101912e36acbSWarner Losh } 102012e36acbSWarner Losh 102112e36acbSWarner Losh sc->sc_flags |= BWI_F_BUS_INITED; 102212e36acbSWarner Losh back: 102312e36acbSWarner Losh return bwi_regwin_switch(sc, old, NULL); 102412e36acbSWarner Losh } 102512e36acbSWarner Losh 102612e36acbSWarner Losh static void 102712e36acbSWarner Losh bwi_get_card_flags(struct bwi_softc *sc) 102812e36acbSWarner Losh { 102912e36acbSWarner Losh #define PCI_VENDOR_APPLE 0x106b 103012e36acbSWarner Losh #define PCI_VENDOR_DELL 0x1028 103112e36acbSWarner Losh sc->sc_card_flags = bwi_read_sprom(sc, BWI_SPROM_CARD_FLAGS); 103212e36acbSWarner Losh if (sc->sc_card_flags == 0xffff) 103312e36acbSWarner Losh sc->sc_card_flags = 0; 103412e36acbSWarner Losh 103512e36acbSWarner Losh if (sc->sc_pci_subvid == PCI_VENDOR_DELL && 103612e36acbSWarner Losh sc->sc_bbp_id == BWI_BBPID_BCM4301 && 103712e36acbSWarner Losh sc->sc_pci_revid == 0x74) 103812e36acbSWarner Losh sc->sc_card_flags |= BWI_CARD_F_BT_COEXIST; 103912e36acbSWarner Losh 104012e36acbSWarner Losh if (sc->sc_pci_subvid == PCI_VENDOR_APPLE && 104112e36acbSWarner Losh sc->sc_pci_subdid == 0x4e && /* XXX */ 104212e36acbSWarner Losh sc->sc_pci_revid > 0x40) 104312e36acbSWarner Losh sc->sc_card_flags |= BWI_CARD_F_PA_GPIO9; 104412e36acbSWarner Losh 104512e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "card flags 0x%04x\n", sc->sc_card_flags); 104612e36acbSWarner Losh #undef PCI_VENDOR_DELL 104712e36acbSWarner Losh #undef PCI_VENDOR_APPLE 104812e36acbSWarner Losh } 104912e36acbSWarner Losh 105012e36acbSWarner Losh static void 105112e36acbSWarner Losh bwi_get_eaddr(struct bwi_softc *sc, uint16_t eaddr_ofs, uint8_t *eaddr) 105212e36acbSWarner Losh { 105312e36acbSWarner Losh int i; 105412e36acbSWarner Losh 105512e36acbSWarner Losh for (i = 0; i < 3; ++i) { 105612e36acbSWarner Losh *((uint16_t *)eaddr + i) = 105712e36acbSWarner Losh htobe16(bwi_read_sprom(sc, eaddr_ofs + 2 * i)); 105812e36acbSWarner Losh } 105912e36acbSWarner Losh } 106012e36acbSWarner Losh 106112e36acbSWarner Losh static void 106212e36acbSWarner Losh bwi_get_clock_freq(struct bwi_softc *sc, struct bwi_clock_freq *freq) 106312e36acbSWarner Losh { 106412e36acbSWarner Losh struct bwi_regwin *com; 106512e36acbSWarner Losh uint32_t val; 106612e36acbSWarner Losh u_int div; 106712e36acbSWarner Losh int src; 106812e36acbSWarner Losh 106912e36acbSWarner Losh bzero(freq, sizeof(*freq)); 107012e36acbSWarner Losh com = &sc->sc_com_regwin; 107112e36acbSWarner Losh 107212e36acbSWarner Losh KASSERT(BWI_REGWIN_EXIST(com), ("regwin does not exist")); 107312e36acbSWarner Losh KASSERT(sc->sc_cur_regwin == com, ("wrong regwin")); 107412e36acbSWarner Losh KASSERT(sc->sc_cap & BWI_CAP_CLKMODE, ("wrong clock mode")); 107512e36acbSWarner Losh 107612e36acbSWarner Losh /* 107712e36acbSWarner Losh * Calculate clock frequency 107812e36acbSWarner Losh */ 107912e36acbSWarner Losh src = -1; 108012e36acbSWarner Losh div = 0; 108112e36acbSWarner Losh if (com->rw_rev < 6) { 108212e36acbSWarner Losh val = pci_read_config(sc->sc_dev, BWI_PCIR_GPIO_OUT, 4); 108312e36acbSWarner Losh if (val & BWI_PCIM_GPIO_OUT_CLKSRC) { 108412e36acbSWarner Losh src = BWI_CLKSRC_PCI; 108512e36acbSWarner Losh div = 64; 108612e36acbSWarner Losh } else { 108712e36acbSWarner Losh src = BWI_CLKSRC_CS_OSC; 108812e36acbSWarner Losh div = 32; 108912e36acbSWarner Losh } 109012e36acbSWarner Losh } else if (com->rw_rev < 10) { 109112e36acbSWarner Losh val = CSR_READ_4(sc, BWI_CLOCK_CTRL); 109212e36acbSWarner Losh 109312e36acbSWarner Losh src = __SHIFTOUT(val, BWI_CLOCK_CTRL_CLKSRC); 109412e36acbSWarner Losh if (src == BWI_CLKSRC_LP_OSC) { 109512e36acbSWarner Losh div = 1; 109612e36acbSWarner Losh } else { 109712e36acbSWarner Losh div = (__SHIFTOUT(val, BWI_CLOCK_CTRL_FDIV) + 1) << 2; 109812e36acbSWarner Losh 109912e36acbSWarner Losh /* Unknown source */ 110012e36acbSWarner Losh if (src >= BWI_CLKSRC_MAX) 110112e36acbSWarner Losh src = BWI_CLKSRC_CS_OSC; 110212e36acbSWarner Losh } 110312e36acbSWarner Losh } else { 110412e36acbSWarner Losh val = CSR_READ_4(sc, BWI_CLOCK_INFO); 110512e36acbSWarner Losh 110612e36acbSWarner Losh src = BWI_CLKSRC_CS_OSC; 110712e36acbSWarner Losh div = (__SHIFTOUT(val, BWI_CLOCK_INFO_FDIV) + 1) << 2; 110812e36acbSWarner Losh } 110912e36acbSWarner Losh 111012e36acbSWarner Losh KASSERT(src >= 0 && src < BWI_CLKSRC_MAX, ("bad src %d", src)); 111112e36acbSWarner Losh KASSERT(div != 0, ("div zero")); 111212e36acbSWarner Losh 111312e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "clksrc %s\n", 111412e36acbSWarner Losh src == BWI_CLKSRC_PCI ? "PCI" : 111512e36acbSWarner Losh (src == BWI_CLKSRC_LP_OSC ? "LP_OSC" : "CS_OSC")); 111612e36acbSWarner Losh 111712e36acbSWarner Losh freq->clkfreq_min = bwi_clkfreq[src].freq_min / div; 111812e36acbSWarner Losh freq->clkfreq_max = bwi_clkfreq[src].freq_max / div; 111912e36acbSWarner Losh 112012e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "clkfreq min %u, max %u\n", 112112e36acbSWarner Losh freq->clkfreq_min, freq->clkfreq_max); 112212e36acbSWarner Losh } 112312e36acbSWarner Losh 112412e36acbSWarner Losh static int 112512e36acbSWarner Losh bwi_set_clock_mode(struct bwi_softc *sc, enum bwi_clock_mode clk_mode) 112612e36acbSWarner Losh { 112712e36acbSWarner Losh struct bwi_regwin *old, *com; 112812e36acbSWarner Losh uint32_t clk_ctrl, clk_src; 112912e36acbSWarner Losh int error, pwr_off = 0; 113012e36acbSWarner Losh 113112e36acbSWarner Losh com = &sc->sc_com_regwin; 113212e36acbSWarner Losh if (!BWI_REGWIN_EXIST(com)) 113312e36acbSWarner Losh return 0; 113412e36acbSWarner Losh 113512e36acbSWarner Losh if (com->rw_rev >= 10 || com->rw_rev < 6) 113612e36acbSWarner Losh return 0; 113712e36acbSWarner Losh 113812e36acbSWarner Losh /* 113912e36acbSWarner Losh * For common regwin whose rev is [6, 10), the chip 114012e36acbSWarner Losh * must be capable to change clock mode. 114112e36acbSWarner Losh */ 114212e36acbSWarner Losh if ((sc->sc_cap & BWI_CAP_CLKMODE) == 0) 114312e36acbSWarner Losh return 0; 114412e36acbSWarner Losh 114512e36acbSWarner Losh error = bwi_regwin_switch(sc, com, &old); 114612e36acbSWarner Losh if (error) 114712e36acbSWarner Losh return error; 114812e36acbSWarner Losh 114912e36acbSWarner Losh if (clk_mode == BWI_CLOCK_MODE_FAST) 115012e36acbSWarner Losh bwi_power_on(sc, 0); /* Don't turn on PLL */ 115112e36acbSWarner Losh 115212e36acbSWarner Losh clk_ctrl = CSR_READ_4(sc, BWI_CLOCK_CTRL); 115312e36acbSWarner Losh clk_src = __SHIFTOUT(clk_ctrl, BWI_CLOCK_CTRL_CLKSRC); 115412e36acbSWarner Losh 115512e36acbSWarner Losh switch (clk_mode) { 115612e36acbSWarner Losh case BWI_CLOCK_MODE_FAST: 115712e36acbSWarner Losh clk_ctrl &= ~BWI_CLOCK_CTRL_SLOW; 115812e36acbSWarner Losh clk_ctrl |= BWI_CLOCK_CTRL_IGNPLL; 115912e36acbSWarner Losh break; 116012e36acbSWarner Losh case BWI_CLOCK_MODE_SLOW: 116112e36acbSWarner Losh clk_ctrl |= BWI_CLOCK_CTRL_SLOW; 116212e36acbSWarner Losh break; 116312e36acbSWarner Losh case BWI_CLOCK_MODE_DYN: 116412e36acbSWarner Losh clk_ctrl &= ~(BWI_CLOCK_CTRL_SLOW | 116512e36acbSWarner Losh BWI_CLOCK_CTRL_IGNPLL | 116612e36acbSWarner Losh BWI_CLOCK_CTRL_NODYN); 116712e36acbSWarner Losh if (clk_src != BWI_CLKSRC_CS_OSC) { 116812e36acbSWarner Losh clk_ctrl |= BWI_CLOCK_CTRL_NODYN; 116912e36acbSWarner Losh pwr_off = 1; 117012e36acbSWarner Losh } 117112e36acbSWarner Losh break; 117212e36acbSWarner Losh } 117312e36acbSWarner Losh CSR_WRITE_4(sc, BWI_CLOCK_CTRL, clk_ctrl); 117412e36acbSWarner Losh 117512e36acbSWarner Losh if (pwr_off) 117612e36acbSWarner Losh bwi_power_off(sc, 0); /* Leave PLL as it is */ 117712e36acbSWarner Losh 117812e36acbSWarner Losh return bwi_regwin_switch(sc, old, NULL); 117912e36acbSWarner Losh } 118012e36acbSWarner Losh 118112e36acbSWarner Losh static int 118212e36acbSWarner Losh bwi_set_clock_delay(struct bwi_softc *sc) 118312e36acbSWarner Losh { 118412e36acbSWarner Losh struct bwi_regwin *old, *com; 118512e36acbSWarner Losh int error; 118612e36acbSWarner Losh 118712e36acbSWarner Losh com = &sc->sc_com_regwin; 118812e36acbSWarner Losh if (!BWI_REGWIN_EXIST(com)) 118912e36acbSWarner Losh return 0; 119012e36acbSWarner Losh 119112e36acbSWarner Losh error = bwi_regwin_switch(sc, com, &old); 119212e36acbSWarner Losh if (error) 119312e36acbSWarner Losh return error; 119412e36acbSWarner Losh 119512e36acbSWarner Losh if (sc->sc_bbp_id == BWI_BBPID_BCM4321) { 119612e36acbSWarner Losh if (sc->sc_bbp_rev == 0) 119712e36acbSWarner Losh CSR_WRITE_4(sc, BWI_CONTROL, BWI_CONTROL_MAGIC0); 119812e36acbSWarner Losh else if (sc->sc_bbp_rev == 1) 119912e36acbSWarner Losh CSR_WRITE_4(sc, BWI_CONTROL, BWI_CONTROL_MAGIC1); 120012e36acbSWarner Losh } 120112e36acbSWarner Losh 120212e36acbSWarner Losh if (sc->sc_cap & BWI_CAP_CLKMODE) { 120312e36acbSWarner Losh if (com->rw_rev >= 10) { 120412e36acbSWarner Losh CSR_FILT_SETBITS_4(sc, BWI_CLOCK_INFO, 0xffff, 0x40000); 120512e36acbSWarner Losh } else { 120612e36acbSWarner Losh struct bwi_clock_freq freq; 120712e36acbSWarner Losh 120812e36acbSWarner Losh bwi_get_clock_freq(sc, &freq); 120912e36acbSWarner Losh CSR_WRITE_4(sc, BWI_PLL_ON_DELAY, 121012e36acbSWarner Losh howmany(freq.clkfreq_max * 150, 1000000)); 121112e36acbSWarner Losh CSR_WRITE_4(sc, BWI_FREQ_SEL_DELAY, 121212e36acbSWarner Losh howmany(freq.clkfreq_max * 15, 1000000)); 121312e36acbSWarner Losh } 121412e36acbSWarner Losh } 121512e36acbSWarner Losh 121612e36acbSWarner Losh return bwi_regwin_switch(sc, old, NULL); 121712e36acbSWarner Losh } 121812e36acbSWarner Losh 121912e36acbSWarner Losh static void 122012e36acbSWarner Losh bwi_init(void *xsc) 122112e36acbSWarner Losh { 122212e36acbSWarner Losh struct bwi_softc *sc = xsc; 122312e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 122412e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 122512e36acbSWarner Losh 122612e36acbSWarner Losh BWI_LOCK(sc); 122712e36acbSWarner Losh bwi_init_statechg(sc, 1); 122812e36acbSWarner Losh BWI_UNLOCK(sc); 122912e36acbSWarner Losh 123012e36acbSWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) 123112e36acbSWarner Losh ieee80211_start_all(ic); /* start all vap's */ 123212e36acbSWarner Losh } 123312e36acbSWarner Losh 123412e36acbSWarner Losh static void 123512e36acbSWarner Losh bwi_init_statechg(struct bwi_softc *sc, int statechg) 123612e36acbSWarner Losh { 123712e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 123812e36acbSWarner Losh struct bwi_mac *mac; 123912e36acbSWarner Losh int error; 124012e36acbSWarner Losh 124112e36acbSWarner Losh bwi_stop_locked(sc, statechg); 124212e36acbSWarner Losh 124312e36acbSWarner Losh bwi_bbp_power_on(sc, BWI_CLOCK_MODE_FAST); 124412e36acbSWarner Losh 124512e36acbSWarner Losh /* TODO: 2 MAC */ 124612e36acbSWarner Losh 124712e36acbSWarner Losh mac = &sc->sc_mac[0]; 124812e36acbSWarner Losh error = bwi_regwin_switch(sc, &mac->mac_regwin, NULL); 124912e36acbSWarner Losh if (error) { 125012e36acbSWarner Losh if_printf(ifp, "%s: error %d on regwin switch\n", 125112e36acbSWarner Losh __func__, error); 125212e36acbSWarner Losh goto bad; 125312e36acbSWarner Losh } 125412e36acbSWarner Losh error = bwi_mac_init(mac); 125512e36acbSWarner Losh if (error) { 125612e36acbSWarner Losh if_printf(ifp, "%s: error %d on MAC init\n", __func__, error); 125712e36acbSWarner Losh goto bad; 125812e36acbSWarner Losh } 125912e36acbSWarner Losh 126012e36acbSWarner Losh bwi_bbp_power_on(sc, BWI_CLOCK_MODE_DYN); 126112e36acbSWarner Losh 126212e36acbSWarner Losh bwi_set_bssid(sc, bwi_zero_addr); /* Clear BSSID */ 126312e36acbSWarner Losh bwi_set_addr_filter(sc, BWI_ADDR_FILTER_MYADDR, IF_LLADDR(ifp)); 126412e36acbSWarner Losh 126512e36acbSWarner Losh bwi_mac_reset_hwkeys(mac); 126612e36acbSWarner Losh 126712e36acbSWarner Losh if ((mac->mac_flags & BWI_MAC_F_HAS_TXSTATS) == 0) { 126812e36acbSWarner Losh int i; 126912e36acbSWarner Losh 127012e36acbSWarner Losh #define NRETRY 1000 127112e36acbSWarner Losh /* 127212e36acbSWarner Losh * Drain any possible pending TX status 127312e36acbSWarner Losh */ 127412e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 127512e36acbSWarner Losh if ((CSR_READ_4(sc, BWI_TXSTATUS0) & 127612e36acbSWarner Losh BWI_TXSTATUS0_VALID) == 0) 127712e36acbSWarner Losh break; 127812e36acbSWarner Losh CSR_READ_4(sc, BWI_TXSTATUS1); 127912e36acbSWarner Losh } 128012e36acbSWarner Losh if (i == NRETRY) 128112e36acbSWarner Losh if_printf(ifp, "%s: can't drain TX status\n", __func__); 128212e36acbSWarner Losh #undef NRETRY 128312e36acbSWarner Losh } 128412e36acbSWarner Losh 128512e36acbSWarner Losh if (mac->mac_phy.phy_mode == IEEE80211_MODE_11G) 128612e36acbSWarner Losh bwi_mac_updateslot(mac, 1); 128712e36acbSWarner Losh 128812e36acbSWarner Losh /* Start MAC */ 128912e36acbSWarner Losh error = bwi_mac_start(mac); 129012e36acbSWarner Losh if (error) { 129112e36acbSWarner Losh if_printf(ifp, "%s: error %d starting MAC\n", __func__, error); 129212e36acbSWarner Losh goto bad; 129312e36acbSWarner Losh } 129412e36acbSWarner Losh 129512e36acbSWarner Losh /* Clear stop flag before enabling interrupt */ 129612e36acbSWarner Losh sc->sc_flags &= ~BWI_F_STOP; 129712e36acbSWarner Losh 129812e36acbSWarner Losh ifp->if_drv_flags |= IFF_DRV_RUNNING; 129958fbe5abSJohn Baldwin callout_reset(&sc->sc_watchdog_timer, hz, bwi_watchdog, sc); 130012e36acbSWarner Losh 130112e36acbSWarner Losh /* Enable intrs */ 130212e36acbSWarner Losh bwi_enable_intrs(sc, BWI_INIT_INTRS); 130312e36acbSWarner Losh return; 130412e36acbSWarner Losh bad: 130512e36acbSWarner Losh bwi_stop_locked(sc, 1); 130612e36acbSWarner Losh } 130712e36acbSWarner Losh 130812e36acbSWarner Losh static int 130912e36acbSWarner Losh bwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 131012e36acbSWarner Losh { 131112e36acbSWarner Losh #define IS_RUNNING(ifp) \ 131212e36acbSWarner Losh ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) 131312e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 131412e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 131512e36acbSWarner Losh struct ifreq *ifr = (struct ifreq *) data; 131612e36acbSWarner Losh int error = 0, startall = 0; 131712e36acbSWarner Losh 131812e36acbSWarner Losh switch (cmd) { 131912e36acbSWarner Losh case SIOCSIFFLAGS: 132012e36acbSWarner Losh BWI_LOCK(sc); 132112e36acbSWarner Losh if (IS_RUNNING(ifp)) { 132212e36acbSWarner Losh struct bwi_mac *mac; 132312e36acbSWarner Losh int promisc = -1; 132412e36acbSWarner Losh 132512e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 132612e36acbSWarner Losh ("current regwin type %d", 132712e36acbSWarner Losh sc->sc_cur_regwin->rw_type)); 132812e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 132912e36acbSWarner Losh 133012e36acbSWarner Losh if ((ifp->if_flags & IFF_PROMISC) && 133112e36acbSWarner Losh (sc->sc_flags & BWI_F_PROMISC) == 0) { 133212e36acbSWarner Losh promisc = 1; 133312e36acbSWarner Losh sc->sc_flags |= BWI_F_PROMISC; 133412e36acbSWarner Losh } else if ((ifp->if_flags & IFF_PROMISC) == 0 && 133512e36acbSWarner Losh (sc->sc_flags & BWI_F_PROMISC)) { 133612e36acbSWarner Losh promisc = 0; 133712e36acbSWarner Losh sc->sc_flags &= ~BWI_F_PROMISC; 133812e36acbSWarner Losh } 133912e36acbSWarner Losh 134012e36acbSWarner Losh if (promisc >= 0) 134112e36acbSWarner Losh bwi_mac_set_promisc(mac, promisc); 134212e36acbSWarner Losh } 134312e36acbSWarner Losh 134412e36acbSWarner Losh if (ifp->if_flags & IFF_UP) { 134512e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 134612e36acbSWarner Losh bwi_init_statechg(sc, 1); 134712e36acbSWarner Losh startall = 1; 134812e36acbSWarner Losh } 134912e36acbSWarner Losh } else { 135012e36acbSWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) 135112e36acbSWarner Losh bwi_stop_locked(sc, 1); 135212e36acbSWarner Losh } 135312e36acbSWarner Losh BWI_UNLOCK(sc); 135412e36acbSWarner Losh if (startall) 135512e36acbSWarner Losh ieee80211_start_all(ic); 135612e36acbSWarner Losh break; 135712e36acbSWarner Losh case SIOCGIFMEDIA: 135812e36acbSWarner Losh error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 135912e36acbSWarner Losh break; 136012e36acbSWarner Losh case SIOCGIFADDR: 136112e36acbSWarner Losh error = ether_ioctl(ifp, cmd, data); 136212e36acbSWarner Losh break; 136312e36acbSWarner Losh default: 136412e36acbSWarner Losh error = EINVAL; 136512e36acbSWarner Losh break; 136612e36acbSWarner Losh } 136712e36acbSWarner Losh return error; 136812e36acbSWarner Losh #undef IS_RUNNING 136912e36acbSWarner Losh } 137012e36acbSWarner Losh 137112e36acbSWarner Losh static void 137212e36acbSWarner Losh bwi_start(struct ifnet *ifp) 137312e36acbSWarner Losh { 137412e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 137512e36acbSWarner Losh 137612e36acbSWarner Losh BWI_LOCK(sc); 137712e36acbSWarner Losh bwi_start_locked(ifp); 137812e36acbSWarner Losh BWI_UNLOCK(sc); 137912e36acbSWarner Losh } 138012e36acbSWarner Losh 138112e36acbSWarner Losh static void 138212e36acbSWarner Losh bwi_start_locked(struct ifnet *ifp) 138312e36acbSWarner Losh { 138412e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 138512e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; 138612e36acbSWarner Losh struct ieee80211_frame *wh; 138712e36acbSWarner Losh struct ieee80211_node *ni; 138812e36acbSWarner Losh struct ieee80211_key *k; 138912e36acbSWarner Losh struct mbuf *m; 139012e36acbSWarner Losh int trans, idx; 139112e36acbSWarner Losh 139212e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 139312e36acbSWarner Losh return; 139412e36acbSWarner Losh 139512e36acbSWarner Losh trans = 0; 139612e36acbSWarner Losh idx = tbd->tbd_idx; 139712e36acbSWarner Losh 139812e36acbSWarner Losh while (tbd->tbd_buf[idx].tb_mbuf == NULL) { 139912e36acbSWarner Losh IFQ_DRV_DEQUEUE(&ifp->if_snd, m); /* XXX: LOCK */ 140012e36acbSWarner Losh if (m == NULL) 140112e36acbSWarner Losh break; 140212e36acbSWarner Losh 140312e36acbSWarner Losh ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; 140412e36acbSWarner Losh wh = mtod(m, struct ieee80211_frame *); 14055945b5f5SKevin Lo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 140612e36acbSWarner Losh k = ieee80211_crypto_encap(ni, m); 140712e36acbSWarner Losh if (k == NULL) { 140812e36acbSWarner Losh ieee80211_free_node(ni); 140912e36acbSWarner Losh m_freem(m); 1410c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 141112e36acbSWarner Losh continue; 141212e36acbSWarner Losh } 141312e36acbSWarner Losh } 141412e36acbSWarner Losh wh = NULL; /* Catch any invalid use */ 141512e36acbSWarner Losh 141612e36acbSWarner Losh if (bwi_encap(sc, idx, m, ni) != 0) { 141712e36acbSWarner Losh /* 'm' is freed in bwi_encap() if we reach here */ 141812e36acbSWarner Losh if (ni != NULL) 141912e36acbSWarner Losh ieee80211_free_node(ni); 1420c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 142112e36acbSWarner Losh continue; 142212e36acbSWarner Losh } 142312e36acbSWarner Losh 142412e36acbSWarner Losh trans = 1; 142512e36acbSWarner Losh tbd->tbd_used++; 142612e36acbSWarner Losh idx = (idx + 1) % BWI_TX_NDESC; 142712e36acbSWarner Losh 1428c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 142912e36acbSWarner Losh 143012e36acbSWarner Losh if (tbd->tbd_used + BWI_TX_NSPRDESC >= BWI_TX_NDESC) { 143112e36acbSWarner Losh ifp->if_drv_flags |= IFF_DRV_OACTIVE; 143212e36acbSWarner Losh break; 143312e36acbSWarner Losh } 143412e36acbSWarner Losh } 143512e36acbSWarner Losh tbd->tbd_idx = idx; 143612e36acbSWarner Losh 143712e36acbSWarner Losh if (trans) 143858fbe5abSJohn Baldwin sc->sc_tx_timer = 5; 143912e36acbSWarner Losh } 144012e36acbSWarner Losh 144112e36acbSWarner Losh static int 144212e36acbSWarner Losh bwi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 144312e36acbSWarner Losh const struct ieee80211_bpf_params *params) 144412e36acbSWarner Losh { 144512e36acbSWarner Losh struct ieee80211com *ic = ni->ni_ic; 144612e36acbSWarner Losh struct ifnet *ifp = ic->ic_ifp; 144712e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 144812e36acbSWarner Losh /* XXX wme? */ 144912e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; 145012e36acbSWarner Losh int idx, error; 145112e36acbSWarner Losh 145212e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 145312e36acbSWarner Losh ieee80211_free_node(ni); 145412e36acbSWarner Losh m_freem(m); 145512e36acbSWarner Losh return ENETDOWN; 145612e36acbSWarner Losh } 145712e36acbSWarner Losh 145812e36acbSWarner Losh BWI_LOCK(sc); 145912e36acbSWarner Losh idx = tbd->tbd_idx; 146012e36acbSWarner Losh KASSERT(tbd->tbd_buf[idx].tb_mbuf == NULL, ("slot %d not empty", idx)); 146112e36acbSWarner Losh if (params == NULL) { 146212e36acbSWarner Losh /* 146312e36acbSWarner Losh * Legacy path; interpret frame contents to decide 146412e36acbSWarner Losh * precisely how to send the frame. 146512e36acbSWarner Losh */ 146612e36acbSWarner Losh error = bwi_encap(sc, idx, m, ni); 146712e36acbSWarner Losh } else { 146812e36acbSWarner Losh /* 146912e36acbSWarner Losh * Caller supplied explicit parameters to use in 147012e36acbSWarner Losh * sending the frame. 147112e36acbSWarner Losh */ 147212e36acbSWarner Losh error = bwi_encap_raw(sc, idx, m, ni, params); 147312e36acbSWarner Losh } 147412e36acbSWarner Losh if (error == 0) { 1475c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 147612e36acbSWarner Losh if (++tbd->tbd_used + BWI_TX_NSPRDESC >= BWI_TX_NDESC) 147712e36acbSWarner Losh ifp->if_drv_flags |= IFF_DRV_OACTIVE; 147812e36acbSWarner Losh tbd->tbd_idx = (idx + 1) % BWI_TX_NDESC; 147958fbe5abSJohn Baldwin sc->sc_tx_timer = 5; 148012e36acbSWarner Losh } else { 148112e36acbSWarner Losh /* NB: m is reclaimed on encap failure */ 148212e36acbSWarner Losh ieee80211_free_node(ni); 1483c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 148412e36acbSWarner Losh } 148512e36acbSWarner Losh BWI_UNLOCK(sc); 148612e36acbSWarner Losh return error; 148712e36acbSWarner Losh } 148812e36acbSWarner Losh 148912e36acbSWarner Losh static void 149058fbe5abSJohn Baldwin bwi_watchdog(void *arg) 149112e36acbSWarner Losh { 149258fbe5abSJohn Baldwin struct bwi_softc *sc; 149358fbe5abSJohn Baldwin struct ifnet *ifp; 149412e36acbSWarner Losh 149558fbe5abSJohn Baldwin sc = arg; 149658fbe5abSJohn Baldwin ifp = sc->sc_ifp; 149758fbe5abSJohn Baldwin BWI_ASSERT_LOCKED(sc); 149858fbe5abSJohn Baldwin if (sc->sc_tx_timer != 0 && --sc->sc_tx_timer == 0) { 149912e36acbSWarner Losh if_printf(ifp, "watchdog timeout\n"); 1500c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 150112e36acbSWarner Losh taskqueue_enqueue(sc->sc_tq, &sc->sc_restart_task); 150212e36acbSWarner Losh } 150358fbe5abSJohn Baldwin callout_reset(&sc->sc_watchdog_timer, hz, bwi_watchdog, sc); 150412e36acbSWarner Losh } 150512e36acbSWarner Losh 150612e36acbSWarner Losh static void 150712e36acbSWarner Losh bwi_stop(struct bwi_softc *sc, int statechg) 150812e36acbSWarner Losh { 150912e36acbSWarner Losh BWI_LOCK(sc); 151012e36acbSWarner Losh bwi_stop_locked(sc, statechg); 151112e36acbSWarner Losh BWI_UNLOCK(sc); 151212e36acbSWarner Losh } 151312e36acbSWarner Losh 151412e36acbSWarner Losh static void 151512e36acbSWarner Losh bwi_stop_locked(struct bwi_softc *sc, int statechg) 151612e36acbSWarner Losh { 151712e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 151812e36acbSWarner Losh struct bwi_mac *mac; 151912e36acbSWarner Losh int i, error, pwr_off = 0; 152012e36acbSWarner Losh 152112e36acbSWarner Losh BWI_ASSERT_LOCKED(sc); 152212e36acbSWarner Losh 152312e36acbSWarner Losh callout_stop(&sc->sc_calib_ch); 152412e36acbSWarner Losh callout_stop(&sc->sc_led_blink_ch); 152512e36acbSWarner Losh sc->sc_led_blinking = 0; 152612e36acbSWarner Losh sc->sc_flags |= BWI_F_STOP; 152712e36acbSWarner Losh 152812e36acbSWarner Losh if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 152912e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 153012e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 153112e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 153212e36acbSWarner Losh 153312e36acbSWarner Losh bwi_disable_intrs(sc, BWI_ALL_INTRS); 153412e36acbSWarner Losh CSR_READ_4(sc, BWI_MAC_INTR_MASK); 153512e36acbSWarner Losh bwi_mac_stop(mac); 153612e36acbSWarner Losh } 153712e36acbSWarner Losh 153812e36acbSWarner Losh for (i = 0; i < sc->sc_nmac; ++i) { 153912e36acbSWarner Losh struct bwi_regwin *old_rw; 154012e36acbSWarner Losh 154112e36acbSWarner Losh mac = &sc->sc_mac[i]; 154212e36acbSWarner Losh if ((mac->mac_flags & BWI_MAC_F_INITED) == 0) 154312e36acbSWarner Losh continue; 154412e36acbSWarner Losh 154512e36acbSWarner Losh error = bwi_regwin_switch(sc, &mac->mac_regwin, &old_rw); 154612e36acbSWarner Losh if (error) 154712e36acbSWarner Losh continue; 154812e36acbSWarner Losh 154912e36acbSWarner Losh bwi_mac_shutdown(mac); 155012e36acbSWarner Losh pwr_off = 1; 155112e36acbSWarner Losh 155212e36acbSWarner Losh bwi_regwin_switch(sc, old_rw, NULL); 155312e36acbSWarner Losh } 155412e36acbSWarner Losh 155512e36acbSWarner Losh if (pwr_off) 155612e36acbSWarner Losh bwi_bbp_power_off(sc); 155712e36acbSWarner Losh 155812e36acbSWarner Losh sc->sc_tx_timer = 0; 155958fbe5abSJohn Baldwin callout_stop(&sc->sc_watchdog_timer); 156012e36acbSWarner Losh ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 156112e36acbSWarner Losh } 156212e36acbSWarner Losh 156312e36acbSWarner Losh void 156412e36acbSWarner Losh bwi_intr(void *xsc) 156512e36acbSWarner Losh { 156612e36acbSWarner Losh struct bwi_softc *sc = xsc; 156712e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 156812e36acbSWarner Losh struct bwi_mac *mac; 156912e36acbSWarner Losh uint32_t intr_status; 157012e36acbSWarner Losh uint32_t txrx_intr_status[BWI_TXRX_NRING]; 157112e36acbSWarner Losh int i, txrx_error, tx = 0, rx_data = -1; 157212e36acbSWarner Losh 157312e36acbSWarner Losh BWI_LOCK(sc); 157412e36acbSWarner Losh 157512e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 157612e36acbSWarner Losh (sc->sc_flags & BWI_F_STOP)) { 157712e36acbSWarner Losh BWI_UNLOCK(sc); 157812e36acbSWarner Losh return; 157912e36acbSWarner Losh } 158012e36acbSWarner Losh /* 158112e36acbSWarner Losh * Get interrupt status 158212e36acbSWarner Losh */ 158312e36acbSWarner Losh intr_status = CSR_READ_4(sc, BWI_MAC_INTR_STATUS); 158412e36acbSWarner Losh if (intr_status == 0xffffffff) { /* Not for us */ 158512e36acbSWarner Losh BWI_UNLOCK(sc); 158612e36acbSWarner Losh return; 158712e36acbSWarner Losh } 158812e36acbSWarner Losh 158912e36acbSWarner Losh DPRINTF(sc, BWI_DBG_INTR, "intr status 0x%08x\n", intr_status); 159012e36acbSWarner Losh 159112e36acbSWarner Losh intr_status &= CSR_READ_4(sc, BWI_MAC_INTR_MASK); 159212e36acbSWarner Losh if (intr_status == 0) { /* Nothing is interesting */ 159312e36acbSWarner Losh BWI_UNLOCK(sc); 159412e36acbSWarner Losh return; 159512e36acbSWarner Losh } 159612e36acbSWarner Losh 159712e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 159812e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 159912e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 160012e36acbSWarner Losh 160112e36acbSWarner Losh txrx_error = 0; 160212e36acbSWarner Losh DPRINTF(sc, BWI_DBG_INTR, "%s\n", "TX/RX intr"); 160312e36acbSWarner Losh for (i = 0; i < BWI_TXRX_NRING; ++i) { 160412e36acbSWarner Losh uint32_t mask; 160512e36acbSWarner Losh 160612e36acbSWarner Losh if (BWI_TXRX_IS_RX(i)) 160712e36acbSWarner Losh mask = BWI_TXRX_RX_INTRS; 160812e36acbSWarner Losh else 160912e36acbSWarner Losh mask = BWI_TXRX_TX_INTRS; 161012e36acbSWarner Losh 161112e36acbSWarner Losh txrx_intr_status[i] = 161212e36acbSWarner Losh CSR_READ_4(sc, BWI_TXRX_INTR_STATUS(i)) & mask; 161312e36acbSWarner Losh 161412e36acbSWarner Losh _DPRINTF(sc, BWI_DBG_INTR, ", %d 0x%08x", 161512e36acbSWarner Losh i, txrx_intr_status[i]); 161612e36acbSWarner Losh 161712e36acbSWarner Losh if (txrx_intr_status[i] & BWI_TXRX_INTR_ERROR) { 161812e36acbSWarner Losh if_printf(ifp, 161912e36acbSWarner Losh "%s: intr fatal TX/RX (%d) error 0x%08x\n", 162012e36acbSWarner Losh __func__, i, txrx_intr_status[i]); 162112e36acbSWarner Losh txrx_error = 1; 162212e36acbSWarner Losh } 162312e36acbSWarner Losh } 162412e36acbSWarner Losh _DPRINTF(sc, BWI_DBG_INTR, "%s\n", ""); 162512e36acbSWarner Losh 162612e36acbSWarner Losh /* 162712e36acbSWarner Losh * Acknowledge interrupt 162812e36acbSWarner Losh */ 162912e36acbSWarner Losh CSR_WRITE_4(sc, BWI_MAC_INTR_STATUS, intr_status); 163012e36acbSWarner Losh 163112e36acbSWarner Losh for (i = 0; i < BWI_TXRX_NRING; ++i) 163212e36acbSWarner Losh CSR_WRITE_4(sc, BWI_TXRX_INTR_STATUS(i), txrx_intr_status[i]); 163312e36acbSWarner Losh 163412e36acbSWarner Losh /* Disable all interrupts */ 163512e36acbSWarner Losh bwi_disable_intrs(sc, BWI_ALL_INTRS); 163612e36acbSWarner Losh 16371771d207SWarner Losh /* 16381771d207SWarner Losh * http://bcm-specs.sipsolutions.net/Interrupts 16391771d207SWarner Losh * Says for this bit (0x800): 16401771d207SWarner Losh * "Fatal Error 16411771d207SWarner Losh * 16421771d207SWarner Losh * We got this one while testing things when by accident the 16431771d207SWarner Losh * template ram wasn't set to big endian when it should have 16441771d207SWarner Losh * been after writing the initial values. It keeps on being 16451771d207SWarner Losh * triggered, the only way to stop it seems to shut down the 16461771d207SWarner Losh * chip." 16471771d207SWarner Losh * 16481771d207SWarner Losh * Suggesting that we should never get it and if we do we're not 16491771d207SWarner Losh * feeding TX packets into the MAC correctly if we do... Apparently, 16501771d207SWarner Losh * it is valid only on mac version 5 and higher, but I couldn't 16511771d207SWarner Losh * find a reference for that... Since I see them from time to time 16521771d207SWarner Losh * on my card, this suggests an error in the tx path still... 16531771d207SWarner Losh */ 165412e36acbSWarner Losh if (intr_status & BWI_INTR_PHY_TXERR) { 165512e36acbSWarner Losh if (mac->mac_flags & BWI_MAC_F_PHYE_RESET) { 165612e36acbSWarner Losh if_printf(ifp, "%s: intr PHY TX error\n", __func__); 165712e36acbSWarner Losh taskqueue_enqueue(sc->sc_tq, &sc->sc_restart_task); 165812e36acbSWarner Losh BWI_UNLOCK(sc); 165912e36acbSWarner Losh return; 166012e36acbSWarner Losh } 166112e36acbSWarner Losh } 166212e36acbSWarner Losh 166312e36acbSWarner Losh if (txrx_error) { 166412e36acbSWarner Losh /* TODO: reset device */ 166512e36acbSWarner Losh } 166612e36acbSWarner Losh 166712e36acbSWarner Losh if (intr_status & BWI_INTR_TBTT) 166812e36acbSWarner Losh bwi_mac_config_ps(mac); 166912e36acbSWarner Losh 167012e36acbSWarner Losh if (intr_status & BWI_INTR_EO_ATIM) 167112e36acbSWarner Losh if_printf(ifp, "EO_ATIM\n"); 167212e36acbSWarner Losh 167312e36acbSWarner Losh if (intr_status & BWI_INTR_PMQ) { 167412e36acbSWarner Losh for (;;) { 167512e36acbSWarner Losh if ((CSR_READ_4(sc, BWI_MAC_PS_STATUS) & 0x8) == 0) 167612e36acbSWarner Losh break; 167712e36acbSWarner Losh } 167812e36acbSWarner Losh CSR_WRITE_2(sc, BWI_MAC_PS_STATUS, 0x2); 167912e36acbSWarner Losh } 168012e36acbSWarner Losh 168112e36acbSWarner Losh if (intr_status & BWI_INTR_NOISE) 168212e36acbSWarner Losh if_printf(ifp, "intr noise\n"); 168312e36acbSWarner Losh 168412e36acbSWarner Losh if (txrx_intr_status[0] & BWI_TXRX_INTR_RX) { 168512e36acbSWarner Losh rx_data = sc->sc_rxeof(sc); 168612e36acbSWarner Losh if (sc->sc_flags & BWI_F_STOP) { 168712e36acbSWarner Losh BWI_UNLOCK(sc); 168812e36acbSWarner Losh return; 168912e36acbSWarner Losh } 169012e36acbSWarner Losh } 169112e36acbSWarner Losh 169212e36acbSWarner Losh if (txrx_intr_status[3] & BWI_TXRX_INTR_RX) { 169312e36acbSWarner Losh sc->sc_txeof_status(sc); 169412e36acbSWarner Losh tx = 1; 169512e36acbSWarner Losh } 169612e36acbSWarner Losh 169712e36acbSWarner Losh if (intr_status & BWI_INTR_TX_DONE) { 169812e36acbSWarner Losh bwi_txeof(sc); 169912e36acbSWarner Losh tx = 1; 170012e36acbSWarner Losh } 170112e36acbSWarner Losh 170212e36acbSWarner Losh /* Re-enable interrupts */ 170312e36acbSWarner Losh bwi_enable_intrs(sc, BWI_INIT_INTRS); 170412e36acbSWarner Losh 170512e36acbSWarner Losh if (sc->sc_blink_led != NULL && sc->sc_led_blink) { 170612e36acbSWarner Losh int evt = BWI_LED_EVENT_NONE; 170712e36acbSWarner Losh 170812e36acbSWarner Losh if (tx && rx_data > 0) { 170912e36acbSWarner Losh if (sc->sc_rx_rate > sc->sc_tx_rate) 171012e36acbSWarner Losh evt = BWI_LED_EVENT_RX; 171112e36acbSWarner Losh else 171212e36acbSWarner Losh evt = BWI_LED_EVENT_TX; 171312e36acbSWarner Losh } else if (tx) { 171412e36acbSWarner Losh evt = BWI_LED_EVENT_TX; 171512e36acbSWarner Losh } else if (rx_data > 0) { 171612e36acbSWarner Losh evt = BWI_LED_EVENT_RX; 171712e36acbSWarner Losh } else if (rx_data == 0) { 171812e36acbSWarner Losh evt = BWI_LED_EVENT_POLL; 171912e36acbSWarner Losh } 172012e36acbSWarner Losh 172112e36acbSWarner Losh if (evt != BWI_LED_EVENT_NONE) 172212e36acbSWarner Losh bwi_led_event(sc, evt); 172312e36acbSWarner Losh } 172412e36acbSWarner Losh 172512e36acbSWarner Losh BWI_UNLOCK(sc); 172612e36acbSWarner Losh } 172712e36acbSWarner Losh 172812e36acbSWarner Losh static void 172912e36acbSWarner Losh bwi_scan_start(struct ieee80211com *ic) 173012e36acbSWarner Losh { 173112e36acbSWarner Losh struct bwi_softc *sc = ic->ic_ifp->if_softc; 173212e36acbSWarner Losh 173312e36acbSWarner Losh BWI_LOCK(sc); 173412e36acbSWarner Losh /* Enable MAC beacon promiscuity */ 173512e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_PASS_BCN); 173612e36acbSWarner Losh BWI_UNLOCK(sc); 173712e36acbSWarner Losh } 173812e36acbSWarner Losh 173912e36acbSWarner Losh static void 174012e36acbSWarner Losh bwi_set_channel(struct ieee80211com *ic) 174112e36acbSWarner Losh { 174212e36acbSWarner Losh struct bwi_softc *sc = ic->ic_ifp->if_softc; 174312e36acbSWarner Losh struct ieee80211_channel *c = ic->ic_curchan; 174412e36acbSWarner Losh struct bwi_mac *mac; 174512e36acbSWarner Losh 174612e36acbSWarner Losh BWI_LOCK(sc); 174712e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 174812e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 174912e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 175012e36acbSWarner Losh bwi_rf_set_chan(mac, ieee80211_chan2ieee(ic, c), 0); 175112e36acbSWarner Losh 175212e36acbSWarner Losh sc->sc_rates = ieee80211_get_ratetable(c); 175312e36acbSWarner Losh 175412e36acbSWarner Losh /* 175512e36acbSWarner Losh * Setup radio tap channel freq and flags 175612e36acbSWarner Losh */ 175712e36acbSWarner Losh sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq = 175812e36acbSWarner Losh htole16(c->ic_freq); 175912e36acbSWarner Losh sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags = 176012e36acbSWarner Losh htole16(c->ic_flags & 0xffff); 176112e36acbSWarner Losh 176212e36acbSWarner Losh BWI_UNLOCK(sc); 176312e36acbSWarner Losh } 176412e36acbSWarner Losh 176512e36acbSWarner Losh static void 176612e36acbSWarner Losh bwi_scan_end(struct ieee80211com *ic) 176712e36acbSWarner Losh { 176812e36acbSWarner Losh struct bwi_softc *sc = ic->ic_ifp->if_softc; 176912e36acbSWarner Losh 177012e36acbSWarner Losh BWI_LOCK(sc); 177112e36acbSWarner Losh CSR_CLRBITS_4(sc, BWI_MAC_STATUS, BWI_MAC_STATUS_PASS_BCN); 177212e36acbSWarner Losh BWI_UNLOCK(sc); 177312e36acbSWarner Losh } 177412e36acbSWarner Losh 177512e36acbSWarner Losh static int 177612e36acbSWarner Losh bwi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 177712e36acbSWarner Losh { 177812e36acbSWarner Losh struct bwi_vap *bvp = BWI_VAP(vap); 177937c3e522SWarner Losh struct ieee80211com *ic= vap->iv_ic; 178037c3e522SWarner Losh struct ifnet *ifp = ic->ic_ifp; 178137c3e522SWarner Losh enum ieee80211_state ostate = vap->iv_state; 178212e36acbSWarner Losh struct bwi_softc *sc = ifp->if_softc; 178312e36acbSWarner Losh struct bwi_mac *mac; 178412e36acbSWarner Losh int error; 178512e36acbSWarner Losh 178612e36acbSWarner Losh BWI_LOCK(sc); 178712e36acbSWarner Losh 178812e36acbSWarner Losh callout_stop(&sc->sc_calib_ch); 178912e36acbSWarner Losh 179012e36acbSWarner Losh if (nstate == IEEE80211_S_INIT) 179112e36acbSWarner Losh sc->sc_txpwrcb_type = BWI_TXPWR_INIT; 179212e36acbSWarner Losh 179312e36acbSWarner Losh bwi_led_newstate(sc, nstate); 179412e36acbSWarner Losh 179512e36acbSWarner Losh error = bvp->bv_newstate(vap, nstate, arg); 179612e36acbSWarner Losh if (error != 0) 179712e36acbSWarner Losh goto back; 179812e36acbSWarner Losh 179937c3e522SWarner Losh /* 180037c3e522SWarner Losh * Clear the BSSID when we stop a STA 180137c3e522SWarner Losh */ 180237c3e522SWarner Losh if (vap->iv_opmode == IEEE80211_M_STA) { 180337c3e522SWarner Losh if (ostate == IEEE80211_S_RUN && nstate != IEEE80211_S_RUN) { 180437c3e522SWarner Losh /* 180537c3e522SWarner Losh * Clear out the BSSID. If we reassociate to 180637c3e522SWarner Losh * the same AP, this will reinialize things 180737c3e522SWarner Losh * correctly... 180837c3e522SWarner Losh */ 180937c3e522SWarner Losh if (ic->ic_opmode == IEEE80211_M_STA && 181037c3e522SWarner Losh !(sc->sc_flags & BWI_F_STOP)) 181137c3e522SWarner Losh bwi_set_bssid(sc, bwi_zero_addr); 181237c3e522SWarner Losh } 181337c3e522SWarner Losh } 181437c3e522SWarner Losh 181512e36acbSWarner Losh if (vap->iv_opmode == IEEE80211_M_MONITOR) { 181612e36acbSWarner Losh /* Nothing to do */ 181712e36acbSWarner Losh } else if (nstate == IEEE80211_S_RUN) { 181812e36acbSWarner Losh bwi_set_bssid(sc, vap->iv_bss->ni_bssid); 181912e36acbSWarner Losh 182012e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 182112e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 182212e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 182312e36acbSWarner Losh 182412e36acbSWarner Losh /* Initial TX power calibration */ 182512e36acbSWarner Losh bwi_mac_calibrate_txpower(mac, BWI_TXPWR_INIT); 182612e36acbSWarner Losh #ifdef notyet 182712e36acbSWarner Losh sc->sc_txpwrcb_type = BWI_TXPWR_FORCE; 182812e36acbSWarner Losh #else 182912e36acbSWarner Losh sc->sc_txpwrcb_type = BWI_TXPWR_CALIB; 183012e36acbSWarner Losh #endif 183112e36acbSWarner Losh 183212e36acbSWarner Losh callout_reset(&sc->sc_calib_ch, hz, bwi_calibrate, sc); 183312e36acbSWarner Losh } 183412e36acbSWarner Losh back: 183512e36acbSWarner Losh BWI_UNLOCK(sc); 183612e36acbSWarner Losh 183712e36acbSWarner Losh return error; 183812e36acbSWarner Losh } 183912e36acbSWarner Losh 184012e36acbSWarner Losh static int 184112e36acbSWarner Losh bwi_media_change(struct ifnet *ifp) 184212e36acbSWarner Losh { 184312e36acbSWarner Losh int error = ieee80211_media_change(ifp); 184412e36acbSWarner Losh /* NB: only the fixed rate can change and that doesn't need a reset */ 184512e36acbSWarner Losh return (error == ENETRESET ? 0 : error); 184612e36acbSWarner Losh } 184712e36acbSWarner Losh 184812e36acbSWarner Losh static int 184912e36acbSWarner Losh bwi_dma_alloc(struct bwi_softc *sc) 185012e36acbSWarner Losh { 185112e36acbSWarner Losh int error, i, has_txstats; 185212e36acbSWarner Losh bus_addr_t lowaddr = 0; 185312e36acbSWarner Losh bus_size_t tx_ring_sz, rx_ring_sz, desc_sz = 0; 185412e36acbSWarner Losh uint32_t txrx_ctrl_step = 0; 185512e36acbSWarner Losh 185612e36acbSWarner Losh has_txstats = 0; 185712e36acbSWarner Losh for (i = 0; i < sc->sc_nmac; ++i) { 185812e36acbSWarner Losh if (sc->sc_mac[i].mac_flags & BWI_MAC_F_HAS_TXSTATS) { 185912e36acbSWarner Losh has_txstats = 1; 186012e36acbSWarner Losh break; 186112e36acbSWarner Losh } 186212e36acbSWarner Losh } 186312e36acbSWarner Losh 186412e36acbSWarner Losh switch (sc->sc_bus_space) { 186512e36acbSWarner Losh case BWI_BUS_SPACE_30BIT: 186612e36acbSWarner Losh case BWI_BUS_SPACE_32BIT: 186712e36acbSWarner Losh if (sc->sc_bus_space == BWI_BUS_SPACE_30BIT) 186812e36acbSWarner Losh lowaddr = BWI_BUS_SPACE_MAXADDR; 186912e36acbSWarner Losh else 187012e36acbSWarner Losh lowaddr = BUS_SPACE_MAXADDR_32BIT; 187112e36acbSWarner Losh desc_sz = sizeof(struct bwi_desc32); 187212e36acbSWarner Losh txrx_ctrl_step = 0x20; 187312e36acbSWarner Losh 187412e36acbSWarner Losh sc->sc_init_tx_ring = bwi_init_tx_ring32; 187512e36acbSWarner Losh sc->sc_free_tx_ring = bwi_free_tx_ring32; 187612e36acbSWarner Losh sc->sc_init_rx_ring = bwi_init_rx_ring32; 187712e36acbSWarner Losh sc->sc_free_rx_ring = bwi_free_rx_ring32; 187812e36acbSWarner Losh sc->sc_setup_rxdesc = bwi_setup_rx_desc32; 187912e36acbSWarner Losh sc->sc_setup_txdesc = bwi_setup_tx_desc32; 188012e36acbSWarner Losh sc->sc_rxeof = bwi_rxeof32; 188112e36acbSWarner Losh sc->sc_start_tx = bwi_start_tx32; 188212e36acbSWarner Losh if (has_txstats) { 188312e36acbSWarner Losh sc->sc_init_txstats = bwi_init_txstats32; 188412e36acbSWarner Losh sc->sc_free_txstats = bwi_free_txstats32; 188512e36acbSWarner Losh sc->sc_txeof_status = bwi_txeof_status32; 188612e36acbSWarner Losh } 188712e36acbSWarner Losh break; 188812e36acbSWarner Losh 188912e36acbSWarner Losh case BWI_BUS_SPACE_64BIT: 189012e36acbSWarner Losh lowaddr = BUS_SPACE_MAXADDR; /* XXX */ 189112e36acbSWarner Losh desc_sz = sizeof(struct bwi_desc64); 189212e36acbSWarner Losh txrx_ctrl_step = 0x40; 189312e36acbSWarner Losh 189412e36acbSWarner Losh sc->sc_init_tx_ring = bwi_init_tx_ring64; 189512e36acbSWarner Losh sc->sc_free_tx_ring = bwi_free_tx_ring64; 189612e36acbSWarner Losh sc->sc_init_rx_ring = bwi_init_rx_ring64; 189712e36acbSWarner Losh sc->sc_free_rx_ring = bwi_free_rx_ring64; 189812e36acbSWarner Losh sc->sc_setup_rxdesc = bwi_setup_rx_desc64; 189912e36acbSWarner Losh sc->sc_setup_txdesc = bwi_setup_tx_desc64; 190012e36acbSWarner Losh sc->sc_rxeof = bwi_rxeof64; 190112e36acbSWarner Losh sc->sc_start_tx = bwi_start_tx64; 190212e36acbSWarner Losh if (has_txstats) { 190312e36acbSWarner Losh sc->sc_init_txstats = bwi_init_txstats64; 190412e36acbSWarner Losh sc->sc_free_txstats = bwi_free_txstats64; 190512e36acbSWarner Losh sc->sc_txeof_status = bwi_txeof_status64; 190612e36acbSWarner Losh } 190712e36acbSWarner Losh break; 190812e36acbSWarner Losh } 190912e36acbSWarner Losh 191012e36acbSWarner Losh KASSERT(lowaddr != 0, ("lowaddr zero")); 191112e36acbSWarner Losh KASSERT(desc_sz != 0, ("desc_sz zero")); 191212e36acbSWarner Losh KASSERT(txrx_ctrl_step != 0, ("txrx_ctrl_step zero")); 191312e36acbSWarner Losh 191412e36acbSWarner Losh tx_ring_sz = roundup(desc_sz * BWI_TX_NDESC, BWI_RING_ALIGN); 191512e36acbSWarner Losh rx_ring_sz = roundup(desc_sz * BWI_RX_NDESC, BWI_RING_ALIGN); 191612e36acbSWarner Losh 191712e36acbSWarner Losh /* 191812e36acbSWarner Losh * Create top level DMA tag 191912e36acbSWarner Losh */ 192012e36acbSWarner Losh error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ 192112e36acbSWarner Losh BWI_ALIGN, 0, /* alignment, bounds */ 192212e36acbSWarner Losh lowaddr, /* lowaddr */ 192312e36acbSWarner Losh BUS_SPACE_MAXADDR, /* highaddr */ 192412e36acbSWarner Losh NULL, NULL, /* filter, filterarg */ 19256f954fb3SAlexander Motin BUS_SPACE_MAXSIZE, /* maxsize */ 192612e36acbSWarner Losh BUS_SPACE_UNRESTRICTED, /* nsegments */ 192712e36acbSWarner Losh BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 1928515b3730SGleb Smirnoff 0, /* flags */ 192912e36acbSWarner Losh NULL, NULL, /* lockfunc, lockarg */ 193012e36acbSWarner Losh &sc->sc_parent_dtag); 193112e36acbSWarner Losh if (error) { 193212e36acbSWarner Losh device_printf(sc->sc_dev, "can't create parent DMA tag\n"); 193312e36acbSWarner Losh return error; 193412e36acbSWarner Losh } 193512e36acbSWarner Losh 193612e36acbSWarner Losh #define TXRX_CTRL(idx) (BWI_TXRX_CTRL_BASE + (idx) * txrx_ctrl_step) 193712e36acbSWarner Losh 193812e36acbSWarner Losh /* 193912e36acbSWarner Losh * Create TX ring DMA stuffs 194012e36acbSWarner Losh */ 194112e36acbSWarner Losh error = bus_dma_tag_create(sc->sc_parent_dtag, 194212e36acbSWarner Losh BWI_RING_ALIGN, 0, 194312e36acbSWarner Losh BUS_SPACE_MAXADDR, 194412e36acbSWarner Losh BUS_SPACE_MAXADDR, 194512e36acbSWarner Losh NULL, NULL, 194612e36acbSWarner Losh tx_ring_sz, 194712e36acbSWarner Losh 1, 1948*2ae078a3SScott Long tx_ring_sz, 1949515b3730SGleb Smirnoff 0, 195012e36acbSWarner Losh NULL, NULL, 195112e36acbSWarner Losh &sc->sc_txring_dtag); 195212e36acbSWarner Losh if (error) { 195312e36acbSWarner Losh device_printf(sc->sc_dev, "can't create TX ring DMA tag\n"); 195412e36acbSWarner Losh return error; 195512e36acbSWarner Losh } 195612e36acbSWarner Losh 195712e36acbSWarner Losh for (i = 0; i < BWI_TX_NRING; ++i) { 195812e36acbSWarner Losh error = bwi_dma_ring_alloc(sc, sc->sc_txring_dtag, 195912e36acbSWarner Losh &sc->sc_tx_rdata[i], tx_ring_sz, 196012e36acbSWarner Losh TXRX_CTRL(i)); 196112e36acbSWarner Losh if (error) { 196212e36acbSWarner Losh device_printf(sc->sc_dev, "%dth TX ring " 196312e36acbSWarner Losh "DMA alloc failed\n", i); 196412e36acbSWarner Losh return error; 196512e36acbSWarner Losh } 196612e36acbSWarner Losh } 196712e36acbSWarner Losh 196812e36acbSWarner Losh /* 196912e36acbSWarner Losh * Create RX ring DMA stuffs 197012e36acbSWarner Losh */ 197112e36acbSWarner Losh error = bus_dma_tag_create(sc->sc_parent_dtag, 197212e36acbSWarner Losh BWI_RING_ALIGN, 0, 197312e36acbSWarner Losh BUS_SPACE_MAXADDR, 197412e36acbSWarner Losh BUS_SPACE_MAXADDR, 197512e36acbSWarner Losh NULL, NULL, 197612e36acbSWarner Losh rx_ring_sz, 197712e36acbSWarner Losh 1, 1978*2ae078a3SScott Long rx_ring_sz, 1979515b3730SGleb Smirnoff 0, 198012e36acbSWarner Losh NULL, NULL, 198112e36acbSWarner Losh &sc->sc_rxring_dtag); 198212e36acbSWarner Losh if (error) { 198312e36acbSWarner Losh device_printf(sc->sc_dev, "can't create RX ring DMA tag\n"); 198412e36acbSWarner Losh return error; 198512e36acbSWarner Losh } 198612e36acbSWarner Losh 198712e36acbSWarner Losh error = bwi_dma_ring_alloc(sc, sc->sc_rxring_dtag, &sc->sc_rx_rdata, 198812e36acbSWarner Losh rx_ring_sz, TXRX_CTRL(0)); 198912e36acbSWarner Losh if (error) { 199012e36acbSWarner Losh device_printf(sc->sc_dev, "RX ring DMA alloc failed\n"); 199112e36acbSWarner Losh return error; 199212e36acbSWarner Losh } 199312e36acbSWarner Losh 199412e36acbSWarner Losh if (has_txstats) { 199512e36acbSWarner Losh error = bwi_dma_txstats_alloc(sc, TXRX_CTRL(3), desc_sz); 199612e36acbSWarner Losh if (error) { 199712e36acbSWarner Losh device_printf(sc->sc_dev, 199812e36acbSWarner Losh "TX stats DMA alloc failed\n"); 199912e36acbSWarner Losh return error; 200012e36acbSWarner Losh } 200112e36acbSWarner Losh } 200212e36acbSWarner Losh 200312e36acbSWarner Losh #undef TXRX_CTRL 200412e36acbSWarner Losh 200512e36acbSWarner Losh return bwi_dma_mbuf_create(sc); 200612e36acbSWarner Losh } 200712e36acbSWarner Losh 200812e36acbSWarner Losh static void 200912e36acbSWarner Losh bwi_dma_free(struct bwi_softc *sc) 201012e36acbSWarner Losh { 201112e36acbSWarner Losh if (sc->sc_txring_dtag != NULL) { 201212e36acbSWarner Losh int i; 201312e36acbSWarner Losh 201412e36acbSWarner Losh for (i = 0; i < BWI_TX_NRING; ++i) { 201512e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_tx_rdata[i]; 201612e36acbSWarner Losh 201712e36acbSWarner Losh if (rd->rdata_desc != NULL) { 201812e36acbSWarner Losh bus_dmamap_unload(sc->sc_txring_dtag, 201912e36acbSWarner Losh rd->rdata_dmap); 202012e36acbSWarner Losh bus_dmamem_free(sc->sc_txring_dtag, 202112e36acbSWarner Losh rd->rdata_desc, 202212e36acbSWarner Losh rd->rdata_dmap); 202312e36acbSWarner Losh } 202412e36acbSWarner Losh } 202512e36acbSWarner Losh bus_dma_tag_destroy(sc->sc_txring_dtag); 202612e36acbSWarner Losh } 202712e36acbSWarner Losh 202812e36acbSWarner Losh if (sc->sc_rxring_dtag != NULL) { 202912e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_rx_rdata; 203012e36acbSWarner Losh 203112e36acbSWarner Losh if (rd->rdata_desc != NULL) { 203212e36acbSWarner Losh bus_dmamap_unload(sc->sc_rxring_dtag, rd->rdata_dmap); 203312e36acbSWarner Losh bus_dmamem_free(sc->sc_rxring_dtag, rd->rdata_desc, 203412e36acbSWarner Losh rd->rdata_dmap); 203512e36acbSWarner Losh } 203612e36acbSWarner Losh bus_dma_tag_destroy(sc->sc_rxring_dtag); 203712e36acbSWarner Losh } 203812e36acbSWarner Losh 203912e36acbSWarner Losh bwi_dma_txstats_free(sc); 204012e36acbSWarner Losh bwi_dma_mbuf_destroy(sc, BWI_TX_NRING, 1); 204112e36acbSWarner Losh 204212e36acbSWarner Losh if (sc->sc_parent_dtag != NULL) 204312e36acbSWarner Losh bus_dma_tag_destroy(sc->sc_parent_dtag); 204412e36acbSWarner Losh } 204512e36acbSWarner Losh 204612e36acbSWarner Losh static int 204712e36acbSWarner Losh bwi_dma_ring_alloc(struct bwi_softc *sc, bus_dma_tag_t dtag, 204812e36acbSWarner Losh struct bwi_ring_data *rd, bus_size_t size, 204912e36acbSWarner Losh uint32_t txrx_ctrl) 205012e36acbSWarner Losh { 205112e36acbSWarner Losh int error; 205212e36acbSWarner Losh 205312e36acbSWarner Losh error = bus_dmamem_alloc(dtag, &rd->rdata_desc, 205412e36acbSWarner Losh BUS_DMA_WAITOK | BUS_DMA_ZERO, 205512e36acbSWarner Losh &rd->rdata_dmap); 205612e36acbSWarner Losh if (error) { 205712e36acbSWarner Losh device_printf(sc->sc_dev, "can't allocate DMA mem\n"); 205812e36acbSWarner Losh return error; 205912e36acbSWarner Losh } 206012e36acbSWarner Losh 206112e36acbSWarner Losh error = bus_dmamap_load(dtag, rd->rdata_dmap, rd->rdata_desc, size, 206212e36acbSWarner Losh bwi_dma_ring_addr, &rd->rdata_paddr, 206312e36acbSWarner Losh BUS_DMA_NOWAIT); 206412e36acbSWarner Losh if (error) { 206512e36acbSWarner Losh device_printf(sc->sc_dev, "can't load DMA mem\n"); 206612e36acbSWarner Losh bus_dmamem_free(dtag, rd->rdata_desc, rd->rdata_dmap); 206712e36acbSWarner Losh rd->rdata_desc = NULL; 206812e36acbSWarner Losh return error; 206912e36acbSWarner Losh } 207012e36acbSWarner Losh 207112e36acbSWarner Losh rd->rdata_txrx_ctrl = txrx_ctrl; 207212e36acbSWarner Losh return 0; 207312e36acbSWarner Losh } 207412e36acbSWarner Losh 207512e36acbSWarner Losh static int 207612e36acbSWarner Losh bwi_dma_txstats_alloc(struct bwi_softc *sc, uint32_t ctrl_base, 207712e36acbSWarner Losh bus_size_t desc_sz) 207812e36acbSWarner Losh { 207912e36acbSWarner Losh struct bwi_txstats_data *st; 208012e36acbSWarner Losh bus_size_t dma_size; 208112e36acbSWarner Losh int error; 208212e36acbSWarner Losh 208312e36acbSWarner Losh st = malloc(sizeof(*st), M_DEVBUF, M_NOWAIT | M_ZERO); 208412e36acbSWarner Losh if (st == NULL) { 208512e36acbSWarner Losh device_printf(sc->sc_dev, "can't allocate txstats data\n"); 208612e36acbSWarner Losh return ENOMEM; 208712e36acbSWarner Losh } 208812e36acbSWarner Losh sc->sc_txstats = st; 208912e36acbSWarner Losh 209012e36acbSWarner Losh /* 209112e36acbSWarner Losh * Create TX stats descriptor DMA stuffs 209212e36acbSWarner Losh */ 209312e36acbSWarner Losh dma_size = roundup(desc_sz * BWI_TXSTATS_NDESC, BWI_RING_ALIGN); 209412e36acbSWarner Losh 209512e36acbSWarner Losh error = bus_dma_tag_create(sc->sc_parent_dtag, 209612e36acbSWarner Losh BWI_RING_ALIGN, 209712e36acbSWarner Losh 0, 209812e36acbSWarner Losh BUS_SPACE_MAXADDR, 209912e36acbSWarner Losh BUS_SPACE_MAXADDR, 210012e36acbSWarner Losh NULL, NULL, 210112e36acbSWarner Losh dma_size, 210212e36acbSWarner Losh 1, 2103*2ae078a3SScott Long dma_size, 2104515b3730SGleb Smirnoff 0, 210512e36acbSWarner Losh NULL, NULL, 210612e36acbSWarner Losh &st->stats_ring_dtag); 210712e36acbSWarner Losh if (error) { 210812e36acbSWarner Losh device_printf(sc->sc_dev, "can't create txstats ring " 210912e36acbSWarner Losh "DMA tag\n"); 211012e36acbSWarner Losh return error; 211112e36acbSWarner Losh } 211212e36acbSWarner Losh 211312e36acbSWarner Losh error = bus_dmamem_alloc(st->stats_ring_dtag, &st->stats_ring, 211412e36acbSWarner Losh BUS_DMA_WAITOK | BUS_DMA_ZERO, 211512e36acbSWarner Losh &st->stats_ring_dmap); 211612e36acbSWarner Losh if (error) { 211712e36acbSWarner Losh device_printf(sc->sc_dev, "can't allocate txstats ring " 211812e36acbSWarner Losh "DMA mem\n"); 211912e36acbSWarner Losh bus_dma_tag_destroy(st->stats_ring_dtag); 212012e36acbSWarner Losh st->stats_ring_dtag = NULL; 212112e36acbSWarner Losh return error; 212212e36acbSWarner Losh } 212312e36acbSWarner Losh 212412e36acbSWarner Losh error = bus_dmamap_load(st->stats_ring_dtag, st->stats_ring_dmap, 212512e36acbSWarner Losh st->stats_ring, dma_size, 212612e36acbSWarner Losh bwi_dma_ring_addr, &st->stats_ring_paddr, 212712e36acbSWarner Losh BUS_DMA_NOWAIT); 212812e36acbSWarner Losh if (error) { 212912e36acbSWarner Losh device_printf(sc->sc_dev, "can't load txstats ring DMA mem\n"); 213012e36acbSWarner Losh bus_dmamem_free(st->stats_ring_dtag, st->stats_ring, 213112e36acbSWarner Losh st->stats_ring_dmap); 213212e36acbSWarner Losh bus_dma_tag_destroy(st->stats_ring_dtag); 213312e36acbSWarner Losh st->stats_ring_dtag = NULL; 213412e36acbSWarner Losh return error; 213512e36acbSWarner Losh } 213612e36acbSWarner Losh 213712e36acbSWarner Losh /* 213812e36acbSWarner Losh * Create TX stats DMA stuffs 213912e36acbSWarner Losh */ 214012e36acbSWarner Losh dma_size = roundup(sizeof(struct bwi_txstats) * BWI_TXSTATS_NDESC, 214112e36acbSWarner Losh BWI_ALIGN); 214212e36acbSWarner Losh 214312e36acbSWarner Losh error = bus_dma_tag_create(sc->sc_parent_dtag, 214412e36acbSWarner Losh BWI_ALIGN, 214512e36acbSWarner Losh 0, 214612e36acbSWarner Losh BUS_SPACE_MAXADDR, 214712e36acbSWarner Losh BUS_SPACE_MAXADDR, 214812e36acbSWarner Losh NULL, NULL, 214912e36acbSWarner Losh dma_size, 215012e36acbSWarner Losh 1, 2151*2ae078a3SScott Long dma_size, 2152515b3730SGleb Smirnoff 0, 215312e36acbSWarner Losh NULL, NULL, 215412e36acbSWarner Losh &st->stats_dtag); 215512e36acbSWarner Losh if (error) { 215612e36acbSWarner Losh device_printf(sc->sc_dev, "can't create txstats DMA tag\n"); 215712e36acbSWarner Losh return error; 215812e36acbSWarner Losh } 215912e36acbSWarner Losh 216012e36acbSWarner Losh error = bus_dmamem_alloc(st->stats_dtag, (void **)&st->stats, 216112e36acbSWarner Losh BUS_DMA_WAITOK | BUS_DMA_ZERO, 216212e36acbSWarner Losh &st->stats_dmap); 216312e36acbSWarner Losh if (error) { 216412e36acbSWarner Losh device_printf(sc->sc_dev, "can't allocate txstats DMA mem\n"); 216512e36acbSWarner Losh bus_dma_tag_destroy(st->stats_dtag); 216612e36acbSWarner Losh st->stats_dtag = NULL; 216712e36acbSWarner Losh return error; 216812e36acbSWarner Losh } 216912e36acbSWarner Losh 217012e36acbSWarner Losh error = bus_dmamap_load(st->stats_dtag, st->stats_dmap, st->stats, 217112e36acbSWarner Losh dma_size, bwi_dma_ring_addr, &st->stats_paddr, 217212e36acbSWarner Losh BUS_DMA_NOWAIT); 217312e36acbSWarner Losh if (error) { 217412e36acbSWarner Losh device_printf(sc->sc_dev, "can't load txstats DMA mem\n"); 217512e36acbSWarner Losh bus_dmamem_free(st->stats_dtag, st->stats, st->stats_dmap); 217612e36acbSWarner Losh bus_dma_tag_destroy(st->stats_dtag); 217712e36acbSWarner Losh st->stats_dtag = NULL; 217812e36acbSWarner Losh return error; 217912e36acbSWarner Losh } 218012e36acbSWarner Losh 218112e36acbSWarner Losh st->stats_ctrl_base = ctrl_base; 218212e36acbSWarner Losh return 0; 218312e36acbSWarner Losh } 218412e36acbSWarner Losh 218512e36acbSWarner Losh static void 218612e36acbSWarner Losh bwi_dma_txstats_free(struct bwi_softc *sc) 218712e36acbSWarner Losh { 218812e36acbSWarner Losh struct bwi_txstats_data *st; 218912e36acbSWarner Losh 219012e36acbSWarner Losh if (sc->sc_txstats == NULL) 219112e36acbSWarner Losh return; 219212e36acbSWarner Losh st = sc->sc_txstats; 219312e36acbSWarner Losh 219412e36acbSWarner Losh if (st->stats_ring_dtag != NULL) { 219512e36acbSWarner Losh bus_dmamap_unload(st->stats_ring_dtag, st->stats_ring_dmap); 219612e36acbSWarner Losh bus_dmamem_free(st->stats_ring_dtag, st->stats_ring, 219712e36acbSWarner Losh st->stats_ring_dmap); 219812e36acbSWarner Losh bus_dma_tag_destroy(st->stats_ring_dtag); 219912e36acbSWarner Losh } 220012e36acbSWarner Losh 220112e36acbSWarner Losh if (st->stats_dtag != NULL) { 220212e36acbSWarner Losh bus_dmamap_unload(st->stats_dtag, st->stats_dmap); 220312e36acbSWarner Losh bus_dmamem_free(st->stats_dtag, st->stats, st->stats_dmap); 220412e36acbSWarner Losh bus_dma_tag_destroy(st->stats_dtag); 220512e36acbSWarner Losh } 220612e36acbSWarner Losh 220712e36acbSWarner Losh free(st, M_DEVBUF); 220812e36acbSWarner Losh } 220912e36acbSWarner Losh 221012e36acbSWarner Losh static void 221112e36acbSWarner Losh bwi_dma_ring_addr(void *arg, bus_dma_segment_t *seg, int nseg, int error) 221212e36acbSWarner Losh { 221312e36acbSWarner Losh KASSERT(nseg == 1, ("too many segments\n")); 221412e36acbSWarner Losh *((bus_addr_t *)arg) = seg->ds_addr; 221512e36acbSWarner Losh } 221612e36acbSWarner Losh 221712e36acbSWarner Losh static int 221812e36acbSWarner Losh bwi_dma_mbuf_create(struct bwi_softc *sc) 221912e36acbSWarner Losh { 222012e36acbSWarner Losh struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; 222112e36acbSWarner Losh int i, j, k, ntx, error; 222212e36acbSWarner Losh 222312e36acbSWarner Losh /* 222412e36acbSWarner Losh * Create TX/RX mbuf DMA tag 222512e36acbSWarner Losh */ 222612e36acbSWarner Losh error = bus_dma_tag_create(sc->sc_parent_dtag, 222712e36acbSWarner Losh 1, 222812e36acbSWarner Losh 0, 222912e36acbSWarner Losh BUS_SPACE_MAXADDR, 223012e36acbSWarner Losh BUS_SPACE_MAXADDR, 223112e36acbSWarner Losh NULL, NULL, 223212e36acbSWarner Losh MCLBYTES, 223312e36acbSWarner Losh 1, 2234515b3730SGleb Smirnoff MCLBYTES, 223512e36acbSWarner Losh BUS_DMA_ALLOCNOW, 223612e36acbSWarner Losh NULL, NULL, 223712e36acbSWarner Losh &sc->sc_buf_dtag); 223812e36acbSWarner Losh if (error) { 223912e36acbSWarner Losh device_printf(sc->sc_dev, "can't create mbuf DMA tag\n"); 224012e36acbSWarner Losh return error; 224112e36acbSWarner Losh } 224212e36acbSWarner Losh 224312e36acbSWarner Losh ntx = 0; 224412e36acbSWarner Losh 224512e36acbSWarner Losh /* 224612e36acbSWarner Losh * Create TX mbuf DMA map 224712e36acbSWarner Losh */ 224812e36acbSWarner Losh for (i = 0; i < BWI_TX_NRING; ++i) { 224912e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[i]; 225012e36acbSWarner Losh 225112e36acbSWarner Losh for (j = 0; j < BWI_TX_NDESC; ++j) { 225212e36acbSWarner Losh error = bus_dmamap_create(sc->sc_buf_dtag, 0, 225312e36acbSWarner Losh &tbd->tbd_buf[j].tb_dmap); 225412e36acbSWarner Losh if (error) { 225512e36acbSWarner Losh device_printf(sc->sc_dev, "can't create " 225612e36acbSWarner Losh "%dth tbd, %dth DMA map\n", i, j); 225712e36acbSWarner Losh 225812e36acbSWarner Losh ntx = i; 225912e36acbSWarner Losh for (k = 0; k < j; ++k) { 226012e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, 226112e36acbSWarner Losh tbd->tbd_buf[k].tb_dmap); 226212e36acbSWarner Losh } 226312e36acbSWarner Losh goto fail; 226412e36acbSWarner Losh } 226512e36acbSWarner Losh } 226612e36acbSWarner Losh } 226712e36acbSWarner Losh ntx = BWI_TX_NRING; 226812e36acbSWarner Losh 226912e36acbSWarner Losh /* 227012e36acbSWarner Losh * Create RX mbuf DMA map and a spare DMA map 227112e36acbSWarner Losh */ 227212e36acbSWarner Losh error = bus_dmamap_create(sc->sc_buf_dtag, 0, 227312e36acbSWarner Losh &rbd->rbd_tmp_dmap); 227412e36acbSWarner Losh if (error) { 227512e36acbSWarner Losh device_printf(sc->sc_dev, 227612e36acbSWarner Losh "can't create spare RX buf DMA map\n"); 227712e36acbSWarner Losh goto fail; 227812e36acbSWarner Losh } 227912e36acbSWarner Losh 228012e36acbSWarner Losh for (j = 0; j < BWI_RX_NDESC; ++j) { 228112e36acbSWarner Losh error = bus_dmamap_create(sc->sc_buf_dtag, 0, 228212e36acbSWarner Losh &rbd->rbd_buf[j].rb_dmap); 228312e36acbSWarner Losh if (error) { 228412e36acbSWarner Losh device_printf(sc->sc_dev, "can't create %dth " 228512e36acbSWarner Losh "RX buf DMA map\n", j); 228612e36acbSWarner Losh 228712e36acbSWarner Losh for (k = 0; k < j; ++k) { 228812e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, 228912e36acbSWarner Losh rbd->rbd_buf[j].rb_dmap); 229012e36acbSWarner Losh } 229112e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, 229212e36acbSWarner Losh rbd->rbd_tmp_dmap); 229312e36acbSWarner Losh goto fail; 229412e36acbSWarner Losh } 229512e36acbSWarner Losh } 229612e36acbSWarner Losh 229712e36acbSWarner Losh return 0; 229812e36acbSWarner Losh fail: 229912e36acbSWarner Losh bwi_dma_mbuf_destroy(sc, ntx, 0); 230012e36acbSWarner Losh return error; 230112e36acbSWarner Losh } 230212e36acbSWarner Losh 230312e36acbSWarner Losh static void 230412e36acbSWarner Losh bwi_dma_mbuf_destroy(struct bwi_softc *sc, int ntx, int nrx) 230512e36acbSWarner Losh { 230612e36acbSWarner Losh int i, j; 230712e36acbSWarner Losh 230812e36acbSWarner Losh if (sc->sc_buf_dtag == NULL) 230912e36acbSWarner Losh return; 231012e36acbSWarner Losh 231112e36acbSWarner Losh for (i = 0; i < ntx; ++i) { 231212e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[i]; 231312e36acbSWarner Losh 231412e36acbSWarner Losh for (j = 0; j < BWI_TX_NDESC; ++j) { 231512e36acbSWarner Losh struct bwi_txbuf *tb = &tbd->tbd_buf[j]; 231612e36acbSWarner Losh 231712e36acbSWarner Losh if (tb->tb_mbuf != NULL) { 231812e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, 231912e36acbSWarner Losh tb->tb_dmap); 232012e36acbSWarner Losh m_freem(tb->tb_mbuf); 232112e36acbSWarner Losh } 232212e36acbSWarner Losh if (tb->tb_ni != NULL) 232312e36acbSWarner Losh ieee80211_free_node(tb->tb_ni); 232412e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, tb->tb_dmap); 232512e36acbSWarner Losh } 232612e36acbSWarner Losh } 232712e36acbSWarner Losh 232812e36acbSWarner Losh if (nrx) { 232912e36acbSWarner Losh struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; 233012e36acbSWarner Losh 233112e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, rbd->rbd_tmp_dmap); 233212e36acbSWarner Losh for (j = 0; j < BWI_RX_NDESC; ++j) { 233312e36acbSWarner Losh struct bwi_rxbuf *rb = &rbd->rbd_buf[j]; 233412e36acbSWarner Losh 233512e36acbSWarner Losh if (rb->rb_mbuf != NULL) { 233612e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, 233712e36acbSWarner Losh rb->rb_dmap); 233812e36acbSWarner Losh m_freem(rb->rb_mbuf); 233912e36acbSWarner Losh } 234012e36acbSWarner Losh bus_dmamap_destroy(sc->sc_buf_dtag, rb->rb_dmap); 234112e36acbSWarner Losh } 234212e36acbSWarner Losh } 234312e36acbSWarner Losh 234412e36acbSWarner Losh bus_dma_tag_destroy(sc->sc_buf_dtag); 234512e36acbSWarner Losh sc->sc_buf_dtag = NULL; 234612e36acbSWarner Losh } 234712e36acbSWarner Losh 234812e36acbSWarner Losh static void 234912e36acbSWarner Losh bwi_enable_intrs(struct bwi_softc *sc, uint32_t enable_intrs) 235012e36acbSWarner Losh { 235112e36acbSWarner Losh CSR_SETBITS_4(sc, BWI_MAC_INTR_MASK, enable_intrs); 235212e36acbSWarner Losh } 235312e36acbSWarner Losh 235412e36acbSWarner Losh static void 235512e36acbSWarner Losh bwi_disable_intrs(struct bwi_softc *sc, uint32_t disable_intrs) 235612e36acbSWarner Losh { 235712e36acbSWarner Losh CSR_CLRBITS_4(sc, BWI_MAC_INTR_MASK, disable_intrs); 235812e36acbSWarner Losh } 235912e36acbSWarner Losh 236012e36acbSWarner Losh static int 236112e36acbSWarner Losh bwi_init_tx_ring32(struct bwi_softc *sc, int ring_idx) 236212e36acbSWarner Losh { 236312e36acbSWarner Losh struct bwi_ring_data *rd; 236412e36acbSWarner Losh struct bwi_txbuf_data *tbd; 236512e36acbSWarner Losh uint32_t val, addr_hi, addr_lo; 236612e36acbSWarner Losh 236712e36acbSWarner Losh KASSERT(ring_idx < BWI_TX_NRING, ("ring_idx %d", ring_idx)); 236812e36acbSWarner Losh rd = &sc->sc_tx_rdata[ring_idx]; 236912e36acbSWarner Losh tbd = &sc->sc_tx_bdata[ring_idx]; 237012e36acbSWarner Losh 237112e36acbSWarner Losh tbd->tbd_idx = 0; 237212e36acbSWarner Losh tbd->tbd_used = 0; 237312e36acbSWarner Losh 237412e36acbSWarner Losh bzero(rd->rdata_desc, sizeof(struct bwi_desc32) * BWI_TX_NDESC); 237512e36acbSWarner Losh bus_dmamap_sync(sc->sc_txring_dtag, rd->rdata_dmap, 237612e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 237712e36acbSWarner Losh 237812e36acbSWarner Losh addr_lo = __SHIFTOUT(rd->rdata_paddr, BWI_TXRX32_RINGINFO_ADDR_MASK); 237912e36acbSWarner Losh addr_hi = __SHIFTOUT(rd->rdata_paddr, BWI_TXRX32_RINGINFO_FUNC_MASK); 238012e36acbSWarner Losh 238112e36acbSWarner Losh val = __SHIFTIN(addr_lo, BWI_TXRX32_RINGINFO_ADDR_MASK) | 238212e36acbSWarner Losh __SHIFTIN(BWI_TXRX32_RINGINFO_FUNC_TXRX, 238312e36acbSWarner Losh BWI_TXRX32_RINGINFO_FUNC_MASK); 238412e36acbSWarner Losh CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_RINGINFO, val); 238512e36acbSWarner Losh 238612e36acbSWarner Losh val = __SHIFTIN(addr_hi, BWI_TXRX32_CTRL_ADDRHI_MASK) | 238712e36acbSWarner Losh BWI_TXRX32_CTRL_ENABLE; 238812e36acbSWarner Losh CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_CTRL, val); 238912e36acbSWarner Losh 239012e36acbSWarner Losh return 0; 239112e36acbSWarner Losh } 239212e36acbSWarner Losh 239312e36acbSWarner Losh static void 239412e36acbSWarner Losh bwi_init_rxdesc_ring32(struct bwi_softc *sc, uint32_t ctrl_base, 239512e36acbSWarner Losh bus_addr_t paddr, int hdr_size, int ndesc) 239612e36acbSWarner Losh { 239712e36acbSWarner Losh uint32_t val, addr_hi, addr_lo; 239812e36acbSWarner Losh 239912e36acbSWarner Losh addr_lo = __SHIFTOUT(paddr, BWI_TXRX32_RINGINFO_ADDR_MASK); 240012e36acbSWarner Losh addr_hi = __SHIFTOUT(paddr, BWI_TXRX32_RINGINFO_FUNC_MASK); 240112e36acbSWarner Losh 240212e36acbSWarner Losh val = __SHIFTIN(addr_lo, BWI_TXRX32_RINGINFO_ADDR_MASK) | 240312e36acbSWarner Losh __SHIFTIN(BWI_TXRX32_RINGINFO_FUNC_TXRX, 240412e36acbSWarner Losh BWI_TXRX32_RINGINFO_FUNC_MASK); 240512e36acbSWarner Losh CSR_WRITE_4(sc, ctrl_base + BWI_RX32_RINGINFO, val); 240612e36acbSWarner Losh 240712e36acbSWarner Losh val = __SHIFTIN(hdr_size, BWI_RX32_CTRL_HDRSZ_MASK) | 240812e36acbSWarner Losh __SHIFTIN(addr_hi, BWI_TXRX32_CTRL_ADDRHI_MASK) | 240912e36acbSWarner Losh BWI_TXRX32_CTRL_ENABLE; 241012e36acbSWarner Losh CSR_WRITE_4(sc, ctrl_base + BWI_RX32_CTRL, val); 241112e36acbSWarner Losh 241212e36acbSWarner Losh CSR_WRITE_4(sc, ctrl_base + BWI_RX32_INDEX, 241312e36acbSWarner Losh (ndesc - 1) * sizeof(struct bwi_desc32)); 241412e36acbSWarner Losh } 241512e36acbSWarner Losh 241612e36acbSWarner Losh static int 241712e36acbSWarner Losh bwi_init_rx_ring32(struct bwi_softc *sc) 241812e36acbSWarner Losh { 241912e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_rx_rdata; 242012e36acbSWarner Losh int i, error; 242112e36acbSWarner Losh 242212e36acbSWarner Losh sc->sc_rx_bdata.rbd_idx = 0; 242312e36acbSWarner Losh 242412e36acbSWarner Losh for (i = 0; i < BWI_RX_NDESC; ++i) { 242512e36acbSWarner Losh error = bwi_newbuf(sc, i, 1); 242612e36acbSWarner Losh if (error) { 242712e36acbSWarner Losh device_printf(sc->sc_dev, 242812e36acbSWarner Losh "can't allocate %dth RX buffer\n", i); 242912e36acbSWarner Losh return error; 243012e36acbSWarner Losh } 243112e36acbSWarner Losh } 243212e36acbSWarner Losh bus_dmamap_sync(sc->sc_rxring_dtag, rd->rdata_dmap, 243312e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 243412e36acbSWarner Losh 243512e36acbSWarner Losh bwi_init_rxdesc_ring32(sc, rd->rdata_txrx_ctrl, rd->rdata_paddr, 243612e36acbSWarner Losh sizeof(struct bwi_rxbuf_hdr), BWI_RX_NDESC); 243712e36acbSWarner Losh return 0; 243812e36acbSWarner Losh } 243912e36acbSWarner Losh 244012e36acbSWarner Losh static int 244112e36acbSWarner Losh bwi_init_txstats32(struct bwi_softc *sc) 244212e36acbSWarner Losh { 244312e36acbSWarner Losh struct bwi_txstats_data *st = sc->sc_txstats; 244412e36acbSWarner Losh bus_addr_t stats_paddr; 244512e36acbSWarner Losh int i; 244612e36acbSWarner Losh 244712e36acbSWarner Losh bzero(st->stats, BWI_TXSTATS_NDESC * sizeof(struct bwi_txstats)); 244812e36acbSWarner Losh bus_dmamap_sync(st->stats_dtag, st->stats_dmap, BUS_DMASYNC_PREWRITE); 244912e36acbSWarner Losh 245012e36acbSWarner Losh st->stats_idx = 0; 245112e36acbSWarner Losh 245212e36acbSWarner Losh stats_paddr = st->stats_paddr; 245312e36acbSWarner Losh for (i = 0; i < BWI_TXSTATS_NDESC; ++i) { 245412e36acbSWarner Losh bwi_setup_desc32(sc, st->stats_ring, BWI_TXSTATS_NDESC, i, 245512e36acbSWarner Losh stats_paddr, sizeof(struct bwi_txstats), 0); 245612e36acbSWarner Losh stats_paddr += sizeof(struct bwi_txstats); 245712e36acbSWarner Losh } 245812e36acbSWarner Losh bus_dmamap_sync(st->stats_ring_dtag, st->stats_ring_dmap, 245912e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 246012e36acbSWarner Losh 246112e36acbSWarner Losh bwi_init_rxdesc_ring32(sc, st->stats_ctrl_base, 246212e36acbSWarner Losh st->stats_ring_paddr, 0, BWI_TXSTATS_NDESC); 246312e36acbSWarner Losh return 0; 246412e36acbSWarner Losh } 246512e36acbSWarner Losh 246612e36acbSWarner Losh static void 246712e36acbSWarner Losh bwi_setup_rx_desc32(struct bwi_softc *sc, int buf_idx, bus_addr_t paddr, 246812e36acbSWarner Losh int buf_len) 246912e36acbSWarner Losh { 247012e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_rx_rdata; 247112e36acbSWarner Losh 247212e36acbSWarner Losh KASSERT(buf_idx < BWI_RX_NDESC, ("buf_idx %d", buf_idx)); 247312e36acbSWarner Losh bwi_setup_desc32(sc, rd->rdata_desc, BWI_RX_NDESC, buf_idx, 247412e36acbSWarner Losh paddr, buf_len, 0); 247512e36acbSWarner Losh } 247612e36acbSWarner Losh 247712e36acbSWarner Losh static void 247812e36acbSWarner Losh bwi_setup_tx_desc32(struct bwi_softc *sc, struct bwi_ring_data *rd, 247912e36acbSWarner Losh int buf_idx, bus_addr_t paddr, int buf_len) 248012e36acbSWarner Losh { 248112e36acbSWarner Losh KASSERT(buf_idx < BWI_TX_NDESC, ("buf_idx %d", buf_idx)); 248212e36acbSWarner Losh bwi_setup_desc32(sc, rd->rdata_desc, BWI_TX_NDESC, buf_idx, 248312e36acbSWarner Losh paddr, buf_len, 1); 248412e36acbSWarner Losh } 248512e36acbSWarner Losh 248612e36acbSWarner Losh static int 248712e36acbSWarner Losh bwi_init_tx_ring64(struct bwi_softc *sc, int ring_idx) 248812e36acbSWarner Losh { 248912e36acbSWarner Losh /* TODO:64 */ 249012e36acbSWarner Losh return EOPNOTSUPP; 249112e36acbSWarner Losh } 249212e36acbSWarner Losh 249312e36acbSWarner Losh static int 249412e36acbSWarner Losh bwi_init_rx_ring64(struct bwi_softc *sc) 249512e36acbSWarner Losh { 249612e36acbSWarner Losh /* TODO:64 */ 249712e36acbSWarner Losh return EOPNOTSUPP; 249812e36acbSWarner Losh } 249912e36acbSWarner Losh 250012e36acbSWarner Losh static int 250112e36acbSWarner Losh bwi_init_txstats64(struct bwi_softc *sc) 250212e36acbSWarner Losh { 250312e36acbSWarner Losh /* TODO:64 */ 250412e36acbSWarner Losh return EOPNOTSUPP; 250512e36acbSWarner Losh } 250612e36acbSWarner Losh 250712e36acbSWarner Losh static void 250812e36acbSWarner Losh bwi_setup_rx_desc64(struct bwi_softc *sc, int buf_idx, bus_addr_t paddr, 250912e36acbSWarner Losh int buf_len) 251012e36acbSWarner Losh { 251112e36acbSWarner Losh /* TODO:64 */ 251212e36acbSWarner Losh } 251312e36acbSWarner Losh 251412e36acbSWarner Losh static void 251512e36acbSWarner Losh bwi_setup_tx_desc64(struct bwi_softc *sc, struct bwi_ring_data *rd, 251612e36acbSWarner Losh int buf_idx, bus_addr_t paddr, int buf_len) 251712e36acbSWarner Losh { 251812e36acbSWarner Losh /* TODO:64 */ 251912e36acbSWarner Losh } 252012e36acbSWarner Losh 252112e36acbSWarner Losh static void 252212e36acbSWarner Losh bwi_dma_buf_addr(void *arg, bus_dma_segment_t *seg, int nseg, 252312e36acbSWarner Losh bus_size_t mapsz __unused, int error) 252412e36acbSWarner Losh { 252512e36acbSWarner Losh if (!error) { 252612e36acbSWarner Losh KASSERT(nseg == 1, ("too many segments(%d)\n", nseg)); 252712e36acbSWarner Losh *((bus_addr_t *)arg) = seg->ds_addr; 252812e36acbSWarner Losh } 252912e36acbSWarner Losh } 253012e36acbSWarner Losh 253112e36acbSWarner Losh static int 253212e36acbSWarner Losh bwi_newbuf(struct bwi_softc *sc, int buf_idx, int init) 253312e36acbSWarner Losh { 253412e36acbSWarner Losh struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; 253512e36acbSWarner Losh struct bwi_rxbuf *rxbuf = &rbd->rbd_buf[buf_idx]; 253612e36acbSWarner Losh struct bwi_rxbuf_hdr *hdr; 253712e36acbSWarner Losh bus_dmamap_t map; 253812e36acbSWarner Losh bus_addr_t paddr; 253912e36acbSWarner Losh struct mbuf *m; 254012e36acbSWarner Losh int error; 254112e36acbSWarner Losh 254212e36acbSWarner Losh KASSERT(buf_idx < BWI_RX_NDESC, ("buf_idx %d", buf_idx)); 254312e36acbSWarner Losh 2544c6499eccSGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 254512e36acbSWarner Losh if (m == NULL) { 254612e36acbSWarner Losh error = ENOBUFS; 254712e36acbSWarner Losh 254812e36acbSWarner Losh /* 254912e36acbSWarner Losh * If the NIC is up and running, we need to: 255012e36acbSWarner Losh * - Clear RX buffer's header. 255112e36acbSWarner Losh * - Restore RX descriptor settings. 255212e36acbSWarner Losh */ 255312e36acbSWarner Losh if (init) 255412e36acbSWarner Losh return error; 255512e36acbSWarner Losh else 255612e36acbSWarner Losh goto back; 255712e36acbSWarner Losh } 255812e36acbSWarner Losh m->m_len = m->m_pkthdr.len = MCLBYTES; 255912e36acbSWarner Losh 256012e36acbSWarner Losh /* 256112e36acbSWarner Losh * Try to load RX buf into temporary DMA map 256212e36acbSWarner Losh */ 256312e36acbSWarner Losh error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, rbd->rbd_tmp_dmap, m, 256412e36acbSWarner Losh bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); 256512e36acbSWarner Losh if (error) { 256612e36acbSWarner Losh m_freem(m); 256712e36acbSWarner Losh 256812e36acbSWarner Losh /* 256912e36acbSWarner Losh * See the comment above 257012e36acbSWarner Losh */ 257112e36acbSWarner Losh if (init) 257212e36acbSWarner Losh return error; 257312e36acbSWarner Losh else 257412e36acbSWarner Losh goto back; 257512e36acbSWarner Losh } 257612e36acbSWarner Losh 257712e36acbSWarner Losh if (!init) 257812e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, rxbuf->rb_dmap); 257912e36acbSWarner Losh rxbuf->rb_mbuf = m; 258012e36acbSWarner Losh rxbuf->rb_paddr = paddr; 258112e36acbSWarner Losh 258212e36acbSWarner Losh /* 258312e36acbSWarner Losh * Swap RX buf's DMA map with the loaded temporary one 258412e36acbSWarner Losh */ 258512e36acbSWarner Losh map = rxbuf->rb_dmap; 258612e36acbSWarner Losh rxbuf->rb_dmap = rbd->rbd_tmp_dmap; 258712e36acbSWarner Losh rbd->rbd_tmp_dmap = map; 258812e36acbSWarner Losh 258912e36acbSWarner Losh back: 259012e36acbSWarner Losh /* 259112e36acbSWarner Losh * Clear RX buf header 259212e36acbSWarner Losh */ 259312e36acbSWarner Losh hdr = mtod(rxbuf->rb_mbuf, struct bwi_rxbuf_hdr *); 259412e36acbSWarner Losh bzero(hdr, sizeof(*hdr)); 259512e36acbSWarner Losh bus_dmamap_sync(sc->sc_buf_dtag, rxbuf->rb_dmap, BUS_DMASYNC_PREWRITE); 259612e36acbSWarner Losh 259712e36acbSWarner Losh /* 259812e36acbSWarner Losh * Setup RX buf descriptor 259912e36acbSWarner Losh */ 260012e36acbSWarner Losh sc->sc_setup_rxdesc(sc, buf_idx, rxbuf->rb_paddr, 260112e36acbSWarner Losh rxbuf->rb_mbuf->m_len - sizeof(*hdr)); 260212e36acbSWarner Losh return error; 260312e36acbSWarner Losh } 260412e36acbSWarner Losh 260512e36acbSWarner Losh static void 260612e36acbSWarner Losh bwi_set_addr_filter(struct bwi_softc *sc, uint16_t addr_ofs, 260712e36acbSWarner Losh const uint8_t *addr) 260812e36acbSWarner Losh { 260912e36acbSWarner Losh int i; 261012e36acbSWarner Losh 261112e36acbSWarner Losh CSR_WRITE_2(sc, BWI_ADDR_FILTER_CTRL, 261212e36acbSWarner Losh BWI_ADDR_FILTER_CTRL_SET | addr_ofs); 261312e36acbSWarner Losh 261412e36acbSWarner Losh for (i = 0; i < (IEEE80211_ADDR_LEN / 2); ++i) { 261512e36acbSWarner Losh uint16_t addr_val; 261612e36acbSWarner Losh 261712e36acbSWarner Losh addr_val = (uint16_t)addr[i * 2] | 261812e36acbSWarner Losh (((uint16_t)addr[(i * 2) + 1]) << 8); 261912e36acbSWarner Losh CSR_WRITE_2(sc, BWI_ADDR_FILTER_DATA, addr_val); 262012e36acbSWarner Losh } 262112e36acbSWarner Losh } 262212e36acbSWarner Losh 262312e36acbSWarner Losh static int 262412e36acbSWarner Losh bwi_rxeof(struct bwi_softc *sc, int end_idx) 262512e36acbSWarner Losh { 262612e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_rx_rdata; 262712e36acbSWarner Losh struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; 262812e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 262912e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 263012e36acbSWarner Losh int idx, rx_data = 0; 263112e36acbSWarner Losh 263212e36acbSWarner Losh idx = rbd->rbd_idx; 263312e36acbSWarner Losh while (idx != end_idx) { 263412e36acbSWarner Losh struct bwi_rxbuf *rb = &rbd->rbd_buf[idx]; 263512e36acbSWarner Losh struct bwi_rxbuf_hdr *hdr; 263612e36acbSWarner Losh struct ieee80211_frame_min *wh; 263712e36acbSWarner Losh struct ieee80211_node *ni; 263812e36acbSWarner Losh struct mbuf *m; 263979c27adfSAdrian Chadd uint32_t plcp; 264012e36acbSWarner Losh uint16_t flags2; 264112e36acbSWarner Losh int buflen, wh_ofs, hdr_extra, rssi, noise, type, rate; 264212e36acbSWarner Losh 264312e36acbSWarner Losh m = rb->rb_mbuf; 264412e36acbSWarner Losh bus_dmamap_sync(sc->sc_buf_dtag, rb->rb_dmap, 264512e36acbSWarner Losh BUS_DMASYNC_POSTREAD); 264612e36acbSWarner Losh 264712e36acbSWarner Losh if (bwi_newbuf(sc, idx, 0)) { 2648c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 264912e36acbSWarner Losh goto next; 265012e36acbSWarner Losh } 265112e36acbSWarner Losh 265212e36acbSWarner Losh hdr = mtod(m, struct bwi_rxbuf_hdr *); 265312e36acbSWarner Losh flags2 = le16toh(hdr->rxh_flags2); 265412e36acbSWarner Losh 265512e36acbSWarner Losh hdr_extra = 0; 265612e36acbSWarner Losh if (flags2 & BWI_RXH_F2_TYPE2FRAME) 265712e36acbSWarner Losh hdr_extra = 2; 265812e36acbSWarner Losh wh_ofs = hdr_extra + 6; /* XXX magic number */ 265912e36acbSWarner Losh 266012e36acbSWarner Losh buflen = le16toh(hdr->rxh_buflen); 266112e36acbSWarner Losh if (buflen < BWI_FRAME_MIN_LEN(wh_ofs)) { 266212e36acbSWarner Losh if_printf(ifp, "%s: zero length data, hdr_extra %d\n", 266312e36acbSWarner Losh __func__, hdr_extra); 2664c8dfaf38SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 266512e36acbSWarner Losh m_freem(m); 266612e36acbSWarner Losh goto next; 266712e36acbSWarner Losh } 266812e36acbSWarner Losh 266979c27adfSAdrian Chadd bcopy((uint8_t *)(hdr + 1) + hdr_extra, &plcp, sizeof(plcp)); 267012e36acbSWarner Losh rssi = bwi_calc_rssi(sc, hdr); 267112e36acbSWarner Losh noise = bwi_calc_noise(sc); 267212e36acbSWarner Losh 267312e36acbSWarner Losh m->m_pkthdr.rcvif = ifp; 267412e36acbSWarner Losh m->m_len = m->m_pkthdr.len = buflen + sizeof(*hdr); 267512e36acbSWarner Losh m_adj(m, sizeof(*hdr) + wh_ofs); 267612e36acbSWarner Losh 267712e36acbSWarner Losh if (htole16(hdr->rxh_flags1) & BWI_RXH_F1_OFDM) 2678fcd9500fSBernhard Schmidt rate = bwi_plcp2rate(plcp, IEEE80211_T_OFDM); 267912e36acbSWarner Losh else 2680fcd9500fSBernhard Schmidt rate = bwi_plcp2rate(plcp, IEEE80211_T_CCK); 268112e36acbSWarner Losh 268212e36acbSWarner Losh /* RX radio tap */ 26835463c4a4SSam Leffler if (ieee80211_radiotap_active(ic)) 268479c27adfSAdrian Chadd bwi_rx_radiotap(sc, m, hdr, &plcp, rate, rssi, noise); 268512e36acbSWarner Losh 268612e36acbSWarner Losh m_adj(m, -IEEE80211_CRC_LEN); 268712e36acbSWarner Losh 268812e36acbSWarner Losh BWI_UNLOCK(sc); 268912e36acbSWarner Losh 269012e36acbSWarner Losh wh = mtod(m, struct ieee80211_frame_min *); 269112e36acbSWarner Losh ni = ieee80211_find_rxnode(ic, wh); 269212e36acbSWarner Losh if (ni != NULL) { 26935463c4a4SSam Leffler type = ieee80211_input(ni, m, rssi - noise, noise); 269412e36acbSWarner Losh ieee80211_free_node(ni); 269512e36acbSWarner Losh } else 26965463c4a4SSam Leffler type = ieee80211_input_all(ic, m, rssi - noise, noise); 269712e36acbSWarner Losh if (type == IEEE80211_FC0_TYPE_DATA) { 269812e36acbSWarner Losh rx_data = 1; 269912e36acbSWarner Losh sc->sc_rx_rate = rate; 270012e36acbSWarner Losh } 270112e36acbSWarner Losh 270212e36acbSWarner Losh BWI_LOCK(sc); 270312e36acbSWarner Losh next: 270412e36acbSWarner Losh idx = (idx + 1) % BWI_RX_NDESC; 270512e36acbSWarner Losh 270612e36acbSWarner Losh if (sc->sc_flags & BWI_F_STOP) { 270712e36acbSWarner Losh /* 270812e36acbSWarner Losh * Take the fast lane, don't do 270912e36acbSWarner Losh * any damage to softc 271012e36acbSWarner Losh */ 271112e36acbSWarner Losh return -1; 271212e36acbSWarner Losh } 271312e36acbSWarner Losh } 271412e36acbSWarner Losh 271512e36acbSWarner Losh rbd->rbd_idx = idx; 271612e36acbSWarner Losh bus_dmamap_sync(sc->sc_rxring_dtag, rd->rdata_dmap, 271712e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 271812e36acbSWarner Losh 271912e36acbSWarner Losh return rx_data; 272012e36acbSWarner Losh } 272112e36acbSWarner Losh 272212e36acbSWarner Losh static int 272312e36acbSWarner Losh bwi_rxeof32(struct bwi_softc *sc) 272412e36acbSWarner Losh { 272512e36acbSWarner Losh uint32_t val, rx_ctrl; 272612e36acbSWarner Losh int end_idx, rx_data; 272712e36acbSWarner Losh 272812e36acbSWarner Losh rx_ctrl = sc->sc_rx_rdata.rdata_txrx_ctrl; 272912e36acbSWarner Losh 273012e36acbSWarner Losh val = CSR_READ_4(sc, rx_ctrl + BWI_RX32_STATUS); 273112e36acbSWarner Losh end_idx = __SHIFTOUT(val, BWI_RX32_STATUS_INDEX_MASK) / 273212e36acbSWarner Losh sizeof(struct bwi_desc32); 273312e36acbSWarner Losh 273412e36acbSWarner Losh rx_data = bwi_rxeof(sc, end_idx); 273512e36acbSWarner Losh if (rx_data >= 0) { 273612e36acbSWarner Losh CSR_WRITE_4(sc, rx_ctrl + BWI_RX32_INDEX, 273712e36acbSWarner Losh end_idx * sizeof(struct bwi_desc32)); 273812e36acbSWarner Losh } 273912e36acbSWarner Losh return rx_data; 274012e36acbSWarner Losh } 274112e36acbSWarner Losh 274212e36acbSWarner Losh static int 274312e36acbSWarner Losh bwi_rxeof64(struct bwi_softc *sc) 274412e36acbSWarner Losh { 274512e36acbSWarner Losh /* TODO:64 */ 274612e36acbSWarner Losh return 0; 274712e36acbSWarner Losh } 274812e36acbSWarner Losh 274912e36acbSWarner Losh static void 275012e36acbSWarner Losh bwi_reset_rx_ring32(struct bwi_softc *sc, uint32_t rx_ctrl) 275112e36acbSWarner Losh { 275212e36acbSWarner Losh int i; 275312e36acbSWarner Losh 275412e36acbSWarner Losh CSR_WRITE_4(sc, rx_ctrl + BWI_RX32_CTRL, 0); 275512e36acbSWarner Losh 275612e36acbSWarner Losh #define NRETRY 10 275712e36acbSWarner Losh 275812e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 275912e36acbSWarner Losh uint32_t status; 276012e36acbSWarner Losh 276112e36acbSWarner Losh status = CSR_READ_4(sc, rx_ctrl + BWI_RX32_STATUS); 276212e36acbSWarner Losh if (__SHIFTOUT(status, BWI_RX32_STATUS_STATE_MASK) == 276312e36acbSWarner Losh BWI_RX32_STATUS_STATE_DISABLED) 276412e36acbSWarner Losh break; 276512e36acbSWarner Losh 276612e36acbSWarner Losh DELAY(1000); 276712e36acbSWarner Losh } 276812e36acbSWarner Losh if (i == NRETRY) 276912e36acbSWarner Losh device_printf(sc->sc_dev, "reset rx ring timedout\n"); 277012e36acbSWarner Losh 277112e36acbSWarner Losh #undef NRETRY 277212e36acbSWarner Losh 277312e36acbSWarner Losh CSR_WRITE_4(sc, rx_ctrl + BWI_RX32_RINGINFO, 0); 277412e36acbSWarner Losh } 277512e36acbSWarner Losh 277612e36acbSWarner Losh static void 277712e36acbSWarner Losh bwi_free_txstats32(struct bwi_softc *sc) 277812e36acbSWarner Losh { 277912e36acbSWarner Losh bwi_reset_rx_ring32(sc, sc->sc_txstats->stats_ctrl_base); 278012e36acbSWarner Losh } 278112e36acbSWarner Losh 278212e36acbSWarner Losh static void 278312e36acbSWarner Losh bwi_free_rx_ring32(struct bwi_softc *sc) 278412e36acbSWarner Losh { 278512e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_rx_rdata; 278612e36acbSWarner Losh struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; 278712e36acbSWarner Losh int i; 278812e36acbSWarner Losh 278912e36acbSWarner Losh bwi_reset_rx_ring32(sc, rd->rdata_txrx_ctrl); 279012e36acbSWarner Losh 279112e36acbSWarner Losh for (i = 0; i < BWI_RX_NDESC; ++i) { 279212e36acbSWarner Losh struct bwi_rxbuf *rb = &rbd->rbd_buf[i]; 279312e36acbSWarner Losh 279412e36acbSWarner Losh if (rb->rb_mbuf != NULL) { 279512e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, rb->rb_dmap); 279612e36acbSWarner Losh m_freem(rb->rb_mbuf); 279712e36acbSWarner Losh rb->rb_mbuf = NULL; 279812e36acbSWarner Losh } 279912e36acbSWarner Losh } 280012e36acbSWarner Losh } 280112e36acbSWarner Losh 280212e36acbSWarner Losh static void 280312e36acbSWarner Losh bwi_free_tx_ring32(struct bwi_softc *sc, int ring_idx) 280412e36acbSWarner Losh { 280512e36acbSWarner Losh struct bwi_ring_data *rd; 280612e36acbSWarner Losh struct bwi_txbuf_data *tbd; 280712e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 280812e36acbSWarner Losh uint32_t state, val; 280912e36acbSWarner Losh int i; 281012e36acbSWarner Losh 281112e36acbSWarner Losh KASSERT(ring_idx < BWI_TX_NRING, ("ring_idx %d", ring_idx)); 281212e36acbSWarner Losh rd = &sc->sc_tx_rdata[ring_idx]; 281312e36acbSWarner Losh tbd = &sc->sc_tx_bdata[ring_idx]; 281412e36acbSWarner Losh 281512e36acbSWarner Losh #define NRETRY 10 281612e36acbSWarner Losh 281712e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 281812e36acbSWarner Losh val = CSR_READ_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_STATUS); 281912e36acbSWarner Losh state = __SHIFTOUT(val, BWI_TX32_STATUS_STATE_MASK); 282012e36acbSWarner Losh if (state == BWI_TX32_STATUS_STATE_DISABLED || 282112e36acbSWarner Losh state == BWI_TX32_STATUS_STATE_IDLE || 282212e36acbSWarner Losh state == BWI_TX32_STATUS_STATE_STOPPED) 282312e36acbSWarner Losh break; 282412e36acbSWarner Losh 282512e36acbSWarner Losh DELAY(1000); 282612e36acbSWarner Losh } 282712e36acbSWarner Losh if (i == NRETRY) { 282812e36acbSWarner Losh if_printf(ifp, "%s: wait for TX ring(%d) stable timed out\n", 282912e36acbSWarner Losh __func__, ring_idx); 283012e36acbSWarner Losh } 283112e36acbSWarner Losh 283212e36acbSWarner Losh CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_CTRL, 0); 283312e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 283412e36acbSWarner Losh val = CSR_READ_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_STATUS); 283512e36acbSWarner Losh state = __SHIFTOUT(val, BWI_TX32_STATUS_STATE_MASK); 283612e36acbSWarner Losh if (state == BWI_TX32_STATUS_STATE_DISABLED) 283712e36acbSWarner Losh break; 283812e36acbSWarner Losh 283912e36acbSWarner Losh DELAY(1000); 284012e36acbSWarner Losh } 284112e36acbSWarner Losh if (i == NRETRY) 284212e36acbSWarner Losh if_printf(ifp, "%s: reset TX ring (%d) timed out\n", 284312e36acbSWarner Losh __func__, ring_idx); 284412e36acbSWarner Losh 284512e36acbSWarner Losh #undef NRETRY 284612e36acbSWarner Losh 284712e36acbSWarner Losh DELAY(1000); 284812e36acbSWarner Losh 284912e36acbSWarner Losh CSR_WRITE_4(sc, rd->rdata_txrx_ctrl + BWI_TX32_RINGINFO, 0); 285012e36acbSWarner Losh 285112e36acbSWarner Losh for (i = 0; i < BWI_TX_NDESC; ++i) { 285212e36acbSWarner Losh struct bwi_txbuf *tb = &tbd->tbd_buf[i]; 285312e36acbSWarner Losh 285412e36acbSWarner Losh if (tb->tb_mbuf != NULL) { 285512e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, tb->tb_dmap); 285612e36acbSWarner Losh m_freem(tb->tb_mbuf); 285712e36acbSWarner Losh tb->tb_mbuf = NULL; 285812e36acbSWarner Losh } 285912e36acbSWarner Losh if (tb->tb_ni != NULL) { 286012e36acbSWarner Losh ieee80211_free_node(tb->tb_ni); 286112e36acbSWarner Losh tb->tb_ni = NULL; 286212e36acbSWarner Losh } 286312e36acbSWarner Losh } 286412e36acbSWarner Losh } 286512e36acbSWarner Losh 286612e36acbSWarner Losh static void 286712e36acbSWarner Losh bwi_free_txstats64(struct bwi_softc *sc) 286812e36acbSWarner Losh { 286912e36acbSWarner Losh /* TODO:64 */ 287012e36acbSWarner Losh } 287112e36acbSWarner Losh 287212e36acbSWarner Losh static void 287312e36acbSWarner Losh bwi_free_rx_ring64(struct bwi_softc *sc) 287412e36acbSWarner Losh { 287512e36acbSWarner Losh /* TODO:64 */ 287612e36acbSWarner Losh } 287712e36acbSWarner Losh 287812e36acbSWarner Losh static void 287912e36acbSWarner Losh bwi_free_tx_ring64(struct bwi_softc *sc, int ring_idx) 288012e36acbSWarner Losh { 288112e36acbSWarner Losh /* TODO:64 */ 288212e36acbSWarner Losh } 288312e36acbSWarner Losh 288412e36acbSWarner Losh /* XXX does not belong here */ 288512e36acbSWarner Losh #define IEEE80211_OFDM_PLCP_RATE_MASK __BITS(3, 0) 288612e36acbSWarner Losh #define IEEE80211_OFDM_PLCP_LEN_MASK __BITS(16, 5) 288712e36acbSWarner Losh 288812e36acbSWarner Losh static __inline void 288912e36acbSWarner Losh bwi_ofdm_plcp_header(uint32_t *plcp0, int pkt_len, uint8_t rate) 289012e36acbSWarner Losh { 289112e36acbSWarner Losh uint32_t plcp; 289212e36acbSWarner Losh 289312e36acbSWarner Losh plcp = __SHIFTIN(ieee80211_rate2plcp(rate, IEEE80211_T_OFDM), 289412e36acbSWarner Losh IEEE80211_OFDM_PLCP_RATE_MASK) | 289512e36acbSWarner Losh __SHIFTIN(pkt_len, IEEE80211_OFDM_PLCP_LEN_MASK); 289612e36acbSWarner Losh *plcp0 = htole32(plcp); 289712e36acbSWarner Losh } 289812e36acbSWarner Losh 289912e36acbSWarner Losh static __inline void 290012e36acbSWarner Losh bwi_ds_plcp_header(struct ieee80211_ds_plcp_hdr *plcp, int pkt_len, 290112e36acbSWarner Losh uint8_t rate) 290212e36acbSWarner Losh { 290312e36acbSWarner Losh int len, service, pkt_bitlen; 290412e36acbSWarner Losh 290512e36acbSWarner Losh pkt_bitlen = pkt_len * NBBY; 290612e36acbSWarner Losh len = howmany(pkt_bitlen * 2, rate); 290712e36acbSWarner Losh 290812e36acbSWarner Losh service = IEEE80211_PLCP_SERVICE_LOCKED; 290912e36acbSWarner Losh if (rate == (11 * 2)) { 291012e36acbSWarner Losh int pkt_bitlen1; 291112e36acbSWarner Losh 291212e36acbSWarner Losh /* 291312e36acbSWarner Losh * PLCP service field needs to be adjusted, 291412e36acbSWarner Losh * if TX rate is 11Mbytes/s 291512e36acbSWarner Losh */ 291612e36acbSWarner Losh pkt_bitlen1 = len * 11; 291712e36acbSWarner Losh if (pkt_bitlen1 - pkt_bitlen >= NBBY) 291812e36acbSWarner Losh service |= IEEE80211_PLCP_SERVICE_LENEXT7; 291912e36acbSWarner Losh } 292012e36acbSWarner Losh 292112e36acbSWarner Losh plcp->i_signal = ieee80211_rate2plcp(rate, IEEE80211_T_CCK); 292212e36acbSWarner Losh plcp->i_service = service; 292312e36acbSWarner Losh plcp->i_length = htole16(len); 292412e36acbSWarner Losh /* NOTE: do NOT touch i_crc */ 292512e36acbSWarner Losh } 292612e36acbSWarner Losh 292712e36acbSWarner Losh static __inline void 292812e36acbSWarner Losh bwi_plcp_header(const struct ieee80211_rate_table *rt, 292912e36acbSWarner Losh void *plcp, int pkt_len, uint8_t rate) 293012e36acbSWarner Losh { 293112e36acbSWarner Losh enum ieee80211_phytype modtype; 293212e36acbSWarner Losh 293312e36acbSWarner Losh /* 293412e36acbSWarner Losh * Assume caller has zeroed 'plcp' 293512e36acbSWarner Losh */ 293612e36acbSWarner Losh modtype = ieee80211_rate2phytype(rt, rate); 293712e36acbSWarner Losh if (modtype == IEEE80211_T_OFDM) 293812e36acbSWarner Losh bwi_ofdm_plcp_header(plcp, pkt_len, rate); 293912e36acbSWarner Losh else if (modtype == IEEE80211_T_DS) 294012e36acbSWarner Losh bwi_ds_plcp_header(plcp, pkt_len, rate); 294112e36acbSWarner Losh else 294212e36acbSWarner Losh panic("unsupport modulation type %u\n", modtype); 294312e36acbSWarner Losh } 294412e36acbSWarner Losh 294512e36acbSWarner Losh static int 294612e36acbSWarner Losh bwi_encap(struct bwi_softc *sc, int idx, struct mbuf *m, 294712e36acbSWarner Losh struct ieee80211_node *ni) 294812e36acbSWarner Losh { 294912e36acbSWarner Losh struct ieee80211vap *vap = ni->ni_vap; 295012e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 295112e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 295212e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_tx_rdata[BWI_TX_DATA_RING]; 295312e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; 295412e36acbSWarner Losh struct bwi_txbuf *tb = &tbd->tbd_buf[idx]; 295512e36acbSWarner Losh struct bwi_mac *mac; 295612e36acbSWarner Losh struct bwi_txbuf_hdr *hdr; 295712e36acbSWarner Losh struct ieee80211_frame *wh; 295812e36acbSWarner Losh const struct ieee80211_txparam *tp; 295912e36acbSWarner Losh uint8_t rate, rate_fb; 296012e36acbSWarner Losh uint32_t mac_ctrl; 296112e36acbSWarner Losh uint16_t phy_ctrl; 296212e36acbSWarner Losh bus_addr_t paddr; 296312e36acbSWarner Losh int type, ismcast, pkt_len, error, rix; 296412e36acbSWarner Losh #if 0 296512e36acbSWarner Losh const uint8_t *p; 296612e36acbSWarner Losh int i; 296712e36acbSWarner Losh #endif 296812e36acbSWarner Losh 296912e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 297012e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 297112e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 297212e36acbSWarner Losh 297312e36acbSWarner Losh wh = mtod(m, struct ieee80211_frame *); 297412e36acbSWarner Losh type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 297512e36acbSWarner Losh ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 297612e36acbSWarner Losh 297712e36acbSWarner Losh /* Get 802.11 frame len before prepending TX header */ 297812e36acbSWarner Losh pkt_len = m->m_pkthdr.len + IEEE80211_CRC_LEN; 297912e36acbSWarner Losh 298012e36acbSWarner Losh /* 298112e36acbSWarner Losh * Find TX rate 298212e36acbSWarner Losh */ 298312e36acbSWarner Losh tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; 298412e36acbSWarner Losh if (type != IEEE80211_FC0_TYPE_DATA || (m->m_flags & M_EAPOL)) { 298512e36acbSWarner Losh rate = rate_fb = tp->mgmtrate; 298612e36acbSWarner Losh } else if (ismcast) { 298712e36acbSWarner Losh rate = rate_fb = tp->mcastrate; 298812e36acbSWarner Losh } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { 298912e36acbSWarner Losh rate = rate_fb = tp->ucastrate; 299012e36acbSWarner Losh } else { 2991b6108616SRui Paulo rix = ieee80211_ratectl_rate(ni, NULL, pkt_len); 299212e36acbSWarner Losh rate = ni->ni_txrate; 299312e36acbSWarner Losh 299412e36acbSWarner Losh if (rix > 0) { 299512e36acbSWarner Losh rate_fb = ni->ni_rates.rs_rates[rix-1] & 299612e36acbSWarner Losh IEEE80211_RATE_VAL; 299712e36acbSWarner Losh } else { 299812e36acbSWarner Losh rate_fb = rate; 299912e36acbSWarner Losh } 300012e36acbSWarner Losh } 300112e36acbSWarner Losh tb->tb_rate[0] = rate; 300212e36acbSWarner Losh tb->tb_rate[1] = rate_fb; 300312e36acbSWarner Losh sc->sc_tx_rate = rate; 300412e36acbSWarner Losh 300512e36acbSWarner Losh /* 300612e36acbSWarner Losh * TX radio tap 300712e36acbSWarner Losh */ 30085463c4a4SSam Leffler if (ieee80211_radiotap_active_vap(vap)) { 300912e36acbSWarner Losh sc->sc_tx_th.wt_flags = 0; 30105945b5f5SKevin Lo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) 301112e36acbSWarner Losh sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; 301212e36acbSWarner Losh if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_DS && 301312e36acbSWarner Losh (ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 301412e36acbSWarner Losh rate != (1 * 2)) { 301512e36acbSWarner Losh sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 301612e36acbSWarner Losh } 301712e36acbSWarner Losh sc->sc_tx_th.wt_rate = rate; 301812e36acbSWarner Losh 30195463c4a4SSam Leffler ieee80211_radiotap_tx(vap, m); 302012e36acbSWarner Losh } 302112e36acbSWarner Losh 302212e36acbSWarner Losh /* 302312e36acbSWarner Losh * Setup the embedded TX header 302412e36acbSWarner Losh */ 3025c6499eccSGleb Smirnoff M_PREPEND(m, sizeof(*hdr), M_NOWAIT); 302612e36acbSWarner Losh if (m == NULL) { 302712e36acbSWarner Losh if_printf(ifp, "%s: prepend TX header failed\n", __func__); 302812e36acbSWarner Losh return ENOBUFS; 302912e36acbSWarner Losh } 303012e36acbSWarner Losh hdr = mtod(m, struct bwi_txbuf_hdr *); 303112e36acbSWarner Losh 303212e36acbSWarner Losh bzero(hdr, sizeof(*hdr)); 303312e36acbSWarner Losh 303412e36acbSWarner Losh bcopy(wh->i_fc, hdr->txh_fc, sizeof(hdr->txh_fc)); 303512e36acbSWarner Losh bcopy(wh->i_addr1, hdr->txh_addr1, sizeof(hdr->txh_addr1)); 303612e36acbSWarner Losh 303712e36acbSWarner Losh if (!ismcast) { 303812e36acbSWarner Losh uint16_t dur; 303912e36acbSWarner Losh 304012e36acbSWarner Losh dur = ieee80211_ack_duration(sc->sc_rates, rate, 304112e36acbSWarner Losh ic->ic_flags & ~IEEE80211_F_SHPREAMBLE); 304212e36acbSWarner Losh 304312e36acbSWarner Losh hdr->txh_fb_duration = htole16(dur); 304412e36acbSWarner Losh } 304512e36acbSWarner Losh 304612e36acbSWarner Losh hdr->txh_id = __SHIFTIN(BWI_TX_DATA_RING, BWI_TXH_ID_RING_MASK) | 304712e36acbSWarner Losh __SHIFTIN(idx, BWI_TXH_ID_IDX_MASK); 304812e36acbSWarner Losh 304912e36acbSWarner Losh bwi_plcp_header(sc->sc_rates, hdr->txh_plcp, pkt_len, rate); 305012e36acbSWarner Losh bwi_plcp_header(sc->sc_rates, hdr->txh_fb_plcp, pkt_len, rate_fb); 305112e36acbSWarner Losh 305212e36acbSWarner Losh phy_ctrl = __SHIFTIN(mac->mac_rf.rf_ant_mode, 305312e36acbSWarner Losh BWI_TXH_PHY_C_ANTMODE_MASK); 305412e36acbSWarner Losh if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) 305512e36acbSWarner Losh phy_ctrl |= BWI_TXH_PHY_C_OFDM; 305612e36acbSWarner Losh else if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && rate != (2 * 1)) 305712e36acbSWarner Losh phy_ctrl |= BWI_TXH_PHY_C_SHPREAMBLE; 305812e36acbSWarner Losh 305912e36acbSWarner Losh mac_ctrl = BWI_TXH_MAC_C_HWSEQ | BWI_TXH_MAC_C_FIRST_FRAG; 306012e36acbSWarner Losh if (!ismcast) 306112e36acbSWarner Losh mac_ctrl |= BWI_TXH_MAC_C_ACK; 306212e36acbSWarner Losh if (ieee80211_rate2phytype(sc->sc_rates, rate_fb) == IEEE80211_T_OFDM) 306312e36acbSWarner Losh mac_ctrl |= BWI_TXH_MAC_C_FB_OFDM; 306412e36acbSWarner Losh 306512e36acbSWarner Losh hdr->txh_mac_ctrl = htole32(mac_ctrl); 306612e36acbSWarner Losh hdr->txh_phy_ctrl = htole16(phy_ctrl); 306712e36acbSWarner Losh 306812e36acbSWarner Losh /* Catch any further usage */ 306912e36acbSWarner Losh hdr = NULL; 307012e36acbSWarner Losh wh = NULL; 307112e36acbSWarner Losh 307212e36acbSWarner Losh /* DMA load */ 307312e36acbSWarner Losh error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, 307412e36acbSWarner Losh bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); 307512e36acbSWarner Losh if (error && error != EFBIG) { 307612e36acbSWarner Losh if_printf(ifp, "%s: can't load TX buffer (1) %d\n", 307712e36acbSWarner Losh __func__, error); 307812e36acbSWarner Losh goto back; 307912e36acbSWarner Losh } 308012e36acbSWarner Losh 308112e36acbSWarner Losh if (error) { /* error == EFBIG */ 308212e36acbSWarner Losh struct mbuf *m_new; 308312e36acbSWarner Losh 3084c6499eccSGleb Smirnoff m_new = m_defrag(m, M_NOWAIT); 308512e36acbSWarner Losh if (m_new == NULL) { 308612e36acbSWarner Losh if_printf(ifp, "%s: can't defrag TX buffer\n", 308712e36acbSWarner Losh __func__); 308812e36acbSWarner Losh error = ENOBUFS; 308912e36acbSWarner Losh goto back; 309012e36acbSWarner Losh } else { 309112e36acbSWarner Losh m = m_new; 309212e36acbSWarner Losh } 309312e36acbSWarner Losh 309412e36acbSWarner Losh error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, 309512e36acbSWarner Losh bwi_dma_buf_addr, &paddr, 309612e36acbSWarner Losh BUS_DMA_NOWAIT); 309712e36acbSWarner Losh if (error) { 309812e36acbSWarner Losh if_printf(ifp, "%s: can't load TX buffer (2) %d\n", 309912e36acbSWarner Losh __func__, error); 310012e36acbSWarner Losh goto back; 310112e36acbSWarner Losh } 310212e36acbSWarner Losh } 310312e36acbSWarner Losh error = 0; 310412e36acbSWarner Losh 310512e36acbSWarner Losh bus_dmamap_sync(sc->sc_buf_dtag, tb->tb_dmap, BUS_DMASYNC_PREWRITE); 310612e36acbSWarner Losh 310712e36acbSWarner Losh tb->tb_mbuf = m; 310812e36acbSWarner Losh tb->tb_ni = ni; 310912e36acbSWarner Losh 311012e36acbSWarner Losh #if 0 311112e36acbSWarner Losh p = mtod(m, const uint8_t *); 311212e36acbSWarner Losh for (i = 0; i < m->m_pkthdr.len; ++i) { 311312e36acbSWarner Losh if (i != 0 && i % 8 == 0) 311412e36acbSWarner Losh printf("\n"); 311512e36acbSWarner Losh printf("%02x ", p[i]); 311612e36acbSWarner Losh } 311712e36acbSWarner Losh printf("\n"); 311812e36acbSWarner Losh #endif 311912e36acbSWarner Losh DPRINTF(sc, BWI_DBG_TX, "idx %d, pkt_len %d, buflen %d\n", 312012e36acbSWarner Losh idx, pkt_len, m->m_pkthdr.len); 312112e36acbSWarner Losh 312212e36acbSWarner Losh /* Setup TX descriptor */ 312312e36acbSWarner Losh sc->sc_setup_txdesc(sc, rd, idx, paddr, m->m_pkthdr.len); 312412e36acbSWarner Losh bus_dmamap_sync(sc->sc_txring_dtag, rd->rdata_dmap, 312512e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 312612e36acbSWarner Losh 312712e36acbSWarner Losh /* Kick start */ 312812e36acbSWarner Losh sc->sc_start_tx(sc, rd->rdata_txrx_ctrl, idx); 312912e36acbSWarner Losh 313012e36acbSWarner Losh back: 313112e36acbSWarner Losh if (error) 313212e36acbSWarner Losh m_freem(m); 313312e36acbSWarner Losh return error; 313412e36acbSWarner Losh } 313512e36acbSWarner Losh 313612e36acbSWarner Losh static int 313712e36acbSWarner Losh bwi_encap_raw(struct bwi_softc *sc, int idx, struct mbuf *m, 313812e36acbSWarner Losh struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) 313912e36acbSWarner Losh { 314012e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 31415463c4a4SSam Leffler struct ieee80211vap *vap = ni->ni_vap; 3142515db61dSSam Leffler struct ieee80211com *ic = ni->ni_ic; 314312e36acbSWarner Losh struct bwi_ring_data *rd = &sc->sc_tx_rdata[BWI_TX_DATA_RING]; 314412e36acbSWarner Losh struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; 314512e36acbSWarner Losh struct bwi_txbuf *tb = &tbd->tbd_buf[idx]; 314612e36acbSWarner Losh struct bwi_mac *mac; 314712e36acbSWarner Losh struct bwi_txbuf_hdr *hdr; 314812e36acbSWarner Losh struct ieee80211_frame *wh; 314912e36acbSWarner Losh uint8_t rate, rate_fb; 315012e36acbSWarner Losh uint32_t mac_ctrl; 315112e36acbSWarner Losh uint16_t phy_ctrl; 315212e36acbSWarner Losh bus_addr_t paddr; 315312e36acbSWarner Losh int ismcast, pkt_len, error; 315412e36acbSWarner Losh 315512e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 315612e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 315712e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 315812e36acbSWarner Losh 315912e36acbSWarner Losh wh = mtod(m, struct ieee80211_frame *); 316012e36acbSWarner Losh ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); 316112e36acbSWarner Losh 316212e36acbSWarner Losh /* Get 802.11 frame len before prepending TX header */ 316312e36acbSWarner Losh pkt_len = m->m_pkthdr.len + IEEE80211_CRC_LEN; 316412e36acbSWarner Losh 316512e36acbSWarner Losh /* 316612e36acbSWarner Losh * Find TX rate 316712e36acbSWarner Losh */ 316812e36acbSWarner Losh rate = params->ibp_rate0; 3169515db61dSSam Leffler if (!ieee80211_isratevalid(ic->ic_rt, rate)) { 3170515db61dSSam Leffler /* XXX fall back to mcast/mgmt rate? */ 3171c5f5009bSSam Leffler m_freem(m); 3172515db61dSSam Leffler return EINVAL; 3173515db61dSSam Leffler } 3174515db61dSSam Leffler if (params->ibp_try1 != 0) { 3175515db61dSSam Leffler rate_fb = params->ibp_rate1; 3176515db61dSSam Leffler if (!ieee80211_isratevalid(ic->ic_rt, rate_fb)) { 3177515db61dSSam Leffler /* XXX fall back to rate0? */ 3178c5f5009bSSam Leffler m_freem(m); 3179515db61dSSam Leffler return EINVAL; 3180515db61dSSam Leffler } 3181515db61dSSam Leffler } else 3182515db61dSSam Leffler rate_fb = rate; 318312e36acbSWarner Losh tb->tb_rate[0] = rate; 318412e36acbSWarner Losh tb->tb_rate[1] = rate_fb; 318512e36acbSWarner Losh sc->sc_tx_rate = rate; 318612e36acbSWarner Losh 318712e36acbSWarner Losh /* 318812e36acbSWarner Losh * TX radio tap 318912e36acbSWarner Losh */ 31905463c4a4SSam Leffler if (ieee80211_radiotap_active_vap(vap)) { 319112e36acbSWarner Losh sc->sc_tx_th.wt_flags = 0; 319212e36acbSWarner Losh /* XXX IEEE80211_BPF_CRYPTO */ 31935945b5f5SKevin Lo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) 319412e36acbSWarner Losh sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP; 319512e36acbSWarner Losh if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 319612e36acbSWarner Losh sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 319712e36acbSWarner Losh sc->sc_tx_th.wt_rate = rate; 319812e36acbSWarner Losh 31995463c4a4SSam Leffler ieee80211_radiotap_tx(vap, m); 320012e36acbSWarner Losh } 320112e36acbSWarner Losh 320212e36acbSWarner Losh /* 320312e36acbSWarner Losh * Setup the embedded TX header 320412e36acbSWarner Losh */ 3205c6499eccSGleb Smirnoff M_PREPEND(m, sizeof(*hdr), M_NOWAIT); 320612e36acbSWarner Losh if (m == NULL) { 320712e36acbSWarner Losh if_printf(ifp, "%s: prepend TX header failed\n", __func__); 320812e36acbSWarner Losh return ENOBUFS; 320912e36acbSWarner Losh } 321012e36acbSWarner Losh hdr = mtod(m, struct bwi_txbuf_hdr *); 321112e36acbSWarner Losh 321212e36acbSWarner Losh bzero(hdr, sizeof(*hdr)); 321312e36acbSWarner Losh 321412e36acbSWarner Losh bcopy(wh->i_fc, hdr->txh_fc, sizeof(hdr->txh_fc)); 321512e36acbSWarner Losh bcopy(wh->i_addr1, hdr->txh_addr1, sizeof(hdr->txh_addr1)); 321612e36acbSWarner Losh 321712e36acbSWarner Losh mac_ctrl = BWI_TXH_MAC_C_HWSEQ | BWI_TXH_MAC_C_FIRST_FRAG; 321812e36acbSWarner Losh if (!ismcast && (params->ibp_flags & IEEE80211_BPF_NOACK) == 0) { 321912e36acbSWarner Losh uint16_t dur; 322012e36acbSWarner Losh 322112e36acbSWarner Losh dur = ieee80211_ack_duration(sc->sc_rates, rate_fb, 0); 322212e36acbSWarner Losh 322312e36acbSWarner Losh hdr->txh_fb_duration = htole16(dur); 322412e36acbSWarner Losh mac_ctrl |= BWI_TXH_MAC_C_ACK; 322512e36acbSWarner Losh } 322612e36acbSWarner Losh 322712e36acbSWarner Losh hdr->txh_id = __SHIFTIN(BWI_TX_DATA_RING, BWI_TXH_ID_RING_MASK) | 322812e36acbSWarner Losh __SHIFTIN(idx, BWI_TXH_ID_IDX_MASK); 322912e36acbSWarner Losh 323012e36acbSWarner Losh bwi_plcp_header(sc->sc_rates, hdr->txh_plcp, pkt_len, rate); 323112e36acbSWarner Losh bwi_plcp_header(sc->sc_rates, hdr->txh_fb_plcp, pkt_len, rate_fb); 323212e36acbSWarner Losh 323312e36acbSWarner Losh phy_ctrl = __SHIFTIN(mac->mac_rf.rf_ant_mode, 323412e36acbSWarner Losh BWI_TXH_PHY_C_ANTMODE_MASK); 323512e36acbSWarner Losh if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) { 323612e36acbSWarner Losh phy_ctrl |= BWI_TXH_PHY_C_OFDM; 323712e36acbSWarner Losh mac_ctrl |= BWI_TXH_MAC_C_FB_OFDM; 323812e36acbSWarner Losh } else if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) 323912e36acbSWarner Losh phy_ctrl |= BWI_TXH_PHY_C_SHPREAMBLE; 324012e36acbSWarner Losh 324112e36acbSWarner Losh hdr->txh_mac_ctrl = htole32(mac_ctrl); 324212e36acbSWarner Losh hdr->txh_phy_ctrl = htole16(phy_ctrl); 324312e36acbSWarner Losh 324412e36acbSWarner Losh /* Catch any further usage */ 324512e36acbSWarner Losh hdr = NULL; 324612e36acbSWarner Losh wh = NULL; 324712e36acbSWarner Losh 324812e36acbSWarner Losh /* DMA load */ 324912e36acbSWarner Losh error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, 325012e36acbSWarner Losh bwi_dma_buf_addr, &paddr, BUS_DMA_NOWAIT); 325112e36acbSWarner Losh if (error != 0) { 325212e36acbSWarner Losh struct mbuf *m_new; 325312e36acbSWarner Losh 325412e36acbSWarner Losh if (error != EFBIG) { 325512e36acbSWarner Losh if_printf(ifp, "%s: can't load TX buffer (1) %d\n", 325612e36acbSWarner Losh __func__, error); 325712e36acbSWarner Losh goto back; 325812e36acbSWarner Losh } 3259c6499eccSGleb Smirnoff m_new = m_defrag(m, M_NOWAIT); 326012e36acbSWarner Losh if (m_new == NULL) { 326112e36acbSWarner Losh if_printf(ifp, "%s: can't defrag TX buffer\n", 326212e36acbSWarner Losh __func__); 326312e36acbSWarner Losh error = ENOBUFS; 326412e36acbSWarner Losh goto back; 326512e36acbSWarner Losh } 326612e36acbSWarner Losh m = m_new; 326712e36acbSWarner Losh error = bus_dmamap_load_mbuf(sc->sc_buf_dtag, tb->tb_dmap, m, 326812e36acbSWarner Losh bwi_dma_buf_addr, &paddr, 326912e36acbSWarner Losh BUS_DMA_NOWAIT); 327012e36acbSWarner Losh if (error) { 327112e36acbSWarner Losh if_printf(ifp, "%s: can't load TX buffer (2) %d\n", 327212e36acbSWarner Losh __func__, error); 327312e36acbSWarner Losh goto back; 327412e36acbSWarner Losh } 327512e36acbSWarner Losh } 327612e36acbSWarner Losh 327712e36acbSWarner Losh bus_dmamap_sync(sc->sc_buf_dtag, tb->tb_dmap, BUS_DMASYNC_PREWRITE); 327812e36acbSWarner Losh 327912e36acbSWarner Losh tb->tb_mbuf = m; 328012e36acbSWarner Losh tb->tb_ni = ni; 328112e36acbSWarner Losh 328212e36acbSWarner Losh DPRINTF(sc, BWI_DBG_TX, "idx %d, pkt_len %d, buflen %d\n", 328312e36acbSWarner Losh idx, pkt_len, m->m_pkthdr.len); 328412e36acbSWarner Losh 328512e36acbSWarner Losh /* Setup TX descriptor */ 328612e36acbSWarner Losh sc->sc_setup_txdesc(sc, rd, idx, paddr, m->m_pkthdr.len); 328712e36acbSWarner Losh bus_dmamap_sync(sc->sc_txring_dtag, rd->rdata_dmap, 328812e36acbSWarner Losh BUS_DMASYNC_PREWRITE); 328912e36acbSWarner Losh 329012e36acbSWarner Losh /* Kick start */ 329112e36acbSWarner Losh sc->sc_start_tx(sc, rd->rdata_txrx_ctrl, idx); 329212e36acbSWarner Losh back: 329312e36acbSWarner Losh if (error) 329412e36acbSWarner Losh m_freem(m); 329512e36acbSWarner Losh return error; 329612e36acbSWarner Losh } 329712e36acbSWarner Losh 329812e36acbSWarner Losh static void 329912e36acbSWarner Losh bwi_start_tx32(struct bwi_softc *sc, uint32_t tx_ctrl, int idx) 330012e36acbSWarner Losh { 330112e36acbSWarner Losh idx = (idx + 1) % BWI_TX_NDESC; 330212e36acbSWarner Losh CSR_WRITE_4(sc, tx_ctrl + BWI_TX32_INDEX, 330312e36acbSWarner Losh idx * sizeof(struct bwi_desc32)); 330412e36acbSWarner Losh } 330512e36acbSWarner Losh 330612e36acbSWarner Losh static void 330712e36acbSWarner Losh bwi_start_tx64(struct bwi_softc *sc, uint32_t tx_ctrl, int idx) 330812e36acbSWarner Losh { 330912e36acbSWarner Losh /* TODO:64 */ 331012e36acbSWarner Losh } 331112e36acbSWarner Losh 331212e36acbSWarner Losh static void 331312e36acbSWarner Losh bwi_txeof_status32(struct bwi_softc *sc) 331412e36acbSWarner Losh { 331512e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 331612e36acbSWarner Losh uint32_t val, ctrl_base; 331712e36acbSWarner Losh int end_idx; 331812e36acbSWarner Losh 331912e36acbSWarner Losh ctrl_base = sc->sc_txstats->stats_ctrl_base; 332012e36acbSWarner Losh 332112e36acbSWarner Losh val = CSR_READ_4(sc, ctrl_base + BWI_RX32_STATUS); 332212e36acbSWarner Losh end_idx = __SHIFTOUT(val, BWI_RX32_STATUS_INDEX_MASK) / 332312e36acbSWarner Losh sizeof(struct bwi_desc32); 332412e36acbSWarner Losh 332512e36acbSWarner Losh bwi_txeof_status(sc, end_idx); 332612e36acbSWarner Losh 332712e36acbSWarner Losh CSR_WRITE_4(sc, ctrl_base + BWI_RX32_INDEX, 332812e36acbSWarner Losh end_idx * sizeof(struct bwi_desc32)); 332912e36acbSWarner Losh 333012e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) 333112e36acbSWarner Losh ifp->if_start(ifp); 333212e36acbSWarner Losh } 333312e36acbSWarner Losh 333412e36acbSWarner Losh static void 333512e36acbSWarner Losh bwi_txeof_status64(struct bwi_softc *sc) 333612e36acbSWarner Losh { 333712e36acbSWarner Losh /* TODO:64 */ 333812e36acbSWarner Losh } 333912e36acbSWarner Losh 334012e36acbSWarner Losh static void 334112e36acbSWarner Losh _bwi_txeof(struct bwi_softc *sc, uint16_t tx_id, int acked, int data_txcnt) 334212e36acbSWarner Losh { 334312e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 334412e36acbSWarner Losh struct bwi_txbuf_data *tbd; 334512e36acbSWarner Losh struct bwi_txbuf *tb; 334612e36acbSWarner Losh int ring_idx, buf_idx; 334712e36acbSWarner Losh struct ieee80211_node *ni; 3348b6108616SRui Paulo struct ieee80211vap *vap; 334912e36acbSWarner Losh 335012e36acbSWarner Losh if (tx_id == 0) { 335112e36acbSWarner Losh if_printf(ifp, "%s: zero tx id\n", __func__); 335212e36acbSWarner Losh return; 335312e36acbSWarner Losh } 335412e36acbSWarner Losh 335512e36acbSWarner Losh ring_idx = __SHIFTOUT(tx_id, BWI_TXH_ID_RING_MASK); 335612e36acbSWarner Losh buf_idx = __SHIFTOUT(tx_id, BWI_TXH_ID_IDX_MASK); 335712e36acbSWarner Losh 335812e36acbSWarner Losh KASSERT(ring_idx == BWI_TX_DATA_RING, ("ring_idx %d", ring_idx)); 335912e36acbSWarner Losh KASSERT(buf_idx < BWI_TX_NDESC, ("buf_idx %d", buf_idx)); 336012e36acbSWarner Losh 336112e36acbSWarner Losh tbd = &sc->sc_tx_bdata[ring_idx]; 336212e36acbSWarner Losh KASSERT(tbd->tbd_used > 0, ("tbd_used %d", tbd->tbd_used)); 336312e36acbSWarner Losh tbd->tbd_used--; 336412e36acbSWarner Losh 336512e36acbSWarner Losh tb = &tbd->tbd_buf[buf_idx]; 336612e36acbSWarner Losh DPRINTF(sc, BWI_DBG_TXEOF, "txeof idx %d, " 336712e36acbSWarner Losh "acked %d, data_txcnt %d, ni %p\n", 336812e36acbSWarner Losh buf_idx, acked, data_txcnt, tb->tb_ni); 336912e36acbSWarner Losh 337012e36acbSWarner Losh bus_dmamap_unload(sc->sc_buf_dtag, tb->tb_dmap); 337112e36acbSWarner Losh 337212e36acbSWarner Losh ni = tb->tb_ni; 337312e36acbSWarner Losh if (tb->tb_ni != NULL) { 337412e36acbSWarner Losh const struct bwi_txbuf_hdr *hdr = 337512e36acbSWarner Losh mtod(tb->tb_mbuf, const struct bwi_txbuf_hdr *); 33764a67d9e8SRui Paulo vap = ni->ni_vap; 337712e36acbSWarner Losh 337812e36acbSWarner Losh /* NB: update rate control only for unicast frames */ 337912e36acbSWarner Losh if (hdr->txh_mac_ctrl & htole32(BWI_TXH_MAC_C_ACK)) { 338012e36acbSWarner Losh /* 338112e36acbSWarner Losh * Feed back 'acked and data_txcnt'. Note that the 338212e36acbSWarner Losh * generic AMRR code only understands one tx rate 338312e36acbSWarner Losh * and the estimator doesn't handle real retry counts 338412e36acbSWarner Losh * well so to avoid over-aggressive downshifting we 338512e36acbSWarner Losh * treat any number of retries as "1". 338612e36acbSWarner Losh */ 3387b6108616SRui Paulo ieee80211_ratectl_tx_complete(vap, ni, 3388b6108616SRui Paulo (data_txcnt > 1) ? IEEE80211_RATECTL_TX_SUCCESS : 3389b6108616SRui Paulo IEEE80211_RATECTL_TX_FAILURE, &acked, NULL); 339012e36acbSWarner Losh } 339112e36acbSWarner Losh 339212e36acbSWarner Losh /* 339312e36acbSWarner Losh * Do any tx complete callback. Note this must 339412e36acbSWarner Losh * be done before releasing the node reference. 339512e36acbSWarner Losh */ 339612e36acbSWarner Losh if (tb->tb_mbuf->m_flags & M_TXCB) 339712e36acbSWarner Losh ieee80211_process_callback(ni, tb->tb_mbuf, !acked); 339812e36acbSWarner Losh 339912e36acbSWarner Losh ieee80211_free_node(tb->tb_ni); 340012e36acbSWarner Losh tb->tb_ni = NULL; 340112e36acbSWarner Losh } 340212e36acbSWarner Losh m_freem(tb->tb_mbuf); 340312e36acbSWarner Losh tb->tb_mbuf = NULL; 340412e36acbSWarner Losh 340512e36acbSWarner Losh if (tbd->tbd_used == 0) 340658fbe5abSJohn Baldwin sc->sc_tx_timer = 0; 340712e36acbSWarner Losh 340812e36acbSWarner Losh ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 340912e36acbSWarner Losh } 341012e36acbSWarner Losh 341112e36acbSWarner Losh static void 341212e36acbSWarner Losh bwi_txeof_status(struct bwi_softc *sc, int end_idx) 341312e36acbSWarner Losh { 341412e36acbSWarner Losh struct bwi_txstats_data *st = sc->sc_txstats; 341512e36acbSWarner Losh int idx; 341612e36acbSWarner Losh 341712e36acbSWarner Losh bus_dmamap_sync(st->stats_dtag, st->stats_dmap, BUS_DMASYNC_POSTREAD); 341812e36acbSWarner Losh 341912e36acbSWarner Losh idx = st->stats_idx; 342012e36acbSWarner Losh while (idx != end_idx) { 342112e36acbSWarner Losh const struct bwi_txstats *stats = &st->stats[idx]; 342212e36acbSWarner Losh 342312e36acbSWarner Losh if ((stats->txs_flags & BWI_TXS_F_PENDING) == 0) { 342412e36acbSWarner Losh int data_txcnt; 342512e36acbSWarner Losh 342612e36acbSWarner Losh data_txcnt = __SHIFTOUT(stats->txs_txcnt, 342712e36acbSWarner Losh BWI_TXS_TXCNT_DATA); 342812e36acbSWarner Losh _bwi_txeof(sc, le16toh(stats->txs_id), 342912e36acbSWarner Losh stats->txs_flags & BWI_TXS_F_ACKED, 343012e36acbSWarner Losh data_txcnt); 343112e36acbSWarner Losh } 343212e36acbSWarner Losh idx = (idx + 1) % BWI_TXSTATS_NDESC; 343312e36acbSWarner Losh } 343412e36acbSWarner Losh st->stats_idx = idx; 343512e36acbSWarner Losh } 343612e36acbSWarner Losh 343712e36acbSWarner Losh static void 343812e36acbSWarner Losh bwi_txeof(struct bwi_softc *sc) 343912e36acbSWarner Losh { 344012e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 344112e36acbSWarner Losh 344212e36acbSWarner Losh for (;;) { 344312e36acbSWarner Losh uint32_t tx_status0, tx_status1; 344412e36acbSWarner Losh uint16_t tx_id; 344512e36acbSWarner Losh int data_txcnt; 344612e36acbSWarner Losh 344712e36acbSWarner Losh tx_status0 = CSR_READ_4(sc, BWI_TXSTATUS0); 344812e36acbSWarner Losh if ((tx_status0 & BWI_TXSTATUS0_VALID) == 0) 344912e36acbSWarner Losh break; 345012e36acbSWarner Losh tx_status1 = CSR_READ_4(sc, BWI_TXSTATUS1); 345112e36acbSWarner Losh 345212e36acbSWarner Losh tx_id = __SHIFTOUT(tx_status0, BWI_TXSTATUS0_TXID_MASK); 345312e36acbSWarner Losh data_txcnt = __SHIFTOUT(tx_status0, 345412e36acbSWarner Losh BWI_TXSTATUS0_DATA_TXCNT_MASK); 345512e36acbSWarner Losh 345612e36acbSWarner Losh if (tx_status0 & (BWI_TXSTATUS0_AMPDU | BWI_TXSTATUS0_PENDING)) 345712e36acbSWarner Losh continue; 345812e36acbSWarner Losh 345912e36acbSWarner Losh _bwi_txeof(sc, le16toh(tx_id), tx_status0 & BWI_TXSTATUS0_ACKED, 346012e36acbSWarner Losh data_txcnt); 346112e36acbSWarner Losh } 346212e36acbSWarner Losh 346312e36acbSWarner Losh if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) 346412e36acbSWarner Losh ifp->if_start(ifp); 346512e36acbSWarner Losh } 346612e36acbSWarner Losh 346712e36acbSWarner Losh static int 346812e36acbSWarner Losh bwi_bbp_power_on(struct bwi_softc *sc, enum bwi_clock_mode clk_mode) 346912e36acbSWarner Losh { 347012e36acbSWarner Losh bwi_power_on(sc, 1); 347112e36acbSWarner Losh return bwi_set_clock_mode(sc, clk_mode); 347212e36acbSWarner Losh } 347312e36acbSWarner Losh 347412e36acbSWarner Losh static void 347512e36acbSWarner Losh bwi_bbp_power_off(struct bwi_softc *sc) 347612e36acbSWarner Losh { 347712e36acbSWarner Losh bwi_set_clock_mode(sc, BWI_CLOCK_MODE_SLOW); 347812e36acbSWarner Losh bwi_power_off(sc, 1); 347912e36acbSWarner Losh } 348012e36acbSWarner Losh 348112e36acbSWarner Losh static int 348212e36acbSWarner Losh bwi_get_pwron_delay(struct bwi_softc *sc) 348312e36acbSWarner Losh { 348412e36acbSWarner Losh struct bwi_regwin *com, *old; 348512e36acbSWarner Losh struct bwi_clock_freq freq; 348612e36acbSWarner Losh uint32_t val; 348712e36acbSWarner Losh int error; 348812e36acbSWarner Losh 348912e36acbSWarner Losh com = &sc->sc_com_regwin; 349012e36acbSWarner Losh KASSERT(BWI_REGWIN_EXIST(com), ("no regwin")); 349112e36acbSWarner Losh 349212e36acbSWarner Losh if ((sc->sc_cap & BWI_CAP_CLKMODE) == 0) 349312e36acbSWarner Losh return 0; 349412e36acbSWarner Losh 349512e36acbSWarner Losh error = bwi_regwin_switch(sc, com, &old); 349612e36acbSWarner Losh if (error) 349712e36acbSWarner Losh return error; 349812e36acbSWarner Losh 349912e36acbSWarner Losh bwi_get_clock_freq(sc, &freq); 350012e36acbSWarner Losh 350112e36acbSWarner Losh val = CSR_READ_4(sc, BWI_PLL_ON_DELAY); 350212e36acbSWarner Losh sc->sc_pwron_delay = howmany((val + 2) * 1000000, freq.clkfreq_min); 350312e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH, "power on delay %u\n", sc->sc_pwron_delay); 350412e36acbSWarner Losh 350512e36acbSWarner Losh return bwi_regwin_switch(sc, old, NULL); 350612e36acbSWarner Losh } 350712e36acbSWarner Losh 350812e36acbSWarner Losh static int 350912e36acbSWarner Losh bwi_bus_attach(struct bwi_softc *sc) 351012e36acbSWarner Losh { 351112e36acbSWarner Losh struct bwi_regwin *bus, *old; 351212e36acbSWarner Losh int error; 351312e36acbSWarner Losh 351412e36acbSWarner Losh bus = &sc->sc_bus_regwin; 351512e36acbSWarner Losh 351612e36acbSWarner Losh error = bwi_regwin_switch(sc, bus, &old); 351712e36acbSWarner Losh if (error) 351812e36acbSWarner Losh return error; 351912e36acbSWarner Losh 352012e36acbSWarner Losh if (!bwi_regwin_is_enabled(sc, bus)) 352112e36acbSWarner Losh bwi_regwin_enable(sc, bus, 0); 352212e36acbSWarner Losh 352312e36acbSWarner Losh /* Disable interripts */ 352412e36acbSWarner Losh CSR_WRITE_4(sc, BWI_INTRVEC, 0); 352512e36acbSWarner Losh 352612e36acbSWarner Losh return bwi_regwin_switch(sc, old, NULL); 352712e36acbSWarner Losh } 352812e36acbSWarner Losh 352912e36acbSWarner Losh static const char * 353012e36acbSWarner Losh bwi_regwin_name(const struct bwi_regwin *rw) 353112e36acbSWarner Losh { 353212e36acbSWarner Losh switch (rw->rw_type) { 353312e36acbSWarner Losh case BWI_REGWIN_T_COM: 353412e36acbSWarner Losh return "COM"; 353512e36acbSWarner Losh case BWI_REGWIN_T_BUSPCI: 353612e36acbSWarner Losh return "PCI"; 353712e36acbSWarner Losh case BWI_REGWIN_T_MAC: 353812e36acbSWarner Losh return "MAC"; 353912e36acbSWarner Losh case BWI_REGWIN_T_BUSPCIE: 354012e36acbSWarner Losh return "PCIE"; 354112e36acbSWarner Losh } 354212e36acbSWarner Losh panic("unknown regwin type 0x%04x\n", rw->rw_type); 354312e36acbSWarner Losh return NULL; 354412e36acbSWarner Losh } 354512e36acbSWarner Losh 354612e36acbSWarner Losh static uint32_t 354712e36acbSWarner Losh bwi_regwin_disable_bits(struct bwi_softc *sc) 354812e36acbSWarner Losh { 354912e36acbSWarner Losh uint32_t busrev; 355012e36acbSWarner Losh 355112e36acbSWarner Losh /* XXX cache this */ 355212e36acbSWarner Losh busrev = __SHIFTOUT(CSR_READ_4(sc, BWI_ID_LO), BWI_ID_LO_BUSREV_MASK); 355312e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT | BWI_DBG_MISC, 355412e36acbSWarner Losh "bus rev %u\n", busrev); 355512e36acbSWarner Losh 355612e36acbSWarner Losh if (busrev == BWI_BUSREV_0) 355712e36acbSWarner Losh return BWI_STATE_LO_DISABLE1; 355812e36acbSWarner Losh else if (busrev == BWI_BUSREV_1) 355912e36acbSWarner Losh return BWI_STATE_LO_DISABLE2; 356012e36acbSWarner Losh else 356112e36acbSWarner Losh return (BWI_STATE_LO_DISABLE1 | BWI_STATE_LO_DISABLE2); 356212e36acbSWarner Losh } 356312e36acbSWarner Losh 356412e36acbSWarner Losh int 356512e36acbSWarner Losh bwi_regwin_is_enabled(struct bwi_softc *sc, struct bwi_regwin *rw) 356612e36acbSWarner Losh { 356712e36acbSWarner Losh uint32_t val, disable_bits; 356812e36acbSWarner Losh 356912e36acbSWarner Losh disable_bits = bwi_regwin_disable_bits(sc); 357012e36acbSWarner Losh val = CSR_READ_4(sc, BWI_STATE_LO); 357112e36acbSWarner Losh 357212e36acbSWarner Losh if ((val & (BWI_STATE_LO_CLOCK | 357312e36acbSWarner Losh BWI_STATE_LO_RESET | 357412e36acbSWarner Losh disable_bits)) == BWI_STATE_LO_CLOCK) { 357512e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT, "%s is enabled\n", 357612e36acbSWarner Losh bwi_regwin_name(rw)); 357712e36acbSWarner Losh return 1; 357812e36acbSWarner Losh } else { 357912e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT, "%s is disabled\n", 358012e36acbSWarner Losh bwi_regwin_name(rw)); 358112e36acbSWarner Losh return 0; 358212e36acbSWarner Losh } 358312e36acbSWarner Losh } 358412e36acbSWarner Losh 358512e36acbSWarner Losh void 358612e36acbSWarner Losh bwi_regwin_disable(struct bwi_softc *sc, struct bwi_regwin *rw, uint32_t flags) 358712e36acbSWarner Losh { 358812e36acbSWarner Losh uint32_t state_lo, disable_bits; 358912e36acbSWarner Losh int i; 359012e36acbSWarner Losh 359112e36acbSWarner Losh state_lo = CSR_READ_4(sc, BWI_STATE_LO); 359212e36acbSWarner Losh 359312e36acbSWarner Losh /* 359412e36acbSWarner Losh * If current regwin is in 'reset' state, it was already disabled. 359512e36acbSWarner Losh */ 359612e36acbSWarner Losh if (state_lo & BWI_STATE_LO_RESET) { 359712e36acbSWarner Losh DPRINTF(sc, BWI_DBG_ATTACH | BWI_DBG_INIT, 359812e36acbSWarner Losh "%s was already disabled\n", bwi_regwin_name(rw)); 359912e36acbSWarner Losh return; 360012e36acbSWarner Losh } 360112e36acbSWarner Losh 360212e36acbSWarner Losh disable_bits = bwi_regwin_disable_bits(sc); 360312e36acbSWarner Losh 360412e36acbSWarner Losh /* 360512e36acbSWarner Losh * Disable normal clock 360612e36acbSWarner Losh */ 360712e36acbSWarner Losh state_lo = BWI_STATE_LO_CLOCK | disable_bits; 360812e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 360912e36acbSWarner Losh 361012e36acbSWarner Losh /* 361112e36acbSWarner Losh * Wait until normal clock is disabled 361212e36acbSWarner Losh */ 361312e36acbSWarner Losh #define NRETRY 1000 361412e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 361512e36acbSWarner Losh state_lo = CSR_READ_4(sc, BWI_STATE_LO); 361612e36acbSWarner Losh if (state_lo & disable_bits) 361712e36acbSWarner Losh break; 361812e36acbSWarner Losh DELAY(10); 361912e36acbSWarner Losh } 362012e36acbSWarner Losh if (i == NRETRY) { 362112e36acbSWarner Losh device_printf(sc->sc_dev, "%s disable clock timeout\n", 362212e36acbSWarner Losh bwi_regwin_name(rw)); 362312e36acbSWarner Losh } 362412e36acbSWarner Losh 362512e36acbSWarner Losh for (i = 0; i < NRETRY; ++i) { 362612e36acbSWarner Losh uint32_t state_hi; 362712e36acbSWarner Losh 362812e36acbSWarner Losh state_hi = CSR_READ_4(sc, BWI_STATE_HI); 362912e36acbSWarner Losh if ((state_hi & BWI_STATE_HI_BUSY) == 0) 363012e36acbSWarner Losh break; 363112e36acbSWarner Losh DELAY(10); 363212e36acbSWarner Losh } 363312e36acbSWarner Losh if (i == NRETRY) { 363412e36acbSWarner Losh device_printf(sc->sc_dev, "%s wait BUSY unset timeout\n", 363512e36acbSWarner Losh bwi_regwin_name(rw)); 363612e36acbSWarner Losh } 363712e36acbSWarner Losh #undef NRETRY 363812e36acbSWarner Losh 363912e36acbSWarner Losh /* 364012e36acbSWarner Losh * Reset and disable regwin with gated clock 364112e36acbSWarner Losh */ 364212e36acbSWarner Losh state_lo = BWI_STATE_LO_RESET | disable_bits | 364312e36acbSWarner Losh BWI_STATE_LO_CLOCK | BWI_STATE_LO_GATED_CLOCK | 364412e36acbSWarner Losh __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); 364512e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 364612e36acbSWarner Losh 364712e36acbSWarner Losh /* Flush pending bus write */ 364812e36acbSWarner Losh CSR_READ_4(sc, BWI_STATE_LO); 364912e36acbSWarner Losh DELAY(1); 365012e36acbSWarner Losh 365112e36acbSWarner Losh /* Reset and disable regwin */ 365212e36acbSWarner Losh state_lo = BWI_STATE_LO_RESET | disable_bits | 365312e36acbSWarner Losh __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); 365412e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 365512e36acbSWarner Losh 365612e36acbSWarner Losh /* Flush pending bus write */ 365712e36acbSWarner Losh CSR_READ_4(sc, BWI_STATE_LO); 365812e36acbSWarner Losh DELAY(1); 365912e36acbSWarner Losh } 366012e36acbSWarner Losh 366112e36acbSWarner Losh void 366212e36acbSWarner Losh bwi_regwin_enable(struct bwi_softc *sc, struct bwi_regwin *rw, uint32_t flags) 366312e36acbSWarner Losh { 366412e36acbSWarner Losh uint32_t state_lo, state_hi, imstate; 366512e36acbSWarner Losh 366612e36acbSWarner Losh bwi_regwin_disable(sc, rw, flags); 366712e36acbSWarner Losh 366812e36acbSWarner Losh /* Reset regwin with gated clock */ 366912e36acbSWarner Losh state_lo = BWI_STATE_LO_RESET | 367012e36acbSWarner Losh BWI_STATE_LO_CLOCK | 367112e36acbSWarner Losh BWI_STATE_LO_GATED_CLOCK | 367212e36acbSWarner Losh __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); 367312e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 367412e36acbSWarner Losh 367512e36acbSWarner Losh /* Flush pending bus write */ 367612e36acbSWarner Losh CSR_READ_4(sc, BWI_STATE_LO); 367712e36acbSWarner Losh DELAY(1); 367812e36acbSWarner Losh 367912e36acbSWarner Losh state_hi = CSR_READ_4(sc, BWI_STATE_HI); 368012e36acbSWarner Losh if (state_hi & BWI_STATE_HI_SERROR) 368112e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_HI, 0); 368212e36acbSWarner Losh 368312e36acbSWarner Losh imstate = CSR_READ_4(sc, BWI_IMSTATE); 368412e36acbSWarner Losh if (imstate & (BWI_IMSTATE_INBAND_ERR | BWI_IMSTATE_TIMEOUT)) { 368512e36acbSWarner Losh imstate &= ~(BWI_IMSTATE_INBAND_ERR | BWI_IMSTATE_TIMEOUT); 368612e36acbSWarner Losh CSR_WRITE_4(sc, BWI_IMSTATE, imstate); 368712e36acbSWarner Losh } 368812e36acbSWarner Losh 368912e36acbSWarner Losh /* Enable regwin with gated clock */ 369012e36acbSWarner Losh state_lo = BWI_STATE_LO_CLOCK | 369112e36acbSWarner Losh BWI_STATE_LO_GATED_CLOCK | 369212e36acbSWarner Losh __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); 369312e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 369412e36acbSWarner Losh 369512e36acbSWarner Losh /* Flush pending bus write */ 369612e36acbSWarner Losh CSR_READ_4(sc, BWI_STATE_LO); 369712e36acbSWarner Losh DELAY(1); 369812e36acbSWarner Losh 369912e36acbSWarner Losh /* Enable regwin with normal clock */ 370012e36acbSWarner Losh state_lo = BWI_STATE_LO_CLOCK | 370112e36acbSWarner Losh __SHIFTIN(flags, BWI_STATE_LO_FLAGS_MASK); 370212e36acbSWarner Losh CSR_WRITE_4(sc, BWI_STATE_LO, state_lo); 370312e36acbSWarner Losh 370412e36acbSWarner Losh /* Flush pending bus write */ 370512e36acbSWarner Losh CSR_READ_4(sc, BWI_STATE_LO); 370612e36acbSWarner Losh DELAY(1); 370712e36acbSWarner Losh } 370812e36acbSWarner Losh 370912e36acbSWarner Losh static void 371012e36acbSWarner Losh bwi_set_bssid(struct bwi_softc *sc, const uint8_t *bssid) 371112e36acbSWarner Losh { 371212e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 371312e36acbSWarner Losh struct bwi_mac *mac; 371412e36acbSWarner Losh struct bwi_myaddr_bssid buf; 371512e36acbSWarner Losh const uint8_t *p; 371612e36acbSWarner Losh uint32_t val; 371712e36acbSWarner Losh int n, i; 371812e36acbSWarner Losh 371912e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 372012e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 372112e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 372212e36acbSWarner Losh 372312e36acbSWarner Losh bwi_set_addr_filter(sc, BWI_ADDR_FILTER_BSSID, bssid); 372412e36acbSWarner Losh 372512e36acbSWarner Losh bcopy(IF_LLADDR(ifp), buf.myaddr, sizeof(buf.myaddr)); 372612e36acbSWarner Losh bcopy(bssid, buf.bssid, sizeof(buf.bssid)); 372712e36acbSWarner Losh 372812e36acbSWarner Losh n = sizeof(buf) / sizeof(val); 372912e36acbSWarner Losh p = (const uint8_t *)&buf; 373012e36acbSWarner Losh for (i = 0; i < n; ++i) { 373112e36acbSWarner Losh int j; 373212e36acbSWarner Losh 373312e36acbSWarner Losh val = 0; 373412e36acbSWarner Losh for (j = 0; j < sizeof(val); ++j) 373512e36acbSWarner Losh val |= ((uint32_t)(*p++)) << (j * 8); 373612e36acbSWarner Losh 373712e36acbSWarner Losh TMPLT_WRITE_4(mac, 0x20 + (i * sizeof(val)), val); 373812e36acbSWarner Losh } 373912e36acbSWarner Losh } 374012e36acbSWarner Losh 374112e36acbSWarner Losh static void 3742272f6adeSGleb Smirnoff bwi_updateslot(struct ieee80211com *ic) 374312e36acbSWarner Losh { 3744272f6adeSGleb Smirnoff struct bwi_softc *sc = ic->ic_softc; 374512e36acbSWarner Losh struct bwi_mac *mac; 374612e36acbSWarner Losh 374712e36acbSWarner Losh BWI_LOCK(sc); 3748272f6adeSGleb Smirnoff if (ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) { 374912e36acbSWarner Losh DPRINTF(sc, BWI_DBG_80211, "%s\n", __func__); 375012e36acbSWarner Losh 375112e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 375212e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 375312e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 375412e36acbSWarner Losh 375512e36acbSWarner Losh bwi_mac_updateslot(mac, (ic->ic_flags & IEEE80211_F_SHSLOT)); 375612e36acbSWarner Losh } 375712e36acbSWarner Losh BWI_UNLOCK(sc); 375812e36acbSWarner Losh } 375912e36acbSWarner Losh 376012e36acbSWarner Losh static void 376112e36acbSWarner Losh bwi_calibrate(void *xsc) 376212e36acbSWarner Losh { 376312e36acbSWarner Losh struct bwi_softc *sc = xsc; 376412e36acbSWarner Losh #ifdef INVARIANTS 376512e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 376612e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 376712e36acbSWarner Losh #endif 376812e36acbSWarner Losh struct bwi_mac *mac; 376912e36acbSWarner Losh 377012e36acbSWarner Losh BWI_ASSERT_LOCKED(sc); 377112e36acbSWarner Losh 377212e36acbSWarner Losh KASSERT(ic->ic_opmode != IEEE80211_M_MONITOR, 377312e36acbSWarner Losh ("opmode %d", ic->ic_opmode)); 377412e36acbSWarner Losh 377512e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 377612e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 377712e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 377812e36acbSWarner Losh 377912e36acbSWarner Losh bwi_mac_calibrate_txpower(mac, sc->sc_txpwrcb_type); 378012e36acbSWarner Losh sc->sc_txpwrcb_type = BWI_TXPWR_CALIB; 378112e36acbSWarner Losh 378212e36acbSWarner Losh /* XXX 15 seconds */ 378312e36acbSWarner Losh callout_reset(&sc->sc_calib_ch, hz * 15, bwi_calibrate, sc); 378412e36acbSWarner Losh } 378512e36acbSWarner Losh 378612e36acbSWarner Losh static int 378712e36acbSWarner Losh bwi_calc_rssi(struct bwi_softc *sc, const struct bwi_rxbuf_hdr *hdr) 378812e36acbSWarner Losh { 378912e36acbSWarner Losh struct bwi_mac *mac; 379012e36acbSWarner Losh 379112e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 379212e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 379312e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 379412e36acbSWarner Losh 379512e36acbSWarner Losh return bwi_rf_calc_rssi(mac, hdr); 379612e36acbSWarner Losh } 379712e36acbSWarner Losh 379812e36acbSWarner Losh static int 379912e36acbSWarner Losh bwi_calc_noise(struct bwi_softc *sc) 380012e36acbSWarner Losh { 380112e36acbSWarner Losh struct bwi_mac *mac; 380212e36acbSWarner Losh 380312e36acbSWarner Losh KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC, 380412e36acbSWarner Losh ("current regwin type %d", sc->sc_cur_regwin->rw_type)); 380512e36acbSWarner Losh mac = (struct bwi_mac *)sc->sc_cur_regwin; 380612e36acbSWarner Losh 380712e36acbSWarner Losh return bwi_rf_calc_noise(mac); 380812e36acbSWarner Losh } 380912e36acbSWarner Losh 381012e36acbSWarner Losh static __inline uint8_t 3811fcd9500fSBernhard Schmidt bwi_plcp2rate(const uint32_t plcp0, enum ieee80211_phytype type) 381212e36acbSWarner Losh { 381379c27adfSAdrian Chadd uint32_t plcp = le32toh(plcp0) & IEEE80211_OFDM_PLCP_RATE_MASK; 3814fcd9500fSBernhard Schmidt return (ieee80211_plcp2rate(plcp, type)); 381512e36acbSWarner Losh } 381612e36acbSWarner Losh 381712e36acbSWarner Losh static void 38185463c4a4SSam Leffler bwi_rx_radiotap(struct bwi_softc *sc, struct mbuf *m, 381912e36acbSWarner Losh struct bwi_rxbuf_hdr *hdr, const void *plcp, int rate, int rssi, int noise) 382012e36acbSWarner Losh { 382112e36acbSWarner Losh const struct ieee80211_frame_min *wh; 382212e36acbSWarner Losh 382312e36acbSWarner Losh sc->sc_rx_th.wr_flags = IEEE80211_RADIOTAP_F_FCS; 382412e36acbSWarner Losh if (htole16(hdr->rxh_flags1) & BWI_RXH_F1_SHPREAMBLE) 382512e36acbSWarner Losh sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 382612e36acbSWarner Losh 382712e36acbSWarner Losh wh = mtod(m, const struct ieee80211_frame_min *); 38285945b5f5SKevin Lo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) 382912e36acbSWarner Losh sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_WEP; 383012e36acbSWarner Losh 383112e36acbSWarner Losh sc->sc_rx_th.wr_tsf = hdr->rxh_tsf; /* No endian convertion */ 383212e36acbSWarner Losh sc->sc_rx_th.wr_rate = rate; 383312e36acbSWarner Losh sc->sc_rx_th.wr_antsignal = rssi; 383412e36acbSWarner Losh sc->sc_rx_th.wr_antnoise = noise; 383512e36acbSWarner Losh } 383612e36acbSWarner Losh 383712e36acbSWarner Losh static void 383812e36acbSWarner Losh bwi_led_attach(struct bwi_softc *sc) 383912e36acbSWarner Losh { 384012e36acbSWarner Losh const uint8_t *led_act = NULL; 384112e36acbSWarner Losh uint16_t gpio, val[BWI_LED_MAX]; 384212e36acbSWarner Losh int i; 384312e36acbSWarner Losh 384412e36acbSWarner Losh #define N(arr) (int)(sizeof(arr) / sizeof(arr[0])) 384512e36acbSWarner Losh 384612e36acbSWarner Losh for (i = 0; i < N(bwi_vendor_led_act); ++i) { 384712e36acbSWarner Losh if (sc->sc_pci_subvid == bwi_vendor_led_act[i].vid) { 384812e36acbSWarner Losh led_act = bwi_vendor_led_act[i].led_act; 384912e36acbSWarner Losh break; 385012e36acbSWarner Losh } 385112e36acbSWarner Losh } 385212e36acbSWarner Losh if (led_act == NULL) 385312e36acbSWarner Losh led_act = bwi_default_led_act; 385412e36acbSWarner Losh 385512e36acbSWarner Losh #undef N 385612e36acbSWarner Losh 385712e36acbSWarner Losh gpio = bwi_read_sprom(sc, BWI_SPROM_GPIO01); 385812e36acbSWarner Losh val[0] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_0); 385912e36acbSWarner Losh val[1] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_1); 386012e36acbSWarner Losh 386112e36acbSWarner Losh gpio = bwi_read_sprom(sc, BWI_SPROM_GPIO23); 386212e36acbSWarner Losh val[2] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_2); 386312e36acbSWarner Losh val[3] = __SHIFTOUT(gpio, BWI_SPROM_GPIO_3); 386412e36acbSWarner Losh 386512e36acbSWarner Losh for (i = 0; i < BWI_LED_MAX; ++i) { 386612e36acbSWarner Losh struct bwi_led *led = &sc->sc_leds[i]; 386712e36acbSWarner Losh 386812e36acbSWarner Losh if (val[i] == 0xff) { 386912e36acbSWarner Losh led->l_act = led_act[i]; 387012e36acbSWarner Losh } else { 387112e36acbSWarner Losh if (val[i] & BWI_LED_ACT_LOW) 387212e36acbSWarner Losh led->l_flags |= BWI_LED_F_ACTLOW; 387312e36acbSWarner Losh led->l_act = __SHIFTOUT(val[i], BWI_LED_ACT_MASK); 387412e36acbSWarner Losh } 387512e36acbSWarner Losh led->l_mask = (1 << i); 387612e36acbSWarner Losh 387712e36acbSWarner Losh if (led->l_act == BWI_LED_ACT_BLINK_SLOW || 387812e36acbSWarner Losh led->l_act == BWI_LED_ACT_BLINK_POLL || 387912e36acbSWarner Losh led->l_act == BWI_LED_ACT_BLINK) { 388012e36acbSWarner Losh led->l_flags |= BWI_LED_F_BLINK; 388112e36acbSWarner Losh if (led->l_act == BWI_LED_ACT_BLINK_POLL) 388212e36acbSWarner Losh led->l_flags |= BWI_LED_F_POLLABLE; 388312e36acbSWarner Losh else if (led->l_act == BWI_LED_ACT_BLINK_SLOW) 388412e36acbSWarner Losh led->l_flags |= BWI_LED_F_SLOW; 388512e36acbSWarner Losh 388612e36acbSWarner Losh if (sc->sc_blink_led == NULL) { 388712e36acbSWarner Losh sc->sc_blink_led = led; 388812e36acbSWarner Losh if (led->l_flags & BWI_LED_F_SLOW) 388912e36acbSWarner Losh BWI_LED_SLOWDOWN(sc->sc_led_idle); 389012e36acbSWarner Losh } 389112e36acbSWarner Losh } 389212e36acbSWarner Losh 389312e36acbSWarner Losh DPRINTF(sc, BWI_DBG_LED | BWI_DBG_ATTACH, 389412e36acbSWarner Losh "%dth led, act %d, lowact %d\n", i, 389512e36acbSWarner Losh led->l_act, led->l_flags & BWI_LED_F_ACTLOW); 389612e36acbSWarner Losh } 389758fbe5abSJohn Baldwin callout_init_mtx(&sc->sc_led_blink_ch, &sc->sc_mtx, 0); 389812e36acbSWarner Losh } 389912e36acbSWarner Losh 390012e36acbSWarner Losh static __inline uint16_t 390112e36acbSWarner Losh bwi_led_onoff(const struct bwi_led *led, uint16_t val, int on) 390212e36acbSWarner Losh { 390312e36acbSWarner Losh if (led->l_flags & BWI_LED_F_ACTLOW) 390412e36acbSWarner Losh on = !on; 390512e36acbSWarner Losh if (on) 390612e36acbSWarner Losh val |= led->l_mask; 390712e36acbSWarner Losh else 390812e36acbSWarner Losh val &= ~led->l_mask; 390912e36acbSWarner Losh return val; 391012e36acbSWarner Losh } 391112e36acbSWarner Losh 391212e36acbSWarner Losh static void 391312e36acbSWarner Losh bwi_led_newstate(struct bwi_softc *sc, enum ieee80211_state nstate) 391412e36acbSWarner Losh { 391512e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 391612e36acbSWarner Losh struct ieee80211com *ic = ifp->if_l2com; 391712e36acbSWarner Losh uint16_t val; 391812e36acbSWarner Losh int i; 391912e36acbSWarner Losh 392012e36acbSWarner Losh if (nstate == IEEE80211_S_INIT) { 392112e36acbSWarner Losh callout_stop(&sc->sc_led_blink_ch); 392212e36acbSWarner Losh sc->sc_led_blinking = 0; 392312e36acbSWarner Losh } 392412e36acbSWarner Losh 392512e36acbSWarner Losh if ((ic->ic_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 392612e36acbSWarner Losh return; 392712e36acbSWarner Losh 392812e36acbSWarner Losh val = CSR_READ_2(sc, BWI_MAC_GPIO_CTRL); 392912e36acbSWarner Losh for (i = 0; i < BWI_LED_MAX; ++i) { 393012e36acbSWarner Losh struct bwi_led *led = &sc->sc_leds[i]; 393112e36acbSWarner Losh int on; 393212e36acbSWarner Losh 393312e36acbSWarner Losh if (led->l_act == BWI_LED_ACT_UNKN || 393412e36acbSWarner Losh led->l_act == BWI_LED_ACT_NULL) 393512e36acbSWarner Losh continue; 393612e36acbSWarner Losh 393712e36acbSWarner Losh if ((led->l_flags & BWI_LED_F_BLINK) && 393812e36acbSWarner Losh nstate != IEEE80211_S_INIT) 393912e36acbSWarner Losh continue; 394012e36acbSWarner Losh 394112e36acbSWarner Losh switch (led->l_act) { 394212e36acbSWarner Losh case BWI_LED_ACT_ON: /* Always on */ 394312e36acbSWarner Losh on = 1; 394412e36acbSWarner Losh break; 394512e36acbSWarner Losh case BWI_LED_ACT_OFF: /* Always off */ 394612e36acbSWarner Losh case BWI_LED_ACT_5GHZ: /* TODO: 11A */ 394712e36acbSWarner Losh on = 0; 394812e36acbSWarner Losh break; 394912e36acbSWarner Losh default: 395012e36acbSWarner Losh on = 1; 395112e36acbSWarner Losh switch (nstate) { 395212e36acbSWarner Losh case IEEE80211_S_INIT: 395312e36acbSWarner Losh on = 0; 395412e36acbSWarner Losh break; 395512e36acbSWarner Losh case IEEE80211_S_RUN: 395612e36acbSWarner Losh if (led->l_act == BWI_LED_ACT_11G && 395712e36acbSWarner Losh ic->ic_curmode != IEEE80211_MODE_11G) 395812e36acbSWarner Losh on = 0; 395912e36acbSWarner Losh break; 396012e36acbSWarner Losh default: 396112e36acbSWarner Losh if (led->l_act == BWI_LED_ACT_ASSOC) 396212e36acbSWarner Losh on = 0; 396312e36acbSWarner Losh break; 396412e36acbSWarner Losh } 396512e36acbSWarner Losh break; 396612e36acbSWarner Losh } 396712e36acbSWarner Losh 396812e36acbSWarner Losh val = bwi_led_onoff(led, val, on); 396912e36acbSWarner Losh } 397012e36acbSWarner Losh CSR_WRITE_2(sc, BWI_MAC_GPIO_CTRL, val); 397112e36acbSWarner Losh } 397212e36acbSWarner Losh static void 397312e36acbSWarner Losh bwi_led_event(struct bwi_softc *sc, int event) 397412e36acbSWarner Losh { 397512e36acbSWarner Losh struct bwi_led *led = sc->sc_blink_led; 397612e36acbSWarner Losh int rate; 397712e36acbSWarner Losh 397812e36acbSWarner Losh if (event == BWI_LED_EVENT_POLL) { 397912e36acbSWarner Losh if ((led->l_flags & BWI_LED_F_POLLABLE) == 0) 398012e36acbSWarner Losh return; 398112e36acbSWarner Losh if (ticks - sc->sc_led_ticks < sc->sc_led_idle) 398212e36acbSWarner Losh return; 398312e36acbSWarner Losh } 398412e36acbSWarner Losh 398512e36acbSWarner Losh sc->sc_led_ticks = ticks; 398612e36acbSWarner Losh if (sc->sc_led_blinking) 398712e36acbSWarner Losh return; 398812e36acbSWarner Losh 398912e36acbSWarner Losh switch (event) { 399012e36acbSWarner Losh case BWI_LED_EVENT_RX: 399112e36acbSWarner Losh rate = sc->sc_rx_rate; 399212e36acbSWarner Losh break; 399312e36acbSWarner Losh case BWI_LED_EVENT_TX: 399412e36acbSWarner Losh rate = sc->sc_tx_rate; 399512e36acbSWarner Losh break; 399612e36acbSWarner Losh case BWI_LED_EVENT_POLL: 399712e36acbSWarner Losh rate = 0; 399812e36acbSWarner Losh break; 399912e36acbSWarner Losh default: 400012e36acbSWarner Losh panic("unknown LED event %d\n", event); 400112e36acbSWarner Losh break; 400212e36acbSWarner Losh } 400312e36acbSWarner Losh bwi_led_blink_start(sc, bwi_led_duration[rate].on_dur, 400412e36acbSWarner Losh bwi_led_duration[rate].off_dur); 400512e36acbSWarner Losh } 400612e36acbSWarner Losh 400712e36acbSWarner Losh static void 400812e36acbSWarner Losh bwi_led_blink_start(struct bwi_softc *sc, int on_dur, int off_dur) 400912e36acbSWarner Losh { 401012e36acbSWarner Losh struct bwi_led *led = sc->sc_blink_led; 401112e36acbSWarner Losh uint16_t val; 401212e36acbSWarner Losh 401312e36acbSWarner Losh val = CSR_READ_2(sc, BWI_MAC_GPIO_CTRL); 401412e36acbSWarner Losh val = bwi_led_onoff(led, val, 1); 401512e36acbSWarner Losh CSR_WRITE_2(sc, BWI_MAC_GPIO_CTRL, val); 401612e36acbSWarner Losh 401712e36acbSWarner Losh if (led->l_flags & BWI_LED_F_SLOW) { 401812e36acbSWarner Losh BWI_LED_SLOWDOWN(on_dur); 401912e36acbSWarner Losh BWI_LED_SLOWDOWN(off_dur); 402012e36acbSWarner Losh } 402112e36acbSWarner Losh 402212e36acbSWarner Losh sc->sc_led_blinking = 1; 402312e36acbSWarner Losh sc->sc_led_blink_offdur = off_dur; 402412e36acbSWarner Losh 402512e36acbSWarner Losh callout_reset(&sc->sc_led_blink_ch, on_dur, bwi_led_blink_next, sc); 402612e36acbSWarner Losh } 402712e36acbSWarner Losh 402812e36acbSWarner Losh static void 402912e36acbSWarner Losh bwi_led_blink_next(void *xsc) 403012e36acbSWarner Losh { 403112e36acbSWarner Losh struct bwi_softc *sc = xsc; 403212e36acbSWarner Losh uint16_t val; 403312e36acbSWarner Losh 403412e36acbSWarner Losh val = CSR_READ_2(sc, BWI_MAC_GPIO_CTRL); 403512e36acbSWarner Losh val = bwi_led_onoff(sc->sc_blink_led, val, 0); 403612e36acbSWarner Losh CSR_WRITE_2(sc, BWI_MAC_GPIO_CTRL, val); 403712e36acbSWarner Losh 403812e36acbSWarner Losh callout_reset(&sc->sc_led_blink_ch, sc->sc_led_blink_offdur, 403912e36acbSWarner Losh bwi_led_blink_end, sc); 404012e36acbSWarner Losh } 404112e36acbSWarner Losh 404212e36acbSWarner Losh static void 404312e36acbSWarner Losh bwi_led_blink_end(void *xsc) 404412e36acbSWarner Losh { 404512e36acbSWarner Losh struct bwi_softc *sc = xsc; 404612e36acbSWarner Losh sc->sc_led_blinking = 0; 404712e36acbSWarner Losh } 404812e36acbSWarner Losh 404912e36acbSWarner Losh static void 405012e36acbSWarner Losh bwi_restart(void *xsc, int pending) 405112e36acbSWarner Losh { 405212e36acbSWarner Losh struct bwi_softc *sc = xsc; 405312e36acbSWarner Losh struct ifnet *ifp = sc->sc_ifp; 405412e36acbSWarner Losh 405512e36acbSWarner Losh if_printf(ifp, "%s begin, help!\n", __func__); 405612e36acbSWarner Losh BWI_LOCK(sc); 405712e36acbSWarner Losh bwi_init_statechg(xsc, 0); 405812e36acbSWarner Losh #if 0 405912e36acbSWarner Losh bwi_start_locked(ifp); 406012e36acbSWarner Losh #endif 406107db17ceSWarner Losh BWI_UNLOCK(sc); 406212e36acbSWarner Losh } 4063