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

【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现的更多相关文章

  1. 【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析

    [linux驱动分析]之dm9000驱动分析(一):dm9000原理及硬件分析 [linux驱动分析]之dm9000驱动分析(二):定义在板文件里的资源和设备以及几个宏 [linux驱动分析]之dm9 ...

  2. Mini2440 DM9000 驱动分析(一)

    Mini2440 DM9000 驱动分析(一) 硬件特性 Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系: PW_RST 连接到复位按键,复位按键按下,低电平 ...

  3. Linux驱动开发:USB驱动之usb_skel分析

    在学习了这么些天的驱动之后,个人觉得驱动就是个架构的问题,只要把架构弄清楚了 然后往里面添砖加瓦就可以了,所以似乎看起来不是太困难,但也许是是我经验不足吧,这只能算是个人浅见了 这两天在学习USB驱动 ...

  4. linux i2c驱动架构-dm368 i2c驱动分析

      linux i2c驱动架构-dm368 i2c驱动分析   在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinc ...

  5. Linux内核中SPI总线驱动分析

    本文主要有两个大的模块:一个是SPI总线驱动的分析 (研究了具体实现的过程): 另一个是SPI总线驱动的编写(不用研究具体的实现过程). 1 SPI概述 SPI是英语Serial Peripheral ...

  6. linux driver ------ platform模型,驱动开发分析

    一.platform总线.设备与驱动 在Linux 2.6 的设备驱动模型中,关心总线.设备和驱动3个实体,总线将设备和驱动绑定.在系统每注册一个设备的时候,会寻找与之匹配的驱动:相反的,在系统每注册 ...

  7. Linux 串口、usb转串口驱动分析(2-2) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...

  8. Linux 串口、usb转串口驱动分析(2-1) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...

  9. linux设备驱动之USB主机控制器驱动分析 【转】

    转自:http://blog.chinaunix.net/uid-20543183-id-1930831.html   ---------------------------------------- ...

随机推荐

  1. 项目优化经验分享(六)SVN冲突和处理

    上一篇博客我们分享了新增需求的确定思想<站在全局看问题>.今天我们来分享项目开发中SVN冲突的解决经验:SVN冲突和处理! 引言 开发过项目的人都知道,公司开发一个项目都会使用到版本号控制 ...

  2. 使用css3写一朵云

  3. perl学习(8) 控制:unless,until,next,redo,last

    Perl中实现了所有C 的操作符! Perl力求代码最少! 1.1.unless unless的含义是:除非条件为真,否则执行块中的代码,和if正好相反 unless($fred=~ /^[A-Z_] ...

  4. 【数位DP】 HDU 4722 Good Numbers

    原题直通车: HDU  4722  Good Numbers 题意: 求区间[a,b]中各位数和mod 10==0的个数. 代码: #include<iostream> #include& ...

  5. 纯css实现苹果表盘动画

    欢迎訪问我们的博客:http://www.w3ctrain.com/2015/07/06/Apple-Watch-Dials/ 随着苹果表的大量生产,我想.用CSS来实现拨号动画的时候到了. 在这篇文 ...

  6. 【视频】零基础学Android开发:蓝牙聊天室APP(四)

    零基础学Android开发:蓝牙聊天室APP第四讲 4.1 ListView控件的使用 4.2 BaseAdapter具体解释 4.3 ListView分布与滚动事件 4.4 ListView事件监听 ...

  7. Python 中的类的相关操作

    构造函数 构造函数是任何类都有的特殊方法.当要创建一个类时,就要调用构造函数.他的名字是__init__.init的前后分别是两个下划线.时间类Time的构造函数如下: >>> cl ...

  8. SQL SERVER 2008- 字符串函数

    /* 1,ASCII返回字符表达式中最左侧字符的ASCII代码值 仅返回首字母的ASCII码值 parameter char或varchar returns integer */ SELECT ASC ...

  9. Eclipse用法和技巧七:自动生成get和set方法2

    上一篇文章中我们介绍了自动批量生成get和set函数的方法.这个方法一般在声明完类的数据域之后使用,比较方便快捷.这里再补充几个自动生成get和set函数的方法. 步骤一:在声明的数据域中按Ctrl+ ...

  10. 基于visual Studio2013解决C语言竞赛题之1079狼羊过河

        题目 解决代码及点评 /************************************************************************/ /* ...