Files
aaruremote/win32/usb.c

639 lines
22 KiB
C
Raw Normal View History

/*
2020-03-01 05:44:49 +00:00
* This file is part of the Aaru Remote Server.
2020-01-03 17:42:13 +00:00
* Copyright (c) 2019-2020 Natalia Portillo.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2019-10-27 18:56:31 +00:00
#include "usb.h"
#include "win32.h"
2019-10-27 18:56:31 +00:00
#include <SetupAPI.h>
#include <cfgmgr32.h>
#include <ntddstor.h>
#include <stdint.h>
2019-10-27 18:56:31 +00:00
#include <stdio.h>
#include <usbioctl.h>
#ifndef DIGCF_PRESENT
#define DIGCF_PRESENT 0x00000002
#endif
#ifndef DIGCF_DEVICEINTERFACE
#define DIGCF_DEVICEINTERFACE 0x00000010
#endif
UsbController_t* GetHostControllers(DWORD* length)
{
GUID hostGuid = GUID_DEVINTERFACE_USB_HOST_CONTROLLER;
HDEVINFO h;
UsbController_t* hostlist;
UsbController_t* host;
DWORD i;
BOOL success;
SP_DEVICE_INTERFACE_DATA dia;
SP_DEVINFO_DATA da;
PSP_DEVICE_INTERFACE_DETAIL_DATA didd;
DWORD nRequiredSize = 0;
DWORD N_BYTES = USB_BUFFER_SIZE;
DWORD requiredSize;
DWORD regType;
CHAR ptrBuf[USB_BUFFER_SIZE];
*length = 0;
didd = malloc(sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + USB_BUFFER_SIZE);
hostlist = malloc(sizeof(UsbController_t) * 64);
if(!hostlist) return NULL;
memset(hostlist, 0, sizeof(UsbController_t) * 64);
// We start at the "root" of the device tree and look for all
// devices that match the interface GUID of a Hub Controller
h = SetupDiGetClassDevs(&hostGuid, 0, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if(h == INVALID_HANDLE_VALUE) return NULL;
i = 0;
do
{
host = &hostlist[*length];
memset(host, 0, sizeof(UsbController_t));
host->ControllerIndex = i;
// create a Device Interface Data structure
memset(&dia, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
dia.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// start the enumeration
success = SetupDiEnumDeviceInterfaces(h, NULL, &hostGuid, i, &dia);
if(success)
{
// build a DevInfo Data structure
memset(&da, 0, sizeof(SP_DEVINFO_DATA));
da.cbSize = sizeof(SP_DEVINFO_DATA);
// build a Device Interface Detail Data structure
memset(didd, 0, USB_BUFFER_SIZE + sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA));
didd->cbSize = 4 + sizeof(TCHAR);
// now we can get some more detailed information
nRequiredSize = 0;
if(SetupDiGetDeviceInterfaceDetail(h, &dia, didd, N_BYTES, &nRequiredSize, &da))
{
strncpy(host->ControllerDevicePath, didd->DevicePath, USB_BUFFER_SIZE);
// get the Device Description and DriverKeyName
requiredSize = 0;
regType = REG_SZ;
if(SetupDiGetDeviceRegistryProperty(
h, &da, SPDRP_DEVICEDESC, &regType, (PBYTE)ptrBuf, USB_BUFFER_SIZE, &requiredSize))
strncpy(host->ControllerDeviceDesc, ptrBuf, USB_BUFFER_SIZE);
if(SetupDiGetDeviceRegistryProperty(
h, &da, SPDRP_DRIVER, &regType, (PBYTE)ptrBuf, USB_BUFFER_SIZE, &requiredSize))
strncpy(host->ControllerDriverKeyName, ptrBuf, USB_BUFFER_SIZE);
}
(*length)++;
}
i++;
} while(success);
SetupDiDestroyDeviceInfoList(h);
return hostlist;
}
DWORD GetDeviceNumberWithHandle(HANDLE handle)
{
STORAGE_DEVICE_NUMBER sdn;
DWORD ans = -1;
DWORD k = 0;
if(DeviceIoControl(handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(STORAGE_DEVICE_NUMBER), &k, NULL))
{ ans = (sdn.DeviceType << 8) + sdn.DeviceNumber; }
return ans;
}
DWORD GetDeviceNumber(char* devicePath)
{
DWORD len = strlen(devicePath);
HANDLE h;
DWORD ans = -1;
if(devicePath[len - 1] == '\\') devicePath[len - 1] = 0;
h = CreateFile(devicePath, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
if(h != INVALID_HANDLE_VALUE)
{
ans = GetDeviceNumberWithHandle(h);
CloseHandle(h);
}
return ans;
}
UsbHub_t* GetRootHub(UsbController_t* controller)
{
HANDLE h, h2;
UsbHub_t* root;
PUSB_ROOT_HUB_NAME hubName;
DWORD nBytes;
DWORD k = 0;
USB_NODE_INFORMATION nodeInfo;
root = malloc(sizeof(UsbHub_t));
hubName = malloc(sizeof(USB_ROOT_HUB_NAME) + USB_BUFFER_SIZE - 1);
if(!root) return NULL;
root->HubIsRootHub = TRUE;
strncpy(root->HubDeviceDesc, "Root Hub", 8);
// Open a handle to the Host Controller
h = CreateFile(controller->ControllerDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(h == INVALID_HANDLE_VALUE) return root;
memset(&hubName, 0, sizeof(USB_ROOT_HUB_NAME) + USB_BUFFER_SIZE - 1);
nBytes = sizeof(USB_ROOT_HUB_NAME) + USB_BUFFER_SIZE - 1;
// get the Hub Name
if(DeviceIoControl(h, IOCTL_USB_GET_ROOT_HUB_NAME, &hubName, nBytes, &hubName, nBytes, &k, NULL))
{ snprintf(root->HubDevicePath, USB_BUFFER_SIZE, "\\\\.\\%ws", hubName->RootHubName); }
// TODO: Get DriverKeyName for Root Hub
// Now let's open the Hub (based upon the HubName we got above)
h2 = CreateFile(root->HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(h2 != INVALID_HANDLE_VALUE)
{
memset(&nodeInfo, 0, sizeof(USB_NODE_INFORMATION));
nodeInfo.NodeType = UsbHub;
nBytes = sizeof(USB_NODE_INFORMATION);
// get the Hub Information
if(DeviceIoControl(h2, IOCTL_USB_GET_NODE_INFORMATION, &nodeInfo, nBytes, &nodeInfo, nBytes, &k, NULL))
{
root->HubIsBusPowered = nodeInfo.u.HubInformation.HubIsBusPowered;
root->HubPortCount = nodeInfo.u.HubInformation.HubDescriptor.bNumberOfPorts;
}
CloseHandle(h2);
}
free(hubName);
CloseHandle(h);
return root;
}
UsbPort_t* GetHubPorts(UsbHub_t* hub, DWORD* length)
{
UsbPort_t* portList;
UsbPort_t* port;
HANDLE h;
USB_NODE_CONNECTION_INFORMATION_EX nodeConnection;
DWORD nBytes;
DWORD k = 0;
DWORD i;
*length = 0;
if(hub == NULL) return NULL;
h = CreateFile(hub->HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(h == INVALID_HANDLE_VALUE) return NULL;
portList = malloc(sizeof(UsbPort_t) * hub->HubPortCount);
if(!portList) return NULL;
for(i = 1; i <= hub->HubPortCount; i++)
{
nBytes = sizeof(USB_NODE_CONNECTION_INFORMATION_EX);
memset(&nodeConnection, 0, nBytes);
nodeConnection.ConnectionIndex = i;
k = 0;
if(!DeviceIoControl(h,
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
&nodeConnection,
nBytes,
&nodeConnection,
nBytes,
&k,
NULL))
continue;
// load up the USBPort class
port = &portList[*length];
port->PortPortNumber = i;
strncpy(port->PortHubDevicePath, hub->HubDevicePath, USB_BUFFER_SIZE);
port->PortStatus = nodeConnection.ConnectionStatus;
port->PortSpeed = nodeConnection.Speed;
port->PortIsDeviceConnected = nodeConnection.ConnectionStatus == DeviceConnected;
port->PortIsHub = nodeConnection.DeviceIsHub;
memcpy(&port->PortDeviceDescriptor, &nodeConnection.DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR));
// add it to the list
(*length)++;
}
CloseHandle(h);
return portList;
}
UsbDevice_t* GetPortDevice(UsbPort_t* port)
{
UsbDevice_t* device;
HANDLE h;
DWORD nBytesReturned;
DWORD nBytes = USB_BUFFER_SIZE + sizeof(USB_DESCRIPTOR_REQUEST) + sizeof(USB_STRING_DESCRIPTOR);
PUSB_DESCRIPTOR_REQUEST request;
PUSB_STRING_DESCRIPTOR descriptor;
DWORD k;
if(port == NULL || port->PortIsDeviceConnected) return NULL;
device = malloc(sizeof(UsbDevice_t));
request = malloc(nBytes);
if(!device) return NULL;
memset(device, 0, sizeof(UsbDevice_t));
// Copy over some values from the Port class
device->PortNumber = port->PortPortNumber;
strncpy(device->HubDevicePath, port->PortHubDevicePath, USB_BUFFER_SIZE);
memcpy(&device->DeviceDescriptor, &port->PortDeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR));
// Open a handle to the Hub device
h = CreateFile(port->PortHubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(h == INVALID_HANDLE_VALUE) return device;
// The iManufacturer, iProduct and iSerialNumber entries in the
// Device Descriptor are really just indexes. So, we have to
// request a String Descriptor to get the values for those strings.
if(port->PortDeviceDescriptor.iManufacturer > 0)
{
memset(request, 0, nBytes);
request->ConnectionIndex = port->PortPortNumber;
request->SetupPacket.wIndex = 0x409;
request->SetupPacket.wValue = ((USB_STRING_DESCRIPTOR_TYPE << 8) + port->PortDeviceDescriptor.iManufacturer);
request->SetupPacket.wLength = nBytes - sizeof(USB_DESCRIPTOR_REQUEST);
// Use an IOCTL call to request the String Descriptor
if(DeviceIoControl(
h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, request, nBytes, request, nBytes, &k, NULL))
{
// The location of the string descriptor is immediately after
// the Request structure. Because this location is not "covered"
// by the structure allocation, we're forced to zero out this
// chunk of memory by using the StringToHGlobalAuto() hack above
descriptor = (PUSB_STRING_DESCRIPTOR)(request + sizeof(USB_DESCRIPTOR_REQUEST));
snprintf(device->Manufacturer, USB_BUFFER_SIZE, "%ws", descriptor->bString);
}
}
if(port->PortDeviceDescriptor.iProduct > 0)
{
memset(request, 0, nBytes);
request->ConnectionIndex = port->PortPortNumber;
request->SetupPacket.wIndex = 0x409;
request->SetupPacket.wValue = ((USB_STRING_DESCRIPTOR_TYPE << 8) + port->PortDeviceDescriptor.iProduct);
request->SetupPacket.wLength = nBytes - sizeof(USB_DESCRIPTOR_REQUEST);
// Use an IOCTL call to request the String Descriptor
if(DeviceIoControl(
h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, request, nBytes, request, nBytes, &k, NULL))
{
// The location of the string descriptor is immediately after
// the Request structure. Because this location is not "covered"
// by the structure allocation, we're forced to zero out this
// chunk of memory by using the StringToHGlobalAuto() hack above
descriptor = (PUSB_STRING_DESCRIPTOR)(request + sizeof(USB_DESCRIPTOR_REQUEST));
snprintf(device->Product, USB_BUFFER_SIZE, "%ws", descriptor->bString);
}
}
if(port->PortDeviceDescriptor.iSerialNumber > 0)
{
memset(request, 0, nBytes);
request->ConnectionIndex = port->PortPortNumber;
request->SetupPacket.wIndex = 0x409;
request->SetupPacket.wValue = ((USB_STRING_DESCRIPTOR_TYPE << 8) + port->PortDeviceDescriptor.iSerialNumber);
request->SetupPacket.wLength = nBytes - sizeof(USB_DESCRIPTOR_REQUEST);
// Use an IOCTL call to request the String Descriptor
if(DeviceIoControl(
h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, request, nBytes, request, nBytes, &k, NULL))
{
// The location of the string descriptor is immediately after
// the Request structure. Because this location is not "covered"
// by the structure allocation, we're forced to zero out this
// chunk of memory by using the StringToHGlobalAuto() hack above
descriptor = (PUSB_STRING_DESCRIPTOR)(request + sizeof(USB_DESCRIPTOR_REQUEST));
snprintf(device->SerialNumber, USB_BUFFER_SIZE, "%ws", descriptor->bString);
}
}
// build a request for configuration descriptor
memset(request, 0, nBytes);
request->ConnectionIndex = port->PortPortNumber;
request->SetupPacket.wIndex = 0;
request->SetupPacket.wValue = ((USB_STRING_DESCRIPTOR_TYPE << 8));
request->SetupPacket.wLength = nBytes - sizeof(USB_DESCRIPTOR_REQUEST);
// Use an IOCTL call to request the String Descriptor
if(DeviceIoControl(
h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, request, nBytes, request, nBytes, &nBytesReturned, NULL))
{
if(nBytesReturned > USB_BUFFER_SIZE) nBytesReturned = USB_BUFFER_SIZE;
memcpy(&device->BinaryDeviceDescriptors, request + sizeof(USB_DESCRIPTOR_REQUEST), nBytesReturned);
device->BinaryDeviceDescriptorsLength = nBytesReturned;
}
free(request);
CloseHandle(h);
return device;
}
UsbHub_t* GetPortHub(UsbPort_t* port)
{
UsbHub_t* hub;
HANDLE h, h2;
PUSB_NODE_CONNECTION_NAME nodeName;
DWORD nBytes;
DWORD k = 0;
USB_NODE_INFORMATION nodeInfo;
UsbDevice_t* device;
nodeName = malloc(sizeof(USB_NODE_CONNECTION_NAME) + USB_BUFFER_SIZE - 1);
if(port == NULL || port->PortIsHub || nodeName == NULL) return NULL;
hub = malloc(sizeof(UsbHub_t));
if(!hub) return NULL;
hub->HubIsRootHub = FALSE;
strncpy(hub->HubDeviceDesc, "External Hub", 12);
// Open a handle to the Host Controller
h = CreateFile(port->PortHubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(h == INVALID_HANDLE_VALUE)
{
free(nodeName);
return hub;
}
// Get the DevicePath for downstream hub
nBytes = sizeof(USB_NODE_CONNECTION_NAME) + USB_BUFFER_SIZE - 1;
memset(nodeName, 0, nBytes);
nodeName->ConnectionIndex = port->PortPortNumber;
k = 0;
// Use an IOCTL call to request the Node Name
if(DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_NAME, nodeName, nBytes, nodeName, nBytes, &k, NULL))
{ snprintf(hub->HubDevicePath, USB_BUFFER_SIZE, "\\\\.\\%ws", nodeName->NodeName); }
// Now let's open the Hub (based upon the HubName we got above)
h2 = CreateFile(hub->HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(h2 != INVALID_HANDLE_VALUE)
{
memset(&nodeInfo, 0, sizeof(USB_NODE_INFORMATION));
nodeInfo.NodeType = UsbHub;
nBytes = sizeof(USB_NODE_INFORMATION);
k = 0;
// get the Hub Information
if(DeviceIoControl(h2, IOCTL_USB_GET_NODE_INFORMATION, &nodeInfo, nBytes, &nodeInfo, nBytes, &k, NULL))
{
hub->HubIsBusPowered = nodeInfo.u.HubInformation.HubIsBusPowered;
hub->HubPortCount = nodeInfo.u.HubInformation.HubDescriptor.bNumberOfPorts;
}
CloseHandle(h2);
}
// Fill in the missing Manufacture, Product, and SerialNumber values
// values by just creating a Device instance and copying the values
device = GetPortDevice(port);
if(device != NULL)
{
strncpy(hub->InstanceId, device->InstanceId, USB_BUFFER_SIZE);
strncpy(hub->Manufacturer, device->Manufacturer, USB_BUFFER_SIZE);
strncpy(hub->Product, device->Product, USB_BUFFER_SIZE);
strncpy(hub->SerialNumber, device->SerialNumber, USB_BUFFER_SIZE);
strncpy(hub->DriverKey, device->DriverKey, USB_BUFFER_SIZE);
free(device);
}
CloseHandle(h);
return hub;
}
static void SearchHubInstanceId(UsbHub_t* hub, UsbDevice_t** foundDevice, const char* instanceId)
{
UsbPort_t* portList;
DWORD portCount = 0;
DWORD i;
UsbDevice_t* device;
UsbHub_t* childHub;
*foundDevice = NULL;
if(hub == NULL) return;
portList = GetHubPorts(hub, &portCount);
if(portList == NULL || portCount == 0) return;
for(i = 0; i < portCount; i++)
{
if(portList[i].PortIsHub)
{
childHub = GetPortHub(&portList[i]);
if(childHub == NULL) continue;
SearchHubInstanceId(childHub, foundDevice, instanceId);
free(childHub);
}
else
{
if(!portList[i].PortIsDeviceConnected) continue;
device = GetPortDevice(&portList[i]);
if(device == NULL || strncmp(device->InstanceId, instanceId, USB_BUFFER_SIZE) != 0) continue;
*foundDevice = device;
break;
}
}
free(portList);
}
UsbDevice_t* FindDeviceByInstanceId(const char* instanceId)
{
UsbController_t* controllers;
DWORD controllers_len = 0;
DWORD i;
UsbDevice_t* foundDevice = NULL;
controllers = GetHostControllers(&controllers_len);
if(controllers == NULL || controllers_len == 0) return NULL;
for(i = 0; i < controllers_len; i++)
{
SearchHubInstanceId(GetRootHub(&controllers[i]), &foundDevice, instanceId);
if(foundDevice != NULL) break;
}
return foundDevice;
}
UsbDevice_t* FindDeviceNumber(DWORD devNum, GUID diskGuid)
{
HDEVINFO h;
BOOL success;
DWORD i = 0;
SP_DEVICE_INTERFACE_DATA dia;
SP_DEVINFO_DATA da;
PSP_DEVICE_INTERFACE_DETAIL_DATA didd;
DWORD nRequiredSize = 0;
DWORD N_BYTES = USB_BUFFER_SIZE;
DEVINST ptrPrevious;
PSTR instanceId = malloc(N_BYTES);
UsbDevice_t* foundDevice = NULL; // TODO: Determine type
didd = malloc(USB_BUFFER_SIZE + sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA));
if(instanceId) return NULL;
memset(instanceId, 0, N_BYTES);
// We start at the "root" of the device tree and look for all
// devices that match the interface GUID of a disk
h = SetupDiGetClassDevs(&diskGuid, 0, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if(h != INVALID_HANDLE_VALUE)
{
do
{
// create a Device Interface Data structure
memset(&dia, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
dia.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// start the enumeration
success = SetupDiEnumDeviceInterfaces(h, NULL, &diskGuid, i, &dia);
if(success)
{
// build a DevInfo Data structure
memset(&da, 0, sizeof(SP_DEVINFO_DATA));
da.cbSize = sizeof(SP_DEVINFO_DATA);
// build a Device Interface Detail Data structure
memset(didd, 0, USB_BUFFER_SIZE + sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA));
didd->cbSize = 4 + sizeof(TCHAR);
// now we can get some more detailed information
nRequiredSize = 0;
if(SetupDiGetDeviceInterfaceDetail(h, &dia, didd, N_BYTES, &nRequiredSize, &da))
if(GetDeviceNumber(didd->DevicePath) == devNum)
{
// current InstanceID is at the "USBSTOR" level, so we
// need up "move up" one level to get to the "USB" level
CM_Get_Parent(&ptrPrevious, da.DevInst, 0);
// Now we get the InstanceID of the USB level device
memset(instanceId, 0, N_BYTES);
CM_Get_Device_ID(ptrPrevious, instanceId, N_BYTES, 0);
}
}
i++;
} while(success);
SetupDiDestroyClassImageList(h);
}
// Did we find an InterfaceID of a USB device?
if(strlen(instanceId) > 0 && strncmp(instanceId, "USB\\", 3) == 0) foundDevice = FindDeviceByInstanceId(instanceId);
free(didd);
return foundDevice;
}
UsbDevice_t* FindDrivePath(Win32DeviceContext* ctx, GUID deviceGuid)
{
// We start by getting the unique DeviceNumber of the given
// DriveLetter. We'll use this later to find a matching
// DevicePath "symbolic name"
DWORD devNum = GetDeviceNumber(ctx->handle);
return devNum < 0 ? NULL : FindDeviceNumber(devNum, deviceGuid);
}
uint8_t Win32GetUsbData(void* device_ctx,
uint16_t* desc_len,
char* descriptors,
uint16_t* id_vendor,
uint16_t* id_product,
char* manufacturer,
char* product,
char* serial)
{
2019-10-27 18:56:31 +00:00
Win32DeviceContext* ctx = device_ctx;
UsbDevice_t* device = NULL;
GUID floppyGuid = GUID_DEVINTERFACE_FLOPPY;
GUID tapeGuid = GUID_DEVINTERFACE_TAPE;
GUID diskGuid = GUID_DEVINTERFACE_DISK;
GUID cdromGuid = GUID_DEVINTERFACE_CDROM;
GUID guids[4] = {floppyGuid, cdromGuid, diskGuid, tapeGuid};
int i;
if(!ctx) return -1;
2019-10-27 18:56:31 +00:00
for(i = 0; i < 4; i++)
{
device = FindDrivePath(ctx, guids[i]);
if(device != NULL) break;
}
if(device == NULL) return 0;
memcpy(descriptors, device->BinaryDeviceDescriptors, device->BinaryDeviceDescriptorsLength);
*desc_len = device->BinaryDeviceDescriptorsLength;
*id_vendor = device->DeviceDescriptor.idVendor;
*id_product = device->DeviceDescriptor.idProduct;
strncpy(manufacturer, device->Manufacturer, 256);
strncpy(product, device->Product, 256);
strncpy(serial, device->SerialNumber, 256);
return 1;
}