linux/drivers/iio/orientation/hid-sensor-rotation.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * HID Sensors Driver
   4 * Copyright (c) 2014, Intel Corporation.
   5 */
   6
   7#include <linux/device.h>
   8#include <linux/platform_device.h>
   9#include <linux/module.h>
  10#include <linux/interrupt.h>
  11#include <linux/irq.h>
  12#include <linux/slab.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 "../common/hid-sensors/hid-sensor-trigger.h"
  18
  19struct dev_rot_state {
  20        struct hid_sensor_hub_callbacks callbacks;
  21        struct hid_sensor_common common_attributes;
  22        struct hid_sensor_hub_attribute_info quaternion;
  23        u32 sampled_vals[4];
  24        int scale_pre_decml;
  25        int scale_post_decml;
  26        int scale_precision;
  27        int value_offset;
  28};
  29
  30/* Channel definitions */
  31static const struct iio_chan_spec dev_rot_channels[] = {
  32        {
  33                .type = IIO_ROT,
  34                .modified = 1,
  35                .channel2 = IIO_MOD_QUATERNION,
  36                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  37                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
  38                                        BIT(IIO_CHAN_INFO_OFFSET) |
  39                                        BIT(IIO_CHAN_INFO_SCALE) |
  40                                        BIT(IIO_CHAN_INFO_HYSTERESIS)
  41        }
  42};
  43
  44/* Adjust channel real bits based on report descriptor */
  45static void dev_rot_adjust_channel_bit_mask(struct iio_chan_spec *chan,
  46                                                int size)
  47{
  48        chan->scan_type.sign = 's';
  49        /* Real storage bits will change based on the report desc. */
  50        chan->scan_type.realbits = size * 8;
  51        /* Maximum size of a sample to capture is u32 */
  52        chan->scan_type.storagebits = sizeof(u32) * 8;
  53        chan->scan_type.repeat = 4;
  54}
  55
  56/* Channel read_raw handler */
  57static int dev_rot_read_raw(struct iio_dev *indio_dev,
  58                                struct iio_chan_spec const *chan,
  59                                int size, int *vals, int *val_len,
  60                                long mask)
  61{
  62        struct dev_rot_state *rot_state = iio_priv(indio_dev);
  63        int ret_type;
  64        int i;
  65
  66        vals[0] = 0;
  67        vals[1] = 0;
  68
  69        switch (mask) {
  70        case IIO_CHAN_INFO_RAW:
  71                if (size >= 4) {
  72                        for (i = 0; i < 4; ++i)
  73                                vals[i] = rot_state->sampled_vals[i];
  74                        ret_type = IIO_VAL_INT_MULTIPLE;
  75                        *val_len =  4;
  76                } else
  77                        ret_type = -EINVAL;
  78                break;
  79        case IIO_CHAN_INFO_SCALE:
  80                vals[0] = rot_state->scale_pre_decml;
  81                vals[1] = rot_state->scale_post_decml;
  82                return rot_state->scale_precision;
  83
  84        case IIO_CHAN_INFO_OFFSET:
  85                *vals = rot_state->value_offset;
  86                return IIO_VAL_INT;
  87
  88        case IIO_CHAN_INFO_SAMP_FREQ:
  89                ret_type = hid_sensor_read_samp_freq_value(
  90                        &rot_state->common_attributes, &vals[0], &vals[1]);
  91                break;
  92        case IIO_CHAN_INFO_HYSTERESIS:
  93                ret_type = hid_sensor_read_raw_hyst_value(
  94                        &rot_state->common_attributes, &vals[0], &vals[1]);
  95                break;
  96        default:
  97                ret_type = -EINVAL;
  98                break;
  99        }
 100
 101        return ret_type;
 102}
 103
 104/* Channel write_raw handler */
 105static int dev_rot_write_raw(struct iio_dev *indio_dev,
 106                               struct iio_chan_spec const *chan,
 107                               int val,
 108                               int val2,
 109                               long mask)
 110{
 111        struct dev_rot_state *rot_state = iio_priv(indio_dev);
 112        int ret;
 113
 114        switch (mask) {
 115        case IIO_CHAN_INFO_SAMP_FREQ:
 116                ret = hid_sensor_write_samp_freq_value(
 117                                &rot_state->common_attributes, val, val2);
 118                break;
 119        case IIO_CHAN_INFO_HYSTERESIS:
 120                ret = hid_sensor_write_raw_hyst_value(
 121                                &rot_state->common_attributes, val, val2);
 122                break;
 123        default:
 124                ret = -EINVAL;
 125        }
 126
 127        return ret;
 128}
 129
 130static const struct iio_info dev_rot_info = {
 131        .read_raw_multi = &dev_rot_read_raw,
 132        .write_raw = &dev_rot_write_raw,
 133};
 134
 135/* Function to push data to buffer */
 136static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
 137{
 138        dev_dbg(&indio_dev->dev, "hid_sensor_push_data >>\n");
 139        iio_push_to_buffers(indio_dev, (u8 *)data);
 140        dev_dbg(&indio_dev->dev, "hid_sensor_push_data <<\n");
 141
 142}
 143
 144/* Callback handler to send event after all samples are received and captured */
 145static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
 146                                unsigned usage_id,
 147                                void *priv)
 148{
 149        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 150        struct dev_rot_state *rot_state = iio_priv(indio_dev);
 151
 152        dev_dbg(&indio_dev->dev, "dev_rot_proc_event\n");
 153        if (atomic_read(&rot_state->common_attributes.data_ready))
 154                hid_sensor_push_data(indio_dev,
 155                                (u8 *)rot_state->sampled_vals,
 156                                sizeof(rot_state->sampled_vals));
 157
 158        return 0;
 159}
 160
 161/* Capture samples in local storage */
 162static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev,
 163                                unsigned usage_id,
 164                                size_t raw_len, char *raw_data,
 165                                void *priv)
 166{
 167        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 168        struct dev_rot_state *rot_state = iio_priv(indio_dev);
 169
 170        if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) {
 171                memcpy(rot_state->sampled_vals, raw_data,
 172                                        sizeof(rot_state->sampled_vals));
 173                dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len,
 174                                        sizeof(rot_state->sampled_vals));
 175        }
 176
 177        return 0;
 178}
 179
 180/* Parse report which is specific to an usage id*/
 181static int dev_rot_parse_report(struct platform_device *pdev,
 182                                struct hid_sensor_hub_device *hsdev,
 183                                struct iio_chan_spec *channels,
 184                                unsigned usage_id,
 185                                struct dev_rot_state *st)
 186{
 187        int ret;
 188
 189        ret = sensor_hub_input_get_attribute_info(hsdev,
 190                                HID_INPUT_REPORT,
 191                                usage_id,
 192                                HID_USAGE_SENSOR_ORIENT_QUATERNION,
 193                                &st->quaternion);
 194        if (ret)
 195                return ret;
 196
 197        dev_rot_adjust_channel_bit_mask(&channels[0],
 198                st->quaternion.size / 4);
 199
 200        dev_dbg(&pdev->dev, "dev_rot %x:%x\n", st->quaternion.index,
 201                st->quaternion.report_id);
 202
 203        dev_dbg(&pdev->dev, "dev_rot: attrib size %d\n",
 204                                st->quaternion.size);
 205
 206        st->scale_precision = hid_sensor_format_scale(
 207                                hsdev->usage,
 208                                &st->quaternion,
 209                                &st->scale_pre_decml, &st->scale_post_decml);
 210
 211        /* Set Sensitivity field ids, when there is no individual modifier */
 212        if (st->common_attributes.sensitivity.index < 0) {
 213                sensor_hub_input_get_attribute_info(hsdev,
 214                        HID_FEATURE_REPORT, usage_id,
 215                        HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
 216                        HID_USAGE_SENSOR_DATA_ORIENTATION,
 217                        &st->common_attributes.sensitivity);
 218                dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
 219                        st->common_attributes.sensitivity.index,
 220                        st->common_attributes.sensitivity.report_id);
 221        }
 222
 223        return 0;
 224}
 225
 226/* Function to initialize the processing for usage id */
 227static int hid_dev_rot_probe(struct platform_device *pdev)
 228{
 229        int ret;
 230        char *name;
 231        struct iio_dev *indio_dev;
 232        struct dev_rot_state *rot_state;
 233        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 234
 235        indio_dev = devm_iio_device_alloc(&pdev->dev,
 236                                          sizeof(struct dev_rot_state));
 237        if (indio_dev == NULL)
 238                return -ENOMEM;
 239
 240        platform_set_drvdata(pdev, indio_dev);
 241
 242        rot_state = iio_priv(indio_dev);
 243        rot_state->common_attributes.hsdev = hsdev;
 244        rot_state->common_attributes.pdev = pdev;
 245
 246        switch (hsdev->usage) {
 247        case HID_USAGE_SENSOR_DEVICE_ORIENTATION:
 248                name = "dev_rotation";
 249                break;
 250        case HID_USAGE_SENSOR_RELATIVE_ORIENTATION:
 251                name = "relative_orientation";
 252                break;
 253        case HID_USAGE_SENSOR_GEOMAGNETIC_ORIENTATION:
 254                name = "geomagnetic_orientation";
 255                break;
 256        default:
 257                return -EINVAL;
 258        }
 259
 260        ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
 261                                &rot_state->common_attributes);
 262        if (ret) {
 263                dev_err(&pdev->dev, "failed to setup common attributes\n");
 264                return ret;
 265        }
 266
 267        indio_dev->channels = devm_kmemdup(&pdev->dev, dev_rot_channels,
 268                                           sizeof(dev_rot_channels),
 269                                           GFP_KERNEL);
 270        if (!indio_dev->channels) {
 271                dev_err(&pdev->dev, "failed to duplicate channels\n");
 272                return -ENOMEM;
 273        }
 274
 275        ret = dev_rot_parse_report(pdev, hsdev,
 276                                   (struct iio_chan_spec *)indio_dev->channels,
 277                                        hsdev->usage, rot_state);
 278        if (ret) {
 279                dev_err(&pdev->dev, "failed to setup attributes\n");
 280                return ret;
 281        }
 282
 283        indio_dev->num_channels = ARRAY_SIZE(dev_rot_channels);
 284        indio_dev->info = &dev_rot_info;
 285        indio_dev->name = name;
 286        indio_dev->modes = INDIO_DIRECT_MODE;
 287
 288        atomic_set(&rot_state->common_attributes.data_ready, 0);
 289
 290        ret = hid_sensor_setup_trigger(indio_dev, name,
 291                                        &rot_state->common_attributes);
 292        if (ret) {
 293                dev_err(&pdev->dev, "trigger setup failed\n");
 294                return ret;
 295        }
 296
 297        ret = iio_device_register(indio_dev);
 298        if (ret) {
 299                dev_err(&pdev->dev, "device register failed\n");
 300                goto error_remove_trigger;
 301        }
 302
 303        rot_state->callbacks.send_event = dev_rot_proc_event;
 304        rot_state->callbacks.capture_sample = dev_rot_capture_sample;
 305        rot_state->callbacks.pdev = pdev;
 306        ret = sensor_hub_register_callback(hsdev, hsdev->usage,
 307                                        &rot_state->callbacks);
 308        if (ret) {
 309                dev_err(&pdev->dev, "callback reg failed\n");
 310                goto error_iio_unreg;
 311        }
 312
 313        return 0;
 314
 315error_iio_unreg:
 316        iio_device_unregister(indio_dev);
 317error_remove_trigger:
 318        hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
 319        return ret;
 320}
 321
 322/* Function to deinitialize the processing for usage id */
 323static int hid_dev_rot_remove(struct platform_device *pdev)
 324{
 325        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 326        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 327        struct dev_rot_state *rot_state = iio_priv(indio_dev);
 328
 329        sensor_hub_remove_callback(hsdev, hsdev->usage);
 330        iio_device_unregister(indio_dev);
 331        hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
 332
 333        return 0;
 334}
 335
 336static const struct platform_device_id hid_dev_rot_ids[] = {
 337        {
 338                /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
 339                .name = "HID-SENSOR-20008a",
 340        },
 341        {
 342                /* Relative orientation(AG) sensor */
 343                .name = "HID-SENSOR-20008e",
 344        },
 345        {
 346                /* Geomagnetic orientation(AM) sensor */
 347                .name = "HID-SENSOR-2000c1",
 348        },
 349        { /* sentinel */ }
 350};
 351MODULE_DEVICE_TABLE(platform, hid_dev_rot_ids);
 352
 353static struct platform_driver hid_dev_rot_platform_driver = {
 354        .id_table = hid_dev_rot_ids,
 355        .driver = {
 356                .name   = KBUILD_MODNAME,
 357                .pm     = &hid_sensor_pm_ops,
 358        },
 359        .probe          = hid_dev_rot_probe,
 360        .remove         = hid_dev_rot_remove,
 361};
 362module_platform_driver(hid_dev_rot_platform_driver);
 363
 364MODULE_DESCRIPTION("HID Sensor Device Rotation");
 365MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
 366MODULE_LICENSE("GPL");
 367