linux/arch/xtensa/platforms/iss/network.c
<<
>>
Prefs
   1/*
   2 *
   3 * arch/xtensa/platforms/iss/network.c
   4 *
   5 * Platform specific initialization.
   6 *
   7 * Authors: Chris Zankel <chris@zankel.net>
   8 * Based on work form the UML team.
   9 *
  10 * Copyright 2005 Tensilica Inc.
  11 *
  12 * This program is free software; you can redistribute  it and/or modify it
  13 * under  the terms of  the GNU General  Public License as published by the
  14 * Free Software Foundation;  either version 2 of the  License, or (at your
  15 * option) any later version.
  16 *
  17 */
  18
  19#include <linux/list.h>
  20#include <linux/irq.h>
  21#include <linux/spinlock.h>
  22#include <linux/slab.h>
  23#include <linux/timer.h>
  24#include <linux/if_ether.h>
  25#include <linux/inetdevice.h>
  26#include <linux/init.h>
  27#include <linux/if_tun.h>
  28#include <linux/etherdevice.h>
  29#include <linux/interrupt.h>
  30#include <linux/ioctl.h>
  31#include <linux/bootmem.h>
  32#include <linux/ethtool.h>
  33#include <linux/rtnetlink.h>
  34#include <linux/platform_device.h>
  35
  36#include <platform/simcall.h>
  37
  38#define DRIVER_NAME "iss-netdev"
  39#define ETH_MAX_PACKET 1500
  40#define ETH_HEADER_OTHER 14
  41#define ISS_NET_TIMER_VALUE (2 * HZ)
  42
  43
  44static DEFINE_SPINLOCK(opened_lock);
  45static LIST_HEAD(opened);
  46
  47static DEFINE_SPINLOCK(devices_lock);
  48static LIST_HEAD(devices);
  49
  50/* ------------------------------------------------------------------------- */
  51
  52/* We currently only support the TUNTAP transport protocol. */
  53
  54#define TRANSPORT_TUNTAP_NAME "tuntap"
  55#define TRANSPORT_TUNTAP_MTU ETH_MAX_PACKET
  56
  57struct tuntap_info {
  58        char dev_name[IFNAMSIZ];
  59        int fixed_config;
  60        unsigned char gw[ETH_ALEN];
  61        int fd;
  62};
  63
  64/* ------------------------------------------------------------------------- */
  65
  66
  67/* This structure contains out private information for the driver. */
  68
  69struct iss_net_private {
  70
  71        struct list_head device_list;
  72        struct list_head opened_list;
  73
  74        spinlock_t lock;
  75        struct net_device *dev;
  76        struct platform_device pdev;
  77        struct timer_list tl;
  78        struct net_device_stats stats;
  79
  80        struct timer_list timer;
  81        unsigned int timer_val;
  82
  83        int index;
  84        int mtu;
  85
  86        unsigned char mac[ETH_ALEN];
  87        int have_mac;
  88
  89        struct {
  90                union {
  91                        struct tuntap_info tuntap;
  92                } info;
  93
  94                int (*open)(struct iss_net_private *lp);
  95                void (*close)(struct iss_net_private *lp);
  96                int (*read)(struct iss_net_private *lp, struct sk_buff **skb);
  97                int (*write)(struct iss_net_private *lp, struct sk_buff **skb);
  98                unsigned short (*protocol)(struct sk_buff *skb);
  99                int (*poll)(struct iss_net_private *lp);
 100        } tp;
 101
 102};
 103
 104/* ================================ HELPERS ================================ */
 105
 106
 107static char *split_if_spec(char *str, ...)
 108{
 109        char **arg, *end;
 110        va_list ap;
 111
 112        va_start(ap, str);
 113        while ((arg = va_arg(ap, char**)) != NULL) {
 114                if (*str == '\0')
 115                        return NULL;
 116                end = strchr(str, ',');
 117                if (end != str)
 118                        *arg = str;
 119                if (end == NULL)
 120                        return NULL;
 121                *end ++ = '\0';
 122                str = end;
 123        }
 124        va_end(ap);
 125        return str;
 126}
 127
 128
 129#if 0
 130/* Adjust SKB. */
 131
 132struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra)
 133{
 134        if ((skb != NULL) && (skb_tailroom(skb) < extra)) {
 135                struct sk_buff *skb2;
 136
 137                skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC);
 138                dev_kfree_skb(skb);
 139                skb = skb2;
 140        }
 141        if (skb != NULL)
 142                skb_put(skb, extra);
 143
 144        return skb;
 145}
 146#endif
 147
 148/* Return the IP address as a string for a given device. */
 149
 150static void dev_ip_addr(void *d, char *buf, char *bin_buf)
 151{
 152        struct net_device *dev = d;
 153        struct in_device *ip = dev->ip_ptr;
 154        struct in_ifaddr *in;
 155        __be32 addr;
 156
 157        if ((ip == NULL) || ((in = ip->ifa_list) == NULL)) {
 158                printk(KERN_WARNING "Device not assigned an IP address!\n");
 159                return;
 160        }
 161
 162        addr = in->ifa_address;
 163        sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff,
 164                (addr >> 16) & 0xff, addr >> 24);
 165
 166        if (bin_buf) {
 167                bin_buf[0] = addr & 0xff;
 168                bin_buf[1] = (addr >> 8) & 0xff;
 169                bin_buf[2] = (addr >> 16) & 0xff;
 170                bin_buf[3] = addr >> 24;
 171        }
 172}
 173
 174/* Set Ethernet address of the specified device. */
 175
 176static void inline set_ether_mac(void *d, unsigned char *addr)
 177{
 178        struct net_device *dev = d;
 179        memcpy(dev->dev_addr, addr, ETH_ALEN);
 180}
 181
 182
 183/* ======================= TUNTAP TRANSPORT INTERFACE ====================== */
 184
 185static int tuntap_open(struct iss_net_private *lp)
 186{
 187        struct ifreq ifr;
 188        char *dev_name = lp->tp.info.tuntap.dev_name;
 189        int err = -EINVAL;
 190        int fd;
 191
 192        /* We currently only support a fixed configuration. */
 193
 194        if (!lp->tp.info.tuntap.fixed_config)
 195                return -EINVAL;
 196
 197        if ((fd = simc_open("/dev/net/tun", 02, 0)) < 0) {      /* O_RDWR */
 198                printk("Failed to open /dev/net/tun, returned %d "
 199                       "(errno = %d)\n", fd, errno);
 200                return fd;
 201        }
 202
 203        memset(&ifr, 0, sizeof ifr);
 204        ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
 205        strlcpy(ifr.ifr_name, dev_name, sizeof ifr.ifr_name);
 206
 207        if ((err = simc_ioctl(fd, TUNSETIFF, (void*) &ifr)) < 0) {
 208                printk("Failed to set interface, returned %d "
 209                       "(errno = %d)\n", err, errno);
 210                simc_close(fd);
 211                return err;
 212        }
 213
 214        lp->tp.info.tuntap.fd = fd;
 215        return err;
 216}
 217
 218static void tuntap_close(struct iss_net_private *lp)
 219{
 220#if 0
 221        if (lp->tp.info.tuntap.fixed_config)
 222                iter_addresses(lp->tp.info.tuntap.dev, close_addr, lp->host.dev_name);
 223#endif
 224        simc_close(lp->tp.info.tuntap.fd);
 225        lp->tp.info.tuntap.fd = -1;
 226}
 227
 228static int tuntap_read (struct iss_net_private *lp, struct sk_buff **skb)
 229{
 230#if 0
 231        *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER);
 232        if (*skb == NULL)
 233                return -ENOMEM;
 234#endif
 235
 236        return simc_read(lp->tp.info.tuntap.fd,
 237                        (*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER);
 238}
 239
 240static int tuntap_write (struct iss_net_private *lp, struct sk_buff **skb)
 241{
 242        return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len);
 243}
 244
 245unsigned short tuntap_protocol(struct sk_buff *skb)
 246{
 247        return eth_type_trans(skb, skb->dev);
 248}
 249
 250static int tuntap_poll(struct iss_net_private *lp)
 251{
 252        return simc_poll(lp->tp.info.tuntap.fd);
 253}
 254
 255/*
 256 * Currently only a device name is supported.
 257 * ethX=tuntap[,[mac address][,[device name]]]
 258 */
 259
 260static int tuntap_probe(struct iss_net_private *lp, int index, char *init)
 261{
 262        const int len = strlen(TRANSPORT_TUNTAP_NAME);
 263        char *dev_name = NULL, *mac_str = NULL, *rem = NULL;
 264
 265        /* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */
 266
 267        if (strncmp(init, TRANSPORT_TUNTAP_NAME, len))
 268                return 0;
 269
 270        if (*(init += strlen(TRANSPORT_TUNTAP_NAME)) == ',') {
 271                if ((rem=split_if_spec(init+1, &mac_str, &dev_name)) != NULL) {
 272                        printk("Extra garbage on specification : '%s'\n", rem);
 273                        return 0;
 274                }
 275        } else if (*init != '\0') {
 276                printk("Invalid argument: %s. Skipping device!\n", init);
 277                return 0;
 278        }
 279
 280        if (dev_name) {
 281                strncpy(lp->tp.info.tuntap.dev_name, dev_name,
 282                         sizeof lp->tp.info.tuntap.dev_name);
 283                lp->tp.info.tuntap.fixed_config = 1;
 284        } else
 285                strcpy(lp->tp.info.tuntap.dev_name, TRANSPORT_TUNTAP_NAME);
 286
 287
 288#if 0
 289        if (setup_etheraddr(mac_str, lp->mac))
 290                lp->have_mac = 1;
 291#endif
 292        lp->mtu = TRANSPORT_TUNTAP_MTU;
 293
 294        //lp->info.tuntap.gate_addr = gate_addr;
 295
 296        lp->tp.info.tuntap.fd = -1;
 297
 298        lp->tp.open = tuntap_open;
 299        lp->tp.close = tuntap_close;
 300        lp->tp.read = tuntap_read;
 301        lp->tp.write = tuntap_write;
 302        lp->tp.protocol = tuntap_protocol;
 303        lp->tp.poll = tuntap_poll;
 304
 305        printk("TUN/TAP backend - ");
 306#if 0
 307        if (lp->host.gate_addr != NULL)
 308                printk("IP = %s", lp->host.gate_addr);
 309#endif
 310        printk("\n");
 311
 312        return 1;
 313}
 314
 315/* ================================ ISS NET ================================ */
 316
 317static int iss_net_rx(struct net_device *dev)
 318{
 319        struct iss_net_private *lp = netdev_priv(dev);
 320        int pkt_len;
 321        struct sk_buff *skb;
 322
 323        /* Check if there is any new data. */
 324
 325        if (lp->tp.poll(lp) == 0)
 326                return 0;
 327
 328        /* Try to allocate memory, if it fails, try again next round. */
 329
 330        if ((skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER)) == NULL) {
 331                lp->stats.rx_dropped++;
 332                return 0;
 333        }
 334
 335        skb_reserve(skb, 2);
 336
 337        /* Setup skb */
 338
 339        skb->dev = dev;
 340        skb_reset_mac_header(skb);
 341        pkt_len = lp->tp.read(lp, &skb);
 342        skb_put(skb, pkt_len);
 343
 344        if (pkt_len > 0) {
 345                skb_trim(skb, pkt_len);
 346                skb->protocol = lp->tp.protocol(skb);
 347
 348                lp->stats.rx_bytes += skb->len;
 349                lp->stats.rx_packets++;
 350        //      netif_rx(skb);
 351                netif_rx_ni(skb);
 352                return pkt_len;
 353        }
 354        kfree_skb(skb);
 355        return pkt_len;
 356}
 357
 358static int iss_net_poll(void)
 359{
 360        struct list_head *ele;
 361        int err, ret = 0;
 362
 363        spin_lock(&opened_lock);
 364
 365        list_for_each(ele, &opened) {
 366                struct iss_net_private *lp;
 367
 368                lp = list_entry(ele, struct iss_net_private, opened_list);
 369
 370                if (!netif_running(lp->dev))
 371                        break;
 372
 373                spin_lock(&lp->lock);
 374
 375                while ((err = iss_net_rx(lp->dev)) > 0)
 376                        ret++;
 377
 378                spin_unlock(&lp->lock);
 379
 380                if (err < 0) {
 381                        printk(KERN_ERR "Device '%s' read returned %d, "
 382                               "shutting it down\n", lp->dev->name, err);
 383                        dev_close(lp->dev);
 384                } else {
 385                        // FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ);
 386                }
 387        }
 388
 389        spin_unlock(&opened_lock);
 390        return ret;
 391}
 392
 393
 394static void iss_net_timer(unsigned long priv)
 395{
 396        struct iss_net_private* lp = (struct iss_net_private*) priv;
 397
 398        spin_lock(&lp->lock);
 399
 400        iss_net_poll();
 401
 402        mod_timer(&lp->timer, jiffies + lp->timer_val);
 403
 404        spin_unlock(&lp->lock);
 405}
 406
 407
 408static int iss_net_open(struct net_device *dev)
 409{
 410        struct iss_net_private *lp = netdev_priv(dev);
 411        char addr[sizeof "255.255.255.255\0"];
 412        int err;
 413
 414        spin_lock(&lp->lock);
 415
 416        if ((err = lp->tp.open(lp)) < 0)
 417                goto out;
 418
 419        if (!lp->have_mac) {
 420                dev_ip_addr(dev, addr, &lp->mac[2]);
 421                set_ether_mac(dev, lp->mac);
 422        }
 423
 424        netif_start_queue(dev);
 425
 426        /* clear buffer - it can happen that the host side of the interface
 427         * is full when we get here. In this case, new data is never queued,
 428         * SIGIOs never arrive, and the net never works.
 429         */
 430        while ((err = iss_net_rx(dev)) > 0)
 431                ;
 432
 433        spin_lock(&opened_lock);
 434        list_add(&lp->opened_list, &opened);
 435        spin_unlock(&opened_lock);
 436
 437        init_timer(&lp->timer);
 438        lp->timer_val = ISS_NET_TIMER_VALUE;
 439        lp->timer.data = (unsigned long) lp;
 440        lp->timer.function = iss_net_timer;
 441        mod_timer(&lp->timer, jiffies + lp->timer_val);
 442
 443out:
 444        spin_unlock(&lp->lock);
 445        return err;
 446}
 447
 448static int iss_net_close(struct net_device *dev)
 449{
 450        struct iss_net_private *lp = netdev_priv(dev);
 451printk("iss_net_close!\n");
 452        netif_stop_queue(dev);
 453        spin_lock(&lp->lock);
 454
 455        spin_lock(&opened_lock);
 456        list_del(&opened);
 457        spin_unlock(&opened_lock);
 458
 459        del_timer_sync(&lp->timer);
 460
 461        lp->tp.close(lp);
 462
 463        spin_unlock(&lp->lock);
 464        return 0;
 465}
 466
 467static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
 468{
 469        struct iss_net_private *lp = netdev_priv(dev);
 470        unsigned long flags;
 471        int len;
 472
 473        netif_stop_queue(dev);
 474        spin_lock_irqsave(&lp->lock, flags);
 475
 476        len = lp->tp.write(lp, &skb);
 477
 478        if (len == skb->len) {
 479                lp->stats.tx_packets++;
 480                lp->stats.tx_bytes += skb->len;
 481                dev->trans_start = jiffies;
 482                netif_start_queue(dev);
 483
 484                /* this is normally done in the interrupt when tx finishes */
 485                netif_wake_queue(dev);
 486
 487        } else if (len == 0) {
 488                netif_start_queue(dev);
 489                lp->stats.tx_dropped++;
 490
 491        } else {
 492                netif_start_queue(dev);
 493                printk(KERN_ERR "iss_net_start_xmit: failed(%d)\n", len);
 494        }
 495
 496        spin_unlock_irqrestore(&lp->lock, flags);
 497
 498        dev_kfree_skb(skb);
 499        return NETDEV_TX_OK;
 500}
 501
 502
 503static struct net_device_stats *iss_net_get_stats(struct net_device *dev)
 504{
 505        struct iss_net_private *lp = netdev_priv(dev);
 506        return &lp->stats;
 507}
 508
 509static void iss_net_set_multicast_list(struct net_device *dev)
 510{
 511#if 0
 512        if (dev->flags & IFF_PROMISC)
 513                return;
 514        else if (!netdev_mc_empty(dev))
 515                dev->flags |= IFF_ALLMULTI;
 516        else
 517                dev->flags &= ~IFF_ALLMULTI;
 518#endif
 519}
 520
 521static void iss_net_tx_timeout(struct net_device *dev)
 522{
 523#if 0
 524        dev->trans_start = jiffies;
 525        netif_wake_queue(dev);
 526#endif
 527}
 528
 529static int iss_net_set_mac(struct net_device *dev, void *addr)
 530{
 531#if 0
 532        struct iss_net_private *lp = netdev_priv(dev);
 533        struct sockaddr *hwaddr = addr;
 534
 535        spin_lock(&lp->lock);
 536        memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN);
 537        spin_unlock(&lp->lock);
 538#endif
 539
 540        return 0;
 541}
 542
 543static int iss_net_change_mtu(struct net_device *dev, int new_mtu)
 544{
 545#if 0
 546        struct iss_net_private *lp = netdev_priv(dev);
 547        int err = 0;
 548
 549        spin_lock(&lp->lock);
 550
 551        // FIXME not needed new_mtu = transport_set_mtu(new_mtu, &lp->user);
 552
 553        if (new_mtu < 0)
 554                err = new_mtu;
 555        else
 556                dev->mtu = new_mtu;
 557
 558        spin_unlock(&lp->lock);
 559        return err;
 560#endif
 561        return -EINVAL;
 562}
 563
 564void iss_net_user_timer_expire(unsigned long _conn)
 565{
 566}
 567
 568
 569static struct platform_driver iss_net_driver = {
 570        .driver = {
 571                .name  = DRIVER_NAME,
 572        },
 573};
 574
 575static int driver_registered;
 576
 577static const struct net_device_ops iss_netdev_ops = {
 578        .ndo_open               = iss_net_open,
 579        .ndo_stop               = iss_net_close,
 580        .ndo_get_stats          = iss_net_get_stats,
 581        .ndo_start_xmit         = iss_net_start_xmit,
 582        .ndo_validate_addr      = eth_validate_addr,
 583        .ndo_change_mtu         = iss_net_change_mtu,
 584        .ndo_set_mac_address    = iss_net_set_mac,
 585        //.ndo_do_ioctl         = iss_net_ioctl,
 586        .ndo_tx_timeout         = iss_net_tx_timeout,
 587        .ndo_set_rx_mode        = iss_net_set_multicast_list,
 588};
 589
 590static int iss_net_configure(int index, char *init)
 591{
 592        struct net_device *dev;
 593        struct iss_net_private *lp;
 594        int err;
 595
 596        if ((dev = alloc_etherdev(sizeof *lp)) == NULL) {
 597                printk(KERN_ERR "eth_configure: failed to allocate device\n");
 598                return 1;
 599        }
 600
 601        /* Initialize private element. */
 602
 603        lp = netdev_priv(dev);
 604        *lp = ((struct iss_net_private) {
 605                .device_list            = LIST_HEAD_INIT(lp->device_list),
 606                .opened_list            = LIST_HEAD_INIT(lp->opened_list),
 607                .lock                   = __SPIN_LOCK_UNLOCKED(lp.lock),
 608                .dev                    = dev,
 609                .index                  = index,
 610                //.fd                   = -1,
 611                .mac                    = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 },
 612                .have_mac               = 0,
 613                });
 614
 615        /*
 616         * Try all transport protocols.
 617         * Note: more protocols can be added by adding '&& !X_init(lp, eth)'.
 618         */
 619
 620        if (!tuntap_probe(lp, index, init)) {
 621                printk("Invalid arguments. Skipping device!\n");
 622                goto errout;
 623        }
 624
 625        printk(KERN_INFO "Netdevice %d ", index);
 626        if (lp->have_mac)
 627                printk("(%pM) ", lp->mac);
 628        printk(": ");
 629
 630        /* sysfs register */
 631
 632        if (!driver_registered) {
 633                platform_driver_register(&iss_net_driver);
 634                driver_registered = 1;
 635        }
 636
 637        spin_lock(&devices_lock);
 638        list_add(&lp->device_list, &devices);
 639        spin_unlock(&devices_lock);
 640
 641        lp->pdev.id = index;
 642        lp->pdev.name = DRIVER_NAME;
 643        platform_device_register(&lp->pdev);
 644        SET_NETDEV_DEV(dev,&lp->pdev.dev);
 645
 646        /*
 647         * If this name ends up conflicting with an existing registered
 648         * netdevice, that is OK, register_netdev{,ice}() will notice this
 649         * and fail.
 650         */
 651        snprintf(dev->name, sizeof dev->name, "eth%d", index);
 652
 653        dev->netdev_ops = &iss_netdev_ops;
 654        dev->mtu = lp->mtu;
 655        dev->watchdog_timeo = (HZ >> 1);
 656        dev->irq = -1;
 657
 658        rtnl_lock();
 659        err = register_netdevice(dev);
 660        rtnl_unlock();
 661
 662        if (err) {
 663                printk("Error registering net device!\n");
 664                /* XXX: should we call ->remove() here? */
 665                free_netdev(dev);
 666                return 1;
 667        }
 668
 669        init_timer(&lp->tl);
 670        lp->tl.function = iss_net_user_timer_expire;
 671
 672#if 0
 673        if (lp->have_mac)
 674                set_ether_mac(dev, lp->mac);
 675#endif
 676        return 0;
 677
 678errout:
 679        // FIXME: unregister; free, etc..
 680        return -EIO;
 681
 682}
 683
 684/* ------------------------------------------------------------------------- */
 685
 686/* Filled in during early boot */
 687
 688struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line);
 689
 690struct iss_net_init {
 691        struct list_head list;
 692        char *init;             /* init string */
 693        int index;
 694};
 695
 696/*
 697 * Parse the command line and look for 'ethX=...' fields, and register all
 698 * those fields. They will be later initialized in iss_net_init.
 699 */
 700
 701#define ERR KERN_ERR "iss_net_setup: "
 702
 703static int __init iss_net_setup(char *str)
 704{
 705        struct iss_net_private *device = NULL;
 706        struct iss_net_init *new;
 707        struct list_head *ele;
 708        char *end;
 709        int n;
 710
 711        n = simple_strtoul(str, &end, 0);
 712        if (end == str) {
 713                printk(ERR "Failed to parse '%s'\n", str);
 714                return 1;
 715        }
 716        if (n < 0) {
 717                printk(ERR "Device %d is negative\n", n);
 718                return 1;
 719        }
 720        if (*(str = end) != '=') {
 721                printk(ERR "Expected '=' after device number\n");
 722                return 1;
 723        }
 724
 725        spin_lock(&devices_lock);
 726
 727        list_for_each(ele, &devices) {
 728                device = list_entry(ele, struct iss_net_private, device_list);
 729                if (device->index == n)
 730                        break;
 731        }
 732
 733        spin_unlock(&devices_lock);
 734
 735        if (device && device->index == n) {
 736                printk(ERR "Device %d already configured\n", n);
 737                return 1;
 738        }
 739
 740        if ((new = alloc_bootmem(sizeof new)) == NULL) {
 741                printk("Alloc_bootmem failed\n");
 742                return 1;
 743        }
 744
 745        INIT_LIST_HEAD(&new->list);
 746        new->index = n;
 747        new->init = str + 1;
 748
 749        list_add_tail(&new->list, &eth_cmd_line);
 750        return 1;
 751}
 752
 753#undef ERR
 754
 755__setup("eth=", iss_net_setup);
 756
 757/*
 758 * Initialize all ISS Ethernet devices previously registered in iss_net_setup.
 759 */
 760
 761static int iss_net_init(void)
 762{
 763        struct list_head *ele, *next;
 764
 765        /* Walk through all Ethernet devices specified in the command line. */
 766
 767        list_for_each_safe(ele, next, &eth_cmd_line) {
 768                struct iss_net_init *eth;
 769                eth = list_entry(ele, struct iss_net_init, list);
 770                iss_net_configure(eth->index, eth->init);
 771        }
 772
 773        return 1;
 774}
 775
 776module_init(iss_net_init);
 777
 778