7d6a10aaff
Base 0 does not work when converting the string to long int. This ends up not showing the correct device id and sometimes the wrong bus id. By changing it to base 10 conversion instead of base 0 this fixes the issue of auto redirect usb devices.
1617 lines
44 KiB
C
1617 lines
44 KiB
C
/**
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
* RemoteFX USB Redirection
|
|
*
|
|
* Copyright 2012 Atrust corp.
|
|
* Copyright 2012 Alfred Liu <alfred.liu@atruscorp.com>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <errno.h>
|
|
#include <err.h>
|
|
#endif
|
|
#if defined(__linux__)
|
|
#include <libudev.h>
|
|
#endif
|
|
|
|
#include <winpr/crt.h>
|
|
#include <winpr/synch.h>
|
|
#include <winpr/string.h>
|
|
#include <winpr/cmdline.h>
|
|
|
|
#include <freerdp/dvc.h>
|
|
#include <freerdp/addin.h>
|
|
#include <freerdp/channels/log.h>
|
|
|
|
#include "urbdrc_types.h"
|
|
#include "urbdrc_main.h"
|
|
#include "data_transfer.h"
|
|
#include "searchman.h"
|
|
|
|
static int func_hardware_id_format(IUDEVICE* pdev, char(*HardwareIds)[DEVICE_HARDWARE_ID_SIZE])
|
|
{
|
|
char str[DEVICE_HARDWARE_ID_SIZE];
|
|
UINT16 idVendor, idProduct, bcdDevice;
|
|
idVendor = (UINT16)pdev->query_device_descriptor(pdev, ID_VENDOR);
|
|
idProduct = (UINT16)pdev->query_device_descriptor(pdev, ID_PRODUCT);
|
|
bcdDevice = (UINT16)pdev->query_device_descriptor(pdev, BCD_DEVICE);
|
|
sprintf_s(str, sizeof(str), "USB\\VID_%04"PRIX16"&PID_%04"PRIX16"", idVendor, idProduct);
|
|
strncpy(HardwareIds[1], str, DEVICE_HARDWARE_ID_SIZE);
|
|
sprintf_s(str, sizeof(str), "%s&REV_%04"PRIX16"", HardwareIds[1], bcdDevice);
|
|
strncpy(HardwareIds[0], str, DEVICE_HARDWARE_ID_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
static int func_compat_id_format(IUDEVICE* pdev,
|
|
char(*CompatibilityIds)[DEVICE_COMPATIBILITY_ID_SIZE])
|
|
{
|
|
char str[DEVICE_COMPATIBILITY_ID_SIZE];
|
|
UINT8 bDeviceClass, bDeviceSubClass, bDeviceProtocol;
|
|
bDeviceClass = (UINT8)pdev->query_device_descriptor(pdev, B_DEVICE_CLASS);
|
|
bDeviceSubClass = (UINT8)pdev->query_device_descriptor(pdev, B_DEVICE_SUBCLASS);
|
|
bDeviceProtocol = (UINT8)pdev->query_device_descriptor(pdev, B_DEVICE_PROTOCOL);
|
|
|
|
if (!(pdev->isCompositeDevice(pdev)))
|
|
{
|
|
sprintf_s(str, sizeof(str), "USB\\Class_%02"PRIX8"", bDeviceClass);
|
|
strncpy(CompatibilityIds[2], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
|
sprintf_s(str, sizeof(str), "%s&SubClass_%02"PRIX8"", CompatibilityIds[2], bDeviceSubClass);
|
|
strncpy(CompatibilityIds[1], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
|
sprintf_s(str, sizeof(str), "%s&Prot_%02"PRIX8"", CompatibilityIds[1], bDeviceProtocol);
|
|
strncpy(CompatibilityIds[0], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
|
}
|
|
else
|
|
{
|
|
sprintf_s(str, sizeof(str), "USB\\DevClass_00");
|
|
strncpy(CompatibilityIds[2], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
|
sprintf_s(str, sizeof(str), "%s&SubClass_00", CompatibilityIds[2]);
|
|
strncpy(CompatibilityIds[1], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
|
sprintf_s(str, sizeof(str), "%s&Prot_00", CompatibilityIds[1]);
|
|
strncpy(CompatibilityIds[0], str, DEVICE_COMPATIBILITY_ID_SIZE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void func_close_udevice(USB_SEARCHMAN* searchman, IUDEVICE* pdev)
|
|
{
|
|
int idVendor = 0;
|
|
int idProduct = 0;
|
|
URBDRC_PLUGIN* urbdrc = searchman->urbdrc;
|
|
pdev->SigToEnd(pdev);
|
|
idVendor = pdev->query_device_descriptor(pdev, ID_VENDOR);
|
|
idProduct = pdev->query_device_descriptor(pdev, ID_PRODUCT);
|
|
searchman->add(searchman, (UINT16) idVendor, (UINT16) idProduct);
|
|
pdev->cancel_all_transfer_request(pdev);
|
|
pdev->wait_action_completion(pdev);
|
|
#if ISOCH_FIFO
|
|
{
|
|
/* free isoch queue */
|
|
ISOCH_CALLBACK_QUEUE* isoch_queue = pdev->get_isoch_queue(pdev);
|
|
|
|
if (isoch_queue)
|
|
isoch_queue->free(isoch_queue);
|
|
}
|
|
#endif
|
|
urbdrc->udevman->unregister_udevice(urbdrc->udevman,
|
|
pdev->get_bus_number(pdev),
|
|
pdev->get_dev_number(pdev));
|
|
}
|
|
|
|
static int fun_device_string_send_set(char* out_data, int out_offset, char* str)
|
|
{
|
|
int i = 0;
|
|
int offset = 0;
|
|
|
|
while (str[i])
|
|
{
|
|
data_write_UINT16(out_data + out_offset + offset, str[i]); /* str */
|
|
i++;
|
|
offset += 2;
|
|
}
|
|
|
|
data_write_UINT16(out_data + out_offset + offset, 0x0000); /* add "\0" */
|
|
offset += 2;
|
|
return offset + out_offset;
|
|
}
|
|
|
|
static int func_container_id_generate(IUDEVICE* pdev, char* strContainerId)
|
|
{
|
|
char* p, *path;
|
|
UINT8 containerId[17];
|
|
UINT16 idVendor, idProduct;
|
|
idVendor = (UINT16)pdev->query_device_descriptor(pdev, ID_VENDOR);
|
|
idProduct = (UINT16)pdev->query_device_descriptor(pdev, ID_PRODUCT);
|
|
path = pdev->getPath(pdev);
|
|
|
|
if (strlen(path) > 8)
|
|
p = (path + strlen(path)) - 8;
|
|
else
|
|
p = path;
|
|
|
|
ZeroMemory(containerId, sizeof(containerId));
|
|
sprintf_s((char*)containerId, sizeof(containerId), "%04"PRIX16"%04"PRIX16"%s", idVendor, idProduct,
|
|
p);
|
|
/* format */
|
|
sprintf_s(strContainerId, DEVICE_CONTAINER_STR_SIZE,
|
|
"{%02"PRIx8"%02"PRIx8"%02"PRIx8"%02"PRIx8"-%02"PRIx8"%02"PRIx8"-%02"PRIx8"%02"PRIx8"-%02"PRIx8"%02"PRIx8"-%02"PRIx8"%02"PRIx8"%02"PRIx8"%02"PRIx8"%02"PRIx8"%02"PRIx8"}",
|
|
containerId[0], containerId[1], containerId[2], containerId[3],
|
|
containerId[4], containerId[5], containerId[6], containerId[7],
|
|
containerId[8], containerId[9], containerId[10], containerId[11],
|
|
containerId[12], containerId[13], containerId[14], containerId[15]);
|
|
return 0;
|
|
}
|
|
|
|
static int func_instance_id_generate(IUDEVICE* pdev, char* strInstanceId)
|
|
{
|
|
UINT8 instanceId[17];
|
|
ZeroMemory(instanceId, sizeof(instanceId));
|
|
sprintf_s((char*)instanceId, sizeof(instanceId), "\\%s", pdev->getPath(pdev));
|
|
/* format */
|
|
sprintf_s(strInstanceId, DEVICE_INSTANCE_STR_SIZE,
|
|
"%02"PRIx8"%02"PRIx8"%02"PRIx8"%02"PRIx8"-%02"PRIx8"%02"PRIx8"-%02"PRIx8"%02"PRIx8"-%02"PRIx8"%02"PRIx8"-%02"PRIx8"%02"PRIx8"%02"PRIx8"%02"PRIx8"%02"PRIx8"%02"PRIx8"",
|
|
instanceId[0], instanceId[1], instanceId[2], instanceId[3],
|
|
instanceId[4], instanceId[5], instanceId[6], instanceId[7],
|
|
instanceId[8], instanceId[9], instanceId[10], instanceId[11],
|
|
instanceId[12], instanceId[13], instanceId[14], instanceId[15]);
|
|
return 0;
|
|
}
|
|
|
|
#if ISOCH_FIFO
|
|
|
|
static void func_lock_isoch_mutex(TRANSFER_DATA* transfer_data)
|
|
{
|
|
int noAck = 0;
|
|
IUDEVICE* pdev;
|
|
UINT32 FunctionId;
|
|
UINT32 RequestField;
|
|
UINT16 URB_Function;
|
|
IUDEVMAN* udevman = transfer_data->udevman;
|
|
|
|
if (transfer_data->cbSize >= 8)
|
|
{
|
|
data_read_UINT32(transfer_data->pBuffer + 4, FunctionId);
|
|
|
|
if ((FunctionId == TRANSFER_IN_REQUEST ||
|
|
FunctionId == TRANSFER_OUT_REQUEST) &&
|
|
transfer_data->cbSize >= 16)
|
|
{
|
|
data_read_UINT16(transfer_data->pBuffer + 14, URB_Function);
|
|
|
|
if (URB_Function == URB_FUNCTION_ISOCH_TRANSFER &&
|
|
transfer_data->cbSize >= 20)
|
|
{
|
|
data_read_UINT32(transfer_data->pBuffer + 16, RequestField);
|
|
noAck = (RequestField & 0x80000000) >> 31;
|
|
|
|
if (!noAck)
|
|
{
|
|
pdev = udevman->get_udevice_by_UsbDevice(udevman, transfer_data->UsbDevice);
|
|
pdev->lock_fifo_isoch(pdev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_process_capability_request(URBDRC_CHANNEL_CALLBACK* callback, char* data,
|
|
UINT32 data_sizem, UINT32 MessageId)
|
|
{
|
|
UINT32 InterfaceId;
|
|
UINT32 Version;
|
|
UINT32 out_size;
|
|
char* out_data;
|
|
UINT ret;
|
|
WLog_VRB(TAG, "");
|
|
data_read_UINT32(data + 0, Version);
|
|
InterfaceId = ((STREAM_ID_NONE << 30) | CAPABILITIES_NEGOTIATOR);
|
|
out_size = 16;
|
|
out_data = (char*) calloc(1, out_size);
|
|
|
|
if (!out_data)
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
data_write_UINT32(out_data + 0, InterfaceId); /* interface id */
|
|
data_write_UINT32(out_data + 4, MessageId); /* message id */
|
|
data_write_UINT32(out_data + 8, Version); /* usb protocol version */
|
|
data_write_UINT32(out_data + 12, 0x00000000); /* HRESULT */
|
|
ret = callback->channel->Write(callback->channel, out_size, (BYTE*) out_data, NULL);
|
|
zfree(out_data);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_process_channel_create(URBDRC_CHANNEL_CALLBACK* callback, char* data,
|
|
UINT32 data_sizem, UINT32 MessageId)
|
|
{
|
|
UINT32 InterfaceId;
|
|
UINT32 out_size;
|
|
UINT32 MajorVersion;
|
|
UINT32 MinorVersion;
|
|
UINT32 Capabilities;
|
|
char* out_data;
|
|
UINT ret;
|
|
WLog_VRB(TAG, "");
|
|
data_read_UINT32(data + 0, MajorVersion);
|
|
data_read_UINT32(data + 4, MinorVersion);
|
|
data_read_UINT32(data + 8, Capabilities);
|
|
InterfaceId = ((STREAM_ID_PROXY << 30) | CLIENT_CHANNEL_NOTIFICATION);
|
|
out_size = 24;
|
|
out_data = (char*) calloc(1, out_size);
|
|
|
|
if (!out_data)
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
data_write_UINT32(out_data + 0, InterfaceId); /* interface id */
|
|
data_write_UINT32(out_data + 4, MessageId); /* message id */
|
|
data_write_UINT32(out_data + 8, CHANNEL_CREATED); /* function id */
|
|
data_write_UINT32(out_data + 12, MajorVersion);
|
|
data_write_UINT32(out_data + 16, MinorVersion);
|
|
data_write_UINT32(out_data + 20, Capabilities); /* capabilities version */
|
|
ret = callback->channel->Write(callback->channel, out_size, (BYTE*)out_data, NULL);
|
|
zfree(out_data);
|
|
return ret;
|
|
}
|
|
|
|
static int urdbrc_send_virtual_channel_add(IWTSVirtualChannel* channel, UINT32 MessageId)
|
|
{
|
|
UINT32 out_size;
|
|
UINT32 InterfaceId;
|
|
char* out_data;
|
|
WLog_VRB(TAG, "");
|
|
assert(NULL != channel);
|
|
assert(NULL != channel->Write);
|
|
InterfaceId = ((STREAM_ID_PROXY << 30) | CLIENT_DEVICE_SINK);
|
|
out_size = 12;
|
|
out_data = (char*) malloc(out_size);
|
|
memset(out_data, 0, out_size);
|
|
data_write_UINT32(out_data + 0, InterfaceId); /* interface */
|
|
data_write_UINT32(out_data + 4, MessageId); /* message id */
|
|
data_write_UINT32(out_data + 8, ADD_VIRTUAL_CHANNEL); /* function id */
|
|
channel->Write(channel, out_size, (BYTE*) out_data, NULL);
|
|
zfree(out_data);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urdbrc_send_usb_device_add(URBDRC_CHANNEL_CALLBACK* callback, IUDEVICE* pdev)
|
|
{
|
|
char* out_data;
|
|
UINT32 InterfaceId;
|
|
char HardwareIds[2][DEVICE_HARDWARE_ID_SIZE];
|
|
char CompatibilityIds[3][DEVICE_COMPATIBILITY_ID_SIZE];
|
|
char strContainerId[DEVICE_CONTAINER_STR_SIZE];
|
|
char strInstanceId[DEVICE_INSTANCE_STR_SIZE];
|
|
char* composite_str = "USB\\COMPOSITE";
|
|
int size, out_offset, cchCompatIds, bcdUSB;
|
|
ISOCH_CALLBACK_QUEUE* cb_queue;
|
|
UINT ret;
|
|
WLog_VRB(TAG, "");
|
|
InterfaceId = ((STREAM_ID_PROXY << 30) | CLIENT_DEVICE_SINK);
|
|
/* USB kernel driver detach!! */
|
|
pdev->detach_kernel_driver(pdev);
|
|
#if ISOCH_FIFO
|
|
/* create/initial isoch queue */
|
|
cb_queue = isoch_queue_new();
|
|
|
|
if (!cb_queue)
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
pdev->set_isoch_queue(pdev, (void*)cb_queue);
|
|
#endif
|
|
func_hardware_id_format(pdev, HardwareIds);
|
|
func_compat_id_format(pdev, CompatibilityIds);
|
|
func_instance_id_generate(pdev, strInstanceId);
|
|
func_container_id_generate(pdev, strContainerId);
|
|
cchCompatIds = strlen(CompatibilityIds[0]) + 1 +
|
|
strlen(CompatibilityIds[1]) + 1 +
|
|
strlen(CompatibilityIds[2]) + 2;
|
|
|
|
if (pdev->isCompositeDevice(pdev))
|
|
cchCompatIds += strlen(composite_str) + 1;
|
|
|
|
out_offset = 24;
|
|
size = 24;
|
|
size += (strlen(strInstanceId) + 1) * 2 +
|
|
(strlen(HardwareIds[0]) + 1) * 2 + 4 +
|
|
(strlen(HardwareIds[1]) + 1) * 2 + 2 +
|
|
4 + (cchCompatIds) * 2 +
|
|
(strlen(strContainerId) + 1) * 2 + 4 + 28;
|
|
out_data = (char*)calloc(1, size);
|
|
|
|
if (!out_data)
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
data_write_UINT32(out_data + 0, InterfaceId); /* interface */
|
|
/* data_write_UINT32(out_data + 4, 0);*/ /* message id */
|
|
data_write_UINT32(out_data + 8, ADD_DEVICE); /* function id */
|
|
data_write_UINT32(out_data + 12, 0x00000001); /* NumUsbDevice */
|
|
data_write_UINT32(out_data + 16, pdev->get_UsbDevice(pdev)); /* UsbDevice */
|
|
data_write_UINT32(out_data + 20, 0x00000025); /* cchDeviceInstanceId */
|
|
out_offset = fun_device_string_send_set(out_data, out_offset, strInstanceId);
|
|
data_write_UINT32(out_data + out_offset, 0x00000036); /* cchHwIds */
|
|
out_offset += 4;
|
|
/* HardwareIds 1 */
|
|
out_offset = fun_device_string_send_set(out_data, out_offset, HardwareIds[0]);
|
|
/* HardwareIds 2 */
|
|
out_offset = fun_device_string_send_set(out_data, out_offset, HardwareIds[1]);
|
|
/*data_write_UINT16(out_data + out_offset, 0x0000);*/ /* add "\0" */
|
|
out_offset += 2;
|
|
data_write_UINT32(out_data + out_offset, cchCompatIds); /* cchCompatIds */
|
|
out_offset += 4;
|
|
/* CompatibilityIds 1 */
|
|
out_offset = fun_device_string_send_set(out_data, out_offset, CompatibilityIds[0]);
|
|
/* CompatibilityIds 2 */
|
|
out_offset = fun_device_string_send_set(out_data, out_offset, CompatibilityIds[1]);
|
|
/* CompatibilityIds 3 */
|
|
out_offset = fun_device_string_send_set(out_data, out_offset, CompatibilityIds[2]);
|
|
|
|
if (pdev->isCompositeDevice(pdev))
|
|
out_offset = fun_device_string_send_set(out_data, out_offset, composite_str);
|
|
|
|
/*data_write_UINT16(out_data + out_offset, 0x0000);*/ /* add "\0" */
|
|
out_offset += 2;
|
|
data_write_UINT32(out_data + out_offset, 0x00000027); /* cchContainerId */
|
|
out_offset += 4;
|
|
/* ContainerId */
|
|
out_offset = fun_device_string_send_set(out_data, out_offset, strContainerId);
|
|
/* USB_DEVICE_CAPABILITIES 28 bytes */
|
|
data_write_UINT32(out_data + out_offset, 0x0000001c); /* CbSize */
|
|
data_write_UINT32(out_data + out_offset + 4, 2); /* UsbBusInterfaceVersion, 0 ,1 or 2 */
|
|
data_write_UINT32(out_data + out_offset + 8, 0x600); /* USBDI_Version, 0x500 or 0x600 */
|
|
/* Supported_USB_Version, 0x110,0x110 or 0x200(usb2.0) */
|
|
bcdUSB = pdev->query_device_descriptor(pdev, BCD_USB);
|
|
data_write_UINT32(out_data + out_offset + 12, bcdUSB);
|
|
data_write_UINT32(out_data + out_offset + 16,
|
|
0x00000000); /* HcdCapabilities, MUST always be zero */
|
|
|
|
if (bcdUSB < 0x200)
|
|
data_write_UINT32(out_data + out_offset + 20, 0x00000000); /* DeviceIsHighSpeed */
|
|
else
|
|
data_write_UINT32(out_data + out_offset + 20, 0x00000001); /* DeviceIsHighSpeed */
|
|
|
|
data_write_UINT32(out_data + out_offset + 24,
|
|
0x50); /* NoAckIsochWriteJitterBufferSizeInMs, >=10 or <=512 */
|
|
out_offset += 28;
|
|
ret = callback->channel->Write(callback->channel, out_offset, (BYTE*)out_data, NULL);
|
|
zfree(out_data);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_exchange_capabilities(URBDRC_CHANNEL_CALLBACK* callback, char* pBuffer,
|
|
UINT32 cbSize)
|
|
{
|
|
UINT32 MessageId;
|
|
UINT32 FunctionId;
|
|
UINT error = CHANNEL_RC_OK;
|
|
data_read_UINT32(pBuffer + 0, MessageId);
|
|
data_read_UINT32(pBuffer + 4, FunctionId);
|
|
|
|
switch (FunctionId)
|
|
{
|
|
case RIM_EXCHANGE_CAPABILITY_REQUEST:
|
|
error = urbdrc_process_capability_request(callback, pBuffer + 8, cbSize - 8, MessageId);
|
|
break;
|
|
|
|
default:
|
|
WLog_ERR(TAG, "%s: unknown FunctionId 0x%"PRIX32"", __FUNCTION__, FunctionId);
|
|
error = ERROR_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
|
static char* devd_get_val(char* buf, size_t buf_size, const char* val_name, size_t val_name_size,
|
|
size_t* val_size)
|
|
{
|
|
char* ret, *buf_end, *ptr;
|
|
buf_end = (buf + buf_size);
|
|
|
|
for (ret = buf; ret != NULL && ret < buf_end;)
|
|
{
|
|
ret = memmem(ret, (buf_end - ret), val_name, val_name_size);
|
|
|
|
if (ret == NULL)
|
|
return NULL;
|
|
|
|
/* Found. */
|
|
/* Check: space before or buf+1. */
|
|
if ((buf + 1) < ret && ret[-1] != ' ')
|
|
{
|
|
ret += val_name_size;
|
|
continue;
|
|
}
|
|
|
|
/* Check: = after name and size for value. */
|
|
ret += val_name_size;
|
|
|
|
if ((ret + 1) >= buf_end)
|
|
return NULL;
|
|
|
|
if (ret[0] != '=')
|
|
continue;
|
|
|
|
ret ++;
|
|
break;
|
|
}
|
|
|
|
if (ret == NULL || val_size == NULL)
|
|
return ret;
|
|
|
|
/* Calc value data size. */
|
|
ptr = memchr(ret, ' ', (buf_end - ret));
|
|
|
|
if (ptr == NULL) /* End of string/last value. */
|
|
ptr = buf_end;
|
|
|
|
(*val_size) = (ptr - ret);
|
|
return ret;
|
|
}
|
|
|
|
static void* urbdrc_search_usb_device(void* arg)
|
|
{
|
|
USB_SEARCHMAN* searchman = (USB_SEARCHMAN*)arg;
|
|
URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*)searchman->urbdrc;
|
|
IUDEVMAN* udevman = urbdrc->udevman;
|
|
IWTSVirtualChannelManager* channel_mgr = urbdrc->listener_callback->channel_mgr;
|
|
IWTSVirtualChannel* dvc_channel;
|
|
USB_SEARCHDEV* sdev;
|
|
IUDEVICE* pdev;
|
|
HANDLE listobj[2];
|
|
HANDLE mon_fd;
|
|
int devd_skt;
|
|
char buf[4096], *val, *ptr, *end_val;
|
|
ssize_t data_size;
|
|
size_t val_size, tm;
|
|
long idVendor, idProduct;
|
|
long busnum, devnum;
|
|
int action, success, found, on_close;
|
|
struct sockaddr_un sun;
|
|
DWORD status;
|
|
UINT32 error;
|
|
WLog_DBG(TAG, "urbdrc_search_usb_device - devd: start");
|
|
devd_skt = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
|
|
|
|
if (devd_skt == -1)
|
|
{
|
|
WLog_ERR(TAG, "Can't create devd socket: error = %i", errno);
|
|
goto err_out;
|
|
}
|
|
|
|
memset(&sun, 0, sizeof(sun));
|
|
sun.sun_family = PF_LOCAL;
|
|
sun.sun_len = sizeof(sun);
|
|
strlcpy(sun.sun_path, "/var/run/devd.seqpacket.pipe", sizeof(sun.sun_path));
|
|
|
|
if (-1 == connect(devd_skt, (struct sockaddr*)&sun, sizeof(sun)))
|
|
{
|
|
WLog_ERR(TAG, "Can't connect devd socket: error = %i - %s", errno, strerror(errno));
|
|
goto err_out;
|
|
}
|
|
|
|
/* Get the file descriptor (fd) for the monitor.
|
|
This fd will get passed to select() */
|
|
mon_fd = CreateFileDescriptorEvent(NULL, TRUE, FALSE, devd_skt, WINPR_FD_READ);
|
|
listobj[0] = searchman->term_event;
|
|
listobj[1] = mon_fd;
|
|
|
|
while (WaitForMultipleObjects(2, listobj, FALSE, INFINITE) != WAIT_OBJECT_0)
|
|
{
|
|
status = WaitForMultipleObjects(2, listobj, FALSE, INFINITE);
|
|
|
|
if (status == WAIT_FAILED)
|
|
{
|
|
error = GetLastError();
|
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", error);
|
|
return 0;
|
|
}
|
|
|
|
if (status == WAIT_OBJECT_0)
|
|
break;
|
|
|
|
errno = 0;
|
|
WLog_DBG(TAG, "======= SEARCH ======= ");
|
|
/* !system=USB subsystem=DEVICE type=ATTACH ugen=ugen3.3 cdev=ugen3.3 vendor=0x046d product=0x082d devclass=0xef devsubclass=0x02 sernum="6E7D726F" release=0x0011 mode=host port=4 parent=ugen3.1 */
|
|
/* !system=USB subsystem=DEVICE type=DETACH ugen=ugen3.3 cdev=ugen3.3 vendor=0x046d product=0x082d devclass=0xef devsubclass=0x02 sernum="6E7D726F" release=0x0011 mode=host port=4 parent=ugen3.1 */
|
|
data_size = read(devd_skt, buf, (sizeof(buf) - 1));
|
|
|
|
if (data_size == -1)
|
|
{
|
|
WLog_ERR(TAG, "devd socket read: error = %i", errno);
|
|
break;
|
|
}
|
|
|
|
buf[data_size] = 0;
|
|
WLog_DBG(TAG, "devd event: %s", buf);
|
|
|
|
if (buf[0] != '!') /* Skeep non notify events. */
|
|
continue;
|
|
|
|
/* Check: system=USB */
|
|
val = devd_get_val(buf, data_size, "system", 6, &val_size);
|
|
|
|
if (val == NULL || val_size != 3 || memcmp(val, "USB", 3) != 0)
|
|
continue;
|
|
|
|
/* Check: subsystem=DEVICE */
|
|
val = devd_get_val(buf, data_size, "subsystem", 9, &val_size);
|
|
|
|
if (val == NULL || val_size != 6 || memcmp(val, "DEVICE", 6) != 0)
|
|
continue;
|
|
|
|
/* Get event type. */
|
|
val = devd_get_val(buf, data_size, "type", 4, &val_size);
|
|
|
|
if (val == NULL || val_size != 6)
|
|
continue;
|
|
|
|
action = -1;
|
|
|
|
if (memcmp(val, "ATTACH", 6) == 0)
|
|
action = 0;
|
|
|
|
if (memcmp(val, "DETACH", 6) == 0)
|
|
action = 1;
|
|
|
|
if (action == -1)
|
|
continue; /* Skeep other actions. */
|
|
|
|
/* Get bus and dev num. */
|
|
/* ugen=ugen3.3 */
|
|
val = devd_get_val(buf, data_size, "ugen", 4, &val_size);
|
|
|
|
if (val == NULL || val_size < 7 || memcmp(val, "ugen", 4) != 0)
|
|
continue;
|
|
|
|
val += 4;
|
|
val_size -= 4;
|
|
ptr = memchr(val, '.', val_size);
|
|
|
|
if (ptr == NULL)
|
|
continue;
|
|
|
|
/* Prepare strings. */
|
|
ptr[0] = 0;
|
|
ptr ++;
|
|
val[val_size] = 0;
|
|
/* Extract numbers. */
|
|
busnum = strtol(val, NULL, 0);
|
|
|
|
if (errno != 0)
|
|
continue;
|
|
|
|
devnum = strtol(ptr, NULL, 0);
|
|
|
|
if (errno != 0)
|
|
continue;
|
|
|
|
/* Restore spaces. */
|
|
ptr[-1] = ' ';
|
|
val[val_size] = ' ';
|
|
/* Handle event. */
|
|
dvc_channel = NULL;
|
|
|
|
switch (action)
|
|
{
|
|
case 0: /* ATTACH */
|
|
sdev = NULL;
|
|
success = 0;
|
|
found = 0;
|
|
/* vendor=0x046d */
|
|
val = devd_get_val(buf, data_size, "vendor", 6, &val_size);
|
|
|
|
if (val == NULL || val_size < 1)
|
|
continue;
|
|
|
|
val[val_size] = 0;
|
|
idVendor = strtol(val, NULL, 16);
|
|
|
|
if (errno != 0)
|
|
continue;
|
|
|
|
val[val_size] = ' ';
|
|
/* product=0x082d */
|
|
val = devd_get_val(buf, data_size, "product", 7, &val_size);
|
|
|
|
if (val == NULL || val_size < 1)
|
|
continue;
|
|
|
|
val[val_size] = 0;
|
|
idProduct = strtol(val, NULL, 16);
|
|
|
|
if (errno != 0)
|
|
continue;
|
|
|
|
val[val_size] = ' ';
|
|
WLog_DBG(TAG, "ATTACH: bus: %i, dev: %i, ven: %i, prod: %i", busnum, devnum, idVendor, idProduct);
|
|
dvc_channel = channel_mgr->FindChannelById(channel_mgr, urbdrc->first_channel_id);
|
|
searchman->rewind(searchman);
|
|
|
|
while (dvc_channel && searchman->has_next(searchman))
|
|
{
|
|
sdev = searchman->get_next(searchman);
|
|
|
|
if (sdev->idVendor == idVendor &&
|
|
sdev->idProduct == idProduct)
|
|
{
|
|
WLog_VRB(TAG, "Searchman Found Device: %04"PRIx16":%04"PRIx16"",
|
|
sdev->idVendor, sdev->idProduct);
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found && udevman->isAutoAdd(udevman))
|
|
{
|
|
WLog_VRB(TAG, "Auto Find Device: %04x:%04x ",
|
|
idVendor, idProduct);
|
|
found = 2;
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
success = udevman->register_udevice(udevman, busnum, devnum,
|
|
searchman->UsbDevice, 0, 0, UDEVMAN_FLAG_ADD_BY_ADDR);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
searchman->UsbDevice ++;
|
|
usleep(400000);
|
|
error = urdbrc_send_virtual_channel_add(dvc_channel, 0);
|
|
|
|
if (found == 1)
|
|
searchman->remove(searchman, sdev->idVendor, sdev->idProduct);
|
|
}
|
|
|
|
break;
|
|
|
|
case 1: /* DETACH */
|
|
pdev = NULL;
|
|
on_close = 0;
|
|
WLog_DBG(TAG, "DETACH: bus: %i, dev: %i", busnum, devnum);
|
|
usleep(500000);
|
|
udevman->loading_lock(udevman);
|
|
udevman->rewind(udevman);
|
|
|
|
while (udevman->has_next(udevman))
|
|
{
|
|
pdev = udevman->get_next(udevman);
|
|
|
|
if (pdev->get_bus_number(pdev) == busnum &&
|
|
pdev->get_dev_number(pdev) == devnum)
|
|
{
|
|
dvc_channel = channel_mgr->FindChannelById(channel_mgr, pdev->get_channel_id(pdev));
|
|
|
|
if (dvc_channel == NULL)
|
|
{
|
|
WLog_ERR(TAG, "SEARCH: dvc_channel %d is NULL!!", pdev->get_channel_id(pdev));
|
|
func_close_udevice(searchman, pdev);
|
|
break;
|
|
}
|
|
|
|
if (!pdev->isSigToEnd(pdev))
|
|
{
|
|
dvc_channel->Write(dvc_channel, 0, NULL, NULL);
|
|
pdev->SigToEnd(pdev);
|
|
}
|
|
|
|
on_close = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
udevman->loading_unlock(udevman);
|
|
usleep(300000);
|
|
|
|
if (pdev && on_close && dvc_channel &&
|
|
pdev->isSigToEnd(pdev) &&
|
|
!(pdev->isChannelClosed(pdev)))
|
|
{
|
|
dvc_channel->Close(dvc_channel);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
CloseHandle(mon_fd);
|
|
err_out:
|
|
close(devd_skt);
|
|
sem_post(&searchman->sem_term);
|
|
WLog_DBG(TAG, "urbdrc_search_usb_device - devd: end");
|
|
return 0;
|
|
}
|
|
#endif
|
|
#if defined (__linux__)
|
|
static void* urbdrc_search_usb_device(void* arg)
|
|
{
|
|
USB_SEARCHMAN* searchman = (USB_SEARCHMAN*) arg;
|
|
URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*) searchman->urbdrc;
|
|
IUDEVMAN* udevman = urbdrc->udevman;
|
|
IWTSVirtualChannelManager* channel_mgr;
|
|
IWTSVirtualChannel* dvc_channel;
|
|
USB_SEARCHDEV* sdev;
|
|
IUDEVICE* pdev = NULL;
|
|
HANDLE listobj[2];
|
|
HANDLE mon_fd;
|
|
int numobj, timeout;
|
|
long busnum, devnum;
|
|
int success = 0, on_close = 0, found = 0;
|
|
WLog_VRB(TAG, "");
|
|
channel_mgr = urbdrc->listener_callback->channel_mgr;
|
|
DWORD status;
|
|
DWORD dwError;
|
|
/* init usb monitor */
|
|
struct udev* udev;
|
|
struct udev_device* dev;
|
|
struct udev_monitor* mon;
|
|
udev = udev_new();
|
|
|
|
if (!udev)
|
|
{
|
|
WLog_ERR(TAG, "Can't create udev");
|
|
return 0;
|
|
}
|
|
|
|
/* Set up a monitor to monitor usb devices */
|
|
mon = udev_monitor_new_from_netlink(udev, "udev");
|
|
udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", "usb_device");
|
|
udev_monitor_enable_receiving(mon);
|
|
/* Get the file descriptor (fd) for the monitor.
|
|
This fd will get passed to select() */
|
|
mon_fd = CreateFileDescriptorEvent(NULL, TRUE, FALSE,
|
|
udev_monitor_get_fd(mon), WINPR_FD_READ);
|
|
|
|
if (!mon_fd)
|
|
goto fail_create_monfd_event;
|
|
|
|
while (1)
|
|
{
|
|
WLog_VRB(TAG, "======= SEARCH ======= ");
|
|
busnum = 0;
|
|
devnum = 0;
|
|
sdev = NULL;
|
|
pdev = NULL;
|
|
dvc_channel = NULL;
|
|
on_close = 0;
|
|
listobj[0] = searchman->term_event;
|
|
listobj[1] = mon_fd;
|
|
numobj = 2;
|
|
status = WaitForMultipleObjects(numobj, listobj, FALSE, INFINITE);
|
|
|
|
if (status == WAIT_FAILED)
|
|
{
|
|
dwError = GetLastError();
|
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"!", dwError);
|
|
goto out;
|
|
}
|
|
|
|
status = WaitForSingleObject(searchman->term_event, 0);
|
|
|
|
if (status == WAIT_FAILED)
|
|
{
|
|
dwError = GetLastError();
|
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", dwError);
|
|
goto out;
|
|
}
|
|
|
|
if (status == WAIT_OBJECT_0)
|
|
{
|
|
sem_post(&searchman->sem_term);
|
|
goto out;
|
|
}
|
|
|
|
status = WaitForSingleObject(mon_fd, 0);
|
|
|
|
if (status == WAIT_FAILED)
|
|
{
|
|
dwError = GetLastError();
|
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", dwError);
|
|
goto out;
|
|
}
|
|
|
|
if (status == WAIT_OBJECT_0)
|
|
{
|
|
dev = udev_monitor_receive_device(mon);
|
|
|
|
if (dev)
|
|
{
|
|
const char* action = udev_device_get_action(dev);
|
|
|
|
if (strcmp(action, "add") == 0)
|
|
{
|
|
long idVendor, idProduct;
|
|
success = 0;
|
|
found = 0;
|
|
idVendor = strtol(udev_device_get_sysattr_value(dev, "idVendor"), NULL, 16);
|
|
|
|
if (errno != 0)
|
|
continue;
|
|
|
|
idProduct = strtol(udev_device_get_sysattr_value(dev, "idProduct"), NULL, 16);
|
|
|
|
if (errno != 0)
|
|
continue;
|
|
|
|
if (idVendor < 0 || idProduct < 0)
|
|
{
|
|
udev_device_unref(dev);
|
|
continue;
|
|
}
|
|
|
|
busnum = strtol(udev_device_get_property_value(dev, "BUSNUM"), NULL, 10);
|
|
|
|
if (errno != 0)
|
|
continue;
|
|
|
|
devnum = strtol(udev_device_get_property_value(dev, "DEVNUM"), NULL, 10);
|
|
|
|
if (errno != 0)
|
|
continue;
|
|
|
|
dvc_channel = channel_mgr->FindChannelById(channel_mgr,
|
|
urbdrc->first_channel_id);
|
|
searchman->rewind(searchman);
|
|
|
|
while (dvc_channel && searchman->has_next(searchman))
|
|
{
|
|
sdev = searchman->get_next(searchman);
|
|
|
|
if (sdev->idVendor == idVendor &&
|
|
sdev->idProduct == idProduct)
|
|
{
|
|
WLog_VRB(TAG, "Searchman Find Device: %04"PRIx16":%04"PRIx16"",
|
|
sdev->idVendor, sdev->idProduct);
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found && udevman->isAutoAdd(udevman))
|
|
{
|
|
WLog_VRB(TAG, "Auto Find Device: %04x:%04x ",
|
|
idVendor, idProduct);
|
|
found = 2;
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
success = udevman->register_udevice(udevman, busnum, devnum,
|
|
searchman->UsbDevice, 0, 0, UDEVMAN_FLAG_ADD_BY_ADDR);
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
searchman->UsbDevice++;
|
|
/* when we send the usb device add request,
|
|
* we will detach the device driver at same
|
|
* time. But, if the time of detach the
|
|
* driver and attach driver is too close,
|
|
* the system will crash. workaround: we
|
|
* wait it for some time to avoid system
|
|
* crash. */
|
|
listobj[0] = searchman->term_event;
|
|
numobj = 1;
|
|
timeout = 4000; /* milliseconds */
|
|
status = WaitForMultipleObjects(numobj, listobj, FALSE, timeout);
|
|
|
|
if (status == WAIT_FAILED)
|
|
{
|
|
dwError = GetLastError();
|
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"!", dwError);
|
|
goto out;
|
|
}
|
|
|
|
status = WaitForSingleObject(searchman->term_event, 0);
|
|
|
|
if (status == WAIT_FAILED)
|
|
{
|
|
dwError = GetLastError();
|
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"!", dwError);
|
|
goto out;
|
|
}
|
|
|
|
if (status == WAIT_OBJECT_0)
|
|
{
|
|
CloseHandle(mon_fd);
|
|
sem_post(&searchman->sem_term);
|
|
return 0;
|
|
}
|
|
|
|
urdbrc_send_virtual_channel_add(dvc_channel, 0);
|
|
|
|
if (found == 1)
|
|
searchman->remove(searchman, sdev->idVendor, sdev->idProduct);
|
|
}
|
|
}
|
|
else if (strcmp(action, "remove") == 0)
|
|
{
|
|
busnum = strtol(udev_device_get_property_value(dev, "BUSNUM"), NULL, 0);
|
|
|
|
if (errno != 0)
|
|
goto out;
|
|
|
|
devnum = strtol(udev_device_get_property_value(dev, "DEVNUM"), NULL, 0);
|
|
|
|
if (errno != 0)
|
|
goto out;
|
|
|
|
usleep(500000);
|
|
udevman->loading_lock(udevman);
|
|
udevman->rewind(udevman);
|
|
|
|
while (udevman->has_next(udevman))
|
|
{
|
|
pdev = udevman->get_next(udevman);
|
|
|
|
if (pdev->get_bus_number(pdev) == busnum && pdev->get_dev_number(pdev) == devnum)
|
|
{
|
|
dvc_channel = channel_mgr->FindChannelById(channel_mgr, pdev->get_channel_id(pdev));
|
|
|
|
if (dvc_channel == NULL)
|
|
{
|
|
WLog_ERR(TAG, "SEARCH: dvc_channel %d is NULL!!", pdev->get_channel_id(pdev));
|
|
func_close_udevice(searchman, pdev);
|
|
break;
|
|
}
|
|
|
|
if (!pdev->isSigToEnd(pdev))
|
|
{
|
|
dvc_channel->Write(dvc_channel, 0, NULL, NULL);
|
|
pdev->SigToEnd(pdev);
|
|
}
|
|
|
|
on_close = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
udevman->loading_unlock(udevman);
|
|
listobj[0] = searchman->term_event;
|
|
numobj = 1;
|
|
timeout = 3000; /* milliseconds */
|
|
status = WaitForMultipleObjects(numobj, listobj, FALSE, timeout);
|
|
|
|
if (status == WAIT_FAILED)
|
|
{
|
|
dwError = GetLastError();
|
|
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"!", dwError);
|
|
goto out;
|
|
}
|
|
|
|
status = WaitForSingleObject(searchman->term_event, 0);
|
|
|
|
if (status == WAIT_FAILED)
|
|
{
|
|
dwError = GetLastError();
|
|
WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"!", dwError);
|
|
goto out;
|
|
}
|
|
|
|
if (status == WAIT_OBJECT_0)
|
|
{
|
|
CloseHandle(mon_fd);
|
|
sem_post(&searchman->sem_term);
|
|
return 0;
|
|
}
|
|
|
|
if (pdev && on_close && dvc_channel && pdev->isSigToEnd(pdev) && !(pdev->isChannelClosed(pdev)))
|
|
{
|
|
on_close = 0;
|
|
dvc_channel->Close(dvc_channel);
|
|
}
|
|
}
|
|
|
|
udev_device_unref(dev);
|
|
}
|
|
else
|
|
{
|
|
WLog_ERR(TAG, "No Device from receive_device(). An error occurred.");
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
CloseHandle(mon_fd);
|
|
fail_create_monfd_event:
|
|
sem_post(&searchman->sem_term);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
void* urbdrc_new_device_create(void* arg)
|
|
{
|
|
TRANSFER_DATA* transfer_data = (TRANSFER_DATA*) arg;
|
|
URBDRC_CHANNEL_CALLBACK* callback = transfer_data->callback;
|
|
IWTSVirtualChannelManager* channel_mgr;
|
|
URBDRC_PLUGIN* urbdrc = transfer_data->urbdrc;
|
|
USB_SEARCHMAN* searchman = urbdrc->searchman;
|
|
BYTE* pBuffer = transfer_data->pBuffer;
|
|
IUDEVMAN* udevman = transfer_data->udevman;
|
|
IUDEVICE* pdev = NULL;
|
|
UINT32 ChannelId = 0;
|
|
UINT32 MessageId;
|
|
UINT32 FunctionId;
|
|
int i = 0, found = 0;
|
|
WLog_DBG(TAG, "...");
|
|
channel_mgr = urbdrc->listener_callback->channel_mgr;
|
|
ChannelId = channel_mgr->GetChannelId(callback->channel);
|
|
data_read_UINT32(pBuffer + 0, MessageId);
|
|
data_read_UINT32(pBuffer + 4, FunctionId);
|
|
int error = 0;
|
|
|
|
switch (urbdrc->vchannel_status)
|
|
{
|
|
case INIT_CHANNEL_IN:
|
|
urbdrc->first_channel_id = ChannelId;
|
|
|
|
if (!searchman->start(searchman, urbdrc_search_usb_device))
|
|
{
|
|
WLog_ERR(TAG, "unable to start searchman thread");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < udevman->get_device_num(udevman); i++)
|
|
error = urdbrc_send_virtual_channel_add(callback->channel, MessageId);
|
|
|
|
urbdrc->vchannel_status = INIT_CHANNEL_OUT;
|
|
break;
|
|
|
|
case INIT_CHANNEL_OUT:
|
|
udevman->loading_lock(udevman);
|
|
udevman->rewind(udevman);
|
|
|
|
while (udevman->has_next(udevman))
|
|
{
|
|
pdev = udevman->get_next(udevman);
|
|
|
|
if (!pdev->isAlreadySend(pdev))
|
|
{
|
|
found = 1;
|
|
pdev->setAlreadySend(pdev);
|
|
pdev->set_channel_id(pdev, ChannelId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
udevman->loading_unlock(udevman);
|
|
|
|
if (found && pdev->isAlreadySend(pdev))
|
|
{
|
|
/* when we send the usb device add request, we will detach
|
|
* the device driver at same time. But, if the time of detach the
|
|
* driver and attach driver is too close, the system will crash.
|
|
* workaround: we wait it for some time to avoid system crash. */
|
|
error = pdev->wait_for_detach(pdev);
|
|
|
|
if (error >= 0)
|
|
urdbrc_send_usb_device_add(callback, pdev);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
WLog_ERR(TAG, "vchannel_status unknown value %"PRIu32"",
|
|
urbdrc->vchannel_status);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_process_channel_notification(URBDRC_CHANNEL_CALLBACK* callback, char* pBuffer,
|
|
UINT32 cbSize)
|
|
{
|
|
UINT32 i;
|
|
UINT32 MessageId;
|
|
UINT32 FunctionId;
|
|
UINT error = CHANNEL_RC_OK;
|
|
URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*) callback->plugin;
|
|
WLog_DBG(TAG, "...");
|
|
data_read_UINT32(pBuffer + 0, MessageId);
|
|
data_read_UINT32(pBuffer + 4, FunctionId);
|
|
|
|
switch (FunctionId)
|
|
{
|
|
case CHANNEL_CREATED:
|
|
error = urbdrc_process_channel_create(callback, pBuffer + 8, cbSize - 8, MessageId);
|
|
break;
|
|
|
|
case RIMCALL_RELEASE:
|
|
WLog_VRB(TAG, "recv RIMCALL_RELEASE");
|
|
pthread_t thread;
|
|
TRANSFER_DATA* transfer_data;
|
|
transfer_data = (TRANSFER_DATA*)malloc(sizeof(TRANSFER_DATA));
|
|
|
|
if (!transfer_data)
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
transfer_data->callback = callback;
|
|
transfer_data->urbdrc = urbdrc;
|
|
transfer_data->udevman = urbdrc->udevman;
|
|
transfer_data->urbdrc = urbdrc;
|
|
transfer_data->cbSize = cbSize;
|
|
transfer_data->pBuffer = (BYTE*) malloc((cbSize));
|
|
|
|
if (!transfer_data->pBuffer)
|
|
{
|
|
free(transfer_data);
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
for (i = 0; i < (cbSize); i++)
|
|
{
|
|
transfer_data->pBuffer[i] = pBuffer[i];
|
|
}
|
|
|
|
if (pthread_create(&thread, 0, urbdrc_new_device_create, transfer_data) != 0)
|
|
{
|
|
free(transfer_data->pBuffer);
|
|
free(transfer_data);
|
|
return ERROR_INVALID_OPERATION;
|
|
}
|
|
|
|
pthread_detach(thread);
|
|
break;
|
|
|
|
default:
|
|
WLog_VRB(TAG, "%s: unknown FunctionId 0x%"PRIX32"", __FUNCTION__, FunctionId);
|
|
error = 1;
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
|
|
{
|
|
URBDRC_CHANNEL_CALLBACK* callback = (URBDRC_CHANNEL_CALLBACK*) pChannelCallback;
|
|
URBDRC_PLUGIN* urbdrc;
|
|
IUDEVMAN* udevman;
|
|
UINT32 InterfaceTemp;
|
|
UINT32 InterfaceId;
|
|
UINT32 Mask;
|
|
UINT error = CHANNEL_RC_OK;
|
|
char* pBuffer = (char*)Stream_Pointer(data);
|
|
UINT32 cbSize = Stream_GetRemainingLength(data);
|
|
|
|
if (callback == NULL)
|
|
return 0;
|
|
|
|
if (callback->plugin == NULL)
|
|
return 0;
|
|
|
|
urbdrc = (URBDRC_PLUGIN*) callback->plugin;
|
|
|
|
if (urbdrc->udevman == NULL)
|
|
return 0;
|
|
|
|
udevman = (IUDEVMAN*) urbdrc->udevman;
|
|
data_read_UINT32(pBuffer + 0, InterfaceTemp);
|
|
InterfaceId = (InterfaceTemp & 0x0fffffff);
|
|
Mask = ((InterfaceTemp & 0xf0000000) >> 30);
|
|
WLog_VRB(TAG, "Size=%"PRIu32" InterfaceId=0x%"PRIX32" Mask=0x%"PRIX32"", cbSize, InterfaceId, Mask);
|
|
|
|
switch (InterfaceId)
|
|
{
|
|
case CAPABILITIES_NEGOTIATOR:
|
|
error = urbdrc_exchange_capabilities(callback, pBuffer + 4, cbSize - 4);
|
|
break;
|
|
|
|
case SERVER_CHANNEL_NOTIFICATION:
|
|
error = urbdrc_process_channel_notification(callback, pBuffer + 4, cbSize - 4);
|
|
break;
|
|
|
|
default:
|
|
WLog_VRB(TAG, "InterfaceId 0x%"PRIX32" Start matching devices list", InterfaceId);
|
|
pthread_t thread;
|
|
TRANSFER_DATA* transfer_data;
|
|
transfer_data = (TRANSFER_DATA*)malloc(sizeof(TRANSFER_DATA));
|
|
|
|
if (!transfer_data)
|
|
{
|
|
WLog_ERR(TAG, "transfer_data is NULL!!");
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
transfer_data->callback = callback;
|
|
transfer_data->urbdrc = urbdrc;
|
|
transfer_data->udevman = udevman;
|
|
transfer_data->cbSize = cbSize - 4;
|
|
transfer_data->UsbDevice = InterfaceId;
|
|
transfer_data->pBuffer = (BYTE*)malloc((cbSize - 4));
|
|
|
|
if (!transfer_data->pBuffer)
|
|
{
|
|
free(transfer_data);
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
memcpy(transfer_data->pBuffer, pBuffer + 4, (cbSize - 4));
|
|
/* To ensure that not too many urb requests at the same time */
|
|
udevman->wait_urb(udevman);
|
|
#if ISOCH_FIFO
|
|
/* lock isoch mutex */
|
|
func_lock_isoch_mutex(transfer_data);
|
|
#endif
|
|
error = pthread_create(&thread, 0, urbdrc_process_udev_data_transfer, transfer_data);
|
|
|
|
if (error != 0)
|
|
{
|
|
WLog_ERR(TAG, "Create Data Transfer Thread got error = %"PRIu32"", error);
|
|
free(transfer_data->pBuffer);
|
|
free(transfer_data);
|
|
return ERROR_INVALID_OPERATION;
|
|
}
|
|
|
|
pthread_detach(thread);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
|
{
|
|
URBDRC_CHANNEL_CALLBACK* callback = (URBDRC_CHANNEL_CALLBACK*) pChannelCallback;
|
|
URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*) callback->plugin;
|
|
IUDEVMAN* udevman = (IUDEVMAN*) urbdrc->udevman;
|
|
USB_SEARCHMAN* searchman = (USB_SEARCHMAN*) urbdrc->searchman;
|
|
IUDEVICE* pdev = NULL;
|
|
UINT32 ChannelId = 0;
|
|
int found = 0;
|
|
ChannelId = callback->channel_mgr->GetChannelId(callback->channel);
|
|
WLog_INFO(TAG, "urbdrc_on_close: channel id %"PRIu32"", ChannelId);
|
|
udevman->loading_lock(udevman);
|
|
udevman->rewind(udevman);
|
|
|
|
while (udevman->has_next(udevman))
|
|
{
|
|
pdev = udevman->get_next(udevman);
|
|
|
|
if (pdev->get_channel_id(pdev) == ChannelId)
|
|
{
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
udevman->loading_unlock(udevman);
|
|
|
|
if (found && pdev && !(pdev->isChannelClosed(pdev)))
|
|
{
|
|
pdev->setChannelClosed(pdev);
|
|
func_close_udevice(searchman, pdev);
|
|
}
|
|
|
|
zfree(callback);
|
|
WLog_DBG(TAG, "success");
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
|
IWTSVirtualChannel* pChannel, BYTE* pData, BOOL* pbAccept, IWTSVirtualChannelCallback** ppCallback)
|
|
{
|
|
URBDRC_LISTENER_CALLBACK* listener_callback = (URBDRC_LISTENER_CALLBACK*) pListenerCallback;
|
|
URBDRC_CHANNEL_CALLBACK* callback;
|
|
WLog_VRB(TAG, "");
|
|
callback = (URBDRC_CHANNEL_CALLBACK*) calloc(1, sizeof(URBDRC_CHANNEL_CALLBACK));
|
|
|
|
if (!callback)
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
callback->iface.OnDataReceived = urbdrc_on_data_received;
|
|
callback->iface.OnClose = urbdrc_on_close;
|
|
callback->plugin = listener_callback->plugin;
|
|
callback->channel_mgr = listener_callback->channel_mgr;
|
|
callback->channel = pChannel;
|
|
*ppCallback = (IWTSVirtualChannelCallback*) callback;
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
|
|
{
|
|
URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*) pPlugin;
|
|
IUDEVMAN* udevman = NULL;
|
|
USB_SEARCHMAN* searchman = NULL;
|
|
WLog_VRB(TAG, "");
|
|
urbdrc->listener_callback = (URBDRC_LISTENER_CALLBACK*) calloc(1, sizeof(URBDRC_LISTENER_CALLBACK));
|
|
|
|
if (!urbdrc->listener_callback)
|
|
return CHANNEL_RC_NO_MEMORY;
|
|
|
|
urbdrc->listener_callback->iface.OnNewChannelConnection = urbdrc_on_new_channel_connection;
|
|
urbdrc->listener_callback->plugin = pPlugin;
|
|
urbdrc->listener_callback->channel_mgr = pChannelMgr;
|
|
/* Init searchman */
|
|
udevman = urbdrc->udevman;
|
|
searchman = searchman_new((void*) urbdrc, udevman->get_defUsbDevice(udevman));
|
|
|
|
if (!searchman)
|
|
{
|
|
free(urbdrc->listener_callback);
|
|
urbdrc->listener_callback = NULL;
|
|
return CHANNEL_RC_NO_MEMORY;
|
|
}
|
|
|
|
urbdrc->searchman = searchman;
|
|
return pChannelMgr->CreateListener(pChannelMgr, "URBDRC", 0,
|
|
(IWTSListenerCallback*) urbdrc->listener_callback, NULL);
|
|
}
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_plugin_terminated(IWTSPlugin* pPlugin)
|
|
{
|
|
URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*) pPlugin;
|
|
IUDEVMAN* udevman = urbdrc->udevman;
|
|
USB_SEARCHMAN* searchman = urbdrc->searchman;
|
|
WLog_VRB(TAG, "");
|
|
|
|
if (searchman)
|
|
{
|
|
/* close searchman */
|
|
searchman->close(searchman);
|
|
|
|
/* free searchman */
|
|
if (searchman->started)
|
|
{
|
|
struct timespec ts;
|
|
ts.tv_sec = time(NULL) + 10;
|
|
ts.tv_nsec = 0;
|
|
sem_timedwait(&searchman->sem_term, &ts);
|
|
}
|
|
|
|
searchman->free(searchman);
|
|
searchman = NULL;
|
|
}
|
|
|
|
if (udevman)
|
|
{
|
|
udevman->free(udevman);
|
|
udevman = NULL;
|
|
}
|
|
|
|
if (urbdrc->listener_callback)
|
|
zfree(urbdrc->listener_callback);
|
|
|
|
if (urbdrc)
|
|
zfree(urbdrc);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
static void urbdrc_register_udevman_addin(IWTSPlugin* pPlugin, IUDEVMAN* udevman)
|
|
{
|
|
URBDRC_PLUGIN* urbdrc = (URBDRC_PLUGIN*) pPlugin;
|
|
|
|
if (urbdrc->udevman)
|
|
{
|
|
WLog_ERR(TAG, "existing device, abort.");
|
|
return;
|
|
}
|
|
|
|
DEBUG_DVC("device registered.");
|
|
urbdrc->udevman = udevman;
|
|
}
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_load_udevman_addin(IWTSPlugin* pPlugin, const char* name, ADDIN_ARGV* args)
|
|
{
|
|
PFREERDP_URBDRC_DEVICE_ENTRY entry;
|
|
FREERDP_URBDRC_SERVICE_ENTRY_POINTS entryPoints;
|
|
entry = (PFREERDP_URBDRC_DEVICE_ENTRY) freerdp_load_channel_addin_entry("urbdrc", (LPSTR) name,
|
|
NULL, 0);
|
|
|
|
if (!entry)
|
|
return ERROR_INVALID_OPERATION;
|
|
|
|
entryPoints.plugin = pPlugin;
|
|
entryPoints.pRegisterUDEVMAN = urbdrc_register_udevman_addin;
|
|
entryPoints.args = args;
|
|
|
|
if (entry(&entryPoints) != 0)
|
|
{
|
|
WLog_ERR(TAG, "%s entry returns error.", name);
|
|
return ERROR_INVALID_OPERATION;
|
|
}
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
BOOL urbdrc_set_subsystem(URBDRC_PLUGIN* urbdrc, char* subsystem)
|
|
{
|
|
free(urbdrc->subsystem);
|
|
urbdrc->subsystem = _strdup(subsystem);
|
|
return (urbdrc->subsystem != NULL);
|
|
}
|
|
|
|
COMMAND_LINE_ARGUMENT_A urbdrc_args[] =
|
|
{
|
|
{ "dbg", COMMAND_LINE_VALUE_FLAG, "", NULL, BoolValueFalse, -1, NULL, "debug" },
|
|
{ "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", NULL, NULL, -1, NULL, "subsystem" },
|
|
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
|
};
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
static UINT urbdrc_process_addin_args(URBDRC_PLUGIN* urbdrc, ADDIN_ARGV* args)
|
|
{
|
|
int status;
|
|
DWORD flags;
|
|
COMMAND_LINE_ARGUMENT_A* arg;
|
|
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
|
|
status = CommandLineParseArgumentsA(args->argc, args->argv,
|
|
urbdrc_args, flags, urbdrc, NULL, NULL);
|
|
|
|
if (status < 0)
|
|
return ERROR_INVALID_DATA;
|
|
|
|
arg = urbdrc_args;
|
|
|
|
do
|
|
{
|
|
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
|
continue;
|
|
|
|
CommandLineSwitchStart(arg)
|
|
CommandLineSwitchCase(arg, "dbg")
|
|
{
|
|
WLog_SetLogLevel(WLog_Get(TAG), WLOG_TRACE);
|
|
}
|
|
CommandLineSwitchCase(arg, "sys")
|
|
{
|
|
if (!urbdrc_set_subsystem(urbdrc, arg->Value))
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
CommandLineSwitchDefault(arg)
|
|
{
|
|
}
|
|
CommandLineSwitchEnd(arg)
|
|
}
|
|
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
|
|
|
return CHANNEL_RC_OK;
|
|
}
|
|
|
|
#ifdef BUILTIN_CHANNELS
|
|
#define DVCPluginEntry urbdrc_DVCPluginEntry
|
|
#else
|
|
#define DVCPluginEntry FREERDP_API DVCPluginEntry
|
|
#endif
|
|
|
|
/**
|
|
* Function description
|
|
*
|
|
* @return 0 on success, otherwise a Win32 error code
|
|
*/
|
|
UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
|
{
|
|
UINT status = 0;
|
|
ADDIN_ARGV* args;
|
|
URBDRC_PLUGIN* urbdrc;
|
|
urbdrc = (URBDRC_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "urbdrc");
|
|
args = pEntryPoints->GetPluginData(pEntryPoints);
|
|
|
|
if (urbdrc == NULL)
|
|
{
|
|
urbdrc = (URBDRC_PLUGIN*) calloc(1, sizeof(URBDRC_PLUGIN));
|
|
|
|
if (!urbdrc)
|
|
return CHANNEL_RC_NO_MEMORY;
|
|
|
|
urbdrc->iface.Initialize = urbdrc_plugin_initialize;
|
|
urbdrc->iface.Connected = NULL;
|
|
urbdrc->iface.Disconnected = NULL;
|
|
urbdrc->iface.Terminated = urbdrc_plugin_terminated;
|
|
urbdrc->searchman = NULL;
|
|
urbdrc->vchannel_status = INIT_CHANNEL_IN;
|
|
status = pEntryPoints->RegisterPlugin(pEntryPoints, "urbdrc", (IWTSPlugin*) urbdrc);
|
|
|
|
if (status != CHANNEL_RC_OK)
|
|
goto error_register;
|
|
}
|
|
|
|
status = urbdrc_process_addin_args(urbdrc, args);
|
|
|
|
if (status != CHANNEL_RC_OK)
|
|
{
|
|
/* TODO: we should unregister the plugin ? */
|
|
WLog_ERR(TAG, "error processing arguments");
|
|
//return status;
|
|
}
|
|
|
|
if (!urbdrc->subsystem && !urbdrc_set_subsystem(urbdrc, "libusb"))
|
|
{
|
|
/* TODO: we should unregister the plugin ? */
|
|
WLog_ERR(TAG, "error setting subsystem");
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
return urbdrc_load_udevman_addin((IWTSPlugin*) urbdrc, urbdrc->subsystem, args);
|
|
error_register:
|
|
free(urbdrc);
|
|
return status;
|
|
}
|