/**
 * 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 <unistd.h>
#if defined(__linux__)
#include <libudev.h>
#endif

#include <errno.h>
#include <winpr/crt.h>

#include "libusb_udevice.h"

#define BASIC_STATE_FUNC_DEFINED(_arg, _type) \
	static _type udev_get_##_arg (IUDEVICE* idev) \
	{ \
		UDEVICE* pdev = (UDEVICE*) idev; \
		return pdev->_arg; \
	} \
	static void udev_set_##_arg (IUDEVICE* idev, _type _t) \
	{ \
		UDEVICE* pdev = (UDEVICE*) idev; \
		pdev->_arg = _t; \
	}

#define BASIC_POINT_FUNC_DEFINED(_arg, _type) \
	static _type udev_get_p_##_arg (IUDEVICE* idev) \
	{ \
		UDEVICE* pdev = (UDEVICE*) idev; \
		return pdev->_arg; \
	} \
	static void udev_set_p_##_arg (IUDEVICE* idev, _type _t) \
	{ \
		UDEVICE* pdev = (UDEVICE*) idev; \
		pdev->_arg = _t; \
	}

#define BASIC_STATE_FUNC_REGISTER(_arg, _dev) \
	_dev->iface.get_##_arg = udev_get_##_arg; \
	_dev->iface.set_##_arg = udev_set_##_arg


typedef struct _ISO_USER_DATA ISO_USER_DATA;

struct _ISO_USER_DATA
{
	BYTE* IsoPacket;
	BYTE* output_data;
	int iso_status;
	int completed;
	UINT32 error_count;
	int noack;
	UINT32 start_frame;
};

static int get_next_timeout(libusb_context* ctx, struct timeval* tv, struct timeval* out)
{
	int r;
	struct timeval timeout;
	r = libusb_get_next_timeout(ctx, &timeout);

	if (r)
	{
		/* timeout already expired? */
		if (!timerisset(&timeout))
			return 1;

		/* choose the smallest of next URB timeout or user specified timeout */
		if (timercmp(&timeout, tv, <))
			*out = timeout;
		else
			*out = *tv;
	}
	else
	{
		*out = *tv;
	}

	return 0;
}

/*
 * a simple wrapper to implement libusb_handle_events_timeout_completed
 * function in libusb library git tree (1.0.9 later) */
static int handle_events_completed(libusb_context* ctx, int* completed)
{
	struct timeval tv;
	tv.tv_sec = 60;
	tv.tv_usec = 0;
#ifdef HAVE_NEW_LIBUSB
	return libusb_handle_events_timeout_completed(ctx, &tv, completed);
#else
	int r;
	struct timeval poll_timeout;
	r = get_next_timeout(ctx, &tv, &poll_timeout);
retry:

	if (libusb_try_lock_events(ctx) == 0)
	{
		if (completed == NULL || !*completed)
		{
			/* we obtained the event lock: do our own event handling */
			WLog_DBG(TAG, "doing our own event handling");
			r = libusb_handle_events_locked(ctx, &tv);
		}

		libusb_unlock_events(ctx);
		return r;
	}

	/* another thread is doing event handling. wait for thread events that
	 * notify event completion. */
	libusb_lock_event_waiters(ctx);

	if (completed && *completed)
		goto already_done;

	if (!libusb_event_handler_active(ctx))
	{
		/* we hit a race: whoever was event handling earlier finished in the
		 * time it took us to reach this point. try the cycle again. */
		libusb_unlock_event_waiters(ctx);
		WLog_DBG(TAG, "event handler was active but went away, retrying");
		goto retry;
	}

	WLog_DBG(TAG, "another thread is doing event handling");
	r = libusb_wait_for_event(ctx, &poll_timeout);
already_done:
	libusb_unlock_event_waiters(ctx);

	if (r < 0)
	{
		return r;
	}
	else if (r == 1)
	{
		return libusb_handle_events_timeout(ctx, &tv);
	}
	else
	{
		return 0;
	}

#endif /* HAVE_NEW_LIBUSE */
}

static void func_iso_callback(struct libusb_transfer* transfer)
{
	ISO_USER_DATA* iso_user_data = (ISO_USER_DATA*) transfer->user_data;
	BYTE* data = iso_user_data->IsoPacket;
	int* completed = &iso_user_data->completed;
	UINT32 offset = 0;
	UINT32 index = 0;
	UINT32 i, act_len;
	BYTE* b;
	*completed = 1;

	/* Fixme: currently fill the dummy frame number, tt needs to be
	 * filled a real frame number */
	// urbdrc_get_mstime(iso_user_data->start_frame);
	if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) && (!iso_user_data->noack))
	{
		for (i = 0; i < transfer->num_iso_packets; i++)
		{
			act_len = transfer->iso_packet_desc[i].actual_length;
			data_write_UINT32(data + offset, index);
			data_write_UINT32(data + offset + 4, act_len);
			data_write_UINT32(data + offset + 8,
			                  transfer->iso_packet_desc[i].status);
			offset += 12;

			if (transfer->iso_packet_desc[i].status == USBD_STATUS_SUCCESS)
			{
				b = libusb_get_iso_packet_buffer_simple(transfer, i);

				if (act_len > 0)
				{
					if (iso_user_data->output_data + index != b)
						memcpy(iso_user_data->output_data + index, b, act_len);

					index += act_len;
				}
				else
				{
					//WLog_ERR(TAG,  "actual length %"PRIu32"", act_len);
					//exit(EXIT_FAILURE);
				}
			}
			else
			{
				iso_user_data->error_count++;
				//print_transfer_status(transfer->iso_packet_desc[i].status);
			}
		}

		transfer->actual_length = index;
		iso_user_data->iso_status = 1;
	}
	else if ((transfer->status == LIBUSB_TRANSFER_COMPLETED) && (iso_user_data->noack))
	{
		/* This situation occurs when we do not need to
		 * return any packet */
		iso_user_data->iso_status = 1;
	}
	else
	{
		//print_status(transfer->status);
		iso_user_data->iso_status = -1;
	}
}

static const LIBUSB_ENDPOINT_DESCEIPTOR* func_get_ep_desc(LIBUSB_CONFIG_DESCRIPTOR* LibusbConfig,
        MSUSB_CONFIG_DESCRIPTOR* MsConfig, UINT32 EndpointAddress)
{
	BYTE alt;
	int inum, pnum;
	MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces;
	const LIBUSB_INTERFACE*	interface;
	const LIBUSB_ENDPOINT_DESCEIPTOR* endpoint;
	MsInterfaces = MsConfig->MsInterfaces;
	interface = LibusbConfig->interface;

	for (inum = 0; inum < MsConfig->NumInterfaces; inum++)
	{
		alt = MsInterfaces[inum]->AlternateSetting;
		endpoint = interface[inum].altsetting[alt].endpoint;

		for (pnum = 0; pnum < MsInterfaces[inum]->NumberOfPipes; pnum++)
		{
			if (endpoint[pnum].bEndpointAddress == EndpointAddress)
			{
				return &endpoint[pnum];
			}
		}
	}

	return NULL;
}

static void func_bulk_transfer_cb(struct libusb_transfer* transfer)
{
	int* completed = transfer->user_data;
	*completed = 1;
	/* caller interprets results and frees transfer */
}

static int func_set_usbd_status(UDEVICE* pdev, UINT32* status, int err_result)
{
	switch (err_result)
	{
		case LIBUSB_SUCCESS:
			*status = USBD_STATUS_SUCCESS;
			break;

		case LIBUSB_ERROR_IO:
			*status = USBD_STATUS_STALL_PID;
			WLog_ERR(TAG, "LIBUSB_ERROR_IO!!");
			break;

		case LIBUSB_ERROR_INVALID_PARAM:
			*status = USBD_STATUS_INVALID_PARAMETER;
			break;

		case LIBUSB_ERROR_ACCESS:
			*status = USBD_STATUS_NOT_ACCESSED;
			break;

		case LIBUSB_ERROR_NO_DEVICE:
			*status = USBD_STATUS_DEVICE_GONE;

			if (pdev)
			{
				if (!(pdev->status & URBDRC_DEVICE_NOT_FOUND))
				{
					pdev->status |= URBDRC_DEVICE_NOT_FOUND;
					WLog_WARN(TAG, "LIBUSB_ERROR_NO_DEVICE!!");
				}
			}

			break;

		case LIBUSB_ERROR_NOT_FOUND:
			*status = USBD_STATUS_STALL_PID;
			break;

		case LIBUSB_ERROR_BUSY:
			*status = USBD_STATUS_STALL_PID;
			break;

		case LIBUSB_ERROR_TIMEOUT:
			*status = USBD_STATUS_TIMEOUT;
			break;

		case LIBUSB_ERROR_OVERFLOW:
			*status = USBD_STATUS_STALL_PID;
			break;

		case LIBUSB_ERROR_PIPE:
			*status = USBD_STATUS_STALL_PID;
			break;

		case LIBUSB_ERROR_INTERRUPTED:
			*status = USBD_STATUS_STALL_PID;
			break;

		case LIBUSB_ERROR_NO_MEM:
			*status = USBD_STATUS_NO_MEMORY;
			break;

		case LIBUSB_ERROR_NOT_SUPPORTED:
			*status = USBD_STATUS_NOT_SUPPORTED;
			break;

		case LIBUSB_ERROR_OTHER:
			*status = USBD_STATUS_STALL_PID;
			break;

		default:
			*status = USBD_STATUS_SUCCESS;
			break;
	}

	return 0;
}

