【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);
上一篇:4:7 Struts实现Ajax


下一篇:jenkin服务关闭和重启