Linux platform子系统【3】-platform设备驱动应用实例解析


前言

  1. 前提文章
    platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动
    Linux 设备树子系统【2】-设备树在platform设备驱动中的使用
    Linux I2C子系统【1】-I2C驱动分析-设备树创建platform_device

下面将通过一个实例来深入我们的学习

一、platform 驱动的工作过程

platform模型驱动编程,需要实现platform_device(设备)与platform_driver(驱动)在platform(虚拟总线)上的注册、匹配,相互绑定,然后再做为一个普通的字符设备进行相应的应用,总之如果编写的是基于字符设备的platform驱动,在遵循并实现platform总线上驱动与设备的特定接口的情况下,最核心的还是字符设备的核心结构:cdev、 file_operations(他包含的操作函数接口)、dev_t(设备号)、设备文件(/dev)等,因为用platform机制编写的字符驱动,它的本质是字符驱动

我们要记住,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳。

在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中。那么我们编写platform模型驱动时,需要完成两个工作:

a – 实现platform驱动

b – 实现platform设备

在这里插入图片描述

1.1那具体platform驱动的工作过程是什么呢:

设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;

如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;

如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。

二、实现platform 驱动与设备的详细过程

  1. 在分析platform 之前,可以先思考一下下面的问题:

    a – 为什么要用 platform 驱动?不用platform驱动可以吗?
    b – 设备驱动中引入platform 概念有什么好处?

现在先不回答,看完下面的分析就明白了,后面会附上总结。

  1. platform_device 结构体 VS platform_driver 结构体
设备(硬件部分):中断号,寄存器,DMA等
platform_device 结构体
驱动(软件部分)platform_driver 结构体
struct platform_device {
const char *name; 名字
int id;
bool id_auto;
struct device dev; 硬件模块必须包含该结构体
u32 num_resources; 资源个数
struct resource resource; 资源 人脉
const struct platform_device_id * id_entry;
/
arch specific additions */
struct pdev_archdata archdata;
};
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; 八字
};
设备实例:
static struct platform_device hello_device=
{
.name = “bigbang”,
.id = -1,
.dev.release = hello_release,
};
驱动实例:
static struct platform_driver hello_driver=
{
.driver.name = “bigbang”,
.probe = hello_probe,
.remove = hello_remove,
};

3、设备资源结构体
struct platform_device 结构体中有一重要成员 struct resource *resource

struct resource {
	resource_size_t start;		/*资源起始地址*/
	resource_size_t end;		/*资源结束地址*/
	const char *name;
	unsigned long flags;		/*区分是资源的什么类型*/
	struct resource *parent, *sibling, *child;
};
#define IORESOURCE_MEM        0x00000200
#define IORESOURCE_IRQ        0x00000400   

flags指资源类型,我们常用的是 IORESOURCE_MEM,IORESOURCE_IRQ 这两种。startend的含义会随着flags而变化,如
a–flagsIORESOURCE_MEM,start,end分别表示该platform_device占据的内存的开始地址和结束地址。
b–flagsIORESOURCE_IRQ 时,startend 分别表示该platform_device使用的中断号的开始地址和结束值;

static struct  resource beep_resource[] =
{
	[0] = {
        	.start = 0x114000a0,
			.end = 0x114000a0+0x4,
        	.flags = IORESOURCE_MEM,
	},
 
	[1] = {
        	.start = 0x139D0000,
        	.end = 0x139D0000+0x14,
        	.flags = IORESOURCE_MEM,
	},
};
  1. 解决问题

    现在可以回答这两个问题了
    a – 为什么要用 platform 驱动?不用platform驱动可以吗?
    b – 设备驱动中引入platform 概念有什么好处?

    引入platform模型符合Linux 设备模型 —— 总线、设备、驱动,设备模型中配套的sysfs节点都可以用,方便我们的开发;当然你也可以选择不用,不过就失去了一些platform带来的便利;

    设备驱动中引入platform 概念,隔离BSP和驱动。在BSP中定义platform设备和设备使用的资源、设备的具体匹配信息,而在驱动中,只需要通过API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。

二、实例