static void func_iso_data_init(ISO_USER_DATA* iso_user_data, UINT32 numPacket, UINT32 buffsize,
                               UINT32 noAck, BYTE* isoPacket, BYTE* buffer)
{
	/* init struct iso_user_data */
	iso_user_data->IsoPacket = isoPacket;
	iso_user_data->output_data = buffer;
	iso_user_data->error_count = 0;
	iso_user_data->completed = 0;
	iso_user_data->noack = noAck;
	urbdrc_get_mstime(iso_user_data->start_frame);
}

static int func_config_release_all_interface(LIBUSB_DEVICE_HANDLE* libusb_handle,
        UINT32 NumInterfaces)
{
	int i, ret;

	for (i = 0; i < NumInterfaces; i++)
	{
		ret = libusb_release_interface(libusb_handle, i);

		if (ret < 0)
		{
			WLog_ERR(TAG,  "config_release_all_interface: error num %d", ret);
			return -1;
		}
	}

	return 0;
}

static int func_claim_all_interface(LIBUSB_DEVICE_HANDLE* libusb_handle, int NumInterfaces)
{
	int i, ret;

	for (i = 0; i < NumInterfaces; i++)
	{
		ret = libusb_claim_interface(libusb_handle, i);

		if (ret < 0)
		{
			WLog_ERR(TAG,  "claim_all_interface: error num %d", ret);
			return -1;
		}
	}

	return 0;
}

/*
static void* print_transfer_status(enum libusb_transfer_status status)
{
	switch (status)
	{
		case LIBUSB_TRANSFER_COMPLETED:
			//WLog_ERR(TAG,  "Transfer Status: LIBUSB_TRANSFER_COMPLETED");
			break;
		case LIBUSB_TRANSFER_ERROR:
			WLog_ERR(TAG,  "Transfer Status: LIBUSB_TRANSFER_ERROR");
			break;
		case LIBUSB_TRANSFER_TIMED_OUT:
			WLog_ERR(TAG,  "Transfer Status: LIBUSB_TRANSFER_TIMED_OUT");
			break;
		case LIBUSB_TRANSFER_CANCELLED:
			WLog_ERR(TAG,  "Transfer Status: LIBUSB_TRANSFER_CANCELLED");
			break;
		case LIBUSB_TRANSFER_STALL:
			WLog_ERR(TAG,  "Transfer Status: LIBUSB_TRANSFER_STALL");
			break;
		case LIBUSB_TRANSFER_NO_DEVICE:
			WLog_ERR(TAG,  "Transfer Status: LIBUSB_TRANSFER_NO_DEVICE");
			break;
		case LIBUSB_TRANSFER_OVERFLOW:
			WLog_ERR(TAG,  "Transfer Status: LIBUSB_TRANSFER_OVERFLOW");
			break;
		default:
			WLog_ERR(TAG,  "Transfer Status: Get unknow error num %d (0x%x)",
					status, status);
	}
	return 0;
}

static void print_status(enum libusb_transfer_status status)
{
	switch (status)
	{
		case LIBUSB_TRANSFER_COMPLETED:
			WLog_ERR(TAG,  "Transfer status: LIBUSB_TRANSFER_COMPLETED");
			break;
		case LIBUSB_TRANSFER_ERROR:
			WLog_ERR(TAG,  "Transfer status: LIBUSB_TRANSFER_ERROR");
			break;
		case LIBUSB_TRANSFER_TIMED_OUT:
			WLog_ERR(TAG,  "Transfer status: LIBUSB_TRANSFER_TIMED_OUT");
			break;
		case LIBUSB_TRANSFER_CANCELLED:
			WLog_ERR(TAG,  "Transfer status: LIBUSB_TRANSFER_CANCELLED");
			break;
		case LIBUSB_TRANSFER_STALL:
			WLog_ERR(TAG,  "Transfer status: LIBUSB_TRANSFER_STALL");
			break;
		case LIBUSB_TRANSFER_NO_DEVICE:
			WLog_ERR(TAG,  "Transfer status: LIBUSB_TRANSFER_NO_DEVICE");
			break;
		case LIBUSB_TRANSFER_OVERFLOW:
			WLog_ERR(TAG,  "Transfer status: LIBUSB_TRANSFER_OVERFLOW");
			break;
		default:
			WLog_ERR(TAG,  "Transfer status: unknow status %d(0x%x)", status, status);
			break;
	}
}
*/

static LIBUSB_DEVICE* udev_get_libusb_dev(int bus_number, int dev_number)
{
	ssize_t i, total_device;
	LIBUSB_DEVICE** libusb_list;
	total_device = libusb_get_device_list(NULL, &libusb_list);

	for (i = 0; i < total_device; i++)
	{
		if ((bus_number == libusb_get_bus_number(libusb_list[i])) &&
		    (dev_number == libusb_get_device_address(libusb_list[i])))
			return libusb_list[i];
	}

	libusb_free_device_list(libusb_list, 1);
	return NULL;
}

static LIBUSB_DEVICE_DESCRIPTOR* udev_new_descript(LIBUSB_DEVICE* libusb_dev)
{
	int ret;
	LIBUSB_DEVICE_DESCRIPTOR* descriptor;
	descriptor = (LIBUSB_DEVICE_DESCRIPTOR*) malloc(sizeof(LIBUSB_DEVICE_DESCRIPTOR));
	ret = libusb_get_device_descriptor(libusb_dev, descriptor);

	if (ret < 0)
	{
		WLog_ERR(TAG,  "libusb_get_device_descriptor: ERROR!!");
		free(descriptor);
		return NULL;
	}

	return descriptor;
}

