本文分析基于Linux Kernel 1.2.13

原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7497260

更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html

作者:闫明

注:标题中的”(上)“,”(下)“表示分析过程基于数据包的传递方向:”(上)“表示分析是从底层向上分析、”(下)“表示分析是从上向下分析。

经过前面两篇博文的分析,已经对Linux的内核网络栈的结构有了一个模糊的认识,这里我们开始从底层开始详细分析Linux内核网络栈的实现。由于这是早期版本,代码的层次隔离做的还不是很好,这里说是从底层分析,但是不免会牵扯上层或下层的函数,许多关键代码都在驱动的文件夹下。

我们首先有第一篇博文中知道在网络栈初始化的时候在net/socket.c中的函数sock_init()函数中当proto_init()完成后会执行dev_init()来进行网络设备模块的初始化。

首先说明一下,在drivers/net/space.c中定义了设备首节点地址dev_base,其实际上是回环设备的地址。

[cpp] view plaincopy
  1. struct device loopback_dev = {
  2. "lo",           /* Software Loopback interface      */
  3. 0x0,            /* recv memory end          */
  4. 0x0,            /* recv memory start            */
  5. 0x0,            /* memory end               */
  6. 0x0,            /* memory start             */
  7. 0,          /* base I/O address         */
  8. 0,          /* IRQ                  */
  9. 0, 0, 0,        /* flags                */
  10. NEXT_DEV,       /* next device              */
  11. loopback_init       /* loopback_init should set up the rest */
  12. };
  13. struct device *dev_base = &loopback_dev;

而NEXT_DEV宏定义即定义了下一个网络设备的地址,这样可以把设备串成链。

附网络设备的定义(include/linux/netdevice.h)如下:

[cpp] view plaincopy
  1. /*
  2. * The DEVICE structure.
  3. * Actually, this whole structure is a big mistake.  It mixes I/O
  4. * data with strictly "high-level" data, and it has to know about
  5. * almost every data structure used in the INET module.
  6. */
  7. struct device
  8. {
  9. /*
  10. * This is the first field of the "visible" part of this structure
  11. * (i.e. as seen by users in the "Space.c" file).  It is the name
  12. * the interface.
  13. */
  14. char            *name;
  15. /* I/O specific fields - FIXME: Merge these and struct ifmap into one */
  16. unsigned long       rmem_end;     /* shmem "recv" end */
  17. unsigned long       rmem_start;       /* shmem "recv" start   */
  18. unsigned long       mem_end;      /* sahared mem end  */
  19. unsigned long       mem_start;        /* shared mem start */
  20. unsigned long       base_addr;        /* device I/O address   */
  21. unsigned char       irq;          /* device IRQ number    */
  22. /* Low-level status flags. */
  23. volatile unsigned char  start,        /* start an operation   */
  24. tbusy,        /* transmitter busy */
  25. interrupt;        /* interrupt arrived    */
  26. struct device       *next;
  27. /* The device initialization function. Called only once. */
  28. int             (*init)(struct device *dev);
  29. /* Some hardware also needs these fields, but they are not part of the
  30. usual set specified in Space.c. */
  31. unsigned char       if_port;      /* Selectable AUI, TP,..*/
  32. unsigned char       dma;          /* DMA channel      */
  33. struct enet_statistics* (*get_stats)(struct device *dev);
  34. /*
  35. * This marks the end of the "visible" part of the structure. All
  36. * fields hereafter are internal to the system, and may change at
  37. * will (read: may be cleaned up at will).
  38. */
  39. /* These may be needed for future network-power-down code. */
  40. unsigned long       trans_start;  /* Time (in jiffies) of last Tx */
  41. unsigned long       last_rx;  /* Time of last Rx      */
  42. unsigned short      flags;    /* interface flags (a la BSD)   */
  43. unsigned short      family;   /* address family ID (AF_INET)  */
  44. unsigned short      metric;   /* routing metric (not used)    */
  45. unsigned short      mtu;      /* interface MTU value      */
  46. unsigned short      type;     /* interface hardware type  */
  47. unsigned short      hard_header_len;  /* hardware hdr length  */
  48. void            *priv;    /* pointer to private data  */
  49. /* Interface address info. */
  50. unsigned char       broadcast[MAX_ADDR_LEN];  /* hw bcast add */
  51. unsigned char       dev_addr[MAX_ADDR_LEN];   /* hw address   */
  52. unsigned char       addr_len; /* hardware address length  */
  53. unsigned long       pa_addr;  /* protocol address     */
  54. unsigned long       pa_brdaddr;   /* protocol broadcast addr  */
  55. unsigned long       pa_dstaddr;   /* protocol P-P other side addr */
  56. unsigned long       pa_mask;  /* protocol netmask     */
  57. unsigned short      pa_alen;  /* protocol address length  */
  58. struct dev_mc_list     *mc_list;  /* Multicast mac addresses  */
  59. int            mc_count;  /* Number of installed mcasts   */
  60. struct ip_mc_list  *ip_mc_list;   /* IP multicast filter chain    */
  61. /* For load balancing driver pair support */
  62. unsigned long        pkt_queue;   /* Packets queued */
  63. struct device       *slave;   /* Slave device */
  64. /* Pointer to the interface buffers. */
  65. struct sk_buff_head     buffs[DEV_NUMBUFFS];
  66. /* Pointers to interface service routines. */
  67. int             (*open)(struct device *dev);
  68. int             (*stop)(struct device *dev);
  69. int             (*hard_start_xmit) (struct sk_buff *skb,
  70. struct device *dev);
  71. int             (*hard_header) (unsigned char *buff,
  72. struct device *dev,
  73. unsigned short type,
  74. void *daddr,
  75. void *saddr,
  76. unsigned len,
  77. struct sk_buff *skb);
  78. int             (*rebuild_header)(void *eth, struct device *dev,
  79. unsigned long raddr, struct sk_buff *skb);
  80. unsigned short      (*type_trans) (struct sk_buff *skb,
  81. struct device *dev);
  82. #define HAVE_MULTICAST
  83. void            (*set_multicast_list)(struct device *dev,
  84. int num_addrs, void *addrs);
  85. #define HAVE_SET_MAC_ADDR
  86. int             (*set_mac_address)(struct device *dev, void *addr);
  87. #define HAVE_PRIVATE_IOCTL
  88. int             (*do_ioctl)(struct device *dev, struct ifreq *ifr, int cmd);
  89. #define HAVE_SET_CONFIG
  90. int             (*set_config)(struct device *dev, struct ifmap *map);
  91. };

