]> Zhao Yanbai Git Server - minix.git/commitdiff
usb:adding usb mass storage driver.
authorKees Jongenburger <kees.jongenburger@gmail.com>
Mon, 26 May 2014 14:47:48 +0000 (16:47 +0200)
committerLionel Sambuc <lionel@minix3.org>
Mon, 28 Jul 2014 15:05:39 +0000 (17:05 +0200)
Change-Id: I9e431d56eddfeec21413c290b2fa7ad35b566f6b

http://gerrit.minix3.org/#/c/2690/

12 files changed:
drivers/usb_storage/Makefile [new file with mode: 0755]
drivers/usb_storage/README.txt [new file with mode: 0755]
drivers/usb_storage/bulk.c [new file with mode: 0755]
drivers/usb_storage/bulk.h [new file with mode: 0755]
drivers/usb_storage/common.h [new file with mode: 0755]
drivers/usb_storage/scsi.c [new file with mode: 0755]
drivers/usb_storage/scsi.h [new file with mode: 0755]
drivers/usb_storage/urb_helper.c [new file with mode: 0755]
drivers/usb_storage/urb_helper.h [new file with mode: 0755]
drivers/usb_storage/usb_storage.c [new file with mode: 0755]
drivers/usb_storage/usb_storage.conf [new file with mode: 0755]
drivers/usb_storage/usb_storage.h [new file with mode: 0755]

