Skip to content

Commit

Permalink
HID keyboard/mouse gadget support
Browse files Browse the repository at this point in the history
Updated for Android kernel 3.18+

Includes minor adjustments to usb function parser

Source: https://github.com/pelya/android-keyboard-gadget
  • Loading branch information
jcadduono authored and hendramanudinata03 committed May 2, 2022
1 parent aed7bfb commit 5e0c631
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 19 deletions.
78 changes: 65 additions & 13 deletions drivers/usb/gadget/android.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,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();
#ifdef CONFIG_MEDIA_SUPPORT
Expand Down Expand Up @@ -3141,6 +3144,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)
{
Expand Down Expand Up @@ -3310,6 +3348,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,
Expand Down Expand Up @@ -3350,6 +3389,7 @@ static struct android_usb_function *default_functions[] = {
#ifdef CONFIG_SND_RAWMIDI
&midi_function,
#endif
&hid_function,
NULL
};

Expand Down Expand Up @@ -3638,8 +3678,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);

Expand Down Expand Up @@ -3681,38 +3721,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;
}
}

Expand Down
3 changes: 2 additions & 1 deletion drivers/usb/gadget/function/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

100 changes: 95 additions & 5 deletions drivers/usb/gadget/function/f_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -260,6 +304,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)
Expand All @@ -274,6 +323,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);
}

Expand Down Expand Up @@ -314,7 +368,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)
Expand Down Expand Up @@ -403,7 +468,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
Expand All @@ -415,6 +485,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;

Expand Down Expand Up @@ -568,13 +646,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)
Expand Down Expand Up @@ -640,6 +720,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;

Expand All @@ -660,12 +741,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);

Expand Down Expand Up @@ -698,7 +788,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;
Expand Down Expand Up @@ -752,7 +842,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;
Expand Down
16 changes: 16 additions & 0 deletions drivers/usb/gadget/function/f_hid.h
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 5e0c631

Please sign in to comment.