blob: 1c92aa6121bca99fb2e427646ce05401f8403cca [file] [log] [blame]
/* This source file is part of the AVR Software Framework 2.0.0 release */
/*This file is prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
*
* \brief Management of the SCSI decoding.
*
* This file manages the SCSI decoding.
*
* - Compiler: IAR EWAVR32 and GNU GCC for AVR32
* - Supported devices: All AVR32 devices with a USB module can be used.
* - AppNote:
*
* \author Atmel Corporation: http://www.atmel.com \n
* Support and FAQ: http://support.atmel.no/
*
******************************************************************************/
/* Copyright (c) 2009 Atmel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an Atmel
* AVR product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*
*/
//_____ I N C L U D E S ___________________________________________________
#include "conf_usb.h"
#if USB_DEVICE_FEATURE == ENABLED
#include "scsi_decoder.h"
#include "usb_drv.h"
#include "ctrl_access.h"
//_____ D E C L A R A T I O N S ____________________________________________
extern U8 usb_LUN;
extern U8 ms_endpoint;
/*! \brief Writes the header of the MODE SENSE (6) and (10) commands.
*
* \param b_sense_10 Boolean indicating whether the (10) version of the
* command is requested:
* \arg \c TRUE to specify a MODE SENSE (10) command;
* \arg \c FALSE to specify a MODE SENSE (6) command.
* \param u8_data_length Data length in bytes.
*/
static void sbc_header_mode_sense(Bool b_sense_10, U8 u8_data_length);
/*! \brief Writes the Error Recovery mode page.
*
* \param length Allocated data length in bytes.
*/
static void send_read_write_error_recovery_page(U8 length);
/*! \brief Writes the Informational Exceptions Control mode page.
*/
static void send_informational_exceptions_page(void);
//_____ D E F I N I T I O N S ______________________________________________
U8 g_scsi_command[16];
U8 g_scsi_status;
U32 g_scsi_data_remaining;
s_scsi_sense g_scsi_sense;
U8 g_scsi_ep_ms_in;
U8 g_scsi_ep_ms_out;
//! INQUIRY data.
static const sbc_st_std_inquiry_data sbc_std_inquiry_data =
{
// Byte 0: 0x00.
0, // PeripheralQualifier: Currently connected.
0, // DeviceType: Direct-access device.
// Byte 1: 0x80.
1, // RMB: Medium is removable (this bit must be at 1, else the medium isn't seen by Windows).
0, // Reserved1.
// Byte 2: 0x00.
0, // Version: Device not compliant to any standard.
// Byte 3: 0x02.
0, // AERC.
0, // Obsolete0.
0, // NormACA.
2, // Response data format.
// Byte 4: 0x1F.
// Byte 5: 0x00.
// Byte 6: 0x00.
// Reserved4[3].
{
0x1F, // Additional Length (n - 4).
0, // SCCS: SCC supported.
0
},
// Byte 7: 0x00.
0, // RelativeAddressing.
0, // Wide32Bit.
0, // Wide16Bit.
0, // Synchronous.
0, // LinkedCommands.
0, // Reserved5.
0, // CommandQueue.
0, // SoftReset.
SBC_VENDOR_ID,
SBC_PRODUCT_ID,
SBC_REVISION_ID
};
Bool scsi_decode_command(void)
{
Bool status = FALSE;
switch (g_scsi_command[0]) // Check received command.
{
case SBC_CMD_TEST_UNIT_READY: // 0x00 - Mandatory.
status = sbc_test_unit_ready();
break;
case SBC_CMD_REQUEST_SENSE: // 0x03 - Mandatory.
status = sbc_request_sense();
break;
case SBC_CMD_INQUIRY: // 0x12 - Mandatory.
status = sbc_inquiry();
break;
case SBC_CMD_MODE_SENSE_6: // 0x1A - Optional.
status = sbc_mode_sense(FALSE);
break;
case SBC_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: // 0x1E - Optional.
status = sbc_prevent_allow_medium_removal();
break;
case SBC_CMD_READ_CAPACITY_10: // 0x25 - Mandatory.
status = sbc_read_capacity();
break;
case SBC_CMD_READ_10: // 0x28 - Mandatory.
Scsi_start_read_action();
status = sbc_read_10();
Scsi_stop_read_action();
break;
case SBC_CMD_WRITE_10: // 0x2A - Optional.
Scsi_start_write_action();
status = sbc_write_10();
Scsi_stop_write_action();
break;
case SBC_CMD_VERIFY_10: // 0x2F - Optional.
status = TRUE;
sbc_lun_status_is_good();
break;
case SBC_CMD_MODE_SENSE_10: // 0x5A - Optional.
status = sbc_mode_sense(TRUE);
break;
case SBC_CMD_START_STOP_UNIT: // 0x1B - Ignored. This command is used by the Linux 2.4 kernel,
break; // for which we can not reply INVALID COMMAND, otherwise the disk will not mount.
case SBC_CMD_FORMAT_UNIT: // 0x04 - Mandatory.
case SBC_CMD_REASSIGN_BLOCKS: // 0x07 - Optional.
case SBC_CMD_READ_6: // 0x08 - Mandatory.
case SBC_CMD_WRITE_6: // 0x0A - Optional.
case SBC_CMD_MODE_SELECT_6: // 0x15 - Optional.
case SBC_CMD_RECEIVE_DIAGNOSTIC_RESULTS: // 0x1C - Optional.
case SBC_CMD_SEND_DIAGNOSTIC: // 0x1D - Mandatory.
case SBC_CMD_WRITE_AND_VERIFY_10: // 0x2E - Optional.
case SBC_CMD_PREFETCH_10: // 0x34 - Optional.
case SBC_CMD_SYNCHRONIZE_CACHE_10: // 0x35 - Optional.
case SBC_CMD_READ_DEFECT_DATA_10: // 0x37 - Optional.
case SBC_CMD_WRITE_BUFFER: // 0x3B - Optional.
case SBC_CMD_READ_BUFFER: // 0x3C - Optional.
case SBC_CMD_READ_LONG_10: // 0x3E - Optional.
case SBC_CMD_WRITE_LONG_10: // 0x3F - Optional.
case SBC_CMD_WRITE_SAME_10: // 0x41 - Optional.
case SBC_CMD_LOG_SELECT: // 0x4C - Optional.
case SBC_CMD_LOG_SENSE: // 0x4D - Optional.
case SBC_CMD_XDWRITE_10: // 0x50 - Optional.
case SBC_CMD_XPWRITE_10: // 0x51 - Optional.
case SBC_CMD_XDREAD_10: // 0x52 - Optional.
case SBC_CMD_XDWRITEREAD_10: // 0x53 - Optional.
case SBC_CMD_MODE_SELECT_10: // 0x55 - Optional.
case SBC_CMD_PERSISTENT_RESERVE_IN: // 0x5E - Optional.
case SBC_CMD_PERSISTENT_RESERVE_OUT: // 0x5F - Optional.
case SBC_CMD_EXTENDED_COPY: // 0x83 - Optional.
case SBC_CMD_RECEIVE_COPY_RESULTS: // 0x84 - Optional.
case SBC_CMD_ACCESS_CONTROL_IN: // 0x86 - Optional.
case SBC_CMD_ACCESS_CONTROL_OUT: // 0x87 - Optional.
case SBC_CMD_READ_16: // 0x88 - Optional.
case SBC_CMD_WRITE_16: // 0x8A - Optional.
case SBC_CMD_READ_ATTRIBUTE: // 0x8C - Optional.
case SBC_CMD_WRITE_ATTRIBUTE: // 0x8D - Optional.
case SBC_CMD_WRITE_AND_VERIFY_16: // 0x8E - Optional.
case SBC_CMD_VERIFY_16: // 0x8F - Optional.
case SBC_CMD_PREFETCH_16: // 0x90 - Optional.
case SBC_CMD_SYNCHRONIZE_CACHE_16: // 0x91 - Optional.
case SBC_CMD_WRITE_SAME_16: // 0x93 - Optional.
case SBC_CMD_REPORT_LUNS: // 0xA0 - Mandator.
case SBC_CMD_READ_12: // 0xA8 - Optional.
case SBC_CMD_WRITE_12: // 0xAA - Optional.
case SBC_CMD_WRITE_AND_VERIFY_12: // 0xAE - Optional.
case SBC_CMD_VERIFY_12: // 0xAF - Optional.
case SBC_CMD_READ_DEFECT_DATA_12: // 0xB7 - Optional.
default:
// Command not supported.
Sbc_send_failed();
Sbc_build_sense(SBC_SENSE_KEY_ILLEGAL_REQUEST, SBC_ASC_INVALID_COMMAND_OPERATION_CODE, 0x00);
break;
}
return status;
}
Bool sbc_test_unit_ready(void)
{
switch (mem_test_unit_ready(usb_LUN))
{
case CTRL_GOOD:
sbc_lun_status_is_good();
break;
case CTRL_NO_PRESENT:
sbc_lun_status_is_not_present();
break;
case CTRL_BUSY:
sbc_lun_status_is_busy_or_change();
break;
case CTRL_FAIL:
default:
sbc_lun_status_is_fail();
break;
}
return TRUE;
}
Bool sbc_request_sense(void)
{
U8 allocation_length;
U8 request_sense_output[18]; // The maximal size of request is 17.
allocation_length = min(g_scsi_command[4], sizeof(request_sense_output));
if( allocation_length != 0 )
{
// Initialize the request sense data.
request_sense_output[ 0] = SBC_RESPONSE_CODE_SENSE; // 0x70.
request_sense_output[ 1] = 0x00; // Obsolete.
request_sense_output[ 2] = g_scsi_sense.key;
request_sense_output[ 3] = 0x00; // For direct access media, Information field.
request_sense_output[ 4] = 0x00; // Give the unsigned logical block.
request_sense_output[ 5] = 0x00; // Address associated with the sense key.
request_sense_output[ 6] = 0x00;
request_sense_output[ 7] = SBC_ADDITIONAL_SENSE_LENGTH; // Device shall not adjust the additional sense length to reflect truncation.
request_sense_output[ 8] = SBC_COMMAND_SPECIFIC_INFORMATION_3;
request_sense_output[ 9] = SBC_COMMAND_SPECIFIC_INFORMATION_2;
request_sense_output[10] = SBC_COMMAND_SPECIFIC_INFORMATION_1;
request_sense_output[11] = SBC_COMMAND_SPECIFIC_INFORMATION_0;
request_sense_output[12] = g_scsi_sense.asc;
request_sense_output[13] = g_scsi_sense.ascq;
request_sense_output[14] = SBC_FIELD_REPLACEABLE_UNIT_CODE;
request_sense_output[15] = SBC_SENSE_KEY_SPECIFIC_2;
request_sense_output[16] = SBC_SENSE_KEY_SPECIFIC_1;
request_sense_output[17] = SBC_SENSE_KEY_SPECIFIC_0;
// Send the request data.
Usb_reset_endpoint_fifo_access(g_scsi_ep_ms_in);
usb_write_ep_txpacket(g_scsi_ep_ms_in, request_sense_output, allocation_length, NULL);
Sbc_valid_write_usb(allocation_length);
// MSC Compliance - Wait end of all transmitions on USB line, because a stall may be send after data
while( 0 != Usb_nb_busy_bank(EP_MS_IN) );
}
sbc_lun_status_is_good();
return (allocation_length == g_scsi_command[4]);
}
Bool sbc_inquiry(void)
{
U8 allocation_length;
// CMDT or EPVD bit is not 0 or PAGE or OPERATION CODE fields != 0x00.
if ((g_scsi_command[1] & 0x03) || g_scsi_command[2])
{
sbc_lun_status_is_cdb_field();
return FALSE;
}
// Send standard INQUIRY data (bytes 0 to (allocation_length - 1)).
allocation_length = min(g_scsi_command[4], sizeof(sbc_st_std_inquiry_data));
if( allocation_length != 0 )
{
while (!Is_usb_in_ready(g_scsi_ep_ms_in))
{
if(!Is_usb_endpoint_enabled(g_scsi_ep_ms_in))
return FALSE; // USB Reset
}
Usb_reset_endpoint_fifo_access(g_scsi_ep_ms_in);
usb_write_ep_txpacket(g_scsi_ep_ms_in, &sbc_std_inquiry_data,
allocation_length, NULL);
Sbc_valid_write_usb(allocation_length);
// MSC Compliance - Wait end of all transmitions on USB line, because a stall may be send after data
while( 0 != Usb_nb_busy_bank(EP_MS_IN) );
}
sbc_lun_status_is_good();
return (allocation_length == g_scsi_command[4]);
}
Bool sbc_mode_sense(Bool b_sense_10)
{
U8 allocation_length = (b_sense_10) ? g_scsi_command[8] : g_scsi_command[4];
// Switch for page code.
switch (g_scsi_command[2] & SBC_MSK_PAGE_CODE)
{
case SBC_PAGE_CODE_READ_WRITE_ERROR_RECOVERY:
sbc_header_mode_sense(b_sense_10, SBC_MODE_DATA_LENGTH_READ_WRITE_ERROR_RECOVERY);
send_read_write_error_recovery_page(allocation_length);
Sbc_valid_write_usb(SBC_MODE_DATA_LENGTH_READ_WRITE_ERROR_RECOVERY + 1);
break;
// Page Code: Informational Exceptions Control mode page.
case SBC_PAGE_CODE_INFORMATIONAL_EXCEPTIONS:
sbc_header_mode_sense(b_sense_10, SBC_MODE_DATA_LENGTH_INFORMATIONAL_EXCEPTIONS);
send_informational_exceptions_page();
Sbc_valid_write_usb(SBC_MODE_DATA_LENGTH_INFORMATIONAL_EXCEPTIONS + 1);
break;
case SBC_PAGE_CODE_ALL:
if( b_sense_10 ) {
sbc_header_mode_sense( b_sense_10 , (allocation_length < (SBC_MODE_DATA_LENGTH_CODE_ALL+2))? (allocation_length-2) : SBC_MODE_DATA_LENGTH_CODE_ALL );
}else{
sbc_header_mode_sense( b_sense_10 , (allocation_length < (SBC_MODE_DATA_LENGTH_CODE_ALL+1))? (allocation_length-1) : SBC_MODE_DATA_LENGTH_CODE_ALL );
}
if (allocation_length == ((b_sense_10) ? 8 : 4))
{
Sbc_valid_write_usb(allocation_length);
break;
}
// Send page by ascending order code.
send_read_write_error_recovery_page(allocation_length); // 12 bytes.
if (allocation_length > 12)
{
send_informational_exceptions_page(); // 12 bytes.
Sbc_valid_write_usb(SBC_MODE_DATA_LENGTH_CODE_ALL + 1);
}
else
{
Sbc_valid_write_usb(allocation_length);
}
break;
default:
sbc_lun_status_is_cdb_field();
return FALSE;
}
sbc_lun_status_is_good();
return TRUE;
}
Bool sbc_prevent_allow_medium_removal(void)
{
sbc_lun_status_is_good();
return TRUE;
}
Bool sbc_read_capacity(void)
{
U32 mem_size_nb_sector;
switch (mem_read_capacity(usb_LUN, &mem_size_nb_sector))
{
case CTRL_GOOD:
Usb_reset_endpoint_fifo_access(g_scsi_ep_ms_in);
// Return nb block.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 32,
sbc_format_mcu_to_scsi_data(32, mem_size_nb_sector));
// Return block size (= 512 bytes).
Usb_write_endpoint_data(g_scsi_ep_ms_in, 32,
Sbc_format_mcu_to_scsi_data(32, 512));
Sbc_valid_write_usb(SBC_READ_CAPACITY_LENGTH);
sbc_lun_status_is_good();
return TRUE;
case CTRL_NO_PRESENT:
sbc_lun_status_is_not_present();
break;
case CTRL_BUSY:
sbc_lun_status_is_busy_or_change();
break;
case CTRL_FAIL:
default:
sbc_lun_status_is_fail();
break;
}
return FALSE;
}
Bool sbc_read_10(void)
{
U32 mass_addr; // Read/write block address.
U16 mass_size; // Read/write number of blocks.
// Read address.
MSB0W(mass_addr) = g_scsi_command[2];
MSB1W(mass_addr) = g_scsi_command[3];
MSB2W(mass_addr) = g_scsi_command[4];
MSB3W(mass_addr) = g_scsi_command[5];
// Read size.
MSB(mass_size) = g_scsi_command[7];
LSB(mass_size) = g_scsi_command[8];
if( ms_endpoint == EP_MS_OUT )
{
// Error in command field
sbc_lun_status_is_cdb_field();
return FALSE;
}
// No data to transfer.
if( 0 == g_scsi_data_remaining )
{
if( mass_size == (g_scsi_data_remaining/512) )
{
sbc_lun_status_is_good();
}else{
sbc_lun_status_is_cdb_field();
}
return TRUE;
}
switch (memory_2_usb(usb_LUN, mass_addr, g_scsi_data_remaining/512))
{
case CTRL_GOOD:
if( mass_size == (g_scsi_data_remaining/512) )
{
sbc_lun_status_is_good();
}else{
sbc_lun_status_is_cdb_field();
}
g_scsi_data_remaining = 0;
return TRUE;
case CTRL_NO_PRESENT:
sbc_lun_status_is_not_present();
break;
case CTRL_BUSY:
sbc_lun_status_is_busy_or_change();
break;
case CTRL_FAIL:
default:
sbc_lun_status_is_fail();
break;
}
return FALSE;
}
Bool sbc_write_10(void)
{
U32 mass_addr; // Read/write block address.
U16 mass_size; // Read/write number of blocks.
// Read address.
MSB0W(mass_addr) = g_scsi_command[2];
MSB1W(mass_addr) = g_scsi_command[3];
MSB2W(mass_addr) = g_scsi_command[4];
MSB3W(mass_addr) = g_scsi_command[5];
// Read size.
MSB(mass_size) = g_scsi_command[7];
LSB(mass_size) = g_scsi_command[8];
if( ms_endpoint == EP_MS_IN )
{
// Error in command field
sbc_lun_status_is_cdb_field();
return FALSE;
}
// No data to transfer.
if( 0 == g_scsi_data_remaining )
{
if( mass_size == (g_scsi_data_remaining/512) )
{
sbc_lun_status_is_good();
}else{
sbc_lun_status_is_cdb_field();
}
return TRUE;
}
if (mem_wr_protect(usb_LUN))
{
sbc_lun_status_is_protected();
return FALSE;
}
switch (usb_2_memory(usb_LUN, mass_addr, g_scsi_data_remaining/512 ))
{
case CTRL_GOOD:
if( mass_size == (g_scsi_data_remaining/512) )
{
sbc_lun_status_is_good();
}else{
sbc_lun_status_is_cdb_field();
}
g_scsi_data_remaining = 0;
return TRUE;
case CTRL_NO_PRESENT:
sbc_lun_status_is_not_present();
break;
case CTRL_BUSY:
sbc_lun_status_is_busy_or_change();
break;
case CTRL_FAIL:
default:
sbc_lun_status_is_fail();
break;
}
return FALSE;
}
static void sbc_header_mode_sense(Bool b_sense_10, U8 u8_data_length)
{
Usb_reset_endpoint_fifo_access(g_scsi_ep_ms_in);
// Send data length.
if (b_sense_10)
{
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, 0);
}
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, u8_data_length);
// Send device type.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, SBC_MEDIUM_TYPE);
// Write protect status.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8,
(mem_wr_protect(usb_LUN)) ? SBC_DEV_SPEC_PARAM_WR_PROTECT :
SBC_DEV_SPEC_PARAM_WR_ENABLE);
if (b_sense_10)
{
// Reserved.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 16, 0);
}
// Send block descriptor length.
if (b_sense_10)
{
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, 0);
}
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, SBC_BLOCK_DESCRIPTOR_LENGTH);
}
static void send_read_write_error_recovery_page(U8 length)
{
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, SBC_PAGE_CODE_READ_WRITE_ERROR_RECOVERY);
// Page Length.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, SBC_PAGE_LENGTH_READ_WRITE_ERROR_RECOVERY);
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, 0x80);
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, SBC_READ_RETRY_COUNT);
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, 0x00);
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, 0x00);
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, 0x00);
// Reserved.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, 0x00);
if (length > 12)
{
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, SBC_WRITE_RETRY_COUNT);
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, 0x00);
Usb_write_endpoint_data(g_scsi_ep_ms_in, 16,
Sbc_format_mcu_to_scsi_data(16, SBC_RECOVERY_TIME_LIMIT));
}
}
static void send_informational_exceptions_page(void)
{
// Page Code: Informational Exceptions Control mode page.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, SBC_PAGE_CODE_INFORMATIONAL_EXCEPTIONS);
// Page Length.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, SBC_PAGE_LENGTH_INFORMATIONAL_EXCEPTIONS);
// ..., test bit = 0, ...
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, 0x00);
// MRIE = 0x05.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 8, SBC_MRIE_GENERATE_NO_SENSE);
// Interval Timer.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 32,
Sbc_format_mcu_to_scsi_data(32, 0x00000000));
// Report Count.
Usb_write_endpoint_data(g_scsi_ep_ms_in, 32,
Sbc_format_mcu_to_scsi_data(32, 0x00000001));
}
void sbc_lun_status_is_good(void)
{
Sbc_send_good();
Sbc_build_sense(SBC_SENSE_KEY_NO_SENSE, SBC_ASC_NO_ADDITIONAL_SENSE_INFORMATION, 0x00);
}
void sbc_lun_status_is_not_present(void)
{
Sbc_send_failed();
Sbc_build_sense(SBC_SENSE_KEY_NOT_READY, SBC_ASC_MEDIUM_NOT_PRESENT, 0x00);
}
void sbc_lun_status_is_busy_or_change(void)
{
Sbc_send_failed();
Sbc_build_sense(SBC_SENSE_KEY_UNIT_ATTENTION, SBC_ASC_NOT_READY_TO_READY_CHANGE, 0x00);
}
void sbc_lun_status_is_fail(void)
{
Sbc_send_failed();
Sbc_build_sense(SBC_SENSE_KEY_HARDWARE_ERROR, SBC_ASC_NO_ADDITIONAL_SENSE_INFORMATION, 0x00);
}
void sbc_lun_status_is_protected(void)
{
Sbc_send_failed();
Sbc_build_sense(SBC_SENSE_KEY_DATA_PROTECT, SBC_ASC_WRITE_PROTECTED, 0x00);
}
void sbc_lun_status_is_cdb_field(void)
{
Sbc_send_failed();
Sbc_build_sense(SBC_SENSE_KEY_ILLEGAL_REQUEST, SBC_ASC_INVALID_FIELD_IN_CDB, 0x00);
}
#endif // USB_DEVICE_FEATURE == ENABLED