The Myriad ma24xx in USB Intel Neural Compute Stick and Intel Neural
Compute Stick 2 provides an API to accelerate AI inference calculations
on the dedicated SHAVE VLIW vector co-processors, which are orchestrated
by one or more LEON SPARC v8 real time cores.

However, they need firmware to be loaded beforehand. An uninitialised
Myriad ma24xx presents with a distinctive USB ID. After firmware
loading, the device detaches from the USB bus and reattaches with a new
device ID. It can then be claimed by the usermode driver.

Signed-off-by: Rhys Kidd <rhysk...@gmail.com>
---
 MAINTAINERS                          |   6 +
 drivers/usb/misc/Kconfig             |   8 ++
 drivers/usb/misc/Makefile            |   1 +
 drivers/usb/misc/myriad-ma24xx-vsc.c | 171 +++++++++++++++++++++++++++
 4 files changed, 186 insertions(+)
 create mode 100644 drivers/usb/misc/myriad-ma24xx-vsc.c

diff --git a/MAINTAINERS b/MAINTAINERS
index a50e97a63bc8..2c3ab39ac26a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16682,6 +16682,12 @@ T:     git 
git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
 S:     Maintained
 F:     sound/usb/midi.*
 
+USB MYRIAD MA24XX FIRMWARE DRIVER
+M:     Rhys Kidd <rhysk...@gmail.com>
+L:     linux-usb@vger.kernel.org
+S:     Maintained
+F:     drivers/usb/misc/myriad-ma24xx-vsc.c
+
 USB NETWORKING DRIVERS
 L:     linux-usb@vger.kernel.org
 S:     Odd Fixes
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index bdae62b2ffe0..5d600fb8ac50 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -275,3 +275,11 @@ config USB_CHAOSKEY
 
          To compile this driver as a module, choose M here: the
          module will be called chaoskey.
+
+config USB_MYRIAD_MA24XX_VSC
+       tristate "USB Intel Myriad MA24xx firmware loading support"
+       select FW_LOADER
+       help
+         This driver loads firmware for Myriad ma24xx AI inference 
accelerators, as
+         found in the USB Intel Neural Compute Stick (ma2450) and Intel Neural
+         Compute Stick 2 (ma2485).
\ No newline at end of file
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 109f54f5b9aa..e19d1348c5b6 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_USB_ISIGHTFW)            += isight_firmware.o
 obj-$(CONFIG_USB_LCD)                  += usblcd.o
 obj-$(CONFIG_USB_LD)                   += ldusb.o
 obj-$(CONFIG_USB_LEGOTOWER)            += legousbtower.o
+obj-$(CONFIG_USB_MYRIAD_MA24XX_VSC)    += myriad-ma24xx-vsc.o
 obj-$(CONFIG_USB_RIO500)               += rio500.o
 obj-$(CONFIG_USB_TEST)                 += usbtest.o
 obj-$(CONFIG_USB_EHSET_TEST_FIXTURE)    += ehset.o
