#include "usb_scsi.h"
#include "device_info.h"
#include "transform_procs.h"
#include "tracing.h"
#include "scsi_commands.h"
#include "settings.h"
#include "strings.h"
#include <string.h>
#define UFI_COMMAND_LEN 12
#define ATAPI_COMMAND_LEN 12
static void
transform_6_to_10(uint8 *cmd, uint8 len, uint8 **rcmd, uint8 *rlen)
{
scsi_cmd_generic_6 *from = (scsi_cmd_generic_6 *)cmd;
*rlen = 10;
memset(*rcmd, 0, 10);
switch (from->opcode) {
case READ_6:
case WRITE_6:
{
scsi_cmd_rw_10 *to = (scsi_cmd_rw_10 *)(*rcmd);
to->opcode = (from->opcode == READ_6) ? READ_10 : WRITE_10;
to->lba = B_HOST_TO_BENDIAN_INT32(((from->addr[0] & 0x1f) << 16)
| (from->addr[1] << 8) | from->addr[0]);
to->lun = (from->addr[0] & CMD_LUN) >> CMD_LUN_SHIFT;
to->control = from->ctrl;
if (from->len == 0) {
to->length = B_HOST_TO_BENDIAN_INT16((uint16)256);
} else
to->length = B_HOST_TO_BENDIAN_INT16((uint16)from->len);
}
case MODE_SENSE_6:
case MODE_SELECT_6:
{
scsi_cmd_generic_10 *to = (scsi_cmd_generic_10 *)(*rcmd);
if (from->opcode == MODE_SENSE_6) {
to->opcode = MODE_SENSE_10;
((scsi_cmd_mode_sense_10 *)to)->byte3
= ((scsi_cmd_mode_sense_6 *)from)->byte3;
} else
to->opcode = MODE_SELECT_10;
to->byte2 = from->addr[0];
to->len[1] = from->len + 4;
to->ctrl = from->ctrl;
}
default:
break;
}
}
static bool
transform_cmd_6_to_10(usb_device_info *udi, uint8 *cmd, uint8 len,
uint8 **rcmd, uint8 *rlen)
{
scsi_cmd_generic_6 *from = (scsi_cmd_generic_6 *)cmd;
switch (from->opcode) {
case READ_6:
case WRITE_6:
{
if (!HAS_FIXES(udi->properties, FIX_FORCE_RW_TO_6)) {
transform_6_to_10(cmd, len, rcmd, rlen);
return true;
}
break;
}
case MODE_SENSE_6:
case MODE_SELECT_6:
{
if (HAS_FIXES(udi->properties, FIX_FORCE_MS_TO_10)) {
transform_6_to_10(cmd, len, rcmd, rlen);
return true;
}
break;
}
}
return false;
}
static bool
transform_cmd_test_unit_ready(usb_device_info *udi, uint8 *cmd, uint8 len,
uint8 **rcmd, uint8 *rlen)
{
scsi_cmd_start_stop_unit *command = (scsi_cmd_start_stop_unit *)(*rcmd);
if (!HAS_FIXES(udi->properties, FIX_TRANS_TEST_UNIT))
return false;
memset(*rcmd, 0, *rlen);
command->opcode = START_STOP_UNIT;
command->start_loej = CMD_SSU_START;
*rlen = 6;
return true;
}
static status_t
scsi_transform(usb_device_info *udi, uint8 *cmd, uint8 len, uint8 **rcmd,
uint8 *rlen)
{
bool transformed = false;
scsi_cmd_generic *command = (scsi_cmd_generic *)cmd;
TRACE_SCSI_COMMAND(cmd, len);
switch (command->opcode) {
case READ_6:
case WRITE_6:
case MODE_SENSE_6:
case MODE_SELECT_6:
transformed = transform_cmd_6_to_10(udi, cmd, len, rcmd, rlen);
break;
case TEST_UNIT_READY:
transformed = transform_cmd_test_unit_ready(udi, cmd, len, rcmd,
rlen);
break;
default:
break;
}
if (!transformed) {
*rcmd = cmd;
*rlen = len;
} else
TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen);
return B_OK;
}
static status_t
rbc_transform(usb_device_info *udi, uint8 *cmd, uint8 len, uint8 **rcmd,
uint8 *rlen)
{
bool transformed = false;
scsi_cmd_generic *command = (scsi_cmd_generic *)cmd;
TRACE_SCSI_COMMAND(cmd, len);
switch (command->opcode) {
case TEST_UNIT_READY:
transformed = transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen);
break;
case READ_6:
case WRITE_6:
transformed = transform_cmd_6_to_10(udi, cmd, len, rcmd, rlen);
break;
case FORMAT_UNIT:
case INQUIRY:
case MODE_SELECT_6:
case MODE_SENSE_6:
case PERSISTENT_RESERVE_IN:
case PERSISTENT_RESERVE_OUT:
case PREVENT_ALLOW_MEDIA_REMOVAL:
case READ_10:
case READ_CAPACITY:
case RELEASE_6:
case REQUEST_SENSE:
case RESERVE_6:
case START_STOP_UNIT:
case SYNCHRONIZE_CACHE:
case VERIFY:
case WRITE_10:
case WRITE_BUFFER:
*rcmd = cmd;
*rlen = len;
break;
default:
TRACE_ALWAYS("An unsupported RBC command: %08x\n", command->opcode);
return B_ERROR;
}
if (transformed)
TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen);
return B_OK;
}
static status_t
ufi_transform(usb_device_info *udi, uint8 *cmd, uint8 len, uint8 **rcmd,
uint8 *rlen)
{
scsi_cmd_generic *command = (scsi_cmd_generic *)cmd;
status_t status = B_OK;
TRACE_SCSI_COMMAND(cmd, len);
memset(*rcmd, 0, UFI_COMMAND_LEN);
switch (command->opcode) {
case READ_6:
case WRITE_6:
case MODE_SENSE_6:
case MODE_SELECT_6:
transform_6_to_10(cmd, len, rcmd, rlen);
break;
case TEST_UNIT_READY:
if (transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen))
break;
case FORMAT_UNIT:
case INQUIRY:
case START_STOP_UNIT:
case MODE_SELECT_10:
case MODE_SENSE_10:
case PREVENT_ALLOW_MEDIA_REMOVAL:
case READ_10:
case READ_12:
case READ_CAPACITY:
case READ_FORMAT_CAPACITY:
case REQUEST_SENSE:
case REZERO_UNIT:
case SEEK_10:
case SEND_DIAGNOSTICS:
case VERIFY:
case WRITE_10:
case WRITE_12:
case WRITE_AND_VERIFY:
memcpy(*rcmd, cmd, len);
break;
default:
TRACE_ALWAYS("An unsupported UFI command: %08x\n",
command->opcode);
status = B_ERROR;
break;
}
*rlen = UFI_COMMAND_LEN;
if (status == B_OK)
TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen);
return status;
}
static status_t
atapi_transform(usb_device_info *udi, uint8 *cmd, uint8 len, uint8 **rcmd,
uint8 *rlen)
{
scsi_cmd_generic *command = (scsi_cmd_generic *)cmd;
status_t status = B_OK;
TRACE_SCSI_COMMAND(cmd, len);
memset(*rcmd, 0, ATAPI_COMMAND_LEN);
switch (command->opcode) {
case READ_6:
case WRITE_6:
case MODE_SENSE_6:
case MODE_SELECT_6:
transform_6_to_10(cmd, len, rcmd, rlen);
break;
case TEST_UNIT_READY:
if (transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen))
break;
case FORMAT_UNIT:
case INQUIRY:
case MODE_SELECT_10:
case MODE_SENSE_10:
case PREVENT_ALLOW_MEDIA_REMOVAL:
case READ_10:
case READ_12:
case READ_CAPACITY:
case READ_FORMAT_CAPACITY:
case REQUEST_SENSE:
case SEEK_10:
case START_STOP_UNIT:
case VERIFY:
case WRITE_10:
case WRITE_12:
case WRITE_AND_VERIFY:
case PAUSE_RESUME:
case PLAY_AUDIO:
case PLAY_AUDIO_MSF:
case REWIND:
case PLAY_AUDIO_TRACK:
case GET_CONFIGURATION:
case SYNCHRONIZE_CACHE:
case READ_BUFFER:
case READ_SUBCHANNEL:
case READ_TOC:
case READ_HEADER:
case READ_DISK_INFO:
case READ_TRACK_INFO:
case SEND_OPC:
case READ_MASTER_CUE:
case CLOSE_TR_SESSION:
case READ_BUFFER_CAP:
case SEND_CUE_SHEET:
case BLANK:
case EXCHANGE_MEDIUM:
case READ_DVD_STRUCTURE:
case SET_CD_SPEED:
case DVD_REPORT_KEY:
case DVD_SEND_KEY:
memcpy(*rcmd, cmd, len);
break;
default:
TRACE_ALWAYS("An unsupported (?) ATAPI command: %08x\n",
command->opcode);
status = B_ERROR;
break;
}
*rlen = ATAPI_COMMAND_LEN;
if (status == B_OK)
TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen);
return status;
}
static status_t
qic157_transform(usb_device_info *udi, uint8 *cmd, uint8 len, uint8 **rcmd,
uint8 *rlen)
{
scsi_cmd_generic *command = (scsi_cmd_generic *)cmd;
status_t status = B_OK;
TRACE_SCSI_COMMAND(cmd, len);
*rlen = ATAPI_COMMAND_LEN;
memset(*rcmd, 0, *rlen);
switch (command->opcode) {
case READ_6:
case WRITE_6:
case MODE_SENSE_6:
case MODE_SELECT_6:
transform_6_to_10(cmd, len, rcmd, rlen);
break;
case TEST_UNIT_READY:
if (transform_cmd_test_unit_ready(udi, cmd, len, rcmd, rlen))
break;
case ERASE:
case INQUIRY:
case LOAD_UNLOAD:
case LOCATE:
case LOG_SELECT:
case LOG_SENSE:
case READ_POSITION:
case REQUEST_SENSE:
case REWIND:
case SPACE:
case WRITE_FILEMARK:
*rcmd = cmd;
*rlen = len;
break;
default:
TRACE_ALWAYS("An unsupported QIC-157 command: %08x\n",
command->opcode);
status = B_ERROR;
break;
}
if (status == B_OK)
TRACE_SCSI_COMMAND_HLIGHT(*rcmd, *rlen);
return status;
}
transform_module_info scsi_transform_m = {
{0, 0, 0},
scsi_transform,
};
transform_module_info rbc_transform_m = {
{0, 0, 0},
rbc_transform,
};
transform_module_info ufi_transform_m = {
{0, 0, 0},
ufi_transform,
};
transform_module_info atapi_transform_m = {
{0, 0, 0},
atapi_transform,
};
transform_module_info qic157_transform_m = {
{0, 0, 0},
qic157_transform,
};