[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bluetooth-dev] [patch] USB Bluetooth driver updated




Hi,

Here is a patch against 2.4.0-test6-pre1 that updates the USB Bluetooth
driver. I worked a bunch with Mark Douglas Corner on getting the driver to
work with real hardware and the Axis Linux Bluetooth stack. The driver now
works properly with the Axis stack, and possibly with others (only the
Axis stack was tested, as it is under the GPL).

I would like to thank Mark for all of the work he has done on this driver.

thanks,

greg k-h

-- 
greg@xxxxxxx.com
diff -Naur -X /home/greg/linux/dontdiff linux-2.4.0-test6-pre1/drivers/usb/bluetooth.c linux-2.4.0-test6-pre1-greg/drivers/usb/bluetooth.c
--- linux-2.4.0-test6-pre1/drivers/usb/bluetooth.c	Fri Jul 14 17:03:44 2000
+++ linux-2.4.0-test6-pre1-greg/drivers/usb/bluetooth.c	Thu Aug  3 13:50:50 2000
@@ -1,11 +1,24 @@
 /*
- * bluetooth.c   Version 0.2
+ * bluetooth.c   Version 0.3
  *
  * Copyright (c) 2000 Greg Kroah-Hartman	<greg@xxxxxxx.com>
+ * Copyright (c) 2000 Mark Douglas Corner	<mcorner@xxxxxxx.edu>
  *
  * USB Bluetooth driver, based on the Bluetooth Spec version 1.0B
  *
  *
+ * (08/03/2000) Version 0.3 gkh mdc
+ *	Merged in Mark's changes to make the driver play nice with the Axis
+ *	stack.
+ *	Made the write bulk use an urb pool to enable larger transfers with
+ *	fewer calls to the driver.
+ *	Fixed off by one bug in acl pkt receive
+ *	Made packet counters specific to each bluetooth device 
+ *	Added checks for zero length callbacks
+ *	Added buffers for int and bulk packets.  Had to do this otherwise 
+ *	packet types could intermingle.
+ *	Made a control urb pool for the control messages.
+ *
  * (07/11/2000) Version 0.2 gkh
  *	Fixed a small bug found by Nils Faerber in the usb_bluetooth_probe 
  *	function.
@@ -49,17 +62,16 @@
 #include <linux/tty_flip.h>
 #include <linux/tty.h>
 #include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/list.h>
-#include <linux/smp_lock.h>
 
 #define DEBUG
 #include <linux/usb.h>
 
 /* Module information */
