0001
0002
0003
0004
0005
0006 #include <linux/device.h>
0007 #include <linux/platform_device.h>
0008 #include <linux/module.h>
0009 #include <linux/mod_devicetable.h>
0010 #include <linux/slab.h>
0011 #include <linux/hid-sensor-hub.h>
0012 #include <linux/iio/iio.h>
0013 #include <linux/iio/buffer.h>
0014 #include "../common/hid-sensors/hid-sensor-trigger.h"
0015
0016 enum accel_3d_channel {
0017 CHANNEL_SCAN_INDEX_X,
0018 CHANNEL_SCAN_INDEX_Y,
0019 CHANNEL_SCAN_INDEX_Z,
0020 ACCEL_3D_CHANNEL_MAX,
0021 };
0022
0023 #define CHANNEL_SCAN_INDEX_TIMESTAMP ACCEL_3D_CHANNEL_MAX
0024 struct accel_3d_state {
0025 struct hid_sensor_hub_callbacks callbacks;
0026 struct hid_sensor_common common_attributes;
0027 struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
0028
0029 struct {
0030 u32 accel_val[3];
0031 s64 timestamp __aligned(8);
0032 } scan;
0033 int scale_pre_decml;
0034 int scale_post_decml;
0035 int scale_precision;
0036 int value_offset;
0037 int64_t timestamp;
0038 };
0039
0040 static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
0041 HID_USAGE_SENSOR_ACCEL_X_AXIS,
0042 HID_USAGE_SENSOR_ACCEL_Y_AXIS,
0043 HID_USAGE_SENSOR_ACCEL_Z_AXIS
0044 };
0045
0046 static const u32 accel_3d_sensitivity_addresses[] = {
0047 HID_USAGE_SENSOR_DATA_ACCELERATION,
0048 };
0049
0050
0051 static const struct iio_chan_spec accel_3d_channels[] = {
0052 {
0053 .type = IIO_ACCEL,
0054 .modified = 1,
0055 .channel2 = IIO_MOD_X,
0056 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0057 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
0058 BIT(IIO_CHAN_INFO_SCALE) |
0059 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
0060 BIT(IIO_CHAN_INFO_HYSTERESIS),
0061 .scan_index = CHANNEL_SCAN_INDEX_X,
0062 }, {
0063 .type = IIO_ACCEL,
0064 .modified = 1,
0065 .channel2 = IIO_MOD_Y,
0066 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0067 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
0068 BIT(IIO_CHAN_INFO_SCALE) |
0069 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
0070 BIT(IIO_CHAN_INFO_HYSTERESIS),
0071 .scan_index = CHANNEL_SCAN_INDEX_Y,
0072 }, {
0073 .type = IIO_ACCEL,
0074 .modified = 1,
0075 .channel2 = IIO_MOD_Z,
0076 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0077 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
0078 BIT(IIO_CHAN_INFO_SCALE) |
0079 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
0080 BIT(IIO_CHAN_INFO_HYSTERESIS),
0081 .scan_index = CHANNEL_SCAN_INDEX_Z,
0082 },
0083 IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
0084 };
0085
0086
0087 static const struct iio_chan_spec gravity_channels[] = {
0088 {
0089 .type = IIO_GRAVITY,
0090 .modified = 1,
0091 .channel2 = IIO_MOD_X,
0092 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0093 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
0094 BIT(IIO_CHAN_INFO_SCALE) |
0095 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
0096 BIT(IIO_CHAN_INFO_HYSTERESIS),
0097 .scan_index = CHANNEL_SCAN_INDEX_X,
0098 }, {
0099 .type = IIO_GRAVITY,
0100 .modified = 1,
0101 .channel2 = IIO_MOD_Y,
0102 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0103 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
0104 BIT(IIO_CHAN_INFO_SCALE) |
0105 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
0106 BIT(IIO_CHAN_INFO_HYSTERESIS),
0107 .scan_index = CHANNEL_SCAN_INDEX_Y,
0108 }, {
0109 .type = IIO_GRAVITY,
0110 .modified = 1,
0111 .channel2 = IIO_MOD_Z,
0112 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
0113 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
0114 BIT(IIO_CHAN_INFO_SCALE) |
0115 BIT(IIO_CHAN_INFO_SAMP_FREQ) |
0116 BIT(IIO_CHAN_INFO_HYSTERESIS),
0117 .scan_index = CHANNEL_SCAN_INDEX_Z,
0118 },
0119 IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP),
0120 };
0121
0122
0123 static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
0124 int channel, int size)
0125 {
0126 channels[channel].scan_type.sign = 's';
0127
0128 channels[channel].scan_type.realbits = size * 8;
0129
0130 channels[channel].scan_type.storagebits = sizeof(u32) * 8;
0131 }
0132
0133
0134 static int accel_3d_read_raw(struct iio_dev *indio_dev,
0135 struct iio_chan_spec const *chan,
0136 int *val, int *val2,
0137 long mask)
0138 {
0139 struct accel_3d_state *accel_state = iio_priv(indio_dev);
0140 int report_id = -1;
0141 u32 address;
0142 int ret_type;
0143 s32 min;
0144 struct hid_sensor_hub_device *hsdev =
0145 accel_state->common_attributes.hsdev;
0146
0147 *val = 0;
0148 *val2 = 0;
0149 switch (mask) {
0150 case IIO_CHAN_INFO_RAW:
0151 hid_sensor_power_state(&accel_state->common_attributes, true);
0152 report_id = accel_state->accel[chan->scan_index].report_id;
0153 min = accel_state->accel[chan->scan_index].logical_minimum;
0154 address = accel_3d_addresses[chan->scan_index];
0155 if (report_id >= 0)
0156 *val = sensor_hub_input_attr_get_raw_value(
0157 accel_state->common_attributes.hsdev,
0158 hsdev->usage, address, report_id,
0159 SENSOR_HUB_SYNC,
0160 min < 0);
0161 else {
0162 *val = 0;
0163 hid_sensor_power_state(&accel_state->common_attributes,
0164 false);
0165 return -EINVAL;
0166 }
0167 hid_sensor_power_state(&accel_state->common_attributes, false);
0168 ret_type = IIO_VAL_INT;
0169 break;
0170 case IIO_CHAN_INFO_SCALE:
0171 *val = accel_state->scale_pre_decml;
0172 *val2 = accel_state->scale_post_decml;
0173 ret_type = accel_state->scale_precision;
0174 break;
0175 case IIO_CHAN_INFO_OFFSET:
0176 *val = accel_state->value_offset;
0177 ret_type = IIO_VAL_INT;
0178 break;
0179 case IIO_CHAN_INFO_SAMP_FREQ:
0180 ret_type = hid_sensor_read_samp_freq_value(
0181 &accel_state->common_attributes, val, val2);
0182 break;
0183 case IIO_CHAN_INFO_HYSTERESIS:
0184 ret_type = hid_sensor_read_raw_hyst_value(
0185 &accel_state->common_attributes, val, val2);
0186 break;
0187 default:
0188 ret_type = -EINVAL;
0189 break;
0190 }
0191
0192 return ret_type;
0193 }
0194
0195
0196 static int accel_3d_write_raw(struct iio_dev *indio_dev,
0197 struct iio_chan_spec const *chan,
0198 int val,
0199 int val2,
0200 long mask)
0201 {
0202 struct accel_3d_state *accel_state = iio_priv(indio_dev);
0203 int ret = 0;
0204
0205 switch (mask) {
0206 case IIO_CHAN_INFO_SAMP_FREQ:
0207 ret = hid_sensor_write_samp_freq_value(
0208 &accel_state->common_attributes, val, val2);
0209 break;
0210 case IIO_CHAN_INFO_HYSTERESIS:
0211 ret = hid_sensor_write_raw_hyst_value(
0212 &accel_state->common_attributes, val, val2);
0213 break;
0214 default:
0215 ret = -EINVAL;
0216 }
0217
0218 return ret;
0219 }
0220
0221 static const struct iio_info accel_3d_info = {
0222 .read_raw = &accel_3d_read_raw,
0223 .write_raw = &accel_3d_write_raw,
0224 };
0225
0226
0227 static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data,
0228 int len, int64_t timestamp)
0229 {
0230 dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
0231 iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
0232 }
0233
0234
0235 static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
0236 unsigned usage_id,
0237 void *priv)
0238 {
0239 struct iio_dev *indio_dev = platform_get_drvdata(priv);
0240 struct accel_3d_state *accel_state = iio_priv(indio_dev);
0241
0242 dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n");
0243 if (atomic_read(&accel_state->common_attributes.data_ready)) {
0244 if (!accel_state->timestamp)
0245 accel_state->timestamp = iio_get_time_ns(indio_dev);
0246
0247 hid_sensor_push_data(indio_dev,
0248 &accel_state->scan,
0249 sizeof(accel_state->scan),
0250 accel_state->timestamp);
0251
0252 accel_state->timestamp = 0;
0253 }
0254
0255 return 0;
0256 }
0257
0258
0259 static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
0260 unsigned usage_id,
0261 size_t raw_len, char *raw_data,
0262 void *priv)
0263 {
0264 struct iio_dev *indio_dev = platform_get_drvdata(priv);
0265 struct accel_3d_state *accel_state = iio_priv(indio_dev);
0266 int offset;
0267 int ret = -EINVAL;
0268
0269 switch (usage_id) {
0270 case HID_USAGE_SENSOR_ACCEL_X_AXIS:
0271 case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
0272 case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
0273 offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
0274 accel_state->scan.accel_val[CHANNEL_SCAN_INDEX_X + offset] =
0275 *(u32 *)raw_data;
0276 ret = 0;
0277 break;
0278 case HID_USAGE_SENSOR_TIME_TIMESTAMP:
0279 accel_state->timestamp =
0280 hid_sensor_convert_timestamp(
0281 &accel_state->common_attributes,
0282 *(int64_t *)raw_data);
0283 break;
0284 default:
0285 break;
0286 }
0287
0288 return ret;
0289 }
0290
0291
0292 static int accel_3d_parse_report(struct platform_device *pdev,
0293 struct hid_sensor_hub_device *hsdev,
0294 struct iio_chan_spec *channels,
0295 unsigned usage_id,
0296 struct accel_3d_state *st)
0297 {
0298 int ret;
0299 int i;
0300
0301 for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
0302 ret = sensor_hub_input_get_attribute_info(hsdev,
0303 HID_INPUT_REPORT,
0304 usage_id,
0305 HID_USAGE_SENSOR_ACCEL_X_AXIS + i,
0306 &st->accel[CHANNEL_SCAN_INDEX_X + i]);
0307 if (ret < 0)
0308 break;
0309 accel_3d_adjust_channel_bit_mask(channels,
0310 CHANNEL_SCAN_INDEX_X + i,
0311 st->accel[CHANNEL_SCAN_INDEX_X + i].size);
0312 }
0313 dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
0314 st->accel[0].index,
0315 st->accel[0].report_id,
0316 st->accel[1].index, st->accel[1].report_id,
0317 st->accel[2].index, st->accel[2].report_id);
0318
0319 st->scale_precision = hid_sensor_format_scale(
0320 hsdev->usage,
0321 &st->accel[CHANNEL_SCAN_INDEX_X],
0322 &st->scale_pre_decml, &st->scale_post_decml);
0323
0324 return ret;
0325 }
0326
0327
0328 static int hid_accel_3d_probe(struct platform_device *pdev)
0329 {
0330 int ret = 0;
0331 const char *name;
0332 struct iio_dev *indio_dev;
0333 struct accel_3d_state *accel_state;
0334 const struct iio_chan_spec *channel_spec;
0335 int channel_size;
0336
0337 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
0338
0339 indio_dev = devm_iio_device_alloc(&pdev->dev,
0340 sizeof(struct accel_3d_state));
0341 if (indio_dev == NULL)
0342 return -ENOMEM;
0343
0344 platform_set_drvdata(pdev, indio_dev);
0345
0346 accel_state = iio_priv(indio_dev);
0347 accel_state->common_attributes.hsdev = hsdev;
0348 accel_state->common_attributes.pdev = pdev;
0349
0350 if (hsdev->usage == HID_USAGE_SENSOR_ACCEL_3D) {
0351 name = "accel_3d";
0352 channel_spec = accel_3d_channels;
0353 channel_size = sizeof(accel_3d_channels);
0354 indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
0355 } else {
0356 name = "gravity";
0357 channel_spec = gravity_channels;
0358 channel_size = sizeof(gravity_channels);
0359 indio_dev->num_channels = ARRAY_SIZE(gravity_channels);
0360 }
0361 ret = hid_sensor_parse_common_attributes(hsdev,
0362 hsdev->usage,
0363 &accel_state->common_attributes,
0364 accel_3d_sensitivity_addresses,
0365 ARRAY_SIZE(accel_3d_sensitivity_addresses));
0366 if (ret) {
0367 dev_err(&pdev->dev, "failed to setup common attributes\n");
0368 return ret;
0369 }
0370 indio_dev->channels = devm_kmemdup(&pdev->dev, channel_spec,
0371 channel_size, GFP_KERNEL);
0372
0373 if (!indio_dev->channels) {
0374 dev_err(&pdev->dev, "failed to duplicate channels\n");
0375 return -ENOMEM;
0376 }
0377 ret = accel_3d_parse_report(pdev, hsdev,
0378 (struct iio_chan_spec *)indio_dev->channels,
0379 hsdev->usage, accel_state);
0380 if (ret) {
0381 dev_err(&pdev->dev, "failed to setup attributes\n");
0382 return ret;
0383 }
0384
0385 indio_dev->info = &accel_3d_info;
0386 indio_dev->name = name;
0387 indio_dev->modes = INDIO_DIRECT_MODE;
0388
0389 atomic_set(&accel_state->common_attributes.data_ready, 0);
0390
0391 ret = hid_sensor_setup_trigger(indio_dev, name,
0392 &accel_state->common_attributes);
0393 if (ret < 0) {
0394 dev_err(&pdev->dev, "trigger setup failed\n");
0395 return ret;
0396 }
0397
0398 ret = iio_device_register(indio_dev);
0399 if (ret) {
0400 dev_err(&pdev->dev, "device register failed\n");
0401 goto error_remove_trigger;
0402 }
0403
0404 accel_state->callbacks.send_event = accel_3d_proc_event;
0405 accel_state->callbacks.capture_sample = accel_3d_capture_sample;
0406 accel_state->callbacks.pdev = pdev;
0407 ret = sensor_hub_register_callback(hsdev, hsdev->usage,
0408 &accel_state->callbacks);
0409 if (ret < 0) {
0410 dev_err(&pdev->dev, "callback reg failed\n");
0411 goto error_iio_unreg;
0412 }
0413
0414 return ret;
0415
0416 error_iio_unreg:
0417 iio_device_unregister(indio_dev);
0418 error_remove_trigger:
0419 hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
0420 return ret;
0421 }
0422
0423
0424 static int hid_accel_3d_remove(struct platform_device *pdev)
0425 {
0426 struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
0427 struct iio_dev *indio_dev = platform_get_drvdata(pdev);
0428 struct accel_3d_state *accel_state = iio_priv(indio_dev);
0429
0430 sensor_hub_remove_callback(hsdev, hsdev->usage);
0431 iio_device_unregister(indio_dev);
0432 hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
0433
0434 return 0;
0435 }
0436
0437 static const struct platform_device_id hid_accel_3d_ids[] = {
0438 {
0439
0440 .name = "HID-SENSOR-200073",
0441 },
0442 {
0443 .name = "HID-SENSOR-20007b",
0444 },
0445 { }
0446 };
0447 MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids);
0448
0449 static struct platform_driver hid_accel_3d_platform_driver = {
0450 .id_table = hid_accel_3d_ids,
0451 .driver = {
0452 .name = KBUILD_MODNAME,
0453 .pm = &hid_sensor_pm_ops,
0454 },
0455 .probe = hid_accel_3d_probe,
0456 .remove = hid_accel_3d_remove,
0457 };
0458 module_platform_driver(hid_accel_3d_platform_driver);
0459
0460 MODULE_DESCRIPTION("HID Sensor Accel 3D");
0461 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
0462 MODULE_LICENSE("GPL");
0463 MODULE_IMPORT_NS(IIO_HID);