我们前面讲的不是平台总线。
只是设备驱动模型。他只是一个利用总线的驱动模型,而平台总线又再这个基础上扩展了一些接口。
平台总线里面的总线不需要创建内核已经创建好了(bus/platform)platform:平台
1, bus
platform_bus:不需要自己创建,开机的时候自动创建
struct bus_type platform_bus_type = {
.name = “platform”,
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
匹配方法:
1,优先匹配pdriver中的id_table,里面包含了支持不同的平台的名字,
:drives里面有一个注册表,代表这个驱动可以匹配的平台名字,一个驱动可以匹配很多平台设备
2,直接匹配driver中名字和device中名字(如果不用id_table就用这个匹配方法)
2、device
设备模型主要存储的设备的信息,它与之前的设备驱动模型扩展了一个
struct resource *resource; // 资源:包括了一个设备的地址和中断
这个资源主要存储着设备的信息。
—————————————示例代码——————-——————
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <plat/irqs.h>
#define GPIO_BASE 0x11000000
/*LED 3*/
#define GPX1CON GPIO_BASE + 0x0C20
#define GPX1_SIZE 24
/*LED 2*/
#define GPX2CON GPIO_BASE + 0x0C40
#define GPX2_SIZE 24
/*LED 4 ,5*/
#define GPF3CON GPIO_BASE + 0x01E0
#define GPF3_SIZE 24
/* RESOURCE结构体成员
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
*/
struct resource led_res[] = {
/*地址*/
[0] = {
.start = GPX1CON,
.end = GPX1CON + GPX1_SIZE - 1, //起始地址为0所以要减一
.flags = IORESOURCE_MEM,
},
[1] = {
.start = GPX2CON,
.end = GPX2CON + GPX2_SIZE - 1,
.flags = IORESOURCE_MEM,
},
[2] = {
.start = GPF3CON,
.end = GPF3CON + GPF3_SIZE -1,
.flags = IORESOURCE_MEM,
},
/*中断*/
[3] = {
.start = 57, //57号中断
.end = 57,
.flags = IORESOURCE_IRQ,},
};
struct platform_device dev_led = {
.name = "exynos4412_led",
.id = -1,
.num_resources = ARRAY_SIZE(led_res),
.resource = led_res,
};
static int __init platf_led_dev_init(void){
//注册设备结构体
return platform_device_register(&dev_led);
}
static void __exit platf_led_dev_exit(void){
platform_device_unregister(&dev_led);
}
module_init(platf_led_dev_init);
module_exit(platf_led_dev_exit);
MODULE_LICENSE("GPL");
—————————————————————————————————————
3.drives
前面也抛出了,申请设备号,设备节点,文件接口,地址映射等问题,
这里我们是在一个probe编写:这个接口会在匹配成功之后自动调用。
这里主要实现probe,和文件接口,
注意,匹配成功会执行probe。会去申请这些资源,而我们随便哪一个模块被卸载都会断开匹配,然后调用release里面的,所以我们要在release释放我们在probe中申请的资源。
通过probe的参数,系统自动把dev的资源传到drv里面使用。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
struct samsung_drv {
unsigned int drvmaj;
struct class *drvcls;
struct device *drvdev;
struct resource *drvres;
void *reg_base;
};
struct samsung_drv *led_drv;
ssize_t samsung_leddrv_read (struct file *filp, char __user *buf, size_t count , loff_t *fops){
printk("--------%s--------\n",__FUNCTION__);
return 0;
};
ssize_t samsung_leddrv_write (struct file *filp, const char __user *buf, size_t count, loff_t *fops){
printk("--------%s--------\n",__FUNCTION__);
int val;
int ret;
int i = 3;
ret = copy_from_user(&val, buf, count);
if(ret > 0)
{
printk("copy_from_user error\n");
return -EFAULT;
}
switch(val){
case 0:
writel(readl(led_drv->reg_base + 4) & ~(0x3 << 4) , led_drv->reg_base+4);
break;
case 1:
writel(readl(led_drv->reg_base + 4) | (0x3<<4) , led_drv->reg_base+4);
break;
case 2:
for(;i<0;--i){
writel(readl(led_drv->reg_base + 4) | (0x1<<4) , led_drv->reg_base+4);
mdelay(30);
writel(readl(led_drv->reg_base + 4) & ~(0x3 << 4) , led_drv->reg_base+4);
writel(readl(led_drv->reg_base + 4) | (0x1<<5) , led_drv->reg_base+4);
mdelay(30);
writel(readl(led_drv->reg_base + 4) & ~(0x1 << 5) , led_drv->reg_base+ 4);
}
break;
default:
printk("input error\n");
break;}
return 0;
}
int samsung_leddrv_open (struct inode *inode, struct file *filp){
printk("--------%s--------\n",__FUNCTION__);
return 0;
}
int samsung_leddrv_close (struct inode *inode, struct file *filp){
printk("--------%s--------\n",__FUNCTION__);
return 0;
}
int samsung_leddrv_fasync (int fd, struct file *filp, int on){
printk("--------%s--------\n",__FUNCTION__);
return 0;
}
unsigned int samsung_leddrv_poll(struct file *filp, struct poll_table_struct * pts){
printk("--------%s--------\n",__FUNCTION__);
return 0;
}
const struct file_operations myfops = {
.open = samsung_leddrv_open,
.release = samsung_leddrv_close,
.write = samsung_leddrv_write,
.read = samsung_leddrv_read,
.poll = samsung_leddrv_poll,
.fasync = samsung_leddrv_fasync,
};
int led_drv_probe(struct platform_device *pdev){
printk("---------%s---------\n",__FUNCTION__);
//申请结构体资源,初始化结构体
led_drv = kzalloc(sizeof(struct samsung_drv), GFP_KERNEL);
if(led_drv == NULL){
printk("kzalloc error\n");
return -ENOMEM;
}
//申请设备号
led_drv->drvmaj = register_chrdev(0,"samsung_led", &myfops);
if(led_drv->drvmaj < 0 ){
printk("register_chrdev error\n");
goto err0;
return -EFAULT;
}
//创建设备节点
//创建类
led_drv->drvcls = class_create(THIS_MODULE,"samsung_led_class");
if(led_drv->drvcls == NULL ){
printk("class_create error\n");
goto err1;
return -EFAULT;
}
//创建设备节点
led_drv->drvcls = device_create(led_drv->drvcls,NULL, MKDEV(led_drv->drvmaj , 0), NULL, "SAMSUNG_LED_DRV");
if(led_drv->drvcls == NULL ){
printk("device_create\n");
goto err2;
return -EFAULT;
}
//获取匹配的设备地址信息
led_drv->drvres = platform_get_resource(pdev, IORESOURCE_MEM, 2);
//还可以获取设备中断信息
//地址映射
led_drv->reg_base = ioremap(led_drv->drvres->start,(led_drv->drvres->end - led_drv->drvres->start + 1));
if(led_drv->reg_base == NULL ){
printk("ioremap\n");
goto err3;
return -EFAULT;}
//配置寄存器引脚设置为输出功能
writel((readl(led_drv->reg_base) & (~(0xff << 16)) | (0x11 << 16)),led_drv->reg_base);
printk("end\n");
return 0;
err3:
device_destroy(led_drv->drvcls, NULL);
err2:
class_destroy(led_drv->drvcls);
err1:
unregister_chrdev(led_drv->drvmaj,"samsung_led");
err0:
kfree(led_drv);
}
int led_drv_remove(struct platform_device *pdev){
printk("---------%s---------\n",__FUNCTION__);
device_destroy(led_drv->drvcls, NULL);
class_destroy(led_drv->drvcls);
unregister_chrdev(led_drv->drvmaj,"samsung_led");
kfree(led_drv);
return 0;
}
const struct platform_device_id led_id_table[] = { //匹配表___表示这个驱动可以和哪些平台的设备匹配(名字得一样)
{"exynos4412_led",0x1111},
{"s5pv210_led",0x2222},
{"sac2410led",0x33333},
};
struct platform_driver led_pdrv = {
.probe = led_drv_probe,
.remove = led_drv_remove,
.driver = {
.name = "samsung_led_drv", //如果不使用id_tabla则这里的name必须和dev的name一致才能匹配
//如果有id_table这个名字的作用为 在/sys/bus/platform/drives/下挂载的驱动名称
},
.id_table = led_id_table, //优先使用ID_TABLE做匹配
};
static int __init platf_led_drv_init(void){
return platform_driver_register(&led_pdrv);
}
static void __exit platf_led_drv_exit(void){
platform_driver_unregister(&led_pdrv);
};
module_init(platf_led_drv_init);
module_exit(platf_led_drv_exit);
MODULE_LICENSE("GPL");