| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- #include "mass_storage_scsi.h"
- #include <core/log.h>
- #define TAG "MassStorageSCSI"
- #define SCSI_TEST_UNIT_READY (0x00)
- #define SCSI_REQUEST_SENSE (0x03)
- #define SCSI_INQUIRY (0x12)
- #define SCSI_READ_FORMAT_CAPACITIES (0x23)
- #define SCSI_READ_CAPACITY_10 (0x25)
- #define SCSI_MODE_SENSE_6 (0x1A)
- #define SCSI_READ_10 (0x28)
- #define SCSI_PREVENT_MEDIUM_REMOVAL (0x1E)
- #define SCSI_START_STOP_UNIT (0x1B)
- #define SCSI_WRITE_10 (0x2A)
- bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len) {
- if(!len) {
- scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
- scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
- return false;
- }
- FURI_LOG_T(TAG, "START %02X", cmd[0]);
- scsi->cmd = cmd;
- scsi->cmd_len = len;
- scsi->rx_done = false;
- scsi->tx_done = false;
- switch(cmd[0]) {
- case SCSI_WRITE_10: {
- if(len < 10) return false;
- scsi->write_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
- scsi->write_10.count = cmd[7] << 8 | cmd[8];
- FURI_LOG_D(TAG, "SCSI_WRITE_10 %08lX %04X", scsi->write_10.lba, scsi->write_10.count);
- return true;
- }; break;
- case SCSI_READ_10: {
- if(len < 10) return false;
- scsi->read_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
- scsi->read_10.count = cmd[7] << 8 | cmd[8];
- FURI_LOG_D(TAG, "SCSI_READ_10 %08lX %04X", scsi->read_10.lba, scsi->read_10.count);
- return true;
- }; break;
- }
- return true;
- }
- bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len) {
- FURI_LOG_T(TAG, "RX %02X len %lu", scsi->cmd[0], len);
- if(scsi->rx_done) return false;
- switch(scsi->cmd[0]) {
- case SCSI_WRITE_10: {
- uint32_t block_size = SCSI_BLOCK_SIZE;
- uint16_t blocks = len / block_size;
- bool result =
- scsi->fn.write(scsi->fn.ctx, scsi->write_10.lba, blocks, data, blocks * block_size);
- scsi->write_10.lba += blocks;
- scsi->write_10.count -= blocks;
- if(!scsi->write_10.count) {
- scsi->rx_done = true;
- }
- return result;
- }; break;
- default: {
- FURI_LOG_W(TAG, "unexpected scsi rx data cmd=%02X", scsi->cmd[0]);
- scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
- scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
- return false;
- }; break;
- }
- }
- bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t cap) {
- FURI_LOG_T(TAG, "TX %02X cap %lu", scsi->cmd[0], cap);
- if(scsi->tx_done) return false;
- switch(scsi->cmd[0]) {
- case SCSI_REQUEST_SENSE: {
- FURI_LOG_D(TAG, "SCSI_REQUEST_SENSE");
- if(cap < 18) return false;
- memset(data, 0, cap);
- data[0] = 0x70; // fixed format sense data
- data[1] = 0; // obsolete
- data[2] = scsi->sk; // sense key
- data[3] = 0; // information
- data[4] = 0; // information
- data[5] = 0; // information
- data[6] = 0; // information
- data[7] = 10; // additional sense length (len-8)
- data[8] = 0; // command specific information
- data[9] = 0; // command specific information
- data[10] = 0; // command specific information
- data[11] = 0; // command specific information
- data[12] = scsi->asc; // additional sense code
- data[13] = 0; // additional sense code qualifier
- data[14] = 0; // field replaceable unit code
- data[15] = 0; // sense key specific information
- data[16] = 0; // sense key specific information
- data[17] = 0; // sense key specific information
- *len = 18;
- scsi->sk = 0;
- scsi->asc = 0;
- scsi->tx_done = true;
- return true;
- }; break;
- case SCSI_INQUIRY: {
- FURI_LOG_D(TAG, "SCSI_INQUIRY");
- if(scsi->cmd_len < 5) return false;
- if(cap < 36) return false;
- bool evpd = scsi->cmd[1] & 1;
- uint8_t page_code = scsi->cmd[2];
- if(evpd == 0) {
- if(page_code != 0) return false;
- data[0] = 0x00; // device type: direct access block device
- data[1] = 0x80; // removable: true
- data[2] = 0x04; // version
- data[3] = 0x02; // response data format
- data[4] = 31; // additional length (len - 5)
- data[5] = 0; // flags
- data[6] = 0; // flags
- data[7] = 0; // flags
- memcpy(data + 8, "Flipper ", 8); // vendor id
- memcpy(data + 16, "Mass Storage ", 16); // product id
- memcpy(data + 32, "0001", 4); // product revision level
- *len = 36;
- scsi->tx_done = true;
- return true;
- } else {
- if(page_code != 0x80) {
- FURI_LOG_W(TAG, "Unsupported VPD code %02X", page_code);
- return false;
- }
- data[0] = 0x00;
- data[1] = 0x80;
- data[2] = 0x00;
- data[3] = 0x01; // Serial len
- data[4] = '0';
- *len = 5;
- scsi->tx_done = true;
- return true;
- }
- }; break;
- case SCSI_READ_FORMAT_CAPACITIES: {
- FURI_LOG_D(TAG, "SCSI_READ_FORMAT_CAPACITIES");
- if(cap < 12) {
- return false;
- }
- uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx);
- uint32_t block_size = SCSI_BLOCK_SIZE;
- // Capacity List Header
- data[0] = 0;
- data[1] = 0;
- data[2] = 0;
- data[3] = 8;
- // Capacity Descriptor
- data[4] = (n_blocks - 1) >> 24;
- data[5] = (n_blocks - 1) >> 16;
- data[6] = (n_blocks - 1) >> 8;
- data[7] = (n_blocks - 1) & 0xFF;
- data[8] = 0x02; // Formatted media
- data[9] = block_size >> 16;
- data[10] = block_size >> 8;
- data[11] = block_size & 0xFF;
- *len = 12;
- scsi->tx_done = true;
- return true;
- }; break;
- case SCSI_READ_CAPACITY_10: {
- FURI_LOG_D(TAG, "SCSI_READ_CAPACITY_10");
- if(cap < 8) return false;
- uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx);
- uint32_t block_size = SCSI_BLOCK_SIZE;
- data[0] = (n_blocks - 1) >> 24;
- data[1] = (n_blocks - 1) >> 16;
- data[2] = (n_blocks - 1) >> 8;
- data[3] = (n_blocks - 1) & 0xFF;
- data[4] = block_size >> 24;
- data[5] = block_size >> 16;
- data[6] = block_size >> 8;
- data[7] = block_size & 0xFF;
- *len = 8;
- scsi->tx_done = true;
- return true;
- }; break;
- case SCSI_MODE_SENSE_6: {
- FURI_LOG_D(TAG, "SCSI_MODE_SENSE_6 %lu", cap);
- if(cap < 4) return false;
- data[0] = 3; // mode data length (len - 1)
- data[1] = 0; // medium type
- data[2] = 0; // device-specific parameter
- data[3] = 0; // block descriptor length
- *len = 4;
- scsi->tx_done = true;
- return true;
- }; break;
- case SCSI_READ_10: {
- uint32_t block_size = SCSI_BLOCK_SIZE;
- bool result =
- scsi->fn.read(scsi->fn.ctx, scsi->read_10.lba, scsi->read_10.count, data, len, cap);
- *len -= *len % block_size;
- uint16_t blocks = *len / block_size;
- scsi->read_10.lba += blocks;
- scsi->read_10.count -= blocks;
- if(!scsi->read_10.count) {
- scsi->tx_done = true;
- }
- return result;
- }; break;
- default: {
- FURI_LOG_W(TAG, "unexpected scsi tx data cmd=%02X", scsi->cmd[0]);
- scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
- scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
- return false;
- }; break;
- }
- }
- bool scsi_cmd_end(SCSISession* scsi) {
- FURI_LOG_T(TAG, "END %02X", scsi->cmd[0]);
- uint8_t* cmd = scsi->cmd;
- uint8_t len = scsi->cmd_len;
- scsi->cmd = NULL;
- scsi->cmd_len = 0;
- switch(cmd[0]) {
- case SCSI_WRITE_10:
- return scsi->rx_done;
- case SCSI_REQUEST_SENSE:
- case SCSI_INQUIRY:
- case SCSI_READ_FORMAT_CAPACITIES:
- case SCSI_READ_CAPACITY_10:
- case SCSI_MODE_SENSE_6:
- case SCSI_READ_10:
- return scsi->tx_done;
- case SCSI_TEST_UNIT_READY: {
- FURI_LOG_D(TAG, "SCSI_TEST_UNIT_READY");
- return true;
- }; break;
- case SCSI_PREVENT_MEDIUM_REMOVAL: {
- if(len < 6) return false;
- bool prevent = cmd[5];
- FURI_LOG_D(TAG, "SCSI_PREVENT_MEDIUM_REMOVAL prevent=%d", prevent);
- return !prevent;
- }; break;
- case SCSI_START_STOP_UNIT: {
- if(len < 6) return false;
- bool eject = (cmd[4] & 2) != 0;
- bool start = (cmd[4] & 1) != 0;
- FURI_LOG_D(TAG, "SCSI_START_STOP_UNIT eject=%d start=%d", eject, start);
- if(eject) {
- scsi->fn.eject(scsi->fn.ctx);
- }
- return true;
- }; break;
- default: {
- FURI_LOG_W(TAG, "unexpected scsi cmd=%02X", cmd[0]);
- scsi->sk = SCSI_SK_ILLEGAL_REQUEST;
- scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE;
- return false;
- }; break;
- }
- }
|