Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dweinstein/7aa5bbc88364af75d5a4 to your computer and use it in GitHub Desktop.
Save dweinstein/7aa5bbc88364af75d5a4 to your computer and use it in GitHub Desktop.
HID gadget implementation
From c5a7d1115318bd02145a4b41109464d564b37af9 Mon Sep 17 00:00:00 2001
From: David Weinstein <dweinst@insitusec.com>
Date: Mon, 14 Jan 2013 12:21:37 -0500
Subject: [PATCH] add HID support to android gadget.
---
drivers/usb/gadget/android.c | 189 ++++++++++++++++++++++++++++++++++++++++++
drivers/usb/gadget/f_hid.c | 8 +-
2 files changed, 194 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index fd6072f..63eab11 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -30,6 +30,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/g_hid.h>
#include "gadget_chips.h"
@@ -45,6 +46,7 @@
#include "epautoconf.c"
#include "composite.c"
+#include "f_hid.c"
#include "f_audio_source.c"
#include "f_mass_storage.c"
#include "u_serial.c"
@@ -818,6 +820,192 @@ static struct android_usb_function dm_function = {
.bind_config = dm_function_bind_config,
};
+
+/* hid descriptor for a keyboard */
+static struct hidg_func_descriptor hid_kb = {
+ .subclass = 0, /* No subclass */
+ .protocol = 1, /* Keyboard */
+ .report_length = 8,
+ .report_desc_length = 63,
+ .report_desc = {
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x06, /* USAGE (Keyboard) */
+ 0xa1, 0x01, /* COLLECTION (Application) */
+ 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 */
+ }
+};
+
+/*
+ *
+ */
+static struct hidg_func_descriptor hid_mouse = {
+ .subclass = 0, /* Boot */
+ .protocol = 2, /* Keyboard */
+ .report_length = 8,
+ .report_desc_length = 50,
+ .report_desc = {
+ 0x05, 0x01,
+ 0x09, 0x02,
+ 0xA1, 0x01,
+ 0x09, 0x01,
+ 0xA1, 0x00,
+ 0x05, 0x09,
+ 0x19, 0x01,
+ 0x29, 0x03,
+ 0x15, 0x00,
+ 0x25, 0x01,
+ 0x95, 0x03,
+ 0x75, 0x01,
+ 0x81, 0x02,
+ 0x95, 0x01,
+ 0x75, 0x05,
+ 0x81, 0x01,
+ 0x05, 0x01,
+ 0x09, 0x30,
+ 0x09, 0x31,
+ 0x15, 0x81,
+ 0x25, 0x7F,
+ 0x75, 0x08,
+ 0x95, 0x02,
+ 0x81, 0x06,
+ 0xC0,
+ 0xC0,
+ }
+
+};
+
+#define HID_MAX_INSTANCES 2
+struct hid_function_config {
+ int instances;
+};
+
+static int hid_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
+{
+ struct hid_function_config *config;
+ int ret;
+ f->config = kzalloc(sizeof(struct hid_function_config), GFP_KERNEL);
+ config = f->config;
+
+ if (!config)
+ return -ENOMEM;
+ printk(KERN_INFO "hid_function_init\n");
+ config->instances = HID_MAX_INSTANCES;
+
+ ret = ghid_setup(cdev->gadget, HID_MAX_INSTANCES);
+ printk(KERN_INFO "hid_function_init: ghid_setup => %d", ret);
+ return ret;
+}
+
+static void hid_function_cleanup(struct android_usb_function *f)
+{
+ ghid_cleanup();
+ kfree(f->config);
+ f->config = NULL;
+}
+
+static int hid_function_bind_config(struct android_usb_function *f, struct usb_configuration *c)
+{
+ int index = 0;
+ int ret = 0;
+ int (*fp)(struct usb_configuration *, struct hidg_func_descriptor *, int);
+ struct hid_function_config *config = f->config;
+
+ if (!config)
+ return -EINVAL;
+
+ printk(KERN_INFO "hid_function_bind_config (instances: %d)\n",
+ config->instances);
+ fp = hidg_bind_config;
+ printk(KERN_INFO "hidg_bind_config %p hid_kb: %p hid_mouse: %p\n",
+ fp, &hid_kb, &hid_mouse);
+
+ for (index = 0; index < config->instances; ++index) {
+ printk(KERN_INFO "hid_function_bind_config idx %d\n", index);
+ switch(index) {
+ case 0:
+ ret = hidg_bind_config(c, &hid_kb, index);
+ printk(KERN_INFO "hid_function_bind_config idx %d ret %d\n", index, ret);
+ if (ret) {
+ pr_err("Could not bind hid%d (keyboard) config\n", index);
+ return -EINVAL;
+ }
+ break;
+
+ case 1:
+ ret = hidg_bind_config(c, &hid_mouse, index);
+ printk(KERN_INFO "hid_function_bind_config idx %d ret %d\n", index, ret);
+ if (ret) {
+ pr_err("Could not bind hid%d (mouse) config\n", index);
+ return -EINVAL;
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static ssize_t hid_instances_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct hid_function_config *config = f->config;
+ return sprintf(buf, "%d\n", config->instances);
+}
+
+static ssize_t hid_instances_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct android_usb_function *f = dev_get_drvdata(dev);
+ struct hid_function_config *config = f->config;
+ int value;
+
+ sscanf(buf, "%d", &value);
+ if (value > HID_MAX_INSTANCES)
+ value = HID_MAX_INSTANCES;
+ config->instances = value;
+ return size;
+}
+
+static DEVICE_ATTR(hinstances, S_IRUGO | S_IWUSR, hid_instances_show, hid_instances_store);
+static struct device_attribute *hid_function_attributes[] = { &dev_attr_hinstances, NULL };
+
+static struct android_usb_function hid_function = {
+ .name = "hid",
+ .init = hid_function_init,
+ .cleanup = hid_function_cleanup,
+ .bind_config = hid_function_bind_config,
+ .attributes = hid_function_attributes,
+};
+
static struct android_usb_function *supported_functions[] = {
&adb_function,
&acm_function,
@@ -828,6 +1016,7 @@ static struct android_usb_function *supported_functions[] = {
&accessory_function,
&audio_source_function,
&dm_function,
+ &hid_function,
NULL
};
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
index 598e7e2..bef26be 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/f_hid.c
@@ -449,7 +449,7 @@ const struct file_operations f_hidg_fops = {
.llseek = noop_llseek,
};
-static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
+static int /*__init*/ hidg_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_ep *ep;
struct f_hidg *hidg = func_to_hidg(f);
@@ -549,6 +549,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_hidg *hidg = func_to_hidg(f);
+ printk(KERN_INFO "hid_unbind\n");
device_destroy(hidg_class, MKDEV(major, hidg->minor));
cdev_del(&hidg->cdev);
@@ -590,7 +591,7 @@ static struct usb_gadget_strings *ct_func_strings[] = {
/*-------------------------------------------------------------------------*/
/* usb_configuration */
-int __init hidg_bind_config(struct usb_configuration *c,
+int /*__init*/ hidg_bind_config(struct usb_configuration *c,
struct hidg_func_descriptor *fdesc, int index)
{
struct f_hidg *hidg;
@@ -599,6 +600,7 @@ int __init hidg_bind_config(struct usb_configuration *c,
if (index >= minors)
return -ENOENT;
+ printk(KERN_INFO "hid_bind_config");
/* 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);
@@ -641,7 +643,7 @@ int __init hidg_bind_config(struct usb_configuration *c,
return status;
}
-int __init ghid_setup(struct usb_gadget *g, int count)
+int /*__init*/ ghid_setup(struct usb_gadget *g, int count)
{
int status;
dev_t dev;
--
1.7.9.5
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <jni.h>
#include <android/log.h>
#define DEBUG_TAG "NATIVE_HID"
#define BUF_LEN 512
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, DEBUG_TAG, __VA_ARGS__))
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, DEBUG_TAG, __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, DEBUG_TAG, __VA_ARGS__))
int sendToDevice(char device, char buf[BUF_LEN]);
struct options {
const char *opt;
unsigned char val;
};
static struct options kmod[] = {
{.opt = "--left-ctrl", .val = 0x01},
{.opt = "--right-ctrl", .val = 0x10},
{.opt = "--left-shift", .val = 0x02},
{.opt = "--right-shift", .val = 0x20},
{.opt = "--left-alt", .val = 0x04},
{.opt = "--right-alt", .val = 0x40},
{.opt = "--left-meta", .val = 0x08},
{.opt = "--right-meta", .val = 0x80},
{.opt = NULL}
};
static struct options kval[] = {
{.opt = "1", .val = 0x1E},
{.opt = "2", .val = 0x1F},
{.opt = "3", .val = 0x20},
{.opt = "4", .val = 0x21},
{.opt = "5", .val = 0x22},
{.opt = "6", .val = 0x23},
{.opt = "7", .val = 0x24},
{.opt = "8", .val = 0x25},
{.opt = "9", .val = 0x26},
{.opt = "0", .val = 0x27},
{.opt = "--return", .val = 0x28},
{.opt = "--esc", .val = 0x29},
{.opt = "--bckspc", .val = 0x2a},
{.opt = "--tab", .val = 0x2b},
{.opt = "--spacebar", .val = 0x2c},
{.opt = "--caps-lock", .val = 0x39},
{.opt = "--f1", .val = 0x3a},
{.opt = "--f2", .val = 0x3b},
{.opt = "--f3", .val = 0x3c},
{.opt = "--f4", .val = 0x3d},
{.opt = "--f5", .val = 0x3e},
{.opt = "--f6", .val = 0x3f},
{.opt = "--f7", .val = 0x40},
{.opt = "--f8", .val = 0x41},
{.opt = "--f9", .val = 0x42},
{.opt = "--f10", .val = 0x43},
{.opt = "--f11", .val = 0x44},
{.opt = "--f12", .val = 0x45},
{.opt = "--insert", .val = 0x49},
{.opt = "--home", .val = 0x4a},
{.opt = "--pageup", .val = 0x4b},
{.opt = "--del", .val = 0x4c},
{.opt = "--end", .val = 0x4d},
{.opt = "--pagedown", .val = 0x4e},
{.opt = "--right", .val = 0x4f},
{.opt = "--left", .val = 0x50},
{.opt = "--down", .val = 0x51},
{.opt = "--kp-enter", .val = 0x58},
{.opt = "--up", .val = 0x52},
{.opt = "--num-lock", .val = 0x53},
{.opt = "--minus", .val = 0x2d},
{.opt = "--plus", .val = 0x57},
{.opt = "--comma", .val = 0x36},
{.opt = "--period", .val = 0x37},
{.opt = "--slash", .val = 0x38},
{.opt = "--backslash", .val = 0x31},
{.opt = "--equal", .val = 0x2e},
{.opt = "--asterisk", .val = 0x55},
{.opt = "--left-brace", .val = 0x2f},
{.opt = "--right-brace", .val = 0x30},
{.opt = "--tilde", .val = 0x35},
{.opt = "--semicolon", .val = 0x33},
{.opt = "--tick", .val = 0x34},
{.opt = NULL}
};
static struct options mmod[] = {
{.opt = "--b1", .val = 0x01},
{.opt = "--b2", .val = 0x02},
{.opt = "--b3", .val = 0x04},
{.opt = NULL}
};
void Java_com_insitusec_pwmanager_Hid_nativeSendCommandToKeyboard(JNIEnv* env, jobject thiz, jstring string) {
char * sz = (*env)->GetStringUTFChars(env, string, NULL);
//LOGD(sz);
int result = sendToDevice('k',sz);
//char mess[50];
//sprintf(mess,"sendToKeyboard result: %d",result);
//LOGI(mess);
(*env)->ReleaseStringUTFChars(env, string, sz);
}
void Java_com_insitusec_pwmanager_Hid__nativeSendCommandToMouse(JNIEnv* env, jobject thiz, jstring string) {
char * sz = (*env)->GetStringUTFChars(env, string, NULL);
//LOGD(sz);
int result = sendToDevice('m',sz);
//char mess[50];
//sprintf(mess,"sendToMouse result: %d",result);
//LOGI(mess);
(*env)->ReleaseStringUTFChars(env, string, sz);
}
int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
char *tok = strtok(buf, " ");
int key = 0;
int i = 0;
for (; tok != NULL; tok = strtok(NULL, " ")) {
if (strcmp(tok, "--quit") == 0)
return -1;
if (strcmp(tok, "--hold") == 0) {
*hold = 1;
continue;
}
if (key < 6) {
for (i = 0; kval[i].opt != NULL; i++)
if (strcmp(tok, kval[i].opt) == 0) {
report[2 + key++] = kval[i].val;
break;
}
if (kval[i].opt != NULL)
continue;
}
if (key < 6)
if (islower(tok[0])) {
report[2 + key++] = (tok[0] - ('a' - 0x04));
continue;
}
for (i = 0; kmod[i].opt != NULL; i++)
if (strcmp(tok, kmod[i].opt) == 0) {
report[0] = report[0] | kmod[i].val;
break;
}
if (kmod[i].opt != NULL)
continue;
if (key < 6)
LOGE("keyboard_fill_report error");
}
return 8;
}
int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
{
char *tok = strtok(buf, " ");
int mvt = 0;
int i = 0;
for (; tok != NULL; tok = strtok(NULL, " ")) {
if (strcmp(tok, "--quit") == 0)
return -1;
if (strcmp(tok, "--hold") == 0) {
*hold = 1;
continue;
}
for (i = 0; mmod[i].opt != NULL; i++)
if (strcmp(tok, mmod[i].opt) == 0) {
report[0] = report[0] | mmod[i].val;
break;
}
if (mmod[i].opt != NULL)
continue;
if (!(tok[0] == '-' && tok[1] == '-') && mvt < 2) {
errno = 0;
report[1 + mvt++] = (char)strtol(tok, NULL, 0);
if (errno != 0) {
report[1 + mvt--] = 0;
}
continue;
}
}
return 3;
}
int sendToDevice(char device, char buf[BUF_LEN]) {
//LOGD(buf);
char *filename = NULL;
int fd = 0;
int cmd_len;
char report[8];
int to_send = 8;
int hold = 0;
if (device == 'k')
filename = "/dev/hidg0";
else if (device == 'm')
filename = "/dev/hidg1";
else
return 1;
//LOGD(filename);
if ((fd = open(filename, O_RDWR, 0666)) == -1) {
return 2;
}
hold = 0;
memset(report, 0x0, sizeof(report));
if (device == 'k')
to_send = keyboard_fill_report(report, buf, &hold);
else if (device == 'm')
to_send = mouse_fill_report(report, buf, &hold);
else
return 3;
if (to_send == -1)
return 4;
if (write(fd, report, to_send) != to_send) {
return 5;
}
if (!hold) {
memset(report, 0x0, sizeof(report));
if (write(fd, report, to_send) != to_send) {
return 6;
}
}
close(fd);
return 0;
}
on init
write /sys/class/android_usb/android0/iSerial $ro.serialno
write /sys/class/android_usb/android0/f_rndis/manufacturer Samsung
write /sys/class/android_usb/android0/f_rndis/vendorID 04e8
write /sys/class/android_usb/android0/f_rndis/wceis 1
on boot
write /sys/class/android_usb/android0/iManufacturer $ro.product.manufacturer
write /sys/class/android_usb/android0/iProduct $ro.product.model
on property:sys.usb.config=mtp
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 04e8
write /sys/class/android_usb/android0/idProduct 685c
write /sys/class/android_usb/android0/functions $sys.usb.config,hid
write /sys/class/android_usb/android0/enable 1
setprop sys.usb.state $sys.usb.config,hid
on property:sys.usb.config=mtp,adb
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 04e8
write /sys/class/android_usb/android0/idProduct 6860
write /sys/class/android_usb/android0/functions $sys.usb.config,hid
write /sys/class/android_usb/android0/enable 1
start adbd
setprop sys.usb.state $sys.usb.config,hid
on property:sys.usb.config=rndis
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 04e8
write /sys/class/android_usb/android0/idProduct 6863
write /sys/class/android_usb/android0/functions $sys.usb.config,hid
write /sys/class/android_usb/android0/bDeviceClass 224
write /sys/class/android_usb/android0/enable 1
setprop sys.usb.state $sys.usb.config,hid
on property:sys.usb.config=rndis,adb
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 04e8
write /sys/class/android_usb/android0/idProduct 6864
write /sys/class/android_usb/android0/functions $sys.usb.config,hid
write /sys/class/android_usb/android0/bDeviceClass 224
write /sys/class/android_usb/android0/enable 1
start adbd
setprop sys.usb.state $sys.usb.config,hid
on property:sys.usb.config=ptp
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 04e8
write /sys/class/android_usb/android0/idProduct 6865
write /sys/class/android_usb/android0/functions $sys.usb.config,hid
write /sys/class/android_usb/android0/enable 1
setprop sys.usb.state $sys.usb.config,hid
on property:sys.usb.config=ptp,adb
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 04e8
write /sys/class/android_usb/android0/idProduct 6866
write /sys/class/android_usb/android0/functions $sys.usb.config,hid
write /sys/class/android_usb/android0/enable 1
start adbd
setprop sys.usb.state $sys.usb.config,hid
on property:sys.usb.config=rndis,dm
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 04e8
write /sys/class/android_usb/android0/idProduct 6864
write /sys/class/android_usb/android0/functions $sys.usb.config,hid
write /sys/class/android_usb/android0/enable 1
setprop sys.usb.state $sys.usb.config,hid
on property:sys.usb.config=rndis,acm,dm
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 04e8
write /sys/class/android_usb/android0/idProduct 6864
write /sys/class/android_usb/android0/functions $sys.usb.config,hid
write /sys/class/android_usb/android0/f_acm/instances 1
write /sys/class/android_usb/android0/enable 1
setprop sys.usb.state $sys.usb.config,hid
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment