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