/* Get HUB handle */
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
static int udev_get_hub_handle(UDEVICE* pdev, UINT16 bus_number, UINT16 dev_number)
{
	int error;
	ssize_t i, total_device, ports_cnt;
	uint8_t port_numbers[16];
	LIBUSB_DEVICE** libusb_list;
	total_device = libusb_get_device_list(NULL, &libusb_list);
	/* Look for device. */
	error = -1;

	for (i = 0; i < total_device; i ++)
	{
		if ((bus_number != libusb_get_bus_number(libusb_list[i])) ||
		    (dev_number != libusb_get_device_address(libusb_list[i])))
			continue;

		error = libusb_open(libusb_list[i], &pdev->hub_handle);

		if (error < 0)
		{
			WLog_ERR(TAG, "libusb_open error: %i - %s", error, libusb_strerror(error));
			break;
		}

		/* get port number */
		error = libusb_get_port_numbers(libusb_list[i], port_numbers, sizeof(port_numbers));
		libusb_close(pdev->hub_handle);

		if (error < 1)
		{
			/* Prevent open hub, treat as error. */
			WLog_ERR(TAG, "libusb_get_port_numbers error: %i - %s", error, libusb_strerror(error));
			break;
		}

		pdev->port_number = port_numbers[(error - 1)];
		error = 0;
		WLog_DBG(TAG, "  Port: %d", pdev->port_number);
		/* gen device path */
		sprintf_s(pdev->path, ARRAYSIZE(pdev->path), "ugen%"PRIu16".%"PRIu16"", bus_number, dev_number);
		WLog_DBG(TAG, "  DevPath: %s", pdev->path);
		break;
	}

	/* Look for device hub. */
	if (error == 0)
	{
		error = -1;

		for (i = 0; i < total_device; i ++)
		{
			if ((bus_number != libusb_get_bus_number(libusb_list[i])) ||
			    (1 != libusb_get_device_address(libusb_list[i]))) /* Root hub allways first on bus. */
				continue;

			WLog_DBG(TAG, "  Open hub: %"PRIu16"", bus_number);
			error = libusb_open(libusb_list[i], &pdev->hub_handle);

			if (error < 0)
				WLog_ERR(TAG, "libusb_open error: %i - %s", error, libusb_strerror(error));

			break;
		}
	}

	libusb_free_device_list(libusb_list, 1);

	if (error < 0)
		return -1;

	WLog_DBG(TAG, "libusb_open success!");
	return 0;
}
#endif
#if defined(__linux__)
static int udev_get_hub_handle(UDEVICE* pdev, UINT16 bus_number, UINT16 dev_number)
{
	struct udev* udev;
	struct udev_enumerate* enumerate;
	struct udev_list_entry* devices;
	struct udev_list_entry* dev_list_entry;
	struct udev_device* dev;
	LIBUSB_DEVICE* libusb_dev;
	int hub_found = 0;
	unsigned long hub_bus = 0;
	unsigned long hub_dev = 0;
	int error = 0;
	udev = udev_new();

	if (!udev)
	{
		WLog_ERR(TAG, "Can't create udev");
		return -1;
	}

	enumerate = udev_enumerate_new(udev);
	udev_enumerate_add_match_subsystem(enumerate, "usb");
	udev_enumerate_add_match_property(enumerate, "DEVTYPE", "usb_device");
	udev_enumerate_scan_devices(enumerate);
	devices = udev_enumerate_get_list_entry(enumerate);
	udev_list_entry_foreach(dev_list_entry, devices)
	{
		const char* path;
		errno = 0;
		path = udev_list_entry_get_name(dev_list_entry);
		dev = udev_device_new_from_syspath(udev, path);

		if (!dev)
			continue;

		unsigned long tmp_b, tmp_d;
		tmp_b = strtoul(udev_device_get_property_value(dev, "BUSNUM"), NULL, 0);

		if (errno != 0)
			continue;

		tmp_d = strtoul(udev_device_get_property_value(dev, "DEVNUM"), NULL, 0);

		if (errno != 0)
			continue;

		if (bus_number == tmp_b && dev_number == tmp_d)
		{
			/* get port number */
			char* p1, *p2;
			const char* sysfs_path =
			    udev_device_get_property_value(dev, "DEVPATH");
			p1 = (char*) sysfs_path;

			do
			{
				p2 = p1 + 1;
				p1 = strchr(p2, '.');
			}
			while (p1 != NULL);

			if ((p2 - sysfs_path) < (strlen(sysfs_path) - 2))
			{
				p1 = (char*) sysfs_path;

				do
				{
					p2 = p1 + 1;
					p1 = strchr(p2, '-');
				}
				while (p1 != NULL);
			}

			errno = 0;
			{
				unsigned long val = strtoul(p2, NULL, 0);

				if ((errno != 0) || (val == 0) || (val > UINT16_MAX))
					continue;

				pdev->port_number = val;
			}
			WLog_DBG(TAG, "  Port: %d", pdev->port_number);
			/* get device path	*/
			p1 = (char*) sysfs_path;

			do
			{
				p2 = p1 + 1;
				p1 = strchr(p2, '/');
			}
			while (p1 != NULL);

			sprintf_s(pdev->path, ARRAYSIZE(pdev->path), "%s", p2);
			WLog_DBG(TAG, "  DevPath: %s", pdev->path);
			/* query parent hub info */
			dev = udev_device_get_parent(dev);

			if (dev != NULL)
			{
				hub_found = 1;
				hub_bus = strtoul(udev_device_get_property_value(dev, "BUSNUM"), NULL, 0);
				hub_dev = strtoul(udev_device_get_property_value(dev, "DEVNUM"), NULL, 0);
				WLog_DBG(TAG, "  Hub BUS/DEV: %d %d", hub_bus, hub_dev);
			}

			udev_device_unref(dev);
			break;
		}

		udev_device_unref(dev);
	}
	udev_enumerate_unref(enumerate);
	udev_unref(udev);

	if (!hub_found)
	{
		WLog_WARN(TAG, "hub was not found!");
		return -1;
	}

	/* Get libusb hub handle */
	libusb_dev = udev_get_libusb_dev(hub_bus, hub_dev);

	if (libusb_dev == NULL)
	{
		WLog_DBG(TAG, "get hub libusb_dev fail!");
		return -1;
	}

	error = libusb_open(libusb_dev, &pdev->hub_handle);

	if (error < 0)
	{
		WLog_DBG(TAG, "libusb_open error!");
		return -1;
	}

	WLog_DBG(TAG, "libusb_open success!");
	/* Success! */
	return 0;
}
#endif

static int libusb_udev_select_interface(IUDEVICE* idev, BYTE InterfaceNumber, BYTE AlternateSetting)
{
	int error = 0, diff = 1;
	UDEVICE* pdev = (UDEVICE*) idev;
	MSUSB_CONFIG_DESCRIPTOR* MsConfig;
	MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces;
	MsConfig = pdev->MsConfig;

	if (MsConfig)
	{
		MsInterfaces = MsConfig->MsInterfaces;

		if ((MsInterfaces) && (MsInterfaces[InterfaceNumber]->AlternateSetting == AlternateSetting))
		{
			diff = 0;
		}
	}

	if (diff)
	{
		error = libusb_set_interface_alt_setting(pdev->libusb_handle,
		        InterfaceNumber, AlternateSetting);

		if (error < 0)
		{
			WLog_ERR(TAG,  "Set interface altsetting get error num %d",
			         error);
		}
	}

	return error;
}

