之一:bus_type
总线是处理器和一个或多个设备之间的通道,在设备模型中,一切的设备都经过总线相连,乃至是内部的虚拟”platform”总线。能够经过ls -l /sys/bus看到体系加载的一切总线。
drwxr-xr-x root root 1970-01-01 00:02 platform
drwxr-xr-x root root 1970-01-01 00:02 spi
drwxr-xr-x root root 1970-01-01 00:02 scsi
drwxr-xr-x root root 1970-01-01 00:02 usb
drwxr-xr-x root root 1970-01-01 00:02 serio
drwxr-xr-x root root 1970-01-01 00:02 i2c
drwxr-xr-x root root 1970-01-01 00:02 mmc
drwxr-xr-x root root 1970-01-01 00:02 sdio
drwxr-xr-x root root 1970-01-01 00:02 ac97
总线能够彼此刺进。设备模型展现了总线和它们所操控的设备之间的实践衔接。在Linux 设备模型中,总线由bus_type 结构表明,界说在 :
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 (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
struct pm_ext_ops *pm;
struct bus_type_private *p;
};
1,总线的注册和删去,总线的首要注册过程:
(1)声明和初始化bus_type 结构体。只要很少的bus_type 成员需求初始化,大部分都由设备模型中心操控。但有必要为总线指定姓名及一些必要的办法。例如:
struct bus_type ldd_bus_type = {
.name = “ldd”,
.match = ldd_match,
.uevent = ldd_uevent,
};
(2)调用bus_register函数注册总线。int bus_register(struct bus_type *bus),该调用或许失利,所以有必要一直查看返回值。
ret = bus_register(&ldd_bus_type);
if (ret)
return ret;
若成功,新的总线子体系将被增加进体系,之后能够向总线增加设备。当有必要从体系中删去一个总线时,调用:
void bus_unregister(struct bus_type *bus);
2,总线办法
在 bus_type 结构中界说了许多办法,它们答应总线中心作为设备中心与独自的驱动程序之间供给服务的中介,首要介绍以下两个办法: int (*match)(struct device * dev, struct device_driver * drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
对设备和驱动的迭代:若要编写总线层代码,或许不得不对一切现已注册到总线的设备或驱动进行一些迭代操作,这或许需求细心研讨嵌入到 bus_type 结构中的其他数据结构,但最好运用内核供给的辅佐函数:
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *));
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *));
3,总线特点
简直Linux 设备模型中的每一层都供给增加特点的函数,总线层也不破例。bus_attribute 类型界说在 如下:
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
};
内核供给了一个宏在编译时创立和初始化bus_attribute 结构:
BUS_ATTR(_name,_mode,_show,_store)
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr);
例如创立一个包括源码版本号简略特点办法如下:
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
return snprintf(buf, PAGE_SIZE, “%s”, Version);
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL); //得到bus_attr_version
if (bus_create_file(&ldd_bus_type, &bus_attr_version))
printk(KERN_NOTICE “Unable to create version attribute”);
之二:device
在最底层,Linux 体系中的每个设备由一个struct device 代表:
struct device
{
struct klist klist_children;
struct klist_node knode_parent;
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device *parent; * 设备的 “父” 设备,该设备所属的设备,一般一个父设备是某种总线或许主操控器。假如 parent 是 NULL, 则该设备是顶层设备,较少见 */
struct kobject kobj;
char bus_id[BUS_ID_SIZE];
const char *init_name;
struct device_type *type;
unsigned uevent_suppress:1;
struct semaphore sem;
struct bus_type *bus;
struct device_driver *driver;
void *driver_data;
void *platform_data;
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node;
#endif
u64 *dma_mask;
u64 coherent_dma_mask;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools;
struct dma_coherent_mem *dma_mem;
struct dev_archdata archdata;
spinlock_t devres_lock;
struct list_head devres_head;
struct list_head node;
struct class *class;
dev_t devt;
struct attribute_group **groups;
void (*release)(struct device *dev);
};
(1)设备注册
在注册struct device 前,最少要设置parent, bus_id, bus, 和 release 成员,设备的注册和刊出函数为:
int device_register(struct device *dev);
void device_unregister(struct device *dev);
一个实践的总线也是一个设备,所以有必要独自注册,以下为lddbus注册它的虚拟总线设备:
static void ldd_bus_release(struct device *dev)
{
printk(KERN_DEBUG “lddbus release”);
}
struct device ldd_bus = {
.bus_id = “ldd0”,
.release = ldd_bus_release
};
ret = device_register(&ldd_bus);
if (ret)
printk(KERN_NOTICE “Unable to register ldd0”);
(2)设备特点
sysfs 中的设备进口可有特点,相关的结构是:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
};
DEVICE_ATTR(_name,_mode,_show,_store);
int device_create_file(struct device *device, struct device_attribute * entry);
void device_remove_file(struct device * dev, struct device_attribute * attr);
一个实例是:终端履行:cd /sys/class/leds/lcd-backlight,
ls回显:
uevent
subsystem
device
power
brightness
这些特点或许都是经过device_create_file增加上去(至少brightness是这样)。进入device目录,再输入pwd,
回显:/sys/devices/platform/smdk-backlight。变换到devices目录下了,可见设备模型的不同构成是指向同一个设备的。
2.6下字符设备开端用struct cdev结构体表明,可是我想调用device_create_file(dev, &dev_attr_debug);函数在/sys中导出信息,device_create_file()的第一个进口参数类型为struct device结构体。问题是struct cdev与struct device这两个结构体没有任何联络的当地?答案是能够选用一起具有的Kobjcet这个成员作为枢纽,所以从子类cdev—>父类kobject—>子类device,推导得到:container_of(kobj)–>list_entry(entry)->(struct device*) 。由于containerof是从结构体指针成员找到结构体地址,所以从cdev的kobj能够找到父类kobject的地址,而一切的kobject的entery都是在一个链表里边,遍历这个链表,找到结构体成员为特定device结构的那一项。
(3)设备结构的嵌入
device 结构包括设备模型中心用来模仿体系的信息。但大部分子体系记录了关于它们具有的设备的额定信息,所以很少单纯用device 结构代表设备,而是一般将其嵌入一个设备的高层结构体表明中。
lddbus 驱动创立了它自己的 device 类型(也即每类设备会树立自己的设备结构体,其间至少一个成员是struct device类型,比方video_device),并希望每个设备驱动运用这个类型来注册它们的设备:
struct ldd_device {
char *name;
struct ldd_driver *driver;
struct device dev;
};
#define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);
lddbus 导出的注册和刊出接口如下:
static void ldd_dev_release(struct device *dev)
{ }
int register_ldd_device(struct ldd_device *ldddev)
{
ldddev->dev.bus = &ldd_bus_type; //依靠的总线
ldddev->dev.parent = &ldd_bus; //父设备
ldddev->dev.release = ldd_dev_release;
strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE); //设备名复制入device结构体中
return device_register(&ldddev->dev); //依然用device_register注册,只不过上层打包了
}
EXPORT_SYMBOL(register_ldd_device);
void unregister_ldd_device(struct ldd_device *ldddev)
{
device_unregister(&ldddev->dev);
}
EXPORT_SYMBOL(unregister_ldd_device);
之三:device_driver
设备模型盯梢一切体系已知的驱动,首要意图是使驱动程序中心能和谐驱动和新设备之间的联系。一旦驱动在体系中是已知的目标就或许完结很多的作业。驱动程序的结构体device_driver 界说如下:
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name;
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);
struct attribute_group **groups;
struct pm_ops *pm;
struct driver_private *p;
};
(1)驱动程序的注册和刊出
int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
(2)驱动程序的特点
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *drv, char *buf);
ssize_t (*store)(struct device_driver *drv, const char *buf, size_t count);
};
DRIVER_ATTR(_name,_mode,_show,_store)
*特点文件创立的办法:*/
int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);
(3)驱动程序结构的嵌入
对大多数驱动程序中心结构,device_driver 结构一般被嵌入到一个更高层的、总线相关的结构中。当然也有直接注册驱动的,不必嵌入到高层结构体。如driver_register(&wm97xx_driver)。
以lddbus 子体系为例,它界说了ldd_driver 结构:
struct ldd_driver {
char *version;
struct module *module;
struct device_driver driver;
struct driver_attribute version_attr;
};
#define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);
lddbus总线中相关的驱动注册和刊出函数是:
static ssize_t show_version(struct device_driver *driver, char *buf)
{
struct ldd_driver *ldriver = to_ldd_driver(driver);
sprintf(buf, “%s”, ldriver->version);
return strlen(buf);
}
int register_ldd_driver(struct ldd_driver *driver) //device_driver被嵌入到更高层结构体
{
int ret;
driver->driver.bus = &ldd_bus_type;
ret = driver_register(&driver->driver);
if (ret)
return ret;
driver->version_attr.attr.name = “version”;
driver->version_attr.attr.owner = driver->module;
driver->version_attr.attr.mode = S_IRUGO;
driver->version_attr.show = show_version;
driver->version_attr.store = NULL;
return driver_create_file(&driver->driver, &driver->version_attr);
}
void unregister_ldd_driver(struct ldd_driver *driver)
{
driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(register_ldd_driver);
EXPORT_SYMBOL(unregister_ldd_driver);
在sculld 中创立的 ldd_driver 结构如下:
static struct ldd_driver sculld_driver = {
.version = “$Revision: 1.21 $”,
.module = THIS_MODULE,
.driver = {
.name = “sculld”,
},
};
之四:class_register
类是一个设备的高层视图,它笼统出了底层的完成细节,然后答应用户空间运用设备所供给的功用,而不必关怀设备是怎么衔接和作业的。类成员一般由上层代码所操控,而无需驱动的清晰支撑。但有些状况下驱动也需求直接处理类。
简直一切的类都显现在/sys/class目录中,能够经过ls -l /sys/class来显现。出于前史的原因,有一个破例:块设备显现在/sys/block目录中。在许多状况,类子体系是向用户空间导出信息的最好办法。当类子体系创立一个类时,它将彻底具有这个类,底子不必忧虑哪个模块具有那些特点,并且信息的表明也比较友爱。为了管理类,驱动程序中心导出了一些接口,其意图之一是供给包括设备号的特点以便主动创立设备节点,所以udev的运用离不开类。类函数和结构与设备模型的其他部分遵从相同的形式,可与前三篇文章类比。
(1)管理类的接口和注册刊出函数
类由 struct class 的结构体来界说:
struct class {
const char *name; *每个类需求一个仅有的姓名, 它将显现在 /sys/class 中*/
struct module *owner;
struct class_attribute *class_attrs;
struct device_attribute *dev_attrs;
struct kobject *dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
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);
struct pm_ops *pm;
struct class_private *p;
};
int class_register(struct class *cls);
void class_unregister(struct class *cls);
struct class_attribute {
struct attribute attr;
ssize_t (*show)(struct class *cls, char *buf);
ssize_t (*store)(struct class *cls, const char *buf, size_t count);
};
CLASS_ATTR(_name,_mode,_show,_store);
int class_create_file(struct class *cls, const struct class_attribute *attr);
void class_remove_file(struct class *cls, const struct class_attribute *attr);、
(2)类设备,类存在的真实意图是给作为类成员的各个设备供给一个容器,成员由struct class_device 来表明,暂没用到。
(3)类接口
类子体系有一个 Linux 设备模型的其他部分找不到的附加概念,称为“接口”,可将它理解为一种设备参加或脱离类时取得信息的触发机制,结构体如下:
struct class_interface {
struct list_head node;
struct class *class;
int (*add_dev) (struct device *, struct class_interface *);
void (*remove_dev) (struct device *, struct class_interface *);
};
int class_interface_register(struct class_interface *class_intf);
void class_interface_unregister(struct class_interface *class_intf);
设定class的优点:设备驱动一般在注册的时分都会调用此类class的一些函数,首要效果便是在sys目录里边创立一些节点,比方cd到/sys/class下面能够看到这一类的设备,与这个相关的便是一些kobjects。当然关于一个新设备,能够注册进一个class也能够不注册进去,假如存在对应class的话注册进去更好。