433 lines
14 KiB
Diff
433 lines
14 KiB
Diff
![]() |
From 7183beccd41e4d5d664ae98a30b5df193e415afb Mon Sep 17 00:00:00 2001
|
||
|
From: anarkia1976 <stefano.villa1976@gmail.com>
|
||
|
Date: Mon, 29 Jun 2015 11:52:37 +0200
|
||
|
Subject: [PATCH] drivers: usb: keyboard/mouse: Use Android device as USB
|
||
|
keyboard/mouse
|
||
|
|
||
|
Adapt to 3.4.y 8974/bacon
|
||
|
Base project: https://github.com/pelya/android-keyboard-gadget
|
||
|
Required app: https://play.google.com/store/apps/details?id=remote.hid.keyboard.client
|
||
|
|
||
|
Steps:
|
||
|
* Start ADB Debugging
|
||
|
* Start app
|
||
|
* Connect device to system via USB cable
|
||
|
|
||
|
All credits to @pelya for this sweet hack.
|
||
|
---
|
||
|
drivers/usb/gadget/Makefile | 2 +-
|
||
|
drivers/usb/gadget/android.c | 45 +++++++++++++++
|
||
|
drivers/usb/gadget/f_hid.c | 87 +++++++++++++++++++++++++++--
|
||
|
drivers/usb/gadget/f_hid.h | 16 ++++++
|
||
|
drivers/usb/gadget/f_hid_android_keyboard.c | 44 +++++++++++++++
|
||
|
drivers/usb/gadget/f_hid_android_mouse.c | 40 +++++++++++++
|
||
|
6 files changed, 227 insertions(+), 7 deletions(-)
|
||
|
create mode 100644 drivers/usb/gadget/f_hid.h
|
||
|
create mode 100644 drivers/usb/gadget/f_hid_android_keyboard.c
|
||
|
create mode 100644 drivers/usb/gadget/f_hid_android_mouse.c
|
||
|
|
||
|
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
|
||
|
index b8f5149..f9a58b3 100644
|
||
|
--- a/drivers/usb/gadget/Makefile
|
||
|
+++ b/drivers/usb/gadget/Makefile
|
||
|
@@ -55,7 +55,7 @@ g_webcam-y := webcam.o
|
||
|
g_ncm-y := ncm.o
|
||
|
g_acm_ms-y := acm_ms.o
|
||
|
g_tcm_usb_gadget-y := tcm_usb_gadget.o
|
||
|
-g_android-y := android.o
|
||
|
+g_android-y := android.o f_hid.o
|
||
|
|
||
|
obj-$(CONFIG_USB_ZERO) += g_zero.o
|
||
|
obj-$(CONFIG_USB_AUDIO) += g_audio.o
|
||
|
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
|
||
|
index 71b35b3..0f5b143 100644
|
||
|
--- a/drivers/usb/gadget/android.c
|
||
|
+++ b/drivers/usb/gadget/android.c
|
||
|
@@ -75,6 +75,9 @@
|
||
|
#include "f_ccid.c"
|
||
|
#include "f_mtp.c"
|
||
|
#include "f_accessory.c"
|
||
|
+#include "f_hid.h"
|
||
|
+#include "f_hid_android_keyboard.c"
|
||
|
+#include "f_hid_android_mouse.c"
|
||
|
#define USB_ETH_RNDIS y
|
||
|
#include "f_rndis.c"
|
||
|
#include "rndis.c"
|
||
|
@@ -2193,6 +2196,41 @@ static struct android_usb_function uasp_function = {
|
||
|
.bind_config = uasp_function_bind_config,
|
||
|
};
|
||
|
|
||
|
+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 struct android_usb_function *supported_functions[] = {
|
||
|
&ffs_function,
|
||
|
&mbim_function,
|
||
|
@@ -2223,6 +2261,7 @@ static struct android_usb_function *supported_functions[] = {
|
||
|
&audio_source_function,
|
||
|
#endif
|
||
|
&uasp_function,
|
||
|
+ &hid_function,
|
||
|
&charger_function,
|
||
|
NULL
|
||
|
};
|
||
|
@@ -2506,6 +2545,7 @@ functions_store(struct device *pdev, struct device_attribute *attr,
|
||
|
int err;
|
||
|
int is_ffs;
|
||
|
int ffs_enabled = 0;
|
||
|
+ int hid_enabled;
|
||
|
|
||
|
mutex_lock(&dev->mutex);
|
||
|
|
||
|
@@ -2574,7 +2614,12 @@ functions_store(struct device *pdev, struct device_attribute *attr,
|
||
|
if (err)
|
||
|
pr_err("android_usb: Cannot enable '%s' (%d)",
|
||
|
name, err);
|
||
|
+ if (!strcmp(name, "hid"))
|
||
|
+ hid_enabled = 1;
|
||
|
}
|
||
|
+ /* HID driver always enabled, it's the whole point of this kernel patch */
|
||
|
+ if (hid_enabled)
|
||
|
+ android_enable_function(dev, conf, "hid");
|
||
|
}
|
||
|
|
||
|
/* Free uneeded configurations if exists */
|
||
|
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
|
||
|
index a4330fa..fa52b72 100644
|
||
|
--- a/drivers/usb/gadget/f_hid.c
|
||
|
+++ b/drivers/usb/gadget/f_hid.c
|
||
|
@@ -18,7 +18,9 @@
|
||
|
#include <linux/poll.h>
|
||
|
#include <linux/uaccess.h>
|
||
|
#include <linux/wait.h>
|
||
|
+#include <linux/delay.h>
|
||
|
#include <linux/usb/g_hid.h>
|
||
|
+#include "f_hid.h"
|
||
|
|
||
|
static int major, minors;
|
||
|
static struct class *hidg_class;
|
||
|
@@ -52,6 +54,43 @@ struct f_hidg {
|
||
|
struct usb_ep *in_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);
|
||
|
@@ -140,6 +179,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 (hidg->set_report_buff != NULL)
|
||
|
@@ -194,6 +238,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)
|
||
|
@@ -208,6 +257,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);
|
||
|
}
|
||
|
|
||
|
@@ -248,7 +302,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)
|
||
|
@@ -448,13 +513,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)
|
||
|
@@ -493,8 +560,6 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||
|
|
||
|
hidg_hs_in_ep_desc.bEndpointAddress =
|
||
|
hidg_fs_in_ep_desc.bEndpointAddress;
|
||
|
- hidg_hs_out_ep_desc.bEndpointAddress =
|
||
|
- hidg_fs_out_ep_desc.bEndpointAddress;
|
||
|
|
||
|
status = usb_assign_descriptors(f, hidg_fs_descriptors,
|
||
|
hidg_hs_descriptors, NULL);
|
||
|
@@ -514,6 +579,7 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
|
||
|
goto fail;
|
||
|
|
||
|
device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
|
||
|
+ hacky_device_list_add(hidg);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
@@ -533,12 +599,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);
|
||
|
|
||
|
@@ -572,7 +647,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;
|
||
|
@@ -623,7 +698,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/f_hid.h b/drivers/usb/gadget/f_hid.h
|
||
|
new file mode 100644
|
||
|
index 0000000..ad3527a
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/usb/gadget/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/f_hid_android_keyboard.c b/drivers/usb/gadget/f_hid_android_keyboard.c
|
||
|
new file mode 100644
|
||
|
index 0000000..76180eb
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/usb/gadget/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 = 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 */
|
||
|
+ }
|
||
|
+};
|
||
|
diff --git a/drivers/usb/gadget/f_hid_android_mouse.c b/drivers/usb/gadget/f_hid_android_mouse.c
|
||
|
new file mode 100644
|
||
|
index 0000000..8a6ab0a
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/usb/gadget/f_hid_android_mouse.c
|
||
|
@@ -0,0 +1,40 @@
|
||
|
+#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 = 0, /* No 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
|
||
|
+ }
|
||
|
+};
|
||
|
+
|