A small RTL8139too Network Interface Card driver for linux

原创 2007年09月27日 16:28:00
/*******************************************************************
* 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux
* Thanks: ¢一天℃( QQ:185687231)
* Fix history: wenxy(wen_kernel@163.com), 20070927, p.m.
*
********************************************************************/

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/wireless.h>
#include <asm/uaccess.h>    //for copy_from_user
#include <asm/io.h>

MODULE_LICENSE("GPL");
#define MODNAME "8139too"

/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
static int multicast_filter_limit = 32;

/* Size of the in-memory receive ring. */
#define RX_BUF_LEN_IDX    2    /* 0==8K, 1==16K, 2==32K, 3==64K */
#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
#define RX_BUF_PAD 16
#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)

/* Number of Tx descriptor registers. */
#define NUM_TX_DESC    4
#define TX_BUF_SIZE    1536
#define TX_BUF_TOT_LEN    (TX_BUF_SIZE * NUM_TX_DESC)
#define TX_FIFO_THRESH 256    /* In bytes, rounded down to 32 byte units. */

/* The following settings are log_2(bytes)-4:  0 == 16 bytes .. 6==1024, 7==end of packet. */
#define RX_FIFO_THRESH    0    /* Rx buffer level before first PCI xfer.  */
#define RX_DMA_BURST    7    
#define TX_DMA_BURST    6   

static struct pci_device_id rtl8139_pci_tbl[] __devinitdata = {
    {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
    {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0,0 },
    {0,}
};
MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl);

/* Symbolic offsets to registers. */
enum RTL8139_registers {
    MAC0 = 0,        /* Ethernet hardware address. */
    MAR0 = 8,        /* Multicast filter. */
    TxStatus0 = 0x10,    /* Transmit status (Four 32bit registers). */
    TxAddr0 = 0x20,        /* Tx descriptors (also four 32bit). */
    RxBuf = 0x30,
    RxEarlyCnt = 0x34,
    RxEarlyStatus = 0x36,
    ChipCmd = 0x37,
    RxBufPtr = 0x38,
    RxBufAddr = 0x3A,
    IntrMask = 0x3C,
    IntrStatus = 0x3E,
    TxConfig = 0x40,
    RxConfig = 0x44,
    RxMissed = 0x4C,    /* 24 bits valid, write clears. */
        MultiIntr = 0x5C,
};

enum ClearBitMasks {
    MultiIntrClear = 0xF000,
    ChipCmdClear = 0xE2,
};

enum ChipCmdBits {
    CmdReset = 0x10,
    CmdRxEnb = 0x08,
    CmdTxEnb = 0x04,
    RxBufEmpty = 0x01,
};

enum IntrStatusBits {
    PCIErr = 0x8000,
    PCSTimeout = 0x4000,
    RxFIFOOver = 0x40,
    RxUnderrun = 0x20,
    RxOverflow = 0x10,
    TxErr = 0x08,
    TxOK = 0x04,
    RxErr = 0x02,
    RxOK = 0x01,
};
enum TxStatusBits {
    TxHostOwns = 0x2000,
    TxUnderrun = 0x4000,
    TxStatOK = 0x8000,
    TxOutOfWindow = 0x20000000,
    TxAborted = 0x40000000,
};
enum RxStatusBits {
    RxMulticast = 0x8000,
    RxPhysical = 0x4000,
    RxBroadcast = 0x2000,
    RxBadSymbol = 0x0020,
    RxRunt = 0x0010,
    RxTooLong = 0x0008,
    RxCRCErr = 0x0004,
    RxBadAlign = 0x0002,
    RxStatusOK = 0x0001,
};

/* Bits in RxConfig. */
enum rx_mode_bits {
    AcceptErr = 0x20,
    AcceptRunt = 0x10,
    AcceptBroadcast = 0x08,
    AcceptMulticast = 0x04,
    AcceptMyPhys = 0x02,
    AcceptAllPhys = 0x01,
};

/* Bits in TxConfig. */
enum tx_config_bits {
    TxIFG1 = (1 << 25),    /* Interframe Gap Time */
    TxIFG0 = (1 << 24),    /* Enabling these bits violates IEEE 802.3 */
    TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
    TxCRC = (1 << 16),    /* DISABLE appending CRC to end of Tx packets */
    TxClearAbt = (1 << 0),    /* Clear abort (WO) */
    TxDMAShift = 8,        /* DMA burst value (0-7) is shift this many bits */

    TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
};

enum RxConfigBits {
    /* Early Rx threshold, none or X/16 */
    RxCfgEarlyRxNone = 0,
    RxCfgEarlyRxShift = 24,

    /* rx fifo threshold */
    RxCfgFIFOShift = 13,
    RxCfgFIFONone = (7 << RxCfgFIFOShift),

    /* Max DMA burst */
    RxCfgDMAShift = 8,
    RxCfgDMAUnlimited = (7 << RxCfgDMAShift),

    /* rx ring buffer length */
    RxCfgRcv8K = 0,
    RxCfgRcv16K = (1 << 11),
    RxCfgRcv32K = (1 << 12),
    RxCfgRcv64K = (1 << 11) | (1 << 12),

    /* Disable packet wrap at end of Rx buffer */
    RxNoWrap = (1 << 7),
};

//enum MediaStatusBits {
//    DuplexMode = 0x0100,    //in BasicModeControlRegister
//    Speed_10 = 0x08,        //in Media Status Register
//};

struct rtl8139_private {
    void *mmio_addr;
    int drv_flags;
    struct pci_dev *pci_dev;
    struct net_device_stats stats;
    unsigned char *rx_ring;
    unsigned int cur_rx;    /* Index into the Rx buffer of next Rx pkt. */
    unsigned int tx_flag;
    unsigned long cur_tx;
    unsigned long dirty_tx;
        struct sk_buff* skb[NUM_TX_DESC];
    unsigned char *tx_buf[NUM_TX_DESC];    /* Tx bounce buffers */
    unsigned char *tx_bufs;    /* Tx bounce buffer region. */
    dma_addr_t rx_ring_dma;
    dma_addr_t tx_bufs_dma;
    spinlock_t lock;
};

static int rtl8139_open (struct net_device *dev);
static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev);
static void rtl8139_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
static void rtl8139_hw_start (struct net_device *dev);
static int rtl8139_close (struct net_device *dev);

static struct net_device_stats *rtl8139_get_stats (struct net_device *dev);
static void rtl8139_set_rx_mode (struct net_device *dev);

static const u16 rtl8139_intr_mask =
    PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver |
    TxErr | TxOK | RxErr | RxOK;

static const unsigned int rtl8139_rx_config =
      RxCfgEarlyRxNone | RxCfgRcv32K | RxNoWrap |
      (RX_FIFO_THRESH << RxCfgFIFOShift) |
      (RX_DMA_BURST << RxCfgDMAShift);

/*-----------------------------------------------------------*/
static int __devinit rtl8139_init_one (struct pci_dev *pdev,
                       const struct pci_device_id *ent)
{
    int i;
    void *ioaddr = NULL;
    struct net_device *dev;
    struct rtl8139_private *tp;
    int rc ;
    unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;

        dev = init_etherdev (NULL, sizeof (*tp));
    if (dev == NULL) {
        printk ("unable to alloc new ethernet/n");
        return -ENOMEM;
    }
    tp = dev->priv;
   
        rc = pci_enable_device (pdev);
    if (rc)
        goto err_out;

    mmio_start = pci_resource_start (pdev, 1);
    mmio_end = pci_resource_end (pdev, 1);
    mmio_flags = pci_resource_flags (pdev, 1);
    mmio_len = pci_resource_len (pdev, 1);

    /* make sure PCI base addr 1 is MMIO */
    if (!(mmio_flags & IORESOURCE_MEM)) {
    printk ("region #1 not an MMIO resource, aborting/n");
        rc = -ENODEV;
        goto err_out;
    }

    /* check for weird/broken PCI region reporting */
    if (mmio_len < 256) {
        printk ("Invalid PCI region size(s), aborting/n");
        rc = -ENODEV;
        goto err_out;
    }

    rc = pci_request_regions (pdev, dev->name);
    if (rc)
        goto err_out;
    pci_set_master (pdev);

    /* ioremap MMIO region */
    ioaddr = ioremap (mmio_start, mmio_len);
    if (ioaddr == NULL) {
        printk ("cannot remap MMIO, aborting/n");
        rc = -EIO;
        goto err_out_free_res;
    }
    /* Soft reset the chip. */
    writeb ((readb(ioaddr+ChipCmd) & ChipCmdClear) | CmdReset,ioaddr+ChipCmd);

    /* Check that the chip has finished the reset. */
    for (i = 1000; i > 0; i--)
        if ((readb(ioaddr+ChipCmd) & CmdReset) == 0)
            break;
        else
            udelay (10);

        for(i = 0; i < 6; i++) {  /* Hardware Address */
        dev->dev_addr[i] = readb(ioaddr+i);
        dev->broadcast[i] = 0xff;
    }

        dev->open = rtl8139_open;
        dev->hard_start_xmit = rtl8139_start_xmit;
        dev->stop = rtl8139_close;
            dev->get_stats = rtl8139_get_stats;
            dev->irq = pdev->irq;
            dev->base_addr = (unsigned long) ioaddr;
   
    tp->pci_dev = pdev;
    tp->mmio_addr = ioaddr;
    pdev->driver_data = dev;
    return 0;

err_out_free_res:
    pci_release_regions (pdev);
err_out:
    unregister_netdev (dev);
    kfree (dev);
        return rc;
}

