关于DM900网卡驱动的分析

本文详细分析了DM9000网卡驱动,涉及sysfs、kobject、kset、ktype等概念,以及平台总线、驱动和设备的注册过程。重点讨论了平台总线的创建,包括bus目录、platform目录的创建与注册。此外,还介绍了DM9000驱动的实例分析,涵盖驱动入口、驱动注册、设备注册、prob函数的细节,如board_info的实例化、硬件初始化、操作函数的实现。最后探讨了net类的注册,完成对同类网络设备的管理。
摘要由CSDN通过智能技术生成

写在前面:最近时间较为充裕,所以对一直很想分析的dm9000的网卡驱动做了一次深入的分析,并总结如下。博文内容大致总结如下:

1.sysfs的对于内核管理方式;

2.kobject、kset、kobject三者的概念;

3.从bus总线的创建开始,从sys的角度看 platform平台总线的创建过程,以及一些内核提供的注册函数的分析;

4.以2.6.32.7版本内核中的dm9000网卡为例,具体的分析网卡驱动。

 

在正式开始分析网卡的实际驱动之前,对于一些基本但是很重要的概念需要做一些解释。 首先要说的就是dm9000使用的是platform平台总线来进行管理的,所以在正式分析之前下进行platform平台总线的分析:

 

1.What is sysfs?

个人理解:sysfs=sys+fs,即一种文件的管理方式,类似于rootfs(根文件系统提供/根目录),众所周知内核负责管理设备和对应的驱动,而这个信息对用户时不可见的,而通过/sys文件夹,操作系统向用户提供的一个查看内核信息的一个窗口,用户可以直观的了解到设备驱动的层次结构。(下图是常见/sys目录的文件情况)

其中:block:代表块设备文件; bus:系统中的各种总线,如I2c;  class:系统中的设备类型,如杂散类设备misc;  dev:系统中已经注册的设备节点的情况,包含char和block目录(linux中的设备文件不是char字符型就是bloak块设备型);  devices:系统中所有设备的一个拓扑结构视图;  fireware:系统中的固件;  fs:文件系统;   kernel:内核配置状态和状态信息;  module:模块;  power:系统的电源管理数据。

 

2.kobjetc、kset、ktype

说到sysfs就不得不说说kobjetc、kset、ktype。

kobject:是一个对象的抽象,用户管理对象,在sys目录中对应一个目录;

kset:一些kobject的集合,其中的kobject可以是同一个ktype的,也可以不同。同时kset自己也包含一个kobjct,在sysfs中kset也代表着自己包含的这个目录,但是在目录下包含着其他的kobject;

ktype:每一个描述kobjetc对象的结构体内都有这样一个结构体变量,用来定义kobject在创建和删除时采取的动作。(结构体内用于作kobject的引用计数的变量为0时,通过release方法来释放相关的资源。)

最下面的kobj都属于一个kset,同时这些kobj的父对象就是kset内嵌的kobj。

以class目录为例:class目录就是sysfs中的一个kset(也就是kset中内嵌的kobject),其中有着其他的类的目录文件夹,同时也可以存放数据文件。其中的每一个类对应的文件相当于一个kobject(例如net目录),其中存放着具体的设备文件(例如net中的eth0)。

 

3.总线、驱动、设备:

3.1总线bus

总线的本质是bus_type结构体类型,各种不同的总线其实就是bus_type的各种实例。它是处理器与一个设备或多个设备进行通讯的通道。在驱动中总线将驱动和设备进行连接

platform作为一种特殊的总线(并无实际的物理意义,而是专用在驱动的管理中的一种总线管理方式),也属于bus的一种,在/sys目录中有着属于platform目录(这时platform就是一个kset,其中的device和driver目录就相当于一个kobject,而其中也有着其他的普通文件),用于管理各种平台总总线架构的设备。

下面以platform平台总线为例,从sysfs的角度分析整个总线的创建过程:

3.1.1、创建bus目录:首先platform平台总线也属于bus总线目录中的一种,所以我么从最基本的bus目录的创建开始进行分析:(drivers/base/bus.c)

int __init buses_init(void)
{
	bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
	if (!bus_kset)
		return -ENOMEM;
	return 0;
}
 

其中最重要的是调用了kset_create_and_add函数,而这个函数又调用了 kset_create函数和kset_register函数完成kset的创建和注册。

struct kset *kset_create_and_add(const char *name,
				 const struct kset_uevent_ops *uevent_ops,
				 struct kobject *parent_kobj)
{
	struct kset *kset;
	int error;

	kset = kset_create(name, uevent_ops, parent_kobj);
	if (!kset)
		return NULL;
	error = kset_register(kset);
	if (error) {
		kfree(kset);
		return NULL;
	}
	return kset;
}
EXPORT_SYMBOL_GPL(kset_create_and_add);

这是内核提供给驱动开发者的一个申请kset的一个函数。(kobject.c文件中)

而后又在头文件中定义了一个所有总线的表述结构体:struct bus_type用来描述每一个属于bus的具体总线(include/linux/device.h)

struct bus_type {
	const char		*name;
	struct bus_attribute	*bus_attrs;
	struct device_attribute	*dev_attrs;
	struct driver_attribute	*drv_attrs;

	int (*match)(struct device *dev, struct device_driver *drv);
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	int (*probe)(struct device *dev);
	int (*remove)(struct device *dev);
	void (*shutdown)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct dev_pm_ops *pm;

	struct bus_type_private *p;
};

我们看到每个bus_type都包含一个kset对象subsys,该kset在/sys/bus/目录下有着对应的一个目录,目录名即为字段name。而platform就属于其中一个。

3.1.2、platform目录的创建,platform平台总线的注册:platform_bus_init函数完成的。该函数在内核启动阶段被调用,我们来简单看下调用过程:(platform.c)

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();

	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);
	if (error)
		device_unregister(&platform_bus);
	return error;
}

bus_register是bus总线给提供的一种注册总线的函数(这部分是由内核开发者自己实现的,而我们大多的情况是使用创建好的platform平台总线架构来管理驱动和设备)以在/sys/bus/下创建不同类的总线这里直接调用bus_register完成platform平台总线的创建:(kobject的注册,注册的名字就是platform)

int bus_register(struct bus_type *bus)
{
	int retval;
	struct bus_type_private *priv;
 
	priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;
	/*互相保存*/
	priv->bus = bus;
	bus->p = priv;
 
	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
	/*设定kobject->name*/
	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
	if (retval)
		goto out;
 
	priv->subsys.kobj.kset = bus_kset;
	priv->subsys.kobj.ktype = &bus_ktype;
	priv->drivers_autoprobe = 1;
 
	/*注册kset,在bus/建立目录XXX,XXX为bus->name*/
	retval = kset_register(&priv->subsys);	
	if (retval)
		goto out;
 
	/*创建属性,在bus/XXX/建立文件uevent*/
	retval =
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值