2018茶学 博士 招聘:Android模拟器学framework和driver之传感器篇1(linux sensor driver)

来源:百度文库 编辑:中财网 时间:2024/05/04 09:22:23
对于android模拟器开发环境的搭建这里我就不多说了,网上google下一大堆,还有就是android 模拟器的kernel使用的是goldfish的kernel,可以使用git得到源码,然后就可以编译了,大家还是可以参考罗老师的博客。。。
 
 
 
 
在这里我准备编写一个温度传感器的虚拟driver,之前写过g-sensor和light sensor,所以不想写了,换个新鲜的,其实驱动架构都是一样的,OK 分化不多说,下面就介绍一下这个驱动。
 
 
 
 
在这里,我比较偷懒的使用了linux的一个iio子系统,这是一个不成熟的子系统,所以被放到源码陌路下面的/drvers/staging中,对于这个子系统,我也只是粗略的看过它的驱动模型,好吧^0^,不过个人觉得这个子系统还是蛮简单使用的,而却里面的api不是很多,相信大家随便分析下就能搞懂了。
 
 
 
 
OK,首先是头文件
 
/common/drivers/staging/iio/temperature/android-temperature.h
 
 
[cpp] view plaincopyprint?#include   
#include   
#include   
#include   
#include "../iio.h"  
 
#define POLL_INTERVAL   2000    //set poll time  
 
struct temperature_drvdata { 
    struct iio_dev *indio_dev; 
    struct input_polled_dev *poll_input; 
    //....reserved  
}; 
#include
#include
#include
#include
#include "../iio.h"
 
#define POLL_INTERVAL     2000       //set poll time
 
struct temperature_drvdata {
       struct iio_dev *indio_dev;
       struct input_polled_dev *poll_input;
       //....reserved
};
相信大家看到了这个头文件就差不多知道驱动是怎么写的了吧,我选用的是platform device driver,driver layer向user space传送数据时通过input sybsystem传送的,这也是android sensor驱动比较主流的做法,还有一些做法是直接往自己创建的sysfs中写数据,这也是一中比较简单的做法,事件的触控方式我选用的是poll方式,因为这里我写的驱动是一个虚拟的设备,一般出发方式会选用中断触发,而我这个驱动选择每隔一段时间往user space上报数据,时间间隔就是这里的POLL_INTERVAL这个宏设定的。
 
说了这么多都没有看到驱动的代码真不好意思,下面来分析下驱动的代码。
 
/common/drivers/staging/iio/temperature/android-temperature.c
 
 
首先是init和exit函数:
 
 
static int __init temperature_init(void) 

    printk(KERN_INFO "temperature init...\n"); 
    return platform_driver_register(&temperature_device_driver); 

 
static void __exit temperature_exit(void) 

    platform_driver_unregister(&temperature_device_driver); 

 
module_init(temperature_init); 
module_exit(temperature_exit);
 
static int __init temperature_init(void)
{
       printk(KERN_INFO "temperature init...\n");
       return platform_driver_register(&temperature_device_driver);
}
 
static void __exit temperature_exit(void)
{
       platform_driver_unregister(&temperature_device_driver);
}
 
module_init(temperature_init);
module_exit(temperature_exit);
这也没啥好说的,大家愿意的话可以再后面在添上这个driver module的作者和出处,传扬千里,哈哈,我就不了,本人比较谦虚。 
这也没啥好说的,大家愿意的话可以再后面在添上这个driver module的作者和出处,传扬千里,哈哈,我就不了,本人比较谦虚。这边最主要的应该就是temperature_device_driver这个变量这是一个platform_driver结构体,在驱动注册的时候必须把这个结构体传进去,我们的platform设备模型就是通过这个结构体找到相应的device,然后把driver和device绑定在一起,这边涉及到linux 设备驱动模型,这边我也不做详细的分析了,想要了解的话可以自己学习,个人觉得如果是做linux驱动的话了解linux 设备驱动模型是很重要的,这可以让我们站在一个比较高的层次上去写代码。
 
OK,不扯了看下这个结构体:
 
 
[cpp] view plaincopyprint?static struct platform_driver temperature_device_driver = { 
    .probe      =   temperature_probe, 
//  .remove     =   __devexit_p(temperature_remove),  
    .driver     = { 
        .name = "android-temperature", 
        .owner= THIS_MODULE, 
    }, 
};
 
static struct platform_driver temperature_device_driver = {
       .probe            =     temperature_probe,
//     .remove          =     __devexit_p(temperature_remove),
       .driver            = {
              .name = "android-temperature",
              .owner= THIS_MODULE,
       },
};

这边就是定义了probe和remove,真实的设备的话还有会suspend,resume,early_suspend,late_resume等回调函数,在适当的时间会回调到这些函数(犀利的读者可能看到了这边remove我没有去实现,哈哈,我比较懒,不过大家要有一个良好的习惯,不要学我)。但是在这边注册了platform的驱动,是去找哪的platform设备呢?当然是我们自己要去实现啦,通常device端我们都会在板级的文件中去定义,我们这里是:
 
