/** * FreeRDP: A Remote Desktop Protocol Implementation * RemoteFX USB Redirection * * Copyright 2012 Atrust corp. * Copyright 2012 Alfred Liu * * 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 #include #include #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); LLOGLN(libusb_debug, ("%s: bus:%d dev:%d not exist in udevman", __func__, 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 { printf("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){ LLOGLN(10, ("libusb_reset_device: ERROR!!ret:%d\n", 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], *s2; memset(s1, 0, sizeof(s1)); s2 = (strchr(str, sign)) + 1; strncpy(s1, str, strlen(str) - (strlen(s2)+1)); *id1 = atoi(s1); *id2 = atoi(s2); } static void udevman_parse_device_pid_vid (char *str, int *id1, int *id2, char sign) { char s1[8], *s2; memset(s1, 0, sizeof(s1)); s2 = (strchr(str, sign)) + 1; strncpy(s1, str, strlen(str) - (strlen(s2)+1)); *id1 = (int) strtol(s1, NULL, 16); *id2 = (int) 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; } LLOGLN(libusb_debug, ("udevman_get_udevice_by_UsbDevice: 0x%x ERROR!!\n", 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; } int FreeRDPUDEVMANEntry(PFREERDP_URBDRC_SERVICE_ENTRY_POINTS pEntryPoints) { UDEVMAN* udevman; RDP_PLUGIN_DATA * plugin_data = pEntryPoints->plugin_data; UINT32 UsbDevice = BASE_USBDEVICE_NUM; char * token; char * message = "id"; char hardware_id[16]; int idVendor; int idProduct; int bus_number; int dev_number; int success = 0; libusb_init(NULL); udevman = (PUDEVMAN)malloc(sizeof(UDEVMAN)); 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*/ if (plugin_data->data[2]) message = (char *)plugin_data->data[2]; if (strstr(message, "id")) udevman->flags = UDEVMAN_FLAG_ADD_BY_VID_PID; else if (strstr(message, "addr")) udevman->flags = UDEVMAN_FLAG_ADD_BY_ADDR; if (strstr(message, "auto")) udevman->flags |= UDEVMAN_FLAG_ADD_BY_AUTO; libusb_debug = 10; if (strstr(message, "debug")) { libusb_debug = 0; udevman->flags |= UDEVMAN_FLAG_DEBUG; } /* register all usb device */ token = strtok((char *)plugin_data->data[1], "#"); while (token) { bus_number = 0; dev_number = 0; idVendor = 0; idProduct = 0; strcpy(hardware_id, 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; pEntryPoints->pRegisterUDEVMAN(pEntryPoints->plugin, (IUDEVMAN*) udevman); return 0; }