add function hidg to android gadget
authorlyz <lyz@rock-chips.com>
Tue, 22 Oct 2013 13:11:07 +0000 (21:11 +0800)
committerlyz <lyz@rock-chips.com>
Tue, 22 Oct 2013 13:11:07 +0000 (21:11 +0800)
drivers/hid/hid-core.c [changed mode: 0644->0755]
drivers/usb/gadget/Kconfig
drivers/usb/gadget/android.c [changed mode: 0644->0755]
drivers/usb/gadget/f_hid_rk.c [new file with mode: 0755]

old mode 100644 (file)
new mode 100755 (executable)
index 53576e7..290ded5
 
 #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);
 }
index e222aceb3251e7851c692238ac293e3a9b2e75d7..49d5f1d2930dc61452d7799f479987a45ab5d2e4 100644 (file)
@@ -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
old mode 100644 (file)
new mode 100755 (executable)
index a9c3f78..1d8893e
 #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 (executable)
index 0000000..6e3ac06
--- /dev/null
@@ -0,0 +1,978 @@
+/*
+ * f_hid.c -- USB HID function driver
+ *
+ * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/module.h>
+#include <linux/hid.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/usb/g_hid.h>
+
+
+
+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);
+}