  {==========================================================}
  {                                                          }
  {  program SetMac                  Ver 09/01/2002          }
  {                                                          }
  {  Program pro nastaveni MAC adresy karty IOETH01          }
  {                                                          }
  {                                                          }
  {  (C)2001, SofCon, Stresovicka 49, 160 00 Praha 6         }
  {           Petr Cernin                                    }
  {                                                          }
  {==========================================================}


program SetMac;

uses
  NumToStr,
  Dos,
  CoBase,
  CoEthDef,
  CoEth01;


{-------------------------------
{ ParseIoBase
{---
{
{---
}

function ParseIoBase( const S: String; var IoBase: Word ): Boolean;
var
  Code : Word;
begin
  if S[1] <> '$' then
  begin
    { Predpokladame decimalni zapis }
    Val( S, IoBase, Code );
    ParseIoBase := (Code = 0);
  end
  else begin
    { Predpokladame hexadecimalni zapis }

    {!@# tady to neni uplne ciste }

    IoBase := HexStrWord( Copy( S, 2, 4 ) );
    ParseIoBase := True;
  end;
end;


{-------------------------------
{ EthAddrToStr
{---
{
{---
}

{ Konverze struktury TEthMacAddress na retezec }

function MacToStr( const Mac: TEthMacAddress ): String;
var
  Result : String[17];
  I      : Integer;
begin
  Result := '';

  for I := 0 to 4 do
  begin
    Result := Result + ByteStrHex(Mac.Bytes[I]) + '-';
  end;

  Result := Result + ByteStrHex(Mac.Bytes[5]);

  MacToStr := Result;
end;


{-------------------------------
{ StrToEthAddr
{---
{
{---
}

{ Konverze retezce na strukturu TEthAddress, retezec
musi mit format XX-XX-XX-XX-XX-XX, kde - je jedno z ['-', ':', ' ', '']
Pokud konverzi nelze provest, funkce vraci False}

function StrToMac( const S: String; var Mac: TEthMacAddress ): Boolean;
var
  Addr: TEthMacAddress;

  function HexToNibble(Ch: Char): Integer;
  begin
    Ch := UpCase(Ch);
    if Ch in ['0'..'9'] then
      HexToNibble := Ord(Ch) - Ord('0')
    else if Ch in ['A'..'F'] then
    begin
      HexToNibble := Ord(Ch) - Ord('A') + 10;
    end
    else
      HexToNibble := -1;
  end;


  function HexToByte( Idx: Integer; var B: Byte): Boolean;
  var
    Lo, Hi: Integer;
  begin
    HexToByte := False;

    if Length(S) >= Idx + 1 then
    begin
      Hi := HexToNibble( S[Idx] );
      Lo := HexToNibble( S[Idx + 1] );

      if (Hi >= 0) and (Lo >= 0) then
      begin
        B := Hi * 16 + Lo;
        HexToByte := True;
      end
    end;
  end;

var
  BIdx: Integer;
  CIdx: Integer;

begin

  CIdx := 1;
  BIdx := 0;

  while (BIdx < 5) and HexToByte(CIdx, Addr.Bytes[BIdx]) do
  begin
    Inc( BIdx );
    Inc( CIdx, 2 );

    if (Length(S) >= CIdx) and (S[CIdx] in [':', '-', ' ']) then
    begin
      Inc( CIdx, 1 );
    end;
  end;

  if (BIdx = 5) and HexToByte(CIdx, Addr.Bytes[BIdx]) then
  begin
    Mac := Addr;
    StrToMac := True;
  end
  else
    StrToMac := False;

end;


procedure SerialToMac( Serial: Word; var Mac: TEthMacAddress );
var
  Hour, Min, Sec, Temp: Word;
begin
  GetTime( Hour, Min, Sec, Temp );
  Mac.Words[0] := $5502;
  Mac.Words[1] := (Sec div 2) or (Min shl 5) or (Hour shl 11);
  Mac.Words[2] := Serial;
end;

function MacToSerial( const Mac: TEthMacAddress; var Serial: Word ): Boolean;
begin
  if Mac.Words[0] = $5502 then
  begin
    Serial := Mac.Words[2];
    MacToSerial := True;
  end
  else begin
    MacToSerial := False;
  end;
end;


function StrToWord( const S: String; var W: Word ): Boolean;
var
  L    : Longint;
  Code : Word;
begin
  Val( S, L, Code );

  if (L >= 0) and (L <= 65535) then
  begin
    W := L;
    StrToWord := True;
  end
  else
    StrToWord := False;
end;


{-----------------------------------------------------------------------}


procedure SetMacAddress( IoBase: Word; Mac: TEthMacAddress );
var
  Dev     : PCoDevice;
  Status  : TCoStatus;
  TestMac : TEthMacAddress;
begin
  Status := CoCreateDevice( 'ETH01', '', '', nil, Dev );

  if Status = CST_SUCCESS then
  begin

    Status := Dev^.CoSetOption( DEV_CLASS_NIC, COPT_ETH01_IOBASE,
                                @IoBase, SizeOf( IoBase ), 0, nil);

    if Status = CST_SUCCESS then
    begin

      Status := Dev^.CoBind( nil, 0, nil );

      if Status = CST_SUCCESS then
      begin

        Status := Dev^.CoIoctl( DEV_CLASS_NIC, 0, IOCTL_ETH01_SETMACADDRESS,
                                @Mac, SizeOf( Mac ), nil );

        if Status = CST_SUCCESS then
        begin
          Status := Dev^.CoIoctl( DEV_CLASS_NIC, 0, IOCTL_ETH01_STOREMACADDRESS,
                                  nil, 0, nil );

          if Status = CST_SUCCESS then
          begin
            WriteLn( 'MAC address stored' );

            Status := Dev^.CoIoctl( DEV_CLASS_NIC, 0, IOCTL_ETH01_RELOADMACADDRESS,
                                    nil, 0, nil );

            if Status = CST_SUCCESS then
            begin
              Write( 'Checking MAC address...' );

              Status := Dev^.CoIoctl( DEV_CLASS_NIC, 0, IOCTL_NIC_GETMACADDRESS,
                                      @TestMAC, SizeOf(TestMac), nil );

              if MemCmp( @TestMac, @Mac, SizeOf( Mac ) ) then
              begin
                WriteLn( 'OK' );
              end
              else begin
                WriteLn( 'failed' );
              end;

            end
            else begin
              WriteLn( 'CoIoctl [RELOADMACADDRESS] failed. ', CoStatusToStr( Status ) );
            end;

          end
          else begin
            WriteLn( 'CoIoctl [STOREMACADDRESS] failed. ', CoStatusToStr( Status ) );
          end;

        end
        else begin
          WriteLn( 'CoIoctl [SETMACADDRESS] failed. ', CoStatusToStr( Status ) );
        end;

      end
      else begin
        WriteLn( 'CoBind failed. ', CoStatusToStr( Status ) );
      end;

    end
    else begin
      WriteLn( 'CoSetOption [IOBASE] failed. ', CoStatusToStr( Status ) );
    end;

  end
  else
    WriteLn( 'CoCreateDevice failed. ', CoStatusToStr( Status ) );


  CoDeleteDevice( Dev );
end;

procedure PrintMacAddress( IoBase: Word );
var
  Dev    : PCoDevice;
  Status : TCoStatus;
  Mac    : TEthMacAddress;
  Serial : Word;
begin
  Status := CoCreateDevice( 'ETH01', '', '', nil, Dev );

  if Status = CST_SUCCESS then
  begin

    Status := Dev^.CoSetOption( DEV_CLASS_NIC, COPT_ETH01_IOBASE,
                                @IoBase, SizeOf( IoBase ), 0, nil );

    if Status = CST_SUCCESS then
    begin

      Status := Dev^.CoBind( nil, 0, nil );

      if Status = CST_SUCCESS then
      begin

        Status := Dev^.CoIoctl( DEV_CLASS_NIC, 0, IOCTL_NIC_GETMACADDRESS,
                                @MAC, SizeOf( Mac ), nil );

        if Status = CST_SUCCESS then
        begin
          WriteLn( 'MAC Address: ', MacToStr( Mac ) );

          if MacToSerial( Mac, Serial ) then
          begin
            WriteLn( 'Serial number: ', Serial );
          end
          else
            WriteLn( 'Serial number: invalid' );

        end
        else begin
          WriteLn( 'CoIoctl failed. ', CoStatusToStr( Status ) );
        end;

      end
      else begin
        WriteLn( 'CoBind failed. ', CoStatusToStr( Status ) );
      end;

    end
    else begin
      WriteLn( 'CoSetOption [IOBASE] failed. ', CoStatusToStr( Status ) );
    end;

  end
  else
    WriteLn( 'CoCreateDevice failed. ', CoStatusToStr( Status ) );


  CoDeleteDevice( Dev );
end;

procedure PrintHelp;
begin
  WriteLn( ' SETMAC IoBase [Mac]' );
  WriteLn( '   IoBase  .. IOETH01 Base Address' );
  WriteLn( '   Serial  .. Serial number (eg. 0345)' );
  WriteLn;
end;


var
  IoBase     : Word;
  Mac        : TEthMacAddress;
  Serial     : Word;

begin

  if (ParamCount < 1) or (ParamCount > 2)  then
  begin
    PrintHelp;
    Exit;
  end;

  if ParseIoBase( ParamStr(1), IoBase ) then
  begin
    if ParamCount = 2 then
    begin

      if StrToWord( ParamStr(2), Serial) then
      begin
        SerialToMac( Serial, Mac );
        WriteLn( 'MAC from serial number: ', MacToStr( Mac ) );
        SetMacAddress( IoBase, Mac );
      end
      else
        WriteLn( 'Invalid MAC address parameter.' );

    end
    else begin
      PrintMacAddress( IoBase );



    end;

  end
  else
    WriteLn( 'Invalid IoBase parameter.' );

end.