05. Linux platform驱动--按键驱动

什么是platform(平台)总线

相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。那为什么需要platform总线呢?其实是Linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。因为对于usb设备、i2c设备、pci设备、spi设备等等,他们与cpu的通信都是直接挂在相应的总线下面与我们的cpu进行数据交互的,但是在我们的嵌入式系统当中,并不是所有的设备都能够归属于这些常见的总线,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。所以Linux驱动模型为了保持完整性,将这些设备挂在一条虚拟的总线上(platform总线),而不至于使得有些设备挂在总线上,另一些设备没有挂在总线上。

两个结构体platform_device和platform_driver

对于任何一种Linux设备驱动模型下的总线都由两个部分组成:描述设备相关的结构体和描述驱动相关的结构体在platform总线下就是platform_device和platform_driver,下面是对两个结构体的各个元素进行分析:
platform_device结构体:(include\linux\platform_device.h)

struct platform_device {
	const char * name; 			/* 平台设备名字 */
	int id; 					/* 一般设置为-1,表示系统自动分配 */
	bool id_auto;
	struct device dev;			/* 每一个设备都有一个 device 结构体 */
	u32 num_resources; 			/* 资源数量 */
	struct resource* resource;	/* 资源 */
	/* platform_device_id,里面有个名字列表 */
	const struct platform_device_id *id_entry;
	…
};

其中有个重要的结构体成员,那就是资源(resource)

struct resource {
	resource_size_t start; 	/* 资源的起始值 */
	resource_size_t end; 	/* 资源的结束值 */
	const char *name;
	/* 资源的类型,如:
	* IORESOURCE_IO(IO 资源)、 IORESOURCE_MEM(内存资源)、
	* IORESOURCE_IRQ(中断资源)、 IORESOURCE_DMA(DMA 资源)
	*/
	unsigned long flags;
	struct resource *parent, *sibling, *child;
};

platform_driver结构体:(include\linux\platform_device.h)

struct platform_driver {
    int (*probe)(struct platform_device *);     //  这个probe函数其实和  device_driver中的是一样的功能,但是一般是使用device_driver中的那个
    int (*remove)(struct platform_device *);    //  卸载平台设备驱动的时候会调用这个函数,但是device_driver下面也有,具体调用的是谁这个就得分析了
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);
    struct device_driver driver;                //   内置的device_driver 结构体 
    const struct platform_device_id *id_table;  //  该设备驱动支持的设备的列表  他是通过这个指针去指向  platform_device_id 类型的数组
};

平台总线如何匹配设备和驱动

int platform_driver_register(struct platform_driver *); // 用来注册我们的设备驱动
void platform_driver_unregister(struct platform_driver *); // 用来卸载我们的设备驱动
int platform_device_register(struct platform_device *); // 用来注册我们的设备
void platform_device_unregister(struct platform_device *); // 用来卸载我们的设备

平台总线驱动设备驱动实例

平台设备驱动实例(platform_device)
流程:

  1. 定义platform_device实例
  2. 根据实际情况定义platform_device里的resource实例或这dev->platform_data实例
  3. 在模块入口函数platform_device_register注册一个平台设备
  4. 在模块出口函数platform_device_unregister注销一个平台设备
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>

#define GPH2CON 0xE0200C40

static struct resource key_resource[] = {
	[0] = {
		.start = GPH2CON,
		.end   = GPH2CON + 8,
		.flags = IORESOURCE_MEM
	},
	[1] = {
		.start = IRQ_EINT(16),
		.end   = IRQ_EINT(17),
		.flags = IORESOURCE_IRQ,
	},
};
static struct platform_device key_dev = {
	.name			 = "my_key",
	.id				 = 0,
	.num_resources	 = ARRAY_SIZE(key_resource),
	.resource		 = key_resource,
};

static int __init  key_dev_init()
{
	platform_device_register(&key_dev);
	printk("platform device register success!\n");
	return 0;
}

static void __exit  key_dev_exit()
{
	platform_device_unregister(&key_dev);
}

module_init(key_dev_init);
module_exit(key_dev_exit);
MODULE_LICENSE("GPL");

平台驱动实例(platform_driver)
流程:

  1. 定义platform_driver实例
  2. 根据实际情况定义实现platform_driver里的probe成员函数,里面注册什么由你决定
  3. 根据实际情况定义实现platform_driver里的remove成员函数,里面注销什么由你决定
  4. 在模块入口函数platform_driver_register注册一个平台驱动
  5. 在模块出口函数platform_driver_unregister注销一个平台驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>  
#include <linux/interrupt.h>  
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <asm/uaccess.h>

#define GPH2CON 0xE0200C40
#define GPH2DAT 0xE0200C44

volatile int ev_press = 0;
unsigned int key_num = 0;
struct work_struct *work1 = NULL;
struct timer_list key_timer;
unsigned int * key_base = NULL;
DECLARE_WAIT_QUEUE_HEAD(key_queue);
struct resource *res = NULL;
struct resource *res_irq = NULL;

static int key_open (struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t key_read (struct file * file, char __user * buf, size_t size, loff_t * ppos)
{
	wait_event(key_queue, ev_press);
	copy_to_user(buf, &key_num, 4);
	key_num = 0;
	ev_press = 0;
	return 0;
}

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

static struct miscdevice key_misc = 
{
	.minor	= 200,
	.name	= "210key",
	.fops	= &key_fops,
};

void key_hw_init()
{
	unsigned short data = 0;
	data = readl(key_base);
	data |= 0xf;
	writel(data, key_base);
}

static irqreturn_t key_irq(int irq, void *dev_id)
{
	/* 检查是否产生中断(共享中断才用)*/
	/* 清除中断标志*/
	/* 中断下半部: 将产生中断的中断号传递到工作函数,提交工作(到内核默认的工作队列)*/
	schedule_work(work1);
	return IRQ_HANDLED;
}

void work1_fun()
{
	mod_timer(&key_timer, jiffies + HZ/5);
}

static void key_timer_function (unsigned long data)
{
	unsigned int pinval = 0;
	ev_press = 1;
	pinval = readl(key_base + 1) & 0x01;
	if(0 == pinval)
	{
		key_num = 1;
	}

	pinval = readl(key_base + 1) & 0x02;
	if(0 == pinval)
	{
		key_num = 2;
	}
	wake_up(&key_queue);
}

static int key_probe(struct platform_device *pdev)
{
	int ret = 0;
	int size = 0;
	/* 注册混杂设备驱动*/
	ret = misc_register(&key_misc);
	printk("register success!\n");
	if(ret != 0)
		printk("register fail!\n");
	/* 硬件初始化*/
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	size = (res->end - res->start) + 1;
	key_base = ioremap(res->start, size);
	key_hw_init();
	/* 注册中断*/
	res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	request_irq(res_irq->start, key_irq, IRQF_TRIGGER_FALLING, "S1", (void *)0);
	request_irq(res_irq->end, key_irq, IRQF_TRIGGER_FALLING, "S2", (void *)1);
	/* 初始化工作*/
	work1 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
	INIT_WORK(work1, work1_fun);
	/* 初始化定时器*/
	init_timer(&key_timer);
	key_timer.function = key_timer_function;
	/* 注册定时器*/
	add_timer(&key_timer);
	return 0;
}

static int key_remove(struct platform_device *pdev)
{
	kfree(work1);
	/* 注销中断*/
	free_irq(IRQ_EINT(16), (void *)0);
	free_irq(IRQ_EINT(17), (void *)1);
	/* 注销混杂设备*/
	misc_deregister(&key_misc);
	return 0;
}

static struct platform_driver key_driver = {
	.probe      = key_probe,
	.remove    = key_remove,
	.driver		= {
		.name	= "my_key",
	},
};

static int __init key_drv_init()
{
	return platform_driver_register(&key_driver);
}

static void __exit key_drv_exit()
{
	platform_driver_unregister(&key_driver);
}

module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章