static int rtl8139_close (struct net_device *dev)
{
        struct rtl8139_private *tp = dev->priv;
        void *ioaddr = tp->mmio_addr;
                                                                               
        netif_stop_queue (dev);                                                                          spin_lock_irq (&tp->lock);
                                                                               
        writeb((readb(ioaddr+ChipCmd) & ChipCmdClear),ioaddr+ChipCmd);
        writew(0x0000,ioaddr+IntrMask);
        writel(0,ioaddr+RxMissed);
                                                                               
        spin_unlock_irq (&tp->lock);
                                                                               
        synchronize_irq ();
        free_irq (dev->irq, dev);
                             
        tp->cur_tx=0;
        tp->dirty_tx=0;int i;
        for (i = 0; i < NUM_TX_DESC; i++) {
                if (tp->skb[i]) {
                        dev_kfree_skb (tp->skb[i]);
                        tp->skb[i] = NULL;

                }
         }
                                                
        pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,tp->rx_ring, tp->rx_ring_dma);
        pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,tp->tx_bufs, tp->tx_bufs_dma);
        tp->rx_ring = NULL;
        tp->tx_bufs = NULL;                                                                               
        return 0;
}

static void __devexit rtl8139_remove_one (struct pci_dev *pdev)
{
    struct net_device *dev = pdev->driver_data;
    struct rtl8139_private *np;

    np = (struct rtl8139_private *) (dev->priv);

    iounmap (np->mmio_addr);
    pci_release_regions (pdev);

    unregister_netdev (dev);

    pdev->driver_data = NULL;

    pci_disable_device (pdev);
}

static int rtl8139_open (struct net_device *dev)
{
    struct rtl8139_private *tp = dev->priv;
    int retval,i;

    retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev);
    if (retval) {
        return retval;
    }

    tp->tx_bufs = pci_alloc_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
                       &tp->tx_bufs_dma);
    tp->rx_ring = pci_alloc_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
                       &tp->rx_ring_dma);
    if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
        free_irq(dev->irq, dev);

        if (tp->tx_bufs)
            pci_free_consistent(tp->pci_dev, TX_BUF_TOT_LEN,
                        tp->tx_bufs, tp->tx_bufs_dma);
        if (tp->rx_ring)
            pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
                        tp->rx_ring, tp->rx_ring_dma);
        return -ENOMEM;

    }

    tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;

        ((struct rtl8139_private*)dev->priv)->cur_rx = 0;
        ((struct rtl8139_private*)dev->priv)->cur_tx = 0;
        ((struct rtl8139_private*)dev->priv)->dirty_tx = 0;
                                                                                                
        for (i = 0; i < NUM_TX_DESC; i++)
                ((struct rtl8139_private*)dev->priv)->tx_buf[i] =
                             &((struct rtl8139_private*)dev->priv)->tx_bufs[i * TX_BUF_SIZE];

    rtl8139_hw_start (dev);
    return 0;
}

