Skip to content

Instantly share code, notes, and snippets.

@BenWoodworth
Created February 6, 2019 15:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BenWoodworth/5b4310dee9adb1cc647cd158074195e4 to your computer and use it in GitHub Desktop.
Save BenWoodworth/5b4310dee9adb1cc647cd158074195e4 to your computer and use it in GitHub Desktop.
From fb3e3c2ebf249d89ecf8974b9d8c85df6744aac3 Mon Sep 17 00:00:00 2001
From: James Christopher Adduono <jc@adduono.com>
Date: Mon, 11 Apr 2016 15:53:59 -0400
Subject: [PATCH] HID keyboard/mouse gadget support
Updated for Android kernel 3.18+
Includes minor adjustments to usb function parser
Source: https://github.com/pelya/android-keyboard-gadget
---
drivers/usb/gadget/android.c | 78 +++++++++++++---
drivers/usb/gadget/function/Makefile | 3 +-
drivers/usb/gadget/function/f_hid.c | 100 +++++++++++++++++++--
drivers/usb/gadget/function/f_hid.h | 16 ++++
drivers/usb/gadget/function/f_hid_android_keyboard.c | 44 +++++++++
drivers/usb/gadget/function/f_hid_android_mouse.c | 39 ++++++++
include/linux/usb/android.h | 3 +
7 files changed, 264 insertions(+), 19 deletions(-)
create mode 100644 drivers/usb/gadget/function/f_hid.h
create mode 100644 drivers/usb/gadget/function/f_hid_android_keyboard.c
create mode 100644 drivers/usb/gadget/function/f_hid_android_mouse.c
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index c002483..7c9bff4 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -69,6 +69,9 @@
#include "u_qc_ether.c"
#include "f_gsi.c"
#include "f_mass_storage.h"
+#include "f_hid.h"
+#include "f_hid_android_keyboard.c"
+#include "f_hid_android_mouse.c"
USB_ETHERNET_MODULE_PARAMETERS();
#include "debug.h"
@@ -2854,6 +2857,41 @@ static struct android_usb_function midi_function = {
};
#endif
+static int hid_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
+{
+ return ghid_setup(cdev->gadget, 2);
+}
+
+static void hid_function_cleanup(struct android_usb_function *f)
+{
+ ghid_cleanup();
+}
+
+static int hid_function_bind_config(struct android_usb_function *f, struct usb_configuration *c)
+{
+ int ret;
+ printk(KERN_INFO "hid keyboard\n");
+ ret = hidg_bind_config(c, &ghid_device_android_keyboard, 0);
+ if (ret) {
+ pr_info("%s: hid_function_bind_config keyboard failed: %d\n", __func__, ret);
+ return ret;
+ }
+ printk(KERN_INFO "hid mouse\n");
+ ret = hidg_bind_config(c, &ghid_device_android_mouse, 1);
+ if (ret) {
+ pr_info("%s: hid_function_bind_config mouse failed: %d\n", __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static struct android_usb_function hid_function = {
+ .name = "hid",
+ .init = hid_function_init,
+ .cleanup = hid_function_cleanup,
+ .bind_config = hid_function_bind_config,
+};
+
static int rndis_gsi_function_init(struct android_usb_function *f,
struct usb_composite_dev *cdev)
{
@@ -3022,6 +3060,7 @@ static struct android_usb_function *supported_functions[] = {
#ifdef CONFIG_SND_RAWMIDI
[ANDROID_MIDI] = &midi_function,
#endif
+ [ANDROID_HID] = &hid_function,
[ANDROID_RNDIS_GSI] = &rndis_gsi_function,
[ANDROID_ECM_GSI] = &ecm_gsi_function,
[ANDROID_RMNET_GSI] = &rmnet_gsi_function,
@@ -3057,6 +3096,7 @@ static struct android_usb_function *default_functions[] = {
#ifdef CONFIG_SND_RAWMIDI
&midi_function,
#endif
+ &hid_function,
NULL
};
@@ -3365,8 +3405,8 @@ functions_store(struct device *pdev, struct device_attribute *attr,
char buf[256], *b;
char aliases[256], *a;
int err;
- int is_ffs;
int ffs_enabled = 0;
+ int hid_enabled = 0;
mutex_lock(&dev->mutex);
@@ -3409,38 +3449,50 @@ functions_store(struct device *pdev, struct device_attribute *attr,
curr_conf = curr_conf->next;
while (conf_str) {
name = strsep(&conf_str, ",");
- is_ffs = 0;
strlcpy(aliases, dev->ffs_aliases, sizeof(aliases));
a = aliases;
while (a) {
char *alias = strsep(&a, ",");
if (alias && !strcmp(name, alias)) {
- is_ffs = 1;
+ name = "ffs";
break;
}
}
- if (is_ffs) {
- if (ffs_enabled)
- continue;
- err = android_enable_function(dev, conf, "ffs");
- if (err)
- pr_err("android_usb: Cannot enable ffs (%d)",
- err);
- else
- ffs_enabled = 1;
+ if (ffs_enabled && !strcmp(name, "ffs"))
+ continue;
+
+ if (hid_enabled && !strcmp(name, "hid"))
continue;
- }
if (!strcmp(name, "rndis") &&
!strcmp(strim(rndis_transports), "BAM2BAM_IPA"))
name = "rndis_qc";
err = android_enable_function(dev, conf, name);
+ if (err) {
+ pr_err("android_usb: Cannot enable '%s' (%d)",
+ name, err);
+ continue;
+ }
+
+ if (!strcmp(name, "ffs"))
+ ffs_enabled = 1;
+
+ if (!strcmp(name, "hid"))
+ hid_enabled = 1;
+ }
+
+ /* Always enable HID gadget function. */
+ if (!hid_enabled) {
+ name = "hid";
+ err = android_enable_function(dev, conf, name);
if (err)
pr_err("android_usb: Cannot enable '%s' (%d)",
name, err);
+ else
+ hid_enabled = 1;
}
}
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index cc428c4..5f68de9 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -46,5 +46,6 @@ usb_f_audio_source-y := f_audio_source.o
obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o
usb_f_accessory-y := f_accessory.o
obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o
-
+usb_f_hid-y := f_hid.o
+obj-$(CONFIG_USB_G_ANDROID) += usb_f_hid.o
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 59ab62c..7ea11eb 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -17,8 +17,10 @@
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
+#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/usb/g_hid.h>
+#include "f_hid.h"
#include "u_f.h"
@@ -62,6 +64,43 @@ struct f_hidg {
struct usb_ep *out_ep;
};
+/* Hacky device list to fix f_hidg_write being called after device destroyed.
+ It covers only most common race conditions, there will be rare crashes anyway. */
+enum { HACKY_DEVICE_LIST_SIZE = 4 };
+static struct f_hidg *hacky_device_list[HACKY_DEVICE_LIST_SIZE];
+static void hacky_device_list_add(struct f_hidg *hidg)
+{
+ int i;
+ for (i = 0; i < HACKY_DEVICE_LIST_SIZE; i++) {
+ if (!hacky_device_list[i]) {
+ hacky_device_list[i] = hidg;
+ return;
+ }
+ }
+ pr_err("%s: too many devices, not adding device %p\n", __func__, hidg);
+}
+static void hacky_device_list_remove(struct f_hidg *hidg)
+{
+ int i;
+ for (i = 0; i < HACKY_DEVICE_LIST_SIZE; i++) {
+ if (hacky_device_list[i] == hidg) {
+ hacky_device_list[i] = NULL;
+ return;
+ }
+ }
+ pr_err("%s: cannot find device %p\n", __func__, hidg);
+}
+static int hacky_device_list_check(struct f_hidg *hidg)
+{
+ int i;
+ for (i = 0; i < HACKY_DEVICE_LIST_SIZE; i++) {
+ if (hacky_device_list[i] == hidg) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
static inline struct f_hidg *func_to_hidg(struct usb_function *f)
{
return container_of(f, struct f_hidg, func);
@@ -178,6 +217,11 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer,
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
+ if (hacky_device_list_check(hidg)) {
+ pr_err("%s: trying to read from device %p that was destroyed\n", __func__, hidg);
+ return -EIO;
+ }
+
spin_lock_irqsave(&hidg->spinlock, flags);
#define READ_COND (!list_empty(&hidg->completed_out_req))
@@ -248,6 +292,11 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
+ if (hacky_device_list_check(hidg)) {
+ pr_err("%s: trying to write to device %p that was destroyed\n", __func__, hidg);
+ return -EIO;
+ }
+
mutex_lock(&hidg->lock);
#define WRITE_COND (!hidg->write_pending)
@@ -262,6 +311,11 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
hidg->write_queue, WRITE_COND))
return -ERESTARTSYS;
+ if (hacky_device_list_check(hidg)) {
+ pr_err("%s: trying to write to device %p that was destroyed\n", __func__, hidg);
+ return -EIO;
+ }
+
mutex_lock(&hidg->lock);
}
@@ -302,7 +356,18 @@ static unsigned int f_hidg_poll(struct file *file, poll_table *wait)
struct f_hidg *hidg = file->private_data;
unsigned int ret = 0;
+ if (hacky_device_list_check(hidg)) {
+ pr_err("%s: trying to poll device %p that was destroyed\n", __func__, hidg);
+ return -EIO;
+ }
+
poll_wait(file, &hidg->read_queue, wait);
+
+ if (hacky_device_list_check(hidg)) {
+ pr_err("%s: trying to poll device %p that was destroyed\n", __func__, hidg);
+ return -EIO;
+ }
+
poll_wait(file, &hidg->write_queue, wait);
if (WRITE_COND)
@@ -391,7 +456,12 @@ static int hidg_setup(struct usb_function *f,
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_PROTOCOL):
VDBG(cdev, "get_protocol\n");
- goto stall;
+ length = min_t(unsigned, length, 1);
+ if (hidg->bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT)
+ ((u8 *) req->buf)[0] = 0; /* Boot protocol */
+ else
+ ((u8 *) req->buf)[0] = 1; /* Report protocol */
+ goto respond;
break;
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
@@ -403,6 +473,14 @@ static int hidg_setup(struct usb_function *f,
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_SET_PROTOCOL):
VDBG(cdev, "set_protocol\n");
+ length = 0;
+ if (hidg->bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
+ if (value == 0) /* Boot protocol */
+ goto respond;
+ } else {
+ if (value == 1) /* Report protocol */
+ goto respond;
+ }
goto stall;
break;
@@ -552,13 +630,15 @@ 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 hidg_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_ep *ep;
struct f_hidg *hidg = func_to_hidg(f);
int status;
dev_t dev;
+ pr_info("%s: creating device %p\n", __func__, hidg);
+
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
@@ -624,6 +704,7 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
goto fail_free_descs;
device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
+ hacky_device_list_add(hidg);
return 0;
@@ -644,12 +725,21 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_hidg *hidg = func_to_hidg(f);
+ pr_info("%s: destroying device %p\n", __func__, hidg);
+ /* This does not cover all race conditions, only most common one */
+ mutex_lock(&hidg->lock);
+ hacky_device_list_remove(hidg);
+ mutex_unlock(&hidg->lock);
+
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);
+ /* TODO: calling this function crash kernel,
+ not calling this funct ion crash kernel inside f_hidg_write */
+ /* usb_ep_dequeue(hidg->in_ep, hidg->req); */
+
kfree(hidg->req->buf);
usb_ep_free_request(hidg->in_ep, hidg->req);
@@ -682,7 +772,7 @@ static struct usb_gadget_strings *ct_func_strings[] = {
/*-------------------------------------------------------------------------*/
/* usb_configuration */
-int __init hidg_bind_config(struct usb_configuration *c,
+int hidg_bind_config(struct usb_configuration *c,
struct hidg_func_descriptor *fdesc, int index)
{
struct f_hidg *hidg;
@@ -736,7 +826,7 @@ int __init hidg_bind_config(struct usb_configuration *c,
return status;
}
-int __init ghid_setup(struct usb_gadget *g, int count)
+int ghid_setup(struct usb_gadget *g, int count)
{
int status;
dev_t dev;
diff --git a/drivers/usb/gadget/function/f_hid.h b/drivers/usb/gadget/function/f_hid.h
new file mode 100644
index 0000000..ad3527a
--- /dev/null
+++ b/drivers/usb/gadget/function/f_hid.h
@@ -0,0 +1,16 @@
+#ifndef _GADGET_F_HID_H
+#define _GADGET_F_HID_H
+
+#include <linux/hid.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/g_hid.h>
+
+int hidg_bind_config(struct usb_configuration *c,
+ struct hidg_func_descriptor *fdesc, int index);
+
+int ghid_setup(struct usb_gadget *g, int count);
+
+void ghid_cleanup(void);
+
+#endif
diff --git a/drivers/usb/gadget/function/f_hid_android_keyboard.c b/drivers/usb/gadget/function/f_hid_android_keyboard.c
new file mode 100644
index 0000000..1824bdd
--- /dev/null
+++ b/drivers/usb/gadget/function/f_hid_android_keyboard.c
@@ -0,0 +1,44 @@
+#include <linux/platform_device.h>
+#include <linux/usb/g_hid.h>
+
+/* hid descriptor for a keyboard */
+static struct hidg_func_descriptor ghid_device_android_keyboard = {
+ .subclass = 1, /* Boot Interface 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 */
+ }
+};
diff --git a/drivers/usb/gadget/function/f_hid_android_mouse.c b/drivers/usb/gadget/function/f_hid_android_mouse.c
new file mode 100644
index 0000000..c0432b3
--- /dev/null
+++ b/drivers/usb/gadget/function/f_hid_android_mouse.c
@@ -0,0 +1,39 @@
+#include <linux/platform_device.h>
+#include <linux/usb/g_hid.h>
+
+/* HID descriptor for a mouse */
+static struct hidg_func_descriptor ghid_device_android_mouse = {
+ .subclass = 1, /* Boot Interface Subclass */
+ .protocol = 2, /* Mouse */
+ .report_length = 4,
+ .report_desc_length = 52,
+ .report_desc = {
+ 0x05, 0x01, //Usage Page(Generic Desktop Controls)
+ 0x09, 0x02, //Usage (Mouse)
+ 0xa1, 0x01, //Collection (Application)
+ 0x09, 0x01, //Usage (pointer)
+ 0xa1, 0x00, //Collection (Physical)
+ 0x05, 0x09, //Usage Page (Button)
+ 0x19, 0x01, //Usage Minimum(1)
+ 0x29, 0x05, //Usage Maximum(5)
+ 0x15, 0x00, //Logical Minimum(1)
+ 0x25, 0x01, //Logical Maximum(1)
+ 0x95, 0x05, //Report Count(5)
+ 0x75, 0x01, //Report Size(1)
+ 0x81, 0x02, //Input(Data,Variable,Absolute,BitField)
+ 0x95, 0x01, //Report Count(1)
+ 0x75, 0x03, //Report Size(3)
+ 0x81, 0x01, //Input(Constant,Array,Absolute,BitField)
+ 0x05, 0x01, //Usage Page(Generic Desktop Controls)
+ 0x09, 0x30, //Usage(x)
+ 0x09, 0x31, //Usage(y)
+ 0x09, 0x38, //Usage(Wheel)
+ 0x15, 0x81, //Logical Minimum(-127)
+ 0x25, 0x7F, //Logical Maximum(127)
+ 0x75, 0x08, //Report Size(8)
+ 0x95, 0x03, //Report Count(3)
+ 0x81, 0x06, //Input(Data,Variable,Relative,BitField)
+ 0xc0, //End Collection
+ 0xc0 //End Collection
+ }
+};
diff --git a/include/linux/usb/android.h b/include/linux/usb/android.h
index f2d88de..b6702e7 100644
--- a/include/linux/usb/android.h
+++ b/include/linux/usb/android.h
@@ -43,6 +43,7 @@ enum android_function_index {
ANDROID_AUDIO_SRC,
ANDROID_CHARGER,
ANDROID_MIDI,
+ ANDROID_HID,
ANDROID_RNDIS_GSI,
ANDROID_ECM_GSI,
ANDROID_RMNET_GSI,
@@ -101,6 +102,8 @@ static enum android_function_index name_to_func_idx(const char *name)
return ANDROID_AUDIO_SRC;
if (!strncasecmp("MIDI", name, FUNC_NAME_LEN))
return ANDROID_MIDI;
+ if (!strncasecmp("HID", name, FUNC_NAME_LEN))
+ return ANDROID_HID;
if (!strncasecmp("RNDIS_GSI", name, FUNC_NAME_LEN))
return ANDROID_RNDIS_GSI;
if (!strncasecmp("ECM_GSI", name, FUNC_NAME_LEN))
--
2.10.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment