Parcourir la source

Huge refactoring. Currently working unreliable

Dimitrios Chr. Ioannidis il y a 8 mois
Parent
commit
da66390f66
2 fichiers modifiés avec 441 ajouts et 331 suppressions
  1. 441 144
      source/usbasp.pas
  2. 0 187
      source/usbasp_threads.pas

+ 441 - 144
source/usbasp.pas

@@ -6,7 +6,7 @@ unit USBasp;
 
   USBasp Class.
 
-  Copyright (C) 2022 - 2025 Dimitrios Chr. Ioannidis.
+  Copyright (C) 2025 Dimitrios Chr. Ioannidis.
     Nephelae - https://www.nephelae.eu
 
   https://www.nephelae.eu/
@@ -32,38 +32,9 @@ interface
 
 uses
   Classes, SysUtils, syncobjs, hidapi,
-  SPSCRingBuffer, USBasp_Threads;
+  SPSCRingBuffer;
 
 const
-  USBASP_SHARED_VID = $16C0;
-  USBASP_SHARED_PID = $05DC;
-
-  USBASP_FUNC_GETCAPABILITIES = 127;
-
-  USBASP_CAP_0_TPI = 1;
-  USBASP_CAP_PDI = 16;
-  USBASP_CAP_2_SNHIDUPDATE = 32;
-  USBASP_CAP_6_UART = 64;
-  USBASP_CAP_7_HID_UART = 128;
-
-  USBASP_NO_CAPS = (-4);
-
-  USBASP_CAP_12MHZ_CLOCK = 0;
-  USBASP_CAP_16MHZ_CLOCK = 1;
-  USBASP_CAP_18MHZ_CLOCK = 2;
-  USBASP_CAP_20MHZ_CLOCK = 3;
-
-  PROG_STATE_IDLE = 0;
-  PROG_STATE_WRITEFLASH = 1;
-  PROG_STATE_READFLASH = 2;
-  PROG_STATE_READEEPROM = 3;
-  PROG_STATE_WRITEEEPROM = 4;
-  PROG_STATE_TPI_READ = 5;
-  PROG_STATE_TPI_WRITE = 6;
-  PROG_STATE_SET_REPORT = 7;
-
-  UART_STATE_ENABLED = 16;
-  UART_STATE_DISABLED = 0;
 
   // USBasp UART Extension
   // https://github.com/dioannidis/usbasp/blob/master/firmware/usbasp.h
@@ -99,41 +70,42 @@ const
 
 type
 
+  TUARTReceive = procedure(Sender: TObject; ASerialData: TByteArray;
+    ASerialDataLength: SizeInt) of object;
+
+  TStatusChange = procedure(Sender: TObject) of object;
+
   { TUSBasp }
 
   TUSBasp = class(TObject)
   private
-    FUART_HIDDevice, FStatus_HIDDevice: PHidDevice;
+    FUART_HIDDevice, FSTATUS_HIDDevice: PHidDevice;
 
-    FUART_HIDDevicePath, FStatus_HIDDevicePath, FSerial, FManufacturer,
-    FProductName, FHidApiVersionString: string;
+    FUART_HIDDevicePath, FSTATUS_HIDDevicePath, FSerial, FManufacturer,
+    FProductName, FHidApiVersionString: unicodestring;
 
     FConnected, FHasHIDUart, FHasStatus, FHasPDI, FHasTPI, FHasSNWrite,
     FUARTOpened: boolean;
     FCrystalOSC: integer;
 
-    FStatus_RxEvent, FStatus_TxEvent, FUART_RxEvent, FUART_TxEvent,
-    FUnpluggedEvent: TEvent;
+    FHIDError: string;
+
+    FUART_RxBuffer, FUART_TxBuffer, FStatus_RxBuffer: TSPSCRingBuffer;
 
-    FHidErrorStr: string;
+    FUART_RxEvent, FUART_StartRxEvent, FUART_TxEvent, FSTATUS_RxEvent,
+    FSTATUS_StartRxEvent: TEvent;
 
-    FUART_RxBuffer, FUART_TxBuffer, FStatus_RxBuffer,
-    FStatus_TxBuffer: TSPSCRingBuffer;
+    FUART_RxThread, FUART_TxThread, FSTATUS_RxThread, FThreadSync: TThread;
 
-    FUART_RxThread: TThread_HID_UART_Rx;
-    FUART_TxThread: TThread_HID_UART_Tx;
-    FStatus_RxThread: TThread_HID_Status_Rx;
-    FStatus_TxThread: TThread_HID_Status_Tx;
+    FOnUARTReceive: TUARTReceive;
+    FOnStatusChange: TStatusChange;
 
     function HIDEnumerate(const ASerialNumber: string = ''): boolean;
+    procedure SetOnStatusChange(AValue: TStatusChange);
+    procedure SetOnUARTReceive(AValue: TUARTReceive);
   protected
-    property ReceiveBuffer: TSPSCRingBuffer read FUART_RxBuffer;
-    property ReceiveEvent: TEvent read FUART_RxEvent;
-    property TransmitBuffer: TSPSCRingBuffer read FUART_TxBuffer;
-    property TransmitEvent: TEvent read FUART_TxEvent;
-    property MonitorBuffer: TSPSCRingBuffer read FStatus_RxBuffer;
-    property MonitorEvent: TEvent read FStatus_RxEvent;
-    property DisconnectEvent: TEvent read FUnpluggedEvent;
+    procedure DoUARTReceive;
+    procedure DoStatusChange;
   public
     constructor Create;
     destructor Destroy; override;
@@ -142,17 +114,251 @@ type
     function Disconnect: boolean;
     function UARTOpen(const ABaudRate, ADataBits, AParity, AStopBits: integer): boolean;
     function UARTClose: boolean;
-    function ChangeSerialNumber(const ASerialNumber: string): integer;
+    function ChangeSerialNumber(const ASerialNumber: string): boolean;
 
     property Connected: boolean read FConnected;
     property UARTOpened: boolean read FUARTOpened;
-    property HidApiVersion: string read FHidApiVersionString;
+    property HidApiVersion: unicodestring read FHidApiVersionString;
+
+    property OnUARTReceive: TUARTReceive read FOnUARTReceive write SetOnUARTReceive;
+    property OnStatusChange: TStatusChange read FOnStatusChange write SetOnStatusChange;
   end;
 
+var
+  UART_IO_Lock: TCriticalSection;
+  STATUS_IO_Lock: TCriticalSection;
+
 implementation
 
