#ifdef HAVE_CONFIG_H
#include <linux/config.h>
#endif

#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/rwsem.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h>


#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a<<16)|(b<<8)|c)
#endif

#if LINUX_VERSION_CODE<KERNEL_VERSION(2,6,27)
#include <asm/semaphore.h>
#else
#include <linux/smp_lock.h>
#endif

#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif

#include "wrg526ioctl.h"


#define MAX_WRG5_DEVICES    16
#define WiNRADiO_MAJOR      82
#define WRG5_MINOR_BASE     (32+32+32+32)
#define WRG5_MINOR_END      (WRG5_MINOR_BASE+MAX_WRG5_DEVICES-1)

#define WRG5_NAME "wrg526"


#define WRG5_DISPLAY_NAME "WRG526"

#define MAX_PIC_RETRY 128

#define USB_TIMEOUT (2000)

#define MAX_ENDPOINTS 16

#define TO_JIFFIES(x) ((x*HZ)/1000)

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
#define USE_MUTEX
#else
#undef USE_MUTEX
#endif


#ifdef USE_MUTEX

#include <linux/mutex.h>

#define WRG5_MUTEX struct mutex
#define WRG5_INIT_MUTEX(object)         mutex_init(&object)
#define WRG5_DESTROY_MUTEX(object)      mutex_destroy(&object)
#define WRG5_LOCK(object)               mutex_lock(&object)
#define WRG5_LOCK_INTERRUPTIBLE(object) mutex_lock_interruptible(&object)
#define WRG5_UNLOCK(object)             mutex_unlock(&object)

#else

#define WRG5_MUTEX struct semaphore
#define WRG5_INIT_MUTEX(object)         init_MUTEX(&object)
#define WRG5_DESTROY_MUTEX(object)
#define WRG5_LOCK(object)               down(&object)
#define WRG5_LOCK_INTERRUPTIBLE(object) down_interruptible(&object)
#define WRG5_UNLOCK(object)             up(&object)

#endif


typedef struct _ASYNC_BULK_URB
{
    unsigned int            SeqNum;
    struct urb             *Urb;
    struct __WRG5          *Dev;
    struct list_head        List;
    void                   *UserBuffer;
    __u32                   UserBufferSize;
    void                   *UserCompletionRoutine;
    void                   *UserData;

} ASYNC_URB,*PASYNC_URB;

typedef struct __WRG5
{
    char Name[32];

    int DeviceIndex;
    int Minor;
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,26)
    struct device ClassDevice;
#else
    struct class_device ClassDevice;
#endif
    struct cdev cdev;
    WRG5_MUTEX Ioctl_Lock;

    __u8 Opened;
    __u8 Connected;

    union
    {
        struct
        {
            struct usb_device          *USBDevice;
            struct usb_interface       *USBInterface;
            __u8                        NumberOfEndPoints;

            volatile int                AsyncUrbs_Active;
            struct list_head            AsyncUrbs_Submitted;
            struct list_head            AsyncUrbs_Complete;
            spinlock_t                  AsyncUrbs_SpinLock;
            unsigned int                AsyncUrbs_SeqNum;
            struct semaphore            AsyncUrbs_Semaphore;
            struct completion           AsyncUrbs_Completion;
        } USB;
    };
} WRG5,*PWRG5;

static PWRG5 WRG5_Devices[MAX_WRG5_DEVICES];

#ifdef USE_MUTEX
static DEFINE_MUTEX(WRG5_Device_Lock);
#else
static DECLARE_MUTEX(WRG5_Device_Lock);
#endif


static struct usb_device_id WRG5USB_ID_Table[]=
{
    {USB_DEVICE(0x14E0,0x0526)},
    {},
};


MODULE_DEVICE_TABLE(usb,WRG5USB_ID_Table);


int RegisterWRG5Device(PWRG5 Dev);
int UnregisterWRG5Device(PWRG5 Dev);


int WRG5_Release(struct inode *inode, struct file *filp);
int WRG5_Open(struct inode *inode, struct file *filp);
int WRG5_Ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
long WRG5_Compat_Ioctl(struct file *filp, unsigned int cmd, unsigned long arg);

static struct file_operations WRG5_File_Operations=
{
    .open=WRG5_Open,
    .release=WRG5_Release,
    .ioctl=WRG5_Ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl   = WRG5_Compat_Ioctl
#endif
};


#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,26)

static void WRG5ReleaseCD(struct device *cd);

static struct class WRG5_Class=
{
    .name=WRG5_NAME,
    .dev_release=WRG5ReleaseCD,
    .dev_attrs=NULL,
};

#else
static void WRG5ReleaseCD(struct class_device *cd);

static struct class WRG5_Class=
{
    .name=WRG5_NAME,
    .release=WRG5ReleaseCD,
    .class_dev_attrs=NULL,
};

#endif


int WRG5USB_Configure(PWRG5 Dev,__u8 Alternate,__u8 ResetConfig);


static int WRG5USB_Probe(struct usb_interface *interface, const struct usb_device_id *id)
{
 struct usb_device *USBDevice;
 PWRG5 Dev;
 int Error; 

    if(interface->cur_altsetting->desc.bInterfaceNumber!= 0)
    {
        return -ENODEV;
    }

    USBDevice=interface_to_usbdev(interface);


    Dev=kmalloc(sizeof(WRG5),GFP_KERNEL);
	
    if(Dev==NULL)
    {
        printk(KERN_ERR WRG5_DISPLAY_NAME ": Out of memory.\n");
        return -ENOMEM;
    }

    memset(Dev,0,sizeof(WRG5));

    Dev->USB.USBDevice=usb_get_dev(USBDevice);
    Dev->USB.USBInterface=interface;
    //Dev->InterfaceType=WRG5_USB;
    Dev->DeviceIndex=-1;
    strcpy(Dev->Name,WRG5_DISPLAY_NAME);

    WRG5_INIT_MUTEX(Dev->Ioctl_Lock);


    INIT_LIST_HEAD(&Dev->USB.AsyncUrbs_Submitted);
    INIT_LIST_HEAD(&Dev->USB.AsyncUrbs_Complete);

    spin_lock_init(&Dev->USB.AsyncUrbs_SpinLock);
    Dev->USB.AsyncUrbs_Active=0;
    sema_init(&Dev->USB.AsyncUrbs_Semaphore,0);


    usb_set_intfdata(interface,Dev);


    WRG5_LOCK(Dev->Ioctl_Lock);

    Error=RegisterWRG5Device(Dev);

    if(Error!=0)
    {
        WRG5_UNLOCK(Dev->Ioctl_Lock);
        goto Error_Exit;
    }
    else
    {
        usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),0x09,USB_TYPE_VENDOR,0,0,NULL,0,TO_JIFFIES(USB_TIMEOUT));
        usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),0x03,USB_TYPE_VENDOR,0xA8,0,NULL,0,TO_JIFFIES(USB_TIMEOUT));
    }

    Dev->Connected=1;

    WRG5_UNLOCK(Dev->Ioctl_Lock);

    return 0;

    Error_Exit:

    usb_set_intfdata(interface,NULL);
    usb_put_dev(Dev->USB.USBDevice);

    WRG5_DESTROY_MUTEX(Dev->Ioctl_Lock);

    kfree(Dev);

    return Error;
}


void WRG5USB_Free(PWRG5 Dev)
{

}

void WRG5USB_KillAsyncUrbs(PWRG5 Dev)
{
 unsigned long Flags;
 struct urb *Urb;

    spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

    while(!list_empty(&Dev->USB.AsyncUrbs_Submitted))
    {
        Urb=list_entry(Dev->USB.AsyncUrbs_Submitted.prev,ASYNC_URB,List)->Urb;
        usb_get_urb(Urb);
        spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        usb_kill_urb(Urb);
        usb_free_urb(Urb);

        spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);
    }

    spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);
}

void WRG5USB_WaitForAsyncUrbs(PWRG5 Dev)
{
 int i;
 unsigned long Flags;

    spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

    for(i=0;!list_empty(&Dev->USB.AsyncUrbs_Submitted) && i<200;i++)
    {
        spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        msleep(10);

        spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);
    }

    spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);
}

int WRG5USB_StopAsyncMessages(PWRG5 Dev);

static void WRG5USB_Disconnect(struct usb_interface *interface)
{
 PWRG5 Dev;

    Dev=usb_get_intfdata(interface);

    WRG5_LOCK(Dev->Ioctl_Lock);

    WRG5USB_StopAsyncMessages(Dev);

    WRG5USB_WaitForAsyncUrbs(Dev);

    //WRG5USB_Configure(Dev,0,0);

    UnregisterWRG5Device(Dev);

    //usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),0x09,USB_TYPE_VENDOR,0,0,NULL,0,TO_JIFFIES(500));
    //usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),0x03,USB_TYPE_VENDOR,0x57,0,NULL,0,TO_JIFFIES(500));        

    if(!Dev->Opened)
    {
        lock_kernel();

        usb_set_intfdata(interface,NULL);

        usb_put_dev(Dev->USB.USBDevice);

        unlock_kernel();

        WRG5_UNLOCK(Dev->Ioctl_Lock);

        WRG5_DESTROY_MUTEX(Dev->Ioctl_Lock);

        kfree(Dev);
    }
    else
    {
        Dev->Connected=0;

        WRG5_UNLOCK(Dev->Ioctl_Lock);
    }
}



