/*
 *************************************************************************
 * Ralink Tech Inc.
 * 5F., No.36, Taiyuan St., Jhubei City,
 * Hsinchu County 302,
 * Taiwan, R.O.C.
 *
 * (c) Copyright 2002-2010, Ralink Technology, Inc.
 *
 * This program is free software; you can redistribute it and/or modify  *
 * it under the terms of the GNU General Public License as published by  *
 * the Free Software Foundation; either version 2 of the License, or     *
 * (at your option) any later version.                                   *
 *                                                                       *
 * This program is distributed in the hope that it will be useful,       *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 * GNU General Public License for more details.                          *
 *                                                                       *
 * You should have received a copy of the GNU General Public License     *
 * along with this program; if not, write to the                         *
 * Free Software Foundation, Inc.,                                       *
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 *                                                                       *
 *************************************************************************/


#define RTMP_MODULE_OS
#define RTMP_MODULE_OS_UTIL

#include "rtmp_comm.h"
#include "rtmp_osabl.h"
#include "rt_os_util.h"

#ifdef RESOURCE_BOOT_ALLOC
#include <linux/usb.h>
#include <hcd.h>


#define RTUSB_MAX_BUS_CNT 1

struct rtusb_bulk_mem{
	void *buf;
	int len;
	int assigned;
	dma_addr_t data_dma;
};


struct rtusb_mem_pool{
	struct rtusb_mem_pool *next;
	struct usb_bus *bus;
	struct device *dev;
	struct rtusb_bulk_mem *buf_pool;
	int pool_cnt;
};


enum RTUSB_POOL_STATE{
	MEM_POOL_INVALID = 0,
	MEM_POOL_INITING = 1,
	MEM_POOL_INITED = 2,
	MEM_POOL_STOPING = 3,
	MEM_POOL_MAX,
};


static DEFINE_SPINLOCK(rtusb_mem_lock);
static enum RTUSB_POOL_STATE mem_pool_stat = MEM_POOL_INVALID;
static struct rtusb_mem_pool *rtusb_buf_pool = NULL;


void dump_mem_pool(void)
{
	struct rtusb_mem_pool *pool;
	struct rtusb_bulk_mem *mem;
	unsigned long irqflags;
	int idx;

	spin_lock_irqsave(&rtusb_mem_lock, irqflags);
	if ((rtusb_buf_pool == NULL) || (mem_pool_stat == MEM_POOL_INVALID)) {
		DBGPRINT(RT_DEBUG_ERROR,("%s(): Invalid pool(0x%p) status(%d)\n",
				__FUNCTION__, rtusb_buf_pool, mem_pool_stat));
	} else {
		DBGPRINT(RT_DEBUG_ERROR,("Dump Pre-allocated mem pool(Hdr:0x%p, flag:%d):\n",
				rtusb_buf_pool, mem_pool_stat));

		pool = rtusb_buf_pool;
		while(pool) {
			DBGPRINT(RT_DEBUG_ERROR,("\tbus(%s): controller=0x%p, pool=0x%p, pool_cnt=%d\n", pool->bus->bus_name, pool->dev, pool, pool->pool_cnt));
			for (idx = 0; idx < pool->pool_cnt; idx++) {
				mem = (struct rtusb_bulk_mem *)(pool->buf_pool + idx);
				DBGPRINT(RT_DEBUG_ERROR,("\t\t%d(0x%p):Flag=%d, buf=0x%p, dma=%llx, len=%x\n",
					idx, mem, mem->assigned, mem->buf, (unsigned long long)mem->data_dma, mem->len));
			}
			pool = pool->next;
		}
	}
	spin_unlock_irqrestore(&rtusb_mem_lock, irqflags);

}


static int pool_stat_change(enum RTUSB_POOL_STATE old, enum RTUSB_POOL_STATE new_state)
{
	unsigned long irqflags;
	int status = 0;

	spin_lock_irqsave(&rtusb_mem_lock, irqflags);
	if ((old != MEM_POOL_MAX) && (mem_pool_stat != old)) {
		DBGPRINT(RT_DEBUG_ERROR,("%s(): invalid mem pool status(exp:%d, act:%d)\n",
				__FUNCTION__, old, mem_pool_stat));
		status = -1;
	}
	else
		mem_pool_stat = new_state;
	spin_unlock_irqrestore(&rtusb_mem_lock, irqflags);

	return status;
}


