自学linux驱动从入门到放弃(九)linux字符设备platform总线改造

        上一个笔记里面,简单的记录了一下字符设备的注册。后面自己通过配置rk3288的gpio,可以实现app控制灯的亮灭,这里一次性的记录一下字符设备点灯,基于platform总线的代码改造。

一.platform总线

1.什么是platform总线?有什么用?直接写字符设备驱动不一样吗?有什么分别吗?

这也是我刚刚听到platform总线的疑惑,也想了半天

1)platform是虚拟出来的总线,实际并不是像I2C SPI一样真实存在的物理总线。

2)主要用于分离设备资源和驱动,更便于驱动的移植,硬件资源的调整

3)如果板子上只有一个led资源,那么我们可以通过将硬件资源写死在驱动里面,一样可以跑,那么试想一下,如果有10个led呢,每一个led占用的IO口不一样,那么对应的mem地址就不一样,那么要驱动起来这些led,就需要led1.c,led2.c led3.c.....去写10份驱动,那通过platform总线,首先platform device里面资源的定义很直观,容易修改,那么我完全可以 将led的驱动写成通用的驱动,通过获取device里面定义的资源来进行通用的操作,所以10个led,我可以定义10个platform device,platform driver,只有一份就可以了。

二.platform 结构体与注册

1.platform_device

#include <linux/platform_device.h>

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;
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

1).name

        platform bus device与driver进行匹配的名字,要与platform_driver里面的名字对应,不然匹配不上

2).id

        设备实例号,或者用“-1”表示只有一个实例

3).dev

        这里面我们会用到它的成员.release,没有这个release函数会报错

4)struct resource *resource

        这里不多说了,后面看代码吧

struct resource {
	resource_size_t start;    //资源的起始
	resource_size_t end;        //资源的结束
	const char *name;        //资源的名字
	unsigned long flags;    //资源的类型
	struct resource *parent, *sibling, *child;
};

这里的.flags有以下类型..等...没有都赋值过来,就是看一下

#define IORESOURCE_BITS		0x000000ff	/* Bus-specific bits */

#define IORESOURCE_TYPE_BITS	0x00001f00	/* Resource type */
#define IORESOURCE_IO		0x00000100	/* PCI/ISA I/O ports */
#define IORESOURCE_MEM		0x00000200
#define IORESOURCE_REG		0x00000300	/* Register offsets */
#define IORESOURCE_IRQ		0x00000400
#define IORESOURCE_DMA		0x00000800
#define IORESOURCE_BUS		0x00001000

5).num_resources

        资源的数量,我们一般给它赋值为ARRAY_SIZE(resource)

代码实例

struct resource led_resource =  {
	{
		.start = 0xFF760000+0x198,
		.end   = 0xFF760000+0x198+3,
		.name  = "GPIO8_CLKGATE_CON",
		.flags  = IORESOURCE_MEM
	}

};

struct platform_device led_device = {
	.name = "alon_charDevPlt_test",
	.id = -1,
	.resource =  led_resource,
	.num_resources = ARRAY_SIZE(led_resource),
	.dev = {
		.release = led_release
	}

};

2.platform_driver

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;
	bool prevent_deferred_probe;
};

1).probe

        在platform driver与platform device匹配后,会马上执行probe,所以可以将上一份代码里面类的注册,设备号申请,设备注册的代码放在这里,同时结合硬件资源,这里会对硬件进行初始化等。

2).driver

        这里我们只用到了它的两个成员.name .owner

struct device_driver {
	const char		*name;
    struct module		*owner;
	...
};

这里的.name与platform device里面的.name要匹配.owner = THIS_MODULE

代码示例

struct platform_driver led_driver = {
	.probe = led_probe,
	.remove = led_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "alon_charDevPlt_test"
	}
};

3.platform注册

        platform device注册/反注册

platform_device_register(drv)
platform_device_unregister(drv)

        platform driver注册/反注册

platform_driver_register(drv)
platform_driver_unregister(drv)

platform总线的注册,也是通过模块加载的方式去注册,只需要在init/exit里面调用对应的platform注册/反注册函数就可以了

代码示例

