Пример #1
0
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();
}
Пример #2
0
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();
}
Пример #3
0
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;
	}
}
Пример #4
0
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);
	}
}
Пример #5
0
/* 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 {
Пример #6
0
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;
}
Пример #7
0
static void phase_error(struct msc_application_data *msc)
{
	send_csw(msc, 0, MSC_STATUS_PHASE_ERROR);
}