Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1807874
  • 博文数量: 134
  • 博客积分: 2488
  • 博客等级: 大尉
  • 技术积分: 7554
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-26 21:19
个人简介

1.每日自省; 2.享受人生; 3.尽力而为; 4.坚持不懈; 5.切莫急躁; 6.慎言敏行; 7.动心忍性; 8.上善若水。

文章分类

全部博文(134)

文章存档

2014年(38)

2013年(42)

2012年(15)

2011年(39)

分类: 嵌入式

2013-07-09 08:42:51


    platform模型驱动编程,需要实现platform_device(设备)与platform_driver(驱动)在platform(虚拟总线)上的注册、匹配,相互绑定,然后再做为一个普通的字符设备进行相应的应用,总之如果编写的是基于字符设备的platform驱动,在遵循并实现platform总线上驱动与设备的特定接口的情况下,最核心的还是字符设备的核心结构:cdev、 file_operations(包含它的操作函数接口)、dev_t(设备号)、设备文件(/dev)等,因为用platform机制编写的字符驱动,它的本质是字符驱动。

    在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中。那么我们编写platform模型驱动时,需要完成两个工作:
    1:实现platform驱动
    2:实现platform设备
    然而在实现这两个工作的过程中还需要实现其他的很多小工作,在后面介绍。platform模型驱动的实现过程核心架构就很简单,如下所示,注意先后顺序
platform驱动模型编程总结(基于mini2440平台的LED驱动)

    platform驱动模型三个对象:platform总线、platform设备、platform驱动。
    platform总线对应的内核结构:struct bus_type-->它包含的最关键的函数:match()
    platform设备对应的内核结构:struct platform_device-->注册:platform_device_register(unregiste)
    platform驱动对应的内核结构:struct platform_driver-->注册:platform_driver_register(unregiste)

    简单介绍下platform驱动的工作过程:设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时同名的设备与驱动,再调用驱动中的probe函数等;如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。


接下来讲解如下实现platform驱动与设备的详细过程。

1:定义驱动实例 gitLedDriver  

static struct platform_driver gitLedDriver = {
    .probe  = PlatLedProbe,
    .remove = __devexit_p(PlatLedRemove),
    .driver = {
        .name = LED_DEV_DRIVER_NAME,
        .owner = THIS_MODULE,
   }
};

2:实现驱动实例成员函数:probe  PlatLedProbe
    probe函数中要实现的功能包括:注册LED设备为混杂设备,绑定file_operations

3:platform模型驱动写字符设备仍要实现字符设备的核心结构file_operations,第一步的cdev要用到
    将file_operations结构体中open、write、read等要用到的接口函数实现。

4:实现驱动实例成员函数:remove mini2440_led_remove
    remove函数中要实现的功能与probe中的相反,进行相关设备与资源的注销。probe与remove函数其实对应前面用非platform机制写驱动的时候所实现的模块加载与卸载函数,而在platform机制中,模块的加载与卸载函数调用的是设备或驱动的注册函数。

5:实现驱动的加载与卸载函数:
    在加载函数中,调用驱动的注册函数,platform_driver_register(...);
    在卸载函数中,调用驱动的卸载函数,platform_driver_unregister(...);



实现platform设备的详细过程

platform设备的实现有两种方法:
     1:最笨的一种:直接在内核源代码里面添加相关的资源代码,\arch\arm\mach-s3c2440\mach-mini2440.c
     2:编写设备模块,用insmod命令加载该设备模块到platform总线上。
    当然用第二种了,不过这两种方法的原理与要写的代码都是一样的,但是第一种不用自己注册,因为系统初始化的时候会将mach-mini2440.c中struct platform_device *mini2440_devices[] __initdata 设备数组中包含的所有设备注册到总线上。而第二种手动注册

1:定义设备与资源实例

static struct resource gitLedResource[] = {
    [0] = {
            .start = 0x56000010,
            .end = 0x56000010 + 12,
            .flags = IORESOURCE_MEM
            },
};
static struct platform_device gitLedDev = {
    .name = LED_DEV_DRIVER_NAME,
    .id = -1,
    .num_resources = ARRAY_SIZE(gitLedResource),
    .resource = gitLedResource,
    .dev = {
        .release = LedDevRelease,
    },
};

2:实现设备的成员函数release  
static void mini2440_led_platform_device_release(struct device * dev)
{
    return ;
}

3:实现设备的加载与卸载函数:
    在加载函数中,调用设备的注册函数,platform_device_register(...);
    在卸载函数中,调用设备的卸载函数,platform_device_unregister(...);
    多个设备同时注册:platform_add_devices(struct platform_devices **devs, int num);
    struct platform_devices **devs设备数组,num包含的设备数目

    驱动与设备是否成功注册,我们都可以在/sys/bus/platform/devices(drivers)/下查看

     本实验实现了通过write或者Ioctl来控制LED,实现了可以单独开启或者关闭其中某个灯,或者一起开启或者关闭。


