how to use hidapi please help

Everything else that doesn't fall into one of the other PB categories.
Deraman
User
User
Posts: 28
Joined: Thu Mar 10, 2016 8:00 am

how to use hidapi please help

Post by Deraman »

Dear anyone can help me in using
https://github.com/signal11/hidapi
https://github.com/libusb/hidapi
thanks
Deraman
User avatar
blueb
Addict
Addict
Posts: 1116
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Re: how to use hidapi please help

Post by blueb »

Your second link is the 'latest' version.
User_Russian did some work on this a while ago and created a module you may find useful.

see: https://www.purebasic.fr/english/viewto ... 58#p320558
- It was too lonely at the top.

System : PB 6.21(x64) and Win 11 Pro (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
User_Russian
Addict
Addict
Posts: 1544
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: how to use hidapi please help

Post by User_Russian »

Deraman
User
User
Posts: 28
Joined: Thu Mar 10, 2016 8:00 am

Re: how to use hidapi please help

Post by Deraman »

Dear I tried both options but was not satisfied that is why asking for this lib.
Thanks
Deraman
infratec
Always Here
Always Here
Posts: 7619
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: how to use hidapi please help

Post by infratec »

I used User_Russians version as base,
I modifed it, because I needed also non blocking read and it was not possible to open different usage pages of one device.

Code: Select all

DeclareModule HID
  
  ;-DeclareModule
  
  Structure HID_CAPS
    Usage.w
    UsagePage.w
    InputReportByteLength.w
    OutputReportByteLength.w
    FeatureReportByteLength.w
    Reserved.w[17]
    NumberLinkCollectionNodes.w
    NumberInputButtonCaps.w
    NumberInputValueCaps.w
    NumberInputDataIndices.w
    NumberOutputButtonCaps.w
    NumberOutputValueCaps.w
    NumberOutputDataIndices.w
    NumberFeatureButtonCaps.w
    NumberFeatureValueCaps.w
    NumberFeatureDataIndices.w
  EndStructure
  
  Structure HID_DeviceInfo
    VendorID.u
    ProductID.u
    VersionNumber.u
    Usage.u
    UsagePage.u
    NumInputBuffers.u
    InputReportByteLength.u
    OutputReportByteLength.u
    FeatureReportByteLength.u
    Manufacturer$
    Product$
    SerialNumber$
    DevicePath$
  EndStructure
  
  NewList DeviceInfoList.HID_DeviceInfo()
  
  Structure HID_Attributes
    VID.u
    PID.u
    VersionNumber.u
  EndStructure
  
  Declare.i Init()
  Declare Close()
  Declare.i OpenDevice(PID.u, VID.u, SerialNumber$="")
  Declare.i OpenDeviceByPath(DevicePath$)
  Declare.i OpenDeviceByPathNonBlocking(DevicePath$)
  Declare CloseDevice(hDevice)
  Declare.i TestDevice(PID.u, VID.u, SerialNumber$="")
  Declare.i TestDeviceByPath(DevicePath$)
  ;Declare DeviceInfo(*Info.HID_DeviceInfo)
  Declare DeviceInfo(List DeviceInfoList.HID_DeviceInfo())
  Declare.l ReadDevice(hDevice, *Buffer, Len)
  Declare.i ReadDeviceNonBlocking(hDevice, *Buffer, Len, *Overlapp.OVERLAPPED)
  Declare.l WriteDevice(hDevice, *Buffer, Len)
  Declare.i WriteDeviceNonBlocking(hDevice, *Buffer, Len, *Overlapp.OVERLAPPED)
  Declare GetFeature(hDevice, *Buffer, Len)
  Declare.i SetFeature(hDevice, *Buffer, Len)
  Declare.i GetInputReport(hDevice, *Buffer, Len)
  Declare.i SetOutputReport(hDevice, *Buffer, Len)
  Declare.i GetCaps(hDevice, *Capabilities.HID_CAPS)
  
  Declare.i GetAttributes(hDevice, *DeviceInfo.HID_Attributes)
  Declare.i GetNumInputBuffers(hDevice)
  Declare.s GetManufacturerString(hDevice)
  Declare.s GetProductString(hDevice)
  Declare.s GetSerialNumberString(hDevice)
  Declare.s GetIndexedString(hDevice, Index)
  
EndDeclareModule




Module HID
  
  EnableExplicit
  
  
  Structure HIDD_ATTRIBUTES
    Size.l
    VendorID.u
    ProductID.u
    VersionNumber.w
  EndStructure
  
  Structure PSP_DEVICE_INTERFACE_DETAIL_DATA
    cbSize.l
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      DevicePath.l
    CompilerElse 
      DevicePath.c
    CompilerEndIf
  EndStructure
  
  CompilerIf Not Defined(SP_DEVICE_INTERFACE_DATA, #PB_Structure)
    Structure SP_DEVICE_INTERFACE_DATA
      cbSize.l
      InterfaceClassGuid.GUID
      Flags.l
      Reserved.l
    EndStructure
  CompilerEndIf
  
  
  Prototype Prototype_HidD_GetHidGuid(*HidGuid.GUID)
  Prototype.i Prototype_HidD_GetAttributes(*HidDeviceObject, *Attributes.HIDD_ATTRIBUTES)
  Prototype.i Prototype_HidD_GetPreparsedData(*HidDeviceObject, *PreparsedData)
  Prototype.i Prototype_HidP_GetCaps(*PreparsedData, *Capabilities.HID_CAPS)
  Prototype.i Prototype_HidD_FreePreparsedData(PreparsedData)
  Prototype.i Prototype_HidD_GetNumInputBuffers(HidHandle.i, *NumInputBuffers)
  Prototype.i Prototype_HidD_GetManufacturerString(HidHandle.i, *Buffer, Len.l)
  Prototype.i Prototype_HidD_GetProductString(HidHandle.i, *Buffer, Len.l)
  Prototype.i Prototype_HidD_GetSerialNumberString(HidHandle.i, *Buffer, Len.l)
  Prototype.i Prototype_HidD_GetIndexedString(HidHandle.i, Index, *Buffer, Len.l)
  Prototype.i Prototype_HidD_GetFeature(HidHandle.i, *Buffer, Len.l)
  Prototype.i Prototype_HidD_SetFeature(HidHandle.i, *Buffer, Len.l)
  Prototype.i Prototype_HidD_GetInputReport(HidHandle.i, *Buffer, Len.l)
  Prototype.i Prototype_HidD_SetOutputReport(HidHandle.i, *Buffer, Len.l)
  
  Prototype.i Prototype_SetupDiEnumDeviceInterfaces(*DeviceInfoSet, DeviceInfoData, *InterfaceClassGuid.GUID, MemberIndex, *DeviceInterfaceData.SP_DEVICE_INTERFACE_DATA)
  Prototype.i Prototype_SetupDiGetDeviceInterfaceDetail(*DeviceInfoSet, *DeviceInterfaceData.SP_DEVICE_INTERFACE_DATA, DeviceInterfaceDetailData, DeviceInterfaceDetailDataSize, *RequiredSize, *DeviceInfoData)
  
  
  Global HidD_GetHidGuid.Prototype_HidD_GetHidGuid
  Global HidD_GetAttributes.Prototype_HidD_GetAttributes
  Global HidD_GetPreparsedData.Prototype_HidD_GetPreparsedData
  Global HidD_FreePreparsedData.Prototype_HidD_FreePreparsedData
  Global HidP_GetCaps.Prototype_HidP_GetCaps
  Global HidD_GetInputReport.Prototype_HidD_GetInputReport
  Global HidD_SetOutputReport.Prototype_HidD_SetOutputReport
  Global HidD_GetFeature.Prototype_HidD_GetFeature
  Global HidD_SetFeature.Prototype_HidD_SetFeature
  Global _HidD_GetNumInputBuffers.Prototype_HidD_GetNumInputBuffers
  Global _HidD_GetManufacturerString.Prototype_HidD_GetManufacturerString
  Global _HidD_GetProductString.Prototype_HidD_GetProductString
  Global _HidD_GetSerialNumberString.Prototype_HidD_GetSerialNumberString
  Global _HidD_GetIndexedString.Prototype_HidD_GetIndexedString
  
  Global SetupDiEnumDeviceInterfaces.Prototype_SetupDiEnumDeviceInterfaces
  Global SetupDiGetDeviceInterfaceDetail.Prototype_SetupDiGetDeviceInterfaceDetail
  
  
  
  
  Define.i HID_Lib, SetupAPI_Lib
  
  
  Procedure.i Init()
    
    Shared HID_Lib, SetupAPI_Lib
    Protected Result.i
    
    
    HID_Lib = OpenLibrary(#PB_Any, "hid.dll")
    If HID_Lib
      
      SetupAPI_Lib = OpenLibrary(#PB_Any, "setupapi.dll")
      If SetupAPI_Lib
        
        HidD_GetHidGuid                 = GetFunction(HID_Lib, "HidD_GetHidGuid")
        HidD_GetAttributes              = GetFunction(HID_Lib, "HidD_GetAttributes")
        HidD_GetPreparsedData           = GetFunction(HID_Lib, "HidD_GetPreparsedData")
        HidD_FreePreparsedData          = GetFunction(HID_Lib, "HidD_FreePreparsedData")
        HidP_GetCaps                    = GetFunction(HID_Lib, "HidP_GetCaps")
        HidD_GetInputReport             = GetFunction(HID_Lib, "HidD_GetInputReport")
        HidD_SetOutputReport            = GetFunction(HID_Lib, "HidD_SetOutputReport")
        HidD_GetFeature                 = GetFunction(HID_Lib, "HidD_GetFeature")
        HidD_SetFeature                 = GetFunction(HID_Lib, "HidD_SetFeature")
        _HidD_GetNumInputBuffers        = GetFunction(HID_Lib, "HidD_GetNumInputBuffers")
        _HidD_GetManufacturerString     = GetFunction(HID_Lib, "HidD_GetManufacturerString")
        _HidD_GetProductString          = GetFunction(HID_Lib, "HidD_GetProductString")
        _HidD_GetSerialNumberString     = GetFunction(HID_Lib, "HidD_GetSerialNumberString")
        _HidD_GetIndexedString          = GetFunction(HID_Lib, "HidD_GetIndexedString")
        
        SetupDiEnumDeviceInterfaces     = GetFunction(SetupAPI_Lib, "SetupDiEnumDeviceInterfaces")
        SetupDiGetDeviceInterfaceDetail = GetFunction(SetupAPI_Lib, "SetupDiGetDeviceInterfaceDetailW")
        
        Result = #True
        
      EndIf
      
    EndIf
    
    ProcedureReturn Result
    
  EndProcedure
  
  
  
  
  Procedure Close()
    
    Shared HID_Lib, SetupAPI_Lib
    
    
    If HID_Lib
      FreeLibrary_(HID_Lib)
      HID_Lib = 0
    EndIf
    
    If SetupAPI_Lib
      FreeLibrary_(SetupAPI_Lib)
      SetupAPI_Lib = 0
    EndIf
    
  EndProcedure
  
  
  
  
  Procedure.i GetNumInputBuffers(hDevice)
    
    Protected NumInputBuffers.i
    
    
    If hDevice And _HidD_GetNumInputBuffers
      _HidD_GetNumInputBuffers(hDevice, @NumInputBuffers)
    EndIf
    
    ProcedureReturn NumInputBuffers
    
  EndProcedure
  
  
  
  
  Procedure.s GetManufacturerString(hDevice)
    
    Protected Result$, *mem
    
    
    If hDevice And _HidD_GetManufacturerString
      *mem = AllocateMemory(256)
      If *mem
        If _HidD_GetManufacturerString(hDevice, *mem, 252)
          Result$ = PeekS(*mem, -1, #PB_Unicode)
        EndIf
        FreeMemory(*mem)
      EndIf
    EndIf
    
    ProcedureReturn Result$
    
  EndProcedure
  
  
  
  
  Procedure.s GetProductString(hDevice)
    
    Protected Result$, *mem
    
    
    If hDevice And _HidD_GetProductString
      *mem = AllocateMemory(256)
      If *mem
        If _HidD_GetProductString(hDevice, *mem, 252)
          Result$ = PeekS(*mem, -1, #PB_Unicode)
        EndIf
        FreeMemory(*mem)
      EndIf
    EndIf
    
    ProcedureReturn Result$
    
  EndProcedure
  
  
  
  
  Procedure.s GetSerialNumberString(hDevice)
    
    Protected Result$, *mem
    
    
    If hDevice And _HidD_GetSerialNumberString
      *mem = AllocateMemory(256)
      If *mem
        If _HidD_GetSerialNumberString(hDevice, *mem, 252)
          Result$ = PeekS(*mem, -1, #PB_Unicode)
        EndIf
        FreeMemory(*mem)
      EndIf
    EndIf
    
    ProcedureReturn Result$
    
  EndProcedure
  
  
  
  
  Procedure.s GetIndexedString(hDevice, Index)
    
    Protected Result$, *mem
    
    
    If hDevice And _HidD_GetIndexedString
      *mem = AllocateMemory(256)
      If *mem
        If _HidD_GetIndexedString(hDevice, Index, *mem, 252)
          Result$ = PeekS(*mem, -1, #PB_Unicode)
        EndIf
        FreeMemory(*mem)
      EndIf
    EndIf
    
    ProcedureReturn Result$
    
  EndProcedure
  
  
  
  Procedure.i Scan_HID_Devices(List InfoList.HID_DeviceInfo())
    
    Protected HidGuid.Guid
    Protected devInfoData.SP_DEVICE_INTERFACE_DATA
    Protected Attributes.HIDD_ATTRIBUTES;, Security.SECURITY_ATTRIBUTES
    Protected *detailData.PSP_DEVICE_INTERFACE_DETAIL_DATA
    Protected Length.l, CurrentIndex.w, hDevInfo
    Protected i, Result, DevicePath$
    Protected Required, hDevice
    
    Protected HIDP_CAPS.HID_CAPS
    
    
    If HidD_GetHidGuid = 0 Or SetupDiEnumDeviceInterfaces = 0 Or SetupDiGetDeviceInterfaceDetail = 0 Or HidD_GetAttributes = 0
      ProcedureReturn 0
    EndIf
    
    devInfoData\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)
    
;     Security\nLength=SizeOf(SECURITY_ATTRIBUTES)
;     Security\bInheritHandle=1
;     Security\lpSecurityDescriptor = 0
    
    HidD_GetHidGuid(@HidGuid)
    
    hDevInfo=SetupDiGetClassDevs_(@HidGuid, 0, 0, #DIGCF_PRESENT|#DIGCF_DEVICEINTERFACE)
    If hDevInfo = 0
      ProcedureReturn 0
    EndIf
    
    ClearList(InfoList())
    
    For i = 0 To 255
    ;Repeat
      
      Result = SetupDiEnumDeviceInterfaces(hDevInfo, 0, @HidGuid, i, @devInfoData)
      If Result
        Result = SetupDiGetDeviceInterfaceDetail(hDevInfo, @devInfoData, 0, 0,@Length, 0)
        *detailData = AllocateMemory(Length)
        *detailData\cbSize = SizeOf(PSP_DEVICE_INTERFACE_DETAIL_DATA)
        Result = SetupDiGetDeviceInterfaceDetail(hDevInfo, @devInfoData, *detailData, Length + 1, @Required, #Null)
        
        DevicePath$ = PeekS(@*detailData\DevicePath)
        FreeMemory(*detailData)
        
        ;hDevice=CreateFile_(@DevicePath$, #GENERIC_READ|#GENERIC_WRITE, #FILE_SHARE_READ|#FILE_SHARE_WRITE, @Security, #OPEN_EXISTING, 0, 0)
        hDevice = CreateFile_(@DevicePath$, #GENERIC_READ|#GENERIC_WRITE, #FILE_SHARE_READ|#FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, #Null, #Null)
        If hDevice <> #INVALID_HANDLE_VALUE
          
          AddElement(InfoList())
          InfoList()\DevicePath$ = DevicePath$
          
          Attributes\Size = SizeOf(HIDD_ATTRIBUTES)
          
          If HidD_GetAttributes(hDevice, @Attributes)
            InfoList()\VendorID        = Attributes\VendorID
            InfoList()\ProductID       = Attributes\ProductID
            InfoList()\VersionNumber   = Attributes\VersionNumber
            InfoList()\Manufacturer$   = GetManufacturerString(hDevice)
            InfoList()\Product$        = GetProductString(hDevice)
            InfoList()\SerialNumber$   = GetSerialNumberString(hDevice)
            InfoList()\NumInputBuffers = GetNumInputBuffers(hDevice)
            
            If GetCaps(hDevice, @HIDP_CAPS)
              InfoList()\Usage                   = HIDP_CAPS\Usage
              InfoList()\UsagePage               = HIDP_CAPS\UsagePage
              InfoList()\InputReportByteLength   = HIDP_CAPS\InputReportByteLength
              InfoList()\OutputReportByteLength  = HIDP_CAPS\OutputReportByteLength
              InfoList()\FeatureReportByteLength = HIDP_CAPS\FeatureReportByteLength
            EndIf
          EndIf
          
          CloseHandle_(hDevice)
        EndIf    
      Else
        Break 
      EndIf
      
      ;Debug i
      
     ; i + 1
     ;Until  GetLastError_() = #ERROR_NO_MORE_ITEMS
    Next i
    
    SetupDiDestroyDeviceInfoList_(hDevInfo)
    
    ProcedureReturn ListSize(InfoList())
    
  EndProcedure
  
  
  
  
  Procedure.i OpenDevice(PID.u, VID.u, SerialNumber$="")
    
    Protected Handle.i
    Protected NewList TmpList.HID_DeviceInfo()
    
    
    If Scan_HID_Devices(TmpList())
      ForEach TmpList()
        If TmpList()\VendorID = VID And TmpList()\ProductID = PID
          If SerialNumber$ <> ""
            If TmpList()\SerialNumber$ = SerialNumber$
              Handle = CreateFile_(@TmpList()\DevicePath$, #GENERIC_READ|#GENERIC_WRITE, #FILE_SHARE_READ|#FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, #Null, #Null)
              Break
            EndIf
          Else
            Handle = CreateFile_(@TmpList()\DevicePath$, #GENERIC_READ|#GENERIC_WRITE, #FILE_SHARE_READ|#FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, #Null, #Null)
            Break
          EndIf
        EndIf
      Next
    EndIf
    
    ProcedureReturn Handle
    
  EndProcedure  
  
  
  
  
  Procedure.i OpenDeviceByPath(DevicePath$)
    ProcedureReturn CreateFile_(@DevicePath$, #GENERIC_READ|#GENERIC_WRITE, #FILE_SHARE_READ|#FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, #Null, #Null)
  EndProcedure
  
  
  
  Procedure.i OpenDeviceByPathNonBlocking(DevicePath$)
    ProcedureReturn CreateFile_(@DevicePath$, #GENERIC_READ|#GENERIC_WRITE, #FILE_SHARE_READ|#FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, #FILE_FLAG_OVERLAPPED, #Null)
  EndProcedure
  
  
  
  
  Procedure.i CloseDevice(hDevice)
    If hDevice
      ProcedureReturn CloseHandle_(hDevice)
    Else
      ProcedureReturn 0
    EndIf
  EndProcedure
  
  
  
  
  Procedure.i TestDevice(PID.u, VID.u, SerialNumber$="")
    
    Protected.i hHid, Result
    
    
    hHid = OpenDevice(PID, VID, SerialNumber$)
    If hHid
      CloseDevice(hHid)
      Result = #True
    EndIf
    
    ProcedureReturn Result
    
  EndProcedure
  
  
  
  Procedure.i TestDeviceByPath(DevicePath$)
    
    Protected.i hHid, Result
    
    
    hHid = OpenDeviceByPath(DevicePath$)
    If hHid
      CloseDevice(hHid)
      Result = #True
    EndIf
    
    ProcedureReturn Result
    
  EndProcedure
  
  
  
  
  Procedure.i DeviceInfo(List InfoList.HID_DeviceInfo())
        
    ClearList(InfoList())
    Scan_HID_Devices(InfoList())
    
    ProcedureReturn ListSize(InfoList())
    
  EndProcedure
  
  
  
  
  Procedure.l ReadDevice(hDevice, *Buffer, Len)
    
    Protected BytesRead.l
    
    
    If hDevice And *Buffer And Len
      ReadFile_(hDevice, *Buffer, Len, @BytesRead, #Null)
    EndIf
    
    ProcedureReturn BytesRead
    
  EndProcedure
  
  
  
  Procedure.i ReadDeviceNonBlocking(hDevice, *Buffer, Len, *Overlapp.OVERLAPPED)
    
    Protected BytesRead.l, Status.i
    
    
    If hDevice And *Buffer And Len
      Status = ReadFile_(hDevice, *Buffer, Len, @BytesRead, *Overlapp)
    EndIf
    
    ProcedureReturn Status
    
  EndProcedure
  
  
  
  
  Procedure.l WriteDevice(hDevice, *Buffer, Len)
    
    Protected BytesWritten.l
    
    
    If hDevice And *Buffer And Len
      WriteFile_(hDevice, *Buffer, Len, @BytesWritten, #Null)
    EndIf
    
    ProcedureReturn BytesWritten
    
  EndProcedure
  
  
  
  
  Procedure.i WriteDeviceNonBlocking(hDevice, *Buffer, Len, *Overlapp.OVERLAPPED)
    
    Protected BytesWritten.l, Status.i
    
    
    If hDevice And *Buffer And Len
      Status = WriteFile_(hDevice, *Buffer, Len, @BytesWritten, *Overlapp)
    EndIf
    
    ProcedureReturn Status
    
  EndProcedure
  
  
  
  
  Procedure.i GetFeature(hDevice, *Buffer, Len)
    If hDevice And *Buffer And Len > 0 And HidD_GetFeature
      ProcedureReturn HidD_GetFeature(hDevice, *Buffer, Len)
    Else
      ProcedureReturn 0
    EndIf
  EndProcedure
  
  
  
  
  Procedure.i SetFeature(hDevice, *Buffer, Len)
    If hDevice And *Buffer And Len > 0 And HidD_SetFeature
      ProcedureReturn HidD_SetFeature(hDevice, *Buffer, Len)
    Else
      ProcedureReturn 0
    EndIf
  EndProcedure
  
  
  
  
  Procedure.i GetInputReport(hDevice.i, *Buffer, Len.i)
    If hDevice And *buffer And Len > 0 And HidD_GetInputReport
      ProcedureReturn HidD_GetInputReport(hDevice, *Buffer, Len)
    Else
      ProcedureReturn 0
    EndIf
  EndProcedure
  
  
  
  
  Procedure.i SetOutputReport(hDevice.i, *Buffer, Len.i)
    If hDevice And *buffer And Len>0 And HidD_SetOutputReport
      ProcedureReturn HidD_SetOutputReport(hDevice, *Buffer, Len)
    Else
      ProcedureReturn 0
    EndIf
  EndProcedure
  
  
  
  Procedure.i GetCaps(hDevice, *Capabilities.HID_CAPS)
    
    Protected.i result, PreparsedData
    
    
    If hDevice And HidD_GetPreparsedData And HidP_GetCaps And HidD_FreePreparsedData
      
      If HidD_GetPreparsedData(hDevice, @PreparsedData)
        HidP_GetCaps(PreparsedData, *Capabilities) 
        HidD_FreePreparsedData(PreparsedData)
        result = #True
      EndIf  
      
    EndIf
    
    ProcedureReturn result
    
  EndProcedure
  
  
  
  
  Procedure.i GetAttributes(hDevice, *DeviceInfo.HID_Attributes)
    
    Protected Info.HIDD_ATTRIBUTES, Result.i
    
    
    If hDevice And *DeviceInfo And HidD_GetAttributes
      Info\Size = SizeOf(HIDD_ATTRIBUTES)
      If HidD_GetAttributes(hDevice, @Info)
        CopyMemory(@Info+OffsetOf(HIDD_ATTRIBUTES\VendorID), *DeviceInfo, SizeOf(HID_Attributes))
        Result = #True
      EndIf
    EndIf
    
    ProcedureReturn Result
    
  EndProcedure
  
EndModule


;-Demo
CompilerIf #PB_Compiler_IsMainFile
  
  CompilerIf Not #PB_Compiler_Thread
    CompilerError "Enable thread save execution in compiler options!"
  CompilerEndIf
  
  EnableExplicit
  
  Enumeration
    #MainWindow
    #DataWindow
  EndEnumeration
  
  Enumeration
    #HID_DeviceList
    #DataInputReport
    #DataReadDevice
    #DataReadDeviceNonBlocking
  EndEnumeration
  
  Enumeration #PB_Event_FirstCustomValue
    #HID_Event_InputReportData
    #HID_Event_ReadDeviceData
    #HID_Event_ReadDeviceNonBlockingData
  EndEnumeration
  
  
  
  Structure ThreadParameter_Structure
    Thread.i
    Mutex.i
    Semaphore.i
    *DeviceInfo.HID::HID_DeviceInfo
    Device.i
    TxLen.i
    Tx.a[10]
    Rx$
    Exit.i
  EndStructure
  
  Import "Kernel32.lib"
    CancelIoEx(hFile.i, *lpOverlapped)
  EndImport
  
  
  Procedure Thread_GetInputReport(*Parameter.ThreadParameter_Structure)
    
    Protected.a Byte
    Protected.i OutLen, i, PostIt
    Protected *InBuffer
    
    
    *Parameter\Device = HID::OpenDeviceByPath(*Parameter\DeviceInfo\DevicePath$)
    If *Parameter\Device
      *InBuffer = AllocateMemory(*Parameter\DeviceInfo\InputReportByteLength)
      If *InBuffer
        Repeat
          
          If TryLockMutex(*Parameter\Mutex)
            If *Parameter\TxLen
              HID::SetOutputReport(*Parameter\Device, @*Parameter\Tx, *Parameter\TxLen)
            EndIf
            UnlockMutex(*Parameter\Mutex)
          EndIf
          
          PokeA(*InBuffer, 0) ; the report which is requested
          If HID::GetInputReport(*Parameter\Device, *InBuffer, MemorySize(*InBuffer))
            *Parameter\Rx$ = ""
            PostIt = #False
            For i = 1 To MemorySize(*InBuffer)
              Byte = PeekA(*InBuffer + i - 1)
              If Byte <> 0
                PostIt = #True
              EndIf
              *Parameter\Rx$ + RSet(Hex(Byte), 2, "0") + " "
            Next i
            If PostIt
              PostEvent(#HID_Event_InputReportData, #DataWindow, 0)
              WaitSemaphore(*Parameter\Semaphore)
            EndIf
          EndIf
        Until *Parameter\Exit
        FreeMemory(*InBuffer)
      EndIf
      HID::CloseDevice(*Parameter\Device)
    EndIf
    
  EndProcedure
  
  
  Procedure Thread_ReadDevice(*Parameter.ThreadParameter_Structure)
    
    Protected.i InLen, i
    Protected *InBuffer
    
    
    *Parameter\Device = HID::OpenDeviceByPath(*Parameter\DeviceInfo\DevicePath$)
    If *Parameter\Device
      *InBuffer = AllocateMemory(*Parameter\DeviceInfo\InputReportByteLength)
      If *InBuffer
        Repeat
          InLen = HID::ReadDevice(*Parameter\Device, *InBuffer, MemorySize(*InBuffer))
          If InLen
            *Parameter\Rx$ = ""
            For i = 1 To InLen
              *Parameter\Rx$ + RSet(Hex(PeekA(*InBuffer + i - 1)), 2, "0") + " "
            Next i
            PostEvent(#HID_Event_ReadDeviceData, #DataWindow, 0)
            WaitSemaphore(*Parameter\Semaphore)
          EndIf
        Until *Parameter\Exit
        FreeMemory(*InBuffer)
      EndIf
      hid::CloseDevice(*Parameter\Device)
    EndIf
    
  EndProcedure
  
  
  Procedure Thread_ReadDeviceNonBlocking(*Parameter.ThreadParameter_Structure)
    
    Protected.i BytesTransferred, Result, i
    Protected *InBuffer
    Protected Overlapp.OVERLAPPED
    
    
    *Parameter\Device = HID::OpenDeviceByPathNonBlocking(*Parameter\DeviceInfo\DevicePath$)
    If *Parameter\Device
      
      *InBuffer = AllocateMemory(*Parameter\DeviceInfo\InputReportByteLength)
      If *InBuffer
        
        Repeat
          
          ;Debug "non blocking"
          
          If TryLockMutex(*Parameter\Mutex)
            If *Parameter\TxLen
              HID::WriteDeviceNonBlocking(*Parameter\Device, @*Parameter\Tx[0], *Parameter\TxLen, @Overlapp)
              ; wait until write is finished
              While GetOverlappedResult_(*Parameter\Device, @Overlapp, @BytesTransferred, #False) = #ERROR_IO_INCOMPLETE
                Delay(5)
              Wend
            EndIf
            UnlockMutex(*Parameter\Mutex)
          EndIf
          
          
          Result = GetOverlappedResult_(*Parameter\Device, @Overlapp, @BytesTransferred, #False)
          If Result <> #ERROR_IO_INCOMPLETE
            If Result
              If BytesTransferred > 0
                *Parameter\Rx$ = ""
                For i = 1 To BytesTransferred
                  *Parameter\Rx$ + RSet(Hex(PeekA(*InBuffer + i - 1)), 2, "0") + " "
                Next i
                PostEvent(#HID_Event_ReadDeviceNonBlockingData, #DataWindow, 0)
                WaitSemaphore(*Parameter\Semaphore)
              EndIf
              HID::ReadDeviceNonBlocking(*Parameter\Device, *InBuffer, MemorySize(*InBuffer), @Overlapp)
            EndIf
          EndIf
          
        Until *Parameter\Exit
        
        FreeMemory(*InBuffer)
      EndIf
      HID::CloseDevice(*Parameter\Device)
    EndIf
    
  EndProcedure
  
  
  
  ;-Main
  Define.i Device, BytesTransferred, Result, TxBytes, i, Event, Exit
  Define Line$
  Define *DeviceInfo
  Define.ThreadParameter_Structure InputReportThread, ReadDeviceThread, ReadDeviceNonBlockingThread
  
  
  
  NewList InfoList.HID::HID_DeviceInfo()
  
  If HID::Init()
    
    If HID::DeviceInfo(InfoList())
      
      OpenWindow(#MainWindow, 0, 0, 1000, 400, "HID Demo", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
      ListIconGadget(#HID_DeviceList, 10, 10, 980, 360, "VID", 40, #PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection)
      AddGadgetColumn(#HID_DeviceList, 1, "PID", 40)
      AddGadgetColumn(#HID_DeviceList, 2, "Manufacturer", 100)
      AddGadgetColumn(#HID_DeviceList, 3, "Product", 150)
      AddGadgetColumn(#HID_DeviceList, 4, "Page", 40)
      AddGadgetColumn(#HID_DeviceList, 5, "In", 40)
      AddGadgetColumn(#HID_DeviceList, 6, "Out", 40)
      AddGadgetColumn(#HID_DeviceList, 7, "Path", 500)
      
      i = 0
      ForEach InfoList()
        Line$ = RSet(Hex(InfoList()\VendorID), 4, "0") + #LF$
        Line$ + RSet(Hex(InfoList()\ProductID), 4, "0") + #LF$
        Line$ + InfoList()\Manufacturer$ + #LF$
        Line$ + InfoList()\Product$ + #LF$
        Line$ + RSet(Hex(InfoList()\UsagePage), 4, "0") + #LF$
        Line$ + Str(InfoList()\InputReportByteLength) + #LF$
        Line$ + Str(InfoList()\OutputReportByteLength) + #LF$
        Line$ + InfoList()\DevicePath$
        AddGadgetItem(#HID_DeviceList, i, Line$)
        SetGadgetItemData(#HID_DeviceList, i, InfoList())
        i + 1
      Next
      
      CreateStatusBar(0, WindowID(#MainWindow))
      AddStatusBarField(120)
      StatusBarText(0, 0, "Found: " + Str(ListSize(InfoList())), #PB_StatusBar_Center)
      
      InputReportThread\Mutex = CreateMutex()
      InputReportThread\Semaphore = CreateSemaphore()
      
      ReadDeviceThread\Semaphore = CreateSemaphore()
      
      ReadDeviceNonBlockingThread\Mutex = CreateMutex()
      ReadDeviceNonBlockingThread\Semaphore = CreateSemaphore()
      
      ;-MainLoop
      Repeat
        Event = WaitWindowEvent()
        
        Select EventWindow()
          Case #MainWindow
            Select Event
              Case #PB_Event_CloseWindow
                Exit = #True
                
              Case #PB_Event_Gadget
                Select EventGadget()
                  Case #HID_DeviceList
                    If EventType() = #PB_EventType_LeftDoubleClick
                      InputReportThread\Exit = #False
                      ReadDeviceThread\Exit = #False
                      ReadDeviceNonBlockingThread\Exit = #False
                      
                      *DeviceInfo = GetGadgetItemData(#HID_DeviceList, GetGadgetState(#HID_DeviceList))
                      InputReportThread\DeviceInfo = *DeviceInfo
                      ReadDeviceThread\DeviceInfo = *DeviceInfo
                      ReadDeviceNonBlockingThread\DeviceInfo = *DeviceInfo
                      
                      OpenWindow(#DataWindow, 0, 0, 640, 200, "HID Demo Data", #PB_Window_SystemMenu|#PB_Window_WindowCentered, WindowID(#MainWindow))
                      ListIconGadget(#DataInputReport, 10, 10, 200, 150, "Data", 170)
                      ListIconGadget(#DataReadDevice, 220, 10, 200, 150, "Data", 170)
                      ListIconGadget(#DataReadDeviceNonBlocking, 430, 10, 200, 150, "Data", 170)
                      
                      InputReportThread\Thread = CreateThread(@Thread_GetInputReport(), @InputReportThread)
                      ReadDeviceThread\Thread = CreateThread(@Thread_ReadDevice(), @ReadDeviceThread)
                      ReadDeviceNonBlockingThread\Thread = CreateThread(@Thread_ReadDeviceNonBlocking(), @ReadDeviceNonBlockingThread)
                    EndIf
                EndSelect
            EndSelect
            
          Case #DataWindow
            Select Event
              Case #HID_Event_InputReportData
                AddGadgetItem(#DataInputReport, 0, InputReportThread\Rx$)
                SignalSemaphore(InputReportThread\Semaphore)
                
              Case #HID_Event_ReadDeviceData
                AddGadgetItem(#DataReadDevice, 0, ReadDeviceThread\Rx$)
                SignalSemaphore(ReadDeviceThread\Semaphore)
                
              Case #HID_Event_ReadDeviceNonBlockingData
                AddGadgetItem(#DataReadDeviceNonBlocking, 0, ReadDeviceNonBlockingThread\Rx$)
                SignalSemaphore(ReadDeviceNonBlockingThread\Semaphore)
                
              Case #PB_Event_CloseWindow
                InputReportThread\Exit = #True
                ReadDeviceThread\Exit = #True
                ReadDeviceNonBlockingThread\Exit = #True
                If IsThread(InputReportThread\Thread)
                  If WaitThread(InputReportThread\Thread, 1000) = 0
                    Debug "Need to kill InputReportThread"
                    KillThread(InputReportThread\Thread)
                  EndIf
                EndIf
                If IsThread(ReadDeviceThread\Thread)
                  CancelIoEx(ReadDeviceThread\Device, #Null)  ; to cancel the blocking read
                  If WaitThread(ReadDeviceThread\Thread, 1000) = 0
                    Debug "Need to kill ReadDeviceThread"
                    KillThread(ReadDeviceThread\Thread)
                  EndIf
                EndIf
                If IsThread(ReadDeviceNonBlockingThread\Thread)
                  If WaitThread(ReadDeviceNonBlockingThread\Thread, 1000) = 0
                    Debug "Need to kill ReadDeviceNonBlockingThread"
                    KillThread(ReadDeviceNonBlockingThread\Thread)
                  EndIf
                EndIf
                CloseWindow(#DataWindow)
                
            EndSelect
            
        EndSelect
        
      Until Exit
      
    Else
      MessageRequester("HID Demo", "No HID device found!")
    EndIf
    HID::Close()
  EndIf
  
CompilerEndIf
But it is windows only.

I also tried the hidapi lib, but they are also still miss something what I need.

I adopted the HID tool of User_Russian to this version, but at the moment I have no access to this code.
I will add a demo at the end of the module.

The demo shows the principal of non blocking read with the possibility to write something to the device.
In a normal program this is inside of a thread, where you 'inject' the buffer to send via the thread parameters.
infratec
Always Here
Always Here
Posts: 7619
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: how to use hidapi please help

Post by infratec »

I also showing a way to cancel a blocking read inside of a child thread.
To avoid a KillThread().
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: how to use hidapi please help

Post by Kwai chang caine »

That works here i see my three Usb DD :D
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Post Reply