Ejemplo n.º 1
0
/**
 *  @brief This function process tdls action frame
 *
 *  @param priv        A pointer to mlan_private structure
 *  @param pbuf        A pointer to tdls action frame buffer
 *  @param len         len of tdls action frame buffer
 *  @return            N/A
 */
void
wlan_process_tdls_action_frame(pmlan_private priv, t_u8 *pbuf, t_u32 len)
{
	sta_node *sta_ptr = MNULL;
	t_u8 *peer;
	t_u8 *pos, *end;
	t_u8 action;
	int ie_len = 0;
	t_u8 i;

#define TDLS_PAYLOAD_TYPE     2
#define TDLS_CATEGORY         0x0c
#define TDLS_REQ_FIX_LEN      6
#define TDLS_RESP_FIX_LEN     8
#define TDLS_CONFIRM_FIX_LEN  6
	if (len < (sizeof(EthII_Hdr_t) + 3))
		return;
	if (*(t_u8 *)(pbuf + sizeof(EthII_Hdr_t)) != TDLS_PAYLOAD_TYPE)
		/* TDLS payload type = 2 */
		return;
	if (*(t_u8 *)(pbuf + sizeof(EthII_Hdr_t) + 1) != TDLS_CATEGORY)
		/* TDLS category = 0xc */
		return;
	peer = pbuf + MLAN_MAC_ADDR_LENGTH;

	action = *(t_u8 *)(pbuf + sizeof(EthII_Hdr_t) + 2);
	/* 2= payload type + category */

	if (action > TDLS_SETUP_CONFIRM) {
		/* just handle TDLS setup request/response/confirm */
		PRINTM(MMSG, "Recv TDLS Action: peer=" MACSTR ", action=%d\n",
		       MAC2STR(peer), action);
		return;
	}

	sta_ptr = wlan_add_station_entry(priv, peer);
	if (!sta_ptr)
		return;
	if (action == TDLS_SETUP_REQUEST) {	/* setup request */
		sta_ptr->status = TDLS_NOT_SETUP;
		PRINTM(MMSG, "Recv TDLS SETUP Request: peer=" MACSTR "\n",
		       MAC2STR(peer));
		wlan_hold_tdls_packets(priv, peer);
		if (len < (sizeof(EthII_Hdr_t) + TDLS_REQ_FIX_LEN))
			return;
		pos = pbuf + sizeof(EthII_Hdr_t) + 4;
		/* payload 1+ category 1 + action 1 +dialog 1 */
		sta_ptr->capability = mlan_ntohs(*(t_u16 *)pos);
		ie_len = len - sizeof(EthII_Hdr_t) - TDLS_REQ_FIX_LEN;
		pos += 2;
	} else if (action == 1) {	/* setup respons */
		PRINTM(MMSG, "Recv TDLS SETUP Response: peer=" MACSTR "\n",
		       MAC2STR(peer));
		if (len < (sizeof(EthII_Hdr_t) + TDLS_RESP_FIX_LEN))
			return;
		pos = pbuf + sizeof(EthII_Hdr_t) + 6;
		/* payload 1+ category 1 + action 1 +dialog 1 +status 2 */
		sta_ptr->capability = mlan_ntohs(*(t_u16 *)pos);
		ie_len = len - sizeof(EthII_Hdr_t) - TDLS_RESP_FIX_LEN;
		pos += 2;
	} else {		/* setup confirm */
		PRINTM(MMSG, "Recv TDLS SETUP Confirm: peer=" MACSTR "\n",
		       MAC2STR(peer));
		if (len < (sizeof(EthII_Hdr_t) + TDLS_CONFIRM_FIX_LEN))
			return;
		pos = pbuf + sizeof(EthII_Hdr_t) + TDLS_CONFIRM_FIX_LEN;
		/* payload 1+ category 1 + action 1 +dialog 1 + status 2 */
		ie_len = len - sizeof(EthII_Hdr_t) - TDLS_CONFIRM_FIX_LEN;
	}
	for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) {
		if (pos + 2 + pos[1] > end)
			break;
		switch (*pos) {
		case SUPPORTED_RATES:
			sta_ptr->rate_len = pos[1];
			for (i = 0; i < pos[1]; i++)
				sta_ptr->support_rate[i] = pos[2 + i];
			break;
		case EXTENDED_SUPPORTED_RATES:
			for (i = 0; i < pos[1]; i++)
				sta_ptr->support_rate[sta_ptr->rate_len + i] =
					pos[2 + i];
			sta_ptr->rate_len += pos[1];
			break;
		case HT_CAPABILITY:
			memcpy(priv->adapter, (t_u8 *)&sta_ptr->HTcap, pos,
			       sizeof(IEEEtypes_HTCap_t));
			sta_ptr->is_11n_enabled = 1;
			DBG_HEXDUMP(MDAT_D, "TDLS HT capability",
				    (t_u8 *)(&sta_ptr->HTcap),
				    MIN(sizeof(IEEEtypes_HTCap_t),
					MAX_DATA_DUMP_LEN));
			break;
		case HT_OPERATION:
			memcpy(priv->adapter, &sta_ptr->HTInfo, pos,
			       sizeof(IEEEtypes_HTInfo_t));
			DBG_HEXDUMP(MDAT_D, "TDLS HT info",
				    (t_u8 *)(&sta_ptr->HTInfo),
				    MIN(sizeof(IEEEtypes_HTInfo_t),
					MAX_DATA_DUMP_LEN));
			break;
		case BSSCO_2040:
			memcpy(priv->adapter, (t_u8 *)&sta_ptr->BSSCO_20_40,
			       pos, sizeof(IEEEtypes_2040BSSCo_t));
			break;
		case EXT_CAPABILITY:
			memcpy(priv->adapter, (t_u8 *)&sta_ptr->ExtCap, pos,
			       pos[1] + sizeof(IEEEtypes_Header_t));
			DBG_HEXDUMP(MDAT_D, "TDLS Extended capability",
				    (t_u8 *)(&sta_ptr->ExtCap),
				    sta_ptr->ExtCap.ieee_hdr.len + 2);
			break;
		case RSN_IE:
			memcpy(priv->adapter, (t_u8 *)&sta_ptr->rsn_ie, pos,
			       pos[1] + sizeof(IEEEtypes_Header_t));
			DBG_HEXDUMP(MDAT_D, "TDLS Rsn ie ",
				    (t_u8 *)(&sta_ptr->rsn_ie),
				    pos[1] + sizeof(IEEEtypes_Header_t));
			break;
		case QOS_INFO:
			sta_ptr->qos_info = pos[2];
			PRINTM(MDAT_D, "TDLS qos info %x\n", sta_ptr->qos_info);
			break;
		case LINK_ID:
			memcpy(priv->adapter, (t_u8 *)&sta_ptr->link_ie, pos,
			       sizeof(IEEEtypes_LinkIDElement_t));
			break;

		default:
			break;
		}
	}
	return;
}
/**
 *  @brief This function will parse the TDLS event for further wlan action
 *
 *  @param priv     A pointer to mlan_private
 *  @param pevent   A pointer to event buf
 *
 *  @return         N/A
 */