下面是代码清单:


#include
#include
#include
#include
#include
#include
#include
#include
#include

#include "plat-led.h"


static volatile unsigned long *gpfcon = NULL;
static volatile unsigned long *gpfdat = NULL; 
static volatile unsigned long *gpfup = NULL;


static void LedDevRelease(struct device * dev)
{
    /* do nothing */
}


static struct resource gitLedResource[] = {
    [0] = {
        .start = 0x56000010,
        .end = 0x56000010 + 12,
        .flags = IORESOURCE_MEM
    },
};
static struct platform_device gitLedDev = {
    .name = LED_DEV_DRIVER_NAME,
    .id = -1,
    .num_resources = ARRAY_SIZE(gitLedResource),
    .resource = gitLedResource,
    .dev = {
        .release = LedDevRelease,
    },
};

static int LedOpen(struct inode * inode,  struct file * file)
{
    DPRINTK("led open[kernel_space]\n");
    *gpfcon &= ~((0x3 << 10) | (0x3 << 12) | (0x3 << 14) | (0x3 << 16)); //清除相应位
    *gpfcon |= ((0x1 << 10) | (0x1 << 12) | (0x1 << 14) | (0x1 << 16)); //置为输出
    return 0;
}

static ssize_t LedRead(struct file *file, const char __user *in, size_t size, loff_t *off)
{
    DPRINTK("led read[kernel_space]\n");
    return 0;
}

static ssize_t LedWrite(struct file *file, const char __user *in, size_t size, loff_t *off)
{
    int ret = 0;
    uint8_t iaBuf[2] = {0}, i = 0;
    DPRINTK("led write[kernel_space]\n");
    ret = copy_from_user(iaBuf, in, size);
    for(i = 0; i < size; i++)
    {
        DPRINTK("iaBuf[%d] = %d\n", i, iaBuf[i]);
    }
    if(iaBuf[0] == 1)
    {
        //*gpfdat &= ~((0x1 << 5) | (0x1 << 6) | (0x1 << 7) | (0x1 << 8));
        *gpfdat &= ~(0x1 << (iaBuf[1] + 5));
    }
    else
    {
        //*gpfdat |= (0x1 << 5) | (0x1 << 6) | (0x1 << 7) | (0x1 << 8);
        *gpfdat |= 0x1 << (iaBuf[1] + 5);
    }
    return 0;
}

int LedIoctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
    int err = 0, ret = 0, iLedNum = 0;
    //检测命令的有效性
    if(_IOC_TYPE(cmd) != LED_IOC_MAGIC)
    {
        return -EINVAL;
    }
    if(_IOC_NR(cmd) > LED_IOC_MAXNR) 
    {
        return -EINVAL;
    }
    //根据命令类型,检测参数空间是否可以访问
    if(_IOC_DIR(cmd) & _IOC_READ)
    {
        err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
    }
    else if(_IOC_DIR(cmd) & _IOC_WRITE)
    {
        err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
    }
    if(err != 0) 
    {
        return -EFAULT;
    }
    DPRINTK("led ioctl[kernel_space]\n");
    //根据命令,执行相应的操作
    switch(cmd) 
    {
        case LED_IOC_ON: 
        {
            ret = __get_user(iLedNum, (int *)arg);
            *gpfdat &= ~(0x1 << (iLedNum + 5));
        }
        break;
        case LED_IOC_OFF: 
        {
            ret = __get_user(iLedNum, (int *)arg);
            *gpfdat |= 0x1 << (iLedNum + 5);
        }
        break;
        case LED_IOC_ALL_ON: 
        {
             *gpfdat &= ~((0x1 << 5) | (0x1 << 6) | (0x1 << 7) | (0x1 << 8));
        }
        break;
        case LED_IOC_ALL_OFF: 
        {
            *gpfdat |= (0x1 << 5) | (0x1 << 6) | (0x1 << 7) | (0x1 << 8);
        }
        break;
        default:
        {
            return -EINVAL;
        }
        break;
        }/* end switch(cmd) */
        return ret;
}


struct file_operations gitLedFops = {
    .owner = THIS_MODULE,
    .open = LedOpen,
    .read = LedRead,
    .write = LedWrite,
    .ioctl = LedIoctl,
};
static struct miscdevice gitLedMiscDev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &gitLedFops,
};

