一、概述
总线(bus):负责管理挂载对应总线的设备以及驱动;
设备(device):挂载在某个总线的物理设备;
驱动(driver):与特定设备相关的软件,负责初始化该设备以及提供一些操作该设备的操作方式;
总线-设备-驱动 模式下的操作
总线管理着两个链表:设备链表 和 驱动链表。
当我们向内核注册一个驱动时,便插入到总线的驱动链表。
当我们向内核注册一个设备时,便插入到总线的设备链表。
在插入的同时,总线会执行一个 bus_type 结构体中的 match 方法对新插入的 设备/驱动进行匹配。(例如以名字的方式匹配)
匹配成功后,会调用 驱动 device_driver 结构体中的 probe 方法,通常在 probe中获取设备的资源信息。
在移除设备或驱动时,会调用 device_driver 结构体中的 remove 方法。
二、函数接口
int bus_register(struct bus_type *bus)
void bus_unregister(struct bus_type *bus)
void device_initialize(struct device *dev)
int device_add(struct device *dev)
void device_del(struct device *dev)
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
struct device *get_device(struct device *dev)
void put_device(struct device *dev)
int device_register(struct device *dev)
void device_unregister(struct device *dev)
int driver_register(struct device_driver *drv)
void driver_unregister(struct device_driver *drv)
三、代码示例
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
MODULE_AUTHOR("hpz");
MODULE_LICENSE("Dual BSD/GPL");
#define hp_prt(fmt, arg...) printk("[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg);
static int hp_bus_match(struct device *dev, struct device_driver *drv)
{
return 1;
}
static int hp_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
int i = 0;
hp_prt("enter!\n");
add_uevent_var(env, "hp_TYPE=%s", "happy");
while( i < env->envp_idx){
hp_prt("%s\n",env->envp[i]);
i++;
}
return 0;
}
static int hp_bus_probe(struct device *dev)
{
hp_prt("enter!\n");
return 0;
}
static int hp_bus_remove(struct device *dev)
{
hp_prt("enter!\n");
return 0;
}
static struct bus_type hp_bus_type = {
.name = "hp_bus",
.match = hp_bus_match,
.uevent = hp_bus_uevent,
.probe = hp_bus_probe,
.remove = hp_bus_remove,
// .shutdown = hp_bus_shutdown,
// .pm = &hp_bus_pm_ops,
};
static void hp_release_device(struct device *dev)
{
hp_prt("enter!\n");
kfree(dev);
}
struct attribute test_attr = {
.name = "hp_attr",
.mode = S_IRUGO,
};
static struct attribute *hp_std_attrs[] = {
&test_attr,
NULL,
};
ATTRIBUTE_GROUPS(hp_std);
static struct device_type hp_type = {
.groups = hp_std_groups,
};
static struct device *hpdev;
static struct device *alloc_hp_device(void)
{
struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (!dev)
return NULL;
dev_set_name(dev, "%s", "hp_device");
device_initialize(dev);
dev->parent = NULL;
dev->bus = &hp_bus_type;
dev->release = hp_release_device;
dev->type = &hp_type;
return dev;
}
static int hp_blk_probe(struct device *dev)
{
hp_prt("enter!\n");
return 0;
}
static int hp_blk_remove(struct device *dev)
{
hp_prt("enter!\n");
return 0;
}
static struct device_driver hp_driver = {
.name = "hp_drv",
// .pm = &hp_blk_pm_ops,
.probe = hp_blk_probe,
.remove = hp_blk_remove,
// .shutdown = hp_blk_shutdown,
};
static int code_case_device_init(void)
{
int ret;
hp_prt("enter!\n");
ret = bus_register(&hp_bus_type);
if(ret != 0) {
hp_prt("bus_register error!\n");
return -1;
}
hpdev = alloc_hp_device();
if(hpdev == NULL) {
hp_prt("alloc_hp_device error!\n");
goto ERR1;
}
ret = device_add(hpdev);
if(ret != 0) {
hp_prt("device_add error!\n");
goto ERR0;
}
hp_driver.bus = &hp_bus_type;
ret = driver_register(&hp_driver);
if(ret != 0) {
hp_prt("device_add error!\n");
goto ERR0;
}
return ret;
ERR0:
kfree(hpdev);
ERR1:
bus_unregister(&hp_bus_type);
return -1;
}
static void code_case_device_exit(void)
{
hp_prt("enter!\n");
device_del(hpdev);
put_device(hpdev);
bus_unregister(&hp_bus_type);
driver_unregister(&hp_driver);
kfree(hpdev);
}
module_init(code_case_device_init);
module_exit(code_case_device_exit);