/**
 * 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>

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "urbdrc_types.h"
#include "data_transfer.h"

static void usb_process_get_port_status(IUDEVICE* pdev, BYTE* OutputBuffer)
{
	int bcdUSB = pdev->query_device_descriptor(pdev, BCD_USB);

	switch (bcdUSB)
	{
		case USB_v1_0:
			data_write_UINT32(OutputBuffer, 0x303);
			break;

		case USB_v1_1:
			data_write_UINT32(OutputBuffer, 0x103);
			break;

		case USB_v2_0:
			data_write_UINT32(OutputBuffer, 0x503);
			break;

		default:
			data_write_UINT32(OutputBuffer, 0x503);
			break;
	}
}

#if ISOCH_FIFO

static int func_check_isochronous_fds(IUDEVICE* pdev)
{
	int ret = 0;
	BYTE* data_temp;
	UINT32 size_temp, process_times = 2;
	ISOCH_CALLBACK_QUEUE* isoch_queue = NULL;
	ISOCH_CALLBACK_DATA* isoch = NULL;
	URBDRC_CHANNEL_CALLBACK* callback;

	isoch_queue = (ISOCH_CALLBACK_QUEUE*) pdev->get_isoch_queue(pdev);

	while (process_times)
	{
		process_times--;

		if (isoch_queue == NULL || !pdev)
			return -1;

		pthread_mutex_lock(&isoch_queue->isoch_loading);

		if (isoch_queue->head == NULL)
		{
			pthread_mutex_unlock(&isoch_queue->isoch_loading);
			continue;
		}
		else
		{
			isoch = isoch_queue->head;
		}

		if (!isoch || !isoch->out_data)
		{
			pthread_mutex_unlock(&isoch_queue->isoch_loading);
			continue;
		}
		else
		{
			callback = (URBDRC_CHANNEL_CALLBACK*) isoch->callback;
			size_temp = isoch->out_size;
			data_temp = isoch->out_data;

			ret = isoch_queue->unregister_data(isoch_queue, isoch);

			if (!ret)
				WLog_DBG(TAG, "isoch_queue_unregister_data: Not found isoch data!!");

			pthread_mutex_unlock(&isoch_queue->isoch_loading);

			if (pdev && !pdev->isSigToEnd(pdev))
			{
				callback->channel->Write(callback->channel, size_temp, data_temp, NULL);
				zfree(data_temp);
			}
		}
	}

	return 0;
}

#endif

static int urbdrc_process_register_request_callback(URBDRC_CHANNEL_CALLBACK* callback,
		BYTE* data, UINT32 data_sizem, IUDEVMAN* udevman, UINT32 UsbDevice)
{
	IUDEVICE* pdev;
	UINT32 NumRequestCompletion = 0;
	UINT32 RequestCompletion = 0;

	WLog_DBG(TAG, "urbdrc_process_register_request_callback");

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	if (data_sizem >= 8)
	{
		data_read_UINT32(data + 0, NumRequestCompletion); /** must be 1 */
		/** RequestCompletion:
		*   unique Request Completion interface for the client to use */
		data_read_UINT32(data + 4, RequestCompletion);
		pdev->set_ReqCompletion(pdev, RequestCompletion);
	}
	else /** Unregister the device */
	{
		data_read_UINT32(data + 0, RequestCompletion);

		if (1)//(pdev->get_ReqCompletion(pdev) == RequestCompletion)
		{
			/** The wrong driver may also receive this message, So we
			 *  need some time(default 3s) to check the driver or delete
			 *  it */
			sleep(3);
			callback->channel->Write(callback->channel, 0, NULL, NULL);
			pdev->SigToEnd(pdev);
		}
	}

	return 0;
}

static int urbdrc_process_cancel_request(BYTE* data, UINT32 data_sizem, IUDEVMAN* udevman, UINT32 UsbDevice)
{
	IUDEVICE* pdev;
	UINT32 CancelId;
	int error = 0;

	data_read_UINT32(data + 0, CancelId); /** RequestId */

	WLog_DBG(TAG, "urbdrc_process_cancel_request: id 0x%x", CancelId);

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	error = pdev->cancel_transfer_request(pdev, CancelId);

	return error;
}

static int urbdrc_process_retract_device_request(BYTE* data, UINT32 data_sizem, IUDEVMAN* udevman, UINT32 UsbDevice)
{
	UINT32 Reason;
	WLog_DBG(TAG, "urbdrc_process_retract_device_request");

	data_read_UINT32(data + 0, Reason); /** Reason */

	switch (Reason)
	{
		case UsbRetractReason_BlockedByPolicy:
			WLog_DBG(TAG, "UsbRetractReason_BlockedByPolicy: now it is not support");
			return -1;
			break;

		default:
			WLog_DBG(TAG, "urbdrc_process_retract_device_request: Unknown Reason %d", Reason);
			return -1;
			break;
	}

	return 0;
}

static int urbdrc_process_io_control(URBDRC_CHANNEL_CALLBACK* callback, BYTE* data,
		UINT32 data_sizem, UINT32 MessageId, IUDEVMAN * udevman, UINT32 UsbDevice)
{
	IUDEVICE* pdev;
	UINT32 out_size;
	UINT32 InterfaceId;
	UINT32 IoControlCode;
	UINT32 InputBufferSize;
	UINT32 OutputBufferSize;
	UINT32 RequestId;
	UINT32 usbd_status = USBD_STATUS_SUCCESS;
	BYTE* OutputBuffer;
	BYTE* out_data;
	int i, offset, success = 0;

	WLog_DBG(TAG, "urbdrc_process__io_control");

	data_read_UINT32(data + 0, IoControlCode);
	data_read_UINT32(data + 4, InputBufferSize);
	data_read_UINT32(data + 8 + InputBufferSize, OutputBufferSize);
	data_read_UINT32(data + 12 + InputBufferSize, RequestId);

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));

	/**  process */
	OutputBuffer = (BYTE *)malloc(OutputBufferSize);
	memset(OutputBuffer, 0, OutputBufferSize);

	switch (IoControlCode)
	{
		case IOCTL_INTERNAL_USB_SUBMIT_URB:  /** 0x00220003 */
			WLog_DBG(TAG, "ioctl: IOCTL_INTERNAL_USB_SUBMIT_URB");
			WLog_ERR(TAG,  " Function IOCTL_INTERNAL_USB_SUBMIT_URB: Unchecked");
			break;

		case IOCTL_INTERNAL_USB_RESET_PORT:  /** 0x00220007 */
			WLog_DBG(TAG, "ioctl: IOCTL_INTERNAL_USB_RESET_PORT");
			break;

		case IOCTL_INTERNAL_USB_GET_PORT_STATUS: /** 0x00220013 */
			WLog_DBG(TAG, "ioctl: IOCTL_INTERNAL_USB_GET_PORT_STATUS");

			success = pdev->query_device_port_status(pdev, &usbd_status, &OutputBufferSize, OutputBuffer);

			if (success)
			{
				if (pdev->isExist(pdev) == 0)
				{
					data_write_UINT32(OutputBuffer, 0);
				}
				else
				{
					usb_process_get_port_status(pdev, OutputBuffer);
					OutputBufferSize = 4;
				}

				WLog_DBG(TAG, "PORT STATUS(fake!):0x%02x%02x%02x%02x",
					OutputBuffer[3], OutputBuffer[2], OutputBuffer[1], OutputBuffer[0]);
			}

			break;

		case IOCTL_INTERNAL_USB_CYCLE_PORT:  /** 0x0022001F */
			WLog_DBG(TAG, "ioctl: IOCTL_INTERNAL_USB_CYCLE_PORT");
			WLog_ERR(TAG,  " Function IOCTL_INTERNAL_USB_CYCLE_PORT: Unchecked");
			break;

		case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: /** 0x00220027 */
			WLog_DBG(TAG, "ioctl: IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION");
			WLog_ERR(TAG,  " Function IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION: Unchecked");
			break;

		default:
			WLog_DBG(TAG, "urbdrc_process_io_control: unknown IoControlCode 0x%X", IoControlCode);
			zfree(OutputBuffer);
			return -1;
			break;
	}

	offset = 28;
	out_size = offset + OutputBufferSize;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);
	data_write_UINT32(out_data + 0, InterfaceId); /** interface */
	data_write_UINT32(out_data + 4, MessageId); /** message id */
	data_write_UINT32(out_data + 8, IOCONTROL_COMPLETION); /** function id */
	data_write_UINT32(out_data + 12, RequestId); /** RequestId */
	data_write_UINT32(out_data + 16, USBD_STATUS_SUCCESS); /** HResult */
	data_write_UINT32(out_data + 20, OutputBufferSize); /** Information */
	data_write_UINT32(out_data + 24, OutputBufferSize); /** OutputBufferSize */

	for (i=0;i<OutputBufferSize;i++)
	{
		data_write_BYTE(out_data + offset, OutputBuffer[i]); /** OutputBuffer */
		offset += 1;
	}

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);

	zfree(out_data);
	zfree(OutputBuffer);

	return 0;
}

