{ ************************************************
  * Asuro
  * Unit: Drivers/AHCI
  * Description: AHCI SATA Driver
  ************************************************
  * Author: Aaron Hance
  * Contributors: 
  ************************************************ }

unit AHCI;

interface

uses 
    util,
    PCI,
    drivertypes,
    drivermanagement,
    lmemorymanager,
    console,
    vmemorymanager;

type

//Struct hell

    TFIS_Type = (
        REG_H2D = $27,
        REG_D2H = $34,
        DMA_ACT = $39,
        DMA_SETUP = $41,
        DATA = $46,
        BIST = $58,
        PIO_SETUP = $5F,
        DEV_BITS = $A0
    );

    PFIS_REG_H2D = ^TFIS_REG_H2D;
    TFIS_REG_H2D = bitpacked record
        fis_type     : uint8; 
        port_mult    : UBit4;
        rsv0         : UBit3;
        coc          : boolean;
        command      : uint8;
        feature_low  : uint8;
        lba0         : uint8;
        lba1         : uint8;
        lba2         : uint8;
        device       : uint8;
        lba3         : uint8;
        lba4         : uint8;
        lba5         : uint8;
        feature_high : uint8;
        count_low    : uint8;
        count_high   : uint8;
        icc          : uint8;
        control      : uint8;
        rsvl         : uint32;
    end;         
    
    TFIS_REG_D2H = bitpacked record
        fis_type     : uint8; 
        port_mult    : UBit4;
        rsv0         : UBit2;
        i            : boolean;
        rsvl         : boolean;
        status       : uint8;
        error        : uint8;
        lba0         : uint8;
        lba1         : uint8;
        lba2         : uint8;
        device       : uint8;
        lba3         : uint8;
        lba4         : uint8;
        lba5         : uint8;
        rsv2         : uint8;
        count_low    : uint8;
        count_high   : uint8;
        rsv3         : uint16;
        rsv4         : uint32;
    end;

    TFIS_Data = bitpacked record
        fis_type  : uint8;
        port_mult : UBit4;
        rsv0      : UBit4;
        rsv1      : uint16;
        data      : ^uint32;
    end;

    TFIS_PIO_Setup = bitpacked record
        fis_type : uint8;
        pmport   : UBit4;
        rsv0     : boolean;
        d        : boolean;
        i        : boolean;
        rsv1     : boolean;
        status   : uint8;
        error    : uint8;
        lba0     : uint8;
        lba1     : uint8;
        lba2     : uint8;
        device   : uint8;
        lba3     : uint8;
        lba4     : uint8;
        lba5     : uint8;
        rsv2     : uint8;
        countl   : uint8;
        counth   : uint8;
        rsv3     : uint8;
        e_status : uint8;
        tc       : uint16;
        rsv4     : uint16;
    end;

    // TFIS_DMA_Setup = bitpacked record
    // end;

    // THBA_Memory = bitpacked record
    // end;

    // THBA_Port = bitpacked record
    // end;

    // THBA_FIS = bitpacked record
    // end;

    PHBA_PORT = ^THBA_PORT;
    THBA_PORT = bitpacked record
        clb : uint32;
        clbu : uint32;
        fb : uint32;
        fbu : uint32;
        istat : uint32;
        ie : uint32;
        cmd : uint32;
        rsv0 : uint32;
        tfd : uint32;
        sig : uint32;
        ssts : uint32;
        sctl : uint32;
        serr : uint32;
        sact : uint32;
        ci : uint32;
        sntf : uint32;
        fbs : uint32;
        rsv1 : array[0..11] of uint32;
        vendor : array[0..4] of uint32;
    end; 

    THBA_MEM = bitpacked record 
        cap                 : uint32; //0
        global_host_control : uint32; //4
        interrupt_status    : uint32; //8
        port_implemented    : uint32; //c
        version             : uint32; //10
        ccc_control         : uint32; //14
        ccc_ports           : uint32; //18
        em_location         : uint32; //1c
        em_Control          : uint32; //20
        hcap2               : uint32; //24
        bohc                : uint32; //28
        rsv0                : array[0..210] of boolean;
        ports               : array[0..31] of THBA_Port;
    end;

    PHBA = ^THBA_MEM;

    PCMDHeader = ^ TCommand_Header;
    TCommand_Header = bitpacked record
        cfl    : ubit5;
        a      : boolean;
        w      : boolean;
        p      : boolean;
        r      : boolean;
        b      : boolean;
        c      : boolean;
        rsv0   : boolean;
        pmp    : ubit4;
        PRDTL  : uint16;
        PRDTBC : uint32;
        CTBA   : uint32;
        CTBAU  : uint32;
        rsv1   : array[0..3] of uint32;
    end;

    TPRD_Entry = bitpacked record
        data_base_address   : uint32;
        data_bade_address_U : uint32;
        rsv0                : uint32;
        data_byte_count     : ubit22;
        rsv1                : ubit9;
        interrupt_oc        : boolean;
    end;

    PCommand_Table = ^TCommand_Table;
    TCommand_Table = bitpacked record
        cfis : array[0..64] of uint8;
        acmd : array[0..16] of uint8;
        rsv  : array[0..48] of uint8;
        prdt : array[0..7] of TPRD_Entry;
    end;

    TSataDevice = record
        controller : uint8;
        port : uint8;
    end;

var
    //constants
    //SATA_SIG_ATA   := $101;
    //SATA_SIG_ATAPI := $EB140101;
    //STA_SIG_SEMB  := $C33C0101;
    //STAT_SIG_PM    := $96690101;
    AHCI_BASE: uint32 = $400000; //irrelivent

    //other 
    ahciControllers     : array[0.16] of PuInt32;
    ahciControllerCount : uint8 = 0;
    hba                 : array[0..16] of PHBA;

    sataDevices     : array[0..127] of TSataDevice;
    sataDeviceCount : uint8;

    

procedure init();
procedure check_ports(controller : uint8);
procedure enable_cmd(port : uint8);
procedure disable_cmd(port : uint8);
procedure port_rebase(port : uint8);
function load(ptr:void): boolean;
function read(port : uint8; startl : uint32; starth : uint32; count : uint32; buf : PuInt32) : boolean;
function write(port : uint8; startl : uint32; starth : uint32; count : uint32; buf : PuInt32) : boolean;
function find_cmd_slot(port : uint8) : uint32;

implementation 

procedure init();
var
    devID : TDeviceIdentifier;
begin
    console.writestringln('AHCI: Registering driver');
    devID.bus:= biPCI;
    devID.id0:= idANY;
    devID.id1:= $00000001;
    devID.id2:= $00000006;
    devID.id3:= $00000001;
    devID.id4:= idANY;
    devID.ex:= nil;
    drivermanagement.register_driver('AHCI Driver', @devID, @load);
end;

procedure load(ptr : void);
begin
    console.writestringln('AHCI: initilizing a new controller');
    ahciControllers[ahciControllerCount] := ptr;
    hba[ahciControllerCount] := PPCI_Device(ptr)^.address5;
    new_page_at_address(hba[ahciControllerCount]);

    //here would be any controller setup needed

    check_ports(ahciControllerCount);
    ahciControllerCount += 1;
    exit(true);
end;

procedure check_ports(controller : uint8);
var
    d : uint32 = 1;
    i : uint32;
begin
    for i:=0 to 31 do begin
        if (d > 0) and (hba[controller]^.port_implemented shr i) = 1 then begin
            if (hba[controller]^.ports[i].ssts shr 8) <> 1 and (hba[controller]^.ports[i].ssts and $0F) <> 3 then begin
                if hba[controller]^.ports[i].sig = 1 then begin
                    //device is sata
                    sataDevices[sataDeviceCount].controller := controller;
                    sataDevices[sataDeviceCount].port := i;
                    sataDeviceCount += 1;
                end;
                //TODO implement ATAPI
            end;
        end;
    end;
end;