/common/arch/arm/mach-goldfish/board-goldfish.c
 
 
[cpp] view plaincopyprint?struct platform_device android_temperature_device = { 
    .name="android-temperature", 
    .id=-1, 
}; 
 
static void __init goldfish_init(void) 

    platform_device_register(&goldfish_pdev_bus_device); 
   platform_device_register(&android_temperature_device); 

struct platform_device android_temperature_device = {
       .name="android-temperature",
       .id=-1,
};

static void __init goldfish_init(void)
{
       platform_device_register(&goldfish_pdev_bus_device);
      platform_device_register(&android_temperature_device);
}大家注意。这边的name和driver中platform_driver中name用该一样,不然他们怎么可以绑定在一起呢,不然他们怎么会找到对方呢,有缘千里来相会嘛,对不?
 
OK,当我们的driver找到了device的时候会执行probe回调函数,也就是这里的temperature_probe函数,好,我们来看一下这个probe函数:
 
 
[cpp] view plaincopyprint?static int temperature_probe(struct platform_device *pdev) 

    struct temperature_drvdata *ddata; 
    struct input_dev *idev; 
    int err=0; 
    printk(KERN_INFO "%s\n",__FUNCTION__); 
    ddata=kzalloc(sizeof(struct temperature_drvdata),GFP_KERNEL); 
    if(!ddata) { 
        printk(KERN_INFO "failed to allocate memory...\n"); 
        err=-ENOMEM; 
        goto exit; 
    } 
    //----for iio device  
    ddata->indio_dev=iio_allocate_device(); 
    if(!ddata->indio_dev){ 
        printk(KERN_INFO "error to allocate iio device memory....\n"); 
        goto exit_iio_alloc; 
    } 
    ddata->indio_dev->attrs = &temperature_attr_group; 
    ddata->indio_dev->dev.parent = &pdev->dev; 
    ddata->indio_dev->dev_data = (void *)(ddata); 
    ddata->indio_dev->driver_module = THIS_MODULE; 
    ddata->indio_dev->modes = INDIO_DIRECT_MODE; 
    err = iio_device_register(ddata->indio_dev); 
    if(err){ 
        printk(KERN_INFO "iio device register failed....\n"); 
        goto exit_iio_reg; 
    } 
    //-----for input device  
    ddata->poll_input=input_allocate_polled_device(); 
    if(!(ddata->poll_input)){  
        err=-ENOMEM; 
        printk(KERN_INFO "input poll allocate failed...\n"); 
        goto exit_iio_reg; 
    } 
    ddata->poll_input->poll=temperature_dev_poll; 
    ddata->poll_input->poll_interval = POLL_INTERVAL; 
    idev=ddata->poll_input->input; 
    idev->name = "Android Temperature Sensor"; 
    idev->phys = "temperature-sensor/input0"; 
    idev->dev.parent=&pdev->dev; 
    idev->id.bustype=BUS_HOST; 
    idev->id.vendor=0x1234; 
    idev->id.product=0x0123; 
    idev->id.version=0x0012; 
    __set_bit(EV_ABS,idev->evbit); 
    __set_bit(ABS_PRESSURE,idev->absbit); 
    __set_bit(EV_SYN,idev->evbit); 
    input_set_abs_params(idev,ABS_PRESSURE,0,65535,0,0); 
    err=input_register_polled_device(ddata->poll_input); 
    if(err){ 
        printk(KERN_INFO "input register poll device failed....\n"); 
        goto err_reg_poll; 
    } 
    platform_set_drvdata(pdev,ddata); 
    return 0; 
err_reg_poll: 
    input_free_polled_device(ddata->poll_input); 
exit_iio_reg: 
    iio_free_device(ddata->indio_dev); 
exit_iio_alloc: 
    kfree(ddata); 
exit: 
    return err; 
}
 
static int temperature_probe(struct platform_device *pdev)
{
       struct temperature_drvdata *ddata;
       struct input_dev *idev;
       int err=0;
       printk(KERN_INFO "%s\n",__FUNCTION__);
       ddata=kzalloc(sizeof(struct temperature_drvdata),GFP_KERNEL);
       if(!ddata) {
              printk(KERN_INFO "failed to allocate memory...\n");
              err=-ENOMEM;
              goto exit;
       }
       //----for iio device
       ddata->indio_dev=iio_allocate_device();
       if(!ddata->indio_dev){
              printk(KERN_INFO "error to allocate iio device memory....\n");
              goto exit_iio_alloc;
       }
       ddata->indio_dev->attrs = &temperature_attr_group;
       ddata->indio_dev->dev.parent = &pdev->dev;
       ddata->indio_dev->dev_data = (void *)(ddata);
       ddata->indio_dev->driver_module = THIS_MODULE;
       ddata->indio_dev->modes = INDIO_DIRECT_MODE;
       err = iio_device_register(ddata->indio_dev);
       if(err){
              printk(KERN_INFO "iio device register failed....\n");
              goto exit_iio_reg;
       }
       //-----for input device
       ddata->poll_input=input_allocate_polled_device();
       if(!(ddata->poll_input)){
              err=-ENOMEM;
              printk(KERN_INFO "input poll allocate failed...\n");
              goto exit_iio_reg;
       }
       ddata->poll_input->poll=temperature_dev_poll;
       ddata->poll_input->poll_interval = POLL_INTERVAL;
       idev=ddata->poll_input->input;
       idev->name = "Android Temperature Sensor";
       idev->phys = "temperature-sensor/input0";
       idev->dev.parent=&pdev->dev;
       idev->id.bustype=BUS_HOST;
       idev->id.vendor=0x1234;
       idev->id.product=0x0123;
       idev->id.version=0x0012;
       __set_bit(EV_ABS,idev->evbit);
       __set_bit(ABS_PRESSURE,idev->absbit);
       __set_bit(EV_SYN,idev->evbit);
       input_set_abs_params(idev,ABS_PRESSURE,0,65535,0,0);
       err=input_register_polled_device(ddata->poll_input);
       if(err){
              printk(KERN_INFO "input register poll device failed....\n");
              goto err_reg_poll;
       }
       platform_set_drvdata(pdev,ddata);
       return 0;
err_reg_poll:
       input_free_polled_device(ddata->poll_input);
exit_iio_reg:
       iio_free_device(ddata->indio_dev);
exit_iio_alloc:
       kfree(ddata);
exit:
       return err;
}