static void
wlan_parse_tdls_event(pmlan_private priv, pmlan_buffer pevent)
{
	Event_tdls_generic *tdls_event = (Event_tdls_generic *)
		(pevent->pbuf + pevent->data_offset + sizeof(mlan_event_id));
	sta_node *sta_ptr = MNULL;
	pmlan_adapter pmadapter = priv->adapter;
	t_u8 i = 0;
	IEEEtypes_HTCap_t *pht_cap = MNULL;
	t_u16 ie_len = 0;
	mlan_ds_misc_tdls_oper tdls_oper;
	t_u8 event_buf[100];
	mlan_event *ptdls_event = (mlan_event *)event_buf;
	tdls_tear_down_event *tdls_evt =
		(tdls_tear_down_event *)ptdls_event->event_buf;
	ENTER();

	/* reason code is not mandatory, hence less by sizeof(t_u16) */
	if (pevent->data_len < (sizeof(Event_tdls_generic) -
				sizeof(t_u16) - sizeof(mlan_event_id))) {
		PRINTM(MERROR, "Invalid length %d for TDLS event\n",
		       pevent->data_len);
		LEAVE();
		return;
	}
	sta_ptr = wlan_get_station_entry(priv, tdls_event->peer_mac_addr);
	PRINTM(MEVENT, "TDLS_EVENT: %d " MACSTR "\n",
	       wlan_le16_to_cpu(tdls_event->event_type),
	       MAC2STR(tdls_event->peer_mac_addr));
	switch (wlan_le16_to_cpu(tdls_event->event_type)) {
	case TDLS_EVENT_TYPE_SETUP_REQ:
		if (sta_ptr == MNULL) {
			sta_ptr =
				wlan_add_station_entry(priv,
						       tdls_event->
						       peer_mac_addr);
			if (sta_ptr) {
				sta_ptr->status = TDLS_SETUP_INPROGRESS;
				wlan_hold_tdls_packets(priv,
						       tdls_event->
						       peer_mac_addr);
			}
		}
		break;

	case TDLS_EVENT_TYPE_LINK_ESTABLISHED:
		if (sta_ptr) {
			sta_ptr->status = TDLS_SETUP_COMPLETE;
			/* parse the TLV for station's capability */
			ie_len = wlan_le16_to_cpu(tdls_event->u.ie_data.
						  ie_length);
			if (ie_len) {
				pht_cap =
					(IEEEtypes_HTCap_t *)
					wlan_get_specific_ie(priv,
							     tdls_event->u.
							     ie_data.ie_ptr,
							     ie_len,
							     HT_CAPABILITY);
				if (pht_cap) {
					sta_ptr->is_11n_enabled = MTRUE;
					if (GETHT_MAXAMSDU
					    (pht_cap->ht_cap.ht_cap_info))
						sta_ptr->max_amsdu =
							MLAN_TX_DATA_BUF_SIZE_8K;
					else
						sta_ptr->max_amsdu =
							MLAN_TX_DATA_BUF_SIZE_4K;
				}
			}
			for (i = 0; i < MAX_NUM_TID; i++) {
				if (sta_ptr->is_11n_enabled)
					sta_ptr->ampdu_sta[i] =
						priv->aggr_prio_tbl[i].
						ampdu_user;
				else
					sta_ptr->ampdu_sta[i] =
						BA_STREAM_NOT_ALLOWED;
			}
			memset(priv->adapter, sta_ptr->rx_seq, 0xff,
			       sizeof(sta_ptr->rx_seq));
			wlan_restore_tdls_packets(priv,
						  tdls_event->peer_mac_addr,
						  TDLS_SETUP_COMPLETE);
			pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL;
		}
		break;

	case TDLS_EVENT_TYPE_SETUP_FAILURE:
		wlan_restore_tdls_packets(priv, tdls_event->peer_mac_addr,
					  TDLS_SETUP_FAILURE);
		if (sta_ptr)
			wlan_delete_station_entry(priv,
						  tdls_event->peer_mac_addr);
		if (MTRUE == wlan_is_station_list_empty(priv))
			pmadapter->tdls_status = TDLS_NOT_SETUP;
		else
			pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL;
		break;
	case TDLS_EVENT_TYPE_LINK_TORN_DOWN:
		if (sta_ptr) {
			if (sta_ptr->external_tdls) {
				PRINTM(MMSG,
				       "Receive TDLS TEAR DOWN event, Disable TDLS LINK\n");
				memset(pmadapter, &tdls_oper, 0,
				       sizeof(tdls_oper));
				tdls_oper.tdls_action = WLAN_TDLS_DISABLE_LINK;
				memcpy(priv->adapter, tdls_oper.peer_mac,
				       tdls_event->peer_mac_addr,
				       MLAN_MAC_ADDR_LENGTH);
				/* Send command to firmware to delete tdls link */
				wlan_prepare_cmd(priv,
						 HostCmd_CMD_TDLS_OPERATION,
						 HostCmd_ACT_GEN_SET,
						 0,
						 (t_void *)MNULL, &tdls_oper);
				ptdls_event->bss_index = priv->bss_index;
				ptdls_event->event_id =
					MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ;
				ptdls_event->event_len =
					sizeof(tdls_tear_down_event);
				memcpy(priv->adapter,
				       (t_u8 *)tdls_evt->peer_mac_addr,
				       tdls_event->peer_mac_addr,
				       MLAN_MAC_ADDR_LENGTH);
				tdls_evt->reason_code =
					wlan_le16_to_cpu(tdls_event->u.
							 reason_code);
				wlan_recv_event(priv,
						MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ,
						ptdls_event);
				/* Signal MOAL to trigger mlan_main_process */
				wlan_recv_event(priv,
						MLAN_EVENT_ID_DRV_DEFER_HANDLING,
						MNULL);
				LEAVE();
				return;
			}
			wlan_restore_tdls_packets(priv,
						  tdls_event->peer_mac_addr,
						  TDLS_TEAR_DOWN);
			if (sta_ptr->is_11n_enabled) {
				wlan_cleanup_reorder_tbl(priv,
							 tdls_event->
							 peer_mac_addr);
				pmadapter->callbacks.moal_spin_lock(pmadapter->
								    pmoal_handle,
								    priv->wmm.
								    ra_list_spinlock);
				wlan_11n_cleanup_txbastream_tbl(priv,
								tdls_event->
								peer_mac_addr);
				pmadapter->callbacks.
					moal_spin_unlock(pmadapter->
							 pmoal_handle,
							 priv->wmm.
							 ra_list_spinlock);
			}
			wlan_delete_station_entry(priv,
						  tdls_event->peer_mac_addr);
			if (MTRUE == wlan_is_station_list_empty(priv))
				pmadapter->tdls_status = TDLS_NOT_SETUP;
			else
				pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL;
		}
		break;
	case TDLS_EVENT_TYPE_CHAN_SWITCH_RESULT:
		PRINTM(MEVENT,
		       "TDLS_CHAN_SWITCH_RESULT: status=0x%x, reason=0x%x current_channel=%d\n",
		       tdls_event->u.switch_result.status,
		       tdls_event->u.switch_result.reason,
		       (int)tdls_event->u.switch_result.current_channel);
		if (tdls_event->u.switch_result.status == MLAN_STATUS_SUCCESS) {
			if (tdls_event->u.switch_result.current_channel ==
			    TDLS_BASE_CHANNEL) {
				/* enable traffic to AP */
				if (pmadapter->tdls_status !=
				    TDLS_IN_BASE_CHANNEL) {
					wlan_update_non_tdls_ralist(priv,
								    tdls_event->
								    peer_mac_addr,
								    MFALSE);
					pmadapter->tdls_status =
						TDLS_IN_BASE_CHANNEL;
				}
			} else if (tdls_event->u.switch_result.
				   current_channel == TDLS_OFF_CHANNEL) {
				/* pause traffic to AP */
				if (pmadapter->tdls_status !=
				    TDLS_IN_OFF_CHANNEL) {
					wlan_update_non_tdls_ralist(priv,
								    tdls_event->
								    peer_mac_addr,
								    MTRUE);
					pmadapter->tdls_status =
						TDLS_IN_OFF_CHANNEL;
				}
			}
		} else {
			if (tdls_event->u.switch_result.current_channel ==
			    TDLS_BASE_CHANNEL)
				pmadapter->tdls_status = TDLS_IN_BASE_CHANNEL;
			else if (tdls_event->u.switch_result.current_channel ==
				 TDLS_OFF_CHANNEL)
				pmadapter->tdls_status = TDLS_IN_OFF_CHANNEL;
		}
		break;
	case TDLS_EVENT_TYPE_START_CHAN_SWITCH:
		PRINTM(MEVENT, "TDLS start channel switch....\n");
		pmadapter->tdls_status = TDLS_SWITCHING_CHANNEL;
		break;
	case TDLS_EVENT_TYPE_CHAN_SWITCH_STOPPED:
		PRINTM(MEVENT, "TDLS channel switch stopped, reason=%d\n",
		       tdls_event->u.cs_stop_reason);
		break;
	case TDLS_EVENT_TYPE_DEBUG:
	case TDLS_EVENT_TYPE_PACKET:
		break;
	default:
		PRINTM(MERROR, "unknown event type %d\n",
		       wlan_le16_to_cpu(tdls_event->event_type));
		break;
	}
	LEAVE();
}