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