#include <sys/cdefs.h>
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#include <sys/taskqueue.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/if_media.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_radiotap.h>
#include <dev/rtwn/if_rtwnreg.h>
#include <dev/rtwn/if_rtwnvar.h>
#include <dev/rtwn/if_rtwn_cam.h>
#include <dev/rtwn/if_rtwn_debug.h>
#include <dev/rtwn/if_rtwn_task.h>
#include <dev/rtwn/rtl8192c/r92c_reg.h>
void
rtwn_init_cam(struct rtwn_softc *sc)
{
rtwn_write_4(sc, R92C_CAMCMD,
R92C_CAMCMD_POLLING | R92C_CAMCMD_CLR);
}
static int
rtwn_cam_write(struct rtwn_softc *sc, uint32_t addr, uint32_t data)
{
int error;
error = rtwn_write_4(sc, R92C_CAMWRITE, data);
if (error != 0)
return (error);
error = rtwn_write_4(sc, R92C_CAMCMD,
R92C_CAMCMD_POLLING | R92C_CAMCMD_WRITE |
SM(R92C_CAMCMD_ADDR, addr));
return (error);
}
void
rtwn_init_seccfg(struct rtwn_softc *sc)
{
uint16_t seccfg;
seccfg = 0;
switch (sc->sc_hwcrypto) {
case RTWN_CRYPTO_SW:
break;
case RTWN_CRYPTO_PAIR:
seccfg = R92C_SECCFG_TXUCKEY_DEF | R92C_SECCFG_RXUCKEY_DEF |
R92C_SECCFG_TXENC_ENA | R92C_SECCFG_RXDEC_ENA |
R92C_SECCFG_MC_SRCH_DIS;
break;
case RTWN_CRYPTO_FULL:
seccfg = R92C_SECCFG_TXUCKEY_DEF | R92C_SECCFG_RXUCKEY_DEF |
R92C_SECCFG_TXENC_ENA | R92C_SECCFG_RXDEC_ENA |
R92C_SECCFG_TXBCKEY_DEF | R92C_SECCFG_RXBCKEY_DEF;
break;
default:
KASSERT(0, ("%s: case %d was not handled\n", __func__,
sc->sc_hwcrypto));
break;
}
RTWN_DPRINTF(sc, RTWN_DEBUG_KEY, "%s: seccfg %04X, hwcrypto %d\n",
__func__, seccfg, sc->sc_hwcrypto);
rtwn_write_2(sc, R92C_SECCFG, seccfg);
}
int
rtwn_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
struct rtwn_softc *sc = vap->iv_ic->ic_softc;
int i, start;
if (ieee80211_is_key_global(vap, k)) {
*keyix = ieee80211_crypto_get_key_wepidx(vap, k);
if (sc->sc_hwcrypto != RTWN_CRYPTO_FULL)
k->wk_flags |= IEEE80211_KEY_SWCRYPT;
else {
RTWN_LOCK(sc);
if (isset(sc->keys_bmap, *keyix)) {
device_printf(sc->sc_dev,
"%s: group key slot %d is already used!\n",
__func__, *keyix);
RTWN_UNLOCK(sc);
return (0);
}
setbit(sc->keys_bmap, *keyix);
RTWN_UNLOCK(sc);
}
goto end;
}
start = sc->cam_entry_limit;
switch (sc->sc_hwcrypto) {
case RTWN_CRYPTO_SW:
k->wk_flags |= IEEE80211_KEY_SWCRYPT;
*keyix = 0;
goto end;
case RTWN_CRYPTO_PAIR:
start = 0;
RTWN_LOCK(sc);
if (sc->sc_flags & RTWN_FLAG_CAM_FIXED)
start = 4;
RTWN_UNLOCK(sc);
break;
case RTWN_CRYPTO_FULL:
start = 4;
break;
default:
KASSERT(0, ("%s: case %d was not handled!\n",
__func__, sc->sc_hwcrypto));
break;
}
RTWN_LOCK(sc);
for (i = start; i < sc->cam_entry_limit; i++) {
if (isclr(sc->keys_bmap, i)) {
setbit(sc->keys_bmap, i);
*keyix = i;
break;
}
}
RTWN_UNLOCK(sc);
if (i == sc->cam_entry_limit) {
k->wk_flags |= IEEE80211_KEY_SWCRYPT;
*keyix = 0;
}
end:
*rxkeyix = *keyix;
return (1);
}
static int
rtwn_key_set_cb0(struct rtwn_softc *sc, const struct ieee80211_key *k)
{
uint8_t algo, keyid;
int i, error;
if (sc->sc_hwcrypto == RTWN_CRYPTO_FULL &&
k->wk_keyix < IEEE80211_WEP_NKID)
keyid = k->wk_keyix;
else
keyid = 0;
switch (k->wk_cipher->ic_cipher) {
case IEEE80211_CIPHER_WEP:
if (k->wk_keylen < 8)
algo = R92C_CAM_ALGO_WEP40;
else
algo = R92C_CAM_ALGO_WEP104;
break;
case IEEE80211_CIPHER_TKIP:
algo = R92C_CAM_ALGO_TKIP;
break;
case IEEE80211_CIPHER_AES_CCM:
algo = R92C_CAM_ALGO_AES;
break;
default:
device_printf(sc->sc_dev, "%s: unknown cipher %u\n",
__func__, k->wk_cipher->ic_cipher);
return (EINVAL);
}
RTWN_DPRINTF(sc, RTWN_DEBUG_KEY,
"%s: keyix %u, keyid %u, algo %u/%u, flags %04X, len %u, "
"macaddr %s\n", __func__, k->wk_keyix, keyid,
k->wk_cipher->ic_cipher, algo, k->wk_flags, k->wk_keylen,
ether_sprintf(k->wk_macaddr));
rtwn_cam_write(sc, R92C_CAM_CTL6(k->wk_keyix), 0);
rtwn_cam_write(sc, R92C_CAM_CTL7(k->wk_keyix), 0);
for (i = 0; i < 4; i++) {
error = rtwn_cam_write(sc, R92C_CAM_KEY(k->wk_keyix, i),
le32dec(&k->wk_key[i * 4]));
if (error != 0)
goto fail;
}
error = rtwn_cam_write(sc, R92C_CAM_CTL1(k->wk_keyix),
le32dec(&k->wk_macaddr[2]));
if (error != 0)
goto fail;
error = rtwn_cam_write(sc, R92C_CAM_CTL0(k->wk_keyix),
SM(R92C_CAM_ALGO, algo) |
SM(R92C_CAM_KEYID, keyid) |
SM(R92C_CAM_MACLO, le16dec(&k->wk_macaddr[0])) |
R92C_CAM_VALID);
if (error != 0)
goto fail;
return (0);
fail:
device_printf(sc->sc_dev, "%s fails, error %d\n", __func__, error);
return (error);
}
static void
rtwn_key_set_cb(struct rtwn_softc *sc, union sec_param *data)
{
const struct ieee80211_key *k = &data->key;
(void) rtwn_key_set_cb0(sc, k);
}
int
rtwn_init_static_keys(struct rtwn_softc *sc, struct rtwn_vap *rvp)
{
int i, error;
if (sc->sc_hwcrypto != RTWN_CRYPTO_FULL)
return (0);
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
const struct ieee80211_key *k = rvp->keys[i];
if (k != NULL) {
error = rtwn_key_set_cb0(sc, k);
if (error != 0)
return (error);
}
}
return (0);
}
static void
rtwn_key_del_cb(struct rtwn_softc *sc, union sec_param *data)
{
struct ieee80211_key *k = &data->key;
int i;
RTWN_DPRINTF(sc, RTWN_DEBUG_KEY,
"%s: keyix %u, flags %04X, macaddr %s\n", __func__,
k->wk_keyix, k->wk_flags, ether_sprintf(k->wk_macaddr));
rtwn_cam_write(sc, R92C_CAM_CTL0(k->wk_keyix), 0);
rtwn_cam_write(sc, R92C_CAM_CTL1(k->wk_keyix), 0);
for (i = 0; i < 4; i++)
rtwn_cam_write(sc, R92C_CAM_KEY(k->wk_keyix, i), 0);
clrbit(sc->keys_bmap, k->wk_keyix);
}
static int
rtwn_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k,
int set)
{
struct rtwn_softc *sc = vap->iv_ic->ic_softc;
if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
return (1);
}
if (ieee80211_is_key_global(vap, k)) {
if (sc->sc_hwcrypto == RTWN_CRYPTO_FULL) {
struct rtwn_vap *rvp = RTWN_VAP(vap);
RTWN_LOCK(sc);
rvp->keys[k->wk_keyix] = (set ? k : NULL);
if ((sc->sc_flags & RTWN_RUNNING) == 0) {
if (!set)
clrbit(sc->keys_bmap, k->wk_keyix);
RTWN_UNLOCK(sc);
return (1);
}
RTWN_UNLOCK(sc);
}
}
return (!rtwn_cmd_sleepable(sc, k, sizeof(*k),
set ? rtwn_key_set_cb : rtwn_key_del_cb));
}
int
rtwn_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
return (rtwn_process_key(vap, k, 1));
}
int
rtwn_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
{
return (rtwn_process_key(vap, k, 0));
}