1*05018d8cSjsg /* $OpenBSD: if_urtwn.c,v 1.112 2024/10/22 22:21:25 jsg Exp $ */ 29ec5c125Sdamien 39ec5c125Sdamien /*- 49ec5c125Sdamien * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> 5c349b8bfSstsp * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org> 690540544Sjmatthew * Copyright (c) 2016 Nathanial Sloss <nathanialsloss@yahoo.com.au> 79ec5c125Sdamien * 89ec5c125Sdamien * Permission to use, copy, modify, and distribute this software for any 99ec5c125Sdamien * purpose with or without fee is hereby granted, provided that the above 109ec5c125Sdamien * copyright notice and this permission notice appear in all copies. 119ec5c125Sdamien * 129ec5c125Sdamien * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 139ec5c125Sdamien * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 149ec5c125Sdamien * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 159ec5c125Sdamien * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 169ec5c125Sdamien * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 179ec5c125Sdamien * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 189ec5c125Sdamien * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 199ec5c125Sdamien */ 209ec5c125Sdamien 219ec5c125Sdamien /* 22067851b1Skevlo * Driver for Realtek RTL8188CE-VAU/RTL8188CUS/RTL8188EU/RTL8188FTV/RTL8188RU/ 23067851b1Skevlo * RTL8192CU/RTL8192EU. 249ec5c125Sdamien */ 259ec5c125Sdamien 269ec5c125Sdamien #include "bpfilter.h" 279ec5c125Sdamien 289ec5c125Sdamien #include <sys/param.h> 299ec5c125Sdamien #include <sys/mbuf.h> 309ec5c125Sdamien #include <sys/systm.h> 319ec5c125Sdamien #include <sys/timeout.h> 329ec5c125Sdamien #include <sys/device.h> 339b18ffb8Sguenther #include <sys/endian.h> 349ec5c125Sdamien 3590540544Sjmatthew #include <machine/bus.h> 369ec5c125Sdamien #include <machine/intr.h> 379ec5c125Sdamien 389ec5c125Sdamien #if NBPFILTER > 0 399ec5c125Sdamien #include <net/bpf.h> 409ec5c125Sdamien #endif 419ec5c125Sdamien #include <net/if.h> 429ec5c125Sdamien #include <net/if_media.h> 439ec5c125Sdamien 449ec5c125Sdamien #include <netinet/in.h> 459ec5c125Sdamien #include <netinet/if_ether.h> 469ec5c125Sdamien 479ec5c125Sdamien #include <net80211/ieee80211_var.h> 48a087613eSstsp #include <net80211/ieee80211_amrr.h> 499ec5c125Sdamien #include <net80211/ieee80211_radiotap.h> 509ec5c125Sdamien 519ec5c125Sdamien #include <dev/usb/usb.h> 529ec5c125Sdamien #include <dev/usb/usbdi.h> 5390540544Sjmatthew #include <dev/usb/usbdivar.h> 549ec5c125Sdamien #include <dev/usb/usbdevs.h> 559ec5c125Sdamien 5653c3c4d0Sstsp #include <dev/ic/r92creg.h> 5723340becSstsp #include <dev/ic/rtwnvar.h> 582a915f22Sstsp 592a915f22Sstsp /* Maximum number of output pipes is 3. */ 602a915f22Sstsp #define R92C_MAX_EPOUT 3 612a915f22Sstsp 6208300c66Skevlo #define R92C_HQ_NPAGES 12 6308300c66Skevlo #define R92C_LQ_NPAGES 2 6408300c66Skevlo #define R92C_NQ_NPAGES 2 652a915f22Sstsp #define R92C_TXPKTBUF_COUNT 256 662a915f22Sstsp #define R92C_TX_PAGE_COUNT 248 6708300c66Skevlo #define R92C_MAX_RX_DMA_SIZE 0x2800 6890540544Sjmatthew 6908300c66Skevlo #define R88E_HQ_NPAGES 0 7008300c66Skevlo #define R88E_LQ_NPAGES 9 7108300c66Skevlo #define R88E_NQ_NPAGES 0 722a915f22Sstsp #define R88E_TXPKTBUF_COUNT 177 7308300c66Skevlo #define R88E_TX_PAGE_COUNT 168 7408300c66Skevlo #define R88E_MAX_RX_DMA_SIZE 0x2400 752a915f22Sstsp 76067851b1Skevlo #define R88F_HQ_NPAGES 12 77067851b1Skevlo #define R88F_LQ_NPAGES 2 78067851b1Skevlo #define R88F_NQ_NPAGES 2 79067851b1Skevlo #define R88F_TXPKTBUF_COUNT 177 80067851b1Skevlo #define R88F_TX_PAGE_COUNT 247 81067851b1Skevlo #define R88F_MAX_RX_DMA_SIZE 0x3f80 82067851b1Skevlo 8390540544Sjmatthew #define R92E_HQ_NPAGES 16 8490540544Sjmatthew #define R92E_LQ_NPAGES 16 8590540544Sjmatthew #define R92E_NQ_NPAGES 16 8690540544Sjmatthew #define R92E_TX_PAGE_COUNT 248 8790540544Sjmatthew #define R92E_MAX_RX_DMA_SIZE 0x3fc0 8890540544Sjmatthew 8990540544Sjmatthew #define R92C_TXDESC_SUMSIZE 32 9090540544Sjmatthew #define R92C_TXDESC_SUMOFFSET 14 9190540544Sjmatthew 922a915f22Sstsp /* USB Requests. */ 932a915f22Sstsp #define R92C_REQ_REGS 0x05 942a915f22Sstsp 952a915f22Sstsp /* 962a915f22Sstsp * Driver definitions. 972a915f22Sstsp */ 982a915f22Sstsp #define URTWN_RX_LIST_COUNT 1 992a915f22Sstsp #define URTWN_TX_LIST_COUNT 8 1002a915f22Sstsp #define URTWN_HOST_CMD_RING_COUNT 32 1012a915f22Sstsp 1022a915f22Sstsp #define URTWN_RXBUFSZ (16 * 1024) 10390540544Sjmatthew #define URTWN_TXBUFSZ (sizeof(struct r92e_tx_desc_usb) + IEEE80211_MAX_LEN) 1042a915f22Sstsp 1052a915f22Sstsp #define URTWN_RIDX_COUNT 28 1062a915f22Sstsp 1072a915f22Sstsp #define URTWN_TX_TIMEOUT 5000 /* ms */ 1082a915f22Sstsp 1092a915f22Sstsp #define URTWN_LED_LINK 0 1102a915f22Sstsp #define URTWN_LED_DATA 1 1112a915f22Sstsp 1122a915f22Sstsp struct urtwn_rx_radiotap_header { 1132a915f22Sstsp struct ieee80211_radiotap_header wr_ihdr; 1142a915f22Sstsp uint8_t wr_flags; 1152a915f22Sstsp uint8_t wr_rate; 1162a915f22Sstsp uint16_t wr_chan_freq; 1172a915f22Sstsp uint16_t wr_chan_flags; 1182a915f22Sstsp uint8_t wr_dbm_antsignal; 1192a915f22Sstsp } __packed; 1202a915f22Sstsp 1212a915f22Sstsp #define URTWN_RX_RADIOTAP_PRESENT \ 1222a915f22Sstsp (1 << IEEE80211_RADIOTAP_FLAGS | \ 1232a915f22Sstsp 1 << IEEE80211_RADIOTAP_RATE | \ 1242a915f22Sstsp 1 << IEEE80211_RADIOTAP_CHANNEL | \ 1252a915f22Sstsp 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) 1262a915f22Sstsp 1272a915f22Sstsp struct urtwn_tx_radiotap_header { 1282a915f22Sstsp struct ieee80211_radiotap_header wt_ihdr; 1292a915f22Sstsp uint8_t wt_flags; 1302a915f22Sstsp uint16_t wt_chan_freq; 1312a915f22Sstsp uint16_t wt_chan_flags; 1322a915f22Sstsp } __packed; 1332a915f22Sstsp 1342a915f22Sstsp #define URTWN_TX_RADIOTAP_PRESENT \ 1352a915f22Sstsp (1 << IEEE80211_RADIOTAP_FLAGS | \ 1362a915f22Sstsp 1 << IEEE80211_RADIOTAP_CHANNEL) 1372a915f22Sstsp 1382a915f22Sstsp struct urtwn_softc; 1392a915f22Sstsp 1402a915f22Sstsp struct urtwn_rx_data { 1412a915f22Sstsp struct urtwn_softc *sc; 1422a915f22Sstsp struct usbd_xfer *xfer; 1432a915f22Sstsp uint8_t *buf; 1442a915f22Sstsp }; 1452a915f22Sstsp 1462a915f22Sstsp struct urtwn_tx_data { 1472a915f22Sstsp struct urtwn_softc *sc; 1482a915f22Sstsp struct usbd_pipe *pipe; 1492a915f22Sstsp struct usbd_xfer *xfer; 1502a915f22Sstsp uint8_t *buf; 1512a915f22Sstsp TAILQ_ENTRY(urtwn_tx_data) next; 1522a915f22Sstsp }; 1532a915f22Sstsp 1542a915f22Sstsp struct urtwn_host_cmd { 1552a915f22Sstsp void (*cb)(struct urtwn_softc *, void *); 1562a915f22Sstsp uint8_t data[256]; 1572a915f22Sstsp }; 1582a915f22Sstsp 1592a915f22Sstsp struct urtwn_cmd_newstate { 1602a915f22Sstsp enum ieee80211_state state; 1612a915f22Sstsp int arg; 1622a915f22Sstsp }; 1632a915f22Sstsp 1642a915f22Sstsp struct urtwn_cmd_key { 1652a915f22Sstsp struct ieee80211_key key; 16623340becSstsp struct ieee80211_node *ni; 1672a915f22Sstsp }; 1682a915f22Sstsp 1692a915f22Sstsp struct urtwn_host_cmd_ring { 1702a915f22Sstsp struct urtwn_host_cmd cmd[URTWN_HOST_CMD_RING_COUNT]; 1712a915f22Sstsp int cur; 1722a915f22Sstsp int next; 1732a915f22Sstsp int queued; 1742a915f22Sstsp }; 1752a915f22Sstsp 1762a915f22Sstsp struct urtwn_softc { 1772a915f22Sstsp struct device sc_dev; 17823340becSstsp struct rtwn_softc sc_sc; 17923340becSstsp 1802a915f22Sstsp struct usbd_device *sc_udev; 1812a915f22Sstsp struct usbd_interface *sc_iface; 1822a915f22Sstsp struct usb_task sc_task; 18323340becSstsp 1842a915f22Sstsp struct timeout scan_to; 1852a915f22Sstsp struct timeout calib_to; 18623340becSstsp 18708300c66Skevlo int ntx; 1882a915f22Sstsp struct usbd_pipe *rx_pipe; 1892a915f22Sstsp struct usbd_pipe *tx_pipe[R92C_MAX_EPOUT]; 1902a915f22Sstsp int ac2idx[EDCA_NUM_AC]; 1912a915f22Sstsp 1922a915f22Sstsp struct urtwn_host_cmd_ring cmdq; 1932a915f22Sstsp struct urtwn_rx_data rx_data[URTWN_RX_LIST_COUNT]; 1942a915f22Sstsp struct urtwn_tx_data tx_data[URTWN_TX_LIST_COUNT]; 1952a915f22Sstsp TAILQ_HEAD(, urtwn_tx_data) tx_free_list; 1962a915f22Sstsp 197a087613eSstsp struct ieee80211_amrr amrr; 198a087613eSstsp struct ieee80211_amrr_node amn; 199a087613eSstsp 2002a915f22Sstsp #if NBPFILTER > 0 2012a915f22Sstsp caddr_t sc_drvbpf; 2022a915f22Sstsp 2032a915f22Sstsp union { 2042a915f22Sstsp struct urtwn_rx_radiotap_header th; 2052a915f22Sstsp uint8_t pad[64]; 2062a915f22Sstsp } sc_rxtapu; 2072a915f22Sstsp #define sc_rxtap sc_rxtapu.th 2082a915f22Sstsp int sc_rxtap_len; 2092a915f22Sstsp 2102a915f22Sstsp union { 2112a915f22Sstsp struct urtwn_tx_radiotap_header th; 2122a915f22Sstsp uint8_t pad[64]; 2132a915f22Sstsp } sc_txtapu; 2142a915f22Sstsp #define sc_txtap sc_txtapu.th 2152a915f22Sstsp int sc_txtap_len; 2162a915f22Sstsp #endif 2172362e6a4Skrw int sc_key_tasks; 2182a915f22Sstsp }; 2199ec5c125Sdamien 2209ec5c125Sdamien #ifdef URTWN_DEBUG 2219ec5c125Sdamien #define DPRINTF(x) do { if (urtwn_debug) printf x; } while (0) 2229ec5c125Sdamien #define DPRINTFN(n, x) do { if (urtwn_debug >= (n)) printf x; } while (0) 2239ec5c125Sdamien int urtwn_debug = 4; 2249ec5c125Sdamien #else 2259ec5c125Sdamien #define DPRINTF(x) 2269ec5c125Sdamien #define DPRINTFN(n, x) 2279ec5c125Sdamien #endif 2289ec5c125Sdamien 22934a2cadeSkevlo /* 23034a2cadeSkevlo * Various supported device vendors/products. 23134a2cadeSkevlo */ 23234a2cadeSkevlo #define URTWN_DEV(v, p, f) \ 23334a2cadeSkevlo { { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, (f) | RTWN_CHIP_USB } 23434a2cadeSkevlo #define URTWN_DEV_8192CU(v, p) URTWN_DEV(v, p, RTWN_CHIP_92C | RTWN_CHIP_88C) 23590540544Sjmatthew #define URTWN_DEV_8192EU(v, p) URTWN_DEV(v, p, RTWN_CHIP_92E) 236067851b1Skevlo #define URTWN_DEV_8188EU(v, p) URTWN_DEV(v, p, RTWN_CHIP_88E) 237067851b1Skevlo #define URTWN_DEV_8188F(v, p) URTWN_DEV(v, p, RTWN_CHIP_88F) 23834a2cadeSkevlo static const struct urtwn_type { 23934a2cadeSkevlo struct usb_devno dev; 24034a2cadeSkevlo uint32_t chip; 24134a2cadeSkevlo } urtwn_devs[] = { 24234a2cadeSkevlo URTWN_DEV_8192CU(ABOCOM, RTL8188CU_1), 24334a2cadeSkevlo URTWN_DEV_8192CU(ABOCOM, RTL8188CU_2), 24434a2cadeSkevlo URTWN_DEV_8192CU(ABOCOM, RTL8192CU), 24534a2cadeSkevlo URTWN_DEV_8192CU(ASUS, RTL8192CU), 24634a2cadeSkevlo URTWN_DEV_8192CU(ASUS, RTL8192CU_2), 24734a2cadeSkevlo URTWN_DEV_8192CU(ASUS, RTL8192CU_3), 24834a2cadeSkevlo URTWN_DEV_8192CU(AZUREWAVE, RTL8188CE_1), 24934a2cadeSkevlo URTWN_DEV_8192CU(AZUREWAVE, RTL8188CE_2), 25034a2cadeSkevlo URTWN_DEV_8192CU(AZUREWAVE, RTL8188CU), 25134a2cadeSkevlo URTWN_DEV_8192CU(BELKIN, F7D2102), 25234a2cadeSkevlo URTWN_DEV_8192CU(BELKIN, F9L1004V1), 25334a2cadeSkevlo URTWN_DEV_8192CU(BELKIN, RTL8188CU), 25434a2cadeSkevlo URTWN_DEV_8192CU(BELKIN, RTL8188CUS), 25534a2cadeSkevlo URTWN_DEV_8192CU(BELKIN, RTL8192CU), 25634a2cadeSkevlo URTWN_DEV_8192CU(BELKIN, RTL8192CU_1), 25734a2cadeSkevlo URTWN_DEV_8192CU(CHICONY, RTL8188CUS_1), 25834a2cadeSkevlo URTWN_DEV_8192CU(CHICONY, RTL8188CUS_2), 25934a2cadeSkevlo URTWN_DEV_8192CU(CHICONY, RTL8188CUS_3), 26034a2cadeSkevlo URTWN_DEV_8192CU(CHICONY, RTL8188CUS_4), 26134a2cadeSkevlo URTWN_DEV_8192CU(CHICONY, RTL8188CUS_5), 26234a2cadeSkevlo URTWN_DEV_8192CU(CHICONY, RTL8188CUS_6), 26334a2cadeSkevlo URTWN_DEV_8192CU(COMPARE, RTL8192CU), 26434a2cadeSkevlo URTWN_DEV_8192CU(COREGA, RTL8192CU), 26534a2cadeSkevlo URTWN_DEV_8192CU(DLINK, DWA131B), 26634a2cadeSkevlo URTWN_DEV_8192CU(DLINK, RTL8188CU), 26734a2cadeSkevlo URTWN_DEV_8192CU(DLINK, RTL8192CU_1), 26834a2cadeSkevlo URTWN_DEV_8192CU(DLINK, RTL8192CU_2), 26934a2cadeSkevlo URTWN_DEV_8192CU(DLINK, RTL8192CU_3), 27034a2cadeSkevlo URTWN_DEV_8192CU(DLINK, RTL8192CU_4), 27134a2cadeSkevlo URTWN_DEV_8192CU(EDIMAX, EW7811UN), 27234a2cadeSkevlo URTWN_DEV_8192CU(EDIMAX, RTL8192CU), 27334a2cadeSkevlo URTWN_DEV_8192CU(FEIXUN, RTL8188CU), 27434a2cadeSkevlo URTWN_DEV_8192CU(FEIXUN, RTL8192CU), 27534a2cadeSkevlo URTWN_DEV_8192CU(GUILLEMOT, HWNUP150), 27634a2cadeSkevlo URTWN_DEV_8192CU(GUILLEMOT, RTL8192CU), 27734a2cadeSkevlo URTWN_DEV_8192CU(HAWKING, RTL8192CU), 27834a2cadeSkevlo URTWN_DEV_8192CU(HAWKING, RTL8192CU_2), 27934a2cadeSkevlo URTWN_DEV_8192CU(HP3, RTL8188CU), 28034a2cadeSkevlo URTWN_DEV_8192CU(IODATA, WNG150UM), 28134a2cadeSkevlo URTWN_DEV_8192CU(IODATA, RTL8192CU), 28234a2cadeSkevlo URTWN_DEV_8192CU(NETGEAR, N300MA), 28334a2cadeSkevlo URTWN_DEV_8192CU(NETGEAR, WNA1000M), 284afb55d64Sjsg URTWN_DEV_8192CU(NETGEAR, WNA1000MV2), 28534a2cadeSkevlo URTWN_DEV_8192CU(NETGEAR, RTL8192CU), 28634a2cadeSkevlo URTWN_DEV_8192CU(NETGEAR4, RTL8188CU), 28734a2cadeSkevlo URTWN_DEV_8192CU(NETWEEN, RTL8192CU), 28834a2cadeSkevlo URTWN_DEV_8192CU(NOVATECH, RTL8188CU), 28934a2cadeSkevlo URTWN_DEV_8192CU(PLANEX2, RTL8188CU_1), 29034a2cadeSkevlo URTWN_DEV_8192CU(PLANEX2, RTL8188CU_2), 29134a2cadeSkevlo URTWN_DEV_8192CU(PLANEX2, RTL8188CU_3), 29234a2cadeSkevlo URTWN_DEV_8192CU(PLANEX2, RTL8188CU_4), 29334a2cadeSkevlo URTWN_DEV_8192CU(PLANEX2, RTL8188CUS), 29434a2cadeSkevlo URTWN_DEV_8192CU(PLANEX2, RTL8192CU), 29534a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CE_0), 29634a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CE_1), 29734a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CTV), 29834a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CU_0), 29934a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CU_1), 30034a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CU_2), 30134a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CU_3), 30234a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CU_4), 30334a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CU_5), 30434a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CU_COMBO), 30534a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188CUS), 30634a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188RU), 30734a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188RU_2), 30834a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8188RU_3), 30934a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8191CU), 31034a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8192CE), 31134a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8192CE_VAU), 31234a2cadeSkevlo URTWN_DEV_8192CU(REALTEK, RTL8192CU), 31334a2cadeSkevlo URTWN_DEV_8192CU(SITECOMEU, RTL8188CU), 31434a2cadeSkevlo URTWN_DEV_8192CU(SITECOMEU, RTL8188CU_2), 31534a2cadeSkevlo URTWN_DEV_8192CU(SITECOMEU, RTL8192CU), 31634a2cadeSkevlo URTWN_DEV_8192CU(SITECOMEU, RTL8192CU_2), 31734a2cadeSkevlo URTWN_DEV_8192CU(SITECOMEU, WLA2100V2), 31834a2cadeSkevlo URTWN_DEV_8192CU(TPLINK, RTL8192CU), 31934a2cadeSkevlo URTWN_DEV_8192CU(TRENDNET, RTL8188CU), 32034a2cadeSkevlo URTWN_DEV_8192CU(TRENDNET, RTL8192CU), 32134a2cadeSkevlo URTWN_DEV_8192CU(ZYXEL, RTL8192CU), 322c349b8bfSstsp /* URTWN_RTL8188E */ 323ccaa33a7Ssthen URTWN_DEV_8188EU(ABOCOM, RTL8188EU), 324dd69d388Sjsg URTWN_DEV_8188EU(DLINK, DWA121B1), 32534a2cadeSkevlo URTWN_DEV_8188EU(DLINK, DWA123D1), 32634a2cadeSkevlo URTWN_DEV_8188EU(DLINK, DWA125D1), 327c63cca64Sjmatthew URTWN_DEV_8188EU(EDIMAX, EW7811UNV2), 32834a2cadeSkevlo URTWN_DEV_8188EU(ELECOM, WDC150SU2M), 329a1d28b40Sjsg URTWN_DEV_8188EU(MERCUSYS, MW150USV2), 33034a2cadeSkevlo URTWN_DEV_8188EU(REALTEK, RTL8188ETV), 3318d713102Sbenno URTWN_DEV_8188EU(REALTEK, RTL8188EU), 33290540544Sjmatthew URTWN_DEV_8188EU(TPLINK, RTL8188EUS), 333bf5e189fSstsp URTWN_DEV_8188EU(ASUS, RTL8188EUS), 334067851b1Skevlo /* URTWN_RTL8188FTV */ 335067851b1Skevlo URTWN_DEV_8188F(REALTEK, RTL8188FTV), 336bf5e189fSstsp 33790540544Sjmatthew /* URTWN_RTL8192EU */ 33833410555Skevlo URTWN_DEV_8192EU(DLINK, DWA131E1), 3391d8ba341Sjmatthew URTWN_DEV_8192EU(REALTEK, RTL8192EU), 34027da0d9aSjsg URTWN_DEV_8192EU(REALTEK, RTL8192EU_2), 3415fbe5b8cSstsp URTWN_DEV_8192EU(TPLINK, RTL8192EU), 342018f6d9cSjsg URTWN_DEV_8192EU(TPLINK, RTL8192EU_2), 343018f6d9cSjsg URTWN_DEV_8192EU(TPLINK, RTL8192EU_3) 3449ec5c125Sdamien }; 3459ec5c125Sdamien 34634a2cadeSkevlo #define urtwn_lookup(v, p) \ 34734a2cadeSkevlo ((const struct urtwn_type *)usb_lookup(urtwn_devs, v, p)) 34834a2cadeSkevlo 3499ec5c125Sdamien int urtwn_match(struct device *, void *, void *); 3509ec5c125Sdamien void urtwn_attach(struct device *, struct device *, void *); 3519ec5c125Sdamien int urtwn_detach(struct device *, int); 3529ec5c125Sdamien int urtwn_open_pipes(struct urtwn_softc *); 3539ec5c125Sdamien void urtwn_close_pipes(struct urtwn_softc *); 3549ec5c125Sdamien int urtwn_alloc_rx_list(struct urtwn_softc *); 3559ec5c125Sdamien void urtwn_free_rx_list(struct urtwn_softc *); 3569ec5c125Sdamien int urtwn_alloc_tx_list(struct urtwn_softc *); 3579ec5c125Sdamien void urtwn_free_tx_list(struct urtwn_softc *); 3589ec5c125Sdamien void urtwn_task(void *); 3599ec5c125Sdamien void urtwn_do_async(struct urtwn_softc *, 3609ec5c125Sdamien void (*)(struct urtwn_softc *, void *), void *, int); 36123340becSstsp void urtwn_wait_async(void *); 3629ec5c125Sdamien int urtwn_write_region_1(struct urtwn_softc *, uint16_t, uint8_t *, 3639ec5c125Sdamien int); 36423340becSstsp void urtwn_write_1(void *, uint16_t, uint8_t); 36523340becSstsp void urtwn_write_2(void *, uint16_t, uint16_t); 36623340becSstsp void urtwn_write_4(void *, uint16_t, uint32_t); 3679ec5c125Sdamien int urtwn_read_region_1(struct urtwn_softc *, uint16_t, uint8_t *, 3689ec5c125Sdamien int); 36923340becSstsp uint8_t urtwn_read_1(void *, uint16_t); 37023340becSstsp uint16_t urtwn_read_2(void *, uint16_t); 37123340becSstsp uint32_t urtwn_read_4(void *, uint16_t); 3729ec5c125Sdamien int urtwn_llt_write(struct urtwn_softc *, uint32_t, uint32_t); 3739ec5c125Sdamien void urtwn_calib_to(void *); 3749ec5c125Sdamien void urtwn_calib_cb(struct urtwn_softc *, void *); 37523340becSstsp void urtwn_scan_to(void *); 3769ec5c125Sdamien void urtwn_next_scan(void *); 37723340becSstsp void urtwn_cancel_scan(void *); 3789ec5c125Sdamien int urtwn_newstate(struct ieee80211com *, enum ieee80211_state, 3799ec5c125Sdamien int); 3809ec5c125Sdamien void urtwn_newstate_cb(struct urtwn_softc *, void *); 381fb1f57eeSstsp void urtwn_updateslot(struct ieee80211com *); 382fb1f57eeSstsp void urtwn_updateslot_cb(struct urtwn_softc *, void *); 3839ec5c125Sdamien void urtwn_updateedca(struct ieee80211com *); 3849ec5c125Sdamien void urtwn_updateedca_cb(struct urtwn_softc *, void *); 3859ec5c125Sdamien int urtwn_set_key(struct ieee80211com *, struct ieee80211_node *, 3869ec5c125Sdamien struct ieee80211_key *); 3879ec5c125Sdamien void urtwn_set_key_cb(struct urtwn_softc *, void *); 3889ec5c125Sdamien void urtwn_delete_key(struct ieee80211com *, 3899ec5c125Sdamien struct ieee80211_node *, struct ieee80211_key *); 3909ec5c125Sdamien void urtwn_delete_key_cb(struct urtwn_softc *, void *); 3918fbaf8a2Sstsp void urtwn_rx_frame(struct urtwn_softc *, uint8_t *, int, 3928fbaf8a2Sstsp struct mbuf_list *); 393ab0b1be7Smglocker void urtwn_rxeof(struct usbd_xfer *, void *, 3949ec5c125Sdamien usbd_status); 395ab0b1be7Smglocker void urtwn_txeof(struct usbd_xfer *, void *, 3969ec5c125Sdamien usbd_status); 39723340becSstsp int urtwn_tx(void *, struct mbuf *, struct ieee80211_node *); 3989ec5c125Sdamien int urtwn_ioctl(struct ifnet *, u_long, caddr_t); 39923340becSstsp int urtwn_power_on(void *); 40023340becSstsp int urtwn_alloc_buffers(void *); 401c349b8bfSstsp int urtwn_r92c_power_on(struct urtwn_softc *); 40290540544Sjmatthew int urtwn_r92e_power_on(struct urtwn_softc *); 403c349b8bfSstsp int urtwn_r88e_power_on(struct urtwn_softc *); 404067851b1Skevlo int urtwn_r88f_power_on(struct urtwn_softc *); 40508300c66Skevlo int urtwn_llt_init(struct urtwn_softc *, int); 40623340becSstsp int urtwn_fw_loadpage(void *, int, uint8_t *, int); 40723340becSstsp int urtwn_load_firmware(void *, u_char **, size_t *); 40823340becSstsp int urtwn_dma_init(void *); 4092d2a7e26Skevlo void urtwn_aggr_init(void *); 41023340becSstsp void urtwn_mac_init(void *); 41123340becSstsp void urtwn_bb_init(void *); 4122d2a7e26Skevlo void urtwn_burstlen_init(struct urtwn_softc *); 41323340becSstsp int urtwn_init(void *); 41423340becSstsp void urtwn_stop(void *); 41523340becSstsp int urtwn_is_oactive(void *); 41623340becSstsp void urtwn_next_calib(void *); 41723340becSstsp void urtwn_cancel_calib(void *); 4189ec5c125Sdamien 4199ec5c125Sdamien /* Aliases. */ 4209ec5c125Sdamien #define urtwn_bb_write urtwn_write_4 4219ec5c125Sdamien #define urtwn_bb_read urtwn_read_4 4229ec5c125Sdamien 4239ec5c125Sdamien struct cfdriver urtwn_cd = { 4249ec5c125Sdamien NULL, "urtwn", DV_IFNET 4259ec5c125Sdamien }; 4269ec5c125Sdamien 4279ec5c125Sdamien const struct cfattach urtwn_ca = { 42853c6612dSmpi sizeof(struct urtwn_softc), urtwn_match, urtwn_attach, urtwn_detach 4299ec5c125Sdamien }; 4309ec5c125Sdamien 4319ec5c125Sdamien int 4329ec5c125Sdamien urtwn_match(struct device *parent, void *match, void *aux) 4339ec5c125Sdamien { 4349ec5c125Sdamien struct usb_attach_arg *uaa = aux; 4359ec5c125Sdamien 4367ebc5b51Smpi if (uaa->iface == NULL || uaa->configno != 1) 4379ec5c125Sdamien return (UMATCH_NONE); 4389ec5c125Sdamien 43934a2cadeSkevlo return ((urtwn_lookup(uaa->vendor, uaa->product) != NULL) ? 4407ebc5b51Smpi UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE); 4419ec5c125Sdamien } 4429ec5c125Sdamien 4439ec5c125Sdamien void 4449ec5c125Sdamien urtwn_attach(struct device *parent, struct device *self, void *aux) 4459ec5c125Sdamien { 4469ec5c125Sdamien struct urtwn_softc *sc = (struct urtwn_softc *)self; 4479ec5c125Sdamien struct usb_attach_arg *uaa = aux; 44823340becSstsp struct ifnet *ifp; 44923340becSstsp struct ieee80211com *ic = &sc->sc_sc.sc_ic; 4509ec5c125Sdamien 4519ec5c125Sdamien sc->sc_udev = uaa->device; 4527ebc5b51Smpi sc->sc_iface = uaa->iface; 4539ec5c125Sdamien 45434a2cadeSkevlo sc->sc_sc.chip = urtwn_lookup(uaa->vendor, uaa->product)->chip; 45534a2cadeSkevlo 4569ec5c125Sdamien usb_init_task(&sc->sc_task, urtwn_task, sc, USB_TASK_TYPE_GENERIC); 45723340becSstsp timeout_set(&sc->scan_to, urtwn_scan_to, sc); 4589ec5c125Sdamien timeout_set(&sc->calib_to, urtwn_calib_to, sc); 45923340becSstsp if (urtwn_open_pipes(sc) != 0) 46023340becSstsp return; 4619ec5c125Sdamien 462a087613eSstsp sc->amrr.amrr_min_success_threshold = 1; 463a087613eSstsp sc->amrr.amrr_max_success_threshold = 10; 464a087613eSstsp 46523340becSstsp /* Attach the bus-agnostic driver. */ 46623340becSstsp sc->sc_sc.sc_ops.cookie = sc; 46723340becSstsp sc->sc_sc.sc_ops.write_1 = urtwn_write_1; 46823340becSstsp sc->sc_sc.sc_ops.write_2 = urtwn_write_2; 46923340becSstsp sc->sc_sc.sc_ops.write_4 = urtwn_write_4; 47023340becSstsp sc->sc_sc.sc_ops.read_1 = urtwn_read_1; 47123340becSstsp sc->sc_sc.sc_ops.read_2 = urtwn_read_2; 47223340becSstsp sc->sc_sc.sc_ops.read_4 = urtwn_read_4; 47323340becSstsp sc->sc_sc.sc_ops.tx = urtwn_tx; 47423340becSstsp sc->sc_sc.sc_ops.power_on = urtwn_power_on; 47523340becSstsp sc->sc_sc.sc_ops.dma_init = urtwn_dma_init; 47623340becSstsp sc->sc_sc.sc_ops.fw_loadpage = urtwn_fw_loadpage; 47723340becSstsp sc->sc_sc.sc_ops.load_firmware = urtwn_load_firmware; 4782d2a7e26Skevlo sc->sc_sc.sc_ops.aggr_init = urtwn_aggr_init; 47923340becSstsp sc->sc_sc.sc_ops.mac_init = urtwn_mac_init; 48023340becSstsp sc->sc_sc.sc_ops.bb_init = urtwn_bb_init; 48123340becSstsp sc->sc_sc.sc_ops.alloc_buffers = urtwn_alloc_buffers; 48223340becSstsp sc->sc_sc.sc_ops.init = urtwn_init; 48323340becSstsp sc->sc_sc.sc_ops.stop = urtwn_stop; 48423340becSstsp sc->sc_sc.sc_ops.is_oactive = urtwn_is_oactive; 48523340becSstsp sc->sc_sc.sc_ops.next_calib = urtwn_next_calib; 48623340becSstsp sc->sc_sc.sc_ops.cancel_calib = urtwn_cancel_calib; 48723340becSstsp sc->sc_sc.sc_ops.next_scan = urtwn_next_scan; 48823340becSstsp sc->sc_sc.sc_ops.cancel_scan = urtwn_cancel_scan; 48923340becSstsp sc->sc_sc.sc_ops.wait_async = urtwn_wait_async; 49034a2cadeSkevlo if (rtwn_attach(&sc->sc_dev, &sc->sc_sc) != 0) { 49123340becSstsp urtwn_close_pipes(sc); 4929ec5c125Sdamien return; 4939ec5c125Sdamien } 4949ec5c125Sdamien 49523340becSstsp /* ifp is now valid */ 49623340becSstsp ifp = &sc->sc_sc.sc_ic.ic_if; 4979ec5c125Sdamien ifp->if_ioctl = urtwn_ioctl; 4989ec5c125Sdamien 499fb1f57eeSstsp ic->ic_updateslot = urtwn_updateslot; 5009ec5c125Sdamien ic->ic_updateedca = urtwn_updateedca; 5019ec5c125Sdamien ic->ic_set_key = urtwn_set_key; 5029ec5c125Sdamien ic->ic_delete_key = urtwn_delete_key; 5039ec5c125Sdamien /* Override state transition machine. */ 5049ec5c125Sdamien ic->ic_newstate = urtwn_newstate; 5059ec5c125Sdamien 5069ec5c125Sdamien #if NBPFILTER > 0 5079ec5c125Sdamien bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO, 5089ec5c125Sdamien sizeof(struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN); 5099ec5c125Sdamien 5109ec5c125Sdamien sc->sc_rxtap_len = sizeof(sc->sc_rxtapu); 5119ec5c125Sdamien sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); 5129ec5c125Sdamien sc->sc_rxtap.wr_ihdr.it_present = htole32(URTWN_RX_RADIOTAP_PRESENT); 5139ec5c125Sdamien 5149ec5c125Sdamien sc->sc_txtap_len = sizeof(sc->sc_txtapu); 5159ec5c125Sdamien sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); 5169ec5c125Sdamien sc->sc_txtap.wt_ihdr.it_present = htole32(URTWN_TX_RADIOTAP_PRESENT); 5179ec5c125Sdamien #endif 5189ec5c125Sdamien } 5199ec5c125Sdamien 5209ec5c125Sdamien int 5219ec5c125Sdamien urtwn_detach(struct device *self, int flags) 5229ec5c125Sdamien { 5239ec5c125Sdamien struct urtwn_softc *sc = (struct urtwn_softc *)self; 5249ec5c125Sdamien int s; 5259ec5c125Sdamien 5265ad57547Sjakemsr s = splusb(); 5279ec5c125Sdamien 5289ec5c125Sdamien if (timeout_initialized(&sc->scan_to)) 5299ec5c125Sdamien timeout_del(&sc->scan_to); 5309ec5c125Sdamien if (timeout_initialized(&sc->calib_to)) 5319ec5c125Sdamien timeout_del(&sc->calib_to); 5325ad57547Sjakemsr 5335ad57547Sjakemsr /* Wait for all async commands to complete. */ 5345ad57547Sjakemsr usb_rem_wait_task(sc->sc_udev, &sc->sc_task); 5355ad57547Sjakemsr 5365ad57547Sjakemsr usbd_ref_wait(sc->sc_udev); 5375ad57547Sjakemsr 53823340becSstsp rtwn_detach(&sc->sc_sc, flags); 5399ec5c125Sdamien 5409ec5c125Sdamien /* Abort and close Tx/Rx pipes. */ 5419ec5c125Sdamien urtwn_close_pipes(sc); 5429ec5c125Sdamien 5439ec5c125Sdamien /* Free Tx/Rx buffers. */ 5449ec5c125Sdamien urtwn_free_tx_list(sc); 5459ec5c125Sdamien urtwn_free_rx_list(sc); 5469ec5c125Sdamien splx(s); 5479ec5c125Sdamien 5489ec5c125Sdamien return (0); 5499ec5c125Sdamien } 5509ec5c125Sdamien 5519ec5c125Sdamien int 5529ec5c125Sdamien urtwn_open_pipes(struct urtwn_softc *sc) 5539ec5c125Sdamien { 5549ec5c125Sdamien /* Bulk-out endpoints addresses (from highest to lowest prio). */ 55508300c66Skevlo uint8_t epaddr[R92C_MAX_EPOUT] = { 0, 0, 0 }; 55608300c66Skevlo uint8_t rx_no; 5579ec5c125Sdamien usb_interface_descriptor_t *id; 5589ec5c125Sdamien usb_endpoint_descriptor_t *ed; 5590b85f768Sstsp int i, error, nrx = 0; 5609ec5c125Sdamien 56108300c66Skevlo /* Find all bulk endpoints. */ 5629ec5c125Sdamien id = usbd_get_interface_descriptor(sc->sc_iface); 5639ec5c125Sdamien for (i = 0; i < id->bNumEndpoints; i++) { 5649ec5c125Sdamien ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); 56508300c66Skevlo if (ed == NULL || UE_GET_XFERTYPE(ed->bmAttributes) != UE_BULK) 56608300c66Skevlo continue; 56708300c66Skevlo 5680b85f768Sstsp if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) { 56908300c66Skevlo rx_no = ed->bEndpointAddress; 5700b85f768Sstsp nrx++; 5710b85f768Sstsp } else { 57239ab79cfSstsp if (sc->ntx < R92C_MAX_EPOUT) 57308300c66Skevlo epaddr[sc->ntx] = ed->bEndpointAddress; 57408300c66Skevlo sc->ntx++; 5759ec5c125Sdamien } 57608300c66Skevlo } 5770b85f768Sstsp if (nrx == 0) { 5780b85f768Sstsp printf("%s: %d: invalid number of Rx bulk pipes\n", 5790b85f768Sstsp sc->sc_dev.dv_xname, nrx); 5800b85f768Sstsp return (EIO); 5810b85f768Sstsp } 58208300c66Skevlo DPRINTF(("found %d bulk-out pipes\n", sc->ntx)); 58308300c66Skevlo if (sc->ntx == 0 || sc->ntx > R92C_MAX_EPOUT) { 5849ec5c125Sdamien printf("%s: %d: invalid number of Tx bulk pipes\n", 58508300c66Skevlo sc->sc_dev.dv_xname, sc->ntx); 5869ec5c125Sdamien return (EIO); 5879ec5c125Sdamien } 5889ec5c125Sdamien 58908300c66Skevlo /* Open bulk-in pipe. */ 59008300c66Skevlo error = usbd_open_pipe(sc->sc_iface, rx_no, 0, &sc->rx_pipe); 5919ec5c125Sdamien if (error != 0) { 5929ec5c125Sdamien printf("%s: could not open Rx bulk pipe\n", 5939ec5c125Sdamien sc->sc_dev.dv_xname); 5949ec5c125Sdamien goto fail; 5959ec5c125Sdamien } 5969ec5c125Sdamien 5979ec5c125Sdamien /* Open bulk-out pipes (up to 3). */ 59808300c66Skevlo for (i = 0; i < sc->ntx; i++) { 5999ec5c125Sdamien error = usbd_open_pipe(sc->sc_iface, epaddr[i], 0, 6009ec5c125Sdamien &sc->tx_pipe[i]); 6019ec5c125Sdamien if (error != 0) { 6029ec5c125Sdamien printf("%s: could not open Tx bulk pipe 0x%02x\n", 6039ec5c125Sdamien sc->sc_dev.dv_xname, epaddr[i]); 6049ec5c125Sdamien goto fail; 6059ec5c125Sdamien } 6069ec5c125Sdamien } 6079ec5c125Sdamien 6089ec5c125Sdamien /* Map 802.11 access categories to USB pipes. */ 6099ec5c125Sdamien sc->ac2idx[EDCA_AC_BK] = 61008300c66Skevlo sc->ac2idx[EDCA_AC_BE] = (sc->ntx == 3) ? 2 : ((sc->ntx == 2) ? 1 : 0); 61108300c66Skevlo sc->ac2idx[EDCA_AC_VI] = (sc->ntx == 3) ? 1 : 0; 6129ec5c125Sdamien sc->ac2idx[EDCA_AC_VO] = 0; /* Always use highest prio. */ 6139ec5c125Sdamien 6149ec5c125Sdamien if (error != 0) 6159ec5c125Sdamien fail: urtwn_close_pipes(sc); 6169ec5c125Sdamien return (error); 6179ec5c125Sdamien } 6189ec5c125Sdamien 6199ec5c125Sdamien void 6209ec5c125Sdamien urtwn_close_pipes(struct urtwn_softc *sc) 6219ec5c125Sdamien { 6229ec5c125Sdamien int i; 6239ec5c125Sdamien 6249ec5c125Sdamien /* Close Rx pipe. */ 6258bca19d0Skevlo if (sc->rx_pipe != NULL) { 6269ec5c125Sdamien usbd_close_pipe(sc->rx_pipe); 6278bca19d0Skevlo sc->rx_pipe = NULL; 6288bca19d0Skevlo } 6299ec5c125Sdamien /* Close Tx pipes. */ 6309ec5c125Sdamien for (i = 0; i < R92C_MAX_EPOUT; i++) { 6319ec5c125Sdamien if (sc->tx_pipe[i] == NULL) 6329ec5c125Sdamien continue; 6339ec5c125Sdamien usbd_close_pipe(sc->tx_pipe[i]); 6348bca19d0Skevlo sc->tx_pipe[i] = NULL; 6359ec5c125Sdamien } 6369ec5c125Sdamien } 6379ec5c125Sdamien 6389ec5c125Sdamien int 6399ec5c125Sdamien urtwn_alloc_rx_list(struct urtwn_softc *sc) 6409ec5c125Sdamien { 6419ec5c125Sdamien struct urtwn_rx_data *data; 6429ec5c125Sdamien int i, error = 0; 6439ec5c125Sdamien 6449ec5c125Sdamien for (i = 0; i < URTWN_RX_LIST_COUNT; i++) { 6459ec5c125Sdamien data = &sc->rx_data[i]; 6469ec5c125Sdamien 6479ec5c125Sdamien data->sc = sc; /* Backpointer for callbacks. */ 6489ec5c125Sdamien 6499ec5c125Sdamien data->xfer = usbd_alloc_xfer(sc->sc_udev); 6509ec5c125Sdamien if (data->xfer == NULL) { 6519ec5c125Sdamien printf("%s: could not allocate xfer\n", 6529ec5c125Sdamien sc->sc_dev.dv_xname); 6539ec5c125Sdamien error = ENOMEM; 6549ec5c125Sdamien break; 6559ec5c125Sdamien } 6569ec5c125Sdamien data->buf = usbd_alloc_buffer(data->xfer, URTWN_RXBUFSZ); 6579ec5c125Sdamien if (data->buf == NULL) { 6589ec5c125Sdamien printf("%s: could not allocate xfer buffer\n", 6599ec5c125Sdamien sc->sc_dev.dv_xname); 6609ec5c125Sdamien error = ENOMEM; 6619ec5c125Sdamien break; 6629ec5c125Sdamien } 6639ec5c125Sdamien } 6649ec5c125Sdamien if (error != 0) 6659ec5c125Sdamien urtwn_free_rx_list(sc); 6669ec5c125Sdamien return (error); 6679ec5c125Sdamien } 6689ec5c125Sdamien 6699ec5c125Sdamien void 6709ec5c125Sdamien urtwn_free_rx_list(struct urtwn_softc *sc) 6719ec5c125Sdamien { 6729ec5c125Sdamien int i; 6739ec5c125Sdamien 6749ec5c125Sdamien /* NB: Caller must abort pipe first. */ 6759ec5c125Sdamien for (i = 0; i < URTWN_RX_LIST_COUNT; i++) { 6769ec5c125Sdamien if (sc->rx_data[i].xfer != NULL) 6779ec5c125Sdamien usbd_free_xfer(sc->rx_data[i].xfer); 6789ec5c125Sdamien sc->rx_data[i].xfer = NULL; 6799ec5c125Sdamien } 6809ec5c125Sdamien } 6819ec5c125Sdamien 6829ec5c125Sdamien int 6839ec5c125Sdamien urtwn_alloc_tx_list(struct urtwn_softc *sc) 6849ec5c125Sdamien { 6859ec5c125Sdamien struct urtwn_tx_data *data; 6869ec5c125Sdamien int i, error = 0; 6879ec5c125Sdamien 6889ec5c125Sdamien TAILQ_INIT(&sc->tx_free_list); 6899ec5c125Sdamien for (i = 0; i < URTWN_TX_LIST_COUNT; i++) { 6909ec5c125Sdamien data = &sc->tx_data[i]; 6919ec5c125Sdamien 6929ec5c125Sdamien data->sc = sc; /* Backpointer for callbacks. */ 6939ec5c125Sdamien 6949ec5c125Sdamien data->xfer = usbd_alloc_xfer(sc->sc_udev); 6959ec5c125Sdamien if (data->xfer == NULL) { 6969ec5c125Sdamien printf("%s: could not allocate xfer\n", 6979ec5c125Sdamien sc->sc_dev.dv_xname); 6989ec5c125Sdamien error = ENOMEM; 6999ec5c125Sdamien break; 7009ec5c125Sdamien } 7019ec5c125Sdamien data->buf = usbd_alloc_buffer(data->xfer, URTWN_TXBUFSZ); 7029ec5c125Sdamien if (data->buf == NULL) { 7039ec5c125Sdamien printf("%s: could not allocate xfer buffer\n", 7049ec5c125Sdamien sc->sc_dev.dv_xname); 7059ec5c125Sdamien error = ENOMEM; 7069ec5c125Sdamien break; 7079ec5c125Sdamien } 7089ec5c125Sdamien /* Append this Tx buffer to our free list. */ 7099ec5c125Sdamien TAILQ_INSERT_TAIL(&sc->tx_free_list, data, next); 7109ec5c125Sdamien } 7119ec5c125Sdamien if (error != 0) 7129ec5c125Sdamien urtwn_free_tx_list(sc); 7139ec5c125Sdamien return (error); 7149ec5c125Sdamien } 7159ec5c125Sdamien 7169ec5c125Sdamien void 7179ec5c125Sdamien urtwn_free_tx_list(struct urtwn_softc *sc) 7189ec5c125Sdamien { 7199ec5c125Sdamien int i; 7209ec5c125Sdamien 7219ec5c125Sdamien /* NB: Caller must abort pipe first. */ 7229ec5c125Sdamien for (i = 0; i < URTWN_TX_LIST_COUNT; i++) { 7239ec5c125Sdamien if (sc->tx_data[i].xfer != NULL) 7249ec5c125Sdamien usbd_free_xfer(sc->tx_data[i].xfer); 7259ec5c125Sdamien sc->tx_data[i].xfer = NULL; 7269ec5c125Sdamien } 7279ec5c125Sdamien } 7289ec5c125Sdamien 7299ec5c125Sdamien void 7309ec5c125Sdamien urtwn_task(void *arg) 7319ec5c125Sdamien { 7329ec5c125Sdamien struct urtwn_softc *sc = arg; 7339ec5c125Sdamien struct urtwn_host_cmd_ring *ring = &sc->cmdq; 7349ec5c125Sdamien struct urtwn_host_cmd *cmd; 7359ec5c125Sdamien int s; 7369ec5c125Sdamien 7379ec5c125Sdamien /* Process host commands. */ 7389ec5c125Sdamien s = splusb(); 7399ec5c125Sdamien while (ring->next != ring->cur) { 7409ec5c125Sdamien cmd = &ring->cmd[ring->next]; 7419ec5c125Sdamien splx(s); 7429ec5c125Sdamien /* Invoke callback. */ 7439ec5c125Sdamien cmd->cb(sc, cmd->data); 7449ec5c125Sdamien s = splusb(); 7459ec5c125Sdamien ring->queued--; 7469ec5c125Sdamien ring->next = (ring->next + 1) % URTWN_HOST_CMD_RING_COUNT; 7479ec5c125Sdamien } 7489ec5c125Sdamien splx(s); 7499ec5c125Sdamien } 7509ec5c125Sdamien 7519ec5c125Sdamien void 7529ec5c125Sdamien urtwn_do_async(struct urtwn_softc *sc, 7539ec5c125Sdamien void (*cb)(struct urtwn_softc *, void *), void *arg, int len) 7549ec5c125Sdamien { 7559ec5c125Sdamien struct urtwn_host_cmd_ring *ring = &sc->cmdq; 7569ec5c125Sdamien struct urtwn_host_cmd *cmd; 7579ec5c125Sdamien int s; 7589ec5c125Sdamien 7599ec5c125Sdamien s = splusb(); 7609ec5c125Sdamien cmd = &ring->cmd[ring->cur]; 7619ec5c125Sdamien cmd->cb = cb; 7629ec5c125Sdamien KASSERT(len <= sizeof(cmd->data)); 7639ec5c125Sdamien memcpy(cmd->data, arg, len); 7649ec5c125Sdamien ring->cur = (ring->cur + 1) % URTWN_HOST_CMD_RING_COUNT; 7659ec5c125Sdamien 7669ec5c125Sdamien /* If there is no pending command already, schedule a task. */ 7679ec5c125Sdamien if (++ring->queued == 1) 7689ec5c125Sdamien usb_add_task(sc->sc_udev, &sc->sc_task); 7699ec5c125Sdamien splx(s); 7709ec5c125Sdamien } 7719ec5c125Sdamien 7729ec5c125Sdamien void 77323340becSstsp urtwn_wait_async(void *cookie) 7749ec5c125Sdamien { 77523340becSstsp struct urtwn_softc *sc = cookie; 77623340becSstsp int s; 77723340becSstsp 77823340becSstsp s = splusb(); 7799ec5c125Sdamien /* Wait for all queued asynchronous commands to complete. */ 7805ad57547Sjakemsr usb_wait_task(sc->sc_udev, &sc->sc_task); 78123340becSstsp splx(s); 7829ec5c125Sdamien } 7839ec5c125Sdamien 7849ec5c125Sdamien int 7859ec5c125Sdamien urtwn_write_region_1(struct urtwn_softc *sc, uint16_t addr, uint8_t *buf, 7869ec5c125Sdamien int len) 7879ec5c125Sdamien { 7889ec5c125Sdamien usb_device_request_t req; 7899ec5c125Sdamien 7909ec5c125Sdamien req.bmRequestType = UT_WRITE_VENDOR_DEVICE; 7919ec5c125Sdamien req.bRequest = R92C_REQ_REGS; 7929ec5c125Sdamien USETW(req.wValue, addr); 7939ec5c125Sdamien USETW(req.wIndex, 0); 7949ec5c125Sdamien USETW(req.wLength, len); 7959ec5c125Sdamien return (usbd_do_request(sc->sc_udev, &req, buf)); 7969ec5c125Sdamien } 7979ec5c125Sdamien 7989ec5c125Sdamien void 79923340becSstsp urtwn_write_1(void *cookie, uint16_t addr, uint8_t val) 8009ec5c125Sdamien { 80123340becSstsp struct urtwn_softc *sc = cookie; 80223340becSstsp 8039ec5c125Sdamien urtwn_write_region_1(sc, addr, &val, 1); 8049ec5c125Sdamien } 8059ec5c125Sdamien 8069ec5c125Sdamien void 80723340becSstsp urtwn_write_2(void *cookie, uint16_t addr, uint16_t val) 8089ec5c125Sdamien { 80923340becSstsp struct urtwn_softc *sc = cookie; 81023340becSstsp 8119ec5c125Sdamien val = htole16(val); 8129ec5c125Sdamien urtwn_write_region_1(sc, addr, (uint8_t *)&val, 2); 8139ec5c125Sdamien } 8149ec5c125Sdamien 8159ec5c125Sdamien void 81623340becSstsp urtwn_write_4(void *cookie, uint16_t addr, uint32_t val) 8179ec5c125Sdamien { 81823340becSstsp struct urtwn_softc *sc = cookie; 81923340becSstsp 8209ec5c125Sdamien val = htole32(val); 8219ec5c125Sdamien urtwn_write_region_1(sc, addr, (uint8_t *)&val, 4); 8229ec5c125Sdamien } 8239ec5c125Sdamien 8249ec5c125Sdamien int 8259ec5c125Sdamien urtwn_read_region_1(struct urtwn_softc *sc, uint16_t addr, uint8_t *buf, 8269ec5c125Sdamien int len) 8279ec5c125Sdamien { 8289ec5c125Sdamien usb_device_request_t req; 8299ec5c125Sdamien 8309ec5c125Sdamien req.bmRequestType = UT_READ_VENDOR_DEVICE; 8319ec5c125Sdamien req.bRequest = R92C_REQ_REGS; 8329ec5c125Sdamien USETW(req.wValue, addr); 8339ec5c125Sdamien USETW(req.wIndex, 0); 8349ec5c125Sdamien USETW(req.wLength, len); 8359ec5c125Sdamien return (usbd_do_request(sc->sc_udev, &req, buf)); 8369ec5c125Sdamien } 8379ec5c125Sdamien 8389ec5c125Sdamien uint8_t 83923340becSstsp urtwn_read_1(void *cookie, uint16_t addr) 8409ec5c125Sdamien { 84123340becSstsp struct urtwn_softc *sc = cookie; 8429ec5c125Sdamien uint8_t val; 8439ec5c125Sdamien 8449ec5c125Sdamien if (urtwn_read_region_1(sc, addr, &val, 1) != 0) 8459ec5c125Sdamien return (0xff); 8469ec5c125Sdamien return (val); 8479ec5c125Sdamien } 8489ec5c125Sdamien 8499ec5c125Sdamien uint16_t 85023340becSstsp urtwn_read_2(void *cookie, uint16_t addr) 8519ec5c125Sdamien { 85223340becSstsp struct urtwn_softc *sc = cookie; 8539ec5c125Sdamien uint16_t val; 8549ec5c125Sdamien 8559ec5c125Sdamien if (urtwn_read_region_1(sc, addr, (uint8_t *)&val, 2) != 0) 8569ec5c125Sdamien return (0xffff); 8579ec5c125Sdamien return (letoh16(val)); 8589ec5c125Sdamien } 8599ec5c125Sdamien 8609ec5c125Sdamien uint32_t 86123340becSstsp urtwn_read_4(void *cookie, uint16_t addr) 8629ec5c125Sdamien { 86323340becSstsp struct urtwn_softc *sc = cookie; 8649ec5c125Sdamien uint32_t val; 8659ec5c125Sdamien 8669ec5c125Sdamien if (urtwn_read_region_1(sc, addr, (uint8_t *)&val, 4) != 0) 8679ec5c125Sdamien return (0xffffffff); 8689ec5c125Sdamien return (letoh32(val)); 8699ec5c125Sdamien } 8709ec5c125Sdamien 8719ec5c125Sdamien int 8729ec5c125Sdamien urtwn_llt_write(struct urtwn_softc *sc, uint32_t addr, uint32_t data) 8739ec5c125Sdamien { 8749ec5c125Sdamien int ntries; 8759ec5c125Sdamien 8769ec5c125Sdamien urtwn_write_4(sc, R92C_LLT_INIT, 8779ec5c125Sdamien SM(R92C_LLT_INIT_OP, R92C_LLT_INIT_OP_WRITE) | 8789ec5c125Sdamien SM(R92C_LLT_INIT_ADDR, addr) | 8799ec5c125Sdamien SM(R92C_LLT_INIT_DATA, data)); 8809ec5c125Sdamien /* Wait for write operation to complete. */ 8819ec5c125Sdamien for (ntries = 0; ntries < 20; ntries++) { 8829ec5c125Sdamien if (MS(urtwn_read_4(sc, R92C_LLT_INIT), R92C_LLT_INIT_OP) == 8839ec5c125Sdamien R92C_LLT_INIT_OP_NO_ACTIVE) 8849ec5c125Sdamien return (0); 8859ec5c125Sdamien DELAY(5); 8869ec5c125Sdamien } 8879ec5c125Sdamien return (ETIMEDOUT); 8889ec5c125Sdamien } 8899ec5c125Sdamien 8909ec5c125Sdamien void 8919ec5c125Sdamien urtwn_calib_to(void *arg) 8929ec5c125Sdamien { 8939ec5c125Sdamien struct urtwn_softc *sc = arg; 8949ec5c125Sdamien 8955ad57547Sjakemsr if (usbd_is_dying(sc->sc_udev)) 8965ad57547Sjakemsr return; 8975ad57547Sjakemsr 8985ad57547Sjakemsr usbd_ref_incr(sc->sc_udev); 8995ad57547Sjakemsr 9009ec5c125Sdamien /* Do it in a process context. */ 9019ec5c125Sdamien urtwn_do_async(sc, urtwn_calib_cb, NULL, 0); 9025ad57547Sjakemsr 9035ad57547Sjakemsr usbd_ref_decr(sc->sc_udev); 9049ec5c125Sdamien } 9059ec5c125Sdamien 9069ec5c125Sdamien void 9079ec5c125Sdamien urtwn_calib_cb(struct urtwn_softc *sc, void *arg) 9089ec5c125Sdamien { 909a087613eSstsp struct ieee80211com *ic = &sc->sc_sc.sc_ic; 910a087613eSstsp int s; 911a087613eSstsp 912a087613eSstsp s = splnet(); 913a087613eSstsp if (ic->ic_opmode == IEEE80211_M_STA) { 914a087613eSstsp ieee80211_amrr_choose(&sc->amrr, ic->ic_bss, &sc->amn); 915a087613eSstsp } 916a087613eSstsp splx(s); 917a087613eSstsp 91823340becSstsp rtwn_calib(&sc->sc_sc); 9199ec5c125Sdamien } 9209ec5c125Sdamien 92123340becSstsp void 92223340becSstsp urtwn_next_calib(void *cookie) 92323340becSstsp { 92423340becSstsp struct urtwn_softc *sc = cookie; 9255e709a81Sdamien 9265ad57547Sjakemsr if (!usbd_is_dying(sc->sc_udev)) 9279ec5c125Sdamien timeout_add_sec(&sc->calib_to, 2); 9289ec5c125Sdamien } 9299ec5c125Sdamien 9309ec5c125Sdamien void 93123340becSstsp urtwn_cancel_calib(void *cookie) 93223340becSstsp { 93323340becSstsp struct urtwn_softc *sc = cookie; 93423340becSstsp 93523340becSstsp if (timeout_initialized(&sc->calib_to)) 93623340becSstsp timeout_del(&sc->calib_to); 93723340becSstsp } 93823340becSstsp 93923340becSstsp void 94023340becSstsp urtwn_scan_to(void *arg) 9419ec5c125Sdamien { 9429ec5c125Sdamien struct urtwn_softc *sc = arg; 9439ec5c125Sdamien 9445ad57547Sjakemsr if (usbd_is_dying(sc->sc_udev)) 9455ad57547Sjakemsr return; 9465ad57547Sjakemsr 9475ad57547Sjakemsr usbd_ref_incr(sc->sc_udev); 94823340becSstsp rtwn_next_scan(&sc->sc_sc); 9495ad57547Sjakemsr usbd_ref_decr(sc->sc_udev); 9509ec5c125Sdamien } 9519ec5c125Sdamien 95223340becSstsp void 95323340becSstsp urtwn_next_scan(void *arg) 95423340becSstsp { 95523340becSstsp struct urtwn_softc *sc = arg; 95623340becSstsp 95723340becSstsp if (!usbd_is_dying(sc->sc_udev)) 95823340becSstsp timeout_add_msec(&sc->scan_to, 200); 95923340becSstsp } 96023340becSstsp 96123340becSstsp void 96223340becSstsp urtwn_cancel_scan(void *cookie) 96323340becSstsp { 96423340becSstsp struct urtwn_softc *sc = cookie; 96523340becSstsp 96623340becSstsp if (timeout_initialized(&sc->scan_to)) 96723340becSstsp timeout_del(&sc->scan_to); 96823340becSstsp } 96923340becSstsp 9709ec5c125Sdamien int 9719ec5c125Sdamien urtwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 9729ec5c125Sdamien { 97323340becSstsp struct rtwn_softc *sc_sc = ic->ic_softc; 97423340becSstsp struct device *self = sc_sc->sc_pdev; 97523340becSstsp struct urtwn_softc *sc = (struct urtwn_softc *)self; 9769ec5c125Sdamien struct urtwn_cmd_newstate cmd; 9779ec5c125Sdamien 9789ec5c125Sdamien /* Do it in a process context. */ 9799ec5c125Sdamien cmd.state = nstate; 9809ec5c125Sdamien cmd.arg = arg; 9819ec5c125Sdamien urtwn_do_async(sc, urtwn_newstate_cb, &cmd, sizeof(cmd)); 9829ec5c125Sdamien return (0); 9839ec5c125Sdamien } 9849ec5c125Sdamien 9859ec5c125Sdamien void 9869ec5c125Sdamien urtwn_newstate_cb(struct urtwn_softc *sc, void *arg) 9879ec5c125Sdamien { 9889ec5c125Sdamien struct urtwn_cmd_newstate *cmd = arg; 98923340becSstsp struct ieee80211com *ic = &sc->sc_sc.sc_ic; 9909ec5c125Sdamien 99123340becSstsp rtwn_newstate(ic, cmd->state, cmd->arg); 9929ec5c125Sdamien } 9939ec5c125Sdamien 9949ec5c125Sdamien void 995fb1f57eeSstsp urtwn_updateslot(struct ieee80211com *ic) 996fb1f57eeSstsp { 997fb1f57eeSstsp struct rtwn_softc *sc_sc = ic->ic_softc; 998fb1f57eeSstsp struct device *self = sc_sc->sc_pdev; 999fb1f57eeSstsp struct urtwn_softc *sc = (struct urtwn_softc *)self; 1000fb1f57eeSstsp 1001fb1f57eeSstsp /* Do it in a process context. */ 1002fb1f57eeSstsp urtwn_do_async(sc, urtwn_updateslot_cb, NULL, 0); 1003fb1f57eeSstsp } 1004fb1f57eeSstsp 1005fb1f57eeSstsp void 1006fb1f57eeSstsp urtwn_updateslot_cb(struct urtwn_softc *sc, void *arg) 1007fb1f57eeSstsp { 1008fb1f57eeSstsp struct ieee80211com *ic = &sc->sc_sc.sc_ic; 1009fb1f57eeSstsp 1010fb1f57eeSstsp rtwn_updateslot(ic); 1011fb1f57eeSstsp } 1012fb1f57eeSstsp 1013fb1f57eeSstsp void 10149ec5c125Sdamien urtwn_updateedca(struct ieee80211com *ic) 10159ec5c125Sdamien { 101623340becSstsp struct rtwn_softc *sc_sc = ic->ic_softc; 101723340becSstsp struct device *self = sc_sc->sc_pdev; 101823340becSstsp struct urtwn_softc *sc = (struct urtwn_softc *)self; 101923340becSstsp 10209ec5c125Sdamien /* Do it in a process context. */ 102123340becSstsp urtwn_do_async(sc, urtwn_updateedca_cb, NULL, 0); 10229ec5c125Sdamien } 10239ec5c125Sdamien 10249ec5c125Sdamien void 10259ec5c125Sdamien urtwn_updateedca_cb(struct urtwn_softc *sc, void *arg) 10269ec5c125Sdamien { 102723340becSstsp struct ieee80211com *ic = &sc->sc_sc.sc_ic; 10289ec5c125Sdamien 102923340becSstsp rtwn_updateedca(ic); 10309ec5c125Sdamien } 10319ec5c125Sdamien 10329ec5c125Sdamien int 10339ec5c125Sdamien urtwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, 10349ec5c125Sdamien struct ieee80211_key *k) 10359ec5c125Sdamien { 103623340becSstsp struct rtwn_softc *sc_sc = ic->ic_softc; 103723340becSstsp struct device *self = sc_sc->sc_pdev; 103823340becSstsp struct urtwn_softc *sc = (struct urtwn_softc *)self; 10399ec5c125Sdamien struct urtwn_cmd_key cmd; 10409ec5c125Sdamien 1041a299d652Sjmatthew /* Only handle keys for CCMP */ 1042a299d652Sjmatthew if (k->k_cipher != IEEE80211_CIPHER_CCMP) 1043a299d652Sjmatthew return ieee80211_set_key(ic, ni, k); 1044a299d652Sjmatthew 10459ec5c125Sdamien /* Defer setting of WEP keys until interface is brought up. */ 10469ec5c125Sdamien if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) != 10479ec5c125Sdamien (IFF_UP | IFF_RUNNING)) 10489ec5c125Sdamien return (0); 10499ec5c125Sdamien 10509ec5c125Sdamien /* Do it in a process context. */ 10519ec5c125Sdamien cmd.key = *k; 105223340becSstsp cmd.ni = ni; 10539ec5c125Sdamien urtwn_do_async(sc, urtwn_set_key_cb, &cmd, sizeof(cmd)); 10542362e6a4Skrw sc->sc_key_tasks++; 10552362e6a4Skrw 10562362e6a4Skrw return (EBUSY); 10579ec5c125Sdamien } 10589ec5c125Sdamien 10599ec5c125Sdamien void 10609ec5c125Sdamien urtwn_set_key_cb(struct urtwn_softc *sc, void *arg) 10619ec5c125Sdamien { 106223340becSstsp struct ieee80211com *ic = &sc->sc_sc.sc_ic; 10639ec5c125Sdamien struct urtwn_cmd_key *cmd = arg; 10649ec5c125Sdamien 10652362e6a4Skrw sc->sc_key_tasks--; 10662362e6a4Skrw 10672362e6a4Skrw if (rtwn_set_key(ic, cmd->ni, &cmd->key) == 0) { 10682362e6a4Skrw if (sc->sc_key_tasks == 0) { 10692362e6a4Skrw DPRINTF(("marking port %s valid\n", 10702362e6a4Skrw ether_sprintf(cmd->ni->ni_macaddr))); 10712362e6a4Skrw cmd->ni->ni_port_valid = 1; 10722362e6a4Skrw ieee80211_set_link_state(ic, LINK_STATE_UP); 10732362e6a4Skrw } 10742362e6a4Skrw } else { 10752362e6a4Skrw IEEE80211_SEND_MGMT(ic, cmd->ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 10762362e6a4Skrw IEEE80211_REASON_AUTH_LEAVE); 10772362e6a4Skrw ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 10782362e6a4Skrw } 10799ec5c125Sdamien } 10809ec5c125Sdamien 10819ec5c125Sdamien void 10829ec5c125Sdamien urtwn_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, 10839ec5c125Sdamien struct ieee80211_key *k) 10849ec5c125Sdamien { 108523340becSstsp struct rtwn_softc *sc_sc = ic->ic_softc; 108623340becSstsp struct device *self = sc_sc->sc_pdev; 108723340becSstsp struct urtwn_softc *sc = (struct urtwn_softc *)self; 10889ec5c125Sdamien struct urtwn_cmd_key cmd; 10899ec5c125Sdamien 1090a299d652Sjmatthew /* Only handle keys for CCMP */ 1091a299d652Sjmatthew if (k->k_cipher != IEEE80211_CIPHER_CCMP) { 1092a299d652Sjmatthew ieee80211_delete_key(ic, ni, k); 1093a299d652Sjmatthew return; 1094a299d652Sjmatthew } 1095a299d652Sjmatthew 10969ec5c125Sdamien if (!(ic->ic_if.if_flags & IFF_RUNNING) || 10979ec5c125Sdamien ic->ic_state != IEEE80211_S_RUN) 10989ec5c125Sdamien return; /* Nothing to do. */ 10999ec5c125Sdamien 11009ec5c125Sdamien /* Do it in a process context. */ 11019ec5c125Sdamien cmd.key = *k; 110223340becSstsp cmd.ni = ni; 11039ec5c125Sdamien urtwn_do_async(sc, urtwn_delete_key_cb, &cmd, sizeof(cmd)); 11049ec5c125Sdamien } 11059ec5c125Sdamien 11069ec5c125Sdamien void 11079ec5c125Sdamien urtwn_delete_key_cb(struct urtwn_softc *sc, void *arg) 11089ec5c125Sdamien { 110923340becSstsp struct ieee80211com *ic = &sc->sc_sc.sc_ic; 11109ec5c125Sdamien struct urtwn_cmd_key *cmd = arg; 11119ec5c125Sdamien 111223340becSstsp rtwn_delete_key(ic, cmd->ni, &cmd->key); 1113c349b8bfSstsp } 1114c349b8bfSstsp 1115a299d652Sjmatthew int 1116a299d652Sjmatthew urtwn_ccmp_decap(struct urtwn_softc *sc, struct mbuf *m, 1117a299d652Sjmatthew struct ieee80211_node *ni) 1118a299d652Sjmatthew { 1119a299d652Sjmatthew struct ieee80211com *ic = &sc->sc_sc.sc_ic; 1120a299d652Sjmatthew struct ieee80211_key *k; 1121a299d652Sjmatthew struct ieee80211_frame *wh; 1122a299d652Sjmatthew uint64_t pn, *prsc; 1123a299d652Sjmatthew uint8_t *ivp; 1124a299d652Sjmatthew uint8_t tid; 1125a299d652Sjmatthew int hdrlen, hasqos; 1126a299d652Sjmatthew 1127a299d652Sjmatthew k = ieee80211_get_rxkey(ic, m, ni); 1128a299d652Sjmatthew if (k == NULL) 1129a299d652Sjmatthew return 1; 1130a299d652Sjmatthew 1131a299d652Sjmatthew wh = mtod(m, struct ieee80211_frame *); 1132a299d652Sjmatthew hdrlen = ieee80211_get_hdrlen(wh); 1133a299d652Sjmatthew ivp = (uint8_t *)wh + hdrlen; 1134a299d652Sjmatthew 1135a299d652Sjmatthew /* Check that ExtIV bit is set. */ 1136a299d652Sjmatthew if (!(ivp[3] & IEEE80211_WEP_EXTIV)) 1137a299d652Sjmatthew return 1; 1138a299d652Sjmatthew 1139a299d652Sjmatthew hasqos = ieee80211_has_qos(wh); 1140a299d652Sjmatthew tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0; 1141a299d652Sjmatthew prsc = &k->k_rsc[tid]; 1142a299d652Sjmatthew 1143a299d652Sjmatthew /* Extract the 48-bit PN from the CCMP header. */ 1144a299d652Sjmatthew pn = (uint64_t)ivp[0] | 1145a299d652Sjmatthew (uint64_t)ivp[1] << 8 | 1146a299d652Sjmatthew (uint64_t)ivp[4] << 16 | 1147a299d652Sjmatthew (uint64_t)ivp[5] << 24 | 1148a299d652Sjmatthew (uint64_t)ivp[6] << 32 | 1149a299d652Sjmatthew (uint64_t)ivp[7] << 40; 1150a299d652Sjmatthew if (pn <= *prsc) { 1151a299d652Sjmatthew ic->ic_stats.is_ccmp_replays++; 1152a299d652Sjmatthew return 1; 1153a299d652Sjmatthew } 1154a299d652Sjmatthew /* Last seen packet number is updated in ieee80211_inputm(). */ 1155a299d652Sjmatthew 1156a299d652Sjmatthew /* Strip MIC. IV will be stripped by ieee80211_inputm(). */ 1157a299d652Sjmatthew m_adj(m, -IEEE80211_CCMP_MICLEN); 1158a299d652Sjmatthew return 0; 1159a299d652Sjmatthew } 1160a299d652Sjmatthew 11619ec5c125Sdamien void 11628fbaf8a2Sstsp urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen, 11638fbaf8a2Sstsp struct mbuf_list *ml) 11649ec5c125Sdamien { 116523340becSstsp struct ieee80211com *ic = &sc->sc_sc.sc_ic; 11669ec5c125Sdamien struct ifnet *ifp = &ic->ic_if; 11679ec5c125Sdamien struct ieee80211_rxinfo rxi; 11689ec5c125Sdamien struct ieee80211_frame *wh; 11699ec5c125Sdamien struct ieee80211_node *ni; 11708caabc14Sstsp struct r92c_rx_desc_usb *rxd; 11719ec5c125Sdamien uint32_t rxdw0, rxdw3; 11729ec5c125Sdamien struct mbuf *m; 11739ec5c125Sdamien uint8_t rate; 11749ec5c125Sdamien int8_t rssi = 0; 11759ec5c125Sdamien int s, infosz; 11769ec5c125Sdamien 11778caabc14Sstsp rxd = (struct r92c_rx_desc_usb *)buf; 11788caabc14Sstsp rxdw0 = letoh32(rxd->rxdw0); 11798caabc14Sstsp rxdw3 = letoh32(rxd->rxdw3); 11809ec5c125Sdamien 11819ec5c125Sdamien if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) { 11829ec5c125Sdamien /* 11839ec5c125Sdamien * This should not happen since we setup our Rx filter 11849ec5c125Sdamien * to not receive these frames. 11859ec5c125Sdamien */ 11869ec5c125Sdamien ifp->if_ierrors++; 11879ec5c125Sdamien return; 11889ec5c125Sdamien } 1189882a6c16Sdamien if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) { 11909ec5c125Sdamien ifp->if_ierrors++; 11919ec5c125Sdamien return; 11929ec5c125Sdamien } 11939ec5c125Sdamien 1194067851b1Skevlo rate = (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) ? 1195969fab7dSkevlo MS(rxdw3, R92E_RXDW3_RATE) : MS(rxdw3, R92C_RXDW3_RATE); 11969ec5c125Sdamien infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; 11979ec5c125Sdamien 11989ec5c125Sdamien /* Get RSSI from PHY status descriptor if present. */ 11999ec5c125Sdamien if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) { 120023340becSstsp rssi = rtwn_get_rssi(&sc->sc_sc, rate, &rxd[1]); 12019ec5c125Sdamien /* Update our average RSSI. */ 120223340becSstsp rtwn_update_avgrssi(&sc->sc_sc, rate, rssi); 12039ec5c125Sdamien } 12049ec5c125Sdamien 12059ec5c125Sdamien DPRINTFN(5, ("Rx frame len=%d rate=%d infosz=%d rssi=%d\n", 12069ec5c125Sdamien pktlen, rate, infosz, rssi)); 12079ec5c125Sdamien 12089ec5c125Sdamien MGETHDR(m, M_DONTWAIT, MT_DATA); 12099ec5c125Sdamien if (__predict_false(m == NULL)) { 12109ec5c125Sdamien ifp->if_ierrors++; 12119ec5c125Sdamien return; 12129ec5c125Sdamien } 12139ec5c125Sdamien if (pktlen > MHLEN) { 12149ec5c125Sdamien MCLGET(m, M_DONTWAIT); 12159ec5c125Sdamien if (__predict_false(!(m->m_flags & M_EXT))) { 12169ec5c125Sdamien ifp->if_ierrors++; 12179ec5c125Sdamien m_freem(m); 12189ec5c125Sdamien return; 12199ec5c125Sdamien } 12209ec5c125Sdamien } 12219ec5c125Sdamien /* Finalize mbuf. */ 12228caabc14Sstsp wh = (struct ieee80211_frame *)((uint8_t *)&rxd[1] + infosz); 12239ec5c125Sdamien memcpy(mtod(m, uint8_t *), wh, pktlen); 12249ec5c125Sdamien m->m_pkthdr.len = m->m_len = pktlen; 12259ec5c125Sdamien 12269ec5c125Sdamien s = splnet(); 12279ec5c125Sdamien #if NBPFILTER > 0 12289ec5c125Sdamien if (__predict_false(sc->sc_drvbpf != NULL)) { 12299ec5c125Sdamien struct urtwn_rx_radiotap_header *tap = &sc->sc_rxtap; 12309ec5c125Sdamien struct mbuf mb; 12319ec5c125Sdamien 12329ec5c125Sdamien tap->wr_flags = 0; 12339ec5c125Sdamien /* Map HW rate index to 802.11 rate. */ 12349ec5c125Sdamien if (!(rxdw3 & R92C_RXDW3_HT)) { 12359ec5c125Sdamien switch (rate) { 12369ec5c125Sdamien /* CCK. */ 12379ec5c125Sdamien case 0: tap->wr_rate = 2; break; 12389ec5c125Sdamien case 1: tap->wr_rate = 4; break; 12399ec5c125Sdamien case 2: tap->wr_rate = 11; break; 12409ec5c125Sdamien case 3: tap->wr_rate = 22; break; 12419ec5c125Sdamien /* OFDM. */ 12429ec5c125Sdamien case 4: tap->wr_rate = 12; break; 12439ec5c125Sdamien case 5: tap->wr_rate = 18; break; 12449ec5c125Sdamien case 6: tap->wr_rate = 24; break; 12459ec5c125Sdamien case 7: tap->wr_rate = 36; break; 12469ec5c125Sdamien case 8: tap->wr_rate = 48; break; 12479ec5c125Sdamien case 9: tap->wr_rate = 72; break; 12489ec5c125Sdamien case 10: tap->wr_rate = 96; break; 12499ec5c125Sdamien case 11: tap->wr_rate = 108; break; 12509ec5c125Sdamien } 1251fdfa064bSkevlo if (rate <= 3) 1252fdfa064bSkevlo tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 12539ec5c125Sdamien } else if (rate >= 12) { /* MCS0~15. */ 12549ec5c125Sdamien /* Bit 7 set means HT MCS instead of rate. */ 12559ec5c125Sdamien tap->wr_rate = 0x80 | (rate - 12); 12569ec5c125Sdamien } 12579ec5c125Sdamien tap->wr_dbm_antsignal = rssi; 12589ec5c125Sdamien tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq); 12599ec5c125Sdamien tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags); 12609ec5c125Sdamien 12619ec5c125Sdamien mb.m_data = (caddr_t)tap; 12629ec5c125Sdamien mb.m_len = sc->sc_rxtap_len; 12639ec5c125Sdamien mb.m_next = m; 12649ec5c125Sdamien mb.m_nextpkt = NULL; 12659ec5c125Sdamien mb.m_type = 0; 12669ec5c125Sdamien mb.m_flags = 0; 12679ec5c125Sdamien bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN); 12689ec5c125Sdamien } 12699ec5c125Sdamien #endif 12709ec5c125Sdamien 12719ec5c125Sdamien ni = ieee80211_find_rxnode(ic, wh); 127252a13037Sstsp memset(&rxi, 0, sizeof(rxi)); 12739ec5c125Sdamien rxi.rxi_rssi = rssi; 1274a299d652Sjmatthew 1275a299d652Sjmatthew /* Handle hardware decryption. */ 1276a299d652Sjmatthew if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) 1277a299d652Sjmatthew && (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && 1278a299d652Sjmatthew (ni->ni_flags & IEEE80211_NODE_RXPROT) && 12797104388aSstsp ((!IEEE80211_IS_MULTICAST(wh->i_addr1) && 12807104388aSstsp ni->ni_pairwise_key.k_cipher == IEEE80211_CIPHER_CCMP) || 12817104388aSstsp (IEEE80211_IS_MULTICAST(wh->i_addr1) && 12827104388aSstsp ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP))) { 1283a299d652Sjmatthew if (urtwn_ccmp_decap(sc, m, ni) != 0) { 1284a299d652Sjmatthew ifp->if_ierrors++; 1285a299d652Sjmatthew m_freem(m); 1286a299d652Sjmatthew ieee80211_release_node(ic, ni); 128706a5d685Sjmatthew splx(s); 1288a299d652Sjmatthew return; 1289a299d652Sjmatthew } 1290a299d652Sjmatthew rxi.rxi_flags |= IEEE80211_RXI_HWDEC; 1291a299d652Sjmatthew } 1292a299d652Sjmatthew 12938fbaf8a2Sstsp ieee80211_inputm(ifp, m, ni, &rxi, ml); 12949ec5c125Sdamien /* Node is no longer needed. */ 12959ec5c125Sdamien ieee80211_release_node(ic, ni); 12969ec5c125Sdamien splx(s); 12979ec5c125Sdamien } 12989ec5c125Sdamien 12999ec5c125Sdamien void 1300ab0b1be7Smglocker urtwn_rxeof(struct usbd_xfer *xfer, void *priv, 13019ec5c125Sdamien usbd_status status) 13029ec5c125Sdamien { 13038fbaf8a2Sstsp struct mbuf_list ml = MBUF_LIST_INITIALIZER(); 13049ec5c125Sdamien struct urtwn_rx_data *data = priv; 13059ec5c125Sdamien struct urtwn_softc *sc = data->sc; 13068fbaf8a2Sstsp struct ieee80211com *ic = &sc->sc_sc.sc_ic; 13078caabc14Sstsp struct r92c_rx_desc_usb *rxd; 13089ec5c125Sdamien uint32_t rxdw0; 13099ec5c125Sdamien uint8_t *buf; 131090540544Sjmatthew int len, totlen, pktlen, infosz, npkts, error, align; 13119ec5c125Sdamien 13129ec5c125Sdamien if (__predict_false(status != USBD_NORMAL_COMPLETION)) { 13139ec5c125Sdamien DPRINTF(("RX status=%d\n", status)); 13149ec5c125Sdamien if (status == USBD_STALLED) 13159ec5c125Sdamien usbd_clear_endpoint_stall_async(sc->rx_pipe); 13169ec5c125Sdamien if (status != USBD_CANCELLED) 13179ec5c125Sdamien goto resubmit; 13189ec5c125Sdamien return; 13199ec5c125Sdamien } 13209ec5c125Sdamien usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL); 13219ec5c125Sdamien 13228caabc14Sstsp if (__predict_false(len < sizeof(*rxd))) { 13239ec5c125Sdamien DPRINTF(("xfer too short %d\n", len)); 13249ec5c125Sdamien goto resubmit; 13259ec5c125Sdamien } 13269ec5c125Sdamien buf = data->buf; 13279ec5c125Sdamien 13289ec5c125Sdamien /* Get the number of encapsulated frames. */ 13298caabc14Sstsp rxd = (struct r92c_rx_desc_usb *)buf; 13308caabc14Sstsp npkts = MS(letoh32(rxd->rxdw2), R92C_RXDW2_PKTCNT); 133123340becSstsp DPRINTFN(4, ("Rx %d frames in one chunk\n", npkts)); 13329ec5c125Sdamien 1333a087613eSstsp if (sc->sc_sc.chip & RTWN_CHIP_88E) { 1334a087613eSstsp int ntries, type; 1335a087613eSstsp struct r88e_tx_rpt_ccx *rxstat; 1336a087613eSstsp 1337a087613eSstsp type = MS(letoh32(rxd->rxdw3), R88E_RXDW3_RPT); 1338a087613eSstsp 1339a087613eSstsp if (type == R88E_RXDW3_RPT_TX1) { 1340a087613eSstsp buf += sizeof(struct r92c_rx_desc_usb); 1341a087613eSstsp rxstat = (struct r88e_tx_rpt_ccx *)buf; 1342a087613eSstsp ntries = MS(letoh32(rxstat->rptb2), 1343a087613eSstsp R88E_RPTB2_RETRY_CNT); 1344a087613eSstsp 1345a087613eSstsp if (rxstat->rptb1 & R88E_RPTB1_PKT_OK) 1346a087613eSstsp sc->amn.amn_txcnt++; 1347a087613eSstsp if (ntries > 0) 1348a087613eSstsp sc->amn.amn_retrycnt++; 1349a087613eSstsp 1350a087613eSstsp goto resubmit; 1351a087613eSstsp } 1352067851b1Skevlo } else if (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) { 135390540544Sjmatthew int type; 135490540544Sjmatthew struct r92e_c2h_tx_rpt *txrpt; 135590540544Sjmatthew 135690540544Sjmatthew if (letoh32(rxd->rxdw2) & R92E_RXDW2_RPT_C2H) { 135790540544Sjmatthew if (len < sizeof(struct r92c_rx_desc_usb) + 2) 135890540544Sjmatthew goto resubmit; 135990540544Sjmatthew 136090540544Sjmatthew type = buf[sizeof(struct r92c_rx_desc_usb)]; 136190540544Sjmatthew switch (type) { 136290540544Sjmatthew case R92C_C2HEVT_TX_REPORT: 136390540544Sjmatthew buf += sizeof(struct r92c_rx_desc_usb) + 2; 136490540544Sjmatthew txrpt = (struct r92e_c2h_tx_rpt *)buf; 136590540544Sjmatthew if (MS(txrpt->rptb2, R92E_RPTB2_RETRY_CNT) > 0) 136690540544Sjmatthew sc->amn.amn_retrycnt++; 136790540544Sjmatthew if ((txrpt->rptb0 & (R92E_RPTB0_RETRY_OVER | 136890540544Sjmatthew R92E_RPTB0_LIFE_EXPIRE)) == 0) 136990540544Sjmatthew sc->amn.amn_txcnt++; 137090540544Sjmatthew break; 137190540544Sjmatthew default: 137290540544Sjmatthew break; 1373a087613eSstsp } 137490540544Sjmatthew goto resubmit; 137590540544Sjmatthew } 137690540544Sjmatthew } 137790540544Sjmatthew 1378067851b1Skevlo align = ((sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) ? 7 : 127); 1379a087613eSstsp 13809ec5c125Sdamien /* Process all of them. */ 13819ec5c125Sdamien while (npkts-- > 0) { 13828caabc14Sstsp if (__predict_false(len < sizeof(*rxd))) 13839ec5c125Sdamien break; 13848caabc14Sstsp rxd = (struct r92c_rx_desc_usb *)buf; 13858caabc14Sstsp rxdw0 = letoh32(rxd->rxdw0); 13869ec5c125Sdamien 13879ec5c125Sdamien pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN); 13889ec5c125Sdamien if (__predict_false(pktlen == 0)) 13899ec5c125Sdamien break; 13909ec5c125Sdamien 13919ec5c125Sdamien infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8; 13929ec5c125Sdamien 13939ec5c125Sdamien /* Make sure everything fits in xfer. */ 13948caabc14Sstsp totlen = sizeof(*rxd) + infosz + pktlen; 13959ec5c125Sdamien if (__predict_false(totlen > len)) 13969ec5c125Sdamien break; 13979ec5c125Sdamien 13989ec5c125Sdamien /* Process 802.11 frame. */ 13998fbaf8a2Sstsp urtwn_rx_frame(sc, buf, pktlen, &ml); 14009ec5c125Sdamien 140190540544Sjmatthew /* Handle chunk alignment. */ 140290540544Sjmatthew totlen = (totlen + align) & ~align; 14039ec5c125Sdamien buf += totlen; 14049ec5c125Sdamien len -= totlen; 14059ec5c125Sdamien } 14068fbaf8a2Sstsp if_input(&ic->ic_if, &ml); 14079ec5c125Sdamien 14089ec5c125Sdamien resubmit: 14099ec5c125Sdamien /* Setup a new transfer. */ 14109ec5c125Sdamien usbd_setup_xfer(xfer, sc->rx_pipe, data, data->buf, URTWN_RXBUFSZ, 14119ec5c125Sdamien USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT, urtwn_rxeof); 141223340becSstsp error = usbd_transfer(data->xfer); 141323340becSstsp if (error != 0 && error != USBD_IN_PROGRESS) 141423340becSstsp DPRINTF(("could not set up new transfer: %d\n", error)); 14159ec5c125Sdamien } 14169ec5c125Sdamien 14179ec5c125Sdamien void 1418ab0b1be7Smglocker urtwn_txeof(struct usbd_xfer *xfer, void *priv, 14199ec5c125Sdamien usbd_status status) 14209ec5c125Sdamien { 14219ec5c125Sdamien struct urtwn_tx_data *data = priv; 14229ec5c125Sdamien struct urtwn_softc *sc = data->sc; 142323340becSstsp struct ifnet *ifp = &sc->sc_sc.sc_ic.ic_if; 14249ec5c125Sdamien int s; 14259ec5c125Sdamien 14269ec5c125Sdamien s = splnet(); 14279ec5c125Sdamien /* Put this Tx buffer back to our free list. */ 14289ec5c125Sdamien TAILQ_INSERT_TAIL(&sc->tx_free_list, data, next); 14299ec5c125Sdamien 14309ec5c125Sdamien if (__predict_false(status != USBD_NORMAL_COMPLETION)) { 14319ec5c125Sdamien DPRINTF(("TX status=%d\n", status)); 14329ec5c125Sdamien if (status == USBD_STALLED) 14339ec5c125Sdamien usbd_clear_endpoint_stall_async(data->pipe); 14349ec5c125Sdamien ifp->if_oerrors++; 14359ec5c125Sdamien splx(s); 14369ec5c125Sdamien return; 14379ec5c125Sdamien } 143823340becSstsp sc->sc_sc.sc_tx_timer = 0; 14399ec5c125Sdamien 14409ec5c125Sdamien /* We just released a Tx buffer, notify Tx. */ 1441de6cd8fbSdlg if (ifq_is_oactive(&ifp->if_snd)) { 1442de6cd8fbSdlg ifq_clr_oactive(&ifp->if_snd); 144323340becSstsp rtwn_start(ifp); 14449ec5c125Sdamien } 14459ec5c125Sdamien splx(s); 14469ec5c125Sdamien } 14479ec5c125Sdamien 144890540544Sjmatthew void 144990540544Sjmatthew urtwn_tx_fill_desc(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m, 145090540544Sjmatthew struct ieee80211_frame *wh, struct ieee80211_key *k, 145190540544Sjmatthew struct ieee80211_node *ni) 14529ec5c125Sdamien { 14538caabc14Sstsp struct r92c_tx_desc_usb *txd; 145490540544Sjmatthew struct ieee80211com *ic = &sc->sc_sc.sc_ic; 1455efc8f9f7Sstsp uint8_t raid, type, rtsrate; 1456a299d652Sjmatthew uint32_t pktlen; 14579ec5c125Sdamien 145890540544Sjmatthew txd = (struct r92c_tx_desc_usb *)*txdp; 145990540544Sjmatthew (*txdp) += sizeof(*txd); 14609ec5c125Sdamien memset(txd, 0, sizeof(*txd)); 14619ec5c125Sdamien 1462a299d652Sjmatthew pktlen = m->m_pkthdr.len; 1463a299d652Sjmatthew if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { 1464a299d652Sjmatthew txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, 1465a299d652Sjmatthew R92C_TXDW1_CIPHER_AES)); 1466a299d652Sjmatthew pktlen += IEEE80211_CCMP_HDRLEN; 1467a299d652Sjmatthew } 1468a299d652Sjmatthew 146990540544Sjmatthew type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 147090540544Sjmatthew 14719ec5c125Sdamien txd->txdw0 |= htole32( 1472a299d652Sjmatthew SM(R92C_TXDW0_PKTLEN, pktlen) | 14739ec5c125Sdamien SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | 14749ec5c125Sdamien R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG); 14759ec5c125Sdamien if (IEEE80211_IS_MULTICAST(wh->i_addr1)) 14769ec5c125Sdamien txd->txdw0 |= htole32(R92C_TXDW0_BMCAST); 14779ec5c125Sdamien 14789ec5c125Sdamien if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && 14799ec5c125Sdamien type == IEEE80211_FC0_TYPE_DATA) { 148056d11693Sstsp if (ic->ic_curmode == IEEE80211_MODE_11B || 148123340becSstsp (sc->sc_sc.sc_flags & RTWN_FLAG_FORCE_RAID_11B)) 14829ec5c125Sdamien raid = R92C_RAID_11B; 14839ec5c125Sdamien else 14849ec5c125Sdamien raid = R92C_RAID_11BG; 148523340becSstsp if (sc->sc_sc.chip & RTWN_CHIP_88E) { 1486c349b8bfSstsp txd->txdw1 |= htole32( 148753c3c4d0Sstsp SM(R88E_TXDW1_MACID, R92C_MACID_BSS) | 1488c349b8bfSstsp SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) | 1489c349b8bfSstsp SM(R92C_TXDW1_RAID, raid)); 1490c349b8bfSstsp txd->txdw2 |= htole32(R88E_TXDW2_AGGBK); 1491a087613eSstsp /* Request TX status report for AMRR */ 1492a087613eSstsp txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT); 1493c349b8bfSstsp } else { 14949ec5c125Sdamien txd->txdw1 |= htole32( 149553c3c4d0Sstsp SM(R92C_TXDW1_MACID, R92C_MACID_BSS) | 14969ec5c125Sdamien SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) | 1497c349b8bfSstsp SM(R92C_TXDW1_RAID, raid) | R92C_TXDW1_AGGBK); 1498c349b8bfSstsp } 14999ec5c125Sdamien 1500a299d652Sjmatthew if (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { 15015098e9a8Sstsp txd->txdw4 |= htole32(R92C_TXDW4_RTSEN | 15025098e9a8Sstsp R92C_TXDW4_HWRTSEN); 15035098e9a8Sstsp } else if (ic->ic_flags & IEEE80211_F_USEPROT) { 15049ec5c125Sdamien if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) { 15059ec5c125Sdamien txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF | 15069ec5c125Sdamien R92C_TXDW4_HWRTSEN); 15079ec5c125Sdamien } else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) { 15089ec5c125Sdamien txd->txdw4 |= htole32(R92C_TXDW4_RTSEN | 15099ec5c125Sdamien R92C_TXDW4_HWRTSEN); 15109ec5c125Sdamien } 15119ec5c125Sdamien } 15129ec5c125Sdamien txd->txdw5 |= htole32(0x0001ff00); 15139ec5c125Sdamien 1514efc8f9f7Sstsp if (ic->ic_curmode == IEEE80211_MODE_11B) 1515efc8f9f7Sstsp rtsrate = 0; /* CCK1 */ 1516efc8f9f7Sstsp else 1517efc8f9f7Sstsp rtsrate = 8; /* OFDM24 */ 1518efc8f9f7Sstsp 1519a087613eSstsp if (sc->sc_sc.chip & RTWN_CHIP_88E) { 1520a087613eSstsp /* Use AMRR */ 1521a087613eSstsp txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE); 1522efc8f9f7Sstsp txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, rtsrate)); 1523a087613eSstsp txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 1524a087613eSstsp ni->ni_txrate)); 1525a087613eSstsp } else { 1526efc8f9f7Sstsp /* Send data at OFDM54. */ 1527efc8f9f7Sstsp txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, rtsrate)); 1528a087613eSstsp txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 11)); 1529a087613eSstsp } 15309ec5c125Sdamien } else { 15319ec5c125Sdamien txd->txdw1 |= htole32( 15329ec5c125Sdamien SM(R92C_TXDW1_MACID, 0) | 15339ec5c125Sdamien SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT) | 15349ec5c125Sdamien SM(R92C_TXDW1_RAID, R92C_RAID_11B)); 15359ec5c125Sdamien 15369ec5c125Sdamien /* Force CCK1. */ 15379ec5c125Sdamien txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE); 15389ec5c125Sdamien txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 0)); 15399ec5c125Sdamien } 15409ec5c125Sdamien /* Set sequence number (already little endian). */ 15419befa0edSjmatthew txd->txdseq |= (*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; 15429ec5c125Sdamien 154390540544Sjmatthew if (!ieee80211_has_qos(wh)) { 15449ec5c125Sdamien /* Use HW sequence numbering for non-QoS frames. */ 15459ec5c125Sdamien txd->txdw4 |= htole32(R92C_TXDW4_HWSEQ); 15469befa0edSjmatthew txd->txdseq |= htole16(R92C_TXDW3_HWSEQEN); 15479ec5c125Sdamien } else 15489ec5c125Sdamien txd->txdw4 |= htole32(R92C_TXDW4_QOS); 154990540544Sjmatthew } 155090540544Sjmatthew 155190540544Sjmatthew void 155290540544Sjmatthew urtwn_tx_fill_desc_gen2(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m, 155390540544Sjmatthew struct ieee80211_frame *wh, struct ieee80211_key *k, 155490540544Sjmatthew struct ieee80211_node *ni) 155590540544Sjmatthew { 155690540544Sjmatthew struct r92e_tx_desc_usb *txd; 155790540544Sjmatthew struct ieee80211com *ic = &sc->sc_sc.sc_ic; 155890540544Sjmatthew uint8_t raid, type; 1559a299d652Sjmatthew uint32_t pktlen; 156090540544Sjmatthew 156190540544Sjmatthew txd = (struct r92e_tx_desc_usb *)*txdp; 156290540544Sjmatthew (*txdp) += sizeof(*txd); 156390540544Sjmatthew memset(txd, 0, sizeof(*txd)); 156490540544Sjmatthew 156590540544Sjmatthew type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 156690540544Sjmatthew 1567a299d652Sjmatthew pktlen = m->m_pkthdr.len; 1568a299d652Sjmatthew if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { 1569a299d652Sjmatthew txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, 1570a299d652Sjmatthew R92C_TXDW1_CIPHER_AES)); 1571a299d652Sjmatthew pktlen += IEEE80211_CCMP_HDRLEN; 1572a299d652Sjmatthew } 1573a299d652Sjmatthew 157490540544Sjmatthew txd->txdw0 |= htole32( 1575a299d652Sjmatthew SM(R92C_TXDW0_PKTLEN, pktlen) | 157690540544Sjmatthew SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | 157790540544Sjmatthew R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG); 157890540544Sjmatthew if (IEEE80211_IS_MULTICAST(wh->i_addr1)) 157990540544Sjmatthew txd->txdw0 |= htole32(R92C_TXDW0_BMCAST); 158090540544Sjmatthew 158190540544Sjmatthew if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && 158290540544Sjmatthew type == IEEE80211_FC0_TYPE_DATA) { 158390540544Sjmatthew if (ic->ic_curmode == IEEE80211_MODE_11B || 158490540544Sjmatthew (sc->sc_sc.sc_flags & RTWN_FLAG_FORCE_RAID_11B)) 158590540544Sjmatthew raid = R92E_RAID_11B; 158690540544Sjmatthew else 158790540544Sjmatthew raid = R92E_RAID_11BG; 158890540544Sjmatthew txd->txdw1 |= htole32( 158990540544Sjmatthew SM(R92E_TXDW1_MACID, R92C_MACID_BSS) | 159090540544Sjmatthew SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) | 159190540544Sjmatthew SM(R92C_TXDW1_RAID, raid)); 159290540544Sjmatthew /* Request TX status report for AMRR */ 159390540544Sjmatthew txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT | R88E_TXDW2_AGGBK); 159490540544Sjmatthew 1595a299d652Sjmatthew if (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { 159690540544Sjmatthew txd->txdw4 |= htole32(R92C_TXDW4_RTSEN | 159790540544Sjmatthew R92C_TXDW4_HWRTSEN); 159890540544Sjmatthew } else if (ic->ic_flags & IEEE80211_F_USEPROT) { 159990540544Sjmatthew if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) { 160090540544Sjmatthew txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF | 160190540544Sjmatthew R92C_TXDW4_HWRTSEN); 160290540544Sjmatthew } else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) { 160390540544Sjmatthew txd->txdw4 |= htole32(R92C_TXDW4_RTSEN | 160490540544Sjmatthew R92C_TXDW4_HWRTSEN); 160590540544Sjmatthew } 160690540544Sjmatthew } 160790540544Sjmatthew txd->txdw5 |= htole32(0x0001ff00); 160890540544Sjmatthew 160990540544Sjmatthew /* Use AMRR */ 161090540544Sjmatthew txd->txdw3 |= htole32(R92E_TXDW3_DRVRATE); 1611efc8f9f7Sstsp txd->txdw4 |= htole32(SM(R92E_TXDW4_RTSRATE, 8)); 161290540544Sjmatthew txd->txdw4 |= htole32(SM(R92E_TXDW4_DATARATE, ni->ni_txrate)); 161390540544Sjmatthew } else { 161490540544Sjmatthew txd->txdw1 |= htole32( 161590540544Sjmatthew SM(R92E_TXDW1_MACID, 0) | 161690540544Sjmatthew SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT) | 161790540544Sjmatthew SM(R92C_TXDW1_RAID, R92E_RAID_11B)); 161890540544Sjmatthew 161990540544Sjmatthew /* Force CCK1. */ 162090540544Sjmatthew txd->txdw3 |= htole32(R92E_TXDW3_DRVRATE); 162190540544Sjmatthew txd->txdw4 |= htole32(SM(R92E_TXDW4_DATARATE, 0)); 162290540544Sjmatthew } 162390540544Sjmatthew txd->txdw4 |= htole32(SM(R92E_TXDW4_DATARATEFB, 0x1f)); 162490540544Sjmatthew 162590540544Sjmatthew txd->txdseq2 |= htole16(SM(R92E_TXDSEQ2_HWSEQ, *(uint16_t *)wh->i_seq)); 162690540544Sjmatthew 162790540544Sjmatthew if (!ieee80211_has_qos(wh)) { 162890540544Sjmatthew /* Use HW sequence numbering for non-QoS frames. */ 162990540544Sjmatthew txd->txdw7 |= htole16(R92C_TXDW3_HWSEQEN); 163090540544Sjmatthew } 163190540544Sjmatthew } 163290540544Sjmatthew 163390540544Sjmatthew int 163490540544Sjmatthew urtwn_tx(void *cookie, struct mbuf *m, struct ieee80211_node *ni) 163590540544Sjmatthew { 163690540544Sjmatthew struct urtwn_softc *sc = cookie; 163790540544Sjmatthew struct ieee80211com *ic = &sc->sc_sc.sc_ic; 163890540544Sjmatthew struct ieee80211_frame *wh; 163990540544Sjmatthew struct ieee80211_key *k = NULL; 164090540544Sjmatthew struct urtwn_tx_data *data; 164190540544Sjmatthew struct usbd_pipe *pipe; 164290540544Sjmatthew uint16_t qos, sum; 164390540544Sjmatthew uint8_t tid, qid; 1644a299d652Sjmatthew int i, xferlen, error, headerlen; 164590540544Sjmatthew uint8_t *txdp; 164690540544Sjmatthew 164790540544Sjmatthew wh = mtod(m, struct ieee80211_frame *); 164890540544Sjmatthew 164990540544Sjmatthew if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 165090540544Sjmatthew k = ieee80211_get_txkey(ic, wh, ni); 1651a299d652Sjmatthew if (k->k_cipher != IEEE80211_CIPHER_CCMP) { 165290540544Sjmatthew if ((m = ieee80211_encrypt(ic, m, k)) == NULL) 165390540544Sjmatthew return (ENOBUFS); 165490540544Sjmatthew wh = mtod(m, struct ieee80211_frame *); 165590540544Sjmatthew } 1656a299d652Sjmatthew } 165790540544Sjmatthew 165890540544Sjmatthew if (ieee80211_has_qos(wh)) { 165990540544Sjmatthew qos = ieee80211_get_qos(wh); 166090540544Sjmatthew tid = qos & IEEE80211_QOS_TID; 166190540544Sjmatthew qid = ieee80211_up_to_ac(ic, tid); 1662c6918e3cSjmatthew } else if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) 166390540544Sjmatthew != IEEE80211_FC0_TYPE_DATA) { 166490540544Sjmatthew /* Use AC VO for management frames. */ 166590540544Sjmatthew qid = EDCA_AC_VO; 166690540544Sjmatthew } else 166790540544Sjmatthew qid = EDCA_AC_BE; 166890540544Sjmatthew 166990540544Sjmatthew /* Get the USB pipe to use for this AC. */ 167090540544Sjmatthew pipe = sc->tx_pipe[sc->ac2idx[qid]]; 167190540544Sjmatthew 167290540544Sjmatthew /* Grab a Tx buffer from our free list. */ 167390540544Sjmatthew data = TAILQ_FIRST(&sc->tx_free_list); 167490540544Sjmatthew TAILQ_REMOVE(&sc->tx_free_list, data, next); 167590540544Sjmatthew 167690540544Sjmatthew /* Fill Tx descriptor. */ 167790540544Sjmatthew txdp = data->buf; 1678067851b1Skevlo if (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) 167990540544Sjmatthew urtwn_tx_fill_desc_gen2(sc, &txdp, m, wh, k, ni); 168090540544Sjmatthew else 168190540544Sjmatthew urtwn_tx_fill_desc(sc, &txdp, m, wh, k, ni); 16829ec5c125Sdamien 16839ec5c125Sdamien /* Compute Tx descriptor checksum. */ 16849ec5c125Sdamien sum = 0; 168590540544Sjmatthew for (i = 0; i < R92C_TXDESC_SUMSIZE / 2; i++) 168690540544Sjmatthew sum ^= ((uint16_t *)data->buf)[i]; 168790540544Sjmatthew ((uint16_t *)data->buf)[R92C_TXDESC_SUMOFFSET] = sum; 16889ec5c125Sdamien 16899ec5c125Sdamien #if NBPFILTER > 0 16909ec5c125Sdamien if (__predict_false(sc->sc_drvbpf != NULL)) { 16919ec5c125Sdamien struct urtwn_tx_radiotap_header *tap = &sc->sc_txtap; 16929ec5c125Sdamien struct mbuf mb; 16939ec5c125Sdamien 16949ec5c125Sdamien tap->wt_flags = 0; 16959ec5c125Sdamien tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq); 16969ec5c125Sdamien tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags); 16979ec5c125Sdamien 16989ec5c125Sdamien mb.m_data = (caddr_t)tap; 16999ec5c125Sdamien mb.m_len = sc->sc_txtap_len; 17009ec5c125Sdamien mb.m_next = m; 17019ec5c125Sdamien mb.m_nextpkt = NULL; 17029ec5c125Sdamien mb.m_type = 0; 17039ec5c125Sdamien mb.m_flags = 0; 17049ec5c125Sdamien bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT); 17059ec5c125Sdamien } 17069ec5c125Sdamien #endif 17079ec5c125Sdamien 1708a299d652Sjmatthew if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { 1709a299d652Sjmatthew xferlen = (txdp - data->buf) + m->m_pkthdr.len + 1710a299d652Sjmatthew IEEE80211_CCMP_HDRLEN; 1711a299d652Sjmatthew headerlen = ieee80211_get_hdrlen(wh); 1712a299d652Sjmatthew 1713a299d652Sjmatthew m_copydata(m, 0, headerlen, txdp); 1714a299d652Sjmatthew txdp += headerlen; 1715a299d652Sjmatthew 1716a299d652Sjmatthew k->k_tsc++; 1717a299d652Sjmatthew txdp[0] = k->k_tsc; 1718a299d652Sjmatthew txdp[1] = k->k_tsc >> 8; 1719a299d652Sjmatthew txdp[2] = 0; 1720a299d652Sjmatthew txdp[3] = k->k_id | IEEE80211_WEP_EXTIV; 1721a299d652Sjmatthew txdp[4] = k->k_tsc >> 16; 1722a299d652Sjmatthew txdp[5] = k->k_tsc >> 24; 1723a299d652Sjmatthew txdp[6] = k->k_tsc >> 32; 1724a299d652Sjmatthew txdp[7] = k->k_tsc >> 40; 1725a299d652Sjmatthew txdp += IEEE80211_CCMP_HDRLEN; 1726a299d652Sjmatthew 1727a299d652Sjmatthew m_copydata(m, headerlen, m->m_pkthdr.len - headerlen, txdp); 1728df3da441Sstsp m_freem(m); 1729a299d652Sjmatthew } else { 173090540544Sjmatthew xferlen = (txdp - data->buf) + m->m_pkthdr.len; 173190540544Sjmatthew m_copydata(m, 0, m->m_pkthdr.len, txdp); 17329ec5c125Sdamien m_freem(m); 1733a299d652Sjmatthew } 17349ec5c125Sdamien 17359ec5c125Sdamien data->pipe = pipe; 17369ec5c125Sdamien usbd_setup_xfer(data->xfer, pipe, data, data->buf, xferlen, 17379ec5c125Sdamien USBD_FORCE_SHORT_XFER | USBD_NO_COPY, URTWN_TX_TIMEOUT, 17389ec5c125Sdamien urtwn_txeof); 17399ec5c125Sdamien error = usbd_transfer(data->xfer); 17409ec5c125Sdamien if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) { 17419ec5c125Sdamien /* Put this Tx buffer back to our free list. */ 17429ec5c125Sdamien TAILQ_INSERT_TAIL(&sc->tx_free_list, data, next); 17439ec5c125Sdamien return (error); 17449ec5c125Sdamien } 17459ec5c125Sdamien ieee80211_release_node(ic, ni); 17469ec5c125Sdamien return (0); 17479ec5c125Sdamien } 17489ec5c125Sdamien 17499ec5c125Sdamien int 17509ec5c125Sdamien urtwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 17519ec5c125Sdamien { 175223340becSstsp struct rtwn_softc *sc_sc = ifp->if_softc; 175323340becSstsp struct device *self = sc_sc->sc_pdev; 175423340becSstsp struct urtwn_softc *sc = (struct urtwn_softc *)self; 175523340becSstsp int error; 17569ec5c125Sdamien 17575ad57547Sjakemsr if (usbd_is_dying(sc->sc_udev)) 17585ad57547Sjakemsr return ENXIO; 17595ad57547Sjakemsr 17605ad57547Sjakemsr usbd_ref_incr(sc->sc_udev); 176123340becSstsp error = rtwn_ioctl(ifp, cmd, data); 17625ad57547Sjakemsr usbd_ref_decr(sc->sc_udev); 17635ad57547Sjakemsr 17649ec5c125Sdamien return (error); 17659ec5c125Sdamien } 17669ec5c125Sdamien 1767c349b8bfSstsp int 1768c349b8bfSstsp urtwn_r92c_power_on(struct urtwn_softc *sc) 1769c349b8bfSstsp { 17709ec5c125Sdamien uint32_t reg; 17719ec5c125Sdamien int ntries; 17729ec5c125Sdamien 17739ec5c125Sdamien /* Wait for autoload done bit. */ 17749ec5c125Sdamien for (ntries = 0; ntries < 1000; ntries++) { 17759ec5c125Sdamien if (urtwn_read_1(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_PFM_ALDN) 17769ec5c125Sdamien break; 17779ec5c125Sdamien DELAY(5); 17789ec5c125Sdamien } 17799ec5c125Sdamien if (ntries == 1000) { 17809ec5c125Sdamien printf("%s: timeout waiting for chip autoload\n", 17819ec5c125Sdamien sc->sc_dev.dv_xname); 17829ec5c125Sdamien return (ETIMEDOUT); 17839ec5c125Sdamien } 17849ec5c125Sdamien 17859ec5c125Sdamien /* Unlock ISO/CLK/Power control register. */ 17869ec5c125Sdamien urtwn_write_1(sc, R92C_RSV_CTRL, 0); 17879ec5c125Sdamien /* Move SPS into PWM mode. */ 17889ec5c125Sdamien urtwn_write_1(sc, R92C_SPS0_CTRL, 0x2b); 17899ec5c125Sdamien DELAY(100); 17909ec5c125Sdamien 17919ec5c125Sdamien reg = urtwn_read_1(sc, R92C_LDOV12D_CTRL); 17929ec5c125Sdamien if (!(reg & R92C_LDOV12D_CTRL_LDV12_EN)) { 17939ec5c125Sdamien urtwn_write_1(sc, R92C_LDOV12D_CTRL, 17949ec5c125Sdamien reg | R92C_LDOV12D_CTRL_LDV12_EN); 17959ec5c125Sdamien DELAY(100); 17969ec5c125Sdamien urtwn_write_1(sc, R92C_SYS_ISO_CTRL, 17979ec5c125Sdamien urtwn_read_1(sc, R92C_SYS_ISO_CTRL) & 17989ec5c125Sdamien ~R92C_SYS_ISO_CTRL_MD2PP); 17999ec5c125Sdamien } 18009ec5c125Sdamien 18019ec5c125Sdamien /* Auto enable WLAN. */ 18029ec5c125Sdamien urtwn_write_2(sc, R92C_APS_FSMCO, 18039ec5c125Sdamien urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC); 18049ec5c125Sdamien for (ntries = 0; ntries < 1000; ntries++) { 1805ae90c074Smpi if (!(urtwn_read_2(sc, R92C_APS_FSMCO) & 1806ae90c074Smpi R92C_APS_FSMCO_APFM_ONMAC)) 18079ec5c125Sdamien break; 18089ec5c125Sdamien DELAY(5); 18099ec5c125Sdamien } 18109ec5c125Sdamien if (ntries == 1000) { 18119ec5c125Sdamien printf("%s: timeout waiting for MAC auto ON\n", 18129ec5c125Sdamien sc->sc_dev.dv_xname); 18139ec5c125Sdamien return (ETIMEDOUT); 18149ec5c125Sdamien } 18159ec5c125Sdamien 18169ec5c125Sdamien /* Enable radio, GPIO and LED functions. */ 18179ec5c125Sdamien urtwn_write_2(sc, R92C_APS_FSMCO, 18189ec5c125Sdamien R92C_APS_FSMCO_AFSM_HSUS | 18199ec5c125Sdamien R92C_APS_FSMCO_PDN_EN | 18209ec5c125Sdamien R92C_APS_FSMCO_PFM_ALDN); 18219ec5c125Sdamien /* Release RF digital isolation. */ 18229ec5c125Sdamien urtwn_write_2(sc, R92C_SYS_ISO_CTRL, 18239ec5c125Sdamien urtwn_read_2(sc, R92C_SYS_ISO_CTRL) & ~R92C_SYS_ISO_CTRL_DIOR); 18249ec5c125Sdamien 18259ec5c125Sdamien /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */ 18269ec5c125Sdamien reg = urtwn_read_2(sc, R92C_CR); 18279ec5c125Sdamien reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | 18289ec5c125Sdamien R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | 18299ec5c125Sdamien R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN | 18309ec5c125Sdamien R92C_CR_ENSEC; 18319ec5c125Sdamien urtwn_write_2(sc, R92C_CR, reg); 18329ec5c125Sdamien 18339ec5c125Sdamien urtwn_write_1(sc, 0xfe10, 0x19); 18349ec5c125Sdamien return (0); 18359ec5c125Sdamien } 18369ec5c125Sdamien 18379ec5c125Sdamien int 183890540544Sjmatthew urtwn_r92e_power_on(struct urtwn_softc *sc) 183990540544Sjmatthew { 184090540544Sjmatthew uint32_t reg; 184190540544Sjmatthew int ntries; 184290540544Sjmatthew 184390540544Sjmatthew if (urtwn_read_4(sc, R92C_SYS_CFG) & R92E_SYS_CFG_SPSLDO_SEL) { 184490540544Sjmatthew /* LDO. */ 184590540544Sjmatthew urtwn_write_1(sc, R92E_LDO_SWR_CTRL, 0xc3); 184690540544Sjmatthew } else { 184790540544Sjmatthew reg = urtwn_read_4(sc, R92C_SYS_SWR_CTRL2); 184890540544Sjmatthew reg &= 0xff0fffff; 184990540544Sjmatthew reg |= 0x00500000; 185090540544Sjmatthew urtwn_write_4(sc, R92C_SYS_SWR_CTRL2, reg); 185190540544Sjmatthew urtwn_write_1(sc, R92E_LDO_SWR_CTRL, 0x83); 185290540544Sjmatthew } 185390540544Sjmatthew 185490540544Sjmatthew /* 40MHz crystal source */ 185590540544Sjmatthew urtwn_write_1(sc, R92C_AFE_PLL_CTRL, 185690540544Sjmatthew urtwn_read_1(sc, R92C_AFE_PLL_CTRL) & 0xfb); 185790540544Sjmatthew urtwn_write_4(sc, R92C_AFE_XTAL_CTRL_EXT, 185890540544Sjmatthew urtwn_read_4(sc, R92C_AFE_XTAL_CTRL_EXT) & 0xfffffc7f); 185990540544Sjmatthew 186090540544Sjmatthew urtwn_write_1(sc, R92C_AFE_PLL_CTRL, 186190540544Sjmatthew urtwn_read_1(sc, R92C_AFE_PLL_CTRL) & 0xbf); 186290540544Sjmatthew urtwn_write_4(sc, R92C_AFE_XTAL_CTRL_EXT, 186390540544Sjmatthew urtwn_read_4(sc, R92C_AFE_XTAL_CTRL_EXT) & 0xffdfffff); 186490540544Sjmatthew 186590540544Sjmatthew /* Disable HWPDN. */ 186690540544Sjmatthew urtwn_write_2(sc, R92C_APS_FSMCO, 186790540544Sjmatthew urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_APDM_HPDN); 186890540544Sjmatthew for (ntries = 0; ntries < 5000; ntries++) { 186990540544Sjmatthew if (urtwn_read_4(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_SUS_HOST) 187090540544Sjmatthew break; 187190540544Sjmatthew DELAY(10); 187290540544Sjmatthew } 187390540544Sjmatthew if (ntries == 5000) { 187490540544Sjmatthew printf("%s: timeout waiting for chip power up\n", 187590540544Sjmatthew sc->sc_dev.dv_xname); 187690540544Sjmatthew return (ETIMEDOUT); 187790540544Sjmatthew } 187890540544Sjmatthew 187990540544Sjmatthew /* Disable WL suspend. */ 188090540544Sjmatthew urtwn_write_2(sc, R92C_APS_FSMCO, 188190540544Sjmatthew urtwn_read_2(sc, R92C_APS_FSMCO) & 188290540544Sjmatthew ~(R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_AFSM_PCIE)); 188390540544Sjmatthew 188490540544Sjmatthew /* Auto enable WLAN. */ 188590540544Sjmatthew urtwn_write_4(sc, R92C_APS_FSMCO, 188690540544Sjmatthew urtwn_read_4(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_RDY_MACON); 188790540544Sjmatthew urtwn_write_2(sc, R92C_APS_FSMCO, 188890540544Sjmatthew urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC); 188990540544Sjmatthew for (ntries = 0; ntries < 5000; ntries++) { 189090540544Sjmatthew if (!(urtwn_read_2(sc, R92C_APS_FSMCO) & 189190540544Sjmatthew R92C_APS_FSMCO_APFM_ONMAC)) 189290540544Sjmatthew break; 189390540544Sjmatthew DELAY(10); 189490540544Sjmatthew } 189590540544Sjmatthew if (ntries == 5000) { 189690540544Sjmatthew printf("%s: timeout waiting for MAC auto ON\n", 189790540544Sjmatthew sc->sc_dev.dv_xname); 189890540544Sjmatthew return (ETIMEDOUT); 189990540544Sjmatthew } 190090540544Sjmatthew 190190540544Sjmatthew /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */ 190290540544Sjmatthew urtwn_write_2(sc, R92C_CR, 0); 190390540544Sjmatthew reg = urtwn_read_2(sc, R92C_CR); 190490540544Sjmatthew reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | 190590540544Sjmatthew R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | 190690540544Sjmatthew R92C_CR_SCHEDULE_EN | R92C_CR_ENSEC | R92C_CR_CALTMR_EN; 190790540544Sjmatthew urtwn_write_2(sc, R92C_CR, reg); 190890540544Sjmatthew return (0); 190990540544Sjmatthew } 191090540544Sjmatthew 191190540544Sjmatthew int 1912c349b8bfSstsp urtwn_r88e_power_on(struct urtwn_softc *sc) 1913c349b8bfSstsp { 1914c349b8bfSstsp uint32_t reg; 1915c349b8bfSstsp int ntries; 1916c349b8bfSstsp 1917c349b8bfSstsp /* Wait for power ready bit. */ 1918c349b8bfSstsp for (ntries = 0; ntries < 5000; ntries++) { 1919a55d0e84Sstsp if (urtwn_read_4(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_SUS_HOST) 1920c349b8bfSstsp break; 1921c349b8bfSstsp DELAY(10); 1922c349b8bfSstsp } 1923c349b8bfSstsp if (ntries == 5000) { 1924c349b8bfSstsp printf("%s: timeout waiting for chip power up\n", 1925c349b8bfSstsp sc->sc_dev.dv_xname); 1926c349b8bfSstsp return (ETIMEDOUT); 1927c349b8bfSstsp } 1928c349b8bfSstsp 1929c349b8bfSstsp /* Reset BB. */ 1930c349b8bfSstsp urtwn_write_1(sc, R92C_SYS_FUNC_EN, 1931c349b8bfSstsp urtwn_read_1(sc, R92C_SYS_FUNC_EN) & ~(R92C_SYS_FUNC_EN_BBRSTB | 1932c349b8bfSstsp R92C_SYS_FUNC_EN_BB_GLB_RST)); 1933c349b8bfSstsp 1934a55d0e84Sstsp urtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 2, 1935a55d0e84Sstsp urtwn_read_1(sc, R92C_AFE_XTAL_CTRL + 2) | 0x80); 1936c349b8bfSstsp 1937c349b8bfSstsp /* Disable HWPDN. */ 1938a55d0e84Sstsp urtwn_write_2(sc, R92C_APS_FSMCO, 1939a55d0e84Sstsp urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_APDM_HPDN); 1940c349b8bfSstsp /* Disable WL suspend. */ 1941a55d0e84Sstsp urtwn_write_2(sc, R92C_APS_FSMCO, 1942a55d0e84Sstsp urtwn_read_2(sc, R92C_APS_FSMCO) & 1943a55d0e84Sstsp ~(R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_AFSM_PCIE)); 1944c349b8bfSstsp 194508300c66Skevlo /* Auto enable WLAN. */ 1946a55d0e84Sstsp urtwn_write_2(sc, R92C_APS_FSMCO, 1947a55d0e84Sstsp urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC); 1948c349b8bfSstsp for (ntries = 0; ntries < 5000; ntries++) { 1949a55d0e84Sstsp if (!(urtwn_read_2(sc, R92C_APS_FSMCO) & 1950a55d0e84Sstsp R92C_APS_FSMCO_APFM_ONMAC)) 1951c349b8bfSstsp break; 1952c349b8bfSstsp DELAY(10); 1953c349b8bfSstsp } 195408300c66Skevlo if (ntries == 5000) { 195508300c66Skevlo printf("%s: timeout waiting for MAC auto ON\n", 195608300c66Skevlo sc->sc_dev.dv_xname); 1957c349b8bfSstsp return (ETIMEDOUT); 195808300c66Skevlo } 1959c349b8bfSstsp 1960c349b8bfSstsp /* Enable LDO normal mode. */ 1961a55d0e84Sstsp urtwn_write_1(sc, R92C_LPLDO_CTRL, 1962a55d0e84Sstsp urtwn_read_1(sc, R92C_LPLDO_CTRL) & ~0x10); 1963c349b8bfSstsp 1964c349b8bfSstsp /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */ 1965c349b8bfSstsp urtwn_write_2(sc, R92C_CR, 0); 1966c349b8bfSstsp reg = urtwn_read_2(sc, R92C_CR); 1967c349b8bfSstsp reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | 1968c349b8bfSstsp R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | 1969c349b8bfSstsp R92C_CR_SCHEDULE_EN | R92C_CR_ENSEC | R92C_CR_CALTMR_EN; 1970c349b8bfSstsp urtwn_write_2(sc, R92C_CR, reg); 1971c349b8bfSstsp return (0); 1972c349b8bfSstsp } 1973c349b8bfSstsp 1974c349b8bfSstsp int 1975067851b1Skevlo urtwn_r88f_power_on(struct urtwn_softc *sc) 1976067851b1Skevlo { 1977067851b1Skevlo uint32_t reg; 1978067851b1Skevlo int ntries; 1979067851b1Skevlo 1980067851b1Skevlo /* Enable WL suspend. */ 1981067851b1Skevlo urtwn_write_2(sc, R92C_APS_FSMCO, 1982067851b1Skevlo urtwn_read_2(sc, R92C_APS_FSMCO) & 1983067851b1Skevlo ~(R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_AFSM_PCIE)); 1984067851b1Skevlo /* Turn off USB APHY LDO under suspend mode. */ 1985067851b1Skevlo urtwn_write_1(sc, 0xc4, urtwn_read_1(sc, 0xc4) & ~0x10); 1986067851b1Skevlo 1987067851b1Skevlo /* Disable SW LPS. */ 1988067851b1Skevlo urtwn_write_2(sc, R92C_APS_FSMCO, 1989067851b1Skevlo urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_APFM_RSM); 1990067851b1Skevlo /* Wait for power ready bit. */ 1991067851b1Skevlo for (ntries = 0; ntries < 5000; ntries++) { 1992067851b1Skevlo if (urtwn_read_4(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_SUS_HOST) 1993067851b1Skevlo break; 1994067851b1Skevlo DELAY(10); 1995067851b1Skevlo } 1996067851b1Skevlo if (ntries == 5000) { 1997067851b1Skevlo printf("%s: timeout waiting for chip power up\n", 1998067851b1Skevlo sc->sc_dev.dv_xname); 1999067851b1Skevlo return (ETIMEDOUT); 2000067851b1Skevlo } 2001067851b1Skevlo /* Disable HWPDN. */ 2002067851b1Skevlo urtwn_write_2(sc, R92C_APS_FSMCO, 2003067851b1Skevlo urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_APDM_HPDN); 2004067851b1Skevlo /* Disable WL suspend. */ 2005067851b1Skevlo urtwn_write_2(sc, R92C_APS_FSMCO, 2006067851b1Skevlo urtwn_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_AFSM_HSUS); 2007067851b1Skevlo /* Auto enable WLAN. */ 2008067851b1Skevlo urtwn_write_2(sc, R92C_APS_FSMCO, 2009067851b1Skevlo urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC); 2010067851b1Skevlo for (ntries = 0; ntries < 5000; ntries++) { 2011067851b1Skevlo if (!(urtwn_read_2(sc, R92C_APS_FSMCO) & 2012067851b1Skevlo R92C_APS_FSMCO_APFM_ONMAC)) 2013067851b1Skevlo break; 2014067851b1Skevlo DELAY(10); 2015067851b1Skevlo } 2016067851b1Skevlo if (ntries == 5000) { 2017067851b1Skevlo printf("%s: timeout waiting for MAC auto ON\n", 2018067851b1Skevlo sc->sc_dev.dv_xname); 2019067851b1Skevlo return (ETIMEDOUT); 2020067851b1Skevlo } 2021067851b1Skevlo /* Reduce RF noise. */ 2022067851b1Skevlo urtwn_write_1(sc, R92C_AFE_LDO_CTRL, 0x35); 2023067851b1Skevlo 2024067851b1Skevlo /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */ 2025067851b1Skevlo urtwn_write_2(sc, R92C_CR, 0); 2026067851b1Skevlo reg = urtwn_read_2(sc, R92C_CR); 2027067851b1Skevlo reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN | 2028067851b1Skevlo R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN | 2029067851b1Skevlo R92C_CR_SCHEDULE_EN | R92C_CR_ENSEC | R92C_CR_CALTMR_EN; 2030067851b1Skevlo urtwn_write_2(sc, R92C_CR, reg); 2031067851b1Skevlo return (0); 2032067851b1Skevlo } 2033067851b1Skevlo 2034067851b1Skevlo int 203508300c66Skevlo urtwn_llt_init(struct urtwn_softc *sc, int page_count) 20369ec5c125Sdamien { 203708300c66Skevlo int i, error, pktbuf_count; 20389ec5c125Sdamien 203923340becSstsp pktbuf_count = (sc->sc_sc.chip & RTWN_CHIP_88E) ? 2040c349b8bfSstsp R88E_TXPKTBUF_COUNT : R92C_TXPKTBUF_COUNT; 2041c349b8bfSstsp 2042c349b8bfSstsp /* Reserve pages [0; page_count]. */ 2043c349b8bfSstsp for (i = 0; i < page_count; i++) { 20449ec5c125Sdamien if ((error = urtwn_llt_write(sc, i, i + 1)) != 0) 20459ec5c125Sdamien return (error); 20469ec5c125Sdamien } 20479ec5c125Sdamien /* NB: 0xff indicates end-of-list. */ 20489ec5c125Sdamien if ((error = urtwn_llt_write(sc, i, 0xff)) != 0) 20499ec5c125Sdamien return (error); 20509ec5c125Sdamien /* 2051c349b8bfSstsp * Use pages [page_count + 1; pktbuf_count - 1] 20529ec5c125Sdamien * as ring buffer. 20539ec5c125Sdamien */ 2054c349b8bfSstsp for (++i; i < pktbuf_count - 1; i++) { 20559ec5c125Sdamien if ((error = urtwn_llt_write(sc, i, i + 1)) != 0) 20569ec5c125Sdamien return (error); 20579ec5c125Sdamien } 20589ec5c125Sdamien /* Make the last page point to the beginning of the ring buffer. */ 2059c349b8bfSstsp error = urtwn_llt_write(sc, i, page_count + 1); 20609ec5c125Sdamien return (error); 20619ec5c125Sdamien } 20629ec5c125Sdamien 20639ec5c125Sdamien int 206490540544Sjmatthew urtwn_auto_llt_init(struct urtwn_softc *sc) 206590540544Sjmatthew { 206690540544Sjmatthew int ntries; 206790540544Sjmatthew 2068067851b1Skevlo urtwn_write_4(sc, R92E_AUTO_LLT, 2069067851b1Skevlo urtwn_read_4(sc, R92E_AUTO_LLT) | R92E_AUTO_LLT_EN); 207090540544Sjmatthew for (ntries = 0; ntries < 1000; ntries++) { 207190540544Sjmatthew if (!(urtwn_read_4(sc, R92E_AUTO_LLT) & R92E_AUTO_LLT_EN)) 207290540544Sjmatthew return (0); 207390540544Sjmatthew DELAY(2); 207490540544Sjmatthew } 207590540544Sjmatthew 207690540544Sjmatthew return (ETIMEDOUT); 207790540544Sjmatthew } 207890540544Sjmatthew 207990540544Sjmatthew int 208023340becSstsp urtwn_fw_loadpage(void *cookie, int page, uint8_t *buf, int len) 20819ec5c125Sdamien { 208223340becSstsp struct urtwn_softc *sc = cookie; 20839ec5c125Sdamien uint32_t reg; 20848bca19d0Skevlo int maxblksz, off, mlen, error = 0; 20859ec5c125Sdamien 20869ec5c125Sdamien reg = urtwn_read_4(sc, R92C_MCUFWDL); 20879ec5c125Sdamien reg = RW(reg, R92C_MCUFWDL_PAGE, page); 20889ec5c125Sdamien urtwn_write_4(sc, R92C_MCUFWDL, reg); 20899ec5c125Sdamien 20908bca19d0Skevlo maxblksz = (sc->sc_sc.chip & RTWN_CHIP_92E) ? 254 : 196; 20918bca19d0Skevlo 20929ec5c125Sdamien off = R92C_FW_START_ADDR; 20939ec5c125Sdamien while (len > 0) { 20948bca19d0Skevlo if (len > maxblksz) 20958bca19d0Skevlo mlen = maxblksz; 20969ec5c125Sdamien else if (len > 4) 20979ec5c125Sdamien mlen = 4; 20989ec5c125Sdamien else 20999ec5c125Sdamien mlen = 1; 21009ec5c125Sdamien error = urtwn_write_region_1(sc, off, buf, mlen); 21019ec5c125Sdamien if (error != 0) 21029ec5c125Sdamien break; 21039ec5c125Sdamien off += mlen; 21049ec5c125Sdamien buf += mlen; 21059ec5c125Sdamien len -= mlen; 21069ec5c125Sdamien } 21079ec5c125Sdamien return (error); 21089ec5c125Sdamien } 21099ec5c125Sdamien 21109ec5c125Sdamien int 211123340becSstsp urtwn_load_firmware(void *cookie, u_char **fw, size_t *len) 21129ec5c125Sdamien { 211323340becSstsp struct urtwn_softc *sc = cookie; 21149ec5c125Sdamien const char *name; 211523340becSstsp int error; 21169ec5c125Sdamien 211790540544Sjmatthew if (sc->sc_sc.chip & RTWN_CHIP_92E) 21181e1b982aSkevlo name = "urtwn-rtl8192eu"; 211990540544Sjmatthew else if (sc->sc_sc.chip & RTWN_CHIP_88E) 21201e1b982aSkevlo name = "urtwn-rtl8188eu"; 2121067851b1Skevlo else if (sc->sc_sc.chip & RTWN_CHIP_88F) 2122067851b1Skevlo name = "urtwn-rtl8188ftv"; 212323340becSstsp else if ((sc->sc_sc.chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) == 212423340becSstsp RTWN_CHIP_UMC_A_CUT) 21251e1b982aSkevlo name = "urtwn-rtl8192cU"; 21269ec5c125Sdamien else 21271e1b982aSkevlo name = "urtwn-rtl8192cT"; 212823340becSstsp 212923340becSstsp error = loadfirmware(name, fw, len); 213023340becSstsp if (error) 213123340becSstsp printf("%s: could not read firmware %s (error %d)\n", 21329ec5c125Sdamien sc->sc_dev.dv_xname, name, error); 21339ec5c125Sdamien return (error); 21349ec5c125Sdamien } 21359ec5c125Sdamien 213623340becSstsp int 213723340becSstsp urtwn_dma_init(void *cookie) 213823340becSstsp { 213923340becSstsp struct urtwn_softc *sc = cookie; 214008300c66Skevlo uint32_t reg; 214108300c66Skevlo uint16_t dmasize; 214208300c66Skevlo int hqpages, lqpages, nqpages, pagecnt, boundary; 214308300c66Skevlo int error, hashq, haslq, hasnq; 2144c349b8bfSstsp 214508300c66Skevlo /* Default initialization of chipset values. */ 214608300c66Skevlo if (sc->sc_sc.chip & RTWN_CHIP_88E) { 214708300c66Skevlo hqpages = R88E_HQ_NPAGES; 214808300c66Skevlo lqpages = R88E_LQ_NPAGES; 214908300c66Skevlo nqpages = R88E_NQ_NPAGES; 215008300c66Skevlo pagecnt = R88E_TX_PAGE_COUNT; 215108300c66Skevlo dmasize = R88E_MAX_RX_DMA_SIZE; 2152067851b1Skevlo } else if (sc->sc_sc.chip & RTWN_CHIP_88F) { 2153067851b1Skevlo hqpages = R88F_HQ_NPAGES; 2154067851b1Skevlo lqpages = R88F_LQ_NPAGES; 2155067851b1Skevlo nqpages = R88F_NQ_NPAGES; 2156067851b1Skevlo pagecnt = R88F_TX_PAGE_COUNT; 2157067851b1Skevlo dmasize = R88F_MAX_RX_DMA_SIZE; 215890540544Sjmatthew } else if (sc->sc_sc.chip & RTWN_CHIP_92E) { 215990540544Sjmatthew hqpages = R92E_HQ_NPAGES; 216090540544Sjmatthew lqpages = R92E_LQ_NPAGES; 216190540544Sjmatthew nqpages = R92E_NQ_NPAGES; 216290540544Sjmatthew pagecnt = R92E_TX_PAGE_COUNT; 216390540544Sjmatthew dmasize = R92E_MAX_RX_DMA_SIZE; 216408300c66Skevlo } else { 216508300c66Skevlo hqpages = R92C_HQ_NPAGES; 216608300c66Skevlo lqpages = R92C_LQ_NPAGES; 216708300c66Skevlo nqpages = R92C_NQ_NPAGES; 216808300c66Skevlo pagecnt = R92C_TX_PAGE_COUNT; 216908300c66Skevlo dmasize = R92C_MAX_RX_DMA_SIZE; 21709ec5c125Sdamien } 2171067851b1Skevlo boundary = pagecnt + 1; 21729ec5c125Sdamien 21739ec5c125Sdamien /* Initialize LLT table. */ 2174067851b1Skevlo if (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) 217590540544Sjmatthew error = urtwn_auto_llt_init(sc); 2176067851b1Skevlo else 217708300c66Skevlo error = urtwn_llt_init(sc, pagecnt); 21789ec5c125Sdamien if (error != 0) 21799ec5c125Sdamien return (error); 21809ec5c125Sdamien 21819ec5c125Sdamien /* Get Tx queues to USB endpoints mapping. */ 21829ec5c125Sdamien hashq = hasnq = haslq = 0; 218308300c66Skevlo switch (sc->ntx) { 218408300c66Skevlo case 3: 21859ec5c125Sdamien haslq = 1; 218608300c66Skevlo pagecnt -= lqpages; 218708300c66Skevlo /* FALLTHROUGH */ 218808300c66Skevlo case 2: 218908300c66Skevlo hasnq = 1; 219008300c66Skevlo pagecnt -= nqpages; 219108300c66Skevlo /* FALLTHROUGH */ 219208300c66Skevlo case 1: 219308300c66Skevlo hashq = 1; 219408300c66Skevlo pagecnt -= hqpages; 219508300c66Skevlo break; 219608300c66Skevlo } 21979ec5c125Sdamien 21989ec5c125Sdamien /* Set number of pages for normal priority queue. */ 21999ec5c125Sdamien urtwn_write_1(sc, R92C_RQPN_NPQ, hasnq ? nqpages : 0); 22009ec5c125Sdamien urtwn_write_4(sc, R92C_RQPN, 22019ec5c125Sdamien /* Set number of pages for public queue. */ 220208300c66Skevlo SM(R92C_RQPN_PUBQ, pagecnt) | 22039ec5c125Sdamien /* Set number of pages for high priority queue. */ 220408300c66Skevlo SM(R92C_RQPN_HPQ, hashq ? hqpages : 0) | 22059ec5c125Sdamien /* Set number of pages for low priority queue. */ 220608300c66Skevlo SM(R92C_RQPN_LPQ, haslq ? lqpages : 0) | 22079ec5c125Sdamien /* Load values. */ 22089ec5c125Sdamien R92C_RQPN_LD); 22099ec5c125Sdamien 221008300c66Skevlo urtwn_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, boundary); 221108300c66Skevlo urtwn_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, boundary); 221208300c66Skevlo urtwn_write_1(sc, R92C_TXPKTBUF_WMAC_LBK_BF_HD, boundary); 221308300c66Skevlo urtwn_write_1(sc, R92C_TRXFF_BNDY, boundary); 221408300c66Skevlo urtwn_write_1(sc, R92C_TDECTRL + 1, boundary); 22159ec5c125Sdamien 22169ec5c125Sdamien /* Set queue to USB pipe mapping. */ 22179ec5c125Sdamien reg = urtwn_read_2(sc, R92C_TRXDMA_CTRL); 22189ec5c125Sdamien reg &= ~R92C_TRXDMA_CTRL_QMAP_M; 221908300c66Skevlo if (haslq) 222008300c66Skevlo reg |= R92C_TRXDMA_CTRL_QMAP_3EP; 222108300c66Skevlo else if (hashq) { 222208300c66Skevlo if (!hasnq) 22239ec5c125Sdamien reg |= R92C_TRXDMA_CTRL_QMAP_HQ; 22249ec5c125Sdamien else 22259ec5c125Sdamien reg |= R92C_TRXDMA_CTRL_QMAP_HQ_NQ; 22269ec5c125Sdamien } 2227c349b8bfSstsp urtwn_write_2(sc, R92C_TRXDMA_CTRL, reg); 2228c349b8bfSstsp 2229c349b8bfSstsp /* Set Tx/Rx transfer page boundary. */ 223008300c66Skevlo urtwn_write_2(sc, R92C_TRXFF_BNDY + 2, dmasize - 1); 2231c349b8bfSstsp 223290540544Sjmatthew if (!(sc->sc_sc.chip & RTWN_CHIP_92E)) { 2233c349b8bfSstsp /* Set Tx/Rx transfer page size. */ 2234067851b1Skevlo if (sc->sc_sc.chip & RTWN_CHIP_88F) { 2235067851b1Skevlo urtwn_write_1(sc, R92C_PBP, 2236067851b1Skevlo SM(R92C_PBP_PSRX, R92C_PBP_256) | 2237067851b1Skevlo SM(R92C_PBP_PSTX, R92C_PBP_256)); 2238067851b1Skevlo } else { 2239c349b8bfSstsp urtwn_write_1(sc, R92C_PBP, 2240c349b8bfSstsp SM(R92C_PBP_PSRX, R92C_PBP_128) | 2241c349b8bfSstsp SM(R92C_PBP_PSTX, R92C_PBP_128)); 224290540544Sjmatthew } 2243067851b1Skevlo } 224408300c66Skevlo return (error); 2245c349b8bfSstsp } 2246c349b8bfSstsp 22479ec5c125Sdamien void 22482d2a7e26Skevlo urtwn_aggr_init(void *cookie) 22492d2a7e26Skevlo { 22502d2a7e26Skevlo struct urtwn_softc *sc = cookie; 22512d2a7e26Skevlo uint32_t reg = 0; 22522d2a7e26Skevlo int dmasize, dmatiming, ndesc; 22532d2a7e26Skevlo 22542d2a7e26Skevlo /* Set burst packet length. */ 2255067851b1Skevlo if (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) 22562d2a7e26Skevlo urtwn_burstlen_init(sc); 22572d2a7e26Skevlo 2258067851b1Skevlo if (sc->sc_sc.chip & RTWN_CHIP_88F) { 2259067851b1Skevlo dmasize = 5; 2260067851b1Skevlo dmatiming = 32; 2261067851b1Skevlo ndesc = 6; 2262067851b1Skevlo } else if (sc->sc_sc.chip & RTWN_CHIP_92E) { 22632d2a7e26Skevlo dmasize = 6; 22642d2a7e26Skevlo dmatiming = 32; 22652d2a7e26Skevlo ndesc = 3; 22662d2a7e26Skevlo } else { 22672d2a7e26Skevlo dmasize = 48; 22682d2a7e26Skevlo dmatiming = 4; 22692d2a7e26Skevlo ndesc = (sc->sc_sc.chip & RTWN_CHIP_88E) ? 1 : 6; 22702d2a7e26Skevlo } 22712d2a7e26Skevlo 22722d2a7e26Skevlo /* Tx aggregation setting. */ 22732d2a7e26Skevlo reg = urtwn_read_4(sc, R92C_TDECTRL); 22742d2a7e26Skevlo reg = RW(reg, R92C_TDECTRL_BLK_DESC_NUM, ndesc); 22752d2a7e26Skevlo urtwn_write_4(sc, R92C_TDECTRL, reg); 2276067851b1Skevlo if (sc->sc_sc.chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) 22778bca19d0Skevlo urtwn_write_1(sc, R92E_DWBCN1_CTRL, ndesc << 1); 22782d2a7e26Skevlo 22792d2a7e26Skevlo /* Rx aggregation setting. */ 22802d2a7e26Skevlo urtwn_write_1(sc, R92C_TRXDMA_CTRL, 22818bca19d0Skevlo urtwn_read_1(sc, R92C_TRXDMA_CTRL) | R92C_TRXDMA_CTRL_RXDMA_AGG_EN); 22822d2a7e26Skevlo 22832d2a7e26Skevlo urtwn_write_1(sc, R92C_RXDMA_AGG_PG_TH, dmasize); 22842d2a7e26Skevlo if (sc->sc_sc.chip & (RTWN_CHIP_92C | RTWN_CHIP_88C)) 22852d2a7e26Skevlo urtwn_write_1(sc, R92C_USB_DMA_AGG_TO, dmatiming); 22862d2a7e26Skevlo else 22872d2a7e26Skevlo urtwn_write_1(sc, R92C_RXDMA_AGG_PG_TH + 1, dmatiming); 22882d2a7e26Skevlo 2289067851b1Skevlo if (sc->sc_sc.chip & RTWN_CHIP_88F) { 2290067851b1Skevlo urtwn_write_1(sc, R92E_RXDMA_PRO, 2291067851b1Skevlo urtwn_read_1(sc, R92E_RXDMA_PRO) | R92E_RXDMA_PRO_DMA_MODE); 2292067851b1Skevlo } 2293067851b1Skevlo 22942d2a7e26Skevlo /* Drop incorrect bulk out. */ 22952d2a7e26Skevlo urtwn_write_4(sc, R92C_TXDMA_OFFSET_CHK, 22962d2a7e26Skevlo urtwn_read_4(sc, R92C_TXDMA_OFFSET_CHK) | 22972d2a7e26Skevlo R92C_TXDMA_OFFSET_CHK_DROP_DATA_EN); 22982d2a7e26Skevlo } 22992d2a7e26Skevlo 23002d2a7e26Skevlo void 230123340becSstsp urtwn_mac_init(void *cookie) 23029ec5c125Sdamien { 230323340becSstsp struct urtwn_softc *sc = cookie; 23049ec5c125Sdamien int i; 23059ec5c125Sdamien 23069ec5c125Sdamien /* Write MAC initialization values. */ 230723340becSstsp if (sc->sc_sc.chip & RTWN_CHIP_88E) { 2308c349b8bfSstsp for (i = 0; i < nitems(rtl8188eu_mac); i++) { 2309c349b8bfSstsp urtwn_write_1(sc, rtl8188eu_mac[i].reg, 2310c349b8bfSstsp rtl8188eu_mac[i].val); 2311c349b8bfSstsp } 2312c349b8bfSstsp urtwn_write_1(sc, R92C_MAX_AGGR_NUM, 0x07); 2313067851b1Skevlo } else if (sc->sc_sc.chip & RTWN_CHIP_88F) { 2314067851b1Skevlo for (i = 0; i < nitems(rtl8188ftv_mac); i++) { 2315067851b1Skevlo urtwn_write_1(sc, rtl8188ftv_mac[i].reg, 2316067851b1Skevlo rtl8188ftv_mac[i].val); 2317067851b1Skevlo } 231890540544Sjmatthew } else if (sc->sc_sc.chip & RTWN_CHIP_92E) { 231990540544Sjmatthew for (i = 0; i < nitems(rtl8192eu_mac); i++) { 232090540544Sjmatthew urtwn_write_1(sc, rtl8192eu_mac[i].reg, 232190540544Sjmatthew rtl8192eu_mac[i].val); 232290540544Sjmatthew } 2323c349b8bfSstsp } else { 23249ec5c125Sdamien for (i = 0; i < nitems(rtl8192cu_mac); i++) 2325c349b8bfSstsp urtwn_write_1(sc, rtl8192cu_mac[i].reg, 2326c349b8bfSstsp rtl8192cu_mac[i].val); 2327c349b8bfSstsp } 23289ec5c125Sdamien } 23299ec5c125Sdamien 23309ec5c125Sdamien void 233123340becSstsp urtwn_bb_init(void *cookie) 23329ec5c125Sdamien { 233323340becSstsp struct urtwn_softc *sc = cookie; 233453c3c4d0Sstsp const struct r92c_bb_prog *prog; 23359ec5c125Sdamien uint32_t reg; 233634a2cadeSkevlo uint8_t xtal; 23379ec5c125Sdamien int i; 23389ec5c125Sdamien 23399ec5c125Sdamien /* Enable BB and RF. */ 23409ec5c125Sdamien urtwn_write_2(sc, R92C_SYS_FUNC_EN, 23419ec5c125Sdamien urtwn_read_2(sc, R92C_SYS_FUNC_EN) | 23429ec5c125Sdamien R92C_SYS_FUNC_EN_BBRSTB | R92C_SYS_FUNC_EN_BB_GLB_RST | 23439ec5c125Sdamien R92C_SYS_FUNC_EN_DIO_RF); 23449ec5c125Sdamien 2345067851b1Skevlo if (!(sc->sc_sc.chip & (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E))) 23469ec5c125Sdamien urtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0xdb83); 23479ec5c125Sdamien 23489ec5c125Sdamien urtwn_write_1(sc, R92C_RF_CTRL, 23499ec5c125Sdamien R92C_RF_CTRL_EN | R92C_RF_CTRL_RSTB | R92C_RF_CTRL_SDMRSTB); 23509ec5c125Sdamien urtwn_write_1(sc, R92C_SYS_FUNC_EN, 23519ec5c125Sdamien R92C_SYS_FUNC_EN_USBA | R92C_SYS_FUNC_EN_USBD | 23529ec5c125Sdamien R92C_SYS_FUNC_EN_BB_GLB_RST | R92C_SYS_FUNC_EN_BBRSTB); 23539ec5c125Sdamien 2354067851b1Skevlo if (!(sc->sc_sc.chip & 2355067851b1Skevlo (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E))) { 23569ec5c125Sdamien urtwn_write_1(sc, R92C_LDOHCI12_CTRL, 0x0f); 23579ec5c125Sdamien urtwn_write_1(sc, 0x15, 0xe9); 23589ec5c125Sdamien urtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80); 2359c349b8bfSstsp } 23609ec5c125Sdamien 23619ec5c125Sdamien /* Select BB programming based on board type. */ 236223340becSstsp if (sc->sc_sc.chip & RTWN_CHIP_88E) 2363c349b8bfSstsp prog = &rtl8188eu_bb_prog; 2364067851b1Skevlo else if (sc->sc_sc.chip & RTWN_CHIP_88F) 2365067851b1Skevlo prog = &rtl8188ftv_bb_prog; 236690540544Sjmatthew else if (sc->sc_sc.chip & RTWN_CHIP_92E) 236790540544Sjmatthew prog = &rtl8192eu_bb_prog; 236823340becSstsp else if (!(sc->sc_sc.chip & RTWN_CHIP_92C)) { 236923340becSstsp if (sc->sc_sc.board_type == R92C_BOARD_TYPE_MINICARD) 23709ec5c125Sdamien prog = &rtl8188ce_bb_prog; 237123340becSstsp else if (sc->sc_sc.board_type == R92C_BOARD_TYPE_HIGHPA) 23729ec5c125Sdamien prog = &rtl8188ru_bb_prog; 23739ec5c125Sdamien else 23749ec5c125Sdamien prog = &rtl8188cu_bb_prog; 23759ec5c125Sdamien } else { 237623340becSstsp if (sc->sc_sc.board_type == R92C_BOARD_TYPE_MINICARD) 23779ec5c125Sdamien prog = &rtl8192ce_bb_prog; 23789ec5c125Sdamien else 23799ec5c125Sdamien prog = &rtl8192cu_bb_prog; 23809ec5c125Sdamien } 23819ec5c125Sdamien /* Write BB initialization values. */ 23829ec5c125Sdamien for (i = 0; i < prog->count; i++) { 23839ec5c125Sdamien urtwn_bb_write(sc, prog->regs[i], prog->vals[i]); 23849ec5c125Sdamien DELAY(1); 23859ec5c125Sdamien } 23869ec5c125Sdamien 238723340becSstsp if (sc->sc_sc.chip & RTWN_CHIP_92C_1T2R) { 23889ec5c125Sdamien /* 8192C 1T only configuration. */ 23899ec5c125Sdamien reg = urtwn_bb_read(sc, R92C_FPGA0_TXINFO); 23909ec5c125Sdamien reg = (reg & ~0x00000003) | 0x2; 23919ec5c125Sdamien urtwn_bb_write(sc, R92C_FPGA0_TXINFO, reg); 23929ec5c125Sdamien 23939ec5c125Sdamien reg = urtwn_bb_read(sc, R92C_FPGA1_TXINFO); 23949ec5c125Sdamien reg = (reg & ~0x00300033) | 0x00200022; 23959ec5c125Sdamien urtwn_bb_write(sc, R92C_FPGA1_TXINFO, reg); 23969ec5c125Sdamien 23979ec5c125Sdamien reg = urtwn_bb_read(sc, R92C_CCK0_AFESETTING); 23989ec5c125Sdamien reg = (reg & ~0xff000000) | 0x45 << 24; 23999ec5c125Sdamien urtwn_bb_write(sc, R92C_CCK0_AFESETTING, reg); 24009ec5c125Sdamien 24019ec5c125Sdamien reg = urtwn_bb_read(sc, R92C_OFDM0_TRXPATHENA); 24029ec5c125Sdamien reg = (reg & ~0x000000ff) | 0x23; 24039ec5c125Sdamien urtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, reg); 24049ec5c125Sdamien 24059ec5c125Sdamien reg = urtwn_bb_read(sc, R92C_OFDM0_AGCPARAM1); 24069ec5c125Sdamien reg = (reg & ~0x00000030) | 1 << 4; 24079ec5c125Sdamien urtwn_bb_write(sc, R92C_OFDM0_AGCPARAM1, reg); 24089ec5c125Sdamien 24099ec5c125Sdamien reg = urtwn_bb_read(sc, 0xe74); 24109ec5c125Sdamien reg = (reg & ~0x0c000000) | 2 << 26; 24119ec5c125Sdamien urtwn_bb_write(sc, 0xe74, reg); 24129ec5c125Sdamien reg = urtwn_bb_read(sc, 0xe78); 24139ec5c125Sdamien reg = (reg & ~0x0c000000) | 2 << 26; 24149ec5c125Sdamien urtwn_bb_write(sc, 0xe78, reg); 24159ec5c125Sdamien reg = urtwn_bb_read(sc, 0xe7c); 24169ec5c125Sdamien reg = (reg & ~0x0c000000) | 2 << 26; 24179ec5c125Sdamien urtwn_bb_write(sc, 0xe7c, reg); 24189ec5c125Sdamien reg = urtwn_bb_read(sc, 0xe80); 24199ec5c125Sdamien reg = (reg & ~0x0c000000) | 2 << 26; 24209ec5c125Sdamien urtwn_bb_write(sc, 0xe80, reg); 24219ec5c125Sdamien reg = urtwn_bb_read(sc, 0xe88); 24229ec5c125Sdamien reg = (reg & ~0x0c000000) | 2 << 26; 24239ec5c125Sdamien urtwn_bb_write(sc, 0xe88, reg); 24249ec5c125Sdamien } 24259ec5c125Sdamien 24269ec5c125Sdamien /* Write AGC values. */ 24279ec5c125Sdamien for (i = 0; i < prog->agccount; i++) { 24289ec5c125Sdamien urtwn_bb_write(sc, R92C_OFDM0_AGCRSSITABLE, 24299ec5c125Sdamien prog->agcvals[i]); 24309ec5c125Sdamien DELAY(1); 24319ec5c125Sdamien } 24329ec5c125Sdamien 2433067851b1Skevlo if (sc->sc_sc.chip & (RTWN_CHIP_88E | RTWN_CHIP_88F)) { 2434c349b8bfSstsp urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x69553422); 2435c349b8bfSstsp DELAY(1); 2436c349b8bfSstsp urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x69553420); 2437c349b8bfSstsp DELAY(1); 243890540544Sjmatthew } else if (sc->sc_sc.chip & RTWN_CHIP_92E) { 243990540544Sjmatthew urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x00040022); 244090540544Sjmatthew DELAY(1); 244190540544Sjmatthew urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x00040020); 244290540544Sjmatthew DELAY(1); 244390540544Sjmatthew } 2444c349b8bfSstsp 2445067851b1Skevlo if (sc->sc_sc.chip & (RTWN_CHIP_88E | RTWN_CHIP_88F)) { 244634a2cadeSkevlo xtal = sc->sc_sc.crystal_cap & 0x3f; 2447c349b8bfSstsp reg = urtwn_bb_read(sc, R92C_AFE_XTAL_CTRL); 2448c349b8bfSstsp urtwn_bb_write(sc, R92C_AFE_XTAL_CTRL, 244934a2cadeSkevlo RW(reg, R92C_AFE_XTAL_CTRL_ADDR, xtal | xtal << 6)); 245090540544Sjmatthew } else if (sc->sc_sc.chip & RTWN_CHIP_92E) { 245190540544Sjmatthew xtal = sc->sc_sc.crystal_cap & 0x3f; 24528bca19d0Skevlo reg = urtwn_bb_read(sc, R92C_AFE_CTRL3); 24538bca19d0Skevlo urtwn_bb_write(sc, R92C_AFE_CTRL3, 24548bca19d0Skevlo RW(reg, R92C_AFE_CTRL3_ADDR, xtal | xtal << 6)); 245590540544Sjmatthew urtwn_write_4(sc, R92C_AFE_XTAL_CTRL, 0x000f81fb); 24569ec5c125Sdamien } 245723f3755dSstsp 245823f3755dSstsp if (urtwn_bb_read(sc, R92C_HSSI_PARAM2(0)) & R92C_HSSI_PARAM2_CCK_HIPWR) 245923f3755dSstsp sc->sc_sc.sc_flags |= RTWN_FLAG_CCK_HIPWR; 24609ec5c125Sdamien } 24619ec5c125Sdamien 24622d2a7e26Skevlo void 24632d2a7e26Skevlo urtwn_burstlen_init(struct urtwn_softc *sc) 24642d2a7e26Skevlo { 24652d2a7e26Skevlo uint8_t reg; 24662d2a7e26Skevlo 24672d2a7e26Skevlo reg = urtwn_read_1(sc, R92E_RXDMA_PRO); 24682d2a7e26Skevlo reg &= ~0x30; 24692d2a7e26Skevlo switch (sc->sc_udev->speed) { 24702d2a7e26Skevlo case USB_SPEED_HIGH: 24712d2a7e26Skevlo urtwn_write_1(sc, R92E_RXDMA_PRO, reg | 0x1e); 24722d2a7e26Skevlo break; 24732d2a7e26Skevlo default: 24742d2a7e26Skevlo urtwn_write_1(sc, R92E_RXDMA_PRO, reg | 0x2e); 24752d2a7e26Skevlo break; 24762d2a7e26Skevlo } 2477067851b1Skevlo 2478067851b1Skevlo if (sc->sc_sc.chip & RTWN_CHIP_88F) { 2479067851b1Skevlo /* Setup AMPDU aggregation. */ 2480067851b1Skevlo urtwn_write_1(sc, R88F_HT_SINGLE_AMPDU, 2481067851b1Skevlo urtwn_read_1(sc, R88F_HT_SINGLE_AMPDU) | 2482067851b1Skevlo R88F_HT_SINGLE_AMPDU_EN); 2483067851b1Skevlo urtwn_write_2(sc, R92C_MAX_AGGR_NUM, 0x0c14); 2484067851b1Skevlo urtwn_write_1(sc, R88F_AMPDU_MAX_TIME, 0x70); 2485067851b1Skevlo urtwn_write_4(sc, R92C_AGGLEN_LMT, 0xffffffff); 2486067851b1Skevlo 2487067851b1Skevlo /* For VHT packet length 11K */ 2488067851b1Skevlo urtwn_write_1(sc, R88F_RX_PKT_LIMIT, 0x18); 2489067851b1Skevlo 2490067851b1Skevlo urtwn_write_1(sc, R92C_PIFS, 0); 2491067851b1Skevlo urtwn_write_1(sc, R92C_FWHW_TXQ_CTRL, 0x80); 2492067851b1Skevlo urtwn_write_4(sc, R92C_FAST_EDCA_CTRL, 0x03086666); 2493067851b1Skevlo urtwn_write_1(sc, R92C_USTIME_TSF, 0x28); 2494067851b1Skevlo urtwn_write_1(sc, R88F_USTIME_EDCA, 0x28); 2495067851b1Skevlo 249654fbbda3Sjsg /* To prevent bus resetting the mac. */ 2497067851b1Skevlo urtwn_write_1(sc, R92C_RSV_CTRL, 2498067851b1Skevlo urtwn_read_1(sc, R92C_RSV_CTRL) | 2499067851b1Skevlo R92C_RSV_CTRL_R_DIS_PRST_0 | R92C_RSV_CTRL_R_DIS_PRST_1); 2500067851b1Skevlo } 25012d2a7e26Skevlo } 25022d2a7e26Skevlo 25039ec5c125Sdamien int 250423340becSstsp urtwn_power_on(void *cookie) 25059ec5c125Sdamien { 250623340becSstsp struct urtwn_softc *sc = cookie; 25079ec5c125Sdamien 250823340becSstsp if (sc->sc_sc.chip & RTWN_CHIP_88E) 250934a2cadeSkevlo return (urtwn_r88e_power_on(sc)); 2510067851b1Skevlo else if (sc->sc_sc.chip & RTWN_CHIP_88F) 2511067851b1Skevlo return (urtwn_r88f_power_on(sc)); 251290540544Sjmatthew else if (sc->sc_sc.chip & RTWN_CHIP_92E) 251390540544Sjmatthew return (urtwn_r92e_power_on(sc)); 25149ec5c125Sdamien 251534a2cadeSkevlo return (urtwn_r92c_power_on(sc)); 25165e709a81Sdamien } 25175e709a81Sdamien 25189ec5c125Sdamien int 251923340becSstsp urtwn_alloc_buffers(void *cookie) 25209ec5c125Sdamien { 252123340becSstsp struct urtwn_softc *sc = cookie; 252223340becSstsp int error; 25239ec5c125Sdamien 25249ec5c125Sdamien /* Init host async commands ring. */ 25259ec5c125Sdamien sc->cmdq.cur = sc->cmdq.next = sc->cmdq.queued = 0; 25269ec5c125Sdamien 25279ec5c125Sdamien /* Allocate Tx/Rx buffers. */ 25289ec5c125Sdamien error = urtwn_alloc_rx_list(sc); 25299ec5c125Sdamien if (error != 0) { 25309ec5c125Sdamien printf("%s: could not allocate Rx buffers\n", 25319ec5c125Sdamien sc->sc_dev.dv_xname); 253223340becSstsp return (error); 25339ec5c125Sdamien } 25349ec5c125Sdamien error = urtwn_alloc_tx_list(sc); 25359ec5c125Sdamien if (error != 0) { 25369ec5c125Sdamien printf("%s: could not allocate Tx buffers\n", 25379ec5c125Sdamien sc->sc_dev.dv_xname); 253823340becSstsp return (error); 25399ec5c125Sdamien } 2540c349b8bfSstsp 254123340becSstsp return (0); 2542c349b8bfSstsp } 25439ec5c125Sdamien 254423340becSstsp int 254523340becSstsp urtwn_init(void *cookie) 254623340becSstsp { 254723340becSstsp struct urtwn_softc *sc = cookie; 254823340becSstsp int i, error; 25499ec5c125Sdamien 25508bca19d0Skevlo /* Reset USB mode switch setting. */ 255190540544Sjmatthew if (sc->sc_sc.chip & RTWN_CHIP_92E) 255290540544Sjmatthew urtwn_write_1(sc, R92C_ACLK_MON, 0); 255390540544Sjmatthew 25549ec5c125Sdamien /* Queue Rx xfers. */ 25559ec5c125Sdamien for (i = 0; i < URTWN_RX_LIST_COUNT; i++) { 255623340becSstsp struct urtwn_rx_data *data = &sc->rx_data[i]; 25579ec5c125Sdamien 25589ec5c125Sdamien usbd_setup_xfer(data->xfer, sc->rx_pipe, data, data->buf, 25599ec5c125Sdamien URTWN_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, 25609ec5c125Sdamien USBD_NO_TIMEOUT, urtwn_rxeof); 25619ec5c125Sdamien error = usbd_transfer(data->xfer); 25629ec5c125Sdamien if (error != 0 && error != USBD_IN_PROGRESS) 25639ec5c125Sdamien return (error); 25649ec5c125Sdamien } 25659ec5c125Sdamien 2566a087613eSstsp ieee80211_amrr_node_init(&sc->amrr, &sc->amn); 2567a087613eSstsp 2568a087613eSstsp /* 2569a087613eSstsp * Enable TX reports for AMRR. 2570a087613eSstsp * In order to get reports we need to explicitly reset the register. 2571a087613eSstsp */ 2572a087613eSstsp if (sc->sc_sc.chip & RTWN_CHIP_88E) 2573a087613eSstsp urtwn_write_1(sc, R88E_TX_RPT_CTRL, (urtwn_read_1(sc, 25742f5d27e0Skevlo R88E_TX_RPT_CTRL) & ~0) | R88E_TX_RPT_CTRL_EN); 2575a087613eSstsp 257623340becSstsp return (0); 257723340becSstsp } 257823340becSstsp 25799ec5c125Sdamien void 258023340becSstsp urtwn_stop(void *cookie) 25819ec5c125Sdamien { 258223340becSstsp struct urtwn_softc *sc = cookie; 258323340becSstsp int i; 25849ec5c125Sdamien 25859ec5c125Sdamien /* Abort Tx. */ 25869ec5c125Sdamien for (i = 0; i < R92C_MAX_EPOUT; i++) { 25879ec5c125Sdamien if (sc->tx_pipe[i] != NULL) 25889ec5c125Sdamien usbd_abort_pipe(sc->tx_pipe[i]); 25899ec5c125Sdamien } 25909ec5c125Sdamien /* Stop Rx pipe. */ 25919ec5c125Sdamien usbd_abort_pipe(sc->rx_pipe); 25929ec5c125Sdamien /* Free Tx/Rx buffers. */ 25939ec5c125Sdamien urtwn_free_tx_list(sc); 25949ec5c125Sdamien urtwn_free_rx_list(sc); 25959ec5c125Sdamien } 259623340becSstsp 259723340becSstsp int 259823340becSstsp urtwn_is_oactive(void *cookie) 259923340becSstsp { 260023340becSstsp struct urtwn_softc *sc = cookie; 260123340becSstsp 260223340becSstsp return (TAILQ_EMPTY(&sc->tx_free_list)); 260323340becSstsp } 2604