1*81508fe3Sjsg /* $OpenBSD: if_otus.c,v 1.73 2024/05/23 03:21:08 jsg Exp $ */
268baf11cSdamien
368baf11cSdamien /*-
468baf11cSdamien * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr>
568baf11cSdamien *
668baf11cSdamien * Permission to use, copy, modify, and distribute this software for any
768baf11cSdamien * purpose with or without fee is hereby granted, provided that the above
868baf11cSdamien * copyright notice and this permission notice appear in all copies.
968baf11cSdamien *
1068baf11cSdamien * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1168baf11cSdamien * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1268baf11cSdamien * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1368baf11cSdamien * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1468baf11cSdamien * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1568baf11cSdamien * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1668baf11cSdamien * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1768baf11cSdamien */
1868baf11cSdamien
19403c1194Sdamien /*
2068baf11cSdamien * Driver for Atheros AR9001U chipset.
2168baf11cSdamien */
2268baf11cSdamien
2368baf11cSdamien #include "bpfilter.h"
2468baf11cSdamien
2568baf11cSdamien #include <sys/param.h>
2668baf11cSdamien #include <sys/sockio.h>
2768baf11cSdamien #include <sys/mbuf.h>
2868baf11cSdamien #include <sys/systm.h>
2968baf11cSdamien #include <sys/timeout.h>
3068baf11cSdamien #include <sys/device.h>
319b18ffb8Sguenther #include <sys/endian.h>
3268baf11cSdamien
3368baf11cSdamien #include <machine/intr.h>
3468baf11cSdamien
3568baf11cSdamien #if NBPFILTER > 0
3668baf11cSdamien #include <net/bpf.h>
3768baf11cSdamien #endif
3868baf11cSdamien #include <net/if.h>
3968baf11cSdamien #include <net/if_dl.h>
4068baf11cSdamien #include <net/if_media.h>
4168baf11cSdamien
4268baf11cSdamien #include <netinet/in.h>
4368baf11cSdamien #include <netinet/if_ether.h>
4468baf11cSdamien
4568baf11cSdamien #include <net80211/ieee80211_var.h>
4668baf11cSdamien #include <net80211/ieee80211_amrr.h>
4768baf11cSdamien #include <net80211/ieee80211_radiotap.h>
4868baf11cSdamien
4968baf11cSdamien #include <dev/usb/usb.h>
5068baf11cSdamien #include <dev/usb/usbdi.h>
5168baf11cSdamien #include <dev/usb/usbdi_util.h>
5268baf11cSdamien #include <dev/usb/usbdevs.h>
5368baf11cSdamien
5468baf11cSdamien #include <dev/usb/if_otusreg.h>
5568baf11cSdamien
5668baf11cSdamien #ifdef OTUS_DEBUG
5768baf11cSdamien #define DPRINTF(x) do { if (otus_debug) printf x; } while (0)
5868baf11cSdamien #define DPRINTFN(n, x) do { if (otus_debug >= (n)) printf x; } while (0)
5968baf11cSdamien int otus_debug = 1;
6068baf11cSdamien #else
6168baf11cSdamien #define DPRINTF(x)
6268baf11cSdamien #define DPRINTFN(n, x)
6368baf11cSdamien #endif
6468baf11cSdamien
6568baf11cSdamien static const struct usb_devno otus_devs[] = {
6668baf11cSdamien { USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_WN7512 },
6708c50b53Sdamien { USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_3CRUSBN275 },
6868baf11cSdamien { USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_TG121N },
6968baf11cSdamien { USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9170 },
7008c50b53Sdamien { USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_WN612 },
71a145d460Sdamien { USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_WN821NV2 },
72bd3ca424Sdamien { USB_VENDOR_AVM, USB_PRODUCT_AVM_FRITZWLAN },
7306c5b759Sdamien { USB_VENDOR_CACE, USB_PRODUCT_CACE_AIRPCAPNX },
749ca817faSjsg { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA130D1 },
7543333fa6Sdamien { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA160A1 },
7643333fa6Sdamien { USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA160A2 },
7768baf11cSdamien { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_WNGDNUS2 },
78a5c5b7a3Sdamien { USB_VENDOR_NEC, USB_PRODUCT_NEC_WL300NUG },
7968baf11cSdamien { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WN111V2 },
8008c50b53Sdamien { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNA1000 },
8168baf11cSdamien { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNDA3100 },
8268baf11cSdamien { USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GW_US300 },
8308c50b53Sdamien { USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_O8494 },
8408c50b53Sdamien { USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_WNC0600 },
8568baf11cSdamien { USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_UB81 },
8668baf11cSdamien { USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_UB82 },
87bd3ca424Sdamien { USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1221 },
88bd3ca424Sdamien { USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_NWD271N }
8968baf11cSdamien };
9068baf11cSdamien
9168baf11cSdamien int otus_match(struct device *, void *, void *);
9268baf11cSdamien void otus_attach(struct device *, struct device *, void *);
9368baf11cSdamien int otus_detach(struct device *, int);
94ef89f9e6Smpi void otus_attachhook(struct device *);
9568baf11cSdamien void otus_get_chanlist(struct otus_softc *);
96d7d7f774Sdamien int otus_load_firmware(struct otus_softc *, const char *,
97d7d7f774Sdamien uint32_t);
98d7d7f774Sdamien int otus_open_pipes(struct otus_softc *);
99d7d7f774Sdamien void otus_close_pipes(struct otus_softc *);
10068baf11cSdamien int otus_alloc_tx_cmd(struct otus_softc *);
10168baf11cSdamien void otus_free_tx_cmd(struct otus_softc *);
10268baf11cSdamien int otus_alloc_tx_data_list(struct otus_softc *);
10368baf11cSdamien void otus_free_tx_data_list(struct otus_softc *);
10468baf11cSdamien int otus_alloc_rx_data_list(struct otus_softc *);
10568baf11cSdamien void otus_free_rx_data_list(struct otus_softc *);
10668baf11cSdamien void otus_next_scan(void *);
10768baf11cSdamien void otus_task(void *);
10868baf11cSdamien void otus_do_async(struct otus_softc *,
10968baf11cSdamien void (*)(struct otus_softc *, void *), void *, int);
11068baf11cSdamien int otus_newstate(struct ieee80211com *, enum ieee80211_state,
11168baf11cSdamien int);
11268baf11cSdamien void otus_newstate_cb(struct otus_softc *, void *);
113d7d7f774Sdamien int otus_cmd(struct otus_softc *, uint8_t, const void *, int,
114d7d7f774Sdamien void *);
115d7d7f774Sdamien void otus_write(struct otus_softc *, uint32_t, uint32_t);
116d7d7f774Sdamien int otus_write_barrier(struct otus_softc *);
117d7d7f774Sdamien struct ieee80211_node *otus_node_alloc(struct ieee80211com *);
118d7d7f774Sdamien int otus_media_change(struct ifnet *);
11968baf11cSdamien int otus_read_eeprom(struct otus_softc *);
12068baf11cSdamien void otus_newassoc(struct ieee80211com *, struct ieee80211_node *,
12168baf11cSdamien int);
122ab0b1be7Smglocker void otus_intr(struct usbd_xfer *, void *, usbd_status);
12368baf11cSdamien void otus_cmd_rxeof(struct otus_softc *, uint8_t *, int);
1248fbaf8a2Sstsp void otus_sub_rxeof(struct otus_softc *, uint8_t *, int,
1258fbaf8a2Sstsp struct mbuf_list *);
126ab0b1be7Smglocker void otus_rxeof(struct usbd_xfer *, void *, usbd_status);
127ab0b1be7Smglocker void otus_txeof(struct usbd_xfer *, void *, usbd_status);
12868baf11cSdamien int otus_tx(struct otus_softc *, struct mbuf *,
12968baf11cSdamien struct ieee80211_node *);
13068baf11cSdamien void otus_start(struct ifnet *);
13168baf11cSdamien void otus_watchdog(struct ifnet *);
13268baf11cSdamien int otus_ioctl(struct ifnet *, u_long, caddr_t);
13368baf11cSdamien int otus_set_multi(struct otus_softc *);
13468baf11cSdamien void otus_updateedca(struct ieee80211com *);
13568baf11cSdamien void otus_updateedca_cb(struct otus_softc *, void *);
13668baf11cSdamien void otus_updateslot(struct ieee80211com *);
13768baf11cSdamien void otus_updateslot_cb(struct otus_softc *, void *);
13868baf11cSdamien int otus_init_mac(struct otus_softc *);
13968baf11cSdamien uint32_t otus_phy_get_def(struct otus_softc *, uint32_t);
14068baf11cSdamien int otus_set_board_values(struct otus_softc *,
14168baf11cSdamien struct ieee80211_channel *);
14268baf11cSdamien int otus_program_phy(struct otus_softc *,
14368baf11cSdamien struct ieee80211_channel *);
1446e30e88dSdamien int otus_set_rf_bank4(struct otus_softc *,
1456e30e88dSdamien struct ieee80211_channel *);
14668baf11cSdamien void otus_get_delta_slope(uint32_t, uint32_t *, uint32_t *);
1476e30e88dSdamien int otus_set_chan(struct otus_softc *, struct ieee80211_channel *,
1486e30e88dSdamien int);
14968baf11cSdamien int otus_set_key(struct ieee80211com *, struct ieee80211_node *,
15068baf11cSdamien struct ieee80211_key *);
15168baf11cSdamien void otus_set_key_cb(struct otus_softc *, void *);
15268baf11cSdamien void otus_delete_key(struct ieee80211com *, struct ieee80211_node *,
15368baf11cSdamien struct ieee80211_key *);
15468baf11cSdamien void otus_delete_key_cb(struct otus_softc *, void *);
15568baf11cSdamien void otus_calibrate_to(void *);
15668baf11cSdamien int otus_set_bssid(struct otus_softc *, const uint8_t *);
15768baf11cSdamien int otus_set_macaddr(struct otus_softc *, const uint8_t *);
15868baf11cSdamien void otus_led_newstate_type1(struct otus_softc *);
15968baf11cSdamien void otus_led_newstate_type2(struct otus_softc *);
16068baf11cSdamien void otus_led_newstate_type3(struct otus_softc *);
16168baf11cSdamien int otus_init(struct ifnet *);
16268baf11cSdamien void otus_stop(struct ifnet *);
16368baf11cSdamien
16468baf11cSdamien struct cfdriver otus_cd = {
16568baf11cSdamien NULL, "otus", DV_IFNET
16668baf11cSdamien };
16768baf11cSdamien
16868baf11cSdamien const struct cfattach otus_ca = {
16953c6612dSmpi sizeof (struct otus_softc), otus_match, otus_attach, otus_detach
17068baf11cSdamien };
17168baf11cSdamien
17268baf11cSdamien int
otus_match(struct device * parent,void * match,void * aux)17368baf11cSdamien otus_match(struct device *parent, void *match, void *aux)
17468baf11cSdamien {
17568baf11cSdamien struct usb_attach_arg *uaa = aux;
17668baf11cSdamien
177f4b7d08eSmpi if (uaa->iface == NULL || uaa->configno != 1)
17868baf11cSdamien return UMATCH_NONE;
17968baf11cSdamien
18068baf11cSdamien return (usb_lookup(otus_devs, uaa->vendor, uaa->product) != NULL) ?
18168baf11cSdamien UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
18268baf11cSdamien }
18368baf11cSdamien
18468baf11cSdamien void
otus_attach(struct device * parent,struct device * self,void * aux)18568baf11cSdamien otus_attach(struct device *parent, struct device *self, void *aux)
18668baf11cSdamien {
18768baf11cSdamien struct otus_softc *sc = (struct otus_softc *)self;
18868baf11cSdamien struct usb_attach_arg *uaa = aux;
18968baf11cSdamien int error;
19068baf11cSdamien
19168baf11cSdamien sc->sc_udev = uaa->device;
19268baf11cSdamien
193c33449aaSjakemsr usb_init_task(&sc->sc_task, otus_task, sc, USB_TASK_TYPE_GENERIC);
19468baf11cSdamien timeout_set(&sc->scan_to, otus_next_scan, sc);
19568baf11cSdamien timeout_set(&sc->calib_to, otus_calibrate_to, sc);
19668baf11cSdamien
19768baf11cSdamien sc->amrr.amrr_min_success_threshold = 1;
19868baf11cSdamien sc->amrr.amrr_max_success_threshold = 10;
19968baf11cSdamien
20068baf11cSdamien /* Get the first interface handle. */
20168baf11cSdamien error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface);
20268baf11cSdamien if (error != 0) {
20368baf11cSdamien printf("%s: could not get interface handle\n",
20468baf11cSdamien sc->sc_dev.dv_xname);
20568baf11cSdamien return;
20668baf11cSdamien }
20768baf11cSdamien
20868baf11cSdamien if ((error = otus_open_pipes(sc)) != 0) {
20968baf11cSdamien printf("%s: could not open pipes\n", sc->sc_dev.dv_xname);
21068baf11cSdamien return;
21168baf11cSdamien }
21268baf11cSdamien
213ef89f9e6Smpi config_mountroot(self, otus_attachhook);
21468baf11cSdamien }
21568baf11cSdamien
21668baf11cSdamien int
otus_detach(struct device * self,int flags)21768baf11cSdamien otus_detach(struct device *self, int flags)
21868baf11cSdamien {
21968baf11cSdamien struct otus_softc *sc = (struct otus_softc *)self;
22068baf11cSdamien struct ifnet *ifp = &sc->sc_ic.ic_if;
22168baf11cSdamien int s;
22268baf11cSdamien
2235ad57547Sjakemsr s = splusb();
22468baf11cSdamien
225c7438bddSjakemsr if (timeout_initialized(&sc->scan_to))
22668baf11cSdamien timeout_del(&sc->scan_to);
227c7438bddSjakemsr if (timeout_initialized(&sc->calib_to))
22868baf11cSdamien timeout_del(&sc->calib_to);
22968baf11cSdamien
2305ad57547Sjakemsr /* Wait for all queued asynchronous commands to complete. */
2315ad57547Sjakemsr usb_rem_wait_task(sc->sc_udev, &sc->sc_task);
2325ad57547Sjakemsr
2335ad57547Sjakemsr usbd_ref_wait(sc->sc_udev);
2345ad57547Sjakemsr
2359f3a1e51Sdamien if (ifp->if_softc != NULL) {
236de6cd8fbSdlg ifp->if_flags &= ~IFF_RUNNING;
237de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd);
23868baf11cSdamien ieee80211_ifdetach(ifp);
23968baf11cSdamien if_detach(ifp);
24068baf11cSdamien }
24168baf11cSdamien
24268baf11cSdamien otus_close_pipes(sc);
24368baf11cSdamien
24468baf11cSdamien splx(s);
24568baf11cSdamien
24668baf11cSdamien return 0;
24768baf11cSdamien }
24868baf11cSdamien
24968baf11cSdamien void
otus_attachhook(struct device * self)250ef89f9e6Smpi otus_attachhook(struct device *self)
25168baf11cSdamien {
252ef89f9e6Smpi struct otus_softc *sc = (struct otus_softc *)self;
25368baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
25468baf11cSdamien struct ifnet *ifp = &ic->ic_if;
25568baf11cSdamien usb_device_request_t req;
25668baf11cSdamien uint32_t in, out;
25768baf11cSdamien int error;
25868baf11cSdamien
25968baf11cSdamien error = otus_load_firmware(sc, "otus-init", AR_FW_INIT_ADDR);
26068baf11cSdamien if (error != 0) {
26168baf11cSdamien printf("%s: could not load %s firmware\n",
26268baf11cSdamien sc->sc_dev.dv_xname, "init");
26368baf11cSdamien return;
26468baf11cSdamien }
26568baf11cSdamien
26668baf11cSdamien usbd_delay_ms(sc->sc_udev, 1000);
26768baf11cSdamien
26868baf11cSdamien error = otus_load_firmware(sc, "otus-main", AR_FW_MAIN_ADDR);
26968baf11cSdamien if (error != 0) {
27068baf11cSdamien printf("%s: could not load %s firmware\n",
27168baf11cSdamien sc->sc_dev.dv_xname, "main");
27268baf11cSdamien return;
27368baf11cSdamien }
27468baf11cSdamien
27568baf11cSdamien /* Tell device that firmware transfer is complete. */
27668baf11cSdamien req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
27768baf11cSdamien req.bRequest = AR_FW_DOWNLOAD_COMPLETE;
27868baf11cSdamien USETW(req.wValue, 0);
27968baf11cSdamien USETW(req.wIndex, 0);
28068baf11cSdamien USETW(req.wLength, 0);
28168baf11cSdamien if (usbd_do_request(sc->sc_udev, &req, NULL) != 0) {
28268baf11cSdamien printf("%s: firmware initialization failed\n",
28368baf11cSdamien sc->sc_dev.dv_xname);
28468baf11cSdamien return;
28568baf11cSdamien }
28668baf11cSdamien
28768baf11cSdamien /* Send an ECHO command to check that everything is settled. */
28868baf11cSdamien in = 0xbadc0ffe;
28968baf11cSdamien if (otus_cmd(sc, AR_CMD_ECHO, &in, sizeof in, &out) != 0) {
29068baf11cSdamien printf("%s: echo command failed\n", sc->sc_dev.dv_xname);
29168baf11cSdamien return;
29268baf11cSdamien }
29368baf11cSdamien if (in != out) {
294d7d7f774Sdamien printf("%s: echo reply mismatch: 0x%08x!=0x%08x\n",
295d7d7f774Sdamien sc->sc_dev.dv_xname, in, out);
29668baf11cSdamien return;
29768baf11cSdamien }
29868baf11cSdamien
29968baf11cSdamien /* Read entire EEPROM. */
30068baf11cSdamien if (otus_read_eeprom(sc) != 0) {
30168baf11cSdamien printf("%s: could not read EEPROM\n", sc->sc_dev.dv_xname);
30268baf11cSdamien return;
30368baf11cSdamien }
30468baf11cSdamien
305803f2018Sdamien sc->txmask = sc->eeprom.baseEepHeader.txMask;
306803f2018Sdamien sc->rxmask = sc->eeprom.baseEepHeader.rxMask;
3076e30e88dSdamien sc->capflags = sc->eeprom.baseEepHeader.opCapFlags;
30868baf11cSdamien IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->eeprom.baseEepHeader.macAddr);
309803f2018Sdamien sc->sc_led_newstate = otus_led_newstate_type3; /* XXX */
31068baf11cSdamien
3116e30e88dSdamien printf("%s: MAC/BBP AR9170, RF AR%X, MIMO %dT%dR, address %s\n",
3126e30e88dSdamien sc->sc_dev.dv_xname, (sc->capflags & AR5416_OPFLAGS_11A) ?
3136e30e88dSdamien 0x9104 : ((sc->txmask == 0x5) ? 0x9102 : 0x9101),
3146e30e88dSdamien (sc->txmask == 0x5) ? 2 : 1, (sc->rxmask == 0x5) ? 2 : 1,
31568baf11cSdamien ether_sprintf(ic->ic_myaddr));
31668baf11cSdamien
31768baf11cSdamien ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
31868baf11cSdamien ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
31968baf11cSdamien ic->ic_state = IEEE80211_S_INIT;
32068baf11cSdamien
32168baf11cSdamien /* Set device capabilities. */
32268baf11cSdamien ic->ic_caps =
32368baf11cSdamien IEEE80211_C_MONITOR | /* monitor mode supported */
32468baf11cSdamien IEEE80211_C_SHPREAMBLE | /* short preamble supported */
32568baf11cSdamien IEEE80211_C_SHSLOT | /* short slot time supported */
32668baf11cSdamien IEEE80211_C_WEP | /* WEP */
32768baf11cSdamien IEEE80211_C_RSN; /* WPA/RSN */
32868baf11cSdamien
3294351e075Sdamien if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) {
33068baf11cSdamien /* Set supported .11b and .11g rates. */
3314351e075Sdamien ic->ic_sup_rates[IEEE80211_MODE_11B] =
3324351e075Sdamien ieee80211_std_rateset_11b;
3334351e075Sdamien ic->ic_sup_rates[IEEE80211_MODE_11G] =
3344351e075Sdamien ieee80211_std_rateset_11g;
3354351e075Sdamien }
3364351e075Sdamien if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) {
3374351e075Sdamien /* Set supported .11a rates. */
3384351e075Sdamien ic->ic_sup_rates[IEEE80211_MODE_11A] =
3394351e075Sdamien ieee80211_std_rateset_11a;
3404351e075Sdamien }
34168baf11cSdamien
34268baf11cSdamien /* Build the list of supported channels. */
34368baf11cSdamien otus_get_chanlist(sc);
34468baf11cSdamien
34568baf11cSdamien ifp->if_softc = sc;
34668baf11cSdamien ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
34768baf11cSdamien ifp->if_ioctl = otus_ioctl;
34868baf11cSdamien ifp->if_start = otus_start;
34968baf11cSdamien ifp->if_watchdog = otus_watchdog;
35068baf11cSdamien memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
35168baf11cSdamien
35268baf11cSdamien if_attach(ifp);
35368baf11cSdamien ieee80211_ifattach(ifp);
35468baf11cSdamien ic->ic_node_alloc = otus_node_alloc;
35568baf11cSdamien ic->ic_newassoc = otus_newassoc;
35668baf11cSdamien ic->ic_updateslot = otus_updateslot;
35768baf11cSdamien ic->ic_updateedca = otus_updateedca;
35868baf11cSdamien #ifdef notyet
35968baf11cSdamien ic->ic_set_key = otus_set_key;
36068baf11cSdamien ic->ic_delete_key = otus_delete_key;
36168baf11cSdamien #endif
36268baf11cSdamien /* Override state transition machine. */
36368baf11cSdamien sc->sc_newstate = ic->ic_newstate;
36468baf11cSdamien ic->ic_newstate = otus_newstate;
36568baf11cSdamien ieee80211_media_init(ifp, otus_media_change, ieee80211_media_status);
36668baf11cSdamien
36768baf11cSdamien #if NBPFILTER > 0
36868baf11cSdamien bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
36968baf11cSdamien sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN);
37068baf11cSdamien
37168baf11cSdamien sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
37268baf11cSdamien sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
37368baf11cSdamien sc->sc_rxtap.wr_ihdr.it_present = htole32(OTUS_RX_RADIOTAP_PRESENT);
37468baf11cSdamien
37568baf11cSdamien sc->sc_txtap_len = sizeof sc->sc_txtapu;
37668baf11cSdamien sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
37768baf11cSdamien sc->sc_txtap.wt_ihdr.it_present = htole32(OTUS_TX_RADIOTAP_PRESENT);
37868baf11cSdamien #endif
37968baf11cSdamien }
38068baf11cSdamien
38168baf11cSdamien void
otus_get_chanlist(struct otus_softc * sc)38268baf11cSdamien otus_get_chanlist(struct otus_softc *sc)
38368baf11cSdamien {
38468baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
38568baf11cSdamien uint16_t domain;
38668baf11cSdamien uint8_t chan;
38768baf11cSdamien int i;
38868baf11cSdamien
38968baf11cSdamien /* XXX regulatory domain. */
39068baf11cSdamien domain = letoh16(sc->eeprom.baseEepHeader.regDmn[0]);
39168baf11cSdamien DPRINTF(("regdomain=0x%04x\n", domain));
39268baf11cSdamien
39368baf11cSdamien if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) {
39468baf11cSdamien for (i = 0; i < 14; i++) {
39568baf11cSdamien chan = ar_chans[i];
39668baf11cSdamien ic->ic_channels[chan].ic_freq =
39768baf11cSdamien ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
39868baf11cSdamien ic->ic_channels[chan].ic_flags =
39968baf11cSdamien IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
40068baf11cSdamien IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
40168baf11cSdamien }
40268baf11cSdamien }
40368baf11cSdamien if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) {
40468baf11cSdamien for (i = 14; i < nitems(ar_chans); i++) {
40568baf11cSdamien chan = ar_chans[i];
40668baf11cSdamien ic->ic_channels[chan].ic_freq =
40768baf11cSdamien ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
40868baf11cSdamien ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A;
40968baf11cSdamien }
41068baf11cSdamien }
41168baf11cSdamien }
41268baf11cSdamien
41368baf11cSdamien int
otus_load_firmware(struct otus_softc * sc,const char * name,uint32_t addr)41468baf11cSdamien otus_load_firmware(struct otus_softc *sc, const char *name, uint32_t addr)
41568baf11cSdamien {
41668baf11cSdamien usb_device_request_t req;
417c9ee9455Sderaadt size_t fwsize, size;
41868baf11cSdamien u_char *fw, *ptr;
41968baf11cSdamien int mlen, error;
42068baf11cSdamien
42168baf11cSdamien /* Read firmware image from the filesystem. */
422c9ee9455Sderaadt if ((error = loadfirmware(name, &fw, &fwsize)) != 0) {
42368baf11cSdamien printf("%s: failed loadfirmware of file %s (error %d)\n",
42468baf11cSdamien sc->sc_dev.dv_xname, name, error);
42568baf11cSdamien return error;
42668baf11cSdamien }
42768baf11cSdamien req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
42868baf11cSdamien req.bRequest = AR_FW_DOWNLOAD;
42968baf11cSdamien USETW(req.wIndex, 0);
43068baf11cSdamien
43168baf11cSdamien ptr = fw;
432c9ee9455Sderaadt size = fwsize;
43368baf11cSdamien addr >>= 8;
43468baf11cSdamien while (size > 0) {
43568baf11cSdamien mlen = MIN(size, 4096);
43668baf11cSdamien
43768baf11cSdamien USETW(req.wValue, addr);
43868baf11cSdamien USETW(req.wLength, mlen);
43968baf11cSdamien if (usbd_do_request(sc->sc_udev, &req, ptr) != 0) {
44068baf11cSdamien error = EIO;
44168baf11cSdamien break;
44268baf11cSdamien }
44368baf11cSdamien addr += mlen >> 8;
44468baf11cSdamien ptr += mlen;
44568baf11cSdamien size -= mlen;
44668baf11cSdamien }
447c9ee9455Sderaadt free(fw, M_DEVBUF, fwsize);
44868baf11cSdamien return error;
44968baf11cSdamien }
45068baf11cSdamien
45168baf11cSdamien int
otus_open_pipes(struct otus_softc * sc)45268baf11cSdamien otus_open_pipes(struct otus_softc *sc)
45368baf11cSdamien {
45468baf11cSdamien usb_endpoint_descriptor_t *ed;
45568baf11cSdamien int i, isize, error;
45668baf11cSdamien
45768baf11cSdamien error = usbd_open_pipe(sc->sc_iface, AR_EPT_BULK_RX_NO, 0,
45868baf11cSdamien &sc->data_rx_pipe);
45968baf11cSdamien if (error != 0) {
46068baf11cSdamien printf("%s: could not open Rx bulk pipe\n",
46168baf11cSdamien sc->sc_dev.dv_xname);
46268baf11cSdamien goto fail;
46368baf11cSdamien }
46468baf11cSdamien
46568baf11cSdamien ed = usbd_get_endpoint_descriptor(sc->sc_iface, AR_EPT_INTR_RX_NO);
46668baf11cSdamien if (ed == NULL) {
46768baf11cSdamien printf("%s: could not retrieve Rx intr pipe descriptor\n",
46868baf11cSdamien sc->sc_dev.dv_xname);
46968baf11cSdamien goto fail;
47068baf11cSdamien }
47168baf11cSdamien isize = UGETW(ed->wMaxPacketSize);
47268baf11cSdamien if (isize == 0) {
47368baf11cSdamien printf("%s: invalid Rx intr pipe descriptor\n",
47468baf11cSdamien sc->sc_dev.dv_xname);
47568baf11cSdamien goto fail;
47668baf11cSdamien }
47768baf11cSdamien sc->ibuf = malloc(isize, M_USBDEV, M_NOWAIT);
47868baf11cSdamien if (sc->ibuf == NULL) {
47968baf11cSdamien printf("%s: could not allocate Rx intr buffer\n",
48068baf11cSdamien sc->sc_dev.dv_xname);
48168baf11cSdamien goto fail;
48268baf11cSdamien }
483234dfda1Sderaadt sc->ibuflen = isize;
48468baf11cSdamien error = usbd_open_pipe_intr(sc->sc_iface, AR_EPT_INTR_RX_NO,
48568baf11cSdamien USBD_SHORT_XFER_OK, &sc->cmd_rx_pipe, sc, sc->ibuf, isize,
48668baf11cSdamien otus_intr, USBD_DEFAULT_INTERVAL);
48768baf11cSdamien if (error != 0) {
48868baf11cSdamien printf("%s: could not open Rx intr pipe\n",
48968baf11cSdamien sc->sc_dev.dv_xname);
49068baf11cSdamien goto fail;
49168baf11cSdamien }
49268baf11cSdamien
49368baf11cSdamien error = usbd_open_pipe(sc->sc_iface, AR_EPT_BULK_TX_NO, 0,
49468baf11cSdamien &sc->data_tx_pipe);
49568baf11cSdamien if (error != 0) {
49668baf11cSdamien printf("%s: could not open Tx bulk pipe\n",
49768baf11cSdamien sc->sc_dev.dv_xname);
49868baf11cSdamien goto fail;
49968baf11cSdamien }
50068baf11cSdamien
50168baf11cSdamien error = usbd_open_pipe(sc->sc_iface, AR_EPT_INTR_TX_NO, 0,
50268baf11cSdamien &sc->cmd_tx_pipe);
50368baf11cSdamien if (error != 0) {
50468baf11cSdamien printf("%s: could not open Tx intr pipe\n",
50568baf11cSdamien sc->sc_dev.dv_xname);
50668baf11cSdamien goto fail;
50768baf11cSdamien }
50868baf11cSdamien
50968baf11cSdamien if (otus_alloc_tx_cmd(sc) != 0) {
51068baf11cSdamien printf("%s: could not allocate command xfer\n",
51168baf11cSdamien sc->sc_dev.dv_xname);
51268baf11cSdamien goto fail;
51368baf11cSdamien }
51468baf11cSdamien
51568baf11cSdamien if (otus_alloc_tx_data_list(sc) != 0) {
51668baf11cSdamien printf("%s: could not allocate Tx xfers\n",
51768baf11cSdamien sc->sc_dev.dv_xname);
51868baf11cSdamien goto fail;
51968baf11cSdamien }
52068baf11cSdamien
52168baf11cSdamien if (otus_alloc_rx_data_list(sc) != 0) {
52268baf11cSdamien printf("%s: could not allocate Rx xfers\n",
52368baf11cSdamien sc->sc_dev.dv_xname);
52468baf11cSdamien goto fail;
52568baf11cSdamien }
52668baf11cSdamien
52768baf11cSdamien for (i = 0; i < OTUS_RX_DATA_LIST_COUNT; i++) {
52868baf11cSdamien struct otus_rx_data *data = &sc->rx_data[i];
52968baf11cSdamien
53068baf11cSdamien usbd_setup_xfer(data->xfer, sc->data_rx_pipe, data, data->buf,
53168baf11cSdamien OTUS_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
53268baf11cSdamien USBD_NO_TIMEOUT, otus_rxeof);
53368baf11cSdamien error = usbd_transfer(data->xfer);
53468baf11cSdamien if (error != USBD_IN_PROGRESS && error != 0) {
53568baf11cSdamien printf("%s: could not queue Rx xfer\n",
53668baf11cSdamien sc->sc_dev.dv_xname);
53768baf11cSdamien goto fail;
53868baf11cSdamien }
53968baf11cSdamien }
54068baf11cSdamien return 0;
54168baf11cSdamien
54268baf11cSdamien fail: otus_close_pipes(sc);
54368baf11cSdamien return error;
54468baf11cSdamien }
54568baf11cSdamien
54668baf11cSdamien void
otus_close_pipes(struct otus_softc * sc)54768baf11cSdamien otus_close_pipes(struct otus_softc *sc)
54868baf11cSdamien {
5494351e075Sdamien otus_free_tx_cmd(sc);
5504351e075Sdamien otus_free_tx_data_list(sc);
5514351e075Sdamien otus_free_rx_data_list(sc);
5524351e075Sdamien
55368baf11cSdamien if (sc->data_rx_pipe != NULL)
55468baf11cSdamien usbd_close_pipe(sc->data_rx_pipe);
555f88cb03eSmglocker if (sc->cmd_rx_pipe != NULL)
55668baf11cSdamien usbd_close_pipe(sc->cmd_rx_pipe);
55768baf11cSdamien if (sc->ibuf != NULL)
558234dfda1Sderaadt free(sc->ibuf, M_USBDEV, sc->ibuflen);
55968baf11cSdamien if (sc->data_tx_pipe != NULL)
56068baf11cSdamien usbd_close_pipe(sc->data_tx_pipe);
56168baf11cSdamien if (sc->cmd_tx_pipe != NULL)
56268baf11cSdamien usbd_close_pipe(sc->cmd_tx_pipe);
56368baf11cSdamien }
56468baf11cSdamien
56568baf11cSdamien int
otus_alloc_tx_cmd(struct otus_softc * sc)56668baf11cSdamien otus_alloc_tx_cmd(struct otus_softc *sc)
56768baf11cSdamien {
56868baf11cSdamien struct otus_tx_cmd *cmd = &sc->tx_cmd;
56968baf11cSdamien
57068baf11cSdamien cmd->xfer = usbd_alloc_xfer(sc->sc_udev);
57168baf11cSdamien if (cmd->xfer == NULL) {
57268baf11cSdamien printf("%s: could not allocate xfer\n",
57368baf11cSdamien sc->sc_dev.dv_xname);
57468baf11cSdamien return ENOMEM;
57568baf11cSdamien }
57668baf11cSdamien cmd->buf = usbd_alloc_buffer(cmd->xfer, OTUS_MAX_TXCMDSZ);
57768baf11cSdamien if (cmd->buf == NULL) {
57868baf11cSdamien printf("%s: could not allocate xfer buffer\n",
57968baf11cSdamien sc->sc_dev.dv_xname);
58068baf11cSdamien usbd_free_xfer(cmd->xfer);
5815b6e5399Sstsp cmd->xfer = NULL;
58268baf11cSdamien return ENOMEM;
58368baf11cSdamien }
58468baf11cSdamien return 0;
58568baf11cSdamien }
58668baf11cSdamien
58768baf11cSdamien void
otus_free_tx_cmd(struct otus_softc * sc)58868baf11cSdamien otus_free_tx_cmd(struct otus_softc *sc)
58968baf11cSdamien {
5904351e075Sdamien /* Make sure no transfers are pending. */
59168baf11cSdamien usbd_abort_pipe(sc->cmd_tx_pipe);
59268baf11cSdamien
59368baf11cSdamien if (sc->tx_cmd.xfer != NULL)
59468baf11cSdamien usbd_free_xfer(sc->tx_cmd.xfer);
59568baf11cSdamien }
59668baf11cSdamien
59768baf11cSdamien int
otus_alloc_tx_data_list(struct otus_softc * sc)59868baf11cSdamien otus_alloc_tx_data_list(struct otus_softc *sc)
59968baf11cSdamien {
60068baf11cSdamien struct otus_tx_data *data;
60168baf11cSdamien int i, error;
60268baf11cSdamien
60368baf11cSdamien for (i = 0; i < OTUS_TX_DATA_LIST_COUNT; i++) {
60468baf11cSdamien data = &sc->tx_data[i];
60568baf11cSdamien
60668baf11cSdamien data->sc = sc; /* Backpointer for callbacks. */
60768baf11cSdamien
60868baf11cSdamien data->xfer = usbd_alloc_xfer(sc->sc_udev);
60968baf11cSdamien if (data->xfer == NULL) {
61068baf11cSdamien printf("%s: could not allocate xfer\n",
61168baf11cSdamien sc->sc_dev.dv_xname);
61268baf11cSdamien error = ENOMEM;
61368baf11cSdamien goto fail;
61468baf11cSdamien }
61568baf11cSdamien data->buf = usbd_alloc_buffer(data->xfer, OTUS_TXBUFSZ);
61668baf11cSdamien if (data->buf == NULL) {
61768baf11cSdamien printf("%s: could not allocate xfer buffer\n",
61868baf11cSdamien sc->sc_dev.dv_xname);
61968baf11cSdamien error = ENOMEM;
62068baf11cSdamien goto fail;
62168baf11cSdamien }
62268baf11cSdamien }
62368baf11cSdamien return 0;
62468baf11cSdamien
62568baf11cSdamien fail: otus_free_tx_data_list(sc);
62668baf11cSdamien return error;
62768baf11cSdamien }
62868baf11cSdamien
62968baf11cSdamien void
otus_free_tx_data_list(struct otus_softc * sc)63068baf11cSdamien otus_free_tx_data_list(struct otus_softc *sc)
63168baf11cSdamien {
63268baf11cSdamien int i;
63368baf11cSdamien
6344351e075Sdamien /* Make sure no transfers are pending. */
63568baf11cSdamien usbd_abort_pipe(sc->data_tx_pipe);
63668baf11cSdamien
63768baf11cSdamien for (i = 0; i < OTUS_TX_DATA_LIST_COUNT; i++)
63868baf11cSdamien if (sc->tx_data[i].xfer != NULL)
63968baf11cSdamien usbd_free_xfer(sc->tx_data[i].xfer);
64068baf11cSdamien }
64168baf11cSdamien
64268baf11cSdamien int
otus_alloc_rx_data_list(struct otus_softc * sc)64368baf11cSdamien otus_alloc_rx_data_list(struct otus_softc *sc)
64468baf11cSdamien {
64568baf11cSdamien struct otus_rx_data *data;
64668baf11cSdamien int i, error;
64768baf11cSdamien
64868baf11cSdamien for (i = 0; i < OTUS_RX_DATA_LIST_COUNT; i++) {
64968baf11cSdamien data = &sc->rx_data[i];
65068baf11cSdamien
65168baf11cSdamien data->sc = sc; /* Backpointer for callbacks. */
65268baf11cSdamien
65368baf11cSdamien data->xfer = usbd_alloc_xfer(sc->sc_udev);
65468baf11cSdamien if (data->xfer == NULL) {
65568baf11cSdamien printf("%s: could not allocate xfer\n",
65668baf11cSdamien sc->sc_dev.dv_xname);
65768baf11cSdamien error = ENOMEM;
65868baf11cSdamien goto fail;
65968baf11cSdamien }
66068baf11cSdamien data->buf = usbd_alloc_buffer(data->xfer, OTUS_RXBUFSZ);
66168baf11cSdamien if (data->buf == NULL) {
66268baf11cSdamien printf("%s: could not allocate xfer buffer\n",
66368baf11cSdamien sc->sc_dev.dv_xname);
66468baf11cSdamien error = ENOMEM;
66568baf11cSdamien goto fail;
66668baf11cSdamien }
66768baf11cSdamien }
66868baf11cSdamien return 0;
66968baf11cSdamien
67068baf11cSdamien fail: otus_free_rx_data_list(sc);
67168baf11cSdamien return error;
67268baf11cSdamien }
67368baf11cSdamien
67468baf11cSdamien void
otus_free_rx_data_list(struct otus_softc * sc)67568baf11cSdamien otus_free_rx_data_list(struct otus_softc *sc)
67668baf11cSdamien {
67768baf11cSdamien int i;
67868baf11cSdamien
67968baf11cSdamien /* Make sure no transfers are pending. */
68068baf11cSdamien usbd_abort_pipe(sc->data_rx_pipe);
68168baf11cSdamien
68268baf11cSdamien for (i = 0; i < OTUS_RX_DATA_LIST_COUNT; i++)
68368baf11cSdamien if (sc->rx_data[i].xfer != NULL)
68468baf11cSdamien usbd_free_xfer(sc->rx_data[i].xfer);
68568baf11cSdamien }
68668baf11cSdamien
68768baf11cSdamien void
otus_next_scan(void * arg)68868baf11cSdamien otus_next_scan(void *arg)
68968baf11cSdamien {
69068baf11cSdamien struct otus_softc *sc = arg;
6911be8cfb2Sstsp struct ieee80211com *ic = &sc->sc_ic;
6921be8cfb2Sstsp struct ifnet *ifp = &ic->ic_if;
69368baf11cSdamien
6945ad57547Sjakemsr if (usbd_is_dying(sc->sc_udev))
6955ad57547Sjakemsr return;
6965ad57547Sjakemsr
6975ad57547Sjakemsr usbd_ref_incr(sc->sc_udev);
6985ad57547Sjakemsr
6991be8cfb2Sstsp if (sc->sc_ic.ic_state == IEEE80211_S_SCAN &&
7001be8cfb2Sstsp (ifp->if_flags & IFF_RUNNING))
70168baf11cSdamien ieee80211_next_scan(&sc->sc_ic.ic_if);
7025ad57547Sjakemsr
7035ad57547Sjakemsr usbd_ref_decr(sc->sc_udev);
70468baf11cSdamien }
70568baf11cSdamien
70668baf11cSdamien void
otus_task(void * arg)70768baf11cSdamien otus_task(void *arg)
70868baf11cSdamien {
70968baf11cSdamien struct otus_softc *sc = arg;
71068baf11cSdamien struct otus_host_cmd_ring *ring = &sc->cmdq;
71168baf11cSdamien struct otus_host_cmd *cmd;
71268baf11cSdamien int s;
71368baf11cSdamien
71468baf11cSdamien /* Process host commands. */
71568baf11cSdamien s = splusb();
71668baf11cSdamien while (ring->next != ring->cur) {
71768baf11cSdamien cmd = &ring->cmd[ring->next];
71868baf11cSdamien splx(s);
71968baf11cSdamien /* Callback. */
72068baf11cSdamien cmd->cb(sc, cmd->data);
72168baf11cSdamien s = splusb();
72268baf11cSdamien ring->queued--;
72368baf11cSdamien ring->next = (ring->next + 1) % OTUS_HOST_CMD_RING_COUNT;
72468baf11cSdamien }
72568baf11cSdamien splx(s);
72668baf11cSdamien }
72768baf11cSdamien
72868baf11cSdamien void
otus_do_async(struct otus_softc * sc,void (* cb)(struct otus_softc *,void *),void * arg,int len)72968baf11cSdamien otus_do_async(struct otus_softc *sc, void (*cb)(struct otus_softc *, void *),
73068baf11cSdamien void *arg, int len)
73168baf11cSdamien {
73268baf11cSdamien struct otus_host_cmd_ring *ring = &sc->cmdq;
73368baf11cSdamien struct otus_host_cmd *cmd;
73468baf11cSdamien int s;
73568baf11cSdamien
73668baf11cSdamien s = splusb();
73768baf11cSdamien cmd = &ring->cmd[ring->cur];
73868baf11cSdamien cmd->cb = cb;
73968baf11cSdamien KASSERT(len <= sizeof (cmd->data));
74068baf11cSdamien memcpy(cmd->data, arg, len);
74168baf11cSdamien ring->cur = (ring->cur + 1) % OTUS_HOST_CMD_RING_COUNT;
74268baf11cSdamien
74368baf11cSdamien /* If there is no pending command already, schedule a task. */
74468baf11cSdamien if (++ring->queued == 1)
74568baf11cSdamien usb_add_task(sc->sc_udev, &sc->sc_task);
74668baf11cSdamien splx(s);
74768baf11cSdamien }
74868baf11cSdamien
74968baf11cSdamien int
otus_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)75068baf11cSdamien otus_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
75168baf11cSdamien {
75268baf11cSdamien struct otus_softc *sc = ic->ic_softc;
75368baf11cSdamien struct otus_cmd_newstate cmd;
75468baf11cSdamien
75568baf11cSdamien /* Do it in a process context. */
75668baf11cSdamien cmd.state = nstate;
75768baf11cSdamien cmd.arg = arg;
75868baf11cSdamien otus_do_async(sc, otus_newstate_cb, &cmd, sizeof cmd);
75968baf11cSdamien return 0;
76068baf11cSdamien }
76168baf11cSdamien
76268baf11cSdamien void
otus_newstate_cb(struct otus_softc * sc,void * arg)76368baf11cSdamien otus_newstate_cb(struct otus_softc *sc, void *arg)
76468baf11cSdamien {
76568baf11cSdamien struct otus_cmd_newstate *cmd = arg;
76668baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
76768baf11cSdamien struct ieee80211_node *ni;
76868baf11cSdamien int s;
76968baf11cSdamien
77068baf11cSdamien s = splnet();
77168baf11cSdamien
77268baf11cSdamien switch (cmd->state) {
77368baf11cSdamien case IEEE80211_S_INIT:
77468baf11cSdamien break;
77568baf11cSdamien
77668baf11cSdamien case IEEE80211_S_SCAN:
7776e30e88dSdamien (void)otus_set_chan(sc, ic->ic_bss->ni_chan, 0);
7785ad57547Sjakemsr if (!usbd_is_dying(sc->sc_udev))
77968baf11cSdamien timeout_add_msec(&sc->scan_to, 200);
78068baf11cSdamien break;
78168baf11cSdamien
78268baf11cSdamien case IEEE80211_S_AUTH:
78368baf11cSdamien case IEEE80211_S_ASSOC:
7846e30e88dSdamien (void)otus_set_chan(sc, ic->ic_bss->ni_chan, 0);
78568baf11cSdamien break;
78668baf11cSdamien
78768baf11cSdamien case IEEE80211_S_RUN:
7886e30e88dSdamien (void)otus_set_chan(sc, ic->ic_bss->ni_chan, 1);
78968baf11cSdamien
79068baf11cSdamien ni = ic->ic_bss;
79168baf11cSdamien
79268baf11cSdamien if (ic->ic_opmode == IEEE80211_M_STA) {
79368baf11cSdamien otus_updateslot(ic);
79468baf11cSdamien otus_set_bssid(sc, ni->ni_bssid);
79568baf11cSdamien
79668baf11cSdamien /* Fake a join to init the Tx rate. */
79768baf11cSdamien otus_newassoc(ic, ni, 1);
79868baf11cSdamien
79968baf11cSdamien /* Start calibration timer. */
8005ad57547Sjakemsr if (!usbd_is_dying(sc->sc_udev))
80168baf11cSdamien timeout_add_sec(&sc->calib_to, 1);
80268baf11cSdamien }
80368baf11cSdamien break;
80468baf11cSdamien }
80568baf11cSdamien
80668baf11cSdamien sc->sc_led_newstate(sc);
80768baf11cSdamien (void)sc->sc_newstate(ic, cmd->state, cmd->arg);
80868baf11cSdamien
80968baf11cSdamien splx(s);
81068baf11cSdamien }
81168baf11cSdamien
81268baf11cSdamien int
otus_cmd(struct otus_softc * sc,uint8_t code,const void * idata,int ilen,void * odata)81368baf11cSdamien otus_cmd(struct otus_softc *sc, uint8_t code, const void *idata, int ilen,
81468baf11cSdamien void *odata)
81568baf11cSdamien {
81668baf11cSdamien struct otus_tx_cmd *cmd = &sc->tx_cmd;
81768baf11cSdamien struct ar_cmd_hdr *hdr;
81868baf11cSdamien int s, xferlen, error;
81968baf11cSdamien
82068baf11cSdamien /* Always bulk-out a multiple of 4 bytes. */
82168baf11cSdamien xferlen = (sizeof (*hdr) + ilen + 3) & ~3;
82268baf11cSdamien
82368baf11cSdamien hdr = (struct ar_cmd_hdr *)cmd->buf;
82468baf11cSdamien hdr->code = code;
82568baf11cSdamien hdr->len = ilen;
82668baf11cSdamien hdr->token = ++cmd->token; /* Don't care about endianness. */
82768baf11cSdamien memcpy((uint8_t *)&hdr[1], idata, ilen);
82868baf11cSdamien
82968baf11cSdamien DPRINTFN(2, ("sending command code=0x%02x len=%d token=%d\n",
83068baf11cSdamien code, ilen, hdr->token));
83168baf11cSdamien
83268baf11cSdamien s = splusb();
83368baf11cSdamien cmd->odata = odata;
83468baf11cSdamien cmd->done = 0;
83568baf11cSdamien
83668baf11cSdamien usbd_setup_xfer(cmd->xfer, sc->cmd_tx_pipe, cmd, cmd->buf, xferlen,
837aa88c704Smpi USBD_FORCE_SHORT_XFER | USBD_NO_COPY | USBD_SYNCHRONOUS,
838aa88c704Smpi OTUS_CMD_TIMEOUT, NULL);
839aa88c704Smpi error = usbd_transfer(cmd->xfer);
84068baf11cSdamien if (error != 0) {
84168baf11cSdamien splx(s);
84268baf11cSdamien printf("%s: could not send command 0x%x (error=%s)\n",
84368baf11cSdamien sc->sc_dev.dv_xname, code, usbd_errstr(error));
84468baf11cSdamien return EIO;
84568baf11cSdamien }
84668baf11cSdamien if (!cmd->done)
84700f6cb32Smpi error = tsleep_nsec(cmd, PCATCH, "otuscmd", SEC_TO_NSEC(1));
84868baf11cSdamien cmd->odata = NULL; /* In case answer is received too late. */
84968baf11cSdamien splx(s);
85068baf11cSdamien if (error != 0) {
85168baf11cSdamien printf("%s: timeout waiting for command 0x%02x reply\n",
85268baf11cSdamien sc->sc_dev.dv_xname, code);
85368baf11cSdamien }
85468baf11cSdamien return error;
85568baf11cSdamien }
85668baf11cSdamien
85768baf11cSdamien void
otus_write(struct otus_softc * sc,uint32_t reg,uint32_t val)85868baf11cSdamien otus_write(struct otus_softc *sc, uint32_t reg, uint32_t val)
85968baf11cSdamien {
86068baf11cSdamien sc->write_buf[sc->write_idx].reg = htole32(reg);
86168baf11cSdamien sc->write_buf[sc->write_idx].val = htole32(val);
86268baf11cSdamien
86368baf11cSdamien if (++sc->write_idx > AR_MAX_WRITE_IDX)
86468baf11cSdamien (void)otus_write_barrier(sc);
86568baf11cSdamien }
86668baf11cSdamien
86768baf11cSdamien int
otus_write_barrier(struct otus_softc * sc)86868baf11cSdamien otus_write_barrier(struct otus_softc *sc)
86968baf11cSdamien {
87068baf11cSdamien int error;
87168baf11cSdamien
87268baf11cSdamien if (sc->write_idx == 0)
87368baf11cSdamien return 0; /* Nothing to flush. */
87468baf11cSdamien
87568baf11cSdamien error = otus_cmd(sc, AR_CMD_WREG, sc->write_buf,
87668baf11cSdamien sizeof (sc->write_buf[0]) * sc->write_idx, NULL);
87768baf11cSdamien sc->write_idx = 0;
87868baf11cSdamien return error;
87968baf11cSdamien }
88068baf11cSdamien
88168baf11cSdamien struct ieee80211_node *
otus_node_alloc(struct ieee80211com * ic)88268baf11cSdamien otus_node_alloc(struct ieee80211com *ic)
88368baf11cSdamien {
8848985a220Smglocker return malloc(sizeof (struct otus_node), M_USBDEV, M_NOWAIT | M_ZERO);
88568baf11cSdamien }
88668baf11cSdamien
88768baf11cSdamien int
otus_media_change(struct ifnet * ifp)88868baf11cSdamien otus_media_change(struct ifnet *ifp)
88968baf11cSdamien {
89068baf11cSdamien struct otus_softc *sc = ifp->if_softc;
89168baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
89268baf11cSdamien uint8_t rate, ridx;
89368baf11cSdamien int error;
89468baf11cSdamien
89568baf11cSdamien error = ieee80211_media_change(ifp);
89668baf11cSdamien if (error != ENETRESET)
89768baf11cSdamien return error;
89868baf11cSdamien
89968baf11cSdamien if (ic->ic_fixed_rate != -1) {
90068baf11cSdamien rate = ic->ic_sup_rates[ic->ic_curmode].
90168baf11cSdamien rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
90268baf11cSdamien for (ridx = 0; ridx <= OTUS_RIDX_MAX; ridx++)
90368baf11cSdamien if (otus_rates[ridx].rate == rate)
90468baf11cSdamien break;
90568baf11cSdamien sc->fixed_ridx = ridx;
90668baf11cSdamien }
90768baf11cSdamien
90868baf11cSdamien if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
90968baf11cSdamien error = otus_init(ifp);
91068baf11cSdamien
91168baf11cSdamien return error;
91268baf11cSdamien }
91368baf11cSdamien
91468baf11cSdamien int
otus_read_eeprom(struct otus_softc * sc)91568baf11cSdamien otus_read_eeprom(struct otus_softc *sc)
91668baf11cSdamien {
91768baf11cSdamien uint32_t regs[8], reg;
91868baf11cSdamien uint8_t *eep;
91968baf11cSdamien int i, j, error;
92068baf11cSdamien
92168baf11cSdamien /* Read EEPROM by blocks of 32 bytes. */
92268baf11cSdamien eep = (uint8_t *)&sc->eeprom;
923803f2018Sdamien reg = AR_EEPROM_OFFSET;
92468baf11cSdamien for (i = 0; i < sizeof (sc->eeprom) / 32; i++) {
92568baf11cSdamien for (j = 0; j < 8; j++, reg += 4)
92668baf11cSdamien regs[j] = htole32(reg);
92768baf11cSdamien error = otus_cmd(sc, AR_CMD_RREG, regs, sizeof regs, eep);
92868baf11cSdamien if (error != 0)
92968baf11cSdamien break;
93068baf11cSdamien eep += 32;
93168baf11cSdamien }
93268baf11cSdamien return error;
93368baf11cSdamien }
93468baf11cSdamien
93568baf11cSdamien void
otus_newassoc(struct ieee80211com * ic,struct ieee80211_node * ni,int isnew)93668baf11cSdamien otus_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew)
93768baf11cSdamien {
93868baf11cSdamien struct otus_softc *sc = ic->ic_softc;
93968baf11cSdamien struct otus_node *on = (void *)ni;
94068baf11cSdamien struct ieee80211_rateset *rs = &ni->ni_rates;
94168baf11cSdamien uint8_t rate;
94268baf11cSdamien int ridx, i;
94368baf11cSdamien
94468baf11cSdamien DPRINTF(("new assoc isnew=%d addr=%s\n",
94568baf11cSdamien isnew, ether_sprintf(ni->ni_macaddr)));
94668baf11cSdamien
94768baf11cSdamien ieee80211_amrr_node_init(&sc->amrr, &on->amn);
94868baf11cSdamien /* Start at lowest available bit-rate, AMRR will raise. */
94968baf11cSdamien ni->ni_txrate = 0;
95068baf11cSdamien
95168baf11cSdamien for (i = 0; i < rs->rs_nrates; i++) {
95268baf11cSdamien rate = rs->rs_rates[i] & IEEE80211_RATE_VAL;
95368baf11cSdamien /* Convert 802.11 rate to hardware rate index. */
95468baf11cSdamien for (ridx = 0; ridx <= OTUS_RIDX_MAX; ridx++)
95568baf11cSdamien if (otus_rates[ridx].rate == rate)
95668baf11cSdamien break;
95768baf11cSdamien on->ridx[i] = ridx;
95868baf11cSdamien DPRINTF(("rate=0x%02x ridx=%d\n",
95968baf11cSdamien rs->rs_rates[i], on->ridx[i]));
96068baf11cSdamien }
96168baf11cSdamien }
96268baf11cSdamien
963d7d7f774Sdamien void
otus_intr(struct usbd_xfer * xfer,void * priv,usbd_status status)964ab0b1be7Smglocker otus_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
965d7d7f774Sdamien {
966d7d7f774Sdamien #if 0
967d7d7f774Sdamien struct otus_softc *sc = priv;
968d7d7f774Sdamien int len;
969d7d7f774Sdamien
970d7d7f774Sdamien /*
971d7d7f774Sdamien * The Rx intr pipe is unused with current firmware. Notifications
972d7d7f774Sdamien * and replies to commands are sent through the Rx bulk pipe instead
973d7d7f774Sdamien * (with a magic PLCP header.)
974d7d7f774Sdamien */
975d7d7f774Sdamien if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
976d7d7f774Sdamien DPRINTF(("intr status=%d\n", status));
977d7d7f774Sdamien if (status == USBD_STALLED)
978d7d7f774Sdamien usbd_clear_endpoint_stall_async(sc->cmd_rx_pipe);
979d7d7f774Sdamien return;
980d7d7f774Sdamien }
981d7d7f774Sdamien usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
982d7d7f774Sdamien
983d7d7f774Sdamien otus_cmd_rxeof(sc, sc->ibuf, len);
984d7d7f774Sdamien #endif
985d7d7f774Sdamien }
986d7d7f774Sdamien
98768baf11cSdamien void
otus_cmd_rxeof(struct otus_softc * sc,uint8_t * buf,int len)98868baf11cSdamien otus_cmd_rxeof(struct otus_softc *sc, uint8_t *buf, int len)
98968baf11cSdamien {
99068baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
99168baf11cSdamien struct otus_tx_cmd *cmd;
99268baf11cSdamien struct ar_cmd_hdr *hdr;
99368baf11cSdamien int s;
99468baf11cSdamien
99568baf11cSdamien if (__predict_false(len < sizeof (*hdr))) {
99668baf11cSdamien DPRINTF(("cmd too small %d\n", len));
99768baf11cSdamien return;
99868baf11cSdamien }
99968baf11cSdamien hdr = (struct ar_cmd_hdr *)buf;
100068baf11cSdamien if (__predict_false(sizeof (*hdr) + hdr->len > len ||
100168baf11cSdamien sizeof (*hdr) + hdr->len > 64)) {
100268baf11cSdamien DPRINTF(("cmd too large %d\n", hdr->len));
100368baf11cSdamien return;
100468baf11cSdamien }
100568baf11cSdamien
100668baf11cSdamien if ((hdr->code & 0xc0) != 0xc0) {
100768baf11cSdamien DPRINTFN(2, ("received reply code=0x%02x len=%d token=%d\n",
100868baf11cSdamien hdr->code, hdr->len, hdr->token));
100968baf11cSdamien cmd = &sc->tx_cmd;
101068baf11cSdamien if (__predict_false(hdr->token != cmd->token))
101168baf11cSdamien return;
101268baf11cSdamien /* Copy answer into caller's supplied buffer. */
101368baf11cSdamien if (cmd->odata != NULL)
101468baf11cSdamien memcpy(cmd->odata, &hdr[1], hdr->len);
101568baf11cSdamien cmd->done = 1;
101668baf11cSdamien wakeup(cmd);
101768baf11cSdamien return;
101868baf11cSdamien }
101968baf11cSdamien
102068baf11cSdamien /* Received unsolicited notification. */
102168baf11cSdamien DPRINTF(("received notification code=0x%02x len=%d\n",
102268baf11cSdamien hdr->code, hdr->len));
102368baf11cSdamien switch (hdr->code & 0x3f) {
102468baf11cSdamien case AR_EVT_BEACON:
102568baf11cSdamien break;
102668baf11cSdamien case AR_EVT_TX_COMP:
102768baf11cSdamien {
102868baf11cSdamien struct ar_evt_tx_comp *tx = (struct ar_evt_tx_comp *)&hdr[1];
102968baf11cSdamien struct ieee80211_node *ni;
103068baf11cSdamien struct otus_node *on;
103168baf11cSdamien
103268baf11cSdamien DPRINTF(("tx completed %s status=%d phy=0x%x\n",
103368baf11cSdamien ether_sprintf(tx->macaddr), letoh16(tx->status),
103468baf11cSdamien letoh32(tx->phy)));
103568baf11cSdamien s = splnet();
103668baf11cSdamien #ifdef notyet
103768baf11cSdamien #ifndef IEEE80211_STA_ONLY
103868baf11cSdamien if (ic->ic_opmode != IEEE80211_M_STA) {
103968baf11cSdamien ni = ieee80211_find_node(ic, tx->macaddr);
104068baf11cSdamien if (__predict_false(ni == NULL)) {
104168baf11cSdamien splx(s);
104268baf11cSdamien break;
104368baf11cSdamien }
104468baf11cSdamien } else
104568baf11cSdamien #endif
104668baf11cSdamien #endif
104768baf11cSdamien ni = ic->ic_bss;
104868baf11cSdamien /* Update rate control statistics. */
104968baf11cSdamien on = (void *)ni;
105068baf11cSdamien /* NB: we do not set the TX_MAC_RATE_PROBING flag. */
105168baf11cSdamien if (__predict_true(tx->status != 0))
105268baf11cSdamien on->amn.amn_retrycnt++;
105368baf11cSdamien splx(s);
105468baf11cSdamien break;
105568baf11cSdamien }
105668baf11cSdamien case AR_EVT_TBTT:
105768baf11cSdamien break;
105868baf11cSdamien }
105968baf11cSdamien }
106068baf11cSdamien
106168baf11cSdamien void
otus_sub_rxeof(struct otus_softc * sc,uint8_t * buf,int len,struct mbuf_list * ml)10628fbaf8a2Sstsp otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, int len,
10638fbaf8a2Sstsp struct mbuf_list *ml)
106468baf11cSdamien {
106568baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
106668baf11cSdamien struct ifnet *ifp = &ic->ic_if;
106768baf11cSdamien struct ieee80211_rxinfo rxi;
106868baf11cSdamien struct ieee80211_node *ni;
106968baf11cSdamien struct ar_rx_tail *tail;
107068baf11cSdamien struct ieee80211_frame *wh;
107168baf11cSdamien struct mbuf *m;
107268baf11cSdamien uint8_t *plcp;
107368baf11cSdamien int s, mlen, align;
107468baf11cSdamien
107568baf11cSdamien if (__predict_false(len < AR_PLCP_HDR_LEN)) {
107668baf11cSdamien DPRINTF(("sub-xfer too short %d\n", len));
107768baf11cSdamien return;
107868baf11cSdamien }
107988185ad4Sdamien plcp = buf;
108068baf11cSdamien
108168baf11cSdamien /* All bits in the PLCP header are set to 1 for non-MPDU. */
108268baf11cSdamien if (memcmp(plcp, AR_PLCP_HDR_INTR, AR_PLCP_HDR_LEN) == 0) {
108368baf11cSdamien otus_cmd_rxeof(sc, plcp + AR_PLCP_HDR_LEN,
108468baf11cSdamien len - AR_PLCP_HDR_LEN);
108568baf11cSdamien return;
108668baf11cSdamien }
108768baf11cSdamien
108868baf11cSdamien /* Received MPDU. */
108968baf11cSdamien if (__predict_false(len < AR_PLCP_HDR_LEN + sizeof (*tail))) {
109068baf11cSdamien DPRINTF(("MPDU too short %d\n", len));
1091b86c5724Sdamien ifp->if_ierrors++;
109268baf11cSdamien return;
109368baf11cSdamien }
109468baf11cSdamien tail = (struct ar_rx_tail *)(plcp + len - sizeof (*tail));
109568baf11cSdamien
109668baf11cSdamien /* Discard error frames. */
109768baf11cSdamien if (__predict_false(tail->error != 0)) {
109868baf11cSdamien DPRINTF(("error frame 0x%02x\n", tail->error));
10994351e075Sdamien if (tail->error & AR_RX_ERROR_FCS) {
11004351e075Sdamien DPRINTFN(3, ("bad FCS\n"));
11014351e075Sdamien } else if (tail->error & AR_RX_ERROR_MMIC) {
1102b86c5724Sdamien /* Report Michael MIC failures to net80211. */
1103b86c5724Sdamien ic->ic_stats.is_rx_locmicfail++;
1104b86c5724Sdamien ieee80211_michael_mic_failure(ic, 0);
1105b86c5724Sdamien }
1106b86c5724Sdamien ifp->if_ierrors++;
110768baf11cSdamien return;
110868baf11cSdamien }
110968baf11cSdamien /* Compute MPDU's length. */
111068baf11cSdamien mlen = len - AR_PLCP_HDR_LEN - sizeof (*tail);
111168baf11cSdamien /* Make sure there's room for an 802.11 header + FCS. */
1112b86c5724Sdamien if (__predict_false(mlen < IEEE80211_MIN_LEN)) {
1113b86c5724Sdamien ifp->if_ierrors++;
111468baf11cSdamien return;
1115b86c5724Sdamien }
111668baf11cSdamien mlen -= IEEE80211_CRC_LEN; /* strip 802.11 FCS */
11177cdb832aSstsp if (mlen > MCLBYTES) {
11187cdb832aSstsp DPRINTF(("frame too large: %d\n", mlen));
11197cdb832aSstsp ifp->if_ierrors++;
11207cdb832aSstsp return;
11217cdb832aSstsp }
112268baf11cSdamien
112368baf11cSdamien wh = (struct ieee80211_frame *)(plcp + AR_PLCP_HDR_LEN);
112468baf11cSdamien /* Provide a 32-bit aligned protocol header to the stack. */
112568baf11cSdamien align = (ieee80211_has_qos(wh) ^ ieee80211_has_addr4(wh)) ? 2 : 0;
112668baf11cSdamien
112768baf11cSdamien MGETHDR(m, M_DONTWAIT, MT_DATA);
112868baf11cSdamien if (__predict_false(m == NULL)) {
112968baf11cSdamien ifp->if_ierrors++;
113068baf11cSdamien return;
113168baf11cSdamien }
113268baf11cSdamien if (align + mlen > MHLEN) {
113368baf11cSdamien MCLGET(m, M_DONTWAIT);
113468baf11cSdamien if (__predict_false(!(m->m_flags & M_EXT))) {
113568baf11cSdamien ifp->if_ierrors++;
113668baf11cSdamien m_freem(m);
113768baf11cSdamien return;
113868baf11cSdamien }
113968baf11cSdamien }
114068baf11cSdamien /* Finalize mbuf. */
114168baf11cSdamien m->m_data += align;
114268baf11cSdamien memcpy(mtod(m, caddr_t), wh, mlen);
114368baf11cSdamien m->m_pkthdr.len = m->m_len = mlen;
114468baf11cSdamien
114568baf11cSdamien #if NBPFILTER > 0
114668baf11cSdamien if (__predict_false(sc->sc_drvbpf != NULL)) {
114768baf11cSdamien struct otus_rx_radiotap_header *tap = &sc->sc_rxtap;
114868baf11cSdamien struct mbuf mb;
114968baf11cSdamien
115068baf11cSdamien tap->wr_flags = 0;
115168baf11cSdamien tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
115268baf11cSdamien tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
115368baf11cSdamien tap->wr_antsignal = tail->rssi;
115468baf11cSdamien tap->wr_rate = 2; /* In case it can't be found below. */
115568baf11cSdamien switch (tail->status & AR_RX_STATUS_MT_MASK) {
115668baf11cSdamien case AR_RX_STATUS_MT_CCK:
115768baf11cSdamien switch (plcp[0]) {
115888185ad4Sdamien case 10: tap->wr_rate = 2; break;
115988185ad4Sdamien case 20: tap->wr_rate = 4; break;
116088185ad4Sdamien case 55: tap->wr_rate = 11; break;
116188185ad4Sdamien case 110: tap->wr_rate = 22; break;
116268baf11cSdamien }
116368baf11cSdamien if (tail->status & AR_RX_STATUS_SHPREAMBLE)
116468baf11cSdamien tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
116568baf11cSdamien break;
116668baf11cSdamien case AR_RX_STATUS_MT_OFDM:
116768baf11cSdamien switch (plcp[0] & 0xf) {
116868baf11cSdamien case 0xb: tap->wr_rate = 12; break;
116968baf11cSdamien case 0xf: tap->wr_rate = 18; break;
117068baf11cSdamien case 0xa: tap->wr_rate = 24; break;
117168baf11cSdamien case 0xe: tap->wr_rate = 36; break;
117268baf11cSdamien case 0x9: tap->wr_rate = 48; break;
117368baf11cSdamien case 0xd: tap->wr_rate = 72; break;
117468baf11cSdamien case 0x8: tap->wr_rate = 96; break;
117568baf11cSdamien case 0xc: tap->wr_rate = 108; break;
117668baf11cSdamien }
117768baf11cSdamien break;
117868baf11cSdamien }
117968baf11cSdamien mb.m_data = (caddr_t)tap;
118068baf11cSdamien mb.m_len = sc->sc_rxtap_len;
118168baf11cSdamien mb.m_next = m;
118268baf11cSdamien mb.m_nextpkt = NULL;
118368baf11cSdamien mb.m_type = 0;
118468baf11cSdamien mb.m_flags = 0;
118568baf11cSdamien bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
118668baf11cSdamien }
118768baf11cSdamien #endif
118868baf11cSdamien
118968baf11cSdamien s = splnet();
119068baf11cSdamien ni = ieee80211_find_rxnode(ic, wh);
119152a13037Sstsp memset(&rxi, 0, sizeof(rxi));
119268baf11cSdamien rxi.rxi_rssi = tail->rssi;
11938fbaf8a2Sstsp ieee80211_inputm(ifp, m, ni, &rxi, ml);
119468baf11cSdamien
119568baf11cSdamien /* Node is no longer needed. */
119668baf11cSdamien ieee80211_release_node(ic, ni);
119768baf11cSdamien splx(s);
119868baf11cSdamien }
119968baf11cSdamien
120068baf11cSdamien void
otus_rxeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1201ab0b1be7Smglocker otus_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
120268baf11cSdamien {
12038fbaf8a2Sstsp struct mbuf_list ml = MBUF_LIST_INITIALIZER();
120468baf11cSdamien struct otus_rx_data *data = priv;
120568baf11cSdamien struct otus_softc *sc = data->sc;
120668baf11cSdamien caddr_t buf = data->buf;
120768baf11cSdamien struct ar_rx_head *head;
120868baf11cSdamien uint16_t hlen;
120968baf11cSdamien int len;
121068baf11cSdamien
121168baf11cSdamien if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
121268baf11cSdamien DPRINTF(("RX status=%d\n", status));
121368baf11cSdamien if (status == USBD_STALLED)
121468baf11cSdamien usbd_clear_endpoint_stall_async(sc->data_rx_pipe);
121568baf11cSdamien if (status != USBD_CANCELLED)
121668baf11cSdamien goto resubmit;
121768baf11cSdamien return;
121868baf11cSdamien }
121968baf11cSdamien usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
122068baf11cSdamien
122168baf11cSdamien while (len >= sizeof (*head)) {
122268baf11cSdamien head = (struct ar_rx_head *)buf;
122368baf11cSdamien if (__predict_false(head->tag != htole16(AR_RX_HEAD_TAG))) {
122468baf11cSdamien DPRINTF(("tag not valid 0x%x\n", letoh16(head->tag)));
122568baf11cSdamien break;
122668baf11cSdamien }
122768baf11cSdamien hlen = letoh16(head->len);
122868baf11cSdamien if (__predict_false(sizeof (*head) + hlen > len)) {
122968baf11cSdamien DPRINTF(("xfer too short %d/%d\n", len, hlen));
123068baf11cSdamien break;
123168baf11cSdamien }
123268baf11cSdamien /* Process sub-xfer. */
12338fbaf8a2Sstsp otus_sub_rxeof(sc, (uint8_t *)&head[1], hlen, &ml);
123468baf11cSdamien
123568baf11cSdamien /* Next sub-xfer is aligned on a 32-bit boundary. */
123668baf11cSdamien hlen = (sizeof (*head) + hlen + 3) & ~3;
123768baf11cSdamien buf += hlen;
123868baf11cSdamien len -= hlen;
123968baf11cSdamien }
12408fbaf8a2Sstsp if_input(&sc->sc_ic.ic_if, &ml);
124168baf11cSdamien
124268baf11cSdamien resubmit:
124368baf11cSdamien usbd_setup_xfer(xfer, sc->data_rx_pipe, data, data->buf, OTUS_RXBUFSZ,
124468baf11cSdamien USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, otus_rxeof);
124568baf11cSdamien (void)usbd_transfer(data->xfer);
124668baf11cSdamien }
124768baf11cSdamien
124868baf11cSdamien void
otus_txeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1249ab0b1be7Smglocker otus_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
125068baf11cSdamien {
125168baf11cSdamien struct otus_tx_data *data = priv;
125268baf11cSdamien struct otus_softc *sc = data->sc;
125368baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
125468baf11cSdamien struct ifnet *ifp = &ic->ic_if;
125568baf11cSdamien int s;
125668baf11cSdamien
1257ba34d050Sdamien s = splnet();
1258ba34d050Sdamien sc->tx_queued--;
125968baf11cSdamien if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
126068baf11cSdamien DPRINTF(("TX status=%d\n", status));
126168baf11cSdamien if (status == USBD_STALLED)
126268baf11cSdamien usbd_clear_endpoint_stall_async(sc->data_tx_pipe);
126368baf11cSdamien ifp->if_oerrors++;
1264ba34d050Sdamien splx(s);
126568baf11cSdamien return;
126668baf11cSdamien }
126768baf11cSdamien sc->sc_tx_timer = 0;
1268de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd);
126968baf11cSdamien otus_start(ifp);
127068baf11cSdamien splx(s);
127168baf11cSdamien }
127268baf11cSdamien
127368baf11cSdamien int
otus_tx(struct otus_softc * sc,struct mbuf * m,struct ieee80211_node * ni)127468baf11cSdamien otus_tx(struct otus_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
127568baf11cSdamien {
127668baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
127768baf11cSdamien struct otus_node *on = (void *)ni;
127868baf11cSdamien struct otus_tx_data *data;
127968baf11cSdamien struct ieee80211_frame *wh;
128068baf11cSdamien struct ieee80211_key *k;
128168baf11cSdamien struct ar_tx_head *head;
128268baf11cSdamien uint32_t phyctl;
128368baf11cSdamien uint16_t macctl, qos;
128488185ad4Sdamien uint8_t tid, qid;
128568baf11cSdamien int error, ridx, hasqos, xferlen;
128668baf11cSdamien
128768baf11cSdamien wh = mtod(m, struct ieee80211_frame *);
128868baf11cSdamien if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
128968baf11cSdamien k = ieee80211_get_txkey(ic, wh, ni);
129068baf11cSdamien if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
129168baf11cSdamien return ENOBUFS;
129268baf11cSdamien wh = mtod(m, struct ieee80211_frame *);
129368baf11cSdamien }
129468baf11cSdamien
129588185ad4Sdamien if ((hasqos = ieee80211_has_qos(wh))) {
129668baf11cSdamien qos = ieee80211_get_qos(wh);
129788185ad4Sdamien tid = qos & IEEE80211_QOS_TID;
129888185ad4Sdamien qid = ieee80211_up_to_ac(ic, tid);
12993dc83bf9Shaesbaert } else {
13003dc83bf9Shaesbaert qos = 0;
130188185ad4Sdamien qid = EDCA_AC_BE;
13023dc83bf9Shaesbaert }
130368baf11cSdamien
130468baf11cSdamien /* Pickup a rate index. */
130568baf11cSdamien if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
130668baf11cSdamien (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA)
130768baf11cSdamien ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
130868baf11cSdamien OTUS_RIDX_OFDM6 : OTUS_RIDX_CCK1;
130968baf11cSdamien else if (ic->ic_fixed_rate != -1)
131068baf11cSdamien ridx = sc->fixed_ridx;
131168baf11cSdamien else
131268baf11cSdamien ridx = on->ridx[ni->ni_txrate];
131368baf11cSdamien
131468baf11cSdamien phyctl = 0;
13154351e075Sdamien macctl = AR_TX_MAC_BACKOFF | AR_TX_MAC_HW_DUR | AR_TX_MAC_QID(qid);
131668baf11cSdamien
131768baf11cSdamien if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
131868baf11cSdamien (hasqos && ((qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
131968baf11cSdamien IEEE80211_QOS_ACK_POLICY_NOACK)))
132068baf11cSdamien macctl |= AR_TX_MAC_NOACK;
132168baf11cSdamien
1322d7d7f774Sdamien if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1323d7d7f774Sdamien if (m->m_pkthdr.len + IEEE80211_CRC_LEN >= ic->ic_rtsthreshold)
132468baf11cSdamien macctl |= AR_TX_MAC_RTS;
132568baf11cSdamien else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
132668baf11cSdamien ridx >= OTUS_RIDX_OFDM6) {
132768baf11cSdamien if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
132868baf11cSdamien macctl |= AR_TX_MAC_CTS;
132968baf11cSdamien else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
133068baf11cSdamien macctl |= AR_TX_MAC_RTS;
133168baf11cSdamien }
1332d7d7f774Sdamien }
133368baf11cSdamien
133468baf11cSdamien phyctl |= AR_TX_PHY_MCS(otus_rates[ridx].mcs);
133568baf11cSdamien if (ridx >= OTUS_RIDX_OFDM6) {
133668baf11cSdamien phyctl |= AR_TX_PHY_MT_OFDM;
133768baf11cSdamien if (ridx <= OTUS_RIDX_OFDM24)
1338803f2018Sdamien phyctl |= AR_TX_PHY_ANTMSK(sc->txmask);
133968baf11cSdamien else
134068baf11cSdamien phyctl |= AR_TX_PHY_ANTMSK(1);
134168baf11cSdamien } else { /* CCK */
134268baf11cSdamien phyctl |= AR_TX_PHY_MT_CCK;
1343803f2018Sdamien phyctl |= AR_TX_PHY_ANTMSK(sc->txmask);
134468baf11cSdamien }
134568baf11cSdamien
1346d7d7f774Sdamien /* Update rate control stats for frames that are ACK'ed. */
134768baf11cSdamien if (!(macctl & AR_TX_MAC_NOACK))
134868baf11cSdamien ((struct otus_node *)ni)->amn.amn_txcnt++;
134968baf11cSdamien
135068baf11cSdamien data = &sc->tx_data[sc->tx_cur];
135168baf11cSdamien /* Fill Tx descriptor. */
135268baf11cSdamien head = (struct ar_tx_head *)data->buf;
135368baf11cSdamien head->len = htole16(m->m_pkthdr.len + IEEE80211_CRC_LEN);
135468baf11cSdamien head->macctl = htole16(macctl);
135568baf11cSdamien head->phyctl = htole32(phyctl);
135668baf11cSdamien
135768baf11cSdamien #if NBPFILTER > 0
135868baf11cSdamien if (__predict_false(sc->sc_drvbpf != NULL)) {
135968baf11cSdamien struct otus_tx_radiotap_header *tap = &sc->sc_txtap;
136068baf11cSdamien struct mbuf mb;
136168baf11cSdamien
136268baf11cSdamien tap->wt_flags = 0;
136368baf11cSdamien tap->wt_rate = otus_rates[ridx].rate;
136468baf11cSdamien tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
136568baf11cSdamien tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
136668baf11cSdamien
136768baf11cSdamien mb.m_data = (caddr_t)tap;
136868baf11cSdamien mb.m_len = sc->sc_txtap_len;
136968baf11cSdamien mb.m_next = m;
137068baf11cSdamien mb.m_nextpkt = NULL;
137168baf11cSdamien mb.m_type = 0;
137268baf11cSdamien mb.m_flags = 0;
137368baf11cSdamien bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
137468baf11cSdamien }
137568baf11cSdamien #endif
137668baf11cSdamien
137768baf11cSdamien xferlen = sizeof (*head) + m->m_pkthdr.len;
13785c7fed39Sdlg m_copydata(m, 0, m->m_pkthdr.len, &head[1]);
137968baf11cSdamien m_freem(m);
138068baf11cSdamien
138168baf11cSdamien DPRINTFN(5, ("tx queued=%d len=%d mac=0x%04x phy=0x%08x rate=%d\n",
138268baf11cSdamien sc->tx_queued, head->len, head->macctl, head->phyctl,
138368baf11cSdamien otus_rates[ridx].rate));
138468baf11cSdamien usbd_setup_xfer(data->xfer, sc->data_tx_pipe, data, data->buf, xferlen,
138568baf11cSdamien USBD_FORCE_SHORT_XFER | USBD_NO_COPY, OTUS_TX_TIMEOUT, otus_txeof);
138668baf11cSdamien error = usbd_transfer(data->xfer);
138768baf11cSdamien if (__predict_false(error != USBD_IN_PROGRESS && error != 0))
138868baf11cSdamien return error;
138968baf11cSdamien
13901b0b2675Sdamien ieee80211_release_node(ic, ni);
13911b0b2675Sdamien
139268baf11cSdamien sc->tx_queued++;
139368baf11cSdamien sc->tx_cur = (sc->tx_cur + 1) % OTUS_TX_DATA_LIST_COUNT;
139468baf11cSdamien
139568baf11cSdamien return 0;
139668baf11cSdamien }
139768baf11cSdamien
139868baf11cSdamien void
otus_start(struct ifnet * ifp)139968baf11cSdamien otus_start(struct ifnet *ifp)
140068baf11cSdamien {
140168baf11cSdamien struct otus_softc *sc = ifp->if_softc;
140268baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
140368baf11cSdamien struct ieee80211_node *ni;
140468baf11cSdamien struct mbuf *m;
140568baf11cSdamien
1406de6cd8fbSdlg if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
140768baf11cSdamien return;
140868baf11cSdamien
140968baf11cSdamien for (;;) {
141068baf11cSdamien if (sc->tx_queued >= OTUS_TX_DATA_LIST_COUNT) {
1411de6cd8fbSdlg ifq_set_oactive(&ifp->if_snd);
141268baf11cSdamien break;
141368baf11cSdamien }
141468baf11cSdamien /* Send pending management frames first. */
1415351e1934Sdlg m = mq_dequeue(&ic->ic_mgtq);
141668baf11cSdamien if (m != NULL) {
14176da4b19dSmpi ni = m->m_pkthdr.ph_cookie;
141868baf11cSdamien goto sendit;
141968baf11cSdamien }
142068baf11cSdamien if (ic->ic_state != IEEE80211_S_RUN)
142168baf11cSdamien break;
142268baf11cSdamien
142368baf11cSdamien /* Encapsulate and send data frames. */
142463bcfa73Spatrick m = ifq_dequeue(&ifp->if_snd);
142568baf11cSdamien if (m == NULL)
142668baf11cSdamien break;
142768baf11cSdamien #if NBPFILTER > 0
142868baf11cSdamien if (ifp->if_bpf != NULL)
142968baf11cSdamien bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
143068baf11cSdamien #endif
143168baf11cSdamien if ((m = ieee80211_encap(ifp, m, &ni)) == NULL)
143268baf11cSdamien continue;
143368baf11cSdamien sendit:
143468baf11cSdamien #if NBPFILTER > 0
143568baf11cSdamien if (ic->ic_rawbpf != NULL)
143668baf11cSdamien bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
143768baf11cSdamien #endif
143868baf11cSdamien if (otus_tx(sc, m, ni) != 0) {
14391b0b2675Sdamien ieee80211_release_node(ic, ni);
144068baf11cSdamien ifp->if_oerrors++;
144168baf11cSdamien continue;
144268baf11cSdamien }
144368baf11cSdamien
144468baf11cSdamien sc->sc_tx_timer = 5;
144568baf11cSdamien ifp->if_timer = 1;
144668baf11cSdamien }
144768baf11cSdamien }
144868baf11cSdamien
144968baf11cSdamien void
otus_watchdog(struct ifnet * ifp)145068baf11cSdamien otus_watchdog(struct ifnet *ifp)
145168baf11cSdamien {
145268baf11cSdamien struct otus_softc *sc = ifp->if_softc;
145368baf11cSdamien
145468baf11cSdamien ifp->if_timer = 0;
145568baf11cSdamien
145668baf11cSdamien if (sc->sc_tx_timer > 0) {
145768baf11cSdamien if (--sc->sc_tx_timer == 0) {
145868baf11cSdamien printf("%s: device timeout\n", sc->sc_dev.dv_xname);
145968baf11cSdamien /* otus_init(ifp); XXX needs a process context! */
146068baf11cSdamien ifp->if_oerrors++;
146168baf11cSdamien return;
146268baf11cSdamien }
146368baf11cSdamien ifp->if_timer = 1;
146468baf11cSdamien }
146568baf11cSdamien ieee80211_watchdog(ifp);
146668baf11cSdamien }
146768baf11cSdamien
146868baf11cSdamien int
otus_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)146968baf11cSdamien otus_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
147068baf11cSdamien {
147168baf11cSdamien struct otus_softc *sc = ifp->if_softc;
147268baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
147368baf11cSdamien int s, error = 0;
147468baf11cSdamien
14755ad57547Sjakemsr if (usbd_is_dying(sc->sc_udev))
14765ad57547Sjakemsr return ENXIO;
14775ad57547Sjakemsr
14785ad57547Sjakemsr usbd_ref_incr(sc->sc_udev);
14795ad57547Sjakemsr
148068baf11cSdamien s = splnet();
148168baf11cSdamien
148268baf11cSdamien switch (cmd) {
148368baf11cSdamien case SIOCSIFADDR:
148468baf11cSdamien ifp->if_flags |= IFF_UP;
148568baf11cSdamien /* FALLTHROUGH */
148668baf11cSdamien case SIOCSIFFLAGS:
148768baf11cSdamien if (ifp->if_flags & IFF_UP) {
148868baf11cSdamien if ((ifp->if_flags & IFF_RUNNING) &&
148968baf11cSdamien ((ifp->if_flags ^ sc->sc_if_flags) &
149068baf11cSdamien (IFF_ALLMULTI | IFF_PROMISC)) != 0) {
149168baf11cSdamien otus_set_multi(sc);
149268baf11cSdamien } else if (!(ifp->if_flags & IFF_RUNNING))
149368baf11cSdamien otus_init(ifp);
149468baf11cSdamien
149568baf11cSdamien } else if (ifp->if_flags & IFF_RUNNING)
149668baf11cSdamien otus_stop(ifp);
149768baf11cSdamien
149868baf11cSdamien sc->sc_if_flags = ifp->if_flags;
149968baf11cSdamien break;
150088185ad4Sdamien case SIOCS80211CHANNEL:
150188185ad4Sdamien error = ieee80211_ioctl(ifp, cmd, data);
150288185ad4Sdamien if (error == ENETRESET &&
150388185ad4Sdamien ic->ic_opmode == IEEE80211_M_MONITOR) {
150488185ad4Sdamien if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
150588185ad4Sdamien (IFF_UP | IFF_RUNNING))
15066e30e88dSdamien otus_set_chan(sc, ic->ic_ibss_chan, 0);
150788185ad4Sdamien error = 0;
150888185ad4Sdamien }
150988185ad4Sdamien break;
151068baf11cSdamien default:
151168baf11cSdamien error = ieee80211_ioctl(ifp, cmd, data);
151268baf11cSdamien }
151368baf11cSdamien
151468baf11cSdamien if (error == ENETRESET) {
151568baf11cSdamien if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
151668baf11cSdamien (IFF_UP | IFF_RUNNING))
151768baf11cSdamien otus_init(ifp);
151868baf11cSdamien error = 0;
151968baf11cSdamien }
152068baf11cSdamien
152168baf11cSdamien splx(s);
15225ad57547Sjakemsr
15235ad57547Sjakemsr usbd_ref_decr(sc->sc_udev);
15245ad57547Sjakemsr
152568baf11cSdamien return error;
152668baf11cSdamien }
152768baf11cSdamien
152868baf11cSdamien int
otus_set_multi(struct otus_softc * sc)152968baf11cSdamien otus_set_multi(struct otus_softc *sc)
153068baf11cSdamien {
153168baf11cSdamien struct arpcom *ac = &sc->sc_ic.ic_ac;
153268baf11cSdamien struct ifnet *ifp = &ac->ac_if;
153368baf11cSdamien struct ether_multi *enm;
153468baf11cSdamien struct ether_multistep step;
153568baf11cSdamien uint32_t lo, hi;
153668baf11cSdamien uint8_t bit;
153768baf11cSdamien
15385caeb4dfSmpi if (ac->ac_multirangecnt > 0)
15395caeb4dfSmpi ifp->if_flags |= IFF_ALLMULTI;
15405caeb4dfSmpi
154168baf11cSdamien if ((ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) != 0) {
154268baf11cSdamien lo = hi = 0xffffffff;
154368baf11cSdamien goto done;
154468baf11cSdamien }
154568baf11cSdamien lo = hi = 0;
154668baf11cSdamien ETHER_FIRST_MULTI(step, ac, enm);
154768baf11cSdamien while (enm != NULL) {
154868baf11cSdamien bit = enm->enm_addrlo[5] >> 2;
154968baf11cSdamien if (bit < 32)
155068baf11cSdamien lo |= 1 << bit;
155168baf11cSdamien else
155268baf11cSdamien hi |= 1 << (bit - 32);
155368baf11cSdamien ETHER_NEXT_MULTI(step, enm);
155468baf11cSdamien }
155568baf11cSdamien done:
155661e87b28Sderaadt hi |= 1U << 31; /* Make sure the broadcast bit is set. */
155768baf11cSdamien otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_L, lo);
155868baf11cSdamien otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_H, hi);
155968baf11cSdamien return otus_write_barrier(sc);
156068baf11cSdamien }
156168baf11cSdamien
156268baf11cSdamien void
otus_updateedca(struct ieee80211com * ic)156368baf11cSdamien otus_updateedca(struct ieee80211com *ic)
156468baf11cSdamien {
156568baf11cSdamien /* Do it in a process context. */
156668baf11cSdamien otus_do_async(ic->ic_softc, otus_updateedca_cb, NULL, 0);
156768baf11cSdamien }
156868baf11cSdamien
156968baf11cSdamien void
otus_updateedca_cb(struct otus_softc * sc,void * arg)157068baf11cSdamien otus_updateedca_cb(struct otus_softc *sc, void *arg)
157168baf11cSdamien {
157268baf11cSdamien #define EXP2(val) ((1 << (val)) - 1)
157368baf11cSdamien #define AIFS(val) ((val) * 9 + 10)
157468baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
157568baf11cSdamien const struct ieee80211_edca_ac_params *edca;
157668baf11cSdamien int s;
157768baf11cSdamien
157868baf11cSdamien s = splnet();
157968baf11cSdamien
158068baf11cSdamien edca = (ic->ic_flags & IEEE80211_F_QOS) ?
158168baf11cSdamien ic->ic_edca_ac : otus_edca_def;
158268baf11cSdamien
158368baf11cSdamien /* Set CWmin/CWmax values. */
158468baf11cSdamien otus_write(sc, AR_MAC_REG_AC0_CW,
158568baf11cSdamien EXP2(edca[EDCA_AC_BE].ac_ecwmax) << 16 |
158668baf11cSdamien EXP2(edca[EDCA_AC_BE].ac_ecwmin));
158768baf11cSdamien otus_write(sc, AR_MAC_REG_AC1_CW,
158868baf11cSdamien EXP2(edca[EDCA_AC_BK].ac_ecwmax) << 16 |
158968baf11cSdamien EXP2(edca[EDCA_AC_BK].ac_ecwmin));
159068baf11cSdamien otus_write(sc, AR_MAC_REG_AC2_CW,
159168baf11cSdamien EXP2(edca[EDCA_AC_VI].ac_ecwmax) << 16 |
159268baf11cSdamien EXP2(edca[EDCA_AC_VI].ac_ecwmin));
159368baf11cSdamien otus_write(sc, AR_MAC_REG_AC3_CW,
159468baf11cSdamien EXP2(edca[EDCA_AC_VO].ac_ecwmax) << 16 |
159568baf11cSdamien EXP2(edca[EDCA_AC_VO].ac_ecwmin));
159668baf11cSdamien otus_write(sc, AR_MAC_REG_AC4_CW, /* Special TXQ. */
159768baf11cSdamien EXP2(edca[EDCA_AC_VO].ac_ecwmax) << 16 |
159868baf11cSdamien EXP2(edca[EDCA_AC_VO].ac_ecwmin));
159968baf11cSdamien
160068baf11cSdamien /* Set AIFSN values. */
160168baf11cSdamien otus_write(sc, AR_MAC_REG_AC1_AC0_AIFS,
160268baf11cSdamien AIFS(edca[EDCA_AC_VI].ac_aifsn) << 24 |
160368baf11cSdamien AIFS(edca[EDCA_AC_BK].ac_aifsn) << 12 |
160468baf11cSdamien AIFS(edca[EDCA_AC_BE].ac_aifsn));
160568baf11cSdamien otus_write(sc, AR_MAC_REG_AC3_AC2_AIFS,
160668baf11cSdamien AIFS(edca[EDCA_AC_VO].ac_aifsn) << 16 | /* Special TXQ. */
160768baf11cSdamien AIFS(edca[EDCA_AC_VO].ac_aifsn) << 4 |
160868baf11cSdamien AIFS(edca[EDCA_AC_VI].ac_aifsn) >> 8);
160968baf11cSdamien
161068baf11cSdamien /* Set TXOP limit. */
161168baf11cSdamien otus_write(sc, AR_MAC_REG_AC1_AC0_TXOP,
161268baf11cSdamien edca[EDCA_AC_BK].ac_txoplimit << 16 |
161368baf11cSdamien edca[EDCA_AC_BE].ac_txoplimit);
161468baf11cSdamien otus_write(sc, AR_MAC_REG_AC3_AC2_TXOP,
161568baf11cSdamien edca[EDCA_AC_VO].ac_txoplimit << 16 |
161668baf11cSdamien edca[EDCA_AC_VI].ac_txoplimit);
161768baf11cSdamien
161868baf11cSdamien splx(s);
161968baf11cSdamien
162068baf11cSdamien (void)otus_write_barrier(sc);
162168baf11cSdamien #undef AIFS
162268baf11cSdamien #undef EXP2
162368baf11cSdamien }
162468baf11cSdamien
162568baf11cSdamien void
otus_updateslot(struct ieee80211com * ic)162668baf11cSdamien otus_updateslot(struct ieee80211com *ic)
162768baf11cSdamien {
162868baf11cSdamien /* Do it in a process context. */
162968baf11cSdamien otus_do_async(ic->ic_softc, otus_updateslot_cb, NULL, 0);
163068baf11cSdamien }
163168baf11cSdamien
163268baf11cSdamien void
otus_updateslot_cb(struct otus_softc * sc,void * arg)163368baf11cSdamien otus_updateslot_cb(struct otus_softc *sc, void *arg)
163468baf11cSdamien {
163568baf11cSdamien uint32_t slottime;
163668baf11cSdamien
16378443256dSkevlo slottime = (sc->sc_ic.ic_flags & IEEE80211_F_SHSLOT) ?
16388443256dSkevlo IEEE80211_DUR_DS_SHSLOT: IEEE80211_DUR_DS_SLOT;
163968baf11cSdamien otus_write(sc, AR_MAC_REG_SLOT_TIME, slottime << 10);
164068baf11cSdamien (void)otus_write_barrier(sc);
164168baf11cSdamien }
164268baf11cSdamien
164368baf11cSdamien int
otus_init_mac(struct otus_softc * sc)164468baf11cSdamien otus_init_mac(struct otus_softc *sc)
164568baf11cSdamien {
164668baf11cSdamien int error;
164768baf11cSdamien
164868baf11cSdamien otus_write(sc, AR_MAC_REG_ACK_EXTENSION, 0x40);
164968baf11cSdamien otus_write(sc, AR_MAC_REG_RETRY_MAX, 0);
165068baf11cSdamien otus_write(sc, AR_MAC_REG_SNIFFER, 0x2000000);
165168baf11cSdamien otus_write(sc, AR_MAC_REG_RX_THRESHOLD, 0xc1f80);
165268baf11cSdamien otus_write(sc, AR_MAC_REG_RX_PE_DELAY, 0x70);
165368baf11cSdamien otus_write(sc, AR_MAC_REG_EIFS_AND_SIFS, 0xa144000);
165468baf11cSdamien otus_write(sc, AR_MAC_REG_SLOT_TIME, 9 << 10);
165568baf11cSdamien otus_write(sc, 0x1c3b2c, 0x19000000);
165668baf11cSdamien /* NAV protects ACK only (in TXOP). */
165768baf11cSdamien otus_write(sc, 0x1c3b38, 0x201);
165868baf11cSdamien /* Set beacon Tx power to 0x7. */
165968baf11cSdamien otus_write(sc, AR_MAC_REG_BCN_HT1, 0x8000170);
166068baf11cSdamien otus_write(sc, AR_MAC_REG_BACKOFF_PROTECT, 0x105);
166168baf11cSdamien otus_write(sc, 0x1c3b9c, 0x10000a);
166268baf11cSdamien /* Filter any control frames, BAR is bit 24. */
166368baf11cSdamien otus_write(sc, 0x1c368c, 0x0500ffff);
166468baf11cSdamien otus_write(sc, 0x1c3c40, 0x1);
166568baf11cSdamien otus_write(sc, AR_MAC_REG_BASIC_RATE, 0x150f);
166668baf11cSdamien otus_write(sc, AR_MAC_REG_MANDATORY_RATE, 0x150f);
166768baf11cSdamien otus_write(sc, AR_MAC_REG_RTS_CTS_RATE, 0x10b01bb);
166868baf11cSdamien otus_write(sc, 0x1c3694, 0x4003c1e);
166968baf11cSdamien /* Enable LED0 and LED1. */
167068baf11cSdamien otus_write(sc, 0x1d0100, 0x3);
167168baf11cSdamien otus_write(sc, 0x1d0104, 0x3);
167268baf11cSdamien /* Switch MAC to OTUS interface. */
167368baf11cSdamien otus_write(sc, 0x1c3600, 0x3);
167468baf11cSdamien otus_write(sc, 0x1c3c50, 0xffff);
167568baf11cSdamien otus_write(sc, 0x1c3680, 0xf00008);
167668baf11cSdamien /* Disable Rx timeout (workaround). */
167768baf11cSdamien otus_write(sc, 0x1c362c, 0);
167868baf11cSdamien
167968baf11cSdamien /* Set USB Rx stream mode maximum frame number to 2. */
168068baf11cSdamien otus_write(sc, 0x1e1110, 0x4);
168168baf11cSdamien /* Set USB Rx stream mode timeout to 10us. */
168268baf11cSdamien otus_write(sc, 0x1e1114, 0x80);
168368baf11cSdamien
168468baf11cSdamien /* Set clock frequency to 88/80MHz. */
168568baf11cSdamien otus_write(sc, 0x1d4008, 0x73);
168668baf11cSdamien /* Set WLAN DMA interrupt mode: generate intr per packet. */
168768baf11cSdamien otus_write(sc, 0x1c3d7c, 0x110011);
168868baf11cSdamien otus_write(sc, 0x1c3bb0, 0x4);
168968baf11cSdamien otus_write(sc, AR_MAC_REG_TXOP_NOT_ENOUGH_INDICATION, 0x141e0f48);
169068baf11cSdamien
169168baf11cSdamien /* Disable HW decryption for now. */
169268baf11cSdamien otus_write(sc, 0x1c3678, 0x78);
169368baf11cSdamien
169468baf11cSdamien if ((error = otus_write_barrier(sc)) != 0)
169568baf11cSdamien return error;
169668baf11cSdamien
169768baf11cSdamien /* Set default EDCA parameters. */
169868baf11cSdamien otus_updateedca_cb(sc, NULL);
169968baf11cSdamien
170068baf11cSdamien return 0;
170168baf11cSdamien }
170268baf11cSdamien
170368baf11cSdamien /*
170468baf11cSdamien * Return default value for PHY register based on current operating mode.
170568baf11cSdamien */
170668baf11cSdamien uint32_t
otus_phy_get_def(struct otus_softc * sc,uint32_t reg)170768baf11cSdamien otus_phy_get_def(struct otus_softc *sc, uint32_t reg)
170868baf11cSdamien {
170968baf11cSdamien int i;
171068baf11cSdamien
171168baf11cSdamien for (i = 0; i < nitems(ar5416_phy_regs); i++)
171268baf11cSdamien if (AR_PHY(ar5416_phy_regs[i]) == reg)
171368baf11cSdamien return sc->phy_vals[i];
171488185ad4Sdamien return 0; /* Register not found. */
171568baf11cSdamien }
171668baf11cSdamien
171768baf11cSdamien /*
171868baf11cSdamien * Update PHY's programming based on vendor-specific data stored in EEPROM.
171968baf11cSdamien * This is for FEM-type devices only.
172068baf11cSdamien */
172168baf11cSdamien int
otus_set_board_values(struct otus_softc * sc,struct ieee80211_channel * c)172268baf11cSdamien otus_set_board_values(struct otus_softc *sc, struct ieee80211_channel *c)
172368baf11cSdamien {
172468baf11cSdamien const struct ModalEepHeader *eep;
172568baf11cSdamien uint32_t tmp, offset;
172668baf11cSdamien
172768baf11cSdamien if (IEEE80211_IS_CHAN_5GHZ(c))
172868baf11cSdamien eep = &sc->eeprom.modalHeader[0];
172968baf11cSdamien else
173068baf11cSdamien eep = &sc->eeprom.modalHeader[1];
173168baf11cSdamien
173268baf11cSdamien /* Offset of chain 2. */
173368baf11cSdamien offset = 2 * 0x1000;
173468baf11cSdamien
173568baf11cSdamien tmp = letoh32(eep->antCtrlCommon);
1736803f2018Sdamien otus_write(sc, AR_PHY_SWITCH_COM, tmp);
173768baf11cSdamien
173868baf11cSdamien tmp = letoh32(eep->antCtrlChain[0]);
1739803f2018Sdamien otus_write(sc, AR_PHY_SWITCH_CHAIN_0, tmp);
174068baf11cSdamien
174168baf11cSdamien tmp = letoh32(eep->antCtrlChain[1]);
1742803f2018Sdamien otus_write(sc, AR_PHY_SWITCH_CHAIN_0 + offset, tmp);
174368baf11cSdamien
174468baf11cSdamien if (1 /* sc->sc_sco == AR_SCO_SCN */) {
174568baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_SETTLING);
174668baf11cSdamien tmp &= ~(0x7f << 7);
174768baf11cSdamien tmp |= (eep->switchSettling & 0x7f) << 7;
1748803f2018Sdamien otus_write(sc, AR_PHY_SETTLING, tmp);
174968baf11cSdamien }
175068baf11cSdamien
175168baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_DESIRED_SZ);
175268baf11cSdamien tmp &= ~0xffff;
175368baf11cSdamien tmp |= eep->pgaDesiredSize << 8 | eep->adcDesiredSize;
1754803f2018Sdamien otus_write(sc, AR_PHY_DESIRED_SZ, tmp);
175568baf11cSdamien
175668baf11cSdamien tmp = eep->txEndToXpaOff << 24 | eep->txEndToXpaOff << 16 |
175768baf11cSdamien eep->txFrameToXpaOn << 8 | eep->txFrameToXpaOn;
1758803f2018Sdamien otus_write(sc, AR_PHY_RF_CTL4, tmp);
175968baf11cSdamien
176068baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_RF_CTL3);
176168baf11cSdamien tmp &= ~(0xff << 16);
176268baf11cSdamien tmp |= eep->txEndToRxOn << 16;
1763803f2018Sdamien otus_write(sc, AR_PHY_RF_CTL3, tmp);
176468baf11cSdamien
176568baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_CCA);
176668baf11cSdamien tmp &= ~(0x7f << 12);
176768baf11cSdamien tmp |= (eep->thresh62 & 0x7f) << 12;
1768803f2018Sdamien otus_write(sc, AR_PHY_CCA, tmp);
176968baf11cSdamien
177068baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_RXGAIN);
177168baf11cSdamien tmp &= ~(0x3f << 12);
177268baf11cSdamien tmp |= (eep->txRxAttenCh[0] & 0x3f) << 12;
1773803f2018Sdamien otus_write(sc, AR_PHY_RXGAIN, tmp);
177468baf11cSdamien
177568baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_RXGAIN + offset);
177668baf11cSdamien tmp &= ~(0x3f << 12);
177768baf11cSdamien tmp |= (eep->txRxAttenCh[1] & 0x3f) << 12;
1778803f2018Sdamien otus_write(sc, AR_PHY_RXGAIN + offset, tmp);
177968baf11cSdamien
178068baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_GAIN_2GHZ);
178168baf11cSdamien tmp &= ~(0x3f << 18);
178268baf11cSdamien tmp |= (eep->rxTxMarginCh[0] & 0x3f) << 18;
178368baf11cSdamien if (IEEE80211_IS_CHAN_5GHZ(c)) {
178468baf11cSdamien tmp &= ~(0xf << 10);
178568baf11cSdamien tmp |= (eep->bswMargin[0] & 0xf) << 10;
178668baf11cSdamien }
1787803f2018Sdamien otus_write(sc, AR_PHY_GAIN_2GHZ, tmp);
178868baf11cSdamien
178968baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_GAIN_2GHZ + offset);
179068baf11cSdamien tmp &= ~(0x3f << 18);
179168baf11cSdamien tmp |= (eep->rxTxMarginCh[1] & 0x3f) << 18;
1792803f2018Sdamien otus_write(sc, AR_PHY_GAIN_2GHZ + offset, tmp);
179368baf11cSdamien
179468baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_TIMING_CTRL4);
179568baf11cSdamien tmp &= ~(0x3f << 5 | 0x1f);
179668baf11cSdamien tmp |= (eep->iqCalICh[0] & 0x3f) << 5 | (eep->iqCalQCh[0] & 0x1f);
1797803f2018Sdamien otus_write(sc, AR_PHY_TIMING_CTRL4, tmp);
179868baf11cSdamien
179968baf11cSdamien tmp = otus_phy_get_def(sc, AR_PHY_TIMING_CTRL4 + offset);
180068baf11cSdamien tmp &= ~(0x3f << 5 | 0x1f);
180168baf11cSdamien tmp |= (eep->iqCalICh[1] & 0x3f) << 5 | (eep->iqCalQCh[1] & 0x1f);
1802803f2018Sdamien otus_write(sc, AR_PHY_TIMING_CTRL4 + offset, tmp);
180368baf11cSdamien
1804803f2018Sdamien tmp = otus_phy_get_def(sc, AR_PHY_TPCRG1);
180568baf11cSdamien tmp &= ~(0xf << 16);
180668baf11cSdamien tmp |= (eep->xpd & 0xf) << 16;
1807803f2018Sdamien otus_write(sc, AR_PHY_TPCRG1, tmp);
180868baf11cSdamien
180968baf11cSdamien return otus_write_barrier(sc);
181068baf11cSdamien }
181168baf11cSdamien
181268baf11cSdamien int
otus_program_phy(struct otus_softc * sc,struct ieee80211_channel * c)181368baf11cSdamien otus_program_phy(struct otus_softc *sc, struct ieee80211_channel *c)
181468baf11cSdamien {
181568baf11cSdamien const uint32_t *vals;
181668baf11cSdamien int error, i;
181768baf11cSdamien
181868baf11cSdamien /* Select PHY programming based on band and bandwidth. */
181968baf11cSdamien if (IEEE80211_IS_CHAN_2GHZ(c))
182068baf11cSdamien vals = ar5416_phy_vals_2ghz_20mhz;
182168baf11cSdamien else
182268baf11cSdamien vals = ar5416_phy_vals_5ghz_20mhz;
182368baf11cSdamien for (i = 0; i < nitems(ar5416_phy_regs); i++)
1824803f2018Sdamien otus_write(sc, AR_PHY(ar5416_phy_regs[i]), vals[i]);
182568baf11cSdamien sc->phy_vals = vals;
182668baf11cSdamien
182768baf11cSdamien if (sc->eeprom.baseEepHeader.deviceType == 0x80) /* FEM */
182868baf11cSdamien if ((error = otus_set_board_values(sc, c)) != 0)
182968baf11cSdamien return error;
183068baf11cSdamien
183168baf11cSdamien /* Initial Tx power settings. */
1832803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE_MAX, 0x7f);
1833803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE1, 0x3f3f3f3f);
1834803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE2, 0x3f3f3f3f);
1835803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE3, 0x3f3f3f3f);
1836803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE4, 0x3f3f3f3f);
1837803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE5, 0x3f3f3f3f);
1838803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE6, 0x3f3f3f3f);
1839803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE7, 0x3f3f3f3f);
1840803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE8, 0x3f3f3f3f);
1841803f2018Sdamien otus_write(sc, AR_PHY_POWER_TX_RATE9, 0x3f3f3f3f);
184268baf11cSdamien
184368baf11cSdamien if (IEEE80211_IS_CHAN_2GHZ(c))
184468baf11cSdamien otus_write(sc, 0x1d4014, 0x5163);
184568baf11cSdamien else
184668baf11cSdamien otus_write(sc, 0x1d4014, 0x5143);
184768baf11cSdamien
184868baf11cSdamien return otus_write_barrier(sc);
184968baf11cSdamien }
185068baf11cSdamien
185168baf11cSdamien static __inline uint8_t
otus_reverse_bits(uint8_t v)185268baf11cSdamien otus_reverse_bits(uint8_t v)
185368baf11cSdamien {
185468baf11cSdamien v = ((v >> 1) & 0x55) | ((v & 0x55) << 1);
185568baf11cSdamien v = ((v >> 2) & 0x33) | ((v & 0x33) << 2);
185668baf11cSdamien v = ((v >> 4) & 0x0f) | ((v & 0x0f) << 4);
185768baf11cSdamien return v;
185868baf11cSdamien }
185968baf11cSdamien
18606e30e88dSdamien int
otus_set_rf_bank4(struct otus_softc * sc,struct ieee80211_channel * c)18616e30e88dSdamien otus_set_rf_bank4(struct otus_softc *sc, struct ieee80211_channel *c)
18626e30e88dSdamien {
18636e30e88dSdamien uint8_t chansel, d0, d1;
18646e30e88dSdamien uint16_t data;
18656e30e88dSdamien int error;
18666e30e88dSdamien
18676e30e88dSdamien d0 = 0;
18686e30e88dSdamien if (IEEE80211_IS_CHAN_5GHZ(c)) {
18696e30e88dSdamien chansel = (c->ic_freq - 4800) / 5;
18706e30e88dSdamien if (chansel & 1)
18716e30e88dSdamien d0 |= AR_BANK4_AMODE_REFSEL(2);
18726e30e88dSdamien else
18736e30e88dSdamien d0 |= AR_BANK4_AMODE_REFSEL(1);
18746e30e88dSdamien } else {
18756e30e88dSdamien d0 |= AR_BANK4_AMODE_REFSEL(2);
18766e30e88dSdamien if (c->ic_freq == 2484) { /* CH 14 */
18776e30e88dSdamien d0 |= AR_BANK4_BMODE_LF_SYNTH_FREQ;
18786e30e88dSdamien chansel = 10 + (c->ic_freq - 2274) / 5;
18796e30e88dSdamien } else
18806e30e88dSdamien chansel = 16 + (c->ic_freq - 2272) / 5;
18816e30e88dSdamien chansel <<= 2;
18826e30e88dSdamien }
18836e30e88dSdamien d0 |= AR_BANK4_ADDR(1) | AR_BANK4_CHUP;
18846e30e88dSdamien d1 = otus_reverse_bits(chansel);
18856e30e88dSdamien
18866e30e88dSdamien /* Write bits 0-4 of d0 and d1. */
18876e30e88dSdamien data = (d1 & 0x1f) << 5 | (d0 & 0x1f);
18886e30e88dSdamien otus_write(sc, AR_PHY(44), data);
18896e30e88dSdamien /* Write bits 5-7 of d0 and d1. */
18906e30e88dSdamien data = (d1 >> 5) << 5 | (d0 >> 5);
18916e30e88dSdamien otus_write(sc, AR_PHY(58), data);
18926e30e88dSdamien
18936e30e88dSdamien if ((error = otus_write_barrier(sc)) == 0)
18946e30e88dSdamien usbd_delay_ms(sc->sc_udev, 10);
18956e30e88dSdamien return error;
18966e30e88dSdamien }
18976e30e88dSdamien
189868baf11cSdamien void
otus_get_delta_slope(uint32_t coeff,uint32_t * exponent,uint32_t * mantissa)189968baf11cSdamien otus_get_delta_slope(uint32_t coeff, uint32_t *exponent, uint32_t *mantissa)
190068baf11cSdamien {
190168baf11cSdamien #define COEFF_SCALE_SHIFT 24
190268baf11cSdamien uint32_t exp, man;
190368baf11cSdamien
190468baf11cSdamien /* exponent = 14 - floor(log2(coeff)) */
190568baf11cSdamien for (exp = 31; exp > 0; exp--)
190668baf11cSdamien if (coeff & (1 << exp))
190768baf11cSdamien break;
190868baf11cSdamien KASSERT(exp != 0);
190968baf11cSdamien exp = 14 - (exp - COEFF_SCALE_SHIFT);
191068baf11cSdamien
191168baf11cSdamien /* mantissa = floor(coeff * 2^exponent + 0.5) */
191268baf11cSdamien man = coeff + (1 << (COEFF_SCALE_SHIFT - exp - 1));
191368baf11cSdamien
191468baf11cSdamien *mantissa = man >> (COEFF_SCALE_SHIFT - exp);
191568baf11cSdamien *exponent = exp - 16;
191668baf11cSdamien #undef COEFF_SCALE_SHIFT
191768baf11cSdamien }
191868baf11cSdamien
191968baf11cSdamien int
otus_set_chan(struct otus_softc * sc,struct ieee80211_channel * c,int assoc)19206e30e88dSdamien otus_set_chan(struct otus_softc *sc, struct ieee80211_channel *c, int assoc)
192168baf11cSdamien {
192268baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
192368baf11cSdamien struct ar_cmd_frequency cmd;
192468baf11cSdamien struct ar_rsp_frequency rsp;
192568baf11cSdamien const uint32_t *vals;
192668baf11cSdamien uint32_t coeff, exp, man, tmp;
19276e30e88dSdamien uint8_t code;
192868baf11cSdamien int error, chan, i;
192968baf11cSdamien
193068baf11cSdamien chan = ieee80211_chan2ieee(ic, c);
19316e30e88dSdamien DPRINTF(("setting channel %d (%dMHz)\n", chan, c->ic_freq));
193268baf11cSdamien
193368baf11cSdamien tmp = IEEE80211_IS_CHAN_2GHZ(c) ? 0x105 : 0x104;
193468baf11cSdamien otus_write(sc, AR_MAC_REG_DYNAMIC_SIFS_ACK, tmp);
19356e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0)
19366e30e88dSdamien return error;
193768baf11cSdamien
193888185ad4Sdamien /* Disable BB Heavy Clip. */
1939803f2018Sdamien otus_write(sc, AR_PHY_HEAVY_CLIP_ENABLE, 0x200);
19406e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0)
19416e30e88dSdamien return error;
194268baf11cSdamien
19436e30e88dSdamien /* XXX Is that FREQ_START ? */
194468baf11cSdamien error = otus_cmd(sc, AR_CMD_FREQ_STRAT, NULL, 0, NULL);
194568baf11cSdamien if (error != 0)
194668baf11cSdamien return error;
194768baf11cSdamien
194888185ad4Sdamien /* Reprogram PHY and RF on channel band or bandwidth changes. */
19496e30e88dSdamien if (sc->bb_reset || c->ic_flags != sc->sc_curchan->ic_flags) {
195068baf11cSdamien DPRINTF(("band switch\n"));
195168baf11cSdamien
19526e30e88dSdamien /* Cold/Warm reset BB/ADDA. */
19536e30e88dSdamien otus_write(sc, 0x1d4004, sc->bb_reset ? 0x800 : 0x400);
19546e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0)
19556e30e88dSdamien return error;
195668baf11cSdamien otus_write(sc, 0x1d4004, 0);
19576e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0)
19586e30e88dSdamien return error;
19596e30e88dSdamien sc->bb_reset = 0;
196068baf11cSdamien
196168baf11cSdamien if ((error = otus_program_phy(sc, c)) != 0) {
196268baf11cSdamien printf("%s: could not program PHY\n",
196368baf11cSdamien sc->sc_dev.dv_xname);
196468baf11cSdamien return error;
196568baf11cSdamien }
196668baf11cSdamien
196768baf11cSdamien /* Select RF programming based on band. */
196868baf11cSdamien if (IEEE80211_IS_CHAN_5GHZ(c))
196968baf11cSdamien vals = ar5416_banks_vals_5ghz;
197068baf11cSdamien else
197168baf11cSdamien vals = ar5416_banks_vals_2ghz;
1972803f2018Sdamien for (i = 0; i < nitems(ar5416_banks_regs); i++)
1973803f2018Sdamien otus_write(sc, AR_PHY(ar5416_banks_regs[i]), vals[i]);
19746e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0) {
19756e30e88dSdamien printf("%s: could not program RF\n",
19766e30e88dSdamien sc->sc_dev.dv_xname);
197768baf11cSdamien return error;
19786e30e88dSdamien }
197968baf11cSdamien code = AR_CMD_RF_INIT;
198068baf11cSdamien } else {
198168baf11cSdamien code = AR_CMD_FREQUENCY;
198268baf11cSdamien }
198368baf11cSdamien
19846e30e88dSdamien if ((error = otus_set_rf_bank4(sc, c)) != 0)
198568baf11cSdamien return error;
198668baf11cSdamien
1987803f2018Sdamien tmp = (sc->txmask == 0x5) ? 0x340 : 0x240;
1988803f2018Sdamien otus_write(sc, AR_PHY_TURBO, tmp);
19896e30e88dSdamien if ((error = otus_write_barrier(sc)) != 0)
19906e30e88dSdamien return error;
199168baf11cSdamien
199268baf11cSdamien /* Send firmware command to set channel. */
199368baf11cSdamien cmd.freq = htole32((uint32_t)c->ic_freq * 1000);
199468baf11cSdamien cmd.dynht2040 = htole32(0);
199568baf11cSdamien cmd.htena = htole32(1);
199668baf11cSdamien /* Set Delta Slope (exponent and mantissa). */
199768baf11cSdamien coeff = (100 << 24) / c->ic_freq;
199868baf11cSdamien otus_get_delta_slope(coeff, &exp, &man);
19996e30e88dSdamien cmd.dsc_exp = htole32(exp);
20006e30e88dSdamien cmd.dsc_man = htole32(man);
20016e30e88dSdamien DPRINTF(("ds coeff=%u exp=%u man=%u\n", coeff, exp, man));
20026e30e88dSdamien /* For Short GI, coeff is 9/10 that of normal coeff. */
200368baf11cSdamien coeff = (9 * coeff) / 10;
200468baf11cSdamien otus_get_delta_slope(coeff, &exp, &man);
20056e30e88dSdamien cmd.dsc_shgi_exp = htole32(exp);
20066e30e88dSdamien cmd.dsc_shgi_man = htole32(man);
20076e30e88dSdamien DPRINTF(("ds shgi coeff=%u exp=%u man=%u\n", coeff, exp, man));
20086e30e88dSdamien /* Set wait time for AGC and noise calibration (100 or 200ms). */
20096e30e88dSdamien cmd.check_loop_count = assoc ? htole32(2000) : htole32(1000);
20104351e075Sdamien DPRINTF(("%s\n", (code == AR_CMD_RF_INIT) ? "RF_INIT" : "FREQUENCY"));
20116e30e88dSdamien error = otus_cmd(sc, code, &cmd, sizeof cmd, &rsp);
201268baf11cSdamien if (error != 0)
201368baf11cSdamien return error;
20146e30e88dSdamien if ((rsp.status & htole32(AR_CAL_ERR_AGC | AR_CAL_ERR_NF_VAL)) != 0) {
201568baf11cSdamien DPRINTF(("status=0x%x\n", letoh32(rsp.status)));
20166e30e88dSdamien /* Force cold reset on next channel. */
20176e30e88dSdamien sc->bb_reset = 1;
20186e30e88dSdamien }
20196e30e88dSdamien #ifdef OTUS_DEBUG
20206e30e88dSdamien if (otus_debug) {
20216e30e88dSdamien printf("calibration status=0x%x\n", letoh32(rsp.status));
20226e30e88dSdamien for (i = 0; i < 2; i++) { /* 2 Rx chains */
20236e30e88dSdamien /* Sign-extend 9-bit NF values. */
20246e30e88dSdamien printf("noisefloor chain %d=%d\n", i,
20256e30e88dSdamien (((int32_t)letoh32(rsp.nf[i])) << 4) >> 23);
20266e30e88dSdamien printf("noisefloor ext chain %d=%d\n", i,
20276e30e88dSdamien ((int32_t)letoh32(rsp.nf_ext[i])) >> 23);
20286e30e88dSdamien }
20296e30e88dSdamien }
20306e30e88dSdamien #endif
203168baf11cSdamien sc->sc_curchan = c;
203268baf11cSdamien return 0;
203368baf11cSdamien }
203468baf11cSdamien
203568baf11cSdamien #ifdef notyet
203668baf11cSdamien int
otus_set_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)203768baf11cSdamien otus_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
203868baf11cSdamien struct ieee80211_key *k)
203968baf11cSdamien {
204068baf11cSdamien struct otus_softc *sc = ic->ic_softc;
204168baf11cSdamien struct otus_cmd_key cmd;
204268baf11cSdamien
20430760dad6Sdamien /* Defer setting of WEP keys until interface is brought up. */
20440760dad6Sdamien if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) !=
20450760dad6Sdamien (IFF_UP | IFF_RUNNING))
20460760dad6Sdamien return 0;
20470760dad6Sdamien
204868baf11cSdamien /* Do it in a process context. */
204968baf11cSdamien cmd.key = *k;
20504fac4e76Skrw cmd.ni = *ni;
205168baf11cSdamien otus_do_async(sc, otus_set_key_cb, &cmd, sizeof cmd);
20524fac4e76Skrw sc->sc_key_tasks++
20534fac4e76Skrw return EBUSY;
205468baf11cSdamien }
205568baf11cSdamien
205668baf11cSdamien void
otus_set_key_cb(struct otus_softc * sc,void * arg)205768baf11cSdamien otus_set_key_cb(struct otus_softc *sc, void *arg)
205868baf11cSdamien {
205968baf11cSdamien struct otus_cmd_key *cmd = arg;
206068baf11cSdamien struct ieee80211_key *k = &cmd->key;
206168baf11cSdamien struct ar_cmd_ekey key;
206268baf11cSdamien uint16_t cipher;
206368baf11cSdamien int error;
206468baf11cSdamien
20654fac4e76Skrw sc->sc_keys_tasks--;
20664fac4e76Skrw
206768baf11cSdamien memset(&key, 0, sizeof key);
206868baf11cSdamien if (k->k_flags & IEEE80211_KEY_GROUP) {
206968baf11cSdamien key.uid = htole16(k->k_id);
207068baf11cSdamien IEEE80211_ADDR_COPY(key.macaddr, sc->sc_ic.ic_myaddr);
20714351e075Sdamien key.macaddr[0] |= 0x80;
207268baf11cSdamien } else {
207368baf11cSdamien key.uid = htole16(OTUS_UID(cmd->associd));
207468baf11cSdamien IEEE80211_ADDR_COPY(key.macaddr, ni->ni_macaddr);
207568baf11cSdamien }
20764351e075Sdamien key.kix = htole16(0);
207768baf11cSdamien /* Map net80211 cipher to hardware. */
207868baf11cSdamien switch (k->k_cipher) {
207968baf11cSdamien case IEEE80211_CIPHER_WEP40:
208068baf11cSdamien cipher = AR_CIPHER_WEP64;
208168baf11cSdamien break;
208268baf11cSdamien case IEEE80211_CIPHER_WEP104:
208368baf11cSdamien cipher = AR_CIPHER_WEP128;
208468baf11cSdamien break;
208568baf11cSdamien case IEEE80211_CIPHER_TKIP:
208668baf11cSdamien cipher = AR_CIPHER_TKIP;
208768baf11cSdamien break;
208868baf11cSdamien case IEEE80211_CIPHER_CCMP:
208968baf11cSdamien cipher = AR_CIPHER_AES;
209068baf11cSdamien break;
209168baf11cSdamien default:
20924fac4e76Skrw IEEE80211_SEND_MGMT(ic, cmd->ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
20934fac4e76Skrw IEEE80211_REASON_AUTH_LEAVE);
20944fac4e76Skrw ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
209568baf11cSdamien return;
209668baf11cSdamien }
209768baf11cSdamien key.cipher = htole16(cipher);
209868baf11cSdamien memcpy(key.key, k->k_key, MIN(k->k_len, 16));
209968baf11cSdamien error = otus_cmd(sc, AR_CMD_EKEY, &key, sizeof key, NULL);
21004fac4e76Skrw if (error != 0 || k->k_cipher != IEEE80211_CIPHER_TKIP) {
21014fac4e76Skrw IEEE80211_SEND_MGMT(ic, cmd->ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
21024fac4e76Skrw IEEE80211_REASON_AUTH_LEAVE);
21034fac4e76Skrw ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
210468baf11cSdamien return;
21054fac4e76Skrw }
210668baf11cSdamien
210768baf11cSdamien /* TKIP: set Tx/Rx MIC Key. */
21084351e075Sdamien key.kix = htole16(1);
210968baf11cSdamien memcpy(key.key, k->k_key + 16, 16);
211068baf11cSdamien (void)otus_cmd(sc, AR_CMD_EKEY, &key, sizeof key, NULL);
21114fac4e76Skrw
21124fac4e76Skrw if (sc->sc_key_tasks == 0) {
21134fac4e76Skrw DPRINTF(("marking port %s valid\n",
21144fac4e76Skrw ether_sprintf(cmd->ni->ni_macaddr)));
21154fac4e76Skrw cmd->ni->ni_port_valid = 1;
21164fac4e76Skrw ieee80211_set_link_state(ic, LINK_STATE_UP);
21174fac4e76Skrw }
211868baf11cSdamien }
211968baf11cSdamien
212068baf11cSdamien void
otus_delete_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)212168baf11cSdamien otus_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
212268baf11cSdamien struct ieee80211_key *k)
212368baf11cSdamien {
212468baf11cSdamien struct otus_softc *sc = ic->ic_softc;
212568baf11cSdamien struct otus_cmd_key cmd;
212668baf11cSdamien
212768baf11cSdamien if (!(ic->ic_if.if_flags & IFF_RUNNING) ||
212868baf11cSdamien ic->ic_state != IEEE80211_S_RUN)
212968baf11cSdamien return; /* Nothing to do. */
213068baf11cSdamien
213168baf11cSdamien /* Do it in a process context. */
213268baf11cSdamien cmd.key = *k;
213368baf11cSdamien cmd.associd = (ni != NULL) ? ni->ni_associd : 0;
213468baf11cSdamien otus_do_async(sc, otus_delete_key_cb, &cmd, sizeof cmd);
213568baf11cSdamien }
213668baf11cSdamien
213768baf11cSdamien void
otus_delete_key_cb(struct otus_softc * sc,void * arg)213868baf11cSdamien otus_delete_key_cb(struct otus_softc *sc, void *arg)
213968baf11cSdamien {
214068baf11cSdamien struct otus_cmd_key *cmd = arg;
214168baf11cSdamien struct ieee80211_key *k = &cmd->key;
214268baf11cSdamien uint32_t uid;
214368baf11cSdamien
214468baf11cSdamien if (k->k_flags & IEEE80211_KEY_GROUP)
214568baf11cSdamien uid = htole32(k->k_id);
214668baf11cSdamien else
214768baf11cSdamien uid = htole32(OTUS_UID(cmd->associd));
214868baf11cSdamien (void)otus_cmd(sc, AR_CMD_DKEY, &uid, sizeof uid, NULL);
214968baf11cSdamien }
215068baf11cSdamien #endif
215168baf11cSdamien
215268baf11cSdamien void
otus_calibrate_to(void * arg)215368baf11cSdamien otus_calibrate_to(void *arg)
215468baf11cSdamien {
215568baf11cSdamien struct otus_softc *sc = arg;
215668baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
215768baf11cSdamien struct ieee80211_node *ni;
215868baf11cSdamien int s;
215968baf11cSdamien
21605ad57547Sjakemsr if (usbd_is_dying(sc->sc_udev))
21615ad57547Sjakemsr return;
21625ad57547Sjakemsr
21635ad57547Sjakemsr usbd_ref_incr(sc->sc_udev);
21645ad57547Sjakemsr
216568baf11cSdamien s = splnet();
216668baf11cSdamien ni = ic->ic_bss;
216768baf11cSdamien ieee80211_amrr_choose(&sc->amrr, ni, &((struct otus_node *)ni)->amn);
216868baf11cSdamien splx(s);
216968baf11cSdamien
21705ad57547Sjakemsr if (!usbd_is_dying(sc->sc_udev))
217168baf11cSdamien timeout_add_sec(&sc->calib_to, 1);
21725ad57547Sjakemsr
21735ad57547Sjakemsr usbd_ref_decr(sc->sc_udev);
217468baf11cSdamien }
217568baf11cSdamien
217668baf11cSdamien int
otus_set_bssid(struct otus_softc * sc,const uint8_t * bssid)217768baf11cSdamien otus_set_bssid(struct otus_softc *sc, const uint8_t *bssid)
217868baf11cSdamien {
21796e30e88dSdamien otus_write(sc, AR_MAC_REG_BSSID_L,
218068baf11cSdamien bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24);
21816e30e88dSdamien otus_write(sc, AR_MAC_REG_BSSID_H,
218268baf11cSdamien bssid[4] | bssid[5] << 8);
218368baf11cSdamien return otus_write_barrier(sc);
218468baf11cSdamien }
218568baf11cSdamien
218668baf11cSdamien int
otus_set_macaddr(struct otus_softc * sc,const uint8_t * addr)218768baf11cSdamien otus_set_macaddr(struct otus_softc *sc, const uint8_t *addr)
218868baf11cSdamien {
218968baf11cSdamien otus_write(sc, AR_MAC_REG_MAC_ADDR_L,
219068baf11cSdamien addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
219168baf11cSdamien otus_write(sc, AR_MAC_REG_MAC_ADDR_H,
219268baf11cSdamien addr[4] | addr[5] << 8);
219368baf11cSdamien return otus_write_barrier(sc);
219468baf11cSdamien }
219568baf11cSdamien
219668baf11cSdamien /* Default single-LED. */
219768baf11cSdamien void
otus_led_newstate_type1(struct otus_softc * sc)219868baf11cSdamien otus_led_newstate_type1(struct otus_softc *sc)
219968baf11cSdamien {
220068baf11cSdamien /* TBD */
220168baf11cSdamien }
220268baf11cSdamien
220368baf11cSdamien /* NETGEAR, dual-LED. */
220468baf11cSdamien void
otus_led_newstate_type2(struct otus_softc * sc)220568baf11cSdamien otus_led_newstate_type2(struct otus_softc *sc)
220668baf11cSdamien {
220768baf11cSdamien /* TBD */
220868baf11cSdamien }
220968baf11cSdamien
221068baf11cSdamien /* NETGEAR, single-LED/3 colors (blue, red, purple.) */
221168baf11cSdamien void
otus_led_newstate_type3(struct otus_softc * sc)221268baf11cSdamien otus_led_newstate_type3(struct otus_softc *sc)
221368baf11cSdamien {
221468baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
221568baf11cSdamien uint32_t state = sc->led_state;
221668baf11cSdamien
221768baf11cSdamien if (ic->ic_state == IEEE80211_S_INIT) {
221868baf11cSdamien state = 0; /* LED off. */
221968baf11cSdamien } else if (ic->ic_state == IEEE80211_S_RUN) {
222068baf11cSdamien /* Associated, LED always on. */
222168baf11cSdamien if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan))
222268baf11cSdamien state = AR_LED0_ON; /* 2GHz=>Red. */
222368baf11cSdamien else
222468baf11cSdamien state = AR_LED1_ON; /* 5GHz=>Blue. */
222568baf11cSdamien } else {
222668baf11cSdamien /* Scanning, blink LED. */
222768baf11cSdamien state ^= AR_LED0_ON | AR_LED1_ON;
222868baf11cSdamien if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan))
222968baf11cSdamien state &= ~AR_LED1_ON;
223068baf11cSdamien else
223168baf11cSdamien state &= ~AR_LED0_ON;
223268baf11cSdamien }
223368baf11cSdamien if (state != sc->led_state) {
223468baf11cSdamien otus_write(sc, 0x1d0104, state);
223568baf11cSdamien if (otus_write_barrier(sc) == 0)
223668baf11cSdamien sc->led_state = state;
223768baf11cSdamien }
223868baf11cSdamien }
223968baf11cSdamien
224068baf11cSdamien int
otus_init(struct ifnet * ifp)224168baf11cSdamien otus_init(struct ifnet *ifp)
224268baf11cSdamien {
224368baf11cSdamien struct otus_softc *sc = ifp->if_softc;
224468baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
224568baf11cSdamien int error;
224668baf11cSdamien
224768baf11cSdamien /* Init host command ring. */
224868baf11cSdamien sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0;
224968baf11cSdamien
225068baf11cSdamien if ((error = otus_init_mac(sc)) != 0) {
225168baf11cSdamien printf("%s: could not initialize MAC\n", sc->sc_dev.dv_xname);
225268baf11cSdamien return error;
225368baf11cSdamien }
225468baf11cSdamien
225568baf11cSdamien IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
225668baf11cSdamien (void)otus_set_macaddr(sc, ic->ic_myaddr);
225768baf11cSdamien
225868baf11cSdamien switch (ic->ic_opmode) {
225968baf11cSdamien #ifdef notyet
226068baf11cSdamien #ifndef IEEE80211_STA_ONLY
226168baf11cSdamien case IEEE80211_M_HOSTAP:
226268baf11cSdamien otus_write(sc, 0x1c3700, 0x0f0000a1);
226368baf11cSdamien otus_write(sc, 0x1c3c40, 0x1);
226468baf11cSdamien break;
226568baf11cSdamien case IEEE80211_M_IBSS:
226668baf11cSdamien otus_write(sc, 0x1c3700, 0x0f000000);
226768baf11cSdamien otus_write(sc, 0x1c3c40, 0x1);
226868baf11cSdamien break;
226968baf11cSdamien #endif
227068baf11cSdamien #endif
227168baf11cSdamien case IEEE80211_M_STA:
227268baf11cSdamien otus_write(sc, 0x1c3700, 0x0f000002);
227368baf11cSdamien otus_write(sc, 0x1c3c40, 0x1);
227468baf11cSdamien break;
227568baf11cSdamien default:
227668baf11cSdamien break;
227768baf11cSdamien }
227868baf11cSdamien otus_write(sc, AR_MAC_REG_SNIFFER,
227968baf11cSdamien (ic->ic_opmode == IEEE80211_M_MONITOR) ? 0x2000001 : 0x2000000);
228068baf11cSdamien (void)otus_write_barrier(sc);
228168baf11cSdamien
22826e30e88dSdamien sc->bb_reset = 1; /* Force cold reset. */
228368baf11cSdamien ic->ic_bss->ni_chan = ic->ic_ibss_chan;
22846e30e88dSdamien if ((error = otus_set_chan(sc, ic->ic_ibss_chan, 0)) != 0) {
228568baf11cSdamien printf("%s: could not set channel\n", sc->sc_dev.dv_xname);
228668baf11cSdamien return error;
228768baf11cSdamien }
228868baf11cSdamien
228968baf11cSdamien /* Start Rx. */
22901de0d41aSstsp otus_write(sc, AR_MAC_REG_DMA_TRIGGER, AR_DMA_TRIGGER_RXQ);
2291d7d7f774Sdamien (void)otus_write_barrier(sc);
229268baf11cSdamien
229368baf11cSdamien ifp->if_flags |= IFF_RUNNING;
2294de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd);
229568baf11cSdamien
229668baf11cSdamien if (ic->ic_opmode == IEEE80211_M_MONITOR)
229768baf11cSdamien ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
229868baf11cSdamien else
229968baf11cSdamien ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
230068baf11cSdamien
230168baf11cSdamien return 0;
230268baf11cSdamien }
230368baf11cSdamien
230468baf11cSdamien void
otus_stop(struct ifnet * ifp)230568baf11cSdamien otus_stop(struct ifnet *ifp)
230668baf11cSdamien {
230768baf11cSdamien struct otus_softc *sc = ifp->if_softc;
230868baf11cSdamien struct ieee80211com *ic = &sc->sc_ic;
230968baf11cSdamien int s;
231068baf11cSdamien
231168baf11cSdamien sc->sc_tx_timer = 0;
231268baf11cSdamien ifp->if_timer = 0;
2313de6cd8fbSdlg ifp->if_flags &= ~IFF_RUNNING;
2314de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd);
231568baf11cSdamien
231668baf11cSdamien timeout_del(&sc->scan_to);
231768baf11cSdamien timeout_del(&sc->calib_to);
231868baf11cSdamien
231968baf11cSdamien s = splusb();
232068baf11cSdamien ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
232168baf11cSdamien /* Wait for all queued asynchronous commands to complete. */
23225ad57547Sjakemsr usb_wait_task(sc->sc_udev, &sc->sc_task);
232368baf11cSdamien splx(s);
232468baf11cSdamien
232568baf11cSdamien /* Stop Rx. */
23261de0d41aSstsp otus_write(sc, AR_MAC_REG_DMA_TRIGGER, 0);
232768baf11cSdamien (void)otus_write_barrier(sc);
232868baf11cSdamien
232968baf11cSdamien sc->tx_queued = 0;
233068baf11cSdamien }
2331