static int __devinit PlatLedProbe(struct platform_device *pdev)
{
    int ret = 0;
    struct resource *pIORESOURCE_MEM;
    pIORESOURCE_MEM = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    gpfcon = ioremap(pIORESOURCE_MEM->start, pIORESOURCE_MEM->end - pIORESOURCE_MEM->start);
    DPRINTK("start is %x\n", pIORESOURCE_MEM->start);
    gpfdat = gpfcon + 1;
    gpfup  = gpfcon + 2;
    ret = misc_register(&gitLedMiscDev);
    return ret;
}

static int __devexit PlatLedRemove(struct platform_device *pdev)
{
    DPRINTK("mini2440_led_remove!\n");
    iounmap(gpfcon);
    misc_deregister(&gitLedMiscDev);
    return 0;
}


static struct platform_driver gitLedDriver = {
    .probe  = PlatLedProbe,
    .remove = __devexit_p(PlatLedRemove),
    .driver = {
        .name = LED_DEV_DRIVER_NAME,
        .owner = THIS_MODULE,
    }
};

static int __init PlatLedInit(void)
{
    DPRINTK("mini2440_led_platform_device add ok!\n");
    platform_device_register(&gitLedDev);
    DPRINTK("platform_driver_for_mini2440_led init\n");
    platform_driver_register(&gitLedDriver);
}

static void __exit PlatLedExit(void)
{
    DPRINTK("platform_driver_for_mini2440_led exit\n");
    platform_driver_unregister(&gitLedDriver);
    DPRINTK("mini2440_led_platform_device remove ok!\n");
    platform_device_unregister(&gitLedDev);
}


MODULE_AUTHOR("apple_guet");
MODULE_LICENSE("GPL");


module_init(PlatLedInit);
module_exit(PlatLedExit);



头文件为:
#ifndef _PLAT_LED_H_
#define _PLAT_LED_H_

#define LED_DEV_DRIVER_NAME "plat-led"
#define DEVICE_NAME "platform-leds"


//定义幻数
#define LED_IOC_MAGIC 'k'
//定义命令
#define LED_IOC_MAXNR 4
#define LED_IOC_ON _IOW(LED_IOC_MAGIC, 0, int)
#define LED_IOC_OFF _IOW(LED_IOC_MAGIC, 1, int)
#define LED_IOC_ALL_ON _IOW(LED_IOC_MAGIC, 2, int)
#define LED_IOC_ALL_OFF _IOW(LED_IOC_MAGIC, 3, int)


#undef DEBUG
#define DEBUG
#ifdef DEBUG
#define DPRINTK printk
#else
#define DPRINTK /\
/DEBUGP
#endif

#endif


测试文件:
#include
#include
#include
#include

#include "ledApp.h"


int main(int argc, char **argv)
{
    unsigned char iaBuf[2] = {0};
    int index = 0, fd = 0, iCmd = 0;
    //检测输入的参数合法性
    if((argc != 3) || (sscanf(argv[2], "%d", &index) != 1) || (index < 1) || (index > 6))
    {
         printf("Usage: leds_test on|off 1|2|3|4\n");
         exit(1);
    }
    iaBuf[1] = (unsigned char)index - 1;
    if(strcmp(argv[1], "on") == 0)
    {
        iaBuf[0] = 1;
        iCmd = LED_IOC_ON;
    }
    else if(strcmp(argv[1], "off") == 0)
    {
        iaBuf[0] = 0;
        iCmd = LED_IOC_OFF;
    }
    else
    {
        printf("Usage: leds_test on|off 1|2|3|4\n");
        exit(1);
    }
    if(iaBuf[1] == 4)
    {
        iCmd = LED_IOC_ALL_ON;
    }
    else if(iaBuf[1] == 5)
    {
        iCmd = LED_IOC_ALL_OFF;
    }
    fd = open("/dev/platform-leds", O_RDWR);   //打开LED设备
    if(fd < 0)
    {
        printf("Open Led Device Faild!\n");
        exit(1);
    }
    if(ioctl(fd, iCmd, &iaBuf[1]) < 0)
    {
        printf("ioctl err!\n");
        exit(1);
    }
    //write(fd, iaBuf, sizeof(iaBuf));
    close(fd);    //关闭LED设备
    return 0;
}

测试头文件:
#ifndef _LED_APP_H_
#define _LED_APP_H_


#include

//定义幻数
#define LED_IOC_MAGIC 'k'
//定义命令
#define LED_IOC_MAXNR 4
#define LED_IOC_ON _IOW(LED_IOC_MAGIC, 0, int)
#define LED_IOC_OFF _IOW(LED_IOC_MAGIC, 1, int)
#define LED_IOC_ALL_ON _IOW(LED_IOC_MAGIC, 2, int)
#define LED_IOC_ALL_OFF _IOW(LED_IOC_MAGIC, 3, int)


#endif



阅读(3162) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~