static struct usb_driver WRG5USB_Driver= 
{
    .name=WRG5_NAME,
    .probe=WRG5USB_Probe,
    .disconnect=WRG5USB_Disconnect,
    .id_table=WRG5USB_ID_Table,
};


static int __init WRG5_Init(void)
{
 int Error;
 dev_t FirstDev;

    FirstDev=MKDEV(WiNRADiO_MAJOR,WRG5_MINOR_BASE);

    Error=register_chrdev_region(FirstDev,MAX_WRG5_DEVICES,WRG5_NAME);

    if(Error<0)
    {
        printk(KERN_ERR WRG5_DISPLAY_NAME ": Failed to register chrdev region (%d)\n",Error);
        return Error;
    }

    Error=class_register(&WRG5_Class);

    if(Error<0)
    {
        printk(KERN_ERR WRG5_DISPLAY_NAME ": Failed to register class (%d)\n",Error);
        unregister_chrdev_region(FirstDev,MAX_WRG5_DEVICES);
        return Error;
    }

    Error=usb_register(&WRG5USB_Driver);

    if(Error!=0)
    {
        printk(KERN_ERR WRG5_DISPLAY_NAME ": Failed to register USB driver (%d)\n",Error);

        class_unregister(&WRG5_Class);
        unregister_chrdev_region(FirstDev,MAX_WRG5_DEVICES);
    }

    return Error;
}

static void __exit WRG5_Exit(void)
{
 dev_t FirstDev;

    usb_deregister(&WRG5USB_Driver);

    class_unregister(&WRG5_Class);

    FirstDev=MKDEV(WiNRADiO_MAJOR,WRG5_MINOR_BASE);
    unregister_chrdev_region(FirstDev,MAX_WRG5_DEVICES); 
}



int WRG5USB_Open(PWRG5 Dev)
{
    WRG5USB_Configure(Dev,0,1);
    usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),0x03,USB_TYPE_VENDOR,0xA0,0,NULL,0,TO_JIFFIES(USB_TIMEOUT));
    return 0;
}


int WRG5_Open(struct inode *inode, struct file *filp)
{
 PWRG5 Dev;
 int Error;

    Dev=container_of(inode->i_cdev,WRG5,cdev);

    if(Dev==NULL)
    {
        return -ENOTTY;
    }

    if(WRG5_LOCK_INTERRUPTIBLE(Dev->Ioctl_Lock))
    {
        return -ERESTARTSYS;
    }

    if(Dev->Opened)
    {
        WRG5_UNLOCK(Dev->Ioctl_Lock);
        return -EBUSY;
    }

    filp->private_data=Dev;

    Error=WRG5USB_Open(Dev);

    if(Error==0)
    {
        Dev->Opened=1;
    }

    WRG5_UNLOCK(Dev->Ioctl_Lock);

    return Error;
}


int WRG5_Release(struct inode *inode, struct file *filp)
{
 PWRG5 Dev;

    Dev=(PWRG5)filp->private_data;

    if(Dev!=NULL)
    { 
        WRG5_LOCK(Dev->Ioctl_Lock);

        Dev->Opened=0;

        if(Dev->Connected)
        {
            usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice, 0),0x2C,USB_TYPE_VENDOR,5,0,NULL,0,USB_CTRL_SET_TIMEOUT);
            usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),0x09,USB_TYPE_VENDOR,0,0,NULL,0,TO_JIFFIES(USB_TIMEOUT));
            usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),0x03,USB_TYPE_VENDOR,0xA8,0,NULL,0,TO_JIFFIES(USB_TIMEOUT));

            WRG5USB_StopAsyncMessages(Dev);

            WRG5USB_WaitForAsyncUrbs(Dev);

            WRG5USB_Configure(Dev,0,0);
        }
        else
        {
            lock_kernel();

            usb_set_intfdata(Dev->USB.USBInterface,NULL);
            usb_put_dev(Dev->USB.USBDevice);

            unlock_kernel();

            WRG5_UNLOCK(Dev->Ioctl_Lock);

            WRG5_DESTROY_MUTEX(Dev->Ioctl_Lock);

            kfree(Dev);
        }


        WRG5_UNLOCK(Dev->Ioctl_Lock);
    }

    return 0;
}


int WRG5USB_ControlMessage_Send(PWRG5 Dev,unsigned long arg)
{
 int Error;
 void *Buff;
 u_int8_t Buffer[768];
 USB_CONTROL_MSG UsbMsg;


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&UsbMsg,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    Buff=Buffer;

    if(UsbMsg.Buffer!=NULL && UsbMsg.Size!=0)
    {
        if(!access_ok(VERIFY_READ,(void __user*)UsbMsg.Buffer,UsbMsg.Size))
        {
            return -EFAULT;
        }

        if(UsbMsg.Size>sizeof(Buffer))
        {
            Buff=kmalloc(UsbMsg.Size,GFP_KERNEL);

            if(Buff==NULL)
            {
                return -ENOMEM;
            }
        }

        if(copy_from_user(Buff,(void __user*)UsbMsg.Buffer,UsbMsg.Size))
        {
            if(Buff!=Buffer)
            {
                kfree(Buff);
            }
            return -EFAULT;
        }
    }
    else
    {
        Buff=NULL;
        UsbMsg.Size=0;
    }

    Error=usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),
                          UsbMsg.Request,USB_TYPE_VENDOR,
                          UsbMsg.Value,UsbMsg.Index,Buff,UsbMsg.Size,TO_JIFFIES(UsbMsg.Timeout));

    if(Error==-EPIPE)
    {
        usb_clear_halt(Dev->USB.USBDevice,0x00);
        usb_clear_halt(Dev->USB.USBDevice,0x80);

        Error=usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),
                              UsbMsg.Request,USB_TYPE_VENDOR,
                              UsbMsg.Value,UsbMsg.Index,Buff,UsbMsg.Size,TO_JIFFIES(UsbMsg.Timeout));
    }


    if(Buff!=NULL && Buff!=Buffer)
    {
        kfree(Buff);
    }

    return Error;
}

int WRG5USB_ControlMessage_Receive(PWRG5 Dev,unsigned long arg)
{
 USB_CONTROL_MSG UsbMsg;
 void *Buff;
 int Error;
 u_int8_t Buffer[768];


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&UsbMsg,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    Buff=Buffer;

    if(UsbMsg.Buffer!=NULL && UsbMsg.Size!=0)
    {
        if(!access_ok(VERIFY_WRITE,(void __user*)UsbMsg.Buffer,UsbMsg.Size))
        {
            return -EFAULT;
        }

        if(UsbMsg.Size>sizeof(Buffer))
        {
            Buff=kmalloc(UsbMsg.Size,GFP_KERNEL);

            if(Buff==NULL)
            {
                return -ENOMEM;
            }
        }
    }
    else
    {
        Buff=NULL;
        UsbMsg.Size=0;
    }

    Error=usb_control_msg(Dev->USB.USBDevice,usb_rcvctrlpipe(Dev->USB.USBDevice,0),
                          UsbMsg.Request,USB_DIR_IN | USB_TYPE_VENDOR,
                          UsbMsg.Value,UsbMsg.Index,Buff,UsbMsg.Size,TO_JIFFIES(UsbMsg.Timeout));

    if(Error==-EPIPE)
    {
        usb_clear_halt(Dev->USB.USBDevice,0x00);
        usb_clear_halt(Dev->USB.USBDevice,0x80);

        Error=usb_control_msg(Dev->USB.USBDevice,usb_rcvctrlpipe(Dev->USB.USBDevice,0),
                              UsbMsg.Request,USB_DIR_IN | USB_TYPE_VENDOR,
                              UsbMsg.Value,UsbMsg.Index,Buff,UsbMsg.Size,TO_JIFFIES(UsbMsg.Timeout));
    }

    if(Buff!=NULL)
    {
        if(Error>0)
        {
            if(copy_to_user((void __user*)UsbMsg.Buffer,Buff,Error))//UsbMsg.Size))
            {
                Error=-EFAULT;
            }
        }

        if(Buff!=Buffer)
        {
            kfree(Buff);
        }
    }

    return Error;
}

int WRG5USB_ControlMessage(PWRG5 Dev,unsigned long arg)
{
 USB_CONTROL_MSG UsbMsg;
 int Error;

    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&UsbMsg,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    Error=usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),
                UsbMsg.Request,USB_TYPE_VENDOR,
                UsbMsg.Value,UsbMsg.Index,NULL,0,TO_JIFFIES(UsbMsg.Timeout));

    if(Error==-EPIPE)
    {
        usb_clear_halt(Dev->USB.USBDevice,0x00);
        usb_clear_halt(Dev->USB.USBDevice,0x80);

        Error=usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),
                            UsbMsg.Request,USB_TYPE_VENDOR,
                            UsbMsg.Value,UsbMsg.Index,NULL,0,TO_JIFFIES(UsbMsg.Timeout));
    }

    if(Error>=0)
    {
        Error=0;
    }

    return Error;
}



