unit unihedrondevice;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Process, StrUtils
  {$IFDEF Windows}, Registry{$ENDIF}
    ;

type
FoundDevice = record
  SerialNumber : String;
  Connection : String;
  Hardware : String;
end;

function FindUSB(): TStringList;

var
   FoundDevicesArray: array of FoundDevice;

implementation

function FindUSB(): TStringList;
const
     READ_BYTES = 2048;
var
  OutputLines: TStringList;
  MemStream: TMemoryStream;
  OurProcess: TProcess;
  NumBytes: LongInt;
  BytesRead: LongInt;
  LookForState: Integer;
  USBDeviceSerial,usbdevname,usbserial: String;
  LinuxDeviceFile: String;
  lStrings,udevStrings: TStringList;
  StringPos:Integer;

  pieces: TStringList;

  kAvailable: TStringlist;
  kConnected: TStringlist;
  {$IFDEF Windows}
  i,j,foundindex: Integer;
  reg,reg2: TRegistry;
  Regkey,RegCOMkey,FTDIKey: String;
  keys,keys2: TStringlist;
  {$ENDIF Windows}

  begin

    kConnected := TStringList.Create;
    kAvailable := TStringList.Create;

    {$IFDEF Windows}

    // Find FTDI USB device on a Windows machine
    Regkey:='HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\';
    RegCOMkey:='HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM\\';

    //Form2.Memo1.Clear;//debug

    //Check for all connected serial commuication devices
    keys := TStringList.Create;
    reg := TRegistry.Create;
    reg.RootKey := HKEY_LOCAL_MACHINE;
    if reg.KeyExists('HARDWARE\DEVICEMAP\SERIALCOMM\') then
        begin
          reg.OpenKeyReadOnly('HARDWARE\DEVICEMAP\SERIALCOMM\');
          //StatusBar.SimpleText:='At least one COMM port is active.';
          reg.GetValueNames(keys);
          for i := 0 to keys.Count-1 do
            begin
              //Form2.Memo1.Lines.Add(reg.ReadString(keys.ValueFromIndex[i]));
              kConnected.Add(reg.ReadString(keys.ValueFromIndex[i]));
            end;
          //Form2.Memo1.Lines.AddStrings(keys);
        end;
     //else
     //   StatusBar.SimpleText:='No COMM ports are active.';
     reg.Free;

    // Check all installed FTDI drivers.
    pieces := TStringList.Create;
    pieces.Delimiter := '+';
    keys := TStringList.Create;
    keys2 := TStringList.Create;
    //StatusBar.SimpleText:=(format ('Checking Registry for: %s ...',[Regkey]));
    reg := TRegistry.Create;
    reg.RootKey := HKEY_LOCAL_MACHINE;
    if reg.KeyExists('SYSTEM\CurrentControlSet\Enum\FTDIBUS\') then
       begin
         reg.OpenKeyReadOnly('SYSTEM\CurrentControlSet\Enum\FTDIBUS\');
         //StatusBar.SimpleText:='FTDI driver has been installed at least once.';
         reg.GetKeyNames(keys);
         for i := 0 to keys.Count-1 do
           begin
             pieces.DelimitedText := keys[i];
             reg2 := TRegistry.Create;
             reg2.RootKey := HKEY_LOCAL_MACHINE;
             reg2.OpenKeyReadOnly('SYSTEM\CurrentControlSet\Enum\FTDIBUS\'+keys[i]+'\0000\Device Parameters\');
             //Form2.Memo1.Lines.Add(keys[i]+' - '+reg2.ReadString('PortName'));
             kConnected.Sort;
             if kConnected.Find(reg2.ReadString('PortName'),foundindex) then
                begin
                  //Form2.Memo1.Lines.Add(pieces.Strings[2]+' = '+reg2.ReadString('PortName'));
                  kAvailable.Add(kConnected[foundindex]+' : '+pieces.Strings[2]);
                  //USBPort.AddItem(pieces.Strings[2]+' = '+reg2.ReadString('PortName'));
                end;
             reg2.Free;
           end;
       end;
    //else
    //    StatusBar.SimpleText:='FTDI driver has never been installed.';

    reg.Free;
    keys.Free;
  {$ENDIF Windows}

  {$IFDEF Unix}
  //kAvailable.Add('/dev/ttyUSB1');
  //writeln('Finding linux USB devices;');

  // A temp Memorystream is used to buffer the output
    MemStream := TMemoryStream.Create;
    BytesRead := 0;

    pieces := TStringList.Create;
    pieces.Delimiter := '=';
  OurProcess := TProcess.Create(nil);
  OurProcess.CommandLine := 'udevadm info --export-db';

  // We cannot use poWaitOnExit here since we don't
  // know the size of the output. On Linux the size of the
  // output pipe is 2 kB; if the output data is more, we
  // need to read the data. This isn't possible since we are
  // waiting. So we get a deadlock here if we use poWaitOnExit.
  OurProcess.Options := [poUsePipes];
  OurProcess.Execute;
  while OurProcess.Running do
  begin
    // make sure we have room
    MemStream.SetSize(BytesRead + READ_BYTES);

    // try reading it
    NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
    if NumBytes > 0
    then begin
      Inc(BytesRead, NumBytes);
      //Write('.') //Output progress to screen.
    end
    else begin
      // no data, wait 100 ms
      Sleep(100);
    end;
  end;
  // read last part
  repeat
    // make sure we have room
    MemStream.SetSize(BytesRead + READ_BYTES);
    // try reading it
    NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
    if NumBytes > 0
    then begin
      Inc(BytesRead, NumBytes);
    end;
  until NumBytes <= 0;
    if BytesRead > 0 then
       WriteLn;
    MemStream.SetSize(BytesRead);

    OutputLines := TStringList.Create;
    OutputLines.LoadFromStream(MemStream);
    LookForState:=0;
    USBDeviceSerial:='';
    LinuxDeviceFile:='';

    for NumBytes := 0 to OutputLines.Count - 1 do
    begin

      if ((AnsiStartsStr('P:',OutputLines[NumBytes])) and (AnsiContainsText(OutputLines[NumBytes],'tty/ttyUSB'))) then
         begin
           //writeln(OutputLines[NumBytes]);//debug
           LookForState:=1;
         end;

      if ((LookForState=1) and (AnsiStartsStr('E: DEVNAME=',OutputLines[NumBytes]))) then
         begin
           pieces.DelimitedText := OutputLines[NumBytes];
           usbdevname:=pieces.Strings[2];
           //writeln(usbdevname);
           LookForState:=2;
         end;

      if ((LookForState=2) and (AnsiStartsStr('E: ID_SERIAL_SHORT',OutputLines[NumBytes]))) then
         begin
           pieces.DelimitedText := OutputLines[NumBytes];
           usbserial:=pieces.Strings[2];
           kAvailable.Add(usbdevname+' : '+usbserial);
           //writeln(usbserial);
           LookForState:=0;
         end;

    end;

    //writeln('Done searching.');//debug

    OutputLines.Free;
    OurProcess.Free;
    MemStream.Free;

    {$ENDIF Unix}

    FindUSB:=kAvailable;

  end;

end.