static int urbdrc_process_internal_io_control(URBDRC_CHANNEL_CALLBACK* callback, BYTE* data,
		UINT32 data_sizem, UINT32 MessageId, IUDEVMAN* udevman, UINT32 UsbDevice)
{
	IUDEVICE* pdev;
	BYTE* out_data;
	UINT32 out_size, IoControlCode, InterfaceId, InputBufferSize;
	UINT32 OutputBufferSize, RequestId, frames;

	data_read_UINT32(data + 0, IoControlCode);

	WLog_DBG(TAG, "urbdrc_process_internal_io_control:0x%x", IoControlCode);

	data_read_UINT32(data + 4, InputBufferSize);
	data_read_UINT32(data + 8, OutputBufferSize);
	data_read_UINT32(data + 12, RequestId);

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));

	/** Fixme: Currently this is a FALSE bustime... */
	urbdrc_get_mstime(frames);

	out_size = 32;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);
	data_write_UINT32(out_data + 0, InterfaceId); /** interface */
	data_write_UINT32(out_data + 4, MessageId); /** message id */
	data_write_UINT32(out_data + 8, IOCONTROL_COMPLETION); /** function id */
	data_write_UINT32(out_data + 12, RequestId); /** RequestId */
	data_write_UINT32(out_data + 16, 0); /** HResult */
	data_write_UINT32(out_data + 20, 4); /** Information */
	data_write_UINT32(out_data + 24, 4); /** OutputBufferSize */
	data_write_UINT32(out_data + 28, frames); /** OutputBuffer */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);

	zfree(out_data);

	return 0;
}

static int urbdrc_process_query_device_text(URBDRC_CHANNEL_CALLBACK* callback, BYTE* data,
		UINT32 data_sizem, UINT32 MessageId, IUDEVMAN* udevman, UINT32 UsbDevice)
{
	IUDEVICE* pdev;
	UINT32 out_size;
	UINT32 InterfaceId;
	UINT32 TextType;
	UINT32 LocaleId;
	UINT32 bufferSize = 1024;
	BYTE* out_data;
	BYTE DeviceDescription[bufferSize];
	int out_offset;

	WLog_DBG(TAG, "urbdrc_process_query_device_text");

	data_read_UINT32(data + 0, TextType);
	data_read_UINT32(data + 4, LocaleId);

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	pdev->control_query_device_text(pdev, TextType, LocaleId, &bufferSize, DeviceDescription);

	InterfaceId = ((STREAM_ID_STUB << 30) | UsbDevice);

	out_offset = 16;
	out_size = out_offset + bufferSize;

	if (bufferSize != 0)
		out_size += 2;

	out_data = (BYTE*) malloc(out_size);
	memset(out_data, 0, out_size);

	data_write_UINT32(out_data + 0, InterfaceId); /** interface */
	data_write_UINT32(out_data + 4, MessageId); /** message id */

	if (bufferSize != 0)
	{
		data_write_UINT32(out_data + 8, (bufferSize/2)+1); /** cchDeviceDescription */
		out_offset = 12;
		memcpy(out_data + out_offset, DeviceDescription, bufferSize);
		out_offset += bufferSize;
		data_write_UINT16(out_data + out_offset, 0x0000);
		out_offset += 2;
	}
	else
	{
		data_write_UINT32(out_data + 8, 0); /** cchDeviceDescription */
		out_offset = 12;
	}

	data_write_UINT32(out_data + out_offset, 0); /** HResult */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);

	zfree(out_data);

	return 0;
}

static void func_select_all_interface_for_msconfig(IUDEVICE* pdev, MSUSB_CONFIG_DESCRIPTOR* MsConfig)
{
	int inum;
	MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = MsConfig->MsInterfaces;
	BYTE  InterfaceNumber, AlternateSetting;
	UINT32 NumInterfaces = MsConfig->NumInterfaces;

	for (inum = 0; inum < NumInterfaces; inum++)
	{
		InterfaceNumber = MsInterfaces[inum]->InterfaceNumber;
		AlternateSetting = MsInterfaces[inum]->AlternateSetting;
		pdev->select_interface(pdev, InterfaceNumber, AlternateSetting);
	}
}

static int urb_select_configuration(URBDRC_CHANNEL_CALLBACK* callback, BYTE* data,
	UINT32 data_sizem, UINT32 MessageId, IUDEVMAN* udevman, UINT32 UsbDevice, int transferDir)
{
	MSUSB_CONFIG_DESCRIPTOR * MsConfig = NULL;
	IUDEVICE* pdev = NULL;
	UINT32 out_size, InterfaceId, RequestId, NumInterfaces, usbd_status = 0;
	BYTE ConfigurationDescriptorIsValid;
	BYTE* out_data;
	int MsOutSize = 0, offset = 0;

	if (transferDir == 0)
	{
		WLog_ERR(TAG,  "urb_select_configuration: not support transfer out");
		return -1;
	}

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));
	data_read_UINT32(data + 0, RequestId);
	data_read_BYTE(data + 4, ConfigurationDescriptorIsValid);
	data_read_UINT32(data + 8, NumInterfaces);
	offset = 12;

	/** if ConfigurationDescriptorIsValid is zero, then just do nothing.*/
	if (ConfigurationDescriptorIsValid)
	{
		/* parser data for struct config */
		MsConfig = msusb_msconfig_read(data + offset, data_sizem - offset, NumInterfaces);
		/* select config */
		pdev->select_configuration(pdev, MsConfig->bConfigurationValue);
		/* select all interface */
		func_select_all_interface_for_msconfig(pdev, MsConfig);
		/* complete configuration setup */
		MsConfig = pdev->complete_msconfig_setup(pdev, MsConfig);
	}

	if (MsConfig)
		MsOutSize = MsConfig->MsOutSize;

	if (MsOutSize > 0)
		out_size = 36 + MsOutSize;
	else
		out_size = 44;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);		/** message id */
	data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);	/** function id */
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	if (MsOutSize > 0)
	{
		/** CbTsUrbResult */
		data_write_UINT32(out_data + 16, 8 + MsOutSize);
		/** TS_URB_RESULT_HEADER Size*/
		data_write_UINT16(out_data + 20, 8 + MsOutSize);
	}
	else
	{
		data_write_UINT32(out_data + 16, 16);
		data_write_UINT16(out_data + 20, 16);
	}

	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_SELECT_CONFIGURATION);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */
	offset = 28;
	/** TS_URB_SELECT_CONFIGURATION_RESULT */
	if (MsOutSize > 0)
	{
		msusb_msconfig_write(MsConfig, out_data, &offset);
	}
	else
	{
		data_write_UINT32(out_data + offset, 0);	/** ConfigurationHandle */
		data_write_UINT32(out_data + offset + 4, NumInterfaces);	/** NumInterfaces */
		offset += 8;
	}
	data_write_UINT32(out_data + offset, 0);	/** HResult */
	data_write_UINT32(out_data + offset + 4, 0);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);
	zfree(out_data);
	return 0;
}

