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