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