static int urb_select_interface(URBDRC_CHANNEL_CALLBACK* callback, BYTE* data, UINT32 data_sizem,
	UINT32 MessageId, IUDEVMAN* udevman, UINT32 UsbDevice, int transferDir)
{
	MSUSB_CONFIG_DESCRIPTOR* MsConfig;
	MSUSB_INTERFACE_DESCRIPTOR* MsInterface;
	IUDEVICE* pdev;
	UINT32 out_size, InterfaceId, RequestId, ConfigurationHandle;
	UINT32 OutputBufferSize;
	BYTE InterfaceNumber;
	BYTE* out_data;
	int out_offset, interface_size;

	if (transferDir == 0)
	{
		WLog_ERR(TAG,  "urb_select_interface: not support transfer out");
		return -1;
	}

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev));

	data_read_UINT32(data + 0, RequestId);
	data_read_UINT32(data + 4, ConfigurationHandle);
	out_offset = 8;

	MsInterface = msusb_msinterface_read(data + out_offset, data_sizem - out_offset, &out_offset);

	data_read_UINT32(data + out_offset, OutputBufferSize);

	pdev->select_interface(pdev, MsInterface->InterfaceNumber, MsInterface->AlternateSetting);

	/* replace device's MsInterface */
	MsConfig = pdev->get_MsConfig(pdev);
	InterfaceNumber = MsInterface->InterfaceNumber;
	msusb_msinterface_replace(MsConfig, InterfaceNumber, MsInterface);

	/* complete configuration setup */
	MsConfig = pdev->complete_msconfig_setup(pdev, MsConfig);
	MsInterface = MsConfig->MsInterfaces[InterfaceNumber];
	interface_size = 16 + (MsInterface->NumberOfPipes * 20);

	out_size = 36 + interface_size ;
	out_data = (BYTE*) malloc(out_size);
	memset(out_data, 0, out_size);

	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);		/** message id */
	data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);	/** function id */
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 8 + interface_size);	/** CbTsUrbResult */
	/** TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 8 + interface_size);	/** Size */
	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_SELECT_INTERFACE);
	data_write_UINT32(out_data + 24, USBD_STATUS_SUCCESS);	/** UsbdStatus */
	out_offset = 28;

	/** TS_URB_SELECT_INTERFACE_RESULT */
	msusb_msinterface_write(MsInterface, out_data + out_offset, &out_offset);

	data_write_UINT32(out_data + out_offset, 0);	/** HResult */
	data_write_UINT32(out_data + out_offset + 4, OutputBufferSize);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);

	zfree(out_data);

	return 0;
}

static int urb_control_transfer(URBDRC_CHANNEL_CALLBACK* callback, BYTE* data,
	UINT32 data_sizem, UINT32 MessageId, IUDEVMAN* udevman, UINT32 UsbDevice, int transferDir, int External)
{
	IUDEVICE* pdev;
	UINT32 out_size, RequestId, InterfaceId, EndpointAddress, PipeHandle;
	UINT32 TransferFlags, OutputBufferSize, usbd_status, Timeout;
	BYTE bmRequestType, Request;
	UINT16 Value, Index, length;
	BYTE* buffer;
	BYTE* out_data;
	int offset, ret;

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev));
	data_read_UINT32(data + 0, RequestId);
	data_read_UINT32(data + 4, PipeHandle);
	data_read_UINT32(data + 8, TransferFlags); /** TransferFlags */

	EndpointAddress = (PipeHandle & 0x000000ff);
	offset  = 12;
	Timeout = 2000;

	switch (External)
	{
		case URB_CONTROL_TRANSFER_EXTERNAL:
			data_read_UINT32(data + offset, Timeout); /** TransferFlags */
			offset += 4;
			break;
		case URB_CONTROL_TRANSFER_NONEXTERNAL:
			break;
	}

	/** SetupPacket 8 bytes */
	data_read_BYTE(data + offset, bmRequestType);
	data_read_BYTE(data + offset + 1, Request);
	data_read_UINT16(data + offset + 2, Value);
	data_read_UINT16(data + offset + 4, Index);
	data_read_UINT16(data + offset + 6, length);
	data_read_UINT32(data + offset + 8, OutputBufferSize);
	offset +=  12;

	if (length != OutputBufferSize)
	{
		WLog_ERR(TAG, "urb_control_transfer ERROR: buf != length");
		return -1;
	}

	out_size = 36 + OutputBufferSize;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);

	buffer = out_data + 36;

	/** Get Buffer Data */
	if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
		memcpy(buffer, data + offset, OutputBufferSize);

	/**  process URB_FUNCTION_CONTROL_TRANSFER */
	ret = pdev->control_transfer(
		pdev, RequestId, EndpointAddress, TransferFlags,
		bmRequestType,
		Request,
		Value,
		Index,
		&usbd_status,
		&OutputBufferSize,
		buffer,
		Timeout);

	if (ret < 0){
		WLog_DBG(TAG, "control_transfer: error num %d!!", ret);
		OutputBufferSize = 0;
	}

	/** send data */
	offset = 36;
	if (transferDir == USBD_TRANSFER_DIRECTION_IN)
		out_size = offset + OutputBufferSize;
	else
		out_size = offset;

	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);		/** message id */

	if(transferDir == USBD_TRANSFER_DIRECTION_IN && OutputBufferSize != 0)
		data_write_UINT32(out_data + 8, URB_COMPLETION);	/** function id */
	else
		data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 0x00000008); 	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 0x0008);	/** Size */

	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_CONTROL_TRANSFER);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */

	data_write_UINT32(out_data + 28, 0);	/** HResult */
	data_write_UINT32(out_data + 32, OutputBufferSize);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);

	zfree(out_data);

	return 0;
}

static int urb_bulk_or_interrupt_transfer(URBDRC_CHANNEL_CALLBACK* callback, BYTE* data,
	UINT32 data_sizem, UINT32 MessageId, IUDEVMAN* udevman, UINT32 UsbDevice, int transferDir)
{
	int offset;
	BYTE* Buffer;
	IUDEVICE* pdev;
	BYTE* out_data;
	UINT32 out_size, RequestId, InterfaceId, EndpointAddress, PipeHandle;
	UINT32 TransferFlags, OutputBufferSize, usbd_status = 0;

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev));

	data_read_UINT32(data + 0, RequestId);
	data_read_UINT32(data + 4, PipeHandle);
	data_read_UINT32(data + 8, TransferFlags);	/** TransferFlags */
	data_read_UINT32(data + 12, OutputBufferSize);
	offset = 16;
	EndpointAddress = (PipeHandle & 0x000000ff);

	if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
		out_size = 36;
	else
		out_size = 36 + OutputBufferSize;

	Buffer = NULL;
	out_data = (BYTE*) malloc(out_size);
	memset(out_data, 0, out_size);

	switch (transferDir)
	{
		case USBD_TRANSFER_DIRECTION_OUT:
			Buffer = data + offset;
			break;

		case USBD_TRANSFER_DIRECTION_IN:
			Buffer = out_data + 36;
			break;
	}

	/**  process URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER */
	pdev->bulk_or_interrupt_transfer(
		pdev, RequestId, EndpointAddress,
		TransferFlags,
		&usbd_status,
		&OutputBufferSize,
		Buffer,
		10000);

	offset = 36;
	if (transferDir == USBD_TRANSFER_DIRECTION_IN)
		out_size = offset + OutputBufferSize;
	else
		out_size = offset;
	/** send data */
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);	/** message id */
	if(transferDir == USBD_TRANSFER_DIRECTION_IN && OutputBufferSize != 0)
		data_write_UINT32(out_data + 8, URB_COMPLETION);	/** function id */
	else
		data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 0x00000008);	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 0x0008);	/** Size */

	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */

	data_write_UINT32(out_data + 28, 0);	/** HResult */
	data_write_UINT32(out_data + 32, OutputBufferSize);	/** OutputBufferSize */

	if (pdev && !pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);

	zfree(out_data);

	return 0;
}


