blob: c814ce9bb3de47f513777f4be0388a380a34ca77 [file] [log] [blame]
Gavin Shan2d283bd2016-07-19 11:54:16 +10001/*
2 * Copyright Gavin Shan, IBM Corporation 2016.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10#include <linux/module.h>
11#include <linux/kernel.h>
12#include <linux/init.h>
13#include <linux/netdevice.h>
14#include <linux/skbuff.h>
Gavin Shan2d283bd2016-07-19 11:54:16 +100015
16#include <net/ncsi.h>
17#include <net/net_namespace.h>
18#include <net/sock.h>
Gavin Shane6f44ed2016-07-19 11:54:19 +100019#include <net/addrconf.h>
20#include <net/ipv6.h>
21#include <net/if_inet6.h>
Justin.Lee1@Dell.com9771b8c2018-10-11 18:07:37 +000022#include <net/genetlink.h>
Gavin Shan2d283bd2016-07-19 11:54:16 +100023
24#include "internal.h"
Gavin Shane6f44ed2016-07-19 11:54:19 +100025#include "ncsi-pkt.h"
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +110026#include "ncsi-netlink.h"
Gavin Shan2d283bd2016-07-19 11:54:16 +100027
28LIST_HEAD(ncsi_dev_list);
29DEFINE_SPINLOCK(ncsi_dev_lock);
30
Gavin Shane6f44ed2016-07-19 11:54:19 +100031static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down)
32{
33 struct ncsi_dev *nd = &ndp->ndev;
34 struct ncsi_package *np;
35 struct ncsi_channel *nc;
Gavin Shand8cedaa2016-10-04 11:25:47 +110036 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +100037
38 nd->state = ncsi_dev_state_functional;
39 if (force_down) {
40 nd->link_up = 0;
41 goto report;
42 }
43
44 nd->link_up = 0;
45 NCSI_FOR_EACH_PACKAGE(ndp, np) {
46 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shand8cedaa2016-10-04 11:25:47 +110047 spin_lock_irqsave(&nc->lock, flags);
48
Gavin Shane6f44ed2016-07-19 11:54:19 +100049 if (!list_empty(&nc->link) ||
Gavin Shand8cedaa2016-10-04 11:25:47 +110050 nc->state != NCSI_CHANNEL_ACTIVE) {
51 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +100052 continue;
Gavin Shand8cedaa2016-10-04 11:25:47 +110053 }
Gavin Shane6f44ed2016-07-19 11:54:19 +100054
55 if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
Gavin Shand8cedaa2016-10-04 11:25:47 +110056 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +100057 nd->link_up = 1;
58 goto report;
59 }
Gavin Shand8cedaa2016-10-04 11:25:47 +110060
61 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +100062 }
63 }
64
65report:
66 nd->handler(nd);
67}
68
Kees Cook86cb30e2017-10-17 20:21:24 -070069static void ncsi_channel_monitor(struct timer_list *t)
Gavin Shane6f44ed2016-07-19 11:54:19 +100070{
Kees Cook86cb30e2017-10-17 20:21:24 -070071 struct ncsi_channel *nc = from_timer(nc, t, monitor.timer);
Gavin Shane6f44ed2016-07-19 11:54:19 +100072 struct ncsi_package *np = nc->package;
73 struct ncsi_dev_priv *ndp = np->ndp;
Gavin Shan52b4c862017-10-19 13:43:08 +110074 struct ncsi_channel_mode *ncm;
Gavin Shane6f44ed2016-07-19 11:54:19 +100075 struct ncsi_cmd_arg nca;
Gavin Shand8cedaa2016-10-04 11:25:47 +110076 bool enabled, chained;
Gavin Shan83afdc62016-10-04 11:25:52 +110077 unsigned int monitor_state;
Gavin Shane6f44ed2016-07-19 11:54:19 +100078 unsigned long flags;
Gavin Shand8cedaa2016-10-04 11:25:47 +110079 int state, ret;
Gavin Shane6f44ed2016-07-19 11:54:19 +100080
81 spin_lock_irqsave(&nc->lock, flags);
Gavin Shand8cedaa2016-10-04 11:25:47 +110082 state = nc->state;
83 chained = !list_empty(&nc->link);
Gavin Shan83afdc62016-10-04 11:25:52 +110084 enabled = nc->monitor.enabled;
85 monitor_state = nc->monitor.state;
Gavin Shane6f44ed2016-07-19 11:54:19 +100086 spin_unlock_irqrestore(&nc->lock, flags);
87
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +110088 if (!enabled || chained) {
89 ncsi_stop_channel_monitor(nc);
Gavin Shane6f44ed2016-07-19 11:54:19 +100090 return;
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +110091 }
Gavin Shand8cedaa2016-10-04 11:25:47 +110092 if (state != NCSI_CHANNEL_INACTIVE &&
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +110093 state != NCSI_CHANNEL_ACTIVE) {
94 ncsi_stop_channel_monitor(nc);
Gavin Shane6f44ed2016-07-19 11:54:19 +100095 return;
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +110096 }
Gavin Shane6f44ed2016-07-19 11:54:19 +100097
Gavin Shan83afdc62016-10-04 11:25:52 +110098 switch (monitor_state) {
99 case NCSI_CHANNEL_MONITOR_START:
100 case NCSI_CHANNEL_MONITOR_RETRY:
Gavin Shane6f44ed2016-07-19 11:54:19 +1000101 nca.ndp = ndp;
102 nca.package = np->id;
103 nca.channel = nc->id;
104 nca.type = NCSI_PKT_CMD_GLS;
Gavin Shana0509cb2016-10-04 11:25:51 +1100105 nca.req_flags = 0;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000106 ret = ncsi_xmit_cmd(&nca);
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100107 if (ret)
Gavin Shane6f44ed2016-07-19 11:54:19 +1000108 netdev_err(ndp->ndev.dev, "Error %d sending GLS\n",
109 ret);
Gavin Shan83afdc62016-10-04 11:25:52 +1100110 break;
111 case NCSI_CHANNEL_MONITOR_WAIT ... NCSI_CHANNEL_MONITOR_WAIT_MAX:
112 break;
113 default:
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100114 netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n",
115 nc->id);
Samuel Mendoza-Jonas60ab49b2018-11-16 15:51:54 +1100116 ncsi_report_link(ndp, true);
117 ndp->flags |= NCSI_DEV_RESHUFFLE;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000118
Samuel Mendoza-Jonas0795fb22017-10-19 13:43:06 +1100119 ncsi_stop_channel_monitor(nc);
120
Gavin Shan52b4c862017-10-19 13:43:08 +1100121 ncm = &nc->modes[NCSI_MODE_LINK];
Gavin Shand8cedaa2016-10-04 11:25:47 +1100122 spin_lock_irqsave(&nc->lock, flags);
123 nc->state = NCSI_CHANNEL_INVISIBLE;
Gavin Shan52b4c862017-10-19 13:43:08 +1100124 ncm->data[2] &= ~0x1;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100125 spin_unlock_irqrestore(&nc->lock, flags);
126
Gavin Shane6f44ed2016-07-19 11:54:19 +1000127 spin_lock_irqsave(&ndp->lock, flags);
Gavin Shan52b4c862017-10-19 13:43:08 +1100128 nc->state = NCSI_CHANNEL_ACTIVE;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000129 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
130 spin_unlock_irqrestore(&ndp->lock, flags);
131 ncsi_process_next_channel(ndp);
132 return;
133 }
134
135 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100136 nc->monitor.state++;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000137 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100138 mod_timer(&nc->monitor.timer, jiffies + HZ);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000139}
140
141void ncsi_start_channel_monitor(struct ncsi_channel *nc)
142{
143 unsigned long flags;
144
145 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100146 WARN_ON_ONCE(nc->monitor.enabled);
147 nc->monitor.enabled = true;
148 nc->monitor.state = NCSI_CHANNEL_MONITOR_START;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000149 spin_unlock_irqrestore(&nc->lock, flags);
150
Gavin Shan83afdc62016-10-04 11:25:52 +1100151 mod_timer(&nc->monitor.timer, jiffies + HZ);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000152}
153
154void ncsi_stop_channel_monitor(struct ncsi_channel *nc)
155{
156 unsigned long flags;
157
158 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan83afdc62016-10-04 11:25:52 +1100159 if (!nc->monitor.enabled) {
Gavin Shane6f44ed2016-07-19 11:54:19 +1000160 spin_unlock_irqrestore(&nc->lock, flags);
161 return;
162 }
Gavin Shan83afdc62016-10-04 11:25:52 +1100163 nc->monitor.enabled = false;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000164 spin_unlock_irqrestore(&nc->lock, flags);
165
Gavin Shan83afdc62016-10-04 11:25:52 +1100166 del_timer_sync(&nc->monitor.timer);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000167}
168
Gavin Shan2d283bd2016-07-19 11:54:16 +1000169struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np,
170 unsigned char id)
171{
172 struct ncsi_channel *nc;
173
174 NCSI_FOR_EACH_CHANNEL(np, nc) {
175 if (nc->id == id)
176 return nc;
177 }
178
179 return NULL;
180}
181
182struct ncsi_channel *ncsi_add_channel(struct ncsi_package *np, unsigned char id)
183{
184 struct ncsi_channel *nc, *tmp;
185 int index;
186 unsigned long flags;
187
188 nc = kzalloc(sizeof(*nc), GFP_ATOMIC);
189 if (!nc)
190 return NULL;
191
192 nc->id = id;
193 nc->package = np;
194 nc->state = NCSI_CHANNEL_INACTIVE;
Gavin Shan83afdc62016-10-04 11:25:52 +1100195 nc->monitor.enabled = false;
Kees Cook86cb30e2017-10-17 20:21:24 -0700196 timer_setup(&nc->monitor.timer, ncsi_channel_monitor, 0);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000197 spin_lock_init(&nc->lock);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000198 INIT_LIST_HEAD(&nc->link);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000199 for (index = 0; index < NCSI_CAP_MAX; index++)
200 nc->caps[index].index = index;
201 for (index = 0; index < NCSI_MODE_MAX; index++)
202 nc->modes[index].index = index;
203
204 spin_lock_irqsave(&np->lock, flags);
205 tmp = ncsi_find_channel(np, id);
206 if (tmp) {
207 spin_unlock_irqrestore(&np->lock, flags);
208 kfree(nc);
209 return tmp;
210 }
211
212 list_add_tail_rcu(&nc->node, &np->channels);
213 np->channel_num++;
214 spin_unlock_irqrestore(&np->lock, flags);
215
216 return nc;
217}
218
219static void ncsi_remove_channel(struct ncsi_channel *nc)
220{
221 struct ncsi_package *np = nc->package;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000222 unsigned long flags;
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000223
224 spin_lock_irqsave(&nc->lock, flags);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000225
226 /* Release filters */
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000227 kfree(nc->mac_filter.addrs);
228 kfree(nc->vlan_filter.vids);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000229
230 nc->state = NCSI_CHANNEL_INACTIVE;
231 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000232 ncsi_stop_channel_monitor(nc);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000233
234 /* Remove and free channel */
235 spin_lock_irqsave(&np->lock, flags);
236 list_del_rcu(&nc->node);
237 np->channel_num--;
238 spin_unlock_irqrestore(&np->lock, flags);
239
240 kfree(nc);
241}
242
243struct ncsi_package *ncsi_find_package(struct ncsi_dev_priv *ndp,
244 unsigned char id)
245{
246 struct ncsi_package *np;
247
248 NCSI_FOR_EACH_PACKAGE(ndp, np) {
249 if (np->id == id)
250 return np;
251 }
252
253 return NULL;
254}
255
256struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp,
257 unsigned char id)
258{
259 struct ncsi_package *np, *tmp;
260 unsigned long flags;
261
262 np = kzalloc(sizeof(*np), GFP_ATOMIC);
263 if (!np)
264 return NULL;
265
266 np->id = id;
267 np->ndp = ndp;
268 spin_lock_init(&np->lock);
269 INIT_LIST_HEAD(&np->channels);
270
271 spin_lock_irqsave(&ndp->lock, flags);
272 tmp = ncsi_find_package(ndp, id);
273 if (tmp) {
274 spin_unlock_irqrestore(&ndp->lock, flags);
275 kfree(np);
276 return tmp;
277 }
278
279 list_add_tail_rcu(&np->node, &ndp->packages);
280 ndp->package_num++;
281 spin_unlock_irqrestore(&ndp->lock, flags);
282
283 return np;
284}
285
286void ncsi_remove_package(struct ncsi_package *np)
287{
288 struct ncsi_dev_priv *ndp = np->ndp;
289 struct ncsi_channel *nc, *tmp;
290 unsigned long flags;
291
292 /* Release all child channels */
293 list_for_each_entry_safe(nc, tmp, &np->channels, node)
294 ncsi_remove_channel(nc);
295
296 /* Remove and free package */
297 spin_lock_irqsave(&ndp->lock, flags);
298 list_del_rcu(&np->node);
299 ndp->package_num--;
300 spin_unlock_irqrestore(&ndp->lock, flags);
301
302 kfree(np);
303}
304
305void ncsi_find_package_and_channel(struct ncsi_dev_priv *ndp,
306 unsigned char id,
307 struct ncsi_package **np,
308 struct ncsi_channel **nc)
309{
310 struct ncsi_package *p;
311 struct ncsi_channel *c;
312
313 p = ncsi_find_package(ndp, NCSI_PACKAGE_INDEX(id));
314 c = p ? ncsi_find_channel(p, NCSI_CHANNEL_INDEX(id)) : NULL;
315
316 if (np)
317 *np = p;
318 if (nc)
319 *nc = c;
320}
321
322/* For two consecutive NCSI commands, the packet IDs shouldn't
323 * be same. Otherwise, the bogus response might be replied. So
324 * the available IDs are allocated in round-robin fashion.
325 */
Gavin Shana0509cb2016-10-04 11:25:51 +1100326struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp,
327 unsigned int req_flags)
Gavin Shan2d283bd2016-07-19 11:54:16 +1000328{
329 struct ncsi_request *nr = NULL;
330 int i, limit = ARRAY_SIZE(ndp->requests);
331 unsigned long flags;
332
333 /* Check if there is one available request until the ceiling */
334 spin_lock_irqsave(&ndp->lock, flags);
Gavin Shana15af542016-10-04 11:25:50 +1100335 for (i = ndp->request_id; i < limit; i++) {
Gavin Shan2d283bd2016-07-19 11:54:16 +1000336 if (ndp->requests[i].used)
337 continue;
338
339 nr = &ndp->requests[i];
340 nr->used = true;
Gavin Shana0509cb2016-10-04 11:25:51 +1100341 nr->flags = req_flags;
Gavin Shana15af542016-10-04 11:25:50 +1100342 ndp->request_id = i + 1;
343 goto found;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000344 }
345
346 /* Fail back to check from the starting cursor */
Gavin Shana15af542016-10-04 11:25:50 +1100347 for (i = NCSI_REQ_START_IDX; i < ndp->request_id; i++) {
Gavin Shan2d283bd2016-07-19 11:54:16 +1000348 if (ndp->requests[i].used)
349 continue;
350
351 nr = &ndp->requests[i];
352 nr->used = true;
Gavin Shana0509cb2016-10-04 11:25:51 +1100353 nr->flags = req_flags;
Gavin Shana15af542016-10-04 11:25:50 +1100354 ndp->request_id = i + 1;
355 goto found;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000356 }
Gavin Shan2d283bd2016-07-19 11:54:16 +1000357
Gavin Shana15af542016-10-04 11:25:50 +1100358found:
359 spin_unlock_irqrestore(&ndp->lock, flags);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000360 return nr;
361}
362
363void ncsi_free_request(struct ncsi_request *nr)
364{
365 struct ncsi_dev_priv *ndp = nr->ndp;
366 struct sk_buff *cmd, *rsp;
367 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000368 bool driven;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000369
370 if (nr->enabled) {
371 nr->enabled = false;
372 del_timer_sync(&nr->timer);
373 }
374
375 spin_lock_irqsave(&ndp->lock, flags);
376 cmd = nr->cmd;
377 rsp = nr->rsp;
378 nr->cmd = NULL;
379 nr->rsp = NULL;
380 nr->used = false;
Gavin Shana0509cb2016-10-04 11:25:51 +1100381 driven = !!(nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000382 spin_unlock_irqrestore(&ndp->lock, flags);
383
Gavin Shane6f44ed2016-07-19 11:54:19 +1000384 if (driven && cmd && --ndp->pending_req_num == 0)
385 schedule_work(&ndp->work);
386
Gavin Shan2d283bd2016-07-19 11:54:16 +1000387 /* Release command and response */
388 consume_skb(cmd);
389 consume_skb(rsp);
390}
391
392struct ncsi_dev *ncsi_find_dev(struct net_device *dev)
393{
394 struct ncsi_dev_priv *ndp;
395
396 NCSI_FOR_EACH_DEV(ndp) {
397 if (ndp->ndev.dev == dev)
398 return &ndp->ndev;
399 }
400
401 return NULL;
402}
403
Kees Cooke99e88a2017-10-16 14:43:17 -0700404static void ncsi_request_timeout(struct timer_list *t)
Gavin Shan2d283bd2016-07-19 11:54:16 +1000405{
Kees Cooke99e88a2017-10-16 14:43:17 -0700406 struct ncsi_request *nr = from_timer(nr, t, timer);
Gavin Shan2d283bd2016-07-19 11:54:16 +1000407 struct ncsi_dev_priv *ndp = nr->ndp;
Justin.Lee1@Dell.com9771b8c2018-10-11 18:07:37 +0000408 struct ncsi_cmd_pkt *cmd;
409 struct ncsi_package *np;
410 struct ncsi_channel *nc;
Gavin Shan2d283bd2016-07-19 11:54:16 +1000411 unsigned long flags;
412
413 /* If the request already had associated response,
414 * let the response handler to release it.
415 */
416 spin_lock_irqsave(&ndp->lock, flags);
417 nr->enabled = false;
418 if (nr->rsp || !nr->cmd) {
419 spin_unlock_irqrestore(&ndp->lock, flags);
420 return;
421 }
422 spin_unlock_irqrestore(&ndp->lock, flags);
423
Justin.Lee1@Dell.com9771b8c2018-10-11 18:07:37 +0000424 if (nr->flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
425 if (nr->cmd) {
426 /* Find the package */
427 cmd = (struct ncsi_cmd_pkt *)
428 skb_network_header(nr->cmd);
429 ncsi_find_package_and_channel(ndp,
430 cmd->cmd.common.channel,
431 &np, &nc);
432 ncsi_send_netlink_timeout(nr, np, nc);
433 }
434 }
435
Gavin Shan2d283bd2016-07-19 11:54:16 +1000436 /* Release the request */
437 ncsi_free_request(nr);
438}
439
Gavin Shane6f44ed2016-07-19 11:54:19 +1000440static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp)
441{
442 struct ncsi_dev *nd = &ndp->ndev;
Samuel Mendoza-Jonascd09ab02018-11-16 15:51:56 +1100443 struct ncsi_package *np;
444 struct ncsi_channel *nc, *tmp;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000445 struct ncsi_cmd_arg nca;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100446 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000447 int ret;
448
Samuel Mendoza-Jonascd09ab02018-11-16 15:51:56 +1100449 np = ndp->active_package;
450 nc = ndp->active_channel;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000451 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +1100452 nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000453 switch (nd->state) {
454 case ncsi_dev_state_suspend:
455 nd->state = ncsi_dev_state_suspend_select;
456 /* Fall through */
457 case ncsi_dev_state_suspend_select:
Gavin Shan7ba5c002016-10-20 11:45:49 +1100458 ndp->pending_req_num = 1;
459
460 nca.type = NCSI_PKT_CMD_SP;
461 nca.package = np->id;
462 nca.channel = NCSI_RESERVED_CHANNEL;
463 if (ndp->flags & NCSI_DEV_HWA)
464 nca.bytes[0] = 0;
465 else
466 nca.bytes[0] = 1;
467
Gavin Shan008a4242016-10-20 11:45:50 +1100468 /* To retrieve the last link states of channels in current
469 * package when current active channel needs fail over to
470 * another one. It means we will possibly select another
471 * channel as next active one. The link states of channels
472 * are most important factor of the selection. So we need
473 * accurate link states. Unfortunately, the link states on
474 * inactive channels can't be updated with LSC AEN in time.
475 */
476 if (ndp->flags & NCSI_DEV_RESHUFFLE)
477 nd->state = ncsi_dev_state_suspend_gls;
478 else
479 nd->state = ncsi_dev_state_suspend_dcnt;
Gavin Shan7ba5c002016-10-20 11:45:49 +1100480 ret = ncsi_xmit_cmd(&nca);
481 if (ret)
482 goto error;
483
484 break;
Gavin Shan008a4242016-10-20 11:45:50 +1100485 case ncsi_dev_state_suspend_gls:
486 ndp->pending_req_num = np->channel_num;
487
488 nca.type = NCSI_PKT_CMD_GLS;
489 nca.package = np->id;
490
491 nd->state = ncsi_dev_state_suspend_dcnt;
492 NCSI_FOR_EACH_CHANNEL(np, nc) {
493 nca.channel = nc->id;
494 ret = ncsi_xmit_cmd(&nca);
495 if (ret)
496 goto error;
497 }
498
499 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000500 case ncsi_dev_state_suspend_dcnt:
Gavin Shan7ba5c002016-10-20 11:45:49 +1100501 ndp->pending_req_num = 1;
502
503 nca.type = NCSI_PKT_CMD_DCNT;
504 nca.package = np->id;
505 nca.channel = nc->id;
506
507 nd->state = ncsi_dev_state_suspend_dc;
508 ret = ncsi_xmit_cmd(&nca);
509 if (ret)
510 goto error;
511
512 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000513 case ncsi_dev_state_suspend_dc:
Gavin Shan7ba5c002016-10-20 11:45:49 +1100514 ndp->pending_req_num = 1;
515
516 nca.type = NCSI_PKT_CMD_DC;
517 nca.package = np->id;
518 nca.channel = nc->id;
519 nca.bytes[0] = 1;
520
521 nd->state = ncsi_dev_state_suspend_deselect;
522 ret = ncsi_xmit_cmd(&nca);
523 if (ret)
524 goto error;
525
Samuel Mendoza-Jonascd09ab02018-11-16 15:51:56 +1100526 NCSI_FOR_EACH_CHANNEL(np, tmp) {
527 /* If there is another channel active on this package
528 * do not deselect the package.
529 */
530 if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) {
531 nd->state = ncsi_dev_state_suspend_done;
532 break;
533 }
534 }
Gavin Shan7ba5c002016-10-20 11:45:49 +1100535 break;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000536 case ncsi_dev_state_suspend_deselect:
537 ndp->pending_req_num = 1;
538
Gavin Shan7ba5c002016-10-20 11:45:49 +1100539 nca.type = NCSI_PKT_CMD_DP;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000540 nca.package = np->id;
Gavin Shan7ba5c002016-10-20 11:45:49 +1100541 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000542
Gavin Shan7ba5c002016-10-20 11:45:49 +1100543 nd->state = ncsi_dev_state_suspend_done;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000544 ret = ncsi_xmit_cmd(&nca);
Gavin Shan7ba5c002016-10-20 11:45:49 +1100545 if (ret)
546 goto error;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000547
548 break;
549 case ncsi_dev_state_suspend_done:
Gavin Shand8cedaa2016-10-04 11:25:47 +1100550 spin_lock_irqsave(&nc->lock, flags);
551 nc->state = NCSI_CHANNEL_INACTIVE;
552 spin_unlock_irqrestore(&nc->lock, flags);
Samuel Mendoza-Jonas2878a2c2018-11-16 15:51:58 +1100553 if (ndp->flags & NCSI_DEV_RESET)
554 ncsi_reset_dev(nd);
555 else
556 ncsi_process_next_channel(ndp);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000557 break;
558 default:
559 netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n",
560 nd->state);
561 }
Gavin Shan7ba5c002016-10-20 11:45:49 +1100562
563 return;
564error:
565 nd->state = ncsi_dev_state_functional;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000566}
567
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000568/* Check the VLAN filter bitmap for a set filter, and construct a
569 * "Set VLAN Filter - Disable" packet if found.
570 */
571static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
572 struct ncsi_cmd_arg *nca)
573{
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000574 struct ncsi_channel_vlan_filter *ncf;
575 unsigned long flags;
576 void *bitmap;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000577 int index;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000578 u16 vid;
579
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000580 ncf = &nc->vlan_filter;
581 bitmap = &ncf->bitmap;
582
583 spin_lock_irqsave(&nc->lock, flags);
584 index = find_next_bit(bitmap, ncf->n_vids, 0);
585 if (index >= ncf->n_vids) {
586 spin_unlock_irqrestore(&nc->lock, flags);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000587 return -1;
588 }
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000589 vid = ncf->vids[index];
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000590
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000591 clear_bit(index, bitmap);
592 ncf->vids[index] = 0;
593 spin_unlock_irqrestore(&nc->lock, flags);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000594
595 nca->type = NCSI_PKT_CMD_SVF;
596 nca->words[1] = vid;
597 /* HW filter index starts at 1 */
598 nca->bytes[6] = index + 1;
599 nca->bytes[7] = 0x00;
600 return 0;
601}
602
603/* Find an outstanding VLAN tag and constuct a "Set VLAN Filter - Enable"
604 * packet.
605 */
606static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc,
607 struct ncsi_cmd_arg *nca)
608{
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000609 struct ncsi_channel_vlan_filter *ncf;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000610 struct vlan_vid *vlan = NULL;
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000611 unsigned long flags;
612 int i, index;
613 void *bitmap;
614 u16 vid;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000615
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000616 if (list_empty(&ndp->vlan_vids))
617 return -1;
618
619 ncf = &nc->vlan_filter;
620 bitmap = &ncf->bitmap;
621
622 spin_lock_irqsave(&nc->lock, flags);
623
624 rcu_read_lock();
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000625 list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000626 vid = vlan->vid;
627 for (i = 0; i < ncf->n_vids; i++)
628 if (ncf->vids[i] == vid) {
629 vid = 0;
630 break;
631 }
632 if (vid)
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000633 break;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000634 }
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000635 rcu_read_unlock();
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000636
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000637 if (!vid) {
638 /* No VLAN ID is not set */
639 spin_unlock_irqrestore(&nc->lock, flags);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000640 return -1;
641 }
642
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000643 index = find_next_zero_bit(bitmap, ncf->n_vids, 0);
644 if (index < 0 || index >= ncf->n_vids) {
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000645 netdev_err(ndp->ndev.dev,
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000646 "Channel %u already has all VLAN filters set\n",
647 nc->id);
648 spin_unlock_irqrestore(&nc->lock, flags);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000649 return -1;
650 }
651
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000652 ncf->vids[index] = vid;
653 set_bit(index, bitmap);
654 spin_unlock_irqrestore(&nc->lock, flags);
655
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000656 nca->type = NCSI_PKT_CMD_SVF;
Samuel Mendoza-Jonas062b3e12018-04-17 14:23:23 +1000657 nca->words[1] = vid;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000658 /* HW filter index starts at 1 */
659 nca->bytes[6] = index + 1;
660 nca->bytes[7] = 0x01;
661
662 return 0;
663}
664
Vijay Khemkacb10c7c2018-10-16 12:13:19 -0700665#if IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC)
666
667/* NCSI OEM Command APIs */
668static int ncsi_oem_gma_handler_bcm(struct ncsi_cmd_arg *nca)
669{
670 unsigned char data[NCSI_OEM_BCM_CMD_GMA_LEN];
671 int ret = 0;
672
673 nca->payload = NCSI_OEM_BCM_CMD_GMA_LEN;
674
675 memset(data, 0, NCSI_OEM_BCM_CMD_GMA_LEN);
676 *(unsigned int *)data = ntohl(NCSI_OEM_MFR_BCM_ID);
677 data[5] = NCSI_OEM_BCM_CMD_GMA;
678
679 nca->data = data;
680
681 ret = ncsi_xmit_cmd(nca);
682 if (ret)
683 netdev_err(nca->ndp->ndev.dev,
684 "NCSI: Failed to transmit cmd 0x%x during configure\n",
685 nca->type);
686 return ret;
687}
688
689/* OEM Command handlers initialization */
690static struct ncsi_oem_gma_handler {
691 unsigned int mfr_id;
692 int (*handler)(struct ncsi_cmd_arg *nca);
693} ncsi_oem_gma_handlers[] = {
694 { NCSI_OEM_MFR_BCM_ID, ncsi_oem_gma_handler_bcm }
695};
696
697static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id)
698{
699 struct ncsi_oem_gma_handler *nch = NULL;
700 int i;
701
702 /* This function should only be called once, return if flag set */
703 if (nca->ndp->gma_flag == 1)
704 return -1;
705
706 /* Find gma handler for given manufacturer id */
707 for (i = 0; i < ARRAY_SIZE(ncsi_oem_gma_handlers); i++) {
708 if (ncsi_oem_gma_handlers[i].mfr_id == mf_id) {
709 if (ncsi_oem_gma_handlers[i].handler)
710 nch = &ncsi_oem_gma_handlers[i];
711 break;
712 }
713 }
714
715 if (!nch) {
716 netdev_err(nca->ndp->ndev.dev,
717 "NCSI: No GMA handler available for MFR-ID (0x%x)\n",
718 mf_id);
719 return -1;
720 }
721
722 /* Set the flag for GMA command which should only be called once */
723 nca->ndp->gma_flag = 1;
724
725 /* Get Mac address from NCSI device */
726 return nch->handler(nca);
727}
728
729#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
730
Gavin Shane6f44ed2016-07-19 11:54:19 +1000731static void ncsi_configure_channel(struct ncsi_dev_priv *ndp)
732{
733 struct ncsi_dev *nd = &ndp->ndev;
734 struct net_device *dev = nd->dev;
735 struct ncsi_package *np = ndp->active_package;
736 struct ncsi_channel *nc = ndp->active_channel;
Gavin Shanbbc7c012016-10-20 11:45:51 +1100737 struct ncsi_channel *hot_nc = NULL;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000738 struct ncsi_cmd_arg nca;
739 unsigned char index;
Gavin Shand8cedaa2016-10-04 11:25:47 +1100740 unsigned long flags;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000741 int ret;
742
743 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +1100744 nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000745 switch (nd->state) {
746 case ncsi_dev_state_config:
747 case ncsi_dev_state_config_sp:
748 ndp->pending_req_num = 1;
749
750 /* Select the specific package */
751 nca.type = NCSI_PKT_CMD_SP;
752 if (ndp->flags & NCSI_DEV_HWA)
753 nca.bytes[0] = 0;
754 else
755 nca.bytes[0] = 1;
756 nca.package = np->id;
Gavin Shanbc7e0f52016-10-04 11:25:48 +1100757 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000758 ret = ncsi_xmit_cmd(&nca);
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100759 if (ret) {
760 netdev_err(ndp->ndev.dev,
761 "NCSI: Failed to transmit CMD_SP\n");
Gavin Shane6f44ed2016-07-19 11:54:19 +1000762 goto error;
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100763 }
Gavin Shane6f44ed2016-07-19 11:54:19 +1000764
765 nd->state = ncsi_dev_state_config_cis;
766 break;
767 case ncsi_dev_state_config_cis:
768 ndp->pending_req_num = 1;
769
770 /* Clear initial state */
771 nca.type = NCSI_PKT_CMD_CIS;
772 nca.package = np->id;
773 nca.channel = nc->id;
774 ret = ncsi_xmit_cmd(&nca);
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100775 if (ret) {
776 netdev_err(ndp->ndev.dev,
777 "NCSI: Failed to transmit CMD_CIS\n");
Gavin Shane6f44ed2016-07-19 11:54:19 +1000778 goto error;
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100779 }
Gavin Shane6f44ed2016-07-19 11:54:19 +1000780
Vijay Khemkacb10c7c2018-10-16 12:13:19 -0700781 nd->state = ncsi_dev_state_config_oem_gma;
782 break;
783 case ncsi_dev_state_config_oem_gma:
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000784 nd->state = ncsi_dev_state_config_clear_vids;
Vijay Khemkacb10c7c2018-10-16 12:13:19 -0700785 ret = -1;
786
787#if IS_ENABLED(CONFIG_NCSI_OEM_CMD_GET_MAC)
788 nca.type = NCSI_PKT_CMD_OEM;
789 nca.package = np->id;
790 nca.channel = nc->id;
791 ndp->pending_req_num = 1;
792 ret = ncsi_gma_handler(&nca, nc->version.mf_id);
793#endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */
794
795 if (ret < 0)
796 schedule_work(&ndp->work);
797
Gavin Shane6f44ed2016-07-19 11:54:19 +1000798 break;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000799 case ncsi_dev_state_config_clear_vids:
800 case ncsi_dev_state_config_svf:
801 case ncsi_dev_state_config_ev:
Gavin Shane6f44ed2016-07-19 11:54:19 +1000802 case ncsi_dev_state_config_sma:
803 case ncsi_dev_state_config_ebf:
804#if IS_ENABLED(CONFIG_IPV6)
805 case ncsi_dev_state_config_egmf:
806#endif
807 case ncsi_dev_state_config_ecnt:
808 case ncsi_dev_state_config_ec:
809 case ncsi_dev_state_config_ae:
810 case ncsi_dev_state_config_gls:
811 ndp->pending_req_num = 1;
812
813 nca.package = np->id;
814 nca.channel = nc->id;
815
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000816 /* Clear any active filters on the channel before setting */
817 if (nd->state == ncsi_dev_state_config_clear_vids) {
818 ret = clear_one_vid(ndp, nc, &nca);
819 if (ret) {
820 nd->state = ncsi_dev_state_config_svf;
821 schedule_work(&ndp->work);
822 break;
823 }
824 /* Repeat */
825 nd->state = ncsi_dev_state_config_clear_vids;
826 /* Add known VLAN tags to the filter */
827 } else if (nd->state == ncsi_dev_state_config_svf) {
828 ret = set_one_vid(ndp, nc, &nca);
829 if (ret) {
830 nd->state = ncsi_dev_state_config_ev;
831 schedule_work(&ndp->work);
832 break;
833 }
834 /* Repeat */
835 nd->state = ncsi_dev_state_config_svf;
836 /* Enable/Disable the VLAN filter */
837 } else if (nd->state == ncsi_dev_state_config_ev) {
838 if (list_empty(&ndp->vlan_vids)) {
839 nca.type = NCSI_PKT_CMD_DV;
840 } else {
841 nca.type = NCSI_PKT_CMD_EV;
842 nca.bytes[3] = NCSI_CAP_VLAN_NO;
843 }
844 nd->state = ncsi_dev_state_config_sma;
845 } else if (nd->state == ncsi_dev_state_config_sma) {
Gavin Shane6f44ed2016-07-19 11:54:19 +1000846 /* Use first entry in unicast filter table. Note that
847 * the MAC filter table starts from entry 1 instead of
848 * 0.
849 */
Gavin Shane6f44ed2016-07-19 11:54:19 +1000850 nca.type = NCSI_PKT_CMD_SMA;
851 for (index = 0; index < 6; index++)
852 nca.bytes[index] = dev->dev_addr[index];
853 nca.bytes[6] = 0x1;
854 nca.bytes[7] = 0x1;
855 nd->state = ncsi_dev_state_config_ebf;
856 } else if (nd->state == ncsi_dev_state_config_ebf) {
857 nca.type = NCSI_PKT_CMD_EBF;
858 nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap;
859 nd->state = ncsi_dev_state_config_ecnt;
860#if IS_ENABLED(CONFIG_IPV6)
861 if (ndp->inet6_addr_num > 0 &&
862 (nc->caps[NCSI_CAP_GENERIC].cap &
863 NCSI_CAP_GENERIC_MC))
864 nd->state = ncsi_dev_state_config_egmf;
865 else
866 nd->state = ncsi_dev_state_config_ecnt;
867 } else if (nd->state == ncsi_dev_state_config_egmf) {
868 nca.type = NCSI_PKT_CMD_EGMF;
869 nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
870 nd->state = ncsi_dev_state_config_ecnt;
871#endif /* CONFIG_IPV6 */
872 } else if (nd->state == ncsi_dev_state_config_ecnt) {
873 nca.type = NCSI_PKT_CMD_ECNT;
874 nd->state = ncsi_dev_state_config_ec;
875 } else if (nd->state == ncsi_dev_state_config_ec) {
876 /* Enable AEN if it's supported */
877 nca.type = NCSI_PKT_CMD_EC;
878 nd->state = ncsi_dev_state_config_ae;
879 if (!(nc->caps[NCSI_CAP_AEN].cap & NCSI_CAP_AEN_MASK))
880 nd->state = ncsi_dev_state_config_gls;
881 } else if (nd->state == ncsi_dev_state_config_ae) {
882 nca.type = NCSI_PKT_CMD_AE;
883 nca.bytes[0] = 0;
884 nca.dwords[1] = nc->caps[NCSI_CAP_AEN].cap;
885 nd->state = ncsi_dev_state_config_gls;
886 } else if (nd->state == ncsi_dev_state_config_gls) {
887 nca.type = NCSI_PKT_CMD_GLS;
888 nd->state = ncsi_dev_state_config_done;
889 }
890
891 ret = ncsi_xmit_cmd(&nca);
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100892 if (ret) {
893 netdev_err(ndp->ndev.dev,
894 "NCSI: Failed to transmit CMD %x\n",
895 nca.type);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000896 goto error;
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100897 }
Gavin Shane6f44ed2016-07-19 11:54:19 +1000898 break;
899 case ncsi_dev_state_config_done:
Joel Stanley6e42a3f2018-06-19 15:08:33 +0930900 netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n",
901 nc->id);
Gavin Shand8cedaa2016-10-04 11:25:47 +1100902 spin_lock_irqsave(&nc->lock, flags);
Samuel Mendoza-Jonas2878a2c2018-11-16 15:51:58 +1100903 nc->state = NCSI_CHANNEL_ACTIVE;
904
905 if (ndp->flags & NCSI_DEV_RESET) {
906 /* A reset event happened during config, start it now */
907 nc->reconfigure_needed = false;
908 spin_unlock_irqrestore(&nc->lock, flags);
909 ncsi_reset_dev(nd);
910 break;
911 }
912
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000913 if (nc->reconfigure_needed) {
914 /* This channel's configuration has been updated
915 * part-way during the config state - start the
916 * channel configuration over
917 */
918 nc->reconfigure_needed = false;
919 nc->state = NCSI_CHANNEL_INACTIVE;
920 spin_unlock_irqrestore(&nc->lock, flags);
921
922 spin_lock_irqsave(&ndp->lock, flags);
923 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
924 spin_unlock_irqrestore(&ndp->lock, flags);
925
Joel Stanley6e42a3f2018-06-19 15:08:33 +0930926 netdev_dbg(dev, "Dirty NCSI channel state reset\n");
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +1000927 ncsi_process_next_channel(ndp);
928 break;
929 }
930
Gavin Shanbbc7c012016-10-20 11:45:51 +1100931 if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) {
932 hot_nc = nc;
Gavin Shanbbc7c012016-10-20 11:45:51 +1100933 } else {
934 hot_nc = NULL;
Joel Stanley87975a02018-06-19 15:08:31 +0930935 netdev_dbg(ndp->ndev.dev,
936 "NCSI: channel %u link down after config\n",
937 nc->id);
Gavin Shanbbc7c012016-10-20 11:45:51 +1100938 }
Gavin Shand8cedaa2016-10-04 11:25:47 +1100939 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000940
Gavin Shanbbc7c012016-10-20 11:45:51 +1100941 /* Update the hot channel */
942 spin_lock_irqsave(&ndp->lock, flags);
943 ndp->hot_channel = hot_nc;
944 spin_unlock_irqrestore(&ndp->lock, flags);
945
Gavin Shane6f44ed2016-07-19 11:54:19 +1000946 ncsi_start_channel_monitor(nc);
947 ncsi_process_next_channel(ndp);
948 break;
949 default:
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +1100950 netdev_alert(dev, "Wrong NCSI state 0x%x in config\n",
951 nd->state);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000952 }
953
954 return;
955
956error:
957 ncsi_report_link(ndp, true);
958}
959
960static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp)
961{
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +1100962 struct ncsi_package *np, *force_package;
963 struct ncsi_channel *nc, *found, *hot_nc, *force_channel;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000964 struct ncsi_channel_mode *ncm;
965 unsigned long flags;
966
Gavin Shanbbc7c012016-10-20 11:45:51 +1100967 spin_lock_irqsave(&ndp->lock, flags);
968 hot_nc = ndp->hot_channel;
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +1100969 force_channel = ndp->force_channel;
970 force_package = ndp->force_package;
Gavin Shanbbc7c012016-10-20 11:45:51 +1100971 spin_unlock_irqrestore(&ndp->lock, flags);
972
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +1100973 /* Force a specific channel whether or not it has link if we have been
974 * configured to do so
975 */
976 if (force_package && force_channel) {
977 found = force_channel;
978 ncm = &found->modes[NCSI_MODE_LINK];
979 if (!(ncm->data[2] & 0x1))
980 netdev_info(ndp->ndev.dev,
981 "NCSI: Channel %u forced, but it is link down\n",
982 found->id);
983 goto out;
984 }
985
Gavin Shane6f44ed2016-07-19 11:54:19 +1000986 /* The search is done once an inactive channel with up
987 * link is found.
988 */
989 found = NULL;
990 NCSI_FOR_EACH_PACKAGE(ndp, np) {
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +1100991 if (ndp->force_package && np != ndp->force_package)
992 continue;
Gavin Shane6f44ed2016-07-19 11:54:19 +1000993 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shand8cedaa2016-10-04 11:25:47 +1100994 spin_lock_irqsave(&nc->lock, flags);
995
Gavin Shane6f44ed2016-07-19 11:54:19 +1000996 if (!list_empty(&nc->link) ||
Gavin Shand8cedaa2016-10-04 11:25:47 +1100997 nc->state != NCSI_CHANNEL_INACTIVE) {
998 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +1000999 continue;
Gavin Shand8cedaa2016-10-04 11:25:47 +11001000 }
Gavin Shane6f44ed2016-07-19 11:54:19 +10001001
1002 if (!found)
1003 found = nc;
1004
Gavin Shanbbc7c012016-10-20 11:45:51 +11001005 if (nc == hot_nc)
1006 found = nc;
1007
Gavin Shane6f44ed2016-07-19 11:54:19 +10001008 ncm = &nc->modes[NCSI_MODE_LINK];
1009 if (ncm->data[2] & 0x1) {
Gavin Shand8cedaa2016-10-04 11:25:47 +11001010 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001011 found = nc;
1012 goto out;
1013 }
Gavin Shand8cedaa2016-10-04 11:25:47 +11001014
1015 spin_unlock_irqrestore(&nc->lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001016 }
1017 }
1018
1019 if (!found) {
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001020 netdev_warn(ndp->ndev.dev,
1021 "NCSI: No channel found with link\n");
Gavin Shane6f44ed2016-07-19 11:54:19 +10001022 ncsi_report_link(ndp, true);
1023 return -ENODEV;
1024 }
1025
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001026 ncm = &found->modes[NCSI_MODE_LINK];
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301027 netdev_dbg(ndp->ndev.dev,
1028 "NCSI: Channel %u added to queue (link %s)\n",
1029 found->id, ncm->data[2] & 0x1 ? "up" : "down");
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001030
Gavin Shane6f44ed2016-07-19 11:54:19 +10001031out:
1032 spin_lock_irqsave(&ndp->lock, flags);
1033 list_add_tail_rcu(&found->link, &ndp->channel_queue);
1034 spin_unlock_irqrestore(&ndp->lock, flags);
1035
1036 return ncsi_process_next_channel(ndp);
1037}
1038
1039static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp)
1040{
1041 struct ncsi_package *np;
1042 struct ncsi_channel *nc;
1043 unsigned int cap;
Gavin Shan100ef012017-10-19 13:43:07 +11001044 bool has_channel = false;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001045
1046 /* The hardware arbitration is disabled if any one channel
1047 * doesn't support explicitly.
1048 */
1049 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1050 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shan100ef012017-10-19 13:43:07 +11001051 has_channel = true;
1052
Gavin Shane6f44ed2016-07-19 11:54:19 +10001053 cap = nc->caps[NCSI_CAP_GENERIC].cap;
1054 if (!(cap & NCSI_CAP_GENERIC_HWA) ||
1055 (cap & NCSI_CAP_GENERIC_HWA_MASK) !=
1056 NCSI_CAP_GENERIC_HWA_SUPPORT) {
1057 ndp->flags &= ~NCSI_DEV_HWA;
1058 return false;
1059 }
1060 }
1061 }
1062
Gavin Shan100ef012017-10-19 13:43:07 +11001063 if (has_channel) {
1064 ndp->flags |= NCSI_DEV_HWA;
1065 return true;
1066 }
1067
1068 ndp->flags &= ~NCSI_DEV_HWA;
1069 return false;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001070}
1071
Gavin Shane6f44ed2016-07-19 11:54:19 +10001072static void ncsi_probe_channel(struct ncsi_dev_priv *ndp)
1073{
1074 struct ncsi_dev *nd = &ndp->ndev;
1075 struct ncsi_package *np;
1076 struct ncsi_channel *nc;
1077 struct ncsi_cmd_arg nca;
1078 unsigned char index;
1079 int ret;
1080
1081 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +11001082 nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001083 switch (nd->state) {
1084 case ncsi_dev_state_probe:
1085 nd->state = ncsi_dev_state_probe_deselect;
1086 /* Fall through */
1087 case ncsi_dev_state_probe_deselect:
1088 ndp->pending_req_num = 8;
1089
1090 /* Deselect all possible packages */
1091 nca.type = NCSI_PKT_CMD_DP;
Gavin Shanbc7e0f52016-10-04 11:25:48 +11001092 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001093 for (index = 0; index < 8; index++) {
1094 nca.package = index;
1095 ret = ncsi_xmit_cmd(&nca);
1096 if (ret)
1097 goto error;
1098 }
1099
1100 nd->state = ncsi_dev_state_probe_package;
1101 break;
1102 case ncsi_dev_state_probe_package:
Gavin Shane6f44ed2016-07-19 11:54:19 +10001103 ndp->pending_req_num = 1;
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001104
Gavin Shane6f44ed2016-07-19 11:54:19 +10001105 nca.type = NCSI_PKT_CMD_SP;
1106 nca.bytes[0] = 1;
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001107 nca.package = ndp->package_probe_id;
Gavin Shanbc7e0f52016-10-04 11:25:48 +11001108 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001109 ret = ncsi_xmit_cmd(&nca);
1110 if (ret)
1111 goto error;
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001112 nd->state = ncsi_dev_state_probe_channel;
1113 break;
1114 case ncsi_dev_state_probe_channel:
1115 ndp->active_package = ncsi_find_package(ndp,
1116 ndp->package_probe_id);
1117 if (!ndp->active_package) {
1118 /* No response */
1119 nd->state = ncsi_dev_state_probe_dp;
1120 schedule_work(&ndp->work);
1121 break;
1122 }
Gavin Shane6f44ed2016-07-19 11:54:19 +10001123 nd->state = ncsi_dev_state_probe_cis;
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001124 schedule_work(&ndp->work);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001125 break;
1126 case ncsi_dev_state_probe_cis:
Gavin Shan55e02d02016-10-04 11:25:49 +11001127 ndp->pending_req_num = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001128
1129 /* Clear initial state */
1130 nca.type = NCSI_PKT_CMD_CIS;
1131 nca.package = ndp->active_package->id;
Gavin Shan55e02d02016-10-04 11:25:49 +11001132 for (index = 0; index < NCSI_RESERVED_CHANNEL; index++) {
Gavin Shane6f44ed2016-07-19 11:54:19 +10001133 nca.channel = index;
1134 ret = ncsi_xmit_cmd(&nca);
1135 if (ret)
1136 goto error;
1137 }
1138
1139 nd->state = ncsi_dev_state_probe_gvi;
1140 break;
1141 case ncsi_dev_state_probe_gvi:
1142 case ncsi_dev_state_probe_gc:
1143 case ncsi_dev_state_probe_gls:
1144 np = ndp->active_package;
1145 ndp->pending_req_num = np->channel_num;
1146
1147 /* Retrieve version, capability or link status */
1148 if (nd->state == ncsi_dev_state_probe_gvi)
1149 nca.type = NCSI_PKT_CMD_GVI;
1150 else if (nd->state == ncsi_dev_state_probe_gc)
1151 nca.type = NCSI_PKT_CMD_GC;
1152 else
1153 nca.type = NCSI_PKT_CMD_GLS;
1154
1155 nca.package = np->id;
1156 NCSI_FOR_EACH_CHANNEL(np, nc) {
1157 nca.channel = nc->id;
1158 ret = ncsi_xmit_cmd(&nca);
1159 if (ret)
1160 goto error;
1161 }
1162
1163 if (nd->state == ncsi_dev_state_probe_gvi)
1164 nd->state = ncsi_dev_state_probe_gc;
1165 else if (nd->state == ncsi_dev_state_probe_gc)
1166 nd->state = ncsi_dev_state_probe_gls;
1167 else
1168 nd->state = ncsi_dev_state_probe_dp;
1169 break;
1170 case ncsi_dev_state_probe_dp:
1171 ndp->pending_req_num = 1;
1172
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001173 /* Deselect the current package */
Gavin Shane6f44ed2016-07-19 11:54:19 +10001174 nca.type = NCSI_PKT_CMD_DP;
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001175 nca.package = ndp->package_probe_id;
Gavin Shanbc7e0f52016-10-04 11:25:48 +11001176 nca.channel = NCSI_RESERVED_CHANNEL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001177 ret = ncsi_xmit_cmd(&nca);
1178 if (ret)
1179 goto error;
1180
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001181 /* Probe next package */
1182 ndp->package_probe_id++;
1183 if (ndp->package_probe_id >= 8) {
1184 /* Probe finished */
1185 ndp->flags |= NCSI_DEV_PROBED;
1186 break;
1187 }
1188 nd->state = ncsi_dev_state_probe_package;
1189 ndp->active_package = NULL;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001190 break;
1191 default:
1192 netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n",
1193 nd->state);
1194 }
1195
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001196 if (ndp->flags & NCSI_DEV_PROBED) {
1197 /* Check if all packages have HWA support */
1198 ncsi_check_hwa(ndp);
1199 ncsi_choose_active_channel(ndp);
1200 }
1201
Gavin Shane6f44ed2016-07-19 11:54:19 +10001202 return;
1203error:
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001204 netdev_err(ndp->ndev.dev,
1205 "NCSI: Failed to transmit cmd 0x%x during probe\n",
1206 nca.type);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001207 ncsi_report_link(ndp, true);
1208}
1209
1210static void ncsi_dev_work(struct work_struct *work)
1211{
1212 struct ncsi_dev_priv *ndp = container_of(work,
1213 struct ncsi_dev_priv, work);
1214 struct ncsi_dev *nd = &ndp->ndev;
1215
1216 switch (nd->state & ncsi_dev_state_major) {
1217 case ncsi_dev_state_probe:
1218 ncsi_probe_channel(ndp);
1219 break;
1220 case ncsi_dev_state_suspend:
1221 ncsi_suspend_channel(ndp);
1222 break;
1223 case ncsi_dev_state_config:
1224 ncsi_configure_channel(ndp);
1225 break;
1226 default:
1227 netdev_warn(nd->dev, "Wrong NCSI state 0x%x in workqueue\n",
1228 nd->state);
1229 }
1230}
1231
1232int ncsi_process_next_channel(struct ncsi_dev_priv *ndp)
1233{
1234 struct ncsi_channel *nc;
1235 int old_state;
1236 unsigned long flags;
1237
1238 spin_lock_irqsave(&ndp->lock, flags);
1239 nc = list_first_or_null_rcu(&ndp->channel_queue,
1240 struct ncsi_channel, link);
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001241 if (!nc) {
1242 spin_unlock_irqrestore(&ndp->lock, flags);
1243 goto out;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001244 }
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001245
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001246 list_del_init(&nc->link);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001247 spin_unlock_irqrestore(&ndp->lock, flags);
1248
Gavin Shand8cedaa2016-10-04 11:25:47 +11001249 spin_lock_irqsave(&nc->lock, flags);
1250 old_state = nc->state;
1251 nc->state = NCSI_CHANNEL_INVISIBLE;
1252 spin_unlock_irqrestore(&nc->lock, flags);
1253
Gavin Shane6f44ed2016-07-19 11:54:19 +10001254 ndp->active_channel = nc;
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001255 ndp->active_package = nc->package;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001256
1257 switch (old_state) {
1258 case NCSI_CHANNEL_INACTIVE:
1259 ndp->ndev.state = ncsi_dev_state_config;
Joel Stanley87975a02018-06-19 15:08:31 +09301260 netdev_dbg(ndp->ndev.dev, "NCSI: configuring channel %u\n",
1261 nc->id);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001262 ncsi_configure_channel(ndp);
1263 break;
1264 case NCSI_CHANNEL_ACTIVE:
1265 ndp->ndev.state = ncsi_dev_state_suspend;
Joel Stanley87975a02018-06-19 15:08:31 +09301266 netdev_dbg(ndp->ndev.dev, "NCSI: suspending channel %u\n",
1267 nc->id);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001268 ncsi_suspend_channel(ndp);
1269 break;
1270 default:
1271 netdev_err(ndp->ndev.dev, "Invalid state 0x%x on %d:%d\n",
Gavin Shand8cedaa2016-10-04 11:25:47 +11001272 old_state, nc->package->id, nc->id);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001273 ncsi_report_link(ndp, false);
1274 return -EINVAL;
1275 }
1276
1277 return 0;
Arnd Bergmanna1b43ed2016-07-21 21:28:34 +02001278
1279out:
1280 ndp->active_channel = NULL;
1281 ndp->active_package = NULL;
1282 if (ndp->flags & NCSI_DEV_RESHUFFLE) {
1283 ndp->flags &= ~NCSI_DEV_RESHUFFLE;
1284 return ncsi_choose_active_channel(ndp);
1285 }
1286
1287 ncsi_report_link(ndp, false);
1288 return -ENODEV;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001289}
1290
1291#if IS_ENABLED(CONFIG_IPV6)
1292static int ncsi_inet6addr_event(struct notifier_block *this,
1293 unsigned long event, void *data)
1294{
1295 struct inet6_ifaddr *ifa = data;
1296 struct net_device *dev = ifa->idev->dev;
1297 struct ncsi_dev *nd = ncsi_find_dev(dev);
1298 struct ncsi_dev_priv *ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
1299 struct ncsi_package *np;
1300 struct ncsi_channel *nc;
1301 struct ncsi_cmd_arg nca;
1302 bool action;
1303 int ret;
1304
1305 if (!ndp || (ipv6_addr_type(&ifa->addr) &
1306 (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK)))
1307 return NOTIFY_OK;
1308
1309 switch (event) {
1310 case NETDEV_UP:
1311 action = (++ndp->inet6_addr_num) == 1;
1312 nca.type = NCSI_PKT_CMD_EGMF;
1313 break;
1314 case NETDEV_DOWN:
1315 action = (--ndp->inet6_addr_num == 0);
1316 nca.type = NCSI_PKT_CMD_DGMF;
1317 break;
1318 default:
1319 return NOTIFY_OK;
1320 }
1321
1322 /* We might not have active channel or packages. The IPv6
1323 * required multicast will be enabled when active channel
1324 * or packages are chosen.
1325 */
1326 np = ndp->active_package;
1327 nc = ndp->active_channel;
1328 if (!action || !np || !nc)
1329 return NOTIFY_OK;
1330
1331 /* We needn't enable or disable it if the function isn't supported */
1332 if (!(nc->caps[NCSI_CAP_GENERIC].cap & NCSI_CAP_GENERIC_MC))
1333 return NOTIFY_OK;
1334
1335 nca.ndp = ndp;
Gavin Shana0509cb2016-10-04 11:25:51 +11001336 nca.req_flags = 0;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001337 nca.package = np->id;
1338 nca.channel = nc->id;
1339 nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap;
1340 ret = ncsi_xmit_cmd(&nca);
1341 if (ret) {
1342 netdev_warn(dev, "Fail to %s global multicast filter (%d)\n",
1343 (event == NETDEV_UP) ? "enable" : "disable", ret);
1344 return NOTIFY_DONE;
1345 }
1346
1347 return NOTIFY_OK;
1348}
1349
1350static struct notifier_block ncsi_inet6addr_notifier = {
1351 .notifier_call = ncsi_inet6addr_event,
1352};
1353#endif /* CONFIG_IPV6 */
1354
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001355static int ncsi_kick_channels(struct ncsi_dev_priv *ndp)
1356{
1357 struct ncsi_dev *nd = &ndp->ndev;
1358 struct ncsi_channel *nc;
1359 struct ncsi_package *np;
1360 unsigned long flags;
1361 unsigned int n = 0;
1362
1363 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1364 NCSI_FOR_EACH_CHANNEL(np, nc) {
1365 spin_lock_irqsave(&nc->lock, flags);
1366
1367 /* Channels may be busy, mark dirty instead of
1368 * kicking if;
1369 * a) not ACTIVE (configured)
1370 * b) in the channel_queue (to be configured)
1371 * c) it's ndev is in the config state
1372 */
1373 if (nc->state != NCSI_CHANNEL_ACTIVE) {
1374 if ((ndp->ndev.state & 0xff00) ==
1375 ncsi_dev_state_config ||
1376 !list_empty(&nc->link)) {
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301377 netdev_dbg(nd->dev,
1378 "NCSI: channel %p marked dirty\n",
1379 nc);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001380 nc->reconfigure_needed = true;
1381 }
1382 spin_unlock_irqrestore(&nc->lock, flags);
1383 continue;
1384 }
1385
1386 spin_unlock_irqrestore(&nc->lock, flags);
1387
1388 ncsi_stop_channel_monitor(nc);
1389 spin_lock_irqsave(&nc->lock, flags);
1390 nc->state = NCSI_CHANNEL_INACTIVE;
1391 spin_unlock_irqrestore(&nc->lock, flags);
1392
1393 spin_lock_irqsave(&ndp->lock, flags);
1394 list_add_tail_rcu(&nc->link, &ndp->channel_queue);
1395 spin_unlock_irqrestore(&ndp->lock, flags);
1396
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301397 netdev_dbg(nd->dev, "NCSI: kicked channel %p\n", nc);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001398 n++;
1399 }
1400 }
1401
1402 return n;
1403}
1404
1405int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
1406{
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001407 struct ncsi_dev_priv *ndp;
1408 unsigned int n_vids = 0;
1409 struct vlan_vid *vlan;
1410 struct ncsi_dev *nd;
1411 bool found = false;
1412
1413 if (vid == 0)
1414 return 0;
1415
1416 nd = ncsi_find_dev(dev);
1417 if (!nd) {
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001418 netdev_warn(dev, "NCSI: No net_device?\n");
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001419 return 0;
1420 }
1421
1422 ndp = TO_NCSI_DEV_PRIV(nd);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001423
1424 /* Add the VLAN id to our internal list */
1425 list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) {
1426 n_vids++;
1427 if (vlan->vid == vid) {
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301428 netdev_dbg(dev, "NCSI: vid %u already registered\n",
1429 vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001430 return 0;
1431 }
1432 }
Samuel Mendoza-Jonas6e9c0072017-10-11 16:54:27 +11001433 if (n_vids >= NCSI_MAX_VLAN_VIDS) {
1434 netdev_warn(dev,
1435 "tried to add vlan id %u but NCSI max already registered (%u)\n",
1436 vid, NCSI_MAX_VLAN_VIDS);
1437 return -ENOSPC;
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001438 }
1439
1440 vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
1441 if (!vlan)
1442 return -ENOMEM;
1443
1444 vlan->proto = proto;
1445 vlan->vid = vid;
1446 list_add_rcu(&vlan->list, &ndp->vlan_vids);
1447
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301448 netdev_dbg(dev, "NCSI: Added new vid %u\n", vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001449
1450 found = ncsi_kick_channels(ndp) != 0;
1451
1452 return found ? ncsi_process_next_channel(ndp) : 0;
1453}
Arnd Bergmannfd0c88b2017-09-05 10:05:47 +02001454EXPORT_SYMBOL_GPL(ncsi_vlan_rx_add_vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001455
1456int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
1457{
1458 struct vlan_vid *vlan, *tmp;
1459 struct ncsi_dev_priv *ndp;
1460 struct ncsi_dev *nd;
1461 bool found = false;
1462
1463 if (vid == 0)
1464 return 0;
1465
1466 nd = ncsi_find_dev(dev);
1467 if (!nd) {
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001468 netdev_warn(dev, "NCSI: no net_device?\n");
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001469 return 0;
1470 }
1471
1472 ndp = TO_NCSI_DEV_PRIV(nd);
1473
1474 /* Remove the VLAN id from our internal list */
1475 list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list)
1476 if (vlan->vid == vid) {
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301477 netdev_dbg(dev, "NCSI: vid %u found, removing\n", vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001478 list_del_rcu(&vlan->list);
1479 found = true;
1480 kfree(vlan);
1481 }
1482
1483 if (!found) {
Samuel Mendoza-Jonas9ef86902017-11-08 16:30:44 +11001484 netdev_err(dev, "NCSI: vid %u wasn't registered!\n", vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001485 return -EINVAL;
1486 }
1487
1488 found = ncsi_kick_channels(ndp) != 0;
1489
1490 return found ? ncsi_process_next_channel(ndp) : 0;
1491}
Arnd Bergmannfd0c88b2017-09-05 10:05:47 +02001492EXPORT_SYMBOL_GPL(ncsi_vlan_rx_kill_vid);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001493
Gavin Shan2d283bd2016-07-19 11:54:16 +10001494struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
1495 void (*handler)(struct ncsi_dev *ndev))
1496{
1497 struct ncsi_dev_priv *ndp;
1498 struct ncsi_dev *nd;
1499 unsigned long flags;
1500 int i;
1501
1502 /* Check if the device has been registered or not */
1503 nd = ncsi_find_dev(dev);
1504 if (nd)
1505 return nd;
1506
1507 /* Create NCSI device */
1508 ndp = kzalloc(sizeof(*ndp), GFP_ATOMIC);
1509 if (!ndp)
1510 return NULL;
1511
1512 nd = &ndp->ndev;
1513 nd->state = ncsi_dev_state_registered;
1514 nd->dev = dev;
1515 nd->handler = handler;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001516 ndp->pending_req_num = 0;
1517 INIT_LIST_HEAD(&ndp->channel_queue);
Samuel Mendoza-Jonas21acf632017-08-28 16:18:42 +10001518 INIT_LIST_HEAD(&ndp->vlan_vids);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001519 INIT_WORK(&ndp->work, ncsi_dev_work);
Gavin Shan2d283bd2016-07-19 11:54:16 +10001520
1521 /* Initialize private NCSI device */
1522 spin_lock_init(&ndp->lock);
1523 INIT_LIST_HEAD(&ndp->packages);
Gavin Shana15af542016-10-04 11:25:50 +11001524 ndp->request_id = NCSI_REQ_START_IDX;
Gavin Shan2d283bd2016-07-19 11:54:16 +10001525 for (i = 0; i < ARRAY_SIZE(ndp->requests); i++) {
1526 ndp->requests[i].id = i;
1527 ndp->requests[i].ndp = ndp;
Kees Cooke99e88a2017-10-16 14:43:17 -07001528 timer_setup(&ndp->requests[i].timer, ncsi_request_timeout, 0);
Gavin Shan2d283bd2016-07-19 11:54:16 +10001529 }
1530
1531 spin_lock_irqsave(&ncsi_dev_lock, flags);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001532#if IS_ENABLED(CONFIG_IPV6)
1533 ndp->inet6_addr_num = 0;
1534 if (list_empty(&ncsi_dev_list))
1535 register_inet6addr_notifier(&ncsi_inet6addr_notifier);
1536#endif
Gavin Shan2d283bd2016-07-19 11:54:16 +10001537 list_add_tail_rcu(&ndp->node, &ncsi_dev_list);
1538 spin_unlock_irqrestore(&ncsi_dev_lock, flags);
1539
Gavin Shane6f44ed2016-07-19 11:54:19 +10001540 /* Register NCSI packet Rx handler */
1541 ndp->ptype.type = cpu_to_be16(ETH_P_NCSI);
1542 ndp->ptype.func = ncsi_rcv_rsp;
1543 ndp->ptype.dev = dev;
1544 dev_add_pack(&ndp->ptype);
1545
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +11001546 /* Set up generic netlink interface */
1547 ncsi_init_netlink(dev);
1548
Gavin Shan2d283bd2016-07-19 11:54:16 +10001549 return nd;
1550}
1551EXPORT_SYMBOL_GPL(ncsi_register_dev);
1552
Gavin Shane6f44ed2016-07-19 11:54:19 +10001553int ncsi_start_dev(struct ncsi_dev *nd)
1554{
1555 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001556
1557 if (nd->state != ncsi_dev_state_registered &&
1558 nd->state != ncsi_dev_state_functional)
1559 return -ENOTTY;
1560
1561 if (!(ndp->flags & NCSI_DEV_PROBED)) {
Samuel Mendoza-Jonas8e13f702018-11-16 15:51:55 +11001562 ndp->package_probe_id = 0;
Gavin Shane6f44ed2016-07-19 11:54:19 +10001563 nd->state = ncsi_dev_state_probe;
1564 schedule_work(&ndp->work);
1565 return 0;
1566 }
1567
Samuel Mendoza-Jonas2878a2c2018-11-16 15:51:58 +11001568 return ncsi_reset_dev(nd);
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001569}
1570EXPORT_SYMBOL_GPL(ncsi_start_dev);
1571
1572void ncsi_stop_dev(struct ncsi_dev *nd)
1573{
1574 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
1575 struct ncsi_package *np;
1576 struct ncsi_channel *nc;
1577 bool chained;
1578 int old_state;
1579 unsigned long flags;
1580
Samuel Mendoza-Jonas2878a2c2018-11-16 15:51:58 +11001581 /* Stop the channel monitor on any active channels. Don't reset the
1582 * channel state so we know which were active when ncsi_start_dev()
1583 * is next called.
1584 */
Gavin Shane6f44ed2016-07-19 11:54:19 +10001585 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1586 NCSI_FOR_EACH_CHANNEL(np, nc) {
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001587 ncsi_stop_channel_monitor(nc);
1588
Gavin Shand8cedaa2016-10-04 11:25:47 +11001589 spin_lock_irqsave(&nc->lock, flags);
1590 chained = !list_empty(&nc->link);
1591 old_state = nc->state;
Gavin Shand8cedaa2016-10-04 11:25:47 +11001592 spin_unlock_irqrestore(&nc->lock, flags);
1593
1594 WARN_ON_ONCE(chained ||
Gavin Shane6f44ed2016-07-19 11:54:19 +10001595 old_state == NCSI_CHANNEL_INVISIBLE);
1596 }
1597 }
1598
Joel Stanley6e42a3f2018-06-19 15:08:33 +09301599 netdev_dbg(ndp->ndev.dev, "NCSI: Stopping device\n");
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001600 ncsi_report_link(ndp, true);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001601}
Gavin Shanc0cd1ba2016-10-04 11:25:53 +11001602EXPORT_SYMBOL_GPL(ncsi_stop_dev);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001603
Samuel Mendoza-Jonas2878a2c2018-11-16 15:51:58 +11001604int ncsi_reset_dev(struct ncsi_dev *nd)
1605{
1606 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
1607 struct ncsi_channel *nc, *active, *tmp;
1608 struct ncsi_package *np;
1609 unsigned long flags;
1610
1611 spin_lock_irqsave(&ndp->lock, flags);
1612
1613 if (!(ndp->flags & NCSI_DEV_RESET)) {
1614 /* Haven't been called yet, check states */
1615 switch (nd->state & ncsi_dev_state_major) {
1616 case ncsi_dev_state_registered:
1617 case ncsi_dev_state_probe:
1618 /* Not even probed yet - do nothing */
1619 spin_unlock_irqrestore(&ndp->lock, flags);
1620 return 0;
1621 case ncsi_dev_state_suspend:
1622 case ncsi_dev_state_config:
1623 /* Wait for the channel to finish its suspend/config
1624 * operation; once it finishes it will check for
1625 * NCSI_DEV_RESET and reset the state.
1626 */
1627 ndp->flags |= NCSI_DEV_RESET;
1628 spin_unlock_irqrestore(&ndp->lock, flags);
1629 return 0;
1630 }
1631 } else {
1632 switch (nd->state) {
1633 case ncsi_dev_state_suspend_done:
1634 case ncsi_dev_state_config_done:
1635 case ncsi_dev_state_functional:
1636 /* Ok */
1637 break;
1638 default:
1639 /* Current reset operation happening */
1640 spin_unlock_irqrestore(&ndp->lock, flags);
1641 return 0;
1642 }
1643 }
1644
1645 if (!list_empty(&ndp->channel_queue)) {
1646 /* Clear any channel queue we may have interrupted */
1647 list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link)
1648 list_del_init(&nc->link);
1649 }
1650 spin_unlock_irqrestore(&ndp->lock, flags);
1651
1652 active = NULL;
1653 NCSI_FOR_EACH_PACKAGE(ndp, np) {
1654 NCSI_FOR_EACH_CHANNEL(np, nc) {
1655 spin_lock_irqsave(&nc->lock, flags);
1656
1657 if (nc->state == NCSI_CHANNEL_ACTIVE) {
1658 active = nc;
1659 nc->state = NCSI_CHANNEL_INVISIBLE;
1660 spin_unlock_irqrestore(&nc->lock, flags);
1661 ncsi_stop_channel_monitor(nc);
1662 break;
1663 }
1664
1665 spin_unlock_irqrestore(&nc->lock, flags);
1666 }
1667 if (active)
1668 break;
1669 }
1670
1671 if (!active) {
1672 /* Done */
1673 spin_lock_irqsave(&ndp->lock, flags);
1674 ndp->flags &= ~NCSI_DEV_RESET;
1675 spin_unlock_irqrestore(&ndp->lock, flags);
1676 return ncsi_choose_active_channel(ndp);
1677 }
1678
1679 spin_lock_irqsave(&ndp->lock, flags);
1680 ndp->flags |= NCSI_DEV_RESET;
1681 ndp->active_channel = active;
1682 ndp->active_package = active->package;
1683 spin_unlock_irqrestore(&ndp->lock, flags);
1684
1685 nd->state = ncsi_dev_state_suspend;
1686 schedule_work(&ndp->work);
1687 return 0;
1688}
1689
Gavin Shan2d283bd2016-07-19 11:54:16 +10001690void ncsi_unregister_dev(struct ncsi_dev *nd)
1691{
1692 struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd);
1693 struct ncsi_package *np, *tmp;
1694 unsigned long flags;
1695
Gavin Shane6f44ed2016-07-19 11:54:19 +10001696 dev_remove_pack(&ndp->ptype);
1697
Gavin Shan2d283bd2016-07-19 11:54:16 +10001698 list_for_each_entry_safe(np, tmp, &ndp->packages, node)
1699 ncsi_remove_package(np);
1700
1701 spin_lock_irqsave(&ncsi_dev_lock, flags);
1702 list_del_rcu(&ndp->node);
Gavin Shane6f44ed2016-07-19 11:54:19 +10001703#if IS_ENABLED(CONFIG_IPV6)
1704 if (list_empty(&ncsi_dev_list))
1705 unregister_inet6addr_notifier(&ncsi_inet6addr_notifier);
1706#endif
Gavin Shan2d283bd2016-07-19 11:54:16 +10001707 spin_unlock_irqrestore(&ncsi_dev_lock, flags);
1708
Samuel Mendoza-Jonas955dc682018-03-05 11:39:05 +11001709 ncsi_unregister_netlink(nd->dev);
1710
Gavin Shan2d283bd2016-07-19 11:54:16 +10001711 kfree(ndp);
1712}
1713EXPORT_SYMBOL_GPL(ncsi_unregister_dev);