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