int rtusb_resource_recycle(struct usb_device *udev, void *buf, dma_addr_t dma)
{
	struct rtusb_mem_pool *pool;
	struct usb_bus *dev_bus = udev->bus;
	struct rtusb_bulk_mem *mem;
	unsigned long irqflags;
	int pool_idx;

	DBGPRINT(RT_DEBUG_TRACE,("-->%s()\n", __FUNCTION__));
	if (!dev_bus) {
		DBGPRINT(RT_DEBUG_ERROR,("Error:Invalid dev_bus!\n"));
		return -1;
	}

	DBGPRINT(RT_DEBUG_TRACE,("Recycle mem(0x%p) for usb_dev(%s) which attached to bus(0x%p, %s), controller=0x%p\n",
			buf, udev->product, dev_bus, dev_bus->bus_name, dev_bus->controller));

	spin_lock_irqsave(&rtusb_mem_lock, irqflags);
	if ((rtusb_buf_pool == NULL) || (mem_pool_stat != MEM_POOL_INITED)) {
		DBGPRINT(RT_DEBUG_TRACE,("%s(): Invalid pool(0x%p) status(%d)\n",
				__FUNCTION__, rtusb_buf_pool, mem_pool_stat));
		spin_unlock_irqrestore(&rtusb_mem_lock, irqflags);
		return -1;
	}

	pool = rtusb_buf_pool;
	while(pool != NULL)
	{
		if (dev_bus->controller == pool->dev)
		{
			DBGPRINT(RT_DEBUG_TRACE,("%s():Find attached controller(0x%p, %s)!\n",
					__FUNCTION__, pool->dev, (pool->bus ? pool->bus->bus_name : "Invalid")));
			for (pool_idx = 0; pool_idx < pool->pool_cnt; pool_idx++)
			{
				mem = (struct rtusb_bulk_mem *)(pool->buf_pool + pool_idx);
				if (mem->assigned && (mem->buf == buf) && (mem->data_dma == dma))
				{
					mem->assigned = 0;
					DBGPRINT(RT_DEBUG_TRACE,("\tRecycle done\n"));
					break;
				}
			}
			break;
		}
		pool = pool->next;
	}
	spin_unlock_irqrestore(&rtusb_mem_lock, irqflags);

	if (pool == NULL) {
		DBGPRINT(RT_DEBUG_ERROR,("%s():Cannot found buf(0x%p) assigned to usb_dev(%s) in mem pool\n",
				__FUNCTION__, buf, udev->product));
		dump_mem_pool();
	}

	return 0;
}


