简介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-13 11:34
推荐帖子
- 关于SensorTag设计实现阶段延期的正式说明,请参与者都注意了啊!!
- 有的网友已经开始发心得,同时每天群里也在如火如荼地对SensorTag进行着讨论交流,看见这些,我们真的非常高兴。春节即将来临,提前祝大家节日快乐,同时,由于过年,休假、旅行、聚会......大家都有各自的事情要做,所以经过我们商量,SensorTag设计实现阶段延期到2014年3月24号结束,即日起到3月24号的任何时间段,大家都可以提交设计。【点击此处提交设计】至于周计划的提交,因为大家提交第一周计划的起点时间并不相同,所以只要在3月24号之前完成八周计划即可。最后祝大
- EEWORLD社区 无线连接
- 轻量级多级菜单控制框架程序
- #轻量级菜单框架(C语言)作为嵌入式软件开发,可能经常会使用命令行或者显示屏等设备实现人机交互的功能,功能中通常情况都包含UI菜单设计;很多开发人员都会有自己的菜单框架模块,防止重复造轮子,网上有很多这种菜单框架的代码,但是大多耦合性太强,无法独立出来适配不同的菜单设计。本文介绍一个降低了耦合性,完全独立的菜单框架,菜单显示风格和显示平台完全由自己根据需求设计,而菜单操作统一由菜单模块处理即可,提高程序的移植性。##介绍主要特点有:-移植性强,无需修改即可使用-采用
- 大橙子疯 单片机
- 来聊聊夏普的窄边框骨传导手机AQUOS CRYSTAL
- 今天的很多科技网站都在报道夏普的一款手机。下午碰巧与好友也聊了一些,欢迎大家说出自己的看法和观点:先从手机本身说起:最初知道这款个性的手机还是在8月末的一个科技新闻上看到的,大概意思是说夏普出了一款用自家效果最好的CGS屏做的超窄边框手机。它运用“水晶玻璃触摸面板(名为CRYSTALDISPLAY的技术)”技术,边缘为斜边切割,通过光学透镜达到了“隐藏”黑边的效果,再加上原本手机的边框就很窄,所以整体上看起来便达到了“近乎无边”。外形沿袭了
- eric_wang 移动便携
- CC2640 CC1310高低温测试
- CC13/26XX是TI全新一代支持Sub1G、2.4G私有协议、BLE、Zigbee、RF4CE和6LowPan的超低功耗多协议SOC处理器。CC2640为BLE低功耗蓝牙芯片,CC1310为支持低于1GHz的无线产品SOC。在datasheet都标注其支持的温度范围为-40至85℃,而在实验室高低温箱做高低温测试,运行CW载波,频偏在该温度范围下似乎都超出了范围。那实际研发的终端产品在-40至85℃还能稳定工作吗?图一下面我们以CC26
- Aguilera RF/无线
- 智能家居有哪些不足?如何解决这些问题呢?
- 什么是智能家居?智能家居是指利用综合布线技术、通信技术、物联网技术、安防技术、自动控制技术、音视频技术、人工智能等,整合与家居生活相关的设施,构建高效的住宅设施和家庭日程事务管理系统。智能家居的概念起源很早,市场前景也很广阔。然而,在探索智能家居的过程中,行业的乱象也随之产生。智能家居常见的缺点有哪些?1、装了不会用,利用率不高。由于智能家居的操作比较复杂,目前智能家居的市场还不够成熟,企业对系统的设计也不够人性化,所以操作起来比较困难,本来是要享受生活的,但是使用过程过于
- 石榴姐 RF/无线
- Keysight34972A 数据采集器
- Keysight(原Agilent)34972A数据采集开关单元包括3插槽主机、内置的6位半数字万用表以及8个不同的开关和控制模块。具有内置的LAN和USB接口。34972A能够支持所134970A插入式模块。Keysight34972A数据采集器
- zbdianzikeji 淘e淘
设计资源 培训 开发板 精华推荐
- LTC1551LCMS8-4.1 1mVP-P 纹波、-2V 输出 GaAs FET 偏置发生器的典型应用电路
- 使用 NXP Semiconductors 的 TDA8580J 的参考设计
- 双通道 LTC6101 允许高低电流范围应用电路
- #第五届立创电子设计大赛#基于HT82K629A的键盘飞线主控
- PCB_namecard PCB名片
- esp32扩展板
- 具有峰值电流软启动、小尺寸的 LTC3642IMS8E-5 3.3V、50mA 降压型稳压器的典型应用电路
- DER-998 - 使用 LinkSwitch-XT2SR 的 12 W 或 6 W 可选输出电压电器电源
- Arduino UNO
- LTC6256 的典型应用 - 双路 6.5MHz 高功率高效轨至轨 I/O 运算放大器
- 下载有礼|是德科技电子书 《通过了解测试精度的基础知识, 提高良率并降低风险》,不做“差不多先生”
- 7月21日有奖直播|ADI开关/多路复用器系列产品的典型应用
- 4小时实战+剖析:TI工程师教你快速上手 各种无线产品开发 (限量$14售CC1352R1无线开发板,助你参与动手实验)
- 有奖直播:低功耗、小尺寸&高温环境、带触摸功能——瑞萨电子最新16位RL78/G系列单片机介绍
- 免费测评|ESP32-S2-Kaluga-1新型多媒体开发板,灵活拆装,满足多种需求
- 电机小课堂 | 单相无刷电机无感控制,小功率电机驱动应用中的最佳解决方案
- 如何在FPGA设计环境中加时序约束
- 直播已结束【基于迈来芯第二代位置传感器优化设计的新一代产品】