Communication instructions between Windows and custom USB HID device

1. Typical Windows API used

CreateFile

ReadFile

WriteFile

The following functions are the contents of DDK +

HidD_SetFeature

HidD_GetFeature

HidD_SetOutputReport

HidD_GetInputReport

CreateFile is used to open the device;  ReadFile , HidD_GetFeature , HidD_GetInputReport is used for data communication from equipment to host;  WriteFile , HidD_SetFeature , HidD_SetOutputReport is used for data communication from host to device. In view of practical application, the following paper mainly discusses # CreateFile, WriteFile, readfile and HidD_SetFeature has four functions. If you understand these four functions, others can be analogized.

2. Several common mistakes

When using the above {API}, if the operation fails, calling} GetLastError() will get the following common errors:

6: invalid handle

23: data error (cyclic redundancy code check)

87: parameter error

1784: the buffer provided by the user is invalid

3. Function instructions

CreateFile(devDetail->DevicePath,  //Device path
               GENERIC_READ | GENERIC_WRITE,  //Access mode
               FILE_SHARE_READ | FILE_SHARE_WRITE,   //Sharing mode
               NULL,
               OPEN_EXISTING,     //Failure is returned when the file does not exist
               FILE_FLAG_OVERLAPPED,   //Open in overlapping (asynchronous) mode
               NULL);

Here, CreateFile is used to open the HID device, where the device path is obtained through the function SetupDiGetInterfaceDeviceDetail. CreateFile # has the following points to pay attention to:

-Access method: if it is a system exclusive device, such as mouse, keyboard, etc., this parameter should be set to 0, otherwise subsequent function operations will fail (such as HidD_GetAttributes); In other words, you cannot perform any operations on exclusive devices other than query, so the functions you can use are also very limited. Some of the following functions are not necessarily suitable for these devices. By the way, the description of this parameter on MSDN # is listed here:

If this parameter is zero, the application can query file and device attributes without accessing the device. This is useful if an application wants to determine the size of a floppy disk drive and the formats it supports without requiring a floppy in the drive. It can also be used to test for the file's or directory's existence without opening it for read or write access .

-Overlapping (asynchronous) mode: this parameter does not show obvious significance here. It mainly affects the subsequent "WriteFile" and "ReadFile". If the overlap (asynchronous) mode is set here, the overlap (asynchronous) mode should also be used when using WriteFile and ReadFile, and vice versa. This first requires that the last parameter of , WriteFile and ReadFile , cannot be NULL. Otherwise, the error number {87 (parameter error) will be returned. Of course, error 87 does not mean that this parameter is incorrect. More information will be pointed out when describing these two functions. When this parameter is , 0 , it represents the synchronization mode, that is, , WriteFile. The ReadFile operation will not return until the data processing is completed, otherwise it will be blocked inside the function.

ReadFile( hDev, //Device handle, that is, the return value of CreateFile
              recvBuffer, //buffer for receiving data
              IN_REPORT_LEN, //Length of data to be read
              &recvBytes, //Number of bytes of data actually received
              &ol);  //Asynchronous mode

ReadFile , is used to read the input report sent by the , HID , device through interrupt , IN , transmission

1. The call of ReadFile , will not cause any response of the device, that is, the interrupt , IN , transmission between the , HID , device and the host does not deal with ReadFile ,. IN fact, the host will poll the device within the maximum interval (specified by the endpoint descriptor of the device) and issue a request to interrupt the transmission. "Reading" means retrieving data from a "buffer". IN fact, this "buffer" is the buffer IN the "HID" device driver. The size of this , buffer , can be determined by , hidd ,_ To change numinputs. On XP , the default value is , 32 reports.

2. The data object read is the input report, that is, the data passed IN through the interrupt input pipeline. Therefore, if the device does not support interrupt transmission, this function cannot be used to obtain the expected results. IN fact, this situation cannot occur IN # HID # because the protocol indicates that there must be at least one interrupt # endpoint.

3 , IN_REPORT_LEN = represents the length of the data to be read (however, the length of the report can also be obtained in advance through another function (hidd_getprepareddata))

4. About asynchronous mode. As mentioned earlier, the setting of this parameter must correspond to the setting of "CreateFile", otherwise error No. 87 (parameter error) will be returned. If asynchronous mode is not required, this parameter should be set to {NULL. In this case, ReadFile # will wait until the data is read successfully, so it will block the current process of the program.

WriteFile(hDev, //Device handle, that is, the return value of CreateFile
                     reportBuf, //Store the buffer of data to be sent
                     OUT_REPORT_LEN, //Length of data to be sent
                     &sendBytes, //Number of bytes of data actually received
                     &ol); //Asynchronous mode

WriteFile is used to transmit an output report to the HID device

1. Unlike ReadFile , the WriteFile , function is called through the driver, but it will eventually be reflected in the device. That is, after calling WriteFile, the device will receive the request to output the report. If the device uses interrupt {OUT} transmission, WriteFile} will transmit through interrupt {OUT} pipeline; Otherwise, the "SetReport" request will be transmitted through the control pipeline.

2,  OUT_REPORT_LEN ^ represents the length of data to be written (actual data body + report ID of a ^ byte). If it is greater than the length of the actual report, the actual report length is used; If it is less than the actual report length, it will return error No. 1784 (the user provided buffer is invalid).

3. reportBuf [0] must store the ID of the report to be sent, and the ID of the report must indicate the output report, otherwise it will return error No. 87 (parameter error). This situation may be easily ignored by programmers. As a result, I don't know what the error number reflects. There are often posts with similar questions on the Internet. Incidentally, the report types such as input report, input report and feature report are reflected in the report descriptor of # HID # equipment. Examples will be discussed later.

4. About asynchronous mode. As mentioned earlier, the setting of this parameter must correspond to the setting of "CreateFile", otherwise an error of "87" will be returned (parameter error). If asynchronous mode is not required, this parameter should be set to {NULL. In this case, WriteFile # will wait until the data is read successfully, so it will block the current process of the program.

HidD_SetFeature(hDev, //Device handle, that is, the return value of CreateFile +
                     reportBuf,  //Store the buffer of data to be sent
                     FEATURE_REPORT_LEN);  //buffer length

HidD_SetOutputReport(hDev, //Device handle, that is, the return value of CreateFile
                     reportBuf, //Store the buffer of data to be sent
                     OUT_REPORT_LEN); //buffer length

HidD_SetFeature: Send a feature report to the device, HidD_ SetOutputReport sends an output report to the device. Note the following:

1. Similar to "WriteFile", the "Id" of the report to be sent must be specified in "reportBuf [0], which corresponds to the appropriate type. That is, HidD_SetFeature , can only send feature reports, so the report , ID , must be the , ID of the feature report; HidD_SetOutputReport can only send output reports, so the report ID can only be the ID of the output report.

2. The most common error code returned by these two functions is {23 (data error). Including but not limited to the following situations:

-The firmware ID does not match the description of the report.

-The length of the buffer passed in is less than the length of the report described by the firmware.

According to relevant information (Unofficial documents), this error will occur as long as the driver does not respond to the request.

5. Summary of common errors

- HID ReadFile

  - Error Code 6 (handle is invalid)

The handle passed in is invalid

- Error Code 87

It is likely that the asynchronous mode is declared during the "createfile", but it is read synchronously.

- Error Code 1784 (invalid buffer provided by the user):

The "read buffer length" passed in during parameter transfer is inconsistent with the actual report length.

- HID WriteFile

  - Error Code 6 (handle is invalid)

The handle passed in is invalid

- Error Code 87

- the synchronous / asynchronous mode declared when creating file , is different from that passed in when actually calling , WriteFile ,.

- the report ID , is inconsistent with that defined in the firmware (the first byte of buffer , is the report ID)

- Error Code 1784 (the buffer provided by the user is invalid)

The "write buffer length" passed in during parameter transfer is inconsistent with the actual report length.

- HidD_SetFeature

- HidD_SetOutputReport

  - Error Code 1 (incorrect function)

This function is not supported. It is likely that such a report type (input, output, feature) is not defined in the report descriptor of the device

  - Error Code 6 (handle is invalid)

The handle passed in is invalid

- Error Code 23

- the report ID , does not match the one defined in the firmware (the first byte of buffer , is the report ID)

- the length of the buffer passed in is less than the report length defined by the firmware (report body + 1byte, 1byte = report ID)

- according to relevant information (Unofficial documents), this error will occur as long as the driver does not accept the request (no response to the request)

6. Report descriptor and data communication program example

_ ReportDescriptor: / / report descriptor

0x06, 0x00, 0xff / / usage page

0x09, 0x01 / / usage (vendor usage 1)

0xa1, 0x01 / / collection starts

0x85, 0x01 / / report ID(1)

0x09, 0x01 / / usage (vendor usage 1)

0x15, 0x00 / / logical minimum (0)

0x26, 0xff, 0x0 / / logical maximum (255)

0x75, 0x08 / / report size (8)

0x95, 0x07 / / report count (7)

0x81, 0x06 / / input (data, variable, relative value)

0x09, 0x01 / / usage (vendor usage 1)

0x85, 0x03 / / report ID (3)

0xb1, 0x06 / / characteristic (data, variable, relative value)

0x09, 0x01 / / usage (vendor usage 1)

0x85, 0x02 / / report ID (2)

0xb1, 0x06 / / characteristic (data, variable, relative value)

0x09, 0x01 / / usage (vendor usage 1)

0x85, 0x04 / / report ID (4)

0x91, 0x06 / / output (data, variable, relative value)

0xc0 / / combination ends

     

_ReportDescriptor_End:

This report descriptor defines 4 # different reports: input report # 1, feature report # 2, feature report # 3 and output report # 4 (the number represents its report # ID). For simplicity, each report is , 7 , bytes (plus the report ID , is , 8 , bytes). The following is a simple example to describe the general method of communication between PC and USB HID devices.

#define     USB_VID       0xFC0   
#define     USB_PID       0x420   
HANDLE OpenMyHIDDevice(int overlapped);   
void HIDSampleFunc()   
{   
    HANDLE       hDev;   
    BYTE         recvDataBuf[8];   
    BYTE         reportBuf[8];   
    DWORD        bytes;   
    hDev = OpenMyHIDDevice(0);                                // Open the device without overlapping (asynchronous) mode;   
    if (hDev == INVALID_HANDLE_VALUE)   
        return;   
    reportBuf[0] = 4;                                         // The report ID of the output report is 4   
    memset(reportBuf, 0, 8);   
    reportBuf[1] = 1;   
    if (!WriteFile(hDev, reportBuf, 8, &bytes, NULL))         // Write data to device   
         return;   
    ReadFile(hDev, recvDatatBuf, 8, &bytes, NULL);            // Read the data sent by the device to the host   
}   
    
HANDLE OpenMyHIDDevice(int overlapped)   
{   
    HANDLE hidHandle;   
    GUID hidGuid;   
    HidD_GetHidGuid(&hidGuid);   
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&hidGuid,   
                                            NULL,   
                                            NULL,   
                                            (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));   
    if (hDevInfo == INVALID_HANDLE_VALUE)   
    {   
        return INVALID_HANDLE_VALUE;   
    }   
    SP_DEVICE_INTERFACE_DATA devInfoData;   
    devInfoData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);   
    int deviceNo = 0;   
    SetLastError(NO_ERROR);   
    while (GetLastError() != ERROR_NO_MORE_ITEMS)   
    {   
        if (SetupDiEnumInterfaceDevice (hDevInfo,   
                                        0,   
                                       &hidGuid,   
                                       deviceNo,   
                                       &devInfoData))   
        {   
            ULONG  requiredLength = 0;   
            SetupDiGetInterfaceDeviceDetail(hDevInfo,   
                                            &devInfoData,   
                                            NULL,   
                                            0,   
                                            &requiredLength,   
                                             NULL);  
            PSP_INTERFACE_DEVICE_DETAIL_DATA devDetail = (SP_INTERFACE_DEVICE_DETAIL_DATA*) malloc (requiredLength);   
            devDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);   
            if(!SetupDiGetInterfaceDeviceDetail(hDevInfo,   
                                                 &devInfoData,   
                                                 devDetail,   
                                                 requiredLength,   
                                                 NULL,   
                                                 NULL))   
            {   
                free(devDetail);   
                SetupDiDestroyDeviceInfoList(hDevInfo);   
                return INVALID_HANDLE_VALUE;   
            }   
            if (overlapped)   
            {   
                hidHandle = CreateFile(devDetail->DevicePath,   
                                       GENERIC_READ | GENERIC_WRITE,   
                                       FILE_SHARE_READ | FILE_SHARE_WRITE,   
                                       NULL,   
                                       OPEN_EXISTING,           
                                       FILE_FLAG_OVERLAPPED,   
                                       NULL);   
            }   
            else   
            {   
                hidHandle = CreateFile(devDetail->DevicePath,   
                                       GENERIC_READ | GENERIC_WRITE,   
                                       FILE_SHARE_READ | FILE_SHARE_WRITE,   
                                       NULL,   
                                       OPEN_EXISTING,           
                                       0,   
                                       NULL);   
            }   
            free(devDetail);   
            if (hidHandle==INVALID_HANDLE_VALUE)   
            {   
                SetupDiDestroyDeviceInfoList(hDevInfo);   
                free(devDetail);   
                return INVALID_HANDLE_VALUE;   
            }   
            _HIDD_ATTRIBUTES hidAttributes;   
            if(!HidD_GetAttributes(hidHandle, &hidAttributes))   
            {   
                CloseHandle(hidHandle);   
                SetupDiDestroyDeviceInfoList(hDevInfo);   
                return INVALID_HANDLE_VALUE;   
            }
        }
    }
}

Added by rockintyler on Wed, 02 Mar 2022 11:05:08 +0200