Linux网络设备结构
来源:互联网 发布:unity3d raycastall 编辑:程序博客网 时间:2024/04/29 14:00
Linux网络设备结构
首先看一下Linux网络设备的结构,如下图:
网络协议接口层向网络层协议提供提供统一的数据包收发接口,不论上层协议为ARP还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接受数据。这一层的存在使得上层协议独立于具体的设备。
网络设备接口层向协议接口层提供统一的用于描述具体网络设备属性和操作的结构体net_device,该结构体是设备驱动功能层中各函数的容器。实际上,网络设备接口层从宏观上规划了具体操作硬件的设备驱动功能层的结构。
设备驱动功能层各函数是网络设备接口层net_device数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,他通过hard_start_xmit()函数启动发送操作,并通过网络设备上的中断触发接受操作。
网络设备与媒介层是完成数据包发送和接受的物理实体,包括网络适配器和具体的传输媒介,网络适配器被驱动功能层中的函数物理上驱动。对于Linux系统而言,网络设备和媒介都可以是虚拟的。
上面的结构还可以细分,如下图
网络协议接口层
这里主要进行数据包的收发,使用函数原型为:
dev_queue_xmit(struct sk_buff *skb);int netif_rx(struct sk_buff *skb);
这里使用了一个skb_buff结构体,定义于include/linux/skbuff.h中,它的含义为“套接字缓冲区”,用于在Linux网络子系统各层间传输数据。
它是一个双向链表,在老的内核中会有一个list域指向sk_buff_head也就是链表头,但是在linux2.6.30.4内核后已经不存在了,如下图:
操作套接字缓冲区的函数
1.分配
struct sk_buff *alloc_skb(unsigned int len, int priority);struct sk_buff *dev_alloc_skb(unsigned int len);
分配一个缓冲区。alloc_skb 函数分配一个缓冲区并初始化skb->data和skb->tail为skb->head。
参数len为数据缓冲区的空间大小,通常以L1_CACHE_BYTES字节(对ARM为32)对齐,参数priority为内存分配的优先级。
dev_alloc_skb()函数以GFP_ATOMIC优先级进行skb的分配。
2.释放
void kfree_skb(struct sk_buff *skb);void dev_kfree_skb(struct sk_buff *skb);
Linux内核内部使用kfree_skb()函数,而网络设备驱动程序中则最好使用dev_kfree_skb()。
sk_buff中比较重要的成员是指向数据包中数据的指针,如下图所示
用于寻址数据包中数据的指针,head指向已分配空间开头,data指向有效的octet开头,tail指向有效的octet结尾,而end指向tail可以到达的最大地址。
如果不这样做而分配一个大小固定的缓冲区,如果buffer不够用,则要申请一个更大的buffer,拷贝进去再增加,这样降低了性能。
3.变更
unsigned char *skb_put(struct sk_buff *skb, int len);unsigned char *skb_push(struct sk_buff *skb, int len);unsigned char *skb_pull(struct sk_buff *skb, int len);void skb_reserve(struct sk_buff ×skb, int len);
下图分别对应了这四个函数,看了这张图应该对这4个函数的作用了然于胸。
网络设备接口层
网络设备接口层的主要功能是为千变万化的网络设备定义了统一,抽象的数据结构net_device结构体,以不变应万变,实现多种硬件在软件层次上的统一。
打开和关闭网络设备
int (*open)(struct net_device *dev);int (*close)(struct net_device *dev);
要注意的是ifconfig是interface config的缩写,通常我们在用户空间输入:
ifconfig eth0 up
会调用这里的open函数。
在用户空间输入:
ifconfig eth0 down
会调用这里的stop函数。
在使用ifconfig向接口赋予地址时,要执行两个任务:
首先,它通过ioctl(SIOCSIFADDR)(Socket I/O Control Set Interface Address)赋予地址;
然后通过ioctl(SIOCSIFFLAGS)(Socket I/O Control Set Interface Flags)设置dev->flag中的IFF_UP标志以打开接口。
这个调用会使得设备的open方法得到调用。
类似的,在接口关闭时,ifconfig使用ioctl(SIOCSIFFLAGS)来清理IFF_UP标志,然后调用stop函数。
重要函数
int (*hard_header)(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);
该方法根据先前检索到的源和目的硬件地址建立硬件头。
int (*rebuild_header)(struct sk_buff *skb);
以太网的mac地址是固定的,为了高效,第一个包去询问mac地址,得到对应的mac地址后就会作为cache把mac地址保存起来。以后每次发包不用询问了,直接把包的地址拷贝出来。
void (*tx_timeout)(struct net_device *dev);
如果数据包发送在超时时间内失败,这时该方法被调用,这个方法应该解决失败的问题,并重新开始发送数据。
int (*set_config)(struct net_device *dev, struct ifmap *map);
改变接口的配置,比如改变I/O端口和中断号等,现在的驱动程序通常无需该方法。
int (*do_ioctl)(struct net_device *dev, struct ifmap *map);
用来实现自定义的ioctl命令,如果不需要可以为NULL。
void (*set_multicast_list)(struct net_device *dev);
当设备的组播列表改变或设备标志改变时,该方法被调用。
int (*set_mac_address)(struct net_device *dev, void *addr);
如果接口支持mac地址改变,则可以实现该函数。
设备驱动接口层
net_device结构体的成员(属性和函数指针)需要被设备驱动功能层的具体数值和函数赋予。
对具体的设置xxx,工程师应该编写设备驱动功能层的函数,这些函数型如xxx_open(),xxx_stop(),xxx_tx(),xxx_hard_header(),xxx_get_stats(),xxx_tx_timeout()等。
网络设备与媒介层
网络设备与媒介层直接对应于实际的硬件设备。
下面从代码中分析dm9000的实现
网络设备的初始化
通过模块的加载函数看出DM9000A的驱动是以平台驱动的形式注册进内核的。
模块的加载函数
下边是模块的加载函数:
1 static int __init 2 dm9000_init(void) 3 { 4 printk(KERN_INFO "%s Ethernet Driver, V%s/n", CARDNAME, DRV_VERSION); 5 6 return platform_driver_register(&dm9000_driver); 7 }
View Code
probe函数
下面来分析probe 函数,用来执行分配的内核函数是alloc_netdev,函数原型是:
struct net_device *alloc_netdev(int sizeof_priv, const char *name, void (*setup)(struct net_device*));
sizeof_priv是驱动程序私有数据区的大小;这个成员和net_device结构一同分配给网络设备。实际上,他们都处于一大块内存中,但是驱动程序不需要知道这些。
name是接口的名字,其在用户空间可见;这个名字可以使用类似printf中%d的格式,内核将用下一个可用的接口号替代%d。
setup是一个初始化函数,用来设置net_device结构剩余的部分。
网络子系统对alloc_netdev,为不同种类的接口封装了许多函数。
最常用的是alloc_etherdev,它定义在linux/etherdevice.h中:
struct net_device *alloc_etherdev(int sizeof_priv);
该函数使用eth%d的形式指定分配给网络设备的名字。
它提供了自己的初始化函数(ether_setup),用正确的值为以太网设备设置net_device中的许多成员。
那么在DM9000A中这个私有数据成员是什么呢,看下边的结构:
1 /* Structure/enum declaration ------------------------------- */ 2 typedef struct board_info { 3 4 void __iomem *io_addr; /* Register I/O base address */ 5 void __iomem *io_data; /* Data I/O address */ 6 u16 irq; /* IRQ */ 7 8 u16 tx_pkt_cnt; 9 u16 queue_pkt_len;10 u16 queue_start_addr;11 u16 dbug_cnt;12 u8 io_mode; /* 0:word, 2:byte */13 u8 phy_addr;14 u8 imr_all;15 16 unsigned int flags;17 unsigned int in_suspend :1;18 int debug_level;19 20 enum dm9000_type type;21 22 void (*inblk)(void __iomem *port, void *data, int length);23 void (*outblk)(void __iomem *port, void *data, int length);24 void (*dumpblk)(void __iomem *port, int length);25 26 struct device *dev; /* parent device */27 28 struct resource *addr_res; /* resources found */29 struct resource *data_res;30 struct resource *addr_req; /* resources requested */31 struct resource *data_req;32 struct resource *irq_res;33 34 struct mutex addr_lock; /* phy and eeprom access lock */35 36 struct delayed_work phy_poll;37 struct net_device *ndev;38 39 spinlock_t lock;40 41 struct mii_if_info mii;42 u32 msg_enable;43 } board_info_t;
View Code
这个struct board_info就是那个私有数据,用来保存芯片相关的一些私有信息。
下面是probe函数的实现:
1 /* 2 * Search DM9000 board, allocate space and register it 3 */ 4 static int __devinit 5 dm9000_probe(struct platform_device *pdev) 6 { 7 /*获得平台数据,这个应该在platform_device那边指定了*/ 8 struct dm9000_plat_data *pdata = pdev->dev.platform_data; 9 struct board_info *db; /* Point a board information structure */ 10 struct net_device *ndev; 11 const unsigned char *mac_src; 12 int ret = 0; 13 int iosize; 14 int i; 15 u32 id_val; 16 17 /*分配以太网的网络设备*/ 18 ndev = alloc_etherdev(sizeof(struct board_info)); 19 if (!ndev) { 20 dev_err(&pdev->dev, "could not allocate device./n"); 21 return -ENOMEM; 22 } 23 /*#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))*/ 24 SET_NETDEV_DEV(ndev, &pdev->dev); 25 26 dev_dbg(&pdev->dev, "dm9000_probe()/n"); 27 28 /*设置struct board_info为ndev的私有数据*/ 29 db = netdev_priv(ndev); 30 memset(db, 0, sizeof(*db)); 31 32 db->dev = &pdev->dev; 33 db->ndev = ndev; 34 35 spin_lock_init(&db->lock); 36 mutex_init(&db->addr_lock); 37 /*提交一个任务给一个工作队列,你需要填充一个work_struct结构db->phy_poll*/ 38 INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work); 39 /*获取IO内存和中断资源*/ 40 db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 41 db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 42 db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 43 44 if (db->addr_res == NULL || db->data_res == NULL || 45 db->irq_res == NULL) { 46 dev_err(db->dev, "insufficient resources/n"); 47 ret = -ENOENT; 48 goto out; 49 } 50 /*映射到内核,并获得IO内存的虚拟地址,ioremap完成页表的建立,不同于vmalloc,但是,它实际上不分配内存*/ 51 iosize = res_size(db->addr_res); 52 db->addr_req = request_mem_region(db->addr_res->start, iosize, 53 pdev->name); 54 55 if (db->addr_req == NULL) { 56 dev_err(db->dev, "cannot claim address reg area/n"); 57 ret = -EIO; 58 goto out; 59 } 60 61 db->io_addr = ioremap(db->addr_res->start, iosize); 62 63 if (db->io_addr == NULL) { 64 dev_err(db->dev, "failed to ioremap address reg/n"); 65 ret = -EINVAL; 66 goto out; 67 } 68 69 iosize = res_size(db->data_res); 70 db->data_req = request_mem_region(db->data_res->start, iosize, 71 pdev->name); 72 73 if (db->data_req == NULL) { 74 dev_err(db->dev, "cannot claim data reg area/n"); 75 ret = -EIO; 76 goto out; 77 } 78 79 db->io_data = ioremap(db->data_res->start, iosize); 80 81 if (db->io_data == NULL) { 82 dev_err(db->dev, "failed to ioremap data reg/n"); 83 ret = -EINVAL; 84 goto out; 85 } 86 87 /*获得网络设备的基地址*/ 88 ndev->base_addr = (unsigned long)db->io_addr; 89 /*获得网络设备的中断号*/ 90 ndev->irq = db->irq_res->start; 91 92 /*设置默认的IO函数*/ 93 dm9000_set_io(db, iosize); 94 95 /*如果平台数据不为空*/ 96 if (pdata != NULL) { 97 /* check to see if the driver wants to over-ride the 98 * default IO width */ 99 100 if (pdata->flags & DM9000_PLATF_8BITONLY)101 dm9000_set_io(db, 1);102 103 if (pdata->flags & DM9000_PLATF_16BITONLY)104 dm9000_set_io(db, 2);105 106 if (pdata->flags & DM9000_PLATF_32BITONLY)107 dm9000_set_io(db, 4);108 109 /* check to see if there are any IO routine110 * over-rides */111 112 if (pdata->inblk != NULL)113 db->inblk = pdata->inblk;114 115 if (pdata->outblk != NULL)116 db->outblk = pdata->outblk;117 118 if (pdata->dumpblk != NULL)119 db->dumpblk = pdata->dumpblk;120 121 db->flags = pdata->flags;122 }123 124 #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL125 db->flags |= DM9000_PLATF_SIMPLE_PHY;126 #endif127 /*dm9000复位*/128 dm9000_reset(db);129 /*读取Vendor ID Register,Product ID Register中的值,与0x90000A46比较,如果相等,则说明是DM9000*/130 /* try multiple times, DM9000 sometimes gets the read wrong */131 for (i = 0; i < 8; i++) {132 id_val = ior(db, DM9000_VIDL);133 id_val |= (u32)ior(db, DM9000_VIDH) << 8;134 id_val |= (u32)ior(db, DM9000_PIDL) << 16;135 id_val |= (u32)ior(db, DM9000_PIDH) << 24;136 137 if (id_val == DM9000_ID)138 break;139 dev_err(db->dev, "read wrong id 0x%08x/n", id_val);140 }141 142 if (id_val != DM9000_ID) {143 dev_err(db->dev, "wrong id: 0x%08x/n", id_val);144 ret = -ENODEV;145 goto out;146 }147 148 /* Identify what type of DM9000 we are working on */149 /*读取Chip Revision Register中的值*/150 id_val = ior(db, DM9000_CHIPR);151 dev_dbg(db->dev, "dm9000 revision 0x%02x/n", id_val);152 153 switch (id_val) {154 case CHIPR_DM9000A:155 db->type = TYPE_DM9000A;156 break;157 case CHIPR_DM9000B:158 db->type = TYPE_DM9000B;159 break;160 default:161 dev_dbg(db->dev, "ID %02x => defaulting to DM9000E/n", id_val);162 db->type = TYPE_DM9000E;163 }164 165 /* from this point we assume that we have found a DM9000 */166 167 /* driver system function */168 /*设置部分net_device字段*/169 ether_setup(ndev);170 171 ndev->open = &dm9000_open;172 ndev->hard_start_xmit = &dm9000_start_xmit;173 ndev->tx_timeout = &dm9000_timeout;174 ndev->watchdog_timeo = msecs_to_jiffies(watchdog);175 ndev->stop = &dm9000_stop;176 ndev->set_multicast_list = &dm9000_hash_table;177 /*对ethtool支持的相关声明可在<linux/ethtool.h>中找到。它的核心是一个ethtool_ops类型的结构,里边包含一个全部的24个不同的方法来支持ethtool*/178 ndev->ethtool_ops = &dm9000_ethtool_ops;179 ndev->do_ioctl = &dm9000_ioctl;180 181 #ifdef CONFIG_NET_POLL_CONTROLLER182 ndev->poll_controller = &dm9000_poll_controller;183 #endif184 185 db->msg_enable = NETIF_MSG_LINK;186 db->mii.phy_id_mask = 0x1f;187 db->mii.reg_num_mask = 0x1f;188 db->mii.force_media = 0;189 db->mii.full_duplex = 0;190 db->mii.dev = ndev;191 db->mii.mdio_read = dm9000_phy_read;192 db->mii.mdio_write = dm9000_phy_write;193 /*MAC地址的源是eeprom*/194 mac_src = "eeprom";195 196 /* try reading the node address from the attached EEPROM */197 for (i = 0; i < 6; i += 2)198 dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);199 /*如果从eeprom中读取的地址无效,并且私有数据不为空,从platform_device的私有数据中获取dev_addr*/200 if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {201 mac_src = "platform data";202 memcpy(ndev->dev_addr, pdata->dev_addr, 6);203 }204 /*如果地址依然无效,从PAR:物理地址(MAC)寄存器(Physical Address Register)中读取*/205 if (!is_valid_ether_addr(ndev->dev_addr)) {206 /* try reading from mac */207 208 mac_src = "chip";209 for (i = 0; i < 6; i++)210 ndev->dev_addr[i] = ior(db, i+DM9000_PAR);211 }212 /*查看以太网网卡设备地址是否有效*/213 if (!is_valid_ether_addr(ndev->dev_addr))214 dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "215 "set using ifconfig/n", ndev->name);216 /*将ndev保存到pdev->dev->driver_data中*/217 platform_set_drvdata(pdev, ndev);218 /*一切都初始化好后,注册网络设备*/219 ret = register_netdev(ndev);220 221 if (ret == 0)222 printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)/n",223 ndev->name, dm9000_type_to_char(db->type),224 db->io_addr, db->io_data, ndev->irq,225 ndev->dev_addr, mac_src);226 return 0;227 228 out:229 dev_err(db->dev, "not found (%d)./n", ret);230 231 dm9000_release_board(pdev, db);232 free_netdev(ndev);233 234 return ret;235 }
View Code
挂起和唤醒函数
挂起函数完成了设置挂起标志,并没有真正把设备移除而只是设置了移除标志,复位PHY,停止PHY,禁止所有中断,禁止接收引脚。
1 static int 2 dm9000_drv_suspend(struct platform_device *dev, pm_message_t state) 3 { 4 struct net_device *ndev = platform_get_drvdata(dev); 5 board_info_t *db; 6 7 if (ndev) { 8 db = netdev_priv(ndev); 9 db->in_suspend = 1;10 11 if (netif_running(ndev)) {12 netif_device_detach(ndev);13 dm9000_shutdown(ndev);14 }15 }16 return 0;17 }
View Code
唤醒函数完成了复位dm9000,初始化dm9000,标记设备为attached,清除挂起标志。
1 static int 2 dm9000_drv_resume(struct platform_device *dev) 3 { 4 struct net_device *ndev = platform_get_drvdata(dev); 5 board_info_t *db = netdev_priv(ndev); 6 7 if (ndev) { 8 9 if (netif_running(ndev)) {10 dm9000_reset(db);11 dm9000_init_dm9000(ndev);12 netif_device_attach(ndev);13 }14 db->in_suspend = 0;15 }16 return 0;17 }
View Code
网络设备的打开与释放
首先来看这个open函数
1 static int 2 dm9000_open(struct net_device *dev) 3 { 4 board_info_t *db = netdev_priv(dev); 5 unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK; 6 7 if (netif_msg_ifup(db)) 8 dev_dbg(db->dev, "enabling %s/n", dev->name); 9 10 /* If there is no IRQ type specified, default to something that11 * may work, and tell the user that this is a problem */12 13 if (irqflags == IRQF_TRIGGER_NONE)14 dev_warn(db->dev, "WARNING: no IRQ resource flags set./n");15 16 irqflags |= IRQF_SHARED;17 /*注册中断处理函数*/18 if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev))19 return -EAGAIN;20 21 /* Initialize DM9000 board */22 /*复位DM9000*/23 dm9000_reset(db);24 /*初始化DM9000的寄存器*/25 dm9000_init_dm9000(dev);26 27 /* Init driver variable */28 db->dbug_cnt = 0;29 /*检查链路载波状况*/30 mii_check_media(&db->mii, netif_msg_link(db), 1);31 /*启动发送队列*/32 netif_start_queue(dev);33 /*之前在probe函数中调用 INIT_DELAYED_WORK初始化了工作队列,并关联了一个操作函数dm9000_poll_work(),此时运行dm9000_schedule_poll来调用这个函数*/34 dm9000_schedule_poll(db);35 36 return 0;37 }
View Code
然后是stop函数
1 static int 2 dm9000_stop(struct net_device *ndev) 3 { 4 board_info_t *db = netdev_priv(ndev); 5 6 if (netif_msg_ifdown(db)) 7 dev_dbg(db->dev, "shutting down %s/n", ndev->name); 8 /*杀死延时工作队列phy_poll*/ 9 cancel_delayed_work_sync(&db->phy_poll);10 /*停止发送队列*/11 netif_stop_queue(ndev);12 /*通知内核链路失去连接*/13 netif_carrier_off(ndev);14 /* free interrupt */15 free_irq(ndev->irq, ndev);16 /*关闭DM9000*/17 dm9000_shutdown(ndev);18 return 0;19 }
View Code
复位PHY,停止PHY,禁止所有中断,禁止接收引脚。
1 static void 2 dm9000_shutdown(struct net_device *dev) 3 { 4 board_info_t *db = netdev_priv(dev); 5 6 /* RESET device */ 7 dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */ 8 iow(db, DM9000_GPR, 0x01); /* Power-Down PHY */ 9 iow(db, DM9000_IMR, IMR_PAR); /* Disable all interrupt */10 iow(db, DM9000_RCR, 0x00); /* Disable RX */11 }
View Code
中断处理函数
发生中断的情况有3种:
- 接收到数据
- 发送完数据
- 链路状态改变
1 static irqreturn_t dm9000_interrupt(int irq, void *dev_id) 2 { 3 struct net_device *dev = dev_id; 4 board_info_t *db = netdev_priv(dev); 5 int int_status; 6 unsigned long flags; 7 u8 reg_save; 8 9 dm9000_dbg(db, 3, "entering %s/n", __func__);10 11 /* A real interrupt coming */12 13 /*保存中断到flags中*/14 spin_lock_irqsave(&db->lock, flags);15 16 /*保存寄存器地址*/17 reg_save = readb(db->io_addr);18 19 /*禁止DM9000的所有中断*/20 iow(db, DM9000_IMR, IMR_PAR);21 22 /*获得DM9000的中断状态*/23 int_status = ior(db, DM9000_ISR); /* Got ISR */24 iow(db, DM9000_ISR, int_status); /* Clear ISR status */25 26 if (netif_msg_intr(db))27 dev_dbg(db->dev, "interrupt status %02x/n", int_status);28 29 /*检查Interrupt Status Register的第0位,看有没有接收数据*/30 if (int_status & ISR_PRS)31 dm9000_rx(dev);32 33 /*检查Interrupt Status Register的第1位,看有没有发送完数据*/34 if (int_status & ISR_PTS)35 dm9000_tx_done(dev, db);36 37 if (db->type != TYPE_DM9000E) {38 /*检查Interrupt Status Register的第5位,看链路状态有没有变化*/39 if (int_status & ISR_LNKCHNG) {40 /* fire a link-change request */41 schedule_delayed_work(&db->phy_poll, 1);42 }43 }44 45 /*重新使能相应中断*/46 iow(db, DM9000_IMR, db->imr_all);47 48 /*还原原先的io地址*/49 writeb(reg_save, db->io_addr);50 /*还原中断状态*/51 spin_unlock_irqrestore(&db->lock, flags);52 53 return IRQ_HANDLED;54 }
View Code
下面说一下DM9000A中的存储部分,DM9000A内部有一个4K Dword SRAM,其中3KB是作为发送,16KB作为接收,如下图所示。
其中0×0000~0x0BFF是传说中的TX buffer(TX buffer中只能存放两个包),0x0C00~0x3FFF是RX buffer
因此在写内存操作时,当IMR的第7位被设置,如果到达了地址的结尾比如到了3KB,则回卷到0。
相似的方式,在读操作中,当IMR的第7位被设置如果到达了地址的结尾比如16K,则回卷到0x0C00。
那么传说中的发送函数又是哪个呢,在probe函数里进行了初始化函数指针操作。
1 ndev->open = &dm9000_open;2 ndev->hard_start_xmit = &dm9000_start_xmit;3 ndev->tx_timeout = &dm9000_timeout;4 ndev->watchdog_timeo = msecs_to_jiffies(watchdog);5 ndev->stop = &dm9000_stop;6 ndev->set_multicast_list = &dm9000_hash_table;7 ndev->ethtool_ops = &dm9000_ethtool_ops;8 ndev->do_ioctl = &dm9000_ioctl;
View Code
可以看到当上层调用hard_start_xmit时,在我们的驱动程序中实际调用的是dm9000_start_xmit,下面来分析一dm9000_start_xmit的源码。
发送函数dm9000_start_xmit
1 /* 2 * Hardware start transmission. 3 * Send a packet to media from the upper layer. 4 */ 5 static int 6 dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev) 7 { 8 unsigned long flags; 9 /*获得ndev的私有数据,也就是芯片相关的信息*/10 board_info_t *db = netdev_priv(dev);11 12 dm9000_dbg(db, 3, "%s:/n", __func__);13 /*只能存放两个,如果已经有两个就退出*/14 if (db->tx_pkt_cnt > 1)15 return 1;16 17 spin_lock_irqsave(&db->lock, flags);18 /*19 *MWCMD是Memory data write command with address increment Register(F8H)20 *写数据到TX SRAM21 *写这个命令后,根据IO操作模式(8-bit or 16-bit)来增加写指针1或222 */23 writeb(DM9000_MWCMD, db->io_addr);24 /*把数据从sk_buff中拷贝到TX SRAM中*/25 (db->outblk)(db->io_data, skb->data, skb->len);26 /*为了统计发送了多少个字节,这个在使用ifconfig中显示出的那个发送了多少个字节就是这里计算的*/27 dev->stats.tx_bytes += skb->len;28 /*待发送的计数加一*/29 db->tx_pkt_cnt++;30 /*如果只有一个要发送就立即发送,如果这是第二个就应该进行排队了*/31 if (db->tx_pkt_cnt == 1) {32 /*把数据的长度填到TXPLL(发送包长度低字节)和TXPLH(发送包长度高字节)中*/33 iow(db, DM9000_TXPLL, skb->len);34 iow(db, DM9000_TXPLH, skb->len >> 8);35 /*置发送控制寄存器(TX Control Register)的发送请求位TXREQ(Auto clears after sending completely),这样就可以发送出去了*/36 iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */37 /*38 *记下此时的时间,这里起一个时间戳的作用,之后的超时会用到。如果当前的系统时间超过设备的trans_start时间39 *至少一个超时周期,网络层将最终调用驱动程序的tx_timeout。那个这个"一个超时周期"又是什么呢?这个是我们在40 *probe函数中设置的,ndev->watchdog_timeo = msecs_to_jiffies(watchdog);41 */42 dev->trans_start = jiffies; /* save the time stamp */43 } else {44 /*如果是第二个包则把skb->len复制给队列的queue_pkt_len,然后告诉上层停止发送队列,因为发送队列已经满了*/45 db->queue_pkt_len = skb->len;46 netif_stop_queue(dev);47 }48 spin_unlock_irqrestore(&db->lock, flags);49 /*每个数据包写入网卡SRAM后都要释放skb*/50 dev_kfree_skb(skb);51 52 return 0;53 }
View Code
下面来看一下刚才提到的那个超时函数,发送超时函数:
1 /* Our watchdog timed out. Called by the networking layer */ 2 static void dm9000_timeout(struct net_device *dev) 3 { 4 board_info_t *db = netdev_priv(dev); 5 u8 reg_save; 6 unsigned long flags; 7 8 /* Save previous register address */ 9 reg_save = readb(db->io_addr);10 spin_lock_irqsave(&db->lock, flags);11 12 netif_stop_queue(dev);13 dm9000_reset(db);14 dm9000_init_dm9000(dev);15 /* We can accept TX packets again */16 dev->trans_start = jiffies;17 netif_wake_queue(dev);18 19 /* Restore previous register address */20 writeb(reg_save, db->io_addr);21 spin_unlock_irqrestore(&db->lock, flags);22 }
View Code
这个函数首先停止了发送队列,然后复位dm9000,初始化dm9000,重新设置了时间戳,然后唤醒发送队列,通知网络子系统可再次传输数据包。
1 /* 2 * DM9000 interrupt handler 3 * receive the packet to upper layer, free the transmitted packet 4 */ 5 6 static void dm9000_tx_done(struct net_device *dev, board_info_t *db) 7 { 8 /*从网络状态寄存器(Network Status Register)中获得发送状态*/ 9 int tx_status = ior(db, DM9000_NSR); 10 /*如果发送状态为NSR_TX2END(第二个包发送完毕)或NSR_TX1END(第一个包发送完毕)*/11 if (tx_status & (NSR_TX2END | NSR_TX1END)) {12 /*如果一个数据包发送完,待发送数据包个数减1*/13 db->tx_pkt_cnt--;14 /*如果一个数据包发送完,已发送数据包个数加1*/15 dev->stats.tx_packets++;16 17 if (netif_msg_tx_done(db))18 dev_dbg(db->dev, "tx done, NSR %02x/n", tx_status);19 20 /*如果还有数据包要发送*/21 if (db->tx_pkt_cnt > 0) {22 /*将发送的长度放到TXPLL,TXPLH寄存器中*/23 iow(db, DM9000_TXPLL, db->queue_pkt_len);24 iow(db, DM9000_TXPLH, db->queue_pkt_len >> 8);25 /*置发送请求位*/26 iow(db, DM9000_TCR, TCR_TXREQ);27 /*保存时间戳*/28 dev->trans_start = jiffies;29 }30 /*通知内核将待发送数据包放入发送队列*/31 netif_wake_queue(dev);32 }33 }
View Code
接收函数dm9000_rx
每接受到一个包,DM9000都会在包的前面加上4个字节,”01H”,status与RSR(RX Status Register)的内容相同,”LENL”(数据包长度低8位),”LENH”(数据包长度高8位)。
所以首先要读取这4个字节来确定数据包的状态,第一个字节“01H”表示接下来的是有效的数据包,”00H”表示没有数据包,若为其他值则表示网卡没有正确初始化,需要从新初始化。
这4个字节封装在一起:
1 struct dm9000_rxhdr {2 u8 RxPktReady;3 u8 RxStatus;4 __le16 RxLen;5 } __attribute__((__packed__));
View Code
清晰一些如下图
接收函数如下
1 /* 2 * Received a packet and pass to upper layer 3 */ 4 static void 5 dm9000_rx(struct net_device *dev) 6 { 7 board_info_t *db = netdev_priv(dev); 8 struct dm9000_rxhdr rxhdr; 9 struct sk_buff *skb; 10 u8 rxbyte, *rdptr; 11 bool GoodPacket; 12 int RxLen; 13 14 /* Check packet ready or not */ 15 do { 16 /*MRCMDX为内存数据预取读命令,并且没有地址增加,读数据的第一个字节,直到读到0x01(数据有效)为止。*/ 17 ior(db, DM9000_MRCMDX); /* Dummy read */ 18 /*获得数据*/ 19 rxbyte = readb(db->io_data); 20 /*因为只能为0x00或0x01,所以如果大于0x01,则返回*/ 21 if (rxbyte > DM9000_PKT_RDY) { 22 dev_warn(db->dev, "status check fail: %d/n", rxbyte); 23 /*停止设备*/ 24 iow(db, DM9000_RCR, 0x00); 25 /*停止中断请求*/ 26 iow(db, DM9000_ISR, IMR_PAR); 27 return; 28 } 29 /*如果为0x00,则返回*/ 30 if (rxbyte != DM9000_PKT_RDY) 31 return; 32 33 /*如果有有效数据包,设置标志标量*/ 34 GoodPacket = true; 35 /*MRCMD是地址增加的内存数据读命令*/ 36 writeb(DM9000_MRCMD, db->io_addr); 37 /*读取RX SRAM中的数据放入struct dm9000_rxhdr中*/ 38 (db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr)); 39 /*将一个无符号的26位小头数值转换成CPU使用的值*/ 40 RxLen = le16_to_cpu(rxhdr.RxLen); 41 42 if (netif_msg_rx_status(db)) 43 dev_dbg(db->dev, "RX: status %02x, length %04x/n", 44 rxhdr.RxStatus, RxLen); 45 46 /*一个数据包的长度应大于64字节*/ 47 if (RxLen < 0x40) { 48 GoodPacket = false; 49 if (netif_msg_rx_err(db)) 50 dev_dbg(db->dev, "RX: Bad Packet (runt)/n"); 51 } 52 /*一个数据包的长度应小于1536字节*/ 53 if (RxLen > DM9000_PKT_MAX) { 54 dev_dbg(db->dev, "RST: RX Len:%x/n", RxLen); 55 } 56 57 /* rxhdr.RxStatus is identical to RSR register. */ 58 if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE | 59 RSR_PLE | RSR_RWTO | 60 RSR_LCS | RSR_RF)) { 61 GoodPacket = false; 62 if (rxhdr.RxStatus & RSR_FOE) { 63 if (netif_msg_rx_err(db)) 64 dev_dbg(db->dev, "fifo error/n"); 65 dev->stats.rx_fifo_errors++; 66 } 67 if (rxhdr.RxStatus & RSR_CE) { 68 if (netif_msg_rx_err(db)) 69 dev_dbg(db->dev, "crc error/n"); 70 dev->stats.rx_crc_errors++; 71 } 72 if (rxhdr.RxStatus & RSR_RF) { 73 if (netif_msg_rx_err(db)) 74 dev_dbg(db->dev, "length error/n"); 75 dev->stats.rx_length_errors++; 76 } 77 } 78 79 /* Move data from DM9000 */ 80 if (GoodPacket 81 /*分配一个套接字缓冲区*/ 82 && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) { 83 skb_reserve(skb, 2); 84 rdptr = (u8 *) skb_put(skb, RxLen - 4); 85 86 /**/ 87 (db->inblk)(db->io_data, rdptr, RxLen); 88 dev->stats.rx_bytes += RxLen; 89 90 /*以太网支持代码导出了辅助函数eth_type_trans,用来查找填入protocol中的正确值*/ 91 skb->protocol = eth_type_trans(skb, dev); 92 /*将套接字缓冲区发向上层*/ 93 netif_rx(skb); 94 /*增加发送包的引用计数*/ 95 dev->stats.rx_packets++; 96 97 } else { 98 /*如果该数据包是坏的,则清理该数据包*/ 99 (db->dumpblk)(db->io_data, RxLen);100 }101 } while (rxbyte == DM9000_PKT_RDY);102 }
View Code
如下图链路层包的传输过程
在netif_rx中会调用napi_schedule,然后该函数又会去调用__napi_schedule()。
在函数__napi_schedule()中会去调用设备的poll函数,它是设备自己注册的。
在设备的poll函数中,会去调用netif_receive_skb函数,在该函数中有下面一条语句pt_prev->func,此处的func为一个函数指针,在之前的注册中设置为ip_rcv。
因此,就完成了从链路层上传到网络层的这一个过程了- Linux网络设备驱动结构
- Linux网络设备结构
- Linux网络设备驱动结构概述
- Linux网络设备驱动结构概述
- linux网络设备驱动的结构
- Linux网络设备驱动的结构
- Linux 网络设备驱动之层次结构
- Linux 网络设备驱动开发(一) —— linux内核网络分层结构
- Linux 网络设备驱动开发(一) —— linux内核网络分层结构
- linux网络设备驱动
- linux 网络设备驱动-DM9000
- Linux网络设备分析
- Linux网络设备驱动编程
- linux网络设备层次
- Linux网络设备驱动概述
- linux网络设备—PHY
- linux网络设备初始化
- linux网络设备—PHY
- 深入浅出OpenStack云计算平台管理(nova-compute/network)
- 自定义图谱控件
- 在window server 2003上 让sql server 2000用大于4G的内存
- ssh样式
- Java 调用对象方法的执行过程
- Linux网络设备结构
- Tips for speed up your algorithm in the CUDA programming
- 【机器学习-斯坦福】学习笔记15 主成分分析(Principal components analysis)-最小平方误差解释
- 做项目
- 将博客搬至CSDN
- 网页mp3播放器不兼容各种浏览器的解决方法
- document.body和document.documentElement比较
- linux直接退出到根目录的方法
- iOS method only defined for abstract class