int rtllib_parse_info_param()

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;
}