浅谈平台总线驱动设计

平台总线是linux2.6内核加入的一种虚拟总线,使用流程:

  1、定义设备

  2、注册设备

  3、定义驱动

  4、注册驱动

总线上的设备和驱动相互匹配由总线来完成。

一、定义设备

  平台设备描述结构:struct platform_device

struct platform_device {
    const char    * name;
    int        id;
    struct device    dev;
    u32        num_resources;
    struct resource    * resource;

    const struct platform_device_id    *id_entry;

    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;

    /* arch specific additions */
    struct pdev_archdata    archdata;
};

  重要成员:

    name:设备名

    struct resource:设备资源(包括中断号、寄存器等)

    id:设备id

    num_resources:硬件占有资源的数量

  

struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    struct resource *parent, *sibling, *child;
};

  start和end的意义由flags来定,flags代表硬件资源的类型,linux规定了如下类型

/*
 * IO resources have these defined flags.
 */
#define IORESOURCE_BITS        0x000000ff    /* Bus-specific bits */

#define IORESOURCE_TYPE_BITS    0x00001f00    /* Resource type */
#define IORESOURCE_IO        0x00000100
#define IORESOURCE_MEM        0x00000200
#define IORESOURCE_IRQ        0x00000400
#define IORESOURCE_DMA        0x00000800
#define IORESOURCE_BUS        0x00001000

#define IORESOURCE_PREFETCH    0x00002000    /* No side effects */
#define IORESOURCE_READONLY    0x00004000
#define IORESOURCE_CACHEABLE    0x00008000
#define IORESOURCE_RANGELENGTH    0x00010000
#define IORESOURCE_SHADOWABLE    0x00020000

#define IORESOURCE_SIZEALIGN    0x00040000    /* size indicates alignment */
#define IORESOURCE_STARTALIGN    0x00080000    /* start field is alignment */

#define IORESOURCE_MEM_64    0x00100000
#define IORESOURCE_WINDOW    0x00200000    /* forwarded by bridge */
#define IORESOURCE_MUXED    0x00400000    /* Resource is software muxed */

#define IORESOURCE_EXCLUSIVE    0x08000000    /* Userland may not map this resource */
#define IORESOURCE_DISABLED    0x10000000
#define IORESOURCE_UNSET    0x20000000
#define IORESOURCE_AUTO        0x40000000
#define IORESOURCE_BUSY        0x80000000    /* Driver has marked this resource busy */

  例如当flags取IORESOURCE_MEM时,start、end代表硬件占有的首末内存地址;当flags取IORESOURCE_IRQ时,start、end则代表硬件占有的首末中断号。

二、注册平台设备

  函数原型:int platform_device_register(struct platform_device *pdev)

以mini2440键盘为例,定义并注册设备代码如下:

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

#define GPGCON 0x56000060
#define GPGDAT 0x56000064

struct resource key_resource[] =
{
    [0] = {
        .start = GPGCON,
        .end = GPGCON + 8,
        .flags = IORESOURCE_MEM,
    },

    [1] = {
        .start = IRQ_EINT8,
        .end = IRQ_EINT19,
        .flags = IORESOURCE_IRQ,
    },
};

struct platform_device key_device =
{
    .name = "my key",
    .id = 0,
    .num_resources = 2,        //硬件占有寄存器和中断号两个资源
    .resource = key_resource,
};

static int key_dev_init()
{
    /*注册平台设备*/
    platform_device_register(&key_device);
    return 0;
}

void key_dev_exit()
{
    /*注销平台设备*/
    platform_device_unregister(&key_device);
}

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

三、定义驱动

  平台驱动描述结构:struct platform_driver

struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table;
};

  重要成员:

    name:驱动名称  (仍采取设备名称和驱动名称来匹配,所以二者一定要一致)

    probe函数: 匹配成功后调用此函数

    remove函数:设备移除后执行此函数

四、注册驱动平台驱动

  函数原型:int platform_driver_register(struct platform_driver *drv)

将按键驱动程序由混杂设备驱动优化为平台总线驱动。之前的驱动程序都包含了硬件资源的信息,如GPGCON、GPGDAT寄存器的地址,各个按键的中断号等,移植性不高。优化过后,这些硬件信息都从匹配到的设备的platform_device结构中去取,从而将驱动和设备彻底分开了。从匹配到的设备中取硬件信息使用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;
}

该函数遍历设备资源后返回与type指定类型相同的资源。

优化后代码如下

key.h:

#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/sched.h>
#include <linux/platform_device.h>

/*存储键盘控制寄存器基地址*/
unsigned int *key_base;

/*定义工作项结构体*/
struct work_struct *work1;

/*定义定时器变量结构体*/
struct timer_list key_timer;

unsigned int key_num = 0;

/*定义等待队列*/
wait_queue_head_t key_queue;