-MODULE_AUTHOR("Greg Kroah-Hartman, greg@xxxxxxx.com/linux-usb/">http://www.kroah.com/linux-usb/");
+MODULE_AUTHOR("Greg Kroah-Hartman, Mark Douglas Corner");
 MODULE_DESCRIPTION("USB Bluetooth driver");
 
+/* define this if you have hardware that is not good */
+/*#define	BTBUGGYHARDWARE */
 
 /* Class, SubClass, and Protocol codes that describe a Bluetooth device */
 #define WIRELESS_CLASS_CODE			0xe0
@@ -72,19 +84,47 @@
 
 #define USB_BLUETOOTH_MAGIC	0x6d02	/* magic number for bluetooth struct */
 
+#define BLUETOOTH_CONTROL_REQUEST_TYPE	0x20
+
+/* Bluetooth packet types */
+#define CMD_PKT			0x01
+#define ACL_PKT			0x02
+#define SCO_PKT			0x03
+#define EVENT_PKT		0x04
+#define ERROR_PKT		0x05
+#define NEG_PKT			0x06
+
+/* Message sizes */
+#define MAX_EVENT_SIZE		0xFF
+#define EVENT_HDR_SIZE		3	/* 2 for the header + 1 for the type indicator */
+#define EVENT_BUFFER_SIZE	(MAX_EVENT_SIZE + EVENT_HDR_SIZE)
+
+#define MAX_ACL_SIZE		0xFFFF
+#define ACL_HDR_SIZE		5	/* 4 for the header + 1 for the type indicator */
+#define ACL_BUFFER_SIZE		(MAX_ACL_SIZE + ACL_HDR_SIZE)
+
 /* parity check flag */
 #define RELEVANT_IFLAG(iflag)	(iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
 
+#define CHAR2INT16(c1,c0)	(((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff))
+#define MIN(a,b)		(((a)<(b))?(a):(b))
 
+#define NUM_BULK_URBS		24
+#define NUM_CONTROL_URBS	16
 
 struct usb_bluetooth {
 	int			magic;
 	struct usb_device *	dev;
 	struct tty_driver *	tty_driver;	/* the tty_driver for this device */
 	struct tty_struct *	tty;		/* the coresponding tty for this port */
-	
+
 	unsigned char		minor;		/* the starting minor number for this device */
 	char			active;		/* someone has this device open */
+	int			throttle;	/* throttled by tty layer */
+	
+	__u8			control_out_bInterfaceNum;
+	struct urb *		control_urb_pool[NUM_CONTROL_URBS];
+	devrequest		dr[NUM_CONTROL_URBS];
 
 	unsigned char *		interrupt_in_buffer;
 	struct urb *		interrupt_in_urb;
@@ -92,17 +132,21 @@
 	unsigned char *		bulk_in_buffer;
 	struct urb *		read_urb;
 
-	unsigned char *		bulk_out_buffer;
 	int			bulk_out_size;
-	struct urb *		write_urb;
+	struct urb *		write_urb_pool[NUM_BULK_URBS];
+	__u8			bulk_out_endpointAddress;
 
 	wait_queue_head_t	write_wait;
 
 	struct tq_struct	tqueue;		/* task queue for line discipline waking up */
+	
+	unsigned int		int_packet_pos;
+	unsigned char		int_buffer[EVENT_BUFFER_SIZE];
+	unsigned int		bulk_packet_pos;
+	unsigned char		bulk_buffer[ACL_BUFFER_SIZE];	/* 64k preallocated, fix? */
 };
 
 
-
 /* local function prototypes */
 static int  bluetooth_open		(struct tty_struct *tty, struct file *filp);
 static void bluetooth_close		(struct tty_struct *tty, struct file *filp);
@@ -114,9 +158,15 @@
 static int  bluetooth_ioctl		(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
 static void bluetooth_set_termios	(struct tty_struct *tty, struct termios *old);
 
+static void bluetooth_int_callback		(struct urb *urb);
+static void bluetooth_ctrl_callback		(struct urb *urb);
+static void bluetooth_read_bulk_callback	(struct urb *urb);
+static void bluetooth_write_bulk_callback	(struct urb *urb);
+
 static void * usb_bluetooth_probe	(struct usb_device *dev, unsigned int ifnum);
 static void usb_bluetooth_disconnect	(struct usb_device *dev, void *ptr);
 
+
 static struct usb_driver usb_bluetooth_driver = {
 	name:		"bluetooth",
 	probe:		usb_bluetooth_probe,
@@ -131,6 +181,7 @@
 static struct usb_bluetooth	*bluetooth_table[BLUETOOTH_TTY_MINORS] = {NULL, };
 
 
+
 static inline int bluetooth_paranoia_check (struct usb_bluetooth *bluetooth, const char *function)
 {
 	if (!bluetooth) {
@@ -141,17 +192,17 @@
 		dbg("%s - bad magic number for bluetooth", function);
 		return -1;
 	}
-	
+
 	return 0;
 }
 
 
-static inline struct usb_bluetooth* get_usb_bluetooth (struct usb_bluetooth *bluetooth, const char *function) 
-{ 
+static inline struct usb_bluetooth* get_usb_bluetooth (struct usb_bluetooth *bluetooth, const char *function)
+{
 	if (!bluetooth || 
-		bluetooth_paranoia_check (bluetooth, function)) {
+	    bluetooth_paranoia_check (bluetooth, function)) { 
 		/* then say that we dont have a valid usb_bluetooth thing, which will
-		 * end up genrating -ENODEV return values */ 
+		 * end up genrating -ENODEV return values */
 		return NULL;
 	}
 
@@ -165,6 +216,54 @@
 }
 
 
+static int bluetooth_ctrl_msg (struct usb_bluetooth *bluetooth, int request, int value, void *buf, int len)
+{
+	struct urb *urb = NULL;
+	devrequest *dr = NULL;
+	int i;
+	int status;
+
+	dbg (__FUNCTION__);
+
+	/* try to find a free urb in our list */
+	for (i = 0; i < NUM_CONTROL_URBS; ++i) {
+		if (bluetooth->control_urb_pool[i]->status != -EINPROGRESS) {
+			urb = bluetooth->control_urb_pool[i];
+			dr = &bluetooth->dr[i];
+			break;
+		}
+	}
+	if (urb == NULL) {
+		dbg (__FUNCTION__ " - no free urbs");
+		return -ENOMEM;
+	}
+
+	/* free up the last buffer that this urb used */
+	if (urb->transfer_buffer != NULL) {
+		kfree(urb->transfer_buffer);
+		urb->transfer_buffer = NULL;
+	}
+
+	dr->requesttype = BLUETOOTH_CONTROL_REQUEST_TYPE;
+	dr->request = request;
+	dr->value = cpu_to_le16p(&value);
+	dr->index = cpu_to_le16p(&bluetooth->control_out_bInterfaceNum);
+	dr->length = cpu_to_le16p(&len);
+	
+	FILL_CONTROL_URB (urb, bluetooth->dev, usb_sndctrlpipe(bluetooth->dev, 0),
+			  (unsigned char*)dr, buf, len, bluetooth_ctrl_callback, 0);
+
+	/* send it down the pipe */
+	status = usb_submit_urb(urb);
+	if (status)
+		dbg(__FUNCTION__ " - usb_submit_urb(control) failed with status = %d", status);
+	
+	return 0;
+}
+
+
+
+
 
 /*****************************************************************************
  * Driver tty interface functions
@@ -172,7 +271,8 @@
 static int bluetooth_open (struct tty_struct *tty, struct file * filp)
 {
 	struct usb_bluetooth *bluetooth;
-	
+	int result;
+
 	dbg(__FUNCTION__);
 
 	/* initialize the pointer incase something fails */
@@ -189,16 +289,26 @@
 		dbg (__FUNCTION__ " - device already open");
 		return -EINVAL;
 	}
-	
+
 	/* set up our structure making the tty driver remember our object, and us it */
 	tty->driver_data = bluetooth;
 	bluetooth->tty = tty;
-	 
+
 	bluetooth->active = 1;
- 
-	/*Start reading from the device*/
-	if (usb_submit_urb(bluetooth->read_urb))
-		dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed");
+
+	/* Reset the packet position counters */
+	bluetooth->int_packet_pos = 0;
+	bluetooth->bulk_packet_pos = 0;
+
+#ifndef BTBUGGYHARDWARE
+	/* Start reading from the device */
+	result = usb_submit_urb(bluetooth->read_urb);
+	if (result)
+		dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed with status %d", result);
+#endif
+	result = usb_submit_urb(bluetooth->interrupt_in_urb);
+	if (result)
+		dbg(__FUNCTION__ " - usb_submit_urb(interrupt in) failed with status %d", result);
 
 	return 0;
 }
@@ -207,20 +317,22 @@
 static void bluetooth_close (struct tty_struct *tty, struct file * filp)
 {
 	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+	int i;
 
 	if (!bluetooth) {
 		return;
 	}
 
 	dbg(__FUNCTION__);
-	
+
 	if (!bluetooth->active) {
 		dbg (__FUNCTION__ " - device not opened");
 		return;
 	}
 
-	/* shutdown any bulk reads that might be going on */
-	usb_unlink_urb (bluetooth->write_urb);
+	/* shutdown any bulk reads and writes that might be going on */
+	for (i = 0; i < NUM_BULK_URBS; ++i)
+		usb_unlink_urb (bluetooth->write_urb_pool[i]);
 	usb_unlink_urb (bluetooth->read_urb);
 
 	bluetooth->active = 0;
@@ -230,55 +342,137 @@
 static int bluetooth_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
 {
 	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
-	
+	struct urb *urb = NULL;
+	unsigned char *new_buffer;
+	const unsigned char *current_position;
+	int status;
+	int bytes_sent;
+	int buffer_size;
+	int i;
+
 	if (!bluetooth) {
 		return -ENODEV;
 	}
-	
+
 	dbg(__FUNCTION__ " - %d byte(s)", count);
 
 	if (!bluetooth->active) {
 		dbg (__FUNCTION__ " - device not opened");
 		return -EINVAL;
 	}
-	
+
 	if (count == 0) {
 		dbg(__FUNCTION__ " - write request of 0 bytes");
-		return (0);
+		return 0;
 	}
-
-	if (bluetooth->write_urb->status == -EINPROGRESS) {
-		dbg (__FUNCTION__ " - already writing");
-		return (0);
+	if (count == 1) {
+		dbg(__FUNCTION__ " - write request only included type %d", buf[0]);
+		return 1;
 	}
 
-	count = (count > bluetooth->bulk_out_size) ? bluetooth->bulk_out_size : count;
-
 #ifdef DEBUG
-	{
-		int i;
-		printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ " - length = %d, data = ", count);
-		for (i = 0; i < count; ++i) {
-			printk ("%.2x ", buf[i]);
-		}
-		printk ("\n");
+	printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ " - length = %d, data = ", count);
+	for (i = 0; i < count; ++i) {
+		printk ("%.2x ", buf[i]);
 	}
+	printk ("\n");
 #endif
 
-	if (from_user) {
-		copy_from_user(bluetooth->write_urb->transfer_buffer, buf, count);
-	}
-	else {
-		memcpy (bluetooth->write_urb->transfer_buffer, buf, count);
-	}  
-
-	/* send the data out the bulk bluetooth */
-	bluetooth->write_urb->transfer_buffer_length = count;
+	switch (*buf) {
+		/* First byte indicates the type of packet */
+		case CMD_PKT:
+			/* dbg(__FUNCTION__ "- Send cmd_pkt len:%d", count);*/
+
+			if (in_interrupt()){
+				printk("cmd_pkt from interrupt!\n");
+				return count;
+			}
+
+			new_buffer = kmalloc (count-1, GFP_KERNEL);
+
+			if (!new_buffer) {
+				err (__FUNCTION__ "- out of memory.");
+				return -ENOMEM;
+			}
+
+			if (from_user)
+				copy_from_user (new_buffer, buf+1, count-1);
+			else
+				memcpy (new_buffer, buf+1, count-1);
+
+			bluetooth_ctrl_msg (bluetooth, 0x00, 0x00, new_buffer, count-1);
+
+			/* need to free new_buffer somehow... FIXME */
+			return count;
+
+		case ACL_PKT:
+			current_position = buf;
+			++current_position;
+			--count;
+			bytes_sent = 0;
+
+			while (count > 0) {
+				urb = NULL;
+
+				/* try to find a free urb in our list */
+				for (i = 0; i < NUM_BULK_URBS; ++i) {
+					if (bluetooth->write_urb_pool[i]->status != -EINPROGRESS) {
+						urb = bluetooth->write_urb_pool[i];
+						break;
+					}
+				}
+				if (urb == NULL) {
+					dbg (__FUNCTION__ " - no free urbs");
+					return bytes_sent;
+				}
+				
+				/* free up the last buffer that this urb used */
+				if (urb->transfer_buffer != NULL) {
+					kfree(urb->transfer_buffer);
+					urb->transfer_buffer = NULL;
+				}
+
+				buffer_size = MIN (count, bluetooth->bulk_out_size);
+				
+				new_buffer = kmalloc (buffer_size, GFP_KERNEL);
+				if (new_buffer == NULL) {
+					err(__FUNCTION__" no more kernel memory...");
+					return bytes_sent;
+				}
+
+				if (from_user)
+					copy_from_user(new_buffer, current_position, buffer_size);
+				else
+					memcpy (new_buffer, current_position, buffer_size);
+
+				/* build up our urb */
+				FILL_BULK_URB (urb, bluetooth->dev, usb_sndbulkpipe(bluetooth->dev, bluetooth->bulk_out_endpointAddress),
+						new_buffer, buffer_size, bluetooth_write_bulk_callback, bluetooth);
+				urb->transfer_flags |= USB_QUEUE_BULK;
+
+				/* send it down the pipe */
+				status = usb_submit_urb(urb);
+				if (status)
+					dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed with status = %d", status);
+#ifdef BTBUGGYHARDWARE
+				/* A workaround for the stalled data bug */
+				/* May or may not be needed...*/
+				if (count != 0) {
+					udelay(500);
+				}
+#endif
+				current_position += buffer_size;
+				bytes_sent += buffer_size;
+				count -= buffer_size;
+			}
 
-	if (usb_submit_urb(bluetooth->write_urb))
-		dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed");
+			return bytes_sent + 1;
+		
+		default :
+			dbg(__FUNCTION__" - unsupported (at this time) write type");
+	}
 
-	return count;
+	return 0;
 } 
 
 
@@ -286,21 +480,25 @@
 {
 	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
 	int room = 0;
+	int i;
 
 	if (!bluetooth) {
 		return -ENODEV;
 	}
 
 	dbg(__FUNCTION__);
-	
+
 	if (!bluetooth->active) {
 		dbg (__FUNCTION__ " - device not open");
 		return -EINVAL;
 	}
 
-	if (bluetooth->write_urb->status != -EINPROGRESS)
-		room = bluetooth->bulk_out_size;
-	
+	for (i = 0; i < NUM_BULK_URBS; ++i) {
+		if (bluetooth->write_urb_pool[i]->status != -EINPROGRESS) {
+			room += bluetooth->bulk_out_size;
+		}
+	}
+
 	dbg(__FUNCTION__ " - returns %d", room);
 	return room;
 }
@@ -310,6 +508,7 @@
 {
 	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
 	int chars = 0;
+	int i;
 
 	if (!bluetooth) {
 		return -ENODEV;
@@ -320,9 +519,11 @@
 		return -EINVAL;
 	}
 
-
-	if (bluetooth->write_urb->status == -EINPROGRESS)
-		chars = bluetooth->write_urb->transfer_buffer_length;
+	for (i = 0; i < NUM_BULK_URBS; ++i) {
+		if (bluetooth->write_urb_pool[i]->status == -EINPROGRESS) {
+			chars += bluetooth->write_urb_pool[i]->transfer_buffer_length;
+		}
+	}
 
 	dbg (__FUNCTION__ " - returns %d", chars);
 	return chars;
@@ -343,9 +544,9 @@
 		dbg (__FUNCTION__ " - device not open");
 		return;
 	}
-
-	/* FIXME!!! */
 	
+	dbg(__FUNCTION__ " unsupported (at this time)");
+
 	return;
 }
 
@@ -365,9 +566,7 @@
 		return;
 	}
 
-	/* FIXME!!! */
-
-	return;
+	dbg(__FUNCTION__ " unsupported (at this time)");
 }
 
 
