linux/drivers/iio/magnetometer/hid-sensor-magn-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 magn_3d_channel {
  35        CHANNEL_SCAN_INDEX_X,
  36        CHANNEL_SCAN_INDEX_Y,
  37        CHANNEL_SCAN_INDEX_Z,
  38        CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP,
  39        CHANNEL_SCAN_INDEX_NORTH_TRUE_TILT_COMP,
  40        CHANNEL_SCAN_INDEX_NORTH_MAGN,
  41        CHANNEL_SCAN_INDEX_NORTH_TRUE,
  42        MAGN_3D_CHANNEL_MAX,
  43};
  44
  45struct magn_3d_state {
  46        struct hid_sensor_hub_callbacks callbacks;
  47        struct hid_sensor_common common_attributes;
  48        struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];
  49
  50        /* dynamically sized array to hold sensor values */
  51        u32 *iio_vals;
  52        /* array of pointers to sensor value */
  53        u32 *magn_val_addr[MAGN_3D_CHANNEL_MAX];
  54
  55        int scale_pre_decml;
  56        int scale_post_decml;
  57        int scale_precision;
  58        int value_offset;
  59};
  60
  61static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
  62        HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS,
  63        HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS,
  64        HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS,
  65        HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
  66        HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH,
  67        HID_USAGE_SENSOR_ORIENT_MAGN_NORTH,
  68        HID_USAGE_SENSOR_ORIENT_TRUE_NORTH,
  69};
  70
  71/* Channel definitions */
  72static const struct iio_chan_spec magn_3d_channels[] = {
  73        {
  74                .type = IIO_MAGN,
  75                .modified = 1,
  76                .channel2 = IIO_MOD_X,
  77                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  78                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
  79                BIT(IIO_CHAN_INFO_SCALE) |
  80                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
  81                BIT(IIO_CHAN_INFO_HYSTERESIS),
  82        }, {
  83                .type = IIO_MAGN,
  84                .modified = 1,
  85                .channel2 = IIO_MOD_Y,
  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        }, {
  92                .type = IIO_MAGN,
  93                .modified = 1,
  94                .channel2 = IIO_MOD_Z,
  95                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  96                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
  97                BIT(IIO_CHAN_INFO_SCALE) |
  98                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
  99                BIT(IIO_CHAN_INFO_HYSTERESIS),
 100        }, {
 101                .type = IIO_ROT,
 102                .modified = 1,
 103                .channel2 = IIO_MOD_NORTH_MAGN_TILT_COMP,
 104                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 105                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
 106                BIT(IIO_CHAN_INFO_SCALE) |
 107                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
 108                BIT(IIO_CHAN_INFO_HYSTERESIS),
 109        }, {
 110                .type = IIO_ROT,
 111                .modified = 1,
 112                .channel2 = IIO_MOD_NORTH_TRUE_TILT_COMP,
 113                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 114                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
 115                BIT(IIO_CHAN_INFO_SCALE) |
 116                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
 117                BIT(IIO_CHAN_INFO_HYSTERESIS),
 118        }, {
 119                .type = IIO_ROT,
 120                .modified = 1,
 121                .channel2 = IIO_MOD_NORTH_MAGN,
 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        }, {
 128                .type = IIO_ROT,
 129                .modified = 1,
 130                .channel2 = IIO_MOD_NORTH_TRUE,
 131                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
 132                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
 133                BIT(IIO_CHAN_INFO_SCALE) |
 134                BIT(IIO_CHAN_INFO_SAMP_FREQ) |
 135                BIT(IIO_CHAN_INFO_HYSTERESIS),
 136        }
 137};
 138
 139/* Adjust channel real bits based on report descriptor */
 140static void magn_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
 141                                                int channel, int size)
 142{
 143        channels[channel].scan_type.sign = 's';
 144        /* Real storage bits will change based on the report desc. */
 145        channels[channel].scan_type.realbits = size * 8;
 146        /* Maximum size of a sample to capture is u32 */
 147        channels[channel].scan_type.storagebits = sizeof(u32) * 8;
 148}
 149
 150/* Channel read_raw handler */
 151static int magn_3d_read_raw(struct iio_dev *indio_dev,
 152                              struct iio_chan_spec const *chan,
 153                              int *val, int *val2,
 154                              long mask)
 155{
 156        struct magn_3d_state *magn_state = iio_priv(indio_dev);
 157        int report_id = -1;
 158        u32 address;
 159        int ret_type;
 160        s32 poll_value;
 161
 162        *val = 0;
 163        *val2 = 0;
 164        switch (mask) {
 165        case 0:
 166                poll_value = hid_sensor_read_poll_value(
 167                                        &magn_state->common_attributes);
 168                if (poll_value < 0)
 169                                return -EINVAL;
 170
 171                hid_sensor_power_state(&magn_state->common_attributes, true);
 172                msleep_interruptible(poll_value * 2);
 173
 174                report_id =
 175                        magn_state->magn[chan->address].report_id;
 176                address = magn_3d_addresses[chan->address];
 177                if (report_id >= 0)
 178                        *val = sensor_hub_input_attr_get_raw_value(
 179                                magn_state->common_attributes.hsdev,
 180                                HID_USAGE_SENSOR_COMPASS_3D, address,
 181                                report_id);
 182                else {
 183                        *val = 0;
 184                        hid_sensor_power_state(&magn_state->common_attributes,
 185                                                false);
 186                        return -EINVAL;
 187                }
 188                hid_sensor_power_state(&magn_state->common_attributes, false);
 189                ret_type = IIO_VAL_INT;
 190                break;
 191        case IIO_CHAN_INFO_SCALE:
 192                *val = magn_state->scale_pre_decml;
 193                *val2 = magn_state->scale_post_decml;
 194                ret_type = magn_state->scale_precision;
 195                break;
 196        case IIO_CHAN_INFO_OFFSET:
 197                *val = magn_state->value_offset;
 198                ret_type = IIO_VAL_INT;
 199                break;
 200        case IIO_CHAN_INFO_SAMP_FREQ:
 201                ret_type = hid_sensor_read_samp_freq_value(
 202                        &magn_state->common_attributes, val, val2);
 203                break;
 204        case IIO_CHAN_INFO_HYSTERESIS:
 205                ret_type = hid_sensor_read_raw_hyst_value(
 206                        &magn_state->common_attributes, val, val2);
 207                break;
 208        default:
 209                ret_type = -EINVAL;
 210                break;
 211        }
 212
 213        return ret_type;
 214}
 215
 216/* Channel write_raw handler */
 217static int magn_3d_write_raw(struct iio_dev *indio_dev,
 218                               struct iio_chan_spec const *chan,
 219                               int val,
 220                               int val2,
 221                               long mask)
 222{
 223        struct magn_3d_state *magn_state = iio_priv(indio_dev);
 224        int ret = 0;
 225
 226        switch (mask) {
 227        case IIO_CHAN_INFO_SAMP_FREQ:
 228                ret = hid_sensor_write_samp_freq_value(
 229                                &magn_state->common_attributes, val, val2);
 230                break;
 231        case IIO_CHAN_INFO_HYSTERESIS:
 232                ret = hid_sensor_write_raw_hyst_value(
 233                                &magn_state->common_attributes, val, val2);
 234                break;
 235        default:
 236                ret = -EINVAL;
 237        }
 238
 239        return ret;
 240}
 241
 242static const struct iio_info magn_3d_info = {
 243        .driver_module = THIS_MODULE,
 244        .read_raw = &magn_3d_read_raw,
 245        .write_raw = &magn_3d_write_raw,
 246};
 247
 248/* Function to push data to buffer */
 249static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
 250        int len)
 251{
 252        dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
 253        iio_push_to_buffers(indio_dev, data);
 254}
 255
 256/* Callback handler to send event after all samples are received and captured */
 257static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
 258                                unsigned usage_id,
 259                                void *priv)
 260{
 261        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 262        struct magn_3d_state *magn_state = iio_priv(indio_dev);
 263
 264        dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
 265        if (atomic_read(&magn_state->common_attributes.data_ready))
 266                hid_sensor_push_data(indio_dev,
 267                                magn_state->iio_vals,
 268                                sizeof(magn_state->iio_vals));
 269
 270        return 0;
 271}
 272
 273/* Capture samples in local storage */
 274static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
 275                                unsigned usage_id,
 276                                size_t raw_len, char *raw_data,
 277                                void *priv)
 278{
 279        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 280        struct magn_3d_state *magn_state = iio_priv(indio_dev);
 281        int offset;
 282        int ret = 0;
 283        u32 *iio_val = NULL;
 284
 285        switch (usage_id) {
 286        case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS:
 287        case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS:
 288        case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS:
 289                offset = (usage_id - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS)
 290                                + CHANNEL_SCAN_INDEX_X;
 291        break;
 292        case HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH:
 293        case HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH:
 294        case HID_USAGE_SENSOR_ORIENT_MAGN_NORTH:
 295        case HID_USAGE_SENSOR_ORIENT_TRUE_NORTH:
 296                offset = (usage_id - HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH)
 297                                + CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP;
 298        break;
 299        default:
 300                return -EINVAL;
 301        }
 302
 303        iio_val = magn_state->magn_val_addr[offset];
 304
 305        if (iio_val != NULL)
 306                *iio_val = *((u32 *)raw_data);
 307        else
 308                ret = -EINVAL;
 309
 310        return ret;
 311}
 312
 313/* Parse report which is specific to an usage id*/
 314static int magn_3d_parse_report(struct platform_device *pdev,
 315                                struct hid_sensor_hub_device *hsdev,
 316                                struct iio_chan_spec **channels,
 317                                int *chan_count,
 318                                unsigned usage_id,
 319                                struct magn_3d_state *st)
 320{
 321        int i;
 322        int attr_count = 0;
 323        struct iio_chan_spec *_channels;
 324
 325        /* Scan for each usage attribute supported */
 326        for (i = 0; i < MAGN_3D_CHANNEL_MAX; i++) {
 327                int status;
 328                u32 address = magn_3d_addresses[i];
 329
 330                /* Check if usage attribute exists in the sensor hub device */
 331                status = sensor_hub_input_get_attribute_info(hsdev,
 332                        HID_INPUT_REPORT,
 333                        usage_id,
 334                        address,
 335                        &(st->magn[i]));
 336                if (!status)
 337                        attr_count++;
 338        }
 339
 340        if (attr_count <= 0) {
 341                dev_err(&pdev->dev,
 342                        "failed to find any supported usage attributes in report\n");
 343                return  -EINVAL;
 344        }
 345
 346        dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n",
 347                        attr_count);
 348        dev_dbg(&pdev->dev, "magn_3d X: %x:%x Y: %x:%x Z: %x:%x\n",
 349                        st->magn[0].index,
 350                        st->magn[0].report_id,
 351                        st->magn[1].index, st->magn[1].report_id,
 352                        st->magn[2].index, st->magn[2].report_id);
 353
 354        /* Setup IIO channel array */
 355        _channels = devm_kcalloc(&pdev->dev, attr_count,
 356                                sizeof(struct iio_chan_spec),
 357                                GFP_KERNEL);
 358        if (!_channels) {
 359                dev_err(&pdev->dev,
 360                        "failed to allocate space for iio channels\n");
 361                return -ENOMEM;
 362        }
 363
 364        st->iio_vals = devm_kcalloc(&pdev->dev, attr_count,
 365                                sizeof(u32),
 366                                GFP_KERNEL);
 367        if (!st->iio_vals) {
 368                dev_err(&pdev->dev,
 369                        "failed to allocate space for iio values array\n");
 370                return -ENOMEM;
 371        }
 372
 373        for (i = 0, *chan_count = 0;
 374        i < MAGN_3D_CHANNEL_MAX && *chan_count < attr_count;
 375        i++){
 376                if (st->magn[i].index >= 0) {
 377                        /* Setup IIO channel struct */
 378                        (_channels[*chan_count]) = magn_3d_channels[i];
 379                        (_channels[*chan_count]).scan_index = *chan_count;
 380                        (_channels[*chan_count]).address = i;
 381
 382                        /* Set magn_val_addr to iio value address */
 383                        st->magn_val_addr[i] = &(st->iio_vals[*chan_count]);
 384                        magn_3d_adjust_channel_bit_mask(_channels,
 385                                                        *chan_count,
 386                                                        st->magn[i].size);
 387                        (*chan_count)++;
 388                }
 389        }
 390
 391        if (*chan_count <= 0) {
 392                dev_err(&pdev->dev,
 393                        "failed to find any magnetic channels setup\n");
 394                return -EINVAL;
 395        }
 396
 397        *channels = _channels;
 398
 399        dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n",
 400                        *chan_count);
 401
 402        st->scale_precision = hid_sensor_format_scale(
 403                                HID_USAGE_SENSOR_COMPASS_3D,
 404                                &st->magn[CHANNEL_SCAN_INDEX_X],
 405                                &st->scale_pre_decml, &st->scale_post_decml);
 406
 407        /* Set Sensitivity field ids, when there is no individual modifier */
 408        if (st->common_attributes.sensitivity.index < 0) {
 409                sensor_hub_input_get_attribute_info(hsdev,
 410                        HID_FEATURE_REPORT, usage_id,
 411                        HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
 412                        HID_USAGE_SENSOR_DATA_ORIENTATION,
 413                        &st->common_attributes.sensitivity);
 414                dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
 415                        st->common_attributes.sensitivity.index,
 416                        st->common_attributes.sensitivity.report_id);
 417        }
 418
 419        return 0;
 420}
 421
 422/* Function to initialize the processing for usage id */
 423static int hid_magn_3d_probe(struct platform_device *pdev)
 424{
 425        int ret = 0;
 426        static char *name = "magn_3d";
 427        struct iio_dev *indio_dev;
 428        struct magn_3d_state *magn_state;
 429        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 430        struct iio_chan_spec *channels;
 431        int chan_count = 0;
 432
 433        indio_dev = devm_iio_device_alloc(&pdev->dev,
 434                                          sizeof(struct magn_3d_state));
 435        if (indio_dev == NULL)
 436                return -ENOMEM;
 437
 438        platform_set_drvdata(pdev, indio_dev);
 439
 440        magn_state = iio_priv(indio_dev);
 441        magn_state->common_attributes.hsdev = hsdev;
 442        magn_state->common_attributes.pdev = pdev;
 443
 444        ret = hid_sensor_parse_common_attributes(hsdev,
 445                                HID_USAGE_SENSOR_COMPASS_3D,
 446                                &magn_state->common_attributes);
 447        if (ret) {
 448                dev_err(&pdev->dev, "failed to setup common attributes\n");
 449                return ret;
 450        }
 451
 452        ret = magn_3d_parse_report(pdev, hsdev,
 453                                &channels, &chan_count,
 454                                HID_USAGE_SENSOR_COMPASS_3D, magn_state);
 455        if (ret) {
 456                dev_err(&pdev->dev, "failed to parse report\n");
 457                return ret;
 458        }
 459
 460        indio_dev->channels = channels;
 461        indio_dev->num_channels = chan_count;
 462        indio_dev->dev.parent = &pdev->dev;
 463        indio_dev->info = &magn_3d_info;
 464        indio_dev->name = name;
 465        indio_dev->modes = INDIO_DIRECT_MODE;
 466
 467        ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
 468                NULL, NULL);
 469        if (ret) {
 470                dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
 471                return ret;
 472        }
 473        atomic_set(&magn_state->common_attributes.data_ready, 0);
 474        ret = hid_sensor_setup_trigger(indio_dev, name,
 475                                        &magn_state->common_attributes);
 476        if (ret < 0) {
 477                dev_err(&pdev->dev, "trigger setup failed\n");
 478                goto error_unreg_buffer_funcs;
 479        }
 480
 481        ret = iio_device_register(indio_dev);
 482        if (ret) {
 483                dev_err(&pdev->dev, "device register failed\n");
 484                goto error_remove_trigger;
 485        }
 486
 487        magn_state->callbacks.send_event = magn_3d_proc_event;
 488        magn_state->callbacks.capture_sample = magn_3d_capture_sample;
 489        magn_state->callbacks.pdev = pdev;
 490        ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D,
 491                                        &magn_state->callbacks);
 492        if (ret < 0) {
 493                dev_err(&pdev->dev, "callback reg failed\n");
 494                goto error_iio_unreg;
 495        }
 496
 497        return ret;
 498
 499error_iio_unreg:
 500        iio_device_unregister(indio_dev);
 501error_remove_trigger:
 502        hid_sensor_remove_trigger(&magn_state->common_attributes);
 503error_unreg_buffer_funcs:
 504        iio_triggered_buffer_cleanup(indio_dev);
 505        return ret;
 506}
 507
 508/* Function to deinitialize the processing for usage id */
 509static int hid_magn_3d_remove(struct platform_device *pdev)
 510{
 511        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 512        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 513        struct magn_3d_state *magn_state = iio_priv(indio_dev);
 514
 515        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
 516        iio_device_unregister(indio_dev);
 517        hid_sensor_remove_trigger(&magn_state->common_attributes);
 518        iio_triggered_buffer_cleanup(indio_dev);
 519
 520        return 0;
 521}
 522
 523static struct platform_device_id hid_magn_3d_ids[] = {
 524        {
 525                /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
 526                .name = "HID-SENSOR-200083",
 527        },
 528        { /* sentinel */ }
 529};
 530MODULE_DEVICE_TABLE(platform, hid_magn_3d_ids);
 531
 532static struct platform_driver hid_magn_3d_platform_driver = {
 533        .id_table = hid_magn_3d_ids,
 534        .driver = {
 535                .name   = KBUILD_MODNAME,
 536                .owner  = THIS_MODULE,
 537        },
 538        .probe          = hid_magn_3d_probe,
 539        .remove         = hid_magn_3d_remove,
 540};
 541module_platform_driver(hid_magn_3d_platform_driver);
 542
 543MODULE_DESCRIPTION("HID Sensor Magnetometer 3D");
 544MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
 545MODULE_LICENSE("GPL");
 546