06.驱动学习复盘(platform 平台总线模型)

1.平台总线模型也叫 platform 总线模型。是 Linux 内核虚拟出来的一条总线,不是真实的导线。

平台总线模型把原来的驱动 C 文件给分成了两个 C 文件,一个是和硬件相关的 device.c,一个是和驱动相关的 driver.c。

把稳定不变的放在 driver.c 里面,需要变的就放在了 device.c 里面。

2. 平台总线模型的优点:
(1)可以提高代码的重用性

(2)减少重复性代码

3.以平台总线模型设计的驱动,要分别注册 device.c 和 driver.c 。

平台总线是以名字来匹配的,实际上就是字符串比较。

PART ONE.注册 platform 设备

1.平台总线注册一个 device :

device.c 里面写的是硬件资源,这里的硬件资源是指寄存器的地址, 中断号, 时钟等硬件资源。

在 Linux 内核里,我们使用 platform_device 结构体来描述硬件资源。

struct platform_device {
	const char	* name;
	//平台总线进行匹配的时候用到的name,会在/sys/bus/platform/devices下生成
	int		id;
	//设备id,一般填-1
	struct device	dev;
	//内嵌的device结构体,设备通用属性
	u32		num_resources;
	//资源的数量
	struct resource	* resource;
	//device里的硬件资源
};

2. 描述硬件资源使用的结构体:

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

3.常用的资源的类型:

#define IORESOURCE_IO		0x00000100
IO的内存
#define IORESOURCE_MEM		0x00000200
常用于表述一段寄存器的地址,物理内存
#define IORESOURCE_IRQ		0x00000400
常用于表述中断号
#define IORESOURCE_DMA		0x00000800
DMA地址
#define IORESOURCE_BUS		0x00001000
总线号

4.视频中描述的是蜂鸣器用到的GPIO引脚的资源,使用ULL的开发板,到相关的使用手册上找到数据寄存器的地址。只控制了数据寄存器,没有设置电器属性,复用关系,在ULL系统启动时加载了设备树,把一系列的配置都给配好了。如果没有配置的话就要手动配置复用寄存器、电器属性寄存器、方向寄存器、数据寄存器等。

5.注册与注销:

platform_device_register(struct platform_device *pdev);

platform_device_unregister(struct platform_device *pdev);

6. 上代码,上代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

void beep_release(struct device *dev)
{
	printk("beep_release\n");
}

//描述硬件资源使用的结构体
struct resource beep_res[] = {
	[0] = {
		.start = 0x20AC000,
		.end   = 0x20AC003,
		.flags = IORESOURCE_MEM,
		.name  = "GPIO5_DR"
	}
};

struct platform_device beep_device = {
	.name = "beep_test",
	.id = -1,
	.resource = beep_res,
	.num_resources = ARRAY_SIZE(beep_res),
	.dev = {
		.release = beep_release
	}
};

static int __init device_init(void)
{
	printk("device_init\n");
	return	platform_device_register(&beep_device);
}

static void __exit device_exit(void)
{
	printk("device_exit\n");
	platform_device_unregister(&beep_device);
}

module_init(device_init);
module_exit(device_exit);

MODULE_LICENSE("GPL");

PART TWO.注册 platform 驱动

1.编写 driver.c 的思路

首先定义一个 platform_driver 结构体变量,然后去实现结构体中的各个成员变量,当 driver 和 device 匹配成功时,就会执行 probe 函数,重点就在于 probe 函数的编写。

2. platform_driver 结构体:

struct platform_driver {
	int (*probe)(struct platform_device *);
	//当driver和device匹配成功的时候就会执行probe函数
	int (*remove)(struct platform_device *);
	//当driver和device任意一个remove的时候会执行
	void (*shutdown)(struct platform_device *);
	//当设备收到shutdown命令时执行
	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;
    //优先匹配id_table中的name
};

	struct device_driver {
		const char		*name;       //进行匹配设备时候用到的名字
		struct module		*owner;  //THIS_MODULE
	};

	struct platform_device_id {
		char name[PLATFORM_NAME_SIZE];
		kernel_ulong_t driver_data;
	};

3.同样的也是需要注册和注销:

int platform_driver_register(struct platform_driver *drv);

void platform_driver_unregister(struct platform_driver *drv);

4. 来了来了,代码来了:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

int beep_probe (struct platform_device *pdev)
{
	printk("beep_probe\n");
	return 0;
};

int beep_remove (struct platform_device *pdev)
{
	printk("beep_remove\n");
	return 0;
};

const struct platform_device_id beep_id_table = {
	.name = "beep_test"
};

struct platform_driver beep_device = {
	.probe = beep_probe,
	.remove = beep_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "beep_test"
	},
	.id_table = &beep_id_table
};

static int __init beep_driver_init(void)
{
	int ret = 0;

	ret = platform_driver_register(&beep_device);

	if(ret < 0)
	{
		printk("platform_driver_register error\n");
		return ret;
	}
	printk("platform_driver_register ok\n");
	
	return 0;
}

static void __exit beep_driver_exit(void)
{
	printk("hello_exit\n");
	platform_driver_unregister(&beep_device);
}

