/*
 * USB Networking Links
 * Copyright (C) 2000-2003 by David Brownell <dbrownell@users.sourceforge.net>
 * Copyright (C) 2002 Pavel Machek <pavel@ucw.cz>
 * Copyright (C) 2003 David Hollis <dhollis@davehollis.com>
 * Copyright (c) 2002-2003 TiVo Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * This code is modified to support only MCS7830 device    
 */


#define    DEBUG    0            // error path messages, extra info
//#define VERBOSE            // more; success messages
#define NEED_MII 1

#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
#include <linux/config.h>
#endif


#ifdef    CONFIG_USB_DEBUG
#define DEBUG
#endif

#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/random.h>
#include <linux/ethtool.h>
#include <linux/workqueue.h>
#include <linux/mii.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <linux/usb.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/kthread.h>
#include <linux/unistd.h> 
#define DRIVER_VERSION        "25-Nov-2005"

/*-------------------------------------------------------------------------*/

/*
 * Nineteen USB 1.1 max size bulk transactions per frame (ms), max.
 * Several dozen bytes of IPv4 data can fit in two such transactions.
 * One maximum size Ethernet packet takes twenty four of them.
 * For high speed, each frame comfortably fits almost 36 max size
 * Ethernet packets (so queues should be bigger).
 */
#define    RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
#define    TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)

// packets are always ethernet inside
// ... except they can be bigger (limit of 64K with NetChip framing)
#define MIN_PACKET    sizeof(struct ethhdr)
#define MAX_PACKET    32768

// reawaken network queue this soon after stopping; else watchdog barks
#define TX_TIMEOUT_JIFFIES    (5*HZ)

// throttle rx/tx briefly after some faults, so khubd might disconnect()
// us (it polls at HZ/4 usually) before we report too many false errors.
#define THROTTLE_JIFFIES    (HZ/8)

// for vendor-specific control operations
#define    CONTROL_TIMEOUT_MS    500

// between wakeups
#define UNLINK_TIMEOUT_MS    3

/*-------------------------------------------------------------------------*/

// MOSCHIP functions start
unsigned char  WITHOUT_PHY = 0;


#define CONFIG_USB_MOSCHIP
#include "mcs7830.h"

#define MSECS_TO_JIFFIES(ms) (((ms)*HZ+999)/1000)

#define NETH_USB 4

#define WAIT_FOR_EVER   (HZ * 0 ) /* timeout urb is wait for ever*/
#define MOS_WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */


#define MCS_VENDOR_ID 0x9710    /* Vendor Id of Moschip MCS7830 */
#define MCS_PRODUCT_ID_7830 0x7830   /* Product Id  */
#define MCS_PRODUCT_ID_7832 0x7832   /* Product Id  */

#define MOS_WRITE       0x0D
#define MOS_READ        0x0E
// mii.h values coresponding to return values of mdio_read
#define BIT_13      0x2000
#define BIT_14      0x4000
#define BIT_15      0x8000
#define BIT_16      0x00010000
#define BIT_17      0x00020000
#define BIT_18      0x00040000
#define BIT_19      0x00080000
#define BIT_20      0x00100000
#define BIT_21      0x00200000
#define BIT_22      0x00400000
#define BIT_23      0x00800000
#define BIT_24      0x01000000
#define LINKOK      0x0004


/* setting and get register values */
#ifdef comment
static int GetVendorCommandPassive(struct usbnet *mcs, __u16 reg_index,__u16 length, void *data);
static int SetVendorCommandPassive(struct usbnet *mcs, __u16 reg_index,__u16 length, void *data);
static int mcs7830_find_endpoints(struct usbnet *mcs, struct usb_endpoint_descriptor *ep, int epnum);
static int MosUsbGetPermanentAddress(struct usbnet *mcs);
static int AutoNegChangemode(struct usbnet *mcs,int ptrUserPhyMode);
static int ANGInitializeDev(struct usbnet *mcs);


static int ReadPhyRegisterPassive(struct usbnet *mcs,__u8 phy_reg_index,__u16 *phy_data);
static int WritePhyRegisterPassive(struct usbnet *mcs,__u8 PhyRegIndex,__u16 *PhyRegValue);
#endif
static void mcs7830_disconnect (struct usb_device *udev, void *ptr);

static DECLARE_COMPLETION(linkStatTh_exit); 
struct linkStatusThread { 
    spinlock_t lock; 
    int terminate;
    struct task_struct *linkTask;
}; 

// randomly generated ethernet address
static u8    node_id [ETH_ALEN];

// state we keep for each device we handle
struct usbnet {
    //housekeeping
    struct usb_device    *udev;
    struct driver_info    *driver_info;
    wait_queue_head_t    *wait;

    // i/o info: pipes etc
    unsigned        in, out;
    struct usb_host_endpoint *status;
    unsigned        maxpacket;
    struct timer_list    delay;

    // protocol/interface state
    struct net_device    *net;
    struct net_device_stats    stats;
    int            msg_enable;
    unsigned long        data [5];

    struct mii_if_info    mii;

    // various kinds of pending driver work
    struct sk_buff_head    rxq;
    struct sk_buff_head    txq;
    struct sk_buff_head    done;
    struct urb        *interrupt;
    struct tasklet_struct    bh;

    struct work_struct    kevent;
    unsigned long        flags;

#define EVENT_TX_HALT        0
#define EVENT_RX_HALT        1
#define EVENT_RX_MEMORY      2
#define EVENT_STS_SPLIT      3
#define EVENT_LINK_RESET     4

// usage for the vendor command MOSCHIP change !!!!
    
    __u8    SpeedDuplex;    // New reg value for speed/duplex
    __u8    ForceDuplex;    // 1 for half-duplex, 2 for full duplex
    __u8    ForceSpeed;        // 10 for 10Mbps, 100 for 100 Mbps
    
    

    __u8    ByteValue [10];
    __u16   WordValue;
    __u8    checkval2[10];    //added for checking removing later
    __u32   CurrentPacketFilter;
    __u8    HifReg15;
    __u16   PhyControlReg;
    __u16   TempPhyControlReg;
    __u16   PhyAutoNegLinkPartnerReg;
    __u16   PhyChipStatusReg;
    __u16   PhyAutoNegAdvReg;
    __u16   DeviceReleaseNumber;

    /* flag for the identification of the type of PHY. */ //mahipal
    __u32    PhyIdentity;
    __u8    AiPermanentNodeAddress[ETHERNET_ADDRESS_LENGTH];
    __u8    bAutoNegStarted;
    __u32    ptrPauseTime;

    //Rev.C change
    __u8    ptrPauseThreshold;
    __u32    ptrFpgaVersion;
    __u32    ptrRxErrorCount;

    /* link status thread */
    struct linkStatusThread linkStatTh;


};

// device-specific info used by the driver
struct driver_info {
    char    *description;
    int        flags;
    
    /* framing is CDC Ethernet, not writing ZLPs (hw issues), or optionally: */
#define FLAG_NO_SETINT    0x0010        /* device can't set_interface() */
#define FLAG_ETHER    0x0020            /* maybe use "eth%d" names */


    /* init device ... can sleep, or cause probe() failure */
    int    (*bind)(struct usbnet *, struct usb_interface *);

    /* cleanup device ... can sleep, but can't fail */
    void (*unbind)(struct usbnet *, struct usb_interface *);

    /* reset device ... can sleep */
    int    (*reset)(struct usbnet *);

    /* see if peer is connected ... can sleep */
    int    (*check_connect)(struct usbnet *);

    /* for status polling */
    void (*status)(struct usbnet *, struct urb *);

    /* link reset handling, called from defer_kevent */
    int (*link_reset)(struct usbnet *);

    /* fixup rx packet (strip framing) */
    int    (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);

    /* fixup tx packet (add framing) */
    struct sk_buff    *(*tx_fixup)(struct usbnet *dev,
                struct sk_buff *skb, int flags);

    // FIXME -- also an interrupt mechanism
    // useful for at least PL2301/2302 and GL620USB-A
    // and CDC use them to report 'is it connected' changes

    /* for new devices, use the descriptor-reading code instead */
    int        in;            /* rx endpoint */
    int        out;        /* tx endpoint */

    unsigned long    data;        /* Misc driver specific data */
};

// we record the state for each of our queued skbs
enum skb_state {
    illegal = 0,
    tx_start, tx_done,
    rx_start, rx_done, rx_cleanup
};

struct skb_data {    // skb->cb is one of these
    struct urb        *urb;
    struct usbnet    *dev;
    enum skb_state    state;
    size_t            length;
};

static const char driver_name [] = "MOSCHIP usb-ethernet driver";


/* use ethtool to change the level for any given device */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
static int msg_level = -1;
module_param (msg_level, int, 0);
MODULE_PARM_DESC (msg_level, "Override default message level");
#endif


/*
 * Debug related defines
 */

/* 1: Enables the debugging -- 0: Disable the debugging */

#define MOS_DEBUG       0

#if MOS_DEBUG
        //static int debug = 0;
        #define DPRINTK(fmt, args...) printk( "%s: " fmt, __FUNCTION__ , ## args)
#else
        //static int debug = 0;
        #define DPRINTK(fmt, args...)
#endif




#ifdef DEBUG
#define devdbg(usbnet, fmt, arg...) \
    printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
#else
#define devdbg(usbnet, fmt, arg...) do {} while(0)
#endif

