diff -u -r --new-file linux-2.4.18-sdk/drivers/net/Config.in linux-2.4.18/drivers/net/Config.in --- linux-2.4.18-sdk/drivers/net/Config.in 2003-06-19 14:00:42.000000000 +0200 +++ linux-2.4.18/drivers/net/Config.in 2004-05-22 21:36:09.000000000 +0200 @@ -25,6 +25,7 @@ comment 'Ethernet (10 or 100Mbit)' bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET if [ "$CONFIG_NET_ETHERNET" = "y" ]; then + tristate 'RTL8139emb (For RTL8181 SOC) 10/100 support' CONFIG_RTL8181_8139EMB tristate 'RTL8139cp (For RTL8181 SOC) 10/100 support' CONFIG_RTL8139CP tristate "RTL8181-Wireless-Router mode(with RTL8305SB support)" CONFIG_RTL8305SB if [ "$CONFIG_RTL8305SB" = "y" ]; then diff -u -r --new-file linux-2.4.18-sdk/drivers/net/Makefile linux-2.4.18/drivers/net/Makefile --- linux-2.4.18-sdk/drivers/net/Makefile 2003-05-12 08:01:06.000000000 +0200 +++ linux-2.4.18/drivers/net/Makefile 2004-05-22 21:30:23.000000000 +0200 @@ -217,6 +220,7 @@ obj-$(CONFIG_MAC89x0) += mac89x0.o obj-$(CONFIG_TUN) += tun.o obj-$(CONFIG_DL2K) += dl2k.o +obj-$(CONFIG_RTL8181_8139EMB) += 8139emb.o ifeq ($(CONFIG_ARCH_ACORN),y) mod-subdirs += ../acorn/net @@ -231,10 +235,12 @@ ifneq ($(ARCH),s390) ifneq ($(ARCH),s390x) ifneq ($(CONFIG_RTL8139CP),y) +ifneq ($(CONFIG_RTL8181_8139EMB),y) obj-y += auto_irq.o endif endif endif +endif include $(TOPDIR)/Rules.make diff -u -r --new-file linux-2.4.18-sdk/drivers/net/8139emb.c linux-2.4.18/drivers/net/8139emb.c --- linux-2.4.18-sdk/drivers/net/8139emb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.18/drivers/net/8139emb.c 2004-02-02 23:03:56.000000000 +0100 @@ -0,0 +1,1293 @@ +/* + * linux/drivers/net/8139emb.c + * A Linux driver for the embedded RTL8139 inside the RTL8181 SoC + * + * Based on linux/drivers/net/8139too.c + * Copyright 2000-2002 Jeff Garzik + * + * Modified by Realtek and distributed as part of the RTL8181 SDK + * Cleaned up by Streetdata (streetdata at users.sourceforge.net) + * + * Portions Copyright (C) Realtek Semiconductors + * Portions Copyright (C) 2004, Streetdata Pty Ltd + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define TRUE 1 +#define FALSE 0 + +#define RTL8139AP_CHECKSUM_OFFLOAD TRUE +//#define REALHACK_RECV TRUE /* Realtek RECV congestion hack */ +//#define REALHACK_IMTR TRUE /* Realtek IMTR undocumented hack */ +//#define RTL8139_EARLY_FREE + +#define RTL8139_VERSION "0.9.10" +#define RTL8139_MODULE_NAME "8139too" +#define RTL8139_DRIVER_NAME RTL8139_MODULE_NAME " Fast Ethernet driver " RTL8139_VERSION +#define PFX RTL8139_MODULE_NAME ": " + +#undef RTL8139_DEBUG + +/* define to 1 to disable lightweight runtime debugging checks */ +#define RTL8139_NDEBUG + +#ifdef RTL8139_DEBUG +/* note: prints function name for you */ +# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +# define DPRINTK(fmt, args...) +#endif + +#ifdef RTL8139_NDEBUG +# define assert(expr) do {} while (0) +#else +# define assert(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + +#define arraysize(x) (sizeof(x)/sizeof(*(x))) + + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; +#define MAX_RX_INTERRUPT_WORK 58 + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The RTL chips use a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; + +/*----------------------------------------------------------------------------- + Some flags about Rx/Tx +------------------------------------------------------------------------------*/ + +typedef struct +{ + int header; + int tag; + int addr; + int dummy; +}desc_t; + + +#define NUM_DESC 64 + + +/*-----------------Rx Flags------------------------*/ +#define NUM_RX_DESC (NUM_DESC) +// please refer to net/core/skbuff.c to find the reasons... sizeof(struct skb_shared_info)==0x3c) +//#define RX_BUF_LEN (2048*2 - 16 - (sizeof(struct skb_shared_info))) // Allocate skb size +#define RX_BUF_LEN (2048 - 16 - (sizeof(struct skb_shared_info))) // Allocate skb size +#define RX_DESC_SIZE (NUM_RX_DESC * sizeof(desc_t)) + + +/*-----------------Rx Flags------------------------*/ +#define NUM_TX_DESC (NUM_DESC) +#define TX_DESC_SIZE (NUM_TX_DESC * sizeof(desc_t)) + +#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ + + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (6*HZ) + +/* Some handy defines */ +#define RTL8181_MAC0_ADDR 0x1f0000 +#define RTL8181_MAC1_ADDR 0x2f0000 +#define RTL8181_MAC0_IRQ 4 +#define RTL8181_MAC1_IRQ 5 + +#ifdef CONFIG_RTL8181_SWAPMACS /* Formerly CONFIG_RTL8181_8305PATCH */ +#define RTL8181_ETH0_ADDR RTL8181_MAC1_ADDR +#define RTL8181_ETH1_ADDR RTL8181_MAC0_ADDR +#define RTL8181_ETH0_IRQ RTL8181_MAC1_IRQ +#define RTL8181_ETH1_IRQ RTL8181_MAC0_IRQ +#else +#define RTL8181_ETH0_ADDR RTL8181_MAC0_ADDR +#define RTL8181_ETH1_ADDR RTL8181_MAC1_ADDR +#define RTL8181_ETH0_IRQ RTL8181_MAC0_IRQ +#define RTL8181_ETH1_IRQ RTL8181_MAC1_IRQ +#endif + +/* + * Values borrowed from Realtek's original driver + */ +#define MII_PHY8305_ETH0 0x1e010 +#define MII_PHY8305_ETH1 0x46000 +#define MII_PHY8201 0x16000 + +enum RxBurstConfig { + RxBurst_64 = 0x0, + RxBurst_128 = 0x1, + RxBurst_256 = 0x2, + RxBurst_512 = 0x3, +}; + + +enum TxBurstConfig { + TxBurst_32 = 0x0, + TxBurst_64 = 0x2, + TxBurst_128 = 0x3, + TxBurst_256 = 0x4, + TxBurst_512 = 0x5, + TxBurst_all = 0x6, +}; + + +enum NIC_CNTRL_REGISTERS { + RxBLenShift = 30, + RxBLenMask = ( BIT(31) | BIT(30) ), + TxBLenShift = 26, + TxBLenMask = ( BIT(28) | BIT(27) | BIT(26) ), + TxDescFN = BIT(24), + Reset = BIT(20), + RxEnable = BIT(19), + TxEnable = BIT(18), + TxFCenable = BIT(17), + RxChkSum = BIT(13), + + AER = BIT(8), /* AcceptErr */ + AB = BIT(7), /* AcceptBroadcast */ + AM = BIT(6), /* AcceptMulticast */ + AMP = BIT(5), /* AcceptMyPhys */ + AAP = BIT(4), /* AcceptAllPhys */ + ATM = BIT(3), + AR = BIT(2), + ALEN= BIT(1), +}; + +/* Interrupt register bits, using my own meaningful names. */ +enum NIC_INTERRUPT_MASK { + LinkChMask = BIT(9), + LinkChStats = BIT(9), + RxErrMask = BIT(8), + RxErrStats = BIT(8), + TxErrMask = BIT(7), + TxErrStats = BIT(7), + RxOkMask = BIT(6), + RxOkStats = BIT(6), + TxOkMask = BIT(5), + TxOkStats = BIT(5), + RxFFOvMask = BIT(4), + RxFFOvStats = BIT(4), + RxDescUnMask = BIT(3), + RxDescUnStats = BIT(3), + TxDescUnMask = BIT(1), + TxDescUnStats = BIT(1), +}; + +enum RTL8139_DESC_ATTRIBUTE { + OWNBYNIC =0x80000000, + ENDOFRING =0x40000000, + FIRSTSEG =0x20000000, + LASTSEG =0x10000000, + TXLENMASK =0x0000ffff, + TXERR =0x00800000, + TXERR_OUTOFWIN =0x00400000, + TXERR_LINKF =0x00200000, + TXERR_COLLISION =0x00100000, + RXLENMASK =0x00001FFF, + RXERR =0x00100000, + RXRUNT =0x00080000, + RXCRC =0x00040000, + RX_PID0 =(1 << 16), + RX_PID1 =(1 << 17), + PROTO_TCP =1, + PROTO_UDP =2, + PROTO_IP =3, + TCPFail = (1 << 13), + UDPFail = (1 << 14), + IPFail = (1 << 15), + TcpTxCsumEn = (1 << 16), + UdpTxCsumEn = (1 << 17), + IpTxCsumEn = (1 << 18), +}; + + +struct rtl8139_private { + void *mmio_addr; + struct net_device_stats stats; +#ifdef REALHACK_RECV + struct timer_list timer; + struct timer_list rxtimer; +#endif +#ifdef REALHACK_IMTR + struct timer_list imtrtimer; // timer for tuning imtr +#endif + unsigned int rxinterrupt_count; + unsigned int intr_mask; + + unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ + desc_t *rx_desc; + struct sk_buff * rx_skb[NUM_RX_DESC]; + + unsigned int tx_flag; + atomic_t cur_tx; + atomic_t dirty_tx; + desc_t *tx_desc; /* Tx bounce buffers */ + struct sk_buff * tx_skb[NUM_TX_DESC]; + + char phys[4]; /* MII device addresses. */ + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int duplex_lock:1; + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int media2:4; /* Secondary monitored media port. */ + unsigned int medialock:1; /* Don't sense media type. */ + unsigned int mediasense:1; /* Media sensing in progress. */ + unsigned char MacAddr[6]; + unsigned int netrxdrop; + spinlock_t lock; // we only need this lock in case of SMP +}; + +static void rtl8139_chip_reset (void *ioaddr); +static int rtl8139_sw_init(struct rtl8139_private *tp); +static int rtl8139_open (struct net_device *dev); +#if 0 +static int mdio_read (struct net_device *dev, int phy_id, int location); +static void mdio_write (struct net_device *dev, int phy_id, int location, int val); +#endif +#ifdef REALHACK_RECV +static void rtl8139_timer (unsigned long data); +static void rtl8139_rxtimer (unsigned long data); +#endif +#ifdef REALHACK_IMTR +static void rtl8139_imtrtimer (unsigned long data); +#endif +static void rtl8139_tx_timeout (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 int rtl8139_close (struct net_device *dev); +static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct net_device_stats *rtl8139_get_stats (struct net_device *dev); +static inline u32 ether_crc (int length, unsigned char *data); +static void rtl8139_set_rx_mode (struct net_device *dev); +static void rtl8139_hw_start (struct net_device *dev); +static int rtl8139_sw_free(struct rtl8139_private *tp); +static void rtl8139_txrx_stop (struct rtl8139_private *tp); + +#define RTL_W32_F(reg, val32) outl ((val32), (int)(ioaddr + (reg))) +#define RTL_W32 RTL_W32_F +#define RTL_R32(reg) inl ((int)(ioaddr + (reg))) + +static const int rtl8139_intr_mask = + LinkChMask | RxOkMask | RxFFOvMask | RxDescUnMask | + TxOkMask | TxDescUnMask; + +#ifdef RTL8139AP_CHECKSUM_OFFLOAD +static const unsigned int rtl8139_trx_config = + (((TxBurst_256 << TxBLenShift) & TxBLenMask) + | ((RxBurst_64 << RxBLenShift) & RxBLenMask)) | RxChkSum ; +#else +static const unsigned int rtl8139_trx_config = + (((TxBurst_256 << TxBLenShift) & TxBLenMask) + | ((RxBurst_64 << RxBLenShift) & RxBLenMask)) ; + +#endif + +#ifdef RTL8139AP_CHECKSUM_OFFLOAD +static inline unsigned int rx_csum_ok(unsigned int status) +{ + unsigned int protocol = (status >> 16) & 0x3; + if ((protocol == PROTO_IP) && (!(status & IPFail))) + return 1; + else if ((protocol == PROTO_TCP) && (!(status & TCPFail))) + return 1; + else if ((protocol == PROTO_UDP) && (!(status & UDPFail))) + return 1; + else + return 0; +} +#endif + +static void rtl8139_chip_reset (void *ioaddr) +{ + int i; + + /* Soft reset the chip. */ + RTL_W32 (NIC_CNR1, (RTL_R32 (NIC_CNR1)) | Reset); + + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) { + // barrier(); + if ((RTL_R32 (NIC_CNR1) & Reset) == 0) + break; + udelay (10); + } + RTL_W32 (NIC_CNR1, (RTL_R32 (NIC_CNR1)) &( ~Reset)); +} + +// Obviously, the job of init_one only to setup ioaddr, hardware reset. +// All the software initialization will be done in rtl8139_open +static int rtl8139_set_hwaddr(struct net_device *dev, void *addr) +{ + unsigned long flags, reg; + int i; + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + void *ioaddr = tp->mmio_addr; + unsigned char *p ; + p = ((struct sockaddr *)addr)->sa_data; + save_flags(flags); cli(); + + for (i = 0; i < 6; ++i){ + dev->dev_addr[i] = p[i]; + //printk("setup %s hw_address %d : %X\n", dev->name, i, p[i]); + } + + reg = *(unsigned long *)(dev->dev_addr); + //printk("programming NIC_ID0=%08X", cpu_to_le32(reg)); + RTL_W32(NIC_ID, (cpu_to_le32(reg))); + + reg = *(unsigned long *)((unsigned long)dev->dev_addr + 4); + reg = reg & 0xffff0000; + //printk("programming NIC_ID0 + 4=%08X", cpu_to_le32(reg)); + RTL_W32(NIC_ID + 4, (cpu_to_le32(reg))); + restore_flags(flags); + return 0; +} + + +static int __init rtl8139_init_one (int nic_nr) +{ + struct net_device *dev = NULL; + struct rtl8139_private *tp; + void *ioaddr = NULL; + + DPRINTK ("ENTER\n"); + + /* dev zeroed in init_etherdev */ + dev = init_etherdev (NULL, sizeof (*tp)); + if (dev == NULL) { + printk (KERN_ERR PFX "unable to alloc new ethernet\n"); + DPRINTK ("EXIT, returning -ENOMEM\n"); + return -ENOMEM; + } + tp = dev->priv; + // for MAC address + tp->MacAddr[0] = 0x00 ; + tp->MacAddr[1] = 0x11 ; + tp->MacAddr[2] = 0x22 + 4*nic_nr; + tp->MacAddr[3] = 0x33 ; + tp->MacAddr[4] = 0x44 ; + tp->MacAddr[5] = 0x00 ; + + memcpy(dev->dev_addr, tp->MacAddr, 6); + + if (nic_nr == 0) { + ioaddr = (void *)RTL8181_ETH0_ADDR; + dev->irq = RTL8181_ETH0_IRQ; + } else { + ioaddr = (void *)RTL8181_ETH1_ADDR; + dev->irq = RTL8181_ETH1_IRQ; + } + + //tp->nic_nr = nic_nr; + //assigned MAC address by driver, instead of reading eeprom + //* The Rtl8139-specific entries in the device structure. */ + dev->base_addr = (unsigned long) ioaddr; + dev->open = rtl8139_open; + dev->hard_start_xmit = rtl8139_start_xmit; + dev->stop = rtl8139_close; + dev->get_stats = rtl8139_get_stats; + dev->set_multicast_list = rtl8139_set_rx_mode; + dev->do_ioctl = mii_ioctl; + dev->tx_timeout = rtl8139_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + dev->set_mac_address = rtl8139_set_hwaddr; +#ifdef RTL8139AP_CHECKSUM_OFFLOAD + dev->features |= ( NETIF_F_IP_CSUM ); + //dev->features |= ( NETIF_F_HW_CSUM | NETIF_F_SG); +#endif + tp->mmio_addr = ioaddr; + tp->lock = SPIN_LOCK_UNLOCKED; + tp->phys[0] = 32; + + rtl8139_chip_reset(ioaddr); + + DPRINTK ("EXIT - returning 0\n"); + return 0; +} + +static void __init rtl8139_init_all (void) +{ + rtl8139_init_one(0); +#ifndef CONFIG_RTL8181_AP + rtl8139_init_one(1); +#endif +} + + +#if 0 +static void rtl8139_quick_reset(struct net_device *dev,struct rtl8139_private *tp, void *ioaddr) +{ + unsigned long flags; + netif_stop_queue (dev); + save_flags(flags);cli(); + rtl8139_txrx_stop (tp); + rtl8139_sw_free(tp); + rtl8139_sw_init(tp); + rtl8139_hw_start (dev); + restore_flags(flags); +} +#endif + +/* MII serial management: mostly bogus for now. */ +/* Read and write the MII management registers using software-generated + serial MDIO protocol. + The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues. */ +#define MDIO_DIR 0x80 +#define MDIO_DATA_OUT 0x04 +#define MDIO_DATA_IN 0x02 +#define MDIO_CLK 0x01 +#define MDIO_WRITE0 (MDIO_DIR) +#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) + +#define mdio_delay() readb(mdio_addr) + + +static void SetOwnByNic(unsigned int* header, int len, int own,int index) +{ + int tmp = (own | FIRSTSEG | LASTSEG | len) ; + + if ( index == NUM_DESC - 1) + tmp |= ENDOFRING; + + *(volatile unsigned long *)header = tmp; +} + + +// The job of this sub-routine is to allocate sw buffer for all purpose +static int rtl8139_sw_init(struct rtl8139_private *tp) +{ + int i; + unsigned char* page_ptr; + struct sk_buff *skb_ptr; + + page_ptr = kmalloc(PAGE_SIZE, GFP_KERNEL); + + // right now we have a 4096 bytes of free memory, allocate 2048 for rx/tx descriptors respectively + + //prom_printf("page_ptr=%08x\n", (unsigned int )page_ptr); + tp->rx_desc = (desc_t *) page_ptr; + tp->tx_desc = (desc_t *)(page_ptr + (PAGE_SIZE >> 1)); + + // initialize rx channel + tp->cur_rx = 0; + for (i=0; i< NUM_RX_DESC; i++) + { + tp->rx_desc[i].header = tp->rx_skb[i] = NULL; + skb_ptr = dev_alloc_skb(RX_BUF_LEN); + if (skb_ptr == NULL) + return 1; + skb_reserve(skb_ptr, 2); // to make tcp/ip happy + tp->rx_skb[i] = skb_ptr; + tp->rx_desc[i].addr = Virtual2Physical((int)(skb_ptr->data)); + tp->rx_desc[i].tag = 0; + tp->rx_desc[i].dummy = 0; + SetOwnByNic(&(tp->rx_desc[i].header),RX_BUF_LEN ,OWNBYNIC ,i); + //prom_printf("rx_skb[i].skb_ptr=%08x\n",(unsigned int)skb_ptr); +#if 0 + printk("rx_skb[i].skb_ptr=%08x, sizeof(struct skb_shared_info)=%08x\n", + (unsigned int)(skb_ptr->data), sizeof(struct skb_shared_info)); +#endif + } + atomic_set (&tp->dirty_tx, 0); + atomic_set (&tp->cur_tx, 0); + for(i=0; i< NUM_TX_DESC; i++) + { + SetOwnByNic(&(tp->tx_desc[i].header),RX_BUF_LEN ,0 ,i); + tp->tx_desc[i].tag=0; + tp->tx_desc[i].dummy=0; + tp->tx_desc[i].addr = 0; + } + + return 0; +} + + +static int rtl8139_sw_free(struct rtl8139_private *tp) +{ + int i; + // free rx channel + for(i=0; i< NUM_RX_DESC; i++) + { + if (tp->rx_skb[i]) + dev_kfree_skb (tp->rx_skb[i]); + // Clear Own By Nic + SetOwnByNic(&(tp->rx_desc[i].header),RX_BUF_LEN ,0 ,i); + } + // free tx channel + for(i=0; i< NUM_TX_DESC; i++) + { + if (tp->tx_skb[i]) + dev_kfree_skb(tp->tx_skb[i]); + // Clear Own By Nic + SetOwnByNic(&(tp->tx_desc[i].header),RX_BUF_LEN ,0 ,i); + } + kfree(tp->rx_desc); + return 0; +} + +static int rtl8139_open (struct net_device *dev) +{ + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + int retval; + + DPRINTK ("ENTER\n"); + + MOD_INC_USE_COUNT; + + retval = request_irq (dev->irq, rtl8139_interrupt, SA_INTERRUPT, dev->name, dev); + if (retval) { + DPRINTK ("EXIT, returning %d\n", retval); + MOD_DEC_USE_COUNT; + return retval; + } + + //sw init + if (rtl8139_sw_init(tp)) + { + printk("sw init %s error! no memory!\n",dev->name); + return -ENOMEM; + } + + tp->full_duplex = tp->duplex_lock; + rtl8139_hw_start (dev); + +#ifdef REALHACK_RECV + /* Set the timer to switch to check for link beat and perhaps switch + to an alternate media type. */ + init_timer (&tp->timer); + tp->timer.expires = jiffies + 5; + tp->timer.data = (unsigned long) dev; + tp->timer.function = &rtl8139_timer; + + init_timer (&tp->rxtimer); + tp->rxtimer.expires = jiffies + 10; + tp->rxtimer.data = (unsigned long) dev; + tp->rxtimer.function = &rtl8139_rxtimer; +#endif + +#ifdef REALHACK_IMTR + init_timer (&tp->imtrtimer); + tp->imtrtimer.expires = jiffies + 400 ; + tp->imtrtimer.data = (unsigned long) dev; + tp->imtrtimer.function = &rtl8139_imtrtimer; + mod_timer(&tp->imtrtimer, jiffies + 400); +#endif + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + +static void rtl8139_txrx_stop (struct rtl8139_private *tp) +{ + void *ioaddr = tp->mmio_addr; + + RTL_W32 (NIC_CNR1, (RTL_R32 (NIC_CNR1)) & ((~RxEnable) | (~TxEnable)) ); + udelay (100); +} + +/* Start the hardware at open or resume. */ +static void rtl8139_hw_start (struct net_device *dev) +{ + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + void *ioaddr = tp->mmio_addr; + + DPRINTK ("ENTER\n"); + + /* Stop txrx first */ + rtl8139_txrx_stop (tp); + rtl8139_chip_reset(ioaddr); + +/* Force MII register to reflect the current status of phy */ +#ifdef CONFIG_PHYTYPE_BYFLASH + if (((*(volatile unsigned short *)(0xbfc06006)) >> 8) & 0x01) { + printk("PHY type (by flash) is 8305SB\n"); + if (ioaddr == (void *) RTL8181_ETH0_ADDR) + RTL_W32(NIC_MII, MII_PHY8305_ETH0); + else + RTL_W32(NIC_MII, MII_PHY8305_ETH1); + } else { + printk("PHY type (by flash) is 8201BL\n"); + RTL_W32(NIC_MII, MII_PHY8201); + } +#else + +#ifdef CONFIG_RTL8181_8305SB //Wireless Router Mode + printk("PHY type (hardcoded) is 8305SB\n"); + if (ioaddr == (void *) RTL8181_ETH0_ADDR) + RTL_W32(NIC_MII, MII_PHY8305_ETH0); + else + RTL_W32(NIC_MII, MII_PHY8305_ETH1); +#else + printk("PHY type (hardcoded) is 8201BL\n"); + RTL_W32(NIC_MII, MII_PHY8201); +#endif + +#endif /* CONFIG_PHYTYPE_BYFLASH */ + + if (ioaddr == (void *) RTL8181_ETH0_ADDR) + RTL_W32_F (NIC_ID + 0, 0x33221100); + else + RTL_W32_F (NIC_ID + 0, 0x33261100); + + RTL_W32_F (NIC_ID + 4, 0x00000044); + + /* Must enable Tx/Rx before setting transfer thresholds! */ + RTL_W32_F (NIC_CNR1, RxEnable | TxEnable ); + RTL_W32_F (NIC_CNR1, RTL_R32(NIC_CNR1) | rtl8139_trx_config); + + rtl8139_set_rx_mode (dev); + + /* fill the base address of tx/rx descriptors! */ + RTL_W32_F(NIC_TSAD,Virtual2Physical(tp->tx_desc)); + RTL_W32_F(NIC_RSAD,Virtual2Physical(tp->rx_desc)); + + tp->intr_mask = rtl8139_intr_mask; + + /* Enable all known interrupts by setting the interrupt mask. */ + RTL_W32_F(NIC_IMR, tp->intr_mask); + RTL_W32(NIC_RXERR, 0x0); + netif_start_queue (dev); + + DPRINTK ("EXIT\n"); +} + +#ifdef REALHACK_RECV +static void rtl8139_timer (unsigned long data) +{ + unsigned long flags; + struct net_device *dev = (struct net_device *)data; + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + void *ioaddr = tp->mmio_addr; + int this_cpu = smp_processor_id(); + // we should query to check if now the rx queue is okay for accepting + //new skb + get_sample_stats(this_cpu); + //if (softnet_data[this_cpu].cng_level != NET_RX_SUCCESS) + if (softnet_data[this_cpu].cng_level == NET_RX_DROP) + { + //printk("timer check shows still congested network traffic for %s\n" + //, dev->name); + mod_timer(&tp->timer, jiffies + 5); + } else { + //printk("timer check success!\n"); + save_flags(flags);cli(); + tp->netrxdrop = FALSE; + //RTL_W32 (NIC_CNR1, (RTL_R32 (NIC_CNR1)) | (RxEnable) ); + RTL_W32(NIC_IMR, tp->intr_mask); + restore_flags(flags); + } +} + +// This is for the case where RxFFOv happens +// We have disabled RxInterrupt for a while +// Therefore, we should re-open it again +static void rtl8139_rxtimer (unsigned long data) +{ + unsigned long flags; + struct net_device *dev = (struct net_device *)data; + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + volatile void *ioaddr = tp->mmio_addr; + + save_flags(flags); cli(); + tp->intr_mask |= (RxDescUnMask | RxFFOvMask); + RTL_W32_F(NIC_IMR, tp->intr_mask); + RTL_W32_F(NIC_ISR, (RxDescUnMask | RxFFOvMask)); + restore_flags(flags); +} +#endif /* REALHACK_RECV */ + +#ifdef REALHACK_IMTR +static void rtl8139_imtrtimer (unsigned long data) +{ + unsigned long flags; + struct net_device *dev = (struct net_device *)data; + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + volatile void *ioaddr = tp->mmio_addr; + unsigned int regval; + save_flags(flags);cli(); + regval = RTL_R32(NIC_IMTR); + if (tp->rxinterrupt_count >= 3600) + { + if (regval != 0xAAAA) + { + RTL_W32(NIC_IMTR, 0xAA88); + //printk("tuning IMTR to 0xAAAA with ioaddr=%X\n", ioaddr); + } + } + else + { + if (regval != 0xAA00) + { + RTL_W32(NIC_IMTR, 0xAA00); + //printk("tuning IMTR to 0xAA00 with ioaddr=%X\n", ioaddr); + } + } + tp->rxinterrupt_count=0; + restore_flags(flags); + mod_timer(&tp->imtrtimer, jiffies + 1000); +} +#endif /* REALHACK_IMTR */ + +static void rtl8139_tx_timeout (struct net_device *dev) +{ + + printk("tx_timeout ! on dev %s\n", dev->name); +#if 0 + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + void *ioaddr = tp->mmio_addr; + int i; + unsigned long flags; + + DPRINTK ("%s: Transmit timeout, status %2.2x %4.4x " + "media %2.2x.\n", dev->name, + RTL_R8 (NIC_CNR1), + RTL_R16 (IntrStatus), + RTL_R8 (MediaStatus)); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16 (NIC_IMR, 0x0000); + + /* Emit info to figure out what went wrong. */ + printk (KERN_DEBUG "%s: Tx queue start entry %d dirty entry %d.\n", + dev->name, atomic_read (&tp->cur_tx), + atomic_read (&tp->dirty_tx)); + for (i = 0; i < NUM_TX_DESC; i++) + printk (KERN_DEBUG "%s: Tx descriptor %d is %8.8x.%s\n", + dev->name, i, RTL_R32 (TxStatus0 + (i * 4)), + i == atomic_read (&tp->dirty_tx) % NUM_TX_DESC ? + " (queue head)" : ""); + + spin_lock_irqsave (&tp->lock, flags); + + /* Stop a shared interrupt from scavenging while we are. */ + atomic_set (&tp->cur_tx, 0); + atomic_set (&tp->dirty_tx, 0); + + /* Dump the unsent Tx packets. */ + for (i = 0; i < NUM_TX_DESC; i++) { + struct ring_info *rp = &tp->tx_info[i]; + if (rp->mapping != 0) { + pci_unmap_single (tp->pci_dev, rp->mapping, + rp->skb->len, PCI_DMA_TODEVICE); + rp->mapping = 0; + } + if (rp->skb) { + dev_kfree_skb (rp->skb); + rp->skb = NULL; + tp->stats.tx_dropped++; + } + } + + spin_unlock_irqrestore (&tp->lock, flags); + + /* ...and finally, reset everything */ + rtl8139_hw_start (dev); +#endif +} + + + +static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) +{ + unsigned long flags; + int txmit_count; + int cur_tx, dirty_tx; + unsigned int own; + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + void *ioaddr = tp->mmio_addr; + + //save_flags(flags);cli(); + cur_tx = atomic_read(&tp->cur_tx) & (NUM_TX_DESC - 1); + dirty_tx = atomic_read(&tp->dirty_tx) & (NUM_TX_DESC - 1); + if (((cur_tx + 1) & (NUM_TX_DESC - 1)) == dirty_tx) + txmit_count = 0; + else + txmit_count = 1; + //if ((txmit_count=(CIRC_SPACE(cur_tx, dirty_tx, NUM_TX_DESC)))) + if (txmit_count) + { + // Great! we have free tx descriptors! + own = OWNBYNIC; + tp->tx_skb[cur_tx]=skb; + *(volatile unsigned int *)(&(tp->tx_desc[cur_tx].addr)) = Virtual2Physical(skb->data); +#if 0 + if ((tp->tx_desc[cur_tx].header) & 0x80000000) + printk("bug in start_xmit! cur_tx=%08x, dirty_tx=%08x\n", + cur_tx, dirty_tx); +#endif + +#ifdef RTL8139AP_CHECKSUM_OFFLOAD + if (skb->ip_summed == CHECKSUM_HW) + { + const struct iphdr *ip = skb->nh.iph; + + printk("tx offload,ip->protocol=%08x\n",ip->protocol); + if ((ip->protocol == IPPROTO_TCP) || + (ip->protocol == IPPROTO_UDP)) { + if (ip->protocol == IPPROTO_TCP) + own |= (IpTxCsumEn) | (TcpTxCsumEn); + else + own |= (IpTxCsumEn) | (UdpTxCsumEn); + } + } +#endif + SetOwnByNic(&(tp->tx_desc[cur_tx].header),skb->len ,own,cur_tx); + + // Remember to Poll Tx Registers!!! + RTL_W32( NIC_CNR1, RTL_R32(NIC_CNR1) | TxDescFN); + //RTL_R32( NIC_CNR1); +#ifdef RTL8139_EARLY_FREE + dev_kfree_skb(skb); +#endif + + dev->trans_start = jiffies; + cur_tx++; + cur_tx &= (NUM_TX_DESC - 1); + atomic_set (&tp->cur_tx, cur_tx); + } + else + { + // No more tx descriptors allowed! + netif_stop_queue (dev); + //printk("stop_tx_queue!\n"); + if (skb) + dev_kfree_skb(skb); + else + printk("tx a null queue?\n"); + } + /* Note: the chip doesn't have auto-pad! */ + + //restore_flags(flags); + return 0; +} + +// The purpose of this tx_interrupt: +// 1. Free the skb that has been sent. +// atomic increase dirty_tx; +static __inline__ void rtl8139_tx_interrupt (struct net_device *dev) +{ + unsigned int tx_header; + unsigned int tx_header_addr; + int tx_count,cur_tx,dirty_tx; + struct rtl8139_private *tp = dev->priv; + + // if we can make sure this handler occurred in ISR, + // then we do'nt need to use atomic operation + dirty_tx = atomic_read (&tp->dirty_tx); + cur_tx = atomic_read (&tp->cur_tx); + tx_count = CIRC_CNT(cur_tx, dirty_tx, NUM_TX_DESC); + while ( tx_count-- ) + //while (1) + { + tx_header_addr=(unsigned int)(&(tp->tx_desc[dirty_tx].header)) | 0x20000000; + tx_header = *(volatile unsigned int *)(tx_header_addr); + + if (tx_header & OWNBYNIC) + break; + tp->stats.tx_bytes += ((tx_header) & (0xfff)) ; + if (tx_header & TXERR) + { + tp->stats.tx_errors++; + if (tx_header & TXERR_OUTOFWIN) + tp->stats.tx_window_errors++; + if (tx_header & TXERR_LINKF) + tp->stats.tx_carrier_errors++; + if (tx_header & TXERR_COLLISION) + tp->stats.collisions += (tx_header >> 16) & (0xf); + } + else + { + tp->stats.tx_packets++; + } +#if 0 + // The code below is for checking if Linux will use scatter-list for tx_skb + if ((tp->tx_skb[dirty_tx])->next) + printk("Non null?\n"); +#endif + + //Free skb +#ifndef RTL8139_EARLY_FREE + dev_kfree_skb_irq(tp->tx_skb[dirty_tx]); +#endif + tp->tx_skb[dirty_tx]=NULL; + dirty_tx++;dirty_tx &= (NUM_TX_DESC - 1); + } + + if (netif_queue_stopped(dev) && CIRC_SPACE(cur_tx, dirty_tx, NUM_TX_DESC)) + netif_wake_queue(dev); + atomic_set (&tp->dirty_tx, dirty_tx); +} + + + +/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the + field alignments and semantics. */ +static __inline__ void rtl8139_rx_interrupt (struct net_device *dev) +{ + int k, netreturn; + volatile int cur_rx; + unsigned int rxheader; + struct sk_buff* skb_ptr; // skb_ptr is used to re-new the desc. + volatile struct sk_buff* rx_ptr; // rx_ptr is used to post to os(network layer) + unsigned int header_addr; + unsigned int flush_cache; + struct rtl8139_private *tp = dev->priv; + // void *ioaddr = tp->mmio_addr; + + cur_rx = tp->cur_rx; + flush_cache=0; + tp->rxinterrupt_count++; + + for(k=0; k< MAX_RX_INTERRUPT_WORK; k++) + { + rx_ptr=(struct sk_buff *)0; + // Check each descriptor's header, by using physical address + header_addr = (unsigned int)(&(tp->rx_desc[cur_rx].header)); + rxheader = *(volatile unsigned int *)(header_addr | 0xa0000000); + if (rxheader & OWNBYNIC) + break; + + if (rxheader & RXERR) + { + //printk("Rx ErrPkt on %s, rxheader=%08x,header_addr=%08x cur_rx=%08x \n", dev->name, rxheader,header_addr,cur_rx); + // just ignore this pkt + // re-use the descriptor + skb_ptr = tp->rx_skb[cur_rx]; + } + else if (tp->netrxdrop) + //else if (softnet_data[0].input_pkt_queue.qlen >= 100) + { + skb_ptr = tp->rx_skb[cur_rx]; + //printk("qlen >=100, stop queuing\n"); + } + else if (((rxheader & RXLENMASK) - 4) > 1520) + { + //printk("weird rxheader=%08x\n", rxheader); + skb_ptr = tp->rx_skb[cur_rx]; + } + else + { + rx_ptr = tp->rx_skb[cur_rx]; + rx_ptr->dev=dev; + skb_ptr = dev_alloc_skb(RX_BUF_LEN); + if (skb_ptr == NULL) + { + + printk("System out of skbuff for %s, k=%d\n",dev->name,k); + skb_ptr = rx_ptr; + rx_ptr = (struct sk_buff *)NULL; + } + else + { + skb_reserve(skb_ptr, 2); // to make tcp/ip happy + tp->rx_skb[cur_rx] = skb_ptr; + skb_ptr->dev=dev; + } + } + + *(volatile unsigned long *)(&(tp->rx_desc[cur_rx].addr)) = Virtual2Physical((int)(skb_ptr->data)); + // Renew this descriptor + SetOwnByNic(&(tp->rx_desc[cur_rx].header),RX_BUF_LEN ,OWNBYNIC ,cur_rx); + cur_rx ++; + cur_rx &= (NUM_RX_DESC - 1); + + if (rx_ptr) + { + flush_cache++; +#ifdef RTL8139AP_CHECKSUM_OFFLOAD + if (rx_csum_ok(rxheader)) + rx_ptr->ip_summed = CHECKSUM_UNNECESSARY; + else + rx_ptr->ip_summed = CHECKSUM_NONE; +#endif + skb_put (rx_ptr, (rxheader & RXLENMASK) - 4); + rx_ptr->protocol = eth_type_trans (rx_ptr, dev); + if ((netreturn = netif_rx(rx_ptr)) == NET_RX_DROP) + { +#ifdef REALHACK_RECV + //printk("NetIf =%08x , halting post skb to os \n",netreturn); + tp->netrxdrop=TRUE; + //RTL_W32 (NIC_CNR1, (RTL_R32 (NIC_CNR1)) & ((~RxEnable))); + mod_timer(&tp->timer, jiffies + 10); +#else + printk("NET_RX_DROP on %s\n", dev->name); +#endif + } + else + tp->netrxdrop=FALSE; + + tp->stats.rx_bytes += (rxheader & RXLENMASK) - 4 ; + tp->stats.rx_packets++; + } + } + tp->cur_rx = cur_rx; +} + + + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +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 = (struct rtl8139_private *) dev->priv; + void *ioaddr = tp->mmio_addr; + int status = 0; /* avoid bogus "uninit" warning */ + + spin_lock (&tp->lock); + + status = RTL_R32 (NIC_ISR); + // RTL_W32_F (NIC_ISR, status); /* Moved to end of function */ + // status &= tp->intr_mask; + + /* Check uncommon events with one test. */ + if (status & (RxErrStats)) + printk("RxErr on %s\n", dev->name); + if (status & (TxErrStats)) + printk("TxErr on %s\n", dev->name); + if (status & (RxDescUnStats)) + printk("RxDescUn on %s\n", dev->name); + if (status & (RxFFOvMask)) + { + // Does this imply Lexra Bus is hardly occupied by NIC ? + // Should we adjust burst length case by case? + // This is partially because of non-consistency of hardware and + // software rx_cur mechanism. Try to reset hardware and software. + //rtl8139_quick_reset(dev,tp,ioaddr); + + printk("RxFFOv on %s\n", dev->name); + +#ifdef REALHACK_RECV + tp->intr_mask &= ~(RxFFOvMask | RxDescUnMask); + RTL_W32_F(NIC_IMR, tp->intr_mask); + mod_timer(&tp->rxtimer, jiffies + 9); +#endif + } + + if (status & (TxOkStats | TxDescUnStats)) + rtl8139_tx_interrupt(dev); + + if (status & (RxOkStats | RxDescUnStats | RxFFOvStats )) /* Rx interrupt */ + rtl8139_rx_interrupt(dev); + + RTL_W32_F (NIC_ISR, status); + spin_unlock (&tp->lock); +} + + +static int rtl8139_close (struct net_device *dev) +{ + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + void *ioaddr = tp->mmio_addr; + unsigned long flags; + printk("before close, nr_free_pages=%08x\n", nr_free_pages()); + DPRINTK ("ENTER\n"); + + netif_stop_queue (dev); + +#ifdef REALHACK_RECV + del_timer_sync (&tp->timer); + del_timer_sync (&tp->rxtimer); +#endif +#ifdef REALHACK_IMTR + del_timer_sync (&tp->imtrtimer); +#endif + + spin_lock_irqsave (&tp->lock, flags); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W32 (NIC_IMR, 0x0000); + + /* Stop the chip's Tx and Rx DMA processes. */ + RTL_W32 (NIC_CNR1, (RTL_R32 (NIC_CNR1) & ((~RxEnable) | (~TxEnable)))); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += (RTL_R32 (NIC_RXERR) & 0xffff); + RTL_W32 (NIC_RXERR, 0); + + spin_unlock_irqrestore (&tp->lock, flags); + + /* snooze for a small bit */ + if (current->need_resched) + schedule (); + + free_irq (dev->irq, dev); + + rtl8139_sw_free(tp); + /* Green! Put the chip in low-power mode. */ + MOD_DEC_USE_COUNT; + + printk("after close, nr_free_pages=%08x\n", nr_free_pages()); + DPRINTK ("EXIT\n"); + return 0; +} + + +static int mii_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ +#if 0 + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + u16 *data = (u16 *) & rq->ifr_data; + unsigned long flags; + int rc = 0; + + DPRINTK ("ENTER\n"); + + switch (cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = tp->phys[0] & 0x3f; + /* Fall Through */ + + case SIOCDEVPRIVATE + 1: /* Read the specified MII register. */ + spin_lock_irqsave (&tp->lock, flags); + data[3] = mdio_read (dev, data[0], data[1] & 0x1f); + spin_unlock_irqrestore (&tp->lock, flags); + break; + + case SIOCDEVPRIVATE + 2: /* Write the specified MII register */ + if (!capable (CAP_NET_ADMIN)) { + rc = -EPERM; + break; + } + + spin_lock_irqsave (&tp->lock, flags); + mdio_write (dev, data[0], data[1] & 0x1f, data[2]); + spin_unlock_irqrestore (&tp->lock, flags); + break; + + default: + rc = -EOPNOTSUPP; + break; + } + + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +#endif + return -EOPNOTSUPP; +} + + +static struct net_device_stats *rtl8139_get_stats (struct net_device *dev) +{ + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + void *ioaddr = tp->mmio_addr; + + DPRINTK ("ENTER\n"); + + assert (tp != NULL); + + if (netif_running(dev)) { + unsigned long flags; + + spin_lock_irqsave (&tp->lock, flags); + + tp->stats.rx_missed_errors += RTL_R32 (NIC_RXERR) & 0xffff; + RTL_W32 (NIC_RXERR, 0); + + spin_unlock_irqrestore (&tp->lock, flags); + } + + DPRINTK ("EXIT\n"); + 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; + + DPRINTK ("ENTER\n"); + + 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); + } + + DPRINTK ("EXIT\n"); + return crc; +} + + +static void rtl8139_set_rx_mode (struct net_device *dev) +{ + struct rtl8139_private *tp = (struct rtl8139_private *) dev->priv; + void *ioaddr = tp->mmio_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + int i, rx_mode; + u32 tmp; + + DPRINTK ("ENTER\n"); +#if 0 + DPRINTK ("%s: rtl8139_set_rx_mode(%4.4x) done -- Rx config %8.8x.\n", + dev->name, dev->flags, RTL_R32 (RxConfig)); +#endif + /* 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 = AB | AM | AMP | AAP; + 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 = AB | AM| AMP; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else { + struct dev_mc_list *mclist; + rx_mode = AB | AM | AMP; + 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); + } + + /* if called from irq handler, lock already acquired */ + if (!in_irq ()) + spin_lock_irq (&tp->lock); + + /* We can safely update without stopping the chip. */ + tmp = rtl8139_trx_config | rx_mode | TxEnable | RxEnable ; + RTL_W32_F (NIC_CNR1, tmp); + RTL_W32_F (NIC_MAR + 0, mc_filter[0]); + RTL_W32_F (NIC_MAR + 4, mc_filter[1]); + + if (!in_irq ()) + spin_unlock_irq (&tp->lock); + + DPRINTK ("EXIT\n"); +} + +module_init(rtl8139_init_all); diff -u -r --new-file linux-2.4.18-sdk/Makefile linux-2.4.18/Makefile --- linux-2.4.18-sdk/Makefile 2004-02-16 12:44:41.000000000 +0100 +++ linux-2.4.18/Makefile 2004-05-22 20:48:41.000000000 +0200 @@ -198,6 +198,7 @@ DRIVERS-$(CONFIG_MD) += drivers/md/mddev.o DRIVERS-$(CONFIG_BLUEZ) += drivers/bluetooth/bluetooth.o DRIVERS-$(CONFIG_RTL8139CP) += drivers/net/rtl8181/rtl8181.o +DRIVERS-$(CONFIG_RTL8139EMB) += drivers/net/8139emb.o DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o DRIVERS := $(DRIVERS-y)