module_init(beep_driver_init);
module_exit(beep_driver_exit);

MODULE_LICENSE("GPL");

PART THREE.编写 probe 函数

1.编写 probe 函数的思路:
(1)从 device.c 里面获得硬件资源

(2)注册杂项/字符设备,完善 file_operation 结构体,并生成设备节点

2.获得硬件资源的两种方式:

方式一:从device.c里面直接获得硬件资源,不推荐。

	printk("beep_res is %s\n",pdev->resource[0].name);

方式二:使用函数获得,推荐。

	extern struct resource *platform_get_resource
    (struct platform_device *,unsigned int, unsigned int);
    //参数:pdev形参,资源的类型,资源的位置(处在同类资源的第几个)

3.注册之前要先登记到内核:

request_mem_region(start, n, name);
//参数:起始地址,长度,名字

4.代码,代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>

#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>

struct resource *beep_mem;
struct resource *beep_mem_tmp;

unsigned int *vir_pwm_dr;

int beep_open(struct inode * inode, struct file * file)
{
	printk("beep_open\n");

	return 0;
}

int beep_release (struct inode *inode, struct file *file)
{
	printk("beep_release\n");

	return 0;
}

ssize_t beep_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
	char kbuf[64] = "hahaha";
	
	if(copy_to_user(ubuf, kbuf, strlen(kbuf)) != 0)
	{
		printk("copy_to_user error\n");
		return -1;
	}

	return 0;
}

ssize_t beep_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
	char kbuf[64] = {0};
	
	if(copy_from_user(kbuf, ubuf, size) != 0)
	{
		printk("copy_from_user error\n");
		return -1;
	}

	printk("beep_write\n");

	if(kbuf[0] == 1)
		*vir_pwm_dr |= (1<<1);
	else if(kbuf[0] == 0)
		*vir_pwm_dr &= ~(1<<1);
		
	return 0;
}

struct file_operations beep_fops = {
	.owner = THIS_MODULE,
	.open = beep_open,
	.release = beep_release,
	.read = beep_read,
	.write = beep_write
};
	
struct miscdevice beep_dev = {

	.minor = MISC_DYNAMIC_MINOR,
	.name = "beep_drv",
	.fops = &beep_fops
};

int beep_probe (struct platform_device *pdev)
{
	int ret = 0;

	printk("beep_probe\n");
	
	/*方法一:从device.c里面直接获得硬件资源,不推荐*/
	//printk("beep_res is %s\n",pdev->resource[0].name);
	
	/*方法二:使用函数获得*/
	//extern struct resource *platform_get_resource
	//(struct platform_device *,unsigned int, unsigned int);
	
	//形参pdev,资源类型,是同类资源的第几个
	beep_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);

	if(beep_mem == NULL){
		printk("platform_get_resource error\n");
		return -EBUSY;
	}
	printk("platform_get_resource ok\n");

	printk("beep_res start is 0x%x\n",beep_mem->start);
	
	printk("beep_res end is 0x%x\n",beep_mem->end);

#if 0
	//写这部分会登记失败,因为内核已经登记过了,正常是要写
	//登记到内核,起始地址,长度,名字
	beep_mem_tmp = request_mem_region(beep_mem->start,
		beep_mem->end - beep_mem->start + 1,"beep");

	if(beep_mem_tmp == NULL){
		printk("request_mem_region error\n");
		//没有登记成功就要释放掉
		goto err_region;
	};

	return 0;

err_region:

	release_mem_region(beep_mem->start, beep_mem->end - beep_mem->start + 1);

	return -EBUSY;
#endif

/****************************************************/
	//映射虚拟地址(物理地址,要映射的大小)
	vir_pwm_dr = ioremap(beep_mem->start, 4);

	if(vir_pwm_dr == NULL)
	{
		printk("PWM ioremap error\n");
		return -EBUSY;
	}

	printk("PWM ioremap ok\n");
/****************************************************/
	ret = misc_register(&beep_dev);

	if(ret < 0)
	{
		printk("beep register is error\n");
		return -1;
	}
	
	printk("beep_init\n");
/****************************************************/
	
	return 0;
};

int beep_remove (struct platform_device *pdev)
{
	printk("beep_remove\n");
	return 0;
};

const struct platform_device_id beep_id_table = {
	.name = "beep_test"
};

struct platform_driver beep_device = {
	.probe = beep_probe,
	.remove = beep_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "beep_test"
	},
	.id_table = &beep_id_table
};

static int __init beep_driver_init(void)
{
	int ret = 0;

	ret = platform_driver_register(&beep_device);

	if(ret < 0)
	{
		printk("platform_driver_register error\n");
		return ret;
	}
	printk("platform_driver_register ok\n");
	
	return 0;
}

static void __exit beep_driver_exit(void)
{
	printk("hello_exit\n");
	platform_driver_unregister(&beep_device);
	misc_deregister(&beep_dev);
	iounmap(vir_pwm_dr);
}

module_init(beep_driver_init);
module_exit(beep_driver_exit);

MODULE_LICENSE("GPL");

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值