dev_init()网络设备的初始化函数如下:

[cpp] view plaincopy
  1. /*
  2. *  Initialize the DEV module. At boot time this walks the device list and
  3. *  unhooks any devices that fail to initialise (normally hardware not
  4. *  present) and leaves us with a valid list of present and active devices.
  5. *
  6. *  The PCMCIA code may need to change this a little, and add a pair
  7. *  of register_inet_device() unregister_inet_device() calls. This will be
  8. *  needed for ethernet as modules support.
  9. */
  10. void dev_init(void)
  11. {
  12. struct device *dev, *dev2;
  13. /*
  14. *  Add the devices.
  15. *  If the call to dev->init fails, the dev is removed
  16. *  from the chain disconnecting the device until the
  17. *  next reboot.
  18. */
  19. dev2 = NULL;
  20. for (dev = dev_base; dev != NULL; dev=dev->next) //循环移除设备由璞傅絛ev_base指向的网络设备链表
  21. {
  22. if (dev->init && dev->init(dev)) //如果设备有初始化函数并且初始化失败,则从链表摘除设备(init()函数成功返回0)
  23. {
  24. /*
  25. *  It failed to come up. Unhook it.这个函数还挺有技巧性的,从默认配置的设备中扫描不存在的设备,将其移除
  26. */
  27. if (dev2 == NULL)
  28. dev_base = dev->next;
  29. else
  30. dev2->next = dev->next;
  31. }
  32. else
  33. {
  34. dev2 = dev;
  35. }
  36. }
  37. }

这里我们看一下dev_base这个队列是如何定义的,这里我们仅仅看eth网卡的定义方式即可