diff --git a/drivers/usb/misc/myriad-ma24xx-vsc.c 
b/drivers/usb/misc/myriad-ma24xx-vsc.c
new file mode 100644
index 000000000000..f516c236a8f5
--- /dev/null
+++ b/drivers/usb/misc/myriad-ma24xx-vsc.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for loading USB Myriad ma24xx firmware
+ *
+ * Copyright (C) 2018 Rhys Kidd <rhysk...@gmail.com>
+ *
+ * The Myriad ma24xx in USB Intel Neural Compute Stick and Intel Neural
+ * Compute Stick 2 provides an API to accelerate AI inference calculations
+ * on the dedicated SHAVE VLIW vector co-processors, which are orchestrated
+ * by one or more LEON SPARC v8 real time cores.
+ *
+ * However, they need firmware to be loaded beforehand. An uninitialised
+ * Myriad ma24xx presents with a distinctive USB ID. After firmware
+ * loading, the device detaches from the USB bus and reattaches with a new
+ * device ID. It can then be claimed by the usermode driver.
+ *
+ * The firmware is non-free and must be extracted by the user.
+ */
+
+/* Standard include files */
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#define usb_dbg(usb_if, format, arg...) \
+       dev_dbg(&(usb_if)->dev, format, ## arg)
+
+#define usb_err(usb_if, format, arg...) \
+       dev_err(&(usb_if)->dev, format, ## arg)
+
+/* Version Information */
+#define DRIVER_AUTHOR "Rhys Kidd <rhysk...@gmail.com>"
+#define DRIVER_DESC   "Driver for loading USB Myriad ma24xx firmware"
+#define DRIVER_SHORT  "myriad_ma24xx_vsc"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define FW_DIR "myriad/"
+#define MA2450_FIRMWARE FW_DIR "mvncapi-2450.mvcmd"
+#define MA2480_FIRMWARE FW_DIR "mvncapi-2480.mvcmd"
+
+MODULE_FIRMWARE(MA2450_FIRMWARE);
+MODULE_FIRMWARE(MA2480_FIRMWARE);
+
+enum {
+       MA2450FW = 0,
+       MA2480FW
+};
+
+/* macros for struct usb_device_id */
+#define MYRIAD_CHIP_VERSION(x) \
+       ((x)->driver_info & 0xf)
+
+#define MYRIAD_VID  0x03e7   /* Movidius Ltd, an Intel company */
+#define MA2450_PID  0x2150   /* ma2450 VSC loopback device, without fw */
+#define MA2485_PID  0x2485   /* ma2485 VSC loopback device, without fw */
+
+#define MYRIAD_BUF_LEN 512   /* max size of USB_SPEED_HIGH packet */
+#define WRITE_TIMEOUT  2000  /* milliseconds */
+
+static const struct usb_device_id id_table[] = {
+       { USB_DEVICE(MYRIAD_VID, MA2450_PID), .driver_info = MA2450FW },
+       { USB_DEVICE(MYRIAD_VID, MA2485_PID), .driver_info = MA2480FW },
+       { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int myriad_ma24xx_vsc_probe(struct usb_interface *intf,
+                                   const struct usb_device_id *id)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct usb_host_interface *altsetting = intf->cur_altsetting;
+       struct usb_endpoint_descriptor *epd;
+       int out_ep, res, size;
+       const struct firmware *firmware = NULL;
+       const u8 *ptr;
+       int offset, ret = 0;
+       char *buf;
+       char *fw_family_name;
+
+       usb_dbg(intf, "probe %s-%s", dev->product, dev->serial);
+
+       /* Find the first bulk OUT endpoint and its packet size */
+       res = usb_find_bulk_out_endpoint(altsetting, &epd);
+       if (res) {
+               usb_err(intf, "no OUT endpoint found");
+               return res;
+       }
+
+       out_ep = usb_endpoint_num(epd);
+       size = usb_endpoint_maxp(epd);
+
+       /* Validate endpoint and size */
+       if (size <= 0) {
+               usb_err(intf, "invalid size (%d)", size);
+               return -ENODEV;
+       }
+
+       if (size > MYRIAD_BUF_LEN) {
+               usb_dbg(intf, "size reduced %d -> %d\n", size, MYRIAD_BUF_LEN);
+               size = MYRIAD_BUF_LEN;
+       }
+
+       buf = kmalloc(size, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       switch (MYRIAD_CHIP_VERSION(id)) {
+       case MA2450FW:
+               fw_family_name = MA2450_FIRMWARE;
+               break;
+       case MA2480FW:
+               fw_family_name = MA2480_FIRMWARE;
+               break;
+       default:
+               usb_err(intf, "unsupported myriad ma24xx firmware family\n");
+               return -ENODEV;
+       }
+
+       if (request_firmware(&firmware, fw_family_name, &dev->dev) != 0) {
+               usb_err(intf, "unable to load myriad ma24xx firmware\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       ptr = firmware->data;
+
+       /* Handle the sending of firmware */
+       usb_dbg(intf, "starting firmware load...\n");
+
+       for (offset = 0; offset < firmware->size; offset += size) {
+               int thislen = min_t(int, size, firmware->size - offset);
+
+               memcpy(buf, firmware->data + offset, thislen);
+
+               if (usb_bulk_msg(dev,
+                                usb_sndbulkpipe(dev, out_ep),
+                                buf,
+                                thislen,
+                                NULL,
+                                WRITE_TIMEOUT) != 0) {
+                       usb_err(intf, "failed to initialise myriad ma24xx 
firmware\n");
+                       ret = -ENODEV;
+                       goto out;
+               }
+       }
+       usb_dbg(intf, "firmware uploaded (%zu bytes)\n", firmware->size);
+
+out:
+       kfree(buf);
+       release_firmware(firmware);
+       return ret;
+}
+
+static void myriad_ma24xx_vsc_disconnect(struct usb_interface *intf)
+{
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver myriad_ma24xx_vsc_driver = {
+       .name = DRIVER_SHORT,
+       .probe = myriad_ma24xx_vsc_probe,
+       .disconnect = myriad_ma24xx_vsc_disconnect,
+       .id_table = id_table,
+};
+
+module_usb_driver(myriad_ma24xx_vsc_driver);
-- 
2.20.1

Reply via email to