platformDevice.c


#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>


struct resource led_resource[] =  {
	[0]={
		.start = 0xFF760000+0x198,            //GPIO8 CLK GATE 寄存器地址
		.end   = 0xFF760000+0x198+3,
		.name  = "GPIO8_CLKGATE_CON",
		.flags  = IORESOURCE_MEM
	},
	[1]={
		.start = 0xFF7F0000+0x000,        // GPIO8 数据寄存器地址
		.end   = 0xFF7F0000+0x000+3,
		.name  = "GPIO8_PORT_DR",
		.flags  = IORESOURCE_MEM		
	},
	[2]={
		.start = 0xFF770000+0x080,        //GPIO8 复用寄存器地址
		.end   = 0xFF770000+0x080+3,
		.name  = "GRF_GPIO8A_IOMUX",
		.flags  = IORESOURCE_MEM	
	},
	[3]={
		.start = 0xFF7F0000+0x004,        //GPIO8 input output控制寄存器地址
		.end   = 0xFF7F0000+0x004+3,
		.name  = "GPIO_PORT_DDR",
		.flags  = IORESOURCE_MEM		
	}
};

void	led_release(struct device *dev){    //platform_device的成员.dev的成员,没有编译会有警告,必须有,可以什么都不做
	printk("led release \n");
}

struct platform_device led_device = {    //platform_device结构体
	.name = "alon_charDevPlt_test",    //driver名字
	.id = -1,
	.resource =  led_resource,            //硬件资源
	.num_resources = ARRAY_SIZE(led_resource),    //资源大小
	.dev = {
		.release = led_release
	}

};


static int platformDeviceTest_init(void)        //模块init入口
{
	platform_device_register(&led_device);        //注册platform_driver
	return 0;
}

static void platformDeviceTest_exit(void){    //模块exit出口
	platform_device_unregister(&led_device);    //反注册platform_driver
}

module_init(platformDeviceTest_init);
module_exit(platformDeviceTest_exit);
MODULE_LICENSE("GPL");





platform_drvier.c

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
#include <linux/io.h>

int inputPara=0;

int dev_major;
int dev_minor=0;
char *chardev_device_name="aloncharDevice";	//设备驱动名字
struct class *class;
char *chardev_class_name="aloncharClass";	//类名字
char *charDev_node_name="aloncharTest";		//设备节点名字

module_param(inputPara, int, S_IRUSR);		//insmod的时候传入的参数,参数名字inputPara,类型int,S_IRUSR是权限

/*
static volatile unsigned int *GPIO8_CLKGATE_CON;    //这里是裸驱动的时候定义的地址指针,改造成platform 总线后就给改成下面的了
static volatile unsigned int *GPIO_PORT_DR;
static volatile unsigned int *GPIO_PORT_DDR;
static volatile unsigned int *GRF_GPIO8A_IOMUX;
*/
static volatile unsigned int *led_reg_vir_addr[4];    //指针数组,存储GPIO8 4个控制寄存器地址

enum ledctlreg{                    //寄存器地址的顺序,对应platform_device里面的资源的顺序,这里是方便获取资源定义的,顺序要和resource对应上,不然控制的时候就乱了
	GPIO8_CLKGATE_CON = 0,
	GPIO_PORT_DR,
	GRF_GPIO8A_IOMUX,
	GPIO_PORT_DDR
	
};


