一、总线设备驱动模型
随着技术的不断进步,系统的拓扑结构也越来越复杂,对热插拔,跨平台移植性的要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,从Linux 2.6内核开始提供了全新的设备模型即总线模型,他的优势在于能够处理热插拔、提高驱动程序的可移植性。
总线模型也可称之为总线设备驱动模型,既然是总线设备驱动模型,首先要有一条总线,设备和驱动则挂载到这条总线上,当驱动安装好,设备连接时,或者设备连接上,驱动安装后,总线会通过设备id_able后者设备与驱动的名称,进行匹配,匹配成功之后,就会调用驱动里面的probe函数进行相应的初始化操作。
1).总线
1 描述结构
在 Linux 内核中, 总线由 bus_type 结构表示,定义在
struct bus_type {
const char *name; /*总线名称*/
int (*match) (struct device *dev, struct
device_driver *drv); /*驱动与设备的匹配函数*/
………
}
int (match)(struct device dev, struct device_driver * drv)
当一个新设备或者新驱动被添加到这个总线时,该函数被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零。
2 总线注册与注销
总线的注册使用如下函数
bus_register(struct bus_type *bus)
若成功,新的总线将被添加进系统,并可在/sys/bus 下看到相应的目录。
总线的注销使用:
void bus_unregister(struct bus_type *bus)
bus.c
/***************************************************
*File name :bus.c
*Author :stone
*Date :2016/07/31
*Function :总线设备驱动模型之总线
**************************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
/*将设备与驱动进行匹配*/
int my_match (struct device *dev, struct device_driver *drv)
{
return !strncmp(dev->kobj.name,drv->name,strlen(drv->name));
}
struct bus_type my_bus = {
.name = "my_bus",
.match = &my_match,
};
EXPORT_SYMBOL(my_bus);
int bus_init()
{
/*注册总线*/
bus_register(&my_bus);
return 0;
}
void bus_exit()
{
bus_unregister(&my_bus);
}
MODULE_LICENSE("GPL");
module_init(bus_init);
module_exit(bus_exit);
将其编译,下载到开发板上,现象如下:
注意:
至于为啥是dev->kobj.name 而不是dev->init_name,在linux内核中有定义,如下:
因此在使用dev->init_name时为空,其实dev->init_name被复制到kobj.name 里面去了,所以使用kobj.name这个。
2)驱动
1 描述结构
在 Linux内核中, 驱动由 device_driver结构表示。
struct device_driver {
const char *name; /*驱动名称*/
struct bus_type *bus; /*驱动程序所在的总线*/
int (*probe) (struct device *dev);/*当总线匹配上之后,会调用此函数对设备进行初始化*/
………
}
2 驱动注册与注销
驱动的注册使用如下函数
int driver_register(struct device_driver *drv)
驱动的注销使用:
void driver_unregister(struct device_driver *drv)
driver.c
/***************************************************
*File name :driver.c
*Author :stone
*Date :2016/07/31
*Function :总线设备驱动模型之驱动
**************************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
extern struct bus_type my_bus;
int my_probe(struct device *dev)
{
printk(KERN_WARNING"driver found the device it can handle!");
return 0;
}
struct device_driver my_drv = {
.name = "my_dev",
.bus = &my_bus, /*总线*/
.probe = my_probe, /*匹配成功之后,调用此函数*/
};
int driver1_init()
{
//驱动注册
driver_register(&my_drv);
return 0;
}
void driver1_exit()
{
driver_unregister(&my_drv);
}
MODULE_LICENSE("GPL");
module_init(driver1_init);
module_exit(driver1_exit);
3)设备
1 描述结构
在 Linux内核中, 设备由struct device结构表示。
struct device {
{
const char *init_name; /*设备的名字*/
struct bus_type *bus; /*设备所在的总线*/
………
}
2 驱动注册与注销
设备的注册使用如下函数
int device_register(struct device *dev)
v设备的注销使用:
void device_unregister(struct device *dev)
device.c
/***************************************************
*File name :device.c
*Author :stone
*Date :2016/07/31
*Function :总线设备驱动模型之设备
**************************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
extern struct bus_type my_bus;
struct device my_drv = {
.init_name = "my_dev",
.bus = &my_bus, /*总线*/
};
int device_init()
{
//驱动注册
device_register(&my_drv);
return 0;
}
void device_exit()
{
device_unregister(&my_drv);
}
MODULE_LICENSE("GPL");
module_init(device_init);
module_exit(device_exit);
二、平台总线设备设计
1)平台总线概述
平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备与驱动进行了管理,这样提高了程序的可移植性。(在Linux内核中,已经创建好,不需要自己创建)
通过平台总线机制开发设备驱动的流程如下:
1.定义 platform_device
2.注册 platform_device
3.定义 platform_driver
4.注册 platform_driver
平台总线匹配设备与驱动机制:
在platform.c中:
可以看到,设备与驱动有两种匹配方式,通过设备名字或者通过id_table匹配,本课中通过设备名字匹配。
2)平台设备
平台设备使用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)
key_dev.c
/***************************************************
*File name :key_dev.c
*Author :stone
*Date :2016/07/31
*Function :平台总线之按键设备
**************************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#define GPNCON 0x7f008830
/*使用的资源*/
struct resource key_resource[] = {
[0] = {
.start = GPNCON, /*资源1 两个按键 */
.end = GPNCON +8,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_EINT(0), /**资源2 两个中断*/
.end = IRQ_EINT(1),
.flags = IORESOURCE_IRQ,
},
};
struct platform_device key_device = {
.name = "my_key", /*与驱动相匹配的名字*/
.num_resources = 2, /*资源数目*/
.resource = key_resource, /*资源描述结构*/
};
/*入口函数*/
int keydev_init()
{
/*注册*/
platform_device_register(&key_device);
return 0;
}
void keydev_exit()
{
platform_device_unregister(&key_device);
}
MODULE_LICENSE("GPL");
module_init(keydev_init);
module_exit(keydev_exit);
效果如下:
3).平台驱动
平台驱动使用struct platform_driver 描述:
struct platform_driver {
int (*probe)(struct platform_device *); /*匹配上时,使用此函数*/
int (*remove)(struct platform_device *); /*当检测到设备拔出时,使用此设备*/
……
}
平台驱动注册使用函数:
int platform_driver_register(struct platform_driver *)
key_drv.c
/***************************************************
*File name :key_drv.c
*Author :stone
*Date :2016/07/31
*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 <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
struct work_struct *work1;
unsigned int *gpio_data;
unsigned int key_num = 0;
wait_queue_head_t key_q;
struct resource *res_irq;
struct resource *res_mem;
unsigned int *key_base;
struct timer_list key_timmer;
void work1_func(struct work_struct *work)
{
/*启动定时器*/
mod_timer(&key_timmer,jiffies + HZ/10); /* 延时1S/10=10ms*/
//printk("key down !\n");
}
void key_timer_func(unsigned long data)
{
unsigned int key_val;
/*检测按键是否按下*/
key_val = readw(key_base + 1)&0x01; //key_base + 1 加一就是data寄存器
if(key_val == 0) /*有效*/
//printk("key1 down !\n");
key_num = 1;
key_val = readw(key_base + 1)&0x02;
if(key_val == 0) /*有效*/
//printk("key2 down !\n");
key_num = 2;
wake_up(&key_q);
}
/*中断处理函数*/
irqreturn_t key_int(int irq,void *dev_id)
{
/*1.检测是否发生按键中断*/
/*2.清除已经发生的按键中断*/
/*3.打印按键值*/
///printk("key down !\n");
/*3.提交下半部*/
schedule_work(work1);
return 0;
}
/*按键硬件初始化*/
void key_hw_init()
{
//unsigned int *gpio_config;
unsigned short data;
/*物理地址转化为虚拟地址*/
//gpio_config = ioremap(GPNCON,4);
data = readw(key_base);
data &= ~0b1111;
data |= 0b1010;
writew(data,key_base);
//gpio_data = ioremap(GPNDAT,4);
}
int key_open(struct inode *node ,struct file *filp)
{
return 0;
}
ssize_t key_read(struct file *filp,char __user *buf,size_t size,loff_t *pos)
{
wait_event(key_q,key_num);
copy_to_user(buf,&key_num,4);
key_num = 0;
return 4;
}
struct file_operations key_fops =
{
.open = key_open,
.read = key_read,
};
/*初始化*/
struct miscdevice key_miscdev = {
.minor = 200, /* 次设备号*/
.name = "key", /* 设备名*/
.fops = &key_fops, /*文件操作*/
};
int __devinit key_probe(struct platform_device *pdev)
{
/*找到设备时才能进行下面的初始化*/
int ret;
int size;
/*注册设备*/
ret = misc_register(&key_miscdev);
if(ret != 0)
printk(KERN_WARNING"register fail!\n");
/*注册中断 下降沿产生中断*/
res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0);
request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING , "key",1);
request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING , "key",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();
/*创建工作*/
work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work1, work1_func);
/*初始化定时器*/
init_timer(&key_timmer);
/*设置超时函数*/
key_timmer.function = key_timer_func;
/*向内核注册定时器*/
add_timer(&key_timmer);
/*初始化等待队列*/
init_waitqueue_head(&key_q);
return ret;
}
static int __devexit key_remove(struct platform_device *pdev)
{
/*注销*/
misc_deregister(&key_miscdev);
return 0;
}
struct platform_driver key_driver = {
.probe = key_probe,
.remove = __devexit_p(key_remove),
.driver ={
.name = "my_key",
},
};
static int button_init()
{
/*平台的注册*/
return platform_driver_register(&key_driver);
}
static void button_exit()
{
platform_driver_unregister(&key_driver);
/*注销中断*/
//free_irq(IRQ_EINT(0),0);
}
MODULE_LICENSE("GPL");
module_init(button_init);
module_exit(button_exit);
现象如下:
菜鸟一枚,如有错误,多多指教。。。