From: Kees Jongenburger Date: Mon, 26 May 2014 14:47:48 +0000 (+0200) Subject: usb:adding usb mass storage driver. X-Git-Tag: v3.3.0~271 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/verify-sign/verify.pl?a=commitdiff_plain;h=dfb2b8398dc45641f999e34d5a2067dc6000e42f;p=minix.git usb:adding usb mass storage driver. Change-Id: I9e431d56eddfeec21413c290b2fa7ad35b566f6b http://gerrit.minix3.org/#/c/2690/ --- diff --git a/drivers/usb_storage/Makefile b/drivers/usb_storage/Makefile new file mode 100755 index 000000000..430faa737 --- /dev/null +++ b/drivers/usb_storage/Makefile @@ -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 diff --git a/drivers/usb_storage/README.txt b/drivers/usb_storage/README.txt new file mode 100755 index 000000000..f560c28a0 --- /dev/null +++ b/drivers/usb_storage/README.txt @@ -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 index 000000000..e7199833a --- /dev/null +++ b/drivers/usb_storage/bulk.c @@ -0,0 +1,39 @@ +/* + * "Bulk only transfer" related implementation + */ + +#include +#include /* 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 index 000000000..2e7dc1994 --- /dev/null +++ b/drivers/usb_storage/bulk.h @@ -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 index 000000000..223146a7e --- /dev/null +++ b/drivers/usb_storage/common.h @@ -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 /* For things, like EXIT_*, NULL, ... */ +#include + +/*---------------------------* + * 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 index 000000000..f4ff43e1a --- /dev/null +++ b/drivers/usb_storage/scsi.c @@ -0,0 +1,266 @@ +/* + * SCSI commands related implementation + */ + +#include /* SECTOR_SIZE */ + +#include +#include /* 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 index 000000000..91d28ef75 --- /dev/null +++ b/drivers/usb_storage/scsi.h @@ -0,0 +1,143 @@ +/* + * SCSI commands related definitions + */ + +#ifndef _SCSI_H_ +#define _SCSI_H_ + +#if 0 +#include /* 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 index 000000000..7302848fa --- /dev/null +++ b/drivers/usb_storage/urb_helper.c @@ -0,0 +1,112 @@ +/* + * URB formatting related implementation + */ + +#include /* panic */ +#include /* struct usb_ctrlrequest */ + +#include /* memset */ +#include + +#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 index 000000000..5046edb65 --- /dev/null +++ b/drivers/usb_storage/urb_helper.h @@ -0,0 +1,30 @@ +/* + * URB formatting related definitions + */ + +#ifndef _URB_HELPER_H_ +#define _URB_HELPER_H_ + +#include +#include + +/* 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 index 000000000..b871a0a10 --- /dev/null +++ b/drivers/usb_storage/usb_storage.c @@ -0,0 +1,1491 @@ +/* + * Minix3 USB mass storage driver implementation + * using DDEkit, and libblockdriver + */ + +#include /* cases for mass_storage_ioctl */ +#ifdef USB_STORAGE_SIGNAL +#include /* signal handling */ +#endif + +#include +#include +#include + +#include +#include /* for msg_queue ranges */ +#include /* DEV_PER_DRIVE, partition */ +#include /* message */ +#include /* GRANT_VALID */ +#include +#include /* panic */ +#include /* structures like usb_ctrlrequest */ +#include /* descriptor structures */ + +#include +#include /* 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 index 000000000..a00376b18 --- /dev/null +++ b/drivers/usb_storage/usb_storage.conf @@ -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 index 000000000..1734498c0 --- /dev/null +++ b/drivers/usb_storage/usb_storage.h @@ -0,0 +1,78 @@ +/* + * Minix3 USB mass storage driver definitions + */ + +#ifndef _USB_STORAGE_H_ +#define _USB_STORAGE_H_ + +#include /* struct device */ +#include /* 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_ */