[cpp] view plaincopy
  1. /* "eth0" defaults to autoprobe (== 0), other use a base of 0xffe0 (== -0x20),
  2. which means "don't probe".  These entries exist to only to provide empty
  3. slots which may be enabled at boot-time. */
  4. static struct device eth3_dev = {
  5. "eth3", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, NEXT_DEV, ethif_probe };
  6. static struct device eth2_dev = {
  7. "eth2", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, eth3_dev, ethif_probe };
  8. static struct device eth1_dev = {
  9. "eth1", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, eth2_dev, ethif_probe };
  10. static struct device eth0_dev = {
  11. "eth0", 0, 0, 0, 0, ETH0_ADDR, ETH0_IRQ, 0, 0, 0, eth1_dev, ethif_probe };
  12. #   undef NEXT_DEV
  13. #   define NEXT_DEV (eth0_dev)

可以看出eth系列网卡设备的init函数定义为ethif_probe(),该函数会调用具体网卡的探测函数,我们还是以 NS8390 ethernet网卡为例来分析,该网卡的驱动实现文件为drivers/net/ne.c

ethif_probe()函数会调用函数ne_probe()探测函数,而该函数对设备地址进行检查后调用ne_probe1()函数,具体工作有ne_probe1()函数完成。

函数如下:

[cpp] view plaincopy
  1. static int ne_probe1(struct device *dev, int ioaddr)
  2. {
  3. .....................//合法性检查
  4. /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
  5. or don't know which one to set. */
  6. dev->irq = 9;//设置中断类型号
  7. /* Snarf the interrupt now.  There's no point in waiting since we cannot
  8. share and the board will usually be enabled. */
  9. {
  10. int irqval = request_irq (dev->irq, ei_interrupt, 0, wordlength==2 ? "ne2000":"ne1000");//注册申请中断,中断处理函数为ei_interrupt
  11. if (irqval) {
  12. printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
  13. return EAGAIN;
  14. }
  15. }
  16. dev->base_addr = ioaddr;
  17. request_region(ioaddr, NE_IO_EXTENT, wordlength==2 ? "ne2000":"ne1000");//申请内存空间
  18. for(i = 0; i < ETHER_ADDR_LEN; i++)
  19. dev->dev_addr[i] = SA_prom[i];
  20. ethdev_init(dev);//调用函数对dev设备结构体进行初始化
  21. printk("\n%s: %s found at %#x, using IRQ %d.\n",
  22. dev->name, name, ioaddr, dev->irq);
  23. if (ei_debug > 0)
  24. printk(version);
  25. ei_status.name = name;
  26. ei_status.tx_start_page = start_page;
  27. ei_status.stop_page = stop_page;
  28. ei_status.word16 = (wordlength == 2);
  29. ei_status.rx_start_page = start_page + TX_PAGES;
  30. #ifdef PACKETBUF_MEMSIZE
  31. /* Allow the packet buffer size to be overridden by know-it-alls. */
  32. ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
  33. #endif
  34. ei_status.reset_8390 = &ne_reset_8390;
  35. ei_status.block_input = &ne_block_input;
  36. ei_status.block_output = &ne_block_output;
  37. NS8390_init(dev, 0);//配置网卡中的寄存器等到默认状态
  38. return 0;
  39. }

初始化函数ethdev_init()在文件drivers/net/8390.c中。如下:

[cpp] view plaincopy
  1. /* Initialize the rest of the 8390 device structure. */
  2. int ethdev_init(struct device *dev)
  3. {
  4. if (ei_debug > 1)
  5. printk(version);
  6. if (dev->priv == NULL) {//申请私有空间存储具体网卡的结构体信息
  7. struct ei_device *ei_local;//8390网卡设备的结构体
  8. dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL);//申请内核内存空间
  9. memset(dev->priv, 0, sizeof(struct ei_device));
  10. ei_local = (struct ei_device *)dev->priv;
  11. #ifndef NO_PINGPONG
  12. ei_local->pingpong = 1;
  13. #endif
  14. }
  15. /* The open call may be overridden by the card-specific code. */
  16. if (dev->open == NULL)
  17. dev->open = &ei_open;//设备的打开函数
  18. /* We should have a dev->stop entry also. */
  19. dev->hard_start_xmit = &ei_start_xmit;//设备的发送函数,定义在8390.c中
  20. dev->get_stats   = get_stats;
  21. #ifdef HAVE_MULTICAST
  22. dev->set_multicast_list = &set_multicast_list;
  23. #endif
  24. ether_setup(dev);//进一步调用函数设置dev设备结构体
  25. return 0;
  26. }

