1*ff233cb5SSergey Matyukevich // SPDX-License-Identifier: GPL-2.0+ 2*ff233cb5SSergey Matyukevich /* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */ 398f44cb0SIgor Mitsyanko 498f44cb0SIgor Mitsyanko #include <linux/types.h> 598f44cb0SIgor Mitsyanko #include <linux/skbuff.h> 698f44cb0SIgor Mitsyanko 798f44cb0SIgor Mitsyanko #include "cfg80211.h" 898f44cb0SIgor Mitsyanko #include "core.h" 998f44cb0SIgor Mitsyanko #include "qlink.h" 1098f44cb0SIgor Mitsyanko #include "qlink_util.h" 1198f44cb0SIgor Mitsyanko #include "bus.h" 1298f44cb0SIgor Mitsyanko #include "commands.h" 1398f44cb0SIgor Mitsyanko 1498f44cb0SIgor Mitsyanko static int qtnf_cmd_check_reply_header(const struct qlink_resp *resp, 1598f44cb0SIgor Mitsyanko u16 cmd_id, u8 mac_id, u8 vif_id, 1698f44cb0SIgor Mitsyanko size_t resp_size) 1798f44cb0SIgor Mitsyanko { 1898f44cb0SIgor Mitsyanko if (unlikely(le16_to_cpu(resp->cmd_id) != cmd_id)) { 1998f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad cmd_id in response: 0x%.4X\n", 2098f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, le16_to_cpu(resp->cmd_id)); 2198f44cb0SIgor Mitsyanko return -EINVAL; 2298f44cb0SIgor Mitsyanko } 2398f44cb0SIgor Mitsyanko 2498f44cb0SIgor Mitsyanko if (unlikely(resp->macid != mac_id)) { 2598f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad MAC in response: %u\n", 2698f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, resp->macid); 2798f44cb0SIgor Mitsyanko return -EINVAL; 2898f44cb0SIgor Mitsyanko } 2998f44cb0SIgor Mitsyanko 3098f44cb0SIgor Mitsyanko if (unlikely(resp->vifid != vif_id)) { 3198f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad VIF in response: %u\n", 3298f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, resp->vifid); 3398f44cb0SIgor Mitsyanko return -EINVAL; 3498f44cb0SIgor Mitsyanko } 3598f44cb0SIgor Mitsyanko 3698f44cb0SIgor Mitsyanko if (unlikely(le16_to_cpu(resp->mhdr.len) < resp_size)) { 3798f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u CMD%x: bad response size %u < %zu\n", 3898f44cb0SIgor Mitsyanko mac_id, vif_id, cmd_id, 3998f44cb0SIgor Mitsyanko le16_to_cpu(resp->mhdr.len), resp_size); 4098f44cb0SIgor Mitsyanko return -ENOSPC; 4198f44cb0SIgor Mitsyanko } 4298f44cb0SIgor Mitsyanko 4398f44cb0SIgor Mitsyanko return 0; 4498f44cb0SIgor Mitsyanko } 4598f44cb0SIgor Mitsyanko 4636e8c538SIgor Mitsyanko static int qtnf_cmd_resp_result_decode(enum qlink_cmd_result qcode) 4736e8c538SIgor Mitsyanko { 4836e8c538SIgor Mitsyanko switch (qcode) { 4936e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_OK: 5036e8c538SIgor Mitsyanko return 0; 5136e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_INVALID: 5236e8c538SIgor Mitsyanko return -EINVAL; 5336e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_ENOTSUPP: 5436e8c538SIgor Mitsyanko return -ENOTSUPP; 5536e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_ENOTFOUND: 5636e8c538SIgor Mitsyanko return -ENOENT; 5736e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_EALREADY: 5836e8c538SIgor Mitsyanko return -EALREADY; 5936e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_EADDRINUSE: 6036e8c538SIgor Mitsyanko return -EADDRINUSE; 6136e8c538SIgor Mitsyanko case QLINK_CMD_RESULT_EADDRNOTAVAIL: 6236e8c538SIgor Mitsyanko return -EADDRNOTAVAIL; 63126824f5SAndrey Shevchenko case QLINK_CMD_RESULT_EBUSY: 64126824f5SAndrey Shevchenko return -EBUSY; 6536e8c538SIgor Mitsyanko default: 6636e8c538SIgor Mitsyanko return -EFAULT; 6736e8c538SIgor Mitsyanko } 6836e8c538SIgor Mitsyanko } 6936e8c538SIgor Mitsyanko 7098f44cb0SIgor Mitsyanko static int qtnf_cmd_send_with_reply(struct qtnf_bus *bus, 7198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, 7298f44cb0SIgor Mitsyanko struct sk_buff **response_skb, 7398f44cb0SIgor Mitsyanko size_t const_resp_size, 7498f44cb0SIgor Mitsyanko size_t *var_resp_size) 7598f44cb0SIgor Mitsyanko { 7698f44cb0SIgor Mitsyanko struct qlink_cmd *cmd; 771066bd19SSergey Matyukevich struct qlink_resp *resp = NULL; 7898f44cb0SIgor Mitsyanko struct sk_buff *resp_skb = NULL; 7998f44cb0SIgor Mitsyanko u16 cmd_id; 80c6ed298fSSergey Matyukevich u8 mac_id; 81c6ed298fSSergey Matyukevich u8 vif_id; 8298f44cb0SIgor Mitsyanko int ret; 8398f44cb0SIgor Mitsyanko 8498f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd *)cmd_skb->data; 8598f44cb0SIgor Mitsyanko cmd_id = le16_to_cpu(cmd->cmd_id); 8698f44cb0SIgor Mitsyanko mac_id = cmd->macid; 8798f44cb0SIgor Mitsyanko vif_id = cmd->vifid; 8898f44cb0SIgor Mitsyanko cmd->mhdr.len = cpu_to_le16(cmd_skb->len); 8998f44cb0SIgor Mitsyanko 90c6ed298fSSergey Matyukevich pr_debug("VIF%u.%u cmd=0x%.4X\n", mac_id, vif_id, 91c6ed298fSSergey Matyukevich le16_to_cpu(cmd->cmd_id)); 92c6ed298fSSergey Matyukevich 93c6ed298fSSergey Matyukevich if (bus->fw_state != QTNF_FW_STATE_ACTIVE && 94c6ed298fSSergey Matyukevich le16_to_cpu(cmd->cmd_id) != QLINK_CMD_FW_INIT) { 9598f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: drop cmd 0x%.4X in fw state %d\n", 9698f44cb0SIgor Mitsyanko mac_id, vif_id, le16_to_cpu(cmd->cmd_id), 9798f44cb0SIgor Mitsyanko bus->fw_state); 98b60769e2SDmitry Lebed dev_kfree_skb(cmd_skb); 9998f44cb0SIgor Mitsyanko return -ENODEV; 10098f44cb0SIgor Mitsyanko } 10198f44cb0SIgor Mitsyanko 10298f44cb0SIgor Mitsyanko ret = qtnf_trans_send_cmd_with_resp(bus, cmd_skb, &resp_skb); 103c6ed298fSSergey Matyukevich if (ret) 10498f44cb0SIgor Mitsyanko goto out; 10598f44cb0SIgor Mitsyanko 1061066bd19SSergey Matyukevich if (WARN_ON(!resp_skb || !resp_skb->data)) { 1071066bd19SSergey Matyukevich ret = -EFAULT; 1081066bd19SSergey Matyukevich goto out; 1091066bd19SSergey Matyukevich } 1101066bd19SSergey Matyukevich 1111066bd19SSergey Matyukevich resp = (struct qlink_resp *)resp_skb->data; 11298f44cb0SIgor Mitsyanko ret = qtnf_cmd_check_reply_header(resp, cmd_id, mac_id, vif_id, 11398f44cb0SIgor Mitsyanko const_resp_size); 114c6ed298fSSergey Matyukevich if (ret) 11598f44cb0SIgor Mitsyanko goto out; 11698f44cb0SIgor Mitsyanko 11798f44cb0SIgor Mitsyanko /* Return length of variable part of response */ 11898f44cb0SIgor Mitsyanko if (response_skb && var_resp_size) 11998f44cb0SIgor Mitsyanko *var_resp_size = le16_to_cpu(resp->mhdr.len) - const_resp_size; 12098f44cb0SIgor Mitsyanko 12198f44cb0SIgor Mitsyanko out: 12298f44cb0SIgor Mitsyanko if (response_skb) 12398f44cb0SIgor Mitsyanko *response_skb = resp_skb; 12498f44cb0SIgor Mitsyanko else 12598f44cb0SIgor Mitsyanko consume_skb(resp_skb); 12698f44cb0SIgor Mitsyanko 127c6ed298fSSergey Matyukevich if (!ret && resp) 128c6ed298fSSergey Matyukevich return qtnf_cmd_resp_result_decode(le16_to_cpu(resp->result)); 129c6ed298fSSergey Matyukevich 130c6ed298fSSergey Matyukevich pr_warn("VIF%u.%u: cmd 0x%.4X failed: %d\n", 131c6ed298fSSergey Matyukevich mac_id, vif_id, le16_to_cpu(cmd->cmd_id), ret); 132c6ed298fSSergey Matyukevich 13398f44cb0SIgor Mitsyanko return ret; 13498f44cb0SIgor Mitsyanko } 13598f44cb0SIgor Mitsyanko 136c6ed298fSSergey Matyukevich static inline int qtnf_cmd_send(struct qtnf_bus *bus, struct sk_buff *cmd_skb) 13798f44cb0SIgor Mitsyanko { 138c6ed298fSSergey Matyukevich return qtnf_cmd_send_with_reply(bus, cmd_skb, NULL, 13998f44cb0SIgor Mitsyanko sizeof(struct qlink_resp), NULL); 14098f44cb0SIgor Mitsyanko } 14198f44cb0SIgor Mitsyanko 14298f44cb0SIgor Mitsyanko static struct sk_buff *qtnf_cmd_alloc_new_cmdskb(u8 macid, u8 vifid, u16 cmd_no, 14398f44cb0SIgor Mitsyanko size_t cmd_size) 14498f44cb0SIgor Mitsyanko { 14598f44cb0SIgor Mitsyanko struct qlink_cmd *cmd; 14698f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 14798f44cb0SIgor Mitsyanko 14898f44cb0SIgor Mitsyanko cmd_skb = __dev_alloc_skb(sizeof(*cmd) + 14998f44cb0SIgor Mitsyanko QTNF_MAX_CMD_BUF_SIZE, GFP_KERNEL); 15098f44cb0SIgor Mitsyanko if (unlikely(!cmd_skb)) { 15198f44cb0SIgor Mitsyanko pr_err("VIF%u.%u CMD %u: alloc failed\n", macid, vifid, cmd_no); 15298f44cb0SIgor Mitsyanko return NULL; 15398f44cb0SIgor Mitsyanko } 15498f44cb0SIgor Mitsyanko 155b080db58SJohannes Berg skb_put_zero(cmd_skb, cmd_size); 15698f44cb0SIgor Mitsyanko 15798f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd *)cmd_skb->data; 15898f44cb0SIgor Mitsyanko cmd->mhdr.len = cpu_to_le16(cmd_skb->len); 15998f44cb0SIgor Mitsyanko cmd->mhdr.type = cpu_to_le16(QLINK_MSG_TYPE_CMD); 16098f44cb0SIgor Mitsyanko cmd->cmd_id = cpu_to_le16(cmd_no); 16198f44cb0SIgor Mitsyanko cmd->macid = macid; 16298f44cb0SIgor Mitsyanko cmd->vifid = vifid; 16398f44cb0SIgor Mitsyanko 16498f44cb0SIgor Mitsyanko return cmd_skb; 16598f44cb0SIgor Mitsyanko } 16698f44cb0SIgor Mitsyanko 16718b7470fSIgor Mitsyanko static void qtnf_cmd_tlv_ie_set_add(struct sk_buff *cmd_skb, u8 frame_type, 16818b7470fSIgor Mitsyanko const u8 *buf, size_t len) 16918b7470fSIgor Mitsyanko { 17018b7470fSIgor Mitsyanko struct qlink_tlv_ie_set *tlv; 17118b7470fSIgor Mitsyanko 17218b7470fSIgor Mitsyanko tlv = (struct qlink_tlv_ie_set *)skb_put(cmd_skb, sizeof(*tlv) + len); 17318b7470fSIgor Mitsyanko tlv->hdr.type = cpu_to_le16(QTN_TLV_ID_IE_SET); 17418b7470fSIgor Mitsyanko tlv->hdr.len = cpu_to_le16(len + sizeof(*tlv) - sizeof(tlv->hdr)); 17518b7470fSIgor Mitsyanko tlv->type = frame_type; 17618b7470fSIgor Mitsyanko tlv->flags = 0; 17718b7470fSIgor Mitsyanko 17818b7470fSIgor Mitsyanko if (len && buf) 17918b7470fSIgor Mitsyanko memcpy(tlv->ie_data, buf, len); 18018b7470fSIgor Mitsyanko } 18118b7470fSIgor Mitsyanko 182f1398fd2SVasily Ulyanov static inline size_t qtnf_cmd_acl_data_size(const struct cfg80211_acl_data *acl) 183f1398fd2SVasily Ulyanov { 184f1398fd2SVasily Ulyanov size_t size = sizeof(struct qlink_acl_data) + 185f1398fd2SVasily Ulyanov acl->n_acl_entries * sizeof(struct qlink_mac_address); 186f1398fd2SVasily Ulyanov 187f1398fd2SVasily Ulyanov return size; 188f1398fd2SVasily Ulyanov } 189f1398fd2SVasily Ulyanov 19017011da0SIgor Mitsyanko static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif, 19117011da0SIgor Mitsyanko const struct cfg80211_ap_settings *s) 19298f44cb0SIgor Mitsyanko { 19317011da0SIgor Mitsyanko unsigned int len = sizeof(struct qlink_cmd_start_ap); 19498f44cb0SIgor Mitsyanko 19517011da0SIgor Mitsyanko len += s->ssid_len; 19617011da0SIgor Mitsyanko len += s->beacon.head_len; 19717011da0SIgor Mitsyanko len += s->beacon.tail_len; 19817011da0SIgor Mitsyanko len += s->beacon.beacon_ies_len; 19917011da0SIgor Mitsyanko len += s->beacon.proberesp_ies_len; 20017011da0SIgor Mitsyanko len += s->beacon.assocresp_ies_len; 20117011da0SIgor Mitsyanko len += s->beacon.probe_resp_len; 20298f44cb0SIgor Mitsyanko 20317011da0SIgor Mitsyanko if (cfg80211_chandef_valid(&s->chandef)) 20417011da0SIgor Mitsyanko len += sizeof(struct qlink_tlv_chandef); 20598f44cb0SIgor Mitsyanko 206f1398fd2SVasily Ulyanov if (s->acl) 20733f98992SVasily Ulyanov len += sizeof(struct qlink_tlv_hdr) + 20833f98992SVasily Ulyanov qtnf_cmd_acl_data_size(s->acl); 209f1398fd2SVasily Ulyanov 21017011da0SIgor Mitsyanko if (len > (sizeof(struct qlink_cmd) + QTNF_MAX_CMD_BUF_SIZE)) { 21117011da0SIgor Mitsyanko pr_err("VIF%u.%u: can not fit AP settings: %u\n", 21217011da0SIgor Mitsyanko vif->mac->macid, vif->vifid, len); 21317011da0SIgor Mitsyanko return false; 21498f44cb0SIgor Mitsyanko } 21598f44cb0SIgor Mitsyanko 21617011da0SIgor Mitsyanko return true; 21798f44cb0SIgor Mitsyanko } 21898f44cb0SIgor Mitsyanko 21917011da0SIgor Mitsyanko int qtnf_cmd_send_start_ap(struct qtnf_vif *vif, 2209b692df1SIgor Mitsyanko const struct cfg80211_ap_settings *s) 22198f44cb0SIgor Mitsyanko { 22298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 22317011da0SIgor Mitsyanko struct qlink_cmd_start_ap *cmd; 2248b5f4aa7SIgor Mitsyanko struct qlink_auth_encr *aen; 22598f44cb0SIgor Mitsyanko int ret; 22698f44cb0SIgor Mitsyanko int i; 22798f44cb0SIgor Mitsyanko 22817011da0SIgor Mitsyanko if (!qtnf_cmd_start_ap_can_fit(vif, s)) 22917011da0SIgor Mitsyanko return -E2BIG; 23017011da0SIgor Mitsyanko 23198f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 23217011da0SIgor Mitsyanko QLINK_CMD_START_AP, 2338b5f4aa7SIgor Mitsyanko sizeof(*cmd)); 234c93fe71cSSergey Matyukevich if (!cmd_skb) 23598f44cb0SIgor Mitsyanko return -ENOMEM; 23698f44cb0SIgor Mitsyanko 23717011da0SIgor Mitsyanko cmd = (struct qlink_cmd_start_ap *)cmd_skb->data; 2388b5f4aa7SIgor Mitsyanko cmd->dtim_period = s->dtim_period; 2398b5f4aa7SIgor Mitsyanko cmd->beacon_interval = cpu_to_le16(s->beacon_interval); 2408b5f4aa7SIgor Mitsyanko cmd->hidden_ssid = qlink_hidden_ssid_nl2q(s->hidden_ssid); 2418b5f4aa7SIgor Mitsyanko cmd->inactivity_timeout = cpu_to_le16(s->inactivity_timeout); 2428b5f4aa7SIgor Mitsyanko cmd->smps_mode = s->smps_mode; 2438b5f4aa7SIgor Mitsyanko cmd->p2p_ctwindow = s->p2p_ctwindow; 2448b5f4aa7SIgor Mitsyanko cmd->p2p_opp_ps = s->p2p_opp_ps; 2458b5f4aa7SIgor Mitsyanko cmd->pbss = s->pbss; 2468b5f4aa7SIgor Mitsyanko cmd->ht_required = s->ht_required; 2478b5f4aa7SIgor Mitsyanko cmd->vht_required = s->vht_required; 24898f44cb0SIgor Mitsyanko 2498b5f4aa7SIgor Mitsyanko aen = &cmd->aen; 2508b5f4aa7SIgor Mitsyanko aen->auth_type = s->auth_type; 2518b5f4aa7SIgor Mitsyanko aen->privacy = !!s->privacy; 2528b5f4aa7SIgor Mitsyanko aen->wpa_versions = cpu_to_le32(s->crypto.wpa_versions); 2538b5f4aa7SIgor Mitsyanko aen->cipher_group = cpu_to_le32(s->crypto.cipher_group); 2548b5f4aa7SIgor Mitsyanko aen->n_ciphers_pairwise = cpu_to_le32(s->crypto.n_ciphers_pairwise); 25598f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) 2568b5f4aa7SIgor Mitsyanko aen->ciphers_pairwise[i] = 2579b692df1SIgor Mitsyanko cpu_to_le32(s->crypto.ciphers_pairwise[i]); 2588b5f4aa7SIgor Mitsyanko aen->n_akm_suites = cpu_to_le32(s->crypto.n_akm_suites); 25998f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) 2608b5f4aa7SIgor Mitsyanko aen->akm_suites[i] = cpu_to_le32(s->crypto.akm_suites[i]); 2618b5f4aa7SIgor Mitsyanko aen->control_port = s->crypto.control_port; 2628b5f4aa7SIgor Mitsyanko aen->control_port_no_encrypt = s->crypto.control_port_no_encrypt; 2638b5f4aa7SIgor Mitsyanko aen->control_port_ethertype = 2649b692df1SIgor Mitsyanko cpu_to_le16(be16_to_cpu(s->crypto.control_port_ethertype)); 26598f44cb0SIgor Mitsyanko 266f99201cbSIgor Mitsyanko if (s->ssid && s->ssid_len > 0 && s->ssid_len <= IEEE80211_MAX_SSID_LEN) 267f99201cbSIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, s->ssid, 268f99201cbSIgor Mitsyanko s->ssid_len); 269f99201cbSIgor Mitsyanko 270f99201cbSIgor Mitsyanko if (cfg80211_chandef_valid(&s->chandef)) { 271f99201cbSIgor Mitsyanko struct qlink_tlv_chandef *chtlv = 272f99201cbSIgor Mitsyanko (struct qlink_tlv_chandef *)skb_put(cmd_skb, 273f99201cbSIgor Mitsyanko sizeof(*chtlv)); 274f99201cbSIgor Mitsyanko 275f99201cbSIgor Mitsyanko chtlv->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANDEF); 276f99201cbSIgor Mitsyanko chtlv->hdr.len = cpu_to_le16(sizeof(*chtlv) - 277f99201cbSIgor Mitsyanko sizeof(chtlv->hdr)); 2785bf374abSSergey Matyukevich qlink_chandef_cfg2q(&s->chandef, &chtlv->chdef); 279f99201cbSIgor Mitsyanko } 280f99201cbSIgor Mitsyanko 28117011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_HEAD, 28217011da0SIgor Mitsyanko s->beacon.head, s->beacon.head_len); 28317011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_TAIL, 28417011da0SIgor Mitsyanko s->beacon.tail, s->beacon.tail_len); 28517011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_BEACON_IES, 28617011da0SIgor Mitsyanko s->beacon.beacon_ies, s->beacon.beacon_ies_len); 28717011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_RESP, 28817011da0SIgor Mitsyanko s->beacon.probe_resp, s->beacon.probe_resp_len); 28917011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_RESP_IES, 29017011da0SIgor Mitsyanko s->beacon.proberesp_ies, 29117011da0SIgor Mitsyanko s->beacon.proberesp_ies_len); 29217011da0SIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_ASSOC_RESP, 29317011da0SIgor Mitsyanko s->beacon.assocresp_ies, 29417011da0SIgor Mitsyanko s->beacon.assocresp_ies_len); 29517011da0SIgor Mitsyanko 296a3945f43SIgor Mitsyanko if (s->ht_cap) { 297a3945f43SIgor Mitsyanko struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *) 298a3945f43SIgor Mitsyanko skb_put(cmd_skb, sizeof(*tlv) + sizeof(*s->ht_cap)); 299a3945f43SIgor Mitsyanko 300a3945f43SIgor Mitsyanko tlv->type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); 301a3945f43SIgor Mitsyanko tlv->len = cpu_to_le16(sizeof(*s->ht_cap)); 302a3945f43SIgor Mitsyanko memcpy(tlv->val, s->ht_cap, sizeof(*s->ht_cap)); 303a3945f43SIgor Mitsyanko } 304a3945f43SIgor Mitsyanko 305a3945f43SIgor Mitsyanko if (s->vht_cap) { 306a3945f43SIgor Mitsyanko struct qlink_tlv_hdr *tlv = (struct qlink_tlv_hdr *) 307a3945f43SIgor Mitsyanko skb_put(cmd_skb, sizeof(*tlv) + sizeof(*s->vht_cap)); 308a3945f43SIgor Mitsyanko 309a3945f43SIgor Mitsyanko tlv->type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY); 310a3945f43SIgor Mitsyanko tlv->len = cpu_to_le16(sizeof(*s->vht_cap)); 311a3945f43SIgor Mitsyanko memcpy(tlv->val, s->vht_cap, sizeof(*s->vht_cap)); 312a3945f43SIgor Mitsyanko } 313a3945f43SIgor Mitsyanko 314f1398fd2SVasily Ulyanov if (s->acl) { 315f1398fd2SVasily Ulyanov size_t acl_size = qtnf_cmd_acl_data_size(s->acl); 316f1398fd2SVasily Ulyanov struct qlink_tlv_hdr *tlv = 317f1398fd2SVasily Ulyanov skb_put(cmd_skb, sizeof(*tlv) + acl_size); 318f1398fd2SVasily Ulyanov 319f1398fd2SVasily Ulyanov tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA); 320f1398fd2SVasily Ulyanov tlv->len = cpu_to_le16(acl_size); 321f1398fd2SVasily Ulyanov qlink_acl_data_cfg2q(s->acl, (struct qlink_acl_data *)tlv->val); 322f1398fd2SVasily Ulyanov } 323f1398fd2SVasily Ulyanov 3248b5f4aa7SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 325c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 326c6ed298fSSergey Matyukevich if (ret) 32798f44cb0SIgor Mitsyanko goto out; 32898f44cb0SIgor Mitsyanko 32917011da0SIgor Mitsyanko netif_carrier_on(vif->netdev); 33017011da0SIgor Mitsyanko 33198f44cb0SIgor Mitsyanko out: 33298f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 333c6ed298fSSergey Matyukevich 33498f44cb0SIgor Mitsyanko return ret; 33598f44cb0SIgor Mitsyanko } 33698f44cb0SIgor Mitsyanko 33798f44cb0SIgor Mitsyanko int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif) 33898f44cb0SIgor Mitsyanko { 33998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 34098f44cb0SIgor Mitsyanko int ret; 34198f44cb0SIgor Mitsyanko 34298f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 34398f44cb0SIgor Mitsyanko QLINK_CMD_STOP_AP, 34498f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 345c93fe71cSSergey Matyukevich if (!cmd_skb) 34698f44cb0SIgor Mitsyanko return -ENOMEM; 34798f44cb0SIgor Mitsyanko 34898f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 349c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 350c6ed298fSSergey Matyukevich if (ret) 35198f44cb0SIgor Mitsyanko goto out; 35298f44cb0SIgor Mitsyanko 35398f44cb0SIgor Mitsyanko out: 35498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 355c6ed298fSSergey Matyukevich 35698f44cb0SIgor Mitsyanko return ret; 35798f44cb0SIgor Mitsyanko } 35898f44cb0SIgor Mitsyanko 35998f44cb0SIgor Mitsyanko int qtnf_cmd_send_register_mgmt(struct qtnf_vif *vif, u16 frame_type, bool reg) 36098f44cb0SIgor Mitsyanko { 36198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 36298f44cb0SIgor Mitsyanko struct qlink_cmd_mgmt_frame_register *cmd; 36398f44cb0SIgor Mitsyanko int ret; 36498f44cb0SIgor Mitsyanko 36598f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 36698f44cb0SIgor Mitsyanko QLINK_CMD_REGISTER_MGMT, 36798f44cb0SIgor Mitsyanko sizeof(*cmd)); 368c93fe71cSSergey Matyukevich if (!cmd_skb) 36998f44cb0SIgor Mitsyanko return -ENOMEM; 37098f44cb0SIgor Mitsyanko 37198f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 37298f44cb0SIgor Mitsyanko 37398f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_mgmt_frame_register *)cmd_skb->data; 37498f44cb0SIgor Mitsyanko cmd->frame_type = cpu_to_le16(frame_type); 37598f44cb0SIgor Mitsyanko cmd->do_register = reg; 37698f44cb0SIgor Mitsyanko 377c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 378c6ed298fSSergey Matyukevich if (ret) 37998f44cb0SIgor Mitsyanko goto out; 38098f44cb0SIgor Mitsyanko 38198f44cb0SIgor Mitsyanko out: 38298f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 383c6ed298fSSergey Matyukevich 38498f44cb0SIgor Mitsyanko return ret; 38598f44cb0SIgor Mitsyanko } 38698f44cb0SIgor Mitsyanko 38798f44cb0SIgor Mitsyanko int qtnf_cmd_send_mgmt_frame(struct qtnf_vif *vif, u32 cookie, u16 flags, 38898f44cb0SIgor Mitsyanko u16 freq, const u8 *buf, size_t len) 38998f44cb0SIgor Mitsyanko { 39098f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 39198f44cb0SIgor Mitsyanko struct qlink_cmd_mgmt_frame_tx *cmd; 39298f44cb0SIgor Mitsyanko int ret; 39398f44cb0SIgor Mitsyanko 39498f44cb0SIgor Mitsyanko if (sizeof(*cmd) + len > QTNF_MAX_CMD_BUF_SIZE) { 39598f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: frame is too big: %zu\n", vif->mac->macid, 39698f44cb0SIgor Mitsyanko vif->vifid, len); 39798f44cb0SIgor Mitsyanko return -E2BIG; 39898f44cb0SIgor Mitsyanko } 39998f44cb0SIgor Mitsyanko 40098f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 40198f44cb0SIgor Mitsyanko QLINK_CMD_SEND_MGMT_FRAME, 40298f44cb0SIgor Mitsyanko sizeof(*cmd)); 403c93fe71cSSergey Matyukevich if (!cmd_skb) 40498f44cb0SIgor Mitsyanko return -ENOMEM; 40598f44cb0SIgor Mitsyanko 40698f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 40798f44cb0SIgor Mitsyanko 40898f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_mgmt_frame_tx *)cmd_skb->data; 40998f44cb0SIgor Mitsyanko cmd->cookie = cpu_to_le32(cookie); 41098f44cb0SIgor Mitsyanko cmd->freq = cpu_to_le16(freq); 41198f44cb0SIgor Mitsyanko cmd->flags = cpu_to_le16(flags); 41298f44cb0SIgor Mitsyanko 41398f44cb0SIgor Mitsyanko if (len && buf) 41498f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_buffer(cmd_skb, buf, len); 41598f44cb0SIgor Mitsyanko 416c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 417c6ed298fSSergey Matyukevich if (ret) 41898f44cb0SIgor Mitsyanko goto out; 41998f44cb0SIgor Mitsyanko 42098f44cb0SIgor Mitsyanko out: 42198f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 422c6ed298fSSergey Matyukevich 42398f44cb0SIgor Mitsyanko return ret; 42498f44cb0SIgor Mitsyanko } 42598f44cb0SIgor Mitsyanko 42698f44cb0SIgor Mitsyanko int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type, 42798f44cb0SIgor Mitsyanko const u8 *buf, size_t len) 42898f44cb0SIgor Mitsyanko { 42998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 43098f44cb0SIgor Mitsyanko int ret; 43198f44cb0SIgor Mitsyanko 4324d1f0fabSIgor Mitsyanko if (len > QTNF_MAX_CMD_BUF_SIZE) { 43398f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: %u frame is too big: %zu\n", vif->mac->macid, 43498f44cb0SIgor Mitsyanko vif->vifid, frame_type, len); 43598f44cb0SIgor Mitsyanko return -E2BIG; 43698f44cb0SIgor Mitsyanko } 43798f44cb0SIgor Mitsyanko 43898f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 43998f44cb0SIgor Mitsyanko QLINK_CMD_MGMT_SET_APPIE, 4404d1f0fabSIgor Mitsyanko sizeof(struct qlink_cmd)); 441c93fe71cSSergey Matyukevich if (!cmd_skb) 44298f44cb0SIgor Mitsyanko return -ENOMEM; 44398f44cb0SIgor Mitsyanko 4444d1f0fabSIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, frame_type, buf, len); 4454d1f0fabSIgor Mitsyanko 44698f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 447c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 448c6ed298fSSergey Matyukevich if (ret) 44998f44cb0SIgor Mitsyanko goto out; 45098f44cb0SIgor Mitsyanko 45198f44cb0SIgor Mitsyanko out: 45298f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 453c6ed298fSSergey Matyukevich 45498f44cb0SIgor Mitsyanko return ret; 45598f44cb0SIgor Mitsyanko } 45698f44cb0SIgor Mitsyanko 45798f44cb0SIgor Mitsyanko static void 45898f44cb0SIgor Mitsyanko qtnf_sta_info_parse_rate(struct rate_info *rate_dst, 45998f44cb0SIgor Mitsyanko const struct qlink_sta_info_rate *rate_src) 46098f44cb0SIgor Mitsyanko { 46198f44cb0SIgor Mitsyanko rate_dst->legacy = get_unaligned_le16(&rate_src->rate) * 10; 46298f44cb0SIgor Mitsyanko 46398f44cb0SIgor Mitsyanko rate_dst->mcs = rate_src->mcs; 46498f44cb0SIgor Mitsyanko rate_dst->nss = rate_src->nss; 46598f44cb0SIgor Mitsyanko rate_dst->flags = 0; 46698f44cb0SIgor Mitsyanko 46798f44cb0SIgor Mitsyanko switch (rate_src->bw) { 4684d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_5: 46998f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_5; 47098f44cb0SIgor Mitsyanko break; 4714d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_10: 47298f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_10; 47398f44cb0SIgor Mitsyanko break; 4744d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_20: 4754d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_20_NOHT: 47698f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_20; 47798f44cb0SIgor Mitsyanko break; 4784d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_40: 47998f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_40; 48098f44cb0SIgor Mitsyanko break; 4814d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_80: 48298f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_80; 48398f44cb0SIgor Mitsyanko break; 4844d2a7a1cSIgor Mitsyanko case QLINK_CHAN_WIDTH_160: 48598f44cb0SIgor Mitsyanko rate_dst->bw = RATE_INFO_BW_160; 48698f44cb0SIgor Mitsyanko break; 48798f44cb0SIgor Mitsyanko default: 48898f44cb0SIgor Mitsyanko rate_dst->bw = 0; 48998f44cb0SIgor Mitsyanko break; 49098f44cb0SIgor Mitsyanko } 49198f44cb0SIgor Mitsyanko 49298f44cb0SIgor Mitsyanko if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_HT_MCS) 49398f44cb0SIgor Mitsyanko rate_dst->flags |= RATE_INFO_FLAGS_MCS; 49498f44cb0SIgor Mitsyanko else if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_VHT_MCS) 49598f44cb0SIgor Mitsyanko rate_dst->flags |= RATE_INFO_FLAGS_VHT_MCS; 496d5657b70SSergey Matyukevich 497d5657b70SSergey Matyukevich if (rate_src->flags & QLINK_STA_INFO_RATE_FLAG_SHORT_GI) 498d5657b70SSergey Matyukevich rate_dst->flags |= RATE_INFO_FLAGS_SHORT_GI; 49998f44cb0SIgor Mitsyanko } 50098f44cb0SIgor Mitsyanko 50198f44cb0SIgor Mitsyanko static void 50298f44cb0SIgor Mitsyanko qtnf_sta_info_parse_flags(struct nl80211_sta_flag_update *dst, 50398f44cb0SIgor Mitsyanko const struct qlink_sta_info_state *src) 50498f44cb0SIgor Mitsyanko { 50598f44cb0SIgor Mitsyanko u32 mask, value; 50698f44cb0SIgor Mitsyanko 50798f44cb0SIgor Mitsyanko dst->mask = 0; 50898f44cb0SIgor Mitsyanko dst->set = 0; 50998f44cb0SIgor Mitsyanko 51098f44cb0SIgor Mitsyanko mask = le32_to_cpu(src->mask); 51198f44cb0SIgor Mitsyanko value = le32_to_cpu(src->value); 51298f44cb0SIgor Mitsyanko 51398f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_AUTHORIZED) { 51498f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_AUTHORIZED); 51598f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_AUTHORIZED) 51698f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_AUTHORIZED); 51798f44cb0SIgor Mitsyanko } 51898f44cb0SIgor Mitsyanko 51998f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_SHORT_PREAMBLE) { 52098f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); 52198f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_SHORT_PREAMBLE) 52298f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE); 52398f44cb0SIgor Mitsyanko } 52498f44cb0SIgor Mitsyanko 52598f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_WME) { 52698f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_WME); 52798f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_WME) 52898f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_WME); 52998f44cb0SIgor Mitsyanko } 53098f44cb0SIgor Mitsyanko 53198f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_MFP) { 53298f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_MFP); 53398f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_MFP) 53498f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_MFP); 53598f44cb0SIgor Mitsyanko } 53698f44cb0SIgor Mitsyanko 53798f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_AUTHENTICATED) { 53898f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED); 53998f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_AUTHENTICATED) 54098f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); 54198f44cb0SIgor Mitsyanko } 54298f44cb0SIgor Mitsyanko 54398f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_TDLS_PEER) { 54498f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_TDLS_PEER); 54598f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_TDLS_PEER) 54698f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_TDLS_PEER); 54798f44cb0SIgor Mitsyanko } 54898f44cb0SIgor Mitsyanko 54998f44cb0SIgor Mitsyanko if (mask & QLINK_STA_FLAG_ASSOCIATED) { 55098f44cb0SIgor Mitsyanko dst->mask |= BIT(NL80211_STA_FLAG_ASSOCIATED); 55198f44cb0SIgor Mitsyanko if (value & QLINK_STA_FLAG_ASSOCIATED) 55298f44cb0SIgor Mitsyanko dst->set |= BIT(NL80211_STA_FLAG_ASSOCIATED); 55398f44cb0SIgor Mitsyanko } 55498f44cb0SIgor Mitsyanko } 55598f44cb0SIgor Mitsyanko 55698f44cb0SIgor Mitsyanko static void 5574d2a7a1cSIgor Mitsyanko qtnf_cmd_sta_info_parse(struct station_info *sinfo, 5584d2a7a1cSIgor Mitsyanko const struct qlink_tlv_hdr *tlv, 5594d2a7a1cSIgor Mitsyanko size_t resp_size) 56098f44cb0SIgor Mitsyanko { 5614d2a7a1cSIgor Mitsyanko const struct qlink_sta_stats *stats = NULL; 5624d2a7a1cSIgor Mitsyanko const u8 *map = NULL; 5634d2a7a1cSIgor Mitsyanko unsigned int map_len = 0; 5644d2a7a1cSIgor Mitsyanko unsigned int stats_len = 0; 5654d2a7a1cSIgor Mitsyanko u16 tlv_len; 56698f44cb0SIgor Mitsyanko 5674d2a7a1cSIgor Mitsyanko #define qtnf_sta_stat_avail(stat_name, bitn) \ 5684d2a7a1cSIgor Mitsyanko (qtnf_utils_is_bit_set(map, bitn, map_len) && \ 5694d2a7a1cSIgor Mitsyanko (offsetofend(struct qlink_sta_stats, stat_name) <= stats_len)) 57098f44cb0SIgor Mitsyanko 5714d2a7a1cSIgor Mitsyanko while (resp_size >= sizeof(*tlv)) { 5724d2a7a1cSIgor Mitsyanko tlv_len = le16_to_cpu(tlv->len); 57398f44cb0SIgor Mitsyanko 5744d2a7a1cSIgor Mitsyanko switch (le16_to_cpu(tlv->type)) { 5754d2a7a1cSIgor Mitsyanko case QTN_TLV_ID_STA_STATS_MAP: 5764d2a7a1cSIgor Mitsyanko map_len = tlv_len; 5774d2a7a1cSIgor Mitsyanko map = tlv->val; 57898f44cb0SIgor Mitsyanko break; 5794d2a7a1cSIgor Mitsyanko case QTN_TLV_ID_STA_STATS: 5804d2a7a1cSIgor Mitsyanko stats_len = tlv_len; 5814d2a7a1cSIgor Mitsyanko stats = (const struct qlink_sta_stats *)tlv->val; 58298f44cb0SIgor Mitsyanko break; 58398f44cb0SIgor Mitsyanko default: 58498f44cb0SIgor Mitsyanko break; 58598f44cb0SIgor Mitsyanko } 5864d2a7a1cSIgor Mitsyanko 5874d2a7a1cSIgor Mitsyanko resp_size -= tlv_len + sizeof(*tlv); 5884d2a7a1cSIgor Mitsyanko tlv = (const struct qlink_tlv_hdr *)(tlv->val + tlv_len); 58998f44cb0SIgor Mitsyanko } 59098f44cb0SIgor Mitsyanko 5914d2a7a1cSIgor Mitsyanko if (!map || !stats) 5924d2a7a1cSIgor Mitsyanko return; 5934d2a7a1cSIgor Mitsyanko 5944d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(inactive_time, QLINK_STA_INFO_INACTIVE_TIME)) { 59522d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME); 5964d2a7a1cSIgor Mitsyanko sinfo->inactive_time = le32_to_cpu(stats->inactive_time); 59798f44cb0SIgor Mitsyanko } 59898f44cb0SIgor Mitsyanko 5994d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(connected_time, 6004d2a7a1cSIgor Mitsyanko QLINK_STA_INFO_CONNECTED_TIME)) { 60122d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME); 6024d2a7a1cSIgor Mitsyanko sinfo->connected_time = le32_to_cpu(stats->connected_time); 6034d2a7a1cSIgor Mitsyanko } 6044d2a7a1cSIgor Mitsyanko 6054d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(signal, QLINK_STA_INFO_SIGNAL)) { 60622d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); 6074d2a7a1cSIgor Mitsyanko sinfo->signal = stats->signal - QLINK_RSSI_OFFSET; 6084d2a7a1cSIgor Mitsyanko } 6094d2a7a1cSIgor Mitsyanko 6104d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(signal_avg, QLINK_STA_INFO_SIGNAL_AVG)) { 61122d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); 6124d2a7a1cSIgor Mitsyanko sinfo->signal_avg = stats->signal_avg - QLINK_RSSI_OFFSET; 6134d2a7a1cSIgor Mitsyanko } 6144d2a7a1cSIgor Mitsyanko 6154d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rxrate, QLINK_STA_INFO_RX_BITRATE)) { 61622d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE); 6174d2a7a1cSIgor Mitsyanko qtnf_sta_info_parse_rate(&sinfo->rxrate, &stats->rxrate); 6184d2a7a1cSIgor Mitsyanko } 6194d2a7a1cSIgor Mitsyanko 6204d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(txrate, QLINK_STA_INFO_TX_BITRATE)) { 62122d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); 6224d2a7a1cSIgor Mitsyanko qtnf_sta_info_parse_rate(&sinfo->txrate, &stats->txrate); 6234d2a7a1cSIgor Mitsyanko } 6244d2a7a1cSIgor Mitsyanko 6254d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(sta_flags, QLINK_STA_INFO_STA_FLAGS)) { 62622d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS); 6274d2a7a1cSIgor Mitsyanko qtnf_sta_info_parse_flags(&sinfo->sta_flags, &stats->sta_flags); 6284d2a7a1cSIgor Mitsyanko } 6294d2a7a1cSIgor Mitsyanko 6304d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES)) { 63122d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES); 6324d2a7a1cSIgor Mitsyanko sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes); 6334d2a7a1cSIgor Mitsyanko } 6344d2a7a1cSIgor Mitsyanko 6354d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES)) { 63622d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES); 6374d2a7a1cSIgor Mitsyanko sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes); 6384d2a7a1cSIgor Mitsyanko } 6394d2a7a1cSIgor Mitsyanko 6404d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rx_bytes, QLINK_STA_INFO_RX_BYTES64)) { 64122d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64); 6424d2a7a1cSIgor Mitsyanko sinfo->rx_bytes = le64_to_cpu(stats->rx_bytes); 6434d2a7a1cSIgor Mitsyanko } 6444d2a7a1cSIgor Mitsyanko 6454d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(tx_bytes, QLINK_STA_INFO_TX_BYTES64)) { 64622d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64); 6474d2a7a1cSIgor Mitsyanko sinfo->tx_bytes = le64_to_cpu(stats->tx_bytes); 6484d2a7a1cSIgor Mitsyanko } 6494d2a7a1cSIgor Mitsyanko 6504d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rx_packets, QLINK_STA_INFO_RX_PACKETS)) { 65122d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); 6524d2a7a1cSIgor Mitsyanko sinfo->rx_packets = le32_to_cpu(stats->rx_packets); 6534d2a7a1cSIgor Mitsyanko } 6544d2a7a1cSIgor Mitsyanko 6554d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(tx_packets, QLINK_STA_INFO_TX_PACKETS)) { 65622d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS); 6574d2a7a1cSIgor Mitsyanko sinfo->tx_packets = le32_to_cpu(stats->tx_packets); 6584d2a7a1cSIgor Mitsyanko } 6594d2a7a1cSIgor Mitsyanko 6604d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rx_beacon, QLINK_STA_INFO_BEACON_RX)) { 66122d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX); 6624d2a7a1cSIgor Mitsyanko sinfo->rx_beacon = le64_to_cpu(stats->rx_beacon); 6634d2a7a1cSIgor Mitsyanko } 6644d2a7a1cSIgor Mitsyanko 6654d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(rx_dropped_misc, QLINK_STA_INFO_RX_DROP_MISC)) { 66622d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC); 6674d2a7a1cSIgor Mitsyanko sinfo->rx_dropped_misc = le32_to_cpu(stats->rx_dropped_misc); 6684d2a7a1cSIgor Mitsyanko } 6694d2a7a1cSIgor Mitsyanko 6704d2a7a1cSIgor Mitsyanko if (qtnf_sta_stat_avail(tx_failed, QLINK_STA_INFO_TX_FAILED)) { 67122d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); 6724d2a7a1cSIgor Mitsyanko sinfo->tx_failed = le32_to_cpu(stats->tx_failed); 6734d2a7a1cSIgor Mitsyanko } 6744d2a7a1cSIgor Mitsyanko 6754d2a7a1cSIgor Mitsyanko #undef qtnf_sta_stat_avail 67698f44cb0SIgor Mitsyanko } 67798f44cb0SIgor Mitsyanko 67898f44cb0SIgor Mitsyanko int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac, 67998f44cb0SIgor Mitsyanko struct station_info *sinfo) 68098f44cb0SIgor Mitsyanko { 68198f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 68298f44cb0SIgor Mitsyanko struct qlink_cmd_get_sta_info *cmd; 68398f44cb0SIgor Mitsyanko const struct qlink_resp_get_sta_info *resp; 6841066bd19SSergey Matyukevich size_t var_resp_len = 0; 68598f44cb0SIgor Mitsyanko int ret = 0; 68698f44cb0SIgor Mitsyanko 68798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 68898f44cb0SIgor Mitsyanko QLINK_CMD_GET_STA_INFO, 68998f44cb0SIgor Mitsyanko sizeof(*cmd)); 690c93fe71cSSergey Matyukevich if (!cmd_skb) 69198f44cb0SIgor Mitsyanko return -ENOMEM; 69298f44cb0SIgor Mitsyanko 69398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 69498f44cb0SIgor Mitsyanko 69598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_get_sta_info *)cmd_skb->data; 69698f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, sta_mac); 69798f44cb0SIgor Mitsyanko 69898f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(vif->mac->bus, cmd_skb, &resp_skb, 699c6ed298fSSergey Matyukevich sizeof(*resp), &var_resp_len); 700c6ed298fSSergey Matyukevich if (ret) 70198f44cb0SIgor Mitsyanko goto out; 70298f44cb0SIgor Mitsyanko 70398f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_sta_info *)resp_skb->data; 70498f44cb0SIgor Mitsyanko 705c6ed298fSSergey Matyukevich if (!ether_addr_equal(sta_mac, resp->sta_addr)) { 70698f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: wrong mac in reply: %pM != %pM\n", 70798f44cb0SIgor Mitsyanko vif->mac->macid, vif->vifid, resp->sta_addr, sta_mac); 70898f44cb0SIgor Mitsyanko ret = -EINVAL; 70998f44cb0SIgor Mitsyanko goto out; 71098f44cb0SIgor Mitsyanko } 71198f44cb0SIgor Mitsyanko 7124d2a7a1cSIgor Mitsyanko qtnf_cmd_sta_info_parse(sinfo, 7134d2a7a1cSIgor Mitsyanko (const struct qlink_tlv_hdr *)resp->info, 7144d2a7a1cSIgor Mitsyanko var_resp_len); 71598f44cb0SIgor Mitsyanko 71698f44cb0SIgor Mitsyanko out: 71798f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 71898f44cb0SIgor Mitsyanko consume_skb(resp_skb); 71998f44cb0SIgor Mitsyanko 72098f44cb0SIgor Mitsyanko return ret; 72198f44cb0SIgor Mitsyanko } 72298f44cb0SIgor Mitsyanko 72398f44cb0SIgor Mitsyanko static int qtnf_cmd_send_add_change_intf(struct qtnf_vif *vif, 72498f44cb0SIgor Mitsyanko enum nl80211_iftype iftype, 725de624a35SSergey Matyukevich int use4addr, 72698f44cb0SIgor Mitsyanko u8 *mac_addr, 72798f44cb0SIgor Mitsyanko enum qlink_cmd_type cmd_type) 72898f44cb0SIgor Mitsyanko { 72998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 73098f44cb0SIgor Mitsyanko struct qlink_cmd_manage_intf *cmd; 73198f44cb0SIgor Mitsyanko const struct qlink_resp_manage_intf *resp; 73298f44cb0SIgor Mitsyanko int ret = 0; 73398f44cb0SIgor Mitsyanko 73498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 73598f44cb0SIgor Mitsyanko cmd_type, 73698f44cb0SIgor Mitsyanko sizeof(*cmd)); 737c93fe71cSSergey Matyukevich if (!cmd_skb) 73898f44cb0SIgor Mitsyanko return -ENOMEM; 73998f44cb0SIgor Mitsyanko 74098f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 74198f44cb0SIgor Mitsyanko 74298f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data; 743de624a35SSergey Matyukevich cmd->intf_info.use4addr = use4addr; 74498f44cb0SIgor Mitsyanko 74598f44cb0SIgor Mitsyanko switch (iftype) { 74698f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 74798f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_AP); 74898f44cb0SIgor Mitsyanko break; 74998f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 75098f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_STATION); 75198f44cb0SIgor Mitsyanko break; 75298f44cb0SIgor Mitsyanko default: 75398f44cb0SIgor Mitsyanko pr_err("VIF%u.%u: unsupported type %d\n", vif->mac->macid, 75498f44cb0SIgor Mitsyanko vif->vifid, iftype); 75598f44cb0SIgor Mitsyanko ret = -EINVAL; 75698f44cb0SIgor Mitsyanko goto out; 75798f44cb0SIgor Mitsyanko } 75898f44cb0SIgor Mitsyanko 75998f44cb0SIgor Mitsyanko if (mac_addr) 76098f44cb0SIgor Mitsyanko ether_addr_copy(cmd->intf_info.mac_addr, mac_addr); 76198f44cb0SIgor Mitsyanko else 76298f44cb0SIgor Mitsyanko eth_zero_addr(cmd->intf_info.mac_addr); 76398f44cb0SIgor Mitsyanko 76498f44cb0SIgor Mitsyanko ret = qtnf_cmd_send_with_reply(vif->mac->bus, cmd_skb, &resp_skb, 765c6ed298fSSergey Matyukevich sizeof(*resp), NULL); 766c6ed298fSSergey Matyukevich if (ret) 76798f44cb0SIgor Mitsyanko goto out; 76898f44cb0SIgor Mitsyanko 76998f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_manage_intf *)resp_skb->data; 77098f44cb0SIgor Mitsyanko ether_addr_copy(vif->mac_addr, resp->intf_info.mac_addr); 77198f44cb0SIgor Mitsyanko 77298f44cb0SIgor Mitsyanko out: 77398f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 77498f44cb0SIgor Mitsyanko consume_skb(resp_skb); 77598f44cb0SIgor Mitsyanko 77698f44cb0SIgor Mitsyanko return ret; 77798f44cb0SIgor Mitsyanko } 77898f44cb0SIgor Mitsyanko 779de624a35SSergey Matyukevich int qtnf_cmd_send_add_intf(struct qtnf_vif *vif, enum nl80211_iftype iftype, 780de624a35SSergey Matyukevich int use4addr, u8 *mac_addr) 78198f44cb0SIgor Mitsyanko { 782de624a35SSergey Matyukevich return qtnf_cmd_send_add_change_intf(vif, iftype, use4addr, mac_addr, 78398f44cb0SIgor Mitsyanko QLINK_CMD_ADD_INTF); 78498f44cb0SIgor Mitsyanko } 78598f44cb0SIgor Mitsyanko 78698f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_intf_type(struct qtnf_vif *vif, 787de624a35SSergey Matyukevich enum nl80211_iftype iftype, 788de624a35SSergey Matyukevich int use4addr, 789de624a35SSergey Matyukevich u8 *mac_addr) 79098f44cb0SIgor Mitsyanko { 791de624a35SSergey Matyukevich return qtnf_cmd_send_add_change_intf(vif, iftype, use4addr, mac_addr, 79298f44cb0SIgor Mitsyanko QLINK_CMD_CHANGE_INTF); 79398f44cb0SIgor Mitsyanko } 79498f44cb0SIgor Mitsyanko 79598f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_intf(struct qtnf_vif *vif) 79698f44cb0SIgor Mitsyanko { 79798f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 79898f44cb0SIgor Mitsyanko struct qlink_cmd_manage_intf *cmd; 79998f44cb0SIgor Mitsyanko int ret = 0; 80098f44cb0SIgor Mitsyanko 80198f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 80298f44cb0SIgor Mitsyanko QLINK_CMD_DEL_INTF, 80398f44cb0SIgor Mitsyanko sizeof(*cmd)); 804c93fe71cSSergey Matyukevich if (!cmd_skb) 80598f44cb0SIgor Mitsyanko return -ENOMEM; 80698f44cb0SIgor Mitsyanko 80798f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 80898f44cb0SIgor Mitsyanko 80998f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_manage_intf *)cmd_skb->data; 81098f44cb0SIgor Mitsyanko 81198f44cb0SIgor Mitsyanko switch (vif->wdev.iftype) { 81298f44cb0SIgor Mitsyanko case NL80211_IFTYPE_AP: 81398f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_AP); 81498f44cb0SIgor Mitsyanko break; 81598f44cb0SIgor Mitsyanko case NL80211_IFTYPE_STATION: 81698f44cb0SIgor Mitsyanko cmd->intf_info.if_type = cpu_to_le16(QLINK_IFTYPE_STATION); 81798f44cb0SIgor Mitsyanko break; 81898f44cb0SIgor Mitsyanko default: 81998f44cb0SIgor Mitsyanko pr_warn("VIF%u.%u: unsupported iftype %d\n", vif->mac->macid, 82098f44cb0SIgor Mitsyanko vif->vifid, vif->wdev.iftype); 82198f44cb0SIgor Mitsyanko ret = -EINVAL; 82298f44cb0SIgor Mitsyanko goto out; 82398f44cb0SIgor Mitsyanko } 82498f44cb0SIgor Mitsyanko 82598f44cb0SIgor Mitsyanko eth_zero_addr(cmd->intf_info.mac_addr); 82698f44cb0SIgor Mitsyanko 827c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 828c6ed298fSSergey Matyukevich if (ret) 82998f44cb0SIgor Mitsyanko goto out; 83098f44cb0SIgor Mitsyanko 83198f44cb0SIgor Mitsyanko out: 83298f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 83398f44cb0SIgor Mitsyanko return ret; 83498f44cb0SIgor Mitsyanko } 83598f44cb0SIgor Mitsyanko 8364dd07d2bSSergey Matyukevich static u32 qtnf_cmd_resp_reg_rule_flags_parse(u32 qflags) 8374dd07d2bSSergey Matyukevich { 8384dd07d2bSSergey Matyukevich u32 flags = 0; 8394dd07d2bSSergey Matyukevich 8404dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_OFDM) 8414dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_OFDM; 8424dd07d2bSSergey Matyukevich 8434dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_CCK) 8444dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_CCK; 8454dd07d2bSSergey Matyukevich 8464dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_INDOOR) 8474dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_INDOOR; 8484dd07d2bSSergey Matyukevich 8494dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_OUTDOOR) 8504dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_OUTDOOR; 8514dd07d2bSSergey Matyukevich 8524dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_DFS) 8534dd07d2bSSergey Matyukevich flags |= NL80211_RRF_DFS; 8544dd07d2bSSergey Matyukevich 8554dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_PTP_ONLY) 8564dd07d2bSSergey Matyukevich flags |= NL80211_RRF_PTP_ONLY; 8574dd07d2bSSergey Matyukevich 8584dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_PTMP_ONLY) 8594dd07d2bSSergey Matyukevich flags |= NL80211_RRF_PTMP_ONLY; 8604dd07d2bSSergey Matyukevich 8614dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_IR) 8624dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_IR; 8634dd07d2bSSergey Matyukevich 8644dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_AUTO_BW) 8654dd07d2bSSergey Matyukevich flags |= NL80211_RRF_AUTO_BW; 8664dd07d2bSSergey Matyukevich 8674dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_IR_CONCURRENT) 8684dd07d2bSSergey Matyukevich flags |= NL80211_RRF_IR_CONCURRENT; 8694dd07d2bSSergey Matyukevich 8704dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_HT40MINUS) 8714dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_HT40MINUS; 8724dd07d2bSSergey Matyukevich 8734dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_HT40PLUS) 8744dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_HT40PLUS; 8754dd07d2bSSergey Matyukevich 8764dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_80MHZ) 8774dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_80MHZ; 8784dd07d2bSSergey Matyukevich 8794dd07d2bSSergey Matyukevich if (qflags & QLINK_RRF_NO_160MHZ) 8804dd07d2bSSergey Matyukevich flags |= NL80211_RRF_NO_160MHZ; 8814dd07d2bSSergey Matyukevich 8824dd07d2bSSergey Matyukevich return flags; 8834dd07d2bSSergey Matyukevich } 8844dd07d2bSSergey Matyukevich 88598f44cb0SIgor Mitsyanko static int 88698f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus, 8874dd07d2bSSergey Matyukevich const struct qlink_resp_get_hw_info *resp, 8884dd07d2bSSergey Matyukevich size_t info_len) 88998f44cb0SIgor Mitsyanko { 89098f44cb0SIgor Mitsyanko struct qtnf_hw_info *hwinfo = &bus->hw_info; 8914dd07d2bSSergey Matyukevich const struct qlink_tlv_hdr *tlv; 8924dd07d2bSSergey Matyukevich const struct qlink_tlv_reg_rule *tlv_rule; 8935ec5b532SVasily Ulyanov const char *bld_name = NULL; 8945ec5b532SVasily Ulyanov const char *bld_rev = NULL; 8955ec5b532SVasily Ulyanov const char *bld_type = NULL; 8965ec5b532SVasily Ulyanov const char *bld_label = NULL; 8975ec5b532SVasily Ulyanov u32 bld_tmstamp = 0; 8985ec5b532SVasily Ulyanov u32 plat_id = 0; 8995ec5b532SVasily Ulyanov const char *hw_id = NULL; 9005ec5b532SVasily Ulyanov const char *calibration_ver = NULL; 9015ec5b532SVasily Ulyanov const char *uboot_ver = NULL; 9025ec5b532SVasily Ulyanov u32 hw_ver = 0; 9034dd07d2bSSergey Matyukevich struct ieee80211_reg_rule *rule; 9044dd07d2bSSergey Matyukevich u16 tlv_type; 9054dd07d2bSSergey Matyukevich u16 tlv_value_len; 9064dd07d2bSSergey Matyukevich unsigned int rule_idx = 0; 9074dd07d2bSSergey Matyukevich 9084dd07d2bSSergey Matyukevich if (WARN_ON(resp->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) 9094dd07d2bSSergey Matyukevich return -E2BIG; 9104dd07d2bSSergey Matyukevich 911db040dfaSGustavo A. R. Silva hwinfo->rd = kzalloc(struct_size(hwinfo->rd, reg_rules, 912db040dfaSGustavo A. R. Silva resp->n_reg_rules), GFP_KERNEL); 9134dd07d2bSSergey Matyukevich 9144dd07d2bSSergey Matyukevich if (!hwinfo->rd) 9154dd07d2bSSergey Matyukevich return -ENOMEM; 91698f44cb0SIgor Mitsyanko 91798f44cb0SIgor Mitsyanko hwinfo->num_mac = resp->num_mac; 91898f44cb0SIgor Mitsyanko hwinfo->mac_bitmap = resp->mac_bitmap; 91998f44cb0SIgor Mitsyanko hwinfo->fw_ver = le32_to_cpu(resp->fw_ver); 92098f44cb0SIgor Mitsyanko hwinfo->ql_proto_ver = le16_to_cpu(resp->ql_proto_ver); 92198f44cb0SIgor Mitsyanko hwinfo->total_tx_chain = resp->total_tx_chain; 92298f44cb0SIgor Mitsyanko hwinfo->total_rx_chain = resp->total_rx_chain; 92398f44cb0SIgor Mitsyanko hwinfo->hw_capab = le32_to_cpu(resp->hw_capab); 9244dd07d2bSSergey Matyukevich hwinfo->rd->n_reg_rules = resp->n_reg_rules; 9254dd07d2bSSergey Matyukevich hwinfo->rd->alpha2[0] = resp->alpha2[0]; 9264dd07d2bSSergey Matyukevich hwinfo->rd->alpha2[1] = resp->alpha2[1]; 9274dd07d2bSSergey Matyukevich 9285ec5b532SVasily Ulyanov bld_tmstamp = le32_to_cpu(resp->bld_tmstamp); 9295ec5b532SVasily Ulyanov plat_id = le32_to_cpu(resp->plat_id); 9305ec5b532SVasily Ulyanov hw_ver = le32_to_cpu(resp->hw_ver); 9315ec5b532SVasily Ulyanov 9324dd07d2bSSergey Matyukevich switch (resp->dfs_region) { 9334dd07d2bSSergey Matyukevich case QLINK_DFS_FCC: 9344dd07d2bSSergey Matyukevich hwinfo->rd->dfs_region = NL80211_DFS_FCC; 9354dd07d2bSSergey Matyukevich break; 9364dd07d2bSSergey Matyukevich case QLINK_DFS_ETSI: 9374dd07d2bSSergey Matyukevich hwinfo->rd->dfs_region = NL80211_DFS_ETSI; 9384dd07d2bSSergey Matyukevich break; 9394dd07d2bSSergey Matyukevich case QLINK_DFS_JP: 9404dd07d2bSSergey Matyukevich hwinfo->rd->dfs_region = NL80211_DFS_JP; 9414dd07d2bSSergey Matyukevich break; 9424dd07d2bSSergey Matyukevich case QLINK_DFS_UNSET: 9434dd07d2bSSergey Matyukevich default: 9444dd07d2bSSergey Matyukevich hwinfo->rd->dfs_region = NL80211_DFS_UNSET; 9454dd07d2bSSergey Matyukevich break; 9464dd07d2bSSergey Matyukevich } 9474dd07d2bSSergey Matyukevich 9484dd07d2bSSergey Matyukevich tlv = (const struct qlink_tlv_hdr *)resp->info; 9494dd07d2bSSergey Matyukevich 9504dd07d2bSSergey Matyukevich while (info_len >= sizeof(*tlv)) { 9514dd07d2bSSergey Matyukevich tlv_type = le16_to_cpu(tlv->type); 9524dd07d2bSSergey Matyukevich tlv_value_len = le16_to_cpu(tlv->len); 9534dd07d2bSSergey Matyukevich 9544dd07d2bSSergey Matyukevich if (tlv_value_len + sizeof(*tlv) > info_len) { 9554dd07d2bSSergey Matyukevich pr_warn("malformed TLV 0x%.2X; LEN: %u\n", 9564dd07d2bSSergey Matyukevich tlv_type, tlv_value_len); 9574dd07d2bSSergey Matyukevich return -EINVAL; 9584dd07d2bSSergey Matyukevich } 9594dd07d2bSSergey Matyukevich 9604dd07d2bSSergey Matyukevich switch (tlv_type) { 9614dd07d2bSSergey Matyukevich case QTN_TLV_ID_REG_RULE: 9624dd07d2bSSergey Matyukevich if (rule_idx >= resp->n_reg_rules) { 9634dd07d2bSSergey Matyukevich pr_warn("unexpected number of rules: %u\n", 9644dd07d2bSSergey Matyukevich resp->n_reg_rules); 9654dd07d2bSSergey Matyukevich return -EINVAL; 9664dd07d2bSSergey Matyukevich } 9674dd07d2bSSergey Matyukevich 9684dd07d2bSSergey Matyukevich if (tlv_value_len != sizeof(*tlv_rule) - sizeof(*tlv)) { 9694dd07d2bSSergey Matyukevich pr_warn("malformed TLV 0x%.2X; LEN: %u\n", 9704dd07d2bSSergey Matyukevich tlv_type, tlv_value_len); 9714dd07d2bSSergey Matyukevich return -EINVAL; 9724dd07d2bSSergey Matyukevich } 9734dd07d2bSSergey Matyukevich 9744dd07d2bSSergey Matyukevich tlv_rule = (const struct qlink_tlv_reg_rule *)tlv; 9754dd07d2bSSergey Matyukevich rule = &hwinfo->rd->reg_rules[rule_idx++]; 9764dd07d2bSSergey Matyukevich 9774dd07d2bSSergey Matyukevich rule->freq_range.start_freq_khz = 9784dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->start_freq_khz); 9794dd07d2bSSergey Matyukevich rule->freq_range.end_freq_khz = 9804dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->end_freq_khz); 9814dd07d2bSSergey Matyukevich rule->freq_range.max_bandwidth_khz = 9824dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->max_bandwidth_khz); 9834dd07d2bSSergey Matyukevich rule->power_rule.max_antenna_gain = 9844dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->max_antenna_gain); 9854dd07d2bSSergey Matyukevich rule->power_rule.max_eirp = 9864dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->max_eirp); 9874dd07d2bSSergey Matyukevich rule->dfs_cac_ms = 9884dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->dfs_cac_ms); 9894dd07d2bSSergey Matyukevich rule->flags = qtnf_cmd_resp_reg_rule_flags_parse( 9904dd07d2bSSergey Matyukevich le32_to_cpu(tlv_rule->flags)); 9914dd07d2bSSergey Matyukevich break; 9925ec5b532SVasily Ulyanov case QTN_TLV_ID_BUILD_NAME: 9935ec5b532SVasily Ulyanov bld_name = (const void *)tlv->val; 9945ec5b532SVasily Ulyanov break; 9955ec5b532SVasily Ulyanov case QTN_TLV_ID_BUILD_REV: 9965ec5b532SVasily Ulyanov bld_rev = (const void *)tlv->val; 9975ec5b532SVasily Ulyanov break; 9985ec5b532SVasily Ulyanov case QTN_TLV_ID_BUILD_TYPE: 9995ec5b532SVasily Ulyanov bld_type = (const void *)tlv->val; 10005ec5b532SVasily Ulyanov break; 10015ec5b532SVasily Ulyanov case QTN_TLV_ID_BUILD_LABEL: 10025ec5b532SVasily Ulyanov bld_label = (const void *)tlv->val; 10035ec5b532SVasily Ulyanov break; 10045ec5b532SVasily Ulyanov case QTN_TLV_ID_HW_ID: 10055ec5b532SVasily Ulyanov hw_id = (const void *)tlv->val; 10065ec5b532SVasily Ulyanov break; 10075ec5b532SVasily Ulyanov case QTN_TLV_ID_CALIBRATION_VER: 10085ec5b532SVasily Ulyanov calibration_ver = (const void *)tlv->val; 10095ec5b532SVasily Ulyanov break; 10105ec5b532SVasily Ulyanov case QTN_TLV_ID_UBOOT_VER: 10115ec5b532SVasily Ulyanov uboot_ver = (const void *)tlv->val; 10125ec5b532SVasily Ulyanov break; 10138f1180e0SAndrey Shevchenko case QTN_TLV_ID_MAX_SCAN_SSIDS: 10148f1180e0SAndrey Shevchenko hwinfo->max_scan_ssids = *tlv->val; 10158f1180e0SAndrey Shevchenko break; 10164dd07d2bSSergey Matyukevich default: 10174dd07d2bSSergey Matyukevich break; 10184dd07d2bSSergey Matyukevich } 10194dd07d2bSSergey Matyukevich 10204dd07d2bSSergey Matyukevich info_len -= tlv_value_len + sizeof(*tlv); 10214dd07d2bSSergey Matyukevich tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 10224dd07d2bSSergey Matyukevich } 10234dd07d2bSSergey Matyukevich 10244dd07d2bSSergey Matyukevich if (rule_idx != resp->n_reg_rules) { 10254dd07d2bSSergey Matyukevich pr_warn("unexpected number of rules: expected %u got %u\n", 10264dd07d2bSSergey Matyukevich resp->n_reg_rules, rule_idx); 10274dd07d2bSSergey Matyukevich kfree(hwinfo->rd); 10284dd07d2bSSergey Matyukevich hwinfo->rd = NULL; 10294dd07d2bSSergey Matyukevich return -EINVAL; 10304dd07d2bSSergey Matyukevich } 103198f44cb0SIgor Mitsyanko 1032db5c6d4aSIgor Mitsyanko pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u, capab=0x%x\n", 103398f44cb0SIgor Mitsyanko hwinfo->fw_ver, hwinfo->mac_bitmap, 10344dd07d2bSSergey Matyukevich hwinfo->rd->alpha2[0], hwinfo->rd->alpha2[1], 1035db5c6d4aSIgor Mitsyanko hwinfo->total_tx_chain, hwinfo->total_rx_chain, 1036db5c6d4aSIgor Mitsyanko hwinfo->hw_capab); 103798f44cb0SIgor Mitsyanko 10385ec5b532SVasily Ulyanov pr_info("\nBuild name: %s" \ 10395ec5b532SVasily Ulyanov "\nBuild revision: %s" \ 10405ec5b532SVasily Ulyanov "\nBuild type: %s" \ 10415ec5b532SVasily Ulyanov "\nBuild label: %s" \ 10425ec5b532SVasily Ulyanov "\nBuild timestamp: %lu" \ 10435ec5b532SVasily Ulyanov "\nPlatform ID: %lu" \ 10445ec5b532SVasily Ulyanov "\nHardware ID: %s" \ 10455ec5b532SVasily Ulyanov "\nCalibration version: %s" \ 10465ec5b532SVasily Ulyanov "\nU-Boot version: %s" \ 10475ec5b532SVasily Ulyanov "\nHardware version: 0x%08x", 10485ec5b532SVasily Ulyanov bld_name, bld_rev, bld_type, bld_label, 10495ec5b532SVasily Ulyanov (unsigned long)bld_tmstamp, 10505ec5b532SVasily Ulyanov (unsigned long)plat_id, 10515ec5b532SVasily Ulyanov hw_id, calibration_ver, uboot_ver, hw_ver); 10525ec5b532SVasily Ulyanov 10530b419d01SVasily Ulyanov strlcpy(hwinfo->fw_version, bld_label, sizeof(hwinfo->fw_version)); 10540b419d01SVasily Ulyanov hwinfo->hw_version = hw_ver; 10550b419d01SVasily Ulyanov 105698f44cb0SIgor Mitsyanko return 0; 105798f44cb0SIgor Mitsyanko } 105898f44cb0SIgor Mitsyanko 105928b91884SSergey Matyukevich static void 106028b91884SSergey Matyukevich qtnf_parse_wowlan_info(struct qtnf_wmac *mac, 106128b91884SSergey Matyukevich const struct qlink_wowlan_capab_data *wowlan) 106228b91884SSergey Matyukevich { 106328b91884SSergey Matyukevich struct qtnf_mac_info *mac_info = &mac->macinfo; 106428b91884SSergey Matyukevich const struct qlink_wowlan_support *data1; 106528b91884SSergey Matyukevich struct wiphy_wowlan_support *supp; 106628b91884SSergey Matyukevich 106728b91884SSergey Matyukevich supp = kzalloc(sizeof(*supp), GFP_KERNEL); 106828b91884SSergey Matyukevich if (!supp) 106928b91884SSergey Matyukevich return; 107028b91884SSergey Matyukevich 107128b91884SSergey Matyukevich switch (le16_to_cpu(wowlan->version)) { 107228b91884SSergey Matyukevich case 0x1: 107328b91884SSergey Matyukevich data1 = (struct qlink_wowlan_support *)wowlan->data; 107428b91884SSergey Matyukevich 107528b91884SSergey Matyukevich supp->flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT; 107628b91884SSergey Matyukevich supp->n_patterns = le32_to_cpu(data1->n_patterns); 107728b91884SSergey Matyukevich supp->pattern_max_len = le32_to_cpu(data1->pattern_max_len); 107828b91884SSergey Matyukevich supp->pattern_min_len = le32_to_cpu(data1->pattern_min_len); 107928b91884SSergey Matyukevich 108028b91884SSergey Matyukevich mac_info->wowlan = supp; 108128b91884SSergey Matyukevich break; 108228b91884SSergey Matyukevich default: 108328b91884SSergey Matyukevich pr_warn("MAC%u: unsupported WoWLAN version 0x%x\n", 108428b91884SSergey Matyukevich mac->macid, le16_to_cpu(wowlan->version)); 108528b91884SSergey Matyukevich kfree(supp); 108628b91884SSergey Matyukevich break; 108728b91884SSergey Matyukevich } 108828b91884SSergey Matyukevich } 108928b91884SSergey Matyukevich 109098f44cb0SIgor Mitsyanko static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac, 109198f44cb0SIgor Mitsyanko const u8 *tlv_buf, size_t tlv_buf_size) 109298f44cb0SIgor Mitsyanko { 1093537faf26SSergey Matyukevich struct ieee80211_iface_combination *comb = NULL; 1094537faf26SSergey Matyukevich size_t n_comb = 0; 1095537faf26SSergey Matyukevich struct ieee80211_iface_limit *limits; 1096537faf26SSergey Matyukevich const struct qlink_iface_comb_num *comb_num; 1097537faf26SSergey Matyukevich const struct qlink_iface_limit_record *rec; 1098537faf26SSergey Matyukevich const struct qlink_iface_limit *lim; 109928b91884SSergey Matyukevich const struct qlink_wowlan_capab_data *wowlan; 1100537faf26SSergey Matyukevich u16 rec_len; 1101537faf26SSergey Matyukevich u16 tlv_type; 1102537faf26SSergey Matyukevich u16 tlv_value_len; 110398f44cb0SIgor Mitsyanko size_t tlv_full_len; 110498f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 11059cbd5999SVasily Ulyanov u8 *ext_capa = NULL; 11069cbd5999SVasily Ulyanov u8 *ext_capa_mask = NULL; 11079cbd5999SVasily Ulyanov u8 ext_capa_len = 0; 11089cbd5999SVasily Ulyanov u8 ext_capa_mask_len = 0; 1109537faf26SSergey Matyukevich int i = 0; 111098f44cb0SIgor Mitsyanko 111198f44cb0SIgor Mitsyanko tlv = (const struct qlink_tlv_hdr *)tlv_buf; 111298f44cb0SIgor Mitsyanko while (tlv_buf_size >= sizeof(struct qlink_tlv_hdr)) { 111398f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 111498f44cb0SIgor Mitsyanko tlv_value_len = le16_to_cpu(tlv->len); 111598f44cb0SIgor Mitsyanko tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); 111698f44cb0SIgor Mitsyanko if (tlv_full_len > tlv_buf_size) { 111798f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV 0x%.2X; LEN: %u\n", 111898f44cb0SIgor Mitsyanko mac->macid, tlv_type, tlv_value_len); 111998f44cb0SIgor Mitsyanko return -EINVAL; 112098f44cb0SIgor Mitsyanko } 112198f44cb0SIgor Mitsyanko 112298f44cb0SIgor Mitsyanko switch (tlv_type) { 112398f44cb0SIgor Mitsyanko case QTN_TLV_ID_NUM_IFACE_COMB: 1124537faf26SSergey Matyukevich if (tlv_value_len != sizeof(*comb_num)) 112598f44cb0SIgor Mitsyanko return -EINVAL; 112698f44cb0SIgor Mitsyanko 1127537faf26SSergey Matyukevich comb_num = (void *)tlv->val; 112898f44cb0SIgor Mitsyanko 1129537faf26SSergey Matyukevich /* free earlier iface comb memory */ 1130537faf26SSergey Matyukevich qtnf_mac_iface_comb_free(mac); 113198f44cb0SIgor Mitsyanko 1132537faf26SSergey Matyukevich mac->macinfo.n_if_comb = 1133537faf26SSergey Matyukevich le32_to_cpu(comb_num->iface_comb_num); 1134537faf26SSergey Matyukevich 1135537faf26SSergey Matyukevich mac->macinfo.if_comb = 1136537faf26SSergey Matyukevich kcalloc(mac->macinfo.n_if_comb, 1137537faf26SSergey Matyukevich sizeof(*mac->macinfo.if_comb), 1138537faf26SSergey Matyukevich GFP_KERNEL); 1139537faf26SSergey Matyukevich 1140537faf26SSergey Matyukevich if (!mac->macinfo.if_comb) 114198f44cb0SIgor Mitsyanko return -ENOMEM; 114298f44cb0SIgor Mitsyanko 1143537faf26SSergey Matyukevich comb = mac->macinfo.if_comb; 1144537faf26SSergey Matyukevich 1145537faf26SSergey Matyukevich pr_debug("MAC%u: %zu iface combinations\n", 1146537faf26SSergey Matyukevich mac->macid, mac->macinfo.n_if_comb); 1147537faf26SSergey Matyukevich 114898f44cb0SIgor Mitsyanko break; 114998f44cb0SIgor Mitsyanko case QTN_TLV_ID_IFACE_LIMIT: 1150537faf26SSergey Matyukevich if (unlikely(!comb)) { 1151537faf26SSergey Matyukevich pr_warn("MAC%u: no combinations advertised\n", 115298f44cb0SIgor Mitsyanko mac->macid); 115398f44cb0SIgor Mitsyanko return -EINVAL; 115498f44cb0SIgor Mitsyanko } 115598f44cb0SIgor Mitsyanko 1156537faf26SSergey Matyukevich if (n_comb >= mac->macinfo.n_if_comb) { 1157537faf26SSergey Matyukevich pr_warn("MAC%u: combinations count exceeded\n", 115898f44cb0SIgor Mitsyanko mac->macid); 1159537faf26SSergey Matyukevich n_comb++; 1160537faf26SSergey Matyukevich break; 1161537faf26SSergey Matyukevich } 1162537faf26SSergey Matyukevich 1163537faf26SSergey Matyukevich rec = (void *)tlv->val; 1164537faf26SSergey Matyukevich rec_len = sizeof(*rec) + rec->n_limits * sizeof(*lim); 1165537faf26SSergey Matyukevich 1166537faf26SSergey Matyukevich if (unlikely(tlv_value_len != rec_len)) { 1167537faf26SSergey Matyukevich pr_warn("MAC%u: record %zu size mismatch\n", 1168537faf26SSergey Matyukevich mac->macid, n_comb); 116998f44cb0SIgor Mitsyanko return -EINVAL; 117098f44cb0SIgor Mitsyanko } 117198f44cb0SIgor Mitsyanko 11726396bb22SKees Cook limits = kcalloc(rec->n_limits, sizeof(*limits), 1173537faf26SSergey Matyukevich GFP_KERNEL); 1174537faf26SSergey Matyukevich if (!limits) 1175537faf26SSergey Matyukevich return -ENOMEM; 117641c8fa0cSSergey Matyukevich 1177537faf26SSergey Matyukevich comb[n_comb].num_different_channels = 1178537faf26SSergey Matyukevich rec->num_different_channels; 1179537faf26SSergey Matyukevich comb[n_comb].max_interfaces = 1180537faf26SSergey Matyukevich le16_to_cpu(rec->max_interfaces); 1181537faf26SSergey Matyukevich comb[n_comb].n_limits = rec->n_limits; 1182537faf26SSergey Matyukevich comb[n_comb].limits = limits; 118398f44cb0SIgor Mitsyanko 1184537faf26SSergey Matyukevich for (i = 0; i < rec->n_limits; i++) { 1185537faf26SSergey Matyukevich lim = &rec->limits[i]; 1186537faf26SSergey Matyukevich limits[i].max = le16_to_cpu(lim->max_num); 1187537faf26SSergey Matyukevich limits[i].types = 1188537faf26SSergey Matyukevich qlink_iface_type_to_nl_mask(le16_to_cpu(lim->type)); 1189537faf26SSergey Matyukevich pr_debug("MAC%u: comb[%zu]: MAX:%u TYPES:%.4X\n", 1190537faf26SSergey Matyukevich mac->macid, n_comb, 1191537faf26SSergey Matyukevich limits[i].max, limits[i].types); 1192537faf26SSergey Matyukevich } 119398f44cb0SIgor Mitsyanko 1194537faf26SSergey Matyukevich n_comb++; 119598f44cb0SIgor Mitsyanko break; 11969cbd5999SVasily Ulyanov case WLAN_EID_EXT_CAPABILITY: 11979cbd5999SVasily Ulyanov if (unlikely(tlv_value_len > U8_MAX)) 11989cbd5999SVasily Ulyanov return -EINVAL; 11999cbd5999SVasily Ulyanov ext_capa = (u8 *)tlv->val; 12009cbd5999SVasily Ulyanov ext_capa_len = tlv_value_len; 12019cbd5999SVasily Ulyanov break; 12029cbd5999SVasily Ulyanov case QTN_TLV_ID_EXT_CAPABILITY_MASK: 12039cbd5999SVasily Ulyanov if (unlikely(tlv_value_len > U8_MAX)) 12049cbd5999SVasily Ulyanov return -EINVAL; 12059cbd5999SVasily Ulyanov ext_capa_mask = (u8 *)tlv->val; 12069cbd5999SVasily Ulyanov ext_capa_mask_len = tlv_value_len; 12079cbd5999SVasily Ulyanov break; 120828b91884SSergey Matyukevich case QTN_TLV_ID_WOWLAN_CAPAB: 120928b91884SSergey Matyukevich if (tlv_value_len < sizeof(*wowlan)) 121028b91884SSergey Matyukevich return -EINVAL; 121128b91884SSergey Matyukevich 121228b91884SSergey Matyukevich wowlan = (void *)tlv->val; 121328b91884SSergey Matyukevich if (!le16_to_cpu(wowlan->len)) { 121428b91884SSergey Matyukevich pr_warn("MAC%u: skip empty WoWLAN data\n", 121528b91884SSergey Matyukevich mac->macid); 121628b91884SSergey Matyukevich break; 121728b91884SSergey Matyukevich } 121828b91884SSergey Matyukevich 121928b91884SSergey Matyukevich rec_len = sizeof(*wowlan) + le16_to_cpu(wowlan->len); 122028b91884SSergey Matyukevich if (unlikely(tlv_value_len != rec_len)) { 122128b91884SSergey Matyukevich pr_warn("MAC%u: WoWLAN data size mismatch\n", 122228b91884SSergey Matyukevich mac->macid); 122328b91884SSergey Matyukevich return -EINVAL; 122428b91884SSergey Matyukevich } 122528b91884SSergey Matyukevich 122628b91884SSergey Matyukevich kfree(mac->macinfo.wowlan); 122728b91884SSergey Matyukevich mac->macinfo.wowlan = NULL; 122828b91884SSergey Matyukevich qtnf_parse_wowlan_info(mac, wowlan); 122928b91884SSergey Matyukevich break; 123098f44cb0SIgor Mitsyanko default: 123128b91884SSergey Matyukevich pr_warn("MAC%u: unknown TLV type %u\n", 123228b91884SSergey Matyukevich mac->macid, tlv_type); 123398f44cb0SIgor Mitsyanko break; 123498f44cb0SIgor Mitsyanko } 1235805b28c0SSergey Matyukevich 123698f44cb0SIgor Mitsyanko tlv_buf_size -= tlv_full_len; 123798f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 123898f44cb0SIgor Mitsyanko } 123998f44cb0SIgor Mitsyanko 124098f44cb0SIgor Mitsyanko if (tlv_buf_size) { 124198f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV buf; bytes left: %zu\n", 124298f44cb0SIgor Mitsyanko mac->macid, tlv_buf_size); 124398f44cb0SIgor Mitsyanko return -EINVAL; 124498f44cb0SIgor Mitsyanko } 124598f44cb0SIgor Mitsyanko 1246537faf26SSergey Matyukevich if (mac->macinfo.n_if_comb != n_comb) { 124798f44cb0SIgor Mitsyanko pr_err("MAC%u: combination mismatch: reported=%zu parsed=%zu\n", 1248537faf26SSergey Matyukevich mac->macid, mac->macinfo.n_if_comb, n_comb); 124998f44cb0SIgor Mitsyanko return -EINVAL; 125098f44cb0SIgor Mitsyanko } 125198f44cb0SIgor Mitsyanko 12529cbd5999SVasily Ulyanov if (ext_capa_len != ext_capa_mask_len) { 12539cbd5999SVasily Ulyanov pr_err("MAC%u: ext_capa/_mask lengths mismatch: %u != %u\n", 12549cbd5999SVasily Ulyanov mac->macid, ext_capa_len, ext_capa_mask_len); 12559cbd5999SVasily Ulyanov return -EINVAL; 12569cbd5999SVasily Ulyanov } 12579cbd5999SVasily Ulyanov 12589cbd5999SVasily Ulyanov if (ext_capa_len > 0) { 12599cbd5999SVasily Ulyanov ext_capa = kmemdup(ext_capa, ext_capa_len, GFP_KERNEL); 12609cbd5999SVasily Ulyanov if (!ext_capa) 12619cbd5999SVasily Ulyanov return -ENOMEM; 12629cbd5999SVasily Ulyanov 12639cbd5999SVasily Ulyanov ext_capa_mask = 12649cbd5999SVasily Ulyanov kmemdup(ext_capa_mask, ext_capa_mask_len, GFP_KERNEL); 12659cbd5999SVasily Ulyanov if (!ext_capa_mask) { 12669cbd5999SVasily Ulyanov kfree(ext_capa); 12679cbd5999SVasily Ulyanov return -ENOMEM; 12689cbd5999SVasily Ulyanov } 12699cbd5999SVasily Ulyanov } else { 12709cbd5999SVasily Ulyanov ext_capa = NULL; 12719cbd5999SVasily Ulyanov ext_capa_mask = NULL; 12729cbd5999SVasily Ulyanov } 12739cbd5999SVasily Ulyanov 1274ab1c64a1SSergey Matyukevich qtnf_mac_ext_caps_free(mac); 12759cbd5999SVasily Ulyanov mac->macinfo.extended_capabilities = ext_capa; 12769cbd5999SVasily Ulyanov mac->macinfo.extended_capabilities_mask = ext_capa_mask; 12779cbd5999SVasily Ulyanov mac->macinfo.extended_capabilities_len = ext_capa_len; 12789cbd5999SVasily Ulyanov 127998f44cb0SIgor Mitsyanko return 0; 128098f44cb0SIgor Mitsyanko } 128198f44cb0SIgor Mitsyanko 128298f44cb0SIgor Mitsyanko static void 128398f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_mac_info(struct qtnf_wmac *mac, 128498f44cb0SIgor Mitsyanko const struct qlink_resp_get_mac_info *resp_info) 128598f44cb0SIgor Mitsyanko { 128698f44cb0SIgor Mitsyanko struct qtnf_mac_info *mac_info; 128798f44cb0SIgor Mitsyanko struct qtnf_vif *vif; 128898f44cb0SIgor Mitsyanko 128998f44cb0SIgor Mitsyanko mac_info = &mac->macinfo; 129098f44cb0SIgor Mitsyanko 129198f44cb0SIgor Mitsyanko mac_info->bands_cap = resp_info->bands_cap; 129298f44cb0SIgor Mitsyanko memcpy(&mac_info->dev_mac, &resp_info->dev_mac, 129398f44cb0SIgor Mitsyanko sizeof(mac_info->dev_mac)); 129498f44cb0SIgor Mitsyanko 129598f44cb0SIgor Mitsyanko ether_addr_copy(mac->macaddr, mac_info->dev_mac); 129698f44cb0SIgor Mitsyanko 129798f44cb0SIgor Mitsyanko vif = qtnf_mac_get_base_vif(mac); 129898f44cb0SIgor Mitsyanko if (vif) 129998f44cb0SIgor Mitsyanko ether_addr_copy(vif->mac_addr, mac->macaddr); 130098f44cb0SIgor Mitsyanko else 130198f44cb0SIgor Mitsyanko pr_err("could not get valid base vif\n"); 130298f44cb0SIgor Mitsyanko 130398f44cb0SIgor Mitsyanko mac_info->num_tx_chain = resp_info->num_tx_chain; 130498f44cb0SIgor Mitsyanko mac_info->num_rx_chain = resp_info->num_rx_chain; 130598f44cb0SIgor Mitsyanko 130698f44cb0SIgor Mitsyanko mac_info->max_ap_assoc_sta = le16_to_cpu(resp_info->max_ap_assoc_sta); 130798f44cb0SIgor Mitsyanko mac_info->radar_detect_widths = 130898f44cb0SIgor Mitsyanko qlink_chan_width_mask_to_nl(le16_to_cpu( 130998f44cb0SIgor Mitsyanko resp_info->radar_detect_widths)); 1310f1398fd2SVasily Ulyanov mac_info->max_acl_mac_addrs = le32_to_cpu(resp_info->max_acl_mac_addrs); 131198f44cb0SIgor Mitsyanko 1312d42df85fSIgor Mitsyanko memcpy(&mac_info->ht_cap_mod_mask, &resp_info->ht_cap_mod_mask, 1313d42df85fSIgor Mitsyanko sizeof(mac_info->ht_cap_mod_mask)); 1314d42df85fSIgor Mitsyanko memcpy(&mac_info->vht_cap_mod_mask, &resp_info->vht_cap_mod_mask, 1315d42df85fSIgor Mitsyanko sizeof(mac_info->vht_cap_mod_mask)); 131698f44cb0SIgor Mitsyanko } 131798f44cb0SIgor Mitsyanko 1318e294cbfdSIgor Mitsyanko static void qtnf_cmd_resp_band_fill_htcap(const u8 *info, 1319e294cbfdSIgor Mitsyanko struct ieee80211_sta_ht_cap *bcap) 1320e294cbfdSIgor Mitsyanko { 1321e294cbfdSIgor Mitsyanko const struct ieee80211_ht_cap *ht_cap = 1322e294cbfdSIgor Mitsyanko (const struct ieee80211_ht_cap *)info; 1323e294cbfdSIgor Mitsyanko 1324e294cbfdSIgor Mitsyanko bcap->ht_supported = true; 1325e294cbfdSIgor Mitsyanko bcap->cap = le16_to_cpu(ht_cap->cap_info); 1326e294cbfdSIgor Mitsyanko bcap->ampdu_factor = 1327e294cbfdSIgor Mitsyanko ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_FACTOR; 1328e294cbfdSIgor Mitsyanko bcap->ampdu_density = 1329e294cbfdSIgor Mitsyanko (ht_cap->ampdu_params_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 1330e294cbfdSIgor Mitsyanko IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT; 1331e294cbfdSIgor Mitsyanko memcpy(&bcap->mcs, &ht_cap->mcs, sizeof(bcap->mcs)); 1332e294cbfdSIgor Mitsyanko } 1333e294cbfdSIgor Mitsyanko 1334e294cbfdSIgor Mitsyanko static void qtnf_cmd_resp_band_fill_vhtcap(const u8 *info, 1335e294cbfdSIgor Mitsyanko struct ieee80211_sta_vht_cap *bcap) 1336e294cbfdSIgor Mitsyanko { 1337e294cbfdSIgor Mitsyanko const struct ieee80211_vht_cap *vht_cap = 1338e294cbfdSIgor Mitsyanko (const struct ieee80211_vht_cap *)info; 1339e294cbfdSIgor Mitsyanko 1340e294cbfdSIgor Mitsyanko bcap->vht_supported = true; 1341e294cbfdSIgor Mitsyanko bcap->cap = le32_to_cpu(vht_cap->vht_cap_info); 1342e294cbfdSIgor Mitsyanko memcpy(&bcap->vht_mcs, &vht_cap->supp_mcs, sizeof(bcap->vht_mcs)); 1343e294cbfdSIgor Mitsyanko } 1344e294cbfdSIgor Mitsyanko 134598f44cb0SIgor Mitsyanko static int 1346e294cbfdSIgor Mitsyanko qtnf_cmd_resp_fill_band_info(struct ieee80211_supported_band *band, 1347e294cbfdSIgor Mitsyanko struct qlink_resp_band_info_get *resp, 134898f44cb0SIgor Mitsyanko size_t payload_len) 134998f44cb0SIgor Mitsyanko { 135098f44cb0SIgor Mitsyanko u16 tlv_type; 135198f44cb0SIgor Mitsyanko size_t tlv_len; 1352e294cbfdSIgor Mitsyanko size_t tlv_dlen; 135398f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 13545bf374abSSergey Matyukevich const struct qlink_channel *qchan; 135598f44cb0SIgor Mitsyanko struct ieee80211_channel *chan; 135698f44cb0SIgor Mitsyanko unsigned int chidx = 0; 135798f44cb0SIgor Mitsyanko u32 qflags; 135898f44cb0SIgor Mitsyanko 1359e294cbfdSIgor Mitsyanko memset(&band->ht_cap, 0, sizeof(band->ht_cap)); 1360e294cbfdSIgor Mitsyanko memset(&band->vht_cap, 0, sizeof(band->vht_cap)); 1361e294cbfdSIgor Mitsyanko 13624dd07d2bSSergey Matyukevich if (band->channels) { 13634dd07d2bSSergey Matyukevich if (band->n_channels == resp->num_chans) { 13644dd07d2bSSergey Matyukevich memset(band->channels, 0, 13654dd07d2bSSergey Matyukevich sizeof(*band->channels) * band->n_channels); 13664dd07d2bSSergey Matyukevich } else { 136798f44cb0SIgor Mitsyanko kfree(band->channels); 13684dd07d2bSSergey Matyukevich band->n_channels = 0; 136998f44cb0SIgor Mitsyanko band->channels = NULL; 13704dd07d2bSSergey Matyukevich } 13714dd07d2bSSergey Matyukevich } 137298f44cb0SIgor Mitsyanko 137398f44cb0SIgor Mitsyanko band->n_channels = resp->num_chans; 137498f44cb0SIgor Mitsyanko if (band->n_channels == 0) 137598f44cb0SIgor Mitsyanko return 0; 137698f44cb0SIgor Mitsyanko 13774dd07d2bSSergey Matyukevich if (!band->channels) 13784dd07d2bSSergey Matyukevich band->channels = kcalloc(band->n_channels, sizeof(*chan), 13794dd07d2bSSergey Matyukevich GFP_KERNEL); 138098f44cb0SIgor Mitsyanko if (!band->channels) { 138198f44cb0SIgor Mitsyanko band->n_channels = 0; 138298f44cb0SIgor Mitsyanko return -ENOMEM; 138398f44cb0SIgor Mitsyanko } 138498f44cb0SIgor Mitsyanko 138598f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)resp->info; 138698f44cb0SIgor Mitsyanko 138798f44cb0SIgor Mitsyanko while (payload_len >= sizeof(*tlv)) { 138898f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 1389e294cbfdSIgor Mitsyanko tlv_dlen = le16_to_cpu(tlv->len); 1390e294cbfdSIgor Mitsyanko tlv_len = tlv_dlen + sizeof(*tlv); 139198f44cb0SIgor Mitsyanko 139298f44cb0SIgor Mitsyanko if (tlv_len > payload_len) { 139398f44cb0SIgor Mitsyanko pr_warn("malformed TLV 0x%.2X; LEN: %zu\n", 139498f44cb0SIgor Mitsyanko tlv_type, tlv_len); 139598f44cb0SIgor Mitsyanko goto error_ret; 139698f44cb0SIgor Mitsyanko } 139798f44cb0SIgor Mitsyanko 139898f44cb0SIgor Mitsyanko switch (tlv_type) { 139998f44cb0SIgor Mitsyanko case QTN_TLV_ID_CHANNEL: 14005bf374abSSergey Matyukevich if (unlikely(tlv_dlen != sizeof(*qchan))) { 140198f44cb0SIgor Mitsyanko pr_err("invalid channel TLV len %zu\n", 140298f44cb0SIgor Mitsyanko tlv_len); 140398f44cb0SIgor Mitsyanko goto error_ret; 140498f44cb0SIgor Mitsyanko } 140598f44cb0SIgor Mitsyanko 140698f44cb0SIgor Mitsyanko if (chidx == band->n_channels) { 140798f44cb0SIgor Mitsyanko pr_err("too many channel TLVs\n"); 140898f44cb0SIgor Mitsyanko goto error_ret; 140998f44cb0SIgor Mitsyanko } 141098f44cb0SIgor Mitsyanko 14115bf374abSSergey Matyukevich qchan = (const struct qlink_channel *)tlv->val; 141298f44cb0SIgor Mitsyanko chan = &band->channels[chidx++]; 141398f44cb0SIgor Mitsyanko qflags = le32_to_cpu(qchan->flags); 141498f44cb0SIgor Mitsyanko 141598f44cb0SIgor Mitsyanko chan->hw_value = le16_to_cpu(qchan->hw_value); 141698f44cb0SIgor Mitsyanko chan->band = band->band; 141798f44cb0SIgor Mitsyanko chan->center_freq = le16_to_cpu(qchan->center_freq); 141898f44cb0SIgor Mitsyanko chan->max_antenna_gain = (int)qchan->max_antenna_gain; 141998f44cb0SIgor Mitsyanko chan->max_power = (int)qchan->max_power; 142098f44cb0SIgor Mitsyanko chan->max_reg_power = (int)qchan->max_reg_power; 142198f44cb0SIgor Mitsyanko chan->beacon_found = qchan->beacon_found; 142298f44cb0SIgor Mitsyanko chan->dfs_cac_ms = le32_to_cpu(qchan->dfs_cac_ms); 142398f44cb0SIgor Mitsyanko chan->flags = 0; 142498f44cb0SIgor Mitsyanko 142598f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_DISABLED) 142698f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_DISABLED; 142798f44cb0SIgor Mitsyanko 142898f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_IR) 142998f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_IR; 143098f44cb0SIgor Mitsyanko 143198f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_HT40PLUS) 143298f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_HT40PLUS; 143398f44cb0SIgor Mitsyanko 143498f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_HT40MINUS) 143598f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_HT40MINUS; 143698f44cb0SIgor Mitsyanko 143798f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_OFDM) 143898f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_OFDM; 143998f44cb0SIgor Mitsyanko 144098f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_80MHZ) 144198f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_80MHZ; 144298f44cb0SIgor Mitsyanko 144398f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_160MHZ) 144498f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_160MHZ; 144598f44cb0SIgor Mitsyanko 144698f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_INDOOR_ONLY) 144798f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_INDOOR_ONLY; 144898f44cb0SIgor Mitsyanko 144998f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_IR_CONCURRENT) 145098f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_IR_CONCURRENT; 145198f44cb0SIgor Mitsyanko 145298f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_20MHZ) 145398f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_20MHZ; 145498f44cb0SIgor Mitsyanko 145598f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_NO_10MHZ) 145698f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_NO_10MHZ; 145798f44cb0SIgor Mitsyanko 145898f44cb0SIgor Mitsyanko if (qflags & QLINK_CHAN_RADAR) { 145998f44cb0SIgor Mitsyanko chan->flags |= IEEE80211_CHAN_RADAR; 146098f44cb0SIgor Mitsyanko chan->dfs_state_entered = jiffies; 146198f44cb0SIgor Mitsyanko 146298f44cb0SIgor Mitsyanko if (qchan->dfs_state == QLINK_DFS_USABLE) 146398f44cb0SIgor Mitsyanko chan->dfs_state = NL80211_DFS_USABLE; 146498f44cb0SIgor Mitsyanko else if (qchan->dfs_state == 146598f44cb0SIgor Mitsyanko QLINK_DFS_AVAILABLE) 146698f44cb0SIgor Mitsyanko chan->dfs_state = NL80211_DFS_AVAILABLE; 146798f44cb0SIgor Mitsyanko else 146898f44cb0SIgor Mitsyanko chan->dfs_state = 146998f44cb0SIgor Mitsyanko NL80211_DFS_UNAVAILABLE; 147098f44cb0SIgor Mitsyanko } 147198f44cb0SIgor Mitsyanko 147298f44cb0SIgor Mitsyanko pr_debug("chan=%d flags=%#x max_pow=%d max_reg_pow=%d\n", 147398f44cb0SIgor Mitsyanko chan->hw_value, chan->flags, chan->max_power, 147498f44cb0SIgor Mitsyanko chan->max_reg_power); 147598f44cb0SIgor Mitsyanko break; 1476e294cbfdSIgor Mitsyanko case WLAN_EID_HT_CAPABILITY: 1477e294cbfdSIgor Mitsyanko if (unlikely(tlv_dlen != 1478e294cbfdSIgor Mitsyanko sizeof(struct ieee80211_ht_cap))) { 1479e294cbfdSIgor Mitsyanko pr_err("bad HTCAP TLV len %zu\n", tlv_dlen); 1480e294cbfdSIgor Mitsyanko goto error_ret; 1481e294cbfdSIgor Mitsyanko } 1482e294cbfdSIgor Mitsyanko 1483e294cbfdSIgor Mitsyanko qtnf_cmd_resp_band_fill_htcap(tlv->val, &band->ht_cap); 1484e294cbfdSIgor Mitsyanko break; 1485e294cbfdSIgor Mitsyanko case WLAN_EID_VHT_CAPABILITY: 1486e294cbfdSIgor Mitsyanko if (unlikely(tlv_dlen != 1487e294cbfdSIgor Mitsyanko sizeof(struct ieee80211_vht_cap))) { 1488e294cbfdSIgor Mitsyanko pr_err("bad VHTCAP TLV len %zu\n", tlv_dlen); 1489e294cbfdSIgor Mitsyanko goto error_ret; 1490e294cbfdSIgor Mitsyanko } 1491e294cbfdSIgor Mitsyanko 1492e294cbfdSIgor Mitsyanko qtnf_cmd_resp_band_fill_vhtcap(tlv->val, 1493e294cbfdSIgor Mitsyanko &band->vht_cap); 1494e294cbfdSIgor Mitsyanko break; 149598f44cb0SIgor Mitsyanko default: 149698f44cb0SIgor Mitsyanko pr_warn("unknown TLV type: %#x\n", tlv_type); 149798f44cb0SIgor Mitsyanko break; 149898f44cb0SIgor Mitsyanko } 149998f44cb0SIgor Mitsyanko 150098f44cb0SIgor Mitsyanko payload_len -= tlv_len; 1501e294cbfdSIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_dlen); 150298f44cb0SIgor Mitsyanko } 150398f44cb0SIgor Mitsyanko 150498f44cb0SIgor Mitsyanko if (payload_len) { 150598f44cb0SIgor Mitsyanko pr_err("malformed TLV buf; bytes left: %zu\n", payload_len); 150698f44cb0SIgor Mitsyanko goto error_ret; 150798f44cb0SIgor Mitsyanko } 150898f44cb0SIgor Mitsyanko 150998f44cb0SIgor Mitsyanko if (band->n_channels != chidx) { 151098f44cb0SIgor Mitsyanko pr_err("channel count mismatch: reported=%d, parsed=%d\n", 151198f44cb0SIgor Mitsyanko band->n_channels, chidx); 151298f44cb0SIgor Mitsyanko goto error_ret; 151398f44cb0SIgor Mitsyanko } 151498f44cb0SIgor Mitsyanko 151598f44cb0SIgor Mitsyanko return 0; 151698f44cb0SIgor Mitsyanko 151798f44cb0SIgor Mitsyanko error_ret: 151898f44cb0SIgor Mitsyanko kfree(band->channels); 151998f44cb0SIgor Mitsyanko band->channels = NULL; 152098f44cb0SIgor Mitsyanko band->n_channels = 0; 152198f44cb0SIgor Mitsyanko 152298f44cb0SIgor Mitsyanko return -EINVAL; 152398f44cb0SIgor Mitsyanko } 152498f44cb0SIgor Mitsyanko 152598f44cb0SIgor Mitsyanko static int qtnf_cmd_resp_proc_phy_params(struct qtnf_wmac *mac, 152698f44cb0SIgor Mitsyanko const u8 *payload, size_t payload_len) 152798f44cb0SIgor Mitsyanko { 152898f44cb0SIgor Mitsyanko struct qtnf_mac_info *mac_info; 152998f44cb0SIgor Mitsyanko struct qlink_tlv_frag_rts_thr *phy_thr; 153098f44cb0SIgor Mitsyanko struct qlink_tlv_rlimit *limit; 153198f44cb0SIgor Mitsyanko struct qlink_tlv_cclass *class; 153298f44cb0SIgor Mitsyanko u16 tlv_type; 153398f44cb0SIgor Mitsyanko u16 tlv_value_len; 153498f44cb0SIgor Mitsyanko size_t tlv_full_len; 153598f44cb0SIgor Mitsyanko const struct qlink_tlv_hdr *tlv; 153698f44cb0SIgor Mitsyanko 153798f44cb0SIgor Mitsyanko mac_info = &mac->macinfo; 153898f44cb0SIgor Mitsyanko 153998f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)payload; 154098f44cb0SIgor Mitsyanko while (payload_len >= sizeof(struct qlink_tlv_hdr)) { 154198f44cb0SIgor Mitsyanko tlv_type = le16_to_cpu(tlv->type); 154298f44cb0SIgor Mitsyanko tlv_value_len = le16_to_cpu(tlv->len); 154398f44cb0SIgor Mitsyanko tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); 154498f44cb0SIgor Mitsyanko 154598f44cb0SIgor Mitsyanko if (tlv_full_len > payload_len) { 154698f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV 0x%.2X; LEN: %u\n", 154798f44cb0SIgor Mitsyanko mac->macid, tlv_type, tlv_value_len); 154898f44cb0SIgor Mitsyanko return -EINVAL; 154998f44cb0SIgor Mitsyanko } 155098f44cb0SIgor Mitsyanko 155198f44cb0SIgor Mitsyanko switch (tlv_type) { 155298f44cb0SIgor Mitsyanko case QTN_TLV_ID_FRAG_THRESH: 155398f44cb0SIgor Mitsyanko phy_thr = (void *)tlv; 15549fe504a1SSergey Matyukevich mac_info->frag_thr = le32_to_cpu(phy_thr->thr); 155598f44cb0SIgor Mitsyanko break; 155698f44cb0SIgor Mitsyanko case QTN_TLV_ID_RTS_THRESH: 155798f44cb0SIgor Mitsyanko phy_thr = (void *)tlv; 15589fe504a1SSergey Matyukevich mac_info->rts_thr = le32_to_cpu(phy_thr->thr); 155998f44cb0SIgor Mitsyanko break; 156098f44cb0SIgor Mitsyanko case QTN_TLV_ID_SRETRY_LIMIT: 156198f44cb0SIgor Mitsyanko limit = (void *)tlv; 156298f44cb0SIgor Mitsyanko mac_info->sretry_limit = limit->rlimit; 156398f44cb0SIgor Mitsyanko break; 156498f44cb0SIgor Mitsyanko case QTN_TLV_ID_LRETRY_LIMIT: 156598f44cb0SIgor Mitsyanko limit = (void *)tlv; 156698f44cb0SIgor Mitsyanko mac_info->lretry_limit = limit->rlimit; 156798f44cb0SIgor Mitsyanko break; 156898f44cb0SIgor Mitsyanko case QTN_TLV_ID_COVERAGE_CLASS: 156998f44cb0SIgor Mitsyanko class = (void *)tlv; 157098f44cb0SIgor Mitsyanko mac_info->coverage_class = class->cclass; 157198f44cb0SIgor Mitsyanko break; 157298f44cb0SIgor Mitsyanko default: 157398f44cb0SIgor Mitsyanko pr_err("MAC%u: Unknown TLV type: %#x\n", mac->macid, 157498f44cb0SIgor Mitsyanko le16_to_cpu(tlv->type)); 157598f44cb0SIgor Mitsyanko break; 157698f44cb0SIgor Mitsyanko } 157798f44cb0SIgor Mitsyanko 157898f44cb0SIgor Mitsyanko payload_len -= tlv_full_len; 157998f44cb0SIgor Mitsyanko tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 158098f44cb0SIgor Mitsyanko } 158198f44cb0SIgor Mitsyanko 158298f44cb0SIgor Mitsyanko if (payload_len) { 158398f44cb0SIgor Mitsyanko pr_warn("MAC%u: malformed TLV buf; bytes left: %zu\n", 158498f44cb0SIgor Mitsyanko mac->macid, payload_len); 158598f44cb0SIgor Mitsyanko return -EINVAL; 158698f44cb0SIgor Mitsyanko } 158798f44cb0SIgor Mitsyanko 158898f44cb0SIgor Mitsyanko return 0; 158998f44cb0SIgor Mitsyanko } 159098f44cb0SIgor Mitsyanko 15917c04b439SSergey Matyukevich static int 15927c04b439SSergey Matyukevich qtnf_cmd_resp_proc_chan_stat_info(struct qtnf_chan_stats *stats, 15937c04b439SSergey Matyukevich const u8 *payload, size_t payload_len) 15947c04b439SSergey Matyukevich { 15957c04b439SSergey Matyukevich struct qlink_chan_stats *qlink_stats; 15967c04b439SSergey Matyukevich const struct qlink_tlv_hdr *tlv; 15977c04b439SSergey Matyukevich size_t tlv_full_len; 15987c04b439SSergey Matyukevich u16 tlv_value_len; 15997c04b439SSergey Matyukevich u16 tlv_type; 16007c04b439SSergey Matyukevich 16017c04b439SSergey Matyukevich tlv = (struct qlink_tlv_hdr *)payload; 16027c04b439SSergey Matyukevich while (payload_len >= sizeof(struct qlink_tlv_hdr)) { 16037c04b439SSergey Matyukevich tlv_type = le16_to_cpu(tlv->type); 16047c04b439SSergey Matyukevich tlv_value_len = le16_to_cpu(tlv->len); 16057c04b439SSergey Matyukevich tlv_full_len = tlv_value_len + sizeof(struct qlink_tlv_hdr); 16067c04b439SSergey Matyukevich if (tlv_full_len > payload_len) { 16077c04b439SSergey Matyukevich pr_warn("malformed TLV 0x%.2X; LEN: %u\n", 16087c04b439SSergey Matyukevich tlv_type, tlv_value_len); 16097c04b439SSergey Matyukevich return -EINVAL; 16107c04b439SSergey Matyukevich } 16117c04b439SSergey Matyukevich switch (tlv_type) { 16127c04b439SSergey Matyukevich case QTN_TLV_ID_CHANNEL_STATS: 16137c04b439SSergey Matyukevich if (unlikely(tlv_value_len != sizeof(*qlink_stats))) { 16147c04b439SSergey Matyukevich pr_err("invalid CHANNEL_STATS entry size\n"); 16157c04b439SSergey Matyukevich return -EINVAL; 16167c04b439SSergey Matyukevich } 16177c04b439SSergey Matyukevich 16187c04b439SSergey Matyukevich qlink_stats = (void *)tlv->val; 16197c04b439SSergey Matyukevich 16207c04b439SSergey Matyukevich stats->chan_num = le32_to_cpu(qlink_stats->chan_num); 16217c04b439SSergey Matyukevich stats->cca_tx = le32_to_cpu(qlink_stats->cca_tx); 16227c04b439SSergey Matyukevich stats->cca_rx = le32_to_cpu(qlink_stats->cca_rx); 16237c04b439SSergey Matyukevich stats->cca_busy = le32_to_cpu(qlink_stats->cca_busy); 16247c04b439SSergey Matyukevich stats->cca_try = le32_to_cpu(qlink_stats->cca_try); 16257c04b439SSergey Matyukevich stats->chan_noise = qlink_stats->chan_noise; 16267c04b439SSergey Matyukevich 16277c04b439SSergey Matyukevich pr_debug("chan(%u) try(%u) busy(%u) noise(%d)\n", 16287c04b439SSergey Matyukevich stats->chan_num, stats->cca_try, 16297c04b439SSergey Matyukevich stats->cca_busy, stats->chan_noise); 16307c04b439SSergey Matyukevich break; 16317c04b439SSergey Matyukevich default: 16327c04b439SSergey Matyukevich pr_warn("Unknown TLV type: %#x\n", 16337c04b439SSergey Matyukevich le16_to_cpu(tlv->type)); 16347c04b439SSergey Matyukevich } 16357c04b439SSergey Matyukevich payload_len -= tlv_full_len; 16367c04b439SSergey Matyukevich tlv = (struct qlink_tlv_hdr *)(tlv->val + tlv_value_len); 16377c04b439SSergey Matyukevich } 16387c04b439SSergey Matyukevich 16397c04b439SSergey Matyukevich if (payload_len) { 16407c04b439SSergey Matyukevich pr_warn("malformed TLV buf; bytes left: %zu\n", payload_len); 16417c04b439SSergey Matyukevich return -EINVAL; 16427c04b439SSergey Matyukevich } 16437c04b439SSergey Matyukevich 16447c04b439SSergey Matyukevich return 0; 16457c04b439SSergey Matyukevich } 16467c04b439SSergey Matyukevich 164798f44cb0SIgor Mitsyanko int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac) 164898f44cb0SIgor Mitsyanko { 164998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 165098f44cb0SIgor Mitsyanko const struct qlink_resp_get_mac_info *resp; 16511066bd19SSergey Matyukevich size_t var_data_len = 0; 165298f44cb0SIgor Mitsyanko int ret = 0; 165398f44cb0SIgor Mitsyanko 165498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 165598f44cb0SIgor Mitsyanko QLINK_CMD_MAC_INFO, 165698f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 1657c93fe71cSSergey Matyukevich if (!cmd_skb) 165898f44cb0SIgor Mitsyanko return -ENOMEM; 165998f44cb0SIgor Mitsyanko 166098f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 1661c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, 166298f44cb0SIgor Mitsyanko sizeof(*resp), &var_data_len); 1663c6ed298fSSergey Matyukevich if (ret) 166498f44cb0SIgor Mitsyanko goto out; 166598f44cb0SIgor Mitsyanko 166698f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_mac_info *)resp_skb->data; 166798f44cb0SIgor Mitsyanko qtnf_cmd_resp_proc_mac_info(mac, resp); 166898f44cb0SIgor Mitsyanko ret = qtnf_parse_variable_mac_info(mac, resp->var_info, var_data_len); 166998f44cb0SIgor Mitsyanko 167098f44cb0SIgor Mitsyanko out: 167198f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 167298f44cb0SIgor Mitsyanko consume_skb(resp_skb); 167398f44cb0SIgor Mitsyanko 167498f44cb0SIgor Mitsyanko return ret; 167598f44cb0SIgor Mitsyanko } 167698f44cb0SIgor Mitsyanko 167798f44cb0SIgor Mitsyanko int qtnf_cmd_get_hw_info(struct qtnf_bus *bus) 167898f44cb0SIgor Mitsyanko { 167998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 168098f44cb0SIgor Mitsyanko const struct qlink_resp_get_hw_info *resp; 16811066bd19SSergey Matyukevich size_t info_len = 0; 168298f44cb0SIgor Mitsyanko int ret = 0; 168398f44cb0SIgor Mitsyanko 168498f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 168598f44cb0SIgor Mitsyanko QLINK_CMD_GET_HW_INFO, 168698f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 1687c93fe71cSSergey Matyukevich if (!cmd_skb) 168898f44cb0SIgor Mitsyanko return -ENOMEM; 168998f44cb0SIgor Mitsyanko 169098f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 1691c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, 16924dd07d2bSSergey Matyukevich sizeof(*resp), &info_len); 1693c6ed298fSSergey Matyukevich if (ret) 169498f44cb0SIgor Mitsyanko goto out; 169598f44cb0SIgor Mitsyanko 169698f44cb0SIgor Mitsyanko resp = (const struct qlink_resp_get_hw_info *)resp_skb->data; 16974dd07d2bSSergey Matyukevich ret = qtnf_cmd_resp_proc_hw_info(bus, resp, info_len); 169898f44cb0SIgor Mitsyanko 169998f44cb0SIgor Mitsyanko out: 170098f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 170198f44cb0SIgor Mitsyanko consume_skb(resp_skb); 170298f44cb0SIgor Mitsyanko 170398f44cb0SIgor Mitsyanko return ret; 170498f44cb0SIgor Mitsyanko } 170598f44cb0SIgor Mitsyanko 1706e294cbfdSIgor Mitsyanko int qtnf_cmd_band_info_get(struct qtnf_wmac *mac, 170798f44cb0SIgor Mitsyanko struct ieee80211_supported_band *band) 170898f44cb0SIgor Mitsyanko { 170998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 1710e294cbfdSIgor Mitsyanko struct qlink_cmd_band_info_get *cmd; 1711e294cbfdSIgor Mitsyanko struct qlink_resp_band_info_get *resp; 17121066bd19SSergey Matyukevich size_t info_len = 0; 171398f44cb0SIgor Mitsyanko int ret = 0; 171498f44cb0SIgor Mitsyanko u8 qband; 171598f44cb0SIgor Mitsyanko 171698f44cb0SIgor Mitsyanko switch (band->band) { 171798f44cb0SIgor Mitsyanko case NL80211_BAND_2GHZ: 171898f44cb0SIgor Mitsyanko qband = QLINK_BAND_2GHZ; 171998f44cb0SIgor Mitsyanko break; 172098f44cb0SIgor Mitsyanko case NL80211_BAND_5GHZ: 172198f44cb0SIgor Mitsyanko qband = QLINK_BAND_5GHZ; 172298f44cb0SIgor Mitsyanko break; 172398f44cb0SIgor Mitsyanko case NL80211_BAND_60GHZ: 172498f44cb0SIgor Mitsyanko qband = QLINK_BAND_60GHZ; 172598f44cb0SIgor Mitsyanko break; 172698f44cb0SIgor Mitsyanko default: 172798f44cb0SIgor Mitsyanko return -EINVAL; 172898f44cb0SIgor Mitsyanko } 172998f44cb0SIgor Mitsyanko 1730bc0384eeSColin Ian King cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 1731e294cbfdSIgor Mitsyanko QLINK_CMD_BAND_INFO_GET, 1732bc0384eeSColin Ian King sizeof(*cmd)); 1733bc0384eeSColin Ian King if (!cmd_skb) 1734bc0384eeSColin Ian King return -ENOMEM; 1735bc0384eeSColin Ian King 1736e294cbfdSIgor Mitsyanko cmd = (struct qlink_cmd_band_info_get *)cmd_skb->data; 173798f44cb0SIgor Mitsyanko cmd->band = qband; 17389ef75095SSergey Matyukevich 17399ef75095SSergey Matyukevich qtnf_bus_lock(mac->bus); 1740c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, 174198f44cb0SIgor Mitsyanko sizeof(*resp), &info_len); 1742c6ed298fSSergey Matyukevich if (ret) 174398f44cb0SIgor Mitsyanko goto out; 174498f44cb0SIgor Mitsyanko 1745e294cbfdSIgor Mitsyanko resp = (struct qlink_resp_band_info_get *)resp_skb->data; 174698f44cb0SIgor Mitsyanko if (resp->band != qband) { 174798f44cb0SIgor Mitsyanko pr_err("MAC%u: reply band %u != cmd band %u\n", mac->macid, 174898f44cb0SIgor Mitsyanko resp->band, qband); 174998f44cb0SIgor Mitsyanko ret = -EINVAL; 175098f44cb0SIgor Mitsyanko goto out; 175198f44cb0SIgor Mitsyanko } 175298f44cb0SIgor Mitsyanko 1753e294cbfdSIgor Mitsyanko ret = qtnf_cmd_resp_fill_band_info(band, resp, info_len); 175498f44cb0SIgor Mitsyanko 175598f44cb0SIgor Mitsyanko out: 17569ef75095SSergey Matyukevich qtnf_bus_unlock(mac->bus); 175798f44cb0SIgor Mitsyanko consume_skb(resp_skb); 175898f44cb0SIgor Mitsyanko 175998f44cb0SIgor Mitsyanko return ret; 176098f44cb0SIgor Mitsyanko } 176198f44cb0SIgor Mitsyanko 176298f44cb0SIgor Mitsyanko int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac) 176398f44cb0SIgor Mitsyanko { 176498f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb, *resp_skb = NULL; 176598f44cb0SIgor Mitsyanko struct qlink_resp_phy_params *resp; 17661066bd19SSergey Matyukevich size_t response_size = 0; 176798f44cb0SIgor Mitsyanko int ret = 0; 176898f44cb0SIgor Mitsyanko 176998f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 177098f44cb0SIgor Mitsyanko QLINK_CMD_PHY_PARAMS_GET, 177198f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 177298f44cb0SIgor Mitsyanko if (!cmd_skb) 177398f44cb0SIgor Mitsyanko return -ENOMEM; 177498f44cb0SIgor Mitsyanko 177598f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 1776c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, 177798f44cb0SIgor Mitsyanko sizeof(*resp), &response_size); 1778c6ed298fSSergey Matyukevich if (ret) 177998f44cb0SIgor Mitsyanko goto out; 178098f44cb0SIgor Mitsyanko 178198f44cb0SIgor Mitsyanko resp = (struct qlink_resp_phy_params *)resp_skb->data; 178298f44cb0SIgor Mitsyanko ret = qtnf_cmd_resp_proc_phy_params(mac, resp->info, response_size); 178398f44cb0SIgor Mitsyanko 178498f44cb0SIgor Mitsyanko out: 178598f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 178698f44cb0SIgor Mitsyanko consume_skb(resp_skb); 178798f44cb0SIgor Mitsyanko 178898f44cb0SIgor Mitsyanko return ret; 178998f44cb0SIgor Mitsyanko } 179098f44cb0SIgor Mitsyanko 179198f44cb0SIgor Mitsyanko int qtnf_cmd_send_update_phy_params(struct qtnf_wmac *mac, u32 changed) 179298f44cb0SIgor Mitsyanko { 179398f44cb0SIgor Mitsyanko struct wiphy *wiphy = priv_to_wiphy(mac); 179498f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 179598f44cb0SIgor Mitsyanko int ret = 0; 179698f44cb0SIgor Mitsyanko 179798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, 0, 179898f44cb0SIgor Mitsyanko QLINK_CMD_PHY_PARAMS_SET, 179998f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 180098f44cb0SIgor Mitsyanko if (!cmd_skb) 180198f44cb0SIgor Mitsyanko return -ENOMEM; 180298f44cb0SIgor Mitsyanko 180398f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 180498f44cb0SIgor Mitsyanko 180598f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_FRAG_THRESHOLD) 18069fe504a1SSergey Matyukevich qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_FRAG_THRESH, 180798f44cb0SIgor Mitsyanko wiphy->frag_threshold); 180898f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_RTS_THRESHOLD) 18099fe504a1SSergey Matyukevich qtnf_cmd_skb_put_tlv_u32(cmd_skb, QTN_TLV_ID_RTS_THRESH, 181098f44cb0SIgor Mitsyanko wiphy->rts_threshold); 181198f44cb0SIgor Mitsyanko if (changed & WIPHY_PARAM_COVERAGE_CLASS) 181298f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_COVERAGE_CLASS, 181398f44cb0SIgor Mitsyanko wiphy->coverage_class); 181498f44cb0SIgor Mitsyanko 1815f3c8bd46SSergey Matyukevich if (changed & WIPHY_PARAM_RETRY_LONG) 1816f3c8bd46SSergey Matyukevich qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_LRETRY_LIMIT, 1817f3c8bd46SSergey Matyukevich wiphy->retry_long); 1818f3c8bd46SSergey Matyukevich 1819f3c8bd46SSergey Matyukevich if (changed & WIPHY_PARAM_RETRY_SHORT) 1820f3c8bd46SSergey Matyukevich qtnf_cmd_skb_put_tlv_u8(cmd_skb, QTN_TLV_ID_SRETRY_LIMIT, 1821f3c8bd46SSergey Matyukevich wiphy->retry_short); 1822f3c8bd46SSergey Matyukevich 1823c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(mac->bus, cmd_skb); 1824c6ed298fSSergey Matyukevich if (ret) 182598f44cb0SIgor Mitsyanko goto out; 182698f44cb0SIgor Mitsyanko 182798f44cb0SIgor Mitsyanko out: 182898f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 1829c6ed298fSSergey Matyukevich 183098f44cb0SIgor Mitsyanko return ret; 183198f44cb0SIgor Mitsyanko } 183298f44cb0SIgor Mitsyanko 183398f44cb0SIgor Mitsyanko int qtnf_cmd_send_init_fw(struct qtnf_bus *bus) 183498f44cb0SIgor Mitsyanko { 183598f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 183698f44cb0SIgor Mitsyanko int ret = 0; 183798f44cb0SIgor Mitsyanko 183898f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 183998f44cb0SIgor Mitsyanko QLINK_CMD_FW_INIT, 184098f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 1841c93fe71cSSergey Matyukevich if (!cmd_skb) 184298f44cb0SIgor Mitsyanko return -ENOMEM; 184398f44cb0SIgor Mitsyanko 184498f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 1845c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 1846c6ed298fSSergey Matyukevich if (ret) 184798f44cb0SIgor Mitsyanko goto out; 184898f44cb0SIgor Mitsyanko 184998f44cb0SIgor Mitsyanko out: 185098f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 1851c6ed298fSSergey Matyukevich 185298f44cb0SIgor Mitsyanko return ret; 185398f44cb0SIgor Mitsyanko } 185498f44cb0SIgor Mitsyanko 185598f44cb0SIgor Mitsyanko void qtnf_cmd_send_deinit_fw(struct qtnf_bus *bus) 185698f44cb0SIgor Mitsyanko { 185798f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 185898f44cb0SIgor Mitsyanko 185998f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 186098f44cb0SIgor Mitsyanko QLINK_CMD_FW_DEINIT, 186198f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 186298f44cb0SIgor Mitsyanko if (!cmd_skb) 186398f44cb0SIgor Mitsyanko return; 186498f44cb0SIgor Mitsyanko 186598f44cb0SIgor Mitsyanko qtnf_bus_lock(bus); 1866c6ed298fSSergey Matyukevich qtnf_cmd_send(bus, cmd_skb); 186798f44cb0SIgor Mitsyanko qtnf_bus_unlock(bus); 186898f44cb0SIgor Mitsyanko } 186998f44cb0SIgor Mitsyanko 187098f44cb0SIgor Mitsyanko int qtnf_cmd_send_add_key(struct qtnf_vif *vif, u8 key_index, bool pairwise, 187198f44cb0SIgor Mitsyanko const u8 *mac_addr, struct key_params *params) 187298f44cb0SIgor Mitsyanko { 187398f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 187498f44cb0SIgor Mitsyanko struct qlink_cmd_add_key *cmd; 187598f44cb0SIgor Mitsyanko int ret = 0; 187698f44cb0SIgor Mitsyanko 187798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 187898f44cb0SIgor Mitsyanko QLINK_CMD_ADD_KEY, 187998f44cb0SIgor Mitsyanko sizeof(*cmd)); 1880c93fe71cSSergey Matyukevich if (!cmd_skb) 188198f44cb0SIgor Mitsyanko return -ENOMEM; 188298f44cb0SIgor Mitsyanko 188398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 188498f44cb0SIgor Mitsyanko 188598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_add_key *)cmd_skb->data; 188698f44cb0SIgor Mitsyanko 188798f44cb0SIgor Mitsyanko if (mac_addr) 188898f44cb0SIgor Mitsyanko ether_addr_copy(cmd->addr, mac_addr); 188998f44cb0SIgor Mitsyanko else 189098f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->addr); 189198f44cb0SIgor Mitsyanko 189298f44cb0SIgor Mitsyanko cmd->cipher = cpu_to_le32(params->cipher); 189398f44cb0SIgor Mitsyanko cmd->key_index = key_index; 189498f44cb0SIgor Mitsyanko cmd->pairwise = pairwise; 189598f44cb0SIgor Mitsyanko 189698f44cb0SIgor Mitsyanko if (params->key && params->key_len > 0) 189798f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_KEY, 189898f44cb0SIgor Mitsyanko params->key, 189998f44cb0SIgor Mitsyanko params->key_len); 190098f44cb0SIgor Mitsyanko 190198f44cb0SIgor Mitsyanko if (params->seq && params->seq_len > 0) 190298f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, QTN_TLV_ID_SEQ, 190398f44cb0SIgor Mitsyanko params->seq, 190498f44cb0SIgor Mitsyanko params->seq_len); 190598f44cb0SIgor Mitsyanko 1906c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 1907c6ed298fSSergey Matyukevich if (ret) 190898f44cb0SIgor Mitsyanko goto out; 190998f44cb0SIgor Mitsyanko 191098f44cb0SIgor Mitsyanko out: 191198f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 1912c6ed298fSSergey Matyukevich 191398f44cb0SIgor Mitsyanko return ret; 191498f44cb0SIgor Mitsyanko } 191598f44cb0SIgor Mitsyanko 191698f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_key(struct qtnf_vif *vif, u8 key_index, bool pairwise, 191798f44cb0SIgor Mitsyanko const u8 *mac_addr) 191898f44cb0SIgor Mitsyanko { 191998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 192098f44cb0SIgor Mitsyanko struct qlink_cmd_del_key *cmd; 192198f44cb0SIgor Mitsyanko int ret = 0; 192298f44cb0SIgor Mitsyanko 192398f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 192498f44cb0SIgor Mitsyanko QLINK_CMD_DEL_KEY, 192598f44cb0SIgor Mitsyanko sizeof(*cmd)); 1926c93fe71cSSergey Matyukevich if (!cmd_skb) 192798f44cb0SIgor Mitsyanko return -ENOMEM; 192898f44cb0SIgor Mitsyanko 192998f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 193098f44cb0SIgor Mitsyanko 193198f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_del_key *)cmd_skb->data; 193298f44cb0SIgor Mitsyanko 193398f44cb0SIgor Mitsyanko if (mac_addr) 193498f44cb0SIgor Mitsyanko ether_addr_copy(cmd->addr, mac_addr); 193598f44cb0SIgor Mitsyanko else 193698f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->addr); 193798f44cb0SIgor Mitsyanko 193898f44cb0SIgor Mitsyanko cmd->key_index = key_index; 193998f44cb0SIgor Mitsyanko cmd->pairwise = pairwise; 194098f44cb0SIgor Mitsyanko 1941c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 1942c6ed298fSSergey Matyukevich if (ret) 194398f44cb0SIgor Mitsyanko goto out; 194498f44cb0SIgor Mitsyanko 194598f44cb0SIgor Mitsyanko out: 194698f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 1947c6ed298fSSergey Matyukevich 194898f44cb0SIgor Mitsyanko return ret; 194998f44cb0SIgor Mitsyanko } 195098f44cb0SIgor Mitsyanko 195198f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_key(struct qtnf_vif *vif, u8 key_index, 195298f44cb0SIgor Mitsyanko bool unicast, bool multicast) 195398f44cb0SIgor Mitsyanko { 195498f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 195598f44cb0SIgor Mitsyanko struct qlink_cmd_set_def_key *cmd; 195698f44cb0SIgor Mitsyanko int ret = 0; 195798f44cb0SIgor Mitsyanko 195898f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 195998f44cb0SIgor Mitsyanko QLINK_CMD_SET_DEFAULT_KEY, 196098f44cb0SIgor Mitsyanko sizeof(*cmd)); 1961c93fe71cSSergey Matyukevich if (!cmd_skb) 196298f44cb0SIgor Mitsyanko return -ENOMEM; 196398f44cb0SIgor Mitsyanko 196498f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 196598f44cb0SIgor Mitsyanko 196698f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_set_def_key *)cmd_skb->data; 196798f44cb0SIgor Mitsyanko cmd->key_index = key_index; 196898f44cb0SIgor Mitsyanko cmd->unicast = unicast; 196998f44cb0SIgor Mitsyanko cmd->multicast = multicast; 197098f44cb0SIgor Mitsyanko 1971c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 1972c6ed298fSSergey Matyukevich if (ret) 197398f44cb0SIgor Mitsyanko goto out; 197498f44cb0SIgor Mitsyanko 197598f44cb0SIgor Mitsyanko out: 197698f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 1977c6ed298fSSergey Matyukevich 197898f44cb0SIgor Mitsyanko return ret; 197998f44cb0SIgor Mitsyanko } 198098f44cb0SIgor Mitsyanko 198198f44cb0SIgor Mitsyanko int qtnf_cmd_send_set_default_mgmt_key(struct qtnf_vif *vif, u8 key_index) 198298f44cb0SIgor Mitsyanko { 198398f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 198498f44cb0SIgor Mitsyanko struct qlink_cmd_set_def_mgmt_key *cmd; 198598f44cb0SIgor Mitsyanko int ret = 0; 198698f44cb0SIgor Mitsyanko 198798f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 198898f44cb0SIgor Mitsyanko QLINK_CMD_SET_DEFAULT_MGMT_KEY, 198998f44cb0SIgor Mitsyanko sizeof(*cmd)); 1990c93fe71cSSergey Matyukevich if (!cmd_skb) 199198f44cb0SIgor Mitsyanko return -ENOMEM; 199298f44cb0SIgor Mitsyanko 199398f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 199498f44cb0SIgor Mitsyanko 199598f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_set_def_mgmt_key *)cmd_skb->data; 199698f44cb0SIgor Mitsyanko cmd->key_index = key_index; 199798f44cb0SIgor Mitsyanko 1998c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 1999c6ed298fSSergey Matyukevich if (ret) 200098f44cb0SIgor Mitsyanko goto out; 200198f44cb0SIgor Mitsyanko 200298f44cb0SIgor Mitsyanko out: 200398f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 2004c6ed298fSSergey Matyukevich 200598f44cb0SIgor Mitsyanko return ret; 200698f44cb0SIgor Mitsyanko } 200798f44cb0SIgor Mitsyanko 200898f44cb0SIgor Mitsyanko static u32 qtnf_encode_sta_flags(u32 flags) 200998f44cb0SIgor Mitsyanko { 201098f44cb0SIgor Mitsyanko u32 code = 0; 201198f44cb0SIgor Mitsyanko 201298f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_AUTHORIZED)) 201398f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_AUTHORIZED; 201498f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) 201598f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_SHORT_PREAMBLE; 201698f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_WME)) 201798f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_WME; 201898f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_MFP)) 201998f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_MFP; 202098f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_AUTHENTICATED)) 202198f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_AUTHENTICATED; 202298f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_TDLS_PEER)) 202398f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_TDLS_PEER; 202498f44cb0SIgor Mitsyanko if (flags & BIT(NL80211_STA_FLAG_ASSOCIATED)) 202598f44cb0SIgor Mitsyanko code |= QLINK_STA_FLAG_ASSOCIATED; 202698f44cb0SIgor Mitsyanko return code; 202798f44cb0SIgor Mitsyanko } 202898f44cb0SIgor Mitsyanko 202998f44cb0SIgor Mitsyanko int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac, 203098f44cb0SIgor Mitsyanko struct station_parameters *params) 203198f44cb0SIgor Mitsyanko { 203298f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 203398f44cb0SIgor Mitsyanko struct qlink_cmd_change_sta *cmd; 203498f44cb0SIgor Mitsyanko int ret = 0; 203598f44cb0SIgor Mitsyanko 203698f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 203798f44cb0SIgor Mitsyanko QLINK_CMD_CHANGE_STA, 203898f44cb0SIgor Mitsyanko sizeof(*cmd)); 2039c93fe71cSSergey Matyukevich if (!cmd_skb) 204098f44cb0SIgor Mitsyanko return -ENOMEM; 204198f44cb0SIgor Mitsyanko 204298f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 204398f44cb0SIgor Mitsyanko 204498f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_change_sta *)cmd_skb->data; 204598f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, mac); 20464d2a7a1cSIgor Mitsyanko cmd->flag_update.mask = 20474d2a7a1cSIgor Mitsyanko cpu_to_le32(qtnf_encode_sta_flags(params->sta_flags_mask)); 20484d2a7a1cSIgor Mitsyanko cmd->flag_update.value = 20494d2a7a1cSIgor Mitsyanko cpu_to_le32(qtnf_encode_sta_flags(params->sta_flags_set)); 2050805b28c0SSergey Matyukevich 2051805b28c0SSergey Matyukevich switch (vif->wdev.iftype) { 2052805b28c0SSergey Matyukevich case NL80211_IFTYPE_AP: 2053805b28c0SSergey Matyukevich cmd->if_type = cpu_to_le16(QLINK_IFTYPE_AP); 2054805b28c0SSergey Matyukevich break; 2055805b28c0SSergey Matyukevich case NL80211_IFTYPE_STATION: 2056805b28c0SSergey Matyukevich cmd->if_type = cpu_to_le16(QLINK_IFTYPE_STATION); 2057805b28c0SSergey Matyukevich break; 2058805b28c0SSergey Matyukevich default: 2059805b28c0SSergey Matyukevich pr_err("unsupported iftype %d\n", vif->wdev.iftype); 2060805b28c0SSergey Matyukevich ret = -EINVAL; 2061805b28c0SSergey Matyukevich goto out; 2062805b28c0SSergey Matyukevich } 206398f44cb0SIgor Mitsyanko 2064c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 2065c6ed298fSSergey Matyukevich if (ret) 206698f44cb0SIgor Mitsyanko goto out; 206798f44cb0SIgor Mitsyanko 206898f44cb0SIgor Mitsyanko out: 206998f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 2070c6ed298fSSergey Matyukevich 207198f44cb0SIgor Mitsyanko return ret; 207298f44cb0SIgor Mitsyanko } 207398f44cb0SIgor Mitsyanko 207498f44cb0SIgor Mitsyanko int qtnf_cmd_send_del_sta(struct qtnf_vif *vif, 207598f44cb0SIgor Mitsyanko struct station_del_parameters *params) 207698f44cb0SIgor Mitsyanko { 207798f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 207898f44cb0SIgor Mitsyanko struct qlink_cmd_del_sta *cmd; 207998f44cb0SIgor Mitsyanko int ret = 0; 208098f44cb0SIgor Mitsyanko 208198f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 208298f44cb0SIgor Mitsyanko QLINK_CMD_DEL_STA, 208398f44cb0SIgor Mitsyanko sizeof(*cmd)); 2084c93fe71cSSergey Matyukevich if (!cmd_skb) 208598f44cb0SIgor Mitsyanko return -ENOMEM; 208698f44cb0SIgor Mitsyanko 208798f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 208898f44cb0SIgor Mitsyanko 208998f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_del_sta *)cmd_skb->data; 209098f44cb0SIgor Mitsyanko 209198f44cb0SIgor Mitsyanko if (params->mac) 209298f44cb0SIgor Mitsyanko ether_addr_copy(cmd->sta_addr, params->mac); 209398f44cb0SIgor Mitsyanko else 209498f44cb0SIgor Mitsyanko eth_broadcast_addr(cmd->sta_addr); /* flush all stations */ 209598f44cb0SIgor Mitsyanko 209698f44cb0SIgor Mitsyanko cmd->subtype = params->subtype; 209798f44cb0SIgor Mitsyanko cmd->reason_code = cpu_to_le16(params->reason_code); 209898f44cb0SIgor Mitsyanko 2099c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 2100c6ed298fSSergey Matyukevich if (ret) 210198f44cb0SIgor Mitsyanko goto out; 210298f44cb0SIgor Mitsyanko 210398f44cb0SIgor Mitsyanko out: 210498f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 2105c6ed298fSSergey Matyukevich 210698f44cb0SIgor Mitsyanko return ret; 210798f44cb0SIgor Mitsyanko } 210898f44cb0SIgor Mitsyanko 2109c9889671SIgor Mitsyanko static void qtnf_cmd_channel_tlv_add(struct sk_buff *cmd_skb, 2110c9889671SIgor Mitsyanko const struct ieee80211_channel *sc) 2111c9889671SIgor Mitsyanko { 2112c9889671SIgor Mitsyanko struct qlink_tlv_channel *qchan; 2113c9889671SIgor Mitsyanko u32 flags = 0; 2114c9889671SIgor Mitsyanko 2115c9889671SIgor Mitsyanko qchan = skb_put_zero(cmd_skb, sizeof(*qchan)); 2116c9889671SIgor Mitsyanko qchan->hdr.type = cpu_to_le16(QTN_TLV_ID_CHANNEL); 2117c9889671SIgor Mitsyanko qchan->hdr.len = cpu_to_le16(sizeof(*qchan) - sizeof(qchan->hdr)); 21185bf374abSSergey Matyukevich qchan->chan.center_freq = cpu_to_le16(sc->center_freq); 21195bf374abSSergey Matyukevich qchan->chan.hw_value = cpu_to_le16(sc->hw_value); 2120c9889671SIgor Mitsyanko 2121c9889671SIgor Mitsyanko if (sc->flags & IEEE80211_CHAN_NO_IR) 2122c9889671SIgor Mitsyanko flags |= QLINK_CHAN_NO_IR; 2123c9889671SIgor Mitsyanko 2124c9889671SIgor Mitsyanko if (sc->flags & IEEE80211_CHAN_RADAR) 2125c9889671SIgor Mitsyanko flags |= QLINK_CHAN_RADAR; 2126c9889671SIgor Mitsyanko 21275bf374abSSergey Matyukevich qchan->chan.flags = cpu_to_le32(flags); 2128c9889671SIgor Mitsyanko } 2129c9889671SIgor Mitsyanko 21306fbef954SAndrey Shevchenko static void qtnf_cmd_randmac_tlv_add(struct sk_buff *cmd_skb, 21316fbef954SAndrey Shevchenko const u8 *mac_addr, 21326fbef954SAndrey Shevchenko const u8 *mac_addr_mask) 21336fbef954SAndrey Shevchenko { 21346fbef954SAndrey Shevchenko struct qlink_random_mac_addr *randmac; 21356fbef954SAndrey Shevchenko struct qlink_tlv_hdr *hdr = 21366fbef954SAndrey Shevchenko skb_put(cmd_skb, sizeof(*hdr) + sizeof(*randmac)); 21376fbef954SAndrey Shevchenko 21386fbef954SAndrey Shevchenko hdr->type = cpu_to_le16(QTN_TLV_ID_RANDOM_MAC_ADDR); 21396fbef954SAndrey Shevchenko hdr->len = cpu_to_le16(sizeof(*randmac)); 21406fbef954SAndrey Shevchenko randmac = (struct qlink_random_mac_addr *)hdr->val; 21416fbef954SAndrey Shevchenko 21426fbef954SAndrey Shevchenko memcpy(randmac->mac_addr, mac_addr, ETH_ALEN); 21436fbef954SAndrey Shevchenko memcpy(randmac->mac_addr_mask, mac_addr_mask, ETH_ALEN); 21446fbef954SAndrey Shevchenko } 21456fbef954SAndrey Shevchenko 214698f44cb0SIgor Mitsyanko int qtnf_cmd_send_scan(struct qtnf_wmac *mac) 214798f44cb0SIgor Mitsyanko { 214898f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 214998f44cb0SIgor Mitsyanko struct ieee80211_channel *sc; 215098f44cb0SIgor Mitsyanko struct cfg80211_scan_request *scan_req = mac->scan_req; 215198f44cb0SIgor Mitsyanko int n_channels; 215298f44cb0SIgor Mitsyanko int count = 0; 215398f44cb0SIgor Mitsyanko int ret; 215498f44cb0SIgor Mitsyanko 215598f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 215698f44cb0SIgor Mitsyanko QLINK_CMD_SCAN, 215798f44cb0SIgor Mitsyanko sizeof(struct qlink_cmd)); 2158c93fe71cSSergey Matyukevich if (!cmd_skb) 215998f44cb0SIgor Mitsyanko return -ENOMEM; 216098f44cb0SIgor Mitsyanko 216198f44cb0SIgor Mitsyanko qtnf_bus_lock(mac->bus); 216298f44cb0SIgor Mitsyanko 216398f44cb0SIgor Mitsyanko if (scan_req->n_ssids != 0) { 216498f44cb0SIgor Mitsyanko while (count < scan_req->n_ssids) { 216598f44cb0SIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, 216698f44cb0SIgor Mitsyanko scan_req->ssids[count].ssid, 216798f44cb0SIgor Mitsyanko scan_req->ssids[count].ssid_len); 216898f44cb0SIgor Mitsyanko count++; 216998f44cb0SIgor Mitsyanko } 217098f44cb0SIgor Mitsyanko } 217198f44cb0SIgor Mitsyanko 217298f44cb0SIgor Mitsyanko if (scan_req->ie_len != 0) 217318b7470fSIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_PROBE_REQ, 217418b7470fSIgor Mitsyanko scan_req->ie, scan_req->ie_len); 217598f44cb0SIgor Mitsyanko 217698f44cb0SIgor Mitsyanko if (scan_req->n_channels) { 217798f44cb0SIgor Mitsyanko n_channels = scan_req->n_channels; 217898f44cb0SIgor Mitsyanko count = 0; 217998f44cb0SIgor Mitsyanko 218098f44cb0SIgor Mitsyanko while (n_channels != 0) { 218198f44cb0SIgor Mitsyanko sc = scan_req->channels[count]; 218298f44cb0SIgor Mitsyanko if (sc->flags & IEEE80211_CHAN_DISABLED) { 218398f44cb0SIgor Mitsyanko n_channels--; 218498f44cb0SIgor Mitsyanko continue; 218598f44cb0SIgor Mitsyanko } 218698f44cb0SIgor Mitsyanko 218798f44cb0SIgor Mitsyanko pr_debug("MAC%u: scan chan=%d, freq=%d, flags=%#x\n", 218898f44cb0SIgor Mitsyanko mac->macid, sc->hw_value, sc->center_freq, 218998f44cb0SIgor Mitsyanko sc->flags); 219098f44cb0SIgor Mitsyanko 2191c9889671SIgor Mitsyanko qtnf_cmd_channel_tlv_add(cmd_skb, sc); 219298f44cb0SIgor Mitsyanko n_channels--; 219398f44cb0SIgor Mitsyanko count++; 219498f44cb0SIgor Mitsyanko } 219598f44cb0SIgor Mitsyanko } 219698f44cb0SIgor Mitsyanko 21976fbef954SAndrey Shevchenko if (scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { 21986fbef954SAndrey Shevchenko pr_debug("MAC%u: scan with random addr=%pM, mask=%pM\n", 21996fbef954SAndrey Shevchenko mac->macid, 22006fbef954SAndrey Shevchenko scan_req->mac_addr, scan_req->mac_addr_mask); 22016fbef954SAndrey Shevchenko 22026fbef954SAndrey Shevchenko qtnf_cmd_randmac_tlv_add(cmd_skb, scan_req->mac_addr, 22036fbef954SAndrey Shevchenko scan_req->mac_addr_mask); 22046fbef954SAndrey Shevchenko } 22056fbef954SAndrey Shevchenko 22066d85930fSSergey Matyukevich if (scan_req->flags & NL80211_SCAN_FLAG_FLUSH) { 22076d85930fSSergey Matyukevich pr_debug("MAC%u: flush cache before scan\n", mac->macid); 22086d85930fSSergey Matyukevich 22096d85930fSSergey Matyukevich qtnf_cmd_skb_put_tlv_tag(cmd_skb, QTN_TLV_ID_SCAN_FLUSH); 22106d85930fSSergey Matyukevich } 22116d85930fSSergey Matyukevich 22122525f188SSergey Matyukevich if (scan_req->duration) { 22132525f188SSergey Matyukevich pr_debug("MAC%u: %s scan duration %u\n", mac->macid, 22142525f188SSergey Matyukevich scan_req->duration_mandatory ? "mandatory" : "max", 22152525f188SSergey Matyukevich scan_req->duration); 22162525f188SSergey Matyukevich 22172525f188SSergey Matyukevich qtnf_cmd_skb_put_tlv_u16(cmd_skb, QTN_TLV_ID_SCAN_DWELL, 22182525f188SSergey Matyukevich scan_req->duration); 22192525f188SSergey Matyukevich } 22202525f188SSergey Matyukevich 2221c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(mac->bus, cmd_skb); 2222c6ed298fSSergey Matyukevich if (ret) 222398f44cb0SIgor Mitsyanko goto out; 222498f44cb0SIgor Mitsyanko 222598f44cb0SIgor Mitsyanko out: 222698f44cb0SIgor Mitsyanko qtnf_bus_unlock(mac->bus); 2227c6ed298fSSergey Matyukevich 222898f44cb0SIgor Mitsyanko return ret; 222998f44cb0SIgor Mitsyanko } 223098f44cb0SIgor Mitsyanko 223198f44cb0SIgor Mitsyanko int qtnf_cmd_send_connect(struct qtnf_vif *vif, 223298f44cb0SIgor Mitsyanko struct cfg80211_connect_params *sme) 223398f44cb0SIgor Mitsyanko { 223498f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 223598f44cb0SIgor Mitsyanko struct qlink_cmd_connect *cmd; 2236d23d1361SIgor Mitsyanko struct qlink_auth_encr *aen; 223798f44cb0SIgor Mitsyanko int ret; 223898f44cb0SIgor Mitsyanko int i; 22399766d1ddSIgor Mitsyanko u32 connect_flags = 0; 224098f44cb0SIgor Mitsyanko 224198f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 224298f44cb0SIgor Mitsyanko QLINK_CMD_CONNECT, 224398f44cb0SIgor Mitsyanko sizeof(*cmd)); 2244c93fe71cSSergey Matyukevich if (!cmd_skb) 224598f44cb0SIgor Mitsyanko return -ENOMEM; 224698f44cb0SIgor Mitsyanko 224798f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_connect *)cmd_skb->data; 224898f44cb0SIgor Mitsyanko 22499766d1ddSIgor Mitsyanko ether_addr_copy(cmd->bssid, vif->bssid); 225098f44cb0SIgor Mitsyanko 2251c9889671SIgor Mitsyanko if (sme->bssid_hint) 2252c9889671SIgor Mitsyanko ether_addr_copy(cmd->bssid_hint, sme->bssid_hint); 225396d4eaf2SIgor Mitsyanko else 2254c9889671SIgor Mitsyanko eth_zero_addr(cmd->bssid_hint); 2255c9889671SIgor Mitsyanko 2256c9889671SIgor Mitsyanko if (sme->prev_bssid) 2257c9889671SIgor Mitsyanko ether_addr_copy(cmd->prev_bssid, sme->prev_bssid); 2258c9889671SIgor Mitsyanko else 2259c9889671SIgor Mitsyanko eth_zero_addr(cmd->prev_bssid); 226098f44cb0SIgor Mitsyanko 2261f5d2ff43SAndrey Shevchenko if ((sme->bg_scan_period >= 0) && 2262f5d2ff43SAndrey Shevchenko (sme->bg_scan_period <= SHRT_MAX)) 22639766d1ddSIgor Mitsyanko cmd->bg_scan_period = cpu_to_le16(sme->bg_scan_period); 22649766d1ddSIgor Mitsyanko else 2265f5d2ff43SAndrey Shevchenko cmd->bg_scan_period = cpu_to_le16(-1); /* use default value */ 22669766d1ddSIgor Mitsyanko 22679766d1ddSIgor Mitsyanko if (sme->flags & ASSOC_REQ_DISABLE_HT) 22689766d1ddSIgor Mitsyanko connect_flags |= QLINK_STA_CONNECT_DISABLE_HT; 22699766d1ddSIgor Mitsyanko if (sme->flags & ASSOC_REQ_DISABLE_VHT) 22709766d1ddSIgor Mitsyanko connect_flags |= QLINK_STA_CONNECT_DISABLE_VHT; 22719766d1ddSIgor Mitsyanko if (sme->flags & ASSOC_REQ_USE_RRM) 22729766d1ddSIgor Mitsyanko connect_flags |= QLINK_STA_CONNECT_USE_RRM; 22739766d1ddSIgor Mitsyanko 22749766d1ddSIgor Mitsyanko cmd->flags = cpu_to_le32(connect_flags); 2275c9889671SIgor Mitsyanko memcpy(&cmd->ht_capa, &sme->ht_capa, sizeof(cmd->ht_capa)); 2276c9889671SIgor Mitsyanko memcpy(&cmd->ht_capa_mask, &sme->ht_capa_mask, 2277c9889671SIgor Mitsyanko sizeof(cmd->ht_capa_mask)); 2278c9889671SIgor Mitsyanko memcpy(&cmd->vht_capa, &sme->vht_capa, sizeof(cmd->vht_capa)); 2279c9889671SIgor Mitsyanko memcpy(&cmd->vht_capa_mask, &sme->vht_capa_mask, 2280c9889671SIgor Mitsyanko sizeof(cmd->vht_capa_mask)); 2281c9889671SIgor Mitsyanko cmd->pbss = sme->pbss; 228298f44cb0SIgor Mitsyanko 2283d23d1361SIgor Mitsyanko aen = &cmd->aen; 2284d23d1361SIgor Mitsyanko aen->auth_type = sme->auth_type; 2285d23d1361SIgor Mitsyanko aen->privacy = !!sme->privacy; 2286c9889671SIgor Mitsyanko cmd->mfp = sme->mfp; 2287d23d1361SIgor Mitsyanko aen->wpa_versions = cpu_to_le32(sme->crypto.wpa_versions); 2288d23d1361SIgor Mitsyanko aen->cipher_group = cpu_to_le32(sme->crypto.cipher_group); 2289d23d1361SIgor Mitsyanko aen->n_ciphers_pairwise = cpu_to_le32(sme->crypto.n_ciphers_pairwise); 229098f44cb0SIgor Mitsyanko 229198f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_CIPHER_SUITES; i++) 2292d23d1361SIgor Mitsyanko aen->ciphers_pairwise[i] = 2293d23d1361SIgor Mitsyanko cpu_to_le32(sme->crypto.ciphers_pairwise[i]); 229498f44cb0SIgor Mitsyanko 2295d23d1361SIgor Mitsyanko aen->n_akm_suites = cpu_to_le32(sme->crypto.n_akm_suites); 229698f44cb0SIgor Mitsyanko 229798f44cb0SIgor Mitsyanko for (i = 0; i < QLINK_MAX_NR_AKM_SUITES; i++) 2298d23d1361SIgor Mitsyanko aen->akm_suites[i] = cpu_to_le32(sme->crypto.akm_suites[i]); 229998f44cb0SIgor Mitsyanko 2300d23d1361SIgor Mitsyanko aen->control_port = sme->crypto.control_port; 2301d23d1361SIgor Mitsyanko aen->control_port_no_encrypt = 23029766d1ddSIgor Mitsyanko sme->crypto.control_port_no_encrypt; 2303d23d1361SIgor Mitsyanko aen->control_port_ethertype = 2304d23d1361SIgor Mitsyanko cpu_to_le16(be16_to_cpu(sme->crypto.control_port_ethertype)); 230598f44cb0SIgor Mitsyanko 23069766d1ddSIgor Mitsyanko qtnf_cmd_skb_put_tlv_arr(cmd_skb, WLAN_EID_SSID, sme->ssid, 23079766d1ddSIgor Mitsyanko sme->ssid_len); 230898f44cb0SIgor Mitsyanko 230998f44cb0SIgor Mitsyanko if (sme->ie_len != 0) 231018b7470fSIgor Mitsyanko qtnf_cmd_tlv_ie_set_add(cmd_skb, QLINK_IE_SET_ASSOC_REQ, 231118b7470fSIgor Mitsyanko sme->ie, sme->ie_len); 231298f44cb0SIgor Mitsyanko 2313c9889671SIgor Mitsyanko if (sme->channel) 2314c9889671SIgor Mitsyanko qtnf_cmd_channel_tlv_add(cmd_skb, sme->channel); 2315c9889671SIgor Mitsyanko 2316d23d1361SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 2317c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 2318c6ed298fSSergey Matyukevich if (ret) 231998f44cb0SIgor Mitsyanko goto out; 232098f44cb0SIgor Mitsyanko 232198f44cb0SIgor Mitsyanko out: 232298f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 2323c6ed298fSSergey Matyukevich 232498f44cb0SIgor Mitsyanko return ret; 232598f44cb0SIgor Mitsyanko } 232698f44cb0SIgor Mitsyanko 232798f44cb0SIgor Mitsyanko int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, u16 reason_code) 232898f44cb0SIgor Mitsyanko { 232998f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 233098f44cb0SIgor Mitsyanko struct qlink_cmd_disconnect *cmd; 233198f44cb0SIgor Mitsyanko int ret; 233298f44cb0SIgor Mitsyanko 233398f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 233498f44cb0SIgor Mitsyanko QLINK_CMD_DISCONNECT, 233598f44cb0SIgor Mitsyanko sizeof(*cmd)); 2336c93fe71cSSergey Matyukevich if (!cmd_skb) 233798f44cb0SIgor Mitsyanko return -ENOMEM; 233898f44cb0SIgor Mitsyanko 233998f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 234098f44cb0SIgor Mitsyanko 234198f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_disconnect *)cmd_skb->data; 234298f44cb0SIgor Mitsyanko cmd->reason = cpu_to_le16(reason_code); 234398f44cb0SIgor Mitsyanko 2344c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 2345c6ed298fSSergey Matyukevich if (ret) 234698f44cb0SIgor Mitsyanko goto out; 234798f44cb0SIgor Mitsyanko 234898f44cb0SIgor Mitsyanko out: 234998f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 2350c6ed298fSSergey Matyukevich 235198f44cb0SIgor Mitsyanko return ret; 235298f44cb0SIgor Mitsyanko } 235398f44cb0SIgor Mitsyanko 235498f44cb0SIgor Mitsyanko int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, bool up) 235598f44cb0SIgor Mitsyanko { 235698f44cb0SIgor Mitsyanko struct sk_buff *cmd_skb; 235798f44cb0SIgor Mitsyanko struct qlink_cmd_updown *cmd; 235898f44cb0SIgor Mitsyanko int ret; 235998f44cb0SIgor Mitsyanko 236098f44cb0SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 236198f44cb0SIgor Mitsyanko QLINK_CMD_UPDOWN_INTF, 236298f44cb0SIgor Mitsyanko sizeof(*cmd)); 2363c93fe71cSSergey Matyukevich if (!cmd_skb) 236498f44cb0SIgor Mitsyanko return -ENOMEM; 236598f44cb0SIgor Mitsyanko 236698f44cb0SIgor Mitsyanko cmd = (struct qlink_cmd_updown *)cmd_skb->data; 236798f44cb0SIgor Mitsyanko cmd->if_up = !!up; 236898f44cb0SIgor Mitsyanko 236998f44cb0SIgor Mitsyanko qtnf_bus_lock(vif->mac->bus); 2370c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(vif->mac->bus, cmd_skb); 2371c6ed298fSSergey Matyukevich if (ret) 237298f44cb0SIgor Mitsyanko goto out; 237398f44cb0SIgor Mitsyanko 237498f44cb0SIgor Mitsyanko out: 237598f44cb0SIgor Mitsyanko qtnf_bus_unlock(vif->mac->bus); 2376c6ed298fSSergey Matyukevich 237798f44cb0SIgor Mitsyanko return ret; 237898f44cb0SIgor Mitsyanko } 23794dd07d2bSSergey Matyukevich 23804dd07d2bSSergey Matyukevich int qtnf_cmd_reg_notify(struct qtnf_bus *bus, struct regulatory_request *req) 23814dd07d2bSSergey Matyukevich { 23824dd07d2bSSergey Matyukevich struct sk_buff *cmd_skb; 23834dd07d2bSSergey Matyukevich int ret; 23844dd07d2bSSergey Matyukevich struct qlink_cmd_reg_notify *cmd; 23854dd07d2bSSergey Matyukevich 23864dd07d2bSSergey Matyukevich cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD, 23874dd07d2bSSergey Matyukevich QLINK_CMD_REG_NOTIFY, 23884dd07d2bSSergey Matyukevich sizeof(*cmd)); 23894dd07d2bSSergey Matyukevich if (!cmd_skb) 23904dd07d2bSSergey Matyukevich return -ENOMEM; 23914dd07d2bSSergey Matyukevich 23924dd07d2bSSergey Matyukevich cmd = (struct qlink_cmd_reg_notify *)cmd_skb->data; 23934dd07d2bSSergey Matyukevich cmd->alpha2[0] = req->alpha2[0]; 23944dd07d2bSSergey Matyukevich cmd->alpha2[1] = req->alpha2[1]; 23954dd07d2bSSergey Matyukevich 23964dd07d2bSSergey Matyukevich switch (req->initiator) { 23974dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_CORE: 23984dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_CORE; 23994dd07d2bSSergey Matyukevich break; 24004dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_USER: 24014dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_USER; 24024dd07d2bSSergey Matyukevich break; 24034dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_DRIVER: 24044dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_DRIVER; 24054dd07d2bSSergey Matyukevich break; 24064dd07d2bSSergey Matyukevich case NL80211_REGDOM_SET_BY_COUNTRY_IE: 24074dd07d2bSSergey Matyukevich cmd->initiator = QLINK_REGDOM_SET_BY_COUNTRY_IE; 24084dd07d2bSSergey Matyukevich break; 24094dd07d2bSSergey Matyukevich } 24104dd07d2bSSergey Matyukevich 24114dd07d2bSSergey Matyukevich switch (req->user_reg_hint_type) { 24124dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_USER: 24134dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_USER; 24144dd07d2bSSergey Matyukevich break; 24154dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_CELL_BASE: 24164dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_CELL_BASE; 24174dd07d2bSSergey Matyukevich break; 24184dd07d2bSSergey Matyukevich case NL80211_USER_REG_HINT_INDOOR: 24194dd07d2bSSergey Matyukevich cmd->user_reg_hint_type = QLINK_USER_REG_HINT_INDOOR; 24204dd07d2bSSergey Matyukevich break; 24214dd07d2bSSergey Matyukevich } 24224dd07d2bSSergey Matyukevich 24234dd07d2bSSergey Matyukevich qtnf_bus_lock(bus); 2424c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 24254dd07d2bSSergey Matyukevich if (ret) 24264dd07d2bSSergey Matyukevich goto out; 24274dd07d2bSSergey Matyukevich 24284dd07d2bSSergey Matyukevich out: 24294dd07d2bSSergey Matyukevich qtnf_bus_unlock(bus); 24304dd07d2bSSergey Matyukevich 24314dd07d2bSSergey Matyukevich return ret; 24324dd07d2bSSergey Matyukevich } 24337c04b439SSergey Matyukevich 24347c04b439SSergey Matyukevich int qtnf_cmd_get_chan_stats(struct qtnf_wmac *mac, u16 channel, 24357c04b439SSergey Matyukevich struct qtnf_chan_stats *stats) 24367c04b439SSergey Matyukevich { 24377c04b439SSergey Matyukevich struct sk_buff *cmd_skb, *resp_skb = NULL; 24387c04b439SSergey Matyukevich struct qlink_cmd_get_chan_stats *cmd; 24397c04b439SSergey Matyukevich struct qlink_resp_get_chan_stats *resp; 24401066bd19SSergey Matyukevich size_t var_data_len = 0; 24417c04b439SSergey Matyukevich int ret = 0; 24427c04b439SSergey Matyukevich 24437c04b439SSergey Matyukevich cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD, 24447c04b439SSergey Matyukevich QLINK_CMD_CHAN_STATS, 24457c04b439SSergey Matyukevich sizeof(*cmd)); 24467c04b439SSergey Matyukevich if (!cmd_skb) 24477c04b439SSergey Matyukevich return -ENOMEM; 24487c04b439SSergey Matyukevich 24497c04b439SSergey Matyukevich qtnf_bus_lock(mac->bus); 24507c04b439SSergey Matyukevich 24517c04b439SSergey Matyukevich cmd = (struct qlink_cmd_get_chan_stats *)cmd_skb->data; 24527c04b439SSergey Matyukevich cmd->channel = cpu_to_le16(channel); 24537c04b439SSergey Matyukevich 2454c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(mac->bus, cmd_skb, &resp_skb, 24557c04b439SSergey Matyukevich sizeof(*resp), &var_data_len); 2456c6ed298fSSergey Matyukevich if (ret) 24577c04b439SSergey Matyukevich goto out; 24587c04b439SSergey Matyukevich 24597c04b439SSergey Matyukevich resp = (struct qlink_resp_get_chan_stats *)resp_skb->data; 24607c04b439SSergey Matyukevich ret = qtnf_cmd_resp_proc_chan_stat_info(stats, resp->info, 24617c04b439SSergey Matyukevich var_data_len); 24627c04b439SSergey Matyukevich 24637c04b439SSergey Matyukevich out: 24647c04b439SSergey Matyukevich qtnf_bus_unlock(mac->bus); 24657c04b439SSergey Matyukevich consume_skb(resp_skb); 2466c6ed298fSSergey Matyukevich 24677c04b439SSergey Matyukevich return ret; 24687c04b439SSergey Matyukevich } 246997883695SSergey Matyukevich 24708c015b90SIgor Mitsyanko int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif, 247197883695SSergey Matyukevich struct cfg80211_csa_settings *params) 247297883695SSergey Matyukevich { 24738c015b90SIgor Mitsyanko struct qtnf_wmac *mac = vif->mac; 247497883695SSergey Matyukevich struct qlink_cmd_chan_switch *cmd; 247597883695SSergey Matyukevich struct sk_buff *cmd_skb; 247697883695SSergey Matyukevich int ret; 247797883695SSergey Matyukevich 24788c015b90SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, vif->vifid, 247997883695SSergey Matyukevich QLINK_CMD_CHAN_SWITCH, 248097883695SSergey Matyukevich sizeof(*cmd)); 2481c93fe71cSSergey Matyukevich if (!cmd_skb) 248297883695SSergey Matyukevich return -ENOMEM; 248397883695SSergey Matyukevich 248497883695SSergey Matyukevich qtnf_bus_lock(mac->bus); 248597883695SSergey Matyukevich 248697883695SSergey Matyukevich cmd = (struct qlink_cmd_chan_switch *)cmd_skb->data; 248797883695SSergey Matyukevich cmd->channel = cpu_to_le16(params->chandef.chan->hw_value); 248897883695SSergey Matyukevich cmd->radar_required = params->radar_required; 248997883695SSergey Matyukevich cmd->block_tx = params->block_tx; 249097883695SSergey Matyukevich cmd->beacon_count = params->count; 249197883695SSergey Matyukevich 2492c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(mac->bus, cmd_skb); 2493c6ed298fSSergey Matyukevich if (ret) 249497883695SSergey Matyukevich goto out; 249597883695SSergey Matyukevich 249697883695SSergey Matyukevich out: 249797883695SSergey Matyukevich qtnf_bus_unlock(mac->bus); 2498c6ed298fSSergey Matyukevich 249997883695SSergey Matyukevich return ret; 250097883695SSergey Matyukevich } 25019e5478b6SIgor Mitsyanko 25029e5478b6SIgor Mitsyanko int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef) 25039e5478b6SIgor Mitsyanko { 25049e5478b6SIgor Mitsyanko struct qtnf_bus *bus = vif->mac->bus; 25059e5478b6SIgor Mitsyanko const struct qlink_resp_channel_get *resp; 25069e5478b6SIgor Mitsyanko struct sk_buff *cmd_skb; 25079e5478b6SIgor Mitsyanko struct sk_buff *resp_skb = NULL; 25089e5478b6SIgor Mitsyanko int ret; 25099e5478b6SIgor Mitsyanko 25109e5478b6SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 25119e5478b6SIgor Mitsyanko QLINK_CMD_CHAN_GET, 25129e5478b6SIgor Mitsyanko sizeof(struct qlink_cmd)); 2513c93fe71cSSergey Matyukevich if (!cmd_skb) 25149e5478b6SIgor Mitsyanko return -ENOMEM; 25159e5478b6SIgor Mitsyanko 25169e5478b6SIgor Mitsyanko qtnf_bus_lock(bus); 2517c6ed298fSSergey Matyukevich ret = qtnf_cmd_send_with_reply(bus, cmd_skb, &resp_skb, 25189e5478b6SIgor Mitsyanko sizeof(*resp), NULL); 2519c6ed298fSSergey Matyukevich if (ret) 25209e5478b6SIgor Mitsyanko goto out; 25219e5478b6SIgor Mitsyanko 25229e5478b6SIgor Mitsyanko resp = (const struct qlink_resp_channel_get *)resp_skb->data; 25239e5478b6SIgor Mitsyanko qlink_chandef_q2cfg(priv_to_wiphy(vif->mac), &resp->chan, chdef); 25249e5478b6SIgor Mitsyanko 25259e5478b6SIgor Mitsyanko out: 2526c6ed298fSSergey Matyukevich qtnf_bus_unlock(bus); 25279e5478b6SIgor Mitsyanko consume_skb(resp_skb); 2528c6ed298fSSergey Matyukevich 25299e5478b6SIgor Mitsyanko return ret; 25309e5478b6SIgor Mitsyanko } 2531b05ee456SIgor Mitsyanko 2532b05ee456SIgor Mitsyanko int qtnf_cmd_start_cac(const struct qtnf_vif *vif, 2533b05ee456SIgor Mitsyanko const struct cfg80211_chan_def *chdef, 2534b05ee456SIgor Mitsyanko u32 cac_time_ms) 2535b05ee456SIgor Mitsyanko { 2536b05ee456SIgor Mitsyanko struct qtnf_bus *bus = vif->mac->bus; 2537b05ee456SIgor Mitsyanko struct sk_buff *cmd_skb; 2538b05ee456SIgor Mitsyanko struct qlink_cmd_start_cac *cmd; 2539b05ee456SIgor Mitsyanko int ret; 2540b05ee456SIgor Mitsyanko 2541b05ee456SIgor Mitsyanko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 2542b05ee456SIgor Mitsyanko QLINK_CMD_START_CAC, 2543b05ee456SIgor Mitsyanko sizeof(*cmd)); 2544c93fe71cSSergey Matyukevich if (!cmd_skb) 2545b05ee456SIgor Mitsyanko return -ENOMEM; 2546b05ee456SIgor Mitsyanko 2547b05ee456SIgor Mitsyanko cmd = (struct qlink_cmd_start_cac *)cmd_skb->data; 2548b05ee456SIgor Mitsyanko cmd->cac_time_ms = cpu_to_le32(cac_time_ms); 2549b05ee456SIgor Mitsyanko qlink_chandef_cfg2q(chdef, &cmd->chan); 2550b05ee456SIgor Mitsyanko 2551b05ee456SIgor Mitsyanko qtnf_bus_lock(bus); 2552c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 2553b05ee456SIgor Mitsyanko if (ret) 2554c6ed298fSSergey Matyukevich goto out; 2555b05ee456SIgor Mitsyanko 2556c6ed298fSSergey Matyukevich out: 2557c6ed298fSSergey Matyukevich qtnf_bus_unlock(bus); 2558b05ee456SIgor Mitsyanko 2559b05ee456SIgor Mitsyanko return ret; 2560b05ee456SIgor Mitsyanko } 2561f1398fd2SVasily Ulyanov 2562f1398fd2SVasily Ulyanov int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif, 2563f1398fd2SVasily Ulyanov const struct cfg80211_acl_data *params) 2564f1398fd2SVasily Ulyanov { 2565f1398fd2SVasily Ulyanov struct qtnf_bus *bus = vif->mac->bus; 2566f1398fd2SVasily Ulyanov struct sk_buff *cmd_skb; 256733f98992SVasily Ulyanov struct qlink_tlv_hdr *tlv; 256833f98992SVasily Ulyanov size_t acl_size = qtnf_cmd_acl_data_size(params); 2569f1398fd2SVasily Ulyanov int ret; 2570f1398fd2SVasily Ulyanov 2571f1398fd2SVasily Ulyanov cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 2572f1398fd2SVasily Ulyanov QLINK_CMD_SET_MAC_ACL, 257333f98992SVasily Ulyanov sizeof(struct qlink_cmd)); 2574c93fe71cSSergey Matyukevich if (!cmd_skb) 2575f1398fd2SVasily Ulyanov return -ENOMEM; 2576f1398fd2SVasily Ulyanov 257733f98992SVasily Ulyanov tlv = skb_put(cmd_skb, sizeof(*tlv) + acl_size); 257833f98992SVasily Ulyanov tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA); 257933f98992SVasily Ulyanov tlv->len = cpu_to_le16(acl_size); 258033f98992SVasily Ulyanov qlink_acl_data_cfg2q(params, (struct qlink_acl_data *)tlv->val); 2581f1398fd2SVasily Ulyanov 2582f1398fd2SVasily Ulyanov qtnf_bus_lock(bus); 2583c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 2584c6ed298fSSergey Matyukevich if (ret) 2585c6ed298fSSergey Matyukevich goto out; 2586c6ed298fSSergey Matyukevich 2587c6ed298fSSergey Matyukevich out: 2588f1398fd2SVasily Ulyanov qtnf_bus_unlock(bus); 2589f1398fd2SVasily Ulyanov 2590f1398fd2SVasily Ulyanov return ret; 2591f1398fd2SVasily Ulyanov } 25924775ad06SSergei Maksimenko 25934775ad06SSergei Maksimenko int qtnf_cmd_send_pm_set(const struct qtnf_vif *vif, u8 pm_mode, int timeout) 25944775ad06SSergei Maksimenko { 25954775ad06SSergei Maksimenko struct qtnf_bus *bus = vif->mac->bus; 25964775ad06SSergei Maksimenko struct sk_buff *cmd_skb; 25974775ad06SSergei Maksimenko struct qlink_cmd_pm_set *cmd; 25984775ad06SSergei Maksimenko int ret = 0; 25994775ad06SSergei Maksimenko 26004775ad06SSergei Maksimenko cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 26014775ad06SSergei Maksimenko QLINK_CMD_PM_SET, sizeof(*cmd)); 26024775ad06SSergei Maksimenko if (!cmd_skb) 26034775ad06SSergei Maksimenko return -ENOMEM; 26044775ad06SSergei Maksimenko 26054775ad06SSergei Maksimenko cmd = (struct qlink_cmd_pm_set *)cmd_skb->data; 26064775ad06SSergei Maksimenko cmd->pm_mode = pm_mode; 26074775ad06SSergei Maksimenko cmd->pm_standby_timer = cpu_to_le32(timeout); 26084775ad06SSergei Maksimenko 26094775ad06SSergei Maksimenko qtnf_bus_lock(bus); 26104775ad06SSergei Maksimenko 2611c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 2612c6ed298fSSergey Matyukevich if (ret) 26134775ad06SSergei Maksimenko goto out; 26144775ad06SSergei Maksimenko 26154775ad06SSergei Maksimenko out: 26164775ad06SSergei Maksimenko qtnf_bus_unlock(bus); 2617c6ed298fSSergey Matyukevich 26184775ad06SSergei Maksimenko return ret; 26194775ad06SSergei Maksimenko } 262028b91884SSergey Matyukevich 262128b91884SSergey Matyukevich int qtnf_cmd_send_wowlan_set(const struct qtnf_vif *vif, 262228b91884SSergey Matyukevich const struct cfg80211_wowlan *wowl) 262328b91884SSergey Matyukevich { 262428b91884SSergey Matyukevich struct qtnf_bus *bus = vif->mac->bus; 262528b91884SSergey Matyukevich struct sk_buff *cmd_skb; 262628b91884SSergey Matyukevich struct qlink_cmd_wowlan_set *cmd; 262728b91884SSergey Matyukevich u32 triggers = 0; 262828b91884SSergey Matyukevich int count = 0; 262928b91884SSergey Matyukevich int ret = 0; 263028b91884SSergey Matyukevich 263128b91884SSergey Matyukevich cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid, 263228b91884SSergey Matyukevich QLINK_CMD_WOWLAN_SET, sizeof(*cmd)); 263328b91884SSergey Matyukevich if (!cmd_skb) 263428b91884SSergey Matyukevich return -ENOMEM; 263528b91884SSergey Matyukevich 263628b91884SSergey Matyukevich qtnf_bus_lock(bus); 263728b91884SSergey Matyukevich 263828b91884SSergey Matyukevich cmd = (struct qlink_cmd_wowlan_set *)cmd_skb->data; 263928b91884SSergey Matyukevich 264028b91884SSergey Matyukevich if (wowl) { 264128b91884SSergey Matyukevich if (wowl->disconnect) 264228b91884SSergey Matyukevich triggers |= QLINK_WOWLAN_TRIG_DISCONNECT; 264328b91884SSergey Matyukevich 264428b91884SSergey Matyukevich if (wowl->magic_pkt) 264528b91884SSergey Matyukevich triggers |= QLINK_WOWLAN_TRIG_MAGIC_PKT; 264628b91884SSergey Matyukevich 264728b91884SSergey Matyukevich if (wowl->n_patterns && wowl->patterns) { 264828b91884SSergey Matyukevich triggers |= QLINK_WOWLAN_TRIG_PATTERN_PKT; 264928b91884SSergey Matyukevich while (count < wowl->n_patterns) { 265028b91884SSergey Matyukevich qtnf_cmd_skb_put_tlv_arr(cmd_skb, 265128b91884SSergey Matyukevich QTN_TLV_ID_WOWLAN_PATTERN, 265228b91884SSergey Matyukevich wowl->patterns[count].pattern, 265328b91884SSergey Matyukevich wowl->patterns[count].pattern_len); 265428b91884SSergey Matyukevich count++; 265528b91884SSergey Matyukevich } 265628b91884SSergey Matyukevich } 265728b91884SSergey Matyukevich } 265828b91884SSergey Matyukevich 265928b91884SSergey Matyukevich cmd->triggers = cpu_to_le32(triggers); 266028b91884SSergey Matyukevich 2661c6ed298fSSergey Matyukevich ret = qtnf_cmd_send(bus, cmd_skb); 2662c6ed298fSSergey Matyukevich if (ret) 266328b91884SSergey Matyukevich goto out; 266428b91884SSergey Matyukevich 266528b91884SSergey Matyukevich out: 266628b91884SSergey Matyukevich qtnf_bus_unlock(bus); 266728b91884SSergey Matyukevich return ret; 266828b91884SSergey Matyukevich } 2669