一、概念
platform是一个驱动管理模型,它不是从设计驱动的角度考虑,而是从管理驱动的角度考虑
- platform device:描述硬件信息的部分(硬件不通,信息或资源不同)。
- platform driver:描述软件信息的部分(对硬件资源的操作,保持相对稳定,较少修改)。
- platform bus:是platform driver和platform device的管理者(管理着两条链表)。
二、platform模型的优点
- 方便驱动的移植(争取做到移植时,只修改device,而driver部分基本不变)。
- 内核中绝大部分设备驱动都是基于platform驱动模型的,,方便分析内核驱动源码,更便于理解驱动程序的框架。
- 当platform driver和platform device匹配时,调用driver->probe函数
- platform driver和platform device一般在不同的源文件中。
三、platform dirver实现步骤
1、platform driver的结构体
struct platform_driver {
int (*probe)(struct platform_device *);-----在platform device 和platform driver 的driver的name匹配时会调用该函数
int (*remove)(struct platform_device *);----是probe的反函数
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
2、platform driver的注册
int platform_driver_register(struct platform_driver *drv)
3、platform driver的注销
void platform_driver_unregister(struct platform_driver *drv)
4、资源的获取
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
功能:获取platform device的资源,
最常用的资源类型有下面几种:
#define IORESOURCE_IO 0x00000100 //GPIO资源
#define IORESOURCE_MEM 0x00000200 //IO内存资源
#define IORESOURCE_IRQ 0x00000400 //中断号资源
#define IORESOURCE_DMA 0x00000800 //DMA资源
返回值:成功返回有效的资源结构体地址,失败返回NULL
四、platform模型驱动实例
以内核源码看门狗驱动为例
1、plafrom device
(1)注册platform device
大部分的platform device都会在nxp_cpu_devs_register中使用platform_device_register进行注册
#if defined(CONFIG_NXP_WDT)
printk("mach: add device watchdog\n");
platform_device_register(&nxp_device_wdt);
#endif
(2) platform结构体
struct platform_device nxp_device_wdt = {
.name = DEV_NAME_WDT, //设备名,资源通过设备名和platform driver匹配,匹配成功后会调用驱动中的probe进行初始化
.id = -1,
.num_resources = ARRAY_SIZE(nxp_wdt_resource),
.resource = nxp_wdt_resource, //看门狗的资源
};
(3) resource
static struct resource nxp_wdt_resource[] = {
[0] = DEFINE_RES_MEM(PHY_BASEADDR_WDT, SZ_1K), //内存资源
[1] = DEFINE_RES_IRQ(IRQ_PHY_WDT), //中断资源
};
(4)platform device的注销
void platform_device_unregister(struct platform_device *pdev)
2、platform driver
nxp_wdt.c (drivers\watchdog)
static struct platform_driver nxp_wdt_driver = {-------platform driver
.probe = nxp_wdt_probe,-----------------基于某个驱动模型的初始化函数
.remove = __devexit_p(nxp_wdt_remove),----相当于probe函数的反函数
.shutdown = nxp_wdt_shutdown,
.suspend = nxp_wdt_suspend,
.resume = nxp_wdt_resume,
.driver = {
.owner = THIS_MODULE,
.name = DEV_NAME_WDT,-----------------用于注册时跟device的name比较
.of_match_table = nxp_wdt_match,
},
};
static int __init watchdog_init(void)
{
pr_info("NXP Watchdog Timer, (c) 2014 SLsiAP\n");
return platform_driver_register(&nxp_wdt_driver);----------注册platform driver
}
static void __exit watchdog_exit(void)
{
platform_driver_unregister(&nxp_wdt_driver);-------注销platform driver
}
static int __devinit nxp_wdt_probe(struct platform_device *pdev)
{
1.获取platform device 的资源(IO内存资源、中断号资源)
2.申请IO内存
3.获取/使能时钟
4.具体硬件相关的操作,停止看门狗、设置定时时间
5.注册看门狗中断
6.其它的资源操作的代码
}
五、platform实例
使用platform模型实现蜂鸣器打开和关闭。
1、beep_dev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/ioctl.h>
#include <mach/platform.h>
#include <mach/soc.h>
#include <mach/devices.h>
static void beep_release(struct device *dev)
{
printk(KERN_INFO"beep_release\n");
}
//platform device拥有的资源
//定义了两种资源类型,可以使用IO口,也可以使用io内存
static struct resource beep_res[] =
{
{
.start =0xC001C000,
.end = 0xC001C000+0x64+3, //加3是因为最后一个寄存器还有四位
.name = "GPIOC_IOMEM",
.flags = IORESOURCE_MEM,
},
{
.start =PAD_GPIO_C+14,
.end = PAD_GPIO_C+14,
.name = "GPIOC_IONUM",
.flags = IORESOURCE_IO,
},
};
static struct platform_device beep_pdev =
{
.name = "beep_pdev_pdrv", //和驱动匹配的设备名
.id = -1,
.num_resources = ARRAY_SIZE(beep_res), //得到的是数组元素的个数
.resource = beep_res,
.dev =
{
.release = beep_release,
},
};
static int __init beep_dev_init(void)
{
printk(KERN_INFO"beep_dev_init\n");
//注册平台设备
platform_device_register(&beep_pdev);
return 0;
}
static void __exit beep_dev_exit(void)
{
//销毁平台设备
printk(KERN_INFO"beep_dev_exit\n");
platform_device_unregister(&beep_pdev);
}
module_init(beep_dev_init);
module_exit(beep_dev_exit);
MODULE_AUTHOR("yqf");
MODULE_DESCRIPTION("beep device program");
MODULE_LICENSE("GPL");
2、beep_drv.c
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/types.h> #include <linux/ioctl.h> #include <linux/miscdevice.h> #include <linux/platform_device.h> static struct resource *beep_mem_res = NULL; static void __iomem *GPIOCBASE = NULL; static void __iomem *GPIOCOUT = NULL; static void __iomem *GPIOCOUTENB= NULL; static void __iomem *GPIOCALTFN0 = NULL; static void __iomem *GPIOCALTFN1 = NULL; #define BEEP_ON _IO('B',0) #define BEEP_OFF _IO('B',1) static int beep_open(struct inode* inode,struct file *filp) { printk(KERN_INFO"beep_open\n"); return 0; } static int beep_close(struct inode* inode,struct file *filp) { printk(KERN_INFO"beep_close\n"); return 0; } static ssize_t beep_write(struct file *filp, const char __user *user, size_t size, loff_t *oft) { printk(KERN_INFO"beep_write\n"); return size; } static long beep_ioctl (struct file *filp, unsigned int cmd, unsigned long arg) { switch(cmd) { case BEEP_ON: writel(readl(GPIOCOUT)|((0x01<<14)),GPIOCOUT); break; case BEEP_OFF: writel(readl(GPIOCOUT)&(~(0x01<<14)),GPIOCOUT); default: return -ENOIOCTLCMD; } return 0; } struct file_operations beep_fops= { .open = beep_open, .release = beep_close, .write = beep_write, .unlocked_ioctl = beep_ioctl, }; static struct miscdevice beep_misc= { .minor = MISC_DYNAMIC_MINOR, .name = "beep_misc", .fops = &beep_fops, }; static int beep_probe(struct platform_device *pdev) { int ret; printk(KERN_INFO"beep_probe\n"); misc_register(&beep_misc); if(ret < 0) { printk(KERN_INFO"beep misc register fail\n"); goto misc_register_err; } //获取IO内存资源 beep_mem_res = platform_get_resource(pdev,IORESOURCE_MEM,0); if(beep_mem_res==NULL) { printk(KERN_INFO"platform_get_resource failed\n"); ret = -ENODEV; goto platform_get_resource_err; } //映射到虚拟地址 GPIOCBASE = ioremap(beep_mem_res->start,beep_mem_res->end-beep_mem_res->start+1); if(GPIOCBASE==NULL) { printk(KERN_INFO"ioremap failed"); ret = -EBUSY; goto ioremap_err; } GPIOCOUT = GPIOCBASE+0x00; GPIOCOUTENB = GPIOCBASE+0x04; GPIOCALTFN0 = GPIOCBASE+0x20; GPIOCALTFN1 = GPIOCBASE+0x24; //beep writel(readl(GPIOCOUTENB)|(0x01<<14),GPIOCOUTENB); writel(readl(GPIOCALTFN0) &(~(0x03<<28)),GPIOCALTFN0); writel(readl(GPIOCALTFN0) |(1<<28),GPIOCALTFN0); writel(readl(GPIOCOUT)&(~(0x01<<14)),GPIOCOUT); return 0; ioremap_err: release_mem_region(beep_mem_res->start,beep_mem_res->end-beep_mem_res->start+1); platform_get_resource_err: misc_deregister(&beep_misc); misc_register_err: return 0; } static int beep_remove(struct platform_device *pdev) { printk(KERN_INFO"beep_remove\n"); iounmap(GPIOCBASE); release_mem_region(beep_mem_res->start,beep_mem_res->end-beep_mem_res->start+1); misc_deregister(&beep_misc); return 0; } static struct platform_driver beep_drv={ .probe = beep_probe, .remove = beep_remove, .driver ={ .owner = THIS_MODULE, .name = "beep_pdev_pdrv", }, }; static int __init beep_init(void) { printk(KERN_INFO"beep_init\n"); platform_driver_register(&beep_drv); return 0; } static void __exit beep_exit(void) { printk(KERN_INFO"beep_exit\n"); platform_driver_unregister(&beep_drv); } module_init(beep_init); module_exit(beep_exit); MODULE_AUTHOR("yqf"); MODULE_DESCRIPTION("beep platform driver"); MODULE_LICENSE("GPL");
3、main.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#define BEEP_ON _IO('B',0)
#define BEEP_OFF _IO('B',1)
int main()
{
int fd;
char buff[2]={0};
fd = open("/dev/beep_misc",O_RDWR);
if(fd<0)
{
perror("open bepp misc error!");
}
while(1)
{
ioctl(fd,BEEP_ON);
sleep(1);
ioctl(fd,BEEP_OFF);
sleep(1);
}
close(fd);
}