in drivers/staging/rtl8192e/rtllib_rx.c [1752:2212]
int rtllib_parse_info_param(struct rtllib_device *ieee,
struct rtllib_info_element *info_element,
u16 length,
struct rtllib_network *network,
struct rtllib_rx_stats *stats)
{
u8 i;
short offset;
u16 tmp_htcap_len = 0;
u16 tmp_htinfo_len = 0;
u16 ht_realtek_agg_len = 0;
u8 ht_realtek_agg_buf[MAX_IE_LEN];
char rates_str[64];
char *p;
while (length >= sizeof(*info_element)) {
if (sizeof(*info_element) + info_element->len > length) {
RTLLIB_DEBUG_MGMT("Info elem: parse failed: "
"info_element->len + 2 > left : "
"info_element->len+2=%zd left=%d, id=%d.\n",
info_element->len +
sizeof(*info_element),
length, info_element->id);
/* We stop processing but don't return an error here
* because some misbehaviour APs break this rule. ie.
* Orinoco AP1000. */
break;
}
switch (info_element->id) {
case MFIE_TYPE_SSID:
if (rtllib_is_empty_essid(info_element->data,
info_element->len)) {
network->flags |= NETWORK_EMPTY_ESSID;
break;
}
network->ssid_len = min(info_element->len,
(u8) IW_ESSID_MAX_SIZE);
memcpy(network->ssid, info_element->data, network->ssid_len);
if (network->ssid_len < IW_ESSID_MAX_SIZE)
memset(network->ssid + network->ssid_len, 0,
IW_ESSID_MAX_SIZE - network->ssid_len);
RTLLIB_DEBUG_MGMT("MFIE_TYPE_SSID: '%s' len=%d.\n",
network->ssid, network->ssid_len);
break;
case MFIE_TYPE_RATES:
p = rates_str;
network->rates_len = min(info_element->len,
MAX_RATES_LENGTH);
for (i = 0; i < network->rates_len; i++) {
network->rates[i] = info_element->data[i];
p += snprintf(p, sizeof(rates_str) -
(p - rates_str), "%02X ",
network->rates[i]);
if (rtllib_is_ofdm_rate
(info_element->data[i])) {
network->flags |= NETWORK_HAS_OFDM;
if (info_element->data[i] &
RTLLIB_BASIC_RATE_MASK)
network->flags &=
~NETWORK_HAS_CCK;
}
if (rtllib_is_cck_rate
(info_element->data[i])) {
network->flags |= NETWORK_HAS_CCK;
}
}
RTLLIB_DEBUG_MGMT("MFIE_TYPE_RATES: '%s' (%d)\n",
rates_str, network->rates_len);
break;
case MFIE_TYPE_RATES_EX:
p = rates_str;
network->rates_ex_len = min(info_element->len,
MAX_RATES_EX_LENGTH);
for (i = 0; i < network->rates_ex_len; i++) {
network->rates_ex[i] = info_element->data[i];
p += snprintf(p, sizeof(rates_str) -
(p - rates_str), "%02X ",
network->rates_ex[i]);
if (rtllib_is_ofdm_rate
(info_element->data[i])) {
network->flags |= NETWORK_HAS_OFDM;
if (info_element->data[i] &
RTLLIB_BASIC_RATE_MASK)
network->flags &=
~NETWORK_HAS_CCK;
}
}
RTLLIB_DEBUG_MGMT("MFIE_TYPE_RATES_EX: '%s' (%d)\n",
rates_str, network->rates_ex_len);
break;
case MFIE_TYPE_DS_SET:
RTLLIB_DEBUG_MGMT("MFIE_TYPE_DS_SET: %d\n",
info_element->data[0]);
network->channel = info_element->data[0];
break;
case MFIE_TYPE_FH_SET:
RTLLIB_DEBUG_MGMT("MFIE_TYPE_FH_SET: ignored\n");
break;
case MFIE_TYPE_CF_SET:
RTLLIB_DEBUG_MGMT("MFIE_TYPE_CF_SET: ignored\n");
break;
case MFIE_TYPE_TIM:
if (info_element->len < 4)
break;
network->tim.tim_count = info_element->data[0];
network->tim.tim_period = info_element->data[1];
network->dtim_period = info_element->data[1];
if (ieee->state != RTLLIB_LINKED)
break;
network->last_dtim_sta_time = jiffies;
network->dtim_data = RTLLIB_DTIM_VALID;
if (info_element->data[2] & 1)
network->dtim_data |= RTLLIB_DTIM_MBCAST;
offset = (info_element->data[2] >> 1)*2;
if (ieee->assoc_id < 8*offset ||
ieee->assoc_id > 8*(offset + info_element->len - 3))
break;
offset = (ieee->assoc_id / 8) - offset;
if (info_element->data[3 + offset] &
(1 << (ieee->assoc_id % 8)))
network->dtim_data |= RTLLIB_DTIM_UCAST;
network->listen_interval = network->dtim_period;
break;
case MFIE_TYPE_ERP:
network->erp_value = info_element->data[0];
network->flags |= NETWORK_HAS_ERP_VALUE;
RTLLIB_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n",
network->erp_value);
break;
case MFIE_TYPE_IBSS_SET:
network->atim_window = info_element->data[0];
RTLLIB_DEBUG_MGMT("MFIE_TYPE_IBSS_SET: %d\n",
network->atim_window);
break;
case MFIE_TYPE_CHALLENGE:
RTLLIB_DEBUG_MGMT("MFIE_TYPE_CHALLENGE: ignored\n");
break;
case MFIE_TYPE_GENERIC:
RTLLIB_DEBUG_MGMT("MFIE_TYPE_GENERIC: %d bytes\n",
info_element->len);
if (!rtllib_parse_qos_info_param_IE(info_element,
network))
break;
if (info_element->len >= 4 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x50 &&
info_element->data[2] == 0xf2 &&
info_element->data[3] == 0x01) {
network->wpa_ie_len = min(info_element->len + 2,
MAX_WPA_IE_LEN);
memcpy(network->wpa_ie, info_element,
network->wpa_ie_len);
break;
}
if (info_element->len == 7 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0xe0 &&
info_element->data[2] == 0x4c &&
info_element->data[3] == 0x01 &&
info_element->data[4] == 0x02)
network->Turbo_Enable = 1;
if (tmp_htcap_len == 0) {
if (info_element->len >= 4 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x90 &&
info_element->data[2] == 0x4c &&
info_element->data[3] == 0x033) {
tmp_htcap_len = min(info_element->len, (u8)MAX_IE_LEN);
if (tmp_htcap_len != 0) {
network->bssht.bdHTSpecVer = HT_SPEC_VER_EWC;
network->bssht.bdHTCapLen = tmp_htcap_len > sizeof(network->bssht.bdHTCapBuf) ?
sizeof(network->bssht.bdHTCapBuf) : tmp_htcap_len;
memcpy(network->bssht.bdHTCapBuf, info_element->data, network->bssht.bdHTCapLen);
}
}
if (tmp_htcap_len != 0) {
network->bssht.bdSupportHT = true;
network->bssht.bdHT1R = ((((struct ht_capab_ele *)(network->bssht.bdHTCapBuf))->MCS[1]) == 0);
} else {
network->bssht.bdSupportHT = false;
network->bssht.bdHT1R = false;
}
}
if (tmp_htinfo_len == 0) {
if (info_element->len >= 4 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x90 &&
info_element->data[2] == 0x4c &&
info_element->data[3] == 0x034) {
tmp_htinfo_len = min(info_element->len, (u8)MAX_IE_LEN);
if (tmp_htinfo_len != 0) {
network->bssht.bdHTSpecVer = HT_SPEC_VER_EWC;
if (tmp_htinfo_len) {
network->bssht.bdHTInfoLen = tmp_htinfo_len > sizeof(network->bssht.bdHTInfoBuf) ?
sizeof(network->bssht.bdHTInfoBuf) : tmp_htinfo_len;
memcpy(network->bssht.bdHTInfoBuf, info_element->data, network->bssht.bdHTInfoLen);
}
}
}
}
if (ieee->aggregation) {
if (network->bssht.bdSupportHT) {
if (info_element->len >= 4 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0xe0 &&
info_element->data[2] == 0x4c &&
info_element->data[3] == 0x02) {
ht_realtek_agg_len = min(info_element->len, (u8)MAX_IE_LEN);
memcpy(ht_realtek_agg_buf, info_element->data, info_element->len);
}
if (ht_realtek_agg_len >= 5) {
network->realtek_cap_exit = true;
network->bssht.bdRT2RTAggregation = true;
if ((ht_realtek_agg_buf[4] == 1) && (ht_realtek_agg_buf[5] & 0x02))
network->bssht.bdRT2RTLongSlotTime = true;
if ((ht_realtek_agg_buf[4] == 1) && (ht_realtek_agg_buf[5] & RT_HT_CAP_USE_92SE))
network->bssht.RT2RT_HT_Mode |= RT_HT_CAP_USE_92SE;
}
}
if (ht_realtek_agg_len >= 5) {
if ((ht_realtek_agg_buf[5] & RT_HT_CAP_USE_SOFTAP))
network->bssht.RT2RT_HT_Mode |= RT_HT_CAP_USE_SOFTAP;
}
}
if ((info_element->len >= 3 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x05 &&
info_element->data[2] == 0xb5) ||
(info_element->len >= 3 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x0a &&
info_element->data[2] == 0xf7) ||
(info_element->len >= 3 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x10 &&
info_element->data[2] == 0x18)) {
network->broadcom_cap_exist = true;
}
if (info_element->len >= 3 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x0c &&
info_element->data[2] == 0x43)
network->ralink_cap_exist = true;
if ((info_element->len >= 3 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x03 &&
info_element->data[2] == 0x7f) ||
(info_element->len >= 3 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x13 &&
info_element->data[2] == 0x74))
network->atheros_cap_exist = true;
if ((info_element->len >= 3 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x50 &&
info_element->data[2] == 0x43))
network->marvell_cap_exist = true;
if (info_element->len >= 3 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x40 &&
info_element->data[2] == 0x96)
network->cisco_cap_exist = true;
if (info_element->len >= 3 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x0a &&
info_element->data[2] == 0xf5)
network->airgo_cap_exist = true;
if (info_element->len > 4 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x40 &&
info_element->data[2] == 0x96 &&
info_element->data[3] == 0x01) {
if (info_element->len == 6) {
memcpy(network->CcxRmState, &info_element[4], 2);
if (network->CcxRmState[0] != 0)
network->bCcxRmEnable = true;
else
network->bCcxRmEnable = false;
network->MBssidMask = network->CcxRmState[1] & 0x07;
if (network->MBssidMask != 0) {
network->bMBssidValid = true;
network->MBssidMask = 0xff << (network->MBssidMask);
memcpy(network->MBssid, network->bssid, ETH_ALEN);
network->MBssid[5] &= network->MBssidMask;
} else {
network->bMBssidValid = false;
}
} else {
network->bCcxRmEnable = false;
}
}
if (info_element->len > 4 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x40 &&
info_element->data[2] == 0x96 &&
info_element->data[3] == 0x03) {
if (info_element->len == 5) {
network->bWithCcxVerNum = true;
network->BssCcxVerNumber = info_element->data[4];
} else {
network->bWithCcxVerNum = false;
network->BssCcxVerNumber = 0;
}
}
if (info_element->len > 4 &&
info_element->data[0] == 0x00 &&
info_element->data[1] == 0x50 &&
info_element->data[2] == 0xf2 &&
info_element->data[3] == 0x04) {
RTLLIB_DEBUG_MGMT("MFIE_TYPE_WZC: %d bytes\n",
info_element->len);
network->wzc_ie_len = min(info_element->len+2,
MAX_WZC_IE_LEN);
memcpy(network->wzc_ie, info_element,
network->wzc_ie_len);
}
break;
case MFIE_TYPE_RSN:
RTLLIB_DEBUG_MGMT("MFIE_TYPE_RSN: %d bytes\n",
info_element->len);
network->rsn_ie_len = min(info_element->len + 2,
MAX_WPA_IE_LEN);
memcpy(network->rsn_ie, info_element,
network->rsn_ie_len);
break;
case MFIE_TYPE_HT_CAP:
RTLLIB_DEBUG_SCAN("MFIE_TYPE_HT_CAP: %d bytes\n",
info_element->len);
tmp_htcap_len = min(info_element->len, (u8)MAX_IE_LEN);
if (tmp_htcap_len != 0) {
network->bssht.bdHTSpecVer = HT_SPEC_VER_EWC;
network->bssht.bdHTCapLen = tmp_htcap_len > sizeof(network->bssht.bdHTCapBuf) ?
sizeof(network->bssht.bdHTCapBuf) : tmp_htcap_len;
memcpy(network->bssht.bdHTCapBuf,
info_element->data,
network->bssht.bdHTCapLen);
network->bssht.bdSupportHT = true;
network->bssht.bdHT1R = ((((struct ht_capab_ele *)
network->bssht.bdHTCapBuf))->MCS[1]) == 0;
network->bssht.bdBandWidth = (enum ht_channel_width)
(((struct ht_capab_ele *)
(network->bssht.bdHTCapBuf))->ChlWidth);
} else {
network->bssht.bdSupportHT = false;
network->bssht.bdHT1R = false;
network->bssht.bdBandWidth = HT_CHANNEL_WIDTH_20;
}
break;
case MFIE_TYPE_HT_INFO:
RTLLIB_DEBUG_SCAN("MFIE_TYPE_HT_INFO: %d bytes\n",
info_element->len);
tmp_htinfo_len = min(info_element->len, (u8)MAX_IE_LEN);
if (tmp_htinfo_len) {
network->bssht.bdHTSpecVer = HT_SPEC_VER_IEEE;
network->bssht.bdHTInfoLen = tmp_htinfo_len >
sizeof(network->bssht.bdHTInfoBuf) ?
sizeof(network->bssht.bdHTInfoBuf) :
tmp_htinfo_len;
memcpy(network->bssht.bdHTInfoBuf,
info_element->data,
network->bssht.bdHTInfoLen);
}
break;
case MFIE_TYPE_AIRONET:
RTLLIB_DEBUG_SCAN("MFIE_TYPE_AIRONET: %d bytes\n",
info_element->len);
if (info_element->len > IE_CISCO_FLAG_POSITION) {
network->bWithAironetIE = true;
if ((info_element->data[IE_CISCO_FLAG_POSITION]
& SUPPORT_CKIP_MIC) ||
(info_element->data[IE_CISCO_FLAG_POSITION]
& SUPPORT_CKIP_PK))
network->bCkipSupported = true;
else
network->bCkipSupported = false;
} else {
network->bWithAironetIE = false;
network->bCkipSupported = false;
}
break;
case MFIE_TYPE_QOS_PARAMETER:
printk(KERN_ERR
"QoS Error need to parse QOS_PARAMETER IE\n");
break;
case MFIE_TYPE_COUNTRY:
RTLLIB_DEBUG_SCAN("MFIE_TYPE_COUNTRY: %d bytes\n",
info_element->len);
rtllib_extract_country_ie(ieee, info_element, network,
network->bssid);
break;
/* TODO */
default:
RTLLIB_DEBUG_MGMT
("Unsupported info element: %s (%d)\n",
get_info_element_string(info_element->id),
info_element->id);
break;
}
length -= sizeof(*info_element) + info_element->len;
info_element =
(struct rtllib_info_element *)&info_element->
data[info_element->len];
}
if (!network->atheros_cap_exist && !network->broadcom_cap_exist &&
!network->cisco_cap_exist && !network->ralink_cap_exist &&
!network->bssht.bdRT2RTAggregation)
network->unknown_cap_exist = true;
else
network->unknown_cap_exist = false;
return 0;
}