/* Start the hardware at open or resume. */
static void rtl8139_hw_start (struct net_device *dev)
{
    struct rtl8139_private *tp = dev->priv;
    void *ioaddr = tp->mmio_addr;
    u32 i;

    writeb((readb(ioaddr+ChipCmd) &ChipCmdClear) | CmdReset,ioaddr+ChipCmd);
    udelay (100);
    for (i = 1000; i > 0; i--)
        if ((readb(ioaddr+ChipCmd) & CmdReset) == 0)
            break;

    writeb((readb(ioaddr+ChipCmd) & ChipCmdClear) |
               CmdRxEnb | CmdTxEnb,ioaddr+ChipCmd);
    writel ((TX_DMA_BURST << TxDMAShift),ioaddr+TxConfig);

    tp->cur_rx = 0;

        writel(tp->rx_ring_dma,ioaddr+RxBuf);
    for (i = 0; i < NUM_TX_DESC; i++)
        {
    writel(tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs),ioaddr+TxAddr0+(i*4));
        readl(ioaddr+TxAddr0+(i * 4));
        }

    writel(0,ioaddr+RxMissed);
    rtl8139_set_rx_mode (dev);

    writew(readw(ioaddr+MultiIntr) & MultiIntrClear,ioaddr+MultiIntr);
    writeb((readb(ioaddr+ChipCmd) & ChipCmdClear) |
               CmdRxEnb | CmdTxEnb,ioaddr+ChipCmd);
        readb(ioaddr+ChipCmd);

    writew(rtl8139_intr_mask,ioaddr+IntrMask);
        readw(ioaddr+IntrMask);

    netif_start_queue (dev);
}

static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
{
    struct rtl8139_private *tp = dev->priv;
    void *ioaddr = tp->mmio_addr;
    int entry;

    /* Calculate the next Tx descriptor entry. */
    entry = tp->cur_tx % NUM_TX_DESC;

    // ----------------------------------------------
    // For skb->len < 60, padding payload with 0x20.
    // ----------------------------------------------
    if( skb->len < ETH_ZLEN ){//if data_len < 60
        if( (skb->data + ETH_ZLEN) <= skb->end ){
            memset( skb->data + skb->len, 0x20, (ETH_ZLEN - skb->len) );   
                    skb->len = (skb->len >= ETH_ZLEN) ? skb->len : ETH_ZLEN;            }
        else{
            printk("%s:(skb->data+ETH_ZLEN) > skb->end/n",__FUNCTION__);
        }                                   
        }
            tp->skb[entry] = skb;
        memcpy (tp->tx_buf[entry], skb->data, skb->len);
writel(tp->tx_bufs_dma + (tp->tx_buf[entry] - tp->tx_bufs),ioaddr+TxAddr0+(entry*4));
    /* Note: the chip doesn't have auto-pad! */
writel(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN),ioaddr+TxStatus0+(entry * 4));

    dev->trans_start = jiffies;
    spin_lock_irq (&tp->lock);
    tp->cur_tx++;
    if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
        netif_stop_queue (dev);
    spin_unlock_irq (&tp->lock);

    return 0;
}         

static void rtl8139_tx_interrupt (struct net_device *dev,
                  struct rtl8139_private *tp,
                  void *ioaddr)
{
    unsigned long dirty_tx, tx_left;

    dirty_tx = tp->dirty_tx;
    tx_left = tp->cur_tx - dirty_tx;
    while (tx_left > 0) {
        int entry = dirty_tx % NUM_TX_DESC;
        int txstatus;

        txstatus = readl(ioaddr+TxStatus0 + (entry * sizeof (u32)));

        if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted)))
            break;
        if (txstatus & (TxOutOfWindow | TxAborted)) {
            if (txstatus & TxAborted) {
                writel(TxClearAbt | (TX_DMA_BURST << TxDMAShift),ioaddr+TxConfig);
            }
        } else {
            if (txstatus & TxUnderrun) {
                if (tp->tx_flag < 0x00300000)
                    tp->tx_flag += 0x00020000;
            }
        }
/* Free the original skb. */
                dev_kfree_skb_irq (tp->skb[entry]);
                tp->skb[entry] = NULL;

        dirty_tx++;
        tx_left--;
    }
/* only wake the queue if we did work, and the queue is stopped */
    if (tp->dirty_tx != dirty_tx) {
        tp->dirty_tx = dirty_tx;
        if (netif_queue_stopped (dev))
            netif_wake_queue (dev);
    }
}

