www.pudn.com > voipAutoDialer.rar > voip-device.c, change:2002-05-31,size:49152b


/*
 * voipblaster.c
 * (C) Copyright 2001, 2002 by Thomas Davis (tdavis@beeble.homelinux.net)
 * (C) Copyright 2001 by Michael Bosland <mike@ring.org>
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/errno.h>

#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>

#include <linux/usb.h>
#include <asm/string.h>

#include "telephony.h"
#include "voip-ver.h"
#include "voip-config.h"
#include "ixjuser.h"
#include "phonedev.h"
#include "voipblaster.h"

/* These lines are taken from the netbsd port.  */

static u_char init_1[] =
{
    0x3b,0x00,0x40,0x8b // first 2 bytes is length, second 2 bytes is command?
};

static u_char init_2[] =
{
    0x00,0x01,0x00,0x00,0x18,0x02,0x8f,0x00,
    0x10,0x00,0x28,0x40,0x03,0x1a,0x0d,0x0c,
    0xfa,0x43,0xfd,0xea,0x93,0xfe,0x1a,0x41,
    0x00,0x4a,0x93,0xfe,0x2a,0x40,0x00,0x1a,
    0x93,0xfe,0x3a,0x0a,0x00,0x1f,0x3c,0x00,
    0x8c,0x0d,0x03,0xa3,0x23,0xa2,0xdf,0x0d,
    0x0c,0x3a,0x40,0x00,0x2a,0x93,0xfe,0x4a,
    0x0a,0x00,0x0f
};

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)

static struct usb_device_id voipblaster_ids[] = {
	{ USB_DEVICE(0x1292,0x258) }, /* Vendor/Device */
	{ },
};

MODULE_DEVICE_TABLE (usb, voipblaster_ids);

#endif

static struct usb_voipblaster *voipblaster_table[VOIP_MAX];

/* lock to protect the voipblaster_table structure */
static DECLARE_MUTEX (voipblaster_table_mutex);

/* local function prototypes */
static ssize_t voipblaster_read(struct file *file, char *buffer, size_t count, loff_t *ppos);
static ssize_t voipblaster_write(struct file *file, const char *buffer, size_t count, loff_t *ppos);
static int voipblaster_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
static int voipblaster_release(struct inode *inode, struct file *file);
static unsigned int voipblaster_poll(struct file *file, poll_table * wait);
static int voipblaster_fasync(int fd, struct file *file, int mode);

/* external functions */
extern void voipblaster_register_device(struct usb_voipblaster *);
extern void voipblaster_proc_init(void);
extern void voipblaster_proc_destroy(void);
extern int voipblaster_get_status_proc(char *);
extern int voipblaster_read_proc(char *page, char **start, off_t off,
				 int count, int *eof, void *data);

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
static void *voipblaster_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);
#else
static void *voipblaster_probe(struct usb_device *dev, unsigned int ifnum);
#endif

static void voipblaster_disconnect(struct usb_device *dev, void *ptr);

struct file_operations voipblaster_fops =
{
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
        owner:          THIS_MODULE,
#endif
        read:           voipblaster_read,
        write:          voipblaster_write,
        poll:           voipblaster_poll,
        ioctl:          voipblaster_ioctl,
        release:        voipblaster_release,
        fasync:         voipblaster_fasync
};


#define VOIP_DEBUG
#ifdef VOIP_DEBUG
	static int VOIPdebug = 1;
#else
	static int VOIPdebug = 0;
#endif

char VOIPversion[32];
static int hertz;

/* Use our own dbg macro */
#undef dbg
#define dbg(format, arg...) do { if (VOIPdebug) printk(KERN_DEBUG __FUNCTION__ ": " format "\n" , ## arg); } while (0)

#undef info
#define info(format, arg...) printk(KERN_INFO __FUNCTION__ ": " format "\n" , ## arg)

#define	get_voipblaster(b)	voipblaster_table[(b)]

extern __inline__ void voipblaster_kill_fasync(struct usb_voipblaster *dev, IXJ_SIGEVENT event, int dir)
{
	dbg("minor %d, event = %d", dev->minor, event);

	if(dev->signals[event]) {
		dbg("Sending signal for event %d", event);
		/* Send apps notice of change */
		/* see config.h for macro definition */
		VOIP_KILL_FASYNC(dev->async_queue, dev->signals[event], dir);
	}
}

static int capabilities_check(struct usb_voipblaster *dev, struct phone_capability *pcreq)
{
	int cnt;
	int retval = 0;
	for (cnt = 0; cnt < dev->caps; cnt++) {
		if (pcreq->captype == dev->caplist[cnt].captype
		    && pcreq->cap == dev->caplist[cnt].cap) {
			retval = 1;
			break;
		}
	}
	return retval;
}

static void add_caps(struct usb_voipblaster *j)
{

	j->caps = 0;
	j->caplist[j->caps].cap = PHONE_VENDOR_VOIPBLASTER;
	strcpy(j->caplist[j->caps].desc, "Linux Hackers, Inc");
	j->caplist[j->caps].captype = vendor;
	j->caplist[j->caps].handle = j->caps++;
	j->caplist[j->caps].captype = device;

	switch (j->cardtype) {
	case CREATIVE_VOIPBLASTER:
		strcpy(j->caplist[j->caps].desc, "Creative VOIP Blaster/USB");
		break;
	default:
		strcpy(j->caplist[j->caps].desc, "Unknown Card");
		break;
	}

	j->caplist[j->caps].cap = j->cardtype;
	j->caplist[j->caps].handle = j->caps++;
	strcpy(j->caplist[j->caps].desc, "POTS");
	j->caplist[j->caps].captype = port;
	j->caplist[j->caps].cap = pots;
	j->caplist[j->caps].handle = j->caps++;

 	/* add devices that can do speaker/mic */
	switch (j->cardtype) {
	case CREATIVE_VOIPBLASTER:
		strcpy(j->caplist[j->caps].desc, "SPEAKER");
		j->caplist[j->caps].captype = port;
		j->caplist[j->caps].cap = speaker;
		j->caplist[j->caps].handle = j->caps++;
        default:
     		break;
	}

#if 0
 	/* add devices that can do handset */
	switch (j->cardtype) {
	case CREATIVE_VOIPBLASTER:
		strcpy(j->caplist[j->caps].desc, "HANDSET");
		j->caplist[j->caps].captype = port;
		j->caplist[j->caps].cap = handset;
		j->caplist[j->caps].handle = j->caps++;
		break;
        default:
     		break;
	}
#endif

#define ENABLE_G723_63 1

#if defined(ENABLE_G723_63)
	strcpy(j->caplist[j->caps].desc, "G.723.1 6.3kbps");
	j->caplist[j->caps].captype = codec;
	j->caplist[j->caps].cap = G723_63;
	j->caplist[j->caps].handle = j->caps++;
#endif

	strcpy(j->caplist[j->caps].desc, "G.723.1 5.3kbps");
	j->caplist[j->caps].captype = codec;
	j->caplist[j->caps].cap = G723_53;
	j->caplist[j->caps].handle = j->caps++;
}

