18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * arch/xtensa/platforms/iss/network.c 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Platform specific initialization. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Authors: Chris Zankel <chris@zankel.net> 98c2ecf20Sopenharmony_ci * Based on work form the UML team. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright 2005 Tensilica Inc. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/list.h> 178c2ecf20Sopenharmony_ci#include <linux/irq.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/timer.h> 218c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 228c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/if_tun.h> 258c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 268c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 278c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 288c2ecf20Sopenharmony_ci#include <linux/memblock.h> 298c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 308c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 318c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <platform/simcall.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define DRIVER_NAME "iss-netdev" 368c2ecf20Sopenharmony_ci#define ETH_MAX_PACKET 1500 378c2ecf20Sopenharmony_ci#define ETH_HEADER_OTHER 14 388c2ecf20Sopenharmony_ci#define ISS_NET_TIMER_VALUE (HZ / 10) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(opened_lock); 428c2ecf20Sopenharmony_cistatic LIST_HEAD(opened); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(devices_lock); 458c2ecf20Sopenharmony_cistatic LIST_HEAD(devices); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* We currently only support the TUNTAP transport protocol. */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define TRANSPORT_TUNTAP_NAME "tuntap" 528c2ecf20Sopenharmony_ci#define TRANSPORT_TUNTAP_MTU ETH_MAX_PACKET 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct tuntap_info { 558c2ecf20Sopenharmony_ci char dev_name[IFNAMSIZ]; 568c2ecf20Sopenharmony_ci int fd; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* This structure contains out private information for the driver. */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct iss_net_private { 658c2ecf20Sopenharmony_ci struct list_head device_list; 668c2ecf20Sopenharmony_ci struct list_head opened_list; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci spinlock_t lock; 698c2ecf20Sopenharmony_ci struct net_device *dev; 708c2ecf20Sopenharmony_ci struct platform_device pdev; 718c2ecf20Sopenharmony_ci struct timer_list tl; 728c2ecf20Sopenharmony_ci struct net_device_stats stats; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci struct timer_list timer; 758c2ecf20Sopenharmony_ci unsigned int timer_val; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci int index; 788c2ecf20Sopenharmony_ci int mtu; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci struct { 818c2ecf20Sopenharmony_ci union { 828c2ecf20Sopenharmony_ci struct tuntap_info tuntap; 838c2ecf20Sopenharmony_ci } info; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci int (*open)(struct iss_net_private *lp); 868c2ecf20Sopenharmony_ci void (*close)(struct iss_net_private *lp); 878c2ecf20Sopenharmony_ci int (*read)(struct iss_net_private *lp, struct sk_buff **skb); 888c2ecf20Sopenharmony_ci int (*write)(struct iss_net_private *lp, struct sk_buff **skb); 898c2ecf20Sopenharmony_ci unsigned short (*protocol)(struct sk_buff *skb); 908c2ecf20Sopenharmony_ci int (*poll)(struct iss_net_private *lp); 918c2ecf20Sopenharmony_ci } tp; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* ================================ HELPERS ================================ */ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic char *split_if_spec(char *str, ...) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci char **arg, *end; 1018c2ecf20Sopenharmony_ci va_list ap; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci va_start(ap, str); 1048c2ecf20Sopenharmony_ci while ((arg = va_arg(ap, char**)) != NULL) { 1058c2ecf20Sopenharmony_ci if (*str == '\0') { 1068c2ecf20Sopenharmony_ci va_end(ap); 1078c2ecf20Sopenharmony_ci return NULL; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci end = strchr(str, ','); 1108c2ecf20Sopenharmony_ci if (end != str) 1118c2ecf20Sopenharmony_ci *arg = str; 1128c2ecf20Sopenharmony_ci if (end == NULL) { 1138c2ecf20Sopenharmony_ci va_end(ap); 1148c2ecf20Sopenharmony_ci return NULL; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci *end++ = '\0'; 1178c2ecf20Sopenharmony_ci str = end; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci va_end(ap); 1208c2ecf20Sopenharmony_ci return str; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* Set Ethernet address of the specified device. */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void setup_etheraddr(struct net_device *dev, char *str) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci unsigned char *addr = dev->dev_addr; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (str == NULL) 1308c2ecf20Sopenharmony_ci goto random; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (!mac_pton(str, addr)) { 1338c2ecf20Sopenharmony_ci pr_err("%s: failed to parse '%s' as an ethernet address\n", 1348c2ecf20Sopenharmony_ci dev->name, str); 1358c2ecf20Sopenharmony_ci goto random; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci if (is_multicast_ether_addr(addr)) { 1388c2ecf20Sopenharmony_ci pr_err("%s: attempt to assign a multicast ethernet address\n", 1398c2ecf20Sopenharmony_ci dev->name); 1408c2ecf20Sopenharmony_ci goto random; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr)) { 1438c2ecf20Sopenharmony_ci pr_err("%s: attempt to assign an invalid ethernet address\n", 1448c2ecf20Sopenharmony_ci dev->name); 1458c2ecf20Sopenharmony_ci goto random; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci if (!is_local_ether_addr(addr)) 1488c2ecf20Sopenharmony_ci pr_warn("%s: assigning a globally valid ethernet address\n", 1498c2ecf20Sopenharmony_ci dev->name); 1508c2ecf20Sopenharmony_ci return; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cirandom: 1538c2ecf20Sopenharmony_ci pr_info("%s: choosing a random ethernet address\n", 1548c2ecf20Sopenharmony_ci dev->name); 1558c2ecf20Sopenharmony_ci eth_hw_addr_random(dev); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* ======================= TUNTAP TRANSPORT INTERFACE ====================== */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int tuntap_open(struct iss_net_private *lp) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct ifreq ifr; 1638c2ecf20Sopenharmony_ci char *dev_name = lp->tp.info.tuntap.dev_name; 1648c2ecf20Sopenharmony_ci int err = -EINVAL; 1658c2ecf20Sopenharmony_ci int fd; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci fd = simc_open("/dev/net/tun", 02, 0); /* O_RDWR */ 1688c2ecf20Sopenharmony_ci if (fd < 0) { 1698c2ecf20Sopenharmony_ci pr_err("%s: failed to open /dev/net/tun, returned %d (errno = %d)\n", 1708c2ecf20Sopenharmony_ci lp->dev->name, fd, errno); 1718c2ecf20Sopenharmony_ci return fd; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci memset(&ifr, 0, sizeof(ifr)); 1758c2ecf20Sopenharmony_ci ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 1768c2ecf20Sopenharmony_ci strlcpy(ifr.ifr_name, dev_name, sizeof(ifr.ifr_name)); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci err = simc_ioctl(fd, TUNSETIFF, &ifr); 1798c2ecf20Sopenharmony_ci if (err < 0) { 1808c2ecf20Sopenharmony_ci pr_err("%s: failed to set interface %s, returned %d (errno = %d)\n", 1818c2ecf20Sopenharmony_ci lp->dev->name, dev_name, err, errno); 1828c2ecf20Sopenharmony_ci simc_close(fd); 1838c2ecf20Sopenharmony_ci return err; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci lp->tp.info.tuntap.fd = fd; 1878c2ecf20Sopenharmony_ci return err; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void tuntap_close(struct iss_net_private *lp) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci simc_close(lp->tp.info.tuntap.fd); 1938c2ecf20Sopenharmony_ci lp->tp.info.tuntap.fd = -1; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int tuntap_read(struct iss_net_private *lp, struct sk_buff **skb) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci return simc_read(lp->tp.info.tuntap.fd, 1998c2ecf20Sopenharmony_ci (*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int tuntap_write(struct iss_net_private *lp, struct sk_buff **skb) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic unsigned short tuntap_protocol(struct sk_buff *skb) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci return eth_type_trans(skb, skb->dev); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int tuntap_poll(struct iss_net_private *lp) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci return simc_poll(lp->tp.info.tuntap.fd); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* 2188c2ecf20Sopenharmony_ci * ethX=tuntap,[mac address],device name 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int tuntap_probe(struct iss_net_private *lp, int index, char *init) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct net_device *dev = lp->dev; 2248c2ecf20Sopenharmony_ci char *dev_name = NULL, *mac_str = NULL, *rem = NULL; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */ 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (strncmp(init, TRANSPORT_TUNTAP_NAME, 2298c2ecf20Sopenharmony_ci sizeof(TRANSPORT_TUNTAP_NAME) - 1)) 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci init += sizeof(TRANSPORT_TUNTAP_NAME) - 1; 2338c2ecf20Sopenharmony_ci if (*init == ',') { 2348c2ecf20Sopenharmony_ci rem = split_if_spec(init + 1, &mac_str, &dev_name, NULL); 2358c2ecf20Sopenharmony_ci if (rem != NULL) { 2368c2ecf20Sopenharmony_ci pr_err("%s: extra garbage on specification : '%s'\n", 2378c2ecf20Sopenharmony_ci dev->name, rem); 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci } else if (*init != '\0') { 2418c2ecf20Sopenharmony_ci pr_err("%s: invalid argument: %s. Skipping device!\n", 2428c2ecf20Sopenharmony_ci dev->name, init); 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (!dev_name) { 2478c2ecf20Sopenharmony_ci pr_err("%s: missing tuntap device name\n", dev->name); 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci strlcpy(lp->tp.info.tuntap.dev_name, dev_name, 2528c2ecf20Sopenharmony_ci sizeof(lp->tp.info.tuntap.dev_name)); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci setup_etheraddr(dev, mac_str); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci lp->mtu = TRANSPORT_TUNTAP_MTU; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci lp->tp.info.tuntap.fd = -1; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci lp->tp.open = tuntap_open; 2618c2ecf20Sopenharmony_ci lp->tp.close = tuntap_close; 2628c2ecf20Sopenharmony_ci lp->tp.read = tuntap_read; 2638c2ecf20Sopenharmony_ci lp->tp.write = tuntap_write; 2648c2ecf20Sopenharmony_ci lp->tp.protocol = tuntap_protocol; 2658c2ecf20Sopenharmony_ci lp->tp.poll = tuntap_poll; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 1; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* ================================ ISS NET ================================ */ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int iss_net_rx(struct net_device *dev) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct iss_net_private *lp = netdev_priv(dev); 2758c2ecf20Sopenharmony_ci int pkt_len; 2768c2ecf20Sopenharmony_ci struct sk_buff *skb; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Check if there is any new data. */ 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (lp->tp.poll(lp) == 0) 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Try to allocate memory, if it fails, try again next round. */ 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER); 2868c2ecf20Sopenharmony_ci if (skb == NULL) { 2878c2ecf20Sopenharmony_ci lp->stats.rx_dropped++; 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci skb_reserve(skb, 2); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* Setup skb */ 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci skb->dev = dev; 2968c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 2978c2ecf20Sopenharmony_ci pkt_len = lp->tp.read(lp, &skb); 2988c2ecf20Sopenharmony_ci skb_put(skb, pkt_len); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (pkt_len > 0) { 3018c2ecf20Sopenharmony_ci skb_trim(skb, pkt_len); 3028c2ecf20Sopenharmony_ci skb->protocol = lp->tp.protocol(skb); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci lp->stats.rx_bytes += skb->len; 3058c2ecf20Sopenharmony_ci lp->stats.rx_packets++; 3068c2ecf20Sopenharmony_ci netif_rx_ni(skb); 3078c2ecf20Sopenharmony_ci return pkt_len; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci kfree_skb(skb); 3108c2ecf20Sopenharmony_ci return pkt_len; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int iss_net_poll(void) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct list_head *ele; 3168c2ecf20Sopenharmony_ci int err, ret = 0; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci spin_lock(&opened_lock); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci list_for_each(ele, &opened) { 3218c2ecf20Sopenharmony_ci struct iss_net_private *lp; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci lp = list_entry(ele, struct iss_net_private, opened_list); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (!netif_running(lp->dev)) 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci spin_lock(&lp->lock); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci while ((err = iss_net_rx(lp->dev)) > 0) 3318c2ecf20Sopenharmony_ci ret++; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci spin_unlock(&lp->lock); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (err < 0) { 3368c2ecf20Sopenharmony_ci pr_err("Device '%s' read returned %d, shutting it down\n", 3378c2ecf20Sopenharmony_ci lp->dev->name, err); 3388c2ecf20Sopenharmony_ci dev_close(lp->dev); 3398c2ecf20Sopenharmony_ci } else { 3408c2ecf20Sopenharmony_ci /* FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ); */ 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci spin_unlock(&opened_lock); 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void iss_net_timer(struct timer_list *t) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct iss_net_private *lp = from_timer(lp, t, timer); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci iss_net_poll(); 3548c2ecf20Sopenharmony_ci spin_lock(&lp->lock); 3558c2ecf20Sopenharmony_ci mod_timer(&lp->timer, jiffies + lp->timer_val); 3568c2ecf20Sopenharmony_ci spin_unlock(&lp->lock); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int iss_net_open(struct net_device *dev) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct iss_net_private *lp = netdev_priv(dev); 3638c2ecf20Sopenharmony_ci int err; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci spin_lock_bh(&lp->lock); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci err = lp->tp.open(lp); 3688c2ecf20Sopenharmony_ci if (err < 0) 3698c2ecf20Sopenharmony_ci goto out; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci netif_start_queue(dev); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* clear buffer - it can happen that the host side of the interface 3748c2ecf20Sopenharmony_ci * is full when we get here. In this case, new data is never queued, 3758c2ecf20Sopenharmony_ci * SIGIOs never arrive, and the net never works. 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci while ((err = iss_net_rx(dev)) > 0) 3788c2ecf20Sopenharmony_ci ; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci spin_unlock_bh(&lp->lock); 3818c2ecf20Sopenharmony_ci spin_lock_bh(&opened_lock); 3828c2ecf20Sopenharmony_ci list_add(&lp->opened_list, &opened); 3838c2ecf20Sopenharmony_ci spin_unlock_bh(&opened_lock); 3848c2ecf20Sopenharmony_ci spin_lock_bh(&lp->lock); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci timer_setup(&lp->timer, iss_net_timer, 0); 3878c2ecf20Sopenharmony_ci lp->timer_val = ISS_NET_TIMER_VALUE; 3888c2ecf20Sopenharmony_ci mod_timer(&lp->timer, jiffies + lp->timer_val); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ciout: 3918c2ecf20Sopenharmony_ci spin_unlock_bh(&lp->lock); 3928c2ecf20Sopenharmony_ci return err; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int iss_net_close(struct net_device *dev) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct iss_net_private *lp = netdev_priv(dev); 3988c2ecf20Sopenharmony_ci netif_stop_queue(dev); 3998c2ecf20Sopenharmony_ci spin_lock_bh(&lp->lock); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci spin_lock(&opened_lock); 4028c2ecf20Sopenharmony_ci list_del(&opened); 4038c2ecf20Sopenharmony_ci spin_unlock(&opened_lock); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci del_timer_sync(&lp->timer); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci lp->tp.close(lp); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci spin_unlock_bh(&lp->lock); 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct iss_net_private *lp = netdev_priv(dev); 4168c2ecf20Sopenharmony_ci int len; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci netif_stop_queue(dev); 4198c2ecf20Sopenharmony_ci spin_lock_bh(&lp->lock); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci len = lp->tp.write(lp, &skb); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (len == skb->len) { 4248c2ecf20Sopenharmony_ci lp->stats.tx_packets++; 4258c2ecf20Sopenharmony_ci lp->stats.tx_bytes += skb->len; 4268c2ecf20Sopenharmony_ci netif_trans_update(dev); 4278c2ecf20Sopenharmony_ci netif_start_queue(dev); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* this is normally done in the interrupt when tx finishes */ 4308c2ecf20Sopenharmony_ci netif_wake_queue(dev); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci } else if (len == 0) { 4338c2ecf20Sopenharmony_ci netif_start_queue(dev); 4348c2ecf20Sopenharmony_ci lp->stats.tx_dropped++; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci } else { 4378c2ecf20Sopenharmony_ci netif_start_queue(dev); 4388c2ecf20Sopenharmony_ci pr_err("%s: %s failed(%d)\n", dev->name, __func__, len); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci spin_unlock_bh(&lp->lock); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 4448c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic struct net_device_stats *iss_net_get_stats(struct net_device *dev) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct iss_net_private *lp = netdev_priv(dev); 4518c2ecf20Sopenharmony_ci return &lp->stats; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic void iss_net_set_multicast_list(struct net_device *dev) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic void iss_net_tx_timeout(struct net_device *dev, unsigned int txqueue) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int iss_net_set_mac(struct net_device *dev, void *addr) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct iss_net_private *lp = netdev_priv(dev); 4658c2ecf20Sopenharmony_ci struct sockaddr *hwaddr = addr; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(hwaddr->sa_data)) 4688c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 4698c2ecf20Sopenharmony_ci spin_lock_bh(&lp->lock); 4708c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); 4718c2ecf20Sopenharmony_ci spin_unlock_bh(&lp->lock); 4728c2ecf20Sopenharmony_ci return 0; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int iss_net_change_mtu(struct net_device *dev, int new_mtu) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci return -EINVAL; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic void iss_net_user_timer_expire(struct timer_list *unused) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic struct platform_driver iss_net_driver = { 4868c2ecf20Sopenharmony_ci .driver = { 4878c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 4888c2ecf20Sopenharmony_ci }, 4898c2ecf20Sopenharmony_ci}; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int driver_registered; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic const struct net_device_ops iss_netdev_ops = { 4948c2ecf20Sopenharmony_ci .ndo_open = iss_net_open, 4958c2ecf20Sopenharmony_ci .ndo_stop = iss_net_close, 4968c2ecf20Sopenharmony_ci .ndo_get_stats = iss_net_get_stats, 4978c2ecf20Sopenharmony_ci .ndo_start_xmit = iss_net_start_xmit, 4988c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 4998c2ecf20Sopenharmony_ci .ndo_change_mtu = iss_net_change_mtu, 5008c2ecf20Sopenharmony_ci .ndo_set_mac_address = iss_net_set_mac, 5018c2ecf20Sopenharmony_ci .ndo_tx_timeout = iss_net_tx_timeout, 5028c2ecf20Sopenharmony_ci .ndo_set_rx_mode = iss_net_set_multicast_list, 5038c2ecf20Sopenharmony_ci}; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic void iss_net_pdev_release(struct device *dev) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 5088c2ecf20Sopenharmony_ci struct iss_net_private *lp = 5098c2ecf20Sopenharmony_ci container_of(pdev, struct iss_net_private, pdev); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci free_netdev(lp->dev); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic void iss_net_configure(int index, char *init) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci struct net_device *dev; 5178c2ecf20Sopenharmony_ci struct iss_net_private *lp; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(*lp)); 5208c2ecf20Sopenharmony_ci if (dev == NULL) { 5218c2ecf20Sopenharmony_ci pr_err("eth_configure: failed to allocate device\n"); 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Initialize private element. */ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci lp = netdev_priv(dev); 5288c2ecf20Sopenharmony_ci *lp = (struct iss_net_private) { 5298c2ecf20Sopenharmony_ci .device_list = LIST_HEAD_INIT(lp->device_list), 5308c2ecf20Sopenharmony_ci .opened_list = LIST_HEAD_INIT(lp->opened_list), 5318c2ecf20Sopenharmony_ci .dev = dev, 5328c2ecf20Sopenharmony_ci .index = index, 5338c2ecf20Sopenharmony_ci }; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci spin_lock_init(&lp->lock); 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * If this name ends up conflicting with an existing registered 5388c2ecf20Sopenharmony_ci * netdevice, that is OK, register_netdev{,ice}() will notice this 5398c2ecf20Sopenharmony_ci * and fail. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci snprintf(dev->name, sizeof(dev->name), "eth%d", index); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* 5448c2ecf20Sopenharmony_ci * Try all transport protocols. 5458c2ecf20Sopenharmony_ci * Note: more protocols can be added by adding '&& !X_init(lp, eth)'. 5468c2ecf20Sopenharmony_ci */ 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (!tuntap_probe(lp, index, init)) { 5498c2ecf20Sopenharmony_ci pr_err("%s: invalid arguments. Skipping device!\n", 5508c2ecf20Sopenharmony_ci dev->name); 5518c2ecf20Sopenharmony_ci goto err_free_netdev; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci pr_info("Netdevice %d (%pM)\n", index, dev->dev_addr); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* sysfs register */ 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!driver_registered) { 5598c2ecf20Sopenharmony_ci if (platform_driver_register(&iss_net_driver)) 5608c2ecf20Sopenharmony_ci goto err_free_netdev; 5618c2ecf20Sopenharmony_ci driver_registered = 1; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci spin_lock(&devices_lock); 5658c2ecf20Sopenharmony_ci list_add(&lp->device_list, &devices); 5668c2ecf20Sopenharmony_ci spin_unlock(&devices_lock); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci lp->pdev.id = index; 5698c2ecf20Sopenharmony_ci lp->pdev.name = DRIVER_NAME; 5708c2ecf20Sopenharmony_ci lp->pdev.dev.release = iss_net_pdev_release; 5718c2ecf20Sopenharmony_ci if (platform_device_register(&lp->pdev)) 5728c2ecf20Sopenharmony_ci goto err_free_netdev; 5738c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &lp->pdev.dev); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci dev->netdev_ops = &iss_netdev_ops; 5768c2ecf20Sopenharmony_ci dev->mtu = lp->mtu; 5778c2ecf20Sopenharmony_ci dev->watchdog_timeo = (HZ >> 1); 5788c2ecf20Sopenharmony_ci dev->irq = -1; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci rtnl_lock(); 5818c2ecf20Sopenharmony_ci if (register_netdevice(dev)) { 5828c2ecf20Sopenharmony_ci rtnl_unlock(); 5838c2ecf20Sopenharmony_ci pr_err("%s: error registering net device!\n", dev->name); 5848c2ecf20Sopenharmony_ci platform_device_unregister(&lp->pdev); 5858c2ecf20Sopenharmony_ci return; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci rtnl_unlock(); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci timer_setup(&lp->tl, iss_net_user_timer_expire, 0); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci return; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cierr_free_netdev: 5948c2ecf20Sopenharmony_ci free_netdev(dev); 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci/* Filled in during early boot */ 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistruct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistruct iss_net_init { 6048c2ecf20Sopenharmony_ci struct list_head list; 6058c2ecf20Sopenharmony_ci char *init; /* init string */ 6068c2ecf20Sopenharmony_ci int index; 6078c2ecf20Sopenharmony_ci}; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci/* 6108c2ecf20Sopenharmony_ci * Parse the command line and look for 'ethX=...' fields, and register all 6118c2ecf20Sopenharmony_ci * those fields. They will be later initialized in iss_net_init. 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int __init iss_net_setup(char *str) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct iss_net_private *device = NULL; 6178c2ecf20Sopenharmony_ci struct iss_net_init *new; 6188c2ecf20Sopenharmony_ci struct list_head *ele; 6198c2ecf20Sopenharmony_ci char *end; 6208c2ecf20Sopenharmony_ci int rc; 6218c2ecf20Sopenharmony_ci unsigned n; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci end = strchr(str, '='); 6248c2ecf20Sopenharmony_ci if (!end) { 6258c2ecf20Sopenharmony_ci pr_err("Expected '=' after device number\n"); 6268c2ecf20Sopenharmony_ci return 1; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci *end = 0; 6298c2ecf20Sopenharmony_ci rc = kstrtouint(str, 0, &n); 6308c2ecf20Sopenharmony_ci *end = '='; 6318c2ecf20Sopenharmony_ci if (rc < 0) { 6328c2ecf20Sopenharmony_ci pr_err("Failed to parse '%s'\n", str); 6338c2ecf20Sopenharmony_ci return 1; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci str = end; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci spin_lock(&devices_lock); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci list_for_each(ele, &devices) { 6408c2ecf20Sopenharmony_ci device = list_entry(ele, struct iss_net_private, device_list); 6418c2ecf20Sopenharmony_ci if (device->index == n) 6428c2ecf20Sopenharmony_ci break; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci spin_unlock(&devices_lock); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (device && device->index == n) { 6488c2ecf20Sopenharmony_ci pr_err("Device %u already configured\n", n); 6498c2ecf20Sopenharmony_ci return 1; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci new = memblock_alloc(sizeof(*new), SMP_CACHE_BYTES); 6538c2ecf20Sopenharmony_ci if (new == NULL) { 6548c2ecf20Sopenharmony_ci pr_err("Alloc_bootmem failed\n"); 6558c2ecf20Sopenharmony_ci return 1; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&new->list); 6598c2ecf20Sopenharmony_ci new->index = n; 6608c2ecf20Sopenharmony_ci new->init = str + 1; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci list_add_tail(&new->list, ð_cmd_line); 6638c2ecf20Sopenharmony_ci return 1; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci__setup("eth", iss_net_setup); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci/* 6698c2ecf20Sopenharmony_ci * Initialize all ISS Ethernet devices previously registered in iss_net_setup. 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int iss_net_init(void) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct list_head *ele, *next; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* Walk through all Ethernet devices specified in the command line. */ 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci list_for_each_safe(ele, next, ð_cmd_line) { 6798c2ecf20Sopenharmony_ci struct iss_net_init *eth; 6808c2ecf20Sopenharmony_ci eth = list_entry(ele, struct iss_net_init, list); 6818c2ecf20Sopenharmony_ci iss_net_configure(eth->index, eth->init); 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci return 1; 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_cidevice_initcall(iss_net_init); 687