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 0:
 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        .driver_module = THIS_MODULE,
 286        .read_raw = &magn_3d_read_raw,
 287        .write_raw = &magn_3d_write_raw,
 288};
 289
 290/* Function to push data to buffer */
 291static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data)
 292{
 293        dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
 294        iio_push_to_buffers(indio_dev, data);
 295}
 296
 297/* Callback handler to send event after all samples are received and captured */
 298static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
 299                                unsigned usage_id,
 300                                void *priv)
 301{
 302        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 303        struct magn_3d_state *magn_state = iio_priv(indio_dev);
 304
 305        dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
 306        if (atomic_read(&magn_state->magn_flux_attributes.data_ready))
 307                hid_sensor_push_data(indio_dev, magn_state->iio_vals);
 308
 309        return 0;
 310}
 311
 312/* Capture samples in local storage */
 313static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
 314                                unsigned usage_id,
 315                                size_t raw_len, char *raw_data,
 316                                void *priv)
 317{
 318        struct iio_dev *indio_dev = platform_get_drvdata(priv);
 319        struct magn_3d_state *magn_state = iio_priv(indio_dev);
 320        int offset;
 321        int ret = 0;
 322        u32 *iio_val = NULL;
 323
 324        switch (usage_id) {
 325        case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS:
 326        case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS:
 327        case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS:
 328                offset = (usage_id - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS)
 329                                + CHANNEL_SCAN_INDEX_X;
 330        break;
 331        case HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH:
 332        case HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH:
 333        case HID_USAGE_SENSOR_ORIENT_MAGN_NORTH:
 334        case HID_USAGE_SENSOR_ORIENT_TRUE_NORTH:
 335                offset = (usage_id - HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH)
 336                                + CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP;
 337        break;
 338        default:
 339                return -EINVAL;
 340        }
 341
 342        iio_val = magn_state->magn_val_addr[offset];
 343
 344        if (iio_val != NULL)
 345                *iio_val = *((u32 *)raw_data);
 346        else
 347                ret = -EINVAL;
 348
 349        return ret;
 350}
 351
 352/* Parse report which is specific to an usage id*/
 353static int magn_3d_parse_report(struct platform_device *pdev,
 354                                struct hid_sensor_hub_device *hsdev,
 355                                struct iio_chan_spec **channels,
 356                                int *chan_count,
 357                                unsigned usage_id,
 358                                struct magn_3d_state *st)
 359{
 360        int i;
 361        int attr_count = 0;
 362        struct iio_chan_spec *_channels;
 363
 364        /* Scan for each usage attribute supported */
 365        for (i = 0; i < MAGN_3D_CHANNEL_MAX; i++) {
 366                int status;
 367                u32 address = magn_3d_addresses[i];
 368
 369                /* Check if usage attribute exists in the sensor hub device */
 370                status = sensor_hub_input_get_attribute_info(hsdev,
 371                        HID_INPUT_REPORT,
 372                        usage_id,
 373                        address,
 374                        &(st->magn[i]));
 375                if (!status)
 376                        attr_count++;
 377        }
 378
 379        if (attr_count <= 0) {
 380                dev_err(&pdev->dev,
 381                        "failed to find any supported usage attributes in report\n");
 382                return  -EINVAL;
 383        }
 384
 385        dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n",
 386                        attr_count);
 387        dev_dbg(&pdev->dev, "magn_3d X: %x:%x Y: %x:%x Z: %x:%x\n",
 388                        st->magn[0].index,
 389                        st->magn[0].report_id,
 390                        st->magn[1].index, st->magn[1].report_id,
 391                        st->magn[2].index, st->magn[2].report_id);
 392
 393        /* Setup IIO channel array */
 394        _channels = devm_kcalloc(&pdev->dev, attr_count,
 395                                sizeof(struct iio_chan_spec),
 396                                GFP_KERNEL);
 397        if (!_channels) {
 398                dev_err(&pdev->dev,
 399                        "failed to allocate space for iio channels\n");
 400                return -ENOMEM;
 401        }
 402
 403        st->iio_vals = devm_kcalloc(&pdev->dev, attr_count,
 404                                sizeof(u32),
 405                                GFP_KERNEL);
 406        if (!st->iio_vals) {
 407                dev_err(&pdev->dev,
 408                        "failed to allocate space for iio values array\n");
 409                return -ENOMEM;
 410        }
 411
 412        for (i = 0, *chan_count = 0;
 413        i < MAGN_3D_CHANNEL_MAX && *chan_count < attr_count;
 414        i++){
 415                if (st->magn[i].index >= 0) {
 416                        /* Setup IIO channel struct */
 417                        (_channels[*chan_count]) = magn_3d_channels[i];
 418                        (_channels[*chan_count]).scan_index = *chan_count;
 419                        (_channels[*chan_count]).address = i;
 420
 421                        /* Set magn_val_addr to iio value address */
 422                        st->magn_val_addr[i] = &(st->iio_vals[*chan_count]);
 423                        magn_3d_adjust_channel_bit_mask(_channels,
 424                                                        *chan_count,
 425                                                        st->magn[i].size);
 426                        (*chan_count)++;
 427                }
 428        }
 429
 430        if (*chan_count <= 0) {
 431                dev_err(&pdev->dev,
 432                        "failed to find any magnetic channels setup\n");
 433                return -EINVAL;
 434        }
 435
 436        *channels = _channels;
 437
 438        dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n",
 439                        *chan_count);
 440
 441        st->magn_flux_attr.scale_precision = hid_sensor_format_scale(
 442                                HID_USAGE_SENSOR_COMPASS_3D,
 443                                &st->magn[CHANNEL_SCAN_INDEX_X],
 444                                &st->magn_flux_attr.scale_pre_decml,
 445                                &st->magn_flux_attr.scale_post_decml);
 446        st->rot_attr.scale_precision
 447                = hid_sensor_format_scale(
 448                        HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
 449                        &st->magn[CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP],
 450                        &st->rot_attr.scale_pre_decml,
 451                        &st->rot_attr.scale_post_decml);
 452
 453        /* Set Sensitivity field ids, when there is no individual modifier */
 454        if (st->magn_flux_attributes.sensitivity.index < 0) {
 455                sensor_hub_input_get_attribute_info(hsdev,
 456                        HID_FEATURE_REPORT, usage_id,
 457                        HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
 458                        HID_USAGE_SENSOR_DATA_ORIENTATION,
 459                        &st->magn_flux_attributes.sensitivity);
 460                dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
 461                        st->magn_flux_attributes.sensitivity.index,
 462                        st->magn_flux_attributes.sensitivity.report_id);
 463        }
 464        if (st->magn_flux_attributes.sensitivity.index < 0) {
 465                sensor_hub_input_get_attribute_info(hsdev,
 466                        HID_FEATURE_REPORT, usage_id,
 467                        HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
 468                        HID_USAGE_SENSOR_ORIENT_MAGN_FLUX,
 469                        &st->magn_flux_attributes.sensitivity);
 470                dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
 471                        st->magn_flux_attributes.sensitivity.index,
 472                        st->magn_flux_attributes.sensitivity.report_id);
 473        }
 474        if (st->rot_attributes.sensitivity.index < 0) {
 475                sensor_hub_input_get_attribute_info(hsdev,
 476                        HID_FEATURE_REPORT, usage_id,
 477                        HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
 478                        HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
 479                        &st->rot_attributes.sensitivity);
 480                dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
 481                        st->rot_attributes.sensitivity.index,
 482                        st->rot_attributes.sensitivity.report_id);
 483        }
 484
 485        return 0;
 486}
 487
 488/* Function to initialize the processing for usage id */
 489static int hid_magn_3d_probe(struct platform_device *pdev)
 490{
 491        int ret = 0;
 492        static char *name = "magn_3d";
 493        struct iio_dev *indio_dev;
 494        struct magn_3d_state *magn_state;
 495        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 496        struct iio_chan_spec *channels;
 497        int chan_count = 0;
 498
 499        indio_dev = devm_iio_device_alloc(&pdev->dev,
 500                                          sizeof(struct magn_3d_state));
 501        if (indio_dev == NULL)
 502                return -ENOMEM;
 503
 504        platform_set_drvdata(pdev, indio_dev);
 505
 506        magn_state = iio_priv(indio_dev);
 507        magn_state->magn_flux_attributes.hsdev = hsdev;
 508        magn_state->magn_flux_attributes.pdev = pdev;
 509
 510        ret = hid_sensor_parse_common_attributes(hsdev,
 511                                HID_USAGE_SENSOR_COMPASS_3D,
 512                                &magn_state->magn_flux_attributes);
 513        if (ret) {
 514                dev_err(&pdev->dev, "failed to setup common attributes\n");
 515                return ret;
 516        }
 517        magn_state->rot_attributes = magn_state->magn_flux_attributes;
 518
 519        ret = magn_3d_parse_report(pdev, hsdev,
 520                                &channels, &chan_count,
 521                                HID_USAGE_SENSOR_COMPASS_3D, magn_state);
 522        if (ret) {
 523                dev_err(&pdev->dev, "failed to parse report\n");
 524                return ret;
 525        }
 526
 527        indio_dev->channels = channels;
 528        indio_dev->num_channels = chan_count;
 529        indio_dev->dev.parent = &pdev->dev;
 530        indio_dev->info = &magn_3d_info;
 531        indio_dev->name = name;
 532        indio_dev->modes = INDIO_DIRECT_MODE;
 533
 534        ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
 535                NULL, NULL);
 536        if (ret) {
 537                dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
 538                return ret;
 539        }
 540        atomic_set(&magn_state->magn_flux_attributes.data_ready, 0);
 541        ret = hid_sensor_setup_trigger(indio_dev, name,
 542                                        &magn_state->magn_flux_attributes);
 543        if (ret < 0) {
 544                dev_err(&pdev->dev, "trigger setup failed\n");
 545                goto error_unreg_buffer_funcs;
 546        }
 547
 548        ret = iio_device_register(indio_dev);
 549        if (ret) {
 550                dev_err(&pdev->dev, "device register failed\n");
 551                goto error_remove_trigger;
 552        }
 553
 554        magn_state->callbacks.send_event = magn_3d_proc_event;
 555        magn_state->callbacks.capture_sample = magn_3d_capture_sample;
 556        magn_state->callbacks.pdev = pdev;
 557        ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D,
 558                                        &magn_state->callbacks);
 559        if (ret < 0) {
 560                dev_err(&pdev->dev, "callback reg failed\n");
 561                goto error_iio_unreg;
 562        }
 563
 564        return ret;
 565
 566error_iio_unreg:
 567        iio_device_unregister(indio_dev);
 568error_remove_trigger:
 569        hid_sensor_remove_trigger(&magn_state->magn_flux_attributes);
 570error_unreg_buffer_funcs:
 571        iio_triggered_buffer_cleanup(indio_dev);
 572        return ret;
 573}
 574
 575/* Function to deinitialize the processing for usage id */
 576static int hid_magn_3d_remove(struct platform_device *pdev)
 577{
 578        struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
 579        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 580        struct magn_3d_state *magn_state = iio_priv(indio_dev);
 581
 582        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
 583        iio_device_unregister(indio_dev);
 584        hid_sensor_remove_trigger(&magn_state->magn_flux_attributes);
 585        iio_triggered_buffer_cleanup(indio_dev);
 586
 587        return 0;
 588}
 589
 590static const struct platform_device_id hid_magn_3d_ids[] = {
 591        {
 592                /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
 593                .name = "HID-SENSOR-200083",
 594        },
 595        { /* sentinel */ }
 596};
 597MODULE_DEVICE_TABLE(platform, hid_magn_3d_ids);
 598
 599static struct platform_driver hid_magn_3d_platform_driver = {
 600        .id_table = hid_magn_3d_ids,
 601        .driver = {
 602                .name   = KBUILD_MODNAME,
 603                .pm     = &hid_sensor_pm_ops,
 604        },
 605        .probe          = hid_magn_3d_probe,
 606        .remove         = hid_magn_3d_remove,
 607};
 608module_platform_driver(hid_magn_3d_platform_driver);
 609
 610MODULE_DESCRIPTION("HID Sensor Magnetometer 3D");
 611MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
 612MODULE_LICENSE("GPL");
 613