目录
1.platform介绍:
从Linux2.6
开始Linux加入了一套驱动管理和注册机制—platform
总线驱动模型。platform
总线是一条虚拟总线(只有一条),这类总线没有对应的硬件结构。platform_device
为相应的设备,platform_driver
为相应的驱动。与传统的bus/device/driver
机制相比,platform
由内核统一进行管理,提高了代码的可移植性和安全性。
优点:
1.可以通过platform总线,可以遍历所有的platform总线设备;platform本质其实也是kset、 kobject,具有kobject的特性;
2.实现设备与驱动的分离,通过platform总线,设备与驱动是分开注册的,通过platform总线的 probe来随时检测与设备匹配的驱动,如匹配上即进行这个设备的驱动注册;
3.一个驱动可以供同类的几个设备使用;
2.设备树介绍:
设备树是一种描述硬件得数据结构,在操作系统引导阶段进行设备初始化得时候,数据结构中得硬件信息被检测并传递给操作系统。
Liunx内核从3.x开始引入设备树得概念,用于实现驱动代码与设备信息相分离。在设备树出现之前,所有关于设备得具体信息都要写在驱动里面,一旦外围设备变化,驱动代码就要重写。引入设备树后,驱动代码只负责处理驱动得逻辑,而关于设备具体信息要存放到设备树文件中。
这样,如果只是硬件接口信息得变化而没有驱动逻辑得变化,驱动开发者只需要修改设备树文件信息,不需要改写驱动代码。
3.实现过程:
1.设备树:(以exynos-fs4412为例)
首先在设备树文件中添加蜂鸣器的设备节点,同时添加对应寄存器地址,由于设备树可以自动遍历查找地址,所以只需填写一个地址范围,包含所有的寄存器就行。compatible ="fs,mybee"而这句话就代表一种匹配规则。
mybee@11000ca0{
compatible ="fs,mybee";
reg = <0x11000a40 0x4>,<0x139d0000 0x14>;
};
2.平台驱动:
1)首先我们可以搭建一个平台驱动的基本框架,框架如下,我们在对应的位置填入对应的代码。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <asm/io.h>
struct of_device_id of_matches[]={
//修改匹配规则,从设备树
//获取buzzer相关硬件信息
};
static struct platform_driver mydriver ={
//通过设备树匹配
};
static int mod_init(void)
{
return platform_driver_register(&mydriver); //平台驱动注册
}
static void mod_exit(void)
{
platform_driver_unregister(&mydriver); //平台驱动注销
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_LICENSE("GPL");
2)我们修改匹配机制,从设备树匹配,配诶规则一定要和我们写的设备树中的规则一致。
struct of_device_id of_matches[]={
{.compatible="fs,mybee"}, //修改匹配规则,从设备树
{}, //获取buzzer相关硬件信息
};
3) paltform选择匹配方式:
static struct platform_driver mydriver ={
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "mytest",
.of_match_table = of_matches, //通过设备树匹配
},
};
3.驱动的启动函数:
前面的准备工作完成后,从设备树中获取设备的信息,就需要将字符设备注册到内核中,首先将所需的寄存器物理地址映射为虚拟地址,然后进行字符设备的注册,首先申请设备号,然后cdev初始化,紧接着将cdev添加到内核,当所有步骤完成后就可以添加硬件操作的函数了。
int my_probe(struct platform_device *pdev)
{
int ret;
//通过设备树获取硬件资源
printk( "match\n");
//0代表匹配的设备树第0个地址为基地址
rescon = platform_ get_ resource (pdev , IORESOURCE MEM, 0);
if ( rescon=-NULL){
goto failed_ getcon;
}
resdata = platform get_ resource (pdev , IORESOURCE MEM,1) ;
if ( resdata==NULL ){
goto failed_ getdata;
gpd0con = ioremap( rescon->start,4);
//利用设备树获取各个寄存器的地址
tcfg0 = ioremap( resdata->start,4) ;
tcfg1 = ioremap ( resdata->start+4,4) ;
tcon = ioremap( resdata->start+8,4);
tcntb0 = ioremap ( resdata->start+12,4);
tcmpb0 = ioremap( resdata->start+16,4);
//字符设备注册
ret = alloc_chrdev_region(&devnum,0,1,name);//1.申请设备号
if(ret!=0){
goto failed_alloc;
}
cdev_init(&mycdev,&myops); //2.cdev初始化
ret = cdev_add(&mycdev,devnum,1); //3.cdev添加到内核
if(ret!=0){
goto failed_add;
}
printk("register success %d,%d\n",MAJOR(devnum),MINOR(devnum));
myclass = class_create(THIS_MODULE,"myclass");
if(IS_ERR(myclass)){
goto failed_class;
}
mydevice = device_create(myclass,NULL,devnum,NULL,"buzzer");
if(IS_ERR(mydevice)){
goto failed_device;
}
//硬件操作
buzzer_init();
buzzer_off();
return 0;
failed_device:
class_destroy(myclass);
failed_class:
cdev_del(&mycdev);
failed_add:
unregister_chrdev_region(devnum,1);
failed_alloc:
return -1;
}
4.驱动卸载
现将所有的寄存器解除映射,释放设备号,释放设备对象,删除设备,将设备从内核中注销。
int my_remove(struct platform_device *pdev)
{
printk("driver remove\n");
iounmap(gpd0con);
iounmap(tcfg0);
iounmap(tcfg1);
iounmap(tcon);
iounmap(tcntb0);
iounmap(tcmpb0);
device_destroy(myclass,devnum);
class_destroy(myclass);
cdev_del(&mycdev);
unregister_chrdev_region(devnum,1);
return 0;
}
这样就基本完成了整个蜂鸣器的驱动编写,希望有帮助!