(Sending this again, because I forgot to switch my mail client to plain-text-only last time). Why not do the mode switch from userspace? I thought we were trying to move all the mode-switching stuff in that direction..... Matt On Tue, Aug 27, 2019 at 4:14 PM Julius Werner <jwerner@xxxxxxxxxxxx> wrote: > > This patch adds a new "unusual" USB mass storage device driver. This > driver will be used for a virtual USB storage device presented by an > Android phone running the 'Chrome OS Recovery'* Android app. This app > uses the Android Open Accessory (AOA) API to talk directly to a USB host > attached to the phone. > > The AOA protocol requires the host to send a custom vendor command on > EP0 to "switch" the phone into "AOA mode" (making it drop off the bus > and reenumerate with different descriptors). The ums-cros-aoa driver is > just a small stub driver to send these vendor commands. It identifies > the device it should operate on by VID/PID passed in through a module > parameter (e.g. from the bootloader). After the phone is in AOA mode, > the normal USB mass storage stack will recognize it by its special > VID/PID like any other "unusual dev". An initializer function will > further double-check that the device is the device previously operated > on by ums-cros-aoa. > > *NOTE: The Android app is still under development and will be released > at a later date. I'm submitting this patch now so that the driver name > and module parameters can be set in stone already, because I have to > bake them into bootloader code that is not field-updatable. > > Signed-off-by: Julius Werner <jwerner@xxxxxxxxxxxx> > --- > drivers/usb/storage/Kconfig | 12 +++ > drivers/usb/storage/Makefile | 2 + > drivers/usb/storage/cros-aoa.c | 129 +++++++++++++++++++++++++++++ > drivers/usb/storage/initializers.c | 34 ++++++++ > drivers/usb/storage/initializers.h | 4 + > drivers/usb/storage/unusual_devs.h | 18 ++++ > 6 files changed, 199 insertions(+) > create mode 100644 drivers/usb/storage/cros-aoa.c > > diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig > index 59aad38b490a6..cc901ee2bb766 100644 > --- a/drivers/usb/storage/Kconfig > +++ b/drivers/usb/storage/Kconfig > @@ -184,6 +184,18 @@ config USB_STORAGE_ENE_UB6250 > To compile this driver as a module, choose M here: the > module will be called ums-eneub6250. > > +config USB_STORAGE_CROS_AOA > + tristate "Support for connecting to Chrome OS Recovery Android app" > + default n > + depends on USB_STORAGE > + help > + Say Y here if you want to connect an Android phone running the Chrome > + OS Recovery app to this device and mount the image served by that app > + as a virtual storage device. Unless you're building for Chrome OS, you > + probably want to say N. > + > + If this driver is compiled as a module, it will be named ums-cros-aoa. > + > endif # USB_STORAGE > > config USB_UAS > diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile > index a67ddcbb4e249..f734741d4658b 100644 > --- a/drivers/usb/storage/Makefile > +++ b/drivers/usb/storage/Makefile > @@ -17,6 +17,7 @@ usb-storage-y += usual-tables.o > usb-storage-$(CONFIG_USB_STORAGE_DEBUG) += debug.o > > obj-$(CONFIG_USB_STORAGE_ALAUDA) += ums-alauda.o > +obj-$(CONFIG_USB_STORAGE_CROS_AOA) += ums-cros-aoa.o > obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += ums-cypress.o > obj-$(CONFIG_USB_STORAGE_DATAFAB) += ums-datafab.o > obj-$(CONFIG_USB_STORAGE_ENE_UB6250) += ums-eneub6250.o > @@ -31,6 +32,7 @@ obj-$(CONFIG_USB_STORAGE_SDDR55) += ums-sddr55.o > obj-$(CONFIG_USB_STORAGE_USBAT) += ums-usbat.o > > ums-alauda-y := alauda.o > +ums-cros-aoa-y := cros-aoa.o > ums-cypress-y := cypress_atacb.o > ums-datafab-y := datafab.o > ums-eneub6250-y := ene_ub6250.o > diff --git a/drivers/usb/storage/cros-aoa.c b/drivers/usb/storage/cros-aoa.c > new file mode 100644 > index 0000000000000..269e9193209d9 > --- /dev/null > +++ b/drivers/usb/storage/cros-aoa.c > @@ -0,0 +1,129 @@ > +// SPDX-License-Identifier: GPL-2 WITH Linux-syscall-note > +/* > + * Driver for Chrome OS Recovery via Android Open Accessory > + * > + * (c) 2019 Google LLC (Julius Werner <jwerner@xxxxxxxxxxxx>) > + * > + * This driver connects to an Android device via the Android Open Accessory > + * protocol to use it as a USB storage back-end. It is used for system recovery > + * on Chrome OS. The descriptors sent are specific to the Chrome OS Recovery app > + * for Android. The driver is inert unless activated by boot firmware with an > + * explicit kernel command line parameter. > + */ > + > +#include <linux/module.h> > +#include <linux/printk.h> > +#include <linux/usb.h> > + > +#include "initializers.h" > + > +#define DRV_NAME "ums-cros-aoa" > + > +MODULE_DESCRIPTION("Driver for Chrome OS Recovery via Android Open Accessory"); > +MODULE_AUTHOR("Julius Werner <jwerner@xxxxxxxxxxxx>"); > +MODULE_LICENSE("GPL"); > + > +#define AOA_GET_PROTOCOL 51 > +#define AOA_SET_STRING 52 > +#define AOA_START 53 > + > +#define AOA_STR_MANUFACTURER 0 > +#define AOA_STR_MODEL 1 > +#define AOA_STR_DESCRIPTION 2 > +#define AOA_STR_VERSION 3 > +#define AOA_STR_URI 4 > +#define AOA_STR_SERIAL 5 > + > +#define CROS_MANUF "Google" > +#define CROS_MODEL "Chrome OS Recovery" > +#define CROS_DESC "Chrome OS device in Recovery Mode" > +#define CROS_VERSION "1.0" > +#define CROS_URI "https://google.com/chromeos/recovery_android" > + > +static char *bind; > +module_param(bind, charp, 0); > + > +static struct usb_device_id cros_aoa_ids[] = { > + { USB_DEVICE(0, 0) }, /* to be filled out by cros_aoa_init() */ > + { } > +}; > +/* No MODULE_DEVICE_TABLE(). Autoloading doesn't make sense for this module. */ > + > +static int set_string(struct usb_device *udev, u16 type, const char *string) > +{ > + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), AOA_SET_STRING, > + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, > + 0, type, (char *)string, strlen(string) + 1, > + USB_CTRL_SET_TIMEOUT); > +} > + > +static int cros_aoa_probe(struct usb_interface *intf, > + const struct usb_device_id *id) > +{ > + int rv; > + u16 aoa_protocol; > + struct usb_device *udev = interface_to_usbdev(intf); > + > + rv = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), AOA_GET_PROTOCOL, > + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, > + 0, 0, &aoa_protocol, sizeof(aoa_protocol), > + USB_CTRL_GET_TIMEOUT); > + if (rv < 0 && rv != -EPROTO) > + goto fail; > + if (rv != sizeof(aoa_protocol) || aoa_protocol < 1) { > + dev_err(&intf->dev, "bound device does not support AOA?\n"); > + rv = -ENODEV; > + goto fail; > + } > + > + if ((rv = set_string(udev, AOA_STR_MANUFACTURER, CROS_MANUF)) < 0 || > + (rv = set_string(udev, AOA_STR_MODEL, CROS_MODEL)) < 0 || > + (rv = set_string(udev, AOA_STR_DESCRIPTION, CROS_DESC)) < 0 || > + (rv = set_string(udev, AOA_STR_VERSION, CROS_VERSION)) < 0 || > + (rv = set_string(udev, AOA_STR_URI, CROS_URI)) < 0) > + goto fail; > + > + rv = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), AOA_START, > + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, > + 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); > + > + if (!rv) { > + dev_info(&intf->dev, "switching to AOA mode\n"); > + usb_stor_cros_aoa_bind_busnum = udev->bus->busnum; > + usb_stor_cros_aoa_bind_route = udev->route; > + return 0; > + } > + > +fail: dev_err(&intf->dev, "probe error %d\n", rv); > + return rv; > +} > + > +static void cros_aoa_disconnect(struct usb_interface *intf) > +{ > + /* nothing to do -- we expect this to happen right after probe() */ > +} > + > +static struct usb_driver cros_aoa_stub_driver = { > + .name = DRV_NAME, > + .probe = cros_aoa_probe, > + .disconnect = cros_aoa_disconnect, > + .id_table = cros_aoa_ids, > +}; > + > +static int __init cros_aoa_init(void) > +{ > + if (!bind || sscanf(bind, "%hx:%hx", &cros_aoa_ids[0].idVendor, > + &cros_aoa_ids[0].idProduct) != 2) > + return -ENODEV; > + pr_info(DRV_NAME ": bound to USB device %4x:%4x\n", > + cros_aoa_ids[0].idVendor, cros_aoa_ids[0].idProduct); > + return usb_register(&cros_aoa_stub_driver); > +} > + > +static void __exit cros_aoa_exit(void) > +{ > + usb_deregister(&cros_aoa_stub_driver); > +} > + > +module_init(cros_aoa_init); > +module_exit(cros_aoa_exit); > diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c > index f8f9ce8dc7102..3056db79cd1d9 100644 > --- a/drivers/usb/storage/initializers.c > +++ b/drivers/usb/storage/initializers.c > @@ -92,3 +92,37 @@ int usb_stor_huawei_e220_init(struct us_data *us) > usb_stor_dbg(us, "Huawei mode set result is %d\n", result); > return 0; > } > + > +#if defined(CONFIG_USB_STORAGE_CROS_AOA) || \ > + defined(CONFIG_USB_STORAGE_CROS_AOA_MODULE) > +/* > + * Our VID/PID match grabs any Android device that was switched into Android > + * Open Accessory mode. We only want to bind to the one that was switched by the > + * ums-cros-aoa driver. There's no 100% way to identify the same device again > + * (because it changes all descriptors), but checking that it is on the same bus > + * with the same topology route should be a pretty good heuristic. > + */ > +int usb_stor_cros_aoa_bind_busnum = -1; > +EXPORT_SYMBOL(usb_stor_cros_aoa_bind_busnum); > +u32 usb_stor_cros_aoa_bind_route; > +EXPORT_SYMBOL(usb_stor_cros_aoa_bind_route); > + > +int usb_stor_cros_aoa_validate(struct us_data *us) > +{ > + if (us->pusb_dev->bus->busnum != usb_stor_cros_aoa_bind_busnum || > + us->pusb_dev->route != usb_stor_cros_aoa_bind_route) { > + dev_info(&us->pusb_intf->dev, > + "ums-cros-aoa ignoring unknown AOA device\n"); > + return -ENODEV; > + } > + > + /* > + * Only interface 0 connects to the AOA app. Android devices that have > + * ADB enabled also export an interface 1. We don't want it. > + */ > + if (us->pusb_intf->cur_altsetting->desc.bInterfaceNumber != 0) > + return -ENODEV; > + > + return 0; > +} > +#endif /* defined(CONFIG_USB_STORAGE_CROS_AOA) || ... */ > diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h > index 2dbf9c7d97492..35fe9ef3247d6 100644 > --- a/drivers/usb/storage/initializers.h > +++ b/drivers/usb/storage/initializers.h > @@ -37,3 +37,7 @@ int usb_stor_ucr61s2b_init(struct us_data *us); > > /* This places the HUAWEI E220 devices in multi-port mode */ > int usb_stor_huawei_e220_init(struct us_data *us); > + > +extern int usb_stor_cros_aoa_bind_busnum; > +extern u32 usb_stor_cros_aoa_bind_route; > +int usb_stor_cros_aoa_validate(struct us_data *us); > diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h > index ea0d27a94afe0..45fe9bbc6da18 100644 > --- a/drivers/usb/storage/unusual_devs.h > +++ b/drivers/usb/storage/unusual_devs.h > @@ -2259,6 +2259,24 @@ UNUSUAL_DEV( 0x1e74, 0x4621, 0x0000, 0x0000, > USB_SC_DEVICE, USB_PR_DEVICE, NULL, > US_FL_BULK_IGNORE_TAG | US_FL_MAX_SECTORS_64 ), > > +/* > + * Using an Android phone as USB storage back-end for Chrome OS recovery. See > + * usb/storage/cros-aoa.c for details. > + */ > +#if defined(CONFIG_USB_STORAGE_CROS_AOA) || \ > + defined(CONFIG_USB_STORAGE_CROS_AOA_MODULE) > +UNUSUAL_DEV( 0x18d1, 0x2d00, 0x0000, 0xffff, > + "Google", > + "Chrome OS Recovery via AOA", > + USB_SC_SCSI, USB_PR_BULK, usb_stor_cros_aoa_validate, > + US_FL_SINGLE_LUN | US_FL_CAPACITY_OK), > +UNUSUAL_DEV( 0x18d1, 0x2d01, 0x0000, 0xffff, > + "Google", > + "Chrome OS Recovery via AOA (with ADB)", > + USB_SC_SCSI, USB_PR_BULK, usb_stor_cros_aoa_validate, > + US_FL_SINGLE_LUN | US_FL_CAPACITY_OK), > +#endif /* defined(CONFIG_USB_STORAGE_CROS_AOA) || ... */ > + > /* Supplied with some Castlewood ORB removable drives */ > UNUSUAL_DEV( 0x2027, 0xa001, 0x0000, 0x9999, > "Double-H Technology", > -- > 2.20.1 > -- Matthew Dharm Former Maintainer, USB Mass Storage driver for Linux