内核驱动 - Platform bus平台总线

一、概念

Platform bus平台总线,是一种虚拟总线,采用总线的模型对设备与驱动进行管理,提高程序的可移植性。

Platform bus平台总线 驱动和设备匹配方法:

1. 通过id_table(芯片型号)进行匹配,但是大多数设备都没有id_table(芯片型号)

2. 通过设备->name驱动->name进行匹配

二、平台设备

平台设备使用struct platform_device来描述:
struct platform_device {
    const char *name; /*设备名*/
    int id; /*设备编号,配合设备名使用*/
    struct device dev;
    u32 num_resources;
    struct resource *resource; /*设备资源*/

}

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

注册平台设备:

int platform_device_register(struct platform_device *pdev)

扫描二维码关注公众号,回复: 151183 查看本文章
三、平台驱动
平台驱动使用struct platform_driver 描述:
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
……

}


注册平台驱动:
int platform_driver_register(struct platform_driver *)

四、程序 下载点击打开链接

key_dev.c

/*********************************************
*File name :key_dev.c
*Author    :JerryGou
*Date      :2017/10/23
*Function  :平台总线之按键设备
*********************************************/

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

#define GPH0CON	        0xE0200C00

/*使用的资源*/
struct resource key_resource[] = {
	[0] = {
	           /*资源1 两个按键*/
	           .start = GPH0CON,
		   .end = ( GPH0CON + 8 ), //CONFIG寄存器+DATA寄存器共8字节
		   .flags = IORESOURCE_MEM,	  /*资源类型-内存*/
		   },
	[1] = {
	            /*资源2 两个中断*/
		    .start = IRQ_EINT0,
		    .end = IRQ_EINT1,
		    .flags = IORESOURCE_IRQ,  /*资源类型-中断*/
		   },
};

struct platform_device key_device = {
	.name = "my-key",            /*与驱动相匹配的名字*/
	.id = 0,		             /*设备编号ID,为0为空,不用可设置为0*/
	.num_resources = 2,	         /*资源数目,与resource对应*/
	.resource = key_resource,    /*资源描述结构*/
};

int keydev_init(void)
{
	int ret;
	/*平台注册*/
	ret = platform_device_register(&key_device);
	return ret;
}

void keydev_exit(void)
{
	/*平台注销*/
	platform_device_unregister(&key_device);
}

module_init(keydev_init);
module_exit(keydev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jerry.Gou");
MODULE_DESCRIPTION("TQ210 button driver");
key_dri.c
/*********************************************
*File name :key_dri.c
*Author    :JerryGou
*Date      :2017/10/24
*Function  :平台总线之按键驱动
*********************************************/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/platform_device.h>

#define GPH0CON               0xE0200C00
#define GPH0DAT               0xE0200C04

struct work_struct	*work;

unsigned int key_num;

/*定时器*/
struct timer_list buttons_timer;

/*等待队列*/
wait_queue_head_t key_q;

struct resource *res_irq;
struct resource *res_mem;
unsigned int *key_base;

/*open 函数*/
int key_open(struct inode *node, struct file *filp)
{
	return 0;
}

/*read 函数*/
ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
	/*进入等待队列睡眠*/
	wait_event(key_q, key_num);	   /*当key_num为true,即当按键按下时唤醒*/
	if (copy_to_user(buf, &key_num, 4))
		return 4;
	else
		return 0;
}

struct file_operations key_fops =
{
	.open = key_open,
	.read = key_read,
};

struct miscdevice key_miscdev = {
	/*MISC_DYNAMIC_MINOR代表动态分配次设备号,即由系统自动分配*/
	//.minor = 200,
	.minor = MISC_DYNAMIC_MINOR,  /*次设备号*/
	.name = "tq210key",	/*设备名称*/
	.fops = &key_fops,	/*文件操作*/
};

static irqreturn_t key_int(int irq, void *dev_id)
{
	//1. 检测是否发生了按键中断

	//2. 清除已经发生的按键中断

	//3. 挂载(提交)工作
	schedule_work(work);

	return IRQ_HANDLED;
}

void buttons_timer_function(unsigned long data)
{
	unsigned int key_val = 0;
	key_val = readw(key_base + 1);

	if (( key_val & 0x01 ) == 0)
	{
		printk("key1 down!\n");
		key_num = 0;
	}
	if (( key_val & 0x02 ) == 0)
	{
		printk("key2 down!\n");
		key_num = 2;
	}

	/*唤醒进程*/
	wake_up(&key_q);
}

/*定时器初始化*/
void timer_init(void)
{
	/*初始化定时器*/
	init_timer(&buttons_timer);
	/*设置超时函数*/
	buttons_timer.function = buttons_timer_function;

	/*向内核注册一个定时器*/
	add_timer(&buttons_timer);
}

void work_func(struct work_struct *work)
{	 /*启动定时器*/   /*延时 1s/10=100ms */
	mod_timer(&buttons_timer, jiffies + HZ / 10);
}

/*队列初始化*/
void queue_init(void)
{
	//创建工作
	work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
	INIT_WORK(work, work_func);
}

void key_hw_init(void)
{
	unsigned int data;
	data = readw(key_base);
	data &= ~0xFFFFF;
	data |= 0xFFFFF;
	writew(data, key_base);
}

/*找到设备后对设备进行初始化*/
int key_probe(struct platform_device *pdev)
{
	int ret, size;
	/*混杂设备驱动注册*/
	ret = misc_register(&key_miscdev);
	if (ret != 0)
		printk(KERN_WARNING"register fail\n");

	/*注册中断处理程序,下降沿产生中断*/
	res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	ret = request_irq(res_irq->start, key_int, IRQF_TRIGGER_FALLING, "tq210key", (void*)1);
	ret = request_irq(res_irq->end, key_int, IRQF_TRIGGER_FALLING, "tq210key", (void*)2);

	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	size = res_mem->end - res_mem->start + 1;
	key_base = ioremap(res_mem->start, size);

	/*按键硬件初始化*/
	key_hw_init();

	/*队列初始化*/
	queue_init();

	/*定时器初始化*/
	timer_init();

	/*初始化等待队列*/
	init_waitqueue_head(&key_q);

	return 0;
}

static int key_remove(struct platform_device *pdev)
{
	/*释放中断*/
	free_irq(res_irq->start, (void *)1);
	free_irq(res_irq->end, (void *)2);

	/*取消虚拟地址映射*/
	iounmap(key_base);

	/*注销设备*/
	misc_deregister(&key_miscdev);
	return 0;
}

struct platform_driver key_driver = {
	.probe = key_probe,  //驱动、设备匹配成功后执行该函数,相当于初始化
	.remove = key_remove,  //驱动注销时执行
	.driver = {
		.owner = THIS_MODULE,
		.name = "my-key",
	},
};

static int button_init(void)
{
	int ret;
	//注册平台设备驱动
	ret = platform_driver_register(&key_driver);
	return ret;
}

static void button_exit(void)
{
	/*注销平台设备驱动*/
	platform_driver_unregister(&key_driver);
}

module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jerry.Gou");
MODULE_DESCRIPTION("TQ210 button driver");



















猜你喜欢

转载自blog.csdn.net/jerrygou/article/details/79795727