HIDinterface.bas - common module
'   This module is common to all of the Example programs
'   It declares the {Open, Read, Write, Close} calls for the USB device
'       These user-calls are translated into OS system calls
'   This module also contains several support routines used by all of the examples
'
'   Declare module-wide variables
Private HidHandle As Long

Public Function OpenUSBdevice(NameOfDevice$) As Boolean
' This function searches the system HID tables for NameOfDevice$
' If found then it opens the device and returns TRUE, else it returns FALSE
Dim HidGuid As Guid: Dim Success As Boolean: Dim Openned As Boolean: Dim Buffer(256) As Byte
Dim DeviceInterfaceData As Device_Interface_Data
Dim FunctionClassDeviceData As Device_Interface_Detail
'
'   First, get the HID class identifier
Call HidD_GetHidGuid(HidGuid.Data(0))
'   Get a handle for the Plug and Play node, request currently active HID devices
PnPHandle& = SetupDiGetClassDevs(HidGuid.Data(0), 0, 0, &H12)
If (PnPHandle& = -1) Then ErrorExit ("Could not attach to PnP node")
'
HidEntry& = 0: Openned = False
DeviceInterfaceData.cbsize = 28 'Length of data structure in bytes
'   Look through the table of HID devices
Do While SetupDiEnumDeviceInterfaces(PnPHandle&, 0, HidGuid.Data(0), HidEntry&, DeviceInterfaceData.cbsize)
'   There is a device here, get it's name
    FunctionClassDeviceData.cbsize = 5
    Success = SetupDiGetDeviceInterfaceDetail(PnPHandle&, DeviceInterfaceData.cbsize, _
            FunctionClassDeviceData.cbsize, UBound(FunctionClassDeviceData.DataPath), BytesReturned&, 0)
    If (Success = 0) Then ErrorExit ("Could not get the name of this HID device")
' Convert returned C string to Visual Basic String
    HidName$ = "": i& = 0
    Do While FunctionClassDeviceData.DataPath(i&) <> 0
        HidName$ = HidName$ & Chr$(FunctionClassDeviceData.DataPath(i&)): i& = i& + 1: Loop
' Can now open this HID device
    HidHandle& = CreateFile(HidName$, &HC0000000, 3, 0, 3, 0, 0)
    If (HidHandle = -1) Then ErrorExit ("Could not open HID device")
' Is it OUR HID device?
    If HidD_GetProductString(HidHandle&, AddressFor(Buffer(0)), UBound(Buffer)) Then
        DeviceName$ = "": i& = 0
        Do While Buffer(i&) <> 0: DeviceName$ = DeviceName$ & Chr$(Buffer(i&)): i& = i& + 2: Loop
        If (StrComp(DeviceName$, NameOfDevice$) = 0) Then
            Openned = True: Exit Do: End If
        End If 'HidD_GetProductString
    Call CloseHandle(HidHandle&) ' Was not OUR HID device
    HidEntry& = HidEntry& + 1 ' Check next entry
    Loop 'SetupDiEnumDeviceInterfaces returns FALSE when there are no more entries
OpenUSBdevice = Openned
End Function

Public Sub ReadUSBdevice(BufferPtr&, ByteCount&)
' This subroutine "reads" from an openned USB device
' This routine gets an Input Report from the USB device and returns the data
' NOTE that ReadFile is a BLOCKING system call, ie it will wait for the USB device to respond
' Do not configure the USB device to "Generate report only on change" since the program
' will appear to 'hang'
' Use a local buffer so that the ReportID (=0) at ReportBuffer(0) may be removed
Dim ReportBuffer(256) As Byte
If ByteCount& > 254 Then ErrorExit ("Maximum ByteCount for ReadUSBdevice is 254")
If ByteCount& < 1 Then ErrorExit ("Minimum ByteCount for ReadUSBdevice is 1")
Success = ReadFile(HidHandle&, AddressFor(ReportBuffer(0)), ByteCount& + 1, BytesReturned&, 0)
If (Success = 0) Then ErrorExit ("Could not get an Input Report")
Call CopyBuffer(AddressFor(ReportBuffer(1)), BufferPtr&, BytesReturned& - 1)
End Sub

Public Sub WriteUSBdevice(BufferPtr&, ByteCount&)
' This subroutine "writes" to an openned USB device
' Copy the user buffer into a local buffer so that a ReportID (=0) may be prepended
' The first byte will contain the ReportID (=0)
Dim ReportBuffer(256) As Byte
If ByteCount& > 254 Then ErrorExit ("Maximum ByteCount for WriteUSBdevice is 254")
Call CopyBuffer(BufferPtr&, AddressFor(ReportBuffer(1)), ByteCount&)
ReportBuffer(0) = 0 ' ReportID
Success = WriteFile(HidHandle&, AddressFor(ReportBuffer(0)), ByteCount& + 1, BytesWritten&, 0)
If (Success = 0) Then ErrorExit ("Could not write an Output Report")
End Sub

Public Sub CloseUSBdevice()
' This subroutine closes the USB device that we have been using
Call CloseHandle(HidHandle&)
End Sub

Public Function ReturnHexByte(Text$) As Byte
' Converts the first two characters of text$ into a byte
Dim Value As Byte
Utext$ = UCase(Text$) ' Convert to uppercase for search
HexString$ = "0123456789ABCDEF" ' Non-Hex characters = 0
Value = 0
For i& = 0 To 15
    If Mid(Utext$, 1, 1) = Mid(HexString$, i& + 1, 1) Then Value = Value + (16 * i&)
    If Mid(Utext$, 2, 1) = Mid(HexString$, i& + 1, 1) Then Value = Value + i&
    Next i&
ReturnHexByte = Value
End Function

Public Function TwoHexCharacters$(Value As Byte)
HexString$ = "0123456789ABCDEF"
TwoHexCharacters$ = Mid(HexString$, Int(Value / 16) + 1, 1) & Mid(HexString$, Int(Value And &HF) + 1, 1)
End Function

Public Function TwoDecimalCharacters$(Value As Byte)
DecimalString$ = "0123456789"
Tens& = Int(Value / 10): Units& = Value - (10 * Tens&)
TwoDecimalCharacters$ = Mid(DecimalString$, Tens& + 1, 1) & Mid(DecimalString$, Units& + 1, 1)
End Function

Public Function ThreeDecimalCharacters$(Value As Byte)
h& = Int(Value / 100): t& = Int((Value - (100 * h&)) / 10): u& = Value - (100 * h&) - (10 * t&)
ThreeDecimalCharacters$ = h& & t& & u&
End Function

Public Sub ErrorExit(Reason$)
ErrorCode = GetLastError()
Call MsgBox(Reason$, vbCritical)
Stop
End Sub