static int urb_isoch_transfer(URBDRC_CHANNEL_CALLBACK * callback, BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	int transferDir)
{
	IUDEVICE * pdev;
	UINT32	RequestId, InterfaceId, EndpointAddress;
	UINT32	PipeHandle, TransferFlags, StartFrame, NumberOfPackets;
	UINT32	ErrorCount, OutputBufferSize, usbd_status = 0;
	UINT32	RequestField, noAck = 0;
	UINT32	out_size = 0;
	BYTE *	iso_buffer	= NULL;
	BYTE *	iso_packets	= NULL;
	BYTE *	out_data	= NULL;
	int	offset, nullBuffer = 0, iso_status;

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);
	if (pdev == NULL)
		return 0;
	if (pdev->isSigToEnd(pdev))
		return 0;

	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));
	data_read_UINT32(data + 0, RequestField);
	RequestId			= RequestField & 0x7fffffff;
	noAck				= (RequestField & 0x80000000)>>31;
	data_read_UINT32(data + 4, PipeHandle);
	EndpointAddress		= (PipeHandle & 0x000000ff);
	data_read_UINT32(data + 8, TransferFlags); /** TransferFlags */
	data_read_UINT32(data + 12, StartFrame); /** StartFrame */
	data_read_UINT32(data + 16, NumberOfPackets); /** NumberOfPackets */
	data_read_UINT32(data + 20, ErrorCount); /** ErrorCount */
	offset = 24 + (NumberOfPackets * 12);
	data_read_UINT32(data + offset, OutputBufferSize);
	offset += 4;

	/** send data memory alloc */
	if (transferDir == USBD_TRANSFER_DIRECTION_OUT) {
		if (!noAck) {
			out_size = 48 + (NumberOfPackets * 12);
			out_data = (BYTE *) malloc(out_size);
			iso_packets = out_data + 40;
		}
	}
	else {
		out_size = 48 + OutputBufferSize + (NumberOfPackets * 12);
		out_data = (BYTE *) malloc(out_size);
		iso_packets = out_data + 40;
	}

	if (out_size)
		memset(out_data, 0, out_size);

	switch (transferDir)
	{
		case USBD_TRANSFER_DIRECTION_OUT:
			/** Get Buffer Data */
			//memcpy(iso_buffer, data + offset, OutputBufferSize);
			iso_buffer = data + offset;
			break;
		case USBD_TRANSFER_DIRECTION_IN:
			iso_buffer = out_data + 48 + (NumberOfPackets * 12);
			break;
	}

	WLog_DBG(TAG, "urb_isoch_transfer: EndpointAddress: 0x%x, "
		"TransferFlags: 0x%x, " "StartFrame: 0x%x, "
		"NumberOfPackets: 0x%x, " "OutputBufferSize: 0x%x "
		"RequestId: 0x%x",
		EndpointAddress, TransferFlags, StartFrame,
		NumberOfPackets, OutputBufferSize, RequestId);

#if ISOCH_FIFO
	ISOCH_CALLBACK_QUEUE * isoch_queue = NULL;
	ISOCH_CALLBACK_DATA * isoch = NULL;
	if(!noAck)
	{
		isoch_queue = (ISOCH_CALLBACK_QUEUE *)pdev->get_isoch_queue(pdev);
		isoch = isoch_queue->register_data(isoch_queue, callback, pdev);
	}
#endif

	iso_status = pdev->isoch_transfer(
		pdev, RequestId, EndpointAddress,
		TransferFlags,
		noAck,
		&ErrorCount,
		&usbd_status,
		&StartFrame,
		NumberOfPackets,
		iso_packets,
		&OutputBufferSize,
		iso_buffer,
		2000);

	if(noAck)
	{
		zfree(out_data);
		return 0;
	}

	if (iso_status < 0)
		nullBuffer = 1;


	out_size = 48;
	if (nullBuffer)
		OutputBufferSize = 0;
	else
		out_size += OutputBufferSize + (NumberOfPackets * 12);
	/* fill the send data */
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);	/** message id */
	if(OutputBufferSize != 0 && !nullBuffer)
		data_write_UINT32(out_data + 8, URB_COMPLETION);	/** function id */
	else
		data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 20 + (NumberOfPackets * 12));	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 20 + (NumberOfPackets * 12));	/** Size */
	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_ISOCH_TRANSFER);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */

	data_write_UINT32(out_data + 28, StartFrame);	/** StartFrame */
	if (!nullBuffer)
	{
		/** NumberOfPackets */
		data_write_UINT32(out_data + 32, NumberOfPackets);
		data_write_UINT32(out_data + 36, ErrorCount);	/** ErrorCount */
		offset = 40 + (NumberOfPackets * 12);
	}
	else
	{
		data_write_UINT32(out_data + 32, 0);	/** NumberOfPackets */
		data_write_UINT32(out_data + 36, NumberOfPackets);	/** ErrorCount */
		offset = 40;
	}

	data_write_UINT32(out_data + offset, 0);	/** HResult */
	data_write_UINT32(out_data + offset + 4, OutputBufferSize);	/** OutputBufferSize */

#if ISOCH_FIFO
	if(!noAck){
		pthread_mutex_lock(&isoch_queue->isoch_loading);
		isoch->out_data = out_data;
		isoch->out_size = out_size;
		pthread_mutex_unlock(&isoch_queue->isoch_loading);
	}
#else
	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);
	zfree(out_data);
#endif

	if (nullBuffer)
		return -1;

	return 0;
}

static int urb_control_descriptor_request(URBDRC_CHANNEL_CALLBACK* callback,
	BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	BYTE func_recipient,
	int transferDir)
{
	IUDEVICE* pdev;
	UINT32 out_size, InterfaceId, RequestId, OutputBufferSize, usbd_status;
	BYTE bmRequestType, desc_index, desc_type;
	UINT16 langId;
	BYTE* buffer;
	BYTE* out_data;
	int ret, offset;

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));
	data_read_UINT32(data + 0, RequestId);
	data_read_BYTE(data + 4, desc_index);
	data_read_BYTE(data + 5, desc_type);
	data_read_UINT16(data + 6, langId);
	data_read_UINT32(data + 8, OutputBufferSize);

	out_size = 36 + OutputBufferSize;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);

	buffer = out_data + 36;

	bmRequestType = func_recipient;
	switch (transferDir)
	{
		case USBD_TRANSFER_DIRECTION_IN:
			bmRequestType |= 0x80;
			break;
		case USBD_TRANSFER_DIRECTION_OUT:
			bmRequestType |= 0x00;
			offset = 12;
			memcpy(buffer, data + offset, OutputBufferSize);
			break;
		default:
			WLog_DBG(TAG, "get error transferDir");
			OutputBufferSize = 0;
			usbd_status = USBD_STATUS_STALL_PID;
			break;
	}

	/** process get usb device descriptor */

	ret = pdev->control_transfer(
		pdev, RequestId, 0, 0, bmRequestType,
		0x06, /* REQUEST_GET_DESCRIPTOR */
		(desc_type << 8) | desc_index,
		langId,
		&usbd_status,
		&OutputBufferSize,
		buffer,
		1000);


	if (ret < 0) {
		WLog_DBG(TAG, "get_descriptor: error num %d", ret);
		OutputBufferSize = 0;
	}

	offset = 36;
	out_size = offset + OutputBufferSize;
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);	/** message id */
	data_write_UINT32(out_data + 8, URB_COMPLETION);	/** function id */
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 0x00000008);	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 0x0008);	/** Size */
	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */
	data_write_UINT32(out_data + 28, 0);	/** HResult */
	data_write_UINT32(out_data + 32, OutputBufferSize);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);

	zfree(out_data);
	return 0;
}

static int urb_control_get_status_request(URBDRC_CHANNEL_CALLBACK * callback, BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	BYTE func_recipient,
	int transferDir)
{
	IUDEVICE* pdev;
	UINT32 out_size, RequestId, InterfaceId, OutputBufferSize, usbd_status;
	UINT16 Index;
	BYTE bmRequestType;
	BYTE* buffer;
	BYTE* out_data;
	int offset, ret;

	if (transferDir == 0){
		WLog_DBG(TAG, "urb_control_get_status_request: not support transfer out");
		return -1;
	}

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);
	if (pdev == NULL)
		return 0;
	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));

	data_read_UINT32(data + 0, RequestId);
	data_read_UINT16(data + 4, Index); /** Index */
	data_read_UINT32(data + 8, OutputBufferSize);

	out_size = 36 + OutputBufferSize;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);

	buffer = out_data + 36;

	bmRequestType = func_recipient | 0x80;

	ret = pdev->control_transfer(
		pdev, RequestId, 0, 0, bmRequestType,
		0x00, /* REQUEST_GET_STATUS */
		0,
		Index,
		&usbd_status,
		&OutputBufferSize,
		buffer,
		1000);

	if (ret < 0){
		WLog_DBG(TAG, "control_transfer: error num %d!!", ret);
		OutputBufferSize = 0;
		usbd_status = USBD_STATUS_STALL_PID;
	}
	else{
		usbd_status = USBD_STATUS_SUCCESS;
	}

	/** send data */
	offset = 36;
	if (transferDir == USBD_TRANSFER_DIRECTION_IN)
		out_size = offset + OutputBufferSize;
	else
		out_size = offset;

	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);		/** message id */

	if(transferDir == USBD_TRANSFER_DIRECTION_IN && OutputBufferSize != 0)
		data_write_UINT32(out_data + 8, URB_COMPLETION);	/** function id */
	else
		data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);

	data_write_UINT32(out_data + 12, RequestId);	/** RequestId, include NoAck*/
	data_write_UINT32(out_data + 16, 0x00000008);	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 0x0008);		/** Size */
	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_VENDOR_DEVICE);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */

	data_write_UINT32(out_data + 28, 0);	/** HResult */
	data_write_UINT32(out_data + 32, OutputBufferSize);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);

	zfree(out_data);

	return 0;
}

