hid-sensor-accel-3d.c 13.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * HID Sensors Driver
 * Copyright (c) 2012, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
25
#include <linux/delay.h>
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
#include <linux/hid-sensor-hub.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include "../common/hid-sensors/hid-sensor-trigger.h"

enum accel_3d_channel {
	CHANNEL_SCAN_INDEX_X,
	CHANNEL_SCAN_INDEX_Y,
	CHANNEL_SCAN_INDEX_Z,
	ACCEL_3D_CHANNEL_MAX,
};

struct accel_3d_state {
	struct hid_sensor_hub_callbacks callbacks;
43
	struct hid_sensor_common common_attributes;
44
	struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
45 46
	/* Reserve for 3 channels + padding + timestamp */
	u32 accel_val[ACCEL_3D_CHANNEL_MAX + 3];
47 48 49 50
	int scale_pre_decml;
	int scale_post_decml;
	int scale_precision;
	int value_offset;
51
	int64_t timestamp;
52 53 54 55 56 57 58 59 60 61 62 63 64 65
};

static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
	HID_USAGE_SENSOR_ACCEL_X_AXIS,
	HID_USAGE_SENSOR_ACCEL_Y_AXIS,
	HID_USAGE_SENSOR_ACCEL_Z_AXIS
};

/* Channel definitions */
static const struct iio_chan_spec accel_3d_channels[] = {
	{
		.type = IIO_ACCEL,
		.modified = 1,
		.channel2 = IIO_MOD_X,
66
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
67 68 69 70
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
		BIT(IIO_CHAN_INFO_SCALE) |
		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
		BIT(IIO_CHAN_INFO_HYSTERESIS),
71 72 73 74 75
		.scan_index = CHANNEL_SCAN_INDEX_X,
	}, {
		.type = IIO_ACCEL,
		.modified = 1,
		.channel2 = IIO_MOD_Y,
76
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
77 78 79 80
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
		BIT(IIO_CHAN_INFO_SCALE) |
		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
		BIT(IIO_CHAN_INFO_HYSTERESIS),
81 82 83 84 85
		.scan_index = CHANNEL_SCAN_INDEX_Y,
	}, {
		.type = IIO_ACCEL,
		.modified = 1,
		.channel2 = IIO_MOD_Z,
86
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
87 88 89 90
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
		BIT(IIO_CHAN_INFO_SCALE) |
		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
		BIT(IIO_CHAN_INFO_HYSTERESIS),
91
		.scan_index = CHANNEL_SCAN_INDEX_Z,
92 93
	},
	IIO_CHAN_SOFT_TIMESTAMP(3)
94 95
};

S
Song Hongyan 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
/* Channel definitions */
static const struct iio_chan_spec gravity_channels[] = {
	{
		.type = IIO_GRAVITY,
		.modified = 1,
		.channel2 = IIO_MOD_X,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
		BIT(IIO_CHAN_INFO_SCALE) |
		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
		BIT(IIO_CHAN_INFO_HYSTERESIS),
		.scan_index = CHANNEL_SCAN_INDEX_X,
	}, {
		.type = IIO_GRAVITY,
		.modified = 1,
		.channel2 = IIO_MOD_Y,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
		BIT(IIO_CHAN_INFO_SCALE) |
		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
		BIT(IIO_CHAN_INFO_HYSTERESIS),
		.scan_index = CHANNEL_SCAN_INDEX_Y,
	}, {
		.type = IIO_GRAVITY,
		.modified = 1,
		.channel2 = IIO_MOD_Z,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
		BIT(IIO_CHAN_INFO_SCALE) |
		BIT(IIO_CHAN_INFO_SAMP_FREQ) |
		BIT(IIO_CHAN_INFO_HYSTERESIS),
		.scan_index = CHANNEL_SCAN_INDEX_Z,
	}
};

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
/* Adjust channel real bits based on report descriptor */
static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
						int channel, int size)
{
	channels[channel].scan_type.sign = 's';
	/* Real storage bits will change based on the report desc. */
	channels[channel].scan_type.realbits = size * 8;
	/* Maximum size of a sample to capture is u32 */
	channels[channel].scan_type.storagebits = sizeof(u32) * 8;
}

