您的位置:首页 > 运维架构 > Linux

Linux 网卡驱动_04

2015-07-11 20:15 671 查看
rtl8139 网卡驱动分析:(注:这里只分析了主要函数,代码版本是:
8139too.c71420 bytes2010-04-01 22:56:18


/*
* 提供了一系列驱动程序可调用的接口,主要用于发现和初始化设备
*/
static struct pci_driver rtl8139_pci_driver = {
.name           = DRV_NAME,
.id_table       = rtl8139_pci_tbl,
.probe          = rtl8139_init_one,
.remove         = __devexit_p(rtl8139_remove_one),
#ifdef CONFIG_PM
.suspend        = rtl8139_suspend,
.resume         = rtl8139_resume,
#endif /* CONFIG_PM */
};

static int __devinit rtl8139_init_one (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct net_device *dev = NULL;
struct rtl8139_private *tp;
int i, addr_len, option;
void __iomem *ioaddr;
static int board_idx = -1;

assert (pdev != NULL);
assert (ent != NULL);

board_idx++;

/* when we're built into the kernel, the driver version message
* is only printed if at least one 8139 board has been found
*/
#ifndef MODULE
{
static int printed_version;
if (!printed_version++)
pr_info(RTL8139_DRIVER_NAME "\n");
}
#endif

if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pdev->revision >= 0x20) {
dev_info(&pdev->dev,
"This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip, use 8139cp\n",
pdev->vendor, pdev->device, pdev->revision);
return -ENODEV;
}

if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&
pdev->device == PCI_DEVICE_ID_REALTEK_8139 &&
pdev->subsystem_vendor == PCI_VENDOR_ID_ATHEROS &&
pdev->subsystem_device == PCI_DEVICE_ID_REALTEK_8139) {
pr_info("8139too: OQO Model 2 detected. Forcing PIO\n");
use_io = 1;
}

dev = rtl8139_init_board (pdev);//返回了一个struct net_device 对象的指针
if (IS_ERR(dev))
return PTR_ERR(dev);

assert (dev != NULL);
tp = netdev_priv(dev);//获取私有结构的指针
tp->dev = dev;//关联rtl8139_init_board 返回的 net_device 对象指针,便于操作

ioaddr = tp->mmio_addr;//获取虚拟io地址,便于操作
assert (ioaddr != NULL);

/*读取MAC地址*/
addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6;
for (i = 0; i < 3; i++)//macaddr 6 byte, each time 2byte ,so three times is ok
((__le16 *) (dev->dev_addr))[i] =
cpu_to_le16(read_eeprom (ioaddr, i + 7, addr_len));
memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);//保存MAC地址

/* The Rtl8139-specific entries in the device structure. */
dev->netdev_ops = &rtl8139_netdev_ops; //关联net_device的操作
dev->ethtool_ops = &rtl8139_ethtool_ops;
dev->watchdog_timeo = TX_TIMEOUT;
netif_napi_add(dev, &tp->napi, rtl8139_poll, 64);
/*注册轮询函数,64为一次处理的帧数,如果一个处理的帧数小于这个数字,则使用中断的方式,否则使用轮询的方式*/

/* note: the hardware is not capable of sg/csum/highdma, however
* through the use of skb_copy_and_csum_dev we enable these
* features
*/
dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA;

dev->irq = pdev->irq;//关联irq,方便在request_irq 函数中的操作

/* tp zeroed and aligned in alloc_etherdev */
tp = netdev_priv(dev);

/* note: tp->chipset set in rtl8139_init_board */
tp->drv_flags = board_info[ent->driver_data].hw_flags;
tp->mmio_addr = ioaddr;
tp->msg_enable =
(debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1));
spin_lock_init (&tp->lock);
spin_lock_init (&tp->rx_lock);
INIT_DELAYED_WORK(&tp->thread, rtl8139_thread);
tp->mii.dev = dev;
tp->mii.mdio_read = mdio_read;
tp->mii.mdio_write = mdio_write;
tp->mii.phy_id_mask = 0x3f;
tp->mii.reg_num_mask = 0x1f;

/* dev is fully set up and ready to use now */
pr_debug("about to register device named %s (%p)...\n", dev->name, dev);
i = register_netdev (dev);//注册网络设备
if (i) goto err_out;

pci_set_drvdata (pdev, dev);
/*把网络设备指针地址放入PCI设备中的设备指针中,便于remove的时候使用
* pci_get_drvdata 获取该网络设备
*/

