mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 03:15:31 +09:00
ANDROID: usb: gadget: configfs: Add Uevent to notify userspace
Android userspace UsbDeviceManager relies on the uevents generated by the composition driver to generate user notifications. This CL adds uevents to be generated whenever USB changes its state i.e. connected, disconnected, configured. This CL also intercepts the setup requests from the usb_core anb routes it to the specific usb function if required. Change-Id: I112662728edcc404e3f08fa0c2d9e4f7b17e1b43 Bug: 68755607 Bug: 120441124 [badhri: Migrate to using udc uevents from upstream sysfs.] Signed-off-by: Badhri Jagan Sridharan <badhri@google.com> [AmitP: Folded following android-4.9 commit changes into this patch9214c899f7("ANDROID: usb: gadget: configfs: handle gadget reset request for android")] Signed-off-by: Amit Pundir <amit.pundir@linaro.org> [astrachan: Folded change 5c899c9fd75d ("ANDROID: usb: gadget: configfs: fix null ptr in android_disconnect") into this patch] Signed-off-by: Alistair Strachan <astrachan@google.com> Signed-off-by: Frank Wang <frank.wang@rock-chips.com> (cherry picked from commit7a160e2b96https://android.googlesource.com/kernel/common)
This commit is contained in:
committed by
Tao Huang
parent
79b5303e24
commit
ac36012140
@@ -230,6 +230,14 @@ config USB_CONFIGFS
|
||||
appropriate symbolic links.
|
||||
For more information see Documentation/usb/gadget_configfs.rst.
|
||||
|
||||
config USB_CONFIGFS_UEVENT
|
||||
bool "Uevent notification of Gadget state"
|
||||
depends on USB_CONFIGFS
|
||||
help
|
||||
Enable uevent notifications to userspace when the gadget
|
||||
state changes. The gadget can be in any of the following
|
||||
three states: "CONNECTED/DISCONNECTED/CONFIGURED"
|
||||
|
||||
config USB_CONFIGFS_SERIAL
|
||||
bool "Generic serial bulk in/out"
|
||||
depends on USB_CONFIGFS
|
||||
|
||||
@@ -10,6 +10,32 @@
|
||||
#include "u_f.h"
|
||||
#include "u_os_desc.h"
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_UEVENT
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_F_ACC
|
||||
extern int acc_ctrlrequest_composite(struct usb_composite_dev *cdev,
|
||||
const struct usb_ctrlrequest *ctrl);
|
||||
void acc_disconnect(void);
|
||||
#endif
|
||||
static struct class *android_class;
|
||||
static struct device *android_device;
|
||||
static int index;
|
||||
static int gadget_index;
|
||||
|
||||
struct device *create_function_device(char *name)
|
||||
{
|
||||
if (android_device && !IS_ERR(android_device))
|
||||
return device_create(android_class, android_device,
|
||||
MKDEV(0, index++), NULL, name);
|
||||
else
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(create_function_device);
|
||||
#endif
|
||||
|
||||
int check_user_usb_string(const char *name,
|
||||
struct usb_gadget_strings *stringtab_dev)
|
||||
{
|
||||
@@ -51,6 +77,12 @@ struct gadget_info {
|
||||
char qw_sign[OS_STRING_QW_SIGN_LEN];
|
||||
spinlock_t spinlock;
|
||||
bool unbind;
|
||||
#ifdef CONFIG_USB_CONFIGFS_UEVENT
|
||||
bool connected;
|
||||
bool sw_connected;
|
||||
struct work_struct work;
|
||||
struct device *dev;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline struct gadget_info *to_gadget_info(struct config_item *item)
|
||||
@@ -273,7 +305,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
|
||||
|
||||
mutex_lock(&gi->lock);
|
||||
|
||||
if (!strlen(name)) {
|
||||
if (!strlen(name) || strcmp(name, "none") == 0) {
|
||||
ret = unregister_gadget(gi);
|
||||
if (ret)
|
||||
goto err;
|
||||
@@ -1427,6 +1459,57 @@ err_comp_cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_UEVENT
|
||||
static void android_work(struct work_struct *data)
|
||||
{
|
||||
struct gadget_info *gi = container_of(data, struct gadget_info, work);
|
||||
struct usb_composite_dev *cdev = &gi->cdev;
|
||||
char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL };
|
||||
char *connected[2] = { "USB_STATE=CONNECTED", NULL };
|
||||
char *configured[2] = { "USB_STATE=CONFIGURED", NULL };
|
||||
/* 0-connected 1-configured 2-disconnected*/
|
||||
bool status[3] = { false, false, false };
|
||||
unsigned long flags;
|
||||
bool uevent_sent = false;
|
||||
|
||||
spin_lock_irqsave(&cdev->lock, flags);
|
||||
if (cdev->config)
|
||||
status[1] = true;
|
||||
|
||||
if (gi->connected != gi->sw_connected) {
|
||||
if (gi->connected)
|
||||
status[0] = true;
|
||||
else
|
||||
status[2] = true;
|
||||
gi->sw_connected = gi->connected;
|
||||
}
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
|
||||
if (status[0]) {
|
||||
kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected);
|
||||
pr_info("%s: sent uevent %s\n", __func__, connected[0]);
|
||||
uevent_sent = true;
|
||||
}
|
||||
|
||||
if (status[1]) {
|
||||
kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured);
|
||||
pr_info("%s: sent uevent %s\n", __func__, configured[0]);
|
||||
uevent_sent = true;
|
||||
}
|
||||
|
||||
if (status[2]) {
|
||||
kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected);
|
||||
pr_info("%s: sent uevent %s\n", __func__, disconnected[0]);
|
||||
uevent_sent = true;
|
||||
}
|
||||
|
||||
if (!uevent_sent) {
|
||||
pr_info("%s: did not send uevent (%d %d %p)\n", __func__,
|
||||
gi->connected, gi->sw_connected, cdev->config);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void configfs_composite_unbind(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_composite_dev *cdev;
|
||||
@@ -1454,6 +1537,50 @@ static void configfs_composite_unbind(struct usb_gadget *gadget)
|
||||
spin_unlock_irqrestore(&gi->spinlock, flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_UEVENT
|
||||
static int android_setup(struct usb_gadget *gadget,
|
||||
const struct usb_ctrlrequest *c)
|
||||
{
|
||||
struct usb_composite_dev *cdev = get_gadget_data(gadget);
|
||||
unsigned long flags;
|
||||
struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
|
||||
int value = -EOPNOTSUPP;
|
||||
struct usb_function_instance *fi;
|
||||
|
||||
spin_lock_irqsave(&cdev->lock, flags);
|
||||
if (!gi->connected) {
|
||||
gi->connected = 1;
|
||||
schedule_work(&gi->work);
|
||||
}
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
list_for_each_entry(fi, &gi->available_func, cfs_list) {
|
||||
if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) {
|
||||
value = fi->f->setup(fi->f, c);
|
||||
if (value >= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_F_ACC
|
||||
if (value < 0)
|
||||
value = acc_ctrlrequest_composite(cdev, c);
|
||||
#endif
|
||||
|
||||
if (value < 0)
|
||||
value = composite_setup(gadget, c);
|
||||
|
||||
spin_lock_irqsave(&cdev->lock, flags);
|
||||
if (c->bRequest == USB_REQ_SET_CONFIGURATION &&
|
||||
cdev->config) {
|
||||
schedule_work(&gi->work);
|
||||
}
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#else // CONFIG_USB_CONFIGFS_UEVENT
|
||||
|
||||
static int configfs_composite_setup(struct usb_gadget *gadget,
|
||||
const struct usb_ctrlrequest *ctrl)
|
||||
{
|
||||
@@ -1479,6 +1606,8 @@ static int configfs_composite_setup(struct usb_gadget *gadget,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // CONFIG_USB_CONFIGFS_UEVENT
|
||||
|
||||
static void configfs_composite_disconnect(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_composite_dev *cdev;
|
||||
@@ -1489,6 +1618,14 @@ static void configfs_composite_disconnect(struct usb_gadget *gadget)
|
||||
if (!cdev)
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_F_ACC
|
||||
/*
|
||||
* accessory HID support can be active while the
|
||||
* accessory function is not actually enabled,
|
||||
* so we need to inform it when we are disconnected.
|
||||
*/
|
||||
acc_disconnect();
|
||||
#endif
|
||||
gi = container_of(cdev, struct gadget_info, cdev);
|
||||
spin_lock_irqsave(&gi->spinlock, flags);
|
||||
cdev = get_gadget_data(gadget);
|
||||
@@ -1497,6 +1634,10 @@ static void configfs_composite_disconnect(struct usb_gadget *gadget)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_UEVENT
|
||||
gi->connected = 0;
|
||||
schedule_work(&gi->work);
|
||||
#endif
|
||||
composite_disconnect(gadget);
|
||||
spin_unlock_irqrestore(&gi->spinlock, flags);
|
||||
}
|
||||
@@ -1571,10 +1712,13 @@ static const struct usb_gadget_driver configfs_driver_template = {
|
||||
.bind = configfs_composite_bind,
|
||||
.unbind = configfs_composite_unbind,
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_UEVENT
|
||||
.setup = android_setup,
|
||||
#else
|
||||
.setup = configfs_composite_setup,
|
||||
#endif
|
||||
.reset = configfs_composite_reset,
|
||||
.disconnect = configfs_composite_disconnect,
|
||||
|
||||
.suspend = configfs_composite_suspend,
|
||||
.resume = configfs_composite_resume,
|
||||
|
||||
@@ -1585,6 +1729,91 @@ static const struct usb_gadget_driver configfs_driver_template = {
|
||||
.match_existing_only = 1,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_UEVENT
|
||||
static ssize_t state_show(struct device *pdev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct gadget_info *dev = dev_get_drvdata(pdev);
|
||||
struct usb_composite_dev *cdev;
|
||||
char *state = "DISCONNECTED";
|
||||
unsigned long flags;
|
||||
|
||||
if (!dev)
|
||||
goto out;
|
||||
|
||||
cdev = &dev->cdev;
|
||||
|
||||
if (!cdev)
|
||||
goto out;
|
||||
|
||||
spin_lock_irqsave(&cdev->lock, flags);
|
||||
if (cdev->config)
|
||||
state = "CONFIGURED";
|
||||
else if (dev->connected)
|
||||
state = "CONNECTED";
|
||||
spin_unlock_irqrestore(&cdev->lock, flags);
|
||||
out:
|
||||
return sprintf(buf, "%s\n", state);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
|
||||
|
||||
static struct device_attribute *android_usb_attributes[] = {
|
||||
&dev_attr_state,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int android_device_create(struct gadget_info *gi)
|
||||
{
|
||||
struct device_attribute **attrs;
|
||||
struct device_attribute *attr;
|
||||
|
||||
INIT_WORK(&gi->work, android_work);
|
||||
gi->dev = device_create(android_class, NULL,
|
||||
MKDEV(0, 0), NULL, "android%d", gadget_index++);
|
||||
if (IS_ERR(gi->dev))
|
||||
return PTR_ERR(gi->dev);
|
||||
|
||||
dev_set_drvdata(gi->dev, gi);
|
||||
if (!android_device)
|
||||
android_device = gi->dev;
|
||||
|
||||
attrs = android_usb_attributes;
|
||||
while ((attr = *attrs++)) {
|
||||
int err;
|
||||
|
||||
err = device_create_file(gi->dev, attr);
|
||||
if (err) {
|
||||
device_destroy(gi->dev->class,
|
||||
gi->dev->devt);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void android_device_destroy(struct gadget_info *gi)
|
||||
{
|
||||
struct device_attribute **attrs;
|
||||
struct device_attribute *attr;
|
||||
|
||||
attrs = android_usb_attributes;
|
||||
while ((attr = *attrs++))
|
||||
device_remove_file(gi->dev, attr);
|
||||
device_destroy(gi->dev->class, gi->dev->devt);
|
||||
}
|
||||
#else
|
||||
static inline int android_device_create(struct gadget_info *gi)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void android_device_destroy(struct gadget_info *gi)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct config_group *gadgets_make(
|
||||
struct config_group *group,
|
||||
const char *name)
|
||||
@@ -1642,6 +1871,9 @@ static struct config_group *gadgets_make(
|
||||
if (!gi->composite.gadget_driver.function)
|
||||
goto out_free_driver_name;
|
||||
|
||||
if (android_device_create(gi) < 0)
|
||||
goto out_free_driver_name;
|
||||
|
||||
return &gi->group;
|
||||
|
||||
out_free_driver_name:
|
||||
@@ -1653,7 +1885,11 @@ err:
|
||||
|
||||
static void gadgets_drop(struct config_group *group, struct config_item *item)
|
||||
{
|
||||
struct gadget_info *gi;
|
||||
|
||||
gi = container_of(to_config_group(item), struct gadget_info, group);
|
||||
config_item_put(item);
|
||||
android_device_destroy(gi);
|
||||
}
|
||||
|
||||
static struct configfs_group_operations gadgets_ops = {
|
||||
@@ -1693,6 +1929,13 @@ static int __init gadget_cfs_init(void)
|
||||
config_group_init(&gadget_subsys.su_group);
|
||||
|
||||
ret = configfs_register_subsystem(&gadget_subsys);
|
||||
|
||||
#ifdef CONFIG_USB_CONFIGFS_UEVENT
|
||||
android_class = class_create(THIS_MODULE, "android_usb");
|
||||
if (IS_ERR(android_class))
|
||||
return PTR_ERR(android_class);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
module_init(gadget_cfs_init);
|
||||
@@ -1700,5 +1943,10 @@ module_init(gadget_cfs_init);
|
||||
static void __exit gadget_cfs_exit(void)
|
||||
{
|
||||
configfs_unregister_subsystem(&gadget_subsys);
|
||||
#ifdef CONFIG_USB_CONFIGFS_UEVENT
|
||||
if (!IS_ERR(android_class))
|
||||
class_destroy(android_class);
|
||||
#endif
|
||||
|
||||
}
|
||||
module_exit(gadget_cfs_exit);
|
||||
|
||||
Reference in New Issue
Block a user