一、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);