minix/drivers/usb_storage/usb_storage.c
Kees Jongenburger dfb2b8398d usb:adding usb mass storage driver.
Change-Id: I9e431d56eddfeec21413c290b2fa7ad35b566f6b

http://gerrit.minix3.org/#/c/2690/
2014-07-28 17:05:39 +02:00

1492 lines
41 KiB
C
Executable file

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