xref: /openbsd/sys/dev/usb/if_otus.c (revision c33449aa)
1*c33449aaSjakemsr /*	$OpenBSD: if_otus.c,v 1.19 2010/10/23 15:42:09 jakemsr 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 
1968baf11cSdamien /*-
2068baf11cSdamien  * Driver for Atheros AR9001U chipset.
2168baf11cSdamien  * http://www.atheros.com/pt/bulletins/AR9001USBBulletin.pdf
2268baf11cSdamien  */
2368baf11cSdamien 
2468baf11cSdamien #include "bpfilter.h"
2568baf11cSdamien 
2668baf11cSdamien #include <sys/param.h>
2768baf11cSdamien #include <sys/sockio.h>
2868baf11cSdamien #include <sys/mbuf.h>
2968baf11cSdamien #include <sys/kernel.h>
3068baf11cSdamien #include <sys/socket.h>
3168baf11cSdamien #include <sys/systm.h>
3268baf11cSdamien #include <sys/timeout.h>
3368baf11cSdamien #include <sys/conf.h>
3468baf11cSdamien #include <sys/device.h>
3568baf11cSdamien 
3668baf11cSdamien #include <machine/bus.h>
3768baf11cSdamien #include <machine/endian.h>
3868baf11cSdamien #include <machine/intr.h>
3968baf11cSdamien 
4068baf11cSdamien #if NBPFILTER > 0
4168baf11cSdamien #include <net/bpf.h>
4268baf11cSdamien #endif
4368baf11cSdamien #include <net/if.h>
4468baf11cSdamien #include <net/if_arp.h>
4568baf11cSdamien #include <net/if_dl.h>
4668baf11cSdamien #include <net/if_media.h>
4768baf11cSdamien #include <net/if_types.h>
4868baf11cSdamien 
4968baf11cSdamien #include <netinet/in.h>
5068baf11cSdamien #include <netinet/in_systm.h>
5168baf11cSdamien #include <netinet/in_var.h>
5268baf11cSdamien #include <netinet/if_ether.h>
5368baf11cSdamien #include <netinet/ip.h>
5468baf11cSdamien 
5568baf11cSdamien #include <net80211/ieee80211_var.h>
5668baf11cSdamien #include <net80211/ieee80211_amrr.h>
5768baf11cSdamien #include <net80211/ieee80211_radiotap.h>
5868baf11cSdamien 
5968baf11cSdamien #include <dev/usb/usb.h>
6068baf11cSdamien #include <dev/usb/usbdi.h>
6168baf11cSdamien #include <dev/usb/usbdi_util.h>
6268baf11cSdamien #include <dev/usb/usbdevs.h>
6368baf11cSdamien 
6468baf11cSdamien #include <dev/usb/if_otusreg.h>
6568baf11cSdamien 
6668baf11cSdamien #ifdef USB_DEBUG
6768baf11cSdamien #define OTUS_DEBUG
6868baf11cSdamien #endif
6968baf11cSdamien 
7068baf11cSdamien #ifdef OTUS_DEBUG
7168baf11cSdamien #define DPRINTF(x)	do { if (otus_debug) printf x; } while (0)
7268baf11cSdamien #define DPRINTFN(n, x)	do { if (otus_debug >= (n)) printf x; } while (0)
7368baf11cSdamien int otus_debug = 1;
7468baf11cSdamien #else
7568baf11cSdamien #define DPRINTF(x)
7668baf11cSdamien #define DPRINTFN(n, x)
7768baf11cSdamien #endif
7868baf11cSdamien 
7968baf11cSdamien static const struct usb_devno otus_devs[] = {
8068baf11cSdamien 	{ USB_VENDOR_ACCTON,		USB_PRODUCT_ACCTON_WN7512 },
8108c50b53Sdamien 	{ USB_VENDOR_ATHEROS2,		USB_PRODUCT_ATHEROS2_3CRUSBN275 },
8268baf11cSdamien 	{ USB_VENDOR_ATHEROS2,		USB_PRODUCT_ATHEROS2_TG121N },
8368baf11cSdamien 	{ USB_VENDOR_ATHEROS2,		USB_PRODUCT_ATHEROS2_AR9170 },
8408c50b53Sdamien 	{ USB_VENDOR_ATHEROS2,		USB_PRODUCT_ATHEROS2_WN612 },
85a145d460Sdamien 	{ USB_VENDOR_ATHEROS2,		USB_PRODUCT_ATHEROS2_WN821NV2 },
86bd3ca424Sdamien 	{ USB_VENDOR_AVM,		USB_PRODUCT_AVM_FRITZWLAN },
8706c5b759Sdamien 	{ USB_VENDOR_CACE,		USB_PRODUCT_CACE_AIRPCAPNX },
889ca817faSjsg 	{ USB_VENDOR_DLINK2,		USB_PRODUCT_DLINK2_DWA130D1 },
8943333fa6Sdamien 	{ USB_VENDOR_DLINK2,		USB_PRODUCT_DLINK2_DWA160A1 },
9043333fa6Sdamien 	{ USB_VENDOR_DLINK2,		USB_PRODUCT_DLINK2_DWA160A2 },
9168baf11cSdamien 	{ USB_VENDOR_IODATA,		USB_PRODUCT_IODATA_WNGDNUS2 },
92a5c5b7a3Sdamien 	{ USB_VENDOR_NEC,		USB_PRODUCT_NEC_WL300NUG },
9368baf11cSdamien 	{ USB_VENDOR_NETGEAR,		USB_PRODUCT_NETGEAR_WN111V2 },
9408c50b53Sdamien 	{ USB_VENDOR_NETGEAR,		USB_PRODUCT_NETGEAR_WNA1000 },
9568baf11cSdamien 	{ USB_VENDOR_NETGEAR,		USB_PRODUCT_NETGEAR_WNDA3100 },
9668baf11cSdamien 	{ USB_VENDOR_PLANEX2,		USB_PRODUCT_PLANEX2_GW_US300 },
9708c50b53Sdamien 	{ USB_VENDOR_WISTRONNEWEB,	USB_PRODUCT_WISTRONNEWEB_O8494 },
9808c50b53Sdamien 	{ USB_VENDOR_WISTRONNEWEB,	USB_PRODUCT_WISTRONNEWEB_WNC0600 },
9968baf11cSdamien 	{ USB_VENDOR_ZCOM,		USB_PRODUCT_ZCOM_UB81 },
10068baf11cSdamien 	{ USB_VENDOR_ZCOM,		USB_PRODUCT_ZCOM_UB82 },
101bd3ca424Sdamien 	{ USB_VENDOR_ZYDAS,		USB_PRODUCT_ZYDAS_ZD1221 },
102bd3ca424Sdamien 	{ USB_VENDOR_ZYXEL,		USB_PRODUCT_ZYXEL_NWD271N }
10368baf11cSdamien };
10468baf11cSdamien 
10568baf11cSdamien int		otus_match(struct device *, void *, void *);
10668baf11cSdamien void		otus_attach(struct device *, struct device *, void *);
10768baf11cSdamien int		otus_detach(struct device *, int);
10868baf11cSdamien void		otus_attachhook(void *);
10968baf11cSdamien void		otus_get_chanlist(struct otus_softc *);
110d7d7f774Sdamien int		otus_load_firmware(struct otus_softc *, const char *,
111d7d7f774Sdamien 		    uint32_t);
112d7d7f774Sdamien int		otus_open_pipes(struct otus_softc *);
113d7d7f774Sdamien void		otus_close_pipes(struct otus_softc *);
11468baf11cSdamien int		otus_alloc_tx_cmd(struct otus_softc *);
11568baf11cSdamien void		otus_free_tx_cmd(struct otus_softc *);
11668baf11cSdamien int		otus_alloc_tx_data_list(struct otus_softc *);
11768baf11cSdamien void		otus_free_tx_data_list(struct otus_softc *);
11868baf11cSdamien int		otus_alloc_rx_data_list(struct otus_softc *);
11968baf11cSdamien void		otus_free_rx_data_list(struct otus_softc *);
12068baf11cSdamien void		otus_next_scan(void *);
12168baf11cSdamien void		otus_task(void *);
12268baf11cSdamien void		otus_do_async(struct otus_softc *,
12368baf11cSdamien 		    void (*)(struct otus_softc *, void *), void *, int);
12468baf11cSdamien int		otus_newstate(struct ieee80211com *, enum ieee80211_state,
12568baf11cSdamien 		    int);
12668baf11cSdamien void		otus_newstate_cb(struct otus_softc *, void *);
127d7d7f774Sdamien int		otus_cmd(struct otus_softc *, uint8_t, const void *, int,
128d7d7f774Sdamien 		    void *);
129d7d7f774Sdamien void		otus_write(struct otus_softc *, uint32_t, uint32_t);
130d7d7f774Sdamien int		otus_write_barrier(struct otus_softc *);
131d7d7f774Sdamien struct		ieee80211_node *otus_node_alloc(struct ieee80211com *);
132d7d7f774Sdamien int		otus_media_change(struct ifnet *);
13368baf11cSdamien int		otus_read_eeprom(struct otus_softc *);
13468baf11cSdamien void		otus_newassoc(struct ieee80211com *, struct ieee80211_node *,
13568baf11cSdamien 		    int);
136d7d7f774Sdamien void		otus_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
13768baf11cSdamien void		otus_cmd_rxeof(struct otus_softc *, uint8_t *, int);
13868baf11cSdamien void		otus_sub_rxeof(struct otus_softc *, uint8_t *, int);
13968baf11cSdamien void		otus_rxeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
14068baf11cSdamien void		otus_txeof(usbd_xfer_handle, usbd_private_handle, usbd_status);
14168baf11cSdamien int		otus_tx(struct otus_softc *, struct mbuf *,
14268baf11cSdamien 		    struct ieee80211_node *);
14368baf11cSdamien void		otus_start(struct ifnet *);
14468baf11cSdamien void		otus_watchdog(struct ifnet *);
14568baf11cSdamien int		otus_ioctl(struct ifnet *, u_long, caddr_t);
14668baf11cSdamien int		otus_set_multi(struct otus_softc *);
14768baf11cSdamien void		otus_updateedca(struct ieee80211com *);
14868baf11cSdamien void		otus_updateedca_cb(struct otus_softc *, void *);
14968baf11cSdamien void		otus_updateslot(struct ieee80211com *);
15068baf11cSdamien void		otus_updateslot_cb(struct otus_softc *, void *);
15168baf11cSdamien int		otus_init_mac(struct otus_softc *);
15268baf11cSdamien uint32_t	otus_phy_get_def(struct otus_softc *, uint32_t);
15368baf11cSdamien int		otus_set_board_values(struct otus_softc *,
15468baf11cSdamien 		    struct ieee80211_channel *);
15568baf11cSdamien int		otus_program_phy(struct otus_softc *,
15668baf11cSdamien 		    struct ieee80211_channel *);
1576e30e88dSdamien int		otus_set_rf_bank4(struct otus_softc *,
1586e30e88dSdamien 		    struct ieee80211_channel *);
15968baf11cSdamien void		otus_get_delta_slope(uint32_t, uint32_t *, uint32_t *);
1606e30e88dSdamien int		otus_set_chan(struct otus_softc *, struct ieee80211_channel *,
1616e30e88dSdamien 		    int);
16268baf11cSdamien int		otus_set_key(struct ieee80211com *, struct ieee80211_node *,
16368baf11cSdamien 		    struct ieee80211_key *);
16468baf11cSdamien void		otus_set_key_cb(struct otus_softc *, void *);
16568baf11cSdamien void		otus_delete_key(struct ieee80211com *, struct ieee80211_node *,
16668baf11cSdamien 		    struct ieee80211_key *);
16768baf11cSdamien void		otus_delete_key_cb(struct otus_softc *, void *);
16868baf11cSdamien void		otus_calibrate_to(void *);
16968baf11cSdamien int		otus_set_bssid(struct otus_softc *, const uint8_t *);
17068baf11cSdamien int		otus_set_macaddr(struct otus_softc *, const uint8_t *);
17168baf11cSdamien void		otus_led_newstate_type1(struct otus_softc *);
17268baf11cSdamien void		otus_led_newstate_type2(struct otus_softc *);
17368baf11cSdamien void		otus_led_newstate_type3(struct otus_softc *);
17468baf11cSdamien int		otus_init(struct ifnet *);
17568baf11cSdamien void		otus_stop(struct ifnet *);
17668baf11cSdamien 
17768baf11cSdamien struct cfdriver otus_cd = {
17868baf11cSdamien 	NULL, "otus", DV_IFNET
17968baf11cSdamien };
18068baf11cSdamien 
18168baf11cSdamien const struct cfattach otus_ca = {
18268baf11cSdamien 	sizeof (struct otus_softc), otus_match, otus_attach, otus_detach
18368baf11cSdamien };
18468baf11cSdamien 
18568baf11cSdamien int
18668baf11cSdamien otus_match(struct device *parent, void *match, void *aux)
18768baf11cSdamien {
18868baf11cSdamien 	struct usb_attach_arg *uaa = aux;
18968baf11cSdamien 
19068baf11cSdamien 	if (uaa->iface != NULL)
19168baf11cSdamien 		return UMATCH_NONE;
19268baf11cSdamien 
19368baf11cSdamien 	return (usb_lookup(otus_devs, uaa->vendor, uaa->product) != NULL) ?
19468baf11cSdamien 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
19568baf11cSdamien }
19668baf11cSdamien 
19768baf11cSdamien void
19868baf11cSdamien otus_attach(struct device *parent, struct device *self, void *aux)
19968baf11cSdamien {
20068baf11cSdamien 	struct otus_softc *sc = (struct otus_softc *)self;
20168baf11cSdamien 	struct usb_attach_arg *uaa = aux;
20268baf11cSdamien 	int error;
20368baf11cSdamien 
20468baf11cSdamien 	sc->sc_udev = uaa->device;
20568baf11cSdamien 
206*c33449aaSjakemsr 	usb_init_task(&sc->sc_task, otus_task, sc, USB_TASK_TYPE_GENERIC);
20768baf11cSdamien 	timeout_set(&sc->scan_to, otus_next_scan, sc);
20868baf11cSdamien 	timeout_set(&sc->calib_to, otus_calibrate_to, sc);
20968baf11cSdamien 
21068baf11cSdamien 	sc->amrr.amrr_min_success_threshold =  1;
21168baf11cSdamien 	sc->amrr.amrr_max_success_threshold = 10;
21268baf11cSdamien 
21368baf11cSdamien 	if (usbd_set_config_no(sc->sc_udev, 1, 0) != 0) {
21468baf11cSdamien 		printf("%s: could not set configuration no\n",
21568baf11cSdamien 		    sc->sc_dev.dv_xname);
21668baf11cSdamien 		return;
21768baf11cSdamien 	}
21868baf11cSdamien 
21968baf11cSdamien 	/* Get the first interface handle. */
22068baf11cSdamien 	error = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface);
22168baf11cSdamien 	if (error != 0) {
22268baf11cSdamien 		printf("%s: could not get interface handle\n",
22368baf11cSdamien 		    sc->sc_dev.dv_xname);
22468baf11cSdamien 		return;
22568baf11cSdamien 	}
22668baf11cSdamien 
22768baf11cSdamien 	if ((error = otus_open_pipes(sc)) != 0) {
22868baf11cSdamien 		printf("%s: could not open pipes\n", sc->sc_dev.dv_xname);
22968baf11cSdamien 		return;
23068baf11cSdamien 	}
23168baf11cSdamien 
23268baf11cSdamien 	if (rootvp == NULL)
23368baf11cSdamien 		mountroothook_establish(otus_attachhook, sc);
23468baf11cSdamien 	else
23568baf11cSdamien 		otus_attachhook(sc);
23668baf11cSdamien 
23768baf11cSdamien 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, &sc->sc_dev);
23868baf11cSdamien }
23968baf11cSdamien 
24068baf11cSdamien int
24168baf11cSdamien otus_detach(struct device *self, int flags)
24268baf11cSdamien {
24368baf11cSdamien 	struct otus_softc *sc = (struct otus_softc *)self;
24468baf11cSdamien 	struct ifnet *ifp = &sc->sc_ic.ic_if;
24568baf11cSdamien 	int s;
24668baf11cSdamien 
24768baf11cSdamien 	s = splnet();
24868baf11cSdamien 
24968baf11cSdamien 	/* Wait for all queued asynchronous commands to complete. */
25068baf11cSdamien 	while (sc->cmdq.queued > 0)
25168baf11cSdamien 		tsleep(&sc->cmdq, 0, "cmdq", 0);
25268baf11cSdamien 
25368baf11cSdamien 	timeout_del(&sc->scan_to);
25468baf11cSdamien 	timeout_del(&sc->calib_to);
25568baf11cSdamien 
25688185ad4Sdamien 	if (ifp->if_flags != 0) {	/* if_attach() has been called. */
25768baf11cSdamien 		ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
25868baf11cSdamien 		ieee80211_ifdetach(ifp);
25968baf11cSdamien 		if_detach(ifp);
26068baf11cSdamien 	}
26168baf11cSdamien 
26268baf11cSdamien 	otus_close_pipes(sc);
26368baf11cSdamien 
26468baf11cSdamien 	splx(s);
26568baf11cSdamien 
26668baf11cSdamien 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, &sc->sc_dev);
26768baf11cSdamien 
26868baf11cSdamien 	return 0;
26968baf11cSdamien }
27068baf11cSdamien 
27168baf11cSdamien void
27268baf11cSdamien otus_attachhook(void *xsc)
27368baf11cSdamien {
27468baf11cSdamien 	struct otus_softc *sc = xsc;
27568baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
27668baf11cSdamien 	struct ifnet *ifp = &ic->ic_if;
27768baf11cSdamien 	usb_device_request_t req;
27868baf11cSdamien 	uint32_t in, out;
27968baf11cSdamien 	int error;
28068baf11cSdamien 
28168baf11cSdamien 	error = otus_load_firmware(sc, "otus-init", AR_FW_INIT_ADDR);
28268baf11cSdamien 	if (error != 0) {
28368baf11cSdamien 		printf("%s: could not load %s firmware\n",
28468baf11cSdamien 		    sc->sc_dev.dv_xname, "init");
28568baf11cSdamien 		return;
28668baf11cSdamien 	}
28768baf11cSdamien 
28868baf11cSdamien 	usbd_delay_ms(sc->sc_udev, 1000);
28968baf11cSdamien 
29068baf11cSdamien 	error = otus_load_firmware(sc, "otus-main", AR_FW_MAIN_ADDR);
29168baf11cSdamien 	if (error != 0) {
29268baf11cSdamien 		printf("%s: could not load %s firmware\n",
29368baf11cSdamien 		    sc->sc_dev.dv_xname, "main");
29468baf11cSdamien 		return;
29568baf11cSdamien 	}
29668baf11cSdamien 
29768baf11cSdamien 	/* Tell device that firmware transfer is complete. */
29868baf11cSdamien 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
29968baf11cSdamien 	req.bRequest = AR_FW_DOWNLOAD_COMPLETE;
30068baf11cSdamien 	USETW(req.wValue, 0);
30168baf11cSdamien 	USETW(req.wIndex, 0);
30268baf11cSdamien 	USETW(req.wLength, 0);
30368baf11cSdamien 	if (usbd_do_request(sc->sc_udev, &req, NULL) != 0) {
30468baf11cSdamien 		printf("%s: firmware initialization failed\n",
30568baf11cSdamien 		    sc->sc_dev.dv_xname);
30668baf11cSdamien 		return;
30768baf11cSdamien 	}
30868baf11cSdamien 
30968baf11cSdamien 	/* Send an ECHO command to check that everything is settled. */
31068baf11cSdamien 	in = 0xbadc0ffe;
31168baf11cSdamien 	if (otus_cmd(sc, AR_CMD_ECHO, &in, sizeof in, &out) != 0) {
31268baf11cSdamien 		printf("%s: echo command failed\n", sc->sc_dev.dv_xname);
31368baf11cSdamien 		return;
31468baf11cSdamien 	}
31568baf11cSdamien 	if (in != out) {
316d7d7f774Sdamien 		printf("%s: echo reply mismatch: 0x%08x!=0x%08x\n",
317d7d7f774Sdamien 		    sc->sc_dev.dv_xname, in, out);
31868baf11cSdamien 		return;
31968baf11cSdamien 	}
32068baf11cSdamien 
32168baf11cSdamien 	/* Read entire EEPROM. */
32268baf11cSdamien 	if (otus_read_eeprom(sc) != 0) {
32368baf11cSdamien 		printf("%s: could not read EEPROM\n", sc->sc_dev.dv_xname);
32468baf11cSdamien 		return;
32568baf11cSdamien 	}
32668baf11cSdamien 
327803f2018Sdamien 	sc->txmask = sc->eeprom.baseEepHeader.txMask;
328803f2018Sdamien 	sc->rxmask = sc->eeprom.baseEepHeader.rxMask;
3296e30e88dSdamien 	sc->capflags = sc->eeprom.baseEepHeader.opCapFlags;
33068baf11cSdamien 	IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->eeprom.baseEepHeader.macAddr);
331803f2018Sdamien 	sc->sc_led_newstate = otus_led_newstate_type3;	/* XXX */
33268baf11cSdamien 
3336e30e88dSdamien 	printf("%s: MAC/BBP AR9170, RF AR%X, MIMO %dT%dR, address %s\n",
3346e30e88dSdamien 	    sc->sc_dev.dv_xname, (sc->capflags & AR5416_OPFLAGS_11A) ?
3356e30e88dSdamien 	        0x9104 : ((sc->txmask == 0x5) ? 0x9102 : 0x9101),
3366e30e88dSdamien 	    (sc->txmask == 0x5) ? 2 : 1, (sc->rxmask == 0x5) ? 2 : 1,
33768baf11cSdamien 	    ether_sprintf(ic->ic_myaddr));
33868baf11cSdamien 
33968baf11cSdamien 	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
34068baf11cSdamien 	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
34168baf11cSdamien 	ic->ic_state = IEEE80211_S_INIT;
34268baf11cSdamien 
34368baf11cSdamien 	/* Set device capabilities. */
34468baf11cSdamien 	ic->ic_caps =
34568baf11cSdamien 	    IEEE80211_C_MONITOR |	/* monitor mode supported */
34668baf11cSdamien 	    IEEE80211_C_SHPREAMBLE |	/* short preamble supported */
34768baf11cSdamien 	    IEEE80211_C_SHSLOT |	/* short slot time supported */
34868baf11cSdamien 	    IEEE80211_C_WEP |		/* WEP */
34968baf11cSdamien 	    IEEE80211_C_RSN;		/* WPA/RSN */
35068baf11cSdamien 
3514351e075Sdamien 	if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) {
35268baf11cSdamien 		/* Set supported .11b and .11g rates. */
3534351e075Sdamien 		ic->ic_sup_rates[IEEE80211_MODE_11B] =
3544351e075Sdamien 		    ieee80211_std_rateset_11b;
3554351e075Sdamien 		ic->ic_sup_rates[IEEE80211_MODE_11G] =
3564351e075Sdamien 		    ieee80211_std_rateset_11g;
3574351e075Sdamien 	}
3584351e075Sdamien 	if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) {
3594351e075Sdamien 		/* Set supported .11a rates. */
3604351e075Sdamien 		ic->ic_sup_rates[IEEE80211_MODE_11A] =
3614351e075Sdamien 		    ieee80211_std_rateset_11a;
3624351e075Sdamien 	}
36368baf11cSdamien 
36468baf11cSdamien 	/* Build the list of supported channels. */
36568baf11cSdamien 	otus_get_chanlist(sc);
36668baf11cSdamien 
36768baf11cSdamien 	ifp->if_softc = sc;
36868baf11cSdamien 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
36968baf11cSdamien 	ifp->if_ioctl = otus_ioctl;
37068baf11cSdamien 	ifp->if_start = otus_start;
37168baf11cSdamien 	ifp->if_watchdog = otus_watchdog;
37268baf11cSdamien 	IFQ_SET_READY(&ifp->if_snd);
37368baf11cSdamien 	memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
37468baf11cSdamien 
37568baf11cSdamien 	if_attach(ifp);
37668baf11cSdamien 	ieee80211_ifattach(ifp);
37768baf11cSdamien 	ic->ic_node_alloc = otus_node_alloc;
37868baf11cSdamien 	ic->ic_newassoc = otus_newassoc;
37968baf11cSdamien 	ic->ic_updateslot = otus_updateslot;
38068baf11cSdamien 	ic->ic_updateedca = otus_updateedca;
38168baf11cSdamien #ifdef notyet
38268baf11cSdamien 	ic->ic_set_key = otus_set_key;
38368baf11cSdamien 	ic->ic_delete_key = otus_delete_key;
38468baf11cSdamien #endif
38568baf11cSdamien 	/* Override state transition machine. */
38668baf11cSdamien 	sc->sc_newstate = ic->ic_newstate;
38768baf11cSdamien 	ic->ic_newstate = otus_newstate;
38868baf11cSdamien 	ieee80211_media_init(ifp, otus_media_change, ieee80211_media_status);
38968baf11cSdamien 
39068baf11cSdamien #if NBPFILTER > 0
39168baf11cSdamien 	bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
39268baf11cSdamien 	    sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN);
39368baf11cSdamien 
39468baf11cSdamien 	sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
39568baf11cSdamien 	sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
39668baf11cSdamien 	sc->sc_rxtap.wr_ihdr.it_present = htole32(OTUS_RX_RADIOTAP_PRESENT);
39768baf11cSdamien 
39868baf11cSdamien 	sc->sc_txtap_len = sizeof sc->sc_txtapu;
39968baf11cSdamien 	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
40068baf11cSdamien 	sc->sc_txtap.wt_ihdr.it_present = htole32(OTUS_TX_RADIOTAP_PRESENT);
40168baf11cSdamien #endif
40268baf11cSdamien }
40368baf11cSdamien 
40468baf11cSdamien void
40568baf11cSdamien otus_get_chanlist(struct otus_softc *sc)
40668baf11cSdamien {
40768baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
40868baf11cSdamien 	uint16_t domain;
40968baf11cSdamien 	uint8_t chan;
41068baf11cSdamien 	int i;
41168baf11cSdamien 
41268baf11cSdamien 	/* XXX regulatory domain. */
41368baf11cSdamien 	domain = letoh16(sc->eeprom.baseEepHeader.regDmn[0]);
41468baf11cSdamien 	DPRINTF(("regdomain=0x%04x\n", domain));
41568baf11cSdamien 
41668baf11cSdamien 	if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) {
41768baf11cSdamien 		for (i = 0; i < 14; i++) {
41868baf11cSdamien 			chan = ar_chans[i];
41968baf11cSdamien 			ic->ic_channels[chan].ic_freq =
42068baf11cSdamien 			    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
42168baf11cSdamien 			ic->ic_channels[chan].ic_flags =
42268baf11cSdamien 			    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
42368baf11cSdamien 			    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
42468baf11cSdamien 		}
42568baf11cSdamien 	}
42668baf11cSdamien 	if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) {
42768baf11cSdamien 		for (i = 14; i < nitems(ar_chans); i++) {
42868baf11cSdamien 			chan = ar_chans[i];
42968baf11cSdamien 			ic->ic_channels[chan].ic_freq =
43068baf11cSdamien 			    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
43168baf11cSdamien 			ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A;
43268baf11cSdamien 		}
43368baf11cSdamien 	}
43468baf11cSdamien }
43568baf11cSdamien 
43668baf11cSdamien int
43768baf11cSdamien otus_load_firmware(struct otus_softc *sc, const char *name, uint32_t addr)
43868baf11cSdamien {
43968baf11cSdamien 	usb_device_request_t req;
44068baf11cSdamien 	size_t size;
44168baf11cSdamien 	u_char *fw, *ptr;
44268baf11cSdamien 	int mlen, error;
44368baf11cSdamien 
44468baf11cSdamien 	/* Read firmware image from the filesystem. */
44568baf11cSdamien 	if ((error = loadfirmware(name, &fw, &size)) != 0) {
44668baf11cSdamien 		printf("%s: failed loadfirmware of file %s (error %d)\n",
44768baf11cSdamien 		    sc->sc_dev.dv_xname, name, error);
44868baf11cSdamien 		return error;
44968baf11cSdamien 	}
45068baf11cSdamien 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
45168baf11cSdamien 	req.bRequest = AR_FW_DOWNLOAD;
45268baf11cSdamien 	USETW(req.wIndex, 0);
45368baf11cSdamien 
45468baf11cSdamien 	ptr = fw;
45568baf11cSdamien 	addr >>= 8;
45668baf11cSdamien 	while (size > 0) {
45768baf11cSdamien 		mlen = MIN(size, 4096);
45868baf11cSdamien 
45968baf11cSdamien 		USETW(req.wValue, addr);
46068baf11cSdamien 		USETW(req.wLength, mlen);
46168baf11cSdamien 		if (usbd_do_request(sc->sc_udev, &req, ptr) != 0) {
46268baf11cSdamien 			error = EIO;
46368baf11cSdamien 			break;
46468baf11cSdamien 		}
46568baf11cSdamien 		addr += mlen >> 8;
46668baf11cSdamien 		ptr  += mlen;
46768baf11cSdamien 		size -= mlen;
46868baf11cSdamien 	}
46968baf11cSdamien 	free(fw, M_DEVBUF);
47068baf11cSdamien 	return error;
47168baf11cSdamien }
47268baf11cSdamien 
47368baf11cSdamien int
47468baf11cSdamien otus_open_pipes(struct otus_softc *sc)
47568baf11cSdamien {
47668baf11cSdamien 	usb_endpoint_descriptor_t *ed;
47768baf11cSdamien 	int i, isize, error;
47868baf11cSdamien 
47968baf11cSdamien 	error = usbd_open_pipe(sc->sc_iface, AR_EPT_BULK_RX_NO, 0,
48068baf11cSdamien 	    &sc->data_rx_pipe);
48168baf11cSdamien 	if (error != 0) {
48268baf11cSdamien 		printf("%s: could not open Rx bulk pipe\n",
48368baf11cSdamien 		    sc->sc_dev.dv_xname);
48468baf11cSdamien 		goto fail;
48568baf11cSdamien 	}
48668baf11cSdamien 
48768baf11cSdamien 	ed = usbd_get_endpoint_descriptor(sc->sc_iface, AR_EPT_INTR_RX_NO);
48868baf11cSdamien 	if (ed == NULL) {
48968baf11cSdamien 		printf("%s: could not retrieve Rx intr pipe descriptor\n",
49068baf11cSdamien 		    sc->sc_dev.dv_xname);
49168baf11cSdamien 		goto fail;
49268baf11cSdamien 	}
49368baf11cSdamien 	isize = UGETW(ed->wMaxPacketSize);
49468baf11cSdamien 	if (isize == 0) {
49568baf11cSdamien 		printf("%s: invalid Rx intr pipe descriptor\n",
49668baf11cSdamien 		    sc->sc_dev.dv_xname);
49768baf11cSdamien 		goto fail;
49868baf11cSdamien 	}
49968baf11cSdamien 	sc->ibuf = malloc(isize, M_USBDEV, M_NOWAIT);
50068baf11cSdamien 	if (sc->ibuf == NULL) {
50168baf11cSdamien 		printf("%s: could not allocate Rx intr buffer\n",
50268baf11cSdamien 		    sc->sc_dev.dv_xname);
50368baf11cSdamien 		goto fail;
50468baf11cSdamien 	}
50568baf11cSdamien 	error = usbd_open_pipe_intr(sc->sc_iface, AR_EPT_INTR_RX_NO,
50668baf11cSdamien 	    USBD_SHORT_XFER_OK, &sc->cmd_rx_pipe, sc, sc->ibuf, isize,
50768baf11cSdamien 	    otus_intr, USBD_DEFAULT_INTERVAL);
50868baf11cSdamien 	if (error != 0) {
50968baf11cSdamien 		printf("%s: could not open Rx intr pipe\n",
51068baf11cSdamien 		    sc->sc_dev.dv_xname);
51168baf11cSdamien 		goto fail;
51268baf11cSdamien 	}
51368baf11cSdamien 
51468baf11cSdamien 	error = usbd_open_pipe(sc->sc_iface, AR_EPT_BULK_TX_NO, 0,
51568baf11cSdamien 	    &sc->data_tx_pipe);
51668baf11cSdamien 	if (error != 0) {
51768baf11cSdamien 		printf("%s: could not open Tx bulk pipe\n",
51868baf11cSdamien 		    sc->sc_dev.dv_xname);
51968baf11cSdamien 		goto fail;
52068baf11cSdamien 	}
52168baf11cSdamien 
52268baf11cSdamien 	error = usbd_open_pipe(sc->sc_iface, AR_EPT_INTR_TX_NO, 0,
52368baf11cSdamien 	    &sc->cmd_tx_pipe);
52468baf11cSdamien 	if (error != 0) {
52568baf11cSdamien 		printf("%s: could not open Tx intr pipe\n",
52668baf11cSdamien 		    sc->sc_dev.dv_xname);
52768baf11cSdamien 		goto fail;
52868baf11cSdamien 	}
52968baf11cSdamien 
53068baf11cSdamien 	if (otus_alloc_tx_cmd(sc) != 0) {
53168baf11cSdamien 		printf("%s: could not allocate command xfer\n",
53268baf11cSdamien 		    sc->sc_dev.dv_xname);
53368baf11cSdamien 		goto fail;
53468baf11cSdamien 	}
53568baf11cSdamien 
53668baf11cSdamien 	if (otus_alloc_tx_data_list(sc) != 0) {
53768baf11cSdamien 		printf("%s: could not allocate Tx xfers\n",
53868baf11cSdamien 		    sc->sc_dev.dv_xname);
53968baf11cSdamien 		goto fail;
54068baf11cSdamien 	}
54168baf11cSdamien 
54268baf11cSdamien 	if (otus_alloc_rx_data_list(sc) != 0) {
54368baf11cSdamien 		printf("%s: could not allocate Rx xfers\n",
54468baf11cSdamien 		    sc->sc_dev.dv_xname);
54568baf11cSdamien 		goto fail;
54668baf11cSdamien 	}
54768baf11cSdamien 
54868baf11cSdamien 	for (i = 0; i < OTUS_RX_DATA_LIST_COUNT; i++) {
54968baf11cSdamien 		struct otus_rx_data *data = &sc->rx_data[i];
55068baf11cSdamien 
55168baf11cSdamien 		usbd_setup_xfer(data->xfer, sc->data_rx_pipe, data, data->buf,
55268baf11cSdamien 		    OTUS_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
55368baf11cSdamien 		    USBD_NO_TIMEOUT, otus_rxeof);
55468baf11cSdamien 		error = usbd_transfer(data->xfer);
55568baf11cSdamien 		if (error != USBD_IN_PROGRESS && error != 0) {
55668baf11cSdamien 			printf("%s: could not queue Rx xfer\n",
55768baf11cSdamien 			    sc->sc_dev.dv_xname);
55868baf11cSdamien 			goto fail;
55968baf11cSdamien 		}
56068baf11cSdamien 	}
56168baf11cSdamien 	return 0;
56268baf11cSdamien 
56368baf11cSdamien  fail:	otus_close_pipes(sc);
56468baf11cSdamien 	return error;
56568baf11cSdamien }
56668baf11cSdamien 
56768baf11cSdamien void
56868baf11cSdamien otus_close_pipes(struct otus_softc *sc)
56968baf11cSdamien {
5704351e075Sdamien 	otus_free_tx_cmd(sc);
5714351e075Sdamien 	otus_free_tx_data_list(sc);
5724351e075Sdamien 	otus_free_rx_data_list(sc);
5734351e075Sdamien 
57468baf11cSdamien 	if (sc->data_rx_pipe != NULL)
57568baf11cSdamien 		usbd_close_pipe(sc->data_rx_pipe);
5764351e075Sdamien 	if (sc->cmd_rx_pipe != NULL) {
5774351e075Sdamien 		usbd_abort_pipe(sc->cmd_rx_pipe);
57868baf11cSdamien 		usbd_close_pipe(sc->cmd_rx_pipe);
5794351e075Sdamien 	}
58068baf11cSdamien 	if (sc->ibuf != NULL)
58168baf11cSdamien 		free(sc->ibuf, M_USBDEV);
58268baf11cSdamien 	if (sc->data_tx_pipe != NULL)
58368baf11cSdamien 		usbd_close_pipe(sc->data_tx_pipe);
58468baf11cSdamien 	if (sc->cmd_tx_pipe != NULL)
58568baf11cSdamien 		usbd_close_pipe(sc->cmd_tx_pipe);
58668baf11cSdamien }
58768baf11cSdamien 
58868baf11cSdamien int
58968baf11cSdamien otus_alloc_tx_cmd(struct otus_softc *sc)
59068baf11cSdamien {
59168baf11cSdamien 	struct otus_tx_cmd *cmd = &sc->tx_cmd;
59268baf11cSdamien 
59368baf11cSdamien 	cmd->xfer = usbd_alloc_xfer(sc->sc_udev);
59468baf11cSdamien 	if (cmd->xfer == NULL) {
59568baf11cSdamien 		printf("%s: could not allocate xfer\n",
59668baf11cSdamien 		    sc->sc_dev.dv_xname);
59768baf11cSdamien 		return ENOMEM;
59868baf11cSdamien 	}
59968baf11cSdamien 	cmd->buf = usbd_alloc_buffer(cmd->xfer, OTUS_MAX_TXCMDSZ);
60068baf11cSdamien 	if (cmd->buf == NULL) {
60168baf11cSdamien 		printf("%s: could not allocate xfer buffer\n",
60268baf11cSdamien 		    sc->sc_dev.dv_xname);
60368baf11cSdamien 		usbd_free_xfer(cmd->xfer);
60468baf11cSdamien 		return ENOMEM;
60568baf11cSdamien 	}
60668baf11cSdamien 	return 0;
60768baf11cSdamien }
60868baf11cSdamien 
60968baf11cSdamien void
61068baf11cSdamien otus_free_tx_cmd(struct otus_softc *sc)
61168baf11cSdamien {
6124351e075Sdamien 	/* Make sure no transfers are pending. */
61368baf11cSdamien 	usbd_abort_pipe(sc->cmd_tx_pipe);
61468baf11cSdamien 
61568baf11cSdamien 	if (sc->tx_cmd.xfer != NULL)
61668baf11cSdamien 		usbd_free_xfer(sc->tx_cmd.xfer);
61768baf11cSdamien }
61868baf11cSdamien 
61968baf11cSdamien int
62068baf11cSdamien otus_alloc_tx_data_list(struct otus_softc *sc)
62168baf11cSdamien {
62268baf11cSdamien 	struct otus_tx_data *data;
62368baf11cSdamien 	int i, error;
62468baf11cSdamien 
62568baf11cSdamien 	for (i = 0; i < OTUS_TX_DATA_LIST_COUNT; i++) {
62668baf11cSdamien 		data = &sc->tx_data[i];
62768baf11cSdamien 
62868baf11cSdamien 		data->sc = sc;  /* Backpointer for callbacks. */
62968baf11cSdamien 
63068baf11cSdamien 		data->xfer = usbd_alloc_xfer(sc->sc_udev);
63168baf11cSdamien 		if (data->xfer == NULL) {
63268baf11cSdamien 			printf("%s: could not allocate xfer\n",
63368baf11cSdamien 			    sc->sc_dev.dv_xname);
63468baf11cSdamien 			error = ENOMEM;
63568baf11cSdamien 			goto fail;
63668baf11cSdamien 		}
63768baf11cSdamien 		data->buf = usbd_alloc_buffer(data->xfer, OTUS_TXBUFSZ);
63868baf11cSdamien 		if (data->buf == NULL) {
63968baf11cSdamien 			printf("%s: could not allocate xfer buffer\n",
64068baf11cSdamien 			    sc->sc_dev.dv_xname);
64168baf11cSdamien 			error = ENOMEM;
64268baf11cSdamien 			goto fail;
64368baf11cSdamien 		}
64468baf11cSdamien 	}
64568baf11cSdamien 	return 0;
64668baf11cSdamien 
64768baf11cSdamien fail:	otus_free_tx_data_list(sc);
64868baf11cSdamien 	return error;
64968baf11cSdamien }
65068baf11cSdamien 
65168baf11cSdamien void
65268baf11cSdamien otus_free_tx_data_list(struct otus_softc *sc)
65368baf11cSdamien {
65468baf11cSdamien 	int i;
65568baf11cSdamien 
6564351e075Sdamien 	/* Make sure no transfers are pending. */
65768baf11cSdamien 	usbd_abort_pipe(sc->data_tx_pipe);
65868baf11cSdamien 
65968baf11cSdamien 	for (i = 0; i < OTUS_TX_DATA_LIST_COUNT; i++)
66068baf11cSdamien 		if (sc->tx_data[i].xfer != NULL)
66168baf11cSdamien 			usbd_free_xfer(sc->tx_data[i].xfer);
66268baf11cSdamien }
66368baf11cSdamien 
66468baf11cSdamien int
66568baf11cSdamien otus_alloc_rx_data_list(struct otus_softc *sc)
66668baf11cSdamien {
66768baf11cSdamien 	struct otus_rx_data *data;
66868baf11cSdamien 	int i, error;
66968baf11cSdamien 
67068baf11cSdamien 	for (i = 0; i < OTUS_RX_DATA_LIST_COUNT; i++) {
67168baf11cSdamien 		data = &sc->rx_data[i];
67268baf11cSdamien 
67368baf11cSdamien 		data->sc = sc;	/* Backpointer for callbacks. */
67468baf11cSdamien 
67568baf11cSdamien 		data->xfer = usbd_alloc_xfer(sc->sc_udev);
67668baf11cSdamien 		if (data->xfer == NULL) {
67768baf11cSdamien 			printf("%s: could not allocate xfer\n",
67868baf11cSdamien 			    sc->sc_dev.dv_xname);
67968baf11cSdamien 			error = ENOMEM;
68068baf11cSdamien 			goto fail;
68168baf11cSdamien 		}
68268baf11cSdamien 		data->buf = usbd_alloc_buffer(data->xfer, OTUS_RXBUFSZ);
68368baf11cSdamien 		if (data->buf == NULL) {
68468baf11cSdamien 			printf("%s: could not allocate xfer buffer\n",
68568baf11cSdamien 			    sc->sc_dev.dv_xname);
68668baf11cSdamien 			error = ENOMEM;
68768baf11cSdamien 			goto fail;
68868baf11cSdamien 		}
68968baf11cSdamien 	}
69068baf11cSdamien 	return 0;
69168baf11cSdamien 
69268baf11cSdamien fail:	otus_free_rx_data_list(sc);
69368baf11cSdamien 	return error;
69468baf11cSdamien }
69568baf11cSdamien 
69668baf11cSdamien void
69768baf11cSdamien otus_free_rx_data_list(struct otus_softc *sc)
69868baf11cSdamien {
69968baf11cSdamien 	int i;
70068baf11cSdamien 
70168baf11cSdamien 	/* Make sure no transfers are pending. */
70268baf11cSdamien 	usbd_abort_pipe(sc->data_rx_pipe);
70368baf11cSdamien 
70468baf11cSdamien 	for (i = 0; i < OTUS_RX_DATA_LIST_COUNT; i++)
70568baf11cSdamien 		if (sc->rx_data[i].xfer != NULL)
70668baf11cSdamien 			usbd_free_xfer(sc->rx_data[i].xfer);
70768baf11cSdamien }
70868baf11cSdamien 
70968baf11cSdamien void
71068baf11cSdamien otus_next_scan(void *arg)
71168baf11cSdamien {
71268baf11cSdamien 	struct otus_softc *sc = arg;
71368baf11cSdamien 
71468baf11cSdamien 	if (sc->sc_ic.ic_state == IEEE80211_S_SCAN)
71568baf11cSdamien 		ieee80211_next_scan(&sc->sc_ic.ic_if);
71668baf11cSdamien }
71768baf11cSdamien 
71868baf11cSdamien void
71968baf11cSdamien otus_task(void *arg)
72068baf11cSdamien {
72168baf11cSdamien 	struct otus_softc *sc = arg;
72268baf11cSdamien 	struct otus_host_cmd_ring *ring = &sc->cmdq;
72368baf11cSdamien 	struct otus_host_cmd *cmd;
72468baf11cSdamien 	int s;
72568baf11cSdamien 
72668baf11cSdamien 	/* Process host commands. */
72768baf11cSdamien 	s = splusb();
72868baf11cSdamien 	while (ring->next != ring->cur) {
72968baf11cSdamien 		cmd = &ring->cmd[ring->next];
73068baf11cSdamien 		splx(s);
73168baf11cSdamien 		/* Callback. */
73268baf11cSdamien 		cmd->cb(sc, cmd->data);
73368baf11cSdamien 		s = splusb();
73468baf11cSdamien 		ring->queued--;
73568baf11cSdamien 		ring->next = (ring->next + 1) % OTUS_HOST_CMD_RING_COUNT;
73668baf11cSdamien 	}
73768baf11cSdamien 	wakeup(ring);
73868baf11cSdamien 	splx(s);
73968baf11cSdamien }
74068baf11cSdamien 
74168baf11cSdamien void
74268baf11cSdamien otus_do_async(struct otus_softc *sc, void (*cb)(struct otus_softc *, void *),
74368baf11cSdamien     void *arg, int len)
74468baf11cSdamien {
74568baf11cSdamien 	struct otus_host_cmd_ring *ring = &sc->cmdq;
74668baf11cSdamien 	struct otus_host_cmd *cmd;
74768baf11cSdamien 	int s;
74868baf11cSdamien 
74968baf11cSdamien 	s = splusb();
75068baf11cSdamien 	cmd = &ring->cmd[ring->cur];
75168baf11cSdamien 	cmd->cb = cb;
75268baf11cSdamien 	KASSERT(len <= sizeof (cmd->data));
75368baf11cSdamien 	memcpy(cmd->data, arg, len);
75468baf11cSdamien 	ring->cur = (ring->cur + 1) % OTUS_HOST_CMD_RING_COUNT;
75568baf11cSdamien 
75668baf11cSdamien 	/* If there is no pending command already, schedule a task. */
75768baf11cSdamien 	if (++ring->queued == 1)
75868baf11cSdamien 		usb_add_task(sc->sc_udev, &sc->sc_task);
75968baf11cSdamien 	splx(s);
76068baf11cSdamien }
76168baf11cSdamien 
76268baf11cSdamien int
76368baf11cSdamien otus_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
76468baf11cSdamien {
76568baf11cSdamien 	struct otus_softc *sc = ic->ic_softc;
76668baf11cSdamien 	struct otus_cmd_newstate cmd;
76768baf11cSdamien 
76868baf11cSdamien 	/* Do it in a process context. */
76968baf11cSdamien 	cmd.state = nstate;
77068baf11cSdamien 	cmd.arg = arg;
77168baf11cSdamien 	otus_do_async(sc, otus_newstate_cb, &cmd, sizeof cmd);
77268baf11cSdamien 	return 0;
77368baf11cSdamien }
77468baf11cSdamien 
77568baf11cSdamien void
77668baf11cSdamien otus_newstate_cb(struct otus_softc *sc, void *arg)
77768baf11cSdamien {
77868baf11cSdamien 	struct otus_cmd_newstate *cmd = arg;
77968baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
78068baf11cSdamien 	struct ieee80211_node *ni;
78168baf11cSdamien 	int s;
78268baf11cSdamien 
78368baf11cSdamien 	s = splnet();
78468baf11cSdamien 
78568baf11cSdamien 	switch (cmd->state) {
78668baf11cSdamien 	case IEEE80211_S_INIT:
78768baf11cSdamien 		break;
78868baf11cSdamien 
78968baf11cSdamien 	case IEEE80211_S_SCAN:
7906e30e88dSdamien 		(void)otus_set_chan(sc, ic->ic_bss->ni_chan, 0);
79168baf11cSdamien 		timeout_add_msec(&sc->scan_to, 200);
79268baf11cSdamien 		break;
79368baf11cSdamien 
79468baf11cSdamien 	case IEEE80211_S_AUTH:
79568baf11cSdamien 	case IEEE80211_S_ASSOC:
7966e30e88dSdamien 		(void)otus_set_chan(sc, ic->ic_bss->ni_chan, 0);
79768baf11cSdamien 		break;
79868baf11cSdamien 
79968baf11cSdamien 	case IEEE80211_S_RUN:
8006e30e88dSdamien 		(void)otus_set_chan(sc, ic->ic_bss->ni_chan, 1);
80168baf11cSdamien 
80268baf11cSdamien 		ni = ic->ic_bss;
80368baf11cSdamien 
80468baf11cSdamien 		if (ic->ic_opmode == IEEE80211_M_STA) {
80568baf11cSdamien 			otus_updateslot(ic);
80668baf11cSdamien 			otus_set_bssid(sc, ni->ni_bssid);
80768baf11cSdamien 
80868baf11cSdamien 			/* Fake a join to init the Tx rate. */
80968baf11cSdamien 			otus_newassoc(ic, ni, 1);
81068baf11cSdamien 
81168baf11cSdamien 			/* Start calibration timer. */
81268baf11cSdamien 			timeout_add_sec(&sc->calib_to, 1);
81368baf11cSdamien 		}
81468baf11cSdamien 		break;
81568baf11cSdamien 	}
81668baf11cSdamien 
81768baf11cSdamien 	sc->sc_led_newstate(sc);
81868baf11cSdamien 	(void)sc->sc_newstate(ic, cmd->state, cmd->arg);
81968baf11cSdamien 
82068baf11cSdamien 	splx(s);
82168baf11cSdamien }
82268baf11cSdamien 
82368baf11cSdamien int
82468baf11cSdamien otus_cmd(struct otus_softc *sc, uint8_t code, const void *idata, int ilen,
82568baf11cSdamien     void *odata)
82668baf11cSdamien {
82768baf11cSdamien 	struct otus_tx_cmd *cmd = &sc->tx_cmd;
82868baf11cSdamien 	struct ar_cmd_hdr *hdr;
82968baf11cSdamien 	int s, xferlen, error;
83068baf11cSdamien 
83168baf11cSdamien 	/* Always bulk-out a multiple of 4 bytes. */
83268baf11cSdamien 	xferlen = (sizeof (*hdr) + ilen + 3) & ~3;
83368baf11cSdamien 
83468baf11cSdamien 	hdr = (struct ar_cmd_hdr *)cmd->buf;
83568baf11cSdamien 	hdr->code  = code;
83668baf11cSdamien 	hdr->len   = ilen;
83768baf11cSdamien 	hdr->token = ++cmd->token;	/* Don't care about endianness. */
83868baf11cSdamien 	memcpy((uint8_t *)&hdr[1], idata, ilen);
83968baf11cSdamien 
84068baf11cSdamien 	DPRINTFN(2, ("sending command code=0x%02x len=%d token=%d\n",
84168baf11cSdamien 	    code, ilen, hdr->token));
84268baf11cSdamien 
84368baf11cSdamien 	s = splusb();
84468baf11cSdamien 	cmd->odata = odata;
84568baf11cSdamien 	cmd->done = 0;
84668baf11cSdamien 
84768baf11cSdamien 	usbd_setup_xfer(cmd->xfer, sc->cmd_tx_pipe, cmd, cmd->buf, xferlen,
84868baf11cSdamien 	    USBD_FORCE_SHORT_XFER | USBD_NO_COPY, OTUS_CMD_TIMEOUT, NULL);
84968baf11cSdamien 	error = usbd_sync_transfer(cmd->xfer);
85068baf11cSdamien 	if (error != 0) {
85168baf11cSdamien 		splx(s);
85268baf11cSdamien 		printf("%s: could not send command 0x%x (error=%s)\n",
85368baf11cSdamien 		    sc->sc_dev.dv_xname, code, usbd_errstr(error));
85468baf11cSdamien 		return EIO;
85568baf11cSdamien 	}
85668baf11cSdamien 	if (!cmd->done)
85768baf11cSdamien 		error = tsleep(cmd, PCATCH, "otuscmd", hz);
85868baf11cSdamien 	cmd->odata = NULL;	/* In case answer is received too late. */
85968baf11cSdamien 	splx(s);
86068baf11cSdamien 	if (error != 0) {
86168baf11cSdamien 		printf("%s: timeout waiting for command 0x%02x reply\n",
86268baf11cSdamien 		    sc->sc_dev.dv_xname, code);
86368baf11cSdamien 	}
86468baf11cSdamien 	return error;
86568baf11cSdamien }
86668baf11cSdamien 
86768baf11cSdamien void
86868baf11cSdamien otus_write(struct otus_softc *sc, uint32_t reg, uint32_t val)
86968baf11cSdamien {
87068baf11cSdamien 	sc->write_buf[sc->write_idx].reg = htole32(reg);
87168baf11cSdamien 	sc->write_buf[sc->write_idx].val = htole32(val);
87268baf11cSdamien 
87368baf11cSdamien 	if (++sc->write_idx > AR_MAX_WRITE_IDX)
87468baf11cSdamien 		(void)otus_write_barrier(sc);
87568baf11cSdamien }
87668baf11cSdamien 
87768baf11cSdamien int
87868baf11cSdamien otus_write_barrier(struct otus_softc *sc)
87968baf11cSdamien {
88068baf11cSdamien 	int error;
88168baf11cSdamien 
88268baf11cSdamien 	if (sc->write_idx == 0)
88368baf11cSdamien 		return 0;	/* Nothing to flush. */
88468baf11cSdamien 
88568baf11cSdamien 	error = otus_cmd(sc, AR_CMD_WREG, sc->write_buf,
88668baf11cSdamien 	    sizeof (sc->write_buf[0]) * sc->write_idx, NULL);
88768baf11cSdamien 	sc->write_idx = 0;
88868baf11cSdamien 	return error;
88968baf11cSdamien }
89068baf11cSdamien 
89168baf11cSdamien struct ieee80211_node *
89268baf11cSdamien otus_node_alloc(struct ieee80211com *ic)
89368baf11cSdamien {
89468baf11cSdamien 	return malloc(sizeof (struct otus_node), M_DEVBUF, M_NOWAIT | M_ZERO);
89568baf11cSdamien }
89668baf11cSdamien 
89768baf11cSdamien int
89868baf11cSdamien otus_media_change(struct ifnet *ifp)
89968baf11cSdamien {
90068baf11cSdamien 	struct otus_softc *sc = ifp->if_softc;
90168baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
90268baf11cSdamien 	uint8_t rate, ridx;
90368baf11cSdamien 	int error;
90468baf11cSdamien 
90568baf11cSdamien 	error = ieee80211_media_change(ifp);
90668baf11cSdamien 	if (error != ENETRESET)
90768baf11cSdamien 		return error;
90868baf11cSdamien 
90968baf11cSdamien 	if (ic->ic_fixed_rate != -1) {
91068baf11cSdamien 		rate = ic->ic_sup_rates[ic->ic_curmode].
91168baf11cSdamien 		    rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
91268baf11cSdamien 		for (ridx = 0; ridx <= OTUS_RIDX_MAX; ridx++)
91368baf11cSdamien 			if (otus_rates[ridx].rate == rate)
91468baf11cSdamien 				break;
91568baf11cSdamien 		sc->fixed_ridx = ridx;
91668baf11cSdamien 	}
91768baf11cSdamien 
91868baf11cSdamien 	if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
91968baf11cSdamien 		error = otus_init(ifp);
92068baf11cSdamien 
92168baf11cSdamien 	return error;
92268baf11cSdamien }
92368baf11cSdamien 
92468baf11cSdamien int
92568baf11cSdamien otus_read_eeprom(struct otus_softc *sc)
92668baf11cSdamien {
92768baf11cSdamien 	uint32_t regs[8], reg;
92868baf11cSdamien 	uint8_t *eep;
92968baf11cSdamien 	int i, j, error;
93068baf11cSdamien 
93168baf11cSdamien 	/* Read EEPROM by blocks of 32 bytes. */
93268baf11cSdamien 	eep = (uint8_t *)&sc->eeprom;
933803f2018Sdamien 	reg = AR_EEPROM_OFFSET;
93468baf11cSdamien 	for (i = 0; i < sizeof (sc->eeprom) / 32; i++) {
93568baf11cSdamien 		for (j = 0; j < 8; j++, reg += 4)
93668baf11cSdamien 			regs[j] = htole32(reg);
93768baf11cSdamien 		error = otus_cmd(sc, AR_CMD_RREG, regs, sizeof regs, eep);
93868baf11cSdamien 		if (error != 0)
93968baf11cSdamien 			break;
94068baf11cSdamien 		eep += 32;
94168baf11cSdamien 	}
94268baf11cSdamien 	return error;
94368baf11cSdamien }
94468baf11cSdamien 
94568baf11cSdamien void
94668baf11cSdamien otus_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew)
94768baf11cSdamien {
94868baf11cSdamien 	struct otus_softc *sc = ic->ic_softc;
94968baf11cSdamien 	struct otus_node *on = (void *)ni;
95068baf11cSdamien 	struct ieee80211_rateset *rs = &ni->ni_rates;
95168baf11cSdamien 	uint8_t rate;
95268baf11cSdamien 	int ridx, i;
95368baf11cSdamien 
95468baf11cSdamien 	DPRINTF(("new assoc isnew=%d addr=%s\n",
95568baf11cSdamien 	    isnew, ether_sprintf(ni->ni_macaddr)));
95668baf11cSdamien 
95768baf11cSdamien 	ieee80211_amrr_node_init(&sc->amrr, &on->amn);
95868baf11cSdamien 	/* Start at lowest available bit-rate, AMRR will raise. */
95968baf11cSdamien 	ni->ni_txrate = 0;
96068baf11cSdamien 
96168baf11cSdamien 	for (i = 0; i < rs->rs_nrates; i++) {
96268baf11cSdamien 		rate = rs->rs_rates[i] & IEEE80211_RATE_VAL;
96368baf11cSdamien 		/* Convert 802.11 rate to hardware rate index. */
96468baf11cSdamien 		for (ridx = 0; ridx <= OTUS_RIDX_MAX; ridx++)
96568baf11cSdamien 			if (otus_rates[ridx].rate == rate)
96668baf11cSdamien 				break;
96768baf11cSdamien 		on->ridx[i] = ridx;
96868baf11cSdamien 		DPRINTF(("rate=0x%02x ridx=%d\n",
96968baf11cSdamien 		    rs->rs_rates[i], on->ridx[i]));
97068baf11cSdamien 	}
97168baf11cSdamien }
97268baf11cSdamien 
973d7d7f774Sdamien /* ARGSUSED */
974d7d7f774Sdamien void
975d7d7f774Sdamien otus_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
976d7d7f774Sdamien {
977d7d7f774Sdamien #if 0
978d7d7f774Sdamien 	struct otus_softc *sc = priv;
979d7d7f774Sdamien 	int len;
980d7d7f774Sdamien 
981d7d7f774Sdamien 	/*
982d7d7f774Sdamien 	 * The Rx intr pipe is unused with current firmware.  Notifications
983d7d7f774Sdamien 	 * and replies to commands are sent through the Rx bulk pipe instead
984d7d7f774Sdamien 	 * (with a magic PLCP header.)
985d7d7f774Sdamien 	 */
986d7d7f774Sdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
987d7d7f774Sdamien 		DPRINTF(("intr status=%d\n", status));
988d7d7f774Sdamien 		if (status == USBD_STALLED)
989d7d7f774Sdamien 			usbd_clear_endpoint_stall_async(sc->cmd_rx_pipe);
990d7d7f774Sdamien 		return;
991d7d7f774Sdamien 	}
992d7d7f774Sdamien 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
993d7d7f774Sdamien 
994d7d7f774Sdamien 	otus_cmd_rxeof(sc, sc->ibuf, len);
995d7d7f774Sdamien #endif
996d7d7f774Sdamien }
997d7d7f774Sdamien 
99868baf11cSdamien void
99968baf11cSdamien otus_cmd_rxeof(struct otus_softc *sc, uint8_t *buf, int len)
100068baf11cSdamien {
100168baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
100268baf11cSdamien 	struct otus_tx_cmd *cmd;
100368baf11cSdamien 	struct ar_cmd_hdr *hdr;
100468baf11cSdamien 	int s;
100568baf11cSdamien 
100668baf11cSdamien 	if (__predict_false(len < sizeof (*hdr))) {
100768baf11cSdamien 		DPRINTF(("cmd too small %d\n", len));
100868baf11cSdamien 		return;
100968baf11cSdamien 	}
101068baf11cSdamien 	hdr = (struct ar_cmd_hdr *)buf;
101168baf11cSdamien 	if (__predict_false(sizeof (*hdr) + hdr->len > len ||
101268baf11cSdamien 	    sizeof (*hdr) + hdr->len > 64)) {
101368baf11cSdamien 		DPRINTF(("cmd too large %d\n", hdr->len));
101468baf11cSdamien 		return;
101568baf11cSdamien 	}
101668baf11cSdamien 
101768baf11cSdamien 	if ((hdr->code & 0xc0) != 0xc0) {
101868baf11cSdamien 		DPRINTFN(2, ("received reply code=0x%02x len=%d token=%d\n",
101968baf11cSdamien 		    hdr->code, hdr->len, hdr->token));
102068baf11cSdamien 		cmd = &sc->tx_cmd;
102168baf11cSdamien 		if (__predict_false(hdr->token != cmd->token))
102268baf11cSdamien 			return;
102368baf11cSdamien 		/* Copy answer into caller's supplied buffer. */
102468baf11cSdamien 		if (cmd->odata != NULL)
102568baf11cSdamien 			memcpy(cmd->odata, &hdr[1], hdr->len);
102668baf11cSdamien 		cmd->done = 1;
102768baf11cSdamien 		wakeup(cmd);
102868baf11cSdamien 		return;
102968baf11cSdamien 	}
103068baf11cSdamien 
103168baf11cSdamien 	/* Received unsolicited notification. */
103268baf11cSdamien 	DPRINTF(("received notification code=0x%02x len=%d\n",
103368baf11cSdamien 	    hdr->code, hdr->len));
103468baf11cSdamien 	switch (hdr->code & 0x3f) {
103568baf11cSdamien 	case AR_EVT_BEACON:
103668baf11cSdamien 		break;
103768baf11cSdamien 	case AR_EVT_TX_COMP:
103868baf11cSdamien 	{
103968baf11cSdamien 		struct ar_evt_tx_comp *tx = (struct ar_evt_tx_comp *)&hdr[1];
104068baf11cSdamien 		struct ieee80211_node *ni;
104168baf11cSdamien 		struct otus_node *on;
104268baf11cSdamien 
104368baf11cSdamien 		DPRINTF(("tx completed %s status=%d phy=0x%x\n",
104468baf11cSdamien 		    ether_sprintf(tx->macaddr), letoh16(tx->status),
104568baf11cSdamien 		    letoh32(tx->phy)));
104668baf11cSdamien 		s = splnet();
104768baf11cSdamien #ifdef notyet
104868baf11cSdamien #ifndef IEEE80211_STA_ONLY
104968baf11cSdamien 		if (ic->ic_opmode != IEEE80211_M_STA) {
105068baf11cSdamien 			ni = ieee80211_find_node(ic, tx->macaddr);
105168baf11cSdamien 			if (__predict_false(ni == NULL)) {
105268baf11cSdamien 				splx(s);
105368baf11cSdamien 				break;
105468baf11cSdamien 			}
105568baf11cSdamien 		} else
105668baf11cSdamien #endif
105768baf11cSdamien #endif
105868baf11cSdamien 			ni = ic->ic_bss;
105968baf11cSdamien 		/* Update rate control statistics. */
106068baf11cSdamien 		on = (void *)ni;
106168baf11cSdamien 		/* NB: we do not set the TX_MAC_RATE_PROBING flag. */
106268baf11cSdamien 		if (__predict_true(tx->status != 0))
106368baf11cSdamien 			on->amn.amn_retrycnt++;
106468baf11cSdamien 		splx(s);
106568baf11cSdamien 		break;
106668baf11cSdamien 	}
106768baf11cSdamien 	case AR_EVT_TBTT:
106868baf11cSdamien 		break;
106968baf11cSdamien 	}
107068baf11cSdamien }
107168baf11cSdamien 
107268baf11cSdamien void
107368baf11cSdamien otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, int len)
107468baf11cSdamien {
107568baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
107668baf11cSdamien 	struct ifnet *ifp = &ic->ic_if;
107768baf11cSdamien 	struct ieee80211_rxinfo rxi;
107868baf11cSdamien 	struct ieee80211_node *ni;
107968baf11cSdamien 	struct ar_rx_tail *tail;
108068baf11cSdamien 	struct ieee80211_frame *wh;
108168baf11cSdamien 	struct mbuf *m;
108268baf11cSdamien 	uint8_t *plcp;
108368baf11cSdamien 	int s, mlen, align;
108468baf11cSdamien 
108568baf11cSdamien 	if (__predict_false(len < AR_PLCP_HDR_LEN)) {
108668baf11cSdamien 		DPRINTF(("sub-xfer too short %d\n", len));
108768baf11cSdamien 		return;
108868baf11cSdamien 	}
108988185ad4Sdamien 	plcp = buf;
109068baf11cSdamien 
109168baf11cSdamien 	/* All bits in the PLCP header are set to 1 for non-MPDU. */
109268baf11cSdamien 	if (memcmp(plcp, AR_PLCP_HDR_INTR, AR_PLCP_HDR_LEN) == 0) {
109368baf11cSdamien 		otus_cmd_rxeof(sc, plcp + AR_PLCP_HDR_LEN,
109468baf11cSdamien 		    len - AR_PLCP_HDR_LEN);
109568baf11cSdamien 		return;
109668baf11cSdamien 	}
109768baf11cSdamien 
109868baf11cSdamien 	/* Received MPDU. */
109968baf11cSdamien 	if (__predict_false(len < AR_PLCP_HDR_LEN + sizeof (*tail))) {
110068baf11cSdamien 		DPRINTF(("MPDU too short %d\n", len));
1101b86c5724Sdamien 		ifp->if_ierrors++;
110268baf11cSdamien 		return;
110368baf11cSdamien 	}
110468baf11cSdamien 	tail = (struct ar_rx_tail *)(plcp + len - sizeof (*tail));
110568baf11cSdamien 
110668baf11cSdamien 	/* Discard error frames. */
110768baf11cSdamien 	if (__predict_false(tail->error != 0)) {
110868baf11cSdamien 		DPRINTF(("error frame 0x%02x\n", tail->error));
11094351e075Sdamien 		if (tail->error & AR_RX_ERROR_FCS) {
11104351e075Sdamien 			DPRINTFN(3, ("bad FCS\n"));
11114351e075Sdamien 		} else if (tail->error & AR_RX_ERROR_MMIC) {
1112b86c5724Sdamien 			/* Report Michael MIC failures to net80211. */
1113b86c5724Sdamien 			ic->ic_stats.is_rx_locmicfail++;
1114b86c5724Sdamien 			ieee80211_michael_mic_failure(ic, 0);
1115b86c5724Sdamien 		}
1116b86c5724Sdamien 		ifp->if_ierrors++;
111768baf11cSdamien 		return;
111868baf11cSdamien 	}
111968baf11cSdamien 	/* Compute MPDU's length. */
112068baf11cSdamien 	mlen = len - AR_PLCP_HDR_LEN - sizeof (*tail);
112168baf11cSdamien 	/* Make sure there's room for an 802.11 header + FCS. */
1122b86c5724Sdamien 	if (__predict_false(mlen < IEEE80211_MIN_LEN)) {
1123b86c5724Sdamien 		ifp->if_ierrors++;
112468baf11cSdamien 		return;
1125b86c5724Sdamien 	}
112668baf11cSdamien 	mlen -= IEEE80211_CRC_LEN;	/* strip 802.11 FCS */
112768baf11cSdamien 
112868baf11cSdamien 	wh = (struct ieee80211_frame *)(plcp + AR_PLCP_HDR_LEN);
112968baf11cSdamien 	/* Provide a 32-bit aligned protocol header to the stack. */
113068baf11cSdamien 	align = (ieee80211_has_qos(wh) ^ ieee80211_has_addr4(wh)) ? 2 : 0;
113168baf11cSdamien 
113268baf11cSdamien 	MGETHDR(m, M_DONTWAIT, MT_DATA);
113368baf11cSdamien 	if (__predict_false(m == NULL)) {
113468baf11cSdamien 		ifp->if_ierrors++;
113568baf11cSdamien 		return;
113668baf11cSdamien 	}
113768baf11cSdamien 	if (align + mlen > MHLEN) {
113868baf11cSdamien 		MCLGET(m, M_DONTWAIT);
113968baf11cSdamien 		if (__predict_false(!(m->m_flags & M_EXT))) {
114068baf11cSdamien 			ifp->if_ierrors++;
114168baf11cSdamien 			m_freem(m);
114268baf11cSdamien 			return;
114368baf11cSdamien 		}
114468baf11cSdamien 	}
114568baf11cSdamien 	/* Finalize mbuf. */
114668baf11cSdamien 	m->m_pkthdr.rcvif = ifp;
114768baf11cSdamien 	m->m_data += align;
114868baf11cSdamien 	memcpy(mtod(m, caddr_t), wh, mlen);
114968baf11cSdamien 	m->m_pkthdr.len = m->m_len = mlen;
115068baf11cSdamien 
115168baf11cSdamien #if NBPFILTER > 0
115268baf11cSdamien 	if (__predict_false(sc->sc_drvbpf != NULL)) {
115368baf11cSdamien 		struct otus_rx_radiotap_header *tap = &sc->sc_rxtap;
115468baf11cSdamien 		struct mbuf mb;
115568baf11cSdamien 
115668baf11cSdamien 		tap->wr_flags = 0;
115768baf11cSdamien 		tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
115868baf11cSdamien 		tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
115968baf11cSdamien 		tap->wr_antsignal = tail->rssi;
116068baf11cSdamien 		tap->wr_rate = 2;	/* In case it can't be found below. */
116168baf11cSdamien 		switch (tail->status & AR_RX_STATUS_MT_MASK) {
116268baf11cSdamien 		case AR_RX_STATUS_MT_CCK:
116368baf11cSdamien 			switch (plcp[0]) {
116488185ad4Sdamien 			case  10: tap->wr_rate =   2; break;
116588185ad4Sdamien 			case  20: tap->wr_rate =   4; break;
116688185ad4Sdamien 			case  55: tap->wr_rate =  11; break;
116788185ad4Sdamien 			case 110: tap->wr_rate =  22; break;
116868baf11cSdamien 			}
116968baf11cSdamien 			if (tail->status & AR_RX_STATUS_SHPREAMBLE)
117068baf11cSdamien 				tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
117168baf11cSdamien 			break;
117268baf11cSdamien 		case AR_RX_STATUS_MT_OFDM:
117368baf11cSdamien 			switch (plcp[0] & 0xf) {
117468baf11cSdamien 			case 0xb: tap->wr_rate =  12; break;
117568baf11cSdamien 			case 0xf: tap->wr_rate =  18; break;
117668baf11cSdamien 			case 0xa: tap->wr_rate =  24; break;
117768baf11cSdamien 			case 0xe: tap->wr_rate =  36; break;
117868baf11cSdamien 			case 0x9: tap->wr_rate =  48; break;
117968baf11cSdamien 			case 0xd: tap->wr_rate =  72; break;
118068baf11cSdamien 			case 0x8: tap->wr_rate =  96; break;
118168baf11cSdamien 			case 0xc: tap->wr_rate = 108; break;
118268baf11cSdamien 			}
118368baf11cSdamien 			break;
118468baf11cSdamien 		}
118568baf11cSdamien 		mb.m_data = (caddr_t)tap;
118668baf11cSdamien 		mb.m_len = sc->sc_rxtap_len;
118768baf11cSdamien 		mb.m_next = m;
118868baf11cSdamien 		mb.m_nextpkt = NULL;
118968baf11cSdamien 		mb.m_type = 0;
119068baf11cSdamien 		mb.m_flags = 0;
119168baf11cSdamien 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
119268baf11cSdamien 	}
119368baf11cSdamien #endif
119468baf11cSdamien 
119568baf11cSdamien 	s = splnet();
119668baf11cSdamien 	ni = ieee80211_find_rxnode(ic, wh);
119768baf11cSdamien 	rxi.rxi_flags = 0;
119868baf11cSdamien 	rxi.rxi_rssi = tail->rssi;
119968baf11cSdamien 	rxi.rxi_tstamp = 0;	/* unused */
120068baf11cSdamien 	ieee80211_input(ifp, m, ni, &rxi);
120168baf11cSdamien 
120268baf11cSdamien 	/* Node is no longer needed. */
120368baf11cSdamien 	ieee80211_release_node(ic, ni);
120468baf11cSdamien 	splx(s);
120568baf11cSdamien }
120668baf11cSdamien 
120768baf11cSdamien void
120868baf11cSdamien otus_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
120968baf11cSdamien {
121068baf11cSdamien 	struct otus_rx_data *data = priv;
121168baf11cSdamien 	struct otus_softc *sc = data->sc;
121268baf11cSdamien 	caddr_t buf = data->buf;
121368baf11cSdamien 	struct ar_rx_head *head;
121468baf11cSdamien 	uint16_t hlen;
121568baf11cSdamien 	int len;
121668baf11cSdamien 
121768baf11cSdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
121868baf11cSdamien 		DPRINTF(("RX status=%d\n", status));
121968baf11cSdamien 		if (status == USBD_STALLED)
122068baf11cSdamien 			usbd_clear_endpoint_stall_async(sc->data_rx_pipe);
122168baf11cSdamien 		if (status != USBD_CANCELLED)
122268baf11cSdamien 			goto resubmit;
122368baf11cSdamien 		return;
122468baf11cSdamien 	}
122568baf11cSdamien 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
122668baf11cSdamien 
122768baf11cSdamien 	while (len >= sizeof (*head)) {
122868baf11cSdamien 		head = (struct ar_rx_head *)buf;
122968baf11cSdamien 		if (__predict_false(head->tag != htole16(AR_RX_HEAD_TAG))) {
123068baf11cSdamien 			DPRINTF(("tag not valid 0x%x\n", letoh16(head->tag)));
123168baf11cSdamien 			break;
123268baf11cSdamien 		}
123368baf11cSdamien 		hlen = letoh16(head->len);
123468baf11cSdamien 		if (__predict_false(sizeof (*head) + hlen > len)) {
123568baf11cSdamien 			DPRINTF(("xfer too short %d/%d\n", len, hlen));
123668baf11cSdamien 			break;
123768baf11cSdamien 		}
123868baf11cSdamien 		/* Process sub-xfer. */
123968baf11cSdamien 		otus_sub_rxeof(sc, (uint8_t *)&head[1], hlen);
124068baf11cSdamien 
124168baf11cSdamien 		/* Next sub-xfer is aligned on a 32-bit boundary. */
124268baf11cSdamien 		hlen = (sizeof (*head) + hlen + 3) & ~3;
124368baf11cSdamien 		buf += hlen;
124468baf11cSdamien 		len -= hlen;
124568baf11cSdamien 	}
124668baf11cSdamien 
124768baf11cSdamien  resubmit:
124868baf11cSdamien 	usbd_setup_xfer(xfer, sc->data_rx_pipe, data, data->buf, OTUS_RXBUFSZ,
124968baf11cSdamien 	    USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, otus_rxeof);
125068baf11cSdamien 	(void)usbd_transfer(data->xfer);
125168baf11cSdamien }
125268baf11cSdamien 
125368baf11cSdamien void
125468baf11cSdamien otus_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
125568baf11cSdamien {
125668baf11cSdamien 	struct otus_tx_data *data = priv;
125768baf11cSdamien 	struct otus_softc *sc = data->sc;
125868baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
125968baf11cSdamien 	struct ifnet *ifp = &ic->ic_if;
126068baf11cSdamien 	int s;
126168baf11cSdamien 
126268baf11cSdamien 	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
126368baf11cSdamien 		DPRINTF(("TX status=%d\n", status));
126468baf11cSdamien 		if (status == USBD_STALLED)
126568baf11cSdamien 			usbd_clear_endpoint_stall_async(sc->data_tx_pipe);
126668baf11cSdamien 		ifp->if_oerrors++;
126768baf11cSdamien 		return;
126868baf11cSdamien 	}
126968baf11cSdamien 	s = splnet();
127068baf11cSdamien 	sc->tx_queued--;
127168baf11cSdamien 	sc->sc_tx_timer = 0;
127268baf11cSdamien 	ifp->if_flags &= ~IFF_OACTIVE;
127368baf11cSdamien 	otus_start(ifp);
127468baf11cSdamien 	splx(s);
127568baf11cSdamien }
127668baf11cSdamien 
127768baf11cSdamien int
127868baf11cSdamien otus_tx(struct otus_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
127968baf11cSdamien {
128068baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
128168baf11cSdamien 	struct otus_node *on = (void *)ni;
128268baf11cSdamien 	struct otus_tx_data *data;
128368baf11cSdamien 	struct ieee80211_frame *wh;
128468baf11cSdamien 	struct ieee80211_key *k;
128568baf11cSdamien 	struct ar_tx_head *head;
128668baf11cSdamien 	uint32_t phyctl;
128768baf11cSdamien 	uint16_t macctl, qos;
128888185ad4Sdamien 	uint8_t tid, qid;
128968baf11cSdamien 	int error, ridx, hasqos, xferlen;
129068baf11cSdamien 
129168baf11cSdamien 	wh = mtod(m, struct ieee80211_frame *);
129268baf11cSdamien 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
129368baf11cSdamien 		k = ieee80211_get_txkey(ic, wh, ni);
129468baf11cSdamien 		if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
129568baf11cSdamien 			return ENOBUFS;
129668baf11cSdamien 		wh = mtod(m, struct ieee80211_frame *);
129768baf11cSdamien 	}
129868baf11cSdamien 
129988185ad4Sdamien 	if ((hasqos = ieee80211_has_qos(wh))) {
130068baf11cSdamien 		qos = ieee80211_get_qos(wh);
130188185ad4Sdamien 		tid = qos & IEEE80211_QOS_TID;
130288185ad4Sdamien 		qid = ieee80211_up_to_ac(ic, tid);
130388185ad4Sdamien 	} else
130488185ad4Sdamien 		qid = EDCA_AC_BE;
130568baf11cSdamien 
130668baf11cSdamien 	/* Pickup a rate index. */
130768baf11cSdamien 	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
130868baf11cSdamien 	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA)
130968baf11cSdamien 		ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
131068baf11cSdamien 		    OTUS_RIDX_OFDM6 : OTUS_RIDX_CCK1;
131168baf11cSdamien 	else if (ic->ic_fixed_rate != -1)
131268baf11cSdamien 		ridx = sc->fixed_ridx;
131368baf11cSdamien 	else
131468baf11cSdamien 		ridx = on->ridx[ni->ni_txrate];
131568baf11cSdamien 
131668baf11cSdamien 	phyctl = 0;
13174351e075Sdamien 	macctl = AR_TX_MAC_BACKOFF | AR_TX_MAC_HW_DUR | AR_TX_MAC_QID(qid);
131868baf11cSdamien 
131968baf11cSdamien 	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
132068baf11cSdamien 	    (hasqos && ((qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
132168baf11cSdamien 	     IEEE80211_QOS_ACK_POLICY_NOACK)))
132268baf11cSdamien 		macctl |= AR_TX_MAC_NOACK;
132368baf11cSdamien 
1324d7d7f774Sdamien 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
1325d7d7f774Sdamien 		if (m->m_pkthdr.len + IEEE80211_CRC_LEN >= ic->ic_rtsthreshold)
132668baf11cSdamien 			macctl |= AR_TX_MAC_RTS;
132768baf11cSdamien 		else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
132868baf11cSdamien 		    ridx >= OTUS_RIDX_OFDM6) {
132968baf11cSdamien 			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
133068baf11cSdamien 				macctl |= AR_TX_MAC_CTS;
133168baf11cSdamien 			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
133268baf11cSdamien 				macctl |= AR_TX_MAC_RTS;
133368baf11cSdamien 		}
1334d7d7f774Sdamien 	}
133568baf11cSdamien 
133668baf11cSdamien 	phyctl |= AR_TX_PHY_MCS(otus_rates[ridx].mcs);
133768baf11cSdamien 	if (ridx >= OTUS_RIDX_OFDM6) {
133868baf11cSdamien 		phyctl |= AR_TX_PHY_MT_OFDM;
133968baf11cSdamien 		if (ridx <= OTUS_RIDX_OFDM24)
1340803f2018Sdamien 			phyctl |= AR_TX_PHY_ANTMSK(sc->txmask);
134168baf11cSdamien 		else
134268baf11cSdamien 			phyctl |= AR_TX_PHY_ANTMSK(1);
134368baf11cSdamien 	} else {	/* CCK */
134468baf11cSdamien 		phyctl |= AR_TX_PHY_MT_CCK;
1345803f2018Sdamien 		phyctl |= AR_TX_PHY_ANTMSK(sc->txmask);
134668baf11cSdamien 	}
134768baf11cSdamien 
1348d7d7f774Sdamien 	/* Update rate control stats for frames that are ACK'ed. */
134968baf11cSdamien 	if (!(macctl & AR_TX_MAC_NOACK))
135068baf11cSdamien 		((struct otus_node *)ni)->amn.amn_txcnt++;
135168baf11cSdamien 
135268baf11cSdamien 	data = &sc->tx_data[sc->tx_cur];
135368baf11cSdamien 	/* Fill Tx descriptor. */
135468baf11cSdamien 	head = (struct ar_tx_head *)data->buf;
135568baf11cSdamien 	head->len = htole16(m->m_pkthdr.len + IEEE80211_CRC_LEN);
135668baf11cSdamien 	head->macctl = htole16(macctl);
135768baf11cSdamien 	head->phyctl = htole32(phyctl);
135868baf11cSdamien 
135968baf11cSdamien #if NBPFILTER > 0
136068baf11cSdamien 	if (__predict_false(sc->sc_drvbpf != NULL)) {
136168baf11cSdamien 		struct otus_tx_radiotap_header *tap = &sc->sc_txtap;
136268baf11cSdamien 		struct mbuf mb;
136368baf11cSdamien 
136468baf11cSdamien 		tap->wt_flags = 0;
136568baf11cSdamien 		tap->wt_rate = otus_rates[ridx].rate;
136668baf11cSdamien 		tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
136768baf11cSdamien 		tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
136868baf11cSdamien 
136968baf11cSdamien 		mb.m_data = (caddr_t)tap;
137068baf11cSdamien 		mb.m_len = sc->sc_txtap_len;
137168baf11cSdamien 		mb.m_next = m;
137268baf11cSdamien 		mb.m_nextpkt = NULL;
137368baf11cSdamien 		mb.m_type = 0;
137468baf11cSdamien 		mb.m_flags = 0;
137568baf11cSdamien 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
137668baf11cSdamien 	}
137768baf11cSdamien #endif
137868baf11cSdamien 
137968baf11cSdamien 	xferlen = sizeof (*head) + m->m_pkthdr.len;
138068baf11cSdamien 	m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&head[1]);
138168baf11cSdamien 	m_freem(m);
138268baf11cSdamien 	ieee80211_release_node(ic, ni);
138368baf11cSdamien 
138468baf11cSdamien 	DPRINTFN(5, ("tx queued=%d len=%d mac=0x%04x phy=0x%08x rate=%d\n",
138568baf11cSdamien 	    sc->tx_queued, head->len, head->macctl, head->phyctl,
138668baf11cSdamien 	    otus_rates[ridx].rate));
138768baf11cSdamien 	usbd_setup_xfer(data->xfer, sc->data_tx_pipe, data, data->buf, xferlen,
138868baf11cSdamien 	    USBD_FORCE_SHORT_XFER | USBD_NO_COPY, OTUS_TX_TIMEOUT, otus_txeof);
138968baf11cSdamien 	error = usbd_transfer(data->xfer);
139068baf11cSdamien 	if (__predict_false(error != USBD_IN_PROGRESS && error != 0))
139168baf11cSdamien 		return error;
139268baf11cSdamien 
139368baf11cSdamien 	sc->tx_queued++;
139468baf11cSdamien 	sc->tx_cur = (sc->tx_cur + 1) % OTUS_TX_DATA_LIST_COUNT;
139568baf11cSdamien 
139668baf11cSdamien 	return 0;
139768baf11cSdamien }
139868baf11cSdamien 
139968baf11cSdamien void
140068baf11cSdamien otus_start(struct ifnet *ifp)
140168baf11cSdamien {
140268baf11cSdamien 	struct otus_softc *sc = ifp->if_softc;
140368baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
140468baf11cSdamien 	struct ieee80211_node *ni;
140568baf11cSdamien 	struct mbuf *m;
140668baf11cSdamien 
140768baf11cSdamien 	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
140868baf11cSdamien 		return;
140968baf11cSdamien 
141068baf11cSdamien 	for (;;) {
141168baf11cSdamien 		if (sc->tx_queued >= OTUS_TX_DATA_LIST_COUNT) {
141268baf11cSdamien 			ifp->if_flags |= IFF_OACTIVE;
141368baf11cSdamien 			break;
141468baf11cSdamien 		}
141568baf11cSdamien 		/* Send pending management frames first. */
141668baf11cSdamien 		IF_DEQUEUE(&ic->ic_mgtq, m);
141768baf11cSdamien 		if (m != NULL) {
141868baf11cSdamien 			ni = (void *)m->m_pkthdr.rcvif;
141968baf11cSdamien 			goto sendit;
142068baf11cSdamien 		}
142168baf11cSdamien 		if (ic->ic_state != IEEE80211_S_RUN)
142268baf11cSdamien 			break;
142368baf11cSdamien 
142468baf11cSdamien 		/* Encapsulate and send data frames. */
142568baf11cSdamien 		IFQ_DEQUEUE(&ifp->if_snd, m);
142668baf11cSdamien 		if (m == NULL)
142768baf11cSdamien 			break;
142868baf11cSdamien #if NBPFILTER > 0
142968baf11cSdamien 		if (ifp->if_bpf != NULL)
143068baf11cSdamien 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
143168baf11cSdamien #endif
143268baf11cSdamien 		if ((m = ieee80211_encap(ifp, m, &ni)) == NULL)
143368baf11cSdamien 			continue;
143468baf11cSdamien sendit:
143568baf11cSdamien #if NBPFILTER > 0
143668baf11cSdamien 		if (ic->ic_rawbpf != NULL)
143768baf11cSdamien 			bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
143868baf11cSdamien #endif
143968baf11cSdamien 		if (otus_tx(sc, m, ni) != 0) {
144068baf11cSdamien 			ieee80211_release_node(ic, ni);
144168baf11cSdamien 			ifp->if_oerrors++;
144268baf11cSdamien 			continue;
144368baf11cSdamien 		}
144468baf11cSdamien 
144568baf11cSdamien 		sc->sc_tx_timer = 5;
144668baf11cSdamien 		ifp->if_timer = 1;
144768baf11cSdamien 	}
144868baf11cSdamien }
144968baf11cSdamien 
145068baf11cSdamien void
145168baf11cSdamien otus_watchdog(struct ifnet *ifp)
145268baf11cSdamien {
145368baf11cSdamien 	struct otus_softc *sc = ifp->if_softc;
145468baf11cSdamien 
145568baf11cSdamien 	ifp->if_timer = 0;
145668baf11cSdamien 
145768baf11cSdamien 	if (sc->sc_tx_timer > 0) {
145868baf11cSdamien 		if (--sc->sc_tx_timer == 0) {
145968baf11cSdamien 			printf("%s: device timeout\n", sc->sc_dev.dv_xname);
146068baf11cSdamien 			/* otus_init(ifp); XXX needs a process context! */
146168baf11cSdamien 			ifp->if_oerrors++;
146268baf11cSdamien 			return;
146368baf11cSdamien 		}
146468baf11cSdamien 		ifp->if_timer = 1;
146568baf11cSdamien 	}
146668baf11cSdamien 	ieee80211_watchdog(ifp);
146768baf11cSdamien }
146868baf11cSdamien 
146968baf11cSdamien int
147068baf11cSdamien otus_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
147168baf11cSdamien {
147268baf11cSdamien 	struct otus_softc *sc = ifp->if_softc;
147368baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
147468baf11cSdamien 	struct ifaddr *ifa;
147568baf11cSdamien 	struct ifreq *ifr;
147668baf11cSdamien 	int s, error = 0;
147768baf11cSdamien 
147868baf11cSdamien 	s = splnet();
147968baf11cSdamien 
148068baf11cSdamien 	switch (cmd) {
148168baf11cSdamien 	case SIOCSIFADDR:
148268baf11cSdamien 		ifa = (struct ifaddr *)data;
148368baf11cSdamien 		ifp->if_flags |= IFF_UP;
148468baf11cSdamien #ifdef INET
148568baf11cSdamien 		if (ifa->ifa_addr->sa_family == AF_INET)
148668baf11cSdamien 			arp_ifinit(&ic->ic_ac, ifa);
148768baf11cSdamien #endif
148868baf11cSdamien 		/* FALLTHROUGH */
148968baf11cSdamien 	case SIOCSIFFLAGS:
149068baf11cSdamien 		if (ifp->if_flags & IFF_UP) {
149168baf11cSdamien 			if ((ifp->if_flags & IFF_RUNNING) &&
149268baf11cSdamien 			    ((ifp->if_flags ^ sc->sc_if_flags) &
149368baf11cSdamien 			     (IFF_ALLMULTI | IFF_PROMISC)) != 0) {
149468baf11cSdamien 				otus_set_multi(sc);
149568baf11cSdamien 			} else if (!(ifp->if_flags & IFF_RUNNING))
149668baf11cSdamien 				otus_init(ifp);
149768baf11cSdamien 
149868baf11cSdamien 		} else if (ifp->if_flags & IFF_RUNNING)
149968baf11cSdamien 			otus_stop(ifp);
150068baf11cSdamien 
150168baf11cSdamien 		sc->sc_if_flags = ifp->if_flags;
150268baf11cSdamien 		break;
150368baf11cSdamien 	case SIOCADDMULTI:
150468baf11cSdamien 	case SIOCDELMULTI:
150568baf11cSdamien 		ifr = (struct ifreq *)data;
150668baf11cSdamien 		error = (cmd == SIOCADDMULTI) ?
150768baf11cSdamien 		    ether_addmulti(ifr, &ic->ic_ac) :
150868baf11cSdamien 		    ether_delmulti(ifr, &ic->ic_ac);
150968baf11cSdamien 		if (error == ENETRESET)
151068baf11cSdamien 			error = 0;
151168baf11cSdamien 		break;
151288185ad4Sdamien 	case SIOCS80211CHANNEL:
151388185ad4Sdamien 		error = ieee80211_ioctl(ifp, cmd, data);
151488185ad4Sdamien 		if (error == ENETRESET &&
151588185ad4Sdamien 		    ic->ic_opmode == IEEE80211_M_MONITOR) {
151688185ad4Sdamien 			if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
151788185ad4Sdamien 			    (IFF_UP | IFF_RUNNING))
15186e30e88dSdamien 				otus_set_chan(sc, ic->ic_ibss_chan, 0);
151988185ad4Sdamien 			error = 0;
152088185ad4Sdamien 		}
152188185ad4Sdamien 		break;
152268baf11cSdamien 	default:
152368baf11cSdamien 		error = ieee80211_ioctl(ifp, cmd, data);
152468baf11cSdamien 	}
152568baf11cSdamien 
152668baf11cSdamien 	if (error == ENETRESET) {
152768baf11cSdamien 		if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
152868baf11cSdamien 		    (IFF_UP | IFF_RUNNING))
152968baf11cSdamien 			otus_init(ifp);
153068baf11cSdamien 		error = 0;
153168baf11cSdamien 	}
153268baf11cSdamien 
153368baf11cSdamien 	splx(s);
153468baf11cSdamien 	return error;
153568baf11cSdamien }
153668baf11cSdamien 
153768baf11cSdamien int
153868baf11cSdamien otus_set_multi(struct otus_softc *sc)
153968baf11cSdamien {
154068baf11cSdamien 	struct arpcom *ac = &sc->sc_ic.ic_ac;
154168baf11cSdamien 	struct ifnet *ifp = &ac->ac_if;
154268baf11cSdamien 	struct ether_multi *enm;
154368baf11cSdamien 	struct ether_multistep step;
154468baf11cSdamien 	uint32_t lo, hi;
154568baf11cSdamien 	uint8_t bit;
154668baf11cSdamien 
154768baf11cSdamien 	if ((ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) != 0) {
154868baf11cSdamien 		lo = hi = 0xffffffff;
154968baf11cSdamien 		goto done;
155068baf11cSdamien 	}
155168baf11cSdamien 	lo = hi = 0;
155268baf11cSdamien 	ETHER_FIRST_MULTI(step, ac, enm);
155368baf11cSdamien 	while (enm != NULL) {
155468baf11cSdamien 		if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
155568baf11cSdamien 			ifp->if_flags |= IFF_ALLMULTI;
155668baf11cSdamien 			lo = hi = 0xffffffff;
155768baf11cSdamien 			goto done;
155868baf11cSdamien 		}
155968baf11cSdamien 		bit = enm->enm_addrlo[5] >> 2;
156068baf11cSdamien 		if (bit < 32)
156168baf11cSdamien 			lo |= 1 << bit;
156268baf11cSdamien 		else
156368baf11cSdamien 			hi |= 1 << (bit - 32);
156468baf11cSdamien 		ETHER_NEXT_MULTI(step, enm);
156568baf11cSdamien 	}
156668baf11cSdamien  done:
156788185ad4Sdamien 	hi |= 1 << 31;	/* Make sure the broadcast bit is set. */
156868baf11cSdamien 	otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_L, lo);
156968baf11cSdamien 	otus_write(sc, AR_MAC_REG_GROUP_HASH_TBL_H, hi);
157068baf11cSdamien 	return otus_write_barrier(sc);
157168baf11cSdamien }
157268baf11cSdamien 
157368baf11cSdamien void
157468baf11cSdamien otus_updateedca(struct ieee80211com *ic)
157568baf11cSdamien {
157668baf11cSdamien 	/* Do it in a process context. */
157768baf11cSdamien 	otus_do_async(ic->ic_softc, otus_updateedca_cb, NULL, 0);
157868baf11cSdamien }
157968baf11cSdamien 
158068baf11cSdamien /* ARGSUSED */
158168baf11cSdamien void
158268baf11cSdamien otus_updateedca_cb(struct otus_softc *sc, void *arg)
158368baf11cSdamien {
158468baf11cSdamien #define EXP2(val)	((1 << (val)) - 1)
158568baf11cSdamien #define AIFS(val)	((val) * 9 + 10)
158668baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
158768baf11cSdamien 	const struct ieee80211_edca_ac_params *edca;
158868baf11cSdamien 	int s;
158968baf11cSdamien 
159068baf11cSdamien 	s = splnet();
159168baf11cSdamien 
159268baf11cSdamien 	edca = (ic->ic_flags & IEEE80211_F_QOS) ?
159368baf11cSdamien 	    ic->ic_edca_ac : otus_edca_def;
159468baf11cSdamien 
159568baf11cSdamien 	/* Set CWmin/CWmax values. */
159668baf11cSdamien 	otus_write(sc, AR_MAC_REG_AC0_CW,
159768baf11cSdamien 	    EXP2(edca[EDCA_AC_BE].ac_ecwmax) << 16 |
159868baf11cSdamien 	    EXP2(edca[EDCA_AC_BE].ac_ecwmin));
159968baf11cSdamien 	otus_write(sc, AR_MAC_REG_AC1_CW,
160068baf11cSdamien 	    EXP2(edca[EDCA_AC_BK].ac_ecwmax) << 16 |
160168baf11cSdamien 	    EXP2(edca[EDCA_AC_BK].ac_ecwmin));
160268baf11cSdamien 	otus_write(sc, AR_MAC_REG_AC2_CW,
160368baf11cSdamien 	    EXP2(edca[EDCA_AC_VI].ac_ecwmax) << 16 |
160468baf11cSdamien 	    EXP2(edca[EDCA_AC_VI].ac_ecwmin));
160568baf11cSdamien 	otus_write(sc, AR_MAC_REG_AC3_CW,
160668baf11cSdamien 	    EXP2(edca[EDCA_AC_VO].ac_ecwmax) << 16 |
160768baf11cSdamien 	    EXP2(edca[EDCA_AC_VO].ac_ecwmin));
160868baf11cSdamien 	otus_write(sc, AR_MAC_REG_AC4_CW,		/* Special TXQ. */
160968baf11cSdamien 	    EXP2(edca[EDCA_AC_VO].ac_ecwmax) << 16 |
161068baf11cSdamien 	    EXP2(edca[EDCA_AC_VO].ac_ecwmin));
161168baf11cSdamien 
161268baf11cSdamien 	/* Set AIFSN values. */
161368baf11cSdamien 	otus_write(sc, AR_MAC_REG_AC1_AC0_AIFS,
161468baf11cSdamien 	    AIFS(edca[EDCA_AC_VI].ac_aifsn) << 24 |
161568baf11cSdamien 	    AIFS(edca[EDCA_AC_BK].ac_aifsn) << 12 |
161668baf11cSdamien 	    AIFS(edca[EDCA_AC_BE].ac_aifsn));
161768baf11cSdamien 	otus_write(sc, AR_MAC_REG_AC3_AC2_AIFS,
161868baf11cSdamien 	    AIFS(edca[EDCA_AC_VO].ac_aifsn) << 16 |	/* Special TXQ. */
161968baf11cSdamien 	    AIFS(edca[EDCA_AC_VO].ac_aifsn) <<  4 |
162068baf11cSdamien 	    AIFS(edca[EDCA_AC_VI].ac_aifsn) >>  8);
162168baf11cSdamien 
162268baf11cSdamien 	/* Set TXOP limit. */
162368baf11cSdamien 	otus_write(sc, AR_MAC_REG_AC1_AC0_TXOP,
162468baf11cSdamien 	    edca[EDCA_AC_BK].ac_txoplimit << 16 |
162568baf11cSdamien 	    edca[EDCA_AC_BE].ac_txoplimit);
162668baf11cSdamien 	otus_write(sc, AR_MAC_REG_AC3_AC2_TXOP,
162768baf11cSdamien 	    edca[EDCA_AC_VO].ac_txoplimit << 16 |
162868baf11cSdamien 	    edca[EDCA_AC_VI].ac_txoplimit);
162968baf11cSdamien 
163068baf11cSdamien 	splx(s);
163168baf11cSdamien 
163268baf11cSdamien 	(void)otus_write_barrier(sc);
163368baf11cSdamien #undef AIFS
163468baf11cSdamien #undef EXP2
163568baf11cSdamien }
163668baf11cSdamien 
163768baf11cSdamien void
163868baf11cSdamien otus_updateslot(struct ieee80211com *ic)
163968baf11cSdamien {
164068baf11cSdamien 	/* Do it in a process context. */
164168baf11cSdamien 	otus_do_async(ic->ic_softc, otus_updateslot_cb, NULL, 0);
164268baf11cSdamien }
164368baf11cSdamien 
164468baf11cSdamien /* ARGSUSED */
164568baf11cSdamien void
164668baf11cSdamien otus_updateslot_cb(struct otus_softc *sc, void *arg)
164768baf11cSdamien {
164868baf11cSdamien 	uint32_t slottime;
164968baf11cSdamien 
165068baf11cSdamien 	slottime = (sc->sc_ic.ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
165168baf11cSdamien 	otus_write(sc, AR_MAC_REG_SLOT_TIME, slottime << 10);
165268baf11cSdamien 	(void)otus_write_barrier(sc);
165368baf11cSdamien }
165468baf11cSdamien 
165568baf11cSdamien int
165668baf11cSdamien otus_init_mac(struct otus_softc *sc)
165768baf11cSdamien {
165868baf11cSdamien 	int error;
165968baf11cSdamien 
166068baf11cSdamien 	otus_write(sc, AR_MAC_REG_ACK_EXTENSION, 0x40);
166168baf11cSdamien 	otus_write(sc, AR_MAC_REG_RETRY_MAX, 0);
166268baf11cSdamien 	otus_write(sc, AR_MAC_REG_SNIFFER, 0x2000000);
166368baf11cSdamien 	otus_write(sc, AR_MAC_REG_RX_THRESHOLD, 0xc1f80);
166468baf11cSdamien 	otus_write(sc, AR_MAC_REG_RX_PE_DELAY, 0x70);
166568baf11cSdamien 	otus_write(sc, AR_MAC_REG_EIFS_AND_SIFS, 0xa144000);
166668baf11cSdamien 	otus_write(sc, AR_MAC_REG_SLOT_TIME, 9 << 10);
166768baf11cSdamien 	otus_write(sc, 0x1c3b2c, 0x19000000);
166868baf11cSdamien 	/* NAV protects ACK only (in TXOP). */
166968baf11cSdamien 	otus_write(sc, 0x1c3b38, 0x201);
167068baf11cSdamien 	/* Set beacon Tx power to 0x7. */
167168baf11cSdamien 	otus_write(sc, AR_MAC_REG_BCN_HT1, 0x8000170);
167268baf11cSdamien 	otus_write(sc, AR_MAC_REG_BACKOFF_PROTECT, 0x105);
167368baf11cSdamien 	otus_write(sc, 0x1c3b9c, 0x10000a);
167468baf11cSdamien 	/* Filter any control frames, BAR is bit 24. */
167568baf11cSdamien 	otus_write(sc, 0x1c368c, 0x0500ffff);
167668baf11cSdamien 	otus_write(sc, 0x1c3c40, 0x1);
167768baf11cSdamien 	otus_write(sc, AR_MAC_REG_BASIC_RATE, 0x150f);
167868baf11cSdamien 	otus_write(sc, AR_MAC_REG_MANDATORY_RATE, 0x150f);
167968baf11cSdamien 	otus_write(sc, AR_MAC_REG_RTS_CTS_RATE, 0x10b01bb);
168068baf11cSdamien 	otus_write(sc, 0x1c3694, 0x4003c1e);
168168baf11cSdamien 	/* Enable LED0 and LED1. */
168268baf11cSdamien 	otus_write(sc, 0x1d0100, 0x3);
168368baf11cSdamien 	otus_write(sc, 0x1d0104, 0x3);
168468baf11cSdamien 	/* Switch MAC to OTUS interface. */
168568baf11cSdamien 	otus_write(sc, 0x1c3600, 0x3);
168668baf11cSdamien 	otus_write(sc, 0x1c3c50, 0xffff);
168768baf11cSdamien 	otus_write(sc, 0x1c3680, 0xf00008);
168868baf11cSdamien 	/* Disable Rx timeout (workaround). */
168968baf11cSdamien 	otus_write(sc, 0x1c362c, 0);
169068baf11cSdamien 
169168baf11cSdamien 	/* Set USB Rx stream mode maximum frame number to 2. */
169268baf11cSdamien 	otus_write(sc, 0x1e1110, 0x4);
169368baf11cSdamien 	/* Set USB Rx stream mode timeout to 10us. */
169468baf11cSdamien 	otus_write(sc, 0x1e1114, 0x80);
169568baf11cSdamien 
169668baf11cSdamien 	/* Set clock frequency to 88/80MHz. */
169768baf11cSdamien 	otus_write(sc, 0x1d4008, 0x73);
169868baf11cSdamien 	/* Set WLAN DMA interrupt mode: generate intr per packet. */
169968baf11cSdamien 	otus_write(sc, 0x1c3d7c, 0x110011);
170068baf11cSdamien 	otus_write(sc, 0x1c3bb0, 0x4);
170168baf11cSdamien 	otus_write(sc, AR_MAC_REG_TXOP_NOT_ENOUGH_INDICATION, 0x141e0f48);
170268baf11cSdamien 
170368baf11cSdamien 	/* Disable HW decryption for now. */
170468baf11cSdamien 	otus_write(sc, 0x1c3678, 0x78);
170568baf11cSdamien 
170668baf11cSdamien 	if ((error = otus_write_barrier(sc)) != 0)
170768baf11cSdamien 		return error;
170868baf11cSdamien 
170968baf11cSdamien 	/* Set default EDCA parameters. */
171068baf11cSdamien 	otus_updateedca_cb(sc, NULL);
171168baf11cSdamien 
171268baf11cSdamien 	return 0;
171368baf11cSdamien }
171468baf11cSdamien 
171568baf11cSdamien /*
171668baf11cSdamien  * Return default value for PHY register based on current operating mode.
171768baf11cSdamien  */
171868baf11cSdamien uint32_t
171968baf11cSdamien otus_phy_get_def(struct otus_softc *sc, uint32_t reg)
172068baf11cSdamien {
172168baf11cSdamien 	int i;
172268baf11cSdamien 
172368baf11cSdamien 	for (i = 0; i < nitems(ar5416_phy_regs); i++)
172468baf11cSdamien 		if (AR_PHY(ar5416_phy_regs[i]) == reg)
172568baf11cSdamien 			return sc->phy_vals[i];
172688185ad4Sdamien 	return 0;	/* Register not found. */
172768baf11cSdamien }
172868baf11cSdamien 
172968baf11cSdamien /*
173068baf11cSdamien  * Update PHY's programming based on vendor-specific data stored in EEPROM.
173168baf11cSdamien  * This is for FEM-type devices only.
173268baf11cSdamien  */
173368baf11cSdamien int
173468baf11cSdamien otus_set_board_values(struct otus_softc *sc, struct ieee80211_channel *c)
173568baf11cSdamien {
173668baf11cSdamien 	const struct ModalEepHeader *eep;
173768baf11cSdamien 	uint32_t tmp, offset;
173868baf11cSdamien 
173968baf11cSdamien 	if (IEEE80211_IS_CHAN_5GHZ(c))
174068baf11cSdamien 		eep = &sc->eeprom.modalHeader[0];
174168baf11cSdamien 	else
174268baf11cSdamien 		eep = &sc->eeprom.modalHeader[1];
174368baf11cSdamien 
174468baf11cSdamien 	/* Offset of chain 2. */
174568baf11cSdamien 	offset = 2 * 0x1000;
174668baf11cSdamien 
174768baf11cSdamien 	tmp = letoh32(eep->antCtrlCommon);
1748803f2018Sdamien 	otus_write(sc, AR_PHY_SWITCH_COM, tmp);
174968baf11cSdamien 
175068baf11cSdamien 	tmp = letoh32(eep->antCtrlChain[0]);
1751803f2018Sdamien 	otus_write(sc, AR_PHY_SWITCH_CHAIN_0, tmp);
175268baf11cSdamien 
175368baf11cSdamien 	tmp = letoh32(eep->antCtrlChain[1]);
1754803f2018Sdamien 	otus_write(sc, AR_PHY_SWITCH_CHAIN_0 + offset, tmp);
175568baf11cSdamien 
175668baf11cSdamien 	if (1 /* sc->sc_sco == AR_SCO_SCN */) {
175768baf11cSdamien 		tmp = otus_phy_get_def(sc, AR_PHY_SETTLING);
175868baf11cSdamien 		tmp &= ~(0x7f << 7);
175968baf11cSdamien 		tmp |= (eep->switchSettling & 0x7f) << 7;
1760803f2018Sdamien 		otus_write(sc, AR_PHY_SETTLING, tmp);
176168baf11cSdamien 	}
176268baf11cSdamien 
176368baf11cSdamien 	tmp = otus_phy_get_def(sc, AR_PHY_DESIRED_SZ);
176468baf11cSdamien 	tmp &= ~0xffff;
176568baf11cSdamien 	tmp |= eep->pgaDesiredSize << 8 | eep->adcDesiredSize;
1766803f2018Sdamien 	otus_write(sc, AR_PHY_DESIRED_SZ, tmp);
176768baf11cSdamien 
176868baf11cSdamien 	tmp = eep->txEndToXpaOff << 24 | eep->txEndToXpaOff << 16 |
176968baf11cSdamien 	      eep->txFrameToXpaOn << 8 | eep->txFrameToXpaOn;
1770803f2018Sdamien 	otus_write(sc, AR_PHY_RF_CTL4, tmp);
177168baf11cSdamien 
177268baf11cSdamien 	tmp = otus_phy_get_def(sc, AR_PHY_RF_CTL3);
177368baf11cSdamien 	tmp &= ~(0xff << 16);
177468baf11cSdamien 	tmp |= eep->txEndToRxOn << 16;
1775803f2018Sdamien 	otus_write(sc, AR_PHY_RF_CTL3, tmp);
177668baf11cSdamien 
177768baf11cSdamien 	tmp = otus_phy_get_def(sc, AR_PHY_CCA);
177868baf11cSdamien 	tmp &= ~(0x7f << 12);
177968baf11cSdamien 	tmp |= (eep->thresh62 & 0x7f) << 12;
1780803f2018Sdamien 	otus_write(sc, AR_PHY_CCA, tmp);
178168baf11cSdamien 
178268baf11cSdamien 	tmp = otus_phy_get_def(sc, AR_PHY_RXGAIN);
178368baf11cSdamien 	tmp &= ~(0x3f << 12);
178468baf11cSdamien 	tmp |= (eep->txRxAttenCh[0] & 0x3f) << 12;
1785803f2018Sdamien 	otus_write(sc, AR_PHY_RXGAIN, tmp);
178668baf11cSdamien 
178768baf11cSdamien 	tmp = otus_phy_get_def(sc, AR_PHY_RXGAIN + offset);
178868baf11cSdamien 	tmp &= ~(0x3f << 12);
178968baf11cSdamien 	tmp |= (eep->txRxAttenCh[1] & 0x3f) << 12;
1790803f2018Sdamien 	otus_write(sc, AR_PHY_RXGAIN + offset, tmp);
179168baf11cSdamien 
179268baf11cSdamien 	tmp = otus_phy_get_def(sc, AR_PHY_GAIN_2GHZ);
179368baf11cSdamien 	tmp &= ~(0x3f << 18);
179468baf11cSdamien 	tmp |= (eep->rxTxMarginCh[0] & 0x3f) << 18;
179568baf11cSdamien 	if (IEEE80211_IS_CHAN_5GHZ(c)) {
179668baf11cSdamien 		tmp &= ~(0xf << 10);
179768baf11cSdamien 		tmp |= (eep->bswMargin[0] & 0xf) << 10;
179868baf11cSdamien 	}
1799803f2018Sdamien 	otus_write(sc, AR_PHY_GAIN_2GHZ, tmp);
180068baf11cSdamien 
180168baf11cSdamien 	tmp = otus_phy_get_def(sc, AR_PHY_GAIN_2GHZ + offset);
180268baf11cSdamien 	tmp &= ~(0x3f << 18);
180368baf11cSdamien 	tmp |= (eep->rxTxMarginCh[1] & 0x3f) << 18;
1804803f2018Sdamien 	otus_write(sc, AR_PHY_GAIN_2GHZ + offset, tmp);
180568baf11cSdamien 
180668baf11cSdamien 	tmp = otus_phy_get_def(sc, AR_PHY_TIMING_CTRL4);
180768baf11cSdamien 	tmp &= ~(0x3f << 5 | 0x1f);
180868baf11cSdamien 	tmp |= (eep->iqCalICh[0] & 0x3f) << 5 | (eep->iqCalQCh[0] & 0x1f);
1809803f2018Sdamien 	otus_write(sc, AR_PHY_TIMING_CTRL4, tmp);
181068baf11cSdamien 
181168baf11cSdamien 	tmp = otus_phy_get_def(sc, AR_PHY_TIMING_CTRL4 + offset);
181268baf11cSdamien 	tmp &= ~(0x3f << 5 | 0x1f);
181368baf11cSdamien 	tmp |= (eep->iqCalICh[1] & 0x3f) << 5 | (eep->iqCalQCh[1] & 0x1f);
1814803f2018Sdamien 	otus_write(sc, AR_PHY_TIMING_CTRL4 + offset, tmp);
181568baf11cSdamien 
1816803f2018Sdamien 	tmp = otus_phy_get_def(sc, AR_PHY_TPCRG1);
181768baf11cSdamien 	tmp &= ~(0xf << 16);
181868baf11cSdamien 	tmp |= (eep->xpd & 0xf) << 16;
1819803f2018Sdamien 	otus_write(sc, AR_PHY_TPCRG1, tmp);
182068baf11cSdamien 
182168baf11cSdamien 	return otus_write_barrier(sc);
182268baf11cSdamien }
182368baf11cSdamien 
182468baf11cSdamien int
182568baf11cSdamien otus_program_phy(struct otus_softc *sc, struct ieee80211_channel *c)
182668baf11cSdamien {
182768baf11cSdamien 	const uint32_t *vals;
182868baf11cSdamien 	int error, i;
182968baf11cSdamien 
183068baf11cSdamien 	/* Select PHY programming based on band and bandwidth. */
183168baf11cSdamien 	if (IEEE80211_IS_CHAN_2GHZ(c))
183268baf11cSdamien 		vals = ar5416_phy_vals_2ghz_20mhz;
183368baf11cSdamien 	else
183468baf11cSdamien 		vals = ar5416_phy_vals_5ghz_20mhz;
183568baf11cSdamien 	for (i = 0; i < nitems(ar5416_phy_regs); i++)
1836803f2018Sdamien 		otus_write(sc, AR_PHY(ar5416_phy_regs[i]), vals[i]);
183768baf11cSdamien 	sc->phy_vals = vals;
183868baf11cSdamien 
183968baf11cSdamien 	if (sc->eeprom.baseEepHeader.deviceType == 0x80)	/* FEM */
184068baf11cSdamien 		if ((error = otus_set_board_values(sc, c)) != 0)
184168baf11cSdamien 			return error;
184268baf11cSdamien 
184368baf11cSdamien 	/* Initial Tx power settings. */
1844803f2018Sdamien 	otus_write(sc, AR_PHY_POWER_TX_RATE_MAX, 0x7f);
1845803f2018Sdamien 	otus_write(sc, AR_PHY_POWER_TX_RATE1, 0x3f3f3f3f);
1846803f2018Sdamien 	otus_write(sc, AR_PHY_POWER_TX_RATE2, 0x3f3f3f3f);
1847803f2018Sdamien 	otus_write(sc, AR_PHY_POWER_TX_RATE3, 0x3f3f3f3f);
1848803f2018Sdamien 	otus_write(sc, AR_PHY_POWER_TX_RATE4, 0x3f3f3f3f);
1849803f2018Sdamien 	otus_write(sc, AR_PHY_POWER_TX_RATE5, 0x3f3f3f3f);
1850803f2018Sdamien 	otus_write(sc, AR_PHY_POWER_TX_RATE6, 0x3f3f3f3f);
1851803f2018Sdamien 	otus_write(sc, AR_PHY_POWER_TX_RATE7, 0x3f3f3f3f);
1852803f2018Sdamien 	otus_write(sc, AR_PHY_POWER_TX_RATE8, 0x3f3f3f3f);
1853803f2018Sdamien 	otus_write(sc, AR_PHY_POWER_TX_RATE9, 0x3f3f3f3f);
185468baf11cSdamien 
185568baf11cSdamien 	if (IEEE80211_IS_CHAN_2GHZ(c))
185668baf11cSdamien 		otus_write(sc, 0x1d4014, 0x5163);
185768baf11cSdamien 	else
185868baf11cSdamien 		otus_write(sc, 0x1d4014, 0x5143);
185968baf11cSdamien 
186068baf11cSdamien 	return otus_write_barrier(sc);
186168baf11cSdamien }
186268baf11cSdamien 
186368baf11cSdamien static __inline uint8_t
186468baf11cSdamien otus_reverse_bits(uint8_t v)
186568baf11cSdamien {
186668baf11cSdamien 	v = ((v >> 1) & 0x55) | ((v & 0x55) << 1);
186768baf11cSdamien 	v = ((v >> 2) & 0x33) | ((v & 0x33) << 2);
186868baf11cSdamien 	v = ((v >> 4) & 0x0f) | ((v & 0x0f) << 4);
186968baf11cSdamien 	return v;
187068baf11cSdamien }
187168baf11cSdamien 
18726e30e88dSdamien int
18736e30e88dSdamien otus_set_rf_bank4(struct otus_softc *sc, struct ieee80211_channel *c)
18746e30e88dSdamien {
18756e30e88dSdamien 	uint8_t chansel, d0, d1;
18766e30e88dSdamien 	uint16_t data;
18776e30e88dSdamien 	int error;
18786e30e88dSdamien 
18796e30e88dSdamien 	d0 = 0;
18806e30e88dSdamien 	if (IEEE80211_IS_CHAN_5GHZ(c)) {
18816e30e88dSdamien 		chansel = (c->ic_freq - 4800) / 5;
18826e30e88dSdamien 		if (chansel & 1)
18836e30e88dSdamien 			d0 |= AR_BANK4_AMODE_REFSEL(2);
18846e30e88dSdamien 		else
18856e30e88dSdamien 			d0 |= AR_BANK4_AMODE_REFSEL(1);
18866e30e88dSdamien 	} else {
18876e30e88dSdamien 		d0 |= AR_BANK4_AMODE_REFSEL(2);
18886e30e88dSdamien 		if (c->ic_freq == 2484) {	/* CH 14 */
18896e30e88dSdamien 			d0 |= AR_BANK4_BMODE_LF_SYNTH_FREQ;
18906e30e88dSdamien 			chansel = 10 + (c->ic_freq - 2274) / 5;
18916e30e88dSdamien 		} else
18926e30e88dSdamien 			chansel = 16 + (c->ic_freq - 2272) / 5;
18936e30e88dSdamien 		chansel <<= 2;
18946e30e88dSdamien 	}
18956e30e88dSdamien 	d0 |= AR_BANK4_ADDR(1) | AR_BANK4_CHUP;
18966e30e88dSdamien 	d1 = otus_reverse_bits(chansel);
18976e30e88dSdamien 
18986e30e88dSdamien 	/* Write bits 0-4 of d0 and d1. */
18996e30e88dSdamien 	data = (d1 & 0x1f) << 5 | (d0 & 0x1f);
19006e30e88dSdamien 	otus_write(sc, AR_PHY(44), data);
19016e30e88dSdamien 	/* Write bits 5-7 of d0 and d1. */
19026e30e88dSdamien 	data = (d1 >> 5) << 5 | (d0 >> 5);
19036e30e88dSdamien 	otus_write(sc, AR_PHY(58), data);
19046e30e88dSdamien 
19056e30e88dSdamien 	if ((error = otus_write_barrier(sc)) == 0)
19066e30e88dSdamien 		usbd_delay_ms(sc->sc_udev, 10);
19076e30e88dSdamien 	return error;
19086e30e88dSdamien }
19096e30e88dSdamien 
191068baf11cSdamien void
191168baf11cSdamien otus_get_delta_slope(uint32_t coeff, uint32_t *exponent, uint32_t *mantissa)
191268baf11cSdamien {
191368baf11cSdamien #define COEFF_SCALE_SHIFT	24
191468baf11cSdamien 	uint32_t exp, man;
191568baf11cSdamien 
191668baf11cSdamien 	/* exponent = 14 - floor(log2(coeff)) */
191768baf11cSdamien 	for (exp = 31; exp > 0; exp--)
191868baf11cSdamien 		if (coeff & (1 << exp))
191968baf11cSdamien 			break;
192068baf11cSdamien 	KASSERT(exp != 0);
192168baf11cSdamien 	exp = 14 - (exp - COEFF_SCALE_SHIFT);
192268baf11cSdamien 
192368baf11cSdamien 	/* mantissa = floor(coeff * 2^exponent + 0.5) */
192468baf11cSdamien 	man = coeff + (1 << (COEFF_SCALE_SHIFT - exp - 1));
192568baf11cSdamien 
192668baf11cSdamien 	*mantissa = man >> (COEFF_SCALE_SHIFT - exp);
192768baf11cSdamien 	*exponent = exp - 16;
192868baf11cSdamien #undef COEFF_SCALE_SHIFT
192968baf11cSdamien }
193068baf11cSdamien 
193168baf11cSdamien int
19326e30e88dSdamien otus_set_chan(struct otus_softc *sc, struct ieee80211_channel *c, int assoc)
193368baf11cSdamien {
193468baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
193568baf11cSdamien 	struct ar_cmd_frequency cmd;
193668baf11cSdamien 	struct ar_rsp_frequency rsp;
193768baf11cSdamien 	const uint32_t *vals;
193868baf11cSdamien 	uint32_t coeff, exp, man, tmp;
19396e30e88dSdamien 	uint8_t code;
194068baf11cSdamien 	int error, chan, i;
194168baf11cSdamien 
194268baf11cSdamien 	chan = ieee80211_chan2ieee(ic, c);
19436e30e88dSdamien 	DPRINTF(("setting channel %d (%dMHz)\n", chan, c->ic_freq));
194468baf11cSdamien 
194568baf11cSdamien 	tmp = IEEE80211_IS_CHAN_2GHZ(c) ? 0x105 : 0x104;
194668baf11cSdamien 	otus_write(sc, AR_MAC_REG_DYNAMIC_SIFS_ACK, tmp);
19476e30e88dSdamien 	if ((error = otus_write_barrier(sc)) != 0)
19486e30e88dSdamien 		return error;
194968baf11cSdamien 
195088185ad4Sdamien 	/* Disable BB Heavy Clip. */
1951803f2018Sdamien 	otus_write(sc, AR_PHY_HEAVY_CLIP_ENABLE, 0x200);
19526e30e88dSdamien 	if ((error = otus_write_barrier(sc)) != 0)
19536e30e88dSdamien 		return error;
195468baf11cSdamien 
19556e30e88dSdamien 	/* XXX Is that FREQ_START ? */
195668baf11cSdamien 	error = otus_cmd(sc, AR_CMD_FREQ_STRAT, NULL, 0, NULL);
195768baf11cSdamien 	if (error != 0)
195868baf11cSdamien 		return error;
195968baf11cSdamien 
196088185ad4Sdamien 	/* Reprogram PHY and RF on channel band or bandwidth changes. */
19616e30e88dSdamien 	if (sc->bb_reset || c->ic_flags != sc->sc_curchan->ic_flags) {
196268baf11cSdamien 		DPRINTF(("band switch\n"));
196368baf11cSdamien 
19646e30e88dSdamien 		/* Cold/Warm reset BB/ADDA. */
19656e30e88dSdamien 		otus_write(sc, 0x1d4004, sc->bb_reset ? 0x800 : 0x400);
19666e30e88dSdamien 		if ((error = otus_write_barrier(sc)) != 0)
19676e30e88dSdamien 			return error;
196868baf11cSdamien 		otus_write(sc, 0x1d4004, 0);
19696e30e88dSdamien 		if ((error = otus_write_barrier(sc)) != 0)
19706e30e88dSdamien 			return error;
19716e30e88dSdamien 		sc->bb_reset = 0;
197268baf11cSdamien 
197368baf11cSdamien 		if ((error = otus_program_phy(sc, c)) != 0) {
197468baf11cSdamien 			printf("%s: could not program PHY\n",
197568baf11cSdamien 			    sc->sc_dev.dv_xname);
197668baf11cSdamien 			return error;
197768baf11cSdamien 		}
197868baf11cSdamien 
197968baf11cSdamien 		/* Select RF programming based on band. */
198068baf11cSdamien 		if (IEEE80211_IS_CHAN_5GHZ(c))
198168baf11cSdamien 			vals = ar5416_banks_vals_5ghz;
198268baf11cSdamien 		else
198368baf11cSdamien 			vals = ar5416_banks_vals_2ghz;
1984803f2018Sdamien 		for (i = 0; i < nitems(ar5416_banks_regs); i++)
1985803f2018Sdamien 			otus_write(sc, AR_PHY(ar5416_banks_regs[i]), vals[i]);
19866e30e88dSdamien 		if ((error = otus_write_barrier(sc)) != 0) {
19876e30e88dSdamien 			printf("%s: could not program RF\n",
19886e30e88dSdamien 			    sc->sc_dev.dv_xname);
198968baf11cSdamien 			return error;
19906e30e88dSdamien 		}
199168baf11cSdamien 		code = AR_CMD_RF_INIT;
199268baf11cSdamien 	} else {
199368baf11cSdamien 		code = AR_CMD_FREQUENCY;
199468baf11cSdamien 	}
199568baf11cSdamien 
19966e30e88dSdamien 	if ((error = otus_set_rf_bank4(sc, c)) != 0)
199768baf11cSdamien 		return error;
199868baf11cSdamien 
1999803f2018Sdamien 	tmp = (sc->txmask == 0x5) ? 0x340 : 0x240;
2000803f2018Sdamien 	otus_write(sc, AR_PHY_TURBO, tmp);
20016e30e88dSdamien 	if ((error = otus_write_barrier(sc)) != 0)
20026e30e88dSdamien 		return error;
200368baf11cSdamien 
200468baf11cSdamien 	/* Send firmware command to set channel. */
200568baf11cSdamien 	cmd.freq = htole32((uint32_t)c->ic_freq * 1000);
200668baf11cSdamien 	cmd.dynht2040 = htole32(0);
200768baf11cSdamien 	cmd.htena = htole32(1);
200868baf11cSdamien 	/* Set Delta Slope (exponent and mantissa). */
200968baf11cSdamien 	coeff = (100 << 24) / c->ic_freq;
201068baf11cSdamien 	otus_get_delta_slope(coeff, &exp, &man);
20116e30e88dSdamien 	cmd.dsc_exp = htole32(exp);
20126e30e88dSdamien 	cmd.dsc_man = htole32(man);
20136e30e88dSdamien 	DPRINTF(("ds coeff=%u exp=%u man=%u\n", coeff, exp, man));
20146e30e88dSdamien 	/* For Short GI, coeff is 9/10 that of normal coeff. */
201568baf11cSdamien 	coeff = (9 * coeff) / 10;
201668baf11cSdamien 	otus_get_delta_slope(coeff, &exp, &man);
20176e30e88dSdamien 	cmd.dsc_shgi_exp = htole32(exp);
20186e30e88dSdamien 	cmd.dsc_shgi_man = htole32(man);
20196e30e88dSdamien 	DPRINTF(("ds shgi coeff=%u exp=%u man=%u\n", coeff, exp, man));
20206e30e88dSdamien 	/* Set wait time for AGC and noise calibration (100 or 200ms). */
20216e30e88dSdamien 	cmd.check_loop_count = assoc ? htole32(2000) : htole32(1000);
20224351e075Sdamien 	DPRINTF(("%s\n", (code == AR_CMD_RF_INIT) ? "RF_INIT" : "FREQUENCY"));
20236e30e88dSdamien 	error = otus_cmd(sc, code, &cmd, sizeof cmd, &rsp);
202468baf11cSdamien 	if (error != 0)
202568baf11cSdamien 		return error;
20266e30e88dSdamien 	if ((rsp.status & htole32(AR_CAL_ERR_AGC | AR_CAL_ERR_NF_VAL)) != 0) {
202768baf11cSdamien 		DPRINTF(("status=0x%x\n", letoh32(rsp.status)));
20286e30e88dSdamien 		/* Force cold reset on next channel. */
20296e30e88dSdamien 		sc->bb_reset = 1;
20306e30e88dSdamien 	}
20316e30e88dSdamien #ifdef OTUS_DEBUG
20326e30e88dSdamien 	if (otus_debug) {
20336e30e88dSdamien 		printf("calibration status=0x%x\n", letoh32(rsp.status));
20346e30e88dSdamien 		for (i = 0; i < 2; i++) {	/* 2 Rx chains */
20356e30e88dSdamien 			/* Sign-extend 9-bit NF values. */
20366e30e88dSdamien 			printf("noisefloor chain %d=%d\n", i,
20376e30e88dSdamien 			    (((int32_t)letoh32(rsp.nf[i])) << 4) >> 23);
20386e30e88dSdamien 			printf("noisefloor ext chain %d=%d\n", i,
20396e30e88dSdamien 			    ((int32_t)letoh32(rsp.nf_ext[i])) >> 23);
20406e30e88dSdamien 		}
20416e30e88dSdamien 	}
20426e30e88dSdamien #endif
204368baf11cSdamien 	sc->sc_curchan = c;
204468baf11cSdamien 	return 0;
204568baf11cSdamien }
204668baf11cSdamien 
204768baf11cSdamien #ifdef notyet
204868baf11cSdamien int
204968baf11cSdamien otus_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
205068baf11cSdamien     struct ieee80211_key *k)
205168baf11cSdamien {
205268baf11cSdamien 	struct otus_softc *sc = ic->ic_softc;
205368baf11cSdamien 	struct otus_cmd_key cmd;
205468baf11cSdamien 
20550760dad6Sdamien 	/* Defer setting of WEP keys until interface is brought up. */
20560760dad6Sdamien 	if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) !=
20570760dad6Sdamien 	    (IFF_UP | IFF_RUNNING))
20580760dad6Sdamien 		return 0;
20590760dad6Sdamien 
206068baf11cSdamien 	/* Do it in a process context. */
206168baf11cSdamien 	cmd.key = *k;
206268baf11cSdamien 	cmd.associd = (ni != NULL) ? ni->ni_associd : 0;
206368baf11cSdamien 	otus_do_async(sc, otus_set_key_cb, &cmd, sizeof cmd);
206468baf11cSdamien 	return 0;
206568baf11cSdamien }
206668baf11cSdamien 
206768baf11cSdamien void
206868baf11cSdamien otus_set_key_cb(struct otus_softc *sc, void *arg)
206968baf11cSdamien {
207068baf11cSdamien 	struct otus_cmd_key *cmd = arg;
207168baf11cSdamien 	struct ieee80211_key *k = &cmd->key;
207268baf11cSdamien 	struct ar_cmd_ekey key;
207368baf11cSdamien 	uint16_t cipher;
207468baf11cSdamien 	int error;
207568baf11cSdamien 
207668baf11cSdamien 	memset(&key, 0, sizeof key);
207768baf11cSdamien 	if (k->k_flags & IEEE80211_KEY_GROUP) {
207868baf11cSdamien 		key.uid = htole16(k->k_id);
207968baf11cSdamien 		IEEE80211_ADDR_COPY(key.macaddr, sc->sc_ic.ic_myaddr);
20804351e075Sdamien 		key.macaddr[0] |= 0x80;
208168baf11cSdamien 	} else {
208268baf11cSdamien 		key.uid = htole16(OTUS_UID(cmd->associd));
208368baf11cSdamien 		IEEE80211_ADDR_COPY(key.macaddr, ni->ni_macaddr);
208468baf11cSdamien 	}
20854351e075Sdamien 	key.kix = htole16(0);
208668baf11cSdamien 	/* Map net80211 cipher to hardware. */
208768baf11cSdamien 	switch (k->k_cipher) {
208868baf11cSdamien 	case IEEE80211_CIPHER_WEP40:
208968baf11cSdamien 		cipher = AR_CIPHER_WEP64;
209068baf11cSdamien 		break;
209168baf11cSdamien 	case IEEE80211_CIPHER_WEP104:
209268baf11cSdamien 		cipher = AR_CIPHER_WEP128;
209368baf11cSdamien 		break;
209468baf11cSdamien 	case IEEE80211_CIPHER_TKIP:
209568baf11cSdamien 		cipher = AR_CIPHER_TKIP;
209668baf11cSdamien 		break;
209768baf11cSdamien 	case IEEE80211_CIPHER_CCMP:
209868baf11cSdamien 		cipher = AR_CIPHER_AES;
209968baf11cSdamien 		break;
210068baf11cSdamien 	default:
210168baf11cSdamien 		return;
210268baf11cSdamien 	}
210368baf11cSdamien 	key.cipher = htole16(cipher);
210468baf11cSdamien 	memcpy(key.key, k->k_key, MIN(k->k_len, 16));
210568baf11cSdamien 	error = otus_cmd(sc, AR_CMD_EKEY, &key, sizeof key, NULL);
210668baf11cSdamien 	if (error != 0 || k->k_cipher != IEEE80211_CIPHER_TKIP)
210768baf11cSdamien 		return;
210868baf11cSdamien 
210968baf11cSdamien 	/* TKIP: set Tx/Rx MIC Key. */
21104351e075Sdamien 	key.kix = htole16(1);
211168baf11cSdamien 	memcpy(key.key, k->k_key + 16, 16);
211268baf11cSdamien 	(void)otus_cmd(sc, AR_CMD_EKEY, &key, sizeof key, NULL);
211368baf11cSdamien }
211468baf11cSdamien 
211568baf11cSdamien void
211668baf11cSdamien otus_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
211768baf11cSdamien     struct ieee80211_key *k)
211868baf11cSdamien {
211968baf11cSdamien 	struct otus_softc *sc = ic->ic_softc;
212068baf11cSdamien 	struct otus_cmd_key cmd;
212168baf11cSdamien 
212268baf11cSdamien 	if (!(ic->ic_if.if_flags & IFF_RUNNING) ||
212368baf11cSdamien 	    ic->ic_state != IEEE80211_S_RUN)
212468baf11cSdamien 		return;	/* Nothing to do. */
212568baf11cSdamien 
212668baf11cSdamien 	/* Do it in a process context. */
212768baf11cSdamien 	cmd.key = *k;
212868baf11cSdamien 	cmd.associd = (ni != NULL) ? ni->ni_associd : 0;
212968baf11cSdamien 	otus_do_async(sc, otus_delete_key_cb, &cmd, sizeof cmd);
213068baf11cSdamien }
213168baf11cSdamien 
213268baf11cSdamien void
213368baf11cSdamien otus_delete_key_cb(struct otus_softc *sc, void *arg)
213468baf11cSdamien {
213568baf11cSdamien 	struct otus_cmd_key *cmd = arg;
213668baf11cSdamien 	struct ieee80211_key *k = &cmd->key;
213768baf11cSdamien 	uint32_t uid;
213868baf11cSdamien 
213968baf11cSdamien 	if (k->k_flags & IEEE80211_KEY_GROUP)
214068baf11cSdamien 		uid = htole32(k->k_id);
214168baf11cSdamien 	else
214268baf11cSdamien 		uid = htole32(OTUS_UID(cmd->associd));
214368baf11cSdamien 	(void)otus_cmd(sc, AR_CMD_DKEY, &uid, sizeof uid, NULL);
214468baf11cSdamien }
214568baf11cSdamien #endif
214668baf11cSdamien 
214768baf11cSdamien void
214868baf11cSdamien otus_calibrate_to(void *arg)
214968baf11cSdamien {
215068baf11cSdamien 	struct otus_softc *sc = arg;
215168baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
215268baf11cSdamien 	struct ieee80211_node *ni;
215368baf11cSdamien 	int s;
215468baf11cSdamien 
215568baf11cSdamien 	s = splnet();
215668baf11cSdamien 	ni = ic->ic_bss;
215768baf11cSdamien 	ieee80211_amrr_choose(&sc->amrr, ni, &((struct otus_node *)ni)->amn);
215868baf11cSdamien 	splx(s);
215968baf11cSdamien 
216068baf11cSdamien 	timeout_add_sec(&sc->calib_to, 1);
216168baf11cSdamien }
216268baf11cSdamien 
216368baf11cSdamien int
216468baf11cSdamien otus_set_bssid(struct otus_softc *sc, const uint8_t *bssid)
216568baf11cSdamien {
21666e30e88dSdamien 	otus_write(sc, AR_MAC_REG_BSSID_L,
216768baf11cSdamien 	    bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24);
21686e30e88dSdamien 	otus_write(sc, AR_MAC_REG_BSSID_H,
216968baf11cSdamien 	    bssid[4] | bssid[5] << 8);
217068baf11cSdamien 	return otus_write_barrier(sc);
217168baf11cSdamien }
217268baf11cSdamien 
217368baf11cSdamien int
217468baf11cSdamien otus_set_macaddr(struct otus_softc *sc, const uint8_t *addr)
217568baf11cSdamien {
217668baf11cSdamien 	otus_write(sc, AR_MAC_REG_MAC_ADDR_L,
217768baf11cSdamien 	    addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
217868baf11cSdamien 	otus_write(sc, AR_MAC_REG_MAC_ADDR_H,
217968baf11cSdamien 	    addr[4] | addr[5] << 8);
218068baf11cSdamien 	return otus_write_barrier(sc);
218168baf11cSdamien }
218268baf11cSdamien 
218368baf11cSdamien /* Default single-LED. */
218468baf11cSdamien void
218568baf11cSdamien otus_led_newstate_type1(struct otus_softc *sc)
218668baf11cSdamien {
218768baf11cSdamien 	/* TBD */
218868baf11cSdamien }
218968baf11cSdamien 
219068baf11cSdamien /* NETGEAR, dual-LED. */
219168baf11cSdamien void
219268baf11cSdamien otus_led_newstate_type2(struct otus_softc *sc)
219368baf11cSdamien {
219468baf11cSdamien 	/* TBD */
219568baf11cSdamien }
219668baf11cSdamien 
219768baf11cSdamien /* NETGEAR, single-LED/3 colors (blue, red, purple.) */
219868baf11cSdamien void
219968baf11cSdamien otus_led_newstate_type3(struct otus_softc *sc)
220068baf11cSdamien {
220168baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
220268baf11cSdamien 	uint32_t state = sc->led_state;
220368baf11cSdamien 
220468baf11cSdamien 	if (ic->ic_state == IEEE80211_S_INIT) {
220568baf11cSdamien 		state = 0;	/* LED off. */
220668baf11cSdamien 	} else if (ic->ic_state == IEEE80211_S_RUN) {
220768baf11cSdamien 		/* Associated, LED always on. */
220868baf11cSdamien 		if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan))
220968baf11cSdamien 			state = AR_LED0_ON;	/* 2GHz=>Red. */
221068baf11cSdamien 		else
221168baf11cSdamien 			state = AR_LED1_ON;	/* 5GHz=>Blue. */
221268baf11cSdamien 	} else {
221368baf11cSdamien 		/* Scanning, blink LED. */
221468baf11cSdamien 		state ^= AR_LED0_ON | AR_LED1_ON;
221568baf11cSdamien 		if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan))
221668baf11cSdamien 			state &= ~AR_LED1_ON;
221768baf11cSdamien 		else
221868baf11cSdamien 			state &= ~AR_LED0_ON;
221968baf11cSdamien 	}
222068baf11cSdamien 	if (state != sc->led_state) {
222168baf11cSdamien 		otus_write(sc, 0x1d0104, state);
222268baf11cSdamien 		if (otus_write_barrier(sc) == 0)
222368baf11cSdamien 			sc->led_state = state;
222468baf11cSdamien 	}
222568baf11cSdamien }
222668baf11cSdamien 
222768baf11cSdamien int
222868baf11cSdamien otus_init(struct ifnet *ifp)
222968baf11cSdamien {
223068baf11cSdamien 	struct otus_softc *sc = ifp->if_softc;
223168baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
223268baf11cSdamien 	int error;
223368baf11cSdamien 
223468baf11cSdamien 	/* Init host command ring. */
223568baf11cSdamien 	sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0;
223668baf11cSdamien 
223768baf11cSdamien 	if ((error = otus_init_mac(sc)) != 0) {
223868baf11cSdamien 		printf("%s: could not initialize MAC\n", sc->sc_dev.dv_xname);
223968baf11cSdamien 		return error;
224068baf11cSdamien 	}
224168baf11cSdamien 
224268baf11cSdamien 	IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
224368baf11cSdamien 	(void)otus_set_macaddr(sc, ic->ic_myaddr);
224468baf11cSdamien 
224568baf11cSdamien 	switch (ic->ic_opmode) {
224668baf11cSdamien #ifdef notyet
224768baf11cSdamien #ifndef IEEE80211_STA_ONLY
224868baf11cSdamien 	case IEEE80211_M_HOSTAP:
224968baf11cSdamien 		otus_write(sc, 0x1c3700, 0x0f0000a1);
225068baf11cSdamien 		otus_write(sc, 0x1c3c40, 0x1);
225168baf11cSdamien 		break;
225268baf11cSdamien 	case IEEE80211_M_IBSS:
225368baf11cSdamien 		otus_write(sc, 0x1c3700, 0x0f000000);
225468baf11cSdamien 		otus_write(sc, 0x1c3c40, 0x1);
225568baf11cSdamien 		break;
225668baf11cSdamien #endif
225768baf11cSdamien #endif
225868baf11cSdamien 	case IEEE80211_M_STA:
225968baf11cSdamien 		otus_write(sc, 0x1c3700, 0x0f000002);
226068baf11cSdamien 		otus_write(sc, 0x1c3c40, 0x1);
226168baf11cSdamien 		break;
226268baf11cSdamien 	default:
226368baf11cSdamien 		break;
226468baf11cSdamien 	}
226568baf11cSdamien 	otus_write(sc, AR_MAC_REG_SNIFFER,
226668baf11cSdamien 	    (ic->ic_opmode == IEEE80211_M_MONITOR) ? 0x2000001 : 0x2000000);
226768baf11cSdamien 	(void)otus_write_barrier(sc);
226868baf11cSdamien 
22696e30e88dSdamien 	sc->bb_reset = 1;	/* Force cold reset. */
227068baf11cSdamien 	ic->ic_bss->ni_chan = ic->ic_ibss_chan;
22716e30e88dSdamien 	if ((error = otus_set_chan(sc, ic->ic_ibss_chan, 0)) != 0) {
227268baf11cSdamien 		printf("%s: could not set channel\n", sc->sc_dev.dv_xname);
227368baf11cSdamien 		return error;
227468baf11cSdamien 	}
227568baf11cSdamien 
227668baf11cSdamien 	/* Start Rx. */
227768baf11cSdamien 	otus_write(sc, 0x1c3d30, 0x100);
2278d7d7f774Sdamien 	(void)otus_write_barrier(sc);
227968baf11cSdamien 
228068baf11cSdamien 	ifp->if_flags &= ~IFF_OACTIVE;
228168baf11cSdamien 	ifp->if_flags |= IFF_RUNNING;
228268baf11cSdamien 
228368baf11cSdamien 	if (ic->ic_opmode == IEEE80211_M_MONITOR)
228468baf11cSdamien 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
228568baf11cSdamien 	else
228668baf11cSdamien 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
228768baf11cSdamien 
228868baf11cSdamien 	return 0;
228968baf11cSdamien }
229068baf11cSdamien 
229168baf11cSdamien void
229268baf11cSdamien otus_stop(struct ifnet *ifp)
229368baf11cSdamien {
229468baf11cSdamien 	struct otus_softc *sc = ifp->if_softc;
229568baf11cSdamien 	struct ieee80211com *ic = &sc->sc_ic;
229668baf11cSdamien 	int s;
229768baf11cSdamien 
229868baf11cSdamien 	sc->sc_tx_timer = 0;
229968baf11cSdamien 	ifp->if_timer = 0;
230068baf11cSdamien 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
230168baf11cSdamien 
230268baf11cSdamien 	timeout_del(&sc->scan_to);
230368baf11cSdamien 	timeout_del(&sc->calib_to);
230468baf11cSdamien 
230568baf11cSdamien 	s = splusb();
230668baf11cSdamien 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
230768baf11cSdamien 	/* Wait for all queued asynchronous commands to complete. */
230868baf11cSdamien 	while (sc->cmdq.queued > 0)
230968baf11cSdamien 		tsleep(&sc->cmdq, 0, "cmdq", 0);
231068baf11cSdamien 	splx(s);
231168baf11cSdamien 
231268baf11cSdamien 	/* Stop Rx. */
231368baf11cSdamien 	otus_write(sc, 0x1c3d30, 0);
231468baf11cSdamien 	(void)otus_write_barrier(sc);
231568baf11cSdamien 
231668baf11cSdamien 	sc->tx_queued = 0;
231768baf11cSdamien }
2318