Merge "HID: Trace events for external sensor driver"

This commit is contained in:
qctecmdr 2020-02-08 00:48:04 -08:00 • committed by Gerrit - the friendly Code Review server
commit bac1da57bd
8 changed files with 664 additions and 0 deletions

View file

@ -1144,6 +1144,18 @@ config HID_ALPS
Say Y here if you have a Alps touchpads over i2c-hid or usbhid
and want support for its special functionalities.
config HID_QVR
tristate "QVR support"
depends on HID
help
Say 'Y' or 'M' if you want to connect an external device to
stream sensor data for QVR support.
This module handles sensor data from external device and
writes it in to shared memory. Sensor data includes
temperature, accelerometer, gyroscope, magnetometer data.
If unsure, say Y.
endmenu
endif # HID

View file

@ -138,3 +138,5 @@ obj-$(CONFIG_I2C_HID) += i2c-hid/
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/
obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/
obj-$(CONFIG_HID_QVR) += hid-qvr.o hid-trace.o

View file

@ -1273,4 +1273,7 @@
#define USB_VENDOR_ID_SIGNOTEC 0x2133
#define USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011 0x0018
#define USB_VENDOR_ID_QVR5 0x045e
#define USB_DEVICE_ID_QVR5 0x0659
#endif

View file

@ -173,6 +173,8 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_QVR5, USB_DEVICE_ID_QVR5),
HID_QUIRK_HIDINPUT_FORCE | HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
{ 0 }
};
@ -561,6 +563,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
#if IS_ENABLED(CONFIG_HID_PRODIKEYS)
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) },
#endif
#if IS_ENABLED(CONFIG_HID_QVR)
{ HID_USB_DEVICE(USB_VENDOR_ID_QVR5, USB_DEVICE_ID_QVR5) },
#endif
#if IS_ENABLED(CONFIG_HID_RETRODE)
{ HID_USB_DEVICE(USB_VENDOR_ID_FUTURE_TECHNOLOGY, USB_DEVICE_ID_RETRODE2) },
#endif

397
drivers/hid/hid-qvr.c Normal file
View file

