Android模擬器學framework和driver之傳感器篇1(linux sensor driver)

對於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 <linux/module.h>  
#include <linux/platform_device.h>  
#include <linux/slab.h>  
#include <linux/input-polldev.h>  
#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 <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/input-polldev.h>
#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函數:
 
 
<span style="color:#cc33cc;"><strong>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);</strong></span> 
<span style="color:#cc33cc;"><strong>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);</strong></span>這也沒啥好說的,大傢願意的話可以再後面在添上這個driver module的作者和出處,傳揚千裡,哈哈,我就不瞭,本人比較謙虛。 
這也沒啥好說的,大傢願意的話可以再後面在添上這個driver module的作者和出處,傳揚千裡,哈哈,我就不瞭,本人比較謙虛。這邊最主要的應該就是temperature_device_driver這個變量這是一個platform_driver結構體,在驅動註冊的時候必須把這個結構體傳進去,我們的platform設備模型就是通過這個結構體找到相應的device,然後把driver和device綁定在一起,這邊涉及到linux 設備驅動模型,這邊我也不做詳細的分析瞭,想要瞭解的話可以自己學習,個人覺得如果是做linux驅動的話瞭解linux 設備驅動模型是很重要的,這可以讓我們站在一個比較高的層次上去寫代碼。
 
OK,不扯瞭看下這個結構體:
 
 
[cpp] view plaincopyprint?<span style="background-color: rgb(255, 255, 255);"><span style="color:#cc33cc;">static struct platform_driver temperature_device_driver = { 
    .probe      =   temperature_probe, 
//  .remove     =   __devexit_p(temperature_remove),  
    .driver     = { 
        .name = "android-temperature", 
        .owner= THIS_MODULE, 
    }, 
};</span></span> 
<span style="background-color: rgb(255, 255, 255);"><span style="color:#cc33cc;">static struct platform_driver temperature_device_driver = {
       .probe            =     temperature_probe,
//     .remove          =     __devexit_p(temperature_remove),
       .driver            = {
              .name = "android-temperature",
              .owner= THIS_MODULE,
       },
};</span></span>
這邊就是定義瞭probe和remove,真實的設備的話還有會suspend,resume,early_suspend,late_resume等回調函數,在適當的時間會回調到這些函數(犀利的讀者可能看到瞭這邊remove我沒有去實現,哈哈,我比較懶,不過大傢要有一個良好的習慣,不要學我)。但是在這邊註冊瞭platform的驅動,是去找哪的platform設備呢?當然是我們自己要去實現啦,通常device端我們都會在板級的文件中去定義,我們這裡是:
 
/common/arch/arm/mach-goldfish/board-goldfish.c
 
 
[cpp] view plaincopyprint?<strong><span style="color:#cc33cc;">struct platform_device android_temperature_device = { 
    .name="android-temperature", 
    .id=-1, 
}; 
</span></strong> 
static void __init goldfish_init(void) 

    platform_device_register(&goldfish_pdev_bus_device); 
<span style="color:#cc33cc;"><strong>   platform_device_register(&android_temperature_device);</strong></span> 

<strong><span style="color:#cc33cc;">struct platform_device android_temperature_device = {
       .name="android-temperature",
       .id=-1,
};
</span></strong>
static void __init goldfish_init(void)
{
       platform_device_register(&goldfish_pdev_bus_device);
<span style="color:#cc33cc;"><strong>      platform_device_register(&android_temperature_device);</strong></span>
}大傢註意。這邊的name和driver中platform_driver中name用該一樣,不然他們怎麼可以綁定在一起呢,不然他們怎麼會找到對方呢,有緣千裡來相會嘛,對不?
 
OK,當我們的driver找到瞭device的時候會執行probe回調函數,也就是這裡的temperature_probe函數,好,我們來看一下這個probe函數:
 
 
[cpp] view plaincopyprint?<strong><span style="color:#cc33cc;">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; 
}</span></strong> 
<strong><span style="color:#cc33cc;">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;
}</span></strong>
這邊做的都是一些初始化的事情,我們這邊首先給我們的機構體分配內存,然後給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?<span style="color:#cc33cc;"><strong>#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); 
}</strong></span> 
<span style="color:#cc33cc;"><strong>#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);
}</strong></span>
 
這裡我們上報的數據就是這個tempValue,會每隔一段時間自增1,直到100再回到0,。
 
OK,驅動介紹完,接下來就可以把驅動編譯進goldfish裡面,然後運行模擬器,使用adb進入:
 
 
<strong><span style="color:#cc33cc;">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 
# </span></strong> 
<strong><span style="color:#cc33cc;">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
# </span></strong>
大傢可以看到我這邊cat出name就是自己寫進去的那個名字,初步測試驅動ok接下來下一篇中給大傢介紹下編譯生成一個tool來測試驅動功能。
 

摘自 zhangjie201412的專欄

發佈留言