usb: gadget: Accessory:Migrate to USB_FUNCTION API
This patch adds support to use Android accessory gadget function through the DECLARE_USB_FUNCTION_INIT interface. Signed-off-by: Badhri Jagan Sridharan <badhri@google.com> Change-Id: Ib352752d5bc905fa1df9049b53eabf1294930db7
This commit is contained in:
parent
9c5cdf4ddd
commit
8cc9024964
3 changed files with 164 additions and 4 deletions
|
@ -208,6 +208,9 @@ config USB_F_PTP
|
|||
config USB_F_AUDIO_SRC
|
||||
tristate
|
||||
|
||||
config USB_F_ACC
|
||||
tristate
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
default USB_ETH
|
||||
|
@ -394,9 +397,16 @@ config USB_CONFIGFS_F_PTP
|
|||
help
|
||||
USB gadget PTP support
|
||||
|
||||
config USB_CONFIGFS_F_ACC
|
||||
boolean "Accessory gadget"
|
||||
depends on USB_CONFIGFS
|
||||
select USB_F_ACC
|
||||
help
|
||||
USB gadget Accessory support
|
||||
|
||||
config USB_CONFIGFS_F_AUDIO_SRC
|
||||
boolean "Audio Source gadget"
|
||||
depends on USB_CONFIGFS
|
||||
depends on USB_CONFIGFS && USB_CONFIGFS_F_ACC
|
||||
select USB_F_AUDIO_SRC
|
||||
help
|
||||
USB gadget Audio Source support
|
||||
|
|
|
@ -9,4 +9,7 @@ obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
|
|||
libcomposite-y := usbstring.o config.o epautoconf.o
|
||||
libcomposite-y += composite.o functions.o configfs.o u_f.o
|
||||
|
||||
usb_f_accessory-y := f_accessory.o
|
||||
obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o
|
||||
|
||||
obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/
|
||||
|
|
|
@ -39,6 +39,10 @@
|
|||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/f_accessory.h>
|
||||
|
||||
#include <linux/configfs.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#define MAX_INST_NAME_LEN 40
|
||||
#define BULK_BUFFER_SIZE 16384
|
||||
#define ACC_STRING_SIZE 256
|
||||
|
||||
|
@ -194,6 +198,11 @@ static struct usb_gadget_strings *acc_strings[] = {
|
|||
/* temporary variable used between acc_open() and acc_gadget_bind() */
|
||||
static struct acc_dev *_acc_dev;
|
||||
|
||||
struct acc_instance {
|
||||
struct usb_function_instance func_inst;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static inline struct acc_dev *func_to_dev(struct usb_function *f)
|
||||
{
|
||||
return container_of(f, struct acc_dev, function);
|
||||
|
@ -775,7 +784,7 @@ static struct hid_driver acc_hid_driver = {
|
|||
.probe = acc_hid_probe,
|
||||
};
|
||||
|
||||
static int acc_ctrlrequest(struct usb_composite_dev *cdev,
|
||||
int acc_ctrlrequest(struct usb_composite_dev *cdev,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
struct acc_dev *dev = _acc_dev;
|
||||
|
@ -879,9 +888,11 @@ err:
|
|||
w_value, w_index, w_length);
|
||||
return value;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acc_ctrlrequest);
|
||||
|
||||
static int
|
||||
acc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
__acc_function_bind(struct usb_configuration *c,
|
||||
struct usb_function *f, bool configfs)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct acc_dev *dev = func_to_dev(f);
|
||||
|
@ -890,6 +901,16 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
|
||||
DBG(cdev, "acc_function_bind dev: %p\n", dev);
|
||||
|
||||
if (configfs) {
|
||||
if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) {
|
||||
ret = usb_string_id(c->cdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
acc_string_defs[INTERFACE_STRING_INDEX].id = ret;
|
||||
acc_interface_desc.iInterface = ret;
|
||||
}
|
||||
dev->cdev = c->cdev;
|
||||
}
|
||||
ret = hid_register_driver(&acc_hid_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -922,6 +943,17 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
acc_function_bind(struct usb_configuration *c, struct usb_function *f) {
|
||||
return __acc_function_bind(c, f, false);
|
||||
}
|
||||
|
||||
static int
|
||||
acc_function_bind_configfs(struct usb_configuration *c,
|
||||
struct usb_function *f) {
|
||||
return __acc_function_bind(c, f, true);
|
||||
}
|
||||
|
||||
static void
|
||||
kill_all_hid_devices(struct acc_dev *dev)
|
||||
{
|
||||
|
@ -1179,11 +1211,12 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void acc_disconnect(void)
|
||||
void acc_disconnect(void)
|
||||
{
|
||||
/* unregister all HID devices if USB is disconnected */
|
||||
kill_all_hid_devices(_acc_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acc_disconnect);
|
||||
|
||||
static void acc_cleanup(void)
|
||||
{
|
||||
|
@ -1191,3 +1224,117 @@ static void acc_cleanup(void)
|
|||
kfree(_acc_dev);
|
||||
_acc_dev = NULL;
|
||||
}
|
||||
static struct acc_instance *to_acc_instance(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct acc_instance,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
static void acc_attr_release(struct config_item *item)
|
||||
{
|
||||
struct acc_instance *fi_acc = to_acc_instance(item);
|
||||
|
||||
usb_put_function_instance(&fi_acc->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations acc_item_ops = {
|
||||
.release = acc_attr_release,
|
||||
};
|
||||
|
||||
static struct config_item_type acc_func_type = {
|
||||
.ct_item_ops = &acc_item_ops,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct acc_instance *to_fi_acc(struct usb_function_instance *fi)
|
||||
{
|
||||
return container_of(fi, struct acc_instance, func_inst);
|
||||
}
|
||||
|
||||
static int acc_set_inst_name(struct usb_function_instance *fi, const char *name)
|
||||
{
|
||||
struct acc_instance *fi_acc;
|
||||
char *ptr;
|
||||
int name_len;
|
||||
|
||||
name_len = strlen(name) + 1;
|
||||
if (name_len > MAX_INST_NAME_LEN)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
ptr = kstrndup(name, name_len, GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
fi_acc = to_fi_acc(fi);
|
||||
fi_acc->name = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acc_free_inst(struct usb_function_instance *fi)
|
||||
{
|
||||
struct acc_instance *fi_acc;
|
||||
|
||||
fi_acc = to_fi_acc(fi);
|
||||
kfree(fi_acc->name);
|
||||
acc_cleanup();
|
||||
}
|
||||
|
||||
static struct usb_function_instance *acc_alloc_inst(void)
|
||||
{
|
||||
struct acc_instance *fi_acc;
|
||||
struct acc_dev *dev;
|
||||
int err;
|
||||
|
||||
fi_acc = kzalloc(sizeof(*fi_acc), GFP_KERNEL);
|
||||
if (!fi_acc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
fi_acc->func_inst.set_inst_name = acc_set_inst_name;
|
||||
fi_acc->func_inst.free_func_inst = acc_free_inst;
|
||||
|
||||
err = acc_setup();
|
||||
if (err) {
|
||||
kfree(fi_acc);
|
||||
pr_err("Error setting ACCESSORY\n");
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
config_group_init_type_name(&fi_acc->func_inst.group,
|
||||
"", &acc_func_type);
|
||||
dev = _acc_dev;
|
||||
return &fi_acc->func_inst;
|
||||
}
|
||||
|
||||
static void acc_free(struct usb_function *f)
|
||||
{
|
||||
/*NO-OP: no function specific resource allocation in mtp_alloc*/
|
||||
}
|
||||
|
||||
int acc_ctrlrequest_configfs(struct usb_function *f,
|
||||
const struct usb_ctrlrequest *ctrl) {
|
||||
if (f->config != NULL && f->config->cdev != NULL)
|
||||
return acc_ctrlrequest(f->config->cdev, ctrl);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct usb_function *acc_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct acc_dev *dev = _acc_dev;
|
||||
|
||||
pr_info("acc_alloc\n");
|
||||
|
||||
dev->function.name = "accessory";
|
||||
dev->function.strings = acc_strings,
|
||||
dev->function.fs_descriptors = fs_acc_descs;
|
||||
dev->function.hs_descriptors = hs_acc_descs;
|
||||
dev->function.bind = acc_function_bind_configfs;
|
||||
dev->function.unbind = acc_function_unbind;
|
||||
dev->function.set_alt = acc_function_set_alt;
|
||||
dev->function.disable = acc_function_disable;
|
||||
dev->function.free_func = acc_free;
|
||||
dev->function.setup = acc_ctrlrequest_configfs;
|
||||
|
||||
return &dev->function;
|
||||
}
|
||||
DECLARE_USB_FUNCTION_INIT(accessory, acc_alloc_inst, acc_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
Loading…
Add table
Reference in a new issue