591 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * HID Sensors Driver4 * 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 bytes1040x0067 with VTF16E14 format -> +1.03105To convert just change to 0x67 to decimal and use two decimal as E14 stands106for 10^-2.107Negative numbers are 2's complement108*/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 else127 *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 else217 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 435static436int 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, will534 * check absolute sensitivity and relative sensitivity of data field535 */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 } else565 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");591