static int urb_control_vendor_or_class_request(URBDRC_CHANNEL_CALLBACK * callback,
	BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	BYTE func_type,
	BYTE func_recipient,
	int transferDir)
{
	IUDEVICE* pdev;
	UINT32 out_size, RequestId, InterfaceId, TransferFlags, usbd_status;
	UINT32 OutputBufferSize;
	BYTE ReqTypeReservedBits, Request, bmRequestType;
	UINT16 Value, Index, Padding;
	BYTE* buffer;
	BYTE* out_data;
	int offset, ret;
	/** control by vendor command */

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);
	if (pdev == NULL)
		return 0;
	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));

	data_read_UINT32(data + 0, RequestId);
	data_read_UINT32(data + 4, TransferFlags); /** TransferFlags */
	data_read_BYTE(data + 8, ReqTypeReservedBits); /** ReqTypeReservedBids */
	data_read_BYTE(data + 9, Request); /** Request */
	data_read_UINT16(data + 10, Value); /** value */
	data_read_UINT16(data + 12, Index); /** index */
	data_read_UINT16(data + 14, Padding); /** Padding */
	data_read_UINT32(data + 16, OutputBufferSize);
	offset = 20;

	out_size = 36 + OutputBufferSize;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);

	buffer = out_data + 36;

	/** Get Buffer */
	if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
		memcpy(buffer, data + offset, OutputBufferSize);

	/** vendor or class command */
	bmRequestType = func_type | func_recipient;

	if (TransferFlags & USBD_TRANSFER_DIRECTION)
		bmRequestType |= 0x80;

	WLog_DBG(TAG, "urb_control_vendor_or_class_request: "
		"RequestId 0x%x TransferFlags: 0x%x ReqTypeReservedBits: 0x%x "
		"Request:0x%x Value: 0x%x Index: 0x%x OutputBufferSize: 0x%x bmRequestType: 0x%x!!",
		RequestId, TransferFlags, ReqTypeReservedBits, Request, Value,
		Index, OutputBufferSize, bmRequestType);

	ret = pdev->control_transfer(
		pdev, RequestId, 0, 0, bmRequestType,
		Request,
		Value,
		Index,
		&usbd_status,
		&OutputBufferSize,
		buffer,
		2000);

	if (ret < 0){
		WLog_DBG(TAG, "control_transfer: error num %d!!", ret);
		OutputBufferSize = 0;
		usbd_status = USBD_STATUS_STALL_PID;
	}
	else{
		usbd_status = USBD_STATUS_SUCCESS;
	}

	offset = 36;
	if (transferDir == USBD_TRANSFER_DIRECTION_IN)
		out_size = offset + OutputBufferSize;
	else
		out_size = offset;
	/** send data */
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);	/** message id */

	if(transferDir == USBD_TRANSFER_DIRECTION_IN && OutputBufferSize != 0)
		data_write_UINT32(out_data + 8, URB_COMPLETION);	/** function id */
	else
		data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);

	data_write_UINT32(out_data + 12, RequestId);	/** RequestId, include NoAck*/
	data_write_UINT32(out_data + 16, 0x00000008);	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 0x0008);	/** Size */
	data_write_UINT16(out_data + 22, URB_FUNCTION_VENDOR_DEVICE);	/** Padding, MUST be ignored upon receipt */
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */

	data_write_UINT32(out_data + 28, 0);	/** HResult */
	data_write_UINT32(out_data + 32, OutputBufferSize);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);

	zfree(out_data);
	return 0;
}



static int urb_os_feature_descriptor_request(URBDRC_CHANNEL_CALLBACK * callback,
	BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	int transferDir)
{
	IUDEVICE* pdev;
	UINT32 out_size, RequestId, InterfaceId, OutputBufferSize, usbd_status;
	BYTE Recipient, InterfaceNumber, Ms_PageIndex;
	UINT16 Ms_featureDescIndex;
	BYTE* out_data;
	BYTE* buffer;
	int offset, ret;

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));

	data_read_UINT32(data + 0, RequestId);
	data_read_BYTE(data + 4, Recipient); /** Recipient */
	Recipient = Recipient && 0x1f;
	data_read_BYTE(data + 5, InterfaceNumber); /** InterfaceNumber */
	data_read_BYTE(data + 6, Ms_PageIndex); /** Ms_PageIndex */
	data_read_UINT16(data + 7, Ms_featureDescIndex); /** Ms_featureDescIndex */
	data_read_UINT32(data + 12, OutputBufferSize);
	offset = 16;

	out_size = 36 + OutputBufferSize;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);

	buffer = out_data + 36;

	switch (transferDir)
	{
		case USBD_TRANSFER_DIRECTION_OUT:
			WLog_ERR(TAG,  "Function urb_os_feature_descriptor_request: OUT Unchecked");
			memcpy(buffer, data + offset, OutputBufferSize);
			break;
		case USBD_TRANSFER_DIRECTION_IN:
			break;
	}

	WLog_DBG(TAG, "Ms descriptor arg: Recipient:0x%x, "
		"InterfaceNumber:0x%x, Ms_PageIndex:0x%x, "
		"Ms_featureDescIndex:0x%x, OutputBufferSize:0x%x",
		Recipient, InterfaceNumber, Ms_PageIndex,
		Ms_featureDescIndex, OutputBufferSize);
	/** get ms string */
	ret = pdev->os_feature_descriptor_request(
		pdev, RequestId, Recipient,
		InterfaceNumber,
		Ms_PageIndex,
		Ms_featureDescIndex,
		&usbd_status,
		&OutputBufferSize,
		buffer,
		1000);

	if (ret < 0)
		WLog_DBG(TAG, "os_feature_descriptor_request: error num %d", ret);

	offset = 36;
	out_size = offset + OutputBufferSize;
	/** send data */
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);	/** message id */
	if(OutputBufferSize!=0)
		data_write_UINT32(out_data + 8, URB_COMPLETION);	/** function id */
	else
		data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 0x00000008);	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 0x0008);	/** Size */

	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_GET_MS_FEATURE_DESCRIPTOR);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */

	data_write_UINT32(out_data + 28, 0);	/** HResult */
	data_write_UINT32(out_data + 32, OutputBufferSize);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);

	zfree(out_data);

	return 0;
}

static int urb_pipe_request(URBDRC_CHANNEL_CALLBACK * callback, BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	int transferDir,
	int action)
{
	IUDEVICE* pdev;
	UINT32 out_size, RequestId, InterfaceId, PipeHandle, EndpointAddress;
	UINT32 OutputBufferSize, usbd_status = 0;
	BYTE* out_data;
	int out_offset, ret;

	if (transferDir == 0){
		WLog_DBG(TAG, "urb_pipe_request: not support transfer out");
		return -1;
	}

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));

	data_read_UINT32(data + 0, RequestId);
	data_read_UINT32(data + 4, PipeHandle); /** PipeHandle */
	data_read_UINT32(data + 8, OutputBufferSize);
	EndpointAddress = (PipeHandle & 0x000000ff);


	switch (action){
		case PIPE_CANCEL:
			WLog_DBG(TAG, "urb_pipe_request: PIPE_CANCEL 0x%x ", EndpointAddress);

			ret = pdev->control_pipe_request(
				pdev, RequestId, EndpointAddress,
				&usbd_status,
				PIPE_CANCEL);

			if (ret < 0) {
				WLog_DBG(TAG, "PIPE SET HALT: error num %d", ret);
			}


			break;
		case PIPE_RESET:
			WLog_DBG(TAG, "urb_pipe_request: PIPE_RESET ep 0x%x ", EndpointAddress);

			ret = pdev->control_pipe_request(
				pdev, RequestId, EndpointAddress,
				&usbd_status,
				PIPE_RESET);

			if (ret < 0)
				WLog_DBG(TAG, "PIPE RESET: error num %d!!", ret);

			break;
		default:
			WLog_DBG(TAG, "urb_pipe_request action: %d is not support!", action);
			break;
	}


	/** send data */
	out_offset = 36;
	out_size = out_offset + OutputBufferSize;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);	/** message id */

	data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 0x00000008);	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 0x0008);	/** Size */

	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */

	data_write_UINT32(out_data + 28, 0);	/** HResult */
	data_write_UINT32(out_data + 32, 0);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);
	zfree(out_data);

	return 0;
}

