一、概念
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 { 注册平台设备: int platform_device_register(struct platform_device *pdev) |
平台驱动使用struct platform_driver 描述: struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); …… } 注册平台驱动: |
四、程序 下载点击打开链接
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");