Linux学习 : 总线-设备-驱动模型
2023-11-28 11:03:30
platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver。Linux 2.6的设备驱动模型中,把I2C、RTC、LCD等都归纳为platform_device。总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
platform_match函数首先判断是否有id_table,如果有则使用id_table来进行匹配,否则,判断platform_device和platform_driver成员里的name,如果二者的name字段相同则匹配,如果匹配则调用platform_driver的probe函数,其中对资源进行get、ioremap再注册驱动。
LED总线设备驱动模型程序示例:
1.device:主要设置硬件相关资源
/*led_dev.c*/
#include <linux/module.h>
#include <linux/version.h> #include <linux/init.h> #include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h> /* 分配/设置/注册一个platform_device */ static struct resource led_resource[] = {
[] = {
.start = 0x56000050, //led控制寄存器地址
.end = 0x56000050 + - ,
.flags = IORESOURCE_MEM, //内存资源
},
[] = {
.start = , //设置引脚号
.end = ,
.flags = IORESOURCE_IRQ, //中断资源
}
}; static void led_release(struct device * dev)
{
//框架需求要设置一个release函数,不然会报错
} static struct platform_device led_dev = {
.name = "myled",
.id = -,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
.dev = {
.release = led_release,
},
}; static int led_dev_init(void)
{
platform_device_register(&led_dev); //注册平台设备
return ;
} static void led_dev_exit(void)
{
platform_device_unregister(&led_dev);
} module_init(led_dev_init);
module_exit(led_dev_exit); MODULE_LICENSE("GPL");
2.driver:注册平台驱动,匹配上设备后调用probe函数:获取device资源,分配、设置、注册字符设备,实现对设备的操作函数(open,read,write)
/*led_drv.c*/ /* 分配/设置/注册一个platform_driver */ #include <linux/module.h>
#include <linux/version.h> #include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h> static int major; static struct class *cls;
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin; static int led_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/* 配置为输出 */
*gpio_con &= ~(0x3<<(pin*));
*gpio_con |= (0x1<<(pin*));
return ;
} static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val; //printk("first_drv_write\n"); copy_from_user(&val, buf, count); // copy_to_user(); if (val == )
{
// 点灯
*gpio_dat &= ~(<<pin);
}
else
{
// 灭灯
*gpio_dat |= (<<pin);
} return ;
} static struct file_operations led_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = led_open,
.write = led_write,
}; static int led_probe(struct platform_device *pdev)
{
struct resource *res; /* 根据platform_device的资源进行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, );
gpio_con = ioremap(res->start, res->end - res->start + );
gpio_dat = gpio_con + ; res = platform_get_resource(pdev, IORESOURCE_IRQ, );
pin = res->start; /* 注册字符设备驱动程序 */ printk("led_probe, found led\n"); major = register_chrdev(, "myled", &led_fops);
//第一个参数指定类的所有者是哪个模块,第二个参数指定类名。
cls = class_create(THIS_MODULE, "myled");
//第一个参数指定所要创建的设备所从属的类,第二个参数是这个设备的父设备,如果没有就指定为NULL,第三个参数是设备号,第四个参数是供回调使用的数据,第五个参数是设备名字。
class_device_create(cls, NULL, MKDEV(major, ), NULL, "led"); /* 自动创建设备节点/dev/led(也可手动mknod),新版为device_create(...)*/ return ;
} static int led_remove(struct platform_device *pdev)
{
/* 卸载字符设备驱动程序 */
/* iounmap */
printk("led_remove, remove led\n"); class_device_destroy(cls, MKDEV(major, ));
class_destroy(cls);
unregister_chrdev(major, "myled");
iounmap(gpio_con); return ;
} struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
}
}; static int led_drv_init(void)
{
platform_driver_register(&led_drv);
return ;
} static void led_drv_exit(void)
{
platform_driver_unregister(&led_drv);
} module_init(led_drv_init);
module_exit(led_drv_exit); MODULE_LICENSE("GPL");
3.测试应用程序:
/*led_test.c*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h> /* led_test on
* led_test off
*/
int main(int argc, char **argv)
{
int fd;
int val = ;
fd = open("/dev/led", O_RDWR);
if (fd < )
{
printf("can't open!\n");
}
if (argc != )
{
printf("Usage :\n");
printf("%s <on|off>\n", argv[]);
return ;
} if (strcmp(argv[], "on") == )
{
val = ;
}
else
{
val = ;
} write(fd, &val, );
return ;
}
4.Makefile
KERN_DIR = /work/system/linux-2.6.22.6 all:
make -C $(KERN_DIR) M=`pwd` modules clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order obj-m += led_drv.o
obj-m += led_dev.o
最新文章
- Microsoft Help Viewer
- Makefile规则③规则语法、依赖、通配符、目录搜寻、目标
- 使用Spring的JAVA Mail支持简化邮件发送
- Hadoop如何恢复被删除的文件
- 在.NET2.0中解析Json和Xml
- POJ 2752 Seek the Name, Seek the Fame (KMP的next函数,求前缀和后缀的匹配长度)
- 配置Rip的认证
- AMQ学习笔记 - 12. Spring-JmsTemplate特性设置
- Photoshop:路径填充边缘虚化问题
- 【动态规划】Vijos P1616 迎接仪式
- oracle以web方式登录EM、ISQLPlus
- [Android 4.4.3] 泛泰A860 Omni4.4.3 20140610 RC2.0 三版通刷 by syhost
- Thinking in UML 学习笔记(一)——建立对象模型
- K2 BPM项目 基于COM组件调用SAP RFC 问题
- java类静态域、块,非静态域、块,构造函数的初始化顺序
- 转:C#生成唯一值的方法汇总
- 音频软件开发中的debug方法和工具
- IPsec_VPN实现技术【转载】
- utl_file包的使用
- 数据库常用操作SQL语句
热门文章
- 雷林鹏分享: C# 教程
- Hypergeometric distribution
- python cook 2
- java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: service-one
- fedora21 中lamp的搭建(测试没有问题)
- thinkphp 中MVC思想
- 跨域和jsonp的原理
- VBA find查找行号和列号的方法
- php + mysql 分布式事务
- Mac系统安装和配置tomcat步骤详解