#define deverr(usbnet, fmt, arg...) \
    printk(KERN_ERR "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
#define devwarn(usbnet, fmt, arg...) \
    printk(KERN_WARNING "%s: " fmt "\n" , (usbnet)->net->name , ## arg)

#define devinfo(usbnet, fmt, arg...) \
    printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net->name , ## arg); \

/*-------------------------------------------------------------------------*/

static void usbnet_get_drvinfo (struct net_device *, struct ethtool_drvinfo *);
static u32 usbnet_get_link (struct net_device *);
static u32 usbnet_get_msglevel (struct net_device *);
static void usbnet_set_msglevel (struct net_device *, u32);
static void defer_kevent (struct usbnet *, int);
static int ReadPhyRegisterPassive(struct usbnet *mcs,__u8 PhyRegIndex,__u16 *PhyRegValue);

//static int mos_status_test(void* arg);
__u16 linkStatus;


static int update_linkStat_thread(void* arg)
{
    struct usbnet *dev = (struct usbnet *) arg;
    int ret;
    __u16 regVal;

    printk(KERN_DEBUG "moschip: in update_linkStat_thread.. \n");

    /* allow signals that should be handled */
    allow_signal(SIGKILL);

    while(!kthread_should_stop()) {
        set_current_state(TASK_INTERRUPTIBLE);
        printk("while_thread\n");

        if(dev->linkStatTh.terminate == 0)
                msleep(2000);

        if(signal_pending(current)) break;

        if(dev->linkStatTh.terminate) break;

        spin_lock(&dev->linkStatTh.lock);
           
        ret = ReadPhyRegisterPassive(dev,PHY_STATUS_REG_INDEX,&regVal);
        if(regVal & PHY_STATUS_REG_LINK_STATUS) {
                netif_carrier_on(dev->net);
                printk("link ok %x\n",regVal);
        } else {
                netif_carrier_off(dev->net);
                printk("link not ok %x\n",regVal);
        }
        spin_unlock(&dev->linkStatTh.lock);
    }

    set_current_state(TASK_RUNNING);
    complete_and_exit(&linkStatTh_exit, 0);
    return 0;

}



/* mostly for PDA style devices, which are always connected if present */
static int always_connected (struct usbnet *dev)
{
    return 0;
}


static int moschip_bind(struct usbnet *dev, struct usb_interface *inf)
{
    printk("moschip_bind\n");
    return 0;
}

static void moschip_unbind(struct usbnet *dev, struct usb_interface *intf)
{
    printk("moschip_bind\n");
    return 0;
}

static int moschip_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
    printk("moschip_rx_fixup\n");
    return 0;
}

static struct sk_buff *moschip_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
{
    printk("moschip_tx_fixup\n");
    return NULL;
}

static void moschip_status(struct usbnet *dev, struct urb *urb)
{
    printk("moschip_status\n");
}

static int moschip_link_reset(struct usbnet *dev)
{

    printk("moschip_link_reset\n");
    u8 mode;
    struct ethtool_cmd ecmd;

    mii_check_media(&dev->mii, 1,1);
    mii_ethtool_gset(&dev->mii, &ecmd);
    printk("link_speed %d , duplex %d\n", ecmd.speed , ecmd.duplex);
    return 0;
}





/* handles CDC Ethernet and many other network "bulk data" interfaces */
static int
get_endpoints (struct usbnet *dev, struct usb_interface *intf)
{
    int                tmp;
    struct usb_host_interface    *alt = NULL;
    struct usb_host_endpoint    *in = NULL, *out = NULL;
    struct usb_host_endpoint    *status = NULL;

    for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
        unsigned    ep;

        in = out = status = NULL;
        alt = intf->altsetting + tmp;

        /* take the first altsetting with in-bulk + out-bulk;
         * remember any status endpoint, just in case;
         * ignore other endpoints and altsetttings.
         */
        for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
            struct usb_host_endpoint    *e;
            int                intr = 0;

            e = alt->endpoint + ep;
            switch (e->desc.bmAttributes) {
            case USB_ENDPOINT_XFER_INT:
                if (!(e->desc.bEndpointAddress & USB_DIR_IN))
                    continue;
                intr = 1;
                /* FALLTHROUGH */
            case USB_ENDPOINT_XFER_BULK:
                break;
            default:
                continue;
            }
            if (e->desc.bEndpointAddress & USB_DIR_IN) {
                if (!intr && !in)
                    in = e;
                else if (intr && !status)
                    status = e;
            } else {
                if (!out)
                    out = e;
            }
        }
        if (in && out)
            break;
    }
    if (!alt || !in || !out)
        return -EINVAL;

    if (alt->desc.bAlternateSetting != 0
            || !(dev->driver_info->flags & FLAG_NO_SETINT)) {
        tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
                alt->desc.bAlternateSetting);
        if (tmp < 0)
            return tmp;
    }
    
    dev->in = usb_rcvbulkpipe (dev->udev,
            in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
    dev->out = usb_sndbulkpipe (dev->udev,
            out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
    dev->status = status;
    return 0;
}


#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
static void intr_complete (struct urb *urb, struct pt_regs *regs);
#else
static void intr_complete (struct urb *urb );
#endif



static int init_status (struct usbnet *dev, struct usb_interface *intf)
{
    char        *buf = NULL;
    unsigned    pipe = 0;
    unsigned    maxp;
    unsigned    period;

    printk("init_status ==>\n");
    
    if (!dev->driver_info->status)
        return 0;

    pipe = usb_rcvintpipe (dev->udev,
            dev->status->desc.bEndpointAddress
                & USB_ENDPOINT_NUMBER_MASK);
    maxp = usb_maxpacket (dev->udev, pipe, 0);

    /* avoid 1 msec chatter:  min 8 msec poll rate */
    period = max ((int) dev->status->desc.bInterval,
        (dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
    buf = kmalloc (maxp, GFP_KERNEL);
#else
    buf = kmalloc (maxp, SLAB_KERNEL);
#endif

    if (buf) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
        dev->interrupt = usb_alloc_urb (0, GFP_KERNEL);
#else
        dev->interrupt = usb_alloc_urb (0, SLAB_KERNEL);
#endif
        if (!dev->interrupt) {
            kfree (buf);
            return -ENOMEM;
        } else {
            usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
                buf, maxp, (usb_complete_t)intr_complete, dev, period);
            dev_dbg(&intf->dev,
                "status ep%din, %d bytes period %d\n",
                usb_pipeendpoint(pipe), maxp, period);
        }
    }
    return  0;
}

static void skb_return (struct usbnet *dev, struct sk_buff *skb)
{
    int    status;

    skb->dev = dev->net;
    skb->protocol = eth_type_trans (skb, dev->net);
    dev->stats.rx_packets++;
    dev->stats.rx_bytes += skb->len;

    if (netif_msg_rx_status (dev))
        devdbg (dev, "< rx, len %zu, type 0x%x",
            skb->len + sizeof (struct ethhdr), skb->protocol);
    memset (skb->cb, 0, sizeof (struct skb_data));
    status = netif_rx (skb);
    if (status != NET_RX_SUCCESS && netif_msg_rx_err (dev))
        devdbg (dev, "netif_rx status %d", status);
}


#ifdef CONFIG_USB_MOSCHIP
static const struct driver_info moschip_info = {
    .description =  "MOSCHIP 7830 usb-NET adapter",
    .check_connect = always_connected,

    .in = 1, .out = 2,
    //.bind = moschip_bind,
    //.unbind = moschip_unbind,
    //.status = moschip_status,
    //.link_reset = moschip_link_reset,
    //.reset = moschip_link_reset,
    //.rx_fixup = moschip_rx_fixup,
    //.tx_fixup = moschip_tx_fixup,
    //.epsize = 64,
};
#endif  /* CONFIG_USB_NOSCHIP */


static int GetVendorCommandPassive(struct usbnet *mcs, __u16 reg_index,__u16 length, void *data)
{
    struct usb_device *dev = mcs->udev;
    int ret=0;
    void *buffer;
    
    buffer = kmalloc(length, GFP_NOIO);
    if(buffer == NULL)
    {
        DPRINTK(" %s - kmallc failed\n", __FUNCTION__);
        return -ENOMEM;
    }
    
    //DPRINTK(" in GetVendorCommandPassive reg_index is %x   content of data %x  status %x\n",reg_index,*data,ret);
    ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RD_BREQ,
        MCS_RD_BMREQ, 0x0000, reg_index, buffer, length,
        MSECS_TO_JIFFIES(MCS_CTRL_TIMEOUT));
    //DPRINTK(" in GetVendorCommandPassive reg_index is %x   content of data %x  status %x\n",reg_index,*data,ret);
    memcpy(data, buffer, length);
    kfree(buffer);
    return ret;
}

static int SetVendorCommandPassive(struct usbnet *mcs, __u16 reg_index,__u16 length, void *data)
{
    struct usb_device *dev = mcs->udev;
    int ret=0;
    void *buffer;
    
    buffer = kmemdup(data, length, GFP_NOIO);
    if(buffer == NULL)
    {
        DPRINTK("%s - kmemdup failed\n");
        return -ENOMEM;
    }
    
    ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WR_BREQ,
        MCS_WR_BMREQ, 0x0000, reg_index, buffer,length,
        MSECS_TO_JIFFIES(MCS_CTRL_TIMEOUT));
    kfree(buffer);
    //DPRINTK(" in SetVendorCommandPassive reg_index is %x   content of data %x  status %x\n",reg_index,*data,ret);
    return ret;
}



static int MosUsbGetPermanentAddress(struct usbnet *mcs)
{
    return GetVendorCommandPassive(mcs,HIF_REG_16,ETHERNET_ADDRESS_LENGTH,mcs->AiPermanentNodeAddress);
}

static int ReadPhyRegisterPassive(struct usbnet *mcs,__u8 PhyRegIndex,__u16 *PhyRegValue)
{
    
    int status=0,Count;
    //__u8 checkval=0x0;
    __u16 checkval=0x0;
    mcs->WordValue = 0x0;
    //__u8 checkval2[10];
    status=SetVendorCommandPassive(mcs,HIF_REG_11,2, &checkval);//(__u8 *)(&mcs->WordValue));
    if(status<0)
    {
        //DPRINTK("ERROR: couldn't clean register HIF_REG_11&12 \n");
        return status;
    }
    else
    {
        //DPRINTK(" cleaned register HIF_REG_11&12 \n");
        checkval=0;
    }

    status=0;
    mcs->checkval2[0]= READ_OPCODE | FARADAY_PHY_ADDRESS;
    //mcs->checkval2[0]= READ_OPCODE |EXTERNEL_PHY_ADDRESS;
    //DPRINTK("in Local SetVendorCommandPassive reg is %x \tval is %x\n",HIF_REG_13,mcs->checkval2[0]);// *(mcs->ByteValue));
    status=SetVendorCommandPassive(mcs,HIF_REG_13,1,&mcs->checkval2[0]);//mcs->ByteValue);
    if(status<0)
    {
        //DPRINTK("ERROR :Couldn't set HIF13 for Read Mode \n");
        return status;
    }
    else
    {
        //DPRINTK("Set HIF_REG_13 with READ_OPCODE | FARADAY_PHY_ADDRESS Done!!\n");
    }
    
    // 2. Build value for HIF_REG14
    // Set Pend Flag, Reset Ready Flag & address of Reg.with in the PHY
    // Will procesed By MAC - MIIM Hardware.
    // Msb 1000 0000 Lsb - b7 Pend, b6 Ready,
    // b5 Not used,b4b3b2b1b0 Address of Reg with in the PHY
    
    status=0;
    mcs->checkval2[0]=0x0;
    mcs->checkval2[0] = HIF_REG_14_PEND_FLAG_BIT | (PhyRegIndex & 0x1F);
    status=SetVendorCommandPassive(mcs,HIF_REG_14,1,&mcs->checkval2[0]);//mcs->ByteValue);
    if(status<0)
    {
        //DPRINTK("ERROR : Setting pend flag & PHY reg.index in HIF_REG_14 to read phy \n");
        return status;
    }
    else
    {
        //DPRINTK(" Setting pend flag & PHY reg.index in HIF_REG_14 to read phy Done !!!\n");
    }
    mcs->checkval2[0]=0x0;
    
    // 3. Read (completion) Status for last command
    Count = 10;
    do
    {
        status=0;
        mcs->checkval2[0]=0x0;
        status = GetVendorCommandPassive(mcs,HIF_REG_14,1,&mcs->checkval2[0]);//(__u8 *)mcs->ByteValue);
        if(status<0)
        {
            //DPRINTK("ERROR : Reading the ReadyFlag bit in the HIF_REG_14 \n");
            break;//command error
        }
        else
        {
            //DPRINTK("Reading the ReadyFlag bit in the HIF_REG_14 DONE!!! \n");
            
        }
        
        //DPRINTK(" mcs->checkval2[0] value is %x\n",mcs->checkval2[0]);
        if (mcs->checkval2[0] & HIF_REG_14_READY_FLAG_BIT)
        {
            //DPRINTK("Command completed\n");
            break; // command complete
        }
        else
        {
            
            Count -- ;
            //DPRINTK("READ PHY register command not Completed. Checking the READY flag again.\n");
            if (Count == 0 )
            {
                //DPRINTK("Checking the READY flag for 10 times completed not yet set.\n");
                status = -1;
                break;
            }
            
        }
        
        
    }while(1);
    
    if(status<0)
    {
        return status;
    }
    
    status=0;    
    //status = GetVendorCommandPassive(mcs,HIF_REG_11,2,(__u8 *)PhyRegValue);
    status = GetVendorCommandPassive(mcs,HIF_REG_11,2,PhyRegValue);
    if(status<0)
    {
        //DPRINTK("ERROR :Couldn't read HIF11-12 for phy data \n");
        return status;
    }
    return status;
}