void  *rtusb_resource_alloc(struct usb_device *udev, int len, dma_addr_t *dma)
{
	struct usb_bus *dev_bus = udev->bus;
	struct rtusb_mem_pool *pool;
	struct rtusb_bulk_mem *mem;
	unsigned long irqflags;
	int pool_idx;

	DBGPRINT(RT_DEBUG_TRACE,("--->%s():\n", __FUNCTION__));
	if (!dev_bus) {
		DBGPRINT(RT_DEBUG_ERROR,("Error, invalid bus!\n"));
		return NULL;
	}

	DBGPRINT(RT_DEBUG_TRACE,("Request mem(len:%d) for usb_dev which attached to bus(0x%p, %s), controller=0x%p\n",
			len, dev_bus, dev_bus->bus_name, dev_bus->controller));

	spin_lock_irqsave(&rtusb_mem_lock, irqflags);
	if ((rtusb_buf_pool == NULL) || (mem_pool_stat != MEM_POOL_INITED)) {
		DBGPRINT(RT_DEBUG_ERROR,("%s(): Invalid pool(0x%p) status(%d)\n",
				__FUNCTION__, rtusb_buf_pool, mem_pool_stat));
		spin_unlock_irqrestore(&rtusb_mem_lock, irqflags);
		return NULL;
	}

	pool = rtusb_buf_pool;
	while(pool != NULL)
	{
		if (dev_bus->controller == pool->dev)
		{
			DBGPRINT(RT_DEBUG_TRACE,("%s():Find attached controller(0x%p) at pool(0x%p)!\n",
					__FUNCTION__, dev_bus->controller, pool));
			if (pool->bus != dev_bus) {
				DBGPRINT(RT_DEBUG_TRACE,("Adjust the pool->bus as current one!\n"));
				pool->bus = dev_bus; /* write it back in case the bus is changed */
			}

			for (pool_idx = 0; pool_idx < pool->pool_cnt; pool_idx++)
			{
				mem = (struct rtusb_bulk_mem *)(pool->buf_pool + pool_idx);
				if ((mem->assigned == 0) && (mem->len == len))
				{
					mem->assigned = 1;
					*dma = mem->data_dma;
					memset(mem->buf, 0, mem->len);
					spin_unlock_irqrestore(&rtusb_mem_lock, irqflags);

					DBGPRINT(RT_DEBUG_TRACE,("%s():Assign the buf(0x%p, len=%d)\n",
						__FUNCTION__, mem->buf, mem->len));

					dump_mem_pool();
					return mem->buf;
				}
			}
		}
		pool = pool->next;
	}
	spin_unlock_irqrestore(&rtusb_mem_lock, irqflags);

	DBGPRINT(RT_DEBUG_ERROR,("%s():Cannot found buf assign to usb_dev(%s)!\n", __FUNCTION__, udev->product));
	dump_mem_pool();

	return NULL;

}


int rtusb_resource_exit(void)
{
	struct usb_bus *bus;
	struct rtusb_mem_pool *pool;
	struct rtusb_bulk_mem *mem;
	/* unsigned long irqflags; */
	int status, idx;

	DBGPRINT(RT_DEBUG_TRACE,("--->%s()\n", __FUNCTION__));
	dump_mem_pool();

	status = pool_stat_change(MEM_POOL_INITED, MEM_POOL_STOPING);
	if (status != 0)
		return -1;

        //spin_lock_irqsave(&rtusb_mem_lock, irqflags);
	while(rtusb_buf_pool != NULL) {
		pool = rtusb_buf_pool;
		DBGPRINT(RT_DEBUG_TRACE,("%s():Free Pre-allocated mem for bus(%s)!\n", __FUNCTION__, pool->bus->bus_name));
		for (idx = 0; idx < pool->pool_cnt; idx++) {
			mem = (struct rtusb_bulk_mem *)(pool->buf_pool + idx);
			bus = pool->bus;
			if (mem->assigned == 1)
				DBGPRINT(RT_DEBUG_ERROR,("Warning, mem still occupied by someone?\n"));
			
			if (mem->buf) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
				usb_free_coherent(bus->root_hub, mem->len, mem->buf, mem->data_dma);
#else
				usb_buffer_free(bus->root_hub, mem->len, mem->buf, mem->data_dma);
#endif
#else
				kfree(mem->buf);
#endif
				DBGPRINT(RT_DEBUG_TRACE,("%s():%d:Free the buf(0x%p) with len(%d)\n",
				        __FUNCTION__, idx, mem, mem->len));
			}
		}
		rtusb_buf_pool = rtusb_buf_pool->next;
		kfree(pool);
	}
	/* spin_unlock_irqrestore(&rtusb_mem_lock, irqflags); */

	DBGPRINT(RT_DEBUG_TRACE,("%s():After free pools, dump it\n", __FUNCTION__));
	dump_mem_pool();
	status = pool_stat_change(MEM_POOL_STOPING, MEM_POOL_INVALID);

	return status;
}