/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
   field alignments and semantics. */
static void rtl8139_rx_interrupt (struct net_device *dev,
                  struct rtl8139_private *tp, void *ioaddr)
{
    unsigned char *rx_ring;
    u16 cur_rx;

    rx_ring = tp->rx_ring;
    cur_rx = tp->cur_rx;

    while ((readb(ioaddr+ChipCmd) & RxBufEmpty) == 0) {
        int ring_offset = cur_rx % RX_BUF_LEN;
        u32 rx_status;
        unsigned int rx_size;
        unsigned int pkt_size;
        struct sk_buff *skb;

        /* read size+status of next frame from DMA ring buffer */
        rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
        rx_size = rx_status >> 16;
        pkt_size = rx_size - 4;

        if (rx_size == 0xfff0)
            break;

            skb = dev_alloc_skb (pkt_size + 2);
            if (skb) {
                skb->dev = dev;
                skb_reserve (skb, 2);    /* 16 byte align the IP fields. */

                eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
                skb_put (skb, pkt_size);
                skb->protocol = eth_type_trans (skb, dev);
                netif_rx (skb);
                dev->last_rx = jiffies;
            } else {
                printk (KERN_WARNING
                    "%s: Memory squeeze, dropping packet./n",
                    dev->name);
            }

        cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
        writew(cur_rx - 16,ioaddr+RxBufPtr);
                readw(ioaddr+RxBufPtr);
    }
    tp->cur_rx = cur_rx;
}

static void rtl8139_interrupt (int irq, void *dev_instance,
                   struct pt_regs *regs)
{
    struct net_device *dev = (struct net_device *) dev_instance;
    struct rtl8139_private *tp = dev->priv;
    void *ioaddr = tp->mmio_addr;
    int status = 0;

        status = readw(ioaddr+IntrStatus);

        /* h/w no longer present (hotplug?) or major error, bail */
        if (status == 0xFFFF)
            goto out;

        writew((status & RxFIFOOver) ? (status | RxOverflow) : status,ioaddr+IntrStatus);

