首先看一下Linux網絡設備的結構,以下圖:node
網絡協議接口層向網絡層協議提供提供統一的數據包收發接口,不論上層協議爲ARP仍是IP,都經過dev_queue_xmit()函數發送數據,並經過netif_rx()函數接受數據。這一層的存在使得上層協議獨立於具體的設備。linux
網絡設備接口層向協議接口層提供統一的用於描述具體網絡設備屬性和操做的結構體net_device,該結構體是設備驅動功能層中各函數的容器。實際上,網絡設備接口層從宏觀上規劃了具體操做硬件的設備驅動功能層的結構。ios
設備驅動功能層各函數是網絡設備接口層net_device數據結構的具體成員,是驅使網絡設備硬件完成相應動做的程序,他經過hard_start_xmit()函數啓動發送操做,並經過網絡設備上的中斷觸發接受操做。api
網絡設備與媒介層是完成數據包發送和接受的物理實體,包括網絡適配器和具體的傳輸媒介,網絡適配器被驅動功能層中的函數物理上驅動。對於Linux系統而言,網絡設備和媒介均可以是虛擬的。網絡
上面的結構還能夠細分,以下圖數據結構
這裏主要進行數據包的收發,使用函數原型爲:ide
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內核後已經不存在了,以下圖:性能
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。ui
參數len爲數據緩衝區的空間大小,一般以L1_CACHE_BYTES字節(對ARM爲32)對齊,參數priority爲內存分配的優先級。
dev_alloc_skb()函數以GFP_ATOMIC優先級進行skb的分配。
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,拷貝進去再增長,這樣下降了性能。
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 }
下面來分析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;
這個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 routine 110 * 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_POLL 125 db->flags |= DM9000_PLATF_SIMPLE_PHY; 126 #endif 127 /*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_CONTROLLER 182 ndev->poll_controller = &dm9000_poll_controller; 183 #endif 184 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 }
掛起函數完成了設置掛起標誌,並無真正把設備移除而只是設置了移除標誌,復位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 }
喚醒函數完成了復位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 }
首先來看這個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 that 11 * 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 }
而後是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 }
復位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 }
發生中斷的狀況有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 }
下面說一下DM9000A中的存儲部分,DM9000A內部有一個4K Dword SRAM,其中3KB是做爲發送,16KB做爲接收,以下圖所示。
其中0x0000~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;
能夠看到當上層調用hard_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 SRAM 21 *寫這個命令後,根據IO操做模式(8-bit or 16-bit)來增長寫指針1或2 22 */ 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 }
下面來看一下剛纔提到的那個超時函數,發送超時函數:
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 }
這個函數首先中止了發送隊列,而後復位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 }
每接受到一個包,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__));
清晰一些以下圖
接收函數以下
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 }
以下圖鏈路層包的傳輸過程
在netif_rx中會調用napi_schedule,而後該函數又會去調用__napi_schedule()。
在函數__napi_schedule()中會去調用設備的poll函數,它是設備本身註冊的。
在設備的poll函數中,會去調用netif_receive_skb函數,在該函數中有下面一條語句pt_prev->func,此處的func爲一個函數指針,在以前的註冊中設置爲ip_rcv。
所以,就完成了從鏈路層上傳到網絡層的這一個過程了。
文章出自李大鵬,源博文已被博主刪掉,如下是博主博客地址
http://blog.csdn.net/woshixingaaa