ether_setup()函数的实现如下:

[cpp] view plaincopy
  1. void ether_setup(struct device *dev)
  2. {
  3. int i;
  4. /* Fill in the fields of the device structure with ethernet-generic values.
  5. This should be in a common file instead of per-driver.  */
  6. for (i = 0; i < DEV_NUMBUFFS; i++)
  7. skb_queue_head_init(&dev->buffs[i]);//缓冲队列初始化
  8. /* register boot-defined "eth" devices */
  9. if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) {//定义eth网卡的名称
  10. i = simple_strtoul(dev->name + 3, NULL, 0);
  11. if (ethdev_index[i] == NULL) {
  12. ethdev_index[i] = dev;
  13. }
  14. else if (dev != ethdev_index[i]) {
  15. /* Really shouldn't happen! */
  16. printk("ether_setup: Ouch! Someone else took %s\n",
  17. dev->name);
  18. }
  19. }
  20. dev->hard_header = eth_header;//该函数的作用是创建链路层首部,定义在eth.c中
  21. dev->rebuild_header = eth_rebuild_header;//该函数的作用是重建链路层首部,用于ARP协议
  22. dev->type_trans = eth_type_trans;
  23. dev->type        = ARPHRD_ETHER;
  24. dev->hard_header_len = ETH_HLEN;
  25. dev->mtu     = 1500; /* eth_mtu */
  26. dev->addr_len    = ETH_ALEN;
  27. for (i = 0; i < ETH_ALEN; i++) {
  28. dev->broadcast[i]=0xff;
  29. }
  30. /* New-style flags. */
  31. dev->flags       = IFF_BROADCAST|IFF_MULTICAST;
  32. dev->family      = AF_INET;
  33. dev->pa_addr = 0;
  34. dev->pa_brdaddr = 0;
  35. dev->pa_mask = 0;
  36. dev->pa_alen = sizeof(unsigned long);
  37. }

这样,网络设备的初始化工作就完成了。

在drivers/net/8390.c中实现了该网卡的设备的基本操作函数,

设备的打开函数ei_open()比较简单,下面列出该设备的发送和接收函数,在这里不做具体的分析,如果想更多了解请点击前面分析过的DM9000网卡驱动,下面给出链接:

  1. ARM-Linux驱动--DM9000网卡驱动分析(一)
  2. ARM-Linux驱动--DM9000网卡驱动分析(二)
  3. ARM-Linux驱动--DM9000网卡驱动分析(三)
  4. ARM-Linux驱动--DM9000网卡驱动分析(四)

其基本结构是一致的。

ei_start_xmit()

