驱动04.平台总线驱动模型——点亮LED灯
1 平台总线的简介
平台总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver。总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
我们可以把一个驱动程序抽出来分为两部分,一部分是硬件相关的dev,另一部分则是稳定的纯软件部分driver。而总线只是一种机制,把dev和driver这两部分建立“联系”的机制。
eg:
①dev部分
a.把device放入bus的dev链表
b.从bus的drv链表取出每一个drv,用bus的match函数判断drv是否支持dev
c.如果支持,将调用drv的probe函数
②drv部分
a.把driver放入bus的drv链表
b.从bus的dev链表取出每一个dev,用bus的match函数判断dev是否支持drv
c.如果支持,将调用drv的probe函数
2 linux平台总线的代码分析
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, //dev和drv匹配时调用的函数
.uevent = platform_uevent,
.suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume,
};
static int platform_match(struct device * dev, struct device_driver * drv)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev); return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == );//当dev的设备名与drv的名字相同时,则匹配成功
}
platform_device结构体的定义
struct platform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;//资源数目
struct resource * resource;//设备信息,使用platform_get_resource函数来获取资源信息
};
注册平台设备platform_device_register,实际上是加入到bus的dev链表中
int platform_device_register(struct platform_device * pdev)
{
device_initialize(&pdev->dev);//初始化platform_device的device成员
return platform_device_add(pdev);//实际上是调用device_add函数
}
同样的,也有platform_driver结构体的定义
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
}
//device_driver的定义:
struct device_driver {
const char * name;
struct bus_type * bus;
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module * owner;
const char * mod_name; /* used for built-in modules */
struct module_kobject * mkobj;
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);
};
注册device_driver结构体:
int driver_register(struct device_driver * drv)
{
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
}
klist_init(&drv->klist_devices, NULL, NULL);
return bus_add_driver(drv);
}
3 写代码
3.1框架
(1)分配、设置、注册一个platform_device/platform_driver结构体
(2)按照led.c的框架来构建
3.2 源代码
#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> static struct resource myled_dev_resource[] = {
[] = {
.start = 0x56000050,
.end = 0x56000050 + - ,
.flags = IORESOURCE_MEM,
},
[] = {
.start = ,
.end = ,
.flags = IORESOURCE_IRQ,
}
}; static void myled_dev_release(struct device * dev)
{
} static struct platform_device myled_dev = {
.name = "myled",
.id = -,
.num_resources = ARRAY_SIZE(myled_dev_resource),
.resource = myled_dev_resource,
.dev = {
.release = myled_dev_release,
}
}; static int myled_dev_init(void)
{
platform_device_register(&myled_dev);
return ;
} static void myled_dev_exit(void)
{
platform_device_unregister(&myled_dev);
} module_init(myled_dev_init);
module_exit(myled_dev_exit); MODULE_LICENSE("GPL");
myled_dev.c
#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 struct class *g_ptMyledCls;
static struct class_device *g_ptMyledClsDev;
static int g_iPin; volatile unsigned long *gpiocon = NULL;
volatile unsigned long *gpiodat = NULL; static int myled_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/* 配置GPF4,5,6为输出 */
*gpiocon &= ~(0x3<<(g_iPin*)) ;
*gpiodat |= (0x1<<(g_iPin*)) ;
return ;
} static ssize_t myled_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 == )
{
// 点灯
*gpiodat &= ~(<<g_iPin);
}
else
{
// 灭灯
*gpiodat |= (<<g_iPin);
} return ;
} static struct file_operations myled_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = myled_open,
.write = myled_write,
}; static int g_iMajor; static int myled_probe(struct platform_device *dev)
{
struct resource *tRes;
tRes = platform_get_resource(dev, IORESOURCE_MEM, );
gpiocon = ioremap(tRes->start, tRes->end - tRes->start +);
gpiodat = gpiocon + ; tRes = platform_get_resource(dev, IORESOURCE_IRQ, );
g_iPin = tRes->start; printk("myled_probe, found led\n"); g_iMajor = register_chrdev(, "myled", &myled_fops); // 注册, 告诉内核
g_ptMyledCls = class_create(THIS_MODULE, "myled"); g_ptMyledClsDev = class_device_create(g_ptMyledCls, NULL, MKDEV(g_iMajor, ), NULL, "myled"); /* /dev/xyz */
return ;
} static int myled_remove(struct platform_device *dev)
{
printk("led_remove, remove led\n"); class_device_destroy(g_ptMyledCls, MKDEV(g_iMajor, ));
class_destroy(g_ptMyledCls);
unregister_chrdev(g_iMajor, "myled");
iounmap(gpiocon); return ;
} static struct platform_driver myled_driver = {
.probe = myled_probe,
.remove = myled_remove,
.driver = {
.name = "myled",
},
}; static int myled_drv_init(void)
{
platform_driver_register(&myled_driver);
return ;
} static void myled_drv_exit(void)
{
platform_driver_unregister(&myled_driver);
} module_init(myled_drv_init);
module_exit(myled_drv_exit); MODULE_LICENSE("GPL");
myled_drv.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h> /* firstdrvtest on
* firstdrvtest off
*/
int main(int argc, char **argv)
{
int fd;
int val = ;
fd = open("/dev/myled", 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 ;
}
测试程序
编辑于2017-01-09 16:36:01
驱动04.平台总线驱动模型——点亮LED灯的更多相关文章
- linux平台总线驱动设备模型之点亮LED
这一节里,我们来使用平台驱动设备这一套架构来实现我们之前使用简单的字符设备驱动点亮LED,这里并无实际意义,只是告诉大家如果编写平台总线驱动设备. 问:如何编写平台总线驱动设备这一套架构的设备驱动? ...
- 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联
转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...
- [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联
转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...
- 第7章 使用寄存器点亮LED灯
第7章 使用寄存器点亮LED灯 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...
- 第7章 使用寄存器点亮LED灯—零死角玩转STM32-F429系列
第7章 使用寄存器点亮LED灯 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fir ...
- 字符型设备驱动程序-first-printf以及点亮LED灯(三)
根据 字符型设备驱动程序-first-printf以及点亮LED灯(二) 学习 修改函数 中的printf 为 printk. #include <linux/module.h> /* ...
- Raspberry PI 系列 —— 裸机点亮LED灯
Raspberry PI 系列 -- 裸机点亮LED灯 背景 近期刚买了Raspberry PI B+,配置执行了官方提供的Raspbian系统,折腾了一周Linux系统,感觉没啥意思,于是就试着想了 ...
- 第二章之S5PV210在BL1中点亮LED灯
1,u-boot中第一个入口在./arch/arm/cpu/armv7/start.S 翻到153行:如下图 前面都是进行一些基本设置,不用管. cpu_init_cp15设置协处理器, cpu_in ...
- C语言版——点亮LED灯,深入到栈
在上一篇进行了汇编语言的编写之后,我们采用C语言来编写程序,毕竟C语言才是我们使用最多的语言. 仅仅是点亮LED灯显然太过于简单,我们需要分析最后的反汇编,了解函数调用栈,深入C语言骨髓去分析代码,并 ...
随机推荐
- 使用Windows2003创建DHCP服务器 - 进阶者系列 - 学习者系列文章
Windows 2003提供的DHCP服务还是挺强大的.下面大概介绍下DHCP服务器的配置. 1. 通过控制面板安装DHCP服务 2. 打开DHCP配置项 3. 选择 新建作用域 4. 输入名 ...
- HTML5表单提示placeholder属性兼容IE
placeholder 属性提供可描述输入字段预期值的提示信息(hint). 该提示会在输入字段为空时显示,并会在字段获得焦点时消失. 注释:placeholder 属性适用于以下的 <inpu ...
- Step one : 熟悉Unix/Linux Shell 常见命令行 (三)
3.学会使用一些管理命令 ps/top/lsof/netstat/kill/tcpdump/iptables/dd 端口查看 ps -- process status ps aux 观察程序所有程序 ...
- 线程:ThreadLocal实现线程范围内共享变量
在web应用中,一个请求(带有请求参数)就是一个线程,那么如何区分哪些参数属于哪个线程呢?比如struts中,A用户登录,B用户也登录,那么在Action中怎么区分哪个是A用户的数据,哪个是B用户的数 ...
- 如何本地测试例如QQ登录等第三方接口
前言:现在基本是个网站就会集成第三方的一些接口,比如QQ登录.分享等等.但是在开发的时候,尤其是没有这方面经验的开发人员来说,调试流程时会显得迷茫,不知道怎么调试.这里就个人的这方面学习摸索做一个总结 ...
- 基于ffmpeg的C++播放器1
基于ffmpeg的C++播放器 (1) 2011年12月份的时候发了这篇博客 http://blog.csdn.net/qq316293804/article/details/7107049 ,博文最 ...
- MongoDB学习2
MongoDB学习(翻译2) C#驱动之LINQ教程 介绍 本教程涵盖了1.8发布版本对linq查询的支持. 开始本教程之前,你应该至少阅读下C#驱动教程关于C#驱动的介绍 快速开始 首先,添加下面命 ...
- 将JDBC ResultSet结果集转成List
private List<Map<String, Object>> list = new ArrayList<Map<String,Object>>() ...
- Make Things Move -- Javascript html5版(三)三角函数形式的动画
角度制和弧度制 生活中通常是用角度度来理解的,代码里都是用弧度制来计算. 角度转弧度:DEG_TO_RAD = Math.PI / 180 弧度装角度:RAD_TO_DEG = 180 / Math. ...
- SQL Server 2008 - Cannot set a credential for principal 'sa'.
SQL Server 2008 - Cannot set a credential for principal 'sa'. 很久没有用到SQL Server了,今天有幸在帮同事解决一个SQL Serv ...