1 /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> 5 * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org> 6 * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/cdefs.h> 22 #include "opt_wlan.h" 23 24 #include <sys/param.h> 25 #include <sys/lock.h> 26 #include <sys/mutex.h> 27 #include <sys/mbuf.h> 28 #include <sys/kernel.h> 29 #include <sys/socket.h> 30 #include <sys/systm.h> 31 #include <sys/malloc.h> 32 #include <sys/queue.h> 33 #include <sys/taskqueue.h> 34 #include <sys/bus.h> 35 #include <sys/endian.h> 36 #include <sys/linker.h> 37 38 #include <net/if.h> 39 #include <net/ethernet.h> 40 #include <net/if_media.h> 41 42 #include <net80211/ieee80211_var.h> 43 #include <net80211/ieee80211_radiotap.h> 44 45 #include <dev/rtwn/if_rtwnreg.h> 46 #include <dev/rtwn/if_rtwnvar.h> 47 48 #include <dev/rtwn/if_rtwn_debug.h> 49 50 #include <dev/rtwn/rtl8188e/r88e.h> 51 #include <dev/rtwn/rtl8188e/r88e_reg.h> 52 #include <dev/rtwn/rtl8188e/r88e_fw_cmd.h> 53 54 #ifndef RTWN_WITHOUT_UCODE 55 int 56 r88e_fw_cmd(struct rtwn_softc *sc, uint8_t id, const void *buf, int len) 57 { 58 struct r88e_fw_cmd cmd; 59 int ntries, error; 60 61 if (!(sc->sc_flags & RTWN_FW_LOADED)) { 62 RTWN_DPRINTF(sc, RTWN_DEBUG_FIRMWARE, "%s: firmware " 63 "was not loaded; command (id %d) will be discarded\n", 64 __func__, id); 65 return (0); 66 } 67 68 /* Wait for current FW box to be empty. */ 69 for (ntries = 0; ntries < 100; ntries++) { 70 if (!(rtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur))) 71 break; 72 rtwn_delay(sc, 2000); 73 } 74 if (ntries == 100) { 75 device_printf(sc->sc_dev, 76 "could not send firmware command\n"); 77 return (ETIMEDOUT); 78 } 79 memset(&cmd, 0, sizeof(cmd)); 80 cmd.id = id; 81 KASSERT(len <= sizeof(cmd.msg), 82 ("%s: firmware command too long (%d > %zu)\n", 83 __func__, len, sizeof(cmd.msg))); 84 memcpy(cmd.msg, buf, len); 85 86 /* Write the first word last since that will trigger the FW. */ 87 if (len > 3) { 88 error = rtwn_write_4(sc, R88E_HMEBOX_EXT(sc->fwcur), 89 *(uint32_t *)((uint8_t *)&cmd + 4)); 90 if (error != 0) 91 return (error); 92 } 93 error = rtwn_write_4(sc, R92C_HMEBOX(sc->fwcur), *(uint32_t *)&cmd); 94 if (error != 0) 95 return (error); 96 97 sc->fwcur = (sc->fwcur + 1) % R92C_H2C_NBOX; 98 99 return (0); 100 } 101 102 void 103 r88e_fw_reset(struct rtwn_softc *sc, int reason) 104 { 105 uint16_t reg; 106 107 reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN); 108 rtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN); 109 110 if (reason != RTWN_FW_RESET_SHUTDOWN) { 111 rtwn_write_2(sc, R92C_SYS_FUNC_EN, 112 reg | R92C_SYS_FUNC_EN_CPUEN); 113 } 114 } 115 116 void 117 r88e_fw_download_enable(struct rtwn_softc *sc, int enable) 118 { 119 if (enable) { 120 /* MCU firmware download enable. */ 121 rtwn_setbits_1(sc, R92C_MCUFWDL, 0, R92C_MCUFWDL_EN); 122 /* 8051 reset. */ 123 rtwn_setbits_1_shift(sc, R92C_MCUFWDL, R92C_MCUFWDL_ROM_DLEN, 124 0, 2); 125 } else { 126 /* MCU download disable. */ 127 rtwn_setbits_1(sc, R92C_MCUFWDL, R92C_MCUFWDL_EN, 0); 128 /* Reserved for f/w extension. */ 129 rtwn_write_1(sc, R92C_MCUFWDL + 1, 0); 130 } 131 } 132 #endif 133 134 void 135 r88e_macid_enable_link(struct rtwn_softc *sc, int id, int enable) 136 { 137 uint32_t reg; 138 139 reg = R88E_MACID_NO_LINK; 140 if (id > 32) 141 reg += 4; 142 143 if (enable) 144 rtwn_setbits_4(sc, reg, 1 << (id % 32), 0); 145 else 146 rtwn_setbits_4(sc, reg, 0, 1 << (id % 32)); 147 148 /* XXX max macid for tx reports */ 149 } 150 151 void 152 r88e_set_media_status(struct rtwn_softc *sc, int macid) 153 { 154 struct r88e_fw_cmd_msrrpt status; 155 156 if (macid & RTWN_MACID_VALID) 157 status.msrb0 = R88E_MSRRPT_B0_ASSOC; 158 else 159 status.msrb0 = R88E_MSRRPT_B0_DISASSOC; 160 status.macid = (macid & ~RTWN_MACID_VALID); 161 162 r88e_macid_enable_link(sc, status.macid, 163 (macid & RTWN_MACID_VALID) != 0); 164 165 #ifndef RTWN_WITHOUT_UCODE 166 if (r88e_fw_cmd(sc, R88E_CMD_MSR_RPT, &status, sizeof(status)) != 0) { 167 device_printf(sc->sc_dev, "%s: cannot change media status!\n", 168 __func__); 169 } 170 #endif 171 } 172 173 #ifndef RTWN_WITHOUT_UCODE 174 int 175 r88e_set_rsvd_page(struct rtwn_softc *sc, int probe_resp, int null, 176 int qos_null) 177 { 178 struct r88e_fw_cmd_rsvdpage rsvd; 179 180 rsvd.probe_resp = probe_resp; 181 rsvd.ps_poll = 0; 182 rsvd.null_data = null; 183 rsvd.null_data_qos = qos_null; 184 rsvd.null_data_qos_bt = 0; 185 return (r88e_fw_cmd(sc, R88E_CMD_RSVD_PAGE, &rsvd, sizeof(rsvd))); 186 } 187 188 int 189 r88e_set_pwrmode(struct rtwn_softc *sc, struct ieee80211vap *vap, 190 int off) 191 { 192 struct r88e_fw_cmd_pwrmode mode; 193 int error; 194 195 if (off && vap->iv_state == IEEE80211_S_RUN && 196 (vap->iv_flags & IEEE80211_F_PMGTON)) { 197 mode.mode = R88E_PWRMODE_LEG; 198 /* 199 * TODO: switch to RFOFF state 200 * (something is missing here - Rx stops with it). 201 */ 202 #ifdef RTWN_TODO 203 mode.pwr_state = R88E_PWRMODE_STATE_RFOFF; 204 #else 205 mode.pwr_state = R88E_PWRMODE_STATE_RFON; 206 #endif 207 } else { 208 mode.mode = R88E_PWRMODE_CAM; 209 mode.pwr_state = R88E_PWRMODE_STATE_ALLON; 210 } 211 mode.pwrb1 = 212 SM(R88E_PWRMODE_B1_SMART_PS, R88E_PWRMODE_B1_LEG_NULLDATA) | 213 SM(R88E_PWRMODE_B1_RLBM, R88E_PWRMODE_B1_MODE_MIN); 214 /* XXX ignored */ 215 mode.bcn_pass = 0; 216 mode.queue_uapsd = 0; 217 error = r88e_fw_cmd(sc, R88E_CMD_SET_PWRMODE, &mode, sizeof(mode)); 218 if (error != 0) { 219 device_printf(sc->sc_dev, 220 "%s: CMD_SET_PWRMODE was not sent, error %d\n", 221 __func__, error); 222 } 223 224 return (error); 225 } 226 #endif 227