Path: blob/master/arch/xtensa/platforms/iss/network.c
15116 views
/*1*2* arch/xtensa/platforms/iss/network.c3*4* Platform specific initialization.5*6* Authors: Chris Zankel <[email protected]>7* Based on work form the UML team.8*9* Copyright 2005 Tensilica Inc.10*11* This program is free software; you can redistribute it and/or modify it12* under the terms of the GNU General Public License as published by the13* Free Software Foundation; either version 2 of the License, or (at your14* option) any later version.15*16*/1718#include <linux/list.h>19#include <linux/irq.h>20#include <linux/spinlock.h>21#include <linux/slab.h>22#include <linux/timer.h>23#include <linux/if_ether.h>24#include <linux/inetdevice.h>25#include <linux/init.h>26#include <linux/if_tun.h>27#include <linux/etherdevice.h>28#include <linux/interrupt.h>29#include <linux/ioctl.h>30#include <linux/bootmem.h>31#include <linux/ethtool.h>32#include <linux/rtnetlink.h>33#include <linux/platform_device.h>3435#include <platform/simcall.h>3637#define DRIVER_NAME "iss-netdev"38#define ETH_MAX_PACKET 150039#define ETH_HEADER_OTHER 1440#define ISS_NET_TIMER_VALUE (2 * HZ)414243static DEFINE_SPINLOCK(opened_lock);44static LIST_HEAD(opened);4546static DEFINE_SPINLOCK(devices_lock);47static LIST_HEAD(devices);4849/* ------------------------------------------------------------------------- */5051/* We currently only support the TUNTAP transport protocol. */5253#define TRANSPORT_TUNTAP_NAME "tuntap"54#define TRANSPORT_TUNTAP_MTU ETH_MAX_PACKET5556struct tuntap_info {57char dev_name[IFNAMSIZ];58int fixed_config;59unsigned char gw[ETH_ALEN];60int fd;61};6263/* ------------------------------------------------------------------------- */646566/* This structure contains out private information for the driver. */6768struct iss_net_private {6970struct list_head device_list;71struct list_head opened_list;7273spinlock_t lock;74struct net_device *dev;75struct platform_device pdev;76struct timer_list tl;77struct net_device_stats stats;7879struct timer_list timer;80unsigned int timer_val;8182int index;83int mtu;8485unsigned char mac[ETH_ALEN];86int have_mac;8788struct {89union {90struct tuntap_info tuntap;91} info;9293int (*open)(struct iss_net_private *lp);94void (*close)(struct iss_net_private *lp);95int (*read)(struct iss_net_private *lp, struct sk_buff **skb);96int (*write)(struct iss_net_private *lp, struct sk_buff **skb);97unsigned short (*protocol)(struct sk_buff *skb);98int (*poll)(struct iss_net_private *lp);99} tp;100101};102103/* ======================= ISS SIMCALL INTERFACE =========================== */104105/* Note: __simc must _not_ be declared inline! */106107static int errno;108109static int __simc (int a, int b, int c, int d, int e, int f) __attribute__((__noinline__));110static int __simc (int a, int b, int c, int d, int e, int f)111{112int ret;113__asm__ __volatile__ ("simcall\n"114"mov %0, a2\n"115"mov %1, a3\n" : "=a" (ret), "=a" (errno)116: : "a2", "a3");117return ret;118}119120static int inline simc_open(char *file, int flags, int mode)121{122return __simc(SYS_open, (int) file, flags, mode, 0, 0);123}124125static int inline simc_close(int fd)126{127return __simc(SYS_close, fd, 0, 0, 0, 0);128}129130static int inline simc_ioctl(int fd, int request, void *arg)131{132return __simc(SYS_ioctl, fd, request, (int) arg, 0, 0);133}134135static int inline simc_read(int fd, void *buf, size_t count)136{137return __simc(SYS_read, fd, (int) buf, count, 0, 0);138}139140static int inline simc_write(int fd, void *buf, size_t count)141{142return __simc(SYS_write, fd, (int) buf, count, 0, 0);143}144145static int inline simc_poll(int fd)146{147struct timeval tv = { .tv_sec = 0, .tv_usec = 0 };148149return __simc(SYS_select_one, fd, XTISS_SELECT_ONE_READ, (int)&tv,0,0);150}151152/* ================================ HELPERS ================================ */153154155static char *split_if_spec(char *str, ...)156{157char **arg, *end;158va_list ap;159160va_start(ap, str);161while ((arg = va_arg(ap, char**)) != NULL) {162if (*str == '\0')163return NULL;164end = strchr(str, ',');165if (end != str)166*arg = str;167if (end == NULL)168return NULL;169*end ++ = '\0';170str = end;171}172va_end(ap);173return str;174}175176177#if 0178/* Adjust SKB. */179180struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra)181{182if ((skb != NULL) && (skb_tailroom(skb) < extra)) {183struct sk_buff *skb2;184185skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC);186dev_kfree_skb(skb);187skb = skb2;188}189if (skb != NULL)190skb_put(skb, extra);191192return skb;193}194#endif195196/* Return the IP address as a string for a given device. */197198static void dev_ip_addr(void *d, char *buf, char *bin_buf)199{200struct net_device *dev = d;201struct in_device *ip = dev->ip_ptr;202struct in_ifaddr *in;203__be32 addr;204205if ((ip == NULL) || ((in = ip->ifa_list) == NULL)) {206printk(KERN_WARNING "Device not assigned an IP address!\n");207return;208}209210addr = in->ifa_address;211sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff,212(addr >> 16) & 0xff, addr >> 24);213214if (bin_buf) {215bin_buf[0] = addr & 0xff;216bin_buf[1] = (addr >> 8) & 0xff;217bin_buf[2] = (addr >> 16) & 0xff;218bin_buf[3] = addr >> 24;219}220}221222/* Set Ethernet address of the specified device. */223224static void inline set_ether_mac(void *d, unsigned char *addr)225{226struct net_device *dev = d;227memcpy(dev->dev_addr, addr, ETH_ALEN);228}229230231/* ======================= TUNTAP TRANSPORT INTERFACE ====================== */232233static int tuntap_open(struct iss_net_private *lp)234{235struct ifreq ifr;236char *dev_name = lp->tp.info.tuntap.dev_name;237int err = -EINVAL;238int fd;239240/* We currently only support a fixed configuration. */241242if (!lp->tp.info.tuntap.fixed_config)243return -EINVAL;244245if ((fd = simc_open("/dev/net/tun", 02, 0)) < 0) { /* O_RDWR */246printk("Failed to open /dev/net/tun, returned %d "247"(errno = %d)\n", fd, errno);248return fd;249}250251memset(&ifr, 0, sizeof ifr);252ifr.ifr_flags = IFF_TAP | IFF_NO_PI;253strlcpy(ifr.ifr_name, dev_name, sizeof ifr.ifr_name);254255if ((err = simc_ioctl(fd, TUNSETIFF, (void*) &ifr)) < 0) {256printk("Failed to set interface, returned %d "257"(errno = %d)\n", err, errno);258simc_close(fd);259return err;260}261262lp->tp.info.tuntap.fd = fd;263return err;264}265266static void tuntap_close(struct iss_net_private *lp)267{268#if 0269if (lp->tp.info.tuntap.fixed_config)270iter_addresses(lp->tp.info.tuntap.dev, close_addr, lp->host.dev_name);271#endif272simc_close(lp->tp.info.tuntap.fd);273lp->tp.info.tuntap.fd = -1;274}275276static int tuntap_read (struct iss_net_private *lp, struct sk_buff **skb)277{278#if 0279*skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);280if (*skb == NULL)281return -ENOMEM;282#endif283284return simc_read(lp->tp.info.tuntap.fd,285(*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER);286}287288static int tuntap_write (struct iss_net_private *lp, struct sk_buff **skb)289{290return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len);291}292293unsigned short tuntap_protocol(struct sk_buff *skb)294{295return eth_type_trans(skb, skb->dev);296}297298static int tuntap_poll(struct iss_net_private *lp)299{300return simc_poll(lp->tp.info.tuntap.fd);301}302303/*304* Currently only a device name is supported.305* ethX=tuntap[,[mac address][,[device name]]]306*/307308static int tuntap_probe(struct iss_net_private *lp, int index, char *init)309{310const int len = strlen(TRANSPORT_TUNTAP_NAME);311char *dev_name = NULL, *mac_str = NULL, *rem = NULL;312313/* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */314315if (strncmp(init, TRANSPORT_TUNTAP_NAME, len))316return 0;317318if (*(init += strlen(TRANSPORT_TUNTAP_NAME)) == ',') {319if ((rem=split_if_spec(init+1, &mac_str, &dev_name)) != NULL) {320printk("Extra garbage on specification : '%s'\n", rem);321return 0;322}323} else if (*init != '\0') {324printk("Invalid argument: %s. Skipping device!\n", init);325return 0;326}327328if (dev_name) {329strncpy(lp->tp.info.tuntap.dev_name, dev_name,330sizeof lp->tp.info.tuntap.dev_name);331lp->tp.info.tuntap.fixed_config = 1;332} else333strcpy(lp->tp.info.tuntap.dev_name, TRANSPORT_TUNTAP_NAME);334335336#if 0337if (setup_etheraddr(mac_str, lp->mac))338lp->have_mac = 1;339#endif340lp->mtu = TRANSPORT_TUNTAP_MTU;341342//lp->info.tuntap.gate_addr = gate_addr;343344lp->tp.info.tuntap.fd = -1;345346lp->tp.open = tuntap_open;347lp->tp.close = tuntap_close;348lp->tp.read = tuntap_read;349lp->tp.write = tuntap_write;350lp->tp.protocol = tuntap_protocol;351lp->tp.poll = tuntap_poll;352353printk("TUN/TAP backend - ");354#if 0355if (lp->host.gate_addr != NULL)356printk("IP = %s", lp->host.gate_addr);357#endif358printk("\n");359360return 1;361}362363/* ================================ ISS NET ================================ */364365static int iss_net_rx(struct net_device *dev)366{367struct iss_net_private *lp = netdev_priv(dev);368int pkt_len;369struct sk_buff *skb;370371/* Check if there is any new data. */372373if (lp->tp.poll(lp) == 0)374return 0;375376/* Try to allocate memory, if it fails, try again next round. */377378if ((skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER)) == NULL) {379lp->stats.rx_dropped++;380return 0;381}382383skb_reserve(skb, 2);384385/* Setup skb */386387skb->dev = dev;388skb_reset_mac_header(skb);389pkt_len = lp->tp.read(lp, &skb);390skb_put(skb, pkt_len);391392if (pkt_len > 0) {393skb_trim(skb, pkt_len);394skb->protocol = lp->tp.protocol(skb);395396lp->stats.rx_bytes += skb->len;397lp->stats.rx_packets++;398// netif_rx(skb);399netif_rx_ni(skb);400return pkt_len;401}402kfree_skb(skb);403return pkt_len;404}405406static int iss_net_poll(void)407{408struct list_head *ele;409int err, ret = 0;410411spin_lock(&opened_lock);412413list_for_each(ele, &opened) {414struct iss_net_private *lp;415416lp = list_entry(ele, struct iss_net_private, opened_list);417418if (!netif_running(lp->dev))419break;420421spin_lock(&lp->lock);422423while ((err = iss_net_rx(lp->dev)) > 0)424ret++;425426spin_unlock(&lp->lock);427428if (err < 0) {429printk(KERN_ERR "Device '%s' read returned %d, "430"shutting it down\n", lp->dev->name, err);431dev_close(lp->dev);432} else {433// FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ);434}435}436437spin_unlock(&opened_lock);438return ret;439}440441442static void iss_net_timer(unsigned long priv)443{444struct iss_net_private* lp = (struct iss_net_private*) priv;445446spin_lock(&lp->lock);447448iss_net_poll();449450mod_timer(&lp->timer, jiffies + lp->timer_val);451452spin_unlock(&lp->lock);453}454455456static int iss_net_open(struct net_device *dev)457{458struct iss_net_private *lp = netdev_priv(dev);459char addr[sizeof "255.255.255.255\0"];460int err;461462spin_lock(&lp->lock);463464if ((err = lp->tp.open(lp)) < 0)465goto out;466467if (!lp->have_mac) {468dev_ip_addr(dev, addr, &lp->mac[2]);469set_ether_mac(dev, lp->mac);470}471472netif_start_queue(dev);473474/* clear buffer - it can happen that the host side of the interface475* is full when we get here. In this case, new data is never queued,476* SIGIOs never arrive, and the net never works.477*/478while ((err = iss_net_rx(dev)) > 0)479;480481spin_lock(&opened_lock);482list_add(&lp->opened_list, &opened);483spin_unlock(&opened_lock);484485init_timer(&lp->timer);486lp->timer_val = ISS_NET_TIMER_VALUE;487lp->timer.data = (unsigned long) lp;488lp->timer.function = iss_net_timer;489mod_timer(&lp->timer, jiffies + lp->timer_val);490491out:492spin_unlock(&lp->lock);493return err;494}495496static int iss_net_close(struct net_device *dev)497{498struct iss_net_private *lp = netdev_priv(dev);499printk("iss_net_close!\n");500netif_stop_queue(dev);501spin_lock(&lp->lock);502503spin_lock(&opened_lock);504list_del(&opened);505spin_unlock(&opened_lock);506507del_timer_sync(&lp->timer);508509lp->tp.close(lp);510511spin_unlock(&lp->lock);512return 0;513}514515static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev)516{517struct iss_net_private *lp = netdev_priv(dev);518unsigned long flags;519int len;520521netif_stop_queue(dev);522spin_lock_irqsave(&lp->lock, flags);523524len = lp->tp.write(lp, &skb);525526if (len == skb->len) {527lp->stats.tx_packets++;528lp->stats.tx_bytes += skb->len;529dev->trans_start = jiffies;530netif_start_queue(dev);531532/* this is normally done in the interrupt when tx finishes */533netif_wake_queue(dev);534535} else if (len == 0) {536netif_start_queue(dev);537lp->stats.tx_dropped++;538539} else {540netif_start_queue(dev);541printk(KERN_ERR "iss_net_start_xmit: failed(%d)\n", len);542}543544spin_unlock_irqrestore(&lp->lock, flags);545546dev_kfree_skb(skb);547return NETDEV_TX_OK;548}549550551static struct net_device_stats *iss_net_get_stats(struct net_device *dev)552{553struct iss_net_private *lp = netdev_priv(dev);554return &lp->stats;555}556557static void iss_net_set_multicast_list(struct net_device *dev)558{559#if 0560if (dev->flags & IFF_PROMISC)561return;562else if (!netdev_mc_empty(dev))563dev->flags |= IFF_ALLMULTI;564else565dev->flags &= ~IFF_ALLMULTI;566#endif567}568569static void iss_net_tx_timeout(struct net_device *dev)570{571#if 0572dev->trans_start = jiffies;573netif_wake_queue(dev);574#endif575}576577static int iss_net_set_mac(struct net_device *dev, void *addr)578{579#if 0580struct iss_net_private *lp = netdev_priv(dev);581struct sockaddr *hwaddr = addr;582583spin_lock(&lp->lock);584memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN);585spin_unlock(&lp->lock);586#endif587588return 0;589}590591static int iss_net_change_mtu(struct net_device *dev, int new_mtu)592{593#if 0594struct iss_net_private *lp = netdev_priv(dev);595int err = 0;596597spin_lock(&lp->lock);598599// FIXME not needed new_mtu = transport_set_mtu(new_mtu, &lp->user);600601if (new_mtu < 0)602err = new_mtu;603else604dev->mtu = new_mtu;605606spin_unlock(&lp->lock);607return err;608#endif609return -EINVAL;610}611612void iss_net_user_timer_expire(unsigned long _conn)613{614}615616617static struct platform_driver iss_net_driver = {618.driver = {619.name = DRIVER_NAME,620},621};622623static int driver_registered;624625static const struct net_device_ops iss_netdev_ops = {626.ndo_open = iss_net_open,627.ndo_stop = iss_net_close,628.ndo_get_stats = iss_net_get_stats,629.ndo_start_xmit = iss_net_start_xmit,630.ndo_validate_addr = eth_validate_addr,631.ndo_change_mtu = iss_net_change_mtu,632.ndo_set_mac_address = iss_net_set_mac,633//.ndo_do_ioctl = iss_net_ioctl,634.ndo_tx_timeout = iss_net_tx_timeout,635.ndo_set_multicast_list = iss_net_set_multicast_list,636};637638static int iss_net_configure(int index, char *init)639{640struct net_device *dev;641struct iss_net_private *lp;642int err;643644if ((dev = alloc_etherdev(sizeof *lp)) == NULL) {645printk(KERN_ERR "eth_configure: failed to allocate device\n");646return 1;647}648649/* Initialize private element. */650651lp = netdev_priv(dev);652*lp = ((struct iss_net_private) {653.device_list = LIST_HEAD_INIT(lp->device_list),654.opened_list = LIST_HEAD_INIT(lp->opened_list),655.lock = __SPIN_LOCK_UNLOCKED(lp.lock),656.dev = dev,657.index = index,658//.fd = -1,659.mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 },660.have_mac = 0,661});662663/*664* Try all transport protocols.665* Note: more protocols can be added by adding '&& !X_init(lp, eth)'.666*/667668if (!tuntap_probe(lp, index, init)) {669printk("Invalid arguments. Skipping device!\n");670goto errout;671}672673printk(KERN_INFO "Netdevice %d ", index);674if (lp->have_mac)675printk("(%pM) ", lp->mac);676printk(": ");677678/* sysfs register */679680if (!driver_registered) {681platform_driver_register(&iss_net_driver);682driver_registered = 1;683}684685spin_lock(&devices_lock);686list_add(&lp->device_list, &devices);687spin_unlock(&devices_lock);688689lp->pdev.id = index;690lp->pdev.name = DRIVER_NAME;691platform_device_register(&lp->pdev);692SET_NETDEV_DEV(dev,&lp->pdev.dev);693694/*695* If this name ends up conflicting with an existing registered696* netdevice, that is OK, register_netdev{,ice}() will notice this697* and fail.698*/699snprintf(dev->name, sizeof dev->name, "eth%d", index);700701dev->netdev_ops = &iss_netdev_ops;702dev->mtu = lp->mtu;703dev->watchdog_timeo = (HZ >> 1);704dev->irq = -1;705706rtnl_lock();707err = register_netdevice(dev);708rtnl_unlock();709710if (err) {711printk("Error registering net device!\n");712/* XXX: should we call ->remove() here? */713free_netdev(dev);714return 1;715}716717init_timer(&lp->tl);718lp->tl.function = iss_net_user_timer_expire;719720#if 0721if (lp->have_mac)722set_ether_mac(dev, lp->mac);723#endif724return 0;725726errout:727// FIXME: unregister; free, etc..728return -EIO;729730}731732/* ------------------------------------------------------------------------- */733734/* Filled in during early boot */735736struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line);737738struct iss_net_init {739struct list_head list;740char *init; /* init string */741int index;742};743744/*745* Parse the command line and look for 'ethX=...' fields, and register all746* those fields. They will be later initialized in iss_net_init.747*/748749#define ERR KERN_ERR "iss_net_setup: "750751static int iss_net_setup(char *str)752{753struct iss_net_private *device = NULL;754struct iss_net_init *new;755struct list_head *ele;756char *end;757int n;758759n = simple_strtoul(str, &end, 0);760if (end == str) {761printk(ERR "Failed to parse '%s'\n", str);762return 1;763}764if (n < 0) {765printk(ERR "Device %d is negative\n", n);766return 1;767}768if (*(str = end) != '=') {769printk(ERR "Expected '=' after device number\n");770return 1;771}772773spin_lock(&devices_lock);774775list_for_each(ele, &devices) {776device = list_entry(ele, struct iss_net_private, device_list);777if (device->index == n)778break;779}780781spin_unlock(&devices_lock);782783if (device && device->index == n) {784printk(ERR "Device %d already configured\n", n);785return 1;786}787788if ((new = alloc_bootmem(sizeof new)) == NULL) {789printk("Alloc_bootmem failed\n");790return 1;791}792793INIT_LIST_HEAD(&new->list);794new->index = n;795new->init = str + 1;796797list_add_tail(&new->list, ð_cmd_line);798return 1;799}800801#undef ERR802803__setup("eth=", iss_net_setup);804805/*806* Initialize all ISS Ethernet devices previously registered in iss_net_setup.807*/808809static int iss_net_init(void)810{811struct list_head *ele, *next;812813/* Walk through all Ethernet devices specified in the command line. */814815list_for_each_safe(ele, next, ð_cmd_line) {816struct iss_net_init *eth;817eth = list_entry(ele, struct iss_net_init, list);818iss_net_configure(eth->index, eth->init);819}820821return 1;822}823824module_init(iss_net_init);825826827828