static int urb_get_current_frame_number(URBDRC_CHANNEL_CALLBACK* callback,
	BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	int transferDir)
{
	IUDEVICE* pdev;
	UINT32 out_size, RequestId, InterfaceId, OutputBufferSize;
	UINT32 dummy_frames;
	BYTE* out_data;

	if (transferDir == 0){
		WLog_DBG(TAG, "urb_get_current_frame_number: not support transfer out");
		//exit(1);
		return -1;
	}

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);
	if (pdev == NULL)
		return 0;
	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));

	data_read_UINT32(data + 0, RequestId);
	data_read_UINT32(data + 4, OutputBufferSize);

	 /** Fixme: Need to fill actual frame number!!*/
	urbdrc_get_mstime(dummy_frames);

	out_size = 40;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);	/** message id */

	data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 12);	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 12);	/** Size */

	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_GET_CURRENT_FRAME_NUMBER);
	data_write_UINT32(out_data + 24, USBD_STATUS_SUCCESS);	/** UsbdStatus */
	data_write_UINT32(out_data + 28, dummy_frames);	/** FrameNumber */

	data_write_UINT32(out_data + 32, 0);	/** HResult */
	data_write_UINT32(out_data + 36, 0);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);
	zfree(out_data);
	return 0;
}


/* Unused function for current server */
static int urb_control_get_configuration_request(URBDRC_CHANNEL_CALLBACK* callback,
	BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	int transferDir)
{
	IUDEVICE* pdev;
	UINT32 out_size, RequestId, InterfaceId, OutputBufferSize, usbd_status;
	BYTE* buffer;
	BYTE* out_data;
	int ret, offset;

	if (transferDir == 0)
	{
		WLog_DBG(TAG, "urb_control_get_configuration_request:"
			" not support transfer out");
		return -1;
	}

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));

	data_read_UINT32(data + 0, RequestId);
	data_read_UINT32(data + 4, OutputBufferSize);

	out_size = 36 + OutputBufferSize;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);

	buffer = out_data + 36;

	ret = pdev->control_transfer(
		pdev, RequestId, 0, 0, 0x80 | 0x00,
		0x08, /* REQUEST_GET_CONFIGURATION */
		0,
		0,
		&usbd_status,
		&OutputBufferSize,
		buffer,
		1000);

	if (ret < 0){
		WLog_DBG(TAG, "control_transfer: error num %d", ret);
		OutputBufferSize = 0;
	}


	offset = 36;
	out_size = offset + OutputBufferSize;
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);	/** message id */

	if (OutputBufferSize != 0)
		data_write_UINT32(out_data + 8, URB_COMPLETION);
	else
		data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 8);	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 8);	/** Size */

	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_GET_CONFIGURATION);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */

	data_write_UINT32(out_data + 28, 0);	/** HResult */
	data_write_UINT32(out_data + 32, OutputBufferSize);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);
	zfree(out_data);
	return 0;
}

/* Unused function for current server */
static int urb_control_get_interface_request(URBDRC_CHANNEL_CALLBACK* callback,
	BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	int transferDir)
{
	IUDEVICE* pdev;
	UINT32 out_size, RequestId, InterfaceId, OutputBufferSize, usbd_status;
	UINT16 interface;
	BYTE* buffer;
	BYTE* out_data;
	int ret, offset;

	if (transferDir == 0){
		WLog_DBG(TAG, "urb_control_get_interface_request: not support transfer out");
		return -1;
	}

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);
	if (pdev == NULL)
		return 0;
	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));

	data_read_UINT32(data + 0, RequestId);
	data_read_UINT16(data + 4, interface);
	data_read_UINT32(data + 8, OutputBufferSize);

	out_size = 36 + OutputBufferSize;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);

	buffer = out_data + 36;

	ret = pdev->control_transfer(pdev, RequestId, 0, 0, 0x80 | 0x01,
		0x0A, /* REQUEST_GET_INTERFACE */
		0,
		interface,
		&usbd_status,
		&OutputBufferSize,
		buffer,
		1000);

	if (ret < 0){
		WLog_DBG(TAG, "control_transfer: error num %d", ret);
		OutputBufferSize = 0;
	}

	offset = 36;
	out_size = offset + OutputBufferSize;
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);		/** message id */

	if (OutputBufferSize != 0)
		data_write_UINT32(out_data + 8, URB_COMPLETION);
	else
		data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 8);	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 8);	/** Size */

	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_GET_INTERFACE);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */

	data_write_UINT32(out_data + 28, 0);	/** HResult */
	data_write_UINT32(out_data + 32, OutputBufferSize);	/** OutputBufferSize */

	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);
	zfree(out_data);
	return 0;
}

static int urb_control_feature_request(URBDRC_CHANNEL_CALLBACK * callback, BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	BYTE func_recipient,
	BYTE command,
	int transferDir)
{
	IUDEVICE* pdev;
	UINT32 out_size, RequestId, InterfaceId, OutputBufferSize, usbd_status;
	UINT16 FeatureSelector, Index;
	BYTE bmRequestType, bmRequest;
	BYTE* buffer;
	BYTE* out_data;
	int ret, offset;

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);

	if (pdev == NULL)
		return 0;

	InterfaceId = ((STREAM_ID_PROXY<<30) | pdev->get_ReqCompletion(pdev));

	data_read_UINT32(data + 0, RequestId);
	data_read_UINT16(data + 4, FeatureSelector);
	data_read_UINT16(data + 6, Index);
	data_read_UINT32(data + 8, OutputBufferSize);
	offset = 12;

	out_size = 36 + OutputBufferSize;
	out_data = (BYTE *) malloc(out_size);
	memset(out_data, 0, out_size);

	buffer = out_data + 36;

	bmRequestType = func_recipient;
	switch (transferDir)
	{
		case USBD_TRANSFER_DIRECTION_OUT:
			WLog_ERR(TAG,  "Function urb_control_feature_request: OUT Unchecked");
			memcpy(buffer, data + offset, OutputBufferSize);
			bmRequestType |= 0x00;
			break;
		case USBD_TRANSFER_DIRECTION_IN:
			bmRequestType |= 0x80;
			break;
	}

	switch (command)
	{
		case URB_SET_FEATURE:
			bmRequest = 0x03; /* REQUEST_SET_FEATURE */
			break;
		case URB_CLEAR_FEATURE:
			bmRequest = 0x01; /* REQUEST_CLEAR_FEATURE */
			break;
		default:
			WLog_ERR(TAG,  "urb_control_feature_request: Error Command %x", command);
			zfree(out_data);
			return -1;
	}

	ret = pdev->control_transfer(
		pdev, RequestId, 0, 0, bmRequestType, bmRequest,
		FeatureSelector,
		Index,
		&usbd_status,
		&OutputBufferSize,
		buffer,
		1000);

	if (ret < 0){
		WLog_DBG(TAG, "feature control transfer: error num %d", ret);
		OutputBufferSize = 0;
	}

	offset = 36;
	out_size = offset + OutputBufferSize;
	data_write_UINT32(out_data + 0, InterfaceId);	/** interface */
	data_write_UINT32(out_data + 4, MessageId);		/** message id */

	if (OutputBufferSize != 0)
		data_write_UINT32(out_data + 8, URB_COMPLETION);
	else
		data_write_UINT32(out_data + 8, URB_COMPLETION_NO_DATA);
	data_write_UINT32(out_data + 12, RequestId);	/** RequestId */
	data_write_UINT32(out_data + 16, 8);	/** CbTsUrbResult */
	/** TsUrbResult TS_URB_RESULT_HEADER */
	data_write_UINT16(out_data + 20, 8);	/** Size */

	/** Padding, MUST be ignored upon receipt */
	data_write_UINT16(out_data + 22, URB_FUNCTION_GET_INTERFACE);
	data_write_UINT32(out_data + 24, usbd_status);	/** UsbdStatus */

	data_write_UINT32(out_data + 28, 0);	/** HResult */
	data_write_UINT32(out_data + 32, OutputBufferSize);	/** OutputBufferSize */


	if (!pdev->isSigToEnd(pdev))
		callback->channel->Write(callback->channel, out_size, out_data, NULL);
	zfree(out_data);
	return 0;
}

