/** * @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(); }