[cpp] view plaincopy
  1. static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
  2. {
  3. int e8390_base = dev->base_addr;
  4. struct ei_device *ei_local = (struct ei_device *) dev->priv;
  5. int length, send_length;
  6. unsigned long flags;
  7. /*
  8. *  We normally shouldn't be called if dev->tbusy is set, but the
  9. *  existing code does anyway. If it has been too long since the
  10. *  last Tx, we assume the board has died and kick it.
  11. */
  12. if (dev->tbusy) {    /* Do timeouts, just like the 8003 driver. */
  13. int txsr = inb(e8390_base+EN0_TSR), isr;
  14. int tickssofar = jiffies - dev->trans_start;
  15. if (tickssofar < TX_TIMEOUT ||   (tickssofar < (TX_TIMEOUT+5) && ! (txsr & ENTSR_PTX))) {
  16. return 1;
  17. }
  18. isr = inb(e8390_base+EN0_ISR);
  19. if (dev->start == 0) {
  20. printk("%s: xmit on stopped card\n", dev->name);
  21. return 1;
  22. }
  23. printk(KERN_DEBUG "%s: transmit timed out, TX status %#2x, ISR %#2x.\n",
  24. dev->name, txsr, isr);
  25. /* Does the 8390 thinks it has posted an interrupt? */
  26. if (isr)
  27. printk(KERN_DEBUG "%s: Possible IRQ conflict on IRQ%d?\n", dev->name, dev->irq);
  28. else {
  29. /* The 8390 probably hasn't gotten on the cable yet. */
  30. printk(KERN_DEBUG "%s: Possible network cable problem?\n", dev->name);
  31. if(ei_local->stat.tx_packets==0)
  32. ei_local->interface_num ^= 1;    /* Try a different xcvr.  */
  33. }
  34. /* Try to restart the card.  Perhaps the user has fixed something. */
  35. ei_reset_8390(dev);
  36. NS8390_init(dev, 1);
  37. dev->trans_start = jiffies;
  38. }
  39. /* Sending a NULL skb means some higher layer thinks we've missed an
  40. tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
  41. itself. */
  42. if (skb == NULL) {
  43. dev_tint(dev);
  44. return 0;
  45. }
  46. length = skb->len;
  47. if (skb->len <= 0)
  48. return 0;
  49. save_flags(flags);
  50. cli();
  51. /* Block a timer-based transmit from overlapping. */
  52. if ((set_bit(0, (void*)&dev->tbusy) != 0) || ei_local->irqlock) {
  53. printk("%s: Tx access conflict. irq=%d lock=%d tx1=%d tx2=%d last=%d\n",
  54. dev->name, dev->interrupt, ei_local->irqlock, ei_local->tx1,
  55. ei_local->tx2, ei_local->lasttx);
  56. restore_flags(flags);
  57. return 1;
  58. }
  59. /* Mask interrupts from the ethercard. */
  60. outb(0x00, e8390_base + EN0_IMR);
  61. ei_local->irqlock = 1;
  62. restore_flags(flags);
  63. send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
  64. if (ei_local->pingpong) {
  65. int output_page;
  66. if (ei_local->tx1 == 0) {
  67. output_page = ei_local->tx_start_page;
  68. ei_local->tx1 = send_length;
  69. if (ei_debug  &&  ei_local->tx2 > 0)
  70. printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
  71. dev->name, ei_local->tx2, ei_local->lasttx,
  72. ei_local->txing);
  73. } else if (ei_local->tx2 == 0) {
  74. output_page = ei_local->tx_start_page + 6;
  75. ei_local->tx2 = send_length;
  76. if (ei_debug  &&  ei_local->tx1 > 0)
  77. printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
  78. dev->name, ei_local->tx1, ei_local->lasttx,
  79. ei_local->txing);
  80. } else {    /* We should never get here. */
  81. if (ei_debug)
  82. printk("%s: No Tx buffers free. irq=%d tx1=%d tx2=%d last=%d\n",
  83. dev->name, dev->interrupt, ei_local->tx1,
  84. ei_local->tx2, ei_local->lasttx);
  85. ei_local->irqlock = 0;
  86. dev->tbusy = 1;
  87. outb_p(ENISR_ALL, e8390_base + EN0_IMR);
  88. return 1;
  89. }
  90. ei_block_output(dev, length, skb->data, output_page);
  91. if (! ei_local->txing) {
  92. ei_local->txing = 1;
  93. NS8390_trigger_send(dev, send_length, output_page);
  94. dev->trans_start = jiffies;
  95. if (output_page == ei_local->tx_start_page)
  96. ei_local->tx1 = -1, ei_local->lasttx = -1;
  97. else
  98. ei_local->tx2 = -1, ei_local->lasttx = -2;
  99. } else
  100. ei_local->txqueue++;
  101. dev->tbusy = (ei_local->tx1  &&  ei_local->tx2);
  102. } else {  /* No pingpong, just a single Tx buffer. */
  103. ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
  104. ei_local->txing = 1;
  105. NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
  106. dev->trans_start = jiffies;
  107. dev->tbusy = 1;
  108. }
  109. /* Turn 8390 interrupts back on. */
  110. ei_local->irqlock = 0;
  111. outb_p(ENISR_ALL, e8390_base + EN0_IMR);
  112. dev_kfree_skb (skb, FREE_WRITE);
  113. return 0;
  114. }

ei_receive()函数