int rtusb_resource_init(int txlen, int rxlen, int tx_cnt, int rx_cnt)
{
	struct usb_bus *bus;
	struct rtusb_mem_pool *pool;
	struct rtusb_bulk_mem *mem;
	int status, idx, buf_len, pool_cnt, bus_cnt;

	pool_cnt = tx_cnt + rx_cnt;
	DBGPRINT(RT_DEBUG_TRACE,("%s()--->txlen=%d,rxlen=%d, pool_cnt=%d(t:%d,r:%d)!\n",
			__FUNCTION__, txlen, rxlen, pool_cnt, tx_cnt,rx_cnt));

	status = pool_stat_change(MEM_POOL_INVALID, MEM_POOL_INITING);
	if ((status!=0) || (txlen == 0) || (rxlen == 0) || (tx_cnt == 0) || (rx_cnt == 0))
		return -1;

	/*
	        for each bus, we need to allocate resource for it, because we cannot
	        expect which bus will be used for our dongle.
	*/
	bus_cnt = 0;
	mutex_lock(&usb_bus_list_lock);
	list_for_each_entry(bus, &usb_bus_list, bus_list) {
		if (bus->root_hub) {
			/*  Currently we only alloc memory for high speed bus */
			if (bus->root_hub->speed != USB_SPEED_HIGH)
				continue;
			buf_len = sizeof(struct rtusb_mem_pool) + sizeof(struct rtusb_bulk_mem) * pool_cnt;
			pool = kmalloc(buf_len, GFP_ATOMIC);
			if (!pool) {
				DBGPRINT(RT_DEBUG_ERROR,("%s():Allocate pool structure for bus(%s) failed\n",
						__FUNCTION__, bus->bus_name));
				continue;
			}

			memset(pool, 0, buf_len);
			pool->pool_cnt = pool_cnt;
			pool->bus = bus;
			pool->dev = bus->controller;
			pool->buf_pool = (struct rtusb_bulk_mem *)(pool + 1);
			for (idx = 0; idx < pool_cnt; idx++) {
				mem = (struct rtusb_bulk_mem *)(pool->buf_pool + idx);
				buf_len = (idx >= tx_cnt) ? rxlen : txlen;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
				mem->buf = usb_alloc_coherent(bus->root_hub, buf_len, GFP_KERNEL, &mem->data_dma);
#else
				mem->buf = usb_buffer_alloc(bus->root_hub, buf_len, GFP_KERNEL, &mem->data_dma);
#endif
#else
				mem->buf = kmalloc(buf_len, GFP_KERNEL);
#endif
				if (mem->buf)
					mem->len = buf_len;
				else
					DBGPRINT(RT_DEBUG_ERROR,("%s():Alloc membuf(idx:%d) for bus(%s) failed!\n",
							__FUNCTION__, idx, bus->bus_name));
			}

			if (rtusb_buf_pool)
				pool->next = rtusb_buf_pool;
			rtusb_buf_pool = pool;

			bus_cnt++;
		}
	}
        mutex_unlock(&usb_bus_list_lock);

	status = pool_stat_change(MEM_POOL_INITING, MEM_POOL_INITED);
	dump_mem_pool();

	DBGPRINT(RT_DEBUG_TRACE,("<---%s(%d)\n", __FUNCTION__, status));

	return status;
}

#ifdef OS_ABL_SUPPORT
EXPORT_SYMBOL(rtusb_resource_exit);
EXPORT_SYMBOL(rtusb_resource_init);
#endif // OS_ABL_SUPPORT //
#endif /* RESOURCE_BOOT_ALLOC */


