Android Sensor Input類型 (二) Device Driver


SENSORS 設備驅動

1.1 Device tree 配置

在msm8909平台上,根據硬件原理圖設計得知sensors 是掛載在BLSP1 QUP1 上,所以需要在 i2c1 的節點下配置,以磁傳感器mmc3416 為例;

    mpu6050@68 {
        compatible = "invn,mpu6050";
        reg = <0x68>;
        pinctrl-names = "mpu_default","mpu_sleep";
        pinctrl-0 = <&mpu6050_default>;
        pinctrl-1 = <&mpu6050_sleep>;
        interrupt-parent = <&msm_gpio>;
        interrupts = <96 0x1>;
        vdd-supply = <&pm8909_l17>;
        vlogic-supply = <&pm8909_l6>;
        invn,gpio-int = <&msm_gpio 96 0x1>;
        invn,place = "Portrait Down";
    };
    mmc3416x@30 { /* Magnetic field sensor */
        compatible = "memsic,mmc3416x";
        reg = <0x30>;
        vdd-supply = <&pm8909_l17>;
        vio-supply = <&pm8909_l6>;
        memsic,dir = "obverse-x-axis-forward";
        memsic,auto-report;
    };

從以上兩個設備樹的信息可知 在sensors device tree 的配置中主要是配置,ic 的供電,i2c 從設備地址, 中斷gpio 腳,以及特有的sensor 屬性等,具體的作用,待解析設備驅動再做簡要的說明。

1.2 設備驅動編譯

在msm8909平台上,sensors 存放的目錄一般是選擇如下路徑下:
msm8909/code/kernel/drivers/input/misc/
以mpu6050 和 mmc3416 為例,需要在
msm8909/code/kernel/arch/arm/configs/msm8909-1gb_defconfig 中將編譯的宏控打開,如下配置:

CONFIG_SENSORS_MPU6050=y
CONFIG_SENSORS_MMC3416X=y

編譯完成后,查看out 目錄是否生成對應的.o 文件。

1.3 設備驅動解析

以mmc3416 為例解析驅動的邏輯

1.3.1 設備驅動注冊

static struct of_device_id mmc3416x_match_table[] = {
    { .compatible = "memsic,mmc3416x", },
    { },
};
static struct i2c_driver mmc3416x_driver = {
    .probe      = mmc3416x_probe,
    .remove     = mmc3416x_remove,
    .id_table   = mmc3416x_id,
    .driver     = {
        .owner  = THIS_MODULE,
        .name   = MMC3416X_I2C_NAME,
        .of_match_table = mmc3416x_match_table,
        .pm = &mmc3416x_pm_ops,
    },
};
module_i2c_driver(mmc3416x_driver);

在mmc3416x.c的驅動中,首先是使用module_i2c_driver將其注冊i2c設備總線上, 這個接口是moudle_init 和 i2c_add_driver 結合后的二次封裝,被注冊的i2c_driver是mmc3416x_driver

這里主要關注注冊信息中的 of_match_table 屬性;mmc3416x_match_table首個元素的compatible與device tree中配置的compatible相同,則mmc3416x_probe將會被調用。

1.3.2 probe 流程分析

這里以mmc3416x.c 驅動 為例,其邏輯相對而言最簡單,但是對於msm8909 平台的sensors 驅動架構來說核心結構都在。 整個probe 函數 精簡后如下:

static int mmc3416x_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct mmc3416x_data *memsic; 
//定義設備結構,后面會介紹其內容
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) ; 
//檢測申請的i2c是否可用
memsic = devm_kzalloc(&client->dev, sizeof(struct mmc3416x_data), GFP_KERNEL);
//為設備結構申請空間
if (client->dev.of_node)
mmc3416x_parse_dt(client, memsic);
 //如果獲取到設備樹node,解析其設備樹信息。
	//這里就是使用of函數族 獲取了memsic,dir及 memsic,auto-report填充到設備結構中。
else
    memsic->dir = 0;
    memsic->auto_report = 1; //如果未獲取到設備樹node,使用一個默認值。
   	memsic->i2c = client;         //填充i2c client 到設備結構中。
   	dev_set_drvdata(&client->dev, memsic); //設置device私有數據,即將設備結構填充到 device的私有數據中去。
    mutex_init(&memsic->ecompass_lock);
    mutex_init(&memsic->ops_lock); //初始化兩個互斥鎖
memsic->regmap = devm_regmap_init_i2c(client, &mmc3416x_regmap_config);
//申請以后 regmap,i2c相關的讀寫都是通過 regmap相關接口來完成。
    res = mmc3416x_power_init(memsic);   //獲取設備樹配置的 vdd,vio。
    res = mmc3416x_check_device(memsic); //通過regmap_read讀取設備id,判斷設備.
