android_kernel_lenovo_1050f/drivers/misc/heci/heci-hid.c

284 lines
6.8 KiB
C

/*
* HECI-HID glue driver.
*
* Copyright (c) 2012-2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/kthread.h>
#include "heci-hid.h"
#include "platform-config.h"
/* TODO - figure out if this number is used for anything but assignment. BUS_I2C is not */
#define BUS_HECI 0x44
/* TODO: just to bootstrap, numbers will probably change */
#define ISH_HID_VENDOR 0x8086
#define ISH_HID_PRODUCT 0x22D8
#define ISH_HID_VERSION 0x0200
extern unsigned char *report_descr[MAX_HID_DEVICES];
extern int report_descr_size[MAX_HID_DEVICES];
extern struct device_info *hid_devices;
extern int may_send;
extern int get_report_done; /* Get Feature/Input report complete flag */
extern unsigned cur_hid_dev;
extern struct hid_device *hid_sensor_hubs[MAX_HID_DEVICES];
extern unsigned num_hid_devices;
extern struct heci_cl *ish_heci_cl; /* HECI client */
int ish_heci_cl_send(struct heci_cl *cl, u8 *buf, size_t length, bool blocking);
static int heci_hid_parse(struct hid_device *hid)
{
int rv;
rv = hid_parse_report(hid, report_descr[cur_hid_dev], report_descr_size[cur_hid_dev]);
if (rv)
return rv;
return 0;
}
static int heci_hid_start(struct hid_device *hid)
{
return 0;
}
/* should we free smth? */
static void heci_hid_stop(struct hid_device *hid)
{
return;
}
/* probably connect might be here (move from probe) */
static int heci_hid_open(struct hid_device *hid)
{
return 0;
}
/* naturally if connect in open, disconnect here */
static void heci_hid_close(struct hid_device *hid)
{
return;
}
static int heci_hid_power(struct hid_device *hid, int lvl)
{
return 0;
}
static void heci_set_feature(struct hid_device *hid, char *buf,
unsigned len, int report_id)
{
int rv;
struct hostif_msg *msg = (struct hostif_msg *)buf;
int i;
memset(msg, 0, sizeof(struct hostif_msg));
msg->hdr.command = HOSTIF_SET_FEATURE_REPORT;
for (i = 0; i < num_hid_devices; ++i)
if (hid == hid_sensor_hubs[i]) {
/* FIXME- temporary when single collection exists,
* then has to be part of hid_device custom fields
*/
msg->hdr.device_id = hid_devices[i].dev_id;
break;
}
if (i == num_hid_devices)
return;
#ifdef HOST_VIRTUALBOX
timed_wait_for(WAIT_FOR_SEND_SLICE, may_send);
#else
timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, may_send, (5*HZ));
#endif
rv = ish_heci_cl_send(ish_heci_cl, buf, len, 0); /* Non-Blocking */
}
static void heci_get_report(struct hid_device *hid,
int report_id, int report_type)
{
int rv;
static unsigned char buf[5];
unsigned len;
struct hostif_msg_to_sensor *msg =
(struct hostif_msg_to_sensor *)buf;
int i;
len = sizeof(struct hostif_msg_to_sensor);
/* Send HOSTIF_DM_ENUM_DEVICES and receive response in kthread_read */
memset(msg, 0, sizeof(struct hostif_msg_to_sensor));
msg->hdr.command = (report_type == HID_FEATURE_REPORT) ? HOSTIF_GET_FEATURE_REPORT : HOSTIF_GET_INPUT_REPORT;
for (i = 0; i < num_hid_devices; ++i)
if (hid == hid_sensor_hubs[i]) {
/* FIXME - temporary when single collection exists,
* then has to be part of hid_device custom fields
*/
msg->hdr.device_id = hid_devices[i].dev_id;
break;
}
if (i == num_hid_devices)
return;
msg->report_id = report_id;
#ifdef HOST_VIRTUALBOX
timed_wait_for(WAIT_FOR_SEND_SLICE, may_send);
#else
timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, may_send, (5*HZ));
#endif
rv = ish_heci_cl_send(ish_heci_cl, buf, len, 0); /* Non-Blocking */
}
static void heci_hid_request(struct hid_device *hid,
struct hid_report *rep, int reqtype)
{
/* this is specific report length, just HID part of it */
unsigned len = ((rep->size - 1) >> 3) + 1 + (rep->id > 0);
char *buf;
/* s32 checkValue = 0; */
/* int i = 0; */
unsigned header_size = sizeof(struct hostif_msg);
len += header_size;
switch (reqtype) {
case HID_REQ_GET_REPORT:
heci_get_report(hid, rep->id, rep->type);
break;
case HID_REQ_SET_REPORT:
buf = kzalloc(len, GFP_KERNEL);
hid_output_report(rep, buf + header_size);
/* checkValue = rep->field[3]->value[0]; */
/* for(;i < len; i++) */
heci_set_feature(hid, buf, len, rep->id);
break;
}
return;
}
static int heci_hid_hidinput_input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
return 0;
}
static int heci_wait_for_response(struct hid_device *hid)
{
timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, get_report_done, (10 * HZ));
if (!get_report_done) {
dbg_hid("timeout waiting for heci device\n");
return -1;
}
return 0;
}
static struct hid_ll_driver heci_hid_ll_driver = {
.parse = heci_hid_parse,
.start = heci_hid_start,
.stop = heci_hid_stop,
.open = heci_hid_open,
.close = heci_hid_close,
.power = heci_hid_power,
.request = heci_hid_request,
.hidinput_input_event = heci_hid_hidinput_input_event,
.wait = heci_wait_for_response
};
struct tmp_heci_data {
int hdesc_length;
struct task_struct *read_task;
};
static struct tmp_heci_data thd;
static int heci_hid_get_raw_report(struct hid_device *hid,
unsigned char report_number, __u8 *buf,
size_t count, unsigned char report_type)
{
return 0;
}
static int heci_hid_output_raw_report(struct hid_device *hid,
__u8 *buf, size_t count, unsigned char report_type)
{
return 0;
}
static void i2c_hid_request(struct hid_device *hid,
struct hid_report *rep, int reqtype)
{
}
/* probably the best way make it driver probe so it will create
* device with itself as ll_driver, as usb and i2c do
*/
int heci_hid_probe(unsigned cur_hid_dev)
{
int rv;
struct hid_device *hid;
hid = hid_allocate_device();
if (IS_ERR(hid)) {
rv = PTR_ERR(hid);
return -ENOMEM;
}
hid->ll_driver = &heci_hid_ll_driver;
hid->hid_get_raw_report = heci_hid_get_raw_report;
hid->hid_output_raw_report = heci_hid_output_raw_report;
hid->bus = BUS_HECI;
hid->version = le16_to_cpu(ISH_HID_VERSION);
hid->vendor = le16_to_cpu(ISH_HID_VENDOR);
hid->product = le16_to_cpu(ISH_HID_PRODUCT);
snprintf(hid->name, sizeof(hid->name),
"%s %04hX:%04hX", "hid-heci", hid->vendor, hid->product);
rv = hid_add_device(hid);
if (rv) {
if (rv != -ENODEV)
dev_err(&hid->dev, "can't add hid device: %d\n", rv);
kfree(hid);
return rv;
}
hid_sensor_hubs[cur_hid_dev] = hid;
return 0;
}
void heci_hid_remove(void)
{
int rv;
int i;
for (i = 0; i < num_hid_devices; ++i)
if (hid_sensor_hubs[i]) {
hid_destroy_device(hid_sensor_hubs[i]);
hid_sensor_hubs[i] = NULL;
}
}