From bb21b20985e8a9fc9d171455246656db2f9e58b6 Mon Sep 17 00:00:00 2001 From: lanshh Date: Sun, 28 Aug 2016 18:55:53 +0800 Subject: [PATCH] hid: add usb hid driver for rockchip discrete vr device. repor key event and send sensor data to iio driver Change-Id: I6fdaa1eb88e8974675550061f022ce506e00f28e Signed-off-by: lanshh --- drivers/hid/Kconfig | 7 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 3 + drivers/hid/hid-ids.h | 5 + drivers/hid/hid-rkvr.c | 1186 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-rkvr.h | 38 ++ 6 files changed, 1240 insertions(+) create mode 100644 drivers/hid/hid-rkvr.c create mode 100644 drivers/hid/hid-rkvr.h diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 513a16cc6e18..187c3d6a3f9d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -920,6 +920,13 @@ config HID_SENSOR_CUSTOM_SENSOR standard sensors. Select this config option for custom/generic sensor support. +config HID_RKVR + tristate "RKVR device support" + depends on USB_HID + ---help--- + Support for RKVR devices. + Say Y here if you have a RKVR device. + endmenu endif # HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 00011fee08b9..e0985302c686 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -104,6 +104,7 @@ obj-$(CONFIG_HID_WALTOP) += hid-waltop.o obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR) += hid-sensor-custom.o +obj-$(CONFIG_HID_RKVR) += hid-rkvr.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ec791e169f8f..7ce13da61203 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1991,6 +1991,9 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5) }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9) }, +#endif +#ifdef CONFIG_HID_RKVR + { HID_USB_DEVICE(USB_VENDOR_ID_ROCKCHIP, USB_DEVICE_ID_NANOC) }, #endif { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 909ab0176ef2..cc801d77e95f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -824,6 +824,11 @@ #define USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO 0x3232 #define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a +#ifdef CONFIG_HID_RKVR +#define USB_VENDOR_ID_ROCKCHIP 0x071b +#define USB_DEVICE_ID_NANOC 0x3205 +#endif + #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 #define USB_DEVICE_ID_SAITEK_PS1000 0x0621 diff --git a/drivers/hid/hid-rkvr.c b/drivers/hid/hid-rkvr.c new file mode 100644 index 000000000000..2eeac6d6654a --- /dev/null +++ b/drivers/hid/hid-rkvr.c @@ -0,0 +1,1186 @@ +/* + * Rockchip VR driver for Linux + * + * Copyright (C) ROCKCHIP, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* + * Driver for Rockchip VR devices. Based on hidraw driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "hid-rkvr.h" +#include "hid-ids.h" + +#define USB_TRACKER_INTERFACE_PROTOCOL 0 +/* define rkvr interface number */ +#define RKVR_INTERFACE_USB_AUDIO_ID 1 +#define RKVR_INTERFACE_USB_SENSOR_ID 2 +#define RKVR_INTERFACE_USB_AUDIO_KEY_ID 1 +/* number of reports to buffer */ +#define RKVR_HIDRAW_BUFFER_SIZE 64 +#define RKVR_HIDRAW_MAX_DEVICES 8 +#define RKVR_FIRST_MINOR 0 + +static struct class *rkvr_class; + +static struct hidraw *rkvr_hidraw_table[RKVR_HIDRAW_MAX_DEVICES]; + +static DEFINE_MUTEX(minors_lock); + +struct keymap_t { + __u16 key_menu_up:1; + __u16 key_menu_down:1; + __u16 key_home_up:1; + __u16 key_home_down:1; + __u16 key_power_up:1; + __u16 key_power_down:1; + __u16 key_volup_up:1; + __u16 key_volup_down:1; + __u16 key_voldn_up:1; + __u16 key_voldn_down:1; + __u16 key_pressed:1; +}; + +union rkvr_data_t { + struct rkvr_data { + __u8 buf_head[6]; + __u8 buf_sensortemperature[2]; + __u8 buf_sensor[40]; + __u8 buf_reserve[12]; + struct keymap_t key_map; + } rkvr_data; + __u8 buf[62]; +}; + +static int rkvr_major; +static struct cdev rkvr_cdev; +static unsigned int count_array[15] = {0,}; +static unsigned long old_jiffy_array[15] = {0,}; +static int rkvr_index; +static int opens; + +struct sensor_hid_data { + void *priv; + int (*send_event)(char *raw_data, size_t raw_len, void *priv); +} sensorData; + +static DEFINE_MUTEX(device_list_lock); +static struct list_head rkvr_hid_hw_device_list = { + .next = &rkvr_hid_hw_device_list, + .prev = &rkvr_hid_hw_device_list +}; + +static struct rkvr_iio_hw_device *inv_hid_alloc(const char *name) +{ + struct rkvr_iio_hw_device *p; + const char *s; + + if (!name) + return 0; + s = kstrdup_const(name, GFP_KERNEL); + if (!s) + goto error; + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + goto error; + p->name = s; + return p; +error: + pr_err("%s error!\n", __func__); + if (s) + kfree_const(s); + return 0; +} + +static void inv_hid_free(struct rkvr_iio_hw_device *hw_device) +{ + kfree_const(hw_device->name); + kfree(hw_device); +} + +static int inv_hid_register_devcie(struct rkvr_iio_hw_device *hw_device) +{ + + mutex_lock(&device_list_lock); + if (hw_device->name && (!list_empty(&rkvr_hid_hw_device_list))) { + struct rkvr_iio_hw_device *p; + + list_for_each_entry(p, &rkvr_hid_hw_device_list, l) { + if (!strcmp(hw_device->name, p->name)) { + pr_err("%s already exist ,abort\n", hw_device->name); + return -1; + } + } + } + list_add_tail(&hw_device->l, &rkvr_hid_hw_device_list); + mutex_unlock(&device_list_lock); + return 0; +} + +static void inv_hid_unregister_and_destroy_devcie_by_name(const char *name) +{ + struct rkvr_iio_hw_device *p = NULL; + + mutex_lock(&device_list_lock); + list_for_each_entry(p, &rkvr_hid_hw_device_list, l) { + if (!strcmp(name, p->name)) { + list_del(&p->l); + break; + } + } + if (p) { + pr_info("find dev with name %s,free now\n", name); + inv_hid_free(p); + } + mutex_unlock(&device_list_lock); +} + +static ssize_t rkvr_hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct hidraw_list *list = file->private_data; + int ret = 0, len; + DECLARE_WAITQUEUE(wait, current); + + mutex_lock(&list->read_mutex); + while (ret == 0) { + if (list->head == list->tail) { + add_wait_queue(&list->hidraw->wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + while (list->head == list->tail) { + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + if (!list->hidraw->exist) { + ret = -EIO; + break; + } + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + + /* allow O_NONBLOCK to work well from other threads */ + mutex_unlock(&list->read_mutex); + schedule(); + mutex_lock(&list->read_mutex); + set_current_state(TASK_INTERRUPTIBLE); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&list->hidraw->wait, &wait); + } + + if (ret) + goto out; + + len = list->buffer[list->tail].len > count ? + count : list->buffer[list->tail].len; + + if (list->buffer[list->tail].value) { + if (copy_to_user(buffer, list->buffer[list->tail].value, len)) { + ret = -EFAULT; + goto out; + } + ret = len; + if (opens > 0 && rkvr_index < 15) { + if (++count_array[rkvr_index] >= 1000) { + unsigned long cur_jiffy = jiffies; + + hid_dbg(list->hidraw->hid, "rkvr: %d Hz, read(%d) (%d:%s)\n", (int)(1000 * HZ / (cur_jiffy - old_jiffy_array[rkvr_index])), rkvr_index, current->pid, current->comm); + count_array[rkvr_index] = 0; + old_jiffy_array[rkvr_index] = cur_jiffy; + } + if (++rkvr_index >= opens) + rkvr_index = 0; + } else { + rkvr_index = 0; + } + } + + kfree(list->buffer[list->tail].value); + list->buffer[list->tail].value = NULL; + list->tail = (list->tail + 1) & (RKVR_HIDRAW_BUFFER_SIZE - 1); + } +out: + mutex_unlock(&list->read_mutex); + return ret; +} + +/* The first byte is expected to be a report number. + * This function is to be called with the minors_lock mutex held + */ +static ssize_t rkvr_hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type) +{ + unsigned int minor = iminor(file_inode(file)); + struct hid_device *dev; + __u8 *buf; + int ret = 0; + + if (!rkvr_hidraw_table[minor] || !rkvr_hidraw_table[minor]->exist) { + ret = -ENODEV; + goto out; + } + + dev = rkvr_hidraw_table[minor]->hid; + + if (count > HID_MAX_BUFFER_SIZE) { + hid_warn(dev, "rkvr - pid %d passed too large report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + if (count < 2) { + hid_warn(dev, "rkvr - pid %d passed too short report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(buf, buffer, count)) { + ret = -EFAULT; + goto out_free; + } + + if ((report_type == HID_OUTPUT_REPORT) && + !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { + ret = hid_hw_output_report(dev, buf, count); + /* + * compatibility with old implementation of USB-HID and I2C-HID: + * if the device does not support receiving output reports, + * on an interrupt endpoint, fallback to SET_REPORT HID command. + */ + if (ret != -ENOSYS) + goto out_free; + } + + ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type, + HID_REQ_SET_REPORT); + +out_free: + kfree(buf); +out: + return ret; +} + +/* the first byte is expected to be a report number */ +static ssize_t rkvr_hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + ssize_t ret; + + mutex_lock(&minors_lock); + ret = rkvr_hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT); + mutex_unlock(&minors_lock); + return ret; +} + +/* This function performs a Get_Report transfer over the control endpoint + * per section 7.2.1 of the HID specification, version 1.1. The first byte + * of buffer is the report number to request, or 0x0 if the defice does not + * use numbered reports. The report_type parameter can be HID_FEATURE_REPORT + * or HID_INPUT_REPORT. This function is to be called with the minors_lock + * mutex held. + */ +static ssize_t rkvr_hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) +{ + unsigned int minor = iminor(file_inode(file)); + struct hid_device *dev; + __u8 *buf; + int ret = 0, len; + unsigned char report_number; + + dev = rkvr_hidraw_table[minor]->hid; + + if (!dev->ll_driver->raw_request) { + ret = -ENODEV; + goto out; + } + + if (count > HID_MAX_BUFFER_SIZE) { + hid_warn(dev, "rkvr - hidraw: pid %d passed too large report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + if (count < 2) { + hid_warn(dev, "rkvr - hidraw: pid %d passed too short report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + /* + * Read the first byte from the user. This is the report number, + * which is passed to hid_hw_raw_request(). + */ + if (copy_from_user(&report_number, buffer, 1)) { + ret = -EFAULT; + goto out_free; + } + + ret = hid_hw_raw_request(dev, report_number, buf, count, report_type, + HID_REQ_GET_REPORT); + if (ret < 0) + goto out_free; + len = (ret < count) ? ret : count; + + if (copy_to_user(buffer, buf, len)) { + ret = -EFAULT; + goto out_free; + } + + ret = len; + +out_free: + kfree(buf); +out: + return ret; +} + +static unsigned int rkvr_hidraw_poll(struct file *file, poll_table *wait) +{ + struct hidraw_list *list = file->private_data; + + poll_wait(file, &list->hidraw->wait, wait); + if (list->head != list->tail) + return POLLIN | POLLRDNORM; + if (!list->hidraw->exist) + return POLLERR | POLLHUP; + + return 0; +} + +static int rkvr_hidraw_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct hidraw *dev; + struct hidraw_list *list; + unsigned long flags; + int err = 0; + + list = kzalloc(sizeof(*list), GFP_KERNEL); + if (!list) { + err = -ENOMEM; + goto out; + } + + mutex_lock(&minors_lock); + if (!rkvr_hidraw_table[minor] || !rkvr_hidraw_table[minor]->exist) { + err = -ENODEV; + goto out_unlock; + } + + dev = rkvr_hidraw_table[minor]; + if (!dev->open++) { + err = hid_hw_power(dev->hid, PM_HINT_FULLON); + if (err < 0) { + dev->open--; + goto out_unlock; + } + + err = hid_hw_open(dev->hid); + + if (err < 0) { + hid_hw_power(dev->hid, PM_HINT_NORMAL); + dev->open--; + goto out_unlock; + } + } + + list->hidraw = rkvr_hidraw_table[minor]; + mutex_init(&list->read_mutex); + spin_lock_irqsave(&rkvr_hidraw_table[minor]->list_lock, flags); + list_add_tail(&list->node, &rkvr_hidraw_table[minor]->list); + spin_unlock_irqrestore(&rkvr_hidraw_table[minor]->list_lock, flags); + file->private_data = list; + + opens = dev->open; + +out_unlock: + mutex_unlock(&minors_lock); +out: + if (err < 0) + kfree(list); + + return err; +} + +static int rkvr_hidraw_fasync(int fd, struct file *file, int on) +{ + struct hidraw_list *list = file->private_data; + + return fasync_helper(fd, file, on, &list->fasync); +} + +static void rkvr_drop_ref(struct hidraw *hidraw, int exists_bit) +{ + if (exists_bit) { /*hw removed**/ + hidraw->exist = 0; + if (hidraw->open) { + hid_hw_close(hidraw->hid); + wake_up_interruptible(&hidraw->wait); + } + } else { + --hidraw->open; + } + + if (!hidraw->open) { + if (!hidraw->exist) { /*no opened && no hardware,delete all**/ + rkvr_hidraw_table[hidraw->minor] = NULL; + kfree(hidraw); + } else { + /* close device for last reader */ + hid_hw_power(hidraw->hid, PM_HINT_NORMAL); + hid_hw_close(hidraw->hid); + } + } +} + +static int rkvr_hidraw_release(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct hidraw_list *list = file->private_data; + unsigned long flags; + + mutex_lock(&minors_lock); + + spin_lock_irqsave(&rkvr_hidraw_table[minor]->list_lock, flags); + list_del(&list->node); + spin_unlock_irqrestore(&rkvr_hidraw_table[minor]->list_lock, flags); + + kfree(list); + rkvr_drop_ref(rkvr_hidraw_table[minor], 0); + + mutex_unlock(&minors_lock); + + return 0; +} + +static void rkvr_send_key_event(struct input_dev *input, int key_value, int state) +{ + if (!input) { + return; + } + if (state) { + input_report_key(input, key_value, 1); + input_sync(input); + } else { + input_report_key(input, key_value, 0); + input_sync(input); + } +} + +static int rkvr_keys_event(struct hid_device *hdev, void *data, unsigned long len) +{ + struct input_dev *input = hdev->hiddev; + union rkvr_data_t *rkvr_data = (union rkvr_data_t *)data; + + if (rkvr_data->rkvr_data.key_map.key_menu_up) + rkvr_send_key_event(input, KEY_MENU, 0); + else if (rkvr_data->rkvr_data.key_map.key_menu_down) + rkvr_send_key_event(input, KEY_MENU, 1); + else if (rkvr_data->rkvr_data.key_map.key_home_up) + rkvr_send_key_event(input, KEY_HOME, 0); + else if (rkvr_data->rkvr_data.key_map.key_home_down) + rkvr_send_key_event(input, KEY_HOME, 1); + else if (rkvr_data->rkvr_data.key_map.key_power_up) + rkvr_send_key_event(input, KEY_POWER, 0); + else if (rkvr_data->rkvr_data.key_map.key_power_down) + rkvr_send_key_event(input, KEY_POWER, 1); + else if (rkvr_data->rkvr_data.key_map.key_volup_up) + rkvr_send_key_event(input, KEY_VOLUMEUP, 0); + else if (rkvr_data->rkvr_data.key_map.key_volup_down) + rkvr_send_key_event(input, KEY_VOLUMEUP, 1); + else if (rkvr_data->rkvr_data.key_map.key_voldn_up) + rkvr_send_key_event(input, KEY_VOLUMEDOWN, 0); + else if (rkvr_data->rkvr_data.key_map.key_voldn_down) + rkvr_send_key_event(input, KEY_VOLUMEDOWN, 1); + return 0; +} + +static int rkvr_report_event(struct hid_device *hid, u8 *data, int len) +{ + struct hidraw *dev = hid->hidraw; + struct hidraw_list *list; + int ret = 0; + unsigned long flags; + union rkvr_data_t *rkvr_data = (union rkvr_data_t *)data; + struct sensor_hid_data *pdata = hid_get_drvdata(hid); + + spin_lock_irqsave(&dev->list_lock, flags); + if (hid->hiddev) { + rkvr_keys_event(hid, data, len); + } + if (pdata && pdata->priv && pdata->send_event) { + pdata->send_event(rkvr_data->buf, len, pdata->priv); + spin_unlock_irqrestore(&dev->list_lock, flags); + } else { + list_for_each_entry(list, &dev->list, node) { + int new_head = (list->head + 1) & (RKVR_HIDRAW_BUFFER_SIZE - 1); + + if (new_head == list->tail) + continue; + + list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC); + if (!list->buffer[list->head].value) { + ret = -ENOMEM; + spin_unlock_irqrestore(&dev->list_lock, flags); + break; + } + + list->buffer[list->head].len = len; + list->head = new_head; + kill_fasync(&list->fasync, SIGIO, POLL_IN); + } + spin_unlock_irqrestore(&dev->list_lock, flags); + wake_up_interruptible(&dev->wait); + } + return ret; +} + +static void hid_report_fill_rw(unsigned char *buf, u8 reg, u8 *data, int len, int w) +{ + if (w) + buf[0] = (1 << 7) | (len && 0x7f); + else + buf[0] = len && 0x7f; + buf[1] = reg; + memcpy(&buf[2], data, len); +} + +#if DEBUG_SYS +#define HID_OUTPUT_READREG 3 + +static int hid_report_readreg(struct device *dev, u8 reg, u8 *data, int len) +{ + struct hid_device *hid = container_of(dev, struct hid_device, dev); + unsigned char report_number = reg; + unsigned char report_type = HID_OUTPUT_READREG; + char buf[1 + sizeof(data) * len]; + int readlen = 1 + sizeof(data) * len; + int ret; + + ret = hid_hw_raw_request(hid, report_number, (unsigned char *)buf, readlen, report_type, HID_REQ_GET_REPORT); + if (ret != readlen) { + hid_info(hid, "id_hw_raw_request fail\n"); + } else { + memcpy(data, &buf[1], readlen); + hid_info(hid, "hid_report_readreg %02x %02x\n", reg, data[0]); + } + + return 0; +} + +static int hid_report_writereg(struct device *dev, u8 reg, u8 data) +{ + struct hid_device *hid = container_of(dev, struct hid_device, dev); + unsigned char report_number = 5; + unsigned char report_type = HID_OUTPUT_REPORT; + char buf[3 + sizeof(data)]; + int ret; + + hid_report_fill_rw(&buf[1], reg, &data, sizeof(data), 1); + ret = hid_hw_raw_request(hid, report_number, (unsigned char *)buf, 4, report_type, HID_REQ_SET_REPORT); + if (ret != 4) + hid_info(hid, "id_hw_raw_request fail\n"); + else + hid_info(hid, "id_hw_raw_request ok\n"); + + return 0; +} + +static ssize_t rkvr_dev_attr_debug_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hidraw *devraw; + struct hid_device *hid = container_of(dev, struct hid_device, dev); + + devraw = dev_get_drvdata(dev); + if (0 == strncmp(buf, "write", 5)) + hid_report_writereg(&devraw->hid->dev, 0, 0); + hid_info(hid, "%s\n", buf); + + return count; +} + +static ssize_t rkvr_dev_attr_debug_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + size_t count = 0; + u8 mpu6500_id = 0; + struct hidraw *devraw; + + devraw = dev_get_drvdata(dev); + if (!hid_report_readreg(&devraw->hid->dev, 0x75 | 0x80, &mpu6500_id, 1)) + count += sprintf(&buf[count], "reg value %d\n", mpu6500_id); + else + count += sprintf(&buf[count], "read fail\n"); + + return count; +} +static DEVICE_ATTR(debug, 0664, rkvr_dev_attr_debug_show, rkvr_dev_attr_debug_store); +#endif + +static int rkvr_hid_read(struct rkvr_iio_hw_device *hdev, int reg, unsigned char *data, int len) +{ + struct hid_device *hid = container_of(hdev->dev, struct hid_device, dev); + unsigned char report_number = reg; + unsigned char report_type = HID_OUTPUT_READREG; + char buf[1 + sizeof(data) * len]; + int readlen = 1 + sizeof(data) * len; + int ret; + + ret = hid_hw_raw_request(hid, report_number, (unsigned char *)buf, readlen, report_type, HID_REQ_GET_REPORT); + if (ret != readlen) { + hid_err(hid, "id_hw_raw_request fail\n"); + } else { + memcpy(data, &buf[1], sizeof(data) * len); + } + + return 0; +} + +static int rkvr_hid_write(struct rkvr_iio_hw_device *hdev, int reg, unsigned char data) +{ + struct hid_device *hid = container_of(hdev->dev, struct hid_device, dev); + unsigned char report_number = 5; + unsigned char report_type = HID_OUTPUT_REPORT; + char buf[3 + sizeof(data)]; + int ret; + + hid_report_fill_rw(&buf[1], reg, &data, sizeof(data), 1); + ret = hid_hw_raw_request(hid, report_number, (unsigned char *)buf, 4, report_type, HID_REQ_SET_REPORT); + if (ret != 4) + hid_info(hid, "id_hw_raw_request fail\n"); + else + hid_info(hid, "id_hw_raw_request ok\n"); + + return 0; +} + +static int rkvr_hid_open(struct rkvr_iio_hw_device *hdev) +{ + struct hid_device *hid; + int err; + + hid = container_of(hdev->dev, struct hid_device, dev); + err = hid_hw_power(hid, PM_HINT_FULLON); + if (err < 0) + return err; + err = hid_hw_open(hid); + if (err < 0) { + hid_hw_power(hid, PM_HINT_NORMAL); + return err; + } + + return 0; +} + +static void rkvr_hid_close(struct rkvr_iio_hw_device *hdev) +{ + struct hid_device *hid; + + hid = container_of(hdev->dev, struct hid_device, dev); + hid_hw_power(hid, PM_HINT_NORMAL); + hid_hw_close(hid); +} + +#if DYNAMIC_LOAD_MPU6500 +static int register_mpu6500; +struct platform_device mpu6500_dev = { + .name = "mpu6500", +}; +#endif + +static int rkvr_connect(struct hid_device *hid) +{ + int minor, result; + struct hidraw *dev; + + /* we accept any HID device, no matter the applications */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + result = -EINVAL; + mutex_lock(&minors_lock); + for (minor = 0; minor < RKVR_HIDRAW_MAX_DEVICES; minor++) { + if (rkvr_hidraw_table[minor]) + continue; + rkvr_hidraw_table[minor] = dev; + result = 0; + break; + } + if (result) { + mutex_unlock(&minors_lock); + kfree(dev); + goto out; + } + + dev->dev = device_create(rkvr_class, &hid->dev, MKDEV(rkvr_major, minor), + NULL, "%s%d", "rkvr", minor); + + if (IS_ERR(dev->dev)) { + rkvr_hidraw_table[minor] = NULL; + mutex_unlock(&minors_lock); + result = PTR_ERR(dev->dev); + kfree(dev); + goto out; + } + + dev_set_drvdata(dev->dev, dev); +#if DEBUG_SYS + device_create_file(dev->dev, &dev_attr_debug); +#endif + + { + struct rkvr_iio_hw_device *hw_device; + + hw_device = inv_hid_alloc("hid-rkvr"); + if (!hw_device) { + hid_err(hid, "inv_hid_alloc(\"hid-rkvr\") fail\n"); + rkvr_hidraw_table[minor] = NULL; + mutex_unlock(&minors_lock); + result = PTR_ERR(dev->dev); + kfree(dev); + goto out; + } + hw_device->dev = &hid->dev; + hw_device->open = rkvr_hid_open; + hw_device->close = rkvr_hid_close; + hw_device->read = rkvr_hid_read; + hw_device->write = rkvr_hid_write; + if (inv_hid_register_devcie(hw_device)) { + hid_err(hid, "inv_hid_register_devcie(\"hid-rkvr\") fail\n"); + inv_hid_free(hw_device); + rkvr_hidraw_table[minor] = NULL; + mutex_unlock(&minors_lock); + result = PTR_ERR(dev->dev); + kfree(dev); + goto out; + } + } + +#if DYNAMIC_LOAD_MPU6500 + if (!register_mpu6500) { + register_mpu6500 = 1; + hid_info(hid, "--->platform_device_register-->\n"); + platform_device_register(&mpu6500_dev); + } +#endif + + if (hid_hw_open(hid)) { + rkvr_hidraw_table[minor] = NULL; + mutex_unlock(&minors_lock); + result = PTR_ERR(dev->dev); + kfree(dev); + hid_err(hid, "rkvr_connect:hid_hw_open fail\n"); + goto out; + } + + init_waitqueue_head(&dev->wait); + spin_lock_init(&dev->list_lock); + INIT_LIST_HEAD(&dev->list); + + dev->hid = hid; + dev->minor = minor; + dev->exist = 1; + hid->hidraw = dev; /*struct hidraw * **/ + mutex_unlock(&minors_lock); +out: + return result; +} + +static int rkvr_keys_remove(struct hid_device *hdev) +{ + struct input_dev *input = hdev->hiddev; + + input_unregister_device(input); + return 0; +} + +static unsigned int key_codes[] = { + KEY_MENU, + KEY_HOME, + KEY_POWER, + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, + KEY_WAKEUP +}; + +static int __must_check rkvr_keys_probe(struct hid_device *hdev) +{ + + struct device *dev = &hdev->dev; + struct input_dev *input = NULL; + int i, error = 0; + + input = devm_input_allocate_device(dev); + if (!input) { + hid_err(hdev, "input_allocate_device fail\n"); + return -ENOMEM; + } + input->name = "rkvr-keypad"; + input->phys = "rkvr-keys/input0"; + input->dev.parent = dev; + input->id.bustype = BUS_HOST; + input->id.vendor = 0x071b; + input->id.product = 0x3205; + input->id.version = 0x0001; + + for (i = 0; i < sizeof(key_codes) / sizeof(key_codes[0]); i++) { + hid_info(hdev, "input_set_capability %d\n", key_codes[i]); + input_set_capability(input, EV_KEY, key_codes[i]); + } + + error = input_register_device(input); + if (error) { + hid_err(hdev, "rkvr-s: Unable to register input device, error: %d\n", error); + return error; + } + hdev->hiddev = input; + + return 0; +} + +static inline int __must_check rkvr_hw_start(struct hid_device *hdev, unsigned int connect_mask) +{ + int ret = hdev->ll_driver->start(hdev); + + if (ret) + return ret; + ret = rkvr_connect(hdev); + if (ret) + hdev->ll_driver->stop(hdev); + + return ret; +} + +static void rkvr_disconnect(struct hid_device *hid) +{ + struct hidraw *hidraw = hid->hidraw; + + mutex_lock(&minors_lock); + /* always unregistering inv_hid_device when hardware disconnect */ + inv_hid_unregister_and_destroy_devcie_by_name("hid-rkvr"); +#if DEBUG_SYS + device_remove_file(hidraw->dev, &dev_attr_debug); +#endif + + device_destroy(rkvr_class, MKDEV(rkvr_major, hidraw->minor)); + rkvr_drop_ref(hidraw, 1); + mutex_unlock(&minors_lock); +} + +static void rkvr_hw_stop(struct hid_device *hdev) +{ + rkvr_disconnect(hdev); + hdev->ll_driver->stop(hdev); +} + +static long rkvr_hidraw_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct inode *inode = file_inode(file); + unsigned int minor = iminor(inode); + long ret = 0; + struct hidraw *dev; + void __user *user_arg = (void __user *)arg; + + mutex_lock(&minors_lock); + dev = rkvr_hidraw_table[minor]; + if (!dev) { + ret = -ENODEV; + goto out; + } + + switch (cmd) { + case HIDIOCGRDESCSIZE: + if (put_user(dev->hid->rsize, (int __user *)arg)) + ret = -EFAULT; + break; + + case HIDIOCGRDESC: + { + __u32 len; + + if (get_user(len, (int __user *)arg)) + ret = -EFAULT; + else if (len > HID_MAX_DESCRIPTOR_SIZE - 1) + ret = -EINVAL; + else if (copy_to_user(user_arg + offsetof( + struct hidraw_report_descriptor, + value[0]), + dev->hid->rdesc, + min(dev->hid->rsize, len))) + ret = -EFAULT; + break; + } + case HIDIOCGRAWINFO: + { + struct hidraw_devinfo dinfo; + + dinfo.bustype = dev->hid->bus; + dinfo.vendor = dev->hid->vendor; + dinfo.product = dev->hid->product; + if (copy_to_user(user_arg, &dinfo, sizeof(dinfo))) + ret = -EFAULT; + break; + } + default: + { + struct hid_device *hid = dev->hid; + + if (_IOC_TYPE(cmd) != 'H') { + ret = -EINVAL; + break; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) { + int len = _IOC_SIZE(cmd); + + ret = rkvr_hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) { + int len = _IOC_SIZE(cmd); + + ret = rkvr_hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + + /* Begin Read-only ioctls. */ + if (_IOC_DIR(cmd) != _IOC_READ) { + ret = -EINVAL; + break; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) { + int len = strlen(hid->name) + 1; + + if (len > _IOC_SIZE(cmd)) + len = _IOC_SIZE(cmd); + ret = copy_to_user(user_arg, hid->name, len) ? + -EFAULT : len; + break; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) { + int len = strlen(hid->phys) + 1; + + if (len > _IOC_SIZE(cmd)) + len = _IOC_SIZE(cmd); + ret = copy_to_user(user_arg, hid->phys, len) ? + -EFAULT : len; + break; + } + } + + ret = -ENOTTY; + } +out: + mutex_unlock(&minors_lock); + return ret; +} + +static const struct file_operations rkvr_ops = { + .owner = THIS_MODULE, + .read = rkvr_hidraw_read, + .write = rkvr_hidraw_write, + .poll = rkvr_hidraw_poll, + .open = rkvr_hidraw_open, + .release = rkvr_hidraw_release, + .unlocked_ioctl = rkvr_hidraw_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rkvr_hidraw_ioctl, +#endif + .fasync = rkvr_hidraw_fasync, + .llseek = noop_llseek, +}; + +int rkvr_sensor_register_callback(int (*callback)(char *, size_t, void *), void *priv) +{ + sensorData.priv = priv; + sensorData.send_event = callback; + + return 0; +} +EXPORT_SYMBOL_GPL(rkvr_sensor_register_callback); + +static int rkvr_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int retval; + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "rkvr - parse failed\n"); + goto exit; + } + hid_set_drvdata(hdev, &sensorData); + if (intf->cur_altsetting->desc.bInterfaceNumber == RKVR_INTERFACE_USB_SENSOR_ID) { + retval = rkvr_keys_probe(hdev); + if (retval) { + hid_err(hdev, "rkvr_keys_probe failed\n"); + goto exit_stop; + } + retval = rkvr_hw_start(hdev, 0); + if (retval) { + hid_err(hdev, "rkvr - rkvr hw start failed\n"); + rkvr_keys_remove(hdev); + goto exit_stop; + } + } else { + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "rkvr - hid hw start failed\n"); + goto exit; + } + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void rkvr_remove(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + if (intf->cur_altsetting->desc.bInterfaceNumber == RKVR_INTERFACE_USB_SENSOR_ID) { + rkvr_keys_remove(hdev); + rkvr_hw_stop(hdev); + } else { + hid_hw_stop(hdev); + } +} + +static int rkvr_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) +{ + int retval = 0; + static unsigned int count; + static unsigned long old_jiffy; + + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + if (intf->cur_altsetting->desc.bInterfaceNumber != RKVR_INTERFACE_USB_SENSOR_ID) { + hid_info(hdev, "%s,ignored interface number is %d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + return 0; + } + + /* print sensor poll frequency */ + if (++count >= 1000) { + unsigned long cur_jiffy = jiffies; + + hid_info(hdev, "rkvr: %d Hz, hidrkvr %d\n", (int)(1000 * HZ / (cur_jiffy - old_jiffy)), (hdev->hidraw ? 1 : 0)); + count = 0; + old_jiffy = cur_jiffy; + } + + if (hdev->hidraw || hdev->hiddev) { + retval = rkvr_report_event(hdev, data, size); + if (retval < 0) + hid_info(hdev, "rkvr: raw event err %d\n", retval); + } + + return retval; +} + +static const struct hid_device_id rkvr_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCKCHIP, USB_DEVICE_ID_NANOC) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, rkvr_devices); + +static struct hid_driver rkvr_driver = { + .name = "rkvr", + .id_table = rkvr_devices, + .probe = rkvr_probe, + .remove = rkvr_remove, + .raw_event = rkvr_raw_event +}; + +static int __init rkvr_init(void) +{ + int retval; + dev_t dev_id; + + rkvr_class = class_create(THIS_MODULE, "rkvr"); + if (IS_ERR(rkvr_class)) + return PTR_ERR(rkvr_class); + + retval = hid_register_driver(&rkvr_driver); + if (retval < 0) { + pr_warn("rkvr_init - Can't register drive.\n"); + goto out_class; + } + + retval = alloc_chrdev_region(&dev_id, RKVR_FIRST_MINOR, + RKVR_HIDRAW_MAX_DEVICES, "rkvr"); + if (retval < 0) { + pr_warn("rkvr_init - Can't allocate chrdev region.\n"); + goto out_register; + } + + rkvr_major = MAJOR(dev_id); + cdev_init(&rkvr_cdev, &rkvr_ops); + cdev_add(&rkvr_cdev, dev_id, RKVR_HIDRAW_MAX_DEVICES); + return 0; + +out_register: + hid_unregister_driver(&rkvr_driver); +out_class: + class_destroy(rkvr_class); + + return retval; +} + +static void __exit rkvr_exit(void) +{ + dev_t dev_id = MKDEV(rkvr_major, 0); + + cdev_del(&rkvr_cdev); + + unregister_chrdev_region(dev_id, RKVR_HIDRAW_MAX_DEVICES); + + hid_unregister_driver(&rkvr_driver); + class_destroy(rkvr_class); +} + +module_init(rkvr_init); +module_exit(rkvr_exit); + +MODULE_AUTHOR("zwp"); +MODULE_DESCRIPTION("USB ROCKCHIP VR char device driver."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-rkvr.h b/drivers/hid/hid-rkvr.h new file mode 100644 index 000000000000..df2778fc803d --- /dev/null +++ b/drivers/hid/hid-rkvr.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 ROCKCHIP, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ +#ifndef __HID_RKVR_H +#define __HID_RKVR_H + +enum tracker_message_type { + TrackerMessage_None = 0, + TrackerMessage_Sensors = 1, + TrackerMessage_Unknown = 0x100, + TrackerMessage_SizeError = 0x101, +}; + +#define DEBUG_SYS 1 + +#define DYNAMIC_LOAD_MPU6500 0 + +int rkvr_sensor_register_callback(int (*callback)(char *, size_t, void *), void *priv); + +struct rkvr_iio_hw_device { + struct device *dev; + const char *name; + int is_open; + struct list_head l; + int (*open)(struct rkvr_iio_hw_device *hdev); + void (*close)(struct rkvr_iio_hw_device *hdev); + int (*power)(struct rkvr_iio_hw_device *hdev, int level); + int (*idle)(struct rkvr_iio_hw_device *hdev, int report, int idle, int reqtype); + int (*read)(struct rkvr_iio_hw_device *hdev, int reg, unsigned char *data, int len); + int (*write)(struct rkvr_iio_hw_device *hdev, int reg, unsigned char data); +}; + +#endif -- 2.34.1