131d98677SRui Paulo /* $OpenBSD: if_rsu.c,v 1.17 2013/04/15 09:23:01 mglocker Exp $ */ 231d98677SRui Paulo 331d98677SRui Paulo /*- 431d98677SRui Paulo * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> 531d98677SRui Paulo * 631d98677SRui Paulo * Permission to use, copy, modify, and distribute this software for any 731d98677SRui Paulo * purpose with or without fee is hereby granted, provided that the above 831d98677SRui Paulo * copyright notice and this permission notice appear in all copies. 931d98677SRui Paulo * 1031d98677SRui Paulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1131d98677SRui Paulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1231d98677SRui Paulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1331d98677SRui Paulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1431d98677SRui Paulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1531d98677SRui Paulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1631d98677SRui Paulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1731d98677SRui Paulo */ 1831d98677SRui Paulo #include <sys/cdefs.h> 1931d98677SRui Paulo __FBSDID("$FreeBSD$"); 2031d98677SRui Paulo 2131d98677SRui Paulo /* 2231d98677SRui Paulo * Driver for Realtek RTL8188SU/RTL8191SU/RTL8192SU. 2331d98677SRui Paulo * 2431d98677SRui Paulo * TODO: 2531d98677SRui Paulo * o 11n support 2631d98677SRui Paulo * o h/w crypto 2731d98677SRui Paulo * o hostap / ibss / mesh 2831d98677SRui Paulo */ 2931d98677SRui Paulo #include <sys/param.h> 3031d98677SRui Paulo #include <sys/endian.h> 3131d98677SRui Paulo #include <sys/sockio.h> 3231d98677SRui Paulo #include <sys/mbuf.h> 3331d98677SRui Paulo #include <sys/kernel.h> 3431d98677SRui Paulo #include <sys/socket.h> 3531d98677SRui Paulo #include <sys/systm.h> 3631d98677SRui Paulo #include <sys/conf.h> 3731d98677SRui Paulo #include <sys/bus.h> 3831d98677SRui Paulo #include <sys/rman.h> 3931d98677SRui Paulo #include <sys/firmware.h> 4031d98677SRui Paulo #include <sys/module.h> 4131d98677SRui Paulo 4231d98677SRui Paulo #include <machine/bus.h> 4331d98677SRui Paulo #include <machine/resource.h> 4431d98677SRui Paulo 4531d98677SRui Paulo #include <net/bpf.h> 4631d98677SRui Paulo #include <net/if.h> 4776039bc8SGleb Smirnoff #include <net/if_var.h> 4831d98677SRui Paulo #include <net/if_arp.h> 4931d98677SRui Paulo #include <net/if_dl.h> 5031d98677SRui Paulo #include <net/if_media.h> 5131d98677SRui Paulo #include <net/if_types.h> 5231d98677SRui Paulo 5331d98677SRui Paulo #include <netinet/in.h> 5431d98677SRui Paulo #include <netinet/in_systm.h> 5531d98677SRui Paulo #include <netinet/in_var.h> 5631d98677SRui Paulo #include <netinet/if_ether.h> 5731d98677SRui Paulo #include <netinet/ip.h> 5831d98677SRui Paulo 5931d98677SRui Paulo #include <net80211/ieee80211_var.h> 6031d98677SRui Paulo #include <net80211/ieee80211_regdomain.h> 6131d98677SRui Paulo #include <net80211/ieee80211_radiotap.h> 6231d98677SRui Paulo 6331d98677SRui Paulo #include <dev/usb/usb.h> 6431d98677SRui Paulo #include <dev/usb/usbdi.h> 6531d98677SRui Paulo #include "usbdevs.h" 6631d98677SRui Paulo 6731d98677SRui Paulo #define USB_DEBUG_VAR rsu_debug 6831d98677SRui Paulo #include <dev/usb/usb_debug.h> 6931d98677SRui Paulo 7031d98677SRui Paulo #include <dev/usb/wlan/if_rsureg.h> 7131d98677SRui Paulo 7231d98677SRui Paulo #ifdef USB_DEBUG 7331d98677SRui Paulo static int rsu_debug = 0; 7431d98677SRui Paulo SYSCTL_NODE(_hw_usb, OID_AUTO, rsu, CTLFLAG_RW, 0, "USB rsu"); 75*ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_rsu, OID_AUTO, debug, CTLFLAG_RWTUN, &rsu_debug, 0, 7631d98677SRui Paulo "Debug level"); 7731d98677SRui Paulo #endif 7831d98677SRui Paulo 7931d98677SRui Paulo static const STRUCT_USB_HOST_ID rsu_devs[] = { 8031d98677SRui Paulo #define RSU_HT_NOT_SUPPORTED 0 8131d98677SRui Paulo #define RSU_HT_SUPPORTED 1 8231d98677SRui Paulo #define RSU_DEV_HT(v,p) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \ 8331d98677SRui Paulo RSU_HT_SUPPORTED) } 8431d98677SRui Paulo #define RSU_DEV(v,p) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, \ 8531d98677SRui Paulo RSU_HT_NOT_SUPPORTED) } 8631d98677SRui Paulo RSU_DEV(ASUS, RTL8192SU), 8731d98677SRui Paulo RSU_DEV(AZUREWAVE, RTL8192SU_4), 8831d98677SRui Paulo RSU_DEV_HT(ACCTON, RTL8192SU), 8931d98677SRui Paulo RSU_DEV_HT(ASUS, USBN10), 9031d98677SRui Paulo RSU_DEV_HT(AZUREWAVE, RTL8192SU_1), 9131d98677SRui Paulo RSU_DEV_HT(AZUREWAVE, RTL8192SU_2), 9231d98677SRui Paulo RSU_DEV_HT(AZUREWAVE, RTL8192SU_3), 9331d98677SRui Paulo RSU_DEV_HT(AZUREWAVE, RTL8192SU_5), 9431d98677SRui Paulo RSU_DEV_HT(BELKIN, RTL8192SU_1), 9531d98677SRui Paulo RSU_DEV_HT(BELKIN, RTL8192SU_2), 9631d98677SRui Paulo RSU_DEV_HT(BELKIN, RTL8192SU_3), 9731d98677SRui Paulo RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_1), 9831d98677SRui Paulo RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_2), 9931d98677SRui Paulo RSU_DEV_HT(CONCEPTRONIC2, RTL8192SU_3), 10031d98677SRui Paulo RSU_DEV_HT(COREGA, RTL8192SU), 10131d98677SRui Paulo RSU_DEV_HT(DLINK2, DWA131A1), 10231d98677SRui Paulo RSU_DEV_HT(DLINK2, RTL8192SU_1), 10331d98677SRui Paulo RSU_DEV_HT(DLINK2, RTL8192SU_2), 10431d98677SRui Paulo RSU_DEV_HT(EDIMAX, RTL8192SU_1), 10531d98677SRui Paulo RSU_DEV_HT(EDIMAX, RTL8192SU_2), 106bf124fcfSKevin Lo RSU_DEV_HT(EDIMAX, EW7622UMN), 10731d98677SRui Paulo RSU_DEV_HT(GUILLEMOT, HWGUN54), 10831d98677SRui Paulo RSU_DEV_HT(GUILLEMOT, HWNUM300), 10931d98677SRui Paulo RSU_DEV_HT(HAWKING, RTL8192SU_1), 11031d98677SRui Paulo RSU_DEV_HT(HAWKING, RTL8192SU_2), 11131d98677SRui Paulo RSU_DEV_HT(PLANEX2, GWUSNANO), 11231d98677SRui Paulo RSU_DEV_HT(REALTEK, RTL8171), 11331d98677SRui Paulo RSU_DEV_HT(REALTEK, RTL8172), 11431d98677SRui Paulo RSU_DEV_HT(REALTEK, RTL8173), 11531d98677SRui Paulo RSU_DEV_HT(REALTEK, RTL8174), 11631d98677SRui Paulo RSU_DEV_HT(REALTEK, RTL8192SU), 11731d98677SRui Paulo RSU_DEV_HT(REALTEK, RTL8712), 11831d98677SRui Paulo RSU_DEV_HT(REALTEK, RTL8713), 11931d98677SRui Paulo RSU_DEV_HT(SENAO, RTL8192SU_1), 12031d98677SRui Paulo RSU_DEV_HT(SENAO, RTL8192SU_2), 12131d98677SRui Paulo RSU_DEV_HT(SITECOMEU, WL349V1), 12231d98677SRui Paulo RSU_DEV_HT(SITECOMEU, WL353), 12331d98677SRui Paulo RSU_DEV_HT(SWEEX2, LW154), 124fa20eb98SKevin Lo RSU_DEV_HT(TRENDNET, TEW646UBH), 12531d98677SRui Paulo #undef RSU_DEV_HT 12631d98677SRui Paulo #undef RSU_DEV 12731d98677SRui Paulo }; 12831d98677SRui Paulo 12931d98677SRui Paulo static device_probe_t rsu_match; 13031d98677SRui Paulo static device_attach_t rsu_attach; 13131d98677SRui Paulo static device_detach_t rsu_detach; 132910593b5SHans Petter Selasky static usb_callback_t rsu_bulk_tx_callback_be_bk; 133910593b5SHans Petter Selasky static usb_callback_t rsu_bulk_tx_callback_vi_vo; 13431d98677SRui Paulo static usb_callback_t rsu_bulk_rx_callback; 13531d98677SRui Paulo static usb_error_t rsu_do_request(struct rsu_softc *, 13631d98677SRui Paulo struct usb_device_request *, void *); 13731d98677SRui Paulo static struct ieee80211vap * 13831d98677SRui Paulo rsu_vap_create(struct ieee80211com *, const char name[], 13931d98677SRui Paulo int, enum ieee80211_opmode, int, const uint8_t bssid[], 14031d98677SRui Paulo const uint8_t mac[]); 14131d98677SRui Paulo static void rsu_vap_delete(struct ieee80211vap *); 14231d98677SRui Paulo static void rsu_scan_start(struct ieee80211com *); 14331d98677SRui Paulo static void rsu_scan_end(struct ieee80211com *); 14431d98677SRui Paulo static void rsu_set_channel(struct ieee80211com *); 14531d98677SRui Paulo static void rsu_update_mcast(struct ifnet *); 14631d98677SRui Paulo static int rsu_alloc_rx_list(struct rsu_softc *); 14731d98677SRui Paulo static void rsu_free_rx_list(struct rsu_softc *); 14831d98677SRui Paulo static int rsu_alloc_tx_list(struct rsu_softc *); 14931d98677SRui Paulo static void rsu_free_tx_list(struct rsu_softc *); 15031d98677SRui Paulo static void rsu_free_list(struct rsu_softc *, struct rsu_data [], int); 15131d98677SRui Paulo static struct rsu_data *_rsu_getbuf(struct rsu_softc *); 15231d98677SRui Paulo static struct rsu_data *rsu_getbuf(struct rsu_softc *); 15331d98677SRui Paulo static int rsu_write_region_1(struct rsu_softc *, uint16_t, uint8_t *, 15431d98677SRui Paulo int); 15531d98677SRui Paulo static void rsu_write_1(struct rsu_softc *, uint16_t, uint8_t); 15631d98677SRui Paulo static void rsu_write_2(struct rsu_softc *, uint16_t, uint16_t); 15731d98677SRui Paulo static void rsu_write_4(struct rsu_softc *, uint16_t, uint32_t); 15831d98677SRui Paulo static int rsu_read_region_1(struct rsu_softc *, uint16_t, uint8_t *, 15931d98677SRui Paulo int); 16031d98677SRui Paulo static uint8_t rsu_read_1(struct rsu_softc *, uint16_t); 16131d98677SRui Paulo static uint16_t rsu_read_2(struct rsu_softc *, uint16_t); 16231d98677SRui Paulo static uint32_t rsu_read_4(struct rsu_softc *, uint16_t); 16331d98677SRui Paulo static int rsu_fw_iocmd(struct rsu_softc *, uint32_t); 16431d98677SRui Paulo static uint8_t rsu_efuse_read_1(struct rsu_softc *, uint16_t); 16531d98677SRui Paulo static int rsu_read_rom(struct rsu_softc *); 16631d98677SRui Paulo static int rsu_fw_cmd(struct rsu_softc *, uint8_t, void *, int); 16731d98677SRui Paulo static void rsu_calib_task(void *, int); 16831d98677SRui Paulo static int rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int); 16931d98677SRui Paulo #ifdef notyet 17031d98677SRui Paulo static void rsu_set_key(struct rsu_softc *, const struct ieee80211_key *); 17131d98677SRui Paulo static void rsu_delete_key(struct rsu_softc *, const struct ieee80211_key *); 17231d98677SRui Paulo #endif 17331d98677SRui Paulo static int rsu_site_survey(struct rsu_softc *, struct ieee80211vap *); 17431d98677SRui Paulo static int rsu_join_bss(struct rsu_softc *, struct ieee80211_node *); 17531d98677SRui Paulo static int rsu_disconnect(struct rsu_softc *); 17631d98677SRui Paulo static void rsu_event_survey(struct rsu_softc *, uint8_t *, int); 17731d98677SRui Paulo static void rsu_event_join_bss(struct rsu_softc *, uint8_t *, int); 17831d98677SRui Paulo static void rsu_rx_event(struct rsu_softc *, uint8_t, uint8_t *, int); 17931d98677SRui Paulo static void rsu_rx_multi_event(struct rsu_softc *, uint8_t *, int); 18031d98677SRui Paulo static int8_t rsu_get_rssi(struct rsu_softc *, int, void *); 18131d98677SRui Paulo static struct mbuf * 18231d98677SRui Paulo rsu_rx_frame(struct rsu_softc *, uint8_t *, int, int *); 18331d98677SRui Paulo static struct mbuf * 18431d98677SRui Paulo rsu_rx_multi_frame(struct rsu_softc *, uint8_t *, int, int *); 18531d98677SRui Paulo static struct mbuf * 18631d98677SRui Paulo rsu_rxeof(struct usb_xfer *, struct rsu_data *, int *); 18731d98677SRui Paulo static void rsu_txeof(struct usb_xfer *, struct rsu_data *); 18831d98677SRui Paulo static int rsu_raw_xmit(struct ieee80211_node *, struct mbuf *, 18931d98677SRui Paulo const struct ieee80211_bpf_params *); 19031d98677SRui Paulo static void rsu_init(void *); 19131d98677SRui Paulo static void rsu_init_locked(struct rsu_softc *); 19231d98677SRui Paulo static int rsu_tx_start(struct rsu_softc *, struct ieee80211_node *, 193400b4e53SHans Petter Selasky struct mbuf *, struct rsu_data *); 19431d98677SRui Paulo static void rsu_start(struct ifnet *); 195400b4e53SHans Petter Selasky static void rsu_start_locked(struct ifnet *); 19631d98677SRui Paulo static int rsu_ioctl(struct ifnet *, u_long, caddr_t); 19731d98677SRui Paulo static void rsu_stop(struct ifnet *, int); 19831d98677SRui Paulo static void rsu_stop_locked(struct ifnet *, int); 199b41381aeSHans Petter Selasky static void rsu_ms_delay(struct rsu_softc *); 20031d98677SRui Paulo 20131d98677SRui Paulo static device_method_t rsu_methods[] = { 20231d98677SRui Paulo DEVMETHOD(device_probe, rsu_match), 20331d98677SRui Paulo DEVMETHOD(device_attach, rsu_attach), 20431d98677SRui Paulo DEVMETHOD(device_detach, rsu_detach), 20531d98677SRui Paulo 20631d98677SRui Paulo DEVMETHOD_END 20731d98677SRui Paulo }; 20831d98677SRui Paulo 20931d98677SRui Paulo static driver_t rsu_driver = { 21031d98677SRui Paulo .name = "rsu", 21131d98677SRui Paulo .methods = rsu_methods, 21231d98677SRui Paulo .size = sizeof(struct rsu_softc) 21331d98677SRui Paulo }; 21431d98677SRui Paulo 21531d98677SRui Paulo static devclass_t rsu_devclass; 21631d98677SRui Paulo 21731d98677SRui Paulo DRIVER_MODULE(rsu, uhub, rsu_driver, rsu_devclass, NULL, 0); 21831d98677SRui Paulo MODULE_DEPEND(rsu, wlan, 1, 1, 1); 21931d98677SRui Paulo MODULE_DEPEND(rsu, usb, 1, 1, 1); 22031d98677SRui Paulo MODULE_DEPEND(rsu, firmware, 1, 1, 1); 22131d98677SRui Paulo MODULE_VERSION(rsu, 1); 22231d98677SRui Paulo 223910593b5SHans Petter Selasky static uint8_t rsu_wme_ac_xfer_map[4] = { 224910593b5SHans Petter Selasky [WME_AC_BE] = RSU_BULK_TX_BE_BK, 225910593b5SHans Petter Selasky [WME_AC_BK] = RSU_BULK_TX_BE_BK, 226910593b5SHans Petter Selasky [WME_AC_VI] = RSU_BULK_TX_VI_VO, 227910593b5SHans Petter Selasky [WME_AC_VO] = RSU_BULK_TX_VI_VO, 228910593b5SHans Petter Selasky }; 229910593b5SHans Petter Selasky 23031d98677SRui Paulo static const struct usb_config rsu_config[RSU_N_TRANSFER] = { 23131d98677SRui Paulo [RSU_BULK_RX] = { 23231d98677SRui Paulo .type = UE_BULK, 23331d98677SRui Paulo .endpoint = UE_ADDR_ANY, 23431d98677SRui Paulo .direction = UE_DIR_IN, 23531d98677SRui Paulo .bufsize = RSU_RXBUFSZ, 23631d98677SRui Paulo .flags = { 23731d98677SRui Paulo .pipe_bof = 1, 23831d98677SRui Paulo .short_xfer_ok = 1 23931d98677SRui Paulo }, 24031d98677SRui Paulo .callback = rsu_bulk_rx_callback 24131d98677SRui Paulo }, 242910593b5SHans Petter Selasky [RSU_BULK_TX_BE_BK] = { 24331d98677SRui Paulo .type = UE_BULK, 24431d98677SRui Paulo .endpoint = 0x06, 24531d98677SRui Paulo .direction = UE_DIR_OUT, 24631d98677SRui Paulo .bufsize = RSU_TXBUFSZ, 24731d98677SRui Paulo .flags = { 24831d98677SRui Paulo .ext_buffer = 1, 24931d98677SRui Paulo .pipe_bof = 1, 25031d98677SRui Paulo .force_short_xfer = 1 25131d98677SRui Paulo }, 252910593b5SHans Petter Selasky .callback = rsu_bulk_tx_callback_be_bk, 25331d98677SRui Paulo .timeout = RSU_TX_TIMEOUT 25431d98677SRui Paulo }, 255910593b5SHans Petter Selasky [RSU_BULK_TX_VI_VO] = { 25631d98677SRui Paulo .type = UE_BULK, 25731d98677SRui Paulo .endpoint = 0x04, 25831d98677SRui Paulo .direction = UE_DIR_OUT, 25931d98677SRui Paulo .bufsize = RSU_TXBUFSZ, 26031d98677SRui Paulo .flags = { 26131d98677SRui Paulo .ext_buffer = 1, 26231d98677SRui Paulo .pipe_bof = 1, 26331d98677SRui Paulo .force_short_xfer = 1 26431d98677SRui Paulo }, 265910593b5SHans Petter Selasky .callback = rsu_bulk_tx_callback_vi_vo, 26631d98677SRui Paulo .timeout = RSU_TX_TIMEOUT 26731d98677SRui Paulo }, 26831d98677SRui Paulo }; 26931d98677SRui Paulo 27031d98677SRui Paulo static int 27131d98677SRui Paulo rsu_match(device_t self) 27231d98677SRui Paulo { 27331d98677SRui Paulo struct usb_attach_arg *uaa = device_get_ivars(self); 27431d98677SRui Paulo 27531d98677SRui Paulo if (uaa->usb_mode != USB_MODE_HOST || 27631d98677SRui Paulo uaa->info.bIfaceIndex != 0 || 27731d98677SRui Paulo uaa->info.bConfigIndex != 0) 27831d98677SRui Paulo return (ENXIO); 27931d98677SRui Paulo 28031d98677SRui Paulo return (usbd_lookup_id_by_uaa(rsu_devs, sizeof(rsu_devs), uaa)); 28131d98677SRui Paulo } 28231d98677SRui Paulo 28331d98677SRui Paulo static int 28431d98677SRui Paulo rsu_attach(device_t self) 28531d98677SRui Paulo { 28631d98677SRui Paulo struct usb_attach_arg *uaa = device_get_ivars(self); 28731d98677SRui Paulo struct rsu_softc *sc = device_get_softc(self); 28831d98677SRui Paulo struct ifnet *ifp; 28931d98677SRui Paulo struct ieee80211com *ic; 29031d98677SRui Paulo int error; 29131d98677SRui Paulo uint8_t iface_index, bands; 29231d98677SRui Paulo 29331d98677SRui Paulo device_set_usb_desc(self); 29431d98677SRui Paulo sc->sc_udev = uaa->device; 29531d98677SRui Paulo sc->sc_dev = self; 29631d98677SRui Paulo 29731d98677SRui Paulo mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, 29831d98677SRui Paulo MTX_DEF); 29931d98677SRui Paulo TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_task, 0, 30031d98677SRui Paulo rsu_calib_task, sc); 30131d98677SRui Paulo 302885476cbSHans Petter Selasky /* Allocate Tx/Rx buffers. */ 303885476cbSHans Petter Selasky error = rsu_alloc_rx_list(sc); 304885476cbSHans Petter Selasky if (error != 0) { 305885476cbSHans Petter Selasky device_printf(sc->sc_dev, "could not allocate Rx buffers\n"); 306885476cbSHans Petter Selasky goto fail_usb; 307885476cbSHans Petter Selasky } 308885476cbSHans Petter Selasky 309885476cbSHans Petter Selasky error = rsu_alloc_tx_list(sc); 310885476cbSHans Petter Selasky if (error != 0) { 311885476cbSHans Petter Selasky device_printf(sc->sc_dev, "could not allocate Tx buffers\n"); 312885476cbSHans Petter Selasky rsu_free_rx_list(sc); 313885476cbSHans Petter Selasky goto fail_usb; 314885476cbSHans Petter Selasky } 315885476cbSHans Petter Selasky 31631d98677SRui Paulo iface_index = 0; 31731d98677SRui Paulo error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, 31831d98677SRui Paulo rsu_config, RSU_N_TRANSFER, sc, &sc->sc_mtx); 31931d98677SRui Paulo if (error) { 32031d98677SRui Paulo device_printf(sc->sc_dev, 32131d98677SRui Paulo "could not allocate USB transfers, err=%s\n", 32231d98677SRui Paulo usbd_errstr(error)); 32353dfd5c1SRui Paulo goto fail_usb; 32431d98677SRui Paulo } 32531d98677SRui Paulo RSU_LOCK(sc); 32631d98677SRui Paulo /* Read chip revision. */ 32731d98677SRui Paulo sc->cut = MS(rsu_read_4(sc, R92S_PMC_FSM), R92S_PMC_FSM_CUT); 32831d98677SRui Paulo if (sc->cut != 3) 32931d98677SRui Paulo sc->cut = (sc->cut >> 1) + 1; 33031d98677SRui Paulo error = rsu_read_rom(sc); 331abfa11d6SHans Petter Selasky RSU_UNLOCK(sc); 33231d98677SRui Paulo if (error != 0) { 33331d98677SRui Paulo device_printf(self, "could not read ROM\n"); 33453dfd5c1SRui Paulo goto fail_rom; 33531d98677SRui Paulo } 33631d98677SRui Paulo IEEE80211_ADDR_COPY(sc->sc_bssid, &sc->rom[0x12]); 33731d98677SRui Paulo device_printf(self, "MAC/BB RTL8712 cut %d\n", sc->cut); 33831d98677SRui Paulo ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); 33931d98677SRui Paulo if (ifp == NULL) { 34031d98677SRui Paulo device_printf(self, "cannot allocate interface\n"); 34153dfd5c1SRui Paulo goto fail_ifalloc; 34231d98677SRui Paulo } 34331d98677SRui Paulo ic = ifp->if_l2com; 34431d98677SRui Paulo ifp->if_softc = sc; 34531d98677SRui Paulo if_initname(ifp, "rsu", device_get_unit(self)); 34631d98677SRui Paulo ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 34731d98677SRui Paulo ifp->if_init = rsu_init; 34831d98677SRui Paulo ifp->if_ioctl = rsu_ioctl; 34931d98677SRui Paulo ifp->if_start = rsu_start; 35031d98677SRui Paulo IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 35131d98677SRui Paulo ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 35231d98677SRui Paulo IFQ_SET_READY(&ifp->if_snd); 35331d98677SRui Paulo ifp->if_capabilities |= IFCAP_RXCSUM; 35431d98677SRui Paulo ifp->if_capenable |= IFCAP_RXCSUM; 35531d98677SRui Paulo ifp->if_hwassist = CSUM_TCP; 35631d98677SRui Paulo 35731d98677SRui Paulo ic->ic_ifp = ifp; 35831d98677SRui Paulo ic->ic_phytype = IEEE80211_T_OFDM; /* Not only, but not used. */ 35931d98677SRui Paulo ic->ic_opmode = IEEE80211_M_STA; /* Default to BSS mode. */ 36031d98677SRui Paulo 36131d98677SRui Paulo /* Set device capabilities. */ 36231d98677SRui Paulo ic->ic_caps = 36331d98677SRui Paulo IEEE80211_C_STA | /* station mode */ 36431d98677SRui Paulo IEEE80211_C_BGSCAN | /* Background scan. */ 36531d98677SRui Paulo IEEE80211_C_SHPREAMBLE | /* Short preamble supported. */ 36631d98677SRui Paulo IEEE80211_C_SHSLOT | /* Short slot time supported. */ 36731d98677SRui Paulo IEEE80211_C_WPA; /* WPA/RSN. */ 36831d98677SRui Paulo 36931d98677SRui Paulo #if 0 37031d98677SRui Paulo /* Check if HT support is present. */ 37131d98677SRui Paulo if (usb_lookup(rsu_devs_noht, uaa->vendor, uaa->product) == NULL) { 37231d98677SRui Paulo /* Set HT capabilities. */ 37331d98677SRui Paulo ic->ic_htcaps = 37431d98677SRui Paulo IEEE80211_HTCAP_CBW20_40 | 37531d98677SRui Paulo IEEE80211_HTCAP_DSSSCCK40; 37631d98677SRui Paulo /* Set supported HT rates. */ 37731d98677SRui Paulo for (i = 0; i < 2; i++) 37831d98677SRui Paulo ic->ic_sup_mcs[i] = 0xff; 37931d98677SRui Paulo } 38031d98677SRui Paulo #endif 38131d98677SRui Paulo 38231d98677SRui Paulo /* Set supported .11b and .11g rates. */ 38331d98677SRui Paulo bands = 0; 38431d98677SRui Paulo setbit(&bands, IEEE80211_MODE_11B); 38531d98677SRui Paulo setbit(&bands, IEEE80211_MODE_11G); 38631d98677SRui Paulo ieee80211_init_channels(ic, NULL, &bands); 38731d98677SRui Paulo 38831d98677SRui Paulo ieee80211_ifattach(ic, sc->sc_bssid); 38931d98677SRui Paulo ic->ic_raw_xmit = rsu_raw_xmit; 39031d98677SRui Paulo ic->ic_scan_start = rsu_scan_start; 39131d98677SRui Paulo ic->ic_scan_end = rsu_scan_end; 39231d98677SRui Paulo ic->ic_set_channel = rsu_set_channel; 39331d98677SRui Paulo ic->ic_vap_create = rsu_vap_create; 39431d98677SRui Paulo ic->ic_vap_delete = rsu_vap_delete; 39531d98677SRui Paulo ic->ic_update_mcast = rsu_update_mcast; 39631d98677SRui Paulo 39731d98677SRui Paulo ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, 39831d98677SRui Paulo sizeof(sc->sc_txtap), RSU_TX_RADIOTAP_PRESENT, 39931d98677SRui Paulo &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 40031d98677SRui Paulo RSU_RX_RADIOTAP_PRESENT); 40131d98677SRui Paulo 40231d98677SRui Paulo if (bootverbose) 40331d98677SRui Paulo ieee80211_announce(ic); 40431d98677SRui Paulo 40531d98677SRui Paulo return (0); 40631d98677SRui Paulo 40753dfd5c1SRui Paulo fail_ifalloc: 40853dfd5c1SRui Paulo fail_rom: 40953dfd5c1SRui Paulo usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER); 41053dfd5c1SRui Paulo fail_usb: 41153dfd5c1SRui Paulo mtx_destroy(&sc->sc_mtx); 41231d98677SRui Paulo return (ENXIO); 41331d98677SRui Paulo } 41431d98677SRui Paulo 41531d98677SRui Paulo static int 41631d98677SRui Paulo rsu_detach(device_t self) 41731d98677SRui Paulo { 41831d98677SRui Paulo struct rsu_softc *sc = device_get_softc(self); 41931d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 42031d98677SRui Paulo struct ieee80211com *ic = ifp->if_l2com; 42131d98677SRui Paulo 42231d98677SRui Paulo rsu_stop(ifp, 1); 42331d98677SRui Paulo usbd_transfer_unsetup(sc->sc_xfer, RSU_N_TRANSFER); 42431d98677SRui Paulo ieee80211_ifdetach(ic); 42531d98677SRui Paulo 42631d98677SRui Paulo taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task); 42731d98677SRui Paulo 42831d98677SRui Paulo /* Free Tx/Rx buffers. */ 42931d98677SRui Paulo rsu_free_tx_list(sc); 43031d98677SRui Paulo rsu_free_rx_list(sc); 43131d98677SRui Paulo 43231d98677SRui Paulo if_free(ifp); 43331d98677SRui Paulo mtx_destroy(&sc->sc_mtx); 43431d98677SRui Paulo 43531d98677SRui Paulo return (0); 43631d98677SRui Paulo } 43731d98677SRui Paulo 43831d98677SRui Paulo static usb_error_t 43931d98677SRui Paulo rsu_do_request(struct rsu_softc *sc, struct usb_device_request *req, 44031d98677SRui Paulo void *data) 44131d98677SRui Paulo { 44231d98677SRui Paulo usb_error_t err; 44331d98677SRui Paulo int ntries = 10; 44431d98677SRui Paulo 44531d98677SRui Paulo RSU_ASSERT_LOCKED(sc); 44631d98677SRui Paulo 44731d98677SRui Paulo while (ntries--) { 44831d98677SRui Paulo err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, 44931d98677SRui Paulo req, data, 0, NULL, 250 /* ms */); 4507243077cSHans Petter Selasky if (err == 0 || err == USB_ERR_NOT_CONFIGURED) 45131d98677SRui Paulo break; 45231d98677SRui Paulo DPRINTFN(1, "Control request failed, %s (retrying)\n", 45331d98677SRui Paulo usbd_errstr(err)); 45431d98677SRui Paulo usb_pause_mtx(&sc->sc_mtx, hz / 100); 45531d98677SRui Paulo } 45631d98677SRui Paulo 45731d98677SRui Paulo return (err); 45831d98677SRui Paulo } 45931d98677SRui Paulo 46031d98677SRui Paulo static struct ieee80211vap * 46131d98677SRui Paulo rsu_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 46231d98677SRui Paulo enum ieee80211_opmode opmode, int flags, 46331d98677SRui Paulo const uint8_t bssid[IEEE80211_ADDR_LEN], 46431d98677SRui Paulo const uint8_t mac[IEEE80211_ADDR_LEN]) 46531d98677SRui Paulo { 46631d98677SRui Paulo struct rsu_vap *uvp; 46731d98677SRui Paulo struct ieee80211vap *vap; 46831d98677SRui Paulo 46931d98677SRui Paulo if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 47031d98677SRui Paulo return (NULL); 47131d98677SRui Paulo 47231d98677SRui Paulo uvp = (struct rsu_vap *) malloc(sizeof(struct rsu_vap), 47331d98677SRui Paulo M_80211_VAP, M_NOWAIT | M_ZERO); 47431d98677SRui Paulo if (uvp == NULL) 47531d98677SRui Paulo return (NULL); 47631d98677SRui Paulo vap = &uvp->vap; 477bb2f69e8SHans Petter Selasky 478bb2f69e8SHans Petter Selasky if (ieee80211_vap_setup(ic, vap, name, unit, opmode, 479bb2f69e8SHans Petter Selasky flags, bssid, mac) != 0) { 480bb2f69e8SHans Petter Selasky /* out of memory */ 481bb2f69e8SHans Petter Selasky free(uvp, M_80211_VAP); 482bb2f69e8SHans Petter Selasky return (NULL); 483bb2f69e8SHans Petter Selasky } 48431d98677SRui Paulo 48531d98677SRui Paulo /* override state transition machine */ 48631d98677SRui Paulo uvp->newstate = vap->iv_newstate; 48731d98677SRui Paulo vap->iv_newstate = rsu_newstate; 48831d98677SRui Paulo 48931d98677SRui Paulo /* complete setup */ 49031d98677SRui Paulo ieee80211_vap_attach(vap, ieee80211_media_change, 49131d98677SRui Paulo ieee80211_media_status); 49231d98677SRui Paulo ic->ic_opmode = opmode; 49331d98677SRui Paulo 49431d98677SRui Paulo return (vap); 49531d98677SRui Paulo } 49631d98677SRui Paulo 49731d98677SRui Paulo static void 49831d98677SRui Paulo rsu_vap_delete(struct ieee80211vap *vap) 49931d98677SRui Paulo { 50031d98677SRui Paulo struct rsu_vap *uvp = RSU_VAP(vap); 50131d98677SRui Paulo 50231d98677SRui Paulo ieee80211_vap_detach(vap); 50331d98677SRui Paulo free(uvp, M_80211_VAP); 50431d98677SRui Paulo } 50531d98677SRui Paulo 50631d98677SRui Paulo static void 50731d98677SRui Paulo rsu_scan_start(struct ieee80211com *ic) 50831d98677SRui Paulo { 50931d98677SRui Paulo int error; 51031d98677SRui Paulo struct ifnet *ifp = ic->ic_ifp; 51131d98677SRui Paulo struct rsu_softc *sc = ifp->if_softc; 51231d98677SRui Paulo 51331d98677SRui Paulo /* Scanning is done by the firmware. */ 51431d98677SRui Paulo RSU_LOCK(sc); 51531d98677SRui Paulo error = rsu_site_survey(sc, TAILQ_FIRST(&ic->ic_vaps)); 51631d98677SRui Paulo RSU_UNLOCK(sc); 51731d98677SRui Paulo if (error != 0) 51831d98677SRui Paulo device_printf(sc->sc_dev, 51931d98677SRui Paulo "could not send site survey command\n"); 52031d98677SRui Paulo } 52131d98677SRui Paulo 52231d98677SRui Paulo static void 52331d98677SRui Paulo rsu_scan_end(struct ieee80211com *ic) 52431d98677SRui Paulo { 52531d98677SRui Paulo /* Nothing to do here. */ 52631d98677SRui Paulo } 52731d98677SRui Paulo 52831d98677SRui Paulo static void 52931d98677SRui Paulo rsu_set_channel(struct ieee80211com *ic __unused) 53031d98677SRui Paulo { 53131d98677SRui Paulo /* We are unable to switch channels, yet. */ 53231d98677SRui Paulo } 53331d98677SRui Paulo 53431d98677SRui Paulo static void 53531d98677SRui Paulo rsu_update_mcast(struct ifnet *ifp) 53631d98677SRui Paulo { 53731d98677SRui Paulo /* XXX do nothing? */ 53831d98677SRui Paulo } 53931d98677SRui Paulo 54031d98677SRui Paulo static int 54131d98677SRui Paulo rsu_alloc_list(struct rsu_softc *sc, struct rsu_data data[], 54231d98677SRui Paulo int ndata, int maxsz) 54331d98677SRui Paulo { 54431d98677SRui Paulo int i, error; 54531d98677SRui Paulo 54631d98677SRui Paulo for (i = 0; i < ndata; i++) { 54731d98677SRui Paulo struct rsu_data *dp = &data[i]; 54831d98677SRui Paulo dp->sc = sc; 54931d98677SRui Paulo dp->m = NULL; 55031d98677SRui Paulo dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); 55131d98677SRui Paulo if (dp->buf == NULL) { 55231d98677SRui Paulo device_printf(sc->sc_dev, 55331d98677SRui Paulo "could not allocate buffer\n"); 55431d98677SRui Paulo error = ENOMEM; 55531d98677SRui Paulo goto fail; 55631d98677SRui Paulo } 55731d98677SRui Paulo dp->ni = NULL; 55831d98677SRui Paulo } 55931d98677SRui Paulo 56031d98677SRui Paulo return (0); 56131d98677SRui Paulo fail: 56231d98677SRui Paulo rsu_free_list(sc, data, ndata); 56331d98677SRui Paulo return (error); 56431d98677SRui Paulo } 56531d98677SRui Paulo 56631d98677SRui Paulo static int 56731d98677SRui Paulo rsu_alloc_rx_list(struct rsu_softc *sc) 56831d98677SRui Paulo { 56931d98677SRui Paulo int error, i; 57031d98677SRui Paulo 57131d98677SRui Paulo error = rsu_alloc_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT, 57231d98677SRui Paulo RSU_RXBUFSZ); 57331d98677SRui Paulo if (error != 0) 57431d98677SRui Paulo return (error); 57531d98677SRui Paulo 57631d98677SRui Paulo STAILQ_INIT(&sc->sc_rx_active); 57731d98677SRui Paulo STAILQ_INIT(&sc->sc_rx_inactive); 57831d98677SRui Paulo 57931d98677SRui Paulo for (i = 0; i < RSU_RX_LIST_COUNT; i++) 58031d98677SRui Paulo STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); 58131d98677SRui Paulo 58231d98677SRui Paulo return (0); 58331d98677SRui Paulo } 58431d98677SRui Paulo 58531d98677SRui Paulo static int 58631d98677SRui Paulo rsu_alloc_tx_list(struct rsu_softc *sc) 58731d98677SRui Paulo { 58831d98677SRui Paulo int error, i; 58931d98677SRui Paulo 59031d98677SRui Paulo error = rsu_alloc_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT, 59131d98677SRui Paulo RSU_TXBUFSZ); 59231d98677SRui Paulo if (error != 0) 59331d98677SRui Paulo return (error); 59431d98677SRui Paulo 59531d98677SRui Paulo STAILQ_INIT(&sc->sc_tx_inactive); 596400b4e53SHans Petter Selasky 597910593b5SHans Petter Selasky for (i = 0; i != RSU_N_TRANSFER; i++) { 598400b4e53SHans Petter Selasky STAILQ_INIT(&sc->sc_tx_active[i]); 599400b4e53SHans Petter Selasky STAILQ_INIT(&sc->sc_tx_pending[i]); 600400b4e53SHans Petter Selasky } 60131d98677SRui Paulo 60231d98677SRui Paulo for (i = 0; i < RSU_TX_LIST_COUNT; i++) { 60331d98677SRui Paulo STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], next); 60431d98677SRui Paulo } 60531d98677SRui Paulo 60631d98677SRui Paulo return (0); 60731d98677SRui Paulo } 60831d98677SRui Paulo 60931d98677SRui Paulo static void 61031d98677SRui Paulo rsu_free_tx_list(struct rsu_softc *sc) 61131d98677SRui Paulo { 612885476cbSHans Petter Selasky int i; 613885476cbSHans Petter Selasky 614885476cbSHans Petter Selasky /* prevent further allocations from TX list(s) */ 615885476cbSHans Petter Selasky STAILQ_INIT(&sc->sc_tx_inactive); 616885476cbSHans Petter Selasky 617910593b5SHans Petter Selasky for (i = 0; i != RSU_N_TRANSFER; i++) { 618885476cbSHans Petter Selasky STAILQ_INIT(&sc->sc_tx_active[i]); 619885476cbSHans Petter Selasky STAILQ_INIT(&sc->sc_tx_pending[i]); 620885476cbSHans Petter Selasky } 621885476cbSHans Petter Selasky 62231d98677SRui Paulo rsu_free_list(sc, sc->sc_tx, RSU_TX_LIST_COUNT); 62331d98677SRui Paulo } 62431d98677SRui Paulo 62531d98677SRui Paulo static void 62631d98677SRui Paulo rsu_free_rx_list(struct rsu_softc *sc) 62731d98677SRui Paulo { 628885476cbSHans Petter Selasky /* prevent further allocations from RX list(s) */ 629885476cbSHans Petter Selasky STAILQ_INIT(&sc->sc_rx_inactive); 630885476cbSHans Petter Selasky STAILQ_INIT(&sc->sc_rx_active); 631885476cbSHans Petter Selasky 63231d98677SRui Paulo rsu_free_list(sc, sc->sc_rx, RSU_RX_LIST_COUNT); 63331d98677SRui Paulo } 63431d98677SRui Paulo 63531d98677SRui Paulo static void 63631d98677SRui Paulo rsu_free_list(struct rsu_softc *sc, struct rsu_data data[], int ndata) 63731d98677SRui Paulo { 63831d98677SRui Paulo int i; 63931d98677SRui Paulo 64031d98677SRui Paulo for (i = 0; i < ndata; i++) { 64131d98677SRui Paulo struct rsu_data *dp = &data[i]; 64231d98677SRui Paulo 64331d98677SRui Paulo if (dp->buf != NULL) { 64431d98677SRui Paulo free(dp->buf, M_USBDEV); 64531d98677SRui Paulo dp->buf = NULL; 64631d98677SRui Paulo } 64731d98677SRui Paulo if (dp->ni != NULL) { 64831d98677SRui Paulo ieee80211_free_node(dp->ni); 64931d98677SRui Paulo dp->ni = NULL; 65031d98677SRui Paulo } 65131d98677SRui Paulo } 65231d98677SRui Paulo } 65331d98677SRui Paulo 65431d98677SRui Paulo static struct rsu_data * 65531d98677SRui Paulo _rsu_getbuf(struct rsu_softc *sc) 65631d98677SRui Paulo { 65731d98677SRui Paulo struct rsu_data *bf; 65831d98677SRui Paulo 65931d98677SRui Paulo bf = STAILQ_FIRST(&sc->sc_tx_inactive); 66031d98677SRui Paulo if (bf != NULL) 66131d98677SRui Paulo STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); 66231d98677SRui Paulo else 66331d98677SRui Paulo bf = NULL; 66431d98677SRui Paulo if (bf == NULL) 66531d98677SRui Paulo DPRINTF("out of xmit buffers\n"); 66631d98677SRui Paulo return (bf); 66731d98677SRui Paulo } 66831d98677SRui Paulo 66931d98677SRui Paulo static struct rsu_data * 67031d98677SRui Paulo rsu_getbuf(struct rsu_softc *sc) 67131d98677SRui Paulo { 67231d98677SRui Paulo struct rsu_data *bf; 67331d98677SRui Paulo 67431d98677SRui Paulo RSU_ASSERT_LOCKED(sc); 67531d98677SRui Paulo 67631d98677SRui Paulo bf = _rsu_getbuf(sc); 67731d98677SRui Paulo if (bf == NULL) { 67831d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 67931d98677SRui Paulo DPRINTF("stop queue\n"); 68031d98677SRui Paulo ifp->if_drv_flags |= IFF_DRV_OACTIVE; 68131d98677SRui Paulo } 68231d98677SRui Paulo return (bf); 68331d98677SRui Paulo } 68431d98677SRui Paulo 68531d98677SRui Paulo static int 68631d98677SRui Paulo rsu_write_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf, 68731d98677SRui Paulo int len) 68831d98677SRui Paulo { 68931d98677SRui Paulo usb_device_request_t req; 69031d98677SRui Paulo 69131d98677SRui Paulo req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 69231d98677SRui Paulo req.bRequest = R92S_REQ_REGS; 69331d98677SRui Paulo USETW(req.wValue, addr); 69431d98677SRui Paulo USETW(req.wIndex, 0); 69531d98677SRui Paulo USETW(req.wLength, len); 69631d98677SRui Paulo 69731d98677SRui Paulo return (rsu_do_request(sc, &req, buf)); 69831d98677SRui Paulo } 69931d98677SRui Paulo 70031d98677SRui Paulo static void 70131d98677SRui Paulo rsu_write_1(struct rsu_softc *sc, uint16_t addr, uint8_t val) 70231d98677SRui Paulo { 70331d98677SRui Paulo rsu_write_region_1(sc, addr, &val, 1); 70431d98677SRui Paulo } 70531d98677SRui Paulo 70631d98677SRui Paulo static void 70731d98677SRui Paulo rsu_write_2(struct rsu_softc *sc, uint16_t addr, uint16_t val) 70831d98677SRui Paulo { 70931d98677SRui Paulo val = htole16(val); 71031d98677SRui Paulo rsu_write_region_1(sc, addr, (uint8_t *)&val, 2); 71131d98677SRui Paulo } 71231d98677SRui Paulo 71331d98677SRui Paulo static void 71431d98677SRui Paulo rsu_write_4(struct rsu_softc *sc, uint16_t addr, uint32_t val) 71531d98677SRui Paulo { 71631d98677SRui Paulo val = htole32(val); 71731d98677SRui Paulo rsu_write_region_1(sc, addr, (uint8_t *)&val, 4); 71831d98677SRui Paulo } 71931d98677SRui Paulo 72031d98677SRui Paulo static int 72131d98677SRui Paulo rsu_read_region_1(struct rsu_softc *sc, uint16_t addr, uint8_t *buf, 72231d98677SRui Paulo int len) 72331d98677SRui Paulo { 72431d98677SRui Paulo usb_device_request_t req; 72531d98677SRui Paulo 72631d98677SRui Paulo req.bmRequestType = UT_READ_VENDOR_DEVICE; 72731d98677SRui Paulo req.bRequest = R92S_REQ_REGS; 72831d98677SRui Paulo USETW(req.wValue, addr); 72931d98677SRui Paulo USETW(req.wIndex, 0); 73031d98677SRui Paulo USETW(req.wLength, len); 73131d98677SRui Paulo 73231d98677SRui Paulo return (rsu_do_request(sc, &req, buf)); 73331d98677SRui Paulo } 73431d98677SRui Paulo 73531d98677SRui Paulo static uint8_t 73631d98677SRui Paulo rsu_read_1(struct rsu_softc *sc, uint16_t addr) 73731d98677SRui Paulo { 73831d98677SRui Paulo uint8_t val; 73931d98677SRui Paulo 74031d98677SRui Paulo if (rsu_read_region_1(sc, addr, &val, 1) != 0) 74131d98677SRui Paulo return (0xff); 74231d98677SRui Paulo return (val); 74331d98677SRui Paulo } 74431d98677SRui Paulo 74531d98677SRui Paulo static uint16_t 74631d98677SRui Paulo rsu_read_2(struct rsu_softc *sc, uint16_t addr) 74731d98677SRui Paulo { 74831d98677SRui Paulo uint16_t val; 74931d98677SRui Paulo 75031d98677SRui Paulo if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0) 75131d98677SRui Paulo return (0xffff); 75231d98677SRui Paulo return (le16toh(val)); 75331d98677SRui Paulo } 75431d98677SRui Paulo 75531d98677SRui Paulo static uint32_t 75631d98677SRui Paulo rsu_read_4(struct rsu_softc *sc, uint16_t addr) 75731d98677SRui Paulo { 75831d98677SRui Paulo uint32_t val; 75931d98677SRui Paulo 76031d98677SRui Paulo if (rsu_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0) 76131d98677SRui Paulo return (0xffffffff); 76231d98677SRui Paulo return (le32toh(val)); 76331d98677SRui Paulo } 76431d98677SRui Paulo 76531d98677SRui Paulo static int 76631d98677SRui Paulo rsu_fw_iocmd(struct rsu_softc *sc, uint32_t iocmd) 76731d98677SRui Paulo { 76831d98677SRui Paulo int ntries; 76931d98677SRui Paulo 77031d98677SRui Paulo rsu_write_4(sc, R92S_IOCMD_CTRL, iocmd); 771b41381aeSHans Petter Selasky rsu_ms_delay(sc); 77231d98677SRui Paulo for (ntries = 0; ntries < 50; ntries++) { 77331d98677SRui Paulo if (rsu_read_4(sc, R92S_IOCMD_CTRL) == 0) 77431d98677SRui Paulo return (0); 775b41381aeSHans Petter Selasky rsu_ms_delay(sc); 77631d98677SRui Paulo } 77731d98677SRui Paulo return (ETIMEDOUT); 77831d98677SRui Paulo } 77931d98677SRui Paulo 78031d98677SRui Paulo static uint8_t 78131d98677SRui Paulo rsu_efuse_read_1(struct rsu_softc *sc, uint16_t addr) 78231d98677SRui Paulo { 78331d98677SRui Paulo uint32_t reg; 78431d98677SRui Paulo int ntries; 78531d98677SRui Paulo 78631d98677SRui Paulo reg = rsu_read_4(sc, R92S_EFUSE_CTRL); 78731d98677SRui Paulo reg = RW(reg, R92S_EFUSE_CTRL_ADDR, addr); 78831d98677SRui Paulo reg &= ~R92S_EFUSE_CTRL_VALID; 78931d98677SRui Paulo rsu_write_4(sc, R92S_EFUSE_CTRL, reg); 79031d98677SRui Paulo /* Wait for read operation to complete. */ 79131d98677SRui Paulo for (ntries = 0; ntries < 100; ntries++) { 79231d98677SRui Paulo reg = rsu_read_4(sc, R92S_EFUSE_CTRL); 79331d98677SRui Paulo if (reg & R92S_EFUSE_CTRL_VALID) 79431d98677SRui Paulo return (MS(reg, R92S_EFUSE_CTRL_DATA)); 795b41381aeSHans Petter Selasky rsu_ms_delay(sc); 79631d98677SRui Paulo } 79731d98677SRui Paulo device_printf(sc->sc_dev, 79831d98677SRui Paulo "could not read efuse byte at address 0x%x\n", addr); 79931d98677SRui Paulo return (0xff); 80031d98677SRui Paulo } 80131d98677SRui Paulo 80231d98677SRui Paulo static int 80331d98677SRui Paulo rsu_read_rom(struct rsu_softc *sc) 80431d98677SRui Paulo { 80531d98677SRui Paulo uint8_t *rom = sc->rom; 80631d98677SRui Paulo uint16_t addr = 0; 80731d98677SRui Paulo uint32_t reg; 80831d98677SRui Paulo uint8_t off, msk; 80931d98677SRui Paulo int i; 81031d98677SRui Paulo 81131d98677SRui Paulo /* Make sure that ROM type is eFuse and that autoload succeeded. */ 81231d98677SRui Paulo reg = rsu_read_1(sc, R92S_EE_9346CR); 81331d98677SRui Paulo if ((reg & (R92S_9356SEL | R92S_EEPROM_EN)) != R92S_EEPROM_EN) 81431d98677SRui Paulo return (EIO); 81531d98677SRui Paulo 81631d98677SRui Paulo /* Turn on 2.5V to prevent eFuse leakage. */ 81731d98677SRui Paulo reg = rsu_read_1(sc, R92S_EFUSE_TEST + 3); 81831d98677SRui Paulo rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg | 0x80); 819b41381aeSHans Petter Selasky rsu_ms_delay(sc); 82031d98677SRui Paulo rsu_write_1(sc, R92S_EFUSE_TEST + 3, reg & ~0x80); 82131d98677SRui Paulo 82231d98677SRui Paulo /* Read full ROM image. */ 82331d98677SRui Paulo memset(&sc->rom, 0xff, sizeof(sc->rom)); 82431d98677SRui Paulo while (addr < 512) { 82531d98677SRui Paulo reg = rsu_efuse_read_1(sc, addr); 82631d98677SRui Paulo if (reg == 0xff) 82731d98677SRui Paulo break; 82831d98677SRui Paulo addr++; 82931d98677SRui Paulo off = reg >> 4; 83031d98677SRui Paulo msk = reg & 0xf; 83131d98677SRui Paulo for (i = 0; i < 4; i++) { 83231d98677SRui Paulo if (msk & (1 << i)) 83331d98677SRui Paulo continue; 83431d98677SRui Paulo rom[off * 8 + i * 2 + 0] = 83531d98677SRui Paulo rsu_efuse_read_1(sc, addr); 83631d98677SRui Paulo addr++; 83731d98677SRui Paulo rom[off * 8 + i * 2 + 1] = 83831d98677SRui Paulo rsu_efuse_read_1(sc, addr); 83931d98677SRui Paulo addr++; 84031d98677SRui Paulo } 84131d98677SRui Paulo } 84231d98677SRui Paulo #ifdef USB_DEBUG 84331d98677SRui Paulo if (rsu_debug >= 5) { 84431d98677SRui Paulo /* Dump ROM content. */ 84531d98677SRui Paulo printf("\n"); 84631d98677SRui Paulo for (i = 0; i < sizeof(sc->rom); i++) 84731d98677SRui Paulo printf("%02x:", rom[i]); 84831d98677SRui Paulo printf("\n"); 84931d98677SRui Paulo } 85031d98677SRui Paulo #endif 85131d98677SRui Paulo return (0); 85231d98677SRui Paulo } 85331d98677SRui Paulo 85431d98677SRui Paulo static int 85531d98677SRui Paulo rsu_fw_cmd(struct rsu_softc *sc, uint8_t code, void *buf, int len) 85631d98677SRui Paulo { 857910593b5SHans Petter Selasky const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO]; 85831d98677SRui Paulo struct rsu_data *data; 85931d98677SRui Paulo struct r92s_tx_desc *txd; 86031d98677SRui Paulo struct r92s_fw_cmd_hdr *cmd; 861400b4e53SHans Petter Selasky int cmdsz; 862400b4e53SHans Petter Selasky int xferlen; 86331d98677SRui Paulo 86431d98677SRui Paulo data = rsu_getbuf(sc); 86531d98677SRui Paulo if (data == NULL) 86631d98677SRui Paulo return (ENOMEM); 86731d98677SRui Paulo 86831d98677SRui Paulo /* Round-up command length to a multiple of 8 bytes. */ 86931d98677SRui Paulo cmdsz = (len + 7) & ~7; 87031d98677SRui Paulo 87131d98677SRui Paulo xferlen = sizeof(*txd) + sizeof(*cmd) + cmdsz; 87231d98677SRui Paulo KASSERT(xferlen <= RSU_TXBUFSZ, ("%s: invalid length", __func__)); 87331d98677SRui Paulo memset(data->buf, 0, xferlen); 87431d98677SRui Paulo 87531d98677SRui Paulo /* Setup Tx descriptor. */ 87631d98677SRui Paulo txd = (struct r92s_tx_desc *)data->buf; 87731d98677SRui Paulo txd->txdw0 = htole32( 87831d98677SRui Paulo SM(R92S_TXDW0_OFFSET, sizeof(*txd)) | 87931d98677SRui Paulo SM(R92S_TXDW0_PKTLEN, sizeof(*cmd) + cmdsz) | 88031d98677SRui Paulo R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG); 88131d98677SRui Paulo txd->txdw1 = htole32(SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_H2C)); 88231d98677SRui Paulo 88331d98677SRui Paulo /* Setup command header. */ 88431d98677SRui Paulo cmd = (struct r92s_fw_cmd_hdr *)&txd[1]; 88531d98677SRui Paulo cmd->len = htole16(cmdsz); 88631d98677SRui Paulo cmd->code = code; 88731d98677SRui Paulo cmd->seq = sc->cmd_seq; 88831d98677SRui Paulo sc->cmd_seq = (sc->cmd_seq + 1) & 0x7f; 88931d98677SRui Paulo 89031d98677SRui Paulo /* Copy command payload. */ 89131d98677SRui Paulo memcpy(&cmd[1], buf, len); 89231d98677SRui Paulo 89331d98677SRui Paulo DPRINTFN(2, "Tx cmd code=0x%x len=0x%x\n", code, cmdsz); 89431d98677SRui Paulo data->buflen = xferlen; 895400b4e53SHans Petter Selasky STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); 896910593b5SHans Petter Selasky usbd_transfer_start(sc->sc_xfer[which]); 89731d98677SRui Paulo 89831d98677SRui Paulo return (0); 89931d98677SRui Paulo } 90031d98677SRui Paulo 90131d98677SRui Paulo /* ARGSUSED */ 90231d98677SRui Paulo static void 90331d98677SRui Paulo rsu_calib_task(void *arg, int pending __unused) 90431d98677SRui Paulo { 90531d98677SRui Paulo struct rsu_softc *sc = arg; 90631d98677SRui Paulo uint32_t reg; 90731d98677SRui Paulo 90831d98677SRui Paulo DPRINTFN(6, "running calibration task\n"); 909910593b5SHans Petter Selasky 91031d98677SRui Paulo RSU_LOCK(sc); 91131d98677SRui Paulo #ifdef notyet 91231d98677SRui Paulo /* Read WPS PBC status. */ 91331d98677SRui Paulo rsu_write_1(sc, R92S_MAC_PINMUX_CTRL, 91431d98677SRui Paulo R92S_GPIOMUX_EN | SM(R92S_GPIOSEL_GPIO, R92S_GPIOSEL_GPIO_JTAG)); 91531d98677SRui Paulo rsu_write_1(sc, R92S_GPIO_IO_SEL, 91631d98677SRui Paulo rsu_read_1(sc, R92S_GPIO_IO_SEL) & ~R92S_GPIO_WPS); 91731d98677SRui Paulo reg = rsu_read_1(sc, R92S_GPIO_CTRL); 91831d98677SRui Paulo if (reg != 0xff && (reg & R92S_GPIO_WPS)) 91931d98677SRui Paulo DPRINTF(("WPS PBC is pushed\n")); 92031d98677SRui Paulo #endif 92131d98677SRui Paulo /* Read current signal level. */ 92231d98677SRui Paulo if (rsu_fw_iocmd(sc, 0xf4000001) == 0) { 92331d98677SRui Paulo reg = rsu_read_4(sc, R92S_IOCMD_DATA); 92431d98677SRui Paulo DPRINTFN(8, "RSSI=%d%%\n", reg >> 4); 92531d98677SRui Paulo } 926910593b5SHans Petter Selasky if (sc->sc_calibrating) 927910593b5SHans Petter Selasky taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz); 92831d98677SRui Paulo RSU_UNLOCK(sc); 92931d98677SRui Paulo } 93031d98677SRui Paulo 93131d98677SRui Paulo static int 93231d98677SRui Paulo rsu_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 93331d98677SRui Paulo { 93431d98677SRui Paulo struct rsu_vap *uvp = RSU_VAP(vap); 93531d98677SRui Paulo struct ieee80211com *ic = vap->iv_ic; 93631d98677SRui Paulo struct rsu_softc *sc = ic->ic_ifp->if_softc; 93731d98677SRui Paulo struct ieee80211_node *ni; 93831d98677SRui Paulo struct ieee80211_rateset *rs; 93931d98677SRui Paulo enum ieee80211_state ostate; 94031d98677SRui Paulo int error, startcal = 0; 94131d98677SRui Paulo 94231d98677SRui Paulo ostate = vap->iv_state; 94331d98677SRui Paulo DPRINTF("%s -> %s\n", ieee80211_state_name[ostate], 94431d98677SRui Paulo ieee80211_state_name[nstate]); 94531d98677SRui Paulo 94631d98677SRui Paulo IEEE80211_UNLOCK(ic); 94731d98677SRui Paulo if (ostate == IEEE80211_S_RUN) { 94831d98677SRui Paulo RSU_LOCK(sc); 94931d98677SRui Paulo /* Stop calibration. */ 95031d98677SRui Paulo sc->sc_calibrating = 0; 95131d98677SRui Paulo RSU_UNLOCK(sc); 95231d98677SRui Paulo taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task); 95331d98677SRui Paulo /* Disassociate from our current BSS. */ 95431d98677SRui Paulo RSU_LOCK(sc); 95531d98677SRui Paulo rsu_disconnect(sc); 95631d98677SRui Paulo } else 95731d98677SRui Paulo RSU_LOCK(sc); 95831d98677SRui Paulo switch (nstate) { 95931d98677SRui Paulo case IEEE80211_S_INIT: 96031d98677SRui Paulo break; 96131d98677SRui Paulo case IEEE80211_S_AUTH: 96231d98677SRui Paulo ni = ieee80211_ref_node(vap->iv_bss); 96331d98677SRui Paulo error = rsu_join_bss(sc, ni); 96431d98677SRui Paulo ieee80211_free_node(ni); 96531d98677SRui Paulo if (error != 0) { 96631d98677SRui Paulo device_printf(sc->sc_dev, 96731d98677SRui Paulo "could not send join command\n"); 96831d98677SRui Paulo } 96931d98677SRui Paulo break; 97031d98677SRui Paulo case IEEE80211_S_RUN: 97131d98677SRui Paulo ni = ieee80211_ref_node(vap->iv_bss); 97231d98677SRui Paulo rs = &ni->ni_rates; 97331d98677SRui Paulo /* Indicate highest supported rate. */ 97431d98677SRui Paulo ni->ni_txrate = rs->rs_rates[rs->rs_nrates - 1]; 97531d98677SRui Paulo ieee80211_free_node(ni); 97631d98677SRui Paulo startcal = 1; 97731d98677SRui Paulo break; 97831d98677SRui Paulo default: 97931d98677SRui Paulo break; 98031d98677SRui Paulo } 98131d98677SRui Paulo sc->sc_calibrating = 1; 982910593b5SHans Petter Selasky /* Start periodic calibration. */ 983910593b5SHans Petter Selasky taskqueue_enqueue_timeout(taskqueue_thread, &sc->calib_task, hz); 98431d98677SRui Paulo RSU_UNLOCK(sc); 98531d98677SRui Paulo IEEE80211_LOCK(ic); 98631d98677SRui Paulo return (uvp->newstate(vap, nstate, arg)); 98731d98677SRui Paulo } 98831d98677SRui Paulo 98931d98677SRui Paulo #ifdef notyet 99031d98677SRui Paulo static void 99131d98677SRui Paulo rsu_set_key(struct rsu_softc *sc, const struct ieee80211_key *k) 99231d98677SRui Paulo { 99331d98677SRui Paulo struct r92s_fw_cmd_set_key key; 99431d98677SRui Paulo 99531d98677SRui Paulo memset(&key, 0, sizeof(key)); 99631d98677SRui Paulo /* Map net80211 cipher to HW crypto algorithm. */ 99731d98677SRui Paulo switch (k->wk_cipher->ic_cipher) { 99831d98677SRui Paulo case IEEE80211_CIPHER_WEP: 99931d98677SRui Paulo if (k->wk_keylen < 8) 100031d98677SRui Paulo key.algo = R92S_KEY_ALGO_WEP40; 100131d98677SRui Paulo else 100231d98677SRui Paulo key.algo = R92S_KEY_ALGO_WEP104; 100331d98677SRui Paulo break; 100431d98677SRui Paulo case IEEE80211_CIPHER_TKIP: 100531d98677SRui Paulo key.algo = R92S_KEY_ALGO_TKIP; 100631d98677SRui Paulo break; 100731d98677SRui Paulo case IEEE80211_CIPHER_AES_CCM: 100831d98677SRui Paulo key.algo = R92S_KEY_ALGO_AES; 100931d98677SRui Paulo break; 101031d98677SRui Paulo default: 101131d98677SRui Paulo return; 101231d98677SRui Paulo } 101331d98677SRui Paulo key.id = k->wk_keyix; 101431d98677SRui Paulo key.grpkey = (k->wk_flags & IEEE80211_KEY_GROUP) != 0; 101531d98677SRui Paulo memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key))); 101631d98677SRui Paulo (void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key)); 101731d98677SRui Paulo } 101831d98677SRui Paulo 101931d98677SRui Paulo static void 102031d98677SRui Paulo rsu_delete_key(struct rsu_softc *sc, const struct ieee80211_key *k) 102131d98677SRui Paulo { 102231d98677SRui Paulo struct r92s_fw_cmd_set_key key; 102331d98677SRui Paulo 102431d98677SRui Paulo memset(&key, 0, sizeof(key)); 102531d98677SRui Paulo key.id = k->wk_keyix; 102631d98677SRui Paulo (void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key)); 102731d98677SRui Paulo } 102831d98677SRui Paulo #endif 102931d98677SRui Paulo 103031d98677SRui Paulo static int 103131d98677SRui Paulo rsu_site_survey(struct rsu_softc *sc, struct ieee80211vap *vap) 103231d98677SRui Paulo { 103331d98677SRui Paulo struct r92s_fw_cmd_sitesurvey cmd; 103431d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 103531d98677SRui Paulo struct ieee80211com *ic = ifp->if_l2com; 103631d98677SRui Paulo 103731d98677SRui Paulo memset(&cmd, 0, sizeof(cmd)); 103831d98677SRui Paulo if ((ic->ic_flags & IEEE80211_F_ASCAN) || sc->scan_pass == 1) 103931d98677SRui Paulo cmd.active = htole32(1); 104031d98677SRui Paulo cmd.limit = htole32(48); 104131d98677SRui Paulo if (sc->scan_pass == 1 && vap->iv_des_nssid > 0) { 104231d98677SRui Paulo /* Do a directed scan for second pass. */ 104331d98677SRui Paulo cmd.ssidlen = htole32(vap->iv_des_ssid[0].len); 104431d98677SRui Paulo memcpy(cmd.ssid, vap->iv_des_ssid[0].ssid, 104531d98677SRui Paulo vap->iv_des_ssid[0].len); 104631d98677SRui Paulo 104731d98677SRui Paulo } 104831d98677SRui Paulo DPRINTF("sending site survey command, pass=%d\n", sc->scan_pass); 104931d98677SRui Paulo return (rsu_fw_cmd(sc, R92S_CMD_SITE_SURVEY, &cmd, sizeof(cmd))); 105031d98677SRui Paulo } 105131d98677SRui Paulo 105231d98677SRui Paulo static int 105331d98677SRui Paulo rsu_join_bss(struct rsu_softc *sc, struct ieee80211_node *ni) 105431d98677SRui Paulo { 105531d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 105631d98677SRui Paulo struct ieee80211com *ic = ifp->if_l2com; 105731d98677SRui Paulo struct ieee80211vap *vap = ni->ni_vap; 105831d98677SRui Paulo struct ndis_wlan_bssid_ex *bss; 105931d98677SRui Paulo struct ndis_802_11_fixed_ies *fixed; 106031d98677SRui Paulo struct r92s_fw_cmd_auth auth; 1061bb03cd6fSHans Petter Selasky uint8_t buf[sizeof(*bss) + 128] __aligned(4); 1062bb03cd6fSHans Petter Selasky uint8_t *frm; 106331d98677SRui Paulo uint8_t opmode; 106431d98677SRui Paulo int error; 106531d98677SRui Paulo 106631d98677SRui Paulo /* Let the FW decide the opmode based on the capinfo field. */ 106731d98677SRui Paulo opmode = NDIS802_11AUTOUNKNOWN; 106831d98677SRui Paulo DPRINTF("setting operating mode to %d\n", opmode); 106931d98677SRui Paulo error = rsu_fw_cmd(sc, R92S_CMD_SET_OPMODE, &opmode, sizeof(opmode)); 107031d98677SRui Paulo if (error != 0) 107131d98677SRui Paulo return (error); 107231d98677SRui Paulo 107331d98677SRui Paulo memset(&auth, 0, sizeof(auth)); 107431d98677SRui Paulo if (vap->iv_flags & IEEE80211_F_WPA) { 107531d98677SRui Paulo auth.mode = R92S_AUTHMODE_WPA; 1076bb03cd6fSHans Petter Selasky auth.dot1x = (ni->ni_authmode == IEEE80211_AUTH_8021X); 107731d98677SRui Paulo } else 107831d98677SRui Paulo auth.mode = R92S_AUTHMODE_OPEN; 107931d98677SRui Paulo DPRINTF("setting auth mode to %d\n", auth.mode); 108031d98677SRui Paulo error = rsu_fw_cmd(sc, R92S_CMD_SET_AUTH, &auth, sizeof(auth)); 108131d98677SRui Paulo if (error != 0) 108231d98677SRui Paulo return (error); 108331d98677SRui Paulo 108431d98677SRui Paulo memset(buf, 0, sizeof(buf)); 108531d98677SRui Paulo bss = (struct ndis_wlan_bssid_ex *)buf; 108631d98677SRui Paulo IEEE80211_ADDR_COPY(bss->macaddr, ni->ni_bssid); 108731d98677SRui Paulo bss->ssid.ssidlen = htole32(ni->ni_esslen); 108831d98677SRui Paulo memcpy(bss->ssid.ssid, ni->ni_essid, ni->ni_esslen); 108931d98677SRui Paulo if (vap->iv_flags & (IEEE80211_F_PRIVACY | IEEE80211_F_WPA)) 109031d98677SRui Paulo bss->privacy = htole32(1); 109131d98677SRui Paulo bss->rssi = htole32(ni->ni_avgrssi); 109231d98677SRui Paulo if (ic->ic_curmode == IEEE80211_MODE_11B) 109331d98677SRui Paulo bss->networktype = htole32(NDIS802_11DS); 109431d98677SRui Paulo else 109531d98677SRui Paulo bss->networktype = htole32(NDIS802_11OFDM24); 109631d98677SRui Paulo bss->config.len = htole32(sizeof(bss->config)); 109731d98677SRui Paulo bss->config.bintval = htole32(ni->ni_intval); 109831d98677SRui Paulo bss->config.dsconfig = htole32(ieee80211_chan2ieee(ic, ni->ni_chan)); 109931d98677SRui Paulo bss->inframode = htole32(NDIS802_11INFRASTRUCTURE); 110031d98677SRui Paulo memcpy(bss->supprates, ni->ni_rates.rs_rates, 110131d98677SRui Paulo ni->ni_rates.rs_nrates); 110231d98677SRui Paulo /* Write the fixed fields of the beacon frame. */ 110331d98677SRui Paulo fixed = (struct ndis_802_11_fixed_ies *)&bss[1]; 110431d98677SRui Paulo memcpy(&fixed->tstamp, ni->ni_tstamp.data, 8); 110531d98677SRui Paulo fixed->bintval = htole16(ni->ni_intval); 110631d98677SRui Paulo fixed->capabilities = htole16(ni->ni_capinfo); 110731d98677SRui Paulo /* Write IEs to be included in the association request. */ 110831d98677SRui Paulo frm = (uint8_t *)&fixed[1]; 110931d98677SRui Paulo frm = ieee80211_add_rsn(frm, vap); 111031d98677SRui Paulo frm = ieee80211_add_wpa(frm, vap); 111131d98677SRui Paulo frm = ieee80211_add_qos(frm, ni); 111231d98677SRui Paulo if (ni->ni_flags & IEEE80211_NODE_HT) 111331d98677SRui Paulo frm = ieee80211_add_htcap(frm, ni); 111431d98677SRui Paulo bss->ieslen = htole32(frm - (uint8_t *)fixed); 111531d98677SRui Paulo bss->len = htole32(((frm - buf) + 3) & ~3); 111631d98677SRui Paulo DPRINTF("sending join bss command to %s chan %d\n", 111731d98677SRui Paulo ether_sprintf(bss->macaddr), le32toh(bss->config.dsconfig)); 111831d98677SRui Paulo return (rsu_fw_cmd(sc, R92S_CMD_JOIN_BSS, buf, sizeof(buf))); 111931d98677SRui Paulo } 112031d98677SRui Paulo 112131d98677SRui Paulo static int 112231d98677SRui Paulo rsu_disconnect(struct rsu_softc *sc) 112331d98677SRui Paulo { 112431d98677SRui Paulo uint32_t zero = 0; /* :-) */ 112531d98677SRui Paulo 112631d98677SRui Paulo /* Disassociate from our current BSS. */ 112731d98677SRui Paulo DPRINTF("sending disconnect command\n"); 112831d98677SRui Paulo return (rsu_fw_cmd(sc, R92S_CMD_DISCONNECT, &zero, sizeof(zero))); 112931d98677SRui Paulo } 113031d98677SRui Paulo 113131d98677SRui Paulo static void 113231d98677SRui Paulo rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len) 113331d98677SRui Paulo { 113431d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 113531d98677SRui Paulo struct ieee80211com *ic = ifp->if_l2com; 113631d98677SRui Paulo struct ieee80211_frame *wh; 113731d98677SRui Paulo struct ieee80211_channel *c; 113831d98677SRui Paulo struct ndis_wlan_bssid_ex *bss; 113931d98677SRui Paulo struct mbuf *m; 114031d98677SRui Paulo int pktlen; 114131d98677SRui Paulo 114231d98677SRui Paulo if (__predict_false(len < sizeof(*bss))) 114331d98677SRui Paulo return; 114431d98677SRui Paulo bss = (struct ndis_wlan_bssid_ex *)buf; 114531d98677SRui Paulo if (__predict_false(len < sizeof(*bss) + le32toh(bss->ieslen))) 114631d98677SRui Paulo return; 114731d98677SRui Paulo 114831d98677SRui Paulo DPRINTFN(2, "found BSS %s: len=%d chan=%d inframode=%d " 114931d98677SRui Paulo "networktype=%d privacy=%d\n", 115031d98677SRui Paulo ether_sprintf(bss->macaddr), le32toh(bss->len), 115131d98677SRui Paulo le32toh(bss->config.dsconfig), le32toh(bss->inframode), 115231d98677SRui Paulo le32toh(bss->networktype), le32toh(bss->privacy)); 115331d98677SRui Paulo 115431d98677SRui Paulo /* Build a fake beacon frame to let net80211 do all the parsing. */ 115531d98677SRui Paulo pktlen = sizeof(*wh) + le32toh(bss->ieslen); 115631d98677SRui Paulo if (__predict_false(pktlen > MCLBYTES)) 115731d98677SRui Paulo return; 1158107f00c0SKevin Lo m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR); 115931d98677SRui Paulo if (__predict_false(m == NULL)) 116031d98677SRui Paulo return; 116131d98677SRui Paulo wh = mtod(m, struct ieee80211_frame *); 116231d98677SRui Paulo wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 116331d98677SRui Paulo IEEE80211_FC0_SUBTYPE_BEACON; 116431d98677SRui Paulo wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 11658cfe5440SHans Petter Selasky USETW(wh->i_dur, 0); 116631d98677SRui Paulo IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); 116731d98677SRui Paulo IEEE80211_ADDR_COPY(wh->i_addr2, bss->macaddr); 116831d98677SRui Paulo IEEE80211_ADDR_COPY(wh->i_addr3, bss->macaddr); 116931d98677SRui Paulo *(uint16_t *)wh->i_seq = 0; 117031d98677SRui Paulo memcpy(&wh[1], (uint8_t *)&bss[1], le32toh(bss->ieslen)); 117131d98677SRui Paulo 117231d98677SRui Paulo /* Finalize mbuf. */ 117331d98677SRui Paulo m->m_pkthdr.len = m->m_len = pktlen; 117431d98677SRui Paulo m->m_pkthdr.rcvif = ifp; 117531d98677SRui Paulo /* Fix the channel. */ 117631d98677SRui Paulo c = ieee80211_find_channel_byieee(ic, 117731d98677SRui Paulo le32toh(bss->config.dsconfig), 117831d98677SRui Paulo IEEE80211_CHAN_G); 117931d98677SRui Paulo if (c) { 118031d98677SRui Paulo ic->ic_curchan = c; 118131d98677SRui Paulo ieee80211_radiotap_chan_change(ic); 118231d98677SRui Paulo } 118331d98677SRui Paulo /* XXX avoid a LOR */ 118431d98677SRui Paulo RSU_UNLOCK(sc); 118531d98677SRui Paulo ieee80211_input_all(ic, m, le32toh(bss->rssi), 0); 118631d98677SRui Paulo RSU_LOCK(sc); 118731d98677SRui Paulo } 118831d98677SRui Paulo 118931d98677SRui Paulo static void 119031d98677SRui Paulo rsu_event_join_bss(struct rsu_softc *sc, uint8_t *buf, int len) 119131d98677SRui Paulo { 119231d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 119331d98677SRui Paulo struct ieee80211com *ic = ifp->if_l2com; 119431d98677SRui Paulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 119531d98677SRui Paulo struct ieee80211_node *ni = vap->iv_bss; 119631d98677SRui Paulo struct r92s_event_join_bss *rsp; 1197bb03cd6fSHans Petter Selasky uint32_t tmp; 119831d98677SRui Paulo int res; 119931d98677SRui Paulo 120031d98677SRui Paulo if (__predict_false(len < sizeof(*rsp))) 120131d98677SRui Paulo return; 120231d98677SRui Paulo rsp = (struct r92s_event_join_bss *)buf; 120331d98677SRui Paulo res = (int)le32toh(rsp->join_res); 120431d98677SRui Paulo 120531d98677SRui Paulo DPRINTF("Rx join BSS event len=%d res=%d\n", len, res); 120631d98677SRui Paulo if (res <= 0) { 120731d98677SRui Paulo RSU_UNLOCK(sc); 120831d98677SRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 120931d98677SRui Paulo RSU_LOCK(sc); 121031d98677SRui Paulo return; 121131d98677SRui Paulo } 1212bb03cd6fSHans Petter Selasky tmp = le32toh(rsp->associd); 1213bb03cd6fSHans Petter Selasky if (tmp >= vap->iv_max_aid) { 1214bb03cd6fSHans Petter Selasky DPRINTF("Assoc ID overflow\n"); 1215bb03cd6fSHans Petter Selasky tmp = 1; 1216bb03cd6fSHans Petter Selasky } 121731d98677SRui Paulo DPRINTF("associated with %s associd=%d\n", 1218bb03cd6fSHans Petter Selasky ether_sprintf(rsp->bss.macaddr), tmp); 1219bb03cd6fSHans Petter Selasky ni->ni_associd = tmp | 0xc000; 122031d98677SRui Paulo RSU_UNLOCK(sc); 122131d98677SRui Paulo ieee80211_new_state(vap, IEEE80211_S_RUN, 122231d98677SRui Paulo IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 122331d98677SRui Paulo RSU_LOCK(sc); 122431d98677SRui Paulo } 122531d98677SRui Paulo 122631d98677SRui Paulo static void 122731d98677SRui Paulo rsu_rx_event(struct rsu_softc *sc, uint8_t code, uint8_t *buf, int len) 122831d98677SRui Paulo { 122931d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 123031d98677SRui Paulo struct ieee80211com *ic = ifp->if_l2com; 123131d98677SRui Paulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 123231d98677SRui Paulo 123331d98677SRui Paulo DPRINTFN(4, "Rx event code=%d len=%d\n", code, len); 123431d98677SRui Paulo switch (code) { 123531d98677SRui Paulo case R92S_EVT_SURVEY: 123631d98677SRui Paulo if (vap->iv_state == IEEE80211_S_SCAN) 123731d98677SRui Paulo rsu_event_survey(sc, buf, len); 123831d98677SRui Paulo break; 123931d98677SRui Paulo case R92S_EVT_SURVEY_DONE: 124031d98677SRui Paulo DPRINTF("site survey pass %d done, found %d BSS\n", 124131d98677SRui Paulo sc->scan_pass, le32toh(*(uint32_t *)buf)); 124231d98677SRui Paulo if (vap->iv_state != IEEE80211_S_SCAN) 124331d98677SRui Paulo break; /* Ignore if not scanning. */ 124431d98677SRui Paulo if (sc->scan_pass == 0 && vap->iv_des_nssid != 0) { 124531d98677SRui Paulo /* Schedule a directed scan for hidden APs. */ 124631d98677SRui Paulo sc->scan_pass = 1; 124731d98677SRui Paulo RSU_UNLOCK(sc); 124831d98677SRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 124931d98677SRui Paulo RSU_LOCK(sc); 125031d98677SRui Paulo break; 125131d98677SRui Paulo } 125231d98677SRui Paulo sc->scan_pass = 0; 125331d98677SRui Paulo break; 125431d98677SRui Paulo case R92S_EVT_JOIN_BSS: 125531d98677SRui Paulo if (vap->iv_state == IEEE80211_S_AUTH) 125631d98677SRui Paulo rsu_event_join_bss(sc, buf, len); 125731d98677SRui Paulo break; 1258910593b5SHans Petter Selasky #if 0 1259910593b5SHans Petter Selasky XXX This event is occurring regularly, possibly due to some power saving event 1260910593b5SHans Petter Selasky XXX and disrupts the WLAN traffic. Disable for now. 126131d98677SRui Paulo case R92S_EVT_DEL_STA: 126231d98677SRui Paulo DPRINTF("disassociated from %s\n", ether_sprintf(buf)); 126331d98677SRui Paulo if (vap->iv_state == IEEE80211_S_RUN && 126431d98677SRui Paulo IEEE80211_ADDR_EQ(vap->iv_bss->ni_bssid, buf)) { 126531d98677SRui Paulo RSU_UNLOCK(sc); 126631d98677SRui Paulo ieee80211_new_state(vap, IEEE80211_S_SCAN, -1); 126731d98677SRui Paulo RSU_LOCK(sc); 126831d98677SRui Paulo } 126931d98677SRui Paulo break; 1270910593b5SHans Petter Selasky #endif 127131d98677SRui Paulo case R92S_EVT_WPS_PBC: 127231d98677SRui Paulo DPRINTF("WPS PBC pushed.\n"); 127331d98677SRui Paulo break; 127431d98677SRui Paulo case R92S_EVT_FWDBG: 127531d98677SRui Paulo if (ifp->if_flags & IFF_DEBUG) { 127631d98677SRui Paulo buf[60] = '\0'; 127731d98677SRui Paulo printf("FWDBG: %s\n", (char *)buf); 127831d98677SRui Paulo } 127931d98677SRui Paulo break; 1280910593b5SHans Petter Selasky default: 1281910593b5SHans Petter Selasky break; 128231d98677SRui Paulo } 128331d98677SRui Paulo } 128431d98677SRui Paulo 128531d98677SRui Paulo static void 128631d98677SRui Paulo rsu_rx_multi_event(struct rsu_softc *sc, uint8_t *buf, int len) 128731d98677SRui Paulo { 128831d98677SRui Paulo struct r92s_fw_cmd_hdr *cmd; 128931d98677SRui Paulo int cmdsz; 129031d98677SRui Paulo 129131d98677SRui Paulo DPRINTFN(6, "Rx events len=%d\n", len); 129231d98677SRui Paulo 129331d98677SRui Paulo /* Skip Rx status. */ 129431d98677SRui Paulo buf += sizeof(struct r92s_rx_stat); 129531d98677SRui Paulo len -= sizeof(struct r92s_rx_stat); 129631d98677SRui Paulo 129731d98677SRui Paulo /* Process all events. */ 129831d98677SRui Paulo for (;;) { 129931d98677SRui Paulo /* Check that command header fits. */ 130031d98677SRui Paulo if (__predict_false(len < sizeof(*cmd))) 130131d98677SRui Paulo break; 130231d98677SRui Paulo cmd = (struct r92s_fw_cmd_hdr *)buf; 130331d98677SRui Paulo /* Check that command payload fits. */ 130431d98677SRui Paulo cmdsz = le16toh(cmd->len); 130531d98677SRui Paulo if (__predict_false(len < sizeof(*cmd) + cmdsz)) 130631d98677SRui Paulo break; 130731d98677SRui Paulo 130831d98677SRui Paulo /* Process firmware event. */ 130931d98677SRui Paulo rsu_rx_event(sc, cmd->code, (uint8_t *)&cmd[1], cmdsz); 131031d98677SRui Paulo 131131d98677SRui Paulo if (!(cmd->seq & R92S_FW_CMD_MORE)) 131231d98677SRui Paulo break; 131331d98677SRui Paulo buf += sizeof(*cmd) + cmdsz; 131431d98677SRui Paulo len -= sizeof(*cmd) + cmdsz; 131531d98677SRui Paulo } 131631d98677SRui Paulo } 131731d98677SRui Paulo 131831d98677SRui Paulo static int8_t 131931d98677SRui Paulo rsu_get_rssi(struct rsu_softc *sc, int rate, void *physt) 132031d98677SRui Paulo { 132131d98677SRui Paulo static const int8_t cckoff[] = { 14, -2, -20, -40 }; 132231d98677SRui Paulo struct r92s_rx_phystat *phy; 132331d98677SRui Paulo struct r92s_rx_cck *cck; 132431d98677SRui Paulo uint8_t rpt; 132531d98677SRui Paulo int8_t rssi; 132631d98677SRui Paulo 132731d98677SRui Paulo if (rate <= 3) { 132831d98677SRui Paulo cck = (struct r92s_rx_cck *)physt; 132931d98677SRui Paulo rpt = (cck->agc_rpt >> 6) & 0x3; 133031d98677SRui Paulo rssi = cck->agc_rpt & 0x3e; 133131d98677SRui Paulo rssi = cckoff[rpt] - rssi; 133231d98677SRui Paulo } else { /* OFDM/HT. */ 133331d98677SRui Paulo phy = (struct r92s_rx_phystat *)physt; 133431d98677SRui Paulo rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 106; 133531d98677SRui Paulo } 133631d98677SRui Paulo return (rssi); 133731d98677SRui Paulo } 133831d98677SRui Paulo 133931d98677SRui Paulo static struct mbuf * 134031d98677SRui Paulo rsu_rx_frame(struct rsu_softc *sc, uint8_t *buf, int pktlen, int *rssi) 134131d98677SRui Paulo { 134231d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 134331d98677SRui Paulo struct ieee80211com *ic = ifp->if_l2com; 134431d98677SRui Paulo struct ieee80211_frame *wh; 134531d98677SRui Paulo struct r92s_rx_stat *stat; 134631d98677SRui Paulo uint32_t rxdw0, rxdw3; 134731d98677SRui Paulo struct mbuf *m; 134831d98677SRui Paulo uint8_t rate; 134931d98677SRui Paulo int infosz; 135031d98677SRui Paulo 135131d98677SRui Paulo stat = (struct r92s_rx_stat *)buf; 135231d98677SRui Paulo rxdw0 = le32toh(stat->rxdw0); 135331d98677SRui Paulo rxdw3 = le32toh(stat->rxdw3); 135431d98677SRui Paulo 135531d98677SRui Paulo if (__predict_false(rxdw0 & R92S_RXDW0_CRCERR)) { 1356a001989dSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 135731d98677SRui Paulo return NULL; 135831d98677SRui Paulo } 135931d98677SRui Paulo if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) { 1360a001989dSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 136131d98677SRui Paulo return NULL; 136231d98677SRui Paulo } 136331d98677SRui Paulo 136431d98677SRui Paulo rate = MS(rxdw3, R92S_RXDW3_RATE); 136531d98677SRui Paulo infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8; 136631d98677SRui Paulo 136731d98677SRui Paulo /* Get RSSI from PHY status descriptor if present. */ 136831d98677SRui Paulo if (infosz != 0) 136931d98677SRui Paulo *rssi = rsu_get_rssi(sc, rate, &stat[1]); 137031d98677SRui Paulo else 137131d98677SRui Paulo *rssi = 0; 137231d98677SRui Paulo 137331d98677SRui Paulo DPRINTFN(5, "Rx frame len=%d rate=%d infosz=%d rssi=%d\n", 137431d98677SRui Paulo pktlen, rate, infosz, *rssi); 137531d98677SRui Paulo 1376107f00c0SKevin Lo m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR); 137731d98677SRui Paulo if (__predict_false(m == NULL)) { 1378a001989dSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 137931d98677SRui Paulo return NULL; 138031d98677SRui Paulo } 138131d98677SRui Paulo /* Finalize mbuf. */ 138231d98677SRui Paulo m->m_pkthdr.rcvif = ifp; 138331d98677SRui Paulo /* Hardware does Rx TCP checksum offload. */ 138431d98677SRui Paulo if (rxdw3 & R92S_RXDW3_TCPCHKVALID) { 138531d98677SRui Paulo if (__predict_true(rxdw3 & R92S_RXDW3_TCPCHKRPT)) 138631d98677SRui Paulo m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; 138731d98677SRui Paulo } 138831d98677SRui Paulo wh = (struct ieee80211_frame *)((uint8_t *)&stat[1] + infosz); 138931d98677SRui Paulo memcpy(mtod(m, uint8_t *), wh, pktlen); 139031d98677SRui Paulo m->m_pkthdr.len = m->m_len = pktlen; 139131d98677SRui Paulo 139231d98677SRui Paulo if (ieee80211_radiotap_active(ic)) { 139331d98677SRui Paulo struct rsu_rx_radiotap_header *tap = &sc->sc_rxtap; 139431d98677SRui Paulo 139531d98677SRui Paulo /* Map HW rate index to 802.11 rate. */ 139631d98677SRui Paulo tap->wr_flags = 2; 139731d98677SRui Paulo if (!(rxdw3 & R92S_RXDW3_HTC)) { 139831d98677SRui Paulo switch (rate) { 139931d98677SRui Paulo /* CCK. */ 140031d98677SRui Paulo case 0: tap->wr_rate = 2; break; 140131d98677SRui Paulo case 1: tap->wr_rate = 4; break; 140231d98677SRui Paulo case 2: tap->wr_rate = 11; break; 140331d98677SRui Paulo case 3: tap->wr_rate = 22; break; 140431d98677SRui Paulo /* OFDM. */ 140531d98677SRui Paulo case 4: tap->wr_rate = 12; break; 140631d98677SRui Paulo case 5: tap->wr_rate = 18; break; 140731d98677SRui Paulo case 6: tap->wr_rate = 24; break; 140831d98677SRui Paulo case 7: tap->wr_rate = 36; break; 140931d98677SRui Paulo case 8: tap->wr_rate = 48; break; 141031d98677SRui Paulo case 9: tap->wr_rate = 72; break; 141131d98677SRui Paulo case 10: tap->wr_rate = 96; break; 141231d98677SRui Paulo case 11: tap->wr_rate = 108; break; 141331d98677SRui Paulo } 141431d98677SRui Paulo } else if (rate >= 12) { /* MCS0~15. */ 141531d98677SRui Paulo /* Bit 7 set means HT MCS instead of rate. */ 141631d98677SRui Paulo tap->wr_rate = 0x80 | (rate - 12); 141731d98677SRui Paulo } 141831d98677SRui Paulo tap->wr_dbm_antsignal = *rssi; 141931d98677SRui Paulo tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); 142031d98677SRui Paulo tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); 142131d98677SRui Paulo } 142231d98677SRui Paulo 142331d98677SRui Paulo return (m); 142431d98677SRui Paulo } 142531d98677SRui Paulo 142631d98677SRui Paulo static struct mbuf * 142731d98677SRui Paulo rsu_rx_multi_frame(struct rsu_softc *sc, uint8_t *buf, int len, int *rssi) 142831d98677SRui Paulo { 142931d98677SRui Paulo struct r92s_rx_stat *stat; 143031d98677SRui Paulo uint32_t rxdw0; 143131d98677SRui Paulo int totlen, pktlen, infosz, npkts; 143231d98677SRui Paulo struct mbuf *m, *m0 = NULL, *prevm = NULL; 143331d98677SRui Paulo 143431d98677SRui Paulo /* Get the number of encapsulated frames. */ 143531d98677SRui Paulo stat = (struct r92s_rx_stat *)buf; 143631d98677SRui Paulo npkts = MS(le32toh(stat->rxdw2), R92S_RXDW2_PKTCNT); 143731d98677SRui Paulo DPRINTFN(6, "Rx %d frames in one chunk\n", npkts); 143831d98677SRui Paulo 143931d98677SRui Paulo /* Process all of them. */ 144031d98677SRui Paulo while (npkts-- > 0) { 144131d98677SRui Paulo if (__predict_false(len < sizeof(*stat))) 144231d98677SRui Paulo break; 144331d98677SRui Paulo stat = (struct r92s_rx_stat *)buf; 144431d98677SRui Paulo rxdw0 = le32toh(stat->rxdw0); 144531d98677SRui Paulo 144631d98677SRui Paulo pktlen = MS(rxdw0, R92S_RXDW0_PKTLEN); 144731d98677SRui Paulo if (__predict_false(pktlen == 0)) 144831d98677SRui Paulo break; 144931d98677SRui Paulo 145031d98677SRui Paulo infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8; 145131d98677SRui Paulo 145231d98677SRui Paulo /* Make sure everything fits in xfer. */ 145331d98677SRui Paulo totlen = sizeof(*stat) + infosz + pktlen; 145431d98677SRui Paulo if (__predict_false(totlen > len)) 145531d98677SRui Paulo break; 145631d98677SRui Paulo 145731d98677SRui Paulo /* Process 802.11 frame. */ 145831d98677SRui Paulo m = rsu_rx_frame(sc, buf, pktlen, rssi); 145931d98677SRui Paulo if (m0 == NULL) 146031d98677SRui Paulo m0 = m; 146131d98677SRui Paulo if (prevm == NULL) 146231d98677SRui Paulo prevm = m; 146331d98677SRui Paulo else { 146431d98677SRui Paulo prevm->m_next = m; 146531d98677SRui Paulo prevm = m; 146631d98677SRui Paulo } 146731d98677SRui Paulo /* Next chunk is 128-byte aligned. */ 146831d98677SRui Paulo totlen = (totlen + 127) & ~127; 146931d98677SRui Paulo buf += totlen; 147031d98677SRui Paulo len -= totlen; 147131d98677SRui Paulo } 147231d98677SRui Paulo 147331d98677SRui Paulo return (m0); 147431d98677SRui Paulo } 147531d98677SRui Paulo 147631d98677SRui Paulo static struct mbuf * 147731d98677SRui Paulo rsu_rxeof(struct usb_xfer *xfer, struct rsu_data *data, int *rssi) 147831d98677SRui Paulo { 147931d98677SRui Paulo struct rsu_softc *sc = data->sc; 148031d98677SRui Paulo struct r92s_rx_stat *stat; 148131d98677SRui Paulo int len; 148231d98677SRui Paulo 148331d98677SRui Paulo usbd_xfer_status(xfer, &len, NULL, NULL, NULL); 148431d98677SRui Paulo 148531d98677SRui Paulo if (__predict_false(len < sizeof(*stat))) { 148631d98677SRui Paulo DPRINTF("xfer too short %d\n", len); 1487a001989dSGleb Smirnoff if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1); 148831d98677SRui Paulo return (NULL); 148931d98677SRui Paulo } 149031d98677SRui Paulo /* Determine if it is a firmware C2H event or an 802.11 frame. */ 149131d98677SRui Paulo stat = (struct r92s_rx_stat *)data->buf; 149231d98677SRui Paulo if ((le32toh(stat->rxdw1) & 0x1ff) == 0x1ff) { 149331d98677SRui Paulo rsu_rx_multi_event(sc, data->buf, len); 149431d98677SRui Paulo /* No packets to process. */ 149531d98677SRui Paulo return (NULL); 149631d98677SRui Paulo } else 149731d98677SRui Paulo return (rsu_rx_multi_frame(sc, data->buf, len, rssi)); 149831d98677SRui Paulo } 149931d98677SRui Paulo 150031d98677SRui Paulo static void 150131d98677SRui Paulo rsu_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) 150231d98677SRui Paulo { 150331d98677SRui Paulo struct rsu_softc *sc = usbd_xfer_softc(xfer); 150431d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 150531d98677SRui Paulo struct ieee80211com *ic = ifp->if_l2com; 150631d98677SRui Paulo struct ieee80211_frame *wh; 150731d98677SRui Paulo struct ieee80211_node *ni; 150831d98677SRui Paulo struct mbuf *m = NULL, *next; 150931d98677SRui Paulo struct rsu_data *data; 151031d98677SRui Paulo int rssi = 1; 151131d98677SRui Paulo 151231d98677SRui Paulo RSU_ASSERT_LOCKED(sc); 151331d98677SRui Paulo 151431d98677SRui Paulo switch (USB_GET_STATE(xfer)) { 151531d98677SRui Paulo case USB_ST_TRANSFERRED: 151631d98677SRui Paulo data = STAILQ_FIRST(&sc->sc_rx_active); 151731d98677SRui Paulo if (data == NULL) 151831d98677SRui Paulo goto tr_setup; 151931d98677SRui Paulo STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); 152031d98677SRui Paulo m = rsu_rxeof(xfer, data, &rssi); 152131d98677SRui Paulo STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); 152231d98677SRui Paulo /* FALLTHROUGH */ 152331d98677SRui Paulo case USB_ST_SETUP: 152431d98677SRui Paulo tr_setup: 152531d98677SRui Paulo data = STAILQ_FIRST(&sc->sc_rx_inactive); 152631d98677SRui Paulo if (data == NULL) { 152731d98677SRui Paulo KASSERT(m == NULL, ("mbuf isn't NULL")); 152831d98677SRui Paulo return; 152931d98677SRui Paulo } 153031d98677SRui Paulo STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); 153131d98677SRui Paulo STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); 153231d98677SRui Paulo usbd_xfer_set_frame_data(xfer, 0, data->buf, 153331d98677SRui Paulo usbd_xfer_max_len(xfer)); 153431d98677SRui Paulo usbd_transfer_submit(xfer); 153531d98677SRui Paulo /* 153631d98677SRui Paulo * To avoid LOR we should unlock our private mutex here to call 153731d98677SRui Paulo * ieee80211_input() because here is at the end of a USB 153831d98677SRui Paulo * callback and safe to unlock. 153931d98677SRui Paulo */ 154031d98677SRui Paulo RSU_UNLOCK(sc); 154131d98677SRui Paulo while (m != NULL) { 154231d98677SRui Paulo next = m->m_next; 154331d98677SRui Paulo m->m_next = NULL; 154431d98677SRui Paulo wh = mtod(m, struct ieee80211_frame *); 154531d98677SRui Paulo ni = ieee80211_find_rxnode(ic, 154631d98677SRui Paulo (struct ieee80211_frame_min *)wh); 154731d98677SRui Paulo if (ni != NULL) { 154831d98677SRui Paulo (void)ieee80211_input(ni, m, rssi, 0); 154931d98677SRui Paulo ieee80211_free_node(ni); 155031d98677SRui Paulo } else 155131d98677SRui Paulo (void)ieee80211_input_all(ic, m, rssi, 0); 155231d98677SRui Paulo m = next; 155331d98677SRui Paulo } 155431d98677SRui Paulo RSU_LOCK(sc); 155531d98677SRui Paulo break; 155631d98677SRui Paulo default: 155731d98677SRui Paulo /* needs it to the inactive queue due to a error. */ 155831d98677SRui Paulo data = STAILQ_FIRST(&sc->sc_rx_active); 155931d98677SRui Paulo if (data != NULL) { 156031d98677SRui Paulo STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); 156131d98677SRui Paulo STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); 156231d98677SRui Paulo } 156331d98677SRui Paulo if (error != USB_ERR_CANCELLED) { 156431d98677SRui Paulo usbd_xfer_set_stall(xfer); 1565a001989dSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 156631d98677SRui Paulo goto tr_setup; 156731d98677SRui Paulo } 156831d98677SRui Paulo break; 156931d98677SRui Paulo } 157031d98677SRui Paulo 157131d98677SRui Paulo } 157231d98677SRui Paulo 157331d98677SRui Paulo 157431d98677SRui Paulo static void 157531d98677SRui Paulo rsu_txeof(struct usb_xfer *xfer, struct rsu_data *data) 157631d98677SRui Paulo { 157731d98677SRui Paulo struct rsu_softc *sc = usbd_xfer_softc(xfer); 157831d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 157931d98677SRui Paulo struct mbuf *m; 158031d98677SRui Paulo 158131d98677SRui Paulo RSU_ASSERT_LOCKED(sc); 158231d98677SRui Paulo 158331d98677SRui Paulo /* 158431d98677SRui Paulo * Do any tx complete callback. Note this must be done before releasing 158531d98677SRui Paulo * the node reference. 158631d98677SRui Paulo */ 158731d98677SRui Paulo if (data->m) { 158831d98677SRui Paulo m = data->m; 158931d98677SRui Paulo if (m->m_flags & M_TXCB) { 159031d98677SRui Paulo /* XXX status? */ 159131d98677SRui Paulo ieee80211_process_callback(data->ni, m, 0); 159231d98677SRui Paulo } 159331d98677SRui Paulo m_freem(m); 159431d98677SRui Paulo data->m = NULL; 159531d98677SRui Paulo } 159631d98677SRui Paulo if (data->ni) { 159731d98677SRui Paulo ieee80211_free_node(data->ni); 159831d98677SRui Paulo data->ni = NULL; 159931d98677SRui Paulo } 1600a001989dSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 160131d98677SRui Paulo ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 160231d98677SRui Paulo } 160331d98677SRui Paulo 160431d98677SRui Paulo static void 1605400b4e53SHans Petter Selasky rsu_bulk_tx_callback_sub(struct usb_xfer *xfer, usb_error_t error, 1606400b4e53SHans Petter Selasky uint8_t which) 160731d98677SRui Paulo { 160831d98677SRui Paulo struct rsu_softc *sc = usbd_xfer_softc(xfer); 160931d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 161031d98677SRui Paulo struct rsu_data *data; 161131d98677SRui Paulo 161231d98677SRui Paulo RSU_ASSERT_LOCKED(sc); 161331d98677SRui Paulo 161431d98677SRui Paulo switch (USB_GET_STATE(xfer)) { 161531d98677SRui Paulo case USB_ST_TRANSFERRED: 1616400b4e53SHans Petter Selasky data = STAILQ_FIRST(&sc->sc_tx_active[which]); 161731d98677SRui Paulo if (data == NULL) 161831d98677SRui Paulo goto tr_setup; 161931d98677SRui Paulo DPRINTF("transfer done %p\n", data); 1620400b4e53SHans Petter Selasky STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); 162131d98677SRui Paulo rsu_txeof(xfer, data); 162231d98677SRui Paulo STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); 162331d98677SRui Paulo /* FALLTHROUGH */ 162431d98677SRui Paulo case USB_ST_SETUP: 162531d98677SRui Paulo tr_setup: 1626400b4e53SHans Petter Selasky data = STAILQ_FIRST(&sc->sc_tx_pending[which]); 162731d98677SRui Paulo if (data == NULL) { 162831d98677SRui Paulo DPRINTF("empty pending queue sc %p\n", sc); 162931d98677SRui Paulo return; 163031d98677SRui Paulo } 1631400b4e53SHans Petter Selasky STAILQ_REMOVE_HEAD(&sc->sc_tx_pending[which], next); 1632400b4e53SHans Petter Selasky STAILQ_INSERT_TAIL(&sc->sc_tx_active[which], data, next); 163331d98677SRui Paulo usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); 163431d98677SRui Paulo DPRINTF("submitting transfer %p\n", data); 163531d98677SRui Paulo usbd_transfer_submit(xfer); 163631d98677SRui Paulo break; 163731d98677SRui Paulo default: 1638400b4e53SHans Petter Selasky data = STAILQ_FIRST(&sc->sc_tx_active[which]); 1639400b4e53SHans Petter Selasky if (data != NULL) { 1640400b4e53SHans Petter Selasky STAILQ_REMOVE_HEAD(&sc->sc_tx_active[which], next); 1641400b4e53SHans Petter Selasky rsu_txeof(xfer, data); 1642400b4e53SHans Petter Selasky STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); 164331d98677SRui Paulo } 1644a001989dSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 1645400b4e53SHans Petter Selasky 164631d98677SRui Paulo if (error != USB_ERR_CANCELLED) { 164731d98677SRui Paulo usbd_xfer_set_stall(xfer); 164831d98677SRui Paulo goto tr_setup; 164931d98677SRui Paulo } 165031d98677SRui Paulo break; 165131d98677SRui Paulo } 165231d98677SRui Paulo } 165331d98677SRui Paulo 1654400b4e53SHans Petter Selasky static void 1655910593b5SHans Petter Selasky rsu_bulk_tx_callback_be_bk(struct usb_xfer *xfer, usb_error_t error) 1656400b4e53SHans Petter Selasky { 1657910593b5SHans Petter Selasky rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_BE_BK); 1658400b4e53SHans Petter Selasky } 1659400b4e53SHans Petter Selasky 1660400b4e53SHans Petter Selasky static void 1661910593b5SHans Petter Selasky rsu_bulk_tx_callback_vi_vo(struct usb_xfer *xfer, usb_error_t error) 1662400b4e53SHans Petter Selasky { 1663910593b5SHans Petter Selasky rsu_bulk_tx_callback_sub(xfer, error, RSU_BULK_TX_VI_VO); 1664400b4e53SHans Petter Selasky } 1665400b4e53SHans Petter Selasky 166631d98677SRui Paulo static int 166731d98677SRui Paulo rsu_tx_start(struct rsu_softc *sc, struct ieee80211_node *ni, 1668400b4e53SHans Petter Selasky struct mbuf *m0, struct rsu_data *data) 166931d98677SRui Paulo { 167031d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 167131d98677SRui Paulo struct ieee80211com *ic = ifp->if_l2com; 167231d98677SRui Paulo struct ieee80211vap *vap = ni->ni_vap; 167331d98677SRui Paulo struct ieee80211_frame *wh; 167431d98677SRui Paulo struct ieee80211_key *k = NULL; 167531d98677SRui Paulo struct r92s_tx_desc *txd; 1676400b4e53SHans Petter Selasky uint8_t type; 1677400b4e53SHans Petter Selasky uint8_t tid = 0; 1678400b4e53SHans Petter Selasky uint8_t which; 1679400b4e53SHans Petter Selasky int hasqos; 1680400b4e53SHans Petter Selasky int xferlen; 168131d98677SRui Paulo 168231d98677SRui Paulo RSU_ASSERT_LOCKED(sc); 168331d98677SRui Paulo 168431d98677SRui Paulo wh = mtod(m0, struct ieee80211_frame *); 168531d98677SRui Paulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 168631d98677SRui Paulo 16875945b5f5SKevin Lo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 168831d98677SRui Paulo k = ieee80211_crypto_encap(ni, m0); 168931d98677SRui Paulo if (k == NULL) { 169031d98677SRui Paulo device_printf(sc->sc_dev, 169131d98677SRui Paulo "ieee80211_crypto_encap returns NULL.\n"); 169231d98677SRui Paulo /* XXX we don't expect the fragmented frames */ 169331d98677SRui Paulo m_freem(m0); 169431d98677SRui Paulo return (ENOBUFS); 169531d98677SRui Paulo } 169631d98677SRui Paulo wh = mtod(m0, struct ieee80211_frame *); 169731d98677SRui Paulo } 169831d98677SRui Paulo switch (type) { 169931d98677SRui Paulo case IEEE80211_FC0_TYPE_CTL: 170031d98677SRui Paulo case IEEE80211_FC0_TYPE_MGT: 1701910593b5SHans Petter Selasky which = rsu_wme_ac_xfer_map[WME_AC_VO]; 170231d98677SRui Paulo break; 170331d98677SRui Paulo default: 1704910593b5SHans Petter Selasky which = rsu_wme_ac_xfer_map[M_WME_GETAC(m0)]; 170531d98677SRui Paulo break; 170631d98677SRui Paulo } 170731d98677SRui Paulo hasqos = 0; 170831d98677SRui Paulo 170931d98677SRui Paulo /* Fill Tx descriptor. */ 171031d98677SRui Paulo txd = (struct r92s_tx_desc *)data->buf; 171131d98677SRui Paulo memset(txd, 0, sizeof(*txd)); 171231d98677SRui Paulo 171331d98677SRui Paulo txd->txdw0 |= htole32( 171431d98677SRui Paulo SM(R92S_TXDW0_PKTLEN, m0->m_pkthdr.len) | 171531d98677SRui Paulo SM(R92S_TXDW0_OFFSET, sizeof(*txd)) | 171631d98677SRui Paulo R92S_TXDW0_OWN | R92S_TXDW0_FSG | R92S_TXDW0_LSG); 171731d98677SRui Paulo 171831d98677SRui Paulo txd->txdw1 |= htole32( 171931d98677SRui Paulo SM(R92S_TXDW1_MACID, R92S_MACID_BSS) | 172031d98677SRui Paulo SM(R92S_TXDW1_QSEL, R92S_TXDW1_QSEL_BE)); 172131d98677SRui Paulo if (!hasqos) 172231d98677SRui Paulo txd->txdw1 |= htole32(R92S_TXDW1_NONQOS); 172331d98677SRui Paulo #ifdef notyet 172431d98677SRui Paulo if (k != NULL) { 172531d98677SRui Paulo switch (k->wk_cipher->ic_cipher) { 172631d98677SRui Paulo case IEEE80211_CIPHER_WEP: 172731d98677SRui Paulo cipher = R92S_TXDW1_CIPHER_WEP; 172831d98677SRui Paulo break; 172931d98677SRui Paulo case IEEE80211_CIPHER_TKIP: 173031d98677SRui Paulo cipher = R92S_TXDW1_CIPHER_TKIP; 173131d98677SRui Paulo break; 173231d98677SRui Paulo case IEEE80211_CIPHER_AES_CCM: 173331d98677SRui Paulo cipher = R92S_TXDW1_CIPHER_AES; 173431d98677SRui Paulo break; 173531d98677SRui Paulo default: 173631d98677SRui Paulo cipher = R92S_TXDW1_CIPHER_NONE; 173731d98677SRui Paulo } 173831d98677SRui Paulo txd->txdw1 |= htole32( 173931d98677SRui Paulo SM(R92S_TXDW1_CIPHER, cipher) | 174031d98677SRui Paulo SM(R92S_TXDW1_KEYIDX, k->k_id)); 174131d98677SRui Paulo } 174231d98677SRui Paulo #endif 174331d98677SRui Paulo txd->txdw2 |= htole32(R92S_TXDW2_BK); 174431d98677SRui Paulo if (IEEE80211_IS_MULTICAST(wh->i_addr1)) 174531d98677SRui Paulo txd->txdw2 |= htole32(R92S_TXDW2_BMCAST); 174631d98677SRui Paulo /* 174731d98677SRui Paulo * Firmware will use and increment the sequence number for the 174831d98677SRui Paulo * specified TID. 174931d98677SRui Paulo */ 175031d98677SRui Paulo txd->txdw3 |= htole32(SM(R92S_TXDW3_SEQ, tid)); 175131d98677SRui Paulo 175231d98677SRui Paulo if (ieee80211_radiotap_active_vap(vap)) { 175331d98677SRui Paulo struct rsu_tx_radiotap_header *tap = &sc->sc_txtap; 175431d98677SRui Paulo 175531d98677SRui Paulo tap->wt_flags = 0; 175631d98677SRui Paulo tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); 175731d98677SRui Paulo tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); 175831d98677SRui Paulo ieee80211_radiotap_tx(vap, m0); 175931d98677SRui Paulo } 176031d98677SRui Paulo xferlen = sizeof(*txd) + m0->m_pkthdr.len; 176131d98677SRui Paulo m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&txd[1]); 176231d98677SRui Paulo 176331d98677SRui Paulo data->buflen = xferlen; 176431d98677SRui Paulo data->ni = ni; 176531d98677SRui Paulo data->m = m0; 1766400b4e53SHans Petter Selasky STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); 176731d98677SRui Paulo 1768400b4e53SHans Petter Selasky /* start transfer, if any */ 1769910593b5SHans Petter Selasky usbd_transfer_start(sc->sc_xfer[which]); 177031d98677SRui Paulo return (0); 177131d98677SRui Paulo } 177231d98677SRui Paulo 177331d98677SRui Paulo static void 177431d98677SRui Paulo rsu_start(struct ifnet *ifp) 177531d98677SRui Paulo { 177631d98677SRui Paulo struct rsu_softc *sc = ifp->if_softc; 177731d98677SRui Paulo 177831d98677SRui Paulo if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 177931d98677SRui Paulo return; 178031d98677SRui Paulo 178131d98677SRui Paulo RSU_LOCK(sc); 1782400b4e53SHans Petter Selasky rsu_start_locked(ifp); 178331d98677SRui Paulo RSU_UNLOCK(sc); 178431d98677SRui Paulo } 178531d98677SRui Paulo 178631d98677SRui Paulo static void 1787400b4e53SHans Petter Selasky rsu_start_locked(struct ifnet *ifp) 178831d98677SRui Paulo { 178931d98677SRui Paulo struct rsu_softc *sc = ifp->if_softc; 179031d98677SRui Paulo struct ieee80211_node *ni; 179131d98677SRui Paulo struct rsu_data *bf; 1792400b4e53SHans Petter Selasky struct mbuf *m; 179331d98677SRui Paulo 179431d98677SRui Paulo RSU_ASSERT_LOCKED(sc); 179531d98677SRui Paulo 179631d98677SRui Paulo for (;;) { 179731d98677SRui Paulo IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 179831d98677SRui Paulo if (m == NULL) 179931d98677SRui Paulo break; 180031d98677SRui Paulo ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 180131d98677SRui Paulo m->m_pkthdr.rcvif = NULL; 180231d98677SRui Paulo 1803400b4e53SHans Petter Selasky bf = rsu_getbuf(sc); 1804400b4e53SHans Petter Selasky if (bf == NULL) { 1805a001989dSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 1806400b4e53SHans Petter Selasky m_freem(m); 1807400b4e53SHans Petter Selasky ieee80211_free_node(ni); 1808400b4e53SHans Petter Selasky } else if (rsu_tx_start(sc, ni, m, bf) != 0) { 1809a001989dSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 181031d98677SRui Paulo STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); 181131d98677SRui Paulo ieee80211_free_node(ni); 181231d98677SRui Paulo } 181331d98677SRui Paulo } 181431d98677SRui Paulo } 181531d98677SRui Paulo 181631d98677SRui Paulo static int 181731d98677SRui Paulo rsu_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 181831d98677SRui Paulo { 181931d98677SRui Paulo struct ieee80211com *ic = ifp->if_l2com; 182031d98677SRui Paulo struct ifreq *ifr = (struct ifreq *) data; 182131d98677SRui Paulo int error = 0, startall = 0; 182231d98677SRui Paulo 182331d98677SRui Paulo switch (cmd) { 182431d98677SRui Paulo case SIOCSIFFLAGS: 182531d98677SRui Paulo if (ifp->if_flags & IFF_UP) { 182631d98677SRui Paulo if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 182731d98677SRui Paulo rsu_init(ifp->if_softc); 182831d98677SRui Paulo startall = 1; 182931d98677SRui Paulo } 183031d98677SRui Paulo } else { 183131d98677SRui Paulo if (ifp->if_drv_flags & IFF_DRV_RUNNING) 183231d98677SRui Paulo rsu_stop(ifp, 1); 183331d98677SRui Paulo } 183431d98677SRui Paulo if (startall) 183531d98677SRui Paulo ieee80211_start_all(ic); 183631d98677SRui Paulo break; 183731d98677SRui Paulo case SIOCGIFMEDIA: 183831d98677SRui Paulo error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); 183931d98677SRui Paulo break; 184031d98677SRui Paulo case SIOCGIFADDR: 184131d98677SRui Paulo error = ether_ioctl(ifp, cmd, data); 184231d98677SRui Paulo break; 184331d98677SRui Paulo default: 184431d98677SRui Paulo error = EINVAL; 184531d98677SRui Paulo break; 184631d98677SRui Paulo } 184731d98677SRui Paulo 184831d98677SRui Paulo return (error); 184931d98677SRui Paulo } 185031d98677SRui Paulo 185131d98677SRui Paulo /* 185231d98677SRui Paulo * Power on sequence for A-cut adapters. 185331d98677SRui Paulo */ 185431d98677SRui Paulo static void 185531d98677SRui Paulo rsu_power_on_acut(struct rsu_softc *sc) 185631d98677SRui Paulo { 185731d98677SRui Paulo uint32_t reg; 185831d98677SRui Paulo 185931d98677SRui Paulo rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53); 186031d98677SRui Paulo rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57); 186131d98677SRui Paulo 186231d98677SRui Paulo /* Enable AFE macro block's bandgap and Mbias. */ 186331d98677SRui Paulo rsu_write_1(sc, R92S_AFE_MISC, 186431d98677SRui Paulo rsu_read_1(sc, R92S_AFE_MISC) | 186531d98677SRui Paulo R92S_AFE_MISC_BGEN | R92S_AFE_MISC_MBEN); 186631d98677SRui Paulo /* Enable LDOA15 block. */ 186731d98677SRui Paulo rsu_write_1(sc, R92S_LDOA15_CTRL, 186831d98677SRui Paulo rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN); 186931d98677SRui Paulo 187031d98677SRui Paulo rsu_write_1(sc, R92S_SPS1_CTRL, 187131d98677SRui Paulo rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_LDEN); 187231d98677SRui Paulo usb_pause_mtx(&sc->sc_mtx, 2 * hz); 187331d98677SRui Paulo /* Enable switch regulator block. */ 187431d98677SRui Paulo rsu_write_1(sc, R92S_SPS1_CTRL, 187531d98677SRui Paulo rsu_read_1(sc, R92S_SPS1_CTRL) | R92S_SPS1_SWEN); 187631d98677SRui Paulo 187731d98677SRui Paulo rsu_write_4(sc, R92S_SPS1_CTRL, 0x00a7b267); 187831d98677SRui Paulo 187931d98677SRui Paulo rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 188031d98677SRui Paulo rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08); 188131d98677SRui Paulo 188231d98677SRui Paulo rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 188331d98677SRui Paulo rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20); 188431d98677SRui Paulo 188531d98677SRui Paulo rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 188631d98677SRui Paulo rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x90); 188731d98677SRui Paulo 188831d98677SRui Paulo /* Enable AFE clock. */ 188931d98677SRui Paulo rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1, 189031d98677SRui Paulo rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04); 189131d98677SRui Paulo /* Enable AFE PLL macro block. */ 189231d98677SRui Paulo rsu_write_1(sc, R92S_AFE_PLL_CTRL, 189331d98677SRui Paulo rsu_read_1(sc, R92S_AFE_PLL_CTRL) | 0x11); 189431d98677SRui Paulo /* Attach AFE PLL to MACTOP/BB. */ 189531d98677SRui Paulo rsu_write_1(sc, R92S_SYS_ISO_CTRL, 189631d98677SRui Paulo rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11); 189731d98677SRui Paulo 189831d98677SRui Paulo /* Switch to 40MHz clock instead of 80MHz. */ 189931d98677SRui Paulo rsu_write_2(sc, R92S_SYS_CLKR, 190031d98677SRui Paulo rsu_read_2(sc, R92S_SYS_CLKR) & ~R92S_SYS_CLKSEL); 190131d98677SRui Paulo 190231d98677SRui Paulo /* Enable MAC clock. */ 190331d98677SRui Paulo rsu_write_2(sc, R92S_SYS_CLKR, 190431d98677SRui Paulo rsu_read_2(sc, R92S_SYS_CLKR) | 190531d98677SRui Paulo R92S_MAC_CLK_EN | R92S_SYS_CLK_EN); 190631d98677SRui Paulo 190731d98677SRui Paulo rsu_write_1(sc, R92S_PMC_FSM, 0x02); 190831d98677SRui Paulo 190931d98677SRui Paulo /* Enable digital core and IOREG R/W. */ 191031d98677SRui Paulo rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 191131d98677SRui Paulo rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08); 191231d98677SRui Paulo 191331d98677SRui Paulo rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 191431d98677SRui Paulo rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80); 191531d98677SRui Paulo 191631d98677SRui Paulo /* Switch the control path to firmware. */ 191731d98677SRui Paulo reg = rsu_read_2(sc, R92S_SYS_CLKR); 191831d98677SRui Paulo reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL; 191931d98677SRui Paulo rsu_write_2(sc, R92S_SYS_CLKR, reg); 192031d98677SRui Paulo 192131d98677SRui Paulo rsu_write_2(sc, R92S_CR, 0x37fc); 192231d98677SRui Paulo 192331d98677SRui Paulo /* Fix USB RX FIFO issue. */ 192431d98677SRui Paulo rsu_write_1(sc, 0xfe5c, 192531d98677SRui Paulo rsu_read_1(sc, 0xfe5c) | 0x80); 192631d98677SRui Paulo rsu_write_1(sc, 0x00ab, 192731d98677SRui Paulo rsu_read_1(sc, 0x00ab) | 0xc0); 192831d98677SRui Paulo 192931d98677SRui Paulo rsu_write_1(sc, R92S_SYS_CLKR, 193031d98677SRui Paulo rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL); 193131d98677SRui Paulo } 193231d98677SRui Paulo 193331d98677SRui Paulo /* 193431d98677SRui Paulo * Power on sequence for B-cut and C-cut adapters. 193531d98677SRui Paulo */ 193631d98677SRui Paulo static void 193731d98677SRui Paulo rsu_power_on_bcut(struct rsu_softc *sc) 193831d98677SRui Paulo { 193931d98677SRui Paulo uint32_t reg; 194031d98677SRui Paulo int ntries; 194131d98677SRui Paulo 194231d98677SRui Paulo /* Prevent eFuse leakage. */ 194331d98677SRui Paulo rsu_write_1(sc, 0x37, 0xb0); 1944400b4e53SHans Petter Selasky usb_pause_mtx(&sc->sc_mtx, hz / 100); 194531d98677SRui Paulo rsu_write_1(sc, 0x37, 0x30); 194631d98677SRui Paulo 194731d98677SRui Paulo /* Switch the control path to hardware. */ 194831d98677SRui Paulo reg = rsu_read_2(sc, R92S_SYS_CLKR); 194931d98677SRui Paulo if (reg & R92S_FWHW_SEL) { 195031d98677SRui Paulo rsu_write_2(sc, R92S_SYS_CLKR, 195131d98677SRui Paulo reg & ~(R92S_SWHW_SEL | R92S_FWHW_SEL)); 195231d98677SRui Paulo } 195331d98677SRui Paulo rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 195431d98677SRui Paulo rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) & ~0x8c); 1955b41381aeSHans Petter Selasky rsu_ms_delay(sc); 195631d98677SRui Paulo 195731d98677SRui Paulo rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x53); 195831d98677SRui Paulo rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x57); 195931d98677SRui Paulo 196031d98677SRui Paulo reg = rsu_read_1(sc, R92S_AFE_MISC); 196131d98677SRui Paulo rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN); 196231d98677SRui Paulo rsu_write_1(sc, R92S_AFE_MISC, reg | R92S_AFE_MISC_BGEN | 196331d98677SRui Paulo R92S_AFE_MISC_MBEN | R92S_AFE_MISC_I32_EN); 196431d98677SRui Paulo 196531d98677SRui Paulo /* Enable PLL. */ 196631d98677SRui Paulo rsu_write_1(sc, R92S_LDOA15_CTRL, 196731d98677SRui Paulo rsu_read_1(sc, R92S_LDOA15_CTRL) | R92S_LDA15_EN); 196831d98677SRui Paulo 196931d98677SRui Paulo rsu_write_1(sc, R92S_LDOV12D_CTRL, 197031d98677SRui Paulo rsu_read_1(sc, R92S_LDOV12D_CTRL) | R92S_LDV12_EN); 197131d98677SRui Paulo 197231d98677SRui Paulo rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 197331d98677SRui Paulo rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) | 0x08); 197431d98677SRui Paulo 197531d98677SRui Paulo rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 197631d98677SRui Paulo rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x20); 197731d98677SRui Paulo 197831d98677SRui Paulo /* Support 64KB IMEM. */ 197931d98677SRui Paulo rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 198031d98677SRui Paulo rsu_read_1(sc, R92S_SYS_ISO_CTRL + 1) & ~0x97); 198131d98677SRui Paulo 198231d98677SRui Paulo /* Enable AFE clock. */ 198331d98677SRui Paulo rsu_write_1(sc, R92S_AFE_XTAL_CTRL + 1, 198431d98677SRui Paulo rsu_read_1(sc, R92S_AFE_XTAL_CTRL + 1) & ~0x04); 198531d98677SRui Paulo /* Enable AFE PLL macro block. */ 198631d98677SRui Paulo reg = rsu_read_1(sc, R92S_AFE_PLL_CTRL); 198731d98677SRui Paulo rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11); 1988b41381aeSHans Petter Selasky rsu_ms_delay(sc); 198931d98677SRui Paulo rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x51); 1990b41381aeSHans Petter Selasky rsu_ms_delay(sc); 199131d98677SRui Paulo rsu_write_1(sc, R92S_AFE_PLL_CTRL, reg | 0x11); 1992b41381aeSHans Petter Selasky rsu_ms_delay(sc); 199331d98677SRui Paulo 199431d98677SRui Paulo /* Attach AFE PLL to MACTOP/BB. */ 199531d98677SRui Paulo rsu_write_1(sc, R92S_SYS_ISO_CTRL, 199631d98677SRui Paulo rsu_read_1(sc, R92S_SYS_ISO_CTRL) & ~0x11); 199731d98677SRui Paulo 199831d98677SRui Paulo /* Switch to 40MHz clock. */ 199931d98677SRui Paulo rsu_write_1(sc, R92S_SYS_CLKR, 0x00); 200031d98677SRui Paulo /* Disable CPU clock and 80MHz SSC. */ 200131d98677SRui Paulo rsu_write_1(sc, R92S_SYS_CLKR, 200231d98677SRui Paulo rsu_read_1(sc, R92S_SYS_CLKR) | 0xa0); 200331d98677SRui Paulo /* Enable MAC clock. */ 200431d98677SRui Paulo rsu_write_2(sc, R92S_SYS_CLKR, 200531d98677SRui Paulo rsu_read_2(sc, R92S_SYS_CLKR) | 200631d98677SRui Paulo R92S_MAC_CLK_EN | R92S_SYS_CLK_EN); 200731d98677SRui Paulo 200831d98677SRui Paulo rsu_write_1(sc, R92S_PMC_FSM, 0x02); 200931d98677SRui Paulo 201031d98677SRui Paulo /* Enable digital core and IOREG R/W. */ 201131d98677SRui Paulo rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 201231d98677SRui Paulo rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x08); 201331d98677SRui Paulo 201431d98677SRui Paulo rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 201531d98677SRui Paulo rsu_read_1(sc, R92S_SYS_FUNC_EN + 1) | 0x80); 201631d98677SRui Paulo 201731d98677SRui Paulo /* Switch the control path to firmware. */ 201831d98677SRui Paulo reg = rsu_read_2(sc, R92S_SYS_CLKR); 201931d98677SRui Paulo reg = (reg & ~R92S_SWHW_SEL) | R92S_FWHW_SEL; 202031d98677SRui Paulo rsu_write_2(sc, R92S_SYS_CLKR, reg); 202131d98677SRui Paulo 202231d98677SRui Paulo rsu_write_2(sc, R92S_CR, 0x37fc); 202331d98677SRui Paulo 202431d98677SRui Paulo /* Fix USB RX FIFO issue. */ 202531d98677SRui Paulo rsu_write_1(sc, 0xfe5c, 202631d98677SRui Paulo rsu_read_1(sc, 0xfe5c) | 0x80); 202731d98677SRui Paulo 202831d98677SRui Paulo rsu_write_1(sc, R92S_SYS_CLKR, 202931d98677SRui Paulo rsu_read_1(sc, R92S_SYS_CLKR) & ~R92S_SYS_CPU_CLKSEL); 203031d98677SRui Paulo 203131d98677SRui Paulo rsu_write_1(sc, 0xfe1c, 0x80); 203231d98677SRui Paulo 203331d98677SRui Paulo /* Make sure TxDMA is ready to download firmware. */ 203431d98677SRui Paulo for (ntries = 0; ntries < 20; ntries++) { 203531d98677SRui Paulo reg = rsu_read_1(sc, R92S_TCR); 203631d98677SRui Paulo if ((reg & (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) == 203731d98677SRui Paulo (R92S_TCR_IMEM_CHK_RPT | R92S_TCR_EMEM_CHK_RPT)) 203831d98677SRui Paulo break; 2039b41381aeSHans Petter Selasky rsu_ms_delay(sc); 204031d98677SRui Paulo } 204131d98677SRui Paulo if (ntries == 20) { 204231d98677SRui Paulo DPRINTF("TxDMA is not ready\n"); 204331d98677SRui Paulo /* Reset TxDMA. */ 204431d98677SRui Paulo reg = rsu_read_1(sc, R92S_CR); 204531d98677SRui Paulo rsu_write_1(sc, R92S_CR, reg & ~R92S_CR_TXDMA_EN); 2046b41381aeSHans Petter Selasky rsu_ms_delay(sc); 204731d98677SRui Paulo rsu_write_1(sc, R92S_CR, reg | R92S_CR_TXDMA_EN); 204831d98677SRui Paulo } 204931d98677SRui Paulo } 205031d98677SRui Paulo 205131d98677SRui Paulo static void 205231d98677SRui Paulo rsu_power_off(struct rsu_softc *sc) 205331d98677SRui Paulo { 205431d98677SRui Paulo /* Turn RF off. */ 205531d98677SRui Paulo rsu_write_1(sc, R92S_RF_CTRL, 0x00); 2056400b4e53SHans Petter Selasky usb_pause_mtx(&sc->sc_mtx, hz / 200); 205731d98677SRui Paulo 205831d98677SRui Paulo /* Turn MAC off. */ 205931d98677SRui Paulo /* Switch control path. */ 206031d98677SRui Paulo rsu_write_1(sc, R92S_SYS_CLKR + 1, 0x38); 206131d98677SRui Paulo /* Reset MACTOP. */ 206231d98677SRui Paulo rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x70); 206331d98677SRui Paulo rsu_write_1(sc, R92S_PMC_FSM, 0x06); 206431d98677SRui Paulo rsu_write_1(sc, R92S_SYS_ISO_CTRL + 0, 0xf9); 206531d98677SRui Paulo rsu_write_1(sc, R92S_SYS_ISO_CTRL + 1, 0xe8); 206631d98677SRui Paulo 206731d98677SRui Paulo /* Disable AFE PLL. */ 206831d98677SRui Paulo rsu_write_1(sc, R92S_AFE_PLL_CTRL, 0x00); 206931d98677SRui Paulo /* Disable A15V. */ 207031d98677SRui Paulo rsu_write_1(sc, R92S_LDOA15_CTRL, 0x54); 207131d98677SRui Paulo /* Disable eFuse 1.2V. */ 207231d98677SRui Paulo rsu_write_1(sc, R92S_SYS_FUNC_EN + 1, 0x50); 207331d98677SRui Paulo rsu_write_1(sc, R92S_LDOV12D_CTRL, 0x24); 207431d98677SRui Paulo /* Enable AFE macro block's bandgap and Mbias. */ 207531d98677SRui Paulo rsu_write_1(sc, R92S_AFE_MISC, 0x30); 207631d98677SRui Paulo /* Disable 1.6V LDO. */ 207731d98677SRui Paulo rsu_write_1(sc, R92S_SPS0_CTRL + 0, 0x56); 207831d98677SRui Paulo rsu_write_1(sc, R92S_SPS0_CTRL + 1, 0x43); 207931d98677SRui Paulo } 208031d98677SRui Paulo 208131d98677SRui Paulo static int 20826d9b2f85SRui Paulo rsu_fw_loadsection(struct rsu_softc *sc, const uint8_t *buf, int len) 208331d98677SRui Paulo { 2084910593b5SHans Petter Selasky const uint8_t which = rsu_wme_ac_xfer_map[WME_AC_VO]; 208531d98677SRui Paulo struct rsu_data *data; 208631d98677SRui Paulo struct r92s_tx_desc *txd; 208731d98677SRui Paulo int mlen; 208831d98677SRui Paulo 208931d98677SRui Paulo while (len > 0) { 209031d98677SRui Paulo data = rsu_getbuf(sc); 209131d98677SRui Paulo if (data == NULL) 209231d98677SRui Paulo return (ENOMEM); 209331d98677SRui Paulo txd = (struct r92s_tx_desc *)data->buf; 209431d98677SRui Paulo memset(txd, 0, sizeof(*txd)); 209531d98677SRui Paulo if (len <= RSU_TXBUFSZ - sizeof(*txd)) { 209631d98677SRui Paulo /* Last chunk. */ 209731d98677SRui Paulo txd->txdw0 |= htole32(R92S_TXDW0_LINIP); 209831d98677SRui Paulo mlen = len; 209931d98677SRui Paulo } else 210031d98677SRui Paulo mlen = RSU_TXBUFSZ - sizeof(*txd); 210131d98677SRui Paulo txd->txdw0 |= htole32(SM(R92S_TXDW0_PKTLEN, mlen)); 210231d98677SRui Paulo memcpy(&txd[1], buf, mlen); 210331d98677SRui Paulo data->buflen = sizeof(*txd) + mlen; 210431d98677SRui Paulo DPRINTF("starting transfer %p\n", data); 2105400b4e53SHans Petter Selasky STAILQ_INSERT_TAIL(&sc->sc_tx_pending[which], data, next); 210631d98677SRui Paulo buf += mlen; 210731d98677SRui Paulo len -= mlen; 210831d98677SRui Paulo } 2109910593b5SHans Petter Selasky usbd_transfer_start(sc->sc_xfer[which]); 211031d98677SRui Paulo return (0); 211131d98677SRui Paulo } 211231d98677SRui Paulo 211331d98677SRui Paulo static int 211431d98677SRui Paulo rsu_load_firmware(struct rsu_softc *sc) 211531d98677SRui Paulo { 21166d9b2f85SRui Paulo const struct r92s_fw_hdr *hdr; 211731d98677SRui Paulo struct r92s_fw_priv *dmem; 21186d9b2f85SRui Paulo const uint8_t *imem, *emem; 211931d98677SRui Paulo int imemsz, ememsz; 212031d98677SRui Paulo const struct firmware *fw; 212131d98677SRui Paulo size_t size; 212231d98677SRui Paulo uint32_t reg; 212331d98677SRui Paulo int ntries, error; 212431d98677SRui Paulo 2125910593b5SHans Petter Selasky if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY) { 2126910593b5SHans Petter Selasky DPRINTF("Firmware already loaded\n"); 2127910593b5SHans Petter Selasky return (0); 2128910593b5SHans Petter Selasky } 2129910593b5SHans Petter Selasky 213031d98677SRui Paulo RSU_UNLOCK(sc); 213131d98677SRui Paulo /* Read firmware image from the filesystem. */ 213231d98677SRui Paulo if ((fw = firmware_get("rsu-rtl8712fw")) == NULL) { 213331d98677SRui Paulo device_printf(sc->sc_dev, 213431d98677SRui Paulo "%s: failed load firmware of file rsu-rtl8712fw\n", 213531d98677SRui Paulo __func__); 213631d98677SRui Paulo RSU_LOCK(sc); 213731d98677SRui Paulo return (ENXIO); 213831d98677SRui Paulo } 213931d98677SRui Paulo RSU_LOCK(sc); 214031d98677SRui Paulo size = fw->datasize; 214131d98677SRui Paulo if (size < sizeof(*hdr)) { 214231d98677SRui Paulo device_printf(sc->sc_dev, "firmware too short\n"); 214331d98677SRui Paulo error = EINVAL; 214431d98677SRui Paulo goto fail; 214531d98677SRui Paulo } 21466d9b2f85SRui Paulo hdr = (const struct r92s_fw_hdr *)fw->data; 214731d98677SRui Paulo if (hdr->signature != htole16(0x8712) && 214831d98677SRui Paulo hdr->signature != htole16(0x8192)) { 214931d98677SRui Paulo device_printf(sc->sc_dev, 215031d98677SRui Paulo "invalid firmware signature 0x%x\n", 215131d98677SRui Paulo le16toh(hdr->signature)); 215231d98677SRui Paulo error = EINVAL; 215331d98677SRui Paulo goto fail; 215431d98677SRui Paulo } 215531d98677SRui Paulo DPRINTF("FW V%d %02x-%02x %02x:%02x\n", le16toh(hdr->version), 215631d98677SRui Paulo hdr->month, hdr->day, hdr->hour, hdr->minute); 215731d98677SRui Paulo 215831d98677SRui Paulo /* Make sure that driver and firmware are in sync. */ 215931d98677SRui Paulo if (hdr->privsz != htole32(sizeof(*dmem))) { 216031d98677SRui Paulo device_printf(sc->sc_dev, "unsupported firmware image\n"); 216131d98677SRui Paulo error = EINVAL; 216231d98677SRui Paulo goto fail; 216331d98677SRui Paulo } 216431d98677SRui Paulo /* Get FW sections sizes. */ 216531d98677SRui Paulo imemsz = le32toh(hdr->imemsz); 216631d98677SRui Paulo ememsz = le32toh(hdr->sramsz); 216731d98677SRui Paulo /* Check that all FW sections fit in image. */ 216831d98677SRui Paulo if (size < sizeof(*hdr) + imemsz + ememsz) { 216931d98677SRui Paulo device_printf(sc->sc_dev, "firmware too short\n"); 217031d98677SRui Paulo error = EINVAL; 217131d98677SRui Paulo goto fail; 217231d98677SRui Paulo } 21736d9b2f85SRui Paulo imem = (const uint8_t *)&hdr[1]; 217431d98677SRui Paulo emem = imem + imemsz; 217531d98677SRui Paulo 217631d98677SRui Paulo /* Load IMEM section. */ 217731d98677SRui Paulo error = rsu_fw_loadsection(sc, imem, imemsz); 217831d98677SRui Paulo if (error != 0) { 217931d98677SRui Paulo device_printf(sc->sc_dev, 218031d98677SRui Paulo "could not load firmware section %s\n", "IMEM"); 218131d98677SRui Paulo goto fail; 218231d98677SRui Paulo } 218331d98677SRui Paulo /* Wait for load to complete. */ 2184885476cbSHans Petter Selasky for (ntries = 0; ntries != 50; ntries++) { 2185400b4e53SHans Petter Selasky usb_pause_mtx(&sc->sc_mtx, hz / 100); 2186910593b5SHans Petter Selasky reg = rsu_read_1(sc, R92S_TCR); 218731d98677SRui Paulo if (reg & R92S_TCR_IMEM_CODE_DONE) 218831d98677SRui Paulo break; 218931d98677SRui Paulo } 2190885476cbSHans Petter Selasky if (ntries == 50) { 2191885476cbSHans Petter Selasky device_printf(sc->sc_dev, "timeout waiting for IMEM transfer\n"); 219231d98677SRui Paulo error = ETIMEDOUT; 219331d98677SRui Paulo goto fail; 219431d98677SRui Paulo } 219531d98677SRui Paulo /* Load EMEM section. */ 219631d98677SRui Paulo error = rsu_fw_loadsection(sc, emem, ememsz); 219731d98677SRui Paulo if (error != 0) { 219831d98677SRui Paulo device_printf(sc->sc_dev, 219931d98677SRui Paulo "could not load firmware section %s\n", "EMEM"); 220031d98677SRui Paulo goto fail; 220131d98677SRui Paulo } 220231d98677SRui Paulo /* Wait for load to complete. */ 22031593e875SHans Petter Selasky for (ntries = 0; ntries != 50; ntries++) { 2204400b4e53SHans Petter Selasky usb_pause_mtx(&sc->sc_mtx, hz / 100); 220531d98677SRui Paulo reg = rsu_read_2(sc, R92S_TCR); 220631d98677SRui Paulo if (reg & R92S_TCR_EMEM_CODE_DONE) 220731d98677SRui Paulo break; 220831d98677SRui Paulo } 22091593e875SHans Petter Selasky if (ntries == 50) { 2210885476cbSHans Petter Selasky device_printf(sc->sc_dev, "timeout waiting for EMEM transfer\n"); 221131d98677SRui Paulo error = ETIMEDOUT; 221231d98677SRui Paulo goto fail; 221331d98677SRui Paulo } 221431d98677SRui Paulo /* Enable CPU. */ 221531d98677SRui Paulo rsu_write_1(sc, R92S_SYS_CLKR, 221631d98677SRui Paulo rsu_read_1(sc, R92S_SYS_CLKR) | R92S_SYS_CPU_CLKSEL); 221731d98677SRui Paulo if (!(rsu_read_1(sc, R92S_SYS_CLKR) & R92S_SYS_CPU_CLKSEL)) { 221831d98677SRui Paulo device_printf(sc->sc_dev, "could not enable system clock\n"); 221931d98677SRui Paulo error = EIO; 222031d98677SRui Paulo goto fail; 222131d98677SRui Paulo } 222231d98677SRui Paulo rsu_write_2(sc, R92S_SYS_FUNC_EN, 222331d98677SRui Paulo rsu_read_2(sc, R92S_SYS_FUNC_EN) | R92S_FEN_CPUEN); 222431d98677SRui Paulo if (!(rsu_read_2(sc, R92S_SYS_FUNC_EN) & R92S_FEN_CPUEN)) { 222531d98677SRui Paulo device_printf(sc->sc_dev, 222631d98677SRui Paulo "could not enable microcontroller\n"); 222731d98677SRui Paulo error = EIO; 222831d98677SRui Paulo goto fail; 222931d98677SRui Paulo } 223031d98677SRui Paulo /* Wait for CPU to initialize. */ 223131d98677SRui Paulo for (ntries = 0; ntries < 100; ntries++) { 2232910593b5SHans Petter Selasky if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_IMEM_RDY) 223331d98677SRui Paulo break; 2234b41381aeSHans Petter Selasky rsu_ms_delay(sc); 223531d98677SRui Paulo } 223631d98677SRui Paulo if (ntries == 100) { 223731d98677SRui Paulo device_printf(sc->sc_dev, 223831d98677SRui Paulo "timeout waiting for microcontroller\n"); 223931d98677SRui Paulo error = ETIMEDOUT; 224031d98677SRui Paulo goto fail; 224131d98677SRui Paulo } 224231d98677SRui Paulo 224331d98677SRui Paulo /* Update DMEM section before loading. */ 22446d9b2f85SRui Paulo dmem = __DECONST(struct r92s_fw_priv *, &hdr->priv); 224531d98677SRui Paulo memset(dmem, 0, sizeof(*dmem)); 224631d98677SRui Paulo dmem->hci_sel = R92S_HCI_SEL_USB | R92S_HCI_SEL_8172; 2247400b4e53SHans Petter Selasky dmem->nendpoints = 0; 224831d98677SRui Paulo dmem->rf_config = 0x12; /* 1T2R */ 224931d98677SRui Paulo dmem->vcs_type = R92S_VCS_TYPE_AUTO; 225031d98677SRui Paulo dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS; 225131d98677SRui Paulo #ifdef notyet 225231d98677SRui Paulo dmem->bw40_en = (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) != 0; 225331d98677SRui Paulo #endif 225431d98677SRui Paulo dmem->turbo_mode = 1; 225531d98677SRui Paulo /* Load DMEM section. */ 225631d98677SRui Paulo error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem)); 225731d98677SRui Paulo if (error != 0) { 225831d98677SRui Paulo device_printf(sc->sc_dev, 225931d98677SRui Paulo "could not load firmware section %s\n", "DMEM"); 226031d98677SRui Paulo goto fail; 226131d98677SRui Paulo } 226231d98677SRui Paulo /* Wait for load to complete. */ 226331d98677SRui Paulo for (ntries = 0; ntries < 100; ntries++) { 2264910593b5SHans Petter Selasky if (rsu_read_1(sc, R92S_TCR) & R92S_TCR_DMEM_CODE_DONE) 226531d98677SRui Paulo break; 2266b41381aeSHans Petter Selasky rsu_ms_delay(sc); 226731d98677SRui Paulo } 226831d98677SRui Paulo if (ntries == 100) { 226931d98677SRui Paulo device_printf(sc->sc_dev, "timeout waiting for %s transfer\n", 227031d98677SRui Paulo "DMEM"); 227131d98677SRui Paulo error = ETIMEDOUT; 227231d98677SRui Paulo goto fail; 227331d98677SRui Paulo } 227431d98677SRui Paulo /* Wait for firmware readiness. */ 227531d98677SRui Paulo for (ntries = 0; ntries < 60; ntries++) { 2276910593b5SHans Petter Selasky if (!(rsu_read_1(sc, R92S_TCR) & R92S_TCR_FWRDY)) 227731d98677SRui Paulo break; 2278b41381aeSHans Petter Selasky rsu_ms_delay(sc); 227931d98677SRui Paulo } 228031d98677SRui Paulo if (ntries == 60) { 228131d98677SRui Paulo device_printf(sc->sc_dev, 228231d98677SRui Paulo "timeout waiting for firmware readiness\n"); 228331d98677SRui Paulo error = ETIMEDOUT; 228431d98677SRui Paulo goto fail; 228531d98677SRui Paulo } 228631d98677SRui Paulo fail: 228731d98677SRui Paulo firmware_put(fw, FIRMWARE_UNLOAD); 228831d98677SRui Paulo return (error); 228931d98677SRui Paulo } 229031d98677SRui Paulo 229131d98677SRui Paulo 229231d98677SRui Paulo static int 229331d98677SRui Paulo rsu_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 229431d98677SRui Paulo const struct ieee80211_bpf_params *params) 229531d98677SRui Paulo { 229631d98677SRui Paulo struct ieee80211com *ic = ni->ni_ic; 229731d98677SRui Paulo struct ifnet *ifp = ic->ic_ifp; 229831d98677SRui Paulo struct rsu_softc *sc = ifp->if_softc; 229931d98677SRui Paulo struct rsu_data *bf; 230031d98677SRui Paulo 230131d98677SRui Paulo /* prevent management frames from being sent if we're not ready */ 230231d98677SRui Paulo if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { 230331d98677SRui Paulo m_freem(m); 230431d98677SRui Paulo ieee80211_free_node(ni); 230531d98677SRui Paulo return (ENETDOWN); 230631d98677SRui Paulo } 230731d98677SRui Paulo RSU_LOCK(sc); 230831d98677SRui Paulo bf = rsu_getbuf(sc); 230931d98677SRui Paulo if (bf == NULL) { 231031d98677SRui Paulo ieee80211_free_node(ni); 231131d98677SRui Paulo m_freem(m); 231231d98677SRui Paulo RSU_UNLOCK(sc); 231331d98677SRui Paulo return (ENOBUFS); 231431d98677SRui Paulo } 2315a001989dSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 2316400b4e53SHans Petter Selasky if (rsu_tx_start(sc, ni, m, bf) != 0) { 231731d98677SRui Paulo ieee80211_free_node(ni); 2318a001989dSGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 231931d98677SRui Paulo STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); 232031d98677SRui Paulo RSU_UNLOCK(sc); 232131d98677SRui Paulo return (EIO); 232231d98677SRui Paulo } 232331d98677SRui Paulo RSU_UNLOCK(sc); 232431d98677SRui Paulo 232531d98677SRui Paulo return (0); 232631d98677SRui Paulo } 232731d98677SRui Paulo 232831d98677SRui Paulo static void 232931d98677SRui Paulo rsu_init(void *arg) 233031d98677SRui Paulo { 233131d98677SRui Paulo struct rsu_softc *sc = arg; 233231d98677SRui Paulo 233331d98677SRui Paulo RSU_LOCK(sc); 233431d98677SRui Paulo rsu_init_locked(arg); 233531d98677SRui Paulo RSU_UNLOCK(sc); 233631d98677SRui Paulo } 233731d98677SRui Paulo 233831d98677SRui Paulo static void 233931d98677SRui Paulo rsu_init_locked(struct rsu_softc *sc) 234031d98677SRui Paulo { 234131d98677SRui Paulo struct ifnet *ifp = sc->sc_ifp; 234231d98677SRui Paulo struct r92s_set_pwr_mode cmd; 234331d98677SRui Paulo int error; 2344885476cbSHans Petter Selasky int i; 234531d98677SRui Paulo 234631d98677SRui Paulo /* Init host async commands ring. */ 234731d98677SRui Paulo sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0; 234831d98677SRui Paulo 234931d98677SRui Paulo /* Power on adapter. */ 235031d98677SRui Paulo if (sc->cut == 1) 235131d98677SRui Paulo rsu_power_on_acut(sc); 235231d98677SRui Paulo else 235331d98677SRui Paulo rsu_power_on_bcut(sc); 2354910593b5SHans Petter Selasky 235531d98677SRui Paulo /* Load firmware. */ 235631d98677SRui Paulo error = rsu_load_firmware(sc); 235731d98677SRui Paulo if (error != 0) 235831d98677SRui Paulo goto fail; 235931d98677SRui Paulo 236031d98677SRui Paulo /* Enable Rx TCP checksum offload. */ 236131d98677SRui Paulo rsu_write_4(sc, R92S_RCR, 236231d98677SRui Paulo rsu_read_4(sc, R92S_RCR) | 0x04000000); 236331d98677SRui Paulo /* Append PHY status. */ 236431d98677SRui Paulo rsu_write_4(sc, R92S_RCR, 236531d98677SRui Paulo rsu_read_4(sc, R92S_RCR) | 0x02000000); 236631d98677SRui Paulo 236731d98677SRui Paulo rsu_write_4(sc, R92S_CR, 236831d98677SRui Paulo rsu_read_4(sc, R92S_CR) & ~0xff000000); 236931d98677SRui Paulo 237031d98677SRui Paulo /* Use 128 bytes pages. */ 237131d98677SRui Paulo rsu_write_1(sc, 0x00b5, 237231d98677SRui Paulo rsu_read_1(sc, 0x00b5) | 0x01); 237331d98677SRui Paulo /* Enable USB Rx aggregation. */ 237431d98677SRui Paulo rsu_write_1(sc, 0x00bd, 237531d98677SRui Paulo rsu_read_1(sc, 0x00bd) | 0x80); 237631d98677SRui Paulo /* Set USB Rx aggregation threshold. */ 237731d98677SRui Paulo rsu_write_1(sc, 0x00d9, 0x01); 237831d98677SRui Paulo /* Set USB Rx aggregation timeout (1.7ms/4). */ 237931d98677SRui Paulo rsu_write_1(sc, 0xfe5b, 0x04); 238031d98677SRui Paulo /* Fix USB Rx FIFO issue. */ 238131d98677SRui Paulo rsu_write_1(sc, 0xfe5c, 238231d98677SRui Paulo rsu_read_1(sc, 0xfe5c) | 0x80); 238331d98677SRui Paulo 238431d98677SRui Paulo /* Set MAC address. */ 238531d98677SRui Paulo rsu_write_region_1(sc, R92S_MACID, IF_LLADDR(ifp), 238631d98677SRui Paulo IEEE80211_ADDR_LEN); 238731d98677SRui Paulo 2388b41381aeSHans Petter Selasky /* It really takes 1.5 seconds for the firmware to boot: */ 2389b41381aeSHans Petter Selasky usb_pause_mtx(&sc->sc_mtx, (3 * hz) / 2); 239031d98677SRui Paulo 239131d98677SRui Paulo DPRINTF("setting MAC address to %s\n", ether_sprintf(IF_LLADDR(ifp))); 239231d98677SRui Paulo error = rsu_fw_cmd(sc, R92S_CMD_SET_MAC_ADDRESS, IF_LLADDR(ifp), 239331d98677SRui Paulo IEEE80211_ADDR_LEN); 239431d98677SRui Paulo if (error != 0) { 239531d98677SRui Paulo device_printf(sc->sc_dev, "could not set MAC address\n"); 239631d98677SRui Paulo goto fail; 239731d98677SRui Paulo } 239831d98677SRui Paulo 239931d98677SRui Paulo rsu_write_1(sc, R92S_USB_HRPWM, 240031d98677SRui Paulo R92S_USB_HRPWM_PS_ST_ACTIVE | R92S_USB_HRPWM_PS_ALL_ON); 240131d98677SRui Paulo 240231d98677SRui Paulo memset(&cmd, 0, sizeof(cmd)); 240331d98677SRui Paulo cmd.mode = R92S_PS_MODE_ACTIVE; 240431d98677SRui Paulo DPRINTF("setting ps mode to %d\n", cmd.mode); 240531d98677SRui Paulo error = rsu_fw_cmd(sc, R92S_CMD_SET_PWR_MODE, &cmd, sizeof(cmd)); 240631d98677SRui Paulo if (error != 0) { 240731d98677SRui Paulo device_printf(sc->sc_dev, "could not set PS mode\n"); 240831d98677SRui Paulo goto fail; 240931d98677SRui Paulo } 241031d98677SRui Paulo 241131d98677SRui Paulo #if 0 241231d98677SRui Paulo if (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) { 241331d98677SRui Paulo /* Enable 40MHz mode. */ 241431d98677SRui Paulo error = rsu_fw_iocmd(sc, 241531d98677SRui Paulo SM(R92S_IOCMD_CLASS, 0xf4) | 241631d98677SRui Paulo SM(R92S_IOCMD_INDEX, 0x00) | 241731d98677SRui Paulo SM(R92S_IOCMD_VALUE, 0x0007)); 241831d98677SRui Paulo if (error != 0) { 241931d98677SRui Paulo device_printf(sc->sc_dev, 242031d98677SRui Paulo "could not enable 40MHz mode\n"); 242131d98677SRui Paulo goto fail; 242231d98677SRui Paulo } 242331d98677SRui Paulo } 242431d98677SRui Paulo 242531d98677SRui Paulo /* Set default channel. */ 242631d98677SRui Paulo ic->ic_bss->ni_chan = ic->ic_ibss_chan; 242731d98677SRui Paulo #endif 242831d98677SRui Paulo sc->scan_pass = 0; 242931d98677SRui Paulo usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]); 243031d98677SRui Paulo 243131d98677SRui Paulo /* We're ready to go. */ 243231d98677SRui Paulo ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 243331d98677SRui Paulo ifp->if_drv_flags |= IFF_DRV_RUNNING; 243431d98677SRui Paulo return; 243531d98677SRui Paulo fail: 2436885476cbSHans Petter Selasky /* Need to stop all failed transfers, if any */ 2437885476cbSHans Petter Selasky for (i = 0; i != RSU_N_TRANSFER; i++) 2438885476cbSHans Petter Selasky usbd_transfer_stop(sc->sc_xfer[i]); 243931d98677SRui Paulo } 244031d98677SRui Paulo 244131d98677SRui Paulo static void 244231d98677SRui Paulo rsu_stop(struct ifnet *ifp, int disable) 244331d98677SRui Paulo { 244431d98677SRui Paulo struct rsu_softc *sc = ifp->if_softc; 244531d98677SRui Paulo 244631d98677SRui Paulo RSU_LOCK(sc); 244731d98677SRui Paulo rsu_stop_locked(ifp, disable); 244831d98677SRui Paulo RSU_UNLOCK(sc); 244931d98677SRui Paulo } 245031d98677SRui Paulo 245131d98677SRui Paulo static void 245231d98677SRui Paulo rsu_stop_locked(struct ifnet *ifp, int disable __unused) 245331d98677SRui Paulo { 245431d98677SRui Paulo struct rsu_softc *sc = ifp->if_softc; 245531d98677SRui Paulo int i; 245631d98677SRui Paulo 245731d98677SRui Paulo ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 245831d98677SRui Paulo sc->sc_calibrating = 0; 245931d98677SRui Paulo taskqueue_cancel_timeout(taskqueue_thread, &sc->calib_task, NULL); 246031d98677SRui Paulo 246131d98677SRui Paulo /* Power off adapter. */ 246231d98677SRui Paulo rsu_power_off(sc); 246331d98677SRui Paulo 246431d98677SRui Paulo for (i = 0; i < RSU_N_TRANSFER; i++) 246531d98677SRui Paulo usbd_transfer_stop(sc->sc_xfer[i]); 246631d98677SRui Paulo } 246731d98677SRui Paulo 2468b41381aeSHans Petter Selasky static void 2469b41381aeSHans Petter Selasky rsu_ms_delay(struct rsu_softc *sc) 2470b41381aeSHans Petter Selasky { 2471b41381aeSHans Petter Selasky usb_pause_mtx(&sc->sc_mtx, hz / 1000); 2472b41381aeSHans Petter Selasky } 2473