static int urbdrc_process_transfer_request(URBDRC_CHANNEL_CALLBACK * callback, BYTE * data,
	UINT32 data_sizem,
	UINT32 MessageId,
	IUDEVMAN * udevman,
	UINT32 UsbDevice,
	int transferDir)
{
	IUDEVICE *	pdev;
	UINT32		CbTsUrb;
	UINT16		Size;
	UINT16		URB_Function;
	UINT32		OutputBufferSize;
	int			error = 0;

	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);
	if (pdev == NULL)
		return 0;
	data_read_UINT32(data + 0, CbTsUrb);	/** CbTsUrb */
	data_read_UINT16(data + 4, Size);	/** size */
	data_read_UINT16(data + 6, URB_Function);
	data_read_UINT32(data + 4 + CbTsUrb, OutputBufferSize);

	switch (URB_Function)
	{
		case URB_FUNCTION_SELECT_CONFIGURATION:			/** 0x0000 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SELECT_CONFIGURATION");
			error = urb_select_configuration(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir);
			break;
		case URB_FUNCTION_SELECT_INTERFACE:				/** 0x0001 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SELECT_INTERFACE");
			error = urb_select_interface(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir);
			break;
		case URB_FUNCTION_ABORT_PIPE:					/** 0x0002  */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_ABORT_PIPE");
			error = urb_pipe_request(
				callback, data + 8, data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir,
				PIPE_CANCEL);
			break;
		case URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL:	/** 0x0003  */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL");
			error = -1;  /** This URB function is obsolete in Windows 2000
							 * and later operating systems
							 * and is not supported by Microsoft. */
			break;
		case URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL:	/** 0x0004 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL");
			error = -1;  /** This URB function is obsolete in Windows 2000
							 * and later operating systems
							 * and is not supported by Microsoft. */
			break;
		case URB_FUNCTION_GET_FRAME_LENGTH:				/** 0x0005 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_FRAME_LENGTH");
			error = -1;  /** This URB function is obsolete in Windows 2000
							 * and later operating systems
							 * and is not supported by Microsoft. */
			break;
		case URB_FUNCTION_SET_FRAME_LENGTH:				/** 0x0006 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SET_FRAME_LENGTH");
			error = -1;  /** This URB function is obsolete in Windows 2000
							 * and later operating systems
							 * and is not supported by Microsoft. */
			break;
		case URB_FUNCTION_GET_CURRENT_FRAME_NUMBER:		/** 0x0007 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_CURRENT_FRAME_NUMBER");
			error = urb_get_current_frame_number(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir);
			break;
		case URB_FUNCTION_CONTROL_TRANSFER:				/** 0x0008 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_CONTROL_TRANSFER");
			error = urb_control_transfer(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir,
				URB_CONTROL_TRANSFER_NONEXTERNAL);
			break;
		case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:	/** 0x0009 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER");
			error = urb_bulk_or_interrupt_transfer(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir);
			break;
		case URB_FUNCTION_ISOCH_TRANSFER:				/** 0x000A */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_ISOCH_TRANSFER");
			error = urb_isoch_transfer(
				callback, data + 8, data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir);
			break;
		case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:	/** 0x000B */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE");
			error = urb_control_descriptor_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x00,
				transferDir);
			break;
		case URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE:		/** 0x000C */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE");
			error = urb_control_descriptor_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x00,
				transferDir);
			break;
		case URB_FUNCTION_SET_FEATURE_TO_DEVICE:		/** 0x000D */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SET_FEATURE_TO_DEVICE");
			error = urb_control_feature_request(callback, 
				data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x00,
				URB_SET_FEATURE,
				transferDir);
			break;
		case URB_FUNCTION_SET_FEATURE_TO_INTERFACE:		/** 0x000E */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SET_FEATURE_TO_INTERFACE");
			error = urb_control_feature_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x01,
				URB_SET_FEATURE,
				transferDir);
			break;
		case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT:		/** 0x000F */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SET_FEATURE_TO_ENDPOINT");
			error = urb_control_feature_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x02,
				URB_SET_FEATURE,
				transferDir);
			break;
		case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE:		/** 0x0010 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE");
			error = urb_control_feature_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x00,
				URB_CLEAR_FEATURE,
				transferDir);
			break;
		case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE:	/** 0x0011 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE");
			error = urb_control_feature_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x01,
				URB_CLEAR_FEATURE,
				transferDir);
			break;
		case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT:	/** 0x0012 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT");
			error = urb_control_feature_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x02,
				URB_CLEAR_FEATURE,
				transferDir);
			break;
		case URB_FUNCTION_GET_STATUS_FROM_DEVICE:		/** 0x0013 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_STATUS_FROM_DEVICE");
			error = urb_control_get_status_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x00,
				transferDir);
			break;
		case URB_FUNCTION_GET_STATUS_FROM_INTERFACE:	/** 0x0014 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_STATUS_FROM_INTERFACE");
			error = urb_control_get_status_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x01,
				transferDir);
			break;
		case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT:		/** 0x0015 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_STATUS_FROM_ENDPOINT");
			error = urb_control_get_status_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x02,
				transferDir);
			break;
		case URB_FUNCTION_RESERVED_0X0016:				/** 0x0016 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_RESERVED_0X0016");
			error = -1;
			break;
		case URB_FUNCTION_VENDOR_DEVICE:				/** 0x0017 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_VENDOR_DEVICE");
			error = urb_control_vendor_or_class_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				(0x02 << 5), /* vendor type */
				0x00,
				transferDir);
			break;
		case URB_FUNCTION_VENDOR_INTERFACE:				/** 0x0018 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_VENDOR_INTERFACE");
			error = urb_control_vendor_or_class_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				(0x02 << 5), /* vendor type */
				0x01,
				transferDir);
			break;
		case URB_FUNCTION_VENDOR_ENDPOINT:				/** 0x0019 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_VENDOR_ENDPOINT");
			error = urb_control_vendor_or_class_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				(0x02 << 5), /* vendor type */
				0x02,
				transferDir);
			break;
		case URB_FUNCTION_CLASS_DEVICE:					/** 0x001A */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_CLASS_DEVICE");
			error = urb_control_vendor_or_class_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				(0x01 << 5), /* class type */
				0x00,
				transferDir);
			break;
		case URB_FUNCTION_CLASS_INTERFACE:				/** 0x001B */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_CLASS_INTERFACE");
			error = urb_control_vendor_or_class_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				(0x01 << 5), /* class type */
				0x01,
				transferDir);
			break;
		case URB_FUNCTION_CLASS_ENDPOINT:				/** 0x001C */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_CLASS_ENDPOINT");
			error = urb_control_vendor_or_class_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				(0x01 << 5), /* class type */
				0x02,
				transferDir);
			break;
		case URB_FUNCTION_RESERVE_0X001D:				/** 0x001D */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_RESERVE_0X001D");
			error = -1;
			break;
		case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL: /** 0x001E */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL");
			error = urb_pipe_request(
				callback, data + 8, data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir,
				PIPE_RESET);
			break;
		case URB_FUNCTION_CLASS_OTHER:					/** 0x001F */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_CLASS_OTHER");
			error = urb_control_vendor_or_class_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				(0x01 << 5), /* class type */
				0x03,
				transferDir);
			break;
		case URB_FUNCTION_VENDOR_OTHER:					/** 0x0020 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_VENDOR_OTHER");
			error = urb_control_vendor_or_class_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				(0x02 << 5), /* vendor type */
				0x03,
				transferDir);
			break;
		case URB_FUNCTION_GET_STATUS_FROM_OTHER:		/** 0x0021 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_STATUS_FROM_OTHER");
			error = urb_control_get_status_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x03,
				transferDir);
			break;
		case URB_FUNCTION_CLEAR_FEATURE_TO_OTHER:		/** 0x0022 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_CLEAR_FEATURE_TO_OTHER");
			error = urb_control_feature_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x03,
				URB_CLEAR_FEATURE,
				transferDir);
			break;
		case URB_FUNCTION_SET_FEATURE_TO_OTHER:			/** 0x0023 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SET_FEATURE_TO_OTHER");
			error = urb_control_feature_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x03,
				URB_SET_FEATURE,
				transferDir);
			break;
		case URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT:	/** 0x0024 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT");
			error = urb_control_descriptor_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x02,
				transferDir);
			break;
		case URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT:	/** 0x0025 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT");
			error = urb_control_descriptor_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x02,
				transferDir);
			break;
		case URB_FUNCTION_GET_CONFIGURATION:			/** 0x0026 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_CONFIGURATION");
			error =  urb_control_get_configuration_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir);
			break;
		case URB_FUNCTION_GET_INTERFACE:				/** 0x0027 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_INTERFACE");
			error =  urb_control_get_interface_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir);
			break;
		case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE:	/** 0x0028 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE");
			error = urb_control_descriptor_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x01,
				transferDir);
			break;
		case URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE:	/** 0x0029 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE");
			error = urb_control_descriptor_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				0x01,
				transferDir);
			break;
		case URB_FUNCTION_GET_MS_FEATURE_DESCRIPTOR:	/** 0x002A */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_GET_MS_FEATURE_DESCRIPTOR");
			error = urb_os_feature_descriptor_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir);
			break;
		case URB_FUNCTION_RESERVE_0X002B:				/** 0x002B */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_RESERVE_0X002B");
			error = -1;
			break;
		case URB_FUNCTION_RESERVE_0X002C:				/** 0x002C */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_RESERVE_0X002C");
			error = -1;
			break;
		case URB_FUNCTION_RESERVE_0X002D:				/** 0x002D */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_RESERVE_0X002D");
			error = -1;
			break;
		case URB_FUNCTION_RESERVE_0X002E:				/** 0x002E */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_RESERVE_0X002E");
			error = -1;
			break;
		case URB_FUNCTION_RESERVE_0X002F:				/** 0x002F */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_RESERVE_0X002F");
			error = -1;
			break;
		/** USB 2.0 calls start at 0x0030 */
		case URB_FUNCTION_SYNC_RESET_PIPE:				/** 0x0030 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SYNC_RESET_PIPE");
			error = urb_pipe_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir,
				PIPE_RESET);
			error = -9;  /** function not support */
			break;
		case URB_FUNCTION_SYNC_CLEAR_STALL:				/** 0x0031 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_SYNC_CLEAR_STALL");
			error = urb_pipe_request(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir,
				PIPE_RESET);
			error = -9;
			break;
		case URB_FUNCTION_CONTROL_TRANSFER_EX:			/** 0x0032 */
			WLog_DBG(TAG, "URB_Func: URB_FUNCTION_CONTROL_TRANSFER_EX");
			error = urb_control_transfer(
				callback, data + 8,
				data_sizem - 8,
				MessageId,
				udevman,
				UsbDevice,
				transferDir,
				URB_CONTROL_TRANSFER_EXTERNAL);
			break;
		default:
			WLog_DBG(TAG, "URB_Func: %x is not found!", URB_Function);
			break;
	}

	return error;
}