        if ((status &
             (PCIErr | PCSTimeout | RxUnderrun | RxOverflow |
              RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0)
            goto out;

        if (status & (RxOK | RxUnderrun | RxOverflow | RxFIFOOver))/* Rx interrupt */
            rtl8139_rx_interrupt (dev, tp, ioaddr);

        if (status & (TxOK | TxErr)) {
            spin_lock (&tp->lock);
            rtl8139_tx_interrupt (dev, tp, ioaddr);
            spin_unlock (&tp->lock);
        }
out:
         writew(0xffff,ioaddr+IntrStatus);
                 readw(ioaddr+IntrStatus);
}


static struct net_device_stats *rtl8139_get_stats (struct net_device *dev)
{
        struct rtl8139_private *tp = dev->priv;
    return &tp->stats;
}

/* Set or clear the multicast filter for this adaptor.
   This routine is not state sensitive and need not be SMP locked. */

static unsigned const ethernet_polynomial = 0x04c11db7U;
static inline u32 ether_crc (int length, unsigned char *data)
{
    int crc = -1;

    while (--length >= 0) {
        unsigned char current_octet = *data++;
        int bit;
        for (bit = 0; bit < 8; bit++, current_octet >>= 1)
            crc = (crc << 1) ^ ((crc < 0) ^ (current_octet & 1) ?
                 ethernet_polynomial : 0);
    }

    return crc;
}


static void rtl8139_set_rx_mode (struct net_device *dev)
{
    struct rtl8139_private *tp = dev->priv;
    void *ioaddr = tp->mmio_addr;
    unsigned long flags;
    u32 mc_filter[2];    /* Multicast hash filter */
    int i, rx_mode;
    u32 tmp;

    /* Note: do not reorder, GCC is clever about common statements. */
    if (dev->flags & IFF_PROMISC) {
        /* Unconditionally log net taps. */
        printk (KERN_NOTICE "%s: Promiscuous mode enabled./n",
            dev->name);
        rx_mode =
            AcceptBroadcast | AcceptMulticast | AcceptMyPhys |
            AcceptAllPhys;
        mc_filter[1] = mc_filter[0] = 0xffffffff;
    } else if ((dev->mc_count > multicast_filter_limit)
           || (dev->flags & IFF_ALLMULTI)) {
        /* Too many to filter perfectly -- accept all multicasts. */
        rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
        mc_filter[1] = mc_filter[0] = 0xffffffff;
    } else {
        struct dev_mc_list *mclist;
        rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
        mc_filter[1] = mc_filter[0] = 0;
        for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
             i++, mclist = mclist->next)
            set_bit (ether_crc (ETH_ALEN, mclist->dmi_addr) >> 26,
                 mc_filter);
    }

    spin_lock_irqsave (&tp->lock, flags);

    /* We can safely update without stopping the chip. */
    tmp = rtl8139_rx_config | rx_mode |
        (readl(ioaddr+RxConfig) );
    writel(tmp,ioaddr+RxConfig);
        readl(ioaddr+RxConfig);
    writel(mc_filter[0],ioaddr+MAR0+0);
        readl(ioaddr+MAR0+0);
    writel(mc_filter[1],ioaddr+MAR0+4);
        readl(ioaddr+MAR0+4);

    spin_unlock_irqrestore (&tp->lock, flags);
}

static struct pci_driver rtl8139_pci_driver = {
    name:        MODNAME,
    id_table:    rtl8139_pci_tbl,
    probe:        rtl8139_init_one,
    remove:        rtl8139_remove_one,
};

static int __init rtl8139_init_module (void)
{
    return pci_module_init (&rtl8139_pci_driver);
}

static void __exit rtl8139_cleanup_module (void)
{
    pci_unregister_driver (&rtl8139_pci_driver);
}

module_init(rtl8139_init_module);
module_exit(rtl8139_cleanup_module);


# Makefile for a RTL8139too NIC driver
# wenxy 20070919, a.m. wen_kernel@163.com

dir = /usr/src/linux-2.4/include
modules = 8139too.o
CC = gcc
MODCFLAGS = -Wall -DMODULE -D__KERNEL__ -DLINUX  -I$(dir)

all : $(modules)

8139too.o : 8139too.c
    $(CC) $(MODCFLAGS) -c 8139too.c
    chmod 711 $@
   
   
.PHONEY : clean run exit
run:
    insmod -f $(modules)
   
exit:
    rmmod 8139too.o
   
clean :
    -rm $(modules)
   

# end makefile
 

相关文章推荐

Linux下Rtl8139too网卡设备驱动程序关键函数剖析

static int __devinit rtl8139_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)函数功能描述:...
  • jw212
  • jw212
  • 2011年07月07日 10:10
  • 625

Linux内核学习笔记之网卡驱动的详细分析:RTL8139

学习应该是一个先把问题简单化,在把问题复杂化的过程。一开始就着手处理复杂的问题,难免让 人有心惊胆颤,捉襟见肘的感觉。读Linux网卡驱动也是一样。那长长的源码夹杂着那些我们陌生的变量和符号,望而生畏...

AD10 导出生产文件【过孔盖油】【gerber文件和位号图】【修改标号相对于元件的位置】【输出gerber的时候提示the film is too small for this pc】【坐标文件】

输出gerber文件 本节部分内容摘录于:http://blog.sina.com.cn/s/blog_9b9a51990100zyyv.html...

用rtl8139网卡制作的bios编程器(不用并口)

最重要一点,8139的4个寄存器只能一次性写入,分开写会出问题。程序有待改进,判断刷写完成的程序用延时代替的。编程对象是w39v040。 #include #include #include "win...

亚马逊服务器(Linux 3.13.6)A very large skb can span too many pages (more than 16) to be put in the driver

Brendan Gregg's Blog  home Linux perf Rides the Rocket 11 Sep 2014 Riding the rocket ...

linux2.6内核SD Card Driver详细解析之一

****************************************************************************************************...
  • wavemcu
  • wavemcu
  • 2012年03月18日 18:27
  • 7741

rtl8139网卡驱动源码解析

学习应该是一个先把问题简单化,在把问题复杂化的过程。一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉。读Linux网卡驱动也是一样。那长长的源码夹杂着那些我们陌生的变量和符号,望而生畏便...
  • tym8865
  • tym8865
  • 2013年01月28日 11:16
  • 1095
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:A small RTL8139too Network Interface Card driver for linux
举报原因:
原因补充:

(最多只允许输入30个字)