#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,19)
static void WRG5USB_AsyncUrbComplete(struct urb *Urb)
#else
static void WRG5USB_AsyncUrbComplete(struct urb *Urb,struct pt_regs *Regs)
#endif
{
 PWRG5 Dev;
 PASYNC_URB AsyncUrb;
 unsigned long Flags;
 __u8 Free;

    AsyncUrb=(PASYNC_URB)Urb->context;
    Dev=(PWRG5)AsyncUrb->Dev;

    spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

    list_del(&AsyncUrb->List);

    if(Dev->USB.AsyncUrbs_Active && AsyncUrb->SeqNum==Dev->USB.AsyncUrbs_SeqNum)
    {
        list_add_tail(&AsyncUrb->List,&Dev->USB.AsyncUrbs_Complete);
        Free=0;
    }
    else
    {
        Free=1;
    }

    spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

    if(Free)
    {
        kfree(AsyncUrb->Urb->transfer_buffer);
        usb_free_urb(AsyncUrb->Urb);
        kfree(AsyncUrb);
    }
    else
    {
        up(&Dev->USB.AsyncUrbs_Semaphore);
    }
}


int WRG5USB_StartAsyncMessages(PWRG5 Dev)
{
    if(!Dev->USB.AsyncUrbs_Active)
    {
        Dev->USB.AsyncUrbs_Active=1;
    }

    return 0;
}

void WRG5USB_FreeAsyncUrbs(PWRG5 Dev)
{
 unsigned long Flags;
 struct list_head *ListEntry;
 PASYNC_URB AsyncUrb;


    for(;;)
    {
        spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        if(list_empty(&Dev->USB.AsyncUrbs_Complete))
        {
            spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);
            break;
        }

        ListEntry=Dev->USB.AsyncUrbs_Complete.next;

        AsyncUrb=list_entry(ListEntry,ASYNC_URB,List);

        list_del(ListEntry);

        spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        if(AsyncUrb->Urb->transfer_buffer!=NULL)
        {
            kfree(AsyncUrb->Urb->transfer_buffer);
        }

        usb_free_urb(AsyncUrb->Urb);

        kfree(AsyncUrb);
    }
}

int WRG5USB_StopAsyncMessages(PWRG5 Dev)
{
 unsigned long Flags;
 int i;

    WRG5USB_KillAsyncUrbs(Dev);

    if(Dev->USB.AsyncUrbs_Active)
    {
        spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        Dev->USB.AsyncUrbs_Active=0;
        Dev->USB.AsyncUrbs_SeqNum++;

        spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);


        for(i=0;i<10;i++)
        {
            up(&Dev->USB.AsyncUrbs_Semaphore);
        }

        WRG5USB_FreeAsyncUrbs(Dev);
    }

    return 0;
}

int WRG5USB_GetCompleteAsyncUrb(PWRG5 Dev,unsigned long arg)
{
 USB_COMPLETE_ASYNC_MESSAGE Result;
 struct list_head *ListEntry;
 PASYNC_URB AsyncUrb;
 unsigned long Flags;
 __u8 *UserBuffer,*Buffer;
 int i;
 struct urb *Urb;

    if(!Dev->Connected || !Dev->USB.AsyncUrbs_Active)
    {
        WRG5_UNLOCK(Dev->Ioctl_Lock);
        return -EINVAL;
    }

    if(!access_ok(VERIFY_WRITE,(void __user*)arg,sizeof(Result)))
    {
        WRG5_UNLOCK(Dev->Ioctl_Lock);
        return -EFAULT;
    }


    spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

    while(list_empty(&Dev->USB.AsyncUrbs_Complete))
    {
        spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        WRG5_UNLOCK(Dev->Ioctl_Lock);

        if(down_interruptible(&Dev->USB.AsyncUrbs_Semaphore))
        {
            return -ERESTARTSYS;
        }

        if(!Dev->Connected)
        {
            return -EINVAL;
        }

        if(!Dev->USB.AsyncUrbs_Active)
        {
            return -EINVAL;
        }

        if(WRG5_LOCK_INTERRUPTIBLE(Dev->Ioctl_Lock))
        {
            return -ERESTARTSYS;
        }

        if(!access_ok(VERIFY_WRITE,(void __user*)arg,sizeof(Result)))
        {
            WRG5_UNLOCK(Dev->Ioctl_Lock);
            return -EFAULT;
        }


        spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);
    }

    ListEntry=Dev->USB.AsyncUrbs_Complete.next;
    AsyncUrb=list_entry(ListEntry,ASYNC_URB,List);
    list_del(ListEntry);

    spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);


    Urb=AsyncUrb->Urb;

    Result.CompletionRoutine=AsyncUrb->UserCompletionRoutine;
    Result.Buffer=AsyncUrb->UserBuffer;
    Result.BufferSize=AsyncUrb->UserBufferSize;
    Result.Transferred=Urb->actual_length;
    Result.Status=Urb->status;
    Result.Data=AsyncUrb->UserData;

    if(Urb->transfer_buffer!=NULL)
    {
        if(Result.Buffer!=NULL && Result.Status==0 && usb_pipein(Urb->pipe))
        {
            if(access_ok(VERIFY_WRITE,(void __user*)Result.Buffer,AsyncUrb->UserBufferSize))
            {
                if(usb_pipeisoc(Urb->pipe))
                {
                    Result.Transferred=0;
                    UserBuffer=(__u8*)Result.Buffer;
                    Buffer=(__u8*)Urb->transfer_buffer;

                    for(i=0;i<Urb->number_of_packets;i++)
                    {
                        if(Urb->iso_frame_desc[i].status==0 && Urb->iso_frame_desc[i].actual_length>0)
                        {
                            if(copy_to_user((void __user*)UserBuffer,Buffer+Urb->iso_frame_desc[i].offset,Urb->iso_frame_desc[i].actual_length))
                            {
                                Result.Status=-EFAULT;
                                Result.Transferred=0;
                                break;
                            }
                            UserBuffer+=Urb->iso_frame_desc[i].actual_length;
                            Result.Transferred+=Urb->iso_frame_desc[i].actual_length;
                        }
                    }
                }
                else
                {
                    if(copy_to_user((void __user*)Result.Buffer,Urb->transfer_buffer,Urb->actual_length))
                    {
                        Result.Status=-EFAULT;
                        Result.Transferred=0;
                    }
                }
            }
            else
            {
                Result.Status=-EFAULT;
                Result.Transferred=0;
            }
        }

        kfree(Urb->transfer_buffer);
    }

    usb_free_urb(Urb);

    kfree(AsyncUrb);

    if(copy_to_user((void __user*)arg,&Result,sizeof(Result)))
    {
        WRG5_UNLOCK(Dev->Ioctl_Lock);
        return -EFAULT;
    }
    else
    {
        WRG5_UNLOCK(Dev->Ioctl_Lock);
        return 0;
    }
}


int WRG5USB_WaitForIdle(PWRG5 Dev)
{
 uint16_t Status[4];
 uint8_t RetryCount;

    RetryCount=100;

    while(RetryCount--)
    {

        if(usb_control_msg(Dev->USB.USBDevice,usb_rcvctrlpipe(Dev->USB.USBDevice,0),
                          0x0A,USB_DIR_IN | USB_TYPE_VENDOR,
                          0,0,Status,sizeof(Status),TO_JIFFIES(2000)))
        {
            return -1;
        }

        if((Status[0]&0x0002)==0)
        {
            return 0;
        }

        msleep(10);
    }
    return -1;
}

int WRG5USB_Configure(PWRG5 Dev,__u8 Alternate,__u8 ResetConfig)
{
 __u32 i;
 int Error;

    WRG5USB_KillAsyncUrbs(Dev);
    WRG5USB_WaitForAsyncUrbs(Dev);

    WRG5USB_WaitForIdle(Dev);

    if(ResetConfig)
    {
        Error=usb_reset_configuration(Dev->USB.USBDevice);

        if(Error!=0)
        {
            printk(KERN_ERR WRG5_DISPLAY_NAME ": Failed to reset configuration (%d).\n",Error);
            return Error;
        }
    }

    Error=usb_set_interface(Dev->USB.USBDevice,0,Alternate);//Dev->USB.USBInterface->altsetting[0].desc.bInterfaceNumber

    if(Error!=0)
    {
        printk(KERN_ERR WRG5_DISPLAY_NAME ": Failed to set alternative setting %u for interface (%d).\n",Alternate,Error);
        return Error;
    }

    Dev->USB.NumberOfEndPoints=Dev->USB.USBInterface->cur_altsetting->desc.bNumEndpoints;


    for(i=0;i<MAX_ENDPOINTS;i++)
    {
        if(i>0)
        {
            if(Dev->USB.USBDevice->ep_in[i]!=NULL)
            {
                Error=usb_control_msg(Dev->USB.USBDevice, usb_sndctrlpipe(Dev->USB.USBDevice, 0),
                                      USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
                                      USB_ENDPOINT_HALT, i|USB_DIR_IN, NULL, 0,
                                      USB_CTRL_SET_TIMEOUT);
            }

            if(Dev->USB.USBDevice->ep_out[i]!=NULL)
            {
                Error=usb_control_msg(Dev->USB.USBDevice, usb_sndctrlpipe(Dev->USB.USBDevice, 0),
                                      USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
                                      USB_ENDPOINT_HALT, i, NULL, 0,
                                      USB_CTRL_SET_TIMEOUT);
            }
        }
    }

    return 0;
}