static int WritePhyRegisterPassive(struct usbnet *mcs,__u8 PhyRegIndex,__u16 *PhyRegValue)
{
    
    int status=0,Count;
    mcs->WordValue = 0x0;
    //status=SetVendorCommandPassive(mcs,HIF_REG_11,2,(__u8 *)PhyRegValue);
    status=SetVendorCommandPassive(mcs,HIF_REG_11,2,PhyRegValue);
    if(status<0)
    {
        //DPRINTK("ERROR: couldn't write data in  register HIF_REG_11&12 \n");
        return status;
    }
    else
    {
        //DPRINTK(" Wrote data to register HIF_REG_11&12 \n");
    }
    status=0;
    mcs->checkval2[0]= WRITE_OPCODE | FARADAY_PHY_ADDRESS;
    //DPRINTK("in Local SetVendorCommandPassive reg is %x \tval is %x\n",HIF_REG_13,mcs->checkval2[0]);// *(mcs->ByteValue));
    status=SetVendorCommandPassive(mcs,HIF_REG_13,1,&mcs->checkval2[0]);//mcs->ByteValue);
    if(status<0)
    {
        //DPRINTK("ERROR :Couldn't set HIF13 for wRITE Mode \n");
        return status;
    }
    else
    {
        //DPRINTK("Set HIF_REG_13 with WRITE_OPCODE | FARADAY_PHY_ADDRESS Done!!\n");
    }
    
    // 2. Build value for HIF_REG14
    // Set Pend Flag, Reset Ready Flag & address of Reg.with in the PHY
    // Will procesed By MAC - MIIM Hardware.
    // Msb 1000 0000 Lsb - b7 Pend, b6 Ready,
    // b5 Not used,b4b3b2b1b0 Address of Reg with in the PHY
    
    status=0;
    mcs->checkval2[0]=0x0;
    mcs->checkval2[0] = HIF_REG_14_PEND_FLAG_BIT | (PhyRegIndex & 0x1F);
    status=SetVendorCommandPassive(mcs,HIF_REG_14,1,&mcs->checkval2[0]);//mcs->ByteValue);
    if(status<0)
    {
        //DPRINTK("ERROR : Setting pend flag & PHY reg.index in HIF_REG_14 to write phy \n");
        return status;
    }
    else
    {
        //DPRINTK(" Setting pend flag & PHY reg.index in HIF_REG_14 to write phy Done !!!\n");
        
    }
    mcs->checkval2[0]=0x0;
    
    // 3. Read (completion) Status for last command
    Count = 10;
    do
    {
        status=0;
        mcs->checkval2[0]=0x0;
        status = GetVendorCommandPassive(mcs,HIF_REG_14,1,&mcs->checkval2[0]);//(__u8 *)mcs->ByteValue);
        if(status<0)
        {
            //DPRINTK("ERROR : Reading the ReadyFlag bit in the HIF_REG_14 \n");
            break;//command error
        }
        else
        {
            //DPRINTK("Reading the ReadyFlag bit in the HIF_REG_14 DONE!!! \n");
        }
        
        //DPRINTK(" mcs->checkval2[0] value is %x\n",mcs->checkval2[0]);
        if (mcs->checkval2[0] & HIF_REG_14_READY_FLAG_BIT)
        {
            //DPRINTK("Command completed\n");
            break; // command complete
        }
        else
        {
            
            Count -- ;
            //DPRINTK("READ PHY register command not Completed. Checking the READY flag again.\n");
            
            if (Count == 0 )
            {
                //DPRINTK("Checking the READY flag for 10 times completed not yet set.\n");
                status = -1;
                break;
            }
            
        }
        
        
    }while(1);
    
    return status;
}


static int PrintAutoNegAdvRegister(struct usbnet *mcs)
{
    return 1;
}


static int AutoNegChangemode(struct usbnet *mcs,int ptrUserPhyMode)
{
    
    int status=0x0;
    __u16 AutoNegAdvtReg;
    __u8 ReadHifVal;    
    if( WITHOUT_PHY==0)
    {
        if(ptrUserPhyMode ==0)
        {
            
            // Writing 0x0021 in PhyAutoNeg Advertisement reg 4
            AutoNegAdvtReg = PHY_AUTONEGADVT_FdxPause | \
                PHY_AUTONEGADVT_Fdx100TX |\
                PHY_AUTONEGADVT_100TX | PHY_AUTONEGADVT_ieee802_3|\
                PHY_AUTONEGADVT_10TFdx|PHY_AUTONEGADVT_10T;
        }
        else if(ptrUserPhyMode ==1)
        {
            // Writing 0x0021 in PhyAutoNeg Advertisement reg 4
            AutoNegAdvtReg = PHY_AUTONEGADVT_10T | PHY_AUTONEGADVT_ieee802_3;
        }
        else if(ptrUserPhyMode ==2)
        {
            // Writing 0x0461 in PhyAutoNeg Advertisement reg 4
            AutoNegAdvtReg = PHY_AUTONEGADVT_FdxPause | PHY_AUTONEGADVT_10TFdx |\
                PHY_AUTONEGADVT_10T | PHY_AUTONEGADVT_ieee802_3;
            
        }
        else if(ptrUserPhyMode ==3)
        {
            // Writing 0x0081 in PhyAutoNeg Advertisement reg 4
            AutoNegAdvtReg = PHY_AUTONEGADVT_100TX | PHY_AUTONEGADVT_ieee802_3;
            
        }
        else if(ptrUserPhyMode ==4)
        {
            // Writing 0x0581 in PhyAutoNeg Advertisement reg 4
            AutoNegAdvtReg = PHY_AUTONEGADVT_FdxPause | PHY_AUTONEGADVT_Fdx100TX |\
                PHY_AUTONEGADVT_100TX | PHY_AUTONEGADVT_ieee802_3;
        }
        //DPRINTK("in AutoNegChangemode  AutoNegAdvtReg is %04x \n", AutoNegAdvtReg);
        status=0x0;
        status=WritePhyRegisterPassive(mcs,PHY_AUTONEGADVT_REG_INDEX,&AutoNegAdvtReg);
        //DPRINTK("Status of WritePhyRegisterPassive ANG is %x\n",status);
        if(status >=0)
        {
            //DPRINTK("Writing value for AutoNegotiation in AutoNegAdvt Register() success\n");
            //break;
            
        }
        else
        {    
            //DPRINTK("Writing value for AutoNegotiation in AutoNegAdvt Register() FAILURE\n");
            mcs7830_disconnect(mcs->udev, (void *) mcs);
            return -1;//NULL;
        }
        //////////////////Change For Link Status Problem :Begin ///////////
        //First Disable All
        mcs->PhyControlReg =  0x0000;
        status=0x0;
        status = WritePhyRegisterPassive(mcs,PHY_CONTROL_REG_INDEX,&mcs->PhyControlReg);
        if(status >=0)
        {
            //DPRINTK("Writing value WritePhyControlRegister() success\n");
            //break;
        }
        else
        {
            //DPRINTK("Writing value for WritePhyControlRegister() FAILURE\n");
            mcs7830_disconnect(mcs->udev, (void *) mcs);
            return -1;//NULL;
        }
        
        mcs->PhyControlReg =  PHY_CONTROL_AUTONEG_ENABLE;
        status=0x0;
        status = WritePhyRegisterPassive(mcs,PHY_CONTROL_REG_INDEX,&mcs->PhyControlReg);
        if(status >=0)
        {
            //DPRINTK("Writing value WritePhyControlRegister() ANG enable success\n");
            //break;
        }
        else
        {
            //DPRINTK("Writing value for WritePhyControlRegister() SNG enable FAILURE\n");
            mcs7830_disconnect(mcs->udev, (void *) mcs);
            return -1;//NULL;
        }
        
        //Restart Auto Neg (Keep the Enable Auto Neg Bit Set)
        mcs->PhyControlReg =  PHY_CONTROL_AUTONEG_ENABLE |PHY_CONTROL_RESTART_AUTONEG;
        status=0x0;
        status = WritePhyRegisterPassive(mcs,PHY_CONTROL_REG_INDEX,&mcs->PhyControlReg);
        
        if(status >=0)
        {
            //DPRINTK("Writing value WritePhyControlRegister() ANG enable&restart success\n");
            //break;
            
        }
        else
        {
            //DPRINTK("Writing value for WritePhyControlRegister() SNG enable&restart FAILURE\n");
            mcs7830_disconnect(mcs->udev, (void *) mcs);
            return -1;//NULL;
            
        }
        
        mcs->bAutoNegStarted = TRUE;
        //////////////////Change For Link Status Problem :End ///////////
        
        
        status=0x0;
        if(mcs->PhyIdentity == INTEL_PHY)
        {
            status = ReadPhyRegisterPassive(mcs,PHY_CHIPSTATUS_REG_INDEX,&mcs->TempPhyControlReg);
        }
        else if(mcs->PhyIdentity == FARADAY_PHY)
        {
            
            status = ReadPhyRegisterPassive(mcs,PHY_CONTROL_REG_INDEX,&mcs->TempPhyControlReg);
        }
        if(status >=0)
        {
            //DPRINTK("PhyControl for AutoNegotiation Value: x%04x\n", mcs->TempPhyControlReg);
            //break;
        }
        else
        {
            //DPRINTK("Reading value from ReadPhyControlRegister() FAILURE FAILURE\n");
            mcs7830_disconnect(mcs->udev, (void *) mcs);
            return -1;//NULL;
        }
        // read linkspeed & duplex from PHY & set to HIF
        AutoNegAdvtReg=0x0;
        status=0x0;
        status = ReadPhyRegisterPassive(mcs,PHY_AUTONEGADVT_REG_INDEX,&AutoNegAdvtReg);
        if(status >=0)
        {
            //DPRINTK("in AutoNegChangemode  AutoNegAdvtReg is %04x \n", AutoNegAdvtReg);
            //break;
        }
        else
        {
            //DPRINTK("in AutoNegChangemode  AutoNegAdvtReg FAILED\n");
            mcs7830_disconnect(mcs->udev, (void *) mcs);
            return -1;//NULL;
        }
        
        
        if(AutoNegAdvtReg & PHY_AUTONEGADVT_100TX) 
            mcs->HifReg15 |=  HIF_REG_15_SPEED100; 
        else
            mcs->HifReg15 &= ~HIF_REG_15_SPEED100;
        
        
        if((AutoNegAdvtReg & PHY_AUTONEGADVT_10TFdx)||(AutoNegAdvtReg & PHY_AUTONEGADVT_Fdx100TX)) 
            mcs->HifReg15 |= HIF_REG_15_FULLDUPLEX_ENABLE;
        else
            mcs->HifReg15 &= ~HIF_REG_15_FULLDUPLEX_ENABLE;
        
        
        status=0x0;
        status=SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
        //DPRINTK("in AutoNegChangemode  Writing mcs->HifReg15 is %04x \n",mcs->HifReg15 );
        if(status >=0)
        {
            //DPRINTK("in AutoNegChangemode  writing mcs->HifReg15 SUCCESS\n" );
            //break;
        }
        else
        {
            //DPRINTK("in AutoNegChangemode  writing mcs->HifReg15 FAILED\n");
            mcs7830_disconnect(mcs->udev, (void *) mcs);
            return -1;//NULL;
        }
        status=0;
        status=GetVendorCommandPassive( mcs,HIF_REG_15,1,&ReadHifVal);
        //DPRINTK("in AutoNegChangemode  Read mcs->HifReg15 is %04x \n",ReadHifVal );
    } // WITHOUT_PHY end

    //DPRINTK("Phy Status Register Value: x%04x\n", mcs->PhyStatusReg);
    return status;        
}


