/drivers/net/wireless/ath/ath6kl/wmi.c
http://github.com/mirrors/linux · C · 4173 lines · 3177 code · 764 blank · 232 comment · 489 complexity · 5cecb7efd1534bb7667e9e3a2b41f31d MD5 · raw file
Large files are truncated click here to view the full file
- /*
- * Copyright (c) 2004-2011 Atheros Communications Inc.
- * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include <linux/ip.h>
- #include <linux/in.h>
- #include "core.h"
- #include "debug.h"
- #include "testmode.h"
- #include "trace.h"
- #include "../regd.h"
- #include "../regd_common.h"
- static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx);
- static const s32 wmi_rate_tbl[][2] = {
- /* {W/O SGI, with SGI} */
- {1000, 1000},
- {2000, 2000},
- {5500, 5500},
- {11000, 11000},
- {6000, 6000},
- {9000, 9000},
- {12000, 12000},
- {18000, 18000},
- {24000, 24000},
- {36000, 36000},
- {48000, 48000},
- {54000, 54000},
- {6500, 7200},
- {13000, 14400},
- {19500, 21700},
- {26000, 28900},
- {39000, 43300},
- {52000, 57800},
- {58500, 65000},
- {65000, 72200},
- {13500, 15000},
- {27000, 30000},
- {40500, 45000},
- {54000, 60000},
- {81000, 90000},
- {108000, 120000},
- {121500, 135000},
- {135000, 150000},
- {0, 0}
- };
- static const s32 wmi_rate_tbl_mcs15[][2] = {
- /* {W/O SGI, with SGI} */
- {1000, 1000},
- {2000, 2000},
- {5500, 5500},
- {11000, 11000},
- {6000, 6000},
- {9000, 9000},
- {12000, 12000},
- {18000, 18000},
- {24000, 24000},
- {36000, 36000},
- {48000, 48000},
- {54000, 54000},
- {6500, 7200}, /* HT 20, MCS 0 */
- {13000, 14400},
- {19500, 21700},
- {26000, 28900},
- {39000, 43300},
- {52000, 57800},
- {58500, 65000},
- {65000, 72200},
- {13000, 14400}, /* HT 20, MCS 8 */
- {26000, 28900},
- {39000, 43300},
- {52000, 57800},
- {78000, 86700},
- {104000, 115600},
- {117000, 130000},
- {130000, 144400}, /* HT 20, MCS 15 */
- {13500, 15000}, /*HT 40, MCS 0 */
- {27000, 30000},
- {40500, 45000},
- {54000, 60000},
- {81000, 90000},
- {108000, 120000},
- {121500, 135000},
- {135000, 150000},
- {27000, 30000}, /*HT 40, MCS 8 */
- {54000, 60000},
- {81000, 90000},
- {108000, 120000},
- {162000, 180000},
- {216000, 240000},
- {243000, 270000},
- {270000, 300000}, /*HT 40, MCS 15 */
- {0, 0}
- };
- /* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */
- static const u8 up_to_ac[] = {
- WMM_AC_BE,
- WMM_AC_BK,
- WMM_AC_BK,
- WMM_AC_BE,
- WMM_AC_VI,
- WMM_AC_VI,
- WMM_AC_VO,
- WMM_AC_VO,
- };
- void ath6kl_wmi_set_control_ep(struct wmi *wmi, enum htc_endpoint_id ep_id)
- {
- if (WARN_ON(ep_id == ENDPOINT_UNUSED || ep_id >= ENDPOINT_MAX))
- return;
- wmi->ep_id = ep_id;
- }
- enum htc_endpoint_id ath6kl_wmi_get_control_ep(struct wmi *wmi)
- {
- return wmi->ep_id;
- }
- struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx)
- {
- struct ath6kl_vif *vif, *found = NULL;
- if (WARN_ON(if_idx > (ar->vif_max - 1)))
- return NULL;
- /* FIXME: Locking */
- spin_lock_bh(&ar->list_lock);
- list_for_each_entry(vif, &ar->vif_list, list) {
- if (vif->fw_vif_idx == if_idx) {
- found = vif;
- break;
- }
- }
- spin_unlock_bh(&ar->list_lock);
- return found;
- }
- /* Performs DIX to 802.3 encapsulation for transmit packets.
- * Assumes the entire DIX header is contiguous and that there is
- * enough room in the buffer for a 802.3 mac header and LLC+SNAP headers.
- */
- int ath6kl_wmi_dix_2_dot3(struct wmi *wmi, struct sk_buff *skb)
- {
- struct ath6kl_llc_snap_hdr *llc_hdr;
- struct ethhdr *eth_hdr;
- size_t new_len;
- __be16 type;
- u8 *datap;
- u16 size;
- if (WARN_ON(skb == NULL))
- return -EINVAL;
- size = sizeof(struct ath6kl_llc_snap_hdr) + sizeof(struct wmi_data_hdr);
- if (skb_headroom(skb) < size)
- return -ENOMEM;
- eth_hdr = (struct ethhdr *) skb->data;
- type = eth_hdr->h_proto;
- if (!is_ethertype(be16_to_cpu(type))) {
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "%s: pkt is already in 802.3 format\n", __func__);
- return 0;
- }
- new_len = skb->len - sizeof(*eth_hdr) + sizeof(*llc_hdr);
- skb_push(skb, sizeof(struct ath6kl_llc_snap_hdr));
- datap = skb->data;
- eth_hdr->h_proto = cpu_to_be16(new_len);
- memcpy(datap, eth_hdr, sizeof(*eth_hdr));
- llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap + sizeof(*eth_hdr));
- llc_hdr->dsap = 0xAA;
- llc_hdr->ssap = 0xAA;
- llc_hdr->cntl = 0x03;
- llc_hdr->org_code[0] = 0x0;
- llc_hdr->org_code[1] = 0x0;
- llc_hdr->org_code[2] = 0x0;
- llc_hdr->eth_type = type;
- return 0;
- }
- static int ath6kl_wmi_meta_add(struct wmi *wmi, struct sk_buff *skb,
- u8 *version, void *tx_meta_info)
- {
- struct wmi_tx_meta_v1 *v1;
- struct wmi_tx_meta_v2 *v2;
- if (WARN_ON(skb == NULL || version == NULL))
- return -EINVAL;
- switch (*version) {
- case WMI_META_VERSION_1:
- skb_push(skb, WMI_MAX_TX_META_SZ);
- v1 = (struct wmi_tx_meta_v1 *) skb->data;
- v1->pkt_id = 0;
- v1->rate_plcy_id = 0;
- *version = WMI_META_VERSION_1;
- break;
- case WMI_META_VERSION_2:
- skb_push(skb, WMI_MAX_TX_META_SZ);
- v2 = (struct wmi_tx_meta_v2 *) skb->data;
- memcpy(v2, (struct wmi_tx_meta_v2 *) tx_meta_info,
- sizeof(struct wmi_tx_meta_v2));
- break;
- }
- return 0;
- }
- int ath6kl_wmi_data_hdr_add(struct wmi *wmi, struct sk_buff *skb,
- u8 msg_type, u32 flags,
- enum wmi_data_hdr_data_type data_type,
- u8 meta_ver, void *tx_meta_info, u8 if_idx)
- {
- struct wmi_data_hdr *data_hdr;
- int ret;
- if (WARN_ON(skb == NULL || (if_idx > wmi->parent_dev->vif_max - 1)))
- return -EINVAL;
- if (tx_meta_info) {
- ret = ath6kl_wmi_meta_add(wmi, skb, &meta_ver, tx_meta_info);
- if (ret)
- return ret;
- }
- skb_push(skb, sizeof(struct wmi_data_hdr));
- data_hdr = (struct wmi_data_hdr *)skb->data;
- memset(data_hdr, 0, sizeof(struct wmi_data_hdr));
- data_hdr->info = msg_type << WMI_DATA_HDR_MSG_TYPE_SHIFT;
- data_hdr->info |= data_type << WMI_DATA_HDR_DATA_TYPE_SHIFT;
- if (flags & WMI_DATA_HDR_FLAGS_MORE)
- data_hdr->info |= WMI_DATA_HDR_MORE;
- if (flags & WMI_DATA_HDR_FLAGS_EOSP)
- data_hdr->info3 |= cpu_to_le16(WMI_DATA_HDR_EOSP);
- data_hdr->info2 |= cpu_to_le16(meta_ver << WMI_DATA_HDR_META_SHIFT);
- data_hdr->info3 |= cpu_to_le16(if_idx & WMI_DATA_HDR_IF_IDX_MASK);
- return 0;
- }
- u8 ath6kl_wmi_determine_user_priority(u8 *pkt, u32 layer2_pri)
- {
- struct iphdr *ip_hdr = (struct iphdr *) pkt;
- u8 ip_pri;
- /*
- * Determine IPTOS priority
- *
- * IP-TOS - 8bits
- * : DSCP(6-bits) ECN(2-bits)
- * : DSCP - P2 P1 P0 X X X
- * where (P2 P1 P0) form 802.1D
- */
- ip_pri = ip_hdr->tos >> 5;
- ip_pri &= 0x7;
- if ((layer2_pri & 0x7) > ip_pri)
- return (u8) layer2_pri & 0x7;
- else
- return ip_pri;
- }
- u8 ath6kl_wmi_get_traffic_class(u8 user_priority)
- {
- return up_to_ac[user_priority & 0x7];
- }
- int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx,
- struct sk_buff *skb,
- u32 layer2_priority, bool wmm_enabled,
- u8 *ac)
- {
- struct wmi_data_hdr *data_hdr;
- struct ath6kl_llc_snap_hdr *llc_hdr;
- struct wmi_create_pstream_cmd cmd;
- u32 meta_size, hdr_size;
- u16 ip_type = IP_ETHERTYPE;
- u8 stream_exist, usr_pri;
- u8 traffic_class = WMM_AC_BE;
- u8 *datap;
- if (WARN_ON(skb == NULL))
- return -EINVAL;
- datap = skb->data;
- data_hdr = (struct wmi_data_hdr *) datap;
- meta_size = ((le16_to_cpu(data_hdr->info2) >> WMI_DATA_HDR_META_SHIFT) &
- WMI_DATA_HDR_META_MASK) ? WMI_MAX_TX_META_SZ : 0;
- if (!wmm_enabled) {
- /* If WMM is disabled all traffic goes as BE traffic */
- usr_pri = 0;
- } else {
- hdr_size = sizeof(struct ethhdr);
- llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap +
- sizeof(struct
- wmi_data_hdr) +
- meta_size + hdr_size);
- if (llc_hdr->eth_type == htons(ip_type)) {
- /*
- * Extract the endpoint info from the TOS field
- * in the IP header.
- */
- usr_pri =
- ath6kl_wmi_determine_user_priority(((u8 *) llc_hdr) +
- sizeof(struct ath6kl_llc_snap_hdr),
- layer2_priority);
- } else {
- usr_pri = layer2_priority & 0x7;
- }
- /*
- * Queue the EAPOL frames in the same WMM_AC_VO queue
- * as that of management frames.
- */
- if (skb->protocol == cpu_to_be16(ETH_P_PAE))
- usr_pri = WMI_VOICE_USER_PRIORITY;
- }
- /*
- * workaround for WMM S5
- *
- * FIXME: wmi->traffic_class is always 100 so this test doesn't
- * make sense
- */
- if ((wmi->traffic_class == WMM_AC_VI) &&
- ((usr_pri == 5) || (usr_pri == 4)))
- usr_pri = 1;
- /* Convert user priority to traffic class */
- traffic_class = up_to_ac[usr_pri & 0x7];
- wmi_data_hdr_set_up(data_hdr, usr_pri);
- spin_lock_bh(&wmi->lock);
- stream_exist = wmi->fat_pipe_exist;
- spin_unlock_bh(&wmi->lock);
- if (!(stream_exist & (1 << traffic_class))) {
- memset(&cmd, 0, sizeof(cmd));
- cmd.traffic_class = traffic_class;
- cmd.user_pri = usr_pri;
- cmd.inactivity_int =
- cpu_to_le32(WMI_IMPLICIT_PSTREAM_INACTIVITY_INT);
- /* Implicit streams are created with TSID 0xFF */
- cmd.tsid = WMI_IMPLICIT_PSTREAM;
- ath6kl_wmi_create_pstream_cmd(wmi, if_idx, &cmd);
- }
- *ac = traffic_class;
- return 0;
- }
- int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb)
- {
- struct ieee80211_hdr_3addr *pwh, wh;
- struct ath6kl_llc_snap_hdr *llc_hdr;
- struct ethhdr eth_hdr;
- u32 hdr_size;
- u8 *datap;
- __le16 sub_type;
- if (WARN_ON(skb == NULL))
- return -EINVAL;
- datap = skb->data;
- pwh = (struct ieee80211_hdr_3addr *) datap;
- sub_type = pwh->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE);
- memcpy((u8 *) &wh, datap, sizeof(struct ieee80211_hdr_3addr));
- /* Strip off the 802.11 header */
- if (sub_type == cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
- hdr_size = roundup(sizeof(struct ieee80211_qos_hdr),
- sizeof(u32));
- skb_pull(skb, hdr_size);
- } else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA)) {
- skb_pull(skb, sizeof(struct ieee80211_hdr_3addr));
- }
- datap = skb->data;
- llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap);
- memset(ð_hdr, 0, sizeof(eth_hdr));
- eth_hdr.h_proto = llc_hdr->eth_type;
- switch ((le16_to_cpu(wh.frame_control)) &
- (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
- case IEEE80211_FCTL_TODS:
- memcpy(eth_hdr.h_dest, wh.addr3, ETH_ALEN);
- memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN);
- break;
- case IEEE80211_FCTL_FROMDS:
- memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN);
- memcpy(eth_hdr.h_source, wh.addr3, ETH_ALEN);
- break;
- case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
- break;
- default:
- memcpy(eth_hdr.h_dest, wh.addr1, ETH_ALEN);
- memcpy(eth_hdr.h_source, wh.addr2, ETH_ALEN);
- break;
- }
- skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr));
- skb_push(skb, sizeof(eth_hdr));
- datap = skb->data;
- memcpy(datap, ð_hdr, sizeof(eth_hdr));
- return 0;
- }
- /*
- * Performs 802.3 to DIX encapsulation for received packets.
- * Assumes the entire 802.3 header is contiguous.
- */
- int ath6kl_wmi_dot3_2_dix(struct sk_buff *skb)
- {
- struct ath6kl_llc_snap_hdr *llc_hdr;
- struct ethhdr eth_hdr;
- u8 *datap;
- if (WARN_ON(skb == NULL))
- return -EINVAL;
- datap = skb->data;
- memcpy(ð_hdr, datap, sizeof(eth_hdr));
- llc_hdr = (struct ath6kl_llc_snap_hdr *) (datap + sizeof(eth_hdr));
- eth_hdr.h_proto = llc_hdr->eth_type;
- skb_pull(skb, sizeof(struct ath6kl_llc_snap_hdr));
- datap = skb->data;
- memcpy(datap, ð_hdr, sizeof(eth_hdr));
- return 0;
- }
- static int ath6kl_wmi_tx_complete_event_rx(u8 *datap, int len)
- {
- struct tx_complete_msg_v1 *msg_v1;
- struct wmi_tx_complete_event *evt;
- int index;
- u16 size;
- evt = (struct wmi_tx_complete_event *) datap;
- ath6kl_dbg(ATH6KL_DBG_WMI, "comp: %d %d %d\n",
- evt->num_msg, evt->msg_len, evt->msg_type);
- for (index = 0; index < evt->num_msg; index++) {
- size = sizeof(struct wmi_tx_complete_event) +
- (index * sizeof(struct tx_complete_msg_v1));
- msg_v1 = (struct tx_complete_msg_v1 *)(datap + size);
- ath6kl_dbg(ATH6KL_DBG_WMI, "msg: %d %d %d %d\n",
- msg_v1->status, msg_v1->pkt_id,
- msg_v1->rate_idx, msg_v1->ack_failures);
- }
- return 0;
- }
- static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap,
- int len, struct ath6kl_vif *vif)
- {
- struct wmi_remain_on_chnl_event *ev;
- u32 freq;
- u32 dur;
- struct ieee80211_channel *chan;
- struct ath6kl *ar = wmi->parent_dev;
- u32 id;
- if (len < sizeof(*ev))
- return -EINVAL;
- ev = (struct wmi_remain_on_chnl_event *) datap;
- freq = le32_to_cpu(ev->freq);
- dur = le32_to_cpu(ev->duration);
- ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: freq=%u dur=%u\n",
- freq, dur);
- chan = ieee80211_get_channel(ar->wiphy, freq);
- if (!chan) {
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "remain_on_chnl: Unknown channel (freq=%u)\n",
- freq);
- return -EINVAL;
- }
- id = vif->last_roc_id;
- cfg80211_ready_on_channel(&vif->wdev, id, chan,
- dur, GFP_ATOMIC);
- return 0;
- }
- static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi,
- u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_cancel_remain_on_chnl_event *ev;
- u32 freq;
- u32 dur;
- struct ieee80211_channel *chan;
- struct ath6kl *ar = wmi->parent_dev;
- u32 id;
- if (len < sizeof(*ev))
- return -EINVAL;
- ev = (struct wmi_cancel_remain_on_chnl_event *) datap;
- freq = le32_to_cpu(ev->freq);
- dur = le32_to_cpu(ev->duration);
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "cancel_remain_on_chnl: freq=%u dur=%u status=%u\n",
- freq, dur, ev->status);
- chan = ieee80211_get_channel(ar->wiphy, freq);
- if (!chan) {
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "cancel_remain_on_chnl: Unknown channel (freq=%u)\n",
- freq);
- return -EINVAL;
- }
- if (vif->last_cancel_roc_id &&
- vif->last_cancel_roc_id + 1 == vif->last_roc_id)
- id = vif->last_cancel_roc_id; /* event for cancel command */
- else
- id = vif->last_roc_id; /* timeout on uncanceled r-o-c */
- vif->last_cancel_roc_id = 0;
- cfg80211_remain_on_channel_expired(&vif->wdev, id, chan, GFP_ATOMIC);
- return 0;
- }
- static int ath6kl_wmi_tx_status_event_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_tx_status_event *ev;
- u32 id;
- if (len < sizeof(*ev))
- return -EINVAL;
- ev = (struct wmi_tx_status_event *) datap;
- id = le32_to_cpu(ev->id);
- ath6kl_dbg(ATH6KL_DBG_WMI, "tx_status: id=%x ack_status=%u\n",
- id, ev->ack_status);
- if (wmi->last_mgmt_tx_frame) {
- cfg80211_mgmt_tx_status(&vif->wdev, id,
- wmi->last_mgmt_tx_frame,
- wmi->last_mgmt_tx_frame_len,
- !!ev->ack_status, GFP_ATOMIC);
- kfree(wmi->last_mgmt_tx_frame);
- wmi->last_mgmt_tx_frame = NULL;
- wmi->last_mgmt_tx_frame_len = 0;
- }
- return 0;
- }
- static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_p2p_rx_probe_req_event *ev;
- u32 freq;
- u16 dlen;
- if (len < sizeof(*ev))
- return -EINVAL;
- ev = (struct wmi_p2p_rx_probe_req_event *) datap;
- freq = le32_to_cpu(ev->freq);
- dlen = le16_to_cpu(ev->len);
- if (datap + len < ev->data + dlen) {
- ath6kl_err("invalid wmi_p2p_rx_probe_req_event: len=%d dlen=%u\n",
- len, dlen);
- return -EINVAL;
- }
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "rx_probe_req: len=%u freq=%u probe_req_report=%d\n",
- dlen, freq, vif->probe_req_report);
- if (vif->probe_req_report || vif->nw_type == AP_NETWORK)
- cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0);
- return 0;
- }
- static int ath6kl_wmi_p2p_capabilities_event_rx(u8 *datap, int len)
- {
- struct wmi_p2p_capabilities_event *ev;
- u16 dlen;
- if (len < sizeof(*ev))
- return -EINVAL;
- ev = (struct wmi_p2p_capabilities_event *) datap;
- dlen = le16_to_cpu(ev->len);
- ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_capab: len=%u\n", dlen);
- return 0;
- }
- static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_rx_action_event *ev;
- u32 freq;
- u16 dlen;
- if (len < sizeof(*ev))
- return -EINVAL;
- ev = (struct wmi_rx_action_event *) datap;
- freq = le32_to_cpu(ev->freq);
- dlen = le16_to_cpu(ev->len);
- if (datap + len < ev->data + dlen) {
- ath6kl_err("invalid wmi_rx_action_event: len=%d dlen=%u\n",
- len, dlen);
- return -EINVAL;
- }
- ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq);
- cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0);
- return 0;
- }
- static int ath6kl_wmi_p2p_info_event_rx(u8 *datap, int len)
- {
- struct wmi_p2p_info_event *ev;
- u32 flags;
- u16 dlen;
- if (len < sizeof(*ev))
- return -EINVAL;
- ev = (struct wmi_p2p_info_event *) datap;
- flags = le32_to_cpu(ev->info_req_flags);
- dlen = le16_to_cpu(ev->len);
- ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: flags=%x len=%d\n", flags, dlen);
- if (flags & P2P_FLAG_CAPABILITIES_REQ) {
- struct wmi_p2p_capabilities *cap;
- if (dlen < sizeof(*cap))
- return -EINVAL;
- cap = (struct wmi_p2p_capabilities *) ev->data;
- ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: GO Power Save = %d\n",
- cap->go_power_save);
- }
- if (flags & P2P_FLAG_MACADDR_REQ) {
- struct wmi_p2p_macaddr *mac;
- if (dlen < sizeof(*mac))
- return -EINVAL;
- mac = (struct wmi_p2p_macaddr *) ev->data;
- ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: MAC Address = %pM\n",
- mac->mac_addr);
- }
- if (flags & P2P_FLAG_HMODEL_REQ) {
- struct wmi_p2p_hmodel *mod;
- if (dlen < sizeof(*mod))
- return -EINVAL;
- mod = (struct wmi_p2p_hmodel *) ev->data;
- ath6kl_dbg(ATH6KL_DBG_WMI, "p2p_info: P2P Model = %d (%s)\n",
- mod->p2p_model,
- mod->p2p_model ? "host" : "firmware");
- }
- return 0;
- }
- static inline struct sk_buff *ath6kl_wmi_get_new_buf(u32 size)
- {
- struct sk_buff *skb;
- skb = ath6kl_buf_alloc(size);
- if (!skb)
- return NULL;
- skb_put(skb, size);
- if (size)
- memset(skb->data, 0, size);
- return skb;
- }
- /* Send a "simple" wmi command -- one with no arguments */
- static int ath6kl_wmi_simple_cmd(struct wmi *wmi, u8 if_idx,
- enum wmi_cmd_id cmd_id)
- {
- struct sk_buff *skb;
- int ret;
- skb = ath6kl_wmi_get_new_buf(0);
- if (!skb)
- return -ENOMEM;
- ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, cmd_id, NO_SYNC_WMIFLAG);
- return ret;
- }
- static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len)
- {
- struct wmi_ready_event_2 *ev = (struct wmi_ready_event_2 *) datap;
- if (len < sizeof(struct wmi_ready_event_2))
- return -EINVAL;
- ath6kl_ready_event(wmi->parent_dev, ev->mac_addr,
- le32_to_cpu(ev->sw_version),
- le32_to_cpu(ev->abi_version), ev->phy_cap);
- return 0;
- }
- /*
- * Mechanism to modify the roaming behavior in the firmware. The lower rssi
- * at which the station has to roam can be passed with
- * WMI_SET_LRSSI_SCAN_PARAMS. Subtract 96 from RSSI to get the signal level
- * in dBm.
- */
- int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi)
- {
- struct sk_buff *skb;
- struct roam_ctrl_cmd *cmd;
- skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
- cmd = (struct roam_ctrl_cmd *) skb->data;
- cmd->info.params.lrssi_scan_period = cpu_to_le16(DEF_LRSSI_SCAN_PERIOD);
- cmd->info.params.lrssi_scan_threshold = a_cpu_to_sle16(lrssi +
- DEF_SCAN_FOR_ROAM_INTVL);
- cmd->info.params.lrssi_roam_threshold = a_cpu_to_sle16(lrssi);
- cmd->info.params.roam_rssi_floor = DEF_LRSSI_ROAM_FLOOR;
- cmd->roam_ctrl = WMI_SET_LRSSI_SCAN_PARAMS;
- return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID,
- NO_SYNC_WMIFLAG);
- }
- int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid)
- {
- struct sk_buff *skb;
- struct roam_ctrl_cmd *cmd;
- skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
- cmd = (struct roam_ctrl_cmd *) skb->data;
- memcpy(cmd->info.bssid, bssid, ETH_ALEN);
- cmd->roam_ctrl = WMI_FORCE_ROAM;
- ath6kl_dbg(ATH6KL_DBG_WMI, "force roam to %pM\n", bssid);
- return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID,
- NO_SYNC_WMIFLAG);
- }
- int ath6kl_wmi_ap_set_beacon_intvl_cmd(struct wmi *wmi, u8 if_idx,
- u32 beacon_intvl)
- {
- struct sk_buff *skb;
- struct set_beacon_int_cmd *cmd;
- skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
- cmd = (struct set_beacon_int_cmd *) skb->data;
- cmd->beacon_intvl = cpu_to_le32(beacon_intvl);
- return ath6kl_wmi_cmd_send(wmi, if_idx, skb,
- WMI_SET_BEACON_INT_CMDID, NO_SYNC_WMIFLAG);
- }
- int ath6kl_wmi_ap_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim_period)
- {
- struct sk_buff *skb;
- struct set_dtim_cmd *cmd;
- skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
- cmd = (struct set_dtim_cmd *) skb->data;
- cmd->dtim_period = cpu_to_le32(dtim_period);
- return ath6kl_wmi_cmd_send(wmi, if_idx, skb,
- WMI_AP_SET_DTIM_CMDID, NO_SYNC_WMIFLAG);
- }
- int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode)
- {
- struct sk_buff *skb;
- struct roam_ctrl_cmd *cmd;
- skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
- cmd = (struct roam_ctrl_cmd *) skb->data;
- cmd->info.roam_mode = mode;
- cmd->roam_ctrl = WMI_SET_ROAM_MODE;
- ath6kl_dbg(ATH6KL_DBG_WMI, "set roam mode %d\n", mode);
- return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_ROAM_CTRL_CMDID,
- NO_SYNC_WMIFLAG);
- }
- static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_connect_event *ev;
- u8 *pie, *peie;
- if (len < sizeof(struct wmi_connect_event))
- return -EINVAL;
- ev = (struct wmi_connect_event *) datap;
- if (vif->nw_type == AP_NETWORK) {
- /* AP mode start/STA connected event */
- struct net_device *dev = vif->ndev;
- if (memcmp(dev->dev_addr, ev->u.ap_bss.bssid, ETH_ALEN) == 0) {
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "%s: freq %d bssid %pM (AP started)\n",
- __func__, le16_to_cpu(ev->u.ap_bss.ch),
- ev->u.ap_bss.bssid);
- ath6kl_connect_ap_mode_bss(
- vif, le16_to_cpu(ev->u.ap_bss.ch));
- } else {
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "%s: aid %u mac_addr %pM auth=%u keymgmt=%u cipher=%u apsd_info=%u (STA connected)\n",
- __func__, ev->u.ap_sta.aid,
- ev->u.ap_sta.mac_addr,
- ev->u.ap_sta.auth,
- ev->u.ap_sta.keymgmt,
- le16_to_cpu(ev->u.ap_sta.cipher),
- ev->u.ap_sta.apsd_info);
- ath6kl_connect_ap_mode_sta(
- vif, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr,
- ev->u.ap_sta.keymgmt,
- le16_to_cpu(ev->u.ap_sta.cipher),
- ev->u.ap_sta.auth, ev->assoc_req_len,
- ev->assoc_info + ev->beacon_ie_len,
- ev->u.ap_sta.apsd_info);
- }
- return 0;
- }
- /* STA/IBSS mode connection event */
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "wmi event connect freq %d bssid %pM listen_intvl %d beacon_intvl %d type %d\n",
- le16_to_cpu(ev->u.sta.ch), ev->u.sta.bssid,
- le16_to_cpu(ev->u.sta.listen_intvl),
- le16_to_cpu(ev->u.sta.beacon_intvl),
- le32_to_cpu(ev->u.sta.nw_type));
- /* Start of assoc rsp IEs */
- pie = ev->assoc_info + ev->beacon_ie_len +
- ev->assoc_req_len + (sizeof(u16) * 3); /* capinfo, status, aid */
- /* End of assoc rsp IEs */
- peie = ev->assoc_info + ev->beacon_ie_len + ev->assoc_req_len +
- ev->assoc_resp_len;
- while (pie < peie) {
- switch (*pie) {
- case WLAN_EID_VENDOR_SPECIFIC:
- if (pie[1] > 3 && pie[2] == 0x00 && pie[3] == 0x50 &&
- pie[4] == 0xf2 && pie[5] == WMM_OUI_TYPE) {
- /* WMM OUT (00:50:F2) */
- if (pie[1] > 5 &&
- pie[6] == WMM_PARAM_OUI_SUBTYPE)
- wmi->is_wmm_enabled = true;
- }
- break;
- }
- if (wmi->is_wmm_enabled)
- break;
- pie += pie[1] + 2;
- }
- ath6kl_connect_event(vif, le16_to_cpu(ev->u.sta.ch),
- ev->u.sta.bssid,
- le16_to_cpu(ev->u.sta.listen_intvl),
- le16_to_cpu(ev->u.sta.beacon_intvl),
- le32_to_cpu(ev->u.sta.nw_type),
- ev->beacon_ie_len, ev->assoc_req_len,
- ev->assoc_resp_len, ev->assoc_info);
- return 0;
- }
- static struct country_code_to_enum_rd *
- ath6kl_regd_find_country(u16 countryCode)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
- if (allCountries[i].countryCode == countryCode)
- return &allCountries[i];
- }
- return NULL;
- }
- static struct reg_dmn_pair_mapping *
- ath6kl_get_regpair(u16 regdmn)
- {
- int i;
- if (regdmn == NO_ENUMRD)
- return NULL;
- for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
- if (regDomainPairs[i].reg_domain == regdmn)
- return ®DomainPairs[i];
- }
- return NULL;
- }
- static struct country_code_to_enum_rd *
- ath6kl_regd_find_country_by_rd(u16 regdmn)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
- if (allCountries[i].regDmnEnum == regdmn)
- return &allCountries[i];
- }
- return NULL;
- }
- static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
- {
- struct ath6kl_wmi_regdomain *ev;
- struct country_code_to_enum_rd *country = NULL;
- struct reg_dmn_pair_mapping *regpair = NULL;
- char alpha2[2];
- u32 reg_code;
- ev = (struct ath6kl_wmi_regdomain *) datap;
- reg_code = le32_to_cpu(ev->reg_code);
- if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG) {
- country = ath6kl_regd_find_country((u16) reg_code);
- } else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) {
- regpair = ath6kl_get_regpair((u16) reg_code);
- country = ath6kl_regd_find_country_by_rd((u16) reg_code);
- if (regpair)
- ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n",
- regpair->reg_domain);
- else
- ath6kl_warn("Regpair not found reg_code 0x%0x\n",
- reg_code);
- }
- if (country && wmi->parent_dev->wiphy_registered) {
- alpha2[0] = country->isoName[0];
- alpha2[1] = country->isoName[1];
- regulatory_hint(wmi->parent_dev->wiphy, alpha2);
- ath6kl_dbg(ATH6KL_DBG_WMI, "Country alpha2 being used: %c%c\n",
- alpha2[0], alpha2[1]);
- }
- }
- static int ath6kl_wmi_disconnect_event_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_disconnect_event *ev;
- wmi->traffic_class = 100;
- if (len < sizeof(struct wmi_disconnect_event))
- return -EINVAL;
- ev = (struct wmi_disconnect_event *) datap;
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "wmi event disconnect proto_reason %d bssid %pM wmi_reason %d assoc_resp_len %d\n",
- le16_to_cpu(ev->proto_reason_status), ev->bssid,
- ev->disconn_reason, ev->assoc_resp_len);
- wmi->is_wmm_enabled = false;
- ath6kl_disconnect_event(vif, ev->disconn_reason,
- ev->bssid, ev->assoc_resp_len, ev->assoc_info,
- le16_to_cpu(ev->proto_reason_status));
- return 0;
- }
- static int ath6kl_wmi_peer_node_event_rx(struct wmi *wmi, u8 *datap, int len)
- {
- struct wmi_peer_node_event *ev;
- if (len < sizeof(struct wmi_peer_node_event))
- return -EINVAL;
- ev = (struct wmi_peer_node_event *) datap;
- if (ev->event_code == PEER_NODE_JOIN_EVENT)
- ath6kl_dbg(ATH6KL_DBG_WMI, "joined node with mac addr: %pM\n",
- ev->peer_mac_addr);
- else if (ev->event_code == PEER_NODE_LEAVE_EVENT)
- ath6kl_dbg(ATH6KL_DBG_WMI, "left node with mac addr: %pM\n",
- ev->peer_mac_addr);
- return 0;
- }
- static int ath6kl_wmi_tkip_micerr_event_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_tkip_micerr_event *ev;
- if (len < sizeof(struct wmi_tkip_micerr_event))
- return -EINVAL;
- ev = (struct wmi_tkip_micerr_event *) datap;
- ath6kl_tkip_micerr_event(vif, ev->key_id, ev->is_mcast);
- return 0;
- }
- void ath6kl_wmi_sscan_timer(struct timer_list *t)
- {
- struct ath6kl_vif *vif = from_timer(vif, t, sched_scan_timer);
- cfg80211_sched_scan_results(vif->ar->wiphy, 0);
- }
- static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_bss_info_hdr2 *bih;
- u8 *buf;
- struct ieee80211_channel *channel;
- struct ath6kl *ar = wmi->parent_dev;
- struct cfg80211_bss *bss;
- if (len <= sizeof(struct wmi_bss_info_hdr2))
- return -EINVAL;
- bih = (struct wmi_bss_info_hdr2 *) datap;
- buf = datap + sizeof(struct wmi_bss_info_hdr2);
- len -= sizeof(struct wmi_bss_info_hdr2);
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "bss info evt - ch %u, snr %d, rssi %d, bssid \"%pM\" "
- "frame_type=%d\n",
- bih->ch, bih->snr, bih->snr - 95, bih->bssid,
- bih->frame_type);
- if (bih->frame_type != BEACON_FTYPE &&
- bih->frame_type != PROBERESP_FTYPE)
- return 0; /* Only update BSS table for now */
- if (bih->frame_type == BEACON_FTYPE &&
- test_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags)) {
- clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags);
- ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
- NONE_BSS_FILTER, 0);
- }
- channel = ieee80211_get_channel(ar->wiphy, le16_to_cpu(bih->ch));
- if (channel == NULL)
- return -EINVAL;
- if (len < 8 + 2 + 2)
- return -EINVAL;
- if (bih->frame_type == BEACON_FTYPE &&
- test_bit(CONNECTED, &vif->flags) &&
- memcmp(bih->bssid, vif->bssid, ETH_ALEN) == 0) {
- const u8 *tim;
- tim = cfg80211_find_ie(WLAN_EID_TIM, buf + 8 + 2 + 2,
- len - 8 - 2 - 2);
- if (tim && tim[1] >= 2) {
- vif->assoc_bss_dtim_period = tim[3];
- set_bit(DTIM_PERIOD_AVAIL, &vif->flags);
- }
- }
- bss = cfg80211_inform_bss(ar->wiphy, channel,
- bih->frame_type == BEACON_FTYPE ?
- CFG80211_BSS_FTYPE_BEACON :
- CFG80211_BSS_FTYPE_PRESP,
- bih->bssid, get_unaligned_le64((__le64 *)buf),
- get_unaligned_le16(((__le16 *)buf) + 5),
- get_unaligned_le16(((__le16 *)buf) + 4),
- buf + 8 + 2 + 2, len - 8 - 2 - 2,
- (bih->snr - 95) * 100, GFP_ATOMIC);
- if (bss == NULL)
- return -ENOMEM;
- cfg80211_put_bss(ar->wiphy, bss);
- /*
- * Firmware doesn't return any event when scheduled scan has
- * finished, so we need to use a timer to find out when there are
- * no more results.
- *
- * The timer is started from the first bss info received, otherwise
- * the timer would not ever fire if the scan interval is short
- * enough.
- */
- if (test_bit(SCHED_SCANNING, &vif->flags) &&
- !timer_pending(&vif->sched_scan_timer)) {
- mod_timer(&vif->sched_scan_timer, jiffies +
- msecs_to_jiffies(ATH6KL_SCHED_SCAN_RESULT_DELAY));
- }
- return 0;
- }
- /* Inactivity timeout of a fatpipe(pstream) at the target */
- static int ath6kl_wmi_pstream_timeout_event_rx(struct wmi *wmi, u8 *datap,
- int len)
- {
- struct wmi_pstream_timeout_event *ev;
- if (len < sizeof(struct wmi_pstream_timeout_event))
- return -EINVAL;
- ev = (struct wmi_pstream_timeout_event *) datap;
- if (ev->traffic_class >= WMM_NUM_AC) {
- ath6kl_err("invalid traffic class: %d\n", ev->traffic_class);
- return -EINVAL;
- }
- /*
- * When the pstream (fat pipe == AC) timesout, it means there were
- * no thinStreams within this pstream & it got implicitly created
- * due to data flow on this AC. We start the inactivity timer only
- * for implicitly created pstream. Just reset the host state.
- */
- spin_lock_bh(&wmi->lock);
- wmi->stream_exist_for_ac[ev->traffic_class] = 0;
- wmi->fat_pipe_exist &= ~(1 << ev->traffic_class);
- spin_unlock_bh(&wmi->lock);
- /* Indicate inactivity to driver layer for this fatpipe (pstream) */
- ath6kl_indicate_tx_activity(wmi->parent_dev, ev->traffic_class, false);
- return 0;
- }
- static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len)
- {
- struct wmi_bit_rate_reply *reply;
- s32 rate;
- u32 sgi, index;
- if (len < sizeof(struct wmi_bit_rate_reply))
- return -EINVAL;
- reply = (struct wmi_bit_rate_reply *) datap;
- ath6kl_dbg(ATH6KL_DBG_WMI, "rateindex %d\n", reply->rate_index);
- if (reply->rate_index == (s8) RATE_AUTO) {
- rate = RATE_AUTO;
- } else {
- index = reply->rate_index & 0x7f;
- if (WARN_ON_ONCE(index > (RATE_MCS_7_40 + 1)))
- return -EINVAL;
- sgi = (reply->rate_index & 0x80) ? 1 : 0;
- rate = wmi_rate_tbl[index][sgi];
- }
- ath6kl_wakeup_event(wmi->parent_dev);
- return 0;
- }
- static int ath6kl_wmi_test_rx(struct wmi *wmi, u8 *datap, int len)
- {
- ath6kl_tm_rx_event(wmi->parent_dev, datap, len);
- return 0;
- }
- static int ath6kl_wmi_ratemask_reply_rx(struct wmi *wmi, u8 *datap, int len)
- {
- if (len < sizeof(struct wmi_fix_rates_reply))
- return -EINVAL;
- ath6kl_wakeup_event(wmi->parent_dev);
- return 0;
- }
- static int ath6kl_wmi_ch_list_reply_rx(struct wmi *wmi, u8 *datap, int len)
- {
- if (len < sizeof(struct wmi_channel_list_reply))
- return -EINVAL;
- ath6kl_wakeup_event(wmi->parent_dev);
- return 0;
- }
- static int ath6kl_wmi_tx_pwr_reply_rx(struct wmi *wmi, u8 *datap, int len)
- {
- struct wmi_tx_pwr_reply *reply;
- if (len < sizeof(struct wmi_tx_pwr_reply))
- return -EINVAL;
- reply = (struct wmi_tx_pwr_reply *) datap;
- ath6kl_txpwr_rx_evt(wmi->parent_dev, reply->dbM);
- return 0;
- }
- static int ath6kl_wmi_keepalive_reply_rx(struct wmi *wmi, u8 *datap, int len)
- {
- if (len < sizeof(struct wmi_get_keepalive_cmd))
- return -EINVAL;
- ath6kl_wakeup_event(wmi->parent_dev);
- return 0;
- }
- static int ath6kl_wmi_scan_complete_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_scan_complete_event *ev;
- ev = (struct wmi_scan_complete_event *) datap;
- ath6kl_scan_complete_evt(vif, a_sle32_to_cpu(ev->status));
- wmi->is_probe_ssid = false;
- return 0;
- }
- static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap,
- int len, struct ath6kl_vif *vif)
- {
- struct wmi_neighbor_report_event *ev;
- u8 i;
- if (len < sizeof(*ev))
- return -EINVAL;
- ev = (struct wmi_neighbor_report_event *) datap;
- if (struct_size(ev, neighbor, ev->num_neighbors) > len) {
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "truncated neighbor event (num=%d len=%d)\n",
- ev->num_neighbors, len);
- return -EINVAL;
- }
- for (i = 0; i < ev->num_neighbors; i++) {
- ath6kl_dbg(ATH6KL_DBG_WMI, "neighbor %d/%d - %pM 0x%x\n",
- i + 1, ev->num_neighbors, ev->neighbor[i].bssid,
- ev->neighbor[i].bss_flags);
- cfg80211_pmksa_candidate_notify(vif->ndev, i,
- ev->neighbor[i].bssid,
- !!(ev->neighbor[i].bss_flags &
- WMI_PREAUTH_CAPABLE_BSS),
- GFP_ATOMIC);
- }
- return 0;
- }
- /*
- * Target is reporting a programming error. This is for
- * developer aid only. Target only checks a few common violations
- * and it is responsibility of host to do all error checking.
- * Behavior of target after wmi error event is undefined.
- * A reset is recommended.
- */
- static int ath6kl_wmi_error_event_rx(struct wmi *wmi, u8 *datap, int len)
- {
- const char *type = "unknown error";
- struct wmi_cmd_error_event *ev;
- ev = (struct wmi_cmd_error_event *) datap;
- switch (ev->err_code) {
- case INVALID_PARAM:
- type = "invalid parameter";
- break;
- case ILLEGAL_STATE:
- type = "invalid state";
- break;
- case INTERNAL_ERROR:
- type = "internal error";
- break;
- }
- ath6kl_dbg(ATH6KL_DBG_WMI, "programming error, cmd=%d %s\n",
- ev->cmd_id, type);
- return 0;
- }
- static int ath6kl_wmi_stats_event_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- ath6kl_tgt_stats_event(vif, datap, len);
- return 0;
- }
- static u8 ath6kl_wmi_get_upper_threshold(s16 rssi,
- struct sq_threshold_params *sq_thresh,
- u32 size)
- {
- u32 index;
- u8 threshold = (u8) sq_thresh->upper_threshold[size - 1];
- /* The list is already in sorted order. Get the next lower value */
- for (index = 0; index < size; index++) {
- if (rssi < sq_thresh->upper_threshold[index]) {
- threshold = (u8) sq_thresh->upper_threshold[index];
- break;
- }
- }
- return threshold;
- }
- static u8 ath6kl_wmi_get_lower_threshold(s16 rssi,
- struct sq_threshold_params *sq_thresh,
- u32 size)
- {
- u32 index;
- u8 threshold = (u8) sq_thresh->lower_threshold[size - 1];
- /* The list is already in sorted order. Get the next lower value */
- for (index = 0; index < size; index++) {
- if (rssi > sq_thresh->lower_threshold[index]) {
- threshold = (u8) sq_thresh->lower_threshold[index];
- break;
- }
- }
- return threshold;
- }
- static int ath6kl_wmi_send_rssi_threshold_params(struct wmi *wmi,
- struct wmi_rssi_threshold_params_cmd *rssi_cmd)
- {
- struct sk_buff *skb;
- struct wmi_rssi_threshold_params_cmd *cmd;
- skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
- cmd = (struct wmi_rssi_threshold_params_cmd *) skb->data;
- memcpy(cmd, rssi_cmd, sizeof(struct wmi_rssi_threshold_params_cmd));
- return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_RSSI_THRESHOLD_PARAMS_CMDID,
- NO_SYNC_WMIFLAG);
- }
- static int ath6kl_wmi_rssi_threshold_event_rx(struct wmi *wmi, u8 *datap,
- int len)
- {
- struct wmi_rssi_threshold_event *reply;
- struct wmi_rssi_threshold_params_cmd cmd;
- struct sq_threshold_params *sq_thresh;
- enum wmi_rssi_threshold_val new_threshold;
- u8 upper_rssi_threshold, lower_rssi_threshold;
- s16 rssi;
- int ret;
- if (len < sizeof(struct wmi_rssi_threshold_event))
- return -EINVAL;
- reply = (struct wmi_rssi_threshold_event *) datap;
- new_threshold = (enum wmi_rssi_threshold_val) reply->range;
- rssi = a_sle16_to_cpu(reply->rssi);
- sq_thresh = &wmi->sq_threshld[SIGNAL_QUALITY_METRICS_RSSI];
- /*
- * Identify the threshold breached and communicate that to the app.
- * After that install a new set of thresholds based on the signal
- * quality reported by the target
- */
- if (new_threshold) {
- /* Upper threshold breached */
- if (rssi < sq_thresh->upper_threshold[0]) {
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "spurious upper rssi threshold event: %d\n",
- rssi);
- } else if ((rssi < sq_thresh->upper_threshold[1]) &&
- (rssi >= sq_thresh->upper_threshold[0])) {
- new_threshold = WMI_RSSI_THRESHOLD1_ABOVE;
- } else if ((rssi < sq_thresh->upper_threshold[2]) &&
- (rssi >= sq_thresh->upper_threshold[1])) {
- new_threshold = WMI_RSSI_THRESHOLD2_ABOVE;
- } else if ((rssi < sq_thresh->upper_threshold[3]) &&
- (rssi >= sq_thresh->upper_threshold[2])) {
- new_threshold = WMI_RSSI_THRESHOLD3_ABOVE;
- } else if ((rssi < sq_thresh->upper_threshold[4]) &&
- (rssi >= sq_thresh->upper_threshold[3])) {
- new_threshold = WMI_RSSI_THRESHOLD4_ABOVE;
- } else if ((rssi < sq_thresh->upper_threshold[5]) &&
- (rssi >= sq_thresh->upper_threshold[4])) {
- new_threshold = WMI_RSSI_THRESHOLD5_ABOVE;
- } else if (rssi >= sq_thresh->upper_threshold[5]) {
- new_threshold = WMI_RSSI_THRESHOLD6_ABOVE;
- }
- } else {
- /* Lower threshold breached */
- if (rssi > sq_thresh->lower_threshold[0]) {
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "spurious lower rssi threshold event: %d %d\n",
- rssi, sq_thresh->lower_threshold[0]);
- } else if ((rssi > sq_thresh->lower_threshold[1]) &&
- (rssi <= sq_thresh->lower_threshold[0])) {
- new_threshold = WMI_RSSI_THRESHOLD6_BELOW;
- } else if ((rssi > sq_thresh->lower_threshold[2]) &&
- (rssi <= sq_thresh->lower_threshold[1])) {
- new_threshold = WMI_RSSI_THRESHOLD5_BELOW;
- } else if ((rssi > sq_thresh->lower_threshold[3]) &&
- (rssi <= sq_thresh->lower_threshold[2])) {
- new_threshold = WMI_RSSI_THRESHOLD4_BELOW;
- } else if ((rssi > sq_thresh->lower_threshold[4]) &&
- (rssi <= sq_thresh->lower_threshold[3])) {
- new_threshold = WMI_RSSI_THRESHOLD3_BELOW;
- } else if ((rssi > sq_thresh->lower_threshold[5]) &&
- (rssi <= sq_thresh->lower_threshold[4])) {
- new_threshold = WMI_RSSI_THRESHOLD2_BELOW;
- } else if (rssi <= sq_thresh->lower_threshold[5]) {
- new_threshold = WMI_RSSI_THRESHOLD1_BELOW;
- }
- }
- /* Calculate and install the next set of thresholds */
- lower_rssi_threshold = ath6kl_wmi_get_lower_threshold(rssi, sq_thresh,
- sq_thresh->lower_threshold_valid_count);
- upper_rssi_threshold = ath6kl_wmi_get_upper_threshold(rssi, sq_thresh,
- sq_thresh->upper_threshold_valid_count);
- /* Issue a wmi command to install the thresholds */
- cmd.thresh_above1_val = a_cpu_to_sle16(upper_rssi_threshold);
- cmd.thresh_below1_val = a_cpu_to_sle16(lower_rssi_threshold);
- cmd.weight = sq_thresh->weight;
- cmd.poll_time = cpu_to_le32(sq_thresh->polling_interval);
- ret = ath6kl_wmi_send_rssi_threshold_params(wmi, &cmd);
- if (ret) {
- ath6kl_err("unable to configure rssi thresholds\n");
- return -EIO;
- }
- return 0;
- }
- static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_cac_event *reply;
- struct ieee80211_tspec_ie *ts;
- u16 active_tsids, tsinfo;
- u8 tsid, index;
- u8 ts_id;
- if (len < sizeof(struct wmi_cac_event))
- return -EINVAL;
- reply = (struct wmi_cac_event *) datap;
- if (reply->ac >= WMM_NUM_AC) {
- ath6kl_err("invalid AC: %d\n", reply->ac);
- return -EINVAL;
- }
- if ((reply->cac_indication == CAC_INDICATION_ADMISSION_RESP) &&
- (reply->status_code != IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED)) {
- ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion);
- tsinfo = le16_to_cpu(ts->tsinfo);
- tsid = (tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) &
- IEEE80211_WMM_IE_TSPEC_TID_MASK;
- ath6kl_wmi_delete_pstream_cmd(wmi, vif->fw_vif_idx,
- reply->ac, tsid);
- } else if (reply->cac_indication == CAC_INDICATION_NO_RESP) {
- /*
- * Following assumes that there is only one outstanding
- * ADDTS request when this event is received
- */
- spin_lock_bh(&wmi->lock);
- active_tsids = wmi->stream_exist_for_ac[reply->ac];
- spin_unlock_bh(&wmi->lock);
- for (index = 0; index < sizeof(active_tsids) * 8; index++) {
- if ((active_tsids >> index) & 1)
- break;
- }
- if (index < (sizeof(active_tsids) * 8))
- ath6kl_wmi_delete_pstream_cmd(wmi, vif->fw_vif_idx,
- reply->ac, index);
- }
- /*
- * Clear active tsids and Add missing handling
- * for delete qos stream from AP
- */
- else if (reply->cac_indication == CAC_INDICATION_DELETE) {
- ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion);
- tsinfo = le16_to_cpu(ts->tsinfo);
- ts_id = ((tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) &
- IEEE80211_WMM_IE_TSPEC_TID_MASK);
- spin_lock_bh(&wmi->lock);
- wmi->stream_exist_for_ac[reply->ac] &= ~(1 << ts_id);
- active_tsids = wmi->stream_exist_for_ac[reply->ac];
- spin_unlock_bh(&wmi->lock);
- /* Indicate stream inactivity to driver layer only if all tsids
- * within this AC are deleted.
- */
- if (!active_tsids) {
- ath6kl_indicate_tx_activity(wmi->parent_dev, reply->ac,
- false);
- wmi->fat_pipe_exist &= ~(1 << reply->ac);
- }
- }
- return 0;
- }
- static int ath6kl_wmi_txe_notify_event_rx(struct wmi *wmi, u8 *datap, int len,
- struct ath6kl_vif *vif)
- {
- struct wmi_txe_notify_event *ev;
- u32 rate, pkts;
- if (len < sizeof(*ev))
- return -EINVAL;
- if (vif->nw_type != INFRA_NETWORK ||
- !test_bit(ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY,
- vif->ar->fw_capabilities))
- return -EOPNOTSUPP;
- if (vif->sme_state != SME_CONNECTED)
- return -ENOTCONN;
- ev = (struct wmi_txe_notify_event *) datap;
- rate = le32_to_cpu(ev->rate);
- pkts = le32_to_cpu(ev->pkts);
- ath6kl_dbg(ATH6KL_DBG_WMI, "TXE notify event: peer %pM rate %d%% pkts %d intvl %ds\n",
- vif->bssid, rate, pkts, vif->txe_intvl);
- cfg80211_cqm_txe_notify(vif->ndev, vif->bssid, pkts,
- rate, vif->txe_intvl, GFP_KERNEL);
- return 0;
- }
- int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx,
- u32 rate, u32 pkts, u32 intvl)
- {
- struct sk_buff *skb;
- struct wmi_txe_notify_cmd *cmd;
- skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
- cmd = (struct wmi_txe_notify_cmd *) skb->data;
- cmd->rate = cpu_to_le32(rate);
- cmd->pkts = cpu_to_le32(pkts);
- cmd->intvl = cpu_to_le32(intvl);
- return ath6kl_wmi_cmd_send(wmi, idx, skb, WMI_SET_TXE_NOTIFY_CMDID,
- NO_SYNC_WMIFLAG);
- }
- int ath6kl_wmi_set_rssi_filter_cmd(struct wmi *wmi, u8 if_idx, s8 rssi)
- {
- struct sk_buff *skb;
- struct wmi_set_rssi_filter_cmd *cmd;
- int ret;
- skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
- cmd = (struct wmi_set_rssi_filter_cmd *) skb->data;
- cmd->rssi = rssi;
- ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RSSI_FILTER_CMDID,
- NO_SYNC_WMIFLAG);
- return ret;
- }
- static int ath6kl_wmi_send_snr_threshold_params(struct wmi *wmi,
- struct wmi_snr_threshold_params_cmd *snr_cmd)
- {
- struct sk_buff *skb;
- struct wmi_snr_threshold_params_cmd *cmd;
- skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
- cmd = (struct wmi_snr_threshold_params_cmd *) skb->data;
- memcpy(cmd, snr_cmd, sizeof(struct wmi_snr_threshold_params_cmd));
- return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SNR_THRESHOLD_PARAMS_CMDID,
- NO_SYNC_WMIFLAG);
- }
- static int ath6kl_wmi_snr_threshold_event_rx(struct wmi *wmi, u8 *datap,
- int len)
- {
- struct wmi_snr_threshold_event *reply;
- struct sq_threshold_params *sq_thresh;
- struct wmi_snr_threshold_params_cmd cmd;
- enum wmi_snr_threshold_val new_threshold;
- u8 upper_snr_threshold, lower_snr_threshold;
- s16 snr;
- int ret;
- if (len < sizeof(struct wmi_snr_threshold_event))
- return -EINVAL;
- reply = (struct wmi_snr_threshold_event *) datap;
- new_threshold = (enum wmi_snr_threshold_val) reply->range;
- snr = reply->snr;
- sq_thresh = &wmi->sq_threshld[SIGNAL_QUALITY_METRICS_SNR];
- /*
- * Identify the threshold breached and communicate that to the app.
- * After that install a new set of thresholds based on the signal
- * quality reported by the target.
- */
- if (new_threshold) {
- /* Upper threshold breached */
- if (snr < sq_thresh->upper_threshold[0]) {
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "spurious upper snr threshold event: %d\n",
- snr);
- } else if ((snr < sq_thresh->upper_threshold[1]) &&
- (snr >= sq_thresh->upper_threshold[0])) {
- new_threshold = WMI_SNR_THRESHOLD1_ABOVE;
- } else if ((snr < sq_thresh->upper_threshold[2]) &&
- (snr >= sq_thresh->upper_threshold[1])) {
- new_threshold = WMI_SNR_THRESHOLD2_ABOVE;
- } else if ((snr < sq_thresh->upper_threshold[3]) &&
- (snr >= sq_thresh->upper_threshold[2])) {
- new_threshold = WMI_SNR_THRESHOLD3_ABOVE;
- } else if (snr >= sq_thresh->upper_threshold[3]) {
- new_threshold = WMI_SNR_THRESHOLD4_ABOVE;
- }
- } else {
- /* Lower threshold breached */
- if (snr > sq_thresh->lower_threshold[0]) {
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "spurious lower snr threshold event: %d\n",
- sq_thresh->lower_threshold[0]);
- } else if ((snr > sq_thresh->lower_threshold[1]) &&
- (snr <= sq_thresh->lower_threshold[0])) {
- new_threshold = WMI_SNR_THRESHOLD4_BELOW;
- } else if ((snr > sq_thresh->lower_threshold[2]) &&
- (snr <= sq_thresh->lower_threshold[1])) {
- new_threshold = WMI_SNR_THRESHOLD3_BELOW;
- } else if ((snr > sq_thresh->lower_threshold[3]) &&
- (snr <= sq_thresh->lower_threshold[2])) {
- new_threshold = WMI_SNR_THRESHOLD2_BELOW;
- } else if (snr <= sq_thresh->lower_threshold[3]) {
- new_threshold = WMI_SNR_THRESHOLD1_BELOW;
- }
- }
- /* Calculate and install the next set of thresholds */
- lower_snr_threshold = ath6kl_wmi_get_lower_threshold(snr, sq_thresh,
- sq_thresh->lower_threshold_valid_count);
- upper_snr_threshold = ath6kl_wmi_get_upper_threshold(snr, sq_thresh,
- sq_thresh->upper_threshold_valid_count);
- /* Issue a wmi command to install the thresholds */
- cmd.thresh_above1_val = upper_snr_threshold;
- cmd.thresh_below1_val = lower_snr_threshold;
- cmd.weight = sq_thresh->weight;
- cmd.poll_time = cpu_to_le32(sq_thresh->polling_interval);
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "snr: %d, threshold: %d, lower: %d, upper: %d\n",
- snr, new_threshold,
- lower_snr_threshold, upper_snr_threshold);
- ret = ath6kl_wmi_send_snr_threshold_params(wmi, &cmd);
- if (ret) {
- ath6kl_err("unable to configure snr threshold\n");
- return -EIO;
- }
- return 0;
- }
- static int ath6kl_wmi_aplist_event_rx(struct wmi *wmi, u8 *datap, int len)
- {
- u16 ap_info_entry_size;
- struct wmi_aplist_event *ev = (struct wmi_aplist_event *) datap;
- struct wmi_ap_info_v1 *ap_info_v1;
- u8 index;
- if (len < sizeof(struct wmi_aplist_event) ||
- ev->ap_list_ver != APLIST_VER1)
- return -EINVAL;
- ap_info_entry_size = sizeof(struct wmi_ap_info_v1);
- ap_info_v1 = (struct wmi_ap_info_v1 *) ev->ap_list;
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "number of APs in aplist event: %d\n", ev->num_ap);
- if (len < (int) (sizeof(struct wmi_aplist_event) +
- (ev->num_ap - 1) * ap_info_entry_size))
- return -EINVAL;
- /* AP list version 1 contents */
- for (index = 0; index < ev->num_ap; index++) {
- ath6kl_dbg(ATH6KL_DBG_WMI, "AP#%d BSSID %pM Channel %d\n",
- index, ap_info_v1->bssid, ap_info_v1->channel);
- ap_info_v1++;
- }
- return 0;
- }
- int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, struct sk_buff *skb,
- enum wmi_cmd_id cmd_id, enum wmi_sync_flag sync_flag)
- {
- struct wmi_cmd_hdr *cmd_hdr;
- enum htc_endpoint_id ep_id = wmi->ep_id;
- int ret;
- u16 info1;
- if (WARN_ON(skb == NULL ||
- (if_idx > (wmi->parent_dev->vif_max - 1)))) {
- dev_kfree_skb(skb);
- return -EINVAL;
- }
- ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n",
- cmd_id, skb->len, sync_flag);
- ath6kl_dbg_dump(ATH6KL_DBG_WMI_DUMP, NULL, "wmi tx ",
- skb->data, skb->len);
- if (sync_flag >= END_WMIFLAG) {
- dev_kfree_skb(skb);
- return -EINVAL;
- }
- if ((sync_flag == SYNC_BEFORE_WMIFLAG) ||
- (sync_flag == SYNC_BOTH_WMIFLAG)) {
- /*
- * Make sure all data currently queued is transmitted before
- * the cmd execution. Establish a new sync point.
- */
- ath6kl_wmi_sync_point(wmi, if_idx);
- }
- skb_push(skb, sizeof(struct wmi_cmd_hdr));
- cmd_hdr = (struct wmi_cmd_hdr *) skb->data;
- cmd_hdr->cmd_id = cpu_to_le16(cmd_id);
- info1 = if_idx & WMI_CMD_HDR_IF_ID_MASK;
- cmd_hdr->info1 = cpu_to_le16(info1);
- /* Only for OPT_TX_CMD, use BE endpoint. */
- if (cmd_id == WMI_OPT_TX_FRAME_CMDID) {
- ret = ath6kl_wmi_data_hdr_add(wmi, skb, OPT_MSGTYPE,
- false, false, 0, NULL, if_idx);
- if (ret) {
- dev_kfree_skb(skb);
- return ret;
- }
- ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev, WMM_AC_BE);
- }
- ath6kl_control_tx(wmi->parent_dev, skb, ep_id);
- if ((sync_flag == SYNC_AFTER_WMIFLAG) ||
- (sync_flag == SYNC_BOTH_WMIFLAG)) {
- /*
- * Make sure all new data queued waits for the command to
- * execute. Establish a new sync point.
- */
- ath6kl_wmi_sync_point(wmi, if_idx);
- }
- return 0;
- }
- int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx,
- enum network_type nw_type,
- enum dot11_auth_mode dot11_auth_mode,
- enum auth_mode auth_mode,
- enum ath6kl_crypto_type pairwise_crypto,
- u8 pairwise_crypto_len,
- enum ath6kl_crypto_type group_crypto,
- u8 group_crypto_len, int ssid_len, u8 *ssid,
- u8 *bssid, u16 channel, u32 ctrl_flags,
- u8 nw_subtype)
- {
- struct sk_buff *skb;
- struct wmi_connect_cmd *cc;
- int ret;
- ath6kl_dbg(ATH6KL_DBG_WMI,
- "wmi connect bssid %pM freq %d flags 0x%x ssid_len %d "
- "type %d dot11_auth %d auth %d pairwise %d group %d\n",
- bssid, channel, ctrl_…