Linux代码版本:linux3.0
开发板环境: tiny4412
导读:很多书籍介绍linux驱动模型的时候,经常会提到bus、device和driver的概念,对class介绍的不是太多。class,类,从名字上来理解也是将设备进行分类,只是和bus标准不同而已。例如一家公司,员工是一个个device,那么所属部门就是bus,公司要进行员工体检,仅仅从部门(bus)的角度分,并不是很好的办法,这个时候从所有的男员工和所有的女员工角度来管理,似乎更合适,这就是员工属于不同的性别(class),员工还是那些员工,从不同的角度看,员工会属于不同的群组,年龄、性别和岗位等信息只是从不同的角度分类方便进行管理。
而class则是从设备类的角度进行区分,比如i2c设备、input设备、rtc设备和misc等等,sys/class目录下则是对设备的分类
[root@FriendlyARM class]# cd /sys/class/
[root@FriendlyARM class]# ls
android_usb hwmon misc scsi_generic tvout
bdi i2c-adapter mmc_host scsi_host ump
block i2c-dev net sec usb_device
firmware ieee80211 power_supply secgpio_check vc
gpio input ppp sound video4linux
graphics lcd rc spi_master vtconsole
hdcp leds rfkill spidev
hdmi_audio lirc rtc switch
hidraw mali scsi_device thermal
host_notify mem scsi_disk tty
一、class结构体
struct class {
const char *name;
struct module *owner;
struct class_attribute *class_attrs;
struct device_attribute *dev_attrs;
struct bin_attribute *dev_bin_attrs;
struct kobject *dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, mode_t *mode);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct kobj_ns_type_operations *ns_type;
const void *(*namespace)(struct device *dev);
const struct dev_pm_ops *pm;
struct subsys_private *p;
};
const char *name;
class的名称,向bus一样也是要有个名字的
struct module *owner;
class所属的模块
struct class_attribute *class_attrs;
默认的class属性,还记得bus_type的bus_attribute吗
struct device_attribute *dev_attrs:
class下设备的属性,注册设备到内核时会在相应的目录下建立属性文件
struct bin_attribute *dev_bin_attrs;
class下设备的属性,只不过是二进制的形式,注册设备到内核时会在相应的目录下建立属性文件
struct kobject *dev_kobj;
用来确定该class设备在/sys/dev下创建链接的路径,或者是在 /sys/dev/char或者是在/sys/dev/block下,如果没有赋值
则默认创建在/sys/dev/char下
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
在注册device发出event时调用,其实就是添加部分环境变量
char *(*devnode)(struct device *dev, mode_t *mode);
返回device节点的相对路径,等代码中见到再详细分析
void (*class_release)(struct class *class);
class释放的时候调用
void (*dev_release)(struct device *dev);
device释放的时候调用
int (*suspend)(struct device *dev, pm_message_t state);
device休眠的时候调用
int (*resume)(struct device *dev);
device恢复的时候调用
const struct kobj_ns_type_operations *ns_type;
暂时不清楚用途,代码中遇到再详细分析
const void *(*namespace)(struct device *dev);
暂时不清楚用途,代码中遇到再详细分析
const struct dev_pm_ops *pm;
电源管理的操作集合,等分析电源管理的时候再详细分析其成员
struct subsys_private *p;
还记得bus_type的私有结构体吗,对,就是这个,class也包含了一个这样的结构体
二、代码分析
sys/class下有多种不同的设备类
[root@FriendlyARM class]# cd /sys/class/
[root@FriendlyARM class]# ls
android_usb hwmon misc scsi_generic tvout
bdi i2c-adapter mmc_host scsi_host ump
block i2c-dev net sec usb_device
firmware ieee80211 power_supply secgpio_check vc
gpio input ppp sound video4linux
graphics lcd rc spi_master vtconsole
hdcp leds rfkill spidev
hdmi_audio lirc rtc switch
hidraw mali scsi_device thermal
host_notify mem scsi_disk tty
每个目录都是在代码中找到创建过程,下面是几个例子
static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc");
.......
}
static int __init i2c_dev_init(void)
{
......
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
......
}
static int __init fbmem_init(void)
{
......
fb_class = class_create(THIS_MODULE, "graphics");
......
}
都是调用的class_creat函数创建,过程也都基本一样,下面分析i2c_dev_class的创建
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
{
static struct lock_class_key __key;
__class_create(owner, name, &__key);
{
struct class *cls;
int retval;
cls = kzalloc(sizeof(*cls), GFP_KERNEL);
if (!cls) {
retval = -ENOMEM;
goto error;
}
/*赋值 i2c_dev_class->name = "i2c-dev"*/
cls->name = name;
/*赋值i2c_dev_class->owner = THIS_MODULE*/
cls->owner = owner;
cls->class_release = class_create_release;
retval = __class_register(cls, key);
{
struct subsys_private *cp;
int error;
pr_debug("device class '%s': registering\n", cls->name);
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp)
return -ENOMEM;
/*初始化 i2c_dev_class->klist_devices
i2c_dev_class->klist_devices->get = klist_class_dev_get
i2c_dev_class->klist_devices->put = klist_class_dev_put*/
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
{
INIT_LIST_HEAD(&k->k_list);
spin_lock_init(&k->k_lock);
k->get = get;
k->put = put;
}
INIT_LIST_HEAD(&cp->class_interfaces);
kset_init(&cp->glue_dirs);
__mutex_init(&cp->class_mutex, "struct class mutex", key);
/*赋值 i2c_dev_class->subsys.kobj->name = "i2c-dev" */
error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
if (error) {
kfree(cp);
return error;
}
/* set the default /sys/dev directory for devices of this class */
/*设置这种class在/sys/dev下创建链接文件的路径,还记得/sys/dev下char和blok目录吗
sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);*/
if (!cls->dev_kobj)
cls->dev_kobj = sysfs_dev_char_kobj;
/*sysfs_deprecated由编译选项CONFIG_SYSFS_DEPRECATED决定,如果没有配置此选项,
sysfs_deprecated = 0,这时 block_class ->subsys.kobj.kset = NULL
就意味着在sys下创建block目录,这样就会觉得怪怪的,其它的class都创建在sys/class下
为什么就 block_class 可以放在sys目录下呢,2.6.22 开始就已标记为过时,为了保持向前的兼容所以才加了个
所以才加了个sysfs_deprecated(弃用)的配置项 */
#if defined(CONFIG_BLOCK)
/* let the block class directory show up in the root of sysfs */
if (!sysfs_deprecated || cls != &block_class)
cp->subsys.kobj.kset = class_kset;
#else
/*赋值i2c_dev_class->p->subsys.kobj.kset = class_kset
还记得sys下class目录的创建吗
class_kset = kset_create_and_add("class", NULL, NULL);*/
cp->subsys.kobj.kset = class_kset;
#endif
/*赋值 i2c_dev_class->p->subsys.kobj.ktype = &class_ktype*/
cp->subsys.kobj.ktype = &class_ktype;
/*i2c_dev_class->p->class = i2c_dev_class,到此有没有觉得跟bus注册如出一辙? */
cp->class = cls;
cls->p = cp;
/*为i2c_dev_class创建目录,由于i2c_dev_class->p->subsys.kobj.parent = NULL
i2c_dev_class->p->subsys.kobj.kset = class_kset,所以将"i2c-dev"创建在了sys/class下*/
error = kset_register(&cp->subsys);
if (error) {
kfree(cp);
return error;
}
/*为i2c_dev_class在sys/class/i2c-dev下创建属性文件
i2c_dev_class->class_attrs未赋值,也就是为NULL,所以不会 创建属性文件*/
error = add_class_attrs(class_get(cls));
{
int i;
int error = 0;
/*i2c_dev_class->class_attrs未赋值,也就是为NULL,所以不会 创建属性文件 */
if (cls->class_attrs) {
for (i = 0; attr_name(cls->class_attrs[i]); i++) {
error = class_create_file(cls, &cls->class_attrs[i]);
if (error)
goto error;
}
}
done:
return error;
error:
while (--i >= 0)
class_remove_file(cls, &cls->class_attrs[i]);
goto done;
}
class_put(cls);
return error;
}
if (retval)
goto error;
return cls;
error:
kfree(cls);
return ERR_PTR(retval);
}
}
上面代码就完成了/sys/class的i2c-dev目录的创建,并且对外输出 i2c_dev_class。在 device_create 中将会用到i2c_dev_class。
下面将进行分析device和driver的注册过程。