// SPDX-License-Identifier: GPL-2.0-only /* * HID Sensors Driver * Copyright (c) 2012, Intel Corporation. */ #include #include #include #include #include #include #include #include #include "hid-ids.h" /*SENSCOL*/ #include /*IIO*/ #include /*#define CONFIG_ISS_PATH_SENSCOL*/ /***************************************/ #if 1 #ifdef CONFIG_ISS_PATH_SENSCOL #define SENSCOL 1 #else #define SENSCOL 0 #endif #ifdef CONFIG_ISS_PATH_IIO #define IIO 1 #else #define IIO 0 #endif #else #define SENSCOL 1 #define IIO 0 #endif /***************************************/ #define HID_SENSOR_HUB_ENUM_QUIRK 0x01 /** * struct sensor_hub_data - Hold a instance data for a HID hub device * @mutex: Mutex to serialize synchronous request. * @lock: Spin lock to protect pending request structure. * @dyn_callback_list: Holds callback function * @dyn_callback_lock: spin lock to protect callback list * @hid_sensor_hub_client_devs: Stores all MFD cells for a hub instance. * @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached). * @ref_cnt: Number of MFD clients have opened this device */ struct sensor_hub_data { struct mutex mutex; spinlock_t lock; struct list_head dyn_callback_list; spinlock_t dyn_callback_lock; struct mfd_cell *hid_sensor_hub_client_devs; int hid_sensor_client_cnt; int ref_cnt; int sensor_hub_index; /* Needed to identify sensor in a collection */ }; #define MAX_HID_SENSOR_HUBS 32 static struct hid_device *hid_sensor_hubs[MAX_HID_SENSOR_HUBS]; static int sensor_hub_count; /** * struct hid_sensor_hub_callbacks_list - Stores callback list * @list: list head. * @usage_id: usage id for a physical device. * @hsdev: Stored hid instance for current hub device. * @usage_callback: Stores registered callback functions. * @priv: Private data for a physical device. */ struct hid_sensor_hub_callbacks_list { struct list_head list; u32 usage_id; struct hid_sensor_hub_device *hsdev; struct hid_sensor_hub_callbacks *usage_callback; void *priv; }; static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev, int dir) { struct hid_report *report; list_for_each_entry(report, &hdev->report_enum[dir].report_list, list) { if (report->id == id) return report; } hid_warn(hdev, "No report with id 0x%x found\n", id); return NULL; } static int sensor_hub_get_physical_device_count(struct hid_device *hdev) { int i; int count = 0; for (i = 0; i < hdev->maxcollection; ++i) { struct hid_collection *collection = &hdev->collection[i]; if (collection->type == HID_COLLECTION_PHYSICAL || collection->type == HID_COLLECTION_APPLICATION) ++count; } return count; } static void sensor_hub_fill_attr_info( struct hid_sensor_hub_attribute_info *info, s32 index, s32 report_id, struct hid_field *field) { info->index = index; info->report_id = report_id; info->units = field->unit; info->unit_expo = field->unit_exponent; info->size = (field->report_size * field->report_count)/8; info->logical_minimum = field->logical_minimum; info->logical_maximum = field->logical_maximum; } static struct hid_sensor_hub_callbacks *sensor_hub_get_callback( struct hid_device *hdev, u32 usage_id, int collection_index, struct hid_sensor_hub_device **hsdev, void **priv) { struct hid_sensor_hub_callbacks_list *callback; struct sensor_hub_data *pdata = hid_get_drvdata(hdev); unsigned long flags; spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) if ((callback->usage_id == usage_id || callback->usage_id == HID_USAGE_SENSOR_COLLECTION) && (collection_index >= callback->hsdev->start_collection_index) && (collection_index < callback->hsdev->end_collection_index)) { *priv = callback->priv; *hsdev = callback->hsdev; spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return callback->usage_callback; } spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return NULL; } int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, u32 usage_id, struct hid_sensor_hub_callbacks *usage_callback) { struct hid_sensor_hub_callbacks_list *callback; struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); unsigned long flags; spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) if (callback->usage_id == usage_id && callback->hsdev == hsdev) { spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return -EINVAL; } callback = kzalloc(sizeof(*callback), GFP_ATOMIC); if (!callback) { spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return -ENOMEM; } callback->hsdev = hsdev; callback->usage_callback = usage_callback; callback->usage_id = usage_id; callback->priv = NULL; /* * If there is a handler registered for the collection type, then * it will handle all reports for sensors in this collection. If * there is also an individual sensor handler registration, then * we want to make sure that the reports are directed to collection * handler, as this may be a fusion sensor. So add collection handlers * to the beginning of the list, so that they are matched first. */ if (usage_id == HID_USAGE_SENSOR_COLLECTION) list_add(&callback->list, &pdata->dyn_callback_list); else list_add_tail(&callback->list, &pdata->dyn_callback_list); spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return 0; } EXPORT_SYMBOL_GPL(sensor_hub_register_callback); int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, u32 usage_id) { struct hid_sensor_hub_callbacks_list *callback; struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); unsigned long flags; spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) if (callback->usage_id == usage_id && callback->hsdev == hsdev) { list_del(&callback->list); kfree(callback); break; } spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return 0; } EXPORT_SYMBOL_GPL(sensor_hub_remove_callback); int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, u32 field_index, int buffer_size, void *buffer) { struct hid_report *report; struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); __s32 *buf32 = buffer; int i = 0; int remaining_bytes; __s32 value; int ret = 0; mutex_lock(&data->mutex); report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); if (!report || (field_index >= report->maxfield)) { ret = -EINVAL; goto done_proc; } remaining_bytes = buffer_size % sizeof(__s32); buffer_size = buffer_size / sizeof(__s32); if (buffer_size) { for (i = 0; i < buffer_size; ++i) { ret = hid_set_field(report->field[field_index], i, (__force __s32)cpu_to_le32(*buf32)); if (ret) goto done_proc; ++buf32; } } if (remaining_bytes) { value = 0; memcpy(&value, (u8 *)buf32, remaining_bytes); ret = hid_set_field(report->field[field_index], i, (__force __s32)cpu_to_le32(value)); if (ret) goto done_proc; } hid_hw_request(hsdev->hdev, report, HID_REQ_SET_REPORT); hid_hw_wait(hsdev->hdev); done_proc: mutex_unlock(&data->mutex); return ret; } EXPORT_SYMBOL_GPL(sensor_hub_set_feature); int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, u32 field_index, int buffer_size, void *buffer) { struct hid_report *report; struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); int report_size; int ret = 0; u8 *val_ptr; int buffer_index = 0; int i; memset(buffer, 0, buffer_size); mutex_lock(&data->mutex); report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); if (!report || (field_index >= report->maxfield) || report->field[field_index]->report_count < 1) { ret = -EINVAL; goto done_proc; } hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); hid_hw_wait(hsdev->hdev); /* calculate number of bytes required to read this field */ report_size = DIV_ROUND_UP(report->field[field_index]->report_size, 8) * report->field[field_index]->report_count; if (!report_size) { ret = -EINVAL; goto done_proc; } ret = min(report_size, buffer_size); val_ptr = (u8 *)report->field[field_index]->value; for (i = 0; i < report->field[field_index]->report_count; ++i) { if (buffer_index >= ret) break; memcpy(&((u8 *)buffer)[buffer_index], val_ptr, report->field[field_index]->report_size / 8); val_ptr += sizeof(__s32); buffer_index += (report->field[field_index]->report_size / 8); } done_proc: mutex_unlock(&data->mutex); return ret; } EXPORT_SYMBOL_GPL(sensor_hub_get_feature); int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, u32 usage_id, u32 attr_usage_id, u32 report_id, enum sensor_hub_read_flags flag, bool is_signed) { struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); unsigned long flags; struct hid_report *report; int ret_val = 0; report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); if (!report) return -EINVAL; mutex_lock(hsdev->mutex_ptr); if (flag == SENSOR_HUB_SYNC) { memset(&hsdev->pending, 0, sizeof(hsdev->pending)); init_completion(&hsdev->pending.ready); hsdev->pending.usage_id = usage_id; hsdev->pending.attr_usage_id = attr_usage_id; hsdev->pending.raw_size = 0; spin_lock_irqsave(&data->lock, flags); hsdev->pending.status = true; spin_unlock_irqrestore(&data->lock, flags); } mutex_lock(&data->mutex); hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); mutex_unlock(&data->mutex); if (flag == SENSOR_HUB_SYNC) { wait_for_completion_interruptible_timeout( &hsdev->pending.ready, HZ*5); switch (hsdev->pending.raw_size) { case 1: if (is_signed) ret_val = *(s8 *)hsdev->pending.raw_data; else ret_val = *(u8 *)hsdev->pending.raw_data; break; case 2: if (is_signed) ret_val = *(s16 *)hsdev->pending.raw_data; else ret_val = *(u16 *)hsdev->pending.raw_data; break; case 4: ret_val = *(u32 *)hsdev->pending.raw_data; break; default: ret_val = 0; } kfree(hsdev->pending.raw_data); hsdev->pending.status = false; } mutex_unlock(hsdev->mutex_ptr); return ret_val; } EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value); int hid_sensor_get_usage_index(struct hid_sensor_hub_device *hsdev, u32 report_id, int field_index, u32 usage_id) { struct hid_report *report; struct hid_field *field; int i; report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); if (!report || (field_index >= report->maxfield)) goto done_proc; field = report->field[field_index]; for (i = 0; i < field->maxusage; ++i) { if (field->usage[i].hid == usage_id) return field->usage[i].usage_index; } done_proc: return -EINVAL; } EXPORT_SYMBOL_GPL(hid_sensor_get_usage_index); int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, u8 type, u32 usage_id, u32 attr_usage_id, struct hid_sensor_hub_attribute_info *info) { int ret = -1; int i; struct hid_report *report; struct hid_field *field; struct hid_report_enum *report_enum; struct hid_device *hdev = hsdev->hdev; /* Initialize with defaults */ info->usage_id = usage_id; info->attrib_id = attr_usage_id; info->report_id = -1; info->index = -1; info->units = -1; info->unit_expo = -1; report_enum = &hdev->report_enum[type]; list_for_each_entry(report, &report_enum->report_list, list) { for (i = 0; i < report->maxfield; ++i) { field = report->field[i]; if (field->maxusage) { if (field->physical == usage_id && (field->logical == attr_usage_id || field->usage[0].hid == attr_usage_id) && (field->usage[0].collection_index >= hsdev->start_collection_index) && (field->usage[0].collection_index < hsdev->end_collection_index)) { sensor_hub_fill_attr_info(info, i, report->id, field); ret = 0; break; } } } } if (info->units == 0) info->units = HID_USAGE_SENSOR_UNITS_MILLISECOND; return ret; } EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info); #ifdef CONFIG_PM static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message) { struct sensor_hub_data *pdata = hid_get_drvdata(hdev); struct hid_sensor_hub_callbacks_list *callback; unsigned long flags; hid_dbg(hdev, " sensor_hub_suspend\n"); spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) { if (callback->usage_callback->suspend) callback->usage_callback->suspend( callback->hsdev, callback->priv); } spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return 0; } static int sensor_hub_resume(struct hid_device *hdev) { struct sensor_hub_data *pdata = hid_get_drvdata(hdev); struct hid_sensor_hub_callbacks_list *callback; unsigned long flags; hid_dbg(hdev, " sensor_hub_resume\n"); spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) { if (callback->usage_callback->resume) callback->usage_callback->resume( callback->hsdev, callback->priv); } spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return 0; } static int sensor_hub_reset_resume(struct hid_device *hdev) { return 0; } #endif static bool is_supported(int physical) { if (physical == 0x200073) return true; if (physical == 0x200041) return true; if (physical == 0x200076) return true; if (physical == 0x200083) return true; return false; } /****************************** SENSCOL block: START ******************************/ static int senscol_impl_added; static int is_sens_data_field(unsigned usage); static int get_field_index(struct hid_device *hdev, unsigned report_id, unsigned usage, int report_idx); /* Get sensor's property by name */ static struct sens_property *get_prop_by_name(struct sensor_def *sensor, char *name) { int i; for (i = 0; i < sensor->num_properties; ++i) if (!strcmp(sensor->properties[i].name, name)) return &sensor->properties[i]; return NULL; } /* Get sensor's data field by name */ static struct data_field *get_data_field_by_name(struct sensor_def *sensor, char *name) { int i; for (i = 0; i < sensor->num_data_fields; ++i) if (!strcmp(sensor->data_fields[i].name, name)) return &sensor->data_fields[i]; return NULL; } static int get_field_index(struct hid_device *hdev, unsigned report_id, unsigned usage, int report_type) { int i = 0; struct hid_report *report; report = sensor_hub_report(report_id, hdev, report_type /*HID_FEATURE_REPORT or HID_INPUT_REPORT*/); if (!report) { return -1; } for (i = 0; i < report->maxfield; ++i) if (report->field[i]->usage->hid == usage) return i; return -1; } /* * The reason for this _ex() function is broken semantics and existing usage of sensor_hub_get_feature() that * doesn't allow anything with ->report_count > 1 to be delivered. * If that was fixed, existing callers would immediately buffer-overflow if such feature was delivered * NOTES: * - if ret != 0, contents of pvalue and count are undefined. * - upon success, count is in s32 values (not in bytes) */ static int sensor_hub_get_feature_ex(struct hid_sensor_hub_device *hsdev, u32 report_id, u32 field_index, u32 *usage_id, s32 **pvalue, size_t *count) { struct hid_report *report; struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); int ret = 0; mutex_lock(&data->mutex); report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); if (!report || (field_index >= report->maxfield) || report->field[field_index]->report_count < 1) { ret = -EINVAL; goto done_proc; } hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); hid_hw_wait(hsdev->hdev); *pvalue = report->field[field_index]->value; *count = report->field[field_index]->report_count; *usage_id = report->field[field_index]->usage->hid; done_proc: mutex_unlock(&data->mutex); return ret; } static bool hid_is_string_property(uint32_t usage_id) { return ((usage_id == HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION) || (usage_id == HID_USAGE_SENSOR_PROPERTY_FRIENDLY_NAME)); } /* Get sensor hub device by index */ static struct sensor_hub_data *get_sensor_hub_by_index(unsigned idx) { int i; struct sensor_hub_data *sd; struct hid_device *hdev; for (i = 0; i < sensor_hub_count; ++i) { if (!hid_sensor_hubs[i]) continue; sd = hid_get_drvdata(hid_sensor_hubs[i]); if (!sd) continue; if (sd->sensor_hub_index == idx) return sd; } return NULL; } static int hid_get_sens_property(struct sensor_def *sensor, const struct sens_property* prop, char *value, size_t val_buf_size) { unsigned idx; struct sensor_hub_data *sd; char buf[1024]; /* Enough for single property (?) */ unsigned report_id; int field; uint32_t usage_id; int32_t *pval; size_t count; int rv; if (!sensor || !prop) return -EINVAL; /* input is invalid */ /* sensor hub device */ idx = sensor->id >> 16 & 0xFFFF; sd = get_sensor_hub_by_index(idx); if (!sd) return -EINVAL; /* sensor->id is bad */ /* Report ID */ report_id = sensor->id & 0xFFFF; /* Field index */ field = get_field_index(sd->hsdev->hdev, report_id, prop->usage_id, HID_FEATURE_REPORT); if (field == -1) return -EINVAL; /* Something is still wrong */ /* Get value */ rv = sensor_hub_get_feature_ex(sd->hsdev, report_id, field, &usage_id, &pval, &count); if (rv) return rv; if (hid_is_string_property(usage_id)) { int i; for (i = 0; i < count; ++i) buf[i] = (char)pval[i]; buf[i] = '\0'; } else { /* Verify output length */ sprintf(buf, "%d", *pval); } if (strlen(buf) >= val_buf_size) return -EMSGSIZE; strcpy(value, buf); return 0; } static int hid_set_sens_property(struct sensor_def *sensor, const struct sens_property *prop, const char *value) { unsigned idx; struct sensor_hub_data *sd; unsigned report_id; int field; int32_t val; int rv; if (!sensor || !prop) return -EINVAL; /* input is invalid */ /* Value */ rv = sscanf(value, " %d ", &val); if (rv != 1) return -EINVAL; /* Bad value */ /* sensor hub device */ idx = sensor->id >> 16 & 0xFFFF; sd = get_sensor_hub_by_index(idx); if (!sd) return -EINVAL; /* sensor->id is bad */ /* Report ID */ report_id = sensor->id & 0xFFFF; /* Field index */ field = get_field_index(sd->hsdev->hdev, report_id, prop->usage_id, HID_FEATURE_REPORT); if (field == -1) return -EINVAL; /* Something is still wrong */ /* Get value */ rv = sensor_hub_set_feature(sd->hsdev, report_id, field, val); return rv; } static int hid_get_sample(struct sensor_def *sensor, void *sample_buf, size_t sample_buf_size) { unsigned idx; struct sensor_hub_data *sd; unsigned report_id; int field; struct data_field *data_field; int32_t val; int rv; /* sensor hub device */ idx = sensor->id >> 16 & 0xFFFF; sd = get_sensor_hub_by_index(idx); if (!sd) return -EINVAL; /* sensor->id is bad */ /* Report ID */ report_id = sensor->id & 0xFFFF; /* Request an input report with the first data field, regardless of what it is */ data_field = &sensor->data_fields[0]; val = sensor_hub_input_attr_get_raw_value(sd->hsdev, sensor->usage_id, data_field->usage_id, HID_INPUT_REPORT); if (!sd->pending.status) return -EIO; /* * Actual sample will be pushed by sensor_hub_raw_event(). * Invoke a short sleep in order to remove threads race condition and ensure that the sample is in senscol buffer */ schedule_timeout(2); return 0; } /* Check sensor is activated and in batch mode * * property_power_state = 2 hid_usage 0x200319 * * property_reporting_state = 2/5 hid_usage 0x200316 * * property_report_interval != 0 hid_usage 0x20030e * * property_report_interval_resolution != 0 hid_usage 0x20530e * * return value: 0 - sensor is not activated in batch * * 1 - sensor is activated in batch */ static int hid_batch_check(struct sensor_def *sensor) { unsigned idx; struct sensor_hub_data *sd; unsigned report_id; struct hid_report *report; int field_idx; __s32 val; idx = sensor->id >> 16 & 0xFFFF; sd = get_sensor_hub_by_index(idx); report_id = sensor->id & 0xFFFF; report = sensor_hub_report(report_id, sd->hsdev->hdev, HID_FEATURE_REPORT); /* property_power_state */ field_idx = get_field_index(sd->hsdev->hdev, report_id, 0x200319, HID_FEATURE_REPORT); if (report->field[field_idx]->value[0] != 2) return 0; /* property_reporting_state */ field_idx = get_field_index(sd->hsdev->hdev, report_id, 0x200316, HID_FEATURE_REPORT); if (report->field[field_idx]->value[0] != 2 && report->field[field_idx]->value[0] != 5) return 0; /* property_report_interval */ field_idx = get_field_index(sd->hsdev->hdev, report_id, 0x20030e, HID_FEATURE_REPORT); if (report->field[field_idx]->value[0] == 0) return 0; /* property_report_interval_resolution */ field_idx = get_field_index(sd->hsdev->hdev, report_id, 0x20530e, HID_FEATURE_REPORT); if (report->field[field_idx]->value[0] == 0) return 0; dev_err(NULL, "%s() sensor 0x%x is in batch mode\n", __func__, sensor->id); return 1; } struct senscol_impl hid_senscol_impl = { .get_sens_property = hid_get_sens_property, .set_sens_property = hid_set_sens_property, .get_sample = hid_get_sample, .batch_check = hid_batch_check }; static int is_sens_data_field(unsigned usage) { if (usage >= 0x400 && usage <= 0x49F || usage >= 0x4B0 && usage <= 0x4DF || usage >= 0x4F0 && usage <= 0x4F7 || usage >= 0x500 && usage <= 0x52F || usage >= 0x540 && usage <= 0x57F || usage >= 590 && usage <= 0x7FF) return 1; return 0; } /******************************* SENSCOL block: END *******************************/ /* * Handle raw report as sent by device */ static int sensor_hub_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *raw_data, int size) { int i; u8 *ptr; int sz; struct sensor_hub_data *pdata = hid_get_drvdata(hdev); unsigned long flags; struct hid_collection *collection = NULL; void *priv = NULL; struct hid_sensor_hub_device *hsdev = NULL; /*#if SENSCOL*/ uint32_t sensor_id; unsigned char data_buf[1024]; unsigned sample_size; /*#endif*/ /*#if IIO*/ struct hid_sensor_hub_callbacks *callback = NULL; /*#endif*/ hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n", report->id, size, report->type); hid_dbg(hdev, "maxfield:%d\n", report->maxfield); if (report->type != HID_INPUT_REPORT) return 0; ptr = raw_data; if (report->id) ptr++; /* Skip report id */ spin_lock_irqsave(&pdata->lock, flags); /*#if SENSCOL*/ /* make up senscol id */ sensor_id = pdata->sensor_hub_index << 16 | report->id & 0xFFFF; sample_size = 0; /*#endif*/ for (i = 0; i < report->maxfield; ++i) { hid_dbg(hdev, "%d collection_index:%x hid:%x sz:%x\n", i, report->field[i]->usage->collection_index, report->field[i]->usage->hid, (report->field[i]->report_size * report->field[i]->report_count)/8); sz = (report->field[i]->report_size * report->field[i]->report_count)/8; /*#if IIO*/ collection = &hdev->collection[ report->field[i]->usage->collection_index]; hid_dbg(hdev, "collection->usage %x\n", collection->usage); callback = sensor_hub_get_callback(hdev, report->field[i]->physical, report->field[i]->usage[0].collection_index, &hsdev, &priv); if (!callback) { ptr += sz; continue; } if (hsdev->pending.status && (hsdev->pending.attr_usage_id == report->field[i]->usage->hid || hsdev->pending.attr_usage_id == report->field[i]->logical)) { hid_dbg(hdev, "data was pending ...\n"); hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC); if (hsdev->pending.raw_data) hsdev->pending.raw_size = sz; else hsdev->pending.raw_size = 0; complete(&hsdev->pending.ready); } if (callback->capture_sample) { if (report->field[i]->logical) callback->capture_sample(hsdev, report->field[i]->logical, sz, ptr, callback->pdev); else callback->capture_sample(hsdev, report->field[i]->usage->hid, sz, ptr, callback->pdev); } /*#endif*/ /*#if SENSCOL*/ /* Prepare data for senscol sample */ if (is_sens_data_field(report->field[i]->usage->hid & 0xFFFF)) { dev_dbg(&hdev->dev, "%s(): aggregating, sz=%u \n", __func__, sample_size); memcpy(data_buf + sample_size, ptr, sz); sample_size += sz; } /*#endif*/ /* If we want to add indication into raw stream that the last sample was synchronous, it's here: check for complete() condition above */ ptr += sz; } /*#if IIO*/ if (callback && collection && callback->send_event) callback->send_event(hsdev, collection->usage, callback->pdev); /*endif*/ spin_unlock_irqrestore(&pdata->lock, flags); /*#if SENSCOL*/ /* Upstream sample to sensor collection framework */ dev_dbg(&hdev->dev, "%s(): calling push_sample, aggregated sample size is %u\n", __func__, sample_size); push_sample(sensor_id, data_buf); /*#endif*/ return 1; } int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev) { int ret = 0; struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); mutex_lock(&data->mutex); if (!data->ref_cnt) { ret = hid_hw_open(hsdev->hdev); if (ret) { hid_err(hsdev->hdev, "failed to open hid device\n"); mutex_unlock(&data->mutex); return ret; } } data->ref_cnt++; mutex_unlock(&data->mutex); return ret; } EXPORT_SYMBOL_GPL(sensor_hub_device_open); void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev) { struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); mutex_lock(&data->mutex); data->ref_cnt--; if (!data->ref_cnt) hid_hw_close(hsdev->hdev); mutex_unlock(&data->mutex); } EXPORT_SYMBOL_GPL(sensor_hub_device_close); static __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { /* * Checks if the report descriptor of Thinkpad Helix 2 has a logical * minimum for magnetic flux axis greater than the maximum. */ if (hdev->product == USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA && *rsize == 2558 && rdesc[913] == 0x17 && rdesc[914] == 0x40 && rdesc[915] == 0x81 && rdesc[916] == 0x08 && rdesc[917] == 0x00 && rdesc[918] == 0x27 && rdesc[921] == 0x07 && rdesc[922] == 0x00) { /* Sets negative logical minimum for mag x, y and z */ rdesc[914] = rdesc[935] = rdesc[956] = 0xc0; rdesc[915] = rdesc[936] = rdesc[957] = 0x7e; rdesc[916] = rdesc[937] = rdesc[958] = 0xf7; rdesc[917] = rdesc[938] = rdesc[959] = 0xff; } return rdesc; } static int fill_data_field(struct hid_field *field, unsigned usage, int need_internal_index, int k, struct sensor_def *senscol_sensor) { struct data_field data_field; char *usage_name; int rv; memset(&data_field, 0, sizeof(struct data_field)); usage_name = senscol_usage_to_name(usage & 0xFFFF); if (usage_name) data_field.name = need_internal_index ? kasprintf(GFP_KERNEL, "%s_%d", usage_name, k) : kasprintf(GFP_KERNEL, "%s", usage_name); else { data_field.name = need_internal_index ? kasprintf(GFP_KERNEL, "data-%X_%d", usage, k) : kasprintf(GFP_KERNEL, "data-%X", usage); } if (!data_field.name) return -ENOMEM; data_field.usage_id = usage; data_field.is_numeric = (field->flags & HID_MAIN_ITEM_VARIABLE); if (data_field.is_numeric) { if (field->unit_exponent > 7 || field->unit_exponent < -8) data_field.exp = 0xFF; else if (field->unit_exponent >= 0) data_field.exp = field->unit_exponent; else data_field.exp = 0x10 - field->unit_exponent; data_field.unit = field->unit; } data_field.len = (field->report_size >> 3) * field->report_count; rv = add_data_field(senscol_sensor, &data_field); senscol_sensor->sample_size += (field->report_size >> 3) * field->report_count; return rv; } static int sensor_hub_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; struct sensor_hub_data *sd; int i; char *name; int dev_cnt; struct hid_sensor_hub_device *hsdev; struct hid_sensor_hub_device *last_hsdev = NULL; struct hid_sensor_hub_device *collection_hsdev = NULL; sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL); if (!sd) { hid_err(hdev, "cannot allocate Sensor data\n"); return -ENOMEM; } hid_set_drvdata(hdev, sd); spin_lock_init(&sd->lock); spin_lock_init(&sd->dyn_callback_lock); mutex_init(&sd->mutex); ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); return ret; } INIT_LIST_HEAD(&hdev->inputs); ret = hid_hw_start(hdev, 0); if (ret) { hid_err(hdev, "hw start failed\n"); return ret; } INIT_LIST_HEAD(&sd->dyn_callback_list); sd->hid_sensor_client_cnt = 0; dev_cnt = sensor_hub_get_physical_device_count(hdev); if (dev_cnt > HID_MAX_PHY_DEVICES) { hid_err(hdev, "Invalid Physical device count\n"); ret = -EINVAL; goto err_stop_hw; } sd->hid_sensor_hub_client_devs = devm_kcalloc(&hdev->dev, dev_cnt, sizeof(struct mfd_cell), GFP_KERNEL); if (sd->hid_sensor_hub_client_devs == NULL) { hid_err(hdev, "Failed to allocate memory for mfd cells\n"); ret = -ENOMEM; goto err_stop_hw; } for (i = 0; i < hdev->maxcollection; ++i) { struct hid_collection *collection = &hdev->collection[i]; if (collection->type == HID_COLLECTION_PHYSICAL || collection->type == HID_COLLECTION_APPLICATION) { hsdev = devm_kzalloc(&hdev->dev, sizeof(*hsdev), GFP_KERNEL); if (!hsdev) { hid_err(hdev, "cannot allocate hid_sensor_hub_device\n"); ret = -ENOMEM; goto err_stop_hw; } hsdev->hdev = hdev; hsdev->vendor_id = hdev->vendor; hsdev->product_id = hdev->product; hsdev->usage = collection->usage; hsdev->mutex_ptr = devm_kzalloc(&hdev->dev, sizeof(struct mutex), GFP_KERNEL); if (!hsdev->mutex_ptr) { ret = -ENOMEM; goto err_stop_hw; } mutex_init(hsdev->mutex_ptr); hsdev->start_collection_index = i; if (last_hsdev) last_hsdev->end_collection_index = i; last_hsdev = hsdev; name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "HID-SENSOR-%x", collection->usage); if (name == NULL) { hid_err(hdev, "Failed MFD device name\n"); ret = -ENOMEM; goto err_stop_hw; } sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].name = name; sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].platform_data = hsdev; sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].pdata_size = sizeof(*hsdev); hid_dbg(hdev, "Adding %s:%d\n", name, hsdev->start_collection_index); sd->hid_sensor_client_cnt++; if (collection_hsdev) collection_hsdev->end_collection_index = i; if (collection->type == HID_COLLECTION_APPLICATION && collection->usage == HID_USAGE_SENSOR_COLLECTION) collection_hsdev = hsdev; } } if (last_hsdev) last_hsdev->end_collection_index = i; if (collection_hsdev) collection_hsdev->end_collection_index = i; ret = mfd_add_hotplug_devices(&hdev->dev, sd->hid_sensor_hub_client_devs, sd->hid_sensor_client_cnt); if (ret < 0) goto err_stop_hw; return ret; err_stop_hw: hid_hw_stop(hdev); return ret; } static void sensor_hub_remove(struct hid_device *hdev) { struct sensor_hub_data *data = hid_get_drvdata(hdev); unsigned long flags; int i; for (i = 0; i < sensor_hub_count; ++i) if (hid_sensor_hubs[i] == hdev) { hid_sensor_hubs[i] = NULL; break; } hid_dbg(hdev, " hardware removed\n"); hid_hw_close(hdev); hid_hw_stop(hdev); spin_lock_irqsave(&data->lock, flags); for (i = 0; i < data->hid_sensor_client_cnt; ++i) { struct hid_sensor_hub_device *hsdev = data->hid_sensor_hub_client_devs[i].platform_data; if (hsdev->pending.status) complete(&hsdev->pending.ready); } spin_unlock_irqrestore(&data->lock, flags); mfd_remove_devices(&hdev->dev); mutex_destroy(&data->mutex); } static const struct hid_device_id sensor_hub_devices[] = { { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID, HID_ANY_ID) }, { } }; MODULE_DEVICE_TABLE(hid, sensor_hub_devices); static struct hid_driver sensor_hub_driver = { .name = "hid-sensor-hub", .id_table = sensor_hub_devices, .probe = sensor_hub_probe, .remove = sensor_hub_remove, .raw_event = sensor_hub_raw_event, .report_fixup = sensor_hub_report_fixup, #ifdef CONFIG_PM .suspend = sensor_hub_suspend, .resume = sensor_hub_resume, .reset_resume = sensor_hub_reset_resume, #endif }; module_hid_driver(sensor_hub_driver); MODULE_DESCRIPTION("HID Sensor Hub driver"); MODULE_AUTHOR("Srinivas Pandruvada "); MODULE_LICENSE("GPL");