0001
0002
0003
0004
0005
0006 #include <linux/module.h>
0007 #include <linux/kernel.h>
0008 #include <linux/time.h>
0009 #include <linux/units.h>
0010
0011 #include <linux/hid-sensor-hub.h>
0012 #include <linux/iio/iio.h>
0013
0014 static struct {
0015 u32 usage_id;
0016 int unit;
0017 int scale_val0;
0018 int scale_val1;
0019 } unit_conversion[] = {
0020 {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650000},
0021 {HID_USAGE_SENSOR_ACCEL_3D,
0022 HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0},
0023 {HID_USAGE_SENSOR_ACCEL_3D,
0024 HID_USAGE_SENSOR_UNITS_G, 9, 806650000},
0025
0026 {HID_USAGE_SENSOR_GRAVITY_VECTOR, 0, 9, 806650000},
0027 {HID_USAGE_SENSOR_GRAVITY_VECTOR,
0028 HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0},
0029 {HID_USAGE_SENSOR_GRAVITY_VECTOR,
0030 HID_USAGE_SENSOR_UNITS_G, 9, 806650000},
0031
0032 {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453293},
0033 {HID_USAGE_SENSOR_GYRO_3D,
0034 HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0},
0035 {HID_USAGE_SENSOR_GYRO_3D,
0036 HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453293},
0037
0038 {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000000},
0039 {HID_USAGE_SENSOR_COMPASS_3D, HID_USAGE_SENSOR_UNITS_GAUSS, 1, 0},
0040
0041 {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453293},
0042 {HID_USAGE_SENSOR_INCLINOMETER_3D,
0043 HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293},
0044 {HID_USAGE_SENSOR_INCLINOMETER_3D,
0045 HID_USAGE_SENSOR_UNITS_RADIANS, 1, 0},
0046
0047 {HID_USAGE_SENSOR_ALS, 0, 1, 0},
0048 {HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0},
0049
0050 {HID_USAGE_SENSOR_PRESSURE, 0, 100, 0},
0051 {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000000},
0052
0053 {HID_USAGE_SENSOR_TIME_TIMESTAMP, 0, 1000000000, 0},
0054 {HID_USAGE_SENSOR_TIME_TIMESTAMP, HID_USAGE_SENSOR_UNITS_MILLISECOND,
0055 1000000, 0},
0056
0057 {HID_USAGE_SENSOR_DEVICE_ORIENTATION, 0, 1, 0},
0058
0059 {HID_USAGE_SENSOR_RELATIVE_ORIENTATION, 0, 1, 0},
0060
0061 {HID_USAGE_SENSOR_GEOMAGNETIC_ORIENTATION, 0, 1, 0},
0062
0063 {HID_USAGE_SENSOR_TEMPERATURE, 0, 1000, 0},
0064 {HID_USAGE_SENSOR_TEMPERATURE, HID_USAGE_SENSOR_UNITS_DEGREES, 1000, 0},
0065
0066 {HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0},
0067 {HID_USAGE_SENSOR_HINGE, 0, 0, 17453293},
0068 {HID_USAGE_SENSOR_HINGE, HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293},
0069 };
0070
0071 static void simple_div(int dividend, int divisor, int *whole,
0072 int *micro_frac)
0073 {
0074 int rem;
0075 int exp = 0;
0076
0077 *micro_frac = 0;
0078 if (divisor == 0) {
0079 *whole = 0;
0080 return;
0081 }
0082 *whole = dividend/divisor;
0083 rem = dividend % divisor;
0084 if (rem) {
0085 while (rem <= divisor) {
0086 rem *= 10;
0087 exp++;
0088 }
0089 *micro_frac = (rem / divisor) * int_pow(10, 6 - exp);
0090 }
0091 }
0092
0093 static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2)
0094 {
0095 int divisor = int_pow(10, exp);
0096
0097 *val1 = no / divisor;
0098 *val2 = no % divisor * int_pow(10, 6 - exp);
0099 }
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109 static void convert_from_vtf_format(u32 value, int size, int exp,
0110 int *val1, int *val2)
0111 {
0112 int sign = 1;
0113
0114 if (value & BIT(size*8 - 1)) {
0115 value = ((1LL << (size * 8)) - value);
0116 sign = -1;
0117 }
0118 exp = hid_sensor_convert_exponent(exp);
0119 if (exp >= 0) {
0120 *val1 = sign * value * int_pow(10, exp);
0121 *val2 = 0;
0122 } else {
0123 split_micro_fraction(value, -exp, val1, val2);
0124 if (*val1)
0125 *val1 = sign * (*val1);
0126 else
0127 *val2 = sign * (*val2);
0128 }
0129 }
0130
0131 static u32 convert_to_vtf_format(int size, int exp, int val1, int val2)
0132 {
0133 int divisor;
0134 u32 value;
0135 int sign = 1;
0136
0137 if (val1 < 0 || val2 < 0)
0138 sign = -1;
0139 exp = hid_sensor_convert_exponent(exp);
0140 if (exp < 0) {
0141 divisor = int_pow(10, 6 + exp);
0142 value = abs(val1) * int_pow(10, -exp);
0143 value += abs(val2) / divisor;
0144 } else {
0145 divisor = int_pow(10, exp);
0146 value = abs(val1) / divisor;
0147 }
0148 if (sign < 0)
0149 value = ((1LL << (size * 8)) - value);
0150
0151 return value;
0152 }
0153
0154 s32 hid_sensor_read_poll_value(struct hid_sensor_common *st)
0155 {
0156 s32 value = 0;
0157 int ret;
0158
0159 ret = sensor_hub_get_feature(st->hsdev,
0160 st->poll.report_id,
0161 st->poll.index, sizeof(value), &value);
0162
0163 if (ret < 0 || value < 0) {
0164 return -EINVAL;
0165 } else {
0166 if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
0167 value = value * 1000;
0168 }
0169
0170 return value;
0171 }
0172 EXPORT_SYMBOL_NS(hid_sensor_read_poll_value, IIO_HID_ATTRIBUTES);
0173
0174 int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,
0175 int *val1, int *val2)
0176 {
0177 s32 value;
0178 int ret;
0179
0180 ret = sensor_hub_get_feature(st->hsdev,
0181 st->poll.report_id,
0182 st->poll.index, sizeof(value), &value);
0183 if (ret < 0 || value < 0) {
0184 *val1 = *val2 = 0;
0185 return -EINVAL;
0186 } else {
0187 if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
0188 simple_div(1000, value, val1, val2);
0189 else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
0190 simple_div(1, value, val1, val2);
0191 else {
0192 *val1 = *val2 = 0;
0193 return -EINVAL;
0194 }
0195 }
0196
0197 return IIO_VAL_INT_PLUS_MICRO;
0198 }
0199 EXPORT_SYMBOL_NS(hid_sensor_read_samp_freq_value, IIO_HID);
0200
0201 int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
0202 int val1, int val2)
0203 {
0204 s32 value;
0205 int ret;
0206
0207 if (val1 < 0 || val2 < 0)
0208 return -EINVAL;
0209
0210 value = val1 * HZ_PER_MHZ + val2;
0211 if (value) {
0212 if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
0213 value = NSEC_PER_SEC / value;
0214 else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
0215 value = USEC_PER_SEC / value;
0216 else
0217 value = 0;
0218 }
0219 ret = sensor_hub_set_feature(st->hsdev, st->poll.report_id,
0220 st->poll.index, sizeof(value), &value);
0221 if (ret < 0 || value < 0)
0222 return -EINVAL;
0223
0224 ret = sensor_hub_get_feature(st->hsdev,
0225 st->poll.report_id,
0226 st->poll.index, sizeof(value), &value);
0227 if (ret < 0 || value < 0)
0228 return -EINVAL;
0229
0230 st->poll_interval = value;
0231
0232 return 0;
0233 }
0234 EXPORT_SYMBOL_NS(hid_sensor_write_samp_freq_value, IIO_HID);
0235
0236 int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st,
0237 int *val1, int *val2)
0238 {
0239 s32 value;
0240 int ret;
0241
0242 ret = sensor_hub_get_feature(st->hsdev,
0243 st->sensitivity.report_id,
0244 st->sensitivity.index, sizeof(value),
0245 &value);
0246 if (ret < 0 || value < 0) {
0247 *val1 = *val2 = 0;
0248 return -EINVAL;
0249 } else {
0250 convert_from_vtf_format(value, st->sensitivity.size,
0251 st->sensitivity.unit_expo,
0252 val1, val2);
0253 }
0254
0255 return IIO_VAL_INT_PLUS_MICRO;
0256 }
0257 EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_value, IIO_HID);
0258
0259 int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1,
0260 int *val2)
0261 {
0262 s32 value;
0263 int ret;
0264
0265 ret = sensor_hub_get_feature(st->hsdev,
0266 st->sensitivity_rel.report_id,
0267 st->sensitivity_rel.index, sizeof(value),
0268 &value);
0269 if (ret < 0 || value < 0) {
0270 *val1 = *val2 = 0;
0271 return -EINVAL;
0272 }
0273
0274 convert_from_vtf_format(value, st->sensitivity_rel.size,
0275 st->sensitivity_rel.unit_expo, val1, val2);
0276
0277 return IIO_VAL_INT_PLUS_MICRO;
0278 }
0279 EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_rel_value, IIO_HID);
0280
0281
0282 int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
0283 int val1, int val2)
0284 {
0285 s32 value;
0286 int ret;
0287
0288 if (val1 < 0 || val2 < 0)
0289 return -EINVAL;
0290
0291 value = convert_to_vtf_format(st->sensitivity.size,
0292 st->sensitivity.unit_expo,
0293 val1, val2);
0294 ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id,
0295 st->sensitivity.index, sizeof(value),
0296 &value);
0297 if (ret < 0 || value < 0)
0298 return -EINVAL;
0299
0300 ret = sensor_hub_get_feature(st->hsdev,
0301 st->sensitivity.report_id,
0302 st->sensitivity.index, sizeof(value),
0303 &value);
0304 if (ret < 0 || value < 0)
0305 return -EINVAL;
0306
0307 st->raw_hystersis = value;
0308
0309 return 0;
0310 }
0311 EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_value, IIO_HID);
0312
0313 int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st,
0314 int val1, int val2)
0315 {
0316 s32 value;
0317 int ret;
0318
0319 if (val1 < 0 || val2 < 0)
0320 return -EINVAL;
0321
0322 value = convert_to_vtf_format(st->sensitivity_rel.size,
0323 st->sensitivity_rel.unit_expo,
0324 val1, val2);
0325 ret = sensor_hub_set_feature(st->hsdev, st->sensitivity_rel.report_id,
0326 st->sensitivity_rel.index, sizeof(value),
0327 &value);
0328 if (ret < 0 || value < 0)
0329 return -EINVAL;
0330
0331 ret = sensor_hub_get_feature(st->hsdev,
0332 st->sensitivity_rel.report_id,
0333 st->sensitivity_rel.index, sizeof(value),
0334 &value);
0335 if (ret < 0 || value < 0)
0336 return -EINVAL;
0337
0338 st->raw_hystersis = value;
0339
0340 return 0;
0341 }
0342 EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, IIO_HID);
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355 static void adjust_exponent_nano(int *val0, int *val1, int scale0,
0356 int scale1, int exp)
0357 {
0358 int divisor;
0359 int i;
0360 int x;
0361 int res;
0362 int rem;
0363
0364 if (exp > 0) {
0365 *val0 = scale0 * int_pow(10, exp);
0366 res = 0;
0367 if (exp > 9) {
0368 *val1 = 0;
0369 return;
0370 }
0371 for (i = 0; i < exp; ++i) {
0372 divisor = int_pow(10, 8 - i);
0373 x = scale1 / divisor;
0374 res += int_pow(10, exp - 1 - i) * x;
0375 scale1 = scale1 % divisor;
0376 }
0377 *val0 += res;
0378 *val1 = scale1 * int_pow(10, exp);
0379 } else if (exp < 0) {
0380 exp = abs(exp);
0381 if (exp > 9) {
0382 *val0 = *val1 = 0;
0383 return;
0384 }
0385 divisor = int_pow(10, exp);
0386 *val0 = scale0 / divisor;
0387 rem = scale0 % divisor;
0388 res = 0;
0389 for (i = 0; i < (9 - exp); ++i) {
0390 divisor = int_pow(10, 8 - i);
0391 x = scale1 / divisor;
0392 res += int_pow(10, 8 - exp - i) * x;
0393 scale1 = scale1 % divisor;
0394 }
0395 *val1 = rem * int_pow(10, 9 - exp) + res;
0396 } else {
0397 *val0 = scale0;
0398 *val1 = scale1;
0399 }
0400 }
0401
0402 int hid_sensor_format_scale(u32 usage_id,
0403 struct hid_sensor_hub_attribute_info *attr_info,
0404 int *val0, int *val1)
0405 {
0406 int i;
0407 int exp;
0408
0409 *val0 = 1;
0410 *val1 = 0;
0411
0412 for (i = 0; i < ARRAY_SIZE(unit_conversion); ++i) {
0413 if (unit_conversion[i].usage_id == usage_id &&
0414 unit_conversion[i].unit == attr_info->units) {
0415 exp = hid_sensor_convert_exponent(
0416 attr_info->unit_expo);
0417 adjust_exponent_nano(val0, val1,
0418 unit_conversion[i].scale_val0,
0419 unit_conversion[i].scale_val1, exp);
0420 break;
0421 }
0422 }
0423
0424 return IIO_VAL_INT_PLUS_NANO;
0425 }
0426 EXPORT_SYMBOL_NS(hid_sensor_format_scale, IIO_HID);
0427
0428 int64_t hid_sensor_convert_timestamp(struct hid_sensor_common *st,
0429 int64_t raw_value)
0430 {
0431 return st->timestamp_ns_scale * raw_value;
0432 }
0433 EXPORT_SYMBOL_NS(hid_sensor_convert_timestamp, IIO_HID);
0434
0435 static
0436 int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev,
0437 u32 usage_id,
0438 struct hid_sensor_common *st)
0439 {
0440 sensor_hub_input_get_attribute_info(hsdev,
0441 HID_FEATURE_REPORT, usage_id,
0442 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
0443 &st->poll);
0444
0445 if (st->poll.units == 0)
0446 st->poll.units = HID_USAGE_SENSOR_UNITS_MILLISECOND;
0447
0448 st->poll_interval = -1;
0449
0450 return 0;
0451
0452 }
0453
0454 static void hid_sensor_get_report_latency_info(struct hid_sensor_hub_device *hsdev,
0455 u32 usage_id,
0456 struct hid_sensor_common *st)
0457 {
0458 sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT,
0459 usage_id,
0460 HID_USAGE_SENSOR_PROP_REPORT_LATENCY,
0461 &st->report_latency);
0462
0463 hid_dbg(hsdev->hdev, "Report latency attributes: %x:%x\n",
0464 st->report_latency.index, st->report_latency.report_id);
0465 }
0466
0467 int hid_sensor_get_report_latency(struct hid_sensor_common *st)
0468 {
0469 int ret;
0470 int value;
0471
0472 ret = sensor_hub_get_feature(st->hsdev, st->report_latency.report_id,
0473 st->report_latency.index, sizeof(value),
0474 &value);
0475 if (ret < 0)
0476 return ret;
0477
0478 return value;
0479 }
0480 EXPORT_SYMBOL_NS(hid_sensor_get_report_latency, IIO_HID_ATTRIBUTES);
0481
0482 int hid_sensor_set_report_latency(struct hid_sensor_common *st, int latency_ms)
0483 {
0484 return sensor_hub_set_feature(st->hsdev, st->report_latency.report_id,
0485 st->report_latency.index,
0486 sizeof(latency_ms), &latency_ms);
0487 }
0488 EXPORT_SYMBOL_NS(hid_sensor_set_report_latency, IIO_HID_ATTRIBUTES);
0489
0490 bool hid_sensor_batch_mode_supported(struct hid_sensor_common *st)
0491 {
0492 return st->report_latency.index > 0 && st->report_latency.report_id > 0;
0493 }
0494 EXPORT_SYMBOL_NS(hid_sensor_batch_mode_supported, IIO_HID_ATTRIBUTES);
0495
0496 int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
0497 u32 usage_id,
0498 struct hid_sensor_common *st,
0499 const u32 *sensitivity_addresses,
0500 u32 sensitivity_addresses_len)
0501 {
0502
0503 struct hid_sensor_hub_attribute_info timestamp;
0504 s32 value;
0505 int ret;
0506 int i;
0507
0508 hid_sensor_get_reporting_interval(hsdev, usage_id, st);
0509
0510 sensor_hub_input_get_attribute_info(hsdev,
0511 HID_FEATURE_REPORT, usage_id,
0512 HID_USAGE_SENSOR_PROP_REPORT_STATE,
0513 &st->report_state);
0514
0515 sensor_hub_input_get_attribute_info(hsdev,
0516 HID_FEATURE_REPORT, usage_id,
0517 HID_USAGE_SENSOR_PROY_POWER_STATE,
0518 &st->power_state);
0519
0520 st->power_state.logical_minimum = 1;
0521 st->report_state.logical_minimum = 1;
0522
0523 sensor_hub_input_get_attribute_info(hsdev,
0524 HID_FEATURE_REPORT, usage_id,
0525 HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
0526 &st->sensitivity);
0527
0528 sensor_hub_input_get_attribute_info(hsdev,
0529 HID_FEATURE_REPORT, usage_id,
0530 HID_USAGE_SENSOR_PROP_SENSITIVITY_REL_PCT,
0531 &st->sensitivity_rel);
0532
0533
0534
0535
0536 for (i = 0; i < sensitivity_addresses_len; i++) {
0537 if (st->sensitivity.index < 0)
0538 sensor_hub_input_get_attribute_info(
0539 hsdev, HID_FEATURE_REPORT, usage_id,
0540 HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
0541 sensitivity_addresses[i],
0542 &st->sensitivity);
0543
0544 if (st->sensitivity_rel.index < 0)
0545 sensor_hub_input_get_attribute_info(
0546 hsdev, HID_FEATURE_REPORT, usage_id,
0547 HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_REL_PCT |
0548 sensitivity_addresses[i],
0549 &st->sensitivity_rel);
0550 }
0551
0552 st->raw_hystersis = -1;
0553
0554 sensor_hub_input_get_attribute_info(hsdev,
0555 HID_INPUT_REPORT, usage_id,
0556 HID_USAGE_SENSOR_TIME_TIMESTAMP,
0557 ×tamp);
0558 if (timestamp.index >= 0 && timestamp.report_id) {
0559 int val0, val1;
0560
0561 hid_sensor_format_scale(HID_USAGE_SENSOR_TIME_TIMESTAMP,
0562 ×tamp, &val0, &val1);
0563 st->timestamp_ns_scale = val0;
0564 } else
0565 st->timestamp_ns_scale = 1000000000;
0566
0567 hid_sensor_get_report_latency_info(hsdev, usage_id, st);
0568
0569 hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x %x:%x\n",
0570 st->poll.index, st->poll.report_id,
0571 st->report_state.index, st->report_state.report_id,
0572 st->power_state.index, st->power_state.report_id,
0573 st->sensitivity.index, st->sensitivity.report_id,
0574 timestamp.index, timestamp.report_id);
0575
0576 ret = sensor_hub_get_feature(hsdev,
0577 st->power_state.report_id,
0578 st->power_state.index, sizeof(value), &value);
0579 if (ret < 0)
0580 return ret;
0581 if (value < 0)
0582 return -EINVAL;
0583
0584 return 0;
0585 }
0586 EXPORT_SYMBOL_NS(hid_sensor_parse_common_attributes, IIO_HID);
0587
0588 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
0589 MODULE_DESCRIPTION("HID Sensor common attribute processing");
0590 MODULE_LICENSE("GPL");