简介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子系统分析与应用
推荐阅读






推荐帖子
- 美国CDE电容模块在缓冲电路中的应用
- 美国CDE电容模块在缓冲电路中的应用众所周知,在电力电子器件的应用电路中,无一例外地都要设置缓冲电路,即吸收电路。一些初次应用全控型器件的读者或许有这样的感受:器件莫名其妙地损坏了。虽然损坏的原因颇多,但缓冲电路和缓冲电容选择不当是不可忽略的重要原因。器件损坏,不外乎是器件在开关过程中遭受了过量di/dt、dv/dt或瞬时功耗的危害而造成的。缓冲电路的作用,就是改变器件的开关轨迹,控制各种瞬态过电压,降低器件开关损耗,保护器件安全运行。图1所示为GTR驱动感性负载时的开关波形。不难看出,在
-
zbz0529
分立器件
- 有关芯片散热的两个参数
- ThermalResistanceJunction-case, ThermalResistanceJunction-ambient, 请教下电源芯片手册中,以上两个参数有什么区别。 谢谢!!~有关芯片散热的两个参数
-
burton
电源技术
- 通过串口下载Vxworks Image
- 我想通过串口下载镜像文件到目标机,config,h文件配置如下: #defineDEFAULT_BOOT_LINE"tsfs(0,0)PC-12032051:/vxWorks 主机上只有一个串口,所以这就把打印信息输出到屏幕了,#defineINCLUDE_PC_CONSOLE #undefCONSOLE_TTY #defineCONSOLE_TTYNONE #undefWDB_TTY_CHANNEL #defineWDB_TTY_CHANN
-
gyudi
实时操作系统RTOS
- [新手求助]matlab音频处理
- 在用matlab进行音频处理是,通过X(2)=0,让频谱中一个特定的频率强度为0后,sound无法播出任何声音 这是为什么? [x,fs,bits]=wavread(\'c:\\Users\\xpfab\\Desktop\\icerain.wav\'); X=fft(x); n=length(x); X(2)=0 magX=abs(X); xnew=ifft(X); sound(xnew,fs,bits); [新手求助]matlab音频处理
-
dreambutterfly
DSP 与 ARM 处理器
- 请各位大虾 帮帮忙~~~关于单片机的问题~~谢谢
- 我现在是一名学生刚接触单片机的编程,遇到了点问题,如下: 我想制作一个跑马灯,有八个灯泡,主芯片是8051,我想让这个跑马灯,跑起来,而且每个灯每秒亮五次,然后换下一个灯···希望大家帮帮忙,编程请用C51程序,谢谢大虾门了,请帮帮我这个菜鸟,我们说清楚的请回帖告诉我我会详细的再说一下的··谢谢谢···很急啊~~请各位大虾帮帮忙~~~关于单片机的问题~~谢谢
-
美女吧是
嵌入式系统
- 坛子里谁搞机顶盒研发?
- 如题,哪位朋友搞机顶盒研发呢? 有个朋友在寻找,我帮忙在这里问问坛子里谁搞机顶盒研发?
-
soso
移动便携