diff options
Diffstat (limited to 'recipes-kernel/aim-network/files/networking.c')
-rw-r--r-- | recipes-kernel/aim-network/files/networking.c | 574 |
1 files changed, 0 insertions, 574 deletions
diff --git a/recipes-kernel/aim-network/files/networking.c b/recipes-kernel/aim-network/files/networking.c deleted file mode 100644 index 3cd90f766..000000000 --- a/recipes-kernel/aim-network/files/networking.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Networking AIM - Networking Application Interface Module for MostCore - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This file is licensed under GPLv2. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/list.h> -#include <linux/wait.h> -#include <linux/kobject.h> -#include "mostcore.h" -#include "networking.h" - -#define MEP_HDR_LEN 8 -#define MDP_HDR_LEN 16 -#define MAMAC_DATA_LEN (1024 - MDP_HDR_LEN) - -#define PMHL 5 - -#define PMS_TELID_UNSEGM_MAMAC 0x0A -#define PMS_FIFONO_MDP 0x01 -#define PMS_FIFONO_MEP 0x04 -#define PMS_MSGTYPE_DATA 0x04 -#define PMS_DEF_PRIO 0 -#define MEP_DEF_RETRY 15 - -#define PMS_FIFONO_MASK 0x07 -#define PMS_FIFONO_SHIFT 3 -#define PMS_RETRY_SHIFT 4 -#define PMS_TELID_MASK 0x0F -#define PMS_TELID_SHIFT 4 - -#define HB(value) ((u8)((u16)(value) >> 8)) -#define LB(value) ((u8)(value)) - -#define EXTRACT_BIT_SET(bitset_name, value) \ - (((value) >> bitset_name##_SHIFT) & bitset_name##_MASK) - -#define PMS_IS_MEP(buf, len) \ - ((len) > MEP_HDR_LEN && \ - EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MEP) - -#define PMS_IS_MAMAC(buf, len) \ - ((len) > MDP_HDR_LEN && \ - EXTRACT_BIT_SET(PMS_FIFONO, (buf)[3]) == PMS_FIFONO_MDP && \ - EXTRACT_BIT_SET(PMS_TELID, (buf)[14]) == PMS_TELID_UNSEGM_MAMAC) - -struct net_dev_channel { - bool linked; - int ch_id; -}; - -struct net_dev_context { - struct most_interface *iface; - bool channels_opened; - bool is_mamac; - unsigned char link_stat; - struct net_device *dev; - struct net_dev_channel rx; - struct net_dev_channel tx; - struct list_head list; -}; - -static struct list_head net_devices = LIST_HEAD_INIT(net_devices); -static struct spinlock list_lock; -static struct most_aim aim; - -static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo) -{ - u8 *buff = mbo->virt_address; - const u8 broadcast[] = { 0x03, 0xFF }; - const u8 *dest_addr = skb->data + 4; - const u8 *eth_type = skb->data + 12; - unsigned int payload_len = skb->len - ETH_HLEN; - unsigned int mdp_len = payload_len + MDP_HDR_LEN; - - if (mbo->buffer_length < mdp_len) { - pr_err("drop: too small buffer! (%d for %d)\n", - mbo->buffer_length, mdp_len); - return -EINVAL; - } - - if (skb->len < ETH_HLEN) { - pr_err("drop: too small packet! (%d)\n", skb->len); - return -EINVAL; - } - - if (dest_addr[0] == 0xFF && dest_addr[1] == 0xFF) - dest_addr = broadcast; - - *buff++ = HB(mdp_len - 2); - *buff++ = LB(mdp_len - 2); - - *buff++ = PMHL; - *buff++ = (PMS_FIFONO_MDP << PMS_FIFONO_SHIFT) | PMS_MSGTYPE_DATA; - *buff++ = PMS_DEF_PRIO; - *buff++ = dest_addr[0]; - *buff++ = dest_addr[1]; - *buff++ = 0x00; - - *buff++ = HB(payload_len + 6); - *buff++ = LB(payload_len + 6); - - /* end of FPH here */ - - *buff++ = eth_type[0]; - *buff++ = eth_type[1]; - *buff++ = 0; - *buff++ = 0; - - *buff++ = PMS_TELID_UNSEGM_MAMAC << 4 | HB(payload_len); - *buff++ = LB(payload_len); - - memcpy(buff, skb->data + ETH_HLEN, payload_len); - mbo->buffer_length = mdp_len; - return 0; -} - -static int skb_to_mep(const struct sk_buff *skb, struct mbo *mbo) -{ - u8 *buff = mbo->virt_address; - unsigned int mep_len = skb->len + MEP_HDR_LEN; - - if (mbo->buffer_length < mep_len) { - pr_err("drop: too small buffer! (%d for %d)\n", - mbo->buffer_length, mep_len); - return -EINVAL; - } - - *buff++ = HB(mep_len - 2); - *buff++ = LB(mep_len - 2); - - *buff++ = PMHL; - *buff++ = (PMS_FIFONO_MEP << PMS_FIFONO_SHIFT) | PMS_MSGTYPE_DATA; - *buff++ = (MEP_DEF_RETRY << PMS_RETRY_SHIFT) | PMS_DEF_PRIO; - *buff++ = 0; - *buff++ = 0; - *buff++ = 0; - - memcpy(buff, skb->data, skb->len); - mbo->buffer_length = mep_len; - return 0; -} - -static int most_nd_set_mac_address(struct net_device *dev, void *p) -{ - struct net_dev_context *nd = dev->ml_priv; - int err = eth_mac_addr(dev, p); - - if (err) - return err; - - BUG_ON(nd->dev != dev); - - nd->is_mamac = - (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0 && - dev->dev_addr[2] == 0 && dev->dev_addr[3] == 0); - - /* - * Set default MTU for the given packet type. - * It is still possible to change MTU using ip tools afterwards. - */ - dev->mtu = nd->is_mamac ? MAMAC_DATA_LEN : ETH_DATA_LEN; - - return 0; -} - -static int most_nd_open(struct net_device *dev) -{ - struct net_dev_context *nd = dev->ml_priv; - - netdev_info(dev, "open net device\n"); - - BUG_ON(nd->dev != dev); - - if (nd->channels_opened) - return -EFAULT; - - BUG_ON(!nd->tx.linked || !nd->rx.linked); - - if (most_start_channel(nd->iface, nd->rx.ch_id, &aim)) { - netdev_err(dev, "most_start_channel() failed\n"); - return -EBUSY; - } - - if (most_start_channel(nd->iface, nd->tx.ch_id, &aim)) { - netdev_err(dev, "most_start_channel() failed\n"); - most_stop_channel(nd->iface, nd->rx.ch_id, &aim); - return -EBUSY; - } - - nd->channels_opened = true; - - if (nd->is_mamac) { - nd->link_stat = 1; - netif_wake_queue(dev); - } else { - nd->iface->request_netinfo(nd->iface, nd->tx.ch_id); - } - - return 0; -} - -static int most_nd_stop(struct net_device *dev) -{ - struct net_dev_context *nd = dev->ml_priv; - - netdev_info(dev, "stop net device\n"); - - BUG_ON(nd->dev != dev); - netif_stop_queue(dev); - - if (nd->channels_opened) { - most_stop_channel(nd->iface, nd->rx.ch_id, &aim); - most_stop_channel(nd->iface, nd->tx.ch_id, &aim); - nd->channels_opened = false; - } - - return 0; -} - -static netdev_tx_t most_nd_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct net_dev_context *nd = dev->ml_priv; - struct mbo *mbo; - int ret; - - BUG_ON(nd->dev != dev); - - mbo = most_get_mbo(nd->iface, nd->tx.ch_id, &aim); - - if (!mbo) { - netif_stop_queue(dev); - dev->stats.tx_fifo_errors++; - return NETDEV_TX_BUSY; - } - - if (nd->is_mamac) - ret = skb_to_mamac(skb, mbo); - else - ret = skb_to_mep(skb, mbo); - - if (ret) { - most_put_mbo(mbo); - dev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; - } - - most_submit_mbo(mbo); - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - kfree_skb(skb); - return NETDEV_TX_OK; -} - -static const struct net_device_ops most_nd_ops = { - .ndo_open = most_nd_open, - .ndo_stop = most_nd_stop, - .ndo_start_xmit = most_nd_start_xmit, - .ndo_set_mac_address = most_nd_set_mac_address, -}; - -static void most_nd_setup(struct net_device *dev) -{ - netdev_info(dev, "setup net device\n"); - ether_setup(dev); - dev->netdev_ops = &most_nd_ops; -} - -static void most_net_rm_netdev_safe(struct net_dev_context *nd) -{ - if (!nd->dev) - return; - - pr_info("remove net device %p\n", nd->dev); - - unregister_netdev(nd->dev); - free_netdev(nd->dev); - nd->dev = NULL; -} - -static struct net_dev_context *get_net_dev_context( - struct most_interface *iface) -{ - struct net_dev_context *nd, *tmp; - - spin_lock(&list_lock); - list_for_each_entry_safe(nd, tmp, &net_devices, list) { - if (nd->iface == iface) { - spin_unlock(&list_lock); - return nd; - } - } - spin_unlock(&list_lock); - return NULL; -} - -static int aim_probe_channel(struct most_interface *iface, int channel_idx, - struct most_channel_config *ccfg, - struct kobject *parent, char *name) -{ - struct net_dev_context *nd; - struct net_dev_channel *ch; - - if (!iface) - return -EINVAL; - - if (ccfg->data_type != MOST_CH_ASYNC) - return -EINVAL; - - nd = get_net_dev_context(iface); - - if (!nd) { - nd = kzalloc(sizeof(*nd), GFP_KERNEL); - if (!nd) - return -ENOMEM; - - nd->iface = iface; - - spin_lock(&list_lock); - list_add(&nd->list, &net_devices); - spin_unlock(&list_lock); - } - - ch = ccfg->direction == MOST_CH_TX ? &nd->tx : &nd->rx; - if (ch->linked) { - pr_err("only one channel per instance & direction allowed\n"); - return -EINVAL; - } - - if (nd->tx.linked || nd->rx.linked) { - struct net_device *dev = - alloc_netdev(0, "meth%d", most_nd_setup); - - if (!dev) { - pr_err("no memory for net_device\n"); - return -ENOMEM; - } - - nd->dev = dev; - ch->ch_id = channel_idx; - ch->linked = true; - - dev->ml_priv = nd; - if (register_netdev(dev)) { - pr_err("registering net device failed\n"); - ch->linked = false; - free_netdev(dev); - return -EINVAL; - } - } - - ch->ch_id = channel_idx; - ch->linked = true; - - return 0; -} - -static int aim_disconnect_channel(struct most_interface *iface, - int channel_idx) -{ - struct net_dev_context *nd; - struct net_dev_channel *ch; - - nd = get_net_dev_context(iface); - if (!nd) - return -EINVAL; - - if (nd->rx.linked && channel_idx == nd->rx.ch_id) - ch = &nd->rx; - else if (nd->tx.linked && channel_idx == nd->tx.ch_id) - ch = &nd->tx; - else - return -EINVAL; - - ch->linked = false; - - /* - * do not call most_stop_channel() here, because channels are - * going to be closed in ndo_stop() after unregister_netdev() - */ - most_net_rm_netdev_safe(nd); - - if (!nd->rx.linked && !nd->tx.linked) { - spin_lock(&list_lock); - list_del(&nd->list); - spin_unlock(&list_lock); - kfree(nd); - } - - return 0; -} - -static int aim_resume_tx_channel(struct most_interface *iface, - int channel_idx) -{ - struct net_dev_context *nd; - - nd = get_net_dev_context(iface); - if (!nd || !nd->channels_opened || nd->tx.ch_id != channel_idx) - return 0; - - if (!nd->dev) - return 0; - - netif_wake_queue(nd->dev); - return 0; -} - -static int aim_rx_data(struct mbo *mbo) -{ - const u32 zero = 0; - struct net_dev_context *nd; - char *buf = mbo->virt_address; - u32 len = mbo->processed_length; - struct sk_buff *skb; - struct net_device *dev; - unsigned int skb_len; - - nd = get_net_dev_context(mbo->ifp); - if (!nd || !nd->channels_opened || nd->rx.ch_id != mbo->hdm_channel_id) - return -EIO; - - dev = nd->dev; - if (!dev) { - pr_err_once("drop packet: missing net_device\n"); - return -EIO; - } - - if (nd->is_mamac) { - if (!PMS_IS_MAMAC(buf, len)) - return -EIO; - - skb = dev_alloc_skb(len - MDP_HDR_LEN + 2 * ETH_ALEN + 2); - } else { - if (!PMS_IS_MEP(buf, len)) - return -EIO; - - skb = dev_alloc_skb(len - MEP_HDR_LEN); - } - - if (!skb) { - dev->stats.rx_dropped++; - pr_err_once("drop packet: no memory for skb\n"); - goto out; - } - - skb->dev = dev; - - if (nd->is_mamac) { - /* dest */ - memcpy(skb_put(skb, ETH_ALEN), dev->dev_addr, ETH_ALEN); - - /* src */ - memcpy(skb_put(skb, 4), &zero, 4); - memcpy(skb_put(skb, 2), buf + 5, 2); - - /* eth type */ - memcpy(skb_put(skb, 2), buf + 10, 2); - - buf += MDP_HDR_LEN; - len -= MDP_HDR_LEN; - } else { - buf += MEP_HDR_LEN; - len -= MEP_HDR_LEN; - } - - memcpy(skb_put(skb, len), buf, len); - skb->protocol = eth_type_trans(skb, dev); - skb_len = skb->len; - if (netif_rx(skb) == NET_RX_SUCCESS) { - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb_len; - } else { - dev->stats.rx_dropped++; - } - -out: - most_put_mbo(mbo); - return 0; -} - -static struct most_aim aim = { - .name = "networking", - .probe_channel = aim_probe_channel, - .disconnect_channel = aim_disconnect_channel, - .tx_completion = aim_resume_tx_channel, - .rx_completion = aim_rx_data, -}; - -static int __init most_net_init(void) -{ - pr_info("most_net_init()\n"); - spin_lock_init(&list_lock); - return most_register_aim(&aim); -} - -static void __exit most_net_exit(void) -{ - struct net_dev_context *nd, *tmp; - - spin_lock(&list_lock); - list_for_each_entry_safe(nd, tmp, &net_devices, list) { - list_del(&nd->list); - spin_unlock(&list_lock); - /* - * do not call most_stop_channel() here, because channels are - * going to be closed in ndo_stop() after unregister_netdev() - */ - most_net_rm_netdev_safe(nd); - kfree(nd); - spin_lock(&list_lock); - } - spin_unlock(&list_lock); - - most_deregister_aim(&aim); - pr_info("most_net_exit()\n"); -} - -/** - * most_deliver_netinfo - callback for HDM to be informed about HW's MAC - * @param iface - most interface instance - * @param link_stat - link status - * @param mac_addr - MAC address - */ -void most_deliver_netinfo(struct most_interface *iface, - unsigned char link_stat, unsigned char *mac_addr) -{ - struct net_dev_context *nd; - struct net_device *dev; - - pr_info("Received netinfo from %s\n", iface->description); - - nd = get_net_dev_context(iface); - if (!nd) - return; - - dev = nd->dev; - if (!dev) - return; - - if (mac_addr) - memcpy(dev->dev_addr, mac_addr, ETH_ALEN); - - if (nd->link_stat != link_stat) { - nd->link_stat = link_stat; - if (nd->link_stat) - netif_wake_queue(dev); - else - netif_stop_queue(dev); - } -} -EXPORT_SYMBOL(most_deliver_netinfo); - -module_init(most_net_init); -module_exit(most_net_exit); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Andrey Shvetsov <andrey.shvetsov@k2l.de>"); -MODULE_DESCRIPTION("Networking Application Interface Module for MostCore"); |