static int ANGInitializeDev(struct usbnet *mcs)
{
    int    Ccnt=0;
    __u16    Read_HIFReg_22; 
    __u8    SingleByte;
    int     status;
    __u16   PhyIDReg1 = 0x0;
    __u16   PhyIDReg2 = 0x0;
    
    
    mcs->DeviceReleaseNumber = DEVICE_REV_B; ///by default revision B
    
    // Check for other revision devices by reading one of the additional registers that are 
    // existing.
    // Here, we are reading HIFReg_22 for the above mentioned purpose
    do {
        status = GetVendorCommandPassive(mcs,0x15,2,&Read_HIFReg_22);
        if(status >0)//STATUS_SUCCESS)
        {
            DPRINTK("Device is of revision C\n");
            mcs->DeviceReleaseNumber = DEVICE_REV_C;
            break;
        }
        Ccnt++;
        //udelay(50);
    } while(Ccnt>2);
    
    Ccnt=5;
    do {
        status=0x0;
        status = MosUsbGetPermanentAddress(mcs);
        Ccnt--;
    } while(Ccnt>0);
    
    if(status >=0)
    {
        DPRINTK("vendor command MosUsbGetPermanentAddress success\n");
        //DPRINTK("address is %x\n",mcs->AiPermanentNodeAddress);
    }
    
    
    // External_Phy
    
    /* code for External PHY implementation */ 
    
    // Reading PhyID Reg1
    status = ReadPhyRegisterPassive(mcs,PHY_IDENTIFICATION1_REG_INDEX,&PhyIDReg1);
    //DPRINTK("PhyIDReg1 is 0x%x,status=%d \n",PhyIDReg1,status);
    if(status >=0)
        DPRINTK("Reading PHY_IDENTIFICATION1_REG_INDEX success...\n");
    else
        DPRINTK("Reading PHY_IDENTIFICATION1_REG_INDEX failure...\n");
    // Reading PhyID Reg2
    status = ReadPhyRegisterPassive(mcs,PHY_IDENTIFICATION2_REG_INDEX,&PhyIDReg2);
    //DPRINTK("PhyIDReg2 is 0x%x, status= %d \n",PhyIDReg2,status);
    if(status >= 0)
        DPRINTK("Reading PHY_IDENTIFICATION2_REG_INDEX success...\n");
    else
        DPRINTK("Reading PHY_IDENTIFICATION2_REG_INDEX failure...\n");
    /* validate the external PHY */
    if( (PhyIDReg1 == INTEL_PHY_ID1) && (PhyIDReg2 == INTEL_PHY_ID2) )
    {
        DPRINTK(" INTEL_PHY detected \n");
        mcs->PhyIdentity = INTEL_PHY;
        //DPRINTK("\n Writing 0x0080 in  PHY_CONFIG_REG_INDEX REG 19 \n");
        //WritePhyRegisterPassive(mcs,PHY_CONFIG_REG_INDEX, 0x0080); // check
    }
    else
    {
        DPRINTK(" FARADAY_PHY detected \n");
        mcs->PhyIdentity =FARADAY_PHY;
    }
    
    
    // Check whether we have to do AutoNegotiation or the USER has forced
    // us into one mode.
    //if ( *(MosUsbContext->ptrUserPhyMode) == USER_SET_AUTONEG )
    
    if(1)
    {
        
        if( WITHOUT_PHY == 0)
        {
            status=0x0;
            status=ReadPhyRegisterPassive(mcs,PHY_AUTONEGADVT_REG_INDEX,&mcs->PhyAutoNegAdvReg);
            DPRINTK("Status of ReadPhyRegisterPassive is %x\n",status);
            if(status >=0)
            {
                DPRINTK("ReadPhyRegisterPassive success\n");
                //break;
            }
            else
            {    
                DPRINTK("ReadPhyRegisterPassive fail\n");
                mcs7830_disconnect(mcs->udev, (void *) mcs);
                return -1;//NULL;
                
            }
        } // end of WITHOUT_PHY
        PrintAutoNegAdvRegister(mcs);
        //static int AutoNegChangemode(struct mcs7830_cb *mcs,int ptrUserPhyMode)
        status=0x0;
        status=AutoNegChangemode(mcs,0);//0x05e1
        if(status>=0)
            DPRINTK("Auto negotiation staarted\n");
        
        //DPRINTK("Phy Status Register Value: x%04x\n", mcs->PhyStatusReg);
        
    }
    else
    {
        
#ifdef comment
        if(interest==1)
            AutoNegChangemode(mcs,1);//0x05e1
        if(interest==2)
            AutoNegChangemode(mcs,2);//0x05e1
        if(interest==3)
            AutoNegChangemode(mcs,3);//0x05e1
        if(interest==4)
            AutoNegChangemode(mcs,4);//0x05e1
        
        
        
        
        
        DBGPRINT(DBG_COMP_USBVENDOR, DBG_LEVEL_ERR,
            ("Phy Status Register Value: x%04x\n", MosUsbContext->PhyStatusReg));
        
        //Updating Link Speed for 10Hdx, 10Fdx, 100Hdx and 100Fdx
        MosUsbContext->LinkSpeed =
            AutoNegAdvtReg & PHY_AUTONEGADVT_100TX ? 1000000:100000;
        
#endif
        
    } // end of else  (UserPhyMode)
    

    //Rev.C change
    if( WITHOUT_PHY == 0)
    {
        mcs->ptrFpgaVersion=PTR_FPGA_VERSION;
        if (mcs->ptrFpgaVersion != 0)
            mcs->HifReg15 |=  HIF_REG_15_CFG; //need to be checked!!!may be not needed in Linux driver
        
        // NOTE:!!!! neeed to read HifReg15 previous values!!!
        
        // enable Rx & Tx register
        mcs->HifReg15 |= (HIF_REG_15_TXENABLE| HIF_REG_15_RXENABLE );
        
        // Temp enable Multicast bit at initialization time. It should be done
        // when driver gets the packetfilter OID.
        mcs->HifReg15 |=  HIF_REG_15_ALLMULTICAST ;
        
        DPRINTK("HIF Register 15 %04x \n",mcs->HifReg15);
        
        
        // write these values to HIF
        status=0x0;
        status=SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
        if (status>=0)
        {
            DPRINTK (" MosUsbStartDevice() UpdateHifReg15() success\n");
        }
        else
        {
            DPRINTK("ERROR: MosUsbStartDevice() UpdateHifReg15() FAILURE (%x)\n", status);
            mcs7830_disconnect(mcs->udev, (void *) mcs);
            return -1;//NULL;    
        }
        
    }
    
    if(WITHOUT_PHY == 1)
    {
        //for with_out PHY mode we are directly initializing HIF_REG_15 with 0xF8.
        status = 0;
        mcs->HifReg15 = 0xF8;
        status=SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
        if (status>=0)
            DPRINTK (" writing HifReg15() 0xF8 is success\n");
        
    }    
    
    
    //Only if we are in 10 Half mode
    if( (!(mcs->HifReg15 & HIF_REG_15_FULLDUPLEX_ENABLE)) &&
        (!(mcs->HifReg15 & HIF_REG_15_SPEED100 )) )
    {
        // Set 40 in HIF register 9
        SingleByte = 40;
        SetVendorCommandPassive( mcs,HIF_REG_09,1,&SingleByte);
        
        // Set 32 in HIF register 10
        SingleByte = 32;
        SetVendorCommandPassive( mcs,HIF_REG_10,1,&SingleByte);
    }
    
    
    //#ifndef MCS7830_WHQL
    // for revision versions 'C' and above
    (mcs->ptrPauseThreshold) = PTR_PAUSE_THRESHOLD;
    if (mcs->DeviceReleaseNumber == DEVICE_REV_C)
    {
        DPRINTK("PauseThreshold = %d\n", mcs->ptrPauseThreshold) ;
        
        // Writing pause data to the device
        SetVendorCommandPassive( mcs,HIF_REG_23,1,&mcs->ptrPauseThreshold);
    }
    return status;
}

static int mdio_read (struct net_device *dev, int phy_id, int location)
{
    int status;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28)
    struct usbnet *mcs=dev->priv;
#else
    struct usbnet *mcs = netdev_priv(dev);
#endif
    __u16 PhyStatusRegValue,phyControlReg;
    status=0;
    mdelay(500);
    printk("mdio_read ==> \n");
    
    if( WITHOUT_PHY == 0)
    {
        if(mcs->PhyIdentity == INTEL_PHY)
            status=ReadPhyRegisterPassive(mcs,PHY_CHIPSTATUS_REG_INDEX,&phyControlReg);
        else if(mcs->PhyIdentity == FARADAY_PHY)
        {
            status=ReadPhyRegisterPassive(mcs,PHY_CONTROL_REG_INDEX,&phyControlReg);
        }
    }
    
    DPRINTK("mdio_read:location is %d\n",location);
    if(location==0x0)// ||(location==0x4))
    {
        
        if( WITHOUT_PHY== 1)
            return BMCR_SPEED100|ADVERTISE_100FULL|ADVERTISE_CSMA; //testing with hotcode
        
        if( WITHOUT_PHY ==0)
        {
            if(mcs->PhyIdentity == INTEL_PHY)
            {
                printk("%s 1 read reg is %x\n",__FUNCTION__,phyControlReg);
                if(phyControlReg & PHY_CHIPSTATUS_REG_SPEED100)//PHY_CONTROL_SPEED100)
                {
                    if( phyControlReg & PHY_CHIPSTATUS_REG_FULLDUPLEX_ENABLE)//PHY_CONTROL_FULLDUPLEX )
                    {
                        DPRINTK("mdio_read: Duplex id FullDuplex\n");
                        mcs->HifReg15 |= HIF_REG_15_FULLDUPLEX_ENABLE;
                        mcs->HifReg15 |= HIF_REG_15_SPEED100;
                        SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
                        printk("mdio_read <== return (INTEL) BMCR_SPEED100|ADVERTISE_100FULL|ADVERTISE_CSMA\n");
                        return BMCR_SPEED100|ADVERTISE_100FULL|ADVERTISE_CSMA;
                    }
                    else
                    {
                        DPRINTK("mdio_read: Duplex id HalfDuplex\n");
                        mcs->HifReg15 &= ~HIF_REG_15_FULLDUPLEX_ENABLE;
                        mcs->HifReg15 |= HIF_REG_15_SPEED100;
                        SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
                        printk("mdio_read <== return (INTEL) BMCR_SPEED100|ADVERTISE_100HALF|ADVERTISE_CSMA\n");
                        return BMCR_SPEED100|ADVERTISE_100HALF|ADVERTISE_CSMA;
                    }
                }
                else
                {
                    if( phyControlReg & PHY_CHIPSTATUS_REG_FULLDUPLEX_ENABLE)//PHY_CONTROL_FULLDUPLEX )
                    {
                        DPRINTK("mdio_read: Duplex id FullDuplex\n");
                        mcs->HifReg15 |= HIF_REG_15_FULLDUPLEX_ENABLE;
                        mcs->HifReg15 &= ~HIF_REG_15_SPEED100;
                        SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
                        printk("mdio_read <== return (INTEL) BMCR_FULLDPLX|ADVERTISE_10FULL|ADVERTISE_CSMA\n");
                        return BMCR_FULLDPLX|ADVERTISE_10FULL|ADVERTISE_CSMA;
                    }
                    else
                    {
                        DPRINTK("mdio_read: Duplex id HalfDuplex\n");
                        mcs->HifReg15 &= ~HIF_REG_15_FULLDUPLEX_ENABLE;
                        mcs->HifReg15 &= ~HIF_REG_15_SPEED100;
                        SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
                        printk("mdio_read <== return (INTEL) ADVERTISE_10HALF|ADVERTISE_CSMA\n");
                        return ADVERTISE_10HALF|ADVERTISE_CSMA;
                    }
                    
                }
                
            }
            else if(mcs->PhyIdentity == FARADAY_PHY)
            {
                
                if(phyControlReg & PHY_CONTROL_SPEED100)
                {
                    if( phyControlReg & PHY_CONTROL_FULLDUPLEX )
                    {
                        DPRINTK("mdio_read: Duplex id FullDuplex\n");
                        mcs->HifReg15 |= HIF_REG_15_FULLDUPLEX_ENABLE;
                        mcs->HifReg15 |= HIF_REG_15_SPEED100;
                        SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
                        printk("mdio_read <== return BMCR_SPEED100|ADVERTISE_100FULL|ADVERTISE_CSMA\n");
                        return BMCR_SPEED100|ADVERTISE_100FULL|ADVERTISE_CSMA;
                    }
                    else
                    {
                        DPRINTK("mdio_read: Duplex id HalfDuplex\n");
                        mcs->HifReg15 &= ~HIF_REG_15_FULLDUPLEX_ENABLE;
                        mcs->HifReg15 |= HIF_REG_15_SPEED100;
                        SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
                        printk("mdio_read <== return BMCR_SPEED100|ADVERTISE_100HALF|ADVERTISE_CSMA\n");
                        return BMCR_SPEED100|ADVERTISE_100HALF|ADVERTISE_CSMA;
                    }
                }
                else
                {
                    if( phyControlReg & PHY_CONTROL_FULLDUPLEX )
                    {
                        DPRINTK("mdio_read: Duplex id FullDuplex\n");
                        mcs->HifReg15 |= HIF_REG_15_FULLDUPLEX_ENABLE;
                        mcs->HifReg15 &= ~HIF_REG_15_SPEED100;
                        SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
                        printk("mdio_read <== return BMCR_FULLDPLX|ADVERTISE_10FULL|ADVERTISE_CSMA\n");
                        return BMCR_FULLDPLX|ADVERTISE_10FULL|ADVERTISE_CSMA;
                    }
                    else
                    {
                        DPRINTK("mdio_read: Duplex id HalfDuplex\n");
                        mcs->HifReg15 &= ~HIF_REG_15_FULLDUPLEX_ENABLE;
                        mcs->HifReg15 &= ~HIF_REG_15_SPEED100;
                        SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
                        printk("mdio_read <== return ADVERTISE_10HALF|ADVERTISE_CSMA\n");
                        return ADVERTISE_10HALF|ADVERTISE_CSMA;
                    }
                    
                }
                
            }
            
        }// END OF WITHOUT_PHY
      
    }
    
    
    if(location ==0x1)
    {
        if( WITHOUT_PHY == 1)
        { // just return LINKOK in without_phy mode
            DPRINTK("mdio_read: Link is OK\n");
            printk("mdio_read <== return 4 Link OK Without Phy\n");
            return LINKOK;
        }
        if( WITHOUT_PHY ==0)
        {    
            status=0;
            status=ReadPhyRegisterPassive(mcs,PHY_STATUS_REG_INDEX,&PhyStatusRegValue);
            if(PhyStatusRegValue & PHY_STATUS_REG_LINK_STATUS)
            {
                DPRINTK("mdio_read: Link is OK\n");
                printk("mdio_read <== return 4 Link OK\n");
                return LINKOK;
            }
            else
            {
                DPRINTK("mdio_read: Link no link\n");
                printk("mdio_read <== return 0 no Link\n");
                
                return 0;
            }
        }
    }
    printk("mdio_read <== END return 0\n");
    
    return 0;
    
}

