251 lines
8.6 KiB
C
251 lines
8.6 KiB
C
/* Benjamin DELPY `gentilkiwi`
|
|
https://blog.gentilkiwi.com
|
|
benjamin@gentilkiwi.com
|
|
Licence : https://creativecommons.org/licenses/by/4.0/
|
|
*/
|
|
#include "kull_m_rdm.h"
|
|
|
|
BOOL rdm_get_version(HANDLE hFile, PSTR *version)
|
|
{
|
|
BOOL status = FALSE;
|
|
BYTE *data, szData;
|
|
|
|
if(rdm_send_receive(hFile, RDM_IOCTL_GET_VERSION, NULL, 0, &data, &szData))
|
|
{
|
|
if(data)
|
|
{
|
|
if(*version = (PSTR) LocalAlloc(LPTR, szData + 1))
|
|
{
|
|
RtlCopyMemory(*version, data, szData);
|
|
status = TRUE;
|
|
}
|
|
LocalFree(data);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
BOOL rdm_send_receive(HANDLE hFile, BYTE ctl, LPCVOID in, BYTE szIn, LPBYTE *out, BYTE *szOut)
|
|
{
|
|
BOOL status = FALSE;
|
|
BYTE i, crc, data[0x100] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 6 + szIn, 0x00, 0xaa, 0x00 /*addr*/, 1 + szIn, ctl, /* data[..], crc, 0xbb */};
|
|
USHORT bigSize;
|
|
LPCWCHAR error;
|
|
|
|
if(szIn <= 242)
|
|
{
|
|
if(szIn)
|
|
RtlCopyMemory(data + 12, in, szIn);
|
|
for(i = 0, crc = 0; i < (szIn + 2); i++)
|
|
crc ^= data[10 + i];
|
|
data[12 + szIn] = crc;
|
|
data[12 + szIn + 1] = 0xbb;
|
|
kprintf(L">> "); kull_m_string_wprintf_hex(data, 14 + szIn, 1); kprintf(L"\n");
|
|
Sleep(RDM_SLEEP_BEFORE_SEND);
|
|
if(HidD_SetFeature(hFile, data, sizeof(data)))
|
|
{
|
|
Sleep(RDM_SLEEP_BEFORE_RECV);
|
|
if(HidD_GetFeature(hFile, data, sizeof(data)))
|
|
{
|
|
if(data[0] == 0x03)
|
|
{
|
|
bigSize = *(PUSHORT) (data + 6);
|
|
if((bigSize >= 6) && (bigSize <= (242 + 6)))
|
|
{
|
|
kprintf(L"<< "); kull_m_string_wprintf_hex(data, bigSize + 8, 1); kprintf(L"\n");
|
|
szIn = (BYTE) (bigSize - 6);
|
|
if(data[8] == 0xaa)
|
|
{
|
|
if((data[10] - 1) == szIn)
|
|
{
|
|
for(i = 0, crc = 0; i < (szIn + 2); i++)
|
|
crc ^= data[10 + i];
|
|
if(crc == data[12 + szIn])
|
|
{
|
|
if(data[12 + szIn + 1] = 0xbb)
|
|
{
|
|
switch(data[11])
|
|
{
|
|
case 0x00: // command OK
|
|
case 0xff:
|
|
status = TRUE;
|
|
if(out && szOut)
|
|
{
|
|
*out = NULL;
|
|
*szOut = 0;
|
|
if(szIn)
|
|
{
|
|
if(*out = (PBYTE) LocalAlloc(LPTR, szIn))
|
|
{
|
|
RtlCopyMemory(*out, data + 12, szIn);
|
|
*szOut = szIn;
|
|
}
|
|
else status = FALSE;
|
|
}
|
|
}
|
|
error = NULL;
|
|
break;
|
|
case 0x01: // command failure
|
|
if(szIn == 1)
|
|
{
|
|
switch(data[12])
|
|
{
|
|
case 0x80: error = L"set OK"; break;
|
|
case 0x81: error = L"set failure"; break;
|
|
case 0x82: error = L"reader reply timeout"; break;
|
|
case 0x83: error = L"card does not exist"; break;
|
|
case 0x84: error = L"data response from the card"; break;
|
|
case 0x85: error = L"format or parameter of the command"; break;
|
|
case 0x87: error = L"unknown internal error"; break;
|
|
case 0x8a: error = L"initVal process"; break;
|
|
case 0x8b: error = L"wrong SNR during anticollision"; break;
|
|
case 0x8c: error = L"authentication failure"; break;
|
|
case 0x8f: error = L"reader received unknown command"; break;
|
|
case 0x90: error = L"card does not support this command"; break;
|
|
case 0x91: error = L"format of the command"; break;
|
|
case 0x92: error = L"does not support option mode"; break;
|
|
case 0x93: error = L"block does not exist"; break;
|
|
case 0x94: error = L"object has been locked"; break;
|
|
case 0x95: error = L"lock operation did not succeed"; break;
|
|
case 0x96: error = L"operation did not succeed"; break;
|
|
default: L"unknow error code!";
|
|
}
|
|
PRINT_ERROR(L"Command failure: 0x%02x - %s\n", data[12], error);
|
|
}
|
|
else PRINT_ERROR(L"data size is not 1 when dealing when an error status (%hhu)\n", szIn);
|
|
error = NULL;
|
|
break;
|
|
case 0x02: error = L"checksum"; break;
|
|
case 0x03: error = L"not COM port selected"; break;
|
|
case 0x04: error = L"timeout reply"; break;
|
|
case 0x05: error = L"check sequence"; break;
|
|
case 0x07: error = L"receive"; break;
|
|
case 0x0a: error = L"parameter value out of range"; break;
|
|
default: error = L"unknow error code!";
|
|
}
|
|
if(error)
|
|
PRINT_ERROR(L"Status failure: 0x%02x - %s\n", data[11], error);
|
|
}
|
|
else PRINT_ERROR(L"Bad China footer (0x%02x)\n", data[12 + szIn + 1]);
|
|
}
|
|
else PRINT_ERROR(L"Bad CRC (0x%02x -- 0x%02x)\n", crc, data[12 + szIn]);
|
|
}
|
|
else PRINT_ERROR(L"Bad HID size vs Data size (0x%02x -- 0x%02x)\n", szIn, data[10] - 1);
|
|
}
|
|
else PRINT_ERROR(L"Bad China header (0x%02x)\n", data[8]);
|
|
}
|
|
else PRINT_ERROR(L"Bad HID size (%hhu)\n", bigSize);
|
|
}
|
|
else PRINT_ERROR(L"Bad HID header (0x%02x)\n", data[0]);
|
|
}
|
|
else PRINT_ERROR_AUTO(L"HidD_GetFeature");
|
|
}
|
|
else PRINT_ERROR_AUTO(L"HidD_SetFeature");
|
|
}
|
|
else PRINT_ERROR(L"Size >= 242 (%hhu)\n", szIn);
|
|
return status;
|
|
}
|
|
|
|
BOOL rdm_devices_get(PRDM_DEVICE *devices, DWORD *count)
|
|
{
|
|
PRDM_DEVICE *next = devices;
|
|
GUID guidHid;
|
|
HDEVINFO hDevInfo;
|
|
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
|
|
BOOL enumStatus;
|
|
DWORD enumIndex, dwRequired, id = 0;
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData;
|
|
HANDLE deviceHandle;
|
|
HIDD_ATTRIBUTES attributes;
|
|
PHIDP_PREPARSED_DATA PreparsedData;
|
|
NTSTATUS status;
|
|
|
|
*next = NULL;
|
|
HidD_GetHidGuid(&guidHid);
|
|
hDevInfo = SetupDiGetClassDevs(&guidHid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
|
if(hDevInfo != INVALID_HANDLE_VALUE)
|
|
{
|
|
for(enumIndex = 0, enumStatus = TRUE; enumStatus; enumIndex++)
|
|
{
|
|
DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|
if(enumStatus = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &guidHid, enumIndex, &DeviceInterfaceData))
|
|
{
|
|
dwRequired = 0;
|
|
if(!SetupDiGetDeviceInterfaceDetail(hDevInfo, &DeviceInterfaceData, NULL, 0, &dwRequired, NULL) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
|
|
{
|
|
if(DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalAlloc(LPTR, dwRequired))
|
|
{
|
|
DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
if(SetupDiGetDeviceInterfaceDetail(hDevInfo, &DeviceInterfaceData, DeviceInterfaceDetailData, dwRequired, &dwRequired, NULL))
|
|
{
|
|
deviceHandle = CreateFile(DeviceInterfaceDetailData->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
|
if(deviceHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
attributes.Size = sizeof(HIDD_ATTRIBUTES);
|
|
if(HidD_GetAttributes(deviceHandle, &attributes))
|
|
{
|
|
if((attributes.VendorID == 0xffff) && (attributes.ProductID == 0x0035))
|
|
{
|
|
if(*next = (PRDM_DEVICE) LocalAlloc(LPTR, sizeof(RDM_DEVICE)))
|
|
{
|
|
if(HidD_GetPreparsedData(deviceHandle, &PreparsedData))
|
|
{
|
|
status = HidP_GetCaps(PreparsedData, &(*next)->hidCaps);
|
|
if(!NT_SUCCESS(status))
|
|
PRINT_ERROR(L"HidP_GetCaps (%08x)\n", status);
|
|
HidD_FreePreparsedData(PreparsedData);
|
|
}
|
|
(*next)->DevicePath = _wcsdup(DeviceInterfaceDetailData->DevicePath);
|
|
(*next)->hidAttributes = attributes;
|
|
(*next)->id = id;
|
|
(*next)->hDevice = CreateFile(DeviceInterfaceDetailData->DevicePath, FILE_READ_DATA | FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
|
|
|
if((*next)->hDevice && ((*next)->hDevice != INVALID_HANDLE_VALUE))
|
|
{
|
|
next = &(*next)->next;
|
|
id++;
|
|
}
|
|
else
|
|
{
|
|
if(GetLastError() != ERROR_ACCESS_DENIED)
|
|
PRINT_ERROR_AUTO(L"CreateFile (hDevice)");
|
|
*next = (PRDM_DEVICE) LocalFree(*next);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CloseHandle(deviceHandle);
|
|
}
|
|
else PRINT_ERROR_AUTO(L"CreateFile (deviceHandle)");
|
|
}
|
|
LocalFree(DeviceInterfaceDetailData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SetupDiDestroyDeviceInfoList(hDevInfo);
|
|
}
|
|
else PRINT_ERROR_AUTO(L"SetupDiGetClassDevs");
|
|
|
|
if(count)
|
|
*count = id;
|
|
return (id > 0);
|
|
}
|
|
|
|
void rdm_devices_free(PRDM_DEVICE devices)
|
|
{
|
|
PRDM_DEVICE tmp;
|
|
while(devices)
|
|
{
|
|
if(devices->hDevice)
|
|
{
|
|
CloseHandle(devices->hDevice);
|
|
devices->hDevice = NULL;
|
|
}
|
|
if(devices->DevicePath)
|
|
free(devices->DevicePath);
|
|
tmp = devices->next;
|
|
LocalFree(devices);
|
|
devices = tmp;
|
|
}
|
|
} |