pr_info("%s: %s at 0x%lx, %pM, IRQ %d\n",
dev->name,
board_info[ent->driver_data].name,
dev->base_addr,
dev->dev_addr,
dev->irq);

pr_debug("%s:  Identified 8139 chip type '%s'\n",
dev->name, rtl_chip_info[tp->chipset].name);

/* Find the connected MII xcvrs.
Doing this in open() would allow detecting external xcvrs later, but
takes too much time. */
#ifdef CONFIG_8139TOO_8129
if (tp->drv_flags & HAS_MII_XCVR) {
int phy, phy_idx = 0;
for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) {
int mii_status = mdio_read(dev, phy, 1);
if (mii_status != 0xffff  &&  mii_status != 0x0000) {
u16 advertising = mdio_read(dev, phy, 4);
tp->phys[phy_idx++] = phy;
pr_info("%s: MII transceiver %d status 0x%4.4x advertising %4.4x.\n",
dev->name, phy, mii_status, advertising);
}
}
if (phy_idx == 0) {
pr_info("%s: No MII transceivers found! Assuming SYM transceiver.\n",
dev->name);
tp->phys[0] = 32;
}
} else
#endif
tp->phys[0] = 32;
tp->mii.phy_id = tp->phys[0];

/* The lower four bits are the media type. */
option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx];
if (option > 0) {
tp->mii.full_duplex = (option & 0x210) ? 1 : 0;
tp->default_port = option & 0xFF;
if (tp->default_port)
tp->mii.force_media = 1;
}
if (board_idx < MAX_UNITS  &&  full_duplex[board_idx] > 0)
tp->mii.full_duplex = full_duplex[board_idx];
if (tp->mii.full_duplex) {
pr_info("%s: Media type forced to Full Duplex.\n", dev->name);
/* Changing the MII-advertised media because might prevent
re-connection. */
tp->mii.force_media = 1;
}
if (tp->default_port) {
pr_info("  Forcing %dMbps %s-duplex operation.\n",
(option & 0x20 ? 100 : 10),
(option & 0x10 ? "full" : "half"));
mdio_write(dev, tp->phys[0], 0,
((option & 0x20) ? 0x2000 : 0) |     /* 100Mbps? */
((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
}

/* Put the chip into low-power mode. */
if (rtl_chip_info[tp->chipset].flags & HasHltClk)
RTL_W8 (HltClk, 'H');   /* 'R' would leave the clock running. */

return 0;

err_out:
__rtl8139_cleanup_dev (dev);
pci_disable_device (pdev);
return i;
}

static __devinit struct net_device * rtl8139_init_board (struct pci_dev *pdev)
{
void __iomem *ioaddr;
struct net_device *dev;
struct rtl8139_private *tp;
u8 tmp8;
int rc, disable_dev_on_err = 0;
unsigned int i;
unsigned long pio_start, pio_end, pio_flags, pio_len;
unsigned long mmio_start, mmio_end, mmio_flags, mmio_len;
u32 version;

assert (pdev != NULL);

/* dev and priv zeroed in alloc_etherdev */
dev = alloc_etherdev (sizeof (*tp));
/*注意到这里分配空间传入的参数是 struct rtl8139rtl 的大小,其实也就是在创建 struct net_device 的同时给 指向 private 的指针分配了空间*/
if (dev == NULL) {
dev_err(&pdev->dev, "Unable to alloc new net device\n");
return ERR_PTR(-ENOMEM);
}
SET_NETDEV_DEV(dev, &pdev->dev);
/*#define SET_NETDEV_DEV(net, pdev)    ((net)->dev.parent = (pdev))*/

/*netdev_priv - access network device private data*/
tp = netdev_priv(dev);
tp->pci_dev = pdev;

/* enable device (incl. PCI PM wakeup and hotplug setup)
相当于windows驱动中发送IRP_MN_START_DEVICE 类型IRP 包给PDO处理
*/
rc = pci_enable_device (pdev);
if (rc)
goto err_out;
/*读取PCI配置空间信息*/
pio_start = pci_resource_start (pdev, 0);
pio_end = pci_resource_end (pdev, 0);
pio_flags = pci_resource_flags (pdev, 0);
pio_len = pci_resource_len (pdev, 0);

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);

/* set this immediately, we need to know before
* we talk to the chip directly */
pr_debug("PIO region size == 0x%02lX\n", pio_len);
pr_debug("MMIO region size == 0x%02lX\n", mmio_len);

retry:
if (use_io) {
/* make sure PCI base addr 0 is PIO */
if (!(pio_flags & IORESOURCE_IO)) {
dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n");
rc = -ENODEV;
goto err_out;
}
/* check for weird/broken PCI region reporting */
if (pio_len < RTL_MIN_IO_SIZE) {
dev_err(&pdev->dev, "Invalid PCI I/O region size(s), aborting\n");
rc = -ENODEV;
goto err_out;
}
} else {
/* make sure PCI base addr 1 is MMIO */
if (!(mmio_flags & IORESOURCE_MEM)) {
dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n");
rc = -ENODEV;
goto err_out;
}
if (mmio_len < RTL_MIN_IO_SIZE) {
dev_err(&pdev->dev, "Invalid PCI mem region size(s), aborting\n");
rc = -ENODEV;
goto err_out;
}
}
/*登记该空间已经分配给了特定对象,不能再次分配给其它设备*/
rc = pci_request_regions (pdev, DRV_NAME);
if (rc)
goto err_out;
disable_dev_on_err = 1;

/* enable PCI bus-mastering */
pci_set_master (pdev);
/*跟ioremap类似的功能,将板上硬件物理地址映射为虚拟地址*/
if (use_io) {
ioaddr = pci_iomap(pdev, 0, 0);
if (!ioaddr) {
dev_err(&pdev->dev, "cannot map PIO, aborting\n");
rc = -EIO;
goto err_out;
}
dev->base_addr = pio_start;
tp->regs_len = pio_len;
} else {
/* ioremap MMIO region */
ioaddr = pci_iomap(pdev, 1, 0);
if (ioaddr == NULL) {
dev_err(&pdev->dev, "cannot remap MMIO, trying PIO\n");
pci_release_regions(pdev);
use_io = 1;
goto retry;
}
dev->base_addr = (long) ioaddr;
tp->regs_len = mmio_len;
}
tp->mmio_addr = ioaddr;//在rtl8139_private 结构中保存该虚拟地址,用于io操作

/* Bring old chips out of low-power mode. */
RTL_W8 (HltClk, 'R');

/* check for missing/broken hardware */
if (RTL_R32 (TxConfig) == 0xFFFFFFFF) {
dev_err(&pdev->dev, "Chip not responding, ignoring board\n");
rc = -EIO;
goto err_out;
}

/* identify chip attached to board */
version = RTL_R32 (TxConfig) & HW_REVID_MASK;
for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++)
if (version == rtl_chip_info[i].version) {
tp->chipset = i;
goto match;
}

/* if unknown chip, assume array element #0, original RTL-8139 in this case */
dev_dbg(&pdev->dev, "unknown chip version, assuming RTL-8139\n");
dev_dbg(&pdev->dev, "TxConfig = 0x%lx\n", RTL_R32 (TxConfig));
tp->chipset = 0;

match:
pr_debug("chipset id (%d) == index %d, '%s'\n",
version, i, rtl_chip_info[i].name);

if (tp->chipset >= CH_8139B) {
u8 new_tmp8 = tmp8 = RTL_R8 (Config1);
pr_debug("PCI PM wakeup\n");
if ((rtl_chip_info[tp->chipset].flags & HasLWake) &&
(tmp8 & LWAKE))
new_tmp8 &= ~LWAKE;
new_tmp8 |= Cfg1_PM_Enable;
if (new_tmp8 != tmp8) {
RTL_W8 (Cfg9346, Cfg9346_Unlock);
RTL_W8 (Config1, tmp8);
RTL_W8 (Cfg9346, Cfg9346_Lock);
}
if (rtl_chip_info[tp->chipset].flags & HasLWake) {
tmp8 = RTL_R8 (Config4);
if (tmp8 & LWPTN) {
RTL_W8 (Cfg9346, Cfg9346_Unlock);
RTL_W8 (Config4, tmp8 & ~LWPTN);
RTL_W8 (Cfg9346, Cfg9346_Lock);
}
}
} else {
pr_debug("Old chip wakeup\n");
tmp8 = RTL_R8 (Config1);
tmp8 &= ~(SLEEP | PWRDN);
RTL_W8 (Config1, tmp8);
}

rtl8139_chip_reset (ioaddr);

return dev;

err_out:
__rtl8139_cleanup_dev (dev);
if (disable_dev_on_err)
pci_disable_device (pdev);
return ERR_PTR(rc);
}
/*
*8139网卡的有⼀个接收缓冲寄存器,用于存放接收缓存的首地址,网
*卡⼀边把网线上的发出的数据放到内部FIFO,⼀边从FIFO中把数据通过
*DMA传送到由接收寄存器指定的内存地址中,接收到的数据依次排放,当
*长度超过默认的缓冲区长度时,会回过头来放到开始的地方,所以接收
*缓冲区被称为环形缓冲区。发送方面:8139有四个发送地址寄存器,CPU
*将要发送的数据在内存中的地址写⼊这四个寄存器中的任何⼀个,网卡
*就会通过DMA操作把数据发送出去。当发送或者接送完成后,网卡会发出
*中断,中断处理程序通过读取网卡的中断状态寄存器来识别出是发送完
*成发出的中断,接收到数据包的中断,还是错误中断。
*当运行ifconfig ethx up的时候,rtl8139_open得到调用。该函数的
*任务就是分配,初始化接收,发送缓冲区,分配中断号等。
*
*/
static int rtl8139_open (struct net_device *dev)
{
struct rtl8139_private *tp = netdev_priv(dev);
int retval;
void __iomem *ioaddr = tp->mmio_addr;

retval = request_irq (dev->irq, rtl8139_interrupt, IRQF_SHARED, dev->name, dev);
if (retval)
return retval;
//dma_alloc_coherent() -- 获取物理页,并将该物理页的总线地址保存于dma_handle,返回该物理页的虚拟地址
tp->tx_bufs = dma_alloc_coherent(&tp->pci_dev->dev, TX_BUF_TOT_LEN,
&tp->tx_bufs_dma, GFP_KERNEL);
tp->rx_ring = dma_alloc_coherent(&tp->pci_dev->dev, RX_BUF_TOT_LEN,
&tp->rx_ring_dma, GFP_KERNEL);
if (tp->tx_bufs == NULL || tp->rx_ring == NULL) {
free_irq(dev->irq, dev);

if (tp->tx_bufs)
dma_free_coherent(&tp->pci_dev->dev, TX_BUF_TOT_LEN,
tp->tx_bufs, tp->tx_bufs_dma);
if (tp->rx_ring)
dma_free_coherent(&tp->pci_dev->dev, RX_BUF_TOT_LEN,
tp->rx_ring, tp->rx_ring_dma);

return -ENOMEM;

}
/**
*    napi_enable - enable NAPI scheduling
*    @n: napi context
*
* Resume NAPI from being scheduled on this context.
* Must be paired with napi_disable.
*/
napi_enable(&tp->napi);

tp->mii.full_duplex = tp->mii.force_media;
tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;

rtl8139_init_ring (dev);//将发送帧所存放的地址写下来
rtl8139_hw_start (dev);//初始化接收和发送寄存器对于的dma地址
netif_start_queue (dev);//该函数用于告诉上层网络驱动层驱动空间有缓冲区可用,开始发送数据包到驱动层。

if (netif_msg_ifup(tp))
pr_debug("%s: rtl8139_open() ioaddr %#llx IRQ %d"
" GP Pins %2.2x %s-duplex.\n", dev->name,
(unsigned long long)pci_resource_start (tp->pci_dev, 1),
dev->irq, RTL_R8 (MediaStatus),
tp->mii.full_duplex ? "full" : "half");

rtl8139_start_thread(tp);

return 0;
}