@ -0,0 +1,397 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/dma-buf.h>
#include <linux/msm_ion.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/hiddev.h>
#include <linux/hid-debug.h>
#include <linux/hidraw.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/spinlock.h>
#include <linux/timekeeping.h>
#include <linux/soc/qcom/smem_state.h>
#include "hid-ids.h"
#include "hid-qvr.h"
#include "hid-trace.h"
static struct dma_buf *qvr_buf;
static void *vaddr;
static size_t vsize;
static uint64_t ts_base;
static uint64_t ts_offset;
struct gpio_info {
unsigned int smem_bit;
struct qcom_smem_state *smem_state;
};
static struct device *qvr_device;
static struct gpio_info gpio_info_out;
static struct hid_driver qvr_external_sensor_driver;
static int fd;
const static int msg_size = 368;
const static int hid_request_report_id = 2;
const static int hid_request_report_size = 64;
struct qvr_buf_index {
int most_recent_index;
uint8_t padding[60];
};
struct qvr_sensor_t {
uint64_t gts;
uint64_t ats;
uint64_t mts;
s32 gx;
s32 gy;
s32 gz;
s32 ax;
s32 ay;
s32 az;
s32 mx;
s32 my;
s32 mz;
uint8_t padding[4];
};
int qvr_send_package_wrap(u8 *message, int msize, struct hid_device *hid)
{
struct qvr_sensor_t *sensor_buf;
struct qvr_sensor_t *data;
static int buf_index;
struct external_imu_format imuData = { 0 };
struct qvr_buf_index *index_buf;
/*
* Actual message size is 369 bytes
* to make it 8 byte aligned we created a structure of size 368 bytes.
* Ignoring the first byte 'report id' (which is always 1)
*
*/
memcpy((void *)&imuData, (void *)message + 1, msg_size);
if (!ts_base)
ts_base = ktime_to_ns(ktime_get_boottime());
if (!ts_offset)
ts_offset = imuData.gts0;
index_buf = (struct qvr_buf_index *)
((uintptr_t)vaddr + (vsize / 2) + (8 * sizeof(*sensor_buf)));
sensor_buf = (struct qvr_sensor_t *)((uintptr_t)vaddr + (vsize / 2));
data = (struct qvr_sensor_t *)&(sensor_buf[buf_index]);
if (ts_offset > imuData.gts0)
data->ats = ts_base + ((ts_offset - imuData.gts0) * 100);
else
data->ats = ts_base + ((imuData.gts0 - ts_offset) * 100);
data->gts = data->ats;
data->mts = data->ats;
data->ax = -imuData.ax0;
data->ay = imuData.ay0;
data->az = -imuData.az0;
data->gx = -imuData.gx0;
data->gy = imuData.gy0;
data->gz = -imuData.gz0;
data->mx = -imuData.mx0;
data->my = imuData.my0;
data->mz = -imuData.mz0;
trace_qvr_recv_sensor("gyro", data->gts, data->gx, data->gy, data->gz);
trace_qvr_recv_sensor("accel", data->ats, data->ax, data->ay, data->az);
index_buf->most_recent_index = buf_index;
buf_index = (buf_index == (8 - 1)) ? 0 : buf_index + 1;
return 0;
}
static int register_smp2p(struct device *dev, char *node_name,
struct gpio_info *gpio_info_ptr)
{
struct device_node *node = dev->of_node;
if (!gpio_info_ptr)
return -EINVAL;
if (node == NULL) {
pr_debug("%s: device node NULL\n", __func__);
dev->of_node = of_find_compatible_node(NULL, NULL, node_name);
node = dev->of_node;
}
if (!of_find_property(node, "qcom,smem-states", NULL))
return -EINVAL;
gpio_info_ptr->smem_state = qcom_smem_state_get(dev,
"qvrexternal-smp2p-out",
&gpio_info_ptr->smem_bit);
pr_debug("%s: state: %pK, bit: %d\n", __func__,
gpio_info_ptr->smem_state,
gpio_info_ptr->smem_bit);
if (IS_ERR_OR_NULL(gpio_info_ptr->smem_state)) {
pr_debug("%s: Error smem_state\n", __func__);
return PTR_ERR(gpio_info_ptr->smem_state);
}
return 0;
}
static int kernel_map_gyro_buffer(int fd)
{
int ret = 0;
qvr_buf = dma_buf_get(fd);
if (IS_ERR_OR_NULL(qvr_buf)) {
ret = -ENOMEM;
pr_err("dma_buf_get failed for fd: %d\n", fd);
goto done;
}
ret = dma_buf_begin_cpu_access(qvr_buf, DMA_BIDIRECTIONAL);
if (ret) {
pr_err("%s: dma_buf_begin_cpu_access failed\n", __func__);
goto err_dma;
}
vsize = qvr_buf->size;
vaddr = dma_buf_kmap(qvr_buf, 0);
if (IS_ERR_OR_NULL(vaddr)) {
ret = -ENOMEM;
pr_err("dma_buf_kmap failed for fd: %d\n", fd);
goto err_end_access;
}
return 0;
err_end_access:
dma_buf_end_cpu_access(qvr_buf, DMA_BIDIRECTIONAL);
err_dma:
dma_buf_put(qvr_buf);
qvr_buf = NULL;
done:
return ret;
}
static void kernel_unmap_gyro_buffer(void)
{
if (IS_ERR_OR_NULL(vaddr))
return;
dma_buf_kunmap(qvr_buf, 0, vaddr);
dma_buf_end_cpu_access(qvr_buf, DMA_BIDIRECTIONAL);
vaddr = NULL;
dma_buf_put(qvr_buf);
qvr_buf = NULL;
}
static ssize_t fd_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return snprintf(buf, 16, "%d\n", fd);
}
static ssize_t fd_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
int ret;
ret = kstrtoint(buf, 10, &fd);
if (ret < 0)
return ret;
if (fd == -1)
kernel_unmap_gyro_buffer();
else
kernel_map_gyro_buffer(fd);
ts_base = 0;
ts_offset = 0;
return count;
}
static ssize_t ts_base_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return snprintf(buf, 16, "%lld\n", ts_base);
}
static ssize_t ts_base_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
return 0;
}
static ssize_t ts_offset_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return snprintf(buf, 16, "%lld\n", ts_offset * 100);
}
static ssize_t ts_offset_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
return 0;
}
static struct kobj_attribute fd_attribute = __ATTR(fd, 0664,
fd_show,
fd_store);
static struct kobj_attribute ts_base_attribute = __ATTR(ts_base, 0664,
ts_base_show,
ts_base_store);
static struct kobj_attribute ts_offset_attribute = __ATTR(ts_offset, 0664,
ts_offset_show,
ts_offset_store);
static struct attribute *attrs[] = {
&fd_attribute.attr,
&ts_base_attribute.attr,
&ts_offset_attribute.attr,
NULL,
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
static struct kobject *qvr_external_sensor_kobj;
static int qvr_external_sensor_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int ret;
char *node_name = "qcom,smp2p-interrupt-qvrexternal-5-out";
__u8 *hid_buf;
ret = register_smp2p(&hdev->dev, node_name, &gpio_info_out);
if (ret) {
pr_err("%s: register_smp2p failed\n", __func__);
goto err_free;
}
ret = hid_open_report(hdev);
if (ret) {
pr_err("%s: hid_open_report failed\n", __func__);
goto err_free;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
pr_err("%s: hid_hw_start failed\n", __func__);
goto err_free;
}
if (hdev->vendor == USB_VENDOR_ID_QVR5) {
hid_buf = kzalloc(255, GFP_ATOMIC);
if (hid_buf == NULL)
return -ENOMEM;
hid_buf[0] = hid_request_report_id;
hid_buf[1] = 7;
ret = hid_hw_raw_request(hdev, hid_buf[0], hid_buf,
hid_request_report_size,
HID_FEATURE_REPORT,
HID_REQ_SET_REPORT);
kfree(hid_buf);
}
qvr_device = &hdev->dev;
return 0;
err_free:
return ret;
}
static int qvr_external_sensor_raw_event(struct hid_device *hid,
struct hid_report *report,
u8 *data, int size)
{
static int val;
int ret = -1;
if ((hid->vendor == USB_VENDOR_ID_QVR5) && (vaddr != NULL)) {
ret = qvr_send_package_wrap(data/*hid_value*/, size, hid);
if (ret == 0) {
val = 1 ^ val;
qcom_smem_state_update_bits(gpio_info_out.smem_state,
BIT(gpio_info_out.smem_bit), val);
ret = -1;
}
}
return ret;
}
static void qvr_external_sensor_device_remove(struct hid_device *hdev)
{
hid_hw_stop(hdev);
}
static struct hid_device_id qvr_external_sensor_table[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_QVR5, USB_DEVICE_ID_QVR5) },
{ }
};
MODULE_DEVICE_TABLE(hid, qvr_external_sensor_table);
static struct hid_driver qvr_external_sensor_driver = {
.name = "qvr_external_sensor",
.id_table = qvr_external_sensor_table,
.probe = qvr_external_sensor_probe,
.raw_event = qvr_external_sensor_raw_event,
.remove = qvr_external_sensor_device_remove,
};
module_hid_driver(qvr_external_sensor_driver);
static int __init qvr_external_sensor_init(void)
{
int ret = 0;
qvr_external_sensor_kobj =
kobject_create_and_add("qvr_external_sensor", kernel_kobj);
if (!qvr_external_sensor_kobj) {
pr_err("%s: kobject_create_and_add() fail\n", __func__);
return -ENOMEM;
}
ret = sysfs_create_group(qvr_external_sensor_kobj, &attr_group);
if (ret) {
pr_err("%s: can't register sysfs\n", __func__);
return -ENOMEM;
}
return ret;
}
static void __exit qvr_external_sensor_exit(void)
{
kobject_put(qvr_external_sensor_kobj);
}
module_init(qvr_external_sensor_init);
module_exit(qvr_external_sensor_exit);
MODULE_LICENSE("GPL v2");

