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