#ifdef RTMP_MAC_USB
#ifdef OS_ABL_SUPPORT
MODULE_LICENSE("GPL");
#endif /* OS_ABL_SUPPORT */

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
/*
========================================================================
Routine Description:
	Dump URB information.

Arguments:
	purb_org		- the URB

Return Value:
	None

Note:
========================================================================
*/
void dump_urb(VOID *purb_org)
{
	struct urb *purb = (struct urb *)purb_org;

	DBGPRINT(RT_DEBUG_TRACE,("urb                  :0x%08lx\n", (unsigned long)purb));
	DBGPRINT(RT_DEBUG_TRACE,("\tdev                   :0x%08lx\n", (unsigned long)purb->dev));
	DBGPRINT(RT_DEBUG_TRACE,("\t\tdev->state          :0x%d\n", purb->dev->state));
	DBGPRINT(RT_DEBUG_TRACE,("\tpipe                  :0x%08x\n", purb->pipe));
	DBGPRINT(RT_DEBUG_TRACE,("\tstatus                :%d\n", purb->status));
	DBGPRINT(RT_DEBUG_TRACE,("\ttransfer_flags        :0x%08x\n", purb->transfer_flags));
	DBGPRINT(RT_DEBUG_TRACE,("\ttransfer_buffer       :0x%08lx\n", (unsigned long)purb->transfer_buffer));
	DBGPRINT(RT_DEBUG_TRACE,("\ttransfer_buffer_length:%d\n", purb->transfer_buffer_length));
	DBGPRINT(RT_DEBUG_TRACE,("\tactual_length         :%d\n", purb->actual_length));
	DBGPRINT(RT_DEBUG_TRACE,("\tsetup_packet          :0x%08lx\n", (unsigned long)purb->setup_packet));
	DBGPRINT(RT_DEBUG_TRACE,("\tstart_frame           :%d\n", purb->start_frame));
	DBGPRINT(RT_DEBUG_TRACE,("\tnumber_of_packets     :%d\n", purb->number_of_packets));
	DBGPRINT(RT_DEBUG_TRACE,("\tinterval              :%d\n", purb->interval));
	DBGPRINT(RT_DEBUG_TRACE,("\terror_count           :%d\n", purb->error_count));
	DBGPRINT(RT_DEBUG_TRACE,("\tcontext               :0x%08lx\n", (unsigned long)purb->context));
	DBGPRINT(RT_DEBUG_TRACE,("\tcomplete              :0x%08lx\n\n", (unsigned long)purb->complete));
}
#else
void dump_urb(VOID *purb_org)
{
	return;
}
#endif /* LINUX_VERSION_CODE */



#ifdef CONFIG_STA_SUPPORT
#ifdef CONFIG_PM
#ifdef USB_SUPPORT_SELECTIVE_SUSPEND


void rausb_autopm_put_interface( void *intf)
{
	usb_autopm_put_interface((struct usb_interface *)intf);
}

int  rausb_autopm_get_interface( void *intf)
{
	return usb_autopm_get_interface((struct usb_interface *)intf);
}



/*
========================================================================
Routine Description:
	RTMP_Usb_AutoPM_Put_Interface

Arguments:
	

Return Value:
	

Note:
========================================================================
*/

int RTMP_Usb_AutoPM_Put_Interface (
	IN	VOID			*pUsb_Devsrc,
	IN	VOID			*intfsrc)
{

	INT	 pm_usage_cnt;
	struct usb_interface	*intf =(struct usb_interface *)intfsrc;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
		pm_usage_cnt = atomic_read(&intf->pm_usage_cnt);	
#else
		pm_usage_cnt = intf->pm_usage_cnt;
#endif
		
		if (pm_usage_cnt == 1)
		{
			rausb_autopm_put_interface(intf);

              }

			return 0;
}

EXPORT_SYMBOL(RTMP_Usb_AutoPM_Put_Interface);

/*
========================================================================
Routine Description:
	RTMP_Usb_AutoPM_Get_Interface

Arguments:
	

Return Value: (-1)  error (resume fail )    1 success ( resume success)  2  (do  nothing)
	

Note:
========================================================================
*/

int RTMP_Usb_AutoPM_Get_Interface (
	IN	VOID			*pUsb_Devsrc,
	IN	VOID			*intfsrc)
{

	INT	 pm_usage_cnt;
	struct usb_interface	*intf =(struct usb_interface *)intfsrc;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
	pm_usage_cnt = (INT)atomic_read(&intf->pm_usage_cnt);	
#else
	pm_usage_cnt = intf->pm_usage_cnt;
#endif

	if (pm_usage_cnt == 0)
	{
		int res=1;
		
		res = rausb_autopm_get_interface(intf);
		if (res)
		{
			DBGPRINT(RT_DEBUG_ERROR, ("AsicSwitchChannel autopm_resume fail ------\n"));
			return (-1);
		}			
			
	}
	return 2;


}

EXPORT_SYMBOL(RTMP_Usb_AutoPM_Get_Interface);

#endif /* USB_SUPPORT_SELECTIVE_SUSPEND */
#endif /* CONFIG_PM */
#endif /* CONFIG_STA_SUPPORT */



#ifdef OS_ABL_SUPPORT
/*
========================================================================
Routine Description:
	Register a USB driver.

Arguments:
	new_driver		- the driver

Return Value:
	0				- successfully
	Otherwise		- fail

Note:
========================================================================
*/
int rausb_register(VOID * new_driver)
{
	return usb_register((struct usb_driver *)new_driver);
}
EXPORT_SYMBOL(rausb_register);


