首先我们要了解bus驱动框架是由三部分组成的:Device(硬件相关),Bus(中间媒介),Driver(软件)
为了实现高内聚,低耦合,解决重复代码过多,对整个驱动进行了分层。硬件相关的部分被抽象成Device部分,纯软件部分抽象成Driver部分,为了将这两部分匹配起来就创建了bus这一中间媒介。
接下来搭建一个基础的框架
(一)Bus的构建
1 先来看看bus中的bus_type结构体
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
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 (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
里面含有非常多的成员,而在我们这个简单框架中要定义的成员只有以下两项
const char *name;//该bus总线的名字
int (*match)(struct device *dev, struct device_driver *drv);//dev和drv的匹配方法
2 内核提供的注册bus的API和销毁bus的API
注册:
int bus_register(struct bus_type *bus);
销毁:
void bus_unregister(struct bus_type *bus);
3 bus代码如下:
#include
#include
#include
/*
做一个bus的总线驱动
*/
//返回值为1时正确匹配,非1则匹配失败
int mybus_match(struct device *dev, struct device_driver *drv)
{
//按名字匹配dev和drv
if(strncmp(drv->name, dev->kobj->name, sizeof(drv->name)) == 0)
{
return 1;
}else
{
return 0;
}
}
struct bus_type mybus = {
.name = "mybus",
.match = mybus_match,
};
EXPORT_SYMBOL(mybus); //将mybus提供到内核中供别的代码使用
static int __init mybus_init(void)
{
printk("-----------%s-----------", __FUNCTION__);
bus_register(&mybus);//调用API注册mybus
return 0;
}
static void __init mybus_exit(void)
{
printk("-----------%s-----------", __FUNCTION__);
bus_unregister(&mybus);//调用API销毁mybus
}
module_init(mybus_init);
module_exit(mybus_exit);
MODULE_LICENSE("GPL");
其中需要注意的部分是,在match函数中,我们是使用了名字来匹配drv和dev,自然就需要strncmp两个结构体中的名字成员。而在内核运行时,系统会将我们在dev中定义的*init_name(设备名字),提取放到kobj中的链表,并将*init_name置为NULL。因此我们在match中要对比的是dev->kobj->name和drv->name。
(二)Device的构建
1 硬件的数据抽取在device结构体中,我们先来看看这个结构体成员:
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
...........
}
因为成员过多,这里只放出公用的成员。其中在我们这个简单框架中需要定义的成员如下:
const char *init_name;//名字
struct bus_type *bus;//挂载的bus总线名字
void *platform_data;//要给drv的数据
void (*release)(struct device *dev);//释放该dev时需要调用的函数
2 内核提供的注册dev的API和销毁dev的API
注册:
int device_register(struct device *dev);
销毁:
void device_unregister(struct device *dev);
3 dev部分代码如下:
#include
#include
#include
/*
做一个bus 的设备
*/
extern struct bus_type mybus;
void mydev_release(struct device *dev)
{
printk("-------------%s------------\n",__FUNCTION__);
}
struct device mydev ={
.init_name = "mydev",
.bus = &mybus,
.release = mydev_release,
};
static int __init mydev_init(void)
{
printk("-----------%s-----------", __FUNCTION__);
device_register(&mydev);
return 0;
}
static void __init mydev_exit(void)
{
printk("-----------%s-----------", __FUNCTION__);
device_unregister(&mydev);
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
(三)Driver的构建
1 还是先来看看结构体device_driver
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
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 attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
在我们这个框架中需要定义的成员如下:
const char *name;//名字(与dev中要匹配)
struct bus_type *bus;//挂载的bus总线名字
int (*probe) (struct device *dev);//匹配到dev后对dev的数据进行操作的函数
int (*remove) (struct device *dev);//与probe成对使用
2 内核提供的注册drv的API和销毁drv的API
注册:
int driver_register(struct device_driver *drv);
销毁:
void driver_unregister(struct device_driver *drv);
3 drv部分代码如下:
#include
#include
#include
/*
做一个bus 的驱动
*/
extern struct bus_type mybus;
int mydrv_probe(struct device *dev)
{
return 0;
}
int mydrv_remove(struct device *dev)
{
return 0;
}
struct device_driver mydrv = {
.name = "mydev",
.bus = &mybus,
.probe = mydrv_probe,
.remove = mydrv_remove,
};
static int __init mydrv_init(void)
{
printk("-----------%s-----------", __FUNCTION__);
driver_register(&mydrv);
return 0;
}
static void __init mydrv_exit(void)
{
printk("-----------%s-----------", __FUNCTION__);
driver_unregister(&mydrv);
}
module_init(mydrv_init);
module_exit(mydrv_exit);
MODULE_LICENSE("GPL");
完成以上三份文件后,一个基础的bus框架就搭建好了。
编译成.ko文件再在板子上跑起来,我们可以用:
ls /sys/bus/
查看在sys/bus目录下有没有我们的mybus
然后可以进入mybus目录下,分别查看devices和dirvers目录下有没有mydev和mydrv
[root@farsight ]# ls /sys/bus/mybus/devices/
mydev
[root@farsight ]# ls /sys/bus/mybus/drivers
mydev
也可以直接用:lsmod命令查看加载的驱动:
mydrv 912 0 - Live 0xbf008000 (O)
mydev 1247 0 - Live 0xbf004000 (O)
mybus 1060 2 mydrv,mydev, Live 0xbf000000 (O)