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