e0f92bef05
Makes usb_storage remember endpoint interval and other parameters, instead of endpoint number only.
1548 lines
43 KiB
C
Executable file
1548 lines
43 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);
|
|
|
|
/* DDEKit IPC related */
|
|
static void ddekit_usb_task(void *);
|
|
|
|
/* 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(urb_ep_config *, urb_ep_config *);
|
|
static int mass_storage_parse_endpoint(usb_descriptor_t *, urb_ep_config *,
|
|
urb_ep_config *);
|
|
static int mass_storage_parse_descriptors(char *, unsigned int, urb_ep_config *,
|
|
urb_ep_config *);
|
|
|
|
|
|
/*---------------------------*
|
|
* 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 = NULL;
|
|
|
|
/* DDEKit USB message handling thread */
|
|
ddekit_thread_t * ddekit_usb_thread = NULL;
|
|
|
|
/* 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("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);
|
|
if (NULL == mass_storage_sem)
|
|
panic("Initializing mass_storage_sem failed!");
|
|
|
|
/* Starting mass storage thread */
|
|
mass_storage_thread = ddekit_thread_create(mass_storage_task, NULL,
|
|
"mass_storage_task");
|
|
if (NULL == mass_storage_thread)
|
|
panic("Initializing mass_storage_thread failed!");
|
|
|
|
MASS_DEBUG_MSG("Storage task (libblockdriver) ready...");
|
|
|
|
/* Run USB IPC task to collect messages */
|
|
ddekit_usb_thread = ddekit_thread_create(ddekit_usb_task, NULL,
|
|
"ddekit_task" );
|
|
if (NULL == ddekit_usb_thread)
|
|
panic("Initializing ddekit_usb_thread failed!");
|
|
|
|
MASS_DEBUG_MSG("USB IPC task ready...");
|
|
|
|
/* Block and wait until exit signal is received */
|
|
ddekit_minix_wait_exit();
|
|
MASS_DEBUG_MSG("Exiting...");
|
|
|
|
/* Release objects that were explicitly allocated above */
|
|
ddekit_thread_terminate(ddekit_usb_thread);
|
|
ddekit_thread_terminate(mass_storage_thread);
|
|
ddekit_sem_deinit(mass_storage_sem);
|
|
|
|
/* TODO: no ddekit_deinit for proper cleanup? */
|
|
|
|
MASS_DEBUG_MSG("Cleanup completed...");
|
|
|
|
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,
|
|
&(driver_state.cur_periph->ep_out));
|
|
|
|
/* 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,
|
|
&(driver_state.cur_periph->ep_in));
|
|
|
|
/* 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,
|
|
&(driver_state.cur_periph->ep_out));
|
|
|
|
/* 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,
|
|
&(driver_state.cur_periph->ep_in));
|
|
|
|
/* 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;
|
|
|
|
/* Control EP configuration */
|
|
urb_ep_config ep_conf;
|
|
|
|
MASS_DEBUG_DUMP;
|
|
|
|
/* Initialize EP configuration */
|
|
ep_conf.ep_num = 0;
|
|
ep_conf.direction = DDEKIT_USB_OUT;
|
|
ep_conf.type = DDEKIT_USB_TRANSFER_CTL;
|
|
|
|
/* Reset URB and assign given values */
|
|
init_urb(&urb, driver_state.cur_periph->dev, &ep_conf);
|
|
|
|
/* 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_DEBUG_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
|
|
|
|
/* Try graceful DDEKit exit */
|
|
ddekit_shutdown();
|
|
|
|
/* Unreachable, when ddekit_shutdown works correctly */
|
|
panic("Calling ddekit_shutdown failed!");
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* ddekit_usb_task *
|
|
*===========================================================================*/
|
|
static void
|
|
ddekit_usb_task(void * UNUSED(arg))
|
|
{
|
|
MASS_DEBUG_DUMP;
|
|
|
|
/* TODO: This call was meant to return 'int' but loops forever instead,
|
|
* so no return value is checked */
|
|
ddekit_usb_init(&mass_storage_driver, NULL, NULL);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* 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 ((URB_INVALID_EP == driver_state.cur_periph->ep_in.ep_num) ||
|
|
(URB_INVALID_EP == driver_state.cur_periph->ep_out.ep_num)) {
|
|
|
|
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.ep_num = URB_INVALID_EP;
|
|
driver_state.cur_periph->ep_out.ep_num = URB_INVALID_EP;
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* 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.ep_num = URB_INVALID_EP;
|
|
driver_state.cur_periph->ep_out.ep_num = URB_INVALID_EP;
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* mass_storage_get_endpoints *
|
|
*===========================================================================*/
|
|
static int
|
|
mass_storage_get_endpoints(urb_ep_config * ep_in, urb_ep_config * ep_out)
|
|
{
|
|
/* URB to be send */
|
|
struct ddekit_usb_urb urb;
|
|
|
|
/* Setup buffer to be attached */
|
|
struct usb_ctrlrequest setup_buf;
|
|
|
|
/* Control EP configuration */
|
|
urb_ep_config ep_conf;
|
|
|
|
/* Descriptors' buffer */
|
|
unsigned char descriptors[MAX_DESCRIPTORS_LEN];
|
|
|
|
MASS_DEBUG_DUMP;
|
|
|
|
/* Initialize EP configuration */
|
|
ep_conf.ep_num = 0;
|
|
ep_conf.direction = DDEKIT_USB_IN;
|
|
ep_conf.type = DDEKIT_USB_TRANSFER_CTL;
|
|
|
|
/* Reset URB and assign given values */
|
|
init_urb(&urb, driver_state.cur_periph->dev, &ep_conf);
|
|
|
|
/* 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,
|
|
urb_ep_config * ep_in, urb_ep_config * ep_out)
|
|
{
|
|
usb_endpoint_descriptor_t * ep_desc;
|
|
|
|
urb_ep_config * this_ep;
|
|
|
|
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)) {
|
|
|
|
this_ep = ep_in;
|
|
this_ep->direction = DDEKIT_USB_IN;
|
|
|
|
} else {
|
|
|
|
this_ep = ep_out;
|
|
this_ep->direction = DDEKIT_USB_OUT;
|
|
}
|
|
|
|
/* Check if it was set before */
|
|
if (URB_INVALID_EP != this_ep->ep_num) {
|
|
MASS_MSG("BULK EP already set");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
/* Assign rest */
|
|
this_ep->ep_num = UE_GET_ADDR(ep_desc->bEndpointAddress);
|
|
this_ep->type = DDEKIT_USB_TRANSFER_BLK;
|
|
this_ep->max_packet_size = UGETW(ep_desc->wMaxPacketSize);
|
|
this_ep->interval = ep_desc->bInterval;
|
|
}
|
|
|
|
/* EP type other than bulk, is also correct,
|
|
* just no parsing is performed */
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* mass_storage_parse_descriptors *
|
|
*===========================================================================*/
|
|
static int
|
|
mass_storage_parse_descriptors(char * desc_buf, unsigned int buf_len,
|
|
urb_ep_config * ep_in, urb_ep_config * 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->ep_num = URB_INVALID_EP;
|
|
ep_out->ep_num = URB_INVALID_EP;
|
|
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 ((URB_INVALID_EP == ep_in->ep_num) ||
|
|
(URB_INVALID_EP == ep_out->ep_num)) {
|
|
MASS_MSG("Valid bulk endpoints not found");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|