static MSUSB_CONFIG_DESCRIPTOR* libusb_udev_complete_msconfig_setup(IUDEVICE* idev,
        MSUSB_CONFIG_DESCRIPTOR* MsConfig)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces;
	MSUSB_INTERFACE_DESCRIPTOR* MsInterface;
	MSUSB_PIPE_DESCRIPTOR** MsPipes;
	MSUSB_PIPE_DESCRIPTOR* MsPipe;
	MSUSB_PIPE_DESCRIPTOR** t_MsPipes;
	MSUSB_PIPE_DESCRIPTOR* t_MsPipe;
	LIBUSB_CONFIG_DESCRIPTOR* LibusbConfig;
	const LIBUSB_INTERFACE* LibusbInterface;
	const LIBUSB_INTERFACE_DESCRIPTOR* LibusbAltsetting;
	const LIBUSB_ENDPOINT_DESCEIPTOR* LibusbEndpoint;
	BYTE LibusbNumEndpoint;
	int inum = 0, pnum = 0, MsOutSize = 0;
	LibusbConfig = pdev->LibusbConfig;

	if (LibusbConfig->bNumInterfaces != MsConfig->NumInterfaces)
	{
		WLog_ERR(TAG,  "Select Configuration: Libusb NumberInterfaces(%"PRIu8") is different "
		         "with MsConfig NumberInterfaces(%"PRIu32")",
		         LibusbConfig->bNumInterfaces, MsConfig->NumInterfaces);
	}

	/* replace MsPipes for libusb */
	MsInterfaces = MsConfig->MsInterfaces;

	for (inum = 0; inum < MsConfig->NumInterfaces; inum++)
	{
		MsInterface = MsInterfaces[inum];
		/* get libusb's number of endpoints */
		LibusbInterface = &LibusbConfig->interface[MsInterface->InterfaceNumber];
		LibusbAltsetting = &LibusbInterface->altsetting[MsInterface->AlternateSetting];
		LibusbNumEndpoint = LibusbAltsetting->bNumEndpoints;
		t_MsPipes = (MSUSB_PIPE_DESCRIPTOR**) calloc(LibusbNumEndpoint, sizeof(MSUSB_PIPE_DESCRIPTOR*));

		for (pnum = 0; pnum < LibusbNumEndpoint; pnum++)
		{
			t_MsPipe = (MSUSB_PIPE_DESCRIPTOR*) malloc(sizeof(MSUSB_PIPE_DESCRIPTOR));
			memset(t_MsPipe, 0, sizeof(MSUSB_PIPE_DESCRIPTOR));

			if (pnum < MsInterface->NumberOfPipes && MsInterface->MsPipes)
			{
				MsPipe = MsInterface->MsPipes[pnum];
				t_MsPipe->MaximumPacketSize = MsPipe->MaximumPacketSize;
				t_MsPipe->MaximumTransferSize = MsPipe->MaximumTransferSize;
				t_MsPipe->PipeFlags = MsPipe->PipeFlags;
			}
			else
			{
				t_MsPipe->MaximumPacketSize = 0;
				t_MsPipe->MaximumTransferSize = 0xffffffff;
				t_MsPipe->PipeFlags = 0;
			}

			t_MsPipe->PipeHandle = 0;
			t_MsPipe->bEndpointAddress = 0;
			t_MsPipe->bInterval = 0;
			t_MsPipe->PipeType = 0;
			t_MsPipe->InitCompleted = 0;
			t_MsPipes[pnum] = t_MsPipe;
		}

		msusb_mspipes_replace(MsInterface, t_MsPipes, LibusbNumEndpoint);
	}

	/* setup configuration */
	MsOutSize = 8;
	/* ConfigurationHandle:  4 bytes
	   * ---------------------------------------------------------------
	   * ||<<< 1 byte >>>|<<< 1 byte >>>|<<<<<<<<<< 2 byte >>>>>>>>>>>||
	   * ||  bus_number  |  dev_number  |      bConfigurationValue    ||
	   * ---------------------------------------------------------------
	 * ***********************/
	MsConfig->ConfigurationHandle = MsConfig->bConfigurationValue |
	                                (pdev->bus_number << 24) |
	                                (pdev->dev_number << 16);
	MsInterfaces = MsConfig->MsInterfaces;

	for (inum = 0; inum < MsConfig->NumInterfaces; inum++)
	{
		MsOutSize += 16;
		MsInterface = MsInterfaces[inum];
		/* get libusb's interface */
		LibusbInterface = &LibusbConfig->interface[MsInterface->InterfaceNumber];
		LibusbAltsetting = &LibusbInterface->altsetting[MsInterface->AlternateSetting];
		/* InterfaceHandle:  4 bytes
		 * ---------------------------------------------------------------
		 * ||<<< 1 byte >>>|<<< 1 byte >>>|<<< 1 byte >>>|<<< 1 byte >>>||
		 * ||  bus_number  |  dev_number  |  altsetting  | interfaceNum ||
		 * ---------------------------------------------------------------
		 * ***********************/
		MsInterface->InterfaceHandle = LibusbAltsetting->bInterfaceNumber
		                               | (LibusbAltsetting->bAlternateSetting << 8)
		                               | (pdev->dev_number << 16)
		                               | (pdev->bus_number << 24);
		MsInterface->Length = 16 + (MsInterface->NumberOfPipes * 20);
		MsInterface->bInterfaceClass = LibusbAltsetting->bInterfaceClass;
		MsInterface->bInterfaceSubClass = LibusbAltsetting->bInterfaceSubClass;
		MsInterface->bInterfaceProtocol = LibusbAltsetting->bInterfaceProtocol;
		MsInterface->InitCompleted = 1;
		MsPipes = MsInterface->MsPipes;
		LibusbNumEndpoint = LibusbAltsetting->bNumEndpoints;

		for (pnum = 0; pnum < LibusbNumEndpoint; pnum++)
		{
			MsOutSize += 20;
			MsPipe = MsPipes[pnum];
			/* get libusb's endpoint */
			LibusbEndpoint = &LibusbAltsetting->endpoint[pnum];
			/* PipeHandle:  4 bytes
			 * ---------------------------------------------------------------
			 * ||<<< 1 byte >>>|<<< 1 byte >>>|<<<<<<<<<< 2 byte >>>>>>>>>>>||
			 * ||  bus_number  |  dev_number  |      bEndpointAddress       ||
			 * ---------------------------------------------------------------
			 * ***********************/
			MsPipe->PipeHandle = LibusbEndpoint->bEndpointAddress
			                     | (pdev->dev_number << 16)
			                     | (pdev->bus_number << 24);
			/* count endpoint max packet size */
			int max = LibusbEndpoint->wMaxPacketSize & 0x07ff;
			BYTE attr = LibusbEndpoint->bmAttributes;

			if ((attr & 0x3) == 1 || (attr & 0x3) == 3)
			{
				max *= (1 + ((LibusbEndpoint->wMaxPacketSize >> 11) & 3));
			}

			MsPipe->MaximumPacketSize = max;
			MsPipe->bEndpointAddress = LibusbEndpoint->bEndpointAddress;
			MsPipe->bInterval = LibusbEndpoint->bInterval;
			MsPipe->PipeType = attr & 0x3;
			MsPipe->InitCompleted = 1;
		}
	}

	MsConfig->MsOutSize = MsOutSize;
	MsConfig->InitCompleted = 1;

	/* replace device's MsConfig */
	if (!(MsConfig == pdev->MsConfig))
	{
		msusb_msconfig_free(pdev->MsConfig);
		pdev->MsConfig = MsConfig;
	}

	return MsConfig;
}

static int libusb_udev_select_configuration(IUDEVICE* idev, UINT32 bConfigurationValue)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	MSUSB_CONFIG_DESCRIPTOR* MsConfig = pdev->MsConfig;
	LIBUSB_DEVICE_HANDLE* libusb_handle = pdev->libusb_handle;
	LIBUSB_DEVICE* libusb_dev = pdev->libusb_dev;
	LIBUSB_CONFIG_DESCRIPTOR** LibusbConfig = &pdev->LibusbConfig;
	int ret = 0;

	if (MsConfig->InitCompleted)
	{
		func_config_release_all_interface(libusb_handle, (*LibusbConfig)->bNumInterfaces);
	}

	/* The configuration value -1 is mean to put the device in unconfigured state. */
	if (bConfigurationValue == 0)
		ret = libusb_set_configuration(libusb_handle, -1);
	else
		ret = libusb_set_configuration(libusb_handle, bConfigurationValue);

	if (ret < 0)
	{
		WLog_ERR(TAG,  "libusb_set_configuration: ERROR number %d!!", ret);
		func_claim_all_interface(libusb_handle, (*LibusbConfig)->bNumInterfaces);
		return -1;
	}
	else
	{
		ret = libusb_get_active_config_descriptor(libusb_dev, LibusbConfig);

		if (ret < 0)
		{
			WLog_ERR(TAG,  "libusb_get_config_descriptor_by_value: ERROR number %d!!", ret);
			func_claim_all_interface(libusb_handle, (*LibusbConfig)->bNumInterfaces);
			return -1;
		}
	}

	func_claim_all_interface(libusb_handle, (*LibusbConfig)->bNumInterfaces);
	return 0;
}

static int libusb_udev_control_pipe_request(IUDEVICE* idev, UINT32 RequestId,
        UINT32 EndpointAddress, UINT32* UsbdStatus, int command)
{
	int error = 0;
	UDEVICE* pdev = (UDEVICE*) idev;

	/*
	pdev->request_queue->register_request(pdev->request_queue, RequestId, NULL, 0);
	*/
	switch (command)
	{
		case PIPE_CANCEL:
			/** cancel bulk or int transfer */
			idev->cancel_all_transfer_request(idev);
			//dummy_wait_s_obj(1);
			/** set feature to ep (set halt)*/
			error = libusb_control_transfer(pdev->libusb_handle,
			                                LIBUSB_ENDPOINT_OUT | LIBUSB_RECIPIENT_ENDPOINT,
			                                LIBUSB_REQUEST_SET_FEATURE,
			                                ENDPOINT_HALT,
			                                EndpointAddress,
			                                NULL,
			                                0,
			                                1000);
			break;

		case PIPE_RESET:
			idev->cancel_all_transfer_request(idev);
			error = libusb_clear_halt(pdev->libusb_handle, EndpointAddress);
			//func_set_usbd_status(pdev, UsbdStatus, error);
			break;

		default:
			error = -0xff;
			break;
	}

	*UsbdStatus = 0;
	/*
	if(pdev->request_queue->unregister_request(pdev->request_queue, RequestId))
		WLog_ERR(TAG,  "request_queue_unregister_request: not fount request 0x%x", RequestId);
	*/
	return error;
}