struct resource *res_irq;
struct resource *res_mem;

/*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_queue,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,
    //.unlocked_ioctl = key_ioctl,
};

/*字符设备描述结构*/
struct miscdevice key_miscdev =
{
    .minor = 200,
    .name = "key",
    .fops = &key_fops,
};

驱动程序:

#include "key.h"

/********************
函数名:work1_func
参数:无
返回值:无
函数功能:实现工作项
结构体中的func成员
********************/
void work1_func()
{
    /*启动定时器*/
    mod_timer(&key_timer,jiffies + (HZ/10));
}

/************************
函数名:que_init
参数:无
返回值:无
函数功能:创建一个工作项
*************************/
int que_init()
{
    /*创建工作*/
    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);

    INIT_WORK(work1, work1_func);
}

/************************
函数名:key_int
参数:无
返回值:0
函数功能:按键中断处理函数
*************************/
irqreturn_t key_int(int irq, void *dev_id)
{
    /*1、检测设备是否产生中断*/

    /*2、清除中断产生标志*/

    /*3、提交下半部分工作*/
    schedule_work(work1);

    return 0;
}

/************************
函数名:key_hw_init
参数:无
返回值:无
函数功能:初始化与按键相关
的寄存器
*************************/
void key_hw_init()
{
    unsigned int data;
    data = readw(key_base);
    data &= ((3)|(3<<6)|(3<<10)|(3<<12)|(3<<14)|(3<<22));//~(0b11);
    data |= (2|(2<<6)|(2<<10)|(2<<12)|(2<<14)|(2<<22));//0b10;
    writew(data,key_base);
}

/************************
函数名:key_timer_func
参数:无
返回值:无
函数功能:定时器超时处理函
数,达到规定时间执行此函数
*************************/
void key_timer_func()
{
    unsigned int key_val,i;

    for(i = 0;i < 15;i++)
    {
        if((i == 0)||(i == 3)||(i == 5)||(i == 6)||(i == 7)||(i == 11))
        {
            key_val = readw(key_base + 1) & (1 << i);
            if(key_val == 0)
                key_num = i+1;
        }
    }
    wake_up(&key_queue);
}

static int __devinit key_probe(struct platform_device *pdev)
{
    int size,ret;

    /*注册设备*/
    ret = misc_register(&key_miscdev);
      /*从匹配到的设备中取出设备硬件信息*/
    res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0);
    res_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);
  /*注册中断*/
    request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0);
    request_irq(res_irq->start + 3,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0);
    request_irq(res_irq->start+5,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0);
    request_irq(res_irq->start+6,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0);
    request_irq(res_irq->start+7,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0);
    request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING ,"key",(void *) 0);

    size = res_mem->end - res_mem->start + 1;
    key_base = ioremap(res_mem->start,size);

    /*硬件初始化*/
    key_hw_init();

    /*工作队列初始化*/
    que_init();

    /*初始化定时器*/

    init_timer(&key_timer);

    key_timer.function = key_timer_func;

    /*注册定时器*/
    add_timer(&key_timer);

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

    return ret;
}

static int key_remove(struct platform_device *pdev)
{
    /*注销设备*/
    misc_deregister(&key_miscdev);
    /*注销中断*/
    free_irq(IRQ_EINT8, 0);
}

/*定义平台总线驱动*/
struct platform_driver key_driver =
{
    .driver = {
        .name = "my key",
    },
    .probe = key_probe,
    .remove = key_remove,
};

static int button_init()
{
    /*注册平台驱动*/
    platform_driver_register(&key_driver);

    printk("key.ko is ready\n");

    return 0;
}

static void button_exit()
{
    /*注销平台驱动*/
    platform_driver_unregister(&key_driver);
}

MODULE_LICENSE("GPL");
module_init(button_init);
module_exit(button_exit);

这次的随笔内容大致这么多,这里我有一个小问题想不明白,就是platform_get_resource这个函数的第三个参数num意义何在?我查阅linux内核代码发现大多数用到这个函数的地方将num赋为0,表示不知道为什么。如果有知道的麻烦告诉我,感激不尽~

此代码适用mini2440开发板,不同型号开发板IO口和中断号不同。如果有疑问或建议,欢迎指出。

   

  

时间: 2024-10-18 04:55:07

浅谈平台总线驱动设计的相关文章

9.平台总线驱动设计

平台总线驱动设计 平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备与驱动进行了管理,这样提高了程序的可移植性.虚拟总线和实际的总线优势相当.我们只要把驱动和设备挂载到虚拟总线就可以了. 平台总线驱动与设备匹配机制 平台总线的结构:platform_bus_type: 该结构中,最重要的是我们的匹配函数platform_match: 在匹配函数里,有我们熟悉的代码,最后一行: Strcmp(pdev->name,drv->name)这

