linux/drivers/net/wireless/quantenna/qtnfmac/trans.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
   3
   4#include <linux/types.h>
   5#include <linux/export.h>
   6#include <linux/slab.h>
   7
   8#include "core.h"
   9#include "commands.h"
  10#include "event.h"
  11#include "bus.h"
  12
  13#define QTNF_DEF_SYNC_CMD_TIMEOUT       (5 * HZ)
  14
  15int qtnf_trans_send_cmd_with_resp(struct qtnf_bus *bus, struct sk_buff *cmd_skb,
  16                                  struct sk_buff **response_skb)
  17{
  18        struct qtnf_cmd_ctl_node *ctl_node = &bus->trans.curr_cmd;
  19        struct qlink_cmd *cmd = (void *)cmd_skb->data;
  20        int ret = 0;
  21        long status;
  22        bool resp_not_handled = true;
  23        struct sk_buff *resp_skb = NULL;
  24
  25        if (unlikely(!response_skb)) {
  26                dev_kfree_skb(cmd_skb);
  27                return -EFAULT;
  28        }
  29
  30        spin_lock(&ctl_node->resp_lock);
  31        ctl_node->seq_num++;
  32        cmd->seq_num = cpu_to_le16(ctl_node->seq_num);
  33        WARN(ctl_node->resp_skb, "qtnfmac: response skb not empty\n");
  34        ctl_node->waiting_for_resp = true;
  35        spin_unlock(&ctl_node->resp_lock);
  36
  37        ret = qtnf_bus_control_tx(bus, cmd_skb);
  38        dev_kfree_skb(cmd_skb);
  39
  40        if (unlikely(ret))
  41                goto out;
  42
  43        status = wait_for_completion_interruptible_timeout(
  44                                                &ctl_node->cmd_resp_completion,
  45                                                QTNF_DEF_SYNC_CMD_TIMEOUT);
  46
  47        spin_lock(&ctl_node->resp_lock);
  48        resp_not_handled = ctl_node->waiting_for_resp;
  49        resp_skb = ctl_node->resp_skb;
  50        ctl_node->resp_skb = NULL;
  51        ctl_node->waiting_for_resp = false;
  52        spin_unlock(&ctl_node->resp_lock);
  53
  54        if (unlikely(status <= 0)) {
  55                if (status == 0) {
  56                        ret = -ETIMEDOUT;
  57                        pr_err("response timeout\n");
  58                } else {
  59                        ret = -EINTR;
  60                        pr_debug("interrupted\n");
  61                }
  62        }
  63
  64        if (unlikely(!resp_skb || resp_not_handled)) {
  65                if (!ret)
  66                        ret = -EFAULT;
  67
  68                goto out;
  69        }
  70
  71        ret = 0;
  72        *response_skb = resp_skb;
  73
  74out:
  75        if (unlikely(resp_skb && resp_not_handled))
  76                dev_kfree_skb(resp_skb);
  77
  78        return ret;
  79}
  80
  81static void qtnf_trans_signal_cmdresp(struct qtnf_bus *bus, struct sk_buff *skb)
  82{
  83        struct qtnf_cmd_ctl_node *ctl_node = &bus->trans.curr_cmd;
  84        const struct qlink_resp *resp = (const struct qlink_resp *)skb->data;
  85        const u16 recvd_seq_num = le16_to_cpu(resp->seq_num);
  86
  87        spin_lock(&ctl_node->resp_lock);
  88
  89        if (unlikely(!ctl_node->waiting_for_resp)) {
  90                pr_err("unexpected response\n");
  91                goto out_err;
  92        }
  93
  94        if (unlikely(recvd_seq_num != ctl_node->seq_num)) {
  95                pr_err("seq num mismatch\n");
  96                goto out_err;
  97        }
  98
  99        ctl_node->resp_skb = skb;
 100        ctl_node->waiting_for_resp = false;
 101
 102        spin_unlock(&ctl_node->resp_lock);
 103
 104        complete(&ctl_node->cmd_resp_completion);
 105        return;
 106
 107out_err:
 108        spin_unlock(&ctl_node->resp_lock);
 109        dev_kfree_skb(skb);
 110}
 111
 112static int qtnf_trans_event_enqueue(struct qtnf_bus *bus, struct sk_buff *skb)
 113{
 114        struct qtnf_qlink_transport *trans = &bus->trans;
 115
 116        if (likely(skb_queue_len(&trans->event_queue) <
 117                   trans->event_queue_max_len)) {
 118                skb_queue_tail(&trans->event_queue, skb);
 119                queue_work(bus->workqueue, &bus->event_work);
 120        } else {
 121                pr_warn("event dropped due to queue overflow\n");
 122                dev_kfree_skb(skb);
 123                return -1;
 124        }
 125
 126        return 0;
 127}
 128
 129void qtnf_trans_init(struct qtnf_bus *bus)
 130{
 131        struct qtnf_qlink_transport *trans = &bus->trans;
 132
 133        init_completion(&trans->curr_cmd.cmd_resp_completion);
 134        spin_lock_init(&trans->curr_cmd.resp_lock);
 135
 136        spin_lock(&trans->curr_cmd.resp_lock);
 137        trans->curr_cmd.seq_num = 0;
 138        trans->curr_cmd.waiting_for_resp = false;
 139        trans->curr_cmd.resp_skb = NULL;
 140        spin_unlock(&trans->curr_cmd.resp_lock);
 141
 142        /* Init event handling related fields */
 143        skb_queue_head_init(&trans->event_queue);
 144        trans->event_queue_max_len = QTNF_MAX_EVENT_QUEUE_LEN;
 145}
 146
 147static void qtnf_trans_free_events(struct qtnf_bus *bus)
 148{
 149        struct sk_buff_head *event_queue = &bus->trans.event_queue;
 150        struct sk_buff *current_event_skb = skb_dequeue(event_queue);
 151
 152        while (current_event_skb) {
 153                dev_kfree_skb_any(current_event_skb);
 154                current_event_skb = skb_dequeue(event_queue);
 155        }
 156}
 157
 158void qtnf_trans_free(struct qtnf_bus *bus)
 159{
 160        if (!bus) {
 161                pr_err("invalid bus pointer\n");
 162                return;
 163        }
 164
 165        qtnf_trans_free_events(bus);
 166}
 167
 168int qtnf_trans_handle_rx_ctl_packet(struct qtnf_bus *bus, struct sk_buff *skb)
 169{
 170        const struct qlink_msg_header *header = (void *)skb->data;
 171        int ret = -1;
 172
 173        if (unlikely(skb->len < sizeof(*header))) {
 174                pr_warn("packet is too small: %u\n", skb->len);
 175                dev_kfree_skb(skb);
 176                return -EINVAL;
 177        }
 178
 179        if (unlikely(skb->len != le16_to_cpu(header->len))) {
 180                pr_warn("cmd reply length mismatch: %u != %u\n",
 181                        skb->len, le16_to_cpu(header->len));
 182                dev_kfree_skb(skb);
 183                return -EFAULT;
 184        }
 185
 186        switch (le16_to_cpu(header->type)) {
 187        case QLINK_MSG_TYPE_CMDRSP:
 188                if (unlikely(skb->len < sizeof(struct qlink_cmd))) {
 189                        pr_warn("cmd reply too short: %u\n", skb->len);
 190                        dev_kfree_skb(skb);
 191                        break;
 192                }
 193
 194                qtnf_trans_signal_cmdresp(bus, skb);
 195                break;
 196        case QLINK_MSG_TYPE_EVENT:
 197                if (unlikely(skb->len < sizeof(struct qlink_event))) {
 198                        pr_warn("event too short: %u\n", skb->len);
 199                        dev_kfree_skb(skb);
 200                        break;
 201                }
 202
 203                ret = qtnf_trans_event_enqueue(bus, skb);
 204                break;
 205        default:
 206                pr_warn("unknown packet type: %x\n", le16_to_cpu(header->type));
 207                dev_kfree_skb(skb);
 208                break;
 209        }
 210
 211        return ret;
 212}
 213EXPORT_SYMBOL_GPL(qtnf_trans_handle_rx_ctl_packet);
 214