add function hidg to android gadget

This commit is contained in:
lyz
2013-10-22 21:11:07 +08:00
parent 20a0304fdc
commit c5c3cda57c
4 changed files with 1174 additions and 4 deletions

27
drivers/hid/hid-core.c Normal file → Executable file
View File

@@ -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);
}

View 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

165
drivers/usb/gadget/android.c Normal file → Executable file
View File

@@ -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;

978
drivers/usb/gadget/f_hid_rk.c Executable file
View File

@@ -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);
}