Index: head/sys/dev/rtwn/if_rtwn.c =================================================================== --- head/sys/dev/rtwn/if_rtwn.c (revision 300753) +++ head/sys/dev/rtwn/if_rtwn.c (revision 300754) @@ -1,3493 +1,3511 @@ /* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2015 Stefan Sperling * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /* * Driver for Realtek RTL8188CE */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RTWN_DEBUG #ifdef RTWN_DEBUG #define DPRINTF(x) do { if (sc->sc_debug > 0) printf x; } while (0) #define DPRINTFN(n, x) do { if (sc->sc_debug >= (n)) printf x; } while (0) #else #define DPRINTF(x) #define DPRINTFN(n, x) #endif /* * PCI configuration space registers. */ #define RTWN_PCI_IOBA 0x10 /* i/o mapped base */ #define RTWN_PCI_MMBA 0x18 /* memory mapped base */ #define RTWN_INT_ENABLE (R92C_IMR_ROK | R92C_IMR_VODOK | R92C_IMR_VIDOK | \ R92C_IMR_BEDOK | R92C_IMR_BKDOK | R92C_IMR_MGNTDOK | \ R92C_IMR_HIGHDOK | R92C_IMR_BDOK | R92C_IMR_RDU | \ R92C_IMR_RXFOVW) struct rtwn_ident { uint16_t vendor; uint16_t device; const char *name; }; static const struct rtwn_ident rtwn_ident_table[] = { { 0x10ec, 0x8176, "Realtek RTL8188CE" }, { 0, 0, NULL } }; static void rtwn_dma_map_addr(void *, bus_dma_segment_t *, int, int); static void rtwn_setup_rx_desc(struct rtwn_softc *, struct r92c_rx_desc *, bus_addr_t, size_t, int); static int rtwn_alloc_rx_list(struct rtwn_softc *); static void rtwn_reset_rx_list(struct rtwn_softc *); static void rtwn_free_rx_list(struct rtwn_softc *); static int rtwn_alloc_tx_list(struct rtwn_softc *, int); static void rtwn_reset_tx_list(struct rtwn_softc *, int); static void rtwn_free_tx_list(struct rtwn_softc *, int); static struct ieee80211vap *rtwn_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 rtwn_vap_delete(struct ieee80211vap *); static void rtwn_write_1(struct rtwn_softc *, uint16_t, uint8_t); static void rtwn_write_2(struct rtwn_softc *, uint16_t, uint16_t); static void rtwn_write_4(struct rtwn_softc *, uint16_t, uint32_t); static uint8_t rtwn_read_1(struct rtwn_softc *, uint16_t); static uint16_t rtwn_read_2(struct rtwn_softc *, uint16_t); static uint32_t rtwn_read_4(struct rtwn_softc *, uint16_t); static int rtwn_fw_cmd(struct rtwn_softc *, uint8_t, const void *, int); static void rtwn_rf_write(struct rtwn_softc *, int, uint8_t, uint32_t); static uint32_t rtwn_rf_read(struct rtwn_softc *, int, uint8_t); static int rtwn_llt_write(struct rtwn_softc *, uint32_t, uint32_t); static uint8_t rtwn_efuse_read_1(struct rtwn_softc *, uint16_t); static void rtwn_efuse_read(struct rtwn_softc *); static int rtwn_read_chipid(struct rtwn_softc *); static void rtwn_read_rom(struct rtwn_softc *); static int rtwn_ra_init(struct rtwn_softc *); static void rtwn_tsf_sync_enable(struct rtwn_softc *); static void rtwn_set_led(struct rtwn_softc *, int, int); static void rtwn_calib_to(void *); static int rtwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); static int rtwn_updateedca(struct ieee80211com *); static void rtwn_update_avgrssi(struct rtwn_softc *, int, int8_t); static int8_t rtwn_get_rssi(struct rtwn_softc *, int, void *); static void rtwn_rx_frame(struct rtwn_softc *, struct r92c_rx_desc *, struct rtwn_rx_data *, int); static int rtwn_tx(struct rtwn_softc *, struct mbuf *, struct ieee80211_node *); static void rtwn_tx_done(struct rtwn_softc *, int); static int rtwn_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static int rtwn_transmit(struct ieee80211com *, struct mbuf *); static void rtwn_parent(struct ieee80211com *); static void rtwn_start(struct rtwn_softc *sc); static void rtwn_watchdog(void *); static int rtwn_power_on(struct rtwn_softc *); static int rtwn_llt_init(struct rtwn_softc *); static void rtwn_fw_reset(struct rtwn_softc *); static void rtwn_fw_loadpage(struct rtwn_softc *, int, const uint8_t *, int); static int rtwn_load_firmware(struct rtwn_softc *); static int rtwn_dma_init(struct rtwn_softc *); static void rtwn_mac_init(struct rtwn_softc *); static void rtwn_bb_init(struct rtwn_softc *); static void rtwn_rf_init(struct rtwn_softc *); static void rtwn_cam_init(struct rtwn_softc *); static void rtwn_pa_bias_init(struct rtwn_softc *); static void rtwn_rxfilter_init(struct rtwn_softc *); static void rtwn_edca_init(struct rtwn_softc *); static void rtwn_write_txpower(struct rtwn_softc *, int, uint16_t[]); static void rtwn_get_txpower(struct rtwn_softc *, int, struct ieee80211_channel *, struct ieee80211_channel *, uint16_t[]); static void rtwn_set_txpower(struct rtwn_softc *, struct ieee80211_channel *, struct ieee80211_channel *); static void rtwn_set_rx_bssid_all(struct rtwn_softc *, int); static void rtwn_set_gain(struct rtwn_softc *, uint8_t); static void rtwn_scan_start(struct ieee80211com *); static void rtwn_scan_end(struct ieee80211com *); +static void rtwn_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); static void rtwn_set_channel(struct ieee80211com *); static void rtwn_update_mcast(struct ieee80211com *); static void rtwn_set_chan(struct rtwn_softc *, struct ieee80211_channel *, struct ieee80211_channel *); static int rtwn_iq_calib_chain(struct rtwn_softc *, int, uint16_t[2], uint16_t[2]); static void rtwn_iq_calib_run(struct rtwn_softc *, int, uint16_t[2][2], uint16_t[2][2]); static int rtwn_iq_calib_compare_results(uint16_t[2][2], uint16_t[2][2], uint16_t[2][2], uint16_t[2][2], int); static void rtwn_iq_calib_write_results(struct rtwn_softc *, uint16_t[2], uint16_t[2], int); static void rtwn_iq_calib(struct rtwn_softc *); static void rtwn_lc_calib(struct rtwn_softc *); static void rtwn_temp_calib(struct rtwn_softc *); static int rtwn_init(struct rtwn_softc *); static void rtwn_stop_locked(struct rtwn_softc *); static void rtwn_stop(struct rtwn_softc *); static void rtwn_intr(void *); /* Aliases. */ #define rtwn_bb_write rtwn_write_4 #define rtwn_bb_read rtwn_read_4 static int rtwn_probe(device_t); static int rtwn_attach(device_t); static int rtwn_detach(device_t); static int rtwn_shutdown(device_t); static int rtwn_suspend(device_t); static int rtwn_resume(device_t); static device_method_t rtwn_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rtwn_probe), DEVMETHOD(device_attach, rtwn_attach), DEVMETHOD(device_detach, rtwn_detach), DEVMETHOD(device_shutdown, rtwn_shutdown), DEVMETHOD(device_suspend, rtwn_suspend), DEVMETHOD(device_resume, rtwn_resume), DEVMETHOD_END }; static driver_t rtwn_driver = { "rtwn", rtwn_methods, sizeof (struct rtwn_softc) }; static devclass_t rtwn_devclass; DRIVER_MODULE(rtwn, pci, rtwn_driver, rtwn_devclass, NULL, NULL); MODULE_VERSION(rtwn, 1); MODULE_DEPEND(rtwn, pci, 1, 1, 1); MODULE_DEPEND(rtwn, wlan, 1, 1, 1); MODULE_DEPEND(rtwn, firmware, 1, 1, 1); +static const uint8_t rtwn_chan_2ghz[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + static int rtwn_probe(device_t dev) { const struct rtwn_ident *ident; for (ident = rtwn_ident_table; ident->name != NULL; ident++) { if (pci_get_vendor(dev) == ident->vendor && pci_get_device(dev) == ident->device) { device_set_desc(dev, ident->name); return (BUS_PROBE_DEFAULT); } } return (ENXIO); } static int rtwn_attach(device_t dev) { struct rtwn_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; uint32_t lcsr; - uint8_t bands[IEEE80211_MODE_BYTES]; int i, count, error, rid; sc->sc_dev = dev; sc->sc_debug = 0; /* * Get the offset of the PCI Express Capability Structure in PCI * Configuration Space. */ error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off); if (error != 0) { device_printf(dev, "PCIe capability structure not found!\n"); return (error); } /* Enable bus-mastering. */ pci_enable_busmaster(dev); rid = PCIR_BAR(2); sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem == NULL) { device_printf(dev, "can't map mem space\n"); return (ENOMEM); } sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); /* Install interrupt handler. */ count = 1; rid = 0; if (pci_alloc_msi(dev, &count) == 0) rid = 1; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | (rid != 0 ? 0 : RF_SHAREABLE)); if (sc->irq == NULL) { device_printf(dev, "can't map interrupt\n"); return (ENXIO); } RTWN_LOCK_INIT(sc); callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); mbufq_init(&sc->sc_snd, ifqmaxlen); error = rtwn_read_chipid(sc); if (error != 0) { device_printf(dev, "unsupported test chip\n"); goto fail; } /* Disable PCIe Active State Power Management (ASPM). */ lcsr = pci_read_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, 4); lcsr &= ~PCIEM_LINK_CTL_ASPMC; pci_write_config(sc->sc_dev, sc->sc_cap_off + PCIER_LINK_CTL, lcsr, 4); /* Allocate Tx/Rx buffers. */ error = rtwn_alloc_rx_list(sc); if (error != 0) { device_printf(dev, "could not allocate Rx buffers\n"); goto fail; } for (i = 0; i < RTWN_NTXQUEUES; i++) { error = rtwn_alloc_tx_list(sc, i); if (error != 0) { device_printf(dev, "could not allocate Tx buffers\n"); goto fail; } } /* Determine number of Tx/Rx chains. */ if (sc->chip & RTWN_CHIP_92C) { sc->ntxchains = (sc->chip & RTWN_CHIP_92C_1T2R) ? 1 : 2; sc->nrxchains = 2; } else { sc->ntxchains = 1; sc->nrxchains = 1; } rtwn_read_rom(sc); device_printf(sc->sc_dev, "MAC/BB RTL%s, RF 6052 %dT%dR\n", (sc->chip & RTWN_CHIP_92C) ? "8192CE" : "8188CE", sc->ntxchains, sc->nrxchains); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(dev); ic->ic_opmode = IEEE80211_M_STA; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_WME /* 802.11e */ ; - memset(bands, 0, sizeof(bands)); - setbit(bands, IEEE80211_MODE_11B); - setbit(bands, IEEE80211_MODE_11G); - ieee80211_init_channels(ic, NULL, bands); + /* XXX TODO: setup regdomain if R92C_CHANNEL_PLAN_BY_HW bit is set. */ + rtwn_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + ieee80211_ifattach(ic); ic->ic_wme.wme_update = rtwn_updateedca; ic->ic_update_mcast = rtwn_update_mcast; - ic->ic_scan_start =rtwn_scan_start; + ic->ic_scan_start = rtwn_scan_start; ic->ic_scan_end = rtwn_scan_end; + ic->ic_getradiocaps = rtwn_getradiocaps; ic->ic_set_channel = rtwn_set_channel; ic->ic_raw_xmit = rtwn_raw_xmit; ic->ic_transmit = rtwn_transmit; ic->ic_parent = rtwn_parent; ic->ic_vap_create = rtwn_vap_create; ic->ic_vap_delete = rtwn_vap_delete; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), RTWN_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), RTWN_RX_RADIOTAP_PRESENT); /* * Hook our interrupt after all initialization is complete. */ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, rtwn_intr, sc, &sc->sc_ih); if (error != 0) { device_printf(dev, "can't establish interrupt, error %d\n", error); goto fail; } if (bootverbose) ieee80211_announce(ic); return (0); fail: rtwn_detach(dev); return (error); } static int rtwn_detach(device_t dev) { struct rtwn_softc *sc = device_get_softc(dev); int i; if (sc->sc_ic.ic_softc != NULL) { rtwn_stop(sc); callout_drain(&sc->calib_to); callout_drain(&sc->watchdog_to); ieee80211_ifdetach(&sc->sc_ic); mbufq_drain(&sc->sc_snd); } /* Uninstall interrupt handler. */ if (sc->irq != NULL) { bus_teardown_intr(dev, sc->irq, sc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), sc->irq); pci_release_msi(dev); } /* Free Tx/Rx buffers. */ for (i = 0; i < RTWN_NTXQUEUES; i++) rtwn_free_tx_list(sc, i); rtwn_free_rx_list(sc); if (sc->mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem), sc->mem); RTWN_LOCK_DESTROY(sc); return (0); } static int rtwn_shutdown(device_t dev) { return (0); } static int rtwn_suspend(device_t dev) { return (0); } static int rtwn_resume(device_t dev) { return (0); } static void rtwn_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); *(bus_addr_t *)arg = segs[0].ds_addr; } static void rtwn_setup_rx_desc(struct rtwn_softc *sc, struct r92c_rx_desc *desc, bus_addr_t addr, size_t len, int idx) { memset(desc, 0, sizeof(*desc)); desc->rxdw0 = htole32(SM(R92C_RXDW0_PKTLEN, len) | ((idx == RTWN_RX_LIST_COUNT - 1) ? R92C_RXDW0_EOR : 0)); desc->rxbufaddr = htole32(addr); bus_space_barrier(sc->sc_st, sc->sc_sh, 0, sc->sc_mapsize, BUS_SPACE_BARRIER_WRITE); desc->rxdw0 |= htole32(R92C_RXDW0_OWN); } static int rtwn_alloc_rx_list(struct rtwn_softc *sc) { struct rtwn_rx_ring *rx_ring = &sc->rx_ring; struct rtwn_rx_data *rx_data; bus_size_t size; int i, error; /* Allocate Rx descriptors. */ size = sizeof(struct r92c_rx_desc) * RTWN_RX_LIST_COUNT; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &rx_ring->desc_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create rx desc DMA tag\n"); goto fail; } error = bus_dmamem_alloc(rx_ring->desc_dmat, (void **)&rx_ring->desc, BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &rx_ring->desc_map); if (error != 0) { device_printf(sc->sc_dev, "could not allocate rx desc\n"); goto fail; } error = bus_dmamap_load(rx_ring->desc_dmat, rx_ring->desc_map, rx_ring->desc, size, rtwn_dma_map_addr, &rx_ring->paddr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not load rx desc DMA map\n"); goto fail; } bus_dmamap_sync(rx_ring->desc_dmat, rx_ring->desc_map, BUS_DMASYNC_PREWRITE); /* Create RX buffer DMA tag. */ error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &rx_ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create rx buf DMA tag\n"); goto fail; } /* Allocate Rx buffers. */ for (i = 0; i < RTWN_RX_LIST_COUNT; i++) { rx_data = &rx_ring->rx_data[i]; error = bus_dmamap_create(rx_ring->data_dmat, 0, &rx_data->map); if (error != 0) { device_printf(sc->sc_dev, "could not create rx buf DMA map\n"); goto fail; } rx_data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (rx_data->m == NULL) { device_printf(sc->sc_dev, "could not allocate rx mbuf\n"); error = ENOMEM; goto fail; } error = bus_dmamap_load(rx_ring->data_dmat, rx_data->map, mtod(rx_data->m, void *), MCLBYTES, rtwn_dma_map_addr, &rx_data->paddr, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not load rx buf DMA map"); goto fail; } rtwn_setup_rx_desc(sc, &rx_ring->desc[i], rx_data->paddr, MCLBYTES, i); } return (0); fail: rtwn_free_rx_list(sc); return (error); } static void rtwn_reset_rx_list(struct rtwn_softc *sc) { struct rtwn_rx_ring *rx_ring = &sc->rx_ring; struct rtwn_rx_data *rx_data; int i; for (i = 0; i < RTWN_RX_LIST_COUNT; i++) { rx_data = &rx_ring->rx_data[i]; rtwn_setup_rx_desc(sc, &rx_ring->desc[i], rx_data->paddr, MCLBYTES, i); } } static void rtwn_free_rx_list(struct rtwn_softc *sc) { struct rtwn_rx_ring *rx_ring = &sc->rx_ring; struct rtwn_rx_data *rx_data; int i; if (rx_ring->desc_dmat != NULL) { if (rx_ring->desc != NULL) { bus_dmamap_unload(rx_ring->desc_dmat, rx_ring->desc_map); bus_dmamem_free(rx_ring->desc_dmat, rx_ring->desc, rx_ring->desc_map); rx_ring->desc = NULL; } bus_dma_tag_destroy(rx_ring->desc_dmat); rx_ring->desc_dmat = NULL; } for (i = 0; i < RTWN_RX_LIST_COUNT; i++) { rx_data = &rx_ring->rx_data[i]; if (rx_data->m != NULL) { bus_dmamap_unload(rx_ring->data_dmat, rx_data->map); m_freem(rx_data->m); rx_data->m = NULL; } bus_dmamap_destroy(rx_ring->data_dmat, rx_data->map); rx_data->map = NULL; } if (rx_ring->data_dmat != NULL) { bus_dma_tag_destroy(rx_ring->data_dmat); rx_ring->data_dmat = NULL; } } static int rtwn_alloc_tx_list(struct rtwn_softc *sc, int qid) { struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid]; struct rtwn_tx_data *tx_data; bus_size_t size; int i, error; size = sizeof(struct r92c_tx_desc) * RTWN_TX_LIST_COUNT; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), PAGE_SIZE, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &tx_ring->desc_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create tx ring DMA tag\n"); goto fail; } error = bus_dmamem_alloc(tx_ring->desc_dmat, (void **)&tx_ring->desc, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &tx_ring->desc_map); if (error != 0) { device_printf(sc->sc_dev, "can't map tx ring DMA memory\n"); goto fail; } error = bus_dmamap_load(tx_ring->desc_dmat, tx_ring->desc_map, tx_ring->desc, size, rtwn_dma_map_addr, &tx_ring->paddr, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "could not load desc DMA map\n"); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &tx_ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create tx buf DMA tag\n"); goto fail; } for (i = 0; i < RTWN_TX_LIST_COUNT; i++) { struct r92c_tx_desc *desc = &tx_ring->desc[i]; /* setup tx desc */ desc->nextdescaddr = htole32(tx_ring->paddr + + sizeof(struct r92c_tx_desc) * ((i + 1) % RTWN_TX_LIST_COUNT)); tx_data = &tx_ring->tx_data[i]; error = bus_dmamap_create(tx_ring->data_dmat, 0, &tx_data->map); if (error != 0) { device_printf(sc->sc_dev, "could not create tx buf DMA map\n"); goto fail; } tx_data->m = NULL; tx_data->ni = NULL; } return (0); fail: rtwn_free_tx_list(sc, qid); return (error); } static void rtwn_reset_tx_list(struct rtwn_softc *sc, int qid) { struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid]; int i; for (i = 0; i < RTWN_TX_LIST_COUNT; i++) { struct r92c_tx_desc *desc = &tx_ring->desc[i]; struct rtwn_tx_data *tx_data = &tx_ring->tx_data[i]; memset(desc, 0, sizeof(*desc) - (sizeof(desc->reserved) + sizeof(desc->nextdescaddr64) + sizeof(desc->nextdescaddr))); if (tx_data->m != NULL) { bus_dmamap_unload(tx_ring->data_dmat, tx_data->map); m_freem(tx_data->m); tx_data->m = NULL; } if (tx_data->ni != NULL) { ieee80211_free_node(tx_data->ni); tx_data->ni = NULL; } } bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, BUS_DMASYNC_POSTWRITE); sc->qfullmsk &= ~(1 << qid); tx_ring->queued = 0; tx_ring->cur = 0; } static void rtwn_free_tx_list(struct rtwn_softc *sc, int qid) { struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid]; struct rtwn_tx_data *tx_data; int i; if (tx_ring->desc_dmat != NULL) { if (tx_ring->desc != NULL) { bus_dmamap_unload(tx_ring->desc_dmat, tx_ring->desc_map); bus_dmamem_free(tx_ring->desc_dmat, tx_ring->desc, tx_ring->desc_map); } bus_dma_tag_destroy(tx_ring->desc_dmat); } for (i = 0; i < RTWN_TX_LIST_COUNT; i++) { tx_data = &tx_ring->tx_data[i]; if (tx_data->m != NULL) { bus_dmamap_unload(tx_ring->data_dmat, tx_data->map); m_freem(tx_data->m); tx_data->m = NULL; } } if (tx_ring->data_dmat != NULL) { bus_dma_tag_destroy(tx_ring->data_dmat); tx_ring->data_dmat = NULL; } sc->qfullmsk &= ~(1 << qid); tx_ring->queued = 0; tx_ring->cur = 0; } static struct ieee80211vap * rtwn_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 rtwn_vap *rvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) return (NULL); rvp = malloc(sizeof(struct rtwn_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &rvp->vap; if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { /* out of memory */ free(rvp, M_80211_VAP); return (NULL); } /* Override state transition machine. */ rvp->newstate = vap->iv_newstate; vap->iv_newstate = rtwn_newstate; /* Complete setup. */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return (vap); } static void rtwn_vap_delete(struct ieee80211vap *vap) { struct rtwn_vap *rvp = RTWN_VAP(vap); ieee80211_vap_detach(vap); free(rvp, M_80211_VAP); } static void rtwn_write_1(struct rtwn_softc *sc, uint16_t addr, uint8_t val) { bus_space_write_1(sc->sc_st, sc->sc_sh, addr, val); } static void rtwn_write_2(struct rtwn_softc *sc, uint16_t addr, uint16_t val) { val = htole16(val); bus_space_write_2(sc->sc_st, sc->sc_sh, addr, val); } static void rtwn_write_4(struct rtwn_softc *sc, uint16_t addr, uint32_t val) { val = htole32(val); bus_space_write_4(sc->sc_st, sc->sc_sh, addr, val); } static uint8_t rtwn_read_1(struct rtwn_softc *sc, uint16_t addr) { return (bus_space_read_1(sc->sc_st, sc->sc_sh, addr)); } static uint16_t rtwn_read_2(struct rtwn_softc *sc, uint16_t addr) { return (bus_space_read_2(sc->sc_st, sc->sc_sh, addr)); } static uint32_t rtwn_read_4(struct rtwn_softc *sc, uint16_t addr) { return (bus_space_read_4(sc->sc_st, sc->sc_sh, addr)); } static int rtwn_fw_cmd(struct rtwn_softc *sc, uint8_t id, const void *buf, int len) { struct r92c_fw_cmd cmd; int ntries; /* Wait for current FW box to be empty. */ for (ntries = 0; ntries < 100; ntries++) { if (!(rtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur))) break; DELAY(1); } if (ntries == 100) { device_printf(sc->sc_dev, "could not send firmware command %d\n", id); return (ETIMEDOUT); } memset(&cmd, 0, sizeof(cmd)); cmd.id = id; if (len > 3) cmd.id |= R92C_CMD_FLAG_EXT; KASSERT(len <= sizeof(cmd.msg), ("rtwn_fw_cmd\n")); memcpy(cmd.msg, buf, len); /* Write the first word last since that will trigger the FW. */ rtwn_write_2(sc, R92C_HMEBOX_EXT(sc->fwcur), *((uint8_t *)&cmd + 4)); rtwn_write_4(sc, R92C_HMEBOX(sc->fwcur), *((uint8_t *)&cmd + 0)); sc->fwcur = (sc->fwcur + 1) % R92C_H2C_NBOX; /* Give firmware some time for processing. */ DELAY(2000); return (0); } static void rtwn_rf_write(struct rtwn_softc *sc, int chain, uint8_t addr, uint32_t val) { rtwn_bb_write(sc, R92C_LSSI_PARAM(chain), SM(R92C_LSSI_PARAM_ADDR, addr) | SM(R92C_LSSI_PARAM_DATA, val)); } static uint32_t rtwn_rf_read(struct rtwn_softc *sc, int chain, uint8_t addr) { uint32_t reg[R92C_MAX_CHAINS], val; reg[0] = rtwn_bb_read(sc, R92C_HSSI_PARAM2(0)); if (chain != 0) reg[chain] = rtwn_bb_read(sc, R92C_HSSI_PARAM2(chain)); rtwn_bb_write(sc, R92C_HSSI_PARAM2(0), reg[0] & ~R92C_HSSI_PARAM2_READ_EDGE); DELAY(1000); rtwn_bb_write(sc, R92C_HSSI_PARAM2(chain), RW(reg[chain], R92C_HSSI_PARAM2_READ_ADDR, addr) | R92C_HSSI_PARAM2_READ_EDGE); DELAY(1000); rtwn_bb_write(sc, R92C_HSSI_PARAM2(0), reg[0] | R92C_HSSI_PARAM2_READ_EDGE); DELAY(1000); if (rtwn_bb_read(sc, R92C_HSSI_PARAM1(chain)) & R92C_HSSI_PARAM1_PI) val = rtwn_bb_read(sc, R92C_HSPI_READBACK(chain)); else val = rtwn_bb_read(sc, R92C_LSSI_READBACK(chain)); return (MS(val, R92C_LSSI_READBACK_DATA)); } static int rtwn_llt_write(struct rtwn_softc *sc, uint32_t addr, uint32_t data) { int ntries; rtwn_write_4(sc, R92C_LLT_INIT, SM(R92C_LLT_INIT_OP, R92C_LLT_INIT_OP_WRITE) | SM(R92C_LLT_INIT_ADDR, addr) | SM(R92C_LLT_INIT_DATA, data)); /* Wait for write operation to complete. */ for (ntries = 0; ntries < 20; ntries++) { if (MS(rtwn_read_4(sc, R92C_LLT_INIT), R92C_LLT_INIT_OP) == R92C_LLT_INIT_OP_NO_ACTIVE) return (0); DELAY(5); } return (ETIMEDOUT); } static uint8_t rtwn_efuse_read_1(struct rtwn_softc *sc, uint16_t addr) { uint32_t reg; int ntries; reg = rtwn_read_4(sc, R92C_EFUSE_CTRL); reg = RW(reg, R92C_EFUSE_CTRL_ADDR, addr); reg &= ~R92C_EFUSE_CTRL_VALID; rtwn_write_4(sc, R92C_EFUSE_CTRL, reg); /* Wait for read operation to complete. */ for (ntries = 0; ntries < 100; ntries++) { reg = rtwn_read_4(sc, R92C_EFUSE_CTRL); if (reg & R92C_EFUSE_CTRL_VALID) return (MS(reg, R92C_EFUSE_CTRL_DATA)); DELAY(5); } device_printf(sc->sc_dev, "could not read efuse byte at address 0x%x\n", addr); return (0xff); } static void rtwn_efuse_read(struct rtwn_softc *sc) { uint8_t *rom = (uint8_t *)&sc->rom; uint16_t addr = 0; uint32_t reg; uint8_t off, msk; int i; reg = rtwn_read_2(sc, R92C_SYS_ISO_CTRL); if (!(reg & R92C_SYS_ISO_CTRL_PWC_EV12V)) { rtwn_write_2(sc, R92C_SYS_ISO_CTRL, reg | R92C_SYS_ISO_CTRL_PWC_EV12V); } reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN); if (!(reg & R92C_SYS_FUNC_EN_ELDR)) { rtwn_write_2(sc, R92C_SYS_FUNC_EN, reg | R92C_SYS_FUNC_EN_ELDR); } reg = rtwn_read_2(sc, R92C_SYS_CLKR); if ((reg & (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) != (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) { rtwn_write_2(sc, R92C_SYS_CLKR, reg | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M); } memset(&sc->rom, 0xff, sizeof(sc->rom)); while (addr < 512) { reg = rtwn_efuse_read_1(sc, addr); if (reg == 0xff) break; addr++; off = reg >> 4; msk = reg & 0xf; for (i = 0; i < 4; i++) { if (msk & (1 << i)) continue; rom[off * 8 + i * 2 + 0] = rtwn_efuse_read_1(sc, addr); addr++; rom[off * 8 + i * 2 + 1] = rtwn_efuse_read_1(sc, addr); addr++; } } #ifdef RTWN_DEBUG if (sc->sc_debug >= 2) { /* Dump ROM content. */ printf("\n"); for (i = 0; i < sizeof(sc->rom); i++) printf("%02x:", rom[i]); printf("\n"); } #endif } static int rtwn_read_chipid(struct rtwn_softc *sc) { uint32_t reg; reg = rtwn_read_4(sc, R92C_SYS_CFG); if (reg & R92C_SYS_CFG_TRP_VAUX_EN) /* Unsupported test chip. */ return (EIO); if (reg & R92C_SYS_CFG_TYPE_92C) { sc->chip |= RTWN_CHIP_92C; /* Check if it is a castrated 8192C. */ if (MS(rtwn_read_4(sc, R92C_HPON_FSM), R92C_HPON_FSM_CHIP_BONDING_ID) == R92C_HPON_FSM_CHIP_BONDING_ID_92C_1T2R) sc->chip |= RTWN_CHIP_92C_1T2R; } if (reg & R92C_SYS_CFG_VENDOR_UMC) { sc->chip |= RTWN_CHIP_UMC; if (MS(reg, R92C_SYS_CFG_CHIP_VER_RTL) == 0) sc->chip |= RTWN_CHIP_UMC_A_CUT; } return (0); } static void rtwn_read_rom(struct rtwn_softc *sc) { struct r92c_rom *rom = &sc->rom; /* Read full ROM image. */ rtwn_efuse_read(sc); if (rom->id != 0x8129) device_printf(sc->sc_dev, "invalid EEPROM ID 0x%x\n", rom->id); /* XXX Weird but this is what the vendor driver does. */ sc->pa_setting = rtwn_efuse_read_1(sc, 0x1fa); DPRINTF(("PA setting=0x%x\n", sc->pa_setting)); sc->board_type = MS(rom->rf_opt1, R92C_ROM_RF1_BOARD_TYPE); sc->regulatory = MS(rom->rf_opt1, R92C_ROM_RF1_REGULATORY); DPRINTF(("regulatory type=%d\n", sc->regulatory)); IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, rom->macaddr); } static __inline uint8_t rate2ridx(uint8_t rate) { switch (rate) { case 12: return 4; case 18: return 5; case 24: return 6; case 36: return 7; case 48: return 8; case 72: return 9; case 96: return 10; case 108: return 11; case 2: return 0; case 4: return 1; case 11: return 2; case 22: return 3; default: return RTWN_RIDX_UNKNOWN; } } /* * Initialize rate adaptation in firmware. */ static int rtwn_ra_init(struct rtwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni = ieee80211_ref_node(vap->iv_bss); struct ieee80211_rateset *rs = &ni->ni_rates; struct r92c_fw_cmd_macid_cfg cmd; uint32_t rates, basicrates; uint8_t maxrate, maxbasicrate, mode, ridx; int error, i; /* Get normal and basic rates mask. */ rates = basicrates = 0; maxrate = maxbasicrate = 0; for (i = 0; i < rs->rs_nrates; i++) { /* Convert 802.11 rate to HW rate index. */ ridx = rate2ridx(IEEE80211_RV(rs->rs_rates[i])); if (ridx == RTWN_RIDX_UNKNOWN) /* Unknown rate, skip. */ continue; rates |= 1 << ridx; if (ridx > maxrate) maxrate = ridx; if (rs->rs_rates[i] & IEEE80211_RATE_BASIC) { basicrates |= 1 << ridx; if (ridx > maxbasicrate) maxbasicrate = ridx; } } if (ic->ic_curmode == IEEE80211_MODE_11B) mode = R92C_RAID_11B; else mode = R92C_RAID_11BG; DPRINTF(("mode=0x%x rates=0x%08x, basicrates=0x%08x\n", mode, rates, basicrates)); /* Set rates mask for group addressed frames. */ cmd.macid = RTWN_MACID_BC | RTWN_MACID_VALID; cmd.mask = htole32(mode << 28 | basicrates); error = rtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); if (error != 0) { device_printf(sc->sc_dev, "could not add broadcast station\n"); return (error); } /* Set initial MRR rate. */ DPRINTF(("maxbasicrate=%d\n", maxbasicrate)); rtwn_write_1(sc, R92C_INIDATA_RATE_SEL(RTWN_MACID_BC), maxbasicrate); /* Set rates mask for unicast frames. */ cmd.macid = RTWN_MACID_BSS | RTWN_MACID_VALID; cmd.mask = htole32(mode << 28 | rates); error = rtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); if (error != 0) { device_printf(sc->sc_dev, "could not add BSS station\n"); return (error); } /* Set initial MRR rate. */ DPRINTF(("maxrate=%d\n", maxrate)); rtwn_write_1(sc, R92C_INIDATA_RATE_SEL(RTWN_MACID_BSS), maxrate); /* Configure Automatic Rate Fallback Register. */ if (ic->ic_curmode == IEEE80211_MODE_11B) { if (rates & 0x0c) rtwn_write_4(sc, R92C_ARFR(0), htole32(rates & 0x0d)); else rtwn_write_4(sc, R92C_ARFR(0), htole32(rates & 0x0f)); } else rtwn_write_4(sc, R92C_ARFR(0), htole32(rates & 0x0ff5)); /* Indicate highest supported rate. */ ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1]; return (0); } static void rtwn_tsf_sync_enable(struct rtwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni = vap->iv_bss; uint64_t tsf; /* Enable TSF synchronization. */ rtwn_write_1(sc, R92C_BCN_CTRL, rtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_DIS_TSF_UDT0); rtwn_write_1(sc, R92C_BCN_CTRL, rtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_EN_BCN); /* Set initial TSF. */ memcpy(&tsf, ni->ni_tstamp.data, 8); tsf = le64toh(tsf); tsf = tsf - (tsf % (vap->iv_bss->ni_intval * IEEE80211_DUR_TU)); tsf -= IEEE80211_DUR_TU; rtwn_write_4(sc, R92C_TSFTR + 0, tsf); rtwn_write_4(sc, R92C_TSFTR + 4, tsf >> 32); rtwn_write_1(sc, R92C_BCN_CTRL, rtwn_read_1(sc, R92C_BCN_CTRL) | R92C_BCN_CTRL_EN_BCN); } static void rtwn_set_led(struct rtwn_softc *sc, int led, int on) { uint8_t reg; if (led == RTWN_LED_LINK) { reg = rtwn_read_1(sc, R92C_LEDCFG2) & 0xf0; if (!on) reg |= R92C_LEDCFG2_DIS; else reg |= R92C_LEDCFG2_EN; rtwn_write_1(sc, R92C_LEDCFG2, reg); sc->ledlink = on; /* Save LED state. */ } } static void rtwn_calib_to(void *arg) { struct rtwn_softc *sc = arg; struct r92c_fw_cmd_rssi cmd; if (sc->avg_pwdb != -1) { /* Indicate Rx signal strength to FW for rate adaptation. */ memset(&cmd, 0, sizeof(cmd)); cmd.macid = 0; /* BSS. */ cmd.pwdb = sc->avg_pwdb; DPRINTFN(3, ("sending RSSI command avg=%d\n", sc->avg_pwdb)); rtwn_fw_cmd(sc, R92C_CMD_RSSI_SETTING, &cmd, sizeof(cmd)); } /* Do temperature compensation. */ rtwn_temp_calib(sc); callout_reset(&sc->calib_to, hz * 2, rtwn_calib_to, sc); } static int rtwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct rtwn_vap *rvp = RTWN_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni = vap->iv_bss; struct rtwn_softc *sc = ic->ic_softc; uint32_t reg; IEEE80211_UNLOCK(ic); RTWN_LOCK(sc); if (vap->iv_state == IEEE80211_S_RUN) { /* Stop calibration. */ callout_stop(&sc->calib_to); /* Turn link LED off. */ rtwn_set_led(sc, RTWN_LED_LINK, 0); /* Set media status to 'No Link'. */ reg = rtwn_read_4(sc, R92C_CR); reg = RW(reg, R92C_CR_NETTYPE, R92C_CR_NETTYPE_NOLINK); rtwn_write_4(sc, R92C_CR, reg); /* Stop Rx of data frames. */ rtwn_write_2(sc, R92C_RXFLTMAP2, 0); /* Rest TSF. */ rtwn_write_1(sc, R92C_DUAL_TSF_RST, 0x03); /* Disable TSF synchronization. */ rtwn_write_1(sc, R92C_BCN_CTRL, rtwn_read_1(sc, R92C_BCN_CTRL) | R92C_BCN_CTRL_DIS_TSF_UDT0); /* Reset EDCA parameters. */ rtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3217); rtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4317); rtwn_write_4(sc, R92C_EDCA_BE_PARAM, 0x00105320); rtwn_write_4(sc, R92C_EDCA_BK_PARAM, 0x0000a444); } switch (nstate) { case IEEE80211_S_INIT: /* Turn link LED off. */ rtwn_set_led(sc, RTWN_LED_LINK, 0); break; case IEEE80211_S_SCAN: /* Make link LED blink during scan. */ rtwn_set_led(sc, RTWN_LED_LINK, !sc->ledlink); /* Pause AC Tx queues. */ rtwn_write_1(sc, R92C_TXPAUSE, rtwn_read_1(sc, R92C_TXPAUSE) | 0x0f); break; case IEEE80211_S_AUTH: rtwn_set_chan(sc, ic->ic_curchan, NULL); break; case IEEE80211_S_RUN: if (ic->ic_opmode == IEEE80211_M_MONITOR) { /* Enable Rx of data frames. */ rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); /* Turn link LED on. */ rtwn_set_led(sc, RTWN_LED_LINK, 1); break; } /* Set media status to 'Associated'. */ reg = rtwn_read_4(sc, R92C_CR); reg = RW(reg, R92C_CR_NETTYPE, R92C_CR_NETTYPE_INFRA); rtwn_write_4(sc, R92C_CR, reg); /* Set BSSID. */ rtwn_write_4(sc, R92C_BSSID + 0, le32dec(&ni->ni_bssid[0])); rtwn_write_4(sc, R92C_BSSID + 4, le16dec(&ni->ni_bssid[4])); if (ic->ic_curmode == IEEE80211_MODE_11B) rtwn_write_1(sc, R92C_INIRTS_RATE_SEL, 0); else /* 802.11b/g */ rtwn_write_1(sc, R92C_INIRTS_RATE_SEL, 3); /* Enable Rx of data frames. */ rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); /* Flush all AC queues. */ rtwn_write_1(sc, R92C_TXPAUSE, 0); /* Set beacon interval. */ rtwn_write_2(sc, R92C_BCN_INTERVAL, ni->ni_intval); /* Allow Rx from our BSSID only. */ rtwn_write_4(sc, R92C_RCR, rtwn_read_4(sc, R92C_RCR) | R92C_RCR_CBSSID_DATA | R92C_RCR_CBSSID_BCN); /* Enable TSF synchronization. */ rtwn_tsf_sync_enable(sc); rtwn_write_1(sc, R92C_SIFS_CCK + 1, 10); rtwn_write_1(sc, R92C_SIFS_OFDM + 1, 10); rtwn_write_1(sc, R92C_SPEC_SIFS + 1, 10); rtwn_write_1(sc, R92C_MAC_SPEC_SIFS + 1, 10); rtwn_write_1(sc, R92C_R2T_SIFS + 1, 10); rtwn_write_1(sc, R92C_T2T_SIFS + 1, 10); /* Intialize rate adaptation. */ rtwn_ra_init(sc); /* Turn link LED on. */ rtwn_set_led(sc, RTWN_LED_LINK, 1); sc->avg_pwdb = -1; /* Reset average RSSI. */ /* Reset temperature calibration state machine. */ sc->thcal_state = 0; sc->thcal_lctemp = 0; /* Start periodic calibration. */ callout_reset(&sc->calib_to, hz * 2, rtwn_calib_to, sc); break; default: break; } RTWN_UNLOCK(sc); IEEE80211_LOCK(ic); return (rvp->newstate(vap, nstate, arg)); } static int rtwn_updateedca(struct ieee80211com *ic) { struct rtwn_softc *sc = ic->ic_softc; const uint16_t aci2reg[WME_NUM_AC] = { R92C_EDCA_BE_PARAM, R92C_EDCA_BK_PARAM, R92C_EDCA_VI_PARAM, R92C_EDCA_VO_PARAM }; int aci, aifs, slottime; IEEE80211_LOCK(ic); slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; for (aci = 0; aci < WME_NUM_AC; aci++) { const struct wmeParams *ac = &ic->ic_wme.wme_chanParams.cap_wmeParams[aci]; /* AIFS[AC] = AIFSN[AC] * aSlotTime + aSIFSTime. */ aifs = ac->wmep_aifsn * slottime + 10; rtwn_write_4(sc, aci2reg[aci], SM(R92C_EDCA_PARAM_TXOP, ac->wmep_txopLimit) | SM(R92C_EDCA_PARAM_ECWMIN, ac->wmep_logcwmin) | SM(R92C_EDCA_PARAM_ECWMAX, ac->wmep_logcwmax) | SM(R92C_EDCA_PARAM_AIFS, aifs)); } IEEE80211_UNLOCK(ic); return (0); } static void rtwn_update_avgrssi(struct rtwn_softc *sc, int rate, int8_t rssi) { int pwdb; /* Convert antenna signal to percentage. */ if (rssi <= -100 || rssi >= 20) pwdb = 0; else if (rssi >= 0) pwdb = 100; else pwdb = 100 + rssi; if (RTWN_RATE_IS_CCK(rate)) { /* CCK gain is smaller than OFDM/MCS gain. */ pwdb += 6; if (pwdb > 100) pwdb = 100; if (pwdb <= 14) pwdb -= 4; else if (pwdb <= 26) pwdb -= 8; else if (pwdb <= 34) pwdb -= 6; else if (pwdb <= 42) pwdb -= 2; } if (sc->avg_pwdb == -1) /* Init. */ sc->avg_pwdb = pwdb; else if (sc->avg_pwdb < pwdb) sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20) + 1; else sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20); DPRINTFN(4, ("PWDB=%d EMA=%d\n", pwdb, sc->avg_pwdb)); } static int8_t rtwn_get_rssi(struct rtwn_softc *sc, int rate, void *physt) { static const int8_t cckoff[] = { 16, -12, -26, -46 }; struct r92c_rx_phystat *phy; struct r92c_rx_cck *cck; uint8_t rpt; int8_t rssi; if (RTWN_RATE_IS_CCK(rate)) { cck = (struct r92c_rx_cck *)physt; if (sc->sc_flags & RTWN_FLAG_CCK_HIPWR) { rpt = (cck->agc_rpt >> 5) & 0x3; rssi = (cck->agc_rpt & 0x1f) << 1; } else { rpt = (cck->agc_rpt >> 6) & 0x3; rssi = cck->agc_rpt & 0x3e; } rssi = cckoff[rpt] - rssi; } else { /* OFDM/HT. */ phy = (struct r92c_rx_phystat *)physt; rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 110; } return (rssi); } static void rtwn_rx_frame(struct rtwn_softc *sc, struct r92c_rx_desc *rx_desc, struct rtwn_rx_data *rx_data, int desc_idx) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame_min *wh; struct ieee80211_node *ni; struct r92c_rx_phystat *phy = NULL; uint32_t rxdw0, rxdw3; struct mbuf *m, *m1; bus_dma_segment_t segs[1]; bus_addr_t physaddr; uint8_t rate; int8_t rssi = 0, nf; int infosz, nsegs, pktlen, shift, error; rxdw0 = le32toh(rx_desc->rxdw0); rxdw3 = le32toh(rx_desc->rxdw3); if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) { /* * This should not happen since we setup our Rx filter * to not receive these frames. */ counter_u64_add(ic->ic_ierrors, 1); return; } pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); if (__predict_false(pktlen < sizeof(struct ieee80211_frame_ack) || pktlen > MCLBYTES)) { counter_u64_add(ic->ic_ierrors, 1); return; } rate = MS(rxdw3, R92C_RXDW3_RATE); infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; if (infosz > sizeof(struct r92c_rx_phystat)) infosz = sizeof(struct r92c_rx_phystat); shift = MS(rxdw0, R92C_RXDW0_SHIFT); /* Get RSSI from PHY status descriptor if present. */ if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) { phy = mtod(rx_data->m, struct r92c_rx_phystat *); rssi = rtwn_get_rssi(sc, rate, phy); /* Update our average RSSI. */ rtwn_update_avgrssi(sc, rate, rssi); } DPRINTFN(5, ("Rx frame len=%d rate=%d infosz=%d shift=%d rssi=%d\n", pktlen, rate, infosz, shift, rssi)); m1 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m1 == NULL) { counter_u64_add(ic->ic_ierrors, 1); return; } bus_dmamap_unload(sc->rx_ring.data_dmat, rx_data->map); error = bus_dmamap_load(sc->rx_ring.data_dmat, rx_data->map, mtod(m1, void *), MCLBYTES, rtwn_dma_map_addr, &physaddr, 0); if (error != 0) { m_freem(m1); if (bus_dmamap_load_mbuf_sg(sc->rx_ring.data_dmat, rx_data->map, rx_data->m, segs, &nsegs, 0)) panic("%s: could not load old RX mbuf", device_get_name(sc->sc_dev)); /* Physical address may have changed. */ rtwn_setup_rx_desc(sc, rx_desc, physaddr, MCLBYTES, desc_idx); counter_u64_add(ic->ic_ierrors, 1); return; } /* Finalize mbuf. */ m = rx_data->m; rx_data->m = m1; m->m_pkthdr.len = m->m_len = pktlen + infosz + shift; /* Update RX descriptor. */ rtwn_setup_rx_desc(sc, rx_desc, physaddr, MCLBYTES, desc_idx); /* Get ieee80211 frame header. */ if (rxdw0 & R92C_RXDW0_PHYST) m_adj(m, infosz + shift); else m_adj(m, shift); nf = -95; if (ieee80211_radiotap_active(ic)) { struct rtwn_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; if (!(rxdw3 & R92C_RXDW3_HT)) { tap->wr_rate = ridx2rate[rate]; } else if (rate >= 12) { /* MCS0~15. */ /* Bit 7 set means HT MCS instead of rate. */ tap->wr_rate = 0x80 | (rate - 12); } tap->wr_dbm_antsignal = rssi; tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); } RTWN_UNLOCK(sc); wh = mtod(m, struct ieee80211_frame_min *); if (m->m_len >= sizeof(*wh)) ni = ieee80211_find_rxnode(ic, wh); else ni = NULL; /* Send the frame to the 802.11 layer. */ if (ni != NULL) { (void)ieee80211_input(ni, m, rssi - nf, nf); /* Node is no longer needed. */ ieee80211_free_node(ni); } else (void)ieee80211_input_all(ic, m, rssi - nf, nf); RTWN_LOCK(sc); } static int rtwn_tx(struct rtwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; struct rtwn_tx_ring *tx_ring; struct rtwn_tx_data *data; struct r92c_tx_desc *txd; bus_dma_segment_t segs[1]; uint16_t qos; uint8_t raid, type, tid, qid; int nsegs, error; wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; /* Encrypt the frame if need be. */ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); return (ENOBUFS); } /* 802.11 header may have moved. */ wh = mtod(m, struct ieee80211_frame *); } if (IEEE80211_QOS_HAS_SEQ(wh)) { qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; tid = qos & IEEE80211_QOS_TID; } else { qos = 0; tid = 0; } switch (type) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: qid = RTWN_VO_QUEUE; break; default: qid = M_WME_GETAC(m); break; } /* Grab a Tx buffer from the ring. */ tx_ring = &sc->tx_ring[qid]; data = &tx_ring->tx_data[tx_ring->cur]; if (data->m != NULL) { m_freem(m); return (ENOBUFS); } /* Fill Tx descriptor. */ txd = &tx_ring->desc[tx_ring->cur]; if (htole32(txd->txdw0) & R92C_RXDW0_OWN) { m_freem(m); return (ENOBUFS); } txd->txdw0 = htole32( SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len) | SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | R92C_TXDW0_FSG | R92C_TXDW0_LSG); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txd->txdw0 |= htole32(R92C_TXDW0_BMCAST); txd->txdw1 = 0; txd->txdw4 = 0; txd->txdw5 = 0; /* XXX TODO: rate control; implement low-rate for EAPOL */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && type == IEEE80211_FC0_TYPE_DATA) { if (ic->ic_curmode == IEEE80211_MODE_11B) raid = R92C_RAID_11B; else raid = R92C_RAID_11BG; txd->txdw1 |= htole32( SM(R92C_TXDW1_MACID, RTWN_MACID_BSS) | SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) | SM(R92C_TXDW1_RAID, raid) | R92C_TXDW1_AGGBK); if (ic->ic_flags & IEEE80211_F_USEPROT) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) { txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF | R92C_TXDW4_HWRTSEN); } else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) { txd->txdw4 |= htole32(R92C_TXDW4_RTSEN | R92C_TXDW4_HWRTSEN); } } /* XXX TODO: implement rate control */ /* Send RTS at OFDM24. */ txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, RTWN_RIDX_OFDM24)); txd->txdw5 |= htole32(SM(R92C_TXDW5_RTSRATE_FBLIMIT, 0xf)); /* Send data at OFDM54. */ txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, RTWN_RIDX_OFDM54)); txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE_FBLIMIT, 0x1f)); } else { txd->txdw1 |= htole32( SM(R92C_TXDW1_MACID, 0) | SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT) | SM(R92C_TXDW1_RAID, R92C_RAID_11B)); /* Force CCK1. */ txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE); txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, RTWN_RIDX_CCK1)); } /* Set sequence number (already little endian). */ txd->txdseq = htole16(M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE); if (!qos) { /* Use HW sequence numbering for non-QoS frames. */ txd->txdw4 |= htole32(R92C_TXDW4_HWSEQ); txd->txdseq |= htole16(0x8000); } else txd->txdw4 |= htole32(R92C_TXDW4_QOS); error = bus_dmamap_load_mbuf_sg(tx_ring->data_dmat, data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", error); m_freem(m); return (error); } if (error != 0) { struct mbuf *mnew; mnew = m_defrag(m, M_NOWAIT); if (mnew == NULL) { device_printf(sc->sc_dev, "can't defragment mbuf\n"); m_freem(m); return (ENOBUFS); } m = mnew; error = bus_dmamap_load_mbuf_sg(tx_ring->data_dmat, data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", error); m_freem(m); return (error); } } txd->txbufaddr = htole32(segs[0].ds_addr); txd->txbufsize = htole16(m->m_pkthdr.len); bus_space_barrier(sc->sc_st, sc->sc_sh, 0, sc->sc_mapsize, BUS_SPACE_BARRIER_WRITE); txd->txdw0 |= htole32(R92C_TXDW0_OWN); bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_sync(tx_ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); data->m = m; data->ni = ni; if (ieee80211_radiotap_active_vap(vap)) { struct rtwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); ieee80211_radiotap_tx(vap, m); } tx_ring->cur = (tx_ring->cur + 1) % RTWN_TX_LIST_COUNT; tx_ring->queued++; if (tx_ring->queued >= (RTWN_TX_LIST_COUNT - 1)) sc->qfullmsk |= (1 << qid); /* Kick TX. */ rtwn_write_2(sc, R92C_PCIE_CTRL_REG, (1 << qid)); return (0); } static void rtwn_tx_done(struct rtwn_softc *sc, int qid) { struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid]; struct rtwn_tx_data *tx_data; struct r92c_tx_desc *tx_desc; int i; bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map, BUS_DMASYNC_POSTREAD); for (i = 0; i < RTWN_TX_LIST_COUNT; i++) { tx_data = &tx_ring->tx_data[i]; if (tx_data->m == NULL) continue; tx_desc = &tx_ring->desc[i]; if (le32toh(tx_desc->txdw0) & R92C_TXDW0_OWN) continue; bus_dmamap_unload(tx_ring->desc_dmat, tx_ring->desc_map); /* * XXX TODO: figure out whether the transmit succeeded or not. * .. and then notify rate control. */ ieee80211_tx_complete(tx_data->ni, tx_data->m, 0); tx_data->ni = NULL; tx_data->m = NULL; sc->sc_tx_timer = 0; tx_ring->queued--; } if (tx_ring->queued < (RTWN_TX_LIST_COUNT - 1)) sc->qfullmsk &= ~(1 << qid); rtwn_start(sc); } static int rtwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct rtwn_softc *sc = ic->ic_softc; RTWN_LOCK(sc); /* Prevent management frames from being sent if we're not ready. */ if (!(sc->sc_flags & RTWN_RUNNING)) { RTWN_UNLOCK(sc); m_freem(m); return (ENETDOWN); } if (rtwn_tx(sc, m, ni) != 0) { RTWN_UNLOCK(sc); return (EIO); } sc->sc_tx_timer = 5; RTWN_UNLOCK(sc); return (0); } static int rtwn_transmit(struct ieee80211com *ic, struct mbuf *m) { struct rtwn_softc *sc = ic->ic_softc; int error; RTWN_LOCK(sc); if ((sc->sc_flags & RTWN_RUNNING) == 0) { RTWN_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { RTWN_UNLOCK(sc); return (error); } rtwn_start(sc); RTWN_UNLOCK(sc); return (0); } static void rtwn_parent(struct ieee80211com *ic) { struct rtwn_softc *sc = ic->ic_softc; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (ic->ic_nrunning > 0) { if (rtwn_init(sc) == 0) ieee80211_start_all(ic); else ieee80211_stop(vap); } else rtwn_stop(sc); } static void rtwn_start(struct rtwn_softc *sc) { struct ieee80211_node *ni; struct mbuf *m; RTWN_LOCK_ASSERT(sc); if ((sc->sc_flags & RTWN_RUNNING) == 0) return; while (sc->qfullmsk == 0 && (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (rtwn_tx(sc, m, ni) != 0) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); continue; } sc->sc_tx_timer = 5; } } static void rtwn_watchdog(void *arg) { struct rtwn_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; RTWN_LOCK_ASSERT(sc); KASSERT(sc->sc_flags & RTWN_RUNNING, ("not running")); if (sc->sc_tx_timer != 0 && --sc->sc_tx_timer == 0) { ic_printf(ic, "device timeout\n"); ieee80211_restart_all(ic); return; } callout_reset(&sc->watchdog_to, hz, rtwn_watchdog, sc); } static int rtwn_power_on(struct rtwn_softc *sc) { uint32_t reg; int ntries; /* Wait for autoload done bit. */ for (ntries = 0; ntries < 1000; ntries++) { if (rtwn_read_1(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_PFM_ALDN) break; DELAY(5); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for chip autoload\n"); return (ETIMEDOUT); } /* Unlock ISO/CLK/Power control register. */ rtwn_write_1(sc, R92C_RSV_CTRL, 0); /* TODO: check if we need this for 8188CE */ if (sc->board_type != R92C_BOARD_TYPE_DONGLE) { /* bt coex */ reg = rtwn_read_4(sc, R92C_APS_FSMCO); reg |= (R92C_APS_FSMCO_SOP_ABG | R92C_APS_FSMCO_SOP_AMB | R92C_APS_FSMCO_XOP_BTCK); rtwn_write_4(sc, R92C_APS_FSMCO, reg); } /* Move SPS into PWM mode. */ rtwn_write_1(sc, R92C_SPS0_CTRL, 0x2b); /* Set low byte to 0x0f, leave others unchanged. */ rtwn_write_4(sc, R92C_AFE_XTAL_CTRL, (rtwn_read_4(sc, R92C_AFE_XTAL_CTRL) & 0xffffff00) | 0x0f); /* TODO: check if we need this for 8188CE */ if (sc->board_type != R92C_BOARD_TYPE_DONGLE) { /* bt coex */ reg = rtwn_read_4(sc, R92C_AFE_XTAL_CTRL); reg &= (~0x00024800); /* XXX magic from linux */ rtwn_write_4(sc, R92C_AFE_XTAL_CTRL, reg); } rtwn_write_2(sc, R92C_SYS_ISO_CTRL, (rtwn_read_2(sc, R92C_SYS_ISO_CTRL) & 0xff) | R92C_SYS_ISO_CTRL_PWC_EV12V | R92C_SYS_ISO_CTRL_DIOR); DELAY(200); /* TODO: linux does additional btcoex stuff here */ /* Auto enable WLAN. */ rtwn_write_2(sc, R92C_APS_FSMCO, rtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC); for (ntries = 0; ntries < 1000; ntries++) { if (!(rtwn_read_2(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_APFM_ONMAC)) break; DELAY(5); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for MAC auto ON\n"); return (ETIMEDOUT); } /* Enable radio, GPIO and LED functions. */ rtwn_write_2(sc, R92C_APS_FSMCO, R92C_APS_FSMCO_AFSM_PCIE | R92C_APS_FSMCO_PDN_EN | R92C_APS_FSMCO_PFM_ALDN); /* Release RF digital isolation. */ rtwn_write_2(sc, R92C_SYS_ISO_CTRL, rtwn_read_2(sc, R92C_SYS_ISO_CTRL) & ~R92C_SYS_ISO_CTRL_DIOR); if (sc->chip & RTWN_CHIP_92C) rtwn_write_1(sc, R92C_PCIE_CTRL_REG + 3, 0x77); else rtwn_write_1(sc, R92C_PCIE_CTRL_REG + 3, 0x22); rtwn_write_4(sc, R92C_INT_MIG, 0); if (sc->board_type != R92C_BOARD_TYPE_DONGLE) { /* bt coex */ reg = rtwn_read_4(sc, R92C_AFE_XTAL_CTRL + 2); reg &= 0xfd; /* XXX magic from linux */ rtwn_write_4(sc, R92C_AFE_XTAL_CTRL + 2, reg); } rtwn_write_1(sc, R92C_GPIO_MUXCFG, rtwn_read_1(sc, R92C_GPIO_MUXCFG) & ~R92C_GPIO_MUXCFG_RFKILL); reg = rtwn_read_1(sc, R92C_GPIO_IO_SEL); if (!(reg & R92C_GPIO_IO_SEL_RFKILL)) { device_printf(sc->sc_dev, "radio is disabled by hardware switch\n"); return (EPERM); } /* Initialize MAC. */ reg = rtwn_read_1(sc, R92C_APSD_CTRL); rtwn_write_1(sc, R92C_APSD_CTRL, rtwn_read_1(sc, R92C_APSD_CTRL) & ~R92C_APSD_CTRL_OFF); for (ntries = 0; ntries < 200; ntries++) { if (!(rtwn_read_1(sc, R92C_APSD_CTRL) & R92C_APSD_CTRL_OFF_STATUS)) break; DELAY(500); } if (ntries == 200) { device_printf(sc->sc_dev, "timeout waiting for MAC initialization\n"); return (ETIMEDOUT); } /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */ reg = rtwn_read_2(sc, R92C_CR); reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN | R92C_CR_ENSEC; rtwn_write_2(sc, R92C_CR, reg); rtwn_write_1(sc, 0xfe10, 0x19); return (0); } static int rtwn_llt_init(struct rtwn_softc *sc) { int i, error; /* Reserve pages [0; R92C_TX_PAGE_COUNT]. */ for (i = 0; i < R92C_TX_PAGE_COUNT; i++) { if ((error = rtwn_llt_write(sc, i, i + 1)) != 0) return (error); } /* NB: 0xff indicates end-of-list. */ if ((error = rtwn_llt_write(sc, i, 0xff)) != 0) return (error); /* * Use pages [R92C_TX_PAGE_COUNT + 1; R92C_TXPKTBUF_COUNT - 1] * as ring buffer. */ for (++i; i < R92C_TXPKTBUF_COUNT - 1; i++) { if ((error = rtwn_llt_write(sc, i, i + 1)) != 0) return (error); } /* Make the last page point to the beginning of the ring buffer. */ error = rtwn_llt_write(sc, i, R92C_TX_PAGE_COUNT + 1); return (error); } static void rtwn_fw_reset(struct rtwn_softc *sc) { uint16_t reg; int ntries; /* Tell 8051 to reset itself. */ rtwn_write_1(sc, R92C_HMETFR + 3, 0x20); /* Wait until 8051 resets by itself. */ for (ntries = 0; ntries < 100; ntries++) { reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN); if (!(reg & R92C_SYS_FUNC_EN_CPUEN)) goto sleep; DELAY(50); } /* Force 8051 reset. */ rtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN); sleep: /* * We must sleep for one second to let the firmware settle. * Accessing registers too early will hang the whole system. */ if (msleep(®, &sc->sc_mtx, 0, "rtwnrst", hz)) { device_printf(sc->sc_dev, "timeout waiting for firmware " "initialization to complete\n"); } } static void rtwn_fw_loadpage(struct rtwn_softc *sc, int page, const uint8_t *buf, int len) { uint32_t reg; int off, mlen, i; reg = rtwn_read_4(sc, R92C_MCUFWDL); reg = RW(reg, R92C_MCUFWDL_PAGE, page); rtwn_write_4(sc, R92C_MCUFWDL, reg); DELAY(5); off = R92C_FW_START_ADDR; while (len > 0) { if (len > 196) mlen = 196; else if (len > 4) mlen = 4; else mlen = 1; for (i = 0; i < mlen; i++) rtwn_write_1(sc, off++, buf[i]); buf += mlen; len -= mlen; } } static int rtwn_load_firmware(struct rtwn_softc *sc) { const struct firmware *fw; const struct r92c_fw_hdr *hdr; const char *name; const u_char *ptr; size_t len; uint32_t reg; int mlen, ntries, page, error = 0; /* Read firmware image from the filesystem. */ if ((sc->chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) == RTWN_CHIP_UMC_A_CUT) name = "rtwn-rtl8192cfwU"; else name = "rtwn-rtl8192cfwU_B"; RTWN_UNLOCK(sc); fw = firmware_get(name); RTWN_LOCK(sc); if (fw == NULL) { device_printf(sc->sc_dev, "could not read firmware %s\n", name); return (ENOENT); } len = fw->datasize; if (len < sizeof(*hdr)) { device_printf(sc->sc_dev, "firmware too short\n"); error = EINVAL; goto fail; } ptr = fw->data; hdr = (const struct r92c_fw_hdr *)ptr; /* Check if there is a valid FW header and skip it. */ if ((le16toh(hdr->signature) >> 4) == 0x88c || (le16toh(hdr->signature) >> 4) == 0x92c) { DPRINTF(("FW V%d.%d %02d-%02d %02d:%02d\n", le16toh(hdr->version), le16toh(hdr->subversion), hdr->month, hdr->date, hdr->hour, hdr->minute)); ptr += sizeof(*hdr); len -= sizeof(*hdr); } if (rtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL) rtwn_fw_reset(sc); /* Enable FW download. */ rtwn_write_2(sc, R92C_SYS_FUNC_EN, rtwn_read_2(sc, R92C_SYS_FUNC_EN) | R92C_SYS_FUNC_EN_CPUEN); rtwn_write_1(sc, R92C_MCUFWDL, rtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_EN); rtwn_write_1(sc, R92C_MCUFWDL + 2, rtwn_read_1(sc, R92C_MCUFWDL + 2) & ~0x08); /* Reset the FWDL checksum. */ rtwn_write_1(sc, R92C_MCUFWDL, rtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_CHKSUM_RPT); for (page = 0; len > 0; page++) { mlen = MIN(len, R92C_FW_PAGE_SIZE); rtwn_fw_loadpage(sc, page, ptr, mlen); ptr += mlen; len -= mlen; } /* Disable FW download. */ rtwn_write_1(sc, R92C_MCUFWDL, rtwn_read_1(sc, R92C_MCUFWDL) & ~R92C_MCUFWDL_EN); rtwn_write_1(sc, R92C_MCUFWDL + 1, 0); /* Wait for checksum report. */ for (ntries = 0; ntries < 1000; ntries++) { if (rtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_CHKSUM_RPT) break; DELAY(5); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for checksum report\n"); error = ETIMEDOUT; goto fail; } reg = rtwn_read_4(sc, R92C_MCUFWDL); reg = (reg & ~R92C_MCUFWDL_WINTINI_RDY) | R92C_MCUFWDL_RDY; rtwn_write_4(sc, R92C_MCUFWDL, reg); /* Wait for firmware readiness. */ for (ntries = 0; ntries < 2000; ntries++) { if (rtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_WINTINI_RDY) break; DELAY(50); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for firmware readiness\n"); error = ETIMEDOUT; goto fail; } fail: firmware_put(fw, FIRMWARE_UNLOAD); return (error); } static int rtwn_dma_init(struct rtwn_softc *sc) { uint32_t reg; int error; /* Initialize LLT table. */ error = rtwn_llt_init(sc); if (error != 0) return error; /* Set number of pages for normal priority queue. */ rtwn_write_2(sc, R92C_RQPN_NPQ, 0); rtwn_write_4(sc, R92C_RQPN, /* Set number of pages for public queue. */ SM(R92C_RQPN_PUBQ, R92C_PUBQ_NPAGES) | /* Set number of pages for high priority queue. */ SM(R92C_RQPN_HPQ, R92C_HPQ_NPAGES) | /* Set number of pages for low priority queue. */ SM(R92C_RQPN_LPQ, R92C_LPQ_NPAGES) | /* Load values. */ R92C_RQPN_LD); rtwn_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, R92C_TX_PAGE_BOUNDARY); rtwn_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, R92C_TX_PAGE_BOUNDARY); rtwn_write_1(sc, R92C_TXPKTBUF_WMAC_LBK_BF_HD, R92C_TX_PAGE_BOUNDARY); rtwn_write_1(sc, R92C_TRXFF_BNDY, R92C_TX_PAGE_BOUNDARY); rtwn_write_1(sc, R92C_TDECTRL + 1, R92C_TX_PAGE_BOUNDARY); reg = rtwn_read_2(sc, R92C_TRXDMA_CTRL); reg &= ~R92C_TRXDMA_CTRL_QMAP_M; reg |= 0xF771; rtwn_write_2(sc, R92C_TRXDMA_CTRL, reg); rtwn_write_4(sc, R92C_TCR, R92C_TCR_CFENDFORM | (1 << 12) | (1 << 13)); /* Configure Tx DMA. */ rtwn_write_4(sc, R92C_BKQ_DESA, sc->tx_ring[RTWN_BK_QUEUE].paddr); rtwn_write_4(sc, R92C_BEQ_DESA, sc->tx_ring[RTWN_BE_QUEUE].paddr); rtwn_write_4(sc, R92C_VIQ_DESA, sc->tx_ring[RTWN_VI_QUEUE].paddr); rtwn_write_4(sc, R92C_VOQ_DESA, sc->tx_ring[RTWN_VO_QUEUE].paddr); rtwn_write_4(sc, R92C_BCNQ_DESA, sc->tx_ring[RTWN_BEACON_QUEUE].paddr); rtwn_write_4(sc, R92C_MGQ_DESA, sc->tx_ring[RTWN_MGNT_QUEUE].paddr); rtwn_write_4(sc, R92C_HQ_DESA, sc->tx_ring[RTWN_HIGH_QUEUE].paddr); /* Configure Rx DMA. */ rtwn_write_4(sc, R92C_RX_DESA, sc->rx_ring.paddr); /* Set Tx/Rx transfer page boundary. */ rtwn_write_2(sc, R92C_TRXFF_BNDY + 2, 0x27ff); /* Set Tx/Rx transfer page size. */ rtwn_write_1(sc, R92C_PBP, SM(R92C_PBP_PSRX, R92C_PBP_128) | SM(R92C_PBP_PSTX, R92C_PBP_128)); return (0); } static void rtwn_mac_init(struct rtwn_softc *sc) { int i; /* Write MAC initialization values. */ for (i = 0; i < nitems(rtl8192ce_mac); i++) rtwn_write_1(sc, rtl8192ce_mac[i].reg, rtl8192ce_mac[i].val); } static void rtwn_bb_init(struct rtwn_softc *sc) { const struct rtwn_bb_prog *prog; uint32_t reg; int i; /* Enable BB and RF. */ rtwn_write_2(sc, R92C_SYS_FUNC_EN, rtwn_read_2(sc, R92C_SYS_FUNC_EN) | R92C_SYS_FUNC_EN_BBRSTB | R92C_SYS_FUNC_EN_BB_GLB_RST | R92C_SYS_FUNC_EN_DIO_RF); rtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0xdb83); rtwn_write_1(sc, R92C_RF_CTRL, R92C_RF_CTRL_EN | R92C_RF_CTRL_RSTB | R92C_RF_CTRL_SDMRSTB); rtwn_write_1(sc, R92C_SYS_FUNC_EN, R92C_SYS_FUNC_EN_DIO_PCIE | R92C_SYS_FUNC_EN_PCIEA | R92C_SYS_FUNC_EN_PPLL | R92C_SYS_FUNC_EN_BB_GLB_RST | R92C_SYS_FUNC_EN_BBRSTB); rtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80); rtwn_write_4(sc, R92C_LEDCFG0, rtwn_read_4(sc, R92C_LEDCFG0) | 0x00800000); /* Select BB programming. */ prog = (sc->chip & RTWN_CHIP_92C) ? &rtl8192ce_bb_prog_2t : &rtl8192ce_bb_prog_1t; /* Write BB initialization values. */ for (i = 0; i < prog->count; i++) { rtwn_bb_write(sc, prog->regs[i], prog->vals[i]); DELAY(1); } if (sc->chip & RTWN_CHIP_92C_1T2R) { /* 8192C 1T only configuration. */ reg = rtwn_bb_read(sc, R92C_FPGA0_TXINFO); reg = (reg & ~0x00000003) | 0x2; rtwn_bb_write(sc, R92C_FPGA0_TXINFO, reg); reg = rtwn_bb_read(sc, R92C_FPGA1_TXINFO); reg = (reg & ~0x00300033) | 0x00200022; rtwn_bb_write(sc, R92C_FPGA1_TXINFO, reg); reg = rtwn_bb_read(sc, R92C_CCK0_AFESETTING); reg = (reg & ~0xff000000) | 0x45 << 24; rtwn_bb_write(sc, R92C_CCK0_AFESETTING, reg); reg = rtwn_bb_read(sc, R92C_OFDM0_TRXPATHENA); reg = (reg & ~0x000000ff) | 0x23; rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, reg); reg = rtwn_bb_read(sc, R92C_OFDM0_AGCPARAM1); reg = (reg & ~0x00000030) | 1 << 4; rtwn_bb_write(sc, R92C_OFDM0_AGCPARAM1, reg); reg = rtwn_bb_read(sc, 0xe74); reg = (reg & ~0x0c000000) | 2 << 26; rtwn_bb_write(sc, 0xe74, reg); reg = rtwn_bb_read(sc, 0xe78); reg = (reg & ~0x0c000000) | 2 << 26; rtwn_bb_write(sc, 0xe78, reg); reg = rtwn_bb_read(sc, 0xe7c); reg = (reg & ~0x0c000000) | 2 << 26; rtwn_bb_write(sc, 0xe7c, reg); reg = rtwn_bb_read(sc, 0xe80); reg = (reg & ~0x0c000000) | 2 << 26; rtwn_bb_write(sc, 0xe80, reg); reg = rtwn_bb_read(sc, 0xe88); reg = (reg & ~0x0c000000) | 2 << 26; rtwn_bb_write(sc, 0xe88, reg); } /* Write AGC values. */ for (i = 0; i < prog->agccount; i++) { rtwn_bb_write(sc, R92C_OFDM0_AGCRSSITABLE, prog->agcvals[i]); DELAY(1); } if (rtwn_bb_read(sc, R92C_HSSI_PARAM2(0)) & R92C_HSSI_PARAM2_CCK_HIPWR) sc->sc_flags |= RTWN_FLAG_CCK_HIPWR; } static void rtwn_rf_init(struct rtwn_softc *sc) { const struct rtwn_rf_prog *prog; uint32_t reg, type; int i, j, idx, off; /* Select RF programming based on board type. */ if (!(sc->chip & RTWN_CHIP_92C)) { if (sc->board_type == R92C_BOARD_TYPE_MINICARD) prog = rtl8188ce_rf_prog; else if (sc->board_type == R92C_BOARD_TYPE_HIGHPA) prog = rtl8188ru_rf_prog; else prog = rtl8188cu_rf_prog; } else prog = rtl8192ce_rf_prog; for (i = 0; i < sc->nrxchains; i++) { /* Save RF_ENV control type. */ idx = i / 2; off = (i % 2) * 16; reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(idx)); type = (reg >> off) & 0x10; /* Set RF_ENV enable. */ reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i)); reg |= 0x100000; rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg); DELAY(1); /* Set RF_ENV output high. */ reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i)); reg |= 0x10; rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg); DELAY(1); /* Set address and data lengths of RF registers. */ reg = rtwn_bb_read(sc, R92C_HSSI_PARAM2(i)); reg &= ~R92C_HSSI_PARAM2_ADDR_LENGTH; rtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg); DELAY(1); reg = rtwn_bb_read(sc, R92C_HSSI_PARAM2(i)); reg &= ~R92C_HSSI_PARAM2_DATA_LENGTH; rtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg); DELAY(1); /* Write RF initialization values for this chain. */ for (j = 0; j < prog[i].count; j++) { if (prog[i].regs[j] >= 0xf9 && prog[i].regs[j] <= 0xfe) { /* * These are fake RF registers offsets that * indicate a delay is required. */ DELAY(50); continue; } rtwn_rf_write(sc, i, prog[i].regs[j], prog[i].vals[j]); DELAY(1); } /* Restore RF_ENV control type. */ reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(idx)); reg &= ~(0x10 << off) | (type << off); rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(idx), reg); /* Cache RF register CHNLBW. */ sc->rf_chnlbw[i] = rtwn_rf_read(sc, i, R92C_RF_CHNLBW); } if ((sc->chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) == RTWN_CHIP_UMC_A_CUT) { rtwn_rf_write(sc, 0, R92C_RF_RX_G1, 0x30255); rtwn_rf_write(sc, 0, R92C_RF_RX_G2, 0x50a00); } } static void rtwn_cam_init(struct rtwn_softc *sc) { /* Invalidate all CAM entries. */ rtwn_write_4(sc, R92C_CAMCMD, R92C_CAMCMD_POLLING | R92C_CAMCMD_CLR); } static void rtwn_pa_bias_init(struct rtwn_softc *sc) { uint8_t reg; int i; for (i = 0; i < sc->nrxchains; i++) { if (sc->pa_setting & (1 << i)) continue; rtwn_rf_write(sc, i, R92C_RF_IPA, 0x0f406); rtwn_rf_write(sc, i, R92C_RF_IPA, 0x4f406); rtwn_rf_write(sc, i, R92C_RF_IPA, 0x8f406); rtwn_rf_write(sc, i, R92C_RF_IPA, 0xcf406); } if (!(sc->pa_setting & 0x10)) { reg = rtwn_read_1(sc, 0x16); reg = (reg & ~0xf0) | 0x90; rtwn_write_1(sc, 0x16, reg); } } static void rtwn_rxfilter_init(struct rtwn_softc *sc) { /* Initialize Rx filter. */ /* TODO: use better filter for monitor mode. */ rtwn_write_4(sc, R92C_RCR, R92C_RCR_AAP | R92C_RCR_APM | R92C_RCR_AM | R92C_RCR_AB | R92C_RCR_APP_ICV | R92C_RCR_AMF | R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_MIC | R92C_RCR_APP_PHYSTS); /* Accept all multicast frames. */ rtwn_write_4(sc, R92C_MAR + 0, 0xffffffff); rtwn_write_4(sc, R92C_MAR + 4, 0xffffffff); /* Accept all management frames. */ rtwn_write_2(sc, R92C_RXFLTMAP0, 0xffff); /* Reject all control frames. */ rtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000); /* Accept all data frames. */ rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); } static void rtwn_edca_init(struct rtwn_softc *sc) { rtwn_write_2(sc, R92C_SPEC_SIFS, 0x1010); rtwn_write_2(sc, R92C_MAC_SPEC_SIFS, 0x1010); rtwn_write_2(sc, R92C_SIFS_CCK, 0x1010); rtwn_write_2(sc, R92C_SIFS_OFDM, 0x0e0e); rtwn_write_4(sc, R92C_EDCA_BE_PARAM, 0x005ea42b); rtwn_write_4(sc, R92C_EDCA_BK_PARAM, 0x0000a44f); rtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4322); rtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3222); } static void rtwn_write_txpower(struct rtwn_softc *sc, int chain, uint16_t power[RTWN_RIDX_COUNT]) { uint32_t reg; /* Write per-CCK rate Tx power. */ if (chain == 0) { reg = rtwn_bb_read(sc, R92C_TXAGC_A_CCK1_MCS32); reg = RW(reg, R92C_TXAGC_A_CCK1, power[0]); rtwn_bb_write(sc, R92C_TXAGC_A_CCK1_MCS32, reg); reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11); reg = RW(reg, R92C_TXAGC_A_CCK2, power[1]); reg = RW(reg, R92C_TXAGC_A_CCK55, power[2]); reg = RW(reg, R92C_TXAGC_A_CCK11, power[3]); rtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg); } else { reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK1_55_MCS32); reg = RW(reg, R92C_TXAGC_B_CCK1, power[0]); reg = RW(reg, R92C_TXAGC_B_CCK2, power[1]); reg = RW(reg, R92C_TXAGC_B_CCK55, power[2]); rtwn_bb_write(sc, R92C_TXAGC_B_CCK1_55_MCS32, reg); reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11); reg = RW(reg, R92C_TXAGC_B_CCK11, power[3]); rtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg); } /* Write per-OFDM rate Tx power. */ rtwn_bb_write(sc, R92C_TXAGC_RATE18_06(chain), SM(R92C_TXAGC_RATE06, power[ 4]) | SM(R92C_TXAGC_RATE09, power[ 5]) | SM(R92C_TXAGC_RATE12, power[ 6]) | SM(R92C_TXAGC_RATE18, power[ 7])); rtwn_bb_write(sc, R92C_TXAGC_RATE54_24(chain), SM(R92C_TXAGC_RATE24, power[ 8]) | SM(R92C_TXAGC_RATE36, power[ 9]) | SM(R92C_TXAGC_RATE48, power[10]) | SM(R92C_TXAGC_RATE54, power[11])); /* Write per-MCS Tx power. */ rtwn_bb_write(sc, R92C_TXAGC_MCS03_MCS00(chain), SM(R92C_TXAGC_MCS00, power[12]) | SM(R92C_TXAGC_MCS01, power[13]) | SM(R92C_TXAGC_MCS02, power[14]) | SM(R92C_TXAGC_MCS03, power[15])); rtwn_bb_write(sc, R92C_TXAGC_MCS07_MCS04(chain), SM(R92C_TXAGC_MCS04, power[16]) | SM(R92C_TXAGC_MCS05, power[17]) | SM(R92C_TXAGC_MCS06, power[18]) | SM(R92C_TXAGC_MCS07, power[19])); rtwn_bb_write(sc, R92C_TXAGC_MCS11_MCS08(chain), SM(R92C_TXAGC_MCS08, power[20]) | SM(R92C_TXAGC_MCS09, power[21]) | SM(R92C_TXAGC_MCS10, power[22]) | SM(R92C_TXAGC_MCS11, power[23])); rtwn_bb_write(sc, R92C_TXAGC_MCS15_MCS12(chain), SM(R92C_TXAGC_MCS12, power[24]) | SM(R92C_TXAGC_MCS13, power[25]) | SM(R92C_TXAGC_MCS14, power[26]) | SM(R92C_TXAGC_MCS15, power[27])); } static void rtwn_get_txpower(struct rtwn_softc *sc, int chain, struct ieee80211_channel *c, struct ieee80211_channel *extc, uint16_t power[RTWN_RIDX_COUNT]) { struct ieee80211com *ic = &sc->sc_ic; struct r92c_rom *rom = &sc->rom; uint16_t cckpow, ofdmpow, htpow, diff, max; const struct rtwn_txpwr *base; int ridx, chan, group; /* Determine channel group. */ chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ if (chan <= 3) group = 0; else if (chan <= 9) group = 1; else group = 2; /* Get original Tx power based on board type and RF chain. */ if (!(sc->chip & RTWN_CHIP_92C)) { if (sc->board_type == R92C_BOARD_TYPE_HIGHPA) base = &rtl8188ru_txagc[chain]; else base = &rtl8192cu_txagc[chain]; } else base = &rtl8192cu_txagc[chain]; memset(power, 0, RTWN_RIDX_COUNT * sizeof(power[0])); if (sc->regulatory == 0) { for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) power[ridx] = base->pwr[0][ridx]; } for (ridx = RTWN_RIDX_OFDM6; ridx < RTWN_RIDX_COUNT; ridx++) { if (sc->regulatory == 3) { power[ridx] = base->pwr[0][ridx]; /* Apply vendor limits. */ if (extc != NULL) max = rom->ht40_max_pwr[group]; else max = rom->ht20_max_pwr[group]; max = (max >> (chain * 4)) & 0xf; if (power[ridx] > max) power[ridx] = max; } else if (sc->regulatory == 1) { if (extc == NULL) power[ridx] = base->pwr[group][ridx]; } else if (sc->regulatory != 2) power[ridx] = base->pwr[0][ridx]; } /* Compute per-CCK rate Tx power. */ cckpow = rom->cck_tx_pwr[chain][group]; for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) { power[ridx] += cckpow; if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } htpow = rom->ht40_1s_tx_pwr[chain][group]; if (sc->ntxchains > 1) { /* Apply reduction for 2 spatial streams. */ diff = rom->ht40_2s_tx_pwr_diff[group]; diff = (diff >> (chain * 4)) & 0xf; htpow = (htpow > diff) ? htpow - diff : 0; } /* Compute per-OFDM rate Tx power. */ diff = rom->ofdm_tx_pwr_diff[group]; diff = (diff >> (chain * 4)) & 0xf; ofdmpow = htpow + diff; /* HT->OFDM correction. */ for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) { power[ridx] += ofdmpow; if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } /* Compute per-MCS Tx power. */ if (extc == NULL) { diff = rom->ht20_tx_pwr_diff[group]; diff = (diff >> (chain * 4)) & 0xf; htpow += diff; /* HT40->HT20 correction. */ } for (ridx = RTWN_RIDX_MCS0; ridx <= RTWN_RIDX_MCS15; ridx++) { power[ridx] += htpow; if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } #ifdef RTWN_DEBUG if (sc->sc_debug >= 4) { /* Dump per-rate Tx power values. */ printf("Tx power for chain %d:\n", chain); for (ridx = RTWN_RIDX_CCK1; ridx < RTWN_RIDX_COUNT; ridx++) printf("Rate %d = %u\n", ridx, power[ridx]); } #endif } static void rtwn_set_txpower(struct rtwn_softc *sc, struct ieee80211_channel *c, struct ieee80211_channel *extc) { uint16_t power[RTWN_RIDX_COUNT]; int i; for (i = 0; i < sc->ntxchains; i++) { /* Compute per-rate Tx power values. */ rtwn_get_txpower(sc, i, c, extc, power); /* Write per-rate Tx power values to hardware. */ rtwn_write_txpower(sc, i, power); } } static void rtwn_set_rx_bssid_all(struct rtwn_softc *sc, int enable) { uint32_t reg; reg = rtwn_read_4(sc, R92C_RCR); if (enable) reg &= ~R92C_RCR_CBSSID_BCN; else reg |= R92C_RCR_CBSSID_BCN; rtwn_write_4(sc, R92C_RCR, reg); } static void rtwn_set_gain(struct rtwn_softc *sc, uint8_t gain) { uint32_t reg; reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(0)); reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, gain); rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg); reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1)); reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, gain); rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg); } static void rtwn_scan_start(struct ieee80211com *ic) { struct rtwn_softc *sc = ic->ic_softc; RTWN_LOCK(sc); /* Receive beacons / probe responses from any BSSID. */ rtwn_set_rx_bssid_all(sc, 1); /* Set gain for scanning. */ rtwn_set_gain(sc, 0x20); RTWN_UNLOCK(sc); } static void rtwn_scan_end(struct ieee80211com *ic) { struct rtwn_softc *sc = ic->ic_softc; RTWN_LOCK(sc); /* Restore limitations. */ rtwn_set_rx_bssid_all(sc, 0); /* Set gain under link. */ rtwn_set_gain(sc, 0x32); RTWN_UNLOCK(sc); +} + +static void +rtwn_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + uint8_t bands[IEEE80211_MODE_BYTES]; + + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, + rtwn_chan_2ghz, nitems(rtwn_chan_2ghz), bands, 0); } static void rtwn_set_channel(struct ieee80211com *ic) { struct rtwn_softc *sc = ic->ic_softc; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); RTWN_LOCK(sc); if (vap->iv_state == IEEE80211_S_SCAN) { /* Make link LED blink during scan. */ rtwn_set_led(sc, RTWN_LED_LINK, !sc->ledlink); } rtwn_set_chan(sc, ic->ic_curchan, NULL); RTWN_UNLOCK(sc); } static void rtwn_update_mcast(struct ieee80211com *ic) { /* XXX do nothing? */ } static void rtwn_set_chan(struct rtwn_softc *sc, struct ieee80211_channel *c, struct ieee80211_channel *extc) { struct ieee80211com *ic = &sc->sc_ic; u_int chan; int i; chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ if (chan == 0 || chan == IEEE80211_CHAN_ANY) { device_printf(sc->sc_dev, "%s: invalid channel %x\n", __func__, chan); return; } /* Set Tx power for this new channel. */ rtwn_set_txpower(sc, c, extc); for (i = 0; i < sc->nrxchains; i++) { rtwn_rf_write(sc, i, R92C_RF_CHNLBW, RW(sc->rf_chnlbw[i], R92C_RF_CHNLBW_CHNL, chan)); } #ifndef IEEE80211_NO_HT if (extc != NULL) { uint32_t reg; /* Is secondary channel below or above primary? */ int prichlo = c->ic_freq < extc->ic_freq; rtwn_write_1(sc, R92C_BWOPMODE, rtwn_read_1(sc, R92C_BWOPMODE) & ~R92C_BWOPMODE_20MHZ); reg = rtwn_read_1(sc, R92C_RRSR + 2); reg = (reg & ~0x6f) | (prichlo ? 1 : 2) << 5; rtwn_write_1(sc, R92C_RRSR + 2, reg); rtwn_bb_write(sc, R92C_FPGA0_RFMOD, rtwn_bb_read(sc, R92C_FPGA0_RFMOD) | R92C_RFMOD_40MHZ); rtwn_bb_write(sc, R92C_FPGA1_RFMOD, rtwn_bb_read(sc, R92C_FPGA1_RFMOD) | R92C_RFMOD_40MHZ); /* Set CCK side band. */ reg = rtwn_bb_read(sc, R92C_CCK0_SYSTEM); reg = (reg & ~0x00000010) | (prichlo ? 0 : 1) << 4; rtwn_bb_write(sc, R92C_CCK0_SYSTEM, reg); reg = rtwn_bb_read(sc, R92C_OFDM1_LSTF); reg = (reg & ~0x00000c00) | (prichlo ? 1 : 2) << 10; rtwn_bb_write(sc, R92C_OFDM1_LSTF, reg); rtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2, rtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) & ~R92C_FPGA0_ANAPARAM2_CBW20); reg = rtwn_bb_read(sc, 0x818); reg = (reg & ~0x0c000000) | (prichlo ? 2 : 1) << 26; rtwn_bb_write(sc, 0x818, reg); /* Select 40MHz bandwidth. */ rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, (sc->rf_chnlbw[0] & ~0xfff) | chan); } else #endif { rtwn_write_1(sc, R92C_BWOPMODE, rtwn_read_1(sc, R92C_BWOPMODE) | R92C_BWOPMODE_20MHZ); rtwn_bb_write(sc, R92C_FPGA0_RFMOD, rtwn_bb_read(sc, R92C_FPGA0_RFMOD) & ~R92C_RFMOD_40MHZ); rtwn_bb_write(sc, R92C_FPGA1_RFMOD, rtwn_bb_read(sc, R92C_FPGA1_RFMOD) & ~R92C_RFMOD_40MHZ); rtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2, rtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) | R92C_FPGA0_ANAPARAM2_CBW20); /* Select 20MHz bandwidth. */ rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, (sc->rf_chnlbw[0] & ~0xfff) | R92C_RF_CHNLBW_BW20 | chan); } } static int rtwn_iq_calib_chain(struct rtwn_softc *sc, int chain, uint16_t tx[2], uint16_t rx[2]) { uint32_t status; int offset = chain * 0x20; if (chain == 0) { /* IQ calibration for chain 0. */ /* IQ calibration settings for chain 0. */ rtwn_bb_write(sc, 0xe30, 0x10008c1f); rtwn_bb_write(sc, 0xe34, 0x10008c1f); rtwn_bb_write(sc, 0xe38, 0x82140102); if (sc->ntxchains > 1) { rtwn_bb_write(sc, 0xe3c, 0x28160202); /* 2T */ /* IQ calibration settings for chain 1. */ rtwn_bb_write(sc, 0xe50, 0x10008c22); rtwn_bb_write(sc, 0xe54, 0x10008c22); rtwn_bb_write(sc, 0xe58, 0x82140102); rtwn_bb_write(sc, 0xe5c, 0x28160202); } else rtwn_bb_write(sc, 0xe3c, 0x28160502); /* 1T */ /* LO calibration settings. */ rtwn_bb_write(sc, 0xe4c, 0x001028d1); /* We're doing LO and IQ calibration in one shot. */ rtwn_bb_write(sc, 0xe48, 0xf9000000); rtwn_bb_write(sc, 0xe48, 0xf8000000); } else { /* IQ calibration for chain 1. */ /* We're doing LO and IQ calibration in one shot. */ rtwn_bb_write(sc, 0xe60, 0x00000002); rtwn_bb_write(sc, 0xe60, 0x00000000); } /* Give LO and IQ calibrations the time to complete. */ DELAY(1000); /* Read IQ calibration status. */ status = rtwn_bb_read(sc, 0xeac); if (status & (1 << (28 + chain * 3))) return (0); /* Tx failed. */ /* Read Tx IQ calibration results. */ tx[0] = (rtwn_bb_read(sc, 0xe94 + offset) >> 16) & 0x3ff; tx[1] = (rtwn_bb_read(sc, 0xe9c + offset) >> 16) & 0x3ff; if (tx[0] == 0x142 || tx[1] == 0x042) return (0); /* Tx failed. */ if (status & (1 << (27 + chain * 3))) return (1); /* Rx failed. */ /* Read Rx IQ calibration results. */ rx[0] = (rtwn_bb_read(sc, 0xea4 + offset) >> 16) & 0x3ff; rx[1] = (rtwn_bb_read(sc, 0xeac + offset) >> 16) & 0x3ff; if (rx[0] == 0x132 || rx[1] == 0x036) return (1); /* Rx failed. */ return (3); /* Both Tx and Rx succeeded. */ } static void rtwn_iq_calib_run(struct rtwn_softc *sc, int n, uint16_t tx[2][2], uint16_t rx[2][2]) { /* Registers to save and restore during IQ calibration. */ struct iq_cal_regs { uint32_t adda[16]; uint8_t txpause; uint8_t bcn_ctrl; uint8_t ustime_tsf; uint32_t gpio_muxcfg; uint32_t ofdm0_trxpathena; uint32_t ofdm0_trmuxpar; uint32_t fpga0_rfifacesw1; } iq_cal_regs; static const uint16_t reg_adda[16] = { 0x85c, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec }; int i, chain; uint32_t hssi_param1; if (n == 0) { for (i = 0; i < nitems(reg_adda); i++) iq_cal_regs.adda[i] = rtwn_bb_read(sc, reg_adda[i]); iq_cal_regs.txpause = rtwn_read_1(sc, R92C_TXPAUSE); iq_cal_regs.bcn_ctrl = rtwn_read_1(sc, R92C_BCN_CTRL); iq_cal_regs.ustime_tsf = rtwn_read_1(sc, R92C_USTIME_TSF); iq_cal_regs.gpio_muxcfg = rtwn_read_4(sc, R92C_GPIO_MUXCFG); } if (sc->ntxchains == 1) { rtwn_bb_write(sc, reg_adda[0], 0x0b1b25a0); for (i = 1; i < nitems(reg_adda); i++) rtwn_bb_write(sc, reg_adda[i], 0x0bdb25a0); } else { for (i = 0; i < nitems(reg_adda); i++) rtwn_bb_write(sc, reg_adda[i], 0x04db25a4); } hssi_param1 = rtwn_bb_read(sc, R92C_HSSI_PARAM1(0)); if (!(hssi_param1 & R92C_HSSI_PARAM1_PI)) { rtwn_bb_write(sc, R92C_HSSI_PARAM1(0), hssi_param1 | R92C_HSSI_PARAM1_PI); rtwn_bb_write(sc, R92C_HSSI_PARAM1(1), hssi_param1 | R92C_HSSI_PARAM1_PI); } if (n == 0) { iq_cal_regs.ofdm0_trxpathena = rtwn_bb_read(sc, R92C_OFDM0_TRXPATHENA); iq_cal_regs.ofdm0_trmuxpar = rtwn_bb_read(sc, R92C_OFDM0_TRMUXPAR); iq_cal_regs.fpga0_rfifacesw1 = rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(1)); } rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, 0x03a05600); rtwn_bb_write(sc, R92C_OFDM0_TRMUXPAR, 0x000800e4); rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(1), 0x22204000); if (sc->ntxchains > 1) { rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00010000); rtwn_bb_write(sc, R92C_LSSI_PARAM(1), 0x00010000); } rtwn_write_1(sc, R92C_TXPAUSE, 0x3f); rtwn_write_1(sc, R92C_BCN_CTRL, iq_cal_regs.bcn_ctrl & ~(0x08)); rtwn_write_1(sc, R92C_USTIME_TSF, iq_cal_regs.ustime_tsf & ~(0x08)); rtwn_write_1(sc, R92C_GPIO_MUXCFG, iq_cal_regs.gpio_muxcfg & ~(0x20)); rtwn_bb_write(sc, 0x0b68, 0x00080000); if (sc->ntxchains > 1) rtwn_bb_write(sc, 0x0b6c, 0x00080000); rtwn_bb_write(sc, 0x0e28, 0x80800000); rtwn_bb_write(sc, 0x0e40, 0x01007c00); rtwn_bb_write(sc, 0x0e44, 0x01004800); rtwn_bb_write(sc, 0x0b68, 0x00080000); for (chain = 0; chain < sc->ntxchains; chain++) { if (chain > 0) { /* Put chain 0 on standby. */ rtwn_bb_write(sc, 0x0e28, 0x00); rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00010000); rtwn_bb_write(sc, 0x0e28, 0x80800000); /* Enable chain 1. */ for (i = 0; i < nitems(reg_adda); i++) rtwn_bb_write(sc, reg_adda[i], 0x0b1b25a4); } /* Run IQ calibration twice. */ for (i = 0; i < 2; i++) { int ret; ret = rtwn_iq_calib_chain(sc, chain, tx[chain], rx[chain]); if (ret == 0) { DPRINTF(("%s: chain %d: Tx failed.\n", __func__, chain)); tx[chain][0] = 0xff; tx[chain][1] = 0xff; rx[chain][0] = 0xff; rx[chain][1] = 0xff; } else if (ret == 1) { DPRINTF(("%s: chain %d: Rx failed.\n", __func__, chain)); rx[chain][0] = 0xff; rx[chain][1] = 0xff; } else if (ret == 3) { DPRINTF(("%s: chain %d: Both Tx and Rx " "succeeded.\n", __func__, chain)); } } DPRINTF(("%s: results for run %d chain %d: tx[0]=0x%x, " "tx[1]=0x%x rx[0]=0x%x rx[1]=0x%x\n", __func__, n, chain, tx[chain][0], tx[chain][1], rx[chain][0], rx[chain][1])); } rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, iq_cal_regs.ofdm0_trxpathena); rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(1), iq_cal_regs.fpga0_rfifacesw1); rtwn_bb_write(sc, R92C_OFDM0_TRMUXPAR, iq_cal_regs.ofdm0_trmuxpar); rtwn_bb_write(sc, 0x0e28, 0x00); rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00032ed3); if (sc->ntxchains > 1) rtwn_bb_write(sc, R92C_LSSI_PARAM(1), 0x00032ed3); if (n != 0) { if (!(hssi_param1 & R92C_HSSI_PARAM1_PI)) { rtwn_bb_write(sc, R92C_HSSI_PARAM1(0), hssi_param1); rtwn_bb_write(sc, R92C_HSSI_PARAM1(1), hssi_param1); } for (i = 0; i < nitems(reg_adda); i++) rtwn_bb_write(sc, reg_adda[i], iq_cal_regs.adda[i]); rtwn_write_1(sc, R92C_TXPAUSE, iq_cal_regs.txpause); rtwn_write_1(sc, R92C_BCN_CTRL, iq_cal_regs.bcn_ctrl); rtwn_write_1(sc, R92C_USTIME_TSF, iq_cal_regs.ustime_tsf); rtwn_write_4(sc, R92C_GPIO_MUXCFG, iq_cal_regs.gpio_muxcfg); } } #define RTWN_IQ_CAL_MAX_TOLERANCE 5 static int rtwn_iq_calib_compare_results(uint16_t tx1[2][2], uint16_t rx1[2][2], uint16_t tx2[2][2], uint16_t rx2[2][2], int ntxchains) { int chain, i, tx_ok[2], rx_ok[2]; tx_ok[0] = tx_ok[1] = rx_ok[0] = rx_ok[1] = 0; for (chain = 0; chain < ntxchains; chain++) { for (i = 0; i < 2; i++) { if (tx1[chain][i] == 0xff || tx2[chain][i] == 0xff || rx1[chain][i] == 0xff || rx2[chain][i] == 0xff) continue; tx_ok[chain] = (abs(tx1[chain][i] - tx2[chain][i]) <= RTWN_IQ_CAL_MAX_TOLERANCE); rx_ok[chain] = (abs(rx1[chain][i] - rx2[chain][i]) <= RTWN_IQ_CAL_MAX_TOLERANCE); } } if (ntxchains > 1) return (tx_ok[0] && tx_ok[1] && rx_ok[0] && rx_ok[1]); else return (tx_ok[0] && rx_ok[0]); } #undef RTWN_IQ_CAL_MAX_TOLERANCE static void rtwn_iq_calib_write_results(struct rtwn_softc *sc, uint16_t tx[2], uint16_t rx[2], int chain) { uint32_t reg, val, x; long y, tx_c; if (tx[0] == 0xff || tx[1] == 0xff) return; reg = rtwn_bb_read(sc, R92C_OFDM0_TXIQIMBALANCE(chain)); val = ((reg >> 22) & 0x3ff); x = tx[0]; if (x & 0x0200) x |= 0xfc00; reg = (((x * val) >> 8) & 0x3ff); rtwn_bb_write(sc, R92C_OFDM0_TXIQIMBALANCE(chain), reg); reg = rtwn_bb_read(sc, R92C_OFDM0_ECCATHRESHOLD); if (((x * val) >> 7) & 0x01) reg |= 0x80000000; else reg &= ~0x80000000; rtwn_bb_write(sc, R92C_OFDM0_ECCATHRESHOLD, reg); y = tx[1]; if (y & 0x00000200) y |= 0xfffffc00; tx_c = (y * val) >> 8; reg = rtwn_bb_read(sc, R92C_OFDM0_TXAFE(chain)); reg |= ((((tx_c & 0x3c0) >> 6) << 24) & 0xf0000000); rtwn_bb_write(sc, R92C_OFDM0_TXAFE(chain), reg); reg = rtwn_bb_read(sc, R92C_OFDM0_TXIQIMBALANCE(chain)); reg |= (((tx_c & 0x3f) << 16) & 0x003F0000); rtwn_bb_write(sc, R92C_OFDM0_TXIQIMBALANCE(chain), reg); reg = rtwn_bb_read(sc, R92C_OFDM0_ECCATHRESHOLD); if (((y * val) >> 7) & 0x01) reg |= 0x20000000; else reg &= ~0x20000000; rtwn_bb_write(sc, R92C_OFDM0_ECCATHRESHOLD, reg); if (rx[0] == 0xff || rx[1] == 0xff) return; reg = rtwn_bb_read(sc, R92C_OFDM0_RXIQIMBALANCE(chain)); reg |= (rx[0] & 0x3ff); rtwn_bb_write(sc, R92C_OFDM0_RXIQIMBALANCE(chain), reg); reg |= (((rx[1] & 0x03f) << 8) & 0xFC00); rtwn_bb_write(sc, R92C_OFDM0_RXIQIMBALANCE(chain), reg); if (chain == 0) { reg = rtwn_bb_read(sc, R92C_OFDM0_RXIQEXTANTA); reg |= (((rx[1] & 0xf) >> 6) & 0x000f); rtwn_bb_write(sc, R92C_OFDM0_RXIQEXTANTA, reg); } else { reg = rtwn_bb_read(sc, R92C_OFDM0_AGCRSSITABLE); reg |= ((((rx[1] & 0xf) >> 6) << 12) & 0xf000); rtwn_bb_write(sc, R92C_OFDM0_AGCRSSITABLE, reg); } } #define RTWN_IQ_CAL_NRUN 3 static void rtwn_iq_calib(struct rtwn_softc *sc) { uint16_t tx[RTWN_IQ_CAL_NRUN][2][2], rx[RTWN_IQ_CAL_NRUN][2][2]; int n, valid; valid = 0; for (n = 0; n < RTWN_IQ_CAL_NRUN; n++) { rtwn_iq_calib_run(sc, n, tx[n], rx[n]); if (n == 0) continue; /* Valid results remain stable after consecutive runs. */ valid = rtwn_iq_calib_compare_results(tx[n - 1], rx[n - 1], tx[n], rx[n], sc->ntxchains); if (valid) break; } if (valid) { rtwn_iq_calib_write_results(sc, tx[n][0], rx[n][0], 0); if (sc->ntxchains > 1) rtwn_iq_calib_write_results(sc, tx[n][1], rx[n][1], 1); } } #undef RTWN_IQ_CAL_NRUN static void rtwn_lc_calib(struct rtwn_softc *sc) { uint32_t rf_ac[2]; uint8_t txmode; int i; txmode = rtwn_read_1(sc, R92C_OFDM1_LSTF + 3); if ((txmode & 0x70) != 0) { /* Disable all continuous Tx. */ rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode & ~0x70); /* Set RF mode to standby mode. */ for (i = 0; i < sc->nrxchains; i++) { rf_ac[i] = rtwn_rf_read(sc, i, R92C_RF_AC); rtwn_rf_write(sc, i, R92C_RF_AC, RW(rf_ac[i], R92C_RF_AC_MODE, R92C_RF_AC_MODE_STANDBY)); } } else { /* Block all Tx queues. */ rtwn_write_1(sc, R92C_TXPAUSE, 0xff); } /* Start calibration. */ rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, rtwn_rf_read(sc, 0, R92C_RF_CHNLBW) | R92C_RF_CHNLBW_LCSTART); /* Give calibration the time to complete. */ DELAY(100); /* Restore configuration. */ if ((txmode & 0x70) != 0) { /* Restore Tx mode. */ rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode); /* Restore RF mode. */ for (i = 0; i < sc->nrxchains; i++) rtwn_rf_write(sc, i, R92C_RF_AC, rf_ac[i]); } else { /* Unblock all Tx queues. */ rtwn_write_1(sc, R92C_TXPAUSE, 0x00); } } static void rtwn_temp_calib(struct rtwn_softc *sc) { int temp; if (sc->thcal_state == 0) { /* Start measuring temperature. */ rtwn_rf_write(sc, 0, R92C_RF_T_METER, 0x60); sc->thcal_state = 1; return; } sc->thcal_state = 0; /* Read measured temperature. */ temp = rtwn_rf_read(sc, 0, R92C_RF_T_METER) & 0x1f; if (temp == 0) /* Read failed, skip. */ return; DPRINTFN(2, ("temperature=%d\n", temp)); /* * Redo IQ and LC calibration if temperature changed significantly * since last calibration. */ if (sc->thcal_lctemp == 0) { /* First calibration is performed in rtwn_init(). */ sc->thcal_lctemp = temp; } else if (abs(temp - sc->thcal_lctemp) > 1) { DPRINTF(("IQ/LC calib triggered by temp: %d -> %d\n", sc->thcal_lctemp, temp)); rtwn_iq_calib(sc); rtwn_lc_calib(sc); /* Record temperature of last calibration. */ sc->thcal_lctemp = temp; } } static int rtwn_init(struct rtwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t reg; uint8_t macaddr[IEEE80211_ADDR_LEN]; int i, error; RTWN_LOCK(sc); if (sc->sc_flags & RTWN_RUNNING) { RTWN_UNLOCK(sc); return 0; } sc->sc_flags |= RTWN_RUNNING; /* Init firmware commands ring. */ sc->fwcur = 0; /* Power on adapter. */ error = rtwn_power_on(sc); if (error != 0) { device_printf(sc->sc_dev, "could not power on adapter\n"); goto fail; } /* Initialize DMA. */ error = rtwn_dma_init(sc); if (error != 0) { device_printf(sc->sc_dev, "could not initialize DMA\n"); goto fail; } /* Set info size in Rx descriptors (in 64-bit words). */ rtwn_write_1(sc, R92C_RX_DRVINFO_SZ, 4); /* Disable interrupts. */ rtwn_write_4(sc, R92C_HISR, 0x00000000); rtwn_write_4(sc, R92C_HIMR, 0x00000000); /* Set MAC address. */ IEEE80211_ADDR_COPY(macaddr, vap ? vap->iv_myaddr : ic->ic_macaddr); for (i = 0; i < IEEE80211_ADDR_LEN; i++) rtwn_write_1(sc, R92C_MACID + i, macaddr[i]); /* Set initial network type. */ reg = rtwn_read_4(sc, R92C_CR); reg = RW(reg, R92C_CR_NETTYPE, R92C_CR_NETTYPE_INFRA); rtwn_write_4(sc, R92C_CR, reg); rtwn_rxfilter_init(sc); reg = rtwn_read_4(sc, R92C_RRSR); reg = RW(reg, R92C_RRSR_RATE_BITMAP, R92C_RRSR_RATE_ALL); rtwn_write_4(sc, R92C_RRSR, reg); /* Set short/long retry limits. */ rtwn_write_2(sc, R92C_RL, SM(R92C_RL_SRL, 0x07) | SM(R92C_RL_LRL, 0x07)); /* Initialize EDCA parameters. */ rtwn_edca_init(sc); /* Set data and response automatic rate fallback retry counts. */ rtwn_write_4(sc, R92C_DARFRC + 0, 0x01000000); rtwn_write_4(sc, R92C_DARFRC + 4, 0x07060504); rtwn_write_4(sc, R92C_RARFRC + 0, 0x01000000); rtwn_write_4(sc, R92C_RARFRC + 4, 0x07060504); rtwn_write_2(sc, R92C_FWHW_TXQ_CTRL, 0x1f80); /* Set ACK timeout. */ rtwn_write_1(sc, R92C_ACKTO, 0x40); /* Initialize beacon parameters. */ rtwn_write_2(sc, R92C_TBTT_PROHIBIT, 0x6404); rtwn_write_1(sc, R92C_DRVERLYINT, 0x05); rtwn_write_1(sc, R92C_BCNDMATIM, 0x02); rtwn_write_2(sc, R92C_BCNTCFG, 0x660f); /* Setup AMPDU aggregation. */ rtwn_write_4(sc, R92C_AGGLEN_LMT, 0x99997631); /* MCS7~0 */ rtwn_write_1(sc, R92C_AGGR_BREAK_TIME, 0x16); rtwn_write_1(sc, R92C_BCN_MAX_ERR, 0xff); rtwn_write_1(sc, R92C_BCN_CTRL, R92C_BCN_CTRL_DIS_TSF_UDT0); rtwn_write_4(sc, R92C_PIFS, 0x1c); rtwn_write_4(sc, R92C_MCUTST_1, 0x0); /* Load 8051 microcode. */ error = rtwn_load_firmware(sc); if (error != 0) goto fail; /* Initialize MAC/BB/RF blocks. */ rtwn_mac_init(sc); rtwn_bb_init(sc); rtwn_rf_init(sc); /* Turn CCK and OFDM blocks on. */ reg = rtwn_bb_read(sc, R92C_FPGA0_RFMOD); reg |= R92C_RFMOD_CCK_EN; rtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg); reg = rtwn_bb_read(sc, R92C_FPGA0_RFMOD); reg |= R92C_RFMOD_OFDM_EN; rtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg); /* Clear per-station keys table. */ rtwn_cam_init(sc); /* Enable hardware sequence numbering. */ rtwn_write_1(sc, R92C_HWSEQ_CTRL, 0xff); /* Perform LO and IQ calibrations. */ rtwn_iq_calib(sc); /* Perform LC calibration. */ rtwn_lc_calib(sc); rtwn_pa_bias_init(sc); /* Initialize GPIO setting. */ rtwn_write_1(sc, R92C_GPIO_MUXCFG, rtwn_read_1(sc, R92C_GPIO_MUXCFG) & ~R92C_GPIO_MUXCFG_ENBT); /* Fix for lower temperature. */ rtwn_write_1(sc, 0x15, 0xe9); /* CLear pending interrupts. */ rtwn_write_4(sc, R92C_HISR, 0xffffffff); /* Enable interrupts. */ rtwn_write_4(sc, R92C_HIMR, RTWN_INT_ENABLE); callout_reset(&sc->watchdog_to, hz, rtwn_watchdog, sc); fail: if (error != 0) rtwn_stop_locked(sc); RTWN_UNLOCK(sc); return error; } static void rtwn_stop_locked(struct rtwn_softc *sc) { uint16_t reg; int i; RTWN_LOCK_ASSERT(sc); if (!(sc->sc_flags & RTWN_RUNNING)) return; sc->sc_tx_timer = 0; callout_stop(&sc->watchdog_to); callout_stop(&sc->calib_to); sc->sc_flags &= ~RTWN_RUNNING; /* Disable interrupts. */ rtwn_write_4(sc, R92C_HISR, 0x00000000); rtwn_write_4(sc, R92C_HIMR, 0x00000000); /* Stop hardware. */ rtwn_write_1(sc, R92C_TXPAUSE, 0xff); rtwn_write_1(sc, R92C_RF_CTRL, 0x00); reg = rtwn_read_1(sc, R92C_SYS_FUNC_EN); reg |= R92C_SYS_FUNC_EN_BB_GLB_RST; rtwn_write_1(sc, R92C_SYS_FUNC_EN, reg); reg &= ~R92C_SYS_FUNC_EN_BB_GLB_RST; rtwn_write_1(sc, R92C_SYS_FUNC_EN, reg); reg = rtwn_read_2(sc, R92C_CR); reg &= ~(R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN | R92C_CR_ENSEC); rtwn_write_2(sc, R92C_CR, reg); if (rtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL) rtwn_fw_reset(sc); /* TODO: linux does additional btcoex stuff here */ rtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0x80); /* linux magic number */ rtwn_write_1(sc, R92C_SPS0_CTRL, 0x23); /* ditto */ rtwn_write_1(sc, R92C_AFE_XTAL_CTRL, 0x0e); /* different with btcoex */ rtwn_write_1(sc, R92C_RSV_CTRL, 0x0e); rtwn_write_1(sc, R92C_APS_FSMCO, R92C_APS_FSMCO_PDN_EN); for (i = 0; i < RTWN_NTXQUEUES; i++) rtwn_reset_tx_list(sc, i); rtwn_reset_rx_list(sc); } static void rtwn_stop(struct rtwn_softc *sc) { RTWN_LOCK(sc); rtwn_stop_locked(sc); RTWN_UNLOCK(sc); } static void rtwn_intr(void *arg) { struct rtwn_softc *sc = arg; uint32_t status; int i; RTWN_LOCK(sc); status = rtwn_read_4(sc, R92C_HISR); if (status == 0 || status == 0xffffffff) { RTWN_UNLOCK(sc); return; } /* Disable interrupts. */ rtwn_write_4(sc, R92C_HIMR, 0x00000000); /* Ack interrupts. */ rtwn_write_4(sc, R92C_HISR, status); /* Vendor driver treats RX errors like ROK... */ if (status & (R92C_IMR_ROK | R92C_IMR_RXFOVW | R92C_IMR_RDU)) { bus_dmamap_sync(sc->rx_ring.desc_dmat, sc->rx_ring.desc_map, BUS_DMASYNC_POSTREAD); for (i = 0; i < RTWN_RX_LIST_COUNT; i++) { struct r92c_rx_desc *rx_desc = &sc->rx_ring.desc[i]; struct rtwn_rx_data *rx_data = &sc->rx_ring.rx_data[i]; if (le32toh(rx_desc->rxdw0) & R92C_RXDW0_OWN) continue; rtwn_rx_frame(sc, rx_desc, rx_data, i); } } if (status & R92C_IMR_BDOK) rtwn_tx_done(sc, RTWN_BEACON_QUEUE); if (status & R92C_IMR_HIGHDOK) rtwn_tx_done(sc, RTWN_HIGH_QUEUE); if (status & R92C_IMR_MGNTDOK) rtwn_tx_done(sc, RTWN_MGNT_QUEUE); if (status & R92C_IMR_BKDOK) rtwn_tx_done(sc, RTWN_BK_QUEUE); if (status & R92C_IMR_BEDOK) rtwn_tx_done(sc, RTWN_BE_QUEUE); if (status & R92C_IMR_VIDOK) rtwn_tx_done(sc, RTWN_VI_QUEUE); if (status & R92C_IMR_VODOK) rtwn_tx_done(sc, RTWN_VO_QUEUE); /* Enable interrupts. */ rtwn_write_4(sc, R92C_HIMR, RTWN_INT_ENABLE); RTWN_UNLOCK(sc); } Index: head/sys/dev/rtwn/if_rtwnreg.h =================================================================== --- head/sys/dev/rtwn/if_rtwnreg.h (revision 300753) +++ head/sys/dev/rtwn/if_rtwnreg.h (revision 300754) @@ -1,2118 +1,2120 @@ /* $OpenBSD: if_rtwnreg.h,v 1.3 2015/06/14 08:02:47 stsp Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2015 Stefan Sperling * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #define R92C_MAX_CHAINS 2 /* Maximum number of output pipes is 3. */ #define R92C_MAX_EPOUT 3 #define R92C_MAX_TX_PWR 0x3f #define R92C_PUBQ_NPAGES 176 #define R92C_HPQ_NPAGES 41 #define R92C_LPQ_NPAGES 28 #define R92C_TXPKTBUF_COUNT 256 #define R92C_TX_PAGE_COUNT \ (R92C_PUBQ_NPAGES + R92C_HPQ_NPAGES + R92C_LPQ_NPAGES) #define R92C_TX_PAGE_BOUNDARY (R92C_TX_PAGE_COUNT + 1) #define R92C_H2C_NBOX 4 /* USB Requests. */ #define R92C_REQ_REGS 0x05 /* * MAC registers. */ /* System Configuration. */ #define R92C_SYS_ISO_CTRL 0x000 #define R92C_SYS_FUNC_EN 0x002 #define R92C_APS_FSMCO 0x004 #define R92C_SYS_CLKR 0x008 #define R92C_AFE_MISC 0x010 #define R92C_SPS0_CTRL 0x011 #define R92C_SPS_OCP_CFG 0x018 #define R92C_RSV_CTRL 0x01c #define R92C_RF_CTRL 0x01f #define R92C_LDOA15_CTRL 0x020 #define R92C_LDOV12D_CTRL 0x021 #define R92C_LDOHCI12_CTRL 0x022 #define R92C_LPLDO_CTRL 0x023 #define R92C_AFE_XTAL_CTRL 0x024 #define R92C_AFE_PLL_CTRL 0x028 #define R92C_EFUSE_CTRL 0x030 #define R92C_EFUSE_TEST 0x034 #define R92C_PWR_DATA 0x038 #define R92C_CAL_TIMER 0x03c #define R92C_ACLK_MON 0x03e #define R92C_GPIO_MUXCFG 0x040 #define R92C_GPIO_IO_SEL 0x042 #define R92C_MAC_PINMUX_CFG 0x043 #define R92C_GPIO_PIN_CTRL 0x044 #define R92C_GPIO_INTM 0x048 #define R92C_LEDCFG0 0x04c #define R92C_LEDCFG1 0x04d #define R92C_LEDCFG2 0x04e #define R92C_LEDCFG3 0x04f #define R92C_FSIMR 0x050 #define R92C_FSISR 0x054 #define R92C_HSIMR 0x058 #define R92C_HSISR 0x05c #define R92C_MCUFWDL 0x080 #define R92C_HMEBOX_EXT(idx) (0x088 + (idx) * 2) #define R92C_BIST_SCAN 0x0d0 #define R92C_BIST_RPT 0x0d4 #define R92C_BIST_ROM_RPT 0x0d8 #define R92C_USB_SIE_INTF 0x0e0 #define R92C_PCIE_MIO_INTF 0x0e4 #define R92C_PCIE_MIO_INTD 0x0e8 #define R92C_HPON_FSM 0x0ec #define R92C_SYS_CFG 0x0f0 /* MAC General Configuration. */ #define R92C_CR 0x100 #define R92C_PBP 0x104 #define R92C_TRXDMA_CTRL 0x10c #define R92C_TRXFF_BNDY 0x114 #define R92C_TRXFF_STATUS 0x118 #define R92C_RXFF_PTR 0x11c #define R92C_HIMR 0x120 #define R92C_HISR 0x124 #define R92C_HIMRE 0x128 #define R92C_HISRE 0x12c #define R92C_CPWM 0x12f #define R92C_FWIMR 0x130 #define R92C_FWISR 0x134 #define R92C_PKTBUF_DBG_CTRL 0x140 #define R92C_PKTBUF_DBG_DATA_L 0x144 #define R92C_PKTBUF_DBG_DATA_H 0x148 #define R92C_TC0_CTRL(i) (0x150 + (i) * 4) #define R92C_TCUNIT_BASE 0x164 #define R92C_MBIST_START 0x174 #define R92C_MBIST_DONE 0x178 #define R92C_MBIST_FAIL 0x17c #define R92C_C2HEVT_MSG_NORMAL 0x1a0 #define R92C_C2HEVT_MSG_TEST 0x1b8 #define R92C_C2HEVT_CLEAR 0x1bf #define R92C_MCUTST_1 0x1c0 #define R92C_FMETHR 0x1c8 #define R92C_HMETFR 0x1cc #define R92C_HMEBOX(idx) (0x1d0 + (idx) * 4) #define R92C_LLT_INIT 0x1e0 #define R92C_BB_ACCESS_CTRL 0x1e8 #define R92C_BB_ACCESS_DATA 0x1ec /* Tx DMA Configuration. */ #define R92C_RQPN 0x200 #define R92C_FIFOPAGE 0x204 #define R92C_TDECTRL 0x208 #define R92C_TXDMA_OFFSET_CHK 0x20c #define R92C_TXDMA_STATUS 0x210 #define R92C_RQPN_NPQ 0x214 /* Rx DMA Configuration. */ #define R92C_RXDMA_AGG_PG_TH 0x280 #define R92C_RXPKT_NUM 0x284 #define R92C_RXDMA_STATUS 0x288 #define R92C_PCIE_CTRL_REG 0x300 #define R92C_INT_MIG 0x304 #define R92C_BCNQ_DESA 0x308 #define R92C_HQ_DESA 0x310 #define R92C_MGQ_DESA 0x318 #define R92C_VOQ_DESA 0x320 #define R92C_VIQ_DESA 0x328 #define R92C_BEQ_DESA 0x330 #define R92C_BKQ_DESA 0x338 #define R92C_RX_DESA 0x340 #define R92C_DBI 0x348 #define R92C_MDIO 0x354 #define R92C_DBG_SEL 0x360 #define R92C_PCIE_HRPWM 0x361 #define R92C_PCIE_HCPWM 0x363 #define R92C_UART_CTRL 0x364 #define R92C_UART_TX_DES 0x370 #define R92C_UART_RX_DES 0x378 #define R92C_VOQ_INFORMATION 0x0400 #define R92C_VIQ_INFORMATION 0x0404 #define R92C_BEQ_INFORMATION 0x0408 #define R92C_BKQ_INFORMATION 0x040C #define R92C_MGQ_INFORMATION 0x0410 #define R92C_HGQ_INFORMATION 0x0414 #define R92C_BCNQ_INFORMATION 0x0418 #define R92C_CPU_MGQ_INFORMATION 0x041C /* Protocol Configuration. */ #define R92C_FWHW_TXQ_CTRL 0x420 #define R92C_HWSEQ_CTRL 0x423 #define R92C_TXPKTBUF_BCNQ_BDNY 0x424 #define R92C_TXPKTBUF_MGQ_BDNY 0x425 #define R92C_SPEC_SIFS 0x428 #define R92C_RL 0x42a #define R92C_DARFRC 0x430 #define R92C_RARFRC 0x438 #define R92C_RRSR 0x440 #define R92C_ARFR(i) (0x444 + (i) * 4) #define R92C_AGGLEN_LMT 0x458 #define R92C_AMPDU_MIN_SPACE 0x45c #define R92C_TXPKTBUF_WMAC_LBK_BF_HD 0x45d #define R92C_FAST_EDCA_CTRL 0x460 #define R92C_RD_RESP_PKT_TH 0x463 #define R92C_INIRTS_RATE_SEL 0x480 #define R92C_INIDATA_RATE_SEL(macid) (0x484 + (macid)) /* EDCA Configuration. */ #define R92C_EDCA_VO_PARAM 0x500 #define R92C_EDCA_VI_PARAM 0x504 #define R92C_EDCA_BE_PARAM 0x508 #define R92C_EDCA_BK_PARAM 0x50c #define R92C_BCNTCFG 0x510 #define R92C_PIFS 0x512 #define R92C_RDG_PIFS 0x513 #define R92C_SIFS_CCK 0x514 #define R92C_SIFS_OFDM 0x516 #define R92C_AGGR_BREAK_TIME 0x51a #define R92C_SLOT 0x51b #define R92C_TX_PTCL_CTRL 0x520 #define R92C_TXPAUSE 0x522 #define R92C_DIS_TXREQ_CLR 0x523 #define R92C_RD_CTRL 0x524 #define R92C_TBTT_PROHIBIT 0x540 #define R92C_RD_NAV_NXT 0x544 #define R92C_NAV_PROT_LEN 0x546 #define R92C_BCN_CTRL 0x550 #define R92C_USTIME_TSF 0x551 #define R92C_MBID_NUM 0x552 #define R92C_DUAL_TSF_RST 0x553 #define R92C_BCN_INTERVAL 0x554 #define R92C_DRVERLYINT 0x558 #define R92C_BCNDMATIM 0x559 #define R92C_ATIMWND 0x55a #define R92C_BCN_MAX_ERR 0x55d #define R92C_RXTSF_OFFSET_CCK 0x55e #define R92C_RXTSF_OFFSET_OFDM 0x55f #define R92C_TSFTR 0x560 #define R92C_INIT_TSFTR 0x564 #define R92C_PSTIMER 0x580 #define R92C_TIMER0 0x584 #define R92C_TIMER1 0x588 #define R92C_ACMHWCTRL 0x5c0 #define R92C_ACMRSTCTRL 0x5c1 #define R92C_ACMAVG 0x5c2 #define R92C_VO_ADMTIME 0x5c4 #define R92C_VI_ADMTIME 0x5c6 #define R92C_BE_ADMTIME 0x5c8 #define R92C_EDCA_RANDOM_GEN 0x5cc #define R92C_SCH_TXCMD 0x5d0 /* WMAC Configuration. */ #define R92C_APSD_CTRL 0x600 #define R92C_BWOPMODE 0x603 #define R92C_TCR 0x604 #define R92C_RCR 0x608 #define R92C_RX_DRVINFO_SZ 0x60f #define R92C_MACID 0x610 #define R92C_BSSID 0x618 #define R92C_MAR 0x620 #define R92C_MAC_SPEC_SIFS 0x63a #define R92C_R2T_SIFS 0x63c #define R92C_T2T_SIFS 0x63e #define R92C_ACKTO 0x640 #define R92C_CAMCMD 0x670 #define R92C_CAMWRITE 0x674 #define R92C_CAMREAD 0x678 #define R92C_CAMDBG 0x67c #define R92C_SECCFG 0x680 #define R92C_RXFLTMAP0 0x6a0 #define R92C_RXFLTMAP1 0x6a2 #define R92C_RXFLTMAP2 0x6a4 /* Bits for R92C_SYS_ISO_CTRL. */ #define R92C_SYS_ISO_CTRL_MD2PP 0x0001 #define R92C_SYS_ISO_CTRL_UA2USB 0x0002 #define R92C_SYS_ISO_CTRL_UD2CORE 0x0004 #define R92C_SYS_ISO_CTRL_PA2PCIE 0x0008 #define R92C_SYS_ISO_CTRL_PD2CORE 0x0010 #define R92C_SYS_ISO_CTRL_IP2MAC 0x0020 #define R92C_SYS_ISO_CTRL_DIOP 0x0040 #define R92C_SYS_ISO_CTRL_DIOE 0x0080 #define R92C_SYS_ISO_CTRL_EB2CORE 0x0100 #define R92C_SYS_ISO_CTRL_DIOR 0x0200 #define R92C_SYS_ISO_CTRL_PWC_EV25V 0x4000 #define R92C_SYS_ISO_CTRL_PWC_EV12V 0x8000 /* Bits for R92C_SYS_FUNC_EN. */ #define R92C_SYS_FUNC_EN_BBRSTB 0x0001 #define R92C_SYS_FUNC_EN_BB_GLB_RST 0x0002 #define R92C_SYS_FUNC_EN_USBA 0x0004 #define R92C_SYS_FUNC_EN_UPLL 0x0008 #define R92C_SYS_FUNC_EN_USBD 0x0010 #define R92C_SYS_FUNC_EN_DIO_PCIE 0x0020 #define R92C_SYS_FUNC_EN_PCIEA 0x0040 #define R92C_SYS_FUNC_EN_PPLL 0x0080 #define R92C_SYS_FUNC_EN_PCIED 0x0100 #define R92C_SYS_FUNC_EN_DIOE 0x0200 #define R92C_SYS_FUNC_EN_CPUEN 0x0400 #define R92C_SYS_FUNC_EN_DCORE 0x0800 #define R92C_SYS_FUNC_EN_ELDR 0x1000 #define R92C_SYS_FUNC_EN_DIO_RF 0x2000 #define R92C_SYS_FUNC_EN_HWPDN 0x4000 #define R92C_SYS_FUNC_EN_MREGEN 0x8000 /* Bits for R92C_APS_FSMCO. */ #define R92C_APS_FSMCO_PFM_LDALL 0x00000001 #define R92C_APS_FSMCO_PFM_ALDN 0x00000002 #define R92C_APS_FSMCO_PFM_LDKP 0x00000004 #define R92C_APS_FSMCO_PFM_WOWL 0x00000008 #define R92C_APS_FSMCO_PDN_EN 0x00000010 #define R92C_APS_FSMCO_PDN_PL 0x00000020 #define R92C_APS_FSMCO_APFM_ONMAC 0x00000100 #define R92C_APS_FSMCO_APFM_OFF 0x00000200 #define R92C_APS_FSMCO_APFM_RSM 0x00000400 #define R92C_APS_FSMCO_AFSM_HSUS 0x00000800 #define R92C_APS_FSMCO_AFSM_PCIE 0x00001000 #define R92C_APS_FSMCO_APDM_MAC 0x00002000 #define R92C_APS_FSMCO_APDM_HOST 0x00004000 #define R92C_APS_FSMCO_APDM_HPDN 0x00008000 #define R92C_APS_FSMCO_RDY_MACON 0x00010000 #define R92C_APS_FSMCO_SUS_HOST 0x00020000 #define R92C_APS_FSMCO_ROP_ALD 0x00100000 #define R92C_APS_FSMCO_ROP_PWR 0x00200000 #define R92C_APS_FSMCO_ROP_SPS 0x00400000 #define R92C_APS_FSMCO_SOP_MRST 0x02000000 #define R92C_APS_FSMCO_SOP_FUSE 0x04000000 #define R92C_APS_FSMCO_SOP_ABG 0x08000000 #define R92C_APS_FSMCO_SOP_AMB 0x10000000 #define R92C_APS_FSMCO_SOP_RCK 0x20000000 #define R92C_APS_FSMCO_SOP_A8M 0x40000000 #define R92C_APS_FSMCO_XOP_BTCK 0x80000000 /* Bits for R92C_SYS_CLKR. */ #define R92C_SYS_CLKR_ANAD16V_EN 0x00000001 #define R92C_SYS_CLKR_ANA8M 0x00000002 #define R92C_SYS_CLKR_MACSLP 0x00000010 #define R92C_SYS_CLKR_LOADER_EN 0x00000020 #define R92C_SYS_CLKR_80M_SSC_DIS 0x00000080 #define R92C_SYS_CLKR_80M_SSC_EN_HO 0x00000100 #define R92C_SYS_CLKR_PHY_SSC_RSTB 0x00000200 #define R92C_SYS_CLKR_SEC_EN 0x00000400 #define R92C_SYS_CLKR_MAC_EN 0x00000800 #define R92C_SYS_CLKR_SYS_EN 0x00001000 #define R92C_SYS_CLKR_RING_EN 0x00002000 /* Bits for R92C_RF_CTRL. */ #define R92C_RF_CTRL_EN 0x01 #define R92C_RF_CTRL_RSTB 0x02 #define R92C_RF_CTRL_SDMRSTB 0x04 /* Bits for R92C_LDOV12D_CTRL. */ #define R92C_LDOV12D_CTRL_LDV12_EN 0x01 /* Bits for R92C_EFUSE_CTRL. */ #define R92C_EFUSE_CTRL_DATA_M 0x000000ff #define R92C_EFUSE_CTRL_DATA_S 0 #define R92C_EFUSE_CTRL_ADDR_M 0x0003ff00 #define R92C_EFUSE_CTRL_ADDR_S 8 #define R92C_EFUSE_CTRL_VALID 0x80000000 /* Bits for R92C_GPIO_MUXCFG. */ #define R92C_GPIO_MUXCFG_RFKILL 0x0008 #define R92C_GPIO_MUXCFG_ENBT 0x0020 /* Bits for R92C_GPIO_IO_SEL. */ #define R92C_GPIO_IO_SEL_RFKILL 0x0008 /* Bits for R92C_LEDCFG0. */ #define R92C_LEDCFG0_DIS 0x08 /* Bits for R92C_LEDCFG2. */ #define R92C_LEDCFG2_EN 0x60 #define R92C_LEDCFG2_DIS 0x68 /* Bits for R92C_MCUFWDL. */ #define R92C_MCUFWDL_EN 0x00000001 #define R92C_MCUFWDL_RDY 0x00000002 #define R92C_MCUFWDL_CHKSUM_RPT 0x00000004 #define R92C_MCUFWDL_MACINI_RDY 0x00000008 #define R92C_MCUFWDL_BBINI_RDY 0x00000010 #define R92C_MCUFWDL_RFINI_RDY 0x00000020 #define R92C_MCUFWDL_WINTINI_RDY 0x00000040 #define R92C_MCUFWDL_RAM_DL_SEL 0x00000080 /* 1: RAM, 0: ROM */ #define R92C_MCUFWDL_PAGE_M 0x00070000 #define R92C_MCUFWDL_PAGE_S 16 #define R92C_MCUFWDL_CPRST 0x00800000 /* Bits for R92C_HPON_FSM. */ #define R92C_HPON_FSM_CHIP_BONDING_ID_S 22 #define R92C_HPON_FSM_CHIP_BONDING_ID_M 0x00c00000 #define R92C_HPON_FSM_CHIP_BONDING_ID_92C_1T2R 1 /* Bits for R92C_SYS_CFG. */ #define R92C_SYS_CFG_XCLK_VLD 0x00000001 #define R92C_SYS_CFG_ACLK_VLD 0x00000002 #define R92C_SYS_CFG_UCLK_VLD 0x00000004 #define R92C_SYS_CFG_PCLK_VLD 0x00000008 #define R92C_SYS_CFG_PCIRSTB 0x00000010 #define R92C_SYS_CFG_V15_VLD 0x00000020 #define R92C_SYS_CFG_TRP_B15V_EN 0x00000080 #define R92C_SYS_CFG_SIC_IDLE 0x00000100 #define R92C_SYS_CFG_BD_MAC2 0x00000200 #define R92C_SYS_CFG_BD_MAC1 0x00000400 #define R92C_SYS_CFG_IC_MACPHY_MODE 0x00000800 #define R92C_SYS_CFG_CHIP_VER_RTL_M 0x0000f000 #define R92C_SYS_CFG_CHIP_VER_RTL_S 12 #define R92C_SYS_CFG_BT_FUNC 0x00010000 #define R92C_SYS_CFG_VENDOR_UMC 0x00080000 #define R92C_SYS_CFG_PAD_HWPD_IDN 0x00400000 #define R92C_SYS_CFG_TRP_VAUX_EN 0x00800000 #define R92C_SYS_CFG_TRP_BT_EN 0x01000000 #define R92C_SYS_CFG_BD_PKG_SEL 0x02000000 #define R92C_SYS_CFG_BD_HCI_SEL 0x04000000 #define R92C_SYS_CFG_TYPE_92C 0x08000000 /* Bits for R92C_CR. */ #define R92C_CR_HCI_TXDMA_EN 0x00000001 #define R92C_CR_HCI_RXDMA_EN 0x00000002 #define R92C_CR_TXDMA_EN 0x00000004 #define R92C_CR_RXDMA_EN 0x00000008 #define R92C_CR_PROTOCOL_EN 0x00000010 #define R92C_CR_SCHEDULE_EN 0x00000020 #define R92C_CR_MACTXEN 0x00000040 #define R92C_CR_MACRXEN 0x00000080 #define R92C_CR_ENSEC 0x00000200 #define R92C_CR_NETTYPE_S 16 #define R92C_CR_NETTYPE_M 0x00030000 #define R92C_CR_NETTYPE_NOLINK 0 #define R92C_CR_NETTYPE_ADHOC 1 #define R92C_CR_NETTYPE_INFRA 2 #define R92C_CR_NETTYPE_AP 3 /* Bits for R92C_PBP. */ #define R92C_PBP_PSRX_M 0x0f #define R92C_PBP_PSRX_S 0 #define R92C_PBP_PSTX_M 0xf0 #define R92C_PBP_PSTX_S 4 #define R92C_PBP_64 0 #define R92C_PBP_128 1 #define R92C_PBP_256 2 #define R92C_PBP_512 3 #define R92C_PBP_1024 4 /* Bits for R92C_TRXDMA_CTRL. */ #define R92C_TRXDMA_CTRL_RXDMA_AGG_EN 0x0004 #define R92C_TRXDMA_CTRL_TXDMA_VOQ_MAP_M 0x0030 #define R92C_TRXDMA_CTRL_TXDMA_VOQ_MAP_S 4 #define R92C_TRXDMA_CTRL_TXDMA_VIQ_MAP_M 0x00c0 #define R92C_TRXDMA_CTRL_TXDMA_VIQ_MAP_S 6 #define R92C_TRXDMA_CTRL_TXDMA_BEQ_MAP_M 0x0300 #define R92C_TRXDMA_CTRL_TXDMA_BEQ_MAP_S 8 #define R92C_TRXDMA_CTRL_TXDMA_BKQ_MAP_M 0x0c00 #define R92C_TRXDMA_CTRL_TXDMA_BKQ_MAP_S 10 #define R92C_TRXDMA_CTRL_TXDMA_MGQ_MAP_M 0x3000 #define R92C_TRXDMA_CTRL_TXDMA_MGQ_MAP_S 12 #define R92C_TRXDMA_CTRL_TXDMA_HIQ_MAP_M 0xc000 #define R92C_TRXDMA_CTRL_TXDMA_HIQ_MAP_S 14 #define R92C_TRXDMA_CTRL_QUEUE_LOW 1 #define R92C_TRXDMA_CTRL_QUEUE_NORMAL 2 #define R92C_TRXDMA_CTRL_QUEUE_HIGH 3 #define R92C_TRXDMA_CTRL_QMAP_M 0xfff0 #define R92C_TRXDMA_CTRL_QMAP_S 4 /* Shortcuts. */ #define R92C_TRXDMA_CTRL_QMAP_3EP 0xf5b0 #define R92C_TRXDMA_CTRL_QMAP_HQ_LQ 0xf5f0 #define R92C_TRXDMA_CTRL_QMAP_HQ_NQ 0xfaf0 #define R92C_TRXDMA_CTRL_QMAP_LQ 0x5550 #define R92C_TRXDMA_CTRL_QMAP_NQ 0xaaa0 #define R92C_TRXDMA_CTRL_QMAP_HQ 0xfff0 /* Bits for R92C_LLT_INIT. */ #define R92C_LLT_INIT_DATA_M 0x000000ff #define R92C_LLT_INIT_DATA_S 0 #define R92C_LLT_INIT_ADDR_M 0x0000ff00 #define R92C_LLT_INIT_ADDR_S 8 #define R92C_LLT_INIT_OP_M 0xc0000000 #define R92C_LLT_INIT_OP_S 30 #define R92C_LLT_INIT_OP_NO_ACTIVE 0 #define R92C_LLT_INIT_OP_WRITE 1 /* Bits for R92C_RQPN. */ #define R92C_RQPN_HPQ_M 0x000000ff #define R92C_RQPN_HPQ_S 0 #define R92C_RQPN_LPQ_M 0x0000ff00 #define R92C_RQPN_LPQ_S 8 #define R92C_RQPN_PUBQ_M 0x00ff0000 #define R92C_RQPN_PUBQ_S 16 #define R92C_RQPN_LD 0x80000000 /* Bits for R92C_TDECTRL. */ #define R92C_TDECTRL_BLK_DESC_NUM_M 0x0000000f #define R92C_TDECTRL_BLK_DESC_NUM_S 4 /* Bits for R92C_FWHW_TXQ_CTRL. */ #define R92C_FWHW_TXQ_CTRL_AMPDU_RTY_NEW 0x80 /* Bits for R92C_SPEC_SIFS. */ #define R92C_SPEC_SIFS_CCK_M 0x00ff #define R92C_SPEC_SIFS_CCK_S 0 #define R92C_SPEC_SIFS_OFDM_M 0xff00 #define R92C_SPEC_SIFS_OFDM_S 8 /* Bits for R92C_RL. */ #define R92C_RL_LRL_M 0x003f #define R92C_RL_LRL_S 0 #define R92C_RL_SRL_M 0x3f00 #define R92C_RL_SRL_S 8 /* Bits for R92C_RRSR. */ #define R92C_RRSR_RATE_BITMAP_M 0x000fffff #define R92C_RRSR_RATE_BITMAP_S 0 #define R92C_RRSR_RATE_CCK_ONLY_1M 0xffff1 #define R92C_RRSR_RATE_ALL 0xfffff #define R92C_RRSR_RSC_LOWSUBCHNL 0x00200000 #define R92C_RRSR_RSC_UPSUBCHNL 0x00400000 #define R92C_RRSR_SHORT 0x00800000 /* Bits for R92C_EDCA_XX_PARAM. */ #define R92C_EDCA_PARAM_AIFS_M 0x000000ff #define R92C_EDCA_PARAM_AIFS_S 0 #define R92C_EDCA_PARAM_ECWMIN_M 0x00000f00 #define R92C_EDCA_PARAM_ECWMIN_S 8 #define R92C_EDCA_PARAM_ECWMAX_M 0x0000f000 #define R92C_EDCA_PARAM_ECWMAX_S 12 #define R92C_EDCA_PARAM_TXOP_M 0xffff0000 #define R92C_EDCA_PARAM_TXOP_S 16 /* Bits for R92C_TXPAUSE. */ #define R92C_TXPAUSE_AC_VO 0x01 #define R92C_TXPAUSE_AC_VI 0x02 #define R92C_TXPAUSE_AC_BE 0x04 #define R92C_TXPAUSE_AC_BK 0x08 /* Bits for R92C_BCN_CTRL. */ #define R92C_BCN_CTRL_EN_MBSSID 0x02 #define R92C_BCN_CTRL_TXBCN_RPT 0x04 #define R92C_BCN_CTRL_EN_BCN 0x08 #define R92C_BCN_CTRL_DIS_TSF_UDT0 0x10 /* Bits for R92C_APSD_CTRL. */ #define R92C_APSD_CTRL_OFF 0x40 #define R92C_APSD_CTRL_OFF_STATUS 0x80 /* Bits for R92C_BWOPMODE. */ #define R92C_BWOPMODE_11J 0x01 #define R92C_BWOPMODE_5G 0x02 #define R92C_BWOPMODE_20MHZ 0x04 /* Bits for R92C_TCR. */ #define R92C_TCR_TSFRST 0x00000001 #define R92C_TCR_DIS_GCLK 0x00000002 #define R92C_TCR_PAD_SEL 0x00000004 #define R92C_TCR_PWR_ST 0x00000040 #define R92C_TCR_PWRBIT_OW_EN 0x00000080 #define R92C_TCR_ACRC 0x00000100 #define R92C_TCR_CFENDFORM 0x00000200 #define R92C_TCR_ICV 0x00000400 /* Bits for R92C_RCR. */ #define R92C_RCR_AAP 0x00000001 #define R92C_RCR_APM 0x00000002 #define R92C_RCR_AM 0x00000004 #define R92C_RCR_AB 0x00000008 #define R92C_RCR_ADD3 0x00000010 #define R92C_RCR_APWRMGT 0x00000020 #define R92C_RCR_CBSSID_DATA 0x00000040 #define R92C_RCR_CBSSID_BCN 0x00000080 #define R92C_RCR_ACRC32 0x00000100 #define R92C_RCR_AICV 0x00000200 #define R92C_RCR_ADF 0x00000800 #define R92C_RCR_ACF 0x00001000 #define R92C_RCR_AMF 0x00002000 #define R92C_RCR_HTC_LOC_CTRL 0x00004000 #define R92C_RCR_MFBEN 0x00400000 #define R92C_RCR_LSIGEN 0x00800000 #define R92C_RCR_ENMBID 0x01000000 #define R92C_RCR_APP_BA_SSN 0x08000000 #define R92C_RCR_APP_PHYSTS 0x10000000 #define R92C_RCR_APP_ICV 0x20000000 #define R92C_RCR_APP_MIC 0x40000000 #define R92C_RCR_APPFCS 0x80000000 /* Bits for R92C_CAMCMD. */ #define R92C_CAMCMD_ADDR_M 0x0000ffff #define R92C_CAMCMD_ADDR_S 0 #define R92C_CAMCMD_WRITE 0x00010000 #define R92C_CAMCMD_CLR 0x40000000 #define R92C_CAMCMD_POLLING 0x80000000 /* IMR */ /*Beacon DMA interrupt 6 */ #define R92C_IMR_BCNDMAINT6 0x80000000 /*Beacon DMA interrupt 5 */ #define R92C_IMR_BCNDMAINT5 0x40000000 /*Beacon DMA interrupt 4 */ #define R92C_IMR_BCNDMAINT4 0x20000000 /*Beacon DMA interrupt 3 */ #define R92C_IMR_BCNDMAINT3 0x10000000 /*Beacon DMA interrupt 2 */ #define R92C_IMR_BCNDMAINT2 0x08000000 /*Beacon DMA interrupt 1 */ #define R92C_IMR_BCNDMAINT1 0x04000000 /*Beacon Queue DMA OK interrupt 8 */ #define R92C_IMR_BCNDOK8 0x02000000 /*Beacon Queue DMA OK interrupt 7 */ #define R92C_IMR_BCNDOK7 0x01000000 /*Beacon Queue DMA OK interrupt 6 */ #define R92C_IMR_BCNDOK6 0x00800000 /*Beacon Queue DMA OK interrupt 5 */ #define R92C_IMR_BCNDOK5 0x00400000 /*Beacon Queue DMA OK interrupt 4 */ #define R92C_IMR_BCNDOK4 0x00200000 /*Beacon Queue DMA OK interrupt 3 */ #define R92C_IMR_BCNDOK3 0x00100000 /*Beacon Queue DMA OK interrupt 2 */ #define R92C_IMR_BCNDOK2 0x00080000 /*Beacon Queue DMA OK interrupt 1 */ #define R92C_IMR_BCNDOK1 0x00040000 /*Timeout interrupt 2 */ #define R92C_IMR_TIMEOUT2 0x00020000 /*Timeout interrupt 1 */ #define R92C_IMR_TIMEOUT1 0x00010000 /*Transmit FIFO Overflow */ #define R92C_IMR_TXFOVW 0x00008000 /*Power save time out interrupt */ #define R92C_IMR_PSTIMEOUT 0x00004000 /*Beacon DMA interrupt 0 */ #define R92C_IMR_BCNINT 0x00002000 /*Receive FIFO Overflow */ #define R92C_IMR_RXFOVW 0x00001000 /*Receive Descriptor Unavailable */ #define R92C_IMR_RDU 0x00000800 /*For 92C,ATIM Window End interrupt */ #define R92C_IMR_ATIMEND 0x00000400 /*Beacon Queue DMA OK interrupt */ #define R92C_IMR_BDOK 0x00000200 /*High Queue DMA OK interrupt */ #define R92C_IMR_HIGHDOK 0x00000100 /*Transmit Beacon OK interrupt */ #define R92C_IMR_TBDOK 0x00000080 /*Management Queue DMA OK interrupt */ #define R92C_IMR_MGNTDOK 0x00000040 /*For 92C,Transmit Beacon Error interrupt */ #define R92C_IMR_TBDER 0x00000020 /*AC_BK DMA OK interrupt */ #define R92C_IMR_BKDOK 0x00000010 /*AC_BE DMA OK interrupt */ #define R92C_IMR_BEDOK 0x00000008 /*AC_VI DMA OK interrupt */ #define R92C_IMR_VIDOK 0x00000004 /*AC_VO DMA interrupt */ #define R92C_IMR_VODOK 0x00000002 /*Receive DMA OK interrupt */ #define R92C_IMR_ROK 0x00000001 #define R92C_IBSS_INT_MASK (R92C_IMR_BCNINT | R92C_IMR_TBDOK | R92C_IMR_TBDER) /* * Baseband registers. */ #define R92C_FPGA0_RFMOD 0x800 #define R92C_FPGA0_TXINFO 0x804 #define R92C_HSSI_PARAM1(chain) (0x820 + (chain) * 8) #define R92C_HSSI_PARAM2(chain) (0x824 + (chain) * 8) #define R92C_TXAGC_RATE18_06(i) (((i) == 0) ? 0xe00 : 0x830) #define R92C_TXAGC_RATE54_24(i) (((i) == 0) ? 0xe04 : 0x834) #define R92C_TXAGC_A_CCK1_MCS32 0xe08 #define R92C_TXAGC_B_CCK1_55_MCS32 0x838 #define R92C_TXAGC_B_CCK11_A_CCK2_11 0x86c #define R92C_TXAGC_MCS03_MCS00(i) (((i) == 0) ? 0xe10 : 0x83c) #define R92C_TXAGC_MCS07_MCS04(i) (((i) == 0) ? 0xe14 : 0x848) #define R92C_TXAGC_MCS11_MCS08(i) (((i) == 0) ? 0xe18 : 0x84c) #define R92C_TXAGC_MCS15_MCS12(i) (((i) == 0) ? 0xe1c : 0x868) #define R92C_LSSI_PARAM(chain) (0x840 + (chain) * 4) #define R92C_FPGA0_RFIFACEOE(chain) (0x860 + (chain) * 4) #define R92C_FPGA0_RFIFACESW(idx) (0x870 + (idx) * 4) #define R92C_FPGA0_RFPARAM(idx) (0x878 + (idx) * 4) #define R92C_FPGA0_ANAPARAM2 0x884 #define R92C_LSSI_READBACK(chain) (0x8a0 + (chain) * 4) #define R92C_HSPI_READBACK(chain) (0x8b8 + (chain) * 4) #define R92C_FPGA1_RFMOD 0x900 #define R92C_FPGA1_TXINFO 0x90c #define R92C_CCK0_SYSTEM 0xa00 #define R92C_CCK0_AFESETTING 0xa04 #define R92C_OFDM0_TRXPATHENA 0xc04 #define R92C_OFDM0_TRMUXPAR 0xc08 #define R92C_OFDM0_RXIQIMBALANCE(chain) (0xc14 + (chain) * 8) #define R92C_OFDM0_ECCATHRESHOLD 0xc4c #define R92C_OFDM0_AGCCORE1(chain) (0xc50 + (chain) * 8) #define R92C_OFDM0_AGCPARAM1 0xc70 #define R92C_OFDM0_AGCRSSITABLE 0xc78 #define R92C_OFDM0_TXIQIMBALANCE(chain) (0xc80 + (chain) * 8) #define R92C_OFDM0_TXAFE(chain) (0xc94 + (chain) * 8) #define R92C_OFDM0_RXIQEXTANTA 0xca0 #define R92C_OFDM1_LSTF 0xd00 /* Bits for R92C_FPGA[01]_RFMOD. */ #define R92C_RFMOD_40MHZ 0x00000001 #define R92C_RFMOD_JAPAN 0x00000002 #define R92C_RFMOD_CCK_TXSC 0x00000030 #define R92C_RFMOD_CCK_EN 0x01000000 #define R92C_RFMOD_OFDM_EN 0x02000000 /* Bits for R92C_HSSI_PARAM1(i). */ #define R92C_HSSI_PARAM1_PI 0x00000100 /* Bits for R92C_HSSI_PARAM2(i). */ #define R92C_HSSI_PARAM2_CCK_HIPWR 0x00000200 #define R92C_HSSI_PARAM2_ADDR_LENGTH 0x00000400 #define R92C_HSSI_PARAM2_DATA_LENGTH 0x00000800 #define R92C_HSSI_PARAM2_READ_ADDR_M 0x7f800000 #define R92C_HSSI_PARAM2_READ_ADDR_S 23 #define R92C_HSSI_PARAM2_READ_EDGE 0x80000000 /* Bits for R92C_TXAGC_A_CCK1_MCS32. */ #define R92C_TXAGC_A_CCK1_M 0x0000ff00 #define R92C_TXAGC_A_CCK1_S 8 /* Bits for R92C_TXAGC_B_CCK11_A_CCK2_11. */ #define R92C_TXAGC_B_CCK11_M 0x000000ff #define R92C_TXAGC_B_CCK11_S 0 #define R92C_TXAGC_A_CCK2_M 0x0000ff00 #define R92C_TXAGC_A_CCK2_S 8 #define R92C_TXAGC_A_CCK55_M 0x00ff0000 #define R92C_TXAGC_A_CCK55_S 16 #define R92C_TXAGC_A_CCK11_M 0xff000000 #define R92C_TXAGC_A_CCK11_S 24 /* Bits for R92C_TXAGC_B_CCK1_55_MCS32. */ #define R92C_TXAGC_B_CCK1_M 0x0000ff00 #define R92C_TXAGC_B_CCK1_S 8 #define R92C_TXAGC_B_CCK2_M 0x00ff0000 #define R92C_TXAGC_B_CCK2_S 16 #define R92C_TXAGC_B_CCK55_M 0xff000000 #define R92C_TXAGC_B_CCK55_S 24 /* Bits for R92C_TXAGC_RATE18_06(x). */ #define R92C_TXAGC_RATE06_M 0x000000ff #define R92C_TXAGC_RATE06_S 0 #define R92C_TXAGC_RATE09_M 0x0000ff00 #define R92C_TXAGC_RATE09_S 8 #define R92C_TXAGC_RATE12_M 0x00ff0000 #define R92C_TXAGC_RATE12_S 16 #define R92C_TXAGC_RATE18_M 0xff000000 #define R92C_TXAGC_RATE18_S 24 /* Bits for R92C_TXAGC_RATE54_24(x). */ #define R92C_TXAGC_RATE24_M 0x000000ff #define R92C_TXAGC_RATE24_S 0 #define R92C_TXAGC_RATE36_M 0x0000ff00 #define R92C_TXAGC_RATE36_S 8 #define R92C_TXAGC_RATE48_M 0x00ff0000 #define R92C_TXAGC_RATE48_S 16 #define R92C_TXAGC_RATE54_M 0xff000000 #define R92C_TXAGC_RATE54_S 24 /* Bits for R92C_TXAGC_MCS03_MCS00(x). */ #define R92C_TXAGC_MCS00_M 0x000000ff #define R92C_TXAGC_MCS00_S 0 #define R92C_TXAGC_MCS01_M 0x0000ff00 #define R92C_TXAGC_MCS01_S 8 #define R92C_TXAGC_MCS02_M 0x00ff0000 #define R92C_TXAGC_MCS02_S 16 #define R92C_TXAGC_MCS03_M 0xff000000 #define R92C_TXAGC_MCS03_S 24 /* Bits for R92C_TXAGC_MCS07_MCS04(x). */ #define R92C_TXAGC_MCS04_M 0x000000ff #define R92C_TXAGC_MCS04_S 0 #define R92C_TXAGC_MCS05_M 0x0000ff00 #define R92C_TXAGC_MCS05_S 8 #define R92C_TXAGC_MCS06_M 0x00ff0000 #define R92C_TXAGC_MCS06_S 16 #define R92C_TXAGC_MCS07_M 0xff000000 #define R92C_TXAGC_MCS07_S 24 /* Bits for R92C_TXAGC_MCS11_MCS08(x). */ #define R92C_TXAGC_MCS08_M 0x000000ff #define R92C_TXAGC_MCS08_S 0 #define R92C_TXAGC_MCS09_M 0x0000ff00 #define R92C_TXAGC_MCS09_S 8 #define R92C_TXAGC_MCS10_M 0x00ff0000 #define R92C_TXAGC_MCS10_S 16 #define R92C_TXAGC_MCS11_M 0xff000000 #define R92C_TXAGC_MCS11_S 24 /* Bits for R92C_TXAGC_MCS15_MCS12(x). */ #define R92C_TXAGC_MCS12_M 0x000000ff #define R92C_TXAGC_MCS12_S 0 #define R92C_TXAGC_MCS13_M 0x0000ff00 #define R92C_TXAGC_MCS13_S 8 #define R92C_TXAGC_MCS14_M 0x00ff0000 #define R92C_TXAGC_MCS14_S 16 #define R92C_TXAGC_MCS15_M 0xff000000 #define R92C_TXAGC_MCS15_S 24 /* Bits for R92C_LSSI_PARAM(i). */ #define R92C_LSSI_PARAM_DATA_M 0x000fffff #define R92C_LSSI_PARAM_DATA_S 0 #define R92C_LSSI_PARAM_ADDR_M 0x03f00000 #define R92C_LSSI_PARAM_ADDR_S 20 /* Bits for R92C_FPGA0_ANAPARAM2. */ #define R92C_FPGA0_ANAPARAM2_CBW20 0x00000400 /* Bits for R92C_LSSI_READBACK(i). */ #define R92C_LSSI_READBACK_DATA_M 0x000fffff #define R92C_LSSI_READBACK_DATA_S 0 /* Bits for R92C_OFDM0_AGCCORE1(i). */ #define R92C_OFDM0_AGCCORE1_GAIN_M 0x0000007f #define R92C_OFDM0_AGCCORE1_GAIN_S 0 /* * USB registers. */ #define R92C_USB_INFO 0xfe17 #define R92C_USB_SPECIAL_OPTION 0xfe55 #define R92C_USB_HCPWM 0xfe57 #define R92C_USB_HRPWM 0xfe58 #define R92C_USB_DMA_AGG_TO 0xfe5b #define R92C_USB_AGG_TO 0xfe5c #define R92C_USB_AGG_TH 0xfe5d #define R92C_USB_VID 0xfe60 #define R92C_USB_PID 0xfe62 #define R92C_USB_OPTIONAL 0xfe64 #define R92C_USB_EP 0xfe65 #define R92C_USB_PHY 0xfe68 #define R92C_USB_MAC_ADDR 0xfe70 #define R92C_USB_STRING 0xfe80 /* Bits for R92C_USB_SPECIAL_OPTION. */ #define R92C_USB_SPECIAL_OPTION_AGG_EN 0x08 /* Bits for R92C_USB_EP. */ #define R92C_USB_EP_HQ_M 0x000f #define R92C_USB_EP_HQ_S 0 #define R92C_USB_EP_NQ_M 0x00f0 #define R92C_USB_EP_NQ_S 4 #define R92C_USB_EP_LQ_M 0x0f00 #define R92C_USB_EP_LQ_S 8 /* * Firmware base address. */ #define R92C_FW_START_ADDR 0x1000 #define R92C_FW_PAGE_SIZE 4096 /* * RF (6052) registers. */ #define R92C_RF_AC 0x00 #define R92C_RF_IQADJ_G(i) (0x01 + (i)) #define R92C_RF_POW_TRSW 0x05 #define R92C_RF_GAIN_RX 0x06 #define R92C_RF_GAIN_TX 0x07 #define R92C_RF_TXM_IDAC 0x08 #define R92C_RF_BS_IQGEN 0x0f #define R92C_RF_MODE1 0x10 #define R92C_RF_MODE2 0x11 #define R92C_RF_RX_AGC_HP 0x12 #define R92C_RF_TX_AGC 0x13 #define R92C_RF_BIAS 0x14 #define R92C_RF_IPA 0x15 #define R92C_RF_POW_ABILITY 0x17 #define R92C_RF_CHNLBW 0x18 #define R92C_RF_RX_G1 0x1a #define R92C_RF_RX_G2 0x1b #define R92C_RF_RX_BB2 0x1c #define R92C_RF_RX_BB1 0x1d #define R92C_RF_RCK1 0x1e #define R92C_RF_RCK2 0x1f #define R92C_RF_TX_G(i) (0x20 + (i)) #define R92C_RF_TX_BB1 0x23 #define R92C_RF_T_METER 0x24 #define R92C_RF_SYN_G(i) (0x25 + (i)) #define R92C_RF_RCK_OS 0x30 #define R92C_RF_TXPA_G(i) (0x31 + (i)) /* Bits for R92C_RF_AC. */ #define R92C_RF_AC_MODE_M 0x70000 #define R92C_RF_AC_MODE_S 16 #define R92C_RF_AC_MODE_STANDBY 1 /* Bits for R92C_RF_CHNLBW. */ #define R92C_RF_CHNLBW_CHNL_M 0x003ff #define R92C_RF_CHNLBW_CHNL_S 0 #define R92C_RF_CHNLBW_BW20 0x00400 #define R92C_RF_CHNLBW_LCSTART 0x08000 /* * CAM entries. */ #define R92C_CAM_ENTRY_COUNT 32 #define R92C_CAM_CTL0(entry) ((entry) * 8 + 0) #define R92C_CAM_CTL1(entry) ((entry) * 8 + 1) #define R92C_CAM_KEY(entry, i) ((entry) * 8 + 2 + (i)) /* Bits for R92C_CAM_CTL0(i). */ #define R92C_CAM_KEYID_M 0x00000003 #define R92C_CAM_KEYID_S 0 #define R92C_CAM_ALGO_M 0x0000001c #define R92C_CAM_ALGO_S 2 #define R92C_CAM_ALGO_NONE 0 #define R92C_CAM_ALGO_WEP40 1 #define R92C_CAM_ALGO_TKIP 2 #define R92C_CAM_ALGO_AES 4 #define R92C_CAM_ALGO_WEP104 5 #define R92C_CAM_VALID 0x00008000 #define R92C_CAM_MACLO_M 0xffff0000 #define R92C_CAM_MACLO_S 16 /* Rate adaptation modes. */ #define R92C_RAID_11GN 1 #define R92C_RAID_11N 3 #define R92C_RAID_11BG 4 #define R92C_RAID_11G 5 /* "pure" 11g */ #define R92C_RAID_11B 6 /* * Macros to access subfields in registers. */ /* Mask and Shift (getter). */ #define MS(val, field) \ (((val) & field##_M) >> field##_S) /* Shift and Mask (setter). */ #define SM(field, val) \ (((val) << field##_S) & field##_M) /* Rewrite. */ #define RW(var, field, val) \ (((var) & ~field##_M) | SM(field, val)) /* * Firmware image header. */ struct r92c_fw_hdr { /* QWORD0 */ uint16_t signature; uint8_t category; uint8_t function; uint16_t version; uint16_t subversion; /* QWORD1 */ uint8_t month; uint8_t date; uint8_t hour; uint8_t minute; uint16_t ramcodesize; uint16_t reserved2; /* QWORD2 */ uint32_t svnidx; uint32_t reserved3; /* QWORD3 */ uint32_t reserved4; uint32_t reserved5; } __packed; /* * Host to firmware commands. */ struct r92c_fw_cmd { uint8_t id; #define R92C_CMD_AP_OFFLOAD 0 #define R92C_CMD_SET_PWRMODE 1 #define R92C_CMD_JOINBSS_RPT 2 #define R92C_CMD_RSVD_PAGE 3 #define R92C_CMD_RSSI 4 #define R92C_CMD_RSSI_SETTING 5 #define R92C_CMD_MACID_CONFIG 6 #define R92C_CMD_MACID_PS_MODE 7 #define R92C_CMD_P2P_PS_OFFLOAD 8 #define R92C_CMD_SELECTIVE_SUSPEND 9 #define R92C_CMD_FLAG_EXT 0x80 uint8_t msg[5]; } __packed; /* Structure for R92C_CMD_RSSI_SETTING. */ struct r92c_fw_cmd_rssi { uint8_t macid; uint8_t reserved; uint8_t pwdb; } __packed; /* Structure for R92C_CMD_MACID_CONFIG. */ struct r92c_fw_cmd_macid_cfg { uint32_t mask; uint8_t macid; #define RTWN_MACID_BSS 0 #define RTWN_MACID_BC 4 /* Broadcast. */ #define RTWN_MACID_VALID 0x80 } __packed; /* * RTL8192CU ROM image. */ struct r92c_rom { uint16_t id; /* 0x8129 */ uint8_t reserved1[5]; uint8_t dbg_sel; uint16_t reserved2; uint16_t vid; uint16_t pid; uint8_t usb_opt; uint8_t ep_setting; uint16_t reserved3; uint8_t usb_phy; uint8_t reserved4[3]; uint8_t macaddr[6]; uint8_t string[61]; /* "Realtek" */ uint8_t subcustomer_id; uint8_t cck_tx_pwr[R92C_MAX_CHAINS][3]; uint8_t ht40_1s_tx_pwr[R92C_MAX_CHAINS][3]; uint8_t ht40_2s_tx_pwr_diff[3]; uint8_t ht20_tx_pwr_diff[3]; uint8_t ofdm_tx_pwr_diff[3]; uint8_t ht40_max_pwr[3]; uint8_t ht20_max_pwr[3]; uint8_t xtal_calib; uint8_t tssi[R92C_MAX_CHAINS]; uint8_t thermal_meter; uint8_t rf_opt1; #define R92C_ROM_RF1_REGULATORY_M 0x07 #define R92C_ROM_RF1_REGULATORY_S 0 #define R92C_ROM_RF1_BOARD_TYPE_M 0xe0 #define R92C_ROM_RF1_BOARD_TYPE_S 5 #define R92C_BOARD_TYPE_DONGLE 0 #define R92C_BOARD_TYPE_HIGHPA 1 #define R92C_BOARD_TYPE_MINICARD 2 #define R92C_BOARD_TYPE_SOLO 3 #define R92C_BOARD_TYPE_COMBO 4 uint8_t rf_opt2; uint8_t rf_opt3; uint8_t rf_opt4; uint8_t channel_plan; +#define R92C_CHANNEL_PLAN_BY_HW 0x80 + uint8_t version; uint8_t curstomer_id; } __packed; /* Rx MAC descriptor. */ struct r92c_rx_desc { uint32_t rxdw0; #define R92C_RXDW0_PKTLEN_M 0x00003fff #define R92C_RXDW0_PKTLEN_S 0 #define R92C_RXDW0_CRCERR 0x00004000 #define R92C_RXDW0_ICVERR 0x00008000 #define R92C_RXDW0_INFOSZ_M 0x000f0000 #define R92C_RXDW0_INFOSZ_S 16 #define R92C_RXDW0_QOS 0x00800000 #define R92C_RXDW0_SHIFT_M 0x03000000 #define R92C_RXDW0_SHIFT_S 24 #define R92C_RXDW0_PHYST 0x04000000 #define R92C_RXDW0_DECRYPTED 0x08000000 #define R92C_RXDW0_LS 0x10000000 #define R92C_RXDW0_FS 0x20000000 #define R92C_RXDW0_EOR 0x40000000 #define R92C_RXDW0_OWN 0x80000000 uint32_t rxdw1; uint32_t rxdw2; #define R92C_RXDW2_PKTCNT_M 0x00ff0000 #define R92C_RXDW2_PKTCNT_S 16 uint32_t rxdw3; #define R92C_RXDW3_RATE_M 0x0000003f #define R92C_RXDW3_RATE_S 0 #define R92C_RXDW3_HT 0x00000040 #define R92C_RXDW3_HTC 0x00000400 uint32_t rxdw4; uint32_t rxdw5; uint32_t rxbufaddr; uint32_t rxbufaddr64; } __packed __attribute__((aligned(4))); /* Rx PHY descriptor. */ struct r92c_rx_phystat { uint32_t phydw0; uint32_t phydw1; uint32_t phydw2; uint32_t phydw3; uint32_t phydw4; uint32_t phydw5; uint32_t phydw6; uint32_t phydw7; } __packed __attribute__((aligned(4))); /* Rx PHY CCK descriptor. */ struct r92c_rx_cck { uint8_t adc_pwdb[4]; uint8_t sq_rpt; uint8_t agc_rpt; } __packed; /* Tx MAC descriptor. */ struct r92c_tx_desc { uint32_t txdw0; #define R92C_TXDW0_PKTLEN_M 0x0000ffff #define R92C_TXDW0_PKTLEN_S 0 #define R92C_TXDW0_OFFSET_M 0x00ff0000 #define R92C_TXDW0_OFFSET_S 16 #define R92C_TXDW0_BMCAST 0x01000000 #define R92C_TXDW0_LSG 0x04000000 #define R92C_TXDW0_FSG 0x08000000 #define R92C_TXDW0_OWN 0x80000000 uint32_t txdw1; #define R92C_TXDW1_MACID_M 0x0000001f #define R92C_TXDW1_MACID_S 0 #define R92C_TXDW1_AGGEN 0x00000020 #define R92C_TXDW1_AGGBK 0x00000040 #define R92C_TXDW1_QSEL_M 0x00001f00 #define R92C_TXDW1_QSEL_S 8 #define R92C_TXDW1_QSEL_BE 0x00 #define R92C_TXDW1_QSEL_BK 0x02 #define R92C_TXDW1_QSEL_VI 0x05 #define R92C_TXDW1_QSEL_VO 0x07 #define R92C_TXDW1_QSEL_BEACON 0x10 #define R92C_TXDW1_QSEL_HIGH 0x11 #define R92C_TXDW1_QSEL_MGNT 0x12 #define R92C_TXDW1_QSEL_CMD 0x13 #define R92C_TXDW1_RAID_M 0x000f0000 #define R92C_TXDW1_RAID_S 16 #define R92C_TXDW1_CIPHER_M 0x00c00000 #define R92C_TXDW1_CIPHER_S 22 #define R92C_TXDW1_CIPHER_NONE 0 #define R92C_TXDW1_CIPHER_RC4 1 #define R92C_TXDW1_CIPHER_AES 3 #define R92C_TXDW1_PKTOFF_M 0x7c000000 #define R92C_TXDW1_PKTOFF_S 26 uint32_t txdw2; uint16_t txdw3; uint16_t txdseq; uint32_t txdw4; #define R92C_TXDW4_RTSRATE_M 0x0000003f #define R92C_TXDW4_RTSRATE_S 0 #define R92C_TXDW4_QOS 0x00000040 #define R92C_TXDW4_HWSEQ 0x00000080 #define R92C_TXDW4_DRVRATE 0x00000100 #define R92C_TXDW4_CTS2SELF 0x00000800 #define R92C_TXDW4_RTSEN 0x00001000 #define R92C_TXDW4_HWRTSEN 0x00002000 #define R92C_TXDW4_SCO_M 0x003f0000 #define R92C_TXDW4_SCO_S 20 #define R92C_TXDW4_SCO_SCA 1 #define R92C_TXDW4_SCO_SCB 2 #define R92C_TXDW4_40MHZ 0x02000000 uint32_t txdw5; #define R92C_TXDW5_DATARATE_M 0x0000003f #define R92C_TXDW5_DATARATE_S 0 #define R92C_TXDW5_SGI 0x00000040 #define R92C_TXDW5_DATARATE_FBLIMIT_M 0x00001f00 #define R92C_TXDW5_DATARATE_FBLIMIT_S 8 #define R92C_TXDW5_RTSRATE_FBLIMIT_M 0x0001e000 #define R92C_TXDW5_RTSRATE_FBLIMIT_S 13 #define R92C_TXDW5_RETRY_LIMIT_ENABLE 0x00020000 #define R92C_TXDW5_DATA_RETRY_LIMIT_M 0x00fc0000 #define R92C_TXDW5_DATA_RETRY_LIMIT_S 18 #define R92C_TXDW5_AGGNUM_M 0xff000000 #define R92C_TXDW5_AGGNUM_S 24 uint32_t txdw6; uint16_t txbufsize; uint16_t pad; uint32_t txbufaddr; uint32_t txbufaddr64; uint32_t nextdescaddr; uint32_t nextdescaddr64; uint32_t reserved[4]; } __packed __attribute__((aligned(4))); static const uint8_t ridx2rate[] = { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 }; /* HW rate indices. */ #define RTWN_RIDX_CCK1 0 #define RTWN_RIDX_CCK11 3 #define RTWN_RIDX_OFDM6 4 #define RTWN_RIDX_OFDM24 8 #define RTWN_RIDX_OFDM54 11 #define RTWN_RIDX_MCS0 12 #define RTWN_RIDX_MCS15 27 #define RTWN_RIDX_COUNT 28 #define RTWN_RIDX_UNKNOWN (uint8_t)-1 #define RTWN_RATE_IS_CCK(rate) ((rate) <= RTWN_RIDX_CCK11) #define RTWN_RATE_IS_OFDM(rate) ((rate) >= RTWN_RIDX_OFDM6 && \ (rate) <= RTWN_RIDX_OFDM54) /* * Driver definitions. */ #define RTWN_NTXQUEUES 9 #define RTWN_RX_LIST_COUNT 256 #define RTWN_TX_LIST_COUNT 256 #define RTWN_HOST_CMD_RING_COUNT 32 /* TX queue indices. */ #define RTWN_BK_QUEUE 0 #define RTWN_BE_QUEUE 1 #define RTWN_VI_QUEUE 2 #define RTWN_VO_QUEUE 3 #define RTWN_BEACON_QUEUE 4 #define RTWN_TXCMD_QUEUE 5 #define RTWN_MGNT_QUEUE 6 #define RTWN_HIGH_QUEUE 7 #define RTWN_HCCA_QUEUE 8 /* RX queue indices. */ #define RTWN_RX_QUEUE 0 #define RTWN_RXBUFSZ (16 * 1024) #define RTWN_TXBUFSZ (sizeof(struct r92c_tx_desc) + IEEE80211_MAX_LEN) #define RTWN_TX_TIMEOUT 5000 /* ms */ #define RTWN_LED_LINK 0 #define RTWN_LED_DATA 1 struct rtwn_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint8_t wr_flags; uint8_t wr_rate; uint16_t wr_chan_freq; uint16_t wr_chan_flags; uint8_t wr_dbm_antsignal; } __packed; #define RTWN_RX_RADIOTAP_PRESENT \ (1 << IEEE80211_RADIOTAP_FLAGS | \ 1 << IEEE80211_RADIOTAP_RATE | \ 1 << IEEE80211_RADIOTAP_CHANNEL | \ 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) struct rtwn_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint16_t wt_chan_freq; uint16_t wt_chan_flags; } __packed; #define RTWN_TX_RADIOTAP_PRESENT \ (1 << IEEE80211_RADIOTAP_FLAGS | \ 1 << IEEE80211_RADIOTAP_CHANNEL) struct rtwn_softc; struct rtwn_rx_data { bus_dmamap_t map; struct mbuf *m; bus_addr_t paddr; }; struct rtwn_rx_ring { struct r92c_rx_desc *desc; bus_addr_t paddr; bus_dma_tag_t desc_dmat; bus_dmamap_t desc_map; bus_dma_tag_t data_dmat; bus_dma_segment_t seg; struct rtwn_rx_data rx_data[RTWN_RX_LIST_COUNT]; }; struct rtwn_tx_data { bus_dmamap_t map; struct mbuf *m; struct ieee80211_node *ni; }; struct rtwn_tx_ring { bus_addr_t paddr; bus_dma_tag_t desc_dmat; bus_dmamap_t desc_map; bus_dma_tag_t data_dmat; bus_dma_segment_t seg; struct r92c_tx_desc *desc; struct rtwn_tx_data tx_data[RTWN_TX_LIST_COUNT]; int queued; int cur; }; struct rtwn_host_cmd { void (*cb)(struct rtwn_softc *, void *); uint8_t data[256]; }; struct rtwn_cmd_key { struct ieee80211_key key; uint16_t associd; }; struct rtwn_host_cmd_ring { struct rtwn_host_cmd cmd[RTWN_HOST_CMD_RING_COUNT]; int cur; int next; int queued; }; struct rtwn_vap { struct ieee80211vap vap; int (*newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define RTWN_VAP(vap) ((struct rtwn_vap *)(vap)) struct rtwn_softc { device_t sc_dev; struct mtx sc_mtx; struct ieee80211com sc_ic; struct mbufq sc_snd; struct resource *irq; struct resource *mem; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; void *sc_ih; bus_size_t sc_mapsize; int sc_cap_off; struct callout calib_to; struct callout watchdog_to; int sc_debug; u_int sc_flags; #define RTWN_FLAG_CCK_HIPWR 0x01 #define RTWN_FLAG_BUSY 0x02 #define RTWN_RUNNING 0x04 u_int chip; #define RTWN_CHIP_88C 0x00 #define RTWN_CHIP_92C 0x01 #define RTWN_CHIP_92C_1T2R 0x02 #define RTWN_CHIP_UMC 0x04 #define RTWN_CHIP_UMC_A_CUT 0x08 uint8_t board_type; uint8_t regulatory; uint8_t pa_setting; int avg_pwdb; int thcal_state; int thcal_lctemp; int ntxchains; int nrxchains; int ledlink; int sc_tx_timer; int fwcur; struct rtwn_rx_ring rx_ring; struct rtwn_tx_ring tx_ring[RTWN_NTXQUEUES]; uint32_t qfullmsk; struct r92c_rom rom; uint32_t rf_chnlbw[R92C_MAX_CHAINS]; struct rtwn_rx_radiotap_header sc_rxtap; struct rtwn_tx_radiotap_header sc_txtap; }; /* * MAC initialization values. */ static const struct { uint16_t reg; uint8_t val; } rtl8192ce_mac[] = { { 0x420, 0x80 }, { 0x423, 0x00 }, { 0x430, 0x00 }, { 0x431, 0x00 }, { 0x432, 0x00 }, { 0x433, 0x01 }, { 0x434, 0x04 }, { 0x435, 0x05 }, { 0x436, 0x06 }, { 0x437, 0x07 }, { 0x438, 0x00 }, { 0x439, 0x00 }, { 0x43a, 0x00 }, { 0x43b, 0x01 }, { 0x43c, 0x04 }, { 0x43d, 0x05 }, { 0x43e, 0x06 }, { 0x43f, 0x07 }, { 0x440, 0x5d }, { 0x441, 0x01 }, { 0x442, 0x00 }, { 0x444, 0x15 }, { 0x445, 0xf0 }, { 0x446, 0x0f }, { 0x447, 0x00 }, { 0x458, 0x41 }, { 0x459, 0xa8 }, { 0x45a, 0x72 }, { 0x45b, 0xb9 }, { 0x460, 0x88 }, { 0x461, 0x88 }, { 0x462, 0x06 }, { 0x463, 0x03 }, { 0x4c8, 0x04 }, { 0x4c9, 0x08 }, { 0x4cc, 0x02 }, { 0x4cd, 0x28 }, { 0x4ce, 0x01 }, { 0x500, 0x26 }, { 0x501, 0xa2 }, { 0x502, 0x2f }, { 0x503, 0x00 }, { 0x504, 0x28 }, { 0x505, 0xa3 }, { 0x506, 0x5e }, { 0x507, 0x00 }, { 0x508, 0x2b }, { 0x509, 0xa4 }, { 0x50a, 0x5e }, { 0x50b, 0x00 }, { 0x50c, 0x4f }, { 0x50d, 0xa4 }, { 0x50e, 0x00 }, { 0x50f, 0x00 }, { 0x512, 0x1c }, { 0x514, 0x0a }, { 0x515, 0x10 }, { 0x516, 0x0a }, { 0x517, 0x10 }, { 0x51a, 0x16 }, { 0x524, 0x0f }, { 0x525, 0x4f }, { 0x546, 0x20 }, { 0x547, 0x00 }, { 0x559, 0x02 }, { 0x55a, 0x02 }, { 0x55d, 0xff }, { 0x605, 0x30 }, { 0x608, 0x0e }, { 0x609, 0x2a }, { 0x652, 0x20 }, { 0x63c, 0x0a }, { 0x63d, 0x0e }, { 0x700, 0x21 }, { 0x701, 0x43 }, { 0x702, 0x65 }, { 0x703, 0x87 }, { 0x708, 0x21 }, { 0x709, 0x43 }, { 0x70a, 0x65 }, { 0x70b, 0x87 } }; /* * Baseband initialization values. */ struct rtwn_bb_prog { int count; const uint16_t *regs; const uint32_t *vals; int agccount; const uint32_t *agcvals; }; /* * RTL8192CU and RTL8192CE-VAU. */ static const uint16_t rtl8192ce_bb_regs[] = { 0x024, 0x028, 0x800, 0x804, 0x808, 0x80c, 0x810, 0x814, 0x818, 0x81c, 0x820, 0x824, 0x828, 0x82c, 0x830, 0x834, 0x838, 0x83c, 0x840, 0x844, 0x848, 0x84c, 0x850, 0x854, 0x858, 0x85c, 0x860, 0x864, 0x868, 0x86c, 0x870, 0x874, 0x878, 0x87c, 0x880, 0x884, 0x888, 0x88c, 0x890, 0x894, 0x898, 0x89c, 0x900, 0x904, 0x908, 0x90c, 0xa00, 0xa04, 0xa08, 0xa0c, 0xa10, 0xa14, 0xa18, 0xa1c, 0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74, 0xc00, 0xc04, 0xc08, 0xc0c, 0xc10, 0xc14, 0xc18, 0xc1c, 0xc20, 0xc24, 0xc28, 0xc2c, 0xc30, 0xc34, 0xc38, 0xc3c, 0xc40, 0xc44, 0xc48, 0xc4c, 0xc50, 0xc54, 0xc58, 0xc5c, 0xc60, 0xc64, 0xc68, 0xc6c, 0xc70, 0xc74, 0xc78, 0xc7c, 0xc80, 0xc84, 0xc88, 0xc8c, 0xc90, 0xc94, 0xc98, 0xc9c, 0xca0, 0xca4, 0xca8, 0xcac, 0xcb0, 0xcb4, 0xcb8, 0xcbc, 0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0, 0xcd4, 0xcd8, 0xcdc, 0xce0, 0xce4, 0xce8, 0xcec, 0xd00, 0xd04, 0xd08, 0xd0c, 0xd10, 0xd14, 0xd18, 0xd2c, 0xd30, 0xd34, 0xd38, 0xd3c, 0xd40, 0xd44, 0xd48, 0xd4c, 0xd50, 0xd54, 0xd58, 0xd5c, 0xd60, 0xd64, 0xd68, 0xd6c, 0xd70, 0xd74, 0xd78, 0xe00, 0xe04, 0xe08, 0xe10, 0xe14, 0xe18, 0xe1c, 0xe28, 0xe30, 0xe34, 0xe38, 0xe3c, 0xe40, 0xe44, 0xe48, 0xe4c, 0xe50, 0xe54, 0xe58, 0xe5c, 0xe60, 0xe68, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec, 0xf14, 0xf4c, 0xf00 }; static const uint32_t rtl8192ce_bb_vals_2t[] = { 0x0011800f, 0x00ffdb83, 0x80040002, 0x00000003, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390004, 0x01000100, 0x00390004, 0x27272727, 0x27272727, 0x27272727, 0x27272727, 0x00010000, 0x00010000, 0x27272727, 0x27272727, 0x00000000, 0x00000000, 0x569a569a, 0x0c1b25a4, 0x66e60230, 0x061f0130, 0x27272727, 0x2b2b2b27, 0x07000700, 0x22184000, 0x08080808, 0x00000000, 0xc0083070, 0x000004d5, 0x00000000, 0xcc0000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121313, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05633, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x69543420, 0x43bc0094, 0x69543420, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020403, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x63db25a4, 0x63db25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x63db25a4, 0x0c1b25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4, 0x001b25a4, 0x001b25a4, 0x6fdb25a4, 0x00000003, 0x00000000, 0x00000300 }; static const uint32_t rtl8192ce_bb_vals_1t[] = { 0x0011800f, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a, 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200, 0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070, 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x69543420, 0x43bc0094, 0x69543420, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x631b25a0, 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003, 0x00000000, 0x00000300, }; static const uint32_t rtl8192ce_agc_vals[] = { 0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001, 0x7b050001, 0x7a060001, 0x79070001, 0x78080001, 0x77090001, 0x760a0001, 0x750b0001, 0x740c0001, 0x730d0001, 0x720e0001, 0x710f0001, 0x70100001, 0x6f110001, 0x6e120001, 0x6d130001, 0x6c140001, 0x6b150001, 0x6a160001, 0x69170001, 0x68180001, 0x67190001, 0x661a0001, 0x651b0001, 0x641c0001, 0x631d0001, 0x621e0001, 0x611f0001, 0x60200001, 0x49210001, 0x48220001, 0x47230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001, 0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001, 0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001, 0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001, 0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001, 0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001, 0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001, 0x7a460001, 0x79470001, 0x78480001, 0x77490001, 0x764a0001, 0x754b0001, 0x744c0001, 0x734d0001, 0x724e0001, 0x714f0001, 0x70500001, 0x6f510001, 0x6e520001, 0x6d530001, 0x6c540001, 0x6b550001, 0x6a560001, 0x69570001, 0x68580001, 0x67590001, 0x665a0001, 0x655b0001, 0x645c0001, 0x635d0001, 0x625e0001, 0x615f0001, 0x60600001, 0x49610001, 0x48620001, 0x47630001, 0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001, 0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001, 0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001, 0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001, 0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001, 0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e, 0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e, 0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e, 0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e, 0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e, 0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e, 0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e }; static const struct rtwn_bb_prog rtl8192ce_bb_prog_2t = { nitems(rtl8192ce_bb_regs), rtl8192ce_bb_regs, rtl8192ce_bb_vals_2t, nitems(rtl8192ce_agc_vals), rtl8192ce_agc_vals }; static const struct rtwn_bb_prog rtl8192ce_bb_prog_1t = { nitems(rtl8192ce_bb_regs), rtl8192ce_bb_regs, rtl8192ce_bb_vals_1t, nitems(rtl8192ce_agc_vals), rtl8192ce_agc_vals }; /* * RTL8188CU. */ static const uint32_t rtl8192cu_bb_vals[] = { 0x0011800d, 0x00ffdb83, 0x80040002, 0x00000003, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390004, 0x01000100, 0x00390004, 0x27272727, 0x27272727, 0x27272727, 0x27272727, 0x00010000, 0x00010000, 0x27272727, 0x27272727, 0x00000000, 0x00000000, 0x569a569a, 0x0c1b25a4, 0x66e60230, 0x061f0130, 0x27272727, 0x2b2b2b27, 0x07000700, 0x22184000, 0x08080808, 0x00000000, 0xc0083070, 0x000004d5, 0x00000000, 0xcc0000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121313, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05633, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x0186115b, 0x0000001f, 0x00b99612, 0x40000100, 0x20f60000, 0x40000100, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020403, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x63db25a4, 0x63db25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x63db25a4, 0x0c1b25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4, 0x001b25a4, 0x001b25a4, 0x6fdb25a4, 0x00000003, 0x00000000, 0x00000300 }; static const struct rtwn_bb_prog rtl8192cu_bb_prog = { nitems(rtl8192ce_bb_regs), rtl8192ce_bb_regs, rtl8192cu_bb_vals, nitems(rtl8192ce_agc_vals), rtl8192ce_agc_vals }; /* * RTL8188CE-VAU. */ static const uint32_t rtl8188ce_bb_vals[] = { 0x0011800d, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a, 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200, 0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070, 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000008, 0x001b25a4, 0x631b25a0, 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003, 0x00000000, 0x00000300 }; static const uint32_t rtl8188ce_agc_vals[] = { 0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001, 0x7b050001, 0x7a060001, 0x79070001, 0x78080001, 0x77090001, 0x760a0001, 0x750b0001, 0x740c0001, 0x730d0001, 0x720e0001, 0x710f0001, 0x70100001, 0x6f110001, 0x6e120001, 0x6d130001, 0x6c140001, 0x6b150001, 0x6a160001, 0x69170001, 0x68180001, 0x67190001, 0x661a0001, 0x651b0001, 0x641c0001, 0x631d0001, 0x621e0001, 0x611f0001, 0x60200001, 0x49210001, 0x48220001, 0x47230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001, 0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001, 0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001, 0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001, 0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001, 0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001, 0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001, 0x7a460001, 0x79470001, 0x78480001, 0x77490001, 0x764a0001, 0x754b0001, 0x744c0001, 0x734d0001, 0x724e0001, 0x714f0001, 0x70500001, 0x6f510001, 0x6e520001, 0x6d530001, 0x6c540001, 0x6b550001, 0x6a560001, 0x69570001, 0x68580001, 0x67590001, 0x665a0001, 0x655b0001, 0x645c0001, 0x635d0001, 0x625e0001, 0x615f0001, 0x60600001, 0x49610001, 0x48620001, 0x47630001, 0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001, 0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001, 0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001, 0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001, 0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001, 0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e, 0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e, 0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e, 0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e, 0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e, 0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e, 0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e }; static const struct rtwn_bb_prog rtl8188ce_bb_prog = { nitems(rtl8192ce_bb_regs), rtl8192ce_bb_regs, rtl8188ce_bb_vals, nitems(rtl8188ce_agc_vals), rtl8188ce_agc_vals }; static const uint32_t rtl8188cu_bb_vals[] = { 0x0011800d, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a, 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200, 0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070, 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000008, 0x001b25a4, 0x631b25a0, 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003, 0x00000000, 0x00000300 }; static const struct rtwn_bb_prog rtl8188cu_bb_prog = { nitems(rtl8192ce_bb_regs), rtl8192ce_bb_regs, rtl8188cu_bb_vals, nitems(rtl8188ce_agc_vals), rtl8188ce_agc_vals }; /* * RTL8188RU. */ static const uint16_t rtl8188ru_bb_regs[] = { 0x024, 0x028, 0x040, 0x800, 0x804, 0x808, 0x80c, 0x810, 0x814, 0x818, 0x81c, 0x820, 0x824, 0x828, 0x82c, 0x830, 0x834, 0x838, 0x83c, 0x840, 0x844, 0x848, 0x84c, 0x850, 0x854, 0x858, 0x85c, 0x860, 0x864, 0x868, 0x86c, 0x870, 0x874, 0x878, 0x87c, 0x880, 0x884, 0x888, 0x88c, 0x890, 0x894, 0x898, 0x89c, 0x900, 0x904, 0x908, 0x90c, 0xa00, 0xa04, 0xa08, 0xa0c, 0xa10, 0xa14, 0xa18, 0xa1c, 0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74, 0xc00, 0xc04, 0xc08, 0xc0c, 0xc10, 0xc14, 0xc18, 0xc1c, 0xc20, 0xc24, 0xc28, 0xc2c, 0xc30, 0xc34, 0xc38, 0xc3c, 0xc40, 0xc44, 0xc48, 0xc4c, 0xc50, 0xc54, 0xc58, 0xc5c, 0xc60, 0xc64, 0xc68, 0xc6c, 0xc70, 0xc74, 0xc78, 0xc7c, 0xc80, 0xc84, 0xc88, 0xc8c, 0xc90, 0xc94, 0xc98, 0xc9c, 0xca0, 0xca4, 0xca8, 0xcac, 0xcb0, 0xcb4, 0xcb8, 0xcbc, 0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0, 0xcd4, 0xcd8, 0xcdc, 0xce0, 0xce4, 0xce8, 0xcec, 0xd00, 0xd04, 0xd08, 0xd0c, 0xd10, 0xd14, 0xd18, 0xd2c, 0xd30, 0xd34, 0xd38, 0xd3c, 0xd40, 0xd44, 0xd48, 0xd4c, 0xd50, 0xd54, 0xd58, 0xd5c, 0xd60, 0xd64, 0xd68, 0xd6c, 0xd70, 0xd74, 0xd78, 0xe00, 0xe04, 0xe08, 0xe10, 0xe14, 0xe18, 0xe1c, 0xe28, 0xe30, 0xe34, 0xe38, 0xe3c, 0xe40, 0xe44, 0xe48, 0xe4c, 0xe50, 0xe54, 0xe58, 0xe5c, 0xe60, 0xe68, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec, 0xee8, 0xf14, 0xf4c, 0xf00 }; static const uint32_t rtl8188ru_bb_vals[] = { 0x0011800d, 0x00ffdb83, 0x000c0004, 0x80040000, 0x00000001, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390204, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a, 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200, 0x03000300, 0x22004000, 0x00000808, 0x00ffc3f1, 0xc0083070, 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x15160000, 0x070b0f12, 0x00000104, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x6954342e, 0x43bc0094, 0x6954342f, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c56000d, 0x018610db, 0x0000001f, 0x00b91612, 0x24000090, 0x20f60000, 0x24000090, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x631b25a0, 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x31555448, 0x00000003, 0x00000000, 0x00000300 }; static const uint32_t rtl8188ru_agc_vals[] = { 0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001, 0x7b050001, 0x7b060001, 0x7b070001, 0x7b080001, 0x7a090001, 0x790a0001, 0x780b0001, 0x770c0001, 0x760d0001, 0x750e0001, 0x740f0001, 0x73100001, 0x72110001, 0x71120001, 0x70130001, 0x6f140001, 0x6e150001, 0x6d160001, 0x6c170001, 0x6b180001, 0x6a190001, 0x691a0001, 0x681b0001, 0x671c0001, 0x661d0001, 0x651e0001, 0x641f0001, 0x63200001, 0x62210001, 0x61220001, 0x60230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001, 0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001, 0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001, 0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001, 0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001, 0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001, 0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001, 0x7b460001, 0x7b470001, 0x7b480001, 0x7a490001, 0x794a0001, 0x784b0001, 0x774c0001, 0x764d0001, 0x754e0001, 0x744f0001, 0x73500001, 0x72510001, 0x71520001, 0x70530001, 0x6f540001, 0x6e550001, 0x6d560001, 0x6c570001, 0x6b580001, 0x6a590001, 0x695a0001, 0x685b0001, 0x675c0001, 0x665d0001, 0x655e0001, 0x645f0001, 0x63600001, 0x62610001, 0x61620001, 0x60630001, 0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001, 0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001, 0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001, 0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001, 0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001, 0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e, 0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e, 0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e, 0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e, 0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e, 0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e, 0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e }; static const struct rtwn_bb_prog rtl8188ru_bb_prog = { nitems(rtl8188ru_bb_regs), rtl8188ru_bb_regs, rtl8188ru_bb_vals, nitems(rtl8188ru_agc_vals), rtl8188ru_agc_vals }; /* * RF initialization values. */ struct rtwn_rf_prog { int count; const uint8_t *regs; const uint32_t *vals; }; /* * RTL8192CU and RTL8192CE-VAU. */ static const uint8_t rtl8192ce_rf1_regs[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2a, 0x2b, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x00, 0x18, 0xfe, 0xfe, 0x1f, 0xfe, 0xfe, 0x1e, 0x1f, 0x00 }; static const uint32_t rtl8192ce_rf1_vals[] = { 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255, 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, 0x00000, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x577c0, 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000, 0x71000, 0xb0000, 0xfc000, 0x287af, 0x244b7, 0x204ab, 0x1c49f, 0x18493, 0x14297, 0x10295, 0x0c298, 0x0819c, 0x040a8, 0x0001c, 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424, 0xcf424, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, 0x30159 }; static const uint8_t rtl8192ce_rf2_regs[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16 }; static const uint32_t rtl8192ce_rf2_vals[] = { 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x32000, 0x71000, 0xb0000, 0xfc000, 0x287af, 0x244b7, 0x204ab, 0x1c49f, 0x18493, 0x14297, 0x10295, 0x0c298, 0x0819c, 0x040a8, 0x0001c, 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424, 0xcf424, 0xe0330, 0xa0330, 0x60330, 0x20330 }; static const struct rtwn_rf_prog rtl8192ce_rf_prog[] = { { nitems(rtl8192ce_rf1_regs), rtl8192ce_rf1_regs, rtl8192ce_rf1_vals }, { nitems(rtl8192ce_rf2_regs), rtl8192ce_rf2_regs, rtl8192ce_rf2_vals } }; /* * RTL8188CE-VAU. */ static const uint32_t rtl8188ce_rf_vals[] = { 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255, 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, 0x00000, 0x01558, 0x00060, 0x00483, 0x4f200, 0xec7d9, 0x577c0, 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000, 0x71000, 0xb0000, 0xfc000, 0x287b3, 0x244b7, 0x204ab, 0x1c49f, 0x18493, 0x1429b, 0x10299, 0x0c29c, 0x081a0, 0x040ac, 0x00020, 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424, 0xcf424, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, 0x30159 }; static const struct rtwn_rf_prog rtl8188ce_rf_prog[] = { { nitems(rtl8192ce_rf1_regs), rtl8192ce_rf1_regs, rtl8188ce_rf_vals } }; /* * RTL8188CU. */ static const uint32_t rtl8188cu_rf_vals[] = { 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255, 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, 0x00000, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x577c0, 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000, 0x71000, 0xb0000, 0xfc000, 0x287b3, 0x244b7, 0x204ab, 0x1c49f, 0x18493, 0x1429b, 0x10299, 0x0c29c, 0x081a0, 0x040ac, 0x00020, 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f405, 0x4f405, 0x8f405, 0xcf405, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, 0x30159 }; static const struct rtwn_rf_prog rtl8188cu_rf_prog[] = { { nitems(rtl8192ce_rf1_regs), rtl8192ce_rf1_regs, rtl8188cu_rf_vals } }; /* * RTL8188RU. */ static const uint32_t rtl8188ru_rf_vals[] = { 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb0, 0x54867, 0x8992e, 0x0e529, 0x39ce7, 0x00451, 0x00000, 0x00255, 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, 0x0083c, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x977c0, 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0xd8000, 0x90000, 0x51000, 0x12000, 0x28fb4, 0x24fa8, 0x207a4, 0x1c798, 0x183a4, 0x14398, 0x101a4, 0x0c198, 0x080a4, 0x04098, 0x00014, 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f405, 0x4f405, 0x8f405, 0xcf405, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, 0x30159 }; static const struct rtwn_rf_prog rtl8188ru_rf_prog[] = { { nitems(rtl8192ce_rf1_regs), rtl8192ce_rf1_regs, rtl8188ru_rf_vals } }; struct rtwn_txpwr { uint8_t pwr[3][28]; }; /* * Per RF chain/group/rate Tx gain values. */ static const struct rtwn_txpwr rtl8192cu_txagc[] = { { { /* Chain 0. */ { /* Group 0. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x0c, 0x0c, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, /* OFDM6~54. */ 0x0e, 0x0d, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, /* MCS0~7. */ 0x0e, 0x0d, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02 /* MCS8~15. */ }, { /* Group 1. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 2. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ } } }, { { /* Chain 1. */ { /* Group 0. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 1. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 2. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ } } } }; static const struct rtwn_txpwr rtl8188ru_txagc[] = { { { /* Chain 0. */ { /* Group 0. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x08, 0x08, 0x08, 0x06, 0x06, 0x04, 0x04, 0x00, /* OFDM6~54. */ 0x08, 0x06, 0x06, 0x04, 0x04, 0x02, 0x02, 0x00, /* MCS0~7. */ 0x08, 0x06, 0x06, 0x04, 0x04, 0x02, 0x02, 0x00 /* MCS8~15. */ }, { /* Group 1. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 2. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ } } } }; #define RTWN_LOCK_INIT(_sc) \ mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ MTX_NETWORK_LOCK, MTX_DEF) #define RTWN_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define RTWN_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define RTWN_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define RTWN_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) Index: head/sys/dev/urtwn/if_urtwn.c =================================================================== --- head/sys/dev/urtwn/if_urtwn.c (revision 300753) +++ head/sys/dev/urtwn/if_urtwn.c (revision 300754) @@ -1,5572 +1,5590 @@ /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * Copyright (c) 2014 Kevin Lo * Copyright (c) 2015 Andriy Voskoboinyk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /* * Driver for Realtek RTL8188CE-VAU/RTL8188CUS/RTL8188EU/RTL8188RU/RTL8192CU. */ #include "opt_wlan.h" #include "opt_urtwn.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #include #include #include #include "usbdevs.h" #include #include #include #ifdef USB_DEBUG enum { URTWN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ URTWN_DEBUG_RECV = 0x00000002, /* basic recv operation */ URTWN_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ URTWN_DEBUG_RA = 0x00000008, /* f/w rate adaptation setup */ URTWN_DEBUG_USB = 0x00000010, /* usb requests */ URTWN_DEBUG_FIRMWARE = 0x00000020, /* firmware(9) loading debug */ URTWN_DEBUG_BEACON = 0x00000040, /* beacon handling */ URTWN_DEBUG_INTR = 0x00000080, /* ISR */ URTWN_DEBUG_TEMP = 0x00000100, /* temperature calibration */ URTWN_DEBUG_ROM = 0x00000200, /* various ROM info */ URTWN_DEBUG_KEY = 0x00000400, /* crypto keys management */ URTWN_DEBUG_TXPWR = 0x00000800, /* dump Tx power values */ URTWN_DEBUG_RSSI = 0x00001000, /* dump RSSI lookups */ URTWN_DEBUG_ANY = 0xffffffff }; #define URTWN_DPRINTF(_sc, _m, ...) do { \ if ((_sc)->sc_debug & (_m)) \ device_printf((_sc)->sc_dev, __VA_ARGS__); \ } while(0) #else #define URTWN_DPRINTF(_sc, _m, ...) do { (void) sc; } while (0) #endif #define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh) static int urtwn_enable_11n = 1; TUNABLE_INT("hw.usb.urtwn.enable_11n", &urtwn_enable_11n); /* various supported device vendors/products */ static const STRUCT_USB_HOST_ID urtwn_devs[] = { #define URTWN_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } #define URTWN_RTL8188E_DEV(v,p) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTWN_RTL8188E) } #define URTWN_RTL8188E 1 URTWN_DEV(ABOCOM, RTL8188CU_1), URTWN_DEV(ABOCOM, RTL8188CU_2), URTWN_DEV(ABOCOM, RTL8192CU), URTWN_DEV(ASUS, RTL8192CU), URTWN_DEV(ASUS, USBN10NANO), URTWN_DEV(AZUREWAVE, RTL8188CE_1), URTWN_DEV(AZUREWAVE, RTL8188CE_2), URTWN_DEV(AZUREWAVE, RTL8188CU), URTWN_DEV(BELKIN, F7D2102), URTWN_DEV(BELKIN, RTL8188CU), URTWN_DEV(BELKIN, RTL8192CU), URTWN_DEV(CHICONY, RTL8188CUS_1), URTWN_DEV(CHICONY, RTL8188CUS_2), URTWN_DEV(CHICONY, RTL8188CUS_3), URTWN_DEV(CHICONY, RTL8188CUS_4), URTWN_DEV(CHICONY, RTL8188CUS_5), URTWN_DEV(COREGA, RTL8192CU), URTWN_DEV(DLINK, RTL8188CU), URTWN_DEV(DLINK, RTL8192CU_1), URTWN_DEV(DLINK, RTL8192CU_2), URTWN_DEV(DLINK, RTL8192CU_3), URTWN_DEV(DLINK, DWA131B), URTWN_DEV(EDIMAX, EW7811UN), URTWN_DEV(EDIMAX, RTL8192CU), URTWN_DEV(FEIXUN, RTL8188CU), URTWN_DEV(FEIXUN, RTL8192CU), URTWN_DEV(GUILLEMOT, HWNUP150), URTWN_DEV(HAWKING, RTL8192CU), URTWN_DEV(HP3, RTL8188CU), URTWN_DEV(NETGEAR, WNA1000M), URTWN_DEV(NETGEAR, RTL8192CU), URTWN_DEV(NETGEAR4, RTL8188CU), URTWN_DEV(NOVATECH, RTL8188CU), URTWN_DEV(PLANEX2, RTL8188CU_1), URTWN_DEV(PLANEX2, RTL8188CU_2), URTWN_DEV(PLANEX2, RTL8188CU_3), URTWN_DEV(PLANEX2, RTL8188CU_4), URTWN_DEV(PLANEX2, RTL8188CUS), URTWN_DEV(PLANEX2, RTL8192CU), URTWN_DEV(REALTEK, RTL8188CE_0), URTWN_DEV(REALTEK, RTL8188CE_1), URTWN_DEV(REALTEK, RTL8188CTV), URTWN_DEV(REALTEK, RTL8188CU_0), URTWN_DEV(REALTEK, RTL8188CU_1), URTWN_DEV(REALTEK, RTL8188CU_2), URTWN_DEV(REALTEK, RTL8188CU_3), URTWN_DEV(REALTEK, RTL8188CU_COMBO), URTWN_DEV(REALTEK, RTL8188CUS), URTWN_DEV(REALTEK, RTL8188RU_1), URTWN_DEV(REALTEK, RTL8188RU_2), URTWN_DEV(REALTEK, RTL8188RU_3), URTWN_DEV(REALTEK, RTL8191CU), URTWN_DEV(REALTEK, RTL8192CE), URTWN_DEV(REALTEK, RTL8192CU), URTWN_DEV(SITECOMEU, RTL8188CU_1), URTWN_DEV(SITECOMEU, RTL8188CU_2), URTWN_DEV(SITECOMEU, RTL8192CU), URTWN_DEV(TRENDNET, RTL8188CU), URTWN_DEV(TRENDNET, RTL8192CU), URTWN_DEV(ZYXEL, RTL8192CU), /* URTWN_RTL8188E */ URTWN_RTL8188E_DEV(ABOCOM, RTL8188EU), URTWN_RTL8188E_DEV(DLINK, DWA123D1), URTWN_RTL8188E_DEV(DLINK, DWA125D1), URTWN_RTL8188E_DEV(ELECOM, WDC150SU2M), URTWN_RTL8188E_DEV(REALTEK, RTL8188ETV), URTWN_RTL8188E_DEV(REALTEK, RTL8188EU), #undef URTWN_RTL8188E_DEV #undef URTWN_DEV }; static device_probe_t urtwn_match; static device_attach_t urtwn_attach; static device_detach_t urtwn_detach; static usb_callback_t urtwn_bulk_tx_callback; static usb_callback_t urtwn_bulk_rx_callback; static void urtwn_sysctlattach(struct urtwn_softc *); static void urtwn_drain_mbufq(struct urtwn_softc *); static usb_error_t urtwn_do_request(struct urtwn_softc *, struct usb_device_request *, void *); static struct ieee80211vap *urtwn_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 urtwn_vap_delete(struct ieee80211vap *); static struct mbuf * urtwn_rx_copy_to_mbuf(struct urtwn_softc *, struct r92c_rx_stat *, int); static struct mbuf * urtwn_report_intr(struct usb_xfer *, struct urtwn_data *); static struct mbuf * urtwn_rxeof(struct urtwn_softc *, uint8_t *, int); static void urtwn_r88e_ratectl_tx_complete(struct urtwn_softc *, void *); static struct ieee80211_node *urtwn_rx_frame(struct urtwn_softc *, struct mbuf *, int8_t *); static void urtwn_txeof(struct urtwn_softc *, struct urtwn_data *, int); static int urtwn_alloc_list(struct urtwn_softc *, struct urtwn_data[], int, int); static int urtwn_alloc_rx_list(struct urtwn_softc *); static int urtwn_alloc_tx_list(struct urtwn_softc *); static void urtwn_free_list(struct urtwn_softc *, struct urtwn_data data[], int); static void urtwn_free_rx_list(struct urtwn_softc *); static void urtwn_free_tx_list(struct urtwn_softc *); static struct urtwn_data * _urtwn_getbuf(struct urtwn_softc *); static struct urtwn_data * urtwn_getbuf(struct urtwn_softc *); static usb_error_t urtwn_write_region_1(struct urtwn_softc *, uint16_t, uint8_t *, int); static usb_error_t urtwn_write_1(struct urtwn_softc *, uint16_t, uint8_t); static usb_error_t urtwn_write_2(struct urtwn_softc *, uint16_t, uint16_t); static usb_error_t urtwn_write_4(struct urtwn_softc *, uint16_t, uint32_t); static usb_error_t urtwn_read_region_1(struct urtwn_softc *, uint16_t, uint8_t *, int); static uint8_t urtwn_read_1(struct urtwn_softc *, uint16_t); static uint16_t urtwn_read_2(struct urtwn_softc *, uint16_t); static uint32_t urtwn_read_4(struct urtwn_softc *, uint16_t); static int urtwn_fw_cmd(struct urtwn_softc *, uint8_t, const void *, int); static void urtwn_cmdq_cb(void *, int); static int urtwn_cmd_sleepable(struct urtwn_softc *, const void *, size_t, CMD_FUNC_PROTO); static void urtwn_r92c_rf_write(struct urtwn_softc *, int, uint8_t, uint32_t); static void urtwn_r88e_rf_write(struct urtwn_softc *, int, uint8_t, uint32_t); static uint32_t urtwn_rf_read(struct urtwn_softc *, int, uint8_t); static int urtwn_llt_write(struct urtwn_softc *, uint32_t, uint32_t); static int urtwn_efuse_read_next(struct urtwn_softc *, uint8_t *); static int urtwn_efuse_read_data(struct urtwn_softc *, uint8_t *, uint8_t, uint8_t); #ifdef USB_DEBUG static void urtwn_dump_rom_contents(struct urtwn_softc *, uint8_t *, uint16_t); #endif static int urtwn_efuse_read(struct urtwn_softc *, uint8_t *, uint16_t); static int urtwn_efuse_switch_power(struct urtwn_softc *); static int urtwn_read_chipid(struct urtwn_softc *); static int urtwn_read_rom(struct urtwn_softc *); static int urtwn_r88e_read_rom(struct urtwn_softc *); static int urtwn_ra_init(struct urtwn_softc *); static void urtwn_init_beacon(struct urtwn_softc *, struct urtwn_vap *); static int urtwn_setup_beacon(struct urtwn_softc *, struct ieee80211_node *); static void urtwn_update_beacon(struct ieee80211vap *, int); static int urtwn_tx_beacon(struct urtwn_softc *sc, struct urtwn_vap *); static int urtwn_key_alloc(struct ieee80211vap *, struct ieee80211_key *, ieee80211_keyix *, ieee80211_keyix *); static void urtwn_key_set_cb(struct urtwn_softc *, union sec_param *); static void urtwn_key_del_cb(struct urtwn_softc *, union sec_param *); static int urtwn_key_set(struct ieee80211vap *, const struct ieee80211_key *); static int urtwn_key_delete(struct ieee80211vap *, const struct ieee80211_key *); static void urtwn_tsf_task_adhoc(void *, int); static void urtwn_tsf_sync_enable(struct urtwn_softc *, struct ieee80211vap *); static void urtwn_get_tsf(struct urtwn_softc *, uint64_t *); static void urtwn_set_led(struct urtwn_softc *, int, int); static void urtwn_set_mode(struct urtwn_softc *, uint8_t); static void urtwn_ibss_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, const struct ieee80211_rx_stats *, int, int); static int urtwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void urtwn_calib_to(void *); static void urtwn_calib_cb(struct urtwn_softc *, union sec_param *); static void urtwn_watchdog(void *); static void urtwn_update_avgrssi(struct urtwn_softc *, int, int8_t); static int8_t urtwn_get_rssi(struct urtwn_softc *, int, void *); static int8_t urtwn_r88e_get_rssi(struct urtwn_softc *, int, void *); static int urtwn_tx_data(struct urtwn_softc *, struct ieee80211_node *, struct mbuf *, struct urtwn_data *); static int urtwn_tx_raw(struct urtwn_softc *, struct ieee80211_node *, struct mbuf *, struct urtwn_data *, const struct ieee80211_bpf_params *); static void urtwn_tx_start(struct urtwn_softc *, struct mbuf *, uint8_t, struct urtwn_data *); static int urtwn_transmit(struct ieee80211com *, struct mbuf *); static void urtwn_start(struct urtwn_softc *); static void urtwn_parent(struct ieee80211com *); static int urtwn_r92c_power_on(struct urtwn_softc *); static int urtwn_r88e_power_on(struct urtwn_softc *); static void urtwn_r92c_power_off(struct urtwn_softc *); static void urtwn_r88e_power_off(struct urtwn_softc *); static int urtwn_llt_init(struct urtwn_softc *); #ifndef URTWN_WITHOUT_UCODE static void urtwn_fw_reset(struct urtwn_softc *); static void urtwn_r88e_fw_reset(struct urtwn_softc *); static int urtwn_fw_loadpage(struct urtwn_softc *, int, const uint8_t *, int); static int urtwn_load_firmware(struct urtwn_softc *); #endif static int urtwn_dma_init(struct urtwn_softc *); static int urtwn_mac_init(struct urtwn_softc *); static void urtwn_bb_init(struct urtwn_softc *); static void urtwn_rf_init(struct urtwn_softc *); static void urtwn_cam_init(struct urtwn_softc *); static int urtwn_cam_write(struct urtwn_softc *, uint32_t, uint32_t); static void urtwn_pa_bias_init(struct urtwn_softc *); static void urtwn_rxfilter_init(struct urtwn_softc *); static void urtwn_edca_init(struct urtwn_softc *); static void urtwn_write_txpower(struct urtwn_softc *, int, uint16_t[]); static void urtwn_get_txpower(struct urtwn_softc *, int, struct ieee80211_channel *, struct ieee80211_channel *, uint16_t[]); static void urtwn_r88e_get_txpower(struct urtwn_softc *, int, struct ieee80211_channel *, struct ieee80211_channel *, uint16_t[]); static void urtwn_set_txpower(struct urtwn_softc *, struct ieee80211_channel *, struct ieee80211_channel *); static void urtwn_set_rx_bssid_all(struct urtwn_softc *, int); static void urtwn_set_gain(struct urtwn_softc *, uint8_t); static void urtwn_scan_start(struct ieee80211com *); static void urtwn_scan_end(struct ieee80211com *); +static void urtwn_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); static void urtwn_set_channel(struct ieee80211com *); static int urtwn_wme_update(struct ieee80211com *); static void urtwn_update_slot(struct ieee80211com *); static void urtwn_update_slot_cb(struct urtwn_softc *, union sec_param *); static void urtwn_update_aifs(struct urtwn_softc *, uint8_t); static uint8_t urtwn_get_multi_pos(const uint8_t[]); static void urtwn_set_multi(struct urtwn_softc *); static void urtwn_set_promisc(struct urtwn_softc *); static void urtwn_update_promisc(struct ieee80211com *); static void urtwn_update_mcast(struct ieee80211com *); static struct ieee80211_node *urtwn_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static void urtwn_newassoc(struct ieee80211_node *, int); static void urtwn_node_free(struct ieee80211_node *); static void urtwn_set_chan(struct urtwn_softc *, struct ieee80211_channel *, struct ieee80211_channel *); static void urtwn_iq_calib(struct urtwn_softc *); static void urtwn_lc_calib(struct urtwn_softc *); static void urtwn_temp_calib(struct urtwn_softc *); static int urtwn_init(struct urtwn_softc *); static void urtwn_stop(struct urtwn_softc *); static void urtwn_abort_xfers(struct urtwn_softc *); static int urtwn_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void urtwn_ms_delay(struct urtwn_softc *); /* Aliases. */ #define urtwn_bb_write urtwn_write_4 #define urtwn_bb_read urtwn_read_4 static const struct usb_config urtwn_config[URTWN_N_TRANSFER] = { [URTWN_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = URTWN_RXBUFSZ, .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = urtwn_bulk_rx_callback, }, [URTWN_BULK_TX_BE] = { .type = UE_BULK, .endpoint = 0x03, .direction = UE_DIR_OUT, .bufsize = URTWN_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, .force_short_xfer = 1 }, .callback = urtwn_bulk_tx_callback, .timeout = URTWN_TX_TIMEOUT, /* ms */ }, [URTWN_BULK_TX_BK] = { .type = UE_BULK, .endpoint = 0x03, .direction = UE_DIR_OUT, .bufsize = URTWN_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, .force_short_xfer = 1, }, .callback = urtwn_bulk_tx_callback, .timeout = URTWN_TX_TIMEOUT, /* ms */ }, [URTWN_BULK_TX_VI] = { .type = UE_BULK, .endpoint = 0x02, .direction = UE_DIR_OUT, .bufsize = URTWN_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, .force_short_xfer = 1 }, .callback = urtwn_bulk_tx_callback, .timeout = URTWN_TX_TIMEOUT, /* ms */ }, [URTWN_BULK_TX_VO] = { .type = UE_BULK, .endpoint = 0x02, .direction = UE_DIR_OUT, .bufsize = URTWN_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, .force_short_xfer = 1 }, .callback = urtwn_bulk_tx_callback, .timeout = URTWN_TX_TIMEOUT, /* ms */ }, }; static const struct wme_to_queue { uint16_t reg; uint8_t qid; } wme2queue[WME_NUM_AC] = { { R92C_EDCA_BE_PARAM, URTWN_BULK_TX_BE}, { R92C_EDCA_BK_PARAM, URTWN_BULK_TX_BK}, { R92C_EDCA_VI_PARAM, URTWN_BULK_TX_VI}, { R92C_EDCA_VO_PARAM, URTWN_BULK_TX_VO} }; +static const uint8_t urtwn_chan_2ghz[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + static int urtwn_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != URTWN_CONFIG_INDEX) return (ENXIO); if (uaa->info.bIfaceIndex != URTWN_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(urtwn_devs, sizeof(urtwn_devs), uaa)); } static void urtwn_update_chw(struct ieee80211com *ic) { } static int urtwn_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { /* We're driving this ourselves (eventually); don't involve net80211 */ return (0); } static int urtwn_attach(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); struct urtwn_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; - uint8_t bands[IEEE80211_MODE_BYTES]; int error; device_set_usb_desc(self); sc->sc_udev = uaa->device; sc->sc_dev = self; if (USB_GET_DRIVER_INFO(uaa) == URTWN_RTL8188E) sc->chip |= URTWN_CHIP_88E; #ifdef USB_DEBUG int debug; if (resource_int_value(device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev), "debug", &debug) == 0) sc->sc_debug = debug; #endif mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF); URTWN_CMDQ_LOCK_INIT(sc); URTWN_NT_LOCK_INIT(sc); callout_init(&sc->sc_calib_to, 0); callout_init(&sc->sc_watchdog_ch, 0); mbufq_init(&sc->sc_snd, ifqmaxlen); sc->sc_iface_index = URTWN_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index, sc->sc_xfer, urtwn_config, URTWN_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(self, "could not allocate USB transfers, " "err=%s\n", usbd_errstr(error)); goto detach; } URTWN_LOCK(sc); error = urtwn_read_chipid(sc); if (error) { device_printf(sc->sc_dev, "unsupported test chip\n"); URTWN_UNLOCK(sc); goto detach; } /* Determine number of Tx/Rx chains. */ if (sc->chip & URTWN_CHIP_92C) { sc->ntxchains = (sc->chip & URTWN_CHIP_92C_1T2R) ? 1 : 2; sc->nrxchains = 2; } else { sc->ntxchains = 1; sc->nrxchains = 1; } if (sc->chip & URTWN_CHIP_88E) error = urtwn_r88e_read_rom(sc); else error = urtwn_read_rom(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: cannot read rom, error %d\n", __func__, error); URTWN_UNLOCK(sc); goto detach; } device_printf(sc->sc_dev, "MAC/BB RTL%s, RF 6052 %dT%dR\n", (sc->chip & URTWN_CHIP_92C) ? "8192CU" : (sc->chip & URTWN_CHIP_88E) ? "8188EU" : (sc->board_type == R92C_BOARD_TYPE_HIGHPA) ? "8188RU" : (sc->board_type == R92C_BOARD_TYPE_MINICARD) ? "8188CE-VAU" : "8188CUS", sc->ntxchains, sc->nrxchains); URTWN_UNLOCK(sc); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(self); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* set device capabilities */ ic->ic_caps = IEEE80211_C_STA /* station mode */ | IEEE80211_C_MONITOR /* monitor mode */ | IEEE80211_C_IBSS /* adhoc mode */ | IEEE80211_C_HOSTAP /* hostap mode */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_SHSLOT /* short slot time supported */ #if 0 | IEEE80211_C_BGSCAN /* capable of bg scanning */ #endif | IEEE80211_C_WPA /* 802.11i */ | IEEE80211_C_WME /* 802.11e */ | IEEE80211_C_SWAMSDUTX /* Do software A-MSDU TX */ | IEEE80211_C_FF /* Atheros fast-frames */ ; ic->ic_cryptocaps = IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_TKIP | IEEE80211_CRYPTO_AES_CCM; /* Assume they're all 11n capable for now */ if (urtwn_enable_11n) { device_printf(self, "enabling 11n\n"); ic->ic_htcaps = IEEE80211_HTC_HT | #if 0 IEEE80211_HTC_AMPDU | #endif IEEE80211_HTC_AMSDU | IEEE80211_HTCAP_MAXAMSDU_3839 | IEEE80211_HTCAP_SMPS_OFF; /* no HT40 just yet */ // ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40; /* XXX TODO: verify chains versus streams for urtwn */ ic->ic_txstream = sc->ntxchains; ic->ic_rxstream = sc->nrxchains; } - memset(bands, 0, sizeof(bands)); - setbit(bands, IEEE80211_MODE_11B); - setbit(bands, IEEE80211_MODE_11G); - if (urtwn_enable_11n) - setbit(bands, IEEE80211_MODE_11NG); - ieee80211_init_channels(ic, NULL, bands); + /* XXX TODO: setup regdomain if R92C_CHANNEL_PLAN_BY_HW bit is set. */ + urtwn_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + ieee80211_ifattach(ic); ic->ic_raw_xmit = urtwn_raw_xmit; ic->ic_scan_start = urtwn_scan_start; ic->ic_scan_end = urtwn_scan_end; + ic->ic_getradiocaps = urtwn_getradiocaps; ic->ic_set_channel = urtwn_set_channel; ic->ic_transmit = urtwn_transmit; ic->ic_parent = urtwn_parent; ic->ic_vap_create = urtwn_vap_create; ic->ic_vap_delete = urtwn_vap_delete; ic->ic_wme.wme_update = urtwn_wme_update; ic->ic_updateslot = urtwn_update_slot; ic->ic_update_promisc = urtwn_update_promisc; ic->ic_update_mcast = urtwn_update_mcast; if (sc->chip & URTWN_CHIP_88E) { ic->ic_node_alloc = urtwn_node_alloc; ic->ic_newassoc = urtwn_newassoc; sc->sc_node_free = ic->ic_node_free; ic->ic_node_free = urtwn_node_free; } ic->ic_update_chw = urtwn_update_chw; ic->ic_ampdu_enable = urtwn_ampdu_enable; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), URTWN_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), URTWN_RX_RADIOTAP_PRESENT); TASK_INIT(&sc->cmdq_task, 0, urtwn_cmdq_cb, sc); urtwn_sysctlattach(sc); if (bootverbose) ieee80211_announce(ic); return (0); detach: urtwn_detach(self); return (ENXIO); /* failure */ } static void urtwn_sysctlattach(struct urtwn_softc *sc) { #ifdef USB_DEBUG struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, sc->sc_debug, "control debugging printfs"); #endif } static int urtwn_detach(device_t self) { struct urtwn_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; unsigned int x; /* Prevent further ioctls. */ URTWN_LOCK(sc); sc->sc_flags |= URTWN_DETACHED; URTWN_UNLOCK(sc); urtwn_stop(sc); callout_drain(&sc->sc_watchdog_ch); callout_drain(&sc->sc_calib_to); /* stop all USB transfers */ usbd_transfer_unsetup(sc->sc_xfer, URTWN_N_TRANSFER); /* Prevent further allocations from RX/TX data lists. */ URTWN_LOCK(sc); STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); URTWN_UNLOCK(sc); /* drain USB transfers */ for (x = 0; x != URTWN_N_TRANSFER; x++) usbd_transfer_drain(sc->sc_xfer[x]); /* Free data buffers. */ URTWN_LOCK(sc); urtwn_free_tx_list(sc); urtwn_free_rx_list(sc); URTWN_UNLOCK(sc); if (ic->ic_softc == sc) { ieee80211_draintask(ic, &sc->cmdq_task); ieee80211_ifdetach(ic); } URTWN_NT_LOCK_DESTROY(sc); URTWN_CMDQ_LOCK_DESTROY(sc); mtx_destroy(&sc->sc_mtx); return (0); } static void urtwn_drain_mbufq(struct urtwn_softc *sc) { struct mbuf *m; struct ieee80211_node *ni; URTWN_ASSERT_LOCKED(sc); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; ieee80211_free_node(ni); m_freem(m); } } static usb_error_t urtwn_do_request(struct urtwn_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; URTWN_ASSERT_LOCKED(sc); while (ntries--) { err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 250 /* ms */); if (err == 0) break; URTWN_DPRINTF(sc, URTWN_DEBUG_USB, "%s: control request failed, %s (retries left: %d)\n", __func__, usbd_errstr(err), ntries); usb_pause_mtx(&sc->sc_mtx, hz / 100); } return (err); } static struct ieee80211vap * urtwn_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 urtwn_softc *sc = ic->ic_softc; struct urtwn_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return (NULL); uvp = malloc(sizeof(struct urtwn_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { /* out of memory */ free(uvp, M_80211_VAP); return (NULL); } if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_IBSS) urtwn_init_beacon(sc, uvp); /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = urtwn_newstate; vap->iv_update_beacon = urtwn_update_beacon; vap->iv_key_alloc = urtwn_key_alloc; vap->iv_key_set = urtwn_key_set; vap->iv_key_delete = urtwn_key_delete; /* 802.11n parameters */ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16; vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; if (opmode == IEEE80211_M_IBSS) { uvp->recv_mgmt = vap->iv_recv_mgmt; vap->iv_recv_mgmt = urtwn_ibss_recv_mgmt; TASK_INIT(&uvp->tsf_task_adhoc, 0, urtwn_tsf_task_adhoc, vap); } if (URTWN_CHIP_HAS_RATECTL(sc)) ieee80211_ratectl_init(vap); /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return (vap); } static void urtwn_vap_delete(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct urtwn_softc *sc = ic->ic_softc; struct urtwn_vap *uvp = URTWN_VAP(vap); if (uvp->bcn_mbuf != NULL) m_freem(uvp->bcn_mbuf); if (vap->iv_opmode == IEEE80211_M_IBSS) ieee80211_draintask(ic, &uvp->tsf_task_adhoc); if (URTWN_CHIP_HAS_RATECTL(sc)) ieee80211_ratectl_deinit(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static struct mbuf * urtwn_rx_copy_to_mbuf(struct urtwn_softc *sc, struct r92c_rx_stat *stat, int totlen) { struct ieee80211com *ic = &sc->sc_ic; struct mbuf *m; uint32_t rxdw0; int pktlen; /* * don't pass packets to the ieee80211 framework if the driver isn't * RUNNING. */ if (!(sc->sc_flags & URTWN_RUNNING)) return (NULL); rxdw0 = le32toh(stat->rxdw0); if (rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR)) { /* * This should not happen since we setup our Rx filter * to not receive these frames. */ URTWN_DPRINTF(sc, URTWN_DEBUG_RECV, "%s: RX flags error (%s)\n", __func__, rxdw0 & R92C_RXDW0_CRCERR ? "CRC" : "ICV"); goto fail; } pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); if (pktlen < sizeof(struct ieee80211_frame_ack)) { URTWN_DPRINTF(sc, URTWN_DEBUG_RECV, "%s: frame is too short: %d\n", __func__, pktlen); goto fail; } if (__predict_false(totlen > MCLBYTES)) { /* convert to m_getjcl if this happens */ device_printf(sc->sc_dev, "%s: frame too long: %d (%d)\n", __func__, pktlen, totlen); goto fail; } m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m == NULL)) { device_printf(sc->sc_dev, "%s: could not allocate RX mbuf\n", __func__); goto fail; } /* Finalize mbuf. */ memcpy(mtod(m, uint8_t *), (uint8_t *)stat, totlen); m->m_pkthdr.len = m->m_len = totlen; return (m); fail: counter_u64_add(ic->ic_ierrors, 1); return (NULL); } static struct mbuf * urtwn_report_intr(struct usb_xfer *xfer, struct urtwn_data *data) { struct urtwn_softc *sc = data->sc; struct ieee80211com *ic = &sc->sc_ic; struct r92c_rx_stat *stat; uint8_t *buf; int len; usbd_xfer_status(xfer, &len, NULL, NULL, NULL); if (len < sizeof(*stat)) { counter_u64_add(ic->ic_ierrors, 1); return (NULL); } buf = data->buf; stat = (struct r92c_rx_stat *)buf; /* * For 88E chips we can tie the FF flushing here; * this is where we do know exactly how deep the * transmit queue is. * * But it won't work for R92 chips, so we can't * take the easy way out. */ if (sc->chip & URTWN_CHIP_88E) { int report_sel = MS(le32toh(stat->rxdw3), R88E_RXDW3_RPT); switch (report_sel) { case R88E_RXDW3_RPT_RX: return (urtwn_rxeof(sc, buf, len)); case R88E_RXDW3_RPT_TX1: urtwn_r88e_ratectl_tx_complete(sc, &stat[1]); break; default: URTWN_DPRINTF(sc, URTWN_DEBUG_INTR, "%s: case %d was not handled\n", __func__, report_sel); break; } } else return (urtwn_rxeof(sc, buf, len)); return (NULL); } static struct mbuf * urtwn_rxeof(struct urtwn_softc *sc, uint8_t *buf, int len) { struct r92c_rx_stat *stat; struct mbuf *m, *m0 = NULL, *prevm = NULL; uint32_t rxdw0; int totlen, pktlen, infosz, npkts; /* Get the number of encapsulated frames. */ stat = (struct r92c_rx_stat *)buf; npkts = MS(le32toh(stat->rxdw2), R92C_RXDW2_PKTCNT); URTWN_DPRINTF(sc, URTWN_DEBUG_RECV, "%s: Rx %d frames in one chunk\n", __func__, npkts); /* Process all of them. */ while (npkts-- > 0) { if (len < sizeof(*stat)) break; stat = (struct r92c_rx_stat *)buf; rxdw0 = le32toh(stat->rxdw0); pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); if (pktlen == 0) break; infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; /* Make sure everything fits in xfer. */ totlen = sizeof(*stat) + infosz + pktlen; if (totlen > len) break; m = urtwn_rx_copy_to_mbuf(sc, stat, totlen); if (m0 == NULL) m0 = m; if (prevm == NULL) prevm = m; else { prevm->m_next = m; prevm = m; } /* Next chunk is 128-byte aligned. */ totlen = (totlen + 127) & ~127; buf += totlen; len -= totlen; } return (m0); } static void urtwn_r88e_ratectl_tx_complete(struct urtwn_softc *sc, void *arg) { struct r88e_tx_rpt_ccx *rpt = arg; struct ieee80211vap *vap; struct ieee80211_node *ni; uint8_t macid; int ntries; macid = MS(rpt->rptb1, R88E_RPTB1_MACID); ntries = MS(rpt->rptb2, R88E_RPTB2_RETRY_CNT); URTWN_NT_LOCK(sc); ni = sc->node_list[macid]; if (ni != NULL) { vap = ni->ni_vap; URTWN_DPRINTF(sc, URTWN_DEBUG_INTR, "%s: frame for macid %d was" "%s sent (%d retries)\n", __func__, macid, (rpt->rptb1 & R88E_RPTB1_PKT_OK) ? "" : " not", ntries); if (rpt->rptb1 & R88E_RPTB1_PKT_OK) { ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &ntries, NULL); } else { ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &ntries, NULL); } } else { URTWN_DPRINTF(sc, URTWN_DEBUG_INTR, "%s: macid %d, ni is NULL\n", __func__, macid); } URTWN_NT_UNLOCK(sc); } static struct ieee80211_node * urtwn_rx_frame(struct urtwn_softc *sc, struct mbuf *m, int8_t *rssi_p) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame_min *wh; struct r92c_rx_stat *stat; uint32_t rxdw0, rxdw3; uint8_t rate, cipher; int8_t rssi = -127; int infosz; stat = mtod(m, struct r92c_rx_stat *); rxdw0 = le32toh(stat->rxdw0); rxdw3 = le32toh(stat->rxdw3); rate = MS(rxdw3, R92C_RXDW3_RATE); cipher = MS(rxdw0, R92C_RXDW0_CIPHER); infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; /* Get RSSI from PHY status descriptor if present. */ if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) { if (sc->chip & URTWN_CHIP_88E) rssi = urtwn_r88e_get_rssi(sc, rate, &stat[1]); else rssi = urtwn_get_rssi(sc, rate, &stat[1]); URTWN_DPRINTF(sc, URTWN_DEBUG_RSSI, "%s: rssi=%d\n", __func__, rssi); /* Update our average RSSI. */ urtwn_update_avgrssi(sc, rate, rssi); } if (ieee80211_radiotap_active(ic)) { struct urtwn_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; urtwn_get_tsf(sc, &tap->wr_tsft); if (__predict_false(le32toh((uint32_t)tap->wr_tsft) < le32toh(stat->rxdw5))) { tap->wr_tsft = le32toh(tap->wr_tsft >> 32) - 1; tap->wr_tsft = (uint64_t)htole32(tap->wr_tsft) << 32; } else tap->wr_tsft &= 0xffffffff00000000; tap->wr_tsft += stat->rxdw5; /* XXX 20/40? */ /* XXX shortgi? */ /* Map HW rate index to 802.11 rate. */ if (!(rxdw3 & R92C_RXDW3_HT)) { tap->wr_rate = ridx2rate[rate]; } else if (rate >= 12) { /* MCS0~15. */ /* Bit 7 set means HT MCS instead of rate. */ tap->wr_rate = 0x80 | (rate - 12); } /* XXX TODO: this isn't right; should use the last good RSSI */ tap->wr_dbm_antsignal = rssi; tap->wr_dbm_antnoise = URTWN_NOISE_FLOOR; } *rssi_p = rssi; /* Drop descriptor. */ m_adj(m, sizeof(*stat) + infosz); wh = mtod(m, struct ieee80211_frame_min *); if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && cipher != R92C_CAM_ALGO_NONE) { m->m_flags |= M_WEP; } if (m->m_len >= sizeof(*wh)) return (ieee80211_find_rxnode(ic, wh)); return (NULL); } static void urtwn_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtwn_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct mbuf *m = NULL, *next; struct urtwn_data *data; int8_t nf, rssi; URTWN_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_rx_active); if (data == NULL) goto tr_setup; STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); m = urtwn_report_intr(xfer, data); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&sc->sc_rx_inactive); if (data == NULL) { KASSERT(m == NULL, ("mbuf isn't NULL")); goto finish; } STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); /* * To avoid LOR we should unlock our private mutex here to call * ieee80211_input() because here is at the end of a USB * callback and safe to unlock. */ while (m != NULL) { next = m->m_next; m->m_next = NULL; ni = urtwn_rx_frame(sc, m, &rssi); /* Store a global last-good RSSI */ if (rssi != -127) sc->last_rssi = rssi; URTWN_UNLOCK(sc); nf = URTWN_NOISE_FLOOR; if (ni != NULL) { if (rssi != -127) URTWN_NODE(ni)->last_rssi = rssi; if (ni->ni_flags & IEEE80211_NODE_HT) m->m_flags |= M_AMPDU; (void)ieee80211_input(ni, m, URTWN_NODE(ni)->last_rssi - nf, nf); ieee80211_free_node(ni); } else { /* Use last good global RSSI */ (void)ieee80211_input_all(ic, m, sc->last_rssi - nf, nf); } URTWN_LOCK(sc); m = next; } break; default: /* needs it to the inactive queue due to a error. */ data = STAILQ_FIRST(&sc->sc_rx_active); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } break; } finish: /* Finished receive; age anything left on the FF queue by a little bump */ /* * XXX TODO: just make this a callout timer schedule so we can * flush the FF staging queue if we're approaching idle. */ #ifdef IEEE80211_SUPPORT_SUPERG URTWN_UNLOCK(sc); ieee80211_ff_age_all(ic, 1); URTWN_LOCK(sc); #endif /* Kick-start more transmit in case we stalled */ urtwn_start(sc); } static void urtwn_txeof(struct urtwn_softc *sc, struct urtwn_data *data, int status) { URTWN_ASSERT_LOCKED(sc); if (data->ni != NULL) /* not a beacon frame */ ieee80211_tx_complete(data->ni, data->m, status); if (sc->sc_tx_n_active > 0) sc->sc_tx_n_active--; data->ni = NULL; data->m = NULL; sc->sc_txtimer = 0; STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); } static int urtwn_alloc_list(struct urtwn_softc *sc, struct urtwn_data data[], int ndata, int maxsz) { int i, error; for (i = 0; i < ndata; i++) { struct urtwn_data *dp = &data[i]; dp->sc = sc; dp->m = NULL; dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); if (dp->buf == NULL) { device_printf(sc->sc_dev, "could not allocate buffer\n"); error = ENOMEM; goto fail; } dp->ni = NULL; } return (0); fail: urtwn_free_list(sc, data, ndata); return (error); } static int urtwn_alloc_rx_list(struct urtwn_softc *sc) { int error, i; error = urtwn_alloc_list(sc, sc->sc_rx, URTWN_RX_LIST_COUNT, URTWN_RXBUFSZ); if (error != 0) return (error); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); for (i = 0; i < URTWN_RX_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); return (0); } static int urtwn_alloc_tx_list(struct urtwn_softc *sc) { int error, i; error = urtwn_alloc_list(sc, sc->sc_tx, URTWN_TX_LIST_COUNT, URTWN_TXBUFSZ); if (error != 0) return (error); STAILQ_INIT(&sc->sc_tx_active); STAILQ_INIT(&sc->sc_tx_inactive); STAILQ_INIT(&sc->sc_tx_pending); for (i = 0; i < URTWN_TX_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); return (0); } static void urtwn_free_list(struct urtwn_softc *sc, struct urtwn_data data[], int ndata) { int i; for (i = 0; i < ndata; i++) { struct urtwn_data *dp = &data[i]; if (dp->buf != NULL) { free(dp->buf, M_USBDEV); dp->buf = NULL; } if (dp->ni != NULL) { ieee80211_free_node(dp->ni); dp->ni = NULL; } } } static void urtwn_free_rx_list(struct urtwn_softc *sc) { urtwn_free_list(sc, sc->sc_rx, URTWN_RX_LIST_COUNT); } static void urtwn_free_tx_list(struct urtwn_softc *sc) { urtwn_free_list(sc, sc->sc_tx, URTWN_TX_LIST_COUNT); } static void urtwn_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtwn_softc *sc = usbd_xfer_softc(xfer); #ifdef IEEE80211_SUPPORT_SUPERG struct ieee80211com *ic = &sc->sc_ic; #endif struct urtwn_data *data; URTWN_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)){ case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto tr_setup; STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); urtwn_txeof(sc, data, 0); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&sc->sc_tx_pending); if (data == NULL) { URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: empty pending queue\n", __func__); sc->sc_tx_n_active = 0; goto finish; } STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); usbd_transfer_submit(xfer); sc->sc_tx_n_active++; break; default: data = STAILQ_FIRST(&sc->sc_tx_active); if (data == NULL) goto tr_setup; STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); urtwn_txeof(sc, data, 1); if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto tr_setup; } break; } finish: #ifdef IEEE80211_SUPPORT_SUPERG /* * If the TX active queue drops below a certain * threshold, ensure we age fast-frames out so they're * transmitted. */ if (sc->sc_tx_n_active <= 1) { /* XXX ew - net80211 should defer this for us! */ /* * Note: this sc_tx_n_active currently tracks * the number of pending transmit submissions * and not the actual depth of the TX frames * pending to the hardware. That means that * we're going to end up with some sub-optimal * aggregation behaviour. */ /* * XXX TODO: just make this a callout timer schedule so we can * flush the FF staging queue if we're approaching idle. */ URTWN_UNLOCK(sc); ieee80211_ff_flush(ic, WME_AC_VO); ieee80211_ff_flush(ic, WME_AC_VI); ieee80211_ff_flush(ic, WME_AC_BE); ieee80211_ff_flush(ic, WME_AC_BK); URTWN_LOCK(sc); } #endif /* Kick-start more transmit */ urtwn_start(sc); } static struct urtwn_data * _urtwn_getbuf(struct urtwn_softc *sc) { struct urtwn_data *bf; bf = STAILQ_FIRST(&sc->sc_tx_inactive); if (bf != NULL) STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); else { URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: out of xmit buffers\n", __func__); } return (bf); } static struct urtwn_data * urtwn_getbuf(struct urtwn_softc *sc) { struct urtwn_data *bf; URTWN_ASSERT_LOCKED(sc); bf = _urtwn_getbuf(sc); if (bf == NULL) { URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: stop queue\n", __func__); } return (bf); } static usb_error_t urtwn_write_region_1(struct urtwn_softc *sc, uint16_t addr, uint8_t *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = R92C_REQ_REGS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); return (urtwn_do_request(sc, &req, buf)); } static usb_error_t urtwn_write_1(struct urtwn_softc *sc, uint16_t addr, uint8_t val) { return (urtwn_write_region_1(sc, addr, &val, sizeof(val))); } static usb_error_t urtwn_write_2(struct urtwn_softc *sc, uint16_t addr, uint16_t val) { val = htole16(val); return (urtwn_write_region_1(sc, addr, (uint8_t *)&val, sizeof(val))); } static usb_error_t urtwn_write_4(struct urtwn_softc *sc, uint16_t addr, uint32_t val) { val = htole32(val); return (urtwn_write_region_1(sc, addr, (uint8_t *)&val, sizeof(val))); } static usb_error_t urtwn_read_region_1(struct urtwn_softc *sc, uint16_t addr, uint8_t *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = R92C_REQ_REGS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); return (urtwn_do_request(sc, &req, buf)); } static uint8_t urtwn_read_1(struct urtwn_softc *sc, uint16_t addr) { uint8_t val; if (urtwn_read_region_1(sc, addr, &val, 1) != 0) return (0xff); return (val); } static uint16_t urtwn_read_2(struct urtwn_softc *sc, uint16_t addr) { uint16_t val; if (urtwn_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0) return (0xffff); return (le16toh(val)); } static uint32_t urtwn_read_4(struct urtwn_softc *sc, uint16_t addr) { uint32_t val; if (urtwn_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0) return (0xffffffff); return (le32toh(val)); } static int urtwn_fw_cmd(struct urtwn_softc *sc, uint8_t id, const void *buf, int len) { struct r92c_fw_cmd cmd; usb_error_t error; int ntries; if (!(sc->sc_flags & URTWN_FW_LOADED)) { URTWN_DPRINTF(sc, URTWN_DEBUG_FIRMWARE, "%s: firmware " "was not loaded; command (id %d) will be discarded\n", __func__, id); return (0); } /* Wait for current FW box to be empty. */ for (ntries = 0; ntries < 100; ntries++) { if (!(urtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur))) break; urtwn_ms_delay(sc); } if (ntries == 100) { device_printf(sc->sc_dev, "could not send firmware command\n"); return (ETIMEDOUT); } memset(&cmd, 0, sizeof(cmd)); cmd.id = id; if (len > 3) cmd.id |= R92C_CMD_FLAG_EXT; KASSERT(len <= sizeof(cmd.msg), ("urtwn_fw_cmd\n")); memcpy(cmd.msg, buf, len); /* Write the first word last since that will trigger the FW. */ error = urtwn_write_region_1(sc, R92C_HMEBOX_EXT(sc->fwcur), (uint8_t *)&cmd + 4, 2); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); error = urtwn_write_region_1(sc, R92C_HMEBOX(sc->fwcur), (uint8_t *)&cmd + 0, 4); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); sc->fwcur = (sc->fwcur + 1) % R92C_H2C_NBOX; return (0); } static void urtwn_cmdq_cb(void *arg, int pending) { struct urtwn_softc *sc = arg; struct urtwn_cmdq *item; /* * Device must be powered on (via urtwn_power_on()) * before any command may be sent. */ URTWN_LOCK(sc); if (!(sc->sc_flags & URTWN_RUNNING)) { URTWN_UNLOCK(sc); return; } URTWN_CMDQ_LOCK(sc); while (sc->cmdq[sc->cmdq_first].func != NULL) { item = &sc->cmdq[sc->cmdq_first]; sc->cmdq_first = (sc->cmdq_first + 1) % URTWN_CMDQ_SIZE; URTWN_CMDQ_UNLOCK(sc); item->func(sc, &item->data); URTWN_CMDQ_LOCK(sc); memset(item, 0, sizeof (*item)); } URTWN_CMDQ_UNLOCK(sc); URTWN_UNLOCK(sc); } static int urtwn_cmd_sleepable(struct urtwn_softc *sc, const void *ptr, size_t len, CMD_FUNC_PROTO) { struct ieee80211com *ic = &sc->sc_ic; KASSERT(len <= sizeof(union sec_param), ("buffer overflow")); URTWN_CMDQ_LOCK(sc); if (sc->cmdq[sc->cmdq_last].func != NULL) { device_printf(sc->sc_dev, "%s: cmdq overflow\n", __func__); URTWN_CMDQ_UNLOCK(sc); return (EAGAIN); } if (ptr != NULL) memcpy(&sc->cmdq[sc->cmdq_last].data, ptr, len); sc->cmdq[sc->cmdq_last].func = func; sc->cmdq_last = (sc->cmdq_last + 1) % URTWN_CMDQ_SIZE; URTWN_CMDQ_UNLOCK(sc); ieee80211_runtask(ic, &sc->cmdq_task); return (0); } static __inline void urtwn_rf_write(struct urtwn_softc *sc, int chain, uint8_t addr, uint32_t val) { sc->sc_rf_write(sc, chain, addr, val); } static void urtwn_r92c_rf_write(struct urtwn_softc *sc, int chain, uint8_t addr, uint32_t val) { urtwn_bb_write(sc, R92C_LSSI_PARAM(chain), SM(R92C_LSSI_PARAM_ADDR, addr) | SM(R92C_LSSI_PARAM_DATA, val)); } static void urtwn_r88e_rf_write(struct urtwn_softc *sc, int chain, uint8_t addr, uint32_t val) { urtwn_bb_write(sc, R92C_LSSI_PARAM(chain), SM(R88E_LSSI_PARAM_ADDR, addr) | SM(R92C_LSSI_PARAM_DATA, val)); } static uint32_t urtwn_rf_read(struct urtwn_softc *sc, int chain, uint8_t addr) { uint32_t reg[R92C_MAX_CHAINS], val; reg[0] = urtwn_bb_read(sc, R92C_HSSI_PARAM2(0)); if (chain != 0) reg[chain] = urtwn_bb_read(sc, R92C_HSSI_PARAM2(chain)); urtwn_bb_write(sc, R92C_HSSI_PARAM2(0), reg[0] & ~R92C_HSSI_PARAM2_READ_EDGE); urtwn_ms_delay(sc); urtwn_bb_write(sc, R92C_HSSI_PARAM2(chain), RW(reg[chain], R92C_HSSI_PARAM2_READ_ADDR, addr) | R92C_HSSI_PARAM2_READ_EDGE); urtwn_ms_delay(sc); urtwn_bb_write(sc, R92C_HSSI_PARAM2(0), reg[0] | R92C_HSSI_PARAM2_READ_EDGE); urtwn_ms_delay(sc); if (urtwn_bb_read(sc, R92C_HSSI_PARAM1(chain)) & R92C_HSSI_PARAM1_PI) val = urtwn_bb_read(sc, R92C_HSPI_READBACK(chain)); else val = urtwn_bb_read(sc, R92C_LSSI_READBACK(chain)); return (MS(val, R92C_LSSI_READBACK_DATA)); } static int urtwn_llt_write(struct urtwn_softc *sc, uint32_t addr, uint32_t data) { usb_error_t error; int ntries; error = urtwn_write_4(sc, R92C_LLT_INIT, SM(R92C_LLT_INIT_OP, R92C_LLT_INIT_OP_WRITE) | SM(R92C_LLT_INIT_ADDR, addr) | SM(R92C_LLT_INIT_DATA, data)); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Wait for write operation to complete. */ for (ntries = 0; ntries < 20; ntries++) { if (MS(urtwn_read_4(sc, R92C_LLT_INIT), R92C_LLT_INIT_OP) == R92C_LLT_INIT_OP_NO_ACTIVE) return (0); urtwn_ms_delay(sc); } return (ETIMEDOUT); } static int urtwn_efuse_read_next(struct urtwn_softc *sc, uint8_t *val) { uint32_t reg; usb_error_t error; int ntries; if (sc->last_rom_addr >= URTWN_EFUSE_MAX_LEN) return (EFAULT); reg = urtwn_read_4(sc, R92C_EFUSE_CTRL); reg = RW(reg, R92C_EFUSE_CTRL_ADDR, sc->last_rom_addr); reg &= ~R92C_EFUSE_CTRL_VALID; error = urtwn_write_4(sc, R92C_EFUSE_CTRL, reg); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Wait for read operation to complete. */ for (ntries = 0; ntries < 100; ntries++) { reg = urtwn_read_4(sc, R92C_EFUSE_CTRL); if (reg & R92C_EFUSE_CTRL_VALID) break; urtwn_ms_delay(sc); } if (ntries == 100) { device_printf(sc->sc_dev, "could not read efuse byte at address 0x%x\n", sc->last_rom_addr); return (ETIMEDOUT); } *val = MS(reg, R92C_EFUSE_CTRL_DATA); sc->last_rom_addr++; return (0); } static int urtwn_efuse_read_data(struct urtwn_softc *sc, uint8_t *rom, uint8_t off, uint8_t msk) { uint8_t reg; int i, error; for (i = 0; i < 4; i++) { if (msk & (1 << i)) continue; error = urtwn_efuse_read_next(sc, ®); if (error != 0) return (error); URTWN_DPRINTF(sc, URTWN_DEBUG_ROM, "rom[0x%03X] == 0x%02X\n", off * 8 + i * 2, reg); rom[off * 8 + i * 2 + 0] = reg; error = urtwn_efuse_read_next(sc, ®); if (error != 0) return (error); URTWN_DPRINTF(sc, URTWN_DEBUG_ROM, "rom[0x%03X] == 0x%02X\n", off * 8 + i * 2 + 1, reg); rom[off * 8 + i * 2 + 1] = reg; } return (0); } #ifdef USB_DEBUG static void urtwn_dump_rom_contents(struct urtwn_softc *sc, uint8_t *rom, uint16_t size) { int i; /* Dump ROM contents. */ device_printf(sc->sc_dev, "%s:", __func__); for (i = 0; i < size; i++) { if (i % 32 == 0) printf("\n%03X: ", i); else if (i % 4 == 0) printf(" "); printf("%02X", rom[i]); } printf("\n"); } #endif static int urtwn_efuse_read(struct urtwn_softc *sc, uint8_t *rom, uint16_t size) { #define URTWN_CHK(res) do { \ if ((error = res) != 0) \ goto end; \ } while(0) uint8_t msk, off, reg; int error; URTWN_CHK(urtwn_efuse_switch_power(sc)); /* Read full ROM image. */ sc->last_rom_addr = 0; memset(rom, 0xff, size); URTWN_CHK(urtwn_efuse_read_next(sc, ®)); while (reg != 0xff) { /* check for extended header */ if ((sc->chip & URTWN_CHIP_88E) && (reg & 0x1f) == 0x0f) { off = reg >> 5; URTWN_CHK(urtwn_efuse_read_next(sc, ®)); if ((reg & 0x0f) != 0x0f) off = ((reg & 0xf0) >> 1) | off; else continue; } else off = reg >> 4; msk = reg & 0xf; URTWN_CHK(urtwn_efuse_read_data(sc, rom, off, msk)); URTWN_CHK(urtwn_efuse_read_next(sc, ®)); } end: #ifdef USB_DEBUG if (sc->sc_debug & URTWN_DEBUG_ROM) urtwn_dump_rom_contents(sc, rom, size); #endif urtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_OFF); if (error != 0) { device_printf(sc->sc_dev, "%s: error while reading ROM\n", __func__); } return (error); #undef URTWN_CHK } static int urtwn_efuse_switch_power(struct urtwn_softc *sc) { usb_error_t error; uint32_t reg; error = urtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_ON); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); reg = urtwn_read_2(sc, R92C_SYS_ISO_CTRL); if (!(reg & R92C_SYS_ISO_CTRL_PWC_EV12V)) { error = urtwn_write_2(sc, R92C_SYS_ISO_CTRL, reg | R92C_SYS_ISO_CTRL_PWC_EV12V); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); } reg = urtwn_read_2(sc, R92C_SYS_FUNC_EN); if (!(reg & R92C_SYS_FUNC_EN_ELDR)) { error = urtwn_write_2(sc, R92C_SYS_FUNC_EN, reg | R92C_SYS_FUNC_EN_ELDR); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); } reg = urtwn_read_2(sc, R92C_SYS_CLKR); if ((reg & (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) != (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) { error = urtwn_write_2(sc, R92C_SYS_CLKR, reg | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); } return (0); } static int urtwn_read_chipid(struct urtwn_softc *sc) { uint32_t reg; if (sc->chip & URTWN_CHIP_88E) return (0); reg = urtwn_read_4(sc, R92C_SYS_CFG); if (reg & R92C_SYS_CFG_TRP_VAUX_EN) return (EIO); if (reg & R92C_SYS_CFG_TYPE_92C) { sc->chip |= URTWN_CHIP_92C; /* Check if it is a castrated 8192C. */ if (MS(urtwn_read_4(sc, R92C_HPON_FSM), R92C_HPON_FSM_CHIP_BONDING_ID) == R92C_HPON_FSM_CHIP_BONDING_ID_92C_1T2R) sc->chip |= URTWN_CHIP_92C_1T2R; } if (reg & R92C_SYS_CFG_VENDOR_UMC) { sc->chip |= URTWN_CHIP_UMC; if (MS(reg, R92C_SYS_CFG_CHIP_VER_RTL) == 0) sc->chip |= URTWN_CHIP_UMC_A_CUT; } return (0); } static int urtwn_read_rom(struct urtwn_softc *sc) { struct r92c_rom *rom = &sc->rom.r92c_rom; int error; /* Read full ROM image. */ error = urtwn_efuse_read(sc, (uint8_t *)rom, sizeof(*rom)); if (error != 0) return (error); /* XXX Weird but this is what the vendor driver does. */ sc->last_rom_addr = 0x1fa; error = urtwn_efuse_read_next(sc, &sc->pa_setting); if (error != 0) return (error); URTWN_DPRINTF(sc, URTWN_DEBUG_ROM, "%s: PA setting=0x%x\n", __func__, sc->pa_setting); sc->board_type = MS(rom->rf_opt1, R92C_ROM_RF1_BOARD_TYPE); sc->regulatory = MS(rom->rf_opt1, R92C_ROM_RF1_REGULATORY); URTWN_DPRINTF(sc, URTWN_DEBUG_ROM, "%s: regulatory type=%d\n", __func__, sc->regulatory); IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, rom->macaddr); sc->sc_rf_write = urtwn_r92c_rf_write; sc->sc_power_on = urtwn_r92c_power_on; sc->sc_power_off = urtwn_r92c_power_off; return (0); } static int urtwn_r88e_read_rom(struct urtwn_softc *sc) { struct r88e_rom *rom = &sc->rom.r88e_rom; int error; error = urtwn_efuse_read(sc, (uint8_t *)rom, sizeof(sc->rom.r88e_rom)); if (error != 0) return (error); sc->bw20_tx_pwr_diff = (rom->tx_pwr_diff >> 4); if (sc->bw20_tx_pwr_diff & 0x08) sc->bw20_tx_pwr_diff |= 0xf0; sc->ofdm_tx_pwr_diff = (rom->tx_pwr_diff & 0xf); if (sc->ofdm_tx_pwr_diff & 0x08) sc->ofdm_tx_pwr_diff |= 0xf0; sc->regulatory = MS(rom->rf_board_opt, R92C_ROM_RF1_REGULATORY); URTWN_DPRINTF(sc, URTWN_DEBUG_ROM, "%s: regulatory type %d\n", __func__,sc->regulatory); IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, rom->macaddr); sc->sc_rf_write = urtwn_r88e_rf_write; sc->sc_power_on = urtwn_r88e_power_on; sc->sc_power_off = urtwn_r88e_power_off; return (0); } static __inline uint8_t rate2ridx(uint8_t rate) { if (rate & IEEE80211_RATE_MCS) { /* 11n rates start at idx 12 */ return ((rate & 0xf) + 12); } switch (rate) { /* 11g */ case 12: return 4; case 18: return 5; case 24: return 6; case 36: return 7; case 48: return 8; case 72: return 9; case 96: return 10; case 108: return 11; /* 11b */ case 2: return 0; case 4: return 1; case 11: return 2; case 22: return 3; default: return URTWN_RIDX_UNKNOWN; } } /* * Initialize rate adaptation in firmware. */ static int urtwn_ra_init(struct urtwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni; struct ieee80211_rateset *rs, *rs_ht; struct r92c_fw_cmd_macid_cfg cmd; uint32_t rates, basicrates; uint8_t mode, ridx; int maxrate, maxbasicrate, error, i; ni = ieee80211_ref_node(vap->iv_bss); rs = &ni->ni_rates; rs_ht = (struct ieee80211_rateset *) &ni->ni_htrates; /* Get normal and basic rates mask. */ rates = basicrates = 0; maxrate = maxbasicrate = 0; /* This is for 11bg */ for (i = 0; i < rs->rs_nrates; i++) { /* Convert 802.11 rate to HW rate index. */ ridx = rate2ridx(IEEE80211_RV(rs->rs_rates[i])); if (ridx == URTWN_RIDX_UNKNOWN) /* Unknown rate, skip. */ continue; rates |= 1 << ridx; if (ridx > maxrate) maxrate = ridx; if (rs->rs_rates[i] & IEEE80211_RATE_BASIC) { basicrates |= 1 << ridx; if (ridx > maxbasicrate) maxbasicrate = ridx; } } /* If we're doing 11n, enable 11n rates */ if (ni->ni_flags & IEEE80211_NODE_HT) { for (i = 0; i < rs_ht->rs_nrates; i++) { if ((rs_ht->rs_rates[i] & 0x7f) > 0xf) continue; /* 11n rates start at index 12 */ ridx = ((rs_ht->rs_rates[i]) & 0xf) + 12; rates |= (1 << ridx); /* Guard against the rate table being oddly ordered */ if (ridx > maxrate) maxrate = ridx; } } #if 0 if (ic->ic_curmode == IEEE80211_MODE_11NG) raid = R92C_RAID_11GN; #endif /* NB: group addressed frames are done at 11bg rates for now */ if (ic->ic_curmode == IEEE80211_MODE_11B) mode = R92C_RAID_11B; else mode = R92C_RAID_11BG; /* XXX misleading 'mode' value here for unicast frames */ URTWN_DPRINTF(sc, URTWN_DEBUG_RA, "%s: mode 0x%x, rates 0x%08x, basicrates 0x%08x\n", __func__, mode, rates, basicrates); /* Set rates mask for group addressed frames. */ cmd.macid = URTWN_MACID_BC | URTWN_MACID_VALID; cmd.mask = htole32(mode << 28 | basicrates); error = urtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); if (error != 0) { ieee80211_free_node(ni); device_printf(sc->sc_dev, "could not add broadcast station\n"); return (error); } /* Set initial MRR rate. */ URTWN_DPRINTF(sc, URTWN_DEBUG_RA, "%s: maxbasicrate %d\n", __func__, maxbasicrate); urtwn_write_1(sc, R92C_INIDATA_RATE_SEL(URTWN_MACID_BC), maxbasicrate); /* Set rates mask for unicast frames. */ if (ni->ni_flags & IEEE80211_NODE_HT) mode = R92C_RAID_11GN; else if (ic->ic_curmode == IEEE80211_MODE_11B) mode = R92C_RAID_11B; else mode = R92C_RAID_11BG; cmd.macid = URTWN_MACID_BSS | URTWN_MACID_VALID; cmd.mask = htole32(mode << 28 | rates); error = urtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); if (error != 0) { ieee80211_free_node(ni); device_printf(sc->sc_dev, "could not add BSS station\n"); return (error); } /* Set initial MRR rate. */ URTWN_DPRINTF(sc, URTWN_DEBUG_RA, "%s: maxrate %d\n", __func__, maxrate); urtwn_write_1(sc, R92C_INIDATA_RATE_SEL(URTWN_MACID_BSS), maxrate); /* Indicate highest supported rate. */ if (ni->ni_flags & IEEE80211_NODE_HT) ni->ni_txrate = rs_ht->rs_rates[rs_ht->rs_nrates - 1] | IEEE80211_RATE_MCS; else ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1]; ieee80211_free_node(ni); return (0); } static void urtwn_init_beacon(struct urtwn_softc *sc, struct urtwn_vap *uvp) { struct r92c_tx_desc *txd = &uvp->bcn_desc; txd->txdw0 = htole32( SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | R92C_TXDW0_BMCAST | R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG); txd->txdw1 = htole32( SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BEACON) | SM(R92C_TXDW1_RAID, R92C_RAID_11B)); if (sc->chip & URTWN_CHIP_88E) { txd->txdw1 |= htole32(SM(R88E_TXDW1_MACID, URTWN_MACID_BC)); txd->txdseq |= htole16(R88E_TXDSEQ_HWSEQ_EN); } else { txd->txdw1 |= htole32(SM(R92C_TXDW1_MACID, URTWN_MACID_BC)); txd->txdw4 |= htole32(R92C_TXDW4_HWSEQ_EN); } txd->txdw4 = htole32(R92C_TXDW4_DRVRATE); txd->txdw5 = htole32(SM(R92C_TXDW5_DATARATE, URTWN_RIDX_CCK1)); } static int urtwn_setup_beacon(struct urtwn_softc *sc, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct urtwn_vap *uvp = URTWN_VAP(vap); struct mbuf *m; int error; URTWN_ASSERT_LOCKED(sc); if (ni->ni_chan == IEEE80211_CHAN_ANYC) return (EINVAL); m = ieee80211_beacon_alloc(ni); if (m == NULL) { device_printf(sc->sc_dev, "%s: could not allocate beacon frame\n", __func__); return (ENOMEM); } if (uvp->bcn_mbuf != NULL) m_freem(uvp->bcn_mbuf); uvp->bcn_mbuf = m; if ((error = urtwn_tx_beacon(sc, uvp)) != 0) return (error); /* XXX bcnq stuck workaround */ if ((error = urtwn_tx_beacon(sc, uvp)) != 0) return (error); URTWN_DPRINTF(sc, URTWN_DEBUG_BEACON, "%s: beacon was %srecognized\n", __func__, urtwn_read_1(sc, R92C_TDECTRL + 2) & (R92C_TDECTRL_BCN_VALID >> 16) ? "" : "not "); return (0); } static void urtwn_update_beacon(struct ieee80211vap *vap, int item) { struct urtwn_softc *sc = vap->iv_ic->ic_softc; struct urtwn_vap *uvp = URTWN_VAP(vap); struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; struct ieee80211_node *ni = vap->iv_bss; int mcast = 0; URTWN_LOCK(sc); if (uvp->bcn_mbuf == NULL) { uvp->bcn_mbuf = ieee80211_beacon_alloc(ni); if (uvp->bcn_mbuf == NULL) { device_printf(sc->sc_dev, "%s: could not allocate beacon frame\n", __func__); URTWN_UNLOCK(sc); return; } } URTWN_UNLOCK(sc); if (item == IEEE80211_BEACON_TIM) mcast = 1; /* XXX */ setbit(bo->bo_flags, item); ieee80211_beacon_update(ni, uvp->bcn_mbuf, mcast); URTWN_LOCK(sc); urtwn_tx_beacon(sc, uvp); URTWN_UNLOCK(sc); } /* * Push a beacon frame into the chip. Beacon will * be repeated by the chip every R92C_BCN_INTERVAL. */ static int urtwn_tx_beacon(struct urtwn_softc *sc, struct urtwn_vap *uvp) { struct r92c_tx_desc *desc = &uvp->bcn_desc; struct urtwn_data *bf; URTWN_ASSERT_LOCKED(sc); bf = urtwn_getbuf(sc); if (bf == NULL) return (ENOMEM); memcpy(bf->buf, desc, sizeof(*desc)); urtwn_tx_start(sc, uvp->bcn_mbuf, IEEE80211_FC0_TYPE_MGT, bf); sc->sc_txtimer = 5; callout_reset(&sc->sc_watchdog_ch, hz, urtwn_watchdog, sc); return (0); } static int urtwn_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) { struct urtwn_softc *sc = vap->iv_ic->ic_softc; uint8_t i; if (!(&vap->iv_nw_keys[0] <= k && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { URTWN_LOCK(sc); /* * First 4 slots for group keys, * what is left - for pairwise. * XXX incompatible with IBSS RSN. */ for (i = IEEE80211_WEP_NKID; i < R92C_CAM_ENTRY_COUNT; i++) { if ((sc->keys_bmap & (1 << i)) == 0) { sc->keys_bmap |= 1 << i; *keyix = i; break; } } URTWN_UNLOCK(sc); if (i == R92C_CAM_ENTRY_COUNT) { device_printf(sc->sc_dev, "%s: no free space in the key table\n", __func__); return 0; } } else *keyix = 0; } else { *keyix = k - vap->iv_nw_keys; } *rxkeyix = *keyix; return 1; } static void urtwn_key_set_cb(struct urtwn_softc *sc, union sec_param *data) { struct ieee80211_key *k = &data->key; uint8_t algo, keyid; int i, error; if (k->wk_keyix < IEEE80211_WEP_NKID) keyid = k->wk_keyix; else keyid = 0; /* Map net80211 cipher to HW crypto algorithm. */ switch (k->wk_cipher->ic_cipher) { case IEEE80211_CIPHER_WEP: if (k->wk_keylen < 8) algo = R92C_CAM_ALGO_WEP40; else algo = R92C_CAM_ALGO_WEP104; break; case IEEE80211_CIPHER_TKIP: algo = R92C_CAM_ALGO_TKIP; break; case IEEE80211_CIPHER_AES_CCM: algo = R92C_CAM_ALGO_AES; break; default: device_printf(sc->sc_dev, "%s: undefined cipher %d\n", __func__, k->wk_cipher->ic_cipher); return; } URTWN_DPRINTF(sc, URTWN_DEBUG_KEY, "%s: keyix %d, keyid %d, algo %d/%d, flags %04X, len %d, " "macaddr %s\n", __func__, k->wk_keyix, keyid, k->wk_cipher->ic_cipher, algo, k->wk_flags, k->wk_keylen, ether_sprintf(k->wk_macaddr)); /* Write key. */ for (i = 0; i < 4; i++) { error = urtwn_cam_write(sc, R92C_CAM_KEY(k->wk_keyix, i), le32dec(&k->wk_key[i * 4])); if (error != 0) goto fail; } /* Write CTL0 last since that will validate the CAM entry. */ error = urtwn_cam_write(sc, R92C_CAM_CTL1(k->wk_keyix), le32dec(&k->wk_macaddr[2])); if (error != 0) goto fail; error = urtwn_cam_write(sc, R92C_CAM_CTL0(k->wk_keyix), SM(R92C_CAM_ALGO, algo) | SM(R92C_CAM_KEYID, keyid) | SM(R92C_CAM_MACLO, le16dec(&k->wk_macaddr[0])) | R92C_CAM_VALID); if (error != 0) goto fail; return; fail: device_printf(sc->sc_dev, "%s fails, error %d\n", __func__, error); } static void urtwn_key_del_cb(struct urtwn_softc *sc, union sec_param *data) { struct ieee80211_key *k = &data->key; int i; URTWN_DPRINTF(sc, URTWN_DEBUG_KEY, "%s: keyix %d, flags %04X, macaddr %s\n", __func__, k->wk_keyix, k->wk_flags, ether_sprintf(k->wk_macaddr)); urtwn_cam_write(sc, R92C_CAM_CTL0(k->wk_keyix), 0); urtwn_cam_write(sc, R92C_CAM_CTL1(k->wk_keyix), 0); /* Clear key. */ for (i = 0; i < 4; i++) urtwn_cam_write(sc, R92C_CAM_KEY(k->wk_keyix, i), 0); sc->keys_bmap &= ~(1 << k->wk_keyix); } static int urtwn_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) { struct urtwn_softc *sc = vap->iv_ic->ic_softc; if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { /* Not for us. */ return (1); } return (!urtwn_cmd_sleepable(sc, k, sizeof(*k), urtwn_key_set_cb)); } static int urtwn_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) { struct urtwn_softc *sc = vap->iv_ic->ic_softc; if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { /* Not for us. */ return (1); } return (!urtwn_cmd_sleepable(sc, k, sizeof(*k), urtwn_key_del_cb)); } static void urtwn_tsf_task_adhoc(void *arg, int pending) { struct ieee80211vap *vap = arg; struct urtwn_softc *sc = vap->iv_ic->ic_softc; struct ieee80211_node *ni; uint32_t reg; URTWN_LOCK(sc); ni = ieee80211_ref_node(vap->iv_bss); reg = urtwn_read_1(sc, R92C_BCN_CTRL); /* Accept beacons with the same BSSID. */ urtwn_set_rx_bssid_all(sc, 0); /* Enable synchronization. */ reg &= ~R92C_BCN_CTRL_DIS_TSF_UDT0; urtwn_write_1(sc, R92C_BCN_CTRL, reg); /* Synchronize. */ usb_pause_mtx(&sc->sc_mtx, hz * ni->ni_intval * 5 / 1000); /* Disable synchronization. */ reg |= R92C_BCN_CTRL_DIS_TSF_UDT0; urtwn_write_1(sc, R92C_BCN_CTRL, reg); /* Remove beacon filter. */ urtwn_set_rx_bssid_all(sc, 1); /* Enable beaconing. */ urtwn_write_1(sc, R92C_MBID_NUM, urtwn_read_1(sc, R92C_MBID_NUM) | R92C_MBID_TXBCN_RPT0); reg |= R92C_BCN_CTRL_EN_BCN; urtwn_write_1(sc, R92C_BCN_CTRL, reg); ieee80211_free_node(ni); URTWN_UNLOCK(sc); } static void urtwn_tsf_sync_enable(struct urtwn_softc *sc, struct ieee80211vap *vap) { struct ieee80211com *ic = &sc->sc_ic; struct urtwn_vap *uvp = URTWN_VAP(vap); /* Reset TSF. */ urtwn_write_1(sc, R92C_DUAL_TSF_RST, R92C_DUAL_TSF_RST0); switch (vap->iv_opmode) { case IEEE80211_M_STA: /* Enable TSF synchronization. */ urtwn_write_1(sc, R92C_BCN_CTRL, urtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_DIS_TSF_UDT0); break; case IEEE80211_M_IBSS: ieee80211_runtask(ic, &uvp->tsf_task_adhoc); break; case IEEE80211_M_HOSTAP: /* Enable beaconing. */ urtwn_write_1(sc, R92C_MBID_NUM, urtwn_read_1(sc, R92C_MBID_NUM) | R92C_MBID_TXBCN_RPT0); urtwn_write_1(sc, R92C_BCN_CTRL, urtwn_read_1(sc, R92C_BCN_CTRL) | R92C_BCN_CTRL_EN_BCN); break; default: device_printf(sc->sc_dev, "undefined opmode %d\n", vap->iv_opmode); return; } } static void urtwn_get_tsf(struct urtwn_softc *sc, uint64_t *buf) { urtwn_read_region_1(sc, R92C_TSFTR, (uint8_t *)buf, sizeof(*buf)); } static void urtwn_set_led(struct urtwn_softc *sc, int led, int on) { uint8_t reg; if (led == URTWN_LED_LINK) { if (sc->chip & URTWN_CHIP_88E) { reg = urtwn_read_1(sc, R92C_LEDCFG2) & 0xf0; urtwn_write_1(sc, R92C_LEDCFG2, reg | 0x60); if (!on) { reg = urtwn_read_1(sc, R92C_LEDCFG2) & 0x90; urtwn_write_1(sc, R92C_LEDCFG2, reg | R92C_LEDCFG0_DIS); urtwn_write_1(sc, R92C_MAC_PINMUX_CFG, urtwn_read_1(sc, R92C_MAC_PINMUX_CFG) & 0xfe); } } else { reg = urtwn_read_1(sc, R92C_LEDCFG0) & 0x70; if (!on) reg |= R92C_LEDCFG0_DIS; urtwn_write_1(sc, R92C_LEDCFG0, reg); } sc->ledlink = on; /* Save LED state. */ } } static void urtwn_set_mode(struct urtwn_softc *sc, uint8_t mode) { uint8_t reg; reg = urtwn_read_1(sc, R92C_MSR); reg = (reg & ~R92C_MSR_MASK) | mode; urtwn_write_1(sc, R92C_MSR, reg); } static void urtwn_ibss_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf) { struct ieee80211vap *vap = ni->ni_vap; struct urtwn_softc *sc = vap->iv_ic->ic_softc; struct urtwn_vap *uvp = URTWN_VAP(vap); uint64_t ni_tstamp, curr_tstamp; uvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); if (vap->iv_state == IEEE80211_S_RUN && (subtype == IEEE80211_FC0_SUBTYPE_BEACON || subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { ni_tstamp = le64toh(ni->ni_tstamp.tsf); URTWN_LOCK(sc); urtwn_get_tsf(sc, &curr_tstamp); URTWN_UNLOCK(sc); curr_tstamp = le64toh(curr_tstamp); if (ni_tstamp >= curr_tstamp) (void) ieee80211_ibss_merge(ni); } } static int urtwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct urtwn_vap *uvp = URTWN_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct urtwn_softc *sc = ic->ic_softc; struct ieee80211_node *ni; enum ieee80211_state ostate; uint32_t reg; uint8_t mode; int error = 0; ostate = vap->iv_state; URTWN_DPRINTF(sc, URTWN_DEBUG_STATE, "%s -> %s\n", ieee80211_state_name[ostate], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); URTWN_LOCK(sc); callout_stop(&sc->sc_watchdog_ch); if (ostate == IEEE80211_S_RUN) { /* Stop calibration. */ callout_stop(&sc->sc_calib_to); /* Turn link LED off. */ urtwn_set_led(sc, URTWN_LED_LINK, 0); /* Set media status to 'No Link'. */ urtwn_set_mode(sc, R92C_MSR_NOLINK); /* Stop Rx of data frames. */ urtwn_write_2(sc, R92C_RXFLTMAP2, 0); /* Disable TSF synchronization. */ urtwn_write_1(sc, R92C_BCN_CTRL, (urtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_EN_BCN) | R92C_BCN_CTRL_DIS_TSF_UDT0); /* Disable beaconing. */ urtwn_write_1(sc, R92C_MBID_NUM, urtwn_read_1(sc, R92C_MBID_NUM) & ~R92C_MBID_TXBCN_RPT0); /* Reset TSF. */ urtwn_write_1(sc, R92C_DUAL_TSF_RST, R92C_DUAL_TSF_RST0); /* Reset EDCA parameters. */ urtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3217); urtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4317); urtwn_write_4(sc, R92C_EDCA_BE_PARAM, 0x00105320); urtwn_write_4(sc, R92C_EDCA_BK_PARAM, 0x0000a444); } switch (nstate) { case IEEE80211_S_INIT: /* Turn link LED off. */ urtwn_set_led(sc, URTWN_LED_LINK, 0); break; case IEEE80211_S_SCAN: /* Pause AC Tx queues. */ urtwn_write_1(sc, R92C_TXPAUSE, urtwn_read_1(sc, R92C_TXPAUSE) | R92C_TX_QUEUE_AC); break; case IEEE80211_S_AUTH: urtwn_set_chan(sc, ic->ic_curchan, NULL); break; case IEEE80211_S_RUN: if (vap->iv_opmode == IEEE80211_M_MONITOR) { /* Turn link LED on. */ urtwn_set_led(sc, URTWN_LED_LINK, 1); break; } ni = ieee80211_ref_node(vap->iv_bss); if (ic->ic_bsschan == IEEE80211_CHAN_ANYC || ni->ni_chan == IEEE80211_CHAN_ANYC) { device_printf(sc->sc_dev, "%s: could not move to RUN state\n", __func__); error = EINVAL; goto end_run; } switch (vap->iv_opmode) { case IEEE80211_M_STA: mode = R92C_MSR_INFRA; break; case IEEE80211_M_IBSS: mode = R92C_MSR_ADHOC; break; case IEEE80211_M_HOSTAP: mode = R92C_MSR_AP; break; default: device_printf(sc->sc_dev, "undefined opmode %d\n", vap->iv_opmode); error = EINVAL; goto end_run; } /* Set media status to 'Associated'. */ urtwn_set_mode(sc, mode); /* Set BSSID. */ urtwn_write_4(sc, R92C_BSSID + 0, le32dec(&ni->ni_bssid[0])); urtwn_write_4(sc, R92C_BSSID + 4, le16dec(&ni->ni_bssid[4])); if (ic->ic_curmode == IEEE80211_MODE_11B) urtwn_write_1(sc, R92C_INIRTS_RATE_SEL, 0); else /* 802.11b/g */ urtwn_write_1(sc, R92C_INIRTS_RATE_SEL, 3); /* Enable Rx of data frames. */ urtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); /* Flush all AC queues. */ urtwn_write_1(sc, R92C_TXPAUSE, 0); /* Set beacon interval. */ urtwn_write_2(sc, R92C_BCN_INTERVAL, ni->ni_intval); /* Allow Rx from our BSSID only. */ if (ic->ic_promisc == 0) { reg = urtwn_read_4(sc, R92C_RCR); if (vap->iv_opmode != IEEE80211_M_HOSTAP) reg |= R92C_RCR_CBSSID_DATA; if (vap->iv_opmode != IEEE80211_M_IBSS) reg |= R92C_RCR_CBSSID_BCN; urtwn_write_4(sc, R92C_RCR, reg); } if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS) { error = urtwn_setup_beacon(sc, ni); if (error != 0) { device_printf(sc->sc_dev, "unable to push beacon into the chip, " "error %d\n", error); goto end_run; } } /* Enable TSF synchronization. */ urtwn_tsf_sync_enable(sc, vap); urtwn_write_1(sc, R92C_SIFS_CCK + 1, 10); urtwn_write_1(sc, R92C_SIFS_OFDM + 1, 10); urtwn_write_1(sc, R92C_SPEC_SIFS + 1, 10); urtwn_write_1(sc, R92C_MAC_SPEC_SIFS + 1, 10); urtwn_write_1(sc, R92C_R2T_SIFS + 1, 10); urtwn_write_1(sc, R92C_T2T_SIFS + 1, 10); /* Intialize rate adaptation. */ if (!(sc->chip & URTWN_CHIP_88E)) urtwn_ra_init(sc); /* Turn link LED on. */ urtwn_set_led(sc, URTWN_LED_LINK, 1); sc->avg_pwdb = -1; /* Reset average RSSI. */ /* Reset temperature calibration state machine. */ sc->sc_flags &= ~URTWN_TEMP_MEASURED; sc->thcal_lctemp = 0; /* Start periodic calibration. */ callout_reset(&sc->sc_calib_to, 2*hz, urtwn_calib_to, sc); end_run: ieee80211_free_node(ni); break; default: break; } URTWN_UNLOCK(sc); IEEE80211_LOCK(ic); return (error != 0 ? error : uvp->newstate(vap, nstate, arg)); } static void urtwn_calib_to(void *arg) { struct urtwn_softc *sc = arg; /* Do it in a process context. */ urtwn_cmd_sleepable(sc, NULL, 0, urtwn_calib_cb); } static void urtwn_calib_cb(struct urtwn_softc *sc, union sec_param *data) { /* Do temperature compensation. */ urtwn_temp_calib(sc); if ((urtwn_read_1(sc, R92C_MSR) & R92C_MSR_MASK) != R92C_MSR_NOLINK) callout_reset(&sc->sc_calib_to, 2*hz, urtwn_calib_to, sc); } static void urtwn_watchdog(void *arg) { struct urtwn_softc *sc = arg; if (sc->sc_txtimer > 0) { if (--sc->sc_txtimer == 0) { device_printf(sc->sc_dev, "device timeout\n"); counter_u64_add(sc->sc_ic.ic_oerrors, 1); return; } callout_reset(&sc->sc_watchdog_ch, hz, urtwn_watchdog, sc); } } static void urtwn_update_avgrssi(struct urtwn_softc *sc, int rate, int8_t rssi) { int pwdb; /* Convert antenna signal to percentage. */ if (rssi <= -100 || rssi >= 20) pwdb = 0; else if (rssi >= 0) pwdb = 100; else pwdb = 100 + rssi; if (!(sc->chip & URTWN_CHIP_88E)) { if (rate <= URTWN_RIDX_CCK11) { /* CCK gain is smaller than OFDM/MCS gain. */ pwdb += 6; if (pwdb > 100) pwdb = 100; if (pwdb <= 14) pwdb -= 4; else if (pwdb <= 26) pwdb -= 8; else if (pwdb <= 34) pwdb -= 6; else if (pwdb <= 42) pwdb -= 2; } } if (sc->avg_pwdb == -1) /* Init. */ sc->avg_pwdb = pwdb; else if (sc->avg_pwdb < pwdb) sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20) + 1; else sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20); URTWN_DPRINTF(sc, URTWN_DEBUG_RSSI, "%s: PWDB %d, EMA %d\n", __func__, pwdb, sc->avg_pwdb); } static int8_t urtwn_get_rssi(struct urtwn_softc *sc, int rate, void *physt) { static const int8_t cckoff[] = { 16, -12, -26, -46 }; struct r92c_rx_phystat *phy; struct r92c_rx_cck *cck; uint8_t rpt; int8_t rssi; if (rate <= URTWN_RIDX_CCK11) { cck = (struct r92c_rx_cck *)physt; if (sc->sc_flags & URTWN_FLAG_CCK_HIPWR) { rpt = (cck->agc_rpt >> 5) & 0x3; rssi = (cck->agc_rpt & 0x1f) << 1; } else { rpt = (cck->agc_rpt >> 6) & 0x3; rssi = cck->agc_rpt & 0x3e; } rssi = cckoff[rpt] - rssi; } else { /* OFDM/HT. */ phy = (struct r92c_rx_phystat *)physt; rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 110; } return (rssi); } static int8_t urtwn_r88e_get_rssi(struct urtwn_softc *sc, int rate, void *physt) { struct r92c_rx_phystat *phy; struct r88e_rx_cck *cck; uint8_t cck_agc_rpt, lna_idx, vga_idx; int8_t rssi; rssi = 0; if (rate <= URTWN_RIDX_CCK11) { cck = (struct r88e_rx_cck *)physt; cck_agc_rpt = cck->agc_rpt; lna_idx = (cck_agc_rpt & 0xe0) >> 5; vga_idx = cck_agc_rpt & 0x1f; switch (lna_idx) { case 7: if (vga_idx <= 27) rssi = -100 + 2* (27 - vga_idx); else rssi = -100; break; case 6: rssi = -48 + 2 * (2 - vga_idx); break; case 5: rssi = -42 + 2 * (7 - vga_idx); break; case 4: rssi = -36 + 2 * (7 - vga_idx); break; case 3: rssi = -24 + 2 * (7 - vga_idx); break; case 2: rssi = -12 + 2 * (5 - vga_idx); break; case 1: rssi = 8 - (2 * vga_idx); break; case 0: rssi = 14 - (2 * vga_idx); break; } rssi += 6; } else { /* OFDM/HT. */ phy = (struct r92c_rx_phystat *)physt; rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 110; } return (rssi); } static int urtwn_tx_data(struct urtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m, struct urtwn_data *data) { const struct ieee80211_txparam *tp; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_key *k = NULL; struct ieee80211_channel *chan; struct ieee80211_frame *wh; struct r92c_tx_desc *txd; uint8_t macid, raid, rate, ridx, type, tid, qos, qsel; int hasqos, ismcast; URTWN_ASSERT_LOCKED(sc); wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; hasqos = IEEE80211_QOS_HAS_SEQ(wh); ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); /* Select TX ring for this frame. */ if (hasqos) { qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; tid = qos & IEEE80211_QOS_TID; } else { qos = 0; tid = 0; } chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ? ni->ni_chan : ic->ic_curchan; tp = &vap->iv_txparms[ieee80211_chan2mode(chan)]; /* Choose a TX rate index. */ if (type == IEEE80211_FC0_TYPE_MGT) rate = tp->mgmtrate; else if (ismcast) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else if (m->m_flags & M_EAPOL) rate = tp->mgmtrate; else { if (URTWN_CHIP_HAS_RATECTL(sc)) { /* XXX pass pktlen */ (void) ieee80211_ratectl_rate(ni, NULL, 0); rate = ni->ni_txrate; } else { /* XXX TODO: drop the default rate for 11b/11g? */ if (ni->ni_flags & IEEE80211_NODE_HT) rate = IEEE80211_RATE_MCS | 0x4; /* MCS4 */ else if (ic->ic_curmode != IEEE80211_MODE_11B) rate = 108; else rate = 22; } } /* * XXX TODO: this should be per-node, for 11b versus 11bg * nodes in hostap mode */ ridx = rate2ridx(rate); if (ni->ni_flags & IEEE80211_NODE_HT) raid = R92C_RAID_11GN; else if (ic->ic_curmode != IEEE80211_MODE_11B) raid = R92C_RAID_11BG; else raid = R92C_RAID_11B; if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m); if (k == NULL) { device_printf(sc->sc_dev, "ieee80211_crypto_encap returns NULL.\n"); return (ENOBUFS); } /* in case packet header moved, reset pointer */ wh = mtod(m, struct ieee80211_frame *); } /* Fill Tx descriptor. */ txd = (struct r92c_tx_desc *)data->buf; memset(txd, 0, sizeof(*txd)); txd->txdw0 |= htole32( SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG); if (ismcast) txd->txdw0 |= htole32(R92C_TXDW0_BMCAST); if (!ismcast) { /* Unicast frame, check if an ACK is expected. */ if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != IEEE80211_QOS_ACKPOLICY_NOACK) { txd->txdw5 |= htole32(R92C_TXDW5_RTY_LMT_ENA); txd->txdw5 |= htole32(SM(R92C_TXDW5_RTY_LMT, tp->maxretry)); } if (sc->chip & URTWN_CHIP_88E) { struct urtwn_node *un = URTWN_NODE(ni); macid = un->id; } else macid = URTWN_MACID_BSS; if (type == IEEE80211_FC0_TYPE_DATA) { qsel = tid % URTWN_MAX_TID; if (sc->chip & URTWN_CHIP_88E) { txd->txdw2 |= htole32( R88E_TXDW2_AGGBK | R88E_TXDW2_CCX_RPT); } else txd->txdw1 |= htole32(R92C_TXDW1_AGGBK); /* protmode, non-HT */ /* XXX TODO: noack frames? */ if ((rate & 0x80) == 0 && (ic->ic_flags & IEEE80211_F_USEPROT)) { switch (ic->ic_protmode) { case IEEE80211_PROT_CTSONLY: txd->txdw4 |= htole32( R92C_TXDW4_CTS2SELF | R92C_TXDW4_HWRTSEN); break; case IEEE80211_PROT_RTSCTS: txd->txdw4 |= htole32( R92C_TXDW4_RTSEN | R92C_TXDW4_HWRTSEN); break; default: break; } } /* protmode, HT */ /* XXX TODO: noack frames? */ if ((rate & 0x80) && (ic->ic_htprotmode == IEEE80211_PROT_RTSCTS)) { txd->txdw4 |= htole32( R92C_TXDW4_RTSEN | R92C_TXDW4_HWRTSEN); } /* XXX TODO: rtsrate is configurable? 24mbit may * be a bit high for RTS rate? */ txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, URTWN_RIDX_OFDM24)); txd->txdw5 |= htole32(0x0001ff00); } else /* IEEE80211_FC0_TYPE_MGT */ qsel = R92C_TXDW1_QSEL_MGNT; } else { macid = URTWN_MACID_BC; qsel = R92C_TXDW1_QSEL_MGNT; } txd->txdw1 |= htole32( SM(R92C_TXDW1_QSEL, qsel) | SM(R92C_TXDW1_RAID, raid)); /* XXX TODO: 40MHZ flag? */ /* XXX TODO: AMPDU flag? (AGG_ENABLE or AGG_BREAK?) Density shift? */ /* XXX Short preamble? */ /* XXX Short-GI? */ if (sc->chip & URTWN_CHIP_88E) txd->txdw1 |= htole32(SM(R88E_TXDW1_MACID, macid)); else txd->txdw1 |= htole32(SM(R92C_TXDW1_MACID, macid)); txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, ridx)); /* Force this rate if needed. */ if (URTWN_CHIP_HAS_RATECTL(sc) || ismcast || (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) || (m->m_flags & M_EAPOL) || type != IEEE80211_FC0_TYPE_DATA) txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE); if (!hasqos) { /* Use HW sequence numbering for non-QoS frames. */ if (sc->chip & URTWN_CHIP_88E) txd->txdseq = htole16(R88E_TXDSEQ_HWSEQ_EN); else txd->txdw4 |= htole32(R92C_TXDW4_HWSEQ_EN); } else { /* Set sequence number. */ txd->txdseq = htole16(M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE); } if (k != NULL && !(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { uint8_t cipher; switch (k->wk_cipher->ic_cipher) { case IEEE80211_CIPHER_WEP: case IEEE80211_CIPHER_TKIP: cipher = R92C_TXDW1_CIPHER_RC4; break; case IEEE80211_CIPHER_AES_CCM: cipher = R92C_TXDW1_CIPHER_AES; break; default: device_printf(sc->sc_dev, "%s: unknown cipher %d\n", __func__, k->wk_cipher->ic_cipher); return (EINVAL); } txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, cipher)); } if (ieee80211_radiotap_active_vap(vap)) { struct urtwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; if (k != NULL) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; ieee80211_radiotap_tx(vap, m); } data->ni = ni; urtwn_tx_start(sc, m, type, data); return (0); } static int urtwn_tx_raw(struct urtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m, struct urtwn_data *data, const struct ieee80211_bpf_params *params) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_key *k = NULL; struct ieee80211_frame *wh; struct r92c_tx_desc *txd; uint8_t cipher, ridx, type; /* Encrypt the frame if need be. */ cipher = R92C_TXDW1_CIPHER_NONE; if (params->ibp_flags & IEEE80211_BPF_CRYPTO) { /* Retrieve key for TX. */ k = ieee80211_crypto_encap(ni, m); if (k == NULL) return (ENOBUFS); if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { switch (k->wk_cipher->ic_cipher) { case IEEE80211_CIPHER_WEP: case IEEE80211_CIPHER_TKIP: cipher = R92C_TXDW1_CIPHER_RC4; break; case IEEE80211_CIPHER_AES_CCM: cipher = R92C_TXDW1_CIPHER_AES; break; default: device_printf(sc->sc_dev, "%s: unknown cipher %d\n", __func__, k->wk_cipher->ic_cipher); return (EINVAL); } } } /* XXX TODO: 11n checks, matching urtwn_tx_data() */ wh = mtod(m, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; /* Fill Tx descriptor. */ txd = (struct r92c_tx_desc *)data->buf; memset(txd, 0, sizeof(*txd)); txd->txdw0 |= htole32( SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txd->txdw0 |= htole32(R92C_TXDW0_BMCAST); if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) { txd->txdw5 |= htole32(R92C_TXDW5_RTY_LMT_ENA); txd->txdw5 |= htole32(SM(R92C_TXDW5_RTY_LMT, params->ibp_try0)); } if (params->ibp_flags & IEEE80211_BPF_RTS) txd->txdw4 |= htole32(R92C_TXDW4_RTSEN); if (params->ibp_flags & IEEE80211_BPF_CTS) txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF); if (txd->txdw4 & htole32(R92C_TXDW4_RTSEN | R92C_TXDW4_CTS2SELF)) { txd->txdw4 |= htole32(R92C_TXDW4_HWRTSEN); txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, URTWN_RIDX_OFDM24)); } if (sc->chip & URTWN_CHIP_88E) txd->txdw1 |= htole32(SM(R88E_TXDW1_MACID, URTWN_MACID_BC)); else txd->txdw1 |= htole32(SM(R92C_TXDW1_MACID, URTWN_MACID_BC)); /* XXX TODO: rate index/config (RAID) for 11n? */ txd->txdw1 |= htole32(SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT)); txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, cipher)); /* Choose a TX rate index. */ ridx = rate2ridx(params->ibp_rate0); txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, ridx)); txd->txdw5 |= htole32(0x0001ff00); txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE); if (!IEEE80211_QOS_HAS_SEQ(wh)) { /* Use HW sequence numbering for non-QoS frames. */ if (sc->chip & URTWN_CHIP_88E) txd->txdseq = htole16(R88E_TXDSEQ_HWSEQ_EN); else txd->txdw4 |= htole32(R92C_TXDW4_HWSEQ_EN); } else { /* Set sequence number. */ txd->txdseq = htole16(M_SEQNO_GET(m) % IEEE80211_SEQ_RANGE); } if (ieee80211_radiotap_active_vap(vap)) { struct urtwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; if (k != NULL) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; ieee80211_radiotap_tx(vap, m); } data->ni = ni; urtwn_tx_start(sc, m, type, data); return (0); } static void urtwn_tx_start(struct urtwn_softc *sc, struct mbuf *m, uint8_t type, struct urtwn_data *data) { struct usb_xfer *xfer; struct r92c_tx_desc *txd; uint16_t ac, sum; int i, xferlen; URTWN_ASSERT_LOCKED(sc); ac = M_WME_GETAC(m); switch (type) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: xfer = sc->sc_xfer[URTWN_BULK_TX_VO]; break; default: xfer = sc->sc_xfer[wme2queue[ac].qid]; break; } txd = (struct r92c_tx_desc *)data->buf; txd->txdw0 |= htole32(SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len)); /* Compute Tx descriptor checksum. */ sum = 0; for (i = 0; i < sizeof(*txd) / 2; i++) sum ^= ((uint16_t *)txd)[i]; txd->txdsum = sum; /* NB: already little endian. */ xferlen = sizeof(*txd) + m->m_pkthdr.len; m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&txd[1]); data->buflen = xferlen; data->m = m; STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); usbd_transfer_start(xfer); } static int urtwn_transmit(struct ieee80211com *ic, struct mbuf *m) { struct urtwn_softc *sc = ic->ic_softc; int error; URTWN_LOCK(sc); if ((sc->sc_flags & URTWN_RUNNING) == 0) { URTWN_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { URTWN_UNLOCK(sc); return (error); } urtwn_start(sc); URTWN_UNLOCK(sc); return (0); } static void urtwn_start(struct urtwn_softc *sc) { struct ieee80211_node *ni; struct mbuf *m; struct urtwn_data *bf; URTWN_ASSERT_LOCKED(sc); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { bf = urtwn_getbuf(sc); if (bf == NULL) { mbufq_prepend(&sc->sc_snd, m); break; } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: called; m=%p\n", __func__, m); if (urtwn_tx_data(sc, ni, m, bf) != 0) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); m_freem(m); ieee80211_free_node(ni); break; } sc->sc_txtimer = 5; callout_reset(&sc->sc_watchdog_ch, hz, urtwn_watchdog, sc); } } static void urtwn_parent(struct ieee80211com *ic) { struct urtwn_softc *sc = ic->ic_softc; URTWN_LOCK(sc); if (sc->sc_flags & URTWN_DETACHED) { URTWN_UNLOCK(sc); return; } URTWN_UNLOCK(sc); if (ic->ic_nrunning > 0) { if (urtwn_init(sc) != 0) { struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (vap != NULL) ieee80211_stop(vap); } else ieee80211_start_all(ic); } else urtwn_stop(sc); } static __inline int urtwn_power_on(struct urtwn_softc *sc) { return sc->sc_power_on(sc); } static int urtwn_r92c_power_on(struct urtwn_softc *sc) { uint32_t reg; usb_error_t error; int ntries; /* Wait for autoload done bit. */ for (ntries = 0; ntries < 1000; ntries++) { if (urtwn_read_1(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_PFM_ALDN) break; urtwn_ms_delay(sc); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for chip autoload\n"); return (ETIMEDOUT); } /* Unlock ISO/CLK/Power control register. */ error = urtwn_write_1(sc, R92C_RSV_CTRL, 0); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Move SPS into PWM mode. */ error = urtwn_write_1(sc, R92C_SPS0_CTRL, 0x2b); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); urtwn_ms_delay(sc); reg = urtwn_read_1(sc, R92C_LDOV12D_CTRL); if (!(reg & R92C_LDOV12D_CTRL_LDV12_EN)) { error = urtwn_write_1(sc, R92C_LDOV12D_CTRL, reg | R92C_LDOV12D_CTRL_LDV12_EN); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); urtwn_ms_delay(sc); error = urtwn_write_1(sc, R92C_SYS_ISO_CTRL, urtwn_read_1(sc, R92C_SYS_ISO_CTRL) & ~R92C_SYS_ISO_CTRL_MD2PP); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); } /* Auto enable WLAN. */ error = urtwn_write_2(sc, R92C_APS_FSMCO, urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); for (ntries = 0; ntries < 1000; ntries++) { if (!(urtwn_read_2(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_APFM_ONMAC)) break; urtwn_ms_delay(sc); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for MAC auto ON\n"); return (ETIMEDOUT); } /* Enable radio, GPIO and LED functions. */ error = urtwn_write_2(sc, R92C_APS_FSMCO, R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_PDN_EN | R92C_APS_FSMCO_PFM_ALDN); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Release RF digital isolation. */ error = urtwn_write_2(sc, R92C_SYS_ISO_CTRL, urtwn_read_2(sc, R92C_SYS_ISO_CTRL) & ~R92C_SYS_ISO_CTRL_DIOR); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Initialize MAC. */ error = urtwn_write_1(sc, R92C_APSD_CTRL, urtwn_read_1(sc, R92C_APSD_CTRL) & ~R92C_APSD_CTRL_OFF); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); for (ntries = 0; ntries < 200; ntries++) { if (!(urtwn_read_1(sc, R92C_APSD_CTRL) & R92C_APSD_CTRL_OFF_STATUS)) break; urtwn_ms_delay(sc); } if (ntries == 200) { device_printf(sc->sc_dev, "timeout waiting for MAC initialization\n"); return (ETIMEDOUT); } /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */ reg = urtwn_read_2(sc, R92C_CR); reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN | R92C_CR_ENSEC; error = urtwn_write_2(sc, R92C_CR, reg); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); error = urtwn_write_1(sc, 0xfe10, 0x19); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); return (0); } static int urtwn_r88e_power_on(struct urtwn_softc *sc) { uint32_t reg; usb_error_t error; int ntries; /* Wait for power ready bit. */ for (ntries = 0; ntries < 5000; ntries++) { if (urtwn_read_4(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_SUS_HOST) break; urtwn_ms_delay(sc); } if (ntries == 5000) { device_printf(sc->sc_dev, "timeout waiting for chip power up\n"); return (ETIMEDOUT); } /* Reset BB. */ error = urtwn_write_1(sc, R92C_SYS_FUNC_EN, urtwn_read_1(sc, R92C_SYS_FUNC_EN) & ~(R92C_SYS_FUNC_EN_BBRSTB | R92C_SYS_FUNC_EN_BB_GLB_RST)); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); error = urtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 2, urtwn_read_1(sc, R92C_AFE_XTAL_CTRL + 2) | 0x80); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Disable HWPDN. */ error = urtwn_write_2(sc, R92C_APS_FSMCO, urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_APDM_HPDN); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Disable WL suspend. */ error = urtwn_write_2(sc, R92C_APS_FSMCO, urtwn_read_2(sc, R92C_APS_FSMCO) & ~(R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_AFSM_PCIE)); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); error = urtwn_write_2(sc, R92C_APS_FSMCO, urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); for (ntries = 0; ntries < 5000; ntries++) { if (!(urtwn_read_2(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_APFM_ONMAC)) break; urtwn_ms_delay(sc); } if (ntries == 5000) return (ETIMEDOUT); /* Enable LDO normal mode. */ error = urtwn_write_1(sc, R92C_LPLDO_CTRL, urtwn_read_1(sc, R92C_LPLDO_CTRL) & ~R92C_LPLDO_CTRL_SLEEP); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */ error = urtwn_write_2(sc, R92C_CR, 0); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); reg = urtwn_read_2(sc, R92C_CR); reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | R92C_CR_SCHEDULE_EN | R92C_CR_ENSEC | R92C_CR_CALTMR_EN; error = urtwn_write_2(sc, R92C_CR, reg); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); return (0); } static __inline void urtwn_power_off(struct urtwn_softc *sc) { return sc->sc_power_off(sc); } static void urtwn_r92c_power_off(struct urtwn_softc *sc) { uint32_t reg; /* Block all Tx queues. */ urtwn_write_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_ALL); /* Disable RF */ urtwn_rf_write(sc, 0, 0, 0); urtwn_write_1(sc, R92C_APSD_CTRL, R92C_APSD_CTRL_OFF); /* Reset BB state machine */ urtwn_write_1(sc, R92C_SYS_FUNC_EN, R92C_SYS_FUNC_EN_USBD | R92C_SYS_FUNC_EN_USBA | R92C_SYS_FUNC_EN_BB_GLB_RST); urtwn_write_1(sc, R92C_SYS_FUNC_EN, R92C_SYS_FUNC_EN_USBD | R92C_SYS_FUNC_EN_USBA); /* * Reset digital sequence */ #ifndef URTWN_WITHOUT_UCODE if (urtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RDY) { /* Reset MCU ready status */ urtwn_write_1(sc, R92C_MCUFWDL, 0); /* If firmware in ram code, do reset */ urtwn_fw_reset(sc); } #endif /* Reset MAC and Enable 8051 */ urtwn_write_1(sc, R92C_SYS_FUNC_EN + 1, (R92C_SYS_FUNC_EN_CPUEN | R92C_SYS_FUNC_EN_ELDR | R92C_SYS_FUNC_EN_HWPDN) >> 8); /* Reset MCU ready status */ urtwn_write_1(sc, R92C_MCUFWDL, 0); /* Disable MAC clock */ urtwn_write_2(sc, R92C_SYS_CLKR, R92C_SYS_CLKR_ANAD16V_EN | R92C_SYS_CLKR_ANA8M | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_80M_SSC_DIS | R92C_SYS_CLKR_SYS_EN | R92C_SYS_CLKR_RING_EN | 0x4000); /* Disable AFE PLL */ urtwn_write_1(sc, R92C_AFE_PLL_CTRL, 0x80); /* Gated AFE DIG_CLOCK */ urtwn_write_2(sc, R92C_AFE_XTAL_CTRL, 0x880F); /* Isolated digital to PON */ urtwn_write_1(sc, R92C_SYS_ISO_CTRL, R92C_SYS_ISO_CTRL_MD2PP | R92C_SYS_ISO_CTRL_PA2PCIE | R92C_SYS_ISO_CTRL_PD2CORE | R92C_SYS_ISO_CTRL_IP2MAC | R92C_SYS_ISO_CTRL_DIOP | R92C_SYS_ISO_CTRL_DIOE); /* * Pull GPIO PIN to balance level and LED control */ /* 1. Disable GPIO[7:0] */ urtwn_write_2(sc, R92C_GPIO_IOSEL, 0x0000); reg = urtwn_read_4(sc, R92C_GPIO_PIN_CTRL) & ~0x0000ff00; reg |= ((reg << 8) & 0x0000ff00) | 0x00ff0000; urtwn_write_4(sc, R92C_GPIO_PIN_CTRL, reg); /* Disable GPIO[10:8] */ urtwn_write_1(sc, R92C_MAC_PINMUX_CFG, 0x00); reg = urtwn_read_2(sc, R92C_GPIO_IO_SEL) & ~0x00f0; reg |= (((reg & 0x000f) << 4) | 0x0780); urtwn_write_2(sc, R92C_GPIO_IO_SEL, reg); /* Disable LED0 & 1 */ urtwn_write_2(sc, R92C_LEDCFG0, 0x8080); /* * Reset digital sequence */ /* Disable ELDR clock */ urtwn_write_2(sc, R92C_SYS_CLKR, R92C_SYS_CLKR_ANAD16V_EN | R92C_SYS_CLKR_ANA8M | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_80M_SSC_DIS | R92C_SYS_CLKR_SYS_EN | R92C_SYS_CLKR_RING_EN | 0x4000); /* Isolated ELDR to PON */ urtwn_write_1(sc, R92C_SYS_ISO_CTRL + 1, (R92C_SYS_ISO_CTRL_DIOR | R92C_SYS_ISO_CTRL_PWC_EV12V) >> 8); /* * Disable analog sequence */ /* Disable A15 power */ urtwn_write_1(sc, R92C_LDOA15_CTRL, R92C_LDOA15_CTRL_OBUF); /* Disable digital core power */ urtwn_write_1(sc, R92C_LDOV12D_CTRL, urtwn_read_1(sc, R92C_LDOV12D_CTRL) & ~R92C_LDOV12D_CTRL_LDV12_EN); /* Enter PFM mode */ urtwn_write_1(sc, R92C_SPS0_CTRL, 0x23); /* Set USB suspend */ urtwn_write_2(sc, R92C_APS_FSMCO, R92C_APS_FSMCO_APDM_HOST | R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_PFM_ALDN); /* Lock ISO/CLK/Power control register. */ urtwn_write_1(sc, R92C_RSV_CTRL, 0x0E); } static void urtwn_r88e_power_off(struct urtwn_softc *sc) { uint8_t reg; int ntries; /* Disable any kind of TX reports. */ urtwn_write_1(sc, R88E_TX_RPT_CTRL, urtwn_read_1(sc, R88E_TX_RPT_CTRL) & ~(R88E_TX_RPT1_ENA | R88E_TX_RPT2_ENA)); /* Stop Rx. */ urtwn_write_1(sc, R92C_CR, 0); /* Move card to Low Power State. */ /* Block all Tx queues. */ urtwn_write_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_ALL); for (ntries = 0; ntries < 20; ntries++) { /* Should be zero if no packet is transmitting. */ if (urtwn_read_4(sc, R88E_SCH_TXCMD) == 0) break; urtwn_ms_delay(sc); } if (ntries == 20) { device_printf(sc->sc_dev, "%s: failed to block Tx queues\n", __func__); return; } /* CCK and OFDM are disabled, and clock are gated. */ urtwn_write_1(sc, R92C_SYS_FUNC_EN, urtwn_read_1(sc, R92C_SYS_FUNC_EN) & ~R92C_SYS_FUNC_EN_BBRSTB); urtwn_ms_delay(sc); /* Reset MAC TRX */ urtwn_write_1(sc, R92C_CR, R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | R92C_CR_SCHEDULE_EN); /* check if removed later */ urtwn_write_1(sc, R92C_CR + 1, urtwn_read_1(sc, R92C_CR + 1) & ~(R92C_CR_ENSEC >> 8)); /* Respond TxOK to scheduler */ urtwn_write_1(sc, R92C_DUAL_TSF_RST, urtwn_read_1(sc, R92C_DUAL_TSF_RST) | 0x20); /* If firmware in ram code, do reset. */ #ifndef URTWN_WITHOUT_UCODE if (urtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RDY) urtwn_r88e_fw_reset(sc); #endif /* Reset MCU ready status. */ urtwn_write_1(sc, R92C_MCUFWDL, 0x00); /* Disable 32k. */ urtwn_write_1(sc, R88E_32K_CTRL, urtwn_read_1(sc, R88E_32K_CTRL) & ~0x01); /* Move card to Disabled state. */ /* Turn off RF. */ urtwn_write_1(sc, R92C_RF_CTRL, 0); /* LDO Sleep mode. */ urtwn_write_1(sc, R92C_LPLDO_CTRL, urtwn_read_1(sc, R92C_LPLDO_CTRL) | R92C_LPLDO_CTRL_SLEEP); /* Turn off MAC by HW state machine */ urtwn_write_1(sc, R92C_APS_FSMCO + 1, urtwn_read_1(sc, R92C_APS_FSMCO + 1) | (R92C_APS_FSMCO_APFM_OFF >> 8)); for (ntries = 0; ntries < 20; ntries++) { /* Wait until it will be disabled. */ if ((urtwn_read_1(sc, R92C_APS_FSMCO + 1) & (R92C_APS_FSMCO_APFM_OFF >> 8)) == 0) break; urtwn_ms_delay(sc); } if (ntries == 20) { device_printf(sc->sc_dev, "%s: could not turn off MAC\n", __func__); return; } /* schmit trigger */ urtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 2, urtwn_read_1(sc, R92C_AFE_XTAL_CTRL + 2) | 0x80); /* Enable WL suspend. */ urtwn_write_1(sc, R92C_APS_FSMCO + 1, (urtwn_read_1(sc, R92C_APS_FSMCO + 1) & ~0x10) | 0x08); /* Enable bandgap mbias in suspend. */ urtwn_write_1(sc, R92C_APS_FSMCO + 3, 0); /* Clear SIC_EN register. */ urtwn_write_1(sc, R92C_GPIO_MUXCFG + 1, urtwn_read_1(sc, R92C_GPIO_MUXCFG + 1) & ~0x10); /* Set USB suspend enable local register */ urtwn_write_1(sc, R92C_USB_SUSPEND, urtwn_read_1(sc, R92C_USB_SUSPEND) | 0x10); /* Reset MCU IO Wrapper. */ reg = urtwn_read_1(sc, R92C_RSV_CTRL + 1); urtwn_write_1(sc, R92C_RSV_CTRL + 1, reg & ~0x08); urtwn_write_1(sc, R92C_RSV_CTRL + 1, reg | 0x08); /* marked as 'For Power Consumption' code. */ urtwn_write_1(sc, R92C_GPIO_OUT, urtwn_read_1(sc, R92C_GPIO_IN)); urtwn_write_1(sc, R92C_GPIO_IOSEL, 0xff); urtwn_write_1(sc, R92C_GPIO_IO_SEL, urtwn_read_1(sc, R92C_GPIO_IO_SEL) << 4); urtwn_write_1(sc, R92C_GPIO_MOD, urtwn_read_1(sc, R92C_GPIO_MOD) | 0x0f); /* Set LNA, TRSW, EX_PA Pin to output mode. */ urtwn_write_4(sc, R88E_BB_PAD_CTRL, 0x00080808); } static int urtwn_llt_init(struct urtwn_softc *sc) { int i, error, page_count, pktbuf_count; page_count = (sc->chip & URTWN_CHIP_88E) ? R88E_TX_PAGE_COUNT : R92C_TX_PAGE_COUNT; pktbuf_count = (sc->chip & URTWN_CHIP_88E) ? R88E_TXPKTBUF_COUNT : R92C_TXPKTBUF_COUNT; /* Reserve pages [0; page_count]. */ for (i = 0; i < page_count; i++) { if ((error = urtwn_llt_write(sc, i, i + 1)) != 0) return (error); } /* NB: 0xff indicates end-of-list. */ if ((error = urtwn_llt_write(sc, i, 0xff)) != 0) return (error); /* * Use pages [page_count + 1; pktbuf_count - 1] * as ring buffer. */ for (++i; i < pktbuf_count - 1; i++) { if ((error = urtwn_llt_write(sc, i, i + 1)) != 0) return (error); } /* Make the last page point to the beginning of the ring buffer. */ error = urtwn_llt_write(sc, i, page_count + 1); return (error); } #ifndef URTWN_WITHOUT_UCODE static void urtwn_fw_reset(struct urtwn_softc *sc) { uint16_t reg; int ntries; /* Tell 8051 to reset itself. */ urtwn_write_1(sc, R92C_HMETFR + 3, 0x20); /* Wait until 8051 resets by itself. */ for (ntries = 0; ntries < 100; ntries++) { reg = urtwn_read_2(sc, R92C_SYS_FUNC_EN); if (!(reg & R92C_SYS_FUNC_EN_CPUEN)) return; urtwn_ms_delay(sc); } /* Force 8051 reset. */ urtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN); } static void urtwn_r88e_fw_reset(struct urtwn_softc *sc) { uint16_t reg; reg = urtwn_read_2(sc, R92C_SYS_FUNC_EN); urtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN); urtwn_write_2(sc, R92C_SYS_FUNC_EN, reg | R92C_SYS_FUNC_EN_CPUEN); } static int urtwn_fw_loadpage(struct urtwn_softc *sc, int page, const uint8_t *buf, int len) { uint32_t reg; usb_error_t error = USB_ERR_NORMAL_COMPLETION; int off, mlen; reg = urtwn_read_4(sc, R92C_MCUFWDL); reg = RW(reg, R92C_MCUFWDL_PAGE, page); urtwn_write_4(sc, R92C_MCUFWDL, reg); off = R92C_FW_START_ADDR; while (len > 0) { if (len > 196) mlen = 196; else if (len > 4) mlen = 4; else mlen = 1; /* XXX fix this deconst */ error = urtwn_write_region_1(sc, off, __DECONST(uint8_t *, buf), mlen); if (error != USB_ERR_NORMAL_COMPLETION) break; off += mlen; buf += mlen; len -= mlen; } return (error); } static int urtwn_load_firmware(struct urtwn_softc *sc) { const struct firmware *fw; const struct r92c_fw_hdr *hdr; const char *imagename; const u_char *ptr; size_t len; uint32_t reg; int mlen, ntries, page, error; URTWN_UNLOCK(sc); /* Read firmware image from the filesystem. */ if (sc->chip & URTWN_CHIP_88E) imagename = "urtwn-rtl8188eufw"; else if ((sc->chip & (URTWN_CHIP_UMC_A_CUT | URTWN_CHIP_92C)) == URTWN_CHIP_UMC_A_CUT) imagename = "urtwn-rtl8192cfwU"; else imagename = "urtwn-rtl8192cfwT"; fw = firmware_get(imagename); URTWN_LOCK(sc); if (fw == NULL) { device_printf(sc->sc_dev, "failed loadfirmware of file %s\n", imagename); return (ENOENT); } len = fw->datasize; if (len < sizeof(*hdr)) { device_printf(sc->sc_dev, "firmware too short\n"); error = EINVAL; goto fail; } ptr = fw->data; hdr = (const struct r92c_fw_hdr *)ptr; /* Check if there is a valid FW header and skip it. */ if ((le16toh(hdr->signature) >> 4) == 0x88c || (le16toh(hdr->signature) >> 4) == 0x88e || (le16toh(hdr->signature) >> 4) == 0x92c) { URTWN_DPRINTF(sc, URTWN_DEBUG_FIRMWARE, "FW V%d.%d %02d-%02d %02d:%02d\n", le16toh(hdr->version), le16toh(hdr->subversion), hdr->month, hdr->date, hdr->hour, hdr->minute); ptr += sizeof(*hdr); len -= sizeof(*hdr); } if (urtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL) { if (sc->chip & URTWN_CHIP_88E) urtwn_r88e_fw_reset(sc); else urtwn_fw_reset(sc); urtwn_write_1(sc, R92C_MCUFWDL, 0); } if (!(sc->chip & URTWN_CHIP_88E)) { urtwn_write_2(sc, R92C_SYS_FUNC_EN, urtwn_read_2(sc, R92C_SYS_FUNC_EN) | R92C_SYS_FUNC_EN_CPUEN); } urtwn_write_1(sc, R92C_MCUFWDL, urtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_EN); urtwn_write_1(sc, R92C_MCUFWDL + 2, urtwn_read_1(sc, R92C_MCUFWDL + 2) & ~0x08); /* Reset the FWDL checksum. */ urtwn_write_1(sc, R92C_MCUFWDL, urtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_CHKSUM_RPT); for (page = 0; len > 0; page++) { mlen = min(len, R92C_FW_PAGE_SIZE); error = urtwn_fw_loadpage(sc, page, ptr, mlen); if (error != 0) { device_printf(sc->sc_dev, "could not load firmware page\n"); goto fail; } ptr += mlen; len -= mlen; } urtwn_write_1(sc, R92C_MCUFWDL, urtwn_read_1(sc, R92C_MCUFWDL) & ~R92C_MCUFWDL_EN); urtwn_write_1(sc, R92C_MCUFWDL + 1, 0); /* Wait for checksum report. */ for (ntries = 0; ntries < 1000; ntries++) { if (urtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_CHKSUM_RPT) break; urtwn_ms_delay(sc); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for checksum report\n"); error = ETIMEDOUT; goto fail; } reg = urtwn_read_4(sc, R92C_MCUFWDL); reg = (reg & ~R92C_MCUFWDL_WINTINI_RDY) | R92C_MCUFWDL_RDY; urtwn_write_4(sc, R92C_MCUFWDL, reg); if (sc->chip & URTWN_CHIP_88E) urtwn_r88e_fw_reset(sc); /* Wait for firmware readiness. */ for (ntries = 0; ntries < 1000; ntries++) { if (urtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_WINTINI_RDY) break; urtwn_ms_delay(sc); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for firmware readiness\n"); error = ETIMEDOUT; goto fail; } fail: firmware_put(fw, FIRMWARE_UNLOAD); return (error); } #endif static int urtwn_dma_init(struct urtwn_softc *sc) { struct usb_endpoint *ep, *ep_end; usb_error_t usb_err; uint32_t reg; int hashq, hasnq, haslq, nqueues, ntx; int error, pagecount, npubqpages, nqpages, nrempages, tx_boundary; /* Initialize LLT table. */ error = urtwn_llt_init(sc); if (error != 0) return (error); /* Determine the number of bulk-out pipes. */ ntx = 0; ep = sc->sc_udev->endpoints; ep_end = sc->sc_udev->endpoints + sc->sc_udev->endpoints_max; for (; ep != ep_end; ep++) { if ((ep->edesc == NULL) || (ep->iface_index != sc->sc_iface_index)) continue; if (UE_GET_DIR(ep->edesc->bEndpointAddress) == UE_DIR_OUT) ntx++; } if (ntx == 0) { device_printf(sc->sc_dev, "%d: invalid number of Tx bulk pipes\n", ntx); return (EIO); } /* Get Tx queues to USB endpoints mapping. */ hashq = hasnq = haslq = nqueues = 0; switch (ntx) { case 1: hashq = 1; break; case 2: hashq = hasnq = 1; break; case 3: case 4: hashq = hasnq = haslq = 1; break; } nqueues = hashq + hasnq + haslq; if (nqueues == 0) return (EIO); npubqpages = nqpages = nrempages = pagecount = 0; if (sc->chip & URTWN_CHIP_88E) tx_boundary = R88E_TX_PAGE_BOUNDARY; else { pagecount = R92C_TX_PAGE_COUNT; npubqpages = R92C_PUBQ_NPAGES; tx_boundary = R92C_TX_PAGE_BOUNDARY; } /* Set number of pages for normal priority queue. */ if (sc->chip & URTWN_CHIP_88E) { usb_err = urtwn_write_2(sc, R92C_RQPN_NPQ, 0xd); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); usb_err = urtwn_write_4(sc, R92C_RQPN, 0x808e000d); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); } else { /* Get the number of pages for each queue. */ nqpages = (pagecount - npubqpages) / nqueues; /* * The remaining pages are assigned to the high priority * queue. */ nrempages = (pagecount - npubqpages) % nqueues; usb_err = urtwn_write_1(sc, R92C_RQPN_NPQ, hasnq ? nqpages : 0); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); usb_err = urtwn_write_4(sc, R92C_RQPN, /* Set number of pages for public queue. */ SM(R92C_RQPN_PUBQ, npubqpages) | /* Set number of pages for high priority queue. */ SM(R92C_RQPN_HPQ, hashq ? nqpages + nrempages : 0) | /* Set number of pages for low priority queue. */ SM(R92C_RQPN_LPQ, haslq ? nqpages : 0) | /* Load values. */ R92C_RQPN_LD); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); } usb_err = urtwn_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, tx_boundary); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); usb_err = urtwn_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, tx_boundary); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); usb_err = urtwn_write_1(sc, R92C_TXPKTBUF_WMAC_LBK_BF_HD, tx_boundary); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); usb_err = urtwn_write_1(sc, R92C_TRXFF_BNDY, tx_boundary); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); usb_err = urtwn_write_1(sc, R92C_TDECTRL + 1, tx_boundary); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Set queue to USB pipe mapping. */ reg = urtwn_read_2(sc, R92C_TRXDMA_CTRL); reg &= ~R92C_TRXDMA_CTRL_QMAP_M; if (nqueues == 1) { if (hashq) reg |= R92C_TRXDMA_CTRL_QMAP_HQ; else if (hasnq) reg |= R92C_TRXDMA_CTRL_QMAP_NQ; else reg |= R92C_TRXDMA_CTRL_QMAP_LQ; } else if (nqueues == 2) { /* * All 2-endpoints configs have high and normal * priority queues. */ reg |= R92C_TRXDMA_CTRL_QMAP_HQ_NQ; } else reg |= R92C_TRXDMA_CTRL_QMAP_3EP; usb_err = urtwn_write_2(sc, R92C_TRXDMA_CTRL, reg); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Set Tx/Rx transfer page boundary. */ usb_err = urtwn_write_2(sc, R92C_TRXFF_BNDY + 2, (sc->chip & URTWN_CHIP_88E) ? 0x23ff : 0x27ff); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); /* Set Tx/Rx transfer page size. */ usb_err = urtwn_write_1(sc, R92C_PBP, SM(R92C_PBP_PSRX, R92C_PBP_128) | SM(R92C_PBP_PSTX, R92C_PBP_128)); if (usb_err != USB_ERR_NORMAL_COMPLETION) return (EIO); return (0); } static int urtwn_mac_init(struct urtwn_softc *sc) { usb_error_t error; int i; /* Write MAC initialization values. */ if (sc->chip & URTWN_CHIP_88E) { for (i = 0; i < nitems(rtl8188eu_mac); i++) { error = urtwn_write_1(sc, rtl8188eu_mac[i].reg, rtl8188eu_mac[i].val); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); } urtwn_write_1(sc, R92C_MAX_AGGR_NUM, 0x07); } else { for (i = 0; i < nitems(rtl8192cu_mac); i++) error = urtwn_write_1(sc, rtl8192cu_mac[i].reg, rtl8192cu_mac[i].val); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); } return (0); } static void urtwn_bb_init(struct urtwn_softc *sc) { const struct urtwn_bb_prog *prog; uint32_t reg; uint8_t crystalcap; int i; /* Enable BB and RF. */ urtwn_write_2(sc, R92C_SYS_FUNC_EN, urtwn_read_2(sc, R92C_SYS_FUNC_EN) | R92C_SYS_FUNC_EN_BBRSTB | R92C_SYS_FUNC_EN_BB_GLB_RST | R92C_SYS_FUNC_EN_DIO_RF); if (!(sc->chip & URTWN_CHIP_88E)) urtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0xdb83); urtwn_write_1(sc, R92C_RF_CTRL, R92C_RF_CTRL_EN | R92C_RF_CTRL_RSTB | R92C_RF_CTRL_SDMRSTB); urtwn_write_1(sc, R92C_SYS_FUNC_EN, R92C_SYS_FUNC_EN_USBA | R92C_SYS_FUNC_EN_USBD | R92C_SYS_FUNC_EN_BB_GLB_RST | R92C_SYS_FUNC_EN_BBRSTB); if (!(sc->chip & URTWN_CHIP_88E)) { urtwn_write_1(sc, R92C_LDOHCI12_CTRL, 0x0f); urtwn_write_1(sc, 0x15, 0xe9); urtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80); } /* Select BB programming based on board type. */ if (sc->chip & URTWN_CHIP_88E) prog = &rtl8188eu_bb_prog; else if (!(sc->chip & URTWN_CHIP_92C)) { if (sc->board_type == R92C_BOARD_TYPE_MINICARD) prog = &rtl8188ce_bb_prog; else if (sc->board_type == R92C_BOARD_TYPE_HIGHPA) prog = &rtl8188ru_bb_prog; else prog = &rtl8188cu_bb_prog; } else { if (sc->board_type == R92C_BOARD_TYPE_MINICARD) prog = &rtl8192ce_bb_prog; else prog = &rtl8192cu_bb_prog; } /* Write BB initialization values. */ for (i = 0; i < prog->count; i++) { urtwn_bb_write(sc, prog->regs[i], prog->vals[i]); urtwn_ms_delay(sc); } if (sc->chip & URTWN_CHIP_92C_1T2R) { /* 8192C 1T only configuration. */ reg = urtwn_bb_read(sc, R92C_FPGA0_TXINFO); reg = (reg & ~0x00000003) | 0x2; urtwn_bb_write(sc, R92C_FPGA0_TXINFO, reg); reg = urtwn_bb_read(sc, R92C_FPGA1_TXINFO); reg = (reg & ~0x00300033) | 0x00200022; urtwn_bb_write(sc, R92C_FPGA1_TXINFO, reg); reg = urtwn_bb_read(sc, R92C_CCK0_AFESETTING); reg = (reg & ~0xff000000) | 0x45 << 24; urtwn_bb_write(sc, R92C_CCK0_AFESETTING, reg); reg = urtwn_bb_read(sc, R92C_OFDM0_TRXPATHENA); reg = (reg & ~0x000000ff) | 0x23; urtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, reg); reg = urtwn_bb_read(sc, R92C_OFDM0_AGCPARAM1); reg = (reg & ~0x00000030) | 1 << 4; urtwn_bb_write(sc, R92C_OFDM0_AGCPARAM1, reg); reg = urtwn_bb_read(sc, 0xe74); reg = (reg & ~0x0c000000) | 2 << 26; urtwn_bb_write(sc, 0xe74, reg); reg = urtwn_bb_read(sc, 0xe78); reg = (reg & ~0x0c000000) | 2 << 26; urtwn_bb_write(sc, 0xe78, reg); reg = urtwn_bb_read(sc, 0xe7c); reg = (reg & ~0x0c000000) | 2 << 26; urtwn_bb_write(sc, 0xe7c, reg); reg = urtwn_bb_read(sc, 0xe80); reg = (reg & ~0x0c000000) | 2 << 26; urtwn_bb_write(sc, 0xe80, reg); reg = urtwn_bb_read(sc, 0xe88); reg = (reg & ~0x0c000000) | 2 << 26; urtwn_bb_write(sc, 0xe88, reg); } /* Write AGC values. */ for (i = 0; i < prog->agccount; i++) { urtwn_bb_write(sc, R92C_OFDM0_AGCRSSITABLE, prog->agcvals[i]); urtwn_ms_delay(sc); } if (sc->chip & URTWN_CHIP_88E) { urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x69553422); urtwn_ms_delay(sc); urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x69553420); urtwn_ms_delay(sc); crystalcap = sc->rom.r88e_rom.crystalcap; if (crystalcap == 0xff) crystalcap = 0x20; crystalcap &= 0x3f; reg = urtwn_bb_read(sc, R92C_AFE_XTAL_CTRL); urtwn_bb_write(sc, R92C_AFE_XTAL_CTRL, RW(reg, R92C_AFE_XTAL_CTRL_ADDR, crystalcap | crystalcap << 6)); } else { if (urtwn_bb_read(sc, R92C_HSSI_PARAM2(0)) & R92C_HSSI_PARAM2_CCK_HIPWR) sc->sc_flags |= URTWN_FLAG_CCK_HIPWR; } } static void urtwn_rf_init(struct urtwn_softc *sc) { const struct urtwn_rf_prog *prog; uint32_t reg, type; int i, j, idx, off; /* Select RF programming based on board type. */ if (sc->chip & URTWN_CHIP_88E) prog = rtl8188eu_rf_prog; else if (!(sc->chip & URTWN_CHIP_92C)) { if (sc->board_type == R92C_BOARD_TYPE_MINICARD) prog = rtl8188ce_rf_prog; else if (sc->board_type == R92C_BOARD_TYPE_HIGHPA) prog = rtl8188ru_rf_prog; else prog = rtl8188cu_rf_prog; } else prog = rtl8192ce_rf_prog; for (i = 0; i < sc->nrxchains; i++) { /* Save RF_ENV control type. */ idx = i / 2; off = (i % 2) * 16; reg = urtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(idx)); type = (reg >> off) & 0x10; /* Set RF_ENV enable. */ reg = urtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i)); reg |= 0x100000; urtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg); urtwn_ms_delay(sc); /* Set RF_ENV output high. */ reg = urtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i)); reg |= 0x10; urtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg); urtwn_ms_delay(sc); /* Set address and data lengths of RF registers. */ reg = urtwn_bb_read(sc, R92C_HSSI_PARAM2(i)); reg &= ~R92C_HSSI_PARAM2_ADDR_LENGTH; urtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg); urtwn_ms_delay(sc); reg = urtwn_bb_read(sc, R92C_HSSI_PARAM2(i)); reg &= ~R92C_HSSI_PARAM2_DATA_LENGTH; urtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg); urtwn_ms_delay(sc); /* Write RF initialization values for this chain. */ for (j = 0; j < prog[i].count; j++) { if (prog[i].regs[j] >= 0xf9 && prog[i].regs[j] <= 0xfe) { /* * These are fake RF registers offsets that * indicate a delay is required. */ usb_pause_mtx(&sc->sc_mtx, hz / 20); /* 50ms */ continue; } urtwn_rf_write(sc, i, prog[i].regs[j], prog[i].vals[j]); urtwn_ms_delay(sc); } /* Restore RF_ENV control type. */ reg = urtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(idx)); reg &= ~(0x10 << off) | (type << off); urtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(idx), reg); /* Cache RF register CHNLBW. */ sc->rf_chnlbw[i] = urtwn_rf_read(sc, i, R92C_RF_CHNLBW); } if ((sc->chip & (URTWN_CHIP_UMC_A_CUT | URTWN_CHIP_92C)) == URTWN_CHIP_UMC_A_CUT) { urtwn_rf_write(sc, 0, R92C_RF_RX_G1, 0x30255); urtwn_rf_write(sc, 0, R92C_RF_RX_G2, 0x50a00); } } static void urtwn_cam_init(struct urtwn_softc *sc) { /* Invalidate all CAM entries. */ urtwn_write_4(sc, R92C_CAMCMD, R92C_CAMCMD_POLLING | R92C_CAMCMD_CLR); } static int urtwn_cam_write(struct urtwn_softc *sc, uint32_t addr, uint32_t data) { usb_error_t error; error = urtwn_write_4(sc, R92C_CAMWRITE, data); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); error = urtwn_write_4(sc, R92C_CAMCMD, R92C_CAMCMD_POLLING | R92C_CAMCMD_WRITE | SM(R92C_CAMCMD_ADDR, addr)); if (error != USB_ERR_NORMAL_COMPLETION) return (EIO); return (0); } static void urtwn_pa_bias_init(struct urtwn_softc *sc) { uint8_t reg; int i; for (i = 0; i < sc->nrxchains; i++) { if (sc->pa_setting & (1 << i)) continue; urtwn_rf_write(sc, i, R92C_RF_IPA, 0x0f406); urtwn_rf_write(sc, i, R92C_RF_IPA, 0x4f406); urtwn_rf_write(sc, i, R92C_RF_IPA, 0x8f406); urtwn_rf_write(sc, i, R92C_RF_IPA, 0xcf406); } if (!(sc->pa_setting & 0x10)) { reg = urtwn_read_1(sc, 0x16); reg = (reg & ~0xf0) | 0x90; urtwn_write_1(sc, 0x16, reg); } } static void urtwn_rxfilter_init(struct urtwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t rcr; uint16_t filter; URTWN_ASSERT_LOCKED(sc); /* Setup multicast filter. */ urtwn_set_multi(sc); /* Filter for management frames. */ filter = 0x7f3f; switch (vap->iv_opmode) { case IEEE80211_M_STA: filter &= ~( R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_REQ) | R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_REQ) | R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_PROBE_REQ)); break; case IEEE80211_M_HOSTAP: filter &= ~( R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_RESP) | R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_RESP)); break; case IEEE80211_M_MONITOR: case IEEE80211_M_IBSS: break; default: device_printf(sc->sc_dev, "%s: undefined opmode %d\n", __func__, vap->iv_opmode); break; } urtwn_write_2(sc, R92C_RXFLTMAP0, filter); /* Reject all control frames. */ urtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000); /* Reject all data frames. */ urtwn_write_2(sc, R92C_RXFLTMAP2, 0x0000); rcr = R92C_RCR_AM | R92C_RCR_AB | R92C_RCR_APM | R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_PHYSTS | R92C_RCR_APP_ICV | R92C_RCR_APP_MIC; if (vap->iv_opmode == IEEE80211_M_MONITOR) { /* Accept all frames. */ rcr |= R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP; } /* Set Rx filter. */ urtwn_write_4(sc, R92C_RCR, rcr); if (ic->ic_promisc != 0) { /* Update Rx filter. */ urtwn_set_promisc(sc); } } static void urtwn_edca_init(struct urtwn_softc *sc) { urtwn_write_2(sc, R92C_SPEC_SIFS, 0x100a); urtwn_write_2(sc, R92C_MAC_SPEC_SIFS, 0x100a); urtwn_write_2(sc, R92C_SIFS_CCK, 0x100a); urtwn_write_2(sc, R92C_SIFS_OFDM, 0x100a); urtwn_write_4(sc, R92C_EDCA_BE_PARAM, 0x005ea42b); urtwn_write_4(sc, R92C_EDCA_BK_PARAM, 0x0000a44f); urtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005ea324); urtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002fa226); } static void urtwn_write_txpower(struct urtwn_softc *sc, int chain, uint16_t power[URTWN_RIDX_COUNT]) { uint32_t reg; /* Write per-CCK rate Tx power. */ if (chain == 0) { reg = urtwn_bb_read(sc, R92C_TXAGC_A_CCK1_MCS32); reg = RW(reg, R92C_TXAGC_A_CCK1, power[0]); urtwn_bb_write(sc, R92C_TXAGC_A_CCK1_MCS32, reg); reg = urtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11); reg = RW(reg, R92C_TXAGC_A_CCK2, power[1]); reg = RW(reg, R92C_TXAGC_A_CCK55, power[2]); reg = RW(reg, R92C_TXAGC_A_CCK11, power[3]); urtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg); } else { reg = urtwn_bb_read(sc, R92C_TXAGC_B_CCK1_55_MCS32); reg = RW(reg, R92C_TXAGC_B_CCK1, power[0]); reg = RW(reg, R92C_TXAGC_B_CCK2, power[1]); reg = RW(reg, R92C_TXAGC_B_CCK55, power[2]); urtwn_bb_write(sc, R92C_TXAGC_B_CCK1_55_MCS32, reg); reg = urtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11); reg = RW(reg, R92C_TXAGC_B_CCK11, power[3]); urtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg); } /* Write per-OFDM rate Tx power. */ urtwn_bb_write(sc, R92C_TXAGC_RATE18_06(chain), SM(R92C_TXAGC_RATE06, power[ 4]) | SM(R92C_TXAGC_RATE09, power[ 5]) | SM(R92C_TXAGC_RATE12, power[ 6]) | SM(R92C_TXAGC_RATE18, power[ 7])); urtwn_bb_write(sc, R92C_TXAGC_RATE54_24(chain), SM(R92C_TXAGC_RATE24, power[ 8]) | SM(R92C_TXAGC_RATE36, power[ 9]) | SM(R92C_TXAGC_RATE48, power[10]) | SM(R92C_TXAGC_RATE54, power[11])); /* Write per-MCS Tx power. */ urtwn_bb_write(sc, R92C_TXAGC_MCS03_MCS00(chain), SM(R92C_TXAGC_MCS00, power[12]) | SM(R92C_TXAGC_MCS01, power[13]) | SM(R92C_TXAGC_MCS02, power[14]) | SM(R92C_TXAGC_MCS03, power[15])); urtwn_bb_write(sc, R92C_TXAGC_MCS07_MCS04(chain), SM(R92C_TXAGC_MCS04, power[16]) | SM(R92C_TXAGC_MCS05, power[17]) | SM(R92C_TXAGC_MCS06, power[18]) | SM(R92C_TXAGC_MCS07, power[19])); urtwn_bb_write(sc, R92C_TXAGC_MCS11_MCS08(chain), SM(R92C_TXAGC_MCS08, power[20]) | SM(R92C_TXAGC_MCS09, power[21]) | SM(R92C_TXAGC_MCS10, power[22]) | SM(R92C_TXAGC_MCS11, power[23])); urtwn_bb_write(sc, R92C_TXAGC_MCS15_MCS12(chain), SM(R92C_TXAGC_MCS12, power[24]) | SM(R92C_TXAGC_MCS13, power[25]) | SM(R92C_TXAGC_MCS14, power[26]) | SM(R92C_TXAGC_MCS15, power[27])); } static void urtwn_get_txpower(struct urtwn_softc *sc, int chain, struct ieee80211_channel *c, struct ieee80211_channel *extc, uint16_t power[URTWN_RIDX_COUNT]) { struct ieee80211com *ic = &sc->sc_ic; struct r92c_rom *rom = &sc->rom.r92c_rom; uint16_t cckpow, ofdmpow, htpow, diff, max; const struct urtwn_txpwr *base; int ridx, chan, group; /* Determine channel group. */ chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ if (chan <= 3) group = 0; else if (chan <= 9) group = 1; else group = 2; /* Get original Tx power based on board type and RF chain. */ if (!(sc->chip & URTWN_CHIP_92C)) { if (sc->board_type == R92C_BOARD_TYPE_HIGHPA) base = &rtl8188ru_txagc[chain]; else base = &rtl8192cu_txagc[chain]; } else base = &rtl8192cu_txagc[chain]; memset(power, 0, URTWN_RIDX_COUNT * sizeof(power[0])); if (sc->regulatory == 0) { for (ridx = URTWN_RIDX_CCK1; ridx <= URTWN_RIDX_CCK11; ridx++) power[ridx] = base->pwr[0][ridx]; } for (ridx = URTWN_RIDX_OFDM6; ridx < URTWN_RIDX_COUNT; ridx++) { if (sc->regulatory == 3) { power[ridx] = base->pwr[0][ridx]; /* Apply vendor limits. */ if (extc != NULL) max = rom->ht40_max_pwr[group]; else max = rom->ht20_max_pwr[group]; max = (max >> (chain * 4)) & 0xf; if (power[ridx] > max) power[ridx] = max; } else if (sc->regulatory == 1) { if (extc == NULL) power[ridx] = base->pwr[group][ridx]; } else if (sc->regulatory != 2) power[ridx] = base->pwr[0][ridx]; } /* Compute per-CCK rate Tx power. */ cckpow = rom->cck_tx_pwr[chain][group]; for (ridx = URTWN_RIDX_CCK1; ridx <= URTWN_RIDX_CCK11; ridx++) { power[ridx] += cckpow; if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } htpow = rom->ht40_1s_tx_pwr[chain][group]; if (sc->ntxchains > 1) { /* Apply reduction for 2 spatial streams. */ diff = rom->ht40_2s_tx_pwr_diff[group]; diff = (diff >> (chain * 4)) & 0xf; htpow = (htpow > diff) ? htpow - diff : 0; } /* Compute per-OFDM rate Tx power. */ diff = rom->ofdm_tx_pwr_diff[group]; diff = (diff >> (chain * 4)) & 0xf; ofdmpow = htpow + diff; /* HT->OFDM correction. */ for (ridx = URTWN_RIDX_OFDM6; ridx <= URTWN_RIDX_OFDM54; ridx++) { power[ridx] += ofdmpow; if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } /* Compute per-MCS Tx power. */ if (extc == NULL) { diff = rom->ht20_tx_pwr_diff[group]; diff = (diff >> (chain * 4)) & 0xf; htpow += diff; /* HT40->HT20 correction. */ } for (ridx = 12; ridx <= 27; ridx++) { power[ridx] += htpow; if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } #ifdef USB_DEBUG if (sc->sc_debug & URTWN_DEBUG_TXPWR) { /* Dump per-rate Tx power values. */ printf("Tx power for chain %d:\n", chain); for (ridx = URTWN_RIDX_CCK1; ridx < URTWN_RIDX_COUNT; ridx++) printf("Rate %d = %u\n", ridx, power[ridx]); } #endif } static void urtwn_r88e_get_txpower(struct urtwn_softc *sc, int chain, struct ieee80211_channel *c, struct ieee80211_channel *extc, uint16_t power[URTWN_RIDX_COUNT]) { struct ieee80211com *ic = &sc->sc_ic; struct r88e_rom *rom = &sc->rom.r88e_rom; uint16_t cckpow, ofdmpow, bw20pow, htpow; const struct urtwn_r88e_txpwr *base; int ridx, chan, group; /* Determine channel group. */ chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ if (chan <= 2) group = 0; else if (chan <= 5) group = 1; else if (chan <= 8) group = 2; else if (chan <= 11) group = 3; else if (chan <= 13) group = 4; else group = 5; /* Get original Tx power based on board type and RF chain. */ base = &rtl8188eu_txagc[chain]; memset(power, 0, URTWN_RIDX_COUNT * sizeof(power[0])); if (sc->regulatory == 0) { for (ridx = URTWN_RIDX_CCK1; ridx <= URTWN_RIDX_CCK11; ridx++) power[ridx] = base->pwr[0][ridx]; } for (ridx = URTWN_RIDX_OFDM6; ridx < URTWN_RIDX_COUNT; ridx++) { if (sc->regulatory == 3) power[ridx] = base->pwr[0][ridx]; else if (sc->regulatory == 1) { if (extc == NULL) power[ridx] = base->pwr[group][ridx]; } else if (sc->regulatory != 2) power[ridx] = base->pwr[0][ridx]; } /* Compute per-CCK rate Tx power. */ cckpow = rom->cck_tx_pwr[group]; for (ridx = URTWN_RIDX_CCK1; ridx <= URTWN_RIDX_CCK11; ridx++) { power[ridx] += cckpow; if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } htpow = rom->ht40_tx_pwr[group]; /* Compute per-OFDM rate Tx power. */ ofdmpow = htpow + sc->ofdm_tx_pwr_diff; for (ridx = URTWN_RIDX_OFDM6; ridx <= URTWN_RIDX_OFDM54; ridx++) { power[ridx] += ofdmpow; if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } bw20pow = htpow + sc->bw20_tx_pwr_diff; for (ridx = 12; ridx <= 27; ridx++) { power[ridx] += bw20pow; if (power[ridx] > R92C_MAX_TX_PWR) power[ridx] = R92C_MAX_TX_PWR; } } static void urtwn_set_txpower(struct urtwn_softc *sc, struct ieee80211_channel *c, struct ieee80211_channel *extc) { uint16_t power[URTWN_RIDX_COUNT]; int i; for (i = 0; i < sc->ntxchains; i++) { /* Compute per-rate Tx power values. */ if (sc->chip & URTWN_CHIP_88E) urtwn_r88e_get_txpower(sc, i, c, extc, power); else urtwn_get_txpower(sc, i, c, extc, power); /* Write per-rate Tx power values to hardware. */ urtwn_write_txpower(sc, i, power); } } static void urtwn_set_rx_bssid_all(struct urtwn_softc *sc, int enable) { uint32_t reg; reg = urtwn_read_4(sc, R92C_RCR); if (enable) reg &= ~R92C_RCR_CBSSID_BCN; else reg |= R92C_RCR_CBSSID_BCN; urtwn_write_4(sc, R92C_RCR, reg); } static void urtwn_set_gain(struct urtwn_softc *sc, uint8_t gain) { uint32_t reg; reg = urtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(0)); reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, gain); urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg); if (!(sc->chip & URTWN_CHIP_88E)) { reg = urtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1)); reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, gain); urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg); } } static void urtwn_scan_start(struct ieee80211com *ic) { struct urtwn_softc *sc = ic->ic_softc; URTWN_LOCK(sc); /* Receive beacons / probe responses from any BSSID. */ if (ic->ic_opmode != IEEE80211_M_IBSS) urtwn_set_rx_bssid_all(sc, 1); /* Set gain for scanning. */ urtwn_set_gain(sc, 0x20); URTWN_UNLOCK(sc); } static void urtwn_scan_end(struct ieee80211com *ic) { struct urtwn_softc *sc = ic->ic_softc; URTWN_LOCK(sc); /* Restore limitations. */ if (ic->ic_promisc == 0 && ic->ic_opmode != IEEE80211_M_IBSS) urtwn_set_rx_bssid_all(sc, 0); /* Set gain under link. */ urtwn_set_gain(sc, 0x32); URTWN_UNLOCK(sc); +} + +static void +urtwn_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + uint8_t bands[IEEE80211_MODE_BYTES]; + + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + if (urtwn_enable_11n) + setbit(bands, IEEE80211_MODE_11NG); + ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, + urtwn_chan_2ghz, nitems(urtwn_chan_2ghz), bands, 0); } static void urtwn_set_channel(struct ieee80211com *ic) { struct urtwn_softc *sc = ic->ic_softc; struct ieee80211_channel *c = ic->ic_curchan; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); URTWN_LOCK(sc); if (vap->iv_state == IEEE80211_S_SCAN) { /* Make link LED blink during scan. */ urtwn_set_led(sc, URTWN_LED_LINK, !sc->ledlink); } urtwn_set_chan(sc, c, NULL); sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); URTWN_UNLOCK(sc); } static int urtwn_wme_update(struct ieee80211com *ic) { const struct wmeParams *wmep = ic->ic_wme.wme_chanParams.cap_wmeParams; struct urtwn_softc *sc = ic->ic_softc; uint8_t aifs, acm, slottime; int ac; acm = 0; slottime = IEEE80211_GET_SLOTTIME(ic); URTWN_LOCK(sc); for (ac = WME_AC_BE; ac < WME_NUM_AC; ac++) { /* AIFS[AC] = AIFSN[AC] * aSlotTime + aSIFSTime. */ aifs = wmep[ac].wmep_aifsn * slottime + IEEE80211_DUR_SIFS; urtwn_write_4(sc, wme2queue[ac].reg, SM(R92C_EDCA_PARAM_TXOP, wmep[ac].wmep_txopLimit) | SM(R92C_EDCA_PARAM_ECWMIN, wmep[ac].wmep_logcwmin) | SM(R92C_EDCA_PARAM_ECWMAX, wmep[ac].wmep_logcwmax) | SM(R92C_EDCA_PARAM_AIFS, aifs)); if (ac != WME_AC_BE) acm |= wmep[ac].wmep_acm << ac; } if (acm != 0) acm |= R92C_ACMHWCTRL_EN; urtwn_write_1(sc, R92C_ACMHWCTRL, (urtwn_read_1(sc, R92C_ACMHWCTRL) & ~R92C_ACMHWCTRL_ACM_MASK) | acm); URTWN_UNLOCK(sc); return 0; } static void urtwn_update_slot(struct ieee80211com *ic) { urtwn_cmd_sleepable(ic->ic_softc, NULL, 0, urtwn_update_slot_cb); } static void urtwn_update_slot_cb(struct urtwn_softc *sc, union sec_param *data) { struct ieee80211com *ic = &sc->sc_ic; uint8_t slottime; slottime = IEEE80211_GET_SLOTTIME(ic); URTWN_DPRINTF(sc, URTWN_DEBUG_ANY, "%s: setting slot time to %uus\n", __func__, slottime); urtwn_write_1(sc, R92C_SLOT, slottime); urtwn_update_aifs(sc, slottime); } static void urtwn_update_aifs(struct urtwn_softc *sc, uint8_t slottime) { const struct wmeParams *wmep = sc->sc_ic.ic_wme.wme_chanParams.cap_wmeParams; uint8_t aifs, ac; for (ac = WME_AC_BE; ac < WME_NUM_AC; ac++) { /* AIFS[AC] = AIFSN[AC] * aSlotTime + aSIFSTime. */ aifs = wmep[ac].wmep_aifsn * slottime + IEEE80211_DUR_SIFS; urtwn_write_1(sc, wme2queue[ac].reg, aifs); } } static uint8_t urtwn_get_multi_pos(const uint8_t maddr[]) { uint64_t mask = 0x00004d101df481b4; uint8_t pos = 0x27; /* initial value */ int i, j; for (i = 0; i < IEEE80211_ADDR_LEN; i++) for (j = (i == 0) ? 1 : 0; j < 8; j++) if ((maddr[i] >> j) & 1) pos ^= (mask >> (i * 8 + j - 1)); pos &= 0x3f; return (pos); } static void urtwn_set_multi(struct urtwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t mfilt[2]; URTWN_ASSERT_LOCKED(sc); /* general structure was copied from ath(4). */ if (ic->ic_allmulti == 0) { struct ieee80211vap *vap; struct ifnet *ifp; struct ifmultiaddr *ifma; /* * Merge multicast addresses to form the hardware filter. */ mfilt[0] = mfilt[1] = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { ifp = vap->iv_ifp; if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { caddr_t dl; uint8_t pos; dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); pos = urtwn_get_multi_pos(dl); mfilt[pos / 32] |= (1 << (pos % 32)); } if_maddr_runlock(ifp); } } else mfilt[0] = mfilt[1] = ~0; urtwn_write_4(sc, R92C_MAR + 0, mfilt[0]); urtwn_write_4(sc, R92C_MAR + 4, mfilt[1]); URTWN_DPRINTF(sc, URTWN_DEBUG_STATE, "%s: MC filter %08x:%08x\n", __func__, mfilt[0], mfilt[1]); } static void urtwn_set_promisc(struct urtwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t rcr, mask1, mask2; URTWN_ASSERT_LOCKED(sc); if (vap->iv_opmode == IEEE80211_M_MONITOR) return; mask1 = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP; mask2 = R92C_RCR_APM; if (vap->iv_state == IEEE80211_S_RUN) { switch (vap->iv_opmode) { case IEEE80211_M_STA: mask2 |= R92C_RCR_CBSSID_DATA; /* FALLTHROUGH */ case IEEE80211_M_HOSTAP: mask2 |= R92C_RCR_CBSSID_BCN; break; case IEEE80211_M_IBSS: mask2 |= R92C_RCR_CBSSID_DATA; break; default: device_printf(sc->sc_dev, "%s: undefined opmode %d\n", __func__, vap->iv_opmode); return; } } rcr = urtwn_read_4(sc, R92C_RCR); if (ic->ic_promisc == 0) rcr = (rcr & ~mask1) | mask2; else rcr = (rcr & ~mask2) | mask1; urtwn_write_4(sc, R92C_RCR, rcr); } static void urtwn_update_promisc(struct ieee80211com *ic) { struct urtwn_softc *sc = ic->ic_softc; URTWN_LOCK(sc); if (sc->sc_flags & URTWN_RUNNING) urtwn_set_promisc(sc); URTWN_UNLOCK(sc); } static void urtwn_update_mcast(struct ieee80211com *ic) { struct urtwn_softc *sc = ic->ic_softc; URTWN_LOCK(sc); if (sc->sc_flags & URTWN_RUNNING) urtwn_set_multi(sc); URTWN_UNLOCK(sc); } static struct ieee80211_node * urtwn_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct urtwn_node *un; un = malloc(sizeof (struct urtwn_node), M_80211_NODE, M_NOWAIT | M_ZERO); if (un == NULL) return NULL; un->id = URTWN_MACID_UNDEFINED; return &un->ni; } static void urtwn_newassoc(struct ieee80211_node *ni, int isnew) { struct urtwn_softc *sc = ni->ni_ic->ic_softc; struct urtwn_node *un = URTWN_NODE(ni); uint8_t id; /* Only do this bit for R88E chips */ if (! (sc->chip & URTWN_CHIP_88E)) return; if (!isnew) return; URTWN_NT_LOCK(sc); for (id = 0; id <= URTWN_MACID_MAX(sc); id++) { if (id != URTWN_MACID_BC && sc->node_list[id] == NULL) { un->id = id; sc->node_list[id] = ni; break; } } URTWN_NT_UNLOCK(sc); if (id > URTWN_MACID_MAX(sc)) { device_printf(sc->sc_dev, "%s: node table is full\n", __func__); } } static void urtwn_node_free(struct ieee80211_node *ni) { struct urtwn_softc *sc = ni->ni_ic->ic_softc; struct urtwn_node *un = URTWN_NODE(ni); URTWN_NT_LOCK(sc); if (un->id != URTWN_MACID_UNDEFINED) sc->node_list[un->id] = NULL; URTWN_NT_UNLOCK(sc); sc->sc_node_free(ni); } static void urtwn_set_chan(struct urtwn_softc *sc, struct ieee80211_channel *c, struct ieee80211_channel *extc) { struct ieee80211com *ic = &sc->sc_ic; uint32_t reg; u_int chan; int i; chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ if (chan == 0 || chan == IEEE80211_CHAN_ANY) { device_printf(sc->sc_dev, "%s: invalid channel %x\n", __func__, chan); return; } /* Set Tx power for this new channel. */ urtwn_set_txpower(sc, c, extc); for (i = 0; i < sc->nrxchains; i++) { urtwn_rf_write(sc, i, R92C_RF_CHNLBW, RW(sc->rf_chnlbw[i], R92C_RF_CHNLBW_CHNL, chan)); } #ifndef IEEE80211_NO_HT if (extc != NULL) { /* Is secondary channel below or above primary? */ int prichlo = c->ic_freq < extc->ic_freq; urtwn_write_1(sc, R92C_BWOPMODE, urtwn_read_1(sc, R92C_BWOPMODE) & ~R92C_BWOPMODE_20MHZ); reg = urtwn_read_1(sc, R92C_RRSR + 2); reg = (reg & ~0x6f) | (prichlo ? 1 : 2) << 5; urtwn_write_1(sc, R92C_RRSR + 2, reg); urtwn_bb_write(sc, R92C_FPGA0_RFMOD, urtwn_bb_read(sc, R92C_FPGA0_RFMOD) | R92C_RFMOD_40MHZ); urtwn_bb_write(sc, R92C_FPGA1_RFMOD, urtwn_bb_read(sc, R92C_FPGA1_RFMOD) | R92C_RFMOD_40MHZ); /* Set CCK side band. */ reg = urtwn_bb_read(sc, R92C_CCK0_SYSTEM); reg = (reg & ~0x00000010) | (prichlo ? 0 : 1) << 4; urtwn_bb_write(sc, R92C_CCK0_SYSTEM, reg); reg = urtwn_bb_read(sc, R92C_OFDM1_LSTF); reg = (reg & ~0x00000c00) | (prichlo ? 1 : 2) << 10; urtwn_bb_write(sc, R92C_OFDM1_LSTF, reg); urtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2, urtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) & ~R92C_FPGA0_ANAPARAM2_CBW20); reg = urtwn_bb_read(sc, 0x818); reg = (reg & ~0x0c000000) | (prichlo ? 2 : 1) << 26; urtwn_bb_write(sc, 0x818, reg); /* Select 40MHz bandwidth. */ urtwn_rf_write(sc, 0, R92C_RF_CHNLBW, (sc->rf_chnlbw[0] & ~0xfff) | chan); } else #endif { urtwn_write_1(sc, R92C_BWOPMODE, urtwn_read_1(sc, R92C_BWOPMODE) | R92C_BWOPMODE_20MHZ); urtwn_bb_write(sc, R92C_FPGA0_RFMOD, urtwn_bb_read(sc, R92C_FPGA0_RFMOD) & ~R92C_RFMOD_40MHZ); urtwn_bb_write(sc, R92C_FPGA1_RFMOD, urtwn_bb_read(sc, R92C_FPGA1_RFMOD) & ~R92C_RFMOD_40MHZ); if (!(sc->chip & URTWN_CHIP_88E)) { urtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2, urtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) | R92C_FPGA0_ANAPARAM2_CBW20); } /* Select 20MHz bandwidth. */ urtwn_rf_write(sc, 0, R92C_RF_CHNLBW, (sc->rf_chnlbw[0] & ~0xfff) | chan | ((sc->chip & URTWN_CHIP_88E) ? R88E_RF_CHNLBW_BW20 : R92C_RF_CHNLBW_BW20)); } } static void urtwn_iq_calib(struct urtwn_softc *sc) { /* TODO */ } static void urtwn_lc_calib(struct urtwn_softc *sc) { uint32_t rf_ac[2]; uint8_t txmode; int i; txmode = urtwn_read_1(sc, R92C_OFDM1_LSTF + 3); if ((txmode & 0x70) != 0) { /* Disable all continuous Tx. */ urtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode & ~0x70); /* Set RF mode to standby mode. */ for (i = 0; i < sc->nrxchains; i++) { rf_ac[i] = urtwn_rf_read(sc, i, R92C_RF_AC); urtwn_rf_write(sc, i, R92C_RF_AC, RW(rf_ac[i], R92C_RF_AC_MODE, R92C_RF_AC_MODE_STANDBY)); } } else { /* Block all Tx queues. */ urtwn_write_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_ALL); } /* Start calibration. */ urtwn_rf_write(sc, 0, R92C_RF_CHNLBW, urtwn_rf_read(sc, 0, R92C_RF_CHNLBW) | R92C_RF_CHNLBW_LCSTART); /* Give calibration the time to complete. */ usb_pause_mtx(&sc->sc_mtx, hz / 10); /* 100ms */ /* Restore configuration. */ if ((txmode & 0x70) != 0) { /* Restore Tx mode. */ urtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode); /* Restore RF mode. */ for (i = 0; i < sc->nrxchains; i++) urtwn_rf_write(sc, i, R92C_RF_AC, rf_ac[i]); } else { /* Unblock all Tx queues. */ urtwn_write_1(sc, R92C_TXPAUSE, 0x00); } } static void urtwn_temp_calib(struct urtwn_softc *sc) { uint8_t temp; URTWN_ASSERT_LOCKED(sc); if (!(sc->sc_flags & URTWN_TEMP_MEASURED)) { /* Start measuring temperature. */ URTWN_DPRINTF(sc, URTWN_DEBUG_TEMP, "%s: start measuring temperature\n", __func__); if (sc->chip & URTWN_CHIP_88E) { urtwn_rf_write(sc, 0, R88E_RF_T_METER, R88E_RF_T_METER_START); } else { urtwn_rf_write(sc, 0, R92C_RF_T_METER, R92C_RF_T_METER_START); } sc->sc_flags |= URTWN_TEMP_MEASURED; return; } sc->sc_flags &= ~URTWN_TEMP_MEASURED; /* Read measured temperature. */ if (sc->chip & URTWN_CHIP_88E) { temp = MS(urtwn_rf_read(sc, 0, R88E_RF_T_METER), R88E_RF_T_METER_VAL); } else { temp = MS(urtwn_rf_read(sc, 0, R92C_RF_T_METER), R92C_RF_T_METER_VAL); } if (temp == 0) { /* Read failed, skip. */ URTWN_DPRINTF(sc, URTWN_DEBUG_TEMP, "%s: temperature read failed, skipping\n", __func__); return; } URTWN_DPRINTF(sc, URTWN_DEBUG_TEMP, "%s: temperature: previous %u, current %u\n", __func__, sc->thcal_lctemp, temp); /* * Redo LC calibration if temperature changed significantly since * last calibration. */ if (sc->thcal_lctemp == 0) { /* First LC calibration is performed in urtwn_init(). */ sc->thcal_lctemp = temp; } else if (abs(temp - sc->thcal_lctemp) > 1) { URTWN_DPRINTF(sc, URTWN_DEBUG_TEMP, "%s: LC calib triggered by temp: %u -> %u\n", __func__, sc->thcal_lctemp, temp); urtwn_lc_calib(sc); /* Record temperature of last LC calibration. */ sc->thcal_lctemp = temp; } } static int urtwn_init(struct urtwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint8_t macaddr[IEEE80211_ADDR_LEN]; uint32_t reg; usb_error_t usb_err = USB_ERR_NORMAL_COMPLETION; int error; URTWN_LOCK(sc); if (sc->sc_flags & URTWN_RUNNING) { URTWN_UNLOCK(sc); return (0); } /* Init firmware commands ring. */ sc->fwcur = 0; /* Allocate Tx/Rx buffers. */ error = urtwn_alloc_rx_list(sc); if (error != 0) goto fail; error = urtwn_alloc_tx_list(sc); if (error != 0) goto fail; /* Power on adapter. */ error = urtwn_power_on(sc); if (error != 0) goto fail; /* Initialize DMA. */ error = urtwn_dma_init(sc); if (error != 0) goto fail; /* Set info size in Rx descriptors (in 64-bit words). */ urtwn_write_1(sc, R92C_RX_DRVINFO_SZ, 4); /* Init interrupts. */ if (sc->chip & URTWN_CHIP_88E) { usb_err = urtwn_write_4(sc, R88E_HISR, 0xffffffff); if (usb_err != USB_ERR_NORMAL_COMPLETION) goto fail; usb_err = urtwn_write_4(sc, R88E_HIMR, R88E_HIMR_CPWM | R88E_HIMR_CPWM2 | R88E_HIMR_TBDER | R88E_HIMR_PSTIMEOUT); if (usb_err != USB_ERR_NORMAL_COMPLETION) goto fail; usb_err = urtwn_write_4(sc, R88E_HIMRE, R88E_HIMRE_RXFOVW | R88E_HIMRE_TXFOVW | R88E_HIMRE_RXERR | R88E_HIMRE_TXERR); if (usb_err != USB_ERR_NORMAL_COMPLETION) goto fail; usb_err = urtwn_write_1(sc, R92C_USB_SPECIAL_OPTION, urtwn_read_1(sc, R92C_USB_SPECIAL_OPTION) | R92C_USB_SPECIAL_OPTION_INT_BULK_SEL); if (usb_err != USB_ERR_NORMAL_COMPLETION) goto fail; } else { usb_err = urtwn_write_4(sc, R92C_HISR, 0xffffffff); if (usb_err != USB_ERR_NORMAL_COMPLETION) goto fail; usb_err = urtwn_write_4(sc, R92C_HIMR, 0xffffffff); if (usb_err != USB_ERR_NORMAL_COMPLETION) goto fail; } /* Set MAC address. */ IEEE80211_ADDR_COPY(macaddr, vap ? vap->iv_myaddr : ic->ic_macaddr); usb_err = urtwn_write_region_1(sc, R92C_MACID, macaddr, IEEE80211_ADDR_LEN); if (usb_err != USB_ERR_NORMAL_COMPLETION) goto fail; /* Set initial network type. */ urtwn_set_mode(sc, R92C_MSR_INFRA); /* Initialize Rx filter. */ urtwn_rxfilter_init(sc); /* Set response rate. */ reg = urtwn_read_4(sc, R92C_RRSR); reg = RW(reg, R92C_RRSR_RATE_BITMAP, R92C_RRSR_RATE_CCK_ONLY_1M); urtwn_write_4(sc, R92C_RRSR, reg); /* Set short/long retry limits. */ urtwn_write_2(sc, R92C_RL, SM(R92C_RL_SRL, 0x30) | SM(R92C_RL_LRL, 0x30)); /* Initialize EDCA parameters. */ urtwn_edca_init(sc); /* Setup rate fallback. */ if (!(sc->chip & URTWN_CHIP_88E)) { urtwn_write_4(sc, R92C_DARFRC + 0, 0x00000000); urtwn_write_4(sc, R92C_DARFRC + 4, 0x10080404); urtwn_write_4(sc, R92C_RARFRC + 0, 0x04030201); urtwn_write_4(sc, R92C_RARFRC + 4, 0x08070605); } urtwn_write_1(sc, R92C_FWHW_TXQ_CTRL, urtwn_read_1(sc, R92C_FWHW_TXQ_CTRL) | R92C_FWHW_TXQ_CTRL_AMPDU_RTY_NEW); /* Set ACK timeout. */ urtwn_write_1(sc, R92C_ACKTO, 0x40); /* Setup USB aggregation. */ reg = urtwn_read_4(sc, R92C_TDECTRL); reg = RW(reg, R92C_TDECTRL_BLK_DESC_NUM, 6); urtwn_write_4(sc, R92C_TDECTRL, reg); urtwn_write_1(sc, R92C_TRXDMA_CTRL, urtwn_read_1(sc, R92C_TRXDMA_CTRL) | R92C_TRXDMA_CTRL_RXDMA_AGG_EN); urtwn_write_1(sc, R92C_RXDMA_AGG_PG_TH, 48); if (sc->chip & URTWN_CHIP_88E) urtwn_write_1(sc, R92C_RXDMA_AGG_PG_TH + 1, 4); else { urtwn_write_1(sc, R92C_USB_DMA_AGG_TO, 4); urtwn_write_1(sc, R92C_USB_SPECIAL_OPTION, urtwn_read_1(sc, R92C_USB_SPECIAL_OPTION) | R92C_USB_SPECIAL_OPTION_AGG_EN); urtwn_write_1(sc, R92C_USB_AGG_TH, 8); urtwn_write_1(sc, R92C_USB_AGG_TO, 6); } /* Initialize beacon parameters. */ urtwn_write_2(sc, R92C_BCN_CTRL, 0x1010); urtwn_write_2(sc, R92C_TBTT_PROHIBIT, 0x6404); urtwn_write_1(sc, R92C_DRVERLYINT, 0x05); urtwn_write_1(sc, R92C_BCNDMATIM, 0x02); urtwn_write_2(sc, R92C_BCNTCFG, 0x660f); if (!(sc->chip & URTWN_CHIP_88E)) { /* Setup AMPDU aggregation. */ urtwn_write_4(sc, R92C_AGGLEN_LMT, 0x99997631); /* MCS7~0 */ urtwn_write_1(sc, R92C_AGGR_BREAK_TIME, 0x16); urtwn_write_2(sc, R92C_MAX_AGGR_NUM, 0x0708); urtwn_write_1(sc, R92C_BCN_MAX_ERR, 0xff); } #ifndef URTWN_WITHOUT_UCODE /* Load 8051 microcode. */ error = urtwn_load_firmware(sc); if (error == 0) sc->sc_flags |= URTWN_FW_LOADED; #endif /* Initialize MAC/BB/RF blocks. */ error = urtwn_mac_init(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: error while initializing MAC block\n", __func__); goto fail; } urtwn_bb_init(sc); urtwn_rf_init(sc); /* Reinitialize Rx filter (D3845 is not committed yet). */ urtwn_rxfilter_init(sc); if (sc->chip & URTWN_CHIP_88E) { urtwn_write_2(sc, R92C_CR, urtwn_read_2(sc, R92C_CR) | R92C_CR_MACTXEN | R92C_CR_MACRXEN); } /* Turn CCK and OFDM blocks on. */ reg = urtwn_bb_read(sc, R92C_FPGA0_RFMOD); reg |= R92C_RFMOD_CCK_EN; usb_err = urtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg); if (usb_err != USB_ERR_NORMAL_COMPLETION) goto fail; reg = urtwn_bb_read(sc, R92C_FPGA0_RFMOD); reg |= R92C_RFMOD_OFDM_EN; usb_err = urtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg); if (usb_err != USB_ERR_NORMAL_COMPLETION) goto fail; /* Clear per-station keys table. */ urtwn_cam_init(sc); /* Enable decryption / encryption. */ urtwn_write_2(sc, R92C_SECCFG, R92C_SECCFG_TXUCKEY_DEF | R92C_SECCFG_RXUCKEY_DEF | R92C_SECCFG_TXENC_ENA | R92C_SECCFG_RXDEC_ENA | R92C_SECCFG_TXBCKEY_DEF | R92C_SECCFG_RXBCKEY_DEF); /* * Install static keys (if any). * Must be called after urtwn_cam_init(). */ ieee80211_runtask(ic, &sc->cmdq_task); /* Enable hardware sequence numbering. */ urtwn_write_1(sc, R92C_HWSEQ_CTRL, R92C_TX_QUEUE_ALL); /* Enable per-packet TX report. */ if (sc->chip & URTWN_CHIP_88E) { urtwn_write_1(sc, R88E_TX_RPT_CTRL, urtwn_read_1(sc, R88E_TX_RPT_CTRL) | R88E_TX_RPT1_ENA); } /* Perform LO and IQ calibrations. */ urtwn_iq_calib(sc); /* Perform LC calibration. */ urtwn_lc_calib(sc); /* Fix USB interference issue. */ if (!(sc->chip & URTWN_CHIP_88E)) { urtwn_write_1(sc, 0xfe40, 0xe0); urtwn_write_1(sc, 0xfe41, 0x8d); urtwn_write_1(sc, 0xfe42, 0x80); urtwn_pa_bias_init(sc); } /* Initialize GPIO setting. */ urtwn_write_1(sc, R92C_GPIO_MUXCFG, urtwn_read_1(sc, R92C_GPIO_MUXCFG) & ~R92C_GPIO_MUXCFG_ENBT); /* Fix for lower temperature. */ if (!(sc->chip & URTWN_CHIP_88E)) urtwn_write_1(sc, 0x15, 0xe9); usbd_transfer_start(sc->sc_xfer[URTWN_BULK_RX]); sc->sc_flags |= URTWN_RUNNING; callout_reset(&sc->sc_watchdog_ch, hz, urtwn_watchdog, sc); fail: if (usb_err != USB_ERR_NORMAL_COMPLETION) error = EIO; URTWN_UNLOCK(sc); return (error); } static void urtwn_stop(struct urtwn_softc *sc) { URTWN_LOCK(sc); if (!(sc->sc_flags & URTWN_RUNNING)) { URTWN_UNLOCK(sc); return; } sc->sc_flags &= ~(URTWN_RUNNING | URTWN_FW_LOADED | URTWN_TEMP_MEASURED); sc->thcal_lctemp = 0; callout_stop(&sc->sc_watchdog_ch); urtwn_abort_xfers(sc); urtwn_drain_mbufq(sc); urtwn_power_off(sc); URTWN_UNLOCK(sc); } static void urtwn_abort_xfers(struct urtwn_softc *sc) { int i; URTWN_ASSERT_LOCKED(sc); /* abort any pending transfers */ for (i = 0; i < URTWN_N_TRANSFER; i++) usbd_transfer_stop(sc->sc_xfer[i]); } static int urtwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct urtwn_softc *sc = ic->ic_softc; struct urtwn_data *bf; int error; URTWN_DPRINTF(sc, URTWN_DEBUG_XMIT, "%s: called; m=%p\n", __func__, m); /* prevent management frames from being sent if we're not ready */ URTWN_LOCK(sc); if (!(sc->sc_flags & URTWN_RUNNING)) { error = ENETDOWN; goto end; } bf = urtwn_getbuf(sc); if (bf == NULL) { error = ENOBUFS; goto end; } if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ error = urtwn_tx_data(sc, ni, m, bf); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ error = urtwn_tx_raw(sc, ni, m, bf, params); } if (error != 0) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); goto end; } sc->sc_txtimer = 5; callout_reset(&sc->sc_watchdog_ch, hz, urtwn_watchdog, sc); end: if (error != 0) m_freem(m); URTWN_UNLOCK(sc); return (error); } static void urtwn_ms_delay(struct urtwn_softc *sc) { usb_pause_mtx(&sc->sc_mtx, hz / 1000); } static device_method_t urtwn_methods[] = { /* Device interface */ DEVMETHOD(device_probe, urtwn_match), DEVMETHOD(device_attach, urtwn_attach), DEVMETHOD(device_detach, urtwn_detach), DEVMETHOD_END }; static driver_t urtwn_driver = { "urtwn", urtwn_methods, sizeof(struct urtwn_softc) }; static devclass_t urtwn_devclass; DRIVER_MODULE(urtwn, uhub, urtwn_driver, urtwn_devclass, NULL, NULL); MODULE_DEPEND(urtwn, usb, 1, 1, 1); MODULE_DEPEND(urtwn, wlan, 1, 1, 1); #ifndef URTWN_WITHOUT_UCODE MODULE_DEPEND(urtwn, firmware, 1, 1, 1); #endif MODULE_VERSION(urtwn, 1); USB_PNP_HOST_INFO(urtwn_devs); Index: head/sys/dev/urtwn/if_urtwnreg.h =================================================================== --- head/sys/dev/urtwn/if_urtwnreg.h (revision 300753) +++ head/sys/dev/urtwn/if_urtwnreg.h (revision 300754) @@ -1,2180 +1,2182 @@ /*- * Copyright (c) 2010 Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $OpenBSD: if_urtwnreg.h,v 1.3 2010/11/16 18:02:59 damien Exp $ * $FreeBSD$ */ #define URTWN_CONFIG_INDEX 0 #define URTWN_IFACE_INDEX 0 #define URTWN_NOISE_FLOOR -95 #define R92C_MAX_CHAINS 2 /* Maximum number of output pipes is 3. */ #define R92C_MAX_EPOUT 3 #define R92C_MAX_TX_PWR 0x3f #define R92C_PUBQ_NPAGES 231 #define R92C_TXPKTBUF_COUNT 256 #define R92C_TX_PAGE_COUNT 248 #define R92C_TX_PAGE_BOUNDARY (R92C_TX_PAGE_COUNT + 1) #define R88E_TXPKTBUF_COUNT 177 #define R88E_TX_PAGE_COUNT 169 #define R88E_TX_PAGE_BOUNDARY (R88E_TX_PAGE_COUNT + 1) #define R92C_H2C_NBOX 4 /* USB Requests. */ #define R92C_REQ_REGS 0x05 /* * MAC registers. */ /* System Configuration. */ #define R92C_SYS_ISO_CTRL 0x000 #define R92C_SYS_FUNC_EN 0x002 #define R92C_APS_FSMCO 0x004 #define R92C_SYS_CLKR 0x008 #define R92C_AFE_MISC 0x010 #define R92C_SPS0_CTRL 0x011 #define R92C_SPS_OCP_CFG 0x018 #define R92C_RSV_CTRL 0x01c #define R92C_RF_CTRL 0x01f #define R92C_LDOA15_CTRL 0x020 #define R92C_LDOV12D_CTRL 0x021 #define R92C_LDOHCI12_CTRL 0x022 #define R92C_LPLDO_CTRL 0x023 #define R92C_AFE_XTAL_CTRL 0x024 #define R92C_AFE_PLL_CTRL 0x028 #define R92C_EFUSE_CTRL 0x030 #define R92C_EFUSE_TEST 0x034 #define R92C_PWR_DATA 0x038 #define R92C_CAL_TIMER 0x03c #define R92C_ACLK_MON 0x03e #define R92C_GPIO_MUXCFG 0x040 #define R92C_GPIO_IO_SEL 0x042 #define R92C_MAC_PINMUX_CFG 0x043 #define R92C_GPIO_PIN_CTRL 0x044 #define R92C_GPIO_IN 0x044 #define R92C_GPIO_OUT 0x045 #define R92C_GPIO_IOSEL 0x046 #define R92C_GPIO_MOD 0x047 #define R92C_GPIO_INTM 0x048 #define R92C_LEDCFG0 0x04c #define R92C_LEDCFG1 0x04d #define R92C_LEDCFG2 0x04e #define R92C_LEDCFG3 0x04f #define R92C_FSIMR 0x050 #define R92C_FSISR 0x054 #define R92C_HSIMR 0x058 #define R92C_HSISR 0x05c #define R88E_BB_PAD_CTRL 0x064 #define R92C_MCUFWDL 0x080 #define R92C_HMEBOX_EXT(idx) (0x088 + (idx) * 2) #define R88E_HIMR 0x0b0 #define R88E_HISR 0x0b4 #define R88E_HIMRE 0x0b8 #define R88E_HISRE 0x0bc #define R92C_EFUSE_ACCESS 0x0cf #define R92C_BIST_SCAN 0x0d0 #define R92C_BIST_RPT 0x0d4 #define R92C_BIST_ROM_RPT 0x0d8 #define R92C_USB_SIE_INTF 0x0e0 #define R92C_PCIE_MIO_INTF 0x0e4 #define R92C_PCIE_MIO_INTD 0x0e8 #define R92C_HPON_FSM 0x0ec #define R92C_SYS_CFG 0x0f0 /* MAC General Configuration. */ #define R92C_CR 0x100 #define R92C_MSR 0x102 #define R92C_PBP 0x104 #define R92C_TRXDMA_CTRL 0x10c #define R92C_TRXFF_BNDY 0x114 #define R92C_TRXFF_STATUS 0x118 #define R92C_RXFF_PTR 0x11c #define R92C_HIMR 0x120 #define R92C_HISR 0x124 #define R92C_HIMRE 0x128 #define R92C_HISRE 0x12c #define R92C_CPWM 0x12f #define R92C_FWIMR 0x130 #define R92C_FWISR 0x134 #define R92C_PKTBUF_DBG_CTRL 0x140 #define R92C_PKTBUF_DBG_DATA_L 0x144 #define R92C_PKTBUF_DBG_DATA_H 0x148 #define R92C_TC0_CTRL(i) (0x150 + (i) * 4) #define R92C_TCUNIT_BASE 0x164 #define R92C_MBIST_START 0x174 #define R92C_MBIST_DONE 0x178 #define R92C_MBIST_FAIL 0x17c #define R88E_32K_CTRL 0x194 #define R92C_C2HEVT_MSG_NORMAL 0x1a0 #define R92C_C2HEVT_MSG_TEST 0x1b8 #define R92C_C2HEVT_CLEAR 0x1bf #define R92C_MCUTST_1 0x1c0 #define R92C_FMETHR 0x1c8 #define R92C_HMETFR 0x1cc #define R92C_HMEBOX(idx) (0x1d0 + (idx) * 4) #define R92C_LLT_INIT 0x1e0 #define R92C_BB_ACCESS_CTRL 0x1e8 #define R92C_BB_ACCESS_DATA 0x1ec #define R88E_HMEBOX_EXT(idx) (0x1f0 + (idx) * 4) /* Tx DMA Configuration. */ #define R92C_RQPN 0x200 #define R92C_FIFOPAGE 0x204 #define R92C_TDECTRL 0x208 #define R92C_TXDMA_OFFSET_CHK 0x20c #define R92C_TXDMA_STATUS 0x210 #define R92C_RQPN_NPQ 0x214 /* Rx DMA Configuration. */ #define R92C_RXDMA_AGG_PG_TH 0x280 #define R92C_RXPKT_NUM 0x284 #define R92C_RXDMA_STATUS 0x288 /* Protocol Configuration. */ #define R92C_FWHW_TXQ_CTRL 0x420 #define R92C_HWSEQ_CTRL 0x423 #define R92C_TXPKTBUF_BCNQ_BDNY 0x424 #define R92C_TXPKTBUF_MGQ_BDNY 0x425 #define R92C_SPEC_SIFS 0x428 #define R92C_RL 0x42a #define R92C_DARFRC 0x430 #define R92C_RARFRC 0x438 #define R92C_RRSR 0x440 #define R92C_ARFR(i) (0x444 + (i) * 4) #define R92C_AGGLEN_LMT 0x458 #define R92C_AMPDU_MIN_SPACE 0x45c #define R92C_TXPKTBUF_WMAC_LBK_BF_HD 0x45d #define R92C_FAST_EDCA_CTRL 0x460 #define R92C_RD_RESP_PKT_TH 0x463 #define R92C_INIRTS_RATE_SEL 0x480 #define R92C_INIDATA_RATE_SEL(macid) (0x484 + (macid)) #define R92C_MAX_AGGR_NUM 0x4ca #define R88E_TX_RPT_CTRL 0x4ec #define R88E_TX_RPT_MACID_MAX 0x4ed #define R88E_TX_RPT_TIME 0x4f0 /* EDCA Configuration. */ #define R92C_EDCA_VO_PARAM 0x500 #define R92C_EDCA_VI_PARAM 0x504 #define R92C_EDCA_BE_PARAM 0x508 #define R92C_EDCA_BK_PARAM 0x50c #define R92C_BCNTCFG 0x510 #define R92C_PIFS 0x512 #define R92C_RDG_PIFS 0x513 #define R92C_SIFS_CCK 0x514 #define R92C_SIFS_OFDM 0x516 #define R92C_AGGR_BREAK_TIME 0x51a #define R92C_SLOT 0x51b #define R92C_TX_PTCL_CTRL 0x520 #define R92C_TXPAUSE 0x522 #define R92C_DIS_TXREQ_CLR 0x523 #define R92C_RD_CTRL 0x524 #define R92C_TBTT_PROHIBIT 0x540 #define R92C_RD_NAV_NXT 0x544 #define R92C_NAV_PROT_LEN 0x546 #define R92C_BCN_CTRL 0x550 #define R92C_MBID_NUM 0x552 #define R92C_DUAL_TSF_RST 0x553 #define R92C_BCN_INTERVAL 0x554 #define R92C_DRVERLYINT 0x558 #define R92C_BCNDMATIM 0x559 #define R92C_ATIMWND 0x55a #define R92C_USTIME_TSF 0x55c #define R92C_BCN_MAX_ERR 0x55d #define R92C_RXTSF_OFFSET_CCK 0x55e #define R92C_RXTSF_OFFSET_OFDM 0x55f #define R92C_TSFTR 0x560 #define R92C_INIT_TSFTR 0x564 #define R92C_PSTIMER 0x580 #define R92C_TIMER0 0x584 #define R92C_TIMER1 0x588 #define R92C_ACMHWCTRL 0x5c0 #define R92C_ACMRSTCTRL 0x5c1 #define R92C_ACMAVG 0x5c2 #define R92C_VO_ADMTIME 0x5c4 #define R92C_VI_ADMTIME 0x5c6 #define R92C_BE_ADMTIME 0x5c8 #define R92C_EDCA_RANDOM_GEN 0x5cc #define R92C_SCH_TXCMD 0x5d0 #define R88E_SCH_TXCMD 0x5f8 /* WMAC Configuration. */ #define R92C_APSD_CTRL 0x600 #define R92C_BWOPMODE 0x603 #define R92C_RCR 0x608 #define R92C_RX_DRVINFO_SZ 0x60f #define R92C_MACID 0x610 #define R92C_BSSID 0x618 #define R92C_MAR 0x620 #define R92C_MAC_SPEC_SIFS 0x63a #define R92C_R2T_SIFS 0x63c #define R92C_T2T_SIFS 0x63e #define R92C_ACKTO 0x640 #define R92C_CAMCMD 0x670 #define R92C_CAMWRITE 0x674 #define R92C_CAMREAD 0x678 #define R92C_CAMDBG 0x67c #define R92C_SECCFG 0x680 #define R92C_RXFLTMAP0 0x6a0 #define R92C_RXFLTMAP1 0x6a2 #define R92C_RXFLTMAP2 0x6a4 /* Bits for R92C_SYS_ISO_CTRL. */ #define R92C_SYS_ISO_CTRL_MD2PP 0x0001 #define R92C_SYS_ISO_CTRL_UA2USB 0x0002 #define R92C_SYS_ISO_CTRL_UD2CORE 0x0004 #define R92C_SYS_ISO_CTRL_PA2PCIE 0x0008 #define R92C_SYS_ISO_CTRL_PD2CORE 0x0010 #define R92C_SYS_ISO_CTRL_IP2MAC 0x0020 #define R92C_SYS_ISO_CTRL_DIOP 0x0040 #define R92C_SYS_ISO_CTRL_DIOE 0x0080 #define R92C_SYS_ISO_CTRL_EB2CORE 0x0100 #define R92C_SYS_ISO_CTRL_DIOR 0x0200 #define R92C_SYS_ISO_CTRL_PWC_EV25V 0x4000 #define R92C_SYS_ISO_CTRL_PWC_EV12V 0x8000 /* Bits for R92C_SYS_FUNC_EN. */ #define R92C_SYS_FUNC_EN_BBRSTB 0x0001 #define R92C_SYS_FUNC_EN_BB_GLB_RST 0x0002 #define R92C_SYS_FUNC_EN_USBA 0x0004 #define R92C_SYS_FUNC_EN_UPLL 0x0008 #define R92C_SYS_FUNC_EN_USBD 0x0010 #define R92C_SYS_FUNC_EN_DIO_PCIE 0x0020 #define R92C_SYS_FUNC_EN_PCIEA 0x0040 #define R92C_SYS_FUNC_EN_PPLL 0x0080 #define R92C_SYS_FUNC_EN_PCIED 0x0100 #define R92C_SYS_FUNC_EN_DIOE 0x0200 #define R92C_SYS_FUNC_EN_CPUEN 0x0400 #define R92C_SYS_FUNC_EN_DCORE 0x0800 #define R92C_SYS_FUNC_EN_ELDR 0x1000 #define R92C_SYS_FUNC_EN_DIO_RF 0x2000 #define R92C_SYS_FUNC_EN_HWPDN 0x4000 #define R92C_SYS_FUNC_EN_MREGEN 0x8000 /* Bits for R92C_APS_FSMCO. */ #define R92C_APS_FSMCO_PFM_LDALL 0x00000001 #define R92C_APS_FSMCO_PFM_ALDN 0x00000002 #define R92C_APS_FSMCO_PFM_LDKP 0x00000004 #define R92C_APS_FSMCO_PFM_WOWL 0x00000008 #define R92C_APS_FSMCO_PDN_EN 0x00000010 #define R92C_APS_FSMCO_PDN_PL 0x00000020 #define R92C_APS_FSMCO_APFM_ONMAC 0x00000100 #define R92C_APS_FSMCO_APFM_OFF 0x00000200 #define R92C_APS_FSMCO_APFM_RSM 0x00000400 #define R92C_APS_FSMCO_AFSM_HSUS 0x00000800 #define R92C_APS_FSMCO_AFSM_PCIE 0x00001000 #define R92C_APS_FSMCO_APDM_MAC 0x00002000 #define R92C_APS_FSMCO_APDM_HOST 0x00004000 #define R92C_APS_FSMCO_APDM_HPDN 0x00008000 #define R92C_APS_FSMCO_RDY_MACON 0x00010000 #define R92C_APS_FSMCO_SUS_HOST 0x00020000 #define R92C_APS_FSMCO_ROP_ALD 0x00100000 #define R92C_APS_FSMCO_ROP_PWR 0x00200000 #define R92C_APS_FSMCO_ROP_SPS 0x00400000 #define R92C_APS_FSMCO_SOP_MRST 0x02000000 #define R92C_APS_FSMCO_SOP_FUSE 0x04000000 #define R92C_APS_FSMCO_SOP_ABG 0x08000000 #define R92C_APS_FSMCO_SOP_AMB 0x10000000 #define R92C_APS_FSMCO_SOP_RCK 0x20000000 #define R92C_APS_FSMCO_SOP_A8M 0x40000000 #define R92C_APS_FSMCO_XOP_BTCK 0x80000000 /* Bits for R92C_SYS_CLKR. */ #define R92C_SYS_CLKR_ANAD16V_EN 0x00000001 #define R92C_SYS_CLKR_ANA8M 0x00000002 #define R92C_SYS_CLKR_MACSLP 0x00000010 #define R92C_SYS_CLKR_LOADER_EN 0x00000020 #define R92C_SYS_CLKR_80M_SSC_DIS 0x00000080 #define R92C_SYS_CLKR_80M_SSC_EN_HO 0x00000100 #define R92C_SYS_CLKR_PHY_SSC_RSTB 0x00000200 #define R92C_SYS_CLKR_SEC_EN 0x00000400 #define R92C_SYS_CLKR_MAC_EN 0x00000800 #define R92C_SYS_CLKR_SYS_EN 0x00001000 #define R92C_SYS_CLKR_RING_EN 0x00002000 /* Bits for R92C_RF_CTRL. */ #define R92C_RF_CTRL_EN 0x01 #define R92C_RF_CTRL_RSTB 0x02 #define R92C_RF_CTRL_SDMRSTB 0x04 /* Bits for R92C_LDOA15_CTRL. */ #define R92C_LDOA15_CTRL_EN 0x01 #define R92C_LDOA15_CTRL_STBY 0x02 #define R92C_LDOA15_CTRL_OBUF 0x04 #define R92C_LDOA15_CTRL_REG_VOS 0x08 /* Bits for R92C_LDOV12D_CTRL. */ #define R92C_LDOV12D_CTRL_LDV12_EN 0x01 /* Bits for R92C_LPLDO_CTRL. */ #define R92C_LPLDO_CTRL_SLEEP 0x10 /* Bits for R92C_AFE_XTAL_CTRL. */ #define R92C_AFE_XTAL_CTRL_ADDR_M 0x007ff800 #define R92C_AFE_XTAL_CTRL_ADDR_S 11 /* Bits for R92C_AFE_PLL_CTRL. */ #define R92C_AFE_PLL_CTRL_EN 0x0001 #define R92C_AFE_PLL_CTRL_320_EN 0x0002 #define R92C_AFE_PLL_CTRL_FREF_SEL 0x0004 #define R92C_AFE_PLL_CTRL_EDGE_SEL 0x0008 #define R92C_AFE_PLL_CTRL_WDOGB 0x0010 #define R92C_AFE_PLL_CTRL_LPFEN 0x0020 /* Bits for R92C_EFUSE_CTRL. */ #define R92C_EFUSE_CTRL_DATA_M 0x000000ff #define R92C_EFUSE_CTRL_DATA_S 0 #define R92C_EFUSE_CTRL_ADDR_M 0x0003ff00 #define R92C_EFUSE_CTRL_ADDR_S 8 #define R92C_EFUSE_CTRL_VALID 0x80000000 /* Bits for R92C_GPIO_MUXCFG. */ #define R92C_GPIO_MUXCFG_ENBT 0x0020 /* Bits for R92C_LEDCFG0. */ #define R92C_LEDCFG0_DIS 0x08 /* Bits for R92C_MCUFWDL. */ #define R92C_MCUFWDL_EN 0x00000001 #define R92C_MCUFWDL_RDY 0x00000002 #define R92C_MCUFWDL_CHKSUM_RPT 0x00000004 #define R92C_MCUFWDL_MACINI_RDY 0x00000008 #define R92C_MCUFWDL_BBINI_RDY 0x00000010 #define R92C_MCUFWDL_RFINI_RDY 0x00000020 #define R92C_MCUFWDL_WINTINI_RDY 0x00000040 #define R92C_MCUFWDL_RAM_DL_SEL 0x00000080 #define R92C_MCUFWDL_PAGE_M 0x00070000 #define R92C_MCUFWDL_PAGE_S 16 #define R92C_MCUFWDL_CPRST 0x00800000 /* Bits for R88E_HIMR. */ #define R88E_HIMR_CPWM 0x00000100 #define R88E_HIMR_CPWM2 0x00000200 #define R88E_HIMR_TBDER 0x04000000 #define R88E_HIMR_PSTIMEOUT 0x20000000 /* Bits for R88E_HIMRE.*/ #define R88E_HIMRE_RXFOVW 0x00000100 #define R88E_HIMRE_TXFOVW 0x00000200 #define R88E_HIMRE_RXERR 0x00000400 #define R88E_HIMRE_TXERR 0x00000800 /* Bits for R92C_EFUSE_ACCESS. */ #define R92C_EFUSE_ACCESS_OFF 0x00 #define R92C_EFUSE_ACCESS_ON 0x69 /* Bits for R92C_HPON_FSM. */ #define R92C_HPON_FSM_CHIP_BONDING_ID_S 22 #define R92C_HPON_FSM_CHIP_BONDING_ID_M 0x00c00000 #define R92C_HPON_FSM_CHIP_BONDING_ID_92C_1T2R 1 /* Bits for R92C_SYS_CFG. */ #define R92C_SYS_CFG_XCLK_VLD 0x00000001 #define R92C_SYS_CFG_ACLK_VLD 0x00000002 #define R92C_SYS_CFG_UCLK_VLD 0x00000004 #define R92C_SYS_CFG_PCLK_VLD 0x00000008 #define R92C_SYS_CFG_PCIRSTB 0x00000010 #define R92C_SYS_CFG_V15_VLD 0x00000020 #define R92C_SYS_CFG_TRP_B15V_EN 0x00000080 #define R92C_SYS_CFG_SIC_IDLE 0x00000100 #define R92C_SYS_CFG_BD_MAC2 0x00000200 #define R92C_SYS_CFG_BD_MAC1 0x00000400 #define R92C_SYS_CFG_IC_MACPHY_MODE 0x00000800 #define R92C_SYS_CFG_CHIP_VER_RTL_M 0x0000f000 #define R92C_SYS_CFG_CHIP_VER_RTL_S 12 #define R92C_SYS_CFG_BT_FUNC 0x00010000 #define R92C_SYS_CFG_VENDOR_UMC 0x00080000 #define R92C_SYS_CFG_PAD_HWPD_IDN 0x00400000 #define R92C_SYS_CFG_TRP_VAUX_EN 0x00800000 #define R92C_SYS_CFG_TRP_BT_EN 0x01000000 #define R92C_SYS_CFG_BD_PKG_SEL 0x02000000 #define R92C_SYS_CFG_BD_HCI_SEL 0x04000000 #define R92C_SYS_CFG_TYPE_92C 0x08000000 /* Bits for R92C_CR. */ #define R92C_CR_HCI_TXDMA_EN 0x0001 #define R92C_CR_HCI_RXDMA_EN 0x0002 #define R92C_CR_TXDMA_EN 0x0004 #define R92C_CR_RXDMA_EN 0x0008 #define R92C_CR_PROTOCOL_EN 0x0010 #define R92C_CR_SCHEDULE_EN 0x0020 #define R92C_CR_MACTXEN 0x0040 #define R92C_CR_MACRXEN 0x0080 #define R92C_CR_ENSEC 0x0200 #define R92C_CR_CALTMR_EN 0x0400 /* Bits for R92C_MSR. */ #define R92C_MSR_NOLINK 0x00 #define R92C_MSR_ADHOC 0x01 #define R92C_MSR_INFRA 0x02 #define R92C_MSR_AP 0x03 #define R92C_MSR_MASK (R92C_MSR_AP) /* Bits for R92C_PBP. */ #define R92C_PBP_PSRX_M 0x0f #define R92C_PBP_PSRX_S 0 #define R92C_PBP_PSTX_M 0xf0 #define R92C_PBP_PSTX_S 4 #define R92C_PBP_64 0 #define R92C_PBP_128 1 #define R92C_PBP_256 2 #define R92C_PBP_512 3 #define R92C_PBP_1024 4 /* Bits for R92C_TRXDMA_CTRL. */ #define R92C_TRXDMA_CTRL_RXDMA_AGG_EN 0x0004 #define R92C_TRXDMA_CTRL_TXDMA_VOQ_MAP_M 0x0030 #define R92C_TRXDMA_CTRL_TXDMA_VOQ_MAP_S 4 #define R92C_TRXDMA_CTRL_TXDMA_VIQ_MAP_M 0x00c0 #define R92C_TRXDMA_CTRL_TXDMA_VIQ_MAP_S 6 #define R92C_TRXDMA_CTRL_TXDMA_BEQ_MAP_M 0x0300 #define R92C_TRXDMA_CTRL_TXDMA_BEQ_MAP_S 8 #define R92C_TRXDMA_CTRL_TXDMA_BKQ_MAP_M 0x0c00 #define R92C_TRXDMA_CTRL_TXDMA_BKQ_MAP_S 10 #define R92C_TRXDMA_CTRL_TXDMA_MGQ_MAP_M 0x3000 #define R92C_TRXDMA_CTRL_TXDMA_MGQ_MAP_S 12 #define R92C_TRXDMA_CTRL_TXDMA_HIQ_MAP_M 0xc000 #define R92C_TRXDMA_CTRL_TXDMA_HIQ_MAP_S 14 #define R92C_TRXDMA_CTRL_QUEUE_LOW 1 #define R92C_TRXDMA_CTRL_QUEUE_NORMAL 2 #define R92C_TRXDMA_CTRL_QUEUE_HIGH 3 #define R92C_TRXDMA_CTRL_QMAP_M 0xfff0 /* Shortcuts. */ #define R92C_TRXDMA_CTRL_QMAP_3EP 0xf5b0 #define R92C_TRXDMA_CTRL_QMAP_HQ_LQ 0xf5f0 #define R92C_TRXDMA_CTRL_QMAP_HQ_NQ 0xfaf0 #define R92C_TRXDMA_CTRL_QMAP_LQ 0x5550 #define R92C_TRXDMA_CTRL_QMAP_NQ 0xaaa0 #define R92C_TRXDMA_CTRL_QMAP_HQ 0xfff0 /* Bits for R92C_LLT_INIT. */ #define R92C_LLT_INIT_DATA_M 0x000000ff #define R92C_LLT_INIT_DATA_S 0 #define R92C_LLT_INIT_ADDR_M 0x0000ff00 #define R92C_LLT_INIT_ADDR_S 8 #define R92C_LLT_INIT_OP_M 0xc0000000 #define R92C_LLT_INIT_OP_S 30 #define R92C_LLT_INIT_OP_NO_ACTIVE 0 #define R92C_LLT_INIT_OP_WRITE 1 /* Bits for R92C_RQPN. */ #define R92C_RQPN_HPQ_M 0x000000ff #define R92C_RQPN_HPQ_S 0 #define R92C_RQPN_LPQ_M 0x0000ff00 #define R92C_RQPN_LPQ_S 8 #define R92C_RQPN_PUBQ_M 0x00ff0000 #define R92C_RQPN_PUBQ_S 16 #define R92C_RQPN_LD 0x80000000 /* Bits for R92C_TDECTRL. */ #define R92C_TDECTRL_BLK_DESC_NUM_M 0x000000f0 #define R92C_TDECTRL_BLK_DESC_NUM_S 4 #define R92C_TDECTRL_BCN_VALID 0x00010000 /* Bits for R92C_FWHW_TXQ_CTRL. */ #define R92C_FWHW_TXQ_CTRL_AMPDU_RTY_NEW 0x80 /* Bits for R92C_SPEC_SIFS. */ #define R92C_SPEC_SIFS_CCK_M 0x00ff #define R92C_SPEC_SIFS_CCK_S 0 #define R92C_SPEC_SIFS_OFDM_M 0xff00 #define R92C_SPEC_SIFS_OFDM_S 8 /* Bits for R92C_RL. */ #define R92C_RL_LRL_M 0x003f #define R92C_RL_LRL_S 0 #define R92C_RL_SRL_M 0x3f00 #define R92C_RL_SRL_S 8 /* Bits for R92C_RRSR. */ #define R92C_RRSR_RATE_BITMAP_M 0x000fffff #define R92C_RRSR_RATE_BITMAP_S 0 #define R92C_RRSR_RATE_CCK_ONLY_1M 0xffff1 #define R92C_RRSR_RSC_LOWSUBCHNL 0x00200000 #define R92C_RRSR_RSC_UPSUBCHNL 0x00400000 #define R92C_RRSR_SHORT 0x00800000 /* Bits for R88E_TX_RPT_CTRL. */ #define R88E_TX_RPT1_ENA 0x01 #define R88E_TX_RPT2_ENA 0x02 /* Bits for R92C_EDCA_XX_PARAM. */ #define R92C_EDCA_PARAM_AIFS_M 0x000000ff #define R92C_EDCA_PARAM_AIFS_S 0 #define R92C_EDCA_PARAM_ECWMIN_M 0x00000f00 #define R92C_EDCA_PARAM_ECWMIN_S 8 #define R92C_EDCA_PARAM_ECWMAX_M 0x0000f000 #define R92C_EDCA_PARAM_ECWMAX_S 12 #define R92C_EDCA_PARAM_TXOP_M 0xffff0000 #define R92C_EDCA_PARAM_TXOP_S 16 /* Bits for R92C_HWSEQ_CTRL / R92C_TXPAUSE. */ #define R92C_TX_QUEUE_VO 0x01 #define R92C_TX_QUEUE_VI 0x02 #define R92C_TX_QUEUE_BE 0x04 #define R92C_TX_QUEUE_BK 0x08 #define R92C_TX_QUEUE_MGT 0x10 #define R92C_TX_QUEUE_HIGH 0x20 #define R92C_TX_QUEUE_BCN 0x40 /* Shortcuts. */ #define R92C_TX_QUEUE_AC \ (R92C_TX_QUEUE_VO | R92C_TX_QUEUE_VI | \ R92C_TX_QUEUE_BE | R92C_TX_QUEUE_BK) #define R92C_TX_QUEUE_ALL \ (R92C_TX_QUEUE_AC | R92C_TX_QUEUE_MGT | \ R92C_TX_QUEUE_HIGH | R92C_TX_QUEUE_BCN | 0x80) /* XXX */ /* Bits for R92C_BCN_CTRL. */ #define R92C_BCN_CTRL_EN_MBSSID 0x02 #define R92C_BCN_CTRL_TXBCN_RPT 0x04 #define R92C_BCN_CTRL_EN_BCN 0x08 #define R92C_BCN_CTRL_DIS_TSF_UDT0 0x10 /* Bits for R92C_MBID_NUM. */ #define R92C_MBID_TXBCN_RPT0 0x08 #define R92C_MBID_TXBCN_RPT1 0x10 /* Bits for R92C_DUAL_TSF_RST. */ #define R92C_DUAL_TSF_RST0 0x01 #define R92C_DUAL_TSF_RST1 0x02 /* Bits for R92C_ACMHWCTRL. */ #define R92C_ACMHWCTRL_EN 0x01 #define R92C_ACMHWCTRL_BE 0x02 #define R92C_ACMHWCTRL_VI 0x04 #define R92C_ACMHWCTRL_VO 0x08 #define R92C_ACMHWCTRL_ACM_MASK 0x0f /* Bits for R92C_APSD_CTRL. */ #define R92C_APSD_CTRL_OFF 0x40 #define R92C_APSD_CTRL_OFF_STATUS 0x80 /* Bits for R92C_BWOPMODE. */ #define R92C_BWOPMODE_11J 0x01 #define R92C_BWOPMODE_5G 0x02 #define R92C_BWOPMODE_20MHZ 0x04 /* Bits for R92C_RCR. */ #define R92C_RCR_AAP 0x00000001 #define R92C_RCR_APM 0x00000002 #define R92C_RCR_AM 0x00000004 #define R92C_RCR_AB 0x00000008 #define R92C_RCR_ADD3 0x00000010 #define R92C_RCR_APWRMGT 0x00000020 #define R92C_RCR_CBSSID_DATA 0x00000040 #define R92C_RCR_CBSSID_BCN 0x00000080 #define R92C_RCR_ACRC32 0x00000100 #define R92C_RCR_AICV 0x00000200 #define R92C_RCR_ADF 0x00000800 #define R92C_RCR_ACF 0x00001000 #define R92C_RCR_AMF 0x00002000 #define R92C_RCR_HTC_LOC_CTRL 0x00004000 #define R92C_RCR_MFBEN 0x00400000 #define R92C_RCR_LSIGEN 0x00800000 #define R92C_RCR_ENMBID 0x01000000 #define R92C_RCR_APP_BA_SSN 0x08000000 #define R92C_RCR_APP_PHYSTS 0x10000000 #define R92C_RCR_APP_ICV 0x20000000 #define R92C_RCR_APP_MIC 0x40000000 #define R92C_RCR_APPFCS 0x80000000 /* Bits for R92C_CAMCMD. */ #define R92C_CAMCMD_ADDR_M 0x0000ffff #define R92C_CAMCMD_ADDR_S 0 #define R92C_CAMCMD_WRITE 0x00010000 #define R92C_CAMCMD_CLR 0x40000000 #define R92C_CAMCMD_POLLING 0x80000000 /* Bits for R92C_SECCFG. */ #define R92C_SECCFG_TXUCKEY_DEF 0x0001 #define R92C_SECCFG_RXUCKEY_DEF 0x0002 #define R92C_SECCFG_TXENC_ENA 0x0004 #define R92C_SECCFG_RXDEC_ENA 0x0008 #define R92C_SECCFG_CMP_A2 0x0010 #define R92C_SECCFG_TXBCKEY_DEF 0x0040 #define R92C_SECCFG_RXBCKEY_DEF 0x0080 #define R88E_SECCFG_CHK_KEYID 0x0100 /* Bits for R92C_RXFLTMAP*. */ #define R92C_RXFLTMAP_SUBTYPE(subtype) \ (1 << ((subtype) >> IEEE80211_FC0_SUBTYPE_SHIFT)) /* * Baseband registers. */ #define R92C_FPGA0_RFMOD 0x800 #define R92C_FPGA0_TXINFO 0x804 #define R92C_HSSI_PARAM1(chain) (0x820 + (chain) * 8) #define R92C_HSSI_PARAM2(chain) (0x824 + (chain) * 8) #define R92C_TXAGC_RATE18_06(i) (((i) == 0) ? 0xe00 : 0x830) #define R92C_TXAGC_RATE54_24(i) (((i) == 0) ? 0xe04 : 0x834) #define R92C_TXAGC_A_CCK1_MCS32 0xe08 #define R92C_TXAGC_B_CCK1_55_MCS32 0x838 #define R92C_TXAGC_B_CCK11_A_CCK2_11 0x86c #define R92C_TXAGC_MCS03_MCS00(i) (((i) == 0) ? 0xe10 : 0x83c) #define R92C_TXAGC_MCS07_MCS04(i) (((i) == 0) ? 0xe14 : 0x848) #define R92C_TXAGC_MCS11_MCS08(i) (((i) == 0) ? 0xe18 : 0x84c) #define R92C_TXAGC_MCS15_MCS12(i) (((i) == 0) ? 0xe1c : 0x868) #define R92C_LSSI_PARAM(chain) (0x840 + (chain) * 4) #define R92C_FPGA0_RFIFACEOE(chain) (0x860 + (chain) * 4) #define R92C_FPGA0_RFIFACESW(idx) (0x870 + (idx) * 4) #define R92C_FPGA0_RFPARAM(idx) (0x878 + (idx) * 4) #define R92C_FPGA0_ANAPARAM2 0x884 #define R92C_LSSI_READBACK(chain) (0x8a0 + (chain) * 4) #define R92C_HSPI_READBACK(chain) (0x8b8 + (chain) * 4) #define R92C_FPGA1_RFMOD 0x900 #define R92C_FPGA1_TXINFO 0x90c #define R92C_CCK0_SYSTEM 0xa00 #define R92C_CCK0_AFESETTING 0xa04 #define R92C_OFDM0_TRXPATHENA 0xc04 #define R92C_OFDM0_TRMUXPAR 0xc08 #define R92C_OFDM0_AGCCORE1(chain) (0xc50 + (chain) * 8) #define R92C_OFDM0_AGCPARAM1 0xc70 #define R92C_OFDM0_AGCRSSITABLE 0xc78 #define R92C_OFDM1_LSTF 0xd00 /* Bits for R92C_FPGA[01]_RFMOD. */ #define R92C_RFMOD_40MHZ 0x00000001 #define R92C_RFMOD_JAPAN 0x00000002 #define R92C_RFMOD_CCK_TXSC 0x00000030 #define R92C_RFMOD_CCK_EN 0x01000000 #define R92C_RFMOD_OFDM_EN 0x02000000 /* Bits for R92C_HSSI_PARAM1(i). */ #define R92C_HSSI_PARAM1_PI 0x00000100 /* Bits for R92C_HSSI_PARAM2(i). */ #define R92C_HSSI_PARAM2_CCK_HIPWR 0x00000200 #define R92C_HSSI_PARAM2_ADDR_LENGTH 0x00000400 #define R92C_HSSI_PARAM2_DATA_LENGTH 0x00000800 #define R92C_HSSI_PARAM2_READ_ADDR_M 0x7f800000 #define R92C_HSSI_PARAM2_READ_ADDR_S 23 #define R92C_HSSI_PARAM2_READ_EDGE 0x80000000 /* Bits for R92C_TXAGC_A_CCK1_MCS32. */ #define R92C_TXAGC_A_CCK1_M 0x0000ff00 #define R92C_TXAGC_A_CCK1_S 8 /* Bits for R92C_TXAGC_B_CCK11_A_CCK2_11. */ #define R92C_TXAGC_B_CCK11_M 0x000000ff #define R92C_TXAGC_B_CCK11_S 0 #define R92C_TXAGC_A_CCK2_M 0x0000ff00 #define R92C_TXAGC_A_CCK2_S 8 #define R92C_TXAGC_A_CCK55_M 0x00ff0000 #define R92C_TXAGC_A_CCK55_S 16 #define R92C_TXAGC_A_CCK11_M 0xff000000 #define R92C_TXAGC_A_CCK11_S 24 /* Bits for R92C_TXAGC_B_CCK1_55_MCS32. */ #define R92C_TXAGC_B_CCK1_M 0x0000ff00 #define R92C_TXAGC_B_CCK1_S 8 #define R92C_TXAGC_B_CCK2_M 0x00ff0000 #define R92C_TXAGC_B_CCK2_S 16 #define R92C_TXAGC_B_CCK55_M 0xff000000 #define R92C_TXAGC_B_CCK55_S 24 /* Bits for R92C_TXAGC_RATE18_06(x). */ #define R92C_TXAGC_RATE06_M 0x000000ff #define R92C_TXAGC_RATE06_S 0 #define R92C_TXAGC_RATE09_M 0x0000ff00 #define R92C_TXAGC_RATE09_S 8 #define R92C_TXAGC_RATE12_M 0x00ff0000 #define R92C_TXAGC_RATE12_S 16 #define R92C_TXAGC_RATE18_M 0xff000000 #define R92C_TXAGC_RATE18_S 24 /* Bits for R92C_TXAGC_RATE54_24(x). */ #define R92C_TXAGC_RATE24_M 0x000000ff #define R92C_TXAGC_RATE24_S 0 #define R92C_TXAGC_RATE36_M 0x0000ff00 #define R92C_TXAGC_RATE36_S 8 #define R92C_TXAGC_RATE48_M 0x00ff0000 #define R92C_TXAGC_RATE48_S 16 #define R92C_TXAGC_RATE54_M 0xff000000 #define R92C_TXAGC_RATE54_S 24 /* Bits for R92C_TXAGC_MCS03_MCS00(x). */ #define R92C_TXAGC_MCS00_M 0x000000ff #define R92C_TXAGC_MCS00_S 0 #define R92C_TXAGC_MCS01_M 0x0000ff00 #define R92C_TXAGC_MCS01_S 8 #define R92C_TXAGC_MCS02_M 0x00ff0000 #define R92C_TXAGC_MCS02_S 16 #define R92C_TXAGC_MCS03_M 0xff000000 #define R92C_TXAGC_MCS03_S 24 /* Bits for R92C_TXAGC_MCS07_MCS04(x). */ #define R92C_TXAGC_MCS04_M 0x000000ff #define R92C_TXAGC_MCS04_S 0 #define R92C_TXAGC_MCS05_M 0x0000ff00 #define R92C_TXAGC_MCS05_S 8 #define R92C_TXAGC_MCS06_M 0x00ff0000 #define R92C_TXAGC_MCS06_S 16 #define R92C_TXAGC_MCS07_M 0xff000000 #define R92C_TXAGC_MCS07_S 24 /* Bits for R92C_TXAGC_MCS11_MCS08(x). */ #define R92C_TXAGC_MCS08_M 0x000000ff #define R92C_TXAGC_MCS08_S 0 #define R92C_TXAGC_MCS09_M 0x0000ff00 #define R92C_TXAGC_MCS09_S 8 #define R92C_TXAGC_MCS10_M 0x00ff0000 #define R92C_TXAGC_MCS10_S 16 #define R92C_TXAGC_MCS11_M 0xff000000 #define R92C_TXAGC_MCS11_S 24 /* Bits for R92C_TXAGC_MCS15_MCS12(x). */ #define R92C_TXAGC_MCS12_M 0x000000ff #define R92C_TXAGC_MCS12_S 0 #define R92C_TXAGC_MCS13_M 0x0000ff00 #define R92C_TXAGC_MCS13_S 8 #define R92C_TXAGC_MCS14_M 0x00ff0000 #define R92C_TXAGC_MCS14_S 16 #define R92C_TXAGC_MCS15_M 0xff000000 #define R92C_TXAGC_MCS15_S 24 /* Bits for R92C_LSSI_PARAM(i). */ #define R92C_LSSI_PARAM_DATA_M 0x000fffff #define R92C_LSSI_PARAM_DATA_S 0 #define R92C_LSSI_PARAM_ADDR_M 0x03f00000 #define R92C_LSSI_PARAM_ADDR_S 20 #define R88E_LSSI_PARAM_ADDR_M 0x0ff00000 #define R88E_LSSI_PARAM_ADDR_S 20 /* Bits for R92C_FPGA0_ANAPARAM2. */ #define R92C_FPGA0_ANAPARAM2_CBW20 0x00000400 /* Bits for R92C_LSSI_READBACK(i). */ #define R92C_LSSI_READBACK_DATA_M 0x000fffff #define R92C_LSSI_READBACK_DATA_S 0 /* Bits for R92C_OFDM0_AGCCORE1(i). */ #define R92C_OFDM0_AGCCORE1_GAIN_M 0x0000007f #define R92C_OFDM0_AGCCORE1_GAIN_S 0 /* * USB registers. */ #define R92C_USB_SUSPEND 0xfe10 #define R92C_USB_INFO 0xfe17 #define R92C_USB_SPECIAL_OPTION 0xfe55 #define R92C_USB_HCPWM 0xfe57 #define R92C_USB_HRPWM 0xfe58 #define R92C_USB_DMA_AGG_TO 0xfe5b #define R92C_USB_AGG_TO 0xfe5c #define R92C_USB_AGG_TH 0xfe5d #define R92C_USB_VID 0xfe60 #define R92C_USB_PID 0xfe62 #define R92C_USB_OPTIONAL 0xfe64 #define R92C_USB_EP 0xfe65 #define R92C_USB_PHY 0xfe68 #define R92C_USB_MAC_ADDR 0xfe70 #define R92C_USB_STRING 0xfe80 /* Bits for R92C_USB_SPECIAL_OPTION. */ #define R92C_USB_SPECIAL_OPTION_AGG_EN 0x08 #define R92C_USB_SPECIAL_OPTION_INT_BULK_SEL 0x10 /* Bits for R92C_USB_EP. */ #define R92C_USB_EP_HQ_M 0x000f #define R92C_USB_EP_HQ_S 0 #define R92C_USB_EP_NQ_M 0x00f0 #define R92C_USB_EP_NQ_S 4 #define R92C_USB_EP_LQ_M 0x0f00 #define R92C_USB_EP_LQ_S 8 /* * Firmware base address. */ #define R92C_FW_START_ADDR 0x1000 #define R92C_FW_PAGE_SIZE 4096 /* * RF (6052) registers. */ #define R92C_RF_AC 0x00 #define R92C_RF_IQADJ_G(i) (0x01 + (i)) #define R92C_RF_POW_TRSW 0x05 #define R92C_RF_GAIN_RX 0x06 #define R92C_RF_GAIN_TX 0x07 #define R92C_RF_TXM_IDAC 0x08 #define R92C_RF_BS_IQGEN 0x0f #define R92C_RF_MODE1 0x10 #define R92C_RF_MODE2 0x11 #define R92C_RF_RX_AGC_HP 0x12 #define R92C_RF_TX_AGC 0x13 #define R92C_RF_BIAS 0x14 #define R92C_RF_IPA 0x15 #define R92C_RF_POW_ABILITY 0x17 #define R92C_RF_CHNLBW 0x18 #define R92C_RF_RX_G1 0x1a #define R92C_RF_RX_G2 0x1b #define R92C_RF_RX_BB2 0x1c #define R92C_RF_RX_BB1 0x1d #define R92C_RF_RCK1 0x1e #define R92C_RF_RCK2 0x1f #define R92C_RF_TX_G(i) (0x20 + (i)) #define R92C_RF_TX_BB1 0x23 #define R92C_RF_T_METER 0x24 #define R92C_RF_SYN_G(i) (0x25 + (i)) #define R92C_RF_RCK_OS 0x30 #define R92C_RF_TXPA_G(i) (0x31 + (i)) #define R88E_RF_T_METER 0x42 /* Bits for R92C_RF_AC. */ #define R92C_RF_AC_MODE_M 0x70000 #define R92C_RF_AC_MODE_S 16 #define R92C_RF_AC_MODE_STANDBY 1 /* Bits for R92C_RF_CHNLBW. */ #define R92C_RF_CHNLBW_CHNL_M 0x003ff #define R92C_RF_CHNLBW_CHNL_S 0 #define R92C_RF_CHNLBW_BW20 0x00400 #define R88E_RF_CHNLBW_BW20 0x00c00 #define R92C_RF_CHNLBW_LCSTART 0x08000 /* Bits for R92C_RF_T_METER. */ #define R92C_RF_T_METER_START 0x60 #define R92C_RF_T_METER_VAL_M 0x1f #define R92C_RF_T_METER_VAL_S 0 /* Bits for R88E_RF_T_METER. */ #define R88E_RF_T_METER_VAL_M 0x0fc00 #define R88E_RF_T_METER_VAL_S 10 #define R88E_RF_T_METER_START 0x30000 /* * CAM entries. */ #define R92C_CAM_ENTRY_COUNT 32 #define R92C_CAM_CTL0(entry) ((entry) * 8 + 0) #define R92C_CAM_CTL1(entry) ((entry) * 8 + 1) #define R92C_CAM_KEY(entry, i) ((entry) * 8 + 2 + (i)) /* Bits for R92C_CAM_CTL0(i). */ #define R92C_CAM_KEYID_M 0x00000003 #define R92C_CAM_KEYID_S 0 #define R92C_CAM_ALGO_M 0x0000001c #define R92C_CAM_ALGO_S 2 #define R92C_CAM_ALGO_NONE 0 #define R92C_CAM_ALGO_WEP40 1 #define R92C_CAM_ALGO_TKIP 2 #define R92C_CAM_ALGO_AES 4 #define R92C_CAM_ALGO_WEP104 5 #define R92C_CAM_VALID 0x00008000 #define R92C_CAM_MACLO_M 0xffff0000 #define R92C_CAM_MACLO_S 16 /* Rate adaptation modes. */ #define R92C_RAID_11GN 1 #define R92C_RAID_11N 3 #define R92C_RAID_11BG 4 #define R92C_RAID_11G 5 /* "pure" 11g */ #define R92C_RAID_11B 6 /* * Macros to access subfields in registers. */ /* Mask and Shift (getter). */ #define MS(val, field) \ (((val) & field##_M) >> field##_S) /* Shift and Mask (setter). */ #define SM(field, val) \ (((val) << field##_S) & field##_M) /* Rewrite. */ #define RW(var, field, val) \ (((var) & ~field##_M) | SM(field, val)) /* * Firmware image header. */ struct r92c_fw_hdr { /* QWORD0 */ uint16_t signature; uint8_t category; uint8_t function; uint16_t version; uint16_t subversion; /* QWORD1 */ uint8_t month; uint8_t date; uint8_t hour; uint8_t minute; uint16_t ramcodesize; uint16_t reserved2; /* QWORD2 */ uint32_t svnidx; uint32_t reserved3; /* QWORD3 */ uint32_t reserved4; uint32_t reserved5; } __packed; /* * Host to firmware commands. */ struct r92c_fw_cmd { uint8_t id; #define R92C_CMD_AP_OFFLOAD 0 #define R92C_CMD_SET_PWRMODE 1 #define R92C_CMD_JOINBSS_RPT 2 #define R92C_CMD_RSVD_PAGE 3 #define R92C_CMD_RSSI 4 #define R92C_CMD_RSSI_SETTING 5 #define R92C_CMD_MACID_CONFIG 6 #define R92C_CMD_MACID_PS_MODE 7 #define R92C_CMD_P2P_PS_OFFLOAD 8 #define R92C_CMD_SELECTIVE_SUSPEND 9 #define R92C_CMD_FLAG_EXT 0x80 uint8_t msg[5]; } __packed; /* Structure for R92C_CMD_RSSI_SETTING. */ struct r92c_fw_cmd_rssi { uint8_t macid; uint8_t reserved; uint8_t pwdb; } __packed; /* Structure for R92C_CMD_MACID_CONFIG. */ struct r92c_fw_cmd_macid_cfg { uint32_t mask; uint8_t macid; #define URTWN_MACID_BSS 0 #define URTWN_MACID_BC 4 /* Broadcast. */ #define R92C_MACID_MAX 31 #define R88E_MACID_MAX 63 #define URTWN_MACID_MAX(sc) (((sc)->chip & URTWN_CHIP_88E) ? \ R88E_MACID_MAX : R92C_MACID_MAX) #define URTWN_MACID_UNDEFINED (uint8_t)-1 #define URTWN_MACID_VALID 0x80 } __packed; /* * RTL8192CU ROM image. */ struct r92c_rom { uint16_t id; /* 0x8192 */ uint8_t reserved1[5]; uint8_t dbg_sel; uint16_t reserved2; uint16_t vid; uint16_t pid; uint8_t usb_opt; uint8_t ep_setting; uint16_t reserved3; uint8_t usb_phy; uint8_t reserved4[3]; uint8_t macaddr[IEEE80211_ADDR_LEN]; uint8_t string[61]; /* "Realtek" */ uint8_t subcustomer_id; uint8_t cck_tx_pwr[R92C_MAX_CHAINS][3]; uint8_t ht40_1s_tx_pwr[R92C_MAX_CHAINS][3]; uint8_t ht40_2s_tx_pwr_diff[3]; uint8_t ht20_tx_pwr_diff[3]; uint8_t ofdm_tx_pwr_diff[3]; uint8_t ht40_max_pwr[3]; uint8_t ht20_max_pwr[3]; uint8_t xtal_calib; uint8_t tssi[R92C_MAX_CHAINS]; uint8_t thermal_meter; uint8_t rf_opt1; #define R92C_ROM_RF1_REGULATORY_M 0x07 #define R92C_ROM_RF1_REGULATORY_S 0 #define R92C_ROM_RF1_BOARD_TYPE_M 0xe0 #define R92C_ROM_RF1_BOARD_TYPE_S 5 #define R92C_BOARD_TYPE_DONGLE 0 #define R92C_BOARD_TYPE_HIGHPA 1 #define R92C_BOARD_TYPE_MINICARD 2 #define R92C_BOARD_TYPE_SOLO 3 #define R92C_BOARD_TYPE_COMBO 4 uint8_t rf_opt2; uint8_t rf_opt3; uint8_t rf_opt4; uint8_t channel_plan; +#define R92C_CHANNEL_PLAN_BY_HW 0x80 + uint8_t version; uint8_t customer_id; } __packed; /* * RTL8188EU ROM image. */ struct r88e_rom { uint8_t reserved1[16]; uint8_t cck_tx_pwr[6]; uint8_t ht40_tx_pwr[5]; uint8_t tx_pwr_diff; uint8_t reserved2[156]; uint8_t channel_plan; uint8_t crystalcap; uint8_t reserved3[7]; uint8_t rf_board_opt; uint8_t rf_feature_opt; uint8_t rf_bt_opt; uint8_t version; uint8_t customer_id; uint8_t reserved4[3]; uint8_t rf_ant_opt; uint8_t reserved5[6]; uint16_t vid; uint16_t pid; uint8_t usb_opt; uint8_t reserved6[2]; uint8_t macaddr[IEEE80211_ADDR_LEN]; uint8_t reserved7[2]; uint8_t string[33]; /* "realtek 802.11n NIC" */ uint8_t reserved8[256]; } __packed; #define URTWN_EFUSE_MAX_LEN 512 /* Rx MAC descriptor. */ struct r92c_rx_stat { uint32_t rxdw0; #define R92C_RXDW0_PKTLEN_M 0x00003fff #define R92C_RXDW0_PKTLEN_S 0 #define R92C_RXDW0_CRCERR 0x00004000 #define R92C_RXDW0_ICVERR 0x00008000 #define R92C_RXDW0_INFOSZ_M 0x000f0000 #define R92C_RXDW0_INFOSZ_S 16 #define R92C_RXDW0_CIPHER_M 0x00700000 #define R92C_RXDW0_CIPHER_S 20 #define R92C_RXDW0_QOS 0x00800000 #define R92C_RXDW0_SHIFT_M 0x03000000 #define R92C_RXDW0_SHIFT_S 24 #define R92C_RXDW0_PHYST 0x04000000 #define R92C_RXDW0_DECRYPTED 0x08000000 uint32_t rxdw1; uint32_t rxdw2; #define R92C_RXDW2_PKTCNT_M 0x00ff0000 #define R92C_RXDW2_PKTCNT_S 16 uint32_t rxdw3; #define R92C_RXDW3_RATE_M 0x0000003f #define R92C_RXDW3_RATE_S 0 #define R92C_RXDW3_HT 0x00000040 #define R92C_RXDW3_HTC 0x00000400 #define R88E_RXDW3_RPT_M 0x0000c000 #define R88E_RXDW3_RPT_S 14 #define R88E_RXDW3_RPT_RX 0 #define R88E_RXDW3_RPT_TX1 1 #define R88E_RXDW3_RPT_TX2 2 uint32_t rxdw4; uint32_t rxdw5; } __packed __attribute__((aligned(4))); /* Rx PHY descriptor. */ struct r92c_rx_phystat { uint32_t phydw0; uint32_t phydw1; uint32_t phydw2; uint32_t phydw3; uint32_t phydw4; uint32_t phydw5; uint32_t phydw6; uint32_t phydw7; } __packed __attribute__((aligned(4))); /* Rx PHY CCK descriptor. */ struct r92c_rx_cck { uint8_t adc_pwdb[4]; uint8_t sq_rpt; uint8_t agc_rpt; } __packed; struct r88e_rx_cck { uint8_t path_agc[2]; uint8_t chan; uint8_t reserved1; uint8_t sig_qual; uint8_t agc_rpt; uint8_t rpt_b; uint8_t reserved2; uint8_t noise_power; uint8_t path_cfotail[2]; uint8_t pcts_mask[2]; uint8_t stream_rxevm[2]; uint8_t path_rxsnr[2]; uint8_t noise_power_db_lsb; uint8_t reserved3[3]; uint8_t stream_csi[2]; uint8_t stream_target_csi[2]; uint8_t sig_evm; } __packed; /* Tx MAC descriptor. */ struct r92c_tx_desc { uint32_t txdw0; #define R92C_TXDW0_PKTLEN_M 0x0000ffff #define R92C_TXDW0_PKTLEN_S 0 #define R92C_TXDW0_OFFSET_M 0x00ff0000 #define R92C_TXDW0_OFFSET_S 16 #define R92C_TXDW0_BMCAST 0x01000000 #define R92C_TXDW0_LSG 0x04000000 #define R92C_TXDW0_FSG 0x08000000 #define R92C_TXDW0_OWN 0x80000000 uint32_t txdw1; #define R92C_TXDW1_MACID_M 0x0000001f #define R92C_TXDW1_MACID_S 0 #define R88E_TXDW1_MACID_M 0x0000003f #define R88E_TXDW1_MACID_S 0 #define R92C_TXDW1_AGGEN 0x00000020 #define R92C_TXDW1_AGGBK 0x00000040 #define R92C_TXDW1_QSEL_M 0x00001f00 #define R92C_TXDW1_QSEL_S 8 #define R92C_TXDW1_QSEL_BE 0x00 /* or 0x03 */ #define R92C_TXDW1_QSEL_BK 0x01 /* or 0x02 */ #define R92C_TXDW1_QSEL_VI 0x04 /* or 0x05 */ #define R92C_TXDW1_QSEL_VO 0x06 /* or 0x07 */ #define URTWN_MAX_TID 8 #define R92C_TXDW1_QSEL_BEACON 0x10 #define R92C_TXDW1_QSEL_MGNT 0x12 #define R92C_TXDW1_RAID_M 0x000f0000 #define R92C_TXDW1_RAID_S 16 #define R92C_TXDW1_CIPHER_M 0x00c00000 #define R92C_TXDW1_CIPHER_S 22 #define R92C_TXDW1_CIPHER_NONE 0 #define R92C_TXDW1_CIPHER_RC4 1 #define R92C_TXDW1_CIPHER_AES 3 #define R92C_TXDW1_PKTOFF_M 0x7c000000 #define R92C_TXDW1_PKTOFF_S 26 uint32_t txdw2; #define R88E_TXDW2_AGGBK 0x00010000 #define R88E_TXDW2_CCX_RPT 0x00080000 uint16_t txdw3; uint16_t txdseq; #define R88E_TXDSEQ_HWSEQ_EN 0x8000 uint32_t txdw4; #define R92C_TXDW4_RTSRATE_M 0x0000003f #define R92C_TXDW4_RTSRATE_S 0 #define R92C_TXDW4_HWSEQ_QOS 0x00000040 #define R92C_TXDW4_HWSEQ_EN 0x00000080 #define R92C_TXDW4_DRVRATE 0x00000100 #define R92C_TXDW4_CTS2SELF 0x00000800 #define R92C_TXDW4_RTSEN 0x00001000 #define R92C_TXDW4_HWRTSEN 0x00002000 #define R92C_TXDW4_SCO_M 0x003f0000 #define R92C_TXDW4_SCO_S 20 #define R92C_TXDW4_SCO_SCA 1 #define R92C_TXDW4_SCO_SCB 2 #define R92C_TXDW4_40MHZ 0x02000000 uint32_t txdw5; #define R92C_TXDW5_DATARATE_M 0x0000003f #define R92C_TXDW5_DATARATE_S 0 #define R92C_TXDW5_SGI 0x00000040 #define R92C_TXDW5_RTY_LMT_ENA 0x00020000 #define R92C_TXDW5_RTY_LMT_M 0x00fc0000 #define R92C_TXDW5_RTY_LMT_S 18 #define R92C_TXDW5_AGGNUM_M 0xff000000 #define R92C_TXDW5_AGGNUM_S 24 uint32_t txdw6; uint16_t txdsum; uint16_t pad; } __packed __attribute__((aligned(4))); struct r88e_tx_rpt_ccx { uint8_t rptb0; uint8_t rptb1; #define R88E_RPTB1_MACID_M 0x3f #define R88E_RPTB1_MACID_S 0 #define R88E_RPTB1_PKT_OK 0x40 #define R88E_RPTB1_BMC 0x80 uint8_t rptb2; #define R88E_RPTB2_RETRY_CNT_M 0x3f #define R88E_RPTB2_RETRY_CNT_S 0 #define R88E_RPTB2_LIFE_EXPIRE 0x40 #define R88E_RPTB2_RETRY_OVER 0x80 uint8_t rptb3; uint8_t rptb4; uint8_t rptb5; uint8_t rptb6; #define R88E_RPTB6_QSEL_M 0xf0 #define R88E_RPTB6_QSEL_S 4 uint8_t rptb7; } __packed; static const uint8_t ridx2rate[] = { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 }; /* HW rate indices. */ #define URTWN_RIDX_CCK1 0 #define URTWN_RIDX_CCK11 3 #define URTWN_RIDX_OFDM6 4 #define URTWN_RIDX_OFDM24 8 #define URTWN_RIDX_OFDM54 11 #define URTWN_RIDX_COUNT 28 #define URTWN_RIDX_UNKNOWN (uint8_t)-1 /* * MAC initialization values. */ static const struct { uint16_t reg; uint8_t val; } rtl8188eu_mac[] = { { 0x026, 0x41 }, { 0x027, 0x35 }, { 0x040, 0x00 }, { 0x428, 0x0a }, { 0x429, 0x10 }, { 0x430, 0x00 }, { 0x431, 0x01 }, { 0x432, 0x02 }, { 0x433, 0x04 }, { 0x434, 0x05 }, { 0x435, 0x06 }, { 0x436, 0x07 }, { 0x437, 0x08 }, { 0x438, 0x00 }, { 0x439, 0x00 }, { 0x43a, 0x01 }, { 0x43b, 0x02 }, { 0x43c, 0x04 }, { 0x43d, 0x05 }, { 0x43e, 0x06 }, { 0x43f, 0x07 }, { 0x440, 0x5d }, { 0x441, 0x01 }, { 0x442, 0x00 }, { 0x444, 0x15 }, { 0x445, 0xf0 }, { 0x446, 0x0f }, { 0x447, 0x00 }, { 0x458, 0x41 }, { 0x459, 0xa8 }, { 0x45a, 0x72 }, { 0x45b, 0xb9 }, { 0x460, 0x66 }, { 0x461, 0x66 }, { 0x480, 0x08 }, { 0x4c8, 0xff }, { 0x4c9, 0x08 }, { 0x4cc, 0xff }, { 0x4cd, 0xff }, { 0x4ce, 0x01 }, { 0x4d3, 0x01 }, { 0x500, 0x26 }, { 0x501, 0xa2 }, { 0x502, 0x2f }, { 0x503, 0x00 }, { 0x504, 0x28 }, { 0x505, 0xa3 }, { 0x506, 0x5e }, { 0x507, 0x00 }, { 0x508, 0x2b }, { 0x509, 0xa4 }, { 0x50a, 0x5e }, { 0x50b, 0x00 }, { 0x50c, 0x4f }, { 0x50d, 0xa4 }, { 0x50e, 0x00 }, { 0x50f, 0x00 }, { 0x512, 0x1c }, { 0x514, 0x0a }, { 0x516, 0x0a }, { 0x525, 0x4f }, { 0x550, 0x10 }, { 0x551, 0x10 }, { 0x559, 0x02 }, { 0x55d, 0xff }, { 0x605, 0x30 }, { 0x608, 0x0e }, { 0x609, 0x2a }, { 0x620, 0xff }, { 0x621, 0xff }, { 0x622, 0xff }, { 0x623, 0xff }, { 0x624, 0xff }, { 0x625, 0xff }, { 0x626, 0xff }, { 0x627, 0xff }, { 0x652, 0x20 }, { 0x63c, 0x0a }, { 0x63d, 0x0a }, { 0x63e, 0x0e }, { 0x63f, 0x0e }, { 0x640, 0x40 }, { 0x66e, 0x05 }, { 0x700, 0x21 }, { 0x701, 0x43 }, { 0x702, 0x65 }, { 0x703, 0x87 }, { 0x708, 0x21 }, { 0x709, 0x43 }, { 0x70a, 0x65 }, { 0x70b, 0x87 } }, rtl8192cu_mac[] = { { 0x420, 0x80 }, { 0x423, 0x00 }, { 0x430, 0x00 }, { 0x431, 0x00 }, { 0x432, 0x00 }, { 0x433, 0x01 }, { 0x434, 0x04 }, { 0x435, 0x05 }, { 0x436, 0x06 }, { 0x437, 0x07 }, { 0x438, 0x00 }, { 0x439, 0x00 }, { 0x43a, 0x00 }, { 0x43b, 0x01 }, { 0x43c, 0x04 }, { 0x43d, 0x05 }, { 0x43e, 0x06 }, { 0x43f, 0x07 }, { 0x440, 0x5d }, { 0x441, 0x01 }, { 0x442, 0x00 }, { 0x444, 0x15 }, { 0x445, 0xf0 }, { 0x446, 0x0f }, { 0x447, 0x00 }, { 0x458, 0x41 }, { 0x459, 0xa8 }, { 0x45a, 0x72 }, { 0x45b, 0xb9 }, { 0x460, 0x66 }, { 0x461, 0x66 }, { 0x462, 0x08 }, { 0x463, 0x03 }, { 0x4c8, 0xff }, { 0x4c9, 0x08 }, { 0x4cc, 0xff }, { 0x4cd, 0xff }, { 0x4ce, 0x01 }, { 0x500, 0x26 }, { 0x501, 0xa2 }, { 0x502, 0x2f }, { 0x503, 0x00 }, { 0x504, 0x28 }, { 0x505, 0xa3 }, { 0x506, 0x5e }, { 0x507, 0x00 }, { 0x508, 0x2b }, { 0x509, 0xa4 }, { 0x50a, 0x5e }, { 0x50b, 0x00 }, { 0x50c, 0x4f }, { 0x50d, 0xa4 }, { 0x50e, 0x00 }, { 0x50f, 0x00 }, { 0x512, 0x1c }, { 0x514, 0x0a }, { 0x515, 0x10 }, { 0x516, 0x0a }, { 0x517, 0x10 }, { 0x51a, 0x16 }, { 0x524, 0x0f }, { 0x525, 0x4f }, { 0x546, 0x40 }, { 0x547, 0x00 }, { 0x550, 0x10 }, { 0x551, 0x10 }, { 0x559, 0x02 }, { 0x55a, 0x02 }, { 0x55d, 0xff }, { 0x605, 0x30 }, { 0x608, 0x0e }, { 0x609, 0x2a }, { 0x652, 0x20 }, { 0x63c, 0x0a }, { 0x63d, 0x0e }, { 0x63e, 0x0a }, { 0x63f, 0x0e }, { 0x66e, 0x05 }, { 0x700, 0x21 }, { 0x701, 0x43 }, { 0x702, 0x65 }, { 0x703, 0x87 }, { 0x708, 0x21 }, { 0x709, 0x43 }, { 0x70a, 0x65 }, { 0x70b, 0x87 } }; /* * Baseband initialization values. */ struct urtwn_bb_prog { int count; const uint16_t *regs; const uint32_t *vals; int agccount; const uint32_t *agcvals; }; /* * RTL8192CU and RTL8192CE-VAU. */ static const uint16_t rtl8192ce_bb_regs[] = { 0x024, 0x028, 0x800, 0x804, 0x808, 0x80c, 0x810, 0x814, 0x818, 0x81c, 0x820, 0x824, 0x828, 0x82c, 0x830, 0x834, 0x838, 0x83c, 0x840, 0x844, 0x848, 0x84c, 0x850, 0x854, 0x858, 0x85c, 0x860, 0x864, 0x868, 0x86c, 0x870, 0x874, 0x878, 0x87c, 0x880, 0x884, 0x888, 0x88c, 0x890, 0x894, 0x898, 0x89c, 0x900, 0x904, 0x908, 0x90c, 0xa00, 0xa04, 0xa08, 0xa0c, 0xa10, 0xa14, 0xa18, 0xa1c, 0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74, 0xc00, 0xc04, 0xc08, 0xc0c, 0xc10, 0xc14, 0xc18, 0xc1c, 0xc20, 0xc24, 0xc28, 0xc2c, 0xc30, 0xc34, 0xc38, 0xc3c, 0xc40, 0xc44, 0xc48, 0xc4c, 0xc50, 0xc54, 0xc58, 0xc5c, 0xc60, 0xc64, 0xc68, 0xc6c, 0xc70, 0xc74, 0xc78, 0xc7c, 0xc80, 0xc84, 0xc88, 0xc8c, 0xc90, 0xc94, 0xc98, 0xc9c, 0xca0, 0xca4, 0xca8, 0xcac, 0xcb0, 0xcb4, 0xcb8, 0xcbc, 0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0, 0xcd4, 0xcd8, 0xcdc, 0xce0, 0xce4, 0xce8, 0xcec, 0xd00, 0xd04, 0xd08, 0xd0c, 0xd10, 0xd14, 0xd18, 0xd2c, 0xd30, 0xd34, 0xd38, 0xd3c, 0xd40, 0xd44, 0xd48, 0xd4c, 0xd50, 0xd54, 0xd58, 0xd5c, 0xd60, 0xd64, 0xd68, 0xd6c, 0xd70, 0xd74, 0xd78, 0xe00, 0xe04, 0xe08, 0xe10, 0xe14, 0xe18, 0xe1c, 0xe28, 0xe30, 0xe34, 0xe38, 0xe3c, 0xe40, 0xe44, 0xe48, 0xe4c, 0xe50, 0xe54, 0xe58, 0xe5c, 0xe60, 0xe68, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec, 0xf14, 0xf4c, 0xf00 }; static const uint32_t rtl8192ce_bb_vals[] = { 0x0011800d, 0x00ffdb83, 0x80040002, 0x00000003, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390004, 0x01000100, 0x00390004, 0x27272727, 0x27272727, 0x27272727, 0x27272727, 0x00010000, 0x00010000, 0x27272727, 0x27272727, 0x00000000, 0x00000000, 0x569a569a, 0x0c1b25a4, 0x66e60230, 0x061f0130, 0x27272727, 0x2b2b2b27, 0x07000700, 0x22184000, 0x08080808, 0x00000000, 0xc0083070, 0x000004d5, 0x00000000, 0xcc0000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121313, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05633, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020403, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x63db25a4, 0x63db25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x63db25a4, 0x0c1b25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4, 0x001b25a4, 0x001b25a4, 0x6fdb25a4, 0x00000003, 0x00000000, 0x00000300 }; static const uint32_t rtl8192ce_agc_vals[] = { 0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001, 0x7b050001, 0x7a060001, 0x79070001, 0x78080001, 0x77090001, 0x760a0001, 0x750b0001, 0x740c0001, 0x730d0001, 0x720e0001, 0x710f0001, 0x70100001, 0x6f110001, 0x6e120001, 0x6d130001, 0x6c140001, 0x6b150001, 0x6a160001, 0x69170001, 0x68180001, 0x67190001, 0x661a0001, 0x651b0001, 0x641c0001, 0x631d0001, 0x621e0001, 0x611f0001, 0x60200001, 0x49210001, 0x48220001, 0x47230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001, 0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001, 0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001, 0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001, 0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001, 0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001, 0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001, 0x7a460001, 0x79470001, 0x78480001, 0x77490001, 0x764a0001, 0x754b0001, 0x744c0001, 0x734d0001, 0x724e0001, 0x714f0001, 0x70500001, 0x6f510001, 0x6e520001, 0x6d530001, 0x6c540001, 0x6b550001, 0x6a560001, 0x69570001, 0x68580001, 0x67590001, 0x665a0001, 0x655b0001, 0x645c0001, 0x635d0001, 0x625e0001, 0x615f0001, 0x60600001, 0x49610001, 0x48620001, 0x47630001, 0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001, 0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001, 0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001, 0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001, 0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001, 0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e, 0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e, 0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e, 0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e, 0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e, 0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e, 0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e }; static const struct urtwn_bb_prog rtl8192ce_bb_prog = { nitems(rtl8192ce_bb_regs), rtl8192ce_bb_regs, rtl8192ce_bb_vals, nitems(rtl8192ce_agc_vals), rtl8192ce_agc_vals }; /* * RTL8188CU. */ static const uint32_t rtl8192cu_bb_vals[] = { 0x0011800d, 0x00ffdb83, 0x80040002, 0x00000003, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390004, 0x01000100, 0x00390004, 0x27272727, 0x27272727, 0x27272727, 0x27272727, 0x00010000, 0x00010000, 0x27272727, 0x27272727, 0x00000000, 0x00000000, 0x569a569a, 0x0c1b25a4, 0x66e60230, 0x061f0130, 0x27272727, 0x2b2b2b27, 0x07000700, 0x22184000, 0x08080808, 0x00000000, 0xc0083070, 0x000004d5, 0x00000000, 0xcc0000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121313, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05633, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x0186115b, 0x0000001f, 0x00b99612, 0x40000100, 0x20f60000, 0x40000100, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020403, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x63db25a4, 0x63db25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x63db25a4, 0x0c1b25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4, 0x001b25a4, 0x001b25a4, 0x6fdb25a4, 0x00000003, 0x00000000, 0x00000300 }; static const struct urtwn_bb_prog rtl8192cu_bb_prog = { nitems(rtl8192ce_bb_regs), rtl8192ce_bb_regs, rtl8192cu_bb_vals, nitems(rtl8192ce_agc_vals), rtl8192ce_agc_vals }; /* * RTL8188CE-VAU. */ static const uint32_t rtl8188ce_bb_vals[] = { 0x0011800d, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a, 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200, 0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070, 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000008, 0x001b25a4, 0x631b25a0, 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003, 0x00000000, 0x00000300 }; static const uint32_t rtl8188ce_agc_vals[] = { 0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001, 0x7b050001, 0x7a060001, 0x79070001, 0x78080001, 0x77090001, 0x760a0001, 0x750b0001, 0x740c0001, 0x730d0001, 0x720e0001, 0x710f0001, 0x70100001, 0x6f110001, 0x6e120001, 0x6d130001, 0x6c140001, 0x6b150001, 0x6a160001, 0x69170001, 0x68180001, 0x67190001, 0x661a0001, 0x651b0001, 0x641c0001, 0x631d0001, 0x621e0001, 0x611f0001, 0x60200001, 0x49210001, 0x48220001, 0x47230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001, 0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001, 0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001, 0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001, 0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001, 0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001, 0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001, 0x7a460001, 0x79470001, 0x78480001, 0x77490001, 0x764a0001, 0x754b0001, 0x744c0001, 0x734d0001, 0x724e0001, 0x714f0001, 0x70500001, 0x6f510001, 0x6e520001, 0x6d530001, 0x6c540001, 0x6b550001, 0x6a560001, 0x69570001, 0x68580001, 0x67590001, 0x665a0001, 0x655b0001, 0x645c0001, 0x635d0001, 0x625e0001, 0x615f0001, 0x60600001, 0x49610001, 0x48620001, 0x47630001, 0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001, 0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001, 0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001, 0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001, 0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001, 0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e, 0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e, 0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e, 0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e, 0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e, 0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e, 0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e }; static const struct urtwn_bb_prog rtl8188ce_bb_prog = { nitems(rtl8192ce_bb_regs), rtl8192ce_bb_regs, rtl8188ce_bb_vals, nitems(rtl8188ce_agc_vals), rtl8188ce_agc_vals }; static const uint32_t rtl8188cu_bb_vals[] = { 0x0011800d, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a, 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200, 0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070, 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db, 0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000008, 0x001b25a4, 0x631b25a0, 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003, 0x00000000, 0x00000300 }; static const struct urtwn_bb_prog rtl8188cu_bb_prog = { nitems(rtl8192ce_bb_regs), rtl8192ce_bb_regs, rtl8188cu_bb_vals, nitems(rtl8188ce_agc_vals), rtl8188ce_agc_vals }; /* * RTL8188EU. */ static const uint16_t rtl8188eu_bb_regs[] = { 0x800, 0x804, 0x808, 0x80c, 0x810, 0x814, 0x818, 0x81c, 0x820, 0x824, 0x828, 0x82c, 0x830, 0x834, 0x838, 0x83c, 0x840, 0x844, 0x848, 0x84c, 0x850, 0x854, 0x858, 0x85c, 0x860, 0x864, 0x868, 0x86c, 0x870, 0x874, 0x878, 0x87c, 0x880, 0x884, 0x888, 0x88c, 0x890, 0x894, 0x898, 0x89c, 0x900, 0x904, 0x908, 0x90c, 0x910, 0x914, 0xa00, 0xa04, 0xa08, 0xa0c, 0xa10, 0xa14, 0xa18, 0xa1c, 0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74, 0xa78, 0xa7c, 0xa80, 0xb2c, 0xc00, 0xc04, 0xc08, 0xc0c, 0xc10, 0xc14, 0xc18, 0xc1c, 0xc20, 0xc24, 0xc28, 0xc2c, 0xc30, 0xc34, 0xc38, 0xc3c, 0xc40, 0xc44, 0xc48, 0xc4c, 0xc50, 0xc54, 0xc58, 0xc5c, 0xc60, 0xc64, 0xc68, 0xc6c, 0xc70, 0xc74, 0xc78, 0xc7c, 0xc80, 0xc84, 0xc88, 0xc8c, 0xc90, 0xc94, 0xc98, 0xc9c, 0xca0, 0xca4, 0xca8, 0xcac, 0xcb0, 0xcb4, 0xcb8, 0xcbc, 0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0, 0xcd4, 0xcd8, 0xcdc, 0xce0, 0xce4, 0xce8, 0xcec, 0xd00, 0xd04, 0xd08, 0xd0c, 0xd10, 0xd14, 0xd18, 0xd2c, 0xd30, 0xd34, 0xd38, 0xd3c, 0xd40, 0xd44, 0xd48, 0xd4c, 0xd50, 0xd54, 0xd58, 0xd5c, 0xd60, 0xd64, 0xd68, 0xd6c, 0xd70, 0xd74, 0xd78, 0xe00, 0xe04, 0xe08, 0xe10, 0xe14, 0xe18, 0xe1c, 0xe28, 0xe30, 0xe34, 0xe38, 0xe3c, 0xe40, 0xe44, 0xe48, 0xe4c, 0xe50, 0xe54, 0xe58, 0xe5c, 0xe60, 0xe68, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xee8, 0xeec, 0xf14, 0xf4c, 0xf00 }; static const uint32_t rtl8188eu_bb_vals[] = { 0x80040000, 0x00000003, 0x0000fc00, 0x0000000a, 0x10001331, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390204, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a11a9, 0x01000014, 0x66f60110, 0x061f0649, 0x00000000, 0x27272700, 0x07000760, 0x25004000, 0x00000808, 0x00000000, 0xb0000c1c, 0x00000001, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121111, 0x00000002, 0x00000201, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e7f120f, 0x9500bb78, 0x1114d028, 0x00881117, 0x89140f00, 0x1a1b0000, 0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007, 0x00000900, 0x225b0606, 0x218075b1, 0x80000000, 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac47, 0x469652af, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x69553420, 0x43bc0094, 0x00013169, 0x00250492, 0x00000000, 0x7112848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x020610db, 0x0000001f, 0x00b91612, 0x390000e4, 0x20f60000, 0x40000100, 0x20200000, 0x00091521, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x000300a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00000740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6f, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00127353, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000282, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2d2d2d2d, 0x2d2d2d2d, 0x0390272d, 0x2d2d2d2d, 0x2d2d2d2d, 0x2d2d2d2d, 0x2d2d2d2d, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000008, 0x001b25a4, 0x00c00014, 0x00c00014, 0x01000014, 0x01000014, 0x01000014, 0x01000014, 0x00c00014, 0x01000014, 0x00c00014, 0x00c00014, 0x00c00014, 0x00c00014, 0x00000014, 0x00000014, 0x21555448, 0x01c00014, 0x00000003, 0x00000000, 0x00000300 }; static const uint32_t rtl8188eu_agc_vals[] = { 0xfb000001, 0xfb010001, 0xfb020001, 0xfb030001, 0xfb040001, 0xfb050001, 0xfa060001, 0xf9070001, 0xf8080001, 0xf7090001, 0xf60a0001, 0xf50b0001, 0xf40c0001, 0xf30d0001, 0xf20e0001, 0xf10f0001, 0xf0100001, 0xef110001, 0xee120001, 0xed130001, 0xec140001, 0xeb150001, 0xea160001, 0xe9170001, 0xe8180001, 0xe7190001, 0xe61a0001, 0xe51b0001, 0xe41c0001, 0xe31d0001, 0xe21e0001, 0xe11f0001, 0x8a200001, 0x89210001, 0x88220001, 0x87230001, 0x86240001, 0x85250001, 0x84260001, 0x83270001, 0x82280001, 0x6b290001, 0x6a2a0001, 0x692b0001, 0x682c0001, 0x672d0001, 0x662e0001, 0x652f0001, 0x64300001, 0x63310001, 0x62320001, 0x61330001, 0x46340001, 0x45350001, 0x44360001, 0x43370001, 0x42380001, 0x41390001, 0x403a0001, 0x403b0001, 0x403c0001, 0x403d0001, 0x403e0001, 0x403f0001, 0xfb400001, 0xfb410001, 0xfb420001, 0xfb430001, 0xfb440001, 0xfb450001, 0xfb460001, 0xfb470001, 0xfb480001, 0xfa490001, 0xf94a0001, 0xf84B0001, 0xf74c0001, 0xf64d0001, 0xf54e0001, 0xf44f0001, 0xf3500001, 0xf2510001, 0xf1520001, 0xf0530001, 0xef540001, 0xee550001, 0xed560001, 0xec570001, 0xeb580001, 0xea590001, 0xe95a0001, 0xe85b0001, 0xe75c0001, 0xe65d0001, 0xe55e0001, 0xe45f0001, 0xe3600001, 0xe2610001, 0xc3620001, 0xc2630001, 0xc1640001, 0x8b650001, 0x8a660001, 0x89670001, 0x88680001, 0x87690001, 0x866a0001, 0x856b0001, 0x846c0001, 0x676d0001, 0x666e0001, 0x656f0001, 0x64700001, 0x63710001, 0x62720001, 0x61730001, 0x60740001, 0x46750001, 0x45760001, 0x44770001, 0x43780001, 0x42790001, 0x417a0001, 0x407b0001, 0x407c0001, 0x407d0001, 0x407e0001, 0x407f0001 }; static const struct urtwn_bb_prog rtl8188eu_bb_prog = { nitems(rtl8188eu_bb_regs), rtl8188eu_bb_regs, rtl8188eu_bb_vals, nitems(rtl8188eu_agc_vals), rtl8188eu_agc_vals }; /* * RTL8188RU. */ static const uint16_t rtl8188ru_bb_regs[] = { 0x024, 0x028, 0x040, 0x800, 0x804, 0x808, 0x80c, 0x810, 0x814, 0x818, 0x81c, 0x820, 0x824, 0x828, 0x82c, 0x830, 0x834, 0x838, 0x83c, 0x840, 0x844, 0x848, 0x84c, 0x850, 0x854, 0x858, 0x85c, 0x860, 0x864, 0x868, 0x86c, 0x870, 0x874, 0x878, 0x87c, 0x880, 0x884, 0x888, 0x88c, 0x890, 0x894, 0x898, 0x89c, 0x900, 0x904, 0x908, 0x90c, 0xa00, 0xa04, 0xa08, 0xa0c, 0xa10, 0xa14, 0xa18, 0xa1c, 0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74, 0xc00, 0xc04, 0xc08, 0xc0c, 0xc10, 0xc14, 0xc18, 0xc1c, 0xc20, 0xc24, 0xc28, 0xc2c, 0xc30, 0xc34, 0xc38, 0xc3c, 0xc40, 0xc44, 0xc48, 0xc4c, 0xc50, 0xc54, 0xc58, 0xc5c, 0xc60, 0xc64, 0xc68, 0xc6c, 0xc70, 0xc74, 0xc78, 0xc7c, 0xc80, 0xc84, 0xc88, 0xc8c, 0xc90, 0xc94, 0xc98, 0xc9c, 0xca0, 0xca4, 0xca8, 0xcac, 0xcb0, 0xcb4, 0xcb8, 0xcbc, 0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0, 0xcd4, 0xcd8, 0xcdc, 0xce0, 0xce4, 0xce8, 0xcec, 0xd00, 0xd04, 0xd08, 0xd0c, 0xd10, 0xd14, 0xd18, 0xd2c, 0xd30, 0xd34, 0xd38, 0xd3c, 0xd40, 0xd44, 0xd48, 0xd4c, 0xd50, 0xd54, 0xd58, 0xd5c, 0xd60, 0xd64, 0xd68, 0xd6c, 0xd70, 0xd74, 0xd78, 0xe00, 0xe04, 0xe08, 0xe10, 0xe14, 0xe18, 0xe1c, 0xe28, 0xe30, 0xe34, 0xe38, 0xe3c, 0xe40, 0xe44, 0xe48, 0xe4c, 0xe50, 0xe54, 0xe58, 0xe5c, 0xe60, 0xe68, 0xe6c, 0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4, 0xed8, 0xedc, 0xee0, 0xeec, 0xee8, 0xf14, 0xf4c, 0xf00 }; static const uint32_t rtl8188ru_bb_vals[] = { 0x0011800d, 0x00ffdb83, 0x000c0004, 0x80040000, 0x00000001, 0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390204, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a, 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200, 0x03000300, 0x22004000, 0x00000808, 0x00ffc3f1, 0xc0083070, 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000, 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x15160000, 0x070b0f12, 0x00000104, 0x00d30000, 0x101fbf00, 0x00000007, 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x6954342e, 0x43bc0094, 0x6954342f, 0x433c0094, 0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c56000d, 0x018610db, 0x0000001f, 0x00b91612, 0x24000090, 0x20f60000, 0x24000090, 0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x631b25a0, 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x31555448, 0x00000003, 0x00000000, 0x00000300 }; static const uint32_t rtl8188ru_agc_vals[] = { 0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001, 0x7b050001, 0x7b060001, 0x7b070001, 0x7b080001, 0x7a090001, 0x790a0001, 0x780b0001, 0x770c0001, 0x760d0001, 0x750e0001, 0x740f0001, 0x73100001, 0x72110001, 0x71120001, 0x70130001, 0x6f140001, 0x6e150001, 0x6d160001, 0x6c170001, 0x6b180001, 0x6a190001, 0x691a0001, 0x681b0001, 0x671c0001, 0x661d0001, 0x651e0001, 0x641f0001, 0x63200001, 0x62210001, 0x61220001, 0x60230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001, 0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001, 0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001, 0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001, 0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001, 0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001, 0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001, 0x7b460001, 0x7b470001, 0x7b480001, 0x7a490001, 0x794a0001, 0x784b0001, 0x774c0001, 0x764d0001, 0x754e0001, 0x744f0001, 0x73500001, 0x72510001, 0x71520001, 0x70530001, 0x6f540001, 0x6e550001, 0x6d560001, 0x6c570001, 0x6b580001, 0x6a590001, 0x695a0001, 0x685b0001, 0x675c0001, 0x665d0001, 0x655e0001, 0x645f0001, 0x63600001, 0x62610001, 0x61620001, 0x60630001, 0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001, 0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001, 0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001, 0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001, 0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001, 0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e, 0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e, 0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e, 0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e, 0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e, 0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e, 0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e }; static const struct urtwn_bb_prog rtl8188ru_bb_prog = { nitems(rtl8188ru_bb_regs), rtl8188ru_bb_regs, rtl8188ru_bb_vals, nitems(rtl8188ru_agc_vals), rtl8188ru_agc_vals }; /* * RF initialization values. */ struct urtwn_rf_prog { int count; const uint8_t *regs; const uint32_t *vals; }; /* * RTL8192CU and RTL8192CE-VAU. */ static const uint8_t rtl8192ce_rf1_regs[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2a, 0x2b, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x00, 0x18, 0xfe, 0xfe, 0x1f, 0xfe, 0xfe, 0x1e, 0x1f, 0x00 }; static const uint32_t rtl8192ce_rf1_vals[] = { 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255, 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, 0x00000, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x577c0, 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000, 0x71000, 0xb0000, 0xfc000, 0x287af, 0x244b7, 0x204ab, 0x1c49f, 0x18493, 0x14297, 0x10295, 0x0c298, 0x0819c, 0x040a8, 0x0001c, 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424, 0xcf424, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, 0x30159 }; static const uint8_t rtl8192ce_rf2_regs[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16 }; static const uint32_t rtl8192ce_rf2_vals[] = { 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x32000, 0x71000, 0xb0000, 0xfc000, 0x287af, 0x244b7, 0x204ab, 0x1c49f, 0x18493, 0x14297, 0x10295, 0x0c298, 0x0819c, 0x040a8, 0x0001c, 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424, 0xcf424, 0xe0330, 0xa0330, 0x60330, 0x20330 }; static const struct urtwn_rf_prog rtl8192ce_rf_prog[] = { { nitems(rtl8192ce_rf1_regs), rtl8192ce_rf1_regs, rtl8192ce_rf1_vals }, { nitems(rtl8192ce_rf2_regs), rtl8192ce_rf2_regs, rtl8192ce_rf2_vals } }; /* * RTL8188CE-VAU. */ static const uint32_t rtl8188ce_rf_vals[] = { 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255, 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, 0x00000, 0x01558, 0x00060, 0x00483, 0x4f200, 0xec7d9, 0x577c0, 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000, 0x71000, 0xb0000, 0xfc000, 0x287b3, 0x244b7, 0x204ab, 0x1c49f, 0x18493, 0x1429b, 0x10299, 0x0c29c, 0x081a0, 0x040ac, 0x00020, 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424, 0xcf424, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, 0x30159 }; static const struct urtwn_rf_prog rtl8188ce_rf_prog[] = { { nitems(rtl8192ce_rf1_regs), rtl8192ce_rf1_regs, rtl8188ce_rf_vals } }; /* * RTL8188CU. */ static const uint32_t rtl8188cu_rf_vals[] = { 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1, 0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255, 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, 0x00000, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x577c0, 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000, 0x71000, 0xb0000, 0xfc000, 0x287b3, 0x244b7, 0x204ab, 0x1c49f, 0x18493, 0x1429b, 0x10299, 0x0c29c, 0x081a0, 0x040ac, 0x00020, 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f405, 0x4f405, 0x8f405, 0xcf405, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, 0x30159 }; static const struct urtwn_rf_prog rtl8188cu_rf_prog[] = { { nitems(rtl8192ce_rf1_regs), rtl8192ce_rf1_regs, rtl8188cu_rf_vals } }; /* * RTL8188EU. */ static const uint8_t rtl8188eu_rf_regs[] = { 0x00, 0x08, 0x18, 0x19, 0x1e, 0x1f, 0x2f, 0x3f, 0x42, 0x57, 0x58, 0x67, 0x83, 0xb0, 0xb1, 0xb2, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbf, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xdf, 0xef, 0x51, 0x52, 0x53, 0x56, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0xb6, 0x18, 0x5a, 0x19, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x00, 0x84, 0x86, 0x87, 0x8e, 0x8f, 0xef, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xef, 0x00, 0x18, 0xfe, 0xfe, 0x1f, 0xfe, 0xfe, 0x1e, 0x1f, 0x00 }; static const uint32_t rtl8188eu_rf_vals[] = { 0x30000, 0x84000, 0x00407, 0x00012, 0x80009, 0x00880, 0x1a060, 0x00000, 0x060c0, 0xd0000, 0xbe180, 0x01552, 0x00000, 0xff8fc, 0x54400, 0xccc19, 0x43003, 0x4953e, 0x1c718, 0x060ff, 0x80001, 0x40000, 0x00400, 0xc0000, 0x02400, 0x00009, 0x40c91, 0x99999, 0x000a3, 0x88820, 0x76c06, 0x00000, 0x80000, 0x00180, 0x001a0, 0x6b27d, 0x7e49d, 0x00073, 0x51ff3, 0x00086, 0x00186, 0x00286, 0x01c25, 0x09c25, 0x11c25, 0x19c25, 0x48538, 0x00c07, 0x4bd00, 0x739d0, 0x0adf3, 0x09df0, 0x08ded, 0x07dea, 0x06de7, 0x054ee, 0x044eb, 0x034e8, 0x0246b, 0x01468, 0x0006d, 0x30159, 0x68200, 0x000ce, 0x48a00, 0x65540, 0x88000, 0x020a0, 0xf02b0, 0xef7b0, 0xd4fb0, 0xcf060, 0xb0090, 0xa0080, 0x90080, 0x8f780, 0x722b0, 0x6f7b0, 0x54fb0, 0x4f060, 0x30090, 0x20080, 0x10080, 0x0f780, 0x000a0, 0x10159, 0x0f407, 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x00001, 0x80000, 0x33e60 }; static const struct urtwn_rf_prog rtl8188eu_rf_prog[] = { { nitems(rtl8188eu_rf_regs), rtl8188eu_rf_regs, rtl8188eu_rf_vals } }; /* * RTL8188RU. */ static const uint32_t rtl8188ru_rf_vals[] = { 0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb0, 0x54867, 0x8992e, 0x0e529, 0x39ce7, 0x00451, 0x00000, 0x00255, 0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000, 0x0083c, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x977c0, 0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808, 0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003, 0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d, 0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333, 0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a, 0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a, 0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d, 0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333, 0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f, 0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500, 0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0xd8000, 0x90000, 0x51000, 0x12000, 0x28fb4, 0x24fa8, 0x207a4, 0x1c798, 0x183a4, 0x14398, 0x101a4, 0x0c198, 0x080a4, 0x04098, 0x00014, 0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f405, 0x4f405, 0x8f405, 0xcf405, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401, 0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000, 0x30159 }; static const struct urtwn_rf_prog rtl8188ru_rf_prog[] = { { nitems(rtl8192ce_rf1_regs), rtl8192ce_rf1_regs, rtl8188ru_rf_vals } }; struct urtwn_txpwr { uint8_t pwr[3][28]; }; struct urtwn_r88e_txpwr { uint8_t pwr[6][28]; }; /* * Per RF chain/group/rate Tx gain values. */ static const struct urtwn_txpwr rtl8192cu_txagc[] = { { { /* Chain 0. */ { /* Group 0. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x0c, 0x0c, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, /* OFDM6~54. */ 0x0e, 0x0d, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, /* MCS0~7. */ 0x0e, 0x0d, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02 /* MCS8~15. */ }, { /* Group 1. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 2. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ } } }, { { /* Chain 1. */ { /* Group 0. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 1. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 2. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ } } } }; static const struct urtwn_txpwr rtl8188ru_txagc[] = { { { /* Chain 0. */ { /* Group 0. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x08, 0x08, 0x08, 0x06, 0x06, 0x04, 0x04, 0x00, /* OFDM6~54. */ 0x08, 0x06, 0x06, 0x04, 0x04, 0x02, 0x02, 0x00, /* MCS0~7. */ 0x08, 0x06, 0x06, 0x04, 0x04, 0x02, 0x02, 0x00 /* MCS8~15. */ }, { /* Group 1. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 2. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ } } } }; static const struct urtwn_r88e_txpwr rtl8188eu_txagc[] = { { { /* Chain 0. */ { /* Group 0. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 1. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 2. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 3. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 4. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ }, { /* Group 5. */ 0x00, 0x00, 0x00, 0x00, /* CCK1~11. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* MCS8~15. */ } } } }; Index: head/sys/dev/usb/wlan/if_rsu.c =================================================================== --- head/sys/dev/usb/wlan/if_rsu.c (revision 300753) +++ head/sys/dev/usb/wlan/if_rsu.c (revision 300754) @@ -1,2946 +1,2963 @@ /* $OpenBSD: if_rsu.c,v 1.17 2013/04/15 09:23:01 mglocker Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /* * Driver for Realtek RTL8188SU/RTL8191SU/RTL8192SU. * * TODO: * o h/w crypto * o hostap / ibss / mesh * o sensible RSSI levels * o power-save operation */ #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR rsu_debug #include #include #ifdef USB_DEBUG static int rsu_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, rsu, CTLFLAG_RW, 0, "USB rsu"); SYSCTL_INT(_hw_usb_rsu, OID_AUTO, debug, CTLFLAG_RWTUN, &rsu_debug, 0, "Debug level"); #define RSU_DPRINTF(_sc, _flg, ...) \ do \ if (((_flg) == (RSU_DEBUG_ANY)) || (rsu_debug & (_flg))) \ device_printf((_sc)->sc_dev, __VA_ARGS__); \ while (0) #else #define RSU_DPRINTF(_sc, _flg, ...) #endif static int rsu_enable_11n = 1; TUNABLE_INT("hw.usb.rsu.enable_11n", &rsu_enable_11n); #define RSU_DEBUG_ANY 0xffffffff #define RSU_DEBUG_TX 0x00000001 #define RSU_DEBUG_RX 0x00000002 #define RSU_DEBUG_RESET 0x00000004 #define RSU_DEBUG_CALIB 0x00000008 #define RSU_DEBUG_STATE 0x00000010 #define RSU_DEBUG_SCAN 0x00000020 #define RSU_DEBUG_FWCMD 0x00000040 #define RSU_DEBUG_TXDONE 0x00000080 #define RSU_DEBUG_FW 0x00000100 #define RSU_DEBUG_FWDBG 0x00000200 #define RSU_DEBUG_AMPDU 0x00000400 static const STRUCT_USB_HOST_ID rsu_devs[] = { #define RSU_HT_NOT_SUPPORTED 0 #define RSU_HT_SUPPORTED 1 #define RSU_DEV_HT(v,p) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \ RSU_HT_SUPPORTED) } #define RSU_DEV(v,p) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \ RSU_HT_NOT_SUPPORTED) } RSU_DEV(ASUS, RTL8192SU), RSU_DEV(AZUREWAVE, RTL8192SU_4), RSU_DEV_HT(ACCTON, RTL8192SU), RSU_DEV_HT(ASUS, USBN10), RSU_DEV_HT(AZUREWAVE, RTL8192SU_1), RSU_DEV_HT(AZUREWAVE, RTL8192SU_2), RSU_DEV_HT(AZUREWAVE, RTL8192SU_3), RSU_DEV_HT(AZUREWAVE, RTL8192SU_5), RSU_DEV_HT(BELKIN, RTL8192SU_1), RSU_DEV_HT(BELKIN, RTL8192SU_2), RSU_DEV_HT(BELKIN, RTL8192SU_3), RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_1), RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_2), RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_3), RSU_DEV_HT(COREGA, RTL8192SU), RSU_DEV_HT(DLINK2, DWA131A1), RSU_DEV_HT(DLINK2, RTL8192SU_1), RSU_DEV_HT(DLINK2, RTL8192SU_2), RSU_DEV_HT(EDIMAX, RTL8192SU_1), RSU_DEV_HT(EDIMAX, RTL8192SU_2), RSU_DEV_HT(EDIMAX, EW7622UMN), RSU_DEV_HT(GUILLEMOT, HWGUN54), RSU_DEV_HT(GUILLEMOT, HWNUM300), RSU_DEV_HT(HAWKING, RTL8192SU_1), RSU_DEV_HT(HAWKING, RTL8192SU_2), RSU_DEV_HT(PLANEX2, GWUSNANO), RSU_DEV_HT(REALTEK, RTL8171), RSU_DEV_HT(REALTEK, RTL8172), RSU_DEV_HT(REALTEK, RTL8173), RSU_DEV_HT(REALTEK, RTL8174), RSU_DEV_HT(REALTEK, RTL8192SU), RSU_DEV_HT(REALTEK, RTL8712), RSU_DEV_HT(REALTEK, RTL8713), RSU_DEV_HT(SENAO, RTL8192SU_1), RSU_DEV_HT(SENAO, RTL8192SU_2), RSU_DEV_HT(SITECOMEU, WL349V1), RSU_DEV_HT(SITECOMEU, WL353), RSU_DEV_HT(SWEEX2, LW154), RSU_DEV_HT(TRENDNET, TEW646UBH), #undef RSU_DEV_HT #undef RSU_DEV }; static device_probe_t rsu_match; static device_attach_t rsu_attach; static device_detach_t rsu_detach; static usb_callback_t rsu_bulk_tx_callback_be_bk; static usb_callback_t rsu_bulk_tx_callback_vi_vo; static usb_callback_t rsu_bulk_tx_callback_h2c; static usb_callback_t rsu_bulk_rx_callback; static usb_error_t rsu_do_request(struct rsu_softc *, struct usb_device_request *, void *); static struct ieee80211vap * rsu_vap_create(struct ieee80211com *, const char name[], int, enum ieee80211_opmode, int, const uint8_t bssid[], const uint8_t mac[]); static void rsu_vap_delete(struct ieee80211vap *); static void rsu_scan_start(struct ieee80211com *); static void rsu_scan_end(struct ieee80211com *); +static void rsu_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); static void rsu_set_channel(struct ieee80211com *); static void rsu_update_mcast(struct ieee80211com *); static int rsu_alloc_rx_list(struct rsu_softc *); static void rsu_free_rx_list(struct rsu_softc *); static int rsu_alloc_tx_list(struct rsu_softc *); static void rsu_free_tx_list(struct rsu_softc *); static void rsu_free_list(struct rsu_softc *, struct rsu_data [], int); static struct rsu_data *_rsu_getbuf(struct rsu_softc *); static struct rsu_data *rsu_getbuf(struct rsu_softc *); static void rsu_freebuf(struct rsu_softc *, struct rsu_data *); static int rsu_write_region_1(struct rsu_softc *, uint16_t, uint8_t *, int); static void rsu_write_1(struct rsu_softc *, uint16_t, uint8_t); static void rsu_write_2(struct rsu_softc *, uint16_t, uint16_t); static void rsu_write_4(struct rsu_softc *, uint16_t, uint32_t); static int rsu_read_region_1(struct rsu_softc *, uint16_t, uint8_t *, int); static uint8_t rsu_read_1(struct rsu_softc *, uint16_t); static uint16_t rsu_read_2(struct rsu_softc *, uint16_t); static uint32_t rsu_read_4(struct rsu_softc *, uint16_t); static int rsu_fw_iocmd(struct rsu_softc *, uint32_t); static uint8_t rsu_efuse_read_1(struct rsu_softc *, uint16_t); static int rsu_read_rom(struct rsu_softc *); static int rsu_fw_cmd(struct rsu_softc *, uint8_t, void *, int); static void rsu_calib_task(void *, int); static void rsu_tx_task(void *, int); static int rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int); #ifdef notyet static void rsu_set_key(struct rsu_softc *, const struct ieee80211_key *); static void rsu_delete_key(struct rsu_softc *, const struct ieee80211_key *); #endif static int rsu_site_survey(struct rsu_softc *, struct ieee80211vap *); static int rsu_join_bss(struct rsu_softc *, struct ieee80211_node *); static int rsu_disconnect(struct rsu_softc *); static int rsu_hwrssi_to_rssi(struct rsu_softc *, int hw_rssi); static void rsu_event_survey(struct rsu_softc *, uint8_t *, int); static void rsu_event_join_bss(struct rsu_softc *, uint8_t *, int); static void rsu_rx_event(struct rsu_softc *, uint8_t, uint8_t *, int); static void rsu_rx_multi_event(struct rsu_softc *, uint8_t *, int); #if 0 static int8_t rsu_get_rssi(struct rsu_softc *, int, void *); #endif static struct mbuf * rsu_rx_frame(struct rsu_softc *, uint8_t *, int); static struct mbuf * rsu_rx_multi_frame(struct rsu_softc *, uint8_t *, int); static struct mbuf * rsu_rxeof(struct usb_xfer *, struct rsu_data *); static void rsu_txeof(struct usb_xfer *, struct rsu_data *); static int rsu_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void rsu_init(struct rsu_softc *); static int rsu_tx_start(struct rsu_softc *, struct ieee80211_node *, struct mbuf *, struct rsu_data *); static int rsu_transmit(struct ieee80211com *, struct mbuf *); static void rsu_start(struct rsu_softc *); static void _rsu_start(struct rsu_softc *); static void rsu_parent(struct ieee80211com *); static void rsu_stop(struct rsu_softc *); static void rsu_ms_delay(struct rsu_softc *, int); static device_method_t rsu_methods[] = { DEVMETHOD(device_probe, rsu_match), DEVMETHOD(device_attach, rsu_attach), DEVMETHOD(device_detach, rsu_detach), DEVMETHOD_END }; static driver_t rsu_driver = { .name = "rsu", .methods = rsu_methods, .size = sizeof(struct rsu_softc) }; static devclass_t rsu_devclass; DRIVER_MODULE(rsu, uhub, rsu_driver, rsu_devclass, NULL, 0); MODULE_DEPEND(rsu, wlan, 1, 1, 1); MODULE_DEPEND(rsu, usb, 1, 1, 1); MODULE_DEPEND(rsu, firmware, 1, 1, 1); MODULE_VERSION(rsu, 1); USB_PNP_HOST_INFO(rsu_devs); +static const uint8_t rsu_chan_2ghz[] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + static uint8_t rsu_wme_ac_xfer_map[4] = { [WME_AC_BE] = RSU_BULK_TX_BE_BK, [WME_AC_BK] = RSU_BULK_TX_BE_BK, [WME_AC_VI] = RSU_BULK_TX_VI_VO, [WME_AC_VO] = RSU_BULK_TX_VI_VO, }; /* XXX hard-coded */ #define RSU_H2C_ENDPOINT 3 static const struct usb_config rsu_config[RSU_N_TRANSFER] = { [RSU_BULK_RX] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = RSU_RXBUFSZ, .flags = { .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = rsu_bulk_rx_callback }, [RSU_BULK_TX_BE_BK] = { .type = UE_BULK, .endpoint = 0x06, .direction = UE_DIR_OUT, .bufsize = RSU_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, .force_short_xfer = 1 }, .callback = rsu_bulk_tx_callback_be_bk, .timeout = RSU_TX_TIMEOUT }, [RSU_BULK_TX_VI_VO] = { .type = UE_BULK, .endpoint = 0x04, .direction = UE_DIR_OUT, .bufsize = RSU_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, .force_short_xfer = 1 }, .callback = rsu_bulk_tx_callback_vi_vo, .timeout = RSU_TX_TIMEOUT }, [RSU_BULK_TX_H2C] = { .type = UE_BULK, .endpoint = 0x0d, .direction = UE_DIR_OUT, .bufsize = RSU_TXBUFSZ, .flags = { .ext_buffer = 1, .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = rsu_bulk_tx_callback_h2c, .timeout = RSU_TX_TIMEOUT }, }; static int rsu_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->usb_mode != USB_MODE_HOST || uaa->info.bIfaceIndex != 0 || uaa->info.bConfigIndex != 0) return (ENXIO); return (usbd_lookup_id_by_uaa(rsu_devs, sizeof(rsu_devs), uaa)); } static int rsu_send_mgmt(struct ieee80211_node *ni, int type, int arg) { return (ENOTSUP); } static void rsu_update_chw(struct ieee80211com *ic) { } /* * notification from net80211 that it'd like to do A-MPDU on the given TID. * * Note: this actually hangs traffic at the present moment, so don't use it. * The firmware debug does indiciate it's sending and establishing a TX AMPDU * session, but then no traffic flows. */ static int rsu_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { #if 0 struct rsu_softc *sc = ni->ni_ic->ic_softc; struct r92s_add_ba_req req; /* Don't enable if it's requested or running */ if (IEEE80211_AMPDU_REQUESTED(tap)) return (0); if (IEEE80211_AMPDU_RUNNING(tap)) return (0); /* We've decided to send addba; so send it */ req.tid = htole32(tap->txa_tid); /* Attempt net80211 state */ if (ieee80211_ampdu_tx_request_ext(ni, tap->txa_tid) != 1) return (0); /* Send the firmware command */ RSU_DPRINTF(sc, RSU_DEBUG_AMPDU, "%s: establishing AMPDU TX for TID %d\n", __func__, tap->txa_tid); RSU_LOCK(sc); if (rsu_fw_cmd(sc, R92S_CMD_ADDBA_REQ, &req, sizeof(req)) != 1) { RSU_UNLOCK(sc); /* Mark failure */ (void) ieee80211_ampdu_tx_request_active_ext(ni, tap->txa_tid, 0); return (0); } RSU_UNLOCK(sc); /* Mark success; we don't get any further notifications */ (void) ieee80211_ampdu_tx_request_active_ext(ni, tap->txa_tid, 1); #endif /* Return 0, we're driving this ourselves */ return (0); } static int rsu_wme_update(struct ieee80211com *ic) { /* Firmware handles this; not our problem */ return (0); } static int rsu_attach(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); struct rsu_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; int error; - uint8_t bands[IEEE80211_MODE_BYTES]; uint8_t iface_index; struct usb_interface *iface; const char *rft; device_set_usb_desc(self); sc->sc_udev = uaa->device; sc->sc_dev = self; if (rsu_enable_11n) sc->sc_ht = !! (USB_GET_DRIVER_INFO(uaa) & RSU_HT_SUPPORTED); /* Get number of endpoints */ iface = usbd_get_iface(sc->sc_udev, 0); sc->sc_nendpoints = iface->idesc->bNumEndpoints; /* Endpoints are hard-coded for now, so enforce 4-endpoint only */ if (sc->sc_nendpoints != 4) { device_printf(sc->sc_dev, "the driver currently only supports 4-endpoint devices\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF); TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_task, 0, rsu_calib_task, sc); TASK_INIT(&sc->tx_task, 0, rsu_tx_task, sc); mbufq_init(&sc->sc_snd, ifqmaxlen); /* Allocate Tx/Rx buffers. */ error = rsu_alloc_rx_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Rx buffers\n"); goto fail_usb; } error = rsu_alloc_tx_list(sc); if (error != 0) { device_printf(sc->sc_dev, "could not allocate Tx buffers\n"); rsu_free_rx_list(sc); goto fail_usb; } iface_index = 0; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, rsu_config, RSU_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(sc->sc_dev, "could not allocate USB transfers, err=%s\n", usbd_errstr(error)); goto fail_usb; } RSU_LOCK(sc); /* Read chip revision. */ sc->cut = MS(rsu_read_4(sc, R92S_PMC_FSM), R92S_PMC_FSM_CUT); if (sc->cut != 3) sc->cut = (sc->cut >> 1) + 1; error = rsu_read_rom(sc); RSU_UNLOCK(sc); if (error != 0) { device_printf(self, "could not read ROM\n"); goto fail_rom; } /* Figure out TX/RX streams */ switch (sc->rom[84]) { case 0x0: sc->sc_rftype = RTL8712_RFCONFIG_1T1R; sc->sc_nrxstream = 1; sc->sc_ntxstream = 1; rft = "1T1R"; break; case 0x1: sc->sc_rftype = RTL8712_RFCONFIG_1T2R; sc->sc_nrxstream = 2; sc->sc_ntxstream = 1; rft = "1T2R"; break; case 0x2: sc->sc_rftype = RTL8712_RFCONFIG_2T2R; sc->sc_nrxstream = 2; sc->sc_ntxstream = 2; rft = "2T2R"; break; default: device_printf(sc->sc_dev, "%s: unknown board type (rfconfig=0x%02x)\n", __func__, sc->rom[84]); goto fail_rom; } IEEE80211_ADDR_COPY(ic->ic_macaddr, &sc->rom[0x12]); device_printf(self, "MAC/BB RTL8712 cut %d %s\n", sc->cut, rft); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(self); ic->ic_phytype = IEEE80211_T_OFDM; /* Not only, but not used. */ ic->ic_opmode = IEEE80211_M_STA; /* Default to BSS mode. */ /* Set device capabilities. */ ic->ic_caps = IEEE80211_C_STA | /* station mode */ #if 0 IEEE80211_C_BGSCAN | /* Background scan. */ #endif IEEE80211_C_SHPREAMBLE | /* Short preamble supported. */ IEEE80211_C_WME | /* WME/QoS */ IEEE80211_C_SHSLOT | /* Short slot time supported. */ IEEE80211_C_WPA; /* WPA/RSN. */ /* Check if HT support is present. */ if (sc->sc_ht) { device_printf(sc->sc_dev, "%s: enabling 11n\n", __func__); /* Enable basic HT */ ic->ic_htcaps = IEEE80211_HTC_HT | #if 0 IEEE80211_HTC_AMPDU | #endif IEEE80211_HTC_AMSDU | IEEE80211_HTCAP_MAXAMSDU_3839 | IEEE80211_HTCAP_SMPS_OFF; ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40; /* set number of spatial streams */ ic->ic_txstream = sc->sc_ntxstream; ic->ic_rxstream = sc->sc_nrxstream; } - /* Set supported .11b and .11g rates. */ - memset(bands, 0, sizeof(bands)); - setbit(bands, IEEE80211_MODE_11B); - setbit(bands, IEEE80211_MODE_11G); - if (sc->sc_ht) - setbit(bands, IEEE80211_MODE_11NG); - ieee80211_init_channels(ic, NULL, bands); + rsu_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); ieee80211_ifattach(ic); ic->ic_raw_xmit = rsu_raw_xmit; ic->ic_scan_start = rsu_scan_start; ic->ic_scan_end = rsu_scan_end; + ic->ic_getradiocaps = rsu_getradiocaps; ic->ic_set_channel = rsu_set_channel; ic->ic_vap_create = rsu_vap_create; ic->ic_vap_delete = rsu_vap_delete; ic->ic_update_mcast = rsu_update_mcast; ic->ic_parent = rsu_parent; ic->ic_transmit = rsu_transmit; ic->ic_send_mgmt = rsu_send_mgmt; ic->ic_update_chw = rsu_update_chw; ic->ic_ampdu_enable = rsu_ampdu_enable; ic->ic_wme.wme_update = rsu_wme_update; ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), RSU_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), RSU_RX_RADIOTAP_PRESENT); if (bootverbose) ieee80211_announce(ic); return (0); fail_rom: usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER); fail_usb: mtx_destroy(&sc->sc_mtx); return (ENXIO); } static int rsu_detach(device_t self) { struct rsu_softc *sc = device_get_softc(self); struct ieee80211com *ic = &sc->sc_ic; RSU_LOCK(sc); rsu_stop(sc); RSU_UNLOCK(sc); usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER); /* * Free buffers /before/ we detach from net80211, else node * references to destroyed vaps will lead to a panic. */ /* Free Tx/Rx buffers. */ RSU_LOCK(sc); rsu_free_tx_list(sc); rsu_free_rx_list(sc); RSU_UNLOCK(sc); /* Frames are freed; detach from net80211 */ ieee80211_ifdetach(ic); taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task); taskqueue_drain(taskqueue_thread, &sc->tx_task); mtx_destroy(&sc->sc_mtx); return (0); } static usb_error_t rsu_do_request(struct rsu_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; RSU_ASSERT_LOCKED(sc); while (ntries--) { err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 250 /* ms */); if (err == 0 || err == USB_ERR_NOT_CONFIGURED) break; DPRINTFN(1, "Control request failed, %s (retrying)\n", usbd_errstr(err)); rsu_ms_delay(sc, 10); } return (err); } static struct ieee80211vap * rsu_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 rsu_vap *uvp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return (NULL); uvp = malloc(sizeof(struct rsu_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &uvp->vap; if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid) != 0) { /* out of memory */ free(uvp, M_80211_VAP); return (NULL); } /* override state transition machine */ uvp->newstate = vap->iv_newstate; vap->iv_newstate = rsu_newstate; /* Limits from the r92su driver */ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16; vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_32K; /* complete setup */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return (vap); } static void rsu_vap_delete(struct ieee80211vap *vap) { struct rsu_vap *uvp = RSU_VAP(vap); ieee80211_vap_detach(vap); free(uvp, M_80211_VAP); } static void rsu_scan_start(struct ieee80211com *ic) { struct rsu_softc *sc = ic->ic_softc; int error; /* Scanning is done by the firmware. */ RSU_LOCK(sc); /* XXX TODO: force awake if in in network-sleep? */ error = rsu_site_survey(sc, TAILQ_FIRST(&ic->ic_vaps)); RSU_UNLOCK(sc); if (error != 0) device_printf(sc->sc_dev, "could not send site survey command\n"); } static void rsu_scan_end(struct ieee80211com *ic) { /* Nothing to do here. */ +} + +static void +rsu_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + struct rsu_softc *sc = ic->ic_softc; + uint8_t bands[IEEE80211_MODE_BYTES]; + + /* Set supported .11b and .11g rates. */ + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + if (sc->sc_ht) + setbit(bands, IEEE80211_MODE_11NG); + ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, + rsu_chan_2ghz, nitems(rsu_chan_2ghz), bands, 0); } static void rsu_set_channel(struct ieee80211com *ic __unused) { /* We are unable to switch channels, yet. */ } static void rsu_update_mcast(struct ieee80211com *ic) { /* XXX do nothing? */ } static int rsu_alloc_list(struct rsu_softc *sc, struct rsu_data data[], int ndata, int maxsz) { int i, error; for (i = 0; i < ndata; i++) { struct rsu_data *dp = &data[i]; dp->sc = sc; dp->m = NULL; dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); if (dp->buf == NULL) { device_printf(sc->sc_dev, "could not allocate buffer\n"); error = ENOMEM; goto fail; } dp->ni = NULL; } return (0); fail: rsu_free_list(sc, data, ndata); return (error); } static int rsu_alloc_rx_list(struct rsu_softc *sc) { int error, i; error = rsu_alloc_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT, RSU_RXBUFSZ); if (error != 0) return (error); STAILQ_INIT(&sc->sc_rx_active); STAILQ_INIT(&sc->sc_rx_inactive); for (i = 0; i < RSU_RX_LIST_COUNT; i++) STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); return (0); } static int rsu_alloc_tx_list(struct rsu_softc *sc) { int error, i; error = rsu_alloc_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT, RSU_TXBUFSZ); if (error != 0) return (error); STAILQ_INIT(&sc->sc_tx_inactive); for (i = 0; i != RSU_N_TRANSFER; i++) { STAILQ_INIT(&sc->sc_tx_active[i]); STAILQ_INIT(&sc->sc_tx_pending[i]); } for (i = 0; i < RSU_TX_LIST_COUNT; i++) { STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); } return (0); } static void rsu_free_tx_list(struct rsu_softc *sc) { int i; /* prevent further allocations from TX list(s) */ STAILQ_INIT(&sc->sc_tx_inactive); for (i = 0; i != RSU_N_TRANSFER; i++) { STAILQ_INIT(&sc->sc_tx_active[i]); STAILQ_INIT(&sc->sc_tx_pending[i]); } rsu_free_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT); } static void rsu_free_rx_list(struct rsu_softc *sc) { /* prevent further allocations from RX list(s) */ STAILQ_INIT(&sc->sc_rx_inactive); STAILQ_INIT(&sc->sc_rx_active); rsu_free_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT); } static void rsu_free_list(struct rsu_softc *sc, struct rsu_data data[], int ndata) { int i; for (i = 0; i < ndata; i++) { struct rsu_data *dp = &data[i]; if (dp->buf != NULL) { free(dp->buf, M_USBDEV); dp->buf = NULL; } if (dp->ni != NULL) { ieee80211_free_node(dp->ni); dp->ni = NULL; } } } static struct rsu_data * _rsu_getbuf(struct rsu_softc *sc) { struct rsu_data *bf; bf = STAILQ_FIRST(&sc->sc_tx_inactive); if (bf != NULL) STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); else bf = NULL; return (bf); } static struct rsu_data * rsu_getbuf(struct rsu_softc *sc) { struct rsu_data *bf; RSU_ASSERT_LOCKED(sc); bf = _rsu_getbuf(sc); if (bf == NULL) { RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: no buffers\n", __func__); } return (bf); } static void rsu_freebuf(struct rsu_softc *sc, struct rsu_data *bf) { RSU_ASSERT_LOCKED(sc); STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, bf, next); } static int rsu_write_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = R92S_REQ_REGS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); return (rsu_do_request(sc, &req, buf)); } static void rsu_write_1(struct rsu_softc *sc, uint16_t addr, uint8_t val) { rsu_write_region_1(sc, addr, &val, 1); } static void rsu_write_2(struct rsu_softc *sc, uint16_t addr, uint16_t val) { val = htole16(val); rsu_write_region_1(sc, addr, (uint8_t *)&val, 2); } static void rsu_write_4(struct rsu_softc *sc, uint16_t addr, uint32_t val) { val = htole32(val); rsu_write_region_1(sc, addr, (uint8_t *)&val, 4); } static int rsu_read_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf, int len) { usb_device_request_t req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = R92S_REQ_REGS; USETW(req.wValue, addr); USETW(req.wIndex, 0); USETW(req.wLength, len); return (rsu_do_request(sc, &req, buf)); } static uint8_t rsu_read_1(struct rsu_softc *sc, uint16_t addr) { uint8_t val; if (rsu_read_region_1(sc, addr, &val, 1) != 0) return (0xff); return (val); } static uint16_t rsu_read_2(struct rsu_softc *sc, uint16_t addr) { uint16_t val; if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0) return (0xffff); return (le16toh(val)); } static uint32_t rsu_read_4(struct rsu_softc *sc, uint16_t addr) { uint32_t val; if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0) return (0xffffffff); return (le32toh(val)); } static int rsu_fw_iocmd(struct rsu_softc *sc, uint32_t iocmd) { int ntries; rsu_write_4(sc, R92S_IOCMD_CTRL, iocmd); rsu_ms_delay(sc, 1); for (ntries = 0; ntries < 50; ntries++) { if (rsu_read_4(sc, R92S_IOCMD_CTRL) == 0) return (0); rsu_ms_delay(sc, 1); } return (ETIMEDOUT); } static uint8_t rsu_efuse_read_1(struct rsu_softc *sc, uint16_t addr) { uint32_t reg; int ntries; reg = rsu_read_4(sc, R92S_EFUSE_CTRL); reg = RW(reg, R92S_EFUSE_CTRL_ADDR, addr); reg &= ~R92S_EFUSE_CTRL_VALID; rsu_write_4(sc, R92S_EFUSE_CTRL, reg); /* Wait for read operation to complete. */ for (ntries = 0; ntries < 100; ntries++) { reg = rsu_read_4(sc, R92S_EFUSE_CTRL); if (reg & R92S_EFUSE_CTRL_VALID) return (MS(reg, R92S_EFUSE_CTRL_DATA)); rsu_ms_delay(sc, 1); } device_printf(sc->sc_dev, "could not read efuse byte at address 0x%x\n", addr); return (0xff); } static int rsu_read_rom(struct rsu_softc *sc) { uint8_t *rom = sc->rom; uint16_t addr = 0; uint32_t reg; uint8_t off, msk; int i; /* Make sure that ROM type is eFuse and that autoload succeeded. */ reg = rsu_read_1(sc, R92S_EE_9346CR); if ((reg & (R92S_9356SEL | R92S_EEPROM_EN)) != R92S_EEPROM_EN) return (EIO); /* Turn on 2.5V to prevent eFuse leakage. */ reg = rsu_read_1(sc, R92S_EFUSE_TEST + 3); rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg | 0x80); rsu_ms_delay(sc, 1); rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg & ~0x80); /* Read full ROM image. */ memset(&sc->rom, 0xff, sizeof(sc->rom)); while (addr < 512) { reg = rsu_efuse_read_1(sc, addr); if (reg == 0xff) break; addr++; off = reg >> 4; msk = reg & 0xf; for (i = 0; i < 4; i++) { if (msk & (1 << i)) continue; rom[off * 8 + i * 2 + 0] = rsu_efuse_read_1(sc, addr); addr++; rom[off * 8 + i * 2 + 1] = rsu_efuse_read_1(sc, addr); addr++; } } #ifdef USB_DEBUG if (rsu_debug >= 5) { /* Dump ROM content. */ printf("\n"); for (i = 0; i < sizeof(sc->rom); i++) printf("%02x:", rom[i]); printf("\n"); } #endif return (0); } static int rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len) { const uint8_t which = RSU_H2C_ENDPOINT; struct rsu_data *data; struct r92s_tx_desc *txd; struct r92s_fw_cmd_hdr *cmd; int cmdsz; int xferlen; RSU_ASSERT_LOCKED(sc); data = rsu_getbuf(sc); if (data == NULL) return (ENOMEM); /* Blank the entire payload, just to be safe */ memset(data->buf, '\0', RSU_TXBUFSZ); /* Round-up command length to a multiple of 8 bytes. */ /* XXX TODO: is this required? */ cmdsz = (len + 7) & ~7; xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz; KASSERT(xferlen <= RSU_TXBUFSZ, ("%s: invalid length", __func__)); memset(data->buf, 0, xferlen); /* Setup Tx descriptor. */ txd = (struct r92s_tx_desc *)data->buf; txd->txdw0 = htole32( SM(R92S_TXDW0_OFFSET, sizeof(*txd)) | SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) | R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG); txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C)); /* Setup command header. */ cmd = (struct r92s_fw_cmd_hdr *)&txd[1]; cmd->len = htole16(cmdsz); cmd->code = code; cmd->seq = sc->cmd_seq; sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f; /* Copy command payload. */ memcpy(&cmd[1], buf, len); RSU_DPRINTF(sc, RSU_DEBUG_TX | RSU_DEBUG_FWCMD, "%s: Tx cmd code=0x%x len=0x%x\n", __func__, code, cmdsz); data->buflen = xferlen; STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); usbd_transfer_start(sc->sc_xfer[which]); return (0); } /* ARGSUSED */ static void rsu_calib_task(void *arg, int pending __unused) { struct rsu_softc *sc = arg; #ifdef notyet uint32_t reg; #endif RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "%s: running calibration task\n", __func__); RSU_LOCK(sc); #ifdef notyet /* Read WPS PBC status. */ rsu_write_1(sc, R92S_MAC_PINMUX_CTRL, R92S_GPIOMUX_EN | SM(R92S_GPIOSEL_GPIO, R92S_GPIOSEL_GPIO_JTAG)); rsu_write_1(sc, R92S_GPIO_IO_SEL, rsu_read_1(sc, R92S_GPIO_IO_SEL) & ~R92S_GPIO_WPS); reg = rsu_read_1(sc, R92S_GPIO_CTRL); if (reg != 0xff && (reg & R92S_GPIO_WPS)) DPRINTF(("WPS PBC is pushed\n")); #endif /* Read current signal level. */ if (rsu_fw_iocmd(sc, 0xf4000001) == 0) { sc->sc_currssi = rsu_read_4(sc, R92S_IOCMD_DATA); RSU_DPRINTF(sc, RSU_DEBUG_CALIB, "%s: RSSI=%d (%d)\n", __func__, sc->sc_currssi, rsu_hwrssi_to_rssi(sc, sc->sc_currssi)); } if (sc->sc_calibrating) taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz); RSU_UNLOCK(sc); } static void rsu_tx_task(void *arg, int pending __unused) { struct rsu_softc *sc = arg; RSU_LOCK(sc); _rsu_start(sc); RSU_UNLOCK(sc); } #define RSU_PWR_UNKNOWN 0x0 #define RSU_PWR_ACTIVE 0x1 #define RSU_PWR_OFF 0x2 #define RSU_PWR_SLEEP 0x3 /* * Set the current power state. * * The rtlwifi code doesn't do this so aggressively; it * waits for an idle period after association with * no traffic before doing this. * * For now - it's on in all states except RUN, and * in RUN it'll transition to allow sleep. */ struct r92s_pwr_cmd { uint8_t mode; uint8_t smart_ps; uint8_t bcn_pass_time; }; static int rsu_set_fw_power_state(struct rsu_softc *sc, int state) { struct r92s_set_pwr_mode cmd; //struct r92s_pwr_cmd cmd; int error; RSU_ASSERT_LOCKED(sc); /* only change state if required */ if (sc->sc_curpwrstate == state) return (0); memset(&cmd, 0, sizeof(cmd)); switch (state) { case RSU_PWR_ACTIVE: /* Force the hardware awake */ rsu_write_1(sc, R92S_USB_HRPWM, R92S_USB_HRPWM_PS_ST_ACTIVE | R92S_USB_HRPWM_PS_ALL_ON); cmd.mode = R92S_PS_MODE_ACTIVE; break; case RSU_PWR_SLEEP: cmd.mode = R92S_PS_MODE_DTIM; /* XXX configurable? */ cmd.smart_ps = 1; /* XXX 2 if doing p2p */ cmd.bcn_pass_time = 5; /* in 100mS usb.c, linux/rtlwifi */ break; case RSU_PWR_OFF: cmd.mode = R92S_PS_MODE_RADIOOFF; break; default: device_printf(sc->sc_dev, "%s: unknown ps mode (%d)\n", __func__, state); return (ENXIO); } RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting ps mode to %d (mode %d)\n", __func__, state, cmd.mode); error = rsu_fw_cmd(sc, R92S_CMD_SET_PWR_MODE, &cmd, sizeof(cmd)); if (error == 0) sc->sc_curpwrstate = state; return (error); } static int rsu_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct rsu_vap *uvp = RSU_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct rsu_softc *sc = ic->ic_softc; struct ieee80211_node *ni; struct ieee80211_rateset *rs; enum ieee80211_state ostate; int error, startcal = 0; ostate = vap->iv_state; RSU_DPRINTF(sc, RSU_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); if (ostate == IEEE80211_S_RUN) { RSU_LOCK(sc); /* Stop calibration. */ sc->sc_calibrating = 0; RSU_UNLOCK(sc); taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task); taskqueue_drain(taskqueue_thread, &sc->tx_task); /* Disassociate from our current BSS. */ RSU_LOCK(sc); rsu_disconnect(sc); } else RSU_LOCK(sc); switch (nstate) { case IEEE80211_S_INIT: (void) rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE); break; case IEEE80211_S_AUTH: ni = ieee80211_ref_node(vap->iv_bss); (void) rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE); error = rsu_join_bss(sc, ni); ieee80211_free_node(ni); if (error != 0) { device_printf(sc->sc_dev, "could not send join command\n"); } break; case IEEE80211_S_RUN: ni = ieee80211_ref_node(vap->iv_bss); rs = &ni->ni_rates; /* Indicate highest supported rate. */ ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1]; (void) rsu_set_fw_power_state(sc, RSU_PWR_SLEEP); ieee80211_free_node(ni); startcal = 1; break; default: break; } sc->sc_calibrating = 1; /* Start periodic calibration. */ taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz); RSU_UNLOCK(sc); IEEE80211_LOCK(ic); return (uvp->newstate(vap, nstate, arg)); } #ifdef notyet static void rsu_set_key(struct rsu_softc *sc, const struct ieee80211_key *k) { struct r92s_fw_cmd_set_key key; memset(&key, 0, sizeof(key)); /* Map net80211 cipher to HW crypto algorithm. */ switch (k->wk_cipher->ic_cipher) { case IEEE80211_CIPHER_WEP: if (k->wk_keylen < 8) key.algo = R92S_KEY_ALGO_WEP40; else key.algo = R92S_KEY_ALGO_WEP104; break; case IEEE80211_CIPHER_TKIP: key.algo = R92S_KEY_ALGO_TKIP; break; case IEEE80211_CIPHER_AES_CCM: key.algo = R92S_KEY_ALGO_AES; break; default: return; } key.id = k->wk_keyix; key.grpkey = (k->wk_flags & IEEE80211_KEY_GROUP) != 0; memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key))); (void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key)); } static void rsu_delete_key(struct rsu_softc *sc, const struct ieee80211_key *k) { struct r92s_fw_cmd_set_key key; memset(&key, 0, sizeof(key)); key.id = k->wk_keyix; (void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key)); } #endif static int rsu_site_survey(struct rsu_softc *sc, struct ieee80211vap *vap) { struct r92s_fw_cmd_sitesurvey cmd; struct ieee80211com *ic = &sc->sc_ic; int r; RSU_ASSERT_LOCKED(sc); memset(&cmd, 0, sizeof(cmd)); if ((ic->ic_flags & IEEE80211_F_ASCAN) || sc->sc_scan_pass == 1) cmd.active = htole32(1); cmd.limit = htole32(48); if (sc->sc_scan_pass == 1 && vap->iv_des_nssid > 0) { /* Do a directed scan for second pass. */ cmd.ssidlen = htole32(vap->iv_des_ssid[0].len); memcpy(cmd.ssid, vap->iv_des_ssid[0].ssid, vap->iv_des_ssid[0].len); } DPRINTF("sending site survey command, pass=%d\n", sc->sc_scan_pass); r = rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd)); if (r == 0) { sc->sc_scanning = 1; } return (r); } static int rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ndis_wlan_bssid_ex *bss; struct ndis_802_11_fixed_ies *fixed; struct r92s_fw_cmd_auth auth; uint8_t buf[sizeof(*bss) + 128] __aligned(4); uint8_t *frm; uint8_t opmode; int error; int cnt; char *msg = "rsujoin"; RSU_ASSERT_LOCKED(sc); /* * Until net80211 scanning doesn't automatically finish * before we tell it to, let's just wait until any pending * scan is done. * * XXX TODO: yes, this releases and re-acquires the lock. * We should re-verify the state whenever we re-attempt this! */ cnt = 0; while (sc->sc_scanning && cnt < 10) { device_printf(sc->sc_dev, "%s: still scanning! (attempt %d)\n", __func__, cnt); msleep(msg, &sc->sc_mtx, 0, msg, hz / 2); cnt++; } /* Let the FW decide the opmode based on the capinfo field. */ opmode = NDIS802_11AUTOUNKNOWN; RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting operating mode to %d\n", __func__, opmode); error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode)); if (error != 0) return (error); memset(&auth, 0, sizeof(auth)); if (vap->iv_flags & IEEE80211_F_WPA) { auth.mode = R92S_AUTHMODE_WPA; auth.dot1x = (ni->ni_authmode == IEEE80211_AUTH_8021X); } else auth.mode = R92S_AUTHMODE_OPEN; RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting auth mode to %d\n", __func__, auth.mode); error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth)); if (error != 0) return (error); memset(buf, 0, sizeof(buf)); bss = (struct ndis_wlan_bssid_ex *)buf; IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid); bss->ssid.ssidlen = htole32(ni->ni_esslen); memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen); if (vap->iv_flags & (IEEE80211_F_PRIVACY | IEEE80211_F_WPA)) bss->privacy = htole32(1); bss->rssi = htole32(ni->ni_avgrssi); if (ic->ic_curmode == IEEE80211_MODE_11B) bss->networktype = htole32(NDIS802_11DS); else bss->networktype = htole32(NDIS802_11OFDM24); bss->config.len = htole32(sizeof(bss->config)); bss->config.bintval = htole32(ni->ni_intval); bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan)); bss->inframode = htole32(NDIS802_11INFRASTRUCTURE); /* XXX verify how this is supposed to look! */ memcpy(bss->supprates, ni->ni_rates.rs_rates, ni->ni_rates.rs_nrates); /* Write the fixed fields of the beacon frame. */ fixed = (struct ndis_802_11_fixed_ies *)&bss[1]; memcpy(&fixed->tstamp, ni->ni_tstamp.data, 8); fixed->bintval = htole16(ni->ni_intval); fixed->capabilities = htole16(ni->ni_capinfo); /* Write IEs to be included in the association request. */ frm = (uint8_t *)&fixed[1]; frm = ieee80211_add_rsn(frm, vap); frm = ieee80211_add_wpa(frm, vap); frm = ieee80211_add_qos(frm, ni); if ((ic->ic_flags & IEEE80211_F_WME) && (ni->ni_ies.wme_ie != NULL)) frm = ieee80211_add_wme_info(frm, &ic->ic_wme); if (ni->ni_flags & IEEE80211_NODE_HT) { frm = ieee80211_add_htcap(frm, ni); frm = ieee80211_add_htinfo(frm, ni); } bss->ieslen = htole32(frm - (uint8_t *)fixed); bss->len = htole32(((frm - buf) + 3) & ~3); RSU_DPRINTF(sc, RSU_DEBUG_RESET | RSU_DEBUG_FWCMD, "%s: sending join bss command to %s chan %d\n", __func__, ether_sprintf(bss->macaddr), le32toh(bss->config.dsconfig)); return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf))); } static int rsu_disconnect(struct rsu_softc *sc) { uint32_t zero = 0; /* :-) */ /* Disassociate from our current BSS. */ RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD, "%s: sending disconnect command\n", __func__); return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero))); } /* * Map the hardware provided RSSI value to a signal level. * For the most part it's just something we divide by and cap * so it doesn't overflow the representation by net80211. */ static int rsu_hwrssi_to_rssi(struct rsu_softc *sc, int hw_rssi) { int v; if (hw_rssi == 0) return (0); v = hw_rssi >> 4; if (v > 80) v = 80; return (v); } static void rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ndis_wlan_bssid_ex *bss; struct ieee80211_rx_stats rxs; struct mbuf *m; int pktlen; if (__predict_false(len < sizeof(*bss))) return; bss = (struct ndis_wlan_bssid_ex *)buf; if (__predict_false(len < sizeof(*bss) + le32toh(bss->ieslen))) return; RSU_DPRINTF(sc, RSU_DEBUG_SCAN, "%s: found BSS %s: len=%d chan=%d inframode=%d " "networktype=%d privacy=%d, RSSI=%d\n", __func__, ether_sprintf(bss->macaddr), le32toh(bss->len), le32toh(bss->config.dsconfig), le32toh(bss->inframode), le32toh(bss->networktype), le32toh(bss->privacy), le32toh(bss->rssi)); /* Build a fake beacon frame to let net80211 do all the parsing. */ /* XXX TODO: just call the new scan API methods! */ pktlen = sizeof(*wh) + le32toh(bss->ieslen); if (__predict_false(pktlen > MCLBYTES)) return; m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m == NULL)) return; wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; USETW(wh->i_dur, 0); IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr); IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr); IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr); *(uint16_t *)wh->i_seq = 0; memcpy(&wh[1], (uint8_t *)&bss[1], le32toh(bss->ieslen)); /* Finalize mbuf. */ m->m_pkthdr.len = m->m_len = pktlen; /* Set channel flags for input path */ bzero(&rxs, sizeof(rxs)); rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; rxs.c_ieee = le32toh(bss->config.dsconfig); rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ); /* This is a number from 0..100; so let's just divide it down a bit */ rxs.rssi = le32toh(bss->rssi) / 2; rxs.nf = -96; /* XXX avoid a LOR */ RSU_UNLOCK(sc); ieee80211_input_mimo_all(ic, m, &rxs); RSU_LOCK(sc); } static void rsu_event_join_bss(struct rsu_softc *sc, uint8_t *buf, int len) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_node *ni = vap->iv_bss; struct r92s_event_join_bss *rsp; uint32_t tmp; int res; if (__predict_false(len < sizeof(*rsp))) return; rsp = (struct r92s_event_join_bss *)buf; res = (int)le32toh(rsp->join_res); RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD, "%s: Rx join BSS event len=%d res=%d\n", __func__, len, res); /* * XXX Don't do this; there's likely a better way to tell * the caller we failed. */ if (res <= 0) { RSU_UNLOCK(sc); ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); RSU_LOCK(sc); return; } tmp = le32toh(rsp->associd); if (tmp >= vap->iv_max_aid) { DPRINTF("Assoc ID overflow\n"); tmp = 1; } RSU_DPRINTF(sc, RSU_DEBUG_STATE | RSU_DEBUG_FWCMD, "%s: associated with %s associd=%d\n", __func__, ether_sprintf(rsp->bss.macaddr), tmp); /* XXX is this required? What's the top two bits for again? */ ni->ni_associd = tmp | 0xc000; RSU_UNLOCK(sc); ieee80211_new_state(vap, IEEE80211_S_RUN, IEEE80211_FC0_SUBTYPE_ASSOC_RESP); RSU_LOCK(sc); } static void rsu_event_addba_req_report(struct rsu_softc *sc, uint8_t *buf, int len) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct r92s_add_ba_event *ba = (void *) buf; struct ieee80211_node *ni; if (len < sizeof(*ba)) { device_printf(sc->sc_dev, "%s: short read (%d)\n", __func__, len); return; } if (vap == NULL) return; RSU_DPRINTF(sc, RSU_DEBUG_AMPDU, "%s: mac=%s, tid=%d, ssn=%d\n", __func__, ether_sprintf(ba->mac_addr), (int) ba->tid, (int) le16toh(ba->ssn)); /* XXX do node lookup; this is STA specific */ ni = ieee80211_ref_node(vap->iv_bss); ieee80211_ampdu_rx_start_ext(ni, ba->tid, le16toh(ba->ssn) >> 4, 32); ieee80211_free_node(ni); } static void rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); RSU_DPRINTF(sc, RSU_DEBUG_RX | RSU_DEBUG_FWCMD, "%s: Rx event code=%d len=%d\n", __func__, code, len); switch (code) { case R92S_EVT_SURVEY: rsu_event_survey(sc, buf, len); break; case R92S_EVT_SURVEY_DONE: RSU_DPRINTF(sc, RSU_DEBUG_SCAN, "%s: site survey pass %d done, found %d BSS\n", __func__, sc->sc_scan_pass, le32toh(*(uint32_t *)buf)); sc->sc_scanning = 0; if (vap->iv_state != IEEE80211_S_SCAN) break; /* Ignore if not scanning. */ /* * XXX TODO: This needs to be done without a transition to * the SCAN state again. Grr. */ if (sc->sc_scan_pass == 0 && vap->iv_des_nssid != 0) { /* Schedule a directed scan for hidden APs. */ /* XXX bad! */ sc->sc_scan_pass = 1; RSU_UNLOCK(sc); ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); RSU_LOCK(sc); break; } sc->sc_scan_pass = 0; break; case R92S_EVT_JOIN_BSS: if (vap->iv_state == IEEE80211_S_AUTH) rsu_event_join_bss(sc, buf, len); break; case R92S_EVT_DEL_STA: RSU_DPRINTF(sc, RSU_DEBUG_FWCMD | RSU_DEBUG_STATE, "%s: disassociated from %s\n", __func__, ether_sprintf(buf)); if (vap->iv_state == IEEE80211_S_RUN && IEEE80211_ADDR_EQ(vap->iv_bss->ni_bssid, buf)) { RSU_UNLOCK(sc); ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); RSU_LOCK(sc); } break; case R92S_EVT_WPS_PBC: RSU_DPRINTF(sc, RSU_DEBUG_RX | RSU_DEBUG_FWCMD, "%s: WPS PBC pushed.\n", __func__); break; case R92S_EVT_FWDBG: buf[60] = '\0'; RSU_DPRINTF(sc, RSU_DEBUG_FWDBG, "FWDBG: %s\n", (char *)buf); break; case R92S_EVT_ADDBA_REQ_REPORT: rsu_event_addba_req_report(sc, buf, len); break; default: device_printf(sc->sc_dev, "%s: unhandled code (%d)\n", __func__, code); break; } } static void rsu_rx_multi_event(struct rsu_softc *sc, uint8_t *buf, int len) { struct r92s_fw_cmd_hdr *cmd; int cmdsz; RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: Rx events len=%d\n", __func__, len); /* Skip Rx status. */ buf += sizeof(struct r92s_rx_stat); len -= sizeof(struct r92s_rx_stat); /* Process all events. */ for (;;) { /* Check that command header fits. */ if (__predict_false(len < sizeof(*cmd))) break; cmd = (struct r92s_fw_cmd_hdr *)buf; /* Check that command payload fits. */ cmdsz = le16toh(cmd->len); if (__predict_false(len < sizeof(*cmd) + cmdsz)) break; /* Process firmware event. */ rsu_rx_event(sc, cmd->code, (uint8_t *)&cmd[1], cmdsz); if (!(cmd->seq & R92S_FW_CMD_MORE)) break; buf += sizeof(*cmd) + cmdsz; len -= sizeof(*cmd) + cmdsz; } } #if 0 static int8_t rsu_get_rssi(struct rsu_softc *sc, int rate, void *physt) { static const int8_t cckoff[] = { 14, -2, -20, -40 }; struct r92s_rx_phystat *phy; struct r92s_rx_cck *cck; uint8_t rpt; int8_t rssi; if (rate <= 3) { cck = (struct r92s_rx_cck *)physt; rpt = (cck->agc_rpt >> 6) & 0x3; rssi = cck->agc_rpt & 0x3e; rssi = cckoff[rpt] - rssi; } else { /* OFDM/HT. */ phy = (struct r92s_rx_phystat *)physt; rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 106; } return (rssi); } #endif static struct mbuf * rsu_rx_frame(struct rsu_softc *sc, uint8_t *buf, int pktlen) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct r92s_rx_stat *stat; uint32_t rxdw0, rxdw3; struct mbuf *m; uint8_t rate; int infosz; stat = (struct r92s_rx_stat *)buf; rxdw0 = le32toh(stat->rxdw0); rxdw3 = le32toh(stat->rxdw3); if (__predict_false(rxdw0 & R92S_RXDW0_CRCERR)) { counter_u64_add(ic->ic_ierrors, 1); return NULL; } if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) { counter_u64_add(ic->ic_ierrors, 1); return NULL; } rate = MS(rxdw3, R92S_RXDW3_RATE); infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8; #if 0 /* Get RSSI from PHY status descriptor if present. */ if (infosz != 0) *rssi = rsu_get_rssi(sc, rate, &stat[1]); else *rssi = 0; #endif RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: Rx frame len=%d rate=%d infosz=%d\n", __func__, pktlen, rate, infosz); m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m == NULL)) { counter_u64_add(ic->ic_ierrors, 1); return NULL; } /* Hardware does Rx TCP checksum offload. */ if (rxdw3 & R92S_RXDW3_TCPCHKVALID) { if (__predict_true(rxdw3 & R92S_RXDW3_TCPCHKRPT)) m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; } wh = (struct ieee80211_frame *)((uint8_t *)&stat[1] + infosz); memcpy(mtod(m, uint8_t *), wh, pktlen); m->m_pkthdr.len = m->m_len = pktlen; if (ieee80211_radiotap_active(ic)) { struct rsu_rx_radiotap_header *tap = &sc->sc_rxtap; /* Map HW rate index to 802.11 rate. */ tap->wr_flags = 2; if (!(rxdw3 & R92S_RXDW3_HTC)) { switch (rate) { /* CCK. */ case 0: tap->wr_rate = 2; break; case 1: tap->wr_rate = 4; break; case 2: tap->wr_rate = 11; break; case 3: tap->wr_rate = 22; break; /* OFDM. */ case 4: tap->wr_rate = 12; break; case 5: tap->wr_rate = 18; break; case 6: tap->wr_rate = 24; break; case 7: tap->wr_rate = 36; break; case 8: tap->wr_rate = 48; break; case 9: tap->wr_rate = 72; break; case 10: tap->wr_rate = 96; break; case 11: tap->wr_rate = 108; break; } } else if (rate >= 12) { /* MCS0~15. */ /* Bit 7 set means HT MCS instead of rate. */ tap->wr_rate = 0x80 | (rate - 12); } #if 0 tap->wr_dbm_antsignal = *rssi; #endif /* XXX not nice */ tap->wr_dbm_antsignal = rsu_hwrssi_to_rssi(sc, sc->sc_currssi); tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); } return (m); } static struct mbuf * rsu_rx_multi_frame(struct rsu_softc *sc, uint8_t *buf, int len) { struct r92s_rx_stat *stat; uint32_t rxdw0; int totlen, pktlen, infosz, npkts; struct mbuf *m, *m0 = NULL, *prevm = NULL; /* Get the number of encapsulated frames. */ stat = (struct r92s_rx_stat *)buf; npkts = MS(le32toh(stat->rxdw2), R92S_RXDW2_PKTCNT); RSU_DPRINTF(sc, RSU_DEBUG_RX, "%s: Rx %d frames in one chunk\n", __func__, npkts); /* Process all of them. */ while (npkts-- > 0) { if (__predict_false(len < sizeof(*stat))) break; stat = (struct r92s_rx_stat *)buf; rxdw0 = le32toh(stat->rxdw0); pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN); if (__predict_false(pktlen == 0)) break; infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8; /* Make sure everything fits in xfer. */ totlen = sizeof(*stat) + infosz + pktlen; if (__predict_false(totlen > len)) break; /* Process 802.11 frame. */ m = rsu_rx_frame(sc, buf, pktlen); if (m0 == NULL) m0 = m; if (prevm == NULL) prevm = m; else { prevm->m_next = m; prevm = m; } /* Next chunk is 128-byte aligned. */ totlen = (totlen + 127) & ~127; buf += totlen; len -= totlen; } return (m0); } static struct mbuf * rsu_rxeof(struct usb_xfer *xfer, struct rsu_data *data) { struct rsu_softc *sc = data->sc; struct ieee80211com *ic = &sc->sc_ic; struct r92s_rx_stat *stat; int len; usbd_xfer_status(xfer, &len, NULL, NULL, NULL); if (__predict_false(len < sizeof(*stat))) { DPRINTF("xfer too short %d\n", len); counter_u64_add(ic->ic_ierrors, 1); return (NULL); } /* Determine if it is a firmware C2H event or an 802.11 frame. */ stat = (struct r92s_rx_stat *)data->buf; if ((le32toh(stat->rxdw1) & 0x1ff) == 0x1ff) { rsu_rx_multi_event(sc, data->buf, len); /* No packets to process. */ return (NULL); } else return (rsu_rx_multi_frame(sc, data->buf, len)); } static void rsu_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct rsu_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *m = NULL, *next; struct rsu_data *data; RSU_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_rx_active); if (data == NULL) goto tr_setup; STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); m = rsu_rxeof(xfer, data); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: /* * XXX TODO: if we have an mbuf list, but then * we hit data == NULL, what now? */ data = STAILQ_FIRST(&sc->sc_rx_inactive); if (data == NULL) { KASSERT(m == NULL, ("mbuf isn't NULL")); return; } STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); /* * To avoid LOR we should unlock our private mutex here to call * ieee80211_input() because here is at the end of a USB * callback and safe to unlock. */ RSU_UNLOCK(sc); while (m != NULL) { int rssi; /* Cheat and get the last calibrated RSSI */ rssi = rsu_hwrssi_to_rssi(sc, sc->sc_currssi); next = m->m_next; m->m_next = NULL; wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); if (ni != NULL) { if (ni->ni_flags & IEEE80211_NODE_HT) m->m_flags |= M_AMPDU; (void)ieee80211_input(ni, m, rssi, -96); ieee80211_free_node(ni); } else (void)ieee80211_input_all(ic, m, rssi, -96); m = next; } RSU_LOCK(sc); break; default: /* needs it to the inactive queue due to a error. */ data = STAILQ_FIRST(&sc->sc_rx_active); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); counter_u64_add(ic->ic_ierrors, 1); goto tr_setup; } break; } } static void rsu_txeof(struct usb_xfer *xfer, struct rsu_data *data) { #ifdef USB_DEBUG struct rsu_softc *sc = usbd_xfer_softc(xfer); #endif RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: called; data=%p\n", __func__, data); if (data->m) { /* XXX status? */ ieee80211_tx_complete(data->ni, data->m, 0); data->m = NULL; data->ni = NULL; } } static void rsu_bulk_tx_callback_sub(struct usb_xfer *xfer, usb_error_t error, uint8_t which) { struct rsu_softc *sc = usbd_xfer_softc(xfer); struct ieee80211com *ic = &sc->sc_ic; struct rsu_data *data; RSU_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: data = STAILQ_FIRST(&sc->sc_tx_active[which]); if (data == NULL) goto tr_setup; RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: transfer done %p\n", __func__, data); STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); rsu_txeof(xfer, data); rsu_freebuf(sc, data); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: data = STAILQ_FIRST(&sc->sc_tx_pending[which]); if (data == NULL) { RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: empty pending queue sc %p\n", __func__, sc); return; } STAILQ_REMOVE_HEAD(&sc->sc_tx_pending[which], next); STAILQ_INSERT_TAIL(&sc->sc_tx_active[which], data, next); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); RSU_DPRINTF(sc, RSU_DEBUG_TXDONE, "%s: submitting transfer %p\n", __func__, data); usbd_transfer_submit(xfer); break; default: data = STAILQ_FIRST(&sc->sc_tx_active[which]); if (data != NULL) { STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); rsu_txeof(xfer, data); rsu_freebuf(sc, data); } counter_u64_add(ic->ic_oerrors, 1); if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); goto tr_setup; } break; } /* * XXX TODO: if the queue is low, flush out FF TX frames. * Remember to unlock the driver for now; net80211 doesn't * defer it for us. */ } static void rsu_bulk_tx_callback_be_bk(struct usb_xfer *xfer, usb_error_t error) { struct rsu_softc *sc = usbd_xfer_softc(xfer); rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_BE_BK); /* This kicks the TX taskqueue */ rsu_start(sc); } static void rsu_bulk_tx_callback_vi_vo(struct usb_xfer *xfer, usb_error_t error) { struct rsu_softc *sc = usbd_xfer_softc(xfer); rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_VI_VO); /* This kicks the TX taskqueue */ rsu_start(sc); } static void rsu_bulk_tx_callback_h2c(struct usb_xfer *xfer, usb_error_t error) { struct rsu_softc *sc = usbd_xfer_softc(xfer); rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_H2C); /* This kicks the TX taskqueue */ rsu_start(sc); } /* * Transmit the given frame. * * This doesn't free the node or mbuf upon failure. */ static int rsu_tx_start(struct rsu_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, struct rsu_data *data) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; struct r92s_tx_desc *txd; uint8_t type; int prio = 0; uint8_t which; int hasqos; int xferlen; int qid; RSU_ASSERT_LOCKED(sc); wh = mtod(m0, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: data=%p, m=%p\n", __func__, data, m0); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { device_printf(sc->sc_dev, "ieee80211_crypto_encap returns NULL.\n"); /* XXX we don't expect the fragmented frames */ return (ENOBUFS); } wh = mtod(m0, struct ieee80211_frame *); } /* If we have QoS then use it */ /* XXX TODO: mbuf WME/PRI versus TID? */ if (IEEE80211_QOS_HAS_SEQ(wh)) { /* Has QoS */ prio = M_WME_GETAC(m0); which = rsu_wme_ac_xfer_map[prio]; hasqos = 1; } else { /* Non-QoS TID */ /* XXX TODO: tid=0 for non-qos TID? */ which = rsu_wme_ac_xfer_map[WME_AC_BE]; hasqos = 0; prio = 0; } qid = rsu_ac2qid[prio]; #if 0 switch (type) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: which = rsu_wme_ac_xfer_map[WME_AC_VO]; break; default: which = rsu_wme_ac_xfer_map[M_WME_GETAC(m0)]; break; } hasqos = 0; #endif RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: pri=%d, which=%d, hasqos=%d\n", __func__, prio, which, hasqos); /* Fill Tx descriptor. */ txd = (struct r92s_tx_desc *)data->buf; memset(txd, 0, sizeof(*txd)); txd->txdw0 |= htole32( SM(R92S_TXDW0_PKTLEN, m0->m_pkthdr.len) | SM(R92S_TXDW0_OFFSET, sizeof(*txd)) | R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG); txd->txdw1 |= htole32( SM(R92S_TXDW1_MACID, R92S_MACID_BSS) | SM(R92S_TXDW1_QSEL, qid)); if (!hasqos) txd->txdw1 |= htole32(R92S_TXDW1_NONQOS); #ifdef notyet if (k != NULL) { switch (k->wk_cipher->ic_cipher) { case IEEE80211_CIPHER_WEP: cipher = R92S_TXDW1_CIPHER_WEP; break; case IEEE80211_CIPHER_TKIP: cipher = R92S_TXDW1_CIPHER_TKIP; break; case IEEE80211_CIPHER_AES_CCM: cipher = R92S_TXDW1_CIPHER_AES; break; default: cipher = R92S_TXDW1_CIPHER_NONE; } txd->txdw1 |= htole32( SM(R92S_TXDW1_CIPHER, cipher) | SM(R92S_TXDW1_KEYIDX, k->k_id)); } #endif /* XXX todo: set AGGEN bit if appropriate? */ txd->txdw2 |= htole32(R92S_TXDW2_BK); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txd->txdw2 |= htole32(R92S_TXDW2_BMCAST); /* * Firmware will use and increment the sequence number for the * specified priority. */ txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, prio)); if (ieee80211_radiotap_active_vap(vap)) { struct rsu_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); ieee80211_radiotap_tx(vap, m0); } xferlen = sizeof(*txd) + m0->m_pkthdr.len; m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&txd[1]); data->buflen = xferlen; data->ni = ni; data->m = m0; STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); /* start transfer, if any */ usbd_transfer_start(sc->sc_xfer[which]); return (0); } static int rsu_transmit(struct ieee80211com *ic, struct mbuf *m) { struct rsu_softc *sc = ic->ic_softc; int error; RSU_LOCK(sc); if (!sc->sc_running) { RSU_UNLOCK(sc); return (ENXIO); } /* * XXX TODO: ensure that we treat 'm' as a list of frames * to transmit! */ error = mbufq_enqueue(&sc->sc_snd, m); if (error) { RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: mbufq_enable: failed (%d)\n", __func__, error); RSU_UNLOCK(sc); return (error); } RSU_UNLOCK(sc); /* This kicks the TX taskqueue */ rsu_start(sc); return (0); } static void rsu_drain_mbufq(struct rsu_softc *sc) { struct mbuf *m; struct ieee80211_node *ni; RSU_ASSERT_LOCKED(sc); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; ieee80211_free_node(ni); m_freem(m); } } static void _rsu_start(struct rsu_softc *sc) { struct ieee80211_node *ni; struct rsu_data *bf; struct mbuf *m; RSU_ASSERT_LOCKED(sc); while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { bf = rsu_getbuf(sc); if (bf == NULL) { RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: failed to get buffer\n", __func__); mbufq_prepend(&sc->sc_snd, m); break; } ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; m->m_pkthdr.rcvif = NULL; if (rsu_tx_start(sc, ni, m, bf) != 0) { RSU_DPRINTF(sc, RSU_DEBUG_TX, "%s: failed to transmit\n", __func__); if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); rsu_freebuf(sc, bf); ieee80211_free_node(ni); m_freem(m); break; } } } static void rsu_start(struct rsu_softc *sc) { taskqueue_enqueue(taskqueue_thread, &sc->tx_task); } static void rsu_parent(struct ieee80211com *ic) { struct rsu_softc *sc = ic->ic_softc; int startall = 0; RSU_LOCK(sc); if (ic->ic_nrunning > 0) { if (!sc->sc_running) { rsu_init(sc); startall = 1; } } else if (sc->sc_running) rsu_stop(sc); RSU_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } /* * Power on sequence for A-cut adapters. */ static void rsu_power_on_acut(struct rsu_softc *sc) { uint32_t reg; rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53); rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57); /* Enable AFE macro block's bandgap and Mbias. */ rsu_write_1(sc, R92S_AFE_MISC, rsu_read_1(sc, R92S_AFE_MISC) | R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN); /* Enable LDOA15 block. */ rsu_write_1(sc, R92S_LDOA15_CTRL, rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN); rsu_write_1(sc, R92S_SPS1_CTRL, rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_LDEN); rsu_ms_delay(sc, 2000); /* Enable switch regulator block. */ rsu_write_1(sc, R92S_SPS1_CTRL, rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_SWEN); rsu_write_4(sc, R92S_SPS1_CTRL, 0x00a7b267); rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08); rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20); rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x90); /* Enable AFE clock. */ rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1, rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04); /* Enable AFE PLL macro block. */ rsu_write_1(sc, R92S_AFE_PLL_CTRL, rsu_read_1(sc, R92S_AFE_PLL_CTRL) | 0x11); /* Attach AFE PLL to MACTOP/BB. */ rsu_write_1(sc, R92S_SYS_ISO_CTRL, rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11); /* Switch to 40MHz clock instead of 80MHz. */ rsu_write_2(sc, R92S_SYS_CLKR, rsu_read_2(sc, R92S_SYS_CLKR) & ~R92S_SYS_CLKSEL); /* Enable MAC clock. */ rsu_write_2(sc, R92S_SYS_CLKR, rsu_read_2(sc, R92S_SYS_CLKR) | R92S_MAC_CLK_EN | R92S_SYS_CLK_EN); rsu_write_1(sc, R92S_PMC_FSM, 0x02); /* Enable digital core and IOREG R/W. */ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08); rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80); /* Switch the control path to firmware. */ reg = rsu_read_2(sc, R92S_SYS_CLKR); reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL; rsu_write_2(sc, R92S_SYS_CLKR, reg); rsu_write_2(sc, R92S_CR, 0x37fc); /* Fix USB RX FIFO issue. */ rsu_write_1(sc, 0xfe5c, rsu_read_1(sc, 0xfe5c) | 0x80); rsu_write_1(sc, 0x00ab, rsu_read_1(sc, 0x00ab) | 0xc0); rsu_write_1(sc, R92S_SYS_CLKR, rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL); } /* * Power on sequence for B-cut and C-cut adapters. */ static void rsu_power_on_bcut(struct rsu_softc *sc) { uint32_t reg; int ntries; /* Prevent eFuse leakage. */ rsu_write_1(sc, 0x37, 0xb0); rsu_ms_delay(sc, 10); rsu_write_1(sc, 0x37, 0x30); /* Switch the control path to hardware. */ reg = rsu_read_2(sc, R92S_SYS_CLKR); if (reg & R92S_FWHW_SEL) { rsu_write_2(sc, R92S_SYS_CLKR, reg & ~(R92S_SWHW_SEL | R92S_FWHW_SEL)); } rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) & ~0x8c); rsu_ms_delay(sc, 1); rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53); rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57); reg = rsu_read_1(sc, R92S_AFE_MISC); rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN); rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN | R92S_AFE_MISC_I32_EN); /* Enable PLL. */ rsu_write_1(sc, R92S_LDOA15_CTRL, rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN); rsu_write_1(sc, R92S_LDOV12D_CTRL, rsu_read_1(sc, R92S_LDOV12D_CTRL) | R92S_LDV12_EN); rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08); rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20); /* Support 64KB IMEM. */ rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x97); /* Enable AFE clock. */ rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1, rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04); /* Enable AFE PLL macro block. */ reg = rsu_read_1(sc, R92S_AFE_PLL_CTRL); rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11); rsu_ms_delay(sc, 1); rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x51); rsu_ms_delay(sc, 1); rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11); rsu_ms_delay(sc, 1); /* Attach AFE PLL to MACTOP/BB. */ rsu_write_1(sc, R92S_SYS_ISO_CTRL, rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11); /* Switch to 40MHz clock. */ rsu_write_1(sc, R92S_SYS_CLKR, 0x00); /* Disable CPU clock and 80MHz SSC. */ rsu_write_1(sc, R92S_SYS_CLKR, rsu_read_1(sc, R92S_SYS_CLKR) | 0xa0); /* Enable MAC clock. */ rsu_write_2(sc, R92S_SYS_CLKR, rsu_read_2(sc, R92S_SYS_CLKR) | R92S_MAC_CLK_EN | R92S_SYS_CLK_EN); rsu_write_1(sc, R92S_PMC_FSM, 0x02); /* Enable digital core and IOREG R/W. */ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08); rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80); /* Switch the control path to firmware. */ reg = rsu_read_2(sc, R92S_SYS_CLKR); reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL; rsu_write_2(sc, R92S_SYS_CLKR, reg); rsu_write_2(sc, R92S_CR, 0x37fc); /* Fix USB RX FIFO issue. */ rsu_write_1(sc, 0xfe5c, rsu_read_1(sc, 0xfe5c) | 0x80); rsu_write_1(sc, R92S_SYS_CLKR, rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL); rsu_write_1(sc, 0xfe1c, 0x80); /* Make sure TxDMA is ready to download firmware. */ for (ntries = 0; ntries < 20; ntries++) { reg = rsu_read_1(sc, R92S_TCR); if ((reg & (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) == (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) break; rsu_ms_delay(sc, 1); } if (ntries == 20) { RSU_DPRINTF(sc, RSU_DEBUG_RESET | RSU_DEBUG_TX, "%s: TxDMA is not ready\n", __func__); /* Reset TxDMA. */ reg = rsu_read_1(sc, R92S_CR); rsu_write_1(sc, R92S_CR, reg & ~R92S_CR_TXDMA_EN); rsu_ms_delay(sc, 1); rsu_write_1(sc, R92S_CR, reg | R92S_CR_TXDMA_EN); } } static void rsu_power_off(struct rsu_softc *sc) { /* Turn RF off. */ rsu_write_1(sc, R92S_RF_CTRL, 0x00); rsu_ms_delay(sc, 5); /* Turn MAC off. */ /* Switch control path. */ rsu_write_1(sc, R92S_SYS_CLKR + 1, 0x38); /* Reset MACTOP. */ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x70); rsu_write_1(sc, R92S_PMC_FSM, 0x06); rsu_write_1(sc, R92S_SYS_ISO_CTRL + 0, 0xf9); rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 0xe8); /* Disable AFE PLL. */ rsu_write_1(sc, R92S_AFE_PLL_CTRL, 0x00); /* Disable A15V. */ rsu_write_1(sc, R92S_LDOA15_CTRL, 0x54); /* Disable eFuse 1.2V. */ rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x50); rsu_write_1(sc, R92S_LDOV12D_CTRL, 0x24); /* Enable AFE macro block's bandgap and Mbias. */ rsu_write_1(sc, R92S_AFE_MISC, 0x30); /* Disable 1.6V LDO. */ rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x56); rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x43); /* Firmware - tell it to switch things off */ (void) rsu_set_fw_power_state(sc, RSU_PWR_OFF); } static int rsu_fw_loadsection(struct rsu_softc *sc, const uint8_t *buf, int len) { const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO]; struct rsu_data *data; struct r92s_tx_desc *txd; int mlen; while (len > 0) { data = rsu_getbuf(sc); if (data == NULL) return (ENOMEM); txd = (struct r92s_tx_desc *)data->buf; memset(txd, 0, sizeof(*txd)); if (len <= RSU_TXBUFSZ - sizeof(*txd)) { /* Last chunk. */ txd->txdw0 |= htole32(R92S_TXDW0_LINIP); mlen = len; } else mlen = RSU_TXBUFSZ - sizeof(*txd); txd->txdw0 |= htole32(SM(R92S_TXDW0_PKTLEN, mlen)); memcpy(&txd[1], buf, mlen); data->buflen = sizeof(*txd) + mlen; RSU_DPRINTF(sc, RSU_DEBUG_TX | RSU_DEBUG_FW | RSU_DEBUG_RESET, "%s: starting transfer %p\n", __func__, data); STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); buf += mlen; len -= mlen; } usbd_transfer_start(sc->sc_xfer[which]); return (0); } static int rsu_load_firmware(struct rsu_softc *sc) { const struct r92s_fw_hdr *hdr; struct r92s_fw_priv *dmem; struct ieee80211com *ic = &sc->sc_ic; const uint8_t *imem, *emem; int imemsz, ememsz; const struct firmware *fw; size_t size; uint32_t reg; int ntries, error; if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY) { RSU_DPRINTF(sc, RSU_DEBUG_ANY, "%s: Firmware already loaded\n", __func__); return (0); } RSU_UNLOCK(sc); /* Read firmware image from the filesystem. */ if ((fw = firmware_get("rsu-rtl8712fw")) == NULL) { device_printf(sc->sc_dev, "%s: failed load firmware of file rsu-rtl8712fw\n", __func__); RSU_LOCK(sc); return (ENXIO); } RSU_LOCK(sc); size = fw->datasize; if (size < sizeof(*hdr)) { device_printf(sc->sc_dev, "firmware too short\n"); error = EINVAL; goto fail; } hdr = (const struct r92s_fw_hdr *)fw->data; if (hdr->signature != htole16(0x8712) && hdr->signature != htole16(0x8192)) { device_printf(sc->sc_dev, "invalid firmware signature 0x%x\n", le16toh(hdr->signature)); error = EINVAL; goto fail; } DPRINTF("FW V%d %02x-%02x %02x:%02x\n", le16toh(hdr->version), hdr->month, hdr->day, hdr->hour, hdr->minute); /* Make sure that driver and firmware are in sync. */ if (hdr->privsz != htole32(sizeof(*dmem))) { device_printf(sc->sc_dev, "unsupported firmware image\n"); error = EINVAL; goto fail; } /* Get FW sections sizes. */ imemsz = le32toh(hdr->imemsz); ememsz = le32toh(hdr->sramsz); /* Check that all FW sections fit in image. */ if (size < sizeof(*hdr) + imemsz + ememsz) { device_printf(sc->sc_dev, "firmware too short\n"); error = EINVAL; goto fail; } imem = (const uint8_t *)&hdr[1]; emem = imem + imemsz; /* Load IMEM section. */ error = rsu_fw_loadsection(sc, imem, imemsz); if (error != 0) { device_printf(sc->sc_dev, "could not load firmware section %s\n", "IMEM"); goto fail; } /* Wait for load to complete. */ for (ntries = 0; ntries != 50; ntries++) { rsu_ms_delay(sc, 10); reg = rsu_read_1(sc, R92S_TCR); if (reg & R92S_TCR_IMEM_CODE_DONE) break; } if (ntries == 50) { device_printf(sc->sc_dev, "timeout waiting for IMEM transfer\n"); error = ETIMEDOUT; goto fail; } /* Load EMEM section. */ error = rsu_fw_loadsection(sc, emem, ememsz); if (error != 0) { device_printf(sc->sc_dev, "could not load firmware section %s\n", "EMEM"); goto fail; } /* Wait for load to complete. */ for (ntries = 0; ntries != 50; ntries++) { rsu_ms_delay(sc, 10); reg = rsu_read_2(sc, R92S_TCR); if (reg & R92S_TCR_EMEM_CODE_DONE) break; } if (ntries == 50) { device_printf(sc->sc_dev, "timeout waiting for EMEM transfer\n"); error = ETIMEDOUT; goto fail; } /* Enable CPU. */ rsu_write_1(sc, R92S_SYS_CLKR, rsu_read_1(sc, R92S_SYS_CLKR) | R92S_SYS_CPU_CLKSEL); if (!(rsu_read_1(sc, R92S_SYS_CLKR) & R92S_SYS_CPU_CLKSEL)) { device_printf(sc->sc_dev, "could not enable system clock\n"); error = EIO; goto fail; } rsu_write_2(sc, R92S_SYS_FUNC_EN, rsu_read_2(sc, R92S_SYS_FUNC_EN) | R92S_FEN_CPUEN); if (!(rsu_read_2(sc, R92S_SYS_FUNC_EN) & R92S_FEN_CPUEN)) { device_printf(sc->sc_dev, "could not enable microcontroller\n"); error = EIO; goto fail; } /* Wait for CPU to initialize. */ for (ntries = 0; ntries < 100; ntries++) { if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_IMEM_RDY) break; rsu_ms_delay(sc, 1); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for microcontroller\n"); error = ETIMEDOUT; goto fail; } /* Update DMEM section before loading. */ dmem = __DECONST(struct r92s_fw_priv *, &hdr->priv); memset(dmem, 0, sizeof(*dmem)); dmem->hci_sel = R92S_HCI_SEL_USB | R92S_HCI_SEL_8172; dmem->nendpoints = sc->sc_nendpoints; dmem->chip_version = sc->cut; dmem->rf_config = sc->sc_rftype; dmem->vcs_type = R92S_VCS_TYPE_AUTO; dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS; dmem->turbo_mode = 0; dmem->bw40_en = !! (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40); dmem->amsdu2ampdu_en = !! (sc->sc_ht); dmem->ampdu_en = !! (sc->sc_ht); dmem->agg_offload = !! (sc->sc_ht); dmem->qos_en = 1; dmem->ps_offload = 1; dmem->lowpower_mode = 1; /* XXX TODO: configurable? */ /* Load DMEM section. */ error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem)); if (error != 0) { device_printf(sc->sc_dev, "could not load firmware section %s\n", "DMEM"); goto fail; } /* Wait for load to complete. */ for (ntries = 0; ntries < 100; ntries++) { if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_DMEM_CODE_DONE) break; rsu_ms_delay(sc, 1); } if (ntries == 100) { device_printf(sc->sc_dev, "timeout waiting for %s transfer\n", "DMEM"); error = ETIMEDOUT; goto fail; } /* Wait for firmware readiness. */ for (ntries = 0; ntries < 60; ntries++) { if (!(rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY)) break; rsu_ms_delay(sc, 1); } if (ntries == 60) { device_printf(sc->sc_dev, "timeout waiting for firmware readiness\n"); error = ETIMEDOUT; goto fail; } fail: firmware_put(fw, FIRMWARE_UNLOAD); return (error); } static int rsu_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct rsu_softc *sc = ic->ic_softc; struct rsu_data *bf; /* prevent management frames from being sent if we're not ready */ if (!sc->sc_running) { m_freem(m); return (ENETDOWN); } RSU_LOCK(sc); bf = rsu_getbuf(sc); if (bf == NULL) { m_freem(m); RSU_UNLOCK(sc); return (ENOBUFS); } if (rsu_tx_start(sc, ni, m, bf) != 0) { m_freem(m); rsu_freebuf(sc, bf); RSU_UNLOCK(sc); return (EIO); } RSU_UNLOCK(sc); return (0); } static void rsu_init(struct rsu_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint8_t macaddr[IEEE80211_ADDR_LEN]; int error; int i; RSU_ASSERT_LOCKED(sc); /* Ensure the mbuf queue is drained */ rsu_drain_mbufq(sc); /* Init host async commands ring. */ sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0; /* Reset power management state. */ rsu_write_1(sc, R92S_USB_HRPWM, 0); /* Power on adapter. */ if (sc->cut == 1) rsu_power_on_acut(sc); else rsu_power_on_bcut(sc); /* Load firmware. */ error = rsu_load_firmware(sc); if (error != 0) goto fail; /* Enable Rx TCP checksum offload. */ rsu_write_4(sc, R92S_RCR, rsu_read_4(sc, R92S_RCR) | 0x04000000); /* Append PHY status. */ rsu_write_4(sc, R92S_RCR, rsu_read_4(sc, R92S_RCR) | 0x02000000); rsu_write_4(sc, R92S_CR, rsu_read_4(sc, R92S_CR) & ~0xff000000); /* Use 128 bytes pages. */ rsu_write_1(sc, 0x00b5, rsu_read_1(sc, 0x00b5) | 0x01); /* Enable USB Rx aggregation. */ rsu_write_1(sc, 0x00bd, rsu_read_1(sc, 0x00bd) | 0x80); /* Set USB Rx aggregation threshold. */ rsu_write_1(sc, 0x00d9, 0x01); /* Set USB Rx aggregation timeout (1.7ms/4). */ rsu_write_1(sc, 0xfe5b, 0x04); /* Fix USB Rx FIFO issue. */ rsu_write_1(sc, 0xfe5c, rsu_read_1(sc, 0xfe5c) | 0x80); /* Set MAC address. */ IEEE80211_ADDR_COPY(macaddr, vap ? vap->iv_myaddr : ic->ic_macaddr); rsu_write_region_1(sc, R92S_MACID, macaddr, IEEE80211_ADDR_LEN); /* It really takes 1.5 seconds for the firmware to boot: */ rsu_ms_delay(sc, 2000); RSU_DPRINTF(sc, RSU_DEBUG_RESET, "%s: setting MAC address to %s\n", __func__, ether_sprintf(macaddr)); error = rsu_fw_cmd(sc, R92S_CMD_SET_MAC_ADDRESS, macaddr, IEEE80211_ADDR_LEN); if (error != 0) { device_printf(sc->sc_dev, "could not set MAC address\n"); goto fail; } /* Set PS mode fully active */ error = rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE); if (error != 0) { device_printf(sc->sc_dev, "could not set PS mode\n"); goto fail; } sc->sc_scan_pass = 0; usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]); /* We're ready to go. */ sc->sc_running = 1; sc->sc_scanning = 0; return; fail: /* Need to stop all failed transfers, if any */ for (i = 0; i != RSU_N_TRANSFER; i++) usbd_transfer_stop(sc->sc_xfer[i]); } static void rsu_stop(struct rsu_softc *sc) { int i; RSU_ASSERT_LOCKED(sc); sc->sc_running = 0; sc->sc_calibrating = 0; taskqueue_cancel_timeout(taskqueue_thread, &sc->calib_task, NULL); taskqueue_cancel(taskqueue_thread, &sc->tx_task, NULL); /* Power off adapter. */ rsu_power_off(sc); for (i = 0; i < RSU_N_TRANSFER; i++) usbd_transfer_stop(sc->sc_xfer[i]); /* Ensure the mbuf queue is drained */ rsu_drain_mbufq(sc); } /* * Note: usb_pause_mtx() actually releases the mutex before calling pause(), * which breaks any kind of driver serialisation. */ static void rsu_ms_delay(struct rsu_softc *sc, int ms) { //usb_pause_mtx(&sc->sc_mtx, hz / 1000); DELAY(ms * 1000); }