From: Max Reitz <max@xxxxxxxxxx>
+ This adds a USB mass storage device driver. It is missing proper error
handling (e.g. for STALLed endpoints) and support for any USB MSD
class other than bulk-only (e.g. control-bulk-interrupt for USB floppy
drives).
Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
usb-storage/main.c | 76 ++++++++++++++++
usb-storage/usb-storage.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++
usb-storage/usb-storage.h | 34 ++++++++
3 files changed, 326 insertions(+)
create mode 100644 usb-storage/main.c
create mode 100644 usb-storage/usb-storage.c
create mode 100644 usb-storage/usb-storage.h
diff --git a/usb-storage/main.c b/usb-storage/main.c
new file mode 100644
index 0000000..76d783e
--- /dev/null
+++ b/usb-storage/main.c
@@ -0,0 +1,76 @@
+/******************************************************************************
+ * Copyright (c) 2015 Max Reitz *
+ * *
+ * Permission is hereby granted, free of charge, to any person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction, including without limitation *
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
+ * and/or sell copies of the Software, and to permit persons to whom the *
+ * Software is furnished to do so, subject to the following conditions: *
+ * *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software. *
+ * *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
+ * DEALINGS IN THE SOFTWARE. *
+ ******************************************************************************/
+
+#include <cdi.h>
+#include <cdi/scsi.h>
+#include <cdi/usb.h>
+
+#include "usb-storage.h"
+
+
+#define DRIVER_NAME "usb-storage"
+
+
+static struct cdi_scsi_driver drv;
+static struct cdi_usb_bus_device_pattern bus_pattern;
+
+static int drv_init(void)
+{
+ cdi_scsi_driver_init(&drv);
+ cdi_handle_bus_device(&drv.drv, &bus_pattern.pattern);
+ return 0;
+}
+
+static int drv_destroy(void)
+{
+ cdi_driver_destroy(&drv.drv);
+ return 0;
+}
+
+
+static struct cdi_scsi_driver drv = {
+ .drv = {
+ .name = DRIVER_NAME,
+ .type = CDI_SCSI,
+ .bus = CDI_USB,
+ .init = drv_init,
+ .destroy = drv_destroy,
+ .init_device = init_usb_device,
+ },
+
+ .request = usb_storage_request,
+};
+
+static struct cdi_usb_bus_device_pattern bus_pattern = {
+ .pattern = {
+ .bus_type = CDI_USB,
+ },
+
+ .vendor_id = -1,
+ .product_id = -1,
+
+ .class_id = 0x08,
+ .subclass_id = -1,
+ .protocol_id = -1,
+};
+
+CDI_DRIVER(DRIVER_NAME, drv)
diff --git a/usb-storage/usb-storage.c b/usb-storage/usb-storage.c
new file mode 100644
index 0000000..f6d126c
--- /dev/null
+++ b/usb-storage/usb-storage.c
@@ -0,0 +1,216 @@
+/******************************************************************************
+ * Copyright (c) 2015 Max Reitz *
+ * *
+ * Permission is hereby granted, free of charge, to any person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction, including without limitation *
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
+ * and/or sell copies of the Software, and to permit persons to whom the *
+ * Software is furnished to do so, subject to the following conditions: *
+ * *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software. *
+ * *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
+ * DEALINGS IN THE SOFTWARE. *
+ ******************************************************************************/
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cdi.h>
+#include <cdi/misc.h>
+#include <cdi/scsi.h>
+#include <cdi/usb.h>
+
+#include "usb-storage.h"
+
+
+#define CBW_IN 0x80
+#define CBW_OUT 0x00
+
+#define CBW_SIGNATURE 0x43425355 // 'USBC' => USB command
+#define CSW_SIGNATURE 0x53425355 // 'USBS' => USB status
+
+struct msd_bo_cbw {
+ uint32_t signature;
+ uint32_t tag;
+ uint32_t data_length;
+ uint8_t flags;
+ uint8_t lun;
+ uint8_t cb_length;
+ uint8_t cb[16];
+} __attribute__((packed));
+
+struct msd_bo_csw {
+ uint32_t signature;
+ uint32_t tag;
+ uint32_t data_residue;
+ uint8_t status;
+} __attribute__((packed));
+
+
+typedef enum usb_mass_storage_type {
+ USBMS_BULK_ONLY,
+} usb_mass_storage_type_t;
+
+
+typedef struct usb_mass_storage_device {
+ struct cdi_scsi_device dev;
+ struct cdi_usb_device *usb;
+
+ usb_mass_storage_type_t type;
+ int luns;
+
+ // FIXME: put into own class
+ int bulk_in, bulk_out;
+} usb_mass_storage_device_t;
+
+
+static int bulk_only_reset(usb_mass_storage_device_t *dev)
+{
+ cdi_usb_transmission_result_t res;
+
+ res = cdi_usb_control_transfer(dev->usb, 0, &(struct cdi_usb_setup_packet){
+ .bm_request_type = CDI_USB_CREQ_CLASS | CDI_USB_CREQ_INTERFACE
+ | CDI_USB_CREQ_OUT,
+ .b_request = 0xff,
+ .w_index = dev->usb->interface
+ }, NULL);
+ if (res != CDI_USB_OK) {
+ printf("bulk reset fail: %x\n", res);
+ return -EIO;
+ }
+
+ if (dev->luns > 0) {
+ return 0;
+ }
+
+ uint8_t lun_count;
+ res = cdi_usb_control_transfer(dev->usb, 0, &(struct cdi_usb_setup_packet) {
+ .bm_request_type = CDI_USB_CREQ_CLASS | CDI_USB_CREQ_INTERFACE
+ | CDI_USB_CREQ_IN,
+ .b_request = 0xfe,
+ .w_index = dev->usb->interface,
+ .w_length = 1
+ }, &lun_count);
+ if (res == CDI_USB_OK) {
+ dev->luns = lun_count + 1;
+ } else {
+ dev->luns = 1;
+ }
+
+ return 0;
+}
+
+
+struct cdi_device *init_usb_device(struct cdi_bus_data *bus_data)
+{
+ static int dev_counter = 0;
+ usb_mass_storage_device_t *dev = calloc(1, sizeof(*dev));
+ dev->usb = CDI_UPCAST(bus_data, bus_data, struct cdi_usb_device);
+
+ if ((dev->usb->subclass_id == 0x00 || dev->usb->subclass_id == 0x06)
+ && dev->usb->protocol_id == 0x50)
+ {
+ dev->type = USBMS_BULK_ONLY;
+ } else {
+ free(dev);
+ return NULL;
+ }
+
+ if (dev->type == USBMS_BULK_ONLY) {
+ if (bulk_only_reset(dev) < 0) {
+ free(dev);
+ return NULL;
+ }
+
+ struct cdi_usb_endpoint_descriptor ep_desc;
+ for (int ep = 1; ep < dev->usb->endpoint_count; ep++) {
+ cdi_usb_get_endpoint_descriptor(dev->usb, ep, &ep_desc);
+
+ if ((ep_desc.bm_attributes & 0x3) == 0x2) {
+ // Bulk
+ bool is_in = ep_desc.b_endpoint_address & 0x80;
+ if (!dev->bulk_in && is_in) {
+ dev->bulk_in = ep;
+ } else if (dev->bulk_in && !is_in) {
+ dev->bulk_out = ep;
+ }
+ }
+ }
+
+ if (!dev->bulk_in || !dev->bulk_out) {
+ free(dev);
+ return NULL;
+ }
+ }
+
+ dev->dev.dev.name = malloc(5);
+ sprintf((char *)dev->dev.dev.name, "msd%c", 'a' + dev_counter++);
+
+ dev->dev.type = CDI_STORAGE;
+ dev->dev.lun_count = dev->luns;
+
+ return &dev->dev.dev;
+}
+
+
+int usb_storage_request(struct cdi_scsi_device *device,
+ struct cdi_scsi_packet *packet)
+{
+ cdi_usb_transmission_result_t res;
+ usb_mass_storage_device_t *dev = CDI_UPCAST(device, dev,
+ usb_mass_storage_device_t);
+
+ struct msd_bo_cbw cbw = {
+ .signature = CBW_SIGNATURE,
+ .tag = 42,
+ .data_length = packet->bufsize,
+ .flags = packet->direction == CDI_SCSI_READ ? CBW_IN : CBW_OUT,
+ .lun = packet->command[1] & 0x7,
+ .cb_length = packet->cmdsize
+ };
+ memcpy(cbw.cb, packet->command, sizeof(cbw.cb));
+
+ res = cdi_usb_bulk_transfer(dev->usb, dev->bulk_out, &cbw, sizeof(cbw));
+ if (res != CDI_USB_OK) {
+ printf("bulk cbw fail %x\n", res);
+ bulk_only_reset(dev);
+ return -1;
+ }
+
+ if (packet->direction == CDI_SCSI_READ) {
+ res = cdi_usb_bulk_transfer(dev->usb, dev->bulk_in,
+ packet->buffer, packet->bufsize);
+ } else if (packet->direction == CDI_SCSI_WRITE) {
+ res = cdi_usb_bulk_transfer(dev->usb, dev->bulk_out,
+ packet->buffer, packet->bufsize);
+ }
+
+ if (res != CDI_USB_OK) {
+ printf("bulk cmd fail %x\n", res);
+ bulk_only_reset(dev);
+ return -1;
+ }
+
+ struct msd_bo_csw csw;
+ res = cdi_usb_bulk_transfer(dev->usb, dev->bulk_in, &csw, sizeof(csw));
+
+ if (res != CDI_USB_OK || csw.signature != CSW_SIGNATURE || csw.tag != 42 ||
+ csw.data_residue || csw.status)
+ {
+ printf("bulk csw fail %x\n", res);
+ bulk_only_reset(dev);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/usb-storage/usb-storage.h b/usb-storage/usb-storage.h
new file mode 100644
index 0000000..13d717f
--- /dev/null
+++ b/usb-storage/usb-storage.h
@@ -0,0 +1,34 @@
+/******************************************************************************
+ * Copyright (c) 2015 Max Reitz *
+ * *
+ * Permission is hereby granted, free of charge, to any person obtaining a *
+ * copy of this software and associated documentation files (the "Software"), *
+ * to deal in the Software without restriction, including without limitation *
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
+ * and/or sell copies of the Software, and to permit persons to whom the *
+ * Software is furnished to do so, subject to the following conditions: *
+ * *
+ * The above copyright notice and this permission notice shall be included in *
+ * all copies or substantial portions of the Software. *
+ * *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
+ * DEALINGS IN THE SOFTWARE. *
+ ******************************************************************************/
+
+#ifndef USB_STORAGE_H
+#define USB_STORAGE_H
+
+#include <cdi.h>
+#include <cdi/scsi.h>
+
+
+struct cdi_device *init_usb_device(struct cdi_bus_data *bus_data);
+int usb_storage_request(struct cdi_scsi_device *device,
+ struct cdi_scsi_packet *packet);
+
+#endif