18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/types.h> 58c2ecf20Sopenharmony_ci#include <linux/export.h> 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "core.h" 98c2ecf20Sopenharmony_ci#include "commands.h" 108c2ecf20Sopenharmony_ci#include "event.h" 118c2ecf20Sopenharmony_ci#include "bus.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define QTNF_DEF_SYNC_CMD_TIMEOUT (5 * HZ) 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciint qtnf_trans_send_cmd_with_resp(struct qtnf_bus *bus, struct sk_buff *cmd_skb, 168c2ecf20Sopenharmony_ci struct sk_buff **response_skb) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci struct qtnf_cmd_ctl_node *ctl_node = &bus->trans.curr_cmd; 198c2ecf20Sopenharmony_ci struct qlink_cmd *cmd = (void *)cmd_skb->data; 208c2ecf20Sopenharmony_ci int ret = 0; 218c2ecf20Sopenharmony_ci long status; 228c2ecf20Sopenharmony_ci bool resp_not_handled = true; 238c2ecf20Sopenharmony_ci struct sk_buff *resp_skb = NULL; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci if (unlikely(!response_skb)) { 268c2ecf20Sopenharmony_ci dev_kfree_skb(cmd_skb); 278c2ecf20Sopenharmony_ci return -EFAULT; 288c2ecf20Sopenharmony_ci } 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci spin_lock(&ctl_node->resp_lock); 318c2ecf20Sopenharmony_ci ctl_node->seq_num++; 328c2ecf20Sopenharmony_ci cmd->seq_num = cpu_to_le16(ctl_node->seq_num); 338c2ecf20Sopenharmony_ci WARN(ctl_node->resp_skb, "qtnfmac: response skb not empty\n"); 348c2ecf20Sopenharmony_ci ctl_node->waiting_for_resp = true; 358c2ecf20Sopenharmony_ci spin_unlock(&ctl_node->resp_lock); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci ret = qtnf_bus_control_tx(bus, cmd_skb); 388c2ecf20Sopenharmony_ci dev_kfree_skb(cmd_skb); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (unlikely(ret)) 418c2ecf20Sopenharmony_ci goto out; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci status = wait_for_completion_interruptible_timeout( 448c2ecf20Sopenharmony_ci &ctl_node->cmd_resp_completion, 458c2ecf20Sopenharmony_ci QTNF_DEF_SYNC_CMD_TIMEOUT); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci spin_lock(&ctl_node->resp_lock); 488c2ecf20Sopenharmony_ci resp_not_handled = ctl_node->waiting_for_resp; 498c2ecf20Sopenharmony_ci resp_skb = ctl_node->resp_skb; 508c2ecf20Sopenharmony_ci ctl_node->resp_skb = NULL; 518c2ecf20Sopenharmony_ci ctl_node->waiting_for_resp = false; 528c2ecf20Sopenharmony_ci spin_unlock(&ctl_node->resp_lock); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (unlikely(status <= 0)) { 558c2ecf20Sopenharmony_ci if (status == 0) { 568c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 578c2ecf20Sopenharmony_ci pr_err("response timeout\n"); 588c2ecf20Sopenharmony_ci } else { 598c2ecf20Sopenharmony_ci ret = -EINTR; 608c2ecf20Sopenharmony_ci pr_debug("interrupted\n"); 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (unlikely(!resp_skb || resp_not_handled)) { 658c2ecf20Sopenharmony_ci if (!ret) 668c2ecf20Sopenharmony_ci ret = -EFAULT; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci goto out; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ret = 0; 728c2ecf20Sopenharmony_ci *response_skb = resp_skb; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ciout: 758c2ecf20Sopenharmony_ci if (unlikely(resp_skb && resp_not_handled)) 768c2ecf20Sopenharmony_ci dev_kfree_skb(resp_skb); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void qtnf_trans_signal_cmdresp(struct qtnf_bus *bus, struct sk_buff *skb) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct qtnf_cmd_ctl_node *ctl_node = &bus->trans.curr_cmd; 848c2ecf20Sopenharmony_ci const struct qlink_resp *resp = (const struct qlink_resp *)skb->data; 858c2ecf20Sopenharmony_ci const u16 recvd_seq_num = le16_to_cpu(resp->seq_num); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci spin_lock(&ctl_node->resp_lock); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (unlikely(!ctl_node->waiting_for_resp)) { 908c2ecf20Sopenharmony_ci pr_err("unexpected response\n"); 918c2ecf20Sopenharmony_ci goto out_err; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (unlikely(recvd_seq_num != ctl_node->seq_num)) { 958c2ecf20Sopenharmony_ci pr_err("seq num mismatch\n"); 968c2ecf20Sopenharmony_ci goto out_err; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ctl_node->resp_skb = skb; 1008c2ecf20Sopenharmony_ci ctl_node->waiting_for_resp = false; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci spin_unlock(&ctl_node->resp_lock); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci complete(&ctl_node->cmd_resp_completion); 1058c2ecf20Sopenharmony_ci return; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ciout_err: 1088c2ecf20Sopenharmony_ci spin_unlock(&ctl_node->resp_lock); 1098c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int qtnf_trans_event_enqueue(struct qtnf_bus *bus, struct sk_buff *skb) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct qtnf_qlink_transport *trans = &bus->trans; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (likely(skb_queue_len(&trans->event_queue) < 1178c2ecf20Sopenharmony_ci trans->event_queue_max_len)) { 1188c2ecf20Sopenharmony_ci skb_queue_tail(&trans->event_queue, skb); 1198c2ecf20Sopenharmony_ci queue_work(bus->workqueue, &bus->event_work); 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci pr_warn("event dropped due to queue overflow\n"); 1228c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1238c2ecf20Sopenharmony_ci return -1; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_civoid qtnf_trans_init(struct qtnf_bus *bus) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct qtnf_qlink_transport *trans = &bus->trans; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci init_completion(&trans->curr_cmd.cmd_resp_completion); 1348c2ecf20Sopenharmony_ci spin_lock_init(&trans->curr_cmd.resp_lock); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci spin_lock(&trans->curr_cmd.resp_lock); 1378c2ecf20Sopenharmony_ci trans->curr_cmd.seq_num = 0; 1388c2ecf20Sopenharmony_ci trans->curr_cmd.waiting_for_resp = false; 1398c2ecf20Sopenharmony_ci trans->curr_cmd.resp_skb = NULL; 1408c2ecf20Sopenharmony_ci spin_unlock(&trans->curr_cmd.resp_lock); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Init event handling related fields */ 1438c2ecf20Sopenharmony_ci skb_queue_head_init(&trans->event_queue); 1448c2ecf20Sopenharmony_ci trans->event_queue_max_len = QTNF_MAX_EVENT_QUEUE_LEN; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void qtnf_trans_free_events(struct qtnf_bus *bus) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct sk_buff_head *event_queue = &bus->trans.event_queue; 1508c2ecf20Sopenharmony_ci struct sk_buff *current_event_skb = skb_dequeue(event_queue); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci while (current_event_skb) { 1538c2ecf20Sopenharmony_ci dev_kfree_skb_any(current_event_skb); 1548c2ecf20Sopenharmony_ci current_event_skb = skb_dequeue(event_queue); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_civoid qtnf_trans_free(struct qtnf_bus *bus) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci if (!bus) { 1618c2ecf20Sopenharmony_ci pr_err("invalid bus pointer\n"); 1628c2ecf20Sopenharmony_ci return; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci qtnf_trans_free_events(bus); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciint qtnf_trans_handle_rx_ctl_packet(struct qtnf_bus *bus, struct sk_buff *skb) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci const struct qlink_msg_header *header = (void *)skb->data; 1718c2ecf20Sopenharmony_ci int ret = -1; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (unlikely(skb->len < sizeof(*header))) { 1748c2ecf20Sopenharmony_ci pr_warn("packet is too small: %u\n", skb->len); 1758c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (unlikely(skb->len != le16_to_cpu(header->len))) { 1808c2ecf20Sopenharmony_ci pr_warn("cmd reply length mismatch: %u != %u\n", 1818c2ecf20Sopenharmony_ci skb->len, le16_to_cpu(header->len)); 1828c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1838c2ecf20Sopenharmony_ci return -EFAULT; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci switch (le16_to_cpu(header->type)) { 1878c2ecf20Sopenharmony_ci case QLINK_MSG_TYPE_CMDRSP: 1888c2ecf20Sopenharmony_ci if (unlikely(skb->len < sizeof(struct qlink_cmd))) { 1898c2ecf20Sopenharmony_ci pr_warn("cmd reply too short: %u\n", skb->len); 1908c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci qtnf_trans_signal_cmdresp(bus, skb); 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case QLINK_MSG_TYPE_EVENT: 1978c2ecf20Sopenharmony_ci if (unlikely(skb->len < sizeof(struct qlink_event))) { 1988c2ecf20Sopenharmony_ci pr_warn("event too short: %u\n", skb->len); 1998c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci ret = qtnf_trans_event_enqueue(bus, skb); 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci default: 2068c2ecf20Sopenharmony_ci pr_warn("unknown packet type: %x\n", le16_to_cpu(header->type)); 2078c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return ret; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qtnf_trans_handle_rx_ctl_packet); 214