• Linux驱动之平台设备


    <平台设备设备驱动>

    a:背景:

    平台总线是Linux2.6的设备驱动模型中,关心总线,设备和驱动这3个实体。一个现实的Linux设备和驱动通常需要挂接在一种总线上(比如本身依附于PCI,USB,IIC,SPI等设备而言)。但是在嵌入式系统里面,SoC系统即集成的独立外设控制器,挂接在SoC内存空间的外设却没有这样的总线依附,为了和Linux设备驱动模型理论相互统一,Linux系统开发了Platform_bus这种虚拟总线,相应的设备叫做platform_device ,相应的驱动叫platform_driver。引入的一种虚拟总线,其优势是采用了总总线的模型对设备和驱动进行管理,同时提高程序的可移植性。

    b:优势:

    Linux platform_driver机制和传统的device_driver 机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)

     

    <平台设备驱动开发流程>

    定义好了platform_device结构体后就可以调用函数platform_add_devices向系统中添加该设备了,之后可以调用platform_device_register()进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register之前,原因是因为驱动注册时需要匹配内核中所以已注册的设备名。

     

    <平台总线>

    a:内核数据结构

    struct bus_type platform_bus_type = {  

        .name       = "platform",  

        .dev_attrs  = platform_dev_attrs,  

        .match      = platform_match,       //设备和驱动使用match函数来判断是否匹配  

        .uevent     = platform_uevent,  

        .pm     = PLATFORM_PM_OPS_PTR,  

     

    };

    a-1:函数platform_match()

    /* platform_match函数用于匹配总线中的驱动和设备 */  

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

      

        /* match against the id table first */  

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

     

    }

    platform_match函数首先判断是否由id_table,如果有则使用id_table来进行匹配,否则,判断platform_device和platform_driver成员里的name,如果二者的name字段相同则匹配,如果匹配则调用platform_driver的probe函数。

     

    <平台设备>

    a:内核数据结构

    struct platform_device{

    const char *name ;//设备名

    int id;//设备编号,配合设备使用

    struct device dev;

    u32 num_resources;

    struct resource  *resource; //设备资源

    }

    a-1:设备资源

    定义硬件资源,比如设备内存,中断号,DMA通道 

    struct resource{

    resource_size_t char;

    resource_size_t end;

    const char *name;

    unsigned long flags; //用于表明多个资源中的某一种资源,比如中断号,内存。

    struct resource *parent,*siling ,*child;

    };

    a-1-1:"unsigned long flags",这里的flags可以取以下值,表示不同的设备资源

    IORESOURCE_IO//IO资源

    IORESOURCE_MEN//设备内存资源

    IORESOURCE_IRQ//设备中断资源

    IORESOURCE_DMA//设备DMA资源

    a-1-2:一般驱动中调用该函数获得这些资源

    int platform_get_irq(struct platform_device *dev, unsigned int num);

     

    b:注册平台设备

     

    int platform _device _register (struct platform_device *pdev )

     

    <平台驱动>

    a:内核数据结构

    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 (
    *suspend_late)(struct platform_device *, pm_message_t state);
        int (
    *resume_early)(struct platform_device *);
        int (
    *resume)(struct platform_device *);
        struct pm
    _ext_ops *pm;
        struct device
    _driver driver;
    };

    a-1:函数int (*probe)(struct platform_device *);

    static int s3c24xx_i2c_probe(struct platform_device *pdev)
    {

    struct s3c24xx_i2c *i2c = &s3c24xx_i2c;

    struct resource *res;

    int ret;

       /* find the clock and enable it */

    i2c->dev = &pdev->dev;

    i2c->clk = clk_get(&pdev->dev, "i2c");

    if (IS_ERR(i2c->clk)) {

        dev_err(&pdev->dev, "cannot get clock ");

        ret = -ENOENT;

        goto out;

    }

    dev_dbg(&pdev->dev, "clock source %p ", i2c->clk);

    clk_enable(i2c->clk);

    /* map the registers */

       res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* 获取设备的IO资源地址 */

    if (res == NULL) {

        dev_err(&pdev->dev, "cannot find IO resource ");

        ret = -ENOENT;

        goto out;

    }

       i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1, pdev->name); /* 申请这块IO Region */

    if (i2c->ioarea == NULL) {

        dev_err(&pdev->dev, "cannot request IO ");

        ret = -ENXIO;

        goto out;

    }

       i2c->regs = ioremap(res->start, (res->end-res->start)+1); /* 映射至内核虚拟空间 */

    if (i2c->regs == NULL) {

        dev_err(&pdev->dev, "cannot map IO ");

        ret = -ENXIO;

        goto out;

    }

       dev_dbg(&pdev->dev, "registers %p (%p, %p) ", i2c->regs, i2c->ioarea, res);

    /* setup info block for the i2c core */

    i2c->adap.algo_data = i2c;

    i2c->adap.dev.parent = &pdev->dev;

    /* initialise the i2c controller */

    ret = s3c24xx_i2c_init(i2c);

    if (ret != 0)

        goto out;

    /* find the IRQ for this unit (note, this relies on the init call to ensure no current IRQs pending */

    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); /* 获取设备IRQ中断号 */

    if (res == NULL) {

        dev_err(&pdev->dev, "cannot find IRQ ");

        ret = -ENOENT;

        goto out;

    }

    ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED, pdev->name, i2c);申请IRQ

    ……

    return ret;

    }

    b:注册总线驱动

    int platform_driver_register(struct platform_driver*)

     

    <平台私有数据>

    a:struct platform_data{ }

    设备除了可以再bsp中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断,内存,DMA通道以外,可能还会有一些配置信息,而这些配置信息也依赖于板,不宜直接放置在设备驱动本身,因此platform也提供了platform_data的支持,platform_data的形式是自定义的,比如对于dm9000网卡来说,platform_data中可以存放mac地址,总线宽度,板上有误eeprom等信息。

    a-1:如对于 DM9000 网卡而言, platform_data 为一个 dm9000_plat_data 结构体,我们就可以将 MAC 地址、总线宽度、板上有无 EEPROM 信息等放入 platform_data

    static struct dm9000_plat_data ldd6410_dm9000_platdata = {
       .flags = DM9000
    _PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM,
       .dev
    _addr = { 0x0, 0x16, 0xd4, 0x9f, 0xed, 0xa4 },
    };
    static struct platform_device ldd6410_dm9000 = {
       .name= "dm9000",
       .id= 0,
       .num
    _resources= ARRAY_SIZE(ldd6410_dm9000_resource),
       .resource =ldd6410
    _dm9000_resource,
       .dev = {
           .platform_data = &ldd6410_dm9000_platdata, //定义和初始化来自上面
       }
    };

     

    <凭他设备驱动实例>

    #include <linux/module.h>  

    #include <linux/device.h>  

    #include <linux/platform_device.h>  

    #include <linux/ioport.h>  

      

    static struct resource beep_resource[] =  

    {  

        [0] ={  

            .start = 0x114000a0,  

            .end =  0x114000a0 + 0x4,  

            .flags = IORESOURCE_MEM,  

        },  

      

        [1] ={  

            .start = 0x139D0000,  

            .end =  0x139D0000 + 0x14,  

            .flags = IORESOURCE_MEM,  

        }  

    };  

      

    static void hello_release(struct device *dev)  

    {  

        printk("hello_release ");  

        return ;  

    }  

      

      

      

    static struct platform_device hello_device=  

    {  

        .name = "bigbang",  

        .id = -1,  

        .dev.release = hello_release,  

        .num_resources = ARRAY_SIZE(beep_resource),  

        .resource = beep_resource,  

    };  

      

    static int hello_init(void)  

    {  

        printk("hello_init");  

        return platform_device_register(&hello_device);  

    }  

      

    static void hello_exit(void)  

    {  

        printk("hello_exit");  

        platform_device_unregister(&hello_device);  

        return;  

    }  

      

    MODULE_LICENSE("GPL");  

    module_init(hello_init);  

    module_exit(hello_exit);  

     

    2、driver.c

    [cpp] view plain copy 

    #include <linux/module.h>  

    #include <linux/fs.h>  

    #include <linux/cdev.h>  

    #include <linux/device.h>  

    #include <linux/platform_device.h>  

    #include <asm/io.h>  

      

    static int major = 250;  

    static int minor=0;  

    static dev_t devno;  

    static struct class *cls;  

    static struct device *test_device;  

               

    #define TCFG0         0x0000                 

    #define TCFG1         0x0004                              

    #define TCON          0x0008               

    #define TCNTB0        0x000C            

    #define TCMPB0        0x0010             

      

    static unsigned int *gpd0con;  

    static void *timer_base;  

      

    #define  MAGIC_NUMBER    'k'  

    #define  BEEP_ON    _IO(MAGIC_NUMBER    ,0)  

    #define  BEEP_OFF   _IO(MAGIC_NUMBER    ,1)  

    #define  BEEP_FREQ   _IO(MAGIC_NUMBER   ,2)  

      

    static void fs4412_beep_init(void)  

    {     

        writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);  

        writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);   

        writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );   

      

        writel (500, timer_base +TCNTB0  );  

        writel (250, timer_base +TCMPB0 );  

        writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );   

    }  

      

    void fs4412_beep_on(void)  

    {  

        writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );  

    }  

      

    void fs4412_beep_off(void)  

    {  

        writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );  

    }  

      

    static void beep_unmap(void)  

    {  

            iounmap(gpd0con);  

            iounmap(timer_base);  

    }  

      

    static int beep_open (struct inode *inode, struct file *filep)  

    {  

        fs4412_beep_on();  

        return 0;  

    }  

      

    static int beep_release(struct inode *inode, struct file *filep)  

    {  

         fs4412_beep_off();  

         return 0;  

    }  

      

    #define BEPP_IN_FREQ 100000  

    static void beep_freq(unsigned long arg)  

    {  

        writel(BEPP_IN_FREQ/arg, timer_base +TCNTB0  );  

        writel(BEPP_IN_FREQ/(2*arg), timer_base +TCMPB0 );  

      

    }  

      

    static long beep_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)  

    {  

        switch(cmd)  

        {  

            case BEEP_ON:  

                fs4412_beep_on();  

                break;  

            case BEEP_OFF:  

                fs4412_beep_off();  

                break;  

            case BEEP_FREQ:  

                beep_freq( arg );  

                break;  

            default :  

                return -EINVAL;  

        }  

        return 0;  

    }  

      

    static struct file_operations beep_ops=  

    {  

        .open     = beep_open,  

        .release = beep_release,  

        .unlocked_ioctl      = beep_ioctl,  

    };  

      

    static int beep_probe(struct platform_device *pdev)  

    {  

        int ret;      

        printk("match ok!");  

          

        gpd0con = ioremap(pdev->resource[0].start,pdev->resource[0].end - pdev->resource[0].start);  

        timer_base = ioremap(pdev->resource[1].start, pdev->resource[1].end - pdev->resource[1].start);  

      

        devno = MKDEV(major,minor);  

        ret = register_chrdev(major,"beep",&beep_ops);  

      

        cls = class_create(THIS_MODULE, "myclass");  

        if(IS_ERR(cls))  

        {  

            unregister_chrdev(major,"beep");  

            return -EBUSY;  

        }  

      

        test_device = device_create(cls,NULL,devno,NULL,"beep");//mknod /dev/hello  

        if(IS_ERR(test_device))  

        {  

            class_destroy(cls);  

            unregister_chrdev(major,"beep");  

            return -EBUSY;  

        }  

          

        fs4412_beep_init();  

          

        return 0;  

    }  

      

    static int beep_remove(struct platform_device *pdev)  

    {  

        beep_unmap();  

        device_destroy(cls,devno);  

        class_destroy(cls);   

        unregister_chrdev(major,"beep");  

      

        return 0;  

    }  

      

      

    static struct platform_driver beep_driver=  

    {  

        .driver.name = "bigbang",  

        .probe = beep_probe,  

        .remove = beep_remove,  

    };  

       

      

    static int beep_init(void)  

    {  

        printk("beep_init");  

          

        return platform_driver_register(&beep_driver);  

    }  

      

    static void beep_exit(void)  

    {  

        printk("beep_exit");  

        platform_driver_unregister(&beep_driver);  

          

        return;  

    }  

      

      

    MODULE_LICENSE("GPL");  

    module_init(beep_init);  

    module_exit(beep_exit);  

     

    3、makefile   

    [cpp] view plain copy 

    ifneq  ($(KERNELRELEASE),)  

    obj-m:=device.o driver.o  

    $(info "2nd")  

    else  

    #KDIR := /lib/modules/$(shell uname -r)/build  

    KDIR := /home/fs/linux/linux-3.14-fs4412  

    PWD:=$(shell pwd)  

    all:  

        $(info "1st")  

        make -C $(KDIR) M=$(PWD) modules  

    clean:  

        rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order  

    endif  

     

    4、test.c

    [cpp] view plain copy 

    #include <sys/types.h>  

    #include <sys/stat.h>  

    #include <fcntl.h>  

    #include <stdio.h>  

      

    main()  

    {  

        int fd,i,lednum;  

      

        fd = open("/dev/beep",O_RDWR);  

        if(fd<0)  

        {  

            perror("open fail ");  

            return ;  

        }  

          

        sleep(10);  

        close(fd);  

    }

    <wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

  • 相关阅读:
    linux系统root用户忘记密码的重置方法
    Linux系统的初始化配置
    LINUX awk 函数
    随机产生一个密码,要求同时包含大小写以及数字这三种字符。
    sed 函数 linux
    grep 函数
    linux sort 函数
    从零开始的JAVA -4. 运算符与表达式
    cp
    PATH
  • 原文地址:https://www.cnblogs.com/big-devil/p/8590028.html
Copyright © 2020-2023  润新知