1069f1a80SAndrew Thompson /*-
23707a5e9SAndrew Thompson * Copyright (c) 2008,2010 Damien Bergamini <damien.bergamini@free.fr>
3069f1a80SAndrew Thompson * ported to FreeBSD by Akinori Furukoshi <moonlightakkiy@yahoo.ca>
43707a5e9SAndrew Thompson * USB Consulting, Hans Petter Selasky <hselasky@freebsd.org>
57a7e01caSKevin Lo * Copyright (c) 2013-2014 Kevin Lo
6069f1a80SAndrew Thompson *
7069f1a80SAndrew Thompson * Permission to use, copy, modify, and distribute this software for any
8069f1a80SAndrew Thompson * purpose with or without fee is hereby granted, provided that the above
9069f1a80SAndrew Thompson * copyright notice and this permission notice appear in all copies.
10069f1a80SAndrew Thompson *
11069f1a80SAndrew Thompson * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12069f1a80SAndrew Thompson * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13069f1a80SAndrew Thompson * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14069f1a80SAndrew Thompson * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15069f1a80SAndrew Thompson * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16069f1a80SAndrew Thompson * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17069f1a80SAndrew Thompson * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18069f1a80SAndrew Thompson */
19069f1a80SAndrew Thompson
20069f1a80SAndrew Thompson /*-
2164891211SKevin Lo * Ralink Technology RT2700U/RT2800U/RT3000U/RT3900E chipset driver.
22069f1a80SAndrew Thompson * http://www.ralinktech.com/
23069f1a80SAndrew Thompson */
24069f1a80SAndrew Thompson
2563a55eabSAndriy Voskoboinyk #include "opt_wlan.h"
2663a55eabSAndriy Voskoboinyk
27069f1a80SAndrew Thompson #include <sys/param.h>
28e2e050c8SConrad Meyer #include <sys/eventhandler.h>
29069f1a80SAndrew Thompson #include <sys/sockio.h>
30069f1a80SAndrew Thompson #include <sys/sysctl.h>
31069f1a80SAndrew Thompson #include <sys/lock.h>
32069f1a80SAndrew Thompson #include <sys/mutex.h>
33069f1a80SAndrew Thompson #include <sys/mbuf.h>
34069f1a80SAndrew Thompson #include <sys/kernel.h>
35069f1a80SAndrew Thompson #include <sys/socket.h>
36069f1a80SAndrew Thompson #include <sys/systm.h>
37069f1a80SAndrew Thompson #include <sys/malloc.h>
38069f1a80SAndrew Thompson #include <sys/module.h>
39069f1a80SAndrew Thompson #include <sys/bus.h>
40069f1a80SAndrew Thompson #include <sys/endian.h>
41069f1a80SAndrew Thompson #include <sys/linker.h>
42069f1a80SAndrew Thompson #include <sys/firmware.h>
43069f1a80SAndrew Thompson #include <sys/kdb.h>
44069f1a80SAndrew Thompson
45069f1a80SAndrew Thompson #include <net/bpf.h>
46069f1a80SAndrew Thompson #include <net/if.h>
4776039bc8SGleb Smirnoff #include <net/if_var.h>
48069f1a80SAndrew Thompson #include <net/if_arp.h>
49069f1a80SAndrew Thompson #include <net/ethernet.h>
50069f1a80SAndrew Thompson #include <net/if_dl.h>
51069f1a80SAndrew Thompson #include <net/if_media.h>
52069f1a80SAndrew Thompson #include <net/if_types.h>
53069f1a80SAndrew Thompson
54069f1a80SAndrew Thompson #include <netinet/in.h>
55069f1a80SAndrew Thompson #include <netinet/in_systm.h>
56069f1a80SAndrew Thompson #include <netinet/in_var.h>
57069f1a80SAndrew Thompson #include <netinet/if_ether.h>
58069f1a80SAndrew Thompson #include <netinet/ip.h>
59069f1a80SAndrew Thompson
60069f1a80SAndrew Thompson #include <net80211/ieee80211_var.h>
61069f1a80SAndrew Thompson #include <net80211/ieee80211_regdomain.h>
62069f1a80SAndrew Thompson #include <net80211/ieee80211_radiotap.h>
63b6108616SRui Paulo #include <net80211/ieee80211_ratectl.h>
64f520d761SAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG
65f520d761SAdrian Chadd #include <net80211/ieee80211_superg.h>
66f520d761SAdrian Chadd #endif
67069f1a80SAndrew Thompson
68069f1a80SAndrew Thompson #include <dev/usb/usb.h>
69069f1a80SAndrew Thompson #include <dev/usb/usbdi.h>
70069f1a80SAndrew Thompson #include "usbdevs.h"
71069f1a80SAndrew Thompson
72069f1a80SAndrew Thompson #define USB_DEBUG_VAR run_debug
73069f1a80SAndrew Thompson #include <dev/usb/usb_debug.h>
747dc4b90eSKevin Lo #include <dev/usb/usb_msctest.h>
75069f1a80SAndrew Thompson
76329f91ffSKevin Lo #include <dev/usb/wlan/if_runreg.h>
77329f91ffSKevin Lo #include <dev/usb/wlan/if_runvar.h>
78069f1a80SAndrew Thompson
79b850ecc1SAndrew Thompson #ifdef USB_DEBUG
80069f1a80SAndrew Thompson #define RUN_DEBUG
81069f1a80SAndrew Thompson #endif
82069f1a80SAndrew Thompson
83069f1a80SAndrew Thompson #ifdef RUN_DEBUG
84069f1a80SAndrew Thompson int run_debug = 0;
85f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, run, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
86f8d2b1f3SPawel Biernacki "USB run");
87ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RWTUN, &run_debug, 0,
88069f1a80SAndrew Thompson "run debug level");
89109005ccSGavin Atkinson
90109005ccSGavin Atkinson enum {
91109005ccSGavin Atkinson RUN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
92109005ccSGavin Atkinson RUN_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */
93109005ccSGavin Atkinson RUN_DEBUG_RECV = 0x00000004, /* basic recv operation */
94109005ccSGavin Atkinson RUN_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */
95109005ccSGavin Atkinson RUN_DEBUG_STATE = 0x00000010, /* 802.11 state transitions */
96109005ccSGavin Atkinson RUN_DEBUG_RATE = 0x00000020, /* rate adaptation */
97109005ccSGavin Atkinson RUN_DEBUG_USB = 0x00000040, /* usb requests */
98109005ccSGavin Atkinson RUN_DEBUG_FIRMWARE = 0x00000080, /* firmware(9) loading debug */
99109005ccSGavin Atkinson RUN_DEBUG_BEACON = 0x00000100, /* beacon handling */
100109005ccSGavin Atkinson RUN_DEBUG_INTR = 0x00000200, /* ISR */
101109005ccSGavin Atkinson RUN_DEBUG_TEMP = 0x00000400, /* temperature calibration */
102109005ccSGavin Atkinson RUN_DEBUG_ROM = 0x00000800, /* various ROM info */
103109005ccSGavin Atkinson RUN_DEBUG_KEY = 0x00001000, /* crypto keys management */
104109005ccSGavin Atkinson RUN_DEBUG_TXPWR = 0x00002000, /* dump Tx power values */
105109005ccSGavin Atkinson RUN_DEBUG_RSSI = 0x00004000, /* dump RSSI lookups */
106109005ccSGavin Atkinson RUN_DEBUG_RESET = 0x00008000, /* initialization progress */
107109005ccSGavin Atkinson RUN_DEBUG_CALIB = 0x00010000, /* calibration progress */
1083656b961SGavin Atkinson RUN_DEBUG_CMD = 0x00020000, /* command queue */
109109005ccSGavin Atkinson RUN_DEBUG_ANY = 0xffffffff
110109005ccSGavin Atkinson };
111109005ccSGavin Atkinson
112109005ccSGavin Atkinson #define RUN_DPRINTF(_sc, _m, ...) do { \
113109005ccSGavin Atkinson if (run_debug & (_m)) \
114109005ccSGavin Atkinson device_printf((_sc)->sc_dev, __VA_ARGS__); \
115109005ccSGavin Atkinson } while(0)
116109005ccSGavin Atkinson #else
117109005ccSGavin Atkinson #define RUN_DPRINTF(_sc, _m, ...) do { (void) _sc; } while (0)
118069f1a80SAndrew Thompson #endif
119069f1a80SAndrew Thompson
120de7eb46eSKevin Lo #define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh)
121069f1a80SAndrew Thompson
12285e7bb81SAndrew Thompson /*
12385e7bb81SAndrew Thompson * Because of LOR in run_key_delete(), use atomic instead.
12485e7bb81SAndrew Thompson * '& RUN_CMDQ_MASQ' is to loop cmdq[].
12585e7bb81SAndrew Thompson */
12685e7bb81SAndrew Thompson #define RUN_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & RUN_CMDQ_MASQ)
12785e7bb81SAndrew Thompson
128f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID run_devs[] = {
1296bce5ae1SAndrew Thompson #define RUN_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
1307dc4b90eSKevin Lo #define RUN_DEV_EJECT(v,p) \
1315c5e99d2SKevin Lo { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RUN_EJECT) }
1325c5e99d2SKevin Lo #define RUN_EJECT 1
1336bce5ae1SAndrew Thompson RUN_DEV(ABOCOM, RT2770),
1346bce5ae1SAndrew Thompson RUN_DEV(ABOCOM, RT2870),
1356bce5ae1SAndrew Thompson RUN_DEV(ABOCOM, RT3070),
1366bce5ae1SAndrew Thompson RUN_DEV(ABOCOM, RT3071),
1376bce5ae1SAndrew Thompson RUN_DEV(ABOCOM, RT3072),
1386bce5ae1SAndrew Thompson RUN_DEV(ABOCOM2, RT2870_1),
1396bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT2770),
1406bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT2870_1),
1416bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT2870_2),
1426bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT2870_3),
1436bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT2870_4),
1446bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT2870_5),
1456bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT3070),
1466bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT3070_1),
1476bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT3070_2),
1486bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT3070_3),
1496bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT3070_4),
1506bce5ae1SAndrew Thompson RUN_DEV(ACCTON, RT3070_5),
1516bce5ae1SAndrew Thompson RUN_DEV(AIRTIES, RT3070),
1526bce5ae1SAndrew Thompson RUN_DEV(ALLWIN, RT2070),
1536bce5ae1SAndrew Thompson RUN_DEV(ALLWIN, RT2770),
1546bce5ae1SAndrew Thompson RUN_DEV(ALLWIN, RT2870),
1556bce5ae1SAndrew Thompson RUN_DEV(ALLWIN, RT3070),
1566bce5ae1SAndrew Thompson RUN_DEV(ALLWIN, RT3071),
1576bce5ae1SAndrew Thompson RUN_DEV(ALLWIN, RT3072),
1586bce5ae1SAndrew Thompson RUN_DEV(ALLWIN, RT3572),
1596bce5ae1SAndrew Thompson RUN_DEV(AMIGO, RT2870_1),
1606bce5ae1SAndrew Thompson RUN_DEV(AMIGO, RT2870_2),
1616bce5ae1SAndrew Thompson RUN_DEV(AMIT, CGWLUSB2GNR),
1626bce5ae1SAndrew Thompson RUN_DEV(AMIT, RT2870_1),
1636bce5ae1SAndrew Thompson RUN_DEV(AMIT2, RT2870),
1646bce5ae1SAndrew Thompson RUN_DEV(ASUS, RT2870_1),
1656bce5ae1SAndrew Thompson RUN_DEV(ASUS, RT2870_2),
1666bce5ae1SAndrew Thompson RUN_DEV(ASUS, RT2870_3),
1676bce5ae1SAndrew Thompson RUN_DEV(ASUS, RT2870_4),
1686bce5ae1SAndrew Thompson RUN_DEV(ASUS, RT2870_5),
1696bce5ae1SAndrew Thompson RUN_DEV(ASUS, USBN13),
1706bce5ae1SAndrew Thompson RUN_DEV(ASUS, RT3070_1),
1717a7e01caSKevin Lo RUN_DEV(ASUS, USBN66),
172419ce5a6SHans Petter Selasky RUN_DEV(ASUS, USB_N53),
173d6fd321eSDmitry Chagin RUN_DEV(ASUS, USBN14),
1746bce5ae1SAndrew Thompson RUN_DEV(ASUS2, USBN11),
1756bce5ae1SAndrew Thompson RUN_DEV(AZUREWAVE, RT2870_1),
1766bce5ae1SAndrew Thompson RUN_DEV(AZUREWAVE, RT2870_2),
1776bce5ae1SAndrew Thompson RUN_DEV(AZUREWAVE, RT3070_1),
1786bce5ae1SAndrew Thompson RUN_DEV(AZUREWAVE, RT3070_2),
1796bce5ae1SAndrew Thompson RUN_DEV(AZUREWAVE, RT3070_3),
1807a7e01caSKevin Lo RUN_DEV(BELKIN, F9L1103),
1816bce5ae1SAndrew Thompson RUN_DEV(BELKIN, F5D8053V3),
1826bce5ae1SAndrew Thompson RUN_DEV(BELKIN, F5D8055),
183c8eeb971SHans Petter Selasky RUN_DEV(BELKIN, F5D8055V2),
1846bce5ae1SAndrew Thompson RUN_DEV(BELKIN, F6D4050V1),
18557733723SHans Petter Selasky RUN_DEV(BELKIN, F6D4050V2),
1866bce5ae1SAndrew Thompson RUN_DEV(BELKIN, RT2870_1),
1876bce5ae1SAndrew Thompson RUN_DEV(BELKIN, RT2870_2),
188c8eeb971SHans Petter Selasky RUN_DEV(CISCOLINKSYS, AE1000),
1896bce5ae1SAndrew Thompson RUN_DEV(CISCOLINKSYS2, RT3070),
1906bce5ae1SAndrew Thompson RUN_DEV(CISCOLINKSYS3, RT3070),
1916bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, RT2870_1),
1926bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, RT2870_2),
1936bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, RT2870_3),
1946bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, RT2870_4),
1956bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, RT2870_5),
1966bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, RT2870_6),
1976bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, RT2870_7),
1986bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, RT2870_8),
1996bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, RT3070_1),
2006bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, RT3070_2),
2016bce5ae1SAndrew Thompson RUN_DEV(CONCEPTRONIC2, VIGORN61),
2026bce5ae1SAndrew Thompson RUN_DEV(COREGA, CGWLUSB300GNM),
2036bce5ae1SAndrew Thompson RUN_DEV(COREGA, RT2870_1),
2046bce5ae1SAndrew Thompson RUN_DEV(COREGA, RT2870_2),
2056bce5ae1SAndrew Thompson RUN_DEV(COREGA, RT2870_3),
2066bce5ae1SAndrew Thompson RUN_DEV(COREGA, RT3070),
2076bce5ae1SAndrew Thompson RUN_DEV(CYBERTAN, RT2870),
2086bce5ae1SAndrew Thompson RUN_DEV(DLINK, RT2870),
2096bce5ae1SAndrew Thompson RUN_DEV(DLINK, RT3072),
21076cffa7aSGavin Atkinson RUN_DEV(DLINK, DWA125A3),
211c7e4729dSRuslan Bukin RUN_DEV(DLINK, DWA127),
21264891211SKevin Lo RUN_DEV(DLINK, DWA140B3),
213242dbae3SKevin Lo RUN_DEV(DLINK, DWA160B2),
214a080f715SKevin Lo RUN_DEV(DLINK, DWA140D1),
2158746bc91SDmitry Chagin RUN_DEV(DLINK, DWA130F1),
2167a7e01caSKevin Lo RUN_DEV(DLINK, DWA162),
2176bce5ae1SAndrew Thompson RUN_DEV(DLINK2, DWA130),
2186bce5ae1SAndrew Thompson RUN_DEV(DLINK2, RT2870_1),
2196bce5ae1SAndrew Thompson RUN_DEV(DLINK2, RT2870_2),
2206bce5ae1SAndrew Thompson RUN_DEV(DLINK2, RT3070_1),
2216bce5ae1SAndrew Thompson RUN_DEV(DLINK2, RT3070_2),
2226bce5ae1SAndrew Thompson RUN_DEV(DLINK2, RT3070_3),
2236bce5ae1SAndrew Thompson RUN_DEV(DLINK2, RT3070_4),
2246bce5ae1SAndrew Thompson RUN_DEV(DLINK2, RT3070_5),
2256bce5ae1SAndrew Thompson RUN_DEV(DLINK2, RT3072),
2266bce5ae1SAndrew Thompson RUN_DEV(DLINK2, RT3072_1),
2276bce5ae1SAndrew Thompson RUN_DEV(EDIMAX, EW7717),
2286bce5ae1SAndrew Thompson RUN_DEV(EDIMAX, EW7718),
2297a7e01caSKevin Lo RUN_DEV(EDIMAX, EW7733UND),
2306bce5ae1SAndrew Thompson RUN_DEV(EDIMAX, RT2870_1),
2316bce5ae1SAndrew Thompson RUN_DEV(ENCORE, RT3070_1),
2326bce5ae1SAndrew Thompson RUN_DEV(ENCORE, RT3070_2),
2336bce5ae1SAndrew Thompson RUN_DEV(ENCORE, RT3070_3),
2346bce5ae1SAndrew Thompson RUN_DEV(GIGABYTE, GNWB31N),
2356bce5ae1SAndrew Thompson RUN_DEV(GIGABYTE, GNWB32L),
2366bce5ae1SAndrew Thompson RUN_DEV(GIGABYTE, RT2870_1),
2376bce5ae1SAndrew Thompson RUN_DEV(GIGASET, RT3070_1),
2386bce5ae1SAndrew Thompson RUN_DEV(GIGASET, RT3070_2),
2396bce5ae1SAndrew Thompson RUN_DEV(GUILLEMOT, HWNU300),
2406bce5ae1SAndrew Thompson RUN_DEV(HAWKING, HWUN2),
2416bce5ae1SAndrew Thompson RUN_DEV(HAWKING, RT2870_1),
2426bce5ae1SAndrew Thompson RUN_DEV(HAWKING, RT2870_2),
2436bce5ae1SAndrew Thompson RUN_DEV(HAWKING, RT3070),
2446bce5ae1SAndrew Thompson RUN_DEV(IODATA, RT3072_1),
2456bce5ae1SAndrew Thompson RUN_DEV(IODATA, RT3072_2),
2466bce5ae1SAndrew Thompson RUN_DEV(IODATA, RT3072_3),
2476bce5ae1SAndrew Thompson RUN_DEV(IODATA, RT3072_4),
2486bce5ae1SAndrew Thompson RUN_DEV(LINKSYS4, RT3070),
2496bce5ae1SAndrew Thompson RUN_DEV(LINKSYS4, WUSB100),
2506bce5ae1SAndrew Thompson RUN_DEV(LINKSYS4, WUSB54GCV3),
2516bce5ae1SAndrew Thompson RUN_DEV(LINKSYS4, WUSB600N),
2526bce5ae1SAndrew Thompson RUN_DEV(LINKSYS4, WUSB600NV2),
2536bce5ae1SAndrew Thompson RUN_DEV(LOGITEC, RT2870_1),
2546bce5ae1SAndrew Thompson RUN_DEV(LOGITEC, RT2870_2),
2556bce5ae1SAndrew Thompson RUN_DEV(LOGITEC, RT2870_3),
2566f0e06a1SHans Petter Selasky RUN_DEV(LOGITEC, LANW300NU2),
257be503393SHiroki Sato RUN_DEV(LOGITEC, LANW150NU2),
258bd247e9dSHans Petter Selasky RUN_DEV(LOGITEC, LANW300NU2S),
2598aa853d5SKevin Lo RUN_DEV(MELCO, WLIUCG300HP),
2606bce5ae1SAndrew Thompson RUN_DEV(MELCO, RT2870_2),
2616bce5ae1SAndrew Thompson RUN_DEV(MELCO, WLIUCAG300N),
2626bce5ae1SAndrew Thompson RUN_DEV(MELCO, WLIUCG300N),
263f1168f99SDaichi GOTO RUN_DEV(MELCO, WLIUCG301N),
2646bce5ae1SAndrew Thompson RUN_DEV(MELCO, WLIUCGN),
2652634f5d7SHans Petter Selasky RUN_DEV(MELCO, WLIUCGNM),
2668aa853d5SKevin Lo RUN_DEV(MELCO, WLIUCG300HPV1),
267be503393SHiroki Sato RUN_DEV(MELCO, WLIUCGNM2),
2686bce5ae1SAndrew Thompson RUN_DEV(MOTOROLA4, RT2770),
2696bce5ae1SAndrew Thompson RUN_DEV(MOTOROLA4, RT3070),
2706bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_1),
2716bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_2),
2726bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_3),
2736bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_4),
2746bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_5),
2756bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_6),
2766bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_7),
2776bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_8),
2786bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_9),
2796bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_10),
2806bce5ae1SAndrew Thompson RUN_DEV(MSI, RT3070_11),
2810840f948SGavin Atkinson RUN_DEV(NETGEAR, WNDA4100),
2826bce5ae1SAndrew Thompson RUN_DEV(OVISLINK, RT3072),
2836bce5ae1SAndrew Thompson RUN_DEV(PARA, RT3070),
2846bce5ae1SAndrew Thompson RUN_DEV(PEGATRON, RT2870),
2856bce5ae1SAndrew Thompson RUN_DEV(PEGATRON, RT3070),
2866bce5ae1SAndrew Thompson RUN_DEV(PEGATRON, RT3070_2),
2876bce5ae1SAndrew Thompson RUN_DEV(PEGATRON, RT3070_3),
2886bce5ae1SAndrew Thompson RUN_DEV(PHILIPS, RT2870),
2896bce5ae1SAndrew Thompson RUN_DEV(PLANEX2, GWUS300MINIS),
2906bce5ae1SAndrew Thompson RUN_DEV(PLANEX2, GWUSMICRON),
2916bce5ae1SAndrew Thompson RUN_DEV(PLANEX2, RT2870),
2926bce5ae1SAndrew Thompson RUN_DEV(PLANEX2, RT3070),
2936bce5ae1SAndrew Thompson RUN_DEV(QCOM, RT2870),
2946bce5ae1SAndrew Thompson RUN_DEV(QUANTA, RT3070),
2956bce5ae1SAndrew Thompson RUN_DEV(RALINK, RT2070),
2966bce5ae1SAndrew Thompson RUN_DEV(RALINK, RT2770),
2976bce5ae1SAndrew Thompson RUN_DEV(RALINK, RT2870),
2986bce5ae1SAndrew Thompson RUN_DEV(RALINK, RT3070),
2996bce5ae1SAndrew Thompson RUN_DEV(RALINK, RT3071),
3006bce5ae1SAndrew Thompson RUN_DEV(RALINK, RT3072),
3016bce5ae1SAndrew Thompson RUN_DEV(RALINK, RT3370),
3026bce5ae1SAndrew Thompson RUN_DEV(RALINK, RT3572),
3037a7e01caSKevin Lo RUN_DEV(RALINK, RT3573),
30464891211SKevin Lo RUN_DEV(RALINK, RT5370),
3052c6d9edbSBen Widawsky RUN_DEV(RALINK, RT5372),
306242dbae3SKevin Lo RUN_DEV(RALINK, RT5572),
3076bce5ae1SAndrew Thompson RUN_DEV(RALINK, RT8070),
308c8eeb971SHans Petter Selasky RUN_DEV(SAMSUNG, WIS09ABGN),
3096bce5ae1SAndrew Thompson RUN_DEV(SAMSUNG2, RT2870_1),
3106bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT2870_1),
3116bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT2870_2),
3126bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT2870_3),
3136bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT2870_4),
3146bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT3070),
3156bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT3071),
3166bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT3072_1),
3176bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT3072_2),
3186bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT3072_3),
3196bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT3072_4),
3206bce5ae1SAndrew Thompson RUN_DEV(SENAO, RT3072_5),
3216bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT2770),
3226bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT2870_1),
3236bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT2870_2),
3246bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT2870_3),
3256bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT2870_4),
3266bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3070),
327*426f4e5eSNia Alarie RUN_DEV(SITECOMEU, RT3070_1),
3286bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3070_2),
3296bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3070_3),
3306bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3070_4),
3316bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3071),
3326bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3072_1),
3336bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3072_2),
3346bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3072_3),
3356bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3072_4),
3366bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3072_5),
3376bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, RT3072_6),
3386bce5ae1SAndrew Thompson RUN_DEV(SITECOMEU, WL608),
3396bce5ae1SAndrew Thompson RUN_DEV(SPARKLAN, RT2870_1),
3406bce5ae1SAndrew Thompson RUN_DEV(SPARKLAN, RT3070),
3416bce5ae1SAndrew Thompson RUN_DEV(SWEEX2, LW153),
3426bce5ae1SAndrew Thompson RUN_DEV(SWEEX2, LW303),
3436bce5ae1SAndrew Thompson RUN_DEV(SWEEX2, LW313),
3446bce5ae1SAndrew Thompson RUN_DEV(TOSHIBA, RT3070),
3456bce5ae1SAndrew Thompson RUN_DEV(UMEDIA, RT2870_1),
3466bce5ae1SAndrew Thompson RUN_DEV(ZCOM, RT2870_1),
3476bce5ae1SAndrew Thompson RUN_DEV(ZCOM, RT2870_2),
3486bce5ae1SAndrew Thompson RUN_DEV(ZINWELL, RT2870_1),
3496bce5ae1SAndrew Thompson RUN_DEV(ZINWELL, RT2870_2),
3506bce5ae1SAndrew Thompson RUN_DEV(ZINWELL, RT3070),
3516bce5ae1SAndrew Thompson RUN_DEV(ZINWELL, RT3072_1),
3526bce5ae1SAndrew Thompson RUN_DEV(ZINWELL, RT3072_2),
3536bce5ae1SAndrew Thompson RUN_DEV(ZYXEL, RT2870_1),
3546bce5ae1SAndrew Thompson RUN_DEV(ZYXEL, RT2870_2),
3554ebc1ddfSHans Petter Selasky RUN_DEV(ZYXEL, RT3070),
3565c5e99d2SKevin Lo RUN_DEV_EJECT(ZYXEL, NWD2705),
3577dc4b90eSKevin Lo RUN_DEV_EJECT(RALINK, RT_STOR),
3587dc4b90eSKevin Lo #undef RUN_DEV_EJECT
3596bce5ae1SAndrew Thompson #undef RUN_DEV
360069f1a80SAndrew Thompson };
361069f1a80SAndrew Thompson
362069f1a80SAndrew Thompson static device_probe_t run_match;
363069f1a80SAndrew Thompson static device_attach_t run_attach;
364069f1a80SAndrew Thompson static device_detach_t run_detach;
365069f1a80SAndrew Thompson
366069f1a80SAndrew Thompson static usb_callback_t run_bulk_rx_callback;
367069f1a80SAndrew Thompson static usb_callback_t run_bulk_tx_callback0;
368069f1a80SAndrew Thompson static usb_callback_t run_bulk_tx_callback1;
369069f1a80SAndrew Thompson static usb_callback_t run_bulk_tx_callback2;
370069f1a80SAndrew Thompson static usb_callback_t run_bulk_tx_callback3;
371069f1a80SAndrew Thompson static usb_callback_t run_bulk_tx_callback4;
372069f1a80SAndrew Thompson static usb_callback_t run_bulk_tx_callback5;
373069f1a80SAndrew Thompson
3747dc4b90eSKevin Lo static void run_autoinst(void *, struct usb_device *,
3757dc4b90eSKevin Lo struct usb_attach_arg *);
3767dc4b90eSKevin Lo static int run_driver_loaded(struct module *, int, void *);
377069f1a80SAndrew Thompson static void run_bulk_tx_callbackN(struct usb_xfer *xfer,
37835a24898SHans Petter Selasky usb_error_t error, u_int index);
379069f1a80SAndrew Thompson static struct ieee80211vap *run_vap_create(struct ieee80211com *,
380fcd9500fSBernhard Schmidt const char [IFNAMSIZ], int, enum ieee80211_opmode, int,
381fcd9500fSBernhard Schmidt const uint8_t [IEEE80211_ADDR_LEN],
382fcd9500fSBernhard Schmidt const uint8_t [IEEE80211_ADDR_LEN]);
383069f1a80SAndrew Thompson static void run_vap_delete(struct ieee80211vap *);
38485e7bb81SAndrew Thompson static void run_cmdq_cb(void *, int);
385069f1a80SAndrew Thompson static void run_setup_tx_list(struct run_softc *,
386069f1a80SAndrew Thompson struct run_endpoint_queue *);
387069f1a80SAndrew Thompson static void run_unsetup_tx_list(struct run_softc *,
388069f1a80SAndrew Thompson struct run_endpoint_queue *);
389069f1a80SAndrew Thompson static int run_load_microcode(struct run_softc *);
390069f1a80SAndrew Thompson static int run_reset(struct run_softc *);
391069f1a80SAndrew Thompson static usb_error_t run_do_request(struct run_softc *,
392069f1a80SAndrew Thompson struct usb_device_request *, void *);
393069f1a80SAndrew Thompson static int run_read(struct run_softc *, uint16_t, uint32_t *);
394069f1a80SAndrew Thompson static int run_read_region_1(struct run_softc *, uint16_t, uint8_t *, int);
395069f1a80SAndrew Thompson static int run_write_2(struct run_softc *, uint16_t, uint16_t);
396069f1a80SAndrew Thompson static int run_write(struct run_softc *, uint16_t, uint32_t);
397069f1a80SAndrew Thompson static int run_write_region_1(struct run_softc *, uint16_t,
398069f1a80SAndrew Thompson const uint8_t *, int);
399069f1a80SAndrew Thompson static int run_set_region_4(struct run_softc *, uint16_t, uint32_t, int);
400010b13faSKevin Lo static int run_efuse_read(struct run_softc *, uint16_t, uint16_t *, int);
401069f1a80SAndrew Thompson static int run_efuse_read_2(struct run_softc *, uint16_t, uint16_t *);
402069f1a80SAndrew Thompson static int run_eeprom_read_2(struct run_softc *, uint16_t, uint16_t *);
4035b751464SKevin Lo static int run_rt2870_rf_write(struct run_softc *, uint32_t);
404069f1a80SAndrew Thompson static int run_rt3070_rf_read(struct run_softc *, uint8_t, uint8_t *);
405069f1a80SAndrew Thompson static int run_rt3070_rf_write(struct run_softc *, uint8_t, uint8_t);
406069f1a80SAndrew Thompson static int run_bbp_read(struct run_softc *, uint8_t, uint8_t *);
407069f1a80SAndrew Thompson static int run_bbp_write(struct run_softc *, uint8_t, uint8_t);
408069f1a80SAndrew Thompson static int run_mcu_cmd(struct run_softc *, uint8_t, uint16_t);
40964891211SKevin Lo static const char *run_get_rf(uint16_t);
4107a7e01caSKevin Lo static void run_rt3593_get_txpower(struct run_softc *);
4117a7e01caSKevin Lo static void run_get_txpower(struct run_softc *);
412069f1a80SAndrew Thompson static int run_read_eeprom(struct run_softc *);
413069f1a80SAndrew Thompson static struct ieee80211_node *run_node_alloc(struct ieee80211vap *,
414069f1a80SAndrew Thompson const uint8_t mac[IEEE80211_ADDR_LEN]);
415935b194dSJustin Hibbits static int run_media_change(if_t);
416069f1a80SAndrew Thompson static int run_newstate(struct ieee80211vap *, enum ieee80211_state, int);
417069f1a80SAndrew Thompson static int run_wme_update(struct ieee80211com *);
41885e7bb81SAndrew Thompson static void run_key_set_cb(void *);
419bc813c40SAdrian Chadd static int run_key_set(struct ieee80211vap *, struct ieee80211_key *);
42085e7bb81SAndrew Thompson static void run_key_delete_cb(void *);
42185e7bb81SAndrew Thompson static int run_key_delete(struct ieee80211vap *, struct ieee80211_key *);
422b6108616SRui Paulo static void run_ratectl_to(void *);
423b6108616SRui Paulo static void run_ratectl_cb(void *, int);
42485e7bb81SAndrew Thompson static void run_drain_fifo(void *);
425069f1a80SAndrew Thompson static void run_iter_func(void *, struct ieee80211_node *);
42685e7bb81SAndrew Thompson static void run_newassoc_cb(void *);
427069f1a80SAndrew Thompson static void run_newassoc(struct ieee80211_node *, int);
42899feb202SAdrian Chadd static void run_recv_mgmt(struct ieee80211_node *, struct mbuf *, int,
42999feb202SAdrian Chadd const struct ieee80211_rx_stats *, int, int);
430069f1a80SAndrew Thompson static void run_rx_frame(struct run_softc *, struct mbuf *, uint32_t);
431069f1a80SAndrew Thompson static void run_tx_free(struct run_endpoint_queue *pq,
432069f1a80SAndrew Thompson struct run_tx_data *, int);
43385e7bb81SAndrew Thompson static void run_set_tx_desc(struct run_softc *, struct run_tx_data *);
434069f1a80SAndrew Thompson static int run_tx(struct run_softc *, struct mbuf *,
435069f1a80SAndrew Thompson struct ieee80211_node *);
436069f1a80SAndrew Thompson static int run_tx_mgt(struct run_softc *, struct mbuf *,
437069f1a80SAndrew Thompson struct ieee80211_node *);
438069f1a80SAndrew Thompson static int run_sendprot(struct run_softc *, const struct mbuf *,
439069f1a80SAndrew Thompson struct ieee80211_node *, int, int);
440069f1a80SAndrew Thompson static int run_tx_param(struct run_softc *, struct mbuf *,
441069f1a80SAndrew Thompson struct ieee80211_node *,
442069f1a80SAndrew Thompson const struct ieee80211_bpf_params *);
443069f1a80SAndrew Thompson static int run_raw_xmit(struct ieee80211_node *, struct mbuf *,
444069f1a80SAndrew Thompson const struct ieee80211_bpf_params *);
4457a79cebfSGleb Smirnoff static int run_transmit(struct ieee80211com *, struct mbuf *);
4467a79cebfSGleb Smirnoff static void run_start(struct run_softc *);
4477a79cebfSGleb Smirnoff static void run_parent(struct ieee80211com *);
448010b13faSKevin Lo static void run_iq_calib(struct run_softc *, u_int);
4493707a5e9SAndrew Thompson static void run_set_agc(struct run_softc *, uint8_t);
450069f1a80SAndrew Thompson static void run_select_chan_group(struct run_softc *, int);
451069f1a80SAndrew Thompson static void run_set_rx_antenna(struct run_softc *, int);
452069f1a80SAndrew Thompson static void run_rt2870_set_chan(struct run_softc *, u_int);
453069f1a80SAndrew Thompson static void run_rt3070_set_chan(struct run_softc *, u_int);
4543707a5e9SAndrew Thompson static void run_rt3572_set_chan(struct run_softc *, u_int);
4557a7e01caSKevin Lo static void run_rt3593_set_chan(struct run_softc *, u_int);
45664891211SKevin Lo static void run_rt5390_set_chan(struct run_softc *, u_int);
457242dbae3SKevin Lo static void run_rt5592_set_chan(struct run_softc *, u_int);
458069f1a80SAndrew Thompson static int run_set_chan(struct run_softc *, struct ieee80211_channel *);
459069f1a80SAndrew Thompson static void run_set_channel(struct ieee80211com *);
46099bb30a9SAndriy Voskoboinyk static void run_getradiocaps(struct ieee80211com *, int, int *,
46199bb30a9SAndriy Voskoboinyk struct ieee80211_channel[]);
462069f1a80SAndrew Thompson static void run_scan_start(struct ieee80211com *);
463069f1a80SAndrew Thompson static void run_scan_end(struct ieee80211com *);
464069f1a80SAndrew Thompson static void run_update_beacon(struct ieee80211vap *, int);
46585e7bb81SAndrew Thompson static void run_update_beacon_cb(void *);
466069f1a80SAndrew Thompson static void run_updateprot(struct ieee80211com *);
467e7d14e9bSBernhard Schmidt static void run_updateprot_cb(void *);
46885e7bb81SAndrew Thompson static void run_usb_timeout_cb(void *);
469069f1a80SAndrew Thompson static void run_reset_livelock(struct run_softc *);
470069f1a80SAndrew Thompson static void run_enable_tsf_sync(struct run_softc *);
471599acbbcSKevin Lo static void run_enable_tsf(struct run_softc *);
472ae6f61b1SAndriy Voskoboinyk static void run_disable_tsf(struct run_softc *);
473ef9c0768SKevin Lo static void run_get_tsf(struct run_softc *, uint64_t *);
474069f1a80SAndrew Thompson static void run_enable_mrr(struct run_softc *);
475069f1a80SAndrew Thompson static void run_set_txpreamble(struct run_softc *);
476069f1a80SAndrew Thompson static void run_set_basicrates(struct run_softc *);
477069f1a80SAndrew Thompson static void run_set_leds(struct run_softc *, uint16_t);
478069f1a80SAndrew Thompson static void run_set_bssid(struct run_softc *, const uint8_t *);
479069f1a80SAndrew Thompson static void run_set_macaddr(struct run_softc *, const uint8_t *);
480272f6adeSGleb Smirnoff static void run_updateslot(struct ieee80211com *);
481e7d14e9bSBernhard Schmidt static void run_updateslot_cb(void *);
482272f6adeSGleb Smirnoff static void run_update_mcast(struct ieee80211com *);
483069f1a80SAndrew Thompson static int8_t run_rssi2dbm(struct run_softc *, uint8_t, uint8_t);
484272f6adeSGleb Smirnoff static void run_update_promisc_locked(struct run_softc *);
485272f6adeSGleb Smirnoff static void run_update_promisc(struct ieee80211com *);
48664891211SKevin Lo static void run_rt5390_bbp_init(struct run_softc *);
487069f1a80SAndrew Thompson static int run_bbp_init(struct run_softc *);
488069f1a80SAndrew Thompson static int run_rt3070_rf_init(struct run_softc *);
4897a7e01caSKevin Lo static void run_rt3593_rf_init(struct run_softc *);
49064891211SKevin Lo static void run_rt5390_rf_init(struct run_softc *);
491069f1a80SAndrew Thompson static int run_rt3070_filter_calib(struct run_softc *, uint8_t, uint8_t,
492069f1a80SAndrew Thompson uint8_t *);
4933707a5e9SAndrew Thompson static void run_rt3070_rf_setup(struct run_softc *);
4947a7e01caSKevin Lo static void run_rt3593_rf_setup(struct run_softc *);
4957a7e01caSKevin Lo static void run_rt5390_rf_setup(struct run_softc *);
496069f1a80SAndrew Thompson static int run_txrx_enable(struct run_softc *);
49764891211SKevin Lo static void run_adjust_freq_offset(struct run_softc *);
498069f1a80SAndrew Thompson static void run_init_locked(struct run_softc *);
499069f1a80SAndrew Thompson static void run_stop(void *);
50035a24898SHans Petter Selasky static void run_delay(struct run_softc *, u_int);
501f520d761SAdrian Chadd static void run_update_chw(struct ieee80211com *ic);
502f520d761SAdrian Chadd static int run_ampdu_enable(struct ieee80211_node *ni,
503f520d761SAdrian Chadd struct ieee80211_tx_ampdu *tap);
504069f1a80SAndrew Thompson
5057dc4b90eSKevin Lo static eventhandler_tag run_etag;
5067dc4b90eSKevin Lo
507010b13faSKevin Lo static const struct rt2860_rate {
508010b13faSKevin Lo uint8_t rate;
509010b13faSKevin Lo uint8_t mcs;
510010b13faSKevin Lo enum ieee80211_phytype phy;
511010b13faSKevin Lo uint8_t ctl_ridx;
512010b13faSKevin Lo uint16_t sp_ack_dur;
513010b13faSKevin Lo uint16_t lp_ack_dur;
514010b13faSKevin Lo } rt2860_rates[] = {
515f520d761SAdrian Chadd /* CCK rates (11b) */
516010b13faSKevin Lo { 2, 0, IEEE80211_T_DS, 0, 314, 314 },
517010b13faSKevin Lo { 4, 1, IEEE80211_T_DS, 1, 258, 162 },
518010b13faSKevin Lo { 11, 2, IEEE80211_T_DS, 2, 223, 127 },
519010b13faSKevin Lo { 22, 3, IEEE80211_T_DS, 3, 213, 117 },
520f520d761SAdrian Chadd
521f520d761SAdrian Chadd /* OFDM rates (11a / 11g) */
522010b13faSKevin Lo { 12, 0, IEEE80211_T_OFDM, 4, 60, 60 },
523010b13faSKevin Lo { 18, 1, IEEE80211_T_OFDM, 4, 52, 52 },
524010b13faSKevin Lo { 24, 2, IEEE80211_T_OFDM, 6, 48, 48 },
525010b13faSKevin Lo { 36, 3, IEEE80211_T_OFDM, 6, 44, 44 },
526010b13faSKevin Lo { 48, 4, IEEE80211_T_OFDM, 8, 44, 44 },
527010b13faSKevin Lo { 72, 5, IEEE80211_T_OFDM, 8, 40, 40 },
528010b13faSKevin Lo { 96, 6, IEEE80211_T_OFDM, 8, 40, 40 },
529f520d761SAdrian Chadd { 108, 7, IEEE80211_T_OFDM, 8, 40, 40 },
530f520d761SAdrian Chadd
531f520d761SAdrian Chadd /* MCS - single stream */
532f520d761SAdrian Chadd { 0x80, 0, IEEE80211_T_HT, 4, 60, 60 },
533f520d761SAdrian Chadd { 0x81, 1, IEEE80211_T_HT, 4, 60, 60 },
534f520d761SAdrian Chadd { 0x82, 2, IEEE80211_T_HT, 4, 60, 60 },
535f520d761SAdrian Chadd { 0x83, 3, IEEE80211_T_HT, 4, 60, 60 },
536f520d761SAdrian Chadd { 0x84, 4, IEEE80211_T_HT, 4, 60, 60 },
537f520d761SAdrian Chadd { 0x85, 5, IEEE80211_T_HT, 4, 60, 60 },
538f520d761SAdrian Chadd { 0x86, 6, IEEE80211_T_HT, 4, 60, 60 },
539f520d761SAdrian Chadd { 0x87, 7, IEEE80211_T_HT, 4, 60, 60 },
540bb7234beSAdrian Chadd
541f520d761SAdrian Chadd /* MCS - 2 streams */
542f520d761SAdrian Chadd { 0x88, 8, IEEE80211_T_HT, 4, 60, 60 },
543f520d761SAdrian Chadd { 0x89, 9, IEEE80211_T_HT, 4, 60, 60 },
544f520d761SAdrian Chadd { 0x8a, 10, IEEE80211_T_HT, 4, 60, 60 },
545f520d761SAdrian Chadd { 0x8b, 11, IEEE80211_T_HT, 4, 60, 60 },
546f520d761SAdrian Chadd { 0x8c, 12, IEEE80211_T_HT, 4, 60, 60 },
547f520d761SAdrian Chadd { 0x8d, 13, IEEE80211_T_HT, 4, 60, 60 },
548f520d761SAdrian Chadd { 0x8e, 14, IEEE80211_T_HT, 4, 60, 60 },
549f520d761SAdrian Chadd { 0x8f, 15, IEEE80211_T_HT, 4, 60, 60 },
550bb7234beSAdrian Chadd
551bb7234beSAdrian Chadd /* MCS - 3 streams */
552bb7234beSAdrian Chadd { 0x90, 16, IEEE80211_T_HT, 4, 60, 60 },
553bb7234beSAdrian Chadd { 0x91, 17, IEEE80211_T_HT, 4, 60, 60 },
554bb7234beSAdrian Chadd { 0x92, 18, IEEE80211_T_HT, 4, 60, 60 },
555bb7234beSAdrian Chadd { 0x93, 19, IEEE80211_T_HT, 4, 60, 60 },
556bb7234beSAdrian Chadd { 0x94, 20, IEEE80211_T_HT, 4, 60, 60 },
557bb7234beSAdrian Chadd { 0x95, 21, IEEE80211_T_HT, 4, 60, 60 },
558bb7234beSAdrian Chadd { 0x96, 22, IEEE80211_T_HT, 4, 60, 60 },
559bb7234beSAdrian Chadd { 0x97, 23, IEEE80211_T_HT, 4, 60, 60 },
560010b13faSKevin Lo };
561010b13faSKevin Lo
562f520d761SAdrian Chadd /* These are indexes into the above rt2860_rates[] array */
563f520d761SAdrian Chadd #define RT2860_RIDX_CCK1 0
564f520d761SAdrian Chadd #define RT2860_RIDX_CCK11 3
565f520d761SAdrian Chadd #define RT2860_RIDX_OFDM6 4
566f520d761SAdrian Chadd #define RT2860_RIDX_MCS0 12
567bb7234beSAdrian Chadd #define RT2860_RIDX_MAX 36
568f520d761SAdrian Chadd
569069f1a80SAndrew Thompson static const struct {
57085e7bb81SAndrew Thompson uint16_t reg;
571069f1a80SAndrew Thompson uint32_t val;
572069f1a80SAndrew Thompson } rt2870_def_mac[] = {
573069f1a80SAndrew Thompson RT2870_DEF_MAC
574069f1a80SAndrew Thompson };
575069f1a80SAndrew Thompson
576069f1a80SAndrew Thompson static const struct {
577069f1a80SAndrew Thompson uint8_t reg;
578069f1a80SAndrew Thompson uint8_t val;
579069f1a80SAndrew Thompson } rt2860_def_bbp[] = {
580069f1a80SAndrew Thompson RT2860_DEF_BBP
58164891211SKevin Lo },rt5390_def_bbp[] = {
58264891211SKevin Lo RT5390_DEF_BBP
583242dbae3SKevin Lo },rt5592_def_bbp[] = {
584242dbae3SKevin Lo RT5592_DEF_BBP
585242dbae3SKevin Lo };
586242dbae3SKevin Lo
587242dbae3SKevin Lo /*
588242dbae3SKevin Lo * Default values for BBP register R196 for RT5592.
589242dbae3SKevin Lo */
590242dbae3SKevin Lo static const uint8_t rt5592_bbp_r196[] = {
591242dbae3SKevin Lo 0xe0, 0x1f, 0x38, 0x32, 0x08, 0x28, 0x19, 0x0a, 0xff, 0x00,
592242dbae3SKevin Lo 0x16, 0x10, 0x10, 0x0b, 0x36, 0x2c, 0x26, 0x24, 0x42, 0x36,
593242dbae3SKevin Lo 0x30, 0x2d, 0x4c, 0x46, 0x3d, 0x40, 0x3e, 0x42, 0x3d, 0x40,
594242dbae3SKevin Lo 0x3c, 0x34, 0x2c, 0x2f, 0x3c, 0x35, 0x2e, 0x2a, 0x49, 0x41,
595242dbae3SKevin Lo 0x36, 0x31, 0x30, 0x30, 0x0e, 0x0d, 0x28, 0x21, 0x1c, 0x16,
596242dbae3SKevin Lo 0x50, 0x4a, 0x43, 0x40, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00,
597242dbae3SKevin Lo 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598242dbae3SKevin Lo 0x00, 0x00, 0x7d, 0x14, 0x32, 0x2c, 0x36, 0x4c, 0x43, 0x2c,
599242dbae3SKevin Lo 0x2e, 0x36, 0x30, 0x6e
600069f1a80SAndrew Thompson };
601069f1a80SAndrew Thompson
602069f1a80SAndrew Thompson static const struct rfprog {
603069f1a80SAndrew Thompson uint8_t chan;
604069f1a80SAndrew Thompson uint32_t r1, r2, r3, r4;
605069f1a80SAndrew Thompson } rt2860_rf2850[] = {
606069f1a80SAndrew Thompson RT2860_RF2850
607069f1a80SAndrew Thompson };
608069f1a80SAndrew Thompson
609069f1a80SAndrew Thompson struct {
610069f1a80SAndrew Thompson uint8_t n, r, k;
6113707a5e9SAndrew Thompson } rt3070_freqs[] = {
6123707a5e9SAndrew Thompson RT3070_RF3052
613069f1a80SAndrew Thompson };
614069f1a80SAndrew Thompson
615242dbae3SKevin Lo static const struct rt5592_freqs {
616242dbae3SKevin Lo uint16_t n;
617242dbae3SKevin Lo uint8_t k, m, r;
618242dbae3SKevin Lo } rt5592_freqs_20mhz[] = {
619242dbae3SKevin Lo RT5592_RF5592_20MHZ
620242dbae3SKevin Lo },rt5592_freqs_40mhz[] = {
621242dbae3SKevin Lo RT5592_RF5592_40MHZ
622242dbae3SKevin Lo };
623242dbae3SKevin Lo
624069f1a80SAndrew Thompson static const struct {
625069f1a80SAndrew Thompson uint8_t reg;
626069f1a80SAndrew Thompson uint8_t val;
627069f1a80SAndrew Thompson } rt3070_def_rf[] = {
628069f1a80SAndrew Thompson RT3070_DEF_RF
6293707a5e9SAndrew Thompson },rt3572_def_rf[] = {
6303707a5e9SAndrew Thompson RT3572_DEF_RF
6317a7e01caSKevin Lo },rt3593_def_rf[] = {
6327a7e01caSKevin Lo RT3593_DEF_RF
63364891211SKevin Lo },rt5390_def_rf[] = {
63464891211SKevin Lo RT5390_DEF_RF
63564891211SKevin Lo },rt5392_def_rf[] = {
63664891211SKevin Lo RT5392_DEF_RF
637242dbae3SKevin Lo },rt5592_def_rf[] = {
638242dbae3SKevin Lo RT5592_DEF_RF
639242dbae3SKevin Lo },rt5592_2ghz_def_rf[] = {
640242dbae3SKevin Lo RT5592_2GHZ_DEF_RF
641242dbae3SKevin Lo },rt5592_5ghz_def_rf[] = {
642242dbae3SKevin Lo RT5592_5GHZ_DEF_RF
643242dbae3SKevin Lo };
644242dbae3SKevin Lo
645242dbae3SKevin Lo static const struct {
646242dbae3SKevin Lo u_int firstchan;
647242dbae3SKevin Lo u_int lastchan;
648242dbae3SKevin Lo uint8_t reg;
649242dbae3SKevin Lo uint8_t val;
650242dbae3SKevin Lo } rt5592_chan_5ghz[] = {
651242dbae3SKevin Lo RT5592_CHAN_5GHZ
652069f1a80SAndrew Thompson };
653069f1a80SAndrew Thompson
654069f1a80SAndrew Thompson static const struct usb_config run_config[RUN_N_XFER] = {
655069f1a80SAndrew Thompson [RUN_BULK_TX_BE] = {
656069f1a80SAndrew Thompson .type = UE_BULK,
657069f1a80SAndrew Thompson .endpoint = UE_ADDR_ANY,
658069f1a80SAndrew Thompson .ep_index = 0,
659069f1a80SAndrew Thompson .direction = UE_DIR_OUT,
660069f1a80SAndrew Thompson .bufsize = RUN_MAX_TXSZ,
661069f1a80SAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
662069f1a80SAndrew Thompson .callback = run_bulk_tx_callback0,
663069f1a80SAndrew Thompson .timeout = 5000, /* ms */
664069f1a80SAndrew Thompson },
665069f1a80SAndrew Thompson [RUN_BULK_TX_BK] = {
666069f1a80SAndrew Thompson .type = UE_BULK,
667069f1a80SAndrew Thompson .endpoint = UE_ADDR_ANY,
668069f1a80SAndrew Thompson .direction = UE_DIR_OUT,
669069f1a80SAndrew Thompson .ep_index = 1,
670069f1a80SAndrew Thompson .bufsize = RUN_MAX_TXSZ,
671069f1a80SAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
672069f1a80SAndrew Thompson .callback = run_bulk_tx_callback1,
673069f1a80SAndrew Thompson .timeout = 5000, /* ms */
674069f1a80SAndrew Thompson },
675069f1a80SAndrew Thompson [RUN_BULK_TX_VI] = {
676069f1a80SAndrew Thompson .type = UE_BULK,
677069f1a80SAndrew Thompson .endpoint = UE_ADDR_ANY,
678069f1a80SAndrew Thompson .direction = UE_DIR_OUT,
679069f1a80SAndrew Thompson .ep_index = 2,
680069f1a80SAndrew Thompson .bufsize = RUN_MAX_TXSZ,
681069f1a80SAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
682069f1a80SAndrew Thompson .callback = run_bulk_tx_callback2,
683069f1a80SAndrew Thompson .timeout = 5000, /* ms */
684069f1a80SAndrew Thompson },
685069f1a80SAndrew Thompson [RUN_BULK_TX_VO] = {
686069f1a80SAndrew Thompson .type = UE_BULK,
687069f1a80SAndrew Thompson .endpoint = UE_ADDR_ANY,
688069f1a80SAndrew Thompson .direction = UE_DIR_OUT,
689069f1a80SAndrew Thompson .ep_index = 3,
690069f1a80SAndrew Thompson .bufsize = RUN_MAX_TXSZ,
691069f1a80SAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
692069f1a80SAndrew Thompson .callback = run_bulk_tx_callback3,
693069f1a80SAndrew Thompson .timeout = 5000, /* ms */
694069f1a80SAndrew Thompson },
695069f1a80SAndrew Thompson [RUN_BULK_TX_HCCA] = {
696069f1a80SAndrew Thompson .type = UE_BULK,
697069f1a80SAndrew Thompson .endpoint = UE_ADDR_ANY,
698069f1a80SAndrew Thompson .direction = UE_DIR_OUT,
699069f1a80SAndrew Thompson .ep_index = 4,
700069f1a80SAndrew Thompson .bufsize = RUN_MAX_TXSZ,
701069f1a80SAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
702069f1a80SAndrew Thompson .callback = run_bulk_tx_callback4,
703069f1a80SAndrew Thompson .timeout = 5000, /* ms */
704069f1a80SAndrew Thompson },
705069f1a80SAndrew Thompson [RUN_BULK_TX_PRIO] = {
706069f1a80SAndrew Thompson .type = UE_BULK,
707069f1a80SAndrew Thompson .endpoint = UE_ADDR_ANY,
708069f1a80SAndrew Thompson .direction = UE_DIR_OUT,
709069f1a80SAndrew Thompson .ep_index = 5,
710069f1a80SAndrew Thompson .bufsize = RUN_MAX_TXSZ,
711069f1a80SAndrew Thompson .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
712069f1a80SAndrew Thompson .callback = run_bulk_tx_callback5,
713069f1a80SAndrew Thompson .timeout = 5000, /* ms */
714069f1a80SAndrew Thompson },
715069f1a80SAndrew Thompson [RUN_BULK_RX] = {
716069f1a80SAndrew Thompson .type = UE_BULK,
717069f1a80SAndrew Thompson .endpoint = UE_ADDR_ANY,
718069f1a80SAndrew Thompson .direction = UE_DIR_IN,
719069f1a80SAndrew Thompson .bufsize = RUN_MAX_RXSZ,
720069f1a80SAndrew Thompson .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
721069f1a80SAndrew Thompson .callback = run_bulk_rx_callback,
722069f1a80SAndrew Thompson }
723069f1a80SAndrew Thompson };
724069f1a80SAndrew Thompson
7257dc4b90eSKevin Lo static void
run_autoinst(void * arg,struct usb_device * udev,struct usb_attach_arg * uaa)7267dc4b90eSKevin Lo run_autoinst(void *arg, struct usb_device *udev,
7277dc4b90eSKevin Lo struct usb_attach_arg *uaa)
7287dc4b90eSKevin Lo {
7297dc4b90eSKevin Lo struct usb_interface *iface;
7307dc4b90eSKevin Lo struct usb_interface_descriptor *id;
7317dc4b90eSKevin Lo
7327dc4b90eSKevin Lo if (uaa->dev_state != UAA_DEV_READY)
7337dc4b90eSKevin Lo return;
7347dc4b90eSKevin Lo
7357dc4b90eSKevin Lo iface = usbd_get_iface(udev, 0);
7367dc4b90eSKevin Lo if (iface == NULL)
7377dc4b90eSKevin Lo return;
7387dc4b90eSKevin Lo id = iface->idesc;
7397dc4b90eSKevin Lo if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
7407dc4b90eSKevin Lo return;
7417dc4b90eSKevin Lo if (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa))
7427dc4b90eSKevin Lo return;
7437dc4b90eSKevin Lo
7447dc4b90eSKevin Lo if (usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT) == 0)
7457dc4b90eSKevin Lo uaa->dev_state = UAA_DEV_EJECTING;
7467dc4b90eSKevin Lo }
7477dc4b90eSKevin Lo
7487dc4b90eSKevin Lo static int
run_driver_loaded(struct module * mod,int what,void * arg)7497dc4b90eSKevin Lo run_driver_loaded(struct module *mod, int what, void *arg)
7507dc4b90eSKevin Lo {
7517dc4b90eSKevin Lo switch (what) {
7527dc4b90eSKevin Lo case MOD_LOAD:
7537dc4b90eSKevin Lo run_etag = EVENTHANDLER_REGISTER(usb_dev_configured,
7547dc4b90eSKevin Lo run_autoinst, NULL, EVENTHANDLER_PRI_ANY);
7557dc4b90eSKevin Lo break;
7567dc4b90eSKevin Lo case MOD_UNLOAD:
7577dc4b90eSKevin Lo EVENTHANDLER_DEREGISTER(usb_dev_configured, run_etag);
7587dc4b90eSKevin Lo break;
7597dc4b90eSKevin Lo default:
7607dc4b90eSKevin Lo return (EOPNOTSUPP);
7617dc4b90eSKevin Lo }
7627dc4b90eSKevin Lo return (0);
7637dc4b90eSKevin Lo }
7647dc4b90eSKevin Lo
765329f91ffSKevin Lo static int
run_match(device_t self)766069f1a80SAndrew Thompson run_match(device_t self)
767069f1a80SAndrew Thompson {
768069f1a80SAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(self);
769069f1a80SAndrew Thompson
770069f1a80SAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST)
771069f1a80SAndrew Thompson return (ENXIO);
772069f1a80SAndrew Thompson if (uaa->info.bConfigIndex != 0)
773069f1a80SAndrew Thompson return (ENXIO);
774069f1a80SAndrew Thompson if (uaa->info.bIfaceIndex != RT2860_IFACE_INDEX)
775069f1a80SAndrew Thompson return (ENXIO);
776069f1a80SAndrew Thompson
777069f1a80SAndrew Thompson return (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa));
778069f1a80SAndrew Thompson }
779069f1a80SAndrew Thompson
780069f1a80SAndrew Thompson static int
run_attach(device_t self)781069f1a80SAndrew Thompson run_attach(device_t self)
782069f1a80SAndrew Thompson {
783069f1a80SAndrew Thompson struct run_softc *sc = device_get_softc(self);
784069f1a80SAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(self);
7857a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
7863707a5e9SAndrew Thompson uint32_t ver;
7870046e186SAndriy Voskoboinyk uint8_t iface_index;
78828dfd841SKevin Lo int ntries, error;
789069f1a80SAndrew Thompson
790069f1a80SAndrew Thompson device_set_usb_desc(self);
791069f1a80SAndrew Thompson sc->sc_udev = uaa->device;
792069f1a80SAndrew Thompson sc->sc_dev = self;
7935c5e99d2SKevin Lo if (USB_GET_DRIVER_INFO(uaa) != RUN_EJECT)
7945c5e99d2SKevin Lo sc->sc_flags |= RUN_FLAG_FWLOAD_NEEDED;
795069f1a80SAndrew Thompson
796069f1a80SAndrew Thompson mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev),
797069f1a80SAndrew Thompson MTX_NETWORK_LOCK, MTX_DEF);
7987a79cebfSGleb Smirnoff mbufq_init(&sc->sc_snd, ifqmaxlen);
799069f1a80SAndrew Thompson
800069f1a80SAndrew Thompson iface_index = RT2860_IFACE_INDEX;
80185e7bb81SAndrew Thompson
802069f1a80SAndrew Thompson error = usbd_transfer_setup(uaa->device, &iface_index,
803069f1a80SAndrew Thompson sc->sc_xfer, run_config, RUN_N_XFER, sc, &sc->sc_mtx);
804069f1a80SAndrew Thompson if (error) {
8053707a5e9SAndrew Thompson device_printf(self, "could not allocate USB transfers, "
806069f1a80SAndrew Thompson "err=%s\n", usbd_errstr(error));
807069f1a80SAndrew Thompson goto detach;
808069f1a80SAndrew Thompson }
809069f1a80SAndrew Thompson
810069f1a80SAndrew Thompson RUN_LOCK(sc);
811069f1a80SAndrew Thompson
812069f1a80SAndrew Thompson /* wait for the chip to settle */
813069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
8143707a5e9SAndrew Thompson if (run_read(sc, RT2860_ASIC_VER_ID, &ver) != 0) {
815069f1a80SAndrew Thompson RUN_UNLOCK(sc);
816069f1a80SAndrew Thompson goto detach;
817069f1a80SAndrew Thompson }
8183707a5e9SAndrew Thompson if (ver != 0 && ver != 0xffffffff)
819069f1a80SAndrew Thompson break;
820069f1a80SAndrew Thompson run_delay(sc, 10);
821069f1a80SAndrew Thompson }
822069f1a80SAndrew Thompson if (ntries == 100) {
82397ccc890SAndrew Thompson device_printf(sc->sc_dev,
82497ccc890SAndrew Thompson "timeout waiting for NIC to initialize\n");
825069f1a80SAndrew Thompson RUN_UNLOCK(sc);
826069f1a80SAndrew Thompson goto detach;
827069f1a80SAndrew Thompson }
8283707a5e9SAndrew Thompson sc->mac_ver = ver >> 16;
8293707a5e9SAndrew Thompson sc->mac_rev = ver & 0xffff;
830069f1a80SAndrew Thompson
831069f1a80SAndrew Thompson /* retrieve RF rev. no and various other things from EEPROM */
832069f1a80SAndrew Thompson run_read_eeprom(sc);
833069f1a80SAndrew Thompson
83497ccc890SAndrew Thompson device_printf(sc->sc_dev,
83597ccc890SAndrew Thompson "MAC/BBP RT%04X (rev 0x%04X), RF %s (MIMO %dT%dR), address %s\n",
8363707a5e9SAndrew Thompson sc->mac_ver, sc->mac_rev, run_get_rf(sc->rf_rev),
8377a79cebfSGleb Smirnoff sc->ntxchains, sc->nrxchains, ether_sprintf(ic->ic_macaddr));
838069f1a80SAndrew Thompson
839069f1a80SAndrew Thompson RUN_UNLOCK(sc);
840069f1a80SAndrew Thompson
84159686fe9SGleb Smirnoff ic->ic_softc = sc;
842c8550c02SGleb Smirnoff ic->ic_name = device_get_nameunit(self);
843069f1a80SAndrew Thompson ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
844069f1a80SAndrew Thompson ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
84585e7bb81SAndrew Thompson
846069f1a80SAndrew Thompson /* set device capabilities */
847069f1a80SAndrew Thompson ic->ic_caps =
848069f1a80SAndrew Thompson IEEE80211_C_STA | /* station mode supported */
849069f1a80SAndrew Thompson IEEE80211_C_MONITOR | /* monitor mode supported */
850069f1a80SAndrew Thompson IEEE80211_C_IBSS |
851069f1a80SAndrew Thompson IEEE80211_C_HOSTAP |
85285e7bb81SAndrew Thompson IEEE80211_C_WDS | /* 4-address traffic works */
85385e7bb81SAndrew Thompson IEEE80211_C_MBSS |
854069f1a80SAndrew Thompson IEEE80211_C_SHPREAMBLE | /* short preamble supported */
855069f1a80SAndrew Thompson IEEE80211_C_SHSLOT | /* short slot time supported */
856f520d761SAdrian Chadd IEEE80211_C_SWAMSDUTX | /* Do software A-MSDU TX */
857f520d761SAdrian Chadd IEEE80211_C_FF | /* Atheros fast-frames */
858069f1a80SAndrew Thompson IEEE80211_C_WME | /* WME */
859a7c6aabdSBernhard Schmidt IEEE80211_C_WPA; /* WPA1|WPA2(RSN) */
860069f1a80SAndrew Thompson
861700af579SAdrian Chadd /*
862700af579SAdrian Chadd * RF2020 is not an 11n device.
863700af579SAdrian Chadd */
864700af579SAdrian Chadd if (sc->rf_rev != RT3070_RF_2020) {
865700af579SAdrian Chadd device_printf(sc->sc_dev, "[HT] Enabling 802.11n\n");
866f520d761SAdrian Chadd ic->ic_htcaps =
867f520d761SAdrian Chadd IEEE80211_HTC_HT |
868f520d761SAdrian Chadd IEEE80211_HTC_AMPDU |
869f520d761SAdrian Chadd IEEE80211_HTC_AMSDU |
870f520d761SAdrian Chadd IEEE80211_HTCAP_MAXAMSDU_3839 |
871f520d761SAdrian Chadd IEEE80211_HTCAP_SMPS_OFF;
872f520d761SAdrian Chadd
873bb7234beSAdrian Chadd ic->ic_rxstream = sc->nrxchains;
874bb7234beSAdrian Chadd ic->ic_txstream = sc->ntxchains;
875700af579SAdrian Chadd }
876f520d761SAdrian Chadd
877069f1a80SAndrew Thompson ic->ic_cryptocaps =
878069f1a80SAndrew Thompson IEEE80211_CRYPTO_WEP |
879069f1a80SAndrew Thompson IEEE80211_CRYPTO_AES_CCM |
880069f1a80SAndrew Thompson IEEE80211_CRYPTO_TKIPMIC |
881069f1a80SAndrew Thompson IEEE80211_CRYPTO_TKIP;
882069f1a80SAndrew Thompson
883069f1a80SAndrew Thompson ic->ic_flags |= IEEE80211_F_DATAPAD;
884069f1a80SAndrew Thompson ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
885069f1a80SAndrew Thompson
88699bb30a9SAndriy Voskoboinyk run_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
88799bb30a9SAndriy Voskoboinyk ic->ic_channels);
888069f1a80SAndrew Thompson
8897a79cebfSGleb Smirnoff ieee80211_ifattach(ic);
890069f1a80SAndrew Thompson
891069f1a80SAndrew Thompson ic->ic_scan_start = run_scan_start;
892069f1a80SAndrew Thompson ic->ic_scan_end = run_scan_end;
893069f1a80SAndrew Thompson ic->ic_set_channel = run_set_channel;
89499bb30a9SAndriy Voskoboinyk ic->ic_getradiocaps = run_getradiocaps;
895069f1a80SAndrew Thompson ic->ic_node_alloc = run_node_alloc;
896069f1a80SAndrew Thompson ic->ic_newassoc = run_newassoc;
897e7d14e9bSBernhard Schmidt ic->ic_updateslot = run_updateslot;
89885e7bb81SAndrew Thompson ic->ic_update_mcast = run_update_mcast;
899069f1a80SAndrew Thompson ic->ic_wme.wme_update = run_wme_update;
900069f1a80SAndrew Thompson ic->ic_raw_xmit = run_raw_xmit;
901069f1a80SAndrew Thompson ic->ic_update_promisc = run_update_promisc;
902069f1a80SAndrew Thompson ic->ic_vap_create = run_vap_create;
903069f1a80SAndrew Thompson ic->ic_vap_delete = run_vap_delete;
9047a79cebfSGleb Smirnoff ic->ic_transmit = run_transmit;
9057a79cebfSGleb Smirnoff ic->ic_parent = run_parent;
906f520d761SAdrian Chadd ic->ic_update_chw = run_update_chw;
907f520d761SAdrian Chadd ic->ic_ampdu_enable = run_ampdu_enable;
908069f1a80SAndrew Thompson
909069f1a80SAndrew Thompson ieee80211_radiotap_attach(ic,
910069f1a80SAndrew Thompson &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
911069f1a80SAndrew Thompson RUN_TX_RADIOTAP_PRESENT,
912069f1a80SAndrew Thompson &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
913069f1a80SAndrew Thompson RUN_RX_RADIOTAP_PRESENT);
914069f1a80SAndrew Thompson
91585e7bb81SAndrew Thompson TASK_INIT(&sc->cmdq_task, 0, run_cmdq_cb, sc);
91685e7bb81SAndrew Thompson TASK_INIT(&sc->ratectl_task, 0, run_ratectl_cb, sc);
9172764a278SHans Petter Selasky usb_callout_init_mtx(&sc->ratectl_ch, &sc->sc_mtx, 0);
91885e7bb81SAndrew Thompson
919069f1a80SAndrew Thompson if (bootverbose)
920069f1a80SAndrew Thompson ieee80211_announce(ic);
921069f1a80SAndrew Thompson
92275c76159SAndrew Thompson return (0);
923069f1a80SAndrew Thompson
924069f1a80SAndrew Thompson detach:
925069f1a80SAndrew Thompson run_detach(self);
926069f1a80SAndrew Thompson return (ENXIO);
927069f1a80SAndrew Thompson }
928069f1a80SAndrew Thompson
9292e83cb98SAdrian Chadd static void
run_drain_mbufq(struct run_softc * sc)9302e83cb98SAdrian Chadd run_drain_mbufq(struct run_softc *sc)
9312e83cb98SAdrian Chadd {
9322e83cb98SAdrian Chadd struct mbuf *m;
9332e83cb98SAdrian Chadd struct ieee80211_node *ni;
9342e83cb98SAdrian Chadd
9352e83cb98SAdrian Chadd RUN_LOCK_ASSERT(sc, MA_OWNED);
9362e83cb98SAdrian Chadd while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
9372e83cb98SAdrian Chadd ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
9382e83cb98SAdrian Chadd m->m_pkthdr.rcvif = NULL;
9392e83cb98SAdrian Chadd ieee80211_free_node(ni);
9402e83cb98SAdrian Chadd m_freem(m);
9412e83cb98SAdrian Chadd }
9422e83cb98SAdrian Chadd }
9432e83cb98SAdrian Chadd
944069f1a80SAndrew Thompson static int
run_detach(device_t self)945069f1a80SAndrew Thompson run_detach(device_t self)
946069f1a80SAndrew Thompson {
947069f1a80SAndrew Thompson struct run_softc *sc = device_get_softc(self);
9487a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
949069f1a80SAndrew Thompson int i;
950069f1a80SAndrew Thompson
951645e4d17SHans Petter Selasky RUN_LOCK(sc);
952645e4d17SHans Petter Selasky sc->sc_detached = 1;
953645e4d17SHans Petter Selasky RUN_UNLOCK(sc);
954645e4d17SHans Petter Selasky
955069f1a80SAndrew Thompson /* stop all USB transfers */
956069f1a80SAndrew Thompson usbd_transfer_unsetup(sc->sc_xfer, RUN_N_XFER);
957069f1a80SAndrew Thompson
958069f1a80SAndrew Thompson RUN_LOCK(sc);
959beaa0537SAndrew Thompson sc->ratectl_run = RUN_RATECTL_OFF;
960beaa0537SAndrew Thompson sc->cmdq_run = sc->cmdq_key_set = RUN_CMDQ_ABORT;
961beaa0537SAndrew Thompson
962069f1a80SAndrew Thompson /* free TX list, if any */
963069f1a80SAndrew Thompson for (i = 0; i != RUN_EP_QUEUES; i++)
964069f1a80SAndrew Thompson run_unsetup_tx_list(sc, &sc->sc_epq[i]);
9652e83cb98SAdrian Chadd
9662e83cb98SAdrian Chadd /* Free TX queue */
9672e83cb98SAdrian Chadd run_drain_mbufq(sc);
968069f1a80SAndrew Thompson RUN_UNLOCK(sc);
969069f1a80SAndrew Thompson
9707a79cebfSGleb Smirnoff if (sc->sc_ic.ic_softc == sc) {
97185e7bb81SAndrew Thompson /* drain tasks */
97285e7bb81SAndrew Thompson usb_callout_drain(&sc->ratectl_ch);
97385e7bb81SAndrew Thompson ieee80211_draintask(ic, &sc->cmdq_task);
97485e7bb81SAndrew Thompson ieee80211_draintask(ic, &sc->ratectl_task);
975069f1a80SAndrew Thompson ieee80211_ifdetach(ic);
976069f1a80SAndrew Thompson }
977069f1a80SAndrew Thompson
978069f1a80SAndrew Thompson mtx_destroy(&sc->sc_mtx);
979069f1a80SAndrew Thompson
980069f1a80SAndrew Thompson return (0);
981069f1a80SAndrew Thompson }
982069f1a80SAndrew Thompson
983069f1a80SAndrew Thompson static struct ieee80211vap *
run_vap_create(struct ieee80211com * ic,const char name[IFNAMSIZ],int unit,enum ieee80211_opmode opmode,int flags,const uint8_t bssid[IEEE80211_ADDR_LEN],const uint8_t mac[IEEE80211_ADDR_LEN])984fcd9500fSBernhard Schmidt run_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
985fcd9500fSBernhard Schmidt enum ieee80211_opmode opmode, int flags,
986069f1a80SAndrew Thompson const uint8_t bssid[IEEE80211_ADDR_LEN],
987069f1a80SAndrew Thompson const uint8_t mac[IEEE80211_ADDR_LEN])
988069f1a80SAndrew Thompson {
989d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
990069f1a80SAndrew Thompson struct run_vap *rvp;
991069f1a80SAndrew Thompson struct ieee80211vap *vap;
99285e7bb81SAndrew Thompson int i;
993069f1a80SAndrew Thompson
99485e7bb81SAndrew Thompson if (sc->rvp_cnt >= RUN_VAP_MAX) {
9957a79cebfSGleb Smirnoff device_printf(sc->sc_dev, "number of VAPs maxed out\n");
99675c76159SAndrew Thompson return (NULL);
99785e7bb81SAndrew Thompson }
99885e7bb81SAndrew Thompson
99985e7bb81SAndrew Thompson switch (opmode) {
100085e7bb81SAndrew Thompson case IEEE80211_M_STA:
100185e7bb81SAndrew Thompson /* enable s/w bmiss handling for sta mode */
100285e7bb81SAndrew Thompson flags |= IEEE80211_CLONE_NOBEACONS;
100385e7bb81SAndrew Thompson /* fall though */
100485e7bb81SAndrew Thompson case IEEE80211_M_IBSS:
100585e7bb81SAndrew Thompson case IEEE80211_M_MONITOR:
100685e7bb81SAndrew Thompson case IEEE80211_M_HOSTAP:
100785e7bb81SAndrew Thompson case IEEE80211_M_MBSS:
100885e7bb81SAndrew Thompson /* other than WDS vaps, only one at a time */
100985e7bb81SAndrew Thompson if (!TAILQ_EMPTY(&ic->ic_vaps))
101075c76159SAndrew Thompson return (NULL);
101185e7bb81SAndrew Thompson break;
101285e7bb81SAndrew Thompson case IEEE80211_M_WDS:
101385e7bb81SAndrew Thompson TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next){
101485e7bb81SAndrew Thompson if(vap->iv_opmode != IEEE80211_M_HOSTAP)
101585e7bb81SAndrew Thompson continue;
101685e7bb81SAndrew Thompson /* WDS vap's always share the local mac address. */
101785e7bb81SAndrew Thompson flags &= ~IEEE80211_CLONE_BSSID;
101885e7bb81SAndrew Thompson break;
101985e7bb81SAndrew Thompson }
102085e7bb81SAndrew Thompson if (vap == NULL) {
10217a79cebfSGleb Smirnoff device_printf(sc->sc_dev,
10227a79cebfSGleb Smirnoff "wds only supported in ap mode\n");
102375c76159SAndrew Thompson return (NULL);
102485e7bb81SAndrew Thompson }
102585e7bb81SAndrew Thompson break;
102685e7bb81SAndrew Thompson default:
10277a79cebfSGleb Smirnoff device_printf(sc->sc_dev, "unknown opmode %d\n", opmode);
102875c76159SAndrew Thompson return (NULL);
102985e7bb81SAndrew Thompson }
103085e7bb81SAndrew Thompson
10317a79cebfSGleb Smirnoff rvp = malloc(sizeof(struct run_vap), M_80211_VAP, M_WAITOK | M_ZERO);
1032069f1a80SAndrew Thompson vap = &rvp->vap;
1033bb2f69e8SHans Petter Selasky
10347a79cebfSGleb Smirnoff if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags,
10357a79cebfSGleb Smirnoff bssid) != 0) {
1036bb2f69e8SHans Petter Selasky /* out of memory */
1037bb2f69e8SHans Petter Selasky free(rvp, M_80211_VAP);
1038bb2f69e8SHans Petter Selasky return (NULL);
1039bb2f69e8SHans Petter Selasky }
1040069f1a80SAndrew Thompson
1041069f1a80SAndrew Thompson vap->iv_update_beacon = run_update_beacon;
104285e7bb81SAndrew Thompson vap->iv_max_aid = RT2870_WCID_MAX;
1043f520d761SAdrian Chadd
1044700af579SAdrian Chadd /*
1045700af579SAdrian Chadd * The linux rt2800 driver limits 1 stream devices to a 32KB
1046700af579SAdrian Chadd * RX AMPDU.
1047700af579SAdrian Chadd */
1048700af579SAdrian Chadd if (ic->ic_rxstream > 1)
1049f520d761SAdrian Chadd vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
1050700af579SAdrian Chadd else
1051700af579SAdrian Chadd vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_32K;
1052700af579SAdrian Chadd vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_2; /* 2uS */
1053f520d761SAdrian Chadd
105485e7bb81SAndrew Thompson /*
105585e7bb81SAndrew Thompson * To delete the right key from h/w, we need wcid.
105685e7bb81SAndrew Thompson * Luckily, there is unused space in ieee80211_key{}, wk_pad,
105785e7bb81SAndrew Thompson * and matching wcid will be written into there. So, cast
105885e7bb81SAndrew Thompson * some spells to remove 'const' from ieee80211_key{}
105985e7bb81SAndrew Thompson */
106085e7bb81SAndrew Thompson vap->iv_key_delete = (void *)run_key_delete;
106185e7bb81SAndrew Thompson vap->iv_key_set = (void *)run_key_set;
1062069f1a80SAndrew Thompson
1063069f1a80SAndrew Thompson /* override state transition machine */
1064069f1a80SAndrew Thompson rvp->newstate = vap->iv_newstate;
1065069f1a80SAndrew Thompson vap->iv_newstate = run_newstate;
106699feb202SAdrian Chadd if (opmode == IEEE80211_M_IBSS) {
106799feb202SAdrian Chadd rvp->recv_mgmt = vap->iv_recv_mgmt;
106899feb202SAdrian Chadd vap->iv_recv_mgmt = run_recv_mgmt;
106999feb202SAdrian Chadd }
1070069f1a80SAndrew Thompson
1071b6108616SRui Paulo ieee80211_ratectl_init(vap);
1072b6108616SRui Paulo ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
1073069f1a80SAndrew Thompson
1074069f1a80SAndrew Thompson /* complete setup */
10757a79cebfSGleb Smirnoff ieee80211_vap_attach(vap, run_media_change, ieee80211_media_status,
10767a79cebfSGleb Smirnoff mac);
107785e7bb81SAndrew Thompson
107885e7bb81SAndrew Thompson /* make sure id is always unique */
107985e7bb81SAndrew Thompson for (i = 0; i < RUN_VAP_MAX; i++) {
108085e7bb81SAndrew Thompson if((sc->rvp_bmap & 1 << i) == 0){
108185e7bb81SAndrew Thompson sc->rvp_bmap |= 1 << i;
108285e7bb81SAndrew Thompson rvp->rvp_id = i;
108385e7bb81SAndrew Thompson break;
108485e7bb81SAndrew Thompson }
108585e7bb81SAndrew Thompson }
108685e7bb81SAndrew Thompson if (sc->rvp_cnt++ == 0)
1087069f1a80SAndrew Thompson ic->ic_opmode = opmode;
108885e7bb81SAndrew Thompson
1089beaa0537SAndrew Thompson if (opmode == IEEE80211_M_HOSTAP)
1090beaa0537SAndrew Thompson sc->cmdq_run = RUN_CMDQ_GO;
1091beaa0537SAndrew Thompson
1092109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_STATE, "rvp_id=%d bmap=%x rvp_cnt=%d\n",
109385e7bb81SAndrew Thompson rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt);
109485e7bb81SAndrew Thompson
109575c76159SAndrew Thompson return (vap);
1096069f1a80SAndrew Thompson }
1097069f1a80SAndrew Thompson
1098069f1a80SAndrew Thompson static void
run_vap_delete(struct ieee80211vap * vap)1099069f1a80SAndrew Thompson run_vap_delete(struct ieee80211vap *vap)
1100069f1a80SAndrew Thompson {
1101069f1a80SAndrew Thompson struct run_vap *rvp = RUN_VAP(vap);
1102069f1a80SAndrew Thompson struct ieee80211com *ic;
1103069f1a80SAndrew Thompson struct run_softc *sc;
110485e7bb81SAndrew Thompson uint8_t rvp_id;
1105069f1a80SAndrew Thompson
1106069f1a80SAndrew Thompson if (vap == NULL)
1107069f1a80SAndrew Thompson return;
1108069f1a80SAndrew Thompson
1109069f1a80SAndrew Thompson ic = vap->iv_ic;
1110d3fdd08cSAdrian Chadd sc = ic->ic_softc;
1111069f1a80SAndrew Thompson
1112069f1a80SAndrew Thompson RUN_LOCK(sc);
11133707a5e9SAndrew Thompson
1114e7d14e9bSBernhard Schmidt m_freem(rvp->beacon_mbuf);
1115e7d14e9bSBernhard Schmidt rvp->beacon_mbuf = NULL;
1116e7d14e9bSBernhard Schmidt
111785e7bb81SAndrew Thompson rvp_id = rvp->rvp_id;
111885e7bb81SAndrew Thompson sc->ratectl_run &= ~(1 << rvp_id);
111985e7bb81SAndrew Thompson sc->rvp_bmap &= ~(1 << rvp_id);
112085e7bb81SAndrew Thompson run_set_region_4(sc, RT2860_SKEY(rvp_id, 0), 0, 128);
112185e7bb81SAndrew Thompson run_set_region_4(sc, RT2860_BCN_BASE(rvp_id), 0, 512);
112285e7bb81SAndrew Thompson --sc->rvp_cnt;
112385e7bb81SAndrew Thompson
1124109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_STATE,
1125109005ccSGavin Atkinson "vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n",
112685e7bb81SAndrew Thompson vap, rvp_id, sc->rvp_bmap, sc->rvp_cnt);
112785e7bb81SAndrew Thompson
112885e7bb81SAndrew Thompson RUN_UNLOCK(sc);
1129069f1a80SAndrew Thompson
1130b6108616SRui Paulo ieee80211_ratectl_deinit(vap);
1131069f1a80SAndrew Thompson ieee80211_vap_detach(vap);
1132069f1a80SAndrew Thompson free(rvp, M_80211_VAP);
113385e7bb81SAndrew Thompson }
113485e7bb81SAndrew Thompson
113585e7bb81SAndrew Thompson /*
113685e7bb81SAndrew Thompson * There are numbers of functions need to be called in context thread.
113785e7bb81SAndrew Thompson * Rather than creating taskqueue event for each of those functions,
113885e7bb81SAndrew Thompson * here is all-for-one taskqueue callback function. This function
113920733245SPedro F. Giffuni * guarantees deferred functions are executed in the same order they
114085e7bb81SAndrew Thompson * were enqueued.
114185e7bb81SAndrew Thompson * '& RUN_CMDQ_MASQ' is to loop cmdq[].
114285e7bb81SAndrew Thompson */
114385e7bb81SAndrew Thompson static void
run_cmdq_cb(void * arg,int pending)114485e7bb81SAndrew Thompson run_cmdq_cb(void *arg, int pending)
114585e7bb81SAndrew Thompson {
114685e7bb81SAndrew Thompson struct run_softc *sc = arg;
114785e7bb81SAndrew Thompson uint8_t i;
114885e7bb81SAndrew Thompson
114985e7bb81SAndrew Thompson /* call cmdq[].func locked */
115085e7bb81SAndrew Thompson RUN_LOCK(sc);
115185e7bb81SAndrew Thompson for (i = sc->cmdq_exec; sc->cmdq[i].func && pending;
115285e7bb81SAndrew Thompson i = sc->cmdq_exec, pending--) {
1153109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_CMD, "cmdq_exec=%d pending=%d\n",
1154109005ccSGavin Atkinson i, pending);
115585e7bb81SAndrew Thompson if (sc->cmdq_run == RUN_CMDQ_GO) {
115685e7bb81SAndrew Thompson /*
115785e7bb81SAndrew Thompson * If arg0 is NULL, callback func needs more
115885e7bb81SAndrew Thompson * than one arg. So, pass ptr to cmdq struct.
115985e7bb81SAndrew Thompson */
116085e7bb81SAndrew Thompson if (sc->cmdq[i].arg0)
116185e7bb81SAndrew Thompson sc->cmdq[i].func(sc->cmdq[i].arg0);
116285e7bb81SAndrew Thompson else
116385e7bb81SAndrew Thompson sc->cmdq[i].func(&sc->cmdq[i]);
116485e7bb81SAndrew Thompson }
116585e7bb81SAndrew Thompson sc->cmdq[i].arg0 = NULL;
116685e7bb81SAndrew Thompson sc->cmdq[i].func = NULL;
116785e7bb81SAndrew Thompson sc->cmdq_exec++;
116885e7bb81SAndrew Thompson sc->cmdq_exec &= RUN_CMDQ_MASQ;
116985e7bb81SAndrew Thompson }
117085e7bb81SAndrew Thompson RUN_UNLOCK(sc);
1171069f1a80SAndrew Thompson }
1172069f1a80SAndrew Thompson
1173069f1a80SAndrew Thompson static void
run_setup_tx_list(struct run_softc * sc,struct run_endpoint_queue * pq)1174069f1a80SAndrew Thompson run_setup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq)
1175069f1a80SAndrew Thompson {
1176069f1a80SAndrew Thompson struct run_tx_data *data;
1177069f1a80SAndrew Thompson
1178069f1a80SAndrew Thompson memset(pq, 0, sizeof(*pq));
1179069f1a80SAndrew Thompson
1180069f1a80SAndrew Thompson STAILQ_INIT(&pq->tx_qh);
1181069f1a80SAndrew Thompson STAILQ_INIT(&pq->tx_fh);
1182069f1a80SAndrew Thompson
1183069f1a80SAndrew Thompson for (data = &pq->tx_data[0];
1184069f1a80SAndrew Thompson data < &pq->tx_data[RUN_TX_RING_COUNT]; data++) {
1185069f1a80SAndrew Thompson data->sc = sc;
1186069f1a80SAndrew Thompson STAILQ_INSERT_TAIL(&pq->tx_fh, data, next);
1187069f1a80SAndrew Thompson }
1188069f1a80SAndrew Thompson pq->tx_nfree = RUN_TX_RING_COUNT;
1189069f1a80SAndrew Thompson }
1190069f1a80SAndrew Thompson
1191069f1a80SAndrew Thompson static void
run_unsetup_tx_list(struct run_softc * sc,struct run_endpoint_queue * pq)1192069f1a80SAndrew Thompson run_unsetup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq)
1193069f1a80SAndrew Thompson {
1194069f1a80SAndrew Thompson struct run_tx_data *data;
1195069f1a80SAndrew Thompson
1196069f1a80SAndrew Thompson /* make sure any subsequent use of the queues will fail */
1197069f1a80SAndrew Thompson pq->tx_nfree = 0;
1198069f1a80SAndrew Thompson STAILQ_INIT(&pq->tx_fh);
1199069f1a80SAndrew Thompson STAILQ_INIT(&pq->tx_qh);
1200069f1a80SAndrew Thompson
1201069f1a80SAndrew Thompson /* free up all node references and mbufs */
1202069f1a80SAndrew Thompson for (data = &pq->tx_data[0];
1203069f1a80SAndrew Thompson data < &pq->tx_data[RUN_TX_RING_COUNT]; data++) {
1204069f1a80SAndrew Thompson if (data->m != NULL) {
1205069f1a80SAndrew Thompson m_freem(data->m);
1206069f1a80SAndrew Thompson data->m = NULL;
1207069f1a80SAndrew Thompson }
1208069f1a80SAndrew Thompson if (data->ni != NULL) {
1209069f1a80SAndrew Thompson ieee80211_free_node(data->ni);
1210069f1a80SAndrew Thompson data->ni = NULL;
1211069f1a80SAndrew Thompson }
1212069f1a80SAndrew Thompson }
1213069f1a80SAndrew Thompson }
1214069f1a80SAndrew Thompson
1215329f91ffSKevin Lo static int
run_load_microcode(struct run_softc * sc)1216069f1a80SAndrew Thompson run_load_microcode(struct run_softc *sc)
1217069f1a80SAndrew Thompson {
1218069f1a80SAndrew Thompson usb_device_request_t req;
12198b1d9a81SAndrew Thompson const struct firmware *fw;
1220069f1a80SAndrew Thompson const u_char *base;
1221069f1a80SAndrew Thompson uint32_t tmp;
1222069f1a80SAndrew Thompson int ntries, error;
1223069f1a80SAndrew Thompson const uint64_t *temp;
1224069f1a80SAndrew Thompson uint64_t bytes;
1225069f1a80SAndrew Thompson
12263707a5e9SAndrew Thompson RUN_UNLOCK(sc);
12278b1d9a81SAndrew Thompson fw = firmware_get("runfw");
12283707a5e9SAndrew Thompson RUN_LOCK(sc);
12298b1d9a81SAndrew Thompson if (fw == NULL) {
123097ccc890SAndrew Thompson device_printf(sc->sc_dev,
123197ccc890SAndrew Thompson "failed loadfirmware of file %s\n", "runfw");
1232069f1a80SAndrew Thompson return ENOENT;
1233069f1a80SAndrew Thompson }
1234069f1a80SAndrew Thompson
12358b1d9a81SAndrew Thompson if (fw->datasize != 8192) {
123697ccc890SAndrew Thompson device_printf(sc->sc_dev,
123797ccc890SAndrew Thompson "invalid firmware size (should be 8KB)\n");
12388b1d9a81SAndrew Thompson error = EINVAL;
12398b1d9a81SAndrew Thompson goto fail;
1240069f1a80SAndrew Thompson }
1241069f1a80SAndrew Thompson
1242069f1a80SAndrew Thompson /*
1243069f1a80SAndrew Thompson * RT3071/RT3072 use a different firmware
1244069f1a80SAndrew Thompson * run-rt2870 (8KB) contains both,
1245069f1a80SAndrew Thompson * first half (4KB) is for rt2870,
1246069f1a80SAndrew Thompson * last half is for rt3071.
1247069f1a80SAndrew Thompson */
12488b1d9a81SAndrew Thompson base = fw->data;
12493707a5e9SAndrew Thompson if ((sc->mac_ver) != 0x2860 &&
12503707a5e9SAndrew Thompson (sc->mac_ver) != 0x2872 &&
12513707a5e9SAndrew Thompson (sc->mac_ver) != 0x3070) {
1252069f1a80SAndrew Thompson base += 4096;
12533707a5e9SAndrew Thompson }
1254069f1a80SAndrew Thompson
1255069f1a80SAndrew Thompson /* cheap sanity check */
12568b1d9a81SAndrew Thompson temp = fw->data;
1257069f1a80SAndrew Thompson bytes = *temp;
12582764a278SHans Petter Selasky if (bytes != be64toh(0xffffff0210280210ULL)) {
125997ccc890SAndrew Thompson device_printf(sc->sc_dev, "firmware checksum failed\n");
12608b1d9a81SAndrew Thompson error = EINVAL;
12618b1d9a81SAndrew Thompson goto fail;
12628b1d9a81SAndrew Thompson }
1263069f1a80SAndrew Thompson
1264069f1a80SAndrew Thompson /* write microcode image */
12655c5e99d2SKevin Lo if (sc->sc_flags & RUN_FLAG_FWLOAD_NEEDED) {
1266069f1a80SAndrew Thompson run_write_region_1(sc, RT2870_FW_BASE, base, 4096);
1267069f1a80SAndrew Thompson run_write(sc, RT2860_H2M_MAILBOX_CID, 0xffffffff);
1268069f1a80SAndrew Thompson run_write(sc, RT2860_H2M_MAILBOX_STATUS, 0xffffffff);
12697a7e01caSKevin Lo }
1270069f1a80SAndrew Thompson
1271069f1a80SAndrew Thompson req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
1272069f1a80SAndrew Thompson req.bRequest = RT2870_RESET;
1273069f1a80SAndrew Thompson USETW(req.wValue, 8);
1274069f1a80SAndrew Thompson USETW(req.wIndex, 0);
1275069f1a80SAndrew Thompson USETW(req.wLength, 0);
1276329f91ffSKevin Lo if ((error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL))
1277329f91ffSKevin Lo != 0) {
127897ccc890SAndrew Thompson device_printf(sc->sc_dev, "firmware reset failed\n");
12798b1d9a81SAndrew Thompson goto fail;
12808b1d9a81SAndrew Thompson }
1281069f1a80SAndrew Thompson
1282069f1a80SAndrew Thompson run_delay(sc, 10);
1283069f1a80SAndrew Thompson
12847a7e01caSKevin Lo run_write(sc, RT2860_H2M_BBPAGENT, 0);
1285069f1a80SAndrew Thompson run_write(sc, RT2860_H2M_MAILBOX, 0);
12867a7e01caSKevin Lo run_write(sc, RT2860_H2M_INTSRC, 0);
12873707a5e9SAndrew Thompson if ((error = run_mcu_cmd(sc, RT2860_MCU_CMD_RFRESET, 0)) != 0)
12888b1d9a81SAndrew Thompson goto fail;
1289069f1a80SAndrew Thompson
1290069f1a80SAndrew Thompson /* wait until microcontroller is ready */
1291069f1a80SAndrew Thompson for (ntries = 0; ntries < 1000; ntries++) {
12927a7e01caSKevin Lo if ((error = run_read(sc, RT2860_SYS_CTRL, &tmp)) != 0)
12938b1d9a81SAndrew Thompson goto fail;
1294069f1a80SAndrew Thompson if (tmp & RT2860_MCU_READY)
1295069f1a80SAndrew Thompson break;
1296069f1a80SAndrew Thompson run_delay(sc, 10);
1297069f1a80SAndrew Thompson }
1298069f1a80SAndrew Thompson if (ntries == 1000) {
129997ccc890SAndrew Thompson device_printf(sc->sc_dev,
130097ccc890SAndrew Thompson "timeout waiting for MCU to initialize\n");
13018b1d9a81SAndrew Thompson error = ETIMEDOUT;
13028b1d9a81SAndrew Thompson goto fail;
1303069f1a80SAndrew Thompson }
1304f9d03266SBernhard Schmidt device_printf(sc->sc_dev, "firmware %s ver. %u.%u loaded\n",
1305f9d03266SBernhard Schmidt (base == fw->data) ? "RT2870" : "RT3071",
1306f9d03266SBernhard Schmidt *(base + 4092), *(base + 4093));
1307069f1a80SAndrew Thompson
13088b1d9a81SAndrew Thompson fail:
13098b1d9a81SAndrew Thompson firmware_put(fw, FIRMWARE_UNLOAD);
13108b1d9a81SAndrew Thompson return (error);
1311069f1a80SAndrew Thompson }
1312069f1a80SAndrew Thompson
1313beb13f04SHans Petter Selasky static int
run_reset(struct run_softc * sc)1314069f1a80SAndrew Thompson run_reset(struct run_softc *sc)
1315069f1a80SAndrew Thompson {
1316069f1a80SAndrew Thompson usb_device_request_t req;
1317069f1a80SAndrew Thompson
1318069f1a80SAndrew Thompson req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
1319069f1a80SAndrew Thompson req.bRequest = RT2870_RESET;
1320069f1a80SAndrew Thompson USETW(req.wValue, 1);
1321069f1a80SAndrew Thompson USETW(req.wIndex, 0);
1322069f1a80SAndrew Thompson USETW(req.wLength, 0);
132375c76159SAndrew Thompson return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
1324069f1a80SAndrew Thompson }
1325069f1a80SAndrew Thompson
1326069f1a80SAndrew Thompson static usb_error_t
run_do_request(struct run_softc * sc,struct usb_device_request * req,void * data)1327069f1a80SAndrew Thompson run_do_request(struct run_softc *sc,
1328069f1a80SAndrew Thompson struct usb_device_request *req, void *data)
1329069f1a80SAndrew Thompson {
1330069f1a80SAndrew Thompson usb_error_t err;
1331069f1a80SAndrew Thompson int ntries = 10;
1332069f1a80SAndrew Thompson
1333069f1a80SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
1334069f1a80SAndrew Thompson
1335069f1a80SAndrew Thompson while (ntries--) {
1336069f1a80SAndrew Thompson err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
1337069f1a80SAndrew Thompson req, data, 0, NULL, 250 /* ms */);
1338069f1a80SAndrew Thompson if (err == 0)
1339069f1a80SAndrew Thompson break;
1340109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_USB,
1341109005ccSGavin Atkinson "Control request failed, %s (retrying)\n",
1342069f1a80SAndrew Thompson usbd_errstr(err));
1343069f1a80SAndrew Thompson run_delay(sc, 10);
1344069f1a80SAndrew Thompson }
1345069f1a80SAndrew Thompson return (err);
1346069f1a80SAndrew Thompson }
1347069f1a80SAndrew Thompson
1348069f1a80SAndrew Thompson static int
run_read(struct run_softc * sc,uint16_t reg,uint32_t * val)1349069f1a80SAndrew Thompson run_read(struct run_softc *sc, uint16_t reg, uint32_t *val)
1350069f1a80SAndrew Thompson {
1351069f1a80SAndrew Thompson uint32_t tmp;
1352069f1a80SAndrew Thompson int error;
1353069f1a80SAndrew Thompson
1354069f1a80SAndrew Thompson error = run_read_region_1(sc, reg, (uint8_t *)&tmp, sizeof tmp);
1355069f1a80SAndrew Thompson if (error == 0)
1356069f1a80SAndrew Thompson *val = le32toh(tmp);
1357069f1a80SAndrew Thompson else
1358069f1a80SAndrew Thompson *val = 0xffffffff;
135975c76159SAndrew Thompson return (error);
1360069f1a80SAndrew Thompson }
1361069f1a80SAndrew Thompson
1362069f1a80SAndrew Thompson static int
run_read_region_1(struct run_softc * sc,uint16_t reg,uint8_t * buf,int len)1363069f1a80SAndrew Thompson run_read_region_1(struct run_softc *sc, uint16_t reg, uint8_t *buf, int len)
1364069f1a80SAndrew Thompson {
1365069f1a80SAndrew Thompson usb_device_request_t req;
1366069f1a80SAndrew Thompson
1367069f1a80SAndrew Thompson req.bmRequestType = UT_READ_VENDOR_DEVICE;
1368069f1a80SAndrew Thompson req.bRequest = RT2870_READ_REGION_1;
1369069f1a80SAndrew Thompson USETW(req.wValue, 0);
1370069f1a80SAndrew Thompson USETW(req.wIndex, reg);
1371069f1a80SAndrew Thompson USETW(req.wLength, len);
1372069f1a80SAndrew Thompson
137375c76159SAndrew Thompson return (run_do_request(sc, &req, buf));
1374069f1a80SAndrew Thompson }
1375069f1a80SAndrew Thompson
1376069f1a80SAndrew Thompson static int
run_write_2(struct run_softc * sc,uint16_t reg,uint16_t val)1377069f1a80SAndrew Thompson run_write_2(struct run_softc *sc, uint16_t reg, uint16_t val)
1378069f1a80SAndrew Thompson {
1379069f1a80SAndrew Thompson usb_device_request_t req;
1380069f1a80SAndrew Thompson
1381069f1a80SAndrew Thompson req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
1382069f1a80SAndrew Thompson req.bRequest = RT2870_WRITE_2;
1383069f1a80SAndrew Thompson USETW(req.wValue, val);
1384069f1a80SAndrew Thompson USETW(req.wIndex, reg);
1385069f1a80SAndrew Thompson USETW(req.wLength, 0);
1386069f1a80SAndrew Thompson
138775c76159SAndrew Thompson return (run_do_request(sc, &req, NULL));
1388069f1a80SAndrew Thompson }
1389069f1a80SAndrew Thompson
1390069f1a80SAndrew Thompson static int
run_write(struct run_softc * sc,uint16_t reg,uint32_t val)1391069f1a80SAndrew Thompson run_write(struct run_softc *sc, uint16_t reg, uint32_t val)
1392069f1a80SAndrew Thompson {
1393069f1a80SAndrew Thompson int error;
1394069f1a80SAndrew Thompson
1395069f1a80SAndrew Thompson if ((error = run_write_2(sc, reg, val & 0xffff)) == 0)
1396069f1a80SAndrew Thompson error = run_write_2(sc, reg + 2, val >> 16);
139775c76159SAndrew Thompson return (error);
1398069f1a80SAndrew Thompson }
1399069f1a80SAndrew Thompson
1400069f1a80SAndrew Thompson static int
run_write_region_1(struct run_softc * sc,uint16_t reg,const uint8_t * buf,int len)1401069f1a80SAndrew Thompson run_write_region_1(struct run_softc *sc, uint16_t reg, const uint8_t *buf,
1402069f1a80SAndrew Thompson int len)
1403069f1a80SAndrew Thompson {
1404069f1a80SAndrew Thompson #if 1
1405069f1a80SAndrew Thompson int i, error = 0;
1406069f1a80SAndrew Thompson /*
1407069f1a80SAndrew Thompson * NB: the WRITE_REGION_1 command is not stable on RT2860.
1408069f1a80SAndrew Thompson * We thus issue multiple WRITE_2 commands instead.
1409069f1a80SAndrew Thompson */
1410069f1a80SAndrew Thompson KASSERT((len & 1) == 0, ("run_write_region_1: Data too long.\n"));
1411069f1a80SAndrew Thompson for (i = 0; i < len && error == 0; i += 2)
1412069f1a80SAndrew Thompson error = run_write_2(sc, reg + i, buf[i] | buf[i + 1] << 8);
141375c76159SAndrew Thompson return (error);
1414069f1a80SAndrew Thompson #else
1415069f1a80SAndrew Thompson usb_device_request_t req;
141667e73b2bSKevin Lo int error = 0;
141767e73b2bSKevin Lo
141867e73b2bSKevin Lo /*
141967e73b2bSKevin Lo * NOTE: It appears the WRITE_REGION_1 command cannot be
142067e73b2bSKevin Lo * passed a huge amount of data, which will crash the
142167e73b2bSKevin Lo * firmware. Limit amount of data passed to 64-bytes at a
142267e73b2bSKevin Lo * time.
142367e73b2bSKevin Lo */
142467e73b2bSKevin Lo while (len > 0) {
142567e73b2bSKevin Lo int delta = 64;
142667e73b2bSKevin Lo if (delta > len)
142767e73b2bSKevin Lo delta = len;
1428069f1a80SAndrew Thompson
1429069f1a80SAndrew Thompson req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
1430069f1a80SAndrew Thompson req.bRequest = RT2870_WRITE_REGION_1;
1431069f1a80SAndrew Thompson USETW(req.wValue, 0);
1432069f1a80SAndrew Thompson USETW(req.wIndex, reg);
143367e73b2bSKevin Lo USETW(req.wLength, delta);
143467e73b2bSKevin Lo error = run_do_request(sc, &req, __DECONST(uint8_t *, buf));
143567e73b2bSKevin Lo if (error != 0)
143667e73b2bSKevin Lo break;
143767e73b2bSKevin Lo reg += delta;
143867e73b2bSKevin Lo buf += delta;
143967e73b2bSKevin Lo len -= delta;
144067e73b2bSKevin Lo }
144167e73b2bSKevin Lo return (error);
1442069f1a80SAndrew Thompson #endif
1443069f1a80SAndrew Thompson }
1444069f1a80SAndrew Thompson
1445069f1a80SAndrew Thompson static int
run_set_region_4(struct run_softc * sc,uint16_t reg,uint32_t val,int len)1446069f1a80SAndrew Thompson run_set_region_4(struct run_softc *sc, uint16_t reg, uint32_t val, int len)
1447069f1a80SAndrew Thompson {
1448069f1a80SAndrew Thompson int i, error = 0;
1449069f1a80SAndrew Thompson
1450069f1a80SAndrew Thompson KASSERT((len & 3) == 0, ("run_set_region_4: Invalid data length.\n"));
1451069f1a80SAndrew Thompson for (i = 0; i < len && error == 0; i += 4)
1452069f1a80SAndrew Thompson error = run_write(sc, reg + i, val);
145375c76159SAndrew Thompson return (error);
1454069f1a80SAndrew Thompson }
1455069f1a80SAndrew Thompson
1456069f1a80SAndrew Thompson static int
run_efuse_read(struct run_softc * sc,uint16_t addr,uint16_t * val,int count)1457010b13faSKevin Lo run_efuse_read(struct run_softc *sc, uint16_t addr, uint16_t *val, int count)
1458069f1a80SAndrew Thompson {
1459069f1a80SAndrew Thompson uint32_t tmp;
1460069f1a80SAndrew Thompson uint16_t reg;
1461069f1a80SAndrew Thompson int error, ntries;
1462069f1a80SAndrew Thompson
1463069f1a80SAndrew Thompson if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0)
146475c76159SAndrew Thompson return (error);
1465069f1a80SAndrew Thompson
14662719d9c9SHans Petter Selasky if (count == 2)
14672719d9c9SHans Petter Selasky addr *= 2;
1468069f1a80SAndrew Thompson /*-
1469069f1a80SAndrew Thompson * Read one 16-byte block into registers EFUSE_DATA[0-3]:
1470069f1a80SAndrew Thompson * DATA0: F E D C
1471069f1a80SAndrew Thompson * DATA1: B A 9 8
1472069f1a80SAndrew Thompson * DATA2: 7 6 5 4
1473069f1a80SAndrew Thompson * DATA3: 3 2 1 0
1474069f1a80SAndrew Thompson */
1475069f1a80SAndrew Thompson tmp &= ~(RT3070_EFSROM_MODE_MASK | RT3070_EFSROM_AIN_MASK);
1476069f1a80SAndrew Thompson tmp |= (addr & ~0xf) << RT3070_EFSROM_AIN_SHIFT | RT3070_EFSROM_KICK;
1477069f1a80SAndrew Thompson run_write(sc, RT3070_EFUSE_CTRL, tmp);
1478069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
1479069f1a80SAndrew Thompson if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0)
148075c76159SAndrew Thompson return (error);
1481069f1a80SAndrew Thompson if (!(tmp & RT3070_EFSROM_KICK))
1482069f1a80SAndrew Thompson break;
1483069f1a80SAndrew Thompson run_delay(sc, 2);
1484069f1a80SAndrew Thompson }
1485069f1a80SAndrew Thompson if (ntries == 100)
148675c76159SAndrew Thompson return (ETIMEDOUT);
1487069f1a80SAndrew Thompson
14882719d9c9SHans Petter Selasky if ((tmp & RT3070_EFUSE_AOUT_MASK) == RT3070_EFUSE_AOUT_MASK) {
14892719d9c9SHans Petter Selasky *val = 0xffff; /* address not found */
149075c76159SAndrew Thompson return (0);
14912719d9c9SHans Petter Selasky }
1492069f1a80SAndrew Thompson /* determine to which 32-bit register our 16-bit word belongs */
1493069f1a80SAndrew Thompson reg = RT3070_EFUSE_DATA3 - (addr & 0xc);
1494069f1a80SAndrew Thompson if ((error = run_read(sc, reg, &tmp)) != 0)
149575c76159SAndrew Thompson return (error);
1496069f1a80SAndrew Thompson
14972719d9c9SHans Petter Selasky tmp >>= (8 * (addr & 0x3));
149856ae0785SKevin Lo *val = (addr & 1) ? tmp >> 16 : tmp & 0xffff;
149956ae0785SKevin Lo
150075c76159SAndrew Thompson return (0);
1501069f1a80SAndrew Thompson }
1502069f1a80SAndrew Thompson
150351fcfa26SKevin Lo /* Read 16-bit from eFUSE ROM for RT3xxx. */
1504010b13faSKevin Lo static int
run_efuse_read_2(struct run_softc * sc,uint16_t addr,uint16_t * val)1505010b13faSKevin Lo run_efuse_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val)
1506010b13faSKevin Lo {
1507010b13faSKevin Lo return (run_efuse_read(sc, addr, val, 2));
1508010b13faSKevin Lo }
1509010b13faSKevin Lo
1510069f1a80SAndrew Thompson static int
run_eeprom_read_2(struct run_softc * sc,uint16_t addr,uint16_t * val)1511069f1a80SAndrew Thompson run_eeprom_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val)
1512069f1a80SAndrew Thompson {
1513069f1a80SAndrew Thompson usb_device_request_t req;
1514069f1a80SAndrew Thompson uint16_t tmp;
1515069f1a80SAndrew Thompson int error;
1516069f1a80SAndrew Thompson
1517069f1a80SAndrew Thompson addr *= 2;
1518069f1a80SAndrew Thompson req.bmRequestType = UT_READ_VENDOR_DEVICE;
1519069f1a80SAndrew Thompson req.bRequest = RT2870_EEPROM_READ;
1520069f1a80SAndrew Thompson USETW(req.wValue, 0);
1521069f1a80SAndrew Thompson USETW(req.wIndex, addr);
15227a7e01caSKevin Lo USETW(req.wLength, sizeof(tmp));
1523069f1a80SAndrew Thompson
1524069f1a80SAndrew Thompson error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &tmp);
1525069f1a80SAndrew Thompson if (error == 0)
1526069f1a80SAndrew Thompson *val = le16toh(tmp);
1527069f1a80SAndrew Thompson else
1528069f1a80SAndrew Thompson *val = 0xffff;
152975c76159SAndrew Thompson return (error);
1530069f1a80SAndrew Thompson }
1531069f1a80SAndrew Thompson
1532069f1a80SAndrew Thompson static __inline int
run_srom_read(struct run_softc * sc,uint16_t addr,uint16_t * val)1533069f1a80SAndrew Thompson run_srom_read(struct run_softc *sc, uint16_t addr, uint16_t *val)
1534069f1a80SAndrew Thompson {
1535069f1a80SAndrew Thompson /* either eFUSE ROM or EEPROM */
1536069f1a80SAndrew Thompson return sc->sc_srom_read(sc, addr, val);
1537069f1a80SAndrew Thompson }
1538069f1a80SAndrew Thompson
1539069f1a80SAndrew Thompson static int
run_rt2870_rf_write(struct run_softc * sc,uint32_t val)15405b751464SKevin Lo run_rt2870_rf_write(struct run_softc *sc, uint32_t val)
1541069f1a80SAndrew Thompson {
1542069f1a80SAndrew Thompson uint32_t tmp;
1543069f1a80SAndrew Thompson int error, ntries;
1544069f1a80SAndrew Thompson
1545069f1a80SAndrew Thompson for (ntries = 0; ntries < 10; ntries++) {
1546069f1a80SAndrew Thompson if ((error = run_read(sc, RT2860_RF_CSR_CFG0, &tmp)) != 0)
154775c76159SAndrew Thompson return (error);
1548069f1a80SAndrew Thompson if (!(tmp & RT2860_RF_REG_CTRL))
1549069f1a80SAndrew Thompson break;
1550069f1a80SAndrew Thompson }
1551069f1a80SAndrew Thompson if (ntries == 10)
155275c76159SAndrew Thompson return (ETIMEDOUT);
1553069f1a80SAndrew Thompson
15545f7e329cSKevin Lo return (run_write(sc, RT2860_RF_CSR_CFG0, val));
1555069f1a80SAndrew Thompson }
1556069f1a80SAndrew Thompson
1557069f1a80SAndrew Thompson static int
run_rt3070_rf_read(struct run_softc * sc,uint8_t reg,uint8_t * val)1558069f1a80SAndrew Thompson run_rt3070_rf_read(struct run_softc *sc, uint8_t reg, uint8_t *val)
1559069f1a80SAndrew Thompson {
1560069f1a80SAndrew Thompson uint32_t tmp;
1561069f1a80SAndrew Thompson int error, ntries;
1562069f1a80SAndrew Thompson
1563069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
1564069f1a80SAndrew Thompson if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0)
156575c76159SAndrew Thompson return (error);
1566069f1a80SAndrew Thompson if (!(tmp & RT3070_RF_KICK))
1567069f1a80SAndrew Thompson break;
1568069f1a80SAndrew Thompson }
1569069f1a80SAndrew Thompson if (ntries == 100)
157075c76159SAndrew Thompson return (ETIMEDOUT);
1571069f1a80SAndrew Thompson
1572069f1a80SAndrew Thompson tmp = RT3070_RF_KICK | reg << 8;
1573069f1a80SAndrew Thompson if ((error = run_write(sc, RT3070_RF_CSR_CFG, tmp)) != 0)
157475c76159SAndrew Thompson return (error);
1575069f1a80SAndrew Thompson
1576069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
1577069f1a80SAndrew Thompson if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0)
157875c76159SAndrew Thompson return (error);
1579069f1a80SAndrew Thompson if (!(tmp & RT3070_RF_KICK))
1580069f1a80SAndrew Thompson break;
1581069f1a80SAndrew Thompson }
1582069f1a80SAndrew Thompson if (ntries == 100)
158375c76159SAndrew Thompson return (ETIMEDOUT);
1584069f1a80SAndrew Thompson
1585069f1a80SAndrew Thompson *val = tmp & 0xff;
158675c76159SAndrew Thompson return (0);
1587069f1a80SAndrew Thompson }
1588069f1a80SAndrew Thompson
1589069f1a80SAndrew Thompson static int
run_rt3070_rf_write(struct run_softc * sc,uint8_t reg,uint8_t val)1590069f1a80SAndrew Thompson run_rt3070_rf_write(struct run_softc *sc, uint8_t reg, uint8_t val)
1591069f1a80SAndrew Thompson {
1592069f1a80SAndrew Thompson uint32_t tmp;
1593069f1a80SAndrew Thompson int error, ntries;
1594069f1a80SAndrew Thompson
1595069f1a80SAndrew Thompson for (ntries = 0; ntries < 10; ntries++) {
1596069f1a80SAndrew Thompson if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0)
159775c76159SAndrew Thompson return (error);
1598069f1a80SAndrew Thompson if (!(tmp & RT3070_RF_KICK))
1599069f1a80SAndrew Thompson break;
1600069f1a80SAndrew Thompson }
1601069f1a80SAndrew Thompson if (ntries == 10)
160275c76159SAndrew Thompson return (ETIMEDOUT);
1603069f1a80SAndrew Thompson
1604069f1a80SAndrew Thompson tmp = RT3070_RF_WRITE | RT3070_RF_KICK | reg << 8 | val;
160575c76159SAndrew Thompson return (run_write(sc, RT3070_RF_CSR_CFG, tmp));
1606069f1a80SAndrew Thompson }
1607069f1a80SAndrew Thompson
1608069f1a80SAndrew Thompson static int
run_bbp_read(struct run_softc * sc,uint8_t reg,uint8_t * val)1609069f1a80SAndrew Thompson run_bbp_read(struct run_softc *sc, uint8_t reg, uint8_t *val)
1610069f1a80SAndrew Thompson {
1611069f1a80SAndrew Thompson uint32_t tmp;
1612069f1a80SAndrew Thompson int ntries, error;
1613069f1a80SAndrew Thompson
1614069f1a80SAndrew Thompson for (ntries = 0; ntries < 10; ntries++) {
1615069f1a80SAndrew Thompson if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0)
161675c76159SAndrew Thompson return (error);
1617069f1a80SAndrew Thompson if (!(tmp & RT2860_BBP_CSR_KICK))
1618069f1a80SAndrew Thompson break;
1619069f1a80SAndrew Thompson }
1620069f1a80SAndrew Thompson if (ntries == 10)
162175c76159SAndrew Thompson return (ETIMEDOUT);
1622069f1a80SAndrew Thompson
1623069f1a80SAndrew Thompson tmp = RT2860_BBP_CSR_READ | RT2860_BBP_CSR_KICK | reg << 8;
1624069f1a80SAndrew Thompson if ((error = run_write(sc, RT2860_BBP_CSR_CFG, tmp)) != 0)
162575c76159SAndrew Thompson return (error);
1626069f1a80SAndrew Thompson
1627069f1a80SAndrew Thompson for (ntries = 0; ntries < 10; ntries++) {
1628069f1a80SAndrew Thompson if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0)
162975c76159SAndrew Thompson return (error);
1630069f1a80SAndrew Thompson if (!(tmp & RT2860_BBP_CSR_KICK))
1631069f1a80SAndrew Thompson break;
1632069f1a80SAndrew Thompson }
1633069f1a80SAndrew Thompson if (ntries == 10)
163475c76159SAndrew Thompson return (ETIMEDOUT);
1635069f1a80SAndrew Thompson
1636069f1a80SAndrew Thompson *val = tmp & 0xff;
163775c76159SAndrew Thompson return (0);
1638069f1a80SAndrew Thompson }
1639069f1a80SAndrew Thompson
1640069f1a80SAndrew Thompson static int
run_bbp_write(struct run_softc * sc,uint8_t reg,uint8_t val)1641069f1a80SAndrew Thompson run_bbp_write(struct run_softc *sc, uint8_t reg, uint8_t val)
1642069f1a80SAndrew Thompson {
1643069f1a80SAndrew Thompson uint32_t tmp;
1644069f1a80SAndrew Thompson int ntries, error;
1645069f1a80SAndrew Thompson
1646069f1a80SAndrew Thompson for (ntries = 0; ntries < 10; ntries++) {
1647069f1a80SAndrew Thompson if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0)
164875c76159SAndrew Thompson return (error);
1649069f1a80SAndrew Thompson if (!(tmp & RT2860_BBP_CSR_KICK))
1650069f1a80SAndrew Thompson break;
1651069f1a80SAndrew Thompson }
1652069f1a80SAndrew Thompson if (ntries == 10)
165375c76159SAndrew Thompson return (ETIMEDOUT);
1654069f1a80SAndrew Thompson
1655069f1a80SAndrew Thompson tmp = RT2860_BBP_CSR_KICK | reg << 8 | val;
165675c76159SAndrew Thompson return (run_write(sc, RT2860_BBP_CSR_CFG, tmp));
1657069f1a80SAndrew Thompson }
1658069f1a80SAndrew Thompson
1659069f1a80SAndrew Thompson /*
1660069f1a80SAndrew Thompson * Send a command to the 8051 microcontroller unit.
1661069f1a80SAndrew Thompson */
1662069f1a80SAndrew Thompson static int
run_mcu_cmd(struct run_softc * sc,uint8_t cmd,uint16_t arg)1663069f1a80SAndrew Thompson run_mcu_cmd(struct run_softc *sc, uint8_t cmd, uint16_t arg)
1664069f1a80SAndrew Thompson {
1665069f1a80SAndrew Thompson uint32_t tmp;
1666069f1a80SAndrew Thompson int error, ntries;
1667069f1a80SAndrew Thompson
1668069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
1669069f1a80SAndrew Thompson if ((error = run_read(sc, RT2860_H2M_MAILBOX, &tmp)) != 0)
1670069f1a80SAndrew Thompson return error;
1671069f1a80SAndrew Thompson if (!(tmp & RT2860_H2M_BUSY))
1672069f1a80SAndrew Thompson break;
1673069f1a80SAndrew Thompson }
1674069f1a80SAndrew Thompson if (ntries == 100)
1675069f1a80SAndrew Thompson return ETIMEDOUT;
1676069f1a80SAndrew Thompson
1677069f1a80SAndrew Thompson tmp = RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg;
1678069f1a80SAndrew Thompson if ((error = run_write(sc, RT2860_H2M_MAILBOX, tmp)) == 0)
1679069f1a80SAndrew Thompson error = run_write(sc, RT2860_HOST_CMD, cmd);
168075c76159SAndrew Thompson return (error);
1681069f1a80SAndrew Thompson }
1682069f1a80SAndrew Thompson
1683069f1a80SAndrew Thompson /*
1684069f1a80SAndrew Thompson * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word.
1685069f1a80SAndrew Thompson * Used to adjust per-rate Tx power registers.
1686069f1a80SAndrew Thompson */
1687069f1a80SAndrew Thompson static __inline uint32_t
b4inc(uint32_t b32,int8_t delta)1688069f1a80SAndrew Thompson b4inc(uint32_t b32, int8_t delta)
1689069f1a80SAndrew Thompson {
1690069f1a80SAndrew Thompson int8_t i, b4;
1691069f1a80SAndrew Thompson
1692069f1a80SAndrew Thompson for (i = 0; i < 8; i++) {
1693069f1a80SAndrew Thompson b4 = b32 & 0xf;
1694069f1a80SAndrew Thompson b4 += delta;
1695069f1a80SAndrew Thompson if (b4 < 0)
1696069f1a80SAndrew Thompson b4 = 0;
1697069f1a80SAndrew Thompson else if (b4 > 0xf)
1698069f1a80SAndrew Thompson b4 = 0xf;
1699069f1a80SAndrew Thompson b32 = b32 >> 4 | b4 << 28;
1700069f1a80SAndrew Thompson }
170175c76159SAndrew Thompson return (b32);
1702069f1a80SAndrew Thompson }
1703069f1a80SAndrew Thompson
1704069f1a80SAndrew Thompson static const char *
run_get_rf(uint16_t rev)170564891211SKevin Lo run_get_rf(uint16_t rev)
1706069f1a80SAndrew Thompson {
1707069f1a80SAndrew Thompson switch (rev) {
1708069f1a80SAndrew Thompson case RT2860_RF_2820: return "RT2820";
1709069f1a80SAndrew Thompson case RT2860_RF_2850: return "RT2850";
1710069f1a80SAndrew Thompson case RT2860_RF_2720: return "RT2720";
1711069f1a80SAndrew Thompson case RT2860_RF_2750: return "RT2750";
1712069f1a80SAndrew Thompson case RT3070_RF_3020: return "RT3020";
1713069f1a80SAndrew Thompson case RT3070_RF_2020: return "RT2020";
1714069f1a80SAndrew Thompson case RT3070_RF_3021: return "RT3021";
1715069f1a80SAndrew Thompson case RT3070_RF_3022: return "RT3022";
1716069f1a80SAndrew Thompson case RT3070_RF_3052: return "RT3052";
17177a7e01caSKevin Lo case RT3593_RF_3053: return "RT3053";
1718242dbae3SKevin Lo case RT5592_RF_5592: return "RT5592";
171964891211SKevin Lo case RT5390_RF_5370: return "RT5370";
172064891211SKevin Lo case RT5390_RF_5372: return "RT5372";
1721069f1a80SAndrew Thompson }
172275c76159SAndrew Thompson return ("unknown");
1723069f1a80SAndrew Thompson }
1724069f1a80SAndrew Thompson
17257a7e01caSKevin Lo static void
run_rt3593_get_txpower(struct run_softc * sc)17267a7e01caSKevin Lo run_rt3593_get_txpower(struct run_softc *sc)
17277a7e01caSKevin Lo {
17287a7e01caSKevin Lo uint16_t addr, val;
17297a7e01caSKevin Lo int i;
17307a7e01caSKevin Lo
17317a7e01caSKevin Lo /* Read power settings for 2GHz channels. */
17327a7e01caSKevin Lo for (i = 0; i < 14; i += 2) {
17337a7e01caSKevin Lo addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE1 :
17347a7e01caSKevin Lo RT2860_EEPROM_PWR2GHZ_BASE1;
17357a7e01caSKevin Lo run_srom_read(sc, addr + i / 2, &val);
17367a7e01caSKevin Lo sc->txpow1[i + 0] = (int8_t)(val & 0xff);
17377a7e01caSKevin Lo sc->txpow1[i + 1] = (int8_t)(val >> 8);
17387a7e01caSKevin Lo
17397a7e01caSKevin Lo addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE2 :
17407a7e01caSKevin Lo RT2860_EEPROM_PWR2GHZ_BASE2;
17417a7e01caSKevin Lo run_srom_read(sc, addr + i / 2, &val);
17427a7e01caSKevin Lo sc->txpow2[i + 0] = (int8_t)(val & 0xff);
17437a7e01caSKevin Lo sc->txpow2[i + 1] = (int8_t)(val >> 8);
17447a7e01caSKevin Lo
17457a7e01caSKevin Lo if (sc->ntxchains == 3) {
17467a7e01caSKevin Lo run_srom_read(sc, RT3593_EEPROM_PWR2GHZ_BASE3 + i / 2,
17477a7e01caSKevin Lo &val);
17487a7e01caSKevin Lo sc->txpow3[i + 0] = (int8_t)(val & 0xff);
17497a7e01caSKevin Lo sc->txpow3[i + 1] = (int8_t)(val >> 8);
17507a7e01caSKevin Lo }
17517a7e01caSKevin Lo }
17527a7e01caSKevin Lo /* Fix broken Tx power entries. */
17537a7e01caSKevin Lo for (i = 0; i < 14; i++) {
1754030dda5dSKevin Lo if (sc->txpow1[i] > 31)
17557a7e01caSKevin Lo sc->txpow1[i] = 5;
1756030dda5dSKevin Lo if (sc->txpow2[i] > 31)
17577a7e01caSKevin Lo sc->txpow2[i] = 5;
17587a7e01caSKevin Lo if (sc->ntxchains == 3) {
1759030dda5dSKevin Lo if (sc->txpow3[i] > 31)
17607a7e01caSKevin Lo sc->txpow3[i] = 5;
17617a7e01caSKevin Lo }
17627a7e01caSKevin Lo }
17637a7e01caSKevin Lo /* Read power settings for 5GHz channels. */
17647a7e01caSKevin Lo for (i = 0; i < 40; i += 2) {
17657a7e01caSKevin Lo run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE1 + i / 2, &val);
17667a7e01caSKevin Lo sc->txpow1[i + 14] = (int8_t)(val & 0xff);
17677a7e01caSKevin Lo sc->txpow1[i + 15] = (int8_t)(val >> 8);
17687a7e01caSKevin Lo
17697a7e01caSKevin Lo run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE2 + i / 2, &val);
17707a7e01caSKevin Lo sc->txpow2[i + 14] = (int8_t)(val & 0xff);
17717a7e01caSKevin Lo sc->txpow2[i + 15] = (int8_t)(val >> 8);
17727a7e01caSKevin Lo
17737a7e01caSKevin Lo if (sc->ntxchains == 3) {
17747a7e01caSKevin Lo run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE3 + i / 2,
17757a7e01caSKevin Lo &val);
17767a7e01caSKevin Lo sc->txpow3[i + 14] = (int8_t)(val & 0xff);
17777a7e01caSKevin Lo sc->txpow3[i + 15] = (int8_t)(val >> 8);
17787a7e01caSKevin Lo }
17797a7e01caSKevin Lo }
17807a7e01caSKevin Lo }
17817a7e01caSKevin Lo
17827a7e01caSKevin Lo static void
run_get_txpower(struct run_softc * sc)17837a7e01caSKevin Lo run_get_txpower(struct run_softc *sc)
17847a7e01caSKevin Lo {
17857a7e01caSKevin Lo uint16_t val;
17867a7e01caSKevin Lo int i;
17877a7e01caSKevin Lo
17887a7e01caSKevin Lo /* Read power settings for 2GHz channels. */
17897a7e01caSKevin Lo for (i = 0; i < 14; i += 2) {
17907a7e01caSKevin Lo run_srom_read(sc, RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2, &val);
17917a7e01caSKevin Lo sc->txpow1[i + 0] = (int8_t)(val & 0xff);
17927a7e01caSKevin Lo sc->txpow1[i + 1] = (int8_t)(val >> 8);
17937a7e01caSKevin Lo
17947a7e01caSKevin Lo if (sc->mac_ver != 0x5390) {
17957a7e01caSKevin Lo run_srom_read(sc,
17967a7e01caSKevin Lo RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2, &val);
17977a7e01caSKevin Lo sc->txpow2[i + 0] = (int8_t)(val & 0xff);
17987a7e01caSKevin Lo sc->txpow2[i + 1] = (int8_t)(val >> 8);
17997a7e01caSKevin Lo }
18007a7e01caSKevin Lo }
18017a7e01caSKevin Lo /* Fix broken Tx power entries. */
18027a7e01caSKevin Lo for (i = 0; i < 14; i++) {
18037a7e01caSKevin Lo if (sc->mac_ver >= 0x5390) {
180424c838f0SKevin Lo if (sc->txpow1[i] < 0 || sc->txpow1[i] > 39)
18057a7e01caSKevin Lo sc->txpow1[i] = 5;
18067a7e01caSKevin Lo } else {
18077a7e01caSKevin Lo if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31)
18087a7e01caSKevin Lo sc->txpow1[i] = 5;
18097a7e01caSKevin Lo }
18107a7e01caSKevin Lo if (sc->mac_ver > 0x5390) {
181124c838f0SKevin Lo if (sc->txpow2[i] < 0 || sc->txpow2[i] > 39)
18127a7e01caSKevin Lo sc->txpow2[i] = 5;
18137a7e01caSKevin Lo } else if (sc->mac_ver < 0x5390) {
18147a7e01caSKevin Lo if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31)
18157a7e01caSKevin Lo sc->txpow2[i] = 5;
18167a7e01caSKevin Lo }
1817109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_TXPWR,
1818109005ccSGavin Atkinson "chan %d: power1=%d, power2=%d\n",
18197a7e01caSKevin Lo rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i]);
18207a7e01caSKevin Lo }
18217a7e01caSKevin Lo /* Read power settings for 5GHz channels. */
18227a7e01caSKevin Lo for (i = 0; i < 40; i += 2) {
18237a7e01caSKevin Lo run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2, &val);
18247a7e01caSKevin Lo sc->txpow1[i + 14] = (int8_t)(val & 0xff);
18257a7e01caSKevin Lo sc->txpow1[i + 15] = (int8_t)(val >> 8);
18267a7e01caSKevin Lo
18277a7e01caSKevin Lo run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2, &val);
18287a7e01caSKevin Lo sc->txpow2[i + 14] = (int8_t)(val & 0xff);
18297a7e01caSKevin Lo sc->txpow2[i + 15] = (int8_t)(val >> 8);
18307a7e01caSKevin Lo }
18317a7e01caSKevin Lo /* Fix broken Tx power entries. */
18327a7e01caSKevin Lo for (i = 0; i < 40; i++ ) {
18337a7e01caSKevin Lo if (sc->mac_ver != 0x5592) {
18347a7e01caSKevin Lo if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15)
18357a7e01caSKevin Lo sc->txpow1[14 + i] = 5;
18367a7e01caSKevin Lo if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15)
18377a7e01caSKevin Lo sc->txpow2[14 + i] = 5;
18387a7e01caSKevin Lo }
1839109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_TXPWR,
1840109005ccSGavin Atkinson "chan %d: power1=%d, power2=%d\n",
18417a7e01caSKevin Lo rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i],
18427a7e01caSKevin Lo sc->txpow2[14 + i]);
18437a7e01caSKevin Lo }
18447a7e01caSKevin Lo }
18457a7e01caSKevin Lo
1846beb13f04SHans Petter Selasky static int
run_read_eeprom(struct run_softc * sc)1847069f1a80SAndrew Thompson run_read_eeprom(struct run_softc *sc)
1848069f1a80SAndrew Thompson {
18497a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
1850069f1a80SAndrew Thompson int8_t delta_2ghz, delta_5ghz;
1851069f1a80SAndrew Thompson uint32_t tmp;
1852069f1a80SAndrew Thompson uint16_t val;
1853069f1a80SAndrew Thompson int ridx, ant, i;
1854069f1a80SAndrew Thompson
1855069f1a80SAndrew Thompson /* check whether the ROM is eFUSE ROM or EEPROM */
1856069f1a80SAndrew Thompson sc->sc_srom_read = run_eeprom_read_2;
18573707a5e9SAndrew Thompson if (sc->mac_ver >= 0x3070) {
1858069f1a80SAndrew Thompson run_read(sc, RT3070_EFUSE_CTRL, &tmp);
1859109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EFUSE_CTRL=0x%08x\n", tmp);
186056ae0785SKevin Lo if ((tmp & RT3070_SEL_EFUSE) || sc->mac_ver == 0x3593)
1861069f1a80SAndrew Thompson sc->sc_srom_read = run_efuse_read_2;
1862069f1a80SAndrew Thompson }
1863069f1a80SAndrew Thompson
1864069f1a80SAndrew Thompson /* read ROM version */
1865069f1a80SAndrew Thompson run_srom_read(sc, RT2860_EEPROM_VERSION, &val);
1866109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM,
1867109005ccSGavin Atkinson "EEPROM rev=%d, FAE=%d\n", val >> 8, val & 0xff);
1868069f1a80SAndrew Thompson
1869069f1a80SAndrew Thompson /* read MAC address */
1870069f1a80SAndrew Thompson run_srom_read(sc, RT2860_EEPROM_MAC01, &val);
18717a79cebfSGleb Smirnoff ic->ic_macaddr[0] = val & 0xff;
18727a79cebfSGleb Smirnoff ic->ic_macaddr[1] = val >> 8;
1873069f1a80SAndrew Thompson run_srom_read(sc, RT2860_EEPROM_MAC23, &val);
18747a79cebfSGleb Smirnoff ic->ic_macaddr[2] = val & 0xff;
18757a79cebfSGleb Smirnoff ic->ic_macaddr[3] = val >> 8;
1876069f1a80SAndrew Thompson run_srom_read(sc, RT2860_EEPROM_MAC45, &val);
18777a79cebfSGleb Smirnoff ic->ic_macaddr[4] = val & 0xff;
18787a79cebfSGleb Smirnoff ic->ic_macaddr[5] = val >> 8;
1879069f1a80SAndrew Thompson
18807a7e01caSKevin Lo if (sc->mac_ver < 0x3593) {
18813707a5e9SAndrew Thompson /* read vender BBP settings */
18823707a5e9SAndrew Thompson for (i = 0; i < 10; i++) {
1883069f1a80SAndrew Thompson run_srom_read(sc, RT2860_EEPROM_BBP_BASE + i, &val);
1884069f1a80SAndrew Thompson sc->bbp[i].val = val & 0xff;
1885069f1a80SAndrew Thompson sc->bbp[i].reg = val >> 8;
1886109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM,
1887109005ccSGavin Atkinson "BBP%d=0x%02x\n", sc->bbp[i].reg, sc->bbp[i].val);
1888069f1a80SAndrew Thompson }
18893707a5e9SAndrew Thompson if (sc->mac_ver >= 0x3071) {
18903707a5e9SAndrew Thompson /* read vendor RF settings */
18913707a5e9SAndrew Thompson for (i = 0; i < 10; i++) {
189264891211SKevin Lo run_srom_read(sc, RT3071_EEPROM_RF_BASE + i,
189364891211SKevin Lo &val);
18943707a5e9SAndrew Thompson sc->rf[i].val = val & 0xff;
18953707a5e9SAndrew Thompson sc->rf[i].reg = val >> 8;
1896109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM, "RF%d=0x%02x\n",
1897109005ccSGavin Atkinson sc->rf[i].reg, sc->rf[i].val);
18983707a5e9SAndrew Thompson }
18993707a5e9SAndrew Thompson }
190064891211SKevin Lo }
1901069f1a80SAndrew Thompson
1902069f1a80SAndrew Thompson /* read RF frequency offset from EEPROM */
19037a7e01caSKevin Lo run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS :
19047a7e01caSKevin Lo RT3593_EEPROM_FREQ, &val);
1905069f1a80SAndrew Thompson sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0;
1906109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM freq offset %d\n",
1907109005ccSGavin Atkinson sc->freq & 0xff);
1908069f1a80SAndrew Thompson
19097a7e01caSKevin Lo run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS :
19107a7e01caSKevin Lo RT3593_EEPROM_FREQ_LEDS, &val);
19113707a5e9SAndrew Thompson if (val >> 8 != 0xff) {
1912069f1a80SAndrew Thompson /* read LEDs operating mode */
19133707a5e9SAndrew Thompson sc->leds = val >> 8;
19147a7e01caSKevin Lo run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED1 :
19157a7e01caSKevin Lo RT3593_EEPROM_LED1, &sc->led[0]);
19167a7e01caSKevin Lo run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED2 :
19177a7e01caSKevin Lo RT3593_EEPROM_LED2, &sc->led[1]);
19187a7e01caSKevin Lo run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED3 :
19197a7e01caSKevin Lo RT3593_EEPROM_LED3, &sc->led[2]);
1920069f1a80SAndrew Thompson } else {
1921069f1a80SAndrew Thompson /* broken EEPROM, use default settings */
1922069f1a80SAndrew Thompson sc->leds = 0x01;
1923069f1a80SAndrew Thompson sc->led[0] = 0x5555;
1924069f1a80SAndrew Thompson sc->led[1] = 0x2221;
1925069f1a80SAndrew Thompson sc->led[2] = 0x5627; /* differs from RT2860 */
1926069f1a80SAndrew Thompson }
1927109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM,
1928109005ccSGavin Atkinson "EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n",
1929069f1a80SAndrew Thompson sc->leds, sc->led[0], sc->led[1], sc->led[2]);
1930069f1a80SAndrew Thompson
1931069f1a80SAndrew Thompson /* read RF information */
1932242dbae3SKevin Lo if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392)
193364891211SKevin Lo run_srom_read(sc, 0x00, &val);
193464891211SKevin Lo else
1935069f1a80SAndrew Thompson run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val);
193664891211SKevin Lo
1937069f1a80SAndrew Thompson if (val == 0xffff) {
19387a7e01caSKevin Lo device_printf(sc->sc_dev,
19397a7e01caSKevin Lo "invalid EEPROM antenna info, using default\n");
19403707a5e9SAndrew Thompson if (sc->mac_ver == 0x3572) {
19413707a5e9SAndrew Thompson /* default to RF3052 2T2R */
19423707a5e9SAndrew Thompson sc->rf_rev = RT3070_RF_3052;
19433707a5e9SAndrew Thompson sc->ntxchains = 2;
19443707a5e9SAndrew Thompson sc->nrxchains = 2;
19453707a5e9SAndrew Thompson } else if (sc->mac_ver >= 0x3070) {
1946069f1a80SAndrew Thompson /* default to RF3020 1T1R */
1947069f1a80SAndrew Thompson sc->rf_rev = RT3070_RF_3020;
1948069f1a80SAndrew Thompson sc->ntxchains = 1;
1949069f1a80SAndrew Thompson sc->nrxchains = 1;
1950069f1a80SAndrew Thompson } else {
1951069f1a80SAndrew Thompson /* default to RF2820 1T2R */
1952069f1a80SAndrew Thompson sc->rf_rev = RT2860_RF_2820;
1953069f1a80SAndrew Thompson sc->ntxchains = 1;
1954069f1a80SAndrew Thompson sc->nrxchains = 2;
1955069f1a80SAndrew Thompson }
1956069f1a80SAndrew Thompson } else {
1957242dbae3SKevin Lo if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392) {
195864891211SKevin Lo sc->rf_rev = val;
195964891211SKevin Lo run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val);
196064891211SKevin Lo } else
1961069f1a80SAndrew Thompson sc->rf_rev = (val >> 8) & 0xf;
1962069f1a80SAndrew Thompson sc->ntxchains = (val >> 4) & 0xf;
1963069f1a80SAndrew Thompson sc->nrxchains = val & 0xf;
1964069f1a80SAndrew Thompson }
1965109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM RF rev=0x%04x chains=%dT%dR\n",
1966069f1a80SAndrew Thompson sc->rf_rev, sc->ntxchains, sc->nrxchains);
1967069f1a80SAndrew Thompson
196885e7bb81SAndrew Thompson /* check if RF supports automatic Tx access gain control */
1969069f1a80SAndrew Thompson run_srom_read(sc, RT2860_EEPROM_CONFIG, &val);
1970109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM CFG 0x%04x\n", val);
19713707a5e9SAndrew Thompson /* check if driver should patch the DAC issue */
19723707a5e9SAndrew Thompson if ((val >> 8) != 0xff)
19733707a5e9SAndrew Thompson sc->patch_dac = (val >> 15) & 1;
1974069f1a80SAndrew Thompson if ((val & 0xff) != 0xff) {
1975069f1a80SAndrew Thompson sc->ext_5ghz_lna = (val >> 3) & 1;
1976069f1a80SAndrew Thompson sc->ext_2ghz_lna = (val >> 2) & 1;
19773707a5e9SAndrew Thompson /* check if RF supports automatic Tx access gain control */
1978069f1a80SAndrew Thompson sc->calib_2ghz = sc->calib_5ghz = (val >> 1) & 1;
19793707a5e9SAndrew Thompson /* check if we have a hardware radio switch */
19803707a5e9SAndrew Thompson sc->rfswitch = val & 1;
1981069f1a80SAndrew Thompson }
1982069f1a80SAndrew Thompson
19837a7e01caSKevin Lo /* Read Tx power settings. */
19847a7e01caSKevin Lo if (sc->mac_ver == 0x3593)
19857a7e01caSKevin Lo run_rt3593_get_txpower(sc);
19867a7e01caSKevin Lo else
19877a7e01caSKevin Lo run_get_txpower(sc);
1988069f1a80SAndrew Thompson
1989069f1a80SAndrew Thompson /* read Tx power compensation for each Tx rate */
1990069f1a80SAndrew Thompson run_srom_read(sc, RT2860_EEPROM_DELTAPWR, &val);
1991069f1a80SAndrew Thompson delta_2ghz = delta_5ghz = 0;
1992069f1a80SAndrew Thompson if ((val & 0xff) != 0xff && (val & 0x80)) {
1993069f1a80SAndrew Thompson delta_2ghz = val & 0xf;
1994069f1a80SAndrew Thompson if (!(val & 0x40)) /* negative number */
1995069f1a80SAndrew Thompson delta_2ghz = -delta_2ghz;
1996069f1a80SAndrew Thompson }
1997069f1a80SAndrew Thompson val >>= 8;
1998069f1a80SAndrew Thompson if ((val & 0xff) != 0xff && (val & 0x80)) {
1999069f1a80SAndrew Thompson delta_5ghz = val & 0xf;
2000069f1a80SAndrew Thompson if (!(val & 0x40)) /* negative number */
2001069f1a80SAndrew Thompson delta_5ghz = -delta_5ghz;
2002069f1a80SAndrew Thompson }
2003109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_TXPWR,
2004109005ccSGavin Atkinson "power compensation=%d (2GHz), %d (5GHz)\n", delta_2ghz, delta_5ghz);
2005069f1a80SAndrew Thompson
2006069f1a80SAndrew Thompson for (ridx = 0; ridx < 5; ridx++) {
2007069f1a80SAndrew Thompson uint32_t reg;
2008069f1a80SAndrew Thompson
200985e7bb81SAndrew Thompson run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2, &val);
201085e7bb81SAndrew Thompson reg = val;
201185e7bb81SAndrew Thompson run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2 + 1, &val);
201285e7bb81SAndrew Thompson reg |= (uint32_t)val << 16;
2013069f1a80SAndrew Thompson
2014069f1a80SAndrew Thompson sc->txpow20mhz[ridx] = reg;
2015069f1a80SAndrew Thompson sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz);
2016069f1a80SAndrew Thompson sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz);
2017069f1a80SAndrew Thompson
2018109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_TXPWR,
2019109005ccSGavin Atkinson "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, "
2020069f1a80SAndrew Thompson "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx],
2021069f1a80SAndrew Thompson sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx]);
2022069f1a80SAndrew Thompson }
2023069f1a80SAndrew Thompson
20247a7e01caSKevin Lo /* Read RSSI offsets and LNA gains from EEPROM. */
20257a7e01caSKevin Lo run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_2GHZ :
20267a7e01caSKevin Lo RT3593_EEPROM_RSSI1_2GHZ, &val);
2027069f1a80SAndrew Thompson sc->rssi_2ghz[0] = val & 0xff; /* Ant A */
2028069f1a80SAndrew Thompson sc->rssi_2ghz[1] = val >> 8; /* Ant B */
20297a7e01caSKevin Lo run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_2GHZ :
20307a7e01caSKevin Lo RT3593_EEPROM_RSSI2_2GHZ, &val);
20313707a5e9SAndrew Thompson if (sc->mac_ver >= 0x3070) {
20327a7e01caSKevin Lo if (sc->mac_ver == 0x3593) {
20337a7e01caSKevin Lo sc->txmixgain_2ghz = 0;
20347a7e01caSKevin Lo sc->rssi_2ghz[2] = val & 0xff; /* Ant C */
20357a7e01caSKevin Lo } else {
20363707a5e9SAndrew Thompson /*
20373707a5e9SAndrew Thompson * On RT3070 chips (limited to 2 Rx chains), this ROM
20383707a5e9SAndrew Thompson * field contains the Tx mixer gain for the 2GHz band.
20393707a5e9SAndrew Thompson */
20403707a5e9SAndrew Thompson if ((val & 0xff) != 0xff)
20413707a5e9SAndrew Thompson sc->txmixgain_2ghz = val & 0x7;
20427a7e01caSKevin Lo }
2043109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM, "tx mixer gain=%u (2GHz)\n",
2044109005ccSGavin Atkinson sc->txmixgain_2ghz);
20453707a5e9SAndrew Thompson } else
2046069f1a80SAndrew Thompson sc->rssi_2ghz[2] = val & 0xff; /* Ant C */
20477a7e01caSKevin Lo if (sc->mac_ver == 0x3593)
20487a7e01caSKevin Lo run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val);
2049069f1a80SAndrew Thompson sc->lna[2] = val >> 8; /* channel group 2 */
2050069f1a80SAndrew Thompson
20517a7e01caSKevin Lo run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_5GHZ :
20527a7e01caSKevin Lo RT3593_EEPROM_RSSI1_5GHZ, &val);
2053069f1a80SAndrew Thompson sc->rssi_5ghz[0] = val & 0xff; /* Ant A */
2054069f1a80SAndrew Thompson sc->rssi_5ghz[1] = val >> 8; /* Ant B */
20557a7e01caSKevin Lo run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_5GHZ :
20567a7e01caSKevin Lo RT3593_EEPROM_RSSI2_5GHZ, &val);
20573707a5e9SAndrew Thompson if (sc->mac_ver == 0x3572) {
20583707a5e9SAndrew Thompson /*
20593707a5e9SAndrew Thompson * On RT3572 chips (limited to 2 Rx chains), this ROM
20603707a5e9SAndrew Thompson * field contains the Tx mixer gain for the 5GHz band.
20613707a5e9SAndrew Thompson */
20623707a5e9SAndrew Thompson if ((val & 0xff) != 0xff)
20633707a5e9SAndrew Thompson sc->txmixgain_5ghz = val & 0x7;
2064109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM, "tx mixer gain=%u (5GHz)\n",
2065109005ccSGavin Atkinson sc->txmixgain_5ghz);
20663707a5e9SAndrew Thompson } else
2067069f1a80SAndrew Thompson sc->rssi_5ghz[2] = val & 0xff; /* Ant C */
20687a7e01caSKevin Lo if (sc->mac_ver == 0x3593) {
20697a7e01caSKevin Lo sc->txmixgain_5ghz = 0;
20707a7e01caSKevin Lo run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val);
20717a7e01caSKevin Lo }
2072069f1a80SAndrew Thompson sc->lna[3] = val >> 8; /* channel group 3 */
2073069f1a80SAndrew Thompson
20747a7e01caSKevin Lo run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LNA :
20757a7e01caSKevin Lo RT3593_EEPROM_LNA, &val);
2076069f1a80SAndrew Thompson sc->lna[0] = val & 0xff; /* channel group 0 */
2077069f1a80SAndrew Thompson sc->lna[1] = val >> 8; /* channel group 1 */
2078069f1a80SAndrew Thompson
2079069f1a80SAndrew Thompson /* fix broken 5GHz LNA entries */
2080069f1a80SAndrew Thompson if (sc->lna[2] == 0 || sc->lna[2] == 0xff) {
2081109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM,
2082109005ccSGavin Atkinson "invalid LNA for channel group %d\n", 2);
2083069f1a80SAndrew Thompson sc->lna[2] = sc->lna[1];
2084069f1a80SAndrew Thompson }
2085069f1a80SAndrew Thompson if (sc->lna[3] == 0 || sc->lna[3] == 0xff) {
2086109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM,
2087109005ccSGavin Atkinson "invalid LNA for channel group %d\n", 3);
2088069f1a80SAndrew Thompson sc->lna[3] = sc->lna[1];
2089069f1a80SAndrew Thompson }
2090069f1a80SAndrew Thompson
2091069f1a80SAndrew Thompson /* fix broken RSSI offset entries */
2092069f1a80SAndrew Thompson for (ant = 0; ant < 3; ant++) {
2093069f1a80SAndrew Thompson if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) {
2094109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_RSSI,
2095109005ccSGavin Atkinson "invalid RSSI%d offset: %d (2GHz)\n",
2096069f1a80SAndrew Thompson ant + 1, sc->rssi_2ghz[ant]);
2097069f1a80SAndrew Thompson sc->rssi_2ghz[ant] = 0;
2098069f1a80SAndrew Thompson }
2099069f1a80SAndrew Thompson if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) {
2100109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_RSSI,
2101109005ccSGavin Atkinson "invalid RSSI%d offset: %d (5GHz)\n",
2102069f1a80SAndrew Thompson ant + 1, sc->rssi_5ghz[ant]);
2103069f1a80SAndrew Thompson sc->rssi_5ghz[ant] = 0;
2104069f1a80SAndrew Thompson }
2105069f1a80SAndrew Thompson }
210675c76159SAndrew Thompson return (0);
2107069f1a80SAndrew Thompson }
2108069f1a80SAndrew Thompson
2109f417369bSHans Petter Selasky static struct ieee80211_node *
run_node_alloc(struct ieee80211vap * vap,const uint8_t mac[IEEE80211_ADDR_LEN])2110069f1a80SAndrew Thompson run_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
2111069f1a80SAndrew Thompson {
21126ecec381SAndriy Voskoboinyk return malloc(sizeof (struct run_node), M_80211_NODE,
21136ecec381SAndriy Voskoboinyk M_NOWAIT | M_ZERO);
2114069f1a80SAndrew Thompson }
2115069f1a80SAndrew Thompson
2116069f1a80SAndrew Thompson static int
run_media_change(if_t ifp)2117935b194dSJustin Hibbits run_media_change(if_t ifp)
2118069f1a80SAndrew Thompson {
2119935b194dSJustin Hibbits struct ieee80211vap *vap = if_getsoftc(ifp);
212085e7bb81SAndrew Thompson struct ieee80211com *ic = vap->iv_ic;
2121069f1a80SAndrew Thompson const struct ieee80211_txparam *tp;
2122d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
2123069f1a80SAndrew Thompson uint8_t rate, ridx;
2124069f1a80SAndrew Thompson int error;
2125069f1a80SAndrew Thompson
2126069f1a80SAndrew Thompson RUN_LOCK(sc);
2127069f1a80SAndrew Thompson
2128069f1a80SAndrew Thompson error = ieee80211_media_change(ifp);
2129c6167b4bSBjoern A. Zeeb if (error != 0) {
2130069f1a80SAndrew Thompson RUN_UNLOCK(sc);
213175c76159SAndrew Thompson return (error);
213285e7bb81SAndrew Thompson }
2133069f1a80SAndrew Thompson
2134069f1a80SAndrew Thompson tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
2135069f1a80SAndrew Thompson if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
21368d8bdb01SAndrew Thompson struct ieee80211_node *ni;
21378d8bdb01SAndrew Thompson struct run_node *rn;
21388d8bdb01SAndrew Thompson
2139f520d761SAdrian Chadd /* XXX TODO: methodize with MCS rates */
2140069f1a80SAndrew Thompson rate = ic->ic_sup_rates[ic->ic_curmode].
2141069f1a80SAndrew Thompson rs_rates[tp->ucastrate] & IEEE80211_RATE_VAL;
2142069f1a80SAndrew Thompson for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
2143069f1a80SAndrew Thompson if (rt2860_rates[ridx].rate == rate)
2144069f1a80SAndrew Thompson break;
2145f520d761SAdrian Chadd
21468d8bdb01SAndrew Thompson ni = ieee80211_ref_node(vap->iv_bss);
2147de7eb46eSKevin Lo rn = RUN_NODE(ni);
214885e7bb81SAndrew Thompson rn->fix_ridx = ridx;
2149109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RATE, "rate=%d, fix_ridx=%d\n",
2150109005ccSGavin Atkinson rate, rn->fix_ridx);
21518d8bdb01SAndrew Thompson ieee80211_free_node(ni);
2152069f1a80SAndrew Thompson }
2153069f1a80SAndrew Thompson
215485e7bb81SAndrew Thompson #if 0
2155935b194dSJustin Hibbits if ((if_getflags(ifp) & IFF_UP) &&
2156935b194dSJustin Hibbits (if_getdrvflags(ifp) & RUN_RUNNING)){
2157069f1a80SAndrew Thompson run_init_locked(sc);
2158069f1a80SAndrew Thompson }
215985e7bb81SAndrew Thompson #endif
2160069f1a80SAndrew Thompson
2161069f1a80SAndrew Thompson RUN_UNLOCK(sc);
2162069f1a80SAndrew Thompson
216375c76159SAndrew Thompson return (0);
2164069f1a80SAndrew Thompson }
2165069f1a80SAndrew Thompson
2166069f1a80SAndrew Thompson static int
run_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)2167069f1a80SAndrew Thompson run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2168069f1a80SAndrew Thompson {
2169069f1a80SAndrew Thompson const struct ieee80211_txparam *tp;
2170069f1a80SAndrew Thompson struct ieee80211com *ic = vap->iv_ic;
2171d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
2172069f1a80SAndrew Thompson struct run_vap *rvp = RUN_VAP(vap);
2173069f1a80SAndrew Thompson enum ieee80211_state ostate;
217485e7bb81SAndrew Thompson uint32_t sta[3];
217585e7bb81SAndrew Thompson uint8_t ratectl;
217685e7bb81SAndrew Thompson uint8_t restart_ratectl = 0;
217785e7bb81SAndrew Thompson uint8_t bid = 1 << rvp->rvp_id;
2178069f1a80SAndrew Thompson
2179069f1a80SAndrew Thompson ostate = vap->iv_state;
2180109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_STATE, "%s -> %s\n",
2181069f1a80SAndrew Thompson ieee80211_state_name[ostate],
2182069f1a80SAndrew Thompson ieee80211_state_name[nstate]);
2183069f1a80SAndrew Thompson
2184069f1a80SAndrew Thompson IEEE80211_UNLOCK(ic);
2185069f1a80SAndrew Thompson RUN_LOCK(sc);
2186069f1a80SAndrew Thompson
218785e7bb81SAndrew Thompson ratectl = sc->ratectl_run; /* remember current state */
218885e7bb81SAndrew Thompson sc->ratectl_run = RUN_RATECTL_OFF;
218985e7bb81SAndrew Thompson usb_callout_stop(&sc->ratectl_ch);
2190069f1a80SAndrew Thompson
2191069f1a80SAndrew Thompson if (ostate == IEEE80211_S_RUN) {
2192069f1a80SAndrew Thompson /* turn link LED off */
2193069f1a80SAndrew Thompson run_set_leds(sc, RT2860_LED_RADIO);
2194069f1a80SAndrew Thompson }
2195069f1a80SAndrew Thompson
2196069f1a80SAndrew Thompson switch (nstate) {
2197069f1a80SAndrew Thompson case IEEE80211_S_INIT:
219885e7bb81SAndrew Thompson restart_ratectl = 1;
219985e7bb81SAndrew Thompson
220085e7bb81SAndrew Thompson if (ostate != IEEE80211_S_RUN)
220185e7bb81SAndrew Thompson break;
220285e7bb81SAndrew Thompson
220385e7bb81SAndrew Thompson ratectl &= ~bid;
220485e7bb81SAndrew Thompson sc->runbmap &= ~bid;
220585e7bb81SAndrew Thompson
220685e7bb81SAndrew Thompson /* abort TSF synchronization if there is no vap running */
2207ae6f61b1SAndriy Voskoboinyk if (--sc->running == 0)
2208ae6f61b1SAndriy Voskoboinyk run_disable_tsf(sc);
2209069f1a80SAndrew Thompson break;
2210069f1a80SAndrew Thompson
2211069f1a80SAndrew Thompson case IEEE80211_S_RUN:
221285e7bb81SAndrew Thompson if (!(sc->runbmap & bid)) {
221385e7bb81SAndrew Thompson if(sc->running++)
221485e7bb81SAndrew Thompson restart_ratectl = 1;
221585e7bb81SAndrew Thompson sc->runbmap |= bid;
221685e7bb81SAndrew Thompson }
221785e7bb81SAndrew Thompson
2218e7d14e9bSBernhard Schmidt m_freem(rvp->beacon_mbuf);
2219e7d14e9bSBernhard Schmidt rvp->beacon_mbuf = NULL;
2220e7d14e9bSBernhard Schmidt
222185e7bb81SAndrew Thompson switch (vap->iv_opmode) {
222285e7bb81SAndrew Thompson case IEEE80211_M_HOSTAP:
222385e7bb81SAndrew Thompson case IEEE80211_M_MBSS:
222485e7bb81SAndrew Thompson sc->ap_running |= bid;
222585e7bb81SAndrew Thompson ic->ic_opmode = vap->iv_opmode;
222685e7bb81SAndrew Thompson run_update_beacon_cb(vap);
222785e7bb81SAndrew Thompson break;
222885e7bb81SAndrew Thompson case IEEE80211_M_IBSS:
222985e7bb81SAndrew Thompson sc->adhoc_running |= bid;
223085e7bb81SAndrew Thompson if (!sc->ap_running)
223185e7bb81SAndrew Thompson ic->ic_opmode = vap->iv_opmode;
223285e7bb81SAndrew Thompson run_update_beacon_cb(vap);
223385e7bb81SAndrew Thompson break;
223485e7bb81SAndrew Thompson case IEEE80211_M_STA:
223585e7bb81SAndrew Thompson sc->sta_running |= bid;
223685e7bb81SAndrew Thompson if (!sc->ap_running && !sc->adhoc_running)
223785e7bb81SAndrew Thompson ic->ic_opmode = vap->iv_opmode;
223885e7bb81SAndrew Thompson
223985e7bb81SAndrew Thompson /* read statistic counters (clear on read) */
224085e7bb81SAndrew Thompson run_read_region_1(sc, RT2860_TX_STA_CNT0,
224185e7bb81SAndrew Thompson (uint8_t *)sta, sizeof sta);
224285e7bb81SAndrew Thompson
224385e7bb81SAndrew Thompson break;
224485e7bb81SAndrew Thompson default:
224585e7bb81SAndrew Thompson ic->ic_opmode = vap->iv_opmode;
224685e7bb81SAndrew Thompson break;
224785e7bb81SAndrew Thompson }
2248069f1a80SAndrew Thompson
2249069f1a80SAndrew Thompson if (vap->iv_opmode != IEEE80211_M_MONITOR) {
22508d8bdb01SAndrew Thompson struct ieee80211_node *ni;
22518d8bdb01SAndrew Thompson
2252bb571462SHans Petter Selasky if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) {
2253bb571462SHans Petter Selasky RUN_UNLOCK(sc);
2254bb571462SHans Petter Selasky IEEE80211_LOCK(ic);
2255bb571462SHans Petter Selasky return (-1);
2256bb571462SHans Petter Selasky }
2257272f6adeSGleb Smirnoff run_updateslot(ic);
2258069f1a80SAndrew Thompson run_enable_mrr(sc);
2259069f1a80SAndrew Thompson run_set_txpreamble(sc);
2260069f1a80SAndrew Thompson run_set_basicrates(sc);
22618d8bdb01SAndrew Thompson ni = ieee80211_ref_node(vap->iv_bss);
2262ae132f11SAndriy Voskoboinyk IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
2263ae132f11SAndriy Voskoboinyk run_set_bssid(sc, sc->sc_bssid);
22648d8bdb01SAndrew Thompson ieee80211_free_node(ni);
2265069f1a80SAndrew Thompson run_enable_tsf_sync(sc);
2266069f1a80SAndrew Thompson
2267069f1a80SAndrew Thompson /* enable automatic rate adaptation */
2268069f1a80SAndrew Thompson tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
2269069f1a80SAndrew Thompson if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
227085e7bb81SAndrew Thompson ratectl |= bid;
2271599acbbcSKevin Lo } else
2272599acbbcSKevin Lo run_enable_tsf(sc);
2273069f1a80SAndrew Thompson
2274069f1a80SAndrew Thompson /* turn link LED on */
2275069f1a80SAndrew Thompson run_set_leds(sc, RT2860_LED_RADIO |
227685e7bb81SAndrew Thompson (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ?
2277069f1a80SAndrew Thompson RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ));
2278069f1a80SAndrew Thompson
2279069f1a80SAndrew Thompson break;
2280069f1a80SAndrew Thompson default:
2281109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_STATE, "undefined state\n");
2282069f1a80SAndrew Thompson break;
2283069f1a80SAndrew Thompson }
2284069f1a80SAndrew Thompson
228585e7bb81SAndrew Thompson /* restart amrr for running VAPs */
228685e7bb81SAndrew Thompson if ((sc->ratectl_run = ratectl) && restart_ratectl)
228785e7bb81SAndrew Thompson usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc);
228885e7bb81SAndrew Thompson
2289069f1a80SAndrew Thompson RUN_UNLOCK(sc);
2290069f1a80SAndrew Thompson IEEE80211_LOCK(ic);
2291069f1a80SAndrew Thompson
2292069f1a80SAndrew Thompson return(rvp->newstate(vap, nstate, arg));
2293069f1a80SAndrew Thompson }
2294069f1a80SAndrew Thompson
2295a14954c5SAndriy Voskoboinyk static int
run_wme_update(struct ieee80211com * ic)2296a14954c5SAndriy Voskoboinyk run_wme_update(struct ieee80211com *ic)
2297069f1a80SAndrew Thompson {
22989fbe631aSAdrian Chadd struct chanAccParams chp;
2299d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
23009fbe631aSAdrian Chadd const struct wmeParams *ac;
2301069f1a80SAndrew Thompson int aci, error = 0;
2302069f1a80SAndrew Thompson
23039fbe631aSAdrian Chadd ieee80211_wme_ic_getparams(ic, &chp);
23049fbe631aSAdrian Chadd ac = chp.cap_wmeParams;
23059fbe631aSAdrian Chadd
2306069f1a80SAndrew Thompson /* update MAC TX configuration registers */
2307a14954c5SAndriy Voskoboinyk RUN_LOCK(sc);
2308069f1a80SAndrew Thompson for (aci = 0; aci < WME_NUM_AC; aci++) {
2309069f1a80SAndrew Thompson error = run_write(sc, RT2860_EDCA_AC_CFG(aci),
23105f28dd73SAdrian Chadd ac[aci].wmep_logcwmax << 16 |
23115f28dd73SAdrian Chadd ac[aci].wmep_logcwmin << 12 |
23125f28dd73SAdrian Chadd ac[aci].wmep_aifsn << 8 |
23135f28dd73SAdrian Chadd ac[aci].wmep_txopLimit);
2314069f1a80SAndrew Thompson if (error) goto err;
2315069f1a80SAndrew Thompson }
2316069f1a80SAndrew Thompson
2317069f1a80SAndrew Thompson /* update SCH/DMA registers too */
2318069f1a80SAndrew Thompson error = run_write(sc, RT2860_WMM_AIFSN_CFG,
23195f28dd73SAdrian Chadd ac[WME_AC_VO].wmep_aifsn << 12 |
23205f28dd73SAdrian Chadd ac[WME_AC_VI].wmep_aifsn << 8 |
23215f28dd73SAdrian Chadd ac[WME_AC_BK].wmep_aifsn << 4 |
23225f28dd73SAdrian Chadd ac[WME_AC_BE].wmep_aifsn);
2323069f1a80SAndrew Thompson if (error) goto err;
2324069f1a80SAndrew Thompson error = run_write(sc, RT2860_WMM_CWMIN_CFG,
23255f28dd73SAdrian Chadd ac[WME_AC_VO].wmep_logcwmin << 12 |
23265f28dd73SAdrian Chadd ac[WME_AC_VI].wmep_logcwmin << 8 |
23275f28dd73SAdrian Chadd ac[WME_AC_BK].wmep_logcwmin << 4 |
23285f28dd73SAdrian Chadd ac[WME_AC_BE].wmep_logcwmin);
2329069f1a80SAndrew Thompson if (error) goto err;
2330069f1a80SAndrew Thompson error = run_write(sc, RT2860_WMM_CWMAX_CFG,
23315f28dd73SAdrian Chadd ac[WME_AC_VO].wmep_logcwmax << 12 |
23325f28dd73SAdrian Chadd ac[WME_AC_VI].wmep_logcwmax << 8 |
23335f28dd73SAdrian Chadd ac[WME_AC_BK].wmep_logcwmax << 4 |
23345f28dd73SAdrian Chadd ac[WME_AC_BE].wmep_logcwmax);
2335069f1a80SAndrew Thompson if (error) goto err;
2336069f1a80SAndrew Thompson error = run_write(sc, RT2860_WMM_TXOP0_CFG,
23375f28dd73SAdrian Chadd ac[WME_AC_BK].wmep_txopLimit << 16 |
23385f28dd73SAdrian Chadd ac[WME_AC_BE].wmep_txopLimit);
2339069f1a80SAndrew Thompson if (error) goto err;
2340069f1a80SAndrew Thompson error = run_write(sc, RT2860_WMM_TXOP1_CFG,
23415f28dd73SAdrian Chadd ac[WME_AC_VO].wmep_txopLimit << 16 |
23425f28dd73SAdrian Chadd ac[WME_AC_VI].wmep_txopLimit);
2343069f1a80SAndrew Thompson
2344069f1a80SAndrew Thompson err:
2345a14954c5SAndriy Voskoboinyk RUN_UNLOCK(sc);
2346069f1a80SAndrew Thompson if (error)
2347109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_USB, "WME update failed\n");
2348069f1a80SAndrew Thompson
2349a14954c5SAndriy Voskoboinyk return (error);
235085e7bb81SAndrew Thompson }
235185e7bb81SAndrew Thompson
2352069f1a80SAndrew Thompson static void
run_key_set_cb(void * arg)235385e7bb81SAndrew Thompson run_key_set_cb(void *arg)
2354069f1a80SAndrew Thompson {
235585e7bb81SAndrew Thompson struct run_cmdq *cmdq = arg;
235685e7bb81SAndrew Thompson struct ieee80211vap *vap = cmdq->arg1;
235785e7bb81SAndrew Thompson struct ieee80211_key *k = cmdq->k;
2358069f1a80SAndrew Thompson struct ieee80211com *ic = vap->iv_ic;
2359d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
2360069f1a80SAndrew Thompson struct ieee80211_node *ni;
23613257f902SKevin Lo u_int cipher = k->wk_cipher->ic_cipher;
2362069f1a80SAndrew Thompson uint32_t attr;
2363069f1a80SAndrew Thompson uint16_t base, associd;
2364beaa0537SAndrew Thompson uint8_t mode, wcid, iv[8];
2365069f1a80SAndrew Thompson
236685e7bb81SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
2367069f1a80SAndrew Thompson
2368beaa0537SAndrew Thompson if (vap->iv_opmode == IEEE80211_M_HOSTAP)
236985e7bb81SAndrew Thompson ni = ieee80211_find_vap_node(&ic->ic_sta, vap, cmdq->mac);
2370beaa0537SAndrew Thompson else
2371069f1a80SAndrew Thompson ni = vap->iv_bss;
237285e7bb81SAndrew Thompson associd = (ni != NULL) ? ni->ni_associd : 0;
2373069f1a80SAndrew Thompson
2374069f1a80SAndrew Thompson /* map net80211 cipher to RT2860 security mode */
23753257f902SKevin Lo switch (cipher) {
2376069f1a80SAndrew Thompson case IEEE80211_CIPHER_WEP:
2377069f1a80SAndrew Thompson if(k->wk_keylen < 8)
2378069f1a80SAndrew Thompson mode = RT2860_MODE_WEP40;
2379069f1a80SAndrew Thompson else
2380069f1a80SAndrew Thompson mode = RT2860_MODE_WEP104;
2381069f1a80SAndrew Thompson break;
2382069f1a80SAndrew Thompson case IEEE80211_CIPHER_TKIP:
2383069f1a80SAndrew Thompson mode = RT2860_MODE_TKIP;
2384069f1a80SAndrew Thompson break;
2385069f1a80SAndrew Thompson case IEEE80211_CIPHER_AES_CCM:
2386069f1a80SAndrew Thompson mode = RT2860_MODE_AES_CCMP;
2387069f1a80SAndrew Thompson break;
2388069f1a80SAndrew Thompson default:
2389109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_KEY, "undefined case\n");
239085e7bb81SAndrew Thompson return;
2391069f1a80SAndrew Thompson }
2392069f1a80SAndrew Thompson
2393109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_KEY,
2394109005ccSGavin Atkinson "associd=%x, keyix=%d, mode=%x, type=%s, tx=%s, rx=%s\n",
2395069f1a80SAndrew Thompson associd, k->wk_keyix, mode,
239685e7bb81SAndrew Thompson (k->wk_flags & IEEE80211_KEY_GROUP) ? "group" : "pairwise",
239785e7bb81SAndrew Thompson (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off",
239885e7bb81SAndrew Thompson (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off");
2399069f1a80SAndrew Thompson
2400069f1a80SAndrew Thompson if (k->wk_flags & IEEE80211_KEY_GROUP) {
2401069f1a80SAndrew Thompson wcid = 0; /* NB: update WCID0 for group keys */
240285e7bb81SAndrew Thompson base = RT2860_SKEY(RUN_VAP(vap)->rvp_id, k->wk_keyix);
2403069f1a80SAndrew Thompson } else {
2404d021dd19SHans Petter Selasky wcid = (vap->iv_opmode == IEEE80211_M_STA) ?
2405d021dd19SHans Petter Selasky 1 : RUN_AID2WCID(associd);
2406069f1a80SAndrew Thompson base = RT2860_PKEY(wcid);
2407069f1a80SAndrew Thompson }
2408069f1a80SAndrew Thompson
24093257f902SKevin Lo if (cipher == IEEE80211_CIPHER_TKIP) {
2410069f1a80SAndrew Thompson if(run_write_region_1(sc, base, k->wk_key, 16))
241185e7bb81SAndrew Thompson return;
2412beaa0537SAndrew Thompson if(run_write_region_1(sc, base + 16, &k->wk_key[16], 8)) /* wk_txmic */
241385e7bb81SAndrew Thompson return;
2414beaa0537SAndrew Thompson if(run_write_region_1(sc, base + 24, &k->wk_key[24], 8)) /* wk_rxmic */
241585e7bb81SAndrew Thompson return;
2416069f1a80SAndrew Thompson } else {
2417069f1a80SAndrew Thompson /* roundup len to 16-bit: XXX fix write_region_1() instead */
2418069f1a80SAndrew Thompson if(run_write_region_1(sc, base, k->wk_key, (k->wk_keylen + 1) & ~1))
241985e7bb81SAndrew Thompson return;
2420069f1a80SAndrew Thompson }
2421069f1a80SAndrew Thompson
2422069f1a80SAndrew Thompson if (!(k->wk_flags & IEEE80211_KEY_GROUP) ||
2423069f1a80SAndrew Thompson (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))) {
2424069f1a80SAndrew Thompson /* set initial packet number in IV+EIV */
24253257f902SKevin Lo if (cipher == IEEE80211_CIPHER_WEP) {
2426069f1a80SAndrew Thompson memset(iv, 0, sizeof iv);
242785e7bb81SAndrew Thompson iv[3] = vap->iv_def_txkey << 6;
2428069f1a80SAndrew Thompson } else {
24293257f902SKevin Lo if (cipher == IEEE80211_CIPHER_TKIP) {
2430069f1a80SAndrew Thompson iv[0] = k->wk_keytsc >> 8;
2431069f1a80SAndrew Thompson iv[1] = (iv[0] | 0x20) & 0x7f;
2432069f1a80SAndrew Thompson iv[2] = k->wk_keytsc;
2433069f1a80SAndrew Thompson } else /* CCMP */ {
2434069f1a80SAndrew Thompson iv[0] = k->wk_keytsc;
2435069f1a80SAndrew Thompson iv[1] = k->wk_keytsc >> 8;
2436069f1a80SAndrew Thompson iv[2] = 0;
2437069f1a80SAndrew Thompson }
2438069f1a80SAndrew Thompson iv[3] = k->wk_keyix << 6 | IEEE80211_WEP_EXTIV;
2439069f1a80SAndrew Thompson iv[4] = k->wk_keytsc >> 16;
2440069f1a80SAndrew Thompson iv[5] = k->wk_keytsc >> 24;
2441069f1a80SAndrew Thompson iv[6] = k->wk_keytsc >> 32;
2442069f1a80SAndrew Thompson iv[7] = k->wk_keytsc >> 40;
2443069f1a80SAndrew Thompson }
2444069f1a80SAndrew Thompson if (run_write_region_1(sc, RT2860_IVEIV(wcid), iv, 8))
244585e7bb81SAndrew Thompson return;
2446069f1a80SAndrew Thompson }
2447069f1a80SAndrew Thompson
2448069f1a80SAndrew Thompson if (k->wk_flags & IEEE80211_KEY_GROUP) {
2449069f1a80SAndrew Thompson /* install group key */
2450069f1a80SAndrew Thompson if (run_read(sc, RT2860_SKEY_MODE_0_7, &attr))
245185e7bb81SAndrew Thompson return;
2452069f1a80SAndrew Thompson attr &= ~(0xf << (k->wk_keyix * 4));
2453069f1a80SAndrew Thompson attr |= mode << (k->wk_keyix * 4);
2454069f1a80SAndrew Thompson if (run_write(sc, RT2860_SKEY_MODE_0_7, attr))
245585e7bb81SAndrew Thompson return;
2456069f1a80SAndrew Thompson } else {
2457069f1a80SAndrew Thompson /* install pairwise key */
2458069f1a80SAndrew Thompson if (run_read(sc, RT2860_WCID_ATTR(wcid), &attr))
245985e7bb81SAndrew Thompson return;
2460069f1a80SAndrew Thompson attr = (attr & ~0xf) | (mode << 1) | RT2860_RX_PKEY_EN;
2461069f1a80SAndrew Thompson if (run_write(sc, RT2860_WCID_ATTR(wcid), attr))
246285e7bb81SAndrew Thompson return;
2463069f1a80SAndrew Thompson }
2464069f1a80SAndrew Thompson
2465069f1a80SAndrew Thompson /* TODO create a pass-thru key entry? */
2466069f1a80SAndrew Thompson
246785e7bb81SAndrew Thompson /* need wcid to delete the right key later */
246885e7bb81SAndrew Thompson k->wk_pad = wcid;
246985e7bb81SAndrew Thompson }
247085e7bb81SAndrew Thompson
247185e7bb81SAndrew Thompson /*
247285e7bb81SAndrew Thompson * Don't have to be deferred, but in order to keep order of
247385e7bb81SAndrew Thompson * execution, i.e. with run_key_delete(), defer this and let
247485e7bb81SAndrew Thompson * run_cmdq_cb() maintain the order.
247585e7bb81SAndrew Thompson *
247685e7bb81SAndrew Thompson * return 0 on error
247785e7bb81SAndrew Thompson */
247885e7bb81SAndrew Thompson static int
run_key_set(struct ieee80211vap * vap,struct ieee80211_key * k)2479bc813c40SAdrian Chadd run_key_set(struct ieee80211vap *vap, struct ieee80211_key *k)
248085e7bb81SAndrew Thompson {
248185e7bb81SAndrew Thompson struct ieee80211com *ic = vap->iv_ic;
2482d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
248385e7bb81SAndrew Thompson uint32_t i;
248485e7bb81SAndrew Thompson
248585e7bb81SAndrew Thompson i = RUN_CMDQ_GET(&sc->cmdq_store);
2486109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_KEY, "cmdq_store=%d\n", i);
248785e7bb81SAndrew Thompson sc->cmdq[i].func = run_key_set_cb;
248885e7bb81SAndrew Thompson sc->cmdq[i].arg0 = NULL;
248985e7bb81SAndrew Thompson sc->cmdq[i].arg1 = vap;
249085e7bb81SAndrew Thompson sc->cmdq[i].k = k;
2491bc813c40SAdrian Chadd IEEE80211_ADDR_COPY(sc->cmdq[i].mac, k->wk_macaddr);
249285e7bb81SAndrew Thompson ieee80211_runtask(ic, &sc->cmdq_task);
249385e7bb81SAndrew Thompson
2494beaa0537SAndrew Thompson /*
2495beaa0537SAndrew Thompson * To make sure key will be set when hostapd
2496beaa0537SAndrew Thompson * calls iv_key_set() before if_init().
2497beaa0537SAndrew Thompson */
2498beaa0537SAndrew Thompson if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
2499beaa0537SAndrew Thompson RUN_LOCK(sc);
2500beaa0537SAndrew Thompson sc->cmdq_key_set = RUN_CMDQ_GO;
2501beaa0537SAndrew Thompson RUN_UNLOCK(sc);
2502beaa0537SAndrew Thompson }
2503beaa0537SAndrew Thompson
250485e7bb81SAndrew Thompson return (1);
250585e7bb81SAndrew Thompson }
250685e7bb81SAndrew Thompson
250785e7bb81SAndrew Thompson /*
250885e7bb81SAndrew Thompson * If wlan is destroyed without being brought down i.e. without
250985e7bb81SAndrew Thompson * wlan down or wpa_cli terminate, this function is called after
251085e7bb81SAndrew Thompson * vap is gone. Don't refer it.
251185e7bb81SAndrew Thompson */
251285e7bb81SAndrew Thompson static void
run_key_delete_cb(void * arg)251385e7bb81SAndrew Thompson run_key_delete_cb(void *arg)
251485e7bb81SAndrew Thompson {
251585e7bb81SAndrew Thompson struct run_cmdq *cmdq = arg;
251685e7bb81SAndrew Thompson struct run_softc *sc = cmdq->arg1;
251785e7bb81SAndrew Thompson struct ieee80211_key *k = &cmdq->key;
251885e7bb81SAndrew Thompson uint32_t attr;
251985e7bb81SAndrew Thompson uint8_t wcid;
252085e7bb81SAndrew Thompson
252185e7bb81SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
252285e7bb81SAndrew Thompson
252385e7bb81SAndrew Thompson if (k->wk_flags & IEEE80211_KEY_GROUP) {
252485e7bb81SAndrew Thompson /* remove group key */
2525109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_KEY, "removing group key\n");
252685e7bb81SAndrew Thompson run_read(sc, RT2860_SKEY_MODE_0_7, &attr);
252785e7bb81SAndrew Thompson attr &= ~(0xf << (k->wk_keyix * 4));
252885e7bb81SAndrew Thompson run_write(sc, RT2860_SKEY_MODE_0_7, attr);
252985e7bb81SAndrew Thompson } else {
253085e7bb81SAndrew Thompson /* remove pairwise key */
2531109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_KEY,
2532109005ccSGavin Atkinson "removing key for wcid %x\n", k->wk_pad);
253385e7bb81SAndrew Thompson /* matching wcid was written to wk_pad in run_key_set() */
253485e7bb81SAndrew Thompson wcid = k->wk_pad;
253585e7bb81SAndrew Thompson run_read(sc, RT2860_WCID_ATTR(wcid), &attr);
253685e7bb81SAndrew Thompson attr &= ~0xf;
253785e7bb81SAndrew Thompson run_write(sc, RT2860_WCID_ATTR(wcid), attr);
253885e7bb81SAndrew Thompson run_set_region_4(sc, RT2860_WCID_ENTRY(wcid), 0, 8);
253985e7bb81SAndrew Thompson }
254085e7bb81SAndrew Thompson
254185e7bb81SAndrew Thompson k->wk_pad = 0;
2542069f1a80SAndrew Thompson }
2543069f1a80SAndrew Thompson
2544069f1a80SAndrew Thompson /*
2545069f1a80SAndrew Thompson * return 0 on error
2546069f1a80SAndrew Thompson */
2547069f1a80SAndrew Thompson static int
run_key_delete(struct ieee80211vap * vap,struct ieee80211_key * k)254885e7bb81SAndrew Thompson run_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k)
2549069f1a80SAndrew Thompson {
2550069f1a80SAndrew Thompson struct ieee80211com *ic = vap->iv_ic;
2551d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
255285e7bb81SAndrew Thompson struct ieee80211_key *k0;
255385e7bb81SAndrew Thompson uint32_t i;
2554069f1a80SAndrew Thompson
255585e7bb81SAndrew Thompson /*
255685e7bb81SAndrew Thompson * When called back, key might be gone. So, make a copy
255785e7bb81SAndrew Thompson * of some values need to delete keys before deferring.
255885e7bb81SAndrew Thompson * But, because of LOR with node lock, cannot use lock here.
255985e7bb81SAndrew Thompson * So, use atomic instead.
256085e7bb81SAndrew Thompson */
256185e7bb81SAndrew Thompson i = RUN_CMDQ_GET(&sc->cmdq_store);
2562109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_KEY, "cmdq_store=%d\n", i);
256385e7bb81SAndrew Thompson sc->cmdq[i].func = run_key_delete_cb;
256485e7bb81SAndrew Thompson sc->cmdq[i].arg0 = NULL;
256585e7bb81SAndrew Thompson sc->cmdq[i].arg1 = sc;
256685e7bb81SAndrew Thompson k0 = &sc->cmdq[i].key;
256785e7bb81SAndrew Thompson k0->wk_flags = k->wk_flags;
256885e7bb81SAndrew Thompson k0->wk_keyix = k->wk_keyix;
256985e7bb81SAndrew Thompson /* matching wcid was written to wk_pad in run_key_set() */
257085e7bb81SAndrew Thompson k0->wk_pad = k->wk_pad;
257185e7bb81SAndrew Thompson ieee80211_runtask(ic, &sc->cmdq_task);
257285e7bb81SAndrew Thompson return (1); /* return fake success */
2573069f1a80SAndrew Thompson
2574069f1a80SAndrew Thompson }
2575069f1a80SAndrew Thompson
2576069f1a80SAndrew Thompson static void
run_ratectl_to(void * arg)2577b6108616SRui Paulo run_ratectl_to(void *arg)
2578069f1a80SAndrew Thompson {
257985e7bb81SAndrew Thompson struct run_softc *sc = arg;
2580069f1a80SAndrew Thompson
2581069f1a80SAndrew Thompson /* do it in a process context, so it can go sleep */
25827a79cebfSGleb Smirnoff ieee80211_runtask(&sc->sc_ic, &sc->ratectl_task);
2583069f1a80SAndrew Thompson /* next timeout will be rescheduled in the callback task */
2584069f1a80SAndrew Thompson }
2585069f1a80SAndrew Thompson
2586069f1a80SAndrew Thompson /* ARGSUSED */
2587069f1a80SAndrew Thompson static void
run_ratectl_cb(void * arg,int pending)2588b6108616SRui Paulo run_ratectl_cb(void *arg, int pending)
2589069f1a80SAndrew Thompson {
259085e7bb81SAndrew Thompson struct run_softc *sc = arg;
25917a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
259285e7bb81SAndrew Thompson struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
2593069f1a80SAndrew Thompson
259485e7bb81SAndrew Thompson if (vap == NULL)
259585e7bb81SAndrew Thompson return;
259685e7bb81SAndrew Thompson
259744bf877eSHans Petter Selasky if (sc->rvp_cnt > 1 || vap->iv_opmode != IEEE80211_M_STA) {
2598069f1a80SAndrew Thompson /*
2599069f1a80SAndrew Thompson * run_reset_livelock() doesn't do anything with AMRR,
2600069f1a80SAndrew Thompson * but Ralink wants us to call it every 1 sec. So, we
2601069f1a80SAndrew Thompson * piggyback here rather than creating another callout.
2602069f1a80SAndrew Thompson * Livelock may occur only in HOSTAP or IBSS mode
2603069f1a80SAndrew Thompson * (when h/w is sending beacons).
2604069f1a80SAndrew Thompson */
2605069f1a80SAndrew Thompson RUN_LOCK(sc);
2606069f1a80SAndrew Thompson run_reset_livelock(sc);
260785e7bb81SAndrew Thompson /* just in case, there are some stats to drain */
260885e7bb81SAndrew Thompson run_drain_fifo(sc);
2609069f1a80SAndrew Thompson RUN_UNLOCK(sc);
2610069f1a80SAndrew Thompson }
2611069f1a80SAndrew Thompson
261244bf877eSHans Petter Selasky ieee80211_iterate_nodes(&ic->ic_sta, run_iter_func, sc);
261344bf877eSHans Petter Selasky
26142764a278SHans Petter Selasky RUN_LOCK(sc);
261585e7bb81SAndrew Thompson if(sc->ratectl_run != RUN_RATECTL_OFF)
261685e7bb81SAndrew Thompson usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc);
26172764a278SHans Petter Selasky RUN_UNLOCK(sc);
2618069f1a80SAndrew Thompson }
2619069f1a80SAndrew Thompson
2620069f1a80SAndrew Thompson static void
run_drain_fifo(void * arg)262185e7bb81SAndrew Thompson run_drain_fifo(void *arg)
2622069f1a80SAndrew Thompson {
262385e7bb81SAndrew Thompson struct run_softc *sc = arg;
262485e7bb81SAndrew Thompson uint32_t stat;
2625f417369bSHans Petter Selasky uint16_t (*wstat)[3];
2626069f1a80SAndrew Thompson uint8_t wcid, mcs, pid;
2627f417369bSHans Petter Selasky int8_t retry;
2628069f1a80SAndrew Thompson
262985e7bb81SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
2630069f1a80SAndrew Thompson
263185e7bb81SAndrew Thompson for (;;) {
2632069f1a80SAndrew Thompson /* drain Tx status FIFO (maxsize = 16) */
2633069f1a80SAndrew Thompson run_read(sc, RT2860_TX_STAT_FIFO, &stat);
2634109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx stat 0x%08x\n", stat);
263585e7bb81SAndrew Thompson if (!(stat & RT2860_TXQ_VLD))
263685e7bb81SAndrew Thompson break;
2637069f1a80SAndrew Thompson
2638069f1a80SAndrew Thompson wcid = (stat >> RT2860_TXQ_WCID_SHIFT) & 0xff;
2639069f1a80SAndrew Thompson
2640069f1a80SAndrew Thompson /* if no ACK was requested, no feedback is available */
264185e7bb81SAndrew Thompson if (!(stat & RT2860_TXQ_ACKREQ) || wcid > RT2870_WCID_MAX ||
264285e7bb81SAndrew Thompson wcid == 0)
264385e7bb81SAndrew Thompson continue;
264485e7bb81SAndrew Thompson
2645069f1a80SAndrew Thompson /*
2646f417369bSHans Petter Selasky * Even though each stat is Tx-complete-status like format,
2647f417369bSHans Petter Selasky * the device can poll stats. Because there is no guarantee
2648f417369bSHans Petter Selasky * that the referring node is still around when read the stats.
2649f417369bSHans Petter Selasky * So that, if we use ieee80211_ratectl_tx_update(), we will
2650f417369bSHans Petter Selasky * have hard time not to refer already freed node.
2651f417369bSHans Petter Selasky *
2652f417369bSHans Petter Selasky * To eliminate such page faults, we poll stats in softc.
2653f417369bSHans Petter Selasky * Then, update the rates later with ieee80211_ratectl_tx_update().
2654f417369bSHans Petter Selasky */
2655f417369bSHans Petter Selasky wstat = &(sc->wcid_stats[wcid]);
2656f417369bSHans Petter Selasky (*wstat)[RUN_TXCNT]++;
2657f417369bSHans Petter Selasky if (stat & RT2860_TXQ_OK)
2658f417369bSHans Petter Selasky (*wstat)[RUN_SUCCESS]++;
2659f417369bSHans Petter Selasky else
26607a79cebfSGleb Smirnoff counter_u64_add(sc->sc_ic.ic_oerrors, 1);
2661f417369bSHans Petter Selasky /*
2662f417369bSHans Petter Selasky * Check if there were retries, ie if the Tx success rate is
2663f417369bSHans Petter Selasky * different from the requested rate. Note that it works only
2664f417369bSHans Petter Selasky * because we do not allow rate fallback from OFDM to CCK.
2665069f1a80SAndrew Thompson */
2666069f1a80SAndrew Thompson mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f;
2667069f1a80SAndrew Thompson pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf;
2668f417369bSHans Petter Selasky if ((retry = pid -1 - mcs) > 0) {
2669f417369bSHans Petter Selasky (*wstat)[RUN_TXCNT] += retry;
2670f417369bSHans Petter Selasky (*wstat)[RUN_RETRY] += retry;
2671069f1a80SAndrew Thompson }
2672069f1a80SAndrew Thompson }
2673109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "count=%d\n", sc->fifo_cnt);
267485e7bb81SAndrew Thompson
267585e7bb81SAndrew Thompson sc->fifo_cnt = 0;
267685e7bb81SAndrew Thompson }
267785e7bb81SAndrew Thompson
267885e7bb81SAndrew Thompson static void
run_iter_func(void * arg,struct ieee80211_node * ni)267985e7bb81SAndrew Thompson run_iter_func(void *arg, struct ieee80211_node *ni)
268085e7bb81SAndrew Thompson {
268185e7bb81SAndrew Thompson struct run_softc *sc = arg;
2682f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs;
268385e7bb81SAndrew Thompson struct ieee80211vap *vap = ni->ni_vap;
2684de7eb46eSKevin Lo struct run_node *rn = RUN_NODE(ni);
2685f417369bSHans Petter Selasky union run_stats sta[2];
2686f417369bSHans Petter Selasky uint16_t (*wstat)[3];
2687f520d761SAdrian Chadd int error, ridx;
268870674500SAdrian Chadd uint8_t dot11rate;
2689f417369bSHans Petter Selasky
2690f417369bSHans Petter Selasky RUN_LOCK(sc);
269185e7bb81SAndrew Thompson
269244bf877eSHans Petter Selasky /* Check for special case */
269344bf877eSHans Petter Selasky if (sc->rvp_cnt <= 1 && vap->iv_opmode == IEEE80211_M_STA &&
269444bf877eSHans Petter Selasky ni != vap->iv_bss)
269544bf877eSHans Petter Selasky goto fail;
269644bf877eSHans Petter Selasky
2697f6930becSAndriy Voskoboinyk txs->flags = IEEE80211_RATECTL_TX_STATS_NODE |
2698f6930becSAndriy Voskoboinyk IEEE80211_RATECTL_TX_STATS_RETRIES;
2699f6930becSAndriy Voskoboinyk txs->ni = ni;
270085e7bb81SAndrew Thompson if (sc->rvp_cnt <= 1 && (vap->iv_opmode == IEEE80211_M_IBSS ||
270185e7bb81SAndrew Thompson vap->iv_opmode == IEEE80211_M_STA)) {
2702069f1a80SAndrew Thompson /* read statistic counters (clear on read) and update AMRR state */
2703069f1a80SAndrew Thompson error = run_read_region_1(sc, RT2860_TX_STA_CNT0, (uint8_t *)sta,
2704069f1a80SAndrew Thompson sizeof sta);
2705069f1a80SAndrew Thompson if (error != 0)
2706f417369bSHans Petter Selasky goto fail;
2707069f1a80SAndrew Thompson
2708069f1a80SAndrew Thompson /* count failed TX as errors */
27097a79cebfSGleb Smirnoff if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS,
27107a79cebfSGleb Smirnoff le16toh(sta[0].error.fail));
2711069f1a80SAndrew Thompson
2712f6930becSAndriy Voskoboinyk txs->nretries = le16toh(sta[1].tx.retry);
2713f6930becSAndriy Voskoboinyk txs->nsuccess = le16toh(sta[1].tx.success);
2714f6930becSAndriy Voskoboinyk /* nretries??? */
2715f6930becSAndriy Voskoboinyk txs->nframes = txs->nretries + txs->nsuccess +
2716f6930becSAndriy Voskoboinyk le16toh(sta[0].error.fail);
2717069f1a80SAndrew Thompson
2718109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RATE,
2719109005ccSGavin Atkinson "retrycnt=%d success=%d failcnt=%d\n",
2720109005ccSGavin Atkinson txs->nretries, txs->nsuccess, le16toh(sta[0].error.fail));
2721f417369bSHans Petter Selasky } else {
2722f417369bSHans Petter Selasky wstat = &(sc->wcid_stats[RUN_AID2WCID(ni->ni_associd)]);
2723069f1a80SAndrew Thompson
2724f417369bSHans Petter Selasky if (wstat == &(sc->wcid_stats[0]) ||
2725f417369bSHans Petter Selasky wstat > &(sc->wcid_stats[RT2870_WCID_MAX]))
2726f417369bSHans Petter Selasky goto fail;
272785e7bb81SAndrew Thompson
2728f6930becSAndriy Voskoboinyk txs->nretries = (*wstat)[RUN_RETRY];
2729f6930becSAndriy Voskoboinyk txs->nsuccess = (*wstat)[RUN_SUCCESS];
2730f6930becSAndriy Voskoboinyk txs->nframes = (*wstat)[RUN_TXCNT];
2731109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RATE,
2732109005ccSGavin Atkinson "retrycnt=%d txcnt=%d success=%d\n",
2733f6930becSAndriy Voskoboinyk txs->nretries, txs->nframes, txs->nsuccess);
273485e7bb81SAndrew Thompson
2735f417369bSHans Petter Selasky memset(wstat, 0, sizeof(*wstat));
2736069f1a80SAndrew Thompson }
2737069f1a80SAndrew Thompson
2738f6930becSAndriy Voskoboinyk ieee80211_ratectl_tx_update(vap, txs);
2739f520d761SAdrian Chadd ieee80211_ratectl_rate(ni, NULL, 0);
2740f520d761SAdrian Chadd /* XXX TODO: methodize with MCS rates */
274170674500SAdrian Chadd dot11rate = ieee80211_node_get_txrate_dot11rate(ni);
2742f520d761SAdrian Chadd for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
274370674500SAdrian Chadd if (rt2860_rates[ridx].rate == dot11rate)
2744f520d761SAdrian Chadd break;
2745f520d761SAdrian Chadd rn->amrr_ridx = ridx;
2746f417369bSHans Petter Selasky
2747f417369bSHans Petter Selasky fail:
2748f417369bSHans Petter Selasky RUN_UNLOCK(sc);
2749f417369bSHans Petter Selasky
275070674500SAdrian Chadd RUN_DPRINTF(sc, RUN_DEBUG_RATE, "rate=0x%02x, ridx=%d\n",
275170674500SAdrian Chadd ieee80211_node_get_txrate_dot11rate(ni), rn->amrr_ridx);
275285e7bb81SAndrew Thompson }
2753069f1a80SAndrew Thompson
275485e7bb81SAndrew Thompson static void
run_newassoc_cb(void * arg)275585e7bb81SAndrew Thompson run_newassoc_cb(void *arg)
275685e7bb81SAndrew Thompson {
275785e7bb81SAndrew Thompson struct run_cmdq *cmdq = arg;
275885e7bb81SAndrew Thompson struct ieee80211_node *ni = cmdq->arg1;
2759d3fdd08cSAdrian Chadd struct run_softc *sc = ni->ni_vap->iv_ic->ic_softc;
276085e7bb81SAndrew Thompson uint8_t wcid = cmdq->wcid;
2761069f1a80SAndrew Thompson
276285e7bb81SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
276385e7bb81SAndrew Thompson
276485e7bb81SAndrew Thompson run_write_region_1(sc, RT2860_WCID_ENTRY(wcid),
276585e7bb81SAndrew Thompson ni->ni_macaddr, IEEE80211_ADDR_LEN);
2766f417369bSHans Petter Selasky
2767f417369bSHans Petter Selasky memset(&(sc->wcid_stats[wcid]), 0, sizeof(sc->wcid_stats[wcid]));
2768069f1a80SAndrew Thompson }
2769069f1a80SAndrew Thompson
2770069f1a80SAndrew Thompson static void
run_newassoc(struct ieee80211_node * ni,int isnew)2771069f1a80SAndrew Thompson run_newassoc(struct ieee80211_node *ni, int isnew)
2772069f1a80SAndrew Thompson {
2773de7eb46eSKevin Lo struct run_node *rn = RUN_NODE(ni);
277485e7bb81SAndrew Thompson struct ieee80211vap *vap = ni->ni_vap;
277585e7bb81SAndrew Thompson struct ieee80211com *ic = vap->iv_ic;
2776d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
2777069f1a80SAndrew Thompson uint8_t rate;
277885e7bb81SAndrew Thompson uint8_t ridx;
2779d021dd19SHans Petter Selasky uint8_t wcid;
2780069f1a80SAndrew Thompson
2781d021dd19SHans Petter Selasky wcid = (vap->iv_opmode == IEEE80211_M_STA) ?
2782d021dd19SHans Petter Selasky 1 : RUN_AID2WCID(ni->ni_associd);
2783d021dd19SHans Petter Selasky
278485e7bb81SAndrew Thompson if (wcid > RT2870_WCID_MAX) {
278585e7bb81SAndrew Thompson device_printf(sc->sc_dev, "wcid=%d out of range\n", wcid);
278685e7bb81SAndrew Thompson return;
278785e7bb81SAndrew Thompson }
278885e7bb81SAndrew Thompson
278985e7bb81SAndrew Thompson /* only interested in true associations */
279085e7bb81SAndrew Thompson if (isnew && ni->ni_associd != 0) {
279185e7bb81SAndrew Thompson /*
279285e7bb81SAndrew Thompson * This function could is called though timeout function.
279385e7bb81SAndrew Thompson * Need to defer.
279485e7bb81SAndrew Thompson */
279585e7bb81SAndrew Thompson uint32_t cnt = RUN_CMDQ_GET(&sc->cmdq_store);
2796109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_STATE, "cmdq_store=%d\n", cnt);
279785e7bb81SAndrew Thompson sc->cmdq[cnt].func = run_newassoc_cb;
279885e7bb81SAndrew Thompson sc->cmdq[cnt].arg0 = NULL;
279985e7bb81SAndrew Thompson sc->cmdq[cnt].arg1 = ni;
280085e7bb81SAndrew Thompson sc->cmdq[cnt].wcid = wcid;
280185e7bb81SAndrew Thompson ieee80211_runtask(ic, &sc->cmdq_task);
280285e7bb81SAndrew Thompson }
280385e7bb81SAndrew Thompson
2804109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_STATE,
2805109005ccSGavin Atkinson "new assoc isnew=%d associd=%x addr=%s\n",
280685e7bb81SAndrew Thompson isnew, ni->ni_associd, ether_sprintf(ni->ni_macaddr));
280785e7bb81SAndrew Thompson
280885e7bb81SAndrew Thompson rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate;
2809f520d761SAdrian Chadd /* XXX TODO: methodize with MCS rates */
281085e7bb81SAndrew Thompson for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
281185e7bb81SAndrew Thompson if (rt2860_rates[ridx].rate == rate)
281285e7bb81SAndrew Thompson break;
281385e7bb81SAndrew Thompson rn->mgt_ridx = ridx;
2814109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_STATE | RUN_DEBUG_RATE,
2815109005ccSGavin Atkinson "rate=%d, mgmt_ridx=%d\n", rate, rn->mgt_ridx);
281685e7bb81SAndrew Thompson
281744bf877eSHans Petter Selasky RUN_LOCK(sc);
281844bf877eSHans Petter Selasky if(sc->ratectl_run != RUN_RATECTL_OFF)
281985e7bb81SAndrew Thompson usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc);
282044bf877eSHans Petter Selasky RUN_UNLOCK(sc);
2821069f1a80SAndrew Thompson }
2822069f1a80SAndrew Thompson
2823069f1a80SAndrew Thompson /*
2824069f1a80SAndrew Thompson * Return the Rx chain with the highest RSSI for a given frame.
2825069f1a80SAndrew Thompson */
2826069f1a80SAndrew Thompson static __inline uint8_t
run_maxrssi_chain(struct run_softc * sc,const struct rt2860_rxwi * rxwi)2827069f1a80SAndrew Thompson run_maxrssi_chain(struct run_softc *sc, const struct rt2860_rxwi *rxwi)
2828069f1a80SAndrew Thompson {
2829069f1a80SAndrew Thompson uint8_t rxchain = 0;
2830069f1a80SAndrew Thompson
2831069f1a80SAndrew Thompson if (sc->nrxchains > 1) {
2832069f1a80SAndrew Thompson if (rxwi->rssi[1] > rxwi->rssi[rxchain])
2833069f1a80SAndrew Thompson rxchain = 1;
2834069f1a80SAndrew Thompson if (sc->nrxchains > 2)
2835069f1a80SAndrew Thompson if (rxwi->rssi[2] > rxwi->rssi[rxchain])
2836069f1a80SAndrew Thompson rxchain = 2;
2837069f1a80SAndrew Thompson }
283875c76159SAndrew Thompson return (rxchain);
2839069f1a80SAndrew Thompson }
2840069f1a80SAndrew Thompson
2841069f1a80SAndrew Thompson static void
run_recv_mgmt(struct ieee80211_node * ni,struct mbuf * m,int subtype,const struct ieee80211_rx_stats * rxs,int rssi,int nf)284299feb202SAdrian Chadd run_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype,
284399feb202SAdrian Chadd const struct ieee80211_rx_stats *rxs, int rssi, int nf)
284499feb202SAdrian Chadd {
284599feb202SAdrian Chadd struct ieee80211vap *vap = ni->ni_vap;
284699feb202SAdrian Chadd struct run_softc *sc = vap->iv_ic->ic_softc;
284799feb202SAdrian Chadd struct run_vap *rvp = RUN_VAP(vap);
284899feb202SAdrian Chadd uint64_t ni_tstamp, rx_tstamp;
284999feb202SAdrian Chadd
285099feb202SAdrian Chadd rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf);
285199feb202SAdrian Chadd
285299feb202SAdrian Chadd if (vap->iv_state == IEEE80211_S_RUN &&
285399feb202SAdrian Chadd (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
285499feb202SAdrian Chadd subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) {
285599feb202SAdrian Chadd ni_tstamp = le64toh(ni->ni_tstamp.tsf);
285699feb202SAdrian Chadd RUN_LOCK(sc);
285799feb202SAdrian Chadd run_get_tsf(sc, &rx_tstamp);
285899feb202SAdrian Chadd RUN_UNLOCK(sc);
285999feb202SAdrian Chadd rx_tstamp = le64toh(rx_tstamp);
286099feb202SAdrian Chadd
286199feb202SAdrian Chadd if (ni_tstamp >= rx_tstamp) {
2862109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV | RUN_DEBUG_BEACON,
2863109005ccSGavin Atkinson "ibss merge, tsf %ju tstamp %ju\n",
286499feb202SAdrian Chadd (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp);
286599feb202SAdrian Chadd (void) ieee80211_ibss_merge(ni);
286699feb202SAdrian Chadd }
286799feb202SAdrian Chadd }
286899feb202SAdrian Chadd }
286999feb202SAdrian Chadd
287099feb202SAdrian Chadd static void
run_rx_frame(struct run_softc * sc,struct mbuf * m,uint32_t dmalen)2871069f1a80SAndrew Thompson run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen)
2872069f1a80SAndrew Thompson {
28737a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
2874069f1a80SAndrew Thompson struct ieee80211_frame *wh;
2875069f1a80SAndrew Thompson struct ieee80211_node *ni;
2876069f1a80SAndrew Thompson struct rt2870_rxd *rxd;
2877069f1a80SAndrew Thompson struct rt2860_rxwi *rxwi;
2878069f1a80SAndrew Thompson uint32_t flags;
2879242dbae3SKevin Lo uint16_t len, rxwisize;
2880069f1a80SAndrew Thompson uint8_t ant, rssi;
2881069f1a80SAndrew Thompson int8_t nf;
2882069f1a80SAndrew Thompson
28837a7e01caSKevin Lo rxwisize = sizeof(struct rt2860_rxwi);
28847a7e01caSKevin Lo if (sc->mac_ver == 0x5592)
28857a7e01caSKevin Lo rxwisize += sizeof(uint64_t);
28867a7e01caSKevin Lo else if (sc->mac_ver == 0x3593)
28877a7e01caSKevin Lo rxwisize += sizeof(uint32_t);
2888fb10d4eeSAndriy Voskoboinyk
2889fb10d4eeSAndriy Voskoboinyk if (__predict_false(dmalen <
2890fb10d4eeSAndriy Voskoboinyk rxwisize + sizeof(struct ieee80211_frame_ack))) {
2891fb10d4eeSAndriy Voskoboinyk RUN_DPRINTF(sc, RUN_DEBUG_RECV,
2892fb10d4eeSAndriy Voskoboinyk "payload is too short: dma length %u < %zu\n",
2893fb10d4eeSAndriy Voskoboinyk dmalen, rxwisize + sizeof(struct ieee80211_frame_ack));
2894fb10d4eeSAndriy Voskoboinyk goto fail;
2895fb10d4eeSAndriy Voskoboinyk }
2896fb10d4eeSAndriy Voskoboinyk
2897fb10d4eeSAndriy Voskoboinyk rxwi = mtod(m, struct rt2860_rxwi *);
2898fb10d4eeSAndriy Voskoboinyk len = le16toh(rxwi->len) & 0xfff;
2899fb10d4eeSAndriy Voskoboinyk
2900fb10d4eeSAndriy Voskoboinyk if (__predict_false(len > dmalen - rxwisize)) {
2901109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV,
2902109005ccSGavin Atkinson "bad RXWI length %u > %u\n", len, dmalen);
2903fb10d4eeSAndriy Voskoboinyk goto fail;
2904069f1a80SAndrew Thompson }
2905fb10d4eeSAndriy Voskoboinyk
2906069f1a80SAndrew Thompson /* Rx descriptor is located at the end */
2907069f1a80SAndrew Thompson rxd = (struct rt2870_rxd *)(mtod(m, caddr_t) + dmalen);
2908069f1a80SAndrew Thompson flags = le32toh(rxd->flags);
2909069f1a80SAndrew Thompson
2910069f1a80SAndrew Thompson if (__predict_false(flags & (RT2860_RX_CRCERR | RT2860_RX_ICVERR))) {
2911109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s error.\n",
2912109005ccSGavin Atkinson (flags & RT2860_RX_CRCERR)?"CRC":"ICV");
2913fb10d4eeSAndriy Voskoboinyk goto fail;
2914069f1a80SAndrew Thompson }
2915069f1a80SAndrew Thompson
2916069f1a80SAndrew Thompson if (flags & RT2860_RX_L2PAD) {
2917109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV,
2918109005ccSGavin Atkinson "received RT2860_RX_L2PAD frame\n");
2919069f1a80SAndrew Thompson len += 2;
2920069f1a80SAndrew Thompson }
2921069f1a80SAndrew Thompson
2922fb10d4eeSAndriy Voskoboinyk m->m_data += rxwisize;
2923fb10d4eeSAndriy Voskoboinyk m->m_pkthdr.len = m->m_len = len;
2924fb10d4eeSAndriy Voskoboinyk
2925fb10d4eeSAndriy Voskoboinyk wh = mtod(m, struct ieee80211_frame *);
2926fb10d4eeSAndriy Voskoboinyk
2927e4c17012SAndriy Voskoboinyk if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) != 0 &&
2928e4c17012SAndriy Voskoboinyk (flags & RT2860_RX_DEC) != 0) {
2929fb10d4eeSAndriy Voskoboinyk wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
2930fb10d4eeSAndriy Voskoboinyk m->m_flags |= M_WEP;
2931fb10d4eeSAndriy Voskoboinyk }
2932fb10d4eeSAndriy Voskoboinyk
2933fb10d4eeSAndriy Voskoboinyk if (len >= sizeof(struct ieee80211_frame_min)) {
293485e7bb81SAndrew Thompson ni = ieee80211_find_rxnode(ic,
293585e7bb81SAndrew Thompson mtod(m, struct ieee80211_frame_min *));
2936fb10d4eeSAndriy Voskoboinyk } else
2937fb10d4eeSAndriy Voskoboinyk ni = NULL;
293885e7bb81SAndrew Thompson
2939f520d761SAdrian Chadd if(ni && ni->ni_flags & IEEE80211_NODE_HT) {
2940f520d761SAdrian Chadd m->m_flags |= M_AMPDU;
2941f520d761SAdrian Chadd }
2942f520d761SAdrian Chadd
2943069f1a80SAndrew Thompson if (__predict_false(flags & RT2860_RX_MICERR)) {
2944069f1a80SAndrew Thompson /* report MIC failures to net80211 for TKIP */
294585e7bb81SAndrew Thompson if (ni != NULL)
2946242dbae3SKevin Lo ieee80211_notify_michael_failure(ni->ni_vap, wh,
2947242dbae3SKevin Lo rxwi->keyidx);
2948109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV,
2949109005ccSGavin Atkinson "MIC error. Someone is lying.\n");
2950fb10d4eeSAndriy Voskoboinyk goto fail;
2951069f1a80SAndrew Thompson }
2952069f1a80SAndrew Thompson
2953069f1a80SAndrew Thompson ant = run_maxrssi_chain(sc, rxwi);
2954069f1a80SAndrew Thompson rssi = rxwi->rssi[ant];
2955069f1a80SAndrew Thompson nf = run_rssi2dbm(sc, rssi, ant);
2956069f1a80SAndrew Thompson
2957069f1a80SAndrew Thompson if (__predict_false(ieee80211_radiotap_active(ic))) {
2958069f1a80SAndrew Thompson struct run_rx_radiotap_header *tap = &sc->sc_rxtap;
2959361f264bSHans Petter Selasky uint16_t phy;
2960069f1a80SAndrew Thompson
2961069f1a80SAndrew Thompson tap->wr_flags = 0;
29621ea35eadSAndriy Voskoboinyk if (flags & RT2860_RX_L2PAD)
29631ea35eadSAndriy Voskoboinyk tap->wr_flags |= IEEE80211_RADIOTAP_F_DATAPAD;
2964069f1a80SAndrew Thompson tap->wr_antsignal = rssi;
2965069f1a80SAndrew Thompson tap->wr_antenna = ant;
2966069f1a80SAndrew Thompson tap->wr_dbm_antsignal = run_rssi2dbm(sc, rssi, ant);
2967069f1a80SAndrew Thompson tap->wr_rate = 2; /* in case it can't be found below */
2968cdf039afSAdrian Chadd RUN_LOCK(sc);
2969ef9c0768SKevin Lo run_get_tsf(sc, &tap->wr_tsf);
2970cdf039afSAdrian Chadd RUN_UNLOCK(sc);
2971069f1a80SAndrew Thompson phy = le16toh(rxwi->phy);
2972069f1a80SAndrew Thompson switch (phy & RT2860_PHY_MODE) {
2973069f1a80SAndrew Thompson case RT2860_PHY_CCK:
2974069f1a80SAndrew Thompson switch ((phy & RT2860_PHY_MCS) & ~RT2860_PHY_SHPRE) {
2975069f1a80SAndrew Thompson case 0: tap->wr_rate = 2; break;
2976069f1a80SAndrew Thompson case 1: tap->wr_rate = 4; break;
2977069f1a80SAndrew Thompson case 2: tap->wr_rate = 11; break;
2978069f1a80SAndrew Thompson case 3: tap->wr_rate = 22; break;
2979069f1a80SAndrew Thompson }
2980069f1a80SAndrew Thompson if (phy & RT2860_PHY_SHPRE)
2981069f1a80SAndrew Thompson tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
2982069f1a80SAndrew Thompson break;
2983069f1a80SAndrew Thompson case RT2860_PHY_OFDM:
2984069f1a80SAndrew Thompson switch (phy & RT2860_PHY_MCS) {
2985069f1a80SAndrew Thompson case 0: tap->wr_rate = 12; break;
2986069f1a80SAndrew Thompson case 1: tap->wr_rate = 18; break;
2987069f1a80SAndrew Thompson case 2: tap->wr_rate = 24; break;
2988069f1a80SAndrew Thompson case 3: tap->wr_rate = 36; break;
2989069f1a80SAndrew Thompson case 4: tap->wr_rate = 48; break;
2990069f1a80SAndrew Thompson case 5: tap->wr_rate = 72; break;
2991069f1a80SAndrew Thompson case 6: tap->wr_rate = 96; break;
2992069f1a80SAndrew Thompson case 7: tap->wr_rate = 108; break;
2993069f1a80SAndrew Thompson }
2994069f1a80SAndrew Thompson break;
2995069f1a80SAndrew Thompson }
2996069f1a80SAndrew Thompson }
2997cc522320SAndriy Voskoboinyk
2998cc522320SAndriy Voskoboinyk if (ni != NULL) {
2999cc522320SAndriy Voskoboinyk (void)ieee80211_input(ni, m, rssi, nf);
3000cc522320SAndriy Voskoboinyk ieee80211_free_node(ni);
3001cc522320SAndriy Voskoboinyk } else {
3002cc522320SAndriy Voskoboinyk (void)ieee80211_input_all(ic, m, rssi, nf);
3003cc522320SAndriy Voskoboinyk }
3004fb10d4eeSAndriy Voskoboinyk
3005fb10d4eeSAndriy Voskoboinyk return;
3006fb10d4eeSAndriy Voskoboinyk
3007fb10d4eeSAndriy Voskoboinyk fail:
3008fb10d4eeSAndriy Voskoboinyk m_freem(m);
3009fb10d4eeSAndriy Voskoboinyk counter_u64_add(ic->ic_ierrors, 1);
3010069f1a80SAndrew Thompson }
3011069f1a80SAndrew Thompson
3012069f1a80SAndrew Thompson static void
run_bulk_rx_callback(struct usb_xfer * xfer,usb_error_t error)3013069f1a80SAndrew Thompson run_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
3014069f1a80SAndrew Thompson {
3015069f1a80SAndrew Thompson struct run_softc *sc = usbd_xfer_softc(xfer);
30167a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
3017069f1a80SAndrew Thompson struct mbuf *m = NULL;
3018069f1a80SAndrew Thompson struct mbuf *m0;
3019fb10d4eeSAndriy Voskoboinyk uint32_t dmalen, mbuf_len;
3020242dbae3SKevin Lo uint16_t rxwisize;
3021069f1a80SAndrew Thompson int xferlen;
3022069f1a80SAndrew Thompson
30237a7e01caSKevin Lo rxwisize = sizeof(struct rt2860_rxwi);
30247a7e01caSKevin Lo if (sc->mac_ver == 0x5592)
30257a7e01caSKevin Lo rxwisize += sizeof(uint64_t);
30267a7e01caSKevin Lo else if (sc->mac_ver == 0x3593)
30277a7e01caSKevin Lo rxwisize += sizeof(uint32_t);
3028242dbae3SKevin Lo
3029069f1a80SAndrew Thompson usbd_xfer_status(xfer, &xferlen, NULL, NULL, NULL);
3030069f1a80SAndrew Thompson
3031069f1a80SAndrew Thompson switch (USB_GET_STATE(xfer)) {
3032069f1a80SAndrew Thompson case USB_ST_TRANSFERRED:
3033069f1a80SAndrew Thompson
3034109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV,
3035109005ccSGavin Atkinson "rx done, actlen=%d\n", xferlen);
3036069f1a80SAndrew Thompson
3037242dbae3SKevin Lo if (xferlen < (int)(sizeof(uint32_t) + rxwisize +
3038242dbae3SKevin Lo sizeof(struct rt2870_rxd))) {
3039109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB,
3040109005ccSGavin Atkinson "xfer too short %d\n", xferlen);
3041069f1a80SAndrew Thompson goto tr_setup;
3042069f1a80SAndrew Thompson }
3043069f1a80SAndrew Thompson
3044069f1a80SAndrew Thompson m = sc->rx_m;
3045069f1a80SAndrew Thompson sc->rx_m = NULL;
3046069f1a80SAndrew Thompson
3047069f1a80SAndrew Thompson /* FALLTHROUGH */
3048069f1a80SAndrew Thompson case USB_ST_SETUP:
3049069f1a80SAndrew Thompson tr_setup:
3050069f1a80SAndrew Thompson if (sc->rx_m == NULL) {
3051c6499eccSGleb Smirnoff sc->rx_m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
3052e649b526SAdrian Chadd RUN_MAX_RXSZ);
3053069f1a80SAndrew Thompson }
3054069f1a80SAndrew Thompson if (sc->rx_m == NULL) {
3055e649b526SAdrian Chadd RUN_DPRINTF(sc, RUN_DEBUG_RECV |
3056e649b526SAdrian Chadd RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB,
3057109005ccSGavin Atkinson "could not allocate mbuf - idle with stall\n");
30587a79cebfSGleb Smirnoff counter_u64_add(ic->ic_ierrors, 1);
3059069f1a80SAndrew Thompson usbd_xfer_set_stall(xfer);
3060069f1a80SAndrew Thompson usbd_xfer_set_frames(xfer, 0);
3061069f1a80SAndrew Thompson } else {
3062069f1a80SAndrew Thompson /*
3063069f1a80SAndrew Thompson * Directly loading a mbuf cluster into DMA to
3064069f1a80SAndrew Thompson * save some data copying. This works because
3065069f1a80SAndrew Thompson * there is only one cluster.
3066069f1a80SAndrew Thompson */
3067069f1a80SAndrew Thompson usbd_xfer_set_frame_data(xfer, 0,
3068069f1a80SAndrew Thompson mtod(sc->rx_m, caddr_t), RUN_MAX_RXSZ);
3069069f1a80SAndrew Thompson usbd_xfer_set_frames(xfer, 1);
3070069f1a80SAndrew Thompson }
3071069f1a80SAndrew Thompson usbd_transfer_submit(xfer);
3072069f1a80SAndrew Thompson break;
3073069f1a80SAndrew Thompson
3074069f1a80SAndrew Thompson default: /* Error */
3075069f1a80SAndrew Thompson if (error != USB_ERR_CANCELLED) {
3076069f1a80SAndrew Thompson /* try to clear stall first */
3077069f1a80SAndrew Thompson usbd_xfer_set_stall(xfer);
3078069f1a80SAndrew Thompson if (error == USB_ERR_TIMEOUT)
3079069f1a80SAndrew Thompson device_printf(sc->sc_dev, "device timeout\n");
30807a79cebfSGleb Smirnoff counter_u64_add(ic->ic_ierrors, 1);
3081069f1a80SAndrew Thompson goto tr_setup;
3082069f1a80SAndrew Thompson }
3083069f1a80SAndrew Thompson if (sc->rx_m != NULL) {
3084069f1a80SAndrew Thompson m_freem(sc->rx_m);
3085069f1a80SAndrew Thompson sc->rx_m = NULL;
3086069f1a80SAndrew Thompson }
3087069f1a80SAndrew Thompson break;
3088069f1a80SAndrew Thompson }
3089069f1a80SAndrew Thompson
3090069f1a80SAndrew Thompson if (m == NULL)
3091069f1a80SAndrew Thompson return;
3092069f1a80SAndrew Thompson
3093069f1a80SAndrew Thompson /* inputting all the frames must be last */
3094069f1a80SAndrew Thompson
3095069f1a80SAndrew Thompson RUN_UNLOCK(sc);
3096069f1a80SAndrew Thompson
3097069f1a80SAndrew Thompson m->m_pkthdr.len = m->m_len = xferlen;
3098069f1a80SAndrew Thompson
3099069f1a80SAndrew Thompson /* HW can aggregate multiple 802.11 frames in a single USB xfer */
3100069f1a80SAndrew Thompson for(;;) {
3101069f1a80SAndrew Thompson dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff;
3102069f1a80SAndrew Thompson
31036d917491SHans Petter Selasky if ((dmalen >= (uint32_t)-8) || (dmalen == 0) ||
31046d917491SHans Petter Selasky ((dmalen & 3) != 0)) {
3105109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB,
3106109005ccSGavin Atkinson "bad DMA length %u\n", dmalen);
3107069f1a80SAndrew Thompson break;
3108069f1a80SAndrew Thompson }
31096d917491SHans Petter Selasky if ((dmalen + 8) > (uint32_t)xferlen) {
3110109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB,
3111109005ccSGavin Atkinson "bad DMA length %u > %d\n",
3112069f1a80SAndrew Thompson dmalen + 8, xferlen);
3113069f1a80SAndrew Thompson break;
3114069f1a80SAndrew Thompson }
3115069f1a80SAndrew Thompson
3116069f1a80SAndrew Thompson /* If it is the last one or a single frame, we won't copy. */
3117069f1a80SAndrew Thompson if ((xferlen -= dmalen + 8) <= 8) {
3118069f1a80SAndrew Thompson /* trim 32-bit DMA-len header */
3119069f1a80SAndrew Thompson m->m_data += 4;
3120069f1a80SAndrew Thompson m->m_pkthdr.len = m->m_len -= 4;
3121069f1a80SAndrew Thompson run_rx_frame(sc, m, dmalen);
3122645826beSHans Petter Selasky m = NULL; /* don't free source buffer */
3123069f1a80SAndrew Thompson break;
3124069f1a80SAndrew Thompson }
3125069f1a80SAndrew Thompson
3126fb10d4eeSAndriy Voskoboinyk mbuf_len = dmalen + sizeof(struct rt2870_rxd);
3127fb10d4eeSAndriy Voskoboinyk if (__predict_false(mbuf_len > MCLBYTES)) {
3128fb10d4eeSAndriy Voskoboinyk RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB,
3129fb10d4eeSAndriy Voskoboinyk "payload is too big: mbuf_len %u\n", mbuf_len);
3130fb10d4eeSAndriy Voskoboinyk counter_u64_add(ic->ic_ierrors, 1);
3131fb10d4eeSAndriy Voskoboinyk break;
3132fb10d4eeSAndriy Voskoboinyk }
3133fb10d4eeSAndriy Voskoboinyk
3134069f1a80SAndrew Thompson /* copy aggregated frames to another mbuf */
3135c6499eccSGleb Smirnoff m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
3136069f1a80SAndrew Thompson if (__predict_false(m0 == NULL)) {
3137109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC,
3138109005ccSGavin Atkinson "could not allocate mbuf\n");
31397a79cebfSGleb Smirnoff counter_u64_add(ic->ic_ierrors, 1);
3140069f1a80SAndrew Thompson break;
3141069f1a80SAndrew Thompson }
3142069f1a80SAndrew Thompson m_copydata(m, 4 /* skip 32-bit DMA-len header */,
3143fb10d4eeSAndriy Voskoboinyk mbuf_len, mtod(m0, caddr_t));
3144fb10d4eeSAndriy Voskoboinyk m0->m_pkthdr.len = m0->m_len = mbuf_len;
3145069f1a80SAndrew Thompson run_rx_frame(sc, m0, dmalen);
3146069f1a80SAndrew Thompson
3147069f1a80SAndrew Thompson /* update data ptr */
3148fb10d4eeSAndriy Voskoboinyk m->m_data += mbuf_len + 4;
3149fb10d4eeSAndriy Voskoboinyk m->m_pkthdr.len = m->m_len -= mbuf_len + 4;
3150069f1a80SAndrew Thompson }
3151069f1a80SAndrew Thompson
3152645826beSHans Petter Selasky /* make sure we free the source buffer, if any */
3153645826beSHans Petter Selasky m_freem(m);
3154645826beSHans Petter Selasky
3155f520d761SAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG
3156f520d761SAdrian Chadd ieee80211_ff_age_all(ic, 100);
3157f520d761SAdrian Chadd #endif
3158069f1a80SAndrew Thompson RUN_LOCK(sc);
3159069f1a80SAndrew Thompson }
3160069f1a80SAndrew Thompson
3161069f1a80SAndrew Thompson static void
run_tx_free(struct run_endpoint_queue * pq,struct run_tx_data * data,int txerr)3162069f1a80SAndrew Thompson run_tx_free(struct run_endpoint_queue *pq,
3163069f1a80SAndrew Thompson struct run_tx_data *data, int txerr)
3164069f1a80SAndrew Thompson {
3165069f1a80SAndrew Thompson
3166db70df04SAndriy Voskoboinyk ieee80211_tx_complete(data->ni, data->m, txerr);
3167db70df04SAndriy Voskoboinyk
3168db70df04SAndriy Voskoboinyk data->m = NULL;
3169069f1a80SAndrew Thompson data->ni = NULL;
3170069f1a80SAndrew Thompson
3171069f1a80SAndrew Thompson STAILQ_INSERT_TAIL(&pq->tx_fh, data, next);
3172069f1a80SAndrew Thompson pq->tx_nfree++;
3173069f1a80SAndrew Thompson }
3174069f1a80SAndrew Thompson
3175069f1a80SAndrew Thompson static void
run_bulk_tx_callbackN(struct usb_xfer * xfer,usb_error_t error,u_int index)317635a24898SHans Petter Selasky run_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index)
3177069f1a80SAndrew Thompson {
3178069f1a80SAndrew Thompson struct run_softc *sc = usbd_xfer_softc(xfer);
31797a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
3180069f1a80SAndrew Thompson struct run_tx_data *data;
3181069f1a80SAndrew Thompson struct ieee80211vap *vap = NULL;
3182069f1a80SAndrew Thompson struct usb_page_cache *pc;
3183069f1a80SAndrew Thompson struct run_endpoint_queue *pq = &sc->sc_epq[index];
3184069f1a80SAndrew Thompson struct mbuf *m;
3185069f1a80SAndrew Thompson usb_frlength_t size;
3186069f1a80SAndrew Thompson int actlen;
3187069f1a80SAndrew Thompson int sumlen;
3188069f1a80SAndrew Thompson
3189069f1a80SAndrew Thompson usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
3190069f1a80SAndrew Thompson
3191069f1a80SAndrew Thompson switch (USB_GET_STATE(xfer)) {
3192069f1a80SAndrew Thompson case USB_ST_TRANSFERRED:
3193109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB,
3194109005ccSGavin Atkinson "transfer complete: %d bytes @ index %d\n", actlen, index);
3195069f1a80SAndrew Thompson
3196069f1a80SAndrew Thompson data = usbd_xfer_get_priv(xfer);
3197069f1a80SAndrew Thompson run_tx_free(pq, data, 0);
3198069f1a80SAndrew Thompson usbd_xfer_set_priv(xfer, NULL);
3199069f1a80SAndrew Thompson
3200069f1a80SAndrew Thompson /* FALLTHROUGH */
3201069f1a80SAndrew Thompson case USB_ST_SETUP:
3202069f1a80SAndrew Thompson tr_setup:
3203069f1a80SAndrew Thompson data = STAILQ_FIRST(&pq->tx_qh);
3204069f1a80SAndrew Thompson if (data == NULL)
3205069f1a80SAndrew Thompson break;
3206069f1a80SAndrew Thompson
3207069f1a80SAndrew Thompson STAILQ_REMOVE_HEAD(&pq->tx_qh, next);
3208069f1a80SAndrew Thompson
3209069f1a80SAndrew Thompson m = data->m;
3210242dbae3SKevin Lo size = (sc->mac_ver == 0x5592) ?
3211198fc7c3SHans Petter Selasky sizeof(data->desc) + sizeof(uint32_t) : sizeof(data->desc);
32122719d9c9SHans Petter Selasky if ((m->m_pkthdr.len +
3213198fc7c3SHans Petter Selasky size + 3 + 8) > RUN_MAX_TXSZ) {
3214109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT_DESC | RUN_DEBUG_USB,
3215109005ccSGavin Atkinson "data overflow, %u bytes\n", m->m_pkthdr.len);
3216069f1a80SAndrew Thompson run_tx_free(pq, data, 1);
3217069f1a80SAndrew Thompson goto tr_setup;
3218069f1a80SAndrew Thompson }
3219069f1a80SAndrew Thompson
3220069f1a80SAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0);
3221069f1a80SAndrew Thompson usbd_copy_in(pc, 0, &data->desc, size);
3222069f1a80SAndrew Thompson usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len);
322358a1ff3bSHans Petter Selasky size += m->m_pkthdr.len;
322458a1ff3bSHans Petter Selasky /*
322558a1ff3bSHans Petter Selasky * Align end on a 4-byte boundary, pad 8 bytes (CRC +
322658a1ff3bSHans Petter Selasky * 4-byte padding), and be sure to zero those trailing
322758a1ff3bSHans Petter Selasky * bytes:
322858a1ff3bSHans Petter Selasky */
322958a1ff3bSHans Petter Selasky usbd_frame_zero(pc, size, ((-size) & 3) + 8);
323058a1ff3bSHans Petter Selasky size += ((-size) & 3) + 8;
3231069f1a80SAndrew Thompson
3232069f1a80SAndrew Thompson vap = data->ni->ni_vap;
3233069f1a80SAndrew Thompson if (ieee80211_radiotap_active_vap(vap)) {
32341ea35eadSAndriy Voskoboinyk const struct ieee80211_frame *wh;
3235069f1a80SAndrew Thompson struct run_tx_radiotap_header *tap = &sc->sc_txtap;
323685e7bb81SAndrew Thompson struct rt2860_txwi *txwi =
323785e7bb81SAndrew Thompson (struct rt2860_txwi *)(&data->desc + sizeof(struct rt2870_txd));
32381ea35eadSAndriy Voskoboinyk int has_l2pad;
32391ea35eadSAndriy Voskoboinyk
32401ea35eadSAndriy Voskoboinyk wh = mtod(m, struct ieee80211_frame *);
32411ea35eadSAndriy Voskoboinyk has_l2pad = IEEE80211_HAS_ADDR4(wh) !=
32421ea35eadSAndriy Voskoboinyk IEEE80211_QOS_HAS_SEQ(wh);
32431ea35eadSAndriy Voskoboinyk
3244069f1a80SAndrew Thompson tap->wt_flags = 0;
3245069f1a80SAndrew Thompson tap->wt_rate = rt2860_rates[data->ridx].rate;
3246069f1a80SAndrew Thompson tap->wt_hwqueue = index;
324785e7bb81SAndrew Thompson if (le16toh(txwi->phy) & RT2860_PHY_SHPRE)
3248069f1a80SAndrew Thompson tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
32491ea35eadSAndriy Voskoboinyk if (has_l2pad)
32501ea35eadSAndriy Voskoboinyk tap->wt_flags |= IEEE80211_RADIOTAP_F_DATAPAD;
3251069f1a80SAndrew Thompson
3252069f1a80SAndrew Thompson ieee80211_radiotap_tx(vap, m);
3253069f1a80SAndrew Thompson }
3254069f1a80SAndrew Thompson
3255109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB,
3256109005ccSGavin Atkinson "sending frame len=%u/%u @ index %d\n",
325758a1ff3bSHans Petter Selasky m->m_pkthdr.len, size, index);
3258069f1a80SAndrew Thompson
325958a1ff3bSHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, size);
3260069f1a80SAndrew Thompson usbd_xfer_set_priv(xfer, data);
3261069f1a80SAndrew Thompson usbd_transfer_submit(xfer);
32627a79cebfSGleb Smirnoff run_start(sc);
3263069f1a80SAndrew Thompson
3264069f1a80SAndrew Thompson break;
3265069f1a80SAndrew Thompson
3266069f1a80SAndrew Thompson default:
3267109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB,
3268109005ccSGavin Atkinson "USB transfer error, %s\n", usbd_errstr(error));
3269069f1a80SAndrew Thompson
3270069f1a80SAndrew Thompson data = usbd_xfer_get_priv(xfer);
3271069f1a80SAndrew Thompson
3272069f1a80SAndrew Thompson if (data != NULL) {
327385e7bb81SAndrew Thompson if(data->ni != NULL)
327485e7bb81SAndrew Thompson vap = data->ni->ni_vap;
3275069f1a80SAndrew Thompson run_tx_free(pq, data, error);
3276069f1a80SAndrew Thompson usbd_xfer_set_priv(xfer, NULL);
3277069f1a80SAndrew Thompson }
32787a79cebfSGleb Smirnoff
327985e7bb81SAndrew Thompson if (vap == NULL)
328085e7bb81SAndrew Thompson vap = TAILQ_FIRST(&ic->ic_vaps);
3281069f1a80SAndrew Thompson
3282069f1a80SAndrew Thompson if (error != USB_ERR_CANCELLED) {
3283069f1a80SAndrew Thompson if (error == USB_ERR_TIMEOUT) {
3284069f1a80SAndrew Thompson device_printf(sc->sc_dev, "device timeout\n");
328585e7bb81SAndrew Thompson uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store);
3286109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB,
3287109005ccSGavin Atkinson "cmdq_store=%d\n", i);
328885e7bb81SAndrew Thompson sc->cmdq[i].func = run_usb_timeout_cb;
328985e7bb81SAndrew Thompson sc->cmdq[i].arg0 = vap;
329085e7bb81SAndrew Thompson ieee80211_runtask(ic, &sc->cmdq_task);
3291069f1a80SAndrew Thompson }
3292069f1a80SAndrew Thompson
3293069f1a80SAndrew Thompson /*
3294069f1a80SAndrew Thompson * Try to clear stall first, also if other
3295069f1a80SAndrew Thompson * errors occur, hence clearing stall
3296069f1a80SAndrew Thompson * introduces a 50 ms delay:
3297069f1a80SAndrew Thompson */
3298069f1a80SAndrew Thompson usbd_xfer_set_stall(xfer);
3299069f1a80SAndrew Thompson goto tr_setup;
3300069f1a80SAndrew Thompson }
3301069f1a80SAndrew Thompson break;
3302069f1a80SAndrew Thompson }
3303f520d761SAdrian Chadd #ifdef IEEE80211_SUPPORT_SUPERG
3304f520d761SAdrian Chadd /* XXX TODO: make this deferred rather than unlock/relock */
3305f520d761SAdrian Chadd /* XXX TODO: should only do the QoS AC this belongs to */
3306f520d761SAdrian Chadd if (pq->tx_nfree >= RUN_TX_RING_COUNT) {
3307f520d761SAdrian Chadd RUN_UNLOCK(sc);
3308f520d761SAdrian Chadd ieee80211_ff_flush_all(ic);
3309f520d761SAdrian Chadd RUN_LOCK(sc);
3310f520d761SAdrian Chadd }
3311f520d761SAdrian Chadd #endif
3312069f1a80SAndrew Thompson }
3313069f1a80SAndrew Thompson
3314069f1a80SAndrew Thompson static void
run_bulk_tx_callback0(struct usb_xfer * xfer,usb_error_t error)3315069f1a80SAndrew Thompson run_bulk_tx_callback0(struct usb_xfer *xfer, usb_error_t error)
3316069f1a80SAndrew Thompson {
3317069f1a80SAndrew Thompson run_bulk_tx_callbackN(xfer, error, 0);
3318069f1a80SAndrew Thompson }
3319069f1a80SAndrew Thompson
3320069f1a80SAndrew Thompson static void
run_bulk_tx_callback1(struct usb_xfer * xfer,usb_error_t error)3321069f1a80SAndrew Thompson run_bulk_tx_callback1(struct usb_xfer *xfer, usb_error_t error)
3322069f1a80SAndrew Thompson {
3323069f1a80SAndrew Thompson run_bulk_tx_callbackN(xfer, error, 1);
3324069f1a80SAndrew Thompson }
3325069f1a80SAndrew Thompson
3326069f1a80SAndrew Thompson static void
run_bulk_tx_callback2(struct usb_xfer * xfer,usb_error_t error)3327069f1a80SAndrew Thompson run_bulk_tx_callback2(struct usb_xfer *xfer, usb_error_t error)
3328069f1a80SAndrew Thompson {
3329069f1a80SAndrew Thompson run_bulk_tx_callbackN(xfer, error, 2);
3330069f1a80SAndrew Thompson }
3331069f1a80SAndrew Thompson
3332069f1a80SAndrew Thompson static void
run_bulk_tx_callback3(struct usb_xfer * xfer,usb_error_t error)3333069f1a80SAndrew Thompson run_bulk_tx_callback3(struct usb_xfer *xfer, usb_error_t error)
3334069f1a80SAndrew Thompson {
3335069f1a80SAndrew Thompson run_bulk_tx_callbackN(xfer, error, 3);
3336069f1a80SAndrew Thompson }
3337069f1a80SAndrew Thompson
3338069f1a80SAndrew Thompson static void
run_bulk_tx_callback4(struct usb_xfer * xfer,usb_error_t error)3339069f1a80SAndrew Thompson run_bulk_tx_callback4(struct usb_xfer *xfer, usb_error_t error)
3340069f1a80SAndrew Thompson {
3341069f1a80SAndrew Thompson run_bulk_tx_callbackN(xfer, error, 4);
3342069f1a80SAndrew Thompson }
3343069f1a80SAndrew Thompson
3344069f1a80SAndrew Thompson static void
run_bulk_tx_callback5(struct usb_xfer * xfer,usb_error_t error)3345069f1a80SAndrew Thompson run_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error)
3346069f1a80SAndrew Thompson {
3347069f1a80SAndrew Thompson run_bulk_tx_callbackN(xfer, error, 5);
3348069f1a80SAndrew Thompson }
3349069f1a80SAndrew Thompson
3350069f1a80SAndrew Thompson static void
run_set_tx_desc(struct run_softc * sc,struct run_tx_data * data)335185e7bb81SAndrew Thompson run_set_tx_desc(struct run_softc *sc, struct run_tx_data *data)
3352069f1a80SAndrew Thompson {
3353069f1a80SAndrew Thompson struct mbuf *m = data->m;
33547a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
335585e7bb81SAndrew Thompson struct ieee80211vap *vap = data->ni->ni_vap;
3356069f1a80SAndrew Thompson struct ieee80211_frame *wh;
3357069f1a80SAndrew Thompson struct rt2870_txd *txd;
3358069f1a80SAndrew Thompson struct rt2860_txwi *txwi;
3359242dbae3SKevin Lo uint16_t xferlen, txwisize;
336085e7bb81SAndrew Thompson uint16_t mcs;
3361069f1a80SAndrew Thompson uint8_t ridx = data->ridx;
336285e7bb81SAndrew Thompson uint8_t pad;
3363069f1a80SAndrew Thompson
3364069f1a80SAndrew Thompson /* get MCS code from rate index */
336585e7bb81SAndrew Thompson mcs = rt2860_rates[ridx].mcs;
3366069f1a80SAndrew Thompson
3367242dbae3SKevin Lo txwisize = (sc->mac_ver == 0x5592) ?
3368242dbae3SKevin Lo sizeof(*txwi) + sizeof(uint32_t) : sizeof(*txwi);
3369242dbae3SKevin Lo xferlen = txwisize + m->m_pkthdr.len;
3370069f1a80SAndrew Thompson
3371069f1a80SAndrew Thompson /* roundup to 32-bit alignment */
3372069f1a80SAndrew Thompson xferlen = (xferlen + 3) & ~3;
3373069f1a80SAndrew Thompson
3374069f1a80SAndrew Thompson txd = (struct rt2870_txd *)&data->desc;
3375069f1a80SAndrew Thompson txd->len = htole16(xferlen);
3376069f1a80SAndrew Thompson
337785e7bb81SAndrew Thompson wh = mtod(m, struct ieee80211_frame *);
337885e7bb81SAndrew Thompson
337985e7bb81SAndrew Thompson /*
338085e7bb81SAndrew Thompson * Ether both are true or both are false, the header
338185e7bb81SAndrew Thompson * are nicely aligned to 32-bit. So, no L2 padding.
338285e7bb81SAndrew Thompson */
338385e7bb81SAndrew Thompson if(IEEE80211_HAS_ADDR4(wh) == IEEE80211_QOS_HAS_SEQ(wh))
338485e7bb81SAndrew Thompson pad = 0;
338585e7bb81SAndrew Thompson else
338685e7bb81SAndrew Thompson pad = 2;
338785e7bb81SAndrew Thompson
3388069f1a80SAndrew Thompson /* setup TX Wireless Information */
3389069f1a80SAndrew Thompson txwi = (struct rt2860_txwi *)(txd + 1);
3390069f1a80SAndrew Thompson txwi->len = htole16(m->m_pkthdr.len - pad);
3391069f1a80SAndrew Thompson if (rt2860_rates[ridx].phy == IEEE80211_T_DS) {
339251a25d34SKevin Lo mcs |= RT2860_PHY_CCK;
3393069f1a80SAndrew Thompson if (ridx != RT2860_RIDX_CCK1 &&
3394069f1a80SAndrew Thompson (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
3395069f1a80SAndrew Thompson mcs |= RT2860_PHY_SHPRE;
3396f520d761SAdrian Chadd } else if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) {
339751a25d34SKevin Lo mcs |= RT2860_PHY_OFDM;
3398f520d761SAdrian Chadd } else if (rt2860_rates[ridx].phy == IEEE80211_T_HT) {
3399f520d761SAdrian Chadd /* XXX TODO: [adrian] set short preamble for MCS? */
34006bc40d8dSAdrian Chadd mcs |= RT2860_PHY_HT_MIX; /* Mixed, not greenfield */
3401f520d761SAdrian Chadd }
340251a25d34SKevin Lo txwi->phy = htole16(mcs);
3403069f1a80SAndrew Thompson
3404069f1a80SAndrew Thompson /* check if RTS/CTS or CTS-to-self protection is required */
3405069f1a80SAndrew Thompson if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
3406f520d761SAdrian Chadd ((m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) ||
3407069f1a80SAndrew Thompson ((ic->ic_flags & IEEE80211_F_USEPROT) &&
3408f520d761SAdrian Chadd rt2860_rates[ridx].phy == IEEE80211_T_OFDM) ||
3409f520d761SAdrian Chadd ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) &&
3410f520d761SAdrian Chadd rt2860_rates[ridx].phy == IEEE80211_T_HT)))
341185e7bb81SAndrew Thompson txwi->txop |= RT2860_TX_TXOP_HT;
3412069f1a80SAndrew Thompson else
341385e7bb81SAndrew Thompson txwi->txop |= RT2860_TX_TXOP_BACKOFF;
3414beaa0537SAndrew Thompson
3415beaa0537SAndrew Thompson if (vap->iv_opmode != IEEE80211_M_STA && !IEEE80211_QOS_HAS_SEQ(wh))
3416beaa0537SAndrew Thompson txwi->xflags |= RT2860_TX_NSEQ;
3417069f1a80SAndrew Thompson }
3418069f1a80SAndrew Thompson
3419069f1a80SAndrew Thompson /* This function must be called locked */
3420069f1a80SAndrew Thompson static int
run_tx(struct run_softc * sc,struct mbuf * m,struct ieee80211_node * ni)3421069f1a80SAndrew Thompson run_tx(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
3422069f1a80SAndrew Thompson {
34237a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
342485e7bb81SAndrew Thompson struct ieee80211vap *vap = ni->ni_vap;
3425069f1a80SAndrew Thompson struct ieee80211_frame *wh;
3426f6313575SAndriy Voskoboinyk const struct ieee80211_txparam *tp = ni->ni_txparms;
3427de7eb46eSKevin Lo struct run_node *rn = RUN_NODE(ni);
3428069f1a80SAndrew Thompson struct run_tx_data *data;
342985e7bb81SAndrew Thompson struct rt2870_txd *txd;
343085e7bb81SAndrew Thompson struct rt2860_txwi *txwi;
3431069f1a80SAndrew Thompson uint16_t qos;
3432069f1a80SAndrew Thompson uint16_t dur;
343385e7bb81SAndrew Thompson uint16_t qid;
3434069f1a80SAndrew Thompson uint8_t type;
3435069f1a80SAndrew Thompson uint8_t tid;
343685e7bb81SAndrew Thompson uint8_t ridx;
343785e7bb81SAndrew Thompson uint8_t ctl_ridx;
3438069f1a80SAndrew Thompson uint8_t qflags;
3439069f1a80SAndrew Thompson uint8_t xflags = 0;
3440069f1a80SAndrew Thompson int hasqos;
3441069f1a80SAndrew Thompson
3442069f1a80SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
3443069f1a80SAndrew Thompson
3444069f1a80SAndrew Thompson wh = mtod(m, struct ieee80211_frame *);
3445069f1a80SAndrew Thompson
3446069f1a80SAndrew Thompson type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
3447069f1a80SAndrew Thompson
3448069f1a80SAndrew Thompson /*
3449069f1a80SAndrew Thompson * There are 7 bulk endpoints: 1 for RX
3450069f1a80SAndrew Thompson * and 6 for TX (4 EDCAs + HCCA + Prio).
3451069f1a80SAndrew Thompson * Update 03-14-2009: some devices like the Planex GW-US300MiniS
3452069f1a80SAndrew Thompson * seem to have only 4 TX bulk endpoints (Fukaumi Naoki).
3453069f1a80SAndrew Thompson */
3454069f1a80SAndrew Thompson if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) {
3455069f1a80SAndrew Thompson uint8_t *frm;
3456069f1a80SAndrew Thompson
3457f3f08e16SAndriy Voskoboinyk frm = ieee80211_getqos(wh);
3458069f1a80SAndrew Thompson qos = le16toh(*(const uint16_t *)frm);
3459069f1a80SAndrew Thompson tid = qos & IEEE80211_QOS_TID;
3460069f1a80SAndrew Thompson qid = TID_TO_WME_AC(tid);
3461069f1a80SAndrew Thompson } else {
3462069f1a80SAndrew Thompson qos = 0;
3463069f1a80SAndrew Thompson tid = 0;
3464069f1a80SAndrew Thompson qid = WME_AC_BE;
3465069f1a80SAndrew Thompson }
3466069f1a80SAndrew Thompson qflags = (qid < 4) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_HCCA;
3467069f1a80SAndrew Thompson
3468109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "qos %d\tqid %d\ttid %d\tqflags %x\n",
3469069f1a80SAndrew Thompson qos, qid, tid, qflags);
3470069f1a80SAndrew Thompson
3471069f1a80SAndrew Thompson /* pickup a rate index */
3472069f1a80SAndrew Thompson if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
347351a25d34SKevin Lo type != IEEE80211_FC0_TYPE_DATA || m->m_flags & M_EAPOL) {
3474f520d761SAdrian Chadd /* XXX TODO: methodize for 11n; use MCS0 for 11NA/11NG */
3475f520d761SAdrian Chadd ridx = (ic->ic_curmode == IEEE80211_MODE_11A || ic->ic_curmode == IEEE80211_MODE_11NA) ?
3476069f1a80SAndrew Thompson RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1;
3477069f1a80SAndrew Thompson ctl_ridx = rt2860_rates[ridx].ctl_ridx;
3478069f1a80SAndrew Thompson } else {
347985e7bb81SAndrew Thompson if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
348085e7bb81SAndrew Thompson ridx = rn->fix_ridx;
348185e7bb81SAndrew Thompson else
348285e7bb81SAndrew Thompson ridx = rn->amrr_ridx;
3483069f1a80SAndrew Thompson ctl_ridx = rt2860_rates[ridx].ctl_ridx;
3484069f1a80SAndrew Thompson }
3485069f1a80SAndrew Thompson
3486069f1a80SAndrew Thompson if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
3487069f1a80SAndrew Thompson (!hasqos || (qos & IEEE80211_QOS_ACKPOLICY) !=
3488069f1a80SAndrew Thompson IEEE80211_QOS_ACKPOLICY_NOACK)) {
3489beaa0537SAndrew Thompson xflags |= RT2860_TX_ACK;
3490069f1a80SAndrew Thompson if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
349185e7bb81SAndrew Thompson dur = rt2860_rates[ctl_ridx].sp_ack_dur;
3492069f1a80SAndrew Thompson else
349385e7bb81SAndrew Thompson dur = rt2860_rates[ctl_ridx].lp_ack_dur;
34948cfe5440SHans Petter Selasky USETW(wh->i_dur, dur);
3495069f1a80SAndrew Thompson }
3496069f1a80SAndrew Thompson
3497069f1a80SAndrew Thompson /* reserve slots for mgmt packets, just in case */
3498069f1a80SAndrew Thompson if (sc->sc_epq[qid].tx_nfree < 3) {
3499109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx ring %d is full\n", qid);
3500069f1a80SAndrew Thompson return (-1);
3501069f1a80SAndrew Thompson }
3502069f1a80SAndrew Thompson
3503069f1a80SAndrew Thompson data = STAILQ_FIRST(&sc->sc_epq[qid].tx_fh);
3504069f1a80SAndrew Thompson STAILQ_REMOVE_HEAD(&sc->sc_epq[qid].tx_fh, next);
3505069f1a80SAndrew Thompson sc->sc_epq[qid].tx_nfree--;
3506069f1a80SAndrew Thompson
350785e7bb81SAndrew Thompson txd = (struct rt2870_txd *)&data->desc;
350885e7bb81SAndrew Thompson txd->flags = qflags;
350985e7bb81SAndrew Thompson txwi = (struct rt2860_txwi *)(txd + 1);
351085e7bb81SAndrew Thompson txwi->xflags = xflags;
3511242dbae3SKevin Lo if (IEEE80211_IS_MULTICAST(wh->i_addr1))
3512d021dd19SHans Petter Selasky txwi->wcid = 0;
3513242dbae3SKevin Lo else
3514d021dd19SHans Petter Selasky txwi->wcid = (vap->iv_opmode == IEEE80211_M_STA) ?
3515d021dd19SHans Petter Selasky 1 : RUN_AID2WCID(ni->ni_associd);
3516242dbae3SKevin Lo
351785e7bb81SAndrew Thompson /* clear leftover garbage bits */
351885e7bb81SAndrew Thompson txwi->flags = 0;
351985e7bb81SAndrew Thompson txwi->txop = 0;
352085e7bb81SAndrew Thompson
3521069f1a80SAndrew Thompson data->m = m;
3522069f1a80SAndrew Thompson data->ni = ni;
3523069f1a80SAndrew Thompson data->ridx = ridx;
3524069f1a80SAndrew Thompson
352585e7bb81SAndrew Thompson run_set_tx_desc(sc, data);
352685e7bb81SAndrew Thompson
352785e7bb81SAndrew Thompson /*
352885e7bb81SAndrew Thompson * The chip keeps track of 2 kind of Tx stats,
352985e7bb81SAndrew Thompson * * TX_STAT_FIFO, for per WCID stats, and
353085e7bb81SAndrew Thompson * * TX_STA_CNT0 for all-TX-in-one stats.
353185e7bb81SAndrew Thompson *
353285e7bb81SAndrew Thompson * To use FIFO stats, we need to store MCS into the driver-private
353385e7bb81SAndrew Thompson * PacketID field. So that, we can tell whose stats when we read them.
353485e7bb81SAndrew Thompson * We add 1 to the MCS because setting the PacketID field to 0 means
353585e7bb81SAndrew Thompson * that we don't want feedback in TX_STAT_FIFO.
353685e7bb81SAndrew Thompson * And, that's what we want for STA mode, since TX_STA_CNT0 does the job.
353785e7bb81SAndrew Thompson *
353885e7bb81SAndrew Thompson * FIFO stats doesn't count Tx with WCID 0xff, so we do this in run_tx().
353985e7bb81SAndrew Thompson */
354085e7bb81SAndrew Thompson if (sc->rvp_cnt > 1 || vap->iv_opmode == IEEE80211_M_HOSTAP ||
354185e7bb81SAndrew Thompson vap->iv_opmode == IEEE80211_M_MBSS) {
354285e7bb81SAndrew Thompson uint16_t pid = (rt2860_rates[ridx].mcs + 1) & 0xf;
354385e7bb81SAndrew Thompson txwi->len |= htole16(pid << RT2860_TX_PID_SHIFT);
354485e7bb81SAndrew Thompson
354585e7bb81SAndrew Thompson /*
354685e7bb81SAndrew Thompson * Unlike PCI based devices, we don't get any interrupt from
354785e7bb81SAndrew Thompson * USB devices, so we simulate FIFO-is-full interrupt here.
354820733245SPedro F. Giffuni * Ralink recommends to drain FIFO stats every 100 ms, but 16 slots
354985e7bb81SAndrew Thompson * quickly get fulled. To prevent overflow, increment a counter on
355085e7bb81SAndrew Thompson * every FIFO stat request, so we know how many slots are left.
355185e7bb81SAndrew Thompson * We do this only in HOSTAP or multiple vap mode since FIFO stats
355285e7bb81SAndrew Thompson * are used only in those modes.
355385e7bb81SAndrew Thompson * We just drain stats. AMRR gets updated every 1 sec by
355485e7bb81SAndrew Thompson * run_ratectl_cb() via callout.
355585e7bb81SAndrew Thompson * Call it early. Otherwise overflow.
355685e7bb81SAndrew Thompson */
355785e7bb81SAndrew Thompson if (sc->fifo_cnt++ == 10) {
355885e7bb81SAndrew Thompson /*
355985e7bb81SAndrew Thompson * With multiple vaps or if_bridge, if_start() is called
356085e7bb81SAndrew Thompson * with a non-sleepable lock, tcpinp. So, need to defer.
356185e7bb81SAndrew Thompson */
356285e7bb81SAndrew Thompson uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store);
3563109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "cmdq_store=%d\n", i);
356485e7bb81SAndrew Thompson sc->cmdq[i].func = run_drain_fifo;
356585e7bb81SAndrew Thompson sc->cmdq[i].arg0 = sc;
356685e7bb81SAndrew Thompson ieee80211_runtask(ic, &sc->cmdq_task);
356785e7bb81SAndrew Thompson }
356885e7bb81SAndrew Thompson }
3569069f1a80SAndrew Thompson
3570069f1a80SAndrew Thompson STAILQ_INSERT_TAIL(&sc->sc_epq[qid].tx_qh, data, next);
3571069f1a80SAndrew Thompson
3572069f1a80SAndrew Thompson usbd_transfer_start(sc->sc_xfer[qid]);
3573069f1a80SAndrew Thompson
3574109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT,
3575109005ccSGavin Atkinson "sending data frame len=%d rate=%d qid=%d\n",
3576242dbae3SKevin Lo m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) +
3577daab849bSHans Petter Selasky sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate, qid);
3578069f1a80SAndrew Thompson
3579069f1a80SAndrew Thompson return (0);
3580069f1a80SAndrew Thompson }
3581069f1a80SAndrew Thompson
3582069f1a80SAndrew Thompson static int
run_tx_mgt(struct run_softc * sc,struct mbuf * m,struct ieee80211_node * ni)3583069f1a80SAndrew Thompson run_tx_mgt(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
3584069f1a80SAndrew Thompson {
35857a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
3586de7eb46eSKevin Lo struct run_node *rn = RUN_NODE(ni);
3587069f1a80SAndrew Thompson struct run_tx_data *data;
3588069f1a80SAndrew Thompson struct ieee80211_frame *wh;
358985e7bb81SAndrew Thompson struct rt2870_txd *txd;
359085e7bb81SAndrew Thompson struct rt2860_txwi *txwi;
3591069f1a80SAndrew Thompson uint16_t dur;
359285e7bb81SAndrew Thompson uint8_t ridx = rn->mgt_ridx;
3593069f1a80SAndrew Thompson uint8_t xflags = 0;
359485e7bb81SAndrew Thompson uint8_t wflags = 0;
3595069f1a80SAndrew Thompson
3596069f1a80SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
3597069f1a80SAndrew Thompson
3598069f1a80SAndrew Thompson wh = mtod(m, struct ieee80211_frame *);
3599069f1a80SAndrew Thompson
3600069f1a80SAndrew Thompson /* tell hardware to add timestamp for probe responses */
3601c249cc38SAdrian Chadd if (IEEE80211_IS_MGMT_PROBE_RESP(wh))
360285e7bb81SAndrew Thompson wflags |= RT2860_TX_TS;
360385e7bb81SAndrew Thompson else if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
360485e7bb81SAndrew Thompson xflags |= RT2860_TX_ACK;
360585e7bb81SAndrew Thompson
360685e7bb81SAndrew Thompson dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate,
360785e7bb81SAndrew Thompson ic->ic_flags & IEEE80211_F_SHPREAMBLE);
36088cfe5440SHans Petter Selasky USETW(wh->i_dur, dur);
3609069f1a80SAndrew Thompson }
3610069f1a80SAndrew Thompson
36117a79cebfSGleb Smirnoff if (sc->sc_epq[0].tx_nfree == 0)
3612069f1a80SAndrew Thompson /* let caller free mbuf */
3613069f1a80SAndrew Thompson return (EIO);
3614069f1a80SAndrew Thompson data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
3615069f1a80SAndrew Thompson STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
3616069f1a80SAndrew Thompson sc->sc_epq[0].tx_nfree--;
3617069f1a80SAndrew Thompson
361885e7bb81SAndrew Thompson txd = (struct rt2870_txd *)&data->desc;
361985e7bb81SAndrew Thompson txd->flags = RT2860_TX_QSEL_EDCA;
362085e7bb81SAndrew Thompson txwi = (struct rt2860_txwi *)(txd + 1);
362185e7bb81SAndrew Thompson txwi->wcid = 0xff;
362285e7bb81SAndrew Thompson txwi->flags = wflags;
362385e7bb81SAndrew Thompson txwi->xflags = xflags;
362485e7bb81SAndrew Thompson txwi->txop = 0; /* clear leftover garbage bits */
362585e7bb81SAndrew Thompson
3626069f1a80SAndrew Thompson data->m = m;
3627069f1a80SAndrew Thompson data->ni = ni;
3628069f1a80SAndrew Thompson data->ridx = ridx;
3629069f1a80SAndrew Thompson
363085e7bb81SAndrew Thompson run_set_tx_desc(sc, data);
3631069f1a80SAndrew Thompson
3632109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending mgt frame len=%d rate=%d\n",
3633109005ccSGavin Atkinson m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) +
3634109005ccSGavin Atkinson sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate);
3635069f1a80SAndrew Thompson
3636069f1a80SAndrew Thompson STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);
3637069f1a80SAndrew Thompson
3638069f1a80SAndrew Thompson usbd_transfer_start(sc->sc_xfer[0]);
3639069f1a80SAndrew Thompson
3640069f1a80SAndrew Thompson return (0);
3641069f1a80SAndrew Thompson }
3642069f1a80SAndrew Thompson
3643069f1a80SAndrew Thompson static int
run_sendprot(struct run_softc * sc,const struct mbuf * m,struct ieee80211_node * ni,int prot,int rate)3644069f1a80SAndrew Thompson run_sendprot(struct run_softc *sc,
3645069f1a80SAndrew Thompson const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
3646069f1a80SAndrew Thompson {
3647069f1a80SAndrew Thompson struct ieee80211com *ic = ni->ni_ic;
3648069f1a80SAndrew Thompson struct run_tx_data *data;
364985e7bb81SAndrew Thompson struct rt2870_txd *txd;
365085e7bb81SAndrew Thompson struct rt2860_txwi *txwi;
3651069f1a80SAndrew Thompson struct mbuf *mprot;
3652069f1a80SAndrew Thompson int ridx;
3653069f1a80SAndrew Thompson int protrate;
365485e7bb81SAndrew Thompson uint8_t wflags = 0;
365585e7bb81SAndrew Thompson uint8_t xflags = 0;
3656069f1a80SAndrew Thompson
3657069f1a80SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
3658069f1a80SAndrew Thompson
3659069f1a80SAndrew Thompson /* check that there are free slots before allocating the mbuf */
36607a79cebfSGleb Smirnoff if (sc->sc_epq[0].tx_nfree == 0)
3661069f1a80SAndrew Thompson /* let caller free mbuf */
3662069f1a80SAndrew Thompson return (ENOBUFS);
3663069f1a80SAndrew Thompson
3664d1b67106SAndriy Voskoboinyk mprot = ieee80211_alloc_prot(ni, m, rate, prot);
3665069f1a80SAndrew Thompson if (mprot == NULL) {
36667a79cebfSGleb Smirnoff if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1);
3667109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "could not allocate mbuf\n");
3668069f1a80SAndrew Thompson return (ENOBUFS);
3669069f1a80SAndrew Thompson }
3670069f1a80SAndrew Thompson
3671d1b67106SAndriy Voskoboinyk protrate = ieee80211_ctl_rate(ic->ic_rt, rate);
3672d1b67106SAndriy Voskoboinyk wflags = RT2860_TX_FRAG;
3673d1b67106SAndriy Voskoboinyk xflags = 0;
3674d1b67106SAndriy Voskoboinyk if (prot == IEEE80211_PROT_RTSCTS)
3675d1b67106SAndriy Voskoboinyk xflags |= RT2860_TX_ACK;
3676d1b67106SAndriy Voskoboinyk
3677069f1a80SAndrew Thompson data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
3678069f1a80SAndrew Thompson STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
3679069f1a80SAndrew Thompson sc->sc_epq[0].tx_nfree--;
3680069f1a80SAndrew Thompson
368185e7bb81SAndrew Thompson txd = (struct rt2870_txd *)&data->desc;
368285e7bb81SAndrew Thompson txd->flags = RT2860_TX_QSEL_EDCA;
368385e7bb81SAndrew Thompson txwi = (struct rt2860_txwi *)(txd + 1);
368485e7bb81SAndrew Thompson txwi->wcid = 0xff;
368585e7bb81SAndrew Thompson txwi->flags = wflags;
368685e7bb81SAndrew Thompson txwi->xflags = xflags;
368785e7bb81SAndrew Thompson txwi->txop = 0; /* clear leftover garbage bits */
368885e7bb81SAndrew Thompson
3689069f1a80SAndrew Thompson data->m = mprot;
3690069f1a80SAndrew Thompson data->ni = ieee80211_ref_node(ni);
3691069f1a80SAndrew Thompson
3692f520d761SAdrian Chadd /* XXX TODO: methodize with MCS rates */
3693069f1a80SAndrew Thompson for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
3694069f1a80SAndrew Thompson if (rt2860_rates[ridx].rate == protrate)
3695069f1a80SAndrew Thompson break;
3696069f1a80SAndrew Thompson data->ridx = ridx;
3697069f1a80SAndrew Thompson
369885e7bb81SAndrew Thompson run_set_tx_desc(sc, data);
3699069f1a80SAndrew Thompson
3700109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending prot len=%u rate=%u\n",
3701069f1a80SAndrew Thompson m->m_pkthdr.len, rate);
3702069f1a80SAndrew Thompson
3703069f1a80SAndrew Thompson STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);
3704069f1a80SAndrew Thompson
3705069f1a80SAndrew Thompson usbd_transfer_start(sc->sc_xfer[0]);
3706069f1a80SAndrew Thompson
3707069f1a80SAndrew Thompson return (0);
3708069f1a80SAndrew Thompson }
3709069f1a80SAndrew Thompson
3710069f1a80SAndrew Thompson static int
run_tx_param(struct run_softc * sc,struct mbuf * m,struct ieee80211_node * ni,const struct ieee80211_bpf_params * params)3711069f1a80SAndrew Thompson run_tx_param(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
3712069f1a80SAndrew Thompson const struct ieee80211_bpf_params *params)
3713069f1a80SAndrew Thompson {
3714069f1a80SAndrew Thompson struct ieee80211com *ic = ni->ni_ic;
3715069f1a80SAndrew Thompson struct run_tx_data *data;
371685e7bb81SAndrew Thompson struct rt2870_txd *txd;
371785e7bb81SAndrew Thompson struct rt2860_txwi *txwi;
371885e7bb81SAndrew Thompson uint8_t ridx;
371985e7bb81SAndrew Thompson uint8_t rate;
372085e7bb81SAndrew Thompson uint8_t opflags = 0;
372185e7bb81SAndrew Thompson uint8_t xflags = 0;
3722069f1a80SAndrew Thompson int error;
3723069f1a80SAndrew Thompson
3724069f1a80SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
3725069f1a80SAndrew Thompson
3726069f1a80SAndrew Thompson KASSERT(params != NULL, ("no raw xmit params"));
3727069f1a80SAndrew Thompson
3728069f1a80SAndrew Thompson rate = params->ibp_rate0;
3729069f1a80SAndrew Thompson if (!ieee80211_isratevalid(ic->ic_rt, rate)) {
3730069f1a80SAndrew Thompson /* let caller free mbuf */
3731069f1a80SAndrew Thompson return (EINVAL);
3732069f1a80SAndrew Thompson }
3733069f1a80SAndrew Thompson
3734069f1a80SAndrew Thompson if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
373585e7bb81SAndrew Thompson xflags |= RT2860_TX_ACK;
3736069f1a80SAndrew Thompson if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
3737069f1a80SAndrew Thompson error = run_sendprot(sc, m, ni,
3738069f1a80SAndrew Thompson params->ibp_flags & IEEE80211_BPF_RTS ?
3739069f1a80SAndrew Thompson IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
3740069f1a80SAndrew Thompson rate);
3741069f1a80SAndrew Thompson if (error) {
3742069f1a80SAndrew Thompson /* let caller free mbuf */
374375c76159SAndrew Thompson return error;
3744069f1a80SAndrew Thompson }
3745069f1a80SAndrew Thompson opflags |= /*XXX RT2573_TX_LONG_RETRY |*/ RT2860_TX_TXOP_SIFS;
3746069f1a80SAndrew Thompson }
3747069f1a80SAndrew Thompson
3748069f1a80SAndrew Thompson if (sc->sc_epq[0].tx_nfree == 0) {
3749069f1a80SAndrew Thompson /* let caller free mbuf */
3750109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT,
3751109005ccSGavin Atkinson "sending raw frame, but tx ring is full\n");
3752069f1a80SAndrew Thompson return (EIO);
3753069f1a80SAndrew Thompson }
3754069f1a80SAndrew Thompson data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
3755069f1a80SAndrew Thompson STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
3756069f1a80SAndrew Thompson sc->sc_epq[0].tx_nfree--;
3757069f1a80SAndrew Thompson
375885e7bb81SAndrew Thompson txd = (struct rt2870_txd *)&data->desc;
375985e7bb81SAndrew Thompson txd->flags = RT2860_TX_QSEL_EDCA;
376085e7bb81SAndrew Thompson txwi = (struct rt2860_txwi *)(txd + 1);
376185e7bb81SAndrew Thompson txwi->wcid = 0xff;
376285e7bb81SAndrew Thompson txwi->xflags = xflags;
376385e7bb81SAndrew Thompson txwi->txop = opflags;
376485e7bb81SAndrew Thompson txwi->flags = 0; /* clear leftover garbage bits */
376585e7bb81SAndrew Thompson
3766069f1a80SAndrew Thompson data->m = m;
3767069f1a80SAndrew Thompson data->ni = ni;
3768f520d761SAdrian Chadd /* XXX TODO: methodize with MCS rates */
3769069f1a80SAndrew Thompson for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++)
3770069f1a80SAndrew Thompson if (rt2860_rates[ridx].rate == rate)
3771069f1a80SAndrew Thompson break;
3772069f1a80SAndrew Thompson data->ridx = ridx;
3773069f1a80SAndrew Thompson
377485e7bb81SAndrew Thompson run_set_tx_desc(sc, data);
3775069f1a80SAndrew Thompson
3776109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending raw frame len=%u rate=%u\n",
3777069f1a80SAndrew Thompson m->m_pkthdr.len, rate);
3778069f1a80SAndrew Thompson
3779069f1a80SAndrew Thompson STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);
3780069f1a80SAndrew Thompson
3781069f1a80SAndrew Thompson usbd_transfer_start(sc->sc_xfer[0]);
3782069f1a80SAndrew Thompson
3783069f1a80SAndrew Thompson return (0);
3784069f1a80SAndrew Thompson }
3785069f1a80SAndrew Thompson
3786069f1a80SAndrew Thompson static int
run_raw_xmit(struct ieee80211_node * ni,struct mbuf * m,const struct ieee80211_bpf_params * params)3787069f1a80SAndrew Thompson run_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
3788069f1a80SAndrew Thompson const struct ieee80211_bpf_params *params)
3789069f1a80SAndrew Thompson {
3790d3fdd08cSAdrian Chadd struct run_softc *sc = ni->ni_ic->ic_softc;
379185e7bb81SAndrew Thompson int error = 0;
3792069f1a80SAndrew Thompson
3793069f1a80SAndrew Thompson RUN_LOCK(sc);
3794069f1a80SAndrew Thompson
3795069f1a80SAndrew Thompson /* prevent management frames from being sent if we're not ready */
37967a79cebfSGleb Smirnoff if (!(sc->sc_flags & RUN_RUNNING)) {
3797069f1a80SAndrew Thompson error = ENETDOWN;
379885e7bb81SAndrew Thompson goto done;
3799069f1a80SAndrew Thompson }
3800069f1a80SAndrew Thompson
3801069f1a80SAndrew Thompson if (params == NULL) {
3802069f1a80SAndrew Thompson /* tx mgt packet */
3803069f1a80SAndrew Thompson if ((error = run_tx_mgt(sc, m, ni)) != 0) {
3804109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "mgt tx failed\n");
380585e7bb81SAndrew Thompson goto done;
3806069f1a80SAndrew Thompson }
3807069f1a80SAndrew Thompson } else {
3808069f1a80SAndrew Thompson /* tx raw packet with param */
3809069f1a80SAndrew Thompson if ((error = run_tx_param(sc, m, ni, params)) != 0) {
3810109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx with param failed\n");
381185e7bb81SAndrew Thompson goto done;
3812069f1a80SAndrew Thompson }
3813069f1a80SAndrew Thompson }
3814069f1a80SAndrew Thompson
381585e7bb81SAndrew Thompson done:
3816069f1a80SAndrew Thompson RUN_UNLOCK(sc);
3817069f1a80SAndrew Thompson
381885e7bb81SAndrew Thompson if (error != 0) {
3819069f1a80SAndrew Thompson if(m != NULL)
3820069f1a80SAndrew Thompson m_freem(m);
382185e7bb81SAndrew Thompson }
3822069f1a80SAndrew Thompson
3823069f1a80SAndrew Thompson return (error);
3824069f1a80SAndrew Thompson }
3825069f1a80SAndrew Thompson
38267a79cebfSGleb Smirnoff static int
run_transmit(struct ieee80211com * ic,struct mbuf * m)38277a79cebfSGleb Smirnoff run_transmit(struct ieee80211com *ic, struct mbuf *m)
382879d2c5e8SGleb Smirnoff {
38297a79cebfSGleb Smirnoff struct run_softc *sc = ic->ic_softc;
38307a79cebfSGleb Smirnoff int error;
38317a79cebfSGleb Smirnoff
38327a79cebfSGleb Smirnoff RUN_LOCK(sc);
38337a79cebfSGleb Smirnoff if ((sc->sc_flags & RUN_RUNNING) == 0) {
38347a79cebfSGleb Smirnoff RUN_UNLOCK(sc);
38357a79cebfSGleb Smirnoff return (ENXIO);
38367a79cebfSGleb Smirnoff }
38377a79cebfSGleb Smirnoff error = mbufq_enqueue(&sc->sc_snd, m);
38387a79cebfSGleb Smirnoff if (error) {
38397a79cebfSGleb Smirnoff RUN_UNLOCK(sc);
38407a79cebfSGleb Smirnoff return (error);
38417a79cebfSGleb Smirnoff }
38427a79cebfSGleb Smirnoff run_start(sc);
38437a79cebfSGleb Smirnoff RUN_UNLOCK(sc);
38447a79cebfSGleb Smirnoff
38457a79cebfSGleb Smirnoff return (0);
38467a79cebfSGleb Smirnoff }
38477a79cebfSGleb Smirnoff
38487a79cebfSGleb Smirnoff static void
run_start(struct run_softc * sc)38497a79cebfSGleb Smirnoff run_start(struct run_softc *sc)
38507a79cebfSGleb Smirnoff {
3851069f1a80SAndrew Thompson struct ieee80211_node *ni;
3852069f1a80SAndrew Thompson struct mbuf *m;
3853069f1a80SAndrew Thompson
38547a79cebfSGleb Smirnoff RUN_LOCK_ASSERT(sc, MA_OWNED);
3855ba2c1fbcSAdrian Chadd
38567a79cebfSGleb Smirnoff if ((sc->sc_flags & RUN_RUNNING) == 0)
38577a79cebfSGleb Smirnoff return;
38587a79cebfSGleb Smirnoff
38597a79cebfSGleb Smirnoff while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
38607a79cebfSGleb Smirnoff ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
38617a79cebfSGleb Smirnoff if (run_tx(sc, m, ni) != 0) {
38627a79cebfSGleb Smirnoff mbufq_prepend(&sc->sc_snd, m);
38637a79cebfSGleb Smirnoff break;
38647a79cebfSGleb Smirnoff }
38657a79cebfSGleb Smirnoff }
38667a79cebfSGleb Smirnoff }
38677a79cebfSGleb Smirnoff
38687a79cebfSGleb Smirnoff static void
run_parent(struct ieee80211com * ic)38697a79cebfSGleb Smirnoff run_parent(struct ieee80211com *ic)
38707a79cebfSGleb Smirnoff {
38717a79cebfSGleb Smirnoff struct run_softc *sc = ic->ic_softc;
38727a79cebfSGleb Smirnoff int startall = 0;
38737a79cebfSGleb Smirnoff
38747a79cebfSGleb Smirnoff RUN_LOCK(sc);
38757a79cebfSGleb Smirnoff if (sc->sc_detached) {
3876069f1a80SAndrew Thompson RUN_UNLOCK(sc);
3877069f1a80SAndrew Thompson return;
3878069f1a80SAndrew Thompson }
3879069f1a80SAndrew Thompson
38807a79cebfSGleb Smirnoff if (ic->ic_nrunning > 0) {
38817a79cebfSGleb Smirnoff if (!(sc->sc_flags & RUN_RUNNING)) {
3882069f1a80SAndrew Thompson startall = 1;
388385e7bb81SAndrew Thompson run_init_locked(sc);
3884069f1a80SAndrew Thompson } else
3885272f6adeSGleb Smirnoff run_update_promisc_locked(sc);
38867a79cebfSGleb Smirnoff } else if ((sc->sc_flags & RUN_RUNNING) && sc->rvp_cnt <= 1)
3887069f1a80SAndrew Thompson run_stop(sc);
3888069f1a80SAndrew Thompson RUN_UNLOCK(sc);
3889069f1a80SAndrew Thompson if (startall)
3890069f1a80SAndrew Thompson ieee80211_start_all(ic);
3891069f1a80SAndrew Thompson }
3892069f1a80SAndrew Thompson
3893069f1a80SAndrew Thompson static void
run_iq_calib(struct run_softc * sc,u_int chan)3894010b13faSKevin Lo run_iq_calib(struct run_softc *sc, u_int chan)
3895010b13faSKevin Lo {
3896010b13faSKevin Lo uint16_t val;
3897010b13faSKevin Lo
3898010b13faSKevin Lo /* Tx0 IQ gain. */
3899010b13faSKevin Lo run_bbp_write(sc, 158, 0x2c);
3900010b13faSKevin Lo if (chan <= 14)
3901010b13faSKevin Lo run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_2GHZ, &val, 1);
3902010b13faSKevin Lo else if (chan <= 64) {
3903010b13faSKevin Lo run_efuse_read(sc,
3904010b13faSKevin Lo RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5GHZ,
3905010b13faSKevin Lo &val, 1);
3906010b13faSKevin Lo } else if (chan <= 138) {
3907010b13faSKevin Lo run_efuse_read(sc,
3908010b13faSKevin Lo RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5GHZ,
3909010b13faSKevin Lo &val, 1);
3910010b13faSKevin Lo } else if (chan <= 165) {
3911010b13faSKevin Lo run_efuse_read(sc,
3912010b13faSKevin Lo RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5GHZ,
3913010b13faSKevin Lo &val, 1);
3914010b13faSKevin Lo } else
3915010b13faSKevin Lo val = 0;
39169df3ee07SKevin Lo run_bbp_write(sc, 159, val);
3917010b13faSKevin Lo
3918010b13faSKevin Lo /* Tx0 IQ phase. */
3919010b13faSKevin Lo run_bbp_write(sc, 158, 0x2d);
3920010b13faSKevin Lo if (chan <= 14) {
3921010b13faSKevin Lo run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_2GHZ,
3922010b13faSKevin Lo &val, 1);
3923010b13faSKevin Lo } else if (chan <= 64) {
3924010b13faSKevin Lo run_efuse_read(sc,
3925010b13faSKevin Lo RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5GHZ,
3926010b13faSKevin Lo &val, 1);
3927010b13faSKevin Lo } else if (chan <= 138) {
3928010b13faSKevin Lo run_efuse_read(sc,
3929010b13faSKevin Lo RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5GHZ,
3930010b13faSKevin Lo &val, 1);
3931010b13faSKevin Lo } else if (chan <= 165) {
3932010b13faSKevin Lo run_efuse_read(sc,
3933010b13faSKevin Lo RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5GHZ,
3934010b13faSKevin Lo &val, 1);
3935010b13faSKevin Lo } else
3936010b13faSKevin Lo val = 0;
39379df3ee07SKevin Lo run_bbp_write(sc, 159, val);
3938010b13faSKevin Lo
3939010b13faSKevin Lo /* Tx1 IQ gain. */
3940010b13faSKevin Lo run_bbp_write(sc, 158, 0x4a);
3941010b13faSKevin Lo if (chan <= 14) {
3942010b13faSKevin Lo run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_2GHZ,
3943010b13faSKevin Lo &val, 1);
3944010b13faSKevin Lo } else if (chan <= 64) {
3945010b13faSKevin Lo run_efuse_read(sc,
3946010b13faSKevin Lo RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5GHZ,
3947010b13faSKevin Lo &val, 1);
3948010b13faSKevin Lo } else if (chan <= 138) {
3949010b13faSKevin Lo run_efuse_read(sc,
3950010b13faSKevin Lo RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5GHZ,
3951010b13faSKevin Lo &val, 1);
3952010b13faSKevin Lo } else if (chan <= 165) {
3953010b13faSKevin Lo run_efuse_read(sc,
3954010b13faSKevin Lo RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5GHZ,
3955010b13faSKevin Lo &val, 1);
3956010b13faSKevin Lo } else
3957010b13faSKevin Lo val = 0;
39589df3ee07SKevin Lo run_bbp_write(sc, 159, val);
3959010b13faSKevin Lo
3960010b13faSKevin Lo /* Tx1 IQ phase. */
3961010b13faSKevin Lo run_bbp_write(sc, 158, 0x4b);
3962010b13faSKevin Lo if (chan <= 14) {
3963010b13faSKevin Lo run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_2GHZ,
3964010b13faSKevin Lo &val, 1);
3965010b13faSKevin Lo } else if (chan <= 64) {
3966010b13faSKevin Lo run_efuse_read(sc,
3967010b13faSKevin Lo RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5GHZ,
3968010b13faSKevin Lo &val, 1);
3969010b13faSKevin Lo } else if (chan <= 138) {
3970010b13faSKevin Lo run_efuse_read(sc,
3971010b13faSKevin Lo RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5GHZ,
3972010b13faSKevin Lo &val, 1);
3973010b13faSKevin Lo } else if (chan <= 165) {
3974010b13faSKevin Lo run_efuse_read(sc,
3975010b13faSKevin Lo RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5GHZ,
3976010b13faSKevin Lo &val, 1);
3977010b13faSKevin Lo } else
3978010b13faSKevin Lo val = 0;
39799df3ee07SKevin Lo run_bbp_write(sc, 159, val);
3980010b13faSKevin Lo
3981010b13faSKevin Lo /* RF IQ compensation control. */
3982010b13faSKevin Lo run_bbp_write(sc, 158, 0x04);
3983010b13faSKevin Lo run_efuse_read(sc, RT5390_EEPROM_RF_IQ_COMPENSATION_CTL,
3984010b13faSKevin Lo &val, 1);
39859df3ee07SKevin Lo run_bbp_write(sc, 159, val);
3986010b13faSKevin Lo
3987010b13faSKevin Lo /* RF IQ imbalance compensation control. */
3988010b13faSKevin Lo run_bbp_write(sc, 158, 0x03);
3989010b13faSKevin Lo run_efuse_read(sc,
3990010b13faSKevin Lo RT5390_EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CTL, &val, 1);
39919df3ee07SKevin Lo run_bbp_write(sc, 159, val);
3992010b13faSKevin Lo }
3993010b13faSKevin Lo
3994010b13faSKevin Lo static void
run_set_agc(struct run_softc * sc,uint8_t agc)39953707a5e9SAndrew Thompson run_set_agc(struct run_softc *sc, uint8_t agc)
39963707a5e9SAndrew Thompson {
39973707a5e9SAndrew Thompson uint8_t bbp;
39983707a5e9SAndrew Thompson
39993707a5e9SAndrew Thompson if (sc->mac_ver == 0x3572) {
40003707a5e9SAndrew Thompson run_bbp_read(sc, 27, &bbp);
40013707a5e9SAndrew Thompson bbp &= ~(0x3 << 5);
40023707a5e9SAndrew Thompson run_bbp_write(sc, 27, bbp | 0 << 5); /* select Rx0 */
40033707a5e9SAndrew Thompson run_bbp_write(sc, 66, agc);
40043707a5e9SAndrew Thompson run_bbp_write(sc, 27, bbp | 1 << 5); /* select Rx1 */
40053707a5e9SAndrew Thompson run_bbp_write(sc, 66, agc);
40063707a5e9SAndrew Thompson } else
40073707a5e9SAndrew Thompson run_bbp_write(sc, 66, agc);
40083707a5e9SAndrew Thompson }
40093707a5e9SAndrew Thompson
40103707a5e9SAndrew Thompson static void
run_select_chan_group(struct run_softc * sc,int group)4011069f1a80SAndrew Thompson run_select_chan_group(struct run_softc *sc, int group)
4012069f1a80SAndrew Thompson {
4013069f1a80SAndrew Thompson uint32_t tmp;
40143707a5e9SAndrew Thompson uint8_t agc;
4015069f1a80SAndrew Thompson
4016069f1a80SAndrew Thompson run_bbp_write(sc, 62, 0x37 - sc->lna[group]);
4017069f1a80SAndrew Thompson run_bbp_write(sc, 63, 0x37 - sc->lna[group]);
4018069f1a80SAndrew Thompson run_bbp_write(sc, 64, 0x37 - sc->lna[group]);
401928dfd841SKevin Lo if (sc->mac_ver < 0x3572)
4020069f1a80SAndrew Thompson run_bbp_write(sc, 86, 0x00);
4021069f1a80SAndrew Thompson
40227a7e01caSKevin Lo if (sc->mac_ver == 0x3593) {
40237a7e01caSKevin Lo run_bbp_write(sc, 77, 0x98);
40247a7e01caSKevin Lo run_bbp_write(sc, 83, (group == 0) ? 0x8a : 0x9a);
40257a7e01caSKevin Lo }
40267a7e01caSKevin Lo
4027069f1a80SAndrew Thompson if (group == 0) {
4028069f1a80SAndrew Thompson if (sc->ext_2ghz_lna) {
402964891211SKevin Lo if (sc->mac_ver >= 0x5390)
403064891211SKevin Lo run_bbp_write(sc, 75, 0x52);
403164891211SKevin Lo else {
4032069f1a80SAndrew Thompson run_bbp_write(sc, 82, 0x62);
4033069f1a80SAndrew Thompson run_bbp_write(sc, 75, 0x46);
403464891211SKevin Lo }
4035069f1a80SAndrew Thompson } else {
4036242dbae3SKevin Lo if (sc->mac_ver == 0x5592) {
4037242dbae3SKevin Lo run_bbp_write(sc, 79, 0x1c);
4038242dbae3SKevin Lo run_bbp_write(sc, 80, 0x0e);
4039242dbae3SKevin Lo run_bbp_write(sc, 81, 0x3a);
4040242dbae3SKevin Lo run_bbp_write(sc, 82, 0x62);
4041242dbae3SKevin Lo
4042242dbae3SKevin Lo run_bbp_write(sc, 195, 0x80);
4043242dbae3SKevin Lo run_bbp_write(sc, 196, 0xe0);
4044242dbae3SKevin Lo run_bbp_write(sc, 195, 0x81);
4045242dbae3SKevin Lo run_bbp_write(sc, 196, 0x1f);
4046242dbae3SKevin Lo run_bbp_write(sc, 195, 0x82);
4047242dbae3SKevin Lo run_bbp_write(sc, 196, 0x38);
4048242dbae3SKevin Lo run_bbp_write(sc, 195, 0x83);
4049242dbae3SKevin Lo run_bbp_write(sc, 196, 0x32);
4050242dbae3SKevin Lo run_bbp_write(sc, 195, 0x85);
4051242dbae3SKevin Lo run_bbp_write(sc, 196, 0x28);
4052242dbae3SKevin Lo run_bbp_write(sc, 195, 0x86);
4053242dbae3SKevin Lo run_bbp_write(sc, 196, 0x19);
4054242dbae3SKevin Lo } else if (sc->mac_ver >= 0x5390)
405564891211SKevin Lo run_bbp_write(sc, 75, 0x50);
405664891211SKevin Lo else {
40577a7e01caSKevin Lo run_bbp_write(sc, 82,
40587a7e01caSKevin Lo (sc->mac_ver == 0x3593) ? 0x62 : 0x84);
4059069f1a80SAndrew Thompson run_bbp_write(sc, 75, 0x50);
4060069f1a80SAndrew Thompson }
406164891211SKevin Lo }
4062069f1a80SAndrew Thompson } else {
4063242dbae3SKevin Lo if (sc->mac_ver == 0x5592) {
4064242dbae3SKevin Lo run_bbp_write(sc, 79, 0x18);
4065242dbae3SKevin Lo run_bbp_write(sc, 80, 0x08);
4066242dbae3SKevin Lo run_bbp_write(sc, 81, 0x38);
4067242dbae3SKevin Lo run_bbp_write(sc, 82, 0x92);
4068242dbae3SKevin Lo
4069242dbae3SKevin Lo run_bbp_write(sc, 195, 0x80);
4070242dbae3SKevin Lo run_bbp_write(sc, 196, 0xf0);
4071242dbae3SKevin Lo run_bbp_write(sc, 195, 0x81);
4072242dbae3SKevin Lo run_bbp_write(sc, 196, 0x1e);
4073242dbae3SKevin Lo run_bbp_write(sc, 195, 0x82);
4074242dbae3SKevin Lo run_bbp_write(sc, 196, 0x28);
4075242dbae3SKevin Lo run_bbp_write(sc, 195, 0x83);
4076242dbae3SKevin Lo run_bbp_write(sc, 196, 0x20);
4077242dbae3SKevin Lo run_bbp_write(sc, 195, 0x85);
4078242dbae3SKevin Lo run_bbp_write(sc, 196, 0x7f);
4079242dbae3SKevin Lo run_bbp_write(sc, 195, 0x86);
4080242dbae3SKevin Lo run_bbp_write(sc, 196, 0x7f);
4081242dbae3SKevin Lo } else if (sc->mac_ver == 0x3572)
40823707a5e9SAndrew Thompson run_bbp_write(sc, 82, 0x94);
40833707a5e9SAndrew Thompson else
40847a7e01caSKevin Lo run_bbp_write(sc, 82,
40857a7e01caSKevin Lo (sc->mac_ver == 0x3593) ? 0x82 : 0xf2);
40863707a5e9SAndrew Thompson if (sc->ext_5ghz_lna)
4087069f1a80SAndrew Thompson run_bbp_write(sc, 75, 0x46);
40883707a5e9SAndrew Thompson else
4089069f1a80SAndrew Thompson run_bbp_write(sc, 75, 0x50);
4090069f1a80SAndrew Thompson }
4091069f1a80SAndrew Thompson
4092069f1a80SAndrew Thompson run_read(sc, RT2860_TX_BAND_CFG, &tmp);
4093069f1a80SAndrew Thompson tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P);
4094069f1a80SAndrew Thompson tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P;
4095069f1a80SAndrew Thompson run_write(sc, RT2860_TX_BAND_CFG, tmp);
4096069f1a80SAndrew Thompson
4097069f1a80SAndrew Thompson /* enable appropriate Power Amplifiers and Low Noise Amplifiers */
409885e7bb81SAndrew Thompson tmp = RT2860_RFTR_EN | RT2860_TRSW_EN | RT2860_LNA_PE0_EN;
40997a7e01caSKevin Lo if (sc->mac_ver == 0x3593)
41007a7e01caSKevin Lo tmp |= 1 << 29 | 1 << 28;
410185e7bb81SAndrew Thompson if (sc->nrxchains > 1)
410285e7bb81SAndrew Thompson tmp |= RT2860_LNA_PE1_EN;
4103069f1a80SAndrew Thompson if (group == 0) { /* 2GHz */
410485e7bb81SAndrew Thompson tmp |= RT2860_PA_PE_G0_EN;
4105069f1a80SAndrew Thompson if (sc->ntxchains > 1)
4106069f1a80SAndrew Thompson tmp |= RT2860_PA_PE_G1_EN;
41077a7e01caSKevin Lo if (sc->mac_ver == 0x3593) {
41087a7e01caSKevin Lo if (sc->ntxchains > 2)
41097a7e01caSKevin Lo tmp |= 1 << 25;
41107a7e01caSKevin Lo }
4111069f1a80SAndrew Thompson } else { /* 5GHz */
411285e7bb81SAndrew Thompson tmp |= RT2860_PA_PE_A0_EN;
4113069f1a80SAndrew Thompson if (sc->ntxchains > 1)
4114069f1a80SAndrew Thompson tmp |= RT2860_PA_PE_A1_EN;
4115069f1a80SAndrew Thompson }
41163707a5e9SAndrew Thompson if (sc->mac_ver == 0x3572) {
41173707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 8, 0x00);
41183707a5e9SAndrew Thompson run_write(sc, RT2860_TX_PIN_CFG, tmp);
41193707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 8, 0x80);
41203707a5e9SAndrew Thompson } else
4121069f1a80SAndrew Thompson run_write(sc, RT2860_TX_PIN_CFG, tmp);
4122069f1a80SAndrew Thompson
4123242dbae3SKevin Lo if (sc->mac_ver == 0x5592) {
4124242dbae3SKevin Lo run_bbp_write(sc, 195, 0x8d);
4125242dbae3SKevin Lo run_bbp_write(sc, 196, 0x1a);
4126242dbae3SKevin Lo }
4127242dbae3SKevin Lo
41287a7e01caSKevin Lo if (sc->mac_ver == 0x3593) {
41297a7e01caSKevin Lo run_read(sc, RT2860_GPIO_CTRL, &tmp);
41307a7e01caSKevin Lo tmp &= ~0x01010000;
41317a7e01caSKevin Lo if (group == 0)
41327a7e01caSKevin Lo tmp |= 0x00010000;
41337a7e01caSKevin Lo tmp = (tmp & ~0x00009090) | 0x00000090;
41347a7e01caSKevin Lo run_write(sc, RT2860_GPIO_CTRL, tmp);
41357a7e01caSKevin Lo }
41367a7e01caSKevin Lo
4137069f1a80SAndrew Thompson /* set initial AGC value */
41383707a5e9SAndrew Thompson if (group == 0) { /* 2GHz band */
41393707a5e9SAndrew Thompson if (sc->mac_ver >= 0x3070)
41403707a5e9SAndrew Thompson agc = 0x1c + sc->lna[0] * 2;
4141069f1a80SAndrew Thompson else
41423707a5e9SAndrew Thompson agc = 0x2e + sc->lna[0];
41433707a5e9SAndrew Thompson } else { /* 5GHz band */
4144242dbae3SKevin Lo if (sc->mac_ver == 0x5592)
4145242dbae3SKevin Lo agc = 0x24 + sc->lna[group] * 2;
41467a7e01caSKevin Lo else if (sc->mac_ver == 0x3572 || sc->mac_ver == 0x3593)
41473707a5e9SAndrew Thompson agc = 0x22 + (sc->lna[group] * 5) / 3;
41483707a5e9SAndrew Thompson else
41493707a5e9SAndrew Thompson agc = 0x32 + (sc->lna[group] * 5) / 3;
41503707a5e9SAndrew Thompson }
41513707a5e9SAndrew Thompson run_set_agc(sc, agc);
4152069f1a80SAndrew Thompson }
4153069f1a80SAndrew Thompson
4154069f1a80SAndrew Thompson static void
run_rt2870_set_chan(struct run_softc * sc,u_int chan)415535a24898SHans Petter Selasky run_rt2870_set_chan(struct run_softc *sc, u_int chan)
4156069f1a80SAndrew Thompson {
4157069f1a80SAndrew Thompson const struct rfprog *rfprog = rt2860_rf2850;
4158069f1a80SAndrew Thompson uint32_t r2, r3, r4;
4159069f1a80SAndrew Thompson int8_t txpow1, txpow2;
4160069f1a80SAndrew Thompson int i;
4161069f1a80SAndrew Thompson
4162069f1a80SAndrew Thompson /* find the settings for this channel (we know it exists) */
4163069f1a80SAndrew Thompson for (i = 0; rfprog[i].chan != chan; i++);
4164069f1a80SAndrew Thompson
4165069f1a80SAndrew Thompson r2 = rfprog[i].r2;
4166069f1a80SAndrew Thompson if (sc->ntxchains == 1)
41675f7e329cSKevin Lo r2 |= 1 << 14; /* 1T: disable Tx chain 2 */
4168069f1a80SAndrew Thompson if (sc->nrxchains == 1)
41695f7e329cSKevin Lo r2 |= 1 << 17 | 1 << 6; /* 1R: disable Rx chains 2 & 3 */
4170069f1a80SAndrew Thompson else if (sc->nrxchains == 2)
41715f7e329cSKevin Lo r2 |= 1 << 6; /* 2R: disable Rx chain 3 */
4172069f1a80SAndrew Thompson
4173069f1a80SAndrew Thompson /* use Tx power values from EEPROM */
4174069f1a80SAndrew Thompson txpow1 = sc->txpow1[i];
4175069f1a80SAndrew Thompson txpow2 = sc->txpow2[i];
41765f7e329cSKevin Lo
41775f7e329cSKevin Lo /* Initialize RF R3 and R4. */
41785f7e329cSKevin Lo r3 = rfprog[i].r3 & 0xffffc1ff;
41795f7e329cSKevin Lo r4 = (rfprog[i].r4 & ~(0x001f87c0)) | (sc->freq << 15);
4180069f1a80SAndrew Thompson if (chan > 14) {
41815f7e329cSKevin Lo if (txpow1 >= 0) {
41825f7e329cSKevin Lo txpow1 = (txpow1 > 0xf) ? (0xf) : (txpow1);
41835f7e329cSKevin Lo r3 |= (txpow1 << 10) | (1 << 9);
41845f7e329cSKevin Lo } else {
41855f7e329cSKevin Lo txpow1 += 7;
41865f7e329cSKevin Lo
41875f7e329cSKevin Lo /* txpow1 is not possible larger than 15. */
41885f7e329cSKevin Lo r3 |= (txpow1 << 10);
4189069f1a80SAndrew Thompson }
41905f7e329cSKevin Lo if (txpow2 >= 0) {
41915f7e329cSKevin Lo txpow2 = (txpow2 > 0xf) ? (0xf) : (txpow2);
41920aff597aSHans Petter Selasky r4 |= (txpow2 << 7) | (1 << 6);
41935f7e329cSKevin Lo } else {
41945f7e329cSKevin Lo txpow2 += 7;
41955f7e329cSKevin Lo r4 |= (txpow2 << 7);
41965f7e329cSKevin Lo }
41975f7e329cSKevin Lo } else {
41985f7e329cSKevin Lo /* Set Tx0 power. */
41995f7e329cSKevin Lo r3 |= (txpow1 << 9);
42005f7e329cSKevin Lo
42015f7e329cSKevin Lo /* Set frequency offset and Tx1 power. */
42025f7e329cSKevin Lo r4 |= (txpow2 << 6);
42035f7e329cSKevin Lo }
4204069f1a80SAndrew Thompson
42055b751464SKevin Lo run_rt2870_rf_write(sc, rfprog[i].r1);
42065b751464SKevin Lo run_rt2870_rf_write(sc, r2);
42075b751464SKevin Lo run_rt2870_rf_write(sc, r3 & ~(1 << 2));
42085b751464SKevin Lo run_rt2870_rf_write(sc, r4);
4209069f1a80SAndrew Thompson
4210069f1a80SAndrew Thompson run_delay(sc, 10);
4211069f1a80SAndrew Thompson
42125b751464SKevin Lo run_rt2870_rf_write(sc, rfprog[i].r1);
42135b751464SKevin Lo run_rt2870_rf_write(sc, r2);
42145b751464SKevin Lo run_rt2870_rf_write(sc, r3 | (1 << 2));
42155b751464SKevin Lo run_rt2870_rf_write(sc, r4);
4216069f1a80SAndrew Thompson
4217069f1a80SAndrew Thompson run_delay(sc, 10);
4218069f1a80SAndrew Thompson
42195b751464SKevin Lo run_rt2870_rf_write(sc, rfprog[i].r1);
42205b751464SKevin Lo run_rt2870_rf_write(sc, r2);
42215b751464SKevin Lo run_rt2870_rf_write(sc, r3 & ~(1 << 2));
42225b751464SKevin Lo run_rt2870_rf_write(sc, r4);
4223069f1a80SAndrew Thompson }
4224069f1a80SAndrew Thompson
4225069f1a80SAndrew Thompson static void
run_rt3070_set_chan(struct run_softc * sc,u_int chan)422635a24898SHans Petter Selasky run_rt3070_set_chan(struct run_softc *sc, u_int chan)
4227069f1a80SAndrew Thompson {
4228069f1a80SAndrew Thompson int8_t txpow1, txpow2;
4229069f1a80SAndrew Thompson uint8_t rf;
42303707a5e9SAndrew Thompson int i;
4231069f1a80SAndrew Thompson
42323707a5e9SAndrew Thompson /* find the settings for this channel (we know it exists) */
42333707a5e9SAndrew Thompson for (i = 0; rt2860_rf2850[i].chan != chan; i++);
4234069f1a80SAndrew Thompson
42353707a5e9SAndrew Thompson /* use Tx power values from EEPROM */
42363707a5e9SAndrew Thompson txpow1 = sc->txpow1[i];
42373707a5e9SAndrew Thompson txpow2 = sc->txpow2[i];
42383707a5e9SAndrew Thompson
42393707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n);
42405d534170SKevin Lo
42415d534170SKevin Lo /* RT3370/RT3390: RF R3 [7:4] is not reserved bits. */
42425d534170SKevin Lo run_rt3070_rf_read(sc, 3, &rf);
42435d534170SKevin Lo rf = (rf & ~0x0f) | rt3070_freqs[i].k;
42445d534170SKevin Lo run_rt3070_rf_write(sc, 3, rf);
42455d534170SKevin Lo
4246069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 6, &rf);
42473707a5e9SAndrew Thompson rf = (rf & ~0x03) | rt3070_freqs[i].r;
4248069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 6, rf);
4249069f1a80SAndrew Thompson
4250069f1a80SAndrew Thompson /* set Tx0 power */
4251069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 12, &rf);
4252069f1a80SAndrew Thompson rf = (rf & ~0x1f) | txpow1;
4253069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 12, rf);
4254069f1a80SAndrew Thompson
4255069f1a80SAndrew Thompson /* set Tx1 power */
4256069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 13, &rf);
4257069f1a80SAndrew Thompson rf = (rf & ~0x1f) | txpow2;
4258069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 13, rf);
4259069f1a80SAndrew Thompson
4260069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 1, &rf);
4261069f1a80SAndrew Thompson rf &= ~0xfc;
4262069f1a80SAndrew Thompson if (sc->ntxchains == 1)
4263069f1a80SAndrew Thompson rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */
4264069f1a80SAndrew Thompson else if (sc->ntxchains == 2)
4265069f1a80SAndrew Thompson rf |= 1 << 7; /* 2T: disable Tx chain 3 */
4266069f1a80SAndrew Thompson if (sc->nrxchains == 1)
4267069f1a80SAndrew Thompson rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */
4268069f1a80SAndrew Thompson else if (sc->nrxchains == 2)
4269069f1a80SAndrew Thompson rf |= 1 << 6; /* 2R: disable Rx chain 3 */
4270069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 1, rf);
4271069f1a80SAndrew Thompson
4272069f1a80SAndrew Thompson /* set RF offset */
4273069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 23, &rf);
4274069f1a80SAndrew Thompson rf = (rf & ~0x7f) | sc->freq;
4275069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 23, rf);
4276069f1a80SAndrew Thompson
4277069f1a80SAndrew Thompson /* program RF filter */
42783707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 24, &rf); /* Tx */
42793707a5e9SAndrew Thompson rf = (rf & ~0x3f) | sc->rf24_20mhz;
42803707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 24, rf);
42813707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 31, &rf); /* Rx */
42823707a5e9SAndrew Thompson rf = (rf & ~0x3f) | sc->rf24_20mhz;
42833707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 31, rf);
4284069f1a80SAndrew Thompson
4285069f1a80SAndrew Thompson /* enable RF tuning */
4286069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 7, &rf);
4287069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 7, rf | 0x01);
4288069f1a80SAndrew Thompson }
4289069f1a80SAndrew Thompson
4290069f1a80SAndrew Thompson static void
run_rt3572_set_chan(struct run_softc * sc,u_int chan)42913707a5e9SAndrew Thompson run_rt3572_set_chan(struct run_softc *sc, u_int chan)
42923707a5e9SAndrew Thompson {
42933707a5e9SAndrew Thompson int8_t txpow1, txpow2;
42943707a5e9SAndrew Thompson uint32_t tmp;
42953707a5e9SAndrew Thompson uint8_t rf;
42963707a5e9SAndrew Thompson int i;
42973707a5e9SAndrew Thompson
42983707a5e9SAndrew Thompson /* find the settings for this channel (we know it exists) */
42993707a5e9SAndrew Thompson for (i = 0; rt2860_rf2850[i].chan != chan; i++);
43003707a5e9SAndrew Thompson
43013707a5e9SAndrew Thompson /* use Tx power values from EEPROM */
43023707a5e9SAndrew Thompson txpow1 = sc->txpow1[i];
43033707a5e9SAndrew Thompson txpow2 = sc->txpow2[i];
43043707a5e9SAndrew Thompson
43053707a5e9SAndrew Thompson if (chan <= 14) {
43063707a5e9SAndrew Thompson run_bbp_write(sc, 25, sc->bbp25);
43073707a5e9SAndrew Thompson run_bbp_write(sc, 26, sc->bbp26);
43083707a5e9SAndrew Thompson } else {
43093707a5e9SAndrew Thompson /* enable IQ phase correction */
43103707a5e9SAndrew Thompson run_bbp_write(sc, 25, 0x09);
43113707a5e9SAndrew Thompson run_bbp_write(sc, 26, 0xff);
43123707a5e9SAndrew Thompson }
43133707a5e9SAndrew Thompson
43143707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n);
43153707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 3, rt3070_freqs[i].k);
43163707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 6, &rf);
43173707a5e9SAndrew Thompson rf = (rf & ~0x0f) | rt3070_freqs[i].r;
43183707a5e9SAndrew Thompson rf |= (chan <= 14) ? 0x08 : 0x04;
43193707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 6, rf);
43203707a5e9SAndrew Thompson
43213707a5e9SAndrew Thompson /* set PLL mode */
43223707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 5, &rf);
43233707a5e9SAndrew Thompson rf &= ~(0x08 | 0x04);
43243707a5e9SAndrew Thompson rf |= (chan <= 14) ? 0x04 : 0x08;
43253707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 5, rf);
43263707a5e9SAndrew Thompson
43273707a5e9SAndrew Thompson /* set Tx power for chain 0 */
43283707a5e9SAndrew Thompson if (chan <= 14)
43293707a5e9SAndrew Thompson rf = 0x60 | txpow1;
43303707a5e9SAndrew Thompson else
43313707a5e9SAndrew Thompson rf = 0xe0 | (txpow1 & 0xc) << 1 | (txpow1 & 0x3);
43323707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 12, rf);
43333707a5e9SAndrew Thompson
43343707a5e9SAndrew Thompson /* set Tx power for chain 1 */
43353707a5e9SAndrew Thompson if (chan <= 14)
43363707a5e9SAndrew Thompson rf = 0x60 | txpow2;
43373707a5e9SAndrew Thompson else
43383707a5e9SAndrew Thompson rf = 0xe0 | (txpow2 & 0xc) << 1 | (txpow2 & 0x3);
43393707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 13, rf);
43403707a5e9SAndrew Thompson
43413707a5e9SAndrew Thompson /* set Tx/Rx streams */
43423707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 1, &rf);
43433707a5e9SAndrew Thompson rf &= ~0xfc;
43443707a5e9SAndrew Thompson if (sc->ntxchains == 1)
43453707a5e9SAndrew Thompson rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */
43463707a5e9SAndrew Thompson else if (sc->ntxchains == 2)
43473707a5e9SAndrew Thompson rf |= 1 << 7; /* 2T: disable Tx chain 3 */
43483707a5e9SAndrew Thompson if (sc->nrxchains == 1)
43493707a5e9SAndrew Thompson rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */
43503707a5e9SAndrew Thompson else if (sc->nrxchains == 2)
43513707a5e9SAndrew Thompson rf |= 1 << 6; /* 2R: disable Rx chain 3 */
43523707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 1, rf);
43533707a5e9SAndrew Thompson
43543707a5e9SAndrew Thompson /* set RF offset */
43553707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 23, &rf);
43563707a5e9SAndrew Thompson rf = (rf & ~0x7f) | sc->freq;
43573707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 23, rf);
43583707a5e9SAndrew Thompson
43593707a5e9SAndrew Thompson /* program RF filter */
43603707a5e9SAndrew Thompson rf = sc->rf24_20mhz;
43613707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 24, rf); /* Tx */
43623707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 31, rf); /* Rx */
43633707a5e9SAndrew Thompson
43643707a5e9SAndrew Thompson /* enable RF tuning */
43653707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 7, &rf);
43663707a5e9SAndrew Thompson rf = (chan <= 14) ? 0xd8 : ((rf & ~0xc8) | 0x14);
43673707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 7, rf);
43683707a5e9SAndrew Thompson
43693707a5e9SAndrew Thompson /* TSSI */
43703707a5e9SAndrew Thompson rf = (chan <= 14) ? 0xc3 : 0xc0;
43713707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 9, rf);
43723707a5e9SAndrew Thompson
43733707a5e9SAndrew Thompson /* set loop filter 1 */
43743707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 10, 0xf1);
43753707a5e9SAndrew Thompson /* set loop filter 2 */
43763707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 11, (chan <= 14) ? 0xb9 : 0x00);
43773707a5e9SAndrew Thompson
43783707a5e9SAndrew Thompson /* set tx_mx2_ic */
43793707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 15, (chan <= 14) ? 0x53 : 0x43);
43803707a5e9SAndrew Thompson /* set tx_mx1_ic */
43813707a5e9SAndrew Thompson if (chan <= 14)
43823707a5e9SAndrew Thompson rf = 0x48 | sc->txmixgain_2ghz;
43833707a5e9SAndrew Thompson else
43843707a5e9SAndrew Thompson rf = 0x78 | sc->txmixgain_5ghz;
43853707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 16, rf);
43863707a5e9SAndrew Thompson
43873707a5e9SAndrew Thompson /* set tx_lo1 */
43883707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 17, 0x23);
43893707a5e9SAndrew Thompson /* set tx_lo2 */
43903707a5e9SAndrew Thompson if (chan <= 14)
43913707a5e9SAndrew Thompson rf = 0x93;
43923707a5e9SAndrew Thompson else if (chan <= 64)
43933707a5e9SAndrew Thompson rf = 0xb7;
43943707a5e9SAndrew Thompson else if (chan <= 128)
43953707a5e9SAndrew Thompson rf = 0x74;
43963707a5e9SAndrew Thompson else
43973707a5e9SAndrew Thompson rf = 0x72;
43983707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 19, rf);
43993707a5e9SAndrew Thompson
44003707a5e9SAndrew Thompson /* set rx_lo1 */
44013707a5e9SAndrew Thompson if (chan <= 14)
44023707a5e9SAndrew Thompson rf = 0xb3;
44033707a5e9SAndrew Thompson else if (chan <= 64)
44043707a5e9SAndrew Thompson rf = 0xf6;
44053707a5e9SAndrew Thompson else if (chan <= 128)
44063707a5e9SAndrew Thompson rf = 0xf4;
44073707a5e9SAndrew Thompson else
44083707a5e9SAndrew Thompson rf = 0xf3;
44093707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 20, rf);
44103707a5e9SAndrew Thompson
44113707a5e9SAndrew Thompson /* set pfd_delay */
44123707a5e9SAndrew Thompson if (chan <= 14)
44133707a5e9SAndrew Thompson rf = 0x15;
44143707a5e9SAndrew Thompson else if (chan <= 64)
44153707a5e9SAndrew Thompson rf = 0x3d;
44163707a5e9SAndrew Thompson else
44173707a5e9SAndrew Thompson rf = 0x01;
44183707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 25, rf);
44193707a5e9SAndrew Thompson
44203707a5e9SAndrew Thompson /* set rx_lo2 */
44213707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 26, (chan <= 14) ? 0x85 : 0x87);
44223707a5e9SAndrew Thompson /* set ldo_rf_vc */
44233707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 27, (chan <= 14) ? 0x00 : 0x01);
44243707a5e9SAndrew Thompson /* set drv_cc */
44253707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 29, (chan <= 14) ? 0x9b : 0x9f);
44263707a5e9SAndrew Thompson
44273707a5e9SAndrew Thompson run_read(sc, RT2860_GPIO_CTRL, &tmp);
44283707a5e9SAndrew Thompson tmp &= ~0x8080;
44293707a5e9SAndrew Thompson if (chan <= 14)
44303707a5e9SAndrew Thompson tmp |= 0x80;
44313707a5e9SAndrew Thompson run_write(sc, RT2860_GPIO_CTRL, tmp);
44323707a5e9SAndrew Thompson
44333707a5e9SAndrew Thompson /* enable RF tuning */
44343707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 7, &rf);
44353707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 7, rf | 0x01);
44363707a5e9SAndrew Thompson
44373707a5e9SAndrew Thompson run_delay(sc, 2);
44383707a5e9SAndrew Thompson }
44393707a5e9SAndrew Thompson
44403707a5e9SAndrew Thompson static void
run_rt3593_set_chan(struct run_softc * sc,u_int chan)44417a7e01caSKevin Lo run_rt3593_set_chan(struct run_softc *sc, u_int chan)
44427a7e01caSKevin Lo {
44437a7e01caSKevin Lo int8_t txpow1, txpow2, txpow3;
44447a7e01caSKevin Lo uint8_t h20mhz, rf;
44457a7e01caSKevin Lo int i;
44467a7e01caSKevin Lo
44477a7e01caSKevin Lo /* find the settings for this channel (we know it exists) */
44487a7e01caSKevin Lo for (i = 0; rt2860_rf2850[i].chan != chan; i++);
44497a7e01caSKevin Lo
44507a7e01caSKevin Lo /* use Tx power values from EEPROM */
44517a7e01caSKevin Lo txpow1 = sc->txpow1[i];
44527a7e01caSKevin Lo txpow2 = sc->txpow2[i];
44537a7e01caSKevin Lo txpow3 = (sc->ntxchains == 3) ? sc->txpow3[i] : 0;
44547a7e01caSKevin Lo
44557a7e01caSKevin Lo if (chan <= 14) {
44567a7e01caSKevin Lo run_bbp_write(sc, 25, sc->bbp25);
44577a7e01caSKevin Lo run_bbp_write(sc, 26, sc->bbp26);
44587a7e01caSKevin Lo } else {
44597a7e01caSKevin Lo /* Enable IQ phase correction. */
44607a7e01caSKevin Lo run_bbp_write(sc, 25, 0x09);
44617a7e01caSKevin Lo run_bbp_write(sc, 26, 0xff);
44627a7e01caSKevin Lo }
44637a7e01caSKevin Lo
44647a7e01caSKevin Lo run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n);
44657a7e01caSKevin Lo run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f);
44667a7e01caSKevin Lo run_rt3070_rf_read(sc, 11, &rf);
44677a7e01caSKevin Lo rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03);
44687a7e01caSKevin Lo run_rt3070_rf_write(sc, 11, rf);
44697a7e01caSKevin Lo
44707a7e01caSKevin Lo /* Set pll_idoh. */
44717a7e01caSKevin Lo run_rt3070_rf_read(sc, 11, &rf);
44727a7e01caSKevin Lo rf &= ~0x4c;
44737a7e01caSKevin Lo rf |= (chan <= 14) ? 0x44 : 0x48;
44747a7e01caSKevin Lo run_rt3070_rf_write(sc, 11, rf);
44757a7e01caSKevin Lo
44767a7e01caSKevin Lo if (chan <= 14)
44777a7e01caSKevin Lo rf = txpow1 & 0x1f;
44787a7e01caSKevin Lo else
44797a7e01caSKevin Lo rf = 0x40 | ((txpow1 & 0x18) << 1) | (txpow1 & 0x07);
44807a7e01caSKevin Lo run_rt3070_rf_write(sc, 53, rf);
44817a7e01caSKevin Lo
44827a7e01caSKevin Lo if (chan <= 14)
44837a7e01caSKevin Lo rf = txpow2 & 0x1f;
44847a7e01caSKevin Lo else
44857a7e01caSKevin Lo rf = 0x40 | ((txpow2 & 0x18) << 1) | (txpow2 & 0x07);
44867a7e01caSKevin Lo run_rt3070_rf_write(sc, 55, rf);
44877a7e01caSKevin Lo
44887a7e01caSKevin Lo if (chan <= 14)
44897a7e01caSKevin Lo rf = txpow3 & 0x1f;
44907a7e01caSKevin Lo else
44917a7e01caSKevin Lo rf = 0x40 | ((txpow3 & 0x18) << 1) | (txpow3 & 0x07);
44927a7e01caSKevin Lo run_rt3070_rf_write(sc, 54, rf);
44937a7e01caSKevin Lo
44947a7e01caSKevin Lo rf = RT3070_RF_BLOCK | RT3070_PLL_PD;
44957a7e01caSKevin Lo if (sc->ntxchains == 3)
44967a7e01caSKevin Lo rf |= RT3070_TX0_PD | RT3070_TX1_PD | RT3070_TX2_PD;
44977a7e01caSKevin Lo else
44987a7e01caSKevin Lo rf |= RT3070_TX0_PD | RT3070_TX1_PD;
44997a7e01caSKevin Lo rf |= RT3070_RX0_PD | RT3070_RX1_PD | RT3070_RX2_PD;
45007a7e01caSKevin Lo run_rt3070_rf_write(sc, 1, rf);
45017a7e01caSKevin Lo
45027a7e01caSKevin Lo run_adjust_freq_offset(sc);
45037a7e01caSKevin Lo
45047a7e01caSKevin Lo run_rt3070_rf_write(sc, 31, (chan <= 14) ? 0xa0 : 0x80);
45057a7e01caSKevin Lo
45067a7e01caSKevin Lo h20mhz = (sc->rf24_20mhz & 0x20) >> 5;
45077a7e01caSKevin Lo run_rt3070_rf_read(sc, 30, &rf);
45087a7e01caSKevin Lo rf = (rf & ~0x06) | (h20mhz << 1) | (h20mhz << 2);
45097a7e01caSKevin Lo run_rt3070_rf_write(sc, 30, rf);
45107a7e01caSKevin Lo
45117a7e01caSKevin Lo run_rt3070_rf_read(sc, 36, &rf);
45127a7e01caSKevin Lo if (chan <= 14)
45137a7e01caSKevin Lo rf |= 0x80;
45147a7e01caSKevin Lo else
45157a7e01caSKevin Lo rf &= ~0x80;
45167a7e01caSKevin Lo run_rt3070_rf_write(sc, 36, rf);
45177a7e01caSKevin Lo
45187a7e01caSKevin Lo /* Set vcolo_bs. */
45197a7e01caSKevin Lo run_rt3070_rf_write(sc, 34, (chan <= 14) ? 0x3c : 0x20);
45207a7e01caSKevin Lo /* Set pfd_delay. */
45217a7e01caSKevin Lo run_rt3070_rf_write(sc, 12, (chan <= 14) ? 0x1a : 0x12);
45227a7e01caSKevin Lo
45237a7e01caSKevin Lo /* Set vco bias current control. */
45247a7e01caSKevin Lo run_rt3070_rf_read(sc, 6, &rf);
45257a7e01caSKevin Lo rf &= ~0xc0;
45267a7e01caSKevin Lo if (chan <= 14)
45277a7e01caSKevin Lo rf |= 0x40;
45287a7e01caSKevin Lo else if (chan <= 128)
45297a7e01caSKevin Lo rf |= 0x80;
45307a7e01caSKevin Lo else
45317a7e01caSKevin Lo rf |= 0x40;
45327a7e01caSKevin Lo run_rt3070_rf_write(sc, 6, rf);
45337a7e01caSKevin Lo
45347a7e01caSKevin Lo run_rt3070_rf_read(sc, 30, &rf);
45357a7e01caSKevin Lo rf = (rf & ~0x18) | 0x10;
45367a7e01caSKevin Lo run_rt3070_rf_write(sc, 30, rf);
45377a7e01caSKevin Lo
45387a7e01caSKevin Lo run_rt3070_rf_write(sc, 10, (chan <= 14) ? 0xd3 : 0xd8);
45397a7e01caSKevin Lo run_rt3070_rf_write(sc, 13, (chan <= 14) ? 0x12 : 0x23);
45407a7e01caSKevin Lo
45417a7e01caSKevin Lo run_rt3070_rf_read(sc, 51, &rf);
45427a7e01caSKevin Lo rf = (rf & ~0x03) | 0x01;
45437a7e01caSKevin Lo run_rt3070_rf_write(sc, 51, rf);
45447a7e01caSKevin Lo /* Set tx_mx1_cc. */
45457a7e01caSKevin Lo run_rt3070_rf_read(sc, 51, &rf);
45467a7e01caSKevin Lo rf &= ~0x1c;
45477a7e01caSKevin Lo rf |= (chan <= 14) ? 0x14 : 0x10;
45487a7e01caSKevin Lo run_rt3070_rf_write(sc, 51, rf);
45497a7e01caSKevin Lo /* Set tx_mx1_ic. */
45507a7e01caSKevin Lo run_rt3070_rf_read(sc, 51, &rf);
45517a7e01caSKevin Lo rf &= ~0xe0;
45527a7e01caSKevin Lo rf |= (chan <= 14) ? 0x60 : 0x40;
45537a7e01caSKevin Lo run_rt3070_rf_write(sc, 51, rf);
45547a7e01caSKevin Lo /* Set tx_lo1_ic. */
45557a7e01caSKevin Lo run_rt3070_rf_read(sc, 49, &rf);
45567a7e01caSKevin Lo rf &= ~0x1c;
45577a7e01caSKevin Lo rf |= (chan <= 14) ? 0x0c : 0x08;
45587a7e01caSKevin Lo run_rt3070_rf_write(sc, 49, rf);
45597a7e01caSKevin Lo /* Set tx_lo1_en. */
45607a7e01caSKevin Lo run_rt3070_rf_read(sc, 50, &rf);
45617a7e01caSKevin Lo run_rt3070_rf_write(sc, 50, rf & ~0x20);
45627a7e01caSKevin Lo /* Set drv_cc. */
45637a7e01caSKevin Lo run_rt3070_rf_read(sc, 57, &rf);
45647a7e01caSKevin Lo rf &= ~0xfc;
45657a7e01caSKevin Lo rf |= (chan <= 14) ? 0x6c : 0x3c;
45667a7e01caSKevin Lo run_rt3070_rf_write(sc, 57, rf);
45677a7e01caSKevin Lo /* Set rx_mix1_ic, rxa_lnactr, lna_vc, lna_inbias_en and lna_en. */
45687a7e01caSKevin Lo run_rt3070_rf_write(sc, 44, (chan <= 14) ? 0x93 : 0x9b);
45697a7e01caSKevin Lo /* Set drv_gnd_a, tx_vga_cc_a and tx_mx2_gain. */
45707a7e01caSKevin Lo run_rt3070_rf_write(sc, 52, (chan <= 14) ? 0x45 : 0x05);
45717a7e01caSKevin Lo /* Enable VCO calibration. */
45727a7e01caSKevin Lo run_rt3070_rf_read(sc, 3, &rf);
45737a7e01caSKevin Lo rf &= ~RT5390_VCOCAL;
45747a7e01caSKevin Lo rf |= (chan <= 14) ? RT5390_VCOCAL : 0xbe;
45757a7e01caSKevin Lo run_rt3070_rf_write(sc, 3, rf);
45767a7e01caSKevin Lo
45777a7e01caSKevin Lo if (chan <= 14)
45787a7e01caSKevin Lo rf = 0x23;
45797a7e01caSKevin Lo else if (chan <= 64)
45807a7e01caSKevin Lo rf = 0x36;
45817a7e01caSKevin Lo else if (chan <= 128)
45827a7e01caSKevin Lo rf = 0x32;
45837a7e01caSKevin Lo else
45847a7e01caSKevin Lo rf = 0x30;
45857a7e01caSKevin Lo run_rt3070_rf_write(sc, 39, rf);
45867a7e01caSKevin Lo if (chan <= 14)
45877a7e01caSKevin Lo rf = 0xbb;
45887a7e01caSKevin Lo else if (chan <= 64)
45897a7e01caSKevin Lo rf = 0xeb;
45907a7e01caSKevin Lo else if (chan <= 128)
45917a7e01caSKevin Lo rf = 0xb3;
45927a7e01caSKevin Lo else
45937a7e01caSKevin Lo rf = 0x9b;
45947a7e01caSKevin Lo run_rt3070_rf_write(sc, 45, rf);
45957a7e01caSKevin Lo
45967a7e01caSKevin Lo /* Set FEQ/AEQ control. */
45977a7e01caSKevin Lo run_bbp_write(sc, 105, 0x34);
45987a7e01caSKevin Lo }
45997a7e01caSKevin Lo
46007a7e01caSKevin Lo static void
run_rt5390_set_chan(struct run_softc * sc,u_int chan)460164891211SKevin Lo run_rt5390_set_chan(struct run_softc *sc, u_int chan)
460264891211SKevin Lo {
460364891211SKevin Lo int8_t txpow1, txpow2;
460464891211SKevin Lo uint8_t rf;
460564891211SKevin Lo int i;
460664891211SKevin Lo
460764891211SKevin Lo /* find the settings for this channel (we know it exists) */
460864891211SKevin Lo for (i = 0; rt2860_rf2850[i].chan != chan; i++);
460964891211SKevin Lo
461064891211SKevin Lo /* use Tx power values from EEPROM */
461164891211SKevin Lo txpow1 = sc->txpow1[i];
461264891211SKevin Lo txpow2 = sc->txpow2[i];
461364891211SKevin Lo
461464891211SKevin Lo run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n);
461564891211SKevin Lo run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f);
461664891211SKevin Lo run_rt3070_rf_read(sc, 11, &rf);
461764891211SKevin Lo rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03);
461864891211SKevin Lo run_rt3070_rf_write(sc, 11, rf);
461964891211SKevin Lo
462064891211SKevin Lo run_rt3070_rf_read(sc, 49, &rf);
462164891211SKevin Lo rf = (rf & ~0x3f) | (txpow1 & 0x3f);
462264891211SKevin Lo /* The valid range of the RF R49 is 0x00 to 0x27. */
462364891211SKevin Lo if ((rf & 0x3f) > 0x27)
462464891211SKevin Lo rf = (rf & ~0x3f) | 0x27;
462564891211SKevin Lo run_rt3070_rf_write(sc, 49, rf);
462664891211SKevin Lo
462764891211SKevin Lo if (sc->mac_ver == 0x5392) {
462864891211SKevin Lo run_rt3070_rf_read(sc, 50, &rf);
462964891211SKevin Lo rf = (rf & ~0x3f) | (txpow2 & 0x3f);
463064891211SKevin Lo /* The valid range of the RF R50 is 0x00 to 0x27. */
463164891211SKevin Lo if ((rf & 0x3f) > 0x27)
463264891211SKevin Lo rf = (rf & ~0x3f) | 0x27;
463364891211SKevin Lo run_rt3070_rf_write(sc, 50, rf);
463464891211SKevin Lo }
463564891211SKevin Lo
463664891211SKevin Lo run_rt3070_rf_read(sc, 1, &rf);
463764891211SKevin Lo rf |= RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD;
463864891211SKevin Lo if (sc->mac_ver == 0x5392)
463964891211SKevin Lo rf |= RT3070_RX1_PD | RT3070_TX1_PD;
464064891211SKevin Lo run_rt3070_rf_write(sc, 1, rf);
464164891211SKevin Lo
464264891211SKevin Lo if (sc->mac_ver != 0x5392) {
464364891211SKevin Lo run_rt3070_rf_read(sc, 2, &rf);
464464891211SKevin Lo rf |= 0x80;
464564891211SKevin Lo run_rt3070_rf_write(sc, 2, rf);
464664891211SKevin Lo run_delay(sc, 10);
464764891211SKevin Lo rf &= 0x7f;
464864891211SKevin Lo run_rt3070_rf_write(sc, 2, rf);
464964891211SKevin Lo }
465064891211SKevin Lo
465164891211SKevin Lo run_adjust_freq_offset(sc);
465264891211SKevin Lo
465364891211SKevin Lo if (sc->mac_ver == 0x5392) {
465464891211SKevin Lo /* Fix for RT5392C. */
465564891211SKevin Lo if (sc->mac_rev >= 0x0223) {
4656b8161ff3SKevin Lo if (chan <= 4)
465764891211SKevin Lo rf = 0x0f;
4658b8161ff3SKevin Lo else if (chan >= 5 && chan <= 7)
465964891211SKevin Lo rf = 0x0e;
4660b8161ff3SKevin Lo else
466164891211SKevin Lo rf = 0x0d;
466264891211SKevin Lo run_rt3070_rf_write(sc, 23, rf);
466364891211SKevin Lo
4664b8161ff3SKevin Lo if (chan <= 4)
466564891211SKevin Lo rf = 0x0c;
466664891211SKevin Lo else if (chan == 5)
466764891211SKevin Lo rf = 0x0b;
4668b8161ff3SKevin Lo else if (chan >= 6 && chan <= 7)
466964891211SKevin Lo rf = 0x0a;
4670b8161ff3SKevin Lo else if (chan >= 8 && chan <= 10)
467164891211SKevin Lo rf = 0x09;
4672b8161ff3SKevin Lo else
467364891211SKevin Lo rf = 0x08;
467464891211SKevin Lo run_rt3070_rf_write(sc, 59, rf);
467564891211SKevin Lo } else {
4676b8161ff3SKevin Lo if (chan <= 11)
467764891211SKevin Lo rf = 0x0f;
4678b8161ff3SKevin Lo else
467964891211SKevin Lo rf = 0x0b;
468064891211SKevin Lo run_rt3070_rf_write(sc, 59, rf);
468164891211SKevin Lo }
468264891211SKevin Lo } else {
468364891211SKevin Lo /* Fix for RT5390F. */
468464891211SKevin Lo if (sc->mac_rev >= 0x0502) {
4685b8161ff3SKevin Lo if (chan <= 11)
468664891211SKevin Lo rf = 0x43;
4687b8161ff3SKevin Lo else
468864891211SKevin Lo rf = 0x23;
468964891211SKevin Lo run_rt3070_rf_write(sc, 55, rf);
469064891211SKevin Lo
4691b8161ff3SKevin Lo if (chan <= 11)
469264891211SKevin Lo rf = 0x0f;
469364891211SKevin Lo else if (chan == 12)
469464891211SKevin Lo rf = 0x0d;
4695b8161ff3SKevin Lo else
469664891211SKevin Lo rf = 0x0b;
469764891211SKevin Lo run_rt3070_rf_write(sc, 59, rf);
469864891211SKevin Lo } else {
469964891211SKevin Lo run_rt3070_rf_write(sc, 55, 0x44);
470064891211SKevin Lo run_rt3070_rf_write(sc, 59, 0x8f);
470164891211SKevin Lo }
470264891211SKevin Lo }
470364891211SKevin Lo
470464891211SKevin Lo /* Enable VCO calibration. */
470564891211SKevin Lo run_rt3070_rf_read(sc, 3, &rf);
470664891211SKevin Lo rf |= RT5390_VCOCAL;
470764891211SKevin Lo run_rt3070_rf_write(sc, 3, rf);
470864891211SKevin Lo }
470964891211SKevin Lo
471064891211SKevin Lo static void
run_rt5592_set_chan(struct run_softc * sc,u_int chan)4711242dbae3SKevin Lo run_rt5592_set_chan(struct run_softc *sc, u_int chan)
4712242dbae3SKevin Lo {
4713242dbae3SKevin Lo const struct rt5592_freqs *freqs;
4714242dbae3SKevin Lo uint32_t tmp;
4715242dbae3SKevin Lo uint8_t reg, rf, txpow_bound;
4716242dbae3SKevin Lo int8_t txpow1, txpow2;
4717242dbae3SKevin Lo int i;
4718242dbae3SKevin Lo
4719242dbae3SKevin Lo run_read(sc, RT5592_DEBUG_INDEX, &tmp);
4720242dbae3SKevin Lo freqs = (tmp & RT5592_SEL_XTAL) ?
4721242dbae3SKevin Lo rt5592_freqs_40mhz : rt5592_freqs_20mhz;
4722242dbae3SKevin Lo
4723242dbae3SKevin Lo /* find the settings for this channel (we know it exists) */
4724242dbae3SKevin Lo for (i = 0; rt2860_rf2850[i].chan != chan; i++, freqs++);
4725242dbae3SKevin Lo
4726242dbae3SKevin Lo /* use Tx power values from EEPROM */
4727242dbae3SKevin Lo txpow1 = sc->txpow1[i];
4728242dbae3SKevin Lo txpow2 = sc->txpow2[i];
4729242dbae3SKevin Lo
4730242dbae3SKevin Lo run_read(sc, RT3070_LDO_CFG0, &tmp);
4731242dbae3SKevin Lo tmp &= ~0x1c000000;
4732242dbae3SKevin Lo if (chan > 14)
4733242dbae3SKevin Lo tmp |= 0x14000000;
4734242dbae3SKevin Lo run_write(sc, RT3070_LDO_CFG0, tmp);
4735242dbae3SKevin Lo
4736242dbae3SKevin Lo /* N setting. */
4737242dbae3SKevin Lo run_rt3070_rf_write(sc, 8, freqs->n & 0xff);
4738242dbae3SKevin Lo run_rt3070_rf_read(sc, 9, &rf);
4739242dbae3SKevin Lo rf &= ~(1 << 4);
4740242dbae3SKevin Lo rf |= ((freqs->n & 0x0100) >> 8) << 4;
4741242dbae3SKevin Lo run_rt3070_rf_write(sc, 9, rf);
4742242dbae3SKevin Lo
4743242dbae3SKevin Lo /* K setting. */
4744242dbae3SKevin Lo run_rt3070_rf_read(sc, 9, &rf);
4745242dbae3SKevin Lo rf &= ~0x0f;
4746242dbae3SKevin Lo rf |= (freqs->k & 0x0f);
4747242dbae3SKevin Lo run_rt3070_rf_write(sc, 9, rf);
4748242dbae3SKevin Lo
4749242dbae3SKevin Lo /* Mode setting. */
4750242dbae3SKevin Lo run_rt3070_rf_read(sc, 11, &rf);
4751242dbae3SKevin Lo rf &= ~0x0c;
4752242dbae3SKevin Lo rf |= ((freqs->m - 0x8) & 0x3) << 2;
4753242dbae3SKevin Lo run_rt3070_rf_write(sc, 11, rf);
4754242dbae3SKevin Lo run_rt3070_rf_read(sc, 9, &rf);
4755242dbae3SKevin Lo rf &= ~(1 << 7);
4756242dbae3SKevin Lo rf |= (((freqs->m - 0x8) & 0x4) >> 2) << 7;
4757242dbae3SKevin Lo run_rt3070_rf_write(sc, 9, rf);
4758242dbae3SKevin Lo
4759242dbae3SKevin Lo /* R setting. */
4760242dbae3SKevin Lo run_rt3070_rf_read(sc, 11, &rf);
4761242dbae3SKevin Lo rf &= ~0x03;
4762242dbae3SKevin Lo rf |= (freqs->r - 0x1);
4763242dbae3SKevin Lo run_rt3070_rf_write(sc, 11, rf);
4764242dbae3SKevin Lo
4765242dbae3SKevin Lo if (chan <= 14) {
4766242dbae3SKevin Lo /* Initialize RF registers for 2GHZ. */
4767242dbae3SKevin Lo for (i = 0; i < nitems(rt5592_2ghz_def_rf); i++) {
4768242dbae3SKevin Lo run_rt3070_rf_write(sc, rt5592_2ghz_def_rf[i].reg,
4769242dbae3SKevin Lo rt5592_2ghz_def_rf[i].val);
4770242dbae3SKevin Lo }
4771242dbae3SKevin Lo
4772242dbae3SKevin Lo rf = (chan <= 10) ? 0x07 : 0x06;
4773242dbae3SKevin Lo run_rt3070_rf_write(sc, 23, rf);
4774242dbae3SKevin Lo run_rt3070_rf_write(sc, 59, rf);
4775242dbae3SKevin Lo
4776242dbae3SKevin Lo run_rt3070_rf_write(sc, 55, 0x43);
4777242dbae3SKevin Lo
4778242dbae3SKevin Lo /*
4779242dbae3SKevin Lo * RF R49/R50 Tx power ALC code.
4780242dbae3SKevin Lo * G-band bit<7:6>=1:0, bit<5:0> range from 0x0 ~ 0x27.
4781242dbae3SKevin Lo */
4782242dbae3SKevin Lo reg = 2;
4783242dbae3SKevin Lo txpow_bound = 0x27;
4784242dbae3SKevin Lo } else {
4785242dbae3SKevin Lo /* Initialize RF registers for 5GHZ. */
4786242dbae3SKevin Lo for (i = 0; i < nitems(rt5592_5ghz_def_rf); i++) {
4787242dbae3SKevin Lo run_rt3070_rf_write(sc, rt5592_5ghz_def_rf[i].reg,
4788242dbae3SKevin Lo rt5592_5ghz_def_rf[i].val);
4789242dbae3SKevin Lo }
4790242dbae3SKevin Lo for (i = 0; i < nitems(rt5592_chan_5ghz); i++) {
4791242dbae3SKevin Lo if (chan >= rt5592_chan_5ghz[i].firstchan &&
4792242dbae3SKevin Lo chan <= rt5592_chan_5ghz[i].lastchan) {
4793242dbae3SKevin Lo run_rt3070_rf_write(sc, rt5592_chan_5ghz[i].reg,
4794242dbae3SKevin Lo rt5592_chan_5ghz[i].val);
4795242dbae3SKevin Lo }
4796242dbae3SKevin Lo }
4797242dbae3SKevin Lo
4798242dbae3SKevin Lo /*
4799242dbae3SKevin Lo * RF R49/R50 Tx power ALC code.
4800242dbae3SKevin Lo * A-band bit<7:6>=1:1, bit<5:0> range from 0x0 ~ 0x2b.
4801242dbae3SKevin Lo */
4802242dbae3SKevin Lo reg = 3;
4803242dbae3SKevin Lo txpow_bound = 0x2b;
4804242dbae3SKevin Lo }
4805242dbae3SKevin Lo
4806242dbae3SKevin Lo /* RF R49 ch0 Tx power ALC code. */
4807242dbae3SKevin Lo run_rt3070_rf_read(sc, 49, &rf);
4808242dbae3SKevin Lo rf &= ~0xc0;
4809242dbae3SKevin Lo rf |= (reg << 6);
4810242dbae3SKevin Lo rf = (rf & ~0x3f) | (txpow1 & 0x3f);
4811242dbae3SKevin Lo if ((rf & 0x3f) > txpow_bound)
4812242dbae3SKevin Lo rf = (rf & ~0x3f) | txpow_bound;
4813242dbae3SKevin Lo run_rt3070_rf_write(sc, 49, rf);
4814242dbae3SKevin Lo
4815242dbae3SKevin Lo /* RF R50 ch1 Tx power ALC code. */
4816242dbae3SKevin Lo run_rt3070_rf_read(sc, 50, &rf);
4817242dbae3SKevin Lo rf &= ~(1 << 7 | 1 << 6);
4818242dbae3SKevin Lo rf |= (reg << 6);
4819242dbae3SKevin Lo rf = (rf & ~0x3f) | (txpow2 & 0x3f);
4820242dbae3SKevin Lo if ((rf & 0x3f) > txpow_bound)
4821242dbae3SKevin Lo rf = (rf & ~0x3f) | txpow_bound;
4822242dbae3SKevin Lo run_rt3070_rf_write(sc, 50, rf);
4823242dbae3SKevin Lo
4824242dbae3SKevin Lo /* Enable RF_BLOCK, PLL_PD, RX0_PD, and TX0_PD. */
4825242dbae3SKevin Lo run_rt3070_rf_read(sc, 1, &rf);
4826242dbae3SKevin Lo rf |= (RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD);
4827242dbae3SKevin Lo if (sc->ntxchains > 1)
4828242dbae3SKevin Lo rf |= RT3070_TX1_PD;
4829242dbae3SKevin Lo if (sc->nrxchains > 1)
4830242dbae3SKevin Lo rf |= RT3070_RX1_PD;
4831242dbae3SKevin Lo run_rt3070_rf_write(sc, 1, rf);
4832242dbae3SKevin Lo
4833242dbae3SKevin Lo run_rt3070_rf_write(sc, 6, 0xe4);
4834242dbae3SKevin Lo
4835242dbae3SKevin Lo run_rt3070_rf_write(sc, 30, 0x10);
4836242dbae3SKevin Lo run_rt3070_rf_write(sc, 31, 0x80);
4837242dbae3SKevin Lo run_rt3070_rf_write(sc, 32, 0x80);
4838242dbae3SKevin Lo
4839242dbae3SKevin Lo run_adjust_freq_offset(sc);
4840242dbae3SKevin Lo
4841242dbae3SKevin Lo /* Enable VCO calibration. */
4842242dbae3SKevin Lo run_rt3070_rf_read(sc, 3, &rf);
4843242dbae3SKevin Lo rf |= RT5390_VCOCAL;
4844242dbae3SKevin Lo run_rt3070_rf_write(sc, 3, rf);
4845242dbae3SKevin Lo }
4846242dbae3SKevin Lo
4847242dbae3SKevin Lo static void
run_set_rx_antenna(struct run_softc * sc,int aux)4848069f1a80SAndrew Thompson run_set_rx_antenna(struct run_softc *sc, int aux)
4849069f1a80SAndrew Thompson {
4850069f1a80SAndrew Thompson uint32_t tmp;
485164891211SKevin Lo uint8_t bbp152;
4852069f1a80SAndrew Thompson
4853069f1a80SAndrew Thompson if (aux) {
485464891211SKevin Lo if (sc->rf_rev == RT5390_RF_5370) {
485564891211SKevin Lo run_bbp_read(sc, 152, &bbp152);
485664891211SKevin Lo run_bbp_write(sc, 152, bbp152 & ~0x80);
4857b8161ff3SKevin Lo } else {
48583707a5e9SAndrew Thompson run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 0);
4859069f1a80SAndrew Thompson run_read(sc, RT2860_GPIO_CTRL, &tmp);
4860069f1a80SAndrew Thompson run_write(sc, RT2860_GPIO_CTRL, (tmp & ~0x0808) | 0x08);
486164891211SKevin Lo }
4862069f1a80SAndrew Thompson } else {
486364891211SKevin Lo if (sc->rf_rev == RT5390_RF_5370) {
486464891211SKevin Lo run_bbp_read(sc, 152, &bbp152);
486564891211SKevin Lo run_bbp_write(sc, 152, bbp152 | 0x80);
4866b8161ff3SKevin Lo } else {
48673707a5e9SAndrew Thompson run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 1);
4868069f1a80SAndrew Thompson run_read(sc, RT2860_GPIO_CTRL, &tmp);
4869069f1a80SAndrew Thompson run_write(sc, RT2860_GPIO_CTRL, tmp & ~0x0808);
4870069f1a80SAndrew Thompson }
4871069f1a80SAndrew Thompson }
487264891211SKevin Lo }
4873069f1a80SAndrew Thompson
4874069f1a80SAndrew Thompson static int
run_set_chan(struct run_softc * sc,struct ieee80211_channel * c)4875069f1a80SAndrew Thompson run_set_chan(struct run_softc *sc, struct ieee80211_channel *c)
4876069f1a80SAndrew Thompson {
48777a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
487835a24898SHans Petter Selasky u_int chan, group;
4879069f1a80SAndrew Thompson
4880069f1a80SAndrew Thompson chan = ieee80211_chan2ieee(ic, c);
4881069f1a80SAndrew Thompson if (chan == 0 || chan == IEEE80211_CHAN_ANY)
488275c76159SAndrew Thompson return (EINVAL);
4883069f1a80SAndrew Thompson
4884242dbae3SKevin Lo if (sc->mac_ver == 0x5592)
4885242dbae3SKevin Lo run_rt5592_set_chan(sc, chan);
4886242dbae3SKevin Lo else if (sc->mac_ver >= 0x5390)
488764891211SKevin Lo run_rt5390_set_chan(sc, chan);
48887a7e01caSKevin Lo else if (sc->mac_ver == 0x3593)
48897a7e01caSKevin Lo run_rt3593_set_chan(sc, chan);
489064891211SKevin Lo else if (sc->mac_ver == 0x3572)
48913707a5e9SAndrew Thompson run_rt3572_set_chan(sc, chan);
48923707a5e9SAndrew Thompson else if (sc->mac_ver >= 0x3070)
4893069f1a80SAndrew Thompson run_rt3070_set_chan(sc, chan);
4894069f1a80SAndrew Thompson else
4895069f1a80SAndrew Thompson run_rt2870_set_chan(sc, chan);
4896069f1a80SAndrew Thompson
4897069f1a80SAndrew Thompson /* determine channel group */
4898069f1a80SAndrew Thompson if (chan <= 14)
4899069f1a80SAndrew Thompson group = 0;
4900069f1a80SAndrew Thompson else if (chan <= 64)
4901069f1a80SAndrew Thompson group = 1;
4902069f1a80SAndrew Thompson else if (chan <= 128)
4903069f1a80SAndrew Thompson group = 2;
4904069f1a80SAndrew Thompson else
4905069f1a80SAndrew Thompson group = 3;
4906069f1a80SAndrew Thompson
4907069f1a80SAndrew Thompson /* XXX necessary only when group has changed! */
4908069f1a80SAndrew Thompson run_select_chan_group(sc, group);
4909069f1a80SAndrew Thompson
4910069f1a80SAndrew Thompson run_delay(sc, 10);
4911069f1a80SAndrew Thompson
4912c48c6bf8SKevin Lo /* Perform IQ calibration. */
4913010b13faSKevin Lo if (sc->mac_ver >= 0x5392)
4914010b13faSKevin Lo run_iq_calib(sc, chan);
4915010b13faSKevin Lo
491675c76159SAndrew Thompson return (0);
4917069f1a80SAndrew Thompson }
4918069f1a80SAndrew Thompson
4919069f1a80SAndrew Thompson static void
run_set_channel(struct ieee80211com * ic)4920069f1a80SAndrew Thompson run_set_channel(struct ieee80211com *ic)
4921069f1a80SAndrew Thompson {
4922d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
4923069f1a80SAndrew Thompson
4924069f1a80SAndrew Thompson RUN_LOCK(sc);
4925069f1a80SAndrew Thompson run_set_chan(sc, ic->ic_curchan);
4926069f1a80SAndrew Thompson RUN_UNLOCK(sc);
4927069f1a80SAndrew Thompson
4928069f1a80SAndrew Thompson return;
4929069f1a80SAndrew Thompson }
4930069f1a80SAndrew Thompson
4931069f1a80SAndrew Thompson static void
run_getradiocaps(struct ieee80211com * ic,int maxchans,int * nchans,struct ieee80211_channel chans[])493299bb30a9SAndriy Voskoboinyk run_getradiocaps(struct ieee80211com *ic,
493399bb30a9SAndriy Voskoboinyk int maxchans, int *nchans, struct ieee80211_channel chans[])
493499bb30a9SAndriy Voskoboinyk {
493599bb30a9SAndriy Voskoboinyk struct run_softc *sc = ic->ic_softc;
493699bb30a9SAndriy Voskoboinyk uint8_t bands[IEEE80211_MODE_BYTES];
493799bb30a9SAndriy Voskoboinyk
493899bb30a9SAndriy Voskoboinyk memset(bands, 0, sizeof(bands));
493999bb30a9SAndriy Voskoboinyk setbit(bands, IEEE80211_MODE_11B);
494099bb30a9SAndriy Voskoboinyk setbit(bands, IEEE80211_MODE_11G);
4941c775d4acSAdrian Chadd if (sc->rf_rev != RT3070_RF_2020)
4942f520d761SAdrian Chadd setbit(bands, IEEE80211_MODE_11NG);
4943f520d761SAdrian Chadd
4944f520d761SAdrian Chadd /* Note: for now, only support HT20 channels */
4945b84b3638SAndriy Voskoboinyk ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0);
494699bb30a9SAndriy Voskoboinyk
494799bb30a9SAndriy Voskoboinyk if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850 ||
494899bb30a9SAndriy Voskoboinyk sc->rf_rev == RT3070_RF_3052 || sc->rf_rev == RT3593_RF_3053 ||
494999bb30a9SAndriy Voskoboinyk sc->rf_rev == RT5592_RF_5592) {
495099bb30a9SAndriy Voskoboinyk setbit(bands, IEEE80211_MODE_11A);
49518b05d37aSAdrian Chadd if (sc->rf_rev != RT3070_RF_2020)
49528b05d37aSAdrian Chadd setbit(bands, IEEE80211_MODE_11NA);
4953f520d761SAdrian Chadd /* Note: for now, only support HT20 channels */
495499bb30a9SAndriy Voskoboinyk ieee80211_add_channel_list_5ghz(chans, maxchans, nchans,
495599bb30a9SAndriy Voskoboinyk run_chan_5ghz, nitems(run_chan_5ghz), bands, 0);
495699bb30a9SAndriy Voskoboinyk }
495799bb30a9SAndriy Voskoboinyk }
495899bb30a9SAndriy Voskoboinyk
495999bb30a9SAndriy Voskoboinyk static void
run_scan_start(struct ieee80211com * ic)4960069f1a80SAndrew Thompson run_scan_start(struct ieee80211com *ic)
4961069f1a80SAndrew Thompson {
4962d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
4963069f1a80SAndrew Thompson
4964069f1a80SAndrew Thompson RUN_LOCK(sc);
4965069f1a80SAndrew Thompson
4966069f1a80SAndrew Thompson /* abort TSF synchronization */
4967ae6f61b1SAndriy Voskoboinyk run_disable_tsf(sc);
49687a79cebfSGleb Smirnoff run_set_bssid(sc, ieee80211broadcastaddr);
4969069f1a80SAndrew Thompson
4970069f1a80SAndrew Thompson RUN_UNLOCK(sc);
4971069f1a80SAndrew Thompson
4972069f1a80SAndrew Thompson return;
4973069f1a80SAndrew Thompson }
4974069f1a80SAndrew Thompson
4975069f1a80SAndrew Thompson static void
run_scan_end(struct ieee80211com * ic)4976069f1a80SAndrew Thompson run_scan_end(struct ieee80211com *ic)
4977069f1a80SAndrew Thompson {
4978d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
4979069f1a80SAndrew Thompson
4980069f1a80SAndrew Thompson RUN_LOCK(sc);
4981069f1a80SAndrew Thompson
4982069f1a80SAndrew Thompson run_enable_tsf_sync(sc);
4983ae132f11SAndriy Voskoboinyk run_set_bssid(sc, sc->sc_bssid);
4984069f1a80SAndrew Thompson
4985069f1a80SAndrew Thompson RUN_UNLOCK(sc);
4986069f1a80SAndrew Thompson
4987069f1a80SAndrew Thompson return;
4988069f1a80SAndrew Thompson }
4989069f1a80SAndrew Thompson
499085e7bb81SAndrew Thompson /*
499185e7bb81SAndrew Thompson * Could be called from ieee80211_node_timeout()
499285e7bb81SAndrew Thompson * (non-sleepable thread)
499385e7bb81SAndrew Thompson */
499485e7bb81SAndrew Thompson static void
run_update_beacon(struct ieee80211vap * vap,int item)499585e7bb81SAndrew Thompson run_update_beacon(struct ieee80211vap *vap, int item)
4996069f1a80SAndrew Thompson {
499785e7bb81SAndrew Thompson struct ieee80211com *ic = vap->iv_ic;
49980cf00015SAdrian Chadd struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
49990cf00015SAdrian Chadd struct ieee80211_node *ni = vap->iv_bss;
5000d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
5001e7d14e9bSBernhard Schmidt struct run_vap *rvp = RUN_VAP(vap);
5002e7d14e9bSBernhard Schmidt int mcast = 0;
500385e7bb81SAndrew Thompson uint32_t i;
500485e7bb81SAndrew Thompson
5005e7d14e9bSBernhard Schmidt switch (item) {
5006e7d14e9bSBernhard Schmidt case IEEE80211_BEACON_ERP:
5007272f6adeSGleb Smirnoff run_updateslot(ic);
5008e7d14e9bSBernhard Schmidt break;
5009e7d14e9bSBernhard Schmidt case IEEE80211_BEACON_HTINFO:
5010e7d14e9bSBernhard Schmidt run_updateprot(ic);
5011e7d14e9bSBernhard Schmidt break;
5012e7d14e9bSBernhard Schmidt case IEEE80211_BEACON_TIM:
5013e7d14e9bSBernhard Schmidt mcast = 1; /*TODO*/
5014e7d14e9bSBernhard Schmidt break;
5015e7d14e9bSBernhard Schmidt default:
5016e7d14e9bSBernhard Schmidt break;
5017e7d14e9bSBernhard Schmidt }
5018e7d14e9bSBernhard Schmidt
50190cf00015SAdrian Chadd setbit(bo->bo_flags, item);
5020b1c5d8f7SKevin Lo if (rvp->beacon_mbuf == NULL) {
5021210ab3c2SAdrian Chadd rvp->beacon_mbuf = ieee80211_beacon_alloc(ni);
5022b1c5d8f7SKevin Lo if (rvp->beacon_mbuf == NULL)
5023b1c5d8f7SKevin Lo return;
5024b1c5d8f7SKevin Lo }
5025210ab3c2SAdrian Chadd ieee80211_beacon_update(ni, rvp->beacon_mbuf, mcast);
5026e7d14e9bSBernhard Schmidt
502785e7bb81SAndrew Thompson i = RUN_CMDQ_GET(&sc->cmdq_store);
5028109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i);
502985e7bb81SAndrew Thompson sc->cmdq[i].func = run_update_beacon_cb;
503085e7bb81SAndrew Thompson sc->cmdq[i].arg0 = vap;
503185e7bb81SAndrew Thompson ieee80211_runtask(ic, &sc->cmdq_task);
503285e7bb81SAndrew Thompson
503385e7bb81SAndrew Thompson return;
5034069f1a80SAndrew Thompson }
5035069f1a80SAndrew Thompson
5036069f1a80SAndrew Thompson static void
run_update_beacon_cb(void * arg)503785e7bb81SAndrew Thompson run_update_beacon_cb(void *arg)
5038069f1a80SAndrew Thompson {
503985e7bb81SAndrew Thompson struct ieee80211vap *vap = arg;
50400cf00015SAdrian Chadd struct ieee80211_node *ni = vap->iv_bss;
5041e7d14e9bSBernhard Schmidt struct run_vap *rvp = RUN_VAP(vap);
5042069f1a80SAndrew Thompson struct ieee80211com *ic = vap->iv_ic;
5043d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
5044069f1a80SAndrew Thompson struct rt2860_txwi txwi;
5045069f1a80SAndrew Thompson struct mbuf *m;
5046242dbae3SKevin Lo uint16_t txwisize;
504785e7bb81SAndrew Thompson uint8_t ridx;
504885e7bb81SAndrew Thompson
50490cf00015SAdrian Chadd if (ni->ni_chan == IEEE80211_CHAN_ANYC)
505085e7bb81SAndrew Thompson return;
5051bb571462SHans Petter Selasky if (ic->ic_bsschan == IEEE80211_CHAN_ANYC)
5052bb571462SHans Petter Selasky return;
5053069f1a80SAndrew Thompson
5054e7d14e9bSBernhard Schmidt /*
5055e7d14e9bSBernhard Schmidt * No need to call ieee80211_beacon_update(), run_update_beacon()
505620733245SPedro F. Giffuni * is taking care of appropriate calls.
5057e7d14e9bSBernhard Schmidt */
5058e7d14e9bSBernhard Schmidt if (rvp->beacon_mbuf == NULL) {
5059210ab3c2SAdrian Chadd rvp->beacon_mbuf = ieee80211_beacon_alloc(ni);
5060e7d14e9bSBernhard Schmidt if (rvp->beacon_mbuf == NULL)
5061069f1a80SAndrew Thompson return;
5062e7d14e9bSBernhard Schmidt }
5063e7d14e9bSBernhard Schmidt m = rvp->beacon_mbuf;
5064069f1a80SAndrew Thompson
5065242dbae3SKevin Lo memset(&txwi, 0, sizeof(txwi));
5066069f1a80SAndrew Thompson txwi.wcid = 0xff;
5067069f1a80SAndrew Thompson txwi.len = htole16(m->m_pkthdr.len);
5068242dbae3SKevin Lo
5069069f1a80SAndrew Thompson /* send beacons at the lowest available rate */
507085e7bb81SAndrew Thompson ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
507185e7bb81SAndrew Thompson RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1;
507285e7bb81SAndrew Thompson txwi.phy = htole16(rt2860_rates[ridx].mcs);
507385e7bb81SAndrew Thompson if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM)
5074069f1a80SAndrew Thompson txwi.phy |= htole16(RT2860_PHY_OFDM);
5075069f1a80SAndrew Thompson txwi.txop = RT2860_TX_TXOP_HT;
5076069f1a80SAndrew Thompson txwi.flags = RT2860_TX_TS;
5077beaa0537SAndrew Thompson txwi.xflags = RT2860_TX_NSEQ;
5078069f1a80SAndrew Thompson
5079242dbae3SKevin Lo txwisize = (sc->mac_ver == 0x5592) ?
5080242dbae3SKevin Lo sizeof(txwi) + sizeof(uint32_t) : sizeof(txwi);
5081242dbae3SKevin Lo run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id), (uint8_t *)&txwi,
5082242dbae3SKevin Lo txwisize);
5083242dbae3SKevin Lo run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id) + txwisize,
5084242dbae3SKevin Lo mtod(m, uint8_t *), (m->m_pkthdr.len + 1) & ~1);
5085069f1a80SAndrew Thompson }
5086069f1a80SAndrew Thompson
5087069f1a80SAndrew Thompson static void
run_updateprot(struct ieee80211com * ic)5088069f1a80SAndrew Thompson run_updateprot(struct ieee80211com *ic)
5089069f1a80SAndrew Thompson {
5090d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
5091e7d14e9bSBernhard Schmidt uint32_t i;
5092e7d14e9bSBernhard Schmidt
5093e7d14e9bSBernhard Schmidt i = RUN_CMDQ_GET(&sc->cmdq_store);
5094109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i);
5095e7d14e9bSBernhard Schmidt sc->cmdq[i].func = run_updateprot_cb;
5096e7d14e9bSBernhard Schmidt sc->cmdq[i].arg0 = ic;
5097e7d14e9bSBernhard Schmidt ieee80211_runtask(ic, &sc->cmdq_task);
5098e7d14e9bSBernhard Schmidt }
5099e7d14e9bSBernhard Schmidt
5100e7d14e9bSBernhard Schmidt static void
run_updateprot_cb(void * arg)5101e7d14e9bSBernhard Schmidt run_updateprot_cb(void *arg)
5102e7d14e9bSBernhard Schmidt {
5103e7d14e9bSBernhard Schmidt struct ieee80211com *ic = arg;
5104d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
5105069f1a80SAndrew Thompson uint32_t tmp;
5106069f1a80SAndrew Thompson
5107069f1a80SAndrew Thompson tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL;
5108069f1a80SAndrew Thompson /* setup protection frame rate (MCS code) */
5109069f1a80SAndrew Thompson tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ?
511051a25d34SKevin Lo rt2860_rates[RT2860_RIDX_OFDM6].mcs | RT2860_PHY_OFDM :
5111069f1a80SAndrew Thompson rt2860_rates[RT2860_RIDX_CCK11].mcs;
5112069f1a80SAndrew Thompson
5113069f1a80SAndrew Thompson /* CCK frames don't require protection */
5114069f1a80SAndrew Thompson run_write(sc, RT2860_CCK_PROT_CFG, tmp);
5115069f1a80SAndrew Thompson if (ic->ic_flags & IEEE80211_F_USEPROT) {
5116069f1a80SAndrew Thompson if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
5117069f1a80SAndrew Thompson tmp |= RT2860_PROT_CTRL_RTS_CTS;
5118069f1a80SAndrew Thompson else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
5119069f1a80SAndrew Thompson tmp |= RT2860_PROT_CTRL_CTS;
5120069f1a80SAndrew Thompson }
5121069f1a80SAndrew Thompson run_write(sc, RT2860_OFDM_PROT_CFG, tmp);
5122069f1a80SAndrew Thompson }
5123069f1a80SAndrew Thompson
5124069f1a80SAndrew Thompson static void
run_usb_timeout_cb(void * arg)512585e7bb81SAndrew Thompson run_usb_timeout_cb(void *arg)
5126069f1a80SAndrew Thompson {
512785e7bb81SAndrew Thompson struct ieee80211vap *vap = arg;
5128d3fdd08cSAdrian Chadd struct run_softc *sc = vap->iv_ic->ic_softc;
5129069f1a80SAndrew Thompson
513085e7bb81SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
5131069f1a80SAndrew Thompson
5132069f1a80SAndrew Thompson if(vap->iv_state == IEEE80211_S_RUN &&
5133069f1a80SAndrew Thompson vap->iv_opmode != IEEE80211_M_STA)
5134069f1a80SAndrew Thompson run_reset_livelock(sc);
5135069f1a80SAndrew Thompson else if (vap->iv_state == IEEE80211_S_SCAN) {
5136109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_USB | RUN_DEBUG_STATE,
5137109005ccSGavin Atkinson "timeout caused by scan\n");
5138069f1a80SAndrew Thompson /* cancel bgscan */
5139069f1a80SAndrew Thompson ieee80211_cancel_scan(vap);
5140069f1a80SAndrew Thompson } else
5141109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_USB | RUN_DEBUG_STATE,
5142109005ccSGavin Atkinson "timeout by unknown cause\n");
5143069f1a80SAndrew Thompson }
5144069f1a80SAndrew Thompson
5145069f1a80SAndrew Thompson static void
run_reset_livelock(struct run_softc * sc)5146069f1a80SAndrew Thompson run_reset_livelock(struct run_softc *sc)
5147069f1a80SAndrew Thompson {
5148069f1a80SAndrew Thompson uint32_t tmp;
5149069f1a80SAndrew Thompson
515085e7bb81SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
515185e7bb81SAndrew Thompson
5152069f1a80SAndrew Thompson /*
5153069f1a80SAndrew Thompson * In IBSS or HostAP modes (when the hardware sends beacons), the MAC
5154069f1a80SAndrew Thompson * can run into a livelock and start sending CTS-to-self frames like
5155069f1a80SAndrew Thompson * crazy if protection is enabled. Reset MAC/BBP for a while
5156069f1a80SAndrew Thompson */
5157069f1a80SAndrew Thompson run_read(sc, RT2860_DEBUG, &tmp);
5158109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RESET, "debug reg %08x\n", tmp);
5159069f1a80SAndrew Thompson if ((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))) {
5160109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RESET,
5161109005ccSGavin Atkinson "CTS-to-self livelock detected\n");
5162069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_SRST);
5163069f1a80SAndrew Thompson run_delay(sc, 1);
5164069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_SYS_CTRL,
5165069f1a80SAndrew Thompson RT2860_MAC_RX_EN | RT2860_MAC_TX_EN);
5166069f1a80SAndrew Thompson }
5167069f1a80SAndrew Thompson }
5168069f1a80SAndrew Thompson
5169069f1a80SAndrew Thompson static void
run_update_promisc_locked(struct run_softc * sc)5170272f6adeSGleb Smirnoff run_update_promisc_locked(struct run_softc *sc)
5171069f1a80SAndrew Thompson {
5172069f1a80SAndrew Thompson uint32_t tmp;
5173069f1a80SAndrew Thompson
5174069f1a80SAndrew Thompson run_read(sc, RT2860_RX_FILTR_CFG, &tmp);
5175069f1a80SAndrew Thompson
5176069f1a80SAndrew Thompson tmp |= RT2860_DROP_UC_NOME;
51777a79cebfSGleb Smirnoff if (sc->sc_ic.ic_promisc > 0)
5178069f1a80SAndrew Thompson tmp &= ~RT2860_DROP_UC_NOME;
5179069f1a80SAndrew Thompson
5180069f1a80SAndrew Thompson run_write(sc, RT2860_RX_FILTR_CFG, tmp);
5181069f1a80SAndrew Thompson
5182109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s promiscuous mode\n",
5183109005ccSGavin Atkinson (sc->sc_ic.ic_promisc > 0) ? "entering" : "leaving");
5184069f1a80SAndrew Thompson }
5185069f1a80SAndrew Thompson
5186069f1a80SAndrew Thompson static void
run_update_promisc(struct ieee80211com * ic)5187272f6adeSGleb Smirnoff run_update_promisc(struct ieee80211com *ic)
5188069f1a80SAndrew Thompson {
5189272f6adeSGleb Smirnoff struct run_softc *sc = ic->ic_softc;
5190069f1a80SAndrew Thompson
51917a79cebfSGleb Smirnoff if ((sc->sc_flags & RUN_RUNNING) == 0)
5192069f1a80SAndrew Thompson return;
5193069f1a80SAndrew Thompson
5194069f1a80SAndrew Thompson RUN_LOCK(sc);
5195272f6adeSGleb Smirnoff run_update_promisc_locked(sc);
5196069f1a80SAndrew Thompson RUN_UNLOCK(sc);
5197069f1a80SAndrew Thompson }
5198069f1a80SAndrew Thompson
5199069f1a80SAndrew Thompson static void
run_enable_tsf_sync(struct run_softc * sc)5200069f1a80SAndrew Thompson run_enable_tsf_sync(struct run_softc *sc)
5201069f1a80SAndrew Thompson {
52027a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
5203069f1a80SAndrew Thompson struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
5204069f1a80SAndrew Thompson uint32_t tmp;
5205069f1a80SAndrew Thompson
5206109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "rvp_id=%d ic_opmode=%d\n",
5207109005ccSGavin Atkinson RUN_VAP(vap)->rvp_id, ic->ic_opmode);
520885e7bb81SAndrew Thompson
5209069f1a80SAndrew Thompson run_read(sc, RT2860_BCN_TIME_CFG, &tmp);
5210069f1a80SAndrew Thompson tmp &= ~0x1fffff;
5211069f1a80SAndrew Thompson tmp |= vap->iv_bss->ni_intval * 16;
5212069f1a80SAndrew Thompson tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN;
5213069f1a80SAndrew Thompson
521485e7bb81SAndrew Thompson if (ic->ic_opmode == IEEE80211_M_STA) {
5215069f1a80SAndrew Thompson /*
5216069f1a80SAndrew Thompson * Local TSF is always updated with remote TSF on beacon
5217069f1a80SAndrew Thompson * reception.
5218069f1a80SAndrew Thompson */
5219069f1a80SAndrew Thompson tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT;
522085e7bb81SAndrew Thompson } else if (ic->ic_opmode == IEEE80211_M_IBSS) {
5221069f1a80SAndrew Thompson tmp |= RT2860_BCN_TX_EN;
5222069f1a80SAndrew Thompson /*
5223069f1a80SAndrew Thompson * Local TSF is updated with remote TSF on beacon reception
5224069f1a80SAndrew Thompson * only if the remote TSF is greater than local TSF.
5225069f1a80SAndrew Thompson */
5226069f1a80SAndrew Thompson tmp |= 2 << RT2860_TSF_SYNC_MODE_SHIFT;
522785e7bb81SAndrew Thompson } else if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
522885e7bb81SAndrew Thompson ic->ic_opmode == IEEE80211_M_MBSS) {
5229069f1a80SAndrew Thompson tmp |= RT2860_BCN_TX_EN;
5230069f1a80SAndrew Thompson /* SYNC with nobody */
5231069f1a80SAndrew Thompson tmp |= 3 << RT2860_TSF_SYNC_MODE_SHIFT;
523285e7bb81SAndrew Thompson } else {
5233109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_BEACON,
5234109005ccSGavin Atkinson "Enabling TSF failed. undefined opmode\n");
523585e7bb81SAndrew Thompson return;
523685e7bb81SAndrew Thompson }
5237069f1a80SAndrew Thompson
5238069f1a80SAndrew Thompson run_write(sc, RT2860_BCN_TIME_CFG, tmp);
5239069f1a80SAndrew Thompson }
5240069f1a80SAndrew Thompson
5241069f1a80SAndrew Thompson static void
run_enable_tsf(struct run_softc * sc)5242599acbbcSKevin Lo run_enable_tsf(struct run_softc *sc)
5243599acbbcSKevin Lo {
5244599acbbcSKevin Lo uint32_t tmp;
5245599acbbcSKevin Lo
5246599acbbcSKevin Lo if (run_read(sc, RT2860_BCN_TIME_CFG, &tmp) == 0) {
5247599acbbcSKevin Lo tmp &= ~(RT2860_BCN_TX_EN | RT2860_TBTT_TIMER_EN);
5248599acbbcSKevin Lo tmp |= RT2860_TSF_TIMER_EN;
5249599acbbcSKevin Lo run_write(sc, RT2860_BCN_TIME_CFG, tmp);
5250599acbbcSKevin Lo }
5251599acbbcSKevin Lo }
5252599acbbcSKevin Lo
5253599acbbcSKevin Lo static void
run_disable_tsf(struct run_softc * sc)5254ae6f61b1SAndriy Voskoboinyk run_disable_tsf(struct run_softc *sc)
5255ae6f61b1SAndriy Voskoboinyk {
5256ae6f61b1SAndriy Voskoboinyk uint32_t tmp;
5257ae6f61b1SAndriy Voskoboinyk
5258ae6f61b1SAndriy Voskoboinyk if (run_read(sc, RT2860_BCN_TIME_CFG, &tmp) == 0) {
5259ae6f61b1SAndriy Voskoboinyk tmp &= ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN |
5260ae6f61b1SAndriy Voskoboinyk RT2860_TBTT_TIMER_EN);
5261ae6f61b1SAndriy Voskoboinyk run_write(sc, RT2860_BCN_TIME_CFG, tmp);
5262ae6f61b1SAndriy Voskoboinyk }
5263ae6f61b1SAndriy Voskoboinyk }
5264ae6f61b1SAndriy Voskoboinyk
5265ae6f61b1SAndriy Voskoboinyk static void
run_get_tsf(struct run_softc * sc,uint64_t * buf)5266ef9c0768SKevin Lo run_get_tsf(struct run_softc *sc, uint64_t *buf)
5267ef9c0768SKevin Lo {
5268ef9c0768SKevin Lo run_read_region_1(sc, RT2860_TSF_TIMER_DW0, (uint8_t *)buf,
5269ef9c0768SKevin Lo sizeof(*buf));
5270ef9c0768SKevin Lo }
5271ef9c0768SKevin Lo
5272ef9c0768SKevin Lo static void
run_enable_mrr(struct run_softc * sc)5273069f1a80SAndrew Thompson run_enable_mrr(struct run_softc *sc)
5274069f1a80SAndrew Thompson {
5275069f1a80SAndrew Thompson #define CCK(mcs) (mcs)
5276069f1a80SAndrew Thompson #define OFDM(mcs) (1 << 3 | (mcs))
5277069f1a80SAndrew Thompson run_write(sc, RT2860_LG_FBK_CFG0,
5278069f1a80SAndrew Thompson OFDM(6) << 28 | /* 54->48 */
5279069f1a80SAndrew Thompson OFDM(5) << 24 | /* 48->36 */
5280069f1a80SAndrew Thompson OFDM(4) << 20 | /* 36->24 */
5281069f1a80SAndrew Thompson OFDM(3) << 16 | /* 24->18 */
5282069f1a80SAndrew Thompson OFDM(2) << 12 | /* 18->12 */
5283069f1a80SAndrew Thompson OFDM(1) << 8 | /* 12-> 9 */
5284069f1a80SAndrew Thompson OFDM(0) << 4 | /* 9-> 6 */
5285069f1a80SAndrew Thompson OFDM(0)); /* 6-> 6 */
5286069f1a80SAndrew Thompson
5287069f1a80SAndrew Thompson run_write(sc, RT2860_LG_FBK_CFG1,
5288069f1a80SAndrew Thompson CCK(2) << 12 | /* 11->5.5 */
5289069f1a80SAndrew Thompson CCK(1) << 8 | /* 5.5-> 2 */
5290069f1a80SAndrew Thompson CCK(0) << 4 | /* 2-> 1 */
5291069f1a80SAndrew Thompson CCK(0)); /* 1-> 1 */
5292069f1a80SAndrew Thompson #undef OFDM
5293069f1a80SAndrew Thompson #undef CCK
5294069f1a80SAndrew Thompson }
5295069f1a80SAndrew Thompson
5296069f1a80SAndrew Thompson static void
run_set_txpreamble(struct run_softc * sc)5297069f1a80SAndrew Thompson run_set_txpreamble(struct run_softc *sc)
5298069f1a80SAndrew Thompson {
52997a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
5300069f1a80SAndrew Thompson uint32_t tmp;
5301069f1a80SAndrew Thompson
5302069f1a80SAndrew Thompson run_read(sc, RT2860_AUTO_RSP_CFG, &tmp);
5303069f1a80SAndrew Thompson if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
5304069f1a80SAndrew Thompson tmp |= RT2860_CCK_SHORT_EN;
5305069f1a80SAndrew Thompson else
5306069f1a80SAndrew Thompson tmp &= ~RT2860_CCK_SHORT_EN;
5307069f1a80SAndrew Thompson run_write(sc, RT2860_AUTO_RSP_CFG, tmp);
5308069f1a80SAndrew Thompson }
5309069f1a80SAndrew Thompson
5310069f1a80SAndrew Thompson static void
run_set_basicrates(struct run_softc * sc)5311069f1a80SAndrew Thompson run_set_basicrates(struct run_softc *sc)
5312069f1a80SAndrew Thompson {
53137a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
5314069f1a80SAndrew Thompson
5315069f1a80SAndrew Thompson /* set basic rates mask */
5316069f1a80SAndrew Thompson if (ic->ic_curmode == IEEE80211_MODE_11B)
5317069f1a80SAndrew Thompson run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x003);
5318069f1a80SAndrew Thompson else if (ic->ic_curmode == IEEE80211_MODE_11A)
5319069f1a80SAndrew Thompson run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x150);
5320069f1a80SAndrew Thompson else /* 11g */
5321069f1a80SAndrew Thompson run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x15f);
5322069f1a80SAndrew Thompson }
5323069f1a80SAndrew Thompson
5324069f1a80SAndrew Thompson static void
run_set_leds(struct run_softc * sc,uint16_t which)5325069f1a80SAndrew Thompson run_set_leds(struct run_softc *sc, uint16_t which)
5326069f1a80SAndrew Thompson {
5327069f1a80SAndrew Thompson (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LEDS,
5328069f1a80SAndrew Thompson which | (sc->leds & 0x7f));
5329069f1a80SAndrew Thompson }
5330069f1a80SAndrew Thompson
5331069f1a80SAndrew Thompson static void
run_set_bssid(struct run_softc * sc,const uint8_t * bssid)5332069f1a80SAndrew Thompson run_set_bssid(struct run_softc *sc, const uint8_t *bssid)
5333069f1a80SAndrew Thompson {
5334069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_BSSID_DW0,
5335069f1a80SAndrew Thompson bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24);
5336069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_BSSID_DW1,
5337069f1a80SAndrew Thompson bssid[4] | bssid[5] << 8);
5338069f1a80SAndrew Thompson }
5339069f1a80SAndrew Thompson
5340069f1a80SAndrew Thompson static void
run_set_macaddr(struct run_softc * sc,const uint8_t * addr)5341069f1a80SAndrew Thompson run_set_macaddr(struct run_softc *sc, const uint8_t *addr)
5342069f1a80SAndrew Thompson {
5343069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_ADDR_DW0,
5344069f1a80SAndrew Thompson addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
5345069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_ADDR_DW1,
5346069f1a80SAndrew Thompson addr[4] | addr[5] << 8 | 0xff << 16);
5347069f1a80SAndrew Thompson }
5348069f1a80SAndrew Thompson
5349069f1a80SAndrew Thompson static void
run_updateslot(struct ieee80211com * ic)5350272f6adeSGleb Smirnoff run_updateslot(struct ieee80211com *ic)
5351069f1a80SAndrew Thompson {
5352272f6adeSGleb Smirnoff struct run_softc *sc = ic->ic_softc;
5353e7d14e9bSBernhard Schmidt uint32_t i;
5354e7d14e9bSBernhard Schmidt
5355e7d14e9bSBernhard Schmidt i = RUN_CMDQ_GET(&sc->cmdq_store);
5356109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i);
5357e7d14e9bSBernhard Schmidt sc->cmdq[i].func = run_updateslot_cb;
53587a79cebfSGleb Smirnoff sc->cmdq[i].arg0 = ic;
5359e7d14e9bSBernhard Schmidt ieee80211_runtask(ic, &sc->cmdq_task);
5360e7d14e9bSBernhard Schmidt
5361e7d14e9bSBernhard Schmidt return;
5362e7d14e9bSBernhard Schmidt }
5363e7d14e9bSBernhard Schmidt
5364e7d14e9bSBernhard Schmidt /* ARGSUSED */
5365e7d14e9bSBernhard Schmidt static void
run_updateslot_cb(void * arg)5366e7d14e9bSBernhard Schmidt run_updateslot_cb(void *arg)
5367e7d14e9bSBernhard Schmidt {
53687a79cebfSGleb Smirnoff struct ieee80211com *ic = arg;
5369d3fdd08cSAdrian Chadd struct run_softc *sc = ic->ic_softc;
5370069f1a80SAndrew Thompson uint32_t tmp;
5371069f1a80SAndrew Thompson
5372069f1a80SAndrew Thompson run_read(sc, RT2860_BKOFF_SLOT_CFG, &tmp);
5373069f1a80SAndrew Thompson tmp &= ~0xff;
5374bdfff33fSAndriy Voskoboinyk tmp |= IEEE80211_GET_SLOTTIME(ic);
5375069f1a80SAndrew Thompson run_write(sc, RT2860_BKOFF_SLOT_CFG, tmp);
5376069f1a80SAndrew Thompson }
5377069f1a80SAndrew Thompson
537885e7bb81SAndrew Thompson static void
run_update_mcast(struct ieee80211com * ic)5379272f6adeSGleb Smirnoff run_update_mcast(struct ieee80211com *ic)
538085e7bb81SAndrew Thompson {
538185e7bb81SAndrew Thompson }
538285e7bb81SAndrew Thompson
5383069f1a80SAndrew Thompson static int8_t
run_rssi2dbm(struct run_softc * sc,uint8_t rssi,uint8_t rxchain)5384069f1a80SAndrew Thompson run_rssi2dbm(struct run_softc *sc, uint8_t rssi, uint8_t rxchain)
5385069f1a80SAndrew Thompson {
53867a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
5387069f1a80SAndrew Thompson struct ieee80211_channel *c = ic->ic_curchan;
5388069f1a80SAndrew Thompson int delta;
5389069f1a80SAndrew Thompson
5390069f1a80SAndrew Thompson if (IEEE80211_IS_CHAN_5GHZ(c)) {
539135a24898SHans Petter Selasky u_int chan = ieee80211_chan2ieee(ic, c);
5392069f1a80SAndrew Thompson delta = sc->rssi_5ghz[rxchain];
5393069f1a80SAndrew Thompson
5394069f1a80SAndrew Thompson /* determine channel group */
5395069f1a80SAndrew Thompson if (chan <= 64)
5396069f1a80SAndrew Thompson delta -= sc->lna[1];
5397069f1a80SAndrew Thompson else if (chan <= 128)
5398069f1a80SAndrew Thompson delta -= sc->lna[2];
5399069f1a80SAndrew Thompson else
5400069f1a80SAndrew Thompson delta -= sc->lna[3];
5401069f1a80SAndrew Thompson } else
5402069f1a80SAndrew Thompson delta = sc->rssi_2ghz[rxchain] - sc->lna[0];
5403069f1a80SAndrew Thompson
540475c76159SAndrew Thompson return (-12 - delta - rssi);
5405069f1a80SAndrew Thompson }
5406069f1a80SAndrew Thompson
540764891211SKevin Lo static void
run_rt5390_bbp_init(struct run_softc * sc)540864891211SKevin Lo run_rt5390_bbp_init(struct run_softc *sc)
540964891211SKevin Lo {
541025cd0475SPedro F. Giffuni u_int i;
5411242dbae3SKevin Lo uint8_t bbp;
5412242dbae3SKevin Lo
5413242dbae3SKevin Lo /* Apply maximum likelihood detection for 2 stream case. */
5414242dbae3SKevin Lo run_bbp_read(sc, 105, &bbp);
5415242dbae3SKevin Lo if (sc->nrxchains > 1)
5416242dbae3SKevin Lo run_bbp_write(sc, 105, bbp | RT5390_MLD);
541764891211SKevin Lo
541864891211SKevin Lo /* Avoid data lost and CRC error. */
5419242dbae3SKevin Lo run_bbp_read(sc, 4, &bbp);
5420e0790ad8SKevin Lo run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL);
542164891211SKevin Lo
5422242dbae3SKevin Lo if (sc->mac_ver == 0x5592) {
5423242dbae3SKevin Lo for (i = 0; i < nitems(rt5592_def_bbp); i++) {
5424242dbae3SKevin Lo run_bbp_write(sc, rt5592_def_bbp[i].reg,
5425242dbae3SKevin Lo rt5592_def_bbp[i].val);
5426242dbae3SKevin Lo }
5427242dbae3SKevin Lo for (i = 0; i < nitems(rt5592_bbp_r196); i++) {
5428242dbae3SKevin Lo run_bbp_write(sc, 195, i + 0x80);
5429242dbae3SKevin Lo run_bbp_write(sc, 196, rt5592_bbp_r196[i]);
5430242dbae3SKevin Lo }
5431242dbae3SKevin Lo } else {
543264891211SKevin Lo for (i = 0; i < nitems(rt5390_def_bbp); i++) {
543364891211SKevin Lo run_bbp_write(sc, rt5390_def_bbp[i].reg,
543464891211SKevin Lo rt5390_def_bbp[i].val);
543564891211SKevin Lo }
5436242dbae3SKevin Lo }
543764891211SKevin Lo if (sc->mac_ver == 0x5392) {
543864891211SKevin Lo run_bbp_write(sc, 88, 0x90);
543964891211SKevin Lo run_bbp_write(sc, 95, 0x9a);
544064891211SKevin Lo run_bbp_write(sc, 98, 0x12);
544164891211SKevin Lo run_bbp_write(sc, 106, 0x12);
544264891211SKevin Lo run_bbp_write(sc, 134, 0xd0);
544364891211SKevin Lo run_bbp_write(sc, 135, 0xf6);
544464891211SKevin Lo run_bbp_write(sc, 148, 0x84);
544564891211SKevin Lo }
544664891211SKevin Lo
5447242dbae3SKevin Lo run_bbp_read(sc, 152, &bbp);
5448242dbae3SKevin Lo run_bbp_write(sc, 152, bbp | 0x80);
5449242dbae3SKevin Lo
5450242dbae3SKevin Lo /* Fix BBP254 for RT5592C. */
5451242dbae3SKevin Lo if (sc->mac_ver == 0x5592 && sc->mac_rev >= 0x0221) {
5452242dbae3SKevin Lo run_bbp_read(sc, 254, &bbp);
5453242dbae3SKevin Lo run_bbp_write(sc, 254, bbp | 0x80);
5454242dbae3SKevin Lo }
5455242dbae3SKevin Lo
545664891211SKevin Lo /* Disable hardware antenna diversity. */
545764891211SKevin Lo if (sc->mac_ver == 0x5390)
545864891211SKevin Lo run_bbp_write(sc, 154, 0);
5459242dbae3SKevin Lo
5460242dbae3SKevin Lo /* Initialize Rx CCK/OFDM frequency offset report. */
5461242dbae3SKevin Lo run_bbp_write(sc, 142, 1);
5462242dbae3SKevin Lo run_bbp_write(sc, 143, 57);
546364891211SKevin Lo }
546464891211SKevin Lo
5465069f1a80SAndrew Thompson static int
run_bbp_init(struct run_softc * sc)5466069f1a80SAndrew Thompson run_bbp_init(struct run_softc *sc)
5467069f1a80SAndrew Thompson {
5468069f1a80SAndrew Thompson int i, error, ntries;
5469069f1a80SAndrew Thompson uint8_t bbp0;
5470069f1a80SAndrew Thompson
5471069f1a80SAndrew Thompson /* wait for BBP to wake up */
5472069f1a80SAndrew Thompson for (ntries = 0; ntries < 20; ntries++) {
5473069f1a80SAndrew Thompson if ((error = run_bbp_read(sc, 0, &bbp0)) != 0)
5474069f1a80SAndrew Thompson return error;
5475069f1a80SAndrew Thompson if (bbp0 != 0 && bbp0 != 0xff)
5476069f1a80SAndrew Thompson break;
5477069f1a80SAndrew Thompson }
5478069f1a80SAndrew Thompson if (ntries == 20)
547975c76159SAndrew Thompson return (ETIMEDOUT);
5480069f1a80SAndrew Thompson
5481069f1a80SAndrew Thompson /* initialize BBP registers to default values */
548264891211SKevin Lo if (sc->mac_ver >= 0x5390)
548364891211SKevin Lo run_rt5390_bbp_init(sc);
548464891211SKevin Lo else {
548564891211SKevin Lo for (i = 0; i < nitems(rt2860_def_bbp); i++) {
5486069f1a80SAndrew Thompson run_bbp_write(sc, rt2860_def_bbp[i].reg,
5487069f1a80SAndrew Thompson rt2860_def_bbp[i].val);
5488069f1a80SAndrew Thompson }
548964891211SKevin Lo }
5490069f1a80SAndrew Thompson
54917a7e01caSKevin Lo if (sc->mac_ver == 0x3593) {
54927a7e01caSKevin Lo run_bbp_write(sc, 79, 0x13);
54937a7e01caSKevin Lo run_bbp_write(sc, 80, 0x05);
54947a7e01caSKevin Lo run_bbp_write(sc, 81, 0x33);
54957a7e01caSKevin Lo run_bbp_write(sc, 86, 0x46);
54967a7e01caSKevin Lo run_bbp_write(sc, 137, 0x0f);
54977a7e01caSKevin Lo }
54987a7e01caSKevin Lo
5499069f1a80SAndrew Thompson /* fix BBP84 for RT2860E */
55003707a5e9SAndrew Thompson if (sc->mac_ver == 0x2860 && sc->mac_rev != 0x0101)
5501069f1a80SAndrew Thompson run_bbp_write(sc, 84, 0x19);
5502069f1a80SAndrew Thompson
55037a7e01caSKevin Lo if (sc->mac_ver >= 0x3070 && (sc->mac_ver != 0x3593 &&
55047a7e01caSKevin Lo sc->mac_ver != 0x5592)) {
5505069f1a80SAndrew Thompson run_bbp_write(sc, 79, 0x13);
5506069f1a80SAndrew Thompson run_bbp_write(sc, 80, 0x05);
5507069f1a80SAndrew Thompson run_bbp_write(sc, 81, 0x33);
55083707a5e9SAndrew Thompson } else if (sc->mac_ver == 0x2860 && sc->mac_rev == 0x0100) {
5509069f1a80SAndrew Thompson run_bbp_write(sc, 69, 0x16);
5510069f1a80SAndrew Thompson run_bbp_write(sc, 73, 0x12);
5511069f1a80SAndrew Thompson }
551275c76159SAndrew Thompson return (0);
5513069f1a80SAndrew Thompson }
5514069f1a80SAndrew Thompson
5515069f1a80SAndrew Thompson static int
run_rt3070_rf_init(struct run_softc * sc)5516069f1a80SAndrew Thompson run_rt3070_rf_init(struct run_softc *sc)
5517069f1a80SAndrew Thompson {
5518069f1a80SAndrew Thompson uint32_t tmp;
5519d48971d0SKevin Lo uint8_t bbp4, mingain, rf, target;
552025cd0475SPedro F. Giffuni u_int i;
5521069f1a80SAndrew Thompson
5522069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 30, &rf);
5523069f1a80SAndrew Thompson /* toggle RF R30 bit 7 */
5524069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 30, rf | 0x80);
5525069f1a80SAndrew Thompson run_delay(sc, 10);
5526069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 30, rf & ~0x80);
5527069f1a80SAndrew Thompson
5528069f1a80SAndrew Thompson /* initialize RF registers to default value */
55293707a5e9SAndrew Thompson if (sc->mac_ver == 0x3572) {
553064891211SKevin Lo for (i = 0; i < nitems(rt3572_def_rf); i++) {
55313707a5e9SAndrew Thompson run_rt3070_rf_write(sc, rt3572_def_rf[i].reg,
55323707a5e9SAndrew Thompson rt3572_def_rf[i].val);
55333707a5e9SAndrew Thompson }
55343707a5e9SAndrew Thompson } else {
553564891211SKevin Lo for (i = 0; i < nitems(rt3070_def_rf); i++) {
5536069f1a80SAndrew Thompson run_rt3070_rf_write(sc, rt3070_def_rf[i].reg,
5537069f1a80SAndrew Thompson rt3070_def_rf[i].val);
5538069f1a80SAndrew Thompson }
55393707a5e9SAndrew Thompson }
55403707a5e9SAndrew Thompson
55416bf8d884SKevin Lo if (sc->mac_ver == 0x3070 && sc->mac_rev < 0x0201) {
55426bf8d884SKevin Lo /*
55436bf8d884SKevin Lo * Change voltage from 1.2V to 1.35V for RT3070.
55446bf8d884SKevin Lo * The DAC issue (RT3070_LDO_CFG0) has been fixed
55456bf8d884SKevin Lo * in RT3070(F).
55466bf8d884SKevin Lo */
5547069f1a80SAndrew Thompson run_read(sc, RT3070_LDO_CFG0, &tmp);
5548069f1a80SAndrew Thompson tmp = (tmp & ~0x0f000000) | 0x0d000000;
5549069f1a80SAndrew Thompson run_write(sc, RT3070_LDO_CFG0, tmp);
5550069f1a80SAndrew Thompson
55513707a5e9SAndrew Thompson } else if (sc->mac_ver == 0x3071) {
5552069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 6, &rf);
5553069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 6, rf | 0x40);
5554069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 31, 0x14);
5555069f1a80SAndrew Thompson
5556069f1a80SAndrew Thompson run_read(sc, RT3070_LDO_CFG0, &tmp);
5557069f1a80SAndrew Thompson tmp &= ~0x1f000000;
55583707a5e9SAndrew Thompson if (sc->mac_rev < 0x0211)
55593707a5e9SAndrew Thompson tmp |= 0x0d000000; /* 1.3V */
5560069f1a80SAndrew Thompson else
55613707a5e9SAndrew Thompson tmp |= 0x01000000; /* 1.2V */
5562069f1a80SAndrew Thompson run_write(sc, RT3070_LDO_CFG0, tmp);
5563069f1a80SAndrew Thompson
5564069f1a80SAndrew Thompson /* patch LNA_PE_G1 */
5565069f1a80SAndrew Thompson run_read(sc, RT3070_GPIO_SWITCH, &tmp);
5566069f1a80SAndrew Thompson run_write(sc, RT3070_GPIO_SWITCH, tmp & ~0x20);
556785e7bb81SAndrew Thompson
55683707a5e9SAndrew Thompson } else if (sc->mac_ver == 0x3572) {
55693707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 6, &rf);
55703707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 6, rf | 0x40);
55713707a5e9SAndrew Thompson
55723707a5e9SAndrew Thompson /* increase voltage from 1.2V to 1.35V */
5573069f1a80SAndrew Thompson run_read(sc, RT3070_LDO_CFG0, &tmp);
5574069f1a80SAndrew Thompson tmp = (tmp & ~0x1f000000) | 0x0d000000;
5575069f1a80SAndrew Thompson run_write(sc, RT3070_LDO_CFG0, tmp);
5576069f1a80SAndrew Thompson
557785e7bb81SAndrew Thompson if (sc->mac_rev < 0x0211 || !sc->patch_dac) {
5578069f1a80SAndrew Thompson run_delay(sc, 1); /* wait for 1msec */
55793707a5e9SAndrew Thompson /* decrease voltage back to 1.2V */
5580069f1a80SAndrew Thompson tmp = (tmp & ~0x1f000000) | 0x01000000;
5581069f1a80SAndrew Thompson run_write(sc, RT3070_LDO_CFG0, tmp);
5582069f1a80SAndrew Thompson }
5583069f1a80SAndrew Thompson }
5584069f1a80SAndrew Thompson
5585069f1a80SAndrew Thompson /* select 20MHz bandwidth */
5586069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 31, &rf);
5587069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 31, rf & ~0x20);
5588069f1a80SAndrew Thompson
5589069f1a80SAndrew Thompson /* calibrate filter for 20MHz bandwidth */
5590069f1a80SAndrew Thompson sc->rf24_20mhz = 0x1f; /* default value */
55913707a5e9SAndrew Thompson target = (sc->mac_ver < 0x3071) ? 0x16 : 0x13;
55923707a5e9SAndrew Thompson run_rt3070_filter_calib(sc, 0x07, target, &sc->rf24_20mhz);
5593069f1a80SAndrew Thompson
5594069f1a80SAndrew Thompson /* select 40MHz bandwidth */
5595069f1a80SAndrew Thompson run_bbp_read(sc, 4, &bbp4);
55966bf8d884SKevin Lo run_bbp_write(sc, 4, (bbp4 & ~0x18) | 0x10);
55973707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 31, &rf);
55983707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 31, rf | 0x20);
5599069f1a80SAndrew Thompson
5600069f1a80SAndrew Thompson /* calibrate filter for 40MHz bandwidth */
5601069f1a80SAndrew Thompson sc->rf24_40mhz = 0x2f; /* default value */
56023707a5e9SAndrew Thompson target = (sc->mac_ver < 0x3071) ? 0x19 : 0x15;
56033707a5e9SAndrew Thompson run_rt3070_filter_calib(sc, 0x27, target, &sc->rf24_40mhz);
5604069f1a80SAndrew Thompson
5605069f1a80SAndrew Thompson /* go back to 20MHz bandwidth */
5606069f1a80SAndrew Thompson run_bbp_read(sc, 4, &bbp4);
5607069f1a80SAndrew Thompson run_bbp_write(sc, 4, bbp4 & ~0x18);
5608069f1a80SAndrew Thompson
56093707a5e9SAndrew Thompson if (sc->mac_ver == 0x3572) {
56103707a5e9SAndrew Thompson /* save default BBP registers 25 and 26 values */
56113707a5e9SAndrew Thompson run_bbp_read(sc, 25, &sc->bbp25);
56123707a5e9SAndrew Thompson run_bbp_read(sc, 26, &sc->bbp26);
56136bf8d884SKevin Lo } else if (sc->mac_rev < 0x0201 || sc->mac_rev < 0x0211)
5614069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 27, 0x03);
5615069f1a80SAndrew Thompson
5616069f1a80SAndrew Thompson run_read(sc, RT3070_OPT_14, &tmp);
5617069f1a80SAndrew Thompson run_write(sc, RT3070_OPT_14, tmp | 1);
5618069f1a80SAndrew Thompson
56193707a5e9SAndrew Thompson if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) {
56203707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 17, &rf);
56213707a5e9SAndrew Thompson rf &= ~RT3070_TX_LO1;
56223707a5e9SAndrew Thompson if ((sc->mac_ver == 0x3070 ||
56233707a5e9SAndrew Thompson (sc->mac_ver == 0x3071 && sc->mac_rev >= 0x0211)) &&
56243707a5e9SAndrew Thompson !sc->ext_2ghz_lna)
56253707a5e9SAndrew Thompson rf |= 0x20; /* fix for long range Rx issue */
5626d48971d0SKevin Lo mingain = (sc->mac_ver == 0x3070) ? 1 : 2;
5627d48971d0SKevin Lo if (sc->txmixgain_2ghz >= mingain)
56283707a5e9SAndrew Thompson rf = (rf & ~0x7) | sc->txmixgain_2ghz;
56293707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 17, rf);
56303707a5e9SAndrew Thompson }
56313707a5e9SAndrew Thompson
563215cb19cdSKevin Lo if (sc->mac_ver == 0x3071) {
5633069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 1, &rf);
5634069f1a80SAndrew Thompson rf &= ~(RT3070_RX0_PD | RT3070_TX0_PD);
5635069f1a80SAndrew Thompson rf |= RT3070_RF_BLOCK | RT3070_RX1_PD | RT3070_TX1_PD;
5636069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 1, rf);
5637069f1a80SAndrew Thompson
5638069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 15, &rf);
5639069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 15, rf & ~RT3070_TX_LO2);
5640069f1a80SAndrew Thompson
5641069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 20, &rf);
5642069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 20, rf & ~RT3070_RX_LO1);
5643069f1a80SAndrew Thompson
5644069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 21, &rf);
5645069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 21, rf & ~RT3070_RX_LO2);
56463707a5e9SAndrew Thompson }
5647069f1a80SAndrew Thompson
56483707a5e9SAndrew Thompson if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) {
56493707a5e9SAndrew Thompson /* fix Tx to Rx IQ glitch by raising RF voltage */
5650069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 27, &rf);
5651069f1a80SAndrew Thompson rf &= ~0x77;
56523707a5e9SAndrew Thompson if (sc->mac_rev < 0x0211)
5653069f1a80SAndrew Thompson rf |= 0x03;
5654069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 27, rf);
5655069f1a80SAndrew Thompson }
565675c76159SAndrew Thompson return (0);
5657069f1a80SAndrew Thompson }
5658069f1a80SAndrew Thompson
565964891211SKevin Lo static void
run_rt3593_rf_init(struct run_softc * sc)56607a7e01caSKevin Lo run_rt3593_rf_init(struct run_softc *sc)
56617a7e01caSKevin Lo {
56627a7e01caSKevin Lo uint32_t tmp;
56637a7e01caSKevin Lo uint8_t rf;
566425cd0475SPedro F. Giffuni u_int i;
56657a7e01caSKevin Lo
56667a7e01caSKevin Lo /* Disable the GPIO bits 4 and 7 for LNA PE control. */
56677a7e01caSKevin Lo run_read(sc, RT3070_GPIO_SWITCH, &tmp);
56687a7e01caSKevin Lo tmp &= ~(1 << 4 | 1 << 7);
56697a7e01caSKevin Lo run_write(sc, RT3070_GPIO_SWITCH, tmp);
56707a7e01caSKevin Lo
56717a7e01caSKevin Lo /* Initialize RF registers to default value. */
56727a7e01caSKevin Lo for (i = 0; i < nitems(rt3593_def_rf); i++) {
56737a7e01caSKevin Lo run_rt3070_rf_write(sc, rt3593_def_rf[i].reg,
56747a7e01caSKevin Lo rt3593_def_rf[i].val);
56757a7e01caSKevin Lo }
56767a7e01caSKevin Lo
56777a7e01caSKevin Lo /* Toggle RF R2 to initiate calibration. */
56787a7e01caSKevin Lo run_rt3070_rf_write(sc, 2, RT5390_RESCAL);
56797a7e01caSKevin Lo
56807a7e01caSKevin Lo /* Initialize RF frequency offset. */
56817a7e01caSKevin Lo run_adjust_freq_offset(sc);
56827a7e01caSKevin Lo
56837a7e01caSKevin Lo run_rt3070_rf_read(sc, 18, &rf);
56847a7e01caSKevin Lo run_rt3070_rf_write(sc, 18, rf | RT3593_AUTOTUNE_BYPASS);
56857a7e01caSKevin Lo
56867a7e01caSKevin Lo /*
56877a7e01caSKevin Lo * Increase voltage from 1.2V to 1.35V, wait for 1 msec to
56887a7e01caSKevin Lo * decrease voltage back to 1.2V.
56897a7e01caSKevin Lo */
56907a7e01caSKevin Lo run_read(sc, RT3070_LDO_CFG0, &tmp);
56917a7e01caSKevin Lo tmp = (tmp & ~0x1f000000) | 0x0d000000;
56927a7e01caSKevin Lo run_write(sc, RT3070_LDO_CFG0, tmp);
56937a7e01caSKevin Lo run_delay(sc, 1);
56947a7e01caSKevin Lo tmp = (tmp & ~0x1f000000) | 0x01000000;
56957a7e01caSKevin Lo run_write(sc, RT3070_LDO_CFG0, tmp);
56967a7e01caSKevin Lo
56977a7e01caSKevin Lo sc->rf24_20mhz = 0x1f;
56987a7e01caSKevin Lo sc->rf24_40mhz = 0x2f;
56997a7e01caSKevin Lo
57007a7e01caSKevin Lo /* Save default BBP registers 25 and 26 values. */
57017a7e01caSKevin Lo run_bbp_read(sc, 25, &sc->bbp25);
57027a7e01caSKevin Lo run_bbp_read(sc, 26, &sc->bbp26);
57037a7e01caSKevin Lo
57047a7e01caSKevin Lo run_read(sc, RT3070_OPT_14, &tmp);
57057a7e01caSKevin Lo run_write(sc, RT3070_OPT_14, tmp | 1);
57067a7e01caSKevin Lo }
57077a7e01caSKevin Lo
57087a7e01caSKevin Lo static void
run_rt5390_rf_init(struct run_softc * sc)570964891211SKevin Lo run_rt5390_rf_init(struct run_softc *sc)
571064891211SKevin Lo {
571164891211SKevin Lo uint32_t tmp;
571264891211SKevin Lo uint8_t rf;
571325cd0475SPedro F. Giffuni u_int i;
571464891211SKevin Lo
571564891211SKevin Lo /* Toggle RF R2 to initiate calibration. */
5716b8161ff3SKevin Lo if (sc->mac_ver == 0x5390) {
5717b8161ff3SKevin Lo run_rt3070_rf_read(sc, 2, &rf);
5718e0790ad8SKevin Lo run_rt3070_rf_write(sc, 2, rf | RT5390_RESCAL);
571964891211SKevin Lo run_delay(sc, 10);
5720e0790ad8SKevin Lo run_rt3070_rf_write(sc, 2, rf & ~RT5390_RESCAL);
5721b8161ff3SKevin Lo } else {
5722e0790ad8SKevin Lo run_rt3070_rf_write(sc, 2, RT5390_RESCAL);
5723b8161ff3SKevin Lo run_delay(sc, 10);
572464891211SKevin Lo }
572564891211SKevin Lo
572664891211SKevin Lo /* Initialize RF registers to default value. */
5727242dbae3SKevin Lo if (sc->mac_ver == 0x5592) {
5728242dbae3SKevin Lo for (i = 0; i < nitems(rt5592_def_rf); i++) {
5729242dbae3SKevin Lo run_rt3070_rf_write(sc, rt5592_def_rf[i].reg,
5730242dbae3SKevin Lo rt5592_def_rf[i].val);
5731242dbae3SKevin Lo }
5732242dbae3SKevin Lo /* Initialize RF frequency offset. */
5733242dbae3SKevin Lo run_adjust_freq_offset(sc);
5734242dbae3SKevin Lo } else if (sc->mac_ver == 0x5392) {
573564891211SKevin Lo for (i = 0; i < nitems(rt5392_def_rf); i++) {
573664891211SKevin Lo run_rt3070_rf_write(sc, rt5392_def_rf[i].reg,
573764891211SKevin Lo rt5392_def_rf[i].val);
573864891211SKevin Lo }
573964891211SKevin Lo if (sc->mac_rev >= 0x0223) {
574064891211SKevin Lo run_rt3070_rf_write(sc, 23, 0x0f);
574164891211SKevin Lo run_rt3070_rf_write(sc, 24, 0x3e);
574264891211SKevin Lo run_rt3070_rf_write(sc, 51, 0x32);
574364891211SKevin Lo run_rt3070_rf_write(sc, 53, 0x22);
574464891211SKevin Lo run_rt3070_rf_write(sc, 56, 0xc1);
574564891211SKevin Lo run_rt3070_rf_write(sc, 59, 0x0f);
574664891211SKevin Lo }
574764891211SKevin Lo } else {
574864891211SKevin Lo for (i = 0; i < nitems(rt5390_def_rf); i++) {
574964891211SKevin Lo run_rt3070_rf_write(sc, rt5390_def_rf[i].reg,
575064891211SKevin Lo rt5390_def_rf[i].val);
575164891211SKevin Lo }
575264891211SKevin Lo if (sc->mac_rev >= 0x0502) {
575364891211SKevin Lo run_rt3070_rf_write(sc, 6, 0xe0);
575464891211SKevin Lo run_rt3070_rf_write(sc, 25, 0x80);
575564891211SKevin Lo run_rt3070_rf_write(sc, 46, 0x73);
575664891211SKevin Lo run_rt3070_rf_write(sc, 53, 0x00);
575764891211SKevin Lo run_rt3070_rf_write(sc, 56, 0x42);
575864891211SKevin Lo run_rt3070_rf_write(sc, 61, 0xd1);
575964891211SKevin Lo }
576064891211SKevin Lo }
576164891211SKevin Lo
576264891211SKevin Lo sc->rf24_20mhz = 0x1f; /* default value */
5763242dbae3SKevin Lo sc->rf24_40mhz = (sc->mac_ver == 0x5592) ? 0 : 0x2f;
576464891211SKevin Lo
576564891211SKevin Lo if (sc->mac_rev < 0x0211)
576664891211SKevin Lo run_rt3070_rf_write(sc, 27, 0x3);
576764891211SKevin Lo
576864891211SKevin Lo run_read(sc, RT3070_OPT_14, &tmp);
576964891211SKevin Lo run_write(sc, RT3070_OPT_14, tmp | 1);
577064891211SKevin Lo }
577164891211SKevin Lo
5772069f1a80SAndrew Thompson static int
run_rt3070_filter_calib(struct run_softc * sc,uint8_t init,uint8_t target,uint8_t * val)5773069f1a80SAndrew Thompson run_rt3070_filter_calib(struct run_softc *sc, uint8_t init, uint8_t target,
5774069f1a80SAndrew Thompson uint8_t *val)
5775069f1a80SAndrew Thompson {
5776069f1a80SAndrew Thompson uint8_t rf22, rf24;
5777069f1a80SAndrew Thompson uint8_t bbp55_pb, bbp55_sb, delta;
5778069f1a80SAndrew Thompson int ntries;
5779069f1a80SAndrew Thompson
5780069f1a80SAndrew Thompson /* program filter */
57813707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 24, &rf24);
57823707a5e9SAndrew Thompson rf24 = (rf24 & 0xc0) | init; /* initial filter value */
5783069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 24, rf24);
5784069f1a80SAndrew Thompson
5785069f1a80SAndrew Thompson /* enable baseband loopback mode */
5786069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 22, &rf22);
5787069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 22, rf22 | 0x01);
5788069f1a80SAndrew Thompson
5789069f1a80SAndrew Thompson /* set power and frequency of passband test tone */
5790069f1a80SAndrew Thompson run_bbp_write(sc, 24, 0x00);
5791069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
5792069f1a80SAndrew Thompson /* transmit test tone */
5793069f1a80SAndrew Thompson run_bbp_write(sc, 25, 0x90);
5794069f1a80SAndrew Thompson run_delay(sc, 10);
5795069f1a80SAndrew Thompson /* read received power */
5796069f1a80SAndrew Thompson run_bbp_read(sc, 55, &bbp55_pb);
5797069f1a80SAndrew Thompson if (bbp55_pb != 0)
5798069f1a80SAndrew Thompson break;
5799069f1a80SAndrew Thompson }
5800069f1a80SAndrew Thompson if (ntries == 100)
580164891211SKevin Lo return (ETIMEDOUT);
5802069f1a80SAndrew Thompson
5803069f1a80SAndrew Thompson /* set power and frequency of stopband test tone */
5804069f1a80SAndrew Thompson run_bbp_write(sc, 24, 0x06);
5805069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
5806069f1a80SAndrew Thompson /* transmit test tone */
5807069f1a80SAndrew Thompson run_bbp_write(sc, 25, 0x90);
5808069f1a80SAndrew Thompson run_delay(sc, 10);
5809069f1a80SAndrew Thompson /* read received power */
5810069f1a80SAndrew Thompson run_bbp_read(sc, 55, &bbp55_sb);
5811069f1a80SAndrew Thompson
5812069f1a80SAndrew Thompson delta = bbp55_pb - bbp55_sb;
5813069f1a80SAndrew Thompson if (delta > target)
5814069f1a80SAndrew Thompson break;
5815069f1a80SAndrew Thompson
5816069f1a80SAndrew Thompson /* reprogram filter */
5817069f1a80SAndrew Thompson rf24++;
5818069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 24, rf24);
5819069f1a80SAndrew Thompson }
5820069f1a80SAndrew Thompson if (ntries < 100) {
5821069f1a80SAndrew Thompson if (rf24 != init)
5822069f1a80SAndrew Thompson rf24--; /* backtrack */
5823069f1a80SAndrew Thompson *val = rf24;
5824069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 24, rf24);
5825069f1a80SAndrew Thompson }
5826069f1a80SAndrew Thompson
5827069f1a80SAndrew Thompson /* restore initial state */
5828069f1a80SAndrew Thompson run_bbp_write(sc, 24, 0x00);
5829069f1a80SAndrew Thompson
5830069f1a80SAndrew Thompson /* disable baseband loopback mode */
5831069f1a80SAndrew Thompson run_rt3070_rf_read(sc, 22, &rf22);
5832069f1a80SAndrew Thompson run_rt3070_rf_write(sc, 22, rf22 & ~0x01);
5833069f1a80SAndrew Thompson
583475c76159SAndrew Thompson return (0);
5835069f1a80SAndrew Thompson }
5836069f1a80SAndrew Thompson
58373707a5e9SAndrew Thompson static void
run_rt3070_rf_setup(struct run_softc * sc)58383707a5e9SAndrew Thompson run_rt3070_rf_setup(struct run_softc *sc)
58393707a5e9SAndrew Thompson {
58403707a5e9SAndrew Thompson uint8_t bbp, rf;
58413707a5e9SAndrew Thompson int i;
58423707a5e9SAndrew Thompson
58437a7e01caSKevin Lo if (sc->mac_ver == 0x3572) {
58443707a5e9SAndrew Thompson /* enable DC filter */
58453707a5e9SAndrew Thompson if (sc->mac_rev >= 0x0201)
58463707a5e9SAndrew Thompson run_bbp_write(sc, 103, 0xc0);
58473707a5e9SAndrew Thompson
58483707a5e9SAndrew Thompson run_bbp_read(sc, 138, &bbp);
58493707a5e9SAndrew Thompson if (sc->ntxchains == 1)
58503707a5e9SAndrew Thompson bbp |= 0x20; /* turn off DAC1 */
58513707a5e9SAndrew Thompson if (sc->nrxchains == 1)
58523707a5e9SAndrew Thompson bbp &= ~0x02; /* turn off ADC1 */
58533707a5e9SAndrew Thompson run_bbp_write(sc, 138, bbp);
58543707a5e9SAndrew Thompson
58553707a5e9SAndrew Thompson if (sc->mac_rev >= 0x0211) {
58563707a5e9SAndrew Thompson /* improve power consumption */
58573707a5e9SAndrew Thompson run_bbp_read(sc, 31, &bbp);
58583707a5e9SAndrew Thompson run_bbp_write(sc, 31, bbp & ~0x03);
58593707a5e9SAndrew Thompson }
58603707a5e9SAndrew Thompson
58613707a5e9SAndrew Thompson run_rt3070_rf_read(sc, 16, &rf);
58623707a5e9SAndrew Thompson rf = (rf & ~0x07) | sc->txmixgain_2ghz;
58633707a5e9SAndrew Thompson run_rt3070_rf_write(sc, 16, rf);
58643707a5e9SAndrew Thompson
58653707a5e9SAndrew Thompson } else if (sc->mac_ver == 0x3071) {
5866c513ecfdSKevin Lo if (sc->mac_rev >= 0x0211) {
58673707a5e9SAndrew Thompson /* enable DC filter */
58683707a5e9SAndrew Thompson run_bbp_write(sc, 103, 0xc0);
58693707a5e9SAndrew Thompson
5870c513ecfdSKevin Lo /* improve power consumption */
5871c513ecfdSKevin Lo run_bbp_read(sc, 31, &bbp);
5872c513ecfdSKevin Lo run_bbp_write(sc, 31, bbp & ~0x03);
5873c513ecfdSKevin Lo }
5874c513ecfdSKevin Lo
58753707a5e9SAndrew Thompson run_bbp_read(sc, 138, &bbp);
58763707a5e9SAndrew Thompson if (sc->ntxchains == 1)
58773707a5e9SAndrew Thompson bbp |= 0x20; /* turn off DAC1 */
58783707a5e9SAndrew Thompson if (sc->nrxchains == 1)
58793707a5e9SAndrew Thompson bbp &= ~0x02; /* turn off ADC1 */
58803707a5e9SAndrew Thompson run_bbp_write(sc, 138, bbp);
58813707a5e9SAndrew Thompson
58823707a5e9SAndrew Thompson run_write(sc, RT2860_TX_SW_CFG1, 0);
58833707a5e9SAndrew Thompson if (sc->mac_rev < 0x0211) {
58843707a5e9SAndrew Thompson run_write(sc, RT2860_TX_SW_CFG2,
58853707a5e9SAndrew Thompson sc->patch_dac ? 0x2c : 0x0f);
58863707a5e9SAndrew Thompson } else
58873707a5e9SAndrew Thompson run_write(sc, RT2860_TX_SW_CFG2, 0);
58883707a5e9SAndrew Thompson
58893707a5e9SAndrew Thompson } else if (sc->mac_ver == 0x3070) {
58903707a5e9SAndrew Thompson if (sc->mac_rev >= 0x0201) {
58913707a5e9SAndrew Thompson /* enable DC filter */
58923707a5e9SAndrew Thompson run_bbp_write(sc, 103, 0xc0);
58933707a5e9SAndrew Thompson
58943707a5e9SAndrew Thompson /* improve power consumption */
58953707a5e9SAndrew Thompson run_bbp_read(sc, 31, &bbp);
58963707a5e9SAndrew Thompson run_bbp_write(sc, 31, bbp & ~0x03);
58973707a5e9SAndrew Thompson }
58983707a5e9SAndrew Thompson
5899d4d0412bSKevin Lo if (sc->mac_rev < 0x0201) {
59003707a5e9SAndrew Thompson run_write(sc, RT2860_TX_SW_CFG1, 0);
59013707a5e9SAndrew Thompson run_write(sc, RT2860_TX_SW_CFG2, 0x2c);
59023707a5e9SAndrew Thompson } else
59033707a5e9SAndrew Thompson run_write(sc, RT2860_TX_SW_CFG2, 0);
59043707a5e9SAndrew Thompson }
59053707a5e9SAndrew Thompson
59063707a5e9SAndrew Thompson /* initialize RF registers from ROM for >=RT3071*/
59077a7e01caSKevin Lo if (sc->mac_ver >= 0x3071) {
59083707a5e9SAndrew Thompson for (i = 0; i < 10; i++) {
59093707a5e9SAndrew Thompson if (sc->rf[i].reg == 0 || sc->rf[i].reg == 0xff)
59103707a5e9SAndrew Thompson continue;
59113707a5e9SAndrew Thompson run_rt3070_rf_write(sc, sc->rf[i].reg, sc->rf[i].val);
59123707a5e9SAndrew Thompson }
59133707a5e9SAndrew Thompson }
59143707a5e9SAndrew Thompson }
59153707a5e9SAndrew Thompson
59167a7e01caSKevin Lo static void
run_rt3593_rf_setup(struct run_softc * sc)59177a7e01caSKevin Lo run_rt3593_rf_setup(struct run_softc *sc)
59187a7e01caSKevin Lo {
59197a7e01caSKevin Lo uint8_t bbp, rf;
59207a7e01caSKevin Lo
59217a7e01caSKevin Lo if (sc->mac_rev >= 0x0211) {
59227a7e01caSKevin Lo /* Enable DC filter. */
59237a7e01caSKevin Lo run_bbp_write(sc, 103, 0xc0);
59247a7e01caSKevin Lo }
59257a7e01caSKevin Lo run_write(sc, RT2860_TX_SW_CFG1, 0);
59267a7e01caSKevin Lo if (sc->mac_rev < 0x0211) {
59277a7e01caSKevin Lo run_write(sc, RT2860_TX_SW_CFG2,
59287a7e01caSKevin Lo sc->patch_dac ? 0x2c : 0x0f);
59297a7e01caSKevin Lo } else
59307a7e01caSKevin Lo run_write(sc, RT2860_TX_SW_CFG2, 0);
59317a7e01caSKevin Lo
59327a7e01caSKevin Lo run_rt3070_rf_read(sc, 50, &rf);
59337a7e01caSKevin Lo run_rt3070_rf_write(sc, 50, rf & ~RT3593_TX_LO2);
59347a7e01caSKevin Lo
59357a7e01caSKevin Lo run_rt3070_rf_read(sc, 51, &rf);
59367a7e01caSKevin Lo rf = (rf & ~(RT3593_TX_LO1 | 0x0c)) |
59377a7e01caSKevin Lo ((sc->txmixgain_2ghz & 0x07) << 2);
59387a7e01caSKevin Lo run_rt3070_rf_write(sc, 51, rf);
59397a7e01caSKevin Lo
59407a7e01caSKevin Lo run_rt3070_rf_read(sc, 38, &rf);
59417a7e01caSKevin Lo run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1);
59427a7e01caSKevin Lo
59437a7e01caSKevin Lo run_rt3070_rf_read(sc, 39, &rf);
59447a7e01caSKevin Lo run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2);
59457a7e01caSKevin Lo
59467a7e01caSKevin Lo run_rt3070_rf_read(sc, 1, &rf);
59477a7e01caSKevin Lo run_rt3070_rf_write(sc, 1, rf & ~(RT3070_RF_BLOCK | RT3070_PLL_PD));
59487a7e01caSKevin Lo
59497a7e01caSKevin Lo run_rt3070_rf_read(sc, 30, &rf);
59507a7e01caSKevin Lo rf = (rf & ~0x18) | 0x10;
59517a7e01caSKevin Lo run_rt3070_rf_write(sc, 30, rf);
59527a7e01caSKevin Lo
59537a7e01caSKevin Lo /* Apply maximum likelihood detection for 2 stream case. */
59547a7e01caSKevin Lo run_bbp_read(sc, 105, &bbp);
59557a7e01caSKevin Lo if (sc->nrxchains > 1)
59567a7e01caSKevin Lo run_bbp_write(sc, 105, bbp | RT5390_MLD);
59577a7e01caSKevin Lo
59587a7e01caSKevin Lo /* Avoid data lost and CRC error. */
59597a7e01caSKevin Lo run_bbp_read(sc, 4, &bbp);
59607a7e01caSKevin Lo run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL);
59617a7e01caSKevin Lo
59627a7e01caSKevin Lo run_bbp_write(sc, 92, 0x02);
59637a7e01caSKevin Lo run_bbp_write(sc, 82, 0x82);
59647a7e01caSKevin Lo run_bbp_write(sc, 106, 0x05);
59657a7e01caSKevin Lo run_bbp_write(sc, 104, 0x92);
59667a7e01caSKevin Lo run_bbp_write(sc, 88, 0x90);
59677a7e01caSKevin Lo run_bbp_write(sc, 148, 0xc8);
59687a7e01caSKevin Lo run_bbp_write(sc, 47, 0x48);
59697a7e01caSKevin Lo run_bbp_write(sc, 120, 0x50);
59707a7e01caSKevin Lo
59717a7e01caSKevin Lo run_bbp_write(sc, 163, 0x9d);
59727a7e01caSKevin Lo
59737a7e01caSKevin Lo /* SNR mapping. */
59747a7e01caSKevin Lo run_bbp_write(sc, 142, 0x06);
59757a7e01caSKevin Lo run_bbp_write(sc, 143, 0xa0);
59767a7e01caSKevin Lo run_bbp_write(sc, 142, 0x07);
59777a7e01caSKevin Lo run_bbp_write(sc, 143, 0xa1);
59787a7e01caSKevin Lo run_bbp_write(sc, 142, 0x08);
59797a7e01caSKevin Lo run_bbp_write(sc, 143, 0xa2);
59807a7e01caSKevin Lo
59817a7e01caSKevin Lo run_bbp_write(sc, 31, 0x08);
59827a7e01caSKevin Lo run_bbp_write(sc, 68, 0x0b);
59837a7e01caSKevin Lo run_bbp_write(sc, 105, 0x04);
59847a7e01caSKevin Lo }
59857a7e01caSKevin Lo
59867a7e01caSKevin Lo static void
run_rt5390_rf_setup(struct run_softc * sc)59877a7e01caSKevin Lo run_rt5390_rf_setup(struct run_softc *sc)
59887a7e01caSKevin Lo {
59897a7e01caSKevin Lo uint8_t bbp, rf;
59907a7e01caSKevin Lo
59917a7e01caSKevin Lo if (sc->mac_rev >= 0x0211) {
59927a7e01caSKevin Lo /* Enable DC filter. */
59937a7e01caSKevin Lo run_bbp_write(sc, 103, 0xc0);
59947a7e01caSKevin Lo
59957a7e01caSKevin Lo if (sc->mac_ver != 0x5592) {
59967a7e01caSKevin Lo /* Improve power consumption. */
59977a7e01caSKevin Lo run_bbp_read(sc, 31, &bbp);
59987a7e01caSKevin Lo run_bbp_write(sc, 31, bbp & ~0x03);
59997a7e01caSKevin Lo }
60007a7e01caSKevin Lo }
60017a7e01caSKevin Lo
60027a7e01caSKevin Lo run_bbp_read(sc, 138, &bbp);
60037a7e01caSKevin Lo if (sc->ntxchains == 1)
60047a7e01caSKevin Lo bbp |= 0x20; /* turn off DAC1 */
60057a7e01caSKevin Lo if (sc->nrxchains == 1)
60067a7e01caSKevin Lo bbp &= ~0x02; /* turn off ADC1 */
60077a7e01caSKevin Lo run_bbp_write(sc, 138, bbp);
60087a7e01caSKevin Lo
60097a7e01caSKevin Lo run_rt3070_rf_read(sc, 38, &rf);
60107a7e01caSKevin Lo run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1);
60117a7e01caSKevin Lo
60127a7e01caSKevin Lo run_rt3070_rf_read(sc, 39, &rf);
60137a7e01caSKevin Lo run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2);
60147a7e01caSKevin Lo
60157a7e01caSKevin Lo /* Avoid data lost and CRC error. */
60167a7e01caSKevin Lo run_bbp_read(sc, 4, &bbp);
60177a7e01caSKevin Lo run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL);
60187a7e01caSKevin Lo
60197a7e01caSKevin Lo run_rt3070_rf_read(sc, 30, &rf);
60207a7e01caSKevin Lo rf = (rf & ~0x18) | 0x10;
60217a7e01caSKevin Lo run_rt3070_rf_write(sc, 30, rf);
60227a7e01caSKevin Lo
60237a7e01caSKevin Lo if (sc->mac_ver != 0x5592) {
60247a7e01caSKevin Lo run_write(sc, RT2860_TX_SW_CFG1, 0);
60257a7e01caSKevin Lo if (sc->mac_rev < 0x0211) {
60267a7e01caSKevin Lo run_write(sc, RT2860_TX_SW_CFG2,
60277a7e01caSKevin Lo sc->patch_dac ? 0x2c : 0x0f);
60287a7e01caSKevin Lo } else
60297a7e01caSKevin Lo run_write(sc, RT2860_TX_SW_CFG2, 0);
60307a7e01caSKevin Lo }
60317a7e01caSKevin Lo }
60327a7e01caSKevin Lo
6033069f1a80SAndrew Thompson static int
run_txrx_enable(struct run_softc * sc)6034069f1a80SAndrew Thompson run_txrx_enable(struct run_softc *sc)
6035069f1a80SAndrew Thompson {
60367a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
6037069f1a80SAndrew Thompson uint32_t tmp;
6038069f1a80SAndrew Thompson int error, ntries;
6039069f1a80SAndrew Thompson
6040069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN);
6041069f1a80SAndrew Thompson for (ntries = 0; ntries < 200; ntries++) {
6042069f1a80SAndrew Thompson if ((error = run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp)) != 0)
604364891211SKevin Lo return (error);
6044069f1a80SAndrew Thompson if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0)
6045069f1a80SAndrew Thompson break;
6046069f1a80SAndrew Thompson run_delay(sc, 50);
6047069f1a80SAndrew Thompson }
6048069f1a80SAndrew Thompson if (ntries == 200)
604964891211SKevin Lo return (ETIMEDOUT);
6050069f1a80SAndrew Thompson
6051069f1a80SAndrew Thompson run_delay(sc, 50);
6052069f1a80SAndrew Thompson
6053069f1a80SAndrew Thompson tmp |= RT2860_RX_DMA_EN | RT2860_TX_DMA_EN | RT2860_TX_WB_DDONE;
6054069f1a80SAndrew Thompson run_write(sc, RT2860_WPDMA_GLO_CFG, tmp);
6055069f1a80SAndrew Thompson
6056069f1a80SAndrew Thompson /* enable Rx bulk aggregation (set timeout and limit) */
6057069f1a80SAndrew Thompson tmp = RT2860_USB_TX_EN | RT2860_USB_RX_EN | RT2860_USB_RX_AGG_EN |
6058069f1a80SAndrew Thompson RT2860_USB_RX_AGG_TO(128) | RT2860_USB_RX_AGG_LMT(2);
6059069f1a80SAndrew Thompson run_write(sc, RT2860_USB_DMA_CFG, tmp);
6060069f1a80SAndrew Thompson
6061069f1a80SAndrew Thompson /* set Rx filter */
6062069f1a80SAndrew Thompson tmp = RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR;
6063069f1a80SAndrew Thompson if (ic->ic_opmode != IEEE80211_M_MONITOR) {
6064069f1a80SAndrew Thompson tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL |
6065069f1a80SAndrew Thompson RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK |
6066069f1a80SAndrew Thompson RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV |
6067069f1a80SAndrew Thompson RT2860_DROP_CFACK | RT2860_DROP_CFEND;
6068069f1a80SAndrew Thompson if (ic->ic_opmode == IEEE80211_M_STA)
6069069f1a80SAndrew Thompson tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL;
6070069f1a80SAndrew Thompson }
6071069f1a80SAndrew Thompson run_write(sc, RT2860_RX_FILTR_CFG, tmp);
6072069f1a80SAndrew Thompson
6073069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_SYS_CTRL,
6074069f1a80SAndrew Thompson RT2860_MAC_RX_EN | RT2860_MAC_TX_EN);
6075069f1a80SAndrew Thompson
607675c76159SAndrew Thompson return (0);
6077069f1a80SAndrew Thompson }
6078069f1a80SAndrew Thompson
6079069f1a80SAndrew Thompson static void
run_adjust_freq_offset(struct run_softc * sc)608064891211SKevin Lo run_adjust_freq_offset(struct run_softc *sc)
608164891211SKevin Lo {
608264891211SKevin Lo uint8_t rf, tmp;
608364891211SKevin Lo
608464891211SKevin Lo run_rt3070_rf_read(sc, 17, &rf);
608564891211SKevin Lo tmp = rf;
608664891211SKevin Lo rf = (rf & ~0x7f) | (sc->freq & 0x7f);
608764891211SKevin Lo rf = MIN(rf, 0x5f);
608864891211SKevin Lo
608964891211SKevin Lo if (tmp != rf)
609064891211SKevin Lo run_mcu_cmd(sc, 0x74, (tmp << 8 ) | rf);
609164891211SKevin Lo }
609264891211SKevin Lo
609364891211SKevin Lo static void
run_init_locked(struct run_softc * sc)6094069f1a80SAndrew Thompson run_init_locked(struct run_softc *sc)
6095069f1a80SAndrew Thompson {
60967a79cebfSGleb Smirnoff struct ieee80211com *ic = &sc->sc_ic;
60977a79cebfSGleb Smirnoff struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
6098069f1a80SAndrew Thompson uint32_t tmp;
6099069f1a80SAndrew Thompson uint8_t bbp1, bbp3;
6100069f1a80SAndrew Thompson int i;
6101069f1a80SAndrew Thompson int ridx;
6102069f1a80SAndrew Thompson int ntries;
6103069f1a80SAndrew Thompson
610485e7bb81SAndrew Thompson if (ic->ic_nrunning > 1)
610585e7bb81SAndrew Thompson return;
610685e7bb81SAndrew Thompson
6107069f1a80SAndrew Thompson run_stop(sc);
6108069f1a80SAndrew Thompson
6109f9d03266SBernhard Schmidt if (run_load_microcode(sc) != 0) {
6110f9d03266SBernhard Schmidt device_printf(sc->sc_dev, "could not load 8051 microcode\n");
6111f9d03266SBernhard Schmidt goto fail;
6112f9d03266SBernhard Schmidt }
6113f9d03266SBernhard Schmidt
6114069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
6115069f1a80SAndrew Thompson if (run_read(sc, RT2860_ASIC_VER_ID, &tmp) != 0)
6116069f1a80SAndrew Thompson goto fail;
6117069f1a80SAndrew Thompson if (tmp != 0 && tmp != 0xffffffff)
6118069f1a80SAndrew Thompson break;
6119069f1a80SAndrew Thompson run_delay(sc, 10);
6120069f1a80SAndrew Thompson }
6121069f1a80SAndrew Thompson if (ntries == 100)
6122069f1a80SAndrew Thompson goto fail;
6123069f1a80SAndrew Thompson
6124069f1a80SAndrew Thompson for (i = 0; i != RUN_EP_QUEUES; i++)
6125069f1a80SAndrew Thompson run_setup_tx_list(sc, &sc->sc_epq[i]);
6126069f1a80SAndrew Thompson
61277a79cebfSGleb Smirnoff run_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr);
6128069f1a80SAndrew Thompson
6129069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
6130069f1a80SAndrew Thompson if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0)
6131069f1a80SAndrew Thompson goto fail;
6132069f1a80SAndrew Thompson if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0)
6133069f1a80SAndrew Thompson break;
6134069f1a80SAndrew Thompson run_delay(sc, 10);
6135069f1a80SAndrew Thompson }
6136069f1a80SAndrew Thompson if (ntries == 100) {
613797ccc890SAndrew Thompson device_printf(sc->sc_dev, "timeout waiting for DMA engine\n");
6138069f1a80SAndrew Thompson goto fail;
6139069f1a80SAndrew Thompson }
6140069f1a80SAndrew Thompson tmp &= 0xff0;
6141069f1a80SAndrew Thompson tmp |= RT2860_TX_WB_DDONE;
6142069f1a80SAndrew Thompson run_write(sc, RT2860_WPDMA_GLO_CFG, tmp);
6143069f1a80SAndrew Thompson
6144069f1a80SAndrew Thompson /* turn off PME_OEN to solve high-current issue */
6145069f1a80SAndrew Thompson run_read(sc, RT2860_SYS_CTRL, &tmp);
6146069f1a80SAndrew Thompson run_write(sc, RT2860_SYS_CTRL, tmp & ~RT2860_PME_OEN);
6147069f1a80SAndrew Thompson
6148069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_SYS_CTRL,
6149069f1a80SAndrew Thompson RT2860_BBP_HRST | RT2860_MAC_SRST);
6150069f1a80SAndrew Thompson run_write(sc, RT2860_USB_DMA_CFG, 0);
6151069f1a80SAndrew Thompson
6152069f1a80SAndrew Thompson if (run_reset(sc) != 0) {
615397ccc890SAndrew Thompson device_printf(sc->sc_dev, "could not reset chipset\n");
6154069f1a80SAndrew Thompson goto fail;
6155069f1a80SAndrew Thompson }
6156069f1a80SAndrew Thompson
6157069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_SYS_CTRL, 0);
6158069f1a80SAndrew Thompson
6159069f1a80SAndrew Thompson /* init Tx power for all Tx rates (from EEPROM) */
6160069f1a80SAndrew Thompson for (ridx = 0; ridx < 5; ridx++) {
6161069f1a80SAndrew Thompson if (sc->txpow20mhz[ridx] == 0xffffffff)
6162069f1a80SAndrew Thompson continue;
6163069f1a80SAndrew Thompson run_write(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]);
6164069f1a80SAndrew Thompson }
6165069f1a80SAndrew Thompson
616664891211SKevin Lo for (i = 0; i < nitems(rt2870_def_mac); i++)
6167069f1a80SAndrew Thompson run_write(sc, rt2870_def_mac[i].reg, rt2870_def_mac[i].val);
6168069f1a80SAndrew Thompson run_write(sc, RT2860_WMM_AIFSN_CFG, 0x00002273);
6169069f1a80SAndrew Thompson run_write(sc, RT2860_WMM_CWMIN_CFG, 0x00002344);
6170069f1a80SAndrew Thompson run_write(sc, RT2860_WMM_CWMAX_CFG, 0x000034aa);
6171069f1a80SAndrew Thompson
6172b8161ff3SKevin Lo if (sc->mac_ver >= 0x5390) {
6173b8161ff3SKevin Lo run_write(sc, RT2860_TX_SW_CFG0,
6174b8161ff3SKevin Lo 4 << RT2860_DLY_PAPE_EN_SHIFT | 4);
6175b8161ff3SKevin Lo if (sc->mac_ver >= 0x5392) {
617664891211SKevin Lo run_write(sc, RT2860_MAX_LEN_CFG, 0x00002fff);
6177242dbae3SKevin Lo if (sc->mac_ver == 0x5592) {
6178242dbae3SKevin Lo run_write(sc, RT2860_HT_FBK_CFG1, 0xedcba980);
6179242dbae3SKevin Lo run_write(sc, RT2860_TXOP_HLDR_ET, 0x00000082);
6180242dbae3SKevin Lo } else {
618164891211SKevin Lo run_write(sc, RT2860_HT_FBK_CFG1, 0xedcb4980);
618264891211SKevin Lo run_write(sc, RT2860_LG_FBK_CFG0, 0xedcba322);
6183b8161ff3SKevin Lo }
6184242dbae3SKevin Lo }
61857a7e01caSKevin Lo } else if (sc->mac_ver == 0x3593) {
61867a7e01caSKevin Lo run_write(sc, RT2860_TX_SW_CFG0,
61877a7e01caSKevin Lo 4 << RT2860_DLY_PAPE_EN_SHIFT | 2);
618864891211SKevin Lo } else if (sc->mac_ver >= 0x3070) {
6189069f1a80SAndrew Thompson /* set delay of PA_PE assertion to 1us (unit of 0.25us) */
6190069f1a80SAndrew Thompson run_write(sc, RT2860_TX_SW_CFG0,
6191069f1a80SAndrew Thompson 4 << RT2860_DLY_PAPE_EN_SHIFT);
6192069f1a80SAndrew Thompson }
6193069f1a80SAndrew Thompson
6194069f1a80SAndrew Thompson /* wait while MAC is busy */
6195069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
6196069f1a80SAndrew Thompson if (run_read(sc, RT2860_MAC_STATUS_REG, &tmp) != 0)
6197069f1a80SAndrew Thompson goto fail;
6198069f1a80SAndrew Thompson if (!(tmp & (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY)))
6199069f1a80SAndrew Thompson break;
6200069f1a80SAndrew Thompson run_delay(sc, 10);
6201069f1a80SAndrew Thompson }
6202069f1a80SAndrew Thompson if (ntries == 100)
6203069f1a80SAndrew Thompson goto fail;
6204069f1a80SAndrew Thompson
6205069f1a80SAndrew Thompson /* clear Host to MCU mailbox */
6206069f1a80SAndrew Thompson run_write(sc, RT2860_H2M_BBPAGENT, 0);
6207069f1a80SAndrew Thompson run_write(sc, RT2860_H2M_MAILBOX, 0);
6208069f1a80SAndrew Thompson run_delay(sc, 10);
6209069f1a80SAndrew Thompson
6210069f1a80SAndrew Thompson if (run_bbp_init(sc) != 0) {
621197ccc890SAndrew Thompson device_printf(sc->sc_dev, "could not initialize BBP\n");
6212069f1a80SAndrew Thompson goto fail;
6213069f1a80SAndrew Thompson }
6214069f1a80SAndrew Thompson
6215069f1a80SAndrew Thompson /* abort TSF synchronization */
6216ae6f61b1SAndriy Voskoboinyk run_disable_tsf(sc);
6217069f1a80SAndrew Thompson
6218069f1a80SAndrew Thompson /* clear RX WCID search table */
6219069f1a80SAndrew Thompson run_set_region_4(sc, RT2860_WCID_ENTRY(0), 0, 512);
6220069f1a80SAndrew Thompson /* clear WCID attribute table */
6221069f1a80SAndrew Thompson run_set_region_4(sc, RT2860_WCID_ATTR(0), 0, 8 * 32);
6222beaa0537SAndrew Thompson
6223beaa0537SAndrew Thompson /* hostapd sets a key before init. So, don't clear it. */
6224beaa0537SAndrew Thompson if (sc->cmdq_key_set != RUN_CMDQ_GO) {
6225069f1a80SAndrew Thompson /* clear shared key table */
6226069f1a80SAndrew Thompson run_set_region_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32);
6227069f1a80SAndrew Thompson /* clear shared key mode */
6228069f1a80SAndrew Thompson run_set_region_4(sc, RT2860_SKEY_MODE_0_7, 0, 4);
6229beaa0537SAndrew Thompson }
6230069f1a80SAndrew Thompson
6231069f1a80SAndrew Thompson run_read(sc, RT2860_US_CYC_CNT, &tmp);
6232069f1a80SAndrew Thompson tmp = (tmp & ~0xff) | 0x1e;
6233069f1a80SAndrew Thompson run_write(sc, RT2860_US_CYC_CNT, tmp);
6234069f1a80SAndrew Thompson
62353707a5e9SAndrew Thompson if (sc->mac_rev != 0x0101)
6236069f1a80SAndrew Thompson run_write(sc, RT2860_TXOP_CTRL_CFG, 0x0000583f);
6237069f1a80SAndrew Thompson
6238069f1a80SAndrew Thompson run_write(sc, RT2860_WMM_TXOP0_CFG, 0);
6239069f1a80SAndrew Thompson run_write(sc, RT2860_WMM_TXOP1_CFG, 48 << 16 | 96);
6240069f1a80SAndrew Thompson
6241069f1a80SAndrew Thompson /* write vendor-specific BBP values (from EEPROM) */
62427a7e01caSKevin Lo if (sc->mac_ver < 0x3593) {
624385e7bb81SAndrew Thompson for (i = 0; i < 10; i++) {
6244069f1a80SAndrew Thompson if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff)
6245069f1a80SAndrew Thompson continue;
6246069f1a80SAndrew Thompson run_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val);
6247069f1a80SAndrew Thompson }
624864891211SKevin Lo }
6249069f1a80SAndrew Thompson
6250069f1a80SAndrew Thompson /* select Main antenna for 1T1R devices */
625164891211SKevin Lo if (sc->rf_rev == RT3070_RF_3020 || sc->rf_rev == RT5390_RF_5370)
6252069f1a80SAndrew Thompson run_set_rx_antenna(sc, 0);
6253069f1a80SAndrew Thompson
6254069f1a80SAndrew Thompson /* send LEDs operating mode to microcontroller */
6255069f1a80SAndrew Thompson (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0]);
6256069f1a80SAndrew Thompson (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1]);
6257069f1a80SAndrew Thompson (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2]);
6258069f1a80SAndrew Thompson
625964891211SKevin Lo if (sc->mac_ver >= 0x5390)
626064891211SKevin Lo run_rt5390_rf_init(sc);
62617a7e01caSKevin Lo else if (sc->mac_ver == 0x3593)
62627a7e01caSKevin Lo run_rt3593_rf_init(sc);
626364891211SKevin Lo else if (sc->mac_ver >= 0x3070)
62643707a5e9SAndrew Thompson run_rt3070_rf_init(sc);
62653707a5e9SAndrew Thompson
6266069f1a80SAndrew Thompson /* disable non-existing Rx chains */
6267069f1a80SAndrew Thompson run_bbp_read(sc, 3, &bbp3);
6268069f1a80SAndrew Thompson bbp3 &= ~(1 << 3 | 1 << 4);
6269069f1a80SAndrew Thompson if (sc->nrxchains == 2)
6270069f1a80SAndrew Thompson bbp3 |= 1 << 3;
6271069f1a80SAndrew Thompson else if (sc->nrxchains == 3)
6272069f1a80SAndrew Thompson bbp3 |= 1 << 4;
6273069f1a80SAndrew Thompson run_bbp_write(sc, 3, bbp3);
6274069f1a80SAndrew Thompson
6275069f1a80SAndrew Thompson /* disable non-existing Tx chains */
6276069f1a80SAndrew Thompson run_bbp_read(sc, 1, &bbp1);
6277069f1a80SAndrew Thompson if (sc->ntxchains == 1)
6278069f1a80SAndrew Thompson bbp1 &= ~(1 << 3 | 1 << 4);
6279069f1a80SAndrew Thompson run_bbp_write(sc, 1, bbp1);
6280069f1a80SAndrew Thompson
62817a7e01caSKevin Lo if (sc->mac_ver >= 0x5390)
62827a7e01caSKevin Lo run_rt5390_rf_setup(sc);
62837a7e01caSKevin Lo else if (sc->mac_ver == 0x3593)
62847a7e01caSKevin Lo run_rt3593_rf_setup(sc);
62857a7e01caSKevin Lo else if (sc->mac_ver >= 0x3070)
62863707a5e9SAndrew Thompson run_rt3070_rf_setup(sc);
6287069f1a80SAndrew Thompson
6288069f1a80SAndrew Thompson /* select default channel */
6289069f1a80SAndrew Thompson run_set_chan(sc, ic->ic_curchan);
6290069f1a80SAndrew Thompson
6291069f1a80SAndrew Thompson /* setup initial protection mode */
6292e7d14e9bSBernhard Schmidt run_updateprot_cb(ic);
6293069f1a80SAndrew Thompson
6294069f1a80SAndrew Thompson /* turn radio LED on */
6295069f1a80SAndrew Thompson run_set_leds(sc, RT2860_LED_RADIO);
6296069f1a80SAndrew Thompson
6297f520d761SAdrian Chadd /* Set up AUTO_RSP_CFG register for auto response */
6298f520d761SAdrian Chadd run_write(sc, RT2860_AUTO_RSP_CFG, RT2860_AUTO_RSP_EN |
6299f520d761SAdrian Chadd RT2860_BAC_ACKPOLICY_EN | RT2860_CTS_40M_MODE_EN);
6300f520d761SAdrian Chadd
63017a79cebfSGleb Smirnoff sc->sc_flags |= RUN_RUNNING;
630285e7bb81SAndrew Thompson sc->cmdq_run = RUN_CMDQ_GO;
6303069f1a80SAndrew Thompson
6304069f1a80SAndrew Thompson for (i = 0; i != RUN_N_XFER; i++)
6305069f1a80SAndrew Thompson usbd_xfer_set_stall(sc->sc_xfer[i]);
6306069f1a80SAndrew Thompson
6307069f1a80SAndrew Thompson usbd_transfer_start(sc->sc_xfer[RUN_BULK_RX]);
6308069f1a80SAndrew Thompson
6309069f1a80SAndrew Thompson if (run_txrx_enable(sc) != 0)
6310069f1a80SAndrew Thompson goto fail;
6311069f1a80SAndrew Thompson
6312069f1a80SAndrew Thompson return;
6313069f1a80SAndrew Thompson
6314069f1a80SAndrew Thompson fail:
6315069f1a80SAndrew Thompson run_stop(sc);
6316069f1a80SAndrew Thompson }
6317069f1a80SAndrew Thompson
6318069f1a80SAndrew Thompson static void
run_stop(void * arg)6319069f1a80SAndrew Thompson run_stop(void *arg)
6320069f1a80SAndrew Thompson {
6321069f1a80SAndrew Thompson struct run_softc *sc = (struct run_softc *)arg;
6322069f1a80SAndrew Thompson uint32_t tmp;
6323069f1a80SAndrew Thompson int i;
6324069f1a80SAndrew Thompson int ntries;
6325069f1a80SAndrew Thompson
6326069f1a80SAndrew Thompson RUN_LOCK_ASSERT(sc, MA_OWNED);
6327069f1a80SAndrew Thompson
63287a79cebfSGleb Smirnoff if (sc->sc_flags & RUN_RUNNING)
6329069f1a80SAndrew Thompson run_set_leds(sc, 0); /* turn all LEDs off */
6330069f1a80SAndrew Thompson
63317a79cebfSGleb Smirnoff sc->sc_flags &= ~RUN_RUNNING;
6332069f1a80SAndrew Thompson
633385e7bb81SAndrew Thompson sc->ratectl_run = RUN_RATECTL_OFF;
6334beaa0537SAndrew Thompson sc->cmdq_run = sc->cmdq_key_set;
633585e7bb81SAndrew Thompson
6336069f1a80SAndrew Thompson RUN_UNLOCK(sc);
6337069f1a80SAndrew Thompson
6338069f1a80SAndrew Thompson for(i = 0; i < RUN_N_XFER; i++)
6339069f1a80SAndrew Thompson usbd_transfer_drain(sc->sc_xfer[i]);
6340069f1a80SAndrew Thompson
6341069f1a80SAndrew Thompson RUN_LOCK(sc);
6342069f1a80SAndrew Thompson
63432e83cb98SAdrian Chadd run_drain_mbufq(sc);
63442e83cb98SAdrian Chadd
6345069f1a80SAndrew Thompson if (sc->rx_m != NULL) {
6346069f1a80SAndrew Thompson m_free(sc->rx_m);
6347069f1a80SAndrew Thompson sc->rx_m = NULL;
6348069f1a80SAndrew Thompson }
6349069f1a80SAndrew Thompson
635064891211SKevin Lo /* Disable Tx/Rx DMA. */
635164891211SKevin Lo if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0)
635264891211SKevin Lo return;
635364891211SKevin Lo tmp &= ~(RT2860_RX_DMA_EN | RT2860_TX_DMA_EN);
635464891211SKevin Lo run_write(sc, RT2860_WPDMA_GLO_CFG, tmp);
635564891211SKevin Lo
635664891211SKevin Lo for (ntries = 0; ntries < 100; ntries++) {
635764891211SKevin Lo if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0)
635864891211SKevin Lo return;
635964891211SKevin Lo if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0)
636064891211SKevin Lo break;
636164891211SKevin Lo run_delay(sc, 10);
636264891211SKevin Lo }
636364891211SKevin Lo if (ntries == 100) {
636464891211SKevin Lo device_printf(sc->sc_dev, "timeout waiting for DMA engine\n");
636564891211SKevin Lo return;
636664891211SKevin Lo }
636764891211SKevin Lo
6368069f1a80SAndrew Thompson /* disable Tx/Rx */
6369069f1a80SAndrew Thompson run_read(sc, RT2860_MAC_SYS_CTRL, &tmp);
6370069f1a80SAndrew Thompson tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN);
6371069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_SYS_CTRL, tmp);
6372069f1a80SAndrew Thompson
6373069f1a80SAndrew Thompson /* wait for pending Tx to complete */
6374069f1a80SAndrew Thompson for (ntries = 0; ntries < 100; ntries++) {
6375069f1a80SAndrew Thompson if (run_read(sc, RT2860_TXRXQ_PCNT, &tmp) != 0) {
6376109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET,
6377109005ccSGavin Atkinson "Cannot read Tx queue count\n");
6378069f1a80SAndrew Thompson break;
6379069f1a80SAndrew Thompson }
6380069f1a80SAndrew Thompson if ((tmp & RT2860_TX2Q_PCNT_MASK) == 0) {
6381109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET,
6382109005ccSGavin Atkinson "All Tx cleared\n");
6383069f1a80SAndrew Thompson break;
6384069f1a80SAndrew Thompson }
6385069f1a80SAndrew Thompson run_delay(sc, 10);
6386069f1a80SAndrew Thompson }
6387069f1a80SAndrew Thompson if (ntries >= 100)
6388109005ccSGavin Atkinson RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET,
6389109005ccSGavin Atkinson "There are still pending Tx\n");
6390069f1a80SAndrew Thompson run_delay(sc, 10);
6391069f1a80SAndrew Thompson run_write(sc, RT2860_USB_DMA_CFG, 0);
6392069f1a80SAndrew Thompson
6393069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST);
6394069f1a80SAndrew Thompson run_write(sc, RT2860_MAC_SYS_CTRL, 0);
6395069f1a80SAndrew Thompson
6396069f1a80SAndrew Thompson for (i = 0; i != RUN_EP_QUEUES; i++)
6397069f1a80SAndrew Thompson run_unsetup_tx_list(sc, &sc->sc_epq[i]);
6398069f1a80SAndrew Thompson }
6399069f1a80SAndrew Thompson
6400069f1a80SAndrew Thompson static void
run_delay(struct run_softc * sc,u_int ms)640135a24898SHans Petter Selasky run_delay(struct run_softc *sc, u_int ms)
6402069f1a80SAndrew Thompson {
6403069f1a80SAndrew Thompson usb_pause_mtx(mtx_owned(&sc->sc_mtx) ?
6404069f1a80SAndrew Thompson &sc->sc_mtx : NULL, USB_MS_TO_TICKS(ms));
6405069f1a80SAndrew Thompson }
6406069f1a80SAndrew Thompson
6407f520d761SAdrian Chadd static void
run_update_chw(struct ieee80211com * ic)6408f520d761SAdrian Chadd run_update_chw(struct ieee80211com *ic)
6409f520d761SAdrian Chadd {
6410f520d761SAdrian Chadd
6411f520d761SAdrian Chadd printf("%s: TODO\n", __func__);
6412f520d761SAdrian Chadd }
6413f520d761SAdrian Chadd
6414f520d761SAdrian Chadd static int
run_ampdu_enable(struct ieee80211_node * ni,struct ieee80211_tx_ampdu * tap)6415f520d761SAdrian Chadd run_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
6416f520d761SAdrian Chadd {
6417f520d761SAdrian Chadd
6418f520d761SAdrian Chadd /* For now, no A-MPDU TX support in the driver */
6419f520d761SAdrian Chadd return (0);
6420f520d761SAdrian Chadd }
6421f520d761SAdrian Chadd
6422069f1a80SAndrew Thompson static device_method_t run_methods[] = {
6423069f1a80SAndrew Thompson /* Device interface */
6424069f1a80SAndrew Thompson DEVMETHOD(device_probe, run_match),
6425069f1a80SAndrew Thompson DEVMETHOD(device_attach, run_attach),
6426069f1a80SAndrew Thompson DEVMETHOD(device_detach, run_detach),
6427645e4d17SHans Petter Selasky DEVMETHOD_END
6428069f1a80SAndrew Thompson };
6429069f1a80SAndrew Thompson
6430069f1a80SAndrew Thompson static driver_t run_driver = {
64316d917491SHans Petter Selasky .name = "run",
64326d917491SHans Petter Selasky .methods = run_methods,
64336d917491SHans Petter Selasky .size = sizeof(struct run_softc)
6434069f1a80SAndrew Thompson };
6435069f1a80SAndrew Thompson
6436bc9372d7SJohn Baldwin DRIVER_MODULE(run, uhub, run_driver, run_driver_loaded, NULL);
6437910cb8feSAndrew Thompson MODULE_DEPEND(run, wlan, 1, 1, 1);
6438910cb8feSAndrew Thompson MODULE_DEPEND(run, usb, 1, 1, 1);
6439910cb8feSAndrew Thompson MODULE_DEPEND(run, firmware, 1, 1, 1);
6440910cb8feSAndrew Thompson MODULE_VERSION(run, 1);
6441f809f280SWarner Losh USB_PNP_HOST_INFO(run_devs);
6442