+const
+  USBASP_SHARED_VID = $16C0;
+  USBASP_SHARED_PID = $05DC;
+
+  USBASP_FUNC_GETCAPABILITIES = 127;
+
+  USBASP_CAP_0_TPI = 1;
+  USBASP_CAP_PDI = 16;
+  USBASP_CAP_2_SNHIDUPDATE = 32;
+  USBASP_CAP_6_UART = 64;
+  USBASP_CAP_7_HID_UART = 128;
+
+  USBASP_NO_CAPS = (-4);
+
+  USBASP_CAP_12MHZ_CLOCK = 0;
+  USBASP_CAP_16MHZ_CLOCK = 1;
+  USBASP_CAP_18MHZ_CLOCK = 2;
+  USBASP_CAP_20MHZ_CLOCK = 3;
+
+  PROG_STATE_IDLE = 0;
+  PROG_STATE_WRITEFLASH = 1;
+  PROG_STATE_READFLASH = 2;
+  PROG_STATE_READEEPROM = 3;
+  PROG_STATE_WRITEEEPROM = 4;
+  PROG_STATE_TPI_READ = 5;
+  PROG_STATE_TPI_WRITE = 6;
+  PROG_STATE_SET_REPORT = 7;
+
+  UART_STATE_ENABLED = 16;
+  UART_STATE_DISABLED = 0;
+
+type
+  THIDPacket_Rx = array[0..7] of byte;
+  THIDPacket_Tx = array[0..8] of byte;
+
+  { TThreadHID }
+
+  TThreadHID = class(TThread)
+  protected
+    FUSBasp: TUSBasp;
+    FHIDIOLock: TCriticalSection;
+  public
+    constructor Create(const AUSBasp: TUSBasp; const AHIDIOLock: TCriticalSection);
+      reintroduce;
+  end;
+
+  { THIDStatus_Rx }
+
+  THIDStatus_Rx = class(TThreadHID)
+  protected
+    procedure Execute; override;
+  end;
+
+  { THIDUART_Rx }
+
+  THIDUART_Rx = class(TThreadHID)
+    procedure Execute; override;
+  end;
+
+  { THIDUART_Tx }
+
+  THIDUART_Tx = class(TThreadHID)
+  protected
+    procedure Execute; override;
+  end;
+
+{ TThreadHID }
+
+constructor TThreadHID.Create(const AUSBasp: TUSBasp;
+  const AHIDIOLock: TCriticalSection);
+begin
+  FUSBasp := AUSBasp;
+  FHIDIOLock := AHIDIOLock;
+  inherited Create(False);
+end;
+
+{ THIDStatus_Rx }
+
+procedure THIDStatus_Rx.Execute;
+var
+  HidPacket, PrevHidPacket: THIDPacket_Rx;
+  DataCount: SizeInt = 0;
+
+  function HIDPacketEqual(constref AHIDPacket, APrevHIDPacket:
+    THIDPacket_Rx): boolean;
+  var
+    Idx: byte;
+  begin
+    Result := True;
+    for idx := Low(AHidPacket) to High(AHIDPacket) do
+      if APrevHIDPacket[Idx] <> AHIDPacket[Idx] then
+      begin
+        Result := False;
+        Break;
+      end;
+  end;
+
+begin
+  FillChar(HIDPacket, High(THIDPacket_Rx) + 1, 0);
+  PrevHidPacket := HidPacket;
+
+  repeat
+    FUSBasp.FSTATUS_StartRxEvent.WaitFor(INFINITE);
+
+    FHIDIOLock.Acquire;
+    try
+      if Assigned(FUSBasp.FSTATUS_HIDDevice) then
+        DataCount := FUSBasp.FSTATUS_HIDDevice^.ReadTimeout(HIDPacket,
+          High(HIDPacket) + 1, 5)
+      else
+        DataCount := 0;
+    finally
+      FHIDIOLock.Release;
+    end;
+
+    if (DataCount > 0) and (not HIDPacketEqual(HIDPacket, PrevHidPacket)) then
+    begin
+      PrevHidPacket := HidPacket;
+      FUSBasp.FStatus_RxBuffer.Write(HIDPacket, DataCount);
+      FUSBasp.FSTATUS_RxEvent.SetEvent;
+    end;
+  until Terminated;
+end;
+
+{ THIDUART_Rx }
+
+procedure THIDUART_Rx.Execute;
+var
+  HIDPacket: THIDPacket_Rx;
+  SerialDataCount, DataCount: byte;
+begin
+  FillChar(HIDPacket, High(THIDPacket_Rx) + 1, 0);
+
+  repeat
+    FUSBasp.FUART_StartRxEvent.WaitFor(INFINITE);
+
+    FHIDIOLock.Acquire;
+    try
+      if Assigned(FUSBasp.FUART_HIDDevice) then
+        DataCount := FUSBasp.FUART_HIDDevice^.ReadTimeout(HIDPacket,
+          High(THIDPacket_Rx) + 1, 5)
+      else
+        DataCount := 0;
+    finally
+      FHIDIOLock.Release;
+    end;
+
+    if (DataCount > 0) and (HIDPacket[7] > 0) then
+    begin
+      if (HIDPacket[7] > 7) then
+        SerialDataCount := 8
+      else
+        SerialDataCount := HIDPacket[7];
+      if FUSBasp.FUART_RxBuffer.Write(HIDPacket, SerialDataCount) <>
+        SerialDataCount then
+        raise TExceptionClass.Create('Receive Buffer OverRun');
+      FUSBasp.FUART_RxEvent.SetEvent;
+    end;
+
+  until Terminated;
+end;
+
+{ THIDUART_Tx }
+
+procedure THIDUART_Tx.Execute;
+var
+  HIDPacket: THIDPacket_Tx;
+  SerialCountOrDataByte, DataCount: byte;
+begin
+  FillChar(HIDPacket, High(HIDPacket) + 1, 0);
+  repeat
+    if FUSBAsp.FUART_TxBuffer.Empty then
+      FUSBasp.FUART_TxEvent.ResetEvent;
+
+    FUSBasp.FUART_TxEvent.WaitFor(INFINITE);
+
+    DataCount := FUSBAsp.FUART_TxBuffer.Peek(HIDPacket[1], 8);
+    SerialCountOrDataByte := DataCount;
+
+    if (SerialCountOrDataByte = 8) and (HIDPacket[8] = 7) then
+      SerialCountOrDataByte := 7
+    else
+    if SerialCountOrDataByte < 8 then
+      HIDPacket[8] := SerialCountOrDataByte;
+
+    if DataCount > 0 then
+    begin
+      FHIDIOLock.Acquire;
+      try
+        if Assigned(FUSBasp.FUART_HIDDevice) then
+          SerialCountOrDataByte :=
+            FUSBasp.FUART_HIDDevice^.Write(HIDPacket, High(HIDPacket) + 1);
+      finally
+        FHIDIOLock.Release;
+      end;
+      FUSBAsp.FUART_TxBuffer.AdvanceReadIdx(DataCount);
+    end;
+
+  until Terminated;
+end;
+
 type