static void mdio_write (struct net_device *dev, int phy_id, int location,
                        int val)
{
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28)
    struct usbnet *mcs=dev->priv;
#else
    struct usbnet *mcs = netdev_priv(dev);
#endif
    
    printk("mdio_write ==> \n");
    
    DPRINTK(" val:0x%x location: 0x%x\n",val,location);
    DPRINTK("\n");
    if(val & 0x1000)
    {
        
        DPRINTK("mdio_write: Trying  to set 100Full\n");
        AutoNegChangemode(mcs,0); //Auto Negotiation
        mcs->SpeedDuplex=0;
    }
    else
    {
        
        if(val & 0x2000) // 100 mbs or 10 mbs
        {
            if(val & 0x100)
            {
                //100 Full
                DPRINTK("mdio_write:  Trying  to set 100Full\n");
                AutoNegChangemode(mcs,4);
                
                mcs->ForceSpeed=100;
                mcs->ForceDuplex=1;
            }
            else
            {
                //100 Half
                DPRINTK(" mdio_write: Trying  to set 100Half\n");
                AutoNegChangemode(mcs,3);//0x05e1
                mcs->ForceSpeed=100;
                mcs->ForceDuplex=0;
            }
        }
        else
        {
            if(val & 0x100)
            {
                //10 Full
                DPRINTK("mdio_write: Trying  to set 10Full\n");
                AutoNegChangemode(mcs,2);//0x05e1
                mcs->ForceSpeed=10;
                mcs->ForceDuplex=1;
                
            }
            else
            {
                //10 Half
                DPRINTK("mdio_write: Trying  to set 10Half\n");
                AutoNegChangemode(mcs,1);//0x05e1
                mcs->ForceSpeed=10;
                mcs->ForceDuplex=0;
            }
            
        }
        mcs->SpeedDuplex=1;
    }
    printk("mdio_write ==> \n");
    return;
}



static void mcs7830_disconnect (struct usb_device *udev, void *ptr)
{
    struct usbnet *mcs = (struct usbnet *) ptr;

    if (mcs->net) 
    {
        unregister_netdev (mcs->net);
        mcs->net = NULL;
    }
    
    mcs->udev=NULL;
    DPRINTK(" USB device MCS7830 device is removed \n");
    usb_put_dev (udev);
    
}





/*-------------------------------------------------------------------------
*
* Network Device Driver (peer link to "Host Device", from USB host)
*
*-------------------------------------------------------------------------*/

static int usbnet_change_mtu (struct net_device *net, int new_mtu)
{
    struct usbnet    *dev = netdev_priv(net);
    
    if (new_mtu <= MIN_PACKET || new_mtu > MAX_PACKET)
        return -EINVAL;
    // no second zero-length packet read wanted after mtu-sized packets
    if (((new_mtu + sizeof (struct ethhdr)) % dev->maxpacket) == 0)
        return -EDOM;
    net->mtu = new_mtu;
    return 0;
}

/*-------------------------------------------------------------------------*/

static struct net_device_stats *usbnet_get_stats (struct net_device *net)
{
    struct usbnet    *dev = netdev_priv(net);
    return &dev->stats;
}

/*-------------------------------------------------------------------------*/

/* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from
* completion callbacks.  2.5 should have fixed those bugs...
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
static void defer_bh (struct usbnet *dev, struct sk_buff *skb)
{
    struct sk_buff_head    *list = skb->list;

#else
static void defer_bh (struct usbnet *dev, struct sk_buff *skb,struct sk_buff_head *list)
{
        
#endif
    unsigned long        flags;
        
    spin_lock_irqsave (&list->lock, flags);
    __skb_unlink (skb, list);
    spin_unlock (&list->lock);
    spin_lock (&dev->done.lock);
    __skb_queue_tail (&dev->done, skb);
    if (dev->done.qlen == 1)
        tasklet_schedule (&dev->bh);
    spin_unlock_irqrestore (&dev->done.lock, flags);
}
    
/* some work can't be done in tasklets, so we use keventd
 * NOTE:  annoying asymmetry:  if it's active, schedule_work() fails,
 * but tasklet_schedule() doesn't.  hope the failure is rare.
 */
static void defer_kevent (struct usbnet *dev, int work)
{
    set_bit (work, &dev->flags);
    if (!schedule_work (&dev->kevent))
        deverr (dev, "kevent %d may have been dropped", work);
    else
        devdbg (dev, "kevent %d scheduled", work);
}
    
    
static void rx_complete (struct urb *urb, struct pt_regs *regs);
    
static void rx_submit (struct usbnet *dev, struct urb *urb, int flags)
{
    struct sk_buff        *skb;
    struct skb_data        *entry;
    int            retval = 0;
    unsigned long        lockflags;
    size_t            size;
    //DPRINTK(" in the function rx_submit start\n");
    //size = (sizeof (struct ethhdr) + dev->net->mtu); Old Line 
    size = (sizeof (struct ethhdr) + dev->net->mtu+1);
    //DPRINTK("in the function rx_submit submited size is %d\n",size);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
    if ((skb = alloc_skb (size, flags)) == NULL) {
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
    if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
#endif
        if (netif_msg_rx_err (dev))
            devdbg (dev, "no rx skb");
        defer_kevent (dev, EVENT_RX_MEMORY);
        usb_free_urb (urb);
        return;
    }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
    skb_reserve (skb, NET_IP_ALIGN);
#endif
            
    entry = (struct skb_data *) skb->cb;
    entry->urb = urb;
    entry->dev = dev;
    entry->state = rx_start;
    entry->length = 0;
            
    usb_fill_bulk_urb (urb, dev->udev, dev->in,
        skb->data, size, (usb_complete_t)rx_complete, skb);

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
    urb->transfer_flags |= URB_ASYNC_UNLINK;
#endif
            
    spin_lock_irqsave (&dev->rxq.lock, lockflags);
            
    if (netif_running (dev->net)
        && netif_device_present (dev->net)
        && !test_bit (EVENT_RX_HALT, &dev->flags)) {
        switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){ 
        case -EPIPE:
            defer_kevent (dev, EVENT_RX_HALT);
            break;
        case -ENOMEM:
            defer_kevent (dev, EVENT_RX_MEMORY);
            break;
        case -ENODEV:
            if (netif_msg_ifdown (dev))
                devdbg (dev, "device gone");
            netif_device_detach (dev->net);
            break;
        default:
            if (netif_msg_rx_err (dev))
                devdbg (dev, "rx submit, %d", retval);
            tasklet_schedule (&dev->bh);
            break;
        case 0:
            __skb_queue_tail (&dev->rxq, skb);
        }
    } else {
        if (netif_msg_ifdown (dev))
            devdbg (dev, "rx: stopped");
        retval = -ENOLINK;
    }
    spin_unlock_irqrestore (&dev->rxq.lock, lockflags);
    if (retval) {
        dev_kfree_skb_any (skb);
        usb_free_urb (urb);
    }
}
        