static int libusb_udev_control_query_device_text(IUDEVICE* idev, UINT32 TextType,
        UINT32 LocaleId,
        UINT32* BufferSize,
        BYTE* Buffer)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	LIBUSB_DEVICE_DESCRIPTOR* devDescriptor = pdev->devDescriptor;
	char* strDesc = "Generic Usb String";
	char deviceLocation[25];
	BYTE bus_number;
	BYTE device_address;
	int ret = 0, i = 0;

	switch (TextType)
	{
		case DeviceTextDescription:
			ret = libusb_get_string_descriptor(pdev->libusb_handle,
			                                   devDescriptor->iProduct,
			                                   LocaleId,
			                                   Buffer,
			                                   *BufferSize);

			for (i = 0; i < ret; i++)
			{
				Buffer[i] = Buffer[i + 2];
			}

			ret -= 2;

			if (ret <= 0 || ret < 4)
			{
				WLog_DBG(TAG, "libusb_get_string_descriptor: "
				         "ERROR num %d, iProduct: %"PRIu8"!", ret, devDescriptor->iProduct);
				memcpy(Buffer, strDesc, strlen(strDesc));
				Buffer[strlen(strDesc)] = '\0';
				*BufferSize = (strlen((char*)Buffer)) * 2;

				for (i = strlen((char*)Buffer); i > 0; i--)
				{
					Buffer[i * 2] = Buffer[i];
					Buffer[(i * 2) - 1] = 0;
				}
			}
			else
			{
				*BufferSize = ret;
			}

			break;

		case DeviceTextLocationInformation:
			bus_number = libusb_get_bus_number(pdev->libusb_dev);
			device_address = libusb_get_device_address(pdev->libusb_dev);
			sprintf_s(deviceLocation, ARRAYSIZE(deviceLocation), "Port_#%04"PRIu8".Hub_#%04"PRIu8"",
			          device_address, bus_number);

			for (i = 0; i < strlen(deviceLocation); i++)
			{
				Buffer[i * 2] = (BYTE)deviceLocation[i];
				Buffer[(i * 2) + 1] = 0;
			}

			*BufferSize = (i * 2);
			break;

		default:
			WLog_DBG(TAG, "Query Text: unknown TextType %"PRIu32"", TextType);
			break;
	}

	return 0;
}


static int libusb_udev_os_feature_descriptor_request(IUDEVICE* idev, UINT32 RequestId,
        BYTE Recipient,
        BYTE InterfaceNumber,
        BYTE Ms_PageIndex,
        UINT16 Ms_featureDescIndex,
        UINT32* UsbdStatus,
        UINT32* BufferSize,
        BYTE* Buffer,
        int Timeout)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	BYTE ms_string_desc[0x13];
	int error = 0;
	/*
	pdev->request_queue->register_request(pdev->request_queue, RequestId, NULL, 0);
	*/
	memset(ms_string_desc, 0, 0x13);
	error = libusb_control_transfer(pdev->libusb_handle,
	                                LIBUSB_ENDPOINT_IN | Recipient,
	                                LIBUSB_REQUEST_GET_DESCRIPTOR,
	                                0x03ee,
	                                0,
	                                ms_string_desc,
	                                0x12,
	                                Timeout);

	//WLog_ERR(TAG,  "Get ms string: result number %d", error);
	if (error > 0)
	{
		BYTE bMS_Vendorcode;
		data_read_BYTE(ms_string_desc + 16, bMS_Vendorcode);
		//WLog_ERR(TAG,  "bMS_Vendorcode:0x%x", bMS_Vendorcode);
		/** get os descriptor */
		error = libusb_control_transfer(pdev->libusb_handle,
		                                LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | Recipient,
		                                bMS_Vendorcode,
		                                (InterfaceNumber << 8) | Ms_PageIndex,
		                                Ms_featureDescIndex,
		                                Buffer,
		                                *BufferSize,
		                                Timeout);
		*BufferSize = error;
	}

	if (error < 0)
		*UsbdStatus = USBD_STATUS_STALL_PID;
	else
		*UsbdStatus = USBD_STATUS_SUCCESS;

	/*
	if(pdev->request_queue->unregister_request(pdev->request_queue, RequestId))
		WLog_ERR(TAG,  "request_queue_unregister_request: not fount request 0x%x", RequestId);
	*/
	return error;
}

static int libusb_udev_query_device_descriptor(IUDEVICE* idev, int offset)
{
	UDEVICE* pdev = (UDEVICE*) idev;

	switch (offset)
	{
		case B_LENGTH:
			return pdev->devDescriptor->bLength;

		case B_DESCRIPTOR_TYPE:
			return pdev->devDescriptor->bDescriptorType;

		case BCD_USB:
			return pdev->devDescriptor->bcdUSB;

		case B_DEVICE_CLASS:
			return pdev->devDescriptor->bDeviceClass;

		case B_DEVICE_SUBCLASS:
			return pdev->devDescriptor->bDeviceSubClass;

		case B_DEVICE_PROTOCOL:
			return pdev->devDescriptor->bDeviceProtocol;

		case B_MAX_PACKET_SIZE0:
			return pdev->devDescriptor->bMaxPacketSize0;

		case ID_VENDOR:
			return pdev->devDescriptor->idVendor;

		case ID_PRODUCT:
			return pdev->devDescriptor->idProduct;

		case BCD_DEVICE:
			return pdev->devDescriptor->bcdDevice;

		case I_MANUFACTURER:
			return pdev->devDescriptor->iManufacturer;

		case I_PRODUCT:
			return pdev->devDescriptor->iProduct;

		case I_SERIAL_NUMBER:
			return pdev->devDescriptor->iSerialNumber;

		case B_NUM_CONFIGURATIONS:
			return pdev->devDescriptor->bNumConfigurations;

		default:
			return 0;
	}

	return 0;
}

static void libusb_udev_detach_kernel_driver(IUDEVICE* idev)
{
	int i, err = 0;
	UDEVICE* pdev = (UDEVICE*) idev;

	if ((pdev->status & URBDRC_DEVICE_DETACH_KERNEL) == 0)
	{
		for (i = 0; i < pdev->LibusbConfig->bNumInterfaces; i++)
		{
			err = libusb_kernel_driver_active(pdev->libusb_handle, i);
			WLog_DBG(TAG, "libusb_kernel_driver_active = %d", err);

			if (err)
			{
				err = libusb_detach_kernel_driver(pdev->libusb_handle, i);
				WLog_DBG(TAG, "libusb_detach_kernel_driver = %d", err);
			}
		}

		pdev->status |= URBDRC_DEVICE_DETACH_KERNEL;
	}
}

static void libusb_udev_attach_kernel_driver(IUDEVICE* idev)
{
	int i, err = 0;
	UDEVICE* pdev = (UDEVICE*) idev;

	for (i = 0; i < pdev->LibusbConfig->bNumInterfaces && err != LIBUSB_ERROR_NO_DEVICE; i++)
	{
		err = libusb_release_interface(pdev->libusb_handle, i);

		if (err < 0)
		{
			WLog_DBG(TAG, "libusb_release_interface: error num %d = %d", i, err);
		}

		if (err != LIBUSB_ERROR_NO_DEVICE)
		{
			err = libusb_attach_kernel_driver(pdev->libusb_handle, i);
			WLog_DBG(TAG, "libusb_attach_kernel_driver if%d = %d", i, err);
		}
	}
}

static int libusb_udev_is_composite_device(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	return pdev->isCompositeDevice;
}

static int libusb_udev_is_signal_end(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	return (pdev->status & URBDRC_DEVICE_SIGNAL_END) ? 1 : 0;
}

static int libusb_udev_is_exist(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	return (pdev->status & URBDRC_DEVICE_NOT_FOUND) ? 0 : 1;
}

static int libusb_udev_is_channel_closed(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	return (pdev->status & URBDRC_DEVICE_CHANNEL_CLOSED) ? 1 : 0;
}

static int libusb_udev_is_already_send(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	return (pdev->status & URBDRC_DEVICE_ALREADY_SEND) ? 1 : 0;
}

static void libusb_udev_signal_end(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	pdev->status |= URBDRC_DEVICE_SIGNAL_END;
}

static void libusb_udev_channel_closed(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	pdev->status |= URBDRC_DEVICE_CHANNEL_CLOSED;
}

static void libusb_udev_set_already_send(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	pdev->status |= URBDRC_DEVICE_ALREADY_SEND;
}

static char* libusb_udev_get_path(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	return pdev->path;
}

static int libusb_udev_wait_action_completion(IUDEVICE* idev)
{
	int error, sval;
	UDEVICE* pdev = (UDEVICE*) idev;

	while (1)
	{
		usleep(500000);
		error = sem_getvalue(&pdev->sem_id, &sval);

		if (sval == 0)
			break;
	}

	return error;
}

static void libusb_udev_push_action(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	sem_post(&pdev->sem_id);
}

static void libusb_udev_complete_action(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	sem_trywait(&pdev->sem_id);
}

static int libusb_udev_wait_for_detach(IUDEVICE* idev)
{
	int error = 0;
	int times = 0;
	UDEVICE* pdev = (UDEVICE*) idev;

	while (times < 25)
	{
		if (pdev->status & URBDRC_DEVICE_SIGNAL_END)
		{
			error = -1;
			break;
		}

		usleep(200000);
		times++;
	}

	return error;
}