-  TUSBPacketBuffer = array[0..8] of byte;
+
+  { TThreadSync }
+
+  TThreadSync = class(TThread)
+  private
+    FUSBasp: TUSBasp;
+  protected
+    procedure Execute; override;
+  public
+    constructor Create(const AUSBasp: TUSBasp); reintroduce;
+  end;
+
+procedure TThreadSync.Execute;
+begin
+  repeat
+    if FUSBasp.FUART_RxEvent.WaitFor(3) = wrSignaled then
+      Synchronize(@FUSBasp.DoUARTReceive);
+    if FUSBasp.FSTATUS_RxEvent.WaitFor(3) = wrSignaled then
+      Synchronize(@FUSBasp.DoStatusChange);
+  until Terminated;
+end;
+
+constructor TThreadSync.Create(const AUSBasp: TUSBasp);
+begin
+  FUSBasp := AUSBasp;
+  inherited Create(False);
+end;
 
 { TUSBasp }
 
@@ -162,24 +368,28 @@ begin
   FUARTOpened := False;
   FSerial := '';
 
-  FUART_RxBuffer := TSPSCRingBuffer.Create(1024);
-  FUART_TxBuffer := TSPSCRingBuffer.Create(1024);
-  FStatus_RxBuffer := TSPSCRingBuffer.Create(1024);
-  FStatus_TxBuffer := TSPSCRingBuffer.Create(1024);
+  FUART_RxBuffer := TSPSCRingBuffer.Create(8192);
+  FUART_TxBuffer := TSPSCRingBuffer.Create(8192);
+  FStatus_RxBuffer := TSPSCRingBuffer.Create(8);
 
   Randomize;
-  FUART_RxEvent := TEvent.Create(nil, False, False, 'UARTRX_' + IntToStr(Random(999)));
-  FUART_TxEvent := TEvent.Create(nil, True, False, 'UARTTX_' + IntToStr(Random(999)));
 
