Changeset View
Changeset View
Standalone View
Standalone View
sys/net80211/ieee80211_ioctl.c
Show First 20 Lines • Show All 370 Lines • ▼ Show 20 Lines | if (ni->ni_vap->iv_opmode == IEEE80211_M_HOSTAP && | ||||
ni->ni_associd == 0) /* only associated stations */ | ni->ni_associd == 0) /* only associated stations */ | ||||
return; | return; | ||||
req->space += sta_space(ni, &ielen); | req->space += sta_space(ni, &ielen); | ||||
} | } | ||||
static void | static void | ||||
get_sta_info(void *arg, struct ieee80211_node *ni) | get_sta_info(void *arg, struct ieee80211_node *ni) | ||||
{ | { | ||||
const struct ieee80211_rate_t *rate; | |||||
const struct ieee80211_rateset *rs = &ni->ni_rates; | |||||
const struct ieee80211_channel *chan = ni->ni_chan; | |||||
struct stainforeq *req = arg; | struct stainforeq *req = arg; | ||||
struct ieee80211vap *vap = ni->ni_vap; | struct ieee80211vap *vap = ni->ni_vap; | ||||
struct ieee80211req_sta_info *si; | struct ieee80211req_sta_info *si; | ||||
size_t ielen, len; | size_t ielen, len; | ||||
uint8_t *cp; | uint8_t *cp; | ||||
uint8_t value; | |||||
int i, shortgi; | |||||
if (vap->iv_opmode == IEEE80211_M_HOSTAP && | if (vap->iv_opmode == IEEE80211_M_HOSTAP && | ||||
ni->ni_associd == 0) /* only associated stations */ | ni->ni_associd == 0) /* only associated stations */ | ||||
return; | return; | ||||
if (ni->ni_chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */ | if (chan == IEEE80211_CHAN_ANYC) /* XXX bogus entry */ | ||||
return; | return; | ||||
len = sta_space(ni, &ielen); | len = sta_space(ni, &ielen); | ||||
if (len > req->space) | if (len > req->space) | ||||
return; | return; | ||||
si = req->si; | si = req->si; | ||||
si->isi_len = len; | si->isi_len = len; | ||||
si->isi_ie_off = sizeof(struct ieee80211req_sta_info); | si->isi_ie_off = sizeof(struct ieee80211req_sta_info); | ||||
si->isi_ie_len = ielen; | si->isi_ie_len = ielen; | ||||
si->isi_freq = ni->ni_chan->ic_freq; | si->isi_freq = chan->ic_freq; | ||||
si->isi_flags = ni->ni_chan->ic_flags; | si->isi_flags = chan->ic_flags; | ||||
si->isi_state = ni->ni_flags; | si->isi_state = ni->ni_flags; | ||||
si->isi_authmode = ni->ni_authmode; | si->isi_authmode = ni->ni_authmode; | ||||
vap->iv_ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise); | vap->iv_ic->ic_node_getsignal(ni, &si->isi_rssi, &si->isi_noise); | ||||
vap->iv_ic->ic_node_getmimoinfo(ni, &si->isi_mimo); | vap->iv_ic->ic_node_getmimoinfo(ni, &si->isi_mimo); | ||||
si->isi_capinfo = ni->ni_capinfo; | si->isi_capinfo = ni->ni_capinfo; | ||||
si->isi_erp = ni->ni_erp; | si->isi_erp = ni->ni_erp; | ||||
IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); | IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr); | ||||
si->isi_nrates = ni->ni_rates.rs_nrates; | si->isi_nrates = MIN(rs->rs_nrates, nitems(si->isi_rates)); | ||||
if (si->isi_nrates > 15) | for (i = 0; i < si->isi_nrates; i++) { | ||||
si->isi_nrates = 15; | rate = ieee80211_get_rate(rs->rates[i].rs_index); | ||||
memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates); | |||||
si->isi_txrate = ni->ni_txrate; | value = rate->value; | ||||
if (si->isi_txrate & IEEE80211_RATE_MCS) { | if (rs->rates[i].rs_basic) | ||||
const struct ieee80211_mcs_rates *mcs = | value |= IEEE80211_RATE_BASIC; | ||||
&ieee80211_htrates[ni->ni_txrate &~ IEEE80211_RATE_MCS]; | |||||
if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { | /* backward compatibility */ | ||||
if (ni->ni_flags & IEEE80211_NODE_SGI40) | si->isi_rates[i] = value; | ||||
si->isi_txmbps = mcs->ht40_rate_800ns; | si->isi_rates_ind[i] = rs->rates[i].rs_index; | ||||
else | |||||
si->isi_txmbps = mcs->ht40_rate_400ns; | |||||
} else { | |||||
if (ni->ni_flags & IEEE80211_NODE_SGI20) | |||||
si->isi_txmbps = mcs->ht20_rate_800ns; | |||||
else | |||||
si->isi_txmbps = mcs->ht20_rate_400ns; | |||||
} | } | ||||
} else | si->isi_txrate = ieee80211_convert_to_legacy_rate(ni->ni_txrate); | ||||
si->isi_txmbps = si->isi_txrate; | si->isi_txrate_ind = ni->ni_txrate; | ||||
shortgi = 0; | |||||
rate = ieee80211_get_rate(ni->ni_txrate); | |||||
switch (rate->type) { | |||||
case IEEE80211_T_VHT: | |||||
/* TODO: VHT80 / VHT160 */ | |||||
if ((IEEE80211_IS_CHAN_VHT40(chan) && | |||||
(ni->ni_flags & IEEE80211_NODE_SGI40) != 0) || | |||||
(IEEE80211_IS_CHAN_VHT20(chan) && | |||||
(ni->ni_flags & IEEE80211_NODE_SGI20) != 0)) | |||||
shortgi = 1; | |||||
break; | |||||
case IEEE80211_T_HT: | |||||
if ((IEEE80211_IS_CHAN_HT40(chan) && | |||||
(ni->ni_flags & IEEE80211_NODE_SGI40) != 0) || | |||||
(IEEE80211_IS_CHAN_HT20(chan) && | |||||
(ni->ni_flags & IEEE80211_NODE_SGI20) != 0)) | |||||
shortgi = 1; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
si->isi_txmbps = ieee80211_get_rateKbps(rate, chan->ic_flags, shortgi); | |||||
si->isi_txmbps /= 500; | |||||
si->isi_associd = ni->ni_associd; | si->isi_associd = ni->ni_associd; | ||||
si->isi_txpower = ni->ni_txpower; | si->isi_txpower = ni->ni_txpower; | ||||
si->isi_vlan = ni->ni_vlan; | si->isi_vlan = ni->ni_vlan; | ||||
if (ni->ni_flags & IEEE80211_NODE_QOS) { | if (ni->ni_flags & IEEE80211_NODE_QOS) { | ||||
memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); | memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs)); | ||||
memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); | memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs)); | ||||
} else { | } else { | ||||
si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID]; | si->isi_txseqs[0] = ni->ni_txseqs[IEEE80211_NONQOS_TID]; | ||||
▲ Show 20 Lines • Show All 230 Lines • ▼ Show 20 Lines | ieee80211_ioctl_getregdomain(struct ieee80211vap *vap, | ||||
return copyout(&ic->ic_regdomain, ireq->i_data, | return copyout(&ic->ic_regdomain, ireq->i_data, | ||||
sizeof(ic->ic_regdomain)); | sizeof(ic->ic_regdomain)); | ||||
} | } | ||||
static int | static int | ||||
ieee80211_ioctl_getroam(struct ieee80211vap *vap, | ieee80211_ioctl_getroam(struct ieee80211vap *vap, | ||||
const struct ieee80211req *ireq) | const struct ieee80211req *ireq) | ||||
{ | { | ||||
struct ieee80211_roamparams_req *req; | |||||
struct ieee80211_roamparam_vht *req_in; | |||||
struct ieee80211_roamparam *req_out; | |||||
size_t len = ireq->i_len; | size_t len = ireq->i_len; | ||||
int mode, error; | |||||
req = IEEE80211_MALLOC(sizeof(*req), M_TEMP, | |||||
IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); | |||||
if (req == NULL) | |||||
return (ENOMEM); | |||||
for (mode = 0; mode < nitems(req->params); mode++) { | |||||
req_out = &req->params[mode]; | |||||
req_in = &vap->iv_roamparms[mode]; | |||||
if (req_in->rate != IEEE80211_RATE_NONEXISTENT) { | |||||
req_out->rssi = req_in->rssi; | |||||
req_out->rate = | |||||
ieee80211_convert_to_legacy_rate(req_in->rate); | |||||
} | |||||
} | |||||
/* NB: accept short requests for backwards compat */ | /* NB: accept short requests for backwards compat */ | ||||
if (len > sizeof(*req)) | |||||
len = sizeof(*req); | |||||
error = copyout(req, ireq->i_data, len); | |||||
IEEE80211_FREE(req, M_TEMP); | |||||
return (error); | |||||
} | |||||
static int | |||||
ieee80211_ioctl_getroam_vht(struct ieee80211vap *vap, | |||||
const struct ieee80211req *ireq) | |||||
{ | |||||
size_t len = ireq->i_len; | |||||
/* NB: accept short requests for backwards compat */ | |||||
if (len > sizeof(vap->iv_roamparms)) | if (len > sizeof(vap->iv_roamparms)) | ||||
len = sizeof(vap->iv_roamparms); | len = sizeof(vap->iv_roamparms); | ||||
return copyout(vap->iv_roamparms, ireq->i_data, len); | return (copyout(vap->iv_roamparms, ireq->i_data, len)); | ||||
} | } | ||||
static int | static int | ||||
ieee80211_ioctl_gettxparams(struct ieee80211vap *vap, | ieee80211_ioctl_gettxparams(struct ieee80211vap *vap, | ||||
const struct ieee80211req *ireq) | const struct ieee80211req *ireq) | ||||
{ | { | ||||
#define TO_LEGACY(_r, _def_value) \ | |||||
((_r) != IEEE80211_RATE_NONEXISTENT) ? \ | |||||
ieee80211_convert_to_legacy_rate((_r)) : (_def_value) | |||||
struct ieee80211_txparams_req *req; | |||||
struct ieee80211_txparam_vht *req_in; | |||||
struct ieee80211_txparam *req_out; | |||||
int mode, error; | |||||
/* NB: no need to zero out memory, all fields are initialized here */ | |||||
req = IEEE80211_MALLOC(sizeof(*req), M_TEMP, IEEE80211_M_NOWAIT); | |||||
if (req == NULL) | |||||
return (ENOMEM); | |||||
for (mode = 0; mode < nitems(req->params); mode++) { | |||||
req_out = &req->params[mode]; | |||||
req_in = &vap->iv_txparms[mode]; | |||||
req_out->ucastrate = TO_LEGACY(req_in->ucastrate, | |||||
IEEE80211_FIXED_RATE_NONE); | |||||
req_out->mgmtrate = TO_LEGACY(req_in->mgmtrate, 0); | |||||
req_out->mcastrate = TO_LEGACY(req_in->mcastrate, 0); | |||||
req_out->maxretry = req_in->maxretry; | |||||
} | |||||
size_t len = ireq->i_len; | size_t len = ireq->i_len; | ||||
/* NB: accept short requests for backwards compat */ | /* NB: accept short requests for backwards compat */ | ||||
if (len > sizeof(*req)) | |||||
len = sizeof(*req); | |||||
error = copyout(req, ireq->i_data, len); | |||||
IEEE80211_FREE(req, M_TEMP); | |||||
return (error); | |||||
#undef TO_LEGACY | |||||
} | |||||
static int | |||||
ieee80211_ioctl_gettxparams_vht(struct ieee80211vap *vap, | |||||
const struct ieee80211req *ireq) | |||||
{ | |||||
size_t len = ireq->i_len; | |||||
/* NB: accept short requests for backwards compat */ | |||||
if (len > sizeof(vap->iv_txparms)) | if (len > sizeof(vap->iv_txparms)) | ||||
len = sizeof(vap->iv_txparms); | len = sizeof(vap->iv_txparms); | ||||
return copyout(vap->iv_txparms, ireq->i_data, len); | return (copyout(vap->iv_txparms, ireq->i_data, len)); | ||||
} | } | ||||
static int | static int | ||||
ieee80211_ioctl_getdevcaps(struct ieee80211com *ic, | ieee80211_ioctl_getdevcaps(struct ieee80211com *ic, | ||||
const struct ieee80211req *ireq) | const struct ieee80211req *ireq) | ||||
{ | { | ||||
struct ieee80211_devcaps_req *dc; | struct ieee80211_devcaps_req *dc; | ||||
struct ieee80211req_chaninfo *ci; | struct ieee80211req_chaninfo *ci; | ||||
▲ Show 20 Lines • Show All 361 Lines • ▼ Show 20 Lines | case IEEE80211_IOC_PUREN: | ||||
ireq->i_val = (vap->iv_flags_ht & IEEE80211_FHT_PUREN) != 0; | ireq->i_val = (vap->iv_flags_ht & IEEE80211_FHT_PUREN) != 0; | ||||
break; | break; | ||||
case IEEE80211_IOC_DOTH: | case IEEE80211_IOC_DOTH: | ||||
ireq->i_val = (vap->iv_flags & IEEE80211_F_DOTH) != 0; | ireq->i_val = (vap->iv_flags & IEEE80211_F_DOTH) != 0; | ||||
break; | break; | ||||
case IEEE80211_IOC_REGDOMAIN: | case IEEE80211_IOC_REGDOMAIN: | ||||
error = ieee80211_ioctl_getregdomain(vap, ireq); | error = ieee80211_ioctl_getregdomain(vap, ireq); | ||||
break; | break; | ||||
case IEEE80211_IOC_ROAM: | case IEEE80211_IOC_ROAM: /* legacy, use ROAM_VHT instead */ | ||||
error = ieee80211_ioctl_getroam(vap, ireq); | error = ieee80211_ioctl_getroam(vap, ireq); | ||||
break; | break; | ||||
case IEEE80211_IOC_TXPARAMS: | case IEEE80211_IOC_ROAM_VHT: | ||||
error = ieee80211_ioctl_getroam_vht(vap, ireq); | |||||
break; | |||||
case IEEE80211_IOC_TXPARAMS: /* legacy, use TXPARAMS_VHT instead */ | |||||
error = ieee80211_ioctl_gettxparams(vap, ireq); | error = ieee80211_ioctl_gettxparams(vap, ireq); | ||||
break; | break; | ||||
case IEEE80211_IOC_TXPARAMS_VHT: | |||||
error = ieee80211_ioctl_gettxparams_vht(vap, ireq); | |||||
break; | |||||
case IEEE80211_IOC_HTCOMPAT: | case IEEE80211_IOC_HTCOMPAT: | ||||
ireq->i_val = (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) != 0; | ireq->i_val = (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) != 0; | ||||
break; | break; | ||||
case IEEE80211_IOC_DWDS: | case IEEE80211_IOC_DWDS: | ||||
ireq->i_val = (vap->iv_flags & IEEE80211_F_DWDS) != 0; | ireq->i_val = (vap->iv_flags & IEEE80211_F_DWDS) != 0; | ||||
break; | break; | ||||
case IEEE80211_IOC_INACTIVITY: | case IEEE80211_IOC_INACTIVITY: | ||||
ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_INACT) != 0; | ireq->i_val = (vap->iv_flags_ext & IEEE80211_FEXT_INACT) != 0; | ||||
▲ Show 20 Lines • Show All 901 Lines • ▼ Show 20 Lines | |||||
* Common code to set the current channel. If the device | * Common code to set the current channel. If the device | ||||
* is up and running this may result in an immediate channel | * is up and running this may result in an immediate channel | ||||
* change or a kick of the state machine. | * change or a kick of the state machine. | ||||
*/ | */ | ||||
static int | static int | ||||
setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c) | setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c) | ||||
{ | { | ||||
struct ieee80211com *ic = vap->iv_ic; | struct ieee80211com *ic = vap->iv_ic; | ||||
enum ieee80211_phymode mode; | |||||
int error; | int error; | ||||
if (c != IEEE80211_CHAN_ANYC) { | if (c != IEEE80211_CHAN_ANYC) { | ||||
if (IEEE80211_IS_CHAN_RADAR(c)) | if (IEEE80211_IS_CHAN_RADAR(c)) | ||||
return EBUSY; /* XXX better code? */ | return EBUSY; /* XXX better code? */ | ||||
if (vap->iv_opmode == IEEE80211_M_HOSTAP) { | if (vap->iv_opmode == IEEE80211_M_HOSTAP) { | ||||
if (IEEE80211_IS_CHAN_NOHOSTAP(c)) | if (IEEE80211_IS_CHAN_NOHOSTAP(c)) | ||||
return EINVAL; | return EINVAL; | ||||
if (!check_mode_consistency(c, vap->iv_des_mode)) | if (!check_mode_consistency(c, vap->iv_des_mode)) | ||||
return EINVAL; | return EINVAL; | ||||
} else if (vap->iv_opmode == IEEE80211_M_IBSS) { | } else if (vap->iv_opmode == IEEE80211_M_IBSS) { | ||||
if (IEEE80211_IS_CHAN_NOADHOC(c)) | if (IEEE80211_IS_CHAN_NOADHOC(c)) | ||||
return EINVAL; | return EINVAL; | ||||
} | } | ||||
if ((vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) && | if ((vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) && | ||||
vap->iv_bss->ni_chan == c) | vap->iv_bss->ni_chan == c) | ||||
return 0; /* NB: nothing to do */ | return 0; /* NB: nothing to do */ | ||||
} | } | ||||
vap->iv_des_chan = c; | vap->iv_des_chan = c; | ||||
error = 0; | error = 0; | ||||
mode = ieee80211_chan2mode(ic->ic_curchan); | |||||
if (vap->iv_opmode == IEEE80211_M_MONITOR && | if (vap->iv_opmode == IEEE80211_M_MONITOR && | ||||
vap->iv_des_chan != IEEE80211_CHAN_ANYC) { | vap->iv_des_chan != IEEE80211_CHAN_ANYC) { | ||||
/* | /* | ||||
* Monitor mode can switch directly. | * Monitor mode can switch directly. | ||||
*/ | */ | ||||
if (IFNET_IS_UP_RUNNING(vap->iv_ifp)) { | if (IFNET_IS_UP_RUNNING(vap->iv_ifp)) { | ||||
/* XXX need state machine for other vap's to follow */ | /* XXX need state machine for other vap's to follow */ | ||||
ieee80211_setcurchan(ic, vap->iv_des_chan); | ieee80211_setcurchan(ic, vap->iv_des_chan); | ||||
vap->iv_bss->ni_chan = ic->ic_curchan; | vap->iv_bss->ni_chan = ic->ic_curchan; | ||||
} else { | } else { | ||||
ic->ic_curchan = vap->iv_des_chan; | ic->ic_curchan = vap->iv_des_chan; | ||||
ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); | ic->ic_rt = ieee80211_get_ratetable(mode); | ||||
} | } | ||||
} else { | } else { | ||||
/* | /* | ||||
* Need to go through the state machine in case we | * Need to go through the state machine in case we | ||||
* need to reassociate or the like. The state machine | * need to reassociate or the like. The state machine | ||||
* will pickup the desired channel and avoid scanning. | * will pickup the desired channel and avoid scanning. | ||||
*/ | */ | ||||
if (IS_UP_AUTO(vap)) | if (IS_UP_AUTO(vap)) | ||||
ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); | ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); | ||||
else if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { | else if (vap->iv_des_chan != IEEE80211_CHAN_ANYC) { | ||||
/* | /* | ||||
* When not up+running and a real channel has | * When not up+running and a real channel has | ||||
* been specified fix the current channel so | * been specified fix the current channel so | ||||
* there is immediate feedback; e.g. via ifconfig. | * there is immediate feedback; e.g. via ifconfig. | ||||
*/ | */ | ||||
ic->ic_curchan = vap->iv_des_chan; | ic->ic_curchan = vap->iv_des_chan; | ||||
ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); | ic->ic_rt = ieee80211_get_ratetable(mode); | ||||
} | } | ||||
} | } | ||||
return error; | return error; | ||||
} | } | ||||
/* | /* | ||||
* Old api for setting the current channel; this is | * Old api for setting the current channel; this is | ||||
* deprecated because channel numbers are ambiguous. | * deprecated because channel numbers are ambiguous. | ||||
▲ Show 20 Lines • Show All 155 Lines • ▼ Show 20 Lines | ieee80211_ioctl_setregdomain(struct ieee80211vap *vap, | ||||
return (error == 0 ? ENETRESET : error); | return (error == 0 ? ENETRESET : error); | ||||
} | } | ||||
static int | static int | ||||
ieee80211_ioctl_setroam(struct ieee80211vap *vap, | ieee80211_ioctl_setroam(struct ieee80211vap *vap, | ||||
const struct ieee80211req *ireq) | const struct ieee80211req *ireq) | ||||
{ | { | ||||
#define FROM_LEGACY(r, mode) ieee80211_convert_from_legacy_rate((r), (mode)) | |||||
struct ieee80211com *ic = vap->iv_ic; | |||||
struct ieee80211_roamparams_req_vht *req; | |||||
struct ieee80211_roamparams_req *req_in; | |||||
struct ieee80211_roamparam_vht *params_vht; | |||||
struct ieee80211_roamparam *params; | |||||
int m, error; | |||||
if (ireq->i_len != sizeof(*req_in)) | |||||
return (EINVAL); | |||||
req_in = IEEE80211_MALLOC(sizeof(*req_in), M_TEMP, IEEE80211_M_NOWAIT); | |||||
if (req_in == NULL) | |||||
return (ENOMEM); | |||||
req = IEEE80211_MALLOC(sizeof(*req), M_TEMP, IEEE80211_M_NOWAIT); | |||||
if (req == NULL) { | |||||
IEEE80211_FREE(req_in, M_TEMP); | |||||
return (ENOMEM); | |||||
} | |||||
error = copyin(ireq->i_data, req_in, sizeof(*req_in)); | |||||
if (error != 0) | |||||
goto end; | |||||
/* Convert to new format (and validate). */ | |||||
for (m = IEEE80211_MODE_AUTO + 1; m < nitems(req_in->params); m++) { | |||||
if (isclr(ic->ic_modecaps, m)) | |||||
continue; | |||||
params_vht = &req->params[m]; | |||||
params = &req_in->params[m]; | |||||
params_vht->rssi = params->rssi; | |||||
params_vht->rate = FROM_LEGACY(params->rate, m); | |||||
if (params_vht->rate == IEEE80211_RATE_NONEXISTENT) { | |||||
error = EINVAL; | |||||
goto end; | |||||
} | |||||
} | |||||
for (m = IEEE80211_MODE_AUTO + 1; m < nitems(req->params); m++) { | |||||
if (isclr(ic->ic_modecaps, m)) | |||||
continue; | |||||
vap->iv_roamparms[m].rssi = req->params[m].rssi; | |||||
vap->iv_roamparms[m].rate = req->params[m].rate; | |||||
} | |||||
end: | |||||
IEEE80211_FREE(req, M_TEMP); | |||||
IEEE80211_FREE(req_in, M_TEMP); | |||||
/* XXX? ENETRESET to push to device? */ | |||||
return (error); | |||||
#undef FROM_LEGACY | |||||
} | |||||
static int | |||||
ieee80211_ioctl_setroam_vht(struct ieee80211vap *vap, | |||||
const struct ieee80211req *ireq) | |||||
{ | |||||
const struct ieee80211_rate_table *rt; | |||||
const struct ieee80211_rate_t *rate; | |||||
struct ieee80211com *ic = vap->iv_ic; | |||||
struct ieee80211_roamparams_req_vht *req; | |||||
struct ieee80211_roamparam_vht *params; | |||||
int m, error; | |||||
if (ireq->i_len != sizeof(vap->iv_roamparms)) | if (ireq->i_len != sizeof(vap->iv_roamparms)) | ||||
return EINVAL; | return (EINVAL); | ||||
/* XXX validate params */ | |||||
req = IEEE80211_MALLOC(sizeof(*req), M_TEMP, IEEE80211_M_NOWAIT); | |||||
if (req == NULL) | |||||
return (ENOMEM); | |||||
error = copyin(ireq->i_data, req, sizeof(*req)); | |||||
if (error != 0) | |||||
goto end; | |||||
/* Validate parameters. */ | |||||
for (m = IEEE80211_MODE_AUTO + 1; m < nitems(req->params); m++) { | |||||
if (isclr(ic->ic_modecaps, m)) | |||||
continue; | |||||
rt = ieee80211_get_ratetable(m); | |||||
params = &req->params[m]; | |||||
rate = ieee80211_get_rate_safe(params->rate); | |||||
if (!rate || !ieee80211_isratevalid(rt, rate)) { | |||||
error = EINVAL; | |||||
goto end; | |||||
} | |||||
} | |||||
for (m = IEEE80211_MODE_AUTO + 1; m < nitems(req->params); m++) { | |||||
if (isclr(ic->ic_modecaps, m)) | |||||
continue; | |||||
vap->iv_roamparms[m].rssi = req->params[m].rssi; | |||||
vap->iv_roamparms[m].rate = req->params[m].rate; | |||||
} | |||||
end: | |||||
IEEE80211_FREE(req, M_TEMP); | |||||
/* XXX? ENETRESET to push to device? */ | /* XXX? ENETRESET to push to device? */ | ||||
return copyin(ireq->i_data, vap->iv_roamparms, | return (error); | ||||
sizeof(vap->iv_roamparms)); | |||||
} | } | ||||
static int | static int | ||||
checkrate(const struct ieee80211_rateset *rs, int rate) | rate_to_index(uint8_t rate, uint16_t *rate_index, enum ieee80211_phymode mode) | ||||
{ | { | ||||
int i; | if (rate != IEEE80211_FIXED_RATE_NONE) { | ||||
*rate_index = ieee80211_convert_from_legacy_rate(rate, mode); | |||||
if (*rate_index == IEEE80211_RATE_NONEXISTENT) | |||||
return (-1); | |||||
} else | |||||
*rate_index = IEEE80211_RATE_NONEXISTENT; | |||||
if (rate == IEEE80211_FIXED_RATE_NONE) | return (0); | ||||
return 1; | |||||
for (i = 0; i < rs->rs_nrates; i++) | |||||
if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rate) | |||||
return 1; | |||||
return 0; | |||||
} | } | ||||
static int | static int | ||||
checkmcs(const struct ieee80211_htrateset *rs, int mcs) | checkrate(const struct ieee80211_rateset *rs, | ||||
const struct ieee80211_htrateset *rs_ht, | |||||
const struct ieee80211_htrateset *rs_vht, | |||||
uint16_t new_rate, uint16_t curr_rate, | |||||
enum ieee80211_phymode mode) | |||||
{ | { | ||||
int rate_val = IEEE80211_RV(mcs); | const struct ieee80211_rate_t *rate; | ||||
int i; | int is11n, is_vht, rv; | ||||
if (mcs == IEEE80211_FIXED_RATE_NONE) | is11n = (mode == IEEE80211_MODE_11NA || mode == IEEE80211_MODE_11NG); | ||||
return 1; | is_vht = (mode == IEEE80211_MODE_VHT_2GHZ || | ||||
if ((mcs & IEEE80211_RATE_MCS) == 0) /* MCS always have 0x80 set */ | mode == IEEE80211_MODE_VHT_5GHZ); | ||||
return 0; | |||||
for (i = 0; i < rs->rs_nrates; i++) | if (new_rate != curr_rate) { | ||||
if (IEEE80211_RV(rs->rs_rates[i]) == rate_val) | if (new_rate == IEEE80211_RATE_NONEXISTENT) | ||||
return 1; | return (1); | ||||
return 0; | |||||
rate = ieee80211_get_rate_safe(new_rate); | |||||
if (!rate) | |||||
return (-1); | |||||
rv = rate->value; | |||||
if (rate->type == IEEE80211_T_VHT) | |||||
rv *= rate->props.ht.streams; | |||||
switch (rate->type) { | |||||
case IEEE80211_T_VHT: | |||||
if (!is_vht || isclr(rs_vht->rs_bitmap, rv)) | |||||
return (-1); | |||||
return (1); | |||||
case IEEE80211_T_HT: | |||||
if (!is11n || isclr(rs_ht->rs_bitmap, rv)) | |||||
return (-1); | |||||
return (1); | |||||
default: | |||||
if (isclr(rs->rs_bitmap, rv)) | |||||
return (-1); | |||||
return (1); | |||||
} | } | ||||
} | |||||
return (0); | |||||
} | |||||
static int | static int | ||||
ieee80211_ioctl_settxparams(struct ieee80211vap *vap, | ieee80211_validate_txparams(struct ieee80211vap *vap, | ||||
const struct ieee80211req *ireq) | const struct ieee80211_txparams_vht_req *parms, int nmodes) | ||||
{ | { | ||||
#define CHECK_RATE(_rs, _rs_ht, _rs_vht, _new, _old, _mode, _counter) do { \ | |||||
int _ret = checkrate((_rs), (_rs_ht), (_rs_vht), (_new), (_old), \ | |||||
(_mode)); \ | |||||
if (_ret < 0) \ | |||||
return (EINVAL); \ | |||||
_counter += _ret; \ | |||||
} while (0) | |||||
struct ieee80211com *ic = vap->iv_ic; | struct ieee80211com *ic = vap->iv_ic; | ||||
struct ieee80211_txparams_req parms; /* XXX stack use? */ | const struct ieee80211_htrateset *rs_vht = &ic->ic_sup_vhtrates; | ||||
struct ieee80211_txparam *src, *dst; | const struct ieee80211_htrateset *rs_ht = &ic->ic_sup_htrates; | ||||
const struct ieee80211_htrateset *rs_ht; | |||||
const struct ieee80211_rateset *rs; | const struct ieee80211_rateset *rs; | ||||
int error, mode, changed, is11n, nmodes; | const struct ieee80211_txparam_vht *src; | ||||
struct ieee80211_txparam_vht *dst; | |||||
enum ieee80211_phymode mode; | |||||
int changed; | |||||
/* NB: accept short requests for backwards compat */ | |||||
if (ireq->i_len > sizeof(parms)) | |||||
return EINVAL; | |||||
error = copyin(ireq->i_data, &parms, ireq->i_len); | |||||
if (error != 0) | |||||
return error; | |||||
nmodes = ireq->i_len / sizeof(struct ieee80211_txparam); | |||||
changed = 0; | |||||
/* validate parameters and check if anything changed */ | /* validate parameters and check if anything changed */ | ||||
changed = 0; | |||||
for (mode = IEEE80211_MODE_11A; mode < nmodes; mode++) { | for (mode = IEEE80211_MODE_11A; mode < nmodes; mode++) { | ||||
if (isclr(ic->ic_modecaps, mode)) | if (isclr(ic->ic_modecaps, mode)) | ||||
continue; | continue; | ||||
src = &parms.params[mode]; | |||||
src = &parms->params[mode]; | |||||
dst = &vap->iv_txparms[mode]; | dst = &vap->iv_txparms[mode]; | ||||
rs = &ic->ic_sup_rates[mode]; /* NB: 11n maps to legacy */ | rs = ic->ic_sup_rates[mode]; | ||||
rs_ht = &ic->ic_sup_htrates; | |||||
is11n = (mode == IEEE80211_MODE_11NA || | CHECK_RATE(rs, rs_ht, rs_vht, src->ucastrate, dst->ucastrate, | ||||
mode == IEEE80211_MODE_11NG); | mode, changed); | ||||
if (src->ucastrate != dst->ucastrate) { | CHECK_RATE(rs, rs_ht, rs_vht, src->mcastrate, dst->mcastrate, | ||||
if (!checkrate(rs, src->ucastrate) && | mode, changed); | ||||
(!is11n || !checkmcs(rs_ht, src->ucastrate))) | CHECK_RATE(rs, rs_ht, rs_vht, src->mgmtrate, dst->mgmtrate, | ||||
return EINVAL; | mode, changed); | ||||
changed++; | |||||
} | |||||
if (src->mcastrate != dst->mcastrate) { | |||||
if (!checkrate(rs, src->mcastrate) && | |||||
(!is11n || !checkmcs(rs_ht, src->mcastrate))) | |||||
return EINVAL; | |||||
changed++; | |||||
} | |||||
if (src->mgmtrate != dst->mgmtrate) { | |||||
if (!checkrate(rs, src->mgmtrate) && | |||||
(!is11n || !checkmcs(rs_ht, src->mgmtrate))) | |||||
return EINVAL; | |||||
changed++; | |||||
} | |||||
if (src->maxretry != dst->maxretry) /* NB: no bounds */ | if (src->maxretry != dst->maxretry) /* NB: no bounds */ | ||||
changed++; | changed++; | ||||
} | } | ||||
if (changed) { | if (changed) { | ||||
/* | /* | ||||
* Copy new parameters in place and notify the | * Copy new parameters in place and notify the | ||||
* driver so it can push state to the device. | * driver so it can push state to the device. | ||||
*/ | */ | ||||
for (mode = IEEE80211_MODE_11A; mode < nmodes; mode++) { | for (mode = IEEE80211_MODE_11A; mode < nmodes; mode++) { | ||||
if (isset(ic->ic_modecaps, mode)) | if (isset(ic->ic_modecaps, mode)) | ||||
vap->iv_txparms[mode] = parms.params[mode]; | vap->iv_txparms[mode] = parms->params[mode]; | ||||
} | } | ||||
/* XXX could be more intelligent, | /* | ||||
e.g. don't reset if setting not being used */ | * XXX could be more intelligent, | ||||
return ENETRESET; | * e.g. don't reset if setting not being used | ||||
*/ | |||||
return (ENETRESET); | |||||
} | } | ||||
return 0; | return (0); | ||||
#undef CHECK_RATE | |||||
} | } | ||||
static int | |||||
ieee80211_ioctl_settxparams(struct ieee80211vap *vap, | |||||
const struct ieee80211req *ireq) | |||||
{ | |||||
#define TO_INDEX(_rate, _index, _mode) do { \ | |||||
if (rate_to_index((_rate), (_index), (_mode)) != 0) { \ | |||||
error = EINVAL; \ | |||||
goto end; \ | |||||
} \ | |||||
} while (0) | |||||
struct ieee80211com *ic = vap->iv_ic; | |||||
const struct ieee80211_txparam *src; | |||||
struct ieee80211_txparams_vht_req *parms; | |||||
struct ieee80211_txparams_req *input; | |||||
struct ieee80211_txparam_vht *dst; | |||||
enum ieee80211_phymode mode; | |||||
int error, nmodes; | |||||
/* NB: accept short requests for backwards compat */ | |||||
if (ireq->i_len > sizeof(*input)) | |||||
return (EINVAL); | |||||
parms = IEEE80211_MALLOC(sizeof(*parms), M_TEMP, IEEE80211_M_NOWAIT); | |||||
if (parms == NULL) | |||||
return (ENOMEM); | |||||
input = IEEE80211_MALLOC(sizeof(*input), M_TEMP, IEEE80211_M_NOWAIT); | |||||
if (input == NULL) { | |||||
IEEE80211_FREE(parms, M_TEMP); | |||||
return (ENOMEM); | |||||
} | |||||
error = copyin(ireq->i_data, input, ireq->i_len); | |||||
if (error != 0) | |||||
goto end; | |||||
nmodes = ireq->i_len / sizeof(struct ieee80211_txparam); | |||||
/* convert parameters to the new format */ | |||||
for (mode = IEEE80211_MODE_11A; mode < nmodes; mode++) { | |||||
if (isclr(ic->ic_modecaps, mode)) | |||||
continue; | |||||
src = &input->params[mode]; | |||||
dst = &parms->params[mode]; | |||||
TO_INDEX(src->ucastrate, &dst->ucastrate, mode); | |||||
TO_INDEX(src->mcastrate, &dst->mcastrate, mode); | |||||
TO_INDEX(src->mgmtrate, &dst->mgmtrate, mode); | |||||
dst->maxretry = src->maxretry; | |||||
} | |||||
error = ieee80211_validate_txparams(vap, parms, nmodes); | |||||
end: | |||||
IEEE80211_FREE(input, M_TEMP); | |||||
IEEE80211_FREE(parms, M_TEMP); | |||||
return (error); | |||||
} | |||||
static int | |||||
ieee80211_ioctl_settxparams_vht(struct ieee80211vap *vap, | |||||
const struct ieee80211req *ireq) | |||||
{ | |||||
struct ieee80211_txparams_vht_req *parms; | |||||
int error, nmodes; | |||||
/* NB: accept short requests for backwards compat */ | |||||
if (ireq->i_len > sizeof(*parms)) | |||||
return (EINVAL); | |||||
parms = IEEE80211_MALLOC(sizeof(*parms), M_TEMP, IEEE80211_M_NOWAIT); | |||||
if (parms == NULL) | |||||
return (ENOMEM); | |||||
error = copyin(ireq->i_data, parms, ireq->i_len); | |||||
if (error != 0) | |||||
goto end; | |||||
nmodes = ireq->i_len / sizeof(struct ieee80211_txparam_vht); | |||||
error = ieee80211_validate_txparams(vap, parms, nmodes); | |||||
end: | |||||
IEEE80211_FREE(parms, M_TEMP); | |||||
return (error); | |||||
} | |||||
/* | /* | ||||
* Application Information Element support. | * Application Information Element support. | ||||
*/ | */ | ||||
static int | static int | ||||
setappie(struct ieee80211_appie **aie, const struct ieee80211req *ireq) | setappie(struct ieee80211_appie **aie, const struct ieee80211req *ireq) | ||||
{ | { | ||||
struct ieee80211_appie *app = *aie; | struct ieee80211_appie *app = *aie; | ||||
struct ieee80211_appie *napp; | struct ieee80211_appie *napp; | ||||
▲ Show 20 Lines • Show All 921 Lines • ▼ Show 20 Lines | #endif | ||||
vap->iv_flags |= IEEE80211_F_DOTH; | vap->iv_flags |= IEEE80211_F_DOTH; | ||||
} else | } else | ||||
vap->iv_flags &= ~IEEE80211_F_DOTH; | vap->iv_flags &= ~IEEE80211_F_DOTH; | ||||
error = ENETRESET; | error = ENETRESET; | ||||
break; | break; | ||||
case IEEE80211_IOC_REGDOMAIN: | case IEEE80211_IOC_REGDOMAIN: | ||||
error = ieee80211_ioctl_setregdomain(vap, ireq); | error = ieee80211_ioctl_setregdomain(vap, ireq); | ||||
break; | break; | ||||
case IEEE80211_IOC_ROAM: | case IEEE80211_IOC_ROAM: /* legacy, use ROAM_VHT instead */ | ||||
error = ieee80211_ioctl_setroam(vap, ireq); | error = ieee80211_ioctl_setroam(vap, ireq); | ||||
break; | break; | ||||
case IEEE80211_IOC_TXPARAMS: | case IEEE80211_IOC_ROAM_VHT: | ||||
error = ieee80211_ioctl_setroam_vht(vap, ireq); | |||||
break; | |||||
case IEEE80211_IOC_TXPARAMS: /* legacy, use TXPARAMS_VHT instead */ | |||||
error = ieee80211_ioctl_settxparams(vap, ireq); | error = ieee80211_ioctl_settxparams(vap, ireq); | ||||
break; | |||||
case IEEE80211_IOC_TXPARAMS_VHT: | |||||
error = ieee80211_ioctl_settxparams_vht(vap, ireq); | |||||
break; | break; | ||||
case IEEE80211_IOC_HTCOMPAT: | case IEEE80211_IOC_HTCOMPAT: | ||||
if (ireq->i_val) { | if (ireq->i_val) { | ||||
if ((vap->iv_flags_ht & IEEE80211_FHT_HT) == 0) | if ((vap->iv_flags_ht & IEEE80211_FHT_HT) == 0) | ||||
return EOPNOTSUPP; | return EOPNOTSUPP; | ||||
vap->iv_flags_ht |= IEEE80211_FHT_HTCOMPAT; | vap->iv_flags_ht |= IEEE80211_FHT_HTCOMPAT; | ||||
} else | } else | ||||
vap->iv_flags_ht &= ~IEEE80211_FHT_HTCOMPAT; | vap->iv_flags_ht &= ~IEEE80211_FHT_HTCOMPAT; | ||||
▲ Show 20 Lines • Show All 357 Lines • Show Last 20 Lines |