1 /* 2 * HID Sensors Driver 3 * Copyright (c) 2012, Intel Corporation. 4 * 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. 8 * 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 12 * more details. 13 * 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. 17 * 18 */ 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> 28 #include "hid-sensor-attributes.h" 29 30 static int pow_10(unsigned power) 31 { 32 int i; 33 int ret = 1; 34 for (i = 0; i < power; ++i) 35 ret = ret * 10; 36 37 return ret; 38 } 39 40 static void simple_div(int dividend, int divisor, int *whole, 41 int *micro_frac) 42 { 43 int rem; 44 int exp = 0; 45 46 *micro_frac = 0; 47 if (divisor == 0) { 48 *whole = 0; 49 return; 50 } 51 *whole = dividend/divisor; 52 rem = dividend % divisor; 53 if (rem) { 54 while (rem <= divisor) { 55 rem *= 10; 56 exp++; 57 } 58 *micro_frac = (rem / divisor) * pow_10(6-exp); 59 } 60 } 61 62 static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2) 63 { 64 *val1 = no/pow_10(exp); 65 *val2 = no%pow_10(exp) * pow_10(6-exp); 66 } 67 68 /* 69 VTF format uses exponent and variable size format. 70 For example if the size is 2 bytes 71 0x0067 with VTF16E14 format -> +1.03 72 To convert just change to 0x67 to decimal and use two decimal as E14 stands 73 for 10^-2. 74 Negative numbers are 2's complement 75 */ 76 static void convert_from_vtf_format(u32 value, int size, int exp, 77 int *val1, int *val2) 78 { 79 int sign = 1; 80 81 if (value & BIT(size*8 - 1)) { 82 value = ((1LL << (size * 8)) - value); 83 sign = -1; 84 } 85 exp = hid_sensor_convert_exponent(exp); 86 if (exp >= 0) { 87 *val1 = sign * value * pow_10(exp); 88 *val2 = 0; 89 } else { 90 split_micro_fraction(value, -exp, val1, val2); 91 if (*val1) 92 *val1 = sign * (*val1); 93 else 94 *val2 = sign * (*val2); 95 } 96 } 97 98 static u32 convert_to_vtf_format(int size, int exp, int val1, int val2) 99 { 100 u32 value; 101 int sign = 1; 102 103 if (val1 < 0 || val2 < 0) 104 sign = -1; 105 exp = hid_sensor_convert_exponent(exp); 106 if (exp < 0) { 107 value = abs(val1) * pow_10(-exp); 108 value += abs(val2) / pow_10(6+exp); 109 } else 110 value = abs(val1) / pow_10(exp); 111 if (sign < 0) 112 value = ((1LL << (size * 8)) - value); 113 114 return value; 115 } 116 117 int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st, 118 int *val1, int *val2) 119 { 120 s32 value; 121 int ret; 122 123 ret = sensor_hub_get_feature(st->hsdev, 124 st->poll.report_id, 125 st->poll.index, &value); 126 if (ret < 0 || value < 0) { 127 *val1 = *val2 = 0; 128 return -EINVAL; 129 } else { 130 if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND) 131 simple_div(1000, value, val1, val2); 132 else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND) 133 simple_div(1, value, val1, val2); 134 else { 135 *val1 = *val2 = 0; 136 return -EINVAL; 137 } 138 } 139 140 return IIO_VAL_INT_PLUS_MICRO; 141 } 142 EXPORT_SYMBOL(hid_sensor_read_samp_freq_value); 143 144 int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st, 145 int val1, int val2) 146 { 147 s32 value; 148 int ret; 149 150 if (val1 < 0 || val2 < 0) 151 ret = -EINVAL; 152 153 value = val1 * pow_10(6) + val2; 154 if (value) { 155 if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND) 156 value = pow_10(9)/value; 157 else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND) 158 value = pow_10(6)/value; 159 else 160 value = 0; 161 } 162 ret = sensor_hub_set_feature(st->hsdev, 163 st->poll.report_id, 164 st->poll.index, value); 165 if (ret < 0 || value < 0) 166 ret = -EINVAL; 167 168 return ret; 169 } 170 EXPORT_SYMBOL(hid_sensor_write_samp_freq_value); 171 172 int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st, 173 int *val1, int *val2) 174 { 175 s32 value; 176 int ret; 177 178 ret = sensor_hub_get_feature(st->hsdev, 179 st->sensitivity.report_id, 180 st->sensitivity.index, &value); 181 if (ret < 0 || value < 0) { 182 *val1 = *val2 = 0; 183 return -EINVAL; 184 } else { 185 convert_from_vtf_format(value, st->sensitivity.size, 186 st->sensitivity.unit_expo, 187 val1, val2); 188 } 189 190 return IIO_VAL_INT_PLUS_MICRO; 191 } 192 EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value); 193 194 int hid_sensor_write_raw_hyst_value(struct hid_sensor_iio_common *st, 195 int val1, int val2) 196 { 197 s32 value; 198 int ret; 199 200 value = convert_to_vtf_format(st->sensitivity.size, 201 st->sensitivity.unit_expo, 202 val1, val2); 203 ret = sensor_hub_set_feature(st->hsdev, 204 st->sensitivity.report_id, 205 st->sensitivity.index, value); 206 if (ret < 0 || value < 0) 207 ret = -EINVAL; 208 209 return ret; 210 } 211 EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value); 212 213 int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, 214 u32 usage_id, 215 struct hid_sensor_iio_common *st) 216 { 217 218 sensor_hub_input_get_attribute_info(hsdev, 219 HID_FEATURE_REPORT, usage_id, 220 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL, 221 &st->poll); 222 223 sensor_hub_input_get_attribute_info(hsdev, 224 HID_FEATURE_REPORT, usage_id, 225 HID_USAGE_SENSOR_PROP_REPORT_STATE, 226 &st->report_state); 227 228 sensor_hub_input_get_attribute_info(hsdev, 229 HID_FEATURE_REPORT, usage_id, 230 HID_USAGE_SENSOR_PROY_POWER_STATE, 231 &st->power_state); 232 233 sensor_hub_input_get_attribute_info(hsdev, 234 HID_FEATURE_REPORT, usage_id, 235 HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, 236 &st->sensitivity); 237 238 hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x\n", 239 st->poll.index, st->poll.report_id, 240 st->report_state.index, st->report_state.report_id, 241 st->power_state.index, st->power_state.report_id, 242 st->sensitivity.index, st->sensitivity.report_id); 243 244 return 0; 245 } 246 EXPORT_SYMBOL(hid_sensor_parse_common_attributes); 247 248 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); 249 MODULE_DESCRIPTION("HID Sensor common attribute processing"); 250 MODULE_LICENSE("GPL"); 251