-  FStatus_RxEvent := TEvent.Create(nil, False, False, 'STUSRX_' +
-    IntToStr(Random(999)));
-  FStatus_TxEvent := TEvent.Create(nil, True, False, 'STUSTX_' +
+  FUART_StartRxEvent := TEvent.Create(nil, True, False, 'STARTUARTRX' +
     IntToStr(Random(999)));
+  FUART_RxEvent := TEvent.Create(nil, False, False, 'UARTRX' + IntToStr(Random(999)));
+  FUART_TxEvent := TEvent.Create(nil, True, False, 'UARTTX' + IntToStr(Random(999)));
 
-  FUnpluggedEvent := TEvent.Create(nil, False, False, 'UNPLD_' +
+  FSTATUS_RxEvent := TEvent.Create(nil, False, False, 'STATUSRX' +
+    IntToStr(Random(999)));
+  FSTATUS_StartRxEvent := TEvent.Create(nil, True, False, 'STARTSTATUSRX' +
     IntToStr(Random(999)));
 
-  HidInit();
+  FThreadSync := TThreadSync.Create(Self);
+
+  FUART_RxThread := THIDUART_Rx.Create(Self, UART_IO_Lock);
+  FUART_TxThread := THIDUART_Tx.Create(Self, UART_IO_Lock);
+
+  FSTATUS_RxThread := THIDStatus_Rx.Create(Self, STATUS_IO_Lock);
 
 end;
 
@@ -187,63 +397,73 @@ destructor TUSBasp.Destroy;
 begin
   Disconnect;
 
+  FThreadSync.Terminate;
+  FThreadSync.WaitFor;
+  FreeAndNil(FThreadSync);
+
+  FUART_RxThread.Terminate;
+  FUART_StartRxEvent.SetEvent;
+  FUART_RxThread.WaitFor;
+  FreeAndNil(FUART_RxThread);
+
+  FUART_TxThread.Terminate;
+  FUART_TxEvent.SetEvent;
+  FUART_TxThread.WaitFor;
+  FreeAndNil(FUART_TxThread);
+
+  FSTATUS_RxThread.Terminate;
+  FSTATUS_StartRxEvent.SetEvent;
+  FSTATUS_RxThread.WaitFor;
+  FreeAndNil(FSTATUS_RxThread);
+
   FreeAndNil(FUART_RxBuffer);
   FreeAndNil(FUART_TxBuffer);
   FreeAndNil(FStatus_RxBuffer);
-  FreeAndNil(FStatus_TxBuffer);
 
-  FUART_RxEvent.Free;
-  FUART_TxEvent.Free;
-  FStatus_RxEvent.Free;
-  FStatus_TxEvent.Free;
-  FUnpluggedEvent.Free;
+  FreeAndNil(FUART_RxEvent);
+  FreeAndNil(FUART_TxEvent);
+  FreeAndNil(FSTATUS_RxEvent);
 
-  HidExit();
+  FreeAndNil(FUART_StartRxEvent);
+  FreeAndNil(FSTATUS_StartRxEvent);
 
   inherited Destroy;
 end;
 
 function TUSBasp.Connect(const ASerialNumber: string = ''): boolean;
 begin
-  FConnected := False;
   if not FConnected then
   begin
 
+    HidInit();
+
     if not HIDEnumerate(ASerialNumber) then
       Exit;
 
     FUART_HIDDevice := THidDevice.OpenPath(FUART_HIDDevicePath);
-    FHidErrorStr := FUART_HIDDevice^.GetError;
+    FHIDError := THIDDevice.GetNullError(FUART_HIDDevice);
 
-    if FHidErrorStr <> 'Success' then
+    if (FHIDError <> 'Success') then
       Exit;
 
-    FUART_HIDDevice^.SetNonBlocking(0);
-
-    FUART_RxThread := TThread_HID_UART_Rx.Create(
-      FUART_HIDDevice, FUART_RxBuffer, FUART_RxEvent, FUnpluggedEvent);
-    FUART_TxThread := TThread_HID_UART_Tx.Create(
-      FUART_HIDDevice, FUART_TxBuffer, FUART_TxEvent, FUnpluggedEvent);
+    FUART_HIDDevice^.SetNonBlocking(1);
 
     if FHasStatus then
     begin
-      FStatus_HIDDevice := THidDevice.OpenPath(FStatus_HIDDevicePath);
-      FHidErrorStr := FStatus_HIDDevice^.GetError;
+      FSTATUS_HIDDevice := THidDevice.OpenPath(FSTATUS_HIDDevicePath);
+      FHIDError := THIDDevice.GetNullError(FSTATUS_HIDDevice);
 
-      if FHidErrorStr = 'Success' then
+      if (FHIDError = 'Success') then
       begin
-        FStatus_HIDDevice^.SetNonBlocking(0);
-
-        FStatus_RxThread := TThread_HID_Status_Rx.Create(FStatus_HIDDevice,
-          FStatus_RxBuffer, FStatus_RxEvent, FUnpluggedEvent);
-
-        FStatus_TxThread := TThread_HID_Status_Tx.Create(FStatus_HIDDevice,
-          FStatus_TxBuffer, FStatus_TxEvent, FUnpluggedEvent);
+        FSTATUS_HIDDevice^.SetNonBlocking(1);
+        FSTATUS_StartRxEvent.SetEvent;
       end;
+
     end;
 
     FConnected := True;
   end;
+
   Result := FConnected;
 end;
 
@@ -251,57 +471,68 @@ function TUSBasp.Disconnect: boolean;
 begin
   if FConnected then
   begin
+
     if FUARTOpened then
+    begin
       UARTClose;
+      Sleep(100);
+    end;
 
-    FUART_RxThread.Terminate;
-    FUART_RxThread.WaitFor;
-    FreeAndNil(FUART_RxThread);
-
-    FUART_TxThread.Terminate;
-    FUART_TxEvent.SetEvent;
-    FUART_TxThread.WaitFor;
-    FreeAndNil(FUART_TxThread);
-
-    FUART_HIDDevice^.Close;
-    FUART_HIDDevice := nil;
+    UART_IO_Lock.Acquire;
+    try
+      FUART_HIDDevice^.Close;
+      FUART_HIDDevice := nil;
+    finally
+      UART_IO_Lock.Release;
+    end;
 
     if FHasStatus then
     begin
-      FStatus_RxThread.Terminate;
-      FStatus_RxThread.WaitFor;
-      FreeAndNil(FStatus_RxThread);
-
-      FStatus_TxThread.Terminate;
-      FStatus_TxEvent.SetEvent;
-      FStatus_TxThread.WaitFor;
-      FreeAndNil(FStatus_TxThread);
-
-      FStatus_HIDDevice^.Close;
-      FStatus_HIDDevice := nil;
+      STATUS_IO_Lock.Acquire;
+      try
+        FSTATUS_HIDDevice^.Close;
+        FSTATUS_HIDDevice := nil;
+        FSTATUS_StartRxEvent.ResetEvent;
+      finally
+        STATUS_IO_Lock.Release;
+      end;
     end;
 
     FConnected := False;
+
+    HidExit;
+
   end;
-  Result := FConnected;
+
+  Result := not FConnected;
 end;
 
 function TUSBasp.UARTOpen(const ABaudRate, ADataBits, AParity,
   AStopBits: integer): boolean;
 var
-  Buffer: TUSBPacketBuffer;
+  HIDPacket: THIDPacket_Tx;
   Prescaler: word;
+  Rslt: SizeInt;
 begin
   if FConnected and not FUARTOpened then
   begin
     Prescaler := FCrystalOsc div 8 div ABaudRate - 1;
 
-    Buffer[0] := 0;
-    Buffer[1] := lo(Prescaler);
-    Buffer[2] := hi(Prescaler);
-    Buffer[3] := ADataBits or AStopBits or AParity;
+    Fillchar(HIDPacket, High(HIDPacket) + 1, 0);
+    HIDPacket[0] := 0;
+    HIDPacket[1] := lo(Prescaler);
+    HIDPacket[2] := hi(Prescaler);
+    HIDPacket[3] := ADataBits or AStopBits or AParity;
+
+    UART_IO_Lock.Acquire;
+    try
+      Rslt := FUART_HIDDevice^.SendFeatureReport(HIDPacket, High(HIDPacket) + 1);
+      FUART_StartRxEvent.SetEvent;
+    finally
+      UART_IO_Lock.Release;
+    end;
 
-    if FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1) > 0 then
+    if Rslt > 0 then
       FUARTOpened := True;
   end;
 
@@ -310,37 +541,49 @@ end;
 
 function TUSBasp.UARTClose: boolean;
 var
-  Buffer: TUSBPacketBuffer;
+  HIDPacket: THIDPacket_Tx;
+  Rslt: SizeInt;
 begin
   if FConnected and FUARTOpened then
   begin
+    Fillchar(HIDPacket, High(HIDPacket) + 1, 0);
+    HIDPacket[0] := 0;
+    HIDPacket[1] := 0;
+    HIDPacket[2] := 0;
+    HIDPacket[3] := 0;
+    HIDPacket[4] := 0;
+
+    UART_IO_Lock.Acquire;
+    try
+      Rslt := FUART_HIDDevice^.SendFeatureReport(HIDPacket, High(HIDPacket) + 1);
+      FUART_StartRxEvent.ResetEvent;
+    finally
+      UART_IO_Lock.Release;
+    end;
 
-    Buffer[0] := 0;
-    Buffer[1] := 0;
-    Buffer[2] := 0;
-    Buffer[3] := 0;
-
-    FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1);
-
-    FUARTOpened := False;
+    if Rslt > 0 then
+      FUARTOpened := False;
   end;
 