int WRG5USB_GetEndPointInfo(PWRG5 Dev,unsigned long arg)
{
 ENDPOINTS EndPoints;
 __u32 i;


    if(!access_ok(VERIFY_WRITE,(void __user*)arg,sizeof(EndPoints)))
    {
        return -EFAULT;
    }

    EndPoints.Count=Dev->USB.NumberOfEndPoints;

    for(i=0;i<EndPoints.Count;i++)
    {
        EndPoints.EndPoint[i].Address=Dev->USB.USBInterface->cur_altsetting->endpoint[i].desc.bEndpointAddress;
        EndPoints.EndPoint[i].Number=EndPoints.EndPoint[i].Address&USB_ENDPOINT_NUMBER_MASK;
        EndPoints.EndPoint[i].MaxPacketSize=le16_to_cpu(Dev->USB.USBInterface->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
    }

    if(copy_to_user((ENDPOINTS __user*)arg,&EndPoints,sizeof(EndPoints)))
    {
        return -EFAULT;
    }

    return 0;
}


int WRG5USB_BulkMessage_Send_Async2(PWRG5 Dev,USB_ASYNC_MESSAGE *Msg)//unsigned long arg)
{
 __u8 *Buffer;
 int Error;
 unsigned long Flags;
 PASYNC_URB AsyncUrb;

    if(Msg->EndPoint>15 || Dev->USB.USBDevice->ep_out[Msg->EndPoint]==NULL || Msg->Buffer==NULL || Msg->BufferSize==0)
    {
        return -EINVAL;
    }

    if(!access_ok(VERIFY_READ,(void __user*)Msg->Buffer,Msg->BufferSize))
    {
        return -EFAULT;
    }

    Buffer=(__u8*)kmalloc(Msg->BufferSize,GFP_KERNEL);

    if(Buffer==NULL)
    {
        return -ENOMEM;
    }

    if(copy_from_user(Buffer,(__u8 __user*)Msg->Buffer,Msg->BufferSize))
    {
        Error=-EFAULT;
    }
    else
    {
        AsyncUrb=(PASYNC_URB)kmalloc(sizeof(ASYNC_URB),GFP_KERNEL);

        if(AsyncUrb==NULL)
        {
            kfree(Buffer);
            return -ENOMEM;
        }

        AsyncUrb->Dev=Dev;
        AsyncUrb->UserBuffer=NULL;
        AsyncUrb->UserBufferSize=0;
        AsyncUrb->UserCompletionRoutine=Msg->CompletionRoutine;
        AsyncUrb->SeqNum=Dev->USB.AsyncUrbs_SeqNum;
        AsyncUrb->UserData=Msg->Data;

        AsyncUrb->Urb=usb_alloc_urb(0,GFP_KERNEL);

        if(AsyncUrb->Urb!=NULL)
        {
            usb_fill_bulk_urb(AsyncUrb->Urb,Dev->USB.USBDevice,usb_sndbulkpipe(Dev->USB.USBDevice,Msg->EndPoint),Buffer,
                              Msg->BufferSize,WRG5USB_AsyncUrbComplete,AsyncUrb);

            spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

            list_add_tail(&AsyncUrb->List,&Dev->USB.AsyncUrbs_Submitted);
            AsyncUrb->SeqNum=Dev->USB.AsyncUrbs_SeqNum;
            spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);


            Error=usb_submit_urb(AsyncUrb->Urb,GFP_KERNEL);

            if(Error!=0)
            {
                spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

                list_del(&AsyncUrb->List);

                spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

                usb_free_urb(AsyncUrb->Urb);

                kfree(AsyncUrb);
            }
        }
        else
        {
            kfree(AsyncUrb);
            Error=-ENOMEM;
        }

        if(Error!=0)
        {
            if(Error==-EPIPE)
            {
                usb_clear_halt(Dev->USB.USBDevice,usb_sndbulkpipe(Dev->USB.USBDevice,Msg->EndPoint));
            }

            kfree(Buffer);
        }
    }

    return Error;
}