static void libusb_udev_lock_fifo_isoch(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	pthread_mutex_lock(&pdev->mutex_isoch);
}

static void  libusb_udev_unlock_fifo_isoch(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	pthread_mutex_unlock(&pdev->mutex_isoch);
}

static int libusb_udev_query_device_port_status(IUDEVICE* idev, UINT32* UsbdStatus,
        UINT32* BufferSize, BYTE* Buffer)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	int success = 0, ret;
	WLog_DBG(TAG, "...");

	if (pdev->hub_handle != NULL)
	{
		ret = idev->control_transfer(idev, 0xffff, 0, 0,
		                             LIBUSB_ENDPOINT_IN
		                             | LIBUSB_REQUEST_TYPE_CLASS
		                             | LIBUSB_RECIPIENT_OTHER,
		                             LIBUSB_REQUEST_GET_STATUS,
		                             0, pdev->port_number, UsbdStatus, BufferSize, Buffer, 1000);

		if (ret < 0)
		{
			WLog_DBG(TAG, "libusb_control_transfer: error num %d", ret);
			*BufferSize = 0;
		}
		else
		{
			WLog_DBG(TAG, "PORT STATUS:0x%02"PRIx8"%02"PRIx8"%02"PRIx8"%02"PRIx8"",
			         Buffer[3], Buffer[2], Buffer[1], Buffer[0]);
			success = 1;
		}
	}

	return success;
}

static int libusb_udev_request_queue_is_none(IUDEVICE* idev)
{
	UDEVICE* pdev = (UDEVICE*) idev;

	if (pdev->request_queue->request_num == 0)
		return 1;

	return 0;
}

static int libusb_udev_isoch_transfer(IUDEVICE* idev, UINT32 RequestId, UINT32 EndpointAddress,
                                      UINT32 TransferFlags, int NoAck, UINT32* ErrorCount,
                                      UINT32* UrbdStatus, UINT32* StartFrame, UINT32 NumberOfPackets,
                                      BYTE* IsoPacket, UINT32* BufferSize, BYTE* Buffer, int Timeout)
{
	UINT32 iso_packet_size;
	UDEVICE* pdev = (UDEVICE*) idev;
	ISO_USER_DATA iso_user_data;
	struct libusb_transfer* iso_transfer = NULL;
	int status = 0, ret = 0, submit = 0;
	iso_packet_size = *BufferSize / NumberOfPackets;
	iso_transfer = libusb_alloc_transfer(NumberOfPackets);

	if (iso_transfer == NULL)
	{
		WLog_ERR(TAG,  "Error: libusb_alloc_transfer.");
		return -1;
	}

	/**  process URB_FUNCTION_IOSCH_TRANSFER */
	func_iso_data_init(&iso_user_data, NumberOfPackets, *BufferSize, NoAck, IsoPacket, Buffer);
	/** fill setting */
	libusb_fill_iso_transfer(iso_transfer,
	                         pdev->libusb_handle, EndpointAddress, Buffer, *BufferSize,
	                         NumberOfPackets, func_iso_callback, &iso_user_data, 2000);
	libusb_set_iso_packet_lengths(iso_transfer, iso_packet_size);

	if (pdev->status & (URBDRC_DEVICE_SIGNAL_END | URBDRC_DEVICE_NOT_FOUND))
		status = -1;

	iso_user_data.iso_status = 0;

	if (!(status < 0))
	{
		submit = libusb_submit_transfer(iso_transfer);

		if (submit < 0)
		{
			WLog_DBG(TAG, "Error: Failed to submit transfer (ret = %d).", submit);
			status = -1;
			func_set_usbd_status(pdev, UrbdStatus, ret);
		}
	}

#if ISOCH_FIFO

	if (!NoAck)
	{
		idev->unlock_fifo_isoch(idev);
	}

#endif

	while (pdev && iso_user_data.iso_status == 0 && status >= 0 && submit >= 0)
	{
		if (pdev->status & URBDRC_DEVICE_NOT_FOUND)
		{
			status = -1;
			break;
		}

		ret = handle_events_completed(NULL, &iso_user_data.completed);

		if (ret < 0)
		{
			WLog_DBG(TAG, "Error: libusb_handle_events (ret = %d).", ret);
			status = -1;
			break;
		}

#if WAIT_COMPLETE_SLEEP

		if (iso_user_data.iso_status == 0)
		{
			usleep(WAIT_COMPLETE_SLEEP);
		}

#endif
	}

	if (iso_user_data.iso_status < 0)
		status = -1;

	*ErrorCount = iso_user_data.error_count;
	*StartFrame = iso_user_data.start_frame;
	*BufferSize = iso_transfer->actual_length;
	libusb_free_transfer(iso_transfer);
	return status;
}

static int libusb_udev_control_transfer(IUDEVICE* idev, UINT32 RequestId, UINT32 EndpointAddress,
                                        UINT32 TransferFlags, BYTE bmRequestType, BYTE Request, UINT16 Value, UINT16 Index,
                                        UINT32* UrbdStatus, UINT32* BufferSize, BYTE* Buffer, UINT32 Timeout)
{
	int status = 0;
	UDEVICE* pdev = (UDEVICE*) idev;
	status = libusb_control_transfer(pdev->libusb_handle,
	                                 bmRequestType, Request, Value, Index, Buffer, *BufferSize, Timeout);

	if (!(status < 0))
		*BufferSize = status;

	func_set_usbd_status(pdev, UrbdStatus, status);
	return status;
}

static int libusb_udev_bulk_or_interrupt_transfer(IUDEVICE* idev, UINT32 RequestId,
        UINT32 EndpointAddress, UINT32 TransferFlags, UINT32* UsbdStatus, UINT32* BufferSize, BYTE* Buffer,
        UINT32 Timeout)
{
	UINT32 transfer_type;
	UDEVICE* pdev = (UDEVICE*) idev;
	const LIBUSB_ENDPOINT_DESCEIPTOR* ep_desc;
	struct libusb_transfer* transfer = NULL;
	TRANSFER_REQUEST* request = NULL;
	int completed = 0, status = 0, submit = 0;
	int transferDir = EndpointAddress & 0x80;
	/* alloc memory for urb transfer */
	transfer = libusb_alloc_transfer(0);
	ep_desc = func_get_ep_desc(pdev->LibusbConfig, pdev->MsConfig, EndpointAddress);

	if (!ep_desc)
	{
		WLog_ERR(TAG,  "func_get_ep_desc: endpoint 0x%"PRIx32" is not found!!", EndpointAddress);
		return -1;
	}

	transfer_type = (ep_desc->bmAttributes) & 0x3;
	WLog_DBG(TAG, "urb_bulk_or_interrupt_transfer: ep:0x%"PRIx32" "
	         "transfer_type %"PRIu32" flag:%"PRIu32" OutputBufferSize:0x%"PRIx32"",
	         EndpointAddress, transfer_type, TransferFlags, *BufferSize);

	switch (transfer_type)
	{
		case BULK_TRANSFER:
			/** Bulk Transfer */
			//Timeout = 10000;
			break;

		case INTERRUPT_TRANSFER:

			/**  Interrupt Transfer */
			/** Sometime, we may have receive a oversized transfer request,
			 * it make submit urb return error, so we set the length of
			 * request to wMaxPacketSize */
			if (*BufferSize != (ep_desc->wMaxPacketSize))
			{
				WLog_DBG(TAG, "Interrupt Transfer(%s): "
				         "BufferSize is different than maxPacketsize(0x%x)",
				         ((transferDir) ? "IN" : "OUT"), ep_desc->wMaxPacketSize);

				if ((*BufferSize) > (ep_desc->wMaxPacketSize) &&
				    transferDir == USBD_TRANSFER_DIRECTION_IN)
					(*BufferSize) = ep_desc->wMaxPacketSize;
			}

			Timeout = 0;
			break;

		default:
			WLog_DBG(TAG, "urb_bulk_or_interrupt_transfer:"
			         " other transfer type 0x%"PRIX32"", transfer_type);
			return -1;
			break;
	}

	libusb_fill_bulk_transfer(transfer, pdev->libusb_handle, EndpointAddress,
	                          Buffer, *BufferSize, func_bulk_transfer_cb, &completed, Timeout);
	transfer->type = (unsigned char) transfer_type;
	/** Bug fixed in libusb-1.0-8 later: issue of memory crash */
	submit = libusb_submit_transfer(transfer);

	if (submit < 0)
	{
		WLog_DBG(TAG, "libusb_bulk_transfer: error num %d", status);
		func_set_usbd_status(pdev, UsbdStatus, status);
		*BufferSize = 0;
	}
	else
	{
		request = pdev->request_queue->register_request(
		              pdev->request_queue, RequestId, transfer, EndpointAddress);
		request->submit = 1;
	}

	if ((pdev && *UsbdStatus == 0) && (submit >= 0) &&
	    (pdev->iface.isSigToEnd((IUDEVICE*) pdev) == 0))
	{
		while (!completed)
		{
			status = handle_events_completed(NULL, &completed);

			if (status < 0)
			{
				if (status == LIBUSB_ERROR_INTERRUPTED)
					continue;

				libusb_cancel_transfer(transfer);

				while (!completed)
				{
					if (handle_events_completed(NULL, &completed) < 0)
						break;

#if WAIT_COMPLETE_SLEEP

					if (!completed)
						usleep(WAIT_COMPLETE_SLEEP);

#endif
				}

				break;
			}

#if WAIT_COMPLETE_SLEEP

			if (!completed)
				usleep(WAIT_COMPLETE_SLEEP);

#endif
		}

		switch (transfer->status)
		{
			case LIBUSB_TRANSFER_COMPLETED:
				func_set_usbd_status(pdev, UsbdStatus, 0);
				break;

			case LIBUSB_TRANSFER_TIMED_OUT:
				func_set_usbd_status(pdev, UsbdStatus, LIBUSB_ERROR_TIMEOUT);
				break;

			case LIBUSB_TRANSFER_STALL:
				func_set_usbd_status(pdev, UsbdStatus, LIBUSB_ERROR_PIPE);
				break;

			case LIBUSB_TRANSFER_OVERFLOW:
				func_set_usbd_status(pdev, UsbdStatus, LIBUSB_ERROR_OVERFLOW);
				break;

			case LIBUSB_TRANSFER_NO_DEVICE:
				func_set_usbd_status(pdev, UsbdStatus, LIBUSB_ERROR_NO_DEVICE);
				break;

			default:
				func_set_usbd_status(pdev, UsbdStatus, LIBUSB_ERROR_OTHER);
				break;
		}

		*BufferSize = transfer->actual_length;
	}

	WLog_DBG(TAG, "bulk or interrupt Transfer data size : 0x%"PRIx32"", *BufferSize);

	if (request)
	{
		if (pdev->request_queue->unregister_request(pdev->request_queue, RequestId))
			WLog_ERR(TAG,  "request_queue_unregister_request: not fount request 0x%"PRIx32"", RequestId);
	}

	libusb_free_transfer(transfer);
	return 0;
}