int led_probe(struct platform_device *pdev){    //device和driver匹配上之后会立刻执行probe

	struct resource *mem_tmp;            //获取的资源指针
	int i;

	for(i = 0;i<4 ;i++)    //因为platform的resource有4个,这里获取4个
	{
		mem_tmp = platform_get_resource(pdev, IORESOURCE_MEM, i);//获取IORESOURCE_MEM类型的第i个资源
		if(mem_tmp == NULL)
		{
			printk(KERN_ERR "get resource error! i = %d\n",i);
			return -EBUSY;		
		}
		led_reg_vir_addr[i] = ioremap(mem_tmp->start, 4);    //获取资源对应的地址后,申请虚拟地址
	}
		
	dev_major = register_chrdev(0,chardev_device_name,&f_op);	//注册设备号,f_op结构体
	if(dev_major < 0)
	{
		printk("can not regist char device!\n");
		return dev_major;
	}
	printk(KERN_ERR "dev_major = %d,chardev device name is %s\n",dev_major,chardev_device_name);
	class = class_create(THIS_MODULE, chardev_class_name);		//注册类
	if(IS_ERR(class))
	{
		printk("can not create charDev class!");
		unregister_chrdev(dev_major,chardev_device_name);
		return PTR_ERR(class);
			
	}
	printk(KERN_ERR "chardev class name is %s\n",chardev_class_name);
	device_create(class, NULL, MKDEV(dev_major,dev_minor), NULL, charDev_node_name);	//注册设备节点
	printk(KERN_ERR "chardev node create ok!\n");
	



	
	return 0;
}


int charDev_open (struct inode *inode, struct file *file){

	*led_reg_vir_addr[GPIO8_CLKGATE_CON] = (1<<(16+8))|(0<<8);	//31:16 write mask :8 gpio8 clk gate,0:enable
	*led_reg_vir_addr[GRF_GPIO8A_IOMUX] = (1<<(16+4))|(~(1<<4));//31:16 write mask :4 0:fuc gpio
	*led_reg_vir_addr[GPIO_PORT_DDR] |= (1<<2);	//gpio8_A2 set output
	printk("open charTest node OK!\n");


	return 0;
}

ssize_t charDev_read (struct file *file, char __user *buf, size_t size, loff_t *ppos){

	return 0;
}

ssize_t charDev_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos){

	int cmdbuf;
	int ret;
	
	ret = copy_from_user(&cmdbuf, buf, 1);
	if(ret < 0)
	{
		printk("access data from user failed!\n");
	}
	if(cmdbuf == 1)
	{
		*led_reg_vir_addr[GPIO_PORT_DR] &= ~(1<<2);	//	0点亮
	}
	else
	{
		*led_reg_vir_addr[GPIO_PORT_DR] |= (1<<2);	//1 熄灭
	}


	return 0;
}





static const struct file_operations f_op = {	//file_operations结构体
	.open = charDev_open,
	.read = charDev_read,
	.write = charDev_write,
	.owner = THIS_MODULE,
}; 



int led_remove(struct platform_device *pdev){


	return 0;
}


struct platform_driver led_driver = {
	.probe = led_probe,
	.remove = led_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "alon_charDevPlt_test"
	}
};


static int charDev_init(void)			//init函数,MODULE_INIT的时候会进行初始化
{										
	

	int ret = 0;
	ret = platform_driver_register(&led_driver);    //在这里注册platform_driver
	if(ret < 0){
		printk(KERN_ERR "platform driver register failed \n");
		return ret;
	}
	printk(KERN_ERR "insmod platform driver\n");

	if(inputPara)			//这里是测试参数是否传入,执行insmod devDrv.ko inputPara=整数(等号前后不能有空格)。
	{
		printk(KERN_ERR "input Param is %d\n",inputPara);
	}
	


	return 0;
}

static void charDev_exit(void)
{
	int i;
	for(i = 0;i<4;i++)
	{
		iounmap(led_reg_vir_addr[i]);
	}
	platform_driver_unregister(&led_driver);
	printk(KERN_ERR "remove platform driver\n");

	device_destroy(class, MKDEV(dev_major,dev_minor));
	printk(KERN_ERR "remove chardev node!\n");
	class_destroy(class);
	
	printk(KERN_ERR "remove chardev class!\n");
	unregister_chrdev(dev_major,chardev_device_name);
	printk(KERN_ERR "remove chardev device!\n");

}



module_init(charDev_init);
module_exit(charDev_exit);
MODULE_LICENSE("GPL");

app代码

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>




int main(int argc, const char ** argv)
{
	int fd;
	char val;
	val = atoi(argv[1]);

	fd = open("/dev/charTest",O_RDWR);
	

	if(fd < 0)
	{
		printf("APP can not open node!\n");
		return 0;
	}
	printf("open /dev/charTest ok!\n");
	
		write(fd, &val, 1);
	
	close(fd);



	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值