Linux驅動修煉之道-DM9000A網絡卡驅動框架原始碼分析
網路裝置的初始化:
通過模組的載入函式看出DM9000A的驅動是以平臺驅動的形式註冊進核心的,下邊是模組的載入函式:
1.static int __init
2.dm9000_init(void)
2.{
3. printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION); 4.
5. return platform_driver_register(&dm9000_driver); 6.}
下邊是平臺驅動結構體:
1.static struct platform_driver dm9000_driver = {
2. .driver = {
3. .name = "dm9000",
2. .owner = THIS_MODULE,
3. },
4. .probe = dm9000_probe,
5. .remove = __devexit_p(dm9000_drv_remove),
6. .suspend = dm9000_drv_suspend,
7. .resume = dm9000_drv_resume,
8.};
下面來分析probe函式,用來執行分配的核心函式是alloc_netdev,函式原型是:
1.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中:
1.struct net_device *alloc_etherdev(int sizeof_priv);
該函式使用eth%d的形式指定分配給網路裝置的名字。它提供了自己的初始化函式(ether_setup),用正確的值為乙太網裝置設定net_device中的許多成員。那麼在DM9000A中這個私有資料成員是什麼呢,看下邊的結構:
1./* Structure/enum declaration ------------------------------- */
2.typedef struct board_info {
2.
3. void __iomem *io_addr; /* Register I/O base address */ 4. void __iomem *io_data; /* Data I/O address */ 5. u16 irq; /* IRQ */ 6.
7. u16 tx_pkt_cnt;
8. u16 queue_pkt_len;
9. u16 queue_start_addr;
10. u16 dbug_cnt;
11. u8 io_mode; /* 0:word, 2:byte */ 12. u8 phy_addr;
13. u8 imr_all;
14.
15. unsigned int flags; 16. unsigned int in_suspend :1; 17. int debug_level; 18.
19. enum dm9000_type type; 20.
21. void (*inblk)(void __iomem *port, void *data, int length); 22. void (*outblk)(void __iomem *port, void *data, int length); 23. void (*dumpblk)(void __iomem *port, int length); 24.
25. struct device *dev; /* parent device */ 26.
27. struct resource *addr_res; /* resources found */ 28. struct resource *data_res; 29. struct resource *addr_req; /* resources requested */ 30. struct resource *data_req; 31. struct resource *irq_res; 32.
33. struct mutex addr_lock; /* phy and eeprom access lock */ 34.
35. struct delayed_work phy_poll; 36. struct net_device *ndev; 37.
38. spinlock_t lock; 39.
40. struct mii_if_info mii; 41. u32 msg_enable;
42.} board_info_t;
這個struct board_info就是那個私有資料,用來儲存晶片相關的一些私有資訊。
下面是probe函式的實現:
1./*
2. * Search DM9000 board, allocate space and register it
3. */
4.static int __devinit
2.dm9000_probe(struct platform_device *pdev) 3.{
4. /*獲得平臺數據,這個應該在platform_device那邊指定了*/ 5. struct dm9000_plat_data *pdata = pdev->dev.platform_data; 6. struct board_info *db; /* Point a board information structure */ 7. struct net_device *ndev; 8. const unsigned char *mac_src; 9.
int ret = 0; 10. int iosize; 11. int i; 12. u32 id_val;
13.
14. /*分配乙太網的網路裝置*/ 15. ndev = alloc_etherdev(sizeof(struct board_info)); 16. if (!ndev) { 17. dev_err(&pdev->dev, "could not allocate device.\n"); 18. return -ENOMEM; 19. }
20. /*#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))*/ 21. SET_NETDEV_DEV(ndev, &pdev->dev);
22.
23. dev_dbg(&pdev->dev, "dm9000_probe()\n"); 24.
25. /*設定struct board_info為ndev的私有資料*/ 26. db = netdev_priv(ndev);
27. memset(db, 0, sizeof(*db)); 28.
29. db->dev = &pdev->dev;
30. db->ndev = ndev;
31.
32. spin_lock_init(&db->lock); 33. mutex_init(&db->addr_lock);
34. /*提交一個任務給一個工作佇列,你需要填充一個work_struct結構db->phy_poll*/ 35. INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
36. /*獲取IO記憶體和中斷資源*/ 37. db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
38. db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
39. db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
40.
41. if (db->addr_res == NULL || db->data_res == NULL || 42. db->irq_res == NULL) {
43. dev_err(db->dev, "insufficient resources\n"); 44. ret = -ENOENT;
45. goto out; 46. }
47. /*對映到核心,並獲得IO記憶體的虛擬地址,ioremap完成頁表的建立,不同於vmalloc,但是,它實際上不分配記憶體*/ 48. iosize = res_size(db->addr_res);
49. db->addr_req = request_mem_region(db->addr_res->start, iosize,
50. pdev->name);
51.
52. if (db->addr_req == NULL) { 53. dev_err(db->dev, "cannot claim address reg area\n"); 54. ret = -EIO;
55. goto out; 56. }
57.
58. db->io_addr = ioremap(db->addr_res->start, iosize);
59.
60. if (db->io_addr == NULL) { 61. dev_err(db->dev, "failed to ioremap address reg\n"); 62. ret = -EINVAL;
63. goto out; 64. }
65.
66. iosize = res_size(db->data_res);
67. db->data_req = request_mem_region(db->data_res->start, iosize,
68. pdev->name);
69.
70. if (db->data_req == NULL) { 71. dev_err(db->dev, "cannot claim data reg area\n"); 72. ret = -EIO;
73. goto out; 74. }
75.
76. db->io_data = ioremap(db->data_res->start, iosize);
77.
78. if (db->io_data == NULL) { 79. dev_err(db->dev, "failed to ioremap data reg\n"); 80. ret = -EINVAL;
81. goto out; 82. }
83.
84. /*獲得網路裝置的基地址*/ 85. ndev->base_addr = (unsigned long)db->io_addr; 86. /*獲得網路裝置的中斷號*/ 87. ndev->irq = db->irq_res->start;
88.
89. /*設定預設的IO函式*/ 90. dm9000_set_io(db, iosize);
91.
92. /*如果平臺數據不為空*/ 93. if (pdata != NULL) { 94. /* check to see if the driver wants to over-ride the 95. * default IO width */
96.
97. if (pdata->flags & DM9000_PLATF_8BITONLY) 98. dm9000_set_io(db, 1);
99.
100. if (pdata->flags & DM9000_PLATF_16BITONLY) 101. dm9000_set_io(db, 2);
102.
103. if (pdata->flags & DM9000_PLATF_32BITONLY) 104. dm9000_set_io(db, 4);
105.
106. /* check to see if there are any IO routine 107. * over-rides */
108.
109. if (pdata->inblk != NULL) 110. db->inblk = pdata->inblk;
111.
112. if (pdata->outblk != NULL) 113. db->outblk = pdata->outblk;
114.
115. if (pdata->dumpblk != NULL) 116. db->dumpblk = pdata->dumpblk;
117.
118. db->flags = pdata->flags;
119. } 120.
121.#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL
122. db->flags |= DM9000_PLATF_SIMPLE_PHY; 123.#endif
124. /*dm9000復位*/ 125. dm9000_reset(db);
126. /*讀取Vendor ID Register,Product ID Register中的值,與0x90000A46比較,如果相等,則說明是DM9000*/ 127. /* try multiple times, DM9000 sometimes gets the read wrong */ 128. for (i = 0; i < 8; i++) { 129. id_val = ior(db, DM9000_VIDL);
130. id_val |= (u32)ior(db, DM9000_VIDH) << 8;
131. id_val |= (u32)ior(db, DM9000_PIDL) << 16;
132. id_val |= (u32)ior(db, DM9000_PIDH) << 24;
133.
134. if (id_val == DM9000_ID) 135. break; 136. dev_err(db->dev, "read wrong id 0x%08x\n", id_val); 137. }
138.
139. if (id_val != DM9000_ID) { 140. dev_err(db->dev, "wrong id: 0x%08x\n", id_val); 141. ret = -ENODEV;
142. goto out; 143. }
144.
145. /* Identify what type of DM9000 we are working on */ 146. /*讀取Chip Revision Register中的值*/ 147. id_val = ior(db, DM9000_CHIPR);
148. dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val); 149.
150. switch (id_val) { 151. case CHIPR_DM9000A: 152. db->type = TYPE_DM9000A;
153. break; 154. case CHIPR_DM9000B: 155. db->type = TYPE_DM9000B;
156. break; 157. default: 158. dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val); 159. db->type = TYPE_DM9000E;
160. }
161.
162. /* from this point we assume that we have found a DM9000 */ 163.
164. /* driver system function */ 165. /*設定部分net_device欄位*/ 166. ether_setup(ndev);
167.
168. ndev->open = &dm9000_open;
169. ndev->hard_start_xmit = &dm9000_start_xmit;
170. ndev->tx_timeout = &dm9000_timeout;
171. ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
172. ndev->stop = &dm9000_stop;
173. ndev->set_multicast_list = &dm9000_hash_table;
174. /*對ethtool支援的相關宣告可在<linux/ethtool.h>中找到。它的核心是一個ethtool_ops型別的結構,裡邊包含一個全部的24個不同的方法來支援ethtool*/ 175. ndev->ethtool_ops = &dm9000_ethtool_ops;
176. ndev->do_ioctl = &dm9000_ioctl; 177.
178.#ifdef CONFIG_NET_POLL_CONTROLLER
179. ndev->poll_controller = &dm9000_poll_controller; 180.#endif
181.
182. db->msg_enable = NETIF_MSG_LINK;
183. db->mii.phy_id_mask = 0x1f;
184. db->mii.reg_num_mask = 0x1f;
185. db->mii.force_media = 0;
186. db->mii.full_duplex = 0;
187. db->mii.dev = ndev;
188. db->mii.mdio_read = dm9000_phy_read;
189. db->mii.mdio_write = dm9000_phy_write;
190. /*MAC地址的源是eeprom*/ 191. mac_src = "eeprom"; 192.
193. /* try reading the node address from the attached EEPROM */ 194. for (i = 0; i < 6; i += 2) 195. dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);
196. /*如果從eeprom中讀取的地址無效,並且私有資料不為空,從platform_device的私有資料中獲取dev_addr*/ 197. if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) { 198. mac_src = "platform data"; 199. memcpy(ndev->dev_addr, pdata->dev_addr, 6);
200. }
201. /*如果地址依然無效,從PAR:實體地址(MAC)暫存器(Physical Address Register)中讀取*/ 202. if (!is_valid_ether_addr(ndev->dev_addr)) { 203. /* try reading from mac */ 204.
205. mac_src = "chip"; 206. for (i = 0; i < 6; i++) 207. ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
208. }
209. /*檢視乙太網網絡卡裝置地址是否有效*/ 210. if (!is_valid_ether_addr(ndev->dev_addr)) 211. dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please " 212. "set using ifconfig\n", ndev->name); 213. /*將ndev儲存到pdev->dev->driver_data中*/
214. platform_set_drvdata(pdev, ndev);
215. /*一切都初始化好後,註冊網路裝置*/ 216. ret = register_netdev(ndev);
217.
218. if (ret == 0) 219. printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n", 220. ndev->name, dm9000_type_to_char(db->type),
221. db->io_addr, db->io_data, ndev->irq,
222. ndev->dev_addr, mac_src);
223. return 0; 224.
225.out: 226. dev_err(db->dev, "not found (%d).\n", ret); 227.
228. dm9000_release_board(pdev, db);
229. free_netdev(ndev);
230.
231. return ret; 232.}
下邊看看掛起和喚醒函式:
掛起函式完成了設定掛起標誌,並沒有真正把裝置移除而只是設定了移除標誌,復位PHY,停止PHY,禁止所有中斷,禁止接受引腳。
1.static int
2.dm9000_drv_suspend(struct platform_device *dev, pm_message_t state)
2.{
3. struct net_device *ndev = platform_get_drvdata(dev); 4. board_info_t *db;
5.
6. if (ndev) { 7. db = netdev_priv(ndev);
8. db->in_suspend = 1;
9.
10. if (netif_running(ndev)) { 11. netif_device_detach(ndev);
12. dm9000_shutdown(ndev);
13. }
14. }
15. return 0; 16.}
喚醒函式完成了復位dm9000,初始化dm9000,標記裝置為attached,清除掛起標誌。
1.static int
2.dm9000_drv_resume(struct platform_device *dev)
2.{
3. struct net_device *ndev = platform_get_drvdata(dev); 4. board_info_t *db = netdev_priv(ndev);
5.
6. if (ndev) { 7.
8. if (netif_running(ndev)) { 9. dm9000_reset(db);
10. dm9000_init_dm9000(ndev);
11. netif_device_attach(ndev);
12. }
13. db->in_suspend = 0;
14. }
15. return 0; 16.}
網路裝置的開啟與釋放:
首先來看這個open函式:
1.static int
2.dm9000_open(struct net_device *dev)
2.{
3. board_info_t *db = netdev_priv(dev);
4. unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;
2.
3. if (netif_msg_ifup(db)) 4. dev_dbg(db->dev, "enabling %s\n", dev->name); 5.
6. /* If there is no IRQ type specified, default to something that 7. * may work, and tell the user that this is a problem */
8.
9. if (irqflags == IRQF_TRIGGER_NONE) 10. dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n"); 11.
12. irqflags |= IRQF_SHARED;
13. /*註冊中斷處理函式*/ 14. if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev)) 15. return -EAGAIN; 16.
17. /* Initialize DM9000 board */ 18. /*復位DM9000*/ 19. dm9000_reset(db);
20. /*初始化DM9000的暫存器*/ 21. dm9000_init_dm9000(dev);
22.
23. /* Init driver variable */ 24. db->dbug_cnt = 0;
25. /*檢查鏈路載波狀況*/ 26. mii_check_media(&db->mii, netif_msg_link(db), 1);
27. /*啟動傳送佇列*/ 28. netif_start_queue(dev);
29. /*之前在probe函式中呼叫 INIT_DELAYED_WORK初始化了工作佇列,並關聯了一個操作函式dm9000_poll_work(),此時執行dm9000_schedule_poll來呼叫這個函式*/ 30. dm9000_schedule_poll(db);
31.
32. return 0; 33.}
然後是stop函式:
1.static int
2.dm9000_stop(struct net_device *ndev)
2.{
3. board_info_t *db = netdev_priv(ndev);
4.
5. if (netif_msg_ifdown(db))
2. dev_dbg(db->dev, "shutting down %s\n", ndev->name);
2. /*殺死延時工作佇列phy_poll*/
2. cancel_delayed_work_sync(&db->phy_poll);
3. /*停止傳送佇列*/ 4. netif_stop_queue(ndev);
5. /*通知核心鏈路失去連線*/ 6. netif_carrier_off(ndev);
7. /* free interrupt */ 8. free_irq(ndev->irq, ndev);
9. /*關閉DM9000*/ 10. dm9000_shutdown(ndev);
11. return 0; 12.}
復位PHY,停止PHY,禁止所有中斷,禁止接收引腳。
1.static void
2.dm9000_shutdown(struct net_device *dev)
2.{
3. board_info_t *db = netdev_priv(dev);
4.
5. /* RESET device */
2. dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */
2. iow(db, DM9000_GPR, 0x01); /* Power-Down PHY */
2. iow(db, DM9000_IMR, IMR_PAR); /* Disable all interrupt */ 3. iow(db, DM9000_RCR, 0x00); /* Disable RX */ 4.}
相關推薦
Linux驅動修煉之道-DM9000A網絡卡驅動框架原始碼分析
網路裝置的初始化: 通過模組的載入函式看出DM9000A的驅動是以平臺驅動的形式註冊進核心的,下邊是模組的載入函式: 1.static int __init 2.dm9000_init(void) 2.{ 3. printk(KERN_INFO "%
tiny4412學習(三)之移植linux-4.x驅動(1)支援網絡卡驅動
一、思路 上一節我們通過DNW將核心、檔案系統、裝置樹檔案燒入到記憶體中,並使用bootm啟動核心:bootm0x40600000 0x41000000 0x42000000。因為此時核心並沒有S
嵌入式Linux——網絡卡驅動(1):網絡卡驅動框架介紹
宣告:文字是看完韋東山老師的視訊和看了一些文章後,所寫的總結。我會盡力將自己所瞭解的知識寫出來,但由於自己感覺並沒有學的很好,所以文中可能有錯的地方敬請指出,謝謝。 在介紹本文之前,我想先對前面的知識做一下總結,我們知道Linux系統的裝置分為字元裝置(ch
基於S3C2440的Linux-3.6.6移植——DM9000網絡卡驅動移植
Linux-3.6.6很好的支援了DM9000,因此對於S3C2440晶片來說無需進行任何修改,甚至連menuconfig都已經預設配置了網絡卡驅動。但我們還需要設定網絡卡的MAC和IP等資訊。有許多方法可以實現網絡卡的設定,在這裡我們選擇一種比較簡單的方法——修改根檔案系
Linux 上安裝Realtek瑞昱網絡卡驅動
新買了個筆記本,安裝了Ubuntu 16.04,發現筆記本的Realtek瑞昱無線網絡卡不能自動被linux識別。上網查詢原因為驅動問題,也有很多人抱怨過Realtek瑞昱網絡卡在linux上驅動難弄。 這裡記錄下解決過程,給其他人一點方便。 其實安裝方法還算
Linux 下小米WIFI 的無線網絡卡驅動
在小米的罈子裡看到了大神發的 小米WIFI 驅動 for Linux。於是就下載下來為自己的linux(Fedora 21 , kernel:3.17.8-300 )安裝小米WIFI 驅動。 過程記錄如下(其實也適用於 小度WIFI 和 360 WIFI ,只要用的是晶
初識Linux 驅動移植 之 dm9621網絡卡驅動移植
概述 將kernel移植到開發板並能正常載入和啟動核心後,發現網絡卡並沒有工作,因此將網絡卡作為第一個移植的實踐。這篇文章用於記錄移植dm9621網絡卡過程中遇到的問題以及如何定位問題並嘗試解決。 配置核心 在找到dm9621網絡卡驅動的原始碼後,需要將其新增
Linux驅動修煉之道-SPI驅動框架原始碼分析(中-續)
然後看這裡是怎樣註冊spi主機控制器驅動的: int spi_register_master(struct spi_master *master) { 。。。。。。。。。。。。。。。。 /*將spi新增到核心,這
Linux驅動修煉之道-SPI驅動框架原始碼分析(中)
來自:http://blog.csdn.net/woshixingaaa/article/details/6574220 這篇來分析spi子系統的建立過程。 嵌入式微處理器訪問SPI裝置有兩種方式:使用GPIO模擬SPI介面的工作時序或者使用SPI控制
linux驅動之網絡卡驅動程式框架
我們這裡說的是網絡卡驅動程式,不是網路驅動程式,網路有七層,我們寫的只是最底層的東西,網路這麼多層,但是最終你還是要操作硬體啊 所以上面肯定有個硬體相關層,我們要寫的就是硬體相關的驅動程式這一小塊。 網絡卡你不需要開啟什麼裝置,你只需要socket程式設計就行了 怎
Linux驅動修煉之道-SPI驅動框架原始碼分析(上)
SPI驅動架構,以前用過,不過沒這個詳細,跟各位一起分享: 來自:http://blog.csdn.net/woshixingaaa/article/details/6574215 SPI協議是一種同步的序列資料連線標準,由摩托羅拉公司命名,可工作於全雙工模式。相
linux驅動之網絡卡驅動-虛擬網絡卡驅動編寫
我們來實現這麼一個目的 我ping 3.3.3.4的時候,按理說如果是真實網絡卡的話,我們3.3.3.3的機器,和3.3.3.4的機器。ping 3.3.3.4的時候3.3.3.3的機器會把包發給3.3.3.4,3.3.3.4收到包之後又會把包發給3.3.3.3 在驅
Linux驅動修煉之道-SPI驅動框架原始碼分析(下-續)
spi_async在spi.h中定義的: <span style="font-size:18px;">staticinlineint spi_async(struct spi_device *spi, struct spi_mess
Linux驅動修煉之道-DMA框架原始碼分析(下)
static irqreturn_t s3c2410_dma_irq(int irq, void *devpw) { struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; struct s3c2410_dma_buf *buf
Linux驅動修煉之道
一些學習Linux驅動的筆記整理在這裡與大家分享,如果那裡有錯誤也請高手指出。若干年後能進入INTEL開源中心或IBM搞linux kernel是我目前的目標。君子藏器於身,待時而動。文章連載,不斷更新中。
linux驅動修煉之道-混雜裝置
Linux驅動中把無法歸類的五花八門的裝置定義為混雜裝置(用miscdevice結構體表述)。miscdevice共享一個主裝置號MISC_MAJOR(即10),但次裝置號不同。 所有的miscdevice裝置形成了一個連結串列,對裝置訪問時核心根據次裝置號查詢對應的mi
linux網絡卡驅動分析之probe函式
從上面結果可以看出,該裝置使用了6個BAR中的2個BAR,即BAR0和BAR1,該裝置申請了兩塊IO記憶體,BAR0的範圍為:fea00000-fea1ffff,大小為128KB,用來對映裝置暫存器,BAR1的範圍為fea20000-fea23fff,大小為32KB,用來對映flash。裝置需要
Linux驅動修煉之道-DMA框架原始碼分析
DMA使用3個狀態的有限狀態機: 1.初始狀態,DMA等待DMA請求,一旦請求到達DMA進入狀態2,DMA ACK與INT REQ為0。 2.在這個狀態,DMA ACK置為1並且計數器CURR_TC的值被從DCON[19:0]載入,注意DMA ACK保持為1直到它被清除。 3.在這個狀態,處理DMA原子
l(轉)Linux DM9000網絡卡驅動程式完全分析
[置頂] Linux DM9000網絡卡驅動程式完全分析 分類: Linux裝置驅動程式第三版學習筆記 2011-02-26 16:11 3513人閱讀 評論(34) 收藏 舉報 說明1:本文分析基於核心原始碼版本為linux-2
LINUX核心升級 - 更新網絡卡驅動
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!