平台总线驱动设计

Linux平台总线驱动模型

Linux系统既支持实际的总线如usb总线,pci总线,也支持虚拟总线。平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备与驱动进行了管理,这样提高了程序的可移植性。

总线设备一个很关键的函数是匹配函数,平台总线的匹配函数为platform_match(不同版本的Linux系统函数实现有差别)

static int platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev = to_platform_device(dev);
    struct platform_driver *pdrv = to_platform_driver(drv);

    /* try to match against the id table */
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;

    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0);
}

若驱动程序带有ip_table,则根据ip_table进行匹配;但一般驱动程序都不带ip_table,此时根据设备名称与驱动名称进行匹配。


开发流程

通过平台总线开发设备驱动流程为:
①定义平台设备
②注册平台设备
③定义平台驱动
④注册平台驱动

平台设备

平台设备使用struct platform_device来描述:

struct platform_device 
{
    const char *name; /*设备名*/
    int id; /*设备编号,配合设备名使用*/
    struct device dev;
    u32 num_resources;/*设备资源的数量*/
    struct resource *resource; /*设备资源*/
}

由于一个设备可以具备多个设备资源,如基地址、中断号等,所以利用该结构体定义设备时一般使用结构数组表示该成员。
struct 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)
注销平台设备:void platform_device_unregister(struct platform_device *pdev)

在平台总线上挂载按键设备:

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

MODULE_LICENSE("GPL");

#define GPFCON 0x56000050

static struct resource key_resource[] = {
    [0] = {
        .start = GPFCON,
        .end   = GPFCON + 8,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_EINT0,
        .end   = IRQ_EINT2,
        .flags = IORESOURCE_IRQ,
    },
};

struct platform_device key_device = {
    .name         = "my-key",
    .id       = 0,
    .num_resources    = ARRAY_SIZE(key_resource),
    .resource     = key_resource,
};

static int button_init()
{
    platform_device_register(&key_device);

    return 0;
}

static void button_exit()
{      
    platform_device_unregister(&key_device);
}

module_init(button_init);
module_exit(button_exit);

平台驱动

平台驱动使用struct platform_driver 描述:

struct platform_driver 
{
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    struct device_driver driver;
    …
}

probe为函数指针,当驱动与设备匹配成功时总线就调用probe指针指向的函数;
remove为函数指针,当与该驱动匹配的设备从总线上移除时总线就调用remove指针指向的函数;
driver结构成员中含有该平台驱动的名称,初始化时需要与设备的名字一致。

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

在编写驱动程序时,还会用到函数platform_get_resource(),使用该函数可以得到设备的信息。

struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
{
    int i;

    for (i = 0; i < dev->num_resources; i++) {
        struct resource *r = &dev->resource[i];

        if (type == resource_type(r) && num-- == 0)
            return r;
    }
    return NULL;
}

平台总线驱动模型的按键驱动程序:

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

MODULE_LICENSE("GPL");

struct work_struct *work;

struct timer_list buttons_timer;

unsigned int key_num = 0;

wait_queue_head_t  key_q;

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

void work_func(struct work_struct *work)
{
    mod_timer(&buttons_timer, jiffies + (HZ /10));  
}

void buttons_timer_function(unsigned long data)  
{
    unsigned int key_val;

    key_val = readw(key_base+1)&0x1; 
    if (key_val == 0)
       key_num = 4;

    key_val = readw(key_base+1)&0x4;
    if (key_val == 0)
        key_num = 3;

    wake_up(&key_q); 
} 

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

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

    //3. 提交下半部
    schedule_work(work);

    //return 0;
    return IRQ_HANDLED;

}

void key_hw_init()
{
    unsigned short data;

    data = readw(key_base);
    data &= ~0b110011;
    data |= 0b100010;

    writew(data,key_base);
}

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 key_probe(struct platform_device *pdev)
{
    int ret,size;

    ret = misc_register(&key_miscdev);

    if (ret !=0)
        printk("register fail!\n");

    //注册中断处理程序

    res_irq =  platform_get_resource(pdev, IORESOURCE_IRQ, 0);

    request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4);
    request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3);

    //按键初始化
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    size = (res->end - res->start) + 1;
    key_base = ioremap(res->start, size);

    key_hw_init();

    //. 创建工作
    work = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work, work_func);

    /* 初始化定时器 */  
    init_timer(&buttons_timer);   
    buttons_timer.function  = buttons_timer_function;  

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

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

    return 0;
}

int key_remove(struct platform_device *dev)
{
    free_irq(res_irq->start, (void *)4);
    free_irq(res_irq->end, (void *)3);

    iounmap(key_base);
    misc_deregister(&key_miscdev);
    return 0;
}

static struct platform_driver key_driver = {
    .probe      = key_probe,
    .remove     = key_remove,
    .driver     = {
        .owner  = THIS_MODULE,
        .name   = "my-key",
    },
};

static int button_init()
{
    return platform_driver_register(&key_driver);
}

static void button_exit()
{      
    platform_driver_unregister(&key_driver);
}

module_init(button_init);
module_exit(button_exit);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值