brintos

brintos / linux-shallow public Read only

0
0
Text · 5.0 KiB · 95356e2 Raw
214 lines · c
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