static void libusb_udev_cancel_all_transfer_request(IUDEVICE* idev)
{
	int status;
	UDEVICE* pdev = (UDEVICE*) idev;
	REQUEST_QUEUE* request_queue = pdev->request_queue;
	TRANSFER_REQUEST* request = NULL;
	pthread_mutex_lock(&request_queue->request_loading);
	request_queue->rewind(request_queue);

	while (request_queue->has_next(request_queue))
	{
		request = request_queue->get_next(request_queue);

		if ((!request) || (!request->transfer) ||
		    (request->endpoint != request->transfer->endpoint) ||
		    (request->transfer->endpoint == 0) || (request->submit != 1))
		{
			continue;
		}

		status = libusb_cancel_transfer(request->transfer);

		if (status < 0)
		{
			WLog_DBG(TAG, "libusb_cancel_transfer: error num %d!!", status);
		}
		else
		{
			request->submit = -1;
		}
	}

	pthread_mutex_unlock(&request_queue->request_loading);
}

static int func_cancel_xact_request(TRANSFER_REQUEST* request)
{
	int status;

	if (!request)
		return -1;

	if ((!request->transfer) || (request->endpoint != request->transfer->endpoint) ||
	    (request->transfer->endpoint == 0) || (request->submit != 1))
	{
		return 0;
	}

	status = libusb_cancel_transfer(request->transfer);

	if (status < 0)
	{
		WLog_DBG(TAG, "libusb_cancel_transfer: error num %d!!", status);

		if (status == LIBUSB_ERROR_NOT_FOUND)
			return -1;
	}
	else
	{
		WLog_DBG(TAG, "libusb_cancel_transfer: Success num:0x%x!!", request->RequestId);
		request->submit = -1;
		return 1;
	}

	return 0;
}

static int libusb_udev_cancel_transfer_request(IUDEVICE* idev, UINT32 RequestId)
{
	UDEVICE* pdev = (UDEVICE*) idev;
	REQUEST_QUEUE* request_queue = pdev->request_queue;
	TRANSFER_REQUEST* request = NULL;
	int status = 0, retry_times = 0;
cancel_retry:
	pthread_mutex_lock(&request_queue->request_loading);
	request_queue->rewind(request_queue);

	while (request_queue->has_next(request_queue))
	{
		request = request_queue->get_next(request_queue);

		if (!request)
			continue;

		WLog_DBG(TAG, "CancelId:0x%"PRIx32" RequestId:0x%x endpoint 0x%x!!",
		         RequestId, request->RequestId, request->endpoint);

		if (request->RequestId == (RequestId && retry_times <= 10))
		{
			status = func_cancel_xact_request(request);
			break;
		}
		else if ((request->transfer) && (retry_times > 10))
		{
			status = -1;
			break;
		}
	}

	pthread_mutex_unlock(&request_queue->request_loading);

	if ((status == 0) && (retry_times < 10))
	{
		retry_times++;
		usleep(100000);
		WLog_DBG(TAG, "urbdrc_process_cancel_request: go retry!!");
		goto cancel_retry;
	}
	else if ((status < 0) || (retry_times >= 10))
	{
		/** END */
		WLog_DBG(TAG, "urbdrc_process_cancel_request: error go exit!!");
		return -1;
	}

	WLog_DBG(TAG, "urbdrc_process_cancel_request: success!!");
	return 0;
}

BASIC_STATE_FUNC_DEFINED(channel_id, UINT32)
BASIC_STATE_FUNC_DEFINED(UsbDevice, UINT32)
BASIC_STATE_FUNC_DEFINED(ReqCompletion, UINT32)
BASIC_STATE_FUNC_DEFINED(bus_number, UINT16)
BASIC_STATE_FUNC_DEFINED(dev_number, UINT16)
BASIC_STATE_FUNC_DEFINED(port_number, int)
BASIC_STATE_FUNC_DEFINED(isoch_queue, void*)
BASIC_STATE_FUNC_DEFINED(MsConfig, MSUSB_CONFIG_DESCRIPTOR*)

BASIC_POINT_FUNC_DEFINED(udev, void*)
BASIC_POINT_FUNC_DEFINED(prev, void*)
BASIC_POINT_FUNC_DEFINED(next, void*)

