void msc_notify_read_operation_complete( struct msc_application_data *app_data, bool passed) { usb_disable_transaction_interrupt(); if (app_data->state != MSC_DATA_TRANSPORT_IN) { goto out; } uint32_t residue = app_data->requested_bytes_cbw - app_data->transferred_bytes; if (!passed) { /* Save off the error codes. These will be read by the host * with a REQUEST_SENSE SCSI command. */ set_scsi_sense(app_data, MSC_ERROR_READ); } uint8_t status = passed? MSC_STATUS_PASSED: MSC_STATUS_FAILED; if (residue > 0) { stall_in_and_set_status(app_data, residue, status); } else { send_csw(app_data, residue, status); app_data->state = MSC_IDLE; } out: usb_enable_transaction_interrupt(); }
void msc_notify_write_operation_complete(struct msc_application_data *msc, bool passed, uint32_t bytes_processed) { uint32_t residue; usb_disable_transaction_interrupt(); if (msc->state != MSC_DATA_TRANSPORT_OUT) { goto out; } residue = msc->requested_bytes_cbw - bytes_processed; if (!passed) { /* Save off the error codes. These will be read by the host * with a REQUEST_SENSE SCSI command. */ set_scsi_sense(msc, MSC_ERROR_WRITE); stall_out_and_set_status(msc, residue, MSC_STATUS_FAILED); msc->out_ep_missed_transactions = 0; goto fail; } if (msc->transferred_bytes < msc->requested_bytes) { /* The application is ending the Data Transport before the * host has sent all the data it expects to send. This * becomes case 11 (Ho > Do). Stall the OUT endpoint, but set * the status to PASSED, because the device processed all the * data it intended to process. */ stall_out_and_set_status(msc, residue, MSC_STATUS_PASSED); msc->out_ep_missed_transactions = 0; goto fail; } else { /* No more data left to transfer */ send_csw(msc, residue, MSC_STATUS_PASSED); msc->state = MSC_IDLE; } out: handle_missed_out_transactions(msc); fail: usb_enable_transaction_interrupt(); }
void msc_in_transaction_complete(uint8_t endpoint) { struct msc_application_data *msc; msc = get_app_data_by_endpoint(endpoint, 1/*IN*/); if (!msc) return; if (msc->state == MSC_DATA_TRANSPORT_IN) { send_next_data_transaction(msc); } else if (msc->state == MSC_STALL) { usb_halt_ep_in(msc->in_endpoint); msc->state = MSC_CSW; } else if (msc->state == MSC_CSW) { send_csw(msc, msc->residue, msc->status); msc->state = MSC_IDLE; } }
void msc_clear_halt(uint8_t endpoint, uint8_t direction) { struct msc_application_data *msc; msc = get_app_data_by_endpoint(endpoint, direction); if (!msc) return; if (msc->state == MSC_CSW) { send_csw(msc, msc->residue, msc->status); msc->state = MSC_IDLE; } else if (msc->state == MSC_NEEDS_RESET_RECOVERY) { /* The device needs a Reset Recovery (BOT 5.3.4) but the * host has not performed a Bulk-Only Mass Storage Reset * (BOT 3.1) control transfer yet, so the endpoint must * remain stalled. (BOT 5.3, Figure 2) */ if (direction) usb_halt_ep_in(endpoint); else usb_halt_ep_out(endpoint); } }
/* called by usb_core_transfer_complete() */ void usb_storage_transfer_complete(int ep,int dir,int status,int length) { (void)ep; struct command_block_wrapper* cbw = (void*)cbw_buffer; #if CONFIG_RTC struct tm tm; #endif logf("transfer result for ep %d/%d %X %d", ep,dir,status, length); switch(state) { case RECEIVING_BLOCKS: if(dir==USB_DIR_IN) { logf("IN received in RECEIVING"); } logf("scsi write %d %d", cur_cmd.sector, cur_cmd.count); if(status==0) { if((unsigned int)length!=(SECTOR_SIZE* cur_cmd.count) && (unsigned int)length!=WRITE_BUFFER_SIZE) { logf("unexpected length :%d",length); break; } unsigned int next_sector = cur_cmd.sector + (WRITE_BUFFER_SIZE/SECTOR_SIZE); unsigned int next_count = cur_cmd.count - MIN(cur_cmd.count,WRITE_BUFFER_SIZE/SECTOR_SIZE); int next_select = !cur_cmd.data_select; if(next_count!=0) { /* Ask the host to send more, to the other buffer */ receive_block_data(cur_cmd.data[next_select], MIN(WRITE_BUFFER_SIZE,next_count*SECTOR_SIZE)); } /* Now write the data that just came in, while the host is sending the next bit */ #ifdef USB_USE_RAMDISK memcpy(ramdisk_buffer + cur_cmd.sector*SECTOR_SIZE, cur_cmd.data[cur_cmd.data_select], MIN(WRITE_BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count)*SECTOR_SIZE); #else int result = USBSTOR_WRITE_SECTORS_FILTER(); if (result == 0) { result = storage_write_sectors(IF_MD(cur_cmd.lun,) cur_cmd.sector, MIN(WRITE_BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count), cur_cmd.data[cur_cmd.data_select]); } if(result != 0) { send_csw(UMS_STATUS_FAIL); cur_sense_data.sense_key=SENSE_MEDIUM_ERROR; cur_sense_data.asc=ASC_WRITE_ERROR; cur_sense_data.ascq=0; break; } #endif if(next_count==0) { send_csw(UMS_STATUS_GOOD); } /* Switch buffers for the next one */ cur_cmd.data_select=!cur_cmd.data_select; cur_cmd.sector = next_sector; cur_cmd.count = next_count; } else {
static void process_msc_command(struct msc_application_data *msc, const uint8_t *data, uint16_t len) { const struct msc_command_block_wrapper *cbw = (const void *) data; const uint8_t command = cbw->CBWCB[0]; const uint8_t lun = cbw->bCBWLUN; const uint32_t cbw_length = cbw->dCBWDataTransferLength; int8_t res; /* Check the Command Block Wrapper (CBW) */ if (!msc_cbw_valid_and_meaningful(msc, data,len)) goto bad_cbw; msc->current_tag = cbw->dCBWTag; if (command == MSC_SCSI_INQUIRY) { uint32_t scsi_request_len; struct msc_scsi_inquiry_command *cmd = (struct msc_scsi_inquiry_command *) cbw->CBWCB; struct scsi_inquiry_response *resp = (struct scsi_inquiry_response *) usb_get_in_buffer(msc->in_endpoint); swap2(&cmd->allocation_length); /* The host may request just the first part of the inquiry * response structure. */ scsi_request_len = MIN(cmd->allocation_length, sizeof(*resp)); /* INQUIRY: Device indends to send data to the host (Di). */ res = check_di_cases(msc, cbw, scsi_request_len); if (res < 0) goto fail; if (usb_in_endpoint_busy(msc->in_endpoint)) goto fail; /* Send INQUIRY response */ memset(resp, 0, sizeof(*resp)); resp->peripheral = 0x0; resp->rmb = (msc->media_is_removable_mask & (1<<lun))? 0x80: 0; resp->version = MSC_SCSI_SPC_VERSION_2; resp->response_data_format = 0x2; resp->additional_length = sizeof(*resp) - 4; strncpy(resp->vendor, msc->vendor, sizeof(resp->vendor)); strncpy(resp->product, msc->product, sizeof(resp->product)); strncpy(resp->revision, msc->revision, sizeof(resp->revision)); usb_send_in_buffer(msc->in_endpoint, scsi_request_len); set_data_in_endpoint_state(msc, cbw_length, scsi_request_len); } else if (command == MSC_SCSI_TEST_UNIT_READY) { /* TEST_UNIT_READY: Device intends to transfer no data (Dn). */ res = check_dn_cases(msc, cbw); if (res < 0) goto fail; if (usb_in_endpoint_busy(msc->in_endpoint)) goto fail; res = MSC_UNIT_READY(msc, lun); if (res < 0) { /* Set error */ set_scsi_sense(msc, res); send_csw(msc, cbw_length, MSC_STATUS_FAILED); goto fail; } send_csw(msc, cbw_length, MSC_STATUS_PASSED); } else if (command == MSC_SCSI_READ_CAPACITY_10) { struct scsi_capacity_response *resp = (struct scsi_capacity_response *) usb_get_in_buffer(msc->in_endpoint); uint32_t block_size, num_blocks; bool write_protect; /* Read Capacity 10: Device intends to send data * to the host (Di) */ res = check_di_cases(msc, cbw, sizeof(*resp)); if (res < 0) goto fail; if (usb_in_endpoint_busy(msc->in_endpoint)) goto fail; res = MSC_GET_STORAGE_INFORMATION( msc, lun, &block_size, &num_blocks, &write_protect); if (res < 0) { /* Stall and set error */ set_scsi_sense(msc, res); stall_in_and_set_status( msc, cbw_length, MSC_STATUS_FAILED); goto fail; } /* Pack and send the response buffer */ resp->last_block = num_blocks - 1; resp->block_length = block_size; swap4(&resp->last_block); swap4(&resp->block_length); usb_send_in_buffer(msc->in_endpoint, sizeof(*resp)); /* Save off block_size */ msc->block_size[lun] = block_size; set_data_in_endpoint_state(msc, cbw_length, sizeof(*resp)); } else if (command == MSC_SCSI_REQUEST_SENSE) { uint32_t scsi_request_len; struct msc_scsi_request_sense_command *cmd = (struct msc_scsi_request_sense_command *) cbw->CBWCB; struct scsi_sense_response *resp = (struct scsi_sense_response *) usb_get_in_buffer(msc->in_endpoint); scsi_request_len = MIN(cmd->allocation_length, sizeof(*resp)); /* REQUEST_SENSE: Device intends to send data * to the host (Di) */ res = check_di_cases(msc, cbw, scsi_request_len); if (res < 0) goto fail; if (usb_in_endpoint_busy(msc->in_endpoint)) goto fail; memset(resp, 0, sizeof(*resp)); resp->response_code = SCSI_SENSE_CURRENT_ERRORS; resp->flags = msc->sense_key; resp->additional_sense_length = 0xa; resp->additional_sense_code = msc->additional_sense_code; usb_send_in_buffer(msc->in_endpoint, scsi_request_len); set_data_in_endpoint_state(msc, cbw_length, scsi_request_len); } else if (command == MSC_SCSI_MODE_SENSE_6) { uint32_t block_size, num_blocks; int8_t res; bool write_protect; struct msc_scsi_mode_sense_6_command *cmd = (struct msc_scsi_mode_sense_6_command *) cbw->CBWCB; struct scsi_mode_sense_response *resp = (struct scsi_mode_sense_response *) usb_get_in_buffer(msc->in_endpoint); /* MODE_SENSE(6): Device intends to send data * to the host (Di) */ res = check_di_cases(msc, cbw, sizeof(*resp)); if (res < 0) goto fail; if (usb_in_endpoint_busy(msc->in_endpoint)) goto fail; /* Look for page code 0x3f, subpage code 0x0. */ if (cmd->pc_page_code != 0x3f || cmd->subpage_code != 0) { msc->sense_key = SCSI_SENSE_KEY_ILLEGAL_REQUEST; msc->additional_sense_code = SCSI_ASC_INVALID_FIELD_IN_COMMAND_PACKET; /* Stall and send the status after the stall. */ stall_in_and_set_status(msc, cbw_length, MSC_STATUS_FAILED); goto fail; } res = MSC_GET_STORAGE_INFORMATION(msc, lun, &block_size, &num_blocks, &write_protect); if (res < 0) { /* Stall and set error */ set_scsi_sense(msc, res); stall_in_and_set_status( msc, cbw_length, MSC_STATUS_FAILED); goto fail; } #ifndef MSC_WRITE_SUPPORT /* Force write-protect on if write is not supported */ write_protect = true; #endif resp->mode_data_length = sizeof(struct scsi_mode_sense_response) - 1; resp->medium_type = 0x0; /* 0 = SBC */ resp->device_specific_parameter = (write_protect)? 0x80: 0; resp->block_descriptor_length = 0; usb_send_in_buffer(msc->in_endpoint, sizeof(*resp)); set_data_in_endpoint_state(msc, cbw_length, sizeof(*resp)); } else if (command == MSC_SCSI_START_STOP_UNIT) { int8_t res; bool start, load_eject; struct msc_scsi_start_stop_unit *cmd = (struct msc_scsi_start_stop_unit *) cbw->CBWCB; /* START STOP UNIT: Device intends to not send or receive * any data (Dn). */ res = check_dn_cases(msc, cbw); if (res < 0) goto fail; if (usb_in_endpoint_busy(msc->in_endpoint)) goto fail; /* Only accept power condition 0x0, START_VALID */ if ((cmd->command & 0xf0) != 0) goto fail; start = ((cmd->command & 0x1) != 0); load_eject = ((cmd->command & 0x2) != 0); res = MSC_START_STOP_UNIT(msc, lun, start, load_eject); if (res < 0) { set_scsi_sense(msc, res); send_csw(msc, cbw_length, MSC_STATUS_FAILED); goto fail; } send_csw(msc, 0, MSC_STATUS_PASSED); } else if (command == MSC_SCSI_READ_10) { uint32_t scsi_request_len; struct msc_scsi_read_10_command *cmd = (struct msc_scsi_read_10_command *) cbw->CBWCB; swap4(&cmd->logical_block_address); swap2(&cmd->transfer_length); /* length in blocks */ if (usb_in_endpoint_busy(msc->in_endpoint)) goto fail; scsi_request_len = cmd->transfer_length * msc->block_size[lun]; /* Handle the nonsensical, but possible case of the host * asking to read 0 bytes in the SCSI. That actually makes * this a Dn case rather than a Di case. */ if (scsi_request_len == 0) { res = check_dn_cases(msc, cbw); if (res < 0) goto fail; /* If check_dn_cases() succeeded, then the host is * not expecting any data, so send the CSW*/ send_csw(msc, 0, MSC_STATUS_PASSED); goto fail; /* Not a failure, but handled the same */ } /* READ(10): Device intends to send data to the host (Di) */ res = check_di_cases(msc, cbw, scsi_request_len); if (res < 0) goto fail; /* Set up the transport state. It's important that this is * done before the call to the MSC_START_READ() callback * below, because the application could concievably start * calling msc_send_to_host() from the callback. */ msc->requested_bytes = MIN(cbw_length, scsi_request_len); msc->requested_bytes_cbw = cbw_length; msc->transferred_bytes = 0; msc->state = MSC_DATA_TRANSPORT_IN; /* Start the Data-Transport. After receiving the call to * MSC_START_READ() the application will repeatedly call * msc_send_to_host() with data read from the medium * and then call msc_data_complete() when finished. */ res = MSC_START_READ(msc, lun, cmd->logical_block_address, cmd->transfer_length); if (res < 0) { set_scsi_sense(msc, res); stall_in_and_set_status(msc, cbw_length, MSC_STATUS_FAILED); /* Reset the state */ msc->requested_bytes = 0; msc->requested_bytes_cbw = 0; msc->state = MSC_IDLE; goto fail; } } #ifdef MSC_WRITE_SUPPORT else if (command == MSC_SCSI_WRITE_10) { uint32_t scsi_request_len; int8_t res; struct msc_scsi_write_10_command *cmd = (struct msc_scsi_write_10_command *) cbw->CBWCB; swap4(&cmd->logical_block_address); swap2(&cmd->transfer_length); /* length in blocks */ scsi_request_len = cmd->transfer_length * msc->block_size[lun]; /* Handle the nonsensical, but possible case of the host * asking to write 0 bytes in the SCSI command. That actually * makes this a Dn case rather than a Do case, and is * required by the USBCV test. */ if (scsi_request_len == 0) { res = check_dn_cases(msc, cbw); if (res < 0) goto fail; /* If check_dn_cases() succeeded, then the host is * not expecting to write any data, so send the CSW */ send_csw(msc, 0, MSC_STATUS_PASSED); goto fail; /* Not a failure, but handled the same */ } /* Write(10): Device intends to receive data * from the host (Do) */ res = check_do_cases(msc, cbw, scsi_request_len); if (res < 0) goto fail; /* Start the Data-Transport. The application will give * a buffer to put the data into. */ res = MSC_START_WRITE(msc, lun, cmd->logical_block_address, cmd->transfer_length, &msc->rx_buf, &msc->rx_buf_len, &msc->operation_complete_callback); if (res < 0) { set_scsi_sense(msc, res); stall_out_and_set_status(msc, cbw_length, MSC_STATUS_FAILED); goto fail; } /* Initialize the data transport */ msc->requested_bytes = scsi_request_len; msc->requested_bytes_cbw = cbw_length; msc->transferred_bytes = 0; msc->rx_buf_cur = msc->rx_buf; msc->state = MSC_DATA_TRANSPORT_OUT; } #endif /* MSC_WRITE_SUPPORT */ else { /* Unsupported command. See Axelson, page 69. */ const bool direc_is_in = direction_is_in(cbw->bmCBWFlags); /* Set error codes which will be requested with REQUEST_SENSE * by the host later. */ msc->sense_key = SCSI_SENSE_KEY_ILLEGAL_REQUEST; msc->additional_sense_code = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; /* Stall appropriate endpoint and send FAILED for the CSW. */ if (direc_is_in || cbw_length == 0) stall_in_and_set_status(msc, cbw_length, MSC_STATUS_FAILED); else stall_out_and_set_status(msc, cbw_length, MSC_STATUS_FAILED); goto fail; } return; bad_cbw: /* If the CBW is not valid or is not meaningful, then stall both * endpoints until a Reset Recovery (BOT 5.3.4) procedure is * completed by the host (BOT 5.3, Figure 2). */ usb_halt_ep_in(msc->in_endpoint); usb_halt_ep_out(msc->out_endpoint); msc->state = MSC_NEEDS_RESET_RECOVERY; fail: return; }
static void phase_error(struct msc_application_data *msc) { send_csw(msc, 0, MSC_STATUS_PHASE_ERROR); }