diff --git a/drivers/usb_storage/Makefile b/drivers/usb_storage/Makefile
new file mode 100755 (executable)
index 0000000..430faa7
--- /dev/null
@@ -0,0 +1,20 @@
+# Makefile for Mass Storage driver
+PROG= usb_storage
+SRCS= urb_helper.c bulk.c scsi.c usb_storage.c
+
+FILES= $(PROG).conf
+FILESNAME= $(PROG)
+FILESDIR= /etc/system.conf.d
+
+DPADD+= ${LIBBLOCKDRIVER} ${LIBDDEKIT_USB_CLIENT} ${LIBSYS} ${LIBMINLIB} ${LIBDDEKIT} ${LIBUSB}
+LDADD+= -lblockdriver -lddekit_usb_client -lsys -lminlib -lddekit -lusb
+
+#For easier debugging, uncomment:
+#LDADD+= -Wl,-Ttext=0x800000
+#CPPFLAGS+= -DMASS_DEBUG
+
+MAN=
+
+BINDIR?= /usr/sbin
+
+.include <minix.service.mk>
diff --git a/drivers/usb_storage/README.txt b/drivers/usb_storage/README.txt
new file mode 100755 (executable)
index 0000000..f560c28
--- /dev/null
@@ -0,0 +1,27 @@
+-------------------------------------------------------------------------------
+*                           INFORMATION:                                      *
+-------------------------------------------------------------------------------
+README file for "USB Mass Storage driver" that uses DDEkit and libblockdriver.
+
+created march-april 2014, JPEmbedded (info@jpembedded.eu)
+
+-------------------------------------------------------------------------------
+*                           KNOWN LIMITATIONS:                                *
+-------------------------------------------------------------------------------
+-Hardcoded interface number for bulk-only reset.
+-Hardcoded configuration number for simple enumeration.
+-Call to ddekit_minix_create_msg_q in _ddekit_usb_thread uses base that
+ overlaps that of blockdriver's (in mass_storage_task) so initialization
+ must be done in fixed order.
+-Some of DDEKit's functions are declared in source files as they are missing
+ from headers.
+-DDEKit has 'init' but no 'deinit' call, so memory is spilled.
+-Hardcoded geometry.
+-LUN always set to 0.
+-SIGTERM handler uses exit instead of DDEkit semaphores.
+-mass_storage.conf taken from dde-linux26-usb-drivers.
+-Subpartitioning does not seem to work.
+-Type ddekit_usb_dev is not defined in any header file but two variants of it
+ should exist (client and server).
+-Magic number in URB setup buffer assignment as there is no header for that
+ (like usb_ch9.h for descriptors).
\ No newline at end of file
diff --git a/drivers/usb_storage/bulk.c b/drivers/usb_storage/bulk.c
new file mode 100755 (executable)
index 0000000..e719983
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * "Bulk only transfer" related implementation
+ */
+
+#include <assert.h>
+#include <string.h>                    /* memset */
+
+#include "common.h"
+#include "bulk.h"
+
+/*===========================================================================*
+ *    init_cbw                                                               *
+ *===========================================================================*/
+void
+init_cbw(mass_storage_cbw * cbw, unsigned int tag)
+{
+       assert(NULL != cbw);
+
+       /* Clearing "Command Block Wrapper" */
+       memset(cbw, 0, sizeof(*cbw));
+
+       /* Filling Command Block Wrapper */
+       cbw->dCBWSignature = CBW_SIGNATURE;
+       cbw->dCBWTag = tag;
+       cbw->bCBWLUN = 0;
+}
+
+
+/*===========================================================================*
+ *    init_csw                                                               *
+ *===========================================================================*/
+void
+init_csw(mass_storage_csw * csw)
+{
+       assert(NULL != csw);
+
+       /* Clearing "Command Status Wrapper" so we can receive data into it */
+       memset(csw, 0, sizeof(*csw));
+}
diff --git a/drivers/usb_storage/bulk.h b/drivers/usb_storage/bulk.h
new file mode 100755 (executable)
index 0000000..2e7dc19
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * "Bulk only transfer" related externally visible info
+ */
+
+#ifndef _BULK_H_
+#define _BULK_H_
+
+/*---------------------------*
+ *    declared types         *
+ *---------------------------*/
+#define BULK_PACKED __attribute__((__packed__))
+
+#define CBW_SIGNATURE          (0x43425355)
+#define CBW_FLAGS_OUT          (0x00)
+#define CBW_FLAGS_IN           (0x80)
+#define CBW_CB_LENGTH          (16)
+
+/* Command Block Wrapper */
+typedef struct BULK_PACKED mass_storage_cbw {
+       uint32_t        dCBWSignature;
+       uint32_t        dCBWTag;
+       uint32_t        dCBWDataTransferLength;
+       uint8_t         bCBWFlags;
+       uint8_t         bCBWLUN;                /* 4 bits */
+       uint8_t         bCDBLength;             /* 5 bits */
+       uint8_t         CBWCB[CBW_CB_LENGTH];
+}
+mass_storage_cbw;
+
+#define CSW_SIGNATURE          (0x53425355)
+#define CSW_STATUS_GOOD                (0x0)
+#define CSWS_TATUS_FAILED      (0x1)
+#define CSW_STATUS_PHASE       (0x2)
+
+/* Command Status Wrapper */
+typedef struct BULK_PACKED mass_storage_csw {
+       uint32_t        dCSWSignature;
+       uint32_t        dCSWTag;
+       uint32_t        dCSWDataResidue;
+       uint8_t         bCSWStatus;
+}
+mass_storage_csw;
+
+#undef BULK_PACKED
+
+/*---------------------------*
+ *    declared functions     *
+ *---------------------------*/
+void init_cbw(mass_storage_cbw *, unsigned int);
+void init_csw(mass_storage_csw *);
+
+#endif /* !_BULK_H_ */
diff --git a/drivers/usb_storage/common.h b/drivers/usb_storage/common.h
new file mode 100755 (executable)
index 0000000..223146a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Whatever is commonly used in mass_storage driver, should be here
+ */
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+/*---------------------------*
+ *  commonly used headers:   *
+ *---------------------------*/
+#include <stdlib.h> /* For things, like EXIT_*, NULL, ... */
+#include <stdio.h>
+
+/*---------------------------*
+ *  commonly used defines:   *
+ *---------------------------*/
+#define THIS_EXEC_NAME "usb_storage"
+#define MASS_MSG(...) do {                                             \
+       printf(THIS_EXEC_NAME": ");                                     \
+       printf(__VA_ARGS__);                                            \
+       printf("; %s:%d\n", __func__, __LINE__);                        \
+       } while(0)
+
+/*---------------------------*
+ *  debug helpers:           *
+ *---------------------------*/
+#ifdef MASS_DEBUG
+#define MASS_DEBUG_MSG         MASS_MSG
+#define MASS_DEBUG_DUMP                printf("%s():%d\n", __func__, __LINE__)
+#else
+#define MASS_DEBUG_MSG(...)
+#define MASS_DEBUG_DUMP
+#endif
+
+#endif /* !_COMMON_H_ */
diff --git a/drivers/usb_storage/scsi.c b/drivers/usb_storage/scsi.c
new file mode 100755 (executable)
index 0000000..f4ff43e
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * SCSI commands related implementation
+ */
+
+#include <minix/blockdriver.h>                         /* SECTOR_SIZE */
+
+#include <assert.h>
+#include <string.h>                                    /* strncpy */
+
+#include "common.h"
+#include "scsi.h"
+
+/*---------------------------*
+ *    declared functions     *
+ *---------------------------*/
+/* To work correctly cbw->CBWCB must be zeroed before calling these: */
+static int create_inquiry_scsi_cmd(mass_storage_cbw *);
+static int create_test_scsi_cmd(mass_storage_cbw *);
+static int create_read_capacity_scsi_cmd(mass_storage_cbw *);
+static int create_write_scsi_cmd(mass_storage_cbw *, scsi_transfer *);
+static int create_read_scsi_cmd(mass_storage_cbw *, scsi_transfer *);
+static int create_mode_sense_scsi_cmd(mass_storage_cbw *);
+
+/*---------------------------*
+ *    defined functions      *
+ *---------------------------*/
+/*===========================================================================*
+ *    create_scsi_cmd                                                        *
+ *===========================================================================*/
+int
+create_scsi_cmd(mass_storage_cbw * cbw, int cmd, scsi_transfer * info)
+{
+       MASS_DEBUG_DUMP;
+
+       assert(NULL != cbw);
+
+       switch (cmd) {
+               case SCSI_INQUIRY:
+                       return create_inquiry_scsi_cmd(cbw);
+               case SCSI_TEST_UNIT_READY:
+                       return create_test_scsi_cmd(cbw);
+               case SCSI_READ_CAPACITY:
+                       return create_read_capacity_scsi_cmd(cbw);
+               case SCSI_WRITE:
+                       return create_write_scsi_cmd(cbw, info);
+               case SCSI_READ:
+                       return create_read_scsi_cmd(cbw, info);
+               case SCSI_MODE_SENSE:
+                       return create_mode_sense_scsi_cmd(cbw);
+               default:
+                       MASS_MSG("Invalid SCSI command!");
+                       return EXIT_FAILURE;
+       }
+}
+
+
+/*===========================================================================*
+ *    create_inquiry_scsi_cmd                                                *
+ *===========================================================================*/
+static int
+create_inquiry_scsi_cmd(mass_storage_cbw * cbw)
+{
+       MASS_DEBUG_DUMP;
+
+       cbw->dCBWDataTransferLength = SCSI_INQUIRY_DATA_LEN;
+       cbw->bCBWFlags = CBW_FLAGS_IN;
+       cbw->bCDBLength = SCSI_INQUIRY_CMD_LEN;
+
+       SCSI_SET_INQUIRY_OP_CODE(cbw->CBWCB);
+       SCSI_SET_INQUIRY_PAGE_CODE(cbw->CBWCB, 0);
+       SCSI_SET_INQUIRY_ALLOC_LEN(cbw->CBWCB, SCSI_INQUIRY_DATA_LEN);
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    create_test_scsi_cmd                                                   *
+ *===========================================================================*/
+static int
+create_test_scsi_cmd(mass_storage_cbw * cbw)
+{
+       MASS_DEBUG_DUMP;
+
+       cbw->bCDBLength = SCSI_TEST_CMD_LEN;
+
+       SCSI_SET_TEST_OP_CODE(cbw->CBWCB);
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    create_read_capacity_scsi_cmd                                          *
+ *===========================================================================*/
+static int
+create_read_capacity_scsi_cmd(mass_storage_cbw * cbw)
+{
+       MASS_DEBUG_DUMP;
+
+       cbw->dCBWDataTransferLength = SCSI_READ_CAPACITY_DATA_LEN;
+       cbw->bCBWFlags = CBW_FLAGS_IN;
+       cbw->bCDBLength = SCSI_READ_CAPACITY_CMD_LEN;
+
+       SCSI_SET_READ_CAPACITY_OP_CODE(cbw->CBWCB);
+       SCSI_SET_READ_CAPACITY_LBA(cbw->CBWCB, 0x00);
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    create_write_scsi_cmd                                                  *
+ *===========================================================================*/
+static int
+create_write_scsi_cmd(mass_storage_cbw * cbw, scsi_transfer * info)
+{
+       MASS_DEBUG_DUMP;
+
+       assert(NULL != info);
+       assert(0 == (info->length % SECTOR_SIZE));
+
+       cbw->dCBWDataTransferLength = info->length;
+       cbw->bCBWFlags = CBW_FLAGS_OUT;
+       cbw->bCDBLength = SCSI_WRITE_CMD_LEN;
+
+       SCSI_SET_WRITE_OP_CODE(cbw->CBWCB);
+       SCSI_SET_WRITE_LBA(cbw->CBWCB, info->lba);
+       SCSI_SET_WRITE_BLEN(cbw->CBWCB, info->length / SECTOR_SIZE);
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    create_read_scsi_cmd                                                   *
+ *===========================================================================*/
+static int
+create_read_scsi_cmd(mass_storage_cbw * cbw, scsi_transfer * info)
+{
+       MASS_DEBUG_DUMP;
+
+       assert(NULL != info);
+       assert(0 == (info->length % SECTOR_SIZE));
+
+       cbw->dCBWDataTransferLength = info->length;
+       cbw->bCBWFlags = CBW_FLAGS_IN;
+       cbw->bCDBLength = SCSI_READ_CMD_LEN;
+
+       SCSI_SET_READ_OP_CODE(cbw->CBWCB);
+       SCSI_SET_READ_LBA(cbw->CBWCB, info->lba);
+       SCSI_SET_READ_BLEN(cbw->CBWCB, info->length / SECTOR_SIZE);
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    create_mode_sense_scsi_cmd                                             *
+ *===========================================================================*/
+static int
+create_mode_sense_scsi_cmd(mass_storage_cbw * cbw)
+{
+       MASS_DEBUG_DUMP;
+
+       cbw->dCBWDataTransferLength = SCSI_MODE_SENSE_FLEX_DATA_LEN;
+       cbw->bCBWFlags = CBW_FLAGS_IN;
+       cbw->bCDBLength = SCSI_MODE_SENSE_CMD_LEN;
+
+       SCSI_SET_MODE_SENSE_OP_CODE(cbw->CBWCB);
+       SCSI_SET_MODE_SENSE_PAGE_CODE(cbw->CBWCB,
+                                       SCSI_MODE_SENSE_FLEXIBLE_DISK_PAGE);
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    check_inquiry_reply                                                    *
+ *===========================================================================*/
+int
+check_inquiry_reply(uint8_t * scsi_reply)
+{
+       char vendor_name[SCSI_INQUIRY_VENDOR_NAME_LEN + 1];
+       char product_name[SCSI_INQUIRY_PRODUCT_NAME_LEN + 1];
+
+       MASS_DEBUG_DUMP;
+
+       /* Stop condition for printing as strncpy() does not add it */
+       vendor_name[SCSI_INQUIRY_VENDOR_NAME_LEN] = '\0';
+       product_name[SCSI_INQUIRY_PRODUCT_NAME_LEN] = '\0';
+
+       if (SCSI_GET_INQUIRY_PERIPH_QUALIF(scsi_reply)) {
+               MASS_MSG("Device not connected");
+               return EXIT_FAILURE;
+       }
+
+       strncpy(vendor_name, SCSI_GET_INQUIRY_VENDOR_NAME(scsi_reply),
+               SCSI_INQUIRY_VENDOR_NAME_LEN);
+       strncpy(product_name, SCSI_GET_INQUIRY_PRODUCT_NAME(scsi_reply),
+               SCSI_INQUIRY_PRODUCT_NAME_LEN);
+
+       MASS_DEBUG_MSG("Vendor name: %s", vendor_name);
+       MASS_DEBUG_MSG("Product name %s", product_name);
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    check_read_capacity_reply                                              *
+ *===========================================================================*/
+int
+check_read_capacity_reply(uint8_t * scsi_reply, uint32_t * lba, uint32_t * blen)
+{
+       MASS_DEBUG_DUMP;
+
+       *lba = SCSI_GET_READ_CAPACITY_LBA(scsi_reply);
+       *blen = SCSI_GET_READ_CAPACITY_BLEN(scsi_reply);
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    check_mode_sense_reply                                                 *
+ *===========================================================================*/
+int
+check_mode_sense_reply(uint8_t * scsi_reply, unsigned * cyl,
+                       unsigned * head, unsigned * sect)
+{
+       MASS_DEBUG_DUMP;
+
+       *cyl = SCSI_GET_MODE_SENSE_CYLINDERS(scsi_reply);
+       *head = SCSI_GET_MODE_SENSE_HEADS(scsi_reply);
+       *sect = SCSI_GET_MODE_SENSE_SECTORS(scsi_reply);
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    check_csw                                                              *
+ *===========================================================================*/
+int
+check_csw(mass_storage_csw * csw, unsigned int tag)
+{
+       MASS_DEBUG_DUMP;
+
+       if (csw->dCSWTag != tag) {
+               MASS_MSG("CSW tag mismatch!");
+               return EXIT_FAILURE;
+       }
+
+       if (CSW_SIGNATURE != csw->dCSWSignature) {
+               MASS_MSG("CSW signature mismatch!");
+               return EXIT_FAILURE;
+       }
+
+       if (CSW_STATUS_GOOD != csw->bCSWStatus) {
+               MASS_MSG("CSW status error!");
+               return EXIT_FAILURE;
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/drivers/usb_storage/scsi.h b/drivers/usb_storage/scsi.h
new file mode 100755 (executable)
index 0000000..91d28ef
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * SCSI commands related definitions
+ */
+
+#ifndef _SCSI_H_
+#define _SCSI_H_
+
+#if 0
+#include <sys/endian.h>                                /* be16dec... */
+#else
+#define be16enc(base, val) {                                           \
+               (base)[0] = (((val) >> 8) & 0xff);                      \
+               (base)[1] = ((val) & 0xff);                             \
+               }
+#define be16dec(base)                                                  \
+               (((base)[0] << 8) | (base)[1])
+#define be32enc(base, val) {                                           \
+               (base)[0] = (((val) >> 24) & 0xff);                     \
+               (base)[1] = (((val) >> 16) & 0xff);                     \
+               (base)[2] = (((val) >>  8) & 0xff);                     \
+               (base)[3] = ((val) & 0xff);                             \
+               }
+#define be32dec(base)                                                  \
+               (((base)[0] << 24) | ((base)[1] << 16) |                \
+               ((base)[2] << 8) | (base)[3])
+#endif
+
+#include "bulk.h"
+
+#define SCSI_FORMAT_UNIT                       (0x04)
+#define SCSI_INQUIRY                           (0x12)
+#define SCSI_START_STOP                                (0x1B)
+#define SCSI_MODE_SELECT                       (0x55)
+#define SCSI_MODE_SENSE                                (0x5A)
+#define SCSI_PREVENT_ALLOW                     (0x1E)
+#define SCSI_READ                              (0x28)
+#define SCSI_READ_12                           (0xA8)
+#define SCSI_READ_CAPACITY                     (0x25)
+#define SCSI_READ_FORMAT_CAP                   (0x23)
+#define SCSI_REQUEST_SENSE                     (0x03)
+#define SCSI_REZERO_UNIT                       (0x01)
+#define SCSI_SEEK                              (0x2B)
+#define SCSI_SEND_DIAGNOSTIC                   (0x1D)
+#define SCSI_TEST_UNIT_READY                   (0x00)
+#define SCSI_VERIFY                            (0x2F)
+#define SCSI_WRITE                             (0x2A)
+#define SCSI_WRITE_12                          (0xAA)
+#define SCSI_WRITE_VERIFY                      (0x2E)
+
+#define SCSI_INQUIRY_DATA_LEN                  (36)
+#define SCSI_INQUIRY_CMD_LEN                   (6)
+
+#define SCSI_MODE_SENSE_FLEX_DATA_LEN          (32)
+#define SCSI_MODE_SENSE_CMD_LEN                        (12)
+
+#define SCSI_READ_DATA_LEN                     (0)
+#define SCSI_READ_CMD_LEN                      (10)
+
+#define SCSI_READ_CAPACITY_DATA_LEN            (8)
+#define SCSI_READ_CAPACITY_CMD_LEN             (10)
+
+#define SCSI_TEST_DATA_LEN                     (0)
+#define SCSI_TEST_CMD_LEN                      (6)
+
+#define SCSI_WRITE_DATA_LEN                    (0)
+#define SCSI_WRITE_CMD_LEN                     (10)
+
+/* These macros are immune to unaligned access
+ * so they can be used on any address */
+/* 1 Byte SCSI operation */
+#define SCSI_WR1(base, offset, value)\
+               (((uint8_t*)(base))[offset] = value)
+#define SCSI_RD1(base, offset)\
+               (((uint8_t*)(base))[offset])
+#define SCSI_SET1(base, offset, value)\
+               (((uint8_t*)(base))[offset] |= value)
+/* 2 Byte SCSI operation */
+#define SCSI_WR2(base, offset, value)\
+               be16enc( &(((uint8_t*)(base))[offset]), value )
+#define SCSI_RD2(base, offset)\
+               be16dec( &(((uint8_t*)(base))[offset]) )
+/* 4 Byte SCSI operation */
+#define SCSI_WR4(base, offset, value)\
+               be32enc( &(((uint8_t*)(base))[offset]), value )
+#define SCSI_RD4(base, offset)\
+               be32dec( &(((uint8_t*)(base))[offset]) )
+
+#define SCSI_SET_INQUIRY_OP_CODE(x)            SCSI_WR1((x), 0, SCSI_INQUIRY)
+#define SCSI_SET_INQUIRY_EVPD(x)               SCSI_SET1((x), 1, 0x01)
+#define SCSI_SET_INQUIRY_CMDDT(x)              SCSI_SET1((x), 1, 0x02)
+#define SCSI_SET_INQUIRY_PAGE_CODE(x, code)    SCSI_WR1((x), 2, code)
+#define SCSI_SET_INQUIRY_ALLOC_LEN(x, len)     SCSI_WR1((x), 4, len)
+
+#define SCSI_GET_INQUIRY_PERIPH_QUALIF(x)      ((SCSI_RD1(x, 0) >> 5) & 0x7)
+#define SCSI_GET_INQUIRY_VENDOR_NAME(x)                ((const char *)(&((x)[8])))
+#define SCSI_INQUIRY_VENDOR_NAME_LEN           (8)
+#define SCSI_GET_INQUIRY_PRODUCT_NAME(x)       ((const char *)(&((x)[16])))
+#define SCSI_INQUIRY_PRODUCT_NAME_LEN          (16)
+
+#define SCSI_MODE_SENSE_FLEXIBLE_DISK_PAGE     (0x5)
+#define SCSI_SET_MODE_SENSE_OP_CODE(x)         SCSI_WR1((x), 0, \
+                                                       SCSI_MODE_SENSE)
+#define SCSI_SET_MODE_SENSE_PAGE_CODE(x, code) SCSI_SET1((x), 2, \
+                                                       (code)&0x3F)
+#define SCSI_GET_MODE_SENSE_CYLINDERS(x)       SCSI_RD2((x), 8)
+#define SCSI_GET_MODE_SENSE_HEADS(x)           SCSI_RD1((x), 4)
+#define SCSI_GET_MODE_SENSE_SECTORS(x)         SCSI_RD1((x), 5)
+
+#define SCSI_SET_READ_OP_CODE(x)               SCSI_WR1((x), 0, SCSI_READ)
+#define SCSI_SET_READ_LBA(x, lba)              SCSI_WR4((x), 2, (lba))
+#define SCSI_SET_READ_BLEN(x, len)             SCSI_WR2((x), 7, (len))
+
+#define SCSI_SET_READ_CAPACITY_OP_CODE(x)      SCSI_WR1((x), 0, \
+                                                       SCSI_READ_CAPACITY)
+#define SCSI_SET_READ_CAPACITY_LBA(x, lba)     SCSI_WR4((x), 2, (lba))
+#define SCSI_SET_READ_CAPACITY_PMI(x)          SCSI_SET1((x), 8, 0x01)
+#define SCSI_GET_READ_CAPACITY_LBA(x)          SCSI_RD4((x), 0)
+#define SCSI_GET_READ_CAPACITY_BLEN(x)         SCSI_RD4((x), 4)
+
+#define SCSI_SET_TEST_OP_CODE(x)               SCSI_WR1((x), 0, \
+                                                       SCSI_TEST_UNIT_READY)
+
+#define SCSI_SET_WRITE_OP_CODE(x)              SCSI_WR1((x), 0, SCSI_WRITE)
+#define SCSI_SET_WRITE_LBA(x, lba)             SCSI_WR4((x), 2, (lba))
+#define SCSI_SET_WRITE_BLEN(x, len)            SCSI_WR2((x), 7, (len))
+
+typedef struct scsi_transfer {
+
+       unsigned int lba;                       /* logical block address */
+       unsigned int length;                    /* transfer length */
+}
+scsi_transfer;
+
+/*---------------------------*
+ *    declared functions     *
+ *---------------------------*/
+int create_scsi_cmd(mass_storage_cbw *, int, struct scsi_transfer *);
+int check_inquiry_reply(uint8_t *);
+int check_read_capacity_reply(uint8_t *, uint32_t *, uint32_t *);
+int check_mode_sense_reply(uint8_t *, unsigned *, unsigned *, unsigned *);
+int check_csw(mass_storage_csw *, unsigned int);
+
+#endif /* !_SCSI_H_ */
diff --git a/drivers/usb_storage/urb_helper.c b/drivers/usb_storage/urb_helper.c
new file mode 100755 (executable)
index 0000000..7302848
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * URB formatting related implementation
+ */
+
+#include <minix/sysutil.h>                     /* panic */
+#include <minix/usb.h>                         /* struct usb_ctrlrequest */
+
+#include <string.h>                            /* memset */
+#include <assert.h>
+
+#include "common.h"
+#include "urb_helper.h"
+
+/*---------------------------*
+ *    defined functions      *
+ *---------------------------*/
+/*===========================================================================*
+ *    init_urb                                                               *
+ *===========================================================================*/
+void
+init_urb(struct ddekit_usb_urb * urb, struct ddekit_usb_dev * dev,
+       ddekit_int32_t urb_type, ddekit_int32_t urb_endpoint,
+       ddekit_int32_t urb_direction, ddekit_uint32_t urb_transfer_flags)
+{
+       MASS_DEBUG_DUMP;
+
+       /* Sanity checks */
+       assert(NULL != urb);
+       assert(NULL != dev);
+       assert((DDEKIT_USB_TRANSFER_BLK == urb_type) ||
+               (DDEKIT_USB_TRANSFER_CTL == urb_type) ||
+               (DDEKIT_USB_TRANSFER_INT == urb_type) ||
+               (DDEKIT_USB_TRANSFER_ISO == urb_type));
+       assert(urb_endpoint < 16);
+       assert((DDEKIT_USB_IN == urb_direction) ||
+               (DDEKIT_USB_OUT == urb_direction));
+
+       /* Clear block first */
+       memset(urb, 0, sizeof(*urb));
+
+       /* Set supplied values */
+       urb->dev = dev;
+       urb->type = urb_type;
+       urb->endpoint = urb_endpoint;
+       urb->direction = urb_direction;
+       urb->transfer_flags = urb_transfer_flags;
+}
+
+
+/*===========================================================================*
+ *    attach_urb_data                                                        *
+ *===========================================================================*/
+void
+attach_urb_data(struct ddekit_usb_urb * urb, int buf_type,
+               void * buf, ddekit_uint32_t buf_len)
+{
+       MASS_DEBUG_DUMP;
+
+       assert(NULL != urb);
+       assert(NULL != buf);
+
+       /* Mutual exclusion */
+       if (URB_BUF_TYPE_DATA == buf_type) {
+               urb->data = buf;
+               urb->size = buf_len;
+       } else if ( URB_BUF_TYPE_SETUP == buf_type ) {
+               assert(sizeof(struct usb_ctrlrequest) == buf_len);
+               urb->setup_packet = buf;
+       } else
+               panic("Unexpected buffer type!");
+}
+
+
+/*===========================================================================*
+ *    blocking_urb_submit                                                    *
+ *===========================================================================*/
+int
+blocking_urb_submit(struct ddekit_usb_urb * urb, ddekit_sem_t * sem,
+               int check_len)
+{
+       MASS_DEBUG_DUMP;
+
+       assert(NULL != urb);
+       assert(NULL != sem);
+       assert((check_len == URB_SUBMIT_CHECK_LEN) ||
+               (check_len == URB_SUBMIT_ALLOW_MISMATCH));
+
+       /* Submit and block until semaphore gets up */
+       if (ddekit_usb_submit_urb(urb)) {
+               MASS_MSG("Submitting DDEKit URB failed");
+               return EXIT_FAILURE;
+       } else {
+               /* Submitting succeeded so block and wait for reply */
+               ddekit_sem_down(sem);
+
+               /* Check for DDEKit status first */
+               if (urb->status) {
+                       MASS_MSG("Invalid DDEKit URB status");
+                       return EXIT_FAILURE;
+               } else {
+                       if (URB_SUBMIT_CHECK_LEN == check_len) {
+                               /* Compare lengths */
+                               if (urb->actual_length != urb->size) {
+                                       MASS_MSG("URB different than expected");
+                                       return EXIT_FAILURE;
+                               }
+                       }
+
+                       return EXIT_SUCCESS;
+               }
+       }
+}
diff --git a/drivers/usb_storage/urb_helper.h b/drivers/usb_storage/urb_helper.h
new file mode 100755 (executable)
index 0000000..5046edb
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * URB formatting related definitions
+ */
+
+#ifndef _URB_HELPER_H_
+#define _URB_HELPER_H_
+
+#include <ddekit/usb.h>
+#include <ddekit/semaphore.h>
+
+/* Possible values for attach_urb_data's buf_type */
+/* Both may be used for single URB */
+#define URB_BUF_TYPE_DATA 0            /* attached buffer is data buffer */
+#define URB_BUF_TYPE_SETUP 1           /* attached buffer is setup structure */
+
+/* Possible values for blocking_urb_submit's check_len */
+/* Use URB_SUBMIT_CHECK_LEN when actual data buffer length returned
+ * by HCD must match expected length, supplied in attach_urb_data */
+#define URB_SUBMIT_CHECK_LEN 0         /* return error on length mismatch */
+#define URB_SUBMIT_ALLOW_MISMATCH 1    /* ignore length check */
+
+/*---------------------------*
+ *    declared functions     *
+ *---------------------------*/
+void init_urb(struct ddekit_usb_urb *, struct ddekit_usb_dev *, ddekit_int32_t,
+               ddekit_int32_t, ddekit_int32_t, ddekit_uint32_t);
+void attach_urb_data(struct ddekit_usb_urb *, int, void *, ddekit_uint32_t);
+int blocking_urb_submit(struct ddekit_usb_urb *, ddekit_sem_t *, int);
+
+#endif /* !_URB_HELPER_H_ */
diff --git a/drivers/usb_storage/usb_storage.c b/drivers/usb_storage/usb_storage.c
new file mode 100755 (executable)
index 0000000..b871a0a
--- /dev/null
@@ -0,0 +1,1491 @@
+/*
+ * Minix3 USB mass storage driver implementation
+ * using DDEkit, and libblockdriver
+ */
+
+#include <sys/ioc_disk.h>              /* cases for mass_storage_ioctl */
+#ifdef USB_STORAGE_SIGNAL
+#include <sys/signal.h>                        /* signal handling */
+#endif
+
+#include <ddekit/minix/msg_queue.h>
+#include <ddekit/thread.h>
+#include <ddekit/usb.h>
+
+#include <minix/blockdriver.h>
+#include <minix/com.h>                 /* for msg_queue ranges */
+#include <minix/drvlib.h>              /* DEV_PER_DRIVE, partition */
+#include <minix/ipc.h>                 /* message */
+#include <minix/safecopies.h>          /* GRANT_VALID */
+#include <minix/sef.h>
+#include <minix/sysutil.h>             /* panic */
+#include <minix/usb.h>                 /* structures like usb_ctrlrequest */
+#include <minix/usb_ch9.h>             /* descriptor structures */
+
+#include <assert.h>
+#include <limits.h>                    /* ULONG_MAX */
+
+#include "common.h"
+#include "bulk.h"
+#include "usb_storage.h"
+#include "urb_helper.h"
+#include "scsi.h"
+
+
+/*---------------------------*
+ *    declared functions     *
+ *---------------------------*/
+/* TODO: these are missing from DDE header files */
+extern void ddekit_minix_wait_exit(void);
+extern void ddekit_shutdown(void);
+
+/* SCSI URB related prototypes */
+static int mass_storage_send_scsi_cbw_out(int, scsi_transfer *);
+static int mass_storage_send_scsi_data_in(void *, unsigned int);
+static int mass_storage_send_scsi_data_out(void *, unsigned int);
+static int mass_storage_send_scsi_csw_in(void);
+
+/* Bulk only URB related prototypes */
+static int mass_storage_send_bulk_reset(void);
+
+/* SEF related functions */
+static int mass_storage_sef_hdlr(int, sef_init_info_t *);
+static void mass_storage_signal_handler(int);
+
+/* Mass storage related prototypes */
+static void mass_storage_task(void *);
+static int mass_storage_try_first_open(void);
+static int mass_storage_transfer_restrictions(u64_t, unsigned long);
+static ssize_t mass_storage_write(unsigned long, endpoint_t, iovec_t *,
+                               unsigned int, unsigned long);
+static ssize_t mass_storage_read(unsigned long, endpoint_t, iovec_t *,
+                               unsigned int, unsigned long);
+
+/* Minix's libblockdriver callbacks */
+static int mass_storage_open(devminor_t, int);
+static int mass_storage_close(devminor_t);
+static ssize_t mass_storage_transfer(devminor_t, int, u64_t, endpoint_t,
+                                       iovec_t *, unsigned int, int);
+static int mass_storage_ioctl(devminor_t, unsigned long, endpoint_t,
+                               cp_grant_id_t, endpoint_t);
+static void mass_storage_cleanup(void);
+static struct device * mass_storage_part(devminor_t);
+static void mass_storage_geometry(devminor_t, struct part_geom *);
+
+/* DDEKit's USB driver callbacks */
+static void usb_driver_completion(void *);
+static void usb_driver_connect(struct ddekit_usb_dev *, unsigned int);
+static void usb_driver_disconnect(struct ddekit_usb_dev *);
+
+/* Simplified enumeration method for endpoint resolution */
+static int mass_storage_get_endpoints(int *, int *);
+static int mass_storage_parse_endpoint(usb_descriptor_t *, int *, int *);
+static int mass_storage_parse_descriptors(char *, unsigned int, int *, int *);
+
+
+/*---------------------------*
+ *    defined variables      *
+ *---------------------------*/
+/* Mass Storage callback structure */
+static struct blockdriver mass_storage = {
+       .bdr_type       = BLOCKDRIVER_TYPE_DISK,
+       .bdr_open       = mass_storage_open,
+       .bdr_close      = mass_storage_close,
+       .bdr_transfer   = mass_storage_transfer,
+       .bdr_ioctl      = mass_storage_ioctl,
+       .bdr_cleanup    = mass_storage_cleanup,
+       .bdr_part       = mass_storage_part,
+       .bdr_geometry   = mass_storage_geometry,
+       .bdr_intr       = NULL,
+       .bdr_alarm      = NULL,
+       .bdr_other      = NULL,
+       .bdr_device     = NULL
+};
+
+/* USB callback structure */
+static struct ddekit_usb_driver mass_storage_driver = {
+       .completion     = usb_driver_completion,
+       .connect        = usb_driver_connect,
+       .disconnect     = usb_driver_disconnect
+};
+
+/* Instance of global driver information */
+mass_storage_state driver_state;
+
+/* Tags used to pair CBW and CSW for bulk communication
+ * With this we can check if SCSI reply was meant for SCSI request */
+static unsigned int current_cbw_tag = 0;       /* What shall be send next */
+static unsigned int last_cbw_tag = 0;          /* What was sent recently */
+
+/* Semaphore used to block mass storage thread to
+ * allow DDE dispatcher operation */
+static ddekit_sem_t * mass_storage_sem = NULL;
+
+/* Mass storage (using libblockdriver) thread */
+ddekit_thread_t * mass_storage_thread;
+
+/* Static URB buffer size (must be multiple of SECTOR_SIZE) */
+#define BUFFER_SIZE (64*SECTOR_SIZE)
+
+/* Large buffer for URB read/write operations */
+static unsigned char buffer[BUFFER_SIZE];
+
+/* Length of local buffer where descriptors are temporarily stored */
+#define MAX_DESCRIPTORS_LEN 128
+
+
+/*---------------------------*
+ *    defined functions      *
+ *---------------------------*/
+/*===========================================================================*
+ *    main                                                                   *
+ *===========================================================================*/
+int
+main(int argc, char * argv[])
+{
+       MASS_DEBUG_MSG(THIS_EXEC_NAME" starting...");
+
+       /* Store arguments for future parsing */
+       env_setargs(argc, argv);
+
+       /* Clear current state */
+       memset(&driver_state, 0, sizeof(driver_state));
+
+       /* Initialize SEF related callbacks */
+       sef_setcb_init_fresh(mass_storage_sef_hdlr);
+       sef_setcb_init_lu(mass_storage_sef_hdlr);
+       sef_setcb_init_restart(mass_storage_sef_hdlr);
+       sef_setcb_signal_handler(mass_storage_signal_handler);
+
+       /* Initialize DDEkit (involves sef_startup()) */
+       ddekit_init();
+       MASS_DEBUG_MSG("DDEkit ready...");
+
+       /* Semaphore initialization */
+       mass_storage_sem = ddekit_sem_init(0);
+       assert(NULL != mass_storage_sem);
+
+       /* Starting mass storage thread */
+       mass_storage_thread = ddekit_thread_create(mass_storage_task,
+                                               NULL, "mass_storage_task");
+       MASS_DEBUG_MSG("libblockdriver ready...");
+
+       /* Run USB client */
+       ddekit_usb_init(&mass_storage_driver, NULL, NULL);
+
+       /* TODO: never reached */
+
+       /* Block and wait */
+       ddekit_minix_wait_exit();
+       MASS_DEBUG_MSG(THIS_EXEC_NAME" exiting...");
+
+       /* Semaphore release */
+       ddekit_sem_deinit(mass_storage_sem);
+
+       /* TODO: no ddekit_deinit for proper cleanup? */
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_send_scsi_cbw_out                                         *
+ *===========================================================================*/
+static int
+mass_storage_send_scsi_cbw_out(int scsi_cmd, scsi_transfer * info)
+{
+       /* URB to be send */
+       struct ddekit_usb_urb urb;
+
+       /* CBW data buffer */
+       mass_storage_cbw cbw;
+
+       MASS_DEBUG_DUMP;
+
+       /* Reset URB and assign given values */
+       init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_BLK,
+               driver_state.cur_periph->ep_out, DDEKIT_USB_OUT, 0);
+
+       /* Reset CBW and assign default values */
+       init_cbw(&cbw, last_cbw_tag = current_cbw_tag++);
+
+       /* Fill CBW with SCSI command */
+       if (create_scsi_cmd(&cbw, scsi_cmd, info))
+               return EXIT_FAILURE;
+
+       /* Attach CBW to URB */
+       attach_urb_data(&urb, URB_BUF_TYPE_DATA, &cbw, sizeof(cbw));
+
+       /* Send and wait for response */
+       if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
+               return EXIT_FAILURE;
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_send_scsi_data_in                                         *
+ *===========================================================================*/
+static int
+mass_storage_send_scsi_data_in(void * buf, unsigned int in_len)
+{
+       /* URB to be send */
+       struct ddekit_usb_urb urb;
+
+       MASS_DEBUG_DUMP;
+
+       /* Reset URB and assign given values */
+       init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_BLK,
+               driver_state.cur_periph->ep_in, DDEKIT_USB_IN, 0);
+
+       /* Attach buffer to URB */
+       attach_urb_data(&urb, URB_BUF_TYPE_DATA, buf, in_len);
+
+       /* Send and wait for response */
+       if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
+               return EXIT_FAILURE;
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_send_scsi_data_out                                        *
+ *===========================================================================*/
+static int
+mass_storage_send_scsi_data_out(void * buf, unsigned int out_len)
+{
+       /* URB to be send */
+       struct ddekit_usb_urb urb;
+
+       MASS_DEBUG_DUMP;
+
+       /* Reset URB and assign given values */
+       init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_BLK,
+               driver_state.cur_periph->ep_out, DDEKIT_USB_OUT, 0);
+
+       /* Attach buffer to URB */
+       attach_urb_data(&urb, URB_BUF_TYPE_DATA, buf, out_len);
+
+       /* Send and wait for response */
+       if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
+               return EXIT_FAILURE;
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_send_scsi_csw_in                                          *
+ *===========================================================================*/
+static int
+mass_storage_send_scsi_csw_in(void)
+{
+       /* URB to be send */
+       struct ddekit_usb_urb urb;
+
+       /* CBW data buffer */
+       mass_storage_csw csw;
+
+       MASS_DEBUG_DUMP;
+
+       /* Reset URB and assign given values */
+       init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_BLK,
+               driver_state.cur_periph->ep_in, DDEKIT_USB_IN, 0);
+
+       /* Clear CSW for receiving */
+       init_csw(&csw);
+
+       /* Attach CSW to URB */
+       attach_urb_data(&urb, URB_BUF_TYPE_DATA, &csw, sizeof(csw));
+
+       /* Send and wait for response */
+       if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
+               return EXIT_FAILURE;
+
+       /* Check for proper reply */
+       if (check_csw(&csw, last_cbw_tag))
+               return EXIT_FAILURE;
+
+       return EXIT_SUCCESS;
+}
+
+/*===========================================================================*
+ *    mass_storage_send_bulk_reset                                           *
+ *===========================================================================*/
+static int
+mass_storage_send_bulk_reset(void)
+{
+       /* URB to be send */
+       struct ddekit_usb_urb urb;
+
+       /* Setup buffer to be send */
+       struct usb_ctrlrequest bulk_setup;
+
+       MASS_DEBUG_DUMP;
+
+       /* Reset URB and assign given values */
+       init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_CTL, 0,
+               DDEKIT_USB_OUT, 0);
+
+       /* Clear setup data */
+       memset(&bulk_setup, 0, sizeof(bulk_setup));
+
+       /* For explanation of these values see usbmassbulk_10.pdf */
+       /* 3.1 Bulk-Only Mass Storage Reset */
+       bulk_setup.bRequestType = 0x21; /* Class, Interface, host to device */
+       bulk_setup.bRequest = 0xff;
+       bulk_setup.wValue = 0x00;
+       bulk_setup.wIndex = 0x00; /* TODO: hard-coded interface 0 */
+       bulk_setup.wLength = 0x00;
+
+       /* Attach request to URB */
+       attach_urb_data(&urb, URB_BUF_TYPE_SETUP,
+                       &bulk_setup, sizeof(bulk_setup));
+
+       /* Send and wait for response */
+       if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN))
+               return EXIT_FAILURE;
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_sef_hdlr                                                  *
+ *===========================================================================*/
+static int
+mass_storage_sef_hdlr(int type, sef_init_info_t * UNUSED(info))
+{
+       int env_res;
+
+       MASS_DEBUG_DUMP;
+
+       /* Parse given environment */
+       env_res = env_parse("instance", "d", 0,
+                       &(driver_state.instance),0, 255);
+
+       /* Get instance number */
+       if (EP_UNSET == env_res) {
+               MASS_MSG("Instance number was not supplied");
+               driver_state.instance = 0;
+       } else {
+               /* Only SET and UNSET are allowed */
+               if (EP_SET != env_res)
+                       return EXIT_FAILURE;
+       }
+
+       switch (type) {
+               case SEF_INIT_FRESH:
+                       /* Announce we are up! */
+                       blockdriver_announce(type);
+                       return EXIT_SUCCESS;
+               case SEF_INIT_LU:
+               case SEF_INIT_RESTART:
+                       MASS_MSG("Only 'fresh' SEF initialization supported\n");
+                       break;
+               default:
+                       MASS_MSG("illegal SEF type\n");
+                       break;
+       }
+
+       return EXIT_FAILURE;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_signal_handler                                            *
+ *===========================================================================*/
+static void
+mass_storage_signal_handler(int this_signal)
+{
+       MASS_DEBUG_DUMP;
+
+#ifdef USB_STORAGE_SIGNAL
+       /* Only check for termination signal, ignore anything else. */
+       if (this_signal != SIGTERM)
+               return;
+#else
+       MASS_MSG("Handling signal 0x%X", this_signal);
+#endif
+
+       ddekit_shutdown();
+
+       /* TODO: broken shutdown */
+       exit(0);
+}
+
+
+/*===========================================================================*
+ *    mass_storage_task                                                      *
+ *===========================================================================*/
+static void
+mass_storage_task(void * UNUSED(unused))
+{
+       message m;
+       int ipc_status;
+       struct ddekit_minix_msg_q * mq;
+
+       MASS_DEBUG_DUMP;
+
+       mq = ddekit_minix_create_msg_q(BDEV_RQ_BASE, BDEV_RQ_BASE + 0xff);
+
+       for (;;) {
+               ddekit_minix_rcv(mq, &m, &ipc_status);
+               blockdriver_process(&mass_storage, &m, ipc_status);
+       }
+}
+
+
+/*===========================================================================*
+ *    mass_storage_try_first_open                                            *
+ *===========================================================================*/
+static int
+mass_storage_try_first_open()
+{
+       unsigned int llba;
+       unsigned int blen;
+       unsigned char inquiry[SCSI_INQUIRY_DATA_LEN];
+       unsigned char capacity[SCSI_READ_CAPACITY_DATA_LEN];
+
+       MASS_DEBUG_DUMP;
+
+       assert(NULL != driver_state.cur_drive);
+
+       llba = 0; /* Last logical block address */
+       blen = 0; /* Block length (usually 512B) */
+
+       /* SCSI INQUIRY OUT stage */
+       if (mass_storage_send_scsi_cbw_out(SCSI_INQUIRY, NULL))
+               return EIO;
+
+       /* SCSI INQUIRY first IN stage */
+       if (mass_storage_send_scsi_data_in(inquiry, sizeof(inquiry)))
+               return EIO;
+
+       /* SCSI INQUIRY second IN stage */
+       if (mass_storage_send_scsi_csw_in())
+               return EIO;
+
+       /* Check for proper reply */
+       if (check_inquiry_reply(inquiry))
+               return EIO;
+
+       /* SCSI READ CAPACITY OUT stage */
+       if (mass_storage_send_scsi_cbw_out(SCSI_READ_CAPACITY, NULL))
+               return EIO;
+
+       /* SCSI READ CAPACITY first IN stage */
+       if (mass_storage_send_scsi_data_in(capacity, sizeof(capacity)))
+               return EIO;
+
+       /* SCSI READ CAPACITY second IN stage */
+       if (mass_storage_send_scsi_csw_in())
+               return EIO;
+
+       /* Check for proper reply */
+       if (check_read_capacity_reply(capacity, &llba, &blen))
+               return EIO;
+
+       /* For now only Minix's default SECTOR_SIZE is supported */
+       if (SECTOR_SIZE != blen)
+               panic("Invalid block size used by USB device!");
+
+       /* Get information about capacity from reply */
+       driver_state.cur_drive->disk.dv_base = 0;
+       driver_state.cur_drive->disk.dv_size = llba * blen;
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_transfer_restrictions                                     *
+ *===========================================================================*/
+static int
+mass_storage_transfer_restrictions(u64_t pos, unsigned long bytes)
+{
+       MASS_DEBUG_DUMP;
+
+       assert(NULL != driver_state.cur_device);
+
+       /* Zero-length request must not be issued */
+       if (0 == bytes) {
+               MASS_MSG("Transfer request length equals 0");
+               return EINVAL;
+       }
+
+       /* Starting position must be aligned to sector */
+       if (0 != (pos % SECTOR_SIZE)) {
+               MASS_MSG("Transfer position not divisible by %u", SECTOR_SIZE);
+               return EINVAL;
+       }
+
+       /* Length must be integer multiple of sector sizes */
+       if (0 != (bytes % SECTOR_SIZE)) {
+               MASS_MSG("Data length not divisible by %u", SECTOR_SIZE);
+               return EINVAL;
+       }
+
+       /* Guard against ridiculous 64B overflow */
+       if ((pos + bytes) <= pos) {
+               MASS_MSG("Request outside available address space");
+               return EINVAL;
+       }
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_write                                                     *
+ *===========================================================================*/
+static ssize_t
+mass_storage_write(unsigned long sector_number,
+               endpoint_t endpt,
+               iovec_t * iov,
+               unsigned int iov_count,
+               unsigned long bytes_left)
+{
+       /*
+        * This function writes whatever was put in 'iov' array
+        * (iov[0] : iov[iov_count]), into continuous region of mass storage,
+        * starting from sector 'sector_number'. Total amount of 'iov'
+        * data should be greater or equal to initial value of 'bytes_left'.
+        *
+        * Endpoint value 'endpt', determines if vectors 'iov' contain memory
+        * addresses for copying or grant IDs.
+        */
+
+       iov_state cur_iov;              /* Current state of vector copying */
+       unsigned long bytes_to_write;   /* To be written in this iteration */
+       ssize_t bytes_already_written;  /* Total amount written (retval) */
+
+       MASS_DEBUG_DUMP;
+
+       /* Initialize locals */
+       cur_iov.remaining_bytes = 0;    /* No IO vector initially */
+       cur_iov.iov_idx = 0;            /* Starting from first vector */
+       bytes_already_written = 0;      /* Nothing copied yet */
+
+       /* Mass storage operations are sector based */
+       assert(0 == (sizeof(buffer) % SECTOR_SIZE));
+       assert(0 == (bytes_left % SECTOR_SIZE));
+
+       while (bytes_left > 0) {
+
+               /* Fill write buffer with data from IO Vectors */
+               {
+                       unsigned long buf_offset;
+                       unsigned long copy_len;
+
+                       /* Start copying to the beginning of the buffer */
+                       buf_offset = 0;
+
+                       /* Copy as long as not copied vectors exist or
+                        * buffer is not fully filled */
+                       for (;;) {
+
+                               /* If entire previous vector
+                                * was used get new one */
+                               if (0 == cur_iov.remaining_bytes) {
+                                       /* Check if there are
+                                        * vectors to copied */
+                                       if (cur_iov.iov_idx < iov_count) {
+
+                                               cur_iov.base_addr =
+                                                 iov[cur_iov.iov_idx].iov_addr;
+                                               cur_iov.remaining_bytes =
+                                                 iov[cur_iov.iov_idx].iov_size;
+                                               cur_iov.offset = 0;
+                                               cur_iov.iov_idx++;
+
+                                       } else {
+                                               /* All vectors copied */
+                                               break;
+                                       }
+                               }
+
+                               /* Copy as much as it is possible from vector
+                                * and at most the amount that can be
+                                * put in buffer */
+                               copy_len = MIN(sizeof(buffer) - buf_offset,
+                                               cur_iov.remaining_bytes);
+
+                               /* This distinction is required as transfer can
+                                * be used from within this process and meaning
+                                * of IO vector'a address is different than
+                                * grant ID */
+                               if (endpt == SELF) {
+                                       memcpy(&buffer[buf_offset],
+                                               (void*)(cur_iov.base_addr +
+                                               cur_iov.offset), copy_len);
+                               } else {
+                                       ssize_t copy_res;
+                                       if ((copy_res = sys_safecopyfrom(endpt,
+                                               cur_iov.base_addr,
+                                               cur_iov.offset,
+                                               (vir_bytes)
+                                               (&buffer[buf_offset]),
+                                               copy_len))) {
+                                               MASS_MSG("sys_safecopyfrom "
+                                                       "failed");
+                                               return copy_res;
+                                       }
+                               }
+
+                               /* Alter current state of copying */
+                               buf_offset += copy_len;
+                               cur_iov.offset += copy_len;
+                               cur_iov.remaining_bytes -= copy_len;
+
+                               /* Buffer was filled */
+                               if (sizeof(buffer) == buf_offset)
+                                       break;
+                       }
+
+                       /* Determine how many bytes from copied buffer we wish
+                        * to write, buf_offset represents total amount of
+                        * bytes copied above */
+                       if (bytes_left >= buf_offset) {
+                               bytes_to_write = buf_offset;
+                               bytes_left -= buf_offset;
+                       } else {
+                               bytes_to_write = bytes_left;
+                               bytes_left = 0;
+                       }
+               }
+
+               /* Send URB and alter sector number */
+               if (bytes_to_write > 0) {
+
+                       scsi_transfer info;
+
+                       info.length = bytes_to_write;
+                       info.lba = sector_number;
+
+                       /* SCSI WRITE first OUT stage */
+                       if (mass_storage_send_scsi_cbw_out(SCSI_WRITE, &info))
+                               return EIO;
+
+                       /* SCSI WRITE second OUT stage */
+                       if (mass_storage_send_scsi_data_out(buffer,
+                                                       bytes_to_write))
+                               return EIO;
+
+                       /* SCSI WRITE IN stage */
+                       if (mass_storage_send_scsi_csw_in())
+                               return EIO;
+
+                       /* Writing completed so shift starting
+                        * sector for next iteration */
+                       sector_number += bytes_to_write / SECTOR_SIZE;
+
+                       /* Update amount of data already copied */
+                       bytes_already_written += bytes_to_write;
+               }
+       }
+
+       return bytes_already_written;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_read                                                      *
+ *===========================================================================*/
+static ssize_t
+mass_storage_read(unsigned long sector_number,
+               endpoint_t endpt,
+               iovec_t * iov,
+               unsigned int iov_count,
+               unsigned long bytes_left)
+{
+       /*
+        * This function reads 'bytes_left' bytes of mass storage data into
+        * 'iov' array (iov[0] : iov[iov_count]) starting from sector
+        * 'sector_number'. Total amount of 'iov' data should be greater or
+        * equal to initial value of 'bytes_left'.
+        *
+        * Endpoint value 'endpt', determines if vectors 'iov' contain memory
+        * addresses for copying or grant IDs.
+        */
+
+       iov_state cur_iov;              /* Current state of vector copying */
+       unsigned long bytes_to_read;    /* To be read in this iteration */
+       ssize_t bytes_already_read;     /* Total amount read (retval) */
+
+       MASS_DEBUG_DUMP;
+
+       /* Initialize locals */
+       cur_iov.remaining_bytes = 0;    /* No IO vector initially */
+       cur_iov.iov_idx = 0;            /* Starting from first vector */
+       bytes_already_read = 0;         /* Nothing copied yet */
+
+       /* Mass storage operations are sector based */
+       assert(0 == (sizeof(buffer) % SECTOR_SIZE));
+       assert(0 == (bytes_left % SECTOR_SIZE));
+
+       while (bytes_left > 0) {
+
+               /* Decide read length and alter remaining bytes */
+               {
+                       /* Number of bytes to be read in next URB */
+                       if (bytes_left > sizeof(buffer)) {
+                               bytes_to_read = sizeof(buffer);
+                       } else {
+                               bytes_to_read = bytes_left;
+                       }
+
+                       bytes_left -= bytes_to_read;
+               }
+
+               /* Send URB and alter sector number */
+               {
+                       scsi_transfer info;
+
+                       info.length = bytes_to_read;
+                       info.lba = sector_number;
+
+                       /* SCSI READ OUT stage */
+                       if (mass_storage_send_scsi_cbw_out(SCSI_READ, &info))
+                               return EIO;
+
+                       /* SCSI READ first IN stage */
+                       if (mass_storage_send_scsi_data_in(buffer,
+                                                       bytes_to_read))
+                               return EIO;
+
+                       /* SCSI READ second IN stage */
+                       if (mass_storage_send_scsi_csw_in())
+                               return EIO;
+
+                       /* Reading completed so shift starting
+                        * sector for next iteration */
+                       sector_number += bytes_to_read / SECTOR_SIZE;
+               }
+
+               /* Fill IO Vectors with data from buffer */
+               {
+                       unsigned long buf_offset;
+                       unsigned long copy_len;
+
+                       /* Start copying from the beginning of the buffer */
+                       buf_offset = 0;
+
+                       /* Copy as long as there are unfilled vectors
+                        * or data in buffer remains */
+                       for (;;) {
+
+                               /* If previous vector was filled get new one */
+                               if (0 == cur_iov.remaining_bytes) {
+                                       /* Check if there are vectors
+                                        * to be filled */
+                                       if (cur_iov.iov_idx < iov_count) {
+
+                                               cur_iov.base_addr =
+                                                 iov[cur_iov.iov_idx].iov_addr;
+                                               cur_iov.remaining_bytes =
+                                                 iov[cur_iov.iov_idx].iov_size;
+                                               cur_iov.offset = 0;
+                                               cur_iov.iov_idx++;
+
+                                       } else {
+                                               /* Total length of vectors
+                                                * should be greater or equal
+                                                * to initial value of
+                                                * bytes_left. Being here means
+                                                * that everything should
+                                                * have been copied already */
+                                               assert(0 == bytes_left);
+                                               break;
+                                       }
+                               }
+
+                               /* Copy as much as it is possible from buffer
+                                * and at most the amount that can be
+                                * put in vector */
+                               copy_len = MIN(bytes_to_read - buf_offset,
+                                               cur_iov.remaining_bytes);
+
+                               /* This distinction is required as transfer can
+                                * be used from within this process and meaning
+                                * of IO vector'a address is different than
+                                * grant ID */
+                               if (endpt == SELF) {
+                                       memcpy((void*)(cur_iov.base_addr +
+                                                       cur_iov.offset),
+                                                       &buffer[buf_offset],
+                                                       copy_len);
+                               } else {
+                                       ssize_t copy_res;
+                                       if ((copy_res = sys_safecopyto(endpt,
+                                               cur_iov.base_addr,
+                                               cur_iov.offset,
+                                               (vir_bytes)
+                                               (&buffer[buf_offset]),
+                                               copy_len))) {
+                                               MASS_MSG("sys_safecopyto "
+                                                       "failed");
+                                               return copy_res;
+                                       }
+                               }
+
+                               /* Alter current state of copying */
+                               buf_offset += copy_len;
+                               cur_iov.offset += copy_len;
+                               cur_iov.remaining_bytes -= copy_len;
+
+                               /* Everything was copied */
+                               if (bytes_to_read == buf_offset)
+                                       break;
+                       }
+
+                       /* Update amount of data already copied */
+                       bytes_already_read += buf_offset;
+               }
+       }
+
+       return bytes_already_read;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_open                                                      *
+ *===========================================================================*/
+static int
+mass_storage_open(devminor_t minor, int UNUSED(access))
+{
+       mass_storage_drive * d;
+       int r;
+
+       MASS_DEBUG_DUMP;
+
+       /* Decode minor into drive device information */
+       if (NULL == (mass_storage_part(minor)))
+               return ENXIO;
+
+       /* Copy evaluated current drive for simplified dereference */
+       d = driver_state.cur_drive;
+
+       /* In case of previous CBW mismatch */
+       if (mass_storage_send_bulk_reset()) {
+               MASS_MSG("Resetting mass storage device failed");
+               return EIO;
+       }
+
+       /* In case of missing endpoint information, do simple
+        * enumeration and hold it for future use */
+       if ((-1 == driver_state.cur_periph->ep_in) ||
+               (-1 == driver_state.cur_periph->ep_out)) {
+
+               if (mass_storage_get_endpoints(&driver_state.cur_periph->ep_in,
+                                       &driver_state.cur_periph->ep_out))
+                       return EIO;
+       }
+
+       /* If drive hasn't been opened yet, try to open it */
+       if (d->open_ct == 0) {
+               if ((r = mass_storage_try_first_open())) {
+                       MASS_MSG("Opening mass storage device"
+                               " for the first time failed");
+                       return r;
+               }
+
+               /* Clear remembered device state for current
+                * drive before calling partition */
+               memset(&d->part[0],     0, sizeof(d->part));
+               memset(&d->subpart[0],  0, sizeof(d->subpart));
+
+               /* Try parsing partition table (for entire drive) */
+               /* Warning!! This call uses mass_storage_part with own minors
+                * and alters global driver_state.cur_device! */
+               partition(&mass_storage, (d->drive_idx * DEV_PER_DRIVE),
+                       P_PRIMARY, 0);
+
+               /* Decode minor into UPDATED drive device information */
+               if (NULL == (mass_storage_part(minor)))
+                       return ENXIO;
+
+               /* Decoded size must be positive or else
+                * we assume device (partition) is unavailable */
+               if (0 == driver_state.cur_device->dv_size)
+                       return ENXIO;
+       }
+
+       /* SCSI TEST UNIT READY OUT stage */
+       if (mass_storage_send_scsi_cbw_out(SCSI_TEST_UNIT_READY, NULL))
+               return EIO;
+
+       /* SCSI TEST UNIT READY IN stage */
+       if (mass_storage_send_scsi_csw_in())
+               return EIO;
+
+       /* Opening completed */
+       d->open_ct++;
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_close                                                     *
+ *===========================================================================*/
+static int mass_storage_close(devminor_t minor)
+{
+       MASS_DEBUG_DUMP;
+
+       /* Decode minor into drive device information */
+       if (NULL == (mass_storage_part(minor)))
+               return ENXIO;
+
+       /* If drive hasn't been opened yet */
+       if (driver_state.cur_drive->open_ct == 0) {
+               MASS_MSG("Device was not opened yet");
+               return ERESTART;
+       }
+
+       /* Act accordingly */
+       driver_state.cur_drive->open_ct--;
+
+       return EXIT_SUCCESS;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_transfer                                                  *
+ *===========================================================================*/
+static ssize_t
+mass_storage_transfer(devminor_t minor,                /* device minor number */
+               int do_write,                   /* 1 write, 0 read */
+               u64_t pos,                      /* position of starting point */
+               endpoint_t endpt,               /* endpoint */
+               iovec_t * iov,                  /* vector */
+               unsigned int iov_count,         /* how many vectors */
+               int UNUSED(flags))              /* transfer flags */
+{
+       u64_t temp_sector_number;
+       unsigned long bytes;
+       unsigned long sector_number;
+       unsigned int cur_iov_idx;
+       int r;
+
+       MASS_DEBUG_DUMP;
+
+       /* Decode minor into drive device information */
+       if (NULL == (mass_storage_part(minor)))
+               return ENXIO;
+
+       bytes = 0;
+
+       /* How much data is going to be transferred? */
+       for (cur_iov_idx = 0; cur_iov_idx < iov_count; ++cur_iov_idx) {
+
+               /* Check if grant ID was supplied
+                * instead of address and if it is valid */
+               if (endpt != SELF)
+                       if (!GRANT_VALID((cp_grant_id_t)
+                               (iov[cur_iov_idx].iov_addr)))
+                               return EINVAL;
+
+               /* All supplied vectors must have positive length */
+               if ((signed long)(iov[cur_iov_idx].iov_size) <= 0) {
+                       MASS_MSG("Transfer request length is not positive");
+                       return EINVAL;
+               }
+
+               /* Requirements were met, more bytes can be transferred */
+               bytes += iov[cur_iov_idx].iov_size;
+
+               /* Request size must never overflow */
+               if ((signed long)bytes <= 0) {
+                       MASS_MSG("Transfer request length overflowed");
+                       return EINVAL;
+               }
+       }
+
+       /* Check if reading beyond device/partition */
+       if (pos >= driver_state.cur_device->dv_size) {
+               MASS_MSG("Request out of bounds for given device");
+               return 0; /* No error and no bytes read */
+       }
+
+       /* Check if arguments agree with accepted restriction
+        * for parameters of transfer */
+       if ((r = mass_storage_transfer_restrictions(pos, bytes)))
+               return r;
+
+       /* If 'hard' requirements above were met, do additional
+        * limiting to device/partition boundary */
+       if ((pos + bytes) > driver_state.cur_device->dv_size)
+               bytes = (driver_state.cur_device->dv_size - pos) &
+                       ~SECTOR_MASK;
+
+       /* We have to obtain sector number of given position
+        * and limit it to what URB can handle */
+       temp_sector_number = (driver_state.cur_device->dv_base + pos) /
+                               SECTOR_SIZE;
+       assert(temp_sector_number < ULONG_MAX); /* LBA is limited to 32B */
+       sector_number = (unsigned long)temp_sector_number;
+
+       if (do_write)
+               return mass_storage_write(sector_number, endpt, iov,
+                                       iov_count, bytes);
+       else
+               return mass_storage_read(sector_number, endpt, iov,
+                                       iov_count, bytes);
+}
+
+
+/*===========================================================================*
+ *    mass_storage_ioctl                                                     *
+ *===========================================================================*/
+static int
+mass_storage_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
+                       cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
+{
+       MASS_DEBUG_DUMP;
+
+       /* Decode minor into drive device information */
+       if (NULL == (mass_storage_part(minor)))
+               return ENXIO;
+
+       switch (request) {
+               case DIOCOPENCT:
+                       if (sys_safecopyto(endpt, grant, 0,
+                               (vir_bytes) &(driver_state.cur_drive->open_ct),
+                               sizeof(driver_state.cur_drive->open_ct)))
+                               panic("sys_safecopyto failed!");
+
+                       return EXIT_SUCCESS;
+
+               default:
+                       MASS_MSG("Unimplemented IOCTL request: 0x%X",
+                               (int)request);
+                       break;
+       }
+
+       return ENOTTY;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_cleanup                                                   *
+ *===========================================================================*/
+static void mass_storage_cleanup(void)
+{
+       MASS_DEBUG_DUMP;
+       return;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_part                                                      *
+ *===========================================================================*/
+static struct device *
+mass_storage_part(devminor_t minor)
+{
+       unsigned long sel_drive;
+       unsigned long sel_device;
+
+       MASS_DEBUG_DUMP;
+
+       /* Override every time before further decision */
+       driver_state.cur_drive = NULL;
+       driver_state.cur_device = NULL;
+       driver_state.cur_periph = NULL;
+
+       /* Decode 'minor' code to find which device file was used */
+       if (minor < MINOR_d0p0s0) {
+
+               /* No sub-partitions used */
+               sel_drive = minor / DEV_PER_DRIVE;
+               sel_device = minor % DEV_PER_DRIVE;
+
+               /* Only valid minors */
+               if (sel_drive < MAX_DRIVES) {
+                       /* Associate minor (device/partition)
+                        * with peripheral number */
+                       /* TODO:PERIPH
+                        * Proper peripheral selection based
+                        * on minor should be here: */
+                       driver_state.cur_periph = &driver_state.periph[0];
+
+                       /* Select drive entry for opening count etc. */
+                       driver_state.cur_drive =
+                               &(driver_state.cur_periph->drives[sel_drive]);
+
+                       /* Select device entry for given device file */
+                       /* Device 0 means entire drive.
+                        * Devices 1,2,3,4 mean partitions 0,1,2,3 */
+                       if (0 == sel_device)
+                               driver_state.cur_device =
+                                       &(driver_state.cur_drive->disk);
+                       else
+                               driver_state.cur_device =
+                                       &(driver_state.cur_drive->part
+                                                               [sel_device-1]);
+               }
+
+       } else {
+
+               /* Shift values accordingly */
+               minor -= MINOR_d0p0s0;
+
+               /* Sub-partitions used */
+               sel_drive = minor / SUBPART_PER_DISK;
+               sel_device = minor % SUBPART_PER_DISK;
+
+               /* Only valid minors */
+               if (sel_drive < MAX_DRIVES) {
+                       /* Leave in case of ridiculously high number */
+                       if (minor < SUBPART_PER_DISK) {
+                               /* Associate minor (device/partition)
+                                * with peripheral number */
+                               /* TODO:PERIPH
+                                * Proper peripheral selection based
+                                * on minor should be here: */
+                               driver_state.cur_periph =
+                                               &driver_state.periph[0];
+
+                               /* Select drive entry for opening count etc. */
+                               driver_state.cur_drive =
+                                       &(driver_state.cur_periph->drives
+                                                               [sel_drive]);
+                               /* Select device entry for given
+                                * sub-partition device file */
+                               driver_state.cur_device =
+                                       &(driver_state.cur_drive->subpart
+                                                               [sel_device]);
+                       }
+               }
+       }
+
+       /* Check for success */
+       if (NULL == driver_state.cur_device) {
+               MASS_MSG("Device for minor: %u not found", minor);
+       } else {
+               /* Assign index as well */
+               driver_state.cur_drive->drive_idx = sel_drive;
+       }
+
+       return driver_state.cur_device;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_geometry                                                  *
+ *===========================================================================*/
+#ifdef MASS_USE_GEOMETRY
+static void
+mass_storage_geometry(devminor_t minor, struct part_geom * part)
+{
+       char flexible_disk_page[SCSI_MODE_SENSE_FLEX_DATA_LEN];
+
+       MASS_DEBUG_DUMP;
+
+       /* Decode minor into drive device information */
+       if (NULL == (mass_storage_part(minor)))
+               return;
+
+       /* SCSI MODE SENSE OUT stage */
+       if (mass_storage_send_scsi_cbw_out(SCSI_MODE_SENSE, NULL))
+               return;
+
+       /* SCSI MODE SENSE first IN stage */
+       if (mass_storage_send_scsi_data_in(flexible_disk_page,
+                                       sizeof(flexible_disk_page)))
+               return;
+
+       /* SCSI MODE SENSE second IN stage */
+       if (mass_storage_send_scsi_csw_in())
+               return;
+
+       /* Get geometry from reply */
+       if (check_mode_sense_reply(flexible_disk_page, &(part->cylinders),
+                               &(part->heads), &(part->sectors)))
+               return;
+#else
+static void
+mass_storage_geometry(devminor_t UNUSED(minor), struct part_geom * part)
+{
+       part->cylinders = part->size / SECTOR_SIZE;
+       part->heads = 64;
+       part->sectors = 32;
+#endif
+}
+
+
+/*===========================================================================*
+ *    usb_driver_completion                                                  *
+ *===========================================================================*/
+static void
+usb_driver_completion(void * UNUSED(priv))
+{
+       /* Last request was completed so allow continuing
+        * execution from place where semaphore was downed */
+       ddekit_sem_up(mass_storage_sem);
+}
+
+
+/*===========================================================================*
+ *    usb_driver_connect                                                     *
+ *===========================================================================*/
+static void
+usb_driver_connect(struct ddekit_usb_dev * dev,
+               unsigned int interfaces)
+{
+       MASS_DEBUG_DUMP;
+
+       /* TODO:PERIPH
+        * Some sort of more complex peripheral assignment should be here */
+       driver_state.cur_periph = &driver_state.periph[0];
+
+       if (NULL != driver_state.cur_periph->dev)
+               panic("Only one peripheral can be connected!");
+
+       /* Hold host information for future use */
+       driver_state.cur_periph->dev = dev;
+       driver_state.cur_periph->interfaces = interfaces;
+       driver_state.cur_periph->ep_in = -1;
+       driver_state.cur_periph->ep_out = -1;
+}
+
+
+/*===========================================================================*
+ *    usb_driver_disconnect                                                  *
+ *===========================================================================*/
+static void
+usb_driver_disconnect(struct ddekit_usb_dev * UNUSED(dev))
+{
+       MASS_DEBUG_DUMP;
+
+       /* TODO:PERIPH
+        * Some sort of peripheral discard should be here */
+       driver_state.cur_periph = &driver_state.periph[0];
+
+       assert(NULL != driver_state.cur_periph->dev);
+
+       /* Clear */
+       driver_state.cur_periph->dev = NULL;
+       driver_state.cur_periph->interfaces = 0;
+       driver_state.cur_periph->ep_in = -1;
+       driver_state.cur_periph->ep_out = -1;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_get_endpoints                                             *
+ *===========================================================================*/
+static int
+mass_storage_get_endpoints(int * ep_in, int * ep_out)
+{
+       /* URB to be send */
+       struct ddekit_usb_urb urb;
+
+       /* Setup buffer to be attached */
+       struct usb_ctrlrequest setup_buf;
+
+       /* Descriptors' buffer */
+       unsigned char descriptors[MAX_DESCRIPTORS_LEN];
+
+       MASS_DEBUG_DUMP;
+
+       /* Reset URB and assign given values */
+       init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_CTL, 0,
+               DDEKIT_USB_IN, 0);
+
+       /* Clear setup data */
+       memset(&setup_buf, 0, sizeof(setup_buf));
+
+       /* Standard get endpoint request */
+       setup_buf.bRequestType = 0x80; /* Device to host */
+       setup_buf.bRequest = UR_GET_DESCRIPTOR;
+       setup_buf.wValue = UDESC_CONFIG << 8; /* TODO: configuration 0 */
+       setup_buf.wIndex = 0x00;
+       setup_buf.wLength = MAX_DESCRIPTORS_LEN;
+
+       /* Attach buffers to URB */
+       attach_urb_data(&urb, URB_BUF_TYPE_SETUP,
+                       &setup_buf, sizeof(setup_buf));
+       attach_urb_data(&urb, URB_BUF_TYPE_DATA,
+                       descriptors, sizeof(descriptors));
+
+       /* Send and wait for response */
+       if (blocking_urb_submit(&urb, mass_storage_sem,
+                               URB_SUBMIT_ALLOW_MISMATCH))
+               return EXIT_FAILURE;
+
+       /* Check if buffer was supposed to hold more data */
+       if (urb.size < urb.actual_length) {
+               MASS_MSG("Too much descriptor data received");
+               return EXIT_FAILURE;
+       }
+
+       return mass_storage_parse_descriptors(urb.data, urb.actual_length,
+                                               ep_in, ep_out);
+}
+
+
+/*===========================================================================*
+ *    mass_storage_parse_endpoint                                            *
+ *===========================================================================*/
+static int
+mass_storage_parse_endpoint(usb_descriptor_t * cur_desc,
+                       int * ep_in, int * ep_out)
+{
+       usb_endpoint_descriptor_t * ep_desc;
+
+       MASS_DEBUG_DUMP;
+
+       ep_desc = (usb_endpoint_descriptor_t *)cur_desc;
+
+       /* Only bulk with no other attributes are important */
+       if (UE_BULK == ep_desc->bmAttributes) {
+
+               /* Check for direction */
+               if (UE_DIR_IN == UE_GET_DIR(ep_desc->bEndpointAddress)) {
+
+                       if (-1 != *ep_in) {
+                               MASS_MSG("BULK IN already set");
+                               return EXIT_FAILURE;
+                       }
+
+                       *ep_in = UE_GET_ADDR(ep_desc->bEndpointAddress);
+
+               } else {
+
+                       if (-1 != *ep_out) {
+                               MASS_MSG("BULK OUT already set");
+                               return EXIT_FAILURE;
+                       }
+
+                       *ep_out = UE_GET_ADDR(ep_desc->bEndpointAddress);
+               }
+       }
+
+       return EXIT_SUCCESS;
+}
+
+/*===========================================================================*
+ *    mass_storage_parse_descriptors                                         *
+ *===========================================================================*/
+static int
+mass_storage_parse_descriptors(char * desc_buf, unsigned int buf_len,
+                               int * ep_in, int * ep_out)
+{
+       /* Currently parsed, descriptors */
+       usb_descriptor_t * cur_desc;
+       usb_interface_descriptor_t * ifc_desc;
+
+       /* Byte counter for descriptor parsing */
+       unsigned int cur_byte;
+
+       /* Non zero if recently parsed interface is valid for this device */
+       int valid_interface;
+
+       MASS_DEBUG_DUMP;
+
+       /* Parse descriptors to get endpoints */
+       *ep_in = -1;
+       *ep_out = -1;
+       valid_interface = 0;
+       cur_byte = 0;
+
+       while (cur_byte < buf_len) {
+
+               /* Map descriptor to buffer */
+               /* Structure is packed so alignment should not matter */
+               cur_desc = (usb_descriptor_t *)&(desc_buf[cur_byte]);
+
+               /* Check this so we won't be reading
+                * memory outside the buffer */
+               if ((cur_desc->bLength > 3) &&
+                       (cur_byte + cur_desc->bLength <= buf_len)) {
+
+                       /* Parse based on descriptor type */
+                       switch (cur_desc->bDescriptorType) {
+
+                               case UDESC_CONFIG: {
+                                       if (USB_CONFIG_DESCRIPTOR_SIZE !=
+                                               cur_desc->bLength) {
+                                               MASS_MSG("Wrong configuration"
+                                                       " descriptor length");
+                                               return EXIT_FAILURE;
+                                       }
+                                       break;
+                               }
+
+                               case UDESC_STRING:
+                                       break;
+
+                               case UDESC_INTERFACE: {
+                                       ifc_desc =
+                                        (usb_interface_descriptor_t *)cur_desc;
+
+                                       if (USB_INTERFACE_DESCRIPTOR_SIZE !=
+                                               cur_desc->bLength) {
+                                               MASS_MSG("Wrong interface"
+                                                       " descriptor length");
+                                               return EXIT_FAILURE;
+                                       }
+
+                                       /* Check if following data is meant
+                                        * for our interfaces */
+                                       if ((1 << ifc_desc->bInterfaceNumber) &
+                                           driver_state.cur_periph->interfaces)
+                                               valid_interface = 1;
+                                       else
+                                               valid_interface = 0;
+
+                                       break;
+                               }
+
+                               case UDESC_ENDPOINT: {
+                                       if (USB_ENDPOINT_DESCRIPTOR_SIZE !=
+                                               cur_desc->bLength) {
+                                               MASS_MSG("Wrong endpoint"
+                                                       " descriptor length");
+                                               return EXIT_FAILURE;
+                                       }
+
+                                       /* Previous interface was,
+                                        * what we were looking for */
+                                       if (valid_interface) {
+                                               if (EXIT_SUCCESS !=
+                                                   mass_storage_parse_endpoint(
+                                                   cur_desc, ep_in, ep_out))
+                                                       return EXIT_FAILURE;
+
+                                       }
+
+                                       break;
+                               }
+
+                               default: {
+                                       MASS_MSG("Wrong descriptor type");
+                                       return EXIT_FAILURE;
+                               }
+                       }
+
+               } else {
+                       MASS_MSG("Invalid descriptor length");
+                       return EXIT_FAILURE;
+               }
+
+               /* Get next descriptor */
+               cur_byte += cur_desc->bLength;
+       }
+
+       /* Total length should match sum of all descriptors' lengths... */
+       if (cur_byte > buf_len)
+               return EXIT_FAILURE;
+
+       /* ...and descriptors should be valid */
+       if ((-1 == *ep_in) || (-1 == *ep_out)) {
+               MASS_MSG("Valid bulk endpoints not found");
+               return EXIT_FAILURE;
+       }
+
+       return EXIT_SUCCESS;
+}
diff --git a/drivers/usb_storage/usb_storage.conf b/drivers/usb_storage/usb_storage.conf
new file mode 100755 (executable)
index 0000000..a00376b
--- /dev/null
@@ -0,0 +1,15 @@
+service usb_storage
+{
+       system
+               PRIVCTL     #  4
+               UMAP        # 14
+               IRQCTL      # 19
+               DEVIO       # 21
+               SDEVIO      # 22
+               ;
+       ipc
+               SYSTEM pm rs log tty ds vfs vm amddev devman
+               pci usbd
+               ;
+       uid     0;
+};
diff --git a/drivers/usb_storage/usb_storage.h b/drivers/usb_storage/usb_storage.h
new file mode 100755 (executable)
index 0000000..1734498
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Minix3 USB mass storage driver definitions
+ */
+
+#ifndef _USB_STORAGE_H_
+#define _USB_STORAGE_H_
+
+#include <minix/driver.h>              /* struct device */
+#include <minix/type.h>                        /* vir_bytes */
+/* TODO: no header for ddekit_usb_dev */
+
+/* Number of handled peripherals (USB devices) */
+#define MAX_PERIPHS                    (1)
+/* Number of handled disks per driver */
+#define MAX_DRIVES                     (4)
+/* 4 partitions per disk */
+#define PART_PER_DISK                  (4)
+/* 4 sub partitions per partition */
+#define SUBPART_PER_PART               (4)
+/* 16 sub partitions per disk */
+#define SUBPART_PER_DISK               (PART_PER_DISK * SUBPART_PER_PART)
+
+/*---------------------------*
+ *    declared types         *
+ *---------------------------*/
+/* Information on opened mass storage drives */
+typedef struct mass_storage_drive {
+
+       struct device disk;                     /* disk device */
+       struct device part[PART_PER_DISK];      /* partition devices */
+       struct device subpart[SUBPART_PER_DISK];/* sub-partition devices */
+       unsigned long open_ct;                  /* opening counter */
+       int drive_idx;                          /* Index of this drive */
+}
+mass_storage_drive;
+
+/* Information on attached peripherals (USB devices) */
+typedef struct mass_storage_periph {
+
+       mass_storage_drive drives[MAX_DRIVES];  /* Possible drive info */
+       struct ddekit_usb_dev * dev;            /* DDEKit device handler */
+       unsigned int interfaces;                /* Interfaces bitmap */
+       int ep_in;                              /* Bulk IN endpoint*/
+       int ep_out;                             /* Bulk OUT endpoint*/
+}
+mass_storage_periph;
+
+/* Structure for the information on current state of driver */
+typedef struct mass_storage_state {
+
+       /* DDEKit device handlers */
+       mass_storage_periph periph[MAX_PERIPHS];
+
+       /* Currently used peripheral */
+       mass_storage_periph * cur_periph;
+
+       /* Currently used drive */
+       mass_storage_drive * cur_drive;
+
+       /* Currently used device (drive/partition/sub-partition) */
+       struct device * cur_device;
+
+       /* Driver instance */
+       long instance;
+}
+mass_storage_state;
+
+/* State of current IO vector array for transfer operations */
+typedef struct iov_state {
+
+       vir_bytes base_addr;            /* Address to read/write or grant ID */
+       vir_bytes remaining_bytes;      /* How many bytes remain in vector */
+       vir_bytes offset;               /* How many bytes were copied */
+       unsigned int iov_idx;           /* Index of currently selected vector */
+}
+iov_state;
+
+#endif /* !_USB_STORAGE_H_ */