【linux驅動分析】之dm9000驅動分析(六):dm9000_init和dm9000_probe的實現

一、dm9000_init
打印出驅動的版本號,註冊dm9000_driver驅動,將驅動添加到總線上,執行match,如果匹配,將會執行probe函數。
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 }

二、dm9000_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     /* 把mach-mini6410.c中定義的dm9000_plat_data傳遞過來 */
  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     /* Init network device */
 18     /* 分配一個名爲eth%d的網絡設備,同時分配一個私有數據區,數據區是32字節對齊 */
 19     ndev = alloc_etherdev(sizeof(struct board_info));
 20     if (!ndev) {
 21         dev_err(&pdev->dev, "could not allocate device.\n");
 22         return -ENOMEM;
 23     }
 24     /* 把網絡設備的基類dev的父指針設爲平臺設備的基類dev */
 25     SET_NETDEV_DEV(ndev, &pdev->dev);
 26 
 27     dev_dbg(&pdev->dev, "dm9000_probe()\n");
 28 
 29     /* setup board info structure */
 30     /* 設置私有數據,下面會具體分析這個函數 */
 31     db = netdev_priv(ndev);
 32     /* 給私有數據賦值 */
 33     db->dev = &pdev->dev;
 34     db->ndev = ndev;
 35     
 36     /* 初始化一個自旋鎖和一個互斥體 */
 37     spin_lock_init(&db->lock);
 38     mutex_init(&db->addr_lock);
 39     
 40     /* 往工作隊列插入一個工作,隨後我們調用schedule_delayed_work就會執行傳遞的函數
 41      * 關於工作隊列會有專門一篇文章來學習總結
 42      */
 43     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
 44 
 45     /* 獲得資源,這個函數會在下面講解 */
 46     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 47     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 48     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 49 
 50     if (db->addr_res == NULL || db->data_res == NULL ||
 51         db->irq_res == NULL) {
 52         dev_err(db->dev, "insufficient resources\n");
 53         ret = -ENOENT;
 54         goto out;
 55     }
 56     
 57     /* 獲取中斷號,這個中斷號是不存在的,因爲resource裏只有一箇中斷號 */
 58     db->irq_wake = platform_get_irq(pdev, 1);
 59     if (db->irq_wake >= 0) {
 60         dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake);
 61         
 62         /* 爲ndev申請中斷,中斷服務程序爲dm9000_wol_interrupt,關於中斷也會有一篇文章來學習總結 */
 63         ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
 64                   IRQF_SHARED, dev_name(db->dev), ndev);
 65         if (ret) {
 66             dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);
 67         } else {
 68 
 69             /* test to see if irq is really wakeup capable */
 70             ret = set_irq_wake(db->irq_wake, 1);
 71             if (ret) {
 72                 dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",
 73                     db->irq_wake, ret);
 74                 ret = 0;
 75             } else {
 76                 set_irq_wake(db->irq_wake, 0);
 77                 db->wake_supported = 1;
 78             }
 79         }
 80     }
 81     /* 返回dm9000內存資源的大小,下面一句是申請內存,關於內存的申請和分配也會有一篇文章 */
 82     iosize = resource_size(db->addr_res);
 83     db->addr_req = request_mem_region(db->addr_res->start, iosize,
 84                       pdev->name);
 85 
 86     if (db->addr_req == NULL) {
 87         dev_err(db->dev, "cannot claim address reg area\n");
 88         ret = -EIO;
 89         goto out;
 90     }
 91     /* 存放地址的內存空間開始地址,地址寄存器,一共佔3個地址,
 92      * 分別是0x18000000,0x18000001,0x18000002,0x18000003,
 93      * 這也是一個巧妙之處,dm9000芯片的cmd引腳接的是arm11的addr2,
 94      * 所以寫“0地址”代表送地址,讀寫4地址表示讀寫數據
 95      * */
 96     db->io_addr = ioremap(db->addr_res->start, iosize);
 97 
 98     if (db->io_addr == NULL) {
 99         dev_err(db->dev, "failed to ioremap address reg\n");
100         ret = -EINVAL;
101         goto out;
102     }
103 
104     iosize = resource_size(db->data_res);
105     db->data_req = request_mem_region(db->data_res->start, iosize,
106                       pdev->name);
107 
108     if (db->data_req == NULL) {
109         dev_err(db->dev, "cannot claim data reg area\n");
110         ret = -EIO;
111         goto out;
112     }
113     /* 數據寄存器的地址,1MB的空間 */
114     db->io_data = ioremap(db->data_res->start, iosize);
115 
116     if (db->io_data == NULL) {
117         dev_err(db->dev, "failed to ioremap data reg\n");
118         ret = -EINVAL;
119         goto out;
120     }
121 
122     /* fill in parameters for net-dev structure */
123     ndev->base_addr = (unsigned long)db->io_addr;
124     ndev->irq    = db->irq_res->start;
125 
126     /* ensure at least we have a default set of IO routines */
127     /* iosize是一個很大的值,這裏是先保證有個默認值位寬,32位 */
128     dm9000_set_io(db, iosize);
129 
130     /* check to see if anything is being over-ridden */
131     if (pdata != NULL) {
132         /* check to see if the driver wants to over-ride the
133          * default IO width */
134 
135         if (pdata->flags & DM9000_PLATF_8BITONLY)
136             dm9000_set_io(db, 1);
137         /*
138          * 我們這裏設置的是16位,他會做下面幾件事:
139          * db->dumpblk = dm9000_dumpblk_16bit;
140          * db->outblk  = dm9000_outblk_16bit;
141          * db->inblk   = dm9000_inblk_16bit;
142          */
143         if (pdata->flags & DM9000_PLATF_16BITONLY)
144             dm9000_set_io(db, 2);
145 
146         if (pdata->flags & DM9000_PLATF_32BITONLY)
147             dm9000_set_io(db, 4);
148 
149         /* check to see if there are any IO routine
150          * over-rides */
151 
152         if (pdata->inblk != NULL)
153             db->inblk = pdata->inblk;
154 
155         if (pdata->outblk != NULL)
156             db->outblk = pdata->outblk;
157 
158         if (pdata->dumpblk != NULL)
159             db->dumpblk = pdata->dumpblk;
160 
161         db->flags = pdata->flags;
162     }
163 
164 #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL
165     db->flags |= DM9000_PLATF_SIMPLE_PHY;
166 #endif
167     /*
168      * dm9000_reset函數是執行下面兩句話,中間有延時,這裏省略了
169      * writeb(DM9000_NCR, db->io_addr); //先寫NCR寄存器的地址到“0地址”(寫到0地址就代表寫地址)
170      * writeb(NCR_RST, db->io_data);    //再給“4地址”寫NCR_RST(0x01),即NCR = 1;
171      * 讀寫“4地址”就相當於發送數據,cmd引腳連的是addr2,dm9000地址線數據線複用
172      * */
173     dm9000_reset(db);
174 
175     /* try multiple times, DM9000 sometimes gets the read wrong */
176     /* 下面所做的是讀出dm9000的供應商ID和產品ID */
177     for (i = 0; i < 8; i++) {
178         /* ior是從reg讀出數據,類型是u8,它的原理與上面分析reset函數的原理是一樣的 */
179         id_val  = ior(db, DM9000_VIDL);
180         id_val |= (u32)ior(db, DM9000_VIDH) << 8;
181         id_val |= (u32)ior(db, DM9000_PIDL) << 16;
182         id_val |= (u32)ior(db, DM9000_PIDH) << 24;
183 
184         if (id_val == DM9000_ID)
185             break;
186         dev_err(db->dev, "read wrong id 0x%08x\n", id_val);
187     }
188 
189     if (id_val != DM9000_ID) {
190         dev_err(db->dev, "wrong id: 0x%08x\n", id_val);
191         ret = -ENODEV;
192         goto out;
193     }
194 
195     /* Identify what type of DM9000 we are working on */
196     /* 讀出芯片版本寄存器,判斷dm9000的型號,默認是dm9000E */
197     id_val = ior(db, DM9000_CHIPR);
198     dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);
199 
200     switch (id_val) {
201     case CHIPR_DM9000A:
202         db->type = TYPE_DM9000A;
203         break;
204     case CHIPR_DM9000B:
205         db->type = TYPE_DM9000B;
206         break;
207     default:
208         dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);
209         db->type = TYPE_DM9000E;
210     }
211 
212     /* dm9000a/b are capable of hardware checksum offload */
213     if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {
214         db->can_csum = 1;
215         db->rx_csum = 1;
216         ndev->features |= NETIF_F_IP_CSUM;
217     }
218 
219     /* from this point we assume that we have found a DM9000 */
220 
221     /* driver system function */
222     /* 這個函數是初始化ndev的一些成員 */
223     ether_setup(ndev);
224 
225     /* 下面也是初始化ndev的一些成員 */
226     ndev->netdev_ops    = &dm9000_netdev_ops;
227     ndev->watchdog_timeo    = msecs_to_jiffies(watchdog);
228     ndev->ethtool_ops    = &dm9000_ethtool_ops;
229 
230     db->msg_enable       = NETIF_MSG_LINK;
231     db->mii.phy_id_mask  = 0x1f;
232     db->mii.reg_num_mask = 0x1f;
233     db->mii.force_media  = 0;
234     db->mii.full_duplex  = 0;
235     db->mii.dev         = ndev;
236     db->mii.mdio_read    = dm9000_phy_read;
237     db->mii.mdio_write   = dm9000_phy_write;
238 
239     mac_src = "eeprom";
240     /* node address是在網絡中的一個電腦或終端的號碼或名字,
241      * 這裏從eeprom讀取,由於我們沒有,所以它讀回來的是6個FF
242      * */
243     /* try reading the node address from the attached EEPROM */
244     for (i = 0; i < 6; i += 2)
245         dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);
246 
247     /* try MAC address passed by kernel command line */
248     /* 這個函數是友善之臂添加的,它在mach-mini6410裏添加了這樣一句話__setup("ethmac=", dm9000_set_mac);
249      * 內核啓動時,遇到"ethmac="回去執行dm9000_set_mac函數,所以就實現了mac從內核傳遞過來
250      * 這也是一個很巧妙的設計,需要寫一篇文章學習總結一下
251      * */
252     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
253         mac_src = "param data";
254         memcpy(ndev->dev_addr, pdata->param_addr, 6);
255     }
256     /* 下面是讀取mac的幾種方法,當前這一種是從dm9000的Physical Address Register讀取 */
257     if (!is_valid_ether_addr(ndev->dev_addr)) {
258         /* try reading from mac */
259         mac_src = "chip";
260         for (i = 0; i < 6; i++)
261             ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
262     }
263     /* 從pdata裏的dev_addr讀取 */
264     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
265         mac_src = "platform data";
266         memcpy(ndev->dev_addr, pdata->dev_addr, 6);
267     }
268     /* 沒有讀到有效的mac地址,提示用ifconfig命令設置 */
269     if (!is_valid_ether_addr(ndev->dev_addr))
270         dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
271              "set using ifconfig\n", ndev->name);
272 
273     /* 
274      * 這裏由於ndev是我們定義的一個局部變量,所以要ndev傳遞給平臺設備pdev
275      * 即pdev->dev->p->driver_data = ndev;
276      * 要使用是通過platform_get_drvdata獲得
277      * */
278     platform_set_drvdata(pdev, ndev);
279     /* net_device結構體初始化好後,剩餘的工作就是把該結構傳遞給register_netdev函數,
280      * 當調用register_netdev後就可以用驅動程序操作設備了,所以
281      * 必須在初始化一切事情後再註冊
282      * */
283     ret = register_netdev(ndev);
284 
285     if (ret == 0)
286         printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",
287                ndev->name, dm9000_type_to_char(db->type),
288                db->io_addr, db->io_data, ndev->irq,
289                ndev->dev_addr, mac_src);
290     return 0;
291 
292 out:
293     dev_err(db->dev, "not found (%d).\n", ret);
294 
295     dm9000_release_board(pdev, db);
296     free_netdev(ndev);
297 
298     return ret;
299 }
300 /*********** probe函數大功告成 *************/
301 