static void udev_load_interface(UDEVICE* pdev)
{
	/* load interface */
	/* Basic */
	BASIC_STATE_FUNC_REGISTER(channel_id, pdev);
	BASIC_STATE_FUNC_REGISTER(UsbDevice, pdev);
	BASIC_STATE_FUNC_REGISTER(ReqCompletion, pdev);
	BASIC_STATE_FUNC_REGISTER(bus_number, pdev);
	BASIC_STATE_FUNC_REGISTER(dev_number, pdev);
	BASIC_STATE_FUNC_REGISTER(port_number, pdev);
	BASIC_STATE_FUNC_REGISTER(isoch_queue, pdev);
	BASIC_STATE_FUNC_REGISTER(MsConfig, pdev);
	BASIC_STATE_FUNC_REGISTER(p_udev, pdev);
	BASIC_STATE_FUNC_REGISTER(p_prev, pdev);
	BASIC_STATE_FUNC_REGISTER(p_next, pdev);
	pdev->iface.isCompositeDevice = libusb_udev_is_composite_device;
	pdev->iface.isSigToEnd = libusb_udev_is_signal_end;
	pdev->iface.isExist = libusb_udev_is_exist;
	pdev->iface.isAlreadySend = libusb_udev_is_already_send;
	pdev->iface.isChannelClosed = libusb_udev_is_channel_closed;
	pdev->iface.SigToEnd = libusb_udev_signal_end;
	pdev->iface.setAlreadySend = libusb_udev_set_already_send;
	pdev->iface.setChannelClosed = libusb_udev_channel_closed;
	pdev->iface.getPath = libusb_udev_get_path;
	/* Transfer */
	pdev->iface.isoch_transfer = libusb_udev_isoch_transfer;
	pdev->iface.control_transfer = libusb_udev_control_transfer;
	pdev->iface.bulk_or_interrupt_transfer = libusb_udev_bulk_or_interrupt_transfer;
	pdev->iface.select_interface = libusb_udev_select_interface;
	pdev->iface.select_configuration = libusb_udev_select_configuration;
	pdev->iface.complete_msconfig_setup = libusb_udev_complete_msconfig_setup;
	pdev->iface.control_pipe_request = libusb_udev_control_pipe_request;
	pdev->iface.control_query_device_text = libusb_udev_control_query_device_text;
	pdev->iface.os_feature_descriptor_request = libusb_udev_os_feature_descriptor_request;
	pdev->iface.cancel_all_transfer_request = libusb_udev_cancel_all_transfer_request;
	pdev->iface.cancel_transfer_request = libusb_udev_cancel_transfer_request;
	pdev->iface.query_device_descriptor = libusb_udev_query_device_descriptor;
	pdev->iface.detach_kernel_driver = libusb_udev_detach_kernel_driver;
	pdev->iface.attach_kernel_driver = libusb_udev_attach_kernel_driver;
	pdev->iface.wait_action_completion = libusb_udev_wait_action_completion;
	pdev->iface.push_action = libusb_udev_push_action;
	pdev->iface.complete_action = libusb_udev_complete_action;
	pdev->iface.lock_fifo_isoch = libusb_udev_lock_fifo_isoch;
	pdev->iface.unlock_fifo_isoch = libusb_udev_unlock_fifo_isoch;
	pdev->iface.query_device_port_status = libusb_udev_query_device_port_status;
	pdev->iface.request_queue_is_none = libusb_udev_request_queue_is_none;
	pdev->iface.wait_for_detach = libusb_udev_wait_for_detach;
}

static IUDEVICE* udev_init(UDEVICE* pdev, UINT16 bus_number, UINT16 dev_number)
{
	int status, num;
	LIBUSB_DEVICE_DESCRIPTOR* devDescriptor;
	LIBUSB_CONFIG_DESCRIPTOR* config_temp;
	LIBUSB_INTERFACE_DESCRIPTOR interface_temp;
	/* Get HUB handle */
	status = udev_get_hub_handle(pdev, bus_number, dev_number);

	if (status < 0)
	{
		WLog_ERR(TAG,  "USB init: Error to get HUB handle!!");
		pdev->hub_handle = NULL;
	}

	pdev->devDescriptor = udev_new_descript(pdev->libusb_dev);

	if (!pdev->devDescriptor)
	{
		WLog_ERR(TAG,  "USB init: Error to get device descriptor!!");
		zfree(pdev);
		return NULL;
	}

	num = pdev->devDescriptor->bNumConfigurations;
	status = libusb_get_active_config_descriptor(pdev->libusb_dev, &pdev->LibusbConfig);

	if (status < 0)
	{
		WLog_ERR(TAG,  "libusb_get_descriptor: ERROR!!ret:%d", status);
		zfree(pdev);
		return NULL;
	}

	config_temp = pdev->LibusbConfig;
	/* get the first interface and first altsetting */
	interface_temp = config_temp->interface[0].altsetting[0];
	WLog_DBG(TAG, "Registered Device: Vid: 0x%04"PRIX16" Pid: 0x%04"PRIX16""
	         " InterfaceClass = 0x%02"PRIX8"",
	         pdev->devDescriptor->idVendor,
	         pdev->devDescriptor->idProduct,
	         interface_temp.bInterfaceClass);

	/* Denied list */
	switch (interface_temp.bInterfaceClass)
	{
		case CLASS_RESERVE:

		//case CLASS_COMMUNICATION_IF:
		//case CLASS_HID:
		//case CLASS_PHYSICAL:
		case CLASS_MASS_STORAGE:
		case CLASS_HUB:

		//case CLASS_COMMUNICATION_DATA_IF:
		case CLASS_SMART_CARD:
		case CLASS_CONTENT_SECURITY:
			//case CLASS_WIRELESS_CONTROLLER:
			//case CLASS_ELSE_DEVICE:
			WLog_ERR(TAG,  "    Device is not supported!!");
			zfree(pdev);
			return NULL;

		default:
			break;
	}

	/* Check composite device */
	devDescriptor = pdev->devDescriptor;

	if ((devDescriptor->bNumConfigurations == 1) &&
	    (config_temp->bNumInterfaces > 1) &&
	    (devDescriptor->bDeviceClass == 0x0))
	{
		pdev->isCompositeDevice = 1;
	}
	else if ((devDescriptor->bDeviceClass == 0xef) &&
	         (devDescriptor->bDeviceSubClass == 0x02) &&
	         (devDescriptor->bDeviceProtocol == 0x01))
	{
		pdev->isCompositeDevice = 1;
	}
	else
	{
		pdev->isCompositeDevice = 0;
	}

	/* set device class to first interface class */
	devDescriptor->bDeviceClass = interface_temp.bInterfaceClass;
	devDescriptor->bDeviceSubClass = interface_temp.bInterfaceSubClass;
	devDescriptor->bDeviceProtocol = interface_temp.bInterfaceProtocol;
	/* initialize pdev */
	pdev->prev = NULL;
	pdev->next = NULL;
	pdev->bus_number = bus_number;
	pdev->dev_number = dev_number;
	pdev->status = 0;
	pdev->ReqCompletion = 0;
	pdev->channel_id = 0xffff;
	pdev->request_queue = request_queue_new();
	pdev->isoch_queue = NULL;
	sem_init(&pdev->sem_id, 0, 0);
	/* set config of windows */
	pdev->MsConfig = msusb_msconfig_new();
	pthread_mutex_init(&pdev->mutex_isoch, NULL);
	//deb_config_msg(pdev->libusb_dev, config_temp, devDescriptor->bNumConfigurations);
	udev_load_interface(pdev);
	return (IUDEVICE*) pdev;
}

int udev_new_by_id(UINT16 idVendor, UINT16 idProduct, IUDEVICE** * devArray)
{
	LIBUSB_DEVICE_DESCRIPTOR* descriptor;
	LIBUSB_DEVICE** libusb_list;
	UDEVICE** array;
	UINT16 bus_number;
	UINT16 dev_number;
	ssize_t i, total_device;
	int status, num = 0;
	WLog_INFO(TAG, "VID: 0x%04"PRIX16", PID: 0x%04"PRIX16"", idVendor, idProduct);
	array = (UDEVICE**) malloc(16 * sizeof(UDEVICE*));
	total_device = libusb_get_device_list(NULL, &libusb_list);

	for (i = 0; i < total_device; i++)
	{
		descriptor = udev_new_descript(libusb_list[i]);

		if ((descriptor->idVendor == idVendor) && (descriptor->idProduct == idProduct))
		{
			bus_number = 0;
			dev_number = 0;
			array[num] = (PUDEVICE) malloc(sizeof(UDEVICE));
			array[num]->libusb_dev = libusb_list[i];
			status = libusb_open(libusb_list[i], &array[num]->libusb_handle);

			if (status < 0)
			{
				WLog_ERR(TAG, "libusb_open: (by id) error: 0x%08X (%d)", status, status);
				zfree(descriptor);
				zfree(array[num]);
				continue;
			}

			bus_number = libusb_get_bus_number(libusb_list[i]);
			dev_number = libusb_get_device_address(libusb_list[i]);
			array[num] = (PUDEVICE) udev_init(array[num], bus_number, dev_number);

			if (array[num] != NULL)
				num++;
		}

		zfree(descriptor);
	}

	libusb_free_device_list(libusb_list, 1);
	*devArray = (IUDEVICE**) array;
	return num;
}

IUDEVICE* udev_new_by_addr(int bus_number, int dev_number)
{
	int status;
	UDEVICE* pDev;
	WLog_DBG(TAG, "bus:%d dev:%d", bus_number, dev_number);
	pDev = (PUDEVICE) malloc(sizeof(UDEVICE));
	pDev->libusb_dev = udev_get_libusb_dev(bus_number, dev_number);

	if (pDev->libusb_dev == NULL)
	{
		WLog_ERR(TAG,  "libusb_device_new: ERROR!!");
		zfree(pDev);
		return NULL;
	}

	status = libusb_open(pDev->libusb_dev, &pDev->libusb_handle);

	if (status < 0)
	{
		WLog_ERR(TAG,  "libusb_open: (by addr) ERROR!!");
		zfree(pDev);
		return NULL;
	}

	return udev_init(pDev, bus_number, dev_number);
}
