红色字体部分与一般字符设备创建,提供应用层面的接口有关。
黑色字体部分则是 platform总线设备相关
内核版本为 4.x
上面是一个platform总线的设备,关于led的一个驱动程序。
大概结构就是通过总线,设备,驱动的设备驱动模型来管理设备,然后再通过字符设备创建的过程来向应用层提供接口,不知道这样做是否有点罗索与重复,但是目前的理解就是还不太到家。
通过probe函数实现应用层接口
remove可以用来取消应用层接口
然后还有使用goto的优点,假如在driver注册失败,那么你不仅要注销driver还要注销device,这个时候用goto就可以很清晰的而且使代码不产生冗余的情况下完成这份工作。
大致的原理是:
在Device添加的函数里面会调用与Driver和Bus相匹配的函数 例如Probe与 Match等。
由此联想到热插拔的可实现的方案(当然只是猜想 不过应该是可行的):
一个模块负责 时刻检测底层的I/O的改变以确定是否有设备加入,这时候这个模块就需要探测这个设备的属性,完成设备注册。这个应该是最底层驱动,负责将设备接入内核,添加Kobject(Device)
在Kobject的添加或者说注册的函数中就会调用一些函数(uevent之类的)来告诉用户有新设备插入,这个时候filter什么的就可以截获并决定是否告诉用户新设备是否插入,以此便实现了热插拔。
所以Bus与Driver就是负责了这个层面的一些事项,而cdev的添加是为了统一文件系统的接口,实现上层应用的调用。贴上源代码 ( 做了什么事情呢?主要是统一了设备驱动中 上层与下层 的设备模型 ):
#include <linux/module.h>
#include <linux/init.h>#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/fs.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Anfo");
void pfDev_release(struct device* dev){
}
struct resource pfDev_resource={
.start=(resource_size_t)0x7F008800,
.end=(resource_size_t)0x7F00880C,
.flags=IORESOURCE_MEM,
};
struct platform_device pfDev={
.name="s3c6410_led",
.id=-1,//auto allocated
.num_resources=1,
.resource=&pfDev_resource,
.dev={
.release=pfDev_release,
},
};
/*
----------------------------------------------------------
app led
*/
#define LED_NAME "led"
#define LED_MAJOR 120
#define LED_MINOR 0
#define LED_DEVT MKDEV(LED_MAJOR,LED_MINOR)
static int led_open(struct inode* ind,struct file* filp){
return 0;
}
static int led_release(struct inode* ind,struct file* filp){
return 0;
}
struct file_operations led_fops={
.owner=THIS_MODULE,
.open=led_open,
.release=led_release,
};
struct class* led_class;
int pfDrv_probe(struct platform_device* pdev){
//register the led to app
//register the device num
if(register_chrdev(LED_MAJOR,LED_NAME,&led_fops))
{
printk("----register_chrdev error!\n");
goto register_chrdev_error;
}
printk("----register_chrdev success\n");
led_class=class_create(THIS_MODULE,LED_NAME);
if(IS_ERR(led_class))
{
printk("----class_create error!\n");
goto class_create_error;
}
printk("----class_create success\n");
device_create(led_class,NULL,LED_DEVT,NULL,LED_NAME);//return struct device*
printk("----device_create success\n");
//
printk("pfDrv_probe success\n");
return 0;
class_create_error:
class_destroy(led_class);
register_chrdev_error:
unregister_chrdev(LED_MAJOR,LED_NAME);
return -1;
}
int pfDrv_remove(struct platform_device* pdev){
unregister_chrdev(LED_MAJOR,LED_NAME);
printk("----unregister_chrdev success!\n");
device_destroy(led_class,LED_DEVT);
printk("----device_destroy success\n");
class_destroy(led_class);
printk("----class_destroy success\n");
printk("pfDrv_remove success\n");
return 0;
}
/*------------------------------------------------------*/
struct platform_driver pfDrv={
.probe=pfDrv_probe,
.remove=pfDrv_remove,
.driver={
.name="s3c6410_led",
.owner=THIS_MODULE,
},
};
int __init PfDevMod_init(void){
printk("PfDevMod_init begin\n");
if(platform_device_register(&pfDev))
{
printk("platform_device_register error!\n");
goto device_register_error;
}
printk("platform_device_register success!\n");
if(platform_driver_register(&pfDrv))
{
printk("platform_driver_register error!\n");
goto driver_register_error;
}
printk("platform_driver_register success!\n");
suc:
return 0;
driver_register_error:
platform_driver_unregister(&pfDrv);
device_register_error:
platform_device_unregister(&pfDev);
return -1;
}
void __exit PfDevMod_exit(void){
printk("PfDevMod_exit begin\n");
platform_device_unregister(&pfDev);
printk("platform_device_unregister success!\n");
platform_driver_unregister(&pfDrv);
printk("platform_driver_unregister success!\n");
}
module_init(PfDevMod_init);
module_exit(PfDevMod_exit) ;