int WRG5USB_BulkMessage_Send_Async(PWRG5 Dev,unsigned long arg)
{
 USB_ASYNC_MESSAGE Msg;


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&Msg,(USB_ASYNC_MESSAGE __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    return WRG5USB_BulkMessage_Send_Async2(Dev,&Msg);
}


int WRG5USB_BulkMessage_Send(PWRG5 Dev,unsigned long arg)
{
 USB_BULK_MESSAGE Msg;
 __u8 Buffer[768];
 __u8 *Buff;
 int Error;


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&Msg,(USB_BULK_MESSAGE __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(Msg.EndPoint>15 || Dev->USB.USBDevice->ep_out[Msg.EndPoint]==NULL || Msg.Buffer==NULL || Msg.BufferSize==0)
    {
        return -EINVAL;
    }

    if(!access_ok(VERIFY_READ,(void __user*)Msg.Buffer,Msg.BufferSize))
    {
        return -EFAULT;
    }

    if(Msg.BufferSize>sizeof(Buffer))
    {
        Buff=(__u8*)kmalloc(Msg.BufferSize,GFP_KERNEL);

        if(Buff==NULL)
        {
            return -ENOMEM;
        }
    }
    else
    {
        Buff=Buffer;
    }

    if(copy_from_user(Buff,(__u8 __user*)Msg.Buffer,Msg.BufferSize))
    {
        Error=-EFAULT;
    }
    else
    {
        Error=usb_bulk_msg(Dev->USB.USBDevice,usb_sndbulkpipe(Dev->USB.USBDevice,Msg.EndPoint),
                           Buff,Msg.BufferSize,&Msg.Transferred,TO_JIFFIES(Msg.Timeout));

        if(Error==0)
        {
            if(copy_to_user((USB_BULK_MESSAGE __user*)arg,&Msg,sizeof(Msg)))
            {
                Error=-EFAULT;
            }
        }
        else
        {
            if(Error==-EPIPE)
            {
                usb_clear_halt(Dev->USB.USBDevice,usb_sndbulkpipe(Dev->USB.USBDevice,Msg.EndPoint));
            }
        }
    }

    if(Buff!=Buffer)
    {
        kfree(Buff);
    }

    return Error;
}

int WRG5USB_IsocMessage_Receive_Async2(PWRG5 Dev,USB_ASYNC_MESSAGE *Msg)
{
 int Error;
 PASYNC_URB AsyncUrb;
 unsigned long Flags;
 struct urb *Urb;
 int NumberOfPackets;
 int Offset;
 int MaxPacketSize,i;
 __u8 *Buffer;

    if(Msg->EndPoint>15 || Dev->USB.USBDevice->ep_in[Msg->EndPoint]==NULL || Msg->Buffer==NULL || Msg->BufferSize==0)
    {
        return -EINVAL;
    }

    if(!access_ok(VERIFY_WRITE,(void __user*)Msg->Buffer,Msg->BufferSize))
    {
        return -EFAULT;
    }

    Buffer=(__u8*)kmalloc(Msg->BufferSize,GFP_KERNEL);

    if(Buffer==NULL)
    {
        return -ENOMEM;
    }

    AsyncUrb=(PASYNC_URB)kmalloc(sizeof(ASYNC_URB),GFP_KERNEL);

    if(AsyncUrb==NULL)
    {
        kfree(Buffer);
        return -ENOMEM;
    }

    AsyncUrb->Dev=Dev;
    AsyncUrb->UserBuffer=Msg->Buffer;
    AsyncUrb->UserBufferSize=Msg->BufferSize;
    AsyncUrb->UserCompletionRoutine=Msg->CompletionRoutine;
    AsyncUrb->UserData=Msg->Data;

    MaxPacketSize=Dev->USB.USBDevice->ep_in[Msg->EndPoint]->desc.wMaxPacketSize;

    NumberOfPackets=(Msg->BufferSize+MaxPacketSize-1)/MaxPacketSize;

    AsyncUrb->Urb=Urb=usb_alloc_urb(NumberOfPackets,GFP_KERNEL);

    if(AsyncUrb->Urb==NULL)
    {
        kfree(AsyncUrb);
        kfree(Buffer);
        return -ENOMEM;
    }

    Urb->dev=Dev->USB.USBDevice;
    Urb->pipe=usb_rcvisocpipe(Dev->USB.USBDevice,Msg->EndPoint);
    Urb->interval=1;
    Urb->transfer_flags=URB_ISO_ASAP;
    Urb->transfer_buffer=Buffer;
    Urb->transfer_buffer_length=Msg->BufferSize;
    Urb->number_of_packets=NumberOfPackets;
    Urb->complete=WRG5USB_AsyncUrbComplete;
    Urb->context=AsyncUrb;

    for(i=0,Offset=0;i<NumberOfPackets;i++,Offset+=MaxPacketSize)
    {
        Urb->iso_frame_desc[i].offset=Offset;
        Urb->iso_frame_desc[i].length=Msg->BufferSize>=MaxPacketSize?MaxPacketSize:Msg->BufferSize;
        Msg->BufferSize-=MaxPacketSize;
    }


    spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

    AsyncUrb->SeqNum=Dev->USB.AsyncUrbs_SeqNum;
    list_add_tail(&AsyncUrb->List,&Dev->USB.AsyncUrbs_Submitted);
    spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

    Error=usb_submit_urb(AsyncUrb->Urb,GFP_KERNEL);

    if(Error!=0)
    {
        spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        list_del(&AsyncUrb->List);

        spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        usb_free_urb(AsyncUrb->Urb);

        kfree(AsyncUrb);
        kfree(Buffer);

        if(Error==-EPIPE)
        {
            usb_clear_halt(Dev->USB.USBDevice,usb_rcvisocpipe(Dev->USB.USBDevice,Msg->EndPoint));
        }
    }

    return Error;
}

int WRG5USB_IsocMessage_Receive_Async(PWRG5 Dev,unsigned long arg)
{
 USB_ASYNC_MESSAGE Msg;


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&Msg,(USB_ASYNC_MESSAGE __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    return WRG5USB_IsocMessage_Receive_Async2(Dev,&Msg);
}

int WRG5USB_BulkMessage_Receive_Async2(PWRG5 Dev,USB_ASYNC_MESSAGE *Msg)
{
 __u8 *Buffer;
 int Error;
 PASYNC_URB AsyncUrb;
 unsigned long Flags;

    if(Msg->EndPoint>15 || Dev->USB.USBDevice->ep_in[Msg->EndPoint]==NULL || Msg->Buffer==NULL || Msg->BufferSize==0)
    {
        return -EINVAL;
    }

    if(!access_ok(VERIFY_WRITE,(void __user*)Msg->Buffer,Msg->BufferSize))
    {
        return -EFAULT;
    }

    Buffer=(__u8*)kmalloc(Msg->BufferSize,GFP_KERNEL);

    if(Buffer==NULL)
    {
        return -ENOMEM;
    }


    AsyncUrb=(PASYNC_URB)kmalloc(sizeof(ASYNC_URB),GFP_KERNEL);

    if(AsyncUrb==NULL)
    {
        kfree(Buffer);
        return -ENOMEM;
    }

    AsyncUrb->Dev=Dev;
    AsyncUrb->UserBuffer=Msg->Buffer;
    AsyncUrb->UserBufferSize=Msg->BufferSize;
    AsyncUrb->UserCompletionRoutine=Msg->CompletionRoutine;
    AsyncUrb->UserData=Msg->Data;

    AsyncUrb->Urb=usb_alloc_urb(0,GFP_KERNEL);

    if(AsyncUrb->Urb==NULL)
    {
        kfree(AsyncUrb);
        kfree(Buffer);
        return -ENOMEM;
    }

    usb_fill_bulk_urb(AsyncUrb->Urb,Dev->USB.USBDevice,usb_rcvbulkpipe(Dev->USB.USBDevice,Msg->EndPoint),Buffer,
                      Msg->BufferSize,WRG5USB_AsyncUrbComplete,AsyncUrb);


    spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

    AsyncUrb->SeqNum=Dev->USB.AsyncUrbs_SeqNum;
    list_add_tail(&AsyncUrb->List,&Dev->USB.AsyncUrbs_Submitted);
    spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

    Error=usb_submit_urb(AsyncUrb->Urb,GFP_KERNEL);

    if(Error!=0)
    {
        spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        list_del(&AsyncUrb->List);

        spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        usb_free_urb(AsyncUrb->Urb);
        kfree(AsyncUrb);
        kfree(Buffer);

        if(Error==-EPIPE)
        {
            usb_clear_halt(Dev->USB.USBDevice,usb_rcvbulkpipe(Dev->USB.USBDevice,Msg->EndPoint));
        }
    }

    return Error;
}

int WRG5USB_BulkMessage_Receive_Async(PWRG5 Dev,unsigned long arg)
{
 USB_ASYNC_MESSAGE Msg;

    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&Msg,(USB_ASYNC_MESSAGE __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }


    return WRG5USB_BulkMessage_Receive_Async2(Dev,&Msg);
}

int WRG5USB_BulkMessage_Receive(PWRG5 Dev,unsigned long arg)
{
 USB_BULK_MESSAGE Msg;
 __u8 Buffer[768];
 __u8 *Buff;
 int Error;

    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&Msg,(USB_BULK_MESSAGE __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(Msg.EndPoint>15 || Dev->USB.USBDevice->ep_in[Msg.EndPoint]==NULL || Msg.Buffer==NULL || Msg.BufferSize==0)
    {
        return -EINVAL;
    }

    if(!access_ok(VERIFY_WRITE,(void __user*)Msg.Buffer,Msg.BufferSize))
    {
        return -EFAULT;
    }

    if(Msg.BufferSize>sizeof(Buffer))
    {
        Buff=(__u8*)kmalloc(Msg.BufferSize,GFP_KERNEL);

        if(Buff==NULL)
        {
            return -ENOMEM;
        }
    }
    else
    {
        Buff=Buffer;
    }

    Error=usb_bulk_msg(Dev->USB.USBDevice,usb_rcvbulkpipe(Dev->USB.USBDevice,Msg.EndPoint),
                       Buff,Msg.BufferSize,&Msg.Transferred,TO_JIFFIES(Msg.Timeout));

    if(Error==0)
    {
        if(copy_to_user((USB_BULK_MESSAGE __user*)arg,&Msg,sizeof(Msg)))
        {
            Error=-EFAULT;
        }

        if(copy_to_user((__u8 __user*)Msg.Buffer,Buff,Msg.BufferSize))
        {
            Error=-EFAULT;
        }
    }
    else
    {
        if(Error==-EPIPE)
        {
            usb_clear_halt(Dev->USB.USBDevice,usb_rcvbulkpipe(Dev->USB.USBDevice,Msg.EndPoint));
        }
    }

    if(Buff!=Buffer)
    {
        kfree(Buff);
    }

    return Error;
}



int WRG5_Ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
 PWRG5 Dev;
 int Error;
 __u8 Value8;

    Dev=(PWRG5)filp->private_data;

    if(Dev==NULL)
    {
        return -EINVAL;
    }


    if(WRG5_LOCK_INTERRUPTIBLE(Dev->Ioctl_Lock))
    {
        return -ERESTARTSYS;
    }

    if(cmd==WRG5_IOCTL_IS_CONNECTED)
    {
        if(access_ok(VERIFY_WRITE,(void __user*)arg,sizeof(__u8)))
        {
            if(Dev->Connected)
            {
                Value8=1;
            }
            else
            {
                Value8=0;
            }

            put_user(Value8,(__u8 __user*)arg);

            Error=0;
        }
        else
        {
            Error=-EFAULT;
        } 


        WRG5_UNLOCK(Dev->Ioctl_Lock);
        return Error;
    }
    else
    {
        if(!Dev->Connected)
        {
            WRG5_UNLOCK(Dev->Ioctl_Lock);
            return -EIO;
        }
    }


    switch(cmd)
    {
        case WRG5_IOCTL_USB_GET_COMPLETE_ASYNC_MESSAGE:
            return WRG5USB_GetCompleteAsyncUrb(Dev,arg);

        case WRG5_IOCTL_USB_ISOC_MESSAGE_RECEIVE_ASYNC:
            Error=WRG5USB_IsocMessage_Receive_Async(Dev,arg);
            break;
        case WRG5_IOCTL_USB_BULK_MESSAGE_SEND_ASYNC:
            Error=WRG5USB_BulkMessage_Send_Async(Dev,arg);
            break;
        case WRG5_IOCTL_USB_BULK_MESSAGE_RECEIVE_ASYNC:
            Error=WRG5USB_BulkMessage_Receive_Async(Dev,arg);
            break;
        case WRG5_IOCTL_USB_BULK_MESSAGE_SEND:
            Error=WRG5USB_BulkMessage_Send(Dev,arg);
            break;
        case WRG5_IOCTL_USB_BULK_MESSAGE_RECEIVE:
            Error=WRG5USB_BulkMessage_Receive(Dev,arg);
            break;
        case WRG5_IOCTL_USB_CONTROL_MESSAGE:
            Error=WRG5USB_ControlMessage(Dev,arg);
            break;
        case WRG5_IOCTL_USB_CONTROL_MESSAGE_SEND:
            Error=WRG5USB_ControlMessage_Send(Dev,arg);
            break;
        case WRG5_IOCTL_USB_CONTROL_MESSAGE_RECEIVE:
            Error=WRG5USB_ControlMessage_Receive(Dev,arg);
            break;
        case WRG5_IOCTL_USB_CONFIGURE:
            if(access_ok(VERIFY_WRITE,(void __user*)arg,sizeof(__u8)))
            {
                if(!get_user(Value8,(__u8 __user*)arg))
                {
                    Error=WRG5USB_Configure(Dev,Value8,1);
                }
                else
                {
                    Error=-EFAULT;
                }
            }
            else
            {
                Error=-EFAULT;
            }
            break;
        case WRG5_IOCTL_USB_GET_ENDPOINTINFO:
            Error=WRG5USB_GetEndPointInfo(Dev,arg);
            break;

        case WRG5_IOCTL_USB_START_ASYNC_MESSAGES:
            Error=WRG5USB_StartAsyncMessages(Dev);
            break;
        case WRG5_IOCTL_USB_STOP_ASYNC_MESSAGES:
            Error=WRG5USB_StopAsyncMessages(Dev);
            break;

        default:
            Error=-EINVAL;
            break;
    }

    WRG5_UNLOCK(Dev->Ioctl_Lock);
 
    return Error;
}

#ifdef CONFIG_COMPAT

#pragma pack(push,1)


typedef struct
{
    __u8                    Request;
    __u16                   Value;
    __u16                   Index;
    compat_uptr_t           Buffer;
    __u16                   Size;
    int                     Timeout;
} COMPAT_USB_CONTROL_MSG;

typedef struct
{
    __u8                    EndPoint;
    compat_uptr_t           Buffer;
    __u32                   BufferSize;
    __u32                   Transferred;
    __u32                   Timeout;
} COMPAT_USB_BULK_MESSAGE;

typedef struct
{
    __u8                    EndPoint;
    compat_uptr_t           Buffer;
    __u32                   BufferSize;
    compat_uptr_t           CompletionRoutine;
    compat_uptr_t           Data;
} COMPAT_USB_ASYNC_MESSAGE;

typedef struct
{
    compat_uptr_t           CompletionRoutine;
    compat_uptr_t           Data;
    compat_uptr_t           Buffer;
    __u32                   BufferSize;
    __u32                   Transferred;
    __s32                   Status;
} COMPAT_USB_COMPLETE_ASYNC_MESSAGE;


#pragma pack(pop)



int WRG5USB_Compat_GetCompleteAsyncUrb(PWRG5 Dev,unsigned long arg)
{
 COMPAT_USB_COMPLETE_ASYNC_MESSAGE Result;
 struct list_head *ListEntry;
 PASYNC_URB AsyncUrb;
 unsigned long Flags;
 __u8 *Buffer;
 compat_uptr_t UserBuffer;
 int i;
 struct urb *Urb;

    if(!Dev->Connected || !Dev->USB.AsyncUrbs_Active)
    {
        WRG5_UNLOCK(Dev->Ioctl_Lock);
        return -EINVAL;
    }

    if(!access_ok(VERIFY_WRITE,(void __user*)arg,sizeof(Result)))
    {
        WRG5_UNLOCK(Dev->Ioctl_Lock);
        return -EFAULT;
    }


    spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);

    while(list_empty(&Dev->USB.AsyncUrbs_Complete))
    {
        spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);

        WRG5_UNLOCK(Dev->Ioctl_Lock);

        if(down_interruptible(&Dev->USB.AsyncUrbs_Semaphore))
        {
            return -ERESTARTSYS;
        }

        if(!Dev->Connected)
        {
            return -EINVAL;
        }

        if(!Dev->USB.AsyncUrbs_Active)
        {
            return -EINVAL;
        }

        if(WRG5_LOCK_INTERRUPTIBLE(Dev->Ioctl_Lock))
        {
            return -ERESTARTSYS;
        }

        if(!access_ok(VERIFY_WRITE,(void __user*)arg,sizeof(Result)))
        {
            WRG5_UNLOCK(Dev->Ioctl_Lock);
            return -EFAULT;
        }


        spin_lock_irqsave(&Dev->USB.AsyncUrbs_SpinLock,Flags);
    }

    ListEntry=Dev->USB.AsyncUrbs_Complete.next;
    AsyncUrb=list_entry(ListEntry,ASYNC_URB,List);
    list_del(ListEntry);

    spin_unlock_irqrestore(&Dev->USB.AsyncUrbs_SpinLock,Flags);


    Urb=AsyncUrb->Urb;

    Result.CompletionRoutine=ptr_to_compat(AsyncUrb->UserCompletionRoutine);
    Result.Buffer=ptr_to_compat(AsyncUrb->UserBuffer);
    Result.BufferSize=AsyncUrb->UserBufferSize;
    Result.Transferred=Urb->actual_length;
    Result.Status=Urb->status;
    Result.Data=ptr_to_compat(AsyncUrb->UserData);

    if(Urb->transfer_buffer!=NULL)
    {
        if(Result.Buffer!=0 && Result.Status==0 && usb_pipein(Urb->pipe))
        {
            if(access_ok(VERIFY_WRITE,compat_ptr(Result.Buffer),AsyncUrb->UserBufferSize))
            {
                if(usb_pipeisoc(Urb->pipe))
                {
                    Result.Transferred=0;
                    UserBuffer=Result.Buffer;
                    Buffer=(__u8*)Urb->transfer_buffer;

                    for(i=0;i<Urb->number_of_packets;i++)
                    {
                        if(Urb->iso_frame_desc[i].status==0 && Urb->iso_frame_desc[i].actual_length>0)
                        {
                            if(copy_to_user(compat_ptr(UserBuffer),Buffer+Urb->iso_frame_desc[i].offset,Urb->iso_frame_desc[i].actual_length))
                            {
                                Result.Status=-EFAULT;
                                Result.Transferred=0;
                                break;
                            }
                            UserBuffer+=Urb->iso_frame_desc[i].actual_length;
                            Result.Transferred+=Urb->iso_frame_desc[i].actual_length;
                        }
                    }
                }
                else
                {
                    if(copy_to_user(compat_ptr(Result.Buffer),Urb->transfer_buffer,Urb->actual_length))
                    {
                        Result.Status=-EFAULT;
                        Result.Transferred=0;
                    }
                }
            }
            else
            {
                Result.Status=-EFAULT;
                Result.Transferred=0;
            }
        }

        kfree(Urb->transfer_buffer);
    }

    usb_free_urb(Urb);

    kfree(AsyncUrb);

    if(copy_to_user((void __user*)arg,&Result,sizeof(Result)))
    {
        WRG5_UNLOCK(Dev->Ioctl_Lock);
        return -EFAULT;
    }
    else
    {
        WRG5_UNLOCK(Dev->Ioctl_Lock);

        return 0;
    }
}

int WRG5USB_Compat_BulkMessage_Send_Async(PWRG5 Dev,unsigned long arg)
{
 COMPAT_USB_ASYNC_MESSAGE CompatMsg;
 USB_ASYNC_MESSAGE Msg;


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(CompatMsg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&CompatMsg,(void __user*)arg,sizeof(CompatMsg)))
    {
        return -EFAULT;
    }

    Msg.EndPoint=CompatMsg.EndPoint;
    Msg.Buffer=compat_ptr(CompatMsg.Buffer);
    Msg.BufferSize=CompatMsg.BufferSize;
    Msg.CompletionRoutine=compat_ptr(CompatMsg.CompletionRoutine);
    Msg.Data=compat_ptr(CompatMsg.Data);

    return WRG5USB_BulkMessage_Send_Async2(Dev,&Msg);
}

