Asuro/src/driver/storage/atapi.pas
2025-03-22 10:20:26 +00:00

449 lines
13 KiB
ObjectPascal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2021 Aaron Hance
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
{
Drivers->Storage->ATAPI - ATAPI Driver.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!! CURRENTLY NOT FUNCTIONAL !!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@author(Aaron Hance <ah@aaronhance.me>)
}
unit atapi;
interface
uses
util,
drivertypes,
console,
terminal,
drivermanagement,
vmemorymanager,
lmemorymanager,
storagemanagement,
strings,
tracer,
drivemanager,
storagetypes,
idetypes;
var
test : uint32;
function identify_device(var device : TIDE_Device) : Boolean;
function read_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean;
function write_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean;
function get_device_size(var device : TIDE_Device) : uint32;
procedure ide_irq();
implementation
uses
ide, ata;
procedure ide_irq();
begin
console.writestringln('IDE IRQ Handler');
console.redrawWindows();
end;
//print status of the device, alt status register
procedure print_status(device : TIDE_Device);
begin
get_status(device);
console.writestring('Status: ');
// console.writeintln(uint32(device.status));
console.writestring('Busy: ');
console.writeintln(uint32(device.status.Busy));
console.writestring('Ready: ');
console.writeintln(uint32(device.status.Ready));
console.writestring('Fault: ');
console.writeintln(uint32(device.status.Fault));
console.writestring('Seek: ');
console.writeintln(uint32(device.status.Seek));
console.writestring('DRQ: ');
console.writeintln(uint32(device.status.DRQ));
console.writestring('CORR: ');
console.writeintln(uint32(device.status.CORR));
console.writestring('IDDEX: ');
console.writeintln(uint32(device.status.IDDEX));
console.writestring('ERROR: ');
console.writeintln(uint32(device.status.ERROR));
end;
procedure outb(port : uint16; value : uint8);
begin
util.outb(port, value);
psleep(1);
// get_status(primaryDevices[0]);
end;
{
Identify the device on the IDE bus
@param(device The device to identify)
@returns(@True if the device is identified, @False otherwise)
}
function identify_device(var device : TIDE_Device) : Boolean;
var
i : uint8;
status : TIDE_Status;
buffer : TIdentResponse;
ready : boolean;
begin
select_device(device);
no_interrupt(device.isPrimary);
// outb(ATA_PRIMARY_BASE1, $04);
// outb(ATA_SECONDARY_BASE1, $04);
// sleep(1);
status := get_status(device);
outb(device.base + ATA_REG_SECCOUNT, 0);
outb(device.base + ATA_REG_LBA0, 0);
outb(device.base + ATA_REG_LBA1, 0);
outb(device.base + ATA_REG_LBA2, 0);
psleep(1);
outb(device.base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
status := get_status(device);
if status.ERROR then begin
console.writestringln('Error identifying device, maybe'); //todo
end;
ready := wait_for_device(device, false);
if not ready then begin
console.writestringln('Device not ready in time!');
//teapot time
// BSOD('Device not ready in time!', 'ATA DEVICE NOT READY IN TIME FOR IDENT');
exit(false);
end;
console.writestringln('ATAPI Device identified: ');
for i:=0 to 255 do begin
buffer[i] := inw(device.base + ATA_REG_DATA);
console.writestring('[');
console.writehexpair(buffer[i]);
console.writestring('] ');
if (i mod 4) = 0 then begin
console.writestringln('');
end;
end;
//bits 14 and 15 of word 0
{
00h
01h
Direct-access device
Sequential-access device
02h
03h
Printer device
Processor device
04h
05h
Write-once device
CD-ROM device
06h
07h
Scanner device
Optical memory device
08h
09h
Medium changer device
Communications device
0A-0Bh
0Ch
Reserved for ACS IT8 (Graphic arts pre-press devices)
Array controller device
0Dh
0Eh
Enclosure services device
Reduced block command devices
0Fh
10-1Eh
Optical card reader/writer device
Reserved
1Fh
Unknown or no device type}
//print device type
console.writestring('Device Type: ');
case (buffer[0] shr 8) and $3 of
0: console.writestringln('Direct-access device');
1: console.writestringln('Sequential-access device');
2: console.writestringln('Printer device');
3: console.writestringln('Processor device');
4: console.writestringln('Write-once device');
5: console.writestringln('CD-ROM device');
6: console.writestringln('Scanner device');
7: console.writestringln('Optical memory device');
8: console.writestringln('Medium changer device');
9: console.writestringln('Communications device');
10: console.writestringln('Reserved for ACS IT8 (Graphic arts pre-press devices)');
11: console.writestringln('Array controller device');
12: console.writestringln('Reserved');
13: console.writestringln('Enclosure services device');
14: console.writestringln('Reduced block command devices');
15: console.writestringln('Unknown or no device type');
end;
outb(ATA_PRIMARY_BASE1, $04);
outb(ATA_SECONDARY_BASE1, $04);
sleep(10);
device.info := @buffer;
identify_device := true;
end;
function read_PIO28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean;
var
i, j : uint16;
status : TIDE_Status;
ready : boolean;
packet : array[0..11] of uint8;
begin
push_trace('read_PIO28()');
if not device.isATAPI then begin
console.writestringln('[ATAPI] Device is not an ATAPI device!');
read_PIO28 := false;
exit;
end;
select_device(device);
no_interrupt(device.isPrimary);
// Step 1: Send PACKET command
outb(device.base + ATA_REG_FEATURES, 0); // No special features
outb(device.base + ATA_REG_SECCOUNT, 0); // ATAPI requires this to be 0
outb(device.base + ATA_REG_LBA0, (2048 shr 0) and $FF); // Byte size of transfer (2048 for CDs)
outb(device.base + ATA_REG_LBA1, (2048 shr 8) and $FF);
outb(device.base + ATA_REG_LBA2, 0); // Reserved
outb(device.base + ATA_REG_COMMAND, ATA_CMD_PACKET);
// Step 2: Wait for DRQ=1 (drive wants a command packet)
ready := wait_for_device(device, false);
if not ready then begin
console.writestringln('[ATAPI] Timeout waiting for PACKET DRQ!');
read_PIO28 := false;
exit;
end;
// Step 3: Prepare 12-byte SCSI CDB (Read Sectors Command 0xA8)
packet[0] := $A8; // ATAPI READ (10) command
packet[1] := 0; // Reserved
packet[2] := (lba shr 24) and $FF; // LBA high byte
packet[3] := (lba shr 16) and $FF;
packet[4] := (lba shr 8) and $FF;
packet[5] := (lba shr 0) and $FF;
packet[6] := 0; // Reserved
packet[7] := count; // Number of sectors to read
packet[8] := 0; // Reserved
packet[9] := 0; // Control
packet[10] := 0; // Reserved
packet[11] := 0; // Reserved
// Step 4: Send the command packet
for i := 0 to 5 do
outw(device.base + ATA_REG_DATA, (packet[i*2] or (packet[i*2+1] shl 8)));
// Step 6: Read the data (count * 2048 bytes)
for i := 0 to (count) - 1 do begin
for j := 0 to 1023 do begin
ready := wait_for_device(device, false);
if not ready then begin
console.writestringln('[ATAPI] Timeout waiting for data DRQ!');
read_PIO28 := false;
exit;
end;
buffer[i * 256 + j] := inw(device.base + ATA_REG_DATA);
end;
end;
// Success
read_PIO28 := true;
pop_trace();
end;
function write_pio28(device : TIDE_Device; lba : uint32; count : uint8; buffer : puint16) : boolean;
var
i : uint16;
ii : uint16;
status : TIDE_Status;
ready : boolean;
begin
write_pio28 := false;
end;
function get_device_size(var device: TIDE_Device): uint32;
var
i: uint8;
ready: boolean;
packet: puint16; // Will hold our 10-byte CDB (as 5 words)
response: puint16; // Will hold the 8-byte capacity response
xferCount: uint16;
temp: uInt32;
data: uint16;
begin
get_device_size := 0;
// 1) Select the device (Master/Slave) - presumably your function
select_device(device);
enable_interrupt(device.isPrimary);
// 2) We set "no special features" for ATAPI
outb(device.base + ATA_REG_FEATURES, 0);
// 3) Indicate how many bytes we expect from the drive.
// Usually we place "MaxByteCount" in LBA1:LBA2.
// We need at least 8 bytes for READ CAPACITY(10).
// We'll do 8 => low byte in LBA1, high byte in LBA2.
outb(device.base + ATA_REG_LBA1, $08);
outb(device.base + ATA_REG_LBA2, $00);
// 4) Send the "PACKET" command (0xA0) to start an ATAPI packet transfer
outb(device.base + ATA_REG_COMMAND, ATA_CMD_PACKET);
// 5) Build our 10-byte SCSI "READ CAPACITY(10)" command descriptor block (CDB)
// We'll store it in 5 words = 10 bytes
packet := puint16(kalloc(12)); // 5 * 2 = 10 bytes
memset(uint32(packet), 0, 12);
// READ CAPACITY(10) = opcode 0x25
// The rest can be 0 for a basic capacity read
// We'll store it as little-endian 16-bit words:
// packet[0] = 0x0025 means low byte=0x25 (opcode), high byte=0x00
packet[0] := $0025; // cdb[0..1]
packet[1] := 0; // cdb[2..3]
packet[2] := 0; // cdb[4..5]
packet[3] := 0; // cdb[6..7]
packet[4] := 0; // cdb[8..9]
packet[5] := 0; // cdb[10..11]
// 6) Wait for drive to be ready to receive the CDB
ready := wait_for_device(device, false);
if not ready then
begin
console.writestringln('Device not ready to receive READ CAPACITY(10) CDB.');
exit;
end;
// 7) Write our 5 words (10 bytes) of CDB to the drive
for i := 0 to 5 do
begin
outw(device.base + ATA_REG_DATA, packet[i]);
psleep(1); // short delay
end;
// Free the CDB memory
// kfree(uint32(packet));
// 8) Wait for next phase (the data phase). The device should assert DRQ
// if it has data to send. We'll poll again.
ready := wait_for_device(device, false);
psleep(1);
if not ready then
begin
console.writestringln('Drive not ready after sending the CDB.');
exit;
end;
// 9) The drive indicates how many bytes its about to send in LBA1:LBA2
// xferCount := (inb(device.base + ATA_REG_LBA2) shl 8) or inb(device.base + ATA_REG_LBA1);
// if xferCount < 8 then
// begin
// console.writestringln('Drive is returning less than 8 bytes for capacity!');
// exit;
// end;
// 10) Allocate space for the 8-byte result
response := puint16(kalloc(8));
memset(uint32(response), 0, 8);
// We'll read 4 words = 8 bytes from the data register
for i := 0 to 3 do
begin
ready := wait_for_device(device, false);
if not ready then
begin
console.writestringln('Device got stuck while reading capacity data!');
exit;
end;
psleep(3);
data := inw(device.base + ATA_REG_DATA);
response[i] := data;
psleep(1);
end;
// //fix the order of the bytes of each number
// endiness of device sector count
temp := puint32(response)[0];
puint32(response)[0] := 0;
for i := 0 to 31 do begin
puint32(response)[0] := puint32(response)[0] or ((temp shr i) and 1) shl (31 - i);
end;
//endiness of device sector size
temp := puint32(response)[1];
puint32(response)[1] := 0;
for i := 0 to 31 do begin
puint32(response)[1] := puint32(response)[1] or ((temp shr i) and 1) shl (31 - i);
end;
// 11) Now parse the returned 8 bytes:
// - first 4 bytes = last LBA
// - next 4 bytes = block size
console.writestring('Device sector count: ');
console.writeintln(puint32(response)[0]); // last LBA
console.writebin32ln(puint32(response)[0]);
console.writestring('Device sector size: ');
console.writeintln(puint32(response)[1] ); // block size
console.writebin32ln(puint32(response)[1]);
device.blockSize := puint32(response)[1];
console.writestringln('Device size: ');
console.writeintln(puint32(response)[0] * (puint32(response)[1]) );
// Return the LBA as "size" (some drivers do lastLBA+1).
get_device_size := puint32(response)[0];
end;
end.