linux/drivers/iio/accel/hid-sensor-accel-3d.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * HID Sensors Driver
   4 * Copyright (c) 2012, Intel Corporation.
   5 */
   6#include <linux/device.h>
   7#include <linux/platform_device.h>
   8#include <linux/module.h>
   9#include <linux/interrupt.h>
  10#include <linux/irq.h>
  11#include <linux/slab.h>
  12#include <linux/delay.h>
  13#include <linux/hid-sensor-hub.h>
  14#include <linux/iio/iio.h>
  15#include <linux/iio/sysfs.h>
  16#include <linux/iio/buffer.h>
  17#include <linux/iio/trigger_consumer.h>
  18#include <linux/iio/triggered_buffer.h>
  19#include "../common/hid-sensors/hid-sensor-trigger.h"
  20
  21enum accel_3d_channel {
  22        CHANNEL_SCAN_INDEX_X,
  23        CHANNEL_SCAN_INDEX_Y,
  24        CHANNEL_SCAN_INDEX_Z,
  25        ACCEL_3D_CHANNEL_MAX,
  26};
  27
  28struct accel_3d_state {
  29        struct hid_sensor_hub_callbacks callbacks;
  30        struct hid_sensor_common common_attributes;
  31        struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
  32        /* Reserve for 3 channels + padding + timestamp */
  33        u32 accel_val[ACCEL_3D_CHANNEL_MAX + 3];
  34        int scale_pre_decml;
  35        int scale_post_decml;
  36        int scale_precision;
  37        int value_offset;
  38        int64_t timestamp;
  39};
  40
  41static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
  42        HID_USAGE_SENSOR_ACCEL_X_AXIS,
  43        HID_USAGE_SENSOR_ACCEL_Y_AXIS,
  44        HID_USAGE_SENSOR_ACCEL_Z_AXIS
  45};
  46
  47/* Channel definitions */
  48static const struct iio_chan_spec accel_3d_channels[] = {
  49        {
  50                .type = IIO_ACCEL,
  51                .modified = 1,
  52                .channel2 = IIO_MOD_X,
  53                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  54                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
  55                BIT(IIO_CHAN_INFO_SCALE) |
  56                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
  57                BIT(IIO_CHAN_INFO_HYSTERESIS),
  58                .scan_index = CHANNEL_SCAN_INDEX_X,
  59        }, {
  60                .type = IIO_ACCEL,
  61                .modified = 1,
  62                .channel2 = IIO_MOD_Y,
  63                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  64                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
  65                BIT(IIO_CHAN_INFO_SCALE) |
  66                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
  67                BIT(IIO_CHAN_INFO_HYSTERESIS),
  68                .scan_index = CHANNEL_SCAN_INDEX_Y,
  69        }, {
  70                .type = IIO_ACCEL,
  71                .modified = 1,
  72                .channel2 = IIO_MOD_Z,
  73                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  74                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
  75                BIT(IIO_CHAN_INFO_SCALE) |
  76                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
  77                BIT(IIO_CHAN_INFO_HYSTERESIS),
  78                .scan_index = CHANNEL_SCAN_INDEX_Z,
  79        },
  80        IIO_CHAN_SOFT_TIMESTAMP(3)
  81};
  82
  83/* Channel definitions */
  84static const struct iio_chan_spec gravity_channels[] = {
  85        {
  86                .type = IIO_GRAVITY,
  87                .modified = 1,
  88                .channel2 = IIO_MOD_X,
  89                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  90                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
  91                BIT(IIO_CHAN_INFO_SCALE) |
  92                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
  93                BIT(IIO_CHAN_INFO_HYSTERESIS),
  94                .scan_index = CHANNEL_SCAN_INDEX_X,
  95        }, {
  96                .type = IIO_GRAVITY,
  97                .modified = 1,
  98                .channel2 = IIO_MOD_Y,
  99                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 100                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
 101                BIT(IIO_CHAN_INFO_SCALE) |
 102                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
 103                BIT(IIO_CHAN_INFO_HYSTERESIS),
 104                .scan_index = CHANNEL_SCAN_INDEX_Y,
 105        }, {
 106                .type = IIO_GRAVITY,
 107                .modified = 1,
 108                .channel2 = IIO_MOD_Z,
 109                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 110                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
 111                BIT(IIO_CHAN_INFO_SCALE) |
 112                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
 113                BIT(IIO_CHAN_INFO_HYSTERESIS),
 114                .scan_index = CHANNEL_SCAN_INDEX_Z,
 115        }
 116};
 117
 118/* Adjust channel real bits based on report descriptor */
 119static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
 120                                                int channel, int size)
 121{
 122        channels[channel].scan_type.sign = 's';
 123        /* Real storage bits will change based on the report desc. */
 124        channels[channel].scan_type.realbits = size * 8;
 125        /* Maximum size of a sample to capture is u32 */
 126        channels[channel].scan_type.storagebits = sizeof(u32) * 8;
 127}
 128
 129/* Channel read_raw handler */
 130static int accel_3d_read_raw(struct iio_dev *indio_dev,
 131                              struct iio_chan_spec const *chan,
 132                              int *val, int *val2,
 133                              long mask)
 134{
 135        struct accel_3d_state *accel_state = iio_priv(indio_dev);
 136        int report_id = -1;
 137        u32 address;
 138        int ret_type;
 139        s32 min;
 140        struct hid_sensor_hub_device *hsdev =
 141                                        accel_state->common_attributes.hsdev;
 142
 143        *val = 0;
 144        *val2 = 0;
 145        switch (mask) {
 146        case IIO_CHAN_INFO_RAW:
 147                hid_sensor_power_state(&accel_state->common_attributes, true);
 148                report_id = accel_state->accel[chan->scan_index].report_id;
 149                min = accel_state->accel[chan->scan_index].logical_minimum;
 150                address = accel_3d_addresses[chan->scan_index];
 151                if (report_id >= 0)
 152                        *val = sensor_hub_input_attr_get_raw_value(
 153                                        accel_state->common_attributes.hsdev,
 154                                        hsdev->usage, address, report_id,
 155                                        SENSOR_HUB_SYNC,
 156                                        min < 0);
 157                else {
 158                        *val = 0;
 159                        hid_sensor_power_state(&accel_state->common_attributes,
 160                                                 false);
 161                        return -EINVAL;
 162                }
 163                hid_sensor_power_state(&accel_state->common_attributes, false);
 164                ret_type = IIO_VAL_INT;
 165                break;
 166        case IIO_CHAN_INFO_SCALE:
 167                *val = accel_state->scale_pre_decml;
 168                *val2 = accel_state->scale_post_decml;
 169                ret_type = accel_state->scale_precision;
 170                break;
 171        case IIO_CHAN_INFO_OFFSET:
 172                *val = accel_state->value_offset;
 173                ret_type = IIO_VAL_INT;
 174                break;
 175        case IIO_CHAN_INFO_SAMP_FREQ:
 176                ret_type = hid_sensor_read_samp_freq_value(
 177                        &accel_state->common_attributes, val, val2);
 178                break;
 179        case IIO_CHAN_INFO_HYSTERESIS:
 180                ret_type = hid_sensor_read_raw_hyst_value(
 181                        &accel_state->common_attributes, val, val2);
 182                break;
 183        default:
 184                ret_type = -EINVAL;
 185                break;
 186        }
 187
 188        return ret_type;
 189}
 190
 191/* Channel write_raw handler */
 192static int accel_3d_write_raw(struct iio_dev *indio_dev,
 193                               struct iio_chan_spec const *chan,
 194                               int val,
 195                               int val2,
 196                               long mask)
 197{
 198        struct accel_3d_state *accel_state = iio_priv(indio_dev);
 199        int ret = 0;
 200
 201        switch (mask) {
 202        case IIO_CHAN_INFO_SAMP_FREQ:
 203                ret = hid_sensor_write_samp_freq_value(
 204                                &accel_state->common_attributes, val, val2);
 205                break;
 206        case IIO_CHAN_INFO_HYSTERESIS:
 207                ret = hid_sensor_write_raw_hyst_value(
 208                                &accel_state->common_attributes, val, val2);
 209                break;
 210        default:
 211                ret = -EINVAL;
 212        }
 213
 214        return ret;
 215}
 216
 217static const struct iio_info accel_3d_info = {
 218        .read_raw = &accel_3d_read_raw,
 219        .write_raw = &accel_3d_write_raw,
 220};
 221
 222/* Function to push data to buffer */
 223static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data,
 224                                 int len, int64_t timestamp)
 225{
 226        dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
 227        iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
 228}
 229
 230/* Callback handler to send event after all samples are received and captured */
 231static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
 232                                unsigned usage_id,
 233                                void *priv)
 234{
 235        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 236        struct accel_3d_state *accel_state = iio_priv(indio_dev);
 237
 238        dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n");
 239        if (atomic_read(&accel_state->common_attributes.data_ready)) {
 240                if (!accel_state->timestamp)
 241                        accel_state->timestamp = iio_get_time_ns(indio_dev);
 242
 243                hid_sensor_push_data(indio_dev,
 244                                     accel_state->accel_val,
 245                                     sizeof(accel_state->accel_val),
 246                                     accel_state->timestamp);
 247
 248                accel_state->timestamp = 0;
 249        }
 250
 251        return 0;
 252}
 253
 254/* Capture samples in local storage */
 255static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
 256                                unsigned usage_id,
 257                                size_t raw_len, char *raw_data,
 258                                void *priv)
 259{
 260        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 261        struct accel_3d_state *accel_state = iio_priv(indio_dev);
 262        int offset;
 263        int ret = -EINVAL;
 264
 265        switch (usage_id) {
 266        case HID_USAGE_SENSOR_ACCEL_X_AXIS:
 267        case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
 268        case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
 269                offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
 270                accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] =
 271                                                *(u32 *)raw_data;
 272                ret = 0;
 273        break;
 274        case HID_USAGE_SENSOR_TIME_TIMESTAMP:
 275                accel_state->timestamp =
 276                        hid_sensor_convert_timestamp(
 277                                        &accel_state->common_attributes,
 278                                        *(int64_t *)raw_data);
 279        break;
 280        default:
 281                break;
 282        }
 283
 284        return ret;
 285}
 286
 287/* Parse report which is specific to an usage id*/
 288static int accel_3d_parse_report(struct platform_device *pdev,
 289                                struct hid_sensor_hub_device *hsdev,
 290                                struct iio_chan_spec *channels,
 291                                unsigned usage_id,
 292                                struct accel_3d_state *st)
 293{
 294        int ret;
 295        int i;
 296
 297        for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
 298                ret = sensor_hub_input_get_attribute_info(hsdev,
 299                                HID_INPUT_REPORT,
 300                                usage_id,
 301                                HID_USAGE_SENSOR_ACCEL_X_AXIS + i,
 302                                &st->accel[CHANNEL_SCAN_INDEX_X + i]);
 303                if (ret < 0)
 304                        break;
 305                accel_3d_adjust_channel_bit_mask(channels,
 306                                CHANNEL_SCAN_INDEX_X + i,
 307                                st->accel[CHANNEL_SCAN_INDEX_X + i].size);
 308        }
 309        dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
 310                        st->accel[0].index,
 311                        st->accel[0].report_id,
 312                        st->accel[1].index, st->accel[1].report_id,
 313                        st->accel[2].index, st->accel[2].report_id);
 314
 315        st->scale_precision = hid_sensor_format_scale(
 316                                hsdev->usage,
 317                                &st->accel[CHANNEL_SCAN_INDEX_X],
 318                                &st->scale_pre_decml, &st->scale_post_decml);
 319
 320        /* Set Sensitivity field ids, when there is no individual modifier */
 321        if (st->common_attributes.sensitivity.index < 0) {
 322                sensor_hub_input_get_attribute_info(hsdev,
 323                        HID_FEATURE_REPORT, usage_id,
 324                        HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
 325                        HID_USAGE_SENSOR_DATA_ACCELERATION,
 326                        &st->common_attributes.sensitivity);
 327                dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
 328                        st->common_attributes.sensitivity.index,
 329                        st->common_attributes.sensitivity.report_id);
 330        }
 331
 332        return ret;
 333}
 334
 335/* Function to initialize the processing for usage id */
 336static int hid_accel_3d_probe(struct platform_device *pdev)
 337{
 338        int ret = 0;
 339        const char *name;
 340        struct iio_dev *indio_dev;
 341        struct accel_3d_state *accel_state;
 342        const struct iio_chan_spec *channel_spec;
 343        int channel_size;
 344
 345        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 346
 347        indio_dev = devm_iio_device_alloc(&pdev->dev,
 348                                          sizeof(struct accel_3d_state));
 349        if (indio_dev == NULL)
 350                return -ENOMEM;
 351
 352        platform_set_drvdata(pdev, indio_dev);
 353
 354        accel_state = iio_priv(indio_dev);
 355        accel_state->common_attributes.hsdev = hsdev;
 356        accel_state->common_attributes.pdev = pdev;
 357
 358        if (hsdev->usage == HID_USAGE_SENSOR_ACCEL_3D) {
 359                name = "accel_3d";
 360                channel_spec = accel_3d_channels;
 361                channel_size = sizeof(accel_3d_channels);
 362                indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
 363        } else {
 364                name = "gravity";
 365                channel_spec = gravity_channels;
 366                channel_size = sizeof(gravity_channels);
 367                indio_dev->num_channels = ARRAY_SIZE(gravity_channels);
 368        }
 369        ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
 370                                        &accel_state->common_attributes);
 371        if (ret) {
 372                dev_err(&pdev->dev, "failed to setup common attributes\n");
 373                return ret;
 374        }
 375        indio_dev->channels = kmemdup(channel_spec, channel_size, GFP_KERNEL);
 376
 377        if (!indio_dev->channels) {
 378                dev_err(&pdev->dev, "failed to duplicate channels\n");
 379                return -ENOMEM;
 380        }
 381        ret = accel_3d_parse_report(pdev, hsdev,
 382                                (struct iio_chan_spec *)indio_dev->channels,
 383                                hsdev->usage, accel_state);
 384        if (ret) {
 385                dev_err(&pdev->dev, "failed to setup attributes\n");
 386                goto error_free_dev_mem;
 387        }
 388
 389        indio_dev->dev.parent = &pdev->dev;
 390        indio_dev->info = &accel_3d_info;
 391        indio_dev->name = name;
 392        indio_dev->modes = INDIO_DIRECT_MODE;
 393
 394        ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
 395                NULL, NULL);
 396        if (ret) {
 397                dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
 398                goto error_free_dev_mem;
 399        }
 400        atomic_set(&accel_state->common_attributes.data_ready, 0);
 401        ret = hid_sensor_setup_trigger(indio_dev, name,
 402                                        &accel_state->common_attributes);
 403        if (ret < 0) {
 404                dev_err(&pdev->dev, "trigger setup failed\n");
 405                goto error_unreg_buffer_funcs;
 406        }
 407
 408        ret = iio_device_register(indio_dev);
 409        if (ret) {
 410                dev_err(&pdev->dev, "device register failed\n");
 411                goto error_remove_trigger;
 412        }
 413
 414        accel_state->callbacks.send_event = accel_3d_proc_event;
 415        accel_state->callbacks.capture_sample = accel_3d_capture_sample;
 416        accel_state->callbacks.pdev = pdev;
 417        ret = sensor_hub_register_callback(hsdev, hsdev->usage,
 418                                        &accel_state->callbacks);
 419        if (ret < 0) {
 420                dev_err(&pdev->dev, "callback reg failed\n");
 421                goto error_iio_unreg;
 422        }
 423
 424        return ret;
 425
 426error_iio_unreg:
 427        iio_device_unregister(indio_dev);
 428error_remove_trigger:
 429        hid_sensor_remove_trigger(&accel_state->common_attributes);
 430error_unreg_buffer_funcs:
 431        iio_triggered_buffer_cleanup(indio_dev);
 432error_free_dev_mem:
 433        kfree(indio_dev->channels);
 434        return ret;
 435}
 436
 437/* Function to deinitialize the processing for usage id */
 438static int hid_accel_3d_remove(struct platform_device *pdev)
 439{
 440        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 441        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 442        struct accel_3d_state *accel_state = iio_priv(indio_dev);
 443
 444        sensor_hub_remove_callback(hsdev, hsdev->usage);
 445        iio_device_unregister(indio_dev);
 446        hid_sensor_remove_trigger(&accel_state->common_attributes);
 447        iio_triggered_buffer_cleanup(indio_dev);
 448        kfree(indio_dev->channels);
 449
 450        return 0;
 451}
 452
 453static const struct platform_device_id hid_accel_3d_ids[] = {
 454        {
 455                /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
 456                .name = "HID-SENSOR-200073",
 457        },
 458        {       /* gravity sensor */
 459                .name = "HID-SENSOR-20007b",
 460        },
 461        { /* sentinel */ }
 462};
 463MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids);
 464
 465static struct platform_driver hid_accel_3d_platform_driver = {
 466        .id_table = hid_accel_3d_ids,
 467        .driver = {
 468                .name   = KBUILD_MODNAME,
 469                .pm     = &hid_sensor_pm_ops,
 470        },
 471        .probe          = hid_accel_3d_probe,
 472        .remove         = hid_accel_3d_remove,
 473};
 474module_platform_driver(hid_accel_3d_platform_driver);
 475
 476MODULE_DESCRIPTION("HID Sensor Accel 3D");
 477MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
 478MODULE_LICENSE("GPL");
 479