uint32_t sata_cmd_dma( SataDev *dev, uint8_t cmd, uint32_t start, uint32_t count, uint8_t *buf ) {
uint8_t ncs = 0; // Number of Command Slots (NCS)
uint32_t slot = 0; // Номер слота в списке команд
uint32_t i;
HBA_CMD_HEADER *cmdheader = 0;
HBA_CMD_TBL *cmdtbl = 0;
FIS_REG_H2D *cmdfis = 0;
// Определим, сколько командных слотов есть
ncs =( ( dev -> abar -> cap ) & 0x00001F00 ) >> 8; // Выделяем количество ncs
printf( "ncs = %d\n\r", ncs );
// Найдем свободный хедер
slot = dev -> abar -> ports[ dev -> portNumber ].sact | dev -> abar -> ports[ dev -> portNumber ].ci;
for( i = 0; i < ncs; ++i ) {
if( ( slot & 1 ) == 0 ) {
break;
}
slot >>= 1;
}
// Проверим, найден ли свободный слот
if( i == ncs ) {
#ifdef DEBUG
printf( "Cannot find free command list entry\n\r" );
#endif
return DEVICE_IS_BUSY;
}
slot = i;
// Слот определили, настраиваем указатели
// Настроимся на наш слот
cmdheader = ( HBA_CMD_HEADER* )dev -> abar -> ports[ dev -> portNumber ].clb;
cmdheader += slot;
// Устанавливаем в нем все необходимые поля
cmdheader -> cfl = sizeof( FIS_REG_H2D ) / sizeof( uint32_t ); // Размер структуры запроса в двойных словах
cmdheader -> w = ( cmd == ATA_CMD_WRITE_DMA_EXT ? 1 : 0 ); // Направление обмена данными 1: H2D, 0: D2H
cmdheader -> prdtl = ( uint16_t )( ( count - 1 ) >> 4 ) + 1; // Формула получения количества записей
// Настраиваем таблицу команд, состоящую из заголовка и записей (0-64535)
cmdtbl = ( HBA_CMD_TBL* )( cmdheader -> ctba );
memset( cmdtbl, 0, sizeof( HBA_CMD_TBL ) + ( cmdheader -> prdtl - 1 ) * sizeof( HBA_PRDT_ENTRY ) );
// Устанавливаем команду в структуру H2D
cmdfis = ( FIS_REG_H2D* )( &cmdtbl -> cfis );
memset( cmdfis, 0, sizeof( FIS_REG_H2D ) );
cmdfis -> fis_type = FIS_TYPE_REG_H2D; // Структура Register FIS - host to device
cmdfis -> cmd_reg = cmd; // Устанавливаем команду
cmdfis -> cmd = 1; // Работаем с командным регистром
// Если это команды с данными, то грузим LBA и буфер
printf( "cmdheader -> prdtl - 1 = %d\n\r", cmdheader -> prdtl - 1 );
if( cmd == ATA_CMD_READ_DMA_EXT || cmd == ATA_CMD_WRITE_DMA_EXT ) {
// Загружаем данные
for( i = 0; i < cmdheader -> prdtl - 1; ++i ) {
cmdtbl -> prdt_entry[ i ].dba = ( uint32_t )buf; // Устанавливаем указатель на буфер
cmdtbl -> prdt_entry[ i ].dbc = 8 * 1024; // Размер блока данных
cmdtbl -> prdt_entry[ i ].i = 1; // И включаем прерывания
buf += 8 * 1024; // Корректируем указатель на буфер
count -= 16; // Корректируем размер оставшихся данных
}
printf( "Count = %d\n\r", count );
// Здесь еще надо обработать последнюю запись
cmdtbl -> prdt_entry[ i ].dba = ( uint32_t )buf; // Устанавливаем указатель на буфер
cmdtbl -> prdt_entry[ i ].dbc = count << 9; // Размер блока данных
cmdtbl -> prdt_entry[ i ].i = 1; // И включаем прерывания
// Загружаем LBA
cmdfis -> lba0 = ( uint8_t )start; // LBA low
cmdfis -> lba1 = ( uint8_t )( start >> 8 ); // LBA mid
cmdfis -> lba2 = ( uint8_t )( start >> 16 ); // LBA high
cmdfis -> count_l = count; // Количество секторов
cmdfis -> device = 1 << 6; // Режим адресации - LBA
}
if( cmd == ATA_CMD_IDENTIFY ) {
cmdtbl -> prdt_entry[ i ].dba = ( uint32_t )buf; // Устанавливаем указатель на буфер
cmdtbl -> prdt_entry[ i ].dbc = count << 9; // Размер блока данных
cmdtbl -> prdt_entry[ i ].i = 1; // И включаем прерывания
// Загружаем LBA
cmdfis -> count_l = count; // Количество секторов
cmdfis -> device = 1 << 6; // Режим адресации - LBA
}
// Ну и собственно выполняем команду
dev -> abar -> ports[ dev -> portNumber ].ci = 1 << slot; // Issue command
// Ждем пока на нашем порту произойдет прерывание
while( 1 ) {
if( dev -> abar -> is & ( 1 << dev -> portNumber ) ) {
printf( "IS.IPS[ %d ] done\n\r", dev -> portNumber );
break;
}
}
// Читаем теперь читаем PxIS
printf( "(is): " );
binary( dev -> abar -> ports[ dev -> portNumber ].is, D );
if( dev -> abar -> ports[ dev -> portNumber ].is != 0x00000001 ) {
printf( "Error while doing operation (serr): " );
binary( dev -> abar -> ports[ dev -> portNumber ].serr, D );
return DEVICE_ERROR;
}
// Очищаем IS
dev -> abar -> ports[ dev -> portNumber ].is = 0;
// Очищаем IS.IPS
dev -> abar -> is &= ( ~( 1 << dev -> portNumber ) );
printf( "IS.IPS cleared\n\r" );
// Дождемся выполнения команды
while( 1 ) {
// Если бит сброшен, то команда выполнена
if( ( dev -> abar -> ports[ dev -> portNumber ].ci & ( 1 << slot ) ) == 0 ) {
break;
}
}
return DONE;
}