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