brintos

brintos / linux-shallow public Read only

0
0
Text · 14.9 KiB · 9b27993 Raw
591 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * HID Sensors Driver4 * Copyright (c) 2012, Intel Corporation.5 */6#include <linux/module.h>7#include <linux/kernel.h>8#include <linux/time.h>9#include <linux/units.h>10 11#include <linux/hid-sensor-hub.h>12#include <linux/iio/iio.h>13 14static struct {15	u32 usage_id;16	int unit; /* 0 for default others from HID sensor spec */17	int scale_val0; /* scale, whole number */18	int scale_val1; /* scale, fraction in nanos */19} unit_conversion[] = {20	{HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650000},21	{HID_USAGE_SENSOR_ACCEL_3D,22		HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0},23	{HID_USAGE_SENSOR_ACCEL_3D,24		HID_USAGE_SENSOR_UNITS_G, 9, 806650000},25 26	{HID_USAGE_SENSOR_GRAVITY_VECTOR, 0, 9, 806650000},27	{HID_USAGE_SENSOR_GRAVITY_VECTOR,28		HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0},29	{HID_USAGE_SENSOR_GRAVITY_VECTOR,30		HID_USAGE_SENSOR_UNITS_G, 9, 806650000},31 32	{HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453293},33	{HID_USAGE_SENSOR_GYRO_3D,34		HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0},35	{HID_USAGE_SENSOR_GYRO_3D,36		HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453293},37 38	{HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000000},39	{HID_USAGE_SENSOR_COMPASS_3D, HID_USAGE_SENSOR_UNITS_GAUSS, 1, 0},40 41	{HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453293},42	{HID_USAGE_SENSOR_INCLINOMETER_3D,43		HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293},44	{HID_USAGE_SENSOR_INCLINOMETER_3D,45		HID_USAGE_SENSOR_UNITS_RADIANS, 1, 0},46 47	{HID_USAGE_SENSOR_ALS, 0, 1, 0},48	{HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0},49 50	{HID_USAGE_SENSOR_PRESSURE, 0, 100, 0},51	{HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000000},52 53	{HID_USAGE_SENSOR_TIME_TIMESTAMP, 0, 1000000000, 0},54	{HID_USAGE_SENSOR_TIME_TIMESTAMP, HID_USAGE_SENSOR_UNITS_MILLISECOND,55		1000000, 0},56 57	{HID_USAGE_SENSOR_DEVICE_ORIENTATION, 0, 1, 0},58 59	{HID_USAGE_SENSOR_RELATIVE_ORIENTATION, 0, 1, 0},60 61	{HID_USAGE_SENSOR_GEOMAGNETIC_ORIENTATION, 0, 1, 0},62 63	{HID_USAGE_SENSOR_TEMPERATURE, 0, 1000, 0},64	{HID_USAGE_SENSOR_TEMPERATURE, HID_USAGE_SENSOR_UNITS_DEGREES, 1000, 0},65 66	{HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0},67	{HID_USAGE_SENSOR_HINGE, 0, 0, 17453293},68	{HID_USAGE_SENSOR_HINGE, HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453293},69};70 71static void simple_div(int dividend, int divisor, int *whole,72				int *micro_frac)73{74	int rem;75	int exp = 0;76 77	*micro_frac = 0;78	if (divisor == 0) {79		*whole = 0;80		return;81	}82	*whole = dividend/divisor;83	rem = dividend % divisor;84	if (rem) {85		while (rem <= divisor) {86			rem *= 10;87			exp++;88		}89		*micro_frac = (rem / divisor) * int_pow(10, 6 - exp);90	}91}92 93static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2)94{95	int divisor = int_pow(10, exp);96 97	*val1 = no / divisor;98	*val2 = no % divisor * int_pow(10, 6 - exp);99}100 101/*102VTF format uses exponent and variable size format.103For example if the size is 2 bytes1040x0067 with VTF16E14 format -> +1.03105To convert just change to 0x67 to decimal and use two decimal as E14 stands106for 10^-2.107Negative numbers are 2's complement108*/109static void convert_from_vtf_format(u32 value, int size, int exp,110					int *val1, int *val2)111{112	int sign = 1;113 114	if (value & BIT(size*8 - 1)) {115		value =  ((1LL << (size * 8)) - value);116		sign = -1;117	}118	exp = hid_sensor_convert_exponent(exp);119	if (exp >= 0) {120		*val1 = sign * value * int_pow(10, exp);121		*val2 = 0;122	} else {123		split_micro_fraction(value, -exp, val1, val2);124		if (*val1)125			*val1 = sign * (*val1);126		else127			*val2 = sign * (*val2);128	}129}130 131static u32 convert_to_vtf_format(int size, int exp, int val1, int val2)132{133	int divisor;134	u32 value;135	int sign = 1;136 137	if (val1 < 0 || val2 < 0)138		sign = -1;139	exp = hid_sensor_convert_exponent(exp);140	if (exp < 0) {141		divisor = int_pow(10, 6 + exp);142		value = abs(val1) * int_pow(10, -exp);143		value += abs(val2) / divisor;144	} else {145		divisor = int_pow(10, exp);146		value = abs(val1) / divisor;147	}148	if (sign < 0)149		value =  ((1LL << (size * 8)) - value);150 151	return value;152}153 154s32 hid_sensor_read_poll_value(struct hid_sensor_common *st)155{156	s32 value = 0;157	int ret;158 159	ret = sensor_hub_get_feature(st->hsdev,160				     st->poll.report_id,161				     st->poll.index, sizeof(value), &value);162 163	if (ret < 0 || value < 0) {164		return -EINVAL;165	} else {166		if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)167			value = value * 1000;168	}169 170	return value;171}172EXPORT_SYMBOL_NS(hid_sensor_read_poll_value, IIO_HID_ATTRIBUTES);173 174int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,175				int *val1, int *val2)176{177	s32 value;178	int ret;179 180	ret = sensor_hub_get_feature(st->hsdev,181				     st->poll.report_id,182				     st->poll.index, sizeof(value), &value);183	if (ret < 0 || value < 0) {184		*val1 = *val2 = 0;185		return -EINVAL;186	} else {187		if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)188			simple_div(1000, value, val1, val2);189		else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)190			simple_div(1, value, val1, val2);191		else {192			*val1 = *val2 = 0;193			return -EINVAL;194		}195	}196 197	return IIO_VAL_INT_PLUS_MICRO;198}199EXPORT_SYMBOL_NS(hid_sensor_read_samp_freq_value, IIO_HID);200 201int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,202				int val1, int val2)203{204	s32 value;205	int ret;206 207	if (val1 < 0 || val2 < 0)208		return -EINVAL;209 210	value = val1 * HZ_PER_MHZ + val2;211	if (value) {212		if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)213			value = NSEC_PER_SEC / value;214		else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)215			value = USEC_PER_SEC / value;216		else217			value = 0;218	}219	ret = sensor_hub_set_feature(st->hsdev, st->poll.report_id,220				     st->poll.index, sizeof(value), &value);221	if (ret < 0 || value < 0)222		return -EINVAL;223 224	ret = sensor_hub_get_feature(st->hsdev,225				     st->poll.report_id,226				     st->poll.index, sizeof(value), &value);227	if (ret < 0 || value < 0)228		return -EINVAL;229 230	st->poll_interval = value;231 232	return 0;233}234EXPORT_SYMBOL_NS(hid_sensor_write_samp_freq_value, IIO_HID);235 236int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st,237				int *val1, int *val2)238{239	s32 value;240	int ret;241 242	ret = sensor_hub_get_feature(st->hsdev,243				     st->sensitivity.report_id,244				     st->sensitivity.index, sizeof(value),245				     &value);246	if (ret < 0 || value < 0) {247		*val1 = *val2 = 0;248		return -EINVAL;249	} else {250		convert_from_vtf_format(value, st->sensitivity.size,251					st->sensitivity.unit_expo,252					val1, val2);253	}254 255	return IIO_VAL_INT_PLUS_MICRO;256}257EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_value, IIO_HID);258 259int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1,260				       int *val2)261{262	s32 value;263	int ret;264 265	ret = sensor_hub_get_feature(st->hsdev,266				     st->sensitivity_rel.report_id,267				     st->sensitivity_rel.index, sizeof(value),268				     &value);269	if (ret < 0 || value < 0) {270		*val1 = *val2 = 0;271		return -EINVAL;272	}273 274	convert_from_vtf_format(value, st->sensitivity_rel.size,275				st->sensitivity_rel.unit_expo, val1, val2);276 277	return IIO_VAL_INT_PLUS_MICRO;278}279EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_rel_value, IIO_HID);280 281 282int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,283					int val1, int val2)284{285	s32 value;286	int ret;287 288	if (val1 < 0 || val2 < 0)289		return -EINVAL;290 291	value = convert_to_vtf_format(st->sensitivity.size,292				st->sensitivity.unit_expo,293				val1, val2);294	ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id,295				     st->sensitivity.index, sizeof(value),296				     &value);297	if (ret < 0 || value < 0)298		return -EINVAL;299 300	ret = sensor_hub_get_feature(st->hsdev,301				     st->sensitivity.report_id,302				     st->sensitivity.index, sizeof(value),303				     &value);304	if (ret < 0 || value < 0)305		return -EINVAL;306 307	st->raw_hystersis = value;308 309	return 0;310}311EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_value, IIO_HID);312 313int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st,314					int val1, int val2)315{316	s32 value;317	int ret;318 319	if (val1 < 0 || val2 < 0)320		return -EINVAL;321 322	value = convert_to_vtf_format(st->sensitivity_rel.size,323				st->sensitivity_rel.unit_expo,324				val1, val2);325	ret = sensor_hub_set_feature(st->hsdev, st->sensitivity_rel.report_id,326				     st->sensitivity_rel.index, sizeof(value),327				     &value);328	if (ret < 0 || value < 0)329		return -EINVAL;330 331	ret = sensor_hub_get_feature(st->hsdev,332				     st->sensitivity_rel.report_id,333				     st->sensitivity_rel.index, sizeof(value),334				     &value);335	if (ret < 0 || value < 0)336		return -EINVAL;337 338	st->raw_hystersis = value;339 340	return 0;341}342EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, IIO_HID);343 344/*345 * This fuction applies the unit exponent to the scale.346 * For example:347 * 9.806650000 ->exp:2-> val0[980]val1[665000000]348 * 9.000806000 ->exp:2-> val0[900]val1[80600000]349 * 0.174535293 ->exp:2-> val0[17]val1[453529300]350 * 1.001745329 ->exp:0-> val0[1]val1[1745329]351 * 1.001745329 ->exp:2-> val0[100]val1[174532900]352 * 1.001745329 ->exp:4-> val0[10017]val1[453290000]353 * 9.806650000 ->exp:-2-> val0[0]val1[98066500]354 */355static void adjust_exponent_nano(int *val0, int *val1, int scale0,356				  int scale1, int exp)357{358	int divisor;359	int i;360	int x;361	int res;362	int rem;363 364	if (exp > 0) {365		*val0 = scale0 * int_pow(10, exp);366		res = 0;367		if (exp > 9) {368			*val1 = 0;369			return;370		}371		for (i = 0; i < exp; ++i) {372			divisor = int_pow(10, 8 - i);373			x = scale1 / divisor;374			res += int_pow(10, exp - 1 - i) * x;375			scale1 = scale1 % divisor;376		}377		*val0 += res;378		*val1 = scale1 * int_pow(10, exp);379	} else if (exp < 0) {380		exp = abs(exp);381		if (exp > 9) {382			*val0 = *val1 = 0;383			return;384		}385		divisor = int_pow(10, exp);386		*val0 = scale0 / divisor;387		rem = scale0 % divisor;388		res = 0;389		for (i = 0; i < (9 - exp); ++i) {390			divisor = int_pow(10, 8 - i);391			x = scale1 / divisor;392			res += int_pow(10, 8 - exp - i) * x;393			scale1 = scale1 % divisor;394		}395		*val1 = rem * int_pow(10, 9 - exp) + res;396	} else {397		*val0 = scale0;398		*val1 = scale1;399	}400}401 402int hid_sensor_format_scale(u32 usage_id,403			struct hid_sensor_hub_attribute_info *attr_info,404			int *val0, int *val1)405{406	int i;407	int exp;408 409	*val0 = 1;410	*val1 = 0;411 412	for (i = 0; i < ARRAY_SIZE(unit_conversion); ++i) {413		if (unit_conversion[i].usage_id == usage_id &&414			unit_conversion[i].unit == attr_info->units) {415			exp  = hid_sensor_convert_exponent(416						attr_info->unit_expo);417			adjust_exponent_nano(val0, val1,418					unit_conversion[i].scale_val0,419					unit_conversion[i].scale_val1, exp);420			break;421		}422	}423 424	return IIO_VAL_INT_PLUS_NANO;425}426EXPORT_SYMBOL_NS(hid_sensor_format_scale, IIO_HID);427 428int64_t hid_sensor_convert_timestamp(struct hid_sensor_common *st,429				     int64_t raw_value)430{431	return st->timestamp_ns_scale * raw_value;432}433EXPORT_SYMBOL_NS(hid_sensor_convert_timestamp, IIO_HID);434 435static436int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev,437					u32 usage_id,438					struct hid_sensor_common *st)439{440	sensor_hub_input_get_attribute_info(hsdev,441					HID_FEATURE_REPORT, usage_id,442					HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,443					&st->poll);444	/* Default unit of measure is milliseconds */445	if (st->poll.units == 0)446		st->poll.units = HID_USAGE_SENSOR_UNITS_MILLISECOND;447 448	st->poll_interval = -1;449 450	return 0;451 452}453 454static void hid_sensor_get_report_latency_info(struct hid_sensor_hub_device *hsdev,455					       u32 usage_id,456					       struct hid_sensor_common *st)457{458	sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT,459					    usage_id,460					    HID_USAGE_SENSOR_PROP_REPORT_LATENCY,461					    &st->report_latency);462 463	hid_dbg(hsdev->hdev, "Report latency attributes: %x:%x\n",464		st->report_latency.index, st->report_latency.report_id);465}466 467int hid_sensor_get_report_latency(struct hid_sensor_common *st)468{469	int ret;470	int value;471 472	ret = sensor_hub_get_feature(st->hsdev, st->report_latency.report_id,473				     st->report_latency.index, sizeof(value),474				     &value);475	if (ret < 0)476		return ret;477 478	return value;479}480EXPORT_SYMBOL_NS(hid_sensor_get_report_latency, IIO_HID_ATTRIBUTES);481 482int hid_sensor_set_report_latency(struct hid_sensor_common *st, int latency_ms)483{484	return sensor_hub_set_feature(st->hsdev, st->report_latency.report_id,485				      st->report_latency.index,486				      sizeof(latency_ms), &latency_ms);487}488EXPORT_SYMBOL_NS(hid_sensor_set_report_latency, IIO_HID_ATTRIBUTES);489 490bool hid_sensor_batch_mode_supported(struct hid_sensor_common *st)491{492	return st->report_latency.index > 0 && st->report_latency.report_id > 0;493}494EXPORT_SYMBOL_NS(hid_sensor_batch_mode_supported, IIO_HID_ATTRIBUTES);495 496int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,497					u32 usage_id,498					struct hid_sensor_common *st,499					const u32 *sensitivity_addresses,500					u32 sensitivity_addresses_len)501{502 503	struct hid_sensor_hub_attribute_info timestamp;504	s32 value;505	int ret;506	int i;507 508	hid_sensor_get_reporting_interval(hsdev, usage_id, st);509 510	sensor_hub_input_get_attribute_info(hsdev,511					HID_FEATURE_REPORT, usage_id,512					HID_USAGE_SENSOR_PROP_REPORT_STATE,513					&st->report_state);514 515	sensor_hub_input_get_attribute_info(hsdev,516					HID_FEATURE_REPORT, usage_id,517					HID_USAGE_SENSOR_PROY_POWER_STATE,518					&st->power_state);519 520	st->power_state.logical_minimum = 1;521	st->report_state.logical_minimum = 1;522 523	sensor_hub_input_get_attribute_info(hsdev,524			HID_FEATURE_REPORT, usage_id,525			HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,526			 &st->sensitivity);527 528	sensor_hub_input_get_attribute_info(hsdev,529			HID_FEATURE_REPORT, usage_id,530			HID_USAGE_SENSOR_PROP_SENSITIVITY_REL_PCT,531			&st->sensitivity_rel);532	/*533	 * Set Sensitivity field ids, when there is no individual modifier, will534	 * check absolute sensitivity and relative sensitivity of data field535	 */536	for (i = 0; i < sensitivity_addresses_len; i++) {537		if (st->sensitivity.index < 0)538			sensor_hub_input_get_attribute_info(539				hsdev, HID_FEATURE_REPORT, usage_id,540				HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |541					sensitivity_addresses[i],542				&st->sensitivity);543 544		if (st->sensitivity_rel.index < 0)545			sensor_hub_input_get_attribute_info(546				hsdev, HID_FEATURE_REPORT, usage_id,547				HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_REL_PCT |548					sensitivity_addresses[i],549				&st->sensitivity_rel);550	}551 552	st->raw_hystersis = -1;553 554	sensor_hub_input_get_attribute_info(hsdev,555					    HID_INPUT_REPORT, usage_id,556					    HID_USAGE_SENSOR_TIME_TIMESTAMP,557					    &timestamp);558	if (timestamp.index >= 0 && timestamp.report_id) {559		int val0, val1;560 561		hid_sensor_format_scale(HID_USAGE_SENSOR_TIME_TIMESTAMP,562					&timestamp, &val0, &val1);563		st->timestamp_ns_scale = val0;564	} else565		st->timestamp_ns_scale = 1000000000;566 567	hid_sensor_get_report_latency_info(hsdev, usage_id, st);568 569	hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x %x:%x\n",570		st->poll.index, st->poll.report_id,571		st->report_state.index, st->report_state.report_id,572		st->power_state.index, st->power_state.report_id,573		st->sensitivity.index, st->sensitivity.report_id,574		timestamp.index, timestamp.report_id);575 576	ret = sensor_hub_get_feature(hsdev,577				st->power_state.report_id,578				st->power_state.index, sizeof(value), &value);579	if (ret < 0)580		return ret;581	if (value < 0)582		return -EINVAL;583 584	return 0;585}586EXPORT_SYMBOL_NS(hid_sensor_parse_common_attributes, IIO_HID);587 588MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");589MODULE_DESCRIPTION("HID Sensor common attribute processing");590MODULE_LICENSE("GPL");591