三、總結probe函數分析是留下的問題
在上面用紅色標記出來了要分析的東西
1、分析netdev_priv
在執行 ndev = alloc_etherdev(sizeof(struct board_info));時,先分配了一個net_device結構,又分配了一個board_info結構體,作爲ndev的私有數據,然後執行了db = netdev_priv( ndev );來獲得私有數據的開始地址,並在以後做初始化。
在probe函數最後,通過platform_set_drvdata函數把ndev結構傳給了平臺設備,以供使用,那麼我猜想這裏把board_info也傳過去了,以後也可以用,獲得它的地址的方法是通過下面的函數。
303 /**
304  *    netdev_priv - access network device private data
305  *    @dev: network device
306  *
307  * Get network device private data
308  */
309 static inline void *netdev_priv(const struct net_device *dev)
310 {
311     return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);
312 }

2、platform_get_resource函數分析
46     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
47     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
48     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
platform_data的關係是這樣的:platform device->dev->platform_data
對於dm9000驅動來說是這樣實現的:
8     struct dm9000_plat_data *pdata = pdev->dev.platform_data;
static struct dm9000_plat_data dm9000_setup = {
	.flags		= DM9000_PLATF_16BITONLY | DM9000_PLATF_EXT_PHY,
	.dev_addr       = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 },
}; 

3、以後要寫文章總結的東西
(1)、關於工作隊列的要總結一下
43     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);

(2)、關於內核中斷的原理和操作方法
63         ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
64                   IRQF_SHARED, dev_name(db->dev), ndev);

(3)、關於內核內存分配和操作方法
83     db->addr_req = request_mem_region(db->addr_res->start, iosize,
84                       pdev->name);
(4)、關於__setup的作用
__setup("ethmac=", dm9000_set_mac);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章