xref: /linux/drivers/iio/orientation/hid-sensor-rotation.c (revision 23d66dbab84e8518943563df2ced14aaab28b77a)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * HID Sensors Driver
4  * Copyright (c) 2014, Intel Corporation.
5  */
6 
7 #include <linux/device.h>
8 #include <linux/platform_device.h>
9 #include <linux/module.h>
10 #include <linux/mod_devicetable.h>
11 #include <linux/hid-sensor-hub.h>
12 #include <linux/iio/iio.h>
13 #include <linux/iio/sysfs.h>
14 #include <linux/iio/buffer.h>
15 #include "../common/hid-sensors/hid-sensor-trigger.h"
16 
17 struct dev_rot_state {
18 	struct hid_sensor_hub_callbacks callbacks;
19 	struct hid_sensor_common common_attributes;
20 	struct hid_sensor_hub_attribute_info quaternion;
21 	struct {
22 		IIO_DECLARE_QUATERNION(s32, sampled_vals);
23 		/*
24 		 * ABI regression avoidance: There are two copies of the same
25 		 * timestamp in case of userspace depending on broken alignment
26 		 * from older kernels.
27 		 */
28 		aligned_s64 timestamp[2];
29 	} scan;
30 	int scale_pre_decml;
31 	int scale_post_decml;
32 	int scale_precision;
33 	int value_offset;
34 	s64 timestamp;
35 };
36 
37 static const u32 rotation_sensitivity_addresses[] = {
38 	HID_USAGE_SENSOR_DATA_ORIENTATION,
39 	HID_USAGE_SENSOR_ORIENT_QUATERNION,
40 };
41 
42 enum {
43 	DEV_ROT_SCAN_TYPE_16BIT,
44 	DEV_ROT_SCAN_TYPE_32BIT,
45 };
46 
47 static const struct iio_scan_type dev_rot_scan_types[] = {
48 	[DEV_ROT_SCAN_TYPE_16BIT] = {
49 		.sign = 's',
50 		.realbits = 16,
51 		/* Storage bits has to stay 32 to not break userspace. */
52 		.storagebits = 32,
53 		.repeat = 4,
54 	},
55 	[DEV_ROT_SCAN_TYPE_32BIT] = {
56 		.sign = 's',
57 		.realbits = 32,
58 		.storagebits = 32,
59 		.repeat = 4,
60 	},
61 };
62 
63 /* Channel definitions */
64 static const struct iio_chan_spec dev_rot_channels[] = {
65 	{
66 		.type = IIO_ROT,
67 		.modified = 1,
68 		.channel2 = IIO_MOD_QUATERNION,
69 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
70 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
71 					BIT(IIO_CHAN_INFO_OFFSET) |
72 					BIT(IIO_CHAN_INFO_SCALE) |
73 					BIT(IIO_CHAN_INFO_HYSTERESIS),
74 		.scan_index = 0,
75 		.has_ext_scan_type = 1,
76 		.ext_scan_type = dev_rot_scan_types,
77 		.num_ext_scan_type = ARRAY_SIZE(dev_rot_scan_types),
78 	},
79 	IIO_CHAN_SOFT_TIMESTAMP(1)
80 };
81 
82 /* Channel read_raw handler */
83 static int dev_rot_read_raw(struct iio_dev *indio_dev,
84 				struct iio_chan_spec const *chan,
85 				int size, int *vals, int *val_len,
86 				long mask)
87 {
88 	struct dev_rot_state *rot_state = iio_priv(indio_dev);
89 	int ret_type;
90 	int i;
91 
92 	vals[0] = 0;
93 	vals[1] = 0;
94 
95 	switch (mask) {
96 	case IIO_CHAN_INFO_RAW:
97 		if (size >= 4) {
98 			for (i = 0; i < 4; ++i)
99 				vals[i] = rot_state->scan.sampled_vals[i];
100 			ret_type = IIO_VAL_INT_MULTIPLE;
101 			*val_len =  4;
102 		} else
103 			ret_type = -EINVAL;
104 		break;
105 	case IIO_CHAN_INFO_SCALE:
106 		vals[0] = rot_state->scale_pre_decml;
107 		vals[1] = rot_state->scale_post_decml;
108 		return rot_state->scale_precision;
109 
110 	case IIO_CHAN_INFO_OFFSET:
111 		*vals = rot_state->value_offset;
112 		return IIO_VAL_INT;
113 
114 	case IIO_CHAN_INFO_SAMP_FREQ:
115 		ret_type = hid_sensor_read_samp_freq_value(
116 			&rot_state->common_attributes, &vals[0], &vals[1]);
117 		break;
118 	case IIO_CHAN_INFO_HYSTERESIS:
119 		ret_type = hid_sensor_read_raw_hyst_value(
120 			&rot_state->common_attributes, &vals[0], &vals[1]);
121 		break;
122 	default:
123 		ret_type = -EINVAL;
124 		break;
125 	}
126 
127 	return ret_type;
128 }
129 
130 /* Channel write_raw handler */
131 static int dev_rot_write_raw(struct iio_dev *indio_dev,
132 			       struct iio_chan_spec const *chan,
133 			       int val,
134 			       int val2,
135 			       long mask)
136 {
137 	struct dev_rot_state *rot_state = iio_priv(indio_dev);
138 	int ret;
139 
140 	switch (mask) {
141 	case IIO_CHAN_INFO_SAMP_FREQ:
142 		ret = hid_sensor_write_samp_freq_value(
143 				&rot_state->common_attributes, val, val2);
144 		break;
145 	case IIO_CHAN_INFO_HYSTERESIS:
146 		ret = hid_sensor_write_raw_hyst_value(
147 				&rot_state->common_attributes, val, val2);
148 		break;
149 	default:
150 		ret = -EINVAL;
151 	}
152 
153 	return ret;
154 }
155 
156 static int dev_rot_get_current_scan_type(const struct iio_dev *indio_dev,
157 					 const struct iio_chan_spec *chan)
158 {
159 	struct dev_rot_state *rot_state = iio_priv(indio_dev);
160 
161 	switch (rot_state->quaternion.size / 4) {
162 	case sizeof(s16):
163 		return DEV_ROT_SCAN_TYPE_16BIT;
164 	case sizeof(s32):
165 		return DEV_ROT_SCAN_TYPE_32BIT;
166 	default:
167 		return -EINVAL;
168 	}
169 }
170 
171 static const struct iio_info dev_rot_info = {
172 	.read_raw_multi = &dev_rot_read_raw,
173 	.write_raw = &dev_rot_write_raw,
174 	.get_current_scan_type = &dev_rot_get_current_scan_type,
175 };
176 
177 /* Callback handler to send event after all samples are received and captured */
178 static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev,
179 				unsigned usage_id,
180 				void *priv)
181 {
182 	struct iio_dev *indio_dev = platform_get_drvdata(priv);
183 	struct dev_rot_state *rot_state = iio_priv(indio_dev);
184 
185 	dev_dbg(&indio_dev->dev, "dev_rot_proc_event\n");
186 	if (atomic_read(&rot_state->common_attributes.data_ready)) {
187 		if (!rot_state->timestamp)
188 			rot_state->timestamp = iio_get_time_ns(indio_dev);
189 
190 		/*
191 		 * ABI regression avoidance: IIO previously had an incorrect
192 		 * implementation of iio_push_to_buffers_with_timestamp() that
193 		 * put the timestamp in the last 8 bytes of the buffer, which
194 		 * was incorrect according to the IIO ABI. To avoid breaking
195 		 * userspace that may be depending on this broken behavior, we
196 		 * put the timestamp in both the correct place [0] and the old
197 		 * incorrect place [1].
198 		 */
199 		rot_state->scan.timestamp[0] = rot_state->timestamp;
200 		rot_state->scan.timestamp[1] = rot_state->timestamp;
201 
202 		iio_push_to_buffers(indio_dev, &rot_state->scan);
203 
204 		rot_state->timestamp = 0;
205 	}
206 
207 	return 0;
208 }
209 
210 /* Capture samples in local storage */
211 static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev,
212 				unsigned usage_id,
213 				size_t raw_len, char *raw_data,
214 				void *priv)
215 {
216 	struct iio_dev *indio_dev = platform_get_drvdata(priv);
217 	struct dev_rot_state *rot_state = iio_priv(indio_dev);
218 
219 	if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) {
220 		if (raw_len / 4 == sizeof(s16)) {
221 			rot_state->scan.sampled_vals[0] = ((s16 *)raw_data)[0];
222 			rot_state->scan.sampled_vals[1] = ((s16 *)raw_data)[1];
223 			rot_state->scan.sampled_vals[2] = ((s16 *)raw_data)[2];
224 			rot_state->scan.sampled_vals[3] = ((s16 *)raw_data)[3];
225 		} else {
226 			memcpy(&rot_state->scan.sampled_vals, raw_data,
227 			       sizeof(rot_state->scan.sampled_vals));
228 		}
229 
230 		dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len,
231 			sizeof(rot_state->scan.sampled_vals));
232 	} else if (usage_id == HID_USAGE_SENSOR_TIME_TIMESTAMP) {
233 		rot_state->timestamp = hid_sensor_convert_timestamp(&rot_state->common_attributes,
234 								    *(s64 *)raw_data);
235 	}
236 
237 	return 0;
238 }
239 
240 /* Parse report which is specific to an usage id*/
241 static int dev_rot_parse_report(struct platform_device *pdev,
242 				struct hid_sensor_hub_device *hsdev,
243 				unsigned usage_id,
244 				struct dev_rot_state *st)
245 {
246 	int ret;
247 
248 	ret = sensor_hub_input_get_attribute_info(hsdev,
249 				HID_INPUT_REPORT,
250 				usage_id,
251 				HID_USAGE_SENSOR_ORIENT_QUATERNION,
252 				&st->quaternion);
253 	if (ret)
254 		return ret;
255 
256 	dev_dbg(&pdev->dev, "dev_rot %x:%x\n", st->quaternion.index,
257 		st->quaternion.report_id);
258 
259 	dev_dbg(&pdev->dev, "dev_rot: attrib size %d\n",
260 				st->quaternion.size);
261 
262 	st->scale_precision = hid_sensor_format_scale(
263 				hsdev->usage,
264 				&st->quaternion,
265 				&st->scale_pre_decml, &st->scale_post_decml);
266 
267 	return 0;
268 }
269 
270 /* Function to initialize the processing for usage id */
271 static int hid_dev_rot_probe(struct platform_device *pdev)
272 {
273 	struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
274 	int ret;
275 	char *name;
276 	struct iio_dev *indio_dev;
277 	struct dev_rot_state *rot_state;
278 
279 	indio_dev = devm_iio_device_alloc(&pdev->dev,
280 					  sizeof(struct dev_rot_state));
281 	if (indio_dev == NULL)
282 		return -ENOMEM;
283 
284 	platform_set_drvdata(pdev, indio_dev);
285 
286 	rot_state = iio_priv(indio_dev);
287 	rot_state->common_attributes.hsdev = hsdev;
288 	rot_state->common_attributes.pdev = pdev;
289 
290 	switch (hsdev->usage) {
291 	case HID_USAGE_SENSOR_DEVICE_ORIENTATION:
292 		name = "dev_rotation";
293 		break;
294 	case HID_USAGE_SENSOR_RELATIVE_ORIENTATION:
295 		name = "relative_orientation";
296 		break;
297 	case HID_USAGE_SENSOR_GEOMAGNETIC_ORIENTATION:
298 		name = "geomagnetic_orientation";
299 		break;
300 	default:
301 		return -EINVAL;
302 	}
303 
304 	ret = hid_sensor_parse_common_attributes(hsdev,
305 						 hsdev->usage,
306 						 &rot_state->common_attributes,
307 						 rotation_sensitivity_addresses,
308 						 ARRAY_SIZE(rotation_sensitivity_addresses));
309 	if (ret) {
310 		dev_err(&pdev->dev, "failed to setup common attributes\n");
311 		return ret;
312 	}
313 
314 	ret = dev_rot_parse_report(pdev, hsdev, hsdev->usage, rot_state);
315 	if (ret) {
316 		dev_err(&pdev->dev, "failed to setup attributes\n");
317 		return ret;
318 	}
319 
320 	indio_dev->channels = dev_rot_channels;
321 	indio_dev->num_channels = ARRAY_SIZE(dev_rot_channels);
322 	indio_dev->info = &dev_rot_info;
323 	indio_dev->name = name;
324 	indio_dev->modes = INDIO_DIRECT_MODE;
325 
326 	atomic_set(&rot_state->common_attributes.data_ready, 0);
327 
328 	ret = hid_sensor_setup_trigger(indio_dev, name,
329 					&rot_state->common_attributes);
330 	if (ret) {
331 		dev_err(&pdev->dev, "trigger setup failed\n");
332 		return ret;
333 	}
334 
335 	ret = iio_device_register(indio_dev);
336 	if (ret) {
337 		dev_err(&pdev->dev, "device register failed\n");
338 		goto error_remove_trigger;
339 	}
340 
341 	rot_state->callbacks.send_event = dev_rot_proc_event;
342 	rot_state->callbacks.capture_sample = dev_rot_capture_sample;
343 	rot_state->callbacks.pdev = pdev;
344 	ret = sensor_hub_register_callback(hsdev, hsdev->usage,
345 					&rot_state->callbacks);
346 	if (ret) {
347 		dev_err(&pdev->dev, "callback reg failed\n");
348 		goto error_iio_unreg;
349 	}
350 
351 	return 0;
352 
353 error_iio_unreg:
354 	iio_device_unregister(indio_dev);
355 error_remove_trigger:
356 	hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
357 	return ret;
358 }
359 
360 /* Function to deinitialize the processing for usage id */
361 static void hid_dev_rot_remove(struct platform_device *pdev)
362 {
363 	struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
364 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
365 	struct dev_rot_state *rot_state = iio_priv(indio_dev);
366 
367 	sensor_hub_remove_callback(hsdev, hsdev->usage);
368 	iio_device_unregister(indio_dev);
369 	hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
370 }
371 
372 static const struct platform_device_id hid_dev_rot_ids[] = {
373 	{
374 		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
375 		.name = "HID-SENSOR-20008a",
376 	},
377 	{
378 		/* Relative orientation(AG) sensor */
379 		.name = "HID-SENSOR-20008e",
380 	},
381 	{
382 		/* Geomagnetic orientation(AM) sensor */
383 		.name = "HID-SENSOR-2000c1",
384 	},
385 	{ }
386 };
387 MODULE_DEVICE_TABLE(platform, hid_dev_rot_ids);
388 
389 static struct platform_driver hid_dev_rot_platform_driver = {
390 	.id_table = hid_dev_rot_ids,
391 	.driver = {
392 		.name	= KBUILD_MODNAME,
393 		.pm     = &hid_sensor_pm_ops,
394 	},
395 	.probe		= hid_dev_rot_probe,
396 	.remove		= hid_dev_rot_remove,
397 };
398 module_platform_driver(hid_dev_rot_platform_driver);
399 
400 MODULE_DESCRIPTION("HID Sensor Device Rotation");
401 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
402 MODULE_LICENSE("GPL");
403 MODULE_IMPORT_NS("IIO_HID");
404