int WRG5USB_Compat_IsocMessage_Receive_Async(PWRG5 Dev,unsigned long arg)
{
 COMPAT_USB_ASYNC_MESSAGE CompatMsg;
 USB_ASYNC_MESSAGE Msg;


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(CompatMsg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&CompatMsg,(void __user*)arg,sizeof(CompatMsg)))
    {
        return -EFAULT;
    }


    Msg.EndPoint=CompatMsg.EndPoint;
    Msg.Buffer=compat_ptr(CompatMsg.Buffer);
    Msg.BufferSize=CompatMsg.BufferSize;
    Msg.CompletionRoutine=compat_ptr(CompatMsg.CompletionRoutine);
    Msg.Data=compat_ptr(CompatMsg.Data);

    return WRG5USB_IsocMessage_Receive_Async2(Dev,&Msg);
}

int WRG5USB_Compat_BulkMessage_Receive_Async(PWRG5 Dev,unsigned long arg)
{
 COMPAT_USB_ASYNC_MESSAGE CompatMsg;
 USB_ASYNC_MESSAGE Msg;


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(CompatMsg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&CompatMsg,(void __user*)arg,sizeof(CompatMsg)))
    {
        return -EFAULT;
    }

    Msg.EndPoint=CompatMsg.EndPoint;
    Msg.Buffer=compat_ptr(CompatMsg.Buffer);
    Msg.BufferSize=CompatMsg.BufferSize;
    Msg.CompletionRoutine=compat_ptr(CompatMsg.CompletionRoutine);
    Msg.Data=compat_ptr(CompatMsg.Data);

    return WRG5USB_BulkMessage_Receive_Async2(Dev,&Msg);
}

