分类:
2010-11-12 14:25:07
1.4 net_olddevs_init函数
我们知道net_olddevs_init函数在do_initcalls函数中被调用并执行,那么它到底要做什么呢?看看实现代码就知道了,它的实现代码可以在
static int __init net_olddevs_init(void){
……
int num;
for (num = 0; num < 8; num)
ethif_probe2(num);
……
}
这段代码就不用讲解了吧,嘿嘿!就是调用了8次ethif_probe2,赶快去看看ethif_probe2长什么样子。
1.5 ethif_probe2函数
先看看该函数的实现代码,该代码也在
static void __init ethif_probe2(int unit)
{
unsigned long base_addr = netdev_boot_base("eth", unit); // 由于ethif_probe2被net_olddevs_init调用了8次,
// 所以unit的值为0~7,也即在这里可以注册eth0~eth7八个网络设备
if (base_addr == 1)
return;
(void)( probe_list2(unit, m68k_probes, base_addr == 0) &&
probe_list2(unit, eisa_probes, base_addr == 0) &&
probe_list2(unit, mca_probes, base_addr == 0) &&
probe_list2(unit, isa_probes, base_addr == 0) &&
probe_list2(unit, parport_probes, base_addr == 0));
}
该函数首先调用netdev_boot_base所给的设备是否已经向内核注册,如果已注册netdev_boot_base返回1,随后推出ethif_probe2。如果设备没注册,则又调用函数probe_list2四次,每次传递的传输不同,注意到每次传递的第二个参数不同,这个参数也是相当重要的,这里拿isa_probes参数为例说明,因为这个参数与cs89x0_probe有关,isa_probes的定义也在
static struct devprobe2 isa_probes[] __initdata = {
……
#ifdef CONFIG_SEEQ8005
{seeq8005_probe, 0},
#endif
#ifdef CONFIG_CS89x0
{cs89x0_probe, 0},
#endif
#ifdef CONFIG_AT1700
{at1700_probe, 0},
#endif
{NULL, 0},
……
};
如果把cs8900的驱动选为非编译进内核,那么它的探测函数cs89x0_probe就不会存在于isa_probes数组中,所以在初始阶段就不能被调用。从上面的代码可以知道devprobe2类型至少包括两个域,至少一个域为函数指针,看看它的原型如下:
struct devprobe2 {
struct net_device *(*probe)(int unit); //函数指针,指向探测函数
int status; /* non-zero if autoprobe has failed */
};
下面看看probe_list2函数是怎么表演的。
1.6 ethif_probe2函数
对于ethif_probe2函数也没有什么需要说明的,它的主要任务是依次调用devprobe2类型的probe域指向的函数。他的实现代码同样在
static int __init probe_list2(int unit, struct devprobe2 *p, int autoprobe)
{
struct net_device *dev;
for (; p->probe; p ) {
……
dev = p->probe(unit);
……
}
……
}
1.7 cs89x0_probe函数
从该函数起,真正开始执行与cs8900驱动初始化程序,该函数在
struct net_device * __init cs89x0_probe(int unit)
{
struct net_device *dev = alloc_etherdev(sizeof(struct net_local)); //该函数申请一个net_device+
//sizeof(struct net_local)的空间,net_local是cs8900驱动的私有数据空间。
unsigned *port;
int err = 0;
int irq;
int io;
if (!dev)
return ERR_PTR(-ENODEV);
sprintf(dev->name, "eth%d", unit); //初始化dev->name域
netdev_boot_setup_check(dev); //检查是否给定了启动参数,如果给定了启动参数,此函数将初始
//化dev的irq、base_addr、mem_start和mem_end域。
io = dev->base_addr; //io实际实质cs8900所占地址空间的起始地址,此地址为虚拟地址
irq = dev->irq;
if (net_debug)
printk("cs89x0:cs89x0_probe(0x%x)\n", io);
//下面根据io的值调用cs89x0_probe1函数
if (io > 0x1ff) {/* Check a single specified location. *///此段没搞懂,由于没给启动参数,这里也不会执行
err = cs89x0_probe1(dev, io, 0);
} else if (io != 0) { /* Don''''''''''''''''''''''''''''''''t probe at all. */
err = -ENXIO;
} else {
for (port = netcard_portlist; *port; port ) {// netcard_portlist为unsigned int型数组,在cs89x0.c文件中定
//义,里面列出了cs8900可能占用空间的起始地址,这些地址
//将在cs89x0_probe1函数中用于向内核申请。
if (cs89x0_probe1(dev, *port, 0) == 0) // cs89x0_probe1探测成功就返回0
break;
dev->irq = irq;
}
if (!*port)
err = -ENODEV;
}
if (err)
goto out;
return dev;
out:
free_netdev(dev); //表示探测失败,这里就释放dev的空间,随后打印些消息
printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n");
return ERR_PTR(err);
}
从上面的程序清单可以看到该函数还没有真正的开始探测cs8900,实质的探测工作是让cs89x0_probe1完成的。在解释cs89x0_probe1之前先提一下网络驱动程序中非常重要的一些函数。内核需要一个数据结构来管理或者描述每个网络驱动程序,这个数据类型就是struct net_device,该数据类型包括很多域,详细的解释可以参见《Linux 设备驱动程序》一书中的描述,也可以参见源代码(在