Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * HID Sensors Driver
0004  * Copyright (c) 2012, Intel Corporation.
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; /* 0 for default others from HID sensor spec */
0017     int scale_val0; /* scale, whole number */
0018     int scale_val1; /* scale, fraction in nanos */
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 VTF format uses exponent and variable size format.
0103 For example if the size is 2 bytes
0104 0x0067 with VTF16E14 format -> +1.03
0105 To convert just change to 0x67 to decimal and use two decimal as E14 stands
0106 for 10^-2.
0107 Negative numbers are 2's complement
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  * This fuction applies the unit exponent to the scale.
0346  * For example:
0347  * 9.806650000 ->exp:2-> val0[980]val1[665000000]
0348  * 9.000806000 ->exp:2-> val0[900]val1[80600000]
0349  * 0.174535293 ->exp:2-> val0[17]val1[453529300]
0350  * 1.001745329 ->exp:0-> val0[1]val1[1745329]
0351  * 1.001745329 ->exp:2-> val0[100]val1[174532900]
0352  * 1.001745329 ->exp:4-> val0[10017]val1[453290000]
0353  * 9.806650000 ->exp:-2-> val0[0]val1[98066500]
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     /* Default unit of measure is milliseconds */
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      * Set Sensitivity field ids, when there is no individual modifier, will
0534      * check absolute sensitivity and relative sensitivity of data field
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                         &timestamp);
0558     if (timestamp.index >= 0 && timestamp.report_id) {
0559         int val0, val1;
0560 
0561         hid_sensor_format_scale(HID_USAGE_SENSOR_TIME_TIMESTAMP,
0562                     &timestamp, &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");