1、简介
Linux内核中常见的的总线有I2C总线,PCI总线,串口总线,SPI总线,PCI总线,CAN总线,单总线等,所以有些设备和驱动就可以挂在这些总线上,然后通过总线上的match进行设备和驱动的匹配。但是有的设备并不属于这些常见总线,所以我们引入了一种虚拟总线,也就是platform总线的概念,对应的设备叫做platform设备,对应的驱动叫做platform驱动。当然引入platform的概念,可以做的与板子相关的代码和驱动的代码分离,使得驱动有更好的可扩展性和跨平台性。
总线上的每个device和driver都有一个name,他们的匹配就是通过name实现的
1.1 主要数据结构
struct platform_device {
const char *name; //资源名字
int id; //一般写0或-1
struct device dev; //内嵌设备
u32 num_resources; //资源大小
struct resource *resource; //资源结构体
const struct platform_device_id *id_entry;
struct pdev_archdata archdata;
};
struct resource {
resource_size_t start; //资源起始的物理地址
resource_size_t end; //资源结束的物理地址
const char *name;
unsigned long flags; //资源类型
struct resource *parent, *sibling, *child;
};
//flags标号有IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA四种选择,常用的是申请内存(IORESOURCE_MEM)和申请中断号(IORESOURCE_IRQ)
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 (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
struct device_driver {
struct module *owner; //填写THIS_MODULE
const char *name; //驱动名字
};
platform_device和platform_driver都定义在include\linux\platform_device.h
1.2 主要方法
//注册platform device到platform_bus
int platform_device_register(struct platform_device *pdev)
参数:填充好的platform_device
返回值:成功返回0.
//注销platform device
void platform_device_unregister(struct platform_device *pdev)
//注册platform driver到platform_bus
int platform_driver_register(struct platform_driver *drv)
参数drv:填充好的platform_driver
//注销platform driver
void platform_driver_unregister(struct platform_driver *drv)
//设备驱动端获取platform device
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)
参数dev: 内核传过来platform_device的指针
参数type:资源类型,与device的flag对应
参数unm: 同类资源序号
1.3 资源的读取
对于资源中的存储空间的资源读取,首先读取资源,然后申请空间,最后完成由虚拟地址到物理地址的映射。具体函数如下
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct resource *buttons_mem = request_mem_region(res->start, res->end-res->start+1, pdev->name);
void __iomem *buttons_base = ioremap(res->start, res->end - res->start + 1);
对于中断资源的读取,只要一步如下操作即可。
struct resource *buttons_irq1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
2、demo
device.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
//声明资源
struct resource demo_resource[] = {
[0] = {
.start = IRQ_EINT(19),
.end = IRQ_EINT(19),
.flags = IORESOURCE_IRQ,
}
};
//一些注销操作
static void platform_demo_release(struct device *dev)
{
printk("%s\n", __func__);
return ;
}
struct platform_device device_demo = {
//name必须和driver中platform_device中的name相同
.name = "platform_demo",
.id = -1,
.num_resources = ARRAY_SIZE(demo_resource),
.resource = demo_resource,
.dev = {
.release = platform_demo_release,
}
};
static int __init my_device_init(void)
{
int ret = 0;
//注册设备模块总线
ret = platform_device_register(&device_demo);
if (ret){
printk("platform_device_register failed\n");
goto out;
}
return 0;
out:
return ret;
}
static void __exit my_device_exit(void)
{
platform_device_unregister(&device_demo);
return ;
}
module_init(my_device_init);
module_exit(my_device_exit);
MODULE_LICENSE("Dual BSD/GPL");
driver.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#define DEMO_DEBUG
#ifdef DEMO_DEBUG
#define dem_dbg(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
#else
#define dem_dbg(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#endif
static int demo_open (struct inode *pnode, struct file *filp)
{
dem_dbg("==>%s major: %d minor: %d\n", __FUNCTION__, imajor(pnode), iminor(pnode));
return 0;
}
static ssize_t demo_read (struct file *filp, char __user *buf, size_t count, loff_t *offp)
{
int key, retval;
unsigned long len = min(count, sizeof(key));
if(copy_to_user(buf, &key, len) != 0){
retval = -EFAULT;
goto cp_err;
}
return len;
cp_err:
return retval;
}
static int demo_release (struct inode *pnode, struct file *filp)
{
dem_dbg("==>%s major: %d minor: %d\n",
__FUNCTION__, imajor(pnode), iminor(pnode));
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = demo_read,
.open = demo_open,
.release = demo_release,
};
static struct miscdevice demo_misc = {
.minor = MISC_DYNAMIC_MINOR,
.fops = &fops,
.name = "demo0",
};
//2 匹配设备成功
static int demo_probe(struct platform_device *pdev)
{
int retval;
struct resource *res = NULL;
dem_dbg("==>%s\n", __FUNCTION__);
//3 获取platform_device 资源 0即是IORESOURCE_IRQ类型的第0个数据
//res即是返回的中断地址
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dem_dbg("==>no memory resource specified\n");
return PTR_ERR(res);
}
//得到资源后做具体的初始化操作
//4 注册驱动
retval = misc_register(&demo_misc);
if(retval < 0){
dem_dbg("==>register misc device failed!\n");
goto misc_err;
}
return 0;
misc_err:
return retval;
}
//6 具体的注销操作
static int demo_remove(struct platform_device *dev)
{
dem_dbg("==>demo_remove\n");
misc_deregister(&demo_misc);
return 0;
}
static struct platform_driver demo_driver = {
//匹配到设备就会调用
.probe = demo_probe,
.remove = demo_remove,
.driver = {
.owner = THIS_MODULE,
//名字必须和device中platform_device中的name相同
.name = "platform_demo",
},
};
static int __init my_driver_init(void)
{
//1 注册驱动模块总线
return platform_driver_register(&demo_driver);
}
static void __exit my_driver_exit(void)
{
//5 注销驱动模块总线
platform_driver_unregister(&demo_driver);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("Dual BSD/GPL");
Makefile
ifneq ($(KERNELRELEASE),)
obj-m +=driver.o
obj-m +=device.o
else
KERNELDIR = /home/ms/iTop4412_Kernel_3.0
#记录当前工程目录
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
@rm -rf *.o .t* .m* .*.cmd *.mod.c *.order *.symvers
endif
clean:
rm -rf *.ko *.o .t* .m* .*.cmd *.mod.c *.order *.symvers