/*
========================================================================
Routine Description:
	De-Register a USB driver.

Arguments:
	new_driver		- the driver

Return Value:
	None

Note:
========================================================================
*/
void rausb_deregister(VOID * driver)
{
	usb_deregister((struct usb_driver *)driver);
}
EXPORT_SYMBOL(rausb_deregister);


/*
========================================================================
Routine Description:
	Create a new urb for a USB driver to use.

Arguments:
	iso_packets		- number of iso packets for this urb

Return Value:
	the URB

Note:
========================================================================
*/
struct urb *rausb_alloc_urb(int iso_packets)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	return usb_alloc_urb(iso_packets, GFP_ATOMIC);
#else
	return usb_alloc_urb(iso_packets);
#endif /* LINUX_VERSION_CODE */
}
EXPORT_SYMBOL(rausb_alloc_urb);


/*
========================================================================
Routine Description:
	Free the memory used by a urb.

Arguments:
	urb				- the URB

Return Value:
	None

Note:
========================================================================
*/
void rausb_free_urb(VOID *urb)
{
	usb_free_urb((struct urb *)urb);
}
EXPORT_SYMBOL(rausb_free_urb);


#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
/*
========================================================================
Routine Description:
	Release a use of the usb device structure.

Arguments:
	dev				- the USB device

Return Value:
	None

Note:
========================================================================
*/
void rausb_put_dev(VOID *dev)
{
	usb_put_dev((struct usb_device *)dev);
}
EXPORT_SYMBOL(rausb_put_dev);


/*
========================================================================
Routine Description:
	Increments the reference count of the usb device structure.

Arguments:
	dev				- the USB device

Return Value:
	the device with the incremented reference counter

Note:
========================================================================
*/
struct usb_device *rausb_get_dev(VOID *dev)
{
	return usb_get_dev((struct usb_device *)dev);
}
EXPORT_SYMBOL(rausb_get_dev);
#endif /* LINUX_VERSION_CODE */


/*
========================================================================
Routine Description:
	Issue an asynchronous transfer request for an endpoint.

Arguments:
	urb				- the URB

Return Value:
	0				- successfully
	Otherwise		- fail

Note:
========================================================================
*/
int rausb_submit_urb(VOID *urb)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	return usb_submit_urb((struct urb *)urb, GFP_ATOMIC);
#else
	return usb_submit_urb((struct urb *)urb);
#endif /* LINUX_VERSION_CODE */
}
EXPORT_SYMBOL(rausb_submit_urb);

/*
========================================================================
Routine Description:
	Allocate dma-consistent buffer.

Arguments:
	dev				- the USB device
	size			- buffer size
	dma				- used to return DMA address of buffer

Return Value:
	a buffer that may be used to perform DMA to the specified device

Note:
========================================================================
*/
void *rausb_buffer_alloc(VOID *dev,
							size_t size,
							ra_dma_addr_t *dma)
{
#ifdef RESOURCE_BOOT_ALLOC
	if (size > 4095) {
		dma_addr_t DmaAddr;
		void *buf = rtusb_resource_alloc(dev, size, &DmaAddr);

		DBGPRINT(RT_DEBUG_ERROR,("%s():alloc usb buffer(p:0x%p, len:%d) %s!\n",
				__FUNCTION__, buf, size, (buf ? "done" : "fail")));
		*dma = (ra_dma_addr_t)DmaAddr;
		return buf;
	}
	else
#endif // RESOURCE_BOOT_ALLOC //
	{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	dma_addr_t DmaAddr = (dma_addr_t)(*dma);
	void *buf;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
	buf = usb_alloc_coherent(dev, size, GFP_ATOMIC, &DmaAddr);
#else
	buf = usb_buffer_alloc(dev, size, GFP_ATOMIC, &DmaAddr);
#endif
	*dma = (ra_dma_addr_t)DmaAddr;
	return buf;
#else
	return kmalloc(size, GFP_ATOMIC);
#endif
	}
}
EXPORT_SYMBOL(rausb_buffer_alloc);


