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/* Adjust channel real bits based on report descriptor */
  97static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
  98                                                int channel, int size)
  99{
 100        channels[channel].scan_type.sign = 's';
 101        /* Real storage bits will change based on the report desc. */
 102        channels[channel].scan_type.realbits = size * 8;
 103        /* Maximum size of a sample to capture is u32 */
 104        channels[channel].scan_type.storagebits = sizeof(u32) * 8;
 105}
 106
 107/* Channel read_raw handler */
 108static int accel_3d_read_raw(struct iio_dev *indio_dev,
 109                              struct iio_chan_spec const *chan,
 110                              int *val, int *val2,
 111                              long mask)
 112{
 113        struct accel_3d_state *accel_state = iio_priv(indio_dev);
 114        int report_id = -1;
 115        u32 address;
 116        int ret_type;
 117
 118        *val = 0;
 119        *val2 = 0;
 120        switch (mask) {
 121        case 0:
 122                hid_sensor_power_state(&accel_state->common_attributes, true);
 123                report_id = accel_state->accel[chan->scan_index].report_id;
 124                address = accel_3d_addresses[chan->scan_index];
 125                if (report_id >= 0)
 126                        *val = sensor_hub_input_attr_get_raw_value(
 127                                        accel_state->common_attributes.hsdev,
 128                                        HID_USAGE_SENSOR_ACCEL_3D, address,
 129                                        report_id,
 130                                        SENSOR_HUB_SYNC);
 131                else {
 132                        *val = 0;
 133                        hid_sensor_power_state(&accel_state->common_attributes,
 134                                                 false);
 135                        return -EINVAL;
 136                }
 137                hid_sensor_power_state(&accel_state->common_attributes, false);
 138                ret_type = IIO_VAL_INT;
 139                break;
 140        case IIO_CHAN_INFO_SCALE:
 141                *val = accel_state->scale_pre_decml;
 142                *val2 = accel_state->scale_post_decml;
 143                ret_type = accel_state->scale_precision;
 144                break;
 145        case IIO_CHAN_INFO_OFFSET:
 146                *val = accel_state->value_offset;
 147                ret_type = IIO_VAL_INT;
 148                break;
 149        case IIO_CHAN_INFO_SAMP_FREQ:
 150                ret_type = hid_sensor_read_samp_freq_value(
 151                        &accel_state->common_attributes, val, val2);
 152                break;
 153        case IIO_CHAN_INFO_HYSTERESIS:
 154                ret_type = hid_sensor_read_raw_hyst_value(
 155                        &accel_state->common_attributes, val, val2);
 156                break;
 157        default:
 158                ret_type = -EINVAL;
 159                break;
 160        }
 161
 162        return ret_type;
 163}
 164
 165/* Channel write_raw handler */
 166static int accel_3d_write_raw(struct iio_dev *indio_dev,
 167                               struct iio_chan_spec const *chan,
 168                               int val,
 169                               int val2,
 170                               long mask)
 171{
 172        struct accel_3d_state *accel_state = iio_priv(indio_dev);
 173        int ret = 0;
 174
 175        switch (mask) {
 176        case IIO_CHAN_INFO_SAMP_FREQ:
 177                ret = hid_sensor_write_samp_freq_value(
 178                                &accel_state->common_attributes, val, val2);
 179                break;
 180        case IIO_CHAN_INFO_HYSTERESIS:
 181                ret = hid_sensor_write_raw_hyst_value(
 182                                &accel_state->common_attributes, val, val2);
 183                break;
 184        default:
 185                ret = -EINVAL;
 186        }
 187
 188        return ret;
 189}
 190
 191static const struct iio_info accel_3d_info = {
 192        .driver_module = THIS_MODULE,
 193        .read_raw = &accel_3d_read_raw,
 194        .write_raw = &accel_3d_write_raw,
 195};
 196
 197/* Function to push data to buffer */
 198static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data,
 199                                 int len, int64_t timestamp)
 200{
 201        dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
 202        iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
 203}
 204
 205/* Callback handler to send event after all samples are received and captured */
 206static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
 207                                unsigned usage_id,
 208                                void *priv)
 209{
 210        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 211        struct accel_3d_state *accel_state = iio_priv(indio_dev);
 212
 213        dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n");
 214        if (atomic_read(&accel_state->common_attributes.data_ready)) {
 215                if (!accel_state->timestamp)
 216                        accel_state->timestamp = iio_get_time_ns();
 217
 218                hid_sensor_push_data(indio_dev,
 219                                     accel_state->accel_val,
 220                                     sizeof(accel_state->accel_val),
 221                                     accel_state->timestamp);
 222
 223                accel_state->timestamp = 0;
 224        }
 225
 226        return 0;
 227}
 228
 229/* Capture samples in local storage */
 230static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
 231                                unsigned usage_id,
 232                                size_t raw_len, char *raw_data,
 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        int offset;
 238        int ret = -EINVAL;
 239
 240        switch (usage_id) {
 241        case HID_USAGE_SENSOR_ACCEL_X_AXIS:
 242        case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
 243        case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
 244                offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
 245                accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] =
 246                                                *(u32 *)raw_data;
 247                ret = 0;
 248        break;
 249        case HID_USAGE_SENSOR_TIME_TIMESTAMP:
 250                accel_state->timestamp =
 251                        hid_sensor_convert_timestamp(
 252                                        &accel_state->common_attributes,
 253                                        *(int64_t *)raw_data);
 254        break;
 255        default:
 256                break;
 257        }
 258
 259        return ret;
 260}
 261
 262/* Parse report which is specific to an usage id*/
 263static int accel_3d_parse_report(struct platform_device *pdev,
 264                                struct hid_sensor_hub_device *hsdev,
 265                                struct iio_chan_spec *channels,
 266                                unsigned usage_id,
 267                                struct accel_3d_state *st)
 268{
 269        int ret;
 270        int i;
 271
 272        for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
 273                ret = sensor_hub_input_get_attribute_info(hsdev,
 274                                HID_INPUT_REPORT,
 275                                usage_id,
 276                                HID_USAGE_SENSOR_ACCEL_X_AXIS + i,
 277                                &st->accel[CHANNEL_SCAN_INDEX_X + i]);
 278                if (ret < 0)
 279                        break;
 280                accel_3d_adjust_channel_bit_mask(channels,
 281                                CHANNEL_SCAN_INDEX_X + i,
 282                                st->accel[CHANNEL_SCAN_INDEX_X + i].size);
 283        }
 284        dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
 285                        st->accel[0].index,
 286                        st->accel[0].report_id,
 287                        st->accel[1].index, st->accel[1].report_id,
 288                        st->accel[2].index, st->accel[2].report_id);
 289
 290        st->scale_precision = hid_sensor_format_scale(
 291                                HID_USAGE_SENSOR_ACCEL_3D,
 292                                &st->accel[CHANNEL_SCAN_INDEX_X],
 293                                &st->scale_pre_decml, &st->scale_post_decml);
 294
 295        /* Set Sensitivity field ids, when there is no individual modifier */
 296        if (st->common_attributes.sensitivity.index < 0) {
 297                sensor_hub_input_get_attribute_info(hsdev,
 298                        HID_FEATURE_REPORT, usage_id,
 299                        HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
 300                        HID_USAGE_SENSOR_DATA_ACCELERATION,
 301                        &st->common_attributes.sensitivity);
 302                dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
 303                        st->common_attributes.sensitivity.index,
 304                        st->common_attributes.sensitivity.report_id);
 305        }
 306
 307        return ret;
 308}
 309
 310/* Function to initialize the processing for usage id */
 311static int hid_accel_3d_probe(struct platform_device *pdev)
 312{
 313        int ret = 0;
 314        static const char *name = "accel_3d";
 315        struct iio_dev *indio_dev;
 316        struct accel_3d_state *accel_state;
 317        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 318
 319        indio_dev = devm_iio_device_alloc(&pdev->dev,
 320                                          sizeof(struct accel_3d_state));
 321        if (indio_dev == NULL)
 322                return -ENOMEM;
 323
 324        platform_set_drvdata(pdev, indio_dev);
 325
 326        accel_state = iio_priv(indio_dev);
 327        accel_state->common_attributes.hsdev = hsdev;
 328        accel_state->common_attributes.pdev = pdev;
 329
 330        ret = hid_sensor_parse_common_attributes(hsdev,
 331                                        HID_USAGE_SENSOR_ACCEL_3D,
 332                                        &accel_state->common_attributes);
 333        if (ret) {
 334                dev_err(&pdev->dev, "failed to setup common attributes\n");
 335                return ret;
 336        }
 337
 338        indio_dev->channels = kmemdup(accel_3d_channels,
 339                                      sizeof(accel_3d_channels), GFP_KERNEL);
 340        if (!indio_dev->channels) {
 341                dev_err(&pdev->dev, "failed to duplicate channels\n");
 342                return -ENOMEM;
 343        }
 344
 345        ret = accel_3d_parse_report(pdev, hsdev,
 346                                    (struct iio_chan_spec *)indio_dev->channels,
 347                                    HID_USAGE_SENSOR_ACCEL_3D, accel_state);
 348        if (ret) {
 349                dev_err(&pdev->dev, "failed to setup attributes\n");
 350                goto error_free_dev_mem;
 351        }
 352
 353        indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
 354        indio_dev->dev.parent = &pdev->dev;
 355        indio_dev->info = &accel_3d_info;
 356        indio_dev->name = name;
 357        indio_dev->modes = INDIO_DIRECT_MODE;
 358
 359        ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
 360                NULL, NULL);
 361        if (ret) {
 362                dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
 363                goto error_free_dev_mem;
 364        }
 365        atomic_set(&accel_state->common_attributes.data_ready, 0);
 366        ret = hid_sensor_setup_trigger(indio_dev, name,
 367                                        &accel_state->common_attributes);
 368        if (ret < 0) {
 369                dev_err(&pdev->dev, "trigger setup failed\n");
 370                goto error_unreg_buffer_funcs;
 371        }
 372
 373        ret = iio_device_register(indio_dev);
 374        if (ret) {
 375                dev_err(&pdev->dev, "device register failed\n");
 376                goto error_remove_trigger;
 377        }
 378
 379        accel_state->callbacks.send_event = accel_3d_proc_event;
 380        accel_state->callbacks.capture_sample = accel_3d_capture_sample;
 381        accel_state->callbacks.pdev = pdev;
 382        ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D,
 383                                        &accel_state->callbacks);
 384        if (ret < 0) {
 385                dev_err(&pdev->dev, "callback reg failed\n");
 386                goto error_iio_unreg;
 387        }
 388
 389        return ret;
 390
 391error_iio_unreg:
 392        iio_device_unregister(indio_dev);
 393error_remove_trigger:
 394        hid_sensor_remove_trigger(&accel_state->common_attributes);
 395error_unreg_buffer_funcs:
 396        iio_triggered_buffer_cleanup(indio_dev);
 397error_free_dev_mem:
 398        kfree(indio_dev->channels);
 399        return ret;
 400}
 401
 402/* Function to deinitialize the processing for usage id */
 403static int hid_accel_3d_remove(struct platform_device *pdev)
 404{
 405        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 406        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 407        struct accel_3d_state *accel_state = iio_priv(indio_dev);
 408
 409        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D);
 410        iio_device_unregister(indio_dev);
 411        hid_sensor_remove_trigger(&accel_state->common_attributes);
 412        iio_triggered_buffer_cleanup(indio_dev);
 413        kfree(indio_dev->channels);
 414
 415        return 0;
 416}
 417
 418static const struct platform_device_id hid_accel_3d_ids[] = {
 419        {
 420                /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
 421                .name = "HID-SENSOR-200073",
 422        },
 423        { /* sentinel */ }
 424};
 425MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids);
 426
 427static struct platform_driver hid_accel_3d_platform_driver = {
 428        .id_table = hid_accel_3d_ids,
 429        .driver = {
 430                .name   = KBUILD_MODNAME,
 431                .owner  = THIS_MODULE,
 432                .pm     = &hid_sensor_pm_ops,
 433        },
 434        .probe          = hid_accel_3d_probe,
 435        .remove         = hid_accel_3d_remove,
 436};
 437module_platform_driver(hid_accel_3d_platform_driver);
 438
 439MODULE_DESCRIPTION("HID Sensor Accel 3D");
 440MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
 441MODULE_LICENSE("GPL");
 442