コード例 #1
0
status_t
usb_disk_receive_csw(disk_device *device, command_status_wrapper *status)
{
	status_t result = usb_disk_transfer_data(device, true, status,
		sizeof(command_status_wrapper));
	if (result != B_OK)
		return result;

	if (device->status != B_OK
		|| device->actual_length != sizeof(command_status_wrapper)) {
		// receiving the command status wrapper failed
		return B_ERROR;
	}

	return B_OK;
}
コード例 #2
0
status_t
usb_disk_operation(device_lun *lun, uint8 operation, uint8 opLength,
	uint32 logicalBlockAddress, uint16 transferLength, void *data,
	uint32 *dataLength, bool directionIn)
{
	TRACE("operation: lun: %u; op: %u; oplen: %u; lba: %lu; tlen: %u; data: %p; dlen: %p (%lu); in: %c\n",
		lun->logical_unit_number, operation, opLength, logicalBlockAddress,
		transferLength, data, dataLength, dataLength ? *dataLength : 0,
		directionIn ? 'y' : 'n');

	disk_device *device = lun->device;
	command_block_wrapper command;
	command.signature = CBW_SIGNATURE;
	command.tag = device->current_tag++;
	command.data_transfer_length = (dataLength != NULL ? *dataLength : 0);
	command.flags = (directionIn ? CBW_DATA_INPUT : CBW_DATA_OUTPUT);
	command.lun = lun->logical_unit_number;
	command.command_block_length = opLength;
	memset(command.command_block, 0, sizeof(command.command_block));

	switch (opLength) {
		case 6: {
			scsi_command_6 *commandBlock = (scsi_command_6 *)command.command_block;
			commandBlock->operation = operation;
			commandBlock->lun = lun->logical_unit_number << 5;
			commandBlock->allocation_length = (uint8)transferLength;
			if (operation == SCSI_MODE_SENSE_6) {
				// we hijack the lba argument to transport the desired page
				commandBlock->reserved[1] = (uint8)logicalBlockAddress;
			}
			break;
		}

		case 10: {
			scsi_command_10 *commandBlock = (scsi_command_10 *)command.command_block;
			commandBlock->operation = operation;
			commandBlock->lun_flags = lun->logical_unit_number << 5;
			commandBlock->logical_block_address = htonl(logicalBlockAddress);
			commandBlock->transfer_length = htons(transferLength);
			break;
		}

		default:
			TRACE_ALWAYS("unsupported operation length %d\n", opLength);
			return B_BAD_VALUE;
	}

	status_t result = usb_disk_transfer_data(device, false, &command,
		sizeof(command_block_wrapper));
	if (result != B_OK)
		return result;

	if (device->status != B_OK ||
		device->actual_length != sizeof(command_block_wrapper)) {
		// sending the command block wrapper failed
		TRACE_ALWAYS("sending the command block wrapper failed\n");
		usb_disk_reset_recovery(device);
		return B_ERROR;
	}

	size_t transferedData = 0;
	if (data != NULL && dataLength != NULL && *dataLength > 0) {
		// we have data to transfer in a data stage
		result = usb_disk_transfer_data(device, directionIn, data, *dataLength);
		if (result != B_OK)
			return result;

		transferedData = device->actual_length;
		if (device->status != B_OK || transferedData != *dataLength) {
			// sending or receiving of the data failed
			if (device->status == B_DEV_STALLED) {
				TRACE("stall while transfering data\n");
				gUSBModule->clear_feature(directionIn ? device->bulk_in
					: device->bulk_out, USB_FEATURE_ENDPOINT_HALT);
			} else {
				TRACE_ALWAYS("sending or receiving of the data failed\n");
				usb_disk_reset_recovery(device);
				return B_ERROR;
			}
		}
	}

	command_status_wrapper status;
	result =  usb_disk_receive_csw(device, &status);
	if (result != B_OK) {
		// in case of a stall or error clear the stall and try again
		gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT);
		result = usb_disk_receive_csw(device, &status);
	}

	if (result != B_OK) {
		TRACE_ALWAYS("receiving the command status wrapper failed\n");
		usb_disk_reset_recovery(device);
		return result;
	}

	if (status.signature != CSW_SIGNATURE || status.tag != command.tag) {
		// the command status wrapper is not valid
		TRACE_ALWAYS("command status wrapper is not valid\n");
		usb_disk_reset_recovery(device);
		return B_ERROR;
	}

	switch (status.status) {
		case CSW_STATUS_COMMAND_PASSED:
		case CSW_STATUS_COMMAND_FAILED: {
			if (status.data_residue > command.data_transfer_length) {
				// command status wrapper is not meaningful
				TRACE_ALWAYS("command status wrapper has invalid residue\n");
				usb_disk_reset_recovery(device);
				return B_ERROR;
			}

			if (dataLength != NULL) {
				*dataLength -= status.data_residue;
				if (transferedData < *dataLength) {
					TRACE_ALWAYS("less data transfered than indicated\n");
					*dataLength = transferedData;
				}
			}

			if (status.status == CSW_STATUS_COMMAND_PASSED) {
				// the operation is complete and has succeeded
				return B_OK;
			} else {
				// the operation is complete but has failed at the SCSI level
				if (operation != SCSI_TEST_UNIT_READY_6) {
					TRACE_ALWAYS("operation 0x%02x failed at the SCSI level\n",
						operation);
				}

				result = usb_disk_request_sense(lun);
				return result == B_OK ? B_ERROR : result;
			}
		}

		case CSW_STATUS_PHASE_ERROR: {
			// a protocol or device error occured
			TRACE_ALWAYS("phase error in operation 0x%02x\n", operation);
			usb_disk_reset_recovery(device);
			return B_ERROR;
		}

		default: {
			// command status wrapper is not meaningful
			TRACE_ALWAYS("command status wrapper has invalid status\n");
			usb_disk_reset_recovery(device);
			return B_ERROR;
		}
	}
}
コード例 #3
0
ファイル: usb_disk.cpp プロジェクト: Karvjorm/haiku
status_t
usb_disk_operation(device_lun *lun, uint8* operation,
	void *data,	uint32 *dataLength, bool directionIn)
{
	// TODO: remove transferLength
	TRACE("operation: lun: %u; op: 0x%x; data: %p; dlen: %p (%lu); in: %c\n",
		lun->logical_unit_number, operation[0], data, dataLength,
		dataLength ? *dataLength : 0, directionIn ? 'y' : 'n');

	disk_device* device = lun->device;

	// Step 1 : send the SCSI operation as a class specific request
	size_t actualLength = 12;
	status_t result = gUSBModule->send_request(device->device,
		USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT, 0 /*request*/, 0/*value*/,
		device->interface/*index*/, 12, operation, &actualLength);

	if (result != B_OK || actualLength != 12) {
		TRACE("Command stage: wrote %ld bytes (error: %s)\n",
			actualLength, strerror(result));

		// There was an error, we have to do a request sense to reset the device
		if (operation[0] != SCSI_REQUEST_SENSE_6) {
			usb_disk_request_sense(lun);
		}
		return result;
	}

	// Step 2 : data phase : send or receive data
	size_t transferedData = 0;
	if (data != NULL && dataLength != NULL && *dataLength > 0) {
		// we have data to transfer in a data stage
		result = usb_disk_transfer_data(device, directionIn, data, *dataLength);
		if (result != B_OK) {
			TRACE("Error %s in data phase\n", strerror(result));
			return result;
		}

		transferedData = device->actual_length;
		if (device->status != B_OK || transferedData != *dataLength) {
			// sending or receiving of the data failed
			if (device->status == B_DEV_STALLED) {
				TRACE("stall while transfering data\n");
				gUSBModule->clear_feature(directionIn ? device->bulk_in
					: device->bulk_out, USB_FEATURE_ENDPOINT_HALT);
			} else {
				TRACE_ALWAYS("sending or receiving of the data failed\n");
				usb_disk_reset_recovery(device);
				return B_ERROR;
			}
		}
	}

	// step 3 : wait for the device to send the interrupt ACK
	if (operation[0] != SCSI_REQUEST_SENSE_6) {
		command_status_wrapper status;
		result =  usb_disk_receive_csw(device, &status);
		if (result != B_OK) {
			// in case of a stall or error clear the stall and try again
			TRACE("Error receiving interrupt: %s. Retrying...\n", strerror(result));
			gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT);
			result = usb_disk_receive_csw(device, &status);
		}

		if (result != B_OK) {
			TRACE_ALWAYS("receiving the command status interrupt failed\n");
			usb_disk_reset_recovery(device);
			return result;
		}

		// wait for the device to finish the operation.
		result = usb_disk_request_sense(lun);
	}
	return result;
}