memsic->idev = mmc3416x_init_input(client);
// 輸入子系統注冊
    memsic->data_wq = NULL;
if (memsic->auto_report) {  
//如果支持自動上報,則注冊一個等待隊列
        dev_dbg(&client->dev, "auto report is enabled\n");
        INIT_DELAYED_WORK(&memsic->dwork, mmc3416x_poll);
        memsic->data_wq =
        create_freezable_workqueue("mmc3416_data_work");
    }
memsic->cdev = sensors_cdev;  
//將定義好的sensors_classdev填充到設備結構的cdev中
memsic->cdev.sensors_enable = mmc3416x_set_enable; 
//填充sensors_enable
memsic->cdev.sensors_poll_delay = mmc3416x_set_poll_delay; 
//填充sensors_poll_delay
res = sensors_classdev_register(&memsic->idev->dev, &memsic->cdev);
//綁定device注冊填充好的sensors_classde到 sensors class中。
    res = mmc3416x_power_set(memsic, false); //失能電
memsic->poll_interval = MMC3416X_DEFAULT_INTERVAL_MS; 
//設置輪詢時間間隔
}

probe總結:

平台上的sensors驅動結構,probe主要做且都會做的一個步驟主要就是以下幾步:

  • 封裝設備結構,主要是獲取電,gpio等信息,初始化用到的數據結構並填充到device私有數據中。
  • 注冊輸入子系統
  • 填充並注冊sensors_classdev到 sensor class中去
  • 設置延時工作隊列,probe結束,然后就是等待調度

1.3.3 驅動driver 分析

根據上小節的總結,我們對這幾個主要的地方做更深一步的分析。
首先先來看下sensor 設備結構的構成,這里還是以mmc3416x 為例。

struct mmc3416x_data {
struct mutex ecompass_lock;
struct mutex ops_lock; //互斥鎖
struct workqueue_struct *data_wq; //工作隊列
struct delayed_work dwork; //延時執行的work
struct sensors_classdev cdev; //sensors_class 設備結構
struct mmc3416x_vec last;
struct i2c_client *i2c; //i2c client
struct input_dev *idev; //input device
struct regulator *vdd; //2.8v 電
struct regulator *vio; //1.8v 電
struct regmap *regmap; //獲取regmap 對i2c 通訊接口的封裝
int dir; //獲取的dir
int auto_report; //獲取的是否自動上報的配置
int enable;
int poll_interval; //輪詢時間間隔的設置
int power_enabled; //上電的狀態
unsigned long timeout; //超時時間
};

相關輸入子系統的注冊:

static struct input_dev *mmc3416x_init_input(struct i2c_client *client)
{
struct input_dev *input = NULL;    
input = devm_input_allocate_device(&client->dev);  
//申請一個 input device
    input->name = "compass";
    input->phys = "mmc3416x/input0";
    input->id.bustype = BUS_I2C;
    //填充input相關的設備信息
    __set_bit(EV_ABS, input->evbit);
    //設置輸入事件為ABS類,即絕對坐標類
    input_set_abs_params(input, ABS_X, -2047, 2047, 0, 0); 
    input_set_abs_params(input, ABS_Y, -2047, 2047, 0, 0);
    input_set_abs_params(input, ABS_Z, -2047, 2047, 0, 0);
    //設置事件代碼為ABS_X, ABS_Y, ABS_Z ,並設置了abs相關的坐標范圍
    input_set_capability(input, EV_REL, REL_X);
    input_set_capability(input, EV_REL, REL_Y);
    input_set_capability(input, EV_REL, REL_Z);
// 設置了 輸入事件為REL類,即相對坐標類,支持事件代碼分別為 REL_X, REL_Y, REL_Z。
    status = input_register_device(input);  //注冊這個input設備到輸入子系統。
    return input;
}

接下來是填充sensors_classdev ,並注冊到 sensor_class中, 這里的核心就是根據sensor_class的要求,將所有信息通過sensors_classdev傳遞上去,
如果是設備信息直接賦值,如果是操作函數則通過函數指針回調。關於msm8909使用的sensor_class的架構留在下一章詳細分析,這里主要分析被傳遞的設備信息和回調的函數接口。

以下是mmc3416x用到的sensors_classdev設備信息,更多的內容請查看sensors_classdev的結構體類型定義。

