diff --git a/Documentation/ABI/testing/sysfs-class-android_usb b/Documentation/ABI/testing/sysfs-class-android_usb new file mode 100644 index 000000000000..3f8131e2b0e4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-android_usb @@ -0,0 +1,16 @@ +Android USB devices (eg. /sys/class/android_usb/android0/) + +What: /sys/class/android_usb//state +Date: Feb 2024 +Contact: Neill Kapron +Description: + The state of the USB connection. This attribute is likely + redundant with the /sys/class/UDC/state attribute, and should + be deprecated/removed when userspace can be refactored. + Change on the state will also generate uevent KOBJ_CHANGE on + the port with the new state included in the message as + "USB_STATE=". Note this is not the correct usage of + uevents, but necessary due to the requirement to maintaine + userspace API compatibility. + + Valid values: CONNECTED, DISCONNECTED, CONFIGURED diff --git a/arch/arm64/configs/gki_defconfig b/arch/arm64/configs/gki_defconfig index 962973fae9cb..866fab892987 100644 --- a/arch/arm64/configs/gki_defconfig +++ b/arch/arm64/configs/gki_defconfig @@ -496,6 +496,7 @@ CONFIG_USB_SERIAL=m CONFIG_USB_SERIAL_FTDI_SIO=m CONFIG_USB_GADGET=y CONFIG_USB_CONFIGFS=y +CONFIG_ANDROID_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_SERIAL=y CONFIG_USB_CONFIGFS_ACM=y CONFIG_USB_CONFIGFS_NCM=y diff --git a/arch/x86/configs/gki_defconfig b/arch/x86/configs/gki_defconfig index 164dbb049f99..05aee6e59767 100644 --- a/arch/x86/configs/gki_defconfig +++ b/arch/x86/configs/gki_defconfig @@ -465,6 +465,7 @@ CONFIG_USB_SERIAL=m CONFIG_USB_SERIAL_FTDI_SIO=m CONFIG_USB_GADGET=y CONFIG_USB_CONFIGFS=y +CONFIG_ANDROID_USB_CONFIGFS_UEVENT=y CONFIG_USB_CONFIGFS_SERIAL=y CONFIG_USB_CONFIGFS_ACM=y CONFIG_USB_CONFIGFS_NCM=y diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 566ff0b1282a..3ce0f547719d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -235,6 +235,17 @@ config USB_CONFIGFS appropriate symbolic links. For more information see Documentation/usb/gadget_configfs.rst. +config ANDROID_USB_CONFIGFS_UEVENT + bool "Uevent notification of Gadget State" + depends on USB_CONFIGFS + help + Enable uevent notifications to userspace when gadget state changes. + The gadget can be in any of the following three states: + "CONNECTED", "DISCONNECTED" or "CONFIGURED". + Additionally, selecting this will create the android_usb class of + devices, including a "state" attribute for the android_device which + shows the gadget state. + config USB_CONFIGFS_SERIAL bool "Generic serial bulk in/out" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 33f1ef91b046..370b393032f4 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -8,5 +8,6 @@ subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG 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 +libcomposite-$(CONFIG_ANDROID_USB_CONFIGFS_UEVENT) += android_configfs_uevent.o obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/android_configfs_uevent.c b/drivers/usb/gadget/android_configfs_uevent.c new file mode 100644 index 000000000000..5cd2de61000f --- /dev/null +++ b/drivers/usb/gadget/android_configfs_uevent.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2011-2024 Google LLC + */ +#include "android_configfs_uevent.h" +#include +#include +#include +#include +#include + +static struct android_uevent_opts *android_opts; + +static DEFINE_SPINLOCK(opts_lock); +static DEFINE_IDA(android_ida); + +static void android_work(struct work_struct *data) +{ + struct android_uevent_opts *opts = container_of(data, + struct android_uevent_opts, work); + + char *disconnected_strs[2] = { "USB_STATE=DISCONNECTED", NULL }; + char *connected_strs[2] = { "USB_STATE=CONNECTED", NULL }; + char *configured_strs[2] = { "USB_STATE=CONFIGURED", NULL }; + unsigned long flags; + bool disconnected = false; + bool connected = false; + bool configured = false; + bool uevent_sent = false; + struct device *dev; + + /* + * I believe locking is important due to the fact that we are checking + * several conditions here, and if the state changes after checking one + * we could potentially drop a uevent to userspace. Additionally, we + * want to prevent teardown until after events are sent. + */ + spin_lock_irqsave(&opts_lock, flags); + + /* + * If the device does not exist, it means we were torn down after + * scheduling this work, but before the work ran, so return to prevent + * use after free. + */ + if (!opts->dev) { + spin_unlock_irqrestore(&opts_lock, flags); + return; + } + + /* + * Cache the dev pointer in the locked area incase it gets cleared by + * android_device_destroy() after we release the lock. The call to + * flush_work in the cleanup path ensures we finish our work prior to + * destroying the dev which we have cached the pointer to. Ideally, + * this would be handled differently (using reference counting), but + * for now this should work. + */ + dev = opts->dev; + + if (opts->connected != opts->sw_connected) { + if (opts->connected) + connected = true; + else + disconnected = true; + opts->sw_connected = opts->connected; + } + if (opts->configured) + configured = true; + + spin_unlock_irqrestore(&opts_lock, flags); + + /* + * This is an abuse of uevents, however the android userspace parses + * the uevent string for information instead of reading the state from + * sysfs entries. This is one of several things about this driver which + * would need to change to upstream it. In an attempt to keep the + * exising userspace api unmodified until either an upstream solution + * is implemented or this functionality is otherwise replaced, leave + * the pre-existing logic in place. + */ + if (connected) { + if (kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, + connected_strs)) { + dev_err(dev, "Failed to send connected uevent\n"); + } else { + dev_dbg(dev, "sent uevent %s\n", connected_strs[0]); + uevent_sent = true; + } + } + + if (configured) { + if (kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, + configured_strs)) { + dev_err(dev, "Failed to send configured uevent\n"); + } else { + dev_dbg(dev, "sent uevent %s\n", configured_strs[0]); + uevent_sent = true; + } + } + + if (disconnected) { + if (kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, + disconnected_strs)) { + dev_err(dev, "Failed to send disconnected uevent\n"); + } else { + dev_dbg(dev, "sent uevent %s\n", disconnected_strs[0]); + uevent_sent = true; + } + } + + if (!uevent_sent) { + /* + * This is an odd case, but not necessarily an error- the state + * of the device may have changed since the work was scheduled, + * and if the state changed, there is likely another scheduled + * work which will send a uevent. + */ + dev_dbg(dev, "did not send uevent\n"); + } +} + +static ssize_t state_show(struct device *pdev, + struct device_attribute *attr, + char *buf) +{ + struct android_uevent_opts *opts = dev_get_drvdata(pdev); + char *state = "DISCONNECTED"; + + if (opts->configured) + state = "CONFIGURED"; + else if (opts->connected) + state = "CONNECTED"; + + return sysfs_emit(buf, "%s\n", state); +} +static DEVICE_ATTR_RO(state); + +static struct attribute *android_usb_attrs[] = { + &dev_attr_state.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(android_usb); + +static struct class android_usb_class = { + .name = "android_usb", + .dev_groups = android_usb_groups, +}; + +int android_class_create(void) +{ + return class_register(&android_usb_class); +} +EXPORT_SYMBOL_GPL(android_class_create); + +void android_class_destroy(void) +{ + class_unregister(&android_usb_class); +} +EXPORT_SYMBOL_GPL(android_class_destroy); + +int android_device_create(struct android_uevent_opts *opts) +{ + unsigned long flags; + struct device *dev; + + spin_lock_irqsave(&opts_lock, flags); + INIT_WORK(&opts->work, android_work); + + opts->device_id = ida_alloc(&android_ida, GFP_ATOMIC); + //Unlock prior to calling device_create() since it may sleep + spin_unlock_irqrestore(&opts_lock, flags); + if (opts->device_id < 0) + return opts->device_id; + + dev = device_create(&android_usb_class, NULL, MKDEV(0, 0), + opts, "android%d", opts->device_id); + + spin_lock_irqsave(&opts_lock, flags); + if (IS_ERR(dev)) { + ida_free(&android_ida, opts->device_id); + opts->device_id = -1; + spin_unlock_irqrestore(&opts_lock, flags); + return PTR_ERR(dev); + } + opts->dev = dev; + ida_init(&opts->function_ida); + if (!android_opts) + android_opts = opts; + spin_unlock_irqrestore(&opts_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(android_device_create); + +void android_device_destroy(struct android_uevent_opts *opts) +{ + unsigned long flags; + struct device *dev; + + /* + * This scheme is used to safely cleanup any remaining work. Once + * opts->dev is set to NULL, any newly scheduled work will return + * after getting the lock and checking for NULL. Any currently + * running work finishes with the flush_work (the worker caches + * opts->dev so it can continue), before we free the device. + * + * Ideally, this cleanup would be handled via reference counting, but + * there are nuances around device destroy (or the fact that we are + * currently statically allocating opts) which prevent this from + * being implemented without a significant refactor. + */ + spin_lock_irqsave(&opts_lock, flags); + dev = opts->dev; + opts->dev = NULL; + spin_unlock_irqrestore(&opts_lock, flags); + + flush_work(&opts->work); + + spin_lock_irqsave(&opts_lock, flags); + if (opts->device_id >= 0) + ida_free(&android_ida, opts->device_id); + + android_opts = NULL; + ida_destroy(&opts->function_ida); + device_destroy(dev->class, dev->devt); + spin_unlock_irqrestore(&opts_lock, flags); +} +EXPORT_SYMBOL_GPL(android_device_destroy); + +void __android_set_connected(struct android_uevent_opts *opts, + bool connected) +{ + unsigned long flags; + + spin_lock_irqsave(&opts_lock, flags); + // Don't send the uevent if connected state is not changed + if (opts->connected != connected) { + opts->connected = connected; + schedule_work(&opts->work); + } + spin_unlock_irqrestore(&opts_lock, flags); +} + +void __android_set_configured(struct android_uevent_opts *opts, + bool configured) +{ + unsigned long flags; + + spin_lock_irqsave(&opts_lock, flags); + // Don't send the uevent if configure state is not changed + if (opts->configured != configured) { + opts->configured = configured; + schedule_work(&opts->work); + } + spin_unlock_irqrestore(&opts_lock, flags); +} + +void android_set_connected(struct android_uevent_opts *opts) +{ + __android_set_connected(opts, true); +} +EXPORT_SYMBOL_GPL(android_set_connected); + +void android_set_disconnected(struct android_uevent_opts *opts) +{ + __android_set_connected(opts, false); +} +EXPORT_SYMBOL_GPL(android_set_disconnected); + +void android_set_configured(struct android_uevent_opts *opts) +{ + __android_set_configured(opts, true); +} +EXPORT_SYMBOL_GPL(android_set_configured); + +void android_set_unconfigured(struct android_uevent_opts *opts) +{ + __android_set_configured(opts, false); +} +EXPORT_SYMBOL_GPL(android_set_unconfigured); + +struct device *android_create_function_device(char *name, void *drvdata, + const struct attribute_group **groups) +{ + struct android_uevent_opts *opts; + struct device *dev; + unsigned long flags; + int id; + + spin_lock_irqsave(&opts_lock, flags); + opts = android_opts; + if (IS_ERR_OR_NULL(opts) || IS_ERR_OR_NULL(opts->dev)) { + spin_unlock_irqrestore(&opts_lock, flags); + return ERR_PTR(-ENODEV); + } + + id = ida_alloc(&opts->function_ida, GFP_ATOMIC); + if (id < 0) { + spin_unlock_irqrestore(&opts_lock, flags); + return ERR_PTR(id); + } + // device_create_with_groups can sleep, so we must unlock first + spin_unlock_irqrestore(&opts_lock, flags); + dev = device_create_with_groups(&android_usb_class, opts->dev, + MKDEV(0, id), drvdata, groups, name); + return dev; +} +EXPORT_SYMBOL_GPL(android_create_function_device); + +void android_remove_function_device(struct device *dev) +{ + struct android_uevent_opts *opts; + unsigned long flags; + + device_destroy(&android_usb_class, dev->devt); + + spin_lock_irqsave(&opts_lock, flags); + opts = android_opts; + if (IS_ERR_OR_NULL(opts)) { + spin_unlock_irqrestore(&opts_lock, flags); + return; + } + + ida_free(&opts->function_ida, MINOR(dev->devt)); + spin_unlock_irqrestore(&opts_lock, flags); +} +EXPORT_SYMBOL_GPL(android_remove_function_device); diff --git a/drivers/usb/gadget/android_configfs_uevent.h b/drivers/usb/gadget/android_configfs_uevent.h new file mode 100644 index 000000000000..6a56efd74888 --- /dev/null +++ b/drivers/usb/gadget/android_configfs_uevent.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2024 Google LLC + */ +#ifndef __ANDROID_CONFIGFS_UEVENT_H +#define __ANDROID_CONFIGFS_UEVENT_H + +#ifdef CONFIG_ANDROID_USB_CONFIGFS_UEVENT +#include + +/** + * android_class_create - essentially the __init() function for the + * configfs_uevent library, since it is not a standalone driver. + * + * Creates the android_usb class of device + * + * Returns: the result of class_register (0 for success, err otherwise) + */ +int android_class_create(void); + +/** + * android_class_destroy - essentially the __exit() function for the + * configfs_uevent library, since it is not a standalone driver. + * + * Removes the android_usb class of devices and performs any necessary + * cleanup. + */ +void android_class_destroy(void); + +/** + * android_device_create - Creates an android device instance and + * a state attribute file which can be read to determine the state of the + * usb gadget. + * @opts: contextual data for the configfs_uevent library. + * + * Note: the state file created by this function mimics the functionaltiy + * of the UDC driver and is likely redundant, but maintained for legacy + * support. + * + * The state can be one of "DISCONNECTED", "CONNECTED", or "CONFIGURED" + * + * Returns: 0 for success, or if an error is encountered during ida_allocation + * or device_creation, that error is returned. + */ +int android_device_create(struct android_uevent_opts *opts); + +/** + * android_device_destroy - Removes the android device instance and performs + * any necessary cleanup. + * @opts: contextual data for the configfs_uevent library. + */ +void android_device_destroy(struct android_uevent_opts *opts); + +/** + * android_set_connected - set the internal state of android_uevent_opts to + * connected and schedule the work to emit a uevent with this status update. + * @opts: contextual data for the configfs_uevent library + * + * This should be called by the gadget composite driver when a usb_ctrlrequest + * is received by the gadget driver. + * + * This function locks the android specific android_uevent_opts->lock and + * therefore should not require locking the containing composite device + * structure as the internal lock is also used in the teardown path of the + * composite driver in android_device_destroy(). + */ +void android_set_connected(struct android_uevent_opts *opts); + +/** + * android_set_disconnected - reset the internal state of android_uevent_opts to + * disconnected and schedule the work to emit a uevent with this status update. + * @opts: contextual data for the configfs_uevent library + * + * This should be called by the gadget composite driver when the link is + * disconnected. + * + * This function locks the android specific android_uevent_opts->lock and + * therefore should not require locking the containing composite device + * structure as the internal lock is also used in the teardown path of the + * composite driver in android_device_destroy(). + */ +void android_set_disconnected(struct android_uevent_opts *opts); + +/** + * android_set_configured - set the internal state of android_uevent_opts to + * configured and schedule the work to emit a uevent with this status update. + * @opts: contextual data for the configfs_uevent library + * + * This should be called by the gadget composite driver when the configuration + * is applied to the gadget composite device + * + * This function locks the android specific android_uevent_opts->lock and + * therefore should not require locking the containing composite device + * structure as the internal lock is also used in the teardown path of the + * composite driver in android_device_destroy(). + */ +void android_set_configured(struct android_uevent_opts *opts); + +/** + * android_set_unconfigured - reset the internal state of android_uevent_opts to + * unconfigured and schedule the work to emit a uevent with this status update. + * @opts: contextual data for the configfs_uevent library + * + * This should be called by the gadget composite driver when the gadget + * configuration is torn down. + * + * This function locks the android specific android_uevent_opts->lock and + * therefore should not require locking the containing composite device + * structure as the internal lock is also used in the teardown path of the + * composite driver in android_device_destroy(). + */ +void android_set_unconfigured(struct android_uevent_opts *opts); + +#else + +static inline int android_class_create(void) +{ + return 0; +} + +static inline void android_class_destroy(void) +{ +} + +static inline int android_device_create(struct android_uevent_opts *opts) +{ + return 0; +} + +static inline void android_device_destroy(struct android_uevent_opts *opts) +{ +} + +static inline void android_set_connected(struct android_uevent_opts *opts) +{ +} + +static inline void android_set_disconnected(struct android_uevent_opts *opts) +{ +} + +static inline void android_set_configured(struct android_uevent_opts *opts) +{ +} + +static inline void android_set_unconfigured(struct android_uevent_opts *opts) +{ +} +#endif /* CONFIG_ANDROID_USB_CONFIGFS_UEVENT */ +#endif /* __ANDROID_CONFIGFS_UEVENT_H */ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 0e151b54aae8..dd794a723070 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -22,6 +22,7 @@ #include #include "u_os_desc.h" +#include "android_configfs_uevent.h" /** * struct usb_os_string - represents OS String to be reported by a gadget @@ -941,6 +942,7 @@ static void reset_config(struct usb_composite_dev *cdev) bitmap_zero(f->endpoints, 32); } cdev->config = NULL; + android_set_unconfigured(&cdev->android_opts); cdev->delayed_status = 0; } @@ -1788,6 +1790,8 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) struct usb_function *iter; u8 endp; + android_set_connected(&cdev->android_opts); + if (w_length > USB_COMP_EP0_BUFSIZ) { if (ctrl->bRequestType & USB_DIR_IN) { /* Cast away the const, we are going to overwrite on purpose. */ @@ -1922,6 +1926,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) spin_lock(&cdev->lock); value = set_config(cdev, ctrl, w_value); spin_unlock(&cdev->lock); + android_set_configured(&cdev->android_opts); break; case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != USB_DIR_IN) @@ -2299,6 +2304,8 @@ static void __composite_disconnect(struct usb_gadget *gadget) struct usb_composite_dev *cdev = get_gadget_data(gadget); unsigned long flags; + android_set_disconnected(&cdev->android_opts); + /* REVISIT: should we have config and device level * disconnect callbacks? */ diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index ce3cfa1f36f5..560b05e2f243 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -9,6 +9,7 @@ #include #include #include "configfs.h" +#include "android_configfs_uevent.h" #include "u_f.h" #include "u_os_desc.h" @@ -283,7 +284,11 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, mutex_lock(&gi->lock); - if (!strlen(name)) { + /* + * ANDROID: Not exactly sure why we need this "none", but worried it + * would break something if removed. + */ + if (!strlen(name) || strcmp(name, "none") == 0) { ret = unregister_gadget(gi); if (ret) goto err; @@ -2034,10 +2039,16 @@ static struct config_group *gadgets_make( if (!gi->composite.gadget_driver.function) goto out_free_driver_name; + if (android_device_create(&gi->cdev.android_opts)) + goto out_free_driver_name_and_function; + return &gi->group; out_free_driver_name: kfree(gi->composite.gadget_driver.driver.name); +out_free_driver_name_and_function: + kfree(gi->composite.gadget_driver.driver.name); + kfree(gi->composite.gadget_driver.function); err: kfree(gi); return ERR_PTR(-ENOMEM); @@ -2045,6 +2056,10 @@ 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); + android_device_destroy(&gi->cdev.android_opts); config_item_put(item); } @@ -2084,7 +2099,13 @@ static int __init gadget_cfs_init(void) config_group_init(&gadget_subsys.su_group); + ret = android_class_create(); + if (ret) + return ret; + ret = configfs_register_subsystem(&gadget_subsys); + if (ret) + android_class_destroy(); return ret; } module_init(gadget_cfs_init); @@ -2092,5 +2113,6 @@ module_init(gadget_cfs_init); static void __exit gadget_cfs_exit(void) { configfs_unregister_subsystem(&gadget_subsys); + android_class_destroy(); } module_exit(gadget_cfs_exit); diff --git a/include/linux/usb/android_configfs_uevent.h b/include/linux/usb/android_configfs_uevent.h new file mode 100644 index 000000000000..07b82dda420b --- /dev/null +++ b/include/linux/usb/android_configfs_uevent.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2024 Google LLC + */ +#ifndef _ANDROID_USB_CONFIGFS_UEVENT_H +#define _ANDROID_USB_CONFIGFS_UEVENT_H + +#ifdef CONFIG_ANDROID_USB_CONFIGFS_UEVENT +#include +#include +#include + +struct android_uevent_opts { + struct device *dev; + int device_id; + bool connected; + bool configured; + bool sw_connected; + struct work_struct work; + struct ida function_ida; +}; + +/** + * android_create_function_device - creates a device within the android_usb + * class with a new minor number. + * @name: the name for the device which is to be created + * @drvdata: the data to be added to the device for callbacks, can be NULL + * @groups: NULL-terminated list of attribute groups to be created, can be NULL + * + * This should be called by function drivers which wish to register a device + * within the android_usb class. + * + * Returns: a pointer to the newly created device upon success, or an ERR_PTR + * for the encountered error. + */ +struct device *android_create_function_device(char *name, void *drvdata, + const struct attribute_group **groups); + +/** + * android_remove_function_device - destroys a device which was created by + * calling android_create_function_device, and performs any necessary cleanup. + * @dev: the device to be destroyed + */ +void android_remove_function_device(struct device *dev); +#else + +struct android_uevent_opts {}; + +static inline struct device *android_create_function_device(char *name) +{ + return ERR_PTR(-ENODEV); +} + +static inline void android_remove_function_device(struct device *dev) +{ +} +#endif /* CONFIG_ANDROID_USB_CONFIGFS_UEVENT */ +#endif /* _ANDROID_USB_CONFIGFS_UEVENT_H */ diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index af3cd2aae4bc..5d4666e5a4b9 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -28,6 +28,7 @@ #include #include #include +#include /* * USB function drivers should return USB_GADGET_DELAYED_STATUS if they @@ -502,6 +503,8 @@ struct usb_composite_dev { /* protects deactivations and delayed_status counts*/ spinlock_t lock; + struct android_uevent_opts android_opts; + /* public: */ unsigned int setup_pending:1; unsigned int os_desc_pending:1;