168
drivers/hid/hid-qvr.h Normal file
View file

@ -0,0 +1,168 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef HID_QVR_H_FILE
#define HID_QVR_H_FILE
#define USB_VENDOR_ID_QVR5 0x045e
#define USB_DEVICE_ID_QVR5 0x0659
#define QVR_EXTERNAL_SENSOR_REPORT_ID 0x1
struct external_imu_format {
s16 temp0;
s16 temp1;
s16 temp2;
s16 temp3;
u64 gts0;
u64 gts1;
u64 gts2;
u64 gts3;
s16 gx0;
s16 gx1;
s16 gx2;
s16 gx3;
s16 gx4;
s16 gx5;
s16 gx6;
s16 gx7;
s16 gx8;
s16 gx9;
s16 gx10;
s16 gx11;
s16 gx12;
s16 gx13;
s16 gx14;
s16 gx15;
s16 gx16;
s16 gx17;
s16 gx18;
s16 gx19;
s16 gx20;
s16 gx21;
s16 gx22;
s16 gx23;
s16 gx24;
s16 gx25;
s16 gx26;
s16 gx27;
s16 gx28;
s16 gx29;
s16 gx30;
s16 gx31;
s16 gy0;
s16 gy1;
s16 gy2;
s16 gy3;
s16 gy4;
s16 gy5;
s16 gy6;
s16 gy7;
s16 gy8;
s16 gy9;
s16 gy10;
s16 gy11;
s16 gy12;
s16 gy13;
s16 gy14;
s16 gy15;
s16 gy16;
s16 gy17;
s16 gy18;
s16 gy19;
s16 gy20;
s16 gy21;
s16 gy22;
s16 gy23;
s16 gy24;
s16 gy25;
s16 gy26;
s16 gy27;
s16 gy28;
s16 gy29;
s16 gy30;
s16 gy31;
s16 gz0;
s16 gz1;
s16 gz2;
s16 gz3;
s16 gz4;
s16 gz5;
s16 gz6;
s16 gz7;
s16 gz8;
s16 gz9;
s16 gz10;
s16 gz11;
s16 gz12;
s16 gz13;
s16 gz14;
s16 gz15;
s16 gz16;
s16 gz17;
s16 gz18;
s16 gz19;
s16 gz20;
s16 gz21;
s16 gz22;
s16 gz23;
s16 gz24;
s16 gz25;
s16 gz26;
s16 gz27;
s16 gz28;
s16 gz29;
s16 gz30;
s16 gz31;
u64 ats0;
u64 ats1;
u64 ats2;
u64 ats3;
s32 ax0;
s32 ax1;
s32 ax2;
s32 ax3;
s32 ay0;
s32 ay1;
s32 ay2;
s32 ay3;
s32 az0;
s32 az1;
s32 az2;
s32 az3;
u64 mts0;
u64 mts1;
u64 mts2;
u64 mts3;
s16 mx0;
s16 mx1;
s16 mx2;
s16 mx3;
s16 my0;
s16 my1;
s16 my2;
s16 my3;
s16 mz0;
s16 mz1;
s16 mz2;
s16 mz3; //368 bytes
};
int qvr_send_package_wrap(u8 *message, int msize, struct hid_device *hid);
void qvr_clear_def_parmeter(void);
void qvr_init(struct hid_device *hdev);
int qvr_input_init(void);
void qvr_input_remove(void);
#endif

19
drivers/hid/hid-trace.c Normal file
View file

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/* Instantiate tracepoints */
#define CREATE_TRACE_POINTS
#include "hid-trace.h"

58
drivers/hid/hid-trace.h Normal file
View file

@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#if !defined(_HID_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _HID_TRACE_H
#undef TRACE_SYSTEM
#define TRACE_SYSTEM hid
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE hid-trace
#include <linux/tracepoint.h>
TRACE_EVENT(qvr_recv_sensor,
TP_PROTO(char *sensor, uint64_t ts, s32 x, s32 y, s32 z),
TP_ARGS(sensor, ts, x, y, z),
TP_STRUCT__entry(
__field(char *, sensor)
__field(uint64_t, ts)
__field(int, x)
__field(int, y)
__field(int, z)
),
TP_fast_assign(
__entry->sensor = sensor;
__entry->ts = ts;
__entry->x = x;
__entry->y = y;
__entry->z = z;
),
TP_printk(
"%s - ts=%llu x=%d y=%d z=%d",
__entry->sensor,
__entry->ts,
__entry->x,
__entry->y,
__entry->z
)
);
#endif /* _HID_TRACE_H */
/* This part must be outside protection */
#include <trace/define_trace.h>