static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
{
    //DPRINTK("in the function rx_process \n");
    if (dev->driver_info->rx_fixup
        && !dev->driver_info->rx_fixup (dev, skb))
        goto error;
    // else network stack removes extra byte if we forced a short packet
    
    if (skb->len)
        skb_return (dev, skb);
    else {
        if (netif_msg_rx_err (dev))
            devdbg (dev, "drop");
error:
        dev->stats.rx_errors++;
        skb_queue_tail (&dev->done, skb);
    }
}
        
        
static void rx_complete (struct urb *urb, struct pt_regs *regs)
{
    struct sk_buff        *skb = (struct sk_buff *) urb->context;
    struct skb_data        *entry = (struct skb_data *) skb->cb;
    struct usbnet        *dev = entry->dev;
    int            urb_status = urb->status;
    //DPRINTK("in the function rx_complete \n");
    skb_put (skb, urb->actual_length);
    entry->state = rx_done;
    entry->urb = NULL;
    
    switch (urb_status) {
        // success
    case 0:
        if (MIN_PACKET > skb->len || skb->len > MAX_PACKET) {
            entry->state = rx_cleanup;
            dev->stats.rx_errors++;
            dev->stats.rx_length_errors++;
            if (netif_msg_rx_err (dev))
                devdbg (dev, "rx length %d", skb->len);
        }
        break;
        
        // stalls need manual reset. this is rare ... except that
        // when going through USB 2.0 TTs, unplug appears this way.
        // we avoid the highspeed version of the ETIMEOUT/EILSEQ
        // storm, recovering as needed.
    case -EPIPE:
        dev->stats.rx_errors++;
        defer_kevent (dev, EVENT_RX_HALT);
        // FALLTHROUGH
        
        // software-driven interface shutdown
    case -ECONNRESET:        // async unlink
    case -ESHUTDOWN:        // hardware gone
        if (netif_msg_ifdown (dev))
            devdbg (dev, "rx shutdown, code %d", urb_status);
        goto block;
        
        // we get controller i/o faults during khubd disconnect() delays.
        // throttle down resubmits, to avoid log floods; just temporarily,
        // so we still recover when the fault isn't a khubd delay.
    case -EPROTO:        // ehci
    case -ETIMEDOUT:        // ohci
    case -EILSEQ:        // uhci
        dev->stats.rx_errors++;
        if (!timer_pending (&dev->delay)) {
            mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
            if (netif_msg_link (dev))
                devdbg (dev, "rx throttle %d", urb_status);
        }
block:
        entry->state = rx_cleanup;
        entry->urb = urb;
        urb = NULL;
        break;
        
        // data overrun ... flush fifo?
    case -EOVERFLOW:
        dev->stats.rx_over_errors++;
        // FALLTHROUGH
        
    default:
        entry->state = rx_cleanup;
        dev->stats.rx_errors++;
        if (netif_msg_rx_err (dev))
            devdbg (dev, "rx status %d", urb_status);
        break;
    }
    
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
    defer_bh (dev, skb);
#else
    defer_bh (dev, skb,&dev->rxq);
#endif
    
    if (urb) {
        if (netif_running (dev->net)
            && !test_bit (EVENT_RX_HALT, &dev->flags)) {
            rx_submit (dev, urb, GFP_ATOMIC);
            return;
        }
        usb_free_urb (urb);
    }
    if (netif_msg_rx_err (dev))
        devdbg (dev, "no read resubmitted");
}
        

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
static void intr_complete (struct urb *urb, struct pt_regs *regs)
#else
static void intr_complete (struct urb *urb )
#endif
{
    struct usbnet    *dev = urb->context;
    int        status = urb->status;
    printk("intr_complete %d\n",status);
    switch (status) {
        /* success */
    case 0:
        dev->driver_info->status(dev, urb);
        break;
        
        /* software-driven interface shutdown */
    case -ENOENT:        // urb killed
    case -ESHUTDOWN:        // hardware gone
        if (netif_msg_ifdown (dev))
            devdbg (dev, "intr shutdown, code %d", status);
        return;
        
        /* NOTE:  not throttling like RX/TX, since this endpoint
        * already polls infrequently
        */
    default:
        devdbg (dev, "intr status %d", status);
        break;
    }
    
    if (!netif_running (dev->net))
        return;
    
    memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
    status = usb_submit_urb (urb, GFP_ATOMIC);
    if (status != 0 && netif_msg_timer (dev))
        deverr(dev, "intr resubmit --> %d", status);
}
        
        
        // unlink pending rx/tx; completion handlers do all other cleanup
        
static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
{
    unsigned long        flags;
    struct sk_buff        *skb, *skbnext;
    int            count = 0;
    printk("unlink_urbs\n");
    //system("ifup eth4");    
    spin_lock_irqsave (&q->lock, flags);
    for (skb = q->next; skb != (struct sk_buff *) q; skb = skbnext) {
        struct skb_data        *entry;
        struct urb        *urb;
        int            retval;
        
        entry = (struct skb_data *) skb->cb;
        urb = entry->urb;
        skbnext = skb->next;
        //printk("for_unlink_urbs\n");
        // during some PM-driven resume scenarios,
        // these (async) unlinks complete immediately
        retval = usb_unlink_urb (urb);
        if (retval != -EINPROGRESS && retval != 0)
            devdbg (dev, "unlink urb err, %d", retval);
        else
            count++;
    }
    spin_unlock_irqrestore (&q->lock, flags);
    return count;
}


/*-------------------------------------------------------------------------*/

// precondition: never called in_interrupt

static int usbnet_stop (struct net_device *net)
{
    struct usbnet        *dev = netdev_priv(net);
    int            temp;
    DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); 
    DECLARE_WAITQUEUE (wait, current);
    printk("START of the function usbnet_stop\n");
    DPRINTK("netif_stop_queue in the function usbnet_stop\n");
    netif_stop_queue (net);
    
    if (netif_msg_ifdown (dev))
        devinfo (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld",
        dev->stats.rx_packets, dev->stats.tx_packets, 
        dev->stats.rx_errors, dev->stats.tx_errors
        );
    printk("stop stats: rx/tx %ld/%ld, errs %ld/%ld",
        dev->stats.rx_packets, dev->stats.tx_packets,
        dev->stats.rx_errors, dev->stats.tx_errors
        );
    
    // ensure there are no more active urbs
    add_wait_queue (&unlink_wakeup, &wait);
    dev->wait = &unlink_wakeup;
    temp = unlink_urbs (dev, &dev->txq) + unlink_urbs (dev, &dev->rxq);
    
    // maybe wait for deletions to finish.
    while (skb_queue_len (&dev->rxq)
        && skb_queue_len (&dev->txq)
        && skb_queue_len (&dev->done)) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
        msleep(UNLINK_TIMEOUT_MS);
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
        set_current_state (TASK_UNINTERRUPTIBLE);
        schedule_timeout (UNLINK_TIMEOUT_JIFFIES);
        
#endif
        if (netif_msg_ifdown (dev))
            devdbg (dev, "waited for %d urb completions", temp);
    }
    dev->wait = NULL;
    remove_wait_queue (&unlink_wakeup, &wait); 
    
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
    usb_kill_urb(dev->interrupt);
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
    usb_unlink_urb(dev->interrupt);
#endif
    // deferred work (task, timer, softirq) must also stop.
    // can't flush_scheduled_work() until we drop rtnl (later),
    // else workers could deadlock; so make workers a NOP.
    
    dev->flags = 0;
    del_timer_sync (&dev->delay);
    tasklet_kill (&dev->bh);
    DPRINTK("END of the function usbnet_stop\n");
    
    return 0;
    
}
        
        
// posts reads, and enables write queuing
// precondition: never called in_interrupt
        
static int usbnet_open (struct net_device *net)
{
    struct usbnet        *dev = netdev_priv(net);
    int            retval = 0;
    struct driver_info    *info = dev->driver_info;
    printk("START of the function usbnet_open\n");
    // put into "known safe" state
    if (info->reset && (retval = info->reset (dev)) < 0) {
        printk("open_reset\n");
        if (netif_msg_ifup (dev)){
            printk("open_msg_ifup\n");
            devinfo (dev,
                "open reset fail (%d) usbnet usb-%s-%s, %s",
                retval,
                dev->udev->bus->bus_name, dev->udev->devpath,
                info->description);}
        goto done;
    }
    
    // insist peer be connected
    if (info->check_connect && (retval = info->check_connect (dev)) < 0) {
        printk("open_check_connect\n");
        if (netif_msg_ifup (dev)){
            printk("open_ifup\n");
            devdbg (dev, "can't open; %d", retval);}
        goto done;
    }
    
    /* start any status interrupt transfer */
    if (dev->interrupt) {
        printk("open_interrupt\n");
        retval = usb_submit_urb (dev->interrupt, GFP_KERNEL);
        if (retval < 0) {
            printk("open_retval<0\n");
            if (netif_msg_ifup (dev))
                deverr (dev, "intr submit %d", retval);
            goto done;
        }
    }
    
    netif_start_queue (net);
    if (netif_msg_ifup (dev)) {
        printk("open_netif_framming\n");
        char    *framing;
        
        framing = "simple";
        
        devinfo (dev, "open: enable queueing "
            "(rx %d, tx %d) mtu %d %s framing",
            RX_QLEN (dev), TX_QLEN (dev), dev->net->mtu,
            framing);
    }
    
    // delay posting reads until we're fully open
    tasklet_schedule (&dev->bh);
done:
    printk("END of the function usbnet_open\n");
    return retval;
}
        
#ifdef check
static void usbnet_set_rx_mode (struct net_device *dev)
{
    struct usbnet *mcs = dev->priv;
    int status;
    __u8 ReadHifVal;    
    DPRINTK("%s: usbnet_set_rx_mode(%4.4x) done\n", dev->name, dev->flags);
    // Setting the board to IFF_ALLMULTI 
    
    if (dev->flags & IFF_ALLMULTI)
    {
        mcs->HifReg15 |= HIF_REG_15_ALLMULTICAST;
        //enableMulticastBit(tp);
        
        DPRINTK("usbnet_set_rx_mode:%s: All Multicast mode enabled.\n",dev->name);
    }
    else
    {
        mcs->HifReg15 &= ~HIF_REG_15_ALLMULTICAST;
        
        //disableMulticastBit(tp);
        
        DPRINTK("usbnet_set_rx_mode:%s: All Multicast mode disabled.\n",dev->name);
    }
    // Setting the board to IFF_PROMISC 
    if (dev->flags & IFF_PROMISC)
    {
        mcs->HifReg15 |= HIF_REG_15_PROMISCIOUS;
        //enablePromiscousBit(tp);
        
        DPRINTK("usbnet_set_rx_mode %s: Promiscuous mode enabled.\n",dev->name);
    }
    else
    {
        mcs->HifReg15 &= ~HIF_REG_15_PROMISCIOUS;
        //disablePromiscousBit(tp);
        
        DPRINTK("usbnet_set_rx_mode:%s: Promiscuous mode disabled.\n",dev->name);
    }
    
    status=0x0;
    status=SetVendorCommandPassive( mcs,HIF_REG_15,1,&mcs->HifReg15);
    DPRINTK("in usbnet_set_rx_mode  Writing mcs->HifReg15 is %04x \n",mcs->HifReg15 );
    if(status >=0)
    {
        DPRINTK("in usbnet_set_rx_mode  writing mcs->HifReg15 SUCCESS\n" );
        //break;
    }
    else
    {
        DPRINTK("in usbnet_set_rx_mode  writing mcs->HifReg15 FAILED\n");
        mcs7830_disconnect(mcs->udev, (void *) mcs);
    }
    status=0;
    status=GetVendorCommandPassive( mcs,HIF_REG_15,1,&ReadHifVal);
    DPRINTK("in usbnet_set_rx_mode  Read mcs->HifReg15 is %04x \n",ReadHifVal );
    
    
    
    
}

#endif
        
        
        
static void moschip_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
{
    printk("moschip_get_wol\n");
}

static void moschip_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
{
    printk("moschip_set_wol\n");
}

static int moschip_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
{
    struct usbnet *dev = netdev_priv(net);
    printk("moschip_get_settings \n");
    return mii_ethtool_gset(&dev->mii,cmd);
}

static int moschip_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
{
    struct usbnet *dev = netdev_priv(net);
    printk("moschip_set_settings \n");
    return mii_ethtool_sset(&dev->mii,cmd);
}


static void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
{
    struct usbnet *dev = netdev_priv(net);
    
    printk("usbnet_get_drvinfo \n");
    strncpy (info->driver, driver_name, sizeof info->driver);
    strncpy (info->version, DRIVER_VERSION, sizeof info->version);
    strncpy (info->fw_version, dev->driver_info->description,
        sizeof info->fw_version);
    usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info);
}
        
static u32 usbnet_get_link (struct net_device *net)
{
    struct usbnet *dev = netdev_priv(net);
    
    printk("usbnet_get_link ==> \n");
    /* If a check_connect is defined, return it's results */
    if (dev->driver_info->check_connect)
    {
        int value = 0;
        value = dev->driver_info->check_connect (dev) == 0;
        printk("usbnet_get_link->check_connect returning %d  \n", value);
        return value;
    }
    
    /* Otherwise, we're up to avoid breaking scripts */
    printk("usbnet_get_link <== returning 1 \n");
    return 1;
}
        