-  Result := FUARTOpened;
+  Result := not FUARTOpened;
 end;
 
 function TUSBasp.HIDEnumerate(const ASerialNumber: string): boolean;
 var
   HidItemRoot, HidItem: PHidDeviceInfo;
+  HIDDevice: PHidDevice;
   HIDDeviceMatch: boolean;
-  FeatureReportBuffer: TUSBPacketBuffer;
+  FeatureReportBuffer: THIDPacket_Rx;
 begin
   Result := False;
 
+  FUART_HIDDevice := nil;
+  FSTATUS_HIDDevice := nil;
+
   FUART_HIDDevicePath := '';
-  FStatus_HIDDevicePath := '';
+  FSTATUS_HIDDevicePath := '';
 
-  FUART_HIDDevice := nil;
-  FStatus_HIDDevice := nil;
+  FHasStatus := False;
 
   try
     HidItemRoot := THidDeviceInfo.Enumerate(USBASP_SHARED_VID, USBASP_SHARED_PID);
@@ -361,7 +604,7 @@ begin
             Result := True;
           end;
           2: begin
-            FStatus_HIDDevicePath := HidItem^.Path;
+            FSTATUS_HIDDevicePath := HidItem^.Path;
             FHasStatus := True;
           end;
         end;
@@ -374,13 +617,13 @@ begin
 
   if Result then
   begin
-    FUART_HIDDevice := THidDevice.OpenPath(FUART_HIDDevicePath);
+    HIDDevice := THidDevice.OpenPath(FUART_HIDDevicePath);
 
-    FSerial := FUART_HIDDevice^.GetSerialNumberString;
-    FManufacturer := FUART_HIDDevice^.GetManufacturerString;
-    FProductName := FUART_HIDDevice^.GetProductString;
+    FSerial := HIDDevice^.GetSerialNumberString;
+    FManufacturer := HIDDevice^.GetManufacturerString;
+    FProductName := HIDDevice^.GetProductString;
 
-    if FUART_HIDDevice^.GetFeatureReport(FeatureReportBuffer, 8 + 1) > 0 then
+    if HIDDevice^.GetFeatureReport(FeatureReportBuffer, 8 + 1) > 0 then
     begin
 
       case FeatureReportBuffer[6] of