/* Channel read_raw handler */
static int accel_3d_read_raw(struct iio_dev *indio_dev,
			      struct iio_chan_spec const *chan,
			      int *val, int *val2,
			      long mask)
{
	struct accel_3d_state *accel_state = iio_priv(indio_dev);
	int report_id = -1;
	u32 address;
	int ret_type;
152
	s32 min;
S
Song Hongyan 已提交
153 154
	struct hid_sensor_hub_device *hsdev =
					accel_state->common_attributes.hsdev;
155 156 157 158

	*val = 0;
	*val2 = 0;
	switch (mask) {
159
	case IIO_CHAN_INFO_RAW:
160
		hid_sensor_power_state(&accel_state->common_attributes, true);
161
		report_id = accel_state->accel[chan->scan_index].report_id;
162
		min = accel_state->accel[chan->scan_index].logical_minimum;
163 164 165
		address = accel_3d_addresses[chan->scan_index];
		if (report_id >= 0)
			*val = sensor_hub_input_attr_get_raw_value(
166
					accel_state->common_attributes.hsdev,
S
Song Hongyan 已提交
167
					hsdev->usage, address, report_id,
168 169
					SENSOR_HUB_SYNC,
					min < 0);
170 171
		else {
			*val = 0;
172 173
			hid_sensor_power_state(&accel_state->common_attributes,
						 false);
174 175
			return -EINVAL;
		}
176
		hid_sensor_power_state(&accel_state->common_attributes, false);
177 178 179
		ret_type = IIO_VAL_INT;
		break;
	case IIO_CHAN_INFO_SCALE:
180 181 182
		*val = accel_state->scale_pre_decml;
		*val2 = accel_state->scale_post_decml;
		ret_type = accel_state->scale_precision;
183 184
		break;
	case IIO_CHAN_INFO_OFFSET:
185
		*val = accel_state->value_offset;
186 187 188
		ret_type = IIO_VAL_INT;
		break;
	case IIO_CHAN_INFO_SAMP_FREQ:
189
		ret_type = hid_sensor_read_samp_freq_value(
190 191 192
			&accel_state->common_attributes, val, val2);
		break;
	case IIO_CHAN_INFO_HYSTERESIS:
193
		ret_type = hid_sensor_read_raw_hyst_value(
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
			&accel_state->common_attributes, val, val2);
		break;
	default:
		ret_type = -EINVAL;
		break;
	}

	return ret_type;
}

/* Channel write_raw handler */
static int accel_3d_write_raw(struct iio_dev *indio_dev,
			       struct iio_chan_spec const *chan,
			       int val,
			       int val2,
			       long mask)
{
	struct accel_3d_state *accel_state = iio_priv(indio_dev);
	int ret = 0;

	switch (mask) {
	case IIO_CHAN_INFO_SAMP_FREQ:
		ret = hid_sensor_write_samp_freq_value(
				&accel_state->common_attributes, val, val2);
		break;
	case IIO_CHAN_INFO_HYSTERESIS:
		ret = hid_sensor_write_raw_hyst_value(
				&accel_state->common_attributes, val, val2);
		break;
	default:
		ret = -EINVAL;
	}

	return ret;
}

static const struct iio_info accel_3d_info = {
	.read_raw = &accel_3d_read_raw,
	.write_raw = &accel_3d_write_raw,
};

/* Function to push data to buffer */
236 237
static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data,
				 int len, int64_t timestamp)
238 239
{
	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
240
	iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
241 242 243 244 245 246 247 248 249 250
}

/* Callback handler to send event after all samples are received and captured */
static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
				unsigned usage_id,
				void *priv)
{
	struct iio_dev *indio_dev = platform_get_drvdata(priv);
	struct accel_3d_state *accel_state = iio_priv(indio_dev);

251
	dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n");
252 253 254 255
	if (atomic_read(&accel_state->common_attributes.data_ready)) {
		if (!accel_state->timestamp)
			accel_state->timestamp = iio_get_time_ns(indio_dev);

256
		hid_sensor_push_data(indio_dev,
257 258 259 260 261 262
				     accel_state->accel_val,
				     sizeof(accel_state->accel_val),
				     accel_state->timestamp);

		accel_state->timestamp = 0;
	}
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286

	return 0;
}

