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