#include <sys/cdefs.h>
#if !defined(DISABLE_SATI_UNMAP)
#include <dev/isci/scil/sati_unmap.h>
#include <dev/isci/scil/sati_callbacks.h>
#include <dev/isci/scil/sati_translator_sequence.h>
#include <dev/isci/scil/sati_util.h>
#include <dev/isci/scil/intel_ata.h>
#include <dev/isci/scil/intel_scsi.h>
#include <dev/isci/scil/intel_sat.h>
U32 sati_unmap_calculate_dsm_blocks(
SATI_TRANSLATOR_SEQUENCE_T * sequence,
U32 dsm_descriptor_count
)
{
U32 blocks = (dsm_descriptor_count * sizeof(TRIM_PAIR))/sequence->device->logical_block_size;
if ((dsm_descriptor_count * sizeof(TRIM_PAIR)) % sequence->device->logical_block_size)
{
blocks++;
}
return blocks;
}
SATI_STATUS sati_unmap_construct(
SATI_TRANSLATOR_SEQUENCE_T * sequence,
void * scsi_io,
void * ata_io,
U32 sector_count
)
{
U8 * h2d_register_fis = sati_cb_get_h2d_register_fis_address(ata_io);
U8 * d2h_register_fis = sati_cb_get_d2h_register_fis_address(ata_io);
sati_set_ata_command(h2d_register_fis, ATA_DATA_SET_MANAGEMENT);
sati_set_ata_features(h2d_register_fis, 0x01);
sati_set_ata_sector_count(h2d_register_fis, (U8)sector_count);
sati_set_ata_device_head(h2d_register_fis, ATA_DEV_HEAD_REG_LBA_MODE_ENABLE);
sati_set_ata_status(d2h_register_fis, 0x00);
sequence->data_direction = SATI_DATA_DIRECTION_OUT;
sequence->protocol = SAT_PROTOCOL_UDMA_DATA_OUT;
sequence->is_translate_response_required = TRUE;
ASSERT(sector_count < 0x100);
return SATI_SUCCESS;
}
SATI_STATUS sati_unmap_load_next_descriptor(
SATI_TRANSLATOR_SEQUENCE_T * sequence,
void * scsi_io
)
{
SATI_UNMAP_PROCESSING_STATE_T * unmap_process_state;
U32 index;
U8 unmap_block_descriptor[16];
unmap_process_state = &sequence->command_specific_data.unmap_process_state;
for(index = unmap_process_state->current_unmap_block_descriptor_index;
index < unmap_process_state->current_unmap_block_descriptor_index +
SATI_UNMAP_SIZEOF_SCSI_UNMAP_BLOCK_DESCRIPTOR;
index++)
{
sati_get_data_byte(sequence,
scsi_io,
index,
&unmap_block_descriptor[index-unmap_process_state->current_unmap_block_descriptor_index]);
}
unmap_process_state->current_lba_count = (unmap_block_descriptor[8] << 24) |
(unmap_block_descriptor[9] << 16) |
(unmap_block_descriptor[10] << 8) |
(unmap_block_descriptor[11]);
unmap_process_state->current_lba = ((SATI_LBA)(unmap_block_descriptor[0]) << 56) |
((SATI_LBA)(unmap_block_descriptor[1]) << 48) |
((SATI_LBA)(unmap_block_descriptor[2]) << 40) |
((SATI_LBA)(unmap_block_descriptor[3]) << 32) |
((SATI_LBA)(unmap_block_descriptor[4]) << 24) |
((SATI_LBA)(unmap_block_descriptor[5]) << 16) |
((SATI_LBA)(unmap_block_descriptor[6]) << 8) |
((SATI_LBA)(unmap_block_descriptor[7]));
unmap_process_state->next_lba = 0;
unmap_process_state->current_unmap_block_descriptor_index += SATI_UNMAP_SIZEOF_SCSI_UNMAP_BLOCK_DESCRIPTOR;
return SATI_SUCCESS;
}
U32 sati_unmap_get_max_buffer_size_in_blocks(
SATI_TRANSLATOR_SEQUENCE_T * sequence
)
{
return MIN(SATI_DSM_MAX_BUFFER_SIZE/sequence->device->logical_block_size,
sequence->device->max_lba_range_entry_blocks);
}
SATI_STATUS sati_unmap_initial_processing(
SATI_TRANSLATOR_SEQUENCE_T * sequence,
void * scsi_io,
void * ata_io
)
{
SATI_UNMAP_PROCESSING_STATE_T * unmap_process_state;
U8 * cdb;
U16 unmap_length;
U32 descriptor_length;
U32 index;
U32 max_dsm_blocks;
U8 unmap_param_list[8];
unmap_process_state = &sequence->command_specific_data.unmap_process_state;
sequence->type = SATI_SEQUENCE_UNMAP;
if ((sequence->device->capabilities & SATI_DEVICE_CAP_DSM_TRIM_SUPPORT)
!= SATI_DEVICE_CAP_DSM_TRIM_SUPPORT)
{
sati_scsi_sense_data_construct(
sequence,
scsi_io,
SCSI_STATUS_CHECK_CONDITION,
SCSI_SENSE_ILLEGAL_REQUEST,
SCSI_ASC_INVALID_FIELD_IN_CDB,
SCSI_ASCQ_INVALID_FIELD_IN_CDB
);
return SATI_FAILURE_CHECK_RESPONSE_DATA;
}
cdb = sati_cb_get_cdb_address(scsi_io);
unmap_length = (sati_get_cdb_byte(cdb, 7) << 8) | sati_get_cdb_byte(cdb, 8);
if (unmap_length == 0)
{
return SATI_SUCCESS;
}
if (unmap_length < SATI_UNMAP_SIZEOF_SCSI_UNMAP_PARAMETER_LIST)
{
sati_scsi_sense_data_construct(
sequence,
scsi_io,
SCSI_STATUS_CHECK_CONDITION,
SCSI_SENSE_ILLEGAL_REQUEST,
SCSI_ASC_INVALID_FIELD_IN_CDB,
SCSI_ASCQ_INVALID_FIELD_IN_CDB
);
return SATI_FAILURE_CHECK_RESPONSE_DATA;
}
sequence->allocation_length = unmap_length;
for(index = 0; index < SATI_UNMAP_SIZEOF_SCSI_UNMAP_PARAMETER_LIST; index++)
{
sati_get_data_byte(sequence, scsi_io, index, &unmap_param_list[index]);
}
descriptor_length = (unmap_param_list[2] << 8) | unmap_param_list[3];
if (descriptor_length == 0)
{
return SATI_SUCCESS;
}
if ((U32)(unmap_length - SATI_UNMAP_SIZEOF_SCSI_UNMAP_PARAMETER_LIST) < descriptor_length)
{
sati_scsi_sense_data_construct(
sequence,
scsi_io,
SCSI_STATUS_CHECK_CONDITION,
SCSI_SENSE_ILLEGAL_REQUEST,
SCSI_ASC_INVALID_FIELD_IN_CDB,
SCSI_ASCQ_INVALID_FIELD_IN_CDB
);
return SATI_FAILURE_CHECK_RESPONSE_DATA;
}
unmap_process_state->max_unmap_block_descriptors =
descriptor_length/SATI_UNMAP_SIZEOF_SCSI_UNMAP_BLOCK_DESCRIPTOR;
max_dsm_blocks = sati_unmap_get_max_buffer_size_in_blocks(sequence);
unmap_process_state->max_lba_range_entries =
(max_dsm_blocks*sequence->device->logical_block_size)/sizeof(TRIM_PAIR);
sati_cb_allocate_dma_buffer(
scsi_io,
max_dsm_blocks*sequence->device->logical_block_size,
&(unmap_process_state->virtual_unmap_buffer),
&(unmap_process_state->physical_unmap_buffer_low),
&(unmap_process_state->physical_unmap_buffer_high));
if (unmap_process_state->virtual_unmap_buffer == NULL)
{
sati_scsi_sense_data_construct(
sequence,
scsi_io,
SCSI_STATUS_BUSY,
SCSI_SENSE_NO_SENSE,
SCSI_ASC_NO_ADDITIONAL_SENSE,
SCSI_ASCQ_NO_ADDITIONAL_SENSE
);
return SATI_FAILURE_CHECK_RESPONSE_DATA;
}
sati_cb_sgl_next_sge(scsi_io,
ata_io,
NULL,
&(unmap_process_state->unmap_buffer_sgl_pair));
unmap_process_state->current_unmap_block_descriptor_index =
SATI_UNMAP_SIZEOF_SCSI_UNMAP_PARAMETER_LIST;
sati_unmap_load_next_descriptor(sequence,scsi_io);
sequence->state = SATI_SEQUENCE_STATE_INCOMPLETE;
return SATI_COMPLETE;
}
SATI_STATUS sati_unmap_process(
SATI_TRANSLATOR_SEQUENCE_T * sequence,
void * scsi_io,
void * ata_io
)
{
SATI_UNMAP_PROCESSING_STATE_T * unmap_process_state;
SATI_LBA dsm_descriptor_lba_count;
U32 dsm_descriptor;
U32 dsm_bytes;
U32 dsm_remainder_bytes;
U32 dsm_blocks;
U32 max_dsm_blocks;
unmap_process_state = &sequence->command_specific_data.unmap_process_state;
unmap_process_state->current_dsm_descriptor = unmap_process_state->virtual_unmap_buffer;
dsm_descriptor = 0;
while ((dsm_descriptor < unmap_process_state->max_lba_range_entries) &&
(unmap_process_state->current_lba_count > 0)) {
if (unmap_process_state->current_lba_count > SATI_DSM_MAX_SECTOR_COUNT) {
dsm_descriptor_lba_count = SATI_DSM_MAX_SECTOR_COUNT;
unmap_process_state->current_lba_count -= SATI_DSM_MAX_SECTOR_COUNT;
unmap_process_state->next_lba =
unmap_process_state->current_lba + SATI_DSM_MAX_SECTOR_COUNT;
} else {
dsm_descriptor_lba_count = unmap_process_state->current_lba_count;
unmap_process_state->current_lba_count = 0;
}
((PTRIM_PAIR)(unmap_process_state->current_dsm_descriptor))->sector_address =
unmap_process_state->current_lba;
((PTRIM_PAIR)(unmap_process_state->current_dsm_descriptor))->sector_count =
dsm_descriptor_lba_count;
if (unmap_process_state->current_lba_count == 0) {
--unmap_process_state->max_unmap_block_descriptors;
if (unmap_process_state->max_unmap_block_descriptors > 0) {
sati_unmap_load_next_descriptor(sequence,scsi_io);
}
} else {
unmap_process_state->current_lba = unmap_process_state->next_lba;
}
ASSERT(unmap_process_state->current_lba <= SATI_DSM_MAX_SECTOR_ADDRESS);
dsm_descriptor++;
unmap_process_state->current_dsm_descriptor =
(U8 *)(unmap_process_state->current_dsm_descriptor) + sizeof(TRIM_PAIR);
}
dsm_blocks = sati_unmap_calculate_dsm_blocks(sequence,dsm_descriptor);
dsm_bytes = dsm_blocks * sequence->device->logical_block_size;
max_dsm_blocks = sati_unmap_get_max_buffer_size_in_blocks(sequence);
dsm_remainder_bytes = sequence->device->logical_block_size;
dsm_remainder_bytes -= (U32)((POINTER_UINT)unmap_process_state->current_dsm_descriptor &
(sequence->device->logical_block_size-1));
if (dsm_remainder_bytes != sequence->device->logical_block_size)
{
dsm_remainder_bytes += (sequence->device->logical_block_size * (max_dsm_blocks - dsm_blocks));
memset((U8 *)unmap_process_state->current_dsm_descriptor, 0, dsm_remainder_bytes);
}
sati_cb_sge_write(unmap_process_state->unmap_buffer_sgl_pair,
unmap_process_state->physical_unmap_buffer_low,
unmap_process_state->physical_unmap_buffer_high,
dsm_bytes);
sati_unmap_construct(sequence,
scsi_io,
ata_io,
dsm_blocks);
if (unmap_process_state->current_lba_count == 0)
{
sequence->state = SATI_SEQUENCE_STATE_FINAL;
}
return SATI_SUCCESS_SGL_TRANSLATED;
}
void sati_unmap_terminate(
SATI_TRANSLATOR_SEQUENCE_T * sequence,
void * scsi_io,
void * ata_io
)
{
SATI_UNMAP_PROCESSING_STATE_T * unmap_process_state;
unmap_process_state = &sequence->command_specific_data.unmap_process_state;
if (unmap_process_state->virtual_unmap_buffer != NULL)
{
sati_cb_free_dma_buffer(scsi_io, unmap_process_state->virtual_unmap_buffer);
unmap_process_state->virtual_unmap_buffer = NULL;
}
}
SATI_STATUS sati_unmap_translate_command(
SATI_TRANSLATOR_SEQUENCE_T * sequence,
void * scsi_io,
void * ata_io
)
{
SATI_STATUS status = SATI_FAILURE_CHECK_RESPONSE_DATA;
SATI_UNMAP_PROCESSING_STATE_T * unmap_process_state;
unmap_process_state = &sequence->command_specific_data.unmap_process_state;
if ( sequence->state == SATI_SEQUENCE_STATE_INITIAL )
{
status = sati_unmap_initial_processing(sequence,scsi_io,ata_io);
if (status != SATI_COMPLETE)
{
return status;
}
}
return sati_unmap_process(sequence, scsi_io, ata_io);
}
SATI_STATUS sati_unmap_translate_response(
SATI_TRANSLATOR_SEQUENCE_T * sequence,
void * scsi_io,
void * ata_io
)
{
U8 * register_fis = sati_cb_get_d2h_register_fis_address(ata_io);
SATI_UNMAP_PROCESSING_STATE_T * unmap_process_state;
SATI_STATUS sati_status = SATI_COMPLETE;
unmap_process_state = &sequence->command_specific_data.unmap_process_state;
if (sati_get_ata_status(register_fis) & ATA_STATUS_REG_ERROR_BIT)
{
sequence->state = SATI_SEQUENCE_STATE_FINAL;
sati_scsi_sense_data_construct(
sequence,
scsi_io,
SCSI_STATUS_CHECK_CONDITION,
SCSI_SENSE_ABORTED_COMMAND,
SCSI_ASC_NO_ADDITIONAL_SENSE,
SCSI_ASCQ_NO_ADDITIONAL_SENSE
);
sati_unmap_terminate(sequence, scsi_io, ata_io);
}
else
{
if (sequence->state != SATI_SEQUENCE_STATE_INCOMPLETE)
{
sati_unmap_terminate(sequence, scsi_io, ata_io);
}
else
{
sati_status = SATI_SEQUENCE_STATE_INCOMPLETE;
}
}
return sati_status;
}
#endif