xref: /openbsd/sys/dev/usb/if_otus.c (revision 81508fe3)
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