这是一个蜂鸣器的驱动,其实前面已经有解析 Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动, 下面来看一下,套上platform 外壳后的程序:

  1. device.c

    #include <linux/module.h>
    #include <linux/device.h>
    #include <linux/platform_device.h>
    #include <linux/ioport.h>
    
    static struct resource beep_resource[] = 
    {
    	[0] = {
    		.start = 0x114000a0,
    		.end = 0x114000a0 + 0x4,
    		.flag = IORESOURCE_MEM,
    	},
    	[1] = {
    		.start = 0x139D0000,
    		.end = 0x139D0000 + 0x14,
    		.flag = IORESOURCE_MEM,
    	},
    };
    
    static void hello_release(struct device *dev)
    {
    	printk("hello_release");
    	return;
    }
    
    static struct platform_device hello_device = 
    {
    	.name = "bigbang",
    	.id = -1,
    	.dev.release = hello_release,
    	.num_resources = ARRAY_SIZE(beep_resource),
    	.resource = beep_resource,
    };
    
    static int hello_init(void)
    {
    	printk("hello_init");
    	return platform_device_register(&hello_device);
    }
    
    static void hello_exit(void)
    {
    	printk("hello_exit");
    	platform_device_unregister(&hello_device);
    	return;
    }
     
    MODULE_LICENSE("GPL");
    module_init(hello_init);
    module_exit(hello_exit);
    
    1. driver.c
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/platform_device.h>
    #include <asm/io.h>
    static int major = 250;
    static int monor = 0;
    static dev_t devno;
    static struct class *cls;
    static struct device *test_device;
    #define TCFG0         0x0000               
    #define TCFG1         0x0004                            
    #define TCON          0x0008             
    #define TCNTB0        0x000C          
    #define TCMPB0        0x0010
    
    static unsigned int *gpd0con;
    static void *timer_base;
    
    #define MAGIC_NUMBER 'k'
    #define BEEP_ON 	_IO(MAGIC_NUMBER ,0)
    #define BEEP_OFF 	_IO(MAGIC_NUMBER ,1)
    #define BEEP_FREQ 	_IO(MAGIC_NUMBER ,2)
    static void fs4412_beep_init(void)
    {	
    	writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);
    	writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0); 
    	writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 ); 
     
    	writel (500, timer_base +TCNTB0  );
    	writel (250, timer_base +TCMPB0 );
    	writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON ); 
    }
    
    void fs4412_beep_on(void)
    {
    	writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );
    }
    
    void fs4412_beep_off(void)
    {
    	writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );
    }
     
    static void beep_unmap(void)
    {
    		iounmap(gpd0con);
    		iounmap(timer_base);
    }
    static int beep_open (struct inode *inode, struct file *filep)
    {
    	fs4412_beep_on();
    	return 0;
    }
    static int beep_release(struct inode *inode, struct file *filep)
    {
    	 fs4412_beep_off();
    	 return 0;
    }
    
    #define BEPP_IN_FREQ 100000
    static void beep_freq(unsigned long arg)
    {
    	writel(BEPP_IN_FREQ/arg, timer_base +TCNTB0  );
    	writel(BEPP_IN_FREQ/(2*arg), timer_base +TCMPB0 );
    }
    
    static long beep_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
    {
    	switch(cmd)
    	{
    		case BEEP_ON:
    			fs4412_beep_on();
    			break;
    		case BEEP_OFF:
    			fs4412_beep_off();
    			break;
    		case BEEP_FREQ:
    			beep_freq( arg );
    			break;
    		default :
    			return -EINVAL;
    	}
    	return 0;
    }
    static struct file_operations beep_ops = 
    {
    	.open = beep_open,
    	.release = beep_release,
    	.unlocked_ioctl = beep_ioctl,
    };
    static int beep_probe(struct platform_device *pdev)
    {
    	int ret;
    	printk("match ok!");
    	gpd0con = ioremap(pdev->resource[0].start,pdev->resource[0].end - pdev->resource[0].start);
    	timer_base = ioremap(pdev->resource[1].start,pdev->resource[1].end - pdev->resource[1].start);
    	
    	devno = MKDEV(major,minor);
    	ret = register_chrdev(major,"beep",&beep_ops);
    	class = class_create(THIS_MODULE,"myclass");
    	if(IS_ERR(cls)){
    		unregister_chrdev(major,"beep");
    		return -EBUSY;
    	}
    	test_device = device_create(cls,NULL,devno,NULL,"beep"); /*mknod /dev/hello*/
    	if(IS_ERR(test_device)){
    		class_destroy(cls);
    		unregister_chrdev(major,"beep");
    		return -EBUSY;
    	}
    	fs4412_beep_init();
    	return 0;
    }
    
    static int beep_remove(struct platform_device *pdev)
    {
    	beep_unmap();
    	device_destroy(cls,devno);
    	class_destroy(cls);
    	unregister_chrdev(major,"beep");
    	return 0;
    }
    
    static struct platform_driver beep_driver = 
    {
    	.driver.name = "bigbang",
    	.probe = beep_probe,
    	.remove = beep_remove,
    };
    static int beep_init(void)
    {
    	printk("beep_init");
    	return platform_driver_register(&beep_driver);
    }
    
    static void beep_exit(void)
    {
    	printk("beep_exit");
    	platform_driver_unregister(&beep_driver);
    	return;
    }
    
    MODULE_LICENSE("GPL");
    module_init(beep_init);
    moduel_exit(beep_exit);
    
    1. test.c
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
     
    main()
    {
    	int fd,i,lednum;
     
    	fd = open("/dev/beep",O_RDWR);
    	if(fd<0)
    	{
    		perror("open fail \n");
    		return ;
    	}
    	
    	sleep(10);
    	close(fd);
    }
    

参考

Linux 设备驱动开发 —— platform设备驱动应用实例解析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值