嵌入式学习-驱动开发-lesson5-总线设备驱动模型及平台总线驱动

一、总线设备驱动模型

随着技术的不断进步,系统的拓扑结构也越来越复杂,对热插拔,跨平台移植性的要求也越来越高,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);

现象如下:
这里写图片描述

菜鸟一枚,如有错误,多多指教。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值