平台总线驱动设计

平台总线驱动设计1.平台总线概述(总线驱动中最为重要的一个总线)平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其 优势在于采用了总线的模型对设备与驱动进行了管理,这总线的模 型对设备与驱动进行了管理,这样提高了程序的可移植性.通过平台总线机制开发设备驱动的流程:定义platform_device->注册platform_device->定义 platform_drivre->注册platform_driver 屏台总线驱动与设备匹配机制:内核代 码/drv

【开源】浅谈Hybrid技术的设计与实现第二弹

前言 接上文:浅谈Hybrid技术的设计与实现(阅读本文前,建议阅读这个先) PS:据说加个开源在前面阅读量高点,于是就试试咯...... 上文说了很多关于Hybrid的概要设计,可以算得上大而全,有说明有demo有代码,对于想接触Hybrid的朋友来说应该有一定帮助,但是对于进阶的朋友可能就不太满足了,他们会想了解其中的每一个细节,甚至是一些Native的实现,小钗这里继续抛砖引玉,希望接下来的内容对各位有一定帮助. 进入今天的内容之前我们首先谈谈两个相关技术Ionic与React Nativ

浅谈Hybrid技术的设计与实现

浅谈Hybrid技术的设计与实现 前言 随着移动浪潮的兴起,各种APP层出不穷,极速的业务扩展提升了团队对开发效率的要求,这个时候使用IOS&Andriod开发一个APP似乎成本有点过高了,而H5的低成本.高效率.跨平台等特性马上被利用起来形成了一种新的开发模式:Hybrid APP. 作为一种混合开发的模式,Hybrid APP底层依赖于Native提供的容器(UIWebview),上层使用Html&Css&JS做业务开发,底层透明化.上层多多样化,这种场景非常有利于前端介入,非

浅谈SAP Fiori的设计美感与发展历程

公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[Fiori系列]浅谈SAP Fiori的设计美感与发展历程 前言部分 大家可以关注我的公众号,公众号里的排版更好,阅读更舒适. 正文部分 最近一段时间比较忙,有很多读者的消息没有及时回复,公众号里消息回复有时长限制,而且超过时间就不能回复了,大家也可以选择在文章末尾留言,这样就不会有时间限制. 我在之前的文章推送里写了关于SAP Fiori的文

浅谈DevExpress&lt;二&gt;:设计一个完整界面(2)

下面来把剩下的工作做完,换肤功能昨天已近讨论过,今天就不重复了.首先建立三个全局变量,一个存放文件路径,一个存放数据,一个存放过滤条件. string DBFileName; DataView dataView; string[] filter = new string[3]; 取得数据并绑定到表格中: DBFileName = DevExpress.Utils.FilesHelper.FindingFileName(Application.StartupPath, "Products.xml&

浅谈DevExpress&lt;二&gt;:设计一个完整界面(1)

昨天谈了界面的换肤问题,今天拿一个简单的界面来介绍一下怎么设计一个五脏俱全的界面,总体效果如下图(种类的图片随便找的^^): 创建一个winform项目,在上面拉进去一个bar管理器和图片列表: 在菜单栏.工具栏和状态栏中,分别加入菜单.编辑栏.按钮和静态文本: 菜单栏改名并设置好图片: 然后改工具栏项的属性,拿第一个举个例子,后面的大同小异,选择EditItem后先将其PaintStyle属性改为CapationGlyph,然后找到Edit,选择CheckEdit,就会变成下面的样子: 依法炮

浅谈logo在PPT设计中的运用

在工业设计范畴,特别是产品设计中常常会提到“形式跟随功用”,也就是说产品的外型是树立在产品功用的根底之上的,同样道理,在PPT设计中则演化为“形式跟随内容”,就是说页面的美化设计是为了更好的将内容向观众传达. 为此我们总结了PPT设计的三个原则,即“图示化”,“图标化”,“图表化” 以“图标化”为例,所谓图标,就是具有指代意义的图形符号,具有高度浓缩并快捷传达信息.便于记忆的特性.应用范围很广,软硬件网页社交场所公共场所无所不在,例如各种交通标志…… 在用户界面设计范畴中则为图标的形式,包括程序

平台总线 —— 平台总线驱动模型

目录 1.为什么会有平台总线? 2.平台总线三要素 3.平台总线编程接口 4.编写能在多平台下使用的led驱动 1.为什么会有平台总线? 1 用于平台升级:三星: 2410, 2440, 6410, s5pc100 s5pv210 4412 2 硬件平台升级的时候,部分的模块的控制方式,基本上是类似的 3 但是模块的地址是不一样 4 5 gpio控制逻辑: 1, 配置gpio的输入输出功能: gpxxconf 6 2, 给gpio的数据寄存器设置高低电平: gpxxdata 7 逻辑操作基本上是