[cpp] view plaincopy
  1. static void ei_receive(struct device *dev)
  2. {
  3. int e8390_base = dev->base_addr;
  4. struct ei_device *ei_local = (struct ei_device *) dev->priv;
  5. int rxing_page, this_frame, next_frame, current_offset;
  6. int rx_pkt_count = 0;
  7. struct e8390_pkt_hdr rx_frame;
  8. int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
  9. while (++rx_pkt_count < 10) {
  10. int pkt_len;
  11. /* Get the rx page (incoming packet pointer). */
  12. outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
  13. rxing_page = inb_p(e8390_base + EN1_CURPAG);
  14. outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
  15. /* Remove one frame from the ring.  Boundary is always a page behind. */
  16. this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
  17. if (this_frame >= ei_local->stop_page)
  18. this_frame = ei_local->rx_start_page;
  19. /* Someday we'll omit the previous, iff we never get this message.
  20. (There is at least one clone claimed to have a problem.)  */
  21. if (ei_debug > 0  &&  this_frame != ei_local->current_page)
  22. printk("%s: mismatched read page pointers %2x vs %2x.\n",
  23. dev->name, this_frame, ei_local->current_page);
  24. if (this_frame == rxing_page)   /* Read all the frames? */
  25. break;              /* Done for now */
  26. current_offset = this_frame << 8;
  27. ei_block_input(dev, sizeof(rx_frame), (char *)&rx_frame,
  28. current_offset);
  29. pkt_len = rx_frame.count - sizeof(rx_frame);
  30. next_frame = this_frame + 1 + ((pkt_len+4)>>8);
  31. /* Check for bogosity warned by 3c503 book: the status byte is never
  32. written.  This happened a lot during testing! This code should be
  33. cleaned up someday. */
  34. if (rx_frame.next != next_frame
  35. && rx_frame.next != next_frame + 1
  36. && rx_frame.next != next_frame - num_rx_pages
  37. && rx_frame.next != next_frame + 1 - num_rx_pages) {
  38. ei_local->current_page = rxing_page;
  39. outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
  40. ei_local->stat.rx_errors++;
  41. continue;
  42. }
  43. if (pkt_len < 60  ||  pkt_len > 1518) {
  44. if (ei_debug)
  45. printk("%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n",
  46. dev->name, rx_frame.count, rx_frame.status,
  47. rx_frame.next);
  48. ei_local->stat.rx_errors++;
  49. } else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
  50. struct sk_buff *skb;
  51. skb = alloc_skb(pkt_len, GFP_ATOMIC);
  52. if (skb == NULL) {
  53. if (ei_debug > 1)
  54. printk("%s: Couldn't allocate a sk_buff of size %d.\n",
  55. dev->name, pkt_len);
  56. ei_local->stat.rx_dropped++;
  57. break;
  58. } else {
  59. skb->len = pkt_len;
  60. skb->dev = dev;
  61. ei_block_input(dev, pkt_len, (char *) skb->data,
  62. current_offset + sizeof(rx_frame));
  63. netif_rx(skb);
  64. ei_local->stat.rx_packets++;
  65. }
  66. } else {
  67. int errs = rx_frame.status;
  68. if (ei_debug)
  69. printk("%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n",
  70. dev->name, rx_frame.status, rx_frame.next,
  71. rx_frame.count);
  72. if (errs & ENRSR_FO)
  73. ei_local->stat.rx_fifo_errors++;
  74. }
  75. next_frame = rx_frame.next;
  76. /* This _should_ never happen: it's here for avoiding bad clones. */
  77. if (next_frame >= ei_local->stop_page) {
  78. printk("%s: next frame inconsistency, %#2x\n", dev->name,
  79. next_frame);
  80. next_frame = ei_local->rx_start_page;
  81. }
  82. ei_local->current_page = next_frame;
  83. outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
  84. }
  85. /* If any worth-while packets have been received, dev_rint()
  86. has done a mark_bh(NET_BH) for us and will work on them
  87. when we get to the bottom-half routine. */
  88. /* Record the maximum Rx packet queue. */
  89. if (rx_pkt_count > high_water_mark)
  90. high_water_mark = rx_pkt_count;
  91. /* Bug alert!  Reset ENISR_OVER to avoid spurious overruns! */
  92. outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, e8390_base+EN0_ISR);
  93. return;
  94. }