/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance)
{
struct net_device *dev = (struct net_device *) dev_instance;
struct rtl8139_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr;
u16 status, ackstat;
int link_changed = 0; /* avoid bogus "uninit" warning */
int handled = 0;

spin_lock (&tp->lock);
status = RTL_R16 (IntrStatus);//从网卡的中断状态寄存器中读出状态值进行分析

/* shared irq? */
if (unlikely((status & rtl8139_intr_mask) == 0))
goto out;

handled = 1;

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

/* close possible race's with dev_close */
if (unlikely(!netif_running(dev))) {
RTL_W16 (IntrMask, 0);
goto out;
}

/* Acknowledge all of the current interrupt sources ASAP, but
an first get an additional status bit from CSCR. */
if (unlikely(status & RxUnderrun))
link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit;

ackstat = status & ~(RxAckBits | TxErr);
if (ackstat)
RTL_W16 (IntrStatus, ackstat);

/* Receive packets are processed by poll routine.
If not running start it now. */
/*如果状态寄存器的接收bit位置为1,则进入接收处理函数。根据NAPI机制。这里先向终端屏蔽寄存器写入rtl8139_norx_intr_mask,关闭中断,然后通过把接收的poll 函数加入中断队列,将来软中断调度的时候回调用rtl8139_poll,进行轮询*/
if (status & RxAckBits){
if (napi_schedule_prep(&tp->napi)) {
RTL_W16_F (IntrMask, rtl8139_norx_intr_mask);
__napi_schedule(&tp->napi); //转为轮询机制,而非中断机制
}
}

/* Check uncommon events with one test. */
if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr)))
rtl8139_weird_interrupt (dev, tp, ioaddr,
status, link_changed);
/* 如果状态寄存器的发送位置 1 ,则进入发送中断的处理函数 */
if (status & (TxOK | TxErr)) {
rtl8139_tx_interrupt (dev, tp, ioaddr);
if (status & TxErr)
RTL_W16 (IntrStatus, TxErr);
}
out:
spin_unlock (&tp->lock);