@@ -387,7 +586,6 @@
 	}
 
 	/* FIXME!!! */
-	
 	return -ENOIOCTLCMD;
 }
 
@@ -408,59 +606,184 @@
 	}
 
 	/* FIXME!!! */
-	
+
 	return;
 }
 
 
+#ifdef BTBUGGYHARDWARE
+void btusb_enable_bulk_read(struct tty_struct *tty){
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+
+	if (!bluetooth) {
+		return;
+	}
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not open");
+		return;
+	}
+
+	if (bluetooth->read_urb)
+		if (usb_submit_urb(bluetooth->read_urb))
+			dbg (__FUNCTION__ " - usb_submit_urb(read bulk) failed");
+}
+
+void btusb_disable_bulk_read(struct tty_struct *tty){
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)tty->driver_data, __FUNCTION__);
+
+	if (!bluetooth) {
+		return;
+	}
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth->active) {
+		dbg (__FUNCTION__ " - device not open");
+		return;
+	}
+
+	if ((bluetooth->read_urb) && (bluetooth->read_urb->actual_length))
+		usb_unlink_urb(bluetooth->read_urb);
+}
+#endif
+
+
 /*****************************************************************************
  * urb callback functions
  *****************************************************************************/
 
+
 static void bluetooth_int_callback (struct urb *urb)
 {
 	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
 	unsigned char *data = urb->transfer_buffer;
-	int i;
+	unsigned int i;
+	unsigned int count = urb->actual_length;
+	unsigned int packet_size;
 
 	dbg(__FUNCTION__);
-	
+
 	if (!bluetooth) {
 		dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
 		return;
 	}
 
 	if (urb->status) {
-		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		dbg(__FUNCTION__ " - nonzero int status received: %d", urb->status);
+		return;
+	}
+
+	if (!count) {
+		dbg(__FUNCTION__ " - zero length int");
 		return;
 	}
 
+
 #ifdef DEBUG
-	if (urb->actual_length) {
-		printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", urb->actual_length);
-		for (i = 0; i < urb->actual_length; ++i) {
+	if (count) {
+		printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", count);
+		for (i = 0; i < count; ++i) {
 			printk ("%.2x ", data[i]);
 		}
 		printk ("\n");
 	}
 #endif
 
-	/* Don't really know what else to do with this data yet. */
-	/* FIXME!!! */
+#ifdef BTBUGGYHARDWARE
+	if ((count >= 2) && (data[0] == 0xFF) && (data[1] == 0x00)) {
+		data += 2;
+		count -= 2;
+	}
+	if (count == 0) {
+		urb->actual_length = 0;
+		return;
+	}
+#endif
+	/* We add  a packet type identifier to the beginning of each
+	   HCI frame.  This makes the data in the tty look like a
+	   serial USB devices.  Each HCI frame can be broken across
+	   multiple URBs so we buffer them until we have a full hci
+	   packet */
+
+	if (!bluetooth->int_packet_pos) {
+		bluetooth->int_buffer[0] = EVENT_PKT;
+		bluetooth->int_packet_pos++;
+	}
 	
-	return;
+	if (bluetooth->int_packet_pos + count > EVENT_BUFFER_SIZE) {
+		err(__FUNCTION__ " - exceeded EVENT_BUFFER_SIZE");
+		bluetooth->int_packet_pos = 0;
+		return;
+	}
+
+	memcpy (&bluetooth->int_buffer[bluetooth->int_packet_pos],
+		urb->transfer_buffer, count);
+	bluetooth->int_packet_pos += count;
+	urb->actual_length = 0;
+
+	if (bluetooth->int_packet_pos >= EVENT_HDR_SIZE)
+		packet_size = bluetooth->int_buffer[2];
+	else
+		return;
+
+	if (packet_size + EVENT_HDR_SIZE < bluetooth->int_packet_pos) {
+		err(__FUNCTION__ " - packet was too long");
+		bluetooth->int_packet_pos = 0;
+		return;
+	}
+
+	if (packet_size + EVENT_HDR_SIZE == bluetooth->int_packet_pos){
+		for (i = 0; i < bluetooth->int_packet_pos; ++i)
+			tty_insert_flip_char(bluetooth->tty, bluetooth->int_buffer[i], 0);
+		tty_flip_buffer_push(bluetooth->tty);
+
+		bluetooth->int_packet_pos = 0;
+	}
+}
+
+
+static void bluetooth_ctrl_callback (struct urb *urb)
+{
+	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
+
+	dbg(__FUNCTION__);
+
+	if (!bluetooth) {
+		dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		return;
+	}
 }
 
 
 static void bluetooth_read_bulk_callback (struct urb *urb)
 {
 	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
-	struct tty_struct *tty;
 	unsigned char *data = urb->transfer_buffer;
-	int i;
+	unsigned int count = urb->actual_length;
+	unsigned int i;
+	uint packet_size;
+
+#ifdef BTBUGGYHARDWARE
+	if ((count == 4) && (data[0] == 0x00) && (data[1] == 0x00)
+	    && (data[2] == 0x00) && (data[3] == 0x00)) {
+		urb->actual_length = 0;
+		if (usb_submit_urb(urb))
+			dbg(__FUNCTION__ " - failed resubmitting read urb");
+
+		return;
+	}
+#endif
 
 	dbg(__FUNCTION__);
-	
+
 	if (!bluetooth) {
 		dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
 		return;
@@ -471,25 +794,67 @@
 		return;
 	}
 
+	if (!count) {
+		dbg(__FUNCTION__ " - zero length read bulk");
+		return;
+	}
+
 #ifdef DEBUG
-	if (urb->actual_length) {
-		printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", urb->actual_length);
-		for (i = 0; i < urb->actual_length; ++i) {
+	if (count) {
+		printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", count);
+		for (i = 0; i < count; ++i) {
 			printk ("%.2x ", data[i]);
 		}
 		printk ("\n");
 	}
 #endif
+	/* We add  a packet type identifier to the beginning of each
+	   HCI frame.  This makes the data in the tty look like a
+	   serial USB devices.  Each HCI frame can be broken across
+	   multiple URBs so we buffer them until we have a full hci
+	   packet */
+	
+	if (!bluetooth->bulk_packet_pos) {
+		bluetooth->bulk_buffer[0] = ACL_PKT;
+		bluetooth->bulk_packet_pos++;
+	}
+
+	if (bluetooth->bulk_packet_pos + count > ACL_BUFFER_SIZE) {
+		err(__FUNCTION__ " - exceeded ACL_BUFFER_SIZE");
+		bluetooth->bulk_packet_pos = 0;
+		if (usb_submit_urb(urb))
+			dbg(__FUNCTION__ " - failed resubmitting read urb");
+		return;
+	}
 
-	tty = bluetooth->tty;
-	if (urb->actual_length) {
-		for (i = 0; i < urb->actual_length ; ++i) {
-			 tty_insert_flip_char(tty, data[i], 0);
-	  	}
-	  	tty_flip_buffer_push(tty);
+	memcpy (&bluetooth->bulk_buffer[bluetooth->bulk_packet_pos],
+		urb->transfer_buffer, count);
+	bluetooth->bulk_packet_pos += count;
+	urb->actual_length = 0;
+
+	if (bluetooth->bulk_packet_pos >= ACL_HDR_SIZE) {
+		packet_size = CHAR2INT16(bluetooth->bulk_buffer[4],bluetooth->bulk_buffer[3]);
+	} else {
+		if (usb_submit_urb(urb))
+			dbg(__FUNCTION__ " - failed resubmitting read urb");
+		return;
 	}
 
-	/* Continue trying to always read  */
+	if (packet_size + ACL_HDR_SIZE < bluetooth->bulk_packet_pos) {
+		err(__FUNCTION__ " - packet was too long");
+		bluetooth->bulk_packet_pos = 0;
+		if (usb_submit_urb(urb))
+			dbg(__FUNCTION__ " - failed resubmitting read urb");
+		return;
+	}
+
+	if (packet_size + ACL_HDR_SIZE == bluetooth->bulk_packet_pos) {
+		for (i = 0; i < bluetooth->bulk_packet_pos; ++i)
+			tty_insert_flip_char(bluetooth->tty, bluetooth->bulk_buffer[i], 0);
+		tty_flip_buffer_push(bluetooth->tty);
+		bluetooth->bulk_packet_pos = 0;
+	}	
+
 	if (usb_submit_urb(urb))
 		dbg(__FUNCTION__ " - failed resubmitting read urb");
 
@@ -502,7 +867,7 @@
 	struct usb_bluetooth *bluetooth = get_usb_bluetooth ((struct usb_bluetooth *)urb->context, __FUNCTION__);
 
 	dbg(__FUNCTION__);
-	
+
 	if (!bluetooth) {
 		dbg(__FUNCTION__ " - bad bluetooth pointer, exiting");
 		return;
@@ -513,9 +878,9 @@
 		return;
 	}
 
+	/* wake up our little function to let the tty layer know that something happened */
 	queue_task(&bluetooth->tqueue, &tq_immediate);
 	mark_bh(IMMEDIATE_BH);
-	
 	return;
 }
 
@@ -526,11 +891,11 @@
 	struct tty_struct *tty;
 
 	dbg(__FUNCTION__);
-	
+
 	if (!bluetooth) {
 		return;
 	}
- 	
+
 	tty = bluetooth->tty;
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
 		dbg(__FUNCTION__ " - write wakeup call.");
@@ -549,28 +914,32 @@
 	struct usb_endpoint_descriptor *interrupt_in_endpoint[8];
 	struct usb_endpoint_descriptor *bulk_in_endpoint[8];
 	struct usb_endpoint_descriptor *bulk_out_endpoint[8];
+	int control_out_endpoint;
+
 	int minor;
 	int buffer_size;
 	int i;
 	int num_interrupt_in = 0;
 	int num_bulk_in = 0;
 	int num_bulk_out = 0;
-	
+
 	/* see if this device has the proper class signature */
 	if ((dev->descriptor.bDeviceClass != WIRELESS_CLASS_CODE) || 
 	    (dev->descriptor.bDeviceSubClass != RF_SUBCLASS_CODE) ||
 	    (dev->descriptor.bDeviceProtocol != BLUETOOTH_PROGRAMMING_PROTOCOL_CODE)) {
 		dbg (__FUNCTION__ " - class signature %d, %d, %d did not match", 
-			dev->descriptor.bDeviceClass, dev->descriptor.bDeviceSubClass,
-			dev->descriptor.bDeviceProtocol);
+		     dev->descriptor.bDeviceClass, dev->descriptor.bDeviceSubClass,
+		     dev->descriptor.bDeviceProtocol);
 		return NULL;
 	}
 
-	/* find the endpoints that we need */
 	interface = &dev->actconfig->interface[ifnum].altsetting[0];
+	control_out_endpoint = interface->bInterfaceNumber;
+
+	/* find the endpoints that we need */
 	for (i = 0; i < interface->bNumEndpoints; ++i) {
 		endpoint = &interface->endpoint[i];
-		
+
 		if ((endpoint->bEndpointAddress & 0x80) &&
 		    ((endpoint->bmAttributes & 3) == 0x02)) {
 			/* we found a bulk in endpoint */
@@ -586,7 +955,7 @@
 			bulk_out_endpoint[num_bulk_out] = endpoint;
 			++num_bulk_out;
 		}
-		
+
 		if ((endpoint->bEndpointAddress & 0x80) &&
 		    ((endpoint->bmAttributes & 3) == 0x03)) {
 			/* we found a interrupt in endpoint */
@@ -595,7 +964,7 @@
 			++num_interrupt_in;
 		}
 	}
-	
+
 	/* according to the spec, we can only have 1 bulk_in, 1 bulk_out, and 1 interrupt_in endpoints */
 	if ((num_bulk_in != 1) ||
 	    (num_bulk_out != 1) ||
@@ -603,7 +972,7 @@
 		dbg (__FUNCTION__ " - improper number of endpoints. Bluetooth driver not bound.");
 		return NULL;
 	}
-	
+
 	MOD_INC_USE_COUNT;
 	info("USB Bluetooth converter detected");
 
@@ -620,15 +989,29 @@
 		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
-	
+
 	memset(bluetooth, 0, sizeof(struct usb_bluetooth));
-	
+
 	bluetooth->magic = USB_BLUETOOTH_MAGIC;
 	bluetooth->dev = dev;
 	bluetooth->minor = minor;
 	bluetooth->tqueue.routine = bluetooth_softint;
 	bluetooth->tqueue.data = bluetooth;
 
+	/* record the interface number for the control out */
+	bluetooth->control_out_bInterfaceNum = control_out_endpoint;
+	
+	/* create our control out urb pool */ 
+	for (i = 0; i < NUM_CONTROL_URBS; ++i) {
+		struct urb  *urb = usb_alloc_urb(0);
+		if (urb == NULL) {
+			err("No free urbs available");
+			goto probe_error;
+		}
+		urb->transfer_buffer = NULL;
+		bluetooth->control_urb_pool[i] = urb;
+	}
+
 	/* set up the endpoint information */
 	endpoint = bulk_in_endpoint[0];
 	bluetooth->read_urb = usb_alloc_urb (0);
@@ -646,20 +1029,20 @@
 		      bluetooth->bulk_in_buffer, buffer_size, bluetooth_read_bulk_callback, bluetooth);
 
 	endpoint = bulk_out_endpoint[0];
-	bluetooth->write_urb = usb_alloc_urb(0);
-	if (!bluetooth->write_urb) {
-		err("No free urbs available");
-		goto probe_error;
-	}
-	buffer_size = endpoint->wMaxPacketSize;
-	bluetooth->bulk_out_size = buffer_size;
-	bluetooth->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
-	if (!bluetooth->bulk_out_buffer) {
-		err("Couldn't allocate bulk_out_buffer");
-		goto probe_error;
+	bluetooth->bulk_out_endpointAddress = endpoint->bEndpointAddress;
+	
+	/* create our write urb pool */ 
+	for (i = 0; i < NUM_BULK_URBS; ++i) {
+		struct urb  *urb = usb_alloc_urb(0);
+		if (urb == NULL) {
+			err("No free urbs available");
+			goto probe_error;
+		}
+		urb->transfer_buffer = NULL;
+		bluetooth->write_urb_pool[i] = urb;
 	}
-	FILL_BULK_URB(bluetooth->write_urb, dev, usb_sndbulkpipe(dev, endpoint->bEndpointAddress),
-		      bluetooth->bulk_out_buffer, buffer_size, bluetooth_write_bulk_callback, bluetooth);
+	
+	bluetooth->bulk_out_size = endpoint->wMaxPacketSize * 2;
 
 	endpoint = interrupt_in_endpoint[0];
 	bluetooth->interrupt_in_urb = usb_alloc_urb(0);
@@ -680,9 +1063,9 @@
 	/* initialize the devfs nodes for this device and let the user know what bluetooths we are bound to */
 	tty_register_devfs (&bluetooth_tty_driver, 0, minor);
 	info("Bluetooth converter now attached to ttyBLUE%d (or usb/ttblue/%d for devfs)", minor, minor);
-	
+
 	bluetooth_table[minor] = bluetooth;
-	
+
 	return bluetooth; /* success */
 
 probe_error:
@@ -690,15 +1073,17 @@
 		usb_free_urb (bluetooth->read_urb);
 	if (bluetooth->bulk_in_buffer)
 		kfree (bluetooth->bulk_in_buffer);
-	if (bluetooth->write_urb)
-		usb_free_urb (bluetooth->write_urb);
-	if (bluetooth->bulk_out_buffer)
-		kfree (bluetooth->bulk_out_buffer);
 	if (bluetooth->interrupt_in_urb)
 		usb_free_urb (bluetooth->interrupt_in_urb);
 	if (bluetooth->interrupt_in_buffer)
 		kfree (bluetooth->interrupt_in_buffer);
-		
+	for (i = 0; i < NUM_BULK_URBS; ++i)
+		if (bluetooth->write_urb_pool[i])
+			usb_free_urb (bluetooth->write_urb_pool[i]);
+	for (i = 0; i < NUM_CONTROL_URBS; ++i) 
+		if (bluetooth->control_urb_pool[i])
+			usb_free_urb (bluetooth->control_urb_pool[i]);
+
 	bluetooth_table[minor] = NULL;
 
 	/* free up any memory that we allocated */
@@ -711,6 +1096,7 @@
 static void usb_bluetooth_disconnect(struct usb_device *dev, void *ptr)
 {
 	struct usb_bluetooth *bluetooth = (struct usb_bluetooth *) ptr;
+	int i;
 
 	if (bluetooth) {
 		bluetooth->active = 0;
@@ -721,14 +1107,7 @@
 		}
 		if (bluetooth->bulk_in_buffer)
 			kfree (bluetooth->bulk_in_buffer);
-			
-		if (bluetooth->write_urb) {
-			usb_unlink_urb (bluetooth->write_urb);
-			usb_free_urb (bluetooth->write_urb);
-		}
-		if (bluetooth->bulk_out_buffer)
-			kfree (bluetooth->bulk_out_buffer);
-		
+
 		if (bluetooth->interrupt_in_urb) {
 			usb_unlink_urb (bluetooth->interrupt_in_urb);
 			usb_free_urb (bluetooth->interrupt_in_urb);
@@ -737,6 +1116,26 @@
 			kfree (bluetooth->interrupt_in_buffer);
 
 		tty_unregister_devfs (&bluetooth_tty_driver, bluetooth->minor);
+
+		if (bluetooth->tty)
+			tty_hangup(bluetooth->tty);
+
+		for (i = 0; i < NUM_BULK_URBS; ++i) {
+			if (bluetooth->write_urb_pool[i]) {
+				usb_unlink_urb (bluetooth->write_urb_pool[i]);
+				if (bluetooth->write_urb_pool[i]->transfer_buffer)
+					kfree (bluetooth->write_urb_pool[i]->transfer_buffer);
+				usb_free_urb (bluetooth->write_urb_pool[i]);
+			}
+		}
+		for (i = 0; i < NUM_CONTROL_URBS; ++i) {
+			if (bluetooth->control_urb_pool[i]) {
+				usb_unlink_urb (bluetooth->control_urb_pool[i]);
+				if (bluetooth->control_urb_pool[i]->transfer_buffer)
+					kfree (bluetooth->control_urb_pool[i]->transfer_buffer);
+				usb_free_urb (bluetooth->control_urb_pool[i]);
+			}
+		}
 		
 		info("Bluetooth converter now disconnected from ttyBLUE%d", bluetooth->minor);
 
@@ -748,7 +1147,7 @@
 	} else {
 		info("device disconnected");
 	}
-	
+
 	MOD_DEC_USE_COUNT;
 }
 
@@ -763,12 +1162,12 @@
 	type:			TTY_DRIVER_TYPE_SERIAL,
 	subtype:		SERIAL_TYPE_NORMAL,
 	flags:			TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
-	
+
 	refcount:		&bluetooth_refcount,
 	table:			bluetooth_tty,
 	termios:		bluetooth_termios,
 	termios_locked:		bluetooth_termios_locked,
-	
+
 	open:			bluetooth_open,
 	close:			bluetooth_close,
 	write:			bluetooth_write,
@@ -808,7 +1207,7 @@
 		err("usb_register failed for the USB bluetooth driver. Error number %d", result);
 		return -1;
 	}
-	
+
 	return 0;
 }