FreeRDP/channels/urbdrc/client/libusb/libusb_udevman.c
Armin Novak 0c87eaee4d COMMAND_LINE_ARGUMENT structs contain parser results, use one per instance
Remove the old global structs as the parser modifies them. When using
multiple instances in the same process space this could break parsing.
2019-10-28 13:30:05 +01:00

586 lines
14 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/cmdline.h>
#include <freerdp/addin.h>
#include "urbdrc_types.h"
#include "urbdrc_main.h"
#include "libusb_udevice.h"
int libusb_debug;
#define BASIC_STATE_FUNC_DEFINED(_arg, _type) \
static _type udevman_get_##_arg (IUDEVMAN* idevman) \
{ \
UDEVMAN * udevman = (UDEVMAN *) idevman; \
return udevman->_arg; \
} \
static void udevman_set_##_arg (IUDEVMAN* idevman, _type _t) \
{ \
UDEVMAN * udevman = (UDEVMAN *) idevman; \
udevman->_arg = _t; \
}
#define BASIC_STATE_FUNC_REGISTER(_arg, _man) \
_man->iface.get_##_arg = udevman_get_##_arg; \
_man->iface.set_##_arg = udevman_set_##_arg
typedef struct _UDEVMAN UDEVMAN;
struct _UDEVMAN
{
IUDEVMAN iface;
IUDEVICE* idev; /* iterator device */
IUDEVICE* head; /* head device in linked list */
IUDEVICE* tail; /* tail device in linked list */
UINT32 defUsbDevice;
UINT16 flags;
int device_num;
int sem_timeout;
pthread_mutex_t devman_loading;
sem_t sem_urb_lock;
};
typedef UDEVMAN* PUDEVMAN;
static void udevman_rewind(IUDEVMAN* idevman)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
udevman->idev = udevman->head;
}
static int udevman_has_next(IUDEVMAN* idevman)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
if (udevman->idev == NULL)
return 0;
else
return 1;
}
static IUDEVICE* udevman_get_next(IUDEVMAN* idevman)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
IUDEVICE* pdev;
pdev = udevman->idev;
udevman->idev = (IUDEVICE*)((UDEVICE*) udevman->idev)->next;
return pdev;
}
static IUDEVICE* udevman_get_udevice_by_addr(IUDEVMAN* idevman, int bus_number, int dev_number)
{
IUDEVICE* pdev;
idevman->loading_lock(idevman);
idevman->rewind(idevman);
while (idevman->has_next(idevman))
{
pdev = idevman->get_next(idevman);
if ((pdev->get_bus_number(pdev) == bus_number) && (pdev->get_dev_number(pdev) == dev_number))
{
idevman->loading_unlock(idevman);
return pdev;
}
}
idevman->loading_unlock(idevman);
WLog_WARN(TAG, "bus:%d dev:%d not exist in udevman",
bus_number, dev_number);
return NULL;
}
static int udevman_register_udevice(IUDEVMAN* idevman, int bus_number, int dev_number,
int UsbDevice, UINT16 idVendor, UINT16 idProduct, int flag)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
IUDEVICE* pdev = NULL;
IUDEVICE** devArray;
int i, num, addnum = 0;
pdev = (IUDEVICE*) udevman_get_udevice_by_addr(idevman, bus_number, dev_number);
if (pdev != NULL)
return 0;
if (flag == UDEVMAN_FLAG_ADD_BY_ADDR)
{
pdev = udev_new_by_addr(bus_number, dev_number);
if (pdev == NULL)
return 0;
pdev->set_UsbDevice(pdev, UsbDevice);
idevman->loading_lock(idevman);
if (udevman->head == NULL)
{
/* linked list is empty */
udevman->head = pdev;
udevman->tail = pdev;
}
else
{
/* append device to the end of the linked list */
udevman->tail->set_p_next(udevman->tail, pdev);
pdev->set_p_prev(pdev, udevman->tail);
udevman->tail = pdev;
}
udevman->device_num += 1;
idevman->loading_unlock(idevman);
}
else if (flag == UDEVMAN_FLAG_ADD_BY_VID_PID)
{
addnum = 0;
/* register all device that match pid vid */
num = udev_new_by_id(idVendor, idProduct, &devArray);
for (i = 0; i < num; i++)
{
pdev = devArray[i];
if (udevman_get_udevice_by_addr(idevman,
pdev->get_bus_number(pdev), pdev->get_dev_number(pdev)) != NULL)
{
zfree(pdev);
continue;
}
pdev->set_UsbDevice(pdev, UsbDevice);
idevman->loading_lock(idevman);
if (udevman->head == NULL)
{
/* linked list is empty */
udevman->head = pdev;
udevman->tail = pdev;
}
else
{
/* append device to the end of the linked list */
udevman->tail->set_p_next(udevman->tail, pdev);
pdev->set_p_prev(pdev, udevman->tail);
udevman->tail = pdev;
}
udevman->device_num += 1;
idevman->loading_unlock(idevman);
addnum++;
}
zfree(devArray);
return addnum;
}
else
{
WLog_ERR(TAG, "udevman_register_udevice: function error!!");
return 0;
}
return 1;
}
static int udevman_unregister_udevice(IUDEVMAN* idevman, int bus_number, int dev_number)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
UDEVICE* pdev, * dev;
int ret = 0, err = 0;
dev = (UDEVICE*) udevman_get_udevice_by_addr(idevman, bus_number, dev_number);
idevman->loading_lock(idevman);
idevman->rewind(idevman);
while (idevman->has_next(idevman) != 0)
{
pdev = (UDEVICE*) idevman->get_next(idevman);
if (pdev == dev) /* device exists */
{
/* set previous device to point to next device */
if (dev->prev != NULL)
{
/* unregistered device is not the head */
pdev = dev->prev;
pdev->next = dev->next;
}
else
{
/* unregistered device is the head, update head */
udevman->head = (IUDEVICE*)dev->next;
}
/* set next device to point to previous device */
if (dev->next != NULL)
{
/* unregistered device is not the tail */
pdev = (UDEVICE*)dev->next;
pdev->prev = dev->prev;
}
else
{
/* unregistered device is the tail, update tail */
udevman->tail = (IUDEVICE*)dev->prev;
}
udevman->device_num--;
break;
}
}
idevman->loading_unlock(idevman);
if (dev)
{
/* reset device */
if (err != LIBUSB_ERROR_NO_DEVICE)
{
ret = libusb_reset_device(dev->libusb_handle);
if (ret < 0)
{
WLog_ERR(TAG, "libusb_reset_device: ERROR!!ret:%d", ret);
}
}
/* release all interface and attach kernel driver */
dev->iface.attach_kernel_driver((IUDEVICE*)dev);
if (dev->request_queue) zfree(dev->request_queue);
/* free the config descriptor that send from windows */
msusb_msconfig_free(dev->MsConfig);
libusb_close(dev->libusb_handle);
libusb_close(dev->hub_handle);
sem_destroy(&dev->sem_id);
/* free device info */
if (dev->devDescriptor)
zfree(dev->devDescriptor);
if (dev)
zfree(dev);
return 1; /* unregistration successful */
}
/* if we reach this point, the device wasn't found */
return 0;
}
static void udevman_parse_device_addr(char* str, int* id1, int* id2, char sign)
{
char s1[8];
char* s2;
ZeroMemory(s1, sizeof(s1));
s2 = (strchr(str, sign)) + 1;
strncpy(s1, str, strlen(str) - (strlen(s2) + 1));
*id1 = strtol(s1, NULL, 0);
*id2 = strtol(s2, NULL, 0);
}
static void udevman_parse_device_pid_vid(char* str, int* id1, int* id2, char sign)
{
char s1[8];
char* s2;
ZeroMemory(s1, sizeof(s1));
s2 = (strchr(str, sign)) + 1;
strncpy(s1, str, strlen(str) - (strlen(s2) + 1));
*id1 = strtol(s1, NULL, 16);
*id2 = strtol(s2, NULL, 16);
}
static int udevman_check_device_exist_by_id(IUDEVMAN* idevman, UINT16 idVendor, UINT16 idProduct)
{
if (libusb_open_device_with_vid_pid(NULL, idVendor, idProduct))
return 1;
return 0;
}
static int udevman_is_auto_add(IUDEVMAN* idevman)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
return (udevman->flags & UDEVMAN_FLAG_ADD_BY_AUTO) ? 1 : 0;
}
static IUDEVICE* udevman_get_udevice_by_UsbDevice_try_again(IUDEVMAN* idevman, UINT32 UsbDevice)
{
UDEVICE* pdev;
idevman->loading_lock(idevman);
idevman->rewind(idevman);
while (idevman->has_next(idevman))
{
pdev = (UDEVICE*) idevman->get_next(idevman);
if (pdev->UsbDevice == UsbDevice)
{
idevman->loading_unlock(idevman);
return (IUDEVICE*) pdev;
}
}
idevman->loading_unlock(idevman);
return NULL;
}
static IUDEVICE* udevman_get_udevice_by_UsbDevice(IUDEVMAN* idevman, UINT32 UsbDevice)
{
UDEVICE* pdev;
idevman->loading_lock(idevman);
idevman->rewind(idevman);
while (idevman->has_next(idevman))
{
pdev = (UDEVICE*) idevman->get_next(idevman);
if (pdev->UsbDevice == UsbDevice)
{
idevman->loading_unlock(idevman);
return (IUDEVICE*) pdev;
}
}
idevman->loading_unlock(idevman);
/* try again */
pdev = (UDEVICE*) idevman->get_udevice_by_UsbDevice_try_again(idevman, UsbDevice);
if (pdev)
{
return (IUDEVICE*) pdev;
}
WLog_ERR(TAG, "0x%"PRIx32" ERROR!!", UsbDevice);
return NULL;
}
static void udevman_loading_lock(IUDEVMAN* idevman)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
pthread_mutex_lock(&udevman->devman_loading);
}
static void udevman_loading_unlock(IUDEVMAN* idevman)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
pthread_mutex_unlock(&udevman->devman_loading);
}
static void udevman_wait_urb(IUDEVMAN* idevman)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
sem_wait(&udevman->sem_urb_lock);
}
static void udevman_push_urb(IUDEVMAN* idevman)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
sem_post(&udevman->sem_urb_lock);
}
BASIC_STATE_FUNC_DEFINED(defUsbDevice, UINT32)
BASIC_STATE_FUNC_DEFINED(device_num, int)
BASIC_STATE_FUNC_DEFINED(sem_timeout, int)
static void udevman_free(IUDEVMAN* idevman)
{
UDEVMAN* udevman = (UDEVMAN*) idevman;
pthread_mutex_destroy(&udevman->devman_loading);
sem_destroy(&udevman->sem_urb_lock);
libusb_exit(NULL);
/* free udevman */
if (udevman)
zfree(udevman);
}
static void udevman_load_interface(UDEVMAN* udevman)
{
/* standard */
udevman->iface.free = udevman_free;
/* manage devices */
udevman->iface.rewind = udevman_rewind;
udevman->iface.get_next = udevman_get_next;
udevman->iface.has_next = udevman_has_next;
udevman->iface.register_udevice = udevman_register_udevice;
udevman->iface.unregister_udevice = udevman_unregister_udevice;
udevman->iface.get_udevice_by_UsbDevice = udevman_get_udevice_by_UsbDevice;
udevman->iface.get_udevice_by_UsbDevice_try_again =
udevman_get_udevice_by_UsbDevice_try_again;
/* Extension */
udevman->iface.check_device_exist_by_id = udevman_check_device_exist_by_id;
udevman->iface.isAutoAdd = udevman_is_auto_add;
/* Basic state */
BASIC_STATE_FUNC_REGISTER(defUsbDevice, udevman);
BASIC_STATE_FUNC_REGISTER(device_num, udevman);
BASIC_STATE_FUNC_REGISTER(sem_timeout, udevman);
/* control semaphore or mutex lock */
udevman->iface.loading_lock = udevman_loading_lock;
udevman->iface.loading_unlock = udevman_loading_unlock;
udevman->iface.push_urb = udevman_push_urb;
udevman->iface.wait_urb = udevman_wait_urb;
}
static void urbdrc_udevman_register_devices(UDEVMAN* udevman, char* devices)
{
char* token;
int idVendor;
int idProduct;
int bus_number;
int dev_number;
int success = 0;
char hardware_id[16];
char* default_devices = "id";
UINT32 UsbDevice = BASE_USBDEVICE_NUM;
if (!devices)
devices = default_devices;
/* register all usb devices */
token = strtok(devices, "#");
while (token)
{
bus_number = 0;
dev_number = 0;
idVendor = 0;
idProduct = 0;
sprintf_s(hardware_id, ARRAYSIZE(hardware_id), "%s", token);
token = strtok(NULL, "#");
if (udevman->flags & UDEVMAN_FLAG_ADD_BY_VID_PID)
{
udevman_parse_device_pid_vid(hardware_id, &idVendor, &idProduct, ':');
success = udevman->iface.register_udevice((IUDEVMAN*) udevman,
0, 0, UsbDevice, (UINT16) idVendor, (UINT16) idProduct, UDEVMAN_FLAG_ADD_BY_VID_PID);
}
else if (udevman->flags & UDEVMAN_FLAG_ADD_BY_ADDR)
{
udevman_parse_device_addr(hardware_id, &bus_number, &dev_number, ':');
success = udevman->iface.register_udevice((IUDEVMAN*) udevman,
bus_number, dev_number, UsbDevice, 0, 0, UDEVMAN_FLAG_ADD_BY_ADDR);
}
if (success)
UsbDevice++;
}
udevman->defUsbDevice = UsbDevice;
}
static void urbdrc_udevman_parse_addin_args(UDEVMAN* udevman, ADDIN_ARGV* args)
{
int status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
COMMAND_LINE_ARGUMENT_A urbdrc_udevman_args[] =
{
{ "dbg", COMMAND_LINE_VALUE_FLAG, "", NULL, BoolValueFalse, -1, NULL, "debug" },
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<devices>", NULL, NULL, -1, NULL, "device list" },
{ "id", COMMAND_LINE_VALUE_FLAG, "", NULL, BoolValueFalse, -1, NULL, "FLAG_ADD_BY_VID_PID" },
{ "addr", COMMAND_LINE_VALUE_FLAG, "", NULL, BoolValueFalse, -1, NULL, "FLAG_ADD_BY_ADDR" },
{ "auto", COMMAND_LINE_VALUE_FLAG, "", NULL, BoolValueFalse, -1, NULL, "FLAG_ADD_BY_AUTO" },
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
status = CommandLineParseArgumentsA(args->argc, args->argv,
urbdrc_udevman_args, flags, udevman, NULL, NULL);
arg = urbdrc_udevman_args;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
CommandLineSwitchStart(arg)
CommandLineSwitchCase(arg, "dbg")
{
WLog_SetLogLevel(WLog_Get(TAG), WLOG_TRACE);
}
CommandLineSwitchCase(arg, "dev")
{
urbdrc_udevman_register_devices(udevman, arg->Value);
}
CommandLineSwitchCase(arg, "id")
{
udevman->flags = UDEVMAN_FLAG_ADD_BY_VID_PID;
}
CommandLineSwitchCase(arg, "addr")
{
udevman->flags = UDEVMAN_FLAG_ADD_BY_ADDR;
}
CommandLineSwitchCase(arg, "auto")
{
udevman->flags |= UDEVMAN_FLAG_ADD_BY_AUTO;
}
CommandLineSwitchDefault(arg)
{
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
}
#ifdef BUILTIN_CHANNELS
#define freerdp_urbdrc_client_subsystem_entry libusb_freerdp_urbdrc_client_subsystem_entry
#else
#define freerdp_urbdrc_client_subsystem_entry FREERDP_API freerdp_urbdrc_client_subsystem_entry
#endif
int freerdp_urbdrc_client_subsystem_entry(PFREERDP_URBDRC_SERVICE_ENTRY_POINTS pEntryPoints)
{
UDEVMAN* udevman;
ADDIN_ARGV* args = pEntryPoints->args;
libusb_init(NULL);
udevman = (PUDEVMAN) malloc(sizeof(UDEVMAN));
if (!udevman)
return -1;
udevman->device_num = 0;
udevman->idev = NULL;
udevman->head = NULL;
udevman->tail = NULL;
udevman->sem_timeout = 0;
udevman->flags = UDEVMAN_FLAG_ADD_BY_VID_PID;
pthread_mutex_init(&udevman->devman_loading, NULL);
sem_init(&udevman->sem_urb_lock, 0, MAX_URB_REQUSET_NUM);
/* load usb device service management */
udevman_load_interface(udevman);
/* set debug flag, to enable Debug message for usb data transfer */
libusb_debug = 10;
urbdrc_udevman_parse_addin_args(udevman, args);
pEntryPoints->pRegisterUDEVMAN(pEntryPoints->plugin, (IUDEVMAN*) udevman);
WLog_DBG(TAG, "UDEVMAN device registered.");
return 0;
}