【驅動】DM9000A網卡驅動框架源碼分析

Linux網絡設備結構

  首先看一下Linux網絡設備的結構,以下圖:node

  

  1. 網絡協議接口層向網絡層協議提供提供統一的數據包收發接口,不論上層協議爲ARP仍是IP,都經過dev_queue_xmit()函數發送數據,並經過netif_rx()函數接受數據。這一層的存在使得上層協議獨立於具體的設備。linux

  2. 網絡設備接口層向協議接口層提供統一的用於描述具體網絡設備屬性和操做的結構體net_device該結構體是設備驅動功能層中各函數的容器。實際上,網絡設備接口層從宏觀上規劃了具體操做硬件的設備驅動功能層的結構。ios

  3. 設備驅動功能層各函數是網絡設備接口層net_device數據結構的具體成員,是驅使網絡設備硬件完成相應動做的程序,他經過hard_start_xmit()函數啓動發送操做,並經過網絡設備上的中斷觸發接受操做。api

  4. 網絡設備與媒介層是完成數據包發送和接受的物理實體,包括網絡適配器和具體的傳輸媒介,網絡適配器被驅動功能層中的函數物理上驅動。對於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內核後已經不存在了,以下圖:性能

  

操做套接字緩衝區的函數

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。ui

  參數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 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 }
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 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 }
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. 接收到數據
  2. 發送完數據
  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做爲接收,以下圖所示。

  

  其中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;
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 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 }
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。

  所以,就完成了從鏈路層上傳到網絡層的這一個過程了。

 


 

參考博客

  文章出自李大鵬,源博文已被博主刪掉,如下是博主博客地址

  http://blog.csdn.net/woshixingaaa

相關文章
相關標籤/搜索