转载于:https://www.cnblogs.com/wangfengju/archive/2013/04/13/6173204.html

Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上)相关推荐

  1. Linux内核--网络栈实现分析(二)--数据包的传递过程--转

    转载地址http://blog.csdn.net/yming0221/article/details/7492423 作者:闫明 本文分析基于Linux Kernel 1.2.13 注:标题中的&qu ...

  2. 显示驱动包含在Linux内核层,驱动程序层(上) - Linux内核--网络栈实现分析_Linux编程_Linux公社-Linux系统门户网站...

    经过前面两篇博文的分析,已经对Linux的内核网络栈的结构有了一个模糊的认识,这里我们开始从底层开始详细分析Linux内核网络栈的实现.由于这是早期版本,代码的层次隔离做的还不是很好,这里说是从底层分 ...

  3. Linux内核--网络栈实现分析(一)--网络栈初始化--转

    转载地址 http://blog.csdn.net/yming0221/article/details/7488828 作者:闫明 本文分析基于内核Linux Kernel 1.2.13 以后的系列博 ...

  4. linux内核网络初始化,Linux内核--网络栈实现分析

    本文分析基于内核Linux Kernel 1.2.13 以后的系列博文将深入分析Linux内核的网络栈实现原理,这里看到曹桂平博士的分析后,也决定选择Linux内核1.2.13版本进行分析. 原因如下 ...

  5. Linux内核--网络栈实现分析(二)--数据包的传递过程(上)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7492423 更多请看专栏,地址 ...

  6. Linux内核网络栈1.2.13-tcp.c概述

    参考资料 <<linux内核网络栈源代码情景分析>> af_inet.c文件中调用函数在协议层的实现 本文主要根据在af_inet.c文件中根据初始化不同的协议,来调用不同的协 ...

  7. Linux内核网络栈1.2.13-af_inet.c概述

    参考资料 <<linux内核网络栈源代码情景分析>> socket常用函数继续调用分析 根据socket提供的常用库函数,socket.read和write等函数,继续往下一层 ...

  8. Linux内核网络栈1.2.13-网卡设备的初始化流程

    参考资料 <<linux内核网络栈源代码情景分析>> 网卡设备的初始化 本文主要描述一下网卡设备的整个初始化的过程,该过程主要就是根据设备的硬件信息来获取与传输网络数据,注册相 ...

  9. Linux内核网络栈1.2.13-route.c概述

    参考资料 <<linux内核网络栈源代码情景分析>> route路由表概述 在IP协议的实现中,只要发送数据包都要查询路由表,选择合适的路由选项,确定下一站的地址,并构造MAC ...

最新文章

  1. 高金吉院士:让机器“自愈化”引领新科技变革
  2. C++之匿名对象与析构函数的关系
  3. Oracle SCN
  4. jQuery学习3:操作元素属性和特性
  5. QT的QGraphicsProxyWidget类的使用
  6. anaconda的执行路径
  7. Wizard of Orz CodeForces - 1467A
  8. html-表单初级验证
  9. BAT的前端,不是技术牛就够了!还应该锻炼这些能力
  10. UVA494 Kindergarten Counting Game
  11. 快速入门:github发布windows版
  12. python编写代码实现文件的拷贝功能_python利用os模块编写文件复制功能——copy()函数用法...
  13. CH579 以太网转串口 串口服务器代码!
  14. could not extract ResultSet
  15. 每天一个Lodash源码解析
  16. 迅雷显示服务器超时,迅雷登录不了出现登录超时怎么办_迅雷登录超时的解决步骤...
  17. 第七章 实验传统的兴起
  18. 怎么关闭Windows7显示器校准?
  19. cisco3560及二层交换机配置vlan及常用命令
  20. 基于Java+Spring+Vue+elementUI大学生求职招聘系统详细设计实现

热门文章

  1. OpenCV中使用类VideoCapture加载视频和打开摄像头
  2. 计算机怎么设置计算机组和用户,怎样设置同一工作组的计算机资源共享
  3. Velocity的中文问题
  4. android四大组件 服务,Android四大组件之Service
  5. Docker容器的启动过程(七)
  6. 浅析Postgres中的并发控制(Concurrency Control)与事务特性(上)
  7. 全新 Hexo Material Design 主题 Mellow
  8. 深度学习数学知识(持续补充)
  9. video视频播放以及主流浏览器兼容
  10. 程序员读研如何提高技术之我见