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 micros */
34 } unit_conversion
[] = {
35 {HID_USAGE_SENSOR_ACCEL_3D
, 0, 9, 806650},
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, 806650},
41 {HID_USAGE_SENSOR_GYRO_3D
, 0, 0, 17453},
42 {HID_USAGE_SENSOR_GYRO_3D
,
43 HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND
, 1, 0},
44 {HID_USAGE_SENSOR_GYRO_3D
,
45 HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND
, 0, 17453},
47 {HID_USAGE_SENSOR_COMPASS_3D
, 0, 0, 1000},
48 {HID_USAGE_SENSOR_COMPASS_3D
, HID_USAGE_SENSOR_UNITS_GAUSS
, 1, 0},
50 {HID_USAGE_SENSOR_INCLINOMETER_3D
, 0, 0, 17453},
51 {HID_USAGE_SENSOR_INCLINOMETER_3D
,
52 HID_USAGE_SENSOR_UNITS_DEGREES
, 0, 17453},
53 {HID_USAGE_SENSOR_INCLINOMETER_3D
,
54 HID_USAGE_SENSOR_UNITS_RADIANS
, 1, 0},
56 {HID_USAGE_SENSOR_ALS
, 0, 1, 0},
57 {HID_USAGE_SENSOR_ALS
, HID_USAGE_SENSOR_UNITS_LUX
, 1, 0},
59 {HID_USAGE_SENSOR_PRESSURE
, 0, 100000, 0},
60 {HID_USAGE_SENSOR_PRESSURE
, HID_USAGE_SENSOR_UNITS_PASCAL
, 1, 0},
63 static int pow_10(unsigned power
)
67 for (i
= 0; i
< power
; ++i
)
73 static void simple_div(int dividend
, int divisor
, int *whole
,
84 *whole
= dividend
/divisor
;
85 rem
= dividend
% divisor
;
87 while (rem
<= divisor
) {
91 *micro_frac
= (rem
/ divisor
) * pow_10(6-exp
);
95 static void split_micro_fraction(unsigned int no
, int exp
, int *val1
, int *val2
)
97 *val1
= no
/pow_10(exp
);
98 *val2
= no
%pow_10(exp
) * pow_10(6-exp
);
102 VTF format uses exponent and variable size format.
103 For example if the size is 2 bytes
104 0x0067 with VTF16E14 format -> +1.03
105 To convert just change to 0x67 to decimal and use two decimal as E14 stands
107 Negative numbers are 2's complement
109 static void convert_from_vtf_format(u32 value
, int size
, int exp
,
110 int *val1
, int *val2
)
114 if (value
& BIT(size
*8 - 1)) {
115 value
= ((1LL << (size
* 8)) - value
);
118 exp
= hid_sensor_convert_exponent(exp
);
120 *val1
= sign
* value
* pow_10(exp
);
123 split_micro_fraction(value
, -exp
, val1
, val2
);
125 *val1
= sign
* (*val1
);
127 *val2
= sign
* (*val2
);
131 static u32
convert_to_vtf_format(int size
, int exp
, int val1
, int val2
)
136 if (val1
< 0 || val2
< 0)
138 exp
= hid_sensor_convert_exponent(exp
);
140 value
= abs(val1
) * pow_10(-exp
);
141 value
+= abs(val2
) / pow_10(6+exp
);
143 value
= abs(val1
) / pow_10(exp
);
145 value
= ((1LL << (size
* 8)) - value
);
150 s32
hid_sensor_read_poll_value(struct hid_sensor_common
*st
)
155 ret
= sensor_hub_get_feature(st
->hsdev
,
157 st
->poll
.index
, sizeof(value
), &value
);
159 if (ret
< 0 || value
< 0) {
162 if (st
->poll
.units
== HID_USAGE_SENSOR_UNITS_SECOND
)
163 value
= value
* 1000;
168 EXPORT_SYMBOL(hid_sensor_read_poll_value
);
170 int hid_sensor_read_samp_freq_value(struct hid_sensor_common
*st
,
171 int *val1
, int *val2
)
176 ret
= sensor_hub_get_feature(st
->hsdev
,
178 st
->poll
.index
, sizeof(value
), &value
);
179 if (ret
< 0 || value
< 0) {
183 if (st
->poll
.units
== HID_USAGE_SENSOR_UNITS_MILLISECOND
)
184 simple_div(1000, value
, val1
, val2
);
185 else if (st
->poll
.units
== HID_USAGE_SENSOR_UNITS_SECOND
)
186 simple_div(1, value
, val1
, val2
);
193 return IIO_VAL_INT_PLUS_MICRO
;
195 EXPORT_SYMBOL(hid_sensor_read_samp_freq_value
);
197 int hid_sensor_write_samp_freq_value(struct hid_sensor_common
*st
,
203 if (val1
< 0 || val2
< 0)
206 value
= val1
* pow_10(6) + val2
;
208 if (st
->poll
.units
== HID_USAGE_SENSOR_UNITS_MILLISECOND
)
209 value
= pow_10(9)/value
;
210 else if (st
->poll
.units
== HID_USAGE_SENSOR_UNITS_SECOND
)
211 value
= pow_10(6)/value
;
215 ret
= sensor_hub_set_feature(st
->hsdev
, st
->poll
.report_id
,
216 st
->poll
.index
, sizeof(value
), &value
);
217 if (ret
< 0 || value
< 0)
222 EXPORT_SYMBOL(hid_sensor_write_samp_freq_value
);
224 int hid_sensor_read_raw_hyst_value(struct hid_sensor_common
*st
,
225 int *val1
, int *val2
)
230 ret
= sensor_hub_get_feature(st
->hsdev
,
231 st
->sensitivity
.report_id
,
232 st
->sensitivity
.index
, sizeof(value
),
234 if (ret
< 0 || value
< 0) {
238 convert_from_vtf_format(value
, st
->sensitivity
.size
,
239 st
->sensitivity
.unit_expo
,
243 return IIO_VAL_INT_PLUS_MICRO
;
245 EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value
);
247 int hid_sensor_write_raw_hyst_value(struct hid_sensor_common
*st
,
253 value
= convert_to_vtf_format(st
->sensitivity
.size
,
254 st
->sensitivity
.unit_expo
,
256 ret
= sensor_hub_set_feature(st
->hsdev
, st
->sensitivity
.report_id
,
257 st
->sensitivity
.index
, sizeof(value
),
259 if (ret
< 0 || value
< 0)
264 EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value
);
267 * This fuction applies the unit exponent to the scale.
269 * 9.806650 ->exp:2-> val0[980]val1[665000]
270 * 9.000806 ->exp:2-> val0[900]val1[80600]
271 * 0.174535 ->exp:2-> val0[17]val1[453500]
272 * 1.001745 ->exp:0-> val0[1]val1[1745]
273 * 1.001745 ->exp:2-> val0[100]val1[174500]
274 * 1.001745 ->exp:4-> val0[10017]val1[450000]
275 * 9.806650 ->exp:-2-> val0[0]val1[98066]
277 static void adjust_exponent_micro(int *val0
, int *val1
, int scale0
,
286 *val0
= scale0
* pow_10(exp
);
292 for (i
= 0; i
< exp
; ++i
) {
293 x
= scale1
/ pow_10(5 - i
);
294 res
+= (pow_10(exp
- 1 - i
) * x
);
295 scale1
= scale1
% pow_10(5 - i
);
298 *val1
= scale1
* pow_10(exp
);
299 } else if (exp
< 0) {
305 *val0
= scale0
/ pow_10(exp
);
306 rem
= scale0
% pow_10(exp
);
308 for (i
= 0; i
< (6 - exp
); ++i
) {
309 x
= scale1
/ pow_10(5 - i
);
310 res
+= (pow_10(5 - exp
- i
) * x
);
311 scale1
= scale1
% pow_10(5 - i
);
313 *val1
= rem
* pow_10(6 - exp
) + res
;
320 int hid_sensor_format_scale(u32 usage_id
,
321 struct hid_sensor_hub_attribute_info
*attr_info
,
322 int *val0
, int *val1
)
330 for (i
= 0; i
< ARRAY_SIZE(unit_conversion
); ++i
) {
331 if (unit_conversion
[i
].usage_id
== usage_id
&&
332 unit_conversion
[i
].unit
== attr_info
->units
) {
333 exp
= hid_sensor_convert_exponent(
334 attr_info
->unit_expo
);
335 adjust_exponent_micro(val0
, val1
,
336 unit_conversion
[i
].scale_val0
,
337 unit_conversion
[i
].scale_val1
, exp
);
342 return IIO_VAL_INT_PLUS_MICRO
;
344 EXPORT_SYMBOL(hid_sensor_format_scale
);
347 int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device
*hsdev
,
349 struct hid_sensor_common
*st
)
351 sensor_hub_input_get_attribute_info(hsdev
,
352 HID_FEATURE_REPORT
, usage_id
,
353 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL
,
355 /* Default unit of measure is milliseconds */
356 if (st
->poll
.units
== 0)
357 st
->poll
.units
= HID_USAGE_SENSOR_UNITS_MILLISECOND
;
362 int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device
*hsdev
,
364 struct hid_sensor_common
*st
)
368 hid_sensor_get_reporting_interval(hsdev
, usage_id
, st
);
370 sensor_hub_input_get_attribute_info(hsdev
,
371 HID_FEATURE_REPORT
, usage_id
,
372 HID_USAGE_SENSOR_PROP_REPORT_STATE
,
375 sensor_hub_input_get_attribute_info(hsdev
,
376 HID_FEATURE_REPORT
, usage_id
,
377 HID_USAGE_SENSOR_PROY_POWER_STATE
,
380 sensor_hub_input_get_attribute_info(hsdev
,
381 HID_FEATURE_REPORT
, usage_id
,
382 HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS
,
385 hid_dbg(hsdev
->hdev
, "common attributes: %x:%x, %x:%x, %x:%x %x:%x\n",
386 st
->poll
.index
, st
->poll
.report_id
,
387 st
->report_state
.index
, st
->report_state
.report_id
,
388 st
->power_state
.index
, st
->power_state
.report_id
,
389 st
->sensitivity
.index
, st
->sensitivity
.report_id
);
393 EXPORT_SYMBOL(hid_sensor_parse_common_attributes
);
395 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
396 MODULE_DESCRIPTION("HID Sensor common attribute processing");
397 MODULE_LICENSE("GPL");