FreeRDP/channels/urbdrc/client/libusb/libusb_udevman.c
2018-08-27 14:34:09 +02:00

587 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;
}
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 }
};
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;
_snprintf(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;
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;
}