• 9.平台总线驱动设计


    平台总线驱动设计

    平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备与驱动进行了管理,这样提高了程序的可移植性。虚拟总线和实际的总线优势相当。我们只要把驱动和设备挂载到虚拟总线就可以了。

    平台总线驱动与设备匹配机制

    平台总线的结构:platform_bus_type:

    该结构中,最重要的是我们的匹配函数platform_match:

    在匹配函数里,有我们熟悉的代码,最后一行:

    Strcmp(pdev->name,drv->name)这是我们前面用到的,用驱动的名字和设备的名字来匹配的。如果名字一样的。我们就认为两者能匹配。

     

    注册平台设备:

    在这个结构中,重要的成员name,设备要和驱动的名字一样。另一个是resource。设备的资源,例如中断号,寄存器.....都是资源。这些就放到resource这个结构里:

    资源的类型:中断、内存等

    注册完了之后,我们的平台设备就挂载到我们的总线上去了。

    完成按键的注册

    实现的代码:key_dev.c:

    #include <linux/module.h>

    #include <linux/init.h>

    #include <linux/platform_device.h>

    #include <linux/of_platform.h>

    #include <linux/interrupt.h>

    #define GPNCON 0x7F008830

    MODULE_LICENSE("GPL");

    //定义资源

    struct resource key_resource[]=

    {

        //第一项资源

        [0]={

            .start=GPNCON,

            .end = GPNCON+8,

            .flags = IORESOURCE_MEM,

            },

        //第二项资源,中断号

        [1]={

            .start=IRQ_EINT(0),

            .end = IRQ_EINT(1),

            .flags= IORESOURCE_IRQ,

            },

    };

    //定义平台设备结构

    struct platform_device key_device=

    {

        .name = "my-key",

        .id =0,

        .num_resources = 2,

        .resource = key_resource,

    };

    int keydev_init()

    {

        //平台设备的注册

        platform_device_register(&key_device);

    }

    void keydev_exit()

    {

        platform_device_unregister(&key_device);

    }

    module_init(keydev_init);

    module_exit(keydev_exit);

    执行make,生成.ko设备文件,拷贝到开发板运行。

    [root@FORLINX6410]# insmod key_dev.ko

    [root@FORLINX6410]# cd /sys/bus/

    [root@FORLINX6410]# ls

    ac97 i2c platform serio usb-serial

    event_source mdio_bus scsi spi

    hid mmc sdio usb

    [root@FORLINX6410]# cd platform/

    [root@FORLINX6410]# ls

    devices drivers_autoprobe uevent

    drivers drivers_probe

    [root@FORLINX6410]# cd devices/

    [root@FORLINX6410]# ls

    alarmtimer s3c-g2d s3c-ts s3c6410-nand

    dm9000.0 s3c-g3d s3c-tvenc s3c64xx-rtc

    dummy_hcd s3c-hsotg s3c-tvscaler s3c64xx-spi.0

    dummy_udc s3c-jpeg s3c2410-ohci s3c64xx-spi.1

    gpio-keys s3c-keypad s3c2410-wdt samsung-ac97

    my-key.0 s3c-mfc s3c2440-i2c.0 samsung-audio

    我们在平台总线的文件夹里看到了我们创建的my-key设备。平台总线实现成功。

     

     

     

     

     

     

    平台总线驱动的实现

    接下来是将前面的按键驱动修改为平台设备驱动模型:

    平台总线驱动的结构:

     

     

    最后:key_drv.c的代码:

     

    #include <linux/module.h>        /* For module specific items */

    #include <linux/fs.h>            /* For file operations */

    #include <linux/ioport.h>        /* For io-port access */

    #include <linux/io.h>            /* For inb/outb/... */

    #include <linux/init.h>

    #include <linux/miscdevice.h>

    #include <linux/interrupt.h>

    #include <linux/slab.h>

    #include <linux/uaccess.h>

    #include <linux/sched.h>

    #include <linux/platform_device.h>

     

    #define TASK_UNINTERRUPTIBLE    2

    #define TASK_INTERRUPTIBLE    1

    #define TASK_NORMAL        (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)

     

    struct work_struct *work1;

    struct timer_list key_timer;//定义定时器

     

     

    //全局变量,初始化为0

    unsigned int key_num =0 ;

    //定义等待队列

    wait_queue_head_t key_q;

    struct resource *res_irq;

    struct resource *res_mem;

    unsigned int *key_data;

     

    //read按键

    ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)

    {

        wait_event(key_q,key_num);

        printk("<0> in kernel:key num is%d ",key_num);

        //返回内核的给用户

        copy_to_user(buf,&key_num,4);

        key_num=0;//清空按键

        return 4;

    }

     

    void work1_func(struct work_struct *work)

    {

        //启动定时器 100毫秒超时=HZ/10,HZ=1秒。jiffies是系统当前时间

        mod_timer(&key_timer,jiffies+HZ/10);

    }

    void key_timer_func(unsigned long data)

    {    //定时器超时的函数需要修改,需要判断是哪个按键超时

        

        unsigned int key_val;

        //超时的时候,就要读取data

        key_val=readw(key_data+1)&0x01;//读出一个按键EINT0的值。

        //当他被按下,就是低电平的时候,就是被按下了。才是有效的按键

        if(0==key_val)//真正按下

            key_num=1;//读取按键编号

     

        key_val=readw(key_data+1)&0x02;//读出一个按键EINT1的值。

        //当他被按下,就是低电平的时候,就是被按下了。才是有效的按键

        if(0==key_val)//真正按下

            key_num=2;

        

        //当有数据的时候,需要唤醒

        wake_up(&key_q);

    }

     

    irqreturn_t key_int(int irq, void *dev_id)

    {

        //1.检测是否发生了按键中断

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

    //前面的都是硬件相关的工作,必须在中断里面执行

    //下面是硬件无关的工作,我们把它提到中断以外的work1_func函数去处理。

        //3.打印按键值

        schedule_work(work1);

        

        return 0;

    }

     

    void key_hw_init()

    {

        unsigned short data;

     

        data = readw(key_data);

        data &= ~0b1111;//增加一个按键

        data |= 0b1010;

        writew(data,key_data);

    }

    int key_open(struct inode *node, struct file *filp)

    {

        return 0;

    }

    struct file_operations key_fops =

    {

        .open = key_open,

        .read = key_read,//增加了读取操作

    };

    struct miscdevice key_miscdevice =

    {

        .minor = 200,

        .name = "6410key",

        .fops = &key_fops,

    };

     

    static int __devinit key_probe(struct platform_device *pdev)

    {

        int ret;

        int size;

        ret=misc_register(&key_miscdevice);

        if(0!=ret)

            printk("<0>register fail! ");

        //注册中断处理程序

        res_irq=platform_get_resource(pdev,IORESOURCE_IRQ,0);

     

        request_irq(res_irq->start,key_int, IRQF_TRIGGER_FALLING,"6410key",0);

        //增加一个按键的支持

        request_irq(res_irq->end,key_int, IRQF_TRIGGER_FALLING,"6410key",0);

        

        res_mem=platform_get_resource(pdev,IORESOURCE_IRQ,0);

        size=res_mem->end-res_mem->start +1;

        key_data=ioremap(res_mem->start,size);

     

        //硬件初始化

        key_hw_init();//相应的位进行设置

     

        //2. 创建工作

        work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);

        INIT_WORK(work1, work1_func);

        //定时器初始化

        init_timer(&key_timer);

        key_timer.function=key_timer_func;

        

        //注册定时器

        add_timer(&key_timer);

     

        //初始化等待队列

        init_waitqueue_head(&key_q);

    }

    static int __devexit key_remove(struct platform_device *pdev)

    {

        return misc_deregister(&key_miscdevice);

    }

    //平台总线驱动结构

    struct platform_driver key_driver=

    {

        .driver    = {

            .name    = "my-kye",//与设备的名字一致

        },

        .probe    = key_probe,

        .remove    = __devexit_p(key_remove),

     

    };

    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);

     

    /*优化:多一个中断,gpio也进行多按键初始化,中断产生的时候要判断是哪个按键产生的中断。*/

    Make的结果:

    运行的结果如下,找到了总线驱动:

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    POJ 3160 Father Christmas flymouse (tarjan+spfa)
    HDU 1133 Buy the Ticket
    Problem F: [USACO 3.1.6]邮票
    无向图 割点模板 (转载)
    POJ 2117 Electricity (割点)
    HDU 4337 King Arthur's Knights
    Delphi2010中保存UTF8/Unicode编码文件的问题
    Delphi的泛型学习
    关于Ehlib5中的DBGridEh使用问题
    delphi中的命名空间
  • 原文地址:https://www.cnblogs.com/FORFISH/p/5188566.html
Copyright © 2020-2023  润新知