A small RTL8139too Network Interface Card driver for linux

/*******************************************************************
* 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux
* Thanks: ¢一天℃( QQ:185687231)
* Fix history: wenxy([email protected]), 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. [email protected]

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
 

你可能感兴趣的:(A small RTL8139too Network Interface Card driver for linux)