/* Capture samples in local storage */
static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
				unsigned usage_id,
				size_t raw_len, char *raw_data,
				void *priv)
{
	struct iio_dev *indio_dev = platform_get_drvdata(priv);
	struct accel_3d_state *accel_state = iio_priv(indio_dev);
	int offset;
	int ret = -EINVAL;

	switch (usage_id) {
	case HID_USAGE_SENSOR_ACCEL_X_AXIS:
	case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
	case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
		offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
		accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] =
						*(u32 *)raw_data;
		ret = 0;
	break;
287 288 289 290 291 292
	case HID_USAGE_SENSOR_TIME_TIMESTAMP:
		accel_state->timestamp =
			hid_sensor_convert_timestamp(
					&accel_state->common_attributes,
					*(int64_t *)raw_data);
	break;
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
	default:
		break;
	}

	return ret;
}

/* Parse report which is specific to an usage id*/
static int accel_3d_parse_report(struct platform_device *pdev,
				struct hid_sensor_hub_device *hsdev,
				struct iio_chan_spec *channels,
				unsigned usage_id,
				struct accel_3d_state *st)
{
	int ret;
	int i;

	for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
		ret = sensor_hub_input_get_attribute_info(hsdev,
				HID_INPUT_REPORT,
				usage_id,
				HID_USAGE_SENSOR_ACCEL_X_AXIS + i,
				&st->accel[CHANNEL_SCAN_INDEX_X + i]);
		if (ret < 0)
			break;
		accel_3d_adjust_channel_bit_mask(channels,
				CHANNEL_SCAN_INDEX_X + i,
				st->accel[CHANNEL_SCAN_INDEX_X + i].size);
	}
	dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
			st->accel[0].index,
			st->accel[0].report_id,
			st->accel[1].index, st->accel[1].report_id,
			st->accel[2].index, st->accel[2].report_id);

328
	st->scale_precision = hid_sensor_format_scale(
S
Song Hongyan 已提交
329
				hsdev->usage,
330 331 332
				&st->accel[CHANNEL_SCAN_INDEX_X],
				&st->scale_pre_decml, &st->scale_post_decml);

333 334 335 336 337 338 339 340 341 342 343 344
	/* Set Sensitivity field ids, when there is no individual modifier */
	if (st->common_attributes.sensitivity.index < 0) {
		sensor_hub_input_get_attribute_info(hsdev,
			HID_FEATURE_REPORT, usage_id,
			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
			HID_USAGE_SENSOR_DATA_ACCELERATION,
			&st->common_attributes.sensitivity);
		dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
			st->common_attributes.sensitivity.index,
			st->common_attributes.sensitivity.report_id);
	}

345 346 347 348
	return ret;
}

/* Function to initialize the processing for usage id */
349
static int hid_accel_3d_probe(struct platform_device *pdev)
350 351
{
	int ret = 0;
352
	const char *name;
353 354
	struct iio_dev *indio_dev;
	struct accel_3d_state *accel_state;
S
Song Hongyan 已提交
355 356 357
	const struct iio_chan_spec *channel_spec;
	int channel_size;

358 359
	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;

360 361 362 363 364
	indio_dev = devm_iio_device_alloc(&pdev->dev,
					  sizeof(struct accel_3d_state));
	if (indio_dev == NULL)
		return -ENOMEM;

365 366 367 368 369 370
	platform_set_drvdata(pdev, indio_dev);

	accel_state = iio_priv(indio_dev);
	accel_state->common_attributes.hsdev = hsdev;
	accel_state->common_attributes.pdev = pdev;

S
Song Hongyan 已提交
371 372 373 374
	if (hsdev->usage == HID_USAGE_SENSOR_ACCEL_3D) {
		name = "accel_3d";
		channel_spec = accel_3d_channels;
		channel_size = sizeof(accel_3d_channels);
375
		indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
S
Song Hongyan 已提交
376 377 378 379
	} else {
		name = "gravity";
		channel_spec = gravity_channels;
		channel_size = sizeof(gravity_channels);
380
		indio_dev->num_channels = ARRAY_SIZE(gravity_channels);
S
Song Hongyan 已提交
381 382
	}
	ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
383 384 385
					&accel_state->common_attributes);
	if (ret) {
		dev_err(&pdev->dev, "failed to setup common attributes\n");
386
		return ret;
387
	}