/*
========================================================================
Routine Description:
	Free memory allocated with usb_buffer_alloc.

Arguments:
	dev				- the USB device
	size			- buffer size
	addr			- CPU address of buffer
	dma				- used to return DMA address of buffer

Return Value:
	None

Note:
========================================================================
*/
void rausb_buffer_free(VOID *dev,
							size_t size,
							void *addr,
							ra_dma_addr_t dma)
{
#ifdef RESOURCE_BOOT_ALLOC
	if (size > 4095)
		rtusb_resource_recycle(dev, addr, dma);
	else
#endif // RESOURCE_BOOT_ALLOC //
	{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	dma_addr_t DmaAddr = (dma_addr_t)(dma);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
	usb_free_coherent(dev, size, addr, DmaAddr);
#else
	usb_buffer_free(dev, size, addr, DmaAddr);
#endif
#else
	kfree(addr);
#endif
	}
}
EXPORT_SYMBOL(rausb_buffer_free);

/*
========================================================================
Routine Description:
	Send a control message to a device.

Arguments:
	dev				- the USB device

Return Value:
	0				- successfully
	Otherwise		- fail

Note:
========================================================================
*/
int rausb_control_msg(VOID *dev,
						unsigned int pipe,
						__u8 request,
						__u8 requesttype,
						__u16 value,
						__u16 index,
						void *data,
						__u16 size,
						int timeout)
{
	int ret;

	ret = usb_control_msg((struct usb_device *)dev, pipe, request, requesttype, value, index,
							data, size, timeout);
	if (ret == -ENODEV)
		return RTMP_USB_CONTROL_MSG_ENODEV;
	if (ret < 0)
		return RTMP_USB_CONTROL_MSG_FAIL;
	return ret;
}
EXPORT_SYMBOL(rausb_control_msg);

unsigned int rausb_sndctrlpipe(VOID *dev, ULONG address)
{
	return usb_sndctrlpipe(dev, address);
}
EXPORT_SYMBOL(rausb_sndctrlpipe);

unsigned int rausb_rcvctrlpipe(VOID *dev, ULONG address)
{
	return usb_rcvctrlpipe(dev, address);
}
EXPORT_SYMBOL(rausb_rcvctrlpipe);


/*
========================================================================
Routine Description:
	Cancel a transfer request and wait for it to finish.

Arguments:
	urb				- the URB

Return Value:
	None

Note:
========================================================================
*/
void rausb_kill_urb(VOID *urb)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,7)
	usb_kill_urb((struct urb *)urb);
#else
	usb_unlink_urb((struct urb *)urb);
#endif /* LINUX_VERSION_CODE */
}
EXPORT_SYMBOL(rausb_kill_urb);

#endif /* OS_ABL_SUPPORT */


VOID RtmpOsUsbEmptyUrbCheck(
	IN	VOID				**ppWait,
	IN	NDIS_SPIN_LOCK		*pBulkInLock,
	IN	UCHAR				*pPendingRx)
{
	UINT32 i = 0;
	DECLARE_WAIT_QUEUE_HEAD(unlink_wakeup); 
	DECLARE_WAITQUEUE(wait, current);


	/* ensure there are no more active urbs. */
	add_wait_queue (&unlink_wakeup, &wait);
	*ppWait = &unlink_wakeup;

	/* maybe wait for deletions to finish. */
	i = 0;
	/*while((i < 25) && atomic_read(&pAd->PendingRx) > 0) */
	while(i < 25)
	{
/*		unsigned long IrqFlags; */

		RTMP_SEM_LOCK(pBulkInLock);
		if (*pPendingRx == 0)
		{
			RTMP_SEM_UNLOCK(pBulkInLock);
			break;
		}
		RTMP_SEM_UNLOCK(pBulkInLock);
		
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
		msleep(UNLINK_TIMEOUT_MS);	/*Time in millisecond */
#else
		RTMPusecDelay(UNLINK_TIMEOUT_MS*1000);	/*Time in microsecond */
#endif
		i++;
	}
	*ppWait = NULL;
	remove_wait_queue (&unlink_wakeup, &wait); 
}


