Merge remote-tracking branch 'staging/staging-next'
[deliverable/linux.git] / drivers / staging / most / aim-network / networking.c
CommitLineData
d4a8ce7f
CG
1/*
2 * Networking AIM - Networking Application Interface Module for MostCore
3 *
4 * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * This file is licensed under GPLv2.
12 */
13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
16#include <linux/module.h>
17#include <linux/netdevice.h>
18#include <linux/etherdevice.h>
19#include <linux/slab.h>
20#include <linux/init.h>
21#include <linux/list.h>
22#include <linux/wait.h>
23#include <linux/kobject.h>
24#include "mostcore.h"
25#include "networking.h"
26
d4a8ce7f
CG
27#define MEP_HDR_LEN 8
28#define MDP_HDR_LEN 16
29#define MAMAC_DATA_LEN (1024 - MDP_HDR_LEN)
30
31#define PMHL 5
32
33#define PMS_TELID_UNSEGM_MAMAC 0x0A
34#define PMS_FIFONO_MDP 0x01
35#define PMS_FIFONO_MEP 0x04
36#define PMS_MSGTYPE_DATA 0x04
37#define PMS_DEF_PRIO 0
38#define MEP_DEF_RETRY 15
39
40#define PMS_FIFONO_MASK 0x07
41#define PMS_FIFONO_SHIFT 3
42#define PMS_RETRY_SHIFT 4
43#define PMS_TELID_MASK 0x0F
44#define PMS_TELID_SHIFT 4
45
46#define HB(value) ((u8)((u16)(value) >> 8))
47#define LB(value) ((u8)(value))
48
d4a8ce7f
CG
49#define EXTRACT_BIT_SET(bitset_name, value) \
50 (((value) >> bitset_name##_SHIFT) & bitset_name##_MASK)
51
52#define PMS_IS_MEP(buf, len) \
53 ((len) > MEP_HDR_LEN && \
54 EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MEP)
55
56#define PMS_IS_MAMAC(buf, len) \
57 ((len) > MDP_HDR_LEN && \
58 EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MDP && \
59 EXTRACT_BIT_SET(PMS_TELID, (buf)[14]) == PMS_TELID_UNSEGM_MAMAC)
60
61struct net_dev_channel {
62 bool linked;
63 int ch_id;
64};
65
66struct net_dev_context {
67 struct most_interface *iface;
68 bool channels_opened;
69 bool is_mamac;
70 unsigned char link_stat;
71 struct net_device *dev;
72 struct net_dev_channel rx;
73 struct net_dev_channel tx;
74 struct list_head list;
75};
76
77static struct list_head net_devices = LIST_HEAD_INIT(net_devices);
78static struct spinlock list_lock;
f13f6981 79static struct most_aim aim;
d4a8ce7f 80
d4a8ce7f
CG
81static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo)
82{
83 u8 *buff = mbo->virt_address;
84 const u8 broadcast[] = { 0x03, 0xFF };
85 const u8 *dest_addr = skb->data + 4;
86 const u8 *eth_type = skb->data + 12;
87 unsigned int payload_len = skb->len - ETH_HLEN;
88 unsigned int mdp_len = payload_len + MDP_HDR_LEN;
89
90 if (mbo->buffer_length < mdp_len) {
91 pr_err("drop: too small buffer! (%d for %d)\n",
92 mbo->buffer_length, mdp_len);
93 return -EINVAL;
94 }
95
96 if (skb->len < ETH_HLEN) {
97 pr_err("drop: too small packet! (%d)\n", skb->len);
98 return -EINVAL;
99 }
100
101 if (dest_addr[0] == 0xFF && dest_addr[1] == 0xFF)
102 dest_addr = broadcast;
103
104 *buff++ = HB(mdp_len - 2);
105 *buff++ = LB(mdp_len - 2);
106
107 *buff++ = PMHL;
108 *buff++ = (PMS_FIFONO_MDP << PMS_FIFONO_SHIFT) | PMS_MSGTYPE_DATA;
109 *buff++ = PMS_DEF_PRIO;
110 *buff++ = dest_addr[0];
111 *buff++ = dest_addr[1];
112 *buff++ = 0x00;
113
114 *buff++ = HB(payload_len + 6);
115 *buff++ = LB(payload_len + 6);
116
117 /* end of FPH here */
118
119 *buff++ = eth_type[0];
120 *buff++ = eth_type[1];
121 *buff++ = 0;
122 *buff++ = 0;
123
124 *buff++ = PMS_TELID_UNSEGM_MAMAC << 4 | HB(payload_len);
125 *buff++ = LB(payload_len);
126
127 memcpy(buff, skb->data + ETH_HLEN, payload_len);
128 mbo->buffer_length = mdp_len;
129 return 0;
130}
131
132static int skb_to_mep(const struct sk_buff *skb, struct mbo *mbo)
133{
134 u8 *buff = mbo->virt_address;
135 unsigned int mep_len = skb->len + MEP_HDR_LEN;
136
137 if (mbo->buffer_length < mep_len) {
138 pr_err("drop: too small buffer! (%d for %d)\n",
139 mbo->buffer_length, mep_len);
140 return -EINVAL;
141 }
142
143 *buff++ = HB(mep_len - 2);
144 *buff++ = LB(mep_len - 2);
145
146 *buff++ = PMHL;
147 *buff++ = (PMS_FIFONO_MEP << PMS_FIFONO_SHIFT) | PMS_MSGTYPE_DATA;
148 *buff++ = (MEP_DEF_RETRY << PMS_RETRY_SHIFT) | PMS_DEF_PRIO;
149 *buff++ = 0;
150 *buff++ = 0;
151 *buff++ = 0;
152
153 memcpy(buff, skb->data, skb->len);
154 mbo->buffer_length = mep_len;
155 return 0;
156}
157
158static int most_nd_set_mac_address(struct net_device *dev, void *p)
159{
160 struct net_dev_context *nd = dev->ml_priv;
161 int err = eth_mac_addr(dev, p);
162
163 if (err)
164 return err;
165
166 BUG_ON(nd->dev != dev);
167
168 nd->is_mamac =
169 (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0 &&
170 dev->dev_addr[2] == 0 && dev->dev_addr[3] == 0);
171
172 /*
173 * Set default MTU for the given packet type.
174 * It is still possible to change MTU using ip tools afterwards.
175 */
176 dev->mtu = nd->is_mamac ? MAMAC_DATA_LEN : ETH_DATA_LEN;
177
178 return 0;
179}
180
181static int most_nd_open(struct net_device *dev)
182{
183 struct net_dev_context *nd = dev->ml_priv;
184
efc0cfa1 185 netdev_info(dev, "open net device\n");
d4a8ce7f
CG
186
187 BUG_ON(nd->dev != dev);
188
189 if (nd->channels_opened)
190 return -EFAULT;
191
192 BUG_ON(!nd->tx.linked || !nd->rx.linked);
193
f13f6981 194 if (most_start_channel(nd->iface, nd->rx.ch_id, &aim)) {
efc0cfa1 195 netdev_err(dev, "most_start_channel() failed\n");
d4a8ce7f
CG
196 return -EBUSY;
197 }
198
f13f6981 199 if (most_start_channel(nd->iface, nd->tx.ch_id, &aim)) {
efc0cfa1 200 netdev_err(dev, "most_start_channel() failed\n");
f13f6981 201 most_stop_channel(nd->iface, nd->rx.ch_id, &aim);
d4a8ce7f
CG
202 return -EBUSY;
203 }
204
205 nd->channels_opened = true;
206
207 if (nd->is_mamac) {
208 nd->link_stat = 1;
209 netif_wake_queue(dev);
210 } else {
211 nd->iface->request_netinfo(nd->iface, nd->tx.ch_id);
212 }
213
214 return 0;
215}
216
217static int most_nd_stop(struct net_device *dev)
218{
219 struct net_dev_context *nd = dev->ml_priv;
220
efc0cfa1 221 netdev_info(dev, "stop net device\n");
d4a8ce7f
CG
222
223 BUG_ON(nd->dev != dev);
224 netif_stop_queue(dev);
225
226 if (nd->channels_opened) {
f13f6981
CG
227 most_stop_channel(nd->iface, nd->rx.ch_id, &aim);
228 most_stop_channel(nd->iface, nd->tx.ch_id, &aim);
d4a8ce7f
CG
229 nd->channels_opened = false;
230 }
231
232 return 0;
233}
234
235static netdev_tx_t most_nd_start_xmit(struct sk_buff *skb,
236 struct net_device *dev)
237{
238 struct net_dev_context *nd = dev->ml_priv;
239 struct mbo *mbo;
240 int ret;
241
242 BUG_ON(nd->dev != dev);
243
71457d48 244 mbo = most_get_mbo(nd->iface, nd->tx.ch_id, &aim);
d4a8ce7f
CG
245
246 if (!mbo) {
247 netif_stop_queue(dev);
248 dev->stats.tx_fifo_errors++;
249 return NETDEV_TX_BUSY;
250 }
251
252 if (nd->is_mamac)
253 ret = skb_to_mamac(skb, mbo);
254 else
255 ret = skb_to_mep(skb, mbo);
256
257 if (ret) {
258 most_put_mbo(mbo);
259 dev->stats.tx_dropped++;
260 kfree_skb(skb);
261 return NETDEV_TX_OK;
262 }
263
264 most_submit_mbo(mbo);
265 dev->stats.tx_packets++;
266 dev->stats.tx_bytes += skb->len;
267 kfree_skb(skb);
268 return NETDEV_TX_OK;
269}
270
271static const struct net_device_ops most_nd_ops = {
272 .ndo_open = most_nd_open,
273 .ndo_stop = most_nd_stop,
274 .ndo_start_xmit = most_nd_start_xmit,
275 .ndo_set_mac_address = most_nd_set_mac_address,
276};
277
278static void most_nd_setup(struct net_device *dev)
279{
efc0cfa1 280 netdev_info(dev, "setup net device\n");
d4a8ce7f
CG
281 ether_setup(dev);
282 dev->netdev_ops = &most_nd_ops;
283}
284
285static void most_net_rm_netdev_safe(struct net_dev_context *nd)
286{
287 if (!nd->dev)
288 return;
289
290 pr_info("remove net device %p\n", nd->dev);
291
292 unregister_netdev(nd->dev);
293 free_netdev(nd->dev);
1865e4ea 294 nd->dev = NULL;
d4a8ce7f
CG
295}
296
297static struct net_dev_context *get_net_dev_context(
298 struct most_interface *iface)
299{
300 struct net_dev_context *nd, *tmp;
a75c0312 301 unsigned long flags;
d4a8ce7f 302
a75c0312 303 spin_lock_irqsave(&list_lock, flags);
d4a8ce7f
CG
304 list_for_each_entry_safe(nd, tmp, &net_devices, list) {
305 if (nd->iface == iface) {
a75c0312 306 spin_unlock_irqrestore(&list_lock, flags);
d4a8ce7f
CG
307 return nd;
308 }
309 }
a75c0312 310 spin_unlock_irqrestore(&list_lock, flags);
b3c1a617 311 return NULL;
d4a8ce7f
CG
312}
313
314static int aim_probe_channel(struct most_interface *iface, int channel_idx,
315 struct most_channel_config *ccfg,
316 struct kobject *parent, char *name)
317{
318 struct net_dev_context *nd;
319 struct net_dev_channel *ch;
a75c0312 320 unsigned long flags;
d4a8ce7f
CG
321
322 if (!iface)
323 return -EINVAL;
324
325 if (ccfg->data_type != MOST_CH_ASYNC)
326 return -EINVAL;
327
328 nd = get_net_dev_context(iface);
329
330 if (!nd) {
331 nd = kzalloc(sizeof(*nd), GFP_KERNEL);
332 if (!nd)
333 return -ENOMEM;
334
335 nd->iface = iface;
336
a75c0312 337 spin_lock_irqsave(&list_lock, flags);
d4a8ce7f 338 list_add(&nd->list, &net_devices);
a75c0312 339 spin_unlock_irqrestore(&list_lock, flags);
d4a8ce7f
CG
340 }
341
342 ch = ccfg->direction == MOST_CH_TX ? &nd->tx : &nd->rx;
343 if (ch->linked) {
344 pr_err("only one channel per instance & direction allowed\n");
345 return -EINVAL;
346 }
347
348 if (nd->tx.linked || nd->rx.linked) {
349 struct net_device *dev =
1446ff09
CG
350 alloc_netdev(0, "meth%d", NET_NAME_UNKNOWN,
351 most_nd_setup);
d4a8ce7f
CG
352
353 if (!dev) {
354 pr_err("no memory for net_device\n");
355 return -ENOMEM;
356 }
357
358 nd->dev = dev;
25ef42f3
CG
359 ch->ch_id = channel_idx;
360 ch->linked = true;
d4a8ce7f
CG
361
362 dev->ml_priv = nd;
363 if (register_netdev(dev)) {
364 pr_err("registering net device failed\n");
25ef42f3 365 ch->linked = false;
d4a8ce7f
CG
366 free_netdev(dev);
367 return -EINVAL;
368 }
369 }
370
371 ch->ch_id = channel_idx;
372 ch->linked = true;
373
374 return 0;
375}
376
377static int aim_disconnect_channel(struct most_interface *iface,
378 int channel_idx)
379{
380 struct net_dev_context *nd;
381 struct net_dev_channel *ch;
a75c0312 382 unsigned long flags;
d4a8ce7f
CG
383
384 nd = get_net_dev_context(iface);
385 if (!nd)
386 return -EINVAL;
387
388 if (nd->rx.linked && channel_idx == nd->rx.ch_id)
389 ch = &nd->rx;
390 else if (nd->tx.linked && channel_idx == nd->tx.ch_id)
391 ch = &nd->tx;
392 else
393 return -EINVAL;
394
395 ch->linked = false;
396
397 /*
398 * do not call most_stop_channel() here, because channels are
399 * going to be closed in ndo_stop() after unregister_netdev()
400 */
401 most_net_rm_netdev_safe(nd);
402
403 if (!nd->rx.linked && !nd->tx.linked) {
a75c0312 404 spin_lock_irqsave(&list_lock, flags);
d4a8ce7f 405 list_del(&nd->list);
a75c0312 406 spin_unlock_irqrestore(&list_lock, flags);
d4a8ce7f
CG
407 kfree(nd);
408 }
409
410 return 0;
411}
412
413static int aim_resume_tx_channel(struct most_interface *iface,
414 int channel_idx)
415{
416 struct net_dev_context *nd;
417
418 nd = get_net_dev_context(iface);
419 if (!nd || !nd->channels_opened || nd->tx.ch_id != channel_idx)
420 return 0;
421
422 if (!nd->dev)
423 return 0;
424
425 netif_wake_queue(nd->dev);
426 return 0;
427}
428
429static int aim_rx_data(struct mbo *mbo)
430{
431 const u32 zero = 0;
432 struct net_dev_context *nd;
433 char *buf = mbo->virt_address;
2aa9b96f 434 u32 len = mbo->processed_length;
d4a8ce7f
CG
435 struct sk_buff *skb;
436 struct net_device *dev;
1c55d30b 437 unsigned int skb_len;
d4a8ce7f
CG
438
439 nd = get_net_dev_context(mbo->ifp);
440 if (!nd || !nd->channels_opened || nd->rx.ch_id != mbo->hdm_channel_id)
441 return -EIO;
442
443 dev = nd->dev;
444 if (!dev) {
445 pr_err_once("drop packet: missing net_device\n");
446 return -EIO;
447 }
448
449 if (nd->is_mamac) {
450 if (!PMS_IS_MAMAC(buf, len))
451 return -EIO;
452
453 skb = dev_alloc_skb(len - MDP_HDR_LEN + 2 * ETH_ALEN + 2);
454 } else {
455 if (!PMS_IS_MEP(buf, len))
456 return -EIO;
457
458 skb = dev_alloc_skb(len - MEP_HDR_LEN);
459 }
460
461 if (!skb) {
462 dev->stats.rx_dropped++;
463 pr_err_once("drop packet: no memory for skb\n");
464 goto out;
465 }
466
467 skb->dev = dev;
468
469 if (nd->is_mamac) {
470 /* dest */
d58b0ee3 471 ether_addr_copy(skb_put(skb, ETH_ALEN), dev->dev_addr);
d4a8ce7f
CG
472
473 /* src */
474 memcpy(skb_put(skb, 4), &zero, 4);
475 memcpy(skb_put(skb, 2), buf + 5, 2);
476
477 /* eth type */
478 memcpy(skb_put(skb, 2), buf + 10, 2);
479
480 buf += MDP_HDR_LEN;
481 len -= MDP_HDR_LEN;
482 } else {
483 buf += MEP_HDR_LEN;
484 len -= MEP_HDR_LEN;
485 }
486
487 memcpy(skb_put(skb, len), buf, len);
488 skb->protocol = eth_type_trans(skb, dev);
1c55d30b
CG
489 skb_len = skb->len;
490 if (netif_rx(skb) == NET_RX_SUCCESS) {
491 dev->stats.rx_packets++;
492 dev->stats.rx_bytes += skb_len;
493 } else {
494 dev->stats.rx_dropped++;
495 }
d4a8ce7f
CG
496
497out:
498 most_put_mbo(mbo);
499 return 0;
500}
501
ec5c00af
CG
502static struct most_aim aim = {
503 .name = "networking",
504 .probe_channel = aim_probe_channel,
505 .disconnect_channel = aim_disconnect_channel,
506 .tx_completion = aim_resume_tx_channel,
507 .rx_completion = aim_rx_data,
508};
509
d4a8ce7f
CG
510static int __init most_net_init(void)
511{
512 pr_info("most_net_init()\n");
513 spin_lock_init(&list_lock);
d4a8ce7f
CG
514 return most_register_aim(&aim);
515}
516
517static void __exit most_net_exit(void)
518{
519 struct net_dev_context *nd, *tmp;
a75c0312 520 unsigned long flags;
d4a8ce7f 521
a75c0312 522 spin_lock_irqsave(&list_lock, flags);
d4a8ce7f
CG
523 list_for_each_entry_safe(nd, tmp, &net_devices, list) {
524 list_del(&nd->list);
a75c0312 525 spin_unlock_irqrestore(&list_lock, flags);
d4a8ce7f
CG
526 /*
527 * do not call most_stop_channel() here, because channels are
528 * going to be closed in ndo_stop() after unregister_netdev()
529 */
530 most_net_rm_netdev_safe(nd);
531 kfree(nd);
a75c0312 532 spin_lock_irqsave(&list_lock, flags);
d4a8ce7f 533 }
a75c0312 534 spin_unlock_irqrestore(&list_lock, flags);
d4a8ce7f
CG
535
536 most_deregister_aim(&aim);
537 pr_info("most_net_exit()\n");
538}
539
540/**
541 * most_deliver_netinfo - callback for HDM to be informed about HW's MAC
542 * @param iface - most interface instance
543 * @param link_stat - link status
544 * @param mac_addr - MAC address
545 */
546void most_deliver_netinfo(struct most_interface *iface,
547 unsigned char link_stat, unsigned char *mac_addr)
548{
549 struct net_dev_context *nd;
550 struct net_device *dev;
551
552 pr_info("Received netinfo from %s\n", iface->description);
553
554 nd = get_net_dev_context(iface);
555 if (!nd)
556 return;
557
558 dev = nd->dev;
559 if (!dev)
560 return;
561
562 if (mac_addr)
d58b0ee3 563 ether_addr_copy(dev->dev_addr, mac_addr);
d4a8ce7f
CG
564
565 if (nd->link_stat != link_stat) {
566 nd->link_stat = link_stat;
567 if (nd->link_stat)
568 netif_wake_queue(dev);
569 else
570 netif_stop_queue(dev);
571 }
572}
573EXPORT_SYMBOL(most_deliver_netinfo);
574
575module_init(most_net_init);
576module_exit(most_net_exit);
577MODULE_LICENSE("GPL");
578MODULE_AUTHOR("Andrey Shvetsov <andrey.shvetsov@k2l.de>");
579MODULE_DESCRIPTION("Networking Application Interface Module for MostCore");
This page took 0.183678 seconds and 5 git commands to generate.