From c5c3cda57ce7b7edeccc84c8c59615b147752566 Mon Sep 17 00:00:00 2001 From: lyz Date: Tue, 22 Oct 2013 21:11:07 +0800 Subject: [PATCH] add function hidg to android gadget --- drivers/hid/hid-core.c | 27 +- drivers/usb/gadget/Kconfig | 8 + drivers/usb/gadget/android.c | 165 +++++- drivers/usb/gadget/f_hid_rk.c | 978 ++++++++++++++++++++++++++++++++++ 4 files changed, 1174 insertions(+), 4 deletions(-) mode change 100644 => 100755 drivers/hid/hid-core.c mode change 100644 => 100755 drivers/usb/gadget/android.c create mode 100755 drivers/usb/gadget/f_hid_rk.c diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c old mode 100644 new mode 100755 index 53576e7c8bcf..290ded561974 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -37,6 +37,12 @@ #include "hid-ids.h" +#ifdef CONFIG_BYPASS_INPUT_TO_HIDG +extern f_hid_bypass_input_get(); +extern f_hid_kbd_translate_report(u8 * data, int len); +extern f_hid_mouse_translate_report(struct hid_report *report , u8 *data); +#endif + /* * Version Information */ @@ -900,7 +906,6 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, __s32 min = field->logical_minimum; __s32 max = field->logical_maximum; __s32 *value; - value = kmalloc(sizeof(__s32) * count, GFP_ATOMIC); if (!value) return; @@ -918,6 +923,11 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) goto exit; } + +#ifdef CONFIG_BYPASS_INPUT_TO_HIDG + if((hid->type == 1) && (f_hid_bypass_input_get() == 1)) + goto memcpy;//bypass mouse report +#endif for (n = 0; n < count; n++) { @@ -936,8 +946,9 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, && search(field->value, value[n], count)) hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt); } - +memcpy: memcpy(field->value, value, count * sizeof(__s32)); + exit: kfree(value); } @@ -1058,10 +1069,20 @@ void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, hid->hiddev_report_event(hid, report); if (hid->claimed & HID_CLAIMED_HIDRAW) hidraw_report_event(hid, data, size); - for (a = 0; a < report->maxfield; a++) hid_input_field(hid, report->field[a], cdata, interrupt); +#ifdef CONFIG_BYPASS_INPUT_TO_HIDG + + if((hid->type == 0) && (size == 8))//kbd + { + f_hid_kbd_translate_report(cdata, csize); + } + else if(hid->type == 1)//mouse + { + f_hid_mouse_translate_report(report, cdata); + } +#endif if (hid->claimed & HID_CLAIMED_INPUT) hidinput_report_event(hid, report); } diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index e222aceb3251..49d5f1d2930d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -1079,4 +1079,12 @@ config USB_G_WEBCAM endchoice +config BYPASS_INPUT_TO_HIDG + bool "Allow send reports form HID devices to PC" + depends on (USB_G_ANDROID && USB_GADGET_DWC_OTG) + default n + help + If say "y" there will create a new gadget HIDG + and HID reports form HID devices will be bypass to PC + endif # USB_GADGET diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c old mode 100644 new mode 100755 index a9c3f78c00f3..1d8893ec3ed9 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -57,6 +57,10 @@ #include "rndis.c" #include "u_ether.c" +#ifdef CONFIG_BYPASS_INPUT_TO_HIDG +#include "f_hid_rk.c" +#endif + MODULE_AUTHOR("Mike Lockwood"); MODULE_DESCRIPTION("Android Composite USB Driver"); MODULE_LICENSE("GPL"); @@ -808,6 +812,160 @@ static struct android_usb_function audio_source_function = { .attributes = audio_source_function_attributes, }; +#ifdef CONFIG_BYPASS_INPUT_TO_HIDG +/* hid descriptor for a keyboard */ +const struct hidg_func_descriptor my_hid_data = { + .subclass = 1, /* No subclass */ + .protocol = 2, /* 1-Keyboard,2-mouse */ + .report_length = 64, + .report_desc_length = 131, + .report_desc = { + + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x06, /* USAGE (Keyboard) */ + 0xA1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x01, /* REPORT ID (0x01) */ + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ + 0x19, 0xE0, /* USAGE_MINIMUM (Keyboard LeftControl) */ + 0x29, 0xE7, /* USAGE_MAXIMUM (Keyboard Right GUI) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ + 0x95, 0x05, /* REPORT_COUNT (5) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x05, 0x08, /* USAGE_PAGE (LEDs) */ + 0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */ + 0x29, 0x05, /* USAGE_MAXIMUM (Kana) */ + 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x03, /* REPORT_SIZE (3) */ + 0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */ + 0x95, 0x06, /* REPORT_COUNT (6) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x65, /* LOGICAL_MAXIMUM (101) */ + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ + 0x19, 0x00, /* USAGE_MINIMUM (Reserved) */ + 0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */ + 0x81, 0x00, /* INPUT (Data,Ary,Abs) */ + 0xC0, /* END_COLLECTION */ + + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x02, /* USAGE (Mouse) */ + 0xA1, 0x01, /* COLLECTION (Application) */ + 0x85, 0x02, /* REPORT ID (0x02) */ + 0x09, 0x01, /* USAGE (Pointer) */ + + 0xA1, 0x00, /* COLLECTION (Application) */ + 0x05, 0x09, /* USAGE_PAGE (Button) */ + 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ + 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x95, 0x03, /* REPORT_COUNT (3) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0x75, 0x05, /* REPORT_SIZE (5) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x81, 0x01, /* INPUT (Cnst,Var,Abs) */ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x30, /* USAGE (X) */ + 0x09, 0x31, /* USAGE (Y) */ + 0x16, 0x01, 0xF8, /* LOGICAL_MINIMUM (-2047) */ + 0x26, 0xFF, 0x07, /* LOGICAL_MAXIMUM (2047) */ + 0x75, 0x0C, /* REPORT_SIZE (12) */ + 0x95, 0x02, /* REPORT_COUNT (2) */ + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ + 0x09, 0x38, + 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ + 0x25, 0x7F, /* LOGICAL_MAXIMUM (127) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x81, 0x06, /* INPUT (Data,Var,Rel) */ + 0xC0 , /* END_COLLECTION */ + + 0xC0 /* END_COLLECTION */ + + }, +}; + +static int hidg_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + ghid_setup(cdev->gadget, 1); + return 0; +} + +static void hidg_function_cleanup(struct android_usb_function *f) +{ + ghid_cleanup(); + return; +} + +static int hidg_function_ctrlrequest(struct android_usb_function *f, + struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *c) +{ + return hidg_ctrlrequest(cdev, c); +} + +static int hidg_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + if(my_hid_data.report_desc_length) + hidg_bind_config(c, &my_hid_data, 0); + return 0; +} +static int hidg_function_unbind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + return 0; +} +static ssize_t hidg_report_descriptor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "hid report_desc_length = %d\n", my_hid_data.report_desc_length); +} + +static DEVICE_ATTR(report_descriptor, S_IRUGO | S_IWUSR, hidg_report_descriptor_show, NULL); + +static ssize_t hidg_bypass_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf," %s \n" , + f_hid_bypass_input_get()? "Input report bypass enable" : "Input report bypass disable"); +} + +static ssize_t hidg_bypass_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int bypass; + sscanf(buf, "%d", &bypass); + f_hid_bypass_input_set(bypass); + return size; +} + +static DEVICE_ATTR(bypass_input, S_IRUGO | S_IWUSR, hidg_bypass_show, hidg_bypass_store); + +static struct device_attribute *hidg_function_attributes[] = + {&dev_attr_bypass_input ,&dev_attr_report_descriptor ,NULL }; + + +static struct android_usb_function hidg_function = { + .name = "hidg", + .init = hidg_function_init, + .cleanup = hidg_function_cleanup, + .bind_config = hidg_function_bind_config, + .unbind_config = hidg_function_unbind_config, + .ctrlrequest = hidg_function_ctrlrequest, + .attributes = hidg_function_attributes, +}; +#endif static struct android_usb_function *supported_functions[] = { &adb_function, &acm_function, @@ -817,10 +975,12 @@ static struct android_usb_function *supported_functions[] = { &mass_storage_function, &accessory_function, &audio_source_function, +#ifdef CONFIG_BYPASS_INPUT_TO_HIDG + &hidg_function, +#endif NULL }; - static int android_init_functions(struct android_usb_function **functions, struct usb_composite_dev *cdev) { @@ -1277,6 +1437,9 @@ static void android_disconnect(struct usb_gadget *gadget) so we need to inform it when we are disconnected. */ acc_disconnect(); +#ifdef CONFIG_BYPASS_INPUT_TO_HIDG + hidg_disconnect(); +#endif spin_lock_irqsave(&cdev->lock, flags); dev->connected = 0; diff --git a/drivers/usb/gadget/f_hid_rk.c b/drivers/usb/gadget/f_hid_rk.c new file mode 100755 index 000000000000..6e3ac061cf9e --- /dev/null +++ b/drivers/usb/gadget/f_hid_rk.c @@ -0,0 +1,978 @@ +/* + * f_hid.c -- USB HID function driver + * + * Copyright (C) 2010 Fabien Chouteau + * + * 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. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +static int major, minors; +static struct class *hidg_class; + +/*-------------------------------------------------------------------------*/ +/* HID gadget struct */ + +struct f_hidg { + /* configuration */ + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned short report_desc_length; + char *report_desc; + unsigned short report_length; + + /* recv report */ + char *set_report_buff; + unsigned short set_report_length; + spinlock_t spinlock; + wait_queue_head_t read_queue; + struct usb_request *req_out; + + /* send report */ + struct mutex lock; + bool write_pending; + wait_queue_head_t write_queue; + struct usb_request *req; + + int minor; + struct cdev cdev; + struct usb_function func; + struct usb_ep *in_ep; + struct usb_endpoint_descriptor *fs_in_ep_desc; + struct usb_endpoint_descriptor *hs_in_ep_desc; + struct usb_endpoint_descriptor *fs_out_ep_desc; + struct usb_endpoint_descriptor *hs_out_ep_desc; + + unsigned short bypass_input; + bool connected; +}; + +static inline struct f_hidg *func_to_hidg(struct usb_function *f) +{ + return container_of(f, struct f_hidg, func); +} + +/*-------------------------------------------------------------------------*/ +/* Static descriptors */ + +static struct usb_interface_descriptor hidg_interface_desc = { + .bLength = sizeof hidg_interface_desc, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + /* .bInterfaceSubClass = DYNAMIC */ + /* .bInterfaceProtocol = DYNAMIC */ + /* .iInterface = DYNAMIC */ +}; + +static struct hid_descriptor hidg_desc = { + .bLength = sizeof hidg_desc, + .bDescriptorType = HID_DT_HID, + .bcdHID = 0x0110, //0x0101, + .bCountryCode = 0x00, + .bNumDescriptors = 0x1, + /*.desc[0].bDescriptorType = DYNAMIC */ + /*.desc[0].wDescriptorLenght = DYNAMIC */ +}; + +/* High-Speed Support */ + +static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 4, /* FIXME: Add this field in the + * HID gadget configuration? + * (struct hidg_func_descriptor) + */ +}; +#if 0 +static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT|0x02, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 4, /* FIXME: Add this field in the + * HID gadget configuration? + * (struct hidg_func_descriptor) + */ +}; +#endif + +static struct usb_descriptor_header *hidg_hs_descriptors[] = { + (struct usb_descriptor_header *)&hidg_interface_desc, + (struct usb_descriptor_header *)&hidg_desc, + (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, + //(struct usb_descriptor_header *)&hidg_hs_out_ep_desc, + NULL, +}; + +/* Full-Speed Support */ + +static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 10, /* FIXME: Add this field in the + * HID gadget configuration? + * (struct hidg_func_descriptor) + */ +}; +#if 0 +static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT|0x02, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 10, /* FIXME: Add this field in the + * HID gadget configuration? + * (struct hidg_func_descriptor) + */ +}; +#endif +static struct usb_descriptor_header *hidg_fs_descriptors[] = { + (struct usb_descriptor_header *)&hidg_interface_desc, + (struct usb_descriptor_header *)&hidg_desc, + (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, + //(struct usb_descriptor_header *)&hidg_fs_out_ep_desc, + NULL, +}; + +struct f_hidg *g_hidg; + +/*-------------------------------------------------------------------------*/ +/* Char Device */ + +static ssize_t f_hidg_read(struct file *file, char __user *buffer, + size_t count, loff_t *ptr) +{ + struct f_hidg *hidg = file->private_data; + char *tmp_buff = NULL; + unsigned long flags; + + if (!count) + return 0; + + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + spin_lock_irqsave(&hidg->spinlock, flags); + +#define READ_COND (hidg->set_report_buff != NULL) + + while (!READ_COND) { + spin_unlock_irqrestore(&hidg->spinlock, flags); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible(hidg->read_queue, READ_COND)) + return -ERESTARTSYS; + + spin_lock_irqsave(&hidg->spinlock, flags); + } + + + count = min_t(unsigned, count, hidg->set_report_length); + tmp_buff = hidg->set_report_buff; + hidg->set_report_buff = NULL; + + spin_unlock_irqrestore(&hidg->spinlock, flags); + + if (tmp_buff != NULL) { + /* copy to user outside spinlock */ + count -= copy_to_user(buffer, tmp_buff, count); + kfree(tmp_buff); + } else + count = -ENOMEM; + + return count; +} + +static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; + + if (req->status != 0) { + //ERROR(hidg->func.config->cdev, + // "End Point Request ERROR: %d\n", req->status); + } + + hidg->write_pending = 0; + wake_up(&hidg->write_queue); +} + +#define WRITE_COND (!hidg->write_pending) +static ssize_t f_hidg_write(struct file *file, const char __user *buffer, + size_t count, loff_t *offp) +{ +#if 0 + struct f_hidg *hidg = file->private_data; + ssize_t status = -ENOMEM; + + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + + mutex_lock(&hidg->lock); + +#define WRITE_COND (!hidg->write_pending) + + /* write queue */ + while (!WRITE_COND) { + mutex_unlock(&hidg->lock); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible_exclusive( + hidg->write_queue, WRITE_COND)) + return -ERESTARTSYS; + + mutex_lock(&hidg->lock); + } + + count = min_t(unsigned, count, hidg->report_length); + status = copy_from_user(hidg->req->buf, buffer, count); + + if (status != 0) { + //ERROR(hidg->func.config->cdev, + // "copy_from_user error\n"); + mutex_unlock(&hidg->lock); + return -EINVAL; + } + + hidg->req->status = 0; + hidg->req->zero = 0; + hidg->req->length = count; + hidg->req->complete = f_hidg_req_complete; + hidg->req->context = hidg; + hidg->write_pending = 1; + + status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC); + if (status < 0) { + //ERROR(hidg->func.config->cdev, + // "usb_ep_queue error on int endpoint %zd\n", status); + hidg->write_pending = 0; + wake_up(&hidg->write_queue); + } else { + status = count; + } + + mutex_unlock(&hidg->lock); +#endif + return count; +} +static void f_hid_queue_report(u8 *data, int len) +{ + //this function will run in interrupt context + ssize_t status = -ENOMEM; + struct f_hidg *hidg = g_hidg; + //static char raw_report[8]; + + if(hidg){ + if(hidg->connected){ + //mutex_lock(&hidg->lock); + memcpy(hidg->req->buf, data, len); + hidg->req->status = 0; + hidg->req->zero = 0; + hidg->req->length = len; + //hidg->req->buf = raw_report; + hidg->req->complete = f_hidg_req_complete; + hidg->req->context = hidg; + + status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC); + if (status < 0) { + printk("usb_ep_queue error on int endpoint %zd\n", status); + } + } + //mutex_unlock(&hidg->lock); + } +} + + +#define KBD_REPORT_ID (0x01) +#define MOUSE_REPORT_ID (0x02) + +unsigned int f_hid_bypass_input_get() +{ + if(!g_hidg) + return 0; + else + return g_hidg->bypass_input; +} +EXPORT_SYMBOL(f_hid_bypass_input_get); + +unsigned char kbd_idle[] = {KBD_REPORT_ID,0,0,0,0,0,0,0,0}; +unsigned char mouse_idle[] = {MOUSE_REPORT_ID,0,0,0,0,0}; + +static void f_hid_send_idle_report(void) +{ + if(g_hidg){ + mdelay(2); + f_hid_queue_report(kbd_idle, sizeof(kbd_idle)); + mdelay(2); + f_hid_queue_report(mouse_idle, sizeof(mouse_idle)); + } +} + +static void f_hid_bypass_input_set(u8 bypass) +{ + if(g_hidg){ + + u8 current_state = f_hid_bypass_input_get(); + + if( bypass && (!current_state)) + { + g_hidg->bypass_input = 1; + } + if(!bypass && (current_state)) + { + f_hid_send_idle_report(); + g_hidg->bypass_input = 0; + } + } +} + +struct kbd_report { + u8 id:8; + u8 raw_report[8]; +}__attribute__ ((packed)); + +void f_hid_kbd_translate_report(u8 *data, int len) +{ + if(f_hid_bypass_input_get()) + { + struct kbd_report k = {0}; + k.id = KBD_REPORT_ID;//report id + memcpy(k.raw_report, data, len);//Byte 1 for report id + + f_hid_queue_report((u8 *)&k, sizeof(k)); + } +} +EXPORT_SYMBOL(f_hid_kbd_translate_report); + +struct mouse_report { + u8 id:8; + u8 button:8; + signed x :12; + signed y :12; + s8 wheel:8; +}__attribute__ ((packed)); + + +void f_hid_mouse_translate_report(struct hid_report *report , u8 *data) +{ + + if(f_hid_bypass_input_get()) + { + struct mouse_report m = {0}; + struct hid_field *field; + + s32 mouse_rel[2] = { 0 };//0 for x, 1 for y + s32 mouse_wheel = 0; + u8 mouse_button = 0;//bit[2:0] is valid, [7:3] reserved + + int i,j; + + for (i = 0; i < report->maxfield; i++){ + field = report->field[i]; + if(field->usage->type == EV_KEY && field->usage->code == BTN_MOUSE) //mouse button + { + for(j=0; j < field->report_count; j++) + { + if(field->value[j]) + mouse_button |= 1 << j; + } + } + if(field->usage->type == EV_REL && field->usage->code == 0) //mouse rel location + { + for(j=0; j < field->report_count ;j++) + { + mouse_rel[j] = field->value[j]; + } + } + if(field->usage->type == EV_REL && field->usage->code == REL_WHEEL) //mouse wheel + { + mouse_wheel = field->value[0]; + } + + } + + /*** generate HID report for hidg ***/ + m.id = MOUSE_REPORT_ID; + m.button = mouse_button; + m.x = mouse_rel[0]; + m.y = mouse_rel[1]; + m.wheel = mouse_wheel; + + f_hid_queue_report((u8 *)&m, sizeof(m)); + } +} +EXPORT_SYMBOL(f_hid_mouse_translate_report); + +#undef KBD_REPORT_ID +#undef MOUSE_REPORT_ID + +static unsigned int f_hidg_poll(struct file *file, poll_table *wait) +{ + struct f_hidg *hidg = file->private_data; + unsigned int ret = 0; + + poll_wait(file, &hidg->read_queue, wait); + poll_wait(file, &hidg->write_queue, wait); + + if (WRITE_COND) + ret |= POLLOUT | POLLWRNORM; + + if (READ_COND) + ret |= POLLIN | POLLRDNORM; + + return ret; +} + +#undef WRITE_COND +#undef READ_COND + +static int f_hidg_release(struct inode *inode, struct file *fd) +{ + fd->private_data = NULL; + return 0; +} + +static int f_hidg_open(struct inode *inode, struct file *fd) +{ + struct f_hidg *hidg = + container_of(inode->i_cdev, struct f_hidg, cdev); + + fd->private_data = hidg; + + return 0; +} + +/*-------------------------------------------------------------------------*/ +/* usb_function */ + +void hidg_connect() +{ + if(g_hidg) + g_hidg->connected = 1; +} + +void hidg_disconnect() +{ + if(g_hidg){ + g_hidg->connected = 0; + } +} +DECLARE_DELAYED_WORK(hidg_cnt, hidg_connect); + +static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_hidg *hidg = (struct f_hidg *)req->context; + printk("hidg_set_report_complete ,req->status = %d len = %d\n", + req->status,req->actual); + if (req->status != 0 || req->buf == NULL || req->actual == 0) { + return; + } + + spin_lock(&hidg->spinlock); + + if(!hidg->connected) + //schedule_delayed_work(&hidg_cnt, msecs_to_jiffies(200)); + hidg_connect(); + + hidg->set_report_buff = krealloc(hidg->set_report_buff, + req->actual, GFP_ATOMIC); + + if (hidg->set_report_buff == NULL) { + spin_unlock(&hidg->spinlock); + return; + } + hidg->set_report_length = req->actual; + memcpy(hidg->set_report_buff, req->buf, req->actual); + + spin_unlock(&hidg->spinlock); + + wake_up(&hidg->read_queue); +} + +static int hidg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_hidg *hidg = func_to_hidg(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int status = 0; + __u16 value, length; + + value = __le16_to_cpu(ctrl->wValue); + length = __le16_to_cpu(ctrl->wLength); + + VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x " + "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value); + + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_GET_REPORT): + VDBG(cdev, "get_report\n"); + + /* send an empty report */ + length = min_t(unsigned, length, hidg->report_length); + memset(req->buf, 0x0, length); + + goto respond; + break; + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_GET_PROTOCOL): + VDBG(cdev, "get_protocol\n"); + goto stall; + break; + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_SET_REPORT): + VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength); + req->context = hidg; + req->complete = hidg_set_report_complete; + goto respond; + break; + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_SET_PROTOCOL): + VDBG(cdev, "set_protocol\n"); + goto stall; + break; + + case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8 + | USB_REQ_GET_DESCRIPTOR): + switch (value >> 8) { + case HID_DT_REPORT: + VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n"); + length = min_t(unsigned short, length, + hidg->report_desc_length); + memcpy(req->buf, hidg->report_desc, length); + goto respond; + break; + + default: + VDBG(cdev, "Unknown decriptor request 0x%x\n", + value >> 8); + goto stall; + break; + } + break; + + default: + VDBG(cdev, "Unknown request 0x%x\n", + ctrl->bRequest); + goto stall; + break; + } + +stall: + return -EOPNOTSUPP; + +respond: + req->zero = 0; + req->length = length; + status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (status < 0) + ;//ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value); + return status; +} + +static int hidg_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl) +{ + struct f_hidg *hidg = g_hidg; + struct usb_request *req = cdev->req; + int status = 0; + __u16 value, length; + + value = __le16_to_cpu(ctrl->wValue); + length = __le16_to_cpu(ctrl->wLength); + + /* + printk("hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x " + "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value); + */ + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_GET_REPORT): + VDBG(cdev, "get_report\n"); + + /* send an empty report */ + length = min_t(unsigned, length, hidg->report_length); + memset(req->buf, 0x0, length); + + goto respond; + break; + + case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_GET_PROTOCOL): + VDBG(cdev, "get_protocol\n"); + goto stall; + break; + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_SET_REPORT): + VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength); + req->context = hidg; + req->complete = hidg_set_report_complete; + goto respond; + break; + + case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 + | HID_REQ_SET_PROTOCOL): + VDBG(cdev, "set_protocol\n"); + goto stall; + break; + + case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8 + | USB_REQ_GET_DESCRIPTOR): + switch (value >> 8) { + case HID_DT_REPORT: + VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n"); + length = min_t(unsigned short, length, + hidg->report_desc_length); + memcpy(req->buf, hidg->report_desc, length); + goto respond; + break; + + default: + VDBG(cdev, "Unknown decriptor request 0x%x\n", + value >> 8); + goto stall; + break; + } + break; + + default: + VDBG(cdev, "Unknown request 0x%x\n", + ctrl->bRequest); + goto stall; + break; + } + +stall: + return -EOPNOTSUPP; + +respond: + req->zero = 0; + req->length = length; + status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (status < 0) + ;//ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value); + return status; +} + +static void hidg_disable(struct usb_function *f) +{ + struct f_hidg *hidg = func_to_hidg(f); + + usb_ep_disable(hidg->in_ep); + hidg->in_ep->driver_data = NULL; +} + +static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct f_hidg *hidg = func_to_hidg(f); + const struct usb_endpoint_descriptor *ep_desc; + int status = 0; + + VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); + + if (hidg->in_ep != NULL) { + /* restart endpoint */ + if (hidg->in_ep->driver_data != NULL) + usb_ep_disable(hidg->in_ep); + + ep_desc = ep_choose(f->config->cdev->gadget, + hidg->hs_in_ep_desc, hidg->fs_in_ep_desc); + status = usb_ep_enable(hidg->in_ep, ep_desc); + if (status < 0) { + //ERROR(cdev, "Enable endpoint FAILED!\n"); + goto fail; + } + hidg->in_ep->driver_data = hidg; + } +fail: + return status; +} + +const struct file_operations f_hidg_fops = { + .owner = THIS_MODULE, + .open = f_hidg_open, + .release = f_hidg_release, + .write = NULL,//f_hidg_write,disable write to /dev/hidg0 + .read = f_hidg_read, + .poll = f_hidg_poll, + .llseek = noop_llseek, +}; + +static int hidg_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_ep *ep_in; + struct f_hidg *hidg = func_to_hidg(f); + int status; + dev_t dev; + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + hidg_interface_desc.bInterfaceNumber = status; + + /* allocate instance-specific endpoints */ + status = -ENODEV; + ep_in = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc); + if (!ep_in) + goto fail; + ep_in->driver_data = c->cdev; /* claim */ + hidg->in_ep = ep_in; +#if 0 + /* allocate out endpoint*/ + ep_out = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); + if (!ep_out) + goto fail; + ep_out->driver_data = c->cdev; /* claim */ + hidg->out_ep = ep_out; + + printk("ep_out->name = %s\n",ep_out->name); +#endif + /* preallocate request and buffer */ + status = -ENOMEM; + hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); + if (!hidg->req) + goto fail; + + + hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL); + if (!hidg->req->buf) + goto fail; + + /* set descriptor dynamic values */ + hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; + hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; +// hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); +// hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); +// hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); +// hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); + hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT; + hidg_desc.desc[0].wDescriptorLength = + cpu_to_le16(hidg->report_desc_length); + + hidg->set_report_buff = NULL; + + /* copy descriptors */ + f->descriptors = usb_copy_descriptors(hidg_fs_descriptors); + if (!f->descriptors) + goto fail; + + hidg->fs_in_ep_desc = usb_find_endpoint(hidg_fs_descriptors, + f->descriptors, + &hidg_fs_in_ep_desc); + + if (gadget_is_dualspeed(c->cdev->gadget)) { + hidg_hs_in_ep_desc.bEndpointAddress = + hidg_fs_in_ep_desc.bEndpointAddress; + f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors); + if (!f->hs_descriptors) + goto fail; + hidg->hs_in_ep_desc = usb_find_endpoint(hidg_hs_descriptors, + f->hs_descriptors, + &hidg_hs_in_ep_desc); + } else { + hidg->hs_in_ep_desc = NULL; + } + + hidg->connected = 0; + + mutex_init(&hidg->lock); + spin_lock_init(&hidg->spinlock); + init_waitqueue_head(&hidg->write_queue); + init_waitqueue_head(&hidg->read_queue); + + /* create char device */ + cdev_init(&hidg->cdev, &f_hidg_fops); + dev = MKDEV(major, hidg->minor); + status = cdev_add(&hidg->cdev, dev, 1); + if (status) + goto fail; + + device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor); + + return 0; + +fail: + ;//ERROR(f->config->cdev, "hidg_bind FAILED\n"); + if (hidg->req != NULL) { + kfree(hidg->req->buf); + if (hidg->in_ep != NULL) + usb_ep_free_request(hidg->in_ep, hidg->req); + } + g_hidg = NULL; + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + + return status; +} + +static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_hidg *hidg = func_to_hidg(f); + + f_hid_bypass_input_set(0); + + device_destroy(hidg_class, MKDEV(major, hidg->minor)); + cdev_del(&hidg->cdev); + + /* disable/free request and end point */ + usb_ep_disable(hidg->in_ep); + usb_ep_dequeue(hidg->in_ep, hidg->req); + if(hidg->req->buf) + kfree(hidg->req->buf); + usb_ep_free_request(hidg->in_ep, hidg->req); + + /* free descriptors copies */ + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + + + kfree(hidg->report_desc); + kfree(hidg->set_report_buff); + kfree(hidg); + + g_hidg = NULL; +} + +/*-------------------------------------------------------------------------*/ +/* Strings */ + +#define CT_FUNC_HID_IDX 0 + +static struct usb_string ct_func_string_defs[] = { + [CT_FUNC_HID_IDX].s = "HID Interface", + {}, /* end of list */ +}; + +static struct usb_gadget_strings ct_func_string_table = { + .language = 0x0409, /* en-US */ + .strings = ct_func_string_defs, +}; + +static struct usb_gadget_strings *ct_func_strings[] = { + &ct_func_string_table, + NULL, +}; + +/*-------------------------------------------------------------------------*/ +/* usb_configuration */ + +int hidg_bind_config(struct usb_configuration *c, + const struct hidg_func_descriptor *fdesc, int index) +{ + struct f_hidg *hidg; + int status; + if (index >= minors) + return -ENOENT; + + /* maybe allocate device-global string IDs, and patch descriptors */ + if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + return status; + ct_func_string_defs[CT_FUNC_HID_IDX].id = status; + hidg_interface_desc.iInterface = status; + } + + /* allocate and initialize one new instance */ + hidg = kzalloc(sizeof *hidg, GFP_KERNEL); + if (!hidg) + return -ENOMEM; + g_hidg = hidg; + hidg->bypass_input = 0; + hidg->minor = index; + hidg->bInterfaceSubClass = fdesc->subclass; + hidg->bInterfaceProtocol = fdesc->protocol; + hidg->report_length = fdesc->report_length; + hidg->report_desc_length = fdesc->report_desc_length; + hidg->report_desc = kmemdup(fdesc->report_desc, + fdesc->report_desc_length, + GFP_KERNEL); + if (!hidg->report_desc) { + kfree(hidg); + return -ENOMEM; + } + + hidg->func.name = "hid"; + hidg->func.strings = ct_func_strings; + hidg->func.bind = hidg_bind; + hidg->func.unbind = hidg_unbind; + hidg->func.set_alt = hidg_set_alt; + hidg->func.disable = hidg_disable; + hidg->func.setup = hidg_setup; + + status = usb_add_function(c, &hidg->func); + if (status) + kfree(hidg); + else + g_hidg = hidg; + return status; +} + +int ghid_setup(struct usb_gadget *g, int count) +{ + int status; + dev_t dev; + + hidg_class = class_create(THIS_MODULE, "hidg"); + + status = alloc_chrdev_region(&dev, 0, count, "hidg"); + if (!status) { + major = MAJOR(dev); + minors = count; + } + + return status; +} + +void ghid_cleanup(void) +{ + if (major) { + unregister_chrdev_region(MKDEV(major, 0), minors); + major = minors = 0; + } + + class_destroy(hidg_class); +} -- 2.34.1