int WRG5USB_Compat_BulkMessage_Send(PWRG5 Dev,unsigned long arg)
{
 COMPAT_USB_BULK_MESSAGE Msg;
 __u8 Buffer[768];
 __u8 *Buff;
 int Error;


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&Msg,(void __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(Msg.EndPoint>15 || Dev->USB.USBDevice->ep_out[Msg.EndPoint]==NULL || Msg.Buffer==0 || Msg.BufferSize==0)
    {
        return -EINVAL;
    }

    if(!access_ok(VERIFY_READ,compat_ptr(Msg.Buffer),Msg.BufferSize))
    {
        return -EFAULT;
    }

    if(Msg.BufferSize>sizeof(Buffer))
    {
        Buff=(__u8*)kmalloc(Msg.BufferSize,GFP_KERNEL);

        if(Buff==NULL)
        {
            return -ENOMEM;
        }
    }
    else
    {
        Buff=Buffer;
    }

    if(copy_from_user(Buff,compat_ptr(Msg.Buffer),Msg.BufferSize))
    {
        Error=-EFAULT;
    }
    else
    {
        Error=usb_bulk_msg(Dev->USB.USBDevice,usb_sndbulkpipe(Dev->USB.USBDevice,Msg.EndPoint),
                           Buff,Msg.BufferSize,&Msg.Transferred,TO_JIFFIES(Msg.Timeout));

        if(Error==0)
        {
            if(copy_to_user((void __user*)arg,&Msg,sizeof(Msg)))
            {
                Error=-EFAULT;
            }
        }
        else
        {
            if(Error==-EPIPE)
            {
                usb_clear_halt(Dev->USB.USBDevice,usb_sndbulkpipe(Dev->USB.USBDevice,Msg.EndPoint));
            }
        }
    }

    if(Buff!=Buffer)
    {
        kfree(Buff);
    }

    return Error;
}

int WRG5USB_Compat_BulkMessage_Receive(PWRG5 Dev,unsigned long arg)
{
 COMPAT_USB_BULK_MESSAGE Msg;
 __u8 Buffer[768];
 __u8 *Buff;
 int Error;

    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&Msg,(void __user*)arg,sizeof(Msg)))
    {
        return -EFAULT;
    }

    if(Msg.EndPoint>15 || Dev->USB.USBDevice->ep_in[Msg.EndPoint]==NULL || Msg.Buffer==0 || Msg.BufferSize==0)
    {
        return -EINVAL;
    }

    if(!access_ok(VERIFY_WRITE,compat_ptr(Msg.Buffer),Msg.BufferSize))
    {
        return -EFAULT;
    }

    if(Msg.BufferSize>sizeof(Buffer))
    {
        Buff=(__u8*)kmalloc(Msg.BufferSize,GFP_KERNEL);

        if(Buff==NULL)
        {
            return -ENOMEM;
        }
    }
    else
    {
        Buff=Buffer;
    }

    Error=usb_bulk_msg(Dev->USB.USBDevice,usb_rcvbulkpipe(Dev->USB.USBDevice,Msg.EndPoint),
                       Buff,Msg.BufferSize,&Msg.Transferred,TO_JIFFIES(Msg.Timeout));

    if(Error==0)
    {
        if(copy_to_user((void __user*)arg,&Msg,sizeof(Msg)))
        {
            Error=-EFAULT;
        }

        if(copy_to_user(compat_ptr(Msg.Buffer),Buff,Msg.BufferSize))
        {
            Error=-EFAULT;
        }
    }
    else
    {
        if(Error==-EPIPE)
        {
            usb_clear_halt(Dev->USB.USBDevice,usb_rcvbulkpipe(Dev->USB.USBDevice,Msg.EndPoint));
        }
    }

    if(Buff!=Buffer)
    {
        kfree(Buff);
    }

    return Error;
}



int WRG5USB_Compat_ControlMessage_Send(PWRG5 Dev,unsigned long arg)
{
 int Error;
 void *Buff;
 u_int8_t Buffer[1024];
 COMPAT_USB_CONTROL_MSG UsbMsg;


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&UsbMsg,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    Buff=Buffer;

    if(UsbMsg.Buffer!=0 && UsbMsg.Size!=0)
    {
        if(!access_ok(VERIFY_READ,compat_ptr(UsbMsg.Buffer),UsbMsg.Size))
        {
            return -EFAULT;
        }

        if(UsbMsg.Size>sizeof(Buffer))
        {
            Buff=kmalloc(UsbMsg.Size,GFP_KERNEL);

            if(Buff==NULL)
            {
                return -ENOMEM;
            }
        }

        if(copy_from_user(Buff,compat_ptr(UsbMsg.Buffer),UsbMsg.Size))
        {
            if(Buff!=Buffer)
            {
                kfree(Buff);
            }
            return -EFAULT;
        }
    }
    else
    {
        Buff=NULL;
        UsbMsg.Size=0;
    }

    Error=usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),
                          UsbMsg.Request,USB_TYPE_VENDOR,
                          UsbMsg.Value,UsbMsg.Index,Buff,UsbMsg.Size,TO_JIFFIES(UsbMsg.Timeout));

    if(Error==-EPIPE)
    {
        usb_clear_halt(Dev->USB.USBDevice,0x00);
        usb_clear_halt(Dev->USB.USBDevice,0x80);

        Error=usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),
                              UsbMsg.Request,USB_TYPE_VENDOR,
                              UsbMsg.Value,UsbMsg.Index,Buff,UsbMsg.Size,TO_JIFFIES(UsbMsg.Timeout));
    }


    if(Buff!=NULL && Buff!=Buffer)
    {
        kfree(Buff);
    }

    return Error;
}


int WRG5USB_Compat_ControlMessage(PWRG5 Dev,unsigned long arg)
{
 COMPAT_USB_CONTROL_MSG UsbMsg;
 int Error;


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&UsbMsg,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    Error=usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),
                UsbMsg.Request,USB_TYPE_VENDOR,
                UsbMsg.Value,UsbMsg.Index,NULL,0,TO_JIFFIES(UsbMsg.Timeout));

    if(Error==-EPIPE)
    {
        usb_clear_halt(Dev->USB.USBDevice,0x00);
        usb_clear_halt(Dev->USB.USBDevice,0x80);

        Error=usb_control_msg(Dev->USB.USBDevice,usb_sndctrlpipe(Dev->USB.USBDevice,0),
                              UsbMsg.Request,USB_TYPE_VENDOR,
                              UsbMsg.Value,UsbMsg.Index,NULL,0,TO_JIFFIES(UsbMsg.Timeout));
    }

    if(Error>=0)
    {
        Error=0;
    }


    return Error;
}