void* urbdrc_process_udev_data_transfer(void* arg)
{
	TRANSFER_DATA*  transfer_data = (TRANSFER_DATA*) arg;
	URBDRC_CHANNEL_CALLBACK * callback = transfer_data->callback;
	BYTE *		pBuffer		= transfer_data->pBuffer;
	UINT32		cbSize		= transfer_data->cbSize;
	UINT32		UsbDevice	= transfer_data->UsbDevice;
	IUDEVMAN *	udevman		= transfer_data->udevman;
	UINT32		MessageId;
	UINT32		FunctionId;
	IUDEVICE*   pdev;
	int error = 0;
	pdev = udevman->get_udevice_by_UsbDevice(udevman, UsbDevice);
	if (pdev == NULL || pdev->isSigToEnd(pdev))
	{
		if (transfer_data)
		{
			if (transfer_data->pBuffer)
				zfree(transfer_data->pBuffer);
			zfree(transfer_data);
		}
		return 0;
	}

	pdev->push_action(pdev);

	/* USB kernel driver detach!! */
	pdev->detach_kernel_driver(pdev);

	data_read_UINT32(pBuffer + 0, MessageId);
	data_read_UINT32(pBuffer + 4, FunctionId);
	switch (FunctionId)
	{
		case CANCEL_REQUEST:
			WLog_DBG(TAG, "urbdrc_process_udev_data_transfer:"
				" >>CANCEL_REQUEST<<0x%X", FunctionId);
			error = urbdrc_process_cancel_request(
				pBuffer + 8,
				cbSize - 8,
				udevman,
				UsbDevice);
			break;
		case REGISTER_REQUEST_CALLBACK:
			WLog_DBG(TAG, "urbdrc_process_udev_data_transfer:"
				" >>REGISTER_REQUEST_CALLBACK<<0x%X", FunctionId);
			error = urbdrc_process_register_request_callback(
				callback,
				pBuffer + 8, 
				cbSize - 8, 
				udevman,
				UsbDevice);
			break;
		case IO_CONTROL:
			WLog_DBG(TAG, "urbdrc_process_udev_data_transfer:"
				" >>IO_CONTROL<<0x%X", FunctionId);
			error = urbdrc_process_io_control(
				callback,
				pBuffer + 8,
				cbSize - 8,
				MessageId,
				udevman, UsbDevice);
			break;
		case INTERNAL_IO_CONTROL:
			WLog_DBG(TAG, "urbdrc_process_udev_data_transfer:"
				" >>INTERNAL_IO_CONTROL<<0x%X", FunctionId);
			error = urbdrc_process_internal_io_control(
				callback,
				pBuffer + 8,
				cbSize - 8, 
				MessageId,
				udevman, UsbDevice);
			break;
		case QUERY_DEVICE_TEXT:
			WLog_DBG(TAG, "urbdrc_process_udev_data_transfer:"
				" >>QUERY_DEVICE_TEXT<<0x%X", FunctionId);
			error = urbdrc_process_query_device_text(
				callback,
				pBuffer + 8,
				cbSize - 8,
				MessageId,
				udevman,
				UsbDevice);
			break;
		case TRANSFER_IN_REQUEST:
			WLog_DBG(TAG, "urbdrc_process_udev_data_transfer:"
				" >>TRANSFER_IN_REQUEST<<0x%X", FunctionId);
			error = urbdrc_process_transfer_request(
				callback,
				pBuffer + 8,
				cbSize - 8,
				MessageId,
				udevman,
				UsbDevice,
				USBD_TRANSFER_DIRECTION_IN);
			break;
		case TRANSFER_OUT_REQUEST:
			WLog_DBG(TAG, "urbdrc_process_udev_data_transfer:"
				" >>TRANSFER_OUT_REQUEST<<0x%X", FunctionId);
			error = urbdrc_process_transfer_request(
				callback,
				pBuffer + 8,
				cbSize - 8,
				MessageId,
				udevman,
				UsbDevice,
				USBD_TRANSFER_DIRECTION_OUT);
			break;
		case RETRACT_DEVICE:
			WLog_DBG(TAG, "urbdrc_process_udev_data_transfer:"
				" >>RETRACT_DEVICE<<0x%X", FunctionId);
			error = urbdrc_process_retract_device_request(
				pBuffer + 8,
				cbSize - 8, 
				udevman, 
				UsbDevice);
			break;
		default:
			WLog_DBG(TAG, "urbdrc_process_udev_data_transfer:"
				" unknown FunctionId 0x%X", FunctionId);
			error = -1;
			break;
	}

	if (transfer_data)
	{
		if (transfer_data->pBuffer)
			zfree(transfer_data->pBuffer);
		zfree(transfer_data);
	}

	if (pdev)
	{
#if ISOCH_FIFO
		/* check isochronous fds */
		func_check_isochronous_fds(pdev);
#endif
		/* close this channel, if device is not found. */
		pdev->complete_action(pdev);
	}
	else
	{
		udevman->push_urb(udevman);
		return 0;
	}

	udevman->push_urb(udevman);
	return 0;
}