static void voipblaster_event_irq(urb_t *urb)
{
	struct usb_voipblaster *dev = urb->context;
	char event;
	unsigned long flags;

	/* Received an event from the voipblaster.  Place it in the event
	   buffer if there is any room.  If it is DTMF data place it in the
	   DTMF buffer instead.  Maybe signal somebody that some new stuff
	   has arrived. */

	spin_lock_irqsave(&dev->io_lock,flags);

	/* Figure out what we have. */
	event = dev->event_data[0];
	dbg("Event Received: 0x%02x, dtmf_ready = %d",event, dev->ex.bits.dtmf_ready);

	if ((event >= VB_EVENT_DTMF0 && event <= VB_EVENT_DTMF9) 
		|| event == VB_EVENT_DTMFSTAR || event == VB_EVENT_DTMFPND) {

		/* This is DTMF Information.  Convert to char and add to
		   the DTMF buffer if there is room available.  If not,
		   drop it. */
		char dtmf;

		switch (event) {
		case VB_EVENT_DTMFSTAR:
			dbg("*");
			dtmf = 10;
			break;
                case VB_EVENT_DTMFPND:
			dbg("#");
			dtmf = 12;
			break;
		case VB_EVENT_DTMF0:
			dbg("0");
			dtmf = 11;
			break;
		default:
			dtmf = event - VB_EVENT_DTMF0;
			break;
		}

      
		dbg("DTMF decoded: 0x%02x", dtmf & 0xff);

		if (dev->dtmf_buffer_len > VB_DTMF_BUFF_LENGTH) {
			info("No more room for DTMF data.  Discarding");
		} else {
			/* Add this DTMF data to the end of the DTMF buffer */
			dev->ex.bits.dtmf_ready = 1;
			if (dev->ex_sig.bits.dtmf_ready) {
				voipblaster_kill_fasync(dev, SIG_DTMF_READY, POLL_IN);
			}
			dev->dtmf_buffer[dev->dtmf_buffer_in] = dtmf;
			dev->dtmf_buffer_len++;
			dev->dtmf_buffer_in++;

			if (dev->dtmf_buffer_in > VB_DTMF_BUFF_LENGTH)
				dev->dtmf_buffer_in = 0;

		}
	} else {
		/* Determine what, if any flags to set. */
		switch (event) {
		case VB_EVENT_OFFHOOK:
			dbg("VB_EVENT_OFFHOOK");
			dev->hookstate = 1;
			dev->ex.bits.hookstate = 1;
			voipblaster_kill_fasync(dev, SIG_HOOKSTATE, POLL_IN);
			break;
		case VB_EVENT_ONHOOK:
			dbg("VB_EVENT_ONHOOK");
			dev->hookstate = 0;
			dev->ex.bits.hookstate = 1;
			voipblaster_kill_fasync(dev, SIG_HOOKSTATE, POLL_IN);
			break;
		case VB_EVENT_RINGING_OFF:
			dbg("VB_EVENT_RINGING_OFF");
			dev->ringing = 0;
			break;
		case VB_EVENT_RINGING_ON:
			dbg("VB_EVENT_RINGING_ON");
			dev->ringing = 1;
			break;
		default:
			/* This is not DTMF data.  It is some other event */
			if (dev->event_buffer_len > VB_EVENT_BUFF_LENGTH) {
				info("No more room for EVENT data.  Discarding");
			} else {
				/* Add this EVENT data to the end of the EVENT buffer */
				dev->event_buffer_len++;
				dev->event_buffer_in++;
				if (dev->event_buffer_in > VB_EVENT_BUFF_LENGTH)
					dev->event_buffer_in = 0;

				dev->event_buffer[dev->event_buffer_in] = event;
			}
			dbg("got event 0x%02x", event);
			break;
		}
			
	}

#if 0
#if !defined(USB_INT_URB)
	if (dev->udev != NULL) {
		/* Prepare the event urb... */
		FILL_BULK_URB(dev->event_urb, dev->udev,
			      usb_rcvbulkpipe(dev->udev, 0x81),
			      dev->event_data, 1,
			      voipblaster_event_irq, dev);

		dbg("Starting Event URB");
		if (usb_submit_urb(dev->event_urb) != 0) {
			dbg("error with event_urb, status = %d", dev->event_urb->status);
		}
	} else {
		dbg("udev = NULL");
	}
#endif
#endif

	/* Notify driver of new event data... */
	dbg("waking sleeper(s)..");

	wake_up(&dev->event_q);
	wake_up(&dev->poll_q);
	wake_up(&dev->read_q);

	spin_unlock_irqrestore(&dev->io_lock,flags);
}

static void voipblaster_event_poll(struct usb_voipblaster *dev, int timeout)
{
	interruptible_sleep_on_timeout(&dev->event_q, timeout);
}

#if 0
static int voipblaster_status(struct usb_voipblaster *dev)
{
	int retval;

	dbg("%d events in buffer", dev->event_buffer_len);

	if (dev->event_buffer_len > 0) {
		dev->event_buffer_len--;
		dev->event_buffer_out++;
		if (dev->event_buffer_out > VB_EVENT_BUFF_LENGTH) 
			dev->event_buffer_out = 0;
		retval = dev->event_buffer[dev->event_buffer_in];
	} else {
		retval = -1;
	}
	dbg("retval = %d", retval);
	return retval;
}
#endif

static void voipblaster_cmd_irq(urb_t *urb)
{
	struct usb_voipblaster *dev = urb->context;
	unsigned long flags;

	/* The last command was sent to the voipblaster.  If there are
	   any more commands send them off too.  If there aren't anymore
	   then unlink this urb.  This completion handler may also be
	   called when we unlink the URB... */

	dbg("URB Returned, urb->status = %d", urb->status);
	spin_lock_irqsave(&dev->io_lock,flags);

	if (urb->status != 0) {
		if ((urb->status != -ENOENT) && 
		    (urb->status != -ECONNRESET)) {
			dbg(" nonzero status received: %d", urb->status);
                }
		spin_unlock_irqrestore(&dev->io_lock,flags);
		goto exit;
        }                        

	spin_unlock_irqrestore(&dev->io_lock,flags);

	dbg("waking sleeper..");

	wake_up(&dev->cmd_q);

 exit:
	dbg("CMD completed");
}

static int voipblaster_cmd(struct usb_voipblaster *dev, char cmd)
{
	/* Send a command to the voipblaster using a single shot
	   interupt URB.  This command will wait for completion before
	   returning control */
 
	unsigned long flags;
	int urb_err;

	dbg("Queuing command: 0x%02x", cmd);

	/* see if we are already in the middle of a write */
	if (dev->cmd_urb->status == -EINPROGRESS) {
		dbg ("already writing");
		return -1;
	}

	spin_lock_irqsave(&dev->io_lock,flags);
	if (dev->cmd_buffer_len > VB_CMD_BUFF_LENGTH) {
		info(" No room in command buffer for new command");
		spin_unlock_irqrestore(&dev->io_lock,flags);
		return -1;
	}

	/* A fresh URB needs to be sent off */
	dbg("Submitting CMD URB");
	dev->cmd_data[0] = cmd;

	/* Prepare the command urb... */
#if defined(USE_INT_URB)
	FILL_INT_URB(dev->cmd_urb, 
		     dev->udev,
		     usb_sndintpipe(dev->udev,0x01),
		     dev->cmd_data, 
		     1,
		     voipblaster_cmd_irq, 
		     dev,
		     0);
#else
	FILL_BULK_URB(dev->cmd_urb, 
		     dev->udev,
		     usb_sndbulkpipe(dev->udev,0x01),
		     dev->cmd_data, 
		     1,
		     voipblaster_cmd_irq, 
		     dev);
#endif

	urb_err = usb_submit_urb(dev->cmd_urb);
	if ( urb_err != 0) {
		info("error submitting CMD URB: %d, %d",dev->cmd_urb->status, urb_err);
		spin_unlock_irqrestore(&dev->io_lock,flags);
		return dev->cmd_urb->status;
	}

	spin_unlock_irqrestore(&dev->io_lock,flags);

	/* now wait for the IRQ to complete. */

	interruptible_sleep_on(&dev->cmd_q);

	dbg("completed");

	return 0;
}

static void voipblaster_snd_voice_irq(urb_t *urb)
{
	struct usb_voipblaster *dev = urb->context;
	unsigned long flags;

	spin_lock_irqsave(&dev->io_lock,flags);
	dbg("URB Returned, urb->status = %d", urb->status);
 
	/* The current voice block has been processed... */
	dev->write_buffer_len = 0;

	spin_unlock_irqrestore(&dev->io_lock,flags);

	dbg("waking sleeper..");

	wake_up(&dev->write_q);

	dbg("completed");
}

static int voipblaster_snd_voice(struct usb_voipblaster *dev,
	u_char *data, int length)
{

	/* Send voice data to the voipblaster using a single shot
	   interupt URB.  This command will wait for completion before
	   returning control */
 
	unsigned long flags;
	int i;

	dbg("sending %d bytes of voice data.", length);

	/* see if we are already in the middle of a write */
	if (dev->write_urb->status == -EINPROGRESS) {
		dbg ("already writing");
		return -1;
	}

	spin_lock_irqsave(&dev->io_lock,flags);
	if (length > VB_W_BUFF_LENGTH) {
		dbg("No room in write buffer for new voice data");
		spin_unlock_irqrestore(&dev->io_lock,flags);
		return -1;
	}

	/* Add this voice data to the write buffer. */
	for (i=0; i<length; i++) {
		dev->write_buffer[i] = data[i];
	}

	dev->write_buffer_len = length;

	/* A fresh URB needs to be sent off */
	dbg("submitting write URB..");

	/* Prepare the write urb... */
#if defined(USE_INT_URB)
	FILL_INT_URB(dev->write_urb, dev->udev,
		usb_sndintpipe(dev->udev,0x2),
		dev->write_buffer, dev->write_buffer_len,
		voipblaster_snd_voice_irq, dev,
		0);
#else
	FILL_BULK_URB(dev->write_urb, dev->udev,
		usb_sndbulkpipe(dev->udev,0x2),
		dev->write_buffer, dev->write_buffer_len,
		voipblaster_snd_voice_irq, dev);
#endif

	if  (usb_submit_urb(dev->write_urb) != 0) {
		dbg("error submitting WRITE URB: %d",dev->write_urb->status);
		spin_unlock_irqrestore(&dev->io_lock,flags);
		return dev->write_urb->status;
	}

	spin_unlock_irqrestore(&dev->io_lock,flags);

	dbg("sleeping..");

	interruptible_sleep_on(&dev->write_q);

	dbg("done.");

	return 0;
}

static void voipblaster_read_irq(urb_t *urb)
{
	struct usb_voipblaster *dev = urb->context;
	unsigned long flags;

	spin_lock_irqsave(&dev->io_lock,flags);
	dbg("URB Returned, status = %d", urb->status);

	if (urb->status != 0) {
		if ((urb->status != -ENOENT) && 
		    (urb->status != -ECONNRESET)) {
			dbg(" - nonzero status received: %d", urb->status);
                }
		spin_unlock_irqrestore(&dev->io_lock,flags);
		goto exit;
        }                        

	if (!dev->ispresent) {
		dev->ispresent = 1;
		memcpy(dev->dsp_version, dev->read_buffer_urb, 10);
		memcpy(dev->dsp_serial, &dev->read_buffer_urb[10], 10);
	} else {
		memcpy(dev->read_buffer, dev->read_buffer_urb, 20);
		dev->read_buffer_ready = 1;
		dev->read_buffer_in = 20;
	}

	/* A fresh URB needs to be sent off */
	dbg("submitting read URB..");

	/* Prepare the read urb... */
	FILL_BULK_URB(dev->read_urb, dev->udev,
		usb_rcvbulkpipe(dev->udev, 0x82),
		dev->read_buffer_urb, 20,
		voipblaster_read_irq, dev);

	dbg("Starting read URB");
	if (usb_submit_urb(dev->read_urb) != 0) {
		dbg("error with read_urb, status = %d", dev->read_urb->status);
	}

	spin_unlock_irqrestore(&dev->io_lock,flags);

	dbg("waking sleeper..");

	//wake_up(&dev->poll_q);
	wake_up(&dev->read_q);

 exit:
	dbg("CMD completed");
}

/*
 *	voipblaster_read
 */
static ssize_t voipblaster_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
	struct usb_voipblaster *dev;
	int retval = 0;

	dev = (struct usb_voipblaster *)file->private_data;
	
	dbg("minor %d, count = %d, read_buffer_ready = %d, read_wait = %ld", dev->minor, 
	    count, dev->read_buffer_ready, dev->read_wait);

	/* lock this object */
	down (&dev->sem);

	if (dev->flags.inread) {
		up (&dev->sem);
		return -EALREADY;
	}

	dev->flags.inread = 1;

#if 0
	while (dev->read_buffer_ready || (dev->dtmf_state && dev->flags.dtmf_oob)) {
#else
	while (!dev->read_buffer_ready) {
#endif
		++dev->read_wait;
		if (file->f_flags & O_NONBLOCK) {
			dbg("O_NONBLOCK");
			dev->flags.inread = 0;
			up (&dev->sem);
			return -EAGAIN;
		}

		if (!dev->hookstate) {
			dbg("on hook..");
			dev->flags.inread = 0;
			up (&dev->sem);
			return 0;
		}

		interruptible_sleep_on(&dev->read_q);
		if (signal_pending(current)) {
			dbg("signal_pending");
			dev->flags.inread = 0;
			up (&dev->sem);
			return -EINTR;
		}
	}

	dev->read_buffer_ready = 0;
	dev->flags.inread = 0;

	if (copy_to_user(buffer, dev->read_buffer, dev->read_buffer_in)) {
		retval = -EFAULT;
	} else {
		if (count == 24 && dev->read_buffer_in == 20) {
			retval = count;
		} else {
			retval =  dev->read_buffer_in;
			dev->read_buffer_in = 0;
		}
	}

	up (&dev->sem);

	dbg("done, retval = %d", retval);

	return retval;

#if 0
	/* verify that the device wasn't unplugged */
	if (dev->udev == NULL) {
		up (&dev->sem);
		return -ENODEV;
	}
	
	
	/* do an immediate bulk read to get data from the device */
	retval = usb_bulk_msg (dev->udev,
			       usb_rcvbulkpipe (dev->udev, 0x82),
			       dev->read_buffer, 20,
			       &count, 60);

	/* if the read was successful, copy the data to userspace */
	if (!retval) {
		if (copy_to_user (buffer, dev->read_buffer, count))
			retval = -EFAULT;
		else
			retval = count;
	}
#endif
}

/**
 *      skel_write_bulk_callback
 */
static void voipblaster_write_callback (struct urb *urb)
{
        struct usb_voipblaster *dev = (struct usb_voipblaster *)urb->context;

        dbg("minor %d", dev->minor);

        if ((urb->status != -ENOENT) && 
            (urb->status != -ECONNRESET)) {
                dbg("write bulk status received: %d", urb->status);
        }

	wake_up(&dev->write_q);

        return;
}


/*
 *	voipblaster_write
 */
 static u_char G723count[4] = { 24, 20, 4, 1};

static ssize_t voipblaster_write (struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
	struct usb_voipblaster *dev;
	ssize_t bytes_written = 0;
	int retval = 0;

	dev = (struct usb_voipblaster *)file->private_data;

	dbg("minor %d, count = %d", dev->minor, count);

	/* lock this object */
	down (&dev->sem);

	/* verify that the device wasn't unplugged */
	if (dev->udev == NULL) {
		retval = -ENODEV;
		goto exit;
	}

	/* verify that we actually have some data to write */
	if (count < 4) {
		dbg("write request of too few bytes");
		goto exit;
	}

	/* see if we are already in the middle of a write */
	if (dev->write_urb->status == -EINPROGRESS) {
		dbg ("already writing");
		goto exit;
	}
        /* the least significant two bits of the start of the frame
           tell us how big the frame really is */
	bytes_written = G723count[(*(u_char *)buffer)&3];

	/* we can only write as much as 1 urb will hold */
	bytes_written = (bytes_written > VB_W_BUFF_LENGTH) ? 
				VB_W_BUFF_LENGTH : bytes_written;
	
	if (count < bytes_written) {
		dbg("write request of too few bytes %d vs %d",count,bytes_written);
		goto exit;
	}

	/* copy the data from userspace into our urb */
	if (copy_from_user(dev->write_urb->transfer_buffer, buffer, 
			   bytes_written)) {
		retval = -EFAULT;
		goto exit;
	}

#if 0
	usb_skel_debug_data (__FUNCTION__, bytes_written, 
			     dev->write_urb->transfer_buffer);
#endif

	/* set up our urb */
	FILL_BULK_URB(dev->write_urb, dev->udev, 
		      usb_sndbulkpipe(dev->udev, 0x02 ),
		      dev->write_buffer, bytes_written,
		      voipblaster_write_callback, dev);

	/* send the data out the bulk port */
	if (usb_submit_urb(dev->write_urb) != 0) {
		err("failed submitting write urb, error %d",
		    dev->write_urb->status);
	} else { /*return full frame size or max buffer size */
		retval = (count > VB_W_BUFF_LENGTH) ? 
                                VB_W_BUFF_LENGTH : count;
	}

	interruptible_sleep_on(&dev->write_q);
exit:

	/* unlock the device */
	up (&dev->sem);

	return retval;
}

static void voipblaster_ring_start(struct usb_voipblaster *dev)
{
	voipblaster_cmd(dev,VB_CMD_RING_ON);
	voipblaster_event_poll(dev, 10000);
}

static void voipblaster_ring_stop(struct usb_voipblaster *dev)
{
	voipblaster_cmd(dev,VB_CMD_RING_OFF);
	voipblaster_event_poll(dev, 10000);
}

static void voipblaster_record_start(struct usb_voipblaster *dev)
{
	voipblaster_cmd(dev,VB_CMD_VINP_START);
	// voipblaster_event_poll(dev, 10000);
}

static void voipblaster_record_stop(struct usb_voipblaster *dev)
{
	voipblaster_cmd(dev,VB_CMD_VINP_STOP);
	// voipblaster_event_poll(dev, 10000);
}

static void voipblaster_play_start(struct usb_voipblaster *dev)
{
	voipblaster_cmd(dev,VB_CMD_VOUT_START);
	voipblaster_cmd(dev,VB_CMD_0x10);
	voipblaster_cmd(dev,VB_CMD_0x11);
	// voipblaster_event_poll(dev, 10000);
}

static void voipblaster_play_stop(struct usb_voipblaster *dev)
{
	voipblaster_cmd(dev,VB_CMD_VOUT_DONE);
	voipblaster_event_poll(dev, 10000);
}

static unsigned int voipblaster_poll(struct file *file, poll_table * wait)
{
	unsigned int mask = 0;

	struct usb_voipblaster *dev;

	dev = (struct usb_voipblaster *) file->private_data;

	dbg("minor = %d, read_buffer_in = %d, write_buffer_in = %d", dev->minor,
	    dev->read_buffer_in, dev->write_buffer_in);

	poll_wait(file, &(dev->poll_q), wait);
	poll_wait(file, &(dev->read_q), wait);

	dbg("poll wakeup");

	if (dev->read_buffer_in > 0) {
		dbg("read_buffer_in = %d", dev->read_buffer_in);
		mask |= POLLIN | POLLRDNORM;	/* readable */
	}

#if 0
	if (dev->write_buffer_in > 0) {
		mask |= POLLOUT | POLLWRNORM;	/* writable */
	}
#endif
	mask |= POLLOUT | POLLWRNORM;	/* writable */

	if (dev->ex.bytes) {
		dbg("ex.bytes = %d", dev->ex.bytes);
		mask |= POLLPRI;
	}

	dbg("mask = 0x%04x", mask);

	return mask;
}

static int voipblaster_fasync(int fd, struct file *file, int mode)
{
	struct usb_voipblaster *dev;

	dev = (struct usb_voipblaster *) file->private_data;
	dbg("minor = %d", dev->minor);

	return fasync_helper(fd, file, mode, &dev->async_queue);
}

/*
 *	voipblaster_ioctl
 */
static int voipblaster_ioctl(struct inode *inode, struct file *file, 
			     unsigned int cmd, unsigned long arg)
{
	struct usb_voipblaster *dev;
	int retval = -ENOTTY;
	int raise, mant;
#if 0
	int c;
#endif

	dev = (struct usb_voipblaster *)file->private_data;

	/* lock this object */
	down (&dev->sem);

	/* verify that the device wasn't unplugged */
	if (dev->udev == NULL) {
		up (&dev->sem);
		return -ENODEV;
	}

	dbg("dev %d, minor %d, cmd 0x%.4x, arg 0x%.8lx", 
	    dev->udev->devnum, dev->minor, cmd, arg);


	/* fill in your device specific stuff here */
	if (!capable(CAP_SYS_ADMIN)) {
		switch (cmd) {
		case IXJCTL_TESTRAM:
		case IXJCTL_HZ:
			retval = -EPERM;
		}
	}
	switch (cmd) {
	case IXJCTL_TESTRAM:
		dbg("IXJCTL_TESTRAM");
		retval = 0;
		break;
	case IXJCTL_CARDTYPE:
		dbg("IXJCTL_CARDTYPE");
		retval = dev->cardtype;
		break;
	case IXJCTL_SERIAL:
		dbg("IXJCTL_SERIAL");
		retval = dev->serial;
		break;
	case IXJCTL_VERSION:
		dbg("IXJCTL_VERSION");
		if (copy_to_user((char *) arg, VOIPversion, strlen(VOIPversion)))
			retval = -EFAULT;
		break;
	case PHONE_RING_CADENCE:
		dbg("PHONE_RING_CADENCE");
		retval = -EPERM;
		break;
	case IXJCTL_CIDCW:
		dbg("IXJCTL_CIDCW");
		retval = -EPERM;
		break;
        /* Binary compatbility */
        case OLD_PHONE_RING_START:
		dbg("OLD_PHONE_RING_START");
                arg = 0;
                /* Fall through */
 	case PHONE_RING_START:
		dbg("PHONE_RING_START");
		memset(&dev->cid_send, 0, sizeof(PHONE_CID));
		voipblaster_ring_start(dev);
		retval = 0;
		break;
	case PHONE_RING_STOP:
		dbg("PHONE_RING_STOP");
		dev->flags.cringing = 0;
		if (dev->cadence_f[5].enable) {
			dev->cadence_f[5].state = 0;
		}
		voipblaster_ring_stop(dev);
		retval = 0;
		break;
	case PHONE_RING:
		dbg("PHONE_RING, maxrings = %d", dev->maxrings);
		if (dev->maxrings == 0) {
			dbg("setting maxrings = 2");
			dev->maxrings = 2;
		}
		dev->ringcnt = 0;
		retval = 0;
		while (dev->ringcnt < dev->maxrings) {
			voipblaster_ring_start(dev);
			voipblaster_event_poll(dev, 10000);
			dbg("poll returned; ringcnt = %d, hookstate = %d", 
			    dev->ringcnt, dev->hookstate);
			if (dev->hookstate == 1) {
				dbg("phone answered, stop ringing");
				retval = 1;
				break;
			}

			dev->ringcnt++;
		}
	
		dbg("ringcnt = %d, maxrings = %d", dev->ringcnt, dev->maxrings);
		if (!dev->hookstate) {
			voipblaster_ring_stop(dev);
		}
		break;
	case PHONE_EXCEPTION:
		dbg("PHONE_EXCEPTION");
		retval = dev->ex.bytes;
#if 0
		if(dev->ex.bits.flash) {
			dev->flash_end = 0;
			dev->ex.bits.flash = 0;
		}
#endif
		dev->ex.bits.pstn_ring = 0;
		dev->ex.bits.caller_id = 0;
		dev->ex.bits.pstn_wink = 0;
		dev->ex.bits.f0 = 0;
		dev->ex.bits.f1 = 0;
		dev->ex.bits.f2 = 0;
		dev->ex.bits.f3 = 0;
		dev->ex.bits.fc0 = 0;
		dev->ex.bits.fc1 = 0;
		dev->ex.bits.fc2 = 0;
		dev->ex.bits.fc3 = 0;
		dev->ex.bits.reserved = 0;
		break;
	case PHONE_HOOKSTATE:
		dbg("PHONE_HOOKSTATE");
		dev->ex.bits.hookstate = 0;
		retval = dev->hookstate;
		break;
	case IXJCTL_SET_LED:
		dbg("IXJCTL_SET_LED");
		return 0;
		break;
	case PHONE_FRAME:
		dbg("PHONE_FRAME");
		if (arg == 30) {
			retval = arg;
		} else {
			retval = -1;
		}
		break;
	case PHONE_REC_CODEC:
		dbg("PHONE_REC_CODEC");
		switch (arg) {
		case G723_53:
			dbg("G723_53");
			dev->rec_codec = arg;
			retval = 0;
			break;
#if defined(ENABLE_G723_63)
		case G723_63:
			dbg("G723_63");
			dev->rec_codec = arg;
			retval = 0;
			break;
#endif
		default:
			dbg("default; %ld not supported.", arg);
			retval = 1;
			break;
		}
		break;
	case PHONE_VAD:
		dbg("PHONE_VAD");
#if 0
		blaster_vad(dev, arg);
#endif
		break;
	case PHONE_REC_START:
		dbg("PHONE_REC_START");
		voipblaster_record_start(dev);
		retval = 0;
		break;
	case PHONE_REC_STOP:
		dbg("PHONE_REC_STOP");
		voipblaster_record_stop(dev);
		retval = 0;
		break;
	case PHONE_REC_DEPTH:
		dbg("PHONE_REC_DEPTH");
#if 0
		set_rec_depth(dev, arg);
#endif
		retval = 1;
		break;
	case PHONE_REC_VOLUME:
		dbg("PHONE_REC_VOLUME");
#if 0
		if(arg == -1) {
			retval = get_rec_volume(dev);
		}
		else {
			set_rec_volume(dev, arg);
			retval = arg;
		}
#endif
		break;
	case PHONE_REC_VOLUME_LINEAR:
		dbg("PHONE_REC_VOLUME_LINEAR");
#if 0
		if(arg == -1) {
			retval = get_rec_volume_linear(dev);
		}
		else {
			set_rec_volume_linear(dev, arg);
			retval = arg;
		}
#endif
		break;
	case IXJCTL_DTMF_PRESCALE:
		dbg("IXJCTL_DTMF_PRESCALE");
#if 0
		if(arg == -1) {
			retval = get_dtmf_prescale(dev);
		}
		else {
			set_dtmf_prescale(dev, arg);
			retval = arg;
		}
#endif
		break;
	case PHONE_REC_LEVEL:
		dbg("PHONE_REC_LEVEL");
#if 0
		retval = get_rec_level(dev);
#endif
		break;
	case IXJCTL_SC_RXG:
		dbg("IXJCTL_SC_RXG");
#if 0
		retval = VOIPblaster_siadc(dev, arg);
#endif
		break;
	case IXJCTL_SC_TXG:
		dbg("IXJCTL_SC_TXG");
#if 0
		retval = VOIPblaster_sidac(dev, arg);
#endif
		break;
	case IXJCTL_AEC_START:
		dbg("IXJCTL_AEC_START");
		retval = -EPERM;
		break;
	case IXJCTL_AEC_STOP:
		dbg("IXJCTL_AEC_STOP");
		retval = -EPERM;
		break;
	case IXJCTL_AEC_GET_LEVEL:
		dbg("IXJCTL_AEC_GET_LEVEL");
		retval = AEC_OFF;
		break;
	case PHONE_PLAY_CODEC:
		dbg("PHONE_PLAY_CODEC");
#if 0
		retval = set_play_codec(dev, arg);
#endif
		retval = 0;
		break;
	case PHONE_PLAY_START:
		dbg("PHONE_PLAY_START");
		voipblaster_play_start(dev);
		dev->drybuffer = 0;
		retval = 0;
		break;
	case PHONE_PLAY_STOP:
		dbg("PHONE_PLAY_STOP");
		voipblaster_play_stop(dev);
		retval = 0;
		break;
	case PHONE_PLAY_DEPTH:
		dbg("PHONE_PLAY_DEPTH");
#if 0
		set_play_depth(dev, arg);
#endif
		break;
	case PHONE_PLAY_VOLUME:
		dbg("PHONE_PLAY_VOLUME");
#if 0
		if (arg == -1) {
			retval = get_play_volume(dev);
		} else {
			set_play_volume(dev, arg);
			retval = arg;
		}
#endif
		break;
	case PHONE_PLAY_VOLUME_LINEAR:
		dbg("PHONE_PLAY_VOLUME_LINEAR");
#if 0
		if (arg == -1) {
			retval = get_play_volume_linear(dev);
		} else {
			set_play_volume_linear(dev, arg);
			retval = arg;
		}
#endif
		break;
	case PHONE_PLAY_LEVEL:
		dbg("PHONE_PLAY_LEVEL");
#if 0
		retval = get_play_level(dev);
#endif
		retval = 0;
		break;
	case IXJCTL_DSP_TYPE:
		dbg("dsp_serial[5] = 0x%02x, dsp_serial[8] = 0x%02x", 
		    dev->dsp_version[5], dev->dsp_version[8]);
		retval = ((dev->dsp_version[5] << 8) | (dev->dsp_version[8] & 0xff)) & 0xffff;
		break;
	case IXJCTL_DSP_VERSION:
		dbg("dsp_serial[1] = 0x%02x, dsp_serial[2] = 0x%02x", 
		    dev->dsp_version[1], dev->dsp_version[2]);
		retval = ((dev->dsp_version[1] << 8) | (dev->dsp_version[2] & 0xff)) & 0xffff;
		break;
	case IXJCTL_HZ:
		dbg("IXJCTL_HZ");
		hertz = arg;
		retval = 0;
		break;
	case IXJCTL_RATE:
		dbg("IXJCTL_RATE");
		retval = -EPERM;
		break;
	case IXJCTL_DRYBUFFER_READ:
		dbg("IXJCTL_DRYBUFFER_READ");
		put_user(dev->drybuffer, (unsigned long *) arg);
		retval = 0;
		break;
	case IXJCTL_DRYBUFFER_CLEAR:
		dbg("IXJCTL_DRYBUFFER_CLEAR");
		dev->drybuffer = 0;
		retval = 0;
		break;
	case IXJCTL_FRAMES_READ:
		dbg("IXJCTL_FRAMES_READ");
		put_user(dev->framesread, (unsigned long *) arg);
		retval = 0;
		break;
	case IXJCTL_FRAMES_WRITTEN:
		dbg("IXJCTL_FRAMES_WRITTEN");
		put_user(dev->frameswritten, (unsigned long *) arg);
		retval = 0;
		break;
	case IXJCTL_READ_WAIT:
		dbg("IXJCTL_READ_WAIT");
		put_user(dev->read_wait, (unsigned long *) arg);
		retval = 0;
		break;
	case IXJCTL_WRITE_WAIT:
		dbg("IXJCTL_WRITE_WAIT");
		put_user(dev->write_wait, (unsigned long *) arg);
		retval = 0;
		break;
	case PHONE_MAXRINGS:
		dbg("IXJCTL_MAXRINGS");
		dev->maxrings = arg;
		break;
	case PHONE_SET_TONE_ON_TIME:
		dbg("PHONE_SET_TONE_ON_TIME");
		retval = -EPERM;
		break;
	case PHONE_SET_TONE_OFF_TIME:
		dbg("PHONE_SET_TONE_OFF_TIME");
		retval = -EPERM;
		break;
	case PHONE_GET_TONE_ON_TIME:
		dbg("PHONE_GET_TONE_ON_TIME");
		retval = -EPERM;
		break;
	case PHONE_GET_TONE_OFF_TIME:
		dbg("PHONE_GET_TONE_OFF_TIME");
		retval = -EPERM;
		break;
	case PHONE_PLAY_TONE:
		dbg("PHONE_PLAY_TONE");
		retval = -EPERM;
		break;
	case PHONE_GET_TONE_STATE:
		dbg("PHONE_GET_TONE_STATE");
		retval = dev->tonestate;
		break;
	case PHONE_DTMF_READY:
		dbg("PHONE_DTMF_READY, %d", dev->ex.bits.dtmf_ready);
		retval = dev->ex.bits.dtmf_ready;
		break;
	case PHONE_GET_DTMF:
		dbg("PHONE_GET_DTMF");
		dbg("hookstate = %d, len = %d, out = %d, in = %d, retval = %d", 
		    dev->hookstate, dev->dtmf_buffer_len, dev->dtmf_buffer_out,
		    dev->dtmf_buffer_in, dev->dtmf_buffer[dev->dtmf_buffer_out]);
		if (dev->hookstate) {
			if (dev->dtmf_buffer_len > 0) {
				retval = dev->dtmf_buffer[dev->dtmf_buffer_out];
				dev->dtmf_buffer_out++;
				dev->dtmf_buffer_len--;
				if (dev->dtmf_buffer_out == VB_DTMF_BUFF_LENGTH)
					dev->dtmf_buffer_out = 0;
				if (dev->dtmf_buffer_len == 0) {
					dev->ex.bits.dtmf_ready = 0;
					dev->dtmf_buffer_out = 0;
					dev->dtmf_buffer_in = 0;
				}
			}
		}
		break;
	case PHONE_GET_DTMF_ASCII:
		dbg("PHONE_GET_DTMF_ASCII");
		if (dev->hookstate) {
			if (dev->dtmf_buffer_len > 0) {
				switch (dev->dtmf_buffer[dev->dtmf_buffer_out]) {
				case 10:
					dbg("10");
					retval = 42;	/* '*'; */
					break;
				case 11:
					dbg("11");
					retval = 48;	/*'0'; */
					break;
				case 12:
					dbg("12");
					retval = 35;	/*'#'; */
					break;
				case 28:
					dbg("28");
					retval = 65;	/*'A'; */
					break;
				case 29:
					dbg("29");
					retval = 66;	/*'B'; */
					break;
				case 30:
					dbg("30");
					retval = 67;	/*'C'; */
					break;
				case 31:
					dbg("31");
					retval = 68;	/*'D'; */
					break;
				default:
					dbg("default");
					retval = 48 + dev->dtmf_buffer[dev->dtmf_buffer_out];
					break;
				}
				dev->dtmf_buffer_out++;
				dev->dtmf_buffer_len--;
				if (dev->dtmf_buffer_out == VB_DTMF_BUFF_LENGTH)
					dev->dtmf_buffer_out = 0;
				if (dev->dtmf_buffer_len == 0) {
					dev->ex.bits.dtmf_ready = 0;
					dev->dtmf_buffer_out = 0;
					dev->dtmf_buffer_in = 0;
				}
			}
		}
		break;
	case PHONE_DTMF_OOB:
		dbg("PHONE_DTMF_OOB");
		dev->flags.dtmf_oob = arg;
		break;
	case PHONE_DIALTONE:
		dbg("PHONE_DIALTONE");
#if 0
		blaster_dialtone(dev);
#endif
		break;
	case PHONE_BUSY:
		dbg("PHONE_BUSY");
#if 0
		blaster_busytone(dev);
#endif
		break;
	case PHONE_RINGBACK:
		dbg("PHONE_RINGBACK");
#if 0
		blaster_ringback(dev);
#endif
		break;
	case PHONE_WINK:
		dbg("PHONE_WINK");
		retval = -EPERM;
		break;
	case PHONE_CPT_STOP:
		dbg("PHONE_CPT_STOP");
#if 0
		blaster_cpt_stop(dev);
		retval = -EPERM;
#endif
		retval = 0;
		break;
        case PHONE_QUERY_CODEC:
        {
                struct phone_codec_data pd;
                int val;
                int proto_size[] = {
                        -1,
                        12, 10, 16, 9, 8, 48, 5,
                        40, 40, 80, 40, 40, 6
                };

		dbg("PHONE_QUERY_CODEC");

                if (copy_from_user(&pd, (void *)arg, sizeof(pd))) {
                        retval = -EFAULT;
			break;
		}
#if defined(ENABLE_G723_63)
                if (pd.type <1 || pd.type>2) {
                        retval = -EPROTONOSUPPORT;
			break;
		}
#else
		if (pd.type != 2) {
			retval = -EPROTONOSUPPORT;
			break;
		}
#endif
		val=proto_size[pd.type];
                pd.buf_min=pd.buf_max=pd.buf_opt=val;
		dbg("type = 0x%02x", pd.type);

                if (copy_to_user((void *)arg, &pd, sizeof(pd)))
                        retval = -EFAULT;
		retval = 0;
        	break;
        }
	case IXJCTL_DSP_IDLE:
		dbg("IXJCTL_DSP_IDLE");
		retval = 0;
		break;
	case IXJCTL_MIXER:
		dbg("IXJCTL_MIXER");
		retval = -EPERM;
		break;
	case IXJCTL_DAA_COEFF_SET:
		dbg("IXJCTL_DAA_COEFF_SET");
		switch (arg) {
		case DAA_US:
			dbg("DAA_US");
			dev->daa_country = DAA_US;
			retval = 1;
			break;
		case DAA_UK:
			dbg("DAA_UK");
			retval = 0;
			break;
		case DAA_FRANCE:
			dbg("DAA_FRANCE");
			retval = 0;
			break;
		case DAA_GERMANY:
			dbg("DAA_GERMANY");
			retval = 0;
			break;
		case DAA_AUSTRALIA:
			dbg("DAA_AUSTRALIA");
			retval = 0;
			break;
		case DAA_JAPAN:
			dbg("DAA_JAPAN");
			retval = 0;
			break;
		default:
			retval = 0;
			break;
		}
		break;
	case IXJCTL_DAA_AGAIN:
		dbg("IXJCTL_DAA_AGAIN");
		retval = -EPERM;
		break;
	case IXJCTL_PSTN_LINETEST:
		dbg("IXJCTL_PSTN_LINETEST");
		retval = -EPERM;
		break;
	case IXJCTL_VMWI:
		dbg("IXJCTL_VMWI");
		retval = -EPERM;
		break;
	case IXJCTL_CID:
		dbg("IXJCTL_CID");
		if (copy_to_user((char *) arg, &dev->cid, sizeof(PHONE_CID))) 
			retval = -EFAULT;
		dev->ex.bits.caller_id = 0;
		retval = -EPERM;
		break;
	case IXJCTL_WINK_DURATION:
		dbg("IXJCTL_WINK_DURATION");
		dev->winktime = arg;
		retval = -EPERM;
		break;
	case IXJCTL_PORT:
		dbg("IXJCTL_PORT");
		/* VOIP Blaster have only one port to be used (Telephone) */
		if ((arg == PORT_POTS) || (arg == PORT_HANDSET)) {
			dev->port = PORT_POTS;
			retval = 0;
		} else if (arg == PORT_QUERY) {
			retval = dev->port;
		} else {
			retval = -EPERM;
		}
		break;
	case IXJCTL_POTS_PSTN:
		dbg("IXJCTL_POTS_PSTN");
#if 0
		retval = voipblaster_set_pots(dev, arg);
#endif
		break;
	case PHONE_CAPABILITIES:
		dbg("PHONE_CAPABILITIES");
		add_caps(dev);
		retval = dev->caps;
		break;
	case PHONE_CAPABILITIES_LIST:
		dbg("PHONE_CAPABILITIES_LIST");
		add_caps(dev);
		if (copy_to_user((char *) arg, dev->caplist, 
				 sizeof(struct phone_capability) * dev->caps)) 
			retval = -EFAULT;
		retval = 0;
		break;
	case PHONE_CAPABILITIES_CHECK:
		dbg("PHONE_CAPABILITIES_CHECK");
		{
			struct phone_capability cap;
			if (copy_from_user(&cap, (char *) arg, sizeof(cap))) 
				retval = -EFAULT;
			else {
				add_caps(dev);
				retval = capabilities_check(dev, &cap);
			}
		}
		break;
	case PHONE_PSTN_SET_STATE:
		dbg("PHONE_PSTN_SET_STATE");
#if 0
		daa_set_mode(dev, arg);
#endif
		retval = 0;
		break;
	case PHONE_PSTN_GET_STATE:
		dbg("PHONE_PSTN_GET_STATE");
#if 0
		retval = dev->daa_mode;
#endif
		dev->ex.bits.pstn_ring = 0;
		retval = 0;
		break;
	case IXJCTL_SET_FILTER:
		dbg("IXJCTL_SET_FILTER");
		retval = -EPERM;
		break;
	case IXJCTL_SET_FILTER_RAW:
		dbg("IXJCTL_SET_FILTER_RAW");
		retval = -EPERM;
		break;
	case IXJCTL_GET_FILTER_HIST:
		dbg("IXJCTL_GET_FILTER_HIST");
		retval = -EINVAL;
		break;
	case IXJCTL_INIT_TONE:
		dbg("IXJCTL_INIT_TONE");
		retval = -EPERM;
		break;
	case IXJCTL_TONE_CADENCE:
		dbg("IXJCTL_TONE_CADENCE");
		retval = -EPERM;
		break;
	case IXJCTL_FILTER_CADENCE:
		dbg("IXJCTL_FILTER_CADENCE");
		retval = -EPERM;
		break;
	case IXJCTL_SIGCTL:
		dbg("IXJCTL_SIGCTL");
		if (copy_from_user(&dev->sigdef, (char *)arg, sizeof(IXJ_SIGDEF)))
			retval = -EFAULT;
		dbg("sigdef.event = %d, signal = %d", dev->sigdef.event, dev->sigdef.signal);
		dev->signals[dev->sigdef.event] = dev->sigdef.signal;
		if(dev->sigdef.event < 33) {
			raise = 1;
			for(mant = 0; mant < dev->sigdef.event; mant++){
				raise *= 2;
			}
			if(dev->sigdef.signal)
				dev->ex_sig.bytes |= raise; 
			else
				dev->ex_sig.bytes &= (raise^0xffff); 
		}
		retval = 0;;
		break;
	case IXJCTL_INTERCOM_STOP:
		dbg("IXJCTL_INTERCOM_STOP");
#if 0
		if(arg < 0 || arg >= IXJMAX)
			return -EINVAL;
		dev->intercom = -1;
		blaster_record_stop(dev);
		blaster_play_stop(dev);
		idle(dev);
		get_ixj(arg)->intercom = -1;
		blaster_record_stop(get_ixj(arg));
		blaster_play_stop(get_ixj(arg));
		idle(get_ixj(arg));
#endif
		break;
	case IXJCTL_INTERCOM_START:
		dbg("IXJCTL_INTERCOM_START");
#if 0
		if(arg < 0 || arg >= IXJMAX)
			return -EINVAL;
		dev->intercom = arg;
		blaster_record_start(dev);
		blaster_play_start(dev);
		get_ixj(arg)->intercom = board;
		blaster_play_start(get_ixj(arg));
		blaster_record_start(get_ixj(arg));
#endif
		break;
	}

	dbg("phone%d ioctl end, cmd: 0x%x, arg: 0x%lx, retval: 0x%.8x", 
	    dev->minor, cmd, arg, retval);
	
	/* unlock the device */
	up (&dev->sem);
	
	/* return that we did not understand this ioctl call */
	return retval;
}

static inline void voipblaster_delete (struct usb_voipblaster *dev)
{
	int status;

	dbg("deleting USB device %d", dev->udev->devnum);

        voipblaster_table[dev->board] = NULL;

	/* Unlinking can be performed even if URB is not active */
	if ((status = usb_unlink_urb(dev->cmd_urb)))
		dbg("trouble unlinking Command URB on close: %d", status);
	
	if ((status = usb_unlink_urb(dev->event_urb)))
		dbg("trouble unlinking Event URB on close: %d", status);

	/* Unlinking can be performed even if URB is not active */
	status = usb_unlink_urb(dev->write_urb);
	if (status > 0)
		dbg("trouble unlinking write URB on close; status = %d", status);
	
	/* Unlinking can be performed even if URB is not active */
	status = usb_unlink_urb(dev->read_urb);
	if (status != 0)
		dbg("trouble unlinking read URB on close; status = %d", status);
	
	if (dev->cq.buffer != NULL) {
		dbg("freeing cq buffer");
		kfree (dev->cq.buffer);
	}
	if (dev->rq.buffer != NULL) {
		dbg("freeing rq buffer");
		kfree (dev->rq.buffer);
	}
	if (dev->wq.buffer != NULL) {
		dbg("freeing wq buffer");
		kfree (dev->wq.buffer);
	}
	if (dev->eq.buffer != NULL) {
		dbg("freeing eq buffer");
		kfree (dev->eq.buffer);
	}

	if (dev->event_urb != NULL) {
		dbg("freeing event_urb");
		usb_free_urb(dev->event_urb);
	}
	if (dev->cmd_urb != NULL) {
		dbg("freeing cmd_urb");
		usb_free_urb(dev->cmd_urb);
	}
	if (dev->read_urb != NULL) {
		dbg("freeing read_urb");
		usb_free_urb(dev->read_urb);
	}
	if (dev->write_urb != NULL) {
		dbg("freeing write_urb");
		usb_free_urb(dev->write_urb);
	}

	kfree (dev);
}

static inline void voipblaster_flush (struct usb_voipblaster *dev)
{
	
	dbg("flushing USB device %d", dev->udev->devnum);
	dev->dtmf_buffer_in = dev->dtmf_buffer_out = dev->dtmf_buffer_len = 0;
	dev->ex.bits.dtmf_ready = 0;
	dev->event_buffer_in = dev->event_buffer_out = dev->event_buffer_len = 0;
	dev->read_buffer_in = 0;
	
}

static int voipblaster_release(struct inode *inode, struct file *file)
{
	struct usb_voipblaster *dev;
	int retval = 0;

	dev = (struct usb_voipblaster *)file->private_data;


	if (dev == NULL) {
		dbg ("object is NULL");
		return -ENODEV;
	}

	dbg("VOIP blaster device %d, minor %d,  (USB dev %d) closed", dev->board, 
	    dev->minor, dev->udev->devnum);

	/* lock our minor table */
	down (&voipblaster_table_mutex);

	/* lock our device */
	down (&dev->sem);

	if (dev->open_count <= 0) {
		dbg ("minordevice not opened");
		retval = -ENODEV;
		goto exit_not_opened;
	}

#if 0
	voipblaster_fasync(-1, file, 0);
#endif

	if (dev->udev == NULL) {
		/* the device was unplugged before the file was released */
		up (&dev->sem);
		voipblaster_delete(dev);
		// MOD_DEC_USE_COUNT;
		up (&voipblaster_table_mutex);
		return 0;
	}

	/* decrement our usage count for the device */
	--dev->open_count;

	// MOD_DEC_USE_COUNT;

	dbg("VoIP Blaster closed");
	

exit_not_opened:
	up (&dev->sem);
	up (&voipblaster_table_mutex);

	return retval;
}

int voipblaster_open(struct phone_device *phone, struct file *file)
{
	struct usb_voipblaster *dev;
	int retval = 0;

	/* increment our usage count for the module */
	// MOD_INC_USE_COUNT;

	/* lock our minor table and get our local data for this minor */
	down (&voipblaster_table_mutex);

	dev = voipblaster_table[phone->board];

	if (dev == NULL) {
		err("No device passed found!");
		up (&voipblaster_table_mutex);
		// MOD_DEC_USE_COUNT;
		return -ENODEV;
	}

	dbg("VOIP Blaster device %d (USB device %d) open...", phone->board, dev->udev->devnum);

	/* unlock the minor table */
	up (&voipblaster_table_mutex);

	/* lock this device */
	down (&dev->sem);

	/* increment our usage count for the driver */
	++dev->open_count;

	/* save our object in the file's private structure */
	file->private_data = dev;


	voipblaster_flush(dev);

	dbg("dtmf = %d", dev->ex.bits.dtmf_ready);

#if 0
	/* Send off an interupt urb to retrieve voice packets from the hardware */
	dbg("Starting read URB");
	if (usb_submit_urb(&dev->read_urb))
		return -EIO;
#endif

	/* unlock this device */
	up (&dev->sem);

	return retval;

}

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
static void *voipblaster_probe(struct usb_device *udev, unsigned int ifnum
			       ,const struct usb_device_id *id)
#else
static void *voipblaster_probe(struct usb_device *udev, unsigned int ifnum)

#endif
{
	struct usb_voipblaster *dev;

	int board;
	int i;
	int status;
	
	dbg(__FUNCTION__);

	/* allocate memory for our device state and intialize it */
	dev = kmalloc (sizeof(struct usb_voipblaster), GFP_KERNEL);
	if (dev == NULL) {
		err ("Out of memory");
		goto error;
	}

	down (&voipblaster_table_mutex);

	for (board = 0; board < VOIP_MAX; ++board) {
		if (voipblaster_table[board] == NULL)
			break;
	}

	if (board >= VOIP_MAX) {
		info ("Too many devices plugged in, can not handle this device.");
		goto error;
	}

	dbg("allocated board %d", board);

	voipblaster_table[board] = dev;

	up (&voipblaster_table_mutex);

	memset(dev, 0, sizeof(struct usb_voipblaster));

	spin_lock_init(&dev->io_lock);

	init_MUTEX (&dev->sem);

	if (udev != NULL) {
		dbg("udev->devnum = %d", udev->devnum);
	} else {
		dbg("udev is null!");
	}

	dev->udev = udev;
	dev->interface = &udev->actconfig->interface[ifnum];

	dev->board = board;
	memcpy(dev->dsp_serial, "0", 1);

	dbg("USB VoIP Blaster device %d", dev->udev->devnum);

	dev->wq.buffer = kmalloc(256, GFP_KERNEL);

	if (dev->wq.buffer == NULL) {
		err("no memory for wq.buffer");
	}

	dev->wq.size = 256;
	dev->rq.buffer = kmalloc(80, GFP_KERNEL);

	if (dev->rq.buffer == NULL) {
		err("no memory for rq.buffer");
	}

	dev->rq.size = 80;

	init_waitqueue_head(&dev->cmd_q);
	init_waitqueue_head(&dev->write_q);
	init_waitqueue_head(&dev->read_q);
	init_waitqueue_head(&dev->poll_q);
	init_waitqueue_head(&dev->event_q);

	dev->event_urb = usb_alloc_urb(0);
	dev->cmd_urb = usb_alloc_urb(0);
	dev->write_urb = usb_alloc_urb(0);
	dev->read_urb = usb_alloc_urb(0);

	/* Prepare the event urb... */
	FILL_INT_URB(dev->event_urb, udev,
		usb_rcvintpipe( udev, 0x81),
		dev->event_data, 1,
		voipblaster_event_irq, dev,
		30);

	/* Prepare the read urb... */
	FILL_BULK_URB(dev->read_urb, dev->udev,
		usb_rcvbulkpipe(dev->udev,0x82),
		dev->read_buffer_urb, 20,
		voipblaster_read_irq, dev);

	/* Send off an interupt urb to retrieve command events from the hardware */
	dbg("Starting Event URB");
	if ((status = usb_submit_urb(dev->event_urb)) != 0) {
		dbg("error with event_urb, status = %d, %d", dev->event_urb->status, status);
	}

	dbg("Starting Read URB");
	if (usb_submit_urb(dev->read_urb) != 0) {
		dbg("error with read_urb, status = %d", dev->read_urb->status);
	}

	voipblaster_cmd(dev, VB_CMD_SETUP_MODE);
	voipblaster_flush(dev);

	voipblaster_snd_voice(dev, init_1, sizeof(init_1));
	voipblaster_flush(dev);

	voipblaster_snd_voice(dev, init_2, sizeof(init_2));
	voipblaster_flush(dev);

	voipblaster_event_poll(dev, 500);

	dbg ("init done.");
	dev->ringing = 1;

	for (i = 0; i < 2; i++) {
		int j = 10;

		voipblaster_cmd(dev, VB_CMD_VOL_3);
		voipblaster_cmd(dev, VB_CMD_RING_OFF);
		voipblaster_cmd(dev, VB_CMD_PHONE_ON);

		while (1) {
			j--;
			dbg("polling; j = %d, dev->ringing = %d", j, dev->ringing);
			voipblaster_event_poll(dev, 100);
			if (!dev->ringing) {
				break;
			}
			if (j == 0) {
				dbg ("exiting while loop..");
				break;
			}
		}
		if (!dev->ringing) {
			break;
		}
	}

	voipblaster_cmd(dev, VB_CMD_VOL_3);
	voipblaster_cmd(dev, VB_CMD_MUTE_OFF);

	dev->hookstate = 0;
	dev->cardtype = CREATIVE_VOIPBLASTER;
	dev->aec_level = AEC_OFF;
	dev->play_codec = G723_53;
	dev->rec_codec = G723_53;
	dev->port = PORT_POTS;

	snprintf(dev->serialnum, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
		 dev->dsp_serial[0] & 0xff, dev->dsp_serial[1] & 0xff,
		 dev->dsp_serial[2] & 0xff, dev->dsp_serial[3] & 0xff,
		 dev->dsp_serial[4] & 0xff, dev->dsp_serial[5] & 0xff);
		 
	snprintf(dev->revision, 30, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
		 dev->dsp_version[0] & 0xff, dev->dsp_version[1] & 0xff,
		 dev->dsp_version[2] & 0xff, dev->dsp_version[3] & 0xff,
		 dev->dsp_version[4] & 0xff, dev->dsp_version[5] & 0xff,
		 dev->dsp_version[6] & 0xff, dev->dsp_version[7] & 0xff,
		 dev->dsp_version[8] & 0xff, dev->dsp_version[9] & 0xff);
		 
	dbg("serial: %s", dev->serialnum);
	dbg("revision: %s", dev->revision);

	voipblaster_register_device(dev);

	dev->phone.f_op = &voipblaster_fops;
	dev->phone.open = voipblaster_open;
	dev->phone.board = board;
	phone_register_device(&dev->phone, PHONE_UNIT_ANY);
	dbg("USB VoIP Blaster registered with Linux Telephony sub system");

	goto exit;

error:
	voipblaster_delete (dev);
	dev = NULL;

exit:
	return dev;
}

static void voipblaster_disconnect(struct usb_device *udev, void *ptr)
{
	struct usb_voipblaster *dev = (struct usb_voipblaster *) ptr;

	phone_unregister_device(&dev->phone);
	dbg("USB VoIP Blaster deregistered with Linux Telephony sub system");

	voipblaster_deregister_device(dev);
	voipblaster_delete(dev);

	dbg("USB VoIP Blaster disconnected");
}

static struct usb_driver voipblaster_driver = {
	name:		"voipblaster",
	probe:		voipblaster_probe,
	disconnect:	voipblaster_disconnect,
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
	id_table:	voipblaster_ids,
#endif
	fops:		&voipblaster_fops,
};

int __init voipblaster_init(void)
{
        int result;

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
	voipblaster_proc_init();
	snprintf(VOIPversion, 32, "%d.%d.%d", VOIP_VER_MAJOR, VOIP_VER_MINOR, VOIP_BLD_VER);
#else 
	/* 2.2.19 does not like create_proc_read_entry and snprintf */
	sprintf(VOIPversion, "%d.%d.%d", VOIP_VER_MAJOR, VOIP_VER_MINOR, VOIP_BLD_VER);
#endif
	result = usb_register(&voipblaster_driver);

	if (result < 0) {
		err("usb_register failed for the "__FILE__" driver. Error number %d",
		    result);
		return -1;
	}

	info(DRIVER_DESC " v%s", VOIPversion);
	info("using %s", ixjuser_h_rcsid);

	return 0;
}

void __exit voipblaster_exit(void)
{
	voipblaster_proc_destroy();
	usb_deregister(&voipblaster_driver);
}

module_init(voipblaster_init);
module_exit(voipblaster_exit);


MODULE_AUTHOR("Michael Bosland, <mike@ring.org>, Thomas Davis, <tadavis@veriomail.com>");
MODULE_DESCRIPTION("USB Driver for Creative Labs VoIP Blaster");

#ifdef MODULE_LICENSE
  MODULE_LICENSE("GPL");
#endif

/*
 * Local variables:
 *  c-indent-level: 8
 *  c-basic-offset: 8
 *  tab-width: 8
 * End:
 */