static u32 usbnet_get_msglevel (struct net_device *net)
{
    struct usbnet *dev = netdev_priv(net);
    printk("usbnet_get_msglevel \n");
    
    return dev->msg_enable;
}

static void usbnet_set_msglevel (struct net_device *net, u32 level)
{
    struct usbnet *dev = netdev_priv(net);
    printk("usbnet_get_msglevel \n");
    dev->msg_enable = level;
}
        
static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
{
#ifdef NEED_MII
    {
        struct usbnet *dev = netdev_priv(net);
        
        
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
        if (dev->mii.mdio_read != NULL && dev->mii.mdio_write != NULL)
            return generic_mii_ioctl(&dev->mii,
            (struct mii_ioctl_data *) &rq->ifr_data,
            cmd, NULL);
        
#endif
        
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
        if (dev->mii.mdio_read != NULL && dev->mii.mdio_write != NULL)
            return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
#endif
    }
#endif
    return -EOPNOTSUPP;
}
        
        
/* work that cannot be done in interrupt context uses keventd.
 *
 * NOTE:  with 2.5 we could do more of this using completion callbacks,
 * especially now that control transfers can be queued.
 */
static void kevent (void *data)
{
    struct usbnet        *dev = data;
    int            status;
    
    /* usb_clear_halt() needs a thread context */
    if (test_bit (EVENT_TX_HALT, &dev->flags)) {
        unlink_urbs (dev, &dev->txq);
        status = usb_clear_halt (dev->udev, dev->out);
        if (status < 0 && status != -EPIPE) {
            if (netif_msg_tx_err (dev))
                deverr (dev, "can't clear tx halt, status %d",
                status);
        } else {
            clear_bit (EVENT_TX_HALT, &dev->flags);
            netif_wake_queue (dev->net);
        }
    }
    if (test_bit (EVENT_RX_HALT, &dev->flags)) {
        unlink_urbs (dev, &dev->rxq);
        status = usb_clear_halt (dev->udev, dev->in);
        if (status < 0 && status != -EPIPE) {
            if (netif_msg_rx_err (dev))
                deverr (dev, "can't clear rx halt, status %d",
                status);
        } else {
            clear_bit (EVENT_RX_HALT, &dev->flags);
            tasklet_schedule (&dev->bh);
        }
    }
    
    /* tasklet could resubmit itself forever if memory is tight */
    if (test_bit (EVENT_RX_MEMORY, &dev->flags)) {
        struct urb    *urb = NULL;
        
        if (netif_running (dev->net))
            urb = usb_alloc_urb (0, GFP_KERNEL);
        else
            clear_bit (EVENT_RX_MEMORY, &dev->flags);
        if (urb != NULL) {
            clear_bit (EVENT_RX_MEMORY, &dev->flags);
            rx_submit (dev, urb, GFP_KERNEL);
            tasklet_schedule (&dev->bh);
        }
    }
    
    if (test_bit (EVENT_LINK_RESET, &dev->flags)) {
        struct driver_info     *info = dev->driver_info;
        int            retval = 0;
        
        clear_bit (EVENT_LINK_RESET, &dev->flags);
        if(info->link_reset && (retval = info->link_reset(dev)) < 0) {
            devinfo(dev, "link reset failed (%d) usbnet usb-%s-%s, %s",
                retval,
                dev->udev->bus->bus_name, dev->udev->devpath,
                info->description);
        }
    }
    
    if (dev->flags)
        devdbg (dev, "kevent done, flags = 0x%lx",
        dev->flags);
}
        
// Call back for zero length packet transfer
static void Zero_complete(struct urb *urb,struct pt_regs *regs)
{
    usb_free_urb (urb);
}

static void tx_complete (struct urb *urb, struct pt_regs *regs)
{
    struct sk_buff        *skb = (struct sk_buff *) urb->context;
    struct skb_data        *entry = (struct skb_data *) skb->cb;
    struct usbnet        *dev = entry->dev;
    //DPRINTK("in the function tx_complete start \n");
    if (urb->status == 0) {
        dev->stats.tx_packets++;
        dev->stats.tx_bytes += entry->length;
    } else {
        dev->stats.tx_errors++;
        
        switch (urb->status) {
        case -EPIPE:
            defer_kevent (dev, EVENT_TX_HALT);
            break;
            
            /* software-driven interface shutdown */
        case -ECONNRESET:        // async unlink
        case -ESHUTDOWN:        // hardware gone
            break;
            
            // like rx, tx gets controller i/o faults during khubd delays
            // and so it uses the same throttling mechanism.
        case -EPROTO:        // ehci
        case -ETIMEDOUT:    // ohci
        case -EILSEQ:        // uhci
            if (!timer_pending (&dev->delay)) {
                mod_timer (&dev->delay,
                    jiffies + THROTTLE_JIFFIES);
                if (netif_msg_link (dev))
                    devdbg (dev, "tx throttle %d",
                    urb->status);
            }
            DPRINTK("netif_stop_queue in the function tx_complete at -EILSEQ\n");
            netif_stop_queue (dev->net);
            break;
        default:
            if (netif_msg_tx_err (dev))
                devdbg (dev, "tx err %d", entry->urb->status);
            break;
        }
    }
    
    urb->dev = NULL;
    entry->state = tx_done;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
    defer_bh (dev, skb);
#else
    defer_bh (dev, skb,&dev->txq);
#endif
}
        
        
static void usbnet_tx_timeout (struct net_device *net)
{
    struct usbnet        *dev = netdev_priv(net);
    
    unlink_urbs (dev, &dev->txq);
    tasklet_schedule (&dev->bh);
    
    // FIXME: device recovery -- reset?
}
        

static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
{
    struct usbnet        *dev = netdev_priv(net);
    int            length;
    int            retval = NET_XMIT_SUCCESS;
    struct urb        *urb = NULL,*Zerourb=NULL;
    struct skb_data        *entry;
    struct driver_info    *info = dev->driver_info;
    unsigned long        flags;
    int NeedtoSendZero=0,retval2;//added for handling 
    //max size packets for USB
    
    
    //DPRINTK(" in usbnet_start_xmit Start\n");
    // some devices want funky USB-level framing, for
    // win32 driver (usually) and/or hardware quirks
    if (info->tx_fixup) {
        skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
        if (!skb) {
            if (netif_msg_tx_err (dev))
                devdbg (dev, "can't tx_fixup skb");
            goto drop;
        }
    }
    length = skb->len;
    
    if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
        if (netif_msg_tx_err (dev))
            devdbg (dev, "no urb");
        goto drop;
    }
    
    entry = (struct skb_data *) skb->cb;
    entry->urb = urb;
    entry->dev = dev;
    entry->state = tx_start;
    entry->length = length;
    
    // FIXME: reorganize a bit, so that fixup() fills out NetChip
    // framing too. (Packet ID update needs the spinlock...)
    // [ BETTER:  we already own net->xmit_lock, that's enough ]
    
    
    usb_fill_bulk_urb (urb, dev->udev, dev->out,
        skb->data, skb->len,(usb_complete_t) tx_complete, skb);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
    urb->transfer_flags |= URB_ASYNC_UNLINK;
#endif
    
    /* don't assume the hardware handles USB_ZERO_PACKET
    * NOTE:  strictly conforming cdc-ether devices should expect
    * the ZLP here, but ignore the one-byte packet.
    *
    * FIXME zero that byte, if it doesn't require a new skb.
    */
    if ((length % dev->maxpacket) == 0){
        NeedtoSendZero=1;//added by 
        urb->transfer_buffer_length++;
    }
    
    spin_lock_irqsave (&dev->txq.lock, flags);
    
    
    switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
    case -EPIPE:
        DPRINTK("netif_stop_queue in the function usbnet_start_xmit at EPIPE\n");
        netif_stop_queue (net);
        defer_kevent (dev, EVENT_TX_HALT);
        break;
    default:
        if (netif_msg_tx_err (dev))
            devdbg (dev, "tx: submit urb err %d", retval);
        break;
    case 0:
        net->trans_start = jiffies;
        __skb_queue_tail (&dev->txq, skb);
        if (dev->txq.qlen >= TX_QLEN (dev)){
            
            DPRINTK("netif_stop_queue in the function usbnet_start_xmit at case0\n");
            netif_stop_queue (net);
        }
    }
    spin_unlock_irqrestore (&dev->txq.lock, flags);
    
    if(NeedtoSendZero ==1)
    {    
        
        if (!(Zerourb = usb_alloc_urb (0, GFP_ATOMIC))) {
            if (netif_msg_tx_err (dev))
                devdbg (dev, "no urb");
            goto drop;
        }
        usb_fill_bulk_urb (Zerourb, dev->udev, dev->out,\
            skb->data, 0,(usb_complete_t)Zero_complete, NULL);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
        Zerourb->transfer_flags |= URB_ASYNC_UNLINK;
#endif
        
        retval2 = usb_submit_urb (urb, GFP_ATOMIC); 
        if(retval2 !=0)
            DPRINTK("Error sending Zero Length packet in transmission\n");
        
    }
    
    
    if (retval) {
        if (netif_msg_tx_err (dev))
            devdbg (dev, "drop, code %d", retval);
drop:
        retval = NET_XMIT_SUCCESS;
        dev->stats.tx_dropped++;
        if (skb)
            dev_kfree_skb_any (skb);
        usb_free_urb (urb);
    } else if (netif_msg_tx_queued (dev)) {
        devdbg (dev, "> tx, len %d, type 0x%x",
            length, skb->protocol);
    }
    return retval;
}



// tasklet (work deferred from completions, in_irq) or timer
static void usbnet_bh (unsigned long param)
{
    struct usbnet        *dev = (struct usbnet *) param;
    struct sk_buff        *skb;
    struct skb_data        *entry;
    
    while ((skb = skb_dequeue (&dev->done))) {
        entry = (struct skb_data *) skb->cb;
        switch (entry->state) {
        case rx_done:
            entry->state = rx_cleanup;
            rx_process (dev, skb);
            continue;
        case tx_done:
        case rx_cleanup:
            usb_free_urb (entry->urb);
            dev_kfree_skb (skb);
            continue;
        default:
            devdbg (dev, "bogus skb state %d", entry->state);
        }
    }
    
    // waiting for all pending urbs to complete?
    if (dev->wait) {
        if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) {
            wake_up (dev->wait);
        }
        
        // or are we maybe short a few urbs?
    } else if (netif_running (dev->net)
        && netif_device_present (dev->net)
        && !timer_pending (&dev->delay)
        && !test_bit (EVENT_RX_HALT, &dev->flags)) {
        int    temp = dev->rxq.qlen;
        int    qlen = RX_QLEN (dev);
        
        if (temp < qlen) {
            struct urb    *urb;
            int        i;
            
            // don't refill the queue all at once
            for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {
                urb = usb_alloc_urb (0, GFP_ATOMIC);
                if (urb != NULL)
                    rx_submit (dev, urb, GFP_ATOMIC);
            }
            if (temp != dev->rxq.qlen && netif_msg_link (dev))
                devdbg (dev, "rxqlen %d --> %d",
                temp, dev->rxq.qlen);
            if (dev->rxq.qlen < qlen)
                tasklet_schedule (&dev->bh);
        }
        if (dev->txq.qlen < TX_QLEN (dev))
            netif_wake_queue (dev->net);
    }
}



/*-------------------------------------------------------------------------
*
* USB Device Driver support
*
*-------------------------------------------------------------------------*/

// precondition: never called in_interrupt

