--- /dev/null
+/*
+ * 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;
+}