简介Platform:
内核中有IIC总线、PCI总线、串口总线、SPI总线、can总线、单总线等,一些设备可以挂载在这些总线上,然后通过总线的match进行设备和驱动的匹配。但是有些设备并不属于这些总线,于是引入了虚拟总线,也就是Platform总线,对于的设备叫做Platform设备,对于的驱动叫做Platform驱动。
平台设备框图:
Platform总线:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.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) == 0);
}
Platform设备:
struct platform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
资源结构体的定义:
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
Platform驱动:
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;
};
一、分离分层构建驱动框架
input子系统中,采用了分离分层的方式构建驱动程序框架。input.c为核心层,其下有左边的硬件设备相关,和右边的软件handler相关处理函数。
二、platform总线驱动框架分析
一个现实的linux设备和驱动通常都需要挂接在一种总线上,比较常见的总线有USB、PCI总线等。但是,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。基于这样的背景下,2.6内核加入了platform虚拟总线。platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性。linux在系统启动时就注册了platform总线.
1.参考:Gpio_keys.c (driversinputkeyboard)来看一个例子
2.Bus-drv-dev模型:
Bus结构体;
drv结构体:比较稳定的代码
dev结构体:硬件相关
三个都是结构体。
static int __init gpio_keys_init(void)
{
return platform_driver_register(&gpio_keys_device_driver);//注册平台设备。
}
总线是虚拟平台总线 platform_bus_type
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}
platform_driver_register--》driver_register
platform_driver_register:把driver的结构体放到bus中的drv链表中,还会和设备比较drv支不支持dev。
虚拟平台总线里面有个match函数
struct bus_type platform_bus_type = {
.name= "platform",
.dev_attrs= platform_dev_attrs,
.match= platform_match,
.uevent= platform_uevent,
.suspend= platform_suspend,
.suspend_late= platform_suspend_late,
.resume_early= platform_resume_early,
.resume= platform_resume,
};
根据名字判断是否匹配,匹配则调用.probe函数
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) == 0);
}
3.
driver_register(稳定的代码):
(1) 会把drv放入Bus总线的drv链表里面
(2) 从bus的dev链表取出每个dev,用bus的match函数一一比较,判断dev能否支持drv。若可以支持调用drv的.probe函数
device_add(硬件相关):
(1) 把dev结构体放入bus的dev链表里面去
(2) 从bus的drv链表取出每个drv,用bus的match函数一一比较,判断drv能否支持dev。若可以支持调用drv的.probe函数
Bus里面有一个比较函数.match比较drv是否支持这个dev,能支持的话调用里面的.probe函数
4.自己写一个led的platform总线驱动程序
(1)参考Mach-smdk2440.c (archarmmach-s3c2440)
(2)led_dev.c
static struct platform_device s3c2440_device_sdi = {
.name = "s3c2440-sdi",
.id = -1,
.num_resources = ARRAY_SIZE(s3c2440_sdi_resource),
.resource = s3c2440_sdi_resource,
};
定义了一个平台设备,平台设备里面含有某些资源,寄存器起始地址,那个引脚等。
/* SDI */
static struct resource s3c2440_sdi_resource[] = {
[0] = {
.start = S3C2410_PA_SDI,
.end = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SDI,
.end = IRQ_SDI,
.flags = IORESOURCE_IRQ,
}
};
入口函数:注册一个平台设备
platform_device_register最后会执行如上:放入平台总线的设备链表里面去。
加上头文件
(3)另一边led_drv.c
参考:
(4)编译:
(5)回到以前实现led:注册一个字符设备,构造一个结构体
staticvolatile unsigned long *gpio_con;
staticvolatile unsigned long *gpio_dat;寄存器
res = platform_get_resource(pdev,IORESOURCE_MEM, 0);
gpio_con = ioremap(res->start,res->end - res->start + 1);//大小
staticint pin;引脚
res = platform_get_resource(pdev,IORESOURCE_IRQ, 0);
pin=res->start;
(6).probe做什么自己决定,自己的核心内容
三、platform总线驱动代码实现
(1)led_dev.c 分配/设置/注册一个platform_device
#include #include #include #include #include #include #include #include #include #include #include /* 分配/设置/注册一个platform_device */ static struct resource led_resource[] = { [0] = { .start = 0x56000050, .end = 0x56000050 + 8 - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = 5, .end = 5, .flags = IORESOURCE_IRQ, } }; static void led_release(struct device * dev) { } static struct platform_device led_dev = { .name = "myled", .id = -1, .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 0; } 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)led_drv.c 分配/设置/注册一个platform_driver /* 分配/设置/注册一个platform_driver */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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_openn"); /* 配置为输出 */ *gpio_con &= ~(0x3<<(pin*2)); *gpio_con |= (0x1<<(pin*2)); return 0; } static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { int val; //printk("first_drv_writen"); copy_from_user(&val, buf, count); // copy_to_user(); if (val == 1) { // 点灯 *gpio_dat &= ~(1< else { // 灭灯 *gpio_dat |= (1< return 0; } 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, 0); gpio_con = ioremap(res->start, res->end - res->start + 1); gpio_dat = gpio_con + 1; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); pin = res->start; /* 注册字符设备驱动程序 */ printk("led_probe, found ledn"); major = register_chrdev(0, "myled", &led_fops); cls = class_create(THIS_MODULE, "myled"); class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */ return 0; } static int led_remove(struct platform_device *pdev) { /* 卸载字符设备驱动程序 */ /* iounmap */ printk("led_remove, remove ledn"); class_device_destroy(cls, MKDEV(major, 0)); class_destroy(cls); unregister_chrdev(major, "myled"); iounmap(gpio_con); return 0; } 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 0; } 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)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 (4)测试程序 #include #include #include #include /* led_test on * led_test off */ int main(int argc, char **argv) { int fd; int val = 1; fd = open("/dev/led", O_RDWR); if (fd < 0) { printf("can't open!n"); } if (argc != 2) { printf("Usage :n"); printf("%s return 0; } if (strcmp(argv[1], "on") == 0)
上一篇:10-S3C2440驱动学习(四)嵌入式linux-LCD驱动程序
下一篇:08-S3C2440驱动学习(二)嵌入式linux-input子系统分析与应用
推荐阅读最新更新时间:2024-11-16 20:11
推荐帖子
- 开关电源的中部分元件的重要性
- 最近被一个项目的CE和EFT折腾的疲惫,带火牛的项目也做过几个,都没遇见这样的,怀疑电源本身对干扰的抑制不好,拆开电源后看到电源适配器的AC输入端无X电容,无共模电感,有些甚至输出的型滤波器也没有。想知道这是都是成本的原因吗?另外,据了解此类适配器在出厂测试时电子负载基本都是在恒流模式,并不会测试动态负载下的情况,这样又如何保证在动态负载突变时的情况一致。开关电源上省下来的元件还是要加到后级产品上,这样做法真的科学吗?开关电源的中部分元件的重要性成本呗感觉就是为了控制成本,
- classd 开关电源学习小组
- [BearPi-Pico H2821]测评 ⑥丢包及连接稳定性测试
- 上两篇我们查了一下SLEserver和client端的demo,怎么运行的,收发应该调用那些接口已经清楚了,就可以开始做丢包及连接稳定性测试了测试方案server端代码不用动,client端连接server后,创建一个TASK,定时用SLE向server发数据,并计数,发送到最大次数后,停止发送,摧毁task。然后提取双方日志,查看中途是否有断连、收发次数是否有缺少1.client端代码修改1.1发送函数参照之前找到的发送函数sle_ua
- 不爱胡萝卜的仓鼠 RF/无线
- 因公司生产需要求购STM32F437VIT6芯片
- 因公司生产需要求购STM32F437VIT6芯片,数量需要3-5K,拆包的、散的也可以,只要是原装的就可以,年份19+以后的都行,如果有这个芯片库存的公司可与我联系,我们会高于原价数倍的价格采购,具体的价格我们详谈,能帮找到也可,电话、微信同号:13718461954因公司生产需要求购STM32F437VIT6芯片您散新原装接受价和全新原装接受价能到多少?我这有朋友在搞你朋友那边是贸易还是工厂的库存啊?数量需要3-5K,拆包的、散的也可以,只要是原装的就可以,年份19+以
- dongfanghanke 淘e淘
- MOSFET的开关电压Vgs
- 如题。请问一下,MOSFET的手册里面哪个参数能看的出来,当其作为开关管,完全打开的时候,Vgs的电压?同事跟我讲,默认12V大多数都可以完全打开(NMOS)。低于12V就有点悬,MOS打开不完全,会导致Rds的电阻过大,MOS管发热。所以他现在设计的电路图,基本上都是12V打开MOS管。举个例子,如下图MOSFET的开关电压VgsMOS管完全打开当然是看Id是否最大值同事说的有一定道理。但是要根据MOS管的手册数据看。以楼主的图中的小功率MOS管2N7002来说
- 平漂流 模拟电子
- 三台以上西门子S7-200之间的MODBUS无线通讯程序
- 本例程是针对两台以及三台以上西门子S7-200之间的的无线MODBUS协议数据的传输,组成一点(主机)对多点(从机)的无线PLC测控网络,其中一台S7-200作为主机,从机根据编号不同可以增加到255个,本方案在大量工程中使用,用户可以参考例程源码编写自己的应用程序。一、主机:S7-2001.一个系统中需要把一个S7-200作为主机,与主机连接的西门子专用无线数据终端型号为:DTD434M无线数据终端。2.主机自带RS485通信接口,可以与DTD433M控制器的RS485接口
- dataie123456 RF/无线
- 用C和WIN32 API写了个五子棋的小游戏,现在想加个联网的功能,用SOCKET,有几个问题大家帮忙
- 1.SOCKET通信写在哪个消息里?我认为应该在每次按下的时候,SERVER端进行一次发送和一次接受,CLINET端进行一次接收和一次发送2.如何判断两个机器有没有联通啊,如何调试3,希望大家能给个WINAPI的例子,要C的C++不懂呢谢谢了用C和WIN32API写了个五子棋的小游戏,现在想加个联网的功能,用SOCKET,有几个问题大家帮忙1、楼主正解,SOCKET消息应该在SEVER或CLIENT走每一步棋的时候发。2、建立连接时,可以根据connect()与accep
- clhlx0785 嵌入式系统
设计资源 培训 开发板 精华推荐
- 用于精密电流源的 ADR360B、2.048V 低功耗、低噪声电压基准的典型应用
- Ch32V208DualCDC
- LTC2992IDE-1 四象限功率监视器的典型应用(10kHz I2C 接口)
- MC33166离线预转换器的典型应用
- STM32F10xxx ADC应用电路使用STM32F10xxx ADC_IN10、ADC_IN11和ADC_IN14连接
- 使用符合 EN55022 B 类(24Vin 和 48Vin,双输出)具有 EMC 滤波的 RP40-2424SFR DC/DC 转换器的典型应用
- NCP3337MN250GEVB:2.5 V LDO 稳压器评估板
- LT1307CS8 外部控制突发模式操作的典型应用电路
- 3528LED灯板
- 使用 ROHM Semiconductor 的 BU94605KV 的参考设计