int WRG5USB_Compat_ControlMessage_Receive(PWRG5 Dev,unsigned long arg)
{
 COMPAT_USB_CONTROL_MSG UsbMsg;
 void *Buff;
 int Error;
 u_int8_t Buffer[1024];


    if(!access_ok(VERIFY_READ,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    if(copy_from_user(&UsbMsg,(void __user*)arg,sizeof(UsbMsg)))
    {
        return -EFAULT;
    }

    Buff=Buffer;

    if(UsbMsg.Buffer!=0 && UsbMsg.Size!=0)
    {
        if(!access_ok(VERIFY_WRITE,compat_ptr(UsbMsg.Buffer),UsbMsg.Size))
        {
            return -EFAULT;
        }

        if(UsbMsg.Size>sizeof(Buffer))
        {
            Buff=kmalloc(UsbMsg.Size,GFP_KERNEL);

            if(Buff==NULL)
            {
                return -ENOMEM;
            }
        }
    }
    else
    {
        Buff=NULL;
        UsbMsg.Size=0;
    }

    Error=usb_control_msg(Dev->USB.USBDevice,usb_rcvctrlpipe(Dev->USB.USBDevice,0),
                          UsbMsg.Request,USB_DIR_IN | USB_TYPE_VENDOR,
                          UsbMsg.Value,UsbMsg.Index,Buff,UsbMsg.Size,TO_JIFFIES(UsbMsg.Timeout));

    if(Error==-EPIPE)
    {
        usb_clear_halt(Dev->USB.USBDevice,0x00);
        usb_clear_halt(Dev->USB.USBDevice,0x80);

        Error=usb_control_msg(Dev->USB.USBDevice,usb_rcvctrlpipe(Dev->USB.USBDevice,0),
                              UsbMsg.Request,USB_DIR_IN | USB_TYPE_VENDOR,
                              UsbMsg.Value,UsbMsg.Index,Buff,UsbMsg.Size,TO_JIFFIES(UsbMsg.Timeout));
    }

    if(Buff!=NULL)
    {
        if(Error>0)
        {
            if(copy_to_user(compat_ptr(UsbMsg.Buffer),Buff,Error))//UsbMsg.Size))
            {
                Error=-EFAULT;
            }
        }

        if(Buff!=Buffer)
        {
            kfree(Buff);
        }
    }

    return Error;
}


#define WRG5_COMPAT_IOCTL_USB_CONTROL_MESSAGE_SEND              _IOW(WRG5_MAGIC,0x20,COMPAT_USB_CONTROL_MSG)
#define WRG5_COMPAT_IOCTL_USB_CONTROL_MESSAGE_RECEIVE           _IOWR(WRG5_MAGIC,0x21,COMPAT_USB_CONTROL_MSG)
#define WRG5_COMPAT_IOCTL_USB_CONTROL_MESSAGE                   _IOW(WRG5_MAGIC,0x22,COMPAT_USB_CONTROL_MSG)
#define WRG5_COMPAT_IOCTL_USB_BULK_MESSAGE_SEND                 _IOWR(WRG5_MAGIC,0x23,COMPAT_USB_BULK_MESSAGE)
#define WRG5_COMPAT_IOCTL_USB_BULK_MESSAGE_SEND_ASYNC           _IOW(WRG5_MAGIC,0x24,COMPAT_USB_ASYNC_MESSAGE)
#define WRG5_COMPAT_IOCTL_USB_BULK_MESSAGE_RECEIVE              _IOWR(WRG5_MAGIC,0x25,COMPAT_USB_BULK_MESSAGE)
#define WRG5_COMPAT_IOCTL_USB_BULK_MESSAGE_RECEIVE_ASYNC        _IOW(WRG5_MAGIC,0x26,COMPAT_USB_ASYNC_MESSAGE)
#define WRG5_COMPAT_IOCTL_USB_GET_COMPLETE_ASYNC_MESSAGE        _IOR(WRG5_MAGIC,0x27,COMPAT_USB_COMPLETE_ASYNC_MESSAGE)
#define WRG5_COMPAT_IOCTL_USB_ISOC_MESSAGE_RECEIVE_ASYNC        _IOW(WRG5_MAGIC,0x2C,COMPAT_USB_ASYNC_MESSAGE)

long WRG5_Compat_Ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
 PWRG5 Dev;
 int Error;
 u_int8_t Value8;

    Dev=(PWRG5)filp->private_data;

    if(Dev==NULL)
    {
        return -EINVAL;
    }


    if(WRG5_LOCK_INTERRUPTIBLE(Dev->Ioctl_Lock))
    {
        return -ERESTARTSYS;
    }

    if(cmd==WRG5_IOCTL_IS_CONNECTED)
    {
        if(access_ok(VERIFY_WRITE,(void __user*)arg,sizeof(__u8)))
        {
            if(Dev->Connected)
            {
                Value8=1;
            }
            else
            {
                Value8=0;
            }

            put_user(Value8,(__u8 __user*)arg);

            Error=0;
        }
        else
        {
            Error=-EFAULT;
        } 


        WRG5_UNLOCK(Dev->Ioctl_Lock);
        return Error;
    }
    else
    {
        if(!Dev->Connected)
        {
            WRG5_UNLOCK(Dev->Ioctl_Lock);
            return -EIO;
        }
    }


    switch(cmd)
    {
        case WRG5_COMPAT_IOCTL_USB_GET_COMPLETE_ASYNC_MESSAGE:
            return WRG5USB_Compat_GetCompleteAsyncUrb(Dev,arg);

        case WRG5_COMPAT_IOCTL_USB_ISOC_MESSAGE_RECEIVE_ASYNC:
            Error=WRG5USB_Compat_IsocMessage_Receive_Async(Dev,arg);
            break;
        case WRG5_COMPAT_IOCTL_USB_BULK_MESSAGE_SEND_ASYNC:
            Error=WRG5USB_Compat_BulkMessage_Send_Async(Dev,arg);
            break;
        case WRG5_COMPAT_IOCTL_USB_BULK_MESSAGE_RECEIVE_ASYNC:
            Error=WRG5USB_Compat_BulkMessage_Receive_Async(Dev,arg);
            break;
        case WRG5_COMPAT_IOCTL_USB_BULK_MESSAGE_SEND:
            Error=WRG5USB_Compat_BulkMessage_Send(Dev,arg);
            break;

        case WRG5_COMPAT_IOCTL_USB_BULK_MESSAGE_RECEIVE:
            Error=WRG5USB_Compat_BulkMessage_Receive(Dev,arg);
            break;

        case WRG5_COMPAT_IOCTL_USB_CONTROL_MESSAGE:
            Error=WRG5USB_Compat_ControlMessage(Dev,arg);
            break;
        case WRG5_COMPAT_IOCTL_USB_CONTROL_MESSAGE_SEND:
            Error=WRG5USB_Compat_ControlMessage_Send(Dev,arg);
            break;
        case WRG5_COMPAT_IOCTL_USB_CONTROL_MESSAGE_RECEIVE:
            Error=WRG5USB_Compat_ControlMessage_Receive(Dev,arg);
            break;
        case WRG5_IOCTL_USB_CONFIGURE:
            if(access_ok(VERIFY_WRITE,(void __user*)arg,sizeof(__u8)))
            {
                if(!get_user(Value8,(__u8 __user*)arg))
                {
                    Error=WRG5USB_Configure(Dev,Value8,1);
                }
                else
                {
                    Error=-EFAULT;
                }
            }
            else
            {
                Error=-EFAULT;
            }
            break;

        case WRG5_IOCTL_USB_GET_ENDPOINTINFO:
            Error=WRG5USB_GetEndPointInfo(Dev,arg);
            break;

        case WRG5_IOCTL_USB_START_ASYNC_MESSAGES:
            Error=WRG5USB_StartAsyncMessages(Dev);
            break;
        case WRG5_IOCTL_USB_STOP_ASYNC_MESSAGES:
            Error=WRG5USB_StopAsyncMessages(Dev);
            break;

        default:
            Error=-EINVAL;
            break;
    }

    WRG5_UNLOCK(Dev->Ioctl_Lock);
 
    return Error;
}

#endif 

#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,26)
static void WRG5ReleaseCD(struct device *cd)
#else
static void WRG5ReleaseCD(struct class_device *cd)
#endif
{
}


int RegisterWRG5Device(PWRG5 Dev)
{
 int i;
 int DeviceIndex;
 int Minor;
 dev_t DevNo;
 int Error;

    DeviceIndex=-1;


    if(WRG5_LOCK_INTERRUPTIBLE(WRG5_Device_Lock))
    {
        return -ERESTARTSYS;
    }

    if(Dev->DeviceIndex>=0)
    {
        //device is already registered

        WRG5_UNLOCK(WRG5_Device_Lock);
        return 0;
    }

    for(i=0;i<MAX_WRG5_DEVICES;i++)
    {
        if(WRG5_Devices[i]==NULL)
        {
            DeviceIndex=i;
            break;
        }
    }

    if(DeviceIndex<0)
    {
        WRG5_UNLOCK(WRG5_Device_Lock);
        printk(KERN_ERR WRG5_DISPLAY_NAME ": Maximum number of devices [%d] exceeded.\n",MAX_WRG5_DEVICES);
        return -ENFILE;
    }

    Minor=WRG5_MINOR_BASE+DeviceIndex;

    DevNo=MKDEV(WiNRADiO_MAJOR,Minor);

    Dev->Minor=Minor;
    Dev->DeviceIndex=DeviceIndex;

    memset(&Dev->ClassDevice,0,sizeof(Dev->ClassDevice));

#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,26)
    Dev->ClassDevice.class=&WRG5_Class;
    Dev->ClassDevice.devt=MKDEV(WiNRADiO_MAJOR,Minor);
    //snprintf(Dev->ClassDevice.bus_id,BUS_ID_SIZE,WRG5_NAME"%d",DeviceIndex);
    dev_set_name(&Dev->ClassDevice,WRG5_NAME"%d",DeviceIndex);

    Error=device_register(&Dev->ClassDevice);
#else
    class_device_initialize(&Dev->ClassDevice);

    Dev->ClassDevice.class=&WRG5_Class;
    Dev->ClassDevice.devt=MKDEV(WiNRADiO_MAJOR,Minor);
    snprintf(Dev->ClassDevice.class_id,BUS_ID_SIZE,WRG5_NAME"%d",DeviceIndex);

    Error=class_device_register(&Dev->ClassDevice);
#endif


    if(Error<0)
    {
        WRG5_UNLOCK(WRG5_Device_Lock);
        printk(KERN_ERR WRG5_DISPLAY_NAME ": Failed to register class (%d).\n",Error);
        return Error;
    }

    cdev_init(&Dev->cdev,&WRG5_File_Operations);
    Dev->cdev.owner=THIS_MODULE;
    Dev->cdev.ops=&WRG5_File_Operations;
    Error=cdev_add(&Dev->cdev,DevNo,1);

    if(Error<0)
    {
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,26)
        device_unregister(&Dev->ClassDevice);
#else
        class_device_unregister(&Dev->ClassDevice);
#endif
        WRG5_UNLOCK(WRG5_Device_Lock);
        printk(KERN_ERR WRG5_DISPLAY_NAME ": Failed to add character device (%d).\n",Error);
        return Error;
    }

    WRG5_Devices[DeviceIndex]=Dev;

    WRG5_UNLOCK(WRG5_Device_Lock);

    return 0;
}

int UnregisterWRG5Device(PWRG5 Dev)
{
    if(WRG5_LOCK_INTERRUPTIBLE(WRG5_Device_Lock))
    {
        return -ERESTARTSYS;
    }

    if(Dev->DeviceIndex>=0 && Dev->DeviceIndex<MAX_WRG5_DEVICES)
    {
        if(WRG5_Devices[Dev->DeviceIndex]==Dev)
        {
            WRG5_Devices[Dev->DeviceIndex]=NULL;
            cdev_del(&Dev->cdev);
#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,6,26)
            device_unregister(&Dev->ClassDevice);
#else
            class_device_unregister(&Dev->ClassDevice);
#endif
        }
    }

    WRG5_UNLOCK(WRG5_Device_Lock);

    return 0;
}

module_init(WRG5_Init);
module_exit(WRG5_Exit);

MODULE_LICENSE("GPL");

//EXPORT_SYMBOL_GPL();
