hid-sensor-attributes.c (15279B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * HID Sensors Driver 4 * Copyright (c) 2012, Intel Corporation. 5 */ 6#include <linux/module.h> 7#include <linux/kernel.h> 8#include <linux/time.h> 9#include <linux/units.h> 10 11#include <linux/hid-sensor-hub.h> 12#include <linux/iio/iio.h> 13 14static struct { 15 u32 usage_id; 16 int unit; /* 0 for default others from HID sensor spec */ 17 int scale_val0; /* scale, whole number */ 18 int scale_val1; /* scale, fraction in nanos */ 19} unit_conversion[] = { 20 {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650000}, 21 {HID_USAGE_SENSOR_ACCEL_3D, 22 HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0}, 23 {HID_USAGE_SENSOR_ACCEL_3D, 24 HID_USAGE_SENSOR_UNITS_G, 9, 806650000}, 25 26 {HID_USAGE_SENSOR_GRAVITY_VECTOR, 0, 9, 806650000}, 27 {HID_USAGE_SENSOR_GRAVITY_VECTOR, 28 HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0}, 29 {HID_USAGE_SENSOR_GRAVITY_VECTOR, 30 HID_USAGE_SENSOR_UNITS_G, 9, 806650000}, 31 32 {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453293}, 33 {HID_USAGE_SENSOR_GYRO_3D, 34 HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0}, 35 {HID_USAGE_SENSOR_GYRO_3D, 36 HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453293}, 37 38 {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000000}, 39 {HID_USAGE_SENSOR_COMPASS_3D, HID_USAGE_SENSOR_UNITS_GAUSS, 1, 0}, 40 41 {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453293}, 42 {HID_USAGE_SENSOR_INCLINOMETER_3D, 43 HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293}, 44 {HID_USAGE_SENSOR_INCLINOMETER_3D, 45 HID_USAGE_SENSOR_UNITS_RADIANS, 1, 0}, 46 47 {HID_USAGE_SENSOR_ALS, 0, 1, 0}, 48 {HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0}, 49 50 {HID_USAGE_SENSOR_PRESSURE, 0, 100, 0}, 51 {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000000}, 52 53 {HID_USAGE_SENSOR_TIME_TIMESTAMP, 0, 1000000000, 0}, 54 {HID_USAGE_SENSOR_TIME_TIMESTAMP, HID_USAGE_SENSOR_UNITS_MILLISECOND, 55 1000000, 0}, 56 57 {HID_USAGE_SENSOR_DEVICE_ORIENTATION, 0, 1, 0}, 58 59 {HID_USAGE_SENSOR_RELATIVE_ORIENTATION, 0, 1, 0}, 60 61 {HID_USAGE_SENSOR_GEOMAGNETIC_ORIENTATION, 0, 1, 0}, 62 63 {HID_USAGE_SENSOR_TEMPERATURE, 0, 1000, 0}, 64 {HID_USAGE_SENSOR_TEMPERATURE, HID_USAGE_SENSOR_UNITS_DEGREES, 1000, 0}, 65 66 {HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0}, 67 {HID_USAGE_SENSOR_HINGE, 0, 0, 17453293}, 68 {HID_USAGE_SENSOR_HINGE, HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293}, 69}; 70 71static void simple_div(int dividend, int divisor, int *whole, 72 int *micro_frac) 73{ 74 int rem; 75 int exp = 0; 76 77 *micro_frac = 0; 78 if (divisor == 0) { 79 *whole = 0; 80 return; 81 } 82 *whole = dividend/divisor; 83 rem = dividend % divisor; 84 if (rem) { 85 while (rem <= divisor) { 86 rem *= 10; 87 exp++; 88 } 89 *micro_frac = (rem / divisor) * int_pow(10, 6 - exp); 90 } 91} 92 93static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2) 94{ 95 int divisor = int_pow(10, exp); 96 97 *val1 = no / divisor; 98 *val2 = no % divisor * int_pow(10, 6 - exp); 99} 100 101/* 102VTF format uses exponent and variable size format. 103For example if the size is 2 bytes 1040x0067 with VTF16E14 format -> +1.03 105To convert just change to 0x67 to decimal and use two decimal as E14 stands 106for 10^-2. 107Negative numbers are 2's complement 108*/ 109static void convert_from_vtf_format(u32 value, int size, int exp, 110 int *val1, int *val2) 111{ 112 int sign = 1; 113 114 if (value & BIT(size*8 - 1)) { 115 value = ((1LL << (size * 8)) - value); 116 sign = -1; 117 } 118 exp = hid_sensor_convert_exponent(exp); 119 if (exp >= 0) { 120 *val1 = sign * value * int_pow(10, exp); 121 *val2 = 0; 122 } else { 123 split_micro_fraction(value, -exp, val1, val2); 124 if (*val1) 125 *val1 = sign * (*val1); 126 else 127 *val2 = sign * (*val2); 128 } 129} 130 131static u32 convert_to_vtf_format(int size, int exp, int val1, int val2) 132{ 133 int divisor; 134 u32 value; 135 int sign = 1; 136 137 if (val1 < 0 || val2 < 0) 138 sign = -1; 139 exp = hid_sensor_convert_exponent(exp); 140 if (exp < 0) { 141 divisor = int_pow(10, 6 + exp); 142 value = abs(val1) * int_pow(10, -exp); 143 value += abs(val2) / divisor; 144 } else { 145 divisor = int_pow(10, exp); 146 value = abs(val1) / divisor; 147 } 148 if (sign < 0) 149 value = ((1LL << (size * 8)) - value); 150 151 return value; 152} 153 154s32 hid_sensor_read_poll_value(struct hid_sensor_common *st) 155{ 156 s32 value = 0; 157 int ret; 158 159 ret = sensor_hub_get_feature(st->hsdev, 160 st->poll.report_id, 161 st->poll.index, sizeof(value), &value); 162 163 if (ret < 0 || value < 0) { 164 return -EINVAL; 165 } else { 166 if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND) 167 value = value * 1000; 168 } 169 170 return value; 171} 172EXPORT_SYMBOL_NS(hid_sensor_read_poll_value, IIO_HID_ATTRIBUTES); 173 174int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, 175 int *val1, int *val2) 176{ 177 s32 value; 178 int ret; 179 180 ret = sensor_hub_get_feature(st->hsdev, 181 st->poll.report_id, 182 st->poll.index, sizeof(value), &value); 183 if (ret < 0 || value < 0) { 184 *val1 = *val2 = 0; 185 return -EINVAL; 186 } else { 187 if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND) 188 simple_div(1000, value, val1, val2); 189 else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND) 190 simple_div(1, value, val1, val2); 191 else { 192 *val1 = *val2 = 0; 193 return -EINVAL; 194 } 195 } 196 197 return IIO_VAL_INT_PLUS_MICRO; 198} 199EXPORT_SYMBOL_NS(hid_sensor_read_samp_freq_value, IIO_HID); 200 201int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, 202 int val1, int val2) 203{ 204 s32 value; 205 int ret; 206 207 if (val1 < 0 || val2 < 0) 208 return -EINVAL; 209 210 value = val1 * HZ_PER_MHZ + val2; 211 if (value) { 212 if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND) 213 value = NSEC_PER_SEC / value; 214 else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND) 215 value = USEC_PER_SEC / value; 216 else 217 value = 0; 218 } 219 ret = sensor_hub_set_feature(st->hsdev, st->poll.report_id, 220 st->poll.index, sizeof(value), &value); 221 if (ret < 0 || value < 0) 222 return -EINVAL; 223 224 ret = sensor_hub_get_feature(st->hsdev, 225 st->poll.report_id, 226 st->poll.index, sizeof(value), &value); 227 if (ret < 0 || value < 0) 228 return -EINVAL; 229 230 st->poll_interval = value; 231 232 return 0; 233} 234EXPORT_SYMBOL_NS(hid_sensor_write_samp_freq_value, IIO_HID); 235 236int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, 237 int *val1, int *val2) 238{ 239 s32 value; 240 int ret; 241 242 ret = sensor_hub_get_feature(st->hsdev, 243 st->sensitivity.report_id, 244 st->sensitivity.index, sizeof(value), 245 &value); 246 if (ret < 0 || value < 0) { 247 *val1 = *val2 = 0; 248 return -EINVAL; 249 } else { 250 convert_from_vtf_format(value, st->sensitivity.size, 251 st->sensitivity.unit_expo, 252 val1, val2); 253 } 254 255 return IIO_VAL_INT_PLUS_MICRO; 256} 257EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_value, IIO_HID); 258 259int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, 260 int *val2) 261{ 262 s32 value; 263 int ret; 264 265 ret = sensor_hub_get_feature(st->hsdev, 266 st->sensitivity_rel.report_id, 267 st->sensitivity_rel.index, sizeof(value), 268 &value); 269 if (ret < 0 || value < 0) { 270 *val1 = *val2 = 0; 271 return -EINVAL; 272 } 273 274 convert_from_vtf_format(value, st->sensitivity_rel.size, 275 st->sensitivity_rel.unit_expo, val1, val2); 276 277 return IIO_VAL_INT_PLUS_MICRO; 278} 279EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_rel_value, IIO_HID); 280 281 282int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, 283 int val1, int val2) 284{ 285 s32 value; 286 int ret; 287 288 if (val1 < 0 || val2 < 0) 289 return -EINVAL; 290 291 value = convert_to_vtf_format(st->sensitivity.size, 292 st->sensitivity.unit_expo, 293 val1, val2); 294 ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id, 295 st->sensitivity.index, sizeof(value), 296 &value); 297 if (ret < 0 || value < 0) 298 return -EINVAL; 299 300 ret = sensor_hub_get_feature(st->hsdev, 301 st->sensitivity.report_id, 302 st->sensitivity.index, sizeof(value), 303 &value); 304 if (ret < 0 || value < 0) 305 return -EINVAL; 306 307 st->raw_hystersis = value; 308 309 return 0; 310} 311EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_value, IIO_HID); 312 313int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, 314 int val1, int val2) 315{ 316 s32 value; 317 int ret; 318 319 if (val1 < 0 || val2 < 0) 320 return -EINVAL; 321 322 value = convert_to_vtf_format(st->sensitivity_rel.size, 323 st->sensitivity_rel.unit_expo, 324 val1, val2); 325 ret = sensor_hub_set_feature(st->hsdev, st->sensitivity_rel.report_id, 326 st->sensitivity_rel.index, sizeof(value), 327 &value); 328 if (ret < 0 || value < 0) 329 return -EINVAL; 330 331 ret = sensor_hub_get_feature(st->hsdev, 332 st->sensitivity_rel.report_id, 333 st->sensitivity_rel.index, sizeof(value), 334 &value); 335 if (ret < 0 || value < 0) 336 return -EINVAL; 337 338 st->raw_hystersis = value; 339 340 return 0; 341} 342EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, IIO_HID); 343 344/* 345 * This fuction applies the unit exponent to the scale. 346 * For example: 347 * 9.806650000 ->exp:2-> val0[980]val1[665000000] 348 * 9.000806000 ->exp:2-> val0[900]val1[80600000] 349 * 0.174535293 ->exp:2-> val0[17]val1[453529300] 350 * 1.001745329 ->exp:0-> val0[1]val1[1745329] 351 * 1.001745329 ->exp:2-> val0[100]val1[174532900] 352 * 1.001745329 ->exp:4-> val0[10017]val1[453290000] 353 * 9.806650000 ->exp:-2-> val0[0]val1[98066500] 354 */ 355static void adjust_exponent_nano(int *val0, int *val1, int scale0, 356 int scale1, int exp) 357{ 358 int divisor; 359 int i; 360 int x; 361 int res; 362 int rem; 363 364 if (exp > 0) { 365 *val0 = scale0 * int_pow(10, exp); 366 res = 0; 367 if (exp > 9) { 368 *val1 = 0; 369 return; 370 } 371 for (i = 0; i < exp; ++i) { 372 divisor = int_pow(10, 8 - i); 373 x = scale1 / divisor; 374 res += int_pow(10, exp - 1 - i) * x; 375 scale1 = scale1 % divisor; 376 } 377 *val0 += res; 378 *val1 = scale1 * int_pow(10, exp); 379 } else if (exp < 0) { 380 exp = abs(exp); 381 if (exp > 9) { 382 *val0 = *val1 = 0; 383 return; 384 } 385 divisor = int_pow(10, exp); 386 *val0 = scale0 / divisor; 387 rem = scale0 % divisor; 388 res = 0; 389 for (i = 0; i < (9 - exp); ++i) { 390 divisor = int_pow(10, 8 - i); 391 x = scale1 / divisor; 392 res += int_pow(10, 8 - exp - i) * x; 393 scale1 = scale1 % divisor; 394 } 395 *val1 = rem * int_pow(10, 9 - exp) + res; 396 } else { 397 *val0 = scale0; 398 *val1 = scale1; 399 } 400} 401 402int hid_sensor_format_scale(u32 usage_id, 403 struct hid_sensor_hub_attribute_info *attr_info, 404 int *val0, int *val1) 405{ 406 int i; 407 int exp; 408 409 *val0 = 1; 410 *val1 = 0; 411 412 for (i = 0; i < ARRAY_SIZE(unit_conversion); ++i) { 413 if (unit_conversion[i].usage_id == usage_id && 414 unit_conversion[i].unit == attr_info->units) { 415 exp = hid_sensor_convert_exponent( 416 attr_info->unit_expo); 417 adjust_exponent_nano(val0, val1, 418 unit_conversion[i].scale_val0, 419 unit_conversion[i].scale_val1, exp); 420 break; 421 } 422 } 423 424 return IIO_VAL_INT_PLUS_NANO; 425} 426EXPORT_SYMBOL_NS(hid_sensor_format_scale, IIO_HID); 427 428int64_t hid_sensor_convert_timestamp(struct hid_sensor_common *st, 429 int64_t raw_value) 430{ 431 return st->timestamp_ns_scale * raw_value; 432} 433EXPORT_SYMBOL_NS(hid_sensor_convert_timestamp, IIO_HID); 434 435static 436int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev, 437 u32 usage_id, 438 struct hid_sensor_common *st) 439{ 440 sensor_hub_input_get_attribute_info(hsdev, 441 HID_FEATURE_REPORT, usage_id, 442 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL, 443 &st->poll); 444 /* Default unit of measure is milliseconds */ 445 if (st->poll.units == 0) 446 st->poll.units = HID_USAGE_SENSOR_UNITS_MILLISECOND; 447 448 st->poll_interval = -1; 449 450 return 0; 451 452} 453 454static void hid_sensor_get_report_latency_info(struct hid_sensor_hub_device *hsdev, 455 u32 usage_id, 456 struct hid_sensor_common *st) 457{ 458 sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, 459 usage_id, 460 HID_USAGE_SENSOR_PROP_REPORT_LATENCY, 461 &st->report_latency); 462 463 hid_dbg(hsdev->hdev, "Report latency attributes: %x:%x\n", 464 st->report_latency.index, st->report_latency.report_id); 465} 466 467int hid_sensor_get_report_latency(struct hid_sensor_common *st) 468{ 469 int ret; 470 int value; 471 472 ret = sensor_hub_get_feature(st->hsdev, st->report_latency.report_id, 473 st->report_latency.index, sizeof(value), 474 &value); 475 if (ret < 0) 476 return ret; 477 478 return value; 479} 480EXPORT_SYMBOL_NS(hid_sensor_get_report_latency, IIO_HID_ATTRIBUTES); 481 482int hid_sensor_set_report_latency(struct hid_sensor_common *st, int latency_ms) 483{ 484 return sensor_hub_set_feature(st->hsdev, st->report_latency.report_id, 485 st->report_latency.index, 486 sizeof(latency_ms), &latency_ms); 487} 488EXPORT_SYMBOL_NS(hid_sensor_set_report_latency, IIO_HID_ATTRIBUTES); 489 490bool hid_sensor_batch_mode_supported(struct hid_sensor_common *st) 491{ 492 return st->report_latency.index > 0 && st->report_latency.report_id > 0; 493} 494EXPORT_SYMBOL_NS(hid_sensor_batch_mode_supported, IIO_HID_ATTRIBUTES); 495 496int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, 497 u32 usage_id, 498 struct hid_sensor_common *st, 499 const u32 *sensitivity_addresses, 500 u32 sensitivity_addresses_len) 501{ 502 503 struct hid_sensor_hub_attribute_info timestamp; 504 s32 value; 505 int ret; 506 int i; 507 508 hid_sensor_get_reporting_interval(hsdev, usage_id, st); 509 510 sensor_hub_input_get_attribute_info(hsdev, 511 HID_FEATURE_REPORT, usage_id, 512 HID_USAGE_SENSOR_PROP_REPORT_STATE, 513 &st->report_state); 514 515 sensor_hub_input_get_attribute_info(hsdev, 516 HID_FEATURE_REPORT, usage_id, 517 HID_USAGE_SENSOR_PROY_POWER_STATE, 518 &st->power_state); 519 520 st->power_state.logical_minimum = 1; 521 st->report_state.logical_minimum = 1; 522 523 sensor_hub_input_get_attribute_info(hsdev, 524 HID_FEATURE_REPORT, usage_id, 525 HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, 526 &st->sensitivity); 527 528 sensor_hub_input_get_attribute_info(hsdev, 529 HID_FEATURE_REPORT, usage_id, 530 HID_USAGE_SENSOR_PROP_SENSITIVITY_REL_PCT, 531 &st->sensitivity_rel); 532 /* 533 * Set Sensitivity field ids, when there is no individual modifier, will 534 * check absolute sensitivity and relative sensitivity of data field 535 */ 536 for (i = 0; i < sensitivity_addresses_len; i++) { 537 if (st->sensitivity.index < 0) 538 sensor_hub_input_get_attribute_info( 539 hsdev, HID_FEATURE_REPORT, usage_id, 540 HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | 541 sensitivity_addresses[i], 542 &st->sensitivity); 543 544 if (st->sensitivity_rel.index < 0) 545 sensor_hub_input_get_attribute_info( 546 hsdev, HID_FEATURE_REPORT, usage_id, 547 HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_REL_PCT | 548 sensitivity_addresses[i], 549 &st->sensitivity_rel); 550 } 551 552 st->raw_hystersis = -1; 553 554 sensor_hub_input_get_attribute_info(hsdev, 555 HID_INPUT_REPORT, usage_id, 556 HID_USAGE_SENSOR_TIME_TIMESTAMP, 557 ×tamp); 558 if (timestamp.index >= 0 && timestamp.report_id) { 559 int val0, val1; 560 561 hid_sensor_format_scale(HID_USAGE_SENSOR_TIME_TIMESTAMP, 562 ×tamp, &val0, &val1); 563 st->timestamp_ns_scale = val0; 564 } else 565 st->timestamp_ns_scale = 1000000000; 566 567 hid_sensor_get_report_latency_info(hsdev, usage_id, st); 568 569 hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x %x:%x\n", 570 st->poll.index, st->poll.report_id, 571 st->report_state.index, st->report_state.report_id, 572 st->power_state.index, st->power_state.report_id, 573 st->sensitivity.index, st->sensitivity.report_id, 574 timestamp.index, timestamp.report_id); 575 576 ret = sensor_hub_get_feature(hsdev, 577 st->power_state.report_id, 578 st->power_state.index, sizeof(value), &value); 579 if (ret < 0) 580 return ret; 581 if (value < 0) 582 return -EINVAL; 583 584 return 0; 585} 586EXPORT_SYMBOL_NS(hid_sensor_parse_common_attributes, IIO_HID); 587 588MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); 589MODULE_DESCRIPTION("HID Sensor common attribute processing"); 590MODULE_LICENSE("GPL");