@@ -311,3 +311,11 @@ config USB_ONBOARD_HUB
this config will enable the driver and it will automatically
match the state of the USB subsystem. If this driver is a
module it will be called onboard_usb_hub.
+
+config USB_BACKPACK_FW
+ tristate "Micro Solutions Backpack firmware loading support"
+ select USB_EZUSB_FX2
+ help
+ This driver loads firmware for Micro Solutions Backpack USB optical
+ and hard drives. Once the firmware is loaded, they're USB mass
+ storage compliant and thus handled by the usb-storage driver.
@@ -34,3 +34,4 @@ obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o
obj-$(CONFIG_BRCM_USB_PINMAP) += brcmstb-usb-pinmap.o
obj-$(CONFIG_USB_ONBOARD_HUB) += onboard_usb_hub.o
+obj-$(CONFIG_USB_BACKPACK_FW) += bpck_usb_firmware.o
new file mode 100644
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for loading firmware into Micro Solutions USB optical and hard drives
+ *
+ * Copyright (C) 2023 Ondrej Zary <linux@zary.sk>
+ *
+ * Most Micro Solutions external drives are parallel port devices that can be
+ * connected directly to a parallel port or a PCMCIA card (Model 836 - also acts
+ * as a parallel port) - and handled by pata_parport driver.
+ *
+ * There are also special USB cables that translate the Backpack parallel
+ * protocol to standard USB mass storage protocol.
+ * Model 839 - based on Cypress EZ-USB FX (USB 1.1)
+ * Model 840 - based on Cypress EZ-USB FX2 (USB 2.0)
+ * Newer drives have integrated USB 2.0 port.
+ *
+ * All these USB devices require a firmware to be downloaded to chip's RAM after
+ * each USB (re)connect. Once the firmware is loaded, they are USB mass storage
+ * compliant and will be handled by the usb-storage driver.
+ *
+ * The cables need various firmware depending on the drive type connected. The
+ * cable appears first with USB device ID 0x0000 or 0x0001. First, a "scan"
+ * firmware is loaded that identifies the drive type and changes the USB device
+ * ID appropriately. Then a final firmware is loaded, depending on the chip
+ * type, drive version (5 or 6) and drive type (CD or HDD).
+ *
+ * The cable is pretty intelligent - if there's no drive connected (or it's not
+ * powered up), it keeps trying (and blinking its LED), eventually detecting a
+ * connected drive and changing the USB ID. When the drive is disconnected, the
+ * cable does an USB reconnect so the "scan" firmware is loaded again.
+ * This means that drives can be hot-pluggged at the parallel port end of the
+ * cable.
+ */
+
+#include <linux/usb.h>
+#include <linux/errno.h>
+#include <linux/usb/ezusb.h>
+
+static const struct usb_device_id id_table[] = {
+ { USB_DEVICE(0x0ac9, 0x0000), .driver_info = (unsigned long) "backpack/BP1SCAN.fw" },
+ { USB_DEVICE(0x0ac9, 0x0001), .driver_info = (unsigned long) "backpack/BP2SCAN.fw" },
+ { USB_DEVICE(0x0ac9, 0x1000), .driver_info = (unsigned long) "backpack/BP1CD5.fw" },
+ { USB_DEVICE(0x0ac9, 0x1001), .driver_info = (unsigned long) "backpack/BP1CD6.fw" },
+ { USB_DEVICE(0x0ac9, 0x1002), .driver_info = (unsigned long) "backpack/BP1HD5.fw" },
+ { USB_DEVICE(0x0ac9, 0x1003), .driver_info = (unsigned long) "backpack/BP1HD6.fw" },
+ { USB_DEVICE(0x0ac9, 0x1004), .driver_info = (unsigned long) "backpack/BP2CD5.fw" },
+ { USB_DEVICE(0x0ac9, 0x1005), .driver_info = (unsigned long) "backpack/BP2CD6.fw" },
+ { USB_DEVICE(0x0ac9, 0x1006), .driver_info = (unsigned long) "backpack/BP2HD5.fw" },
+ { USB_DEVICE(0x0ac9, 0x1007), .driver_info = (unsigned long) "backpack/BP2HD6.fw" },
+ { USB_DEVICE(0x0ac9, 0x0010), .driver_info = (unsigned long) "backpack/BPINTCD.fw" },
+ { USB_DEVICE(0x0ac9, 0x0011), .driver_info = (unsigned long) "backpack/BPINTHD.fw" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int bpck_usb_fw_load(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ char *fw_name = (char *)id->driver_info;
+ int ret;
+
+ if (le16_to_cpu(dev->descriptor.bcdUSB) >= 0x0200) {
+ ezusb_fx2_set_reset(dev, 1);
+ ret = ezusb_fx2_ihex_firmware_download(dev, fw_name);
+ } else {
+ ezusb_fx1_set_reset(dev, 1);
+ ret = ezusb_fx1_ihex_firmware_download(dev, fw_name);
+ }
+
+ if (ret < 0) {
+ dev_err(&dev->dev, "failed to load firmware \"%s\"\n", fw_name);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+/* scan firmware */
+MODULE_FIRMWARE("backpack/BP1SCAN.fw"); /* USB 1.1 cable */
+MODULE_FIRMWARE("backpack/BP2SCAN.fw"); /* USB 2.0 cable */
+/* for USB 1.1 chips (integrated in cable) */
+MODULE_FIRMWARE("backpack/BP1CD5.fw"); /* series 5 optical drives */
+MODULE_FIRMWARE("backpack/BP1CD6.fw"); /* series 6 optical drives */
+MODULE_FIRMWARE("backpack/BP1HD5.fw"); /* series 5 hard drives */
+MODULE_FIRMWARE("backpack/BP1HD6.fw"); /* series 6 hard drives */
+/* for USB 2.0 chips (integrated in cable) */
+MODULE_FIRMWARE("backpack/BP2CD5.fw"); /* series 5 optical drives */
+MODULE_FIRMWARE("backpack/BP2CD6.fw"); /* series 6 optical drives */
+MODULE_FIRMWARE("backpack/BP2HD5.fw"); /* series 5 hard drives */
+MODULE_FIRMWARE("backpack/BP2HD6.fw"); /* series 6 hard drives */
+/* for device-integrated USB 2.0 chips */
+MODULE_FIRMWARE("backpack/BPINTCD.fw"); /* optical drives */
+MODULE_FIRMWARE("backpack/BPINTHD.fw"); /* hard drives */
+
+static void bpck_usb_fw_disconnect(struct usb_interface *intf)
+{
+ /* nothing to do here but mandatory */
+}
+
+static struct usb_driver bpck_usb_fw_driver = {
+ .name = "bpck_usb_firwmare",
+ .probe = bpck_usb_fw_load,
+ .disconnect = bpck_usb_fw_disconnect,
+ .id_table = id_table,
+};
+
+module_usb_driver(bpck_usb_fw_driver);
+
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("Micro Solutions Backpack USB firmware loader");
+MODULE_LICENSE("GPL");
@@ -124,11 +124,6 @@ int ezusb_fx1_ihex_firmware_download(struct usb_device *dev,
}
EXPORT_SYMBOL_GPL(ezusb_fx1_ihex_firmware_download);
-#if 0
-/*
- * Once someone one needs these fx2 functions, uncomment them
- * and add them to ezusb.h and all should be good.
- */
static struct ezusb_fx_type ezusb_fx2 = {
.cpucs_reg = 0xE600,
.max_internal_adress = 0x3FFF,
@@ -146,6 +141,5 @@ int ezusb_fx2_ihex_firmware_download(struct usb_device *dev,
return ezusb_ihex_firmware_download(dev, ezusb_fx2, firmware_path);
}
EXPORT_SYMBOL_GPL(ezusb_fx2_ihex_firmware_download);
-#endif
MODULE_LICENSE("GPL");
@@ -5,5 +5,8 @@
extern int ezusb_fx1_set_reset(struct usb_device *dev, unsigned char reset_bit);
extern int ezusb_fx1_ihex_firmware_download(struct usb_device *dev,
const char *firmware_path);
+extern int ezusb_fx2_set_reset(struct usb_device *dev, unsigned char reset_bit);
+extern int ezusb_fx2_ihex_firmware_download(struct usb_device *dev,
+ const char *firmware_path);
#endif /* __EZUSB_H */