VOID	RtmpOsUsbInitHTTxDesc(
	IN	VOID			*pUrbSrc,
	IN	VOID			*pUsb_Dev,
	IN	UINT			BulkOutEpAddr,
	IN	PUCHAR			pSrc,
	IN	ULONG			BulkOutSize,
	IN	USB_COMPLETE_HANDLER	Func,
	IN	VOID			*pTxContext,
	IN	ra_dma_addr_t		TransferDma)
{
	PURB pUrb = (PURB)pUrbSrc;
	dma_addr_t DmaAddr = (dma_addr_t)(TransferDma);


	ASSERT(pUrb);

	/*Initialize a tx bulk urb */
	RTUSB_FILL_HTTX_BULK_URB(pUrb,
						pUsb_Dev,
						BulkOutEpAddr,
						pSrc,
						BulkOutSize,
						(usb_complete_t)Func,
						pTxContext,
						DmaAddr);
}


VOID	RtmpOsUsbInitRxDesc(
	IN	VOID			*pUrbSrc,
	IN	VOID			*pUsb_Dev,
	IN	UINT			BulkInEpAddr,
	IN	UCHAR			*pTransferBuffer,
	IN	UINT32			BufSize,
	IN	USB_COMPLETE_HANDLER	Func,
	IN	VOID			*pRxContext,
	IN	ra_dma_addr_t		TransferDma)
{
	PURB pUrb = (PURB)pUrbSrc;
	dma_addr_t DmaAddr = (dma_addr_t)(TransferDma);


	ASSERT(pUrb);

	/*Initialize a rx bulk urb */
	RTUSB_FILL_RX_BULK_URB(pUrb,
						pUsb_Dev,
						BulkInEpAddr,
						pTransferBuffer,
						BufSize,
						(usb_complete_t)Func,
						pRxContext,
						DmaAddr);
}


VOID *RtmpOsUsbContextGet(
	IN	VOID			*pUrb)
{
	return ((purbb_t)pUrb)->rtusb_urb_context;
}


NTSTATUS RtmpOsUsbStatusGet(
	IN	VOID			*pUrb)
{
	return ((purbb_t)pUrb)->rtusb_urb_status;
}


VOID RtmpOsUsbDmaMapping(
	IN	VOID			*pUrb)
{
	RTUSB_URB_DMA_MAPPING(((purbb_t)pUrb));
}


/*
========================================================================
Routine Description:
	Get the data pointer from the URB.

Arguments:
	pUrb			- USB URB

Return Value:
	the data pointer

Note:
========================================================================
*/
VOID *RtmpOsUsbUrbDataGet(
	IN	VOID					*pUrb)
{
	return RTMP_USB_URB_DATA_GET(pUrb);
}


/*
========================================================================
Routine Description:
	Get the status from the URB.

Arguments:
	pUrb			- USB URB

Return Value:
	the status

Note:
========================================================================
*/
NTSTATUS RtmpOsUsbUrbStatusGet(
	IN	VOID					*pUrb)
{
	return RTMP_USB_URB_STATUS_GET(pUrb);
}


/*
========================================================================
Routine Description:
	Get the data length from the URB.

Arguments:
	pUrb			- USB URB

Return Value:
	the data length

Note:
========================================================================
*/
ULONG RtmpOsUsbUrbLenGet(
	IN	VOID					*pUrb)
{
	return RTMP_USB_URB_LEN_GET(pUrb);
}

/*
========================================================================
Routine Description:
	Get USB Vendor ID.

Arguments:
	pUsbDev			- the usb device

Return Value:
	the name

Note:
========================================================================
*/
UINT32 RtmpOsGetUsbDevVendorID(IN VOID *pUsbDev) {
	return ((struct usb_device *) pUsbDev)->descriptor.idVendor;
}

/*
========================================================================
Routine Description:
	Get USB Product ID.

Arguments:
	pUsbDev			- the usb device

Return Value:
	the name

Note:
========================================================================
*/
UINT32 RtmpOsGetUsbDevProductID(IN VOID *pUsbDev) {
	return ((struct usb_device *) pUsbDev)->descriptor.idProduct;
}

#endif /* RTMP_MAC_USB */

/* End of rt_usb_util.c */
