/* * Minix3 USB mass storage driver implementation * using DDEkit, and libblockdriver */ #include /* cases for mass_storage_ioctl */ #ifdef USB_STORAGE_SIGNAL #include /* signal handling */ #endif #include #include #include #include #include /* for msg_queue ranges */ #include /* DEV_PER_DRIVE, partition */ #include /* message */ #include /* GRANT_VALID */ #include #include /* panic */ #include /* structures like usb_ctrlrequest */ #include /* descriptor structures */ #include #include /* ULONG_MAX */ #include "common.h" #include "bulk.h" #include "usb_storage.h" #include "urb_helper.h" #include "scsi.h" /*---------------------------* * declared functions * *---------------------------*/ /* TODO: these are missing from DDE header files */ extern void ddekit_minix_wait_exit(void); extern void ddekit_shutdown(void); /* SCSI URB related prototypes */ static int mass_storage_send_scsi_cbw_out(int, scsi_transfer *); static int mass_storage_send_scsi_data_in(void *, unsigned int); static int mass_storage_send_scsi_data_out(void *, unsigned int); static int mass_storage_send_scsi_csw_in(void); /* Bulk only URB related prototypes */ static int mass_storage_send_bulk_reset(void); /* SEF related functions */ static int mass_storage_sef_hdlr(int, sef_init_info_t *); static void mass_storage_signal_handler(int); /* Mass storage related prototypes */ static void mass_storage_task(void *); static int mass_storage_try_first_open(void); static int mass_storage_transfer_restrictions(u64_t, unsigned long); static ssize_t mass_storage_write(unsigned long, endpoint_t, iovec_t *, unsigned int, unsigned long); static ssize_t mass_storage_read(unsigned long, endpoint_t, iovec_t *, unsigned int, unsigned long); /* Minix's libblockdriver callbacks */ static int mass_storage_open(devminor_t, int); static int mass_storage_close(devminor_t); static ssize_t mass_storage_transfer(devminor_t, int, u64_t, endpoint_t, iovec_t *, unsigned int, int); static int mass_storage_ioctl(devminor_t, unsigned long, endpoint_t, cp_grant_id_t, endpoint_t); static void mass_storage_cleanup(void); static struct device * mass_storage_part(devminor_t); static void mass_storage_geometry(devminor_t, struct part_geom *); /* DDEKit's USB driver callbacks */ static void usb_driver_completion(void *); static void usb_driver_connect(struct ddekit_usb_dev *, unsigned int); static void usb_driver_disconnect(struct ddekit_usb_dev *); /* Simplified enumeration method for endpoint resolution */ static int mass_storage_get_endpoints(int *, int *); static int mass_storage_parse_endpoint(usb_descriptor_t *, int *, int *); static int mass_storage_parse_descriptors(char *, unsigned int, int *, int *); /*---------------------------* * defined variables * *---------------------------*/ /* Mass Storage callback structure */ static struct blockdriver mass_storage = { .bdr_type = BLOCKDRIVER_TYPE_DISK, .bdr_open = mass_storage_open, .bdr_close = mass_storage_close, .bdr_transfer = mass_storage_transfer, .bdr_ioctl = mass_storage_ioctl, .bdr_cleanup = mass_storage_cleanup, .bdr_part = mass_storage_part, .bdr_geometry = mass_storage_geometry, .bdr_intr = NULL, .bdr_alarm = NULL, .bdr_other = NULL, .bdr_device = NULL }; /* USB callback structure */ static struct ddekit_usb_driver mass_storage_driver = { .completion = usb_driver_completion, .connect = usb_driver_connect, .disconnect = usb_driver_disconnect }; /* Instance of global driver information */ mass_storage_state driver_state; /* Tags used to pair CBW and CSW for bulk communication * With this we can check if SCSI reply was meant for SCSI request */ static unsigned int current_cbw_tag = 0; /* What shall be send next */ static unsigned int last_cbw_tag = 0; /* What was sent recently */ /* Semaphore used to block mass storage thread to * allow DDE dispatcher operation */ static ddekit_sem_t * mass_storage_sem = NULL; /* Mass storage (using libblockdriver) thread */ ddekit_thread_t * mass_storage_thread; /* Static URB buffer size (must be multiple of SECTOR_SIZE) */ #define BUFFER_SIZE (64*SECTOR_SIZE) /* Large buffer for URB read/write operations */ static unsigned char buffer[BUFFER_SIZE]; /* Length of local buffer where descriptors are temporarily stored */ #define MAX_DESCRIPTORS_LEN 128 /*---------------------------* * defined functions * *---------------------------*/ /*===========================================================================* * main * *===========================================================================*/ int main(int argc, char * argv[]) { MASS_DEBUG_MSG(THIS_EXEC_NAME" starting..."); /* Store arguments for future parsing */ env_setargs(argc, argv); /* Clear current state */ memset(&driver_state, 0, sizeof(driver_state)); /* Initialize SEF related callbacks */ sef_setcb_init_fresh(mass_storage_sef_hdlr); sef_setcb_init_lu(mass_storage_sef_hdlr); sef_setcb_init_restart(mass_storage_sef_hdlr); sef_setcb_signal_handler(mass_storage_signal_handler); /* Initialize DDEkit (involves sef_startup()) */ ddekit_init(); MASS_DEBUG_MSG("DDEkit ready..."); /* Semaphore initialization */ mass_storage_sem = ddekit_sem_init(0); assert(NULL != mass_storage_sem); /* Starting mass storage thread */ mass_storage_thread = ddekit_thread_create(mass_storage_task, NULL, "mass_storage_task"); MASS_DEBUG_MSG("libblockdriver ready..."); /* Run USB client */ ddekit_usb_init(&mass_storage_driver, NULL, NULL); /* TODO: never reached */ /* Block and wait */ ddekit_minix_wait_exit(); MASS_DEBUG_MSG(THIS_EXEC_NAME" exiting..."); /* Semaphore release */ ddekit_sem_deinit(mass_storage_sem); /* TODO: no ddekit_deinit for proper cleanup? */ return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_send_scsi_cbw_out * *===========================================================================*/ static int mass_storage_send_scsi_cbw_out(int scsi_cmd, scsi_transfer * info) { /* URB to be send */ struct ddekit_usb_urb urb; /* CBW data buffer */ mass_storage_cbw cbw; MASS_DEBUG_DUMP; /* Reset URB and assign given values */ init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_BLK, driver_state.cur_periph->ep_out, DDEKIT_USB_OUT, 0); /* Reset CBW and assign default values */ init_cbw(&cbw, last_cbw_tag = current_cbw_tag++); /* Fill CBW with SCSI command */ if (create_scsi_cmd(&cbw, scsi_cmd, info)) return EXIT_FAILURE; /* Attach CBW to URB */ attach_urb_data(&urb, URB_BUF_TYPE_DATA, &cbw, sizeof(cbw)); /* Send and wait for response */ if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN)) return EXIT_FAILURE; return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_send_scsi_data_in * *===========================================================================*/ static int mass_storage_send_scsi_data_in(void * buf, unsigned int in_len) { /* URB to be send */ struct ddekit_usb_urb urb; MASS_DEBUG_DUMP; /* Reset URB and assign given values */ init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_BLK, driver_state.cur_periph->ep_in, DDEKIT_USB_IN, 0); /* Attach buffer to URB */ attach_urb_data(&urb, URB_BUF_TYPE_DATA, buf, in_len); /* Send and wait for response */ if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN)) return EXIT_FAILURE; return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_send_scsi_data_out * *===========================================================================*/ static int mass_storage_send_scsi_data_out(void * buf, unsigned int out_len) { /* URB to be send */ struct ddekit_usb_urb urb; MASS_DEBUG_DUMP; /* Reset URB and assign given values */ init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_BLK, driver_state.cur_periph->ep_out, DDEKIT_USB_OUT, 0); /* Attach buffer to URB */ attach_urb_data(&urb, URB_BUF_TYPE_DATA, buf, out_len); /* Send and wait for response */ if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN)) return EXIT_FAILURE; return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_send_scsi_csw_in * *===========================================================================*/ static int mass_storage_send_scsi_csw_in(void) { /* URB to be send */ struct ddekit_usb_urb urb; /* CBW data buffer */ mass_storage_csw csw; MASS_DEBUG_DUMP; /* Reset URB and assign given values */ init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_BLK, driver_state.cur_periph->ep_in, DDEKIT_USB_IN, 0); /* Clear CSW for receiving */ init_csw(&csw); /* Attach CSW to URB */ attach_urb_data(&urb, URB_BUF_TYPE_DATA, &csw, sizeof(csw)); /* Send and wait for response */ if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN)) return EXIT_FAILURE; /* Check for proper reply */ if (check_csw(&csw, last_cbw_tag)) return EXIT_FAILURE; return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_send_bulk_reset * *===========================================================================*/ static int mass_storage_send_bulk_reset(void) { /* URB to be send */ struct ddekit_usb_urb urb; /* Setup buffer to be send */ struct usb_ctrlrequest bulk_setup; MASS_DEBUG_DUMP; /* Reset URB and assign given values */ init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_CTL, 0, DDEKIT_USB_OUT, 0); /* Clear setup data */ memset(&bulk_setup, 0, sizeof(bulk_setup)); /* For explanation of these values see usbmassbulk_10.pdf */ /* 3.1 Bulk-Only Mass Storage Reset */ bulk_setup.bRequestType = 0x21; /* Class, Interface, host to device */ bulk_setup.bRequest = 0xff; bulk_setup.wValue = 0x00; bulk_setup.wIndex = 0x00; /* TODO: hard-coded interface 0 */ bulk_setup.wLength = 0x00; /* Attach request to URB */ attach_urb_data(&urb, URB_BUF_TYPE_SETUP, &bulk_setup, sizeof(bulk_setup)); /* Send and wait for response */ if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_CHECK_LEN)) return EXIT_FAILURE; return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_sef_hdlr * *===========================================================================*/ static int mass_storage_sef_hdlr(int type, sef_init_info_t * UNUSED(info)) { int env_res; MASS_DEBUG_DUMP; /* Parse given environment */ env_res = env_parse("instance", "d", 0, &(driver_state.instance),0, 255); /* Get instance number */ if (EP_UNSET == env_res) { MASS_MSG("Instance number was not supplied"); driver_state.instance = 0; } else { /* Only SET and UNSET are allowed */ if (EP_SET != env_res) return EXIT_FAILURE; } switch (type) { case SEF_INIT_FRESH: /* Announce we are up! */ blockdriver_announce(type); return EXIT_SUCCESS; case SEF_INIT_LU: case SEF_INIT_RESTART: MASS_MSG("Only 'fresh' SEF initialization supported\n"); break; default: MASS_MSG("illegal SEF type\n"); break; } return EXIT_FAILURE; } /*===========================================================================* * mass_storage_signal_handler * *===========================================================================*/ static void mass_storage_signal_handler(int this_signal) { MASS_DEBUG_DUMP; #ifdef USB_STORAGE_SIGNAL /* Only check for termination signal, ignore anything else. */ if (this_signal != SIGTERM) return; #else MASS_MSG("Handling signal 0x%X", this_signal); #endif ddekit_shutdown(); /* TODO: broken shutdown */ exit(0); } /*===========================================================================* * mass_storage_task * *===========================================================================*/ static void mass_storage_task(void * UNUSED(unused)) { message m; int ipc_status; struct ddekit_minix_msg_q * mq; MASS_DEBUG_DUMP; mq = ddekit_minix_create_msg_q(BDEV_RQ_BASE, BDEV_RQ_BASE + 0xff); for (;;) { ddekit_minix_rcv(mq, &m, &ipc_status); blockdriver_process(&mass_storage, &m, ipc_status); } } /*===========================================================================* * mass_storage_try_first_open * *===========================================================================*/ static int mass_storage_try_first_open() { unsigned int llba; unsigned int blen; unsigned char inquiry[SCSI_INQUIRY_DATA_LEN]; unsigned char capacity[SCSI_READ_CAPACITY_DATA_LEN]; MASS_DEBUG_DUMP; assert(NULL != driver_state.cur_drive); llba = 0; /* Last logical block address */ blen = 0; /* Block length (usually 512B) */ /* SCSI INQUIRY OUT stage */ if (mass_storage_send_scsi_cbw_out(SCSI_INQUIRY, NULL)) return EIO; /* SCSI INQUIRY first IN stage */ if (mass_storage_send_scsi_data_in(inquiry, sizeof(inquiry))) return EIO; /* SCSI INQUIRY second IN stage */ if (mass_storage_send_scsi_csw_in()) return EIO; /* Check for proper reply */ if (check_inquiry_reply(inquiry)) return EIO; /* SCSI READ CAPACITY OUT stage */ if (mass_storage_send_scsi_cbw_out(SCSI_READ_CAPACITY, NULL)) return EIO; /* SCSI READ CAPACITY first IN stage */ if (mass_storage_send_scsi_data_in(capacity, sizeof(capacity))) return EIO; /* SCSI READ CAPACITY second IN stage */ if (mass_storage_send_scsi_csw_in()) return EIO; /* Check for proper reply */ if (check_read_capacity_reply(capacity, &llba, &blen)) return EIO; /* For now only Minix's default SECTOR_SIZE is supported */ if (SECTOR_SIZE != blen) panic("Invalid block size used by USB device!"); /* Get information about capacity from reply */ driver_state.cur_drive->disk.dv_base = 0; driver_state.cur_drive->disk.dv_size = llba * blen; return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_transfer_restrictions * *===========================================================================*/ static int mass_storage_transfer_restrictions(u64_t pos, unsigned long bytes) { MASS_DEBUG_DUMP; assert(NULL != driver_state.cur_device); /* Zero-length request must not be issued */ if (0 == bytes) { MASS_MSG("Transfer request length equals 0"); return EINVAL; } /* Starting position must be aligned to sector */ if (0 != (pos % SECTOR_SIZE)) { MASS_MSG("Transfer position not divisible by %u", SECTOR_SIZE); return EINVAL; } /* Length must be integer multiple of sector sizes */ if (0 != (bytes % SECTOR_SIZE)) { MASS_MSG("Data length not divisible by %u", SECTOR_SIZE); return EINVAL; } /* Guard against ridiculous 64B overflow */ if ((pos + bytes) <= pos) { MASS_MSG("Request outside available address space"); return EINVAL; } return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_write * *===========================================================================*/ static ssize_t mass_storage_write(unsigned long sector_number, endpoint_t endpt, iovec_t * iov, unsigned int iov_count, unsigned long bytes_left) { /* * This function writes whatever was put in 'iov' array * (iov[0] : iov[iov_count]), into continuous region of mass storage, * starting from sector 'sector_number'. Total amount of 'iov' * data should be greater or equal to initial value of 'bytes_left'. * * Endpoint value 'endpt', determines if vectors 'iov' contain memory * addresses for copying or grant IDs. */ iov_state cur_iov; /* Current state of vector copying */ unsigned long bytes_to_write; /* To be written in this iteration */ ssize_t bytes_already_written; /* Total amount written (retval) */ MASS_DEBUG_DUMP; /* Initialize locals */ cur_iov.remaining_bytes = 0; /* No IO vector initially */ cur_iov.iov_idx = 0; /* Starting from first vector */ bytes_already_written = 0; /* Nothing copied yet */ /* Mass storage operations are sector based */ assert(0 == (sizeof(buffer) % SECTOR_SIZE)); assert(0 == (bytes_left % SECTOR_SIZE)); while (bytes_left > 0) { /* Fill write buffer with data from IO Vectors */ { unsigned long buf_offset; unsigned long copy_len; /* Start copying to the beginning of the buffer */ buf_offset = 0; /* Copy as long as not copied vectors exist or * buffer is not fully filled */ for (;;) { /* If entire previous vector * was used get new one */ if (0 == cur_iov.remaining_bytes) { /* Check if there are * vectors to copied */ if (cur_iov.iov_idx < iov_count) { cur_iov.base_addr = iov[cur_iov.iov_idx].iov_addr; cur_iov.remaining_bytes = iov[cur_iov.iov_idx].iov_size; cur_iov.offset = 0; cur_iov.iov_idx++; } else { /* All vectors copied */ break; } } /* Copy as much as it is possible from vector * and at most the amount that can be * put in buffer */ copy_len = MIN(sizeof(buffer) - buf_offset, cur_iov.remaining_bytes); /* This distinction is required as transfer can * be used from within this process and meaning * of IO vector'a address is different than * grant ID */ if (endpt == SELF) { memcpy(&buffer[buf_offset], (void*)(cur_iov.base_addr + cur_iov.offset), copy_len); } else { ssize_t copy_res; if ((copy_res = sys_safecopyfrom(endpt, cur_iov.base_addr, cur_iov.offset, (vir_bytes) (&buffer[buf_offset]), copy_len))) { MASS_MSG("sys_safecopyfrom " "failed"); return copy_res; } } /* Alter current state of copying */ buf_offset += copy_len; cur_iov.offset += copy_len; cur_iov.remaining_bytes -= copy_len; /* Buffer was filled */ if (sizeof(buffer) == buf_offset) break; } /* Determine how many bytes from copied buffer we wish * to write, buf_offset represents total amount of * bytes copied above */ if (bytes_left >= buf_offset) { bytes_to_write = buf_offset; bytes_left -= buf_offset; } else { bytes_to_write = bytes_left; bytes_left = 0; } } /* Send URB and alter sector number */ if (bytes_to_write > 0) { scsi_transfer info; info.length = bytes_to_write; info.lba = sector_number; /* SCSI WRITE first OUT stage */ if (mass_storage_send_scsi_cbw_out(SCSI_WRITE, &info)) return EIO; /* SCSI WRITE second OUT stage */ if (mass_storage_send_scsi_data_out(buffer, bytes_to_write)) return EIO; /* SCSI WRITE IN stage */ if (mass_storage_send_scsi_csw_in()) return EIO; /* Writing completed so shift starting * sector for next iteration */ sector_number += bytes_to_write / SECTOR_SIZE; /* Update amount of data already copied */ bytes_already_written += bytes_to_write; } } return bytes_already_written; } /*===========================================================================* * mass_storage_read * *===========================================================================*/ static ssize_t mass_storage_read(unsigned long sector_number, endpoint_t endpt, iovec_t * iov, unsigned int iov_count, unsigned long bytes_left) { /* * This function reads 'bytes_left' bytes of mass storage data into * 'iov' array (iov[0] : iov[iov_count]) starting from sector * 'sector_number'. Total amount of 'iov' data should be greater or * equal to initial value of 'bytes_left'. * * Endpoint value 'endpt', determines if vectors 'iov' contain memory * addresses for copying or grant IDs. */ iov_state cur_iov; /* Current state of vector copying */ unsigned long bytes_to_read; /* To be read in this iteration */ ssize_t bytes_already_read; /* Total amount read (retval) */ MASS_DEBUG_DUMP; /* Initialize locals */ cur_iov.remaining_bytes = 0; /* No IO vector initially */ cur_iov.iov_idx = 0; /* Starting from first vector */ bytes_already_read = 0; /* Nothing copied yet */ /* Mass storage operations are sector based */ assert(0 == (sizeof(buffer) % SECTOR_SIZE)); assert(0 == (bytes_left % SECTOR_SIZE)); while (bytes_left > 0) { /* Decide read length and alter remaining bytes */ { /* Number of bytes to be read in next URB */ if (bytes_left > sizeof(buffer)) { bytes_to_read = sizeof(buffer); } else { bytes_to_read = bytes_left; } bytes_left -= bytes_to_read; } /* Send URB and alter sector number */ { scsi_transfer info; info.length = bytes_to_read; info.lba = sector_number; /* SCSI READ OUT stage */ if (mass_storage_send_scsi_cbw_out(SCSI_READ, &info)) return EIO; /* SCSI READ first IN stage */ if (mass_storage_send_scsi_data_in(buffer, bytes_to_read)) return EIO; /* SCSI READ second IN stage */ if (mass_storage_send_scsi_csw_in()) return EIO; /* Reading completed so shift starting * sector for next iteration */ sector_number += bytes_to_read / SECTOR_SIZE; } /* Fill IO Vectors with data from buffer */ { unsigned long buf_offset; unsigned long copy_len; /* Start copying from the beginning of the buffer */ buf_offset = 0; /* Copy as long as there are unfilled vectors * or data in buffer remains */ for (;;) { /* If previous vector was filled get new one */ if (0 == cur_iov.remaining_bytes) { /* Check if there are vectors * to be filled */ if (cur_iov.iov_idx < iov_count) { cur_iov.base_addr = iov[cur_iov.iov_idx].iov_addr; cur_iov.remaining_bytes = iov[cur_iov.iov_idx].iov_size; cur_iov.offset = 0; cur_iov.iov_idx++; } else { /* Total length of vectors * should be greater or equal * to initial value of * bytes_left. Being here means * that everything should * have been copied already */ assert(0 == bytes_left); break; } } /* Copy as much as it is possible from buffer * and at most the amount that can be * put in vector */ copy_len = MIN(bytes_to_read - buf_offset, cur_iov.remaining_bytes); /* This distinction is required as transfer can * be used from within this process and meaning * of IO vector'a address is different than * grant ID */ if (endpt == SELF) { memcpy((void*)(cur_iov.base_addr + cur_iov.offset), &buffer[buf_offset], copy_len); } else { ssize_t copy_res; if ((copy_res = sys_safecopyto(endpt, cur_iov.base_addr, cur_iov.offset, (vir_bytes) (&buffer[buf_offset]), copy_len))) { MASS_MSG("sys_safecopyto " "failed"); return copy_res; } } /* Alter current state of copying */ buf_offset += copy_len; cur_iov.offset += copy_len; cur_iov.remaining_bytes -= copy_len; /* Everything was copied */ if (bytes_to_read == buf_offset) break; } /* Update amount of data already copied */ bytes_already_read += buf_offset; } } return bytes_already_read; } /*===========================================================================* * mass_storage_open * *===========================================================================*/ static int mass_storage_open(devminor_t minor, int UNUSED(access)) { mass_storage_drive * d; int r; MASS_DEBUG_DUMP; /* Decode minor into drive device information */ if (NULL == (mass_storage_part(minor))) return ENXIO; /* Copy evaluated current drive for simplified dereference */ d = driver_state.cur_drive; /* In case of previous CBW mismatch */ if (mass_storage_send_bulk_reset()) { MASS_MSG("Resetting mass storage device failed"); return EIO; } /* In case of missing endpoint information, do simple * enumeration and hold it for future use */ if ((-1 == driver_state.cur_periph->ep_in) || (-1 == driver_state.cur_periph->ep_out)) { if (mass_storage_get_endpoints(&driver_state.cur_periph->ep_in, &driver_state.cur_periph->ep_out)) return EIO; } /* If drive hasn't been opened yet, try to open it */ if (d->open_ct == 0) { if ((r = mass_storage_try_first_open())) { MASS_MSG("Opening mass storage device" " for the first time failed"); return r; } /* Clear remembered device state for current * drive before calling partition */ memset(&d->part[0], 0, sizeof(d->part)); memset(&d->subpart[0], 0, sizeof(d->subpart)); /* Try parsing partition table (for entire drive) */ /* Warning!! This call uses mass_storage_part with own minors * and alters global driver_state.cur_device! */ partition(&mass_storage, (d->drive_idx * DEV_PER_DRIVE), P_PRIMARY, 0); /* Decode minor into UPDATED drive device information */ if (NULL == (mass_storage_part(minor))) return ENXIO; /* Decoded size must be positive or else * we assume device (partition) is unavailable */ if (0 == driver_state.cur_device->dv_size) return ENXIO; } /* SCSI TEST UNIT READY OUT stage */ if (mass_storage_send_scsi_cbw_out(SCSI_TEST_UNIT_READY, NULL)) return EIO; /* SCSI TEST UNIT READY IN stage */ if (mass_storage_send_scsi_csw_in()) return EIO; /* Opening completed */ d->open_ct++; return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_close * *===========================================================================*/ static int mass_storage_close(devminor_t minor) { MASS_DEBUG_DUMP; /* Decode minor into drive device information */ if (NULL == (mass_storage_part(minor))) return ENXIO; /* If drive hasn't been opened yet */ if (driver_state.cur_drive->open_ct == 0) { MASS_MSG("Device was not opened yet"); return ERESTART; } /* Act accordingly */ driver_state.cur_drive->open_ct--; return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_transfer * *===========================================================================*/ static ssize_t mass_storage_transfer(devminor_t minor, /* device minor number */ int do_write, /* 1 write, 0 read */ u64_t pos, /* position of starting point */ endpoint_t endpt, /* endpoint */ iovec_t * iov, /* vector */ unsigned int iov_count, /* how many vectors */ int UNUSED(flags)) /* transfer flags */ { u64_t temp_sector_number; unsigned long bytes; unsigned long sector_number; unsigned int cur_iov_idx; int r; MASS_DEBUG_DUMP; /* Decode minor into drive device information */ if (NULL == (mass_storage_part(minor))) return ENXIO; bytes = 0; /* How much data is going to be transferred? */ for (cur_iov_idx = 0; cur_iov_idx < iov_count; ++cur_iov_idx) { /* Check if grant ID was supplied * instead of address and if it is valid */ if (endpt != SELF) if (!GRANT_VALID((cp_grant_id_t) (iov[cur_iov_idx].iov_addr))) return EINVAL; /* All supplied vectors must have positive length */ if ((signed long)(iov[cur_iov_idx].iov_size) <= 0) { MASS_MSG("Transfer request length is not positive"); return EINVAL; } /* Requirements were met, more bytes can be transferred */ bytes += iov[cur_iov_idx].iov_size; /* Request size must never overflow */ if ((signed long)bytes <= 0) { MASS_MSG("Transfer request length overflowed"); return EINVAL; } } /* Check if reading beyond device/partition */ if (pos >= driver_state.cur_device->dv_size) { MASS_MSG("Request out of bounds for given device"); return 0; /* No error and no bytes read */ } /* Check if arguments agree with accepted restriction * for parameters of transfer */ if ((r = mass_storage_transfer_restrictions(pos, bytes))) return r; /* If 'hard' requirements above were met, do additional * limiting to device/partition boundary */ if ((pos + bytes) > driver_state.cur_device->dv_size) bytes = (driver_state.cur_device->dv_size - pos) & ~SECTOR_MASK; /* We have to obtain sector number of given position * and limit it to what URB can handle */ temp_sector_number = (driver_state.cur_device->dv_base + pos) / SECTOR_SIZE; assert(temp_sector_number < ULONG_MAX); /* LBA is limited to 32B */ sector_number = (unsigned long)temp_sector_number; if (do_write) return mass_storage_write(sector_number, endpt, iov, iov_count, bytes); else return mass_storage_read(sector_number, endpt, iov, iov_count, bytes); } /*===========================================================================* * mass_storage_ioctl * *===========================================================================*/ static int mass_storage_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, cp_grant_id_t grant, endpoint_t UNUSED(user_endpt)) { MASS_DEBUG_DUMP; /* Decode minor into drive device information */ if (NULL == (mass_storage_part(minor))) return ENXIO; switch (request) { case DIOCOPENCT: if (sys_safecopyto(endpt, grant, 0, (vir_bytes) &(driver_state.cur_drive->open_ct), sizeof(driver_state.cur_drive->open_ct))) panic("sys_safecopyto failed!"); return EXIT_SUCCESS; default: MASS_MSG("Unimplemented IOCTL request: 0x%X", (int)request); break; } return ENOTTY; } /*===========================================================================* * mass_storage_cleanup * *===========================================================================*/ static void mass_storage_cleanup(void) { MASS_DEBUG_DUMP; return; } /*===========================================================================* * mass_storage_part * *===========================================================================*/ static struct device * mass_storage_part(devminor_t minor) { unsigned long sel_drive; unsigned long sel_device; MASS_DEBUG_DUMP; /* Override every time before further decision */ driver_state.cur_drive = NULL; driver_state.cur_device = NULL; driver_state.cur_periph = NULL; /* Decode 'minor' code to find which device file was used */ if (minor < MINOR_d0p0s0) { /* No sub-partitions used */ sel_drive = minor / DEV_PER_DRIVE; sel_device = minor % DEV_PER_DRIVE; /* Only valid minors */ if (sel_drive < MAX_DRIVES) { /* Associate minor (device/partition) * with peripheral number */ /* TODO:PERIPH * Proper peripheral selection based * on minor should be here: */ driver_state.cur_periph = &driver_state.periph[0]; /* Select drive entry for opening count etc. */ driver_state.cur_drive = &(driver_state.cur_periph->drives[sel_drive]); /* Select device entry for given device file */ /* Device 0 means entire drive. * Devices 1,2,3,4 mean partitions 0,1,2,3 */ if (0 == sel_device) driver_state.cur_device = &(driver_state.cur_drive->disk); else driver_state.cur_device = &(driver_state.cur_drive->part [sel_device-1]); } } else { /* Shift values accordingly */ minor -= MINOR_d0p0s0; /* Sub-partitions used */ sel_drive = minor / SUBPART_PER_DISK; sel_device = minor % SUBPART_PER_DISK; /* Only valid minors */ if (sel_drive < MAX_DRIVES) { /* Leave in case of ridiculously high number */ if (minor < SUBPART_PER_DISK) { /* Associate minor (device/partition) * with peripheral number */ /* TODO:PERIPH * Proper peripheral selection based * on minor should be here: */ driver_state.cur_periph = &driver_state.periph[0]; /* Select drive entry for opening count etc. */ driver_state.cur_drive = &(driver_state.cur_periph->drives [sel_drive]); /* Select device entry for given * sub-partition device file */ driver_state.cur_device = &(driver_state.cur_drive->subpart [sel_device]); } } } /* Check for success */ if (NULL == driver_state.cur_device) { MASS_MSG("Device for minor: %u not found", minor); } else { /* Assign index as well */ driver_state.cur_drive->drive_idx = sel_drive; } return driver_state.cur_device; } /*===========================================================================* * mass_storage_geometry * *===========================================================================*/ #ifdef MASS_USE_GEOMETRY static void mass_storage_geometry(devminor_t minor, struct part_geom * part) { char flexible_disk_page[SCSI_MODE_SENSE_FLEX_DATA_LEN]; MASS_DEBUG_DUMP; /* Decode minor into drive device information */ if (NULL == (mass_storage_part(minor))) return; /* SCSI MODE SENSE OUT stage */ if (mass_storage_send_scsi_cbw_out(SCSI_MODE_SENSE, NULL)) return; /* SCSI MODE SENSE first IN stage */ if (mass_storage_send_scsi_data_in(flexible_disk_page, sizeof(flexible_disk_page))) return; /* SCSI MODE SENSE second IN stage */ if (mass_storage_send_scsi_csw_in()) return; /* Get geometry from reply */ if (check_mode_sense_reply(flexible_disk_page, &(part->cylinders), &(part->heads), &(part->sectors))) return; #else static void mass_storage_geometry(devminor_t UNUSED(minor), struct part_geom * part) { part->cylinders = part->size / SECTOR_SIZE; part->heads = 64; part->sectors = 32; #endif } /*===========================================================================* * usb_driver_completion * *===========================================================================*/ static void usb_driver_completion(void * UNUSED(priv)) { /* Last request was completed so allow continuing * execution from place where semaphore was downed */ ddekit_sem_up(mass_storage_sem); } /*===========================================================================* * usb_driver_connect * *===========================================================================*/ static void usb_driver_connect(struct ddekit_usb_dev * dev, unsigned int interfaces) { MASS_DEBUG_DUMP; /* TODO:PERIPH * Some sort of more complex peripheral assignment should be here */ driver_state.cur_periph = &driver_state.periph[0]; if (NULL != driver_state.cur_periph->dev) panic("Only one peripheral can be connected!"); /* Hold host information for future use */ driver_state.cur_periph->dev = dev; driver_state.cur_periph->interfaces = interfaces; driver_state.cur_periph->ep_in = -1; driver_state.cur_periph->ep_out = -1; } /*===========================================================================* * usb_driver_disconnect * *===========================================================================*/ static void usb_driver_disconnect(struct ddekit_usb_dev * UNUSED(dev)) { MASS_DEBUG_DUMP; /* TODO:PERIPH * Some sort of peripheral discard should be here */ driver_state.cur_periph = &driver_state.periph[0]; assert(NULL != driver_state.cur_periph->dev); /* Clear */ driver_state.cur_periph->dev = NULL; driver_state.cur_periph->interfaces = 0; driver_state.cur_periph->ep_in = -1; driver_state.cur_periph->ep_out = -1; } /*===========================================================================* * mass_storage_get_endpoints * *===========================================================================*/ static int mass_storage_get_endpoints(int * ep_in, int * ep_out) { /* URB to be send */ struct ddekit_usb_urb urb; /* Setup buffer to be attached */ struct usb_ctrlrequest setup_buf; /* Descriptors' buffer */ unsigned char descriptors[MAX_DESCRIPTORS_LEN]; MASS_DEBUG_DUMP; /* Reset URB and assign given values */ init_urb(&urb, driver_state.cur_periph->dev, DDEKIT_USB_TRANSFER_CTL, 0, DDEKIT_USB_IN, 0); /* Clear setup data */ memset(&setup_buf, 0, sizeof(setup_buf)); /* Standard get endpoint request */ setup_buf.bRequestType = 0x80; /* Device to host */ setup_buf.bRequest = UR_GET_DESCRIPTOR; setup_buf.wValue = UDESC_CONFIG << 8; /* TODO: configuration 0 */ setup_buf.wIndex = 0x00; setup_buf.wLength = MAX_DESCRIPTORS_LEN; /* Attach buffers to URB */ attach_urb_data(&urb, URB_BUF_TYPE_SETUP, &setup_buf, sizeof(setup_buf)); attach_urb_data(&urb, URB_BUF_TYPE_DATA, descriptors, sizeof(descriptors)); /* Send and wait for response */ if (blocking_urb_submit(&urb, mass_storage_sem, URB_SUBMIT_ALLOW_MISMATCH)) return EXIT_FAILURE; /* Check if buffer was supposed to hold more data */ if (urb.size < urb.actual_length) { MASS_MSG("Too much descriptor data received"); return EXIT_FAILURE; } return mass_storage_parse_descriptors(urb.data, urb.actual_length, ep_in, ep_out); } /*===========================================================================* * mass_storage_parse_endpoint * *===========================================================================*/ static int mass_storage_parse_endpoint(usb_descriptor_t * cur_desc, int * ep_in, int * ep_out) { usb_endpoint_descriptor_t * ep_desc; MASS_DEBUG_DUMP; ep_desc = (usb_endpoint_descriptor_t *)cur_desc; /* Only bulk with no other attributes are important */ if (UE_BULK == ep_desc->bmAttributes) { /* Check for direction */ if (UE_DIR_IN == UE_GET_DIR(ep_desc->bEndpointAddress)) { if (-1 != *ep_in) { MASS_MSG("BULK IN already set"); return EXIT_FAILURE; } *ep_in = UE_GET_ADDR(ep_desc->bEndpointAddress); } else { if (-1 != *ep_out) { MASS_MSG("BULK OUT already set"); return EXIT_FAILURE; } *ep_out = UE_GET_ADDR(ep_desc->bEndpointAddress); } } return EXIT_SUCCESS; } /*===========================================================================* * mass_storage_parse_descriptors * *===========================================================================*/ static int mass_storage_parse_descriptors(char * desc_buf, unsigned int buf_len, int * ep_in, int * ep_out) { /* Currently parsed, descriptors */ usb_descriptor_t * cur_desc; usb_interface_descriptor_t * ifc_desc; /* Byte counter for descriptor parsing */ unsigned int cur_byte; /* Non zero if recently parsed interface is valid for this device */ int valid_interface; MASS_DEBUG_DUMP; /* Parse descriptors to get endpoints */ *ep_in = -1; *ep_out = -1; valid_interface = 0; cur_byte = 0; while (cur_byte < buf_len) { /* Map descriptor to buffer */ /* Structure is packed so alignment should not matter */ cur_desc = (usb_descriptor_t *)&(desc_buf[cur_byte]); /* Check this so we won't be reading * memory outside the buffer */ if ((cur_desc->bLength > 3) && (cur_byte + cur_desc->bLength <= buf_len)) { /* Parse based on descriptor type */ switch (cur_desc->bDescriptorType) { case UDESC_CONFIG: { if (USB_CONFIG_DESCRIPTOR_SIZE != cur_desc->bLength) { MASS_MSG("Wrong configuration" " descriptor length"); return EXIT_FAILURE; } break; } case UDESC_STRING: break; case UDESC_INTERFACE: { ifc_desc = (usb_interface_descriptor_t *)cur_desc; if (USB_INTERFACE_DESCRIPTOR_SIZE != cur_desc->bLength) { MASS_MSG("Wrong interface" " descriptor length"); return EXIT_FAILURE; } /* Check if following data is meant * for our interfaces */ if ((1 << ifc_desc->bInterfaceNumber) & driver_state.cur_periph->interfaces) valid_interface = 1; else valid_interface = 0; break; } case UDESC_ENDPOINT: { if (USB_ENDPOINT_DESCRIPTOR_SIZE != cur_desc->bLength) { MASS_MSG("Wrong endpoint" " descriptor length"); return EXIT_FAILURE; } /* Previous interface was, * what we were looking for */ if (valid_interface) { if (EXIT_SUCCESS != mass_storage_parse_endpoint( cur_desc, ep_in, ep_out)) return EXIT_FAILURE; } break; } default: { MASS_MSG("Wrong descriptor type"); return EXIT_FAILURE; } } } else { MASS_MSG("Invalid descriptor length"); return EXIT_FAILURE; } /* Get next descriptor */ cur_byte += cur_desc->bLength; } /* Total length should match sum of all descriptors' lengths... */ if (cur_byte > buf_len) return EXIT_FAILURE; /* ...and descriptors should be valid */ if ((-1 == *ep_in) || (-1 == *ep_out)) { MASS_MSG("Valid bulk endpoints not found"); return EXIT_FAILURE; } return EXIT_SUCCESS; }