pr_debug("%s: exiting interrupt, intr_status=%#4.4x.\n",
dev->name, RTL_R16 (IntrStatus));
return IRQ_RETVAL(handled);
}

static int rtl8139_poll(struct napi_struct *napi, int budget)
{
struct rtl8139_private *tp = container_of(napi, struct rtl8139_private, napi);
struct net_device *dev = tp->dev;
void __iomem *ioaddr = tp->mmio_addr;
int work_done;

spin_lock(&tp->rx_lock);
work_done = 0;
if (likely(RTL_R16(IntrStatus) & RxAckBits))//接收中断
work_done += rtl8139_rx(dev, tp, budget);
/*rtl8139_rx 将接收到的数据拷贝出来封装到 sk_buff 里面传递给上层协议驱动*/
if (work_done < budget) {//如果一次处理的帧个数小于budget(64)则退出轮询模式
unsigned long flags;
/*
* Order is important since data can get interrupted
* again when we think we are done.
*/
spin_lock_irqsave(&tp->lock, flags);
RTL_W16_F(IntrMask, rtl8139_intr_mask);
__napi_complete(napi); //退出轮询模式
spin_unlock_irqrestore(&tp->lock, flags);
}
spin_unlock(&tp->rx_lock);

return work_done;
}

static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
int budget)
{
void __iomem *ioaddr = tp->mmio_addr;
int received = 0;
unsigned char *rx_ring = tp->rx_ring;
unsigned int cur_rx = tp->cur_rx;
unsigned int rx_size = 0;

pr_debug("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x,"
" free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx,
RTL_R16 (RxBufAddr),
RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));
//这个函数是一个大循环,循环条件是只要接收缓存不为空就还可以继续读取数据,循环不会停止,读空了之后就跳出
while (netif_running(dev) && received < budget
&& (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) {
u32 ring_offset = cur_rx % RX_BUF_LEN;  //
u32 rx_status;
unsigned int pkt_size;
struct sk_buff *skb;

rmb();

/* read size+status of next frame from DMA ring buffer */
rx_status = le32_to_cpu (*(__le32 *) (rx_ring + ring_offset));
rx_size = rx_status >> 16; //计算出要接收的包的长度
pkt_size = rx_size - 4;

if (netif_msg_rx_status(tp))
pr_debug("%s:  rtl8139_rx() status %4.4x, size %4.4x,"
" cur %4.4x.\n", dev->name, rx_status,
rx_size, cur_rx);
#if RTL8139_DEBUG > 2
{
int i;
pr_debug("%s: Frame contents ", dev->name);
for (i = 0; i < 70; i++)
pr_cont(" %2.2x",
rx_ring[ring_offset + i]);
pr_cont(".\n");
}
#endif

/* Packet copy from FIFO still in progress.
* Theoretically, this should never happen
* since EarlyRx is disabled.
*/
if (unlikely(rx_size == 0xfff0)) {
if (!tp->fifo_copy_timeout)
tp->fifo_copy_timeout = jiffies + 2;
else if (time_after(jiffies, tp->fifo_copy_timeout)) {
pr_debug("%s: hung FIFO. Reset.", dev->name);
rx_size = 0;
goto no_early_rx;
}
if (netif_msg_intr(tp)) {
pr_debug("%s: fifo copy in progress.",
dev->name);
}
tp->xstats.early_rx++;
break;
}

no_early_rx:
tp->fifo_copy_timeout = 0;

/* If Rx err or invalid rx_size/rx_status received
* (which happens if we get lost in the ring),
* Rx process gets reset, so we abort any further
* Rx processing.
*/
if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) ||
(rx_size < 8) ||
(!(rx_status & RxStatusOK)))) {
rtl8139_rx_err (rx_status, dev, tp, ioaddr);
received = -1;
goto out;
}