这边做的都是一些初始化的事情,我们这边首先给我们的机构体分配内存,然后给iio device分配空间,然后注册iio device,然后注册input_polled_device这里可以参考input)poll的源码,主要就是内嵌了一个工作队列来poll数据,这里不多说读者可以自行去分析。
 
这里最重要的有2点我提一下,首先就是我们poll数据的回调函数被挂在ddata->poll_input->poll=temperature_dev_poll;参考源码这个回调函数是什么时候被执行的呢,其实input_polled_dev还有几个回调函数,其中有一个open和close函数,当user space去open input下面的这个event的时候poll回调函数就会一直执行,时间间隔为我们定义的interval这个参数。还有一点就是iio 设备驱动上面挂的文件系统就是ddata->indio_dev->attrs = &temperature_attr_group;用法很简单吧,这边我只是注册了一个name的文件节点,user space可以去读写这个节点,一般我们写驱动的时候可以用这个文件节点来开关我们的设备。
 
OK,接下来就是一些事件的处理,看如下代码:
 
 
[cpp] view plaincopyprint?#include "android-temperature.h" 
 
 
static ssize_t temperature_show_name(struct device *dev, 
    struct device_attribute *attr, char *buf) 
{    
    return sprintf(buf, "%s\n", "android-temperature sensor"); 

 
static IIO_DEVICE_ATTR(name, S_IRUGO, temperature_show_name, NULL,0); 
 
static struct attribute *temperature_attributes[] = { 
    &iio_dev_attr_name.dev_attr.attr, 
    NULL 
}; 
 
static const struct attribute_group temperature_attr_group = { 
    .attrs = temperature_attributes, 
}; 
 
static int tempValue; 
static void temperature_dev_poll(struct input_polled_dev *dev) 

    printk(KERN_INFO "Current Temperature: %d\n",tempValue); 
    if((tempValue++)==100) 
        tempValue=0; 
    input_event(dev->input,EV_ABS,ABS_PRESSURE,tempValue); 
    input_sync(dev->input); 
}
 
#include "android-temperature.h"
 
 
static ssize_t temperature_show_name(struct device *dev,
       struct device_attribute *attr, char *buf)
{    
       return sprintf(buf, "%s\n", "android-temperature sensor");
}
 
static IIO_DEVICE_ATTR(name, S_IRUGO, temperature_show_name, NULL,0);
 
static struct attribute *temperature_attributes[] = {
       &iio_dev_attr_name.dev_attr.attr,
       NULL
};
 
static const struct attribute_group temperature_attr_group = {
       .attrs = temperature_attributes,
};
 
static int tempValue;
static void temperature_dev_poll(struct input_polled_dev *dev)
{
       printk(KERN_INFO "Current Temperature: %d\n",tempValue);
       if((tempValue++)==100)
              tempValue=0;
       input_event(dev->input,EV_ABS,ABS_PRESSURE,tempValue);
       input_sync(dev->input);
}

 
这里我们上报的数据就是这个tempValue,会每隔一段时间自增1,直到100再回到0,。
 
OK,驱动介绍完,接下来就可以把驱动编译进goldfish里面,然后运行模拟器,使用adb进入:
 
 
root@jay:/home/jay/android/common# adb shell 
# cd sys/bus/iio/devices/ 
# ls 
device0 
device1 
device2 
# cd device2 
# ls 
uevent 
subsystem 
power 
name 
# cat name 
android-temperature sensor 
#
 
root@jay:/home/jay/android/common# adb shell
# cd sys/bus/iio/devices/
# ls
device0
device1
device2
# cd device2
# ls
uevent
subsystem
power
name
# cat name
android-temperature sensor
#

大家可以看到我这边cat出name就是自己写进去的那个名字,初步测试驱动ok接下来下一篇中给大家介绍下编译生成一个tool来测试驱动功能。