S
Song Hongyan 已提交
388
	indio_dev->channels = kmemdup(channel_spec, channel_size, GFP_KERNEL);
389

390
	if (!indio_dev->channels) {
391
		dev_err(&pdev->dev, "failed to duplicate channels\n");
392
		return -ENOMEM;
393
	}
394
	ret = accel_3d_parse_report(pdev, hsdev,
S
Song Hongyan 已提交
395 396
				(struct iio_chan_spec *)indio_dev->channels,
				hsdev->usage, accel_state);
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
	if (ret) {
		dev_err(&pdev->dev, "failed to setup attributes\n");
		goto error_free_dev_mem;
	}

	indio_dev->dev.parent = &pdev->dev;
	indio_dev->info = &accel_3d_info;
	indio_dev->name = name;
	indio_dev->modes = INDIO_DIRECT_MODE;

	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
		NULL, NULL);
	if (ret) {
		dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
		goto error_free_dev_mem;
	}
413
	atomic_set(&accel_state->common_attributes.data_ready, 0);
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
	ret = hid_sensor_setup_trigger(indio_dev, name,
					&accel_state->common_attributes);
	if (ret < 0) {
		dev_err(&pdev->dev, "trigger setup failed\n");
		goto error_unreg_buffer_funcs;
	}

	ret = iio_device_register(indio_dev);
	if (ret) {
		dev_err(&pdev->dev, "device register failed\n");
		goto error_remove_trigger;
	}

	accel_state->callbacks.send_event = accel_3d_proc_event;
	accel_state->callbacks.capture_sample = accel_3d_capture_sample;
	accel_state->callbacks.pdev = pdev;
S
Song Hongyan 已提交
430
	ret = sensor_hub_register_callback(hsdev, hsdev->usage,
431 432 433 434 435 436 437 438 439 440 441
					&accel_state->callbacks);
	if (ret < 0) {
		dev_err(&pdev->dev, "callback reg failed\n");
		goto error_iio_unreg;
	}

	return ret;

error_iio_unreg:
	iio_device_unregister(indio_dev);
error_remove_trigger:
442
	hid_sensor_remove_trigger(&accel_state->common_attributes);
443 444 445 446 447 448 449 450
error_unreg_buffer_funcs:
	iio_triggered_buffer_cleanup(indio_dev);
error_free_dev_mem:
	kfree(indio_dev->channels);
	return ret;
}

/* Function to deinitialize the processing for usage id */
451
static int hid_accel_3d_remove(struct platform_device *pdev)
452 453 454
{
	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
455
	struct accel_3d_state *accel_state = iio_priv(indio_dev);
456

S
Song Hongyan 已提交
457
	sensor_hub_remove_callback(hsdev, hsdev->usage);
458
	iio_device_unregister(indio_dev);
459
	hid_sensor_remove_trigger(&accel_state->common_attributes);
460 461 462 463 464 465
	iio_triggered_buffer_cleanup(indio_dev);
	kfree(indio_dev->channels);

	return 0;
}

466
static const struct platform_device_id hid_accel_3d_ids[] = {
467 468 469 470
	{
		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
		.name = "HID-SENSOR-200073",
	},
S
Song Hongyan 已提交
471 472 473
	{	/* gravity sensor */
		.name = "HID-SENSOR-20007b",
	},
474 475 476 477
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids);

478
static struct platform_driver hid_accel_3d_platform_driver = {
479
	.id_table = hid_accel_3d_ids,
480
	.driver = {
481
		.name	= KBUILD_MODNAME,
482
		.pm	= &hid_sensor_pm_ops,
483 484 485 486 487 488 489 490 491
	},
	.probe		= hid_accel_3d_probe,
	.remove		= hid_accel_3d_remove,
};
module_platform_driver(hid_accel_3d_platform_driver);

MODULE_DESCRIPTION("HID Sensor Accel 3D");
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
MODULE_LICENSE("GPL");