/* Malloc up new buffer, compatible with net-2e. */
/* Omit the four octet CRC from the length. */

skb = netdev_alloc_skb(dev, pkt_size + NET_IP_ALIGN);
//如果分配成功就把数据从接收缓存中拷贝到这个包中
if (likely(skb)) {
skb_reserve (skb, NET_IP_ALIGN);        /* 16 byte align the IP fields. */
#if RX_BUF_IDX == 3
wrap_copy(skb, rx_ring, ring_offset+4, pkt_size);
#else  //现在我们已经熟知,&rx_ring[ring_offset + 4]就是接收缓存,也是源地址,
//而skb->data就是包的数据地址,下面这个函数就是将接收到的数据拷贝到skb->data 所指向的位置
skb_copy_to_linear_data (skb, &rx_ring[ring_offset + 4], pkt_size);
#endif
skb_put (skb, pkt_size);
//当接收到的数据应经复制到skb中head指向的位置之后,需要改变skb->tail 指针

skb->protocol = eth_type_trans (skb, dev);

dev->stats.rx_bytes += pkt_size;
dev->stats.rx_packets++;
//在netif_receive_skb (skb)函数执行完后,这个包的数据就脱离了网卡驱动范畴,而进入了Linux网络协议栈里面
netif_receive_skb (skb);
} else {
if (net_ratelimit())
pr_warning("%s: Memory squeeze, dropping packet.\n",
dev->name);
dev->stats.rx_dropped++;
}
received++;

cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
RTL_W16 (RxBufPtr, (u16) (cur_rx - 16));

rtl8139_isr_ack(tp);
}

if (unlikely(!received || rx_size == 0xfff0))
rtl8139_isr_ack(tp);

pr_debug("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x,"
" free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx,
RTL_R16 (RxBufAddr),
RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd));

tp->cur_rx = cur_rx;

/*
* The receive buffer should be mostly empty.
* Tell NAPI to reenable the Rx irq.
*/
if (tp->fifo_copy_timeout)
received = budget;

out:
return received;
}

static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev)
{
struct rtl8139_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr;
unsigned int entry;
unsigned int len = skb->len;
unsigned long flags;

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

/* Note: the chip doesn't have auto-pad! */
if (likely(len < TX_BUF_SIZE)) {
if (len < ETH_ZLEN)
memset(tp->tx_buf[entry], 0, ETH_ZLEN);
skb_copy_and_csum_dev(skb, tp->tx_buf[entry]);//把待发送的数据拷贝到发送缓冲区,也就是skb->data 指向的空间
dev_kfree_skb(skb);
} else {
dev_kfree_skb(skb);
dev->stats.tx_dropped++;
return 0;
}

spin_lock_irqsave(&tp->lock, flags);
/*
* Writing to TxStatus triggers a DMA transfer of the data
* copied to tp->tx_buf[entry] above. Use a memory barrier
* to make sure that the device sees the updated data.
*/
wmb();
/* 我们要让网卡知道这个包的长度,才能保证数据不多不少精确的从缓存中截取出来搬运到网卡中去,
这是靠写发送状态寄存器(TSD)来完成的。 */
RTL_W32_F (TxStatus0 + (entry * sizeof (u32)),
tp->tx_flag | max(len, (unsigned int)ETH_ZLEN));

dev->trans_start = jiffies;

tp->cur_tx++;
/*判断发送缓存是否已经满了,如果满了在发就覆盖数据了,要停发。*/
if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx)
netif_stop_queue (dev);
spin_unlock_irqrestore(&tp->lock, flags);

if (netif_msg_tx_queued(tp))
pr_debug("%s: Queued Tx packet size %u to slot %d.\n",
dev->name, len, entry);

return 0;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: