/*****************************************************************************
* voicesync.c	Amfeltec VoiceSync - USB Voice Sync Timer Interface
*
*
* Copyright:	(c) 2010 Amfeltec Corp.
*
* ============================================================================
* June 10, 2008		Initial version.
*****************************************************************************/

/***************************************************************************
****		I N C L U D E  		F I L E S			****
***************************************************************************/


# include "os/os_abstr_includes.h"

#if defined(__Linux__)
# include <linux/tty.h>
# include <linux/usb.h>
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
    # include <linux/usb/input.h>
# else
	/*input.h is really not used in 1.0.5....uses a function to decode the ID values to the registered names*/
# endif
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
    # include <linux/hid.h>
# else
	# include <linux/hiddev.h>
# endif
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
	# include <linux/usb/serial.h>
# else
	# define MAX_NUM_PORTS 8  //This is normally defined in /linux/usb/serial.h
# endif
# include <linux/input.h>

#endif

#include "os/os_abstr_defines.h"
#include "os/os_abstr_debug.h"
#include "os/os_abstr_basic.h"
#include "os/os_abstr_time.h"

#if defined(ENABLE_STATS)
# include "voicesync_stats.h"
#endif

#include "voicesync.h"

/***************************************************************************
****		   	  M A C R O S / D E F I N E S			****
***************************************************************************/
#define VSUSB_NAME		"vsusb"
#define VSUSB_DESC		"Amfeltec VoiceSync (usb)"

#define VSUSB_VENDOR_ID		0x10C4	
#define VSUSB_PRODUCT_ID	0x8599	

#define VSUSB_NAME_LEN		20

#define VSUSB_STATUS_READY		1
#define VSUSB_STATUS_DOWN		2

#define VSUSB_RXBYTE_PATTERN	0x41
#define VSUSB_MAX_RX_LEN	1				// number of bytes per ms (real)
#define VSUSB_STATS_CALC    	1       			// sec delay to calculate stats
#define VSUSB_RX_IN_SEC		(1000) 				// number of rx in a sec
#define VSUSB_RXBYTES_SEC		(VSUSB_MAX_RX_LEN * VSUSB_RX_IN_SEC)	// number of bytes per sec
#define VSUSB_RXBYTES_USEC  	(VSUSB_RXBYTES_SEC/1000000)	// number of bytes per usec		

#define VSUSB_TRANSFER_BULK	1
#define VSUSB_TRANSFER_INTR	2

/***************************************************************************
****		     S T R U C T U R E S / T Y P E D E F S		****
***************************************************************************/
typedef struct vs_softc_
{
	char				name[VSUSB_NAME_LEN];
	char 				phys[64];		//hid
	int				transfer_type;

	struct usb_interface		*usb_intf;
	struct usb_device		*usb_dev;
	struct input_dev 		*idev;
	int				usb_class;
	
	unsigned int			status;
        struct urb			*rx_urb;
	unsigned char			*rxdata;
	dma_addr_t			rxdata_dma;		//hid

	uint32_t			irqcount;
#if defined(ENABLE_STATS)
	voicesync_stats_t		stats;
#endif
	int				err_rx_len;
	int				err_rx_data;

	voicesync_dahdi_t		dahdi;

	OS_LIST_ENTRY(vs_softc_)	next;

} vs_softc_t;

struct vs_desc {
        char	*name;
        int	adptr_type;
	int	transfer_type;
};

/***************************************************************************
****			F U N C T I O N  P R O T O T Y P E S		****
***************************************************************************/

static int vs_probe(struct usb_interface*, const struct usb_device_id*);
static void vs_disconnect(struct usb_interface*);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) 
	static int vs_suspend (struct usb_interface*, pm_message_t);
#else
	static int vs_suspend (struct usb_interface*, u32 msg);
#endif
static int vs_resume (struct usb_interface*);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
	static int vs_prereset (struct usb_interface*);
	static int vs_postreset (struct usb_interface*);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
	static void vs_prereset (struct usb_interface*);
	static void vs_postreset (struct usb_interface*);
#endif


static int vs_setup(struct usb_interface*);
static int vs_remove(struct usb_interface*);
static int vs_start_transfer(vs_softc_t*, struct urb*);
static int vs_setup_cp2102_urbs(vs_softc_t *sc);
#if 0
static int vs_setup_hid(vs_softc_t *sc);
#endif

/***************************************************************************
****			   G L O B A L  V A R I A B L E S			****
***************************************************************************/

static struct vs_desc vs_dinfo = { VSUSB_DESC, 0, VSUSB_TRANSFER_BULK };
//static struct vs_desc vs_dinfo2 = { VSUSB_MOD_DESC, 0, VSUSB_TRANSFER_INTR };

OS_LIST_HEAD(, vs_softc_) vs_softc_head = 
			OS_LIST_HEAD_INITIALIZER(&vs_softc_head);

static struct usb_device_id vs_dev_ids[] = {

	{ 
		match_flags: (USB_DEVICE_ID_MATCH_VENDOR|USB_DEVICE_ID_MATCH_DEVICE),
		bInterfaceClass: USB_CLASS_AUDIO,
		bInterfaceSubClass: 1,
		idVendor:	VSUSB_VENDOR_ID,
		idProduct: 	VSUSB_PRODUCT_ID,
		driver_info:	(unsigned long)&vs_dinfo,
	},
	{ }     /* Terminating Entry */
};

static struct usb_driver vs_driver =
{
	name: 		VSUSB_NAME,
	probe: 		vs_probe,
	disconnect:	vs_disconnect,
	suspend:	vs_suspend,
	resume:		vs_resume,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
	pre_reset:	vs_prereset,
	post_reset:	vs_postreset,
#endif
	id_table:	vs_dev_ids,
};


/***************************************************************************
****			 F U N C T I O N  D E F I N I T I O N		****
***************************************************************************/

int voicesync_usb_register(void)
{
	int	ret;

	OS_LIST_INIT(&vs_softc_head);

	/* USB-FXO */
	ret = usb_register(&vs_driver);
	if (ret){
		DEBUG_EVENT("ERROR: Failed to register %s (usb) device!\n",
				VSUSB_NAME);
		return ret;
	}

	return 0;
}

int voicesync_usb_unregister(void)
{
	vs_softc_t	*sc = NULL;

	/* Mark all devices down */
	OS_LIST_FOREACH(sc, &vs_softc_head, next){
		os_set_bit(VSUSB_STATUS_DOWN, &sc->status);
	}
	usb_deregister(&vs_driver);

	DEBUG_EVENT("%s: %s Driver Unloaded\n",
			VSUSB_NAME, VSUSB_DESC);
	return 0;
}

/****************************************************************************
****************************************************************************/
static int
vs_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	struct usb_device	*dev = interface_to_usbdev(intf);
	struct vs_desc 	*dinfo = (struct vs_desc *)id->driver_info;
	vs_softc_t		*sc = NULL;

	DEBUG_EVENT("%s: Probing %s device on %d ...\n",
				VSUSB_NAME, 
				dinfo->name, dev->devnum);

	if ((sc= usb_get_intfdata(intf)) != NULL){
		DEBUG_EVENT("%s: Force to add usb device!\n",
				VSUSB_NAME);
	}

	if (vs_setup(intf)){
		DEBUG_EVENT("%s: Failed to setup VoiceSync device!\n",
				VSUSB_NAME);
		return -ENODEV;
	}

	return 0;
}

static void vs_disconnect(struct usb_interface *intf)
{
	struct usb_device	*dev = interface_to_usbdev(intf);

	DEBUG_EVENT("%s: Disconnecting %s device from %d ...\n",
				VSUSB_NAME, VSUSB_DESC, dev->devnum);
	vs_remove(intf);
	return;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
static int vs_suspend (struct usb_interface *intf, pm_message_t message)
{
	struct usb_device	*dev = interface_to_usbdev(intf);

	DEBUG_EVENT("%s: WARNING: Suspend USB device on %d \n",
				VSUSB_NAME, dev->devnum);
	return 0;
}
#else
static int vs_suspend (struct usb_interface *intf, u32 msg)
{
	struct usb_device	*dev = interface_to_usbdev(intf);

	DEBUG_EVENT("%s: WARNING: Suspend USB device on %d \n",
				VSUSB_NAME, dev->devnum);
	return 0;
}
#endif

static int vs_resume (struct usb_interface *intf)
{
	struct usb_device	*dev = interface_to_usbdev(intf);

	DEBUG_EVENT("%s: WARNING: Resume USB device on %d \n",
				VSUSB_NAME, dev->devnum);
	return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
static int vs_prereset (struct usb_interface *intf)
# else
static void vs_prereset (struct usb_interface *intf)
# endif
{
	struct usb_device	*dev = interface_to_usbdev(intf);

	DEBUG_EVENT("%s: WARNING: Pre-reset USB device on %d \n",
				VSUSB_NAME, dev->devnum);

# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
	return 0;
# else
	return;
# endif
}
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
static int vs_postreset (struct usb_interface *intf)
# else
static void vs_postreset (struct usb_interface *intf)
# endif
{
	struct usb_device	*dev = interface_to_usbdev(intf);

	DEBUG_EVENT("%s: WARNING: Post-Reset USB device on %d \n",
				VSUSB_NAME, dev->devnum);
	
# if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23))
	return 0;
# else
	return;
# endif
}
#endif

/****************************************************************************
****************************************************************************/

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
void vs_read_complete(struct urb *q, struct pt_regs *regs)
#else
void vs_read_complete(struct urb *q)
#endif
{
	vs_softc_t	*sc = (vs_softc_t*)q->context;

	OS_ASSERT_VOID(sc == NULL);

	sc->irqcount++;
#if defined(ENABLE_STATS)
	sc->stats.rx_bytes += VS_RX_LEN;
#endif

	if (os_test_bit(VSUSB_STATUS_DOWN, &sc->status)){
		goto read_complete_done;
	}
	if (!os_test_bit(VSUSB_STATUS_READY, &sc->status)){
		DEBUG_EVENT("%s: ERROR: RX USB core is not ready!\n",
					sc->name);
		goto read_complete_done;
	}
	if (q->actual_length != VSUSB_MAX_RX_LEN){
		sc->err_rx_len++;
	}else if (sc->rxdata[0] != VSUSB_RXBYTE_PATTERN){
		sc->err_rx_data++;
	}

	zt_receive(&sc->dahdi.span);
	zt_transmit(&sc->dahdi.span);

	if (vs_start_transfer(sc, q)){
		DEBUG_RX(
		"%s: Failed to re-start RX transfer request!\n",
					sc->name);
		goto read_complete_done;
	}

read_complete_done:
	return;
}

static int vs_start_transfer(vs_softc_t *sc, struct urb *q)
{
	int	ret;

	if (os_test_bit(VSUSB_STATUS_DOWN, &sc->status)){
		return -EINVAL; 
	}

	ret = usb_submit_urb(q, GFP_KERNEL); 
	if (ret){
		DEBUG_TEST(
		"%s: Failed to issue transfer request (%d)!\n",
					sc->name, ret);
		return -EINVAL;
	}
	return 0;
}


#define usb_endpoint_dir_in(epd)					\
		(((epd)->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
#define usb_endpoint_dir_out(epd)					\
		(((epd)->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
#define usb_endpoint_xfer_bulk(epd)					\
		(((epd)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==	\
		USB_ENDPOINT_XFER_BULK)
#define usb_endpoint_is_bulk_in(epd)					\
		(usb_endpoint_xfer_bulk((epd)) && usb_endpoint_dir_in(epd))
#define usb_endpoint_is_bulk_out(epd)					\
		(usb_endpoint_xfer_bulk((epd)) && usb_endpoint_dir_out(epd))

static int vs_setup_cp2102_urbs(vs_softc_t *sc)
{
	struct usb_host_interface	*iface_desc;
	struct usb_endpoint_descriptor	*endpoint;
	struct usb_endpoint_descriptor	*bulk_in_endpoint[MAX_NUM_PORTS];
	int				num_bulk_in = 0;
	int				i = 0;

	OS_ASSERT(sc == NULL);
	OS_ASSERT(sc->usb_intf == NULL);

	/* descriptor matches, let's find the endpoints needed */
	/* check out the endpoints */
	iface_desc = sc->usb_intf->cur_altsetting;
	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
		endpoint = &iface_desc->endpoint[i].desc;

		if (usb_endpoint_is_bulk_in(endpoint)) {
			/* we found a bulk in endpoint */
			DEBUG_TEST("%s: Found bulk in on endpoint %d\n", 
						sc->name, i);
			bulk_in_endpoint[num_bulk_in] = endpoint;
			++num_bulk_in;
		}
	}

	DEBUG_TEST("%s: Bulk in: %d MaxLen: %d\n",
				sc->name, num_bulk_in, VSUSB_MAX_RX_LEN);
	/* set up the endpoint information */
	for (i = 0; i < num_bulk_in; ++i) {
		endpoint = bulk_in_endpoint[i];
		sc->rxdata = os_malloc(endpoint->wMaxPacketSize + 1);
		if (sc->rxdata == NULL){
			DEBUG_EVENT("%s: Failed to allocate rx buffer!\n", sc->name);
			return -ENOMEM;
		}
		usb_init_urb(sc->rx_urb);
		usb_fill_bulk_urb (
				sc->rx_urb,
				sc->usb_dev,
				usb_rcvbulkpipe (sc->usb_dev,
						   endpoint->bEndpointAddress),
				sc->rxdata, 
				VSUSB_MAX_RX_LEN,
				vs_read_complete,
				sc);	//p
		DEBUG_TEST("%s: Bulk Int ENAddress %X\n",
					sc->name,
					endpoint->bEndpointAddress);
	}
	
	return 0;
}

#define REQTYPE_HOST_TO_DEVICE	0x41
#define REQTYPE_DEVICE_TO_HOST	0xc1

/* Config SET requests. To GET, add 1 to the request number */
#define CP2101_UART 		0x00	/* Enable / Disable */
#define CP2101_BAUDRATE		0x01	/* (BAUD_RATE_GEN_FREQ / baudrate) */
#define CP2101_BITS		0x03	/* 0x(0)(databits)(parity)(stopbits) */
#define CP2101_BREAK		0x05	/* On / Off */
#define CP2101_CONTROL		0x07	/* Flow control line states */
#define CP2101_MODEMCTL		0x13	/* Modem controls */
#define CP2101_CONFIG_6		0x19	/* 6 bytes of config data ??? */

/* CP2101_UART */
#define UART_ENABLE		0x0001
#define UART_DISABLE		0x0000

/* CP2101_BAUDRATE */
#define BAUD_RATE_GEN_FREQ	0x384000

/* CP2101_BITS */
#define BITS_DATA_MASK		0X0f00
#define BITS_DATA_5		0X0500
#define BITS_DATA_6		0X0600
#define BITS_DATA_7		0X0700
#define BITS_DATA_8		0X0800
#define BITS_DATA_9		0X0900

#define BITS_PARITY_MASK	0x00f0
#define BITS_PARITY_NONE	0x0000
#define BITS_PARITY_ODD		0x0010
#define BITS_PARITY_EVEN	0x0020
#define BITS_PARITY_MARK	0x0030
#define BITS_PARITY_SPACE	0x0040

#define BITS_STOP_MASK		0x000f
#define BITS_STOP_1		0x0000
#define BITS_STOP_1_5		0x0001
#define BITS_STOP_2		0x0002


static int 
vs_set_config(vs_softc_t *sc, u8 request, unsigned int *data, int size)
{
//struct sdla_usb_pvt *p;
	__le32 *buf;
	int result, i, length;

	OS_ASSERT(sc->usb_dev == NULL);
	/* Number of integers required to contain the array */
	length = (((size - 1) | 3) + 1)/4;

	buf = kmalloc(length * sizeof(__le32), GFP_KERNEL);
	if (!buf) {
		DEBUG_EVENT("%s: ERROR: Failed to allocate memory (%ld bytes)!\n",
					sc->name, (unsigned long)(length*sizeof(__le32)));
		return -ENOMEM;
	}

	/* Array of integers into bytes */
	for (i = 0; i < length; i++)
		buf[i] = cpu_to_le32(data[i]);

	if (size > 2) {
		result = usb_control_msg (sc->usb_dev,
				usb_sndctrlpipe(sc->usb_dev, 0),
				request, REQTYPE_HOST_TO_DEVICE, 0x0000,
				0, buf, size, 300);
	} else {
		result = usb_control_msg (sc->usb_dev,
				usb_sndctrlpipe(sc->usb_dev, 0),
				request, REQTYPE_HOST_TO_DEVICE, data[0],
				0, NULL, 0, 300);
	}

	kfree(buf);

	if ((size > 2 && result != size) || result < 0) {
		DEBUG_EVENT("%s: ERROR: Failed to send request (%X:%d:%d)!\n",
					sc->name, request, size, result);
		return -EPROTO;
	}

	/* Single data value */
	result = usb_control_msg(sc->usb_dev,
				usb_sndctrlpipe(sc->usb_dev, 0),
				request, REQTYPE_HOST_TO_DEVICE, data[0],
				0, NULL, 0, 300);
	return 0;
}


/*
 * cp2101_get_config
 * Reads from the CP2101 configuration registers
 * 'size' is specified in bytes.
 * 'data' is a pointer to a pre-allocated array of integers large
 * enough to hold 'size' bytes (with 4 bytes to each integer)
 */
static int 
vs_get_config(vs_softc_t *sc, u8 request, unsigned int *data, int size)
{
//struct sdla_usb_pvt *p;
	__le32 *buf;
	int result, i, length;

	OS_ASSERT(sc->usb_dev == NULL);
	/* Number of integers required to contain the array */
	length = (((size - 1) | 3) + 1)/4;

	buf = kcalloc(length, sizeof(__le32), GFP_KERNEL);
	if (!buf) {
		DEBUG_EVENT("%s: ERROR: Failed allocate memory (%ld bytes)!\n",
					sc->name, (unsigned long)(length*sizeof(__le32)));
		return -ENOMEM;
	}

	/* For get requests, the request number must be incremented */
	request++;

	/* Issue the request, attempting to read 'size' bytes */
	result = usb_control_msg (	sc->usb_dev,
					usb_rcvctrlpipe (sc->usb_dev, 0),
					request, REQTYPE_DEVICE_TO_HOST, 0x0000,
					0, buf, size, 300);

	/* Convert data into an array of integers */
	for (i=0; i<length; i++)
		data[i] = le32_to_cpu(buf[i]);

	kfree(buf);

	if (result != size) {
		DEBUG_EVENT("%s: ERROR: Unable to send config request (%X:%d:%d)!\n",
					sc->name, request, size, result);
		return -EPROTO;
	}

	return 0;
}

/*
 * cp2101_set_config_single
 * Convenience function for calling cp2101_set_config on single data values
 * without requiring an integer pointer
 */
static inline int 
vs_set_config_single(vs_softc_t *sc, u8 request, unsigned int data)
{
	return vs_set_config(sc, request, &data, 2);
}

static int vs_setup_cp2102(vs_softc_t *sc)
{
	unsigned int	baud, bits;

	if (vs_set_config_single(sc, CP2101_UART, UART_ENABLE)) {
		DEBUG_EVENT("%s: ERROR: Failed to enable UART!\n",
					sc->name);
		return -EINVAL;
	}

//	baud = 512000;	//9600;
//	baud = 500000;	//9600;
// Original -->	baud = 921600;
	baud = 230400;
	if (vs_set_config_single(sc, CP2101_BAUDRATE, (BAUD_RATE_GEN_FREQ / baud))){
		DEBUG_EVENT("%s: ERROR: Failed to set new baud rate (%d bps)!\n",
					sc->name, baud);
		return -EINVAL;
	}

	vs_get_config(sc, CP2101_BITS, &bits, 2);
	bits &= ~BITS_DATA_MASK;
	bits |= BITS_DATA_8;
	if (vs_set_config(sc, CP2101_BITS, &bits, 2)){
		DEBUG_EVENT("%s: ERROR: Failed set new data bits (%X)!\n",
					sc->name, bits);
		return -EINVAL;
	}

	//os_spin_lock_irq_init(&sc->ilock, VSUSB_NAME);

	if (vs_setup_cp2102_urbs(sc)) {
		DEBUG_EVENT("%s: ERROR: Failed to prepare the urbs for transfer!\n",
					sc->name);
		return -EINVAL;
	}
	sc->usb_class = USB_CLASS_VENDOR_SPEC;
	return 0;
}

#if 0
static int vs_setup_hid(vs_softc_t *sc)
{
	struct input_dev *input_dev;
	struct usb_endpoint_descriptor *endpoint;
	struct usb_host_interface *interface;
	int pipe, maxp, status;

	OS_ASSERT(sc == NULL);
	OS_ASSERT(sc->usb_intf == NULL);

	interface = sc->usb_intf->cur_altsetting;
	if (interface->desc.bNumEndpoints != 1){
		return -ENODEV;
	}

	endpoint = &interface->endpoint[0].desc;
	if (!usb_endpoint_is_int_in(endpoint)){
		return -ENODEV;
	}

	sc->rxdata = usb_buffer_alloc(sc->usb_dev, 8, GFP_ATOMIC, &sc->rxdata_dma);
	if (!sc->rxdata){
		return -ENOMEM;
	}

	usb_make_path(sc->usb_dev, sc->phys, sizeof(sc->phys));
	strlcat(sc->phys, "/input0", sizeof(sc->phys));

	input_dev = input_allocate_device();
	if (!input_dev){
		os_usb_buffer_free(sc->usb_dev, 8, sc->rxdata, sc->rxdata_dma);
		DEBUG_EVENT("%s: Failed allocate input dev!\n",
				VSUSB_NAME);
		return -ENOMEM;
	}
	sc->idev = input_dev;

	input_dev->name = sc->name;
	input_dev->phys = sc->phys;
	usb_to_input_id(sc->usb_dev, &input_dev->id);
	input_dev->dev.parent = &sc->usb_intf->dev;

	pipe = usb_rcvintpipe(sc->usb_dev, endpoint->bEndpointAddress);

	usb_fill_int_urb(sc->rx_urb, sc->usb_dev, pipe, sc->rxdata, VSUSB_MAX_RX_LEN,
			 vs_read_complete, sc, endpoint->bInterval);
 	sc->rx_urb->transfer_dma = sc->rxdata_dma;
	sc->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

	if (input_register_device(sc->idev)){
		DEBUG_EVENT("%s: Failed allocate input dev!\n",
					VSUSB_NAME);
		input_free_device(input_dev);
		os_usb_buffer_free(sc->usb_dev, 8, sc->rxdata, sc->rxdata_dma);
		return -ENODEV;
	}
	sc->usb_class = USB_CLASS_HID;
	return 0;
}
#endif

static int vs_setup(struct usb_interface *intf)
{
	struct usb_device	*dev = interface_to_usbdev(intf);
	vs_softc_t		*sc = NULL;
	int			err;

	if (voicesync_register() != 0){
		return -EINVAL;
	}

	DEBUG_TEST("%s: Creating private data...\n", VSUSB_NAME);
	sc = os_malloc(sizeof(vs_softc_t));
	if (!sc) {
		DEBUG_EVENT("%s: Failed allocate private data!\n",
				VSUSB_NAME);
		return -ENODEV;
	}
	memset(sc, 0, sizeof(vs_softc_t));
	sprintf(sc->name, "%s0", VSUSB_NAME);
	sc->usb_intf	= intf;
	sc->usb_dev	= dev;

	sc->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!sc->rx_urb){
		DEBUG_EVENT("%s: Failed allocate irq!\n",
				VSUSB_NAME);
		os_free(sc);
		return -ENOMEM;
	}

	err = vs_setup_cp2102(sc);
	//err = vs_setup_hid(sc);

	if (err){
		usb_free_urb(sc->rx_urb);
		os_free(sc);
		return -ENODEV;
	}

	usb_set_intfdata(sc->usb_intf, sc);
	OS_LIST_INSERT_HEAD(&vs_softc_head, sc, next);
	os_set_bit(VSUSB_STATUS_READY, &sc->status);

	if (voicesync_initialize(&sc->dahdi)){
		DEBUG_EVENT("%s: Failed to register into Dahdi/Zaptel driver!\n",
					sc->name);
		goto vs_setup_error;
	}

	if (vs_start_transfer(sc, sc->rx_urb)) {
		DEBUG_EVENT("%s: Failed to start RX transfer!\n",
					sc->name);
		zt_unregister(&sc->dahdi.span);
		goto vs_setup_error;
	}
#if defined(ENABLE_STATS)
	voicesync_stats_init(&sc->stats);
#endif

	return 0;

vs_setup_error:

	OS_LIST_REMOVE(sc, next);
	os_clear_bit(VSUSB_STATUS_READY, &sc->status);
	if (sc->usb_class == USB_CLASS_HID){
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) 
		input_free_device(sc->idev);
#else
		input_unregister_device(sc->idev);
#endif
		os_usb_buffer_free(sc->usb_dev, 8, sc->rxdata, sc->rxdata_dma);
	}else{
		if (sc->rxdata) os_free(sc->rxdata);
	}
	usb_set_intfdata(sc->usb_intf, NULL);
	if (sc) os_free(sc);
	return -ENODEV;
}

static int vs_remove(struct usb_interface *intf)
{
	vs_softc_t	*sc;

	DEBUG_TEST("Releasing private data...\n");

#if defined(ENABLE_STATS)
	voicesync_stats_release(&sc->stats);
#endif

#if VS_VERBOSE >= 1
	DEBUG_EVENT("%s: Voicesync Error Stats: Len=%d Data=%d\n",
			sc->name, sc->err_rx_len, sc->err_rx_data);
#endif

	if ((sc = usb_get_intfdata(intf)) != NULL){

		os_clear_bit(VSUSB_STATUS_READY, &sc->status);

		zt_unregister(&sc->dahdi.span);

		OS_LIST_REMOVE(sc, next);
		usb_unlink_urb(sc->rx_urb);
		usb_kill_urb(sc->rx_urb);

		if (sc->usb_class == USB_CLASS_HID){
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) 
		input_free_device(sc->idev);
#else
		input_unregister_device(sc->idev);
#endif
			os_usb_buffer_free(sc->usb_dev, 8, sc->rxdata, sc->rxdata_dma);
		}else{
			if (sc->rxdata) os_free(sc->rxdata);
		}
		usb_set_intfdata(sc->usb_intf, NULL);
		if (sc) os_free(sc);
	}else{

		DEBUG_EVENT("%s: Internal Error: [%s:%d]\n",
				VSUSB_NAME, __FUNCTION__,__LINE__);
	}
	
	voicesync_unregister();
	return 0;
}
