net:wireless:Support eswin usb wifi ECR6600U
[platform/kernel/linux-starfive.git] / drivers / net / wireless / eswin / ecrnx_cmds.c
1 /**
2  ******************************************************************************
3  *
4  * ecrnx_cmds.c
5  *
6  * Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to
7  * LMAC FW
8  *
9  * Copyright (C) ESWIN 2015-2020
10  *
11  ******************************************************************************
12  */
13
14 #include <linux/list.h>
15
16 #include "ecrnx_cmds.h"
17 #include "ecrnx_defs.h"
18 #include "ecrnx_strs.h"
19 #define CREATE_TRACE_POINTS
20 #include "ecrnx_events.h"
21
22 /**
23  *
24  */
25 static void cmd_dump(const struct ecrnx_cmd *cmd)
26 {
27 #ifndef CONFIG_ECRNX_FHOST
28     ECRNX_PRINT("tkn[%d]  flags:%04x  result:%3d  cmd:%4d-%-24s - reqcfm(%4d-%-s)\n",
29            cmd->tkn, cmd->flags, cmd->result, cmd->id, ECRNX_ID2STR(cmd->id),
30            cmd->reqid, cmd->reqid != (lmac_msg_id_t)-1 ? ECRNX_ID2STR(cmd->reqid) : "none");
31 #endif
32 }
33
34 /**
35  *
36  */
37 static void cmd_complete(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd *cmd)
38 {
39     lockdep_assert_held(&cmd_mgr->lock);
40
41     list_del(&cmd->list);
42     cmd_mgr->queue_sz--;
43
44     cmd->flags |= ECRNX_CMD_FLAG_DONE;
45     if (cmd->flags & ECRNX_CMD_FLAG_NONBLOCK) {
46         kfree(cmd);
47     } else {
48         if (ECRNX_CMD_WAIT_COMPLETE(cmd->flags)) {
49             cmd->result = 0;
50             complete(&cmd->complete);
51         }
52     }
53 }
54
55 /**
56  *
57  */
58 static int cmd_mgr_queue(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd *cmd)
59 {
60     struct ecrnx_hw *ecrnx_hw = container_of(cmd_mgr, struct ecrnx_hw, cmd_mgr);
61     bool defer_push = false;
62
63     ECRNX_DBG(ECRNX_FN_ENTRY_STR);
64     trace_msg_send(cmd->id);
65
66     spin_lock_bh(&cmd_mgr->lock);
67
68     if (cmd_mgr->state == ECRNX_CMD_MGR_STATE_CRASHED) {
69         ECRNX_PRINT(KERN_CRIT"cmd queue crashed\n");
70         cmd->result = -EPIPE;
71         spin_unlock_bh(&cmd_mgr->lock);
72         return -EPIPE;
73     }
74
75     #ifndef CONFIG_ECRNX_FHOST
76     if (!list_empty(&cmd_mgr->cmds)) {
77         struct ecrnx_cmd *last;
78
79         if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
80             ECRNX_ERR(KERN_CRIT"Too many cmds (%d) already queued\n",
81                    cmd_mgr->max_queue_sz);
82             cmd->result = -ENOMEM;
83             spin_unlock_bh(&cmd_mgr->lock);
84             return -ENOMEM;
85         }
86         last = list_entry(cmd_mgr->cmds.prev, struct ecrnx_cmd, list);
87         if (last->flags & (ECRNX_CMD_FLAG_WAIT_ACK | ECRNX_CMD_FLAG_WAIT_PUSH)) {
88 #if 0 // queue even NONBLOCK command.
89             if (cmd->flags & ECRNX_CMD_FLAG_NONBLOCK) {
90                 printk(KERN_CRIT"cmd queue busy\n");
91                 cmd->result = -EBUSY;
92                 spin_unlock_bh(&cmd_mgr->lock);
93                 return -EBUSY;
94             }
95 #endif
96             cmd->flags |= ECRNX_CMD_FLAG_WAIT_PUSH;
97             defer_push = true;
98         }
99     }
100     #endif
101
102     cmd->flags |= ECRNX_CMD_FLAG_WAIT_ACK;
103     if (cmd->flags & ECRNX_CMD_FLAG_REQ_CFM)
104         cmd->flags |= ECRNX_CMD_FLAG_WAIT_CFM;
105
106     cmd->tkn    = cmd_mgr->next_tkn++;
107     cmd->result = -EINTR;
108
109     if (!(cmd->flags & ECRNX_CMD_FLAG_NONBLOCK))
110         init_completion(&cmd->complete);
111
112     list_add_tail(&cmd->list, &cmd_mgr->cmds);
113     cmd_mgr->queue_sz++;
114     spin_unlock_bh(&cmd_mgr->lock);
115
116     if (!defer_push) {
117         ecrnx_ipc_msg_push(ecrnx_hw, cmd, ECRNX_CMD_A2EMSG_LEN(cmd->a2e_msg));
118         kfree(cmd->a2e_msg);
119     }
120
121     if (!(cmd->flags & ECRNX_CMD_FLAG_NONBLOCK)) {
122         #ifdef CONFIG_ECRNX_FHOST
123         if (wait_for_completion_killable(&cmd->complete)) {
124             if (cmd->flags & ECRNX_CMD_FLAG_WAIT_ACK)
125                 up(&ecrnx_hw->term.fw_cmd);
126             cmd->result = -EINTR;
127             spin_lock_bh(&cmd_mgr->lock);
128             cmd_complete(cmd_mgr, cmd);
129             spin_unlock_bh(&cmd_mgr->lock);
130             /* TODO: kill the cmd at fw level */
131         } else {
132             if (cmd->flags & ECRNX_CMD_FLAG_WAIT_ACK)
133                 up(&ecrnx_hw->term.fw_cmd);
134         }
135         #else
136         unsigned long tout = msecs_to_jiffies(ECRNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
137         if (!wait_for_completion_killable_timeout(&cmd->complete, tout)) {
138             ECRNX_PRINT("cmd timed-out queue_sz:%d\n",cmd_mgr->queue_sz);
139             cmd_dump(cmd);
140             spin_lock_bh(&cmd_mgr->lock);
141             //cmd_mgr->state = ECRNX_CMD_MGR_STATE_CRASHED;
142             if (!(cmd->flags & ECRNX_CMD_FLAG_DONE)) {
143                 cmd->result = -ETIMEDOUT;
144                 cmd_complete(cmd_mgr, cmd);
145             }
146             spin_unlock_bh(&cmd_mgr->lock);
147         }
148         #endif
149     } else {
150         cmd->result = 0;
151     }
152
153     return 0;
154 }
155
156 /**
157  *
158  */
159 static int cmd_mgr_llind(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd *cmd)
160 {
161     struct ecrnx_cmd *cur, *acked = NULL, *next = NULL;
162
163     ECRNX_DBG(ECRNX_FN_ENTRY_STR);
164
165     spin_lock(&cmd_mgr->lock);
166     list_for_each_entry(cur, &cmd_mgr->cmds, list) {
167         if (!acked) {
168             if (cur->tkn == cmd->tkn) {
169                 if (WARN_ON_ONCE(cur != cmd)) {
170                     cmd_dump(cmd);
171                 }
172                 acked = cur;
173                 continue;
174             }
175         }
176         if (cur->flags & ECRNX_CMD_FLAG_WAIT_PUSH) {
177                 next = cur;
178                 break;
179         }
180     }
181     if (!acked) {
182         ECRNX_PRINT(KERN_CRIT "Error: acked cmd not found\n");
183     } else {
184         cmd->flags &= ~ECRNX_CMD_FLAG_WAIT_ACK;
185         if (ECRNX_CMD_WAIT_COMPLETE(cmd->flags))
186             cmd_complete(cmd_mgr, cmd);
187     }
188     if (next) {
189         struct ecrnx_hw *ecrnx_hw = container_of(cmd_mgr, struct ecrnx_hw, cmd_mgr);
190         next->flags &= ~ECRNX_CMD_FLAG_WAIT_PUSH;
191         ecrnx_ipc_msg_push(ecrnx_hw, next, ECRNX_CMD_A2EMSG_LEN(next->a2e_msg));
192         kfree(next->a2e_msg);
193     }
194     spin_unlock(&cmd_mgr->lock);
195
196     return 0;
197 }
198
199
200
201 static int cmd_mgr_run_callback(struct ecrnx_hw *ecrnx_hw, struct ecrnx_cmd *cmd,
202                                 struct ecrnx_cmd_e2amsg *msg, msg_cb_fct cb)
203 {
204     int res;
205
206     if (! cb)
207         return 0;
208
209     spin_lock(&ecrnx_hw->cb_lock);
210     res = cb(ecrnx_hw, cmd, msg);
211     spin_unlock(&ecrnx_hw->cb_lock);
212
213     return res;
214 }
215
216 /**
217  *
218
219  */
220 static int cmd_mgr_msgind(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd_e2amsg *msg,
221                           msg_cb_fct cb)
222 {
223     struct ecrnx_hw *ecrnx_hw = container_of(cmd_mgr, struct ecrnx_hw, cmd_mgr);
224     struct ecrnx_cmd *cmd;
225     bool found = false;
226
227     ECRNX_DBG(ECRNX_FN_ENTRY_STR);
228     trace_msg_recv(msg->id);
229
230     spin_lock(&cmd_mgr->lock);
231     list_for_each_entry(cmd, &cmd_mgr->cmds, list) {
232         if (cmd->reqid == msg->id &&
233             (cmd->flags & ECRNX_CMD_FLAG_WAIT_CFM)) {
234
235             if (!cmd_mgr_run_callback(ecrnx_hw, cmd, msg, cb)) {
236                 found = true;
237                 cmd->flags &= ~ECRNX_CMD_FLAG_WAIT_CFM;
238
239                 if (WARN((msg->param_len > ECRNX_CMD_E2AMSG_LEN_MAX),
240                          "Unexpect E2A msg len %d > %d\n", msg->param_len,
241                          ECRNX_CMD_E2AMSG_LEN_MAX)) {
242                     msg->param_len = ECRNX_CMD_E2AMSG_LEN_MAX;
243                 }
244
245                 if (cmd->e2a_msg && msg->param_len)
246                     memcpy(cmd->e2a_msg, &msg->param, msg->param_len);
247
248                 if (ECRNX_CMD_WAIT_COMPLETE(cmd->flags))
249                     cmd_complete(cmd_mgr, cmd);
250
251                 break;
252             }
253         }
254     }
255     spin_unlock(&cmd_mgr->lock);
256
257     if (!found)
258         cmd_mgr_run_callback(ecrnx_hw, NULL, msg, cb);
259
260     ECRNX_DBG("%s exit!! \n", __func__);
261     return 0;
262 }
263
264 /**
265  *
266  */
267 static void cmd_mgr_print(struct ecrnx_cmd_mgr *cmd_mgr)
268 {
269     struct ecrnx_cmd *cur;
270
271     spin_lock_bh(&cmd_mgr->lock);
272     ECRNX_PRINT("q_sz/max: %2d / %2d - next tkn: %d\n",
273              cmd_mgr->queue_sz, cmd_mgr->max_queue_sz,
274              cmd_mgr->next_tkn);
275     list_for_each_entry(cur, &cmd_mgr->cmds, list) {
276         cmd_dump(cur);
277     }
278     spin_unlock_bh(&cmd_mgr->lock);
279 }
280
281 /**
282  *
283  */
284 static void cmd_mgr_drain(struct ecrnx_cmd_mgr *cmd_mgr)
285 {
286     struct ecrnx_cmd *cur, *nxt;
287
288     ECRNX_DBG(ECRNX_FN_ENTRY_STR);
289
290     spin_lock_bh(&cmd_mgr->lock);
291     list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) {
292         list_del(&cur->list);
293         cmd_mgr->queue_sz--;
294         if (!(cur->flags & ECRNX_CMD_FLAG_NONBLOCK))
295             complete(&cur->complete);
296     }
297     spin_unlock_bh(&cmd_mgr->lock);
298 }
299
300 /**
301  *
302  */
303 void ecrnx_cmd_mgr_init(struct ecrnx_cmd_mgr *cmd_mgr)
304 {
305     ECRNX_DBG(ECRNX_FN_ENTRY_STR);
306
307     INIT_LIST_HEAD(&cmd_mgr->cmds);
308     spin_lock_init(&cmd_mgr->lock);
309     cmd_mgr->max_queue_sz = ECRNX_CMD_MAX_QUEUED;
310     cmd_mgr->queue  = &cmd_mgr_queue;
311     cmd_mgr->print  = &cmd_mgr_print;
312     cmd_mgr->drain  = &cmd_mgr_drain;
313     cmd_mgr->llind  = &cmd_mgr_llind;
314     cmd_mgr->msgind = &cmd_mgr_msgind;
315 }
316
317 /**
318  *
319  */
320 void ecrnx_cmd_mgr_deinit(struct ecrnx_cmd_mgr *cmd_mgr)
321 {
322     cmd_mgr->print(cmd_mgr);
323     cmd_mgr->drain(cmd_mgr);
324     memset(cmd_mgr, 0, sizeof(*cmd_mgr));
325 }