static struct sensors_classdev sensors_cdev = {
    .name = "mmc3416x-mag",                     //sensor name
    .vendor = "MEMSIC, Inc",                    //廠商信息
    .version = 1,                                //版本號
    .handle = SENSORS_MAGNETIC_FIELD_HANDLE,  //
    .type = SENSOR_TYPE_MAGNETIC_FIELD,        //2表示type為 磁力傳感器
    .max_range = "1228.8",                         
    .resolution = "0.0488228125",
    .sensor_power = "0.35",
    .min_delay = 10000,
    .max_delay = 10000,
    .fifo_reserved_event_count = 0,
    .fifo_max_event_count = 0,
    .enabled = 0,
    .delay_msec = MMC3416X_DEFAULT_INTERVAL_MS,
    .sensors_enable = NULL,                      
    .sensors_poll_delay = NULL,                  
};

在probe中設置了兩個函數的回調:

	memsic->cdev.sensors_enable = mmc3416x_set_enable;
	memsic->cdev.sensors_poll_delay = mmc3416x_set_poll_delay;

這兩個函數最終的目的最終都是為例調用工作隊列的處理函數,就輪詢讀取sensor獲取的坐標信息。

下面是這兩個回調的實現:

static int mmc3416x_set_enable(struct sensors_classdev *sensors_cdev,
        unsigned int enable)
{
    struct mmc3416x_data *memsic = container_of(sensors_cdev,
            struct mmc3416x_data, cdev);
    //通過cdev獲取到sensor設備結構體。
mutex_lock(&memsic->ops_lock);
if (enable && (!memsic->enable)) {
rc = mmc3416x_power_set(memsic, true); //上電
      rc = regmap_write(memsic->regmap, MMC3416X_REG_CTRL,MMC3416X_CTRL_TM);
      // 發送TM命令 在讀數據之前
        memsic->timeout = jiffies;
        if (memsic->auto_report)
            queue_delayed_work(memsic->data_wq,
                &memsic->dwork,
                msecs_to_jiffies(memsic->poll_interval)); //調用延時工作隊列
} 
else if ((!enable) && memsic->enable) {
        if (memsic->auto_report)
            cancel_delayed_work_sync(&memsic->dwork);
        if (mmc3416x_power_set(memsic, false)) 
            //接收到enable = 0,輪訓結束,下電
    }
    memsic->enable = enable;
}

static int mmc3416x_set_poll_delay(struct sensors_classdev *sensors_cdev,
        unsigned int delay_msec)
{
    struct mmc3416x_data *memsic = container_of(sensors_cdev,
            struct mmc3416x_data, cdev);
    //通過cdev獲取到sensor設備結構體。
    mutex_lock(&memsic->ops_lock);
    if (memsic->poll_interval != delay_msec)
        memsic->poll_interval = delay_msec;
     //根據傳入的時間參數更新 延時執行的時間,就輪詢間隔。
    if (memsic->auto_report && memsic->enable)
        mod_delayed_work(system_wq, &memsic->dwork,
                msecs_to_jiffies(delay_msec));
    mutex_unlock(&memsic->ops_lock);

    return 0;
}

最后就來分析下工作隊列的處理函數是如何調用工作的,根據初始化的延時執行work的處理函數INIT_DELAYED_WORK(&memsic->dwork, mmc3416x_poll); 這個work被假如到data_wq工作隊列中。

mmc3416x_poll

static void mmc3416x_poll(struct work_struct *work)
{
    int ret;
    s8 *tmp;
    struct mmc3416x_vec vec;
    struct mmc3416x_vec report;
    struct mmc3416x_data *memsic = container_of((struct delayed_work *)work,
            struct mmc3416x_data, dwork);
//通過cdev獲取到sensor設備結構體。
    ktime_t timestamp;
    vec.x = vec.y = vec.z = 0;
    ret = mmc3416x_read_xyz(memsic, &vec);

    tmp = &mmc3416x_rotation_matrix[memsic->dir][0];
    report.x = tmp[0] * vec.x + tmp[1] * vec.y + tmp[2] * vec.z;
    report.y = tmp[3] * vec.x + tmp[4] * vec.y + tmp[5] * vec.z;
    report.z = tmp[6] * vec.x + tmp[7] * vec.y + tmp[8] * vec.z;

    timestamp = ktime_get_boottime();
    input_report_abs(memsic->idev, ABS_X, report.x);
    input_report_abs(memsic->idev, ABS_Y, report.y);
    input_report_abs(memsic->idev, ABS_Z, report.z);
    input_event(memsic->idev,
            EV_SYN, SYN_TIME_SEC,
            ktime_to_timespec(timestamp).tv_sec);
    input_event(memsic->idev,
        EV_SYN, SYN_TIME_NSEC,
        ktime_to_timespec(timestamp).tv_nsec);
    input_sync(memsic->idev);
    //上報信息
exit:
    queue_delayed_work(memsic->data_wq,
            &memsic->dwork,
            msecs_to_jiffies(memsic->poll_interval));
    //再次調度work,延時時間為poll_interval
}
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM