Skip to content

Commit

Permalink
usb: gadget: f_hid: convert to new function interface with backward c…
Browse files Browse the repository at this point in the history
…ompatibility

Converting hid to the new function interface requires converting
the USB hid's function code and its users.

This patch converts the f_hid.c to the new function interface.

The file can now be compiled into a separate usb_f_hid.ko module.

The old function interface is provided by means of a preprocessor
conditional directives. After all users are converted, the old interface
can be removed.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
  • Loading branch information
Andrzej Pietrasiewicz authored and Felipe Balbi committed Nov 6, 2014
1 parent 00896f6 commit cb38253
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 32 deletions.
3 changes: 3 additions & 0 deletions drivers/usb/gadget/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ config USB_F_UVC
config USB_F_MIDI
tristate

config USB_F_HID
tristate

choice
tristate "USB Gadget Drivers"
default USB_ETH
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/gadget/function/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
usb_f_hid-y := f_hid.o
obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
218 changes: 186 additions & 32 deletions drivers/usb/gadget/function/f_hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hid.h>
#include <linux/idr.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/poll.h>
Expand All @@ -21,9 +22,16 @@
#include <linux/usb/g_hid.h>

#include "u_f.h"
#include "u_hid.h"

#define HIDG_MINORS 4

static int major, minors;
static struct class *hidg_class;
#ifndef USBF_HID_INCLUDED
static DEFINE_IDA(hidg_ida);
static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
#endif

/*-------------------------------------------------------------------------*/
/* HID gadget struct */
Expand Down Expand Up @@ -160,6 +168,26 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = {
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,
};

/*-------------------------------------------------------------------------*/
/* Char Device */

Expand Down Expand Up @@ -552,14 +580,23 @@ 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);
struct device *device;
int status;
dev_t dev;

/* 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 instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
Expand Down Expand Up @@ -647,6 +684,7 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
return status;
}

#ifdef USBF_HID_INCLUDED
static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_hidg *hidg = func_to_hidg(f);
Expand All @@ -666,29 +704,8 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(hidg);
}

/*-------------------------------------------------------------------------*/
/* 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 __init hidg_bind_config(struct usb_configuration *c,
struct hidg_func_descriptor *fdesc, int index)
{
Expand All @@ -698,15 +715,6 @@ int __init hidg_bind_config(struct usb_configuration *c,
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)
Expand Down Expand Up @@ -743,7 +751,153 @@ int __init hidg_bind_config(struct usb_configuration *c,
return status;
}

int __init ghid_setup(struct usb_gadget *g, int count)
#else

static inline int hidg_get_minor(void)
{
int ret;

ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL);

return ret;
}

static inline void hidg_put_minor(int minor)
{
ida_simple_remove(&hidg_ida, minor);
}

static void hidg_free_inst(struct usb_function_instance *f)
{
struct f_hid_opts *opts;

opts = container_of(f, struct f_hid_opts, func_inst);

mutex_lock(&hidg_ida_lock);

hidg_put_minor(opts->minor);
if (idr_is_empty(&hidg_ida.idr))
ghid_cleanup();

mutex_unlock(&hidg_ida_lock);

if (opts->report_desc_alloc)
kfree(opts->report_desc);

kfree(opts);
}

static struct usb_function_instance *hidg_alloc_inst(void)
{
struct f_hid_opts *opts;
struct usb_function_instance *ret;
int status = 0;

opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);

opts->func_inst.free_func_inst = hidg_free_inst;
ret = &opts->func_inst;

mutex_lock(&hidg_ida_lock);

if (idr_is_empty(&hidg_ida.idr)) {
status = ghid_setup(NULL, HIDG_MINORS);
if (status) {
ret = ERR_PTR(status);
kfree(opts);
goto unlock;
}
}

opts->minor = hidg_get_minor();
if (opts->minor < 0) {
ret = ERR_PTR(opts->minor);
kfree(opts);
if (idr_is_empty(&hidg_ida.idr))
ghid_cleanup();
}

unlock:
mutex_unlock(&hidg_ida_lock);
return ret;
}

static void hidg_free(struct usb_function *f)
{
struct f_hidg *hidg;

hidg = func_to_hidg(f);
kfree(hidg->report_desc);
kfree(hidg);
}

static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_hidg *hidg = func_to_hidg(f);

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);
kfree(hidg->req->buf);
usb_ep_free_request(hidg->in_ep, hidg->req);

usb_free_all_descriptors(f);
}

struct usb_function *hidg_alloc(struct usb_function_instance *fi)
{
struct f_hidg *hidg;
struct f_hid_opts *opts;

/* allocate and initialize one new instance */
hidg = kzalloc(sizeof(*hidg), GFP_KERNEL);
if (!hidg)
return ERR_PTR(-ENOMEM);

opts = container_of(fi, struct f_hid_opts, func_inst);

hidg->minor = opts->minor;
hidg->bInterfaceSubClass = opts->subclass;
hidg->bInterfaceProtocol = opts->protocol;
hidg->report_length = opts->report_length;
hidg->report_desc_length = opts->report_desc_length;
if (opts->report_desc) {
hidg->report_desc = kmemdup(opts->report_desc,
opts->report_desc_length,
GFP_KERNEL);
if (!hidg->report_desc) {
kfree(hidg);
return ERR_PTR(-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;
hidg->func.free_func = hidg_free;

/* this could me made configurable at some point */
hidg->qlen = 4;

return &hidg->func;
}

DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fabien Chouteau");

#endif

int ghid_setup(struct usb_gadget *g, int count)
{
int status;
dev_t dev;
Expand Down
35 changes: 35 additions & 0 deletions drivers/usb/gadget/function/u_hid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* u_hid.h
*
* Utility definitions for the hid function
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

#ifndef U_HID_H
#define U_HID_H

#include <linux/usb/composite.h>

struct f_hid_opts {
struct usb_function_instance func_inst;
int minor;
unsigned char subclass;
unsigned char protocol;
unsigned short report_length;
unsigned short report_desc_length;
unsigned char *report_desc;
bool report_desc_alloc;
};

int ghid_setup(struct usb_gadget *g, int count);
void ghid_cleanup(void);

#endif /* U_HID_H */
1 change: 1 addition & 0 deletions drivers/usb/gadget/legacy/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
#define USBF_HID_INCLUDED
#include "f_hid.c"


Expand Down

0 comments on commit cb38253

Please sign in to comment.