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 | |
15 | int 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(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(lock: &ctl_node->resp_lock); |
36 | |
37 | ret = qtnf_bus_control_tx(bus, skb: 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 | x: &ctl_node->cmd_resp_completion, |
45 | QTNF_DEF_SYNC_CMD_TIMEOUT); |
46 | |
47 | spin_lock(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(lock: &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 | |
74 | out: |
75 | if (unlikely(resp_skb && resp_not_handled)) |
76 | dev_kfree_skb(resp_skb); |
77 | |
78 | return ret; |
79 | } |
80 | |
81 | static 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(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(lock: &ctl_node->resp_lock); |
103 | |
104 | complete(&ctl_node->cmd_resp_completion); |
105 | return; |
106 | |
107 | out_err: |
108 | spin_unlock(lock: &ctl_node->resp_lock); |
109 | dev_kfree_skb(skb); |
110 | } |
111 | |
112 | static 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(list: &trans->event_queue, newsk: skb); |
119 | queue_work(wq: bus->workqueue, work: &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 | |
129 | void qtnf_trans_init(struct qtnf_bus *bus) |
130 | { |
131 | struct qtnf_qlink_transport *trans = &bus->trans; |
132 | |
133 | init_completion(x: &trans->curr_cmd.cmd_resp_completion); |
134 | spin_lock_init(&trans->curr_cmd.resp_lock); |
135 | |
136 | spin_lock(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(lock: &trans->curr_cmd.resp_lock); |
141 | |
142 | /* Init event handling related fields */ |
143 | skb_queue_head_init(list: &trans->event_queue); |
144 | trans->event_queue_max_len = QTNF_MAX_EVENT_QUEUE_LEN; |
145 | } |
146 | |
147 | static 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(list: event_queue); |
151 | |
152 | while (current_event_skb) { |
153 | dev_kfree_skb_any(skb: current_event_skb); |
154 | current_event_skb = skb_dequeue(list: event_queue); |
155 | } |
156 | } |
157 | |
158 | void 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 | |
168 | int qtnf_trans_handle_rx_ctl_packet(struct qtnf_bus *bus, struct sk_buff *skb) |
169 | { |
170 | const struct qlink_msg_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 | } |
213 | EXPORT_SYMBOL_GPL(qtnf_trans_handle_rx_ctl_packet); |
214 | |