accelerometer Sensor (加速度传感器)驱动上报数据流程分析

一.相关文件位置

        kernel-4.19/drivers/misc/mediatek/sensors-1.0/accelerometer/accel.c

        kernel-4.19/drivers/misc/mediatek/sensors-1.0/accelerometer/accelhub/accelhub.c

        kernel-4.19/drivers/misc/mediatek/sensors-1.0/hwmon/sensor_event/sensor_event.c

        kernel-4.19/drivers/misc/mediatek/sensors-1.0/sensorHub/SCP_nanoHub.c

        kernel-4.19/drivers/misc/mediatek/sensors-1.0/nanohub/main.c

文件功能描述
accel.caccelerometer Sensor 驱动
accelhub.c收集accelerometer Sensor数据
sensor_event.c封装sensor驱动的文件操作方法
SCP_nanoHub.c收集处理sensor数据

二.数据上报流程分析

        2.1 poll机制

        获取sensor数据,使用poll的方法获取数据,poll可以监测文件的状态并返回,还可以设置超时时间。在这个驱动中的poll函数里使用poll_wait()函数将进程挂起,直到有数据写入时唤醒即可返回。poll机制参考

        POLL机制_One Piece&的博客-CSDN博客_poll机制

        2.2 accelerometer Sensor在sensor1.0中的数据上报流程

        在kernel-4.19/drivers/misc/mediatek/sensors-1.0/accelerometer/accel.c中可以看到文件操作结构如下:

static const struct file_operations accel_fops = {
	.owner = THIS_MODULE,
	.open = accel_open,
	.read = accel_read,
	.poll = accel_poll,
};

        accel.c中的poll调用sensor_event.c文件中的sensor_event_poll()函数(alsps.c光感和mag.c磁场传感器等也是)。sensor_event_poll()函数实现如下:

unsigned int sensor_event_poll(unsigned char handle, struct file *file,poll_table *wait) {

    struct sensor_event_client *client = &event_obj->client[handle];
    unsigned int mask = 0;
    poll_wait(file, &client->wait, wait);
    if (client->head != client->tail) {
        mask |= POLLIN | POLLRDNORM;
    }
    return mask;
    
}

        可以看到它调用了poll_wait()函数将当前进程添加到等待队列,唤醒它的地方在这个文件的sensor_input_event()函数中。该函数实现如下:

int sensor_input_event(unsigned char handle, const struct sensor_event *event) {
    struct sensor_event_client *client = &event_obj->client[handle];
    unsigned int dummy = 0;

    spin_lock(&client->buffer_lock);

    if (unlikely(client->buffull == true)) {
        pr_err_ratelimited(
                "input buffull, handle:%d, head:%d, tail:%d\n", handle,
                client->head, client->tail);
        spin_unlock(&client->buffer_lock);
        wake_up_interruptible(&client->wait);
        return -1;
    }
    client->buffer[client->head++] = *event;
    client->head &= client->bufsize - 1;
    dummy = client->head + 1;
    dummy &= client->bufsize - 1;
    if (unlikely(dummy == client->tail))
        client->buffull = true;
    spin_unlock(&client->buffer_lock);

    wake_up_interruptible(&client->wait);
    return 0;
}

        在accel.c文件中可以搜索到,在acc_data_report()中调用到了sensor_input_event(),这个名字可以猜测得到它是在汇报传感器的数据,也就是说sensor有汇报数据的时候就能将poll唤醒,通知上层有数据可以读。acc_data_report()如下:

int acc_data_report(struct acc_data *data) {
    struct sensor_event event;
    int err = 0;

    memset(&event, 0, sizeof(struct sensor_event));

    event.time_stamp = data->timestamp;
    event.flush_action = DATA_ACTION;
    event.status = data->status;
    event.word[0] = data->x;
    event.word[1] = data->y;
    event.word[2] = data->z;
    event.reserved = data->reserved[0];

    if (event.reserved == 1)
        mark_timestamp(ID_ACCELEROMETER, DATA_REPORT,
                       ktime_get_boot_ns(), event.time_stamp);
    err = sensor_input_event(acc_context_obj->mdev.minor, &event);
    return err;
}

        acc_data_report()函数又经过了accelhub.c中的gsensor_recv_data()函数调用,再查gsensor_recv_data(),发现它在accelhub.c的probe函数中通过调用scp_sensorHub_data_registration()函数将它和传感器ID建立了关系:

err = scp_sensorHub_data_registration(ID_ACCELEROMETER,gsensor_recv_data);

        scp_sensorHub_data_registration()函数位于SCP_nanoHub.c,可以看到这里把gsensor_recv_data()通过函数指针的方式放到了dispatch_data_cb[]数组中,以sensor的ID作为数组的索引scp_sensorHub_data_registration()函数如下:

int scp_sensorHub_data_registration(uint8_t sensor,SCP_sensorHub_handler handler) {
    struct SCP_sensorHub_data *obj = obj_data;

    if (sensor > ID_SENSOR_MAX_HANDLE)
        /*......*/
    if (handler == NULL)
        /*......*/
    obj->dispatch_data_cb[sensor] = handler;
    
    return 0;
}

        跟着函数名走下去,一直到SCP_sensorHub_direct_push_work()函数,发现是一个循环:

static int SCP_sensorHub_direct_push_work(void *data)
{
    for (;;) {
        wait_event(chre_kthread_wait,
                   READ_ONCE(chre_kthread_wait_condition));
        WRITE_ONCE(chre_kthread_wait_condition, false);
        mark_timestamp(0, WORK_START, ktime_get_boot_ns(), 0);
        SCP_sensorHub_read_wp_queue();//该函数会调用到gsensor_recv_data()
    }
    return 0;
}

        最后发现这是在sensorHub_probe()里面开启的一个内核线程:

static int sensorHub_probe(struct platform_device *pdev) {
    /*.......*/
    WRITE_ONCE(chre_kthread_wait_condition, false);
    task = kthread_run(SCP_sensorHub_direct_push_work,
                       NULL, "chre_kthread");
    if (IS_ERR(task)) {
        pr_err("SCP_sensorHub_direct_push_work create fail!\n");
        goto exit_direct_push;
    }
    sched_setscheduler(task, SCHED_FIFO, &param);
    /*.......*/
}

 三.小结:

        在上层需要获取sensor数据的时候,就会开启这个线程,不断地上报数据,这样就会触发poll返回有数据可读的消息,这时候再使用read()来读数据即可。至于数据是何时何地过来的,还在学习之中。另外,这只是sensor逻辑上的驱动,主要功能是处理上报数据。

        注:本人新手,后续会学习更新修正,希望巨佬们多多指教


版权声明:本文为weixin_43914603原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。