3 * Copyright (c) 2012, Intel Corporation.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
19 #include <linux/device.h>
20 #include <linux/platform_device.h>
21 #include <linux/module.h>
22 #include <linux/interrupt.h>
23 #include <linux/irq.h>
24 #include <linux/slab.h>
25 #include <linux/hid-sensor-hub.h>
26 #include <linux/iio/iio.h>
27 #include <linux/iio/sysfs.h>
31 int unit
; /* 0 for default others from HID sensor spec */
32 int scale_val0
; /* scale, whole number */
33 int scale_val1
; /* scale, fraction in nanos */
34 } unit_conversion
[] = {
35 {HID_USAGE_SENSOR_ACCEL_3D
, 0, 9, 806650000},
36 {HID_USAGE_SENSOR_ACCEL_3D
,
37 HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD
, 1, 0},
38 {HID_USAGE_SENSOR_ACCEL_3D
,
39 HID_USAGE_SENSOR_UNITS_G
, 9, 806650000},
41 {HID_USAGE_SENSOR_GRAVITY_VECTOR
, 0, 9, 806650000},
42 {HID_USAGE_SENSOR_GRAVITY_VECTOR
,
43 HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD
, 1, 0},
44 {HID_USAGE_SENSOR_GRAVITY_VECTOR
,
45 HID_USAGE_SENSOR_UNITS_G
, 9, 806650000},
47 {HID_USAGE_SENSOR_GYRO_3D
, 0, 0, 17453293},
48 {HID_USAGE_SENSOR_GYRO_3D
,
49 HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND
, 1, 0},
50 {HID_USAGE_SENSOR_GYRO_3D
,
51 HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND
, 0, 17453293},
53 {HID_USAGE_SENSOR_COMPASS_3D
, 0, 0, 1000000},
54 {HID_USAGE_SENSOR_COMPASS_3D
, HID_USAGE_SENSOR_UNITS_GAUSS
, 1, 0},
56 {HID_USAGE_SENSOR_INCLINOMETER_3D
, 0, 0, 17453293},
57 {HID_USAGE_SENSOR_INCLINOMETER_3D
,
58 HID_USAGE_SENSOR_UNITS_DEGREES
, 0, 17453293},
59 {HID_USAGE_SENSOR_INCLINOMETER_3D
,
60 HID_USAGE_SENSOR_UNITS_RADIANS
, 1, 0},
62 {HID_USAGE_SENSOR_ALS
, 0, 1, 0},
63 {HID_USAGE_SENSOR_ALS
, HID_USAGE_SENSOR_UNITS_LUX
, 1, 0},
65 {HID_USAGE_SENSOR_PRESSURE
, 0, 100, 0},
66 {HID_USAGE_SENSOR_PRESSURE
, HID_USAGE_SENSOR_UNITS_PASCAL
, 0, 1000000},
68 {HID_USAGE_SENSOR_TIME_TIMESTAMP
, 0, 1000000000, 0},
69 {HID_USAGE_SENSOR_TIME_TIMESTAMP
, HID_USAGE_SENSOR_UNITS_MILLISECOND
,
72 {HID_USAGE_SENSOR_TEMPERATURE
, 0, 1000, 0},
73 {HID_USAGE_SENSOR_TEMPERATURE
, HID_USAGE_SENSOR_UNITS_DEGREES
, 1000, 0},
75 {HID_USAGE_SENSOR_HUMIDITY
, 0, 1000, 0},
78 static int pow_10(unsigned power
)
82 for (i
= 0; i
< power
; ++i
)
88 static void simple_div(int dividend
, int divisor
, int *whole
,
99 *whole
= dividend
/divisor
;
100 rem
= dividend
% divisor
;
102 while (rem
<= divisor
) {
106 *micro_frac
= (rem
/ divisor
) * pow_10(6-exp
);
110 static void split_micro_fraction(unsigned int no
, int exp
, int *val1
, int *val2
)
112 *val1
= no
/pow_10(exp
);
113 *val2
= no
%pow_10(exp
) * pow_10(6-exp
);
117 VTF format uses exponent and variable size format.
118 For example if the size is 2 bytes
119 0x0067 with VTF16E14 format -> +1.03
120 To convert just change to 0x67 to decimal and use two decimal as E14 stands
122 Negative numbers are 2's complement
124 static void convert_from_vtf_format(u32 value
, int size
, int exp
,
125 int *val1
, int *val2
)
129 if (value
& BIT(size
*8 - 1)) {
130 value
= ((1LL << (size
* 8)) - value
);
133 exp
= hid_sensor_convert_exponent(exp
);
135 *val1
= sign
* value
* pow_10(exp
);
138 split_micro_fraction(value
, -exp
, val1
, val2
);
140 *val1
= sign
* (*val1
);
142 *val2
= sign
* (*val2
);
146 static u32
convert_to_vtf_format(int size
, int exp
, int val1
, int val2
)
151 if (val1
< 0 || val2
< 0)
153 exp
= hid_sensor_convert_exponent(exp
);
155 value
= abs(val1
) * pow_10(-exp
);
156 value
+= abs(val2
) / pow_10(6+exp
);
158 value
= abs(val1
) / pow_10(exp
);
160 value
= ((1LL << (size
* 8)) - value
);
165 s32
hid_sensor_read_poll_value(struct hid_sensor_common
*st
)
170 ret
= sensor_hub_get_feature(st
->hsdev
,
172 st
->poll
.index
, sizeof(value
), &value
);
174 if (ret
< 0 || value
< 0) {
177 if (st
->poll
.units
== HID_USAGE_SENSOR_UNITS_SECOND
)
178 value
= value
* 1000;
183 EXPORT_SYMBOL(hid_sensor_read_poll_value
);
185 int hid_sensor_read_samp_freq_value(struct hid_sensor_common
*st
,
186 int *val1
, int *val2
)
191 ret
= sensor_hub_get_feature(st
->hsdev
,
193 st
->poll
.index
, sizeof(value
), &value
);
194 if (ret
< 0 || value
< 0) {
198 if (st
->poll
.units
== HID_USAGE_SENSOR_UNITS_MILLISECOND
)
199 simple_div(1000, value
, val1
, val2
);
200 else if (st
->poll
.units
== HID_USAGE_SENSOR_UNITS_SECOND
)
201 simple_div(1, value
, val1
, val2
);
208 return IIO_VAL_INT_PLUS_MICRO
;
210 EXPORT_SYMBOL(hid_sensor_read_samp_freq_value
);
212 int hid_sensor_write_samp_freq_value(struct hid_sensor_common
*st
,
218 if (val1
< 0 || val2
< 0)
221 value
= val1
* pow_10(6) + val2
;
223 if (st
->poll
.units
== HID_USAGE_SENSOR_UNITS_MILLISECOND
)
224 value
= pow_10(9)/value
;
225 else if (st
->poll
.units
== HID_USAGE_SENSOR_UNITS_SECOND
)
226 value
= pow_10(6)/value
;
230 ret
= sensor_hub_set_feature(st
->hsdev
, st
->poll
.report_id
,
231 st
->poll
.index
, sizeof(value
), &value
);
232 if (ret
< 0 || value
< 0)
235 ret
= sensor_hub_get_feature(st
->hsdev
,
237 st
->poll
.index
, sizeof(value
), &value
);
238 if (ret
< 0 || value
< 0)
241 st
->poll_interval
= value
;
245 EXPORT_SYMBOL(hid_sensor_write_samp_freq_value
);
247 int hid_sensor_read_raw_hyst_value(struct hid_sensor_common
*st
,
248 int *val1
, int *val2
)
253 ret
= sensor_hub_get_feature(st
->hsdev
,
254 st
->sensitivity
.report_id
,
255 st
->sensitivity
.index
, sizeof(value
),
257 if (ret
< 0 || value
< 0) {
261 convert_from_vtf_format(value
, st
->sensitivity
.size
,
262 st
->sensitivity
.unit_expo
,
266 return IIO_VAL_INT_PLUS_MICRO
;
268 EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value
);
270 int hid_sensor_write_raw_hyst_value(struct hid_sensor_common
*st
,
276 if (val1
< 0 || val2
< 0)
279 value
= convert_to_vtf_format(st
->sensitivity
.size
,
280 st
->sensitivity
.unit_expo
,
282 ret
= sensor_hub_set_feature(st
->hsdev
, st
->sensitivity
.report_id
,
283 st
->sensitivity
.index
, sizeof(value
),
285 if (ret
< 0 || value
< 0)
288 ret
= sensor_hub_get_feature(st
->hsdev
,
289 st
->sensitivity
.report_id
,
290 st
->sensitivity
.index
, sizeof(value
),
292 if (ret
< 0 || value
< 0)
295 st
->raw_hystersis
= value
;
299 EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value
);
302 * This fuction applies the unit exponent to the scale.
304 * 9.806650000 ->exp:2-> val0[980]val1[665000000]
305 * 9.000806000 ->exp:2-> val0[900]val1[80600000]
306 * 0.174535293 ->exp:2-> val0[17]val1[453529300]
307 * 1.001745329 ->exp:0-> val0[1]val1[1745329]
308 * 1.001745329 ->exp:2-> val0[100]val1[174532900]
309 * 1.001745329 ->exp:4-> val0[10017]val1[453290000]
310 * 9.806650000 ->exp:-2-> val0[0]val1[98066500]
312 static void adjust_exponent_nano(int *val0
, int *val1
, int scale0
,
321 *val0
= scale0
* pow_10(exp
);
327 for (i
= 0; i
< exp
; ++i
) {
328 x
= scale1
/ pow_10(8 - i
);
329 res
+= (pow_10(exp
- 1 - i
) * x
);
330 scale1
= scale1
% pow_10(8 - i
);
333 *val1
= scale1
* pow_10(exp
);
334 } else if (exp
< 0) {
340 *val0
= scale0
/ pow_10(exp
);
341 rem
= scale0
% pow_10(exp
);
343 for (i
= 0; i
< (9 - exp
); ++i
) {
344 x
= scale1
/ pow_10(8 - i
);
345 res
+= (pow_10(8 - exp
- i
) * x
);
346 scale1
= scale1
% pow_10(8 - i
);
348 *val1
= rem
* pow_10(9 - exp
) + res
;
355 int hid_sensor_format_scale(u32 usage_id
,
356 struct hid_sensor_hub_attribute_info
*attr_info
,
357 int *val0
, int *val1
)
365 for (i
= 0; i
< ARRAY_SIZE(unit_conversion
); ++i
) {
366 if (unit_conversion
[i
].usage_id
== usage_id
&&
367 unit_conversion
[i
].unit
== attr_info
->units
) {
368 exp
= hid_sensor_convert_exponent(
369 attr_info
->unit_expo
);
370 adjust_exponent_nano(val0
, val1
,
371 unit_conversion
[i
].scale_val0
,
372 unit_conversion
[i
].scale_val1
, exp
);
377 return IIO_VAL_INT_PLUS_NANO
;
379 EXPORT_SYMBOL(hid_sensor_format_scale
);
381 int64_t hid_sensor_convert_timestamp(struct hid_sensor_common
*st
,
384 return st
->timestamp_ns_scale
* raw_value
;
386 EXPORT_SYMBOL(hid_sensor_convert_timestamp
);
389 int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device
*hsdev
,
391 struct hid_sensor_common
*st
)
393 sensor_hub_input_get_attribute_info(hsdev
,
394 HID_FEATURE_REPORT
, usage_id
,
395 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL
,
397 /* Default unit of measure is milliseconds */
398 if (st
->poll
.units
== 0)
399 st
->poll
.units
= HID_USAGE_SENSOR_UNITS_MILLISECOND
;
401 st
->poll_interval
= -1;
407 int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device
*hsdev
,
409 struct hid_sensor_common
*st
)
412 struct hid_sensor_hub_attribute_info timestamp
;
416 hid_sensor_get_reporting_interval(hsdev
, usage_id
, st
);
418 sensor_hub_input_get_attribute_info(hsdev
,
419 HID_FEATURE_REPORT
, usage_id
,
420 HID_USAGE_SENSOR_PROP_REPORT_STATE
,
423 sensor_hub_input_get_attribute_info(hsdev
,
424 HID_FEATURE_REPORT
, usage_id
,
425 HID_USAGE_SENSOR_PROY_POWER_STATE
,
428 sensor_hub_input_get_attribute_info(hsdev
,
429 HID_FEATURE_REPORT
, usage_id
,
430 HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS
,
433 st
->raw_hystersis
= -1;
435 sensor_hub_input_get_attribute_info(hsdev
,
436 HID_INPUT_REPORT
, usage_id
,
437 HID_USAGE_SENSOR_TIME_TIMESTAMP
,
439 if (timestamp
.index
>= 0 && timestamp
.report_id
) {
442 hid_sensor_format_scale(HID_USAGE_SENSOR_TIME_TIMESTAMP
,
443 ×tamp
, &val0
, &val1
);
444 st
->timestamp_ns_scale
= val0
;
446 st
->timestamp_ns_scale
= 1000000000;
448 hid_dbg(hsdev
->hdev
, "common attributes: %x:%x, %x:%x, %x:%x %x:%x %x:%x\n",
449 st
->poll
.index
, st
->poll
.report_id
,
450 st
->report_state
.index
, st
->report_state
.report_id
,
451 st
->power_state
.index
, st
->power_state
.report_id
,
452 st
->sensitivity
.index
, st
->sensitivity
.report_id
,
453 timestamp
.index
, timestamp
.report_id
);
455 ret
= sensor_hub_get_feature(hsdev
,
456 st
->power_state
.report_id
,
457 st
->power_state
.index
, sizeof(value
), &value
);
465 EXPORT_SYMBOL(hid_sensor_parse_common_attributes
);
467 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
468 MODULE_DESCRIPTION("HID Sensor common attribute processing");
469 MODULE_LICENSE("GPL");