@@ -399,18 +642,62 @@ begin
 
     end;
 
-    FUART_HIDDevice^.Close;
-    FUART_HIDDevice := nil;
+    HIDDevice^.Close;
+    HIDDevice := nil;
+
   end;
 end;
 
-function TUSBasp.ChangeSerialNumber(const ASerialNumber: string): integer;
+procedure TUSBasp.DoUARTReceive;
 var
-  Buffer: TUSBPacketBuffer;
+  SerialData: TByteArray;
+  SerialDataLength, CRIdx: SizeInt;
+begin
+  repeat
+    SerialDataLength := FUART_RxBuffer.Peek(SerialData, FUART_RxBuffer.Size);
+
+    CRIdx := IndexByte(SerialData, SerialDataLength, Ord(#10));
+    if (CRIdx > 0) and (SerialData[CRIdx - 1] = Ord(#13)) then
+      FUART_RxBuffer.AdvanceReadIdx(CRIdx + 1);
+
+    if Assigned(FOnUARTReceive) and (CRIdx - 1 > 0) then
+      FOnUARTReceive(Self, SerialData, CRIdx - 1);
+  until CRIdx = -1;
+
+  //SerialDataLength := FUART_RxBuffer.Read(SerialData, FUART_RxBuffer.Size);
+  //if Assigned(FOnUARTReceive) then
+  //  FOnUARTReceive(Self, SerialData, SerialDataLength);
+
+end;
+
+procedure TUSBasp.SetOnUARTReceive(AValue: TUARTReceive);
+begin
+  if FOnUARTReceive = AValue then Exit;
+  FOnUARTReceive := AValue;
+end;
+
+procedure TUSBasp.DoStatusChange;
+var
+  StatusBuffer: TByteArray;
+begin
+  FStatus_RxBuffer.Read(StatusBuffer, FStatus_RxBuffer.Size);
+  if Assigned(FOnStatusChange) then
+    FOnStatusChange(Self);
+end;
+
+procedure TUSBasp.SetOnStatusChange(AValue: TStatusChange);
+begin
+  if FOnStatusChange = AValue then Exit;
+  FOnStatusChange := AValue;
+end;
+
+function TUSBasp.ChangeSerialNumber(const ASerialNumber: string): boolean;
+var
+  Buffer: THIDPacket_Rx;
   ErrorCode: integer;
   SerNumValue: word;
 begin
-  Result := 0;
+  Result := False;
   if FConnected and not FUARTOpened then
   begin
     Val(ASerialNumber, SerNumValue, ErrorCode);
@@ -422,7 +709,17 @@ begin
     Buffer[4] := 1;
 
     FUART_HIDDevice^.SendFeatureReport(Buffer, 8 + 1);
+    FHIDError := THidDevice.GetNullError();
+    //Result := FUART_HIDDevice^.GetError = 'Success';
   end;
 end;
 
+initialization
+  UART_IO_Lock := TCriticalSection.Create;
+  STATUS_IO_Lock := TCriticalSection.Create;
+
+finalization
+  UART_IO_Lock.Free;
+  STATUS_IO_Lock.Free;
+
 end.

+ 0 - 187
source/usbasp_threads.pas

@@ -1,187 +0,0 @@
-unit USBasp_Threads;
-
-{
-
-  This file is part of
-    Nephelae USBasp HID UART.
-
-  USB HID Read / Write threads.
-
-  Copyright (C) 2022 - 2025 Dimitrios Chr. Ioannidis.
-    Nephelae - https://www.nephelae.eu
-
-  https://www.nephelae.eu/
-
-  Licensed under the MIT License (MIT).
-  See licence file in root directory.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
-  ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-  TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-  SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-  ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-  OTHER DEALINGS IN THE SOFTWARE.
-
-}
-
-{$mode ObjFPC}{$H+}
-
-interface
-
-uses
-  Classes, SysUtils, syncobjs, hidapi,
-  SPSCRingBuffer;
-
-type
-
-  { TThreadHID_Base }
-
-  TThreadHID_Base = class(TThread)
-  protected
-    FThreadEvent: TEvent;
-    FThreadUnpluggedEvent: TEvent;
-    FBuffer: TSPSCRingBuffer;
-    FHIDDevice: PHidDevice;
-  public
-    constructor Create(const AHIDDevice: PHidDevice;
-      const ABuffer: TSPSCRingBuffer; const AThreadEvent: TEvent;
-      const AUnpluggedEvent: TEvent); reintroduce;
-  end;
-
-
-  { TThread_HID_Status_Rx }
-
-  TThread_HID_Status_Rx = class(TThreadHID_Base)
-  protected
-    procedure Execute; override;
-  end;
-
-  { TThread_HID_Status_Tx }
-
-  TThread_HID_Status_Tx = class(TThreadHID_Base)
-  protected
-    procedure Execute; override;
-  end;
-
-  { TThread_HID_UART_Rx }
-
-  TThread_HID_UART_Rx = class(TThreadHID_Base)
-    procedure Execute; override;
-  end;
-
-  { TThread_HID_UART_Tx }
-
-  TThread_HID_UART_Tx = class(TThreadHID_Base)
-  protected
-    procedure Execute; override;
-  end;
-
-implementation
-
-{ TThreadHID_Base }
-
-constructor TThreadHID_Base.Create(const AHIDDevice: PHidDevice;
-  const ABuffer: TSPSCRingBuffer; const AThreadEvent: TEvent;
-  const AUnpluggedEvent: TEvent);
-begin
-  inherited Create(False);
-  FThreadEvent := AThreadEvent;
-  FThreadUnpluggedEvent := AUnpluggedEvent;
-  FBuffer := ABuffer;
-  FHIDDevice := AHIDDevice;
-end;
-
-{ TThread_HID_Status_Rx }
-
-procedure TThread_HID_Status_Rx.Execute;
-var
-  USBAspHidPacket: array[0..7] of byte = (0, 0, 0, 0, 0, 0, 0, 0);
-  DataCount: SizeInt = 0;
-begin
-  repeat
-    DataCount := FHIDDevice^.Read(USBAspHidPacket, 8);
-    if DataCount > 0 then
-    begin
-      FBuffer.Write(USBAspHidPacket, DataCount);
-      FThreadEvent.SetEvent;
-    end;
-  until Terminated;
-end;
-
-{ TThread_HID_Status_Tx }
-
-procedure TThread_HID_Status_Tx.Execute;
-var
-  USBAspHidPacket: array[0..8] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0);
-  DataCount: byte = 0;
-begin
-  repeat
-    if FBuffer.Empty then
-      FThreadEvent.ResetEvent;
-    FThreadEvent.WaitFor(INFINITE);
-
-    FillChar(USBAspHidPacket, 8, 0);
-    DataCount := FBuffer.Peek(USBAspHidPacket[1], 8);
-    if DataCount > 0 then
-    begin
-      FHIDDevice^.Write(USBAspHidPacket, 8 + 1);
-      FBuffer.AdvanceReadIdx(DataCount);
-    end;
-  until Terminated;
-end;
-
-{ TThread_HID_UART_Rx }
-
-procedure TThread_HID_UART_Rx.Execute;
-var
-  USBAspHidPacket: array[0..7] of byte = (0, 0, 0, 0, 0, 0, 0, 0);
-  SerialDataCount, DataCount: byte;
-begin
-  repeat
-    DataCount := FHIDDevice^.Read(USBAspHidPacket, 8);
-    if (DataCount > 0) and (USBAspHidPacket[7] > 0) then
-    begin
-      if (USBAspHidPacket[7] > 7) then
-        SerialDataCount := 8
-      else
-        SerialDataCount := USBAspHidPacket[7];
-      if FBuffer.Write(USBAspHidPacket, SerialDataCount) <> SerialDataCount then
-        raise TExceptionClass.Create('Buffer OverRun');
-      FThreadEvent.SetEvent;
-    end;
-  until Terminated;
-end;
-
-{ TThread_HID_UART_Tx }
-
-procedure TThread_HID_UART_Tx.Execute;
-var
-  USBAspHidPacket: array[0..8] of byte = (0, 0, 0, 0, 0, 0, 0, 0, 0);
-  SerialCountOrDataByte, DataCount: byte;
-begin
-  repeat
-    if FBuffer.Empty then
-      FThreadEvent.ResetEvent;
-    FThreadEvent.WaitFor(INFINITE);
-
-    DataCount := FBuffer.Peek(USBAspHidPacket[1], 8);
-    SerialCountOrDataByte := DataCount;
-    if (SerialCountOrDataByte = 8) and (USBAspHidPacket[8] = 7) then
-      SerialCountOrDataByte := 7
-    else
-    if SerialCountOrDataByte < 8 then
-      USBAspHidPacket[8] := SerialCountOrDataByte;
-
-    if DataCount > 0 then
-    begin
-      SerialCountOrDataByte :=
-        FHIDDevice^.Write(USBAspHidPacket, 8 + 1);
-      FBuffer.AdvanceReadIdx(DataCount);
-    end;
-  until Terminated;
-end;
-
-end.