linux/drivers/iio/temperature/hid-sensor-temperature.c
<<
>>
Prefs
   1/*
   2 * HID Sensors Driver
   3 * Copyright (c) 2017, 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.
  16 */
  17#include <linux/device.h>
  18#include <linux/hid-sensor-hub.h>
  19#include <linux/iio/buffer.h>
  20#include <linux/iio/iio.h>
  21#include <linux/iio/triggered_buffer.h>
  22#include <linux/iio/trigger_consumer.h>
  23#include <linux/module.h>
  24#include <linux/platform_device.h>
  25
  26#include "../common/hid-sensors/hid-sensor-trigger.h"
  27
  28struct temperature_state {
  29        struct hid_sensor_common common_attributes;
  30        struct hid_sensor_hub_attribute_info temperature_attr;
  31        s32 temperature_data;
  32        int scale_pre_decml;
  33        int scale_post_decml;
  34        int scale_precision;
  35        int value_offset;
  36};
  37
  38/* Channel definitions */
  39static const struct iio_chan_spec temperature_channels[] = {
  40        {
  41                .type = IIO_TEMP,
  42                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  43                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
  44                        BIT(IIO_CHAN_INFO_SCALE) |
  45                        BIT(IIO_CHAN_INFO_SAMP_FREQ) |
  46                        BIT(IIO_CHAN_INFO_HYSTERESIS),
  47        },
  48        IIO_CHAN_SOFT_TIMESTAMP(3),
  49};
  50
  51/* Adjust channel real bits based on report descriptor */
  52static void temperature_adjust_channel_bit_mask(struct iio_chan_spec *channels,
  53                                        int channel, int size)
  54{
  55        channels[channel].scan_type.sign = 's';
  56        /* Real storage bits will change based on the report desc. */
  57        channels[channel].scan_type.realbits = size * 8;
  58        /* Maximum size of a sample to capture is s32 */
  59        channels[channel].scan_type.storagebits = sizeof(s32) * 8;
  60}
  61
  62static int temperature_read_raw(struct iio_dev *indio_dev,
  63                                struct iio_chan_spec const *chan,
  64                                int *val, int *val2, long mask)
  65{
  66        struct temperature_state *temp_st = iio_priv(indio_dev);
  67
  68        switch (mask) {
  69        case IIO_CHAN_INFO_RAW:
  70                if (chan->type != IIO_TEMP)
  71                        return -EINVAL;
  72                hid_sensor_power_state(
  73                        &temp_st->common_attributes, true);
  74                *val = sensor_hub_input_attr_get_raw_value(
  75                        temp_st->common_attributes.hsdev,
  76                        HID_USAGE_SENSOR_TEMPERATURE,
  77                        HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE,
  78                        temp_st->temperature_attr.report_id,
  79                        SENSOR_HUB_SYNC,
  80                        temp_st->temperature_attr.logical_minimum < 0);
  81                hid_sensor_power_state(
  82                                &temp_st->common_attributes,
  83                                false);
  84
  85                return IIO_VAL_INT;
  86
  87        case IIO_CHAN_INFO_SCALE:
  88                *val = temp_st->scale_pre_decml;
  89                *val2 = temp_st->scale_post_decml;
  90                return temp_st->scale_precision;
  91
  92        case IIO_CHAN_INFO_OFFSET:
  93                *val = temp_st->value_offset;
  94                return IIO_VAL_INT;
  95
  96        case IIO_CHAN_INFO_SAMP_FREQ:
  97                return hid_sensor_read_samp_freq_value(
  98                                &temp_st->common_attributes, val, val2);
  99
 100        case IIO_CHAN_INFO_HYSTERESIS:
 101                return hid_sensor_read_raw_hyst_value(
 102                                &temp_st->common_attributes, val, val2);
 103        default:
 104                return -EINVAL;
 105        }
 106}
 107
 108static int temperature_write_raw(struct iio_dev *indio_dev,
 109                                struct iio_chan_spec const *chan,
 110                                int val, int val2, long mask)
 111{
 112        struct temperature_state *temp_st = iio_priv(indio_dev);
 113
 114        switch (mask) {
 115        case IIO_CHAN_INFO_SAMP_FREQ:
 116                return hid_sensor_write_samp_freq_value(
 117                                &temp_st->common_attributes, val, val2);
 118        case IIO_CHAN_INFO_HYSTERESIS:
 119                return hid_sensor_write_raw_hyst_value(
 120                                &temp_st->common_attributes, val, val2);
 121        default:
 122                return -EINVAL;
 123        }
 124}
 125
 126static const struct iio_info temperature_info = {
 127        .read_raw = &temperature_read_raw,
 128        .write_raw = &temperature_write_raw,
 129};
 130
 131/* Callback handler to send event after all samples are received and captured */
 132static int temperature_proc_event(struct hid_sensor_hub_device *hsdev,
 133                                unsigned int usage_id, void *pdev)
 134{
 135        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 136        struct temperature_state *temp_st = iio_priv(indio_dev);
 137
 138        if (atomic_read(&temp_st->common_attributes.data_ready))
 139                iio_push_to_buffers_with_timestamp(indio_dev,
 140                                &temp_st->temperature_data,
 141                                iio_get_time_ns(indio_dev));
 142
 143        return 0;
 144}
 145
 146/* Capture samples in local storage */
 147static int temperature_capture_sample(struct hid_sensor_hub_device *hsdev,
 148                                unsigned int usage_id, size_t raw_len,
 149                                char *raw_data, void *pdev)
 150{
 151        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 152        struct temperature_state *temp_st = iio_priv(indio_dev);
 153
 154        switch (usage_id) {
 155        case HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE:
 156                temp_st->temperature_data = *(s32 *)raw_data;
 157                return 0;
 158        default:
 159                return -EINVAL;
 160        }
 161}
 162
 163/* Parse report which is specific to an usage id*/
 164static int temperature_parse_report(struct platform_device *pdev,
 165                                struct hid_sensor_hub_device *hsdev,
 166                                struct iio_chan_spec *channels,
 167                                unsigned int usage_id,
 168                                struct temperature_state *st)
 169{
 170        int ret;
 171
 172        ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
 173                        usage_id,
 174                        HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE,
 175                        &st->temperature_attr);
 176        if (ret < 0)
 177                return ret;
 178
 179        temperature_adjust_channel_bit_mask(channels, 0,
 180                                        st->temperature_attr.size);
 181
 182        st->scale_precision = hid_sensor_format_scale(
 183                                HID_USAGE_SENSOR_TEMPERATURE,
 184                                &st->temperature_attr,
 185                                &st->scale_pre_decml, &st->scale_post_decml);
 186
 187        /* Set Sensitivity field ids, when there is no individual modifier */
 188        if (st->common_attributes.sensitivity.index < 0)
 189                sensor_hub_input_get_attribute_info(hsdev,
 190                        HID_FEATURE_REPORT, usage_id,
 191                        HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
 192                        HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE,
 193                        &st->common_attributes.sensitivity);
 194
 195        return ret;
 196}
 197
 198static struct hid_sensor_hub_callbacks temperature_callbacks = {
 199        .send_event = &temperature_proc_event,
 200        .capture_sample = &temperature_capture_sample,
 201};
 202
 203/* Function to initialize the processing for usage id */
 204static int hid_temperature_probe(struct platform_device *pdev)
 205{
 206        static const char *name = "temperature";
 207        struct iio_dev *indio_dev;
 208        struct temperature_state *temp_st;
 209        struct iio_chan_spec *temp_chans;
 210        struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
 211        int ret;
 212
 213        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*temp_st));
 214        if (!indio_dev)
 215                return -ENOMEM;
 216
 217        temp_st = iio_priv(indio_dev);
 218        temp_st->common_attributes.hsdev = hsdev;
 219        temp_st->common_attributes.pdev = pdev;
 220
 221        ret = hid_sensor_parse_common_attributes(hsdev,
 222                                        HID_USAGE_SENSOR_TEMPERATURE,
 223                                        &temp_st->common_attributes);
 224        if (ret)
 225                return ret;
 226
 227        temp_chans = devm_kmemdup(&indio_dev->dev, temperature_channels,
 228                                sizeof(temperature_channels), GFP_KERNEL);
 229        if (!temp_chans)
 230                return -ENOMEM;
 231
 232        ret = temperature_parse_report(pdev, hsdev, temp_chans,
 233                                HID_USAGE_SENSOR_TEMPERATURE, temp_st);
 234        if (ret)
 235                return ret;
 236
 237        indio_dev->channels = temp_chans;
 238        indio_dev->num_channels = ARRAY_SIZE(temperature_channels);
 239        indio_dev->dev.parent = &pdev->dev;
 240        indio_dev->info = &temperature_info;
 241        indio_dev->name = name;
 242        indio_dev->modes = INDIO_DIRECT_MODE;
 243
 244        ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev,
 245                                        &iio_pollfunc_store_time, NULL, NULL);
 246        if (ret)
 247                return ret;
 248
 249        atomic_set(&temp_st->common_attributes.data_ready, 0);
 250        ret = hid_sensor_setup_trigger(indio_dev, name,
 251                                &temp_st->common_attributes);
 252        if (ret)
 253                return ret;
 254
 255        platform_set_drvdata(pdev, indio_dev);
 256
 257        temperature_callbacks.pdev = pdev;
 258        ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE,
 259                                        &temperature_callbacks);
 260        if (ret)
 261                goto error_remove_trigger;
 262
 263        ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
 264        if (ret)
 265                goto error_remove_callback;
 266
 267        return ret;
 268
 269error_remove_callback:
 270        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE);
 271error_remove_trigger:
 272        hid_sensor_remove_trigger(&temp_st->common_attributes);
 273        return ret;
 274}
 275
 276/* Function to deinitialize the processing for usage id */
 277static int hid_temperature_remove(struct platform_device *pdev)
 278{
 279        struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
 280        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 281        struct temperature_state *temp_st = iio_priv(indio_dev);
 282
 283        sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE);
 284        hid_sensor_remove_trigger(&temp_st->common_attributes);
 285
 286        return 0;
 287}
 288
 289static const struct platform_device_id hid_temperature_ids[] = {
 290        {
 291                /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
 292                .name = "HID-SENSOR-200033",
 293        },
 294        { /* sentinel */ }
 295};
 296MODULE_DEVICE_TABLE(platform, hid_temperature_ids);
 297
 298static struct platform_driver hid_temperature_platform_driver = {
 299        .id_table = hid_temperature_ids,
 300        .driver = {
 301                .name   = "temperature-sensor",
 302                .pm     = &hid_sensor_pm_ops,
 303        },
 304        .probe          = hid_temperature_probe,
 305        .remove         = hid_temperature_remove,
 306};
 307module_platform_driver(hid_temperature_platform_driver);
 308
 309MODULE_DESCRIPTION("HID Environmental temperature sensor");
 310MODULE_AUTHOR("Song Hongyan <hongyan.song@intel.com>");
 311MODULE_LICENSE("GPL v2");
 312