Index: brcmwl.c =================================================================== --- brcmwl.c +++ brcmwl.c @@ -39,50 +39,5460 @@ #include #include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __FreeBSD_version >= 800000 +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "brcmwl.h" + +#include + + +#include "sdiobus_if.h" +#include + #include -#define SDIO_VENDOR_BROADCOM 0x02d0 -struct brcmwl_softc { - device_t dev; -}; +#define BWN_DEBUG 1 + +/* + +Since SDIO-protocol support multiple devices and manufacturer use firmware-solution frequently, +my final goal is keep the code simple (that mean is replace the manufacturer with the others easily) + + SDIO + | + ------------------------ + | | | | + WLAN GPS Camera Bluetooth + /|\ + / | \ + / | \ +brm ath rtk + + + wlan.c (probe/attach/detach method) + |-- if_brcm.c + |-- if_qualcomm.c + |-- if_realtek.c + + gps.c + |---blah-blah-blah.c + |---blah-blah-blah.c + |---blah-blah-blah.c + + bluetooth.c + |---blah-blah-blah.c + |---blah-blah-blah.c + |---blah-blah-blah.c + + +*/ + +static SYSCTL_NODE(_hw, OID_AUTO, bwn, CTLFLAG_RD, 0, "Broadcom driver parameters"); + + +#ifdef BWN_DEBUG +static int bwn_debug = 0; +SYSCTL_INT(_hw_bwn, OID_AUTO, debug, CTLFLAG_RWTUN, &bwn_debug, 0, + "Broadcom debugging printfs"); +#endif + + +static int bwn_bfp = 0; /* use "Bad Frames Preemption" */ +SYSCTL_INT(_hw_bwn, OID_AUTO, bfp, CTLFLAG_RW, &bwn_bfp, 0, "uses Bad Frames Preemption"); + + /* +static int bwn_bluetooth = 1; +SYSCTL_INT(_hw_bwn, OID_AUTO, bluetooth, CTLFLAG_RW, &bwn_bluetooth, 0, "turns on Bluetooth Coexistence");*/ + +static int bwn_hwpctl = 0; +SYSCTL_INT(_hw_bwn, OID_AUTO, hwpctl, CTLFLAG_RW, &bwn_hwpctl, 0, "uses H/W power control"); + +static int bwn_msi_disable = 0; /* MSI disabled */ +TUNABLE_INT("hw.bwn.msi_disable", &bwn_msi_disable); +/* +static int bwn_usedma = 1; +SYSCTL_INT(_hw_bwn, OID_AUTO, usedma, CTLFLAG_RD, &bwn_usedma, 0, "uses DMA"); +TUNABLE_INT("hw.bwn.usedma", &bwn_usedma);*/ +static int bwn_wme = 1; +SYSCTL_INT(_hw_bwn, OID_AUTO, wme, CTLFLAG_RW, &bwn_wme, 0, "uses WME support"); + + +static void bwn_attach_pre(struct brcmwl_softc *); +static int bwn_attach_post(struct brcmwl_softc *); +static int bwn_init(struct brcmwl_softc *); +static void bwn_parent(struct ieee80211com *); +static void bwn_start(struct brcmwl_softc *); +static int bwn_transmit(struct ieee80211com *, struct mbuf *); +static int bwn_attach_core(struct bwn_mac *); +static int bwn_phy_getinfo(struct bwn_mac *, int); +static int bwn_chiptest(struct bwn_mac *); +static int bwn_setup_channels(struct bwn_mac *, int, int); +static void bwn_addchannels(struct ieee80211_channel [], int, int *, + const struct bwn_channelinfo *, const uint8_t []); + + +static struct ieee80211vap *bwn_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, int, + const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void bwn_vap_delete(struct ieee80211vap *); +static void bwn_stop(struct brcmwl_softc *); +static int bwn_core_init(struct bwn_mac *); +static void bwn_core_start(struct bwn_mac *); +static void bwn_core_exit(struct bwn_mac *); +static void bwn_bt_disable(struct bwn_mac *); +static int bwn_chip_init(struct bwn_mac *); +static void bwn_set_txretry(struct bwn_mac *, int, int); +static void bwn_rate_init(struct bwn_mac *); +static void bwn_set_phytxctl(struct bwn_mac *); + + +static void bwn_set_macaddr(struct bwn_mac *); +static void bwn_crypt_init(struct bwn_mac *); +static void bwn_chip_exit(struct bwn_mac *); +static int brcmwl_fw_fillinfo(struct bwn_mac *); + + +static int brcmwl_fw_loadinitvals(struct bwn_mac *); +static int bwn_phy_init(struct bwn_mac *); +static void bwn_set_txantenna(struct bwn_mac *, int); +static void bwn_set_opmode(struct bwn_mac *); +static void bwn_rate_write(struct bwn_mac *, uint16_t, int); +static uint8_t bwn_plcp_getcck(const uint8_t); +static uint8_t bwn_plcp_getofdm(const uint8_t); + + + + +static void bwn_destroy_queue_tx(struct bwn_pio_txqueue *); + +static void bwn_pio_cancel_tx_packets(struct bwn_pio_txqueue *); +static int bwn_pio_rx(struct bwn_pio_rxqueue *); +static uint8_t bwn_pio_rxeof(struct bwn_pio_rxqueue *); +static void bwn_pio_handle_txeof(struct bwn_mac *, const struct bwn_txstatus *); + +static int bwn_pio_tx_start(struct bwn_mac *, struct ieee80211_node *, struct mbuf *); +static struct bwn_pio_txqueue *bwn_pio_select(struct bwn_mac *, uint8_t); + +static struct bwn_pio_txqueue *bwn_pio_parse_cookie(struct bwn_mac *, uint16_t, struct bwn_pio_txpkt **); + +static int brcmwl_fw_gets(struct bwn_mac *, enum bwn_fwtype); + +static int brcmwl_fw_get(char *fwname, struct bwn_fwfile *bfw, enum bwn_fwtype type); + +static void brcmwl_release_firmware(struct bwn_mac *); +static void brcmwl_do_release_fw(struct bwn_fwfile *); + +static uint16_t bwn_ant2phy(int); +static void bwn_mac_write_bssid(struct bwn_mac *); +static void bwn_mac_setfilter(struct bwn_mac *, uint16_t, + const uint8_t *); +static void bwn_key_dowrite(struct bwn_mac *, uint8_t, uint8_t, + const uint8_t *, size_t, const uint8_t *); +static void bwn_key_macwrite(struct bwn_mac *, uint8_t, + const uint8_t *); +static void bwn_key_write(struct bwn_mac *, uint8_t, uint8_t, + const uint8_t *); +static void bwn_phy_exit(struct bwn_mac *); +static void bwn_core_stop(struct bwn_mac *); + +static int bwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static void bwn_set_pretbtt(struct bwn_mac *); +static void bwn_intrtask(void *, int); +static void bwn_restart(struct bwn_mac *, const char *); + +static void bwn_intr_noise(struct bwn_mac *); +static void bwn_intr_txeof(struct bwn_mac *); +static void bwn_hwreset(void *, int); + + +static uint32_t bwn_jssi_read(struct bwn_mac *); +static void bwn_noise_gensample(struct bwn_mac *); +static void bwn_handle_txeof(struct bwn_mac *, + const struct bwn_txstatus *); +static void bwn_rxeof(struct bwn_mac *, struct mbuf *, const void *); +static void bwn_phy_txpower_check(struct bwn_mac *, uint32_t); +static int bwn_tx_start(struct brcmwl_softc *, struct ieee80211_node *, + struct mbuf *); +static int bwn_tx_isfull(struct brcmwl_softc *, struct mbuf *); +static int bwn_set_txhdr(struct bwn_mac *, + struct ieee80211_node *, struct mbuf *, struct bwn_txhdr *, + uint16_t); + +static void bwn_txpwr(void *, int); +static void bwn_tasks(void *); +static void bwn_task_15s(struct bwn_mac *); +static void bwn_task_30s(struct bwn_mac *); +static void bwn_task_60s(struct bwn_mac *); + + + +static void bwn_watchdog(void *); +static void bwn_pio_stop(struct bwn_mac *); + + + +static void bwn_sysctl_node(struct brcmwl_softc *); + +void bwn_reset_core(struct bwn_mac *mac, int g_mode); +void bwn_psctl(struct bwn_mac *mac, uint32_t flags); +void bwn_mac_suspend(struct bwn_mac *mac); +int bwn_switch_channel(struct bwn_mac *mac, int chan); static int brcmwl_probe(device_t dev); static int brcmwl_attach(device_t dev); static int brcmwl_detach(device_t dev); -/* - * XXX Do we need to implement device_identify()? - */ + + +static int brcmwl_sdiobus_write_stream(struct brcmwl_softc *sc, uint32_t address, uint8_t *data, uint32_t size); +static int brcmwl_sdiobus_read_stream(struct brcmwl_softc *sc, uint32_t addr, uint8_t *data, uint32_t datalen); + +static void brcm_dhdcore_destroy(struct brcmwl_softc *sc); + +void bwn_mac_enable(struct bwn_mac *mac); + +static int brcmwl_sdiobus_write_1(struct brcmwl_softc *sc, uint8_t func, uint32_t addr, uint8_t datain); +static int brcmwl_sdiobus_read_1(struct brcmwl_softc *sc, uint8_t func, uint32_t addr, uint8_t *dataout); +static int brcmwl_sdiobus_write_4(struct brcmwl_softc *sc, uint32_t addr, uint32_t datain); +static int brcmwl_sdiobus_read_4(struct brcmwl_softc *sc, uint32_t addr, uint32_t *dataout); + +static bool brcmwl_chip_sr_capable(struct brcmwl_softc *sc, struct brcmwl_chip_t *pub); +static bool brcmwl_chip_set_active(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore); +static int brcmwl_sdio_clkctl(struct brcmwl_softc *sc, unsigned int target, bool pendok); + +static int +brcmwl_sdiodev_read(struct brcmwl_softc *sc, uint32_t reg, uint32_t *val); +static int +brcmwl_sdiodev_write(struct brcmwl_softc *sc, uint32_t reg, uint32_t val); +static void +brcmwl_sdio_readshared(struct brcmwl_softc *sc); +static void +brcmwl_sdio_rx_frames(struct brcmwl_softc *sc); +struct mbuf * +brcmwl_sdio_newbuf(void); + +static MALLOC_DEFINE(M_BCMBUF, "bcmbuf", "BCMBUF driver and services"); + + +#define BRCM_CC_43430_CHIP_ID 0xA9A6 + +// I dont't like hardcode-in-function, so I want all HARDCODE in one table +static const struct wlan_table_t { + // VID/PID not CHIPID + uint16_t vendid; + uint16_t prodid; + char fwname[FWCHAR_LEN]; + uint8_t revi; + uint32_t rambase; + uint32_t ramsize; + uint32_t srsize; +} model_name[] = { + { SDIO_BROADCOM, 0xA94D, "bcm433xx", 0x0, 0x00000000, 0x00000000, 0x00000000 }, + { SDIO_BROADCOM, 0xA94D, "bcm433xx", 0x1, 0x00000000, 0x00000000, 0x00000000}, + { SDIO_BROADCOM, 0xA94D, "bcm433xx", 0x2, 0x00000000, 0x00000000, 0x00000000 }, + { SDIO_BROADCOM, 0xA9A6, "bcm434xx", 0x0, 0x00000000, 0x00000000, 0x00010000}, // bcm43430/ bcm43438 + { SDIO_BROADCOM, 0xA9A6, "bcm434xx", 0x1, 0x00000000, 0x00000000, 0x00000000}, +}; + + +static const uint16_t bwn_wme_shm_offsets[] = { + [0] = BWN_WME_BESTEFFORT, + [1] = BWN_WME_BACKGROUND, + [2] = BWN_WME_VOICE, + [3] = BWN_WME_VIDEO, +}; + + +static const struct bwn_channelinfo bwn_chantable_bg = { + .channels = { + { 2412, 1, 30 }, { 2417, 2, 30 }, { 2422, 3, 30 }, + { 2427, 4, 30 }, { 2432, 5, 30 }, { 2437, 6, 30 }, + { 2442, 7, 30 }, { 2447, 8, 30 }, { 2452, 9, 30 }, + { 2457, 10, 30 }, { 2462, 11, 30 }, { 2467, 12, 30 }, + { 2472, 13, 30 }, { 2484, 14, 30 } }, + .nchannels = 14 +}; + +static const struct bwn_channelinfo bwn_chantable_a = { + .channels = { + { 5170, 34, 30 }, { 5180, 36, 30 }, { 5190, 38, 30 }, + { 5200, 40, 30 }, { 5210, 42, 30 }, { 5220, 44, 30 }, + { 5230, 46, 30 }, { 5240, 48, 30 }, { 5260, 52, 30 }, + { 5280, 56, 30 }, { 5300, 60, 30 }, { 5320, 64, 30 }, + { 5500, 100, 30 }, { 5520, 104, 30 }, { 5540, 108, 30 }, + { 5560, 112, 30 }, { 5580, 116, 30 }, { 5600, 120, 30 }, + { 5620, 124, 30 }, { 5640, 128, 30 }, { 5660, 132, 30 }, + { 5680, 136, 30 }, { 5700, 140, 30 }, { 5745, 149, 30 }, + { 5765, 153, 30 }, { 5785, 157, 30 }, { 5805, 161, 30 }, + { 5825, 165, 30 }, { 5920, 184, 30 }, { 5940, 188, 30 }, + { 5960, 192, 30 }, { 5980, 196, 30 }, { 6000, 200, 30 }, + { 6020, 204, 30 }, { 6040, 208, 30 }, { 6060, 212, 30 }, + { 6080, 216, 30 } }, + .nchannels = 37 +}; + + static int -brcmwl_probe(device_t dev) +brcmwl_check_id(device_t dev, uint8_t revcode, struct id_struct_t *readid){ + + int i; + int ret = 0xff; + uint16_t vendid = sdio_get_vendor_id(dev); + + i = sizeof(model_name) / sizeof(model_name[0]); + while (--i >= 0) { + if(vendid == model_name[i].vendid){ + uint16_t prodid = sdio_get_product_id(dev) & 0xfff0; // dont care last 4BIT + uint16_t tmppid = model_name[i].prodid & 0xfff0; + if(prodid == tmppid){ + if(revcode == model_name[i].revi){ + memcpy(readid->fwname, model_name[i].fwname, sizeof(model_name[i].fwname)); + readid->rambase = model_name[i].rambase; + readid->ramsize = model_name[i].ramsize; + readid->srsize = model_name[i].srsize; + ret = 0; + break; + } + if(i == 0){ + device_printf(dev, "no matched device, exiting.\n"); + return ret; + } + } + } + } + if (i < 0) { + device_printf(dev, " (unsupported)\n"); + return ret; + } + + return ret; +} + + + +static void +brcmwl_do_release_fw(struct bwn_fwfile *bfw) { - int vendor_id; - device_printf(dev, "probe() called\n"); - vendor_id = sdio_get_vendor_id(dev); - device_printf(dev, "vendor_id=%d\n", vendor_id); - if (vendor_id != SDIO_VENDOR_BROADCOM) { - device_printf(dev, "Non-Broadcom card\n"); - return (-1); + if (bfw->fw != NULL) + firmware_put(bfw->fw, FIRMWARE_UNLOAD); + bfw->fw = NULL; + bfw->filename = NULL; +} + + +static void +brcmwl_release_firmware(struct bwn_mac *mac){ + + brcmwl_do_release_fw(&mac->mac_fw.nvram); + brcmwl_do_release_fw(&mac->mac_fw.bin); + brcmwl_do_release_fw(&mac->mac_fw.ucode); + brcmwl_do_release_fw(&mac->mac_fw.pcm); + brcmwl_do_release_fw(&mac->mac_fw.initvals); + brcmwl_do_release_fw(&mac->mac_fw.initvals_band); + +} + +static int +brcmwl_fw_get(char *fwname, struct bwn_fwfile *bfw, enum bwn_fwtype type){ + + const struct firmware *fw = firmware_get(fwname); + if (fw == NULL) { + return (ENOENT); } - return (BUS_PROBE_GENERIC); + + bfw->filename = fwname; + bfw->fw = fw; + bfw->type = type; + return (0); +} + +static int +brcmwl_fw_gets(struct bwn_mac *mac, enum bwn_fwtype type){ + + struct brcmwl_softc *sc = mac->mac_sc; + struct bwn_fw *fw = &mac->mac_fw; + int error; + char buf[FWCHAR_LEN+6]; + + memset(buf, 0, sizeof(buf)); + sprintf(buf, "%s_nvram", sc->predef_val.fwname); + error = brcmwl_fw_get(buf, &fw->nvram, type); + if (error) { + device_printf(sc->sc_dev, "nvram file(%s) format error\n", buf); + brcmwl_release_firmware(mac); + return (error); + } + + memset(buf, 0, sizeof(buf)); + sprintf(buf, "%s_bin", sc->predef_val.fwname); + error = brcmwl_fw_get(buf, &fw->bin, type); + if (error) { + device_printf(sc->sc_dev, "bin file(%s) format error\n", buf); + brcmwl_release_firmware(mac); + return (error); + } + + return (0); +} + +static int +brcmwl_fw_fillinfo(struct bwn_mac *mac){ + + int error; + error = brcmwl_fw_gets(mac, BWN_FWTYPE_DEFAULT); + if (error == 0) + return (0); + /*error = brcmwl_fw_gets(mac, BWN_FWTYPE_OPENSOURCE); + if (error == 0) + return (0);*/ + return (error); +} + +static int +brcmwl_fw_loadinitvals(struct bwn_mac *mac){ + + struct bwn_fw *fw = &mac->mac_fw; + struct brcmwl_softc *sc = mac->mac_sc; + int error; + struct brcmwl_chip_priv_t *prChip = sc->bcmChip; + uint8_t *buffer1 = NULL; + uint8_t *verify = NULL; + + brcmwl_sdio_clkctl(sc, CLK_AVAIL, false); + + + device_printf(sc->sc_dev, "== Configuration ==\n"); + device_printf(sc->sc_dev, "Download: "); + buffer1 = malloc(fw->nvram.fw->datasize, M_BCMBUF, M_ZERO | M_WAITOK); + memcpy(buffer1, fw->nvram.fw->data, fw->nvram.fw->datasize); + + error = brcmwl_sdiobus_write_stream(sc, prChip->pub.rambase + prChip->pub.ramsize - fw->nvram.fw->datasize, + buffer1, fw->nvram.fw->datasize); + if (error){ + printf(" [ Failed ] 0x%x\n", error); + goto out; + } + else printf(" [ Completed ]\n"); + + device_printf(sc->sc_dev, "Verification: "); + verify = malloc(fw->nvram.fw->datasize, M_BCMBUF, M_ZERO | M_WAITOK); + memset(verify, 0xfe, fw->nvram.fw->datasize); + error = brcmwl_sdiobus_read_stream(sc, prChip->pub.rambase + prChip->pub.ramsize - fw->nvram.fw->datasize, + verify, fw->nvram.fw->datasize); + if (error || memcmp(verify, buffer1, fw->nvram.fw->datasize)) { + printf("[ Failed ] 0x%x\n", error); + goto out; + } + else printf("[ Completed ]\n"); + + free(verify, M_BCMBUF); + + free(buffer1, M_BCMBUF); + buffer1 = NULL; + + device_printf(sc->sc_dev, "== Binary == \n"); + device_printf(sc->sc_dev, "Download: "); + buffer1 = malloc(fw->bin.fw->datasize, M_BCMBUF, M_ZERO | M_WAITOK); + memcpy(buffer1, fw->bin.fw->data, fw->bin.fw->datasize); + + /* int i; + for(i=fw->bin.fw->datasize; i>fw->bin.fw->datasize-60; i--) + printf("0x%02x ", buffer1[i]); + printf("\n-------------\n");*/ + + error = brcmwl_sdiobus_write_stream(sc, prChip->pub.rambase, buffer1, fw->bin.fw->datasize); + if (error){ + printf(" [ Failed ] 0x%x\n", error); + goto out; + } + else printf(" [ Completed ]\n"); + + device_printf(sc->sc_dev, "Verification: "); + verify = malloc(fw->bin.fw->datasize, M_BCMBUF, M_ZERO | M_WAITOK); + memset(verify, 0xfe, fw->bin.fw->datasize); + error = brcmwl_sdiobus_read_stream(sc, prChip->pub.rambase, verify, fw->bin.fw->datasize); + + /* for(i=fw->bin.fw->datasize; i>fw->bin.fw->datasize-60; i--) + printf("0x%02x ", verify[i]); + printf("\n-------------\n");*/ + /// alway have 42 bytes different, is that a SWAP partition? + if (error || memcmp(verify, buffer1, fw->bin.fw->datasize-42)) { + printf("[ Failed ] 0x%x\n", error); + goto out; + } + else printf("[ Completed ]\n"); + + free(verify, M_BCMBUF); + + free(buffer1, M_BCMBUF); + buffer1 = NULL; + verify = NULL; + + if(!brcmwl_chip_set_active(sc, sc->prCoreSoc)){ + device_printf(sc->sc_dev, "error getting out of ARM core reset \n"); + goto out; + } + + + return 0; + + out: + free(verify, M_BCMBUF); + free(buffer1, M_BCMBUF); + brcmwl_sdio_clkctl(sc, CLK_SDONLY, false); + return error; +} + +/////////////// +int +bwn_switch_channel(struct bwn_mac *mac, int chan) +{ + struct bwn_phy *phy = &(mac->mac_phy); + struct brcmwl_softc *sc = mac->mac_sc; + struct ieee80211com *ic = &sc->sc_ic; + uint16_t channelcookie; + /// uint16_t savedcookie; + int error; + + if (chan == 0xffff) + chan = phy->get_default_chan(mac); + + channelcookie = chan; + if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) + channelcookie |= 0x100; +/// savedcookie = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_CHAN); + /// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_CHAN, channelcookie); + error = phy->switch_channel(mac, chan); + if (error) + goto fail; + + mac->mac_phy.chan = chan; + DELAY(8000); + return (0); +fail: + device_printf(sc->sc_dev, "failed to switch channel\n"); + /// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_CHAN, savedcookie); + return (error); } static int -brcmwl_attach(device_t dev) +bwn_phy_init(struct bwn_mac *mac) { - device_printf(dev, "attach() called\n"); - device_set_desc(dev, "Imaginary SDIO WiFi driver"); + struct brcmwl_softc *sc = mac->mac_sc; + int error; + + mac->mac_phy.chan = mac->mac_phy.get_default_chan(mac); + mac->mac_phy.rf_onoff(mac, 1); + error = mac->mac_phy.init(mac); + if (error) { + device_printf(sc->sc_dev, "PHY init failed\n"); + goto fail0; + } + error = bwn_switch_channel(mac, + mac->mac_phy.get_default_chan(mac)); + if (error) { + device_printf(sc->sc_dev, + "failed to switch default channel\n"); + goto fail1; + } + return (0); +fail1: + if (mac->mac_phy.exit) + mac->mac_phy.exit(mac); +fail0: + mac->mac_phy.rf_onoff(mac, 0); + + return (error); +} + +static uint16_t +bwn_ant2phy(int antenna){ + + switch (antenna) { + case BWN_ANT0: + return (BWN_TX_PHY_ANT0); + case BWN_ANT1: + return (BWN_TX_PHY_ANT1); + case BWN_ANT2: + return (BWN_TX_PHY_ANT2); + case BWN_ANT3: + return (BWN_TX_PHY_ANT3); + case BWN_ANTAUTO: + return (BWN_TX_PHY_ANT01AUTO); + } + KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); return (0); } +///////// +static void +bwn_set_txantenna(struct bwn_mac *mac, int antenna) +{ + uint16_t ant; + uint16_t tmp=0; + + ant = bwn_ant2phy(antenna); + + /* For ACK/CTS */ +/// tmp = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_ACKCTS_PHYCTL); + tmp = (tmp & ~BWN_TX_PHY_ANT) | ant; + /// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_ACKCTS_PHYCTL, tmp); + + /* For Probe Resposes */ +/// tmp = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_PHYCTL); + tmp = (tmp & ~BWN_TX_PHY_ANT) | ant; + /// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_PHYCTL, tmp); +} + +///////////////// +static void +bwn_set_opmode(struct bwn_mac *mac) +{ + struct brcmwl_softc *sc = mac->mac_sc; + struct ieee80211com *ic = &sc->sc_ic; + uint32_t ctl=0; + uint16_t cfp_pretbtt; + +/// ctl = BWN_READ_4(mac, BWN_MACCTL); + ctl &= ~(BWN_MACCTL_HOSTAP | BWN_MACCTL_PASS_CTL | + BWN_MACCTL_PASS_BADPLCP | BWN_MACCTL_PASS_BADFCS | + BWN_MACCTL_PROMISC | BWN_MACCTL_BEACON_PROMISC); + ctl |= BWN_MACCTL_STA; + + if (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_MBSS) + ctl |= BWN_MACCTL_HOSTAP; + else if (ic->ic_opmode == IEEE80211_M_IBSS) + ctl &= ~BWN_MACCTL_STA; + ctl |= sc->sc_filters; + + ///if ( siba_get_revid(sc->sc_dev) <= 4) + /// ctl |= BWN_MACCTL_PROMISC; + + ////BWN_WRITE_4(mac, BWN_MACCTL, ctl); + + cfp_pretbtt = 2; + /* if ((ctl & BWN_MACCTL_STA) && !(ctl & BWN_MACCTL_HOSTAP)) { + if (siba_get_chipid(sc->sc_dev) == 0x4306 && + /// siba_get_chiprev(sc->sc_dev) == 3) + cfp_pretbtt = 100; + else + cfp_pretbtt = 50; + }*/ + /// BWN_WRITE_2(mac, 0x612, cfp_pretbtt); +} + +////////////////// static int -brcmwl_detach(device_t dev) +bwn_chip_init(struct bwn_mac *mac){ + +/// struct brcmwl_softc *sc = mac->mac_sc; + struct bwn_phy *phy = &mac->mac_phy; + uint32_t macctl; + int error; + + macctl = BWN_MACCTL_IHR_ON | BWN_MACCTL_SHM_ON | BWN_MACCTL_STA; + if (phy->gmode) + macctl |= BWN_MACCTL_GMODE; + ////BWN_WRITE_4(mac, BWN_MACCTL, macctl); + + phy->switch_analog(mac, 1); + error = bwn_phy_init(mac); + if (error) { + /// siba_gpio_set(sc->sc_dev, 0); + return (error); + } + if (phy->set_im) + phy->set_im(mac, BWN_IMMODE_NONE); + if (phy->set_antenna) + phy->set_antenna(mac, BWN_ANT_DEFAULT); + bwn_set_txantenna(mac, BWN_ANT_DEFAULT); + + ///if (phy->type == BWN_PHYTYPE_B) + /// BWN_WRITE_2(mac, 0x005e, BWN_READ_2(mac, 0x005e) | 0x0004); + ////BWN_WRITE_4(mac, 0x0100, 0x01000000); + ///if ( siba_get_revid(sc->sc_dev) < 5) + ////BWN_WRITE_4(mac, 0x010c, 0x01000000); + + ////BWN_WRITE_4(mac, BWN_MACCTL, + /// BWN_READ_4(mac, BWN_MACCTL) & ~BWN_MACCTL_STA); + ////BWN_WRITE_4(mac, BWN_MACCTL, + /// BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_STA); + /// bwn_shm_write_2(mac, BWN_SHARED, 0x0074, 0x0000); + + bwn_set_opmode(mac); +/* if ( siba_get_revid(sc->sc_dev) < 3) { + /// BWN_WRITE_2(mac, 0x060e, 0x0000); + /// BWN_WRITE_2(mac, 0x0610, 0x8000); + /// BWN_WRITE_2(mac, 0x0604, 0x0000); + /// BWN_WRITE_2(mac, 0x0606, 0x0200); + } +else { + ////BWN_WRITE_4(mac, 0x0188, 0x80000000); + ////BWN_WRITE_4(mac, 0x018c, 0x02000000); + }*/ + ////BWN_WRITE_4(mac, BWN_INTR_REASON, 0x00004000); + ////BWN_WRITE_4(mac, BWN_DMA0_INTR_MASK, 0x0001dc00); + ////BWN_WRITE_4(mac, BWN_DMA1_INTR_MASK, 0x0000dc00); + ////BWN_WRITE_4(mac, BWN_DMA2_INTR_MASK, 0x0000dc00); + ////BWN_WRITE_4(mac, BWN_DMA3_INTR_MASK, 0x0001dc00); + ////BWN_WRITE_4(mac, BWN_DMA4_INTR_MASK, 0x0000dc00); + ////BWN_WRITE_4(mac, BWN_DMA5_INTR_MASK, 0x0000dc00); + +/// bwn_mac_phy_clock_set(mac, true); + + /* SIBA powerup */ + /* XXX TODO: BCMA powerup */ + /// BWN_WRITE_2(mac, BWN_POWERUP_DELAY, siba_get_cc_powerdelay(sc->sc_dev)); + return (error); +} + + + +//////////////////////////////////// +static void +bwn_set_txretry(struct bwn_mac *mac, int s, int l) +{ + + ///bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_SHORT_RETRY, MIN(s, 0xf)); + ///bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_LONG_RETRY, MIN(l, 0xf)); +} + +static uint8_t +bwn_plcp_getcck(const uint8_t bitrate) { + + switch (bitrate) { + case BWN_CCK_RATE_1MB: + return (0x0a); + case BWN_CCK_RATE_2MB: + return (0x14); + case BWN_CCK_RATE_5MB: + return (0x37); + case BWN_CCK_RATE_11MB: + return (0x6e); + } + KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); + return (0); +} + +static uint8_t +bwn_plcp_getofdm(const uint8_t bitrate) +{ + + switch (bitrate) { + case BWN_OFDM_RATE_6MB: + return (0xb); + case BWN_OFDM_RATE_9MB: + return (0xf); + case BWN_OFDM_RATE_12MB: + return (0xa); + case BWN_OFDM_RATE_18MB: + return (0xe); + case BWN_OFDM_RATE_24MB: + return (0x9); + case BWN_OFDM_RATE_36MB: + return (0xd); + case BWN_OFDM_RATE_48MB: + return (0x8); + case BWN_OFDM_RATE_54MB: + return (0xc); + } + KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); return (0); } + +////////////////////// +static void +bwn_rate_write(struct bwn_mac *mac, uint16_t rate, int ofdm) +{ + uint16_t offset; + + if (ofdm) { + offset = 0x480; + offset += (bwn_plcp_getofdm(rate) & 0x000f) * 2; + } else { + offset = 0x4c0; + offset += (bwn_plcp_getcck(rate) & 0x000f) * 2; + } + ///bwn_shm_write_2(mac, BWN_SHARED, offset + 0x20, bwn_shm_read_2(mac, BWN_SHARED, offset)); +} + +static void +bwn_rate_init(struct bwn_mac *mac) +{ + + switch (mac->mac_phy.type) { + case BWN_PHYTYPE_A: + case BWN_PHYTYPE_G: + case BWN_PHYTYPE_LP: + case BWN_PHYTYPE_N: + bwn_rate_write(mac, BWN_OFDM_RATE_6MB, 1); + bwn_rate_write(mac, BWN_OFDM_RATE_12MB, 1); + bwn_rate_write(mac, BWN_OFDM_RATE_18MB, 1); + bwn_rate_write(mac, BWN_OFDM_RATE_24MB, 1); + bwn_rate_write(mac, BWN_OFDM_RATE_36MB, 1); + bwn_rate_write(mac, BWN_OFDM_RATE_48MB, 1); + bwn_rate_write(mac, BWN_OFDM_RATE_54MB, 1); + if (mac->mac_phy.type == BWN_PHYTYPE_A) + break; + /* FALLTHROUGH */ + case BWN_PHYTYPE_B: + bwn_rate_write(mac, BWN_CCK_RATE_1MB, 0); + bwn_rate_write(mac, BWN_CCK_RATE_2MB, 0); + bwn_rate_write(mac, BWN_CCK_RATE_5MB, 0); + bwn_rate_write(mac, BWN_CCK_RATE_11MB, 0); + break; + default: + KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); + } +} + +//////////////////////////////////// +static void +bwn_set_phytxctl(struct bwn_mac *mac) +{ + uint16_t ctl; + + ctl = (BWN_TX_PHY_ENC_CCK | BWN_TX_PHY_ANT01AUTO | BWN_TX_PHY_TXPWR); + ///bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_BEACON_PHYCTL, ctl); + ///bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_ACKCTS_PHYCTL, ctl); + ///bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_PHYCTL, ctl); +} + +/////////////////////// +static void +bwn_key_macwrite(struct bwn_mac *mac, uint8_t index, const uint8_t *addr){ + + ///struct brcmwl_softc *sc = mac->mac_sc; + uint32_t addrtmp[2] = { 0, 0 }; + uint8_t start = 8; + + if (BWN_SEC_NEWAPI(mac)) + start = 4; + + KASSERT(index >= start, + ("%s:%d: fail", __func__, __LINE__)); + index -= start; + + if (addr) { + addrtmp[0] = addr[0]; + addrtmp[0] |= ((uint32_t) (addr[1]) << 8); + addrtmp[0] |= ((uint32_t) (addr[2]) << 16); + addrtmp[0] |= ((uint32_t) (addr[3]) << 24); + addrtmp[1] = addr[4]; + addrtmp[1] |= ((uint32_t) (addr[5]) << 8); + } + +/* if (siba_get_revid(sc->sc_dev) >= 5) { + bwn_shm_write_4(mac, BWN_RCMTA, (index * 2) + 0, addrtmp[0]); + bwn_shm_write_2(mac, BWN_RCMTA, (index * 2) + 1, addrtmp[1]); + } else { + if (index >= 8) { + bwn_shm_write_4(mac, BWN_SHARED, + BWN_SHARED_PSM + (index * 6) + 0, addrtmp[0]); + bwn_shm_write_2(mac, BWN_SHARED, + BWN_SHARED_PSM + (index * 6) + 4, addrtmp[1]); + } + }*/ +} + +/////////////// +static void +bwn_key_write(struct bwn_mac *mac, uint8_t index, uint8_t algorithm, + const uint8_t *key) +{ + unsigned int i; + uint32_t offset; + uint16_t kidx, value; + + kidx = BWN_SEC_KEY2FW(mac, index); +/// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_KEYIDX_BLOCK + (kidx * 2), (kidx << 4) | algorithm); + + offset = mac->mac_ktp + (index * BWN_SEC_KEYSIZE); + for (i = 0; i < BWN_SEC_KEYSIZE; i += 2) { + value = key[i]; + value |= (uint16_t)(key[i + 1]) << 8; + ///bwn_shm_write_2(mac, BWN_SHARED, offset + i, value); + } +} + + +static void +bwn_key_dowrite(struct bwn_mac *mac, + uint8_t index, + uint8_t algorithm, + const uint8_t *key, + size_t key_len, + const uint8_t *mac_addr) +{ + uint8_t buf[BWN_SEC_KEYSIZE] = { 0, }; + uint8_t per_sta_keys_start = 8; + + if (BWN_SEC_NEWAPI(mac)) + per_sta_keys_start = 4; + + KASSERT(index < mac->mac_max_nr_keys, ("%s:%d: fail", __func__, __LINE__)); + KASSERT(key_len <= BWN_SEC_KEYSIZE, ("%s:%d: fail", __func__, __LINE__)); + + if (index >= per_sta_keys_start) + bwn_key_macwrite(mac, index, NULL); + if (key) + memcpy(buf, key, key_len); + bwn_key_write(mac, index, algorithm, buf); + if (index >= per_sta_keys_start) + bwn_key_macwrite(mac, index, mac_addr); + + mac->mac_key[index].algorithm = algorithm; +} + +static void +bwn_clear_keys(struct bwn_mac *mac) +{ + int i; + + for (i = 0; i < mac->mac_max_nr_keys; i++) { + KASSERT(i >= 0 && i < mac->mac_max_nr_keys, ("%s:%d: fail", __func__, __LINE__)); + + bwn_key_dowrite(mac, i, BWN_SEC_ALGO_NONE, NULL, BWN_SEC_KEYSIZE, NULL); + if ((i <= 3) && !BWN_SEC_NEWAPI(mac)) { + bwn_key_dowrite(mac, i + 4, BWN_SEC_ALGO_NONE, NULL, BWN_SEC_KEYSIZE, NULL); + } + mac->mac_key[i].keyconf = NULL; + } +} + +//////////////////////////////////// +static void +bwn_crypt_init(struct bwn_mac *mac) +{ + /*struct brcmwl_softc *sc = mac->mac_sc; + + mac->mac_max_nr_keys = (siba_get_revid(sc->sc_dev) >= 5) ? 58 : 20; + KASSERT(mac->mac_max_nr_keys <= N(mac->mac_key), ("%s:%d: fail", __func__, __LINE__)); + mac->mac_ktp = bwn_shm_read_2(mac, BWN_SHARED, BWN_SHARED_KEY_TABLEP); + mac->mac_ktp *= 2; + if (siba_get_revid(sc->sc_dev) >= 5) + BWN_WRITE_2(mac, BWN_RCMTA_COUNT, mac->mac_max_nr_keys - 8);*/ + bwn_clear_keys(mac); +} + + +//////////////////////////////////// +static int +bwn_core_init(struct bwn_mac *mac) +{ + struct brcmwl_softc *sc = mac->mac_sc; + uint64_t hf=0; + int error; + +printf("bwn_core_init \n"); + + KASSERT(mac->mac_status == BWN_MAC_STATUS_UNINIT, ("%s:%d: fail", __func__, __LINE__)); + + DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: called\n", __func__); + +/// siba_powerup(sc->sc_dev, 0); +/// if (!siba_dev_isup(sc->sc_dev)) + ///bwn_reset_core(mac, mac->mac_phy.gmode); + + mac->mac_flags &= ~BWN_MAC_FLAG_DFQVALID; + mac->mac_flags |= BWN_MAC_FLAG_RADIO_ON; + mac->mac_phy.hwpctl = (bwn_hwpctl) ? 1 : 0; + BWN_GETTIME(mac->mac_phy.nexttime); + mac->mac_phy.txerrors = BWN_TXERROR_MAX; + bzero(&mac->mac_stats, sizeof(mac->mac_stats)); + mac->mac_stats.link_noise = -95; + mac->mac_reason_intr = 0; + bzero(mac->mac_reason, sizeof(mac->mac_reason)); + mac->mac_intr_mask = BWN_INTR_MASKTEMPLATE; +#ifdef BWN_DEBUG + if (sc->sc_debug & BWN_DEBUG_XMIT) + mac->mac_intr_mask &= ~BWN_INTR_PHY_TXERR; +#endif + mac->mac_suspended = 1; + mac->mac_task_state = 0; + memset(&mac->mac_noise, 0, sizeof(mac->mac_noise)); + + mac->mac_phy.init_pre(mac); + +/// siba_pcicore_intr(sc->sc_dev); + +/// siba_fix_imcfglobug(sc->sc_dev); + bwn_bt_disable(mac); + if (mac->mac_phy.prepare_hw) { + error = mac->mac_phy.prepare_hw(mac); + if (error) + goto fail0; + } + DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: chip_init\n", __func__); + error = bwn_chip_init(mac); + if (error) + goto fail0; + /// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_COREREV, + /// siba_get_revid(sc->sc_dev)); + /// hf = bwn_hf_read(mac); + if (mac->mac_phy.type == BWN_PHYTYPE_G) { + hf |= BWN_HF_GPHY_SYM_WORKAROUND; + /// if (siba_sprom_get_bf_lo(sc->sc_dev) & BWN_BFL_PACTRL) + /// hf |= BWN_HF_PAGAINBOOST_OFDM_ON; + if (mac->mac_phy.rev == 1) + hf |= BWN_HF_GPHY_DC_CANCELFILTER; + } + if (mac->mac_phy.rf_ver == 0x2050) { + if (mac->mac_phy.rf_rev < 6) + hf |= BWN_HF_FORCE_VCO_RECALC; + if (mac->mac_phy.rf_rev == 6) + hf |= BWN_HF_4318_TSSI; + } +/// if (siba_sprom_get_bf_lo(sc->sc_dev) & BWN_BFL_CRYSTAL_NOSLOW) +/// hf |= BWN_HF_SLOWCLOCK_REQ_OFF; +/// if ((siba_get_type(sc->sc_dev) == SIBA_TYPE_PCI) && + /// (siba_get_pcicore_revid(sc->sc_dev) <= 10)) + /// hf |= BWN_HF_PCI_SLOWCLOCK_WORKAROUND; + hf &= ~BWN_HF_SKIP_CFP_UPDATE; + /// bwn_hf_write(mac, hf); + + /* Tell the firmware about the MAC capabilities */ +/* if ( siba_get_revid(sc->sc_dev) >= 13) { + uint32_t cap; + cap = /// BWN_READ_4(mac, BWN_MAC_HW_CAP); + /// DPRINTF(sc, BWN_DEBUG_RESET, + "%s: hw capabilities: 0x%08x\n", + __func__, cap); + /// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_MACHW_L, + cap & 0xffff); + /// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_MACHW_H, + (cap >> 16) & 0xffff); + }*/ + + bwn_set_txretry(mac, BWN_RETRY_SHORT, BWN_RETRY_LONG); + /// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_SHORT_RETRY_FALLBACK, 3); + /// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_LONG_RETRY_FALLBACK, 2); + /// bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PROBE_RESP_MAXTIME, 1); + + bwn_rate_init(mac); + bwn_set_phytxctl(mac); + + /// bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_CONT_MIN, (mac->mac_phy.type == BWN_PHYTYPE_B) ? 0x1f : 0xf); + /// bwn_shm_write_2(mac, BWN_SCRATCH, BWN_SCRATCH_CONT_MAX, 0x3ff); + +/* if ( siba_get_type(sc->sc_dev) == SIBA_TYPE_PCMCIA || bwn_usedma == 0) + bwn_pio_init(mac); + else + bwn_dma_init(mac);*/ +/// bwn_bt_enable(mac); + + DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: powerup\n", __func__); +/// siba_powerup(sc->sc_dev, !(siba_sprom_get_bf_lo(sc->sc_dev) & BWN_BFL_CRYSTAL_NOSLOW)); + bwn_set_macaddr(mac); + bwn_crypt_init(mac); + + /* XXX LED initializatin */ + + mac->mac_status = BWN_MAC_STATUS_INITED; + + DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: done\n", __func__); + return (error); + +fail0: +//// siba_powerdown(sc->sc_dev); + KASSERT(mac->mac_status == BWN_MAC_STATUS_UNINIT, + ("%s:%d: fail", __func__, __LINE__)); + DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: fail\n", __func__); + return (error); +} + + +static void +bwn_restart(struct bwn_mac *mac, const char *msg) +{ + struct brcmwl_softc *sc = mac->mac_sc; + struct ieee80211com *ic = &sc->sc_ic; + + if (mac->mac_status < BWN_MAC_STATUS_INITED) + return; + + device_printf(sc->sc_dev, "HW reset: %s\n", msg); + ieee80211_runtask(ic, &mac->mac_hwreset); +} + +////////////////////// +static void +bwn_task_15s(struct bwn_mac *mac){ + + uint16_t reg; + + if (mac->mac_fw.opensource) { + ///reg = bwn_shm_read_2(mac, BWN_SCRATCH, BWN_WATCHDOG_REG); + reg = 0; + if (reg) { + bwn_restart(mac, "fw watchdog"); + return; + } + ////bwn_shm_write_2(mac, BWN_SCRATCH, BWN_WATCHDOG_REG, 1); + } + if (mac->mac_phy.task_15s) + mac->mac_phy.task_15s(mac); + + mac->mac_phy.txerrors = BWN_TXERROR_MAX; +} + +static void +bwn_task_30s(struct bwn_mac *mac){ + + if (mac->mac_phy.type != BWN_PHYTYPE_G || mac->mac_noise.noi_running) + return; + mac->mac_noise.noi_running = 1; + mac->mac_noise.noi_nsamples = 0; + + bwn_noise_gensample(mac); +} + +static void +bwn_task_60s(struct bwn_mac *mac){ + + if (mac->mac_phy.task_60s) + mac->mac_phy.task_60s(mac); + bwn_phy_txpower_check(mac, BWN_TXPWR_IGNORE_TIME); +} + +static void +bwn_tasks(void *arg){ + + struct bwn_mac *mac = arg; + struct brcmwl_softc *sc = mac->mac_sc; + + BWN_ASSERT_LOCKED(sc); + if (mac->mac_status != BWN_MAC_STATUS_STARTED) + //// printf(" != BWN_MAC_STATUS_STARTED \n"); + return; + + if (mac->mac_task_state % 4 == 0) + bwn_task_60s(mac); /// bwn_phy_txpower_check + if (mac->mac_task_state % 2 == 0) + bwn_task_30s(mac); /// bwn_noise_gensample + bwn_task_15s(mac); + + uint8_t clkctl, devctl; + uint32_t intrstat, hostintr; + int ret; + + if (!sc->sr_enabled && sc->clkstate == CLK_PENDING) { + ret = brcmwl_sdiobus_read_1(sc, 1, SBSDIO_FUNC1_CHIPCLKCSR, &clkctl); + if (BRCMWL_SDIO_FUNC1_CHIPCLKCSR_HTAV(clkctl)) { + ret = brcmwl_sdiobus_read_1(sc, 1, SBSDIO_DEVICE_CTL, &devctl); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + ret = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_DEVICE_CTL, devctl); + sc->clkstate = CLK_AVAIL; + } + } + + // read intr statu , clear status and operate + ret = brcmwl_sdiodev_read(sc, SDPCMD_INTSTATUS, &intrstat); + intrstat &= (SDPCMD_INTSTATUS_HMB_SW_MASK | SDPCMD_INTSTATUS_CHIPACTIVE); + //// XXX fc state + if (intrstat) + ret = brcmwl_sdiodev_write(sc, SDPCMD_INTSTATUS, intrstat); + + if (intrstat & SDPCMD_INTSTATUS_HMB_HOST_INT) { + ret = brcmwl_sdiodev_read(sc, SDPCMD_TOHOSTMAILBOXDATA, &hostintr); + ret = brcmwl_sdiodev_write(sc, SDPCMD_TOSBMAILBOX, SDPCMD_TOSBMAILBOX_INT_ACK); + if (hostintr & SDPCMD_TOHOSTMAILBOXDATA_NAKHANDLED) + intrstat |= SDPCMD_INTSTATUS_HMB_FRAME_IND; + + if (hostintr & SDPCMD_TOHOSTMAILBOXDATA_DEVREADY || hostintr & SDPCMD_TOHOSTMAILBOXDATA_FWREADY) + brcmwl_sdio_readshared(sc); + } + + /* FIXME: Might stall if we don't when not set. */ + if (1 || intrstat & SDPCMD_INTSTATUS_HMB_FRAME_IND) { + brcmwl_sdio_rx_frames(sc); + } + + /*if (!ml_empty(&sc->sc_tx_queue)) { + brcmwl_sdio_tx_frames(sc); + }*/ + + printf("bwn_tasks exit intrstat %x \n", intrstat); + mac->mac_task_state++; + callout_reset(&sc->sc_task_ch, hz * 15, bwn_tasks, mac); /// re call bwn_tasks + +} + +///////////////////////// +static void +bwn_core_start(struct bwn_mac *mac){ + + struct brcmwl_softc *sc = mac->mac_sc; +/// uint32_t tmp; + + KASSERT(mac->mac_status == BWN_MAC_STATUS_INITED, + ("%s:%d: fail", __func__, __LINE__)); + +/// if ( siba_get_revid(sc->sc_dev) < 5) +/// return; + +/// while (1) { + /// tmp = BWN_READ_4(mac, BWN_XMITSTAT_0); + /// if (!(tmp & 0x00000001)) + /// break; + /// tmp = BWN_READ_4(mac, BWN_XMITSTAT_1); +/// } + +/// bwn_mac_enable(mac); + ////BWN_WRITE_4(mac, BWN_INTR_MASK, mac->mac_intr_mask); + callout_reset(&sc->sc_task_ch, hz * 15, bwn_tasks, mac); // callback bwn_tasks + + mac->mac_status = BWN_MAC_STATUS_STARTED; +} + +static void +bwn_pio_cancel_tx_packets(struct bwn_pio_txqueue *tq) +{ + struct bwn_pio_txpkt *tp; + unsigned int i; + + for (i = 0; i < N(tq->tq_pkts); i++) { + tp = &(tq->tq_pkts[i]); + if (tp->tp_m) { + m_freem(tp->tp_m); + tp->tp_m = NULL; + } + } +} + +static void +bwn_destroy_pioqueue_tx(struct bwn_pio_txqueue *tq) +{ + if (tq == NULL) + return; + bwn_pio_cancel_tx_packets(tq); +} + + +static void +bwn_destroy_queue_tx(struct bwn_pio_txqueue *pio) +{ + + bwn_destroy_pioqueue_tx(pio); +} + +static void +bwn_pio_stop(struct bwn_mac *mac) +{ + struct bwn_pio *pio; + + if (mac->mac_flags & BWN_MAC_FLAG_DMA) + return; + pio = &mac->mac_method.pio; + + bwn_destroy_queue_tx(&pio->mcast); + bwn_destroy_queue_tx(&pio->wme[WME_AC_VO]); + bwn_destroy_queue_tx(&pio->wme[WME_AC_VI]); + bwn_destroy_queue_tx(&pio->wme[WME_AC_BE]); + bwn_destroy_queue_tx(&pio->wme[WME_AC_BK]); +} + +static void +bwn_phy_exit(struct bwn_mac *mac) +{ + + mac->mac_phy.rf_onoff(mac, 0); + if (mac->mac_phy.exit != NULL) + mac->mac_phy.exit(mac); +} + +/////////// fix ///////////////////// +static void +bwn_chip_exit(struct bwn_mac *mac) +{ +/// struct brcmwl_softc *sc = mac->mac_sc; + + bwn_phy_exit(mac); + ///siba_gpio_set(sc->sc_dev, 0); +} + +////////////////////////////// +static void +bwn_core_exit(struct bwn_mac *mac) +{ +/// struct brcmwl_softc *sc = mac->mac_sc; +// uint32_t macctl; + + BWN_ASSERT_LOCKED(mac->mac_sc); + + KASSERT(mac->mac_status <= BWN_MAC_STATUS_INITED, + ("%s:%d: fail", __func__, __LINE__)); + + if (mac->mac_status != BWN_MAC_STATUS_INITED) + return; + mac->mac_status = BWN_MAC_STATUS_UNINIT; + +/* macctl = BWN_READ_4(mac, BWN_MACCTL); +/// macctl &= ~BWN_MACCTL_MCODE_RUN; + macctl |= BWN_MACCTL_MCODE_JMP0; + ////BWN_WRITE_4(mac, BWN_MACCTL, macctl); +*/ +/// bwn_dma_stop(mac); + bwn_pio_stop(mac); + bwn_chip_exit(mac); + mac->mac_phy.switch_analog(mac, 0); +//// siba_dev_down(sc->sc_dev, 0); +/// siba_powerdown(sc->sc_dev); +} + +static void +bwn_bt_disable(struct bwn_mac *mac) +{ + struct brcmwl_softc *sc = mac->mac_sc; + + (void)sc; + /* XXX do nothing yet */ +} + + +///////////////////////// +static void +bwn_set_pretbtt(struct bwn_mac *mac) +{ +/* struct brcmwl_softc *sc = mac->mac_sc; + struct ieee80211com *ic = &sc->sc_ic; + uint16_t pretbtt; + + if (ic->ic_opmode == IEEE80211_M_IBSS) + pretbtt = 2; + else + pretbtt = (mac->mac_phy.type == BWN_PHYTYPE_A) ? 120 : 250; + bwn_shm_write_2(mac, BWN_SHARED, BWN_SHARED_PRETBTT, pretbtt); + BWN_WRITE_2(mac, BWN_TSF_CFP_PRETBTT, pretbtt);*/ +} + +////////////////////// +static void +bwn_rfswitch(void *arg) +{ + struct brcmwl_softc *sc = arg; + struct bwn_mac *mac = sc->sc_curmac; + int cur = 0, prev = 0; + + KASSERT(mac->mac_status >= BWN_MAC_STATUS_STARTED, ("%s: invalid MAC status %d", __func__, mac->mac_status)); + + /*if (mac->mac_phy.rev >= 3 || + mac->mac_phy.type == BWN_PHYTYPE_LP || + mac->mac_phy.type == BWN_PHYTYPE_N) { + if (!(BWN_READ_4(mac, BWN_RF_HWENABLED_HI) & BWN_RF_HWENABLED_HI_MASK)) + cur = 1; + } + else { + if (BWN_READ_2(mac, BWN_RF_HWENABLED_LO) & BWN_RF_HWENABLED_LO_MASK) + cur = 1; + }*/ + + if (mac->mac_flags & BWN_MAC_FLAG_RADIO_ON) + prev = 1; + + DPRINTF(sc, BWN_DEBUG_RESET, "%s: called; cur=%d, prev=%d\n", __func__, cur, prev); + + if (cur != prev) { + if (cur) + mac->mac_flags |= BWN_MAC_FLAG_RADIO_ON; + else + mac->mac_flags &= ~BWN_MAC_FLAG_RADIO_ON; + + device_printf(sc->sc_dev, + "status of RF switch is changed to %s\n", + cur ? "ON" : "OFF"); + + /*if (cur != mac->mac_phy.rf_on) { + if (cur) + bwn_rf_turnon(mac); + else + bwn_rf_turnoff(mac); + }*/ + } + + callout_schedule(&sc->sc_rfswitch_ch, hz); +} + +static void +bwn_watchdog(void *arg) +{ + struct brcmwl_softc *sc = arg; + + if (sc->sc_watchdog_timer != 0 && --sc->sc_watchdog_timer == 0) { + device_printf(sc->sc_dev, "device timeout\n"); + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + } + callout_schedule(&sc->sc_watchdog_ch, hz); +} + +static void +bwn_wme_clear(struct brcmwl_softc *sc) +{ +#define MS(_v, _f) (((_v) & _f) >> _f##_S) + struct wmeParams *p; + unsigned int i; + + KASSERT(N(bwn_wme_shm_offsets) == N(sc->sc_wmeParams), + ("%s:%d: fail", __func__, __LINE__)); + + for (i = 0; i < N(sc->sc_wmeParams); i++) { + p = &(sc->sc_wmeParams[i]); + + switch (bwn_wme_shm_offsets[i]) { + case BWN_WME_VOICE: + p->wmep_txopLimit = 0; + p->wmep_aifsn = 2; + /* XXX FIXME: log2(cwmin) */ + p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN); + p->wmep_logcwmax = MS(0x0001, WME_PARAM_LOGCWMAX); + break; + case BWN_WME_VIDEO: + p->wmep_txopLimit = 0; + p->wmep_aifsn = 2; + /* XXX FIXME: log2(cwmin) */ + p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN); + p->wmep_logcwmax = MS(0x0001, WME_PARAM_LOGCWMAX); + break; + case BWN_WME_BESTEFFORT: + p->wmep_txopLimit = 0; + p->wmep_aifsn = 3; + /* XXX FIXME: log2(cwmin) */ + p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN); + p->wmep_logcwmax = MS(0x03ff, WME_PARAM_LOGCWMAX); + break; + case BWN_WME_BACKGROUND: + p->wmep_txopLimit = 0; + p->wmep_aifsn = 7; + /* XXX FIXME: log2(cwmin) */ + p->wmep_logcwmin = MS(0x0001, WME_PARAM_LOGCWMIN); + p->wmep_logcwmax = MS(0x03ff, WME_PARAM_LOGCWMAX); + break; + default: + KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); + } + } +} +///////////////////////// +static int +bwn_init(struct brcmwl_softc *sc) +{ + struct bwn_mac *mac; + int error; + + BWN_ASSERT_LOCKED(sc); + + DPRINTF(sc, BWN_DEBUG_RESET, "%s: called\n", __func__); + + bzero(sc->sc_bssid, IEEE80211_ADDR_LEN); + sc->sc_flags |= BWN_FLAG_NEED_BEACON_TP; + sc->sc_filters = 0; + bwn_wme_clear(sc); + sc->sc_beacons[0] = sc->sc_beacons[1] = 0; + sc->sc_rf_enabled = 1; + + mac = sc->sc_curmac; + if (mac->mac_status == BWN_MAC_STATUS_UNINIT) { + error = bwn_core_init(mac); + if (error != 0) + return (error); + } + if (mac->mac_status == BWN_MAC_STATUS_INITED) + bwn_core_start(mac); + + bwn_set_opmode(mac); + bwn_set_pretbtt(mac); + /// bwn_spu_setdelay(mac, 0); + bwn_set_macaddr(mac); + + sc->sc_flags |= BWN_FLAG_RUNNING; + callout_reset(&sc->sc_rfswitch_ch, hz, bwn_rfswitch, sc); // calback bwn_rfswitch + callout_reset(&sc->sc_watchdog_ch, hz, bwn_watchdog, sc); // callback bwn_watchdog + + return (0); +} + +////////////////////////////// +void +bwn_psctl(struct bwn_mac *mac, uint32_t flags) +{ + /*struct brcmwl_softc *sc = mac->mac_sc; + int i; + uint16_t ucstat;*/ + + KASSERT(!((flags & BWN_PS_ON) && (flags & BWN_PS_OFF)), + ("%s:%d: fail", __func__, __LINE__)); + KASSERT(!((flags & BWN_PS_AWAKE) && (flags & BWN_PS_ASLEEP)), + ("%s:%d: fail", __func__, __LINE__)); + + /* XXX forcibly awake and hwps-off */ + + /*BWN_WRITE_4(mac, BWN_MACCTL, + (BWN_READ_4(mac, BWN_MACCTL) | BWN_MACCTL_AWAKE) & + ~BWN_MACCTL_HWPS); + BWN_READ_4(mac, BWN_MACCTL); + if (siba_get_revid(sc->sc_dev) >= 5) { + for (i = 0; i < 100; i++) { + ucstat = bwn_shm_read_2(mac, BWN_SHARED, + BWN_SHARED_UCODESTAT); + if (ucstat != BWN_SHARED_UCODESTAT_SLEEP) + break; + DELAY(10); + } + }*/ + DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: ucstat=%d\n", __func__, ucstat); +} + +////////////////////////////// +void +bwn_mac_suspend(struct bwn_mac *mac) +{ + struct brcmwl_softc *sc = mac->mac_sc; + ////int i; +/// uint32_t tmp; + + KASSERT(mac->mac_suspended >= 0, + ("%s:%d: fail", __func__, __LINE__)); + + DPRINTF(mac->mac_sc, BWN_DEBUG_RESET, "%s: suspended=%d\n", __func__, mac->mac_suspended); + + if (mac->mac_suspended == 0) { + bwn_psctl(mac, BWN_PS_AWAKE); + /*BWN_WRITE_4(mac, BWN_MACCTL, + BWN_READ_4(mac, BWN_MACCTL) + & ~BWN_MACCTL_ON); + BWN_READ_4(mac, BWN_MACCTL); + for (i = 35; i; i--) { + tmp = BWN_READ_4(mac, BWN_INTR_REASON); + if (tmp & BWN_INTR_MAC_SUSPENDED) + goto out; + DELAY(10); + } + for (i = 40; i; i--) { + tmp = BWN_READ_4(mac, BWN_INTR_REASON); + if (tmp & BWN_INTR_MAC_SUSPENDED) + goto out; + DELAY(1000); + }*/ + device_printf(sc->sc_dev, "MAC suspend failed\n"); + } +///out: +/// mac->mac_suspended++; +} + +////////////////////////////////// +static void +bwn_core_stop(struct bwn_mac *mac) +{ + struct brcmwl_softc *sc = mac->mac_sc; + + BWN_ASSERT_LOCKED(sc); + + if (mac->mac_status < BWN_MAC_STATUS_STARTED) + return; + + callout_stop(&sc->sc_rfswitch_ch); + callout_stop(&sc->sc_task_ch); + callout_stop(&sc->sc_watchdog_ch); + sc->sc_watchdog_timer = 0; + /*BWN_WRITE_4(mac, BWN_INTR_MASK, 0); + BWN_READ_4(mac, BWN_INTR_MASK);*/ + bwn_mac_suspend(mac); + + mac->mac_status = BWN_MAC_STATUS_INITED; +} + +//////////////// +static void +bwn_set_macaddr(struct bwn_mac *mac) +{ + + /*bwn_mac_write_bssid(mac); + bwn_mac_setfilter(mac, BWN_MACFILTER_SELF, mac->mac_sc->sc_ic.ic_macaddr);*/ +} + +static void +bwn_stop(struct brcmwl_softc *sc) +{ + struct bwn_mac *mac = sc->sc_curmac; + + BWN_ASSERT_LOCKED(sc); + + DPRINTF(sc, BWN_DEBUG_RESET, "%s: called\n", __func__); + + if (mac->mac_status >= BWN_MAC_STATUS_INITED) { + /* XXX FIXME opmode not based on VAP */ + bwn_set_opmode(mac); + bwn_set_macaddr(mac); + } + + if (mac->mac_status >= BWN_MAC_STATUS_STARTED) + bwn_core_stop(mac); + + ///callout_stop(&sc->sc_led_blink_ch); +//// sc->sc_led_blinking = 0; + + bwn_core_exit(mac); + sc->sc_rf_enabled = 0; + + sc->sc_flags &= ~BWN_FLAG_RUNNING; +} + +static void +bwn_parent(struct ieee80211com *ic) +{ + struct brcmwl_softc *sc = ic->ic_softc; + int startall = 0; + + BWN_LOCK(sc); + if (ic->ic_nrunning > 0) { + if ((sc->sc_flags & BWN_FLAG_RUNNING) == 0) { + bwn_init(sc); + startall = 1; + } + //else + /// bwn_update_promisc(ic); + } else if (sc->sc_flags & BWN_FLAG_RUNNING) + bwn_stop(sc); + BWN_UNLOCK(sc); + + if (startall) + ieee80211_start_all(ic); +} + +static int +bwn_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct brcmwl_softc *sc = ic->ic_softc; + int error; + + BWN_LOCK(sc); + if ((sc->sc_flags & BWN_FLAG_RUNNING) == 0) { + BWN_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + BWN_UNLOCK(sc); + return (error); + } + bwn_start(sc); + BWN_UNLOCK(sc); + return (0); +} + +static struct bwn_pio_txqueue * +bwn_pio_select(struct bwn_mac *mac, uint8_t prio) +{ + + if ((mac->mac_flags & BWN_MAC_FLAG_WME) == 0) + return (&mac->mac_method.pio.wme[WME_AC_BE]); + + switch (prio) { + case 0: + return (&mac->mac_method.pio.wme[WME_AC_BE]); + case 1: + return (&mac->mac_method.pio.wme[WME_AC_BK]); + case 2: + return (&mac->mac_method.pio.wme[WME_AC_VI]); + case 3: + return (&mac->mac_method.pio.wme[WME_AC_VO]); + } + KASSERT(0 == 1, ("%s:%d: fail", __func__, __LINE__)); + return (NULL); +} + +/////////////////////// +static int +bwn_pio_tx_start(struct bwn_mac *mac, struct ieee80211_node *ni, struct mbuf *m) +{ +/* struct bwn_pio_txpkt *tp; + struct bwn_pio_txqueue *tq = bwn_pio_select(mac, M_WME_GETAC(m)); + struct brcmwl_softc *sc = mac->mac_sc; + struct bwn_txhdr txhdr; + struct mbuf *m_new; + uint32_t ctl32; + int error; + uint16_t ctl16; + + BWN_ASSERT_LOCKED(sc); + + /// XXX TODO send packets after DTIM + + KASSERT(!TAILQ_EMPTY(&tq->tq_pktlist), ("%s: fail", __func__)); + tp = TAILQ_FIRST(&tq->tq_pktlist); + tp->tp_ni = ni; + tp->tp_m = m; + + error = bwn_set_txhdr(mac, ni, m, &txhdr, BWN_PIO_COOKIE(tq, tp)); + if (error) { + device_printf(sc->sc_dev, "tx fail\n"); + return (error); + } + + TAILQ_REMOVE(&tq->tq_pktlist, tp, tp_list); + tq->tq_used += roundup(m->m_pkthdr.len + BWN_HDRSIZE(mac), 4); + tq->tq_free--; + + if (siba_get_revid(sc->sc_dev) >= 8) { + //// + /// XXX please removes m_defrag(9) + /// + m_new = m_defrag(m, M_NOWAIT); + if (m_new == NULL) { + device_printf(sc->sc_dev, + "%s: can't defrag TX buffer\n", + __func__); + return (ENOBUFS); + } + if (m_new->m_next != NULL) + device_printf(sc->sc_dev, + "TODO: fragmented packets for PIO\n"); + tp->tp_m = m_new; + + ///send HEADER + ctl32 = bwn_pio_write_multi_4(mac, tq, + (BWN_PIO_READ_4(mac, tq, BWN_PIO8_TXCTL) | + BWN_PIO8_TXCTL_FRAMEREADY) & ~BWN_PIO8_TXCTL_EOF, + (const uint8_t *)&txhdr, BWN_HDRSIZE(mac)); + /// send BODY + ctl32 = bwn_pio_write_multi_4(mac, tq, ctl32, + mtod(m_new, const void *), m_new->m_pkthdr.len); + bwn_pio_write_4(mac, tq, BWN_PIO_TXCTL, + ctl32 | BWN_PIO8_TXCTL_EOF); + } + else { + ctl16 = bwn_pio_write_multi_2(mac, tq, + (bwn_pio_read_2(mac, tq, BWN_PIO_TXCTL) | + BWN_PIO_TXCTL_FRAMEREADY) & ~BWN_PIO_TXCTL_EOF, + (const uint8_t *)&txhdr, BWN_HDRSIZE(mac)); + ctl16 = bwn_pio_write_mbuf_2(mac, tq, ctl16, m); + BWN_PIO_WRITE_2(mac, tq, BWN_PIO_TXCTL, + ctl16 | BWN_PIO_TXCTL_EOF); + }*/ + + return (0); +} + +static int +bwn_tx_isfull(struct brcmwl_softc *sc, struct mbuf *m){ + + ///struct bwn_dma_ring *dr; + struct bwn_mac *mac = sc->sc_curmac; + struct bwn_pio_txqueue *tq; + int pktlen = roundup(m->m_pkthdr.len + BWN_HDRSIZE(mac), 4); + + BWN_ASSERT_LOCKED(sc); + + /*if (mac->mac_flags & BWN_MAC_FLAG_DMA) { + dr = bwn_dma_select(mac, M_WME_GETAC(m)); + if (dr->dr_stop == 1 || + bwn_dma_freeslot(dr) < BWN_TX_SLOTS_PER_FRAME) { + dr->dr_stop = 1; + goto full; + } + } + else*/ { + tq = bwn_pio_select(mac, M_WME_GETAC(m)); + if (tq->tq_free == 0 || pktlen > tq->tq_size || + pktlen > (tq->tq_size - tq->tq_used)) + goto full; + } + return (0); +full: + mbufq_prepend(&sc->sc_snd, m); + return (1); +} + +static int +bwn_tx_start(struct brcmwl_softc *sc, struct ieee80211_node *ni, struct mbuf *m) +{ + struct bwn_mac *mac = sc->sc_curmac; + int error; + + BWN_ASSERT_LOCKED(sc); + + if (m->m_pkthdr.len < IEEE80211_MIN_LEN || mac == NULL) { + m_freem(m); + return (ENXIO); + } + + ///error = (mac->mac_flags & BWN_MAC_FLAG_DMA) ? bwn_dma_tx_start(mac, ni, m) : bwn_pio_tx_start(mac, ni, m); + error = bwn_pio_tx_start(mac, ni, m); + if (error) { + m_freem(m); + return (error); + } + return (0); +} + +static void +bwn_start(struct brcmwl_softc *sc) +{ + struct bwn_mac *mac = sc->sc_curmac; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct ieee80211_key *k; + struct mbuf *m; + + BWN_ASSERT_LOCKED(sc); + + if ((sc->sc_flags & BWN_FLAG_RUNNING) == 0 || mac == NULL || + mac->mac_status < BWN_MAC_STATUS_STARTED) + return; + + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + if (bwn_tx_isfull(sc, m)) + break; + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + if (ni == NULL) { + device_printf(sc->sc_dev, "unexpected NULL ni\n"); + m_freem(m); + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + continue; + } + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + m_freem(m); + continue; + } + } + wh = NULL; /* Catch any invalid use */ + if (bwn_tx_start(sc, ni, m) != 0) { + if (ni != NULL) { + if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + } + continue; + } + sc->sc_watchdog_timer = 5; + } +} + +static int +bwn_is_valid_ether_addr(uint8_t *addr) +{ + char zero_addr[6] = { 0, 0, 0, 0, 0, 0 }; + + if ((addr[0] & 1) || (!bcmp(addr, zero_addr, ETHER_ADDR_LEN))) + return (FALSE); + + return (TRUE); +} + +///////////////////////// +static void +bwn_intrtask(void *arg, int npending) +{ + struct bwn_mac *mac = arg; + struct brcmwl_softc *sc = mac->mac_sc; + uint32_t merged = 0; + int i, tx = 0, rx = 0; + + BWN_LOCK(sc); + if (mac->mac_status < BWN_MAC_STATUS_STARTED || + (sc->sc_flags & BWN_FLAG_INVALID)) { + BWN_UNLOCK(sc); + return; + } + + for (i = 0; i < N(mac->mac_reason); i++) + merged |= mac->mac_reason[i]; + + if (mac->mac_reason_intr & BWN_INTR_MAC_TXERR) + device_printf(sc->sc_dev, "MAC trans error\n"); + + if (mac->mac_reason_intr & BWN_INTR_PHY_TXERR) { + DPRINTF(sc, BWN_DEBUG_INTR, "%s: PHY trans error\n", __func__); + mac->mac_phy.txerrors--; + if (mac->mac_phy.txerrors == 0) { + mac->mac_phy.txerrors = BWN_TXERROR_MAX; + bwn_restart(mac, "PHY TX errors"); + } + } + + if (merged & (BWN_DMAINTR_FATALMASK | BWN_DMAINTR_NONFATALMASK)) { + if (merged & BWN_DMAINTR_FATALMASK) { + device_printf(sc->sc_dev, + "Fatal DMA error: %#x %#x %#x %#x %#x %#x\n", + mac->mac_reason[0], mac->mac_reason[1], + mac->mac_reason[2], mac->mac_reason[3], + mac->mac_reason[4], mac->mac_reason[5]); + bwn_restart(mac, "DMA error"); + BWN_UNLOCK(sc); + return; + } + + if (merged & BWN_DMAINTR_NONFATALMASK) { + device_printf(sc->sc_dev, + "DMA error: %#x %#x %#x %#x %#x %#x\n", + mac->mac_reason[0], mac->mac_reason[1], + mac->mac_reason[2], mac->mac_reason[3], + mac->mac_reason[4], mac->mac_reason[5]); + } + } + + + if (mac->mac_reason_intr & BWN_INTR_NOISESAMPLE_OK) + bwn_intr_noise(mac); + + /*if (mac->mac_flags & BWN_MAC_FLAG_DMA) { + if (mac->mac_reason[0] & BWN_DMAINTR_RX_DONE) { + bwn_dma_rx(mac->mac_method.dma.rx); + rx = 1; + } + } + else*/ + rx = bwn_pio_rx(&mac->mac_method.pio.rx); + +/* KASSERT(!(mac->mac_reason[1] & BWN_DMAINTR_RX_DONE), ("%s", __func__)); + KASSERT(!(mac->mac_reason[2] & BWN_DMAINTR_RX_DONE), ("%s", __func__)); + KASSERT(!(mac->mac_reason[3] & BWN_DMAINTR_RX_DONE), ("%s", __func__)); + KASSERT(!(mac->mac_reason[4] & BWN_DMAINTR_RX_DONE), ("%s", __func__)); + KASSERT(!(mac->mac_reason[5] & BWN_DMAINTR_RX_DONE), ("%s", __func__));*/ + + if (mac->mac_reason_intr & BWN_INTR_TX_OK) { + bwn_intr_txeof(mac); + tx = 1; + } + +/// BWN_WRITE_4(mac, BWN_INTR_MASK, mac->mac_intr_mask); + + /*if (sc->sc_blink_led != NULL && sc->sc_led_blink) { + int evt = BWN_LED_EVENT_NONE; + + if (tx && rx) { + if (sc->sc_rx_rate > sc->sc_tx_rate) + evt = BWN_LED_EVENT_RX; + else + evt = BWN_LED_EVENT_TX; + } else if (tx) { + evt = BWN_LED_EVENT_TX; + } else if (rx) { + evt = BWN_LED_EVENT_RX; + } else if (rx == 0) { + evt = BWN_LED_EVENT_POLL; + } + + if (evt != BWN_LED_EVENT_NONE) + bwn_led_event(mac, evt); + }*/ + + if (mbufq_first(&sc->sc_snd) != NULL) + bwn_start(sc); + + /// BWN_BARRIER(mac, BUS_SPACE_BARRIER_READ); + /// BWN_BARRIER(mac, BUS_SPACE_BARRIER_WRITE); + + + BWN_UNLOCK(sc); +} + + +/////////////////////// +static void +bwn_intr_noise(struct bwn_mac *mac) +{ + struct bwn_phy_g *pg = &mac->mac_phy.phy_g; + uint16_t tmp=0; + uint8_t noise[4]; + uint8_t i, j; + int32_t average; + + if (mac->mac_phy.type != BWN_PHYTYPE_G) + return; + + KASSERT(mac->mac_noise.noi_running, ("%s: fail", __func__)); + *((uint32_t *)noise) = htole32(bwn_jssi_read(mac)); + if (noise[0] == 0x7f || noise[1] == 0x7f || noise[2] == 0x7f || + noise[3] == 0x7f) + goto new; + + KASSERT(mac->mac_noise.noi_nsamples < 8, ("%s:%d: fail", __func__, __LINE__)); + i = mac->mac_noise.noi_nsamples; + noise[0] = MIN(MAX(noise[0], 0), N(pg->pg_nrssi_lt) - 1); + noise[1] = MIN(MAX(noise[1], 0), N(pg->pg_nrssi_lt) - 1); + noise[2] = MIN(MAX(noise[2], 0), N(pg->pg_nrssi_lt) - 1); + noise[3] = MIN(MAX(noise[3], 0), N(pg->pg_nrssi_lt) - 1); + mac->mac_noise.noi_samples[i][0] = pg->pg_nrssi_lt[noise[0]]; + mac->mac_noise.noi_samples[i][1] = pg->pg_nrssi_lt[noise[1]]; + mac->mac_noise.noi_samples[i][2] = pg->pg_nrssi_lt[noise[2]]; + mac->mac_noise.noi_samples[i][3] = pg->pg_nrssi_lt[noise[3]]; + mac->mac_noise.noi_nsamples++; + if (mac->mac_noise.noi_nsamples == 8) { + average = 0; + for (i = 0; i < 8; i++) { + for (j = 0; j < 4; j++) + average += mac->mac_noise.noi_samples[i][j]; + } + average = (((average / 32) * 125) + 64) / 128; + ///tmp = (bwn_shm_read_2(mac, BWN_SHARED, 0x40c) / 128) & 0x1f; + if (tmp >= 8) + average += 2; + else + average -= 25; + average -= (tmp == 8) ? 72 : 48; + + mac->mac_stats.link_noise = average; + mac->mac_noise.noi_running = 0; + return; + } +new: + bwn_noise_gensample(mac); +} + +/////////////////////// +static uint8_t +bwn_pio_rxeof(struct bwn_pio_rxqueue *prq) +{ + /*struct bwn_mac *mac = prq->prq_mac; + struct brcmwl_softc *sc = mac->mac_sc; + struct bwn_rxhdr4 rxhdr; + struct mbuf *m; + uint32_t ctl32, macstat, v32; + unsigned int i, padding; + uint16_t ctl16, len, totlen, v16; + unsigned char *mp; + char *data; + + memset(&rxhdr, 0, sizeof(rxhdr)); + + if (prq->prq_rev >= 8) { + ctl32 = bwn_pio_rx_read_4(prq, BWN_PIO8_RXCTL); + if (!(ctl32 & BWN_PIO8_RXCTL_FRAMEREADY)) + return (0); + bwn_pio_rx_write_4(prq, BWN_PIO8_RXCTL, + BWN_PIO8_RXCTL_FRAMEREADY); + for (i = 0; i < 10; i++) { + ctl32 = bwn_pio_rx_read_4(prq, BWN_PIO8_RXCTL); + if (ctl32 & BWN_PIO8_RXCTL_DATAREADY) + goto ready; + DELAY(10); + } + } else { + ctl16 = bwn_pio_rx_read_2(prq, BWN_PIO_RXCTL); + if (!(ctl16 & BWN_PIO_RXCTL_FRAMEREADY)) + return (0); + bwn_pio_rx_write_2(prq, BWN_PIO_RXCTL, + BWN_PIO_RXCTL_FRAMEREADY); + for (i = 0; i < 10; i++) { + ctl16 = bwn_pio_rx_read_2(prq, BWN_PIO_RXCTL); + if (ctl16 & BWN_PIO_RXCTL_DATAREADY) + goto ready; + DELAY(10); + } + } + device_printf(sc->sc_dev, "%s: timed out\n", __func__); + return (1); +ready: + if (prq->prq_rev >= 8) + siba_read_multi_4(sc->sc_dev, &rxhdr, sizeof(rxhdr), + prq->prq_base + BWN_PIO8_RXDATA); + else + siba_read_multi_2(sc->sc_dev, &rxhdr, sizeof(rxhdr), + prq->prq_base + BWN_PIO_RXDATA); + len = le16toh(rxhdr.frame_len); + if (len > 0x700) { + device_printf(sc->sc_dev, "%s: len is too big\n", __func__); + goto error; + } + if (len == 0) { + device_printf(sc->sc_dev, "%s: len is 0\n", __func__); + goto error; + } + + switch (mac->mac_fw.fw_hdr_format) { + case BWN_FW_HDR_351: + case BWN_FW_HDR_410: + macstat = le32toh(rxhdr.ps4.r351.mac_status); + break; + case BWN_FW_HDR_598: + macstat = le32toh(rxhdr.ps4.r598.mac_status); + break; + } + + if (macstat & BWN_RX_MAC_FCSERR) { + if (!(mac->mac_sc->sc_filters & BWN_MACCTL_PASS_BADFCS)) { + device_printf(sc->sc_dev, "%s: FCS error", __func__); + goto error; + } + } + + padding = (macstat & BWN_RX_MAC_PADDING) ? 2 : 0; + totlen = len + padding; + KASSERT(totlen <= MCLBYTES, ("too big..\n")); + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + device_printf(sc->sc_dev, "%s: out of memory", __func__); + goto error; + } + mp = mtod(m, unsigned char *); + if (prq->prq_rev >= 8) { + siba_read_multi_4(sc->sc_dev, mp, (totlen & ~3), + prq->prq_base + BWN_PIO8_RXDATA); + if (totlen & 3) { + v32 = bwn_pio_rx_read_4(prq, BWN_PIO8_RXDATA); + data = &(mp[totlen - 1]); + switch (totlen & 3) { + case 3: + *data = (v32 >> 16); + data--; + case 2: + *data = (v32 >> 8); + data--; + case 1: + *data = v32; + } + } + } else { + siba_read_multi_2(sc->sc_dev, mp, (totlen & ~1), + prq->prq_base + BWN_PIO_RXDATA); + if (totlen & 1) { + v16 = bwn_pio_rx_read_2(prq, BWN_PIO_RXDATA); + mp[totlen - 1] = v16; + } + } + + m->m_len = m->m_pkthdr.len = totlen; + + bwn_rxeof(prq->prq_mac, m, &rxhdr); //brcmwl_scan_node + + return (1); +error: + if (prq->prq_rev >= 8) + bwn_pio_rx_write_4(prq, BWN_PIO8_RXCTL, + BWN_PIO8_RXCTL_DATAREADY); + else + bwn_pio_rx_write_2(prq, BWN_PIO_RXCTL, BWN_PIO_RXCTL_DATAREADY); + return (1);*/ + + return 0; +} + +static int +bwn_pio_rx(struct bwn_pio_rxqueue *prq) +{ + struct bwn_mac *mac = prq->prq_mac; + struct brcmwl_softc *sc = mac->mac_sc; + unsigned int i; + + BWN_ASSERT_LOCKED(sc); + + if (mac->mac_status < BWN_MAC_STATUS_STARTED) + return (0); + + for (i = 0; i < 5000; i++) { + if (bwn_pio_rxeof(prq) == 0) + break; + } + if (i >= 5000) + device_printf(sc->sc_dev, "too many RX frames in PIO mode\n"); + return ((i > 0) ? 1 : 0); +} + +/////////////////////// +static void +bwn_phy_txpower_check(struct bwn_mac *mac, uint32_t flags) +{ + struct brcmwl_softc *sc = mac->mac_sc; + struct bwn_phy *phy = &mac->mac_phy; + struct ieee80211com *ic = &sc->sc_ic; + unsigned long now; + bwn_txpwr_result_t result; + + BWN_GETTIME(now); + + if (!(flags & BWN_TXPWR_IGNORE_TIME) && ieee80211_time_before(now, phy->nexttime)) + return; + phy->nexttime = now + 2 * 1000; + +/* if (siba_get_pci_subvendor(sc->sc_dev) == SIBA_BOARDVENDOR_BCM && + siba_get_pci_subdevice(sc->sc_dev) == SIBA_BOARD_BU4306) + return;*/ + + if (phy->recalc_txpwr != NULL) { + result = phy->recalc_txpwr(mac, + (flags & BWN_TXPWR_IGNORE_TSSI) ? 1 : 0); + if (result == BWN_TXPWR_RES_DONE) + return; + KASSERT(result == BWN_TXPWR_RES_NEED_ADJUST, ("%s: fail", __func__)); + KASSERT(phy->set_txpwr != NULL, ("%s: fail", __func__)); + + ieee80211_runtask(ic, &mac->mac_txpower); + } +} + +static struct bwn_pio_txqueue * +bwn_pio_parse_cookie(struct bwn_mac *mac, uint16_t cookie, struct bwn_pio_txpkt **pack) +{ + struct bwn_pio *pio = &mac->mac_method.pio; + struct bwn_pio_txqueue *tq = NULL; + unsigned int index; + + switch (cookie & 0xf000) { + case 0x1000: + tq = &pio->wme[WME_AC_BK]; + break; + case 0x2000: + tq = &pio->wme[WME_AC_BE]; + break; + case 0x3000: + tq = &pio->wme[WME_AC_VI]; + break; + case 0x4000: + tq = &pio->wme[WME_AC_VO]; + break; + case 0x5000: + tq = &pio->mcast; + break; + } + KASSERT(tq != NULL, ("%s:%d: fail", __func__, __LINE__)); + if (tq == NULL) + return (NULL); + index = (cookie & 0x0fff); + KASSERT(index < N(tq->tq_pkts), ("%s:%d: fail", __func__, __LINE__)); + if (index >= N(tq->tq_pkts)) + return (NULL); + *pack = &tq->tq_pkts[index]; + KASSERT(*pack != NULL, ("%s:%d: fail", __func__, __LINE__)); + return (tq); +} + +static void +bwn_pio_handle_txeof(struct bwn_mac *mac, const struct bwn_txstatus *status) +{ + struct bwn_pio_txqueue *tq; + struct bwn_pio_txpkt *tp = NULL; + struct brcmwl_softc *sc = mac->mac_sc; + int retrycnt = 0; + + BWN_ASSERT_LOCKED(sc); + + tq = bwn_pio_parse_cookie(mac, status->cookie, &tp); + if (tq == NULL) + return; + + tq->tq_used -= roundup(tp->tp_m->m_pkthdr.len + BWN_HDRSIZE(mac), 4); + tq->tq_free++; + + if (tp->tp_ni != NULL) { + /* + * Do any tx complete callback. Note this must + * be done before releasing the node reference. + */ + + /* + * If we don't get an ACK, then we should log the + * full framecnt. That may be 0 if it's a PHY + * failure, so ensure that gets logged as some + * retry attempt. + */ + if (status->ack) { + retrycnt = status->framecnt - 1; + } + else { + retrycnt = status->framecnt; + if (retrycnt == 0) + retrycnt = 1; + } + ieee80211_ratectl_tx_complete(tp->tp_ni->ni_vap, tp->tp_ni, + status->ack ? + IEEE80211_RATECTL_TX_SUCCESS : + IEEE80211_RATECTL_TX_FAILURE, + &retrycnt, 0); + + if (tp->tp_m->m_flags & M_TXCB) + ieee80211_process_callback(tp->tp_ni, tp->tp_m, 0); + ieee80211_free_node(tp->tp_ni); + tp->tp_ni = NULL; + } + m_freem(tp->tp_m); + tp->tp_m = NULL; + TAILQ_INSERT_TAIL(&tq->tq_pktlist, tp, tp_list); + + sc->sc_watchdog_timer = 0; +} + +static void +bwn_handle_txeof(struct bwn_mac *mac, const struct bwn_txstatus *status) +{ + struct brcmwl_softc *sc = mac->mac_sc; + struct bwn_stats *stats = &mac->mac_stats; + + BWN_ASSERT_LOCKED(mac->mac_sc); + + if (status->im) + device_printf(sc->sc_dev, "TODO: STATUS IM\n"); + if (status->ampdu) + device_printf(sc->sc_dev, "TODO: STATUS AMPDU\n"); + if (status->rtscnt) { + if (status->rtscnt == 0xf) + stats->rtsfail++; + else + stats->rts++; + } + + /*if (mac->mac_flags & BWN_MAC_FLAG_DMA) { + bwn_dma_handle_txeof(mac, status); + } + else */ + bwn_pio_handle_txeof(mac, status); + + bwn_phy_txpower_check(mac, 0); +} + +/////////////////////////// +static void +bwn_intr_txeof(struct bwn_mac *mac) +{ + struct bwn_txstatus stat; + uint32_t stat0=0, stat1=0; + uint16_t tmp; + + BWN_ASSERT_LOCKED(mac->mac_sc); + + while (1) { + //// stat0 = BWN_READ_4(mac, BWN_XMITSTAT_0); + if (!(stat0 & 0x00000001)) + break; + //// stat1 = BWN_READ_4(mac, BWN_XMITSTAT_1); + + DPRINTF(mac->mac_sc, BWN_DEBUG_XMIT, + "%s: stat0=0x%08x, stat1=0x%08x\n", + __func__, + stat0, + stat1); + + stat.cookie = (stat0 >> 16); + stat.seq = (stat1 & 0x0000ffff); + stat.phy_stat = ((stat1 & 0x00ff0000) >> 16); + tmp = (stat0 & 0x0000ffff); + stat.framecnt = ((tmp & 0xf000) >> 12); + stat.rtscnt = ((tmp & 0x0f00) >> 8); + stat.sreason = ((tmp & 0x001c) >> 2); + stat.pm = (tmp & 0x0080) ? 1 : 0; + stat.im = (tmp & 0x0040) ? 1 : 0; + stat.ampdu = (tmp & 0x0020) ? 1 : 0; + stat.ack = (tmp & 0x0002) ? 1 : 0; + + DPRINTF(mac->mac_sc, BWN_DEBUG_XMIT, + "%s: cookie=%d, seq=%d, phystat=0x%02x, framecnt=%d, " + "rtscnt=%d, sreason=%d, pm=%d, im=%d, ampdu=%d, ack=%d\n", + __func__, + stat.cookie, + stat.seq, + stat.phy_stat, + stat.framecnt, + stat.rtscnt, + stat.sreason, + stat.pm, + stat.im, + stat.ampdu, + stat.ack); + + bwn_handle_txeof(mac, &stat); + } +} + +static void +bwn_hwreset(void *arg, int npending) +{ + struct bwn_mac *mac = arg; + struct brcmwl_softc *sc = mac->mac_sc; + int error = 0; + int prev_status; + + BWN_LOCK(sc); + + prev_status = mac->mac_status; + if (prev_status >= BWN_MAC_STATUS_STARTED) + bwn_core_stop(mac); + if (prev_status >= BWN_MAC_STATUS_INITED) + bwn_core_exit(mac); + + if (prev_status >= BWN_MAC_STATUS_INITED) { + error = bwn_core_init(mac); + if (error) + goto out; + } + if (prev_status >= BWN_MAC_STATUS_STARTED) + bwn_core_start(mac); + +out: + if (error) { + device_printf(sc->sc_dev, "%s: failed (%d)\n", __func__, error); + sc->sc_curmac = NULL; + } + BWN_UNLOCK(sc); +} + + +/////////////////////////// +static uint32_t +bwn_jssi_read(struct bwn_mac *mac) +{ + uint32_t val = 0; + + /*val = bwn_shm_read_2(mac, BWN_SHARED, 0x08a); + val <<= 16; + val |= bwn_shm_read_2(mac, BWN_SHARED, 0x088);*/ + + return (val); +} + +/////////////////////////// +static void +bwn_noise_gensample(struct bwn_mac *mac) +{ +/* uint32_t jssi = 0x7f7f7f7f; + + bwn_shm_write_2(mac, BWN_SHARED, 0x088, (jssi & 0x0000ffff)); + bwn_shm_write_2(mac, BWN_SHARED, 0x08a, (jssi & 0xffff0000) >> 16); + BWN_WRITE_4(mac, BWN_MACCMD, + BWN_READ_4(mac, BWN_MACCMD) | BWN_MACCMD_BGNOISE);*/ +} + +static void +bwn_sysctl_node(struct brcmwl_softc *sc) +{ + device_t dev = sc->sc_dev; + struct bwn_mac *mac; + struct bwn_stats *stats; + + /* XXX assume that count of MAC is only 1. */ + + if ((mac = sc->sc_curmac) == NULL) + return; + + stats = &mac->mac_stats; + + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "linknoise", CTLFLAG_RW, &stats->rts, 0, "Noise level"); + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "rts", CTLFLAG_RW, &stats->rts, 0, "RTS"); + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "rtsfail", CTLFLAG_RW, &stats->rtsfail, 0, "RTS failed to send"); + +#ifdef BWN_DEBUG + SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "debug", CTLFLAG_RW, &sc->sc_debug, 0, "Debug flags"); +#endif +} + +static int +bwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct bwn_vap *bvp = BWN_VAP(vap); + struct ieee80211com *ic= vap->iv_ic; + enum ieee80211_state ostate = vap->iv_state; + struct brcmwl_softc *sc = ic->ic_softc; + struct bwn_mac *mac = sc->sc_curmac; + int error; + + DPRINTF(sc, BWN_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + error = bvp->bv_newstate(vap, nstate, arg); + if (error != 0) + return (error); + + BWN_LOCK(sc); + + ///bwn_led_newstate(mac, nstate); + + /* + * Clear the BSSID when we stop a STA + */ + if (vap->iv_opmode == IEEE80211_M_STA) { + if (ostate == IEEE80211_S_RUN && nstate != IEEE80211_S_RUN) { + /* + * Clear out the BSSID. If we reassociate to + * the same AP, this will reinialize things + * correctly... + */ + if (ic->ic_opmode == IEEE80211_M_STA && (sc->sc_flags & BWN_FLAG_INVALID) == 0) { + memset(sc->sc_bssid, 0, IEEE80211_ADDR_LEN); + bwn_set_macaddr(mac); + } + } + } + + if (vap->iv_opmode == IEEE80211_M_MONITOR || vap->iv_opmode == IEEE80211_M_AHDEMO) { + /* XXX nothing to do? */ + } else if (nstate == IEEE80211_S_RUN) { + memcpy(sc->sc_bssid, vap->iv_bss->ni_bssid, IEEE80211_ADDR_LEN); + bwn_set_opmode(mac); + bwn_set_pretbtt(mac); + /// bwn_spu_setdelay(mac, 0); + bwn_set_macaddr(mac); + } + + BWN_UNLOCK(sc); + + return (error); +} + +static struct ieee80211vap * +bwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]){ + + struct ieee80211vap *vap; + struct bwn_vap *bvp; + + switch (opmode) { + case IEEE80211_M_HOSTAP: + case IEEE80211_M_MBSS: + case IEEE80211_M_STA: + case IEEE80211_M_WDS: + case IEEE80211_M_MONITOR: + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + break; + default: + return (NULL); + } + + bvp = malloc(sizeof(struct bwn_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &bvp->bv_vap; + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); + /* override with driver methods */ + bvp->bv_newstate = vap->iv_newstate; + vap->iv_newstate = bwn_newstate; + +/* + ic->ic_newstate = brcmwl_newstate; + ic->ic_send_mgmt = brcmwl_send_mgmt; + ic->ic_set_key = brcmwl_set_key; + ic->ic_delete_key = brcmwl_delete_key; + +*/ + /* override max aid so sta's cannot assoc when we're out of sta id's */ + vap->iv_max_aid = BWN_STAID_MAX; + + ieee80211_ratectl_init(vap); + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); + return (vap); +} + +static void +bwn_vap_delete(struct ieee80211vap *vap) +{ + struct bwn_vap *bvp = BWN_VAP(vap); + + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + free(bvp, M_80211_VAP); +} + + +static int +brcmwl_ifmedia_update(struct ifnet *ifp) +{ + + int error; + + error = ieee80211_media_change(ifp); + if (error != ENETRESET) + return error; + + if ((ifp->if_flags & (IFF_UP | IFF_DRV_RUNNING)) == + (IFF_UP | IFF_DRV_RUNNING)) { + ///brcmwl_stop(ifp); + ///brcmwl_init(ifp); + printf("brcmwl_stop \n"); + + } + return 0; +} + +static void +brcmwl_stop(if_t ifp){ + + + +} + + +static int +brcmwl_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data){ + + int s, error = 0; + + s = splnet(); + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + // FALLTHROUGH + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_DRV_RUNNING)) + ///brcmwl_init(ifp); + printf("brcmwl_init \n"); + } else { + if (ifp->if_flags & IFF_DRV_RUNNING) + brcmwl_stop(ifp); + } + break; + default: + error = ieee80211_ioctl(ifp, cmd, data); + } + if (error == ENETRESET) { + if ((ifp->if_flags & (IFF_UP | IFF_DRV_RUNNING)) == (IFF_UP | IFF_DRV_RUNNING)) { + brcmwl_stop(ifp); + ///brcmwl_init(ifp); + } + error = 0; + } + splx(s); + return error; + +} + + + +static void +brcmwl_tx_start(if_t ifp) { // bwn_pio_tx_start()?? /// tart(struct brcmwl_softc *sc + + + + struct mbuf *m_head; + struct brcmwl_softc *sc = ifp->if_softc; +// /* struct brcmwl_softc *sc = if_getsoftc(ifp); + + ///IXGB_LOCK_ASSERT(adapter); + + //if (!sc->link_active) + /// return; + + //if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING) + /// return; + + + while (ifp->if_snd.ifq_head != NULL) { + IF_DEQUEUE(&ifp->if_snd, m_head); + + if (m_head == NULL) + break; + + /*if (ixgb_encap(adapter, m_head)) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + IF_PREPEND(&ifp->if_snd, m_head); + break; + }*/ + /* Send a copy of the frame to the BPF listener */ + ETHER_BPF_MTAP(ifp, m_head); + + /* Set timeout in case hardware has problems transmitting */ + ///adapter->tx_timer = IXGB_TX_TIMEOUT; + sc->sc_watchdog_timer = 5; + + } + return; + + +} + +static void +brcmwl_start(if_t ifp) +{ + ///struct brcmwl_softc *sc = if_getsoftc(ifp); + ///struct tx_ring *txr = sc->tx_rings; + + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) { + ///EM_TX_LOCK(txr); + brcmwl_tx_start(ifp); + ///EM_TX_UNLOCK(txr); + } + return; +} + + + + + +static int +brcmwl_init_ifnet(struct brcmwl_softc *sc){ + + if_t ifp; + + /* ifconfig entrypoint for media type/status reporting */ + ifmedia_init(&sc->ifmedia, IFM_IMASK, brcmwl_ifmedia_update, ieee80211_media_status); + + /* set the default interface values */ + ifmedia_add(&sc->ifmedia, (IFM_ETHER | IFM_FDX), 0, NULL); + ifmedia_add(&sc->ifmedia, (IFM_ETHER | IFM_AUTO), 0, NULL); + ifmedia_set(&sc->ifmedia, (IFM_ETHER | IFM_AUTO)); + +/* + ifp->if_watchdog = brcmwl_watchdog; + +*/ + sc->ifmedia.ifm_media = sc->ifmedia.ifm_cur->ifm_media; /* XXX ? */ + + /* allocate the ifnet structure */ + if ((ifp = if_gethandle(IFT_ETHER)) == NULL) { + printf("Interface allocation failed!\n"); + return (ENXIO); + } + + if_setsoftc(ifp, sc); + if_initname(ifp, device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev)); + if_setflags(ifp, (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST)); + if_setioctlfn(ifp, brcmwl_ioctl); + if_setstartfn(ifp, brcmwl_start); + ////if_setgetcounterfn(ifp, brcmwl_get_counter); + /// if_setinitfn(ifp, brcmwl_init); + if_setmtu(ifp, sc->mtu); + if_sethwassist(ifp, (CSUM_IP | + CSUM_TCP | + CSUM_UDP | + CSUM_TSO | + CSUM_TCP_IPV6 | + CSUM_UDP_IPV6)); + + int capabilities; + capabilities = + (IFCAP_VLAN_MTU | + IFCAP_VLAN_HWTAGGING | + IFCAP_VLAN_HWTSO | + IFCAP_VLAN_HWFILTER | + IFCAP_VLAN_HWCSUM | + IFCAP_HWCSUM | + IFCAP_JUMBO_MTU | + IFCAP_LRO | + IFCAP_TSO4 | + IFCAP_TSO6 | + IFCAP_WOL_MAGIC); + if_setcapabilitiesbit(ifp, capabilities, 0); /* XXX */ + /// if_setbaudrate(ifp, IF_Gbps(10)); + /* + //// XXX + if_setsendqlen(ifp, sc->tx_ring_size); + if_setsendqready(ifp); + ///XXX */ + + sc->ifp = ifp; + + /* attach to the Ethernet interface list */ + memset(sc->macaddr, 0x12, sizeof(sc->macaddr)); + /// ether_ifattach(ifp, sc->macaddr); + + return (0); +} + + +/////////////////// +static int +bwn_attach_post(struct brcmwl_softc *sc){ + + brcmwl_init_ifnet(sc); // fake LAN mode + struct ieee80211com *ic = &sc->sc_ic; // fake WLAN mode + + ////ic->ic_state = IEEE80211_S_INIT; + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(sc->sc_dev); + /* XXX not right but it's not used anywhere important */ + ic->ic_phytype = IEEE80211_T_OFDM; + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_caps = + IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_MONITOR /* monitor mode */ + | IEEE80211_C_AHDEMO /* adhoc demo mode */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_WME /* WME/WMM supported */ + | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ + | IEEE80211_C_TXPMGT /* capable of txpow mgt */ + ; + +/// ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; /* s/w bmiss */ + + /// [6] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xab }; + IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->macaddr); + /*IEEE80211_ADDR_COPY(ic->ic_macaddr, + bwn_is_valid_ether_addr(siba_sprom_get_mac_80211a(sc->sc_dev)) ? + siba_sprom_get_mac_80211a(sc->sc_dev) : + siba_sprom_get_mac_80211bg(sc->sc_dev))*/ + + /* call MI attach routine. */ + ieee80211_ifattach(ic); + + ic->ic_vap_create = bwn_vap_create; + ic->ic_vap_delete = bwn_vap_delete; + ///ic->ic_set_channel = bwn_set_channel; + +/* + ic->ic_headroom = sizeof(struct bwn_txhdr); + + + bwn_sysctl_node(sc); +*/ + + ieee80211_radiotap_attach(ic, + &sc->sc_tx_th.wt_ihdr, + sizeof(sc->sc_tx_th), + BWN_TX_RADIOTAP_PRESENT, + &sc->sc_rx_th.wr_ihdr, + sizeof(sc->sc_rx_th), + BWN_RX_RADIOTAP_PRESENT); + + + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +} + +static void +bwn_phy_detach(struct bwn_mac *mac){ + + if (mac->mac_phy.detach != NULL) + mac->mac_phy.detach(mac); +} + +/* + * XXX Do we need to implement device_identify()? + */ + /* + check SDIO FBR standard -> function Wireless sub-function + */ +static int +brcmwl_probe(device_t dev){ + + return (BUS_PROBE_GENERIC); +} + +/////////////////////////// +static int +brcmwl_detach(device_t dev){ + + struct brcmwl_softc *sc = BRCMWL_SOFTC(dev); + struct bwn_mac *mac = sc->sc_curmac; + struct ieee80211com *ic = &sc->sc_ic; + + sc->sc_flags |= BWN_FLAG_INVALID; + + if (device_is_attached(sc->sc_dev)) { + BWN_LOCK(sc); + bwn_stop(sc); + BWN_UNLOCK(sc); + callout_drain(&sc->sc_rfswitch_ch); + callout_drain(&sc->sc_task_ch); + callout_drain(&sc->sc_watchdog_ch); + bwn_phy_detach(mac); + ieee80211_draintask(ic, &mac->mac_hwreset); + ieee80211_draintask(ic, &mac->mac_txpower); + ieee80211_ifdetach(ic); + } + taskqueue_drain(sc->sc_tq, &mac->mac_intrtask); + taskqueue_free(sc->sc_tq); + +/* for (i = 0; i < BWN_MSI_MESSAGES; i++) { + if (mac->mac_intrhand[i] != NULL) { + ///bus_teardown_intr(dev, mac->mac_res_irq[i], mac->mac_intrhand[i]); + mac->mac_intrhand[i] = NULL; + } + }*/ + + + mbufq_drain(&sc->sc_snd); + brcm_dhdcore_destroy(sc); + free(sc->sc_bounce_buf, M_TEMP); + brcmwl_release_firmware(mac); + BWN_LOCK_DESTROY(sc); + return (0); + +} + +//////////////////////// +static int +bwn_phy_getinfo(struct bwn_mac *mac, int gmode) +{ + return (0); + +} + +//////////////////////// +static int +bwn_chiptest(struct bwn_mac *mac){ + + struct brcmwl_softc *sc = mac->mac_sc; + + int ret; + uint8_t reg; + + + ret = brcmwl_sdiobus_read_1(sc, 0, SD_IO_CCCR_FN_ENABLE, ®); + reg &= ~(BIT_CCCR_ENAFUNC2); + ret = brcmwl_sdiobus_write_1(sc, 0, SD_IO_CCCR_FN_ENABLE, reg); + if(ret) + printf("SDIO_WRITE, 0, SD_IO_CCCR_FN_ENABL fail \n"); + + ret = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_FUNC1_CHIPCLKCSR, 0); + if(ret) + printf("SDIO_WRITE SBSDIO_FUNC1_CHIPCLKCSR fail \n"); + + sc->clkstate = CLK_SDONLY; + + sc->sr_enabled = false; + + return 0; +} + +static void +bwn_addchannels(struct ieee80211_channel chans[], + int maxchans, + int *nchans, + const struct bwn_channelinfo *ci, + const uint8_t bands[]){ + + int i, error; + + for (i = 0, error = 0; i < ci->nchannels && error == 0; i++) { + const struct bwn_channel *hc = &ci->channels[i]; + + error = ieee80211_add_channel(chans, + maxchans, + nchans, + hc->ieee, + hc->freq, + hc->maxTxPow, + 0, bands); + } +} + +static int +bwn_setup_channels(struct bwn_mac *mac, int have_bg, int have_a) +{ + struct brcmwl_softc *sc = mac->mac_sc; + struct ieee80211com *ic = &sc->sc_ic; + uint8_t bands[IEEE80211_MODE_BYTES]; + + memset(ic->ic_channels, 0, sizeof(ic->ic_channels)); + ic->ic_nchans = 0; + + DPRINTF(sc, BWN_DEBUG_EEPROM, "%s: called; bg=%d, a=%d\n", + __func__, + have_bg, + have_a); + + if (have_bg) { + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + bwn_addchannels(ic->ic_channels, IEEE80211_CHAN_MAX, + &ic->ic_nchans, &bwn_chantable_bg, bands); + } + + if (have_a) { + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11A); + bwn_addchannels(ic->ic_channels, IEEE80211_CHAN_MAX, &ic->ic_nchans, &bwn_chantable_a, bands); + } + + mac->mac_phy.supports_2ghz = have_bg; + mac->mac_phy.supports_5ghz = have_a; + + return (ic->ic_nchans == 0 ? ENXIO : 0); +} + + + +static int +brcmwl_sdiod_set_backplane_window(struct brcmwl_softc *sc, uint32_t addr){ + + uint32_t bar0 = addr & SBSDIO_SBWINDOW_MASK; + if (bar0 == sc->backplaneAddr) + return 0; + + uint8_t tmpreg; + int err = 0x12; + tmpreg = ((addr >> 8) & 0xff) & SBSDIO_SBADDRLOW_MASK; + err = brcmwl_sdiobus_write_1(sc, 1, OFFSET_FUNC1_SBADDRLOW, tmpreg); + if (!err){ + tmpreg = ((addr >> 16) & 0xff) & SBSDIO_SBADDRMID_MASK; + err = brcmwl_sdiobus_write_1(sc, 1, OFFSET_FUNC1_SBADDRMID, tmpreg); + } + if (!err){ + tmpreg = ((addr >> 24) & 0xff) & SBSDIO_SBADDRHIGH_MASK; + err = brcmwl_sdiobus_write_1(sc, 1, OFFSET_FUNC1_SBADDRHIGH, tmpreg); + } + + sc->backplaneAddr = bar0; + + return err; +} + + +static int +brcmwl_sdiobus_read_stream(struct brcmwl_softc *sc, uint32_t address, uint8_t *data, uint32_t datalen){ + + uint32_t sbaddr, sdaddr, off; + size_t size; + int err = off = 0; + + while (datalen > 0) { + + sbaddr = address + off; + err = brcmwl_sdiod_set_backplane_window(sc, sbaddr); + if (err){ + device_printf(sc->sc_dev, "brcmwl_sdiod_set_backplane_window error %x \n", err); + break; + } + + sdaddr = sbaddr & OFFSET_OFT_ADDR_MASK; + size = min(datalen, (BRCMWL_SDIO_SB_OFT_ADDR_PAGE - sdaddr)); + sdaddr |= OFFSET_ACCESS_2_4B_FLAG; + + KASSERT(((vaddr_t)data & 0x3) == 0, ( "%s: ", __func__)); + KASSERT((datalen & 0x3) == 0, ( "%s: ", __func__)); + + err = SDIOBUS_BLOCKS_READ(device_get_parent(sc->sc_dev), sc->sc_dev, + 1, sdaddr, sc->sc_bounce_buf, roundup(size, 4)); + if (err) + break; + + memcpy(data + off, sc->sc_bounce_buf, size); + + off += size; + datalen -= size; + printf("."); + + } + + return err; +} + +static int +brcmwl_sdiobus_write_stream(struct brcmwl_softc *sc, uint32_t address, uint8_t *data, uint32_t datalen){ + + uint32_t sbaddr, sdaddr, off; + size_t size; + int err = off = 0; + + while (datalen > 0) { + + sbaddr = address + off; + err = brcmwl_sdiod_set_backplane_window(sc, sbaddr); + if (err){ + device_printf(sc->sc_dev, "brcmwl_sdiod_set_backplane_window error %x \n", err); + break; + } + + sdaddr = sbaddr & OFFSET_OFT_ADDR_MASK; + size = min(datalen, (BRCMWL_SDIO_SB_OFT_ADDR_PAGE - sdaddr)); + sdaddr |= OFFSET_ACCESS_2_4B_FLAG; + + memcpy(sc->sc_bounce_buf, data + off, size); + if (roundup(size, 4) != size) + memset(sc->sc_bounce_buf + size, 0, roundup(size, 4) - size); + { + KASSERT(((vaddr_t)data & 0x3) == 0, ( "%s: ", __func__)); + KASSERT((datalen & 0x3) == 0, ( "%s: ", __func__)); + + err = SDIOBUS_BLOCKS_WRITE(device_get_parent(sc->sc_dev), sc->sc_dev, + 1, sdaddr, sc->sc_bounce_buf, roundup(size, 4)); + if (err) + break; + + + } + + + off += size; + datalen -= size; + printf("."); + } + + return err; +} + +static int +brcmwl_sdiobus_read_4(struct brcmwl_softc *sc, uint32_t addr, uint32_t *dataout){ + + int retval=0; + uint8_t tmpuint[4]; + + retval = brcmwl_sdiod_set_backplane_window(sc, addr); + if (retval) + return retval; + + addr &= OFFSET_OFT_ADDR_MASK; + addr |= OFFSET_ACCESS_2_4B_FLAG; + + retval = SDIOBUS_BYTES_READ(device_get_parent(sc->sc_dev), sc->sc_dev, 1, + addr, tmpuint, sizeof(tmpuint)); + + if (retval){ + return (retval); + } + + *dataout = tmpuint[3]; + *dataout = (*dataout << 8) | tmpuint[2]; + *dataout = (*dataout << 8) | tmpuint[1]; + *dataout = (*dataout << 8) | tmpuint[0]; + + return retval; +} + +static int +brcmwl_sdiobus_write_1(struct brcmwl_softc *sc, uint8_t func, uint32_t addr, uint8_t datain){ + uint8_t reg = datain; + return(SDIOBUS_REG_RW(device_get_parent(sc->sc_dev), sc->sc_dev, SDIO_WRITE, func, addr, ®)); +} + +static int +brcmwl_sdiobus_read_1(struct brcmwl_softc *sc, uint8_t func, uint32_t addr, uint8_t *dataout){ + return(SDIOBUS_REG_RW(device_get_parent(sc->sc_dev), sc->sc_dev, SDIO_READ, func, addr, dataout)); +} + + +static int +brcmwl_sdiobus_write_4(struct brcmwl_softc *sc, uint32_t addr, uint32_t datain){ + + int retval; + retval = brcmwl_sdiod_set_backplane_window(sc, addr); + if (retval) + return retval; + + addr &= OFFSET_OFT_ADDR_MASK; + addr |= OFFSET_ACCESS_2_4B_FLAG; + + uint8_t tmpuint[4]; + tmpuint[0] = datain & 0xff; + tmpuint[1] = (datain >> 8) & 0xff; + tmpuint[2] = (datain >> 16) & 0xff; + tmpuint[3] = (datain >> 24) & 0xff; + + retval = SDIOBUS_BYTES_WRITE(device_get_parent(sc->sc_dev), sc->sc_dev, 1, + addr, tmpuint, sizeof(tmpuint)); + + return retval; + +} + +static int +brcmwl_chip_core_read_4(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore, uint16_t reg, uint32_t *dataout){ + return (brcmwl_sdiobus_read_4(sc, prCore->pub.base + reg, dataout)); +} + +static int +brcmwl_chip_core_write_4(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore, uint16_t reg, uint32_t dataoin){ + return (brcmwl_sdiobus_write_4(sc, prCore->pub.base + reg, dataoin)); +} + +static bool +brcmwl_chip_axi_iscoreup(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore){ + + uint32_t regdata; + bool ret; + + ret = brcmwl_sdiobus_read_4(sc, prCore->wrapbase + BCMA_IOCTL, ®data); + ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; + + ret = brcmwl_sdiobus_read_4(sc, prCore->wrapbase + BCMA_RESET_CTL, ®data); + ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); + + return ret; +} + +static void +brcmwl_chip_axi_coredisable(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore, uint32_t prereset, uint32_t reset){ + + uint32_t regdata; + + brcmwl_sdiobus_read_4(sc, prCore->wrapbase + BCMA_RESET_CTL, ®data); + if ((regdata & BCMA_RESET_CTL_RESET) != 0) + goto in_reset_configure; + + brcmwl_sdiobus_read_4(sc, prCore->wrapbase + BCMA_IOCTL, ®data); + regdata |= prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK; + brcmwl_sdiobus_write_4(sc, prCore->wrapbase + BCMA_IOCTL, regdata); + + brcmwl_sdiobus_write_4(sc, prCore->wrapbase + BCMA_RESET_CTL, BCMA_RESET_CTL_RESET); + + int timeout =10; + regdata = 0; + while ((regdata & BCMA_RESET_CTL_RESET) == 0) { + brcmwl_sdiobus_read_4(sc, prCore->wrapbase + BCMA_RESET_CTL, ®data); + if (--timeout == 0) { + device_printf(sc->sc_dev, "brcmwl_chip_ai_coredisable reset operation timed out %x \n", regdata); + } + DELAY(20); + } + +in_reset_configure: + brcmwl_sdiobus_read_4(sc, prCore->wrapbase + BCMA_IOCTL, ®data); + regdata |= reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK; + brcmwl_sdiobus_write_4(sc, prCore->wrapbase + BCMA_IOCTL, regdata); +} + +static void +brcmwl_chip_axi_resetcore(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore, uint32_t prereset, uint32_t reset, uint32_t postreset){ + + uint32_t val=0xff; + int ret=0xff; + int timeout = 10; + + /* must disable first to work for arbitrary current core state */ + brcmwl_chip_axi_coredisable(sc, prCore, prereset, reset); + + while (val & BCMA_RESET_CTL_RESET) { + ret = brcmwl_sdiobus_read_4(sc, prCore->wrapbase + BCMA_RESET_CTL, &val); + if(!ret) + ret = brcmwl_sdiobus_write_4(sc, prCore->wrapbase + BCMA_RESET_CTL, 0); + if (--timeout == 0) { + device_printf(sc->sc_dev, "brcmwl_chip_ai_resetcore reset operation timed out %x \n", val); + } + DELAY(20); + } + + ret = brcmwl_sdiobus_read_4(sc, prCore->wrapbase + BCMA_IOCTL, &val); + val |= postreset | BCMA_IOCTL_CLK; + ret = brcmwl_sdiobus_write_4(sc, prCore->wrapbase + BCMA_IOCTL, val); + +} + +static void +brcmwl_chip_coredisable(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore, uint32_t prereset, uint32_t reset){ + brcmwl_chip_axi_coredisable(sc, prCore, prereset, reset); +} + +static void +brcmwl_chip_resetcore(struct brcmwl_softc *sc, + struct brcmwl_core_priv_t *prCore, + uint32_t prereset, + uint32_t reset, + uint32_t postreset){ + + brcmwl_chip_axi_resetcore(sc, prCore, prereset, reset, postreset); +} + + + +static uint32_t +brcmwl_chip_dmp_get_desc(struct brcmwl_softc *sc, + uint32_t *eromaddr, + uint8_t *type) +{ + uint32_t val; + int ret; + + /* read next descriptor */ + ret = brcmwl_sdiobus_read_4(sc, *eromaddr, &val); + *eromaddr += 4; + + if (!type) + return val; + + /* determine descriptor type */ + *type = (val & DMP_DESC_TYPE_MSK); + if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS) + *type = DMP_DESC_ADDRESS; + + return val; +} + +static int +brcmwl_chip_dmp_get_regaddr(struct brcmwl_softc *sc, uint32_t *eromaddr, + uint32_t *regbase, uint32_t *wrapbase) +{ + uint8_t desc; + uint32_t val; + uint8_t mpnum = 0; + uint8_t stype, sztype, wraptype; + + *regbase = 0; + *wrapbase = 0; + + val = brcmwl_chip_dmp_get_desc(sc, eromaddr, &desc); + if (desc == DMP_DESC_MASTER_PORT) { + mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S; + wraptype = DMP_SLAVE_TYPE_MWRAP; + } + else if (desc == DMP_DESC_ADDRESS) { + /* revert erom address */ + *eromaddr -= 4; + wraptype = DMP_SLAVE_TYPE_SWRAP; + } else { + *eromaddr -= 4; + return (EILSEQ); + } + + do { + /* locate address descriptor */ + do { + val = brcmwl_chip_dmp_get_desc(sc, eromaddr, &desc); + /* unexpected table end */ + if (desc == DMP_DESC_EOT) { + *eromaddr -= 4; + return (EFAULT); + } + } while (desc != DMP_DESC_ADDRESS && + desc != DMP_DESC_COMPONENT); + + /* stop if we crossed current component border */ + if (desc == DMP_DESC_COMPONENT) { + *eromaddr -= 4; + return 0; + } + + /* skip upper 32-bit address descriptor */ + if (val & DMP_DESC_ADDRSIZE_GT32) + brcmwl_chip_dmp_get_desc(sc, eromaddr, NULL); + + sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S; + + /* next size descriptor can be skipped */ + if (sztype == DMP_SLAVE_SIZE_DESC) { + val = brcmwl_chip_dmp_get_desc(sc, eromaddr, NULL); + /* skip upper size descriptor if present */ + if (val & DMP_DESC_ADDRSIZE_GT32) + brcmwl_chip_dmp_get_desc(sc, eromaddr, NULL); + } + + /* only look for 4K register regions */ + if (sztype != DMP_SLAVE_SIZE_4K) + continue; + + stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S; + + /* only regular slave and wrapper */ + if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE) + *regbase = val & DMP_SLAVE_ADDR_BASE; + if (*wrapbase == 0 && stype == wraptype) + *wrapbase = val & DMP_SLAVE_ADDR_BASE; + } while (*regbase == 0 || *wrapbase == 0); + + return 0; +} + +static struct brcmwl_core_priv_t* +brcmwl_chip_add_core(struct brcmwl_chip_priv_t *prChip, uint16_t coreid, uint32_t base, uint32_t wrapbase) +{ + struct brcmwl_core_priv_t *prCore = malloc(sizeof(struct brcmwl_core_priv_t), M_TEMP, M_ZERO | M_WAITOK); + if (prCore == NULL) + return (NULL); + + prCore->pub.id = coreid; + prCore->pub.base = base; + prCore->chip = prChip; + prCore->wrapbase = wrapbase; + + return prCore; + +} + +static struct brcmwl_core_t * +brcmwl_chip_get_core(struct brcmwl_core_priv_t *prCore, uint16_t coreid){ + + if (prCore->pub.id == coreid) + return &prCore->pub; + + return NULL; +} + +static int +brcmwl_chip_cores_check(struct brcmwl_softc *sc, struct brcmwl_chip_priv_t *prChip){ + + struct brcmwl_core_priv_t *prCore = NULL; + bool need_socram = false; + bool has_socram = false; + bool cpu_found = false; + + + prCore = sc->prCoreSoc; + switch (prCore->pub.id) { + case BCMA_CORE_ARM_CM3: + cpu_found = true; + need_socram = true; + break; + case BCMA_CORE_ARM_CR4: + cpu_found = true; + break; + case BCMA_CORE_ARM_CA7: + cpu_found = true; + break; + default: + break; + } + + prCore = sc->prCoreRam; + switch (prCore->pub.id) { + case BCMA_CORE_INTERNAL_MEM: + has_socram = true; + break; + default: + break; + } + + if (!cpu_found) { + device_printf(sc->sc_dev, "CPU core not detected\n"); + return (ENXIO); + } + else { + device_printf(sc->sc_dev, "CoreSoc "); + if(sc->prCoreSoc->pub.id == BCMA_CORE_ARM_CR4) + printf("Cortex-R4"); + else if(sc->prCoreSoc->pub.id == BCMA_CORE_ARM_CA7) + printf("Cortex-A7"); + else if(sc->prCoreSoc->pub.id == BCMA_CORE_ARM_CM3) + printf("Cortex-M3"); + else + printf("Unknown"); + printf(" detected. \n"); + + } + + if (need_socram && !has_socram) { + device_printf(sc->sc_dev, "AM core not provided with ARM CM3 core\n"); + return (ENODEV); + } + else + device_printf(sc->sc_dev, "CoreRam 0x%x detected. \n", sc->prCoreRam->pub.id); + + return 0; +} + +struct mbuf * +brcmwl_sdio_newbuf(void) +{ + struct mbuf *m_head; + + MGETHDR(m_head, M_NOWAIT, MT_DATA); + if (m_head == NULL) + return (NULL); + + MCLGET(m_head, M_NOWAIT); + if (!(m_head->m_flags & M_EXT)) { + m_freem(m_head); + return (NULL); + } + + m_head->m_len = m_head->m_pkthdr.len = MCLBYTES; + + return (m_head); +} + +/* +int +brcmwl_sdio_tx_ok(struct brcmwl_sdio_softc *sc){ + return (uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq) != 0 && ((uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq) & 0x80) == 0; +} + +int +brcmwl_sdio_txcheck(struct brcmwl_softc *bwfm){ + + struct brcmwl_sdio_softc *sc = (void *)bwfm; + + if (sc->sc_tx_count >= 64) + return ENOBUFS; + + return 0; +} + +int +brcmwl_sdio_txctl(struct brcmwl_softc *bwfm, void *arg) +{ + struct brcmwl_sdio_softc *sc = (void *)bwfm; + struct brcmwl_proto_bcdc_ctl *ctl = arg; + struct mbuf *m; + + MGET(m, M_DONTWAIT, MT_CONTROL); + if (m == NULL || M_TRAILINGSPACE(m) < ctl->len) { + free(ctl->buf, M_TEMP, ctl->len); + free(ctl, M_TEMP, sizeof(*ctl)); + return 1; + } + memcpy(mtod(m, char *), ctl->buf, ctl->len); + m->m_len = ctl->len; + + TAILQ_INSERT_TAIL(&sc->sc_sc.sc_bcdc_rxctlq, ctl, next); + ml_enqueue(&sc->sc_tx_queue, m); + task_add(systq, &sc->sc_task); + return 0; +} + + +int +brcmwl_sdio_txdata(struct brcmwl_softc *bwfm, struct mbuf *m){ + + struct brcmwl_sdio_softc *sc = (void *)bwfm; + + if (sc->sc_tx_count >= 64) + return ENOBUFS; + + sc->sc_tx_count++; + ml_enqueue(&sc->sc_tx_queue, m); + task_add(systq, &sc->sc_task); + return 0; +}*/ + +static int +brcmwl_sdio_frame_read_write(struct brcmwl_softc *sc, uint8_t *data, size_t size, int write){ + + int ret=0xff; + + struct brcmwl_core_priv_t *prCore = sc->prCoreComm; + uint32_t addr = prCore->pub.base; + + ret = brcmwl_sdiod_set_backplane_window(sc, addr); + if (ret) + return ret; + + addr &= OFFSET_OFT_ADDR_MASK; + addr |= OFFSET_ACCESS_2_4B_FLAG; + + KASSERT(((vaddr_t)data & 0x3) == 0, ( "%s: ", __func__)); + KASSERT((datalen & 0x3) == 0, ( "%s: ", __func__)); + + if (write) + ret = SDIOBUS_BLOCKS_WRITE(device_get_parent(sc->sc_dev), sc->sc_dev, 2, addr, data, size); + else + ret = SDIOBUS_BLOCKS_READ(device_get_parent(sc->sc_dev), sc->sc_dev, 2, addr, data, size); + + return ret; + +} + + +static void +brcmwl_sdio_tx_ctrlframe(struct brcmwl_softc *sc, struct mbuf *m){ + + struct brcmwl_sdio_hwhdr *hwhdr; + struct brcmwl_sdio_swhdr *swhdr; + size_t len, roundto; + + len = sizeof(*hwhdr) + sizeof(*swhdr) + m->m_len; + + /* Zero-pad to either block-size or 4-byte alignment. */ + if (len > 512 && (len % 512) != 0) + roundto = 512; + else + roundto = 4; + + KASSERT(roundup(len, roundto) <= sc->sc_bounce_size, ("%s:%d: fail", __func__, __LINE__)); + + + hwhdr = (void *)sc->sc_bounce_buf; + hwhdr->frmlen = htole16(len); + hwhdr->cksum = htole16(~len); + + swhdr = (void *)&hwhdr[1]; + swhdr->seqnr = sc->sc_tx_seq++; + swhdr->chanflag = BRCMWL_SDIO_SWHDR_CHANNEL_CONTROL; + swhdr->nextlen = 0; + swhdr->dataoff = sizeof(*hwhdr) + sizeof(*swhdr); + swhdr->maxseqnr = 0; + + m_copydata(m, 0, m->m_len, (caddr_t)&swhdr[1]); + + if (roundup(len, roundto) != len) + memset(sc->sc_bounce_buf + len, 0, roundup(len, roundto) - len); + + brcmwl_sdio_frame_read_write(sc, sc->sc_bounce_buf, roundup(len, roundto), 1); +} + +static void +brcmwl_sdio_tx_dataframe(struct brcmwl_softc *sc, struct mbuf *m){ + + struct brcmwl_sdio_hwhdr *hwhdr; + struct brcmwl_sdio_swhdr *swhdr; + struct brcmwl_proto_bcdc_hdr *bcdc; + size_t len, roundto; + + len = sizeof(*hwhdr) + sizeof(*swhdr) + sizeof(*bcdc) + m->m_pkthdr.len; + + /* Zero-pad to either block-size or 4-byte alignment. */ + if (len > 512 && (len % 512) != 0) + roundto = 512; + else + roundto = 4; + + KASSERT(roundup(len, roundto) <= sc->sc_bounce_size, ("%s:%d: fail", __func__, __LINE__)); + + hwhdr = (void *)sc->sc_bounce_buf; + hwhdr->frmlen = htole16(len); + hwhdr->cksum = htole16(~len); + + swhdr = (void *)&hwhdr[1]; + swhdr->seqnr = sc->sc_tx_seq++; + swhdr->chanflag = BRCMWL_SDIO_SWHDR_CHANNEL_DATA; + swhdr->nextlen = 0; + swhdr->dataoff = sizeof(*hwhdr) + sizeof(*swhdr); + swhdr->maxseqnr = 0; + + bcdc = (void *)&swhdr[1]; + bcdc->data_offset = 0; + ///bcdc->priority = ieee80211_classify(&sc->sc_sc.sc_ic, m); + bcdc->flags = BRCMWL_BCDC_FLAG_VER(BRCMWL_BCDC_FLAG_PROTO_VER); + bcdc->flags2 = 0; + + m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&bcdc[1]); + + if (roundup(len, roundto) != len) + memset(sc->sc_bounce_buf + len, 0, roundup(len, roundto) - len); + + brcmwl_sdio_frame_read_write(sc, sc->sc_bounce_buf, roundup(len, roundto), 1); + + sc->sc_tx_count--; +} + +/* +void +brcmwl_sdio_tx_frames(struct brcmwl_softc *sc){ + + struct ifnet *ifp = &sc->sc_sc.sc_ic.ic_if; + struct mbuf *m; + int i; + + if (!brcmwl_sdio_tx_ok(sc)) + return; + + i = min((uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq), 32); + while (i--) { + m = ml_dequeue(&sc->sc_tx_queue); + if (m == NULL) + break; + + if (m->m_type == MT_CONTROL) + brcmwl_sdio_tx_ctrlframe(sc, m); + else + brcmwl_sdio_tx_dataframe(sc, m); + + m_freem(m); + } + + if (sc->sc_tx_count < 64) + ifq_restart(&ifp->if_snd); + + + + + + + struct bwn_pio_txqueue *tq; + struct bwn_pio_txpkt *tp = NULL; + struct bwn_softc *sc = mac->mac_sc; + int retrycnt = 0; + + BWN_ASSERT_LOCKED(sc); + + tq = bwn_pio_parse_cookie(mac, status->cookie, &tp); + if (tq == NULL) + return; + + tq->tq_used -= roundup(tp->tp_m->m_pkthdr.len + BWN_HDRSIZE(mac), 4); + tq->tq_free++; + + if (tp->tp_ni != NULL) { + /// + ///Do any tx complete callback. Note this must + /// be done before releasing the node reference. + /// + + /// + /// If we don't get an ACK, then we should log the + /// full framecnt. That may be 0 if it's a PHY + /// failure, so ensure that gets logged as some + /// retry attempt. + // + if (status->ack) { + retrycnt = status->framecnt - 1; + } else { + retrycnt = status->framecnt; + if (retrycnt == 0) + retrycnt = 1; + } + ieee80211_ratectl_tx_complete(tp->tp_ni->ni_vap, tp->tp_ni, + status->ack ? + IEEE80211_RATECTL_TX_SUCCESS : + IEEE80211_RATECTL_TX_FAILURE, + &retrycnt, 0); + + ///if (tp->tp_m->m_flags & M_TXCB) + /// ieee80211_process_callback(tp->tp_ni, tp->tp_m, 0); + + if (tp->tp_m->m_type == MT_CONTROL) + brcmwl_sdio_tx_ctrlframe(sc, tp->tp_m); + else + brcmwl_sdio_tx_dataframe(sc, tp->tp_m); + + ieee80211_free_node(tp->tp_ni); + tp->tp_ni = NULL; + } + m_freem(tp->tp_m); + tp->tp_m = NULL; + TAILQ_INSERT_TAIL(&tq->tq_pktlist, tp, tp_list); + + sc->sc_watchdog_timer = 0; + + + + +}*/ + +static void +brcmwl_sdio_rx_glom(struct brcmwl_softc *sc, uint16_t *sublen, int nsub, uint16_t *nextlen){ + + struct brcmwl_sdio_hwhdr hwhdr; + struct brcmwl_sdio_swhdr swhdr; + ///struct mbuf_list ml, drop; + struct mbuf *m; + size_t flen; + ///off_t off; + uint8_t off; + int i; + + //ml_init(&mlst); + ///ml_init(&droplst); + + if (nsub == 0) + return; + + for (i = 0; i < nsub; i++) { + m = brcmwl_sdio_newbuf(); + if (m == NULL) { + ///ml_purge(&mlst); + return; + } + ///ml_enqueue(&mlst, m); + if (letoh16(sublen[i]) > m->m_len) { + ///ml_purge(&mlst); + return; + } + if (brcmwl_sdio_frame_read_write(sc, mtod(m, char *), letoh16(sublen[i]), 0)) { + ///ml_purge(&mlst); + return; + } + m->m_len = m->m_pkthdr.len = letoh16(sublen[i]); + } + + /// TODO: Verify actual superframe header + ///m = MBUF_LIST_FIRST(&mlst); + if (m->m_len >= sizeof(hwhdr) + sizeof(swhdr)) { + m_copydata(m, 0, sizeof(hwhdr), (caddr_t)&hwhdr); + m_copydata(m, sizeof(hwhdr), sizeof(swhdr), (caddr_t)&swhdr); + *nextlen = swhdr.nextlen << 4; + m_adj(m, sizeof(struct brcmwl_sdio_hwhdr) + + sizeof(struct brcmwl_sdio_swhdr)); + } + + /*while ((m = ml_dequeue(&mlst)) != NULL) { + if (m->m_len < sizeof(hwhdr) + sizeof(swhdr)) + goto drop; + + m_copydata(m, 0, sizeof(hwhdr), (caddr_t)&hwhdr); + m_copydata(m, sizeof(hwhdr), sizeof(swhdr), (caddr_t)&swhdr); + + hwhdr.frmlen = letoh16(hwhdr.frmlen); + hwhdr.cksum = letoh16(hwhdr.cksum); + + if (hwhdr.frmlen == 0 && hwhdr.cksum == 0) + goto drop; + + if ((hwhdr.frmlen ^ hwhdr.cksum) != 0xffff) { + device_printf(sc->sc_dev, "checksum error\n"); + goto drop; + } + + if (hwhdr.frmlen < sizeof(hwhdr) + sizeof(swhdr)) { + device_printf(sc->sc_dev, "checksum error\n"); + goto drop; + } + + flen = hwhdr.frmlen - (sizeof(hwhdr) + sizeof(swhdr)); + if (flen == 0) + goto drop; + if (m->m_len < flen) + goto drop; + + if (swhdr.dataoff < (sizeof(hwhdr) + sizeof(swhdr))) + goto drop; + + off = swhdr.dataoff - (sizeof(hwhdr) + sizeof(swhdr)); + if (off > flen) + goto drop; + + switch (swhdr.chanflag & BRCMWL_SDIO_SWHDR_CHANNEL_MASK) { + case BRCMWL_SDIO_SWHDR_CHANNEL_CONTROL: + device_printf(sc->sc_dev, "control channel not allowed in glom\n"); + goto drop; + + case BRCMWL_SDIO_SWHDR_CHANNEL_EVENT: + case BRCMWL_SDIO_SWHDR_CHANNEL_DATA: + m_adj(m, swhdr.dataoff); + /// sc->sc_sc.sc_proto_ops->proto_rx(&sc->sc_sc, m); + break; + + case BRCMWL_SDIO_SWHDR_CHANNEL_GLOM: + device_printf(sc->sc_dev, " glom not allowed in glom\n"); + goto drop; + + default: + device_printf(sc->sc_dev, " unknown channel\n"); + goto drop; + } + + continue; +drop: + /// ml_enqueue(&droplst, m); + } + + ///ml_purge(&droplst);*/ +} + +static int +brcmwl_proto_bcdc_txctl(struct brcmwl_softc *sc, int reqid, char *buf, size_t *len) +{ + struct brcmwl_proto_bcdc_ctl *ctl, *tmp; + int timeout = 0; + + ctl = malloc(sizeof(*ctl), M_TEMP, M_WAITOK | M_ZERO); + ctl->reqid = reqid; + ctl->buf = buf; + ctl->len = *len; + + /*if(brcmwl_sdio_txctl(sc, ctl)) { + ////DPRINTF(("%s: tx failed\n", DEVNAME(sc))); + return 1; + }*/ + + if (tsleep(ctl, PWAIT, "bwfm", hz)) + timeout = 1; + + TAILQ_FOREACH_SAFE(ctl, &sc->sc_bcdc_rxctlq, next, tmp) { + if (ctl->reqid != reqid) + continue; + if (ctl->done) { + TAILQ_REMOVE(&sc->sc_bcdc_rxctlq, ctl, next); + *len = ctl->len; + free(ctl, M_TEMP); + return 0; + } + if (timeout) { + TAILQ_REMOVE(&sc->sc_bcdc_rxctlq, ctl, next); + device_printf(sc->sc_dev, "timeout waiting for txctl response\n"); + free(ctl->buf, M_TEMP); + free(ctl, M_TEMP); + return 1; + } + break; + } + + device_printf(sc->sc_dev, "did%s find txctl metadata (timeout %d)\n", ctl == NULL ? " not": "", timeout); + return 1; +} + + +static int +brcmwl_proto_bcdc_query_dcmd(struct brcmwl_softc *sc, int ifidx, int cmd, char *buf, size_t *len) +{ + struct brcmwl_proto_bcdc_dcmd *dcmd; + size_t size = sizeof(dcmd->hdr) + *len; + int ret = 1, reqid; + + reqid = sc->sc_bcdc_reqid++; + + if (*len > sizeof(dcmd->buf)) + return ret; + + dcmd = malloc(size, M_TEMP, M_WAITOK | M_ZERO); + dcmd->hdr.cmd = htole32(cmd); + dcmd->hdr.len = htole32(*len); + dcmd->hdr.flags |= BRCMWL_BCDC_DCMD_GET; + dcmd->hdr.flags |= BRCMWL_BCDC_DCMD_ID_SET(reqid); + dcmd->hdr.flags |= BRCMWL_BCDC_DCMD_IF_SET(ifidx); + dcmd->hdr.flags = htole32(dcmd->hdr.flags); + memcpy(&dcmd->buf, buf, *len); + + if (brcmwl_proto_bcdc_txctl(sc, reqid, (char *)dcmd, &size)) { + device_printf(sc->sc_dev, "tx failed\n"); + return ret; + } + + if (buf) { + *len = min(*len, size); + memcpy(buf, dcmd->buf, *len); + } + + if (dcmd->hdr.flags & BRCMWL_BCDC_DCMD_ERROR) + ret = dcmd->hdr.status; + else + ret = 0; + free(dcmd, M_TEMP); + return ret; +} + +static int +brcmwl_proto_bcdc_set_dcmd(struct brcmwl_softc *sc, int ifidx, int cmd, char *buf, size_t len) +{ + struct brcmwl_proto_bcdc_dcmd *dcmd; + size_t size = sizeof(dcmd->hdr) + len; + int ret = 1, reqid; + + reqid = sc->sc_bcdc_reqid++; + + if (len > sizeof(dcmd->buf)) + return ret; + + dcmd = malloc(size, M_TEMP, M_WAITOK | M_ZERO); + dcmd->hdr.cmd = htole32(cmd); + dcmd->hdr.len = htole32(len); + dcmd->hdr.flags |= BRCMWL_BCDC_DCMD_SET; + dcmd->hdr.flags |= BRCMWL_BCDC_DCMD_ID_SET(reqid); + dcmd->hdr.flags |= BRCMWL_BCDC_DCMD_IF_SET(ifidx); + dcmd->hdr.flags = htole32(dcmd->hdr.flags); + memcpy(&dcmd->buf, buf, len); + + if (brcmwl_proto_bcdc_txctl(sc, reqid, (char *)dcmd, &size)) { + device_printf(sc->sc_dev, "tx failed\n"); + return ret; + } + + if (dcmd->hdr.flags & BRCMWL_BCDC_DCMD_ERROR) + ret = dcmd->hdr.status; + else + ret = 0; + free(dcmd, M_TEMP); + return ret; +} + +static void +brcmwl_proto_bcdc_rxctl(struct brcmwl_softc *sc, char *buf, size_t len) +{ + struct brcmwl_proto_bcdc_dcmd *dcmd; + struct brcmwl_proto_bcdc_ctl *ctl, *tmp; + + if (len < sizeof(dcmd->hdr)) + return; + + dcmd = (struct brcmwl_proto_bcdc_dcmd *)buf; + dcmd->hdr.cmd = letoh32(dcmd->hdr.cmd); + dcmd->hdr.len = letoh32(dcmd->hdr.len); + dcmd->hdr.flags = letoh32(dcmd->hdr.flags); + dcmd->hdr.status = letoh32(dcmd->hdr.status); + + TAILQ_FOREACH_SAFE(ctl, &sc->sc_bcdc_rxctlq, next, tmp) { + if (ctl->reqid != BRCMWL_BCDC_DCMD_ID_GET(dcmd->hdr.flags)) + continue; + if (ctl->len != len) { + free(ctl->buf, M_TEMP); + free(ctl, M_TEMP); + return; + } + memcpy(ctl->buf, buf, len); + ctl->done = 1; + wakeup(ctl); + return; + } +} + +/* +void +brcmwl_rx_event(struct brcmwl_softc *sc, struct mbuf *m) +{ + struct brcmwl_cmd_mbuf cmd; + + cmd.m = m; + brcmwl_do_async(sc, brcmwl_rx_event_cb, &cmd, sizeof(cmd)); +} + +void +brcmwl_rx_event_cb(struct brcmwl_softc *sc, void *arg) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct brcmwl_cmd_mbuf *cmd = arg; + struct mbuf *m = cmd->m; + struct brcmwl_event *e = mtod(m, void *); + size_t len = m->m_len; + + if (ntohl(e->msg.event_type) >= BRCMWL_E_LAST) { + m_freem(m); + return; + } + + switch (ntohl(e->msg.event_type)) { + case BRCMWL_E_ESCAN_RESULT: { + struct brcmwl_escan_results *res; + struct brcmwl_bss_info *bss; + size_t reslen; + int i; + if (ntohl(e->msg.status) != BRCMWL_E_STATUS_PARTIAL && + ic->ic_state == IEEE80211_S_SCAN) { + ieee80211_end_scan(ifp); + break; + } + len -= sizeof(*e); + if (len < sizeof(*res)) { + DPRINTF(("%s: results too small\n", DEVNAME(sc))); + m_freem(m); + return; + } + reslen = len; + res = malloc(len, M_TEMP, M_WAITOK); + memcpy(res, (void *)&e[1], len); + if (len < letoh32(res->buflen)) { + DPRINTF(("%s: results too small\n", DEVNAME(sc))); + free(res, M_TEMP, reslen); + m_freem(m); + return; + } + len -= sizeof(*res); + if (len < letoh16(res->bss_count) * sizeof(struct brcmwl_bss_info)) { + DPRINTF(("%s: results too small\n", DEVNAME(sc))); + free(res, M_TEMP, reslen); + m_freem(m); + return; + } + bss = &res->bss_info[0]; + for (i = 0; i < letoh16(res->bss_count); i++) { + brcmwl_scan_node(sc, &res->bss_info[i], len); + len -= sizeof(*bss) + letoh32(bss->length); + bss = (void *)((char *)bss) + letoh32(bss->length); + if (len <= 0) + break; + } + free(res, M_TEMP, reslen); + break; + } + case BRCMWL_E_SET_SSID: + if (ntohl(e->msg.status) != BRCMWL_E_STATUS_SUCCESS) + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + case BRCMWL_E_AUTH: + if (ntohl(e->msg.status) == BRCMWL_E_STATUS_SUCCESS && + ic->ic_state == IEEE80211_S_AUTH) + ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); + else + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + case BRCMWL_E_ASSOC: + if (ntohl(e->msg.status) == BRCMWL_E_STATUS_SUCCESS && + ic->ic_state == IEEE80211_S_ASSOC) + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + else + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + case BRCMWL_E_DEAUTH: + case BRCMWL_E_DISASSOC: + if (ic->ic_state != IEEE80211_S_INIT) + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; + case BRCMWL_E_LINK: + if (ntohl(e->msg.status) == BRCMWL_E_STATUS_SUCCESS && + ntohl(e->msg.reason) == 0) + break; + //// Link status has changed + if (ic->ic_state != IEEE80211_S_INIT) + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + break; +#ifndef IEEE80211_STA_ONLY + case BRCMWL_E_AUTH_IND: + brcmwl_rx_auth_ind(sc, e, len); + break; + case BRCMWL_E_ASSOC_IND: + brcmwl_rx_assoc_ind(sc, e, len, 0); + break; + case BRCMWL_E_REASSOC_IND: + brcmwl_rx_assoc_ind(sc, e, len, 1); + break; + case BRCMWL_E_DEAUTH_IND: + brcmwl_rx_deauth_ind(sc, e, len); + break; + case BRCMWL_E_DISASSOC_IND: + brcmwl_rx_disassoc_ind(sc, e, len); + break; +#endif + default: + DPRINTF(("%s: len %lu datalen %u code %u status %u" + " reason %u\n", __func__, len, ntohl(e->msg.datalen), + ntohl(e->msg.event_type), ntohl(e->msg.status), + ntohl(e->msg.reason))); + break; + } + + m_freem(m); +}*/ + + +static void +brcmwl_rx(struct brcmwl_softc *sc, struct mbuf *m) +{ + /*struct ieee80211com *ic = &sc->sc_ic; + ///struct ifnet *ifp = &ic->ic_if; + if_t ifp = sc->ifp; + enum ieee80211_state ostate; + // enum ieee80211_state ostate = vap->iv_state; + + ///struct mbuf_list mlst = MBUF_LIST_INITIALIZER(); + struct ieee80211_node *ni; +*/ +/* +#ifdef __STRICT_ALIGNMENT + /// Remaining data is an ethernet packet, so align. + if ((mtod(m, paddr_t) & 0x3) != ETHER_ALIGN) { + struct mbuf *m0; + m0 = m_dup_pkt(m, ETHER_ALIGN, M_WAITOK); + m_freem(m); + if (m0 == NULL) { + ifp->if_ierrors++; + return; + } + m = m0; + } +#endif*//* + struct brcmwl_event *e = mtod(m, struct brcmwl_event *); + + if (m->m_len >= sizeof(e->ehdr) && + ntohs(e->ehdr.ether_type) == BRCMWL_ETHERTYPE_LINK_CTL && + memcmp(BRCMWL_BRCM_OUI, e->hdr.oui, sizeof(e->hdr.oui)) == 0 && + ntohs(e->hdr.usr_subtype) == BRCMWL_BRCM_SUBTYPE_EVENT) { + ///brcmwl_rx_event(sc, m); + return; + } + + /// Drop network packets if we are not in RUN state. + if (ostate != IEEE80211_S_RUN) { + m_freem(m); + return; + } + + if ((ic->ic_flags & 123) && m->m_len >= sizeof(e->ehdr) && ntohs(e->ehdr.ether_type) == ETHERTYPE_PAE) { + ///if ((ic->ic_flags & IEEE80211_F_RSNON) && m->m_len >= sizeof(e->ehdr) && ntohs(e->ehdr.ether_type) == ETHERTYPE_PAE) { + ifp->if_ipackets++;*/ +/*#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); +#endif*//* +#ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + ni = ieee80211_find_node(ic, (void *)&e->ehdr.ether_shost); + if (ni == NULL) { + m_free(m); + return; + } + } else +#endif + ni = ic->ic_bss; + ////ieee80211_eapol_key_input(ic, m, ni); + } *//*else { + ml_enqueue(&mlst, m); + if_input(ifp, &mlst); + }*/ +} + +static void +brcmwl_proto_bcdc_rx(struct brcmwl_softc *sc, struct mbuf *m) +{ + struct brcmwl_proto_bcdc_hdr *hdr; + + hdr = mtod(m, struct brcmwl_proto_bcdc_hdr *); + if (m->m_len < sizeof(*hdr)) { + m_freem(m); + return; + } + if (m->m_len < sizeof(*hdr) + (hdr->data_offset << 2)) { + m_freem(m); + return; + } + m_adj(m, sizeof(*hdr) + (hdr->data_offset << 2)); + + ///brcmwl_rx(sc, m); +} + + +static void +brcmwl_sdio_rx_frames(struct brcmwl_softc *sc){ + + uint16_t *sublen, nextlen = 0; + struct mbuf *m; + size_t flen; + uint8_t *data; + ///off_t off; + uint8_t off; + int nsub; + +/// rx = bwn_pio_rx(&mac->mac_method.pio.rx); + + + + struct brcmwl_sdio_hwhdr *hwhdr = (struct brcmwl_sdio_hwhdr *)sc->sc_bounce_buf; + struct brcmwl_sdio_swhdr *swhdr = (struct brcmwl_sdio_swhdr *)&hwhdr[1]; + data = (uint8_t *)&swhdr[1]; + + for (;;) { + /// If we know the next size, just read ahead. + if (nextlen) { + if (brcmwl_sdio_frame_read_write(sc, sc->sc_bounce_buf, nextlen, 0)) + break; + } + else { + if (brcmwl_sdio_frame_read_write(sc, sc->sc_bounce_buf, sizeof(*hwhdr) + sizeof(*swhdr), 0)) + break; + } + + hwhdr->frmlen = letoh16(hwhdr->frmlen); + hwhdr->cksum = letoh16(hwhdr->cksum); + + if (hwhdr->frmlen == 0 && hwhdr->cksum == 0) + break; + + if ((hwhdr->frmlen ^ hwhdr->cksum) != 0xffff) { + device_printf(sc->sc_dev, " checksum error\n"); + break; + } + + if (hwhdr->frmlen < sizeof(*hwhdr) + sizeof(*swhdr)) { + device_printf(sc->sc_dev, " length error\n"); + break; + } + + if (nextlen && hwhdr->frmlen > nextlen) { + device_printf(sc->sc_dev, " read ahead length error (%u > %u)\n", hwhdr->frmlen, nextlen); + break; + } + + sc->sc_tx_max_seq = swhdr->maxseqnr; + + flen = hwhdr->frmlen - (sizeof(*hwhdr) + sizeof(*swhdr)); + if (flen == 0) { + nextlen = swhdr->nextlen << 4; + continue; + } + + if (!nextlen) { + KASSERT(roundup(flen, 4) <= sc->sc_bounce_size - (sizeof(*hwhdr) + sizeof(*swhdr)), ("%s:%d: fail", __func__, __LINE__)); + if (brcmwl_sdio_frame_read_write(sc, data, roundup(flen, 4), 0)) + break; + } + + if (swhdr->dataoff < (sizeof(*hwhdr) + sizeof(*swhdr))) + break; + + off = swhdr->dataoff - (sizeof(*hwhdr) + sizeof(*swhdr)); + if (off > flen) + break; + + switch (swhdr->chanflag & BRCMWL_SDIO_SWHDR_CHANNEL_MASK) { + case BRCMWL_SDIO_SWHDR_CHANNEL_CONTROL: + brcmwl_proto_bcdc_rxctl(sc, data + off, flen - off); + nextlen = swhdr->nextlen << 4; + break; + + case BRCMWL_SDIO_SWHDR_CHANNEL_EVENT: + case BRCMWL_SDIO_SWHDR_CHANNEL_DATA: + m = brcmwl_sdio_newbuf(); + if (m == NULL) + break; + if (flen - off > m->m_len) { + device_printf(sc->sc_dev, " frame bigger than anticipated\n"); + m_free(m); + break; + } + m->m_len = m->m_pkthdr.len = flen - off; + memcpy(mtod(m, char *), data + off, flen - off); + brcmwl_proto_bcdc_rx(sc, m); + nextlen = swhdr->nextlen << 4; + break; + + case BRCMWL_SDIO_SWHDR_CHANNEL_GLOM: + if ((flen % sizeof(uint16_t)) != 0) + break; + nsub = flen / sizeof(uint16_t); + sublen = malloc(nsub * sizeof(uint16_t), M_DEVBUF, M_WAITOK | M_ZERO); + memcpy(sublen, data, nsub * sizeof(uint16_t)); + brcmwl_sdio_rx_glom(sc, sublen, nsub, &nextlen); + free(sublen, M_DEVBUF); + break; + + default: + device_printf(sc->sc_dev, " unknown channel\n"); + break; + } + } +} + +static int +brcmwl_sdiodev_read(struct brcmwl_softc *sc, uint32_t reg, uint32_t *val) +{ + int ret=0xff; + + struct brcmwl_core_priv_t *prCore = sc->prCoreSdio; + struct brcmwl_core_t *sdioCore = brcmwl_chip_get_core(prCore, BCMA_CORE_SDIO_DEV); + if(sdioCore) + ret = brcmwl_sdiobus_read_4(sc, prCore->pub.base + reg, val); + + return ret; + +} + +static int +brcmwl_sdiodev_write(struct brcmwl_softc *sc, uint32_t reg, uint32_t val){ + + int ret=0xff; + + struct brcmwl_core_priv_t *prCore = sc->prCoreSdio; + struct brcmwl_core_t *sdioCore = brcmwl_chip_get_core(prCore, BCMA_CORE_SDIO_DEV); + if(sdioCore) + ret = brcmwl_sdiobus_write_4(sc, prCore->pub.base + reg, val); + + return ret; + +} + + +static void +brcmwl_sdio_readshared(struct brcmwl_softc *sc){ + + struct brcmwl_sdio_sdpcm_t sdpcm; + uint32_t addr, shaddr; + int err; + struct brcmwl_chip_priv_t *prChip = sc->bcmChip; + struct brcmwl_chip_t *pub = &sc->bcmChip->pub; + + shaddr = prChip->pub.rambase + prChip->pub.ramsize - 4; + if (!prChip->pub.rambase && brcmwl_chip_sr_capable(sc, pub)) + shaddr -= prChip->pub.srsize; + + err = brcmwl_sdiobus_read_stream(sc, shaddr, (uint8_t *)&addr, sizeof(addr)); + if (err) + return; + + addr = letoh32(addr); + if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) + return; + + err = brcmwl_sdiobus_read_stream(sc, addr, (uint8_t *)&sdpcm, sizeof(sdpcm)); + if (err) + return; + + sc->console_addr = letoh32(sdpcm.console_addr); +} + +static int +brcmwl_chip_dmp_erom_scan(struct brcmwl_softc *sc, struct brcmwl_chip_priv_t *prChip) +{ + uint32_t eromaddr; + uint8_t desc_type = 0; + uint32_t val; + uint16_t id; + uint8_t nmp, nsp, nmw, nsw, rev; + uint32_t base, wrap; + int err; + + err = brcmwl_sdiobus_read_4( + sc, CORE_CC_REG(SI_ENUM_BASE, eromptr), &eromaddr); + + while (desc_type != DMP_DESC_EOT) { + val = brcmwl_chip_dmp_get_desc(sc, &eromaddr, &desc_type); + if (!(val & DMP_DESC_VALID)) + continue; + + if (desc_type == DMP_DESC_EMPTY) + continue; + + /* need a component descriptor */ + if (desc_type != DMP_DESC_COMPONENT) + continue; + + id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S; + + /* next descriptor must be component as well */ + val = brcmwl_chip_dmp_get_desc(sc, &eromaddr, &desc_type); + if ((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT){ + return EFAULT; + } + + /* only look at cores with master port(s) */ + nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S; + nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S; + nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S; + nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S; + rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S; + + /* need core with ports */ + if (nmw + nsw == 0 && id != BCMA_CORE_PMU) + continue; + + /* try to obtain register address info */ + err = brcmwl_chip_dmp_get_regaddr(sc, &eromaddr, &base, &wrap); + if (err) + continue; + + switch(id){ + case BCMA_CORE_ARM_CR4: + /* finally a core to be added */ + sc->prCoreSoc = brcmwl_chip_add_core(prChip, id, base, wrap); + if(sc->prCoreSoc == NULL) + return (ENOMEM); + + sc->prCoreSoc->pub.rev = rev; + break; + + case BCMA_CORE_ARM_CA7: + /* finally a core to be added */ + sc->prCoreSoc = brcmwl_chip_add_core(prChip, id, base, wrap); + if(sc->prCoreSoc == NULL) + return (ENOMEM); + + sc->prCoreSoc->pub.rev = rev; + break; + + case BCMA_CORE_ARM_CM3: + /* finally a core to be added */ + sc->prCoreSoc = brcmwl_chip_add_core(prChip, id, base, wrap); + if(sc->prCoreSoc == NULL) + return (ENOMEM); + + sc->prCoreSoc->pub.rev = rev; + break; + + case BCMA_CORE_INTERNAL_MEM: + /* finally a core to be added */ + sc->prCoreRam = brcmwl_chip_add_core(prChip, id, base, wrap); + if(sc->prCoreRam == NULL) + return (ENOMEM); + + sc->prCoreRam->pub.rev = rev; + break; + + case BCMA_CORE_80211: + /* finally a core to be added */ + sc->prCoreNet = brcmwl_chip_add_core(prChip, id, base, wrap); + if(sc->prCoreNet == NULL) + return (ENOMEM); + + sc->prCoreNet->pub.rev = rev; + break; + + case BCMA_CORE_CHIPCOMMON: + + /* finally a core to be added */ + sc->prCoreComm = brcmwl_chip_add_core(prChip, id, base, wrap); + if(sc->prCoreComm == NULL) + return (ENOMEM); + + sc->prCoreComm->pub.rev = rev; + + break; + + case BCMA_CORE_SDIO_DEV: + + /* finally a core to be added */ + sc->prCoreSdio = brcmwl_chip_add_core(prChip, id, base, wrap); + if(sc->prCoreSdio == NULL) + return (ENOMEM); + + sc->prCoreSdio->pub.rev = rev; + break; + } + + } + + return 0; +} + + +static bool +brcmwl_chip_socram_banksize(struct brcmwl_softc *sc, + struct brcmwl_core_priv_t *prCore, + uint8_t idx, + uint32_t *banksize) +{ + uint32_t bankinfo; + uint32_t bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); + int ret; + + bankidx |= idx; + ret = brcmwl_chip_core_write_4(sc, prCore, SOCRAMREGOFFS(bankidx), bankidx); + + ret = brcmwl_chip_core_read_4(sc, prCore, SOCRAMREGOFFS(bankinfo), &bankinfo); + + *banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1; + *banksize *= SOCRAM_BANKINFO_SZBASE; + return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK); +} + +static int +brcmwl_chip_socram_ramsize(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore, uint32_t *ramsize, uint32_t *srsize) +{ + uint32_t coreinfo; + uint32_t nb, banksize, lss; + bool retent; + int i; + int ret=123; + + *ramsize = 0; + *srsize = 0; + + if(prCore->pub.rev < 4){ + return 0; + } + + if (!brcmwl_chip_axi_iscoreup(sc, prCore)) + brcmwl_chip_resetcore(sc, sc->prCoreRam, 0, 0, 0); + + ret = brcmwl_chip_core_read_4(sc, prCore, SOCRAMREGOFFS(coreinfo), &coreinfo); + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + + if ((prCore->pub.rev <= 7) || (prCore->pub.rev == 12)) { + banksize = (coreinfo & SRCI_SRBSZ_MASK); + lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT; + if (lss != 0) + nb--; + *ramsize = nb * (1 << (banksize + SR_BSZ_BASE)); + if (lss != 0) + *ramsize += (1 << ((lss - 1) + SR_BSZ_BASE)); + } + else { + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + for (i = 0; i < nb; i++) { + retent = brcmwl_chip_socram_banksize(sc, prCore, i, &banksize); + *ramsize += banksize; + if (retent) + *srsize += banksize; + } + } + + return ret; +} + + + +static int +brcmwl_chip_get_raminfo(struct brcmwl_softc *sc) +{ + int ret; + + struct brcmwl_core_priv_t *prCore = sc->prCoreRam; + struct brcmwl_chip_priv_t *prChip = sc->bcmChip; + struct brcmwl_core_t *mem = brcmwl_chip_get_core(prCore, BCMA_CORE_INTERNAL_MEM); + if (!mem) { + device_printf(sc->sc_dev, "No memory cores found. \n"); + return (ENOMEM); + } + + ret = brcmwl_chip_socram_ramsize(sc, sc->prCoreRam, &prChip->pub.ramsize, &prChip->pub.srsize); + if(!ret){ + if(!prChip->pub.ramsize) + prChip->pub.ramsize = sc->predef_val.ramsize; + if(!prChip->pub.srsize) + prChip->pub.srsize = sc->predef_val.srsize; + } + else { + // use pre-define table + prChip->pub.rambase = sc->predef_val.rambase; + prChip->pub.ramsize = sc->predef_val.ramsize; + prChip->pub.srsize = sc->predef_val.srsize; + } + + if (!prChip->pub.ramsize) { + device_printf(sc->sc_dev, "RAM size is undetermined.\n"); + return (ENOMEM); + } + + if (prChip->pub.ramsize > BRCMWL_CHIP_MAX_MEMSIZE) { + device_printf(sc->sc_dev, "RAM size is incorrect.\n"); + return (ENOMEM); + } + + return 0; + + +} + +static void +brcmwl_chip_disable_arm(struct brcmwl_softc *sc, struct brcmwl_chip_priv_t *prChip, uint16_t id){ + + uint32_t val; + struct brcmwl_core_priv_t *prCore = sc->prCoreSoc; + + switch (id) { + case BCMA_CORE_ARM_CM3: + brcmwl_chip_coredisable(sc, prCore, 0, 0); + break; + case BCMA_CORE_ARM_CR4: + break; + case BCMA_CORE_ARM_CA7: + + /* clear all IOCTL bits except HALT bit */ + brcmwl_sdiobus_read_4(sc, prCore->wrapbase + BCMA_IOCTL, &val); + val &= ARMCR4_BCMA_IOCTL_CPUHALT; + brcmwl_chip_resetcore(sc, prCore, val, ARMCR4_BCMA_IOCTL_CPUHALT, ARMCR4_BCMA_IOCTL_CPUHALT); + break; + default: + device_printf(sc->sc_dev, "unknown id: %u\n", id); + break; + } +} + +static inline void +brcmwl_chip_cm3_set_passive(struct brcmwl_softc *sc, struct brcmwl_chip_priv_t *prChip){ + + brcmwl_chip_disable_arm(sc, prChip, BCMA_CORE_ARM_CM3); + + struct brcmwl_core_priv_t *prCore =NULL; + prCore = sc->prCoreNet; + brcmwl_chip_resetcore(sc, prCore, D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); + + + prCore = sc->prCoreRam; + brcmwl_chip_resetcore(sc, prCore, 0, 0, 0); + + if (prChip->pub.chip == BRCM_CC_43430_CHIP_ID) { + brcmwl_chip_core_write_4(sc, prCore, SOCRAMREGOFFS(bankidx), 3); + brcmwl_chip_core_write_4(sc, prCore, SOCRAMREGOFFS(bankpda), 0); + } + +} + +static inline void +brcmwl_chip_cr4_set_passive(struct brcmwl_softc *sc, struct brcmwl_chip_priv_t *prChip){ + + brcmwl_chip_disable_arm(sc, prChip, BCMA_CORE_ARM_CR4); + + struct brcmwl_core_priv_t *prCore = sc->prCoreNet; + brcmwl_chip_resetcore(sc, prCore, D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); + + +} + +static inline void +brcmwl_chip_ca7_set_passive(struct brcmwl_softc *sc, struct brcmwl_chip_priv_t *prChip){ + + brcmwl_chip_disable_arm(sc, prChip, BCMA_CORE_ARM_CA7); + + struct brcmwl_core_priv_t *prCore = sc->prCoreNet; + brcmwl_chip_resetcore(sc, prCore, D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); + +} + +static int +brcmwl_chip_set_passive(struct brcmwl_softc *sc, struct brcmwl_chip_t *pub){ + + struct brcmwl_chip_priv_t *prChip = sc->bcmChip; + struct brcmwl_core_t *bcmCore = NULL; + struct brcmwl_core_priv_t *prCore = sc->prCoreSoc; + + bcmCore = brcmwl_chip_get_core(prCore, BCMA_CORE_ARM_CR4); + if (bcmCore) { + brcmwl_chip_cr4_set_passive(sc, prChip); + return 0; + } + bcmCore = brcmwl_chip_get_core(prCore, BCMA_CORE_ARM_CA7); + if (bcmCore) { + brcmwl_chip_ca7_set_passive(sc, prChip); + return 0; + } + bcmCore = brcmwl_chip_get_core(prCore, BCMA_CORE_ARM_CM3); + if (bcmCore) { + brcmwl_chip_cm3_set_passive(sc, prChip); + return 0; + } + else + return 1; + +} + + +static bool +brcmwl_chip_cm3_set_active(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore){ + + struct brcmwl_core_t *tmpcore = NULL; + + struct brcmwl_core_priv_t *prCoreRam = sc->prCoreRam; + tmpcore= brcmwl_chip_get_core(prCoreRam, BCMA_CORE_INTERNAL_MEM); + if (tmpcore) { + if (brcmwl_chip_axi_iscoreup(sc, prCoreRam)) { + printf("SOCRAM core is down after reset?\n"); + return false; + } + } + + tmpcore = brcmwl_chip_get_core(prCore, BCMA_CORE_ARM_CM3); + if(tmpcore) + brcmwl_chip_resetcore(sc, prCore, 0, 0, 0); + else + printf("SOC core is down after reset?\n"); + + return true; +} + + +static bool +brcmwl_chip_set_active(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore) +{ + struct brcmwl_core_t *tmpcore = brcmwl_chip_get_core(prCore, BCMA_CORE_ARM_CM3); + if (tmpcore) + return brcmwl_chip_cm3_set_active(sc, prCore); + + return false; +} + + +static struct brcmwl_core_t * +brcmwl_chip_get_chipcommon(struct brcmwl_softc *sc, struct brcmwl_core_priv_t *prCore) +{ + return brcmwl_chip_get_core(prCore, BCMA_CORE_CHIPCOMMON); + +} + +static struct brcmwl_core_t * +brcmwl_chip_get_pmu(struct brcmwl_softc *sc, struct brcmwl_chip_t *pub){ + + struct brcmwl_core_priv_t *prCore = NULL; + prCore = sc->prCoreComm; + if (prCore->pub.rev >= 35 && pub->cc_caps_ext & BCMA_CC_CAP_EXT_AOB_PRESENT) { + sc->prCorePmu = malloc(sizeof(struct brcmwl_core_priv_t), M_TEMP, M_ZERO | M_WAITOK); + if(sc->prCorePmu == NULL) + return NULL; + + prCore = sc->prCorePmu; + struct brcmwl_core_t *pmu = brcmwl_chip_get_core(prCore, BCMA_CORE_PMU); + if (pmu) + return pmu; + } + + return &prCore->pub; +} + +static int +brcm_dhdcore_init(struct brcmwl_softc *sc){ + + int ret; + uint32_t reg32; + uint8_t reg; + struct brcmwl_chip_t *pub = &sc->bcmChip->pub; + + device_printf(sc->sc_dev, "Access Identification. Offset 0x%x ...... ", CORE_CC_REG(SI_ENUM_BASE, chipid)); + ret = brcmwl_sdiobus_read_4(sc, CORE_CC_REG(SI_ENUM_BASE, chipid), ®32); + if(!ret){ + printf("[ Completed ]\n"); + pub->chip = reg32 & CID_ID_MASK; + pub->chiprev = (reg32 & CID_REV_MASK) >> CID_REV_SHIFT; + uint32_t socitype = (reg32 & CID_TYPE_MASK) >> CID_TYPE_SHIFT; + char modelid[FWCHAR_LEN]; + memset(modelid, '\0', sizeof(modelid)); + + if(!brcmwl_check_id(sc->sc_dev, pub->chiprev, &sc->predef_val)){ + memcpy(modelid, sc->predef_val.fwname, MANPREFIX_LEN); + sprintf(pub->name, "%s%d", modelid, (int)pub->chip); + device_printf(sc->sc_dev, "Initialize %s Rev.%d, Pkg.%x, %s Bus. \n", + pub->name, + (int)pub->chiprev, + ((reg32 & CID_PKG_MASK) >> CID_PKG_SHIFT), + socitype ? "AXI" : "SSB" ); + } + + + if(socitype == 1) { // AXI BUS + + brcmwl_chip_dmp_erom_scan(sc, sc->bcmChip); + } + // if (socitype == SOCI_SSB) { + /// do we need to support SSB (OLD) bus? + ///} + + ret = brcmwl_chip_cores_check(sc, sc->bcmChip); + if (ret) + return ret; + + device_printf(sc->sc_dev, "CoreSoc (Rev.%d) Initializing ...... ", sc->prCoreSoc->pub.rev); + ret = brcmwl_chip_set_passive(sc, &sc->bcmChip->pub); + if(!ret) + printf("[ Completed ]\n"); + else + printf("[ Failed ] 0x%x\n", ret); + + + device_printf(sc->sc_dev, "CoreRam (Rev.%d) Initializing ...... ", sc->prCoreRam->pub.rev); + ret = brcmwl_chip_get_raminfo(sc); + if(!ret) + printf("Func2 RamSize: %dk, Func1 RamSize: %dk at 0x%x [ Completed ]\n", pub->ramsize / 1024, pub->srsize / 1024, pub->rambase); + else + printf("[ Failed ] 0x%x\n", ret); + + uint32_t coreComm = sc->prCoreComm->pub.base; + + ret = brcmwl_sdiobus_read_4(sc, CORE_CC_REG(coreComm, capabilities), &pub->cc_caps); + ret = brcmwl_sdiobus_read_4(sc, CORE_CC_REG(coreComm, capabilities_ext), &pub->cc_caps_ext); + + struct brcmwl_core_t *bcmCore = NULL; + struct brcmwl_core_priv_t *prCore = NULL; + + device_printf(sc->sc_dev, "CorePmu "); + bcmCore = brcmwl_chip_get_pmu(sc, pub); + if (pub->cc_caps & CC_CAP_PMU) { + ret = brcmwl_sdiobus_read_4(sc, CORE_CC_REG(bcmCore->base, pmucapabilities), ®32); + pub->pmurev = reg32 & PCAP_REV_MASK; + pub->pmucaps = reg32; + printf("(Rev.%d)", pub->pmurev); + } + printf(" Initializing ...... "); + if(!ret){ + printf("Capabilities: 0x%x [ Completed ]\n", pub->pmucaps); + } + else + printf("[ Failed ] 0x%x\n", ret); + + /* execute bus core specific setup + if (chip->ops->setup) + ret = chip->ops->setup(chip->ctx, pub); */ + + device_printf(sc->sc_dev, "CoreSDIO (Rev.%d) Initializing ...... ", sc->prCoreSdio->pub.rev); + prCore = sc->prCoreSdio; + bcmCore = brcmwl_chip_get_core(prCore, BCMA_CORE_SDIO_DEV); + if(bcmCore != NULL){ + if(prCore->pub.rev >= 12){ + ret = brcmwl_sdiobus_read_1(sc, 1, BRCMWL_SDIO_FUNC1_SLEEPCSR, ®); + if (!(reg & BRCMWL_SDIO_FUNC1_SLEEPCSR_KSO)) { + reg |= BRCMWL_SDIO_FUNC1_SLEEPCSR_KSO; + ret = brcmwl_sdiobus_write_1(sc, 1, BRCMWL_SDIO_FUNC1_SLEEPCSR, reg); + } + } + if(!ret) + printf("[ Completed ]\n"); + } + else + printf("[ Failed ]\n"); + + device_printf(sc->sc_dev, "CoreCommon (Rev.%d) Initializing ...... ", sc->prCoreComm->pub.rev); + prCore = sc->prCoreComm; + bcmCore = brcmwl_chip_get_core(prCore, BCMA_CORE_CHIPCOMMON); + if(bcmCore != NULL) + printf("Cap: 0x%x, Cap_ext: 0x%x [ Completed ]\n", pub->cc_caps, pub->cc_caps_ext); + else + printf("[ Failed ]\n"); + + } + else{ + printf("[ Failed ] 0x%x\n", ret); + } + + return ret; +} // brcm_dhdcore_init + +static void +brcm_dhdcore_destroy(struct brcmwl_softc *sc){ + + free(sc->prCoreSoc, M_TEMP); + free(sc->prCoreRam, M_TEMP); + free(sc->prCoreSdio, M_TEMP); + if(sc->prCorePmu != NULL) + free(sc->prCorePmu, M_TEMP); + free(sc->prCoreNet, M_TEMP); + free(sc->prCoreComm, M_TEMP); + free(sc->bcmChip, M_TEMP); + + sc->prCoreSoc = NULL; + sc->prCoreRam = NULL; + sc->prCoreSdio = NULL; + if(sc->prCorePmu != NULL) + sc->prCorePmu = NULL; + sc->prCoreNet = NULL; + sc->prCoreComm = NULL; + sc->bcmChip = NULL; + +} + +/////////////////////////// +/* + * Reset - SIBA. + * + * XXX TODO: implement BCMA version! + */ +void +bwn_reset_core(struct bwn_mac *mac, int g_mode){ + + struct brcmwl_softc *sc = mac->mac_sc; + int ret; + uint32_t reg32; + uint8_t reg; + + uint32_t reg32_val; + + DPRINTF(sc, BWN_DEBUG_RESET, "%s: g_mode=%d\n", __func__, g_mode); + + device_printf(sc->sc_dev, "WLAN backplane Reset ...... "); + ret = brcmwl_sdiobus_read_1(sc, 0, SDIO_CCCR_BRCM_CARDCTRL, ®); + if(ret) + printf("[ Access Failed ]%x\n", ret); + + reg |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET; + ret = brcmwl_sdiobus_write_1(sc, 0, SDIO_CCCR_BRCM_CARDCTRL, reg); + if(!ret) + printf("[ Completed ]\n"); + else + printf("[ Failed ] 0x%x\n", ret); + + + device_printf(sc->sc_dev, "PMU Control Reset ...... "); + struct brcmwl_chip_t *pub = &sc->bcmChip->pub; + struct brcmwl_core_t *bcmCore = brcmwl_chip_get_pmu(sc, pub); + reg32 = CORE_CC_REG(bcmCore->base, pmucontrol); + ret = brcmwl_sdiobus_read_4(sc, reg32, ®32_val); + if(ret) + printf("[ Aceess Failed ] 0x%x\n", ret); + + reg32_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); + ret = brcmwl_sdiobus_write_4(sc, reg32, reg32_val); + if(!ret) + printf("[ Completed ]\n"); + else + printf("[ Failed ] 0x%x\n", ret); + +} + +static int +brcmwl_sdio_sdclk(struct brcmwl_softc *sc, bool on){ + + if (on) + sc->clkstate = CLK_SDONLY; + else + sc->clkstate = CLK_NONE; + + return 0; +} + +static int +brcmwl_sdio_htclk(struct brcmwl_softc *sc, bool on, bool pendok){ + + int err; + uint8_t clkctl, clkreq, devctl; + clkctl = 0; + int trycnt = 1000; + + if (sc->sr_enabled) { + sc->clkstate = (on ? CLK_AVAIL : CLK_SDONLY); + return 0; + } + + if (on) { + clkreq = sc->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;; + err = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq); + if(err){ + printf("HT Avail request error: %d\n", err); + return 123; + } + + err = brcmwl_sdiobus_read_1(sc, 1, SBSDIO_FUNC1_CHIPCLKCSR, &clkctl); + if(err){ + printf("HT Avail read error: %d\n", err); + return 123; + } + + if (!SBSDIO_CLKAV(clkctl, sc->alp_only) && pendok) { + err = brcmwl_sdiobus_read_1(sc, 1, SBSDIO_DEVICE_CTL, &devctl); + if(err){ + printf("Devctl error setting CA: %d\n", err); + return 123; + } + + devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; + err = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_DEVICE_CTL, devctl); + if(err){ + printf("devctl |= SBSDIO_DEVCTL_CA_INT_ONLY %d\n", err); + } + + device_printf(sc->sc_dev, "CLKCTL: set PENDING\n"); + sc->clkstate = CLK_PENDING; + + return 0; + + } + else if (sc->clkstate == CLK_PENDING) { + err = brcmwl_sdiobus_read_1(sc, 1, SBSDIO_DEVICE_CTL, &devctl); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + err = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_DEVICE_CTL, devctl); + } + + do{ + if(SBSDIO_CLKAV(clkctl, sc->alp_only)) + break; + + err = brcmwl_sdiobus_read_1(sc, 1, SBSDIO_FUNC1_CHIPCLKCSR, &clkctl); + DELAY(1000); + }while(trycnt-- > 0); + + if(trycnt < 1){ + device_printf(sc->sc_dev, "HT Avail timeout clkctl 0x%02x \n", clkctl); + return 123; + } + else + sc->clkstate = CLK_AVAIL; + } + else { + if (sc->clkstate == CLK_PENDING) { + err = brcmwl_sdiobus_read_1(sc, 1, SBSDIO_DEVICE_CTL, &devctl); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + err = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_DEVICE_CTL, devctl); + } + + sc->clkstate = CLK_SDONLY; + err = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_FUNC1_CHIPCLKCSR, 0); + if (err) { + device_printf(sc->sc_dev, "Failed access turning clock off: %d\n", err); + return 123; + } + } + + return 0; +} + +/* clkstate */ +#define CLK_NONE 0 +#define CLK_SDONLY 1 +#define CLK_PENDING 2 +#define CLK_AVAIL 3 + +static int +brcmwl_sdio_clkctl(struct brcmwl_softc *sc, unsigned int target, bool pendok){ + + if (sc->clkstate == target) + return 0; + + switch (target) { + case CLK_AVAIL: + if (sc->clkstate == CLK_NONE) + brcmwl_sdio_sdclk(sc, true); + brcmwl_sdio_htclk(sc, true, pendok); + break; + + case CLK_SDONLY: + if (sc->clkstate == CLK_NONE) + brcmwl_sdio_sdclk(sc, true); + else if (sc->clkstate == CLK_AVAIL) + brcmwl_sdio_htclk(sc, false, false); + else + device_printf(sc->sc_dev, "request for %d -> %d\n", sc->clkstate, target); + break; + + case CLK_NONE: + if (sc->clkstate == CLK_AVAIL) + brcmwl_sdio_htclk(sc, false, false); + brcmwl_sdio_sdclk(sc, false); + break; + } + + return 0; +} + +static int +brcmwl_sdio_sr_init(struct brcmwl_softc *sc){ + + int err = 0; + uint8_t val; + + err = brcmwl_sdiobus_read_1(sc, 1, SBSDIO_FUNC1_WAKEUPCTRL, &val); + if (err) { + printf("error reading SBSDIO_FUNC1_WAKEUPCTRL\n"); + return err; + } + + val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; + err = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_FUNC1_WAKEUPCTRL, val); + if (err) { + printf("error writing SBSDIO_FUNC1_WAKEUPCTRL\n"); + return err; + } + + /* Add CMD14 Support */ + err = brcmwl_sdiobus_write_1(sc, 0, SDIO_CCCR_BRCM_CARDCAP, + SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT); + if (err) { + printf("error writing SDIO_CCCR_BRCM_CARDCAP\n"); + return err; + } + + err = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_FUNC1_CHIPCLKCSR, BIT_CHIPCLKCSR_FORCE_HT); + if (err) { + printf("error writing SBSDIO_FUNC1_CHIPCLKCSR\n"); + return err; + } + + sc->sr_enabled = true; + return err; +} + +static bool +brcmwl_chip_sr_capable(struct brcmwl_softc *sc, struct brcmwl_chip_t *pub){ + + int ret; + uint32_t base, addr, reg; + struct brcmwl_core_t *pmu = brcmwl_chip_get_pmu(sc, pub); + struct brcmwl_core_priv_t *prCore = sc->prCoreComm; + struct brcmwl_core_t *corecc = brcmwl_chip_get_chipcommon(sc, prCore); + + /* old chips with PMU version less than 17 don't support save restore */ + if (pub->pmurev < 17) + return false; + + base = corecc->base; + switch (pub->chip) { + case BRCM_CC_43430_CHIP_ID: + addr = CORE_CC_REG(base, sr_control1); + ret = brcmwl_sdiobus_read_4(sc, addr, ®); + return reg != 0; + + default: + addr = CORE_CC_REG(pmu->base, pmucapabilities_ext); + ret = brcmwl_sdiobus_read_4(sc, addr, ®); + if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0) + return false; + + addr = CORE_CC_REG(pmu->base, retention_ctl); + ret = brcmwl_sdiobus_read_4(sc, addr, ®); + return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK | PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; + } +} + +static int +bwn_attach_core(struct bwn_mac *mac){ + + struct brcmwl_softc *sc = mac->mac_sc; + int error, have_bg = 0, have_a = 0; + uint8_t reg; + + sc->bcmChip = malloc(sizeof(struct brcmwl_chip_priv_t), M_TEMP, M_WAITOK | M_ZERO); + if(sc->bcmChip == NULL) + return (ENOMEM); + sc->bcmChip->num_cores = 0; + + + /* Enable function 2 (frame transfers)*/ + device_printf(sc->sc_dev, "Enabled Function 1 ...... "); + error = brcmwl_sdiobus_read_1(sc, 0, SD_IO_CCCR_FN_ENABLE, ®); + if(error){ + printf("[ Aceess Failed ] 0x%x\n", error); + goto fail; + } + + reg |= BIT_CCCR_ENAFUNC1; + error = brcmwl_sdiobus_write_1(sc, 0, SD_IO_CCCR_FN_ENABLE, reg); + if(!error) + printf("[ Completed ]\n"); + else{ + printf("[ Failed ] 0x%x\n", error); + goto fail; + } + + reg = 0; // technically, it wouldn't stuck cause func1/func2 was enabled + while((reg & BIT_CCCR_ENAFUNC1) == 0){ + reg = sdio_get_io_ready(sc->sc_dev); + }; + +/* + /// Force PLL off until brcmwl_chip_attach() + /// programs PLL control regs + uint8_t clkctl; + error = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_FUNC1_CHIPCLKCSR, (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ)); + if (!error) + error = brcmwl_sdiobus_read_1(sc, 1, SBSDIO_FUNC1_CHIPCLKCSR, &clkctl); + + if (error || ((clkctl & ~SBSDIO_AVBITS) != (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ))) { + device_printf(sc->sc_dev, "ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", error, clkctl); + /// goto fail; + }*/ + if(brcm_dhdcore_init(sc)) + goto fail; + + ///bwn_reset_core(mac, have_bg); + + /* + * XXX The PHY-G support doesn't do 5GHz operation. + */ + if (mac->mac_phy.type != BWN_PHYTYPE_LP && + mac->mac_phy.type != BWN_PHYTYPE_N) { + device_printf(sc->sc_dev, "%s: forcing 2GHz only; no dual-band support for PHY\n", __func__); + have_a = 0; + have_bg = 1; + } + + mac->mac_phy.phy_n = NULL; + + mac->mac_phy.gmode = have_bg; + if (mac->mac_phy.attach != NULL) { + error = mac->mac_phy.attach(mac); + if (error) { + device_printf(sc->sc_dev, "failed\n"); + goto fail; + } + } + + bwn_reset_core(mac, have_bg); + + error = bwn_chiptest(mac); + if (error) + goto fail; + + error = brcmwl_fw_fillinfo(mac); + if (error){ + goto fail; + } + + sc->alp_only = true; + + error = brcmwl_fw_loadinitvals(mac); // upload firmware to WLAN card + if (error) { + brcmwl_sdio_clkctl(sc, CLK_SDONLY, 0); + goto fail; + } + sc->alp_only = false; + + + /* Start the watchdog timer */ + + + /* Make sure backplane clock is on, needed to generate F2 interrupt */ + brcmwl_sdio_clkctl(sc, CLK_AVAIL, false); + if (sc->clkstate != CLK_AVAIL) + goto fail; + + + /* Force clocks on backplane to be sure F2 interrupt propagates */ + uint8_t saveclk; + device_printf(sc->sc_dev, "Force clocks on backplane ...... "); + error = brcmwl_sdiobus_read_1(sc, 1, SD_IO_FUNC1_CHIPCLKCSR, &saveclk); + if(error) + printf("[ Access Failed ] %x\n", error); + + saveclk |= BIT_CHIPCLKCSR_FORCE_HT; + error = brcmwl_sdiobus_write_1(sc, 1, SD_IO_FUNC1_CHIPCLKCSR, saveclk); + if(!error) + printf("[ Completed ]\n"); + else + printf("[ Failed ] 0x%x\n", error); + + + + error = brcmwl_sdiodev_write(sc, SDPCMD_TOSBMAILBOXDATA, SDPCM_PROT_VERSION << SDPCM_PROT_VERSION_SHIFT); + if(error) + printf("tosbmailboxdata fail %x \n", error); + + device_printf(sc->sc_dev, "Enabled Function 2 ...... "); + error = brcmwl_sdiobus_read_1(sc, 0, SD_IO_CCCR_FN_ENABLE, ®); + if(error) + printf("[ Access Failed ] %x\n", error); + + reg |= BIT_CCCR_ENAFUNC2; + error = brcmwl_sdiobus_write_1(sc, 0, SD_IO_CCCR_FN_ENABLE, reg); + if(!error) + printf("[ Completed ]\n"); + else + printf("[ Failed ] 0x%x\n", error); + /// got fail + +/* +/// register it to INTERRUPT CONTROL? + ///If F2 successfully enabled, set core and enable interrupts + // Set up the interrupt mask and enable interrupts + sc->hostintmask = HOSTINTMASK; + error = brcmwl_sdiobus_write_4(sc, prCore->pub.base + SD_REG(hostintmask), sc->hostintmask);*/ + + error = brcmwl_sdiodev_write(sc, SDPCMD_HOSTINTMASK, SDPCMD_INTSTATUS_HMB_SW_MASK | SDPCMD_INTSTATUS_CHIPACTIVE); + if(!error){ + error = brcmwl_sdiobus_write_1(sc, 1, SBSDIO_WATERMARK, 8); + } + else{ + error = brcmwl_sdiobus_read_1(sc, 0, SD_IO_CCCR_FN_ENABLE, ®); + reg &= ~(BIT_CCCR_ENAFUNC1 | BIT_CCCR_ENAFUNC2); + error = brcmwl_sdiobus_write_1(sc, 0, SD_IO_CCCR_FN_ENABLE, reg); + } + + struct brcmwl_chip_t *pub = &sc->bcmChip->pub; + if(brcmwl_chip_sr_capable(sc, pub)) + brcmwl_sdio_sr_init(sc); + else + brcmwl_sdiobus_write_1(sc, 1, SBSDIO_FUNC1_CHIPCLKCSR, saveclk); + + + error = bwn_phy_getinfo(mac, have_bg); + if (error) + goto fail; + + error = bwn_setup_channels(mac, have_bg, have_a); + if (error) { + device_printf(sc->sc_dev, "failed to setup channels\n"); + goto fail; + } + + if (sc->sc_curmac == NULL) + sc->sc_curmac = mac; + + if(mac->mac_phy.switch_analog != NULL) + mac->mac_phy.switch_analog(mac, 0); + + /// siba_dev_down(sc->sc_dev, 0); +fail: + /// siba_powerdown(sc->sc_dev); + brcmwl_release_firmware(mac); + return (error); +} + +static void +bwn_txpwr(void *arg, int npending) +{ + struct bwn_mac *mac = arg; + struct brcmwl_softc *sc = mac->mac_sc; + + BWN_LOCK(sc); + if (mac && mac->mac_status >= BWN_MAC_STATUS_STARTED && + mac->mac_phy.set_txpwr != NULL) + mac->mac_phy.set_txpwr(mac); + + BWN_UNLOCK(sc); +} + +static void +bwn_attach_pre(struct brcmwl_softc *sc){ + + BWN_LOCK_INIT(sc); + TAILQ_INIT(&sc->sc_maclist); + TAILQ_INIT(&sc->sc_bcdc_rxctlq); + callout_init_mtx(&sc->sc_rfswitch_ch, &sc->sc_mtx, 0); + callout_init_mtx(&sc->sc_task_ch, &sc->sc_mtx, 0); + callout_init_mtx(&sc->sc_watchdog_ch, &sc->sc_mtx, 0); + mbufq_init(&sc->sc_snd, ifqmaxlen); + sc->sc_tq = taskqueue_create_fast("bwn_taskq", M_NOWAIT, taskqueue_thread_enqueue, &sc->sc_tq); + taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->sc_dev)); +} + +static int +brcmwl_attach(device_t dev) { + + int error; + + struct brcmwl_softc *sc = BRCMWL_SOFTC(dev); + sc->sc_dev = dev; + +#ifdef BWN_DEBUG + sc->sc_debug = bwn_debug; +#endif + + + if ((sc->sc_flags & BWN_FLAG_ATTACHED) == 0) { + bwn_attach_pre(sc); + sc->sc_flags |= BWN_FLAG_ATTACHED; + } + + /*if (!TAILQ_EMPTY(&sc->sc_maclist)) { + ///if (siba_get_pci_device(dev) != 0x4313 && + siba_get_pci_device(dev) != 0x431a && + siba_get_pci_device(dev) != 0x4321) { + device_printf(sc->sc_dev, "skip 802.11 cores\n"); + return (ENODEV); + } + device_printf(sc->sc_dev, "TAILQ_EMPTY(&sc->sc_macli \n"); + + }*/ + + sc->sc_bounce_size = 64 * 1024; + sc->sc_bounce_buf = malloc(sc->sc_bounce_size, M_TEMP, M_WAITOK | M_ZERO); + struct bwn_mac *mac = malloc(sizeof(*mac), M_DEVBUF, M_WAITOK | M_ZERO); + mac->mac_sc = sc; + mac->mac_status = BWN_MAC_STATUS_UNINIT; + if (bwn_bfp != 0) + mac->mac_flags |= BWN_MAC_FLAG_BADFRAME_PREEMP; + + TASK_INIT(&mac->mac_hwreset, 0, bwn_hwreset, mac); + TASK_INIT(&mac->mac_intrtask, 0, bwn_intrtask, mac); + TASK_INIT(&mac->mac_txpower, 0, bwn_txpwr, mac); + + uint8_t reg_val; + reg_val = sdio_get_sdiocccr_rev(dev); + printf("SDIO Revision: %x / CCCR Revision: %x \n", reg_val >> 4, reg_val & 0xf); + + + error = bwn_attach_core(mac); + if (error) + goto fail0; + else + device_printf(sc->sc_dev, "SDIO wireless running in PIO Mode\n"); + + TAILQ_INSERT_TAIL(&sc->sc_maclist, mac, mac_list); + + /* + * calls attach-post routine + */ + + if ((sc->sc_flags & BWN_FLAG_ATTACHED) != 0) + bwn_attach_post(sc); + + return (0); + + fail0: + free(mac, M_DEVBUF); + return (error); +} + static device_method_t brcmwl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, brcmwl_probe),