static void usbnet_disconnect (struct usb_interface *intf)
{
    struct usbnet        *dev;
    struct usb_device    *xdev;
    struct net_device    *net;
    
    dev = usb_get_intfdata(intf);
    usb_set_intfdata(intf, NULL);
    
    printk("usbnet_disconnect ==>\n");
    if (!dev)
        return;
    printk(KERN_DEBUG "moschip: In %s...\n", __FUNCTION__);
    dev->linkStatTh.terminate = 1;
    
    kthread_stop(dev->linkStatTh.linkTask);
    wait_for_completion(&linkStatTh_exit);
    
    
    
    xdev = interface_to_usbdev (intf);
    
    if (netif_msg_probe (dev))
        devinfo (dev, "unregister usbnet usb-%s-%s, %s",
        xdev->bus->bus_name, xdev->devpath,
        dev->driver_info->description);
    
    net = dev->net;
    unregister_netdev (net);
    
    /* we don't hold rtnl here ... */
    flush_scheduled_work ();
    
    if (dev->driver_info->unbind)
        dev->driver_info->unbind (dev, intf);
    
    free_netdev(net);
    usb_put_dev (xdev);
    printk("usbnet_disconnect <==\n");
    
}


/*---------------------------------------------------------*/

static struct ethtool_ops usbnet_ethtool_ops;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
static struct net_device_ops mcs7830_netdev_ops;
#endif
// precondition: never called in_interrupt

static int
usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
{
    struct usbnet            *dev;
    struct net_device         *net;
    struct usb_host_interface    *interface;
    struct driver_info        *info;
    struct usb_device        *xdev;
    int                status;
    int                             i;
    info = (struct driver_info *) prod->driver_info;
    
    printk("usbnet_probe ==> \n"); 
    if (!info) {
        dev_dbg (&udev->dev, "blacklisted by %s\n", driver_name);
        printk("usbnet_probe <== return ENODEV\n"); 
        return -ENODEV;
    }
    xdev = interface_to_usbdev (udev);
    interface = udev->cur_altsetting;
    
    usb_get_dev (xdev);
    
    status = -ENOMEM;
    
    // set up our own records
    net = alloc_etherdev(sizeof(*dev));
    if (!net) {
        dbg ("can't kmalloc dev");
        goto out;
    }
    
    dev = netdev_priv(net);
    dev->udev = xdev;
    dev->driver_info = info;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
    dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
        | NETIF_MSG_PROBE | NETIF_MSG_LINK);
#endif
    
#ifdef CONFIG_USB_MOSCHIP
    //        if(udev->descriptor.idVendor == 0x9710 &&  udev->descriptor.idProduct == 0x7830 )
    
    if((dev)->udev->speed == USB_SPEED_HIGH) 
        DPRINTK("device is USB2.0\n"); 
    else
        DPRINTK("device is USB1.1\n");
    
    {
        usb_get_dev (xdev);
        DPRINTK(" USB device MCS7830 device is attached \n");
    }
    status=0;
    status=ANGInitializeDev(dev);
    if(status >=0)
        DPRINTK("Device initialization is comepleted\n");
    else
    {
        DPRINTK("ERROR: device initialization  FAILURE (%x)\n", status);
        mcs7830_disconnect(dev->udev, (void *) dev);
        printk("usbnet_probe <== return -1 initilisation failed\n"); 
        return -1;//NULL;
    }
    
#endif
    skb_queue_head_init (&dev->rxq);
    skb_queue_head_init (&dev->txq);
    skb_queue_head_init (&dev->done);
    dev->bh.func = usbnet_bh;
    dev->bh.data = (unsigned long) dev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
    INIT_WORK (&dev->kevent, kevent);
#else
    INIT_WORK (&dev->kevent, kevent, dev);
#endif
    dev->delay.function = usbnet_bh;
    dev->delay.data = (unsigned long) dev;
    init_timer (&dev->delay);
    
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
    SET_MODULE_OWNER(net);
#endif
    dev->net = net;
    //strcpy (net->name, "usb%d");
    strcpy (net->name, "eth%d");
    memcpy (net->dev_addr, node_id, sizeof node_id);
    for (i = 0; i < 6; i++) // Hardware Address
    {
        net->dev_addr[i] = dev->AiPermanentNodeAddress[i];
        net->broadcast[i] = 0xff;
    } 
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
    net->netdev_ops = &mcs7830_netdev_ops;
    net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
#else
    net->change_mtu = usbnet_change_mtu;
    net->get_stats = usbnet_get_stats;
    net->hard_start_xmit = usbnet_start_xmit;
    net->open = usbnet_open;
    net->stop = usbnet_stop;
    net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
    net->tx_timeout = usbnet_tx_timeout;
    net->do_ioctl = usbnet_ioctl;
#endif
    net->ethtool_ops = &usbnet_ethtool_ops;
    //    net->set_multicast_list = usbnet_set_rx_mode;
    
    dev->mii.dev = net;
    dev->mii.mdio_read = mdio_read;
    dev->mii.mdio_write = mdio_write;
    dev->mii.phy_id_mask = 0x3f;
    dev->mii.reg_num_mask = 0x1f;
    dev->mii.phy_id = *((u8 *)dev->AiPermanentNodeAddress + 1);
    
    
    // allow device-specific bind/init procedures
    // NOTE net->name still not usable ...
    if (info->bind) {
        status = info->bind (dev, udev);
        // heuristic:  "usb%d" for links we know are two-host,
        // else "eth%d" when there's reasonable doubt.  userspace
        // can rename the link if it knows better.
        if ((dev->driver_info->flags & FLAG_ETHER) != 0
            && (net->dev_addr [0] & 0x02) == 0)
            //strcpy (net->name, "usb%d");
            strcpy (net->name, "eth%d");
    } else if (!info->in || info->out)
        status = get_endpoints (dev, udev);
    else {
        dev->in = usb_rcvbulkpipe (xdev, info->in);
        dev->out = usb_sndbulkpipe (xdev, info->out);
        if (!(info->flags & FLAG_NO_SETINT))
            status = usb_set_interface (xdev,
            interface->desc.bInterfaceNumber,
            interface->desc.bAlternateSetting);
        else
            status = 0;
        
    }

    if (status < 0)
        goto out1;

    
    /* Comment the following code to stop intr ep polling 
	 * as we dont get the link status from the intr ep.
	 * we use a thread which reads the status register to find 
	 * link status.
	 */
	
	//if (status == 0 && dev->status)
    //    status = init_status (dev, udev);

    
    
    /* spawn a link status thread */
    spin_lock_init(&dev->linkStatTh.lock);
    dev->linkStatTh.terminate = 0;
    /*kernel_thread(update_linkStat_thread, dev,
    *                         CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);*/
    dev->linkStatTh.linkTask = kthread_run(update_linkStat_thread, dev,
        "%s", "update_linkStat_thread");
    
    
    dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
    // dev->status_pid = kernel_thread(mos_status_test, dev,0);    
    SET_NETDEV_DEV(net, &udev->dev);
    status = register_netdev (net);
    if (status)
        goto out3;
    if (netif_msg_probe (dev))
        devinfo (dev, "register usbnet at usb-%s-%s, %s, "
        "%02x:%02x:%02x:%02x:%02x:%02x",
        xdev->bus->bus_name, xdev->devpath,
        dev->driver_info->description,
        net->dev_addr [0], net->dev_addr [1],
        net->dev_addr [2], net->dev_addr [3],
        net->dev_addr [4], net->dev_addr [5]);
    
    // ok, it's ready to go.
    usb_set_intfdata (udev, dev);
    
    // start as if the link is up
    netif_device_attach (net);
    
    printk("usbnet_probe <== success\n"); 
    return 0;
    
out3:
    if (info->unbind)
        info->unbind (dev, udev);
out1:
    free_netdev(net);
out:
    usb_put_dev(xdev);
    printk("usbnet_probe <== failed status = %x\n", status); 
    return status;
}





/*-------------------------------------------------------------------------*/

#ifdef    CONFIG_PM

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
static int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
{
    struct usbnet        *dev = usb_get_intfdata(intf);
    printk("suspend ==>\n");    
    /* accelerate emptying of the rx and queues, to avoid
    * having everything error out.
    */
    netif_device_detach (dev->net);
    (void) unlink_urbs (dev, &dev->rxq);
    (void) unlink_urbs (dev, &dev->txq);
    intf->dev.power.power_state = PMSG_SUSPEND;
    printk("suspend <==\n");    
    
    return 0;
}

static int usbnet_resume (struct usb_interface *intf)
{
    struct usbnet        *dev = usb_get_intfdata(intf);
    printk("resume ==>\n");
    intf->dev.power.power_state = PMSG_ON;
    netif_device_attach (dev->net);
    tasklet_schedule (&dev->bh);
    printk("resume <==\n");
    return 0;
}
#endif
#else    /* !CONFIG_PM */

#define    usbnet_suspend    NULL
#define    usbnet_resume    NULL

#endif    /* CONFIG_PM */

/*-------------------------------------------------------------------------*/

/*
 * chip vendor names won't normally be on the cables, and
 * may not be on the device.
 */

static const struct usb_device_id    products [] = {
    
#ifdef  CONFIG_USB_MOSCHIP
    {
        USB_DEVICE (MCS_VENDOR_ID,MCS_PRODUCT_ID_7830),      //MOSCHIP7830
            .driver_info =  (unsigned long) &moschip_info,
    },
    {
        USB_DEVICE (MCS_VENDOR_ID,MCS_PRODUCT_ID_7832),      //MOSCHIP7832
            .driver_info =  (unsigned long) &moschip_info,
    },
#endif
    { },    // END
};

MODULE_DEVICE_TABLE (usb, products);

static struct usb_driver usbnet_driver = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
    .owner      = THIS_MODULE,
#endif
    .name       = driver_name,
    .id_table   = products,
    .probe      = usbnet_probe,
    .disconnect = usbnet_disconnect,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
    .suspend    = usbnet_suspend,
    .resume     = usbnet_resume,
#endif
};

/* Default ethtool_ops assigned.  Devices can override in their bind() routine */
static struct ethtool_ops usbnet_ethtool_ops = {
    .get_drvinfo        = usbnet_get_drvinfo,
    .get_link           = usbnet_get_link,
    .get_msglevel       = usbnet_get_msglevel,
    .set_msglevel       = usbnet_set_msglevel,
    //.get_wol            = moschip_get_wol,
    //.set_wol            = moschip_set_wol,
    //.get_settings       = moschip_get_settings,
    //.set_settings       = moschip_set_settings,
    //.nway_reset         = moschip_nway_reset,
        
};

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28)
static struct net_device_ops mcs7830_netdev_ops = {
    .ndo_open               = usbnet_open,
    .ndo_stop               = usbnet_stop,
    .ndo_start_xmit         = usbnet_start_xmit,
    .ndo_tx_timeout         = usbnet_tx_timeout,
    .ndo_change_mtu         = usbnet_change_mtu,
    .ndo_do_ioctl           = usbnet_ioctl,
    .ndo_get_stats          = usbnet_get_stats,
};
#endif
/*-------------------------------------------------------------------------*/

static int __init usbnet_init (void)
{
    // compiler should optimize these out
    BUG_ON (sizeof (((struct sk_buff *)0)->cb)
        < sizeof (struct skb_data));
    
    printk("usbnet_init\n");
    random_ether_addr(node_id);
    
    return usb_register(&usbnet_driver);
}
module_init (usbnet_init);

static void __exit usbnet_exit (void)
{
    printk("usbnet_exit\n");
    usb_deregister (&usbnet_driver);
}
module_exit (usbnet_exit);
MODULE_DESCRIPTION ("USB to network adapter MCS7830)");
MODULE_LICENSE ("GPL");
