diff -urN linux-2.4.18/arch/alpha/config.in linux-2.4.18-mh15/arch/alpha/config.in
--- linux-2.4.18/arch/alpha/config.in	2001-11-21 00:49:31.000000000 +0100
+++ linux-2.4.18-mh15/arch/alpha/config.in	2004-08-01 16:26:22.000000000 +0200
@@ -371,9 +371,7 @@
 source drivers/usb/Config.in
 source drivers/input/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
diff -urN linux-2.4.18/arch/arm/config.in linux-2.4.18-mh15/arch/arm/config.in
--- linux-2.4.18/arch/arm/config.in	2001-11-09 22:58:02.000000000 +0100
+++ linux-2.4.18-mh15/arch/arm/config.in	2004-08-01 16:26:22.000000000 +0200
@@ -584,9 +584,7 @@
 
 source drivers/usb/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
diff -urN linux-2.4.18/arch/i386/config.in linux-2.4.18-mh15/arch/i386/config.in
--- linux-2.4.18/arch/i386/config.in	2002-02-25 20:37:52.000000000 +0100
+++ linux-2.4.18-mh15/arch/i386/config.in	2004-08-01 16:26:22.000000000 +0200
@@ -407,9 +407,7 @@
 
 source drivers/usb/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
diff -urN linux-2.4.18/arch/ppc/config.in linux-2.4.18-mh15/arch/ppc/config.in
--- linux-2.4.18/arch/ppc/config.in	2002-02-25 20:37:55.000000000 +0100
+++ linux-2.4.18-mh15/arch/ppc/config.in	2004-08-01 16:26:22.000000000 +0200
@@ -389,9 +389,7 @@
 
 source drivers/usb/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
diff -urN linux-2.4.18/arch/sparc/config.in linux-2.4.18-mh15/arch/sparc/config.in
--- linux-2.4.18/arch/sparc/config.in	2001-06-12 04:15:27.000000000 +0200
+++ linux-2.4.18-mh15/arch/sparc/config.in	2004-08-01 16:26:22.000000000 +0200
@@ -251,9 +251,7 @@
 
 source fs/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Watchdog'
diff -urN linux-2.4.18/arch/sparc64/config.in linux-2.4.18-mh15/arch/sparc64/config.in
--- linux-2.4.18/arch/sparc64/config.in	2001-12-21 18:41:53.000000000 +0100
+++ linux-2.4.18-mh15/arch/sparc64/config.in	2004-08-01 16:26:22.000000000 +0200
@@ -283,9 +283,7 @@
 
 source drivers/usb/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Watchdog'
diff -urN linux-2.4.18/arch/sparc64/kernel/ioctl32.c linux-2.4.18-mh15/arch/sparc64/kernel/ioctl32.c
--- linux-2.4.18/arch/sparc64/kernel/ioctl32.c	2002-02-25 20:37:56.000000000 +0100
+++ linux-2.4.18-mh15/arch/sparc64/kernel/ioctl32.c	2004-08-01 16:26:23.000000000 +0200
@@ -92,6 +92,7 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci.h>
+#include <net/bluetooth/rfcomm.h>
 
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
@@ -3822,6 +3823,15 @@
 	return err;
 }
 
+/* Bluetooth ioctls */
+#define HCIUARTSETPROTO	_IOW('U', 200, int)
+#define HCIUARTGETPROTO	_IOR('U', 201, int)
+
+#define BNEPCONNADD	_IOW('B', 200, int)
+#define BNEPCONNDEL	_IOW('B', 201, int)
+#define BNEPGETCONNLIST	_IOR('B', 210, int)
+#define BNEPGETCONNINFO	_IOR('B', 211, int)
+
 struct mtd_oob_buf32 {
 	u32 start;
 	u32 length;
@@ -3878,6 +3888,16 @@
 	return ((0 == ret) ? 0 : -EFAULT);
 }	
 
+#define CMTPCONNADD	_IOW('C', 200, int)
+#define CMTPCONNDEL	_IOW('C', 201, int)
+#define CMTPGETCONNLIST	_IOR('C', 210, int)
+#define CMTPGETCONNINFO	_IOR('C', 211, int)
+
+#define HIDPCONNADD	_IOW('H', 200, int)
+#define HIDPCONNDEL	_IOW('H', 201, int)
+#define HIDPGETCONNLIST	_IOR('H', 210, int)
+#define HIDPGETCONNINFO	_IOR('H', 211, int)
+
 struct ioctl_trans {
 	unsigned int cmd;
 	unsigned int handler;
@@ -4540,6 +4560,25 @@
 COMPATIBLE_IOCTL(HCISETSCAN)
 COMPATIBLE_IOCTL(HCISETAUTH)
 COMPATIBLE_IOCTL(HCIINQUIRY)
+COMPATIBLE_IOCTL(HCIUARTSETPROTO)
+COMPATIBLE_IOCTL(HCIUARTGETPROTO)
+COMPATIBLE_IOCTL(RFCOMMCREATEDEV)
+COMPATIBLE_IOCTL(RFCOMMRELEASEDEV)
+COMPATIBLE_IOCTL(RFCOMMGETDEVLIST)
+COMPATIBLE_IOCTL(RFCOMMGETDEVINFO)
+COMPATIBLE_IOCTL(RFCOMMSTEALDLC)
+COMPATIBLE_IOCTL(BNEPCONNADD)
+COMPATIBLE_IOCTL(BNEPCONNDEL)
+COMPATIBLE_IOCTL(BNEPGETCONNLIST)
+COMPATIBLE_IOCTL(BNEPGETCONNINFO)
+COMPATIBLE_IOCTL(CMTPCONNADD)
+COMPATIBLE_IOCTL(CMTPCONNDEL)
+COMPATIBLE_IOCTL(CMTPGETCONNLIST)
+COMPATIBLE_IOCTL(CMTPGETCONNINFO)
+COMPATIBLE_IOCTL(HIDPCONNADD)
+COMPATIBLE_IOCTL(HIDPCONNDEL)
+COMPATIBLE_IOCTL(HIDPGETCONNLIST)
+COMPATIBLE_IOCTL(HIDPGETCONNINFO)
 /* Misc. */
 COMPATIBLE_IOCTL(0x41545900)		/* ATYIO_CLKR */
 COMPATIBLE_IOCTL(0x41545901)		/* ATYIO_CLKW */
diff -urN linux-2.4.18/CREDITS linux-2.4.18-mh15/CREDITS
--- linux-2.4.18/CREDITS	2002-02-25 20:37:50.000000000 +0100
+++ linux-2.4.18-mh15/CREDITS	2004-08-01 16:26:23.000000000 +0200
@@ -1317,6 +1317,16 @@
 S: Provo, Utah 84606-5607
 S: USA
 
+N: Marcel Holtmann
+E: marcel@holtmann.org
+W: http://www.holtmann.org
+D: Maintainer of the Linux Bluetooth Subsystem
+D: Author and maintainer of the various Bluetooth HCI drivers
+D: Author and maintainer of the CAPI message transport protocol driver
+D: Author and maintainer of the Bluetooth HID protocol driver
+D: Various other Bluetooth related patches, cleanups and fixes
+S: Germany
+
 N: Rob W. W. Hooft
 E: hooft@EMBL-Heidelberg.DE
 D: Shared libs for graphics-tools and for the f2c compiler
@@ -2546,6 +2556,7 @@
 N: Aristeu Sergio Rozanski Filho
 E: aris@conectiva.com.br
 D: Support for EtherExpress 10 ISA (i82595) in eepro driver
+D: User level driver support for input
 S: Conectiva S.A.
 S: R. Tocantins, 89 - Cristo Rei
 S: 80050-430 - Curitiba - Paran�
diff -urN linux-2.4.18/Documentation/Configure.help linux-2.4.18-mh15/Documentation/Configure.help
--- linux-2.4.18/Documentation/Configure.help	2002-02-25 20:37:51.000000000 +0100
+++ linux-2.4.18-mh15/Documentation/Configure.help	2004-08-01 16:26:23.000000000 +0200
@@ -2824,14 +2824,6 @@
 
   If unsure, say N.
 
-HCI EMU (virtual device) driver
-CONFIG_BLUEZ_HCIEMU
-  Bluetooth Virtual HCI device driver.
-  This driver is required if you want to use HCI Emulation software.
-
-  Say Y here to compile support for Virtual HCI devices into the
-  kernel or say M to compile it as module (hci_usb.o).
-
 # Choice: alphatype
 Alpha system type
 CONFIG_ALPHA_GENERIC
@@ -11037,6 +11029,12 @@
 
   If unsure, say N.
 
+Hotplug firmware loading support (EXPERIMENTAL)
+CONFIG_FW_LOADER
+  This option is provided for the case where no in-kernel-tree modules require
+  hotplug firmware loading support, but a module built outside the kernel tree
+  does.
+
 Use PCI shared memory for NIC registers
 CONFIG_TULIP_MMIO
   Use PCI shared memory for the NIC registers, rather than going through 
@@ -12896,6 +12894,15 @@
   accessible under char device 13:64+ - /dev/input/eventX in a generic
   way.  This is the future ...
 
+CONFIG_INPUT_UINPUT
+  Say Y here if you want to support user level drivers for input
+  subsystem accessible under char device 10:223 - /dev/input/uinput.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called uinput.o.  If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.	    
+
 USB Scanner support
 CONFIG_USB_SCANNER
   Say Y here if you want to connect a USB scanner to your computer's
@@ -19870,19 +19877,22 @@
   Bluetooth can be found at <http://www.bluetooth.com/>.
 
   Linux Bluetooth subsystem consist of several layers:
-               HCI Core (device and connection manager, scheduler)
-               HCI Device drivers (interface to the hardware)
-               L2CAP Module (L2CAP protocol)
+               BlueZ Core (HCI device and connection manager, scheduler)
+               HCI Device drivers (Interface to the hardware)
+               SCO Module (SCO audio links)
+               L2CAP Module (Logical Link Control and Adaptation Protocol)
+               RFCOMM Module (RFCOMM Protocol)
+               BNEP Module (Bluetooth Network Encapsulation Protocol)
+               CMTP Module (CAPI Message Transport Protocol)
+               HIDP Module (Human Interface Device Protocol)
 
-  Say Y here to enable Linux Bluetooth support and to build HCI Core
-  layer.
+  Say Y here to compile Bluetooth support into the kernel or say M to
+  compile it as module (bluez.o).
 
   To use Linux Bluetooth subsystem, you will need several user-space
   utilities like hciconfig and hcid.  These utilities and updates to
   Bluetooth kernel modules are provided in the BlueZ package.
-  For more information, see <http://bluez.sourceforge.net/>.
-
-  If you want to compile HCI Core as module (hci.o) say M here.
+  For more information, see <http://www.bluez.org/>.
 
 L2CAP protocol support
 CONFIG_BLUEZ_L2CAP
@@ -19893,15 +19903,96 @@
   Say Y here to compile L2CAP support into the kernel or say M to
   compile it as module (l2cap.o).
 
+SCO links support
+CONFIG_BLUEZ_SCO
+  SCO link provides voice transport over Bluetooth.  SCO support is
+  required for voice applications like Headset and Audio.
+
+  Say Y here to compile SCO support into the kernel or say M to
+  compile it as module (sco.o).
+
+RFCOMM protocol support
+CONFIG_BLUEZ_RFCOMM
+  RFCOMM provides connection oriented stream transport.  RFCOMM
+  support is required for Dialup Networking, OBEX and other Bluetooth
+  applications.
+
+  Say Y here to compile RFCOMM support into the kernel or say M to
+  compile it as module (rfcomm.o).
+
+RFCOMM TTY emulation support
+CONFIG_BLUEZ_RFCOMM_TTY
+  This option enables TTY emulation support for RFCOMM channels.
+
+BNEP protocol support
+CONFIG_BLUEZ_BNEP
+  BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
+  emulation layer on top of Bluetooth.  BNEP is required for
+  Bluetooth PAN (Personal Area Network).
+
+  Say Y here to compile BNEP support into the kernel or say M to
+  compile it as module (bnep.o).
+
+BNEP multicast filter support
+CONFIG_BLUEZ_BNEP_MC_FILTER
+  This option enables the multicast filter support for BNEP.
+
+BNEP protocol filter support
+CONFIG_BLUEZ_BNEP_PROTO_FILTER
+  This option enables the protocol filter support for BNEP.
+
+CMTP protocol support
+CONFIG_BLUEZ_CMTP
+  CMTP (CAPI Message Transport Protocol) is a transport layer
+  for CAPI messages.  CMTP is required for the Bluetooth Common
+  ISDN Access Profile.
+
+  Say Y here to compile CMTP support into the kernel or say M to
+  compile it as module (cmtp.o).
+
+HIDP protocol support
+CONFIG_BLUEZ_HIDP
+  HIDP (Human Interface Device Protocol) is a transport layer
+  for HID reports.  HIDP is required for the Bluetooth Human
+  Interface Device Profile.
+
+  Say Y here to compile HIDP support into the kernel or say M to
+  compile it as module (hidp.o).
+
 HCI UART driver
 CONFIG_BLUEZ_HCIUART
   Bluetooth HCI UART driver.
   This driver is required if you want to use Bluetooth devices with
-  serial port interface.
+  serial port interface. You will also need this driver if you have 
+  UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card 
+  adapter and BrainBoxes Bluetooth PC Card.
 
   Say Y here to compile support for Bluetooth UART devices into the
   kernel or say M to compile it as module (hci_uart.o).
 
+HCI UART (H4) protocol support 
+CONFIG_BLUEZ_HCIUART_H4
+  UART (H4) is serial protocol for communication between Bluetooth 
+  device and host. This protocol is required for most Bluetooth devices 
+  with UART interface, including PCMCIA and CF cards. 
+
+  Say Y here to compile support for HCI UART (H4) protocol.
+
+HCI BCSP protocol support 
+CONFIG_BLUEZ_HCIUART_BCSP
+  BCSP (BlueCore Serial Protocol) is serial protocol for communication 
+  between Bluetooth device and host. This protocol is required for non
+  USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and 
+  CF cards.
+
+  Say Y here to compile support for HCI BCSP protocol.
+
+HCI BCSP transmit CRC with every BCSP packet
+CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+  If you say Y here, a 16-bit CRC checksum will be transmitted along with
+  every BCSP (BlueCore Serial Protocol) packet sent to the Bluetooth chip.
+  This increases reliability, but slightly reduces efficiency.
+
 HCI USB driver
 CONFIG_BLUEZ_HCIUSB
   Bluetooth HCI USB driver.
@@ -19911,7 +20002,16 @@
   Say Y here to compile support for Bluetooth USB devices into the
   kernel or say M to compile it as module (hci_usb.o).
 
-HCI VHCI virtual HCI device driver
+HCI USB SCO (voice) support
+CONFIG_BLUEZ_HCIUSB_SCO
+  This option enables the SCO support in the HCI USB driver. You need this
+  to transmit voice data with your Bluetooth USB device. And your device
+  must also support sending SCO data over the HCI layer, because some of
+  them sends the SCO data to an internal PCM adapter.
+ 
+  Say Y here to compile support for HCI SCO data.
+ 
+HCI VHCI Virtual HCI device driver
 CONFIG_BLUEZ_HCIVHCI
   Bluetooth Virtual HCI device driver.
   This driver is required if you want to use HCI Emulation software.
@@ -19919,6 +20019,63 @@
   Say Y here to compile support for virtual HCI devices into the
   kernel or say M to compile it as module (hci_vhci.o).
 
+HCI BFUSB device driver
+CONFIG_BLUEZ_HCIBFUSB
+  Bluetooth HCI BlueFRITZ! USB driver.
+  This driver provides support for Bluetooth USB devices with AVM
+  interface:
+     AVM BlueFRITZ! USB
+
+  Say Y here to compile support for HCI BFUSB devices into the
+  kernel or say M to compile it as module (bfusb.o).
+
+HCI DTL1 (PC Card) device driver
+CONFIG_BLUEZ_HCIDTL1
+  Bluetooth HCI DTL1 (PC Card) driver.
+  This driver provides support for Bluetooth PCMCIA devices with
+  Nokia DTL1 interface:
+     Nokia Bluetooth Card
+     Socket Bluetooth CF Card
+
+  Say Y here to compile support for HCI DTL1 devices into the
+  kernel or say M to compile it as module (dtl1_cs.o).
+
+HCI BT3C (PC Card) device driver
+CONFIG_BLUEZ_HCIBT3C
+  Bluetooth HCI BT3C (PC Card) driver.
+  This driver provides support for Bluetooth PCMCIA devices with
+  3Com BT3C interface:
+     3Com Bluetooth Card (3CRWB6096)
+     HP Bluetooth Card
+
+  Say Y here to compile support for HCI BT3C devices into the
+  kernel or say M to compile it as module (bt3c_cs.o).
+
+HCI BlueCard (PC Card) device driver
+CONFIG_BLUEZ_HCIBLUECARD
+  Bluetooth HCI BlueCard (PC Card) driver.
+  This driver provides support for Bluetooth PCMCIA devices with
+  Anycom BlueCard interface:
+     Anycom Bluetooth PC Card
+     Anycom Bluetooth CF Card
+
+  Say Y here to compile support for HCI BlueCard devices into the
+  kernel or say M to compile it as module (bluecard_cs.o).
+
+HCI UART (PC Card) device driver
+CONFIG_BLUEZ_HCIBTUART
+  Bluetooth HCI UART (PC Card) driver.
+  This driver provides support for Bluetooth PCMCIA devices with
+  an UART interface:
+     Xircom CreditCard Bluetooth Adapter
+     Xircom RealPort2 Bluetooth Adapter
+     Sphinx PICO Card
+     H-Soft blue+Card
+     Cyber-blue Compact Flash Card
+
+  Say Y here to compile support for HCI UART devices into the
+  kernel or say M to compile it as module (btuart_cs.o).
+
 # The following options are for Linux when running on the Hitachi
 # SuperH family of RISC microprocessors.
 
diff -urN linux-2.4.18/Documentation/devices.txt linux-2.4.18-mh15/Documentation/devices.txt
--- linux-2.4.18/Documentation/devices.txt	2001-11-07 23:46:01.000000000 +0100
+++ linux-2.4.18-mh15/Documentation/devices.txt	2004-08-01 16:26:23.000000000 +0200
@@ -419,6 +419,7 @@
 		220 = /dev/mptctl	Message passing technology (MPT) control
 		221 = /dev/mvista/hssdsi	Montavista PICMG hot swap system driver
 		222 = /dev/mvista/hasi		Montavista PICMG high availability
+		223 = /dev/input/uinput		User level driver support for input
 		240-255			Reserved for local use
 
  11 char	Raw keyboard device
diff -urN linux-2.4.18/Documentation/firmware_class/firmware_sample_driver.c linux-2.4.18-mh15/Documentation/firmware_class/firmware_sample_driver.c
--- linux-2.4.18/Documentation/firmware_class/firmware_sample_driver.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/Documentation/firmware_class/firmware_sample_driver.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,121 @@
+/*
+ * firmware_sample_driver.c -
+ *
+ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
+ *
+ * Sample code on how to use request_firmware() from drivers.
+ *
+ * Note that register_firmware() is currently useless.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include "linux/firmware.h"
+
+#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
+#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
+char __init inkernel_firmware[] = "let's say that this is firmware\n";
+#endif
+
+static char ghost_device[] = "ghost0";
+
+static void sample_firmware_load(char *firmware, int size)
+{
+	u8 buf[size+1];
+	memcpy(buf, firmware, size);
+	buf[size] = '\0';
+	printk("firmware_sample_driver: firmware: %s\n", buf);
+}
+
+static void sample_probe_default(void)
+{
+	/* uses the default method to get the firmware */
+        const struct firmware *fw_entry;
+	printk("firmware_sample_driver: a ghost device got inserted :)\n");
+
+        if(request_firmware(&fw_entry, "sample_driver_fw", ghost_device)!=0)
+	{
+		printk(KERN_ERR
+		       "firmware_sample_driver: Firmware not available\n");
+		return;
+	}
+	
+	sample_firmware_load(fw_entry->data, fw_entry->size);
+
+	release_firmware(fw_entry);
+
+	/* finish setting up the device */
+}
+static void sample_probe_specific(void)
+{
+	/* Uses some specific hotplug support to get the firmware from
+	 * userspace  directly into the hardware, or via some sysfs file */
+
+	/* NOTE: This currently doesn't work */
+
+	printk("firmware_sample_driver: a ghost device got inserted :)\n");
+
+        if(request_firmware(NULL, "sample_driver_fw", ghost_device)!=0)
+	{
+		printk(KERN_ERR
+		       "firmware_sample_driver: Firmware load failed\n");
+		return;
+	}
+	
+	/* request_firmware blocks until userspace finished, so at
+	 * this point the firmware should be already in the device */
+
+	/* finish setting up the device */
+}
+static void sample_probe_async_cont(const struct firmware *fw, void *context)
+{
+	if(!fw){
+		printk(KERN_ERR
+		       "firmware_sample_driver: firmware load failed\n");
+		return;
+	}
+
+	printk("firmware_sample_driver: device pointer \"%s\"\n",
+	       (char *)context);
+	sample_firmware_load(fw->data, fw->size);
+}
+static void sample_probe_async(void)
+{
+	/* Let's say that I can't sleep */
+	int error;
+	error = request_firmware_nowait (THIS_MODULE,
+					 "sample_driver_fw", ghost_device,
+					 "my device pointer",
+					 sample_probe_async_cont);
+	if(error){
+		printk(KERN_ERR 
+		       "firmware_sample_driver:"
+		       " request_firmware_nowait failed\n");
+	}
+}
+
+static int sample_init(void)
+{
+#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
+	register_firmware("sample_driver_fw", inkernel_firmware,
+			  sizeof(inkernel_firmware));
+#endif
+	/* since there is no real hardware insertion I just call the
+	 * sample probe functions here */
+	sample_probe_specific();
+	sample_probe_default();
+	sample_probe_async();
+	return 0;
+}
+static void __exit sample_exit(void)
+{
+}
+
+module_init (sample_init);
+module_exit (sample_exit);
+
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/Documentation/firmware_class/hotplug-script linux-2.4.18-mh15/Documentation/firmware_class/hotplug-script
--- linux-2.4.18/Documentation/firmware_class/hotplug-script	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/Documentation/firmware_class/hotplug-script	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# Simple hotplug script sample:
+# 
+# Both $DEVPATH and $FIRMWARE are already provided in the environment.
+
+HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/
+
+echo 1 > /sysfs/$DEVPATH/loading
+cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
+echo 0 > /sysfs/$DEVPATH/loading
+
+# To cancel the load in case of error:
+#
+#	echo -1 > /sysfs/$DEVPATH/loading
+#
diff -urN linux-2.4.18/Documentation/firmware_class/README linux-2.4.18-mh15/Documentation/firmware_class/README
--- linux-2.4.18/Documentation/firmware_class/README	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/Documentation/firmware_class/README	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,58 @@
+
+ request_firmware() hotplug interface:
+ ------------------------------------
+	Copyright (C) 2003 Manuel Estrada Sainz <ranty@debian.org>
+
+ Why:
+ ---
+
+ Today, the most extended way to use firmware in the Linux kernel is linking
+ it statically in a header file. Which has political and technical issues:
+
+  1) Some firmware is not legal to redistribute.
+  2) The firmware occupies memory permanently, even though it often is just
+     used once.
+  3) Some people, like the Debian crowd, don't consider some firmware free
+     enough and remove entire drivers (e.g.: keyspan).
+
+ about in-kernel persistence:
+ ---------------------------
+ Under some circumstances, as explained below, it would be interesting to keep
+ firmware images in non-swappable kernel memory or even in the kernel image
+ (probably within initramfs).
+
+ Note that this functionality has not been implemented.
+
+ - Why OPTIONAL in-kernel persistence may be a good idea sometimes:
+ 
+	- If the device that needs the firmware is needed to access the
+	  filesystem. When upon some error the device has to be reset and the
+	  firmware reloaded, it won't be possible to get it from userspace.
+	  e.g.:
+		- A diskless client with a network card that needs firmware.
+		- The filesystem is stored in a disk behind an scsi device
+		  that needs firmware.
+	- Replacing buggy DSDT/SSDT ACPI tables on boot.
+	  Note: this would require the persistent objects to be included
+	  within the kernel image, probably within initramfs.
+	  
+   And the same device can be needed to access the filesystem or not depending
+   on the setup, so I think that the choice on what firmware to make
+   persistent should be left to userspace.
+
+ - Why register_firmware()+__init can be useful:
+ 	- For boot devices needing firmware.
+	- To make the transition easier:
+		The firmware can be declared __init and register_firmware()
+		called on module_init. Then the firmware is warranted to be
+		there even if "firmware hotplug userspace" is not there yet or
+		it doesn't yet provide the needed firmware.
+		Once the firmware is widely available in userspace, it can be
+		removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE).
+
+	In either case, if firmware hotplug support is there, it can move the
+	firmware out of kernel memory into the real filesystem for later
+	usage.
+
+	Note: If persistence is implemented on top of initramfs,
+	register_firmware() may not be appropriate.
diff -urN linux-2.4.18/drivers/bluetooth/bfusb.c linux-2.4.18-mh15/drivers/bluetooth/bfusb.c
--- linux-2.4.18/drivers/bluetooth/bfusb.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/bfusb.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,782 @@
+/*
+ *
+ *  AVM BlueFRITZ! USB driver
+ *
+ *  Copyright (C) 2003  Marcel Holtmann <marcel@holtmann.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/firmware.h>
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef CONFIG_BLUEZ_HCIBFUSB_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.1"
+
+static struct usb_device_id bfusb_table[] = {
+	/* AVM BlueFRITZ! USB */
+	{ USB_DEVICE(0x057c, 0x2200) },
+
+	{ }	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bfusb_table);
+
+
+#define BFUSB_MAX_BLOCK_SIZE	256
+
+#define BFUSB_BLOCK_TIMEOUT	(HZ * 3)
+
+#define BFUSB_TX_PROCESS	1
+#define BFUSB_TX_WAKEUP		2
+
+#define BFUSB_MAX_BULK_TX	1
+#define BFUSB_MAX_BULK_RX	1
+
+struct bfusb {
+	struct hci_dev		hdev;
+
+	unsigned long		state;
+
+	struct usb_device	*udev;
+
+	unsigned int		bulk_in_ep;
+	unsigned int		bulk_out_ep;
+	unsigned int		bulk_pkt_size;
+
+	rwlock_t		lock;
+
+	struct sk_buff_head	transmit_q;
+
+	struct sk_buff		*reassembly;
+
+	atomic_t		pending_tx;
+	struct sk_buff_head	pending_q;
+	struct sk_buff_head	completed_q;
+};
+
+struct bfusb_scb {
+	struct urb *urb;
+};
+
+static void bfusb_tx_complete(struct urb *urb);
+static void bfusb_rx_complete(struct urb *urb);
+
+static struct urb *bfusb_get_completed(struct bfusb *bfusb)
+{
+	struct sk_buff *skb;
+	struct urb *urb = NULL;
+
+	BT_DBG("bfusb %p", bfusb);
+
+	skb = skb_dequeue(&bfusb->completed_q);
+	if (skb) {
+		urb = ((struct bfusb_scb *) skb->cb)->urb;
+		kfree_skb(skb);
+	}
+
+	return urb;
+}
+
+static inline void bfusb_unlink_urbs(struct bfusb *bfusb)
+{
+	struct sk_buff *skb;
+	struct urb *urb;
+
+	BT_DBG("bfusb %p", bfusb);
+
+	while ((skb = skb_dequeue(&bfusb->pending_q))) {
+		urb = ((struct bfusb_scb *) skb->cb)->urb;
+		usb_unlink_urb(urb);
+		skb_queue_tail(&bfusb->completed_q, skb);
+	}
+
+	while ((urb = bfusb_get_completed(bfusb)))
+		usb_free_urb(urb);
+}
+
+
+static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb)
+{
+	struct bfusb_scb *scb = (void *) skb->cb;
+	struct urb *urb = bfusb_get_completed(bfusb);
+	int err, pipe;
+
+	BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len);
+
+	if (!urb && !(urb = usb_alloc_urb(0)))
+		return -ENOMEM;
+
+	pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+
+	FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, skb->len,
+			bfusb_tx_complete, skb);
+
+	urb->transfer_flags = USB_QUEUE_BULK;
+
+	scb->urb = urb;
+
+	skb_queue_tail(&bfusb->pending_q, skb);
+
+	err = usb_submit_urb(urb);
+	if (err) {
+		BT_ERR("%s bulk tx submit failed urb %p err %d", 
+					bfusb->hdev.name, urb, err);
+		skb_unlink(skb);
+		usb_free_urb(urb);
+	} else
+		atomic_inc(&bfusb->pending_tx);
+
+	return err;
+}
+
+static void bfusb_tx_wakeup(struct bfusb *bfusb)
+{
+	struct sk_buff *skb;
+
+	BT_DBG("bfusb %p", bfusb);
+
+	if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) {
+		set_bit(BFUSB_TX_WAKEUP, &bfusb->state);
+		return;
+	}
+
+	do {
+		clear_bit(BFUSB_TX_WAKEUP, &bfusb->state);
+
+		while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) &&
+				(skb = skb_dequeue(&bfusb->transmit_q))) {
+			if (bfusb_send_bulk(bfusb, skb) < 0) {
+				skb_queue_head(&bfusb->transmit_q, skb);
+				break;
+			}
+		}
+
+	} while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state));
+
+	clear_bit(BFUSB_TX_PROCESS, &bfusb->state);
+}
+
+static void bfusb_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct bfusb *bfusb = (struct bfusb *) skb->dev;
+
+	BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
+
+	atomic_dec(&bfusb->pending_tx);
+
+	if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
+		return;
+
+	if (!urb->status)
+		bfusb->hdev.stat.byte_tx += skb->len;
+	else
+		bfusb->hdev.stat.err_tx++;
+
+	read_lock(&bfusb->lock);
+
+	skb_unlink(skb);
+	skb_queue_tail(&bfusb->completed_q, skb);
+
+	bfusb_tx_wakeup(bfusb);
+
+	read_unlock(&bfusb->lock);
+}
+
+
+static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
+{
+	struct bfusb_scb *scb;
+	struct sk_buff *skb;
+	int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
+
+	BT_DBG("bfusb %p urb %p", bfusb, urb);
+
+	if (!urb && !(urb = usb_alloc_urb(0)))
+		return -ENOMEM;
+
+	if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	skb->dev = (void *) bfusb;
+
+	scb = (struct bfusb_scb *) skb->cb;
+	scb->urb = urb;
+
+	pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep);
+
+	FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, size,
+			bfusb_rx_complete, skb);
+
+	urb->transfer_flags = USB_QUEUE_BULK;
+
+	skb_queue_tail(&bfusb->pending_q, skb);
+
+	err = usb_submit_urb(urb);
+	if (err) {
+		BT_ERR("%s bulk rx submit failed urb %p err %d",
+					bfusb->hdev.name, urb, err);
+		skb_unlink(skb);
+		kfree_skb(skb);
+		usb_free_urb(urb);
+	}
+
+	return err;
+}
+
+static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len)
+{
+	BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len);
+
+	if (hdr & 0x10) {
+		BT_ERR("%s error in block", bfusb->hdev.name);
+		if (bfusb->reassembly)
+			kfree_skb(bfusb->reassembly);
+		bfusb->reassembly = NULL;
+		return -EIO;
+	}
+
+	if (hdr & 0x04) {
+		struct sk_buff *skb;
+		unsigned char pkt_type;
+		int pkt_len = 0;
+
+		if (bfusb->reassembly) {
+			BT_ERR("%s unexpected start block", bfusb->hdev.name);
+			kfree_skb(bfusb->reassembly);
+			bfusb->reassembly = NULL;
+		}
+
+		if (len < 1) {
+			BT_ERR("%s no packet type found", bfusb->hdev.name);
+			return -EPROTO;
+		}
+
+		pkt_type = *data++; len--;
+
+		switch (pkt_type) {
+		case HCI_EVENT_PKT:
+			if (len >= HCI_EVENT_HDR_SIZE) {
+				hci_event_hdr *hdr = (hci_event_hdr *) data;
+				pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+			} else {
+				BT_ERR("%s event block is too short", bfusb->hdev.name);
+				return -EILSEQ;
+			}
+			break;
+
+		case HCI_ACLDATA_PKT:
+			if (len >= HCI_ACL_HDR_SIZE) {
+				hci_acl_hdr *hdr = (hci_acl_hdr *) data;
+				pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
+			} else {
+				BT_ERR("%s data block is too short", bfusb->hdev.name);
+				return -EILSEQ;
+			}
+			break;
+
+		case HCI_SCODATA_PKT:
+			if (len >= HCI_SCO_HDR_SIZE) {
+				hci_sco_hdr *hdr = (hci_sco_hdr *) data;
+				pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
+			} else {
+				BT_ERR("%s audio block is too short", bfusb->hdev.name);
+				return -EILSEQ;
+			}
+			break;
+		}
+
+		skb = bluez_skb_alloc(pkt_len, GFP_ATOMIC);
+		if (!skb) {
+			BT_ERR("%s no memory for the packet", bfusb->hdev.name);
+			return -ENOMEM;
+		}
+
+		skb->dev = (void *) &bfusb->hdev;
+		skb->pkt_type = pkt_type;
+
+		bfusb->reassembly = skb;
+	} else {
+		if (!bfusb->reassembly) {
+			BT_ERR("%s unexpected continuation block", bfusb->hdev.name);
+			return -EIO;
+		}
+	}
+
+	if (len > 0)
+		memcpy(skb_put(bfusb->reassembly, len), data, len);
+
+	if (hdr & 0x08) {
+		hci_recv_frame(bfusb->reassembly);
+		bfusb->reassembly = NULL;
+	}
+
+	return 0;
+}
+
+static void bfusb_rx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct bfusb *bfusb = (struct bfusb *) skb->dev;
+	unsigned char *buf = urb->transfer_buffer;
+	int count = urb->actual_length;
+	int err, hdr, len;
+
+	BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
+
+	read_lock(&bfusb->lock);
+
+	if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
+		goto unlock;
+
+	if (urb->status || !count)
+		goto resubmit;
+
+	bfusb->hdev.stat.byte_rx += count;
+
+	skb_put(skb, count);
+
+	while (count) {
+		hdr = buf[0] | (buf[1] << 8);
+
+		if (hdr & 0x4000) {
+			len = 0;
+			count -= 2;
+			buf   += 2;
+		} else {
+			len = (buf[2] == 0) ? 256 : buf[2];
+			count -= 3;
+			buf   += 3;
+		}
+
+		if (count < len) {
+			BT_ERR("%s block extends over URB buffer ranges",
+					bfusb->hdev.name);
+		}
+
+		if ((hdr & 0xe1) == 0xc1)
+			bfusb_recv_block(bfusb, hdr, buf, len);
+
+		count -= len;
+		buf   += len;
+	}
+
+	skb_unlink(skb);
+	kfree_skb(skb);
+
+	bfusb_rx_submit(bfusb, urb);
+
+	read_unlock(&bfusb->lock);
+
+	return;
+
+resubmit:
+	urb->dev = bfusb->udev;
+
+	err = usb_submit_urb(urb);
+	if (err) {
+		BT_ERR("%s bulk resubmit failed urb %p err %d",
+					bfusb->hdev.name, urb, err);
+	}
+
+unlock:
+	read_unlock(&bfusb->lock);
+}
+
+
+static int bfusb_open(struct hci_dev *hdev)
+{
+	struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+	unsigned long flags;
+	int i, err;
+
+	BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+
+	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	MOD_INC_USE_COUNT;
+
+	write_lock_irqsave(&bfusb->lock, flags);
+
+	err = bfusb_rx_submit(bfusb, NULL);
+	if (!err) {
+		for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
+			bfusb_rx_submit(bfusb, NULL);
+	} else {
+		clear_bit(HCI_RUNNING, &hdev->flags);
+		MOD_DEC_USE_COUNT;
+	}
+
+	write_unlock_irqrestore(&bfusb->lock, flags);
+
+	return err;
+}
+
+static int bfusb_flush(struct hci_dev *hdev)
+{
+	struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+
+	BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+
+	skb_queue_purge(&bfusb->transmit_q);
+
+	return 0;
+}
+
+static int bfusb_close(struct hci_dev *hdev)
+{
+	struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+	unsigned long flags;
+
+	BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	write_lock_irqsave(&bfusb->lock, flags);
+
+	bfusb_unlink_urbs(bfusb);
+	bfusb_flush(hdev);
+
+	write_unlock_irqrestore(&bfusb->lock, flags);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static int bfusb_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct bfusb *bfusb;
+	struct sk_buff *nskb;
+	unsigned char buf[3];
+	int sent = 0, size, count;
+
+	BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len);
+
+	if (!hdev) {
+		BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	bfusb = (struct bfusb *) hdev->driver_data;
+
+	switch (skb->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
+
+	count = skb->len;
+
+	/* Max HCI frame size seems to be 1511 + 1 */
+	if (!(nskb = bluez_skb_alloc(count + 32, GFP_ATOMIC))) {
+		BT_ERR("Can't allocate memory for new packet");
+		return -ENOMEM;
+	}
+
+	nskb->dev = (void *) bfusb;
+
+	while (count) {
+		size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
+
+		buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
+		buf[1] = 0x00;
+		buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
+
+		memcpy(skb_put(nskb, 3), buf, 3);
+		memcpy(skb_put(nskb, size), skb->data + sent, size);
+
+		sent  += size;
+		count -= size;
+	}
+
+	/* Don't send frame with multiple size of bulk max packet */
+	if ((nskb->len % bfusb->bulk_pkt_size) == 0) {
+		buf[0] = 0xdd;
+		buf[1] = 0x00;
+		memcpy(skb_put(nskb, 2), buf, 2);
+	}
+
+	read_lock(&bfusb->lock);
+
+	skb_queue_tail(&bfusb->transmit_q, nskb);
+	bfusb_tx_wakeup(bfusb);
+
+	read_unlock(&bfusb->lock);
+
+	kfree_skb(skb);
+
+	return 0;
+}
+
+static void bfusb_destruct(struct hci_dev *hdev)
+{
+	struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+
+	BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+
+	kfree(bfusb);
+}
+
+static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count)
+{
+	unsigned char *buf;
+	int err, pipe, len, size, sent = 0;
+
+	BT_DBG("bfusb %p udev %p firmware %p count %d", bfusb, bfusb->udev, firmware, count);
+
+	BT_INFO("BlueFRITZ! USB loading firmware");
+
+	if (usb_set_configuration(bfusb->udev, 1) < 0) {
+		BT_ERR("Can't change to loading configuration");
+		return -EBUSY;
+	}
+
+	buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
+	if (!buf) {
+		BT_ERR("Can't allocate memory chunk for firmware");
+		return -ENOMEM;
+	}
+
+	pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+
+	while (count) {
+		size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
+
+		memcpy(buf, firmware + sent, size);
+
+		err = usb_bulk_msg(bfusb->udev, pipe, buf, size,
+					&len, BFUSB_BLOCK_TIMEOUT);
+
+		if (err || (len != size)) {
+			BT_ERR("Error in firmware loading");
+			goto error;
+		}
+
+		sent  += size;
+		count -= size;
+	}
+
+	if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0,
+				&len, BFUSB_BLOCK_TIMEOUT)) < 0) {
+		BT_ERR("Error in null packet request");
+		goto error;
+	}
+
+	if ((err = usb_set_configuration(bfusb->udev, 2)) < 0) {
+		BT_ERR("Can't change to running configuration");
+		goto error;
+	}
+
+	BT_INFO("BlueFRITZ! USB device ready");
+
+	kfree(buf);
+	return 0;
+
+error:
+	kfree(buf);
+
+	pipe = usb_sndctrlpipe(bfusb->udev, 0);
+
+	usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
+				0, 0, 0, NULL, 0, BFUSB_BLOCK_TIMEOUT);
+
+	return err;
+}
+
+static void *bfusb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
+{
+	const struct firmware *firmware;
+	char device[16];
+	struct usb_interface *iface;
+	struct usb_interface_descriptor *iface_desc;
+	struct usb_endpoint_descriptor *bulk_out_ep;
+	struct usb_endpoint_descriptor *bulk_in_ep;
+	struct hci_dev *hdev;
+	struct bfusb *bfusb;
+
+	BT_DBG("udev %p ifnum %d id %p", udev, ifnum, id);
+
+	/* Check number of endpoints */
+	iface = &udev->actconfig->interface[0];
+	iface_desc = &iface->altsetting[0];
+
+	if (iface_desc->bNumEndpoints < 2)
+		return NULL;
+
+	bulk_out_ep = &iface_desc->endpoint[0];
+	bulk_in_ep  = &iface_desc->endpoint[1];
+
+	if (!bulk_out_ep || !bulk_in_ep) {
+		BT_ERR("Bulk endpoints not found");
+		goto done;
+	}
+
+	/* Initialize control structure and load firmware */
+	if (!(bfusb = kmalloc(sizeof(struct bfusb), GFP_KERNEL))) {
+		BT_ERR("Can't allocate memory for control structure");
+		goto done;
+	}
+
+	memset(bfusb, 0, sizeof(struct bfusb));
+
+	bfusb->udev = udev;
+	bfusb->bulk_in_ep    = bulk_in_ep->bEndpointAddress;
+	bfusb->bulk_out_ep   = bulk_out_ep->bEndpointAddress;
+	bfusb->bulk_pkt_size = bulk_out_ep->wMaxPacketSize;
+
+	bfusb->lock = RW_LOCK_UNLOCKED;
+
+	bfusb->reassembly = NULL;
+
+	skb_queue_head_init(&bfusb->transmit_q);
+	skb_queue_head_init(&bfusb->pending_q);
+	skb_queue_head_init(&bfusb->completed_q);
+
+	snprintf(device, sizeof(device), "bfusb%3.3d%3.3d", udev->bus->busnum, udev->devnum);
+
+	if (request_firmware(&firmware, "bfubase.frm", device) < 0) {
+		BT_ERR("Firmware request failed");
+		goto error;
+	}
+
+	if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) {
+		BT_ERR("Firmware loading failed");
+		goto release;
+	}
+
+	release_firmware(firmware);
+
+	/* Initialize and register HCI device */
+	hdev = &bfusb->hdev;
+
+	hdev->type = HCI_USB;
+	hdev->driver_data = bfusb;
+
+	hdev->open     = bfusb_open;
+	hdev->close    = bfusb_close;
+	hdev->flush    = bfusb_flush;
+	hdev->send     = bfusb_send_frame;
+	hdev->destruct = bfusb_destruct;
+	hdev->ioctl    = bfusb_ioctl;
+
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device");
+		goto error;
+	}
+
+	return bfusb;
+
+release:
+	release_firmware(firmware);
+
+error:
+	kfree(bfusb);
+
+done:
+	return NULL;
+}
+
+static void bfusb_disconnect(struct usb_device *udev, void *ptr)
+{
+	struct bfusb *bfusb = (struct bfusb *) ptr;
+	struct hci_dev *hdev = &bfusb->hdev;
+
+	BT_DBG("udev %p ptr %p", udev, ptr);
+
+	if (!hdev)
+		return;
+
+	bfusb_close(hdev);
+
+	if (hci_unregister_dev(hdev) < 0)
+		BT_ERR("Can't unregister HCI device %s", hdev->name);
+}
+
+static struct usb_driver bfusb_driver = {
+	name:		"bfusb",
+	probe:		bfusb_probe,
+	disconnect:	bfusb_disconnect,
+	id_table:	bfusb_table,
+};
+
+static int __init bfusb_init(void)
+{
+	int err;
+
+	BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
+	BT_INFO("Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>");
+
+	if ((err = usb_register(&bfusb_driver)) < 0)
+		BT_ERR("Failed to register BlueFRITZ! USB driver");
+
+	return err;
+}
+
+static void __exit bfusb_cleanup(void)
+{
+	usb_deregister(&bfusb_driver);
+}
+
+module_init(bfusb_init);
+module_exit(bfusb_cleanup);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/drivers/bluetooth/bluecard_cs.c linux-2.4.18-mh15/drivers/bluetooth/bluecard_cs.c
--- linux-2.4.18/drivers/bluetooth/bluecard_cs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/bluecard_cs.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,1116 @@
+/*
+ *
+ *  Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
+ *
+ *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0x86bc;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueZ driver for the Anycom BlueCard (LSE039/LSE041)");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bluecard_info_t {
+	dev_link_t link;
+	dev_node_t node;
+
+	struct hci_dev hdev;
+
+	spinlock_t lock;		/* For serializing operations */
+	struct timer_list timer;	/* For LED control */
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+
+	unsigned char ctrl_reg;
+	unsigned long hw_state;		/* Status of the hardware and LED control */
+} bluecard_info_t;
+
+
+void bluecard_config(dev_link_t *link);
+void bluecard_release(u_long arg);
+int bluecard_event(event_t event, int priority, event_callback_args_t *args);
+
+static dev_info_t dev_info = "bluecard_cs";
+
+dev_link_t *bluecard_attach(void);
+void bluecard_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE  230400
+
+
+/* Hardware states */
+#define CARD_READY             1
+#define CARD_HAS_PCCARD_ID     4
+#define CARD_HAS_POWER_LED     5
+#define CARD_HAS_ACTIVITY_LED  6
+
+/* Transmit states  */
+#define XMIT_SENDING         1
+#define XMIT_WAKEUP          2
+#define XMIT_BUFFER_NUMBER   5	/* unset = buffer one, set = buffer two */
+#define XMIT_BUF_ONE_READY   6
+#define XMIT_BUF_TWO_READY   7
+#define XMIT_SENDING_READY   8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE   0
+#define RECV_WAIT_EVENT_HEADER  1
+#define RECV_WAIT_ACL_HEADER    2
+#define RECV_WAIT_SCO_HEADER    3
+#define RECV_WAIT_DATA          4
+
+/* Special packet types */
+#define PKT_BAUD_RATE_57600   0x80
+#define PKT_BAUD_RATE_115200  0x81
+#define PKT_BAUD_RATE_230400  0x82
+#define PKT_BAUD_RATE_460800  0x83
+
+
+/* These are the register offsets */
+#define REG_COMMAND     0x20
+#define REG_INTERRUPT   0x21
+#define REG_CONTROL     0x22
+#define REG_RX_CONTROL  0x24
+#define REG_CARD_RESET  0x30
+#define REG_LED_CTRL    0x30
+
+/* REG_COMMAND */
+#define REG_COMMAND_TX_BUF_ONE  0x01
+#define REG_COMMAND_TX_BUF_TWO  0x02
+#define REG_COMMAND_RX_BUF_ONE  0x04
+#define REG_COMMAND_RX_BUF_TWO  0x08
+#define REG_COMMAND_RX_WIN_ONE  0x00
+#define REG_COMMAND_RX_WIN_TWO  0x10
+
+/* REG_CONTROL */
+#define REG_CONTROL_BAUD_RATE_57600   0x00
+#define REG_CONTROL_BAUD_RATE_115200  0x01
+#define REG_CONTROL_BAUD_RATE_230400  0x02
+#define REG_CONTROL_BAUD_RATE_460800  0x03
+#define REG_CONTROL_RTS               0x04
+#define REG_CONTROL_BT_ON             0x08
+#define REG_CONTROL_BT_RESET          0x10
+#define REG_CONTROL_BT_RES_PU         0x20
+#define REG_CONTROL_INTERRUPT         0x40
+#define REG_CONTROL_CARD_RESET        0x80
+
+/* REG_RX_CONTROL */
+#define RTS_LEVEL_SHIFT_BITS  0x02
+
+
+
+/* ======================== LED handling routines ======================== */
+
+
+void bluecard_activity_led_timeout(u_long arg)
+{
+	bluecard_info_t *info = (bluecard_info_t *)arg;
+	unsigned int iobase = info->link.io.BasePort1;
+
+	if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+		/* Disable activity LED */
+		outb(0x08 | 0x20, iobase + 0x30);
+	} else {
+		/* Disable power LED */
+		outb(0x00, iobase + 0x30);
+	}
+}
+
+
+static void bluecard_enable_activity_led(bluecard_info_t *info)
+{
+	unsigned int iobase = info->link.io.BasePort1;
+
+	if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+		/* Enable activity LED */
+		outb(0x10 | 0x40, iobase + 0x30);
+
+		/* Stop the LED after HZ/4 */
+		mod_timer(&(info->timer), jiffies + HZ / 4);
+	} else {
+		/* Enable power LED */
+		outb(0x08 | 0x20, iobase + 0x30);
+
+		/* Stop the LED after HZ/2 */
+		mod_timer(&(info->timer), jiffies + HZ / 2);
+	}
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len)
+{
+	int i, actual;
+
+	actual = (len > 15) ? 15 : len;
+
+	outb_p(actual, iobase + offset);
+
+	for (i = 0; i < actual; i++)
+		outb_p(buf[i], iobase + offset + i + 1);
+
+	return actual;
+}
+
+
+static void bluecard_write_wakeup(bluecard_info_t *info)
+{
+	if (!info) {
+		printk(KERN_WARNING "bluecard_cs: Call of write_wakeup for unknown device.\n");
+		return;
+	}
+
+	if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+		return;
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+		return;
+	}
+
+	do {
+		register unsigned int iobase = info->link.io.BasePort1;
+		register unsigned int offset;
+		register unsigned char command;
+		register unsigned long ready_bit;
+		register struct sk_buff *skb;
+		register int len;
+
+		clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+		if (!(info->link.state & DEV_PRESENT))
+			return;
+
+		if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
+			if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state)))
+				break;
+			offset = 0x10;
+			command = REG_COMMAND_TX_BUF_TWO;
+			ready_bit = XMIT_BUF_TWO_READY;
+		} else {
+			if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state)))
+				break;
+			offset = 0x00;
+			command = REG_COMMAND_TX_BUF_ONE;
+			ready_bit = XMIT_BUF_ONE_READY;
+		}
+
+		if (!(skb = skb_dequeue(&(info->txq))))
+			break;
+
+		if (skb->pkt_type & 0x80) {
+			/* Disable RTS */
+			info->ctrl_reg |= REG_CONTROL_RTS;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+		}
+
+		/* Activate LED */
+		bluecard_enable_activity_led(info);
+
+		/* Send frame */
+		len = bluecard_write(iobase, offset, skb->data, skb->len);
+
+		/* Tell the FPGA to send the data */
+		outb_p(command, iobase + REG_COMMAND);
+
+		/* Mark the buffer as dirty */
+		clear_bit(ready_bit, &(info->tx_state));
+
+		if (skb->pkt_type & 0x80) {
+
+			wait_queue_head_t wait;
+			unsigned char baud_reg;
+
+			switch (skb->pkt_type) {
+			case PKT_BAUD_RATE_460800:
+				baud_reg = REG_CONTROL_BAUD_RATE_460800;
+				break;
+			case PKT_BAUD_RATE_230400:
+				baud_reg = REG_CONTROL_BAUD_RATE_230400;
+				break;
+			case PKT_BAUD_RATE_115200:
+				baud_reg = REG_CONTROL_BAUD_RATE_115200;
+				break;
+			case PKT_BAUD_RATE_57600:
+				/* Fall through... */
+			default:
+				baud_reg = REG_CONTROL_BAUD_RATE_57600;
+				break;
+			}
+
+			/* Wait until the command reaches the baseband */
+			init_waitqueue_head(&wait);
+			interruptible_sleep_on_timeout(&wait, HZ / 10);
+
+			/* Set baud on baseband */
+			info->ctrl_reg &= ~0x03;
+			info->ctrl_reg |= baud_reg;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+			/* Enable RTS */
+			info->ctrl_reg &= ~REG_CONTROL_RTS;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+			/* Wait before the next HCI packet can be send */
+			interruptible_sleep_on_timeout(&wait, HZ);
+
+		}
+
+		if (len == skb->len) {
+			kfree_skb(skb);
+		} else {
+			skb_pull(skb, len);
+			skb_queue_head(&(info->txq), skb);
+		}
+
+		info->hdev.stat.byte_tx += len;
+
+		/* Change buffer */
+		change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
+
+	} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+	clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size)
+{
+	int i, n, len;
+
+	outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
+
+	len = inb(iobase + offset);
+	n = 0;
+	i = 1;
+
+	while (n < len) {
+
+		if (i == 16) {
+			outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
+			i = 0;
+		}
+
+		buf[n] = inb(iobase + offset + i);
+
+		n++;
+		i++;
+
+	}
+
+	return len;
+}
+
+
+static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
+{
+	unsigned int iobase;
+	unsigned char buf[31];
+	int i, len;
+
+	if (!info) {
+		printk(KERN_WARNING "bluecard_cs: Call of receive for unknown device.\n");
+		return;
+	}
+
+	iobase = info->link.io.BasePort1;
+
+	if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+		bluecard_enable_activity_led(info);
+
+	len = bluecard_read(iobase, offset, buf, sizeof(buf));
+
+	for (i = 0; i < len; i++) {
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL) {
+			info->rx_state = RECV_WAIT_PACKET_TYPE;
+			info->rx_count = 0;
+			if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n");
+				return;
+			}
+		}
+
+		if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+			info->rx_skb->dev = (void *)&(info->hdev);
+			info->rx_skb->pkt_type = buf[i];
+
+			switch (info->rx_skb->pkt_type) {
+
+			case 0x00:
+				/* init packet */
+				if (offset != 0x00) {
+					set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+					set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+					set_bit(XMIT_SENDING_READY, &(info->tx_state));
+					bluecard_write_wakeup(info);
+				}
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			case HCI_EVENT_PKT:
+				info->rx_state = RECV_WAIT_EVENT_HEADER;
+				info->rx_count = HCI_EVENT_HDR_SIZE;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				info->rx_state = RECV_WAIT_ACL_HEADER;
+				info->rx_count = HCI_ACL_HDR_SIZE;
+				break;
+
+			case HCI_SCODATA_PKT:
+				info->rx_state = RECV_WAIT_SCO_HEADER;
+				info->rx_count = HCI_SCO_HDR_SIZE;
+				break;
+
+			default:
+				/* unknown packet */
+				printk(KERN_WARNING "bluecard_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
+				info->hdev.stat.err_rx++;
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			}
+
+		} else {
+
+			*skb_put(info->rx_skb, 1) = buf[i];
+			info->rx_count--;
+
+			if (info->rx_count == 0) {
+
+				int dlen;
+				hci_event_hdr *eh;
+				hci_acl_hdr *ah;
+				hci_sco_hdr *sh;
+
+				switch (info->rx_state) {
+
+				case RECV_WAIT_EVENT_HEADER:
+					eh = (hci_event_hdr *)(info->rx_skb->data);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = eh->plen;
+					break;
+
+				case RECV_WAIT_ACL_HEADER:
+					ah = (hci_acl_hdr *)(info->rx_skb->data);
+					dlen = __le16_to_cpu(ah->dlen);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = dlen;
+					break;
+
+				case RECV_WAIT_SCO_HEADER:
+					sh = (hci_sco_hdr *)(info->rx_skb->data);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = sh->dlen;
+					break;
+
+				case RECV_WAIT_DATA:
+					hci_recv_frame(info->rx_skb);
+					info->rx_skb = NULL;
+					break;
+
+				}
+
+			}
+
+		}
+
+
+	}
+
+	info->hdev.stat.byte_rx += len;
+}
+
+
+void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+{
+	bluecard_info_t *info = dev_inst;
+	unsigned int iobase;
+	unsigned char reg;
+
+	if (!info) {
+		printk(KERN_WARNING "bluecard_cs: Call of irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	if (!test_bit(CARD_READY, &(info->hw_state)))
+		return;
+
+	iobase = info->link.io.BasePort1;
+
+	spin_lock(&(info->lock));
+
+	/* Disable interrupt */
+	info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	reg = inb(iobase + REG_INTERRUPT);
+
+	if ((reg != 0x00) && (reg != 0xff)) {
+
+		if (reg & 0x04) {
+			bluecard_receive(info, 0x00);
+			outb(0x04, iobase + REG_INTERRUPT);
+			outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+		}
+
+		if (reg & 0x08) {
+			bluecard_receive(info, 0x10);
+			outb(0x08, iobase + REG_INTERRUPT);
+			outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+		}
+
+		if (reg & 0x01) {
+			set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+			outb(0x01, iobase + REG_INTERRUPT);
+			bluecard_write_wakeup(info);
+		}
+
+		if (reg & 0x02) {
+			set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+			outb(0x02, iobase + REG_INTERRUPT);
+			bluecard_write_wakeup(info);
+		}
+
+	}
+
+	/* Enable interrupt */
+	info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	spin_unlock(&(info->lock));
+}
+
+
+
+/* ======================== Device specific HCI commands ======================== */
+
+
+static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
+{
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+	struct sk_buff *skb;
+
+	/* Ericsson baud rate command */
+	unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
+
+	if (!(skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+		printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n");
+		return -1;
+	}
+
+	switch (baud) {
+	case 460800:
+		cmd[4] = 0x00;
+		skb->pkt_type = PKT_BAUD_RATE_460800;
+		break;
+	case 230400:
+		cmd[4] = 0x01;
+		skb->pkt_type = PKT_BAUD_RATE_230400;
+		break;
+	case 115200:
+		cmd[4] = 0x02;
+		skb->pkt_type = PKT_BAUD_RATE_115200;
+		break;
+	case 57600:
+		/* Fall through... */
+	default:
+		cmd[4] = 0x03;
+		skb->pkt_type = PKT_BAUD_RATE_57600;
+		break;
+	}
+
+	memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
+
+	skb_queue_tail(&(info->txq), skb);
+
+	bluecard_write_wakeup(info);
+
+	return 0;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bluecard_hci_flush(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int bluecard_hci_open(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+	unsigned int iobase = info->link.io.BasePort1;
+
+	bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
+
+	if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	/* Enable LED */
+	outb(0x08 | 0x20, iobase + 0x30);
+
+	return 0;
+}
+
+
+static int bluecard_hci_close(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+	unsigned int iobase = info->link.io.BasePort1;
+
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	bluecard_hci_flush(hdev);
+
+	/* Disable LED */
+	outb(0x00, iobase + 0x30);
+
+	return 0;
+}
+
+
+static int bluecard_hci_send_frame(struct sk_buff *skb)
+{
+	bluecard_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+	if (!hdev) {
+		printk(KERN_WARNING "bluecard_cs: Frame for unknown HCI device (hdev=NULL).");
+		return -ENODEV;
+	}
+
+	info = (bluecard_info_t *)(hdev->driver_data);
+
+	switch (skb->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
+	skb_queue_tail(&(info->txq), skb);
+
+	bluecard_write_wakeup(info);
+
+	return 0;
+}
+
+
+static void bluecard_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+int bluecard_open(bluecard_info_t *info)
+{
+	unsigned int iobase = info->link.io.BasePort1;
+	struct hci_dev *hdev;
+	unsigned char id;
+
+	spin_lock_init(&(info->lock));
+
+	init_timer(&(info->timer));
+	info->timer.function = &bluecard_activity_led_timeout;
+	info->timer.data = (u_long)info;
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_PACKET_TYPE;
+	info->rx_count = 0;
+	info->rx_skb = NULL;
+
+	id = inb(iobase + 0x30);
+
+	if ((id & 0x0f) == 0x02)
+		set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
+
+	if (id & 0x10)
+		set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
+
+	if (id & 0x20)
+		set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
+
+	/* Reset card */
+	info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Turn FPGA off */
+	outb(0x80, iobase + 0x30);
+
+	/* Wait some time */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(HZ / 100);
+
+	/* Turn FPGA on */
+	outb(0x00, iobase + 0x30);
+
+	/* Activate card */
+	info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Enable interrupt */
+	outb(0xff, iobase + REG_INTERRUPT);
+	info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Start the RX buffers */
+	outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+	outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+
+	/* Signal that the hardware is ready */
+	set_bit(CARD_READY, &(info->hw_state));
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	/* Control the point at which RTS is enabled */
+	outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
+
+	/* Timeout before it is safe to send the first HCI packet */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout((HZ * 5) / 4);		// or set it to 3/2
+
+
+	/* Initialize and register HCI device */
+
+	hdev = &(info->hdev);
+
+	hdev->type = HCI_PCCARD;
+	hdev->driver_data = info;
+
+	hdev->open = bluecard_hci_open;
+	hdev->close = bluecard_hci_close;
+	hdev->flush = bluecard_hci_flush;
+	hdev->send = bluecard_hci_send_frame;
+	hdev->destruct = bluecard_hci_destruct;
+	hdev->ioctl = bluecard_hci_ioctl;
+
+	if (hci_register_dev(hdev) < 0) {
+		printk(KERN_WARNING "bluecard_cs: Can't register HCI device %s.\n", hdev->name);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+int bluecard_close(bluecard_info_t *info)
+{
+	unsigned int iobase = info->link.io.BasePort1;
+	struct hci_dev *hdev = &(info->hdev);
+
+	if (info->link.state & DEV_CONFIG_PENDING)
+		return -ENODEV;
+
+	bluecard_hci_close(hdev);
+
+	clear_bit(CARD_READY, &(info->hw_state));
+
+	/* Reset card */
+	info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Turn FPGA off */
+	outb(0x80, iobase + 0x30);
+
+	if (hci_unregister_dev(hdev) < 0)
+		printk(KERN_WARNING "bluecard_cs: Can't unregister HCI device %s.\n", hdev->name);
+
+	return 0;
+}
+
+
+
+/* ======================== Card services ======================== */
+
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+	error_info_t err = { func, ret };
+
+	CardServices(ReportError, handle, &err);
+}
+
+
+dev_link_t *bluecard_attach(void)
+{
+	bluecard_info_t *info;
+	client_reg_t client_reg;
+	dev_link_t *link;
+	int i, ret;
+
+	/* Create new info device */
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return NULL;
+	memset(info, 0, sizeof(*info));
+
+	link = &info->link;
+	link->priv = info;
+
+	link->release.function = &bluecard_release;
+	link->release.data = (u_long)link;
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	link->io.NumPorts1 = 8;
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+	link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+
+	if (irq_list[0] == -1)
+		link->irq.IRQInfo2 = irq_mask;
+	else
+		for (i = 0; i < 4; i++)
+			link->irq.IRQInfo2 |= 1 << irq_list[i];
+
+	link->irq.Handler = bluecard_interrupt;
+	link->irq.Instance = info;
+
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.Vcc = 50;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+
+	/* Register with Card Services */
+	link->next = dev_list;
+	dev_list = link;
+	client_reg.dev_info = &dev_info;
+	client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+	client_reg.EventMask =
+		CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+		CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+		CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+	client_reg.event_handler = &bluecard_event;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+
+	ret = CardServices(RegisterClient, &link->handle, &client_reg);
+	if (ret != CS_SUCCESS) {
+		cs_error(link->handle, RegisterClient, ret);
+		bluecard_detach(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+
+void bluecard_detach(dev_link_t *link)
+{
+	bluecard_info_t *info = link->priv;
+	dev_link_t **linkp;
+	int ret;
+
+	/* Locate device structure */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+		if (*linkp == link)
+			break;
+
+	if (*linkp == NULL)
+		return;
+
+	del_timer(&link->release);
+	if (link->state & DEV_CONFIG)
+		bluecard_release((u_long)link);
+
+	if (link->handle) {
+		ret = CardServices(DeregisterClient, link->handle);
+		if (ret != CS_SUCCESS)
+			cs_error(link->handle, DeregisterClient, ret);
+	}
+
+	/* Unlink device structure, free bits */
+	*linkp = link->next;
+
+	kfree(info);
+}
+
+
+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+	int i;
+
+	i = CardServices(fn, handle, tuple);
+	if (i != CS_SUCCESS)
+		return CS_NO_MORE_ITEMS;
+
+	i = CardServices(GetTupleData, handle, tuple);
+	if (i != CS_SUCCESS)
+		return i;
+
+	return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+void bluecard_config(dev_link_t *link)
+{
+	client_handle_t handle = link->handle;
+	bluecard_info_t *info = link->priv;
+	tuple_t tuple;
+	u_short buf[256];
+	cisparse_t parse;
+	config_info_t config;
+	int i, n, last_ret, last_fn;
+
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleOffset = 0;
+	tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+
+	/* Get configuration register information */
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	last_ret = first_tuple(handle, &tuple, &parse);
+	if (last_ret != CS_SUCCESS) {
+		last_fn = ParseTuple;
+		goto cs_failed;
+	}
+	link->conf.ConfigBase = parse.config.base;
+	link->conf.Present = parse.config.rmask[0];
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+	i = CardServices(GetConfigurationInfo, handle, &config);
+	link->conf.Vcc = config.Vcc;
+
+	link->conf.ConfigIndex = 0x20;
+	link->io.NumPorts1 = 64;
+	link->io.IOAddrLines = 6;
+
+	for (n = 0; n < 0x400; n += 0x40) {
+		link->io.BasePort1 = n ^ 0x300;
+		i = CardServices(RequestIO, link->handle, &link->io);
+		if (i == CS_SUCCESS)
+			break;
+	}
+
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestIO, i);
+		goto failed;
+	}
+
+	i = CardServices(RequestIRQ, link->handle, &link->irq);
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestIRQ, i);
+		link->irq.AssignedIRQ = 0;
+	}
+
+	i = CardServices(RequestConfiguration, link->handle, &link->conf);
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestConfiguration, i);
+		goto failed;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	if (bluecard_open(info) != 0)
+		goto failed;
+
+	strcpy(info->node.dev_name, info->hdev.name);
+	link->dev = &info->node;
+	link->state &= ~DEV_CONFIG_PENDING;
+
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+
+failed:
+	bluecard_release((u_long)link);
+}
+
+
+void bluecard_release(u_long arg)
+{
+	dev_link_t *link = (dev_link_t *)arg;
+	bluecard_info_t *info = link->priv;
+
+	if (link->state & DEV_PRESENT)
+		bluecard_close(info);
+
+	MOD_DEC_USE_COUNT;
+
+	link->dev = NULL;
+
+	CardServices(ReleaseConfiguration, link->handle);
+	CardServices(ReleaseIO, link->handle, &link->io);
+	CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+	link->state &= ~DEV_CONFIG;
+}
+
+
+int bluecard_event(event_t event, int priority, event_callback_args_t *args)
+{
+	dev_link_t *link = args->client_data;
+	bluecard_info_t *info = link->priv;
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG) {
+			bluecard_close(info);
+			mod_timer(&link->release, jiffies + HZ / 20);
+		}
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+		bluecard_config(link);
+		break;
+	case CS_EVENT_PM_SUSPEND:
+		link->state |= DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		if (link->state & DEV_CONFIG)
+			CardServices(ReleaseConfiguration, link->handle);
+		break;
+	case CS_EVENT_PM_RESUME:
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		if (DEV_OK(link))
+			CardServices(RequestConfiguration, link->handle, &link->conf);
+		break;
+	}
+
+	return 0;
+}
+
+
+
+/* ======================== Module initialization ======================== */
+
+
+int __init init_bluecard_cs(void)
+{
+	servinfo_t serv;
+	int err;
+
+	CardServices(GetCardServicesInfo, &serv);
+	if (serv.Revision != CS_RELEASE_CODE) {
+		printk(KERN_NOTICE "bluecard_cs: Card Services release does not match!\n");
+		return -1;
+	}
+
+	err = register_pccard_driver(&dev_info, &bluecard_attach, &bluecard_detach);
+
+	return err;
+}
+
+
+void __exit exit_bluecard_cs(void)
+{
+	unregister_pccard_driver(&dev_info);
+
+	while (dev_list != NULL)
+		bluecard_detach(dev_list);
+}
+
+
+module_init(init_bluecard_cs);
+module_exit(exit_bluecard_cs);
+
+EXPORT_NO_SYMBOLS;
diff -urN linux-2.4.18/drivers/bluetooth/bt3c_cs.c linux-2.4.18-mh15/drivers/bluetooth/bt3c_cs.c
--- linux-2.4.18/drivers/bluetooth/bt3c_cs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/bt3c_cs.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,986 @@
+/*
+ *
+ *  Driver for the 3Com Bluetooth PCMCIA card
+ *
+ *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
+ *                           Jose Orlando Pereira <jop@di.uminho.pt>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/firmware.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xffff;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>, Jose Orlando Pereira <jop@di.uminho.pt>");
+MODULE_DESCRIPTION("BlueZ driver for the 3Com Bluetooth PCMCIA card");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bt3c_info_t {
+	dev_link_t link;
+	dev_node_t node;
+
+	struct hci_dev hdev;
+
+	spinlock_t lock;		/* For serializing operations */
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+} bt3c_info_t;
+
+
+void bt3c_config(dev_link_t *link);
+void bt3c_release(u_long arg);
+int bt3c_event(event_t event, int priority, event_callback_args_t *args);
+
+static dev_info_t dev_info = "bt3c_cs";
+
+dev_link_t *bt3c_attach(void);
+void bt3c_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+
+/* Transmit states  */
+#define XMIT_SENDING  1
+#define XMIT_WAKEUP   2
+#define XMIT_WAITING  8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE   0
+#define RECV_WAIT_EVENT_HEADER  1
+#define RECV_WAIT_ACL_HEADER    2
+#define RECV_WAIT_SCO_HEADER    3
+#define RECV_WAIT_DATA          4
+
+
+
+/* ======================== Special I/O functions ======================== */
+
+
+#define DATA_L   0
+#define DATA_H   1
+#define ADDR_L   2
+#define ADDR_H   3
+#define CONTROL  4
+
+
+inline void bt3c_address(unsigned int iobase, unsigned short addr)
+{
+	outb(addr & 0xff, iobase + ADDR_L);
+	outb((addr >> 8) & 0xff, iobase + ADDR_H);
+}
+
+
+inline void bt3c_put(unsigned int iobase, unsigned short value)
+{
+	outb(value & 0xff, iobase + DATA_L);
+	outb((value >> 8) & 0xff, iobase + DATA_H);
+}
+
+
+inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
+{
+	bt3c_address(iobase, addr);
+	bt3c_put(iobase, value);
+}
+
+
+inline unsigned short bt3c_get(unsigned int iobase)
+{
+	unsigned short value = inb(iobase + DATA_L);
+
+	value |= inb(iobase + DATA_H) << 8;
+
+	return value;
+}
+
+
+inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
+{
+	bt3c_address(iobase, addr);
+
+	return bt3c_get(iobase);
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+	int actual = 0;
+
+	bt3c_address(iobase, 0x7080);
+
+	/* Fill FIFO with current frame */
+	while (actual < len) {
+		/* Transmit next byte */
+		bt3c_put(iobase, buf[actual]);
+		actual++;
+	}
+
+	bt3c_io_write(iobase, 0x7005, actual);
+
+	return actual;
+}
+
+
+static void bt3c_write_wakeup(bt3c_info_t *info, int from)
+{
+	unsigned long flags;
+
+	if (!info) {
+		printk(KERN_WARNING "bt3c_cs: Call of write_wakeup for unknown device.\n");
+		return;
+	}
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
+		return;
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	do {
+		register unsigned int iobase = info->link.io.BasePort1;
+		register struct sk_buff *skb;
+		register int len;
+
+		if (!(info->link.state & DEV_PRESENT))
+			break;
+
+
+		if (!(skb = skb_dequeue(&(info->txq)))) {
+			clear_bit(XMIT_SENDING, &(info->tx_state));
+			break;
+		}
+
+		/* Send frame */
+		len = bt3c_write(iobase, 256, skb->data, skb->len);
+
+		if (len != skb->len) {
+			printk(KERN_WARNING "bt3c_cs: very strange\n");
+		}
+
+		kfree_skb(skb);
+
+		info->hdev.stat.byte_tx += len;
+
+	} while (0);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+}
+
+
+static void bt3c_receive(bt3c_info_t *info)
+{
+	unsigned int iobase;
+	int size = 0, avail;
+
+	if (!info) {
+		printk(KERN_WARNING "bt3c_cs: Call of receive for unknown device.\n");
+		return;
+	}
+
+	iobase = info->link.io.BasePort1;
+
+	avail = bt3c_read(iobase, 0x7006);
+	//printk("bt3c_cs: receiving %d bytes\n", avail);
+
+	bt3c_address(iobase, 0x7480);
+	while (size < avail) {
+		size++;
+		info->hdev.stat.byte_rx++;
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL) {
+			info->rx_state = RECV_WAIT_PACKET_TYPE;
+			info->rx_count = 0;
+			if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				printk(KERN_WARNING "bt3c_cs: Can't allocate mem for new packet.\n");
+				return;
+			}
+		}
+
+
+		if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+			info->rx_skb->dev = (void *)&(info->hdev);
+			info->rx_skb->pkt_type = inb(iobase + DATA_L);
+			inb(iobase + DATA_H);
+			//printk("bt3c: PACKET_TYPE=%02x\n", info->rx_skb->pkt_type);
+
+			switch (info->rx_skb->pkt_type) {
+
+			case HCI_EVENT_PKT:
+				info->rx_state = RECV_WAIT_EVENT_HEADER;
+				info->rx_count = HCI_EVENT_HDR_SIZE;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				info->rx_state = RECV_WAIT_ACL_HEADER;
+				info->rx_count = HCI_ACL_HDR_SIZE;
+				break;
+
+			case HCI_SCODATA_PKT:
+				info->rx_state = RECV_WAIT_SCO_HEADER;
+				info->rx_count = HCI_SCO_HDR_SIZE;
+				break;
+
+			default:
+				/* Unknown packet */
+				printk(KERN_WARNING "bt3c_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
+				info->hdev.stat.err_rx++;
+				clear_bit(HCI_RUNNING, &(info->hdev.flags));
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			}
+
+		} else {
+
+			__u8 x = inb(iobase + DATA_L);
+
+			*skb_put(info->rx_skb, 1) = x;
+			inb(iobase + DATA_H);
+			info->rx_count--;
+
+			if (info->rx_count == 0) {
+
+				int dlen;
+				hci_event_hdr *eh;
+				hci_acl_hdr *ah;
+				hci_sco_hdr *sh;
+
+				switch (info->rx_state) {
+
+				case RECV_WAIT_EVENT_HEADER:
+					eh = (hci_event_hdr *)(info->rx_skb->data);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = eh->plen;
+					break;
+
+				case RECV_WAIT_ACL_HEADER:
+					ah = (hci_acl_hdr *)(info->rx_skb->data);
+					dlen = __le16_to_cpu(ah->dlen);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = dlen;
+					break;
+
+				case RECV_WAIT_SCO_HEADER:
+					sh = (hci_sco_hdr *)(info->rx_skb->data);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = sh->dlen;
+					break;
+
+				case RECV_WAIT_DATA:
+					hci_recv_frame(info->rx_skb);
+					info->rx_skb = NULL;
+					break;
+
+				}
+
+			}
+
+		}
+
+	}
+
+	bt3c_io_write(iobase, 0x7006, 0x0000);
+}
+
+
+void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+{
+	bt3c_info_t *info = dev_inst;
+	unsigned int iobase;
+	int iir;
+
+	if (!info) {
+		printk(KERN_WARNING "bt3c_cs: Call of irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	iobase = info->link.io.BasePort1;
+
+	spin_lock(&(info->lock));
+
+	iir = inb(iobase + CONTROL);
+	if (iir & 0x80) {
+		int stat = bt3c_read(iobase, 0x7001);
+
+		if ((stat & 0xff) == 0x7f) {
+			printk(KERN_WARNING "bt3c_cs: STRANGE stat=%04x\n", stat);
+		} else if ((stat & 0xff) != 0xff) {
+			if (stat & 0x0020) {
+				int stat = bt3c_read(iobase, 0x7002) & 0x10;
+				printk(KERN_WARNING "bt3c_cs: antena %s\n", stat ? "OUT" : "IN");
+			}
+			if (stat & 0x0001)
+				bt3c_receive(info);
+			if (stat & 0x0002) {
+				//printk("bt3c_cs: ACK %04x\n", stat);
+				clear_bit(XMIT_SENDING, &(info->tx_state));
+				bt3c_write_wakeup(info, 1);
+			}
+
+			bt3c_io_write(iobase, 0x7001, 0x0000);
+
+			outb(iir, iobase + CONTROL);
+		}
+	}
+
+	spin_unlock(&(info->lock));
+}
+
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bt3c_hci_flush(struct hci_dev *hdev)
+{
+	bt3c_info_t *info = (bt3c_info_t *)(hdev->driver_data);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int bt3c_hci_open(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &(hdev->flags));
+
+	return 0;
+}
+
+
+static int bt3c_hci_close(struct hci_dev *hdev)
+{
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	bt3c_hci_flush(hdev);
+
+	return 0;
+}
+
+
+static int bt3c_hci_send_frame(struct sk_buff *skb)
+{
+	bt3c_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+	if (!hdev) {
+		printk(KERN_WARNING "bt3c_cs: Frame for unknown HCI device (hdev=NULL).");
+		return -ENODEV;
+	}
+
+	info = (bt3c_info_t *) (hdev->driver_data);
+
+	switch (skb->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
+	skb_queue_tail(&(info->txq), skb);
+
+	bt3c_write_wakeup(info, 0);
+
+	return 0;
+}
+
+
+static void bt3c_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int count)
+{
+	char *ptr = (char *) firmware;
+	char b[9];
+	unsigned int iobase, size, addr, fcs, tmp;
+	int i, err = 0;
+
+	iobase = info->link.io.BasePort1;
+
+	/* Reset */
+
+	bt3c_io_write(iobase, 0x8040, 0x0404);
+	bt3c_io_write(iobase, 0x8040, 0x0400);
+
+	udelay(1);
+
+	bt3c_io_write(iobase, 0x8040, 0x0404);
+
+	udelay(17);
+
+	/* Load */
+
+	while (count) {
+		if (ptr[0] != 'S') {
+			printk(KERN_WARNING "bt3c_cs: Bad address in firmware.\n");
+			err = -EFAULT;
+			goto error;
+		}
+
+		memset(b, 0, sizeof(b));
+		memcpy(b, ptr + 2, 2);
+		size = simple_strtol(b, NULL, 16);
+
+		memset(b, 0, sizeof(b));
+		memcpy(b, ptr + 4, 8);
+		addr = simple_strtol(b, NULL, 16);
+
+		memset(b, 0, sizeof(b));
+		memcpy(b, ptr + (size * 2) + 2, 2);
+		fcs = simple_strtol(b, NULL, 16);
+
+		memset(b, 0, sizeof(b));
+		for (tmp = 0, i = 0; i < size; i++) {
+			memcpy(b, ptr + (i * 2) + 2, 2);
+			tmp += simple_strtol(b, NULL, 16);
+		}
+
+		if (((tmp + fcs) & 0xff) != 0xff) {
+			printk(KERN_WARNING "bt3c_cs: Checksum error in firmware.\n");
+			err = -EILSEQ;
+			goto error;
+		}
+
+		if (ptr[1] == '3') {
+			bt3c_address(iobase, addr);
+
+			memset(b, 0, sizeof(b));
+			for (i = 0; i < (size - 4) / 2; i++) {
+				memcpy(b, ptr + (i * 4) + 12, 4);
+				tmp = simple_strtol(b, NULL, 16);
+				bt3c_put(iobase, tmp);
+			}
+		}
+
+		ptr   += (size * 2) + 6;
+		count -= (size * 2) + 6;
+	}
+
+	udelay(17);
+
+	/* Boot */
+
+	bt3c_address(iobase, 0x3000);
+	outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
+
+error:
+	udelay(17);
+
+	/* Clear */
+
+	bt3c_io_write(iobase, 0x7006, 0x0000);
+	bt3c_io_write(iobase, 0x7005, 0x0000);
+	bt3c_io_write(iobase, 0x7001, 0x0000);
+
+	return err;
+}
+
+
+int bt3c_open(bt3c_info_t *info)
+{
+	const struct firmware *firmware;
+	char device[16];
+	struct hci_dev *hdev;
+	int err;
+
+	spin_lock_init(&(info->lock));
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_PACKET_TYPE;
+	info->rx_count = 0;
+	info->rx_skb = NULL;
+
+	/* Load firmware */
+
+	snprintf(device, sizeof(device), "bt3c%4.4x", info->link.io.BasePort1);
+
+	err = request_firmware(&firmware, "BT3CPCC.bin", device);
+	if (err < 0) {
+		printk(KERN_WARNING "bt3c_cs: Firmware request failed.\n");
+		return err;
+	}
+
+	err = bt3c_load_firmware(info, firmware->data, firmware->size);
+
+	release_firmware(firmware);
+
+	if (err < 0) {
+		printk(KERN_WARNING "bt3c_cs: Firmware loading failed.\n");
+		return err;
+	}
+
+	/* Timeout before it is safe to send the first HCI packet */
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(HZ);
+
+
+	/* Initialize and register HCI device */
+
+	hdev = &(info->hdev);
+
+	hdev->type = HCI_PCCARD;
+	hdev->driver_data = info;
+
+	hdev->open = bt3c_hci_open;
+	hdev->close = bt3c_hci_close;
+	hdev->flush = bt3c_hci_flush;
+	hdev->send = bt3c_hci_send_frame;
+	hdev->destruct = bt3c_hci_destruct;
+	hdev->ioctl = bt3c_hci_ioctl;
+
+	if (hci_register_dev(hdev) < 0) {
+		printk(KERN_WARNING "bt3c_cs: Can't register HCI device %s.\n", hdev->name);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+int bt3c_close(bt3c_info_t *info)
+{
+	struct hci_dev *hdev = &(info->hdev);
+
+	if (info->link.state & DEV_CONFIG_PENDING)
+		return -ENODEV;
+
+	bt3c_hci_close(hdev);
+
+	if (hci_unregister_dev(hdev) < 0)
+		printk(KERN_WARNING "bt3c_cs: Can't unregister HCI device %s.\n", hdev->name);
+
+	return 0;
+}
+
+
+
+/* ======================== Card services ======================== */
+
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+	error_info_t err = { func, ret };
+
+	CardServices(ReportError, handle, &err);
+}
+
+
+dev_link_t *bt3c_attach(void)
+{
+	bt3c_info_t *info;
+	client_reg_t client_reg;
+	dev_link_t *link;
+	int i, ret;
+
+	/* Create new info device */
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return NULL;
+	memset(info, 0, sizeof(*info));
+
+	link = &info->link;
+	link->priv = info;
+
+	link->release.function = &bt3c_release;
+	link->release.data = (u_long)link;
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	link->io.NumPorts1 = 8;
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+	link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+
+	if (irq_list[0] == -1)
+		link->irq.IRQInfo2 = irq_mask;
+	else
+		for (i = 0; i < 4; i++)
+			link->irq.IRQInfo2 |= 1 << irq_list[i];
+
+	link->irq.Handler = bt3c_interrupt;
+	link->irq.Instance = info;
+
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.Vcc = 50;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+
+	/* Register with Card Services */
+	link->next = dev_list;
+	dev_list = link;
+	client_reg.dev_info = &dev_info;
+	client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+	client_reg.EventMask =
+	    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+	client_reg.event_handler = &bt3c_event;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+
+	ret = CardServices(RegisterClient, &link->handle, &client_reg);
+	if (ret != CS_SUCCESS) {
+		cs_error(link->handle, RegisterClient, ret);
+		bt3c_detach(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+
+void bt3c_detach(dev_link_t *link)
+{
+	bt3c_info_t *info = link->priv;
+	dev_link_t **linkp;
+	int ret;
+
+	/* Locate device structure */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+		if (*linkp == link)
+			break;
+
+	if (*linkp == NULL)
+		return;
+
+	del_timer(&link->release);
+
+	if (link->state & DEV_CONFIG)
+		bt3c_release((u_long)link);
+
+	if (link->handle) {
+		ret = CardServices(DeregisterClient, link->handle);
+		if (ret != CS_SUCCESS)
+			cs_error(link->handle, DeregisterClient, ret);
+	}
+
+	/* Unlink device structure, free bits */
+	*linkp = link->next;
+
+	kfree(info);
+}
+
+
+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+	int i;
+
+	i = CardServices(fn, handle, tuple);
+	if (i != CS_SUCCESS)
+		return CS_NO_MORE_ITEMS;
+
+	i = CardServices(GetTupleData, handle, tuple);
+	if (i != CS_SUCCESS)
+		return i;
+
+	return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+void bt3c_config(dev_link_t *link)
+{
+	static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+	client_handle_t handle = link->handle;
+	bt3c_info_t *info = link->priv;
+	tuple_t tuple;
+	u_short buf[256];
+	cisparse_t parse;
+	cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+	config_info_t config;
+	int i, j, try, last_ret, last_fn;
+
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleOffset = 0;
+	tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+
+	/* Get configuration register information */
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	last_ret = first_tuple(handle, &tuple, &parse);
+	if (last_ret != CS_SUCCESS) {
+		last_fn = ParseTuple;
+		goto cs_failed;
+	}
+	link->conf.ConfigBase = parse.config.base;
+	link->conf.Present = parse.config.rmask[0];
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+	i = CardServices(GetConfigurationInfo, handle, &config);
+	link->conf.Vcc = config.Vcc;
+
+	/* First pass: look for a config entry that looks normal. */
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleOffset = 0;
+	tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	/* Two tries: without IO aliases, then with aliases */
+	for (try = 0; try < 2; try++) {
+		i = first_tuple(handle, &tuple, &parse);
+		while (i != CS_NO_MORE_ITEMS) {
+			if (i != CS_SUCCESS)
+				goto next_entry;
+			if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+				link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+			if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) {
+				link->conf.ConfigIndex = cf->index;
+				link->io.BasePort1 = cf->io.win[0].base;
+				link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
+				i = CardServices(RequestIO, link->handle, &link->io);
+				if (i == CS_SUCCESS)
+					goto found_port;
+			}
+next_entry:
+			i = next_tuple(handle, &tuple, &parse);
+		}
+	}
+
+	/* Second pass: try to find an entry that isn't picky about
+	   its base address, then try to grab any standard serial port
+	   address, and finally try to get any free port. */
+	i = first_tuple(handle, &tuple, &parse);
+	while (i != CS_NO_MORE_ITEMS) {
+		if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
+			link->conf.ConfigIndex = cf->index;
+			for (j = 0; j < 5; j++) {
+				link->io.BasePort1 = base[j];
+				link->io.IOAddrLines = base[j] ? 16 : 3;
+				i = CardServices(RequestIO, link->handle, &link->io);
+				if (i == CS_SUCCESS)
+					goto found_port;
+			}
+		}
+		i = next_tuple(handle, &tuple, &parse);
+	}
+
+found_port:
+	if (i != CS_SUCCESS) {
+		printk(KERN_NOTICE "bt3c_cs: No usable port range found. Giving up.\n");
+		cs_error(link->handle, RequestIO, i);
+		goto failed;
+	}
+
+	i = CardServices(RequestIRQ, link->handle, &link->irq);
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestIRQ, i);
+		link->irq.AssignedIRQ = 0;
+	}
+
+	i = CardServices(RequestConfiguration, link->handle, &link->conf);
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestConfiguration, i);
+		goto failed;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	if (bt3c_open(info) != 0)
+		goto failed;
+
+	strcpy(info->node.dev_name, info->hdev.name);
+	link->dev = &info->node;
+	link->state &= ~DEV_CONFIG_PENDING;
+
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+
+failed:
+	bt3c_release((u_long)link);
+}
+
+
+void bt3c_release(u_long arg)
+{
+	dev_link_t *link = (dev_link_t *)arg;
+	bt3c_info_t *info = link->priv;
+
+	if (link->state & DEV_PRESENT)
+		bt3c_close(info);
+
+	MOD_DEC_USE_COUNT;
+
+	link->dev = NULL;
+
+	CardServices(ReleaseConfiguration, link->handle);
+	CardServices(ReleaseIO, link->handle, &link->io);
+	CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+	link->state &= ~DEV_CONFIG;
+}
+
+
+int bt3c_event(event_t event, int priority, event_callback_args_t *args)
+{
+	dev_link_t *link = args->client_data;
+	bt3c_info_t *info = link->priv;
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG) {
+			bt3c_close(info);
+			mod_timer(&link->release, jiffies + HZ / 20);
+		}
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+		bt3c_config(link);
+		break;
+	case CS_EVENT_PM_SUSPEND:
+		link->state |= DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		if (link->state & DEV_CONFIG)
+			CardServices(ReleaseConfiguration, link->handle);
+		break;
+	case CS_EVENT_PM_RESUME:
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		if (DEV_OK(link))
+			CardServices(RequestConfiguration, link->handle, &link->conf);
+		break;
+	}
+
+	return 0;
+}
+
+
+
+/* ======================== Module initialization ======================== */
+
+
+int __init init_bt3c_cs(void)
+{
+	servinfo_t serv;
+	int err;
+
+	CardServices(GetCardServicesInfo, &serv);
+	if (serv.Revision != CS_RELEASE_CODE) {
+		printk(KERN_NOTICE "bt3c_cs: Card Services release does not match!\n");
+		return -1;
+	}
+
+	err = register_pccard_driver(&dev_info, &bt3c_attach, &bt3c_detach);
+
+	return err;
+}
+
+
+void __exit exit_bt3c_cs(void)
+{
+	unregister_pccard_driver(&dev_info);
+
+	while (dev_list != NULL)
+		bt3c_detach(dev_list);
+}
+
+
+module_init(init_bt3c_cs);
+module_exit(exit_bt3c_cs);
+
+EXPORT_NO_SYMBOLS;
diff -urN linux-2.4.18/drivers/bluetooth/btuart_cs.c linux-2.4.18-mh15/drivers/bluetooth/btuart_cs.c
--- linux-2.4.18/drivers/bluetooth/btuart_cs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/btuart_cs.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,909 @@
+/*
+ *
+ *  Driver for Bluetooth PCMCIA cards with HCI UART interface
+ *
+ *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xffff;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueZ driver for Bluetooth PCMCIA cards with HCI UART interface");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct btuart_info_t {
+	dev_link_t link;
+	dev_node_t node;
+
+	struct hci_dev hdev;
+
+	spinlock_t lock;	/* For serializing operations */
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+} btuart_info_t;
+
+
+void btuart_config(dev_link_t *link);
+void btuart_release(u_long arg);
+int btuart_event(event_t event, int priority, event_callback_args_t *args);
+
+static dev_info_t dev_info = "btuart_cs";
+
+dev_link_t *btuart_attach(void);
+void btuart_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+
+/* Maximum baud rate */
+#define SPEED_MAX  115200
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE  115200
+
+
+/* Transmit states  */
+#define XMIT_SENDING	1
+#define XMIT_WAKEUP	2
+#define XMIT_WAITING	8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE	0
+#define RECV_WAIT_EVENT_HEADER	1
+#define RECV_WAIT_ACL_HEADER	2
+#define RECV_WAIT_SCO_HEADER	3
+#define RECV_WAIT_DATA		4
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+	int actual = 0;
+
+	/* Tx FIFO should be empty */
+	if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
+		return 0;
+
+	/* Fill FIFO with current frame */
+	while ((fifo_size-- > 0) && (actual < len)) {
+		/* Transmit next byte */
+		outb(buf[actual], iobase + UART_TX);
+		actual++;
+	}
+
+	return actual;
+}
+
+
+static void btuart_write_wakeup(btuart_info_t *info)
+{
+	if (!info) {
+		printk(KERN_WARNING "btuart_cs: Call of write_wakeup for unknown device.\n");
+		return;
+	}
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+		return;
+	}
+
+	do {
+		register unsigned int iobase = info->link.io.BasePort1;
+		register struct sk_buff *skb;
+		register int len;
+
+		clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+		if (!(info->link.state & DEV_PRESENT))
+			return;
+
+		if (!(skb = skb_dequeue(&(info->txq))))
+			break;
+
+		/* Send frame */
+		len = btuart_write(iobase, 16, skb->data, skb->len);
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+
+		if (len == skb->len) {
+			kfree_skb(skb);
+		} else {
+			skb_pull(skb, len);
+			skb_queue_head(&(info->txq), skb);
+		}
+
+		info->hdev.stat.byte_tx += len;
+
+	} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+	clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static void btuart_receive(btuart_info_t *info)
+{
+	unsigned int iobase;
+	int boguscount = 0;
+
+	if (!info) {
+		printk(KERN_WARNING "btuart_cs: Call of receive for unknown device.\n");
+		return;
+	}
+
+	iobase = info->link.io.BasePort1;
+
+	do {
+		info->hdev.stat.byte_rx++;
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL) {
+			info->rx_state = RECV_WAIT_PACKET_TYPE;
+			info->rx_count = 0;
+			if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				printk(KERN_WARNING "btuart_cs: Can't allocate mem for new packet.\n");
+				return;
+			}
+		}
+
+		if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+			info->rx_skb->dev = (void *)&(info->hdev);
+			info->rx_skb->pkt_type = inb(iobase + UART_RX);
+
+			switch (info->rx_skb->pkt_type) {
+
+			case HCI_EVENT_PKT:
+				info->rx_state = RECV_WAIT_EVENT_HEADER;
+				info->rx_count = HCI_EVENT_HDR_SIZE;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				info->rx_state = RECV_WAIT_ACL_HEADER;
+				info->rx_count = HCI_ACL_HDR_SIZE;
+				break;
+
+			case HCI_SCODATA_PKT:
+				info->rx_state = RECV_WAIT_SCO_HEADER;
+				info->rx_count = HCI_SCO_HDR_SIZE;
+				break;
+
+			default:
+				/* Unknown packet */
+				printk(KERN_WARNING "btuart_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
+				info->hdev.stat.err_rx++;
+				clear_bit(HCI_RUNNING, &(info->hdev.flags));
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			}
+
+		} else {
+
+			*skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
+			info->rx_count--;
+
+			if (info->rx_count == 0) {
+
+				int dlen;
+				hci_event_hdr *eh;
+				hci_acl_hdr *ah;
+				hci_sco_hdr *sh;
+
+
+				switch (info->rx_state) {
+
+				case RECV_WAIT_EVENT_HEADER:
+					eh = (hci_event_hdr *)(info->rx_skb->data);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = eh->plen;
+					break;
+
+				case RECV_WAIT_ACL_HEADER:
+					ah = (hci_acl_hdr *)(info->rx_skb->data);
+					dlen = __le16_to_cpu(ah->dlen);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = dlen;
+					break;
+
+				case RECV_WAIT_SCO_HEADER:
+					sh = (hci_sco_hdr *)(info->rx_skb->data);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = sh->dlen;
+					break;
+
+				case RECV_WAIT_DATA:
+					hci_recv_frame(info->rx_skb);
+					info->rx_skb = NULL;
+					break;
+
+				}
+
+			}
+
+		}
+
+		/* Make sure we don't stay here to long */
+		if (boguscount++ > 16)
+			break;
+
+	} while (inb(iobase + UART_LSR) & UART_LSR_DR);
+}
+
+
+void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+{
+	btuart_info_t *info = dev_inst;
+	unsigned int iobase;
+	int boguscount = 0;
+	int iir, lsr;
+
+	if (!info) {
+		printk(KERN_WARNING "btuart_cs: Call of irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	iobase = info->link.io.BasePort1;
+
+	spin_lock(&(info->lock));
+
+	iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+	while (iir) {
+
+		/* Clear interrupt */
+		lsr = inb(iobase + UART_LSR);
+
+		switch (iir) {
+		case UART_IIR_RLSI:
+			printk(KERN_NOTICE "btuart_cs: RLSI\n");
+			break;
+		case UART_IIR_RDI:
+			/* Receive interrupt */
+			btuart_receive(info);
+			break;
+		case UART_IIR_THRI:
+			if (lsr & UART_LSR_THRE) {
+				/* Transmitter ready for data */
+				btuart_write_wakeup(info);
+			}
+			break;
+		default:
+			printk(KERN_NOTICE "btuart_cs: Unhandled IIR=%#x\n", iir);
+			break;
+		}
+
+		/* Make sure we don't stay here to long */
+		if (boguscount++ > 100)
+			break;
+
+		iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+
+	}
+
+	spin_unlock(&(info->lock));
+}
+
+
+static void btuart_change_speed(btuart_info_t *info, unsigned int speed)
+{
+	unsigned long flags;
+	unsigned int iobase;
+	int fcr;		/* FIFO control reg */
+	int lcr;		/* Line control reg */
+	int divisor;
+
+	if (!info) {
+		printk(KERN_WARNING "btuart_cs: Call of change speed for unknown device.\n");
+		return;
+	}
+
+	iobase = info->link.io.BasePort1;
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	/* Turn off interrupts */
+	outb(0, iobase + UART_IER);
+
+	divisor = SPEED_MAX / speed;
+
+	fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
+
+	/* 
+	 * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
+	 * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
+	 * about this timeout since it will always be fast enough. 
+	 */
+
+	if (speed < 38400)
+		fcr |= UART_FCR_TRIGGER_1;
+	else
+		fcr |= UART_FCR_TRIGGER_14;
+
+	/* Bluetooth cards use 8N1 */
+	lcr = UART_LCR_WLEN8;
+
+	outb(UART_LCR_DLAB | lcr, iobase + UART_LCR);	/* Set DLAB */
+	outb(divisor & 0xff, iobase + UART_DLL);	/* Set speed */
+	outb(divisor >> 8, iobase + UART_DLM);
+	outb(lcr, iobase + UART_LCR);	/* Set 8N1  */
+	outb(fcr, iobase + UART_FCR);	/* Enable FIFO's */
+
+	/* Turn on interrups */
+	outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int btuart_hci_flush(struct hci_dev *hdev)
+{
+	btuart_info_t *info = (btuart_info_t *)(hdev->driver_data);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int btuart_hci_open(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &(hdev->flags));
+
+	return 0;
+}
+
+
+static int btuart_hci_close(struct hci_dev *hdev)
+{
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	btuart_hci_flush(hdev);
+
+	return 0;
+}
+
+
+static int btuart_hci_send_frame(struct sk_buff *skb)
+{
+	btuart_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+	if (!hdev) {
+		printk(KERN_WARNING "btuart_cs: Frame for unknown HCI device (hdev=NULL).");
+		return -ENODEV;
+	}
+
+	info = (btuart_info_t *)(hdev->driver_data);
+
+	switch (skb->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
+	skb_queue_tail(&(info->txq), skb);
+
+	btuart_write_wakeup(info);
+
+	return 0;
+}
+
+
+static void btuart_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+int btuart_open(btuart_info_t *info)
+{
+	unsigned long flags;
+	unsigned int iobase = info->link.io.BasePort1;
+	struct hci_dev *hdev;
+
+	spin_lock_init(&(info->lock));
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_PACKET_TYPE;
+	info->rx_count = 0;
+	info->rx_skb = NULL;
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	/* Reset UART */
+	outb(0, iobase + UART_MCR);
+
+	/* Turn off interrupts */
+	outb(0, iobase + UART_IER);
+
+	/* Initialize UART */
+	outb(UART_LCR_WLEN8, iobase + UART_LCR);	/* Reset DLAB */
+	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
+
+	/* Turn on interrupts */
+	// outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+
+	btuart_change_speed(info, DEFAULT_BAUD_RATE);
+
+	/* Timeout before it is safe to send the first HCI packet */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(HZ);
+
+
+	/* Initialize and register HCI device */
+
+	hdev = &(info->hdev);
+
+	hdev->type = HCI_PCCARD;
+	hdev->driver_data = info;
+
+	hdev->open = btuart_hci_open;
+	hdev->close = btuart_hci_close;
+	hdev->flush = btuart_hci_flush;
+	hdev->send = btuart_hci_send_frame;
+	hdev->destruct = btuart_hci_destruct;
+	hdev->ioctl = btuart_hci_ioctl;
+
+	if (hci_register_dev(hdev) < 0) {
+		printk(KERN_WARNING "btuart_cs: Can't register HCI device %s.\n", hdev->name);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+int btuart_close(btuart_info_t *info)
+{
+	unsigned long flags;
+	unsigned int iobase = info->link.io.BasePort1;
+	struct hci_dev *hdev = &(info->hdev);
+
+	if (info->link.state & DEV_CONFIG_PENDING)
+		return -ENODEV;
+
+	btuart_hci_close(hdev);
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	/* Reset UART */
+	outb(0, iobase + UART_MCR);
+
+	/* Turn off interrupts */
+	outb(0, iobase + UART_IER);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+
+	if (hci_unregister_dev(hdev) < 0)
+		printk(KERN_WARNING "btuart_cs: Can't unregister HCI device %s.\n", hdev->name);
+
+	return 0;
+}
+
+
+
+/* ======================== Card services ======================== */
+
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+	error_info_t err = { func, ret };
+
+	CardServices(ReportError, handle, &err);
+}
+
+
+dev_link_t *btuart_attach(void)
+{
+	btuart_info_t *info;
+	client_reg_t client_reg;
+	dev_link_t *link;
+	int i, ret;
+
+	/* Create new info device */
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return NULL;
+	memset(info, 0, sizeof(*info));
+
+	link = &info->link;
+	link->priv = info;
+
+	link->release.function = &btuart_release;
+	link->release.data = (u_long)link;
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	link->io.NumPorts1 = 8;
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+	link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+
+	if (irq_list[0] == -1)
+		link->irq.IRQInfo2 = irq_mask;
+	else
+		for (i = 0; i < 4; i++)
+			link->irq.IRQInfo2 |= 1 << irq_list[i];
+
+	link->irq.Handler = btuart_interrupt;
+	link->irq.Instance = info;
+
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.Vcc = 50;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+
+	/* Register with Card Services */
+	link->next = dev_list;
+	dev_list = link;
+	client_reg.dev_info = &dev_info;
+	client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+	client_reg.EventMask =
+		CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+		CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+		CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+	client_reg.event_handler = &btuart_event;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+
+	ret = CardServices(RegisterClient, &link->handle, &client_reg);
+	if (ret != CS_SUCCESS) {
+		cs_error(link->handle, RegisterClient, ret);
+		btuart_detach(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+
+void btuart_detach(dev_link_t *link)
+{
+	btuart_info_t *info = link->priv;
+	dev_link_t **linkp;
+	int ret;
+
+	/* Locate device structure */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+		if (*linkp == link)
+			break;
+
+	if (*linkp == NULL)
+		return;
+
+	del_timer(&link->release);
+	if (link->state & DEV_CONFIG)
+		btuart_release((u_long)link);
+
+	if (link->handle) {
+		ret = CardServices(DeregisterClient, link->handle);
+		if (ret != CS_SUCCESS)
+			cs_error(link->handle, DeregisterClient, ret);
+	}
+
+	/* Unlink device structure, free bits */
+	*linkp = link->next;
+
+	kfree(info);
+}
+
+
+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+	int i;
+
+	i = CardServices(fn, handle, tuple);
+	if (i != CS_SUCCESS)
+		return CS_NO_MORE_ITEMS;
+
+	i = CardServices(GetTupleData, handle, tuple);
+	if (i != CS_SUCCESS)
+		return i;
+
+	return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+void btuart_config(dev_link_t *link)
+{
+	static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+	client_handle_t handle = link->handle;
+	btuart_info_t *info = link->priv;
+	tuple_t tuple;
+	u_short buf[256];
+	cisparse_t parse;
+	cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+	config_info_t config;
+	int i, j, try, last_ret, last_fn;
+
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleOffset = 0;
+	tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+
+	/* Get configuration register information */
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	last_ret = first_tuple(handle, &tuple, &parse);
+	if (last_ret != CS_SUCCESS) {
+		last_fn = ParseTuple;
+		goto cs_failed;
+	}
+	link->conf.ConfigBase = parse.config.base;
+	link->conf.Present = parse.config.rmask[0];
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+	i = CardServices(GetConfigurationInfo, handle, &config);
+	link->conf.Vcc = config.Vcc;
+
+	/* First pass: look for a config entry that looks normal. */
+	tuple.TupleData = (cisdata_t *) buf;
+	tuple.TupleOffset = 0;
+	tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	/* Two tries: without IO aliases, then with aliases */
+	for (try = 0; try < 2; try++) {
+		i = first_tuple(handle, &tuple, &parse);
+		while (i != CS_NO_MORE_ITEMS) {
+			if (i != CS_SUCCESS)
+				goto next_entry;
+			if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+				link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+			if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) {
+				link->conf.ConfigIndex = cf->index;
+				link->io.BasePort1 = cf->io.win[0].base;
+				link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
+				i = CardServices(RequestIO, link->handle, &link->io);
+				if (i == CS_SUCCESS)
+					goto found_port;
+			}
+next_entry:
+			i = next_tuple(handle, &tuple, &parse);
+		}
+	}
+
+	/* Second pass: try to find an entry that isn't picky about
+	   its base address, then try to grab any standard serial port
+	   address, and finally try to get any free port. */
+	i = first_tuple(handle, &tuple, &parse);
+	while (i != CS_NO_MORE_ITEMS) {
+		if ((i == CS_SUCCESS) && (cf->io.nwin > 0)
+		    && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
+			link->conf.ConfigIndex = cf->index;
+			for (j = 0; j < 5; j++) {
+				link->io.BasePort1 = base[j];
+				link->io.IOAddrLines = base[j] ? 16 : 3;
+				i = CardServices(RequestIO, link->handle, &link->io);
+				if (i == CS_SUCCESS)
+					goto found_port;
+			}
+		}
+		i = next_tuple(handle, &tuple, &parse);
+	}
+
+found_port:
+	if (i != CS_SUCCESS) {
+		printk(KERN_NOTICE "btuart_cs: No usable port range found. Giving up.\n");
+		cs_error(link->handle, RequestIO, i);
+		goto failed;
+	}
+
+	i = CardServices(RequestIRQ, link->handle, &link->irq);
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestIRQ, i);
+		link->irq.AssignedIRQ = 0;
+	}
+
+	i = CardServices(RequestConfiguration, link->handle, &link->conf);
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestConfiguration, i);
+		goto failed;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	if (btuart_open(info) != 0)
+		goto failed;
+
+	strcpy(info->node.dev_name, info->hdev.name);
+	link->dev = &info->node;
+	link->state &= ~DEV_CONFIG_PENDING;
+
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+
+failed:
+	btuart_release((u_long) link);
+}
+
+
+void btuart_release(u_long arg)
+{
+	dev_link_t *link = (dev_link_t *)arg;
+	btuart_info_t *info = link->priv;
+
+	if (link->state & DEV_PRESENT)
+		btuart_close(info);
+
+	MOD_DEC_USE_COUNT;
+
+	link->dev = NULL;
+
+	CardServices(ReleaseConfiguration, link->handle);
+	CardServices(ReleaseIO, link->handle, &link->io);
+	CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+	link->state &= ~DEV_CONFIG;
+}
+
+
+int btuart_event(event_t event, int priority, event_callback_args_t *args)
+{
+	dev_link_t *link = args->client_data;
+	btuart_info_t *info = link->priv;
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG) {
+			btuart_close(info);
+			mod_timer(&link->release, jiffies + HZ / 20);
+		}
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+		btuart_config(link);
+		break;
+	case CS_EVENT_PM_SUSPEND:
+		link->state |= DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		if (link->state & DEV_CONFIG)
+			CardServices(ReleaseConfiguration, link->handle);
+		break;
+	case CS_EVENT_PM_RESUME:
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		if (DEV_OK(link))
+			CardServices(RequestConfiguration, link->handle, &link->conf);
+		break;
+	}
+
+	return 0;
+}
+
+
+
+/* ======================== Module initialization ======================== */
+
+
+int __init init_btuart_cs(void)
+{
+	servinfo_t serv;
+	int err;
+
+	CardServices(GetCardServicesInfo, &serv);
+	if (serv.Revision != CS_RELEASE_CODE) {
+		printk(KERN_NOTICE "btuart_cs: Card Services release does not match!\n");
+		return -1;
+	}
+
+	err = register_pccard_driver(&dev_info, &btuart_attach, &btuart_detach);
+
+	return err;
+}
+
+
+void __exit exit_btuart_cs(void)
+{
+	unregister_pccard_driver(&dev_info);
+
+	while (dev_list != NULL)
+		btuart_detach(dev_list);
+}
+
+
+module_init(init_btuart_cs);
+module_exit(exit_btuart_cs);
+
+EXPORT_NO_SYMBOLS;
diff -urN linux-2.4.18/drivers/bluetooth/Config.in linux-2.4.18-mh15/drivers/bluetooth/Config.in
--- linux-2.4.18/drivers/bluetooth/Config.in	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/drivers/bluetooth/Config.in	2004-08-01 16:26:23.000000000 +0200
@@ -1,8 +1,33 @@
+#
+# Bluetooth HCI device drivers configuration
+#
+
 mainmenu_option next_comment
 comment 'Bluetooth device drivers'
 
 dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB
+if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then
+   bool '  SCO (voice) support'  CONFIG_BLUEZ_HCIUSB_SCO
+fi
+
 dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
-dep_tristate 'HCI VHCI virtual HCI device driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ
+if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then
+   bool '  UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4
+   bool '  BCSP protocol support' CONFIG_BLUEZ_HCIUART_BCSP
+   dep_bool '  Transmit CRC with every BCSP packet' CONFIG_BLUEZ_HCIUART_BCSP_TXCRC $CONFIG_BLUEZ_HCIUART_BCSP
+fi
+
+dep_tristate 'HCI BlueFRITZ! USB driver' CONFIG_BLUEZ_HCIBFUSB $CONFIG_BLUEZ $CONFIG_USB
+
+dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ
+
+dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ
+
+dep_tristate 'HCI BlueCard (PC Card) driver' CONFIG_BLUEZ_HCIBLUECARD $CONFIG_PCMCIA $CONFIG_BLUEZ
+
+dep_tristate 'HCI UART (PC Card) driver' CONFIG_BLUEZ_HCIBTUART $CONFIG_PCMCIA $CONFIG_BLUEZ
+
+dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ
 
 endmenu
+
diff -urN linux-2.4.18/drivers/bluetooth/dtl1_cs.c linux-2.4.18-mh15/drivers/bluetooth/dtl1_cs.c
--- linux-2.4.18/drivers/bluetooth/dtl1_cs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/dtl1_cs.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,861 @@
+/*
+ *
+ *  A driver for Nokia Connectivity Card DTL-1 devices
+ *
+ *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xffff;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueZ driver for Nokia Connectivity Card DTL-1");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct dtl1_info_t {
+	dev_link_t link;
+	dev_node_t node;
+
+	struct hci_dev hdev;
+
+	spinlock_t lock;		/* For serializing operations */
+
+	unsigned long flowmask;		/* HCI flow mask */
+	int ri_latch;
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+} dtl1_info_t;
+
+
+void dtl1_config(dev_link_t *link);
+void dtl1_release(u_long arg);
+int dtl1_event(event_t event, int priority, event_callback_args_t *args);
+
+static dev_info_t dev_info = "dtl1_cs";
+
+dev_link_t *dtl1_attach(void);
+void dtl1_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+
+/* Transmit states  */
+#define XMIT_SENDING  1
+#define XMIT_WAKEUP   2
+#define XMIT_WAITING  8
+
+/* Receiver States */
+#define RECV_WAIT_NSH   0
+#define RECV_WAIT_DATA  1
+
+
+typedef struct {
+	u8 type;
+	u8 zero;
+	u16 len;
+} __attribute__ ((packed)) nsh_t;	/* Nokia Specific Header */
+
+#define NSHL  4				/* Nokia Specific Header Length */
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+	int actual = 0;
+
+	/* Tx FIFO should be empty */
+	if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
+		return 0;
+
+	/* Fill FIFO with current frame */
+	while ((fifo_size-- > 0) && (actual < len)) {
+		/* Transmit next byte */
+		outb(buf[actual], iobase + UART_TX);
+		actual++;
+	}
+
+	return actual;
+}
+
+
+static void dtl1_write_wakeup(dtl1_info_t *info)
+{
+	if (!info) {
+		printk(KERN_WARNING "dtl1_cs: Call of write_wakeup for unknown device.\n");
+		return;
+	}
+
+	if (test_bit(XMIT_WAITING, &(info->tx_state))) {
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+		return;
+	}
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+		return;
+	}
+
+	do {
+		register unsigned int iobase = info->link.io.BasePort1;
+		register struct sk_buff *skb;
+		register int len;
+
+		clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+		if (!(info->link.state & DEV_PRESENT))
+			return;
+
+		if (!(skb = skb_dequeue(&(info->txq))))
+			break;
+
+		/* Send frame */
+		len = dtl1_write(iobase, 32, skb->data, skb->len);
+
+		if (len == skb->len) {
+			set_bit(XMIT_WAITING, &(info->tx_state));
+			kfree_skb(skb);
+		} else {
+			skb_pull(skb, len);
+			skb_queue_head(&(info->txq), skb);
+		}
+
+		info->hdev.stat.byte_tx += len;
+
+	} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+	clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb)
+{
+	u8 flowmask = *(u8 *)skb->data;
+	int i;
+
+	printk(KERN_INFO "dtl1_cs: Nokia control data = ");
+	for (i = 0; i < skb->len; i++) {
+		printk("%02x ", skb->data[i]);
+	}
+	printk("\n");
+
+	/* transition to active state */
+	if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
+		clear_bit(XMIT_WAITING, &(info->tx_state));
+		dtl1_write_wakeup(info);
+	}
+
+	info->flowmask = flowmask;
+
+	kfree_skb(skb);
+}
+
+
+static void dtl1_receive(dtl1_info_t *info)
+{
+	unsigned int iobase;
+	nsh_t *nsh;
+	int boguscount = 0;
+
+	if (!info) {
+		printk(KERN_WARNING "dtl1_cs: Call of receive for unknown device.\n");
+		return;
+	}
+
+	iobase = info->link.io.BasePort1;
+
+	do {
+		info->hdev.stat.byte_rx++;
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL)
+			if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				printk(KERN_WARNING "dtl1_cs: Can't allocate mem for new packet.\n");
+				info->rx_state = RECV_WAIT_NSH;
+				info->rx_count = NSHL;
+				return;
+			}
+
+		*skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
+		nsh = (nsh_t *)info->rx_skb->data;
+
+		info->rx_count--;
+
+		if (info->rx_count == 0) {
+
+			switch (info->rx_state) {
+			case RECV_WAIT_NSH:
+				info->rx_state = RECV_WAIT_DATA;
+				info->rx_count = nsh->len + (nsh->len & 0x0001);
+				break;
+			case RECV_WAIT_DATA:
+				info->rx_skb->pkt_type = nsh->type;
+
+				/* remove PAD byte if it exists */
+				if (nsh->len & 0x0001) {
+					info->rx_skb->tail--;
+					info->rx_skb->len--;
+				}
+
+				/* remove NSH */
+				skb_pull(info->rx_skb, NSHL);
+
+				switch (info->rx_skb->pkt_type) {
+				case 0x80:
+					/* control data for the Nokia Card */
+					dtl1_control(info, info->rx_skb);
+					break;
+				case 0x82:
+				case 0x83:
+				case 0x84:
+					/* send frame to the HCI layer */
+					info->rx_skb->dev = (void *)&(info->hdev);
+					info->rx_skb->pkt_type &= 0x0f;
+					hci_recv_frame(info->rx_skb);
+					break;
+				default:
+					/* unknown packet */
+					printk(KERN_WARNING "dtl1_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
+					kfree_skb(info->rx_skb);
+					break;
+				}
+
+				info->rx_state = RECV_WAIT_NSH;
+				info->rx_count = NSHL;
+				info->rx_skb = NULL;
+				break;
+			}
+
+		}
+
+		/* Make sure we don't stay here to long */
+		if (boguscount++ > 32)
+			break;
+
+	} while (inb(iobase + UART_LSR) & UART_LSR_DR);
+}
+
+
+void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+{
+	dtl1_info_t *info = dev_inst;
+	unsigned int iobase;
+	unsigned char msr;
+	int boguscount = 0;
+	int iir, lsr;
+
+	if (!info) {
+		printk(KERN_WARNING "dtl1_cs: Call of irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	iobase = info->link.io.BasePort1;
+
+	spin_lock(&(info->lock));
+
+	iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+	while (iir) {
+
+		/* Clear interrupt */
+		lsr = inb(iobase + UART_LSR);
+
+		switch (iir) {
+		case UART_IIR_RLSI:
+			printk(KERN_NOTICE "dtl1_cs: RLSI\n");
+			break;
+		case UART_IIR_RDI:
+			/* Receive interrupt */
+			dtl1_receive(info);
+			break;
+		case UART_IIR_THRI:
+			if (lsr & UART_LSR_THRE) {
+				/* Transmitter ready for data */
+				dtl1_write_wakeup(info);
+			}
+			break;
+		default:
+			printk(KERN_NOTICE "dtl1_cs: Unhandled IIR=%#x\n", iir);
+			break;
+		}
+
+		/* Make sure we don't stay here to long */
+		if (boguscount++ > 100)
+			break;
+
+		iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+
+	}
+
+	msr = inb(iobase + UART_MSR);
+
+	if (info->ri_latch ^ (msr & UART_MSR_RI)) {
+		info->ri_latch = msr & UART_MSR_RI;
+		clear_bit(XMIT_WAITING, &(info->tx_state));
+		dtl1_write_wakeup(info);
+	}
+
+	spin_unlock(&(info->lock));
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int dtl1_hci_open(struct hci_dev *hdev)
+{
+	set_bit(HCI_RUNNING, &(hdev->flags));
+
+	return 0;
+}
+
+
+static int dtl1_hci_flush(struct hci_dev *hdev)
+{
+	dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int dtl1_hci_close(struct hci_dev *hdev)
+{
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	dtl1_hci_flush(hdev);
+
+	return 0;
+}
+
+
+static int dtl1_hci_send_frame(struct sk_buff *skb)
+{
+	dtl1_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+	struct sk_buff *s;
+	nsh_t nsh;
+
+	if (!hdev) {
+		printk(KERN_WARNING "dtl1_cs: Frame for unknown HCI device (hdev=NULL).");
+		return -ENODEV;
+	}
+
+	info = (dtl1_info_t *)(hdev->driver_data);
+
+	switch (skb->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		nsh.type = 0x81;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		nsh.type = 0x82;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		nsh.type = 0x83;
+		break;
+	};
+
+	nsh.zero = 0;
+	nsh.len = skb->len;
+
+	s = bluez_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
+	skb_reserve(s, NSHL);
+	memcpy(skb_put(s, skb->len), skb->data, skb->len);
+	if (skb->len & 0x0001)
+		*skb_put(s, 1) = 0;	/* PAD */
+
+	/* Prepend skb with Nokia frame header and queue */
+	memcpy(skb_push(s, NSHL), &nsh, NSHL);
+	skb_queue_tail(&(info->txq), s);
+
+	dtl1_write_wakeup(info);
+
+	kfree_skb(skb);
+
+	return 0;
+}
+
+
+static void dtl1_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd,  unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+int dtl1_open(dtl1_info_t *info)
+{
+	unsigned long flags;
+	unsigned int iobase = info->link.io.BasePort1;
+	struct hci_dev *hdev;
+
+	spin_lock_init(&(info->lock));
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_NSH;
+	info->rx_count = NSHL;
+	info->rx_skb = NULL;
+
+	set_bit(XMIT_WAITING, &(info->tx_state));
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	/* Reset UART */
+	outb(0, iobase + UART_MCR);
+
+	/* Turn off interrupts */
+	outb(0, iobase + UART_IER);
+
+	/* Initialize UART */
+	outb(UART_LCR_WLEN8, iobase + UART_LCR);	/* Reset DLAB */
+	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
+
+	info->ri_latch = inb(info->link.io.BasePort1 + UART_MSR) & UART_MSR_RI;
+
+	/* Turn on interrupts */
+	outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+
+	/* Timeout before it is safe to send the first HCI packet */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(HZ * 2);
+
+
+	/* Initialize and register HCI device */
+
+	hdev = &(info->hdev);
+
+	hdev->type = HCI_PCCARD;
+	hdev->driver_data = info;
+
+	hdev->open = dtl1_hci_open;
+	hdev->close = dtl1_hci_close;
+	hdev->flush = dtl1_hci_flush;
+	hdev->send = dtl1_hci_send_frame;
+	hdev->destruct = dtl1_hci_destruct;
+	hdev->ioctl = dtl1_hci_ioctl;
+
+	if (hci_register_dev(hdev) < 0) {
+		printk(KERN_WARNING "dtl1_cs: Can't register HCI device %s.\n", hdev->name);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+int dtl1_close(dtl1_info_t *info)
+{
+	unsigned long flags;
+	unsigned int iobase = info->link.io.BasePort1;
+	struct hci_dev *hdev = &(info->hdev);
+
+	if (info->link.state & DEV_CONFIG_PENDING)
+		return -ENODEV;
+
+	dtl1_hci_close(hdev);
+
+	spin_lock_irqsave(&(info->lock), flags);
+
+	/* Reset UART */
+	outb(0, iobase + UART_MCR);
+
+	/* Turn off interrupts */
+	outb(0, iobase + UART_IER);
+
+	spin_unlock_irqrestore(&(info->lock), flags);
+
+	if (hci_unregister_dev(hdev) < 0)
+		printk(KERN_WARNING "dtl1_cs: Can't unregister HCI device %s.\n", hdev->name);
+
+	return 0;
+}
+
+
+
+/* ======================== Card services ======================== */
+
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+	error_info_t err = { func, ret };
+
+	CardServices(ReportError, handle, &err);
+}
+
+
+dev_link_t *dtl1_attach(void)
+{
+	dtl1_info_t *info;
+	client_reg_t client_reg;
+	dev_link_t *link;
+	int i, ret;
+
+	/* Create new info device */
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return NULL;
+	memset(info, 0, sizeof(*info));
+
+	link = &info->link;
+	link->priv = info;
+
+	link->release.function = &dtl1_release;
+	link->release.data = (u_long)link;
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	link->io.NumPorts1 = 8;
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+	link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+
+	if (irq_list[0] == -1)
+		link->irq.IRQInfo2 = irq_mask;
+	else
+		for (i = 0; i < 4; i++)
+			link->irq.IRQInfo2 |= 1 << irq_list[i];
+
+	link->irq.Handler = dtl1_interrupt;
+	link->irq.Instance = info;
+
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.Vcc = 50;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+
+	/* Register with Card Services */
+	link->next = dev_list;
+	dev_list = link;
+	client_reg.dev_info = &dev_info;
+	client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+	client_reg.EventMask =
+		CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+		CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+		CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+	client_reg.event_handler = &dtl1_event;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+
+	ret = CardServices(RegisterClient, &link->handle, &client_reg);
+	if (ret != CS_SUCCESS) {
+		cs_error(link->handle, RegisterClient, ret);
+		dtl1_detach(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+
+void dtl1_detach(dev_link_t *link)
+{
+	dtl1_info_t *info = link->priv;
+	dev_link_t **linkp;
+	int ret;
+
+	/* Locate device structure */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+		if (*linkp == link)
+			break;
+
+	if (*linkp == NULL)
+		return;
+
+	del_timer(&link->release);
+	if (link->state & DEV_CONFIG)
+		dtl1_release((u_long)link);
+
+	if (link->handle) {
+		ret = CardServices(DeregisterClient, link->handle);
+		if (ret != CS_SUCCESS)
+			cs_error(link->handle, DeregisterClient, ret);
+	}
+
+	/* Unlink device structure, free bits */
+	*linkp = link->next;
+
+	kfree(info);
+}
+
+
+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+	int i;
+
+	i = CardServices(fn, handle, tuple);
+	if (i != CS_SUCCESS)
+		return CS_NO_MORE_ITEMS;
+
+	i = CardServices(GetTupleData, handle, tuple);
+	if (i != CS_SUCCESS)
+		return i;
+
+	return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+void dtl1_config(dev_link_t *link)
+{
+	client_handle_t handle = link->handle;
+	dtl1_info_t *info = link->priv;
+	tuple_t tuple;
+	u_short buf[256];
+	cisparse_t parse;
+	cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+	config_info_t config;
+	int i, last_ret, last_fn;
+
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleOffset = 0;
+	tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+
+	/* Get configuration register information */
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	last_ret = first_tuple(handle, &tuple, &parse);
+	if (last_ret != CS_SUCCESS) {
+		last_fn = ParseTuple;
+		goto cs_failed;
+	}
+	link->conf.ConfigBase = parse.config.base;
+	link->conf.Present = parse.config.rmask[0];
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+	i = CardServices(GetConfigurationInfo, handle, &config);
+	link->conf.Vcc = config.Vcc;
+
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleOffset = 0;
+	tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+
+	/* Look for a generic full-sized window */
+	link->io.NumPorts1 = 8;
+	i = first_tuple(handle, &tuple, &parse);
+	while (i != CS_NO_MORE_ITEMS) {
+		if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
+			link->conf.ConfigIndex = cf->index;
+			link->io.BasePort1 = cf->io.win[0].base;
+			link->io.NumPorts1 = cf->io.win[0].len;	/*yo */
+			link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
+			i = CardServices(RequestIO, link->handle, &link->io);
+			if (i == CS_SUCCESS)
+				break;
+		}
+		i = next_tuple(handle, &tuple, &parse);
+	}
+
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestIO, i);
+		goto failed;
+	}
+
+	i = CardServices(RequestIRQ, link->handle, &link->irq);
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestIRQ, i);
+		link->irq.AssignedIRQ = 0;
+	}
+
+	i = CardServices(RequestConfiguration, link->handle, &link->conf);
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestConfiguration, i);
+		goto failed;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	if (dtl1_open(info) != 0)
+		goto failed;
+
+	strcpy(info->node.dev_name, info->hdev.name);
+	link->dev = &info->node;
+	link->state &= ~DEV_CONFIG_PENDING;
+
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+
+failed:
+	dtl1_release((u_long)link);
+}
+
+
+void dtl1_release(u_long arg)
+{
+	dev_link_t *link = (dev_link_t *)arg;
+	dtl1_info_t *info = link->priv;
+
+	if (link->state & DEV_PRESENT)
+		dtl1_close(info);
+
+	MOD_DEC_USE_COUNT;
+
+	link->dev = NULL;
+
+	CardServices(ReleaseConfiguration, link->handle);
+	CardServices(ReleaseIO, link->handle, &link->io);
+	CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+	link->state &= ~DEV_CONFIG;
+}
+
+
+int dtl1_event(event_t event, int priority, event_callback_args_t *args)
+{
+	dev_link_t *link = args->client_data;
+	dtl1_info_t *info = link->priv;
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG) {
+			dtl1_close(info);
+			mod_timer(&link->release, jiffies + HZ / 20);
+		}
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+		dtl1_config(link);
+		break;
+	case CS_EVENT_PM_SUSPEND:
+		link->state |= DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		if (link->state & DEV_CONFIG)
+			CardServices(ReleaseConfiguration, link->handle);
+		break;
+	case CS_EVENT_PM_RESUME:
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		if (DEV_OK(link))
+			CardServices(RequestConfiguration, link->handle, &link->conf);
+		break;
+	}
+
+	return 0;
+}
+
+
+
+/* ======================== Module initialization ======================== */
+
+
+int __init init_dtl1_cs(void)
+{
+	servinfo_t serv;
+	int err;
+
+	CardServices(GetCardServicesInfo, &serv);
+	if (serv.Revision != CS_RELEASE_CODE) {
+		printk(KERN_NOTICE "dtl1_cs: Card Services release does not match!\n");
+		return -1;
+	}
+
+	err = register_pccard_driver(&dev_info, &dtl1_attach, &dtl1_detach);
+
+	return err;
+}
+
+
+void __exit exit_dtl1_cs(void)
+{
+	unregister_pccard_driver(&dev_info);
+
+	while (dev_list != NULL)
+		dtl1_detach(dev_list);
+}
+
+
+module_init(init_dtl1_cs);
+module_exit(exit_dtl1_cs);
+
+EXPORT_NO_SYMBOLS;
diff -urN linux-2.4.18/drivers/bluetooth/hci_bcsp.c linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.c
--- linux-2.4.18/drivers/bluetooth/hci_bcsp.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,710 @@
+/* 
+   BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
+   Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com>
+
+   Based on
+       hci_h4.c  by Maxim Krasnyansky <maxk@qualcomm.com>
+       ABCSP     by Carl Orsborn <cjo@csr.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: hci_bcsp.c,v 1.2 2002/09/26 05:05:14 maxk Exp $
+ */
+
+#define VERSION "0.1"
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include "hci_uart.h"
+#include "hci_bcsp.h"
+
+#ifndef HCI_UART_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#undef  BT_DMP
+#define BT_DMP( A... )
+#endif
+
+/* ---- BCSP CRC calculation ---- */
+
+/* Table for calculating CRC for polynomial 0x1021, LSB processed first,
+initial value 0xffff, bits shifted in reverse order. */
+
+static const u16 crc_table[] = {
+	0x0000, 0x1081, 0x2102, 0x3183,
+	0x4204, 0x5285, 0x6306, 0x7387,
+	0x8408, 0x9489, 0xa50a, 0xb58b,
+	0xc60c, 0xd68d, 0xe70e, 0xf78f
+};
+
+/* Initialise the crc calculator */
+#define BCSP_CRC_INIT(x) x = 0xffff
+
+/*
+   Update crc with next data byte
+
+   Implementation note
+        The data byte is treated as two nibbles.  The crc is generated
+        in reverse, i.e., bits are fed into the register from the top.
+*/
+static void bcsp_crc_update(u16 *crc, u8 d)
+{
+	u16 reg = *crc;
+
+	reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f];
+	reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f];
+
+	*crc = reg;
+}
+
+/*
+   Get reverse of generated crc
+
+   Implementation note
+        The crc generator (bcsp_crc_init() and bcsp_crc_update())
+        creates a reversed crc, so it needs to be swapped back before
+        being passed on.
+*/
+static u16 bcsp_crc_reverse(u16 crc)
+{
+	u16 b, rev;
+
+	for (b = 0, rev = 0; b < 16; b++) {
+		rev = rev << 1;
+		rev |= (crc & 1);
+		crc = crc >> 1;
+	}
+	return (rev);
+}
+
+/* ---- BCSP core ---- */
+
+static void bcsp_slip_msgdelim(struct sk_buff *skb)
+{
+	const char pkt_delim = 0xc0;
+	memcpy(skb_put(skb, 1), &pkt_delim, 1);
+}
+
+static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c)
+{
+	const char esc_c0[2] = { 0xdb, 0xdc };
+	const char esc_db[2] = { 0xdb, 0xdd };
+
+	switch (c) {
+	case 0xc0:
+		memcpy(skb_put(skb, 2), &esc_c0, 2);
+		break;
+	case 0xdb:
+		memcpy(skb_put(skb, 2), &esc_db, 2);
+		break;
+	default:
+		memcpy(skb_put(skb, 1), &c, 1);
+	}
+}
+
+static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+
+	if (skb->len > 0xFFF) {
+		BT_ERR("Packet too long");
+		kfree_skb(skb);
+		return 0;
+	}
+
+	switch (skb->pkt_type) {
+	case HCI_ACLDATA_PKT:
+	case HCI_COMMAND_PKT:
+		skb_queue_tail(&bcsp->rel, skb);
+		break;
+
+	case HCI_SCODATA_PKT:
+		skb_queue_tail(&bcsp->unrel, skb);
+		break;
+		
+	default:
+		BT_ERR("Unknown packet type");
+		kfree_skb(skb);
+		break;
+	}
+	return 0;
+}
+
+static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
+		int len, int pkt_type)
+{
+	struct sk_buff *nskb;
+	u8  hdr[4], chan;
+	int rel, i;
+
+#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+	u16 BCSP_CRC_INIT(bcsp_txmsg_crc);
+#endif
+
+	switch (pkt_type) {
+	case HCI_ACLDATA_PKT:
+		chan = 6;	/* BCSP ACL channel */
+		rel = 1;	/* reliable channel */
+		break;
+	case HCI_COMMAND_PKT:
+		chan = 5;	/* BCSP cmd/evt channel */
+		rel = 1;	/* reliable channel */
+		break;
+	case HCI_SCODATA_PKT:
+		chan = 7;	/* BCSP SCO channel */
+		rel = 0;	/* unreliable channel */
+		break;
+	case BCSP_LE_PKT:
+		chan = 1;	/* BCSP LE channel */
+		rel = 0;	/* unreliable channel */
+		break;
+	case BCSP_ACK_PKT:
+		chan = 0;	/* BCSP internal channel */
+		rel = 0;	/* unreliable channel */
+		break;
+	default:
+		BT_ERR("Unknown packet type");
+		return NULL;
+	}
+
+	/* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2
+	   (because bytes 0xc0 and 0xdb are escaped, worst case is
+	   when the packet is all made of 0xc0 and 0xdb :) )
+	   + 2 (0xc0 delimiters at start and end). */
+
+	nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
+	if (!nskb)
+		return NULL;
+
+	nskb->pkt_type = pkt_type;
+
+	bcsp_slip_msgdelim(nskb);
+
+	hdr[0] = bcsp->rxseq_txack << 3;
+	bcsp->txack_req = 0;
+	BT_DBG("We request packet no %u to card", bcsp->rxseq_txack);
+
+	if (rel) {
+		hdr[0] |= 0x80 + bcsp->msgq_txseq;
+		BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq);
+		bcsp->msgq_txseq = ++(bcsp->msgq_txseq) & 0x07;
+	}
+#ifdef  CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+	hdr[0] |= 0x40;
+#endif
+
+	hdr[1]  = (len << 4) & 0xFF;
+	hdr[1] |= chan;
+	hdr[2]  = len >> 4;
+	hdr[3]  = ~(hdr[0] + hdr[1] + hdr[2]);
+
+	/* Put BCSP header */
+	for (i = 0; i < 4; i++) {
+		bcsp_slip_one_byte(nskb, hdr[i]);
+#ifdef  CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+		bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]);
+#endif
+	}
+
+	/* Put payload */
+	for (i = 0; i < len; i++) {
+		bcsp_slip_one_byte(nskb, data[i]);
+#ifdef  CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+		bcsp_crc_update(&bcsp_txmsg_crc, data[i]);
+#endif
+	}
+
+#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+	/* Put CRC */
+	bcsp_txmsg_crc = bcsp_crc_reverse(bcsp_txmsg_crc);
+	bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff));
+	bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff));
+#endif
+
+	bcsp_slip_msgdelim(nskb);
+	return nskb;
+}
+
+/* This is a rewrite of pkt_avail in ABCSP */
+static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv;
+	unsigned long flags;
+	struct sk_buff *skb;
+	
+	/* First of all, check for unreliable messages in the queue,
+	   since they have priority */
+
+	if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) {
+		struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type);
+		if (nskb) {
+			kfree_skb(skb);
+			return nskb;
+		} else {
+			skb_queue_head(&bcsp->unrel, skb);
+			BT_ERR("Could not dequeue pkt because alloc_skb failed");
+		}
+	}
+
+	/* Now, try to send a reliable pkt. We can only send a
+	   reliable packet if the number of packets sent but not yet ack'ed
+	   is < than the winsize */
+
+	spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+	if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) {
+		struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type);
+		if (nskb) {
+			__skb_queue_tail(&bcsp->unack, skb);
+			mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
+			spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+			return nskb;
+		} else {
+			skb_queue_head(&bcsp->rel, skb);
+			BT_ERR("Could not dequeue pkt because alloc_skb failed");
+		}
+	}
+
+	spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+
+	/* We could not send a reliable packet, either because there are
+	   none or because there are too many unack'ed pkts. Did we receive
+	   any packets we have not acknowledged yet ? */
+
+	if (bcsp->txack_req) {
+		/* if so, craft an empty ACK pkt and send it on BCSP unreliable
+		   channel 0 */
+		struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT);
+		return nskb;
+	}
+
+	/* We have nothing to send */
+	return NULL;
+}
+
+static int bcsp_flush(struct hci_uart *hu)
+{
+	BT_DBG("hu %p", hu);
+	return 0;
+}
+
+/* Remove ack'ed packets */
+static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
+{
+	unsigned long flags;
+	struct sk_buff *skb;
+	int i, pkts_to_be_removed;
+	u8 seqno;
+
+	spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+	pkts_to_be_removed = bcsp->unack.qlen;
+	seqno = bcsp->msgq_txseq;
+
+	while (pkts_to_be_removed) {
+		if (bcsp->rxack == seqno)
+			break;
+		pkts_to_be_removed--;
+		seqno = (seqno - 1) & 0x07;
+	}
+
+	if (bcsp->rxack != seqno)
+		BT_ERR("Peer acked invalid packet");
+
+	BT_DBG("Removing %u pkts out of %u, up to seqno %u",
+	       pkts_to_be_removed, bcsp->unack.qlen, (seqno - 1) & 0x07);
+
+	for (i = 0, skb = ((struct sk_buff *) &bcsp->unack)->next; i < pkts_to_be_removed
+			&& skb != (struct sk_buff *) &bcsp->unack; i++) {
+		struct sk_buff *nskb;
+
+		nskb = skb->next;
+		__skb_unlink(skb, &bcsp->unack);
+		kfree_skb(skb);
+		skb = nskb;
+	}
+	if (bcsp->unack.qlen == 0)
+		del_timer(&bcsp->tbcsp);
+	spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+	if (i != pkts_to_be_removed)
+		BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed);
+}
+
+/* Handle BCSP link-establishment packets. When we
+   detect a "sync" packet, symptom that the BT module has reset,
+   we do nothing :) (yet) */
+static void bcsp_handle_le_pkt(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	u8 conf_pkt[4]     = { 0xad, 0xef, 0xac, 0xed };
+	u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
+	u8 sync_pkt[4]     = { 0xda, 0xdc, 0xed, 0xed };
+
+	/* spot "conf" pkts and reply with a "conf rsp" pkt */
+	if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
+			!memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
+		struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
+
+		BT_DBG("Found a LE conf pkt");
+		if (!nskb)
+			return;
+		memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
+		nskb->pkt_type = BCSP_LE_PKT;
+
+		skb_queue_head(&bcsp->unrel, nskb);
+		hci_uart_tx_wakeup(hu);
+	}
+	/* Spot "sync" pkts. If we find one...disaster! */
+	else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
+			!memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
+		BT_ERR("Found a LE sync pkt, card has reset");
+	}
+}
+
+static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte)
+{
+	const u8 c0 = 0xc0, db = 0xdb;
+
+	switch (bcsp->rx_esc_state) {
+	case BCSP_ESCSTATE_NOESC:
+		switch (byte) {
+		case 0xdb:
+			bcsp->rx_esc_state = BCSP_ESCSTATE_ESC;
+			break;
+		default:
+			memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
+			if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && 
+					bcsp->rx_state != BCSP_W4_CRC)
+				bcsp_crc_update(&bcsp->message_crc, byte);
+			bcsp->rx_count--;
+		}
+		break;
+
+	case BCSP_ESCSTATE_ESC:
+		switch (byte) {
+		case 0xdc:
+			memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
+			if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && 
+					bcsp->rx_state != BCSP_W4_CRC)
+				bcsp_crc_update(&bcsp-> message_crc, 0xc0);
+			bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+			bcsp->rx_count--;
+			break;
+
+		case 0xdd:
+			memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
+			if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && 
+					bcsp->rx_state != BCSP_W4_CRC) 
+				bcsp_crc_update(&bcsp-> message_crc, 0xdb);
+			bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+			bcsp->rx_count--;
+			break;
+
+		default:
+			BT_ERR ("Invalid byte %02x after esc byte", byte);
+			kfree_skb(bcsp->rx_skb);
+			bcsp->rx_skb = NULL;
+			bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+			bcsp->rx_count = 0;
+		}
+	}
+}
+
+static inline void bcsp_complete_rx_pkt(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	int pass_up;
+
+	if (bcsp->rx_skb->data[0] & 0x80) {	/* reliable pkt */
+		BT_DBG("Received seqno %u from card", bcsp->rxseq_txack);
+		bcsp->rxseq_txack++;
+		bcsp->rxseq_txack %= 0x8;
+		bcsp->txack_req    = 1;
+
+		/* If needed, transmit an ack pkt */
+		hci_uart_tx_wakeup(hu);
+	}
+
+	bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
+	BT_DBG("Request for pkt %u from card", bcsp->rxack);
+
+	bcsp_pkt_cull(bcsp);
+	if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
+			bcsp->rx_skb->data[0] & 0x80) {
+		bcsp->rx_skb->pkt_type = HCI_ACLDATA_PKT;
+		pass_up = 1;
+	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
+			bcsp->rx_skb->data[0] & 0x80) {
+		bcsp->rx_skb->pkt_type = HCI_EVENT_PKT;
+		pass_up = 1;
+	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
+		bcsp->rx_skb->pkt_type = HCI_SCODATA_PKT;
+		pass_up = 1;
+	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
+			!(bcsp->rx_skb->data[0] & 0x80)) {
+		bcsp_handle_le_pkt(hu);
+		pass_up = 0;
+	} else
+		pass_up = 0;
+
+	if (!pass_up) {
+		if ((bcsp->rx_skb->data[1] & 0x0f) != 0 &&
+	    		(bcsp->rx_skb->data[1] & 0x0f) != 1) {
+			BT_ERR ("Packet for unknown channel (%u %s)",
+				bcsp->rx_skb->data[1] & 0x0f,
+				bcsp->rx_skb->data[0] & 0x80 ? 
+				"reliable" : "unreliable");
+		}
+		kfree_skb(bcsp->rx_skb);
+	} else {
+		/* Pull out BCSP hdr */
+		skb_pull(bcsp->rx_skb, 4);
+
+		hci_recv_frame(bcsp->rx_skb);
+	}
+	bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+	bcsp->rx_skb = NULL;
+}
+
+/* Recv data */
+static int bcsp_recv(struct hci_uart *hu, void *data, int count)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	register unsigned char *ptr;
+
+	BT_DBG("hu %p count %d rx_state %ld rx_count %ld", 
+		hu, count, bcsp->rx_state, bcsp->rx_count);
+
+	ptr = data;
+	while (count) {
+		if (bcsp->rx_count) {
+			if (*ptr == 0xc0) {
+				BT_ERR("Short BCSP packet");
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_START;
+				bcsp->rx_count = 0;
+			} else
+				bcsp_unslip_one_byte(bcsp, *ptr);
+
+			ptr++; count--;
+			continue;
+		}
+
+		switch (bcsp->rx_state) {
+		case BCSP_W4_BCSP_HDR:
+			if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
+					bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
+				BT_ERR("Error in BCSP hdr checksum");
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+				bcsp->rx_count = 0;
+				continue;
+			}
+			if (bcsp->rx_skb->data[0] & 0x80	/* reliable pkt */
+			    		&& (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
+				BT_ERR ("Out-of-order packet arrived, got %u expected %u",
+					bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
+
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+				bcsp->rx_count = 0;
+				continue;
+			}
+			bcsp->rx_state = BCSP_W4_DATA;
+			bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) + 
+					(bcsp->rx_skb->data[2] << 4);	/* May be 0 */
+			continue;
+
+		case BCSP_W4_DATA:
+			if (bcsp->rx_skb->data[0] & 0x40) {	/* pkt with crc */
+				bcsp->rx_state = BCSP_W4_CRC;
+				bcsp->rx_count = 2;
+			} else
+				bcsp_complete_rx_pkt(hu);
+			continue;
+
+		case BCSP_W4_CRC:
+			if (bcsp_crc_reverse(bcsp->message_crc) !=
+					(bcsp->rx_skb->data[bcsp->rx_skb->len - 2] << 8) +
+					bcsp->rx_skb->data[bcsp->rx_skb->len - 1]) {
+
+				BT_ERR ("Checksum failed: computed %04x received %04x",
+					bcsp_crc_reverse(bcsp->message_crc),
+				     	(bcsp->rx_skb-> data[bcsp->rx_skb->len - 2] << 8) +
+				     	bcsp->rx_skb->data[bcsp->rx_skb->len - 1]);
+
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+				bcsp->rx_count = 0;
+				continue;
+			}
+			skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2);
+			bcsp_complete_rx_pkt(hu);
+			continue;
+
+		case BCSP_W4_PKT_DELIMITER:
+			switch (*ptr) {
+			case 0xc0:
+				bcsp->rx_state = BCSP_W4_PKT_START;
+				break;
+			default:
+				/*BT_ERR("Ignoring byte %02x", *ptr);*/
+				break;
+			}
+			ptr++; count--;
+			break;
+
+		case BCSP_W4_PKT_START:
+			switch (*ptr) {
+			case 0xc0:
+				ptr++; count--;
+				break;
+
+			default:
+				bcsp->rx_state = BCSP_W4_BCSP_HDR;
+				bcsp->rx_count = 4;
+				bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+				BCSP_CRC_INIT(bcsp->message_crc);
+				
+				/* Do not increment ptr or decrement count
+				 * Allocate packet. Max len of a BCSP pkt= 
+				 * 0xFFF (payload) +4 (header) +2 (crc) */
+
+				bcsp->rx_skb = bluez_skb_alloc(0x1005, GFP_ATOMIC);
+				if (!bcsp->rx_skb) {
+					BT_ERR("Can't allocate mem for new packet");
+					bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+					bcsp->rx_count = 0;
+					return 0;
+				}
+				bcsp->rx_skb->dev = (void *) &hu->hdev;
+				break;
+			}
+			break;
+		}
+	}
+	return count;
+}
+
+	/* Arrange to retransmit all messages in the relq. */
+static void bcsp_timed_event(unsigned long arg)
+{
+	struct hci_uart *hu = (struct hci_uart *) arg;
+	struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
+
+	spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+	while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
+		bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
+		skb_queue_head(&bcsp->rel, skb);
+	}
+
+	spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+	hci_uart_tx_wakeup(hu);
+}
+
+static int bcsp_open(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp;
+
+	BT_DBG("hu %p", hu);
+
+	bcsp = kmalloc(sizeof(*bcsp), GFP_ATOMIC);
+	if (!bcsp)
+		return -ENOMEM;
+	memset(bcsp, 0, sizeof(*bcsp));
+
+	hu->priv = bcsp;
+	skb_queue_head_init(&bcsp->unack);
+	skb_queue_head_init(&bcsp->rel);
+	skb_queue_head_init(&bcsp->unrel);
+
+	init_timer(&bcsp->tbcsp);
+	bcsp->tbcsp.function = bcsp_timed_event;
+	bcsp->tbcsp.data     = (u_long) hu;
+
+	bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+
+	return 0;
+}
+
+static int bcsp_close(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	hu->priv = NULL;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&bcsp->unack);
+	skb_queue_purge(&bcsp->rel);
+	skb_queue_purge(&bcsp->unrel);
+	del_timer(&bcsp->tbcsp);
+
+	kfree(bcsp);
+	return 0;
+}
+
+static struct hci_uart_proto bcsp = {
+	id:      HCI_UART_BCSP,
+	open:    bcsp_open,
+	close:   bcsp_close,
+	enqueue: bcsp_enqueue,
+	dequeue: bcsp_dequeue,
+	recv:    bcsp_recv,
+	flush:   bcsp_flush
+};
+
+int bcsp_init(void)
+{
+	return hci_uart_register_proto(&bcsp);
+}
+
+int bcsp_deinit(void)
+{
+	return hci_uart_unregister_proto(&bcsp);
+}
diff -urN linux-2.4.18/drivers/bluetooth/hci_bcsp.h linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.h
--- linux-2.4.18/drivers/bluetooth/hci_bcsp.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_bcsp.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,70 @@
+/* 
+   BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
+   Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com>
+
+   Based on
+       hci_h4.c  by Maxim Krasnyansky <maxk@qualcomm.com>
+       ABCSP     by Carl Orsborn <cjo@csr.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/* 
+ * $Id: hci_bcsp.h,v 1.2 2002/09/26 05:05:14 maxk Exp $
+ */
+
+#ifndef __HCI_BCSP_H__
+#define __HCI_BCSP_H__
+
+#define BCSP_TXWINSIZE  4
+
+#define BCSP_ACK_PKT    0x05
+#define BCSP_LE_PKT     0x06
+
+struct bcsp_struct {
+	struct sk_buff_head unack;	/* Unack'ed packets queue */
+	struct sk_buff_head rel;	/* Reliable packets queue */
+	struct sk_buff_head unrel;	/* Unreliable packets queue */
+
+	unsigned long rx_count;
+	struct  sk_buff *rx_skb;
+	u8      rxseq_txack;		/* rxseq == txack. */
+	u8      rxack;			/* Last packet sent by us that the peer ack'ed */
+	struct  timer_list tbcsp;
+	
+	enum {
+		BCSP_W4_PKT_DELIMITER,
+		BCSP_W4_PKT_START,
+		BCSP_W4_BCSP_HDR,
+		BCSP_W4_DATA,
+		BCSP_W4_CRC
+	} rx_state;
+
+	enum {
+		BCSP_ESCSTATE_NOESC,
+		BCSP_ESCSTATE_ESC
+	} rx_esc_state;
+
+	u16     message_crc;
+	u8      txack_req;		/* Do we need to send ack's to the peer? */
+
+	/* Reliable packet sequence number - used to assign seq to each rel pkt. */
+	u8      msgq_txseq;
+};
+
+#endif	/* __HCI_BCSP_H__ */
diff -urN linux-2.4.18/drivers/bluetooth/hci_h4.c linux-2.4.18-mh15/drivers/bluetooth/hci_h4.c
--- linux-2.4.18/drivers/bluetooth/hci_h4.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_h4.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,277 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * BlueZ HCI UART(H4) protocol.
+ *
+ * $Id: hci_h4.c,v 1.3 2002/09/09 01:17:32 maxk Exp $    
+ */
+#define VERSION "1.2"
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include "hci_uart.h"
+#include "hci_h4.h"
+
+#ifndef HCI_UART_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#undef  BT_DMP
+#define BT_DMP( A... )
+#endif
+
+/* Initialize protocol */
+static int h4_open(struct hci_uart *hu)
+{
+	struct h4_struct *h4;
+	
+	BT_DBG("hu %p", hu);
+	
+	h4 = kmalloc(sizeof(*h4), GFP_ATOMIC);
+	if (!h4)
+		return -ENOMEM;
+	memset(h4, 0, sizeof(*h4));
+
+	skb_queue_head_init(&h4->txq);
+
+	hu->priv = h4;
+	return 0;
+}
+
+/* Flush protocol data */
+static int h4_flush(struct hci_uart *hu)
+{
+	struct h4_struct *h4 = hu->priv;
+
+	BT_DBG("hu %p", hu);
+	skb_queue_purge(&h4->txq);
+	return 0;
+}
+
+/* Close protocol */
+static int h4_close(struct hci_uart *hu)
+{
+	struct h4_struct *h4 = hu->priv;
+	hu->priv = NULL;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&h4->txq);
+	if (h4->rx_skb)
+		kfree_skb(h4->rx_skb);
+
+	hu->priv = NULL;
+	kfree(h4);
+	return 0;
+}
+
+/* Enqueue frame for transmittion (padding, crc, etc) */
+static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct h4_struct *h4 = hu->priv;
+
+	BT_DBG("hu %p skb %p", hu, skb);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
+	skb_queue_tail(&h4->txq, skb);
+	return 0;
+}
+
+static inline int h4_check_data_len(struct h4_struct *h4, int len)
+{
+	register int room = skb_tailroom(h4->rx_skb);
+
+	BT_DBG("len %d room %d", len, room);
+	if (!len) {
+		BT_DMP(h4->rx_skb->data, h4->rx_skb->len);
+		hci_recv_frame(h4->rx_skb);
+	} else if (len > room) {
+		BT_ERR("Data length is too large");
+		kfree_skb(h4->rx_skb);
+	} else {
+		h4->rx_state = H4_W4_DATA;
+		h4->rx_count = len;
+		return len;
+	}
+
+	h4->rx_state = H4_W4_PACKET_TYPE;
+	h4->rx_skb   = NULL;
+	h4->rx_count = 0;
+	return 0;
+}
+
+/* Recv data */
+static int h4_recv(struct hci_uart *hu, void *data, int count)
+{
+	struct h4_struct *h4 = hu->priv;
+	register char *ptr;
+	hci_event_hdr *eh;
+	hci_acl_hdr   *ah;
+	hci_sco_hdr   *sh;
+	register int len, type, dlen;
+
+	BT_DBG("hu %p count %d rx_state %ld rx_count %ld", 
+			hu, count, h4->rx_state, h4->rx_count);
+
+	ptr = data;
+	while (count) {
+		if (h4->rx_count) {
+			len = MIN(h4->rx_count, count);
+			memcpy(skb_put(h4->rx_skb, len), ptr, len);
+			h4->rx_count -= len; count -= len; ptr += len;
+
+			if (h4->rx_count)
+				continue;
+
+			switch (h4->rx_state) {
+			case H4_W4_DATA:
+				BT_DBG("Complete data");
+
+				BT_DMP(h4->rx_skb->data, h4->rx_skb->len);
+
+				hci_recv_frame(h4->rx_skb);
+
+				h4->rx_state = H4_W4_PACKET_TYPE;
+				h4->rx_skb = NULL;
+				continue;
+
+			case H4_W4_EVENT_HDR:
+				eh = (hci_event_hdr *) h4->rx_skb->data;
+
+				BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
+
+				h4_check_data_len(h4, eh->plen);
+				continue;
+
+			case H4_W4_ACL_HDR:
+				ah = (hci_acl_hdr *) h4->rx_skb->data;
+				dlen = __le16_to_cpu(ah->dlen);
+
+				BT_DBG("ACL header: dlen %d", dlen);
+
+				h4_check_data_len(h4, dlen);
+				continue;
+
+			case H4_W4_SCO_HDR:
+				sh = (hci_sco_hdr *) h4->rx_skb->data;
+
+				BT_DBG("SCO header: dlen %d", sh->dlen);
+
+				h4_check_data_len(h4, sh->dlen);
+				continue;
+			}
+		}
+
+		/* H4_W4_PACKET_TYPE */
+		switch (*ptr) {
+		case HCI_EVENT_PKT:
+			BT_DBG("Event packet");
+			h4->rx_state = H4_W4_EVENT_HDR;
+			h4->rx_count = HCI_EVENT_HDR_SIZE;
+			type = HCI_EVENT_PKT;
+			break;
+
+		case HCI_ACLDATA_PKT:
+			BT_DBG("ACL packet");
+			h4->rx_state = H4_W4_ACL_HDR;
+			h4->rx_count = HCI_ACL_HDR_SIZE;
+			type = HCI_ACLDATA_PKT;
+			break;
+
+		case HCI_SCODATA_PKT:
+			BT_DBG("SCO packet");
+			h4->rx_state = H4_W4_SCO_HDR;
+			h4->rx_count = HCI_SCO_HDR_SIZE;
+			type = HCI_SCODATA_PKT;
+			break;
+
+		default:
+			BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
+			hu->hdev.stat.err_rx++;
+			ptr++; count--;
+			continue;
+		};
+		ptr++; count--;
+
+		/* Allocate packet */
+		h4->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+		if (!h4->rx_skb) {
+			BT_ERR("Can't allocate mem for new packet");
+			h4->rx_state = H4_W4_PACKET_TYPE;
+			h4->rx_count = 0;
+			return 0;
+		}
+		h4->rx_skb->dev = (void *) &hu->hdev;
+		h4->rx_skb->pkt_type = type;
+	}
+	return count;
+}
+
+static struct sk_buff *h4_dequeue(struct hci_uart *hu)
+{
+	struct h4_struct *h4 = hu->priv;
+	return skb_dequeue(&h4->txq);
+}
+
+static struct hci_uart_proto h4p = {
+	id:      HCI_UART_H4,
+	open:    h4_open,
+	close:   h4_close,
+	recv:    h4_recv,
+	enqueue: h4_enqueue,
+	dequeue: h4_dequeue,
+	flush:   h4_flush,
+};
+	      
+int h4_init(void)
+{
+	return hci_uart_register_proto(&h4p);
+}
+
+int h4_deinit(void)
+{
+	return hci_uart_unregister_proto(&h4p);
+}
diff -urN linux-2.4.18/drivers/bluetooth/hci_h4.h linux-2.4.18-mh15/drivers/bluetooth/hci_h4.h
--- linux-2.4.18/drivers/bluetooth/hci_h4.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_h4.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,44 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: hci_h4.h,v 1.2 2002/09/09 01:17:32 maxk Exp $
+ */
+
+#ifdef __KERNEL__
+struct h4_struct {
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+	struct sk_buff_head txq;
+};
+
+/* H4 receiver States */
+#define H4_W4_PACKET_TYPE 0
+#define H4_W4_EVENT_HDR	  1
+#define H4_W4_ACL_HDR     2
+#define H4_W4_SCO_HDR     3
+#define H4_W4_DATA        4
+
+#endif /* __KERNEL__ */
diff -urN linux-2.4.18/drivers/bluetooth/hci_ldisc.c linux-2.4.18-mh15/drivers/bluetooth/hci_ldisc.c
--- linux-2.4.18/drivers/bluetooth/hci_ldisc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_ldisc.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,579 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * BlueZ HCI UART driver.
+ *
+ * $Id: hci_ldisc.c,v 1.5 2002/10/02 18:37:20 maxk Exp $    
+ */
+#define VERSION "2.1"
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include "hci_uart.h"
+
+#ifndef HCI_UART_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#undef  BT_DMP
+#define BT_DMP( A... )
+#endif
+
+static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
+
+int hci_uart_register_proto(struct hci_uart_proto *p)
+{
+	if (p->id >= HCI_UART_MAX_PROTO)
+		return -EINVAL;
+
+	if (hup[p->id])
+		return -EEXIST;
+
+	hup[p->id] = p;
+	return 0;
+}
+
+int hci_uart_unregister_proto(struct hci_uart_proto *p)
+{
+	if (p->id >= HCI_UART_MAX_PROTO)
+		return -EINVAL;
+
+	if (!hup[p->id])
+		return -EINVAL;
+
+	hup[p->id] = NULL;
+	return 0;
+}
+
+static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
+{
+	if (id >= HCI_UART_MAX_PROTO)
+		return NULL;
+	return hup[id];
+}
+
+static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
+{
+	struct hci_dev *hdev = &hu->hdev;
+	
+	/* Update HCI stat counters */
+	switch (pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	}
+}
+
+static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
+{
+	struct sk_buff *skb = hu->tx_skb;
+	if (!skb)
+		skb = hu->proto->dequeue(hu);
+	else
+		hu->tx_skb = NULL;
+	return skb;
+}
+
+int hci_uart_tx_wakeup(struct hci_uart *hu)
+{
+	struct tty_struct *tty = hu->tty;
+	struct hci_dev *hdev = &hu->hdev;
+	struct sk_buff *skb;
+	
+	if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
+		set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
+		return 0;
+	}
+
+	BT_DBG("");
+
+restart:
+	clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
+
+	while ((skb = hci_uart_dequeue(hu))) {
+		int len;
+	
+		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+		len = tty->driver.write(tty, 0, skb->data, skb->len);
+		hdev->stat.byte_tx += len;
+
+		skb_pull(skb, len);
+		if (skb->len) {
+			hu->tx_skb = skb;
+			break;
+		}
+	
+		hci_uart_tx_complete(hu, skb->pkt_type);
+		kfree_skb(skb);
+	} 
+	
+	if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
+		goto restart;
+
+	clear_bit(HCI_UART_SENDING, &hu->tx_state);
+	return 0;
+}
+
+/* ------- Interface to HCI layer ------ */
+/* Initialize device */
+static int hci_uart_open(struct hci_dev *hdev)
+{
+	BT_DBG("%s %p", hdev->name, hdev);
+
+	/* Nothing to do for UART driver */
+
+	set_bit(HCI_RUNNING, &hdev->flags);
+	return 0;
+}
+
+/* Reset device */
+static int hci_uart_flush(struct hci_dev *hdev)
+{
+	struct hci_uart *hu  = (struct hci_uart *) hdev->driver_data;
+	struct tty_struct *tty = hu->tty;
+
+	BT_DBG("hdev %p tty %p", hdev, tty);
+
+	if (hu->tx_skb) {
+		kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
+	}
+
+	/* Flush any pending characters in the driver and discipline. */
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+
+	if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+		hu->proto->flush(hu);
+
+	return 0;
+}
+
+/* Close device */
+static int hci_uart_close(struct hci_dev *hdev)
+{
+	BT_DBG("hdev %p", hdev);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	hci_uart_flush(hdev);
+	return 0;
+}
+
+/* Send frames from HCI layer */
+static int hci_uart_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+	struct tty_struct *tty;
+	struct hci_uart *hu;
+
+	if (!hdev) {
+		BT_ERR("Frame for uknown device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	hu = (struct hci_uart *) hdev->driver_data;
+	tty = hu->tty;
+
+	BT_DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
+
+	hu->proto->enqueue(hu, skb);
+
+	hci_uart_tx_wakeup(hu);
+	return 0;
+}
+
+static void hci_uart_destruct(struct hci_dev *hdev)
+{
+	struct hci_uart *hu;
+
+	if (!hdev) return;
+
+	BT_DBG("%s", hdev->name);
+
+	hu = (struct hci_uart *) hdev->driver_data;
+	kfree(hu);
+
+	MOD_DEC_USE_COUNT;
+}
+
+/* ------ LDISC part ------ */
+/* hci_uart_tty_open
+ * 
+ *     Called when line discipline changed to HCI_UART.
+ *
+ * Arguments:
+ *     tty    pointer to tty info structure
+ * Return Value:    
+ *     0 if success, otherwise error code
+ */
+static int hci_uart_tty_open(struct tty_struct *tty)
+{
+	struct hci_uart *hu = (void *) tty->disc_data;
+
+	BT_DBG("tty %p", tty);
+
+	if (hu)
+		return -EEXIST;
+
+	if (!(hu = kmalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
+		BT_ERR("Can't allocate controll structure");
+		return -ENFILE;
+	}
+	memset(hu, 0, sizeof(struct hci_uart));
+
+	tty->disc_data = hu;
+	hu->tty = tty;
+
+	spin_lock_init(&hu->rx_lock);
+
+	/* Flush any pending characters in the driver and line discipline */
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* hci_uart_tty_close()
+ *
+ *    Called when the line discipline is changed to something
+ *    else, the tty is closed, or the tty detects a hangup.
+ */
+static void hci_uart_tty_close(struct tty_struct *tty)
+{
+	struct hci_uart *hu = (void *)tty->disc_data;
+
+	BT_DBG("tty %p", tty);
+
+	/* Detach from the tty */
+	tty->disc_data = NULL;
+
+	if (hu) {
+		struct hci_dev *hdev = &hu->hdev;
+		hci_uart_close(hdev);
+
+		if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+			hu->proto->close(hu);
+			hci_unregister_dev(hdev);
+		}
+
+		MOD_DEC_USE_COUNT;
+	}
+}
+
+/* hci_uart_tty_wakeup()
+ *
+ *    Callback for transmit wakeup. Called when low level
+ *    device driver can accept more send data.
+ *
+ * Arguments:        tty    pointer to associated tty instance data
+ * Return Value:    None
+ */
+static void hci_uart_tty_wakeup(struct tty_struct *tty)
+{
+	struct hci_uart *hu = (void *)tty->disc_data;
+
+	BT_DBG("");
+
+	if (!hu)
+		return;
+
+	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+
+	if (tty != hu->tty)
+		return;
+
+	if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+		hci_uart_tx_wakeup(hu);
+}
+
+/* hci_uart_tty_room()
+ * 
+ *    Callback function from tty driver. Return the amount of 
+ *    space left in the receiver's buffer to decide if remote
+ *    transmitter is to be throttled.
+ *
+ * Arguments:        tty    pointer to associated tty instance data
+ * Return Value:    number of bytes left in receive buffer
+ */
+static int hci_uart_tty_room (struct tty_struct *tty)
+{
+	return 65536;
+}
+
+/* hci_uart_tty_receive()
+ * 
+ *     Called by tty low level driver when receive data is
+ *     available.
+ *     
+ * Arguments:  tty          pointer to tty isntance data
+ *             data         pointer to received data
+ *             flags        pointer to flags for data
+ *             count        count of received data in bytes
+ *     
+ * Return Value:    None
+ */
+static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count)
+{
+	struct hci_uart *hu = (void *)tty->disc_data;
+	
+	if (!hu || tty != hu->tty)
+		return;
+
+	if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
+		return;
+	
+	spin_lock(&hu->rx_lock);
+	hu->proto->recv(hu, (void *) data, count);
+	hu->hdev.stat.byte_rx += count;
+	spin_unlock(&hu->rx_lock);
+
+	if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
+		tty->driver.unthrottle(tty);
+}
+
+static int hci_uart_register_dev(struct hci_uart *hu)
+{
+	struct hci_dev *hdev;
+
+	BT_DBG("");
+
+	/* Initialize and register HCI device */
+	hdev = &hu->hdev;
+
+	hdev->type = HCI_UART;
+	hdev->driver_data = hu;
+
+	hdev->open  = hci_uart_open;
+	hdev->close = hci_uart_close;
+	hdev->flush = hci_uart_flush;
+	hdev->send  = hci_uart_send_frame;
+	hdev->destruct = hci_uart_destruct;
+
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device %s", hdev->name);
+		return -ENODEV;
+	}
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int hci_uart_set_proto(struct hci_uart *hu, int id)
+{
+	struct hci_uart_proto *p;
+	int err;	
+	
+	p = hci_uart_get_proto(id);
+	if (!p)
+		return -EPROTONOSUPPORT;
+
+	err = p->open(hu);
+	if (err)
+		return err;
+
+	hu->proto = p;
+
+	err = hci_uart_register_dev(hu);
+	if (err) {
+		p->close(hu);
+		return err;
+	}
+	return 0;
+}
+
+/* hci_uart_tty_ioctl()
+ *
+ *    Process IOCTL system call for the tty device.
+ *
+ * Arguments:
+ *
+ *    tty        pointer to tty instance data
+ *    file       pointer to open file object for device
+ *    cmd        IOCTL command code
+ *    arg        argument for IOCTL call (cmd dependent)
+ *
+ * Return Value:    Command dependent
+ */
+static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
+                            unsigned int cmd, unsigned long arg)
+{
+	struct hci_uart *hu = (void *)tty->disc_data;
+	int err = 0;
+
+	BT_DBG("");
+
+	/* Verify the status of the device */
+	if (!hu)
+		return -EBADF;
+
+	switch (cmd) {
+	case HCIUARTSETPROTO:
+		if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+			err = hci_uart_set_proto(hu, arg);
+			if (err) {
+				clear_bit(HCI_UART_PROTO_SET, &hu->flags);
+				return err;
+			}
+			tty->low_latency = 1;
+		} else	
+			return -EBUSY;
+
+	case HCIUARTGETPROTO:
+		if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+			return hu->proto->id;
+		return -EUNATCH;
+		
+	default:
+		err = n_tty_ioctl(tty, file, cmd, arg);
+		break;
+	};
+
+	return err;
+}
+
+/*
+ * We don't provide read/write/poll interface for user space.
+ */
+static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
+{
+	return 0;
+}
+static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
+{
+	return 0;
+}
+static unsigned int hci_uart_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
+{
+	return 0;
+}
+
+#ifdef CONFIG_BLUEZ_HCIUART_H4
+int h4_init(void);
+int h4_deinit(void);
+#endif
+#ifdef CONFIG_BLUEZ_HCIUART_BCSP
+int bcsp_init(void);
+int bcsp_deinit(void);
+#endif
+
+int __init hci_uart_init(void)
+{
+	static struct tty_ldisc hci_uart_ldisc;
+	int err;
+
+	BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", 
+		VERSION);
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+
+	/* Register the tty discipline */
+
+	memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));
+	hci_uart_ldisc.magic       = TTY_LDISC_MAGIC;
+	hci_uart_ldisc.name        = "n_hci";
+	hci_uart_ldisc.open        = hci_uart_tty_open;
+	hci_uart_ldisc.close       = hci_uart_tty_close;
+	hci_uart_ldisc.read        = hci_uart_tty_read;
+	hci_uart_ldisc.write       = hci_uart_tty_write;
+	hci_uart_ldisc.ioctl       = hci_uart_tty_ioctl;
+	hci_uart_ldisc.poll        = hci_uart_tty_poll;
+	hci_uart_ldisc.receive_room= hci_uart_tty_room;
+	hci_uart_ldisc.receive_buf = hci_uart_tty_receive;
+	hci_uart_ldisc.write_wakeup= hci_uart_tty_wakeup;
+
+	if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {
+		BT_ERR("Can't register HCI line discipline (%d)", err);
+		return err;
+	}
+
+#ifdef CONFIG_BLUEZ_HCIUART_H4
+	h4_init();
+#endif
+#ifdef CONFIG_BLUEZ_HCIUART_BCSP
+	bcsp_init();
+#endif
+	
+	return 0;
+}
+
+void hci_uart_cleanup(void)
+{
+	int err;
+
+#ifdef CONFIG_BLUEZ_HCIUART_H4
+	h4_deinit();
+#endif
+#ifdef CONFIG_BLUEZ_HCIUART_BCSP
+	bcsp_deinit();
+#endif
+
+	/* Release tty registration of line discipline */
+	if ((err = tty_register_ldisc(N_HCI, NULL)))
+		BT_ERR("Can't unregister HCI line discipline (%d)", err);
+}
+
+module_init(hci_uart_init);
+module_exit(hci_uart_cleanup);
+
+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/drivers/bluetooth/hci_uart.c linux-2.4.18-mh15/drivers/bluetooth/hci_uart.c
--- linux-2.4.18/drivers/bluetooth/hci_uart.c	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_uart.c	1970-01-01 01:00:00.000000000 +0100
@@ -1,580 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * BlueZ HCI UART driver.
- *
- * $Id: hci_uart.c,v 1.5 2001/07/05 18:42:44 maxk Exp $    
- */
-#define VERSION "1.0"
-
-#include <linux/config.h>
-#include <linux/module.h>
-
-#include <linux/version.h>
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/poll.h>
-
-#include <linux/slab.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/signal.h>
-#include <linux/ioctl.h>
-#include <linux/skbuff.h>
-
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
-#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/hci_uart.h>
-
-#ifndef HCI_UART_DEBUG
-#undef  DBG
-#define DBG( A... )
-#undef  DMP
-#define DMP( A... )
-#endif
-
-/* ------- Interface to HCI layer ------ */
-/* Initialize device */
-int n_hci_open(struct hci_dev *hdev)
-{
-	DBG("%s %p", hdev->name, hdev);
-
-	/* Nothing to do for UART driver */
-
-	hdev->flags |= HCI_RUNNING;
-
-	return 0;
-}
-
-/* Reset device */
-int n_hci_flush(struct hci_dev *hdev)
-{
-	struct n_hci *n_hci  = (struct n_hci *) hdev->driver_data;
-	struct tty_struct *tty = n_hci->tty;
-
-	DBG("hdev %p tty %p", hdev, tty);
-
-	/* Drop TX queue */
-	skb_queue_purge(&n_hci->txq);
-
-	/* Flush any pending characters in the driver and discipline. */
-	if (tty->ldisc.flush_buffer)
-		tty->ldisc.flush_buffer(tty);
-
-	if (tty->driver.flush_buffer)
-		tty->driver.flush_buffer(tty);
-
-	return 0;
-}
-
-/* Close device */
-int n_hci_close(struct hci_dev *hdev)
-{
-	DBG("hdev %p", hdev);
-
-	hdev->flags &= ~HCI_RUNNING;
-
-	n_hci_flush(hdev);
-
-	return 0;
-}
-
-int n_hci_tx_wakeup(struct n_hci *n_hci)
-{
-	register struct tty_struct *tty = n_hci->tty;
-
-	if (test_and_set_bit(TRANS_SENDING, &n_hci->tx_state)) {
-		set_bit(TRANS_WAKEUP, &n_hci->tx_state);
-		return 0;
-	}
-
-	DBG("");
-	do {
-		register struct sk_buff *skb;
-		register int len;
-
-		clear_bit(TRANS_WAKEUP, &n_hci->tx_state);
-
-		if (!(skb = skb_dequeue(&n_hci->txq)))
-			break;
-
-		DMP(skb->data, skb->len);
-
-		/* Send frame to TTY driver */
-		tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-		len = tty->driver.write(tty, 0, skb->data, skb->len);
-
-		n_hci->hdev.stat.byte_tx += len;
-
-		DBG("sent %d", len);
-
-		if (len == skb->len) {
-			/* Full frame was sent */
-			kfree_skb(skb);
-		} else {
-			/* Subtract sent part and requeue  */
-			skb_pull(skb, len);
-			skb_queue_head(&n_hci->txq, skb);
-		}
-	} while (test_bit(TRANS_WAKEUP, &n_hci->tx_state));
-	clear_bit(TRANS_SENDING, &n_hci->tx_state);
-
-	return 0;
-}
-
-/* Send frames from HCI layer */
-int n_hci_send_frame(struct sk_buff *skb)
-{
-	struct hci_dev* hdev = (struct hci_dev *) skb->dev;
-	struct tty_struct *tty;
-	struct n_hci *n_hci;
-
-	if (!hdev) {
-		ERR("Frame for uknown device (hdev=NULL)");
-		return -ENODEV;
-	}
-
-	if (!(hdev->flags & HCI_RUNNING))
-		return -EBUSY;
-
-	n_hci = (struct n_hci *) hdev->driver_data;
-	tty = n_hci2tty(n_hci);
-
-	DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
-
-	switch (skb->pkt_type) {
-		case HCI_COMMAND_PKT:
-			hdev->stat.cmd_tx++;
-			break;
-
-                case HCI_ACLDATA_PKT:
-			hdev->stat.acl_tx++;
-                        break;
-
-		case HCI_SCODATA_PKT:
-			hdev->stat.cmd_tx++;
-                        break;
-	};
-
-	/* Prepend skb with frame type and queue */
-	memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
-	skb_queue_tail(&n_hci->txq, skb);
-
-	n_hci_tx_wakeup(n_hci);
-
-	return 0;
-}
-
-/* ------ LDISC part ------ */
-
-/* n_hci_tty_open
- * 
- *     Called when line discipline changed to N_HCI.
- *     
- * Arguments:    
- *     tty    pointer to tty info structure
- * Return Value:    
- *     0 if success, otherwise error code
- */
-static int n_hci_tty_open(struct tty_struct *tty)
-{
-	struct n_hci *n_hci = tty2n_hci(tty);
-	struct hci_dev *hdev;
-
-	DBG("tty %p", tty);
-
-	if (n_hci)
-		return -EEXIST;
-
-	if (!(n_hci = kmalloc(sizeof(struct n_hci), GFP_KERNEL))) {
-		ERR("Can't allocate controll structure");
-		return -ENFILE;
-	}
-	memset(n_hci, 0, sizeof(struct n_hci));
-
-	/* Initialize and register HCI device */
-	hdev = &n_hci->hdev;
-
-	hdev->type = HCI_UART;
-	hdev->driver_data = n_hci;
-
-	hdev->open  = n_hci_open;
-	hdev->close = n_hci_close;
-	hdev->flush = n_hci_flush;
-	hdev->send  = n_hci_send_frame;
-
-	if (hci_register_dev(hdev) < 0) {
-		ERR("Can't register HCI device %s", hdev->name);
-		kfree(n_hci);
-		return -ENODEV;
-	}
-
-	tty->disc_data = n_hci;
-	n_hci->tty = tty;
-
-	spin_lock_init(&n_hci->rx_lock);
-	n_hci->rx_state = WAIT_PACKET_TYPE;
-
-	skb_queue_head_init(&n_hci->txq);
-
-	MOD_INC_USE_COUNT;
-
-	/* Flush any pending characters in the driver and discipline. */
-	if (tty->ldisc.flush_buffer)
-		tty->ldisc.flush_buffer(tty);
-
-	if (tty->driver.flush_buffer)
-		tty->driver.flush_buffer(tty);
-
-	return 0;
-}
-
-/* n_hci_tty_close()
- *
- *    Called when the line discipline is changed to something
- *    else, the tty is closed, or the tty detects a hangup.
- */
-static void n_hci_tty_close(struct tty_struct *tty)
-{
-	struct n_hci *n_hci = tty2n_hci(tty);
-	struct hci_dev *hdev = &n_hci->hdev;
-
-	DBG("tty %p hdev %p", tty, hdev);
-
-	if (n_hci != NULL) {
-		n_hci_close(hdev);
-
-		if (hci_unregister_dev(hdev) < 0) {
-			ERR("Can't unregister HCI device %s",hdev->name);
-		}
-
-		hdev->driver_data = NULL;
-		tty->disc_data = NULL;
-		kfree(n_hci);
-
-		MOD_DEC_USE_COUNT;
-	}
-}
-
-/* n_hci_tty_wakeup()
- *
- *    Callback for transmit wakeup. Called when low level
- *    device driver can accept more send data.
- *
- * Arguments:        tty    pointer to associated tty instance data
- * Return Value:    None
- */
-static void n_hci_tty_wakeup( struct tty_struct *tty )
-{
-	struct n_hci *n_hci = tty2n_hci(tty);
-
-	DBG("");
-
-	if (!n_hci)
-		return;
-
-	tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-
-	if (tty != n_hci->tty)
-		return;
-
-	n_hci_tx_wakeup(n_hci);
-}
-
-/* n_hci_tty_room()
- * 
- *    Callback function from tty driver. Return the amount of 
- *    space left in the receiver's buffer to decide if remote
- *    transmitter is to be throttled.
- *
- * Arguments:        tty    pointer to associated tty instance data
- * Return Value:    number of bytes left in receive buffer
- */
-static int n_hci_tty_room (struct tty_struct *tty)
-{
-	return 65536;
-}
-
-static inline int n_hci_check_data_len(struct n_hci *n_hci, int len)
-{
-	register int room = skb_tailroom(n_hci->rx_skb);
-
-	DBG("len %d room %d", len, room);
-	if (!len) {
-		DMP(n_hci->rx_skb->data, n_hci->rx_skb->len);
-		hci_recv_frame(n_hci->rx_skb);
-	} else if (len > room) {
-		ERR("Data length is to large");
-		kfree_skb(n_hci->rx_skb);
-		n_hci->hdev.stat.err_rx++;
-	} else {
-		n_hci->rx_state = WAIT_DATA;
-		n_hci->rx_count = len;
-		return len;
-	}
-
-	n_hci->rx_state = WAIT_PACKET_TYPE;
-	n_hci->rx_skb   = NULL;
-	n_hci->rx_count = 0;
-	return 0;
-}
-
-static inline void n_hci_rx(struct n_hci *n_hci, const __u8 * data, char *flags, int count)
-{
-	register const char *ptr;
-	hci_event_hdr *eh;
-	hci_acl_hdr   *ah;
-	hci_sco_hdr   *sh;
-	register int len, type, dlen;
-
-	DBG("count %d state %ld rx_count %ld", count, n_hci->rx_state, n_hci->rx_count);
-
-	n_hci->hdev.stat.byte_rx += count;
-
-	ptr = data;
-	while (count) {
-		if (n_hci->rx_count) {
-			len = MIN(n_hci->rx_count, count);
-			memcpy(skb_put(n_hci->rx_skb, len), ptr, len);
-			n_hci->rx_count -= len; count -= len; ptr += len;
-
-			if (n_hci->rx_count)
-				continue;
-
-			switch (n_hci->rx_state) {
-				case WAIT_DATA:
-					DBG("Complete data");
-
-					DMP(n_hci->rx_skb->data, n_hci->rx_skb->len);
-
-					hci_recv_frame(n_hci->rx_skb);
-
-					n_hci->rx_state = WAIT_PACKET_TYPE;
-					n_hci->rx_skb = NULL;
-					continue;
-
-				case WAIT_EVENT_HDR:
-					eh = (hci_event_hdr *) n_hci->rx_skb->data;
-
-					DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
-
-					n_hci_check_data_len(n_hci, eh->plen);
-					continue;
-
-				case WAIT_ACL_HDR:
-					ah = (hci_acl_hdr *) n_hci->rx_skb->data;
-					dlen = __le16_to_cpu(ah->dlen);
-
-					DBG("ACL header: dlen %d", dlen);
-
-					n_hci_check_data_len(n_hci, dlen);
-					continue;
-
-				case WAIT_SCO_HDR:
-					sh = (hci_sco_hdr *) n_hci->rx_skb->data;
-
-					DBG("SCO header: dlen %d", sh->dlen);
-
-					n_hci_check_data_len(n_hci, sh->dlen);
-					continue;
-			};
-		}
-
-		/* WAIT_PACKET_TYPE */
-		switch (*ptr) {
-			case HCI_EVENT_PKT:
-				DBG("Event packet");
-				n_hci->rx_state = WAIT_EVENT_HDR;
-				n_hci->rx_count = HCI_EVENT_HDR_SIZE;
-				type = HCI_EVENT_PKT;
-				break;
-
-			case HCI_ACLDATA_PKT:
-				DBG("ACL packet");
-				n_hci->rx_state = WAIT_ACL_HDR;
-				n_hci->rx_count = HCI_ACL_HDR_SIZE;
-				type = HCI_ACLDATA_PKT;
-				break;
-
-			case HCI_SCODATA_PKT:
-				DBG("SCO packet");
-				n_hci->rx_state = WAIT_SCO_HDR;
-				n_hci->rx_count = HCI_SCO_HDR_SIZE;
-				type = HCI_SCODATA_PKT;
-				break;
-
-			default:
-				ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
-				n_hci->hdev.stat.err_rx++;
-				ptr++; count--;
-				continue;
-		};
-		ptr++; count--;
-
-		/* Allocate packet */
-		if (!(n_hci->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
-			ERR("Can't allocate mem for new packet");
-
-			n_hci->rx_state = WAIT_PACKET_TYPE;
-			n_hci->rx_count = 0;
-			return;
-		}
-		n_hci->rx_skb->dev = (void *) &n_hci->hdev;
-		n_hci->rx_skb->pkt_type = type;
-	}
-}
-
-/* n_hci_tty_receive()
- * 
- *     Called by tty low level driver when receive data is
- *     available.
- *     
- * Arguments:  tty          pointer to tty isntance data
- *             data         pointer to received data
- *             flags        pointer to flags for data
- *             count        count of received data in bytes
- *     
- * Return Value:    None
- */
-static void n_hci_tty_receive(struct tty_struct *tty, const __u8 * data, char *flags, int count)
-{
-	struct n_hci *n_hci = tty2n_hci(tty);
-
-	if (!n_hci || tty != n_hci->tty)
-		return;
-
-	spin_lock(&n_hci->rx_lock);
-	n_hci_rx(n_hci, data, flags, count);
-	spin_unlock(&n_hci->rx_lock);
-
-	if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
-		tty->driver.unthrottle(tty);
-}
-
-/* n_hci_tty_ioctl()
- *
- *    Process IOCTL system call for the tty device.
- *
- * Arguments:
- *
- *    tty        pointer to tty instance data
- *    file       pointer to open file object for device
- *    cmd        IOCTL command code
- *    arg        argument for IOCTL call (cmd dependent)
- *
- * Return Value:    Command dependent
- */
-static int n_hci_tty_ioctl (struct tty_struct *tty, struct file * file,
-                            unsigned int cmd, unsigned long arg)
-{
-	struct n_hci *n_hci = tty2n_hci(tty);
-	int error = 0;
-
-	DBG("");
-
-	/* Verify the status of the device */
-	if (!n_hci)
-		return -EBADF;
-
-	switch (cmd) {
-		default:
-			error = n_tty_ioctl(tty, file, cmd, arg);
-			break;
-	};
-
-	return error;
-}
-
-/*
- * We don't provide read/write/poll interface for user space.
- */
-static ssize_t n_hci_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
-{
-	return 0;
-}
-static ssize_t n_hci_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
-{
-	return 0;
-}
-static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
-{
-	return 0;
-}
-
-int __init n_hci_init(void)
-{
-	static struct tty_ldisc n_hci_ldisc;
-	int err;
-
-	INF("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", 
-		VERSION);
-	INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
-
-	/* Register the tty discipline */
-
-	memset(&n_hci_ldisc, 0, sizeof (n_hci_ldisc));
-	n_hci_ldisc.magic       = TTY_LDISC_MAGIC;
-	n_hci_ldisc.name        = "n_hci";
-	n_hci_ldisc.open        = n_hci_tty_open;
-	n_hci_ldisc.close       = n_hci_tty_close;
-	n_hci_ldisc.read        = n_hci_tty_read;
-	n_hci_ldisc.write       = n_hci_tty_write;
-	n_hci_ldisc.ioctl       = n_hci_tty_ioctl;
-	n_hci_ldisc.poll        = n_hci_tty_poll;
-	n_hci_ldisc.receive_room= n_hci_tty_room;
-	n_hci_ldisc.receive_buf = n_hci_tty_receive;
-	n_hci_ldisc.write_wakeup= n_hci_tty_wakeup;
-
-	if ((err = tty_register_ldisc(N_HCI, &n_hci_ldisc))) {
-		ERR("Can't register HCI line discipline (%d)", err);
-		return err;
-	}
-
-	return 0;
-}
-
-void n_hci_cleanup(void)
-{
-	int err;
-
-	/* Release tty registration of line discipline */
-	if ((err = tty_register_ldisc(N_HCI, NULL)))
-		ERR("Can't unregister HCI line discipline (%d)", err);
-}
-
-module_init(n_hci_init);
-module_exit(n_hci_cleanup);
-
-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
-MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
-MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/drivers/bluetooth/hci_uart.h linux-2.4.18-mh15/drivers/bluetooth/hci_uart.h
--- linux-2.4.18/drivers/bluetooth/hci_uart.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_uart.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,82 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: hci_uart.h,v 1.2 2002/09/09 01:17:32 maxk Exp $
+ */
+
+#ifndef N_HCI 
+#define N_HCI	15
+#endif
+
+/* Ioctls */
+#define HCIUARTSETPROTO	_IOW('U', 200, int)
+#define HCIUARTGETPROTO	_IOR('U', 201, int)
+
+/* UART protocols */
+#define HCI_UART_MAX_PROTO	4
+
+#define HCI_UART_H4	0
+#define HCI_UART_BCSP	1
+#define HCI_UART_3WIRE	2
+#define HCI_UART_H4DS	3
+
+#ifdef __KERNEL__
+struct hci_uart;
+
+struct hci_uart_proto {
+	unsigned int id;
+	int (*open)(struct hci_uart *hu);
+	int (*close)(struct hci_uart *hu);
+	int (*flush)(struct hci_uart *hu);
+	int (*recv)(struct hci_uart *hu, void *data, int len);
+	int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
+	struct sk_buff *(*dequeue)(struct hci_uart *hu);
+};
+
+struct hci_uart {
+	struct tty_struct  *tty;
+	struct hci_dev     hdev;
+	unsigned long      flags;
+
+	struct hci_uart_proto *proto;
+	void               *priv;
+	
+	struct sk_buff     *tx_skb;
+	unsigned long      tx_state;
+	spinlock_t         rx_lock;
+};
+
+/* HCI_UART flag bits */
+#define HCI_UART_PROTO_SET		0
+
+/* TX states  */
+#define HCI_UART_SENDING		1
+#define HCI_UART_TX_WAKEUP		2
+
+int hci_uart_register_proto(struct hci_uart_proto *p);
+int hci_uart_unregister_proto(struct hci_uart_proto *p);
+int hci_uart_tx_wakeup(struct hci_uart *hu);
+
+#endif /* __KERNEL__ */
diff -urN linux-2.4.18/drivers/bluetooth/hci_usb.c linux-2.4.18-mh15/drivers/bluetooth/hci_usb.c
--- linux-2.4.18/drivers/bluetooth/hci_usb.c	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_usb.c	2004-08-01 16:26:23.000000000 +0200
@@ -1,9 +1,10 @@
 /* 
-   BlueZ - Bluetooth protocol stack for Linux
+   HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
    Copyright (C) 2000-2001 Qualcomm Incorporated
-
    Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
 
+   Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
+
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2 as
    published by the Free Software Foundation;
@@ -23,598 +24,938 @@
 */
 
 /*
- * BlueZ HCI USB driver.
  * Based on original USB Bluetooth driver for Linux kernel
  *    Copyright (c) 2000 Greg Kroah-Hartman        <greg@kroah.com>
  *    Copyright (c) 2000 Mark Douglas Corner       <mcorner@umich.edu>
  *
- * $Id: hci_usb.c,v 1.5 2001/07/05 18:42:44 maxk Exp $    
+ * $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $    
  */
-#define VERSION "1.0"
+#define VERSION "2.7"
 
 #include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/version.h>
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/sched.h>
+#include <linux/unistd.h>
 #include <linux/types.h>
-#include <linux/fcntl.h>
 #include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/poll.h>
 
 #include <linux/slab.h>
-#include <linux/tty.h>
 #include <linux/errno.h>
 #include <linux/string.h>
-#include <linux/signal.h>
-#include <linux/ioctl.h>
 #include <linux/skbuff.h>
 
 #include <linux/usb.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
 #include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/hci_usb.h>
+
+#include "hci_usb.h"
 
 #ifndef HCI_USB_DEBUG
-#undef  DBG
-#define DBG( A... )
-#undef  DMP
-#define DMP( A... )
+#undef  BT_DBG
+#define BT_DBG( A... )
+#undef  BT_DMP
+#define BT_DMP( A... )
 #endif
 
-static struct usb_device_id usb_bluetooth_ids [] = {
+#ifndef CONFIG_BLUEZ_HCIUSB_ZERO_PACKET
+#undef  USB_ZERO_PACKET
+#define USB_ZERO_PACKET 0
+#endif
+
+static struct usb_driver hci_usb_driver; 
+
+static struct usb_device_id bluetooth_ids[] = {
+	/* Generic Bluetooth USB device */
 	{ USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
+
+	/* AVM BlueFRITZ! USB v2.0 */
+	{ USB_DEVICE(0x057c, 0x3800) },
+
+	/* Bluetooth Ultraport Module from IBM */
+	{ USB_DEVICE(0x04bf, 0x030a) },
+
+	/* ALPS Modules with non-standard id */
+	{ USB_DEVICE(0x044e, 0x3001) },
+	{ USB_DEVICE(0x044e, 0x3002) },
+
+	/* Ericsson with non-standard id */
+	{ USB_DEVICE(0x0bdb, 0x1002) },
+
 	{ }	/* Terminating entry */
 };
 
-MODULE_DEVICE_TABLE (usb, usb_bluetooth_ids);
+MODULE_DEVICE_TABLE (usb, bluetooth_ids);
 
-static int hci_usb_ctrl_msg(struct hci_usb *husb,  struct sk_buff *skb);
-static int hci_usb_write_msg(struct hci_usb *husb, struct sk_buff *skb);
+static struct usb_device_id blacklist_ids[] = {
+	/* Broadcom BCM2033 without firmware */
+	{ USB_DEVICE(0x0a5c, 0x2033), driver_info: HCI_IGNORE },
 
-static void hci_usb_unlink_urbs(struct hci_usb *husb)
+	/* Broadcom BCM2035 */
+	{ USB_DEVICE(0x0a5c, 0x200a), driver_info: HCI_RESET },
+
+	/* ISSC Bluetooth Adapter v3.1 */
+	{ USB_DEVICE(0x1131, 0x1001), driver_info: HCI_RESET },
+
+	/* Digianswer device */
+	{ USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER },
+
+	/* RTX Telecom based adapter with buggy SCO support */
+	{ USB_DEVICE(0x0400, 0x0807), driver_info: HCI_BROKEN_ISOC },
+
+	{ }	/* Terminating entry */
+};
+
+struct _urb *_urb_alloc(int isoc, int gfp)
 {
-	usb_unlink_urb(husb->read_urb);
-	usb_unlink_urb(husb->intr_urb);
-	usb_unlink_urb(husb->ctrl_urb);
-	usb_unlink_urb(husb->write_urb);
+	struct _urb *_urb = kmalloc(sizeof(struct _urb) +
+				sizeof(iso_packet_descriptor_t) * isoc, gfp);
+	if (_urb) {
+		memset(_urb, 0, sizeof(*_urb));
+		spin_lock_init(&_urb->urb.lock);
+	}
+	return _urb;
+}
+
+struct _urb *_urb_dequeue(struct _urb_queue *q)
+{
+	struct _urb *_urb = NULL;
+        unsigned long flags;
+        spin_lock_irqsave(&q->lock, flags);
+	{
+		struct list_head *head = &q->head;
+		struct list_head *next = head->next;
+		if (next != head) {
+			_urb = list_entry(next, struct _urb, list);
+			list_del(next); _urb->queue = NULL;
+		}
+	}
+	spin_unlock_irqrestore(&q->lock, flags);
+	return _urb;
 }
 
-static void hci_usb_free_bufs(struct hci_usb *husb)
+static void hci_usb_rx_complete(struct urb *urb);
+static void hci_usb_tx_complete(struct urb *urb);
+
+#define __pending_tx(husb, type)  (&husb->pending_tx[type-1])
+#define __pending_q(husb, type)   (&husb->pending_q[type-1])
+#define __completed_q(husb, type) (&husb->completed_q[type-1])
+#define __transmit_q(husb, type)  (&husb->transmit_q[type-1])
+#define __reassembly(husb, type)  (husb->reassembly[type-1])
+
+static inline struct _urb *__get_completed(struct hci_usb *husb, int type)
 {
-	if (husb->read_urb) {
-		if (husb->read_urb->transfer_buffer)
-			kfree(husb->read_urb->transfer_buffer);
-		usb_free_urb(husb->read_urb);
-	}
+	return _urb_dequeue(__completed_q(husb, type)); 
+}
 
-	if (husb->intr_urb) {
-		if (husb->intr_urb->transfer_buffer)
-			kfree(husb->intr_urb->transfer_buffer);
-		usb_free_urb(husb->intr_urb);
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
+{
+	int offset = 0, i;
+
+	BT_DBG("len %d mtu %d", len, mtu);
+
+	for (i=0; i < HCI_MAX_ISOC_FRAMES && len >= mtu; i++, offset += mtu, len -= mtu) {
+		urb->iso_frame_desc[i].offset = offset;
+		urb->iso_frame_desc[i].length = mtu;
+		BT_DBG("desc %d offset %d len %d", i, offset, mtu);
+	}
+	if (len && i < HCI_MAX_ISOC_FRAMES) {
+		urb->iso_frame_desc[i].offset = offset;
+		urb->iso_frame_desc[i].length = len;
+		BT_DBG("desc %d offset %d len %d", i, offset, len);
+		i++;
 	}
+	urb->number_of_packets = i;
+}
+#endif
 
-	if (husb->ctrl_urb)
-		usb_free_urb(husb->ctrl_urb);
+static int hci_usb_intr_rx_submit(struct hci_usb *husb)
+{
+	struct _urb *_urb;
+	struct urb *urb;
+	int err, pipe, interval, size;
+	void *buf;
+
+	BT_DBG("%s", husb->hdev.name);
+
+        size = husb->intr_in_ep->wMaxPacketSize;
+
+	buf = kmalloc(size, GFP_ATOMIC);
+	if (!buf)
+		return -ENOMEM;
+
+	_urb = _urb_alloc(0, GFP_ATOMIC);
+	if (!_urb) {
+		kfree(buf);
+		return -ENOMEM;
+	}
+	_urb->type = HCI_EVENT_PKT;
+	_urb_queue_tail(__pending_q(husb, _urb->type), _urb);
+
+	urb = &_urb->urb;
+	pipe     = usb_rcvintpipe(husb->udev, husb->intr_in_ep->bEndpointAddress);
+	interval = husb->intr_in_ep->bInterval;
+	FILL_INT_URB(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb, interval);
+	
+	err = usb_submit_urb(urb);
+	if (err) {
+		BT_ERR("%s intr rx submit failed urb %p err %d",
+				husb->hdev.name, urb, err);
+		_urb_unlink(_urb);
+		_urb_free(_urb);
+		kfree(buf);
+	}
+	return err;
+}
 
-	if (husb->write_urb)
-		usb_free_urb(husb->write_urb);
+static int hci_usb_bulk_rx_submit(struct hci_usb *husb)
+{
+	struct _urb *_urb;
+	struct urb *urb;
+	int err, pipe, size = HCI_MAX_FRAME_SIZE;
+	void *buf;
+
+	buf = kmalloc(size, GFP_ATOMIC);
+	if (!buf)
+		return -ENOMEM;
+
+	_urb = _urb_alloc(0, GFP_ATOMIC);
+	if (!_urb) {
+		kfree(buf);
+		return -ENOMEM;
+	}
+	_urb->type = HCI_ACLDATA_PKT;
+	_urb_queue_tail(__pending_q(husb, _urb->type), _urb);
+
+	urb  = &_urb->urb;
+	pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep->bEndpointAddress);
+        FILL_BULK_URB(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb);
+        urb->transfer_flags = USB_QUEUE_BULK;
+
+	BT_DBG("%s urb %p", husb->hdev.name, urb);
+
+	err = usb_submit_urb(urb);
+	if (err) {
+		BT_ERR("%s bulk rx submit failed urb %p err %d",
+				husb->hdev.name, urb, err);
+		_urb_unlink(_urb);
+		_urb_free(_urb);
+		kfree(buf);
+	}
+	return err;
+}
 
-	if (husb->intr_skb)
-		kfree_skb(husb->intr_skb);
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
+{
+	struct _urb *_urb;
+	struct urb *urb;
+	int err, mtu, size;
+	void *buf;
+
+	mtu  = husb->isoc_in_ep->wMaxPacketSize;
+        size = mtu * HCI_MAX_ISOC_FRAMES;
+
+	buf = kmalloc(size, GFP_ATOMIC);
+	if (!buf)
+		return -ENOMEM;
+
+	_urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);
+	if (!_urb) {
+		kfree(buf);
+		return -ENOMEM;
+	}
+	_urb->type = HCI_SCODATA_PKT;
+	_urb_queue_tail(__pending_q(husb, _urb->type), _urb);
+
+	urb = &_urb->urb;
+
+	urb->context  = husb;
+	urb->dev      = husb->udev;
+	urb->pipe     = usb_rcvisocpipe(husb->udev, husb->isoc_in_ep->bEndpointAddress);
+	urb->complete = hci_usb_rx_complete;
+
+	urb->transfer_buffer_length = size;
+	urb->transfer_buffer = buf;
+	urb->transfer_flags  = USB_ISO_ASAP;
+
+	__fill_isoc_desc(urb, size, mtu);
+
+	BT_DBG("%s urb %p", husb->hdev.name, urb);
+
+	err = usb_submit_urb(urb);
+	if (err) {
+		BT_ERR("%s isoc rx submit failed urb %p err %d",
+				husb->hdev.name, urb, err);
+		_urb_unlink(_urb);
+		_urb_free(_urb);
+		kfree(buf);
+	}
+	return err;
 }
+#endif
 
-/* ------- Interface to HCI layer ------ */
 /* Initialize device */
-int hci_usb_open(struct hci_dev *hdev)
+static int hci_usb_open(struct hci_dev *hdev)
 {
 	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
-	int status;
+	int i, err;
+	unsigned long flags;
+
+	BT_DBG("%s", hdev->name);
 
-	DBG("%s", hdev->name);
+	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
 
-	husb->read_urb->dev = husb->udev;
-	if ((status = usb_submit_urb(husb->read_urb)))
-		DBG("read submit failed. %d", status);
+	MOD_INC_USE_COUNT;
 
-	husb->intr_urb->dev = husb->udev;
-	if ((status = usb_submit_urb(husb->intr_urb)))
-		DBG("interrupt submit failed. %d", status);
+	write_lock_irqsave(&husb->completion_lock, flags);
 
-	hdev->flags |= HCI_RUNNING;
+	err = hci_usb_intr_rx_submit(husb);
+	if (!err) {
+		for (i = 0; i < HCI_MAX_BULK_RX; i++)
+			hci_usb_bulk_rx_submit(husb);
+
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+		if (husb->isoc_iface)
+			for (i = 0; i < HCI_MAX_ISOC_RX; i++)
+				hci_usb_isoc_rx_submit(husb);
+#endif
+	} else {
+		clear_bit(HCI_RUNNING, &hdev->flags);
+		MOD_DEC_USE_COUNT;
+	}
 
-	return 0;
+	write_unlock_irqrestore(&husb->completion_lock, flags);
+	return err;
 }
 
 /* Reset device */
-int hci_usb_flush(struct hci_dev *hdev)
+static int hci_usb_flush(struct hci_dev *hdev)
 {
 	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+	int i;
 
-	DBG("%s", hdev->name);
-
-	/* Drop TX queues */
-	skb_queue_purge(&husb->tx_ctrl_q);
-	skb_queue_purge(&husb->tx_write_q);
+	BT_DBG("%s", hdev->name);
 
+	for (i=0; i < 4; i++)
+		skb_queue_purge(&husb->transmit_q[i]);
 	return 0;
 }
 
-/* Close device */
-int hci_usb_close(struct hci_dev *hdev)
+static void hci_usb_unlink_urbs(struct hci_usb *husb)
 {
-	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+	int i;
 
-	DBG("%s", hdev->name);
+	BT_DBG("%s", husb->hdev.name);
 
-	hdev->flags &= ~HCI_RUNNING;
-	hci_usb_unlink_urbs(husb);
+	for (i=0; i < 4; i++) {
+		struct _urb *_urb;
+		struct urb *urb;
+
+		/* Kill pending requests */
+		while ((_urb = _urb_dequeue(&husb->pending_q[i]))) {
+			urb = &_urb->urb;
+			BT_DBG("%s unlinking _urb %p type %d urb %p", 
+					husb->hdev.name, _urb, _urb->type, urb);
+			usb_unlink_urb(urb);
+			_urb_queue_tail(__completed_q(husb, _urb->type), _urb);
+		}
 
-	hci_usb_flush(hdev);
+		/* Release completed requests */
+		while ((_urb = _urb_dequeue(&husb->completed_q[i]))) {
+			urb = &_urb->urb;
+			BT_DBG("%s freeing _urb %p type %d urb %p",
+					husb->hdev.name, _urb, _urb->type, urb);
+			if (urb->setup_packet)
+				kfree(urb->setup_packet);
+			if (urb->transfer_buffer)
+				kfree(urb->transfer_buffer);
+			_urb_free(_urb);
+		}
 
-	return 0;
+		/* Release reassembly buffers */
+		if (husb->reassembly[i]) {
+			kfree_skb(husb->reassembly[i]);
+			husb->reassembly[i] = NULL;
+		}
+	}
 }
 
-void hci_usb_ctrl_wakeup(struct hci_usb *husb)
+/* Close device */
+static int hci_usb_close(struct hci_dev *hdev)
 {
-	struct sk_buff *skb;
+	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+	unsigned long flags;
+	
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
 
-	if (test_and_set_bit(HCI_TX_CTRL, &husb->tx_state))
-		return;
+	BT_DBG("%s", hdev->name);
 
-	DBG("%s", husb->hdev.name);
+	write_lock_irqsave(&husb->completion_lock, flags);
+	
+	hci_usb_unlink_urbs(husb);
+	hci_usb_flush(hdev);
 
-	if (!(skb = skb_dequeue(&husb->tx_ctrl_q)))
-		goto done;
+	write_unlock_irqrestore(&husb->completion_lock, flags);
 
-	if (hci_usb_ctrl_msg(husb, skb)){
-		kfree_skb(skb);
-		goto done;
-	}
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
 
-	DMP(skb->data, skb->len);
+static int __tx_submit(struct hci_usb *husb, struct _urb *_urb)
+{
+	struct urb *urb = &_urb->urb;
+	int err;
 
-	husb->hdev.stat.byte_tx += skb->len;
-	return;
+	BT_DBG("%s urb %p type %d", husb->hdev.name, urb, _urb->type);
+	
+	_urb_queue_tail(__pending_q(husb, _urb->type), _urb);
+	err = usb_submit_urb(urb);
+	if (err) {
+		BT_ERR("%s tx submit failed urb %p type %d err %d",
+				husb->hdev.name, urb, _urb->type, err);
+		_urb_unlink(_urb);
+		_urb_queue_tail(__completed_q(husb, _urb->type), _urb);
+	} else
+		atomic_inc(__pending_tx(husb, _urb->type));
 
-done:
-	clear_bit(HCI_TX_CTRL, &husb->tx_state);
-	return;
+	return err;
 }
 
-void hci_usb_write_wakeup(struct hci_usb *husb)
+static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb)
 {
-	struct sk_buff *skb;
+	struct _urb *_urb = __get_completed(husb, skb->pkt_type);
+	devrequest *dr;
+	struct urb *urb;
+
+	if (!_urb) {
+	       	_urb = _urb_alloc(0, GFP_ATOMIC);
+	       	if (!_urb)
+			return -ENOMEM;
+		_urb->type = skb->pkt_type;
+
+		dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+		if (!dr) {
+			_urb_free(_urb);
+			return -ENOMEM;
+		}
+	} else
+		dr = (void *) _urb->urb.setup_packet;
 
-	if (test_and_set_bit(HCI_TX_WRITE, &husb->tx_state))
-		return;
+	dr->requesttype = husb->ctrl_req;
+	dr->request = 0;
+	dr->index   = 0;
+	dr->value   = 0;
+	dr->length  = __cpu_to_le16(skb->len);
 
-	DBG("%s", husb->hdev.name);
+	urb = &_urb->urb;
+	FILL_CONTROL_URB(urb, husb->udev, usb_sndctrlpipe(husb->udev, 0),
+		(void *) dr, skb->data, skb->len, hci_usb_tx_complete, husb);
 
-	if (!(skb = skb_dequeue(&husb->tx_write_q)))
-		goto done;
+	BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
+	
+	_urb->priv = skb;
+	return __tx_submit(husb, _urb);
+}
 
-	if (hci_usb_write_msg(husb, skb)) {
-		skb_queue_head(&husb->tx_write_q, skb);
-		goto done;
+static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb)
+{
+	struct _urb *_urb = __get_completed(husb, skb->pkt_type);
+	struct urb *urb;
+	int pipe;
+
+	if (!_urb) {
+	       	_urb = _urb_alloc(0, GFP_ATOMIC);
+	       	if (!_urb)
+			return -ENOMEM;
+		_urb->type = skb->pkt_type;
 	}
 
-	DMP(skb->data, skb->len);
+	urb  = &_urb->urb;
+	pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep->bEndpointAddress);
+	FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len, 
+			hci_usb_tx_complete, husb);
+	urb->transfer_flags = USB_QUEUE_BULK | USB_ZERO_PACKET;
 
-	husb->hdev.stat.byte_tx += skb->len;
-	return;
+	BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
 
-done:
-	clear_bit(HCI_TX_WRITE, &husb->tx_state);
-	return;
+	_urb->priv = skb;
+	return __tx_submit(husb, _urb);
 }
 
-/* Send frames from HCI layer */
-int hci_usb_send_frame(struct sk_buff *skb)
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
 {
-	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-	struct hci_usb *husb;
-
-	if (!hdev) {
-		ERR("frame for uknown device (hdev=NULL)");
-		return -ENODEV;
+	struct _urb *_urb = __get_completed(husb, skb->pkt_type);
+	struct urb *urb;
+	
+	if (!_urb) {
+	       	_urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);
+	       	if (!_urb)
+			return -ENOMEM;
+		_urb->type = skb->pkt_type;
 	}
 
-	if (!(hdev->flags & HCI_RUNNING))
-		return 0;
+	BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
 
-	husb = (struct hci_usb *) hdev->driver_data;
+	urb = &_urb->urb;
+	
+	urb->context  = husb;
+	urb->dev      = husb->udev;
+	urb->pipe     = usb_sndisocpipe(husb->udev, husb->isoc_out_ep->bEndpointAddress);
+	urb->complete = hci_usb_tx_complete;
+	urb->transfer_flags = USB_ISO_ASAP;
 
-	DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+	urb->transfer_buffer = skb->data;
+	urb->transfer_buffer_length = skb->len;
+	
+	__fill_isoc_desc(urb, skb->len, husb->isoc_out_ep->wMaxPacketSize);
 
-	switch (skb->pkt_type) {
-		case HCI_COMMAND_PKT:
-			skb_queue_tail(&husb->tx_ctrl_q, skb);
-			hci_usb_ctrl_wakeup(husb);
-			hdev->stat.cmd_tx++;
-			return 0;
-
-		case HCI_ACLDATA_PKT:
-			skb_queue_tail(&husb->tx_write_q, skb);
-			hci_usb_write_wakeup(husb);
-			hdev->stat.acl_tx++;
-			return 0;
-
-		case HCI_SCODATA_PKT:
-			return -EOPNOTSUPP;
-	};
-
-	return 0;
+	_urb->priv = skb;
+	return __tx_submit(husb, _urb);
 }
+#endif
 
-/* ---------- USB ------------- */
-
-static void hci_usb_ctrl(struct urb *urb)
+static void hci_usb_tx_process(struct hci_usb *husb)
 {
-	struct sk_buff *skb = (struct sk_buff *) urb->context;
-	struct hci_dev *hdev;
-	struct hci_usb *husb;
+	struct sk_buff_head *q;
+	struct sk_buff *skb;
 
-	if (!skb)
-		return;
-	hdev = (struct hci_dev *) skb->dev;
-	husb = (struct hci_usb *) hdev->driver_data;
+	BT_DBG("%s", husb->hdev.name);
 
-	DBG("%s", hdev->name);
+	do {
+		clear_bit(HCI_USB_TX_WAKEUP, &husb->state);
 
-	if (urb->status)
-		DBG("%s ctrl status: %d", hdev->name, urb->status);
+		/* Process command queue */
+		q = __transmit_q(husb, HCI_COMMAND_PKT);
+		if (!atomic_read(__pending_tx(husb, HCI_COMMAND_PKT)) &&
+				(skb = skb_dequeue(q))) {
+			if (hci_usb_send_ctrl(husb, skb) < 0)
+				skb_queue_head(q, skb);
+		}
 
-	clear_bit(HCI_TX_CTRL, &husb->tx_state);
-	kfree_skb(skb);
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+		/* Process SCO queue */
+		q = __transmit_q(husb, HCI_SCODATA_PKT);
+		if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX &&
+				(skb = skb_dequeue(q))) {
+			if (hci_usb_send_isoc(husb, skb) < 0)
+				skb_queue_head(q, skb);
+		}
+#endif
+		
+		/* Process ACL queue */
+		q = __transmit_q(husb, HCI_ACLDATA_PKT);
+		while (atomic_read(__pending_tx(husb, HCI_ACLDATA_PKT)) < HCI_MAX_BULK_TX &&
+				(skb = skb_dequeue(q))) {
+			if (hci_usb_send_bulk(husb, skb) < 0) {
+				skb_queue_head(q, skb);
+				break;
+			}
+		}
+	} while(test_bit(HCI_USB_TX_WAKEUP, &husb->state));
+}
 
-	/* Wake up device */
-	hci_usb_ctrl_wakeup(husb);
+static inline void hci_usb_tx_wakeup(struct hci_usb *husb)
+{
+	/* Serialize TX queue processing to avoid data reordering */
+	if (!test_and_set_bit(HCI_USB_TX_PROCESS, &husb->state)) {
+		hci_usb_tx_process(husb);
+		clear_bit(HCI_USB_TX_PROCESS, &husb->state);
+	} else
+		set_bit(HCI_USB_TX_WAKEUP, &husb->state);
 }
 
-static void hci_usb_bulk_write(struct urb *urb)
+/* Send frames from HCI layer */
+static int hci_usb_send_frame(struct sk_buff *skb)
 {
-	struct sk_buff *skb = (struct sk_buff *) urb->context;
-	struct hci_dev *hdev;
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
 	struct hci_usb *husb;
 
-	if (!skb)
-		return;
-	hdev = (struct hci_dev *) skb->dev;
-	husb = (struct hci_usb *) hdev->driver_data;
+	if (!hdev) {
+		BT_ERR("frame for uknown device (hdev=NULL)");
+		return -ENODEV;
+	}
 
-	DBG("%s", hdev->name);
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
 
-	if (urb->status)
-		DBG("%s bulk write status: %d", hdev->name, urb->status);
+	BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
 
-	clear_bit(HCI_TX_WRITE, &husb->tx_state);
-	kfree_skb(skb);
+	husb = (struct hci_usb *) hdev->driver_data;
 
-	/* Wake up device */
-	hci_usb_write_wakeup(husb);
+	switch (skb->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+#endif
 
-	return;
-}
+	default:
+		kfree_skb(skb);
+		return 0;
+	}
 
-static void hci_usb_intr(struct urb *urb)
-{
-	struct hci_usb *husb = (struct hci_usb *) urb->context;
-	unsigned char *data = urb->transfer_buffer;
-	register int count  = urb->actual_length;
-	register struct sk_buff *skb = husb->intr_skb;
-	hci_event_hdr *eh;
-	register int len;
+	read_lock(&husb->completion_lock);
 
-	if (!husb)
-		return;
+	skb_queue_tail(__transmit_q(husb, skb->pkt_type), skb);
+	hci_usb_tx_wakeup(husb);
 
-	DBG("%s count %d", husb->hdev.name, count);
+	read_unlock(&husb->completion_lock);
+	return 0;
+}
 
-	if (urb->status || !count) {
-		DBG("%s intr status %d, count %d", husb->hdev.name, urb->status, count);
-		return;
-	}
+static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int count)
+{
+	BT_DBG("%s type %d data %p count %d", husb->hdev.name, type, data, count);
 
-	/* Do we really have to handle continuations here ? */
-	if (!skb) {
-		/* New frame */
-		if (count < HCI_EVENT_HDR_SIZE) {
-			DBG("%s bad frame len %d", husb->hdev.name, count);
-			return;
-		}
+	husb->hdev.stat.byte_rx += count;
 
-		eh = (hci_event_hdr *) data;
-		len = eh->plen + HCI_EVENT_HDR_SIZE;
+	while (count) {
+		struct sk_buff *skb = __reassembly(husb, type);
+		struct { int expect; } *scb;
+		int len = 0;
+	
+		if (!skb) {
+			/* Start of the frame */
+
+			switch (type) {
+			case HCI_EVENT_PKT:
+				if (count >= HCI_EVENT_HDR_SIZE) {
+					hci_event_hdr *h = data;
+					len = HCI_EVENT_HDR_SIZE + h->plen;
+				} else
+					return -EILSEQ;
+				break;
 
-		if (count > len) {
-			DBG("%s corrupted frame, len %d", husb->hdev.name, count);
-			return;
-		}
+			case HCI_ACLDATA_PKT:
+				if (count >= HCI_ACL_HDR_SIZE) {
+					hci_acl_hdr *h = data;
+					len = HCI_ACL_HDR_SIZE + __le16_to_cpu(h->dlen);
+				} else
+					return -EILSEQ;
+				break;
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+			case HCI_SCODATA_PKT:
+				if (count >= HCI_SCO_HDR_SIZE) {
+					hci_sco_hdr *h = data;
+					len = HCI_SCO_HDR_SIZE + h->dlen;
+				} else 
+					return -EILSEQ;
+				break;
+#endif
+			}
+			BT_DBG("new packet len %d", len);
 
-		/* Allocate skb */
-		if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) {
-			ERR("Can't allocate mem for new packet");
-			return;
+			skb = bluez_skb_alloc(len, GFP_ATOMIC);
+			if (!skb) {
+				BT_ERR("%s no memory for the packet", husb->hdev.name);
+				return -ENOMEM;
+			}
+			skb->dev = (void *) &husb->hdev;
+			skb->pkt_type = type;
+	
+			__reassembly(husb, type) = skb;
+
+			scb = (void *) skb->cb;
+			scb->expect = len;
+		} else {
+			/* Continuation */
+			scb = (void *) skb->cb;
+			len = scb->expect;
 		}
-		skb->dev = (void *) &husb->hdev;
-		skb->pkt_type = HCI_EVENT_PKT;
-
-		husb->intr_skb = skb;
-		husb->intr_count = len;
-	} else {
-		/* Continuation */
-		if (count > husb->intr_count) {
-			ERR("%s bad frame len %d (expected %d)", husb->hdev.name, count, husb->intr_count);
 
-			kfree_skb(skb);
-			husb->intr_skb = NULL;
-			husb->intr_count = 0;
-			return;
+		len = min(len, count);
+		
+		memcpy(skb_put(skb, len), data, len);
+
+		scb->expect -= len;
+		if (!scb->expect) {
+			/* Complete frame */
+			__reassembly(husb, type) = NULL;
+			hci_recv_frame(skb);
 		}
-	}
-
-	memcpy(skb_put(skb, count), data, count);
-	husb->intr_count -= count;
-
-	DMP(data, count);
-
-	if (!husb->intr_count) {
-		/* Got complete frame */
 
-		husb->hdev.stat.byte_rx += skb->len;
-		hci_recv_frame(skb);
-
-		husb->intr_skb = NULL;
+		count -= len; data += len;
 	}
+	return 0;
 }
 
-static void hci_usb_bulk_read(struct urb *urb)
+static void hci_usb_rx_complete(struct urb *urb)
 {
-	struct hci_usb *husb = (struct hci_usb *) urb->context;
-	unsigned char *data = urb->transfer_buffer;
-	int count = urb->actual_length, status;
-	struct sk_buff *skb;
-	hci_acl_hdr *ah;
-	register __u16 dlen;
-
-	if (!husb)
-		return;
+	struct _urb *_urb = container_of(urb, struct _urb, urb);
+	struct hci_usb *husb = (void *) urb->context;
+	struct hci_dev *hdev = &husb->hdev;
+	int    err, count = urb->actual_length;
 
-	DBG("%s status %d, count %d, flags %x", husb->hdev.name, urb->status, count, urb->transfer_flags);
+	BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,
+			_urb->type, urb->status, count, urb->transfer_flags);
 
-	if (urb->status) {
-		/* Do not re-submit URB on critical errors */
-		switch (urb->status) {
-			case -ENOENT:
-				return;
-			default:
-				goto resubmit;
-		};
-	}
-	if (!count)
-		goto resubmit;
-
-	DMP(data, count);
+	read_lock(&husb->completion_lock);
 
-	ah = (hci_acl_hdr *) data;
-	dlen = le16_to_cpu(ah->dlen);
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		goto unlock;
 
-	/* Verify frame len and completeness */
-	if ((count - HCI_ACL_HDR_SIZE) != dlen) {
-		ERR("%s corrupted ACL packet: count %d, plen %d", husb->hdev.name, count, dlen);
+	if (urb->status || !count)
 		goto resubmit;
-	}
 
-	/* Allocate packet */
-	if (!(skb = bluez_skb_alloc(count, GFP_ATOMIC))) {
-		ERR("Can't allocate mem for new packet");
-		goto resubmit;
+	if (_urb->type == HCI_SCODATA_PKT) {
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+		int i;
+		for (i=0; i < urb->number_of_packets; i++) {
+			BT_DBG("desc %d status %d offset %d len %d", i,
+					urb->iso_frame_desc[i].status,
+					urb->iso_frame_desc[i].offset,
+					urb->iso_frame_desc[i].actual_length);
+	
+			if (!urb->iso_frame_desc[i].status)
+				__recv_frame(husb, _urb->type, 
+					urb->transfer_buffer + urb->iso_frame_desc[i].offset,
+					urb->iso_frame_desc[i].actual_length);
+		}
+#else
+		;
+#endif
+	} else {
+		err = __recv_frame(husb, _urb->type, urb->transfer_buffer, count);
+		if (err < 0) { 
+			BT_ERR("%s corrupted packet: type %d count %d",
+					husb->hdev.name, _urb->type, count);
+			hdev->stat.err_rx++;
+		}
 	}
 
-	memcpy(skb_put(skb, count), data, count);
-	skb->dev = (void *) &husb->hdev;
-	skb->pkt_type = HCI_ACLDATA_PKT;
-
-	husb->hdev.stat.byte_rx += skb->len;
-
-	hci_recv_frame(skb);
-
 resubmit:
-	husb->read_urb->dev = husb->udev;
-	if ((status = usb_submit_urb(husb->read_urb)))
-		DBG("%s read URB submit failed %d", husb->hdev.name, status);
+	if (_urb->type != HCI_EVENT_PKT) {
+		urb->dev = husb->udev;
+		err      = usb_submit_urb(urb);
+		BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,
+				_urb->type, err);
+	}
 
-	DBG("%s read URB re-submited", husb->hdev.name);
+unlock:
+	read_unlock(&husb->completion_lock);
 }
 
-static int hci_usb_ctrl_msg(struct hci_usb *husb, struct sk_buff *skb)
+static void hci_usb_tx_complete(struct urb *urb)
 {
-	struct urb *urb = husb->ctrl_urb;
-	devrequest *dr  = &husb->dev_req;
-	int pipe, status;
+	struct _urb *_urb = container_of(urb, struct _urb, urb);
+	struct hci_usb *husb = (void *) urb->context;
+	struct hci_dev *hdev = &husb->hdev;
 
-	DBG("%s len %d", husb->hdev.name, skb->len);
+	BT_DBG("%s urb %p status %d flags %x", hdev->name, urb,
+			urb->status, urb->transfer_flags);
 
-	pipe = usb_sndctrlpipe(husb->udev, 0);
+	atomic_dec(__pending_tx(husb, _urb->type));
 
-	dr->requesttype = HCI_CTRL_REQ;
-	dr->request = 0;
-	dr->index   = 0;
-	dr->value   = 0;
-	dr->length  = cpu_to_le16(skb->len);
-
-	FILL_CONTROL_URB(urb, husb->udev, pipe, (void*)dr, skb->data, skb->len,
-	                 hci_usb_ctrl, skb);
+	urb->transfer_buffer = NULL;
+	kfree_skb((struct sk_buff *) _urb->priv);
 
-	if ((status = usb_submit_urb(urb))) {
-		DBG("%s control URB submit failed %d", husb->hdev.name, status);
-		return status;
-	}
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return;
 
-	return 0;
-}
+	if (!urb->status)
+		hdev->stat.byte_tx += urb->transfer_buffer_length;
+	else
+		hdev->stat.err_tx++;
 
-static int hci_usb_write_msg(struct hci_usb *husb, struct sk_buff *skb)
-{
-	struct urb *urb = husb->write_urb;
-	int pipe, status;
+	read_lock(&husb->completion_lock);
 
-	DBG("%s len %d", husb->hdev.name, skb->len);
+	_urb_unlink(_urb);
+	_urb_queue_tail(__completed_q(husb, _urb->type), _urb);
 
-	pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep_addr);
+	hci_usb_tx_wakeup(husb);
+	
+	read_unlock(&husb->completion_lock);
+}
 
-	FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len,
-	              hci_usb_bulk_write, skb);
-	urb->transfer_flags |= USB_QUEUE_BULK;
+static void hci_usb_destruct(struct hci_dev *hdev)
+{
+	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
 
-	if ((status = usb_submit_urb(urb))) {
-		DBG("%s write URB submit failed %d", husb->hdev.name, status);
-		return status;
-	}
+	BT_DBG("%s", hdev->name);
 
-	return 0;
+	kfree(husb);
 }
 
-static void * hci_usb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
+static void *hci_usb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
 {
-	struct usb_endpoint_descriptor *bulk_out_ep, *intr_in_ep, *bulk_in_ep;
+	struct usb_endpoint_descriptor *bulk_out_ep[HCI_MAX_IFACE_NUM];
+	struct usb_endpoint_descriptor *isoc_out_ep[HCI_MAX_IFACE_NUM];
+	struct usb_endpoint_descriptor *bulk_in_ep[HCI_MAX_IFACE_NUM];
+	struct usb_endpoint_descriptor *isoc_in_ep[HCI_MAX_IFACE_NUM];
+	struct usb_endpoint_descriptor *intr_in_ep[HCI_MAX_IFACE_NUM];
 	struct usb_interface_descriptor *uif;
 	struct usb_endpoint_descriptor *ep;
+	struct usb_interface *iface, *isoc_iface;
 	struct hci_usb *husb;
 	struct hci_dev *hdev;
-	int i, size, pipe;
-	__u8 * buf;
+	int i, a, e, size, ifn, isoc_ifnum, isoc_alts;
 
-	DBG("udev %p ifnum %d", udev, ifnum);
+	BT_DBG("udev %p ifnum %d", udev, ifnum);
 
-	/* Check device signature */
-	if ((udev->descriptor.bDeviceClass    != HCI_DEV_CLASS)   ||
-	    (udev->descriptor.bDeviceSubClass != HCI_DEV_SUBCLASS)||
-	    (udev->descriptor.bDeviceProtocol != HCI_DEV_PROTOCOL) )
-		return NULL;
+	iface = &udev->actconfig->interface[0];
 
-	MOD_INC_USE_COUNT;
-
-	uif = &udev->actconfig->interface[ifnum].altsetting[0];
+	if (!id->driver_info) {
+		const struct usb_device_id *match;
+		match = usb_match_id(udev, iface, blacklist_ids);
+		if (match)
+			id = match;
+	}
 
-	if (uif->bNumEndpoints != 3) {
-		DBG("Wrong number of endpoints %d", uif->bNumEndpoints);
-		MOD_DEC_USE_COUNT;
+	if (id->driver_info & HCI_IGNORE)
 		return NULL;
-	}
 
-	bulk_out_ep = intr_in_ep = bulk_in_ep = NULL;
+	/* Check number of endpoints */
+	if (udev->actconfig->interface[ifnum].altsetting[0].bNumEndpoints < 3)
+		return NULL;
 
+	memset(bulk_out_ep, 0, sizeof(bulk_out_ep));
+	memset(isoc_out_ep, 0, sizeof(isoc_out_ep));
+	memset(bulk_in_ep,  0, sizeof(bulk_in_ep));
+	memset(isoc_in_ep,  0, sizeof(isoc_in_ep));
+	memset(intr_in_ep,  0, sizeof(intr_in_ep));
+
+	size = 0; 
+	isoc_iface = NULL;
+	isoc_alts  = isoc_ifnum = 0;
+	
 	/* Find endpoints that we need */
-	for ( i = 0; i < uif->bNumEndpoints; ++i) {
-		ep = &uif->endpoint[i];
 
-		switch (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
-			case USB_ENDPOINT_XFER_BULK:
-				if (ep->bEndpointAddress & USB_DIR_IN)
-					bulk_in_ep  = ep;
-				else
-					bulk_out_ep = ep;
-				break;
+	ifn = MIN(udev->actconfig->bNumInterfaces, HCI_MAX_IFACE_NUM);
+	for (i = 0; i < ifn; i++) {
+		iface = &udev->actconfig->interface[i];
+		for (a = 0; a < iface->num_altsetting; a++) {
+			uif = &iface->altsetting[a];
+			for (e = 0; e < uif->bNumEndpoints; e++) {
+				ep = &uif->endpoint[e];
+
+				switch (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+				case USB_ENDPOINT_XFER_INT:
+					if (ep->bEndpointAddress & USB_DIR_IN)
+						intr_in_ep[i] = ep;
+					break;
+
+				case USB_ENDPOINT_XFER_BULK:
+					if (ep->bEndpointAddress & USB_DIR_IN)
+						bulk_in_ep[i]  = ep;
+					else
+						bulk_out_ep[i] = ep;
+					break;
+
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+				case USB_ENDPOINT_XFER_ISOC:
+					if (ep->wMaxPacketSize < size || a > 2)
+						break;
+					size = ep->wMaxPacketSize;
+
+					isoc_iface = iface;
+					isoc_alts  = a;
+					isoc_ifnum = i;
+
+					if (ep->bEndpointAddress & USB_DIR_IN)
+						isoc_in_ep[i]  = ep;
+					else
+						isoc_out_ep[i] = ep;
+					break;
+#endif
+				}
+			}
+		}
+	}
 
-			case USB_ENDPOINT_XFER_INT:
-				intr_in_ep = ep;
-				break;
-		};
+	if (!bulk_in_ep[0] || !bulk_out_ep[0] || !intr_in_ep[0]) {
+		BT_DBG("Bulk endpoints not found");
+		goto done;
 	}
 
-	if (!bulk_in_ep || !bulk_out_ep || !intr_in_ep) {
-		DBG("Endpoints not found: %p %p %p", bulk_in_ep, bulk_out_ep, intr_in_ep);
-		MOD_DEC_USE_COUNT;
-		return NULL;
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+	if (id->driver_info & HCI_BROKEN_ISOC || !isoc_in_ep[1] || !isoc_out_ep[1]) {
+		BT_DBG("Isoc endpoints not found");
+		isoc_iface = NULL;
 	}
+#endif
 
 	if (!(husb = kmalloc(sizeof(struct hci_usb), GFP_KERNEL))) {
-		ERR("Can't allocate: control structure");
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		BT_ERR("Can't allocate: control structure");
+		goto done;
 	}
 
 	memset(husb, 0, sizeof(struct hci_usb));
 
 	husb->udev = udev;
-	husb->bulk_out_ep_addr = bulk_out_ep->bEndpointAddress;
-
-	if (!(husb->ctrl_urb = usb_alloc_urb(0))) {
-		ERR("Can't allocate: control URB");
-		goto probe_error;
-	}
-
-	if (!(husb->write_urb = usb_alloc_urb(0))) {
-		ERR("Can't allocate: write URB");
-		goto probe_error;
-	}
-
-	if (!(husb->read_urb = usb_alloc_urb(0))) {
-		ERR("Can't allocate: read URB");
-		goto probe_error;
-	}
-
-	ep = bulk_in_ep;
-	pipe = usb_rcvbulkpipe(udev, ep->bEndpointAddress);
-	size = HCI_MAX_FRAME_SIZE;
-
-	if (!(buf = kmalloc(size, GFP_KERNEL))) {
-		ERR("Can't allocate: read buffer");
-		goto probe_error;
-	}
-
-	FILL_BULK_URB(husb->read_urb, udev, pipe, buf, size, hci_usb_bulk_read, husb);
-	husb->read_urb->transfer_flags |= USB_QUEUE_BULK;
-
-	ep = intr_in_ep;
-	pipe = usb_rcvintpipe(udev, ep->bEndpointAddress);
-	size = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
-
-	if (!(husb->intr_urb = usb_alloc_urb(0))) {
-		ERR("Can't allocate: interrupt URB");
-		goto probe_error;
+	husb->bulk_out_ep = bulk_out_ep[0];
+	husb->bulk_in_ep  = bulk_in_ep[0];
+	husb->intr_in_ep  = intr_in_ep[0];
+
+	if (id->driver_info & HCI_DIGIANSWER)
+		husb->ctrl_req = HCI_DIGI_REQ;
+	else
+		husb->ctrl_req = HCI_CTRL_REQ;
+
+#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+	if (isoc_iface) {
+		BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);
+		if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
+			BT_ERR("Can't set isoc interface settings");
+			isoc_iface = NULL;
+		}
+		usb_driver_claim_interface(&hci_usb_driver, isoc_iface, husb);
+		husb->isoc_iface  = isoc_iface;
+		husb->isoc_in_ep  = isoc_in_ep[isoc_ifnum];
+		husb->isoc_out_ep = isoc_out_ep[isoc_ifnum];
 	}
+#endif
+	
+	husb->completion_lock = RW_LOCK_UNLOCKED;
 
-	if (!(buf = kmalloc(size, GFP_KERNEL))) {
-		ERR("Can't allocate: interrupt buffer");
-		goto probe_error;
+	for (i = 0; i < 4; i++) {	
+		skb_queue_head_init(&husb->transmit_q[i]);
+		_urb_queue_init(&husb->pending_q[i]);
+		_urb_queue_init(&husb->completed_q[i]);
 	}
 
-	FILL_INT_URB(husb->intr_urb, udev, pipe, buf, size, hci_usb_intr, husb, ep->bInterval);
-
-	skb_queue_head_init(&husb->tx_ctrl_q);
-	skb_queue_head_init(&husb->tx_write_q);
-
 	/* Initialize and register HCI device */
 	hdev = &husb->hdev;
 
-	hdev->type = HCI_USB;
+	hdev->type  = HCI_USB;
 	hdev->driver_data = husb;
 
 	hdev->open  = hci_usb_open;
 	hdev->close = hci_usb_close;
 	hdev->flush = hci_usb_flush;
-	hdev->send	= hci_usb_send_frame;
+	hdev->send  = hci_usb_send_frame;
+	hdev->destruct = hci_usb_destruct;
+
+	if (id->driver_info & HCI_RESET)
+		set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
 
 	if (hci_register_dev(hdev) < 0) {
-		ERR("Can't register HCI device %s", hdev->name);
+		BT_ERR("Can't register HCI device");
 		goto probe_error;
 	}
 
 	return husb;
 
 probe_error:
-	hci_usb_free_bufs(husb);
 	kfree(husb);
-	MOD_DEC_USE_COUNT;
+
+done:
 	return NULL;
 }
 
@@ -626,38 +967,34 @@
 	if (!husb)
 		return;
 
-	DBG("%s", hdev->name);
+	BT_DBG("%s", hdev->name);
 
 	hci_usb_close(hdev);
 
-	if (hci_unregister_dev(hdev) < 0) {
-		ERR("Can't unregister HCI device %s", hdev->name);
-	}
+	if (husb->isoc_iface)
+		usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface);
 
-	hci_usb_free_bufs(husb);
-	kfree(husb);
-
-	MOD_DEC_USE_COUNT;
+	if (hci_unregister_dev(hdev) < 0)
+		BT_ERR("Can't unregister HCI device %s", hdev->name);
 }
 
-static struct usb_driver hci_usb_driver =
-{
+static struct usb_driver hci_usb_driver = {
 	name:           "hci_usb",
 	probe:          hci_usb_probe,
 	disconnect:     hci_usb_disconnect,
-	id_table:       usb_bluetooth_ids,
+	id_table:       bluetooth_ids,
 };
 
 int hci_usb_init(void)
 {
 	int err;
 
-	INF("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",  
+	BT_INFO("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",  
 		VERSION);
-	INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
 
 	if ((err = usb_register(&hci_usb_driver)) < 0)
-		ERR("Failed to register HCI USB driver");
+		BT_ERR("Failed to register HCI USB driver");
 
 	return err;
 }
@@ -670,6 +1007,6 @@
 module_init(hci_usb_init);
 module_exit(hci_usb_cleanup);
 
-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("BlueZ HCI USB driver ver " VERSION);
 MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/drivers/bluetooth/hci_usb.h linux-2.4.18-mh15/drivers/bluetooth/hci_usb.h
--- linux-2.4.18/drivers/bluetooth/hci_usb.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_usb.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,147 @@
+/* 
+   HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: hci_usb.h,v 1.2 2002/03/18 19:10:04 maxk Exp $
+ */
+
+#ifdef __KERNEL__
+
+/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
+#define HCI_DEV_CLASS        0xe0	/* Wireless class */
+#define HCI_DEV_SUBCLASS     0x01	/* RF subclass */
+#define HCI_DEV_PROTOCOL     0x01	/* Bluetooth programming protocol */
+
+#define HCI_CTRL_REQ	     0x20
+#define HCI_DIGI_REQ	     0x40
+
+#define HCI_IGNORE           0x01
+#define HCI_RESET            0x02
+#define HCI_DIGIANSWER       0x04
+#define HCI_BROKEN_ISOC      0x08
+
+#define HCI_MAX_IFACE_NUM	3 
+
+#define HCI_MAX_BULK_TX     	4
+#define HCI_MAX_BULK_RX     	1
+
+#define HCI_MAX_ISOC_RX		2
+#define HCI_MAX_ISOC_TX		2
+
+#define HCI_MAX_ISOC_FRAMES     10
+
+struct _urb_queue {
+	struct list_head head;
+	spinlock_t       lock;
+};
+
+struct _urb {
+	struct list_head  list;
+	struct _urb_queue *queue;
+	int               type;
+	void              *priv;
+	struct urb        urb;
+};
+
+struct _urb *_urb_alloc(int isoc, int gfp);
+
+static inline void _urb_free(struct _urb *_urb)
+{
+	kfree(_urb);
+}
+
+static inline void _urb_queue_init(struct _urb_queue *q)
+{
+	INIT_LIST_HEAD(&q->head);
+	spin_lock_init(&q->lock);
+}
+
+static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb)
+{
+        unsigned long flags;
+        spin_lock_irqsave(&q->lock, flags);
+	list_add(&_urb->list, &q->head); _urb->queue = q;
+	spin_unlock_irqrestore(&q->lock, flags);
+}
+
+static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb)
+{
+        unsigned long flags;
+        spin_lock_irqsave(&q->lock, flags);
+	list_add_tail(&_urb->list, &q->head); _urb->queue = q;
+	spin_unlock_irqrestore(&q->lock, flags);
+}
+
+static inline void _urb_unlink(struct _urb *_urb)
+{
+	struct _urb_queue *q = _urb->queue;
+        unsigned long flags;
+	if (q) {
+        	spin_lock_irqsave(&q->lock, flags);
+		list_del(&_urb->list); _urb->queue = NULL;
+		spin_unlock_irqrestore(&q->lock, flags);
+	}
+}
+
+struct _urb *_urb_dequeue(struct _urb_queue *q);
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({                      \
+		        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+			        (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+struct hci_usb {
+	struct hci_dev		hdev;
+
+	unsigned long		state;
+	
+	struct usb_device 	*udev;
+	
+	struct usb_endpoint_descriptor	*bulk_in_ep;
+	struct usb_endpoint_descriptor	*bulk_out_ep;
+	struct usb_endpoint_descriptor	*intr_in_ep;
+
+	struct usb_interface            *isoc_iface;
+	struct usb_endpoint_descriptor	*isoc_out_ep;
+	struct usb_endpoint_descriptor	*isoc_in_ep;
+
+	__u8			ctrl_req;
+
+	struct sk_buff_head	transmit_q[4];
+	struct sk_buff		*reassembly[4]; // Reassembly buffers
+
+	rwlock_t		completion_lock;
+
+	atomic_t		pending_tx[4];  // Number of pending requests 
+	struct _urb_queue	pending_q[4];   // Pending requests
+	struct _urb_queue	completed_q[4]; // Completed requests
+};
+
+/* States  */
+#define HCI_USB_TX_PROCESS	1
+#define HCI_USB_TX_WAKEUP	2
+
+#endif /* __KERNEL__ */
diff -urN linux-2.4.18/drivers/bluetooth/hci_vhci.c linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.c
--- linux-2.4.18/drivers/bluetooth/hci_vhci.c	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.c	2004-08-01 16:26:23.000000000 +0200
@@ -25,9 +25,9 @@
 /*
  * BlueZ HCI virtual device driver.
  *
- * $Id: hci_vhci.c,v 1.3 2001/08/03 04:19:50 maxk Exp $ 
+ * $Id: hci_vhci.c,v 1.3 2002/04/17 17:37:20 maxk Exp $ 
  */
-#define VERSION "1.0"
+#define VERSION "1.1"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -49,43 +49,56 @@
 #include <asm/uaccess.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
 #include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/hci_vhci.h>
+#include "hci_vhci.h"
 
 /* HCI device part */
 
-int hci_vhci_open(struct hci_dev *hdev)
+static int hci_vhci_open(struct hci_dev *hdev)
 {
-	hdev->flags |= HCI_RUNNING;
+	set_bit(HCI_RUNNING, &hdev->flags);
 	return 0;
 }
 
-int hci_vhci_flush(struct hci_dev *hdev)
+static int hci_vhci_flush(struct hci_dev *hdev)
 {
 	struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
 	skb_queue_purge(&hci_vhci->readq);
 	return 0;
 }
 
-int hci_vhci_close(struct hci_dev *hdev)
+static int hci_vhci_close(struct hci_dev *hdev)
 {
-	hdev->flags &= ~HCI_RUNNING;
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
 	hci_vhci_flush(hdev);
 	return 0;
 }
 
-int hci_vhci_send_frame(struct sk_buff *skb)
+static void hci_vhci_destruct(struct hci_dev *hdev)
+{
+	struct hci_vhci_struct *vhci;
+
+	if (!hdev) return;
+
+	vhci = (struct hci_vhci_struct *) hdev->driver_data;
+	kfree(vhci);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static int hci_vhci_send_frame(struct sk_buff *skb)
 {
 	struct hci_dev* hdev = (struct hci_dev *) skb->dev;
 	struct hci_vhci_struct *hci_vhci;
 
 	if (!hdev) {
-		ERR("Frame for uknown device (hdev=NULL)");
+		BT_ERR("Frame for uknown device (hdev=NULL)");
 		return -ENODEV;
 	}
 
-	if (!(hdev->flags & HCI_RUNNING))
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
 		return -EBUSY;
 
 	hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
@@ -188,7 +201,7 @@
 
 	add_wait_queue(&hci_vhci->read_wait, &wait);
 	while (count) {
-		current->state = TASK_INTERRUPTIBLE;
+		set_current_state(TASK_INTERRUPTIBLE);
 
 		/* Read frames from device queue */
 		if (!(skb = skb_dequeue(&hci_vhci->readq))) {
@@ -214,8 +227,7 @@
 		kfree_skb(skb);
 		break;
 	}
-
-	current->state = TASK_RUNNING;
+	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&hci_vhci->read_wait, &wait);
 
 	return ret;
@@ -270,11 +282,13 @@
 	hdev->close = hci_vhci_close;
 	hdev->flush = hci_vhci_flush;
 	hdev->send  = hci_vhci_send_frame;
+	hdev->destruct = hci_vhci_destruct;
 
 	if (hci_register_dev(hdev) < 0) {
 		kfree(hci_vhci);
 		return -EBUSY;
 	}
+	MOD_INC_USE_COUNT;
 
 	file->private_data = hci_vhci;
 	return 0;   
@@ -285,12 +299,10 @@
 	struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
 
 	if (hci_unregister_dev(&hci_vhci->hdev) < 0) {
-		ERR("Can't unregister HCI device %s", hci_vhci->hdev.name);
+		BT_ERR("Can't unregister HCI device %s", hci_vhci->hdev.name);
 	}
 
-	kfree(hci_vhci);
 	file->private_data = NULL;
-
 	return 0;
 }
 
@@ -315,12 +327,12 @@
 
 int __init hci_vhci_init(void)
 {
-	INF("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",  
+	BT_INFO("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",  
 		VERSION);
-	INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
 
 	if (misc_register(&hci_vhci_miscdev)) {
-		ERR("Can't register misc device %d\n", VHCI_MINOR);
+		BT_ERR("Can't register misc device %d\n", VHCI_MINOR);
 		return -EIO;
 	}
 
@@ -337,4 +349,4 @@
 
 MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
 MODULE_DESCRIPTION("BlueZ VHCI driver ver " VERSION);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL"); 
diff -urN linux-2.4.18/drivers/bluetooth/hci_vhci.h linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.h
--- linux-2.4.18/drivers/bluetooth/hci_vhci.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/hci_vhci.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,50 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: hci_vhci.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $
+ */
+
+#ifndef __HCI_VHCI_H
+#define __HCI_VHCI_H
+
+#ifdef __KERNEL__
+
+struct hci_vhci_struct {
+	struct hci_dev       hdev;
+	__u32                flags;
+	wait_queue_head_t    read_wait;
+	struct sk_buff_head  readq;
+	struct fasync_struct *fasync;
+};
+
+/* VHCI device flags */
+#define VHCI_FASYNC		0x0010
+
+#endif /* __KERNEL__ */
+
+#define VHCI_DEV	"/dev/vhci"
+#define VHCI_MINOR	250
+
+#endif /* __HCI_VHCI_H */
diff -urN linux-2.4.18/drivers/bluetooth/Makefile linux-2.4.18-mh15/drivers/bluetooth/Makefile
--- linux-2.4.18/drivers/bluetooth/Makefile	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/drivers/bluetooth/Makefile	2004-08-01 16:26:23.000000000 +0200
@@ -1,11 +1,27 @@
 #
-# Makefile for Bluetooth HCI device drivers.
+# Makefile for the Linux Bluetooth HCI device drivers
 #
 
 O_TARGET	:= bluetooth.o
 
+list-multi	:= hci_uart.o
+
 obj-$(CONFIG_BLUEZ_HCIUSB)	+= hci_usb.o
-obj-$(CONFIG_BLUEZ_HCIUART)	+= hci_uart.o
 obj-$(CONFIG_BLUEZ_HCIVHCI)	+= hci_vhci.o
 
+obj-$(CONFIG_BLUEZ_HCIUART)		+= hci_uart.o
+uart-y					:= hci_ldisc.o
+uart-$(CONFIG_BLUEZ_HCIUART_H4)		+= hci_h4.o
+uart-$(CONFIG_BLUEZ_HCIUART_BCSP)	+= hci_bcsp.o
+
+obj-$(CONFIG_BLUEZ_HCIBFUSB)	+= bfusb.o
+
+obj-$(CONFIG_BLUEZ_HCIDTL1)	+= dtl1_cs.o
+obj-$(CONFIG_BLUEZ_HCIBT3C)	+= bt3c_cs.o
+obj-$(CONFIG_BLUEZ_HCIBLUECARD)	+= bluecard_cs.o
+obj-$(CONFIG_BLUEZ_HCIBTUART)	+= btuart_cs.o
+
 include $(TOPDIR)/Rules.make
+
+hci_uart.o: $(uart-y)
+	$(LD) -r -o $@ $(uart-y)
diff -urN linux-2.4.18/drivers/bluetooth/Makefile.lib linux-2.4.18-mh15/drivers/bluetooth/Makefile.lib
--- linux-2.4.18/drivers/bluetooth/Makefile.lib	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/bluetooth/Makefile.lib	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,2 @@
+obj-$(CONFIG_BLUEZ_HCIBFUSB)	+= firmware_class.o
+obj-$(CONFIG_BLUEZ_HCIBT3C)	+= firmware_class.o
diff -urN linux-2.4.18/drivers/char/pcmcia/serial_cs.c linux-2.4.18-mh15/drivers/char/pcmcia/serial_cs.c
--- linux-2.4.18/drivers/char/pcmcia/serial_cs.c	2001-12-21 18:41:54.000000000 +0100
+++ linux-2.4.18-mh15/drivers/char/pcmcia/serial_cs.c	2004-08-01 16:26:23.000000000 +0200
@@ -2,7 +2,7 @@
 
     A driver for PCMCIA serial devices
 
-    serial_cs.c 1.128 2001/10/18 12:18:35
+    serial_cs.c 1.138 2002/10/25 06:24:52
 
     The contents of this file are subject to the Mozilla Public
     License Version 1.1 (the "License"); you may not use this file
@@ -69,14 +69,14 @@
 static int irq_list[4] = { -1 };
 MODULE_PARM(irq_list, "1-4i");
 
-/* Enable the speaker? */
-INT_MODULE_PARM(do_sound, 1);
+INT_MODULE_PARM(do_sound, 1);		/* Enable the speaker? */
+INT_MODULE_PARM(buggy_uart, 0);		/* Skip strict UART tests? */
 
 #ifdef PCMCIA_DEBUG
 INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"serial_cs.c 1.128 2001/10/18 12:18:35 (David Hinds)";
+"serial_cs.c 1.138 2002/10/25 06:24:52 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -95,6 +95,7 @@
     { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 },
     { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 },
     { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 },
+    { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D2, 2 },
     { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 },
     { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS422, 2 },
     { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS422, 4 },
@@ -148,7 +149,7 @@
     client_reg_t client_reg;
     dev_link_t *link;
     int i, ret;
-    
+
     DEBUG(0, "serial_attach()\n");
 
     /* Create new serial device */
@@ -160,7 +161,7 @@
     link->release.function = &serial_release;
     link->release.data = (u_long)link;
     link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-    link->io.NumPorts1 = 8;
+    link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
     link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
     if (irq_list[0] == -1)
@@ -169,13 +170,12 @@
 	for (i = 0; i < 4; i++)
 	    link->irq.IRQInfo2 |= 1 << irq_list[i];
     link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
     if (do_sound) {
 	link->conf.Attributes |= CONF_ENABLE_SPKR;
 	link->conf.Status = CCSR_AUDIO_ENA;
     }
     link->conf.IntType = INT_MEMORY_AND_IO;
-    
+
     /* Register with Card Services */
     link->next = dev_list;
     dev_list = link;
@@ -194,7 +194,7 @@
 	serial_detach(link);
 	return NULL;
     }
-    
+
     return link;
 } /* serial_attach */
 
@@ -214,7 +214,7 @@
     int ret;
 
     DEBUG(0, "serial_detach(0x%p)\n", link);
-    
+
     /* Locate device structure */
     for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
 	if (*linkp == link) break;
@@ -224,17 +224,17 @@
     del_timer(&link->release);
     if (link->state & DEV_CONFIG)
 	serial_release((u_long)link);
-    
+
     if (link->handle) {
 	ret = CardServices(DeregisterClient, link->handle);
 	if (ret != CS_SUCCESS)
 	    cs_error(link->handle, DeregisterClient, ret);
     }
-    
+
     /* Unlink device structure, free bits */
     *linkp = link->next;
     kfree(info);
-    
+
 } /* serial_detach */
 
 /*====================================================================*/
@@ -243,18 +243,20 @@
 {
     struct serial_struct serial;
     int line;
-    
+
     memset(&serial, 0, sizeof(serial));
     serial.port = port;
     serial.irq = irq;
     serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ;
+    if (buggy_uart)
+	serial.flags |= ASYNC_BUGGY_UART;
     line = register_serial(&serial);
     if (line < 0) {
 	printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx,"
 	       " irq %d failed\n", (u_long)serial.port, serial.irq);
 	return -1;
     }
-    
+
     info->line[info->ndev] = line;
     sprintf(info->node[info->ndev].dev_name, "ttyS%d", line);
     info->node[info->ndev].major = TTY_MAJOR;
@@ -262,7 +264,7 @@
     if (info->ndev > 0)
 	info->node[info->ndev-1].next = &info->node[info->ndev];
     info->ndev++;
-    
+
     return 0;
 }
 
@@ -313,7 +315,10 @@
 	    return setup_serial(info, port, config.AssignedIRQ);
     }
     link->conf.Vcc = config.Vcc;
-    
+
+    link->io.NumPorts1 = 8;
+    link->io.NumPorts2 = 0;
+
     /* First pass: look for a config entry that looks normal. */
     tuple.TupleData = (cisdata_t *)buf;
     tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
@@ -340,7 +345,7 @@
 	    i = next_tuple(handle, &tuple, &parse);
 	}
     }
-    
+
     /* Second pass: try to find an entry that isn't picky about
        its base address, then try to grab any standard serial port
        address, and finally try to get any free port. */
@@ -352,8 +357,7 @@
 	    for (j = 0; j < 5; j++) {
 		link->io.BasePort1 = base[j];
 		link->io.IOAddrLines = base[j] ? 16 : 3;
-		i = CardServices(RequestIO, link->handle,
-				 &link->io);
+		i = CardServices(RequestIO, link->handle, &link->io);
 		if (i == CS_SUCCESS) goto found_port;
 	    }
 	}
@@ -365,7 +369,7 @@
 	cs_error(link->handle, RequestIO, i);
 	return -1;
     }
-    
+
     i = CardServices(RequestIRQ, link->handle, &link->irq);
     if (i != CS_SUCCESS) {
 	cs_error(link->handle, RequestIRQ, i);
@@ -390,8 +394,12 @@
     u_char buf[256];
     cisparse_t parse;
     cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    config_info_t config;
     int i, base2 = 0;
 
+    CardServices(GetConfigurationInfo, handle, &config);
+    link->conf.Vcc = config.Vcc;
+
     tuple.TupleData = (cisdata_t *)buf;
     tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
     tuple.Attributes = 0;
@@ -433,12 +441,12 @@
 	    i = next_tuple(handle, &tuple, &parse);
 	}
     }
-    
+
     if (i != CS_SUCCESS) {
-	cs_error(link->handle, RequestIO, i);
-	return -1;
+	/* At worst, try to configure as a single port */
+	return simple_config(link);
     }
-    
+
     i = CardServices(RequestIRQ, link->handle, &link->irq);
     if (i != CS_SUCCESS) {
 	cs_error(link->handle, RequestIRQ, i);
@@ -454,14 +462,27 @@
 	cs_error(link->handle, RequestConfiguration, i);
 	return -1;
     }
-    
+
+    /* The Oxford Semiconductor OXCF950 cards are in fact single-port:
+       8 registers are for the UART, the others are extra registers */
+    if (info->manfid == MANFID_OXSEMI) {
+	if (cf->index == 1 || cf->index == 3) {
+	    setup_serial(info, base2, link->irq.AssignedIRQ);
+	    outb(12,link->io.BasePort1+1);
+	} else {
+	    setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
+	    outb(12,base2+1);
+	}
+	return 0;
+    }
+
     setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
     /* The Nokia cards are not really multiport cards */
     if (info->manfid == MANFID_NOKIA)
 	return 0;
     for (i = 0; i < info->multi-1; i++)
 	setup_serial(info, base2+(8*i), link->irq.AssignedIRQ);
-    
+
     return 0;
 }
 
@@ -500,7 +521,7 @@
     }
     link->conf.ConfigBase = parse.config.base;
     link->conf.Present = parse.config.rmask[0];
-    
+
     /* Configure card */
     link->state |= DEV_CONFIG;
 
@@ -508,8 +529,8 @@
     tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
     tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
     info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS);
-    
-    /* Is this a multiport card? */
+
+    /* Scan list of known multiport card ID's */
     tuple.DesiredTuple = CISTPL_MANFID;
     if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
 	info->manfid = le16_to_cpu(buf[0]);
@@ -537,15 +558,15 @@
 		info->multi = 2;
 	}
     }
-    
+
     if (info->multi > 1)
 	multi_config(link);
     else
 	simple_config(link);
-    
+
     if (info->ndev == 0)
 	goto failed;
-    
+
     if (info->manfid == MANFID_IBM) {
 	conf_reg_t reg = { 0, CS_READ, 0x800, 0 };
 	CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
@@ -562,6 +583,7 @@
     cs_error(link->handle, last_fn, last_ret);
 failed:
     serial_release((u_long)link);
+    link->state &= ~DEV_CONFIG_PENDING;
 
 } /* serial_config */
 
@@ -569,7 +591,7 @@
 
     After a card is removed, serial_release() will unregister the net
     device, and release the PCMCIA configuration.
-    
+
 ======================================================================*/
 
 void serial_release(u_long arg)
@@ -577,7 +599,7 @@
     dev_link_t *link = (dev_link_t *)arg;
     serial_info_t *info = link->priv;
     int i;
-    
+
     DEBUG(0, "serial_release(0x%p)\n", link);
 
     for (i = 0; i < info->ndev; i++) {
@@ -590,7 +612,7 @@
 	CardServices(ReleaseIO, link->handle, &link->io);
 	CardServices(ReleaseIRQ, link->handle, &link->irq);
     }
-    
+
     link->state &= ~DEV_CONFIG;
 
 } /* serial_release */
@@ -601,7 +623,7 @@
     stuff to run after an event is received.  A CARD_REMOVAL event
     also sets some flags to discourage the serial drivers from
     talking to the ports.
-    
+
 ======================================================================*/
 
 static int serial_event(event_t event, int priority,
@@ -609,9 +631,9 @@
 {
     dev_link_t *link = args->client_data;
     serial_info_t *info = link->priv;
-    
+
     DEBUG(1, "serial_event(0x%06x)\n", event);
-    
+
     switch (event) {
     case CS_EVENT_CARD_REMOVAL:
 	link->state &= ~DEV_PRESENT;
@@ -650,7 +672,7 @@
     if (serv.Revision != CS_RELEASE_CODE) {
 	printk(KERN_NOTICE "serial_cs: Card Services release "
 	       "does not match!\n");
-	return -1;
+	return -EINVAL;
     }
     register_pccard_driver(&dev_info, &serial_attach, &serial_detach);
     return 0;
diff -urN linux-2.4.18/drivers/input/Config.in linux-2.4.18-mh15/drivers/input/Config.in
--- linux-2.4.18/drivers/input/Config.in	2001-09-13 00:34:06.000000000 +0200
+++ linux-2.4.18-mh15/drivers/input/Config.in	2004-08-01 16:26:23.000000000 +0200
@@ -14,5 +14,6 @@
 fi
 dep_tristate '  Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_INPUT
 dep_tristate '  Event interface support' CONFIG_INPUT_EVDEV $CONFIG_INPUT
+dep_tristate '  User level driver support' CONFIG_INPUT_UINPUT $CONFIG_INPUT
 
 endmenu
diff -urN linux-2.4.18/drivers/input/keybdev.c linux-2.4.18-mh15/drivers/input/keybdev.c
--- linux-2.4.18/drivers/input/keybdev.c	2001-10-11 18:14:32.000000000 +0200
+++ linux-2.4.18-mh15/drivers/input/keybdev.c	2004-08-01 16:26:23.000000000 +0200
@@ -154,16 +154,18 @@
 
 static struct input_handler keybdev_handler;
 
+static unsigned int ledstate = 0xff;
+
 void keybdev_ledfunc(unsigned int led)
 {
 	struct input_handle *handle;	
 
-	for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
+	ledstate = led;
 
+	for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
 		input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01));
 		input_event(handle->dev, EV_LED, LED_NUML,    !!(led & 0x02));
 		input_event(handle->dev, EV_LED, LED_CAPSL,   !!(led & 0x04));
-
 	}
 }
 
@@ -202,6 +204,12 @@
 
 //	printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number);
 
+	if (ledstate != 0xff) {
+		input_event(dev, EV_LED, LED_SCROLLL, !!(ledstate & 0x01));
+		input_event(dev, EV_LED, LED_NUML,    !!(ledstate & 0x02));
+		input_event(dev, EV_LED, LED_CAPSL,   !!(ledstate & 0x04));
+	}
+
 	return handle;
 }
 
diff -urN linux-2.4.18/drivers/input/Makefile linux-2.4.18-mh15/drivers/input/Makefile
--- linux-2.4.18/drivers/input/Makefile	2000-12-29 23:07:22.000000000 +0100
+++ linux-2.4.18-mh15/drivers/input/Makefile	2004-08-01 16:26:23.000000000 +0200
@@ -24,6 +24,7 @@
 obj-$(CONFIG_INPUT_MOUSEDEV)	+= mousedev.o
 obj-$(CONFIG_INPUT_JOYDEV)	+= joydev.o
 obj-$(CONFIG_INPUT_EVDEV)	+= evdev.o
+obj-$(CONFIG_INPUT_UINPUT)	+= uinput.o
 
 # The global Rules.make.
 
diff -urN linux-2.4.18/drivers/input/uinput.c linux-2.4.18-mh15/drivers/input/uinput.c
--- linux-2.4.18/drivers/input/uinput.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/drivers/input/uinput.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,428 @@
+/*
+ *  User level driver support for input subsystem
+ *
+ * Heavily based on evdev.c by Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
+ * 
+ * Changes/Revisions:
+ *	0.1	20/06/2002
+ *		- first public version
+ */
+
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/smp_lock.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uinput.h>
+
+static int uinput_dev_open(struct input_dev *dev)
+{
+	return 0;
+}
+
+static void uinput_dev_close(struct input_dev *dev)
+{
+}
+
+static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct uinput_device	*udev;
+
+	udev = (struct uinput_device *)dev->private;
+
+	udev->buff[udev->head].type = type;
+	udev->buff[udev->head].code = code;
+	udev->buff[udev->head].value = value;
+	do_gettimeofday(&udev->buff[udev->head].time);
+	udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
+
+	wake_up_interruptible(&udev->waitq);
+
+	return 0;
+}
+
+static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect)
+{
+	return 0;
+}
+
+static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
+{
+	return 0;
+}					
+
+static int uinput_create_device(struct uinput_device *udev)
+{
+	if (!udev->dev->name) {
+		printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
+		return -EINVAL;
+	}
+
+	udev->dev->open = uinput_dev_open;
+	udev->dev->close = uinput_dev_close;
+	udev->dev->event = uinput_dev_event;
+	udev->dev->upload_effect = uinput_dev_upload_effect;
+	udev->dev->erase_effect = uinput_dev_erase_effect;
+	udev->dev->private = udev;
+
+	init_waitqueue_head(&(udev->waitq));
+
+	input_register_device(udev->dev);
+
+	set_bit(UIST_CREATED, &(udev->state));
+
+	return 0;
+}
+
+static int uinput_destroy_device(struct uinput_device *udev)
+{
+	if (!test_bit(UIST_CREATED, &(udev->state))) {
+		printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME);
+		return -EINVAL;
+	}
+
+	input_unregister_device(udev->dev);
+
+	clear_bit(UIST_CREATED, &(udev->state));
+
+	return 0;
+}
+
+static int uinput_open(struct inode *inode, struct file *file)
+{
+	struct uinput_device	*newdev;
+	struct input_dev	*newinput;
+
+	newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL);
+	if (!newdev)
+		goto error;
+	memset(newdev, 0, sizeof(struct uinput_device));
+
+	newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
+	if (!newinput)
+		goto cleanup;
+	memset(newinput, 0, sizeof(struct input_dev));
+
+	newdev->dev = newinput;
+	
+	file->private_data = newdev;
+
+	return 0;
+cleanup:
+	kfree(newdev);
+error:
+	return -ENOMEM;
+}
+
+static int uinput_validate_absbits(struct input_dev *dev)
+{
+	unsigned int cnt;
+	int retval = 0;
+	
+	for (cnt = 0; cnt < ABS_MAX; cnt++) {
+		if (!test_bit(cnt, dev->absbit)) 
+			continue;
+		
+		if (/*!dev->absmin[cnt] || !dev->absmax[cnt] || */
+		    (dev->absmax[cnt] <= dev->absmin[cnt])) {
+			printk(KERN_DEBUG 
+				"%s: invalid abs[%02x] min:%d max:%d\n",
+				UINPUT_NAME, cnt, 
+				dev->absmin[cnt], dev->absmax[cnt]);
+			retval = -EINVAL;
+			break;
+		}
+
+		if ((dev->absflat[cnt] < dev->absmin[cnt]) ||
+		    (dev->absflat[cnt] > dev->absmax[cnt])) {
+			printk(KERN_DEBUG 
+				"%s: absflat[%02x] out of range: %d "
+				"(min:%d/max:%d)\n",
+				UINPUT_NAME, cnt, dev->absflat[cnt],
+				dev->absmin[cnt], dev->absmax[cnt]);
+			retval = -EINVAL;
+			break;
+		}
+	}
+	return retval;
+}
+
+static int uinput_alloc_device(struct file *file, const char *buffer, size_t count)
+{
+	struct uinput_user_dev	*user_dev;
+	struct input_dev	*dev;
+	struct uinput_device	*udev;
+	int			size,
+				retval;
+
+	retval = count;
+
+	udev = (struct uinput_device *)file->private_data;
+	dev = udev->dev;
+
+	user_dev = kmalloc(sizeof(*user_dev), GFP_KERNEL);
+	if (!user_dev) {
+		retval = -ENOMEM;
+		goto exit;
+	}
+
+	if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
+		retval = -EFAULT;
+		goto exit;
+	}
+
+	if (NULL != dev->name) 
+		kfree(dev->name);
+
+	size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
+	dev->name = kmalloc(size, GFP_KERNEL);
+	if (!dev->name) {
+		retval = -ENOMEM;
+		goto exit;
+	}
+
+	strncpy(dev->name, user_dev->name, size);
+	dev->idbus	= user_dev->idbus;
+	dev->idvendor	= user_dev->idvendor;
+	dev->idproduct	= user_dev->idproduct;
+	dev->idversion	= user_dev->idversion;
+	dev->ff_effects_max = user_dev->ff_effects_max;
+
+	size = sizeof(int) * (ABS_MAX + 1);
+	memcpy(dev->absmax, user_dev->absmax, size);
+	memcpy(dev->absmin, user_dev->absmin, size);
+	memcpy(dev->absfuzz, user_dev->absfuzz, size);
+	memcpy(dev->absflat, user_dev->absflat, size);
+
+	/* check if absmin/absmax/absfuzz/absflat are filled as
+	 * told in Documentation/input/input-programming.txt */
+	if (test_bit(EV_ABS, dev->evbit)) {
+		retval = uinput_validate_absbits(dev);
+		if (retval < 0)
+			kfree(dev->name);
+	}
+
+exit:
+	kfree(user_dev);
+	return retval;
+}
+
+static ssize_t uinput_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct uinput_device	*udev = file->private_data;
+	
+	if (test_bit(UIST_CREATED, &(udev->state))) {
+		struct input_event	ev;
+
+		if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
+			return -EFAULT;
+		input_event(udev->dev, ev.type, ev.code, ev.value);
+	}
+	else
+		count = uinput_alloc_device(file, buffer, count);
+
+	return count;
+}
+
+static ssize_t uinput_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct uinput_device *udev = file->private_data;
+	int retval = 0;
+	
+	if (!test_bit(UIST_CREATED, &(udev->state)))
+		return -ENODEV;
+
+	if ((udev->head == udev->tail) && (file->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	retval = wait_event_interruptible(udev->waitq,
+			(udev->head != udev->tail) || 
+			!test_bit(UIST_CREATED, &(udev->state)));
+	
+	if (retval)
+		return retval;
+
+	if (!test_bit(UIST_CREATED, &(udev->state)))
+		return -ENODEV;
+
+	while ((udev->head != udev->tail) && 
+	    (retval + sizeof(struct input_event) <= count)) {
+		if (copy_to_user(buffer + retval, &(udev->buff[udev->tail]),
+		    sizeof(struct input_event))) return -EFAULT;
+		udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
+		retval += sizeof(struct input_event);
+	}
+
+	return retval;
+}
+
+static unsigned int uinput_poll(struct file *file, poll_table *wait)
+{
+	struct uinput_device *udev = file->private_data;
+
+	poll_wait(file, &udev->waitq, wait);
+
+	if (udev->head != udev->tail)
+		return POLLIN | POLLRDNORM;
+
+	return 0;			
+}
+
+static int uinput_burn_device(struct uinput_device *udev)
+{
+	if (test_bit(UIST_CREATED, &(udev->state)))
+		uinput_destroy_device(udev);
+
+	kfree(udev->dev);
+	kfree(udev);
+
+	return 0;
+}
+
+static int uinput_close(struct inode *inode, struct file *file)
+{
+	return uinput_burn_device((struct uinput_device *)file->private_data);
+}
+
+static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int			retval = 0;
+	struct uinput_device	*udev;
+
+	udev = (struct uinput_device *)file->private_data;
+
+	/* device attributes can not be changed after the device is created */
+	if (cmd >= UI_SET_EVBIT && test_bit(UIST_CREATED, &(udev->state)))
+		return -EINVAL;
+
+	switch (cmd) {
+		case UI_DEV_CREATE:
+			retval = uinput_create_device(udev);
+			break;
+			
+		case UI_DEV_DESTROY:
+			retval = uinput_destroy_device(udev);
+			break;
+
+		case UI_SET_EVBIT:
+			if (arg > EV_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->evbit);
+			break;
+			
+		case UI_SET_KEYBIT:
+			if (arg > KEY_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->keybit);
+			break;
+			
+		case UI_SET_RELBIT:
+			if (arg > REL_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->relbit);
+			break;
+			
+		case UI_SET_ABSBIT:
+			if (arg > ABS_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->absbit);
+			break;
+			
+		case UI_SET_MSCBIT:
+			if (arg > MSC_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->mscbit);
+			break;
+			
+		case UI_SET_LEDBIT:
+			if (arg > LED_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->ledbit);
+			break;
+			
+		case UI_SET_SNDBIT:
+			if (arg > SND_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->sndbit);
+			break;
+			
+		case UI_SET_FFBIT:
+			if (arg > FF_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->ffbit);
+			break;
+			
+		default:
+			retval = -EFAULT;
+	}
+	return retval;
+}
+
+struct file_operations uinput_fops = {
+	owner:		THIS_MODULE,
+	open:		uinput_open,
+	release:	uinput_close,
+	read:		uinput_read,
+	write:		uinput_write,
+	poll:		uinput_poll,
+	ioctl:		uinput_ioctl,
+};
+
+static struct miscdevice uinput_misc = {
+	fops:		&uinput_fops,
+	minor:		UINPUT_MINOR,
+	name:		UINPUT_NAME,
+};
+
+static int __init uinput_init(void)
+{
+	return misc_register(&uinput_misc);
+}
+
+static void __exit uinput_exit(void)
+{
+	misc_deregister(&uinput_misc);
+}
+
+MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
+MODULE_DESCRIPTION("User level driver support for input subsystem");
+MODULE_LICENSE("GPL");
+
+module_init(uinput_init);
+module_exit(uinput_exit);
+
diff -urN linux-2.4.18/drivers/isdn/avmb1/capidrv.c linux-2.4.18-mh15/drivers/isdn/avmb1/capidrv.c
--- linux-2.4.18/drivers/isdn/avmb1/capidrv.c	2001-12-21 18:41:54.000000000 +0100
+++ linux-2.4.18-mh15/drivers/isdn/avmb1/capidrv.c	2004-08-01 16:26:23.000000000 +0200
@@ -514,13 +514,25 @@
 
 static void send_message(capidrv_contr * card, _cmsg * cmsg)
 {
-	struct sk_buff *skb;
-	size_t len;
+	struct sk_buff	*skb;
+	size_t		len;
+	u16		err;
+
 	capi_cmsg2message(cmsg, cmsg->buf);
 	len = CAPIMSG_LEN(cmsg->buf);
 	skb = alloc_skb(len, GFP_ATOMIC);
+	if(!skb) {
+		printk(KERN_ERR "no skb len(%d) memory\n", len);
+		return;
+	}
 	memcpy(skb_put(skb, len), cmsg->buf, len);
-	(*capifuncs->capi_put_message) (global.appid, skb);
+	err = (*capifuncs->capi_put_message) (global.appid, skb);
+	if (err) {
+		printk(KERN_WARNING "%s: capi_put_message error: %04x\n",
+			__FUNCTION__, err);
+		kfree_skb(skb);
+		return;
+	}
 	global.nsentctlpkt++;
 }
 
@@ -2179,10 +2191,10 @@
 			free_ncci(card, card->bchans[card->nbchan-1].nccip);
 		if (card->bchans[card->nbchan-1].plcip)
 			free_plci(card, card->bchans[card->nbchan-1].plcip);
-		if (card->plci_list)
-			printk(KERN_ERR "capidrv: bug in free_plci()\n");
 		card->nbchan--;
 	}
+	if (card->plci_list)
+		printk(KERN_ERR "capidrv: bug in free_plci()\n");
 	kfree(card->bchans);
 	card->bchans = 0;
 
diff -urN linux-2.4.18/drivers/isdn/avmb1/kcapi.c linux-2.4.18-mh15/drivers/isdn/avmb1/kcapi.c
--- linux-2.4.18/drivers/isdn/avmb1/kcapi.c	2001-12-21 18:41:54.000000000 +0100
+++ linux-2.4.18-mh15/drivers/isdn/avmb1/kcapi.c	2004-08-01 16:26:23.000000000 +0200
@@ -545,7 +545,13 @@
 static void notify_up(__u32 contr)
 {
 	struct capi_interface_user *p;
+	__u16 appl;
 
+	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+		if (!VALID_APPLID(appl)) continue;
+		if (APPL(appl)->releasing) continue;
+		CARD(contr)->driver->register_appl(CARD(contr), appl, &APPL(appl)->rparam);
+	}
         printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);
 	spin_lock(&capi_users_lock);
 	for (p = capi_users; p; p = p->next) {
@@ -705,12 +711,16 @@
 			nextpp = &(*pp)->next;
 		}
 	}
-	APPL(appl)->releasing--;
-	if (APPL(appl)->releasing <= 0) {
-		APPL(appl)->signal = 0;
-		APPL_MARK_FREE(appl);
-		printk(KERN_INFO "kcapi: appl %d down\n", appl);
-	}
+	if (APPL(appl)->releasing) { /* only release if the application was marked for release */
+		printk(KERN_DEBUG "kcapi: appl %d releasing(%d)\n", appl, APPL(appl)->releasing);
+		APPL(appl)->releasing--;
+		if (APPL(appl)->releasing <= 0) {
+			APPL(appl)->signal = 0;
+			APPL_MARK_FREE(appl);
+			printk(KERN_INFO "kcapi: appl %d down\n", appl);
+		}
+	} else
+		printk(KERN_WARNING "kcapi: appl %d card%d released without request\n", appl, card->cnr);
 }
 /*
  * ncci management
@@ -863,16 +873,7 @@
 
 static void controllercb_ready(struct capi_ctr * card)
 {
-	__u16 appl;
-
 	card->cardstate = CARD_RUNNING;
-
-	for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
-		if (!VALID_APPLID(appl)) continue;
-		if (APPL(appl)->releasing) continue;
-		card->driver->register_appl(card, appl, &APPL(appl)->rparam);
-	}
-
         printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
 		CARDNR(card), card->name);
 
diff -urN linux-2.4.18/drivers/usb/Config.in linux-2.4.18-mh15/drivers/usb/Config.in
--- linux-2.4.18/drivers/usb/Config.in	2002-02-25 20:38:07.000000000 +0100
+++ linux-2.4.18-mh15/drivers/usb/Config.in	2004-08-01 16:26:23.000000000 +0200
@@ -31,7 +31,13 @@
 
 comment 'USB Device Class drivers'
 dep_tristate '  USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND
-dep_tristate '  USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   if [ "$CONFIG_BLUEZ" = "n" ]; then
+      dep_tristate '  USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB
+   else
+      comment '  USB Bluetooth can only be used with disabled Bluetooth subsystem'
+   fi
+fi
 if [ "$CONFIG_SCSI" = "n" ]; then
    comment '  SCSI support is needed for USB Storage'
 fi
diff -urN linux-2.4.18/drivers/usb/hid-core.c linux-2.4.18-mh15/drivers/usb/hid-core.c
--- linux-2.4.18/drivers/usb/hid-core.c	2001-12-21 18:41:55.000000000 +0100
+++ linux-2.4.18-mh15/drivers/usb/hid-core.c	2004-08-01 16:26:23.000000000 +0200
@@ -217,6 +217,8 @@
 
 	offset = report->size;
 	report->size += parser->global.report_size * parser->global.report_count;
+	if (usages < parser->global.report_count)
+		usages = parser->global.report_count;
 
 	if (usages == 0)
 		return 0; /* ignore padding fields */
diff -urN linux-2.4.18/include/linux/firmware.h linux-2.4.18-mh15/include/linux/firmware.h
--- linux-2.4.18/include/linux/firmware.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/include/linux/firmware.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,20 @@
+#ifndef _LINUX_FIRMWARE_H
+#define _LINUX_FIRMWARE_H
+#include <linux/module.h>
+#include <linux/types.h>
+#define FIRMWARE_NAME_MAX 30 
+struct firmware {
+	size_t size;
+	u8 *data;
+};
+int request_firmware (const struct firmware **fw, const char *name,
+		      const char *device);
+int request_firmware_nowait (
+	struct module *module,
+	const char *name, const char *device, void *context,
+	void (*cont)(const struct firmware *fw, void *context));
+/* On 2.5 'device' is 'struct device *' */
+
+void release_firmware (const struct firmware *fw);
+void register_firmware (const char *name, const u8 *data, size_t size);
+#endif
diff -urN linux-2.4.18/include/linux/input.h linux-2.4.18-mh15/include/linux/input.h
--- linux-2.4.18/include/linux/input.h	2001-09-13 00:34:06.000000000 +0200
+++ linux-2.4.18-mh15/include/linux/input.h	2004-08-01 16:26:23.000000000 +0200
@@ -468,6 +468,8 @@
 #define BUS_PCI			0x01
 #define BUS_ISAPNP		0x02
 #define BUS_USB			0x03
+#define BUS_HIL			0x04
+#define BUS_BLUETOOTH		0x05
 
 #define BUS_ISA			0x10
 #define BUS_I8042		0x11
diff -urN linux-2.4.18/include/linux/kernel.h linux-2.4.18-mh15/include/linux/kernel.h
--- linux-2.4.18/include/linux/kernel.h	2002-02-25 20:38:13.000000000 +0100
+++ linux-2.4.18-mh15/include/linux/kernel.h	2004-08-01 16:26:23.000000000 +0200
@@ -11,6 +11,7 @@
 #include <linux/linkage.h>
 #include <linux/stddef.h>
 #include <linux/types.h>
+#include <linux/compiler.h>
 
 /* Optimization barrier */
 /* The "volatile" is due to gcc bugs */
@@ -181,4 +182,6 @@
 	char _f[20-2*sizeof(long)-sizeof(int)];	/* Padding: libc5 uses this.. */
 };
 
-#endif
+#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
+
+#endif /* _LINUX_KERNEL_H */
diff -urN linux-2.4.18/include/linux/net.h linux-2.4.18-mh15/include/linux/net.h
--- linux-2.4.18/include/linux/net.h	2001-11-22 20:46:19.000000000 +0100
+++ linux-2.4.18-mh15/include/linux/net.h	2004-08-01 16:26:23.000000000 +0200
@@ -139,6 +139,7 @@
 extern int	sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags);
 extern int	sock_readv_writev(int type, struct inode * inode, struct file * file,
 				  const struct iovec * iov, long count, long size);
+extern struct socket *sockfd_lookup(int fd, int *err);
 
 extern int	net_ratelimit(void);
 extern unsigned long net_random(void);
diff -urN linux-2.4.18/include/linux/uinput.h linux-2.4.18-mh15/include/linux/uinput.h
--- linux-2.4.18/include/linux/uinput.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/include/linux/uinput.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,79 @@
+/*
+ *  User level driver support for input subsystem
+ *
+ * Heavily based on evdev.c by Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
+ * 
+ * Changes/Revisions:
+ *	0.1	20/06/2002
+ *		- first public version
+ */
+
+#ifndef __UINPUT_H_
+#define __UINPUT_H_
+
+#ifdef __KERNEL__
+#define UINPUT_MINOR		223
+#define UINPUT_NAME		"uinput"
+#define UINPUT_BUFFER_SIZE	16
+
+/* state flags => bit index for {set|clear|test}_bit ops */
+#define UIST_CREATED		0
+
+struct uinput_device {
+	struct input_dev	*dev;
+	unsigned long		state;
+	wait_queue_head_t	waitq;
+	unsigned char		ready,
+				head,
+				tail;
+	struct input_event	buff[UINPUT_BUFFER_SIZE];
+};
+#endif	/* __KERNEL__ */
+
+/* ioctl */
+#define UINPUT_IOCTL_BASE	'U'
+#define UI_DEV_CREATE		_IO(UINPUT_IOCTL_BASE, 1)
+#define UI_DEV_DESTROY		_IO(UINPUT_IOCTL_BASE, 2)
+#define UI_SET_EVBIT		_IOW(UINPUT_IOCTL_BASE, 100, int)
+#define UI_SET_KEYBIT		_IOW(UINPUT_IOCTL_BASE, 101, int)
+#define UI_SET_RELBIT		_IOW(UINPUT_IOCTL_BASE, 102, int)
+#define UI_SET_ABSBIT		_IOW(UINPUT_IOCTL_BASE, 103, int)
+#define UI_SET_MSCBIT		_IOW(UINPUT_IOCTL_BASE, 104, int)
+#define UI_SET_LEDBIT		_IOW(UINPUT_IOCTL_BASE, 105, int)
+#define UI_SET_SNDBIT		_IOW(UINPUT_IOCTL_BASE, 106, int)
+#define UI_SET_FFBIT		_IOW(UINPUT_IOCTL_BASE, 107, int)
+
+#ifndef NBITS
+#define NBITS(x) ((((x)-1)/(sizeof(long)*8))+1)
+#endif	/* NBITS */
+
+#define UINPUT_MAX_NAME_SIZE	80
+struct uinput_user_dev {
+	char name[UINPUT_MAX_NAME_SIZE];
+	unsigned short idbus;
+	unsigned short idvendor;
+	unsigned short idproduct;
+	unsigned short idversion;
+	int ff_effects_max;
+	int absmax[ABS_MAX + 1];
+	int absmin[ABS_MAX + 1];
+	int absfuzz[ABS_MAX + 1];
+	int absflat[ABS_MAX + 1];
+};
+#endif	/* __UINPUT_H_ */
diff -urN linux-2.4.18/include/net/bluetooth/bluetooth.h linux-2.4.18-mh15/include/net/bluetooth/bluetooth.h
--- linux-2.4.18/include/net/bluetooth/bluetooth.h	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/include/net/bluetooth/bluetooth.h	2004-08-01 16:26:23.000000000 +0200
@@ -23,7 +23,7 @@
 */
 
 /*
- *  $Id: bluetooth.h,v 1.6 2001/08/03 04:19:49 maxk Exp $
+ *  $Id: bluetooth.h,v 1.9 2002/05/06 21:11:55 maxk Exp $
  */
 
 #ifndef __BLUETOOTH_H
@@ -31,17 +31,64 @@
 
 #include <asm/types.h>
 #include <asm/byteorder.h>
+#include <linux/poll.h>
+#include <net/sock.h>
 
 #ifndef AF_BLUETOOTH
 #define AF_BLUETOOTH	31
 #define PF_BLUETOOTH	AF_BLUETOOTH
 #endif
 
+/* Reserv for core and drivers use */
+#define BLUEZ_SKB_RESERVE       8
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
 #define BTPROTO_L2CAP   0
 #define BTPROTO_HCI     1
+#define BTPROTO_SCO   	2
+#define BTPROTO_RFCOMM	3
+#define BTPROTO_BNEP	4
+#define BTPROTO_CMTP	5
+#define BTPROTO_HIDP	6
 
 #define SOL_HCI     0
 #define SOL_L2CAP   6
+#define SOL_SCO     17
+#define SOL_RFCOMM  18
+
+/* Debugging */
+#ifdef CONFIG_BLUEZ_DEBUG
+
+#define HCI_CORE_DEBUG		1
+#define HCI_SOCK_DEBUG		1
+#define HCI_UART_DEBUG		1
+#define HCI_USB_DEBUG		1
+//#define HCI_DATA_DUMP		1
+
+#define L2CAP_DEBUG		1
+#define SCO_DEBUG		1
+#define AF_BLUETOOTH_DEBUG	1
+
+#endif /* CONFIG_BLUEZ_DEBUG */
+
+extern void bluez_dump(char *pref, __u8 *buf, int count);
+
+#if __GNUC__ <= 2 && __GNUC_MINOR__ < 95
+#define __func__ __FUNCTION__
+#endif
+
+#define BT_INFO(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg)
+#define BT_DBG(fmt, arg...)  printk(KERN_INFO "%s: " fmt "\n" , __func__ , ## arg)
+#define BT_ERR(fmt, arg...)  printk(KERN_ERR  "%s: " fmt "\n" , __func__ , ## arg)
+
+#ifdef HCI_DATA_DUMP
+#define BT_DMP(buf, len)    bluez_dump(__func__, buf, len)
+#else
+#define BT_DMP(D...)
+#endif
 
 /* Connection and socket states */
 enum {
@@ -50,6 +97,7 @@
 	BT_BOUND,
 	BT_LISTEN,
 	BT_CONNECT,
+	BT_CONNECT2,
 	BT_CONFIG,
 	BT_DISCONN,
 	BT_CLOSED
@@ -66,7 +114,8 @@
 	__u8 b[6];
 } __attribute__((packed)) bdaddr_t;
 
-#define BDADDR_ANY ((bdaddr_t *)"\000\000\000\000\000")
+#define BDADDR_ANY   (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
+#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}})
 
 /* Copy, swap, convert BD Address */
 static inline int bacmp(bdaddr_t *ba1, bdaddr_t *ba2)
@@ -82,6 +131,91 @@
 char *batostr(bdaddr_t *ba);
 bdaddr_t *strtoba(char *str);
 
+/* Common socket structures and functions */
+
+#define bluez_pi(sk) ((struct bluez_pinfo *) &sk->protinfo)
+#define bluez_sk(pi) ((struct sock *) \
+	((void *)pi - (unsigned long)(&((struct sock *)0)->protinfo)))
+
+struct bluez_pinfo {
+	bdaddr_t	src;
+	bdaddr_t	dst;
+
+	struct list_head accept_q;
+	struct sock *parent;
+};
+
+struct bluez_sock_list {
+	struct sock *head;
+	rwlock_t     lock;
+};
+
+int  bluez_sock_register(int proto, struct net_proto_family *ops);
+int  bluez_sock_unregister(int proto);
+void bluez_sock_init(struct socket *sock, struct sock *sk);
+void bluez_sock_link(struct bluez_sock_list *l, struct sock *s);
+void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s);
+int  bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm);
+uint bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait);
+int  bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
+
+void bluez_accept_enqueue(struct sock *parent, struct sock *sk);
+struct sock * bluez_accept_dequeue(struct sock *parent, struct socket *newsock);
+
+/* Skb helpers */
+struct bluez_skb_cb {
+	int    incomming;
+};
+#define bluez_cb(skb)	((struct bluez_skb_cb *)(skb->cb)) 
+
+static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how)
+{
+	struct sk_buff *skb;
+
+	if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) {
+		skb_reserve(skb, BLUEZ_SKB_RESERVE);
+		bluez_cb(skb)->incomming  = 0;
+	}
+	return skb;
+}
+
+static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len, 
+						       int nb, int *err)
+{
+	struct sk_buff *skb;
+
+	if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) {
+		skb_reserve(skb, BLUEZ_SKB_RESERVE);
+		bluez_cb(skb)->incomming  = 0;
+	}
+
+	return skb;
+}
+
+static inline int skb_frags_no(struct sk_buff *skb)
+{
+	register struct sk_buff *frag = skb_shinfo(skb)->frag_list;
+	register int n = 1;
+
+	for (; frag; frag=frag->next, n++);
+	return n;
+}
+
+int hci_core_init(void);
+int hci_core_cleanup(void);
+int hci_sock_init(void);
+int hci_sock_cleanup(void);
+
 int bterr(__u16 code);
 
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(x)
+#endif
+
+#ifndef list_for_each_safe
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+#endif
+
 #endif /* __BLUETOOTH_H */
diff -urN linux-2.4.18/include/net/bluetooth/bluez.h linux-2.4.18-mh15/include/net/bluetooth/bluez.h
--- linux-2.4.18/include/net/bluetooth/bluez.h	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/include/net/bluetooth/bluez.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,124 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- *  $Id: bluez.h,v 1.4 2001/08/03 04:19:49 maxk Exp $
- */
-
-#ifndef __IF_BLUEZ_H
-#define __IF_BLUEZ_H
-
-#include <net/sock.h>
-
-#define BLUEZ_MAX_PROTO 	2
-
-/* Reserv for core and drivers use */
-#define BLUEZ_SKB_RESERVE	8
-
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-/* Debugging */
-#ifdef BLUEZ_DEBUG
-
-#define HCI_CORE_DEBUG		1
-#define HCI_SOCK_DEBUG		1
-#define HCI_UART_DEBUG		1
-#define HCI_USB_DEBUG		1
-//#define HCI_DATA_DUMP		1
-
-#define L2CAP_DEBUG			1
-
-#endif /* BLUEZ_DEBUG */
-
-extern void bluez_dump(char *pref, __u8 *buf, int count);
-
-#define INF(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg)
-#define DBG(fmt, arg...) printk(KERN_INFO __FUNCTION__ ": " fmt "\n" , ## arg)
-#define ERR(fmt, arg...) printk(KERN_ERR  __FUNCTION__ ": " fmt "\n" , ## arg)
-
-#ifdef HCI_DATA_DUMP
-#define DMP(buf, len)    bluez_dump(__FUNCTION__, buf, len)
-#else
-#define DMP(D...)
-#endif
-
-/* ----- Sockets ------ */
-struct bluez_sock_list {
-	struct sock *head;
-	rwlock_t     lock;
-};
-
-extern int  bluez_sock_register(int proto, struct net_proto_family *ops);
-extern int  bluez_sock_unregister(int proto);
-
-extern void bluez_sock_link(struct bluez_sock_list *l, struct sock *s);
-extern void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s);
-
-/* ----- SKB helpers ----- */
-struct bluez_skb_cb {
-	int    incomming;
-};
-#define bluez_cb(skb)	((struct bluez_skb_cb *)(skb->cb)) 
-
-static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how)
-{
-	struct sk_buff *skb;
-
-	if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) {
-		skb_reserve(skb, BLUEZ_SKB_RESERVE);
-		bluez_cb(skb)->incomming  = 0;
-	}
-	return skb;
-}
-
-static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len, 
-						       int nb, int *err)
-{
-	struct sk_buff *skb;
-
-	if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) {
-		skb_reserve(skb, BLUEZ_SKB_RESERVE);
-		bluez_cb(skb)->incomming  = 0;
-	}
-
-	return skb;
-}
-
-static inline int skb_frags_no(struct sk_buff *skb)
-{
-	register struct sk_buff *frag = skb_shinfo(skb)->frag_list;
-	register int n = 1;
-
-	for (; frag; frag=frag->next, n++);
-	return n;
-}
-
-extern int hci_core_init(void);
-extern int hci_core_cleanup(void);
-extern int hci_sock_init(void);
-extern int hci_sock_cleanup(void);
-
-#endif /* __IF_BLUEZ_H */
diff -urN linux-2.4.18/include/net/bluetooth/hci_core.h linux-2.4.18-mh15/include/net/bluetooth/hci_core.h
--- linux-2.4.18/include/net/bluetooth/hci_core.h	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/include/net/bluetooth/hci_core.h	2004-08-01 16:26:23.000000000 +0200
@@ -23,7 +23,7 @@
 */
 
 /* 
- * $Id: hci_core.h,v 1.11 2001/08/05 06:02:15 maxk Exp $ 
+ * $Id: hci_core.h,v 1.5 2002/06/27 04:56:30 maxk Exp $ 
  */
 
 #ifndef __HCI_CORE_H
@@ -32,14 +32,12 @@
 #include <net/bluetooth/hci.h>
 
 /* HCI upper protocols */
-#define HCI_MAX_PROTO 	1
 #define HCI_PROTO_L2CAP	0
+#define HCI_PROTO_SCO	1
 
 #define HCI_INIT_TIMEOUT (HZ * 10)
 
-/* ----- Inquiry cache ----- */
-#define INQUIRY_CACHE_AGE_MAX   (HZ*5)    // 5 seconds
-#define INQUIRY_ENTRY_AGE_MAX   (HZ*60)   // 60 seconds
+/* HCI Core structures */
 
 struct inquiry_entry {
 	struct inquiry_entry 	*next;
@@ -53,111 +51,188 @@
 	struct inquiry_entry 	*list;
 };
 
-static inline void inquiry_cache_init(struct inquiry_cache *cache)
-{
-	spin_lock_init(&cache->lock);
-	cache->list = NULL;
-}
+struct conn_hash {
+	struct list_head list;
+	spinlock_t       lock;
+	unsigned int     num;
+};
 
-static inline void inquiry_cache_lock(struct inquiry_cache *cache)
-{
-	spin_lock(&cache->lock);
-}
+struct hci_dev {
+	struct list_head list;
+	spinlock_t	lock;
+	atomic_t 	refcnt;
 
-static inline void inquiry_cache_unlock(struct inquiry_cache *cache)
-{
-	spin_unlock(&cache->lock);
-}
+	char		name[8];
+	unsigned long	flags;
+	__u16		id;
+	__u8	 	type;
+	bdaddr_t	bdaddr;
+	__u8		features[8];
 
-static inline void inquiry_cache_lock_bh(struct inquiry_cache *cache)
-{
-	spin_lock_bh(&cache->lock);
-}
+	__u16		pkt_type;
+	__u16		link_policy;
+	__u16		link_mode;
 
-static inline void inquiry_cache_unlock_bh(struct inquiry_cache *cache)
-{
-	spin_unlock_bh(&cache->lock);
-}
+	unsigned long	quirks;
 
-static inline long inquiry_cache_age(struct inquiry_cache *cache)
-{
-	return jiffies - cache->timestamp;
-}
+	atomic_t 	cmd_cnt;
+	unsigned int 	acl_cnt;
+	unsigned int 	sco_cnt;
 
-static inline long inquiry_entry_age(struct inquiry_entry *e)
-{
-	return jiffies - e->timestamp;
-}
-extern void inquiry_cache_flush(struct inquiry_cache *cache);
+	unsigned int	acl_mtu;
+	unsigned int 	sco_mtu;
+	unsigned int	acl_pkts;
+	unsigned int	sco_pkts;
 
-struct hci_dev;
+	unsigned long   cmd_last_tx;
+	unsigned long   acl_last_tx;
+	unsigned long   sco_last_tx;
+	
+	struct tasklet_struct 	cmd_task;
+	struct tasklet_struct	rx_task;
+	struct tasklet_struct 	tx_task;
+
+	struct sk_buff_head	rx_q;
+	struct sk_buff_head 	raw_q;
+	struct sk_buff_head 	cmd_q;
+
+	struct sk_buff     	*sent_cmd;
+
+	struct semaphore	req_lock;
+	wait_queue_head_t	req_wait_q;
+	__u32			req_status;
+	__u32			req_result;
+
+	struct inquiry_cache 	inq_cache;
+	struct conn_hash 	conn_hash;
+
+	struct hci_dev_stats 	stat;
+
+	void			*driver_data;
+	void			*core_data;
+
+	atomic_t 		promisc;
+
+	int (*open)(struct hci_dev *hdev);
+	int (*close)(struct hci_dev *hdev);
+	int (*flush)(struct hci_dev *hdev);
+	int (*send)(struct sk_buff *skb);
+	void (*destruct)(struct hci_dev *hdev);
+	int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
+};
 
-/* ----- HCI Connections ----- */
 struct hci_conn {
 	struct list_head list;
+
+	atomic_t	 refcnt;
+	spinlock_t	 lock;
+
 	bdaddr_t         dst;
 	__u16            handle;
+	__u16            state;
 	__u8		 type;
-	unsigned int     sent;
+	__u8		 out;
+	__u32		 link_mode;
+	unsigned long	 pend;
+	
+	unsigned int	 sent;
+	
+	struct sk_buff_head data_q;
 
+	struct timer_list timer;
+	
 	struct hci_dev 	*hdev;
 	void		*l2cap_data;
+	void		*sco_data;
 	void		*priv;
 
-	struct sk_buff_head data_q;
+	struct hci_conn *link;
 };
 
-struct conn_hash {
-	struct list_head list;
-	spinlock_t       lock;
-	unsigned int     num;
-};
+extern struct hci_proto *hci_proto[];
+extern struct list_head hdev_list;
+extern rwlock_t hdev_list_lock;
+
+/* ----- Inquiry cache ----- */
+#define INQUIRY_CACHE_AGE_MAX   (HZ*30)   // 30 seconds
+#define INQUIRY_ENTRY_AGE_MAX   (HZ*60)   // 60 seconds
+
+#define inquiry_cache_lock(c)		spin_lock(&c->lock)
+#define inquiry_cache_unlock(c)		spin_unlock(&c->lock)
+#define inquiry_cache_lock_bh(c)	spin_lock_bh(&c->lock)
+#define inquiry_cache_unlock_bh(c)	spin_unlock_bh(&c->lock)
 
-static inline void conn_hash_init(struct conn_hash *h)
+static inline void inquiry_cache_init(struct hci_dev *hdev)
 {
-	INIT_LIST_HEAD(&h->list);
-	spin_lock_init(&h->lock);
-	h->num = 0;	
+	struct inquiry_cache *c = &hdev->inq_cache;
+	spin_lock_init(&c->lock);
+	c->list = NULL;
 }
 
-static inline void conn_hash_lock(struct conn_hash *h)
+static inline int inquiry_cache_empty(struct hci_dev *hdev)
 {
-	spin_lock(&h->lock);
+	struct inquiry_cache *c = &hdev->inq_cache;
+	return (c->list == NULL);
 }
 
-static inline void conn_hash_unlock(struct conn_hash *h)
+static inline long inquiry_cache_age(struct hci_dev *hdev)
 {
-	spin_unlock(&h->lock);
+	struct inquiry_cache *c = &hdev->inq_cache;
+	return jiffies - c->timestamp;
 }
 
-static inline void __conn_hash_add(struct conn_hash *h, __u16 handle, struct hci_conn *c)
+static inline long inquiry_entry_age(struct inquiry_entry *e)
 {
-	list_add(&c->list, &h->list);
-	h->num++;
+	return jiffies - e->timestamp;
 }
 
-static inline void conn_hash_add(struct conn_hash *h, __u16 handle, struct hci_conn *c)
+struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
+void inquiry_cache_update(struct hci_dev *hdev, inquiry_info *info);
+void inquiry_cache_flush(struct hci_dev *hdev);
+int  inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf);
+
+/* ----- HCI Connections ----- */
+enum {
+	HCI_CONN_AUTH_PEND,
+	HCI_CONN_ENCRYPT_PEND
+};
+
+#define hci_conn_lock(c)	spin_lock(&c->lock)
+#define hci_conn_unlock(c)	spin_unlock(&c->lock)
+#define hci_conn_lock_bh(c)	spin_lock_bh(&c->lock)
+#define hci_conn_unlock_bh(c)	spin_unlock_bh(&c->lock)
+
+#define conn_hash_lock(d)	spin_lock(&d->conn_hash->lock)
+#define conn_hash_unlock(d)	spin_unlock(&d->conn_hash->lock)
+#define conn_hash_lock_bh(d)	spin_lock_bh(&d->conn_hash->lock)
+#define conn_hash_unlock_bh(d)	spin_unlock_bh(&d->conn_hash->lock)
+
+static inline void conn_hash_init(struct hci_dev *hdev)
 {
-	conn_hash_lock(h);
-	__conn_hash_add(h, handle, c);
-	conn_hash_unlock(h);
+	struct conn_hash *h = &hdev->conn_hash;
+	INIT_LIST_HEAD(&h->list);
+	spin_lock_init(&h->lock);
+	h->num = 0;	
 }
 
-static inline void __conn_hash_del(struct conn_hash *h, struct hci_conn *c)
+static inline void conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
 {
-	list_del(&c->list);
-	h->num--;
+	struct conn_hash *h = &hdev->conn_hash;
+	list_add(&c->list, &h->list);
+	h->num++;
 }
 
-static inline void conn_hash_del(struct conn_hash *h, struct hci_conn *c)
+static inline void conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
 {
-	conn_hash_lock(h);
-	__conn_hash_del(h, c);
-	conn_hash_unlock(h);
+	struct conn_hash *h = &hdev->conn_hash;
+	list_del(&c->list);
+	h->num--;
 }
 
-static inline  struct hci_conn *__conn_hash_lookup(struct conn_hash *h, __u16 handle)
+static inline struct hci_conn *conn_hash_lookup_handle(struct hci_dev *hdev,
+	       				__u16 handle)
 {
+	register struct conn_hash *h = &hdev->conn_hash;
 	register struct list_head *p;
 	register struct hci_conn  *c;
 
@@ -169,101 +244,97 @@
         return NULL;
 }
 
-static inline struct hci_conn *conn_hash_lookup(struct conn_hash *h, __u16 handle)
+static inline struct hci_conn *conn_hash_lookup_ba(struct hci_dev *hdev,
+					__u8 type, bdaddr_t *ba)
 {
-	struct hci_conn *conn;
+	register struct conn_hash *h = &hdev->conn_hash;
+	register struct list_head *p;
+	register struct hci_conn  *c;
 
-	conn_hash_lock(h);
-	conn = __conn_hash_lookup(h, handle);
-	conn_hash_unlock(h);
-	return conn;
+	list_for_each(p, &h->list) {
+		c = list_entry(p, struct hci_conn, list);
+		if (c->type == type && !bacmp(&c->dst, ba))
+			return c;
+	}
+        return NULL;
 }
 
-/* ----- HCI Devices ----- */
-struct hci_dev {
-	atomic_t 	refcnt;
-
-	char		name[8];
-	__u32	 	flags;
-	__u16		id;
-	__u8	 	type;
-	bdaddr_t	bdaddr;
-	__u8		features[8];
-
-	__u16		pkt_type;
-
-	atomic_t 	cmd_cnt;
-	unsigned int 	acl_cnt;
-	unsigned int 	sco_cnt;
-
-	unsigned int	acl_mtu;
-	unsigned int 	sco_mtu;
-	unsigned int	acl_max;
-	unsigned int	sco_max;
-
-	void		*driver_data;
-	void		*l2cap_data;
-	void		*priv;
-
-	struct tasklet_struct 	cmd_task;
-	struct tasklet_struct	rx_task;
-	struct tasklet_struct 	tx_task;
-
-	struct sk_buff_head	rx_q;
-	struct sk_buff_head 	raw_q;
-	struct sk_buff_head 	cmd_q;
-
-	struct sk_buff     	*sent_cmd;
+void hci_acl_connect(struct hci_conn *conn);
+void hci_acl_disconn(struct hci_conn *conn, __u8 reason);
+void hci_add_sco(struct hci_conn *conn, __u16 handle);
 
-	struct semaphore	req_lock;
-	wait_queue_head_t	req_wait_q;
-	__u32			req_status;
-	__u32			req_result;
+struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
+int    hci_conn_del(struct hci_conn *conn);
+void   hci_conn_hash_flush(struct hci_dev *hdev);
 
-	struct inquiry_cache 	inq_cache;
+struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *src);
+int hci_conn_auth(struct hci_conn *conn);
+int hci_conn_encrypt(struct hci_conn *conn);
 
-	struct conn_hash 	conn_hash;
-
-	struct hci_dev_stats 	stat;
-
-	int (*open)(struct hci_dev *hdev);
-	int (*close)(struct hci_dev *hdev);
-	int (*flush)(struct hci_dev *hdev);
-	int (*send)(struct sk_buff *skb);
-};
+static inline void hci_conn_set_timer(struct hci_conn *conn, long timeout)
+{
+	mod_timer(&conn->timer, jiffies + timeout);
+}
 
-static inline void hci_dev_hold(struct hci_dev *hdev)
+static inline void hci_conn_del_timer(struct hci_conn *conn)
 {
-	atomic_inc(&hdev->refcnt);
+	del_timer(&conn->timer);
 }
 
-static inline void hci_dev_put(struct hci_dev *hdev)
+static inline void hci_conn_hold(struct hci_conn *conn)
 {
-	atomic_dec(&hdev->refcnt);
+	atomic_inc(&conn->refcnt);
+	hci_conn_del_timer(conn);
 }
 
-extern struct hci_dev *hci_dev_get(int index);
-extern int hci_register_dev(struct hci_dev *hdev);
-extern int hci_unregister_dev(struct hci_dev *hdev);
-extern int hci_dev_open(__u16 dev);
-extern int hci_dev_close(__u16 dev);
-extern int hci_dev_reset(__u16 dev);
-extern int hci_dev_reset_stat(__u16 dev);
-extern int hci_dev_info(unsigned long arg);
-extern int hci_dev_list(unsigned long arg);
-extern int hci_dev_setscan(unsigned long arg);
-extern int hci_dev_setauth(unsigned long arg);
-extern int hci_dev_setptype(unsigned long arg);
-extern int hci_conn_list(unsigned long arg);
-extern int hci_inquiry(unsigned long arg);
+static inline void hci_conn_put(struct hci_conn *conn)
+{
+	if (atomic_dec_and_test(&conn->refcnt)) {
+		if (conn->type == ACL_LINK) {
+			unsigned long timeo = (conn->out) ?
+				HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
+			hci_conn_set_timer(conn, timeo);
+		} else
+			hci_conn_set_timer(conn, HZ / 100);
+	}
+}
 
-extern __u32 hci_dev_setmode(struct hci_dev *hdev, __u32 mode);
-extern __u32 hci_dev_getmode(struct hci_dev *hdev);
+/* ----- HCI Devices ----- */
+static inline void hci_dev_put(struct hci_dev *d)
+{ 
+	if (atomic_dec_and_test(&d->refcnt))
+		d->destruct(d);
+}
+#define hci_dev_hold(d)		atomic_inc(&d->refcnt)
+
+#define hci_dev_lock(d)		spin_lock(&d->lock)
+#define hci_dev_unlock(d)	spin_unlock(&d->lock)
+#define hci_dev_lock_bh(d)	spin_lock_bh(&d->lock)
+#define hci_dev_unlock_bh(d)	spin_unlock_bh(&d->lock)
+
+struct hci_dev *hci_dev_get(int index);
+struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
+int hci_register_dev(struct hci_dev *hdev);
+int hci_unregister_dev(struct hci_dev *hdev);
+int hci_suspend_dev(struct hci_dev *hdev);
+int hci_resume_dev(struct hci_dev *hdev);
+int hci_dev_open(__u16 dev);
+int hci_dev_close(__u16 dev);
+int hci_dev_reset(__u16 dev);
+int hci_dev_reset_stat(__u16 dev);
+int hci_dev_cmd(unsigned int cmd, unsigned long arg);
+int hci_get_dev_list(unsigned long arg);
+int hci_get_dev_info(unsigned long arg);
+int hci_get_conn_list(unsigned long arg);
+int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg);
+int hci_inquiry(unsigned long arg);
 
-extern int hci_recv_frame(struct sk_buff *skb);
+int  hci_recv_frame(struct sk_buff *skb);
+void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 
 /* ----- LMP capabilities ----- */
 #define lmp_rswitch_capable(dev) (dev->features[0] & LMP_RSWITCH)
+#define lmp_encrypt_capable(dev) (dev->features[0] & LMP_ENCRYPT)
 
 /* ----- HCI tasks ----- */
 static inline void hci_sched_cmd(struct hci_dev *hdev)
@@ -284,43 +355,130 @@
 /* ----- HCI protocols ----- */
 struct hci_proto {
 	char 		*name;
-	__u32		id;
-	__u32		flags;
+	unsigned int 	id;
+	unsigned long	flags;
 
 	void		*priv;
 
-	int (*connect_ind) 	(struct hci_dev *hdev, bdaddr_t *bdaddr);
-	int (*connect_cfm)	(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 status, struct hci_conn *conn);
+	int (*connect_ind) 	(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type);
+	int (*connect_cfm)	(struct hci_conn *conn, __u8 status);
 	int (*disconn_ind)	(struct hci_conn *conn, __u8 reason);
-	int (*recv_acldata)	(struct hci_conn *conn, struct sk_buff *skb , __u16 flags);
+	int (*recv_acldata)	(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
 	int (*recv_scodata)	(struct hci_conn *conn, struct sk_buff *skb);
+	int (*auth_cfm)		(struct hci_conn *conn, __u8 status);
+	int (*encrypt_cfm)	(struct hci_conn *conn, __u8 status);
 };
 
-extern int hci_register_proto(struct hci_proto *hproto);
-extern int hci_unregister_proto(struct hci_proto *hproto);
-extern int hci_register_notifier(struct notifier_block *nb);
-extern int hci_unregister_notifier(struct notifier_block *nb);
-extern int hci_connect(struct hci_dev * hdev, bdaddr_t * bdaddr);
-extern int hci_disconnect(struct hci_conn *conn, __u8 reason);
-extern int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void * param);
-extern int hci_send_raw(struct sk_buff *skb);
-extern int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
-extern int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
+static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
+{
+	register struct hci_proto *hp;
+	int mask = 0;
+	
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->connect_ind)
+		mask |= hp->connect_ind(hdev, bdaddr, type);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->connect_ind)
+		mask |= hp->connect_ind(hdev, bdaddr, type);
+
+	return mask;
+}
+
+static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->connect_cfm)
+		hp->connect_cfm(conn, status);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->connect_cfm)
+		hp->connect_cfm(conn, status);
+}
+
+static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->disconn_ind)
+		hp->disconn_ind(conn, reason);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->disconn_ind)
+		hp->disconn_ind(conn, reason);
+}
+
+static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->auth_cfm)
+		hp->auth_cfm(conn, status);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->auth_cfm)
+		hp->auth_cfm(conn, status);
+}
+
+static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->encrypt_cfm)
+		hp->encrypt_cfm(conn, status);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->encrypt_cfm)
+		hp->encrypt_cfm(conn, status);
+}
+
+int hci_register_proto(struct hci_proto *hproto);
+int hci_unregister_proto(struct hci_proto *hproto);
+int hci_register_notifier(struct notifier_block *nb);
+int hci_unregister_notifier(struct notifier_block *nb);
+
+int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param);
+int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
+
+void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf);
+
+void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data);
 
 /* ----- HCI Sockets ----- */
-extern void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
+void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
 
 /* HCI info for socket */
-#define hci_pi(sk)	((struct hci_pinfo *) &sk->protinfo)
+#define hci_pi(sk)	((struct hci_pinfo *) &sk->tp_pinfo)
 struct hci_pinfo {
 	struct hci_dev 	  *hdev;
 	struct hci_filter filter;
 	__u32             cmsg_mask;
 };
 
+/* HCI security filter */
+#define HCI_SFLT_MAX_OGF  5
+
+struct hci_sec_filter {
+	__u32 type_mask;
+	__u32 event_mask[2];
+	__u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
+};
+
 /* ----- HCI requests ----- */
 #define HCI_REQ_DONE	  0
 #define HCI_REQ_PEND	  1
 #define HCI_REQ_CANCELED  2
 
+#define hci_req_lock(d)		down(&d->req_lock)
+#define hci_req_unlock(d)	up(&d->req_lock)
+
+void hci_req_complete(struct hci_dev *hdev, int result);
+void hci_req_cancel(struct hci_dev *hdev, int err);
+
 #endif /* __HCI_CORE_H */
diff -urN linux-2.4.18/include/net/bluetooth/hci.h linux-2.4.18-mh15/include/net/bluetooth/hci.h
--- linux-2.4.18/include/net/bluetooth/hci.h	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/include/net/bluetooth/hci.h	2004-08-01 16:26:23.000000000 +0200
@@ -23,59 +23,80 @@
 */
 
 /*
- *  $Id: hci.h,v 1.15 2001/08/05 06:02:15 maxk Exp $
+ *  $Id: hci.h,v 1.5 2002/06/27 17:29:30 maxk Exp $
  */
 
 #ifndef __HCI_H
 #define __HCI_H
 
-#include <asm/byteorder.h>
-
-#define HCI_MAX_DEV 	8
-#define HCI_MAX_FRAME_SIZE	2048
+#define HCI_MAX_ACL_SIZE	1024
+#define HCI_MAX_SCO_SIZE	255
+#define HCI_MAX_EVENT_SIZE	260
+#define HCI_MAX_FRAME_SIZE	(HCI_MAX_ACL_SIZE + 4)
 
 /* HCI dev events */
 #define HCI_DEV_REG	1
 #define HCI_DEV_UNREG   2
 #define HCI_DEV_UP	3
 #define HCI_DEV_DOWN	4
+#define HCI_DEV_SUSPEND	5
+#define HCI_DEV_RESUME	6
 
 /* HCI device types */
-#define HCI_UART 	0
+#define HCI_VHCI	0
 #define HCI_USB		1
-#define HCI_VHCI	2
-
-/* HCI device modes */
-#define HCI_NORMAL 	0x0001
-#define HCI_RAW		0x0002
-#define HCI_MODE_MASK   (HCI_NORMAL | HCI_RAW)
-#define HCI_SOCK	0x1000
-
-/* HCI device states */
-#define HCI_INIT	0x0010
-#define HCI_UP 		0x0020
-#define HCI_RUNNING	0x0040
+#define HCI_PCCARD	2
+#define HCI_UART 	3
+#define HCI_RS232 	4
+#define HCI_PCI		5
+
+/* HCI device quirks */
+enum {
+	HCI_QUIRK_RESET_ON_INIT
+};
 
 /* HCI device flags */
-#define HCI_PSCAN	0x0100
-#define HCI_ISCAN	0x0200
-#define HCI_AUTH	0x0400
+enum {
+	HCI_UP,
+	HCI_INIT,
+	HCI_RUNNING,
+
+	HCI_PSCAN,
+	HCI_ISCAN,
+	HCI_AUTH,
+	HCI_ENCRYPT,
+	HCI_INQUIRY,
+
+	HCI_RAW
+};
 
-/* HCI Ioctl defines */
+/* HCI ioctl defines */
 #define HCIDEVUP        _IOW('H', 201, int)
 #define HCIDEVDOWN      _IOW('H', 202, int)
 #define HCIDEVRESET     _IOW('H', 203, int)
-#define HCIRESETSTAT    _IOW('H', 204, int)
-#define HCIGETINFO      _IOR('H', 205, int)
-#define HCIGETDEVLIST   _IOR('H', 206, int)
-#define HCISETRAW       _IOW('H', 207, int)
-#define HCISETSCAN      _IOW('H', 208, int)
-#define HCISETAUTH      _IOW('H', 209, int)
-#define HCIINQUIRY      _IOR('H', 210, int)
-#define HCISETPTYPE     _IOW('H', 211, int)
+#define HCIDEVRESTAT    _IOW('H', 204, int)
+
+#define HCIGETDEVLIST   _IOR('H', 210, int)
+#define HCIGETDEVINFO   _IOR('H', 211, int)
 #define HCIGETCONNLIST  _IOR('H', 212, int)
+#define HCIGETCONNINFO  _IOR('H', 213, int)
 
-#ifndef __NO_HCI_DEFS
+#define HCISETRAW       _IOW('H', 220, int)
+#define HCISETSCAN      _IOW('H', 221, int)
+#define HCISETAUTH      _IOW('H', 222, int)
+#define HCISETENCRYPT   _IOW('H', 223, int)
+#define HCISETPTYPE     _IOW('H', 224, int)
+#define HCISETLINKPOL   _IOW('H', 225, int)
+#define HCISETLINKMODE  _IOW('H', 226, int)
+#define HCISETACLMTU    _IOW('H', 227, int)
+#define HCISETSCOMTU    _IOW('H', 228, int)
+
+#define HCIINQUIRY      _IOR('H', 240, int)
+
+/* HCI timeouts */
+#define HCI_CONN_TIMEOUT 	(HZ * 40)
+#define HCI_DISCONN_TIMEOUT 	(HZ * 2)
+#define HCI_CONN_IDLE_TIMEOUT	(HZ * 60)
 
 /* HCI Packet types */
 #define HCI_COMMAND_PKT		0x01
@@ -92,11 +113,18 @@
 #define HCI_DH3 	0x0800
 #define HCI_DH5 	0x8000
 
+#define HCI_HV1		0x0020
+#define HCI_HV2		0x0040
+#define HCI_HV3		0x0080
+
+#define SCO_PTYPE_MASK	(HCI_HV1 | HCI_HV2 | HCI_HV3)
+#define ACL_PTYPE_MASK	(~SCO_PTYPE_MASK)
+
 /* ACL flags */
-#define ACL_CONT		0x0001
-#define ACL_START		0x0002
-#define ACL_ACTIVE_BCAST	0x0010
-#define ACL_PICO_BCAST		0x0020
+#define ACL_CONT		0x01
+#define ACL_START		0x02
+#define ACL_ACTIVE_BCAST	0x04
+#define ACL_PICO_BCAST		0x08
 
 /* Baseband links */
 #define SCO_LINK	0x00
@@ -125,6 +153,20 @@
 #define LMP_PSCHEME	0x02
 #define LMP_PCONTROL	0x04
 
+/* Link policies */
+#define HCI_LP_RSWITCH	0x0001
+#define HCI_LP_HOLD	0x0002
+#define HCI_LP_SNIFF	0x0004
+#define HCI_LP_PARK	0x0008
+
+/* Link mode */
+#define HCI_LM_ACCEPT	0x8000
+#define HCI_LM_MASTER	0x0001
+#define HCI_LM_AUTH	0x0002
+#define HCI_LM_ENCRYPT	0x0004
+#define HCI_LM_TRUSTED	0x0008
+#define HCI_LM_RELIABLE	0x0010
+
 /* -----  HCI Commands ----- */
 /* OGF & OCF values */
 
@@ -137,9 +179,10 @@
 	__u8  hci_ver;
 	__u16 hci_rev;
 	__u8  lmp_ver;
-	__u16 man_name;
-	__u16 lmp_sub;
+	__u16 manufacturer;
+	__u16 lmp_subver;
 } __attribute__ ((packed)) read_local_version_rp;
+#define READ_LOCAL_VERSION_RP_SIZE 9
 
 #define OCF_READ_LOCAL_FEATURES	0x0003
 typedef struct {
@@ -165,18 +208,24 @@
 /* Host Controller and Baseband */
 #define OGF_HOST_CTL	0x03
 #define OCF_RESET		0x0003
+#define OCF_READ_AUTH_ENABLE	0x001F
 #define OCF_WRITE_AUTH_ENABLE	0x0020
-	#define AUTH_DISABLED			0x00
-	#define AUTH_ENABLED			0x01
+	#define AUTH_DISABLED		0x00
+	#define AUTH_ENABLED		0x01
+
+#define OCF_READ_ENCRYPT_MODE	0x0021
+#define OCF_WRITE_ENCRYPT_MODE	0x0022
+	#define ENCRYPT_DISABLED	0x00
+	#define ENCRYPT_P2P		0x01
+	#define ENCRYPT_BOTH		0x02
 
 #define OCF_WRITE_CA_TIMEOUT  	0x0016	
 #define OCF_WRITE_PG_TIMEOUT  	0x0018
 
 #define OCF_WRITE_SCAN_ENABLE 	0x001A
-	#define SCANS_DISABLED		0x00
-	#define IS_ENA_PS_DIS		0x01
-	#define IS_DIS_PS_ENA		0x02
-	#define IS_ENA_PS_ENA		0x03
+	#define SCAN_DISABLED		0x00
+	#define SCAN_INQUIRY		0x01
+	#define SCAN_PAGE		0x02
 
 #define OCF_SET_EVENT_FLT	0x0005
 typedef struct {
@@ -226,9 +275,18 @@
 } __attribute__ ((packed)) write_class_of_dev_cp;
 #define WRITE_CLASS_OF_DEV_CP_SIZE 3
 
+#define OCF_HOST_BUFFER_SIZE	0x0033
+typedef struct {
+	__u16 	acl_mtu;
+	__u8 	sco_mtu;
+	__u16 	acl_max_pkt;
+	__u16	sco_max_pkt;
+} __attribute__ ((packed)) host_buffer_size_cp;
+#define HOST_BUFFER_SIZE_CP_SIZE 7
+
 /* Link Control */
 #define OGF_LINK_CTL	0x01 
-#define OCF_CREATE_CONN	0x0005
+#define OCF_CREATE_CONN		0x0005
 typedef struct {
 	bdaddr_t bdaddr;
 	__u16 	pkt_type;
@@ -246,6 +304,13 @@
 } __attribute__ ((packed)) accept_conn_req_cp;
 #define ACCEPT_CONN_REQ_CP_SIZE	7
 
+#define OCF_REJECT_CONN_REQ	0x000a
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8 	reason;
+} __attribute__ ((packed)) reject_conn_req_cp;
+#define REJECT_CONN_REQ_CP_SIZE	7
+
 #define OCF_DISCONNECT	0x0006
 typedef struct {
 	__u16 	handle;
@@ -253,17 +318,142 @@
 } __attribute__ ((packed)) disconnect_cp;
 #define DISCONNECT_CP_SIZE 3
 
+#define OCF_ADD_SCO	0x0007
+typedef struct {
+	__u16 	handle;
+	__u16 	pkt_type;
+} __attribute__ ((packed)) add_sco_cp;
+#define ADD_SCO_CP_SIZE 4
+
 #define OCF_INQUIRY		0x0001
 typedef struct {
 	__u8 	lap[3];
-	__u8 	lenght;
+	__u8 	length;
 	__u8	num_rsp;
 } __attribute__ ((packed)) inquiry_cp;
 #define INQUIRY_CP_SIZE 5
 
-#define OGF_LINK_POLICY	 	0x02   /* Link Policy */
+typedef struct {
+	__u8     status;
+	bdaddr_t bdaddr;
+} __attribute__ ((packed)) status_bdaddr_rp;
+#define STATUS_BDADDR_RP_SIZE 7
+
+#define OCF_INQUIRY_CANCEL	0x0002
+
+#define OCF_LINK_KEY_REPLY	0x000B
+#define OCF_LINK_KEY_NEG_REPLY	0x000C
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8     link_key[16];
+} __attribute__ ((packed)) link_key_reply_cp;
+#define LINK_KEY_REPLY_CP_SIZE 22
+
+#define OCF_PIN_CODE_REPLY	0x000D
+#define OCF_PIN_CODE_NEG_REPLY	0x000E
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8	 pin_len;
+	__u8	 pin_code[16];
+} __attribute__ ((packed)) pin_code_reply_cp;
+#define PIN_CODE_REPLY_CP_SIZE 23
+
+#define OCF_CHANGE_CONN_PTYPE	0x000F
+typedef struct {
+	__u16	 handle;
+	__u16	 pkt_type;
+} __attribute__ ((packed)) change_conn_ptype_cp;
+#define CHANGE_CONN_PTYPE_CP_SIZE 4
+
+#define OCF_AUTH_REQUESTED	0x0011
+typedef struct {
+	__u16	 handle;
+} __attribute__ ((packed)) auth_requested_cp;
+#define AUTH_REQUESTED_CP_SIZE 2
+
+#define OCF_SET_CONN_ENCRYPT	0x0013
+typedef struct {
+	__u16	 handle;
+	__u8	 encrypt;
+} __attribute__ ((packed)) set_conn_encrypt_cp;
+#define SET_CONN_ENCRYPT_CP_SIZE 3
+
+#define OCF_REMOTE_NAME_REQ	0x0019
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8     pscan_rep_mode;
+	__u8     pscan_mode;
+	__u16    clock_offset;
+} __attribute__ ((packed)) remote_name_req_cp;
+#define REMOTE_NAME_REQ_CP_SIZE 10
+
+#define OCF_READ_REMOTE_FEATURES 0x001B
+typedef struct {
+	__u16   handle;
+} __attribute__ ((packed)) read_remote_features_cp;
+#define READ_REMOTE_FEATURES_CP_SIZE 2
+
+#define OCF_READ_REMOTE_VERSION 0x001D
+typedef struct {
+	__u16   handle;
+} __attribute__ ((packed)) read_remote_version_cp;
+#define READ_REMOTE_VERSION_CP_SIZE 2
+
+/* Link Policy */
+#define OGF_LINK_POLICY	 0x02   
+#define OCF_ROLE_DISCOVERY	0x0009
+typedef struct {
+	__u16	handle;
+} __attribute__ ((packed)) role_discovery_cp;
+#define ROLE_DISCOVERY_CP_SIZE 2
+typedef struct {
+	__u8    status;
+	__u16	handle;
+	__u8    role;
+} __attribute__ ((packed)) role_discovery_rp;
+#define ROLE_DISCOVERY_RP_SIZE 4
+
+#define OCF_READ_LINK_POLICY	0x000C
+typedef struct {
+	__u16	handle;
+} __attribute__ ((packed)) read_link_policy_cp;
+#define READ_LINK_POLICY_CP_SIZE 2
+typedef struct {
+	__u8    status;
+	__u16	handle;
+	__u16   policy;
+} __attribute__ ((packed)) read_link_policy_rp;
+#define READ_LINK_POLICY_RP_SIZE 5
 
-/* --------- HCI Events --------- */
+#define OCF_SWITCH_ROLE	0x000B
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8     role;
+} __attribute__ ((packed)) switch_role_cp;
+#define SWITCH_ROLE_CP_SIZE 7
+
+#define OCF_WRITE_LINK_POLICY	0x000D
+typedef struct {
+	__u16	handle;
+	__u16   policy;
+} __attribute__ ((packed)) write_link_policy_cp;
+#define WRITE_LINK_POLICY_CP_SIZE 4
+typedef struct {
+	__u8    status;
+	__u16	handle;
+} __attribute__ ((packed)) write_link_policy_rp;
+#define WRITE_LINK_POLICY_RP_SIZE 3
+
+/* Status params */
+#define OGF_STATUS_PARAM 	0x05
+
+/* Testing commands */
+#define OGF_TESTING_CMD 	0x3e
+
+/* Vendor specific commands */
+#define OGF_VENDOR_CMD  	0x3f
+
+/* ---- HCI Events ---- */
 #define EVT_INQUIRY_COMPLETE 	0x01
 
 #define EVT_INQUIRY_RESULT 	0x02
@@ -272,11 +462,22 @@
 	__u8	pscan_rep_mode;
 	__u8	pscan_period_mode;
 	__u8	pscan_mode;
-	__u8	class[3];
+	__u8	dev_class[3];
 	__u16	clock_offset;
 } __attribute__ ((packed)) inquiry_info;
 #define INQUIRY_INFO_SIZE 14
 
+#define EVT_INQUIRY_RESULT_WITH_RSSI	0x22
+typedef struct {
+	bdaddr_t	bdaddr;
+	__u8	pscan_rep_mode;
+	__u8	pscan_period_mode;
+	__u8	dev_class[3];
+	__u16	clock_offset;
+	__s8	rssi;
+} __attribute__ ((packed)) inquiry_info_with_rssi;
+#define INQUIRY_INFO_WITH_RSSI_SIZE 14
+
 #define EVT_CONN_COMPLETE 	0x03
 typedef struct {
 	__u8	status;
@@ -303,6 +504,44 @@
 } __attribute__ ((packed)) evt_disconn_complete;
 #define EVT_DISCONN_COMPLETE_SIZE 4
 
+#define EVT_AUTH_COMPLETE	0x06
+typedef struct {
+	__u8 	status;
+	__u16 	handle;
+} __attribute__ ((packed)) evt_auth_complete;
+#define EVT_AUTH_COMPLETE_SIZE 3
+
+#define EVT_REMOTE_NAME_REQ_COMPLETE	0x07
+typedef struct {
+	__u8 	 status;
+	bdaddr_t bdaddr;
+	__u8 	 name[248];
+} __attribute__ ((packed)) evt_remote_name_req_complete;
+#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255
+
+#define EVT_ENCRYPT_CHANGE	0x08
+typedef struct {
+	__u8 	status;
+	__u16 	handle;
+	__u8	encrypt;
+} __attribute__ ((packed)) evt_encrypt_change;
+#define EVT_ENCRYPT_CHANGE_SIZE 5
+
+#define EVT_QOS_SETUP_COMPLETE 0x0D
+typedef struct {
+	__u8	service_type;
+	__u32	token_rate;
+	__u32	peak_bandwidth;
+	__u32	latency;
+	__u32	delay_variation;
+} __attribute__ ((packed)) hci_qos;
+typedef struct {
+	__u8	status;
+	__u16	handle;
+	hci_qos qos;
+} __attribute__ ((packed)) evt_qos_setup_complete;
+#define EVT_QOS_SETUP_COMPLETE_SIZE 20
+
 #define EVT_CMD_COMPLETE 	0x0e
 typedef struct {
 	__u8 	ncmd;
@@ -321,16 +560,78 @@
 #define EVT_NUM_COMP_PKTS	0x13
 typedef struct {
 	__u8 	num_hndl;
-	/* variable lenght part */
+	/* variable length part */
 } __attribute__ ((packed)) evt_num_comp_pkts;
 #define EVT_NUM_COMP_PKTS_SIZE 1
 
-#define EVT_HCI_DEV_EVENT	0xfd
+#define EVT_ROLE_CHANGE		0x12
+typedef struct {
+	__u8 	 status;
+	bdaddr_t bdaddr;
+	__u8     role;
+} __attribute__ ((packed)) evt_role_change;
+#define EVT_ROLE_CHANGE_SIZE 8
+
+#define EVT_PIN_CODE_REQ        0x16
+typedef struct {
+	bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_pin_code_req;
+#define EVT_PIN_CODE_REQ_SIZE 6
+
+#define EVT_LINK_KEY_REQ        0x17
+typedef struct {
+	bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_link_key_req;
+#define EVT_LINK_KEY_REQ_SIZE 6
+
+#define EVT_LINK_KEY_NOTIFY	0x18
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8	 link_key[16];
+	__u8	 key_type;
+} __attribute__ ((packed)) evt_link_key_notify;
+#define EVT_LINK_KEY_NOTIFY_SIZE 23
+
+#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B
+typedef struct {
+	__u8    status;
+	__u16   handle;
+	__u8    features[8];
+} __attribute__ ((packed)) evt_read_remote_features_complete;
+#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11
+
+#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C
+typedef struct {
+	__u8    status;
+	__u16   handle;
+	__u8    lmp_ver;
+	__u16   manufacturer;
+	__u16   lmp_subver;
+} __attribute__ ((packed)) evt_read_remote_version_complete;
+#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8
+
+/* Internal events generated by BlueZ stack */
+#define EVT_STACK_INTERNAL	0xfd
+typedef struct {
+	__u16   type;
+	__u8 	data[0];
+} __attribute__ ((packed)) evt_stack_internal;
+#define EVT_STACK_INTERNAL_SIZE 2
+
+#define EVT_SI_DEVICE  	0x01
+typedef struct {
+	__u16   event;
+	__u16 	dev_id;
+} __attribute__ ((packed)) evt_si_device;
+#define EVT_SI_DEVICE_SIZE 4
+
+#define EVT_SI_SECURITY	0x02
 typedef struct {
 	__u16 	event;
-	__u16 	param;
-} __attribute__ ((packed)) evt_hci_dev_event;
-#define EVT_HCI_DEV_EVENT_SIZE 4
+	__u16   proto;
+	__u16   subproto;
+	__u8    incomming;
+} __attribute__ ((packed)) evt_si_security;
 
 /* --------  HCI Packet structures  -------- */
 #define HCI_TYPE_LEN	1
@@ -369,14 +670,14 @@
 #define acl_handle(h)		(h & 0x0fff)
 #define acl_flags(h)		(h >> 12)
 
-#endif /* _NO_HCI_DEFS */
-
 /* HCI Socket options */
-#define HCI_DATA_DIR	0x0001
-#define HCI_FILTER	0x0002
+#define HCI_DATA_DIR	1
+#define HCI_FILTER	2
+#define HCI_TIME_STAMP	3
 
 /* HCI CMSG flags */
 #define HCI_CMSG_DIR	0x0001
+#define HCI_CMSG_TSTAMP	0x0002
 
 struct sockaddr_hci {
 	sa_family_t    hci_family;
@@ -387,27 +688,29 @@
 struct hci_filter {
 	__u32 type_mask;
 	__u32 event_mask[2];
+	__u16 opcode;
 };
 
-struct hci_dev_req {
-	__u16 dev_id;
-	__u32 dev_opt;
-};
-
-struct hci_dev_list_req {
-	__u16  dev_num;
-	struct hci_dev_req dev_req[0];	/* hci_dev_req structures */
-};
-
-struct hci_inquiry_req {
-	__u16 dev_id;
-	__u16 flags;
-	__u8  lap[3];
-	__u8  length;
-	__u8  num_rsp;
-};
-#define IREQ_CACHE_FLUSH 0x0001
+#define HCI_FLT_TYPE_BITS	31
+#define HCI_FLT_EVENT_BITS	63
+#define HCI_FLT_OGF_BITS	63
+#define HCI_FLT_OCF_BITS	127
+
+#if BITS_PER_LONG == 64
+static inline void hci_set_bit(int nr, void *addr)
+{
+	*((__u32 *) addr + (nr >> 5)) |= ((__u32) 1 << (nr & 31));
+}
+static inline int hci_test_bit(int nr, void *addr)
+{
+	return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
+}
+#else
+#define hci_set_bit	set_bit
+#define hci_test_bit	test_bit
+#endif
 
+/* Ioctl requests structures */
 struct hci_dev_stats {
 	__u32 err_rx;
 	__u32 err_tx;
@@ -433,11 +736,13 @@
 	__u8  features[8];
 
 	__u32 pkt_type;
+	__u32 link_policy;
+	__u32 link_mode;
 
 	__u16 acl_mtu;
-	__u16 acl_max;
+	__u16 acl_pkts;
 	__u16 sco_mtu;
-	__u16 sco_max;
+	__u16 sco_pkts;
 
 	struct hci_dev_stats stat;
 };
@@ -445,6 +750,20 @@
 struct hci_conn_info {
 	__u16    handle;
 	bdaddr_t bdaddr;
+	__u8	 type;
+	__u8	 out;
+	__u16	 state;
+	__u32	 link_mode;
+};
+
+struct hci_dev_req {
+	__u16 dev_id;
+	__u32 dev_opt;
+};
+
+struct hci_dev_list_req {
+	__u16  dev_num;
+	struct hci_dev_req dev_req[0];	/* hci_dev_req structures */
 };
 
 struct hci_conn_list_req {
@@ -453,4 +772,26 @@
 	struct hci_conn_info conn_info[0];
 };
 
+struct hci_conn_info_req {
+	bdaddr_t bdaddr;
+	__u8     type;
+	struct   hci_conn_info conn_info[0];
+};
+
+struct hci_inquiry_req {
+	__u16 dev_id;
+	__u16 flags;
+	__u8  lap[3];
+	__u8  length;
+	__u8  num_rsp;
+};
+#define IREQ_CACHE_FLUSH 0x0001
+
+struct hci_remotename_req {
+	__u16 dev_id;
+	__u16 flags;
+	bdaddr_t bdaddr;
+	__u8  name[248];
+};
+
 #endif /* __HCI_H */
diff -urN linux-2.4.18/include/net/bluetooth/hci_uart.h linux-2.4.18-mh15/include/net/bluetooth/hci_uart.h
--- linux-2.4.18/include/net/bluetooth/hci_uart.h	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/include/net/bluetooth/hci_uart.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,62 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * $Id: hci_uart.h,v 1.2 2001/06/02 01:40:08 maxk Exp $
- */
-
-#ifndef N_HCI
-#define N_HCI	15
-#endif
-
-#ifdef __KERNEL__
-
-#define tty2n_hci(tty)  ((struct n_hci *)((tty)->disc_data))
-#define n_hci2tty(n_hci) ((n_hci)->tty)
-
-struct n_hci {
-	struct tty_struct *tty;
-	struct hci_dev hdev;
-
-	struct sk_buff_head txq;
-	unsigned long tx_state;
-
-	spinlock_t rx_lock;
-	unsigned long rx_state;
-	unsigned long rx_count;
-	struct sk_buff *rx_skb;
-};
-
-/* Transmit states  */
-#define TRANS_SENDING		1
-#define TRANS_WAKEUP		2
-
-/* Receiver States */
-#define WAIT_PACKET_TYPE	0
-#define WAIT_EVENT_HDR	 	1
-#define WAIT_ACL_HDR		2
-#define WAIT_SCO_HDR		3
-#define WAIT_DATA	        4
-
-#endif /* __KERNEL__ */
diff -urN linux-2.4.18/include/net/bluetooth/hci_usb.h linux-2.4.18-mh15/include/net/bluetooth/hci_usb.h
--- linux-2.4.18/include/net/bluetooth/hci_usb.h	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/include/net/bluetooth/hci_usb.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,68 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * $Id: hci_usb.h,v 1.3 2001/06/02 01:40:08 maxk Exp $
- */
-
-#ifdef __KERNEL__
-
-/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
-#define HCI_DEV_CLASS        0xe0	/* Wireless class */
-#define HCI_DEV_SUBCLASS     0x01	/* RF subclass */
-#define HCI_DEV_PROTOCOL     0x01	/* Bluetooth programming protocol */
-
-#define HCI_CTRL_REQ	     0x20
-
-struct hci_usb {
-	struct usb_device 	*udev;
-
-	devrequest		dev_req;
-	struct urb 		*ctrl_urb;
-	struct urb		*intr_urb;
-	struct urb		*read_urb;
-	struct urb		*write_urb;
-
-	__u8			*read_buf;
-	__u8			*intr_buf;
-	struct sk_buff		*intr_skb;
-	int			intr_count;
-
-	__u8			bulk_out_ep_addr;
-	__u8			bulk_in_ep_addr;
-	__u8			intr_in_ep_addr;
-	__u8			intr_in_interval;
-
-	struct hci_dev		hdev;
-
-	unsigned long		tx_state;
-	struct sk_buff_head	tx_ctrl_q;
-	struct sk_buff_head	tx_write_q;
-};
-
-/* Transmit states  */
-#define HCI_TX_CTRL	1
-#define HCI_TX_WRITE	2
-
-#endif /* __KERNEL__ */
diff -urN linux-2.4.18/include/net/bluetooth/hci_vhci.h linux-2.4.18-mh15/include/net/bluetooth/hci_vhci.h
--- linux-2.4.18/include/net/bluetooth/hci_vhci.h	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/include/net/bluetooth/hci_vhci.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,50 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * $Id: hci_vhci.h,v 1.2 2001/08/01 01:02:20 maxk Exp $
- */
-
-#ifndef __HCI_VHCI_H
-#define __HCI_VHCI_H
-
-#ifdef __KERNEL__
-
-struct hci_vhci_struct {
-	struct hci_dev       hdev;
-	__u32                flags;
-	wait_queue_head_t    read_wait;
-	struct sk_buff_head  readq;
-	struct fasync_struct *fasync;
-};
-
-/* VHCI device flags */
-#define VHCI_FASYNC		0x0010
-
-#endif /* __KERNEL__ */
-
-#define VHCI_DEV	"/dev/vhci"
-#define VHCI_MINOR	250
-
-#endif /* __HCI_VHCI_H */
diff -urN linux-2.4.18/include/net/bluetooth/l2cap_core.h linux-2.4.18-mh15/include/net/bluetooth/l2cap_core.h
--- linux-2.4.18/include/net/bluetooth/l2cap_core.h	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/include/net/bluetooth/l2cap_core.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,144 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- *  $Id: l2cap_core.h,v 1.6 2001/08/03 04:19:49 maxk Exp $
- */
-
-#ifndef __L2CAP_CORE_H
-#define __L2CAP_CORE_H
-
-#ifdef __KERNEL__
-
-/* ----- L2CAP interface ----- */
-struct l2cap_iff {
-	struct list_head list;
-	struct hci_dev   *hdev;
-	bdaddr_t         *bdaddr;
-	__u16            mtu;
-	spinlock_t       lock;
-	struct list_head conn_list;
-};
-
-static inline void l2cap_iff_lock(struct l2cap_iff *iff)
-{
-	spin_lock(&iff->lock);
-}
-
-static inline void l2cap_iff_unlock(struct l2cap_iff *iff)
-{
-	spin_unlock(&iff->lock);
-}
-
-/* ----- L2CAP connections ----- */
-struct l2cap_chan_list {
-	struct sock	*head;
-	rwlock_t	lock;
-	long		num;
-};
-
-struct l2cap_conn {
-	struct l2cap_iff *iff;
-	struct list_head list;
-
-	struct hci_conn	 *hconn;
-
-	__u16		state;
-	__u8		out;
-	bdaddr_t	src;
-	bdaddr_t	dst;
-
-	spinlock_t	lock;
-	atomic_t	refcnt;
-
-	struct sk_buff *rx_skb;
-	__u32		rx_len;
-	__u8		rx_ident;
-	__u8		tx_ident;
-
-	struct l2cap_chan_list chan_list;
-
-	struct timer_list timer;
-};
-
-static inline void __l2cap_conn_link(struct l2cap_iff *iff, struct l2cap_conn *c)
-{
-	list_add(&c->list, &iff->conn_list);
-}
-
-static inline void __l2cap_conn_unlink(struct l2cap_iff *iff, struct l2cap_conn *c)
-{
-	list_del(&c->list);
-}
-
-/* ----- L2CAP channel and socket info ----- */
-#define l2cap_pi(sk)   ((struct l2cap_pinfo *) &sk->protinfo)
-
-struct l2cap_accept_q {
-	struct sock 	*head;
-	struct sock 	*tail;
-};
-
-struct l2cap_pinfo {
-	bdaddr_t	src;
-	bdaddr_t	dst;
-	__u16		psm;
-	__u16		dcid;
-	__u16		scid;
-	__u32		flags;
-
-	__u16		imtu;
-	__u16		omtu;
-	__u16		flush_to;
-
-	__u8		conf_state;
-	__u16		conf_mtu;
-
-	__u8		ident;
-
-	struct l2cap_conn 	*conn;
-	struct sock 		*next_c;
-	struct sock 		*prev_c;
-
-	struct sock *parent;
-	struct sock *next_q;
-	struct sock *prev_q;
-
-	struct l2cap_accept_q accept_q;
-};
-
-#define CONF_REQ_SENT    0x01
-#define CONF_INPUT_DONE  0x02
-#define CONF_OUTPUT_DONE 0x04
-
-extern struct bluez_sock_list l2cap_sk_list;
-extern struct list_head  l2cap_iff_list;
-extern rwlock_t l2cap_rt_lock;
-
-extern void l2cap_register_proc(void);
-extern void l2cap_unregister_proc(void);
-
-#endif /* __KERNEL__ */
-
-#endif /* __L2CAP_CORE_H */
diff -urN linux-2.4.18/include/net/bluetooth/l2cap.h linux-2.4.18-mh15/include/net/bluetooth/l2cap.h
--- linux-2.4.18/include/net/bluetooth/l2cap.h	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/include/net/bluetooth/l2cap.h	2004-08-01 16:26:23.000000000 +0200
@@ -23,22 +23,17 @@
 */
 
 /*
- *  $Id: l2cap.h,v 1.5 2001/06/14 21:28:26 maxk Exp $
+ *  $Id: l2cap.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $
  */
 
 #ifndef __L2CAP_H
 #define __L2CAP_H
 
-#include <asm/types.h>
-#include <asm/byteorder.h>
-
 /* L2CAP defaults */
 #define L2CAP_DEFAULT_MTU 	672
 #define L2CAP_DEFAULT_FLUSH_TO	0xFFFF
 
 #define L2CAP_CONN_TIMEOUT 	(HZ * 40)
-#define L2CAP_DISCONN_TIMEOUT 	(HZ * 2)
-#define L2CAP_CONN_IDLE_TIMEOUT	(HZ * 60)
 
 /* L2CAP socket address */
 struct sockaddr_l2 {
@@ -47,17 +42,12 @@
 	bdaddr_t	l2_bdaddr;
 };
 
-/* set/get sockopt defines */
-#define L2CAP_OPTIONS  0x01
+/* Socket options */
+#define L2CAP_OPTIONS	0x01
 struct l2cap_options {
 	__u16 omtu;
 	__u16 imtu;
 	__u16 flush_to;
-	__u32 token_rate;
-	__u32 bucket_size;
-	__u32 pick_band;
-	__u32 latency;
-	__u32 delay_var;
 };
 
 #define L2CAP_CONNINFO  0x02
@@ -65,6 +55,27 @@
 	__u16 hci_handle;
 };
 
+#define L2CAP_LM	0x03
+#define L2CAP_LM_MASTER		0x0001
+#define L2CAP_LM_AUTH		0x0002
+#define L2CAP_LM_ENCRYPT	0x0004
+#define L2CAP_LM_TRUSTED	0x0008
+#define L2CAP_LM_RELIABLE	0x0010
+
+#define L2CAP_QOS	0x04
+struct l2cap_qos {
+	__u16 service_type;
+	__u32 token_rate;
+	__u32 token_bucket_size;
+	__u32 peak_bandwidth;
+	__u32 latency;
+	__u32 delay_variation;
+};
+
+#define L2CAP_SERV_NO_TRAFFIC	0x00
+#define L2CAP_SERV_BEST_EFFORT	0x01
+#define L2CAP_SERV_GUARANTEED	0x02
+
 /* L2CAP command codes */
 #define L2CAP_COMMAND_REJ 0x01
 #define L2CAP_CONN_REQ    0x02
@@ -79,7 +90,6 @@
 #define L2CAP_INFO_RSP    0x0b
 
 /* L2CAP structures */
-
 typedef struct {
 	__u16      len;
 	__u16      cid;
@@ -112,11 +122,17 @@
 } __attribute__ ((packed))	l2cap_conn_rsp;
 #define L2CAP_CONN_RSP_SIZE	8
 
-#define L2CAP_CONN_SUCCESS    0x0000
-#define L2CAP_CONN_PEND       0x0001
-#define L2CAP_CONN_BAD_PSM    0x0002
-#define L2CAP_CONN_SEC_BLOCK  0x0003
-#define L2CAP_CONN_NO_MEM     0x0004
+/* connect result */
+#define L2CAP_CR_SUCCESS    0x0000
+#define L2CAP_CR_PEND       0x0001
+#define L2CAP_CR_BAD_PSM    0x0002
+#define L2CAP_CR_SEC_BLOCK  0x0003
+#define L2CAP_CR_NO_MEM     0x0004
+
+/* connect status */
+#define L2CAP_CS_NO_INFO      0x0000
+#define L2CAP_CS_AUTHEN_PEND  0x0001
+#define L2CAP_CS_AUTHOR_PEND  0x0002
 
 typedef struct {
 	__u16      dcid;
@@ -147,6 +163,8 @@
 #define L2CAP_CONF_FLUSH_TO	0x02
 #define L2CAP_CONF_QOS		0x03
 
+#define L2CAP_CONF_MAX_SIZE	22
+
 typedef struct {
 	__u16      dcid;
 	__u16      scid;
@@ -159,4 +177,82 @@
 } __attribute__ ((packed)) 	l2cap_disconn_rsp;
 #define L2CAP_DISCONN_RSP_SIZE	4
 
+typedef struct {
+	__u16       type;
+	__u8        data[0];
+} __attribute__ ((packed))	l2cap_info_req;
+#define L2CAP_INFO_REQ_SIZE	2
+
+typedef struct {
+	__u16       type;
+	__u16       result;
+	__u8        data[0];
+} __attribute__ ((packed))	l2cap_info_rsp;
+#define L2CAP_INFO_RSP_SIZE	4
+
+/* info type */
+#define L2CAP_IT_CL_MTU     0x0001
+#define L2CAP_IT_FEAT_MASK  0x0002
+
+/* info result */
+#define L2CAP_IR_SUCCESS    0x0000
+#define L2CAP_IR_NOTSUPP    0x0001
+
+/* ----- L2CAP connections ----- */
+struct l2cap_chan_list {
+	struct sock	*head;
+	rwlock_t	lock;
+	long		num;
+};
+
+struct l2cap_conn {
+	struct hci_conn	*hcon;
+
+	bdaddr_t 	*dst;
+	bdaddr_t 	*src;
+	
+	unsigned int    mtu;
+
+	spinlock_t	lock;
+	
+	struct sk_buff *rx_skb;
+	__u32		rx_len;
+	__u8		rx_ident;
+	__u8		tx_ident;
+
+	struct l2cap_chan_list chan_list;
+};
+
+/* ----- L2CAP channel and socket info ----- */
+#define l2cap_pi(sk)   ((struct l2cap_pinfo *) &sk->tp_pinfo)
+
+struct l2cap_pinfo {
+	__u16		psm;
+	__u16		dcid;
+	__u16		scid;
+
+	__u16		imtu;
+	__u16		omtu;
+	__u16		flush_to;
+	
+	__u32		link_mode;
+
+	__u8		conf_state;
+	__u8		conf_retry;
+	__u16		conf_mtu;
+
+	__u8		ident;
+
+	struct l2cap_conn 	*conn;
+	struct sock 		*next_c;
+	struct sock 		*prev_c;
+};
+
+#define L2CAP_CONF_REQ_SENT    0x01
+#define L2CAP_CONF_INPUT_DONE  0x02
+#define L2CAP_CONF_OUTPUT_DONE 0x04
+#define L2CAP_CONF_MAX_RETRIES 2
+
+void l2cap_load(void);
+
 #endif /* __L2CAP_H */
diff -urN linux-2.4.18/include/net/bluetooth/rfcomm.h linux-2.4.18-mh15/include/net/bluetooth/rfcomm.h
--- linux-2.4.18/include/net/bluetooth/rfcomm.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/include/net/bluetooth/rfcomm.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,361 @@
+/* 
+   RFCOMM implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+   Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/* 
+   RPN support    -    Dirk Husemann <hud@zurich.ibm.com>
+*/
+
+/*
+ * $Id: rfcomm.h,v 1.31 2002/10/18 20:12:11 maxk Exp $
+ */
+
+#ifndef __RFCOMM_H
+#define __RFCOMM_H
+
+#define RFCOMM_PSM 3
+
+#define RFCOMM_CONN_TIMEOUT (HZ * 30)
+#define RFCOMM_DISC_TIMEOUT (HZ * 20)
+
+#define RFCOMM_DEFAULT_MTU	127
+#define RFCOMM_DEFAULT_CREDITS	7
+
+#define RFCOMM_MAX_L2CAP_MTU	1024
+#define RFCOMM_MAX_CREDITS	40
+
+#define RFCOMM_SKB_HEAD_RESERVE	8
+#define RFCOMM_SKB_TAIL_RESERVE	2
+#define RFCOMM_SKB_RESERVE	(RFCOMM_SKB_HEAD_RESERVE + RFCOMM_SKB_TAIL_RESERVE)
+
+#define RFCOMM_SABM	0x2f
+#define RFCOMM_DISC	0x43
+#define RFCOMM_UA	0x63
+#define RFCOMM_DM	0x0f
+#define RFCOMM_UIH	0xef
+
+#define RFCOMM_TEST	0x08
+#define RFCOMM_FCON	0x28
+#define RFCOMM_FCOFF	0x18
+#define RFCOMM_MSC	0x38
+#define RFCOMM_RPN	0x24
+#define RFCOMM_RLS	0x14
+#define RFCOMM_PN	0x20
+#define RFCOMM_NSC	0x04
+
+#define RFCOMM_V24_FC	0x02
+#define RFCOMM_V24_RTC	0x04
+#define RFCOMM_V24_RTR	0x08
+#define RFCOMM_V24_IC	0x40
+#define RFCOMM_V24_DV	0x80
+
+#define RFCOMM_RPN_BR_2400	0x0
+#define RFCOMM_RPN_BR_4800	0x1
+#define RFCOMM_RPN_BR_7200	0x2
+#define RFCOMM_RPN_BR_9600	0x3
+#define RFCOMM_RPN_BR_19200	0x4
+#define RFCOMM_RPN_BR_38400	0x5
+#define RFCOMM_RPN_BR_57600	0x6
+#define RFCOMM_RPN_BR_115200	0x7
+#define RFCOMM_RPN_BR_230400	0x8
+
+#define RFCOMM_RPN_DATA_5	0x0
+#define RFCOMM_RPN_DATA_6	0x1
+#define RFCOMM_RPN_DATA_7	0x2
+#define RFCOMM_RPN_DATA_8	0x3
+
+#define RFCOMM_RPN_STOP_1	0
+#define RFCOMM_RPN_STOP_15	1
+
+#define RFCOMM_RPN_PARITY_NONE	0x0
+#define RFCOMM_RPN_PARITY_ODD	0x4
+#define RFCOMM_RPN_PARITY_EVEN	0x5
+#define RFCOMM_RPN_PARITY_MARK	0x6
+#define RFCOMM_RPN_PARITY_SPACE	0x7
+
+#define RFCOMM_RPN_FLOW_NONE	0x00
+
+#define RFCOMM_RPN_XON_CHAR	0x11
+#define RFCOMM_RPN_XOFF_CHAR	0x13
+
+#define RFCOMM_RPN_PM_BITRATE		0x0001
+#define RFCOMM_RPN_PM_DATA		0x0002
+#define RFCOMM_RPN_PM_STOP		0x0004
+#define RFCOMM_RPN_PM_PARITY		0x0008
+#define RFCOMM_RPN_PM_PARITY_TYPE	0x0010
+#define RFCOMM_RPN_PM_XON		0x0020
+#define RFCOMM_RPN_PM_XOFF		0x0040
+#define RFCOMM_RPN_PM_FLOW		0x3F00
+
+#define RFCOMM_RPN_PM_ALL		0x3F7F
+
+struct rfcomm_hdr {
+	u8 addr;
+	u8 ctrl;
+	u8 len;    // Actual size can be 2 bytes
+} __attribute__ ((packed));
+
+struct rfcomm_cmd {
+	u8 addr;
+	u8 ctrl;
+	u8 len;
+	u8 fcs;
+} __attribute__ ((packed));
+
+struct rfcomm_mcc {
+	u8 type;
+	u8 len;
+} __attribute__ ((packed));
+
+struct rfcomm_pn {
+	u8  dlci;
+	u8  flow_ctrl;
+	u8  priority;
+	u8  ack_timer;
+	u16 mtu;
+	u8  max_retrans;
+	u8  credits;
+} __attribute__ ((packed));
+
+struct rfcomm_rpn {
+	u8  dlci;
+	u8  bit_rate;
+	u8  line_settings;
+	u8  flow_ctrl;
+	u8  xon_char;
+	u8  xoff_char;
+	u16 param_mask;
+} __attribute__ ((packed));
+
+struct rfcomm_rls {
+	u8  dlci;
+	u8  status;
+} __attribute__ ((packed));
+
+struct rfcomm_msc {
+	u8  dlci;
+	u8  v24_sig;
+} __attribute__ ((packed));
+
+/* ---- Core structures, flags etc ---- */
+
+struct rfcomm_session {
+	struct list_head list;
+	struct socket   *sock;
+	unsigned long    state;
+	unsigned long    flags;
+	atomic_t         refcnt;
+	int              initiator;
+
+	/* Default DLC parameters */
+	int    cfc;
+	uint   mtu;
+
+	struct list_head dlcs;
+};
+
+struct rfcomm_dlc {
+	struct list_head      list;
+	struct rfcomm_session *session;
+	struct sk_buff_head   tx_queue;
+	struct timer_list     timer;
+
+	spinlock_t    lock;
+	unsigned long state;
+	unsigned long flags;
+	atomic_t      refcnt;
+	u8            dlci;
+	u8            addr;
+	u8            priority;
+	u8            v24_sig;
+	u8            mscex;
+
+	uint          mtu;
+	uint          cfc;
+	uint          rx_credits;
+	uint          tx_credits;
+
+	void          *owner;
+
+	void (*data_ready)(struct rfcomm_dlc *d, struct sk_buff *skb);
+	void (*state_change)(struct rfcomm_dlc *d, int err);
+	void (*modem_status)(struct rfcomm_dlc *d, u8 v24_sig);
+};
+
+/* DLC and session flags */
+#define RFCOMM_RX_THROTTLED 0
+#define RFCOMM_TX_THROTTLED 1
+#define RFCOMM_MSC_PENDING  2
+#define RFCOMM_TIMED_OUT    3
+
+/* Scheduling flags and events */
+#define RFCOMM_SCHED_STATE  0
+#define RFCOMM_SCHED_RX     1
+#define RFCOMM_SCHED_TX     2
+#define RFCOMM_SCHED_TIMEO  3
+#define RFCOMM_SCHED_WAKEUP 31
+
+/* MSC exchange flags */
+#define RFCOMM_MSCEX_TX     1
+#define RFCOMM_MSCEX_RX     2
+#define RFCOMM_MSCEX_OK     (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX)
+
+/* CFC states */
+#define RFCOMM_CFC_UNKNOWN  -1
+#define RFCOMM_CFC_DISABLED 0
+#define RFCOMM_CFC_ENABLED  RFCOMM_MAX_CREDITS
+
+extern struct task_struct *rfcomm_thread;
+extern unsigned long rfcomm_event;
+
+static inline void rfcomm_schedule(uint event)
+{
+	if (!rfcomm_thread)
+		return;
+	set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
+	wake_up_process(rfcomm_thread);
+}
+
+extern struct semaphore rfcomm_sem;
+#define rfcomm_lock()	down(&rfcomm_sem);
+#define rfcomm_unlock()	up(&rfcomm_sem);
+
+/* ---- RFCOMM DLCs (channels) ---- */
+struct rfcomm_dlc *rfcomm_dlc_alloc(int prio);
+void rfcomm_dlc_free(struct rfcomm_dlc *d);
+int  rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel);
+int  rfcomm_dlc_close(struct rfcomm_dlc *d, int reason);
+int  rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
+int  rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig);
+int  rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
+
+#define rfcomm_dlc_lock(d)	spin_lock(&d->lock)
+#define rfcomm_dlc_unlock(d)	spin_unlock(&d->lock)
+
+static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d)
+{
+	atomic_inc(&d->refcnt);
+}
+
+static inline void rfcomm_dlc_put(struct rfcomm_dlc *d)
+{
+	if (atomic_dec_and_test(&d->refcnt))
+		rfcomm_dlc_free(d);
+}
+
+extern void FASTCALL(__rfcomm_dlc_throttle(struct rfcomm_dlc *d));
+extern void FASTCALL(__rfcomm_dlc_unthrottle(struct rfcomm_dlc *d));
+
+static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d)
+{
+	if (!test_and_set_bit(RFCOMM_RX_THROTTLED, &d->flags))
+		__rfcomm_dlc_throttle(d);
+}
+
+static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
+{
+	if (test_and_clear_bit(RFCOMM_RX_THROTTLED, &d->flags))
+		__rfcomm_dlc_unthrottle(d);
+}
+
+/* ---- RFCOMM sessions ---- */
+struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state);
+struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst);
+struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err);
+void   rfcomm_session_del(struct rfcomm_session *s);
+void   rfcomm_session_close(struct rfcomm_session *s, int err);
+void   rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst);
+
+static inline void rfcomm_session_hold(struct rfcomm_session *s)
+{
+	atomic_inc(&s->refcnt);
+}
+
+static inline void rfcomm_session_put(struct rfcomm_session *s)
+{
+	if (atomic_dec_and_test(&s->refcnt))
+		rfcomm_session_del(s);
+}
+
+/* ---- RFCOMM chechsum ---- */
+extern u8 rfcomm_crc_table[];
+
+/* ---- RFCOMM sockets ---- */
+struct sockaddr_rc {
+	sa_family_t rc_family;
+	bdaddr_t    rc_bdaddr;
+	u8          rc_channel;
+};
+
+#define rfcomm_pi(sk)	((struct rfcomm_pinfo *) &sk->tp_pinfo)
+
+struct rfcomm_pinfo {
+	struct rfcomm_dlc *dlc;
+	u8 channel;
+};
+
+int  rfcomm_init_sockets(void);
+void rfcomm_cleanup_sockets(void);
+
+int  rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d);
+
+/* ---- RFCOMM TTY ---- */
+#define RFCOMM_MAX_DEV  256
+
+#define RFCOMMCREATEDEV		_IOW('R', 200, int)
+#define RFCOMMRELEASEDEV	_IOW('R', 201, int)
+#define RFCOMMGETDEVLIST	_IOR('R', 210, int)
+#define RFCOMMGETDEVINFO	_IOR('R', 211, int)
+#define RFCOMMSTEALDLC		_IOW('R', 220, int)
+
+#define RFCOMM_REUSE_DLC	0
+#define RFCOMM_RELEASE_ONHUP	1
+#define RFCOMM_HANGUP_NOW	2
+#define RFCOMM_TTY_ATTACHED	3
+
+struct rfcomm_dev_req {
+	s16      dev_id;
+	u32      flags;
+	bdaddr_t src;
+	bdaddr_t dst;
+	u8       channel;
+};
+
+struct rfcomm_dev_info {
+	s16      id;
+	u32      flags;
+	u16      state;
+	bdaddr_t src;
+	bdaddr_t dst;
+	u8       channel;
+};
+
+struct rfcomm_dev_list_req {
+	u16      dev_num;
+	struct   rfcomm_dev_info dev_info[0];
+};
+
+int  rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg);
+int  rfcomm_init_ttys(void);
+void rfcomm_cleanup_ttys(void);
+
+#endif /* __RFCOMM_H */
diff -urN linux-2.4.18/include/net/bluetooth/sco.h linux-2.4.18-mh15/include/net/bluetooth/sco.h
--- linux-2.4.18/include/net/bluetooth/sco.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/include/net/bluetooth/sco.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,81 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ *  $Id: sco.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $
+ */
+
+#ifndef __SCO_H
+#define __SCO_H
+
+/* SCO defaults */
+#define SCO_DEFAULT_MTU 	500
+#define SCO_DEFAULT_FLUSH_TO	0xFFFF
+
+#define SCO_CONN_TIMEOUT 	(HZ * 40)
+#define SCO_DISCONN_TIMEOUT 	(HZ * 2)
+#define SCO_CONN_IDLE_TIMEOUT	(HZ * 60)
+
+/* SCO socket address */
+struct sockaddr_sco {
+	sa_family_t	sco_family;
+	bdaddr_t	sco_bdaddr;
+};
+
+/* set/get sockopt defines */
+#define SCO_OPTIONS  0x01
+struct sco_options {
+	__u16 mtu;
+};
+
+#define SCO_CONNINFO  0x02
+struct sco_conninfo {
+	__u16 hci_handle;
+};
+
+/* ---- SCO connections ---- */
+struct sco_conn {
+	struct hci_conn	*hcon;
+
+	bdaddr_t 	*dst;
+	bdaddr_t 	*src;
+	
+	spinlock_t	lock;
+	struct sock 	*sk;
+
+	unsigned int    mtu;
+};
+
+#define sco_conn_lock(c)	spin_lock(&c->lock);
+#define sco_conn_unlock(c)	spin_unlock(&c->lock);
+
+/* ----- SCO socket info ----- */
+#define sco_pi(sk)   ((struct sco_pinfo *) &sk->tp_pinfo)
+
+struct sco_pinfo {
+	__u32		flags;
+	struct sco_conn	*conn;
+};
+
+#endif /* __SCO_H */
diff -urN linux-2.4.18/include/pcmcia/ciscode.h linux-2.4.18-mh15/include/pcmcia/ciscode.h
--- linux-2.4.18/include/pcmcia/ciscode.h	2001-12-21 18:42:04.000000000 +0100
+++ linux-2.4.18-mh15/include/pcmcia/ciscode.h	2004-08-01 16:26:23.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * ciscode.h 1.48 2001/08/24 12:16:12
+ * ciscode.h 1.57 2002/11/03 20:38:14
  *
  * The contents of this file are subject to the Mozilla Public License
  * Version 1.1 (the "License"); you may not use this file except in
@@ -60,6 +60,10 @@
 #define PRODID_INTEL_DUAL_RS232		0x0301
 #define PRODID_INTEL_2PLUS		0x8422
 
+#define MANFID_KME			0x0032
+#define PRODID_KME_KXLC005_A		0x0704
+#define PRODID_KME_KXLC005_B		0x2904
+
 #define MANFID_LINKSYS			0x0143
 #define PRODID_LINKSYS_PCMLM28		0xc0ab
 #define PRODID_LINKSYS_3400		0x3341
@@ -94,6 +98,8 @@
 #define PRODID_OSITECH_JACK_336		0x0007
 #define PRODID_OSITECH_SEVEN		0x0008
 
+#define MANFID_OXSEMI			0x0279
+
 #define MANFID_PIONEER			0x000b
 
 #define MANFID_PSION			0x016c
@@ -103,6 +109,7 @@
 #define PRODID_QUATECH_SPP100		0x0003
 #define PRODID_QUATECH_DUAL_RS232	0x0012
 #define PRODID_QUATECH_DUAL_RS232_D1	0x0007
+#define PRODID_QUATECH_DUAL_RS232_D2	0x0052
 #define PRODID_QUATECH_QUAD_RS232	0x001b
 #define PRODID_QUATECH_DUAL_RS422	0x000e
 #define PRODID_QUATECH_QUAD_RS422	0x0045
@@ -120,9 +127,12 @@
 
 #define MANFID_TDK			0x0105
 #define PRODID_TDK_CF010		0x0900
+#define PRODID_TDK_GN3410		0x4815
 
 #define MANFID_TOSHIBA			0x0098
 
+#define MANFID_UNGERMANN		0x02c0
+
 #define MANFID_XIRCOM			0x0105
 
 #endif /* _LINUX_CISCODE_H */
diff -urN linux-2.4.18/kernel/ksyms.c linux-2.4.18-mh15/kernel/ksyms.c
--- linux-2.4.18/kernel/ksyms.c	2002-02-25 20:38:13.000000000 +0100
+++ linux-2.4.18-mh15/kernel/ksyms.c	2004-08-01 16:26:23.000000000 +0200
@@ -47,6 +47,7 @@
 #include <linux/in6.h>
 #include <linux/completion.h>
 #include <linux/seq_file.h>
+#include <linux/firmware.h>
 #include <asm/checksum.h>
 
 #if defined(CONFIG_PROC_FS)
@@ -538,6 +539,13 @@
 EXPORT_SYMBOL(strspn);
 EXPORT_SYMBOL(strsep);
 
+#ifdef CONFIG_FW_LOADER
+EXPORT_SYMBOL(release_firmware);
+EXPORT_SYMBOL(request_firmware);
+EXPORT_SYMBOL(request_firmware_nowait);
+EXPORT_SYMBOL(register_firmware);
+#endif
+
 /* software interrupts */
 EXPORT_SYMBOL(tasklet_hi_vec);
 EXPORT_SYMBOL(tasklet_vec);
diff -urN linux-2.4.18/lib/Config.in linux-2.4.18-mh15/lib/Config.in
--- linux-2.4.18/lib/Config.in	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/lib/Config.in	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,12 @@
+#
+# Library configuration
+#
+mainmenu_option next_comment
+comment 'Library routines'
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" -a \
+     "$CONFIG_HOTPLUG" = "y" ]; then
+   tristate 'Hotplug firmware loading support (EXPERIMENTAL)' CONFIG_FW_LOADER
+fi
+
+endmenu
diff -urN linux-2.4.18/lib/firmware_class.c linux-2.4.18-mh15/lib/firmware_class.c
--- linux-2.4.18/lib/firmware_class.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/lib/firmware_class.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,573 @@
+/*
+ * firmware_class.c - Multi purpose firmware loading support
+ *
+ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
+ *
+ * Please see Documentation/firmware_class/ for more information.
+ *
+ */
+/*
+ * Based on kernel/kmod.c and drivers/usb/usb.c
+ */
+/*
+        kernel/kmod.c
+        Kirk Petersen
+
+        Reorganized not to be a daemon by Adam Richter, with guidance
+        from Greg Zornetzer.
+
+        Modified to avoid chroot and file sharing problems.
+        Mikael Pettersson
+
+        Limit the concurrent number of kmod modprobes to catch loops from
+        "modprobe needs a service that is in a module".
+        Keith Owens <kaos@ocs.com.au> December 1999
+
+        Unblock all signals when we exec a usermode process.
+        Shuu Yamaguchi <shuu@wondernetworkresources.com> December 2000
+*/
+/*
+ * drivers/usb/usb.c
+ *
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id)
+ * (C) Copyright Yggdrasil Computing, Inc. 2000
+ *     (usb_device_id matching changes by Adam J. Richter)
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <asm/hardirq.h>
+
+#include "linux/firmware.h"
+
+MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
+MODULE_DESCRIPTION("Multi purpose firmware loading support");
+MODULE_LICENSE("GPL");
+
+#define err(format, arg...) \
+     printk(KERN_ERR  "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
+#define warn(format, arg...) \
+     printk(KERN_WARNING "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
+#define dbg(format, arg...) \
+     printk(KERN_DEBUG "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
+
+static int loading_timeout = 10;	/* In seconds */
+static struct proc_dir_entry *proc_dir_timeout;
+static struct proc_dir_entry *proc_dir;
+
+#ifdef CONFIG_HOTPLUG
+
+static int
+call_helper(char *verb, const char *name, const char *device)
+{
+	char *argv[3], **envp, *buf, *scratch;
+	int i = 0;
+
+	int retval = 0;
+
+	if (!hotplug_path[0])
+		return -ENOENT;
+	if (in_interrupt()) {
+		err("in_interrupt");
+		return -EFAULT;
+	}
+	if (!current->fs->root) {
+		warn("call_policy %s -- no FS yet", verb);
+		return -EPERM;
+	}
+
+	if (!(envp = (char **) kmalloc(20 * sizeof (char *), GFP_KERNEL))) {
+		err("unable to allocate envp");
+		return -ENOMEM;
+	}
+	if (!(buf = kmalloc(256, GFP_KERNEL))) {
+		kfree(envp);
+		err("unable to allocate buf");
+		return -ENOMEM;
+	}
+
+	/* only one standardized param to hotplug command: type */
+	argv[0] = hotplug_path;
+	argv[1] = "firmware";
+	argv[2] = 0;
+
+	/* minimal command environment */
+	envp[i++] = "HOME=/";
+	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+#ifdef  DEBUG
+	/* hint that policy agent should enter no-stdout debug mode */
+	envp[i++] = "DEBUG=kernel";
+#endif
+	scratch = buf;
+
+	if (device) {
+		envp[i++] = scratch;
+		scratch += snprintf(scratch, FIRMWARE_NAME_MAX+25,
+				    "DEVPATH=/driver/firmware/%s", device) + 1;
+	}
+
+	envp[i++] = scratch;
+	scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
+
+	envp[i++] = scratch;
+	scratch += snprintf(scratch, FIRMWARE_NAME_MAX,
+			    "FIRMWARE=%s", name) + 1;
+
+	envp[i++] = 0;
+
+#ifdef  DEBUG
+	dbg("firmware: %s %s %s", argv[0], argv[1], verb);
+#endif
+
+	retval = call_usermodehelper(argv[0], argv, envp);
+	if (retval) {
+		printk("call_usermodehelper return %d\n", retval);
+	}
+
+	kfree(buf);
+	kfree(envp);
+	return retval;
+}
+#else
+
+static inline int
+call_helper(char *verb, const char *name, const char *device)
+{
+	return -ENOENT;
+}
+
+#endif /* CONFIG_HOTPLUG */
+
+struct firmware_priv {
+	struct completion completion;
+	struct proc_dir_entry *proc_dir;
+	struct proc_dir_entry *attr_data;
+	struct proc_dir_entry *attr_loading;
+	struct firmware *fw;
+	int loading;
+	int abort;
+	int alloc_size;
+	struct timer_list timeout;
+};
+
+static int
+firmware_timeout_show(char *buf, char **start, off_t off,
+		      int count, int *eof, void *data)
+{
+	return sprintf(buf, "%d\n", loading_timeout);
+}
+
+/**
+ * firmware_timeout_store:
+ * Description:
+ *	Sets the number of seconds to wait for the firmware.  Once
+ *	this expires an error will be return to the driver and no
+ *	firmware will be provided.
+ *
+ *	Note: zero means 'wait for ever'
+ *  
+ **/
+static int
+firmware_timeout_store(struct file *file, const char *buf,
+		       unsigned long count, void *data)
+{
+	loading_timeout = simple_strtol(buf, NULL, 10);
+	return count;
+}
+
+static int
+firmware_loading_show(char *buf, char **start, off_t off,
+		      int count, int *eof, void *data)
+{
+	struct firmware_priv *fw_priv = data;
+	return sprintf(buf, "%d\n", fw_priv->loading);
+}
+
+/**
+ * firmware_loading_store: - loading control file
+ * Description:
+ *	The relevant values are: 
+ *
+ *	 1: Start a load, discarding any previous partial load.
+ *	 0: Conclude the load and handle the data to the driver code.
+ *	-1: Conclude the load with an error and discard any written data.
+ **/
+static int
+firmware_loading_store(struct file *file, const char *buf,
+		       unsigned long count, void *data)
+{
+	struct firmware_priv *fw_priv = data;
+	int prev_loading = fw_priv->loading;
+
+	fw_priv->loading = simple_strtol(buf, NULL, 10);
+
+	switch (fw_priv->loading) {
+	case -1:
+		fw_priv->abort = 1;
+		wmb();
+		complete(&fw_priv->completion);
+		break;
+	case 1:
+		kfree(fw_priv->fw->data);
+		fw_priv->fw->data = NULL;
+		fw_priv->fw->size = 0;
+		fw_priv->alloc_size = 0;
+		break;
+	case 0:
+		if (prev_loading == 1)
+			complete(&fw_priv->completion);
+		break;
+	}
+
+	return count;
+}
+
+static int
+firmware_data_read(char *buffer, char **start, off_t offset,
+		   int count, int *eof, void *data)
+{
+	struct firmware_priv *fw_priv = data;
+	struct firmware *fw = fw_priv->fw;
+
+	if (offset > fw->size)
+		return 0;
+	if (offset + count > fw->size)
+		count = fw->size - offset;
+
+	memcpy(buffer, fw->data + offset, count);
+	*start = (void *) ((long) count);
+	return count;
+}
+static int
+fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
+{
+	u8 *new_data;
+	int new_size;
+
+	if (min_size <= fw_priv->alloc_size)
+		return 0;
+	if((min_size % PAGE_SIZE) == 0)
+		new_size = min_size;
+	else
+		new_size = (min_size + PAGE_SIZE) & PAGE_MASK;
+	new_data = vmalloc(new_size);
+	if (!new_data) {
+		printk(KERN_ERR "%s: unable to alloc buffer\n", __FUNCTION__);
+		/* Make sure that we don't keep incomplete data */
+		fw_priv->abort = 1;
+		return -ENOMEM;
+	}
+	fw_priv->alloc_size = new_size;
+	if (fw_priv->fw->data) {
+		memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
+		vfree(fw_priv->fw->data);
+	}
+	fw_priv->fw->data = new_data;
+	BUG_ON(min_size > fw_priv->alloc_size);
+	return 0;
+}
+
+/**
+ * firmware_data_write:
+ *
+ * Description:
+ *
+ *	Data written to the 'data' attribute will be later handled to
+ *	the driver as a firmware image.
+ **/
+static int
+firmware_data_write(struct file *file, const char *buffer,
+		    unsigned long count, void *data)
+{
+	struct firmware_priv *fw_priv = data;
+	struct firmware *fw = fw_priv->fw;
+	int offset = file->f_pos;
+	int retval;
+
+	retval = fw_realloc_buffer(fw_priv, offset + count);
+	if (retval) {
+		printk("%s: retval:%d\n", __FUNCTION__, retval);
+		return retval;
+	}
+
+	memcpy(fw->data + offset, buffer, count);
+
+	fw->size = max_t(size_t, offset + count, fw->size);
+	file->f_pos += count;
+	return count;
+}
+
+static void
+firmware_class_timeout(u_long data)
+{
+	struct firmware_priv *fw_priv = (struct firmware_priv *) data;
+	fw_priv->abort = 1;
+	wmb();
+	complete(&fw_priv->completion);
+}
+static int
+fw_setup_class_device(struct firmware_priv **fw_priv_p,
+		      const char *fw_name, const char *device)
+{
+	int retval;
+	struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv),
+						GFP_KERNEL);
+	*fw_priv_p = fw_priv;
+	if (!fw_priv) {
+		retval = -ENOMEM;
+		goto out;
+	}
+	memset(fw_priv, 0, sizeof (*fw_priv));
+
+	init_completion(&fw_priv->completion);
+
+	fw_priv->timeout.function = firmware_class_timeout;
+	fw_priv->timeout.data = (u_long) fw_priv;
+	init_timer(&fw_priv->timeout);
+
+	retval = -EAGAIN;
+	fw_priv->proc_dir = create_proc_entry(device, 0644 | S_IFDIR, proc_dir);
+	if (!fw_priv->proc_dir)
+		goto err_free_fw_priv;
+
+	fw_priv->attr_data = create_proc_entry("data", 0644 | S_IFREG,
+					       fw_priv->proc_dir);
+	if (!fw_priv->attr_data)
+		goto err_remove_dir;
+
+	fw_priv->attr_data->read_proc = firmware_data_read;
+	fw_priv->attr_data->write_proc = firmware_data_write;
+	fw_priv->attr_data->data = fw_priv;
+
+	fw_priv->attr_loading = create_proc_entry("loading", 0644 | S_IFREG,
+						  fw_priv->proc_dir);
+	if (!fw_priv->attr_loading)
+		goto err_remove_data;
+
+	fw_priv->attr_loading->read_proc = firmware_loading_show;
+	fw_priv->attr_loading->write_proc = firmware_loading_store;
+	fw_priv->attr_loading->data = fw_priv;
+
+	retval = 0;
+	fw_priv->fw = kmalloc(sizeof (struct firmware), GFP_KERNEL);
+	if (!fw_priv->fw) {
+		printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
+		       __FUNCTION__);
+		retval = -ENOMEM;
+		goto err_remove_loading;
+	}
+	memset(fw_priv->fw, 0, sizeof (*fw_priv->fw));
+
+	goto out;
+
+err_remove_loading:
+	remove_proc_entry("loading", fw_priv->proc_dir);
+err_remove_data:
+	remove_proc_entry("data", fw_priv->proc_dir);
+err_remove_dir:
+	remove_proc_entry(device, proc_dir);
+err_free_fw_priv:
+	kfree(fw_priv);
+out:
+	return retval;
+}
+static void
+fw_remove_class_device(struct firmware_priv *fw_priv)
+{
+	remove_proc_entry("loading", fw_priv->proc_dir);
+	remove_proc_entry("data", fw_priv->proc_dir);
+	remove_proc_entry(fw_priv->proc_dir->name, proc_dir);
+}
+
+/** 
+ * request_firmware: - request firmware to hotplug and wait for it
+ * Description:
+ *	@firmware will be used to return a firmware image by the name
+ *	of @name for device @device.
+ *
+ *	Should be called from user context where sleeping is allowed.
+ *
+ *	@name will be use as $FIRMWARE in the hotplug environment and
+ *	should be distinctive enough not to be confused with any other
+ *	firmware image for this or any other device.
+ **/
+int
+request_firmware(const struct firmware **firmware, const char *name,
+		 const char *device)
+{
+	struct firmware_priv *fw_priv;
+	int retval;
+
+	if (!firmware) {
+		retval = -EINVAL;
+		goto out;
+	}
+	*firmware = NULL;
+
+	retval = fw_setup_class_device(&fw_priv, name, device);
+	if (retval)
+		goto out;
+
+	retval = call_helper("add", name, device);
+	if (retval)
+		goto out;
+	if (loading_timeout) {
+		fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
+		add_timer(&fw_priv->timeout);
+	}
+
+	wait_for_completion(&fw_priv->completion);
+
+	del_timer(&fw_priv->timeout);
+	fw_remove_class_device(fw_priv);
+
+	if (fw_priv->fw->size && !fw_priv->abort) {
+		*firmware = fw_priv->fw;
+	} else {
+		retval = -ENOENT;
+		vfree(fw_priv->fw->data);
+		kfree(fw_priv->fw);
+	}
+out:
+	kfree(fw_priv);
+	return retval;
+}
+
+void
+release_firmware(const struct firmware *fw)
+{
+	if (fw) {
+		vfree(fw->data);
+		kfree(fw);
+	}
+}
+
+/**
+ * register_firmware: - provide a firmware image for later usage
+ * 
+ * Description:
+ *	Make sure that @data will be available by requesting firmware @name.
+ *
+ *	Note: This will not be possible until some kind of persistence
+ *	is available.
+ **/
+void
+register_firmware(const char *name, const u8 *data, size_t size)
+{
+	/* This is meaningless without firmware caching, so until we
+	 * decide if firmware caching is reasonable just leave it as a
+	 * noop */
+}
+
+/* Async support */
+struct firmware_work {
+	struct tq_struct work;
+	struct module *module;
+	const char *name;
+	const char *device;
+	void *context;
+	void (*cont)(const struct firmware *fw, void *context);
+};
+
+static void
+request_firmware_work_func(void *arg)
+{
+	struct firmware_work *fw_work = arg;
+	const struct firmware *fw;
+	if (!arg)
+		return;
+	request_firmware(&fw, fw_work->name, fw_work->device);
+	fw_work->cont(fw, fw_work->context);
+	release_firmware(fw);
+	__MOD_DEC_USE_COUNT(fw_work->module);
+	kfree(fw_work);
+}
+
+/**
+ * request_firmware_nowait:
+ *
+ * Description:
+ *	Asynchronous variant of request_firmware() for contexts where
+ *	it is not possible to sleep.
+ *
+ *	@cont will be called asynchronously when the firmware request is over.
+ *
+ *	@context will be passed over to @cont.
+ *
+ *	@fw may be %NULL if firmware request fails.
+ *
+ **/
+int
+request_firmware_nowait(
+	struct module *module,
+	const char *name, const char *device, void *context,
+	void (*cont)(const struct firmware *fw, void *context))
+{
+	struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
+						GFP_ATOMIC);
+	if (!fw_work)
+		return -ENOMEM;
+	if (!try_inc_mod_count(module)) {
+		kfree(fw_work);
+		return -EFAULT;
+	}
+
+	*fw_work = (struct firmware_work) {
+		.module = module,
+		.name = name,
+		.device = device,
+		.context = context,
+		.cont = cont,
+	};
+	INIT_TQUEUE(&fw_work->work, request_firmware_work_func, fw_work);
+
+	schedule_task(&fw_work->work);
+	return 0;
+}
+
+static int __init
+firmware_class_init(void)
+{
+	proc_dir = create_proc_entry("driver/firmware", 0755 | S_IFDIR, NULL);
+	if (!proc_dir)
+		return -EAGAIN;
+	proc_dir_timeout = create_proc_entry("timeout",
+					     0644 | S_IFREG, proc_dir);
+	if (!proc_dir_timeout) {
+		remove_proc_entry("driver/firmware", NULL);
+		return -EAGAIN;
+	}
+	proc_dir_timeout->read_proc = firmware_timeout_show;
+	proc_dir_timeout->write_proc = firmware_timeout_store;
+	return 0;
+}
+static void __exit
+firmware_class_exit(void)
+{
+	remove_proc_entry("timeout", proc_dir);
+	remove_proc_entry("driver/firmware", NULL);
+}
+
+module_init(firmware_class_init);
+module_exit(firmware_class_exit);
+
+#ifndef CONFIG_FW_LOADER
+EXPORT_SYMBOL(release_firmware);
+EXPORT_SYMBOL(request_firmware);
+EXPORT_SYMBOL(request_firmware_nowait);
+EXPORT_SYMBOL(register_firmware);
+#endif
diff -urN linux-2.4.18/lib/Makefile linux-2.4.18-mh15/lib/Makefile
--- linux-2.4.18/lib/Makefile	2001-09-18 00:31:15.000000000 +0200
+++ linux-2.4.18-mh15/lib/Makefile	2004-08-01 16:26:23.000000000 +0200
@@ -8,13 +8,17 @@
 
 L_TARGET := lib.a
 
-export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o
+export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \
+	       firmware_class.o
 
 obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o bust_spinlocks.o rbtree.o
 
+obj-$(CONFIG_FW_LOADER) += firmware_class.o
 obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
 obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
 
+include $(TOPDIR)/drivers/bluetooth/Makefile.lib
+
 ifneq ($(CONFIG_HAVE_DEC_LOCK),y) 
   obj-y += dec_and_lock.o
 endif
diff -urN linux-2.4.18/MAINTAINERS linux-2.4.18-mh15/MAINTAINERS
--- linux-2.4.18/MAINTAINERS	2002-02-25 20:37:52.000000000 +0100
+++ linux-2.4.18-mh15/MAINTAINERS	2004-08-01 16:26:23.000000000 +0200
@@ -252,10 +252,88 @@
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 
-BLUETOOTH SUBSYSTEM (BlueZ)
+BLUETOOTH SUBSYSTEM
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
 P:	Maxim Krasnyansky
 M:	maxk@qualcomm.com
+L:	bluez-devel@lists.sf.net
 W:	http://bluez.sf.net
+W:	http://www.bluez.org
+W:	http://www.holtmann.org/linux/bluetooth/
+S:	Maintained
+
+BLUETOOTH RFCOMM LAYER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+P:	Maxim Krasnyansky
+M:	maxk@qualcomm.com
+S:	Maintained
+
+BLUETOOTH BNEP LAYER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+P:	Maxim Krasnyansky
+M:	maxk@qualcomm.com
+S:	Maintained
+
+BLUETOOTH CMTP LAYER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+S:	Maintained
+
+BLUETOOTH HIDP LAYER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+S:	Maintained
+
+BLUETOOTH HCI UART DRIVER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+P:	Maxim Krasnyansky
+M:	maxk@qualcomm.com
+S:	Maintained
+
+BLUETOOTH HCI USB DRIVER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+P:	Maxim Krasnyansky
+M:	maxk@qualcomm.com
+S:	Maintained
+
+BLUETOOTH HCI BCM203X DRIVER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+S:	Maintained
+
+BLUETOOTH HCI BFUSB DRIVER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+S:	Maintained
+
+BLUETOOTH HCI DTL1 DRIVER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+S:	Maintained
+
+BLUETOOTH HCI BLUECARD DRIVER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+S:	Maintained
+
+BLUETOOTH HCI BT3C DRIVER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+S:	Maintained
+
+BLUETOOTH HCI BTUART DRIVER
+P:	Marcel Holtmann
+M:	marcel@holtmann.org
+S:	Maintained
+
+BLUETOOTH HCI VHCI DRIVER
+P:	Maxim Krasnyansky
+M:	maxk@qualcomm.com
 S:	Maintained
 
 BTTV VIDEO4LINUX DRIVER
diff -urN linux-2.4.18/net/bluetooth/af_bluetooth.c linux-2.4.18-mh15/net/bluetooth/af_bluetooth.c
--- linux-2.4.18/net/bluetooth/af_bluetooth.c	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/net/bluetooth/af_bluetooth.c	2004-08-01 16:26:23.000000000 +0200
@@ -25,14 +25,15 @@
 /*
  * BlueZ Bluetooth address family and sockets.
  *
- * $Id: af_bluetooth.c,v 1.4 2001/07/05 18:42:44 maxk Exp $
+ * $Id: af_bluetooth.c,v 1.8 2002/07/22 20:32:54 maxk Exp $
  */
-#define VERSION "1.1"
+#define VERSION "2.4"
 
 #include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/types.h>
+#include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
@@ -40,6 +41,7 @@
 #include <linux/slab.h>
 #include <linux/skbuff.h>
 #include <linux/init.h>
+#include <linux/poll.h>
 #include <linux/proc_fs.h>
 #include <net/sock.h>
 
@@ -48,70 +50,79 @@
 #endif
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
+
+#ifndef AF_BLUETOOTH_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
 
 /* Bluetooth sockets */
-static struct net_proto_family *bluez_sock[BLUEZ_MAX_PROTO];
+#define BLUEZ_MAX_PROTO	7
+static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
 
 int bluez_sock_register(int proto, struct net_proto_family *ops)
 {
-	if (proto > BLUEZ_MAX_PROTO)
+	if (proto >= BLUEZ_MAX_PROTO)
 		return -EINVAL;
 
-	if (bluez_sock[proto])
+	if (bluez_proto[proto])
 		return -EEXIST;
 
-	bluez_sock[proto] = ops;
+	bluez_proto[proto] = ops;
 	return 0;
 }
 
 int bluez_sock_unregister(int proto)
 {
-	if (proto > BLUEZ_MAX_PROTO)
+	if (proto >= BLUEZ_MAX_PROTO)
 		return -EINVAL;
 
-	if (!bluez_sock[proto])
+	if (!bluez_proto[proto])
 		return -ENOENT;
 
-	bluez_sock[proto] = NULL;
+	bluez_proto[proto] = NULL;
 	return 0;
 }
 
 static int bluez_sock_create(struct socket *sock, int proto)
 {
-	if (proto > BLUEZ_MAX_PROTO)
+	if (proto >= BLUEZ_MAX_PROTO)
 		return -EINVAL;
 
 #if defined(CONFIG_KMOD)
-	if (!bluez_sock[proto]) {
+	if (!bluez_proto[proto]) {
 		char module_name[30];
 		sprintf(module_name, "bt-proto-%d", proto);
 		request_module(module_name);
 	}
 #endif
 
-	if (!bluez_sock[proto])
+	if (!bluez_proto[proto])
 		return -ENOENT;
 
-	return bluez_sock[proto]->create(sock, proto);
+	return bluez_proto[proto]->create(sock, proto);
+}
+
+void bluez_sock_init(struct socket *sock, struct sock *sk)
+{ 
+	sock_init_data(sock, sk);
+	INIT_LIST_HEAD(&bluez_pi(sk)->accept_q);
 }
 
 void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk)
 {
-	write_lock(&l->lock);
-
+	write_lock_bh(&l->lock);
 	sk->next = l->head;
 	l->head = sk;
 	sock_hold(sk);
-
-	write_unlock(&l->lock);
+	write_unlock_bh(&l->lock);
 }
 
 void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk)
 {
 	struct sock **skp;
 
-	write_lock(&l->lock);
+	write_lock_bh(&l->lock);
 	for (skp = &l->head; *skp; skp = &((*skp)->next)) {
 		if (*skp == sk) {
 			*skp = sk->next;
@@ -119,7 +130,163 @@
 			break;
 		}
 	}
-	write_unlock(&l->lock);
+	write_unlock_bh(&l->lock);
+}
+
+void bluez_accept_enqueue(struct sock *parent, struct sock *sk)
+{
+	BT_DBG("parent %p, sk %p", parent, sk);
+
+	sock_hold(sk);
+	list_add_tail(&bluez_pi(sk)->accept_q, &bluez_pi(parent)->accept_q);
+	bluez_pi(sk)->parent = parent;
+	parent->ack_backlog++;
+}
+
+static void bluez_accept_unlink(struct sock *sk)
+{
+	BT_DBG("sk %p state %d", sk, sk->state);
+
+	list_del_init(&bluez_pi(sk)->accept_q);
+	bluez_pi(sk)->parent->ack_backlog--;
+	bluez_pi(sk)->parent = NULL;
+	sock_put(sk);
+}
+
+struct sock *bluez_accept_dequeue(struct sock *parent, struct socket *newsock)
+{
+	struct list_head *p, *n;
+	struct bluez_pinfo *pi;
+	struct sock *sk;
+	
+	BT_DBG("parent %p", parent);
+
+	list_for_each_safe(p, n, &bluez_pi(parent)->accept_q) {
+		pi = list_entry(p, struct bluez_pinfo, accept_q);
+		sk = bluez_sk(pi);
+		
+		lock_sock(sk);
+		if (sk->state == BT_CLOSED) {
+			release_sock(sk);
+			bluez_accept_unlink(sk);
+			continue;
+		}
+		
+		if (sk->state == BT_CONNECTED || !newsock) {
+			bluez_accept_unlink(sk);
+			if (newsock)
+				sock_graft(sk, newsock);
+			release_sock(sk);
+			return sk;
+		}
+		release_sock(sk);
+	}
+	return NULL;
+}
+
+int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
+{
+	int noblock = flags & MSG_DONTWAIT;
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int copied, err;
+
+	BT_DBG("sock %p sk %p len %d", sock, sk, len);
+
+	if (flags & (MSG_OOB))
+		return -EOPNOTSUPP;
+
+	if (!(skb = skb_recv_datagram(sk, flags, noblock, &err))) {
+		if (sk->shutdown & RCV_SHUTDOWN)
+			return 0;
+		return err;
+	}
+
+	msg->msg_namelen = 0;
+
+	copied = skb->len;
+	if (len < copied) {
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
+	}
+
+	skb->h.raw = skb->data;
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+	skb_free_datagram(sk, skb);
+
+	return err ? : copied;
+}
+
+unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
+{
+	struct sock *sk = sock->sk;
+	unsigned int mask = 0;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	poll_wait(file, sk->sleep, wait);
+
+	if (sk->err || !skb_queue_empty(&sk->error_queue))
+		mask |= POLLERR;
+
+	if (sk->shutdown == SHUTDOWN_MASK)
+		mask |= POLLHUP;
+
+	if (!skb_queue_empty(&sk->receive_queue) || 
+			!list_empty(&bluez_pi(sk)->accept_q) ||
+			(sk->shutdown & RCV_SHUTDOWN))
+		mask |= POLLIN | POLLRDNORM;
+
+	if (sk->state == BT_CLOSED)
+		mask |= POLLHUP;
+
+	if (sk->state == BT_CONNECT ||
+			sk->state == BT_CONNECT2 ||
+			sk->state == BT_CONFIG)
+		return mask;
+
+	if (sock_writeable(sk))
+		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+	else
+		set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+
+	return mask;
+}
+
+int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int err = 0;
+
+	BT_DBG("sk %p", sk);
+
+	add_wait_queue(sk->sleep, &wait);
+	while (sk->state != state) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (!timeo) {
+			err = -EAGAIN;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			err = sock_intr_errno(timeo);
+			break;
+		}
+
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+
+		if (sk->err) {
+			err = sock_error(sk);
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+	return err;
 }
 
 struct net_proto_family bluez_sock_family_ops =
@@ -129,9 +296,9 @@
 
 int bluez_init(void)
 {
-	INF("BlueZ HCI Core ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+	BT_INFO("BlueZ Core ver %s Copyright (C) 2000,2001 Qualcomm Inc",
 		 VERSION);
-	INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
 
 	proc_mkdir("bluetooth", NULL);
 
@@ -164,5 +331,6 @@
 module_exit(bluez_cleanup);
 
 MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
-MODULE_DESCRIPTION("BlueZ HCI Core ver " VERSION);
+MODULE_DESCRIPTION("BlueZ Core ver " VERSION);
+MODULE_LICENSE("GPL");
 #endif
diff -urN linux-2.4.18/net/bluetooth/bnep/bnep.h linux-2.4.18-mh15/net/bluetooth/bnep/bnep.h
--- linux-2.4.18/net/bluetooth/bnep/bnep.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/bnep/bnep.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,185 @@
+/*
+  BNEP protocol definition for Linux Bluetooth stack (BlueZ).
+  Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+	
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License, version 2, as
+  published by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+*/
+
+/*
+ * $Id: bnep2.h,v 1.9 2002/07/14 07:09:19 maxk Exp $
+ */
+
+#ifndef _BNEP_H
+#define _BNEP_H
+
+#include <linux/types.h>
+#include <net/bluetooth/bluetooth.h>
+
+#include "crc32.h"
+
+// Limits
+#define BNEP_MAX_PROTO_FILTERS     5
+#define BNEP_MAX_MULTICAST_FILTERS 20
+
+// UUIDs
+#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
+#define BNEP_UUID16    0x02
+#define BNEP_UUID32    0x04
+#define BNEP_UUID128   0x16
+
+#define BNEP_SVC_PANU  0x1115
+#define BNEP_SVC_NAP   0x1116
+#define BNEP_SVC_GN    0x1117
+
+// Packet types
+#define BNEP_GENERAL               0x00
+#define BNEP_CONTROL               0x01
+#define BNEP_COMPRESSED            0x02
+#define BNEP_COMPRESSED_SRC_ONLY   0x03
+#define BNEP_COMPRESSED_DST_ONLY   0x04
+
+// Control types
+#define BNEP_CMD_NOT_UNDERSTOOD    0x00
+#define BNEP_SETUP_CONN_REQ        0x01
+#define BNEP_SETUP_CONN_RSP        0x02
+#define BNEP_FILTER_NET_TYPE_SET   0x03
+#define BNEP_FILTER_NET_TYPE_RSP   0x04
+#define BNEP_FILTER_MULTI_ADDR_SET 0x05
+#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
+
+// Extension types
+#define BNEP_EXT_CONTROL           0x00
+
+// Response messages 
+#define BNEP_SUCCESS               0x00
+
+#define BNEP_CONN_INVALID_DST      0x01
+#define BNEP_CONN_INVALID_SRC      0x02
+#define BNEP_CONN_INVALID_SVC      0x03
+#define BNEP_CONN_NOT_ALLOWED      0x04
+
+#define BNEP_FILTER_UNSUPPORTED_REQ    0x01
+#define BNEP_FILTER_INVALID_RANGE      0x02
+#define BNEP_FILTER_INVALID_MCADDR     0x02
+#define BNEP_FILTER_LIMIT_REACHED      0x03
+#define BNEP_FILTER_DENIED_SECURITY    0x04
+
+// L2CAP settings
+#define BNEP_MTU         1691
+#define BNEP_PSM	 0x0f
+#define BNEP_FLUSH_TO    0xffff
+#define BNEP_CONNECT_TO  15
+#define BNEP_FILTER_TO   15
+
+// Headers 
+#define BNEP_TYPE_MASK	 0x7f
+#define BNEP_EXT_HEADER	 0x80
+
+struct bnep_setup_conn_req {
+	__u8  type;
+	__u8  ctrl;
+	__u8  uuid_size;
+	__u8  service[0];
+} __attribute__((packed));
+
+struct bnep_set_filter_req {
+	__u8  type;
+	__u8  ctrl;
+	__u16 len;
+	__u8  list[0];
+} __attribute__((packed));
+
+struct bnep_control_rsp {
+	__u8  type;
+	__u8  ctrl;
+	__u16 resp;
+} __attribute__((packed));
+
+struct bnep_ext_hdr {
+	__u8  type;
+	__u8  len;
+	__u8  data[0];
+} __attribute__((packed));
+
+/* BNEP ioctl defines */
+#define BNEPCONNADD	_IOW('B', 200, int)
+#define BNEPCONNDEL	_IOW('B', 201, int)
+#define BNEPGETCONNLIST	_IOR('B', 210, int)
+#define BNEPGETCONNINFO	_IOR('B', 211, int)
+
+struct bnep_connadd_req {
+	int   sock;       // Connected socket
+	__u32 flags;
+	__u16 role;
+	char  device[16]; // Name of the Ethernet device
+};
+
+struct bnep_conndel_req {
+	__u32 flags;
+	__u8  dst[ETH_ALEN];
+};
+
+struct bnep_conninfo {
+	__u32 flags;
+	__u16 role;
+	__u16 state;	
+	__u8  dst[ETH_ALEN];
+	char  device[16];
+};
+
+struct bnep_connlist_req {
+	__u32  cnum;
+	struct bnep_conninfo *ci;
+};
+
+struct bnep_proto_filter {
+	__u16 start;
+	__u16 end;
+};
+
+int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock);
+int bnep_del_connection(struct bnep_conndel_req *req);
+int bnep_get_connlist(struct bnep_connlist_req *req);
+int bnep_get_conninfo(struct bnep_conninfo *ci);
+
+// BNEP sessions
+struct bnep_session {
+	struct list_head list;
+	
+	unsigned int  role;
+        unsigned long state;
+        unsigned long flags;
+	atomic_t      killed;
+
+	struct ethhdr eh;
+	struct msghdr msg;
+
+	struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS];
+	u64    mc_filter;
+	
+	struct socket    *sock;
+	struct net_device dev;
+	struct net_device_stats stats;
+};
+
+int bnep_net_init(struct net_device *dev);
+int bnep_sock_init(void);
+int bnep_sock_cleanup(void);
+
+static inline int bnep_mc_hash(__u8 *addr)
+{
+        return (bnep_crc32(~0, addr, ETH_ALEN) >> 26);
+}
+
+#endif
diff -urN linux-2.4.18/net/bluetooth/bnep/Config.in linux-2.4.18-mh15/net/bluetooth/bnep/Config.in
--- linux-2.4.18/net/bluetooth/bnep/Config.in	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/bnep/Config.in	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,11 @@
+#
+# Bluetooth BNEP layer configuration
+#
+
+dep_tristate 'BNEP protocol support' CONFIG_BLUEZ_BNEP $CONFIG_BLUEZ_L2CAP
+
+if [ "$CONFIG_BLUEZ_BNEP" != "n" ]; then
+   bool '  Multicast filter support' CONFIG_BLUEZ_BNEP_MC_FILTER
+   bool '  Protocol filter support'  CONFIG_BLUEZ_BNEP_PROTO_FILTER
+fi
+
diff -urN linux-2.4.18/net/bluetooth/bnep/core.c linux-2.4.18-mh15/net/bluetooth/bnep/core.c
--- linux-2.4.18/net/bluetooth/bnep/core.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/bnep/core.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,718 @@
+/* 
+   BNEP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2001-2002 Inventel Systemes
+   Written 2001-2002 by
+	Cl�ment Moreau <clement.moreau@inventel.fr>
+	David Libault  <david.libault@inventel.fr>
+
+   Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: core.c,v 1.18 2002/07/14 07:09:19 maxk Exp $
+ */ 
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/smp_lock.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+#include <linux/socket.h>
+#include <linux/file.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "bnep.h"
+
+#ifndef CONFIG_BLUEZ_BNEP_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.2"
+
+static LIST_HEAD(bnep_session_list);
+static DECLARE_RWSEM(bnep_session_sem);
+
+static struct bnep_session *__bnep_get_session(u8 *dst)
+{
+	struct bnep_session *s;
+	struct list_head *p;
+
+	BT_DBG("");
+
+	list_for_each(p, &bnep_session_list) {
+		s = list_entry(p, struct bnep_session, list);	
+		if (!memcmp(dst, s->eh.h_source, ETH_ALEN))
+			return s;
+	}
+	return NULL;
+}
+
+static void __bnep_link_session(struct bnep_session *s)
+{
+	MOD_INC_USE_COUNT;
+	list_add(&s->list, &bnep_session_list);	
+}
+
+static void __bnep_unlink_session(struct bnep_session *s)
+{
+	list_del(&s->list);
+	MOD_DEC_USE_COUNT;
+}
+
+static int bnep_send(struct bnep_session *s, void *data, size_t len)
+{
+	struct socket *sock = s->sock;
+	struct iovec iv = { data, len };
+	s->msg.msg_iov    = &iv;
+	s->msg.msg_iovlen = 1;
+	return sock->ops->sendmsg(sock, &s->msg, len, NULL);
+}
+
+static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
+{
+	struct bnep_control_rsp rsp;
+	rsp.type = BNEP_CONTROL;
+	rsp.ctrl = ctrl;
+	rsp.resp = htons(resp);
+	return bnep_send(s, &rsp, sizeof(rsp));
+}
+
+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+static inline void bnep_set_default_proto_filter(struct bnep_session *s)
+{
+	/* (IPv4, ARP)  */
+	s->proto_filter[0].start = htons(0x0800);
+	s->proto_filter[0].end   = htons(0x0806);
+	/* (RARP, AppleTalk) */
+	s->proto_filter[1].start = htons(0x8035);
+	s->proto_filter[1].end   = htons(0x80F3);
+	/* (IPX, IPv6) */
+	s->proto_filter[2].start = htons(0x8137);
+	s->proto_filter[2].end   = htons(0x86DD);
+}
+#endif
+
+static int bnep_ctrl_set_netfilter(struct bnep_session *s, u16 *data, int len)
+{
+	int n;
+
+	if (len < 2)
+		return -EILSEQ;
+
+	n = ntohs(get_unaligned(data));
+	data++; len -= 2;
+
+	if (len < n)
+		return -EILSEQ;
+
+	BT_DBG("filter len %d", n);
+
+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+	n /= 4;
+	if (n <= BNEP_MAX_PROTO_FILTERS) {
+		struct bnep_proto_filter *f = s->proto_filter;
+		int i;
+
+		for (i = 0; i < n; i++) {
+			f[i].start = get_unaligned(data++);
+			f[i].end   = get_unaligned(data++);
+
+			BT_DBG("proto filter start %d end %d",
+				f[i].start, f[i].end);
+		}
+
+		if (i < BNEP_MAX_PROTO_FILTERS)
+			memset(f + i, 0, sizeof(*f));
+
+		if (n == 0)
+			bnep_set_default_proto_filter(s);
+
+		bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
+	} else {
+		bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
+	}
+#else
+	bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
+#endif
+	return 0;
+}
+
+static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
+{
+	int n;
+
+	if (len < 2)
+		return -EILSEQ;
+
+	n = ntohs(get_unaligned((u16 *) data)); 
+	data += 2; len -= 2;
+
+	if (len < n)
+		return -EILSEQ;
+
+	BT_DBG("filter len %d", n);
+
+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+	n /= (ETH_ALEN * 2);
+
+	if (n > 0) {
+		s->mc_filter = 0;
+
+		/* Always send broadcast */
+		set_bit(bnep_mc_hash(s->dev.broadcast), &s->mc_filter);
+
+		/* Add address ranges to the multicast hash */
+		for (; n > 0; n--) {
+			u8 a1[6], *a2;
+
+			memcpy(a1, data, ETH_ALEN); data += ETH_ALEN;
+			a2 = data; data += ETH_ALEN;
+	
+			BT_DBG("mc filter %s -> %s",
+				batostr((void *) a1), batostr((void *) a2));
+
+			#define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); }
+
+			/* Iterate from a1 to a2 */
+			set_bit(bnep_mc_hash(a1), &s->mc_filter);
+			while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
+				INCA(a1);
+				set_bit(bnep_mc_hash(a1), &s->mc_filter);
+			}
+		}
+	}
+
+	BT_DBG("mc filter hash 0x%llx", s->mc_filter);
+
+	bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
+#else
+	bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
+#endif
+	return 0;
+}
+
+static int bnep_rx_control(struct bnep_session *s, void *data, int len)
+{
+	u8  cmd = *(u8 *)data;
+	int err = 0;
+
+	data++; len--;
+
+	switch (cmd) {
+	case BNEP_CMD_NOT_UNDERSTOOD:
+	case BNEP_SETUP_CONN_REQ:
+	case BNEP_SETUP_CONN_RSP:
+	case BNEP_FILTER_NET_TYPE_RSP:
+	case BNEP_FILTER_MULTI_ADDR_RSP:
+		/* Ignore these for now */
+		break;
+		
+	case BNEP_FILTER_NET_TYPE_SET:
+		err = bnep_ctrl_set_netfilter(s, data, len);
+		break;
+
+	case BNEP_FILTER_MULTI_ADDR_SET:
+		err = bnep_ctrl_set_mcfilter(s, data, len);
+		break;
+
+	default: {
+			u8 pkt[3];
+			pkt[0] = BNEP_CONTROL;
+			pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
+			pkt[2] = cmd;
+			bnep_send(s, pkt, sizeof(pkt));
+		}
+		break;
+	}
+
+	return err;
+}
+
+static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
+{
+	struct bnep_ext_hdr *h;
+	int err = 0;
+
+	do {
+		h = (void *) skb->data;
+		if (!skb_pull(skb, sizeof(*h))) {
+			err = -EILSEQ;
+			break;
+		}
+
+		BT_DBG("type 0x%x len %d", h->type, h->len);
+		
+		switch (h->type & BNEP_TYPE_MASK) {
+		case BNEP_EXT_CONTROL:
+			bnep_rx_control(s, skb->data, skb->len);
+			break;
+
+		default:
+			/* Unknown extension, skip it. */
+			break;
+		}
+
+		if (!skb_pull(skb, h->len)) {
+			err = -EILSEQ;
+			break;
+		}
+	} while (!err && (h->type & BNEP_EXT_HEADER));
+
+	return err;
+}
+
+static u8 __bnep_rx_hlen[] = {
+	ETH_HLEN,     /* BNEP_GENERAL */
+	0,            /* BNEP_CONTROL */
+	2,            /* BNEP_COMPRESSED */
+	ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
+	ETH_ALEN + 2  /* BNEP_COMPRESSED_DST_ONLY */
+};
+#define BNEP_RX_TYPES	(sizeof(__bnep_rx_hlen) - 1)
+
+static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
+{
+	struct net_device *dev = &s->dev;
+	struct sk_buff *nskb;
+	u8 type;
+
+	dev->last_rx = jiffies;
+	s->stats.rx_bytes += skb->len;
+
+	type = *(u8 *) skb->data; skb_pull(skb, 1);
+
+	if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
+		goto badframe;
+
+	if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
+		bnep_rx_control(s, skb->data, skb->len);
+		kfree_skb(skb);
+		return 0;
+	}
+
+	skb->mac.raw = skb->data;
+
+	/* Verify and pull out header */
+	if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
+		goto badframe;
+
+	s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
+
+	if (type & BNEP_EXT_HEADER) {
+		if (bnep_rx_extension(s, skb) < 0)
+			goto badframe;
+	}
+
+	/* Strip 802.1p header */
+	if (ntohs(s->eh.h_proto) == 0x8100) {
+		if (!skb_pull(skb, 4))
+			goto badframe;
+		s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
+	}
+
+	/* We have to alloc new skb and copy data here :(. Because original skb
+	 * may not be modified and because of the alignment requirements. */
+	nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
+	if (!nskb) {
+		s->stats.rx_dropped++;
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+	skb_reserve(nskb, 2);
+
+	/* Decompress header and construct ether frame */
+	switch (type & BNEP_TYPE_MASK) {
+	case BNEP_COMPRESSED:
+		memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
+		break;
+
+	case BNEP_COMPRESSED_SRC_ONLY:
+		memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
+		memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
+		put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2));
+		break;
+
+	case BNEP_COMPRESSED_DST_ONLY:
+		memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
+		memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, ETH_ALEN + 2);
+		break;
+
+	case BNEP_GENERAL:
+		memcpy(__skb_put(nskb, ETH_ALEN * 2), skb->mac.raw, ETH_ALEN * 2);
+		put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2));
+		break;
+	}
+
+	memcpy(__skb_put(nskb, skb->len), skb->data, skb->len);
+	kfree_skb(skb);
+
+	s->stats.rx_packets++;
+	nskb->dev       = dev;
+	nskb->ip_summed = CHECKSUM_UNNECESSARY;
+	nskb->protocol  = eth_type_trans(nskb, dev);
+	netif_rx_ni(nskb);
+	return 0;
+
+badframe:
+	s->stats.rx_errors++;
+	kfree_skb(skb);
+	return 0;
+}
+
+static u8 __bnep_tx_types[] = {
+	BNEP_GENERAL,
+	BNEP_COMPRESSED_SRC_ONLY,
+	BNEP_COMPRESSED_DST_ONLY,
+	BNEP_COMPRESSED
+};
+
+static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
+{
+	struct ethhdr *eh = (void *) skb->data;
+	struct socket *sock = s->sock;
+	struct iovec iv[3];
+	int len = 0, il = 0;
+	u8 type = 0;
+
+	BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
+
+	if (!skb->dev) {
+		/* Control frame sent by us */
+		goto send;
+	}
+
+	iv[il++] = (struct iovec) { &type, 1 };
+	len++;
+
+	if (!memcmp(eh->h_dest, s->eh.h_source, ETH_ALEN))
+		type |= 0x01;
+
+	if (!memcmp(eh->h_source, s->eh.h_dest, ETH_ALEN))
+		type |= 0x02;
+
+	if (type)
+		skb_pull(skb, ETH_ALEN * 2);
+
+	type = __bnep_tx_types[type];
+	switch (type) {
+	case BNEP_COMPRESSED_SRC_ONLY:
+		iv[il++] = (struct iovec) { eh->h_source, ETH_ALEN };
+		len += ETH_ALEN;
+		break;
+		
+	case BNEP_COMPRESSED_DST_ONLY:
+		iv[il++] = (struct iovec) { eh->h_dest, ETH_ALEN };
+		len += ETH_ALEN;
+		break;
+	}
+
+send:
+	iv[il++] = (struct iovec) { skb->data, skb->len };
+	len += skb->len;
+
+	/* FIXME: linearize skb */
+
+	s->msg.msg_iov    = iv;
+	s->msg.msg_iovlen = il;
+	len = sock->ops->sendmsg(sock, &s->msg, len, NULL);
+	kfree_skb(skb);
+
+	if (len > 0) {
+		s->stats.tx_bytes += len;
+		s->stats.tx_packets++;
+		return 0;
+	}
+
+	return len;
+}
+
+static int bnep_session(void *arg)
+{
+	struct bnep_session *s = arg;
+	struct net_device *dev = &s->dev;
+	struct sock *sk = s->sock->sk;
+	struct sk_buff *skb;
+	wait_queue_t wait;
+
+	BT_DBG("");
+
+	daemonize(); reparent_to_init();
+
+	sprintf(current->comm, "kbnepd %s", dev->name);
+
+	sigfillset(&current->blocked);
+	flush_signals(current);
+
+	current->nice = -15;
+
+	set_fs(KERNEL_DS);
+
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(sk->sleep, &wait);
+	while (!atomic_read(&s->killed)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		// RX
+		while ((skb = skb_dequeue(&sk->receive_queue))) {
+			skb_orphan(skb);
+			bnep_rx_frame(s, skb);
+		}
+
+		if (sk->state != BT_CONNECTED)
+			break;
+
+		// TX
+		while ((skb = skb_dequeue(&sk->write_queue)))
+			if (bnep_tx_frame(s, skb))
+				break;
+		netif_wake_queue(dev);
+
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+
+	/* Cleanup session */
+	down_write(&bnep_session_sem);
+
+	/* Delete network device */
+	unregister_netdev(dev);
+
+	/* Release the socket */
+	fput(s->sock->file);
+
+	__bnep_unlink_session(s);
+
+	up_write(&bnep_session_sem);
+	kfree(s);
+	return 0;
+}
+
+int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
+{
+	struct net_device *dev;
+	struct bnep_session *s, *ss;
+	u8 dst[ETH_ALEN], src[ETH_ALEN];
+	int err;
+
+	BT_DBG("");
+
+	baswap((void *) dst, &bluez_pi(sock->sk)->dst);
+	baswap((void *) src, &bluez_pi(sock->sk)->src);
+
+	s = kmalloc(sizeof(struct bnep_session), GFP_KERNEL);
+	if (!s) 
+		return -ENOMEM;
+	memset(s, 0, sizeof(struct bnep_session));
+
+	down_write(&bnep_session_sem);
+
+	ss = __bnep_get_session(dst);
+	if (ss && ss->state == BT_CONNECTED) {
+		err = -EEXIST;
+		goto failed;
+	}
+
+	dev = &s->dev;
+	
+	if (*req->device)
+		strcpy(dev->name, req->device);
+	else
+		strcpy(dev->name, "bnep%d");
+
+	memset(dev->broadcast, 0xff, ETH_ALEN);
+
+	/* This is rx header therefor addresses are swaped.
+	 * ie eh.h_dest is our local address. */
+	memcpy(s->eh.h_dest,   &src, ETH_ALEN);
+	memcpy(s->eh.h_source, &dst, ETH_ALEN);
+
+	s->sock  = sock;
+	s->role  = req->role;
+	s->state = BT_CONNECTED;
+
+	s->msg.msg_flags = MSG_NOSIGNAL;
+
+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+	/* Set default mc filter */
+	set_bit(bnep_mc_hash(dev->broadcast), &s->mc_filter);
+#endif
+
+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+	/* Set default protocol filter */
+	bnep_set_default_proto_filter(s);
+#endif
+
+	dev->init = bnep_net_init;
+	dev->priv = s;
+	err = register_netdev(dev);
+	if (err) {
+		goto failed;
+	}
+
+	__bnep_link_session(s);
+
+	err = kernel_thread(bnep_session, s, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	if (err < 0) {
+		/* Session thread start failed, gotta cleanup. */
+		unregister_netdev(dev);
+		__bnep_unlink_session(s);
+		goto failed;
+	}
+
+	up_write(&bnep_session_sem);
+	strcpy(req->device, dev->name);
+	return 0;
+
+failed:
+	up_write(&bnep_session_sem);
+	kfree(s);
+	return err;
+}
+
+int bnep_del_connection(struct bnep_conndel_req *req)
+{
+	struct bnep_session *s;
+	int  err = 0;
+
+	BT_DBG("");
+
+	down_read(&bnep_session_sem);
+
+	s = __bnep_get_session(req->dst);
+	if (s) {
+		/* Wakeup user-space which is polling for socket errors.
+		 * This is temporary hack untill we have shutdown in L2CAP */
+		s->sock->sk->err = EUNATCH;
+		
+		/* Kill session thread */
+		atomic_inc(&s->killed);
+		wake_up_interruptible(s->sock->sk->sleep);
+	} else
+		err = -ENOENT;
+
+	up_read(&bnep_session_sem);
+	return err;
+}
+
+static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
+{
+	memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
+	strcpy(ci->device, s->dev.name);
+	ci->flags = s->flags;
+	ci->state = s->state;
+	ci->role  = s->role;
+}
+
+int bnep_get_connlist(struct bnep_connlist_req *req)
+{
+	struct list_head *p;
+	int err = 0, n = 0;
+
+	down_read(&bnep_session_sem);
+
+	list_for_each(p, &bnep_session_list) {
+		struct bnep_session *s;
+		struct bnep_conninfo ci;
+
+		s = list_entry(p, struct bnep_session, list);
+
+		__bnep_copy_ci(&ci, s);
+		
+		if (copy_to_user(req->ci, &ci, sizeof(ci))) {
+			err = -EFAULT;
+			break;
+		}
+
+		if (++n >= req->cnum)
+			break;
+
+		req->ci++;
+	}
+	req->cnum = n;
+
+	up_read(&bnep_session_sem);
+	return err;
+}
+
+int bnep_get_conninfo(struct bnep_conninfo *ci)
+{
+	struct bnep_session *s;
+	int err = 0;
+
+	down_read(&bnep_session_sem);
+
+	s = __bnep_get_session(ci->dst);
+	if (s)
+		__bnep_copy_ci(ci, s);
+	else
+		err = -ENOENT;
+
+	up_read(&bnep_session_sem);
+	return err;
+}
+
+static int __init bnep_init_module(void)
+{
+	l2cap_load();
+
+	bnep_crc32_init();
+	bnep_sock_init();
+
+	BT_INFO("BlueZ BNEP ver %s", VERSION);
+	BT_INFO("Copyright (C) 2001,2002 Inventel Systemes");
+	BT_INFO("Written 2001,2002 by Clement Moreau <clement.moreau@inventel.fr>");
+	BT_INFO("Written 2001,2002 by David Libault <david.libault@inventel.fr>");
+	BT_INFO("Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>");
+
+	return 0;
+}
+
+static void __exit bnep_cleanup_module(void)
+{
+	bnep_sock_cleanup();
+	bnep_crc32_cleanup();
+}
+
+module_init(bnep_init_module);
+module_exit(bnep_cleanup_module);
+
+MODULE_DESCRIPTION("BlueZ BNEP ver " VERSION);
+MODULE_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyanskiy <maxk@qualcomm.com>");
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/net/bluetooth/bnep/crc32.c linux-2.4.18-mh15/net/bluetooth/bnep/crc32.c
--- linux-2.4.18/net/bluetooth/bnep/crc32.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/bnep/crc32.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,59 @@
+/* 
+ * Based on linux-2.5/lib/crc32 by Matt Domsch <Matt_Domsch@dell.com>
+ *
+ * FIXME: Remove in 2.5  
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/atomic.h>
+
+#include "crc32.h"
+
+#define CRCPOLY_BE 0x04c11db7
+#define CRC_BE_BITS 8
+
+static u32 *bnep_crc32_table;
+
+/*
+ * This code is in the public domain; copyright abandoned.
+ * Liability for non-performance of this code is limited to the amount
+ * you paid for it.  Since it is distributed for free, your refund will
+ * be very very small.  If it breaks, you get to keep both pieces.
+ */
+u32 bnep_crc32(u32 crc, unsigned char const *p, size_t len)
+{
+	while (len--)
+		crc = (crc << 8) ^ bnep_crc32_table[(crc >> 24) ^ *p++];
+	
+	return crc;
+}
+
+int __init bnep_crc32_init(void)
+{
+	unsigned i, j;
+	u32 crc = 0x80000000;
+
+	bnep_crc32_table = kmalloc((1 << CRC_BE_BITS) * sizeof(u32), GFP_KERNEL);
+	if (!bnep_crc32_table)
+		return -ENOMEM;
+
+	bnep_crc32_table[0] = 0;
+
+	for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) {
+		crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
+		for (j = 0; j < i; j++)
+			bnep_crc32_table[i + j] = crc ^ bnep_crc32_table[j];
+	}
+	return 0;
+}
+
+void __exit bnep_crc32_cleanup(void)
+{
+	if (bnep_crc32_table)
+		kfree(bnep_crc32_table);
+	bnep_crc32_table = NULL;
+}
diff -urN linux-2.4.18/net/bluetooth/bnep/crc32.h linux-2.4.18-mh15/net/bluetooth/bnep/crc32.h
--- linux-2.4.18/net/bluetooth/bnep/crc32.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/bnep/crc32.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,10 @@
+/*
+ * crc32.h
+ * See crc32.c for license and changes
+ *
+ * FIXME: Remove in 2.5
+ */
+
+int  bnep_crc32_init(void);
+void bnep_crc32_cleanup(void);
+u32  bnep_crc32(u32 crc, unsigned char const *p, size_t len);
diff -urN linux-2.4.18/net/bluetooth/bnep/Makefile linux-2.4.18-mh15/net/bluetooth/bnep/Makefile
--- linux-2.4.18/net/bluetooth/bnep/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/bnep/Makefile	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux Bluetooth BNEP layer
+#
+
+O_TARGET := bnep.o
+
+obj-y	 := core.o sock.o netdev.o crc32.o
+obj-m    += $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.4.18/net/bluetooth/bnep/netdev.c linux-2.4.18-mh15/net/bluetooth/bnep/netdev.c
--- linux-2.4.18/net/bluetooth/bnep/netdev.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/bnep/netdev.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,254 @@
+/* 
+   BNEP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2001-2002 Inventel Systemes
+   Written 2001-2002 by
+	Cl�ment Moreau <clement.moreau@inventel.fr>
+	David Libault  <david.libault@inventel.fr>
+
+   Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: netdev.c,v 1.7 2002/07/14 05:39:26 maxk Exp $
+ */ 
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/socket.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "bnep.h"
+
+#ifndef CONFIG_BLUEZ_BNEP_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+#define BNEP_TX_QUEUE_LEN 20
+
+static int bnep_net_open(struct net_device *dev)
+{
+	netif_start_queue(dev);
+	return 0;
+}
+
+static int bnep_net_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	return 0;
+}
+
+static struct net_device_stats *bnep_net_get_stats(struct net_device *dev)
+{
+	struct bnep_session *s = dev->priv;
+	return &s->stats;
+}
+
+static void bnep_net_set_mc_list(struct net_device *dev)
+{
+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+	struct bnep_session *s = dev->priv;
+	struct sock *sk = s->sock->sk;
+	struct bnep_set_filter_req *r;
+	struct sk_buff *skb;
+	int size;
+
+	BT_DBG("%s mc_count %d", dev->name, dev->mc_count);
+
+	size = sizeof(*r) + (BNEP_MAX_MULTICAST_FILTERS + 1) * ETH_ALEN * 2;
+	skb  = alloc_skb(size, GFP_ATOMIC);
+	if (!skb) {
+		BT_ERR("%s Multicast list allocation failed", dev->name);
+		return;
+	}
+
+	r = (void *) skb->data;
+	__skb_put(skb, sizeof(*r));
+
+	r->type = BNEP_CONTROL;
+	r->ctrl = BNEP_FILTER_MULTI_ADDR_SET;
+
+        if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+		u8 start[ETH_ALEN] = { 0x01 };
+
+		/* Request all addresses */
+		memcpy(__skb_put(skb, ETH_ALEN), start, ETH_ALEN);
+		memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
+		r->len = htons(ETH_ALEN * 2);
+	} else {
+                struct dev_mc_list *dmi = dev->mc_list;
+		int i, len = skb->len;
+
+		if (dev->flags & IFF_BROADCAST) {
+			memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
+			memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
+		}	
+		
+		/* FIXME: We should group addresses here. */
+
+		for (i = 0; i < dev->mc_count && i < BNEP_MAX_MULTICAST_FILTERS; i++) {
+			memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN);
+			memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN);
+			dmi = dmi->next;
+		}
+		r->len = htons(skb->len - len);
+	}
+
+	skb_queue_tail(&sk->write_queue, skb);
+	wake_up_interruptible(sk->sleep);
+#endif
+}
+
+static int bnep_net_set_mac_addr(struct net_device *dev, void *arg)
+{
+	BT_DBG("%s", dev->name);
+	return 0;
+}
+
+static void bnep_net_timeout(struct net_device *dev)
+{
+	BT_DBG("net_timeout");
+	netif_wake_queue(dev);
+}
+
+static int bnep_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	return -EINVAL;
+}
+
+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s)
+{
+	struct ethhdr *eh = (void *) skb->data;
+
+	if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), &s->mc_filter)) {
+		BT_DBG("BNEP: filtered skb %p, dst %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", skb, 
+				eh->h_dest[0], eh->h_dest[1], eh->h_dest[2],
+				eh->h_dest[3], eh->h_dest[4], eh->h_dest[5]);
+		return 1;
+	}
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+/* Determine ether protocol. Based on eth_type_trans. */
+static inline u16 bnep_net_eth_proto(struct sk_buff *skb)
+{
+	struct ethhdr *eh = (void *) skb->data;
+	
+	if (ntohs(eh->h_proto) >= 1536)
+		return eh->h_proto;
+		
+	if (get_unaligned((u16 *) skb->data) == 0xFFFF)
+		return htons(ETH_P_802_3);
+		
+	return htons(ETH_P_802_2);
+}
+
+static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s)
+{
+	u16 proto = bnep_net_eth_proto(skb);
+	struct bnep_proto_filter *f = s->proto_filter;
+	int i;
+	
+	for (i = 0; i < BNEP_MAX_PROTO_FILTERS && f[i].end; i++) {
+		if (proto >= f[i].start && proto <= f[i].end)
+			return 0;
+	}
+
+	BT_DBG("BNEP: filtered skb %p, proto 0x%.4x", skb, proto);
+	return 1;
+}
+#endif
+
+static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct bnep_session *s = dev->priv;
+	struct sock *sk = s->sock->sk;
+
+	BT_DBG("skb %p, dev %p", skb, dev);
+
+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+	if (bnep_net_mc_filter(skb, s)) {
+		kfree_skb(skb);
+		return 0;
+	}
+#endif
+	
+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+	if (bnep_net_proto_filter(skb, s)) {
+		kfree_skb(skb);
+		return 0;
+	}
+#endif
+	
+	/*
+	 * We cannot send L2CAP packets from here as we are potentially in a bh.
+	 * So we have to queue them and wake up session thread which is sleeping
+	 * on the sk->sleep.
+	 */
+	dev->trans_start = jiffies;
+	skb_queue_tail(&sk->write_queue, skb);
+	wake_up_interruptible(sk->sleep);
+
+	if (skb_queue_len(&sk->write_queue) >= BNEP_TX_QUEUE_LEN) {
+		BT_DBG("tx queue is full");
+
+		/* Stop queuing.
+		 * Session thread will do netif_wake_queue() */
+		netif_stop_queue(dev);
+	}
+
+	return 0;
+}
+
+int bnep_net_init(struct net_device *dev)
+{
+	struct bnep_session *s = dev->priv;
+
+	memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
+	dev->addr_len = ETH_ALEN;
+
+	ether_setup(dev);
+
+	dev->open            = bnep_net_open;
+	dev->stop            = bnep_net_close;
+	dev->hard_start_xmit = bnep_net_xmit;
+	dev->get_stats       = bnep_net_get_stats;
+	dev->do_ioctl        = bnep_net_ioctl;
+	dev->set_mac_address = bnep_net_set_mac_addr;
+	dev->set_multicast_list = bnep_net_set_mc_list;
+
+	dev->watchdog_timeo  = HZ * 2;
+	dev->tx_timeout      = bnep_net_timeout;
+
+	return 0;
+}
diff -urN linux-2.4.18/net/bluetooth/bnep/sock.c linux-2.4.18-mh15/net/bluetooth/bnep/sock.c
--- linux-2.4.18/net/bluetooth/bnep/sock.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/bnep/sock.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,210 @@
+/* 
+   BNEP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2001-2002 Inventel Systemes
+   Written 2001-2002 by
+	David Libault  <david.libault@inventel.fr>
+
+   Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: sock.c,v 1.3 2002/07/10 22:59:52 maxk Exp $
+ */ 
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "bnep.h"
+
+#ifndef CONFIG_BLUEZ_BNEP_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+static int bnep_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p sk %p", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	sock_orphan(sk);
+	sock_put(sk);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct bnep_connlist_req cl;
+	struct bnep_connadd_req  ca;
+	struct bnep_conndel_req  cd;
+	struct bnep_conninfo ci;
+	struct socket *nsock;
+	int err;
+
+	BT_DBG("cmd %x arg %lx", cmd, arg);
+
+	switch (cmd) {
+	case BNEPCONNADD:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+
+		if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
+			return -EFAULT;
+	
+		nsock = sockfd_lookup(ca.sock, &err);
+		if (!nsock)
+			return err;
+
+		if (nsock->sk->state != BT_CONNECTED) {
+			fput(nsock->file);
+			return -EBADFD;
+		}
+
+		err = bnep_add_connection(&ca, nsock);
+		if (!err) {
+    			if (copy_to_user((void *) arg, &ca, sizeof(ca)))
+				err = -EFAULT;
+		} else
+			fput(nsock->file);
+
+		return err;
+	
+	case BNEPCONNDEL:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+
+		if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
+			return -EFAULT;
+	
+		return bnep_del_connection(&cd);
+
+	case BNEPGETCONNLIST:
+		if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
+			return -EFAULT;
+
+		if (cl.cnum <= 0)
+			return -EINVAL;
+	
+		err = bnep_get_connlist(&cl);
+		if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
+			return -EFAULT;
+
+		return err;
+
+	case BNEPGETCONNINFO:
+		if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
+			return -EFAULT;
+
+		err = bnep_get_conninfo(&ci);
+		if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
+			return -EFAULT;
+
+		return err;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct proto_ops bnep_sock_ops = {
+	family:     PF_BLUETOOTH,
+	release:    bnep_sock_release,
+	ioctl:      bnep_sock_ioctl,
+	bind:       sock_no_bind,
+	getname:    sock_no_getname,
+	sendmsg:    sock_no_sendmsg,
+	recvmsg:    sock_no_recvmsg,
+	poll:       sock_no_poll,
+	listen:     sock_no_listen,
+	shutdown:   sock_no_shutdown,
+	setsockopt: sock_no_setsockopt,
+	getsockopt: sock_no_getsockopt,
+	connect:    sock_no_connect,
+	socketpair: sock_no_socketpair,
+	accept:     sock_no_accept,
+	mmap:       sock_no_mmap
+};
+
+static int bnep_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	if (sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	sock->ops = &bnep_sock_ops;
+
+	if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
+		return -ENOMEM;
+
+	MOD_INC_USE_COUNT;
+
+	sock->state = SS_UNCONNECTED;
+	sock_init_data(sock, sk);
+
+	sk->destruct = NULL;
+	sk->protocol = protocol;
+
+	return 0;
+}
+
+static struct net_proto_family bnep_sock_family_ops = {
+	family: PF_BLUETOOTH,
+	create: bnep_sock_create
+};
+
+int bnep_sock_init(void)
+{
+	bluez_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops);
+	return 0;
+}
+
+int bnep_sock_cleanup(void)
+{
+	if (bluez_sock_unregister(BTPROTO_BNEP))
+		BT_ERR("Can't unregister BNEP socket");
+	return 0;
+}
diff -urN linux-2.4.18/net/bluetooth/cmtp/capi.c linux-2.4.18-mh15/net/bluetooth/cmtp/capi.c
--- linux-2.4.18/net/bluetooth/cmtp/capi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/cmtp/capi.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,707 @@
+/* 
+   CMTP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <net/sock.h>
+
+#include <linux/capi.h>
+
+#include "../drivers/isdn/avmb1/capilli.h"
+#include "../drivers/isdn/avmb1/capicmd.h"
+#include "../drivers/isdn/avmb1/capiutil.h"
+
+#include "cmtp.h"
+
+#ifndef CONFIG_BLUEZ_CMTP_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define REVISION "1.0"
+
+#define CAPI_INTEROPERABILITY		0x20
+
+#define CAPI_INTEROPERABILITY_REQ	CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
+#define CAPI_INTEROPERABILITY_CONF	CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
+#define CAPI_INTEROPERABILITY_IND	CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
+#define CAPI_INTEROPERABILITY_RESP	CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
+
+#define CAPI_INTEROPERABILITY_REQ_LEN	(CAPI_MSG_BASELEN + 2)
+#define CAPI_INTEROPERABILITY_CONF_LEN	(CAPI_MSG_BASELEN + 4)
+#define CAPI_INTEROPERABILITY_IND_LEN	(CAPI_MSG_BASELEN + 2)
+#define CAPI_INTEROPERABILITY_RESP_LEN	(CAPI_MSG_BASELEN + 2)
+
+#define CAPI_FUNCTION_REGISTER		0
+#define CAPI_FUNCTION_RELEASE		1
+#define CAPI_FUNCTION_GET_PROFILE	2
+#define CAPI_FUNCTION_GET_MANUFACTURER	3
+#define CAPI_FUNCTION_GET_VERSION	4
+#define CAPI_FUNCTION_GET_SERIAL_NUMBER	5
+#define CAPI_FUNCTION_MANUFACTURER	6
+#define CAPI_FUNCTION_LOOPBACK		7
+
+static struct capi_driver_interface *di;
+
+
+#define CMTP_MSGNUM	1
+#define CMTP_APPLID	2
+#define CMTP_MAPPING	3
+
+static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
+{
+	struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL);
+
+	BT_DBG("session %p application %p appl %d", session, app, appl);
+
+	if (!app)
+		return NULL;
+
+	memset(app, 0, sizeof(*app));
+
+	app->state = BT_OPEN;
+	app->appl = appl;
+
+	list_add_tail(&app->list, &session->applications);
+
+	return app;
+}
+
+static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
+{
+	BT_DBG("session %p application %p", session, app);
+
+	if (app) {
+		list_del(&app->list);
+		kfree(app);
+	}
+}
+
+static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
+{
+	struct cmtp_application *app;
+	struct list_head *p, *n;
+
+	list_for_each_safe(p, n, &session->applications) {
+		app = list_entry(p, struct cmtp_application, list);
+		switch (pattern) {
+		case CMTP_MSGNUM:
+			if (app->msgnum == value)
+				return app;
+			break;
+		case CMTP_APPLID:
+			if (app->appl == value)
+				return app;
+			break;
+		case CMTP_MAPPING:
+			if (app->mapping == value)
+				return app;
+			break;
+		}
+	}
+
+	return NULL;
+}
+
+static int cmtp_msgnum_get(struct cmtp_session *session)
+{
+	session->msgnum++;
+
+	if ((session->msgnum & 0xff) > 200)
+		session->msgnum = CMTP_INITIAL_MSGNUM + 1;
+
+	return session->msgnum;
+}
+
+
+static void cmtp_send_interopmsg(struct cmtp_session *session,
+					__u8 subcmd, __u16 appl, __u16 msgnum,
+					__u16 function, unsigned char *buf, int len)
+{
+	struct sk_buff *skb;
+	unsigned char *s;
+
+	BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
+
+	if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
+		BT_ERR("Can't allocate memory for interoperability packet");
+		return;
+	}
+
+	s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
+
+	capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
+	capimsg_setu16(s, 2, appl);
+	capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
+	capimsg_setu8 (s, 5, subcmd);
+	capimsg_setu16(s, 6, msgnum);
+
+	/* Interoperability selector (Bluetooth Device Management) */
+	capimsg_setu16(s, 8, 0x0001);
+
+	capimsg_setu8 (s, 10, 3 + len);
+	capimsg_setu16(s, 11, function);
+	capimsg_setu8 (s, 13, len);
+
+	if (len > 0)
+		memcpy(s + 14, buf, len);
+
+	cmtp_send_capimsg(session, skb);
+}
+
+static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
+{
+	struct capi_ctr *ctrl = session->ctrl;
+	struct cmtp_application *application;
+	__u16 appl, msgnum, func, info;
+	__u32 controller;
+
+	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+	switch (CAPIMSG_SUBCOMMAND(skb->data)) {
+	case CAPI_CONF:
+		func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
+		info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
+
+		switch (func) {
+		case CAPI_FUNCTION_REGISTER:
+			msgnum = CAPIMSG_MSGID(skb->data);
+
+			application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
+			if (application) {
+				application->state = BT_CONNECTED;
+				application->msgnum = 0;
+				application->mapping = CAPIMSG_APPID(skb->data);
+				wake_up_interruptible(&session->wait);
+			}
+
+			break;
+
+		case CAPI_FUNCTION_RELEASE:
+			appl = CAPIMSG_APPID(skb->data);
+
+			application = cmtp_application_get(session, CMTP_MAPPING, appl);
+			if (application) {
+				application->state = BT_CLOSED;
+				application->msgnum = 0;
+				wake_up_interruptible(&session->wait);
+			}
+
+			break;
+
+		case CAPI_FUNCTION_GET_PROFILE:
+			controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
+			msgnum = CAPIMSG_MSGID(skb->data);
+
+			if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
+				session->ncontroller = controller;
+				wake_up_interruptible(&session->wait);
+				break;
+			}
+
+			if (!info && ctrl) {
+				memcpy(&ctrl->profile,
+					skb->data + CAPI_MSG_BASELEN + 11,
+					sizeof(capi_profile));
+				session->state = BT_CONNECTED;
+				ctrl->ready(ctrl);
+			}
+
+			break;
+
+		case CAPI_FUNCTION_GET_MANUFACTURER:
+			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
+
+			if (!info && ctrl) {
+				strncpy(ctrl->manu,
+					skb->data + CAPI_MSG_BASELEN + 15,
+					skb->data[CAPI_MSG_BASELEN + 14]);
+			}
+
+			break;
+
+		case CAPI_FUNCTION_GET_VERSION:
+			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
+
+			if (!info && ctrl) {
+				ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
+				ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
+				ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
+				ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
+			}
+
+			break;
+
+		case CAPI_FUNCTION_GET_SERIAL_NUMBER:
+			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
+
+			if (!info && ctrl) {
+				memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
+				strncpy(ctrl->serial,
+					skb->data + CAPI_MSG_BASELEN + 17,
+					skb->data[CAPI_MSG_BASELEN + 16]);
+			}
+
+			break;
+		}
+
+		break;
+
+	case CAPI_IND:
+		func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
+
+		if (func == CAPI_FUNCTION_LOOPBACK) {
+			appl = CAPIMSG_APPID(skb->data);
+			msgnum = CAPIMSG_MSGID(skb->data);
+			cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
+						skb->data + CAPI_MSG_BASELEN + 6,
+						skb->data[CAPI_MSG_BASELEN + 5]);
+		}
+
+		break;
+	}
+
+	kfree_skb(skb);
+}
+
+void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
+{
+	struct capi_ctr *ctrl = session->ctrl;
+	struct cmtp_application *application;
+	__u16 cmd, appl, info;
+	__u32 ncci, contr;
+
+	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+	if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
+		cmtp_recv_interopmsg(session, skb);
+		return;
+	}
+
+	if (session->flags & (1 << CMTP_LOOPBACK)) {
+		kfree_skb(skb);
+		return;
+	}
+
+	cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
+	appl = CAPIMSG_APPID(skb->data);
+	contr = CAPIMSG_CONTROL(skb->data);
+
+	application = cmtp_application_get(session, CMTP_MAPPING, appl);
+	if (application) {
+		appl = application->appl;
+		CAPIMSG_SETAPPID(skb->data, appl);
+	} else {
+		BT_ERR("Can't find application with id %d", appl);
+		kfree_skb(skb);
+		return;
+	}
+
+	if ((contr & 0x7f) == 0x01) {
+		contr = (contr & 0xffffff80) | session->num;
+		CAPIMSG_SETCONTROL(skb->data, contr);
+	}
+
+	if (!ctrl) {
+		BT_ERR("Can't find controller %d for message", session->num);
+		kfree_skb(skb);
+		return;
+	}
+
+	switch (cmd) {
+	case CAPI_CONNECT_B3_CONF:
+		ncci = CAPIMSG_NCCI(skb->data);
+		info = CAPIMSG_U16(skb->data, 12);
+
+		BT_DBG("CONNECT_B3_CONF ncci 0x%02x info 0x%02x", ncci, info);
+
+		if (info == 0)
+			ctrl->new_ncci(ctrl, appl, ncci, 8);
+
+		ctrl->handle_capimsg(ctrl, appl, skb);
+		break;
+
+	case CAPI_CONNECT_B3_IND:
+		ncci = CAPIMSG_NCCI(skb->data);
+
+		BT_DBG("CONNECT_B3_IND ncci 0x%02x", ncci);
+
+		ctrl->new_ncci(ctrl, appl, ncci, 8);
+		ctrl->handle_capimsg(ctrl, appl, skb);
+		break;
+
+	case CAPI_DISCONNECT_B3_IND:
+		ncci = CAPIMSG_NCCI(skb->data);
+
+		BT_DBG("DISCONNECT_B3_IND ncci 0x%02x", ncci);
+
+		if (ncci == 0xffffffff)
+			BT_ERR("DISCONNECT_B3_IND with ncci 0xffffffff");
+
+		ctrl->handle_capimsg(ctrl, appl, skb);
+		ctrl->free_ncci(ctrl, appl, ncci);
+		break;
+
+	default:
+		ctrl->handle_capimsg(ctrl, appl, skb);
+		break;
+	}
+}
+
+void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
+{
+	struct cmtp_scb *scb = (void *) skb->cb;
+
+	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+	scb->id = -1;
+	scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
+
+	skb_queue_tail(&session->transmit, skb);
+
+	cmtp_schedule(session);
+}
+
+
+static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+	BT_DBG("ctrl %p data %p", ctrl, data);
+
+	return -EIO;
+}
+
+static void cmtp_reset_ctr(struct capi_ctr *ctrl)
+{
+	BT_DBG("ctrl %p", ctrl);
+
+	ctrl->reseted(ctrl);
+}
+
+static void cmtp_remove_ctr(struct capi_ctr *ctrl)
+{
+	struct cmtp_session *session = ctrl->driverdata;
+
+	BT_DBG("ctrl %p", ctrl);
+
+	ctrl->suspend_output(ctrl);
+
+	atomic_inc(&session->terminate);
+	cmtp_schedule(session);
+}
+
+static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct cmtp_session *session = ctrl->driverdata;
+	struct cmtp_application *application;
+	unsigned long timeo = CMTP_INTEROP_TIMEOUT;
+	unsigned char buf[8];
+	int err = 0, nconn, want = rp->level3cnt;
+
+	BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
+		ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
+
+	application = cmtp_application_add(session, appl);
+	if (!application) {
+		BT_ERR("Can't allocate memory for new application");
+		ctrl->appl_released(ctrl, appl);
+		return;
+	}
+
+	if (want < 0)
+		nconn = ctrl->profile.nbchannel * -want;
+	else
+		nconn = want;
+
+	if (nconn == 0)
+		nconn = ctrl->profile.nbchannel;
+
+	capimsg_setu16(buf, 0, nconn);
+	capimsg_setu16(buf, 2, rp->datablkcnt);
+	capimsg_setu16(buf, 4, rp->datablklen);
+
+	application->state = BT_CONFIG;
+	application->msgnum = cmtp_msgnum_get(session);
+
+	cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
+				CAPI_FUNCTION_REGISTER, buf, 6);
+
+	add_wait_queue(&session->wait, &wait);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (!timeo) {
+			err = -EAGAIN;
+			break;
+		}
+
+		if (application->state == BT_CLOSED) {
+			err = -application->err;
+			break;
+		}
+
+		if (application->state == BT_CONNECTED)
+			break;
+
+		if (signal_pending(current)) {
+			err = -EINTR;
+			break;
+		}
+
+		timeo = schedule_timeout(timeo);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&session->wait, &wait);
+
+	if (err) {
+		ctrl->appl_released(ctrl, appl);
+		cmtp_application_del(session, application);
+		return;
+	}
+
+	ctrl->appl_registered(ctrl, appl);
+}
+
+static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct cmtp_session *session = ctrl->driverdata;
+	struct cmtp_application *application;
+	unsigned long timeo = CMTP_INTEROP_TIMEOUT;
+
+	BT_DBG("ctrl %p appl %d", ctrl, appl);
+
+	application = cmtp_application_get(session, CMTP_APPLID, appl);
+	if (!application) {
+		BT_ERR("Can't find application");
+		return;
+	}
+
+	application->msgnum = cmtp_msgnum_get(session);
+
+	cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
+				CAPI_FUNCTION_RELEASE, NULL, 0);
+
+	add_wait_queue(&session->wait, &wait);
+	while (timeo) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (application->state == BT_CLOSED)
+			break;
+
+		if (signal_pending(current))
+			break;
+
+		timeo = schedule_timeout(timeo);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&session->wait, &wait);
+
+	cmtp_application_del(session, application);
+	ctrl->appl_released(ctrl, appl);
+}
+
+static void cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+	struct cmtp_session *session = ctrl->driverdata;
+	struct cmtp_application *application;
+	__u16 appl;
+	__u32 contr;
+
+	BT_DBG("ctrl %p skb %p", ctrl, skb);
+
+	appl = CAPIMSG_APPID(skb->data);
+	contr = CAPIMSG_CONTROL(skb->data);
+
+	application = cmtp_application_get(session, CMTP_APPLID, appl);
+	if ((!application) || (application->state != BT_CONNECTED)) {
+		BT_ERR("Can't find application with id %d", appl);
+		kfree_skb(skb);
+		return;
+	}
+
+	CAPIMSG_SETAPPID(skb->data, application->mapping);
+
+	if ((contr & 0x7f) == session->num) {
+		contr = (contr & 0xffffff80) | 0x01;
+		CAPIMSG_SETCONTROL(skb->data, contr);
+	}
+
+	cmtp_send_capimsg(session, skb);
+}
+
+static char *cmtp_procinfo(struct capi_ctr *ctrl)
+{
+	return "CAPI Message Transport Protocol";
+}
+
+static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl)
+{
+	struct cmtp_session *session = ctrl->driverdata;
+	struct cmtp_application *app;
+	struct list_head *p, *n;
+	int len = 0;
+
+	len += sprintf(page + len, "%s (Revision %s)\n\n", cmtp_procinfo(ctrl), REVISION);
+	len += sprintf(page + len, "addr %s\n", session->name);
+	len += sprintf(page + len, "ctrl %d\n", session->num);
+
+	list_for_each_safe(p, n, &session->applications) {
+		app = list_entry(p, struct cmtp_application, list);
+		len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping);
+	}
+
+	if (off + count >= len)
+		*eof = 1;
+
+	if (len < off)
+		return 0;
+
+	*start = page + off;
+
+	return ((count < len - off) ? count : len - off);
+}
+
+static struct capi_driver cmtp_driver = {
+	name:		"cmtp",
+	revision:	REVISION,
+	load_firmware:	cmtp_load_firmware,
+	reset_ctr:	cmtp_reset_ctr,
+	remove_ctr:	cmtp_remove_ctr,
+	register_appl:	cmtp_register_appl,
+	release_appl:	cmtp_release_appl,
+	send_message:	cmtp_send_message,
+	procinfo:	cmtp_procinfo,
+	ctr_read_proc:	cmtp_ctr_read_proc,
+
+	driver_read_proc:	0,
+	add_card:		0,
+};
+
+
+int cmtp_attach_device(struct cmtp_session *session)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long timeo = CMTP_INTEROP_TIMEOUT;
+	unsigned char buf[4];
+
+	BT_DBG("session %p", session);
+
+	capimsg_setu32(buf, 0, 0);
+
+	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
+				CAPI_FUNCTION_GET_PROFILE, buf, 4);
+
+	add_wait_queue(&session->wait, &wait);
+	while (timeo) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (session->ncontroller)
+			break;
+
+		if (signal_pending(current))
+			break;
+
+		timeo = schedule_timeout(timeo);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&session->wait, &wait);
+
+	BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
+
+	if (!timeo)
+		return -ETIMEDOUT;
+
+	if (!session->ncontroller)
+		return -ENODEV;
+
+
+	if (session->ncontroller > 1)
+		BT_INFO("Setting up only CAPI controller 1");
+
+	if (!(session->ctrl = di->attach_ctr(&cmtp_driver, session->name, session))) {
+		BT_ERR("Can't attach new controller");
+		return -EBUSY;
+	}
+
+	session->num = session->ctrl->cnr;
+
+	BT_DBG("session %p ctrl %p num %d", session, session->ctrl, session->num);
+
+	capimsg_setu32(buf, 0, 1);
+
+	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+				CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
+
+	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+				CAPI_FUNCTION_GET_VERSION, buf, 4);
+
+	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+				CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
+
+	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+				CAPI_FUNCTION_GET_PROFILE, buf, 4);
+
+	return 0;
+}
+
+void cmtp_detach_device(struct cmtp_session *session)
+{
+	struct capi_ctr *ctrl = session->ctrl;
+
+	BT_DBG("session %p ctrl %p", session, ctrl);
+
+	if (!ctrl)
+		return;
+
+	ctrl->reseted(ctrl);
+
+	di->detach_ctr(ctrl);
+}
+
+int cmtp_init_capi(void)
+{
+	if (!(di = attach_capi_driver(&cmtp_driver))) {
+		BT_ERR("Can't attach CAPI driver");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+void cmtp_cleanup_capi(void)
+{
+	detach_capi_driver(&cmtp_driver);
+}
diff -urN linux-2.4.18/net/bluetooth/cmtp/cmtp.h linux-2.4.18-mh15/net/bluetooth/cmtp/cmtp.h
--- linux-2.4.18/net/bluetooth/cmtp/cmtp.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/cmtp/cmtp.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,138 @@
+/* 
+   CMTP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#ifndef __CMTP_H
+#define __CMTP_H
+
+#include <linux/types.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define BTNAMSIZ 18
+
+/* CMTP ioctl defines */
+#define CMTPCONNADD	_IOW('C', 200, int)
+#define CMTPCONNDEL	_IOW('C', 201, int)
+#define CMTPGETCONNLIST	_IOR('C', 210, int)
+#define CMTPGETCONNINFO	_IOR('C', 211, int)
+
+#define CMTP_LOOPBACK	0
+
+struct cmtp_connadd_req {
+	int   sock;	// Connected socket
+	__u32 flags;
+};
+
+struct cmtp_conndel_req {
+	bdaddr_t bdaddr;
+	__u32    flags;
+};
+
+struct cmtp_conninfo {
+	bdaddr_t bdaddr;
+	__u32    flags;
+	__u16    state;
+	int      num;
+};
+
+struct cmtp_connlist_req {
+	__u32  cnum;
+	struct cmtp_conninfo *ci;
+};
+
+int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock);
+int cmtp_del_connection(struct cmtp_conndel_req *req);
+int cmtp_get_connlist(struct cmtp_connlist_req *req);
+int cmtp_get_conninfo(struct cmtp_conninfo *ci);
+
+/* CMTP session defines */
+#define CMTP_INTEROP_TIMEOUT	(HZ * 5)
+#define CMTP_INITIAL_MSGNUM	0xff00
+
+struct cmtp_session {
+	struct list_head list;
+
+	struct socket *sock;
+
+	bdaddr_t bdaddr;
+
+	unsigned long state;
+	unsigned long flags;
+
+	uint mtu;
+
+	char name[BTNAMSIZ];
+
+	atomic_t terminate;
+
+	wait_queue_head_t wait;
+
+	int ncontroller;
+	int num;
+	struct capi_ctr *ctrl;
+
+	struct list_head applications;
+
+	unsigned long blockids;
+	int msgnum;
+
+	struct sk_buff_head transmit;
+
+	struct sk_buff *reassembly[16];
+};
+
+struct cmtp_application {
+	struct list_head list;
+
+	unsigned long state;
+	int err;
+
+	__u16 appl;
+	__u16 mapping;
+
+	__u16 msgnum;
+};
+
+struct cmtp_scb {
+	int id;
+	int data;
+};
+
+int  cmtp_attach_device(struct cmtp_session *session);
+void cmtp_detach_device(struct cmtp_session *session);
+
+void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
+void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb);
+
+static inline void cmtp_schedule(struct cmtp_session *session)
+{
+	struct sock *sk = session->sock->sk;
+
+	wake_up_interruptible(sk->sleep);
+}
+
+/* CMTP init defines */
+int cmtp_init_capi(void);
+int cmtp_init_sockets(void);
+void cmtp_cleanup_capi(void);
+void cmtp_cleanup_sockets(void);
+
+#endif /* __CMTP_H */
diff -urN linux-2.4.18/net/bluetooth/cmtp/Config.in linux-2.4.18-mh15/net/bluetooth/cmtp/Config.in
--- linux-2.4.18/net/bluetooth/cmtp/Config.in	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/cmtp/Config.in	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,7 @@
+#
+# Bluetooth CMTP layer configuration
+#
+
+if [ "$CONFIG_ISDN" = "y" -o "$CONFIG_ISDN" = "m" ]; then 
+   dep_tristate 'CMTP protocol support' CONFIG_BLUEZ_CMTP $CONFIG_ISDN_CAPI $CONFIG_BLUEZ_L2CAP
+fi
diff -urN linux-2.4.18/net/bluetooth/cmtp/core.c linux-2.4.18-mh15/net/bluetooth/cmtp/core.c
--- linux-2.4.18/net/bluetooth/cmtp/core.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/cmtp/core.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,515 @@
+/* 
+   CMTP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <net/sock.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "cmtp.h"
+
+#ifndef CONFIG_BLUEZ_CMTP_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.0"
+
+static DECLARE_RWSEM(cmtp_session_sem);
+static LIST_HEAD(cmtp_session_list);
+
+static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
+{
+	struct cmtp_session *session;
+	struct list_head *p;
+
+	BT_DBG("");
+
+	list_for_each(p, &cmtp_session_list) {
+		session = list_entry(p, struct cmtp_session, list);
+		if (!bacmp(bdaddr, &session->bdaddr))
+			return session;
+	}
+	return NULL;
+}
+
+static void __cmtp_link_session(struct cmtp_session *session)
+{
+	MOD_INC_USE_COUNT;
+	list_add(&session->list, &cmtp_session_list);
+}
+
+static void __cmtp_unlink_session(struct cmtp_session *session)
+{
+	list_del(&session->list);
+	MOD_DEC_USE_COUNT;
+}
+
+static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
+{
+	bacpy(&ci->bdaddr, &session->bdaddr);
+
+	ci->flags = session->flags;
+	ci->state = session->state;
+
+	ci->num = session->num;
+}
+
+
+static inline int cmtp_alloc_block_id(struct cmtp_session *session)
+{
+	int i, id = -1;
+
+	for (i = 0; i < 16; i++)
+		if (!test_and_set_bit(i, &session->blockids)) {
+			id = i;
+			break;
+		}
+
+	return id;
+}
+
+static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
+{
+	clear_bit(id, &session->blockids);
+}
+
+static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
+{
+	struct sk_buff *skb = session->reassembly[id], *nskb;
+	int size;
+
+	BT_DBG("session %p buf %p count %d", session, buf, count);
+
+	size = (skb) ? skb->len + count : count;
+
+	if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
+		BT_ERR("Can't allocate memory for CAPI message");
+		return;
+	}
+
+	if (skb && (skb->len > 0))
+		memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
+
+	memcpy(skb_put(nskb, count), buf, count);
+
+	session->reassembly[id] = nskb;
+
+	if (skb)
+		kfree_skb(skb);
+}
+
+static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
+{
+	__u8 hdr, hdrlen, id;
+	__u16 len;
+
+	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+	while (skb->len > 0) {
+		hdr = skb->data[0];
+
+		switch (hdr & 0xc0) {
+		case 0x40:
+			hdrlen = 2;
+			len = skb->data[1];
+			break;
+		case 0x80:
+			hdrlen = 3;
+			len = skb->data[1] | (skb->data[2] << 8);
+			break;
+		default:
+			hdrlen = 1;
+			len = 0;
+			break;
+		}
+
+		id = (hdr & 0x3c) >> 2;
+
+		BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
+
+		if (hdrlen + len > skb->len) {
+			BT_ERR("Wrong size or header information in CMTP frame");
+			break;
+		}
+
+		if (len == 0) {
+			skb_pull(skb, hdrlen);
+			continue;
+		}
+
+		switch (hdr & 0x03) {
+		case 0x00:
+			cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
+			cmtp_recv_capimsg(session, session->reassembly[id]);
+			session->reassembly[id] = NULL;
+			break;
+		case 0x01:
+			cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
+			break;
+		default:
+			if (session->reassembly[id] != NULL)
+				kfree_skb(session->reassembly[id]);
+			session->reassembly[id] = NULL;
+			break;
+		}
+
+		skb_pull(skb, hdrlen + len);
+	}
+
+	kfree_skb(skb);
+	return 0;
+}
+
+static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
+{
+	struct socket *sock = session->sock;
+	struct iovec iv = { data, len };
+	struct msghdr msg;
+	int err;
+
+	BT_DBG("session %p data %p len %d", session, data, len);
+
+	if (!len)
+		return 0;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iovlen = 1;
+	msg.msg_iov = &iv;
+
+	err = sock->ops->sendmsg(sock, &msg, len, 0);
+	return err;
+}
+
+static int cmtp_process_transmit(struct cmtp_session *session)
+{
+	struct sk_buff *skb, *nskb;
+	unsigned char *hdr;
+	unsigned int size, tail;
+
+	BT_DBG("session %p", session);
+
+	if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) {
+		BT_ERR("Can't allocate memory for new frame");
+		return -ENOMEM;
+	}
+
+	while ((skb = skb_dequeue(&session->transmit))) {
+		struct cmtp_scb *scb = (void *) skb->cb;
+
+		if ((tail = (session->mtu - nskb->len)) < 5) {
+			cmtp_send_frame(session, nskb->data, nskb->len);
+			skb_trim(nskb, 0);
+			tail = session->mtu;
+		}
+
+		size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
+
+		if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
+			skb_queue_head(&session->transmit, skb);
+			break;
+		}
+
+		if (size < 256) {
+			hdr = skb_put(nskb, 2);
+			hdr[0] = 0x40
+				| ((scb->id << 2) & 0x3c)
+				| ((skb->len == size) ? 0x00 : 0x01);
+			hdr[1] = size;
+		} else {
+			hdr = skb_put(nskb, 3);
+			hdr[0] = 0x80
+				| ((scb->id << 2) & 0x3c)
+				| ((skb->len == size) ? 0x00 : 0x01);
+			hdr[1] = size & 0xff;
+			hdr[2] = size >> 8;
+		}
+
+		memcpy(skb_put(nskb, size), skb->data, size);
+		skb_pull(skb, size);
+
+		if (skb->len > 0) {
+			skb_queue_head(&session->transmit, skb);
+		} else {
+			cmtp_free_block_id(session, scb->id);
+			if (scb->data) {
+				cmtp_send_frame(session, nskb->data, nskb->len);
+				skb_trim(nskb, 0);
+			}
+			kfree_skb(skb);
+		}
+	}
+
+	cmtp_send_frame(session, nskb->data, nskb->len);
+
+	kfree_skb(nskb);
+
+	return skb_queue_len(&session->transmit);
+}
+
+static int cmtp_session(void *arg)
+{
+	struct cmtp_session *session = arg;
+	struct sock *sk = session->sock->sk;
+	struct sk_buff *skb;
+	wait_queue_t wait;
+
+	BT_DBG("session %p", session);
+
+	daemonize(); reparent_to_init();
+
+	sprintf(current->comm, "kcmtpd_ctr_%d", session->num);
+
+	sigfillset(&current->blocked);
+	flush_signals(current);
+
+	current->nice = -15;
+
+	set_fs(KERNEL_DS);
+
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(sk->sleep, &wait);
+	while (!atomic_read(&session->terminate)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (sk->state != BT_CONNECTED)
+			break;
+
+		while ((skb = skb_dequeue(&sk->receive_queue))) {
+			skb_orphan(skb);
+			cmtp_recv_frame(session, skb);
+		}
+
+		cmtp_process_transmit(session);
+
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+
+	down_write(&cmtp_session_sem);
+
+	if (!(session->flags & (1 << CMTP_LOOPBACK)))
+		cmtp_detach_device(session);
+
+	fput(session->sock->file);
+
+	__cmtp_unlink_session(session);
+
+	up_write(&cmtp_session_sem);
+
+	kfree(session);
+	return 0;
+}
+
+int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
+{
+	struct cmtp_session *session, *s;
+	bdaddr_t src, dst;
+	int i, err;
+
+	BT_DBG("");
+
+	baswap(&src, &bluez_pi(sock->sk)->src);
+	baswap(&dst, &bluez_pi(sock->sk)->dst);
+
+	session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL);
+	if (!session) 
+		return -ENOMEM;
+	memset(session, 0, sizeof(struct cmtp_session));
+
+	down_write(&cmtp_session_sem);
+
+	s = __cmtp_get_session(&bluez_pi(sock->sk)->dst);
+	if (s && s->state == BT_CONNECTED) {
+		err = -EEXIST;
+		goto failed;
+	}
+
+	bacpy(&session->bdaddr, &bluez_pi(sock->sk)->dst);
+
+	session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
+
+	BT_DBG("mtu %d", session->mtu);
+
+	sprintf(session->name, "%s", batostr(&dst));
+
+	session->sock  = sock;
+	session->state = BT_CONFIG;
+
+	init_waitqueue_head(&session->wait);
+
+	session->ctrl   = NULL;
+	session->msgnum = CMTP_INITIAL_MSGNUM;
+
+	INIT_LIST_HEAD(&session->applications);
+
+	skb_queue_head_init(&session->transmit);
+
+	for (i = 0; i < 16; i++)
+		session->reassembly[i] = NULL;
+
+	session->flags = req->flags;
+
+	__cmtp_link_session(session);
+
+	err = kernel_thread(cmtp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	if (err < 0)
+		goto unlink;
+
+	if (!(session->flags & (1 << CMTP_LOOPBACK))) {
+		err = cmtp_attach_device(session);
+		if (err < 0)
+			goto detach;
+	}
+
+	up_write(&cmtp_session_sem);
+	return 0;
+
+detach:
+	cmtp_detach_device(session);
+
+unlink:
+	__cmtp_unlink_session(session);
+
+failed:
+	up_write(&cmtp_session_sem);
+	kfree(session);
+	return err;
+}
+
+int cmtp_del_connection(struct cmtp_conndel_req *req)
+{
+	struct cmtp_session *session;
+	int err = 0;
+
+	BT_DBG("");
+
+	down_read(&cmtp_session_sem);
+
+	session = __cmtp_get_session(&req->bdaddr);
+	if (session) {
+		/* Flush the transmit queue */
+		skb_queue_purge(&session->transmit);
+
+		/* Kill session thread */
+		atomic_inc(&session->terminate);
+		cmtp_schedule(session);
+	} else
+		err = -ENOENT;
+
+	up_read(&cmtp_session_sem);
+	return err;
+}
+
+int cmtp_get_connlist(struct cmtp_connlist_req *req)
+{
+	struct list_head *p;
+	int err = 0, n = 0;
+
+	BT_DBG("");
+
+	down_read(&cmtp_session_sem);
+
+	list_for_each(p, &cmtp_session_list) {
+		struct cmtp_session *session;
+		struct cmtp_conninfo ci;
+
+		session = list_entry(p, struct cmtp_session, list);
+
+		__cmtp_copy_session(session, &ci);
+
+		if (copy_to_user(req->ci, &ci, sizeof(ci))) {
+			err = -EFAULT;
+			break;
+		}
+
+		if (++n >= req->cnum)
+			break;
+
+		req->ci++;
+	}
+	req->cnum = n;
+
+	up_read(&cmtp_session_sem);
+	return err;
+}
+
+int cmtp_get_conninfo(struct cmtp_conninfo *ci)
+{
+	struct cmtp_session *session;
+	int err = 0;
+
+	down_read(&cmtp_session_sem);
+
+	session = __cmtp_get_session(&ci->bdaddr);
+	if (session)
+		__cmtp_copy_session(session, ci);
+	else
+		err = -ENOENT;
+
+	up_read(&cmtp_session_sem);
+	return err;
+}
+
+
+int __init init_cmtp(void)
+{
+	l2cap_load();
+
+	cmtp_init_capi();
+	cmtp_init_sockets();
+
+	BT_INFO("BlueZ CMTP ver %s", VERSION);
+	BT_INFO("Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>");
+
+	return 0;
+}
+
+void __exit exit_cmtp(void)
+{
+	cmtp_cleanup_sockets();
+	cmtp_cleanup_capi();
+}
+
+module_init(init_cmtp);
+module_exit(exit_cmtp);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueZ CMTP ver " VERSION);
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/net/bluetooth/cmtp/Makefile linux-2.4.18-mh15/net/bluetooth/cmtp/Makefile
--- linux-2.4.18/net/bluetooth/cmtp/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/cmtp/Makefile	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux Bluetooth CMTP layer
+#
+
+O_TARGET := cmtp.o
+
+obj-y	:= core.o sock.o capi.o
+obj-m	+= $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.4.18/net/bluetooth/cmtp/sock.c linux-2.4.18-mh15/net/bluetooth/cmtp/sock.c
--- linux-2.4.18/net/bluetooth/cmtp/sock.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/cmtp/sock.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,208 @@
+/* 
+   CMTP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "cmtp.h"
+
+#ifndef CONFIG_BLUEZ_CMTP_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+static int cmtp_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p sk %p", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	sock_orphan(sk);
+	sock_put(sk);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct cmtp_connadd_req ca;
+	struct cmtp_conndel_req cd;
+	struct cmtp_connlist_req cl;
+	struct cmtp_conninfo ci;
+	struct socket *nsock;
+	int err;
+
+	BT_DBG("cmd %x arg %lx", cmd, arg);
+
+	switch (cmd) {
+	case CMTPCONNADD:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+
+		if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
+			return -EFAULT;
+
+		nsock = sockfd_lookup(ca.sock, &err);
+		if (!nsock)
+			return err;
+
+		if (nsock->sk->state != BT_CONNECTED) {
+			fput(nsock->file);
+			return -EBADFD;
+		}
+
+		err = cmtp_add_connection(&ca, nsock);
+		if (!err) {
+			if (copy_to_user((void *) arg, &ca, sizeof(ca)))
+				err = -EFAULT;
+		} else
+			fput(nsock->file);
+
+		return err;
+
+	case CMTPCONNDEL:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+
+		if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
+			return -EFAULT;
+
+		return cmtp_del_connection(&cd);
+
+	case CMTPGETCONNLIST:
+		if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
+			return -EFAULT;
+
+		if (cl.cnum <= 0)
+			return -EINVAL;
+
+		err = cmtp_get_connlist(&cl);
+		if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
+			return -EFAULT;
+
+		return err;
+
+	case CMTPGETCONNINFO:
+		if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
+			return -EFAULT;
+
+		err = cmtp_get_conninfo(&ci);
+		if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
+			return -EFAULT;
+
+		return err;
+	}
+
+	return -EINVAL;
+}
+
+static struct proto_ops cmtp_sock_ops = {
+	family:		PF_BLUETOOTH,
+	release:	cmtp_sock_release,
+	ioctl:		cmtp_sock_ioctl,
+	bind:		sock_no_bind,
+	getname:	sock_no_getname,
+	sendmsg:	sock_no_sendmsg,
+	recvmsg:	sock_no_recvmsg,
+	poll:		sock_no_poll,
+	listen:		sock_no_listen,
+	shutdown:	sock_no_shutdown,
+	setsockopt:	sock_no_setsockopt,
+	getsockopt:	sock_no_getsockopt,
+	connect:	sock_no_connect,
+	socketpair:	sock_no_socketpair,
+	accept:		sock_no_accept,
+	mmap:		sock_no_mmap
+};
+
+static int cmtp_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	if (sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	sock->ops = &cmtp_sock_ops;
+
+	if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
+		return -ENOMEM;
+
+	MOD_INC_USE_COUNT;
+
+	sock->state = SS_UNCONNECTED;
+	sock_init_data(sock, sk);
+
+	sk->destruct = NULL;
+	sk->protocol = protocol;
+
+	return 0;
+}
+
+static struct net_proto_family cmtp_sock_family_ops = {
+	family:		PF_BLUETOOTH,
+	create:		cmtp_sock_create
+};
+
+int cmtp_init_sockets(void)
+{
+	int err;
+
+	if ((err = bluez_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops))) {
+		BT_ERR("Can't register CMTP socket layer (%d)", err);
+		return err;
+	}
+
+	return 0;
+}
+
+void cmtp_cleanup_sockets(void)
+{
+	int err;
+
+	if ((err = bluez_sock_unregister(BTPROTO_CMTP)))
+		BT_ERR("Can't unregister CMTP socket layer (%d)", err);
+
+	return;
+}
diff -urN linux-2.4.18/net/bluetooth/Config.in linux-2.4.18-mh15/net/bluetooth/Config.in
--- linux-2.4.18/net/bluetooth/Config.in	2001-06-12 04:15:27.000000000 +0200
+++ linux-2.4.18-mh15/net/bluetooth/Config.in	2004-08-01 16:26:23.000000000 +0200
@@ -1,16 +1,23 @@
 #
-# Bluetooth configuration
+# Bluetooth subsystem configuration
 #
 
 if [ "$CONFIG_NET" != "n" ]; then
+
    mainmenu_option next_comment
    comment 'Bluetooth support'
    dep_tristate 'Bluetooth subsystem support' CONFIG_BLUEZ $CONFIG_NET
 
    if [ "$CONFIG_BLUEZ" != "n" ]; then
       dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ
+      dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ
+      source net/bluetooth/rfcomm/Config.in
+      source net/bluetooth/bnep/Config.in
+      source net/bluetooth/cmtp/Config.in
+      source net/bluetooth/hidp/Config.in
       source drivers/bluetooth/Config.in
    fi
+
    endmenu
 fi
 
diff -urN linux-2.4.18/net/bluetooth/hci_conn.c linux-2.4.18-mh15/net/bluetooth/hci_conn.c
--- linux-2.4.18/net/bluetooth/hci_conn.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/hci_conn.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,435 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * HCI Connection handling.
+ *
+ * $Id: hci_conn.c,v 1.5 2002/07/17 18:46:25 maxk Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef HCI_CORE_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+void hci_acl_connect(struct hci_conn *conn)
+{
+	struct hci_dev *hdev = conn->hdev;
+	struct inquiry_entry *ie;
+	create_conn_cp cp;
+
+	BT_DBG("%p", conn);
+
+	conn->state = BT_CONNECT;
+	conn->out   = 1;
+	conn->link_mode = HCI_LM_MASTER;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, &conn->dst);
+	cp.pscan_rep_mode = 0x02;
+
+	if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) &&
+			inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
+		cp.pscan_rep_mode = ie->info.pscan_rep_mode;
+		cp.pscan_mode     = ie->info.pscan_mode;
+		cp.clock_offset   = ie->info.clock_offset | __cpu_to_le16(0x8000);
+	}
+
+	cp.pkt_type = __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK);
+	if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
+		cp.role_switch	= 0x01;
+	else
+		cp.role_switch	= 0x00;
+		
+	hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN,
+				CREATE_CONN_CP_SIZE, &cp);
+}
+
+void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
+{
+	disconnect_cp cp;
+
+	BT_DBG("%p", conn);
+
+	conn->state = BT_DISCONN;
+
+	cp.handle = __cpu_to_le16(conn->handle);
+	cp.reason = reason;
+	hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT,
+				DISCONNECT_CP_SIZE, &cp);
+}
+
+void hci_add_sco(struct hci_conn *conn, __u16 handle)
+{
+	struct hci_dev *hdev = conn->hdev;
+	add_sco_cp cp;
+
+	BT_DBG("%p", conn);
+
+	conn->state = BT_CONNECT;
+	conn->out = 1;
+
+	cp.pkt_type = __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
+	cp.handle   = __cpu_to_le16(handle);
+
+	hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ADD_SCO, ADD_SCO_CP_SIZE, &cp);
+}
+
+static void hci_conn_timeout(unsigned long arg)
+{
+	struct hci_conn *conn = (void *)arg;
+	struct hci_dev  *hdev = conn->hdev;
+
+	BT_DBG("conn %p state %d", conn, conn->state);
+
+	if (atomic_read(&conn->refcnt))
+		return;
+
+	hci_dev_lock(hdev);
+ 	if (conn->state == BT_CONNECTED)
+		hci_acl_disconn(conn, 0x13);
+	else
+		conn->state = BT_CLOSED;
+	hci_dev_unlock(hdev);
+	return;
+}
+
+static void hci_conn_init_timer(struct hci_conn *conn)
+{
+	init_timer(&conn->timer);
+	conn->timer.function = hci_conn_timeout;
+	conn->timer.data = (unsigned long)conn;
+}
+
+struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
+{
+	struct hci_conn *conn;
+
+	BT_DBG("%s dst %s", hdev->name, batostr(dst));
+
+	if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC)))
+		return NULL;
+	memset(conn, 0, sizeof(struct hci_conn));
+
+	bacpy(&conn->dst, dst);
+	conn->type   = type;
+	conn->hdev   = hdev;
+	conn->state  = BT_OPEN;
+
+	skb_queue_head_init(&conn->data_q);
+	hci_conn_init_timer(conn);
+
+	atomic_set(&conn->refcnt, 0);
+
+	hci_dev_hold(hdev);
+
+	tasklet_disable(&hdev->tx_task);
+	conn_hash_add(hdev, conn);
+	tasklet_enable(&hdev->tx_task);
+
+	return conn;
+}
+
+int hci_conn_del(struct hci_conn *conn)
+{
+	struct hci_dev  *hdev = conn->hdev;
+
+	BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
+	
+	hci_conn_del_timer(conn);
+
+	if (conn->type == SCO_LINK) {
+		struct hci_conn *acl = conn->link;
+		if (acl) {
+			acl->link = NULL;
+			hci_conn_put(acl);
+		}
+	} else {
+		struct hci_conn *sco = conn->link;
+		if (sco)
+			sco->link = NULL;
+
+		/* Unacked frames */
+		hdev->acl_cnt += conn->sent;
+	}
+
+	tasklet_disable(&hdev->tx_task);
+	conn_hash_del(hdev, conn);
+	tasklet_enable(&hdev->tx_task);
+
+	skb_queue_purge(&conn->data_q);
+
+	hci_dev_put(hdev);
+
+	kfree(conn);
+	return 0;
+}
+
+struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
+{
+	int use_src = bacmp(src, BDADDR_ANY);
+	struct hci_dev *hdev = NULL;
+	struct list_head *p;
+
+	BT_DBG("%s -> %s", batostr(src), batostr(dst));
+
+	read_lock_bh(&hdev_list_lock);
+
+	list_for_each(p, &hdev_list) {
+		struct hci_dev *d;
+		d = list_entry(p, struct hci_dev, list);
+		
+		if (!test_bit(HCI_UP, &d->flags))
+			continue;
+
+		/* Simple routing: 
+	 	 * 	No source address - find interface with bdaddr != dst 
+	 	 *	Source address 	  - find interface with bdaddr == src 
+	 	 */
+
+		if (use_src) {
+			if (!bacmp(&d->bdaddr, src)) {
+				hdev = d; break;
+			}
+		} else {
+			if (bacmp(&d->bdaddr, dst)) {
+				hdev = d; break;
+			}
+		}
+	}
+
+	if (hdev)
+		hci_dev_hold(hdev);
+
+	read_unlock_bh(&hdev_list_lock);
+	return hdev;
+}
+
+/* Create SCO or ACL connection.
+ * Device _must_ be locked */
+struct hci_conn * hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst)
+{
+	struct hci_conn *acl;
+
+	BT_DBG("%s dst %s", hdev->name, batostr(dst));
+
+	if (!(acl = conn_hash_lookup_ba(hdev, ACL_LINK, dst))) {
+		if (!(acl = hci_conn_add(hdev, ACL_LINK, dst)))
+			return NULL;
+	}
+
+	hci_conn_hold(acl);
+
+	if (acl->state == BT_OPEN || acl->state == BT_CLOSED)
+		hci_acl_connect(acl);
+
+	if (type == SCO_LINK) {
+		struct hci_conn *sco;
+
+		if (!(sco = conn_hash_lookup_ba(hdev, SCO_LINK, dst))) {
+			if (!(sco = hci_conn_add(hdev, SCO_LINK, dst))) {
+				hci_conn_put(acl);
+				return NULL;
+			}
+		}
+		acl->link = sco;
+		sco->link = acl;
+
+		hci_conn_hold(sco);
+
+		if (acl->state == BT_CONNECTED && 
+				(sco->state == BT_OPEN || sco->state == BT_CLOSED))
+			hci_add_sco(sco, acl->handle);
+
+		return sco;
+	} else {
+		return acl;
+	}
+}
+
+/* Authenticate remote device */
+int hci_conn_auth(struct hci_conn *conn)
+{
+	BT_DBG("conn %p", conn);
+	
+	if (conn->link_mode & HCI_LM_AUTH)
+		return 1;
+	
+	if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+		auth_requested_cp ar;
+		ar.handle = __cpu_to_le16(conn->handle);
+		hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
+				AUTH_REQUESTED_CP_SIZE, &ar);
+	}
+	return 0;
+}
+
+/* Enable encryption */
+int hci_conn_encrypt(struct hci_conn *conn)
+{
+	BT_DBG("conn %p", conn);
+	
+	if (conn->link_mode & HCI_LM_ENCRYPT)
+		return 1;
+	
+	if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
+		return 0;
+
+	if (hci_conn_auth(conn)) {
+		set_conn_encrypt_cp ce;
+		ce.handle  = __cpu_to_le16(conn->handle);
+		ce.encrypt = 1; 
+		hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT,
+				SET_CONN_ENCRYPT_CP_SIZE, &ce);
+	}
+	return 0;
+}
+
+/* Drop all connection on the device */
+void hci_conn_hash_flush(struct hci_dev *hdev)
+{
+	struct conn_hash *h = &hdev->conn_hash;
+        struct list_head *p;
+
+	BT_DBG("hdev %s", hdev->name);
+
+	p = h->list.next;
+	while (p != &h->list) {
+		struct hci_conn *c;
+
+		c = list_entry(p, struct hci_conn, list);
+		p = p->next;
+
+		c->state = BT_CLOSED;
+
+		hci_proto_disconn_ind(c, 0x16);
+		hci_conn_del(c);
+	}
+}
+
+int hci_get_conn_list(unsigned long arg)
+{
+	struct hci_conn_list_req req, *cl;
+	struct hci_conn_info *ci;
+	struct hci_dev *hdev;
+	struct list_head *p;
+	int n = 0, size, err;
+
+	if (copy_from_user(&req, (void *) arg, sizeof(req)))
+		return -EFAULT;
+
+	if (!req.conn_num || req.conn_num > (PAGE_SIZE * 2) / sizeof(*ci))
+		return -EINVAL;
+
+	size = sizeof(req) + req.conn_num * sizeof(*ci);
+
+	if (!(cl = (void *) kmalloc(size, GFP_KERNEL)))
+		return -ENOMEM;
+
+	if (!(hdev = hci_dev_get(req.dev_id))) {
+		kfree(cl);
+		return -ENODEV;
+	}
+
+	ci = cl->conn_info;
+
+	hci_dev_lock_bh(hdev);
+	list_for_each(p, &hdev->conn_hash.list) {
+		register struct hci_conn *c;
+		c = list_entry(p, struct hci_conn, list);
+
+		bacpy(&(ci + n)->bdaddr, &c->dst);
+		(ci + n)->handle = c->handle;
+		(ci + n)->type  = c->type;
+		(ci + n)->out   = c->out;
+		(ci + n)->state = c->state;
+		(ci + n)->link_mode = c->link_mode;
+		if (++n >= req.conn_num)
+			break;
+	}
+	hci_dev_unlock_bh(hdev);
+
+	cl->dev_id = hdev->id;
+	cl->conn_num = n;
+	size = sizeof(req) + n * sizeof(*ci);
+
+	hci_dev_put(hdev);
+
+	err = copy_to_user((void *) arg, cl, size);
+	kfree(cl);
+
+	return err ? -EFAULT : 0;
+}
+
+int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg)
+{
+	struct hci_conn_info_req req;
+	struct hci_conn_info ci;
+	struct hci_conn *conn;
+	char *ptr = (void *) arg + sizeof(req);
+
+	if (copy_from_user(&req, (void *) arg, sizeof(req)))
+		return -EFAULT;
+
+	hci_dev_lock_bh(hdev);
+	conn = conn_hash_lookup_ba(hdev, req.type, &req.bdaddr);
+	if (conn) {
+		bacpy(&ci.bdaddr, &conn->dst);
+		ci.handle = conn->handle;
+		ci.type  = conn->type;
+		ci.out   = conn->out;
+		ci.state = conn->state;
+		ci.link_mode = conn->link_mode;
+	}
+	hci_dev_unlock_bh(hdev);
+
+	if (!conn)
+		return -ENOENT;
+
+	return copy_to_user(ptr, &ci, sizeof(ci)) ? -EFAULT : 0;
+}
diff -urN linux-2.4.18/net/bluetooth/hci_core.c linux-2.4.18-mh15/net/bluetooth/hci_core.c
--- linux-2.4.18/net/bluetooth/hci_core.c	2001-11-09 23:21:21.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/hci_core.c	2004-08-01 16:26:23.000000000 +0200
@@ -25,11 +25,12 @@
 /*
  * BlueZ HCI Core.
  *
- * $Id: hci_core.c,v 1.22 2001/08/03 04:19:50 maxk Exp $
+ * $Id: hci_core.c,v 1.14 2002/08/26 16:57:57 maxk Exp $
  */
 
 #include <linux/config.h>
 #include <linux/module.h>
+#include <linux/kmod.h>
 
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -50,12 +51,11 @@
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
 #include <net/bluetooth/hci_core.h>
 
 #ifndef HCI_CORE_DEBUG
-#undef  DBG
-#define DBG( A... )
+#undef  BT_DBG
+#define BT_DBG( A... )
 #endif
 
 static void hci_cmd_task(unsigned long arg);
@@ -63,279 +63,69 @@
 static void hci_tx_task(unsigned long arg);
 static void hci_notify(struct hci_dev *hdev, int event);
 
-static rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
+rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
 
 /* HCI device list */
-struct hci_dev *hdev_list[HCI_MAX_DEV];
-spinlock_t hdev_list_lock;
-#define GET_HDEV(a) (hdev_list[a])
-
-/* HCI protocol list */
-struct hci_proto *hproto_list[HCI_MAX_PROTO];
-#define GET_HPROTO(a) (hproto_list[a])
+LIST_HEAD(hdev_list);
+rwlock_t hdev_list_lock = RW_LOCK_UNLOCKED;
 
-/* HCI notifiers list */
-struct notifier_block *hci_dev_notifier;
-
-/* HCI device notifications */
-int hci_register_notifier(struct notifier_block *nb)
-{
-	int err, i;
-	struct hci_dev *hdev;
-
-	if ((err = notifier_chain_register(&hci_dev_notifier, nb)))
-		return err;
-
-	/* Notify about already registered devices */
-	spin_lock(&hdev_list_lock);
-	for (i = 0; i < HCI_MAX_DEV; i++) {
-		if (!(hdev = GET_HDEV(i)))
-			continue;
-		if (hdev->flags & HCI_UP)
-			(*nb->notifier_call)(nb, HCI_DEV_UP, hdev);
-	}
-	spin_unlock(&hdev_list_lock);
-
-	return 0;
-}
-
-int hci_unregister_notifier(struct notifier_block *nb)
-{
-	return notifier_chain_unregister(&hci_dev_notifier, nb);
-}
-
-static inline void hci_notify(struct hci_dev *hdev, int event)
-{
-	notifier_call_chain(&hci_dev_notifier, event, hdev);
-}
-
-/* Get HCI device by index (device is locked on return)*/
-struct hci_dev *hci_dev_get(int index)
-{
-	struct hci_dev *hdev;
-	DBG("%d", index);
-
-	if (index < 0 || index >= HCI_MAX_DEV)
-		return NULL;
-
-	spin_lock(&hdev_list_lock);
-	if ((hdev = GET_HDEV(index)))
-		hci_dev_hold(hdev);
-	spin_unlock(&hdev_list_lock);
-
-	return hdev;
-}
-
-/* Flush inquiry cache */
-void inquiry_cache_flush(struct inquiry_cache *cache)
-{
-	struct inquiry_entry *next = cache->list, *e;
-
-	DBG("cache %p", cache);
-
-	cache->list = NULL;
-	while ((e = next)) {
-		next = e->next;
-		kfree(e);
-	}
-}
-
-/* Lookup by bdaddr.
- * Cache must be locked. */
-static struct inquiry_entry * __inquiry_cache_lookup(struct inquiry_cache *cache, bdaddr_t *bdaddr)
-{
-	struct inquiry_entry *e;
-
-	DBG("cache %p, %s", cache, batostr(bdaddr));
-
-	for (e = cache->list; e; e = e->next)
-		if (!bacmp(&e->info.bdaddr, bdaddr))
-			break;
-
-	return e;
-}
-
-static void inquiry_cache_update(struct inquiry_cache *cache, inquiry_info *info)
-{
-	struct inquiry_entry *e;
-
-	DBG("cache %p, %s", cache, batostr(&info->bdaddr));
-
-	inquiry_cache_lock(cache);
-
-	if (!(e = __inquiry_cache_lookup(cache, &info->bdaddr))) {
-		/* Entry not in the cache. Add new one. */
-		if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))
-			goto unlock;
-		memset(e, 0, sizeof(struct inquiry_entry));
-		e->next     = cache->list;
-		cache->list = e;
-	}
-
-	memcpy(&e->info, info, sizeof(inquiry_info));
-	e->timestamp = jiffies;
-	cache->timestamp = jiffies;
-unlock:
-	inquiry_cache_unlock(cache);
-}
-
-static int inquiry_cache_dump(struct inquiry_cache *cache, int num, __u8 *buf)
-{
-	inquiry_info *info = (inquiry_info *) buf;
-	struct inquiry_entry *e;
-	int copied = 0;
+/* HCI protocols */
+#define HCI_MAX_PROTO	2
+struct hci_proto *hci_proto[HCI_MAX_PROTO];
 
-	inquiry_cache_lock(cache);
-
-	for (e = cache->list; e && copied < num; e = e->next, copied++)
-		memcpy(info++, &e->info, sizeof(inquiry_info));
+/* HCI notifiers list */
+static struct notifier_block *hci_notifier;
 
-	inquiry_cache_unlock(cache);
 
-	DBG("cache %p, copied %d", cache, copied);
-	return copied;
-}
+/* ---- HCI notifications ---- */
 
-/* --------- BaseBand connections --------- */
-static struct hci_conn *hci_conn_add(struct hci_dev *hdev, __u16 handle, __u8 type, bdaddr_t *dst)
+int hci_register_notifier(struct notifier_block *nb)
 {
-	struct hci_conn *conn;
-
-	DBG("%s handle %d dst %s", hdev->name, handle, batostr(dst));
-
-	if ( conn_hash_lookup(&hdev->conn_hash, handle)) {
-		ERR("%s handle 0x%x already exists", hdev->name, handle);
-		return NULL;
-	}
-
-	if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC)))
-		return NULL;
-	memset(conn, 0, sizeof(struct hci_conn));
-
-	bacpy(&conn->dst, dst);
-	conn->handle = handle;
-	conn->type   = type;
-	conn->hdev   = hdev;
-
-	skb_queue_head_init(&conn->data_q);
-
-	hci_dev_hold(hdev);
-	conn_hash_add(&hdev->conn_hash, handle, conn);
-
-	return conn;
+	return notifier_chain_register(&hci_notifier, nb);
 }
 
-static int hci_conn_del(struct hci_dev *hdev, struct hci_conn *conn)
+int hci_unregister_notifier(struct notifier_block *nb)
 {
-	DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
-
-	conn_hash_del(&hdev->conn_hash, conn);
-	hci_dev_put(hdev);
-
-	/* Unacked frames */
-	hdev->acl_cnt += conn->sent;
-
-	skb_queue_purge(&conn->data_q);
-
-	kfree(conn);
-	return 0;
+	return notifier_chain_unregister(&hci_notifier, nb);
 }
 
-/* Drop all connection on the device */
-static void hci_conn_hash_flush(struct hci_dev *hdev)
+void hci_notify(struct hci_dev *hdev, int event)
 {
-	struct conn_hash *h = &hdev->conn_hash;
-	struct hci_proto *hp;
-        struct list_head *p;
-
-	DBG("hdev %s", hdev->name);
-
-	p = h->list.next;
-	while (p != &h->list) {
-		struct hci_conn *c;
-
-		c = list_entry(p, struct hci_conn, list);
-		p = p->next;
-
-		if (c->type == ACL_LINK) {
-			/* ACL link notify L2CAP layer */
-			if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->disconn_ind)
-				hp->disconn_ind(c, 0x16);
-		} else {
-			/* SCO link (no notification) */
-		}
-
-		hci_conn_del(hdev, c);
-	}
+	notifier_call_chain(&hci_notifier, event, hdev);
 }
 
-int hci_connect(struct hci_dev *hdev, bdaddr_t *bdaddr)
-{
-	struct inquiry_cache *cache = &hdev->inq_cache;
-	struct inquiry_entry *e;
-	create_conn_cp cc;
-	__u16 clock_offset;
-
-	DBG("%s bdaddr %s", hdev->name, batostr(bdaddr));
-
-	if (!(hdev->flags & HCI_UP))
-		return -ENODEV;
-
-	inquiry_cache_lock_bh(cache);
-
-	if (!(e = __inquiry_cache_lookup(cache, bdaddr)) || inquiry_entry_age(e) > INQUIRY_ENTRY_AGE_MAX) {
-		cc.pscan_rep_mode = 0;
-		cc.pscan_mode     = 0;
-		clock_offset      = 0;
-	} else {
-		cc.pscan_rep_mode = e->info.pscan_rep_mode;
-		cc.pscan_mode     = e->info.pscan_mode;
-		clock_offset      = __le16_to_cpu(e->info.clock_offset) & 0x8000;
-	}
-
-	inquiry_cache_unlock_bh(cache);
-
-	bacpy(&cc.bdaddr, bdaddr);
-	cc.pkt_type	= __cpu_to_le16(hdev->pkt_type);
-	cc.clock_offset	= __cpu_to_le16(clock_offset);
-
-	if (lmp_rswitch_capable(hdev))
-		cc.role_switch	= 0x01;
-	else
-		cc.role_switch	= 0x00;
-		
-	hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN, CREATE_CONN_CP_SIZE, &cc);
+/* ---- HCI hotplug support ---- */
 
-	return 0;
-}
+#ifdef CONFIG_HOTPLUG
 
-int hci_disconnect(struct hci_conn *conn, __u8 reason)
+static int hci_run_hotplug(char *dev, char *action)
 {
-	disconnect_cp dc;
-
-	DBG("conn %p handle %d", conn, conn->handle);
+	char *argv[3], *envp[5], dstr[20], astr[32];
 
-	dc.handle = __cpu_to_le16(conn->handle);
-	dc.reason = reason;
-	hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT, DISCONNECT_CP_SIZE, &dc);
+	sprintf(dstr, "DEVICE=%s", dev);
+	sprintf(astr, "ACTION=%s", action);
 
-	return 0;
-}
+        argv[0] = hotplug_path;
+        argv[1] = "bluetooth";
+        argv[2] = NULL;
 
-/* --------- HCI request handling ------------ */
-static inline void hci_req_lock(struct hci_dev *hdev)
-{
-	down(&hdev->req_lock);
+	envp[0] = "HOME=/";
+	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	envp[2] = dstr;
+	envp[3] = astr;
+	envp[4] = NULL;
+	
+	return call_usermodehelper(argv[0], argv, envp);
 }
+#else
+#define hci_run_hotplug(A...)
+#endif
 
-static inline void hci_req_unlock(struct hci_dev *hdev)
-{
-	up(&hdev->req_lock);
-}
+/* ---- HCI requests ---- */
 
-static inline void hci_req_complete(struct hci_dev *hdev, int result)
+void hci_req_complete(struct hci_dev *hdev, int result)
 {
-	DBG("%s result 0x%2.2x", hdev->name, result);
+	BT_DBG("%s result 0x%2.2x", hdev->name, result);
 
 	if (hdev->req_status == HCI_REQ_PEND) {
 		hdev->req_result = result;
@@ -344,9 +134,9 @@
 	}
 }
 
-static inline void hci_req_cancel(struct hci_dev *hdev, int err)
+void hci_req_cancel(struct hci_dev *hdev, int err)
 {
-	DBG("%s err 0x%2.2x", hdev->name, err);
+	BT_DBG("%s err 0x%2.2x", hdev->name, err);
 
 	if (hdev->req_status == HCI_REQ_PEND) {
 		hdev->req_result = err;
@@ -356,23 +146,22 @@
 }
 
 /* Execute request and wait for completion. */
-static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt),
-                         unsigned long opt, __u32 timeout)
+static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), unsigned long opt, __u32 timeout)
 {
 	DECLARE_WAITQUEUE(wait, current);
 	int err = 0;
 
-	DBG("%s start", hdev->name);
+	BT_DBG("%s start", hdev->name);
 
 	hdev->req_status = HCI_REQ_PEND;
 
 	add_wait_queue(&hdev->req_wait_q, &wait);
-	current->state = TASK_INTERRUPTIBLE;
+	set_current_state(TASK_INTERRUPTIBLE);
 
 	req(hdev, opt);
 	schedule_timeout(timeout);
 
-	current->state = TASK_RUNNING;
+	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&hdev->req_wait_q, &wait);
 
 	if (signal_pending(current))
@@ -394,7 +183,7 @@
 
 	hdev->req_status = hdev->req_result = 0;
 
-	DBG("%s end: err %d", hdev->name, err);
+	BT_DBG("%s end: err %d", hdev->name, err);
 
 	return err;
 }
@@ -412,10 +201,9 @@
 	return ret;
 }
 
-/* --------- HCI requests ---------- */
 static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
 {
-	DBG("%s %ld", hdev->name, opt);
+	BT_DBG("%s %ld", hdev->name, opt);
 
 	/* Reset device */
 	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL);
@@ -423,27 +211,44 @@
 
 static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
 {
-	set_event_flt_cp ec;
+	set_event_flt_cp ef;
 	__u16 param;
 
-	DBG("%s %ld", hdev->name, opt);
+	BT_DBG("%s %ld", hdev->name, opt);
 
 	/* Mandatory initialization */
 
+	/* Reset */
+	if (test_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks))
+		hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL);
+
 	/* Read Local Supported Features */
 	hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES, 0, NULL);
 
 	/* Read Buffer Size (ACL mtu, max pkt, etc.) */
 	hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE, 0, NULL);
 
+#if 0
+	/* Host buffer size */
+	{
+		host_buffer_size_cp bs;
+		bs.acl_mtu = __cpu_to_le16(HCI_MAX_ACL_SIZE);
+		bs.sco_mtu = HCI_MAX_SCO_SIZE;
+		bs.acl_max_pkt = __cpu_to_le16(0xffff);
+		bs.sco_max_pkt = __cpu_to_le16(0xffff);
+		hci_send_cmd(hdev, OGF_HOST_CTL, OCF_HOST_BUFFER_SIZE,
+				HOST_BUFFER_SIZE_CP_SIZE, &bs);
+	}
+#endif
+
 	/* Read BD Address */
 	hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BD_ADDR, 0, NULL);
 
 	/* Optional initialization */
 
 	/* Clear Event Filters */
-	ec.flt_type  = FLT_CLEAR_ALL;
-	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &ec);
+	ef.flt_type  = FLT_CLEAR_ALL;
+	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &ef);
 
 	/* Page timeout ~20 secs */
 	param = __cpu_to_le16(0x8000);
@@ -458,7 +263,7 @@
 {
 	__u8 scan = opt;
 
-	DBG("%s %x", hdev->name, scan);
+	BT_DBG("%s %x", hdev->name, scan);
 
 	/* Inquiry and Page scans */
 	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE, 1, &scan);
@@ -468,116 +273,273 @@
 {
 	__u8 auth = opt;
 
-	DBG("%s %x", hdev->name, auth);
+	BT_DBG("%s %x", hdev->name, auth);
 
 	/* Authentication */
 	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE, 1, &auth);
 }
 
-static void hci_inq_req(struct hci_dev *hdev, unsigned long opt)
+static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt)
 {
-	struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;
-	inquiry_cp ic;
+	__u8 encrypt = opt;
 
-	DBG("%s", hdev->name);
+	BT_DBG("%s %x", hdev->name, encrypt);
 
-	/* Start Inquiry */
-	memcpy(&ic.lap, &ir->lap, 3);
-	ic.lenght  = ir->length;
-	ic.num_rsp = ir->num_rsp;
-	hci_send_cmd(hdev, OGF_LINK_CTL, OCF_INQUIRY, INQUIRY_CP_SIZE, &ic);
+	/* Authentication */
+	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE, 1, &encrypt);
 }
 
-/* HCI ioctl helpers */
-int hci_dev_open(__u16 dev)
+/* Get HCI device by index. 
+ * Device is locked on return. */
+struct hci_dev *hci_dev_get(int index)
 {
 	struct hci_dev *hdev;
-	int ret = 0;
-
-	if (!(hdev = hci_dev_get(dev)))
-		return -ENODEV;
+	struct list_head *p;
 
-	DBG("%s %p", hdev->name, hdev);
+	BT_DBG("%d", index);
 
-	hci_req_lock(hdev);
+	if (index < 0)
+		return NULL;
 
-	if (hdev->flags & HCI_UP) {
-		ret = -EALREADY;
-		goto done;
+	read_lock(&hdev_list_lock);
+	list_for_each(p, &hdev_list) {
+		hdev = list_entry(p, struct hci_dev, list);
+		if (hdev->id == index) {
+			hci_dev_hold(hdev);
+			goto done;
+		}
 	}
+	hdev = NULL;
+done:
+	read_unlock(&hdev_list_lock);
+	return hdev;
+}
 
-	if (hdev->open(hdev)) {
-		ret = -EIO;
-		goto done;
-	}
+/* ---- Inquiry support ---- */
+void inquiry_cache_flush(struct hci_dev *hdev)
+{
+	struct inquiry_cache *cache = &hdev->inq_cache;
+	struct inquiry_entry *next  = cache->list, *e;
 
-	if (hdev->flags & HCI_NORMAL) {
-		atomic_set(&hdev->cmd_cnt, 1);
-		hdev->flags |= HCI_INIT;
+	BT_DBG("cache %p", cache);
 
-		//__hci_request(hdev, hci_reset_req, 0, HZ);
-		ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
-       
-		hdev->flags &= ~HCI_INIT;
+	cache->list = NULL;
+	while ((e = next)) {
+		next = e->next;
+		kfree(e);
 	}
+}
 
-	if (!ret) {
-		hdev->flags |= HCI_UP;
-		hci_notify(hdev, HCI_DEV_UP);
-	} else {	
-		/* Init failed, cleanup */
-		tasklet_kill(&hdev->rx_task);
-		tasklet_kill(&hdev->tx_task);
-		tasklet_kill(&hdev->cmd_task);
+struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+	struct inquiry_cache *cache = &hdev->inq_cache;
+	struct inquiry_entry *e;
 
-		skb_queue_purge(&hdev->cmd_q);
-		skb_queue_purge(&hdev->rx_q);
+	BT_DBG("cache %p, %s", cache, batostr(bdaddr));
 
-		if (hdev->flush)
-			hdev->flush(hdev);
+	for (e = cache->list; e; e = e->next)
+		if (!bacmp(&e->info.bdaddr, bdaddr))
+			break;
+	return e;
+}
 
-		if (hdev->sent_cmd) {
-			kfree_skb(hdev->sent_cmd);
-			hdev->sent_cmd = NULL;
-		}
+void inquiry_cache_update(struct hci_dev *hdev, inquiry_info *info)
+{
+	struct inquiry_cache *cache = &hdev->inq_cache;
+	struct inquiry_entry *e;
 
-		hdev->close(hdev);
-	}
+	BT_DBG("cache %p, %s", cache, batostr(&info->bdaddr));
 
-done:
-	hci_req_unlock(hdev);
-	hci_dev_put(hdev);
+	if (!(e = inquiry_cache_lookup(hdev, &info->bdaddr))) {
+		/* Entry not in the cache. Add new one. */
+		if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))
+			return;
+		memset(e, 0, sizeof(struct inquiry_entry));
+		e->next     = cache->list;
+		cache->list = e;
+	}
 
-	return ret;
+	memcpy(&e->info, info, sizeof(inquiry_info));
+	e->timestamp = jiffies;
+	cache->timestamp = jiffies;
 }
 
-int hci_dev_close(__u16 dev)
+int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
 {
-	struct hci_dev *hdev;
-
-	if (!(hdev = hci_dev_get(dev)))
-		return -ENODEV;
+	struct inquiry_cache *cache = &hdev->inq_cache;
+	inquiry_info *info = (inquiry_info *) buf;
+	struct inquiry_entry *e;
+	int copied = 0;
 
-	DBG("%s %p", hdev->name, hdev);
+	for (e = cache->list; e && copied < num; e = e->next, copied++)
+		memcpy(info++, &e->info, sizeof(inquiry_info));
 
-	hci_req_cancel(hdev, ENODEV);
-	hci_req_lock(hdev);
+	BT_DBG("cache %p, copied %d", cache, copied);
+	return copied;
+}
 
-	if (!(hdev->flags & HCI_UP))
-		goto done;
+static void hci_inq_req(struct hci_dev *hdev, unsigned long opt)
+{
+	struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;
+	inquiry_cp ic;
 
-	/* Kill RX and TX tasks */
-	tasklet_kill(&hdev->rx_task);
-	tasklet_kill(&hdev->tx_task);
+	BT_DBG("%s", hdev->name);
 
-	inquiry_cache_flush(&hdev->inq_cache);
+	if (test_bit(HCI_INQUIRY, &hdev->flags))
+		return;
 
-	hci_conn_hash_flush(hdev);
+	/* Start Inquiry */
+	memcpy(&ic.lap, &ir->lap, 3);
+	ic.length  = ir->length;
+	ic.num_rsp = ir->num_rsp;
+	hci_send_cmd(hdev, OGF_LINK_CTL, OCF_INQUIRY, INQUIRY_CP_SIZE, &ic);
+}
 
-	/* Clear flags */
-	hdev->flags &= HCI_SOCK;
-	hdev->flags |= HCI_NORMAL;
+int hci_inquiry(unsigned long arg)
+{
+	struct hci_inquiry_req ir;
+	struct hci_dev *hdev;
+	int err = 0, do_inquiry = 0, max_rsp;
+	long timeo;
+	__u8 *buf, *ptr;
 
+	ptr = (void *) arg;
+	if (copy_from_user(&ir, ptr, sizeof(ir)))
+		return -EFAULT;
+
+	if (!(hdev = hci_dev_get(ir.dev_id)))
+		return -ENODEV;
+
+	hci_dev_lock_bh(hdev);
+	if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX || 
+					inquiry_cache_empty(hdev) ||
+					ir.flags & IREQ_CACHE_FLUSH) {
+		inquiry_cache_flush(hdev);
+		do_inquiry = 1;
+	}
+	hci_dev_unlock_bh(hdev);
+
+	timeo = ir.length * 2 * HZ;
+	if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
+		goto done;
+
+	/* for unlimited number of responses we will use buffer with 255 entries */
+	max_rsp = (ir.num_rsp == 0) ? 255 : ir.num_rsp;
+
+	/* cache_dump can't sleep. Therefore we allocate temp buffer and then
+	 * copy it to the user space.
+	 */
+	if (!(buf = kmalloc(sizeof(inquiry_info) * max_rsp, GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto done;
+	}
+
+	hci_dev_lock_bh(hdev);
+	ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);
+	hci_dev_unlock_bh(hdev);
+
+	BT_DBG("num_rsp %d", ir.num_rsp);
+
+	if (!verify_area(VERIFY_WRITE, ptr, sizeof(ir) + 
+				(sizeof(inquiry_info) * ir.num_rsp))) {
+		copy_to_user(ptr, &ir, sizeof(ir));
+		ptr += sizeof(ir);
+	        copy_to_user(ptr, buf, sizeof(inquiry_info) * ir.num_rsp);
+	} else 
+		err = -EFAULT;
+
+	kfree(buf);
+
+done:
+	hci_dev_put(hdev);
+	return err;
+}
+
+/* ---- HCI ioctl helpers ---- */
+
+int hci_dev_open(__u16 dev)
+{
+	struct hci_dev *hdev;
+	int ret = 0;
+
+	if (!(hdev = hci_dev_get(dev)))
+		return -ENODEV;
+
+	BT_DBG("%s %p", hdev->name, hdev);
+
+	hci_req_lock(hdev);
+
+	if (test_bit(HCI_UP, &hdev->flags)) {
+		ret = -EALREADY;
+		goto done;
+	}
+
+	if (hdev->open(hdev)) {
+		ret = -EIO;
+		goto done;
+	}
+
+	if (!test_bit(HCI_RAW, &hdev->flags)) {
+		atomic_set(&hdev->cmd_cnt, 1);
+		set_bit(HCI_INIT, &hdev->flags);
+
+		//__hci_request(hdev, hci_reset_req, 0, HZ);
+		ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
+       
+		clear_bit(HCI_INIT, &hdev->flags);
+	}
+
+	if (!ret) {
+		set_bit(HCI_UP, &hdev->flags);
+		hci_notify(hdev, HCI_DEV_UP);
+	} else {	
+		/* Init failed, cleanup */
+		tasklet_kill(&hdev->rx_task);
+		tasklet_kill(&hdev->tx_task);
+		tasklet_kill(&hdev->cmd_task);
+
+		skb_queue_purge(&hdev->cmd_q);
+		skb_queue_purge(&hdev->rx_q);
+
+		if (hdev->flush)
+			hdev->flush(hdev);
+
+		if (hdev->sent_cmd) {
+			kfree_skb(hdev->sent_cmd);
+			hdev->sent_cmd = NULL;
+		}
+
+		hdev->close(hdev);
+		hdev->flags = 0;
+	}
+
+done:
+	hci_req_unlock(hdev);
+	hci_dev_put(hdev);
+	return ret;
+}
+
+static int hci_dev_do_close(struct hci_dev *hdev)
+{
+	BT_DBG("%s %p", hdev->name, hdev);
+
+	hci_req_cancel(hdev, ENODEV);
+	hci_req_lock(hdev);
+
+	if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
+		hci_req_unlock(hdev);
+		return 0;
+	}
+
+	/* Kill RX and TX tasks */
+	tasklet_kill(&hdev->rx_task);
+	tasklet_kill(&hdev->tx_task);
+
+	hci_dev_lock_bh(hdev);
+	inquiry_cache_flush(hdev);
+	hci_conn_hash_flush(hdev);
+	hci_dev_unlock_bh(hdev);
+	
 	hci_notify(hdev, HCI_DEV_DOWN);
 
 	if (hdev->flush)
@@ -586,9 +548,9 @@
 	/* Reset device */
 	skb_queue_purge(&hdev->cmd_q);
 	atomic_set(&hdev->cmd_cnt, 1);
-	hdev->flags |= HCI_INIT;
-	__hci_request(hdev, hci_reset_req, 0, HZ);
-	hdev->flags &= ~HCI_INIT;
+	set_bit(HCI_INIT, &hdev->flags);
+	__hci_request(hdev, hci_reset_req, 0, HZ/4);
+	clear_bit(HCI_INIT, &hdev->flags);
 
 	/* Kill cmd task */
 	tasklet_kill(&hdev->cmd_task);
@@ -605,17 +567,28 @@
 	}
 
 	/* After this point our queues are empty
-	 * and no tasks are scheduled.
-	 */
+	 * and no tasks are scheduled. */
 	hdev->close(hdev);
 
-done:
-	hci_req_unlock(hdev);
-	hci_dev_put(hdev);
+	/* Clear flags */
+	hdev->flags = 0;
 
+	hci_req_unlock(hdev);
 	return 0;
 }
 
+int hci_dev_close(__u16 dev)
+{
+	struct hci_dev *hdev;
+	int err;
+	
+	if (!(hdev = hci_dev_get(dev)))
+		return -ENODEV;
+	err = hci_dev_do_close(hdev);
+	hci_dev_put(hdev);
+	return err;
+}
+
 int hci_dev_reset(__u16 dev)
 {
 	struct hci_dev *hdev;
@@ -627,16 +600,17 @@
 	hci_req_lock(hdev);
 	tasklet_disable(&hdev->tx_task);
 
-	if (!(hdev->flags & HCI_UP))
+	if (!test_bit(HCI_UP, &hdev->flags))
 		goto done;
 
 	/* Drop queues */
 	skb_queue_purge(&hdev->rx_q);
 	skb_queue_purge(&hdev->cmd_q);
 
-	inquiry_cache_flush(&hdev->inq_cache);
-
+	hci_dev_lock_bh(hdev);
+	inquiry_cache_flush(hdev);
 	hci_conn_hash_flush(hdev);
+	hci_dev_unlock_bh(hdev);
 
 	if (hdev->flush)
 		hdev->flush(hdev);
@@ -650,7 +624,6 @@
 	tasklet_enable(&hdev->tx_task);
 	hci_req_unlock(hdev);
 	hci_dev_put(hdev);
-
 	return ret;
 }
 
@@ -669,30 +642,11 @@
 	return ret;
 }
 
-int hci_dev_setauth(unsigned long arg)
-{
-	struct hci_dev *hdev;
-	struct hci_dev_req dr;
-	int ret = 0;
-
-	if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
-		return -EFAULT;
-
-	if (!(hdev = hci_dev_get(dr.dev_id)))
-		return -ENODEV;
-
-	ret = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
-
-	hci_dev_put(hdev);
-
-	return ret;
-}
-
-int hci_dev_setscan(unsigned long arg)
+int hci_dev_cmd(unsigned int cmd, unsigned long arg)
 {
 	struct hci_dev *hdev;
 	struct hci_dev_req dr;
-	int ret = 0;
+	int err = 0;
 
 	if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
 		return -EFAULT;
@@ -700,75 +654,105 @@
 	if (!(hdev = hci_dev_get(dr.dev_id)))
 		return -ENODEV;
 
-	ret = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
-
-	hci_dev_put(hdev);
+	switch (cmd) {
+	case HCISETAUTH:
+		err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+		break;
 
-	return ret;
-}
+	case HCISETENCRYPT:
+		if (!lmp_encrypt_capable(hdev)) {
+			err = -EOPNOTSUPP;
+			break;
+		}
 
-int hci_dev_setptype(unsigned long arg)
-{
-	struct hci_dev *hdev;
-	struct hci_dev_req dr;
-	int ret = 0;
+		if (!test_bit(HCI_AUTH, &hdev->flags)) {
+			/* Auth must be enabled first */
+			err = hci_request(hdev, hci_auth_req,
+					dr.dev_opt, HCI_INIT_TIMEOUT);
+			if (err)
+				break;
+		}
+			
+		err = hci_request(hdev, hci_encrypt_req,
+					dr.dev_opt, HCI_INIT_TIMEOUT);
+		break;
+	
+	case HCISETSCAN:
+		err = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+		break;
+	
+	case HCISETPTYPE:
+		hdev->pkt_type = (__u16) dr.dev_opt;
+		break;
+		
+	case HCISETLINKPOL:
+		hdev->link_policy = (__u16) dr.dev_opt;
+		break;
 
-	if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
-		return -EFAULT;
+	case HCISETLINKMODE:
+		hdev->link_mode = ((__u16) dr.dev_opt) & (HCI_LM_MASTER | HCI_LM_ACCEPT);
+		break;
 
-	if (!(hdev = hci_dev_get(dr.dev_id)))
-		return -ENODEV;
+	case HCISETACLMTU:
+		hdev->acl_mtu  = *((__u16 *)&dr.dev_opt + 1);
+		hdev->acl_pkts = *((__u16 *)&dr.dev_opt + 0);
+		break;
 
-	hdev->pkt_type = (__u16) dr.dev_opt;
+	case HCISETSCOMTU:
+		hdev->sco_mtu  = *((__u16 *)&dr.dev_opt + 1);
+		hdev->sco_pkts = *((__u16 *)&dr.dev_opt + 0);
+		break;
 
+	default:
+		err = -EINVAL;
+		break;
+	}	
 	hci_dev_put(hdev);
-
-	return ret;
+	return err;
 }
 
-int hci_dev_list(unsigned long arg)
+int hci_get_dev_list(unsigned long arg)
 {
 	struct hci_dev_list_req *dl;
 	struct hci_dev_req *dr;
-	struct hci_dev *hdev;
-	int i, n, size;
+	struct list_head *p;
+	int n = 0, size, err;
 	__u16 dev_num;
 
 	if (get_user(dev_num, (__u16 *) arg))
 		return -EFAULT;
 
-	/* Avoid long loop, overflow */
-	if (dev_num > 2048)
+	if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr))
 		return -EINVAL;
-	
-	size = dev_num * sizeof(struct hci_dev_req) + sizeof(__u16);
 
-	if (verify_area(VERIFY_WRITE, (void *) arg, size))
-		return -EFAULT;
+	size = sizeof(*dl) + dev_num * sizeof(*dr);
 
 	if (!(dl = kmalloc(size, GFP_KERNEL)))
 		return -ENOMEM;
+
 	dr = dl->dev_req;
 
-	spin_lock_bh(&hdev_list_lock);
-	for (i = 0, n = 0; i < HCI_MAX_DEV && n < dev_num; i++) {
-		if ((hdev = hdev_list[i])) {
-			(dr + n)->dev_id  = hdev->id;
-			(dr + n)->dev_opt = hdev->flags;
-			n++;
-		}
+	read_lock_bh(&hdev_list_lock);
+	list_for_each(p, &hdev_list) {
+		struct hci_dev *hdev;
+		hdev = list_entry(p, struct hci_dev, list);
+		(dr + n)->dev_id  = hdev->id;
+		(dr + n)->dev_opt = hdev->flags;
+		if (++n >= dev_num)
+			break;
 	}
-	spin_unlock_bh(&hdev_list_lock);
+	read_unlock_bh(&hdev_list_lock);
 
 	dl->dev_num = n;
-	size = n * sizeof(struct hci_dev_req) + sizeof(__u16);
+	size = sizeof(*dl) + n * sizeof(*dr);
 
-	copy_to_user((void *) arg, dl, size);
+	err = copy_to_user((void *) arg, dl, size);
+	kfree(dl);
 
-	return 0;
+	return err ? -EFAULT : 0;
 }
 
-int hci_dev_info(unsigned long arg)
+int hci_get_dev_info(unsigned long arg)
 {
 	struct hci_dev *hdev;
 	struct hci_dev_info di;
@@ -786,9 +770,11 @@
 	di.flags    = hdev->flags;
 	di.pkt_type = hdev->pkt_type;
 	di.acl_mtu  = hdev->acl_mtu;
-	di.acl_max  = hdev->acl_max;
+	di.acl_pkts = hdev->acl_pkts;
 	di.sco_mtu  = hdev->sco_mtu;
-	di.sco_max  = hdev->sco_max;
+	di.sco_pkts = hdev->sco_pkts;
+	di.link_policy = hdev->link_policy;
+	di.link_mode   = hdev->link_mode;
 
 	memcpy(&di.stat, &hdev->stat, sizeof(di.stat));
 	memcpy(&di.features, &hdev->features, sizeof(di.features));
@@ -801,258 +787,168 @@
 	return err;
 }
 
-__u32 hci_dev_setmode(struct hci_dev *hdev, __u32 mode)
-{
-	__u32 omode = hdev->flags & HCI_MODE_MASK;
-
-	hdev->flags &= ~HCI_MODE_MASK;
-	hdev->flags |= (mode & HCI_MODE_MASK);
 
-	return omode;
-}
+/* ---- Interface to HCI drivers ---- */
 
-__u32 hci_dev_getmode(struct hci_dev *hdev)
+/* Register HCI device */
+int hci_register_dev(struct hci_dev *hdev)
 {
-	return hdev->flags & HCI_MODE_MASK;
-}
+	struct list_head *head = &hdev_list, *p;
+	int id = 0;
 
-int hci_conn_list(unsigned long arg)
-{
-	struct hci_conn_list_req req, *cl;
-	struct hci_conn_info *ci;
-	struct hci_dev *hdev;
-	struct list_head *p;
-	int n = 0, size;
+	BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
 
-	if (copy_from_user(&req, (void *) arg, sizeof(req)))
-		return -EFAULT;
+	if (!hdev->open || !hdev->close || !hdev->destruct)
+		return -EINVAL;
 
-	if (!(hdev = hci_dev_get(req.dev_id)))
-		return -ENODEV;
+	write_lock_bh(&hdev_list_lock);
 
-	/* Set a limit to avoid overlong loops, and also numeric overflow - AC */
-	if(req.conn_num < 2048)
-		return -EINVAL;
+	/* Find first available device id */
+	list_for_each(p, &hdev_list) {
+	       	if (list_entry(p, struct hci_dev, list)->id != id)
+			break;
+		head = p; id++;
+	}
 	
-	size = req.conn_num * sizeof(struct hci_conn_info) + sizeof(req);
+	sprintf(hdev->name, "hci%d", id);
+	hdev->id = id;
+	list_add(&hdev->list, head);
 
-	if (!(cl = kmalloc(size, GFP_KERNEL)))
-		return -ENOMEM;
-	ci = cl->conn_info;
-
-	local_bh_disable();
-	conn_hash_lock(&hdev->conn_hash);
-	list_for_each(p, &hdev->conn_hash.list) {
-		register struct hci_conn *c;
-		c = list_entry(p, struct hci_conn, list);
+	atomic_set(&hdev->refcnt, 1);
+	spin_lock_init(&hdev->lock);
+			
+	hdev->flags = 0;
+	hdev->pkt_type  = (HCI_DM1 | HCI_DH1 | HCI_HV1);
+	hdev->link_mode = (HCI_LM_ACCEPT);
 
-		(ci + n)->handle = c->handle;
-		bacpy(&(ci + n)->bdaddr, &c->dst);
-		n++;
-	}
-	conn_hash_unlock(&hdev->conn_hash);
-	local_bh_enable();
-
-	cl->dev_id = hdev->id;
-	cl->conn_num = n;
-	size = n * sizeof(struct hci_conn_info) + sizeof(req);
+	tasklet_init(&hdev->cmd_task, hci_cmd_task,(unsigned long) hdev);
+	tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
+	tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
 
-	hci_dev_put(hdev);
+	skb_queue_head_init(&hdev->rx_q);
+	skb_queue_head_init(&hdev->cmd_q);
+	skb_queue_head_init(&hdev->raw_q);
 
-	if(copy_to_user((void *) arg, cl, size))
-		return -EFAULT;
-	return 0;
-}
+	init_waitqueue_head(&hdev->req_wait_q);
+	init_MUTEX(&hdev->req_lock);
 
-int hci_inquiry(unsigned long arg)
-{
-	struct inquiry_cache *cache;
-	struct hci_inquiry_req ir;
-	struct hci_dev *hdev;
-	int err = 0, do_inquiry = 0;
-	long timeo;
-	__u8 *buf, *ptr;
+	inquiry_cache_init(hdev);
 
-	ptr = (void *) arg;
-	if (copy_from_user(&ir, ptr, sizeof(ir)))
-		return -EFAULT;
+	conn_hash_init(hdev);
 
-	if (!(hdev = hci_dev_get(ir.dev_id)))
-		return -ENODEV;
+	memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
 
-	cache = &hdev->inq_cache;
+	atomic_set(&hdev->promisc, 0);
 
-	inquiry_cache_lock(cache);
-	if (inquiry_cache_age(cache) > INQUIRY_CACHE_AGE_MAX || ir.flags & IREQ_CACHE_FLUSH) {
-		inquiry_cache_flush(cache);
-		do_inquiry = 1;
-	}
-	inquiry_cache_unlock(cache);
+	MOD_INC_USE_COUNT;
 
-	/* Limit inquiry time, also avoid overflows */
+	write_unlock_bh(&hdev_list_lock);
 
-	if(ir.length > 2048 || ir.num_rsp > 2048)
-	{
-		err = -EINVAL;
-		goto done;
-	}
+	hci_notify(hdev, HCI_DEV_REG);
+	hci_run_hotplug(hdev->name, "register");
 
-	timeo = ir.length * 2 * HZ;
-	if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
-		goto done;
+	return id;
+}
 
-	/* cache_dump can't sleep. Therefore we allocate temp buffer and then
-	 * copy it to the user space.
-	 */
-	if (!(buf = kmalloc(sizeof(inquiry_info) * ir.num_rsp, GFP_KERNEL))) {
-		err = -ENOMEM;
-		goto done;
-	}
-	ir.num_rsp = inquiry_cache_dump(cache, ir.num_rsp, buf);
+/* Unregister HCI device */
+int hci_unregister_dev(struct hci_dev *hdev)
+{
+	BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
 
-	DBG("num_rsp %d", ir.num_rsp);
+	write_lock_bh(&hdev_list_lock);
+	list_del(&hdev->list);
+	write_unlock_bh(&hdev_list_lock);
 
-	if (!verify_area(VERIFY_WRITE, ptr, sizeof(ir) + (sizeof(inquiry_info) * ir.num_rsp))) {
-		copy_to_user(ptr, &ir, sizeof(ir));
-		ptr += sizeof(ir);
-	        copy_to_user(ptr, buf, sizeof(inquiry_info) * ir.num_rsp);
-	} else 
-		err = -EFAULT;
+	hci_dev_do_close(hdev);
 
-	kfree(buf);
+	hci_notify(hdev, HCI_DEV_UNREG);
+	hci_run_hotplug(hdev->name, "unregister");
 
-done:
 	hci_dev_put(hdev);
 
-	return err;
+	MOD_DEC_USE_COUNT;
+	return 0;
 }
 
-/* Interface to HCI drivers */
-
-/* Register HCI device */
-int hci_register_dev(struct hci_dev *hdev)
+/* Suspend HCI device */
+int hci_suspend_dev(struct hci_dev *hdev)
 {
-	int i;
+	hci_notify(hdev, HCI_DEV_SUSPEND);
+	hci_run_hotplug(hdev->name, "suspend");
+	return 0;
+}
 
-	DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
+/* Resume HCI device */
+int hci_resume_dev(struct hci_dev *hdev)
+{
+	hci_notify(hdev, HCI_DEV_RESUME);
+	hci_run_hotplug(hdev->name, "resume");
+	return 0;
+}       
 
-	/* Find free slot */
-	spin_lock_bh(&hdev_list_lock);
-	for (i = 0; i < HCI_MAX_DEV; i++) {
-		if (!hdev_list[i]) {
-			hdev_list[i] = hdev;
-
-			sprintf(hdev->name, "hci%d", i);
-			atomic_set(&hdev->refcnt, 0);
-			hdev->id    = i;
-			hdev->flags = HCI_NORMAL;
-
-			hdev->pkt_type = (HCI_DM1 | HCI_DH1);
-
-			tasklet_init(&hdev->cmd_task, hci_cmd_task, (unsigned long) hdev);
-			tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
-			tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
-
-			skb_queue_head_init(&hdev->rx_q);
-			skb_queue_head_init(&hdev->cmd_q);
-			skb_queue_head_init(&hdev->raw_q);
-
-			init_waitqueue_head(&hdev->req_wait_q);
-			init_MUTEX(&hdev->req_lock);
-
-			inquiry_cache_init(&hdev->inq_cache);
-
-			conn_hash_init(&hdev->conn_hash);
-
-			memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
-
-			hci_notify(hdev, HCI_DEV_REG);
-
-			MOD_INC_USE_COUNT;
-			break;
-		}
-	}
-	spin_unlock_bh(&hdev_list_lock);
-
-	return (i == HCI_MAX_DEV) ? -1 : i;
-}
-
-/* Unregister HCI device */
-int hci_unregister_dev(struct hci_dev *hdev)
+/* Receive frame from HCI drivers */
+int hci_recv_frame(struct sk_buff *skb)
 {
-	int i;
-
-	DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
-
-	if (hdev->flags & HCI_UP)
-		hci_dev_close(hdev->id);
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
 
-	/* Find device slot */
-	spin_lock(&hdev_list_lock);
-	for (i = 0; i < HCI_MAX_DEV; i++) {
-		if (hdev_list[i] == hdev) {
-			hdev_list[i] = NULL;
-			MOD_DEC_USE_COUNT;
-			break;
-		}
+	if (!hdev || (!test_bit(HCI_UP, &hdev->flags) && 
+				!test_bit(HCI_INIT, &hdev->flags)) ) {
+		kfree_skb(skb);
+		return -1;
 	}
-	spin_unlock(&hdev_list_lock);
 
-	hci_notify(hdev, HCI_DEV_UNREG);
-
-	/* Sleep while device is in use */
-	while (atomic_read(&hdev->refcnt)) {
-		int sleep_cnt = 100;
+	BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
 
-		DBG("%s sleeping on lock %d", hdev->name, atomic_read(&hdev->refcnt));
+	/* Incomming skb */
+	bluez_cb(skb)->incomming = 1;
 
-		sleep_on_timeout(&hdev->req_wait_q, HZ*10);
-		if (!(--sleep_cnt))
-			break;
-	}
+	/* Time stamp */
+	do_gettimeofday(&skb->stamp);
 
+	/* Queue frame for rx task */
+	skb_queue_tail(&hdev->rx_q, skb);
+	hci_sched_rx(hdev);
 	return 0;
 }
 
-/* Interface to upper protocols */
+/* ---- Interface to upper protocols ---- */
 
 /* Register/Unregister protocols.
- * hci_task_lock is used to ensure that no tasks are running.
- */
-int hci_register_proto(struct hci_proto *hproto)
+ * hci_task_lock is used to ensure that no tasks are running. */
+int hci_register_proto(struct hci_proto *hp)
 {
 	int err = 0;
 
-	DBG("%p name %s", hproto, hproto->name);
+	BT_DBG("%p name %s id %d", hp, hp->name, hp->id);
 
-	if (hproto->id >= HCI_MAX_PROTO)
+	if (hp->id >= HCI_MAX_PROTO)
 		return -EINVAL;
 
 	write_lock_bh(&hci_task_lock);
 
-	if (!hproto_list[hproto->id])
-		hproto_list[hproto->id] = hproto;
+	if (!hci_proto[hp->id])
+		hci_proto[hp->id] = hp;
 	else
-		err = -1;
+		err = -EEXIST;
 
 	write_unlock_bh(&hci_task_lock);
 
 	return err;
 }
 
-int hci_unregister_proto(struct hci_proto *hproto)
+int hci_unregister_proto(struct hci_proto *hp)
 {
 	int err = 0;
 
-	DBG("%p name %s", hproto, hproto->name);
+	BT_DBG("%p name %s id %d", hp, hp->name, hp->id);
 
-	if (hproto->id > HCI_MAX_PROTO)
+	if (hp->id >= HCI_MAX_PROTO)
 		return -EINVAL;
 
 	write_lock_bh(&hci_task_lock);
 
-	if (hproto_list[hproto->id])
-		hproto_list[hproto->id] = NULL;
+	if (hci_proto[hp->id])
+		hci_proto[hp->id] = NULL;
 	else
 		err = -ENOENT;
 
@@ -1070,10 +966,14 @@
 		return -ENODEV;
 	}
 
-	DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+	BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+
+	if (atomic_read(&hdev->promisc)) {
+		/* Time stamp */
+	        do_gettimeofday(&skb->stamp);
 
-	if (hdev->flags & HCI_SOCK)
 		hci_send_to_sock(hdev, skb);
+	}
 
 	/* Get rid of skb owner, prior to sending to the driver. */
 	skb_orphan(skb);
@@ -1081,128 +981,6 @@
 	return hdev->send(skb);
 }
 
-/* Connection scheduler */
-static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
-{
-	struct conn_hash *h = &hdev->conn_hash;
-	struct hci_conn *conn = NULL;
-	int num = 0, min = 0xffff;
-        struct list_head *p;
-
-	conn_hash_lock(h);
-	list_for_each(p, &h->list) {
-		register struct hci_conn *c;
-
-		c = list_entry(p, struct hci_conn, list);
-
-		if (c->type != type || skb_queue_empty(&c->data_q))
-			continue;
-		num++;
-
-		if (c->sent < min) {
-			min  = c->sent;
-			conn = c;
-		}
-	}
-	conn_hash_unlock(h);
-
-	if (conn) {
-		int q = hdev->acl_cnt / num;
-		*quote = q ? q : 1;
-	} else
-		*quote = 0;
-
-	DBG("conn %p quote %d", conn, *quote);
-
-	return conn;
-}
-
-static inline void hci_sched_acl(struct hci_dev *hdev)
-{
-	struct hci_conn *conn;
-	struct sk_buff *skb;
-	int quote;
-
-	DBG("%s", hdev->name);
-
-	while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
-		while (quote && (skb = skb_dequeue(&conn->data_q))) {
-			DBG("skb %p len %d", skb, skb->len);
-
-			hci_send_frame(skb);
-
-			conn->sent++;
-			hdev->acl_cnt--;
-			quote--;
-		}
-	}
-}
-
-/* Schedule SCO */
-static inline void hci_sched_sco(struct hci_dev *hdev)
-{
-	/* FIXME: For now we queue SCO packets to the raw queue 
-
-		while (hdev->sco_cnt && (skb = skb_dequeue(&conn->data_q))) {
-			hci_send_frame(skb);
-			conn->sco_sent++;
-			hdev->sco_cnt--;
-		}
-	*/
-}
-
-/* Get data from the previously sent command */
-static void * hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf)
-{
-	hci_command_hdr *hc;
-
-	if (!hdev->sent_cmd)
-		return NULL;
-
-	hc = (void *) hdev->sent_cmd->data;
-
-	if (hc->opcode != __cpu_to_le16(cmd_opcode_pack(ogf, ocf)))
-		return NULL;
-
-	DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf);
-
-	return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
-}
-
-/* Send raw HCI frame */
-int hci_send_raw(struct sk_buff *skb)
-{
-	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-
-	if (!hdev) {
-		kfree_skb(skb);
-		return -ENODEV;
-	}
-
-	DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
-
-	if (hdev->flags & HCI_NORMAL) {
-		/* Queue frame according it's type */
-		switch (skb->pkt_type) {
-		case HCI_COMMAND_PKT:
-			skb_queue_tail(&hdev->cmd_q, skb);
-			hci_sched_cmd(hdev);
-			return 0;
-
-		case HCI_ACLDATA_PKT:
-		case HCI_SCODATA_PKT:
-			/* FIXME:
-		 	 * Check header here and queue to apropriate connection.
-		 	 */
-			break;
-		}
-	}
-
-	skb_queue_tail(&hdev->raw_q, skb);
-	hci_sched_tx(hdev);
-	return 0;
-}
-
 /* Send HCI command */
 int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param)
 {
@@ -1210,10 +988,10 @@
 	hci_command_hdr *hc;
 	struct sk_buff *skb;
 
-	DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen);
+	BT_DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen);
 
 	if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) {
-		ERR("%s Can't allocate memory for HCI command", hdev->name);
+		BT_ERR("%s Can't allocate memory for HCI command", hdev->name);
 		return -ENOMEM;
 	}
 	
@@ -1224,7 +1002,7 @@
 	if (plen)
 		memcpy(skb_put(skb, plen), param, plen);
 
-	DBG("skb len %d", skb->len);
+	BT_DBG("skb len %d", skb->len);
 
 	skb->pkt_type = HCI_COMMAND_PKT;
 	skb->dev = (void *) hdev;
@@ -1234,10 +1012,28 @@
 	return 0;
 }
 
+/* Get data from the previously sent command */
+void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf)
+{
+	hci_command_hdr *hc;
+
+	if (!hdev->sent_cmd)
+		return NULL;
+
+	hc = (void *) hdev->sent_cmd->data;
+
+	if (hc->opcode != __cpu_to_le16(cmd_opcode_pack(ogf, ocf)))
+		return NULL;
+
+	BT_DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf);
+
+	return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
+}
+
 /* Send ACL data */
 static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
 {
-	int len = skb->len;	
+	int len = skb->len;
 	hci_acl_hdr *ah;
 
 	ah = (hci_acl_hdr *) skb_push(skb, HCI_ACL_HDR_SIZE);
@@ -1252,7 +1048,7 @@
 	struct hci_dev *hdev = conn->hdev;
 	struct sk_buff *list;
 
-	DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
+	BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
 
 	skb->dev = (void *) hdev;
 	skb->pkt_type = HCI_ACLDATA_PKT;
@@ -1260,12 +1056,12 @@
 
 	if (!(list = skb_shinfo(skb)->frag_list)) {
 		/* Non fragmented */
-		DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
+		BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
 		
 		skb_queue_tail(&conn->data_q, skb);
 	} else {
 		/* Fragmented */
-		DBG("%s frag %p len %d", hdev->name, skb, skb->len);
+		BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
 
 		skb_shinfo(skb)->frag_list = NULL;
 
@@ -1280,7 +1076,7 @@
 			skb->pkt_type = HCI_ACLDATA_PKT;
 			hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT);
 		
-			DBG("%s frag %p len %d", hdev->name, skb, skb->len);
+			BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
 
 			__skb_queue_tail(&conn->data_q, skb);
 		} while (list);
@@ -1298,7 +1094,7 @@
 	struct hci_dev *hdev = conn->hdev;
 	hci_sco_hdr hs;
 
-	DBG("%s len %d", hdev->name, skb->len);
+	BT_DBG("%s len %d", hdev->name, skb->len);
 
 	if (skb->len > hdev->sco_mtu) {
 		kfree_skb(skb);
@@ -1315,544 +1111,136 @@
 	skb->pkt_type = HCI_SCODATA_PKT;
 	skb_queue_tail(&conn->data_q, skb);
 	hci_sched_tx(hdev);
-
 	return 0;
 }
 
-/* Handle HCI Event packets */
-
-/* Command Complete OGF LINK_CTL  */
-static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
-{
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	default:
-		DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Complete OGF LINK_POLICY  */
-static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
-{
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	default:
-		DBG("%s: Command complete: ogf LINK_POLICY ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Complete OGF HOST_CTL  */
-static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
-{
-	__u8 status, param;
-	void *sent;
-
-
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	case OCF_RESET:
-		status = *((__u8 *) skb->data);
-
-		hci_req_complete(hdev, status);
-		break;
-
-	case OCF_SET_EVENT_FLT:
-		status = *((__u8 *) skb->data);
-
-		if (status) {
-			DBG("%s SET_EVENT_FLT failed %d", hdev->name, status);
-		} else {
-			DBG("%s SET_EVENT_FLT succeseful", hdev->name);
-		}
-		break;
-
-	case OCF_WRITE_AUTH_ENABLE:
-		if (!(sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE)))
-			break;
-
-		status = *((__u8 *) skb->data);
-		param  = *((__u8 *) sent);
-
-		if (!status) {
-			if (param == AUTH_ENABLED)
-				hdev->flags |= HCI_AUTH;
-			else
-				hdev->flags &= ~HCI_AUTH;
-		}
-		hci_req_complete(hdev, status);
-		break;
-
-	case OCF_WRITE_CA_TIMEOUT:
-		status = *((__u8 *) skb->data);
-
-		if (status) {
-			DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status);
-		} else {
-			DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name);
-		}
-		break;
-
-	case OCF_WRITE_PG_TIMEOUT:
-		status = *((__u8 *) skb->data);
-
-		if (status) {
-			DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status);
-		} else {
-			DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name);
-		}
-		break;
-
-	case OCF_WRITE_SCAN_ENABLE:
-		if (!(sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE)))
-			break;
-		status = *((__u8 *) skb->data);
-		param  = *((__u8 *) sent);
-
-		DBG("param 0x%x", param);
-
-		if (!status) {
-			switch (param) {
-			case IS_ENA_PS_ENA:
-				hdev->flags |=  HCI_PSCAN | HCI_ISCAN;
-				break;
-
-			case IS_ENA_PS_DIS:
-				hdev->flags &= ~HCI_PSCAN;
-				hdev->flags |=  HCI_ISCAN;
-				break;
+/* ---- HCI TX task (outgoing data) ---- */
 
-			case IS_DIS_PS_ENA:
-				hdev->flags &= ~HCI_ISCAN;
-				hdev->flags |=  HCI_PSCAN;
-				break;
-
-			default:
-				hdev->flags &= ~(HCI_ISCAN | HCI_PSCAN);
-				break;
-			};
-		}
-		hci_req_complete(hdev, status);
-		break;
-
-	default:
-		DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Complete OGF INFO_PARAM  */
-static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+/* HCI Connection scheduler */
+static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
 {
-	read_local_features_rp *lf;
-	read_buffer_size_rp *bs;
-	read_bd_addr_rp *ba;
-
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	case OCF_READ_LOCAL_FEATURES:
-		lf = (read_local_features_rp *) skb->data;
-
-		if (lf->status) {
-			DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
-			break;
-		}
-
-		memcpy(hdev->features, lf->features, sizeof(hdev->features));
-
-		/* Adjust default settings according to features 
-		 * supported by device. */
-		if (hdev->features[0] & LMP_3SLOT)
-			hdev->pkt_type |= (HCI_DM3 | HCI_DH3);
-
-		if (hdev->features[0] & LMP_5SLOT)
-			hdev->pkt_type |= (HCI_DM5 | HCI_DH5);
-
-		DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]);
-
-		break;
-
-	case OCF_READ_BUFFER_SIZE:
-		bs = (read_buffer_size_rp *) skb->data;
-
-		if (bs->status) {
-			DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status);
-			break;
-		}
-
-		hdev->acl_mtu = __le16_to_cpu(bs->acl_mtu);
-		hdev->sco_mtu = bs->sco_mtu;
-		hdev->acl_max = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt);
-		hdev->sco_max = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt);
-
-		DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name,
-		    hdev->acl_mtu, hdev->sco_mtu, hdev->acl_max, hdev->sco_max);
+	struct conn_hash *h = &hdev->conn_hash;
+	struct hci_conn  *conn = NULL;
+	int num = 0, min = ~0;
+        struct list_head *p;
 
-		break;
+	/* We don't have to lock device here. Connections are always 
+	 * added and removed with TX task disabled. */
+	list_for_each(p, &h->list) {
+		struct hci_conn *c;
+		c = list_entry(p, struct hci_conn, list);
 
-	case OCF_READ_BD_ADDR:
-		ba = (read_bd_addr_rp *) skb->data;
+		if (c->type != type || c->state != BT_CONNECTED
+				|| skb_queue_empty(&c->data_q))
+			continue;
+		num++;
 
-		if (!ba->status) {
-			bacpy(&hdev->bdaddr, &ba->bdaddr);
-		} else {
-			DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status);
+		if (c->sent < min) {
+			min  = c->sent;
+			conn = c;
 		}
+	}
 
-		hci_req_complete(hdev, ba->status);
-		break;
+	if (conn) {
+		int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
+		int q = cnt / num;
+		*quote = q ? q : 1;
+	} else
+		*quote = 0;
 
-	default:
-		DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf);
-		break;
-	};
+	BT_DBG("conn %p quote %d", conn, *quote);
+	return conn;
 }
 
-/* Command Status OGF LINK_CTL  */
-static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
+static inline void hci_acl_tx_to(struct hci_dev *hdev)
 {
-	struct hci_proto * hp;
-
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	case OCF_CREATE_CONN:
-		if (status) {
-			create_conn_cp *cc = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN);
-
-			if (!cc)
-				break;
-
-			DBG("%s Create connection error: status 0x%x %s", hdev->name,
-			    status, batostr(&cc->bdaddr));
+	struct conn_hash *h = &hdev->conn_hash;
+	struct list_head *p;
+	struct hci_conn  *c;
 
-			/* Notify upper protocols */
-			if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_cfm) {
-				tasklet_disable(&hdev->tx_task);
-				hp->connect_cfm(hdev, &cc->bdaddr, status, NULL);
-				tasklet_enable(&hdev->tx_task);
-			}
-		}
-		break;
+	BT_ERR("%s ACL tx timeout", hdev->name);
 
-	case OCF_INQUIRY:
-		if (status) {
-			DBG("%s Inquiry error: status 0x%x", hdev->name, status);
-			hci_req_complete(hdev, status);
+	/* Kill stalled connections */
+	list_for_each(p, &h->list) {
+		c = list_entry(p, struct hci_conn, list);
+		if (c->type == ACL_LINK && c->sent) {
+			BT_ERR("%s killing stalled ACL connection %s",
+				hdev->name, batostr(&c->dst));
+			hci_acl_disconn(c, 0x13);
 		}
-		break;
-
-	default:
-		DBG("%s Command status: ogf LINK_CTL ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Status OGF LINK_POLICY */
-static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status)
-{
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	default:
-		DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Status OGF HOST_CTL */
-static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
-{
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	default:
-		DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Status OGF INFO_PARAM  */
-static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status)
-{
-	DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	default:
-		DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Inquiry Complete */
-static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
-{
-	__u8 status = *((__u8 *) skb->data);
-
-	DBG("%s status %d", hdev->name, status);
-
-	hci_req_complete(hdev, status);
+	}
 }
 
-/* Inquiry Result */
-static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
+static inline void hci_sched_acl(struct hci_dev *hdev)
 {
-	inquiry_info *info = (inquiry_info *) (skb->data + 1);
-	int num_rsp = *((__u8 *) skb->data);
-
-	DBG("%s num_rsp %d", hdev->name, num_rsp);
+	struct hci_conn *conn;
+	struct sk_buff *skb;
+	int quote;
 
-	for (; num_rsp; num_rsp--)
-		inquiry_cache_update(&hdev->inq_cache, info++);
-}
+	BT_DBG("%s", hdev->name);
 
-/* Connect Request */
-static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
-{
-	evt_conn_request *cr = (evt_conn_request *) skb->data;
-	struct hci_proto *hp;
-	accept_conn_req_cp ac;
-	int accept = 0;
+	/* ACL tx timeout must be longer than maximum
+	 * link supervision timeout (40.9 seconds) */
+	if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45))
+		hci_acl_tx_to(hdev);
 
-	DBG("%s Connection request: %s type 0x%x", hdev->name, batostr(&cr->bdaddr), cr->link_type);
+	while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
+		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+			BT_DBG("skb %p len %d", skb, skb->len);
+			hci_send_frame(skb);
+			hdev->acl_last_tx = jiffies;
 
-	/* Notify upper protocols */
-	if (cr->link_type == ACL_LINK) {
-		/* ACL link notify L2CAP */
-		if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_ind) {
-			tasklet_disable(&hdev->tx_task);
-			accept = hp->connect_ind(hdev, &cr->bdaddr);
-			tasklet_enable(&hdev->tx_task);
+			hdev->acl_cnt--;
+			conn->sent++;
 		}
-	} else {
-		/* SCO link (no notification) */
-		/* FIXME: Should be accept it here or let the requester (app) accept it ? */
-		accept = 1;
-	}
-
-	if (accept) {
-		/* Connection accepted by upper layer */
-		bacpy(&ac.bdaddr, &cr->bdaddr);
-		ac.role = 0x01; /* Remain slave */
-		hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, ACCEPT_CONN_REQ_CP_SIZE, &ac);
-	} else {
-		/* Connection rejected by upper layer */
-		/* FIXME: 
-		 * Should we use HCI reject here ?
-		 */
-		return;
 	}
 }
 
-/* Connect Complete */
-static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+/* Schedule SCO */
+static inline void hci_sched_sco(struct hci_dev *hdev)
 {
-	evt_conn_complete *cc = (evt_conn_complete *) skb->data;
-	struct hci_conn *conn = NULL;
-	struct hci_proto *hp;
-
-	DBG("%s", hdev->name);
-
-	tasklet_disable(&hdev->tx_task);
-
-	if (!cc->status)
- 		conn = hci_conn_add(hdev, __le16_to_cpu(cc->handle), cc->link_type, &cc->bdaddr);
+	struct hci_conn *conn;
+	struct sk_buff *skb;
+	int quote;
 
-	/* Notify upper protocols */
-	if (cc->link_type == ACL_LINK) {
-		/* ACL link notify L2CAP layer */
-		if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_cfm)
-			hp->connect_cfm(hdev, &cc->bdaddr, cc->status, conn);
-	} else {
-		/* SCO link (no notification) */
-	}
+	BT_DBG("%s", hdev->name);
 
-	tasklet_enable(&hdev->tx_task);
-}
+	while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
+		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+			BT_DBG("skb %p len %d", skb, skb->len);
+			hci_send_frame(skb);
 
-/* Disconnect Complete */
-static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
-{
-	evt_disconn_complete *dc = (evt_disconn_complete *) skb->data;
-	struct hci_conn *conn = NULL;
-	struct hci_proto *hp;
-	__u16 handle = __le16_to_cpu(dc->handle);
-
-	DBG("%s", hdev->name);
-
-	if (!dc->status && (conn = conn_hash_lookup(&hdev->conn_hash, handle))) {
-		tasklet_disable(&hdev->tx_task);
-
-		/* Notify upper protocols */
-		if (conn->type == ACL_LINK) {
-			/* ACL link notify L2CAP layer */
-			if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->disconn_ind)
-				hp->disconn_ind(conn, dc->reason);
-		} else {
-			/* SCO link (no notification) */
+			conn->sent++;
+			if (conn->sent == ~0)
+				conn->sent = 0;
 		}
-
-		hci_conn_del(hdev, conn);
-
-		tasklet_enable(&hdev->tx_task);
 	}
 }
 
-/* Number of completed packets */
-static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
+static void hci_tx_task(unsigned long arg)
 {
-	evt_num_comp_pkts *nc = (evt_num_comp_pkts *) skb->data;
-	__u16 *ptr;
-	int i;
-
-	skb_pull(skb, EVT_NUM_COMP_PKTS_SIZE);
-
-	DBG("%s num_hndl %d", hdev->name, nc->num_hndl);
+	struct hci_dev *hdev = (struct hci_dev *) arg;
+	struct sk_buff *skb;
 
-	if (skb->len < nc->num_hndl * 4) {
-		DBG("%s bad parameters", hdev->name);
-		return;
-	}
+	read_lock(&hci_task_lock);
 
-	tasklet_disable(&hdev->tx_task);
+	BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
 
-	for (i = 0, ptr = (__u16 *) skb->data; i < nc->num_hndl; i++) {
-		struct hci_conn *conn;
-		__u16 handle, count;
+	/* Schedule queues and send stuff to HCI driver */
 
-		handle = __le16_to_cpu(get_unaligned(ptr++));
-		count  = __le16_to_cpu(get_unaligned(ptr++));
+	hci_sched_acl(hdev);
 
-		hdev->acl_cnt += count;
+	hci_sched_sco(hdev);
 
-		if ((conn = conn_hash_lookup(&hdev->conn_hash, handle)))
-			conn->sent -= count;
-	}
+	/* Send next queued raw (unknown type) packet */
+	while ((skb = skb_dequeue(&hdev->raw_q)))
+		hci_send_frame(skb);
 
-	tasklet_enable(&hdev->tx_task);
-	
-	hci_sched_tx(hdev);
+	read_unlock(&hci_task_lock);
 }
 
-static inline void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
-{
-	hci_event_hdr *he = (hci_event_hdr *) skb->data;
-	evt_cmd_status *cs;
-	evt_cmd_complete *ec;
-	__u16 opcode, ocf, ogf;
-
-	skb_pull(skb, HCI_EVENT_HDR_SIZE);
-
-	DBG("%s evt 0x%x", hdev->name, he->evt);
-
-	switch (he->evt) {
-	case EVT_NUM_COMP_PKTS:
-		hci_num_comp_pkts_evt(hdev, skb);
-		break;
-
-	case EVT_INQUIRY_COMPLETE:
-		hci_inquiry_complete_evt(hdev, skb);
-		break;
 
-	case EVT_INQUIRY_RESULT:
-		hci_inquiry_result_evt(hdev, skb);
-		break;
-
-	case EVT_CONN_REQUEST:
-		hci_conn_request_evt(hdev, skb);
-		break;
-
-	case EVT_CONN_COMPLETE:
-		hci_conn_complete_evt(hdev, skb);
-		break;
-
-	case EVT_DISCONN_COMPLETE:
-		hci_disconn_complete_evt(hdev, skb);
-		break;
-
-	case EVT_CMD_STATUS:
-		cs = (evt_cmd_status *) skb->data;
-		skb_pull(skb, EVT_CMD_STATUS_SIZE);
-				
-		opcode = __le16_to_cpu(cs->opcode);
-		ogf = cmd_opcode_ogf(opcode);
-		ocf = cmd_opcode_ocf(opcode);
-
-		switch (ogf) {
-		case OGF_INFO_PARAM:
-			hci_cs_info_param(hdev, ocf, cs->status);
-			break;
-
-		case OGF_HOST_CTL:
-			hci_cs_host_ctl(hdev, ocf, cs->status);
-			break;
-
-		case OGF_LINK_CTL:
-			hci_cs_link_ctl(hdev, ocf, cs->status);
-			break;
-
-		case OGF_LINK_POLICY:
-			hci_cs_link_policy(hdev, ocf, cs->status);
-			break;
-
-		default:
-			DBG("%s Command Status OGF %x", hdev->name, ogf);
-			break;
-		};
-
-		if (cs->ncmd) {
-			atomic_set(&hdev->cmd_cnt, 1);
-			if (!skb_queue_empty(&hdev->cmd_q))
-				hci_sched_cmd(hdev);
-		}
-		break;
-
-	case EVT_CMD_COMPLETE:
-		ec = (evt_cmd_complete *) skb->data;
-		skb_pull(skb, EVT_CMD_COMPLETE_SIZE);
-
-		opcode = __le16_to_cpu(ec->opcode);
-		ogf = cmd_opcode_ogf(opcode);
-		ocf = cmd_opcode_ocf(opcode);
-
-		switch (ogf) {
-		case OGF_INFO_PARAM:
-			hci_cc_info_param(hdev, ocf, skb);
-			break;
-
-		case OGF_HOST_CTL:
-			hci_cc_host_ctl(hdev, ocf, skb);
-			break;
-
-		case OGF_LINK_CTL:
-			hci_cc_link_ctl(hdev, ocf, skb);
-			break;
-
-		case OGF_LINK_POLICY:
-			hci_cc_link_policy(hdev, ocf, skb);
-			break;
-
-		default:
-			DBG("%s Command Completed OGF %x", hdev->name, ogf);
-			break;
-		};
-
-		if (ec->ncmd) {
-			atomic_set(&hdev->cmd_cnt, 1);
-			if (!skb_queue_empty(&hdev->cmd_q))
-				hci_sched_cmd(hdev);
-		}
-		break;
-	};
-
-	kfree_skb(skb);
-	hdev->stat.evt_rx++;
-}
+/* ----- HCI RX task (incomming data proccessing) ----- */
 
 /* ACL data packet */
 static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1867,51 +1255,86 @@
 	flags  = acl_flags(handle);
 	handle = acl_handle(handle);
 
-	DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags);
+	BT_DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags);
+
+	hdev->stat.acl_rx++;
 
-	if ((conn = conn_hash_lookup(&hdev->conn_hash, handle))) {
+	hci_dev_lock(hdev);
+	conn = conn_hash_lookup_handle(hdev, handle);
+	hci_dev_unlock(hdev);
+	
+	if (conn) {
 		register struct hci_proto *hp;
 
 		/* Send to upper protocol */
-		if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->recv_acldata) {
+		if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) {
 			hp->recv_acldata(conn, skb, flags);
-			goto sent;
+			return;
 		}
 	} else {
-		ERR("%s ACL packet for unknown connection handle %d", hdev->name, handle);
+		BT_ERR("%s ACL packet for unknown connection handle %d", 
+			hdev->name, handle);
 	}
 
 	kfree_skb(skb);
-sent:
-	hdev->stat.acl_rx++;
 }
 
 /* SCO data packet */
 static inline void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
 {
-	DBG("%s len %d", hdev->name, skb->len);
+	hci_sco_hdr *sh = (void *) skb->data;
+	struct hci_conn *conn;
+	__u16 handle;
+
+	skb_pull(skb, HCI_SCO_HDR_SIZE);
+
+	handle = __le16_to_cpu(sh->handle);
+
+	BT_DBG("%s len %d handle 0x%x", hdev->name, skb->len, handle);
 
-	kfree_skb(skb);
 	hdev->stat.sco_rx++;
+
+	hci_dev_lock(hdev);
+	conn = conn_hash_lookup_handle(hdev, handle);
+	hci_dev_unlock(hdev);
+	
+	if (conn) {
+		register struct hci_proto *hp;
+
+		/* Send to upper protocol */
+		if ((hp = hci_proto[HCI_PROTO_SCO]) && hp->recv_scodata) {
+			hp->recv_scodata(conn, skb);
+			return;
+		}
+	} else {
+		BT_ERR("%s SCO packet for unknown connection handle %d", 
+			hdev->name, handle);
+	}
+
+	kfree_skb(skb);
 }
 
-/* ----- HCI tasks ----- */
 void hci_rx_task(unsigned long arg)
 {
 	struct hci_dev *hdev = (struct hci_dev *) arg;
 	struct sk_buff *skb;
 
-	DBG("%s", hdev->name);
+	BT_DBG("%s", hdev->name);
 
 	read_lock(&hci_task_lock);
 
 	while ((skb = skb_dequeue(&hdev->rx_q))) {
-		if (hdev->flags & HCI_SOCK) {
+		if (atomic_read(&hdev->promisc)) {
 			/* Send copy to the sockets */
 			hci_send_to_sock(hdev, skb);
 		}
 
-		if (hdev->flags & HCI_INIT) {
+		if (test_bit(HCI_RAW, &hdev->flags)) {
+			kfree_skb(skb);
+			continue;
+		}
+
+		if (test_bit(HCI_INIT, &hdev->flags)) {
 			/* Don't process data packets in this states. */
 			switch (skb->pkt_type) {
 			case HCI_ACLDATA_PKT:
@@ -1921,64 +1344,43 @@
 			};
 		}
 
-		if (hdev->flags & HCI_NORMAL) {
-			/* Process frame */
-			switch (skb->pkt_type) {
-			case HCI_EVENT_PKT:
-				hci_event_packet(hdev, skb);
-				break;
+		/* Process frame */
+		switch (skb->pkt_type) {
+		case HCI_EVENT_PKT:
+			hci_event_packet(hdev, skb);
+			break;
 
-			case HCI_ACLDATA_PKT:
-				DBG("%s ACL data packet", hdev->name);
-				hci_acldata_packet(hdev, skb);
-				break;
+		case HCI_ACLDATA_PKT:
+			BT_DBG("%s ACL data packet", hdev->name);
+			hci_acldata_packet(hdev, skb);
+			break;
 
-			case HCI_SCODATA_PKT:
-				DBG("%s SCO data packet", hdev->name);
-				hci_scodata_packet(hdev, skb);
-				break;
+		case HCI_SCODATA_PKT:
+			BT_DBG("%s SCO data packet", hdev->name);
+			hci_scodata_packet(hdev, skb);
+			break;
 
-			default:
-				kfree_skb(skb);
-				break;
-			};
-		} else {
+		default:
 			kfree_skb(skb);
+			break;
 		}
 	}
 
 	read_unlock(&hci_task_lock);
 }
 
-static void hci_tx_task(unsigned long arg)
-{
-	struct hci_dev *hdev = (struct hci_dev *) arg;
-	struct sk_buff *skb;
-
-	read_lock(&hci_task_lock);
-
-	DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
-
-	/* Schedule queues and send stuff to HCI driver */
-
-	hci_sched_acl(hdev);
-
-	hci_sched_sco(hdev);
-
-	/* Send next queued raw (unknown type) packet */
-	while ((skb = skb_dequeue(&hdev->raw_q)))
-		hci_send_frame(skb);
-
-	read_unlock(&hci_task_lock);
-}
-
 static void hci_cmd_task(unsigned long arg)
 {
 	struct hci_dev *hdev = (struct hci_dev *) arg;
 	struct sk_buff *skb;
 
-	DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
+	BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
 
+	if (!atomic_read(&hdev->cmd_cnt) && (jiffies - hdev->cmd_last_tx) > HZ) {
+		BT_ERR("%s command tx timeout", hdev->name);
+		atomic_set(&hdev->cmd_cnt, 1);
+	}
+	
 	/* Send queued commands */
 	if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) {
 		if (hdev->sent_cmd)
@@ -1987,6 +1389,7 @@
 		if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) {
 			atomic_dec(&hdev->cmd_cnt);
 			hci_send_frame(skb);
+			hdev->cmd_last_tx = jiffies;
 		} else {
 			skb_queue_head(&hdev->cmd_q, skb);
 			hci_sched_cmd(hdev);
@@ -1994,33 +1397,10 @@
 	}
 }
 
-/* Receive frame from HCI drivers */
-int hci_recv_frame(struct sk_buff *skb)
-{
-	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-
-	if (!hdev || !(hdev->flags & (HCI_UP | HCI_INIT))) {
-		kfree_skb(skb);
-		return -1;
-	}
-
-	DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
-
-	/* Incomming skb */
-	bluez_cb(skb)->incomming = 1;
-
-	/* Queue frame for rx task */
-	skb_queue_tail(&hdev->rx_q, skb);
-	hci_sched_rx(hdev);
-
-	return 0;
-}
+/* ---- Initialization ---- */
 
 int hci_core_init(void)
 {
-	/* Init locks */
-	spin_lock_init(&hdev_list_lock);
-
 	return 0;
 }
 
@@ -2028,5 +1408,3 @@
 {
 	return 0;
 }
-
-MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/net/bluetooth/hci_event.c linux-2.4.18-mh15/net/bluetooth/hci_event.c
--- linux-2.4.18/net/bluetooth/hci_event.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/hci_event.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,910 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * HCI Events.
+ *
+ * $Id: hci_event.c,v 1.4 2002/07/27 18:14:38 maxk Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef HCI_CORE_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+/* Handle HCI Event packets */
+
+/* Command Complete OGF LINK_CTL  */
+static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+{
+	__u8 status;
+
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	case OCF_INQUIRY_CANCEL:
+		status = *((__u8 *) skb->data);
+
+		if (status) {
+			BT_DBG("%s Inquiry cancel error: status 0x%x", hdev->name, status);
+		} else {
+			clear_bit(HCI_INQUIRY, &hdev->flags);
+			hci_req_complete(hdev, status);
+		}
+		break;
+
+	default:
+		BT_DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Complete OGF LINK_POLICY  */
+static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+{
+	struct hci_conn *conn;
+	role_discovery_rp *rd;
+
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	case OCF_ROLE_DISCOVERY: 
+		rd = (void *) skb->data;
+
+		if (rd->status)
+			break;
+		
+		hci_dev_lock(hdev);
+	
+		conn = conn_hash_lookup_handle(hdev, __le16_to_cpu(rd->handle));
+		if (conn) {
+			if (rd->role)
+				conn->link_mode &= ~HCI_LM_MASTER;
+			else
+				conn->link_mode |= HCI_LM_MASTER;
+		}
+			
+		hci_dev_unlock(hdev);
+		break;
+
+	default:
+		BT_DBG("%s: Command complete: ogf LINK_POLICY ocf %x", 
+				hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Complete OGF HOST_CTL  */
+static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+{
+	__u8 status, param;
+	void *sent;
+
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	case OCF_RESET:
+		status = *((__u8 *) skb->data);
+		hci_req_complete(hdev, status);
+		break;
+
+	case OCF_SET_EVENT_FLT:
+		status = *((__u8 *) skb->data);
+		if (status) {
+			BT_DBG("%s SET_EVENT_FLT failed %d", hdev->name, status);
+		} else {
+			BT_DBG("%s SET_EVENT_FLT succeseful", hdev->name);
+		}
+		break;
+
+	case OCF_WRITE_AUTH_ENABLE:
+		sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE);
+		if (!sent)
+			break;
+
+		status = *((__u8 *) skb->data);
+		param  = *((__u8 *) sent);
+
+		if (!status) {
+			if (param == AUTH_ENABLED)
+				set_bit(HCI_AUTH, &hdev->flags);
+			else
+				clear_bit(HCI_AUTH, &hdev->flags);
+		}
+		hci_req_complete(hdev, status);
+		break;
+
+	case OCF_WRITE_ENCRYPT_MODE:
+		sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE);
+		if (!sent)
+			break;
+
+		status = *((__u8 *) skb->data);
+		param  = *((__u8 *) sent);
+
+		if (!status) {
+			if (param)
+				set_bit(HCI_ENCRYPT, &hdev->flags);
+			else
+				clear_bit(HCI_ENCRYPT, &hdev->flags);
+		}
+		hci_req_complete(hdev, status);
+		break;
+
+	case OCF_WRITE_CA_TIMEOUT:
+		status = *((__u8 *) skb->data);
+		if (status) {
+			BT_DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status);
+		} else {
+			BT_DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name);
+		}
+		break;
+
+	case OCF_WRITE_PG_TIMEOUT:
+		status = *((__u8 *) skb->data);
+		if (status) {
+			BT_DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status);
+		} else {
+			BT_DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name);
+		}
+		break;
+
+	case OCF_WRITE_SCAN_ENABLE:
+		sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE);
+		if (!sent)
+			break;
+		status = *((__u8 *) skb->data);
+		param  = *((__u8 *) sent);
+
+		BT_DBG("param 0x%x", param);
+
+		if (!status) {
+			clear_bit(HCI_PSCAN, &hdev->flags);
+			clear_bit(HCI_ISCAN, &hdev->flags);
+			if (param & SCAN_INQUIRY) 
+				set_bit(HCI_ISCAN, &hdev->flags);
+
+			if (param & SCAN_PAGE) 
+				set_bit(HCI_PSCAN, &hdev->flags);
+		}
+		hci_req_complete(hdev, status);
+		break;
+
+	case OCF_HOST_BUFFER_SIZE:
+		status = *((__u8 *) skb->data);
+		if (status) {
+			BT_DBG("%s OCF_BUFFER_SIZE failed %d", hdev->name, status);
+			hci_req_complete(hdev, status);
+		}
+		break;
+
+	default:
+		BT_DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Complete OGF INFO_PARAM  */
+static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+{
+	read_local_features_rp *lf;
+	read_buffer_size_rp *bs;
+	read_bd_addr_rp *ba;
+
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	case OCF_READ_LOCAL_FEATURES:
+		lf = (read_local_features_rp *) skb->data;
+
+		if (lf->status) {
+			BT_DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
+			break;
+		}
+
+		memcpy(hdev->features, lf->features, sizeof(hdev->features));
+
+		/* Adjust default settings according to features 
+		 * supported by device. */
+		if (hdev->features[0] & LMP_3SLOT)
+			hdev->pkt_type |= (HCI_DM3 | HCI_DH3);
+
+		if (hdev->features[0] & LMP_5SLOT)
+			hdev->pkt_type |= (HCI_DM5 | HCI_DH5);
+
+		if (hdev->features[1] & LMP_HV2)
+			hdev->pkt_type |= (HCI_HV2);
+
+		if (hdev->features[1] & LMP_HV3)
+			hdev->pkt_type |= (HCI_HV3);
+
+		BT_DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]);
+
+		break;
+
+	case OCF_READ_BUFFER_SIZE:
+		bs = (read_buffer_size_rp *) skb->data;
+
+		if (bs->status) {
+			BT_DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status);
+			hci_req_complete(hdev, bs->status);
+			break;
+		}
+
+		hdev->acl_mtu  = __le16_to_cpu(bs->acl_mtu);
+		hdev->sco_mtu  = bs->sco_mtu ? bs->sco_mtu : 64;
+		hdev->acl_pkts = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt);
+		hdev->sco_pkts = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt);
+
+		BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name,
+		    hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts);
+		break;
+
+	case OCF_READ_BD_ADDR:
+		ba = (read_bd_addr_rp *) skb->data;
+
+		if (!ba->status) {
+			bacpy(&hdev->bdaddr, &ba->bdaddr);
+		} else {
+			BT_DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status);
+		}
+
+		hci_req_complete(hdev, ba->status);
+		break;
+
+	default:
+		BT_DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Status OGF LINK_CTL  */
+static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
+{
+	struct hci_conn *conn;
+	create_conn_cp *cc = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN);
+
+	if (!cc)
+		return;
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_ba(hdev, ACL_LINK, &cc->bdaddr);
+
+	BT_DBG("%s status 0x%x bdaddr %s conn %p", hdev->name, 
+			status, batostr(&cc->bdaddr), conn);
+
+	if (status) {
+		if (conn && conn->state == BT_CONNECT) {
+			conn->state = BT_CLOSED;
+			hci_proto_connect_cfm(conn, status);
+			hci_conn_del(conn);
+		}
+	} else {
+		if (!conn) {
+			conn = hci_conn_add(hdev, ACL_LINK, &cc->bdaddr);
+			if (conn) {
+				conn->out = 1;
+				conn->link_mode |= HCI_LM_MASTER;
+			} else
+				BT_ERR("No memmory for new connection");
+		}
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
+{
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	case OCF_CREATE_CONN:
+		hci_cs_create_conn(hdev, status);
+		break;
+
+	case OCF_ADD_SCO:
+		if (status) {
+			struct hci_conn *acl, *sco;
+			add_sco_cp *cp = hci_sent_cmd_data(hdev, 
+						OGF_LINK_CTL, OCF_ADD_SCO);
+			__u16 handle;
+
+			if (!cp)
+				break;
+
+			handle = __le16_to_cpu(cp->handle);
+
+			BT_DBG("%s Add SCO error: handle %d status 0x%x", hdev->name, handle, status);
+
+			hci_dev_lock(hdev);
+	
+			acl = conn_hash_lookup_handle(hdev, handle);
+			if (acl && (sco = acl->link)) {
+				sco->state = BT_CLOSED;
+				hci_proto_connect_cfm(sco, status);
+				hci_conn_del(sco);
+			}
+
+			hci_dev_unlock(hdev);
+		}
+		break;
+
+	case OCF_INQUIRY:
+		if (status) {
+			BT_DBG("%s Inquiry error: status 0x%x", hdev->name, status);
+			hci_req_complete(hdev, status);
+		} else {
+			set_bit(HCI_INQUIRY, &hdev->flags);
+		}
+		break;
+
+	default:
+		BT_DBG("%s Command status: ogf LINK_CTL ocf %x status %d", 
+			hdev->name, ocf, status);
+		break;
+	};
+}
+
+/* Command Status OGF LINK_POLICY */
+static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status)
+{
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	default:
+		BT_DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Status OGF HOST_CTL */
+static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
+{
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	default:
+		BT_DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Status OGF INFO_PARAM  */
+static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status)
+{
+	BT_DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	default:
+		BT_DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Inquiry Complete */
+static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	__u8 status = *((__u8 *) skb->data);
+
+	BT_DBG("%s status %d", hdev->name, status);
+
+	clear_bit(HCI_INQUIRY, &hdev->flags);
+	hci_req_complete(hdev, status);
+}
+
+/* Inquiry Result */
+static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	inquiry_info *info = (inquiry_info *) (skb->data + 1);
+	int num_rsp = *((__u8 *) skb->data);
+
+	BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
+
+	hci_dev_lock(hdev);
+	for (; num_rsp; num_rsp--)
+		inquiry_cache_update(hdev, info++);
+	hci_dev_unlock(hdev);
+}
+
+/* Inquiry Result With RSSI */
+static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	inquiry_info_with_rssi *info = (inquiry_info_with_rssi *) (skb->data + 1);
+	int num_rsp = *((__u8 *) skb->data);
+
+	BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
+
+	hci_dev_lock(hdev);
+	for (; num_rsp; num_rsp--) {
+		inquiry_info tmp;
+		bacpy(&tmp.bdaddr, &info->bdaddr);
+		tmp.pscan_rep_mode    = info->pscan_rep_mode;
+		tmp.pscan_period_mode = info->pscan_period_mode;
+		tmp.pscan_mode        = 0x00;
+		memcpy(tmp.dev_class, &info->dev_class, 3);
+		tmp.clock_offset      = info->clock_offset;
+		info++;
+		inquiry_cache_update(hdev, &tmp);
+	}
+	hci_dev_unlock(hdev);
+}
+
+/* Connect Request */
+static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_conn_request *cr = (evt_conn_request *) skb->data;
+	int mask = hdev->link_mode;
+
+	BT_DBG("%s Connection request: %s type 0x%x", hdev->name,
+			batostr(&cr->bdaddr), cr->link_type);
+
+	mask |= hci_proto_connect_ind(hdev, &cr->bdaddr, cr->link_type);
+
+	if (mask & HCI_LM_ACCEPT) {
+		/* Connection accepted */
+		struct hci_conn *conn;
+		accept_conn_req_cp ac;
+
+		hci_dev_lock(hdev);
+		conn = conn_hash_lookup_ba(hdev, cr->link_type, &cr->bdaddr);
+		if (!conn) {
+			if (!(conn = hci_conn_add(hdev, cr->link_type, &cr->bdaddr))) {
+				BT_ERR("No memmory for new connection");
+				hci_dev_unlock(hdev);
+				return;
+			}
+		}
+		conn->state = BT_CONNECT;
+		hci_dev_unlock(hdev);
+
+		bacpy(&ac.bdaddr, &cr->bdaddr);
+	
+		if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
+			ac.role = 0x00; /* Become master */
+		else
+			ac.role = 0x01; /* Remain slave */
+
+		hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, 
+				ACCEPT_CONN_REQ_CP_SIZE, &ac);
+	} else {
+		/* Connection rejected */
+		reject_conn_req_cp rc;
+
+		bacpy(&rc.bdaddr, &cr->bdaddr);
+		rc.reason = 0x0f;
+		hci_send_cmd(hdev, OGF_LINK_CTL, OCF_REJECT_CONN_REQ,
+				REJECT_CONN_REQ_CP_SIZE, &rc);
+	}
+}
+
+/* Connect Complete */
+static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_conn_complete *cc = (evt_conn_complete *) skb->data;
+	struct hci_conn *conn = NULL;
+
+	BT_DBG("%s", hdev->name);
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_ba(hdev, cc->link_type, &cc->bdaddr);
+	if (!conn) {
+		hci_dev_unlock(hdev);
+		return;
+	}
+
+	if (!cc->status) {
+		conn->handle = __le16_to_cpu(cc->handle);
+		conn->state  = BT_CONNECTED;
+
+		if (test_bit(HCI_AUTH, &hdev->flags))
+			conn->link_mode |= HCI_LM_AUTH;
+		
+		if (test_bit(HCI_ENCRYPT, &hdev->flags))
+			conn->link_mode |= HCI_LM_ENCRYPT;
+
+
+		/* Set link policy */
+		if (conn->type == ACL_LINK && hdev->link_policy) {
+			write_link_policy_cp lp;
+			lp.handle = cc->handle;
+			lp.policy = __cpu_to_le16(hdev->link_policy);
+			hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY,
+				WRITE_LINK_POLICY_CP_SIZE, &lp);
+		}
+
+		/* Set packet type for incomming connection */
+		if (!conn->out) {
+			change_conn_ptype_cp cp;
+			cp.handle = cc->handle;
+			cp.pkt_type = (conn->type == ACL_LINK) ? 
+				__cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK):
+				__cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
+
+			hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE,
+				CHANGE_CONN_PTYPE_CP_SIZE, &cp);
+		}
+	} else
+		conn->state = BT_CLOSED;
+
+	if (conn->type == ACL_LINK) {
+		struct hci_conn *sco = conn->link;
+		if (sco) {
+			if (!cc->status)
+				hci_add_sco(sco, conn->handle);
+			else {
+				hci_proto_connect_cfm(sco, cc->status);
+				hci_conn_del(sco);
+			}
+		}
+	}
+
+	hci_proto_connect_cfm(conn, cc->status);
+	if (cc->status)
+		hci_conn_del(conn);
+
+	hci_dev_unlock(hdev);
+}
+
+/* Disconnect Complete */
+static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_disconn_complete *dc = (evt_disconn_complete *) skb->data;
+	struct hci_conn *conn = NULL;
+	__u16 handle = __le16_to_cpu(dc->handle);
+
+	BT_DBG("%s status %d", hdev->name, dc->status);
+
+	if (dc->status)
+		return;
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_handle(hdev, handle);
+	if (conn) {
+		conn->state = BT_CLOSED;
+		hci_proto_disconn_ind(conn, dc->reason);
+		hci_conn_del(conn);
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+/* Number of completed packets */
+static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_num_comp_pkts *nc = (evt_num_comp_pkts *) skb->data;
+	__u16 *ptr;
+	int i;
+
+	skb_pull(skb, EVT_NUM_COMP_PKTS_SIZE);
+
+	BT_DBG("%s num_hndl %d", hdev->name, nc->num_hndl);
+
+	if (skb->len < nc->num_hndl * 4) {
+		BT_DBG("%s bad parameters", hdev->name);
+		return;
+	}
+
+	tasklet_disable(&hdev->tx_task);
+
+	for (i = 0, ptr = (__u16 *) skb->data; i < nc->num_hndl; i++) {
+		struct hci_conn *conn;
+		__u16  handle, count;
+
+		handle = __le16_to_cpu(get_unaligned(ptr++));
+		count  = __le16_to_cpu(get_unaligned(ptr++));
+
+		conn = conn_hash_lookup_handle(hdev, handle);
+		if (conn) {
+			conn->sent -= count;
+
+			if (conn->type == SCO_LINK) {
+				if ((hdev->sco_cnt += count) > hdev->sco_pkts)
+					hdev->sco_cnt = hdev->sco_pkts;
+			} else {
+				if ((hdev->acl_cnt += count) > hdev->acl_pkts)
+					hdev->acl_cnt = hdev->acl_pkts;
+			}
+		}
+	}
+	hci_sched_tx(hdev);
+
+	tasklet_enable(&hdev->tx_task);
+}
+
+/* Role Change */
+static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_role_change *rc = (evt_role_change *) skb->data;
+	struct hci_conn *conn = NULL;
+
+	BT_DBG("%s status %d", hdev->name, rc->status);
+
+	if (rc->status)
+		return;
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_ba(hdev, ACL_LINK, &rc->bdaddr);
+	if (conn) {
+		if (rc->role)
+			conn->link_mode &= ~HCI_LM_MASTER;
+		else 
+			conn->link_mode |= HCI_LM_MASTER;
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+/* Authentication Complete */
+static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_auth_complete *ac = (evt_auth_complete *) skb->data;
+	struct hci_conn *conn = NULL;
+	__u16 handle = __le16_to_cpu(ac->handle);
+
+	BT_DBG("%s status %d", hdev->name, ac->status);
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_handle(hdev, handle);
+	if (conn) {
+		if (!ac->status)
+			conn->link_mode |= HCI_LM_AUTH;
+		clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
+
+		hci_proto_auth_cfm(conn, ac->status);
+		
+		if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
+			if (!ac->status) {
+				set_conn_encrypt_cp ce;
+				ce.handle  = __cpu_to_le16(conn->handle);
+				ce.encrypt = 1;
+				hci_send_cmd(conn->hdev, OGF_LINK_CTL,
+						OCF_SET_CONN_ENCRYPT,
+						SET_CONN_ENCRYPT_CP_SIZE, &ce);
+			} else {
+				clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
+				hci_proto_encrypt_cfm(conn, ac->status);
+			}
+		}
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+/* Encryption Change */
+static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_encrypt_change *ec = (evt_encrypt_change *) skb->data;
+	struct hci_conn *conn = NULL;
+	__u16 handle = __le16_to_cpu(ec->handle);
+
+	BT_DBG("%s status %d", hdev->name, ec->status);
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_handle(hdev, handle);
+	if (conn) {
+		if (!ec->status) {
+		       	if (ec->encrypt)
+				conn->link_mode |= HCI_LM_ENCRYPT;
+			else
+				conn->link_mode &= ~HCI_LM_ENCRYPT;
+		}
+		clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
+		
+		hci_proto_encrypt_cfm(conn, ec->status);
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	hci_event_hdr *he = (hci_event_hdr *) skb->data;
+	evt_cmd_status *cs;
+	evt_cmd_complete *ec;
+	__u16 opcode, ocf, ogf;
+
+	skb_pull(skb, HCI_EVENT_HDR_SIZE);
+
+	BT_DBG("%s evt 0x%x", hdev->name, he->evt);
+
+	switch (he->evt) {
+	case EVT_NUM_COMP_PKTS:
+		hci_num_comp_pkts_evt(hdev, skb);
+		break;
+
+	case EVT_INQUIRY_COMPLETE:
+		hci_inquiry_complete_evt(hdev, skb);
+		break;
+
+	case EVT_INQUIRY_RESULT:
+		hci_inquiry_result_evt(hdev, skb);
+		break;
+
+	case EVT_INQUIRY_RESULT_WITH_RSSI:
+		hci_inquiry_result_with_rssi_evt(hdev, skb);
+		break;
+
+	case EVT_CONN_REQUEST:
+		hci_conn_request_evt(hdev, skb);
+		break;
+
+	case EVT_CONN_COMPLETE:
+		hci_conn_complete_evt(hdev, skb);
+		break;
+
+	case EVT_DISCONN_COMPLETE:
+		hci_disconn_complete_evt(hdev, skb);
+		break;
+
+	case EVT_ROLE_CHANGE:
+		hci_role_change_evt(hdev, skb);
+		break;
+
+	case EVT_AUTH_COMPLETE:
+		hci_auth_complete_evt(hdev, skb);
+		break;
+
+	case EVT_ENCRYPT_CHANGE:
+		hci_encrypt_change_evt(hdev, skb);
+		break;
+
+	case EVT_CMD_STATUS:
+		cs = (evt_cmd_status *) skb->data;
+		skb_pull(skb, EVT_CMD_STATUS_SIZE);
+				
+		opcode = __le16_to_cpu(cs->opcode);
+		ogf = cmd_opcode_ogf(opcode);
+		ocf = cmd_opcode_ocf(opcode);
+
+		switch (ogf) {
+		case OGF_INFO_PARAM:
+			hci_cs_info_param(hdev, ocf, cs->status);
+			break;
+
+		case OGF_HOST_CTL:
+			hci_cs_host_ctl(hdev, ocf, cs->status);
+			break;
+
+		case OGF_LINK_CTL:
+			hci_cs_link_ctl(hdev, ocf, cs->status);
+			break;
+
+		case OGF_LINK_POLICY:
+			hci_cs_link_policy(hdev, ocf, cs->status);
+			break;
+
+		default:
+			BT_DBG("%s Command Status OGF %x", hdev->name, ogf);
+			break;
+		};
+
+		if (cs->ncmd) {
+			atomic_set(&hdev->cmd_cnt, 1);
+			if (!skb_queue_empty(&hdev->cmd_q))
+				hci_sched_cmd(hdev);
+		}
+		break;
+
+	case EVT_CMD_COMPLETE:
+		ec = (evt_cmd_complete *) skb->data;
+		skb_pull(skb, EVT_CMD_COMPLETE_SIZE);
+
+		opcode = __le16_to_cpu(ec->opcode);
+		ogf = cmd_opcode_ogf(opcode);
+		ocf = cmd_opcode_ocf(opcode);
+
+		switch (ogf) {
+		case OGF_INFO_PARAM:
+			hci_cc_info_param(hdev, ocf, skb);
+			break;
+
+		case OGF_HOST_CTL:
+			hci_cc_host_ctl(hdev, ocf, skb);
+			break;
+
+		case OGF_LINK_CTL:
+			hci_cc_link_ctl(hdev, ocf, skb);
+			break;
+
+		case OGF_LINK_POLICY:
+			hci_cc_link_policy(hdev, ocf, skb);
+			break;
+
+		default:
+			BT_DBG("%s Command Completed OGF %x", hdev->name, ogf);
+			break;
+		};
+
+		if (ec->ncmd) {
+			atomic_set(&hdev->cmd_cnt, 1);
+			if (!skb_queue_empty(&hdev->cmd_q))
+				hci_sched_cmd(hdev);
+		}
+		break;
+	};
+
+	kfree_skb(skb);
+	hdev->stat.evt_rx++;
+}
+
+/* General internal stack event */
+void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
+{
+	hci_event_hdr *eh;
+	evt_stack_internal *si;
+	struct sk_buff *skb;
+	int size;
+	void *ptr;
+
+	size = HCI_EVENT_HDR_SIZE + EVT_STACK_INTERNAL_SIZE + dlen;
+	skb  = bluez_skb_alloc(size, GFP_ATOMIC);
+	if (!skb)
+		return;
+
+	ptr = skb_put(skb, size);
+
+	eh = ptr;
+       	eh->evt  = EVT_STACK_INTERNAL;
+	eh->plen = EVT_STACK_INTERNAL_SIZE + dlen;
+	ptr += HCI_EVENT_HDR_SIZE;
+
+	si = ptr;
+	si->type = type;
+	memcpy(si->data, data, dlen);
+	
+	skb->pkt_type = HCI_EVENT_PKT;
+	skb->dev = (void *) hdev;
+	hci_send_to_sock(hdev, skb);
+	kfree_skb(skb);
+}
diff -urN linux-2.4.18/net/bluetooth/hci_sock.c linux-2.4.18-mh15/net/bluetooth/hci_sock.c
--- linux-2.4.18/net/bluetooth/hci_sock.c	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/net/bluetooth/hci_sock.c	2004-08-01 16:26:23.000000000 +0200
@@ -25,7 +25,7 @@
 /*
  * BlueZ HCI socket layer.
  *
- * $Id: hci_sock.c,v 1.9 2001/08/05 06:02:16 maxk Exp $
+ * $Id: hci_sock.c,v 1.5 2002/07/22 20:32:54 maxk Exp $
  */
 
 #include <linux/config.h>
@@ -49,45 +49,54 @@
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
 #include <net/bluetooth/hci_core.h>
 
 #ifndef HCI_SOCK_DEBUG
-#undef  DBG
-#define DBG( A... )
+#undef  BT_DBG
+#define BT_DBG( A... )
 #endif
 
-/* HCI socket interface */
+/* ----- HCI socket interface ----- */
+
+/* Security filter */
+static struct hci_sec_filter hci_sec_filter = {
+	/* Packet types */
+	0x10,
+	/* Events */
+	{ 0x1000d9fe, 0x0000300c },
+	/* Commands */
+	{
+		{ 0x0 },
+		/* OGF_LINK_CTL */
+		{ 0xbe000006, 0x00000001, 0x0000, 0x00 },
+		/* OGF_LINK_POLICY */
+		{ 0x00005200, 0x00000000, 0x0000, 0x00 },
+		/* OGF_HOST_CTL */
+		{ 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
+		/* OGF_INFO_PARAM */
+		{ 0x000002be, 0x00000000, 0x0000, 0x00 },
+		/* OGF_STATUS_PARAM */
+		{ 0x000000ea, 0x00000000, 0x0000, 0x00 }
+	}
+};
 
 static struct bluez_sock_list hci_sk_list = {
 	lock: RW_LOCK_UNLOCKED
 };
 
-static struct sock *hci_sock_lookup(struct hci_dev *hdev)
-{
-	struct sock *sk;
-
-	read_lock(&hci_sk_list.lock);
-	for (sk = hci_sk_list.head; sk; sk = sk->next) {
-		if (hci_pi(sk)->hdev == hdev)
-			break;
-	}
-	read_unlock(&hci_sk_list.lock);
-	return sk;
-}
-
 /* Send frame to RAW socket */
 void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct sock * sk;
 
-	DBG("hdev %p len %d", hdev, skb->len);
+	BT_DBG("hdev %p len %d", hdev, skb->len);
 
 	read_lock(&hci_sk_list.lock);
 	for (sk = hci_sk_list.head; sk; sk = sk->next) {
-		struct hci_filter *flt;	
+		struct hci_filter *flt;
 		struct sk_buff *nskb;
 
 		if (sk->state != BT_BOUND || hci_pi(sk)->hdev != hdev)
@@ -100,13 +109,19 @@
 		/* Apply filter */
 		flt = &hci_pi(sk)->filter;
 
-		if (!test_bit(skb->pkt_type, &flt->type_mask))
+		if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
 			continue;
 
 		if (skb->pkt_type == HCI_EVENT_PKT) {
-			register int evt = (*(__u8 *)skb->data & 63);
+			register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
+			
+			if (!hci_test_bit(evt, &flt->event_mask))
+				continue;
 
-			if (!test_bit(evt, &flt->event_mask))
+			if (flt->opcode && ((evt == EVT_CMD_COMPLETE && 
+					flt->opcode != *(__u16 *)(skb->data + 3)) ||
+					(evt == EVT_CMD_STATUS && 
+					flt->opcode != *(__u16 *)(skb->data + 4))))
 				continue;
 		}
 
@@ -116,8 +131,8 @@
 		/* Put type byte before the data */
 		memcpy(skb_push(nskb, 1), &nskb->pkt_type, 1);
 
-		skb_queue_tail(&sk->receive_queue, nskb);
-		sk->data_ready(sk, nskb->len);
+		if (sock_queue_rcv_skb(sk, nskb))
+			kfree_skb(nskb);
 	}
 	read_unlock(&hci_sk_list.lock);
 }
@@ -127,7 +142,7 @@
 	struct sock *sk = sock->sk;
 	struct hci_dev *hdev = hci_pi(sk)->hdev;
 
-	DBG("sock %p sk %p", sock, sk);
+	BT_DBG("sock %p sk %p", sock, sk);
 
 	if (!sk)
 		return 0;
@@ -135,9 +150,7 @@
 	bluez_sock_unlink(&hci_sk_list, sk);
 
 	if (hdev) {
-		if (!hci_sock_lookup(hdev))
-			hdev->flags &= ~HCI_SOCK;
-
+		atomic_dec(&hdev->promisc);
 		hci_dev_put(hdev);
 	}
 
@@ -149,24 +162,55 @@
 	sock_put(sk);
 
 	MOD_DEC_USE_COUNT;
-
 	return 0;
 }
 
-static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+/* Ioctls that require bound socket */ 
+static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
 {
-	struct sock *sk = sock->sk;
 	struct hci_dev *hdev = hci_pi(sk)->hdev;
-	__u32 mode;
 
-	DBG("cmd %x arg %lx", cmd, arg);
+	if (!hdev)
+		return -EBADFD;
 
 	switch (cmd) {
-	case HCIGETINFO:
-		return hci_dev_info(arg);
+	case HCISETRAW:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
 
+		if (arg)
+			set_bit(HCI_RAW, &hdev->flags);
+		else
+			clear_bit(HCI_RAW, &hdev->flags);
+
+		return 0;
+
+	case HCIGETCONNINFO:
+		return hci_get_conn_info(hdev, arg);
+
+	default:
+		if (hdev->ioctl)
+			return hdev->ioctl(hdev, cmd, arg);
+		return -EINVAL;
+	}
+}
+
+static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+	int err;
+
+	BT_DBG("cmd %x arg %lx", cmd, arg);
+
+	switch (cmd) {
 	case HCIGETDEVLIST:
-		return hci_dev_list(arg);
+		return hci_get_dev_list(arg);
+
+	case HCIGETDEVINFO:
+		return hci_get_dev_info(arg);
+
+	case HCIGETCONNLIST:
+		return hci_get_conn_list(arg);
 
 	case HCIDEVUP:
 		if (!capable(CAP_NET_ADMIN))
@@ -183,48 +227,31 @@
 			return -EACCES;
 		return hci_dev_reset(arg);
 
-	case HCIRESETSTAT:
+	case HCIDEVRESTAT:
 		if (!capable(CAP_NET_ADMIN))
 			return -EACCES;
 		return hci_dev_reset_stat(arg);
 
 	case HCISETSCAN:
-		if (!capable(CAP_NET_ADMIN))
-			return -EACCES;
-		return hci_dev_setscan(arg);
-
 	case HCISETAUTH:
-		if (!capable(CAP_NET_ADMIN))
-			return -EACCES;
-		return hci_dev_setauth(arg);
-
-	case HCISETRAW:
-		if (!capable(CAP_NET_ADMIN))
-			return -EACCES;
-
-		if (!hdev)
-			return -EBADFD;
-
-		if (arg)
-			mode = HCI_RAW;
-		else
-			mode = HCI_NORMAL;
-
-		return hci_dev_setmode(hdev, mode);
-
+	case HCISETENCRYPT:
 	case HCISETPTYPE:
+	case HCISETLINKPOL:
+	case HCISETLINKMODE:
+	case HCISETACLMTU:
+	case HCISETSCOMTU:
 		if (!capable(CAP_NET_ADMIN))
 			return -EACCES;
-		return hci_dev_setptype(arg);
+		return hci_dev_cmd(cmd, arg);
 
 	case HCIINQUIRY:
 		return hci_inquiry(arg);
 
-	case HCIGETCONNLIST:
-		return hci_conn_list(arg);
-
 	default:
-		return -EINVAL;
+		lock_sock(sk);
+		err = hci_sock_bound_ioctl(sk, cmd, arg);
+		release_sock(sk);
+		return err;
 	};
 }
 
@@ -233,28 +260,35 @@
 	struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
 	struct sock *sk = sock->sk;
 	struct hci_dev *hdev = NULL;
+	int err = 0;
 
-	DBG("sock %p sk %p", sock, sk);
+	BT_DBG("sock %p sk %p", sock, sk);
 
 	if (!haddr || haddr->hci_family != AF_BLUETOOTH)
 		return -EINVAL;
 
+	lock_sock(sk);
+
 	if (hci_pi(sk)->hdev) {
-		/* Already bound */
-		return 0;
+		err = -EALREADY;
+		goto done;
 	}
 
 	if (haddr->hci_dev != HCI_DEV_NONE) {
-		if (!(hdev = hci_dev_get(haddr->hci_dev)))
-			return -ENODEV;
+		if (!(hdev = hci_dev_get(haddr->hci_dev))) {
+			err = -ENODEV;
+			goto done;
+		}
 
-		hdev->flags |= HCI_SOCK;
+		atomic_inc(&hdev->promisc);
 	}
 
 	hci_pi(sk)->hdev = hdev;
 	sk->state = BT_BOUND;
 
-	return 0;
+done:
+	release_sock(sk);
+	return err;
 }
 
 static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer)
@@ -262,73 +296,44 @@
 	struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
 	struct sock *sk = sock->sk;
 
-	DBG("sock %p sk %p", sock, sk);
+	BT_DBG("sock %p sk %p", sock, sk);
+
+	lock_sock(sk);
 
 	*addr_len = sizeof(*haddr);
 	haddr->hci_family = AF_BLUETOOTH;
 	haddr->hci_dev    = hci_pi(sk)->hdev->id;
 
+	release_sock(sk);
 	return 0;
 }
 
-static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
-                            struct scm_cookie *scm)
-{
-	struct sock *sk = sock->sk;
-	struct hci_dev *hdev = hci_pi(sk)->hdev;
-	struct sk_buff *skb;
-	int err;
-
-	DBG("sock %p sk %p", sock, sk);
-
-	if (msg->msg_flags & MSG_OOB)
-		return -EOPNOTSUPP;
-
-	if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
-		return -EINVAL;
-
-	if (!hdev)
-		return -EBADFD;
-
-	if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
-		return err;
-
-	if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
-		kfree_skb(skb);
-		return -EFAULT;
-	}
-
-	skb->dev = (void *) hdev;
-	skb->pkt_type = *((unsigned char *) skb->data);
-	skb_pull(skb, 1);
-
-	/* Send frame to HCI core */
-	hci_send_raw(skb);
-
-	return len;
-}
-
 static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
 {
 	__u32 mask = hci_pi(sk)->cmsg_mask;
 
 	if (mask & HCI_CMSG_DIR)
         	put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(int), &bluez_cb(skb)->incomming);
+
+	if (mask & HCI_CMSG_TSTAMP)
+        	put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, sizeof(skb->stamp), &skb->stamp);
 }
  
-static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len,
-                            int flags, struct scm_cookie *scm)
+static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
 {
 	int noblock = flags & MSG_DONTWAIT;
 	struct sock *sk = sock->sk;
 	struct sk_buff *skb;
 	int copied, err;
 
-	DBG("sock %p sk %p", sock, sk);
+	BT_DBG("sock %p, sk %p", sock, sk);
 
-	if (flags & (MSG_OOB | MSG_PEEK))
+	if (flags & (MSG_OOB))
 		return -EOPNOTSUPP;
 
+	if (sk->state == BT_CLOSED)
+		return 0;
+
 	if (!(skb = skb_recv_datagram(sk, flags, noblock, &err)))
 		return err;
 
@@ -343,28 +348,107 @@
 	skb->h.raw = skb->data;
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 
-	if (hci_pi(sk)->cmsg_mask)
-		hci_sock_cmsg(sk, msg, skb);
-
+	hci_sock_cmsg(sk, msg, skb);
+	
 	skb_free_datagram(sk, skb);
 
 	return err ? : copied;
 }
 
+static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+                            struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct hci_dev *hdev;
+	struct sk_buff *skb;
+	int err;
+
+	BT_DBG("sock %p sk %p", sock, sk);
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
+		return -EINVAL;
+
+	if (len < 4)
+		return -EINVAL;
+	
+	lock_sock(sk);
+
+	if (!(hdev = hci_pi(sk)->hdev)) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
+		goto done;
+
+	if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+		err = -EFAULT;
+		goto drop;
+	}
+
+	skb->pkt_type = *((unsigned char *) skb->data);
+	skb_pull(skb, 1);
+	skb->dev = (void *) hdev;
+
+	if (skb->pkt_type == HCI_COMMAND_PKT) {
+		u16 opcode = __le16_to_cpu(get_unaligned((u16 *)skb->data));
+		u16 ogf = cmd_opcode_ogf(opcode);
+		u16 ocf = cmd_opcode_ocf(opcode);
+
+		if (((ogf > HCI_SFLT_MAX_OGF) || 
+				!hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) &&
+		    			!capable(CAP_NET_RAW)) {
+			err = -EPERM;
+			goto drop;
+		}
+
+		if (test_bit(HCI_RAW, &hdev->flags) || (ogf == OGF_VENDOR_CMD)) {
+			skb_queue_tail(&hdev->raw_q, skb);
+			hci_sched_tx(hdev);
+		} else {
+			skb_queue_tail(&hdev->cmd_q, skb);
+			hci_sched_cmd(hdev);
+		}
+	} else {
+		if (!capable(CAP_NET_RAW)) {
+			err = -EPERM;
+			goto drop;
+		}
+
+		skb_queue_tail(&hdev->raw_q, skb);
+		hci_sched_tx(hdev);
+	}
+
+	err = len;
+
+done:
+	release_sock(sk);
+	return err;
+
+drop:
+	kfree_skb(skb);
+	goto done;
+}
+
 int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len)
 {
 	struct sock *sk = sock->sk;
-	struct hci_filter flt;
+	struct hci_filter flt = { opcode: 0 };
 	int err = 0, opt = 0;
 
-	DBG("sk %p, opt %d", sk, optname);
+	BT_DBG("sk %p, opt %d", sk, optname);
 
 	lock_sock(sk);
 
 	switch (optname) {
 	case HCI_DATA_DIR:
-		if (get_user(opt, (int *)optval))
-			return -EFAULT;
+		if (get_user(opt, (int *)optval)) {
+			err = -EFAULT;
+			break;
+		}
 
 		if (opt)
 			hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR;
@@ -372,12 +456,31 @@
 			hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR;
 		break;
 
+	case HCI_TIME_STAMP:
+		if (get_user(opt, (int *)optval)) {
+			err = -EFAULT;
+			break;
+		}
+
+		if (opt)
+			hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP;
+		else
+			hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP;
+		break;
+
 	case HCI_FILTER:
 		len = MIN(len, sizeof(struct hci_filter));
 		if (copy_from_user(&flt, optval, len)) {
 			err = -EFAULT;
 			break;
 		}
+
+		if (!capable(CAP_NET_RAW)) {
+			flt.type_mask     &= hci_sec_filter.type_mask;
+			flt.event_mask[0] &= hci_sec_filter.event_mask[0];
+			flt.event_mask[1] &= hci_sec_filter.event_mask[1];
+		}
+		
 		memcpy(&hci_pi(sk)->filter, &flt, len);
 		break;
 
@@ -409,6 +512,16 @@
 			return -EFAULT;
 		break;
 
+	case HCI_TIME_STAMP:
+		if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP)
+			opt = 1;
+		else 
+			opt = 0;
+
+		if (put_user(opt, optval))
+			return -EFAULT;
+		break;
+
 	case HCI_FILTER:
 		len = MIN(len, sizeof(struct hci_filter));
 		if (copy_to_user(optval, &hci_pi(sk)->filter, len))
@@ -446,7 +559,7 @@
 {
 	struct sock *sk;
 
-	DBG("sock %p", sock);
+	BT_DBG("sock %p", sock);
 
 	if (sock->type != SOCK_RAW)
 		return -ESOCKTNOSUPPORT;
@@ -464,44 +577,31 @@
 	sk->protocol = protocol;
 	sk->state    = BT_OPEN;
 
-	/* Initialize filter */
-	hci_pi(sk)->filter.type_mask  = (1<<HCI_EVENT_PKT);
-	hci_pi(sk)->filter.event_mask[0] = ~0L;
-	hci_pi(sk)->filter.event_mask[1] = ~0L;
-
 	bluez_sock_link(&hci_sk_list, sk);
 
 	MOD_INC_USE_COUNT;
-
 	return 0;
 }
 
 static int hci_sock_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
 	struct hci_dev *hdev = (struct hci_dev *) ptr;
-	struct sk_buff *skb;
-
-	DBG("hdev %s event %ld", hdev->name, event);
+	evt_si_device sd;
+	
+	BT_DBG("hdev %s event %ld", hdev->name, event);
 
 	/* Send event to sockets */
-	if ((skb = bluez_skb_alloc(HCI_EVENT_HDR_SIZE + EVT_HCI_DEV_EVENT_SIZE, GFP_ATOMIC))) {
-		hci_event_hdr eh = { EVT_HCI_DEV_EVENT, EVT_HCI_DEV_EVENT_SIZE };
-		evt_hci_dev_event he = { event, hdev->id };
-
-		skb->pkt_type = HCI_EVENT_PKT;
-		memcpy(skb_put(skb, HCI_EVENT_HDR_SIZE), &eh, HCI_EVENT_HDR_SIZE);
-		memcpy(skb_put(skb, EVT_HCI_DEV_EVENT_SIZE), &he, EVT_HCI_DEV_EVENT_SIZE);
-
-		hci_send_to_sock(NULL, skb);
-		kfree_skb(skb);
-	}
-
+	sd.event  = event;
+	sd.dev_id = hdev->id;
+	hci_si_event(NULL, EVT_SI_DEVICE, EVT_SI_DEVICE_SIZE, &sd);
+	
 	if (event == HCI_DEV_UNREG) {
 		struct sock *sk;
 
 		/* Detach sockets from device */
 		read_lock(&hci_sk_list.lock);
 		for (sk = hci_sk_list.head; sk; sk = sk->next) {
+			bh_lock_sock(sk);
 			if (hci_pi(sk)->hdev == hdev) {
 				hci_pi(sk)->hdev = NULL;
 				sk->err = EPIPE;
@@ -510,6 +610,7 @@
 
 				hci_dev_put(hdev);
 			}
+			bh_unlock_sock(sk);
 		}
 		read_unlock(&hci_sk_list.lock);
 	}
@@ -529,21 +630,19 @@
 int hci_sock_init(void)
 {
 	if (bluez_sock_register(BTPROTO_HCI, &hci_sock_family_ops)) {
-		ERR("Can't register HCI socket");
+		BT_ERR("Can't register HCI socket");
 		return -EPROTO;
 	}
 
 	hci_register_notifier(&hci_sock_nblock);
-
 	return 0;
 }
 
 int hci_sock_cleanup(void)
 {
 	if (bluez_sock_unregister(BTPROTO_HCI))
-		ERR("Can't unregister HCI socket");
+		BT_ERR("Can't unregister HCI socket");
 
 	hci_unregister_notifier(&hci_sock_nblock);
-
 	return 0;
 }
diff -urN linux-2.4.18/net/bluetooth/hidp/Config.in linux-2.4.18-mh15/net/bluetooth/hidp/Config.in
--- linux-2.4.18/net/bluetooth/hidp/Config.in	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/hidp/Config.in	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,5 @@
+#
+# Bluetooth HIDP layer configuration
+#
+
+dep_tristate 'HIDP protocol support' CONFIG_BLUEZ_HIDP $CONFIG_INPUT $CONFIG_BLUEZ_L2CAP
diff -urN linux-2.4.18/net/bluetooth/hidp/core.c linux-2.4.18-mh15/net/bluetooth/hidp/core.c
--- linux-2.4.18/net/bluetooth/hidp/core.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/hidp/core.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,655 @@
+/* 
+   HIDP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <net/sock.h>
+
+#include <linux/input.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "hidp.h"
+
+#ifndef CONFIG_BT_HIDP_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.0"
+
+static DECLARE_RWSEM(hidp_session_sem);
+static LIST_HEAD(hidp_session_list);
+
+static unsigned char hidp_keycode[256] = {
+	  0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
+	 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,
+	  4,  5,  6,  7,  8,  9, 10, 11, 28,  1, 14, 15, 57, 12, 13, 26,
+	 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
+	 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
+	105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
+	 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
+	191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
+	115,114,  0,  0,  0,121,  0, 89, 93,124, 92, 94, 95,  0,  0,  0,
+	122,123, 90, 91, 85,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
+	150,158,159,128,136,177,178,176,142,152,173,140
+};
+
+static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
+{
+	struct hidp_session *session;
+	struct list_head *p;
+
+	BT_DBG("");
+
+	list_for_each(p, &hidp_session_list) {
+		session = list_entry(p, struct hidp_session, list);
+		if (!bacmp(bdaddr, &session->bdaddr))
+			return session;
+	}
+	return NULL;
+}
+
+static void __hidp_link_session(struct hidp_session *session)
+{
+	MOD_INC_USE_COUNT;
+	list_add(&session->list, &hidp_session_list);
+}
+
+static void __hidp_unlink_session(struct hidp_session *session)
+{
+	list_del(&session->list);
+	MOD_DEC_USE_COUNT;
+}
+
+static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
+{
+	bacpy(&ci->bdaddr, &session->bdaddr);
+
+	ci->flags = session->flags;
+	ci->state = session->state;
+
+	ci->vendor  = 0x0000;
+	ci->product = 0x0000;
+	ci->version = 0x0000;
+	memset(ci->name, 0, 128);
+
+	if (session->input) {
+		ci->vendor  = session->input->idvendor;
+		ci->product = session->input->idproduct;
+		ci->version = session->input->idversion;
+		if (session->input->name)
+			strncpy(ci->name, session->input->name, 128);
+		else
+			strncpy(ci->name, "HID Boot Device", 128);
+	}
+}
+
+static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct hidp_session *session = dev->private;
+	struct sk_buff *skb;
+	unsigned char newleds;
+
+	BT_DBG("session %p hid %p data %p size %d", session, device, data, size);
+
+	if (type != EV_LED)
+		return -1;
+
+	newleds = (!!test_bit(LED_KANA,    dev->led) << 3) |
+		  (!!test_bit(LED_COMPOSE, dev->led) << 3) |
+		  (!!test_bit(LED_SCROLLL, dev->led) << 2) |
+		  (!!test_bit(LED_CAPSL,   dev->led) << 1) |
+		  (!!test_bit(LED_NUML,    dev->led));
+
+	if (session->leds == newleds)
+		return 0;
+
+	session->leds = newleds;
+
+	if (!(skb = alloc_skb(3, GFP_ATOMIC))) {
+		BT_ERR("Can't allocate memory for new frame");
+		return -ENOMEM;
+	}
+
+	*skb_put(skb, 1) = 0xa2;
+	*skb_put(skb, 1) = 0x01;
+	*skb_put(skb, 1) = newleds;
+
+	skb_queue_tail(&session->intr_transmit, skb);
+
+	hidp_schedule(session);
+
+	return 0;
+}
+
+static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
+{
+	struct input_dev *dev = session->input;
+	unsigned char *keys = session->keys;
+	unsigned char *udata = skb->data + 1;
+	signed char *sdata = skb->data + 1;
+	int i, size = skb->len - 1;
+
+	switch (skb->data[0]) {
+	case 0x01:	/* Keyboard report */
+		for (i = 0; i < 8; i++)
+			input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
+
+		for (i = 2; i < 8; i++) {
+			if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
+				if (hidp_keycode[keys[i]])
+					input_report_key(dev, hidp_keycode[keys[i]], 0);
+				else
+					BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
+			}
+
+			if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
+				if (hidp_keycode[udata[i]])
+					input_report_key(dev, hidp_keycode[udata[i]], 1);
+				else
+					BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
+			}
+		}
+
+		memcpy(keys, udata, 8);
+		break;
+
+	case 0x02:	/* Mouse report */
+		input_report_key(dev, BTN_LEFT,   sdata[0] & 0x01);
+		input_report_key(dev, BTN_RIGHT,  sdata[0] & 0x02);
+		input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
+		input_report_key(dev, BTN_SIDE,   sdata[0] & 0x08);
+		input_report_key(dev, BTN_EXTRA,  sdata[0] & 0x10);
+
+		input_report_rel(dev, REL_X, sdata[1]);
+		input_report_rel(dev, REL_Y, sdata[2]);
+
+		if (size > 3)
+			input_report_rel(dev, REL_WHEEL, sdata[3]);
+		break;
+	}
+
+	input_event(dev, EV_RST, 0, 0);
+}
+
+static void hidp_idle_timeout(unsigned long arg)
+{
+	struct hidp_session *session = (struct hidp_session *) arg;
+
+	atomic_inc(&session->terminate);
+	hidp_schedule(session);
+}
+
+static inline void hidp_set_timer(struct hidp_session *session)
+{
+	if (session->idle_to > 0)
+		mod_timer(&session->timer, jiffies + HZ * session->idle_to);
+}
+
+static inline void hidp_del_timer(struct hidp_session *session)
+{
+	if (session->idle_to > 0)
+		del_timer(&session->timer);
+}
+
+static inline void hidp_send_message(struct hidp_session *session, unsigned char hdr)
+{
+	struct sk_buff *skb;
+
+	BT_DBG("session %p", session);
+
+	if (!(skb = alloc_skb(1, GFP_ATOMIC))) {
+		BT_ERR("Can't allocate memory for message");
+		return;
+	}
+
+	*skb_put(skb, 1) = hdr;
+
+	skb_queue_tail(&session->ctrl_transmit, skb);
+
+	hidp_schedule(session);
+}
+
+static inline int hidp_recv_frame(struct hidp_session *session, struct sk_buff *skb)
+{
+	__u8 hdr;
+
+	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+	hdr = skb->data[0];
+	skb_pull(skb, 1);
+
+	if (hdr == 0xa1) {
+		hidp_set_timer(session);
+
+		if (session->input)
+			hidp_input_report(session, skb);
+	} else {
+		BT_DBG("Unsupported protocol header 0x%02x", hdr);
+	}
+
+	kfree_skb(skb);
+	return 0;
+}
+
+static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
+{
+	struct iovec iv = { data, len };
+	struct msghdr msg;
+
+	BT_DBG("sock %p data %p len %d", sock, data, len);
+
+	if (!len)
+		return 0;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iovlen = 1;
+	msg.msg_iov = &iv;
+
+	return sock_sendmsg(sock, &msg, len);
+}
+
+static int hidp_process_transmit(struct hidp_session *session)
+{
+	struct sk_buff *skb;
+
+	BT_DBG("session %p", session);
+
+	while ((skb = skb_dequeue(&session->ctrl_transmit))) {
+		if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
+			skb_queue_head(&session->ctrl_transmit, skb);
+			break;
+		}
+
+		hidp_set_timer(session);
+		kfree_skb(skb);
+	}
+
+	while ((skb = skb_dequeue(&session->intr_transmit))) {
+		if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
+			skb_queue_head(&session->intr_transmit, skb);
+			break;
+		}
+
+		hidp_set_timer(session);
+		kfree_skb(skb);
+	}
+
+	return skb_queue_len(&session->ctrl_transmit) +
+				skb_queue_len(&session->intr_transmit);
+}
+
+static int hidp_session(void *arg)
+{
+	struct hidp_session *session = arg;
+	struct sock *ctrl_sk = session->ctrl_sock->sk;
+	struct sock *intr_sk = session->intr_sock->sk;
+	struct sk_buff *skb;
+	int vendor = 0x0000, product = 0x0000;
+	wait_queue_t ctrl_wait, intr_wait;
+	unsigned long timeo = HZ;
+
+	BT_DBG("session %p", session);
+
+	if (session->input) {
+		vendor  = session->input->idvendor;
+		product = session->input->idproduct;
+	}
+
+	daemonize(); reparent_to_init();
+
+	sprintf(current->comm, "khidpd_%04x%04x", vendor, product);
+
+	sigfillset(&current->blocked);
+	flush_signals(current);
+
+	current->nice = -15;
+
+	set_fs(KERNEL_DS);
+
+	init_waitqueue_entry(&ctrl_wait, current);
+	init_waitqueue_entry(&intr_wait, current);
+	add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
+	add_wait_queue(intr_sk->sleep, &intr_wait);
+	while (!atomic_read(&session->terminate)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (ctrl_sk->state != BT_CONNECTED || intr_sk->state != BT_CONNECTED)
+			break;
+
+		while ((skb = skb_dequeue(&ctrl_sk->receive_queue))) {
+			skb_orphan(skb);
+			hidp_recv_frame(session, skb);
+		}
+
+		while ((skb = skb_dequeue(&intr_sk->receive_queue))) {
+			skb_orphan(skb);
+			hidp_recv_frame(session, skb);
+		}
+
+		hidp_process_transmit(session);
+
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(intr_sk->sleep, &intr_wait);
+	remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
+
+	down_write(&hidp_session_sem);
+
+	hidp_del_timer(session);
+
+	if (intr_sk->state != BT_CONNECTED) {
+		init_waitqueue_entry(&ctrl_wait, current);
+		add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
+		while (timeo && ctrl_sk->state != BT_CLOSED) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			timeo = schedule_timeout(timeo);
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
+		timeo = HZ;
+	}
+
+	fput(session->ctrl_sock->file);
+
+	init_waitqueue_entry(&intr_wait, current);
+	add_wait_queue(intr_sk->sleep, &intr_wait);
+	while (timeo && intr_sk->state != BT_CLOSED) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		timeo = schedule_timeout(timeo);
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(intr_sk->sleep, &intr_wait);
+
+	fput(session->intr_sock->file);
+
+	__hidp_unlink_session(session);
+
+	if (session->input) {
+		input_unregister_device(session->input);
+		kfree(session->input);
+	}
+
+	up_write(&hidp_session_sem);
+
+	kfree(session);
+	return 0;
+}
+
+static inline void hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req)
+{
+	struct input_dev *input = session->input;
+	int i;
+
+	input->private = session;
+
+	input->idbus     = BUS_BLUETOOTH;
+	input->idvendor  = req->vendor;
+	input->idproduct = req->product;
+	input->idversion = req->version;
+
+	if (req->subclass & 0x40) {
+		set_bit(EV_KEY, input->evbit);
+		set_bit(EV_LED, input->evbit);
+		set_bit(EV_REP, input->evbit);
+
+		set_bit(LED_NUML,    input->ledbit);
+		set_bit(LED_CAPSL,   input->ledbit);
+		set_bit(LED_SCROLLL, input->ledbit);
+		set_bit(LED_COMPOSE, input->ledbit);
+		set_bit(LED_KANA,    input->ledbit);
+
+		for (i = 0; i < sizeof(hidp_keycode); i++)
+			set_bit(hidp_keycode[i], input->keybit);
+		clear_bit(0, input->keybit);
+	}
+
+	if (req->subclass & 0x80) {
+		input->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+		input->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+		input->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+		input->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+		input->relbit[0] |= BIT(REL_WHEEL);
+	}
+
+	input->event = hidp_input_event;
+
+	input_register_device(input);
+}
+
+int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
+{
+	struct hidp_session *session, *s;
+	int err;
+
+	BT_DBG("");
+
+	if (bacmp(&bluez_pi(ctrl_sock->sk)->src, &bluez_pi(intr_sock->sk)->src) ||
+			bacmp(&bluez_pi(ctrl_sock->sk)->dst, &bluez_pi(intr_sock->sk)->dst))
+		return -ENOTUNIQ;
+
+	session = kmalloc(sizeof(struct hidp_session), GFP_KERNEL);
+	if (!session) 
+		return -ENOMEM;
+	memset(session, 0, sizeof(struct hidp_session));
+
+	session->input = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
+	if (!session->input) {
+		kfree(session);
+		return -ENOMEM;
+	}
+	memset(session->input, 0, sizeof(struct input_dev));
+
+	down_write(&hidp_session_sem);
+
+	s = __hidp_get_session(&bluez_pi(ctrl_sock->sk)->dst);
+	if (s && s->state == BT_CONNECTED) {
+		err = -EEXIST;
+		goto failed;
+	}
+
+	bacpy(&session->bdaddr, &bluez_pi(ctrl_sock->sk)->dst);
+
+	session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
+	session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
+
+	BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
+
+	session->ctrl_sock = ctrl_sock;
+	session->intr_sock = intr_sock;
+	session->state     = BT_CONNECTED;
+
+	init_timer(&session->timer);
+
+	session->timer.function = hidp_idle_timeout;
+	session->timer.data     = (unsigned long) session;
+
+	skb_queue_head_init(&session->ctrl_transmit);
+	skb_queue_head_init(&session->intr_transmit);
+
+	session->flags   = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
+	session->idle_to = req->idle_to;
+
+	if (session->input)
+		hidp_setup_input(session, req);
+
+	__hidp_link_session(session);
+
+	hidp_set_timer(session);
+
+	err = kernel_thread(hidp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	if (err < 0)
+		goto unlink;
+
+	if (session->input) {
+		hidp_send_message(session, 0x70);
+		session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
+
+		session->leds = 0xff;
+		hidp_input_event(session->input, EV_LED, 0, 0);
+	}
+
+	up_write(&hidp_session_sem);
+	return 0;
+
+unlink:
+	hidp_del_timer(session);
+
+	__hidp_unlink_session(session);
+
+	if (session->input)
+		input_unregister_device(session->input);
+
+failed:
+	up_write(&hidp_session_sem);
+
+	if (session->input)
+		kfree(session->input);
+
+	kfree(session);
+	return err;
+}
+
+int hidp_del_connection(struct hidp_conndel_req *req)
+{
+	struct hidp_session *session;
+	int err = 0;
+
+	BT_DBG("");
+
+	down_read(&hidp_session_sem);
+
+	session = __hidp_get_session(&req->bdaddr);
+	if (session) {
+		if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
+			hidp_send_message(session, 0x15);
+		} else {
+			/* Flush the transmit queues */
+			skb_queue_purge(&session->ctrl_transmit);
+			skb_queue_purge(&session->intr_transmit);
+
+			/* Kill session thread */
+			atomic_inc(&session->terminate);
+			hidp_schedule(session);
+		}
+	} else
+		err = -ENOENT;
+
+	up_read(&hidp_session_sem);
+	return err;
+}
+
+int hidp_get_connlist(struct hidp_connlist_req *req)
+{
+	struct list_head *p;
+	int err = 0, n = 0;
+
+	BT_DBG("");
+
+	down_read(&hidp_session_sem);
+
+	list_for_each(p, &hidp_session_list) {
+		struct hidp_session *session;
+		struct hidp_conninfo ci;
+
+		session = list_entry(p, struct hidp_session, list);
+
+		__hidp_copy_session(session, &ci);
+
+		if (copy_to_user(req->ci, &ci, sizeof(ci))) {
+			err = -EFAULT;
+			break;
+		}
+
+		if (++n >= req->cnum)
+			break;
+
+		req->ci++;
+	}
+	req->cnum = n;
+
+	up_read(&hidp_session_sem);
+	return err;
+}
+
+int hidp_get_conninfo(struct hidp_conninfo *ci)
+{
+	struct hidp_session *session;
+	int err = 0;
+
+	down_read(&hidp_session_sem);
+
+	session = __hidp_get_session(&ci->bdaddr);
+	if (session)
+		__hidp_copy_session(session, ci);
+	else
+		err = -ENOENT;
+
+	up_read(&hidp_session_sem);
+	return err;
+}
+
+static int __init hidp_init(void)
+{
+	l2cap_load();
+
+	hidp_init_sockets();
+
+	BT_INFO("BlueZ HIDP ver %s", VERSION);
+	BT_INFO("Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>");
+
+	return 0;
+}
+
+static void __exit hidp_exit(void)
+{
+	hidp_cleanup_sockets();
+}
+
+module_init(hidp_init);
+module_exit(hidp_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/net/bluetooth/hidp/hidp.h linux-2.4.18-mh15/net/bluetooth/hidp/hidp.h
--- linux-2.4.18/net/bluetooth/hidp/hidp.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/hidp/hidp.h	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,122 @@
+/* 
+   HIDP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#ifndef __HIDP_H
+#define __HIDP_H
+
+#include <linux/types.h>
+#include <net/bluetooth/bluetooth.h>
+
+/* HIDP ioctl defines */
+#define HIDPCONNADD	_IOW('H', 200, int)
+#define HIDPCONNDEL	_IOW('H', 201, int)
+#define HIDPGETCONNLIST	_IOR('H', 210, int)
+#define HIDPGETCONNINFO	_IOR('H', 211, int)
+
+#define HIDP_VIRTUAL_CABLE_UNPLUG	0
+#define HIDP_BOOT_PROTOCOL_MODE		1
+#define HIDP_BLUETOOTH_VENDOR_ID	9
+
+struct hidp_connadd_req {
+	int   ctrl_sock;	// Connected control socket
+	int   intr_sock;	// Connteted interrupt socket
+	__u16 parser;
+	__u16 rd_size;
+	__u8 *rd_data;
+	__u8  country;
+	__u8  subclass;
+	__u16 vendor;
+	__u16 product;
+	__u16 version;
+	__u32 flags;
+	__u32 idle_to;
+	char  name[128];
+};
+
+struct hidp_conndel_req {
+	bdaddr_t bdaddr;
+	__u32    flags;
+};
+
+struct hidp_conninfo {
+	bdaddr_t bdaddr;
+	__u32    flags;
+	__u16    state;
+	__u16    vendor;
+	__u16    product;
+	__u16    version;
+	char     name[128];
+};
+
+struct hidp_connlist_req {
+	__u32  cnum;
+	struct hidp_conninfo *ci;
+};
+
+int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock);
+int hidp_del_connection(struct hidp_conndel_req *req);
+int hidp_get_connlist(struct hidp_connlist_req *req);
+int hidp_get_conninfo(struct hidp_conninfo *ci);
+
+/* HIDP session defines */
+struct hidp_session {
+	struct list_head list;
+
+	struct socket *ctrl_sock;
+	struct socket *intr_sock;
+
+	bdaddr_t bdaddr;
+
+	unsigned long state;
+	unsigned long flags;
+	unsigned long idle_to;
+
+	uint ctrl_mtu;
+	uint intr_mtu;
+
+	atomic_t terminate;
+
+	unsigned char keys[8];
+	unsigned char leds;
+
+	struct input_dev *input;
+
+	struct timer_list timer;
+
+	struct sk_buff_head ctrl_transmit;
+	struct sk_buff_head intr_transmit;
+};
+
+static inline void hidp_schedule(struct hidp_session *session)
+{
+	struct sock *ctrl_sk = session->ctrl_sock->sk;
+	struct sock *intr_sk = session->intr_sock->sk;
+
+	wake_up_interruptible(ctrl_sk->sleep);
+	wake_up_interruptible(intr_sk->sleep);
+}
+
+/* HIDP init defines */
+extern int __init hidp_init_sockets(void);
+extern void __exit hidp_cleanup_sockets(void);
+
+#endif /* __HIDP_H */
diff -urN linux-2.4.18/net/bluetooth/hidp/Makefile linux-2.4.18-mh15/net/bluetooth/hidp/Makefile
--- linux-2.4.18/net/bluetooth/hidp/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/hidp/Makefile	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux Bluetooth HIDP layer
+#
+
+O_TARGET := hidp.o
+
+obj-y	:= core.o sock.o
+obj-m	+= $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.4.18/net/bluetooth/hidp/sock.c linux-2.4.18-mh15/net/bluetooth/hidp/sock.c
--- linux-2.4.18/net/bluetooth/hidp/sock.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/hidp/sock.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,212 @@
+/* 
+   HIDP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <net/sock.h>
+
+#include "hidp.h"
+
+#ifndef CONFIG_BT_HIDP_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+static int hidp_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p sk %p", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	sock_orphan(sk);
+	sock_put(sk);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct hidp_connadd_req ca;
+	struct hidp_conndel_req cd;
+	struct hidp_connlist_req cl;
+	struct hidp_conninfo ci;
+	struct socket *csock;
+	struct socket *isock;
+	int err;
+
+	BT_DBG("cmd %x arg %lx", cmd, arg);
+
+	switch (cmd) {
+	case HIDPCONNADD:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+
+		if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
+			return -EFAULT;
+
+		csock = sockfd_lookup(ca.ctrl_sock, &err);
+		if (!csock)
+			return err;
+
+		isock = sockfd_lookup(ca.intr_sock, &err);
+		if (!isock) {
+			fput(csock->file);
+			return err;
+		}
+
+		if (csock->sk->state != BT_CONNECTED || isock->sk->state != BT_CONNECTED) {
+			fput(csock->file);
+			fput(isock->file);
+			return -EBADFD;
+		}
+
+		err = hidp_add_connection(&ca, csock, isock);
+		if (!err) {
+			if (copy_to_user((void *) arg, &ca, sizeof(ca)))
+				err = -EFAULT;
+		} else {
+			fput(csock->file);
+			fput(isock->file);
+		}
+
+		return err;
+
+	case HIDPCONNDEL:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+
+		if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
+			return -EFAULT;
+
+		return hidp_del_connection(&cd);
+
+	case HIDPGETCONNLIST:
+		if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
+			return -EFAULT;
+
+		if (cl.cnum <= 0)
+			return -EINVAL;
+
+		err = hidp_get_connlist(&cl);
+		if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
+			return -EFAULT;
+
+		return err;
+
+	case HIDPGETCONNINFO:
+		if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
+			return -EFAULT;
+
+		err = hidp_get_conninfo(&ci);
+		if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
+			return -EFAULT;
+
+		return err;
+	}
+
+	return -EINVAL;
+}
+
+static struct proto_ops hidp_sock_ops = {
+	family:		PF_BLUETOOTH,
+	release:	hidp_sock_release,
+	ioctl:		hidp_sock_ioctl,
+	bind:		sock_no_bind,
+	getname:	sock_no_getname,
+	sendmsg:	sock_no_sendmsg,
+	recvmsg:	sock_no_recvmsg,
+	poll:		sock_no_poll,
+	listen:		sock_no_listen,
+	shutdown:	sock_no_shutdown,
+	setsockopt:	sock_no_setsockopt,
+	getsockopt:	sock_no_getsockopt,
+	connect:	sock_no_connect,
+	socketpair:	sock_no_socketpair,
+	accept:		sock_no_accept,
+	mmap:		sock_no_mmap
+};
+
+static int hidp_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	if (sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	sock->ops = &hidp_sock_ops;
+
+	if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
+		return -ENOMEM;
+
+	MOD_INC_USE_COUNT;
+
+	sock->state = SS_UNCONNECTED;
+	sock_init_data(sock, sk);
+
+	sk->destruct = NULL;
+	sk->protocol = protocol;
+
+	return 0;
+}
+
+static struct net_proto_family hidp_sock_family_ops = {
+	family:		PF_BLUETOOTH,
+	create:		hidp_sock_create
+};
+
+int __init hidp_init_sockets(void)
+{
+	int err;
+
+	if ((err = bluez_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops)))
+		BT_ERR("Can't register HIDP socket layer (%d)", err);
+
+	return err;
+}
+
+void __exit hidp_cleanup_sockets(void)
+{
+	int err;
+
+	if ((err = bluez_sock_unregister(BTPROTO_HIDP)))
+		BT_ERR("Can't unregister HIDP socket layer (%d)", err);
+}
diff -urN linux-2.4.18/net/bluetooth/l2cap.c linux-2.4.18-mh15/net/bluetooth/l2cap.c
--- linux-2.4.18/net/bluetooth/l2cap.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/l2cap.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,2222 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * BlueZ L2CAP core and sockets.
+ *
+ * $Id: l2cap.c,v 1.15 2002/09/09 01:14:52 maxk Exp $
+ */
+#define VERSION "2.3"
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#ifndef L2CAP_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+static struct proto_ops l2cap_sock_ops;
+
+struct bluez_sock_list l2cap_sk_list = {
+	lock: RW_LOCK_UNLOCKED
+};
+
+static int l2cap_conn_del(struct hci_conn *conn, int err);
+
+static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
+static void l2cap_chan_del(struct sock *sk, int err);
+static int  l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
+
+static void __l2cap_sock_close(struct sock *sk, int reason);
+static void l2cap_sock_close(struct sock *sk);
+static void l2cap_sock_kill(struct sock *sk);
+
+static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data);
+static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data);
+
+/* ----- L2CAP timers ------ */
+static void l2cap_sock_timeout(unsigned long arg)
+{
+	struct sock *sk = (struct sock *) arg;
+
+	BT_DBG("sock %p state %d", sk, sk->state);
+
+	bh_lock_sock(sk);
+	__l2cap_sock_close(sk, ETIMEDOUT);
+	bh_unlock_sock(sk);
+
+	l2cap_sock_kill(sk);
+	sock_put(sk);
+}
+
+static void l2cap_sock_set_timer(struct sock *sk, long timeout)
+{
+	BT_DBG("sk %p state %d timeout %ld", sk, sk->state, timeout);
+
+	if (!mod_timer(&sk->timer, jiffies + timeout))
+		sock_hold(sk);
+}
+
+static void l2cap_sock_clear_timer(struct sock *sk)
+{
+	BT_DBG("sock %p state %d", sk, sk->state);
+
+	if (timer_pending(&sk->timer) && del_timer(&sk->timer))
+		__sock_put(sk);
+}
+
+static void l2cap_sock_init_timer(struct sock *sk)
+{
+	init_timer(&sk->timer);
+	sk->timer.function = l2cap_sock_timeout;
+	sk->timer.data = (unsigned long)sk;
+}
+
+/* -------- L2CAP connections --------- */
+static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, __u8 status)
+{
+	struct l2cap_conn *conn;
+
+	if ((conn = hcon->l2cap_data))
+		return conn;
+
+	if (status)
+		return conn;
+
+	if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_ATOMIC)))
+		return NULL;
+	memset(conn, 0, sizeof(struct l2cap_conn));
+
+	hcon->l2cap_data = conn;
+	conn->hcon = hcon;
+	
+	conn->mtu = hcon->hdev->acl_mtu;
+	conn->src = &hcon->hdev->bdaddr;
+	conn->dst = &hcon->dst;
+	
+	spin_lock_init(&conn->lock);
+	conn->chan_list.lock = RW_LOCK_UNLOCKED;
+
+	BT_DBG("hcon %p conn %p", hcon, conn);
+
+	MOD_INC_USE_COUNT;
+	return conn;
+}
+
+static int l2cap_conn_del(struct hci_conn *hcon, int err)
+{
+	struct l2cap_conn *conn;
+	struct sock *sk;
+
+	if (!(conn = hcon->l2cap_data)) 
+		return 0;
+
+	BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
+
+	if (conn->rx_skb)
+		kfree_skb(conn->rx_skb);
+
+	/* Kill channels */
+	while ((sk = conn->chan_list.head)) {
+		bh_lock_sock(sk);
+		l2cap_chan_del(sk, err);
+		bh_unlock_sock(sk);
+		l2cap_sock_kill(sk);
+	}
+
+	hcon->l2cap_data = NULL;
+	kfree(conn);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* -------- Socket interface ---------- */
+static struct sock *__l2cap_get_sock_by_addr(__u16 psm, bdaddr_t *src)
+{
+	struct sock *sk;
+	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
+		if (sk->sport == psm && !bacmp(&bluez_pi(sk)->src, src))
+			break;
+	}
+	return sk;
+}
+
+/* Find socket with psm and source bdaddr.
+ * Returns closest match.
+ */
+static struct sock *__l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
+{
+	struct sock *sk, *sk1 = NULL;
+
+	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
+		if (state && sk->state != state)
+			continue;
+
+		if (l2cap_pi(sk)->psm == psm) {
+			/* Exact match. */
+			if (!bacmp(&bluez_pi(sk)->src, src))
+				break;
+
+			/* Closest match */
+			if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
+				sk1 = sk;
+		}
+	}
+	return sk ? sk : sk1;
+}
+
+/* Find socket with given address (psm, src).
+ * Returns locked socket */
+static inline struct sock *l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
+{
+	struct sock *s;
+	read_lock(&l2cap_sk_list.lock);
+	s = __l2cap_get_sock_by_psm(state, psm, src);
+	if (s) bh_lock_sock(s);
+	read_unlock(&l2cap_sk_list.lock);
+	return s;
+}
+
+static void l2cap_sock_destruct(struct sock *sk)
+{
+	BT_DBG("sk %p", sk);
+
+	skb_queue_purge(&sk->receive_queue);
+	skb_queue_purge(&sk->write_queue);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static void l2cap_sock_cleanup_listen(struct sock *parent)
+{
+	struct sock *sk;
+
+	BT_DBG("parent %p", parent);
+
+	/* Close not yet accepted channels */
+	while ((sk = bluez_accept_dequeue(parent, NULL)))
+		l2cap_sock_close(sk);
+
+	parent->state  = BT_CLOSED;
+	parent->zapped = 1;
+}
+
+/* Kill socket (only if zapped and orphan)
+ * Must be called on unlocked socket.
+ */
+static void l2cap_sock_kill(struct sock *sk)
+{
+	if (!sk->zapped || sk->socket)
+		return;
+
+	BT_DBG("sk %p state %d", sk, sk->state);
+
+	/* Kill poor orphan */
+	bluez_sock_unlink(&l2cap_sk_list, sk);
+	sk->dead = 1;
+	sock_put(sk);
+}
+
+/* Close socket.
+ */
+static void __l2cap_sock_close(struct sock *sk, int reason)
+{
+	BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
+
+	switch (sk->state) {
+	case BT_LISTEN:
+		l2cap_sock_cleanup_listen(sk);
+		break;
+
+	case BT_CONNECTED:
+	case BT_CONFIG:
+	case BT_CONNECT2:
+		if (sk->type == SOCK_SEQPACKET) {
+			struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+			l2cap_disconn_req req;
+
+			sk->state = BT_DISCONN;
+			l2cap_sock_set_timer(sk, sk->sndtimeo);
+
+			req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
+		} else {
+			l2cap_chan_del(sk, reason);
+		}
+		break;
+
+	case BT_CONNECT:
+	case BT_DISCONN:
+		l2cap_chan_del(sk, reason);
+		break;
+
+	default:
+		sk->zapped = 1;
+		break;
+	};
+}
+
+/* Must be called on unlocked socket. */
+static void l2cap_sock_close(struct sock *sk)
+{
+	l2cap_sock_clear_timer(sk);
+	lock_sock(sk);
+	__l2cap_sock_close(sk, ECONNRESET);
+	release_sock(sk);
+	l2cap_sock_kill(sk);
+}
+
+static void l2cap_sock_init(struct sock *sk, struct sock *parent)
+{
+	struct l2cap_pinfo *pi = l2cap_pi(sk);
+
+	BT_DBG("sk %p", sk);
+
+	if (parent) {
+		sk->type = parent->type;
+		pi->imtu = l2cap_pi(parent)->imtu;
+		pi->omtu = l2cap_pi(parent)->omtu;
+		pi->link_mode = l2cap_pi(parent)->link_mode;
+	} else {
+		pi->imtu = L2CAP_DEFAULT_MTU;
+		pi->omtu = 0;
+		pi->link_mode = 0;
+	}
+
+	/* Default config options */
+	pi->conf_mtu = L2CAP_DEFAULT_MTU;
+	pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+}
+
+static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio)
+{
+	struct sock *sk;
+
+	if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1)))
+		return NULL;
+
+	bluez_sock_init(sock, sk);
+	
+	sk->zapped   = 0;
+
+	sk->destruct = l2cap_sock_destruct;
+	sk->sndtimeo = L2CAP_CONN_TIMEOUT;
+
+	sk->protocol = proto;
+	sk->state    = BT_OPEN;
+
+	l2cap_sock_init_timer(sk);
+
+	bluez_sock_link(&l2cap_sk_list, sk);
+
+	MOD_INC_USE_COUNT;
+	return sk;
+}
+
+static int l2cap_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	sock->state = SS_UNCONNECTED;
+
+	if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	if (sock->type == SOCK_RAW && !capable(CAP_NET_RAW))
+		return -EPERM;
+	
+	sock->ops = &l2cap_sock_ops;
+
+	if (!(sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL)))
+		return -ENOMEM;
+
+	l2cap_sock_init(sk, NULL);
+	return 0;
+}
+
+static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
+
+	if (!addr || addr->sa_family != AF_BLUETOOTH)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_OPEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	write_lock_bh(&l2cap_sk_list.lock);
+	if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {
+		err = -EADDRINUSE;
+	} else {
+		/* Save source address */
+		bacpy(&bluez_pi(sk)->src, &la->l2_bdaddr);
+		l2cap_pi(sk)->psm = la->l2_psm;
+		sk->sport = la->l2_psm;
+		sk->state = BT_BOUND;
+	}
+	write_unlock_bh(&l2cap_sk_list.lock);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_do_connect(struct sock *sk)
+{
+	bdaddr_t *src = &bluez_pi(sk)->src;
+	bdaddr_t *dst = &bluez_pi(sk)->dst;
+	struct l2cap_conn *conn;
+	struct hci_conn   *hcon;
+	struct hci_dev    *hdev;
+	int err = 0;
+
+	BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
+
+	if (!(hdev = hci_get_route(dst, src)))
+		return -EHOSTUNREACH;
+
+	hci_dev_lock_bh(hdev);
+
+	err = -ENOMEM;
+
+	hcon = hci_connect(hdev, ACL_LINK, dst);
+	if (!hcon)
+		goto done;
+
+	conn = l2cap_conn_add(hcon, 0);
+	if (!conn) {
+		hci_conn_put(hcon);
+		goto done;
+	}
+
+	err = 0;
+
+	/* Update source addr of the socket */
+	bacpy(src, conn->src);
+
+	l2cap_chan_add(conn, sk, NULL);
+
+	sk->state = BT_CONNECT;
+	l2cap_sock_set_timer(sk, sk->sndtimeo);
+
+	if (hcon->state == BT_CONNECTED) {
+		if (sk->type == SOCK_SEQPACKET) {
+			l2cap_conn_req req;
+			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			req.psm  = l2cap_pi(sk)->psm;
+			l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
+		} else {
+			l2cap_sock_clear_timer(sk);
+			sk->state = BT_CONNECTED;
+		}
+	}
+
+done:
+	hci_dev_unlock_bh(hdev);
+	hci_dev_put(hdev);
+	return err;
+}
+
+static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+{
+	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	lock_sock(sk);
+
+	BT_DBG("sk %p", sk);
+
+	if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	if (sk->type == SOCK_SEQPACKET && !la->l2_psm) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	switch(sk->state) {
+	case BT_CONNECT:
+	case BT_CONNECT2:
+	case BT_CONFIG:
+		/* Already connecting */
+		goto wait;
+
+	case BT_CONNECTED:
+		/* Already connected */
+		goto done;
+
+	case BT_OPEN:
+	case BT_BOUND:
+		/* Can connect */
+		break;
+
+	default:
+		err = -EBADFD;
+		goto done;
+	}
+
+	/* Set destination address and psm */
+	bacpy(&bluez_pi(sk)->dst, &la->l2_bdaddr);
+	l2cap_pi(sk)->psm = la->l2_psm;
+
+	if ((err = l2cap_do_connect(sk)))
+		goto done;
+
+wait:
+	err = bluez_sock_wait_state(sk, BT_CONNECTED,
+			sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+int l2cap_sock_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p backlog %d", sk, backlog);
+
+	lock_sock(sk);
+
+	if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	if (!l2cap_pi(sk)->psm) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	sk->max_ack_backlog = backlog;
+	sk->ack_backlog = 0;
+	sk->state = BT_LISTEN;
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct sock *sk = sock->sk, *nsk;
+	long timeo;
+	int err = 0;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_LISTEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
+
+	BT_DBG("sk %p timeo %ld", sk, timeo);
+
+	/* Wait for an incoming connection. (wake-one). */
+	add_wait_queue_exclusive(sk->sleep, &wait);
+	while (!(nsk = bluez_accept_dequeue(sk, newsock))) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!timeo) {
+			err = -EAGAIN;
+			break;
+		}
+
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+
+		if (sk->state != BT_LISTEN) {
+			err = -EBADFD;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			err = sock_intr_errno(timeo);
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+
+	if (err)
+		goto done;
+
+	newsock->state = SS_CONNECTED;
+
+	BT_DBG("new socket %p", nsk);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
+{
+	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	addr->sa_family = AF_BLUETOOTH;
+	*len = sizeof(struct sockaddr_l2);
+
+	if (peer)
+		bacpy(&la->l2_bdaddr, &bluez_pi(sk)->dst);
+	else
+		bacpy(&la->l2_bdaddr, &bluez_pi(sk)->src);
+
+	la->l2_psm = l2cap_pi(sk)->psm;
+	return 0;
+}
+
+static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (sk->err)
+		return sock_error(sk);
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	/* Check outgoing MTU */
+	if (len > l2cap_pi(sk)->omtu)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (sk->state == BT_CONNECTED)
+		err = l2cap_chan_send(sk, msg, len);
+	else
+		err = -ENOTCONN;
+
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+	struct sock *sk = sock->sk;
+	struct l2cap_options opts;
+	int err = 0, len;
+	__u32 opt;
+
+	BT_DBG("sk %p", sk);
+
+	lock_sock(sk);
+
+	switch (optname) {
+	case L2CAP_OPTIONS:
+		len = MIN(sizeof(opts), optlen);
+		if (copy_from_user((char *)&opts, optval, len)) {
+			err = -EFAULT;
+			break;
+		}
+		l2cap_pi(sk)->imtu  = opts.imtu;
+		l2cap_pi(sk)->omtu  = opts.omtu;
+		break;
+
+	case L2CAP_LM:
+		if (get_user(opt, (__u32 *)optval)) {
+			err = -EFAULT;
+			break;
+		}
+
+		l2cap_pi(sk)->link_mode = opt;
+		break;
+
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	}
+
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+{
+	struct sock *sk = sock->sk;
+	struct l2cap_options opts;
+	struct l2cap_conninfo cinfo;
+	int len, err = 0; 
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	lock_sock(sk);
+
+	switch (optname) {
+	case L2CAP_OPTIONS:
+		opts.imtu     = l2cap_pi(sk)->imtu;
+		opts.omtu     = l2cap_pi(sk)->omtu;
+		opts.flush_to = l2cap_pi(sk)->flush_to;
+
+		len = MIN(len, sizeof(opts));
+		if (copy_to_user(optval, (char *)&opts, len))
+			err = -EFAULT;
+
+		break;
+
+	case L2CAP_LM:
+		if (put_user(l2cap_pi(sk)->link_mode, (__u32 *)optval))
+			err = -EFAULT;
+		break;
+
+	case L2CAP_CONNINFO:
+		if (sk->state != BT_CONNECTED) {
+			err = -ENOTCONN;
+			break;
+		}
+
+		cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle;
+
+		len = MIN(len, sizeof(cinfo));
+		if (copy_to_user(optval, (char *)&cinfo, len))
+			err = -EFAULT;
+
+		break;
+
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	}
+
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_sock_shutdown(struct socket *sock, int how)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (!sk) return 0;
+
+	lock_sock(sk);
+	if (!sk->shutdown) {
+		sk->shutdown = SHUTDOWN_MASK;
+		l2cap_sock_clear_timer(sk);
+		__l2cap_sock_close(sk, 0);
+
+		if (sk->linger)
+			err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
+	}
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	int err;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (!sk) return 0;
+
+	err = l2cap_sock_shutdown(sock, 2);
+
+	sock_orphan(sk);
+	l2cap_sock_kill(sk);
+	return err;
+}
+
+/* --------- L2CAP channels --------- */
+static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
+{
+	struct sock *s;
+	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
+		if (l2cap_pi(s)->dcid == cid)
+			break;
+	}
+	return s;
+}
+
+static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
+{
+	struct sock *s;
+	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
+		if (l2cap_pi(s)->scid == cid)
+			break;
+	}
+	return s;
+}
+
+/* Find channel with given SCID.
+ * Returns locked socket */
+static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
+{
+	struct sock *s;
+	read_lock(&l->lock);
+	s = __l2cap_get_chan_by_scid(l, cid);
+	if (s) bh_lock_sock(s);
+	read_unlock(&l->lock);
+	return s;
+}
+
+static __u16 l2cap_alloc_cid(struct l2cap_chan_list *l)
+{
+	__u16 cid = 0x0040;
+
+	for (; cid < 0xffff; cid++) {
+		if(!__l2cap_get_chan_by_scid(l, cid))
+			return cid;
+	}
+
+	return 0;
+}
+
+static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk)
+{
+	sock_hold(sk);
+
+	if (l->head)
+		l2cap_pi(l->head)->prev_c = sk;
+
+	l2cap_pi(sk)->next_c = l->head;
+	l2cap_pi(sk)->prev_c = NULL;
+	l->head = sk;
+}
+
+static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk)
+{
+	struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c;
+
+	write_lock(&l->lock);
+	if (sk == l->head)
+		l->head = next;
+
+	if (next)
+		l2cap_pi(next)->prev_c = prev;
+	if (prev)
+		l2cap_pi(prev)->next_c = next;
+	write_unlock(&l->lock);
+
+	__sock_put(sk);
+}
+
+static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+{
+	struct l2cap_chan_list *l = &conn->chan_list;
+
+	BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
+
+	l2cap_pi(sk)->conn = conn;
+
+	if (sk->type == SOCK_SEQPACKET) {
+		/* Alloc CID for connection-oriented socket */
+		l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
+	} else if (sk->type == SOCK_DGRAM) {
+		/* Connectionless socket */
+		l2cap_pi(sk)->scid = 0x0002;
+		l2cap_pi(sk)->dcid = 0x0002;
+		l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
+	} else {
+		/* Raw socket can send/recv signalling messages only */
+		l2cap_pi(sk)->scid = 0x0001;
+		l2cap_pi(sk)->dcid = 0x0001;
+		l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
+	}
+
+	__l2cap_chan_link(l, sk);
+
+	if (parent)
+		bluez_accept_enqueue(parent, sk);
+}
+
+static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+{
+	struct l2cap_chan_list *l = &conn->chan_list;
+	write_lock(&l->lock);
+	__l2cap_chan_add(conn, sk, parent);
+	write_unlock(&l->lock);
+}
+
+/* Delete channel. 
+ * Must be called on the locked socket. */
+static void l2cap_chan_del(struct sock *sk, int err)
+{
+	struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+	struct sock *parent = bluez_pi(sk)->parent;
+
+	l2cap_sock_clear_timer(sk);
+
+	BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
+
+	if (conn) { 
+		/* Unlink from channel list */
+		l2cap_chan_unlink(&conn->chan_list, sk);
+		l2cap_pi(sk)->conn = NULL;
+		hci_conn_put(conn->hcon);
+	}
+
+	sk->state  = BT_CLOSED;
+	sk->zapped = 1;
+
+	if (err)
+		sk->err = err;
+
+	if (parent)
+		parent->data_ready(parent, 0);
+	else
+		sk->state_change(sk);
+}
+
+static void l2cap_conn_ready(struct l2cap_conn *conn)
+{
+	struct l2cap_chan_list *l = &conn->chan_list;
+	struct sock *sk;
+
+	BT_DBG("conn %p", conn);
+
+	read_lock(&l->lock);
+
+	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+		bh_lock_sock(sk);
+
+		if (sk->type != SOCK_SEQPACKET) {
+			l2cap_sock_clear_timer(sk);
+			sk->state = BT_CONNECTED;
+			sk->state_change(sk);
+		} else if (sk->state == BT_CONNECT) {
+			l2cap_conn_req req;
+			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			req.psm  = l2cap_pi(sk)->psm;
+			l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
+		}
+
+		bh_unlock_sock(sk);
+	}
+
+	read_unlock(&l->lock);
+}
+
+/* Notify sockets that we cannot guaranty reliability anymore */
+static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
+{
+	struct l2cap_chan_list *l = &conn->chan_list;
+	struct sock *sk;
+
+	BT_DBG("conn %p", conn);
+
+	read_lock(&l->lock);
+	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+		if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE)
+			sk->err = err;
+	}
+	read_unlock(&l->lock);
+}
+
+static void l2cap_chan_ready(struct sock *sk)
+{
+	struct sock *parent = bluez_pi(sk)->parent;
+
+	BT_DBG("sk %p, parent %p", sk, parent);
+
+	l2cap_pi(sk)->conf_state = 0;
+	l2cap_sock_clear_timer(sk);
+
+	if (!parent) {
+		/* Outgoing channel.
+		 * Wake up socket sleeping on connect.
+		 */
+		sk->state = BT_CONNECTED;
+		sk->state_change(sk);
+	} else {
+		/* Incomming channel.
+		 * Wake up socket sleeping on accept.
+		 */
+		parent->data_ready(parent, 0);
+	}
+}
+
+/* Copy frame to all raw sockets on that connection */
+void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	struct l2cap_chan_list *l = &conn->chan_list;
+	struct sk_buff *nskb;
+	struct sock * sk;
+
+	BT_DBG("conn %p", conn);
+
+	read_lock(&l->lock);
+	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+		if (sk->type != SOCK_RAW)
+			continue;
+
+		/* Don't send frame to the socket it came from */
+		if (skb->sk == sk)
+			continue;
+
+		if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
+			continue;
+
+		if (sock_queue_rcv_skb(sk, nskb))
+			kfree_skb(nskb);
+	}
+	read_unlock(&l->lock);
+}
+
+static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len)
+{
+	struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+	struct sk_buff *skb, **frag;
+	int err, hlen, count, sent=0;
+	l2cap_hdr *lh;
+
+	BT_DBG("sk %p len %d", sk, len);
+
+	/* First fragment (with L2CAP header) */
+	if (sk->type == SOCK_DGRAM)
+		hlen = L2CAP_HDR_SIZE + 2;
+	else
+		hlen = L2CAP_HDR_SIZE;
+
+	count = MIN(conn->mtu - hlen, len);
+
+	skb = bluez_skb_send_alloc(sk, hlen + count,
+			msg->msg_flags & MSG_DONTWAIT, &err);
+	if (!skb)
+		return err;
+
+	/* Create L2CAP header */
+	lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+	lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+	lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+
+	if (sk->type == SOCK_DGRAM)
+		put_unaligned(l2cap_pi(sk)->psm, (__u16 *) skb_put(skb, 2));
+
+	if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
+		err = -EFAULT;
+		goto fail;
+	}
+
+	sent += count;
+	len  -= count;
+
+	/* Continuation fragments (no L2CAP header) */
+	frag = &skb_shinfo(skb)->frag_list;
+	while (len) {
+		count = MIN(conn->mtu, len);
+
+		*frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
+		if (!*frag)
+			goto fail;
+		
+		if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) {
+			err = -EFAULT;
+			goto fail;
+		}
+
+		sent += count;
+		len  -= count;
+
+		frag = &(*frag)->next;
+	}
+
+	if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0)
+		goto fail;
+
+	return sent;
+
+fail:
+	kfree_skb(skb);
+	return err;
+}
+
+/* --------- L2CAP signalling commands --------- */
+static inline __u8 l2cap_get_ident(struct l2cap_conn *conn)
+{
+	__u8 id;
+
+	/* Get next available identificator.
+	 *    1 - 199 are used by kernel.
+	 *  200 - 254 are used by utilities like l2ping, etc 
+	 */
+
+	spin_lock(&conn->lock);
+
+	if (++conn->tx_ident > 199)
+		conn->tx_ident = 1;
+
+	id = conn->tx_ident;
+
+	spin_unlock(&conn->lock);
+
+	return id;
+}
+
+static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
+				__u8 code, __u8 ident, __u16 dlen, void *data)
+{
+	struct sk_buff *skb, **frag;
+	l2cap_cmd_hdr *cmd;
+	l2cap_hdr *lh;
+	int len, count;
+
+	BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", conn, code, ident, dlen);
+
+	len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen;
+	count = MIN(conn->mtu, len);
+	
+	skb = bluez_skb_alloc(count, GFP_ATOMIC);
+	if (!skb)
+		return NULL;
+
+	lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+	lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
+	lh->cid = __cpu_to_le16(0x0001);
+
+	cmd = (l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
+	cmd->code  = code;
+	cmd->ident = ident;
+	cmd->len   = __cpu_to_le16(dlen);
+
+	if (dlen) {
+		count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE;
+		memcpy(skb_put(skb, count), data, count);
+		data += count;
+	}
+
+	len -= skb->len;
+	
+	/* Continuation fragments (no L2CAP header) */
+	frag = &skb_shinfo(skb)->frag_list;
+	while (len) {
+		count = MIN(conn->mtu, len);
+
+		*frag = bluez_skb_alloc(count, GFP_ATOMIC);
+		if (!*frag)
+			goto fail;
+		
+		memcpy(skb_put(*frag, count), data, count);
+
+		len  -= count;
+		data += count;
+		
+		frag = &(*frag)->next;
+	}
+
+	return skb;
+
+fail:
+	kfree_skb(skb);
+	return NULL;
+}
+
+static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data)
+{
+	__u8 ident = l2cap_get_ident(conn);
+	struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
+
+	BT_DBG("code 0x%2.2x", code);
+
+	if (!skb)
+		return -ENOMEM;
+	return hci_send_acl(conn->hcon, skb, 0);
+}
+
+static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data)
+{
+	struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
+
+	BT_DBG("code 0x%2.2x", code);
+
+	if (!skb)
+		return -ENOMEM;
+	return hci_send_acl(conn->hcon, skb, 0);
+}
+
+static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val)
+{
+	l2cap_conf_opt *opt = *ptr;
+	int len;
+
+	len = L2CAP_CONF_OPT_SIZE + opt->len;
+	*ptr += len;
+
+	*type = opt->type;
+	*olen = opt->len;
+
+	switch (opt->len) {
+	case 1:
+		*val = *((__u8 *) opt->val);
+		break;
+
+	case 2:
+		*val = __le16_to_cpu(*((__u16 *)opt->val));
+		break;
+
+	case 4:
+		*val = __le32_to_cpu(*((__u32 *)opt->val));
+		break;
+
+	default:
+		*val = (unsigned long) opt->val;
+		break;
+	};
+
+	BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val);
+	return len;
+}
+
+static inline void l2cap_parse_conf_req(struct sock *sk, void *data, int len)
+{
+	int type, hint, olen; 
+	unsigned long val;
+	void *ptr = data;
+
+	BT_DBG("sk %p len %d", sk, len);
+
+	while (len >= L2CAP_CONF_OPT_SIZE) {
+		len -= l2cap_get_conf_opt(&ptr, &type, &olen, &val);
+
+		hint  = type & 0x80;
+		type &= 0x7f;
+
+		switch (type) {
+		case L2CAP_CONF_MTU:
+			l2cap_pi(sk)->conf_mtu = val;
+			break;
+
+		case L2CAP_CONF_FLUSH_TO:
+			l2cap_pi(sk)->flush_to = val;
+			break;
+
+		case L2CAP_CONF_QOS:
+			break;
+		
+		default:
+			if (hint)
+				break;
+
+			/* FIXME: Reject unknown option */
+			break;
+		};
+	}
+}
+
+static void l2cap_add_conf_opt(void **ptr, __u8 type, __u8 len, unsigned long val)
+{
+	register l2cap_conf_opt *opt = *ptr;
+
+	BT_DBG("type 0x%2.2x len %d val 0x%lx", type, len, val);
+
+	opt->type = type;
+	opt->len  = len;
+
+	switch (len) {
+	case 1:
+		*((__u8 *) opt->val)  = val;
+		break;
+
+	case 2:
+		*((__u16 *) opt->val) = __cpu_to_le16(val);
+		break;
+
+	case 4:
+		*((__u32 *) opt->val) = __cpu_to_le32(val);
+		break;
+
+	default:
+		memcpy(opt->val, (void *) val, len);
+		break;
+	};
+
+	*ptr += L2CAP_CONF_OPT_SIZE + len;
+}
+
+static int l2cap_build_conf_req(struct sock *sk, void *data)
+{
+	struct l2cap_pinfo *pi = l2cap_pi(sk);
+	l2cap_conf_req *req = (l2cap_conf_req *) data;
+	void *ptr = req->data;
+
+	BT_DBG("sk %p", sk);
+
+	if (pi->imtu != L2CAP_DEFAULT_MTU)
+		l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
+
+	/* FIXME. Need actual value of the flush timeout */
+	//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
+	//   l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
+
+	req->dcid  = __cpu_to_le16(pi->dcid);
+	req->flags = __cpu_to_le16(0);
+
+	return ptr - data;
+}
+
+static inline int l2cap_conf_output(struct sock *sk, void **ptr)
+{
+	struct l2cap_pinfo *pi = l2cap_pi(sk);
+	int result = 0;
+
+	/* Configure output options and let the other side know
+	 * which ones we don't like.
+	 */
+	if (pi->conf_mtu < pi->omtu) {
+		l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, pi->omtu);
+		result = L2CAP_CONF_UNACCEPT;
+	} else {
+		pi->omtu = pi->conf_mtu;
+	}
+
+	BT_DBG("sk %p result %d", sk, result);
+	return result;
+}
+
+static int l2cap_build_conf_rsp(struct sock *sk, void *data, int *result)
+{
+	l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data;
+	void *ptr = rsp->data;
+	u16 flags = 0;
+
+	BT_DBG("sk %p complete %d", sk, result ? 1 : 0);
+
+	if (result)
+		*result = l2cap_conf_output(sk, &ptr);
+	else	
+		flags |= 0x0001;
+
+	rsp->scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
+	rsp->result = __cpu_to_le16(result ? *result : 0);
+	rsp->flags  = __cpu_to_le16(flags);
+
+	return ptr - data;
+}
+
+static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	struct l2cap_chan_list *list = &conn->chan_list;
+	l2cap_conn_req *req = (l2cap_conn_req *) data;
+	l2cap_conn_rsp rsp;
+	struct sock *sk, *parent;
+	int result = 0, status = 0;
+
+	__u16 dcid = 0, scid = __le16_to_cpu(req->scid);
+	__u16 psm  = req->psm;
+
+	BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
+
+	/* Check if we have socket listening on psm */
+	parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src);
+	if (!parent) {
+		result = L2CAP_CR_BAD_PSM;
+		goto sendresp;
+	}
+
+	result = L2CAP_CR_NO_MEM;
+
+	/* Check for backlog size */
+	if (parent->ack_backlog > parent->max_ack_backlog) {
+		BT_DBG("backlog full %d", parent->ack_backlog); 
+		goto response;
+	}
+
+	sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC);
+	if (!sk)
+		goto response;
+
+	write_lock(&list->lock);
+
+	/* Check if we already have channel with that dcid */
+	if (__l2cap_get_chan_by_dcid(list, scid)) {
+		write_unlock(&list->lock);
+		sk->zapped = 1;
+		l2cap_sock_kill(sk);
+		goto response;
+	}
+
+	hci_conn_hold(conn->hcon);
+
+	l2cap_sock_init(sk, parent);
+	bacpy(&bluez_pi(sk)->src, conn->src);
+	bacpy(&bluez_pi(sk)->dst, conn->dst);
+	l2cap_pi(sk)->psm  = psm;
+	l2cap_pi(sk)->dcid = scid;
+
+	__l2cap_chan_add(conn, sk, parent);
+	dcid = l2cap_pi(sk)->scid;
+
+	l2cap_sock_set_timer(sk, sk->sndtimeo);
+
+	/* Service level security */
+	result = L2CAP_CR_PEND;
+	status = L2CAP_CS_AUTHEN_PEND;
+	sk->state = BT_CONNECT2;
+	l2cap_pi(sk)->ident = cmd->ident;
+	
+	if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) {
+		if (!hci_conn_encrypt(conn->hcon))
+			goto done;
+	} else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {
+		if (!hci_conn_auth(conn->hcon))
+			goto done;
+	}
+
+	sk->state = BT_CONFIG;
+	result = status = 0;
+
+done:
+	write_unlock(&list->lock);
+
+response:
+	bh_unlock_sock(parent);
+
+sendresp:
+	rsp.scid   = __cpu_to_le16(scid);
+	rsp.dcid   = __cpu_to_le16(dcid);
+	rsp.result = __cpu_to_le16(result);
+	rsp.status = __cpu_to_le16(status);
+	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
+	return 0;
+}
+
+static inline int l2cap_connect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	l2cap_conn_rsp *rsp = (l2cap_conn_rsp *) data;
+	__u16 scid, dcid, result, status;
+	struct sock *sk;
+	char req[128];
+
+	scid   = __le16_to_cpu(rsp->scid);
+	dcid   = __le16_to_cpu(rsp->dcid);
+	result = __le16_to_cpu(rsp->result);
+	status = __le16_to_cpu(rsp->status);
+
+	BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
+
+	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+		return -ENOENT;
+
+	switch (result) {
+	case L2CAP_CR_SUCCESS:
+		sk->state = BT_CONFIG;
+		l2cap_pi(sk)->dcid = dcid;
+		l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+
+		l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+		break;
+
+	case L2CAP_CR_PEND:
+		break;
+
+	default:
+		l2cap_chan_del(sk, ECONNREFUSED);
+		break;
+	}
+
+	bh_unlock_sock(sk);
+	return 0;
+}
+
+static inline int l2cap_config_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	l2cap_conf_req * req = (l2cap_conf_req *) data;
+	__u16 dcid, flags;
+	__u8 rsp[64];
+	struct sock *sk;
+	int result;
+
+	dcid  = __le16_to_cpu(req->dcid);
+	flags = __le16_to_cpu(req->flags);
+
+	BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
+
+	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
+		return -ENOENT;
+
+	l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
+
+	if (flags & 0x0001) {
+		/* Incomplete config. Send empty response. */
+		l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
+		goto unlock;
+	}
+
+	/* Complete config. */
+	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp);
+
+	if (result)
+		goto unlock;
+
+	/* Output config done */
+	l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+
+	if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
+		sk->state = BT_CONNECTED;
+		l2cap_chan_ready(sk);
+	} else if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) {
+		char req[64];
+		l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+	}
+
+unlock:
+	bh_unlock_sock(sk);
+	return 0;
+}
+
+static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data;
+	__u16 scid, flags, result;
+	struct sock *sk;
+	int err = 0;
+
+	scid   = __le16_to_cpu(rsp->scid);
+	flags  = __le16_to_cpu(rsp->flags);
+	result = __le16_to_cpu(rsp->result);
+
+	BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result);
+
+	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+		return -ENOENT;
+
+	switch (result) {
+	case L2CAP_CONF_SUCCESS:
+		break;
+
+	case L2CAP_CONF_UNACCEPT:
+		if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) {
+			char req[128];
+			/* 
+			   It does not make sense to adjust L2CAP parameters 
+			   that are currently defined in the spec. We simply 
+			   resend config request that we sent earlier. It is
+			   stupid :) but it helps qualification testing
+			   which expects at least some response from us.
+			*/
+			l2cap_send_req(conn, L2CAP_CONF_REQ,
+				l2cap_build_conf_req(sk, req), req);
+			goto done;
+		}
+	default: 
+		sk->state = BT_DISCONN;
+		sk->err   = ECONNRESET;
+		l2cap_sock_set_timer(sk, HZ * 5);
+		{
+			l2cap_disconn_req req;
+			req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
+		}
+		goto done;
+	}
+
+	if (flags & 0x01)
+		goto done;
+
+	/* Input config done */
+	l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;
+
+	if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+		sk->state = BT_CONNECTED;
+		l2cap_chan_ready(sk);
+	}
+
+done:
+	bh_unlock_sock(sk);
+	return err;
+}
+
+static inline int l2cap_disconnect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	l2cap_disconn_req *req = (l2cap_disconn_req *) data;
+	l2cap_disconn_rsp rsp;
+	__u16 dcid, scid;
+	struct sock *sk;
+
+	scid = __le16_to_cpu(req->scid);
+	dcid = __le16_to_cpu(req->dcid);
+
+	BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
+
+	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
+		return 0;
+
+	rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
+	rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+	l2cap_send_rsp(conn, cmd->ident, L2CAP_DISCONN_RSP, L2CAP_DISCONN_RSP_SIZE, &rsp);
+
+	sk->shutdown = SHUTDOWN_MASK;
+	
+	l2cap_chan_del(sk, ECONNRESET);
+	bh_unlock_sock(sk);
+
+	l2cap_sock_kill(sk);
+	return 0;
+}
+
+static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	l2cap_disconn_rsp *rsp = (l2cap_disconn_rsp *) data;
+	__u16 dcid, scid;
+	struct sock *sk;
+
+	scid = __le16_to_cpu(rsp->scid);
+	dcid = __le16_to_cpu(rsp->dcid);
+
+	BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
+
+	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+		return 0;
+	l2cap_chan_del(sk, 0);
+	bh_unlock_sock(sk);
+
+	l2cap_sock_kill(sk);
+	return 0;
+}
+
+static inline int l2cap_information_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
+{
+	l2cap_info_req *req = (l2cap_info_req *) data;
+	l2cap_info_rsp rsp;
+	u16 type;
+
+	type = __le16_to_cpu(req->type);
+
+	BT_DBG("type 0x%4.4x", type);
+
+	rsp.type   = __cpu_to_le16(type);
+	rsp.result = __cpu_to_le16(L2CAP_IR_NOTSUPP);
+	l2cap_send_rsp(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp);
+	return 0;
+}
+
+static inline int l2cap_information_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
+{
+	l2cap_info_rsp *rsp = (l2cap_info_rsp *) data;
+	u16 type, result;
+
+	type   = __le16_to_cpu(rsp->type);
+	result = __le16_to_cpu(rsp->result);
+
+	BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
+
+	return 0;
+}
+
+static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	__u8 *data = skb->data;
+	int len = skb->len;
+	l2cap_cmd_hdr cmd;
+	int err = 0;
+
+	l2cap_raw_recv(conn, skb);
+
+	while (len >= L2CAP_CMD_HDR_SIZE) {
+		memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
+		data += L2CAP_CMD_HDR_SIZE;
+		len  -= L2CAP_CMD_HDR_SIZE;
+
+		cmd.len = __le16_to_cpu(cmd.len);
+
+		BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd.len, cmd.ident);
+
+		if (cmd.len > len || !cmd.ident) {
+			BT_DBG("corrupted command");
+			break;
+		}
+
+		switch (cmd.code) {
+		case L2CAP_COMMAND_REJ:
+			/* FIXME: We should process this */
+			break;
+
+		case L2CAP_CONN_REQ:
+			err = l2cap_connect_req(conn, &cmd, data);
+			break;
+
+		case L2CAP_CONN_RSP:
+			err = l2cap_connect_rsp(conn, &cmd, data);
+			break;
+
+		case L2CAP_CONF_REQ:
+			err = l2cap_config_req(conn, &cmd, data);
+			break;
+
+		case L2CAP_CONF_RSP:
+			err = l2cap_config_rsp(conn, &cmd, data);
+			break;
+
+		case L2CAP_DISCONN_REQ:
+			err = l2cap_disconnect_req(conn, &cmd, data);
+			break;
+
+		case L2CAP_DISCONN_RSP:
+			err = l2cap_disconnect_rsp(conn, &cmd, data);
+			break;
+
+		case L2CAP_ECHO_REQ:
+			l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data);
+			break;
+
+		case L2CAP_ECHO_RSP:
+			break;
+
+		case L2CAP_INFO_REQ:
+			err = l2cap_information_req(conn, &cmd, data);
+			break;
+
+		case L2CAP_INFO_RSP:
+			err = l2cap_information_rsp(conn, &cmd, data);
+			break;
+
+		default:
+			BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);
+			err = -EINVAL;
+			break;
+		};
+
+		if (err) {
+			l2cap_cmd_rej rej;
+			BT_DBG("error %d", err);
+
+			/* FIXME: Map err to a valid reason */
+			rej.reason = __cpu_to_le16(0);
+			l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej);
+		}
+
+		data += cmd.len;
+		len  -= cmd.len;
+	}
+
+	kfree_skb(skb);
+}
+
+static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct sk_buff *skb)
+{
+	struct sock *sk;
+
+	sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
+	if (!sk) {
+		BT_DBG("unknown cid 0x%4.4x", cid);
+		goto drop;
+	}
+
+	BT_DBG("sk %p, len %d", sk, skb->len);
+
+	if (sk->state != BT_CONNECTED)
+		goto drop;
+
+	if (l2cap_pi(sk)->imtu < skb->len)
+		goto drop;
+
+	/* If socket recv buffers overflows we drop data here 
+	 * which is *bad* because L2CAP has to be reliable. 
+	 * But we don't have any other choice. L2CAP doesn't 
+	 * provide flow control mechanism */ 
+	
+	if (!sock_queue_rcv_skb(sk, skb))
+		goto done;
+
+drop:
+	kfree_skb(skb);
+
+done:
+	if (sk) bh_unlock_sock(sk);
+	return 0;
+}
+
+static inline int l2cap_conless_channel(struct l2cap_conn *conn, __u16 psm, struct sk_buff *skb)
+{
+	struct sock *sk;
+
+	sk = l2cap_get_sock_by_psm(0, psm, conn->src);
+	if (!sk)
+		goto drop;
+
+	BT_DBG("sk %p, len %d", sk, skb->len);
+
+	if (sk->state != BT_BOUND && sk->state != BT_CONNECTED)
+		goto drop;
+
+	if (l2cap_pi(sk)->imtu < skb->len)
+		goto drop;
+
+	if (!sock_queue_rcv_skb(sk, skb))
+		goto done;
+
+drop:
+	kfree_skb(skb);
+
+done:
+	if (sk) bh_unlock_sock(sk);
+	return 0;
+}
+
+static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	l2cap_hdr *lh = (l2cap_hdr *) skb->data;
+	__u16 cid, psm, len;
+
+	skb_pull(skb, L2CAP_HDR_SIZE);
+	cid = __le16_to_cpu(lh->cid);
+	len = __le16_to_cpu(lh->len);
+
+	BT_DBG("len %d, cid 0x%4.4x", len, cid);
+
+	switch (cid) {
+	case 0x0001:
+		l2cap_sig_channel(conn, skb);
+		break;
+
+	case 0x0002:
+		psm = get_unaligned((__u16 *) skb->data);
+		skb_pull(skb, 2);
+		l2cap_conless_channel(conn, psm, skb);
+		break;
+		
+	default:
+		l2cap_data_channel(conn, cid, skb);
+		break;
+	}
+}
+
+/* ------------ L2CAP interface with lower layer (HCI) ------------- */
+
+static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
+{
+	int exact = 0, lm1 = 0, lm2 = 0;
+	register struct sock *sk;
+
+	if (type != ACL_LINK)
+		return 0;
+
+	BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
+
+	/* Find listening sockets and check their link_mode */
+	read_lock(&l2cap_sk_list.lock);
+	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
+		if (sk->state != BT_LISTEN)
+			continue;
+
+		if (!bacmp(&bluez_pi(sk)->src, &hdev->bdaddr)) {
+			lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
+			exact++;
+		} else if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
+			lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
+	}
+	read_unlock(&l2cap_sk_list.lock);
+
+	return exact ? lm1 : lm2;
+}
+
+static int l2cap_connect_cfm(struct hci_conn *hcon, __u8 status)
+{
+	BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
+
+	if (hcon->type != ACL_LINK)
+		return 0;
+
+	if (!status) {
+		struct l2cap_conn *conn;
+
+		conn = l2cap_conn_add(hcon, status);
+		if (conn)
+			l2cap_conn_ready(conn);
+	} else 
+		l2cap_conn_del(hcon, bterr(status));
+	
+	return 0;
+}
+
+static int l2cap_disconn_ind(struct hci_conn *hcon, __u8 reason)
+{
+	BT_DBG("hcon %p reason %d", hcon, reason);
+
+	if (hcon->type != ACL_LINK)
+		return 0;
+
+	l2cap_conn_del(hcon, bterr(reason));
+	return 0;
+}
+
+static int l2cap_auth_cfm(struct hci_conn *hcon, __u8 status)
+{
+	struct l2cap_chan_list *l;
+	struct l2cap_conn *conn;
+	l2cap_conn_rsp rsp;
+	struct sock *sk;
+	int result;
+	
+	if (!(conn = hcon->l2cap_data))
+		return 0;
+	l = &conn->chan_list;
+
+	BT_DBG("conn %p", conn);
+
+	read_lock(&l->lock);
+
+	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+		bh_lock_sock(sk);
+
+		if (sk->state != BT_CONNECT2 ||
+				(l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT)) {
+			bh_unlock_sock(sk);
+			continue;
+		}
+
+		if (!status) {
+			sk->state = BT_CONFIG;
+			result = 0;
+		} else {
+			sk->state = BT_DISCONN;
+			l2cap_sock_set_timer(sk, HZ/10);
+			result = L2CAP_CR_SEC_BLOCK;
+		}
+
+		rsp.scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
+		rsp.dcid   = __cpu_to_le16(l2cap_pi(sk)->scid);
+		rsp.result = __cpu_to_le16(result);
+		rsp.status = __cpu_to_le16(0);
+		l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP,
+			L2CAP_CONN_RSP_SIZE, &rsp);
+
+		bh_unlock_sock(sk);
+	}
+
+	read_unlock(&l->lock);
+	return 0;
+}
+
+static int l2cap_encrypt_cfm(struct hci_conn *hcon, __u8 status)
+{
+	struct l2cap_chan_list *l;
+	struct l2cap_conn *conn;
+	l2cap_conn_rsp rsp;
+	struct sock *sk;
+	int result;
+	
+	if (!(conn = hcon->l2cap_data))
+		return 0;
+	l = &conn->chan_list;
+
+	BT_DBG("conn %p", conn);
+
+	read_lock(&l->lock);
+
+	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+		bh_lock_sock(sk);
+
+		if (sk->state != BT_CONNECT2) {
+			bh_unlock_sock(sk);
+			continue;
+		}
+
+		if (!status) {
+			sk->state = BT_CONFIG;
+			result = 0;
+		} else {
+			sk->state = BT_DISCONN;
+			l2cap_sock_set_timer(sk, HZ/10);
+			result = L2CAP_CR_SEC_BLOCK;
+		}
+
+		rsp.scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
+		rsp.dcid   = __cpu_to_le16(l2cap_pi(sk)->scid);
+		rsp.result = __cpu_to_le16(result);
+		rsp.status = __cpu_to_le16(0);
+		l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, 
+			L2CAP_CONN_RSP_SIZE, &rsp);
+
+		bh_unlock_sock(sk);
+	}
+
+	read_unlock(&l->lock);
+	return 0;
+}
+
+static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16 flags)
+{
+	struct l2cap_conn *conn = hcon->l2cap_data;
+
+	if (!conn && !(conn = l2cap_conn_add(hcon, 0)))
+		goto drop;
+
+	BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
+
+	if (flags & ACL_START) {
+		l2cap_hdr *hdr;
+		int len;
+
+		if (conn->rx_len) {
+			BT_ERR("Unexpected start frame (len %d)", skb->len);
+			kfree_skb(conn->rx_skb);
+			conn->rx_skb = NULL;
+			conn->rx_len = 0;
+			l2cap_conn_unreliable(conn, ECOMM);
+		}
+
+		if (skb->len < 2) {
+			BT_ERR("Frame is too short (len %d)", skb->len);
+			l2cap_conn_unreliable(conn, ECOMM);
+			goto drop;
+		}
+
+		hdr = (l2cap_hdr *) skb->data;
+		len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
+
+		if (len == skb->len) {
+			/* Complete frame received */
+			l2cap_recv_frame(conn, skb);
+			return 0;
+		}
+
+		BT_DBG("Start: total len %d, frag len %d", len, skb->len);
+
+		if (skb->len > len) {
+			BT_ERR("Frame is too long (len %d, expected len %d)",
+				skb->len, len);
+			l2cap_conn_unreliable(conn, ECOMM);
+			goto drop;
+		}
+
+		/* Allocate skb for the complete frame including header */
+		conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC);
+		if (!conn->rx_skb)
+			goto drop;
+
+		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+		conn->rx_len = len - skb->len;
+	} else {
+		BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
+
+		if (!conn->rx_len) {
+			BT_ERR("Unexpected continuation frame (len %d)", skb->len);
+			l2cap_conn_unreliable(conn, ECOMM);
+			goto drop;
+		}
+
+		if (skb->len > conn->rx_len) {
+			BT_ERR("Fragment is too long (len %d, expected %d)",
+					skb->len, conn->rx_len);
+			kfree_skb(conn->rx_skb);
+			conn->rx_skb = NULL;
+			conn->rx_len = 0;
+			l2cap_conn_unreliable(conn, ECOMM);
+			goto drop;
+		}
+
+		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+		conn->rx_len -= skb->len;
+
+		if (!conn->rx_len) {
+			/* Complete frame received */
+			l2cap_recv_frame(conn, conn->rx_skb);
+			conn->rx_skb = NULL;
+		}
+	}
+
+drop:
+	kfree_skb(skb);
+	return 0;
+}
+
+/* ----- Proc fs support ------ */
+static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
+{
+	struct l2cap_pinfo *pi;
+	struct sock *sk;
+	char *ptr = buf;
+
+	read_lock_bh(&list->lock);
+
+	for (sk = list->head; sk; sk = sk->next) {
+		pi = l2cap_pi(sk);
+		ptr += sprintf(ptr, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n",
+				batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst), 
+				sk->state, pi->psm, pi->scid, pi->dcid, pi->imtu, pi->omtu,
+				pi->link_mode);
+	}
+
+	read_unlock_bh(&list->lock);
+
+	ptr += sprintf(ptr, "\n");
+	return ptr - buf;
+}
+
+static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
+{
+	char *ptr = buf;
+	int len;
+
+	BT_DBG("count %d, offset %ld", count, offset);
+
+	ptr += l2cap_sock_dump(ptr, &l2cap_sk_list);
+	len  = ptr - buf;
+
+	if (len <= count + offset)
+		*eof = 1;
+
+	*start = buf + offset;
+	len -= offset;
+
+	if (len > count)
+		len = count;
+	if (len < 0)
+		len = 0;
+
+	return len;
+}
+
+static struct proto_ops l2cap_sock_ops = {
+	family:		PF_BLUETOOTH,
+	release:	l2cap_sock_release,
+	bind:		l2cap_sock_bind,
+	connect:	l2cap_sock_connect,
+	listen:		l2cap_sock_listen,
+	accept:		l2cap_sock_accept,
+	getname:	l2cap_sock_getname,
+	sendmsg:	l2cap_sock_sendmsg,
+	recvmsg:	bluez_sock_recvmsg,
+	poll:		bluez_sock_poll,
+	socketpair:	sock_no_socketpair,
+	ioctl:		sock_no_ioctl,
+	shutdown:	l2cap_sock_shutdown,
+	setsockopt:	l2cap_sock_setsockopt,
+	getsockopt:	l2cap_sock_getsockopt,
+	mmap:		sock_no_mmap
+};
+
+static struct net_proto_family l2cap_sock_family_ops = {
+	family:		PF_BLUETOOTH,
+	create:		l2cap_sock_create
+};
+
+static struct hci_proto l2cap_hci_proto = {
+	name:		"L2CAP",
+	id:		HCI_PROTO_L2CAP,
+	connect_ind:	l2cap_connect_ind,
+	connect_cfm:	l2cap_connect_cfm,
+	disconn_ind:	l2cap_disconn_ind,
+	recv_acldata:	l2cap_recv_acldata,
+	auth_cfm:	l2cap_auth_cfm,
+	encrypt_cfm:	l2cap_encrypt_cfm
+};
+
+int __init l2cap_init(void)
+{
+	int err;
+
+	if ((err = bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops))) {
+		BT_ERR("Can't register L2CAP socket");
+		return err;
+	}
+
+	if ((err = hci_register_proto(&l2cap_hci_proto))) {
+		BT_ERR("Can't register L2CAP protocol");
+		return err;
+	}
+
+	create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL);
+
+	BT_INFO("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION);
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+	return 0;
+}
+
+void l2cap_cleanup(void)
+{
+	remove_proc_entry("bluetooth/l2cap", NULL);
+
+	/* Unregister socket and protocol */
+	if (bluez_sock_unregister(BTPROTO_L2CAP))
+		BT_ERR("Can't unregister L2CAP socket");
+
+	if (hci_unregister_proto(&l2cap_hci_proto))
+		BT_ERR("Can't unregister L2CAP protocol");
+}
+
+void l2cap_load(void)
+{
+	/* Dummy function to trigger automatic L2CAP module loading by 
+	   other modules that use L2CAP sockets but do not use any other
+	   symbols from it. */
+	return;
+}
+
+EXPORT_SYMBOL(l2cap_load);
+
+module_init(l2cap_init);
+module_exit(l2cap_cleanup);
+
+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION);
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/net/bluetooth/l2cap_core.c linux-2.4.18-mh15/net/bluetooth/l2cap_core.c
--- linux-2.4.18/net/bluetooth/l2cap_core.c	2001-09-30 21:26:08.000000000 +0200
+++ linux-2.4.18-mh15/net/bluetooth/l2cap_core.c	1970-01-01 01:00:00.000000000 +0100
@@ -1,2316 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * BlueZ L2CAP core and sockets.
- *
- * $Id: l2cap_core.c,v 1.19 2001/08/03 04:19:50 maxk Exp $
- */
-#define VERSION "1.1"
-
-#include <linux/config.h>
-#include <linux/module.h>
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/fcntl.h>
-#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/interrupt.h>
-#include <linux/socket.h>
-#include <linux/skbuff.h>
-#include <linux/proc_fs.h>
-#include <linux/list.h>
-#include <net/sock.h>
-
-#include <asm/system.h>
-#include <asm/uaccess.h>
-
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
-#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/l2cap_core.h>
-
-#ifndef L2CAP_DEBUG
-#undef  DBG
-#define DBG( A... )
-#endif
-
-struct proto_ops l2cap_sock_ops;
-
-struct bluez_sock_list l2cap_sk_list = {
-	lock: RW_LOCK_UNLOCKED
-};
-
-struct list_head l2cap_iff_list = LIST_HEAD_INIT(l2cap_iff_list);
-rwlock_t l2cap_rt_lock = RW_LOCK_UNLOCKED;
-
-static int  l2cap_conn_del(struct l2cap_conn *conn, int err);
-
-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
-static void l2cap_chan_del(struct sock *sk, int err);
-static int  l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
-
-static void l2cap_sock_close(struct sock *sk);
-static void l2cap_sock_kill(struct sock *sk);
-
-static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data);
-static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data);
-
-/* -------- L2CAP interfaces & routing --------- */
-/* Add/delete L2CAP interface.
- * Must be called with locked rt_lock
- */ 
-
-static void l2cap_iff_add(struct hci_dev *hdev)
-{
-	struct l2cap_iff *iff;
-
-	DBG("%s", hdev->name);
-
-	DBG("iff_list %p next %p prev %p", &l2cap_iff_list, l2cap_iff_list.next, l2cap_iff_list.prev);
-
-	/* Allocate new interface and lock HCI device */
-	if (!(iff = kmalloc(sizeof(struct l2cap_iff), GFP_KERNEL))) {
-		ERR("Can't allocate new interface %s", hdev->name);
-		return;
-	}
-	memset(iff, 0, sizeof(struct l2cap_iff));
-
-	hci_dev_hold(hdev);
-	hdev->l2cap_data = iff;
-	iff->hdev   = hdev;
-	iff->mtu    = hdev->acl_mtu - HCI_ACL_HDR_SIZE;
-	iff->bdaddr = &hdev->bdaddr;
-
-	spin_lock_init(&iff->lock);
-	INIT_LIST_HEAD(&iff->conn_list);
-
-	list_add(&iff->list, &l2cap_iff_list);
-}
-
-static void l2cap_iff_del(struct hci_dev *hdev)
-{
-	struct l2cap_iff *iff;
-
-	if (!(iff = hdev->l2cap_data))
-		return;
-
-	DBG("%s iff %p", hdev->name, iff);
-
-	list_del(&iff->list);
-
-	l2cap_iff_lock(iff);
-
-	/* Drop connections */
-	while (!list_empty(&iff->conn_list)) {
-		struct l2cap_conn *c;
-
-		c = list_entry(iff->conn_list.next, struct l2cap_conn, list);
-		l2cap_conn_del(c, ENODEV);
-	}
-
-	l2cap_iff_unlock(iff);
-
-	/* Unlock HCI device */
-	hdev->l2cap_data = NULL;
-	hci_dev_put(hdev);
-
-	kfree(iff);
-}
-
-/* Get route. Returns L2CAP interface.
- * Must be called with locked rt_lock
- */
-static struct l2cap_iff *l2cap_get_route(bdaddr_t *src, bdaddr_t *dst)
-{
-	struct list_head *p;
-	int use_src;
-
-	DBG("%s -> %s", batostr(src), batostr(dst));
-
-	use_src = bacmp(src, BDADDR_ANY) ? 0 : 1;
-	
-	/* Simple routing: 
-	 * 	No source address - find interface with bdaddr != dst 
-	 *	Source address 	  - find interface with bdaddr == src 
-	 */
-
-	list_for_each(p, &l2cap_iff_list) {
-		struct l2cap_iff *iff;
-
-		iff = list_entry(p, struct l2cap_iff, list);
-
-		if (use_src && !bacmp(iff->bdaddr, src))
-			return iff;
-		else if (bacmp(iff->bdaddr, dst))
-			return iff;
-	}
-	return NULL;
-}
-
-/* ----- L2CAP timers ------ */
-static void l2cap_sock_timeout(unsigned long arg)
-{
-	struct sock *sk = (struct sock *) arg;
-
-	DBG("sock %p state %d", sk, sk->state);
-
-	bh_lock_sock(sk);
-	switch (sk->state) {
-	case BT_DISCONN:
-		l2cap_chan_del(sk, ETIMEDOUT);
-		break;
-
-	default:
-		sk->err = ETIMEDOUT;
-		sk->state_change(sk);
-		break;
-	};
-	bh_unlock_sock(sk);
-
-	l2cap_sock_kill(sk);
-	sock_put(sk);
-}
-
-static void l2cap_sock_set_timer(struct sock *sk, long timeout)
-{
-	DBG("sock %p state %d timeout %ld", sk, sk->state, timeout);
-
-	if (!mod_timer(&sk->timer, jiffies + timeout))
-		sock_hold(sk);
-}
-
-static void l2cap_sock_clear_timer(struct sock *sk)
-{
-	DBG("sock %p state %d", sk, sk->state);
-
-	if (timer_pending(&sk->timer) && del_timer(&sk->timer))
-		__sock_put(sk);
-}
-
-static void l2cap_sock_init_timer(struct sock *sk)
-{
-	init_timer(&sk->timer);
-	sk->timer.function = l2cap_sock_timeout;
-	sk->timer.data = (unsigned long)sk;
-}
-
-static void l2cap_conn_timeout(unsigned long arg)
-{
-	struct l2cap_conn *conn = (void *)arg;
-	
-	DBG("conn %p state %d", conn, conn->state);
-
-	if (conn->state == BT_CONNECTED) {
-		hci_disconnect(conn->hconn, 0x13);
-	}
-		
-	return;
-}
-
-static void l2cap_conn_set_timer(struct l2cap_conn *conn, long timeout)
-{
-	DBG("conn %p state %d timeout %ld", conn, conn->state, timeout);
-
-	mod_timer(&conn->timer, jiffies + timeout);
-}
-
-static void l2cap_conn_clear_timer(struct l2cap_conn *conn)
-{
-	DBG("conn %p state %d", conn, conn->state);
-
-	del_timer(&conn->timer);
-}
-
-static void l2cap_conn_init_timer(struct l2cap_conn *conn)
-{
-	init_timer(&conn->timer);
-	conn->timer.function = l2cap_conn_timeout;
-	conn->timer.data = (unsigned long)conn;
-}
-
-/* -------- L2CAP connections --------- */
-/* Add new connection to the interface.
- * Interface must be locked
- */
-static struct l2cap_conn *l2cap_conn_add(struct l2cap_iff *iff, bdaddr_t *dst)
-{
-	struct l2cap_conn *conn;
-	bdaddr_t *src = iff->bdaddr;
-
-	if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_KERNEL)))
-		return NULL;
-
-	memset(conn, 0, sizeof(struct l2cap_conn));
-
-	conn->state = BT_OPEN;
-	conn->iff   = iff;
-	bacpy(&conn->src, src);
-	bacpy(&conn->dst, dst);
-
-	spin_lock_init(&conn->lock);
-	conn->chan_list.lock = RW_LOCK_UNLOCKED;
-
-	l2cap_conn_init_timer(conn);
-	
-	__l2cap_conn_link(iff, conn);
-
-	DBG("%s -> %s, %p", batostr(src), batostr(dst), conn);
-
-	MOD_INC_USE_COUNT;
-
-	return conn;
-}
-
-/* Delete connection on the interface.
- * Interface must be locked
- */
-static int l2cap_conn_del(struct l2cap_conn *conn, int err)
-{
-	struct sock *sk;
-
-	DBG("conn %p, state %d, err %d", conn, conn->state, err);
-
-	l2cap_conn_clear_timer(conn);
-	__l2cap_conn_unlink(conn->iff, conn);
-
-	conn->state = BT_CLOSED;
-
-	if (conn->rx_skb)
-		kfree_skb(conn->rx_skb);
-
-	/* Kill channels */
-	while ((sk = conn->chan_list.head)) {
-		bh_lock_sock(sk);
-		l2cap_sock_clear_timer(sk);
-		l2cap_chan_del(sk, err);
-		bh_unlock_sock(sk);
-
-		l2cap_sock_kill(sk);
-	}
-
-	kfree(conn);
-
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-static inline struct l2cap_conn *l2cap_get_conn_by_addr(struct l2cap_iff *iff, bdaddr_t *dst)
-{
-	struct list_head *p;
-
-	list_for_each(p, &iff->conn_list) {
-		struct l2cap_conn *c;
-
-		c = list_entry(p, struct l2cap_conn, list);
-		if (!bacmp(&c->dst, dst))
-			return c;
-	}
-	return NULL;
-}
-
-int l2cap_connect(struct sock *sk)
-{
-	bdaddr_t *src = &l2cap_pi(sk)->src;
-	bdaddr_t *dst = &l2cap_pi(sk)->dst;
-	struct l2cap_conn *conn;
-	struct l2cap_iff *iff;
-	int err = 0;
-
-	DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
-
-	read_lock_bh(&l2cap_rt_lock);
-
-	/* Get route to remote BD address */
-	if (!(iff = l2cap_get_route(src, dst))) {
-		err = -EHOSTUNREACH;
-		goto done;
-	}
-
-	/* Update source addr of the socket */
-	bacpy(src, iff->bdaddr);
-
-	l2cap_iff_lock(iff);
-
-	if (!(conn = l2cap_get_conn_by_addr(iff, dst))) {
-		/* Connection doesn't exist */
-		if (!(conn = l2cap_conn_add(iff, dst))) {
-			l2cap_iff_unlock(iff);
-			err = -ENOMEM;
-			goto done;
-		}
-		conn->out = 1;
-	}
-
-	l2cap_iff_unlock(iff);
-
-	l2cap_chan_add(conn, sk, NULL);
-
-	sk->state = BT_CONNECT;
-	l2cap_sock_set_timer(sk, sk->sndtimeo);
-
-	switch (conn->state) {
-	case BT_CONNECTED:
-		if (sk->type == SOCK_SEQPACKET) {
-			l2cap_conn_req req;
-			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
-			req.psm  = l2cap_pi(sk)->psm;
-			l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
-		} else {
-			l2cap_sock_clear_timer(sk);
-			sk->state = BT_CONNECTED;
-		}
-		break;
-
-	case BT_CONNECT:
-		break;
-
-	default:
-		/* Create ACL connection */
-		conn->state = BT_CONNECT;
-		hci_connect(iff->hdev, dst);
-		break;
-	};
-
-done:
-	read_unlock_bh(&l2cap_rt_lock);
-	return err;
-}
-
-/* ------ Channel queues for listening sockets ------ */
-void l2cap_accept_queue(struct sock *parent, struct sock *sk)
-{
-	struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
-
-	DBG("parent %p, sk %p", parent, sk);
-
-	sock_hold(sk);
-	l2cap_pi(sk)->parent = parent;
-	l2cap_pi(sk)->next_q = NULL;
-
-	if (!q->head) {
-		q->head = q->tail = sk;
-	} else {
-		struct sock *tail = q->tail;
-
-		l2cap_pi(sk)->prev_q = tail;
-		l2cap_pi(tail)->next_q = sk;
-		q->tail = sk;
-	}
-
-	parent->ack_backlog++;
-}
-
-void l2cap_accept_unlink(struct sock *sk)
-{
-	struct sock *parent = l2cap_pi(sk)->parent;
-	struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
-	struct sock *next, *prev;
-
-	DBG("sk %p", sk);
-
-	next = l2cap_pi(sk)->next_q;
-	prev = l2cap_pi(sk)->prev_q;
-
-	if (sk == q->head)
-		q->head = next;
-	if (sk == q->tail)
-		q->tail = prev;
-
-	if (next)
-		l2cap_pi(next)->prev_q = prev;
-	if (prev)
-		l2cap_pi(prev)->next_q = next;
-
-	l2cap_pi(sk)->parent = NULL;
-
-	parent->ack_backlog--;
-	__sock_put(sk);
-}
-
-/* Get next connected channel in queue. */
-struct sock *l2cap_accept_dequeue(struct sock *parent, int state)
-{
-	struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
-	struct sock *sk;
-
-	for (sk = q->head; sk; sk = l2cap_pi(sk)->next_q) {
-		if (!state || sk->state == state) {
-			l2cap_accept_unlink(sk);
-			break;
-		}
-	}
-
-	DBG("parent %p, sk %p", parent, sk);
-
-	return sk;
-}
-
-/* -------- Socket interface ---------- */
-static struct sock *__l2cap_get_sock_by_addr(struct sockaddr_l2 *addr)
-{
-	bdaddr_t *src = &addr->l2_bdaddr;
-	__u16 psm = addr->l2_psm;
-	struct sock *sk;
-
-	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
-		if (l2cap_pi(sk)->psm == psm &&
-		    !bacmp(&l2cap_pi(sk)->src, src))
-			break;
-	}
-
-	return sk;
-}
-
-/* Find socket listening on psm and source bdaddr.
- * Returns closest match.
- */
-static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm)
-{
-	struct sock *sk, *sk1 = NULL;
-
-	read_lock(&l2cap_sk_list.lock);
-
-	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
-		struct l2cap_pinfo *pi;
-
-		if (sk->state != BT_LISTEN)
-			continue;
-
-		pi = l2cap_pi(sk);
-
-		if (pi->psm == psm) {
-			/* Exact match. */
-			if (!bacmp(&pi->src, src))
-				break;
-
-			/* Closest match */
-			if (!bacmp(&pi->src, BDADDR_ANY))
-				sk1 = sk;
-		}
-	}
-
-	read_unlock(&l2cap_sk_list.lock);
-
-	return sk ? sk : sk1;
-}
-
-static void l2cap_sock_destruct(struct sock *sk)
-{
-	DBG("sk %p", sk);
-
-	skb_queue_purge(&sk->receive_queue);
-	skb_queue_purge(&sk->write_queue);
-
-	MOD_DEC_USE_COUNT;
-}
-
-static void l2cap_sock_cleanup_listen(struct sock *parent)
-{
-	struct sock *sk;
-
-	DBG("parent %p", parent);
-
-	/* Close not yet accepted channels */
-	while ((sk = l2cap_accept_dequeue(parent, 0)))
-		l2cap_sock_close(sk);
-
-	parent->state  = BT_CLOSED;
-	parent->zapped = 1;
-}
-
-/* Kill socket (only if zapped and orphan)
- * Must be called on unlocked socket.
- */
-static void l2cap_sock_kill(struct sock *sk)
-{
-	if (!sk->zapped || sk->socket)
-		return;
-
-	DBG("sk %p state %d", sk, sk->state);
-
-	/* Kill poor orphan */
-	bluez_sock_unlink(&l2cap_sk_list, sk);
-	sk->dead = 1;
-	sock_put(sk);
-}
-
-/* Close socket.
- * Must be called on unlocked socket.
- */
-static void l2cap_sock_close(struct sock *sk)
-{
-	struct l2cap_conn *conn;
-
-	l2cap_sock_clear_timer(sk);
-
-	lock_sock(sk);
-
-	conn = l2cap_pi(sk)->conn;
-
-	DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket);
-
-	switch (sk->state) {
-	case BT_LISTEN:
-		l2cap_sock_cleanup_listen(sk);
-		break;
-
-	case BT_CONNECTED:
-	case BT_CONFIG:
-		if (sk->type == SOCK_SEQPACKET) {
-			l2cap_disconn_req req;
-
-			sk->state = BT_DISCONN;
-
-			req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
-			l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
-
-			l2cap_sock_set_timer(sk, sk->sndtimeo);
-		} else {
-			l2cap_chan_del(sk, ECONNRESET);
-		}
-		break;
-
-	case BT_CONNECT:
-	case BT_DISCONN:
-		l2cap_chan_del(sk, ECONNRESET);
-		break;
-
-	default:
-		sk->zapped = 1;
-		break;
-	};
-
-	release_sock(sk);
-
-	l2cap_sock_kill(sk);
-}
-
-static void l2cap_sock_init(struct sock *sk, struct sock *parent)
-{
-	struct l2cap_pinfo *pi = l2cap_pi(sk);
-
-	DBG("sk %p", sk);
-
-	if (parent) {
-		sk->type = parent->type;
-
-		pi->imtu = l2cap_pi(parent)->imtu;
-		pi->omtu = l2cap_pi(parent)->omtu;
-	} else {
-		pi->imtu = L2CAP_DEFAULT_MTU;
-		pi->omtu = 0;
-	}
-
-	/* Default config options */
-	pi->conf_mtu = L2CAP_DEFAULT_MTU;
-	pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
-}
-
-static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio)
-{
-	struct sock *sk;
-
-	if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1)))
-		return NULL;
-
-	sock_init_data(sock, sk);
-
-	sk->zapped   = 0;
-
-	sk->destruct = l2cap_sock_destruct;
-	sk->sndtimeo = L2CAP_CONN_TIMEOUT;
-
-	sk->protocol = proto;
-	sk->state    = BT_OPEN;
-
-	l2cap_sock_init_timer(sk);
-
-	bluez_sock_link(&l2cap_sk_list, sk);
-
-	MOD_INC_USE_COUNT;
-
-	return sk;
-}
-
-static int l2cap_sock_create(struct socket *sock, int protocol)
-{
-	struct sock *sk;
-
-	DBG("sock %p", sock);
-
-	sock->state = SS_UNCONNECTED;
-
-	if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_RAW)
-		return -ESOCKTNOSUPPORT;
-
-	sock->ops = &l2cap_sock_ops;
-
-	if (!(sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL)))
-		return -ENOMEM;
-
-	l2cap_sock_init(sk, NULL);
-
-	return 0;
-}
-
-static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
-{
-	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
-	struct sock *sk = sock->sk;
-	int err = 0;
-
-	DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
-
-	if (!addr || addr->sa_family != AF_BLUETOOTH)
-		return -EINVAL;
-
-	lock_sock(sk);
-
-	if (sk->state != BT_OPEN) {
-		err = -EBADFD;
-		goto done;
-	}
-
-	write_lock(&l2cap_sk_list.lock);
-
-	if (la->l2_psm && __l2cap_get_sock_by_addr(la)) {
-		err = -EADDRINUSE;
-		goto unlock;
-	}
-
-	/* Save source address */
-	bacpy(&l2cap_pi(sk)->src, &la->l2_bdaddr);
-	l2cap_pi(sk)->psm = la->l2_psm;
-	sk->state = BT_BOUND;
-
-unlock:
-	write_unlock(&l2cap_sk_list.lock);
-
-done:
-	release_sock(sk);
-
-	return err;
-}
-
-static int l2cap_sock_w4_connect(struct sock *sk, int flags)
-{
-	DECLARE_WAITQUEUE(wait, current);
-	long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
-	int err = 0;
-
-	DBG("sk %p", sk);
-
-	add_wait_queue(sk->sleep, &wait);
-	current->state = TASK_INTERRUPTIBLE;
-
-	while (sk->state != BT_CONNECTED) {
-		if (!timeo) {
-			err = -EAGAIN;
-			break;
-		}
-
-		release_sock(sk);
-		timeo = schedule_timeout(timeo);
-		lock_sock(sk);
-
-		err = 0;
-		if (sk->state == BT_CONNECTED)
-			break;
-
-		if (sk->err) {
-			err = sock_error(sk);
-			break;
-		}
-
-		if (signal_pending(current)) {
-			err = sock_intr_errno(timeo);
-			break;
-		}
-	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(sk->sleep, &wait);
-
-	return err;
-}
-
-static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
-{
-	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
-	struct sock *sk = sock->sk;
-	int err = 0;
-
-	lock_sock(sk);
-
-	DBG("sk %p", sk);
-
-	if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
-		err = -EINVAL;
-		goto done;
-	}
-
-	if (sk->state != BT_OPEN && sk->state != BT_BOUND) {
-		err = -EBADFD;
-		goto done;
-	}
-
-	if (sk->type == SOCK_SEQPACKET && !la->l2_psm) {
-		err = -EINVAL;
-		goto done;
-	}
-
-	/* Set destination address and psm */
-	bacpy(&l2cap_pi(sk)->dst, &la->l2_bdaddr);
-	l2cap_pi(sk)->psm = la->l2_psm;
-
-	if ((err = l2cap_connect(sk)))
-		goto done;
-
-	err = l2cap_sock_w4_connect(sk, flags);
-
-done:
-	release_sock(sk);
-	return err;
-}
-
-int l2cap_sock_listen(struct socket *sock, int backlog)
-{
-	struct sock *sk = sock->sk;
-	int err = 0;
-
-	DBG("sk %p backlog %d", sk, backlog);
-
-	lock_sock(sk);
-
-	if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
-		err = -EBADFD;
-		goto done;
-	}
-
-	if (!l2cap_pi(sk)->psm) {
-		err = -EINVAL;
-		goto done;
-	}
-
-	sk->max_ack_backlog = backlog;
-	sk->ack_backlog = 0;
-	sk->state = BT_LISTEN;
-
-done:
-	release_sock(sk);
-	return err;
-}
-
-int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
-{
-	DECLARE_WAITQUEUE(wait, current);
-	struct sock *sk = sock->sk, *ch;
-	long timeo;
-	int err = 0;
-
-	lock_sock(sk);
-
-	if (sk->state != BT_LISTEN) {
-		err = -EBADFD;
-		goto done;
-	}
-
-	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
-
-	DBG("sk %p timeo %ld", sk, timeo);
-
-	/* Wait for an incoming connection. (wake-one). */
-	add_wait_queue_exclusive(sk->sleep, &wait);
-	current->state = TASK_INTERRUPTIBLE;
-	while (!(ch = l2cap_accept_dequeue(sk, BT_CONNECTED))) {
-		if (!timeo) {
-			err = -EAGAIN;
-			break;
-		}
-
-		release_sock(sk);
-		timeo = schedule_timeout(timeo);
-		lock_sock(sk);
-
-		if (sk->state != BT_LISTEN) {
-			err = -EBADFD;
-			break;
-		}
-
-		if (signal_pending(current)) {
-			err = sock_intr_errno(timeo);
-			break;
-		}
-	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(sk->sleep, &wait);
-
-	if (err)
-		goto done;
-
-	sock_graft(ch, newsock);
-	newsock->state = SS_CONNECTED;
-
-	DBG("new socket %p", ch);
-
-done:
-	release_sock(sk);
-
-	return err;
-}
-
-static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
-{
-	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
-	struct sock *sk = sock->sk;
-
-	DBG("sock %p, sk %p", sock, sk);
-
-	addr->sa_family = AF_BLUETOOTH;
-	*len = sizeof(struct sockaddr_l2);
-
-	if (peer)
-		bacpy(&la->l2_bdaddr, &l2cap_pi(sk)->dst);
-	else
-		bacpy(&la->l2_bdaddr, &l2cap_pi(sk)->src);
-
-	la->l2_psm = l2cap_pi(sk)->psm;
-
-	return 0;
-}
-
-static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
-{
-	struct sock *sk = sock->sk;
-	int err = 0;
-
-	DBG("sock %p, sk %p", sock, sk);
-
-	if (sk->err)
-		return sock_error(sk);
-
-	if (msg->msg_flags & MSG_OOB)
-		return -EOPNOTSUPP;
-
-	lock_sock(sk);
-
-	if (sk->state == BT_CONNECTED)
-		err = l2cap_chan_send(sk, msg, len);
-	else
-		err = -ENOTCONN;
-
-	release_sock(sk);
-	return err;
-}
-
-static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
-{
-	struct sock *sk = sock->sk;
-	int noblock = flags & MSG_DONTWAIT;
-	int copied, err;
-	struct sk_buff *skb;
-
-	DBG("sock %p, sk %p", sock, sk);
-
-	if (flags & (MSG_OOB))
-		return -EOPNOTSUPP;
-
-	if (sk->state == BT_CLOSED)
-		return 0;
-
-	if (!(skb = skb_recv_datagram(sk, flags, noblock, &err)))
-		return err;
-
-	msg->msg_namelen = 0;
-
-	copied = skb->len;
-	if (len < copied) {
-		msg->msg_flags |= MSG_TRUNC;
-		copied = len;
-	}
-
-	skb->h.raw = skb->data;
-	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
-
-	skb_free_datagram(sk, skb);
-
-	return err ? : copied;
-}
-
-int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
-{
-	struct sock *sk = sock->sk;
-	struct l2cap_options opts;
-	int err = 0;
-
-	DBG("sk %p", sk);
-
-	lock_sock(sk);
-
-	switch (optname) {
-	case L2CAP_OPTIONS:
-		if (copy_from_user((char *)&opts, optval, optlen)) {
-			err = -EFAULT;
-			break;
-		}
-		l2cap_pi(sk)->imtu = opts.imtu;
-		l2cap_pi(sk)->omtu = opts.omtu;
-		break;
-
-	default:
-		err = -ENOPROTOOPT;
-		break;
-	};
-
-	release_sock(sk);
-	return err;
-}
-
-int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
-{
-	struct sock *sk = sock->sk;
-	struct l2cap_options opts;
-	struct l2cap_conninfo cinfo;
-	int len, err = 0; 
-
-	if (get_user(len, optlen))
-		return -EFAULT;
-
-	lock_sock(sk);
-
-	switch (optname) {
-	case L2CAP_OPTIONS:
-		opts.imtu     = l2cap_pi(sk)->imtu;
-		opts.omtu     = l2cap_pi(sk)->omtu;
-		opts.flush_to = l2cap_pi(sk)->flush_to;
-
-		len = MIN(len, sizeof(opts));
-		if (copy_to_user(optval, (char *)&opts, len))
-			err = -EFAULT;
-
-		break;
-
-	case L2CAP_CONNINFO:
-		if (sk->state != BT_CONNECTED) {
-			err = -ENOTCONN;
-			break;
-		}
-
-		cinfo.hci_handle = l2cap_pi(sk)->conn->hconn->handle;
-
-		len = MIN(len, sizeof(cinfo));
-		if (copy_to_user(optval, (char *)&cinfo, len))
-			err = -EFAULT;
-
-		break;
-
-	default:
-		err = -ENOPROTOOPT;
-		break;
-	};
-
-	release_sock(sk);
-	return err;
-}
-
-static unsigned int l2cap_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
-{
-	struct sock *sk = sock->sk;
-	struct l2cap_accept_q *aq;
-	unsigned int mask;
-
-	DBG("sock %p, sk %p", sock, sk);
-
-	poll_wait(file, sk->sleep, wait);
-	mask = 0;
-
-	if (sk->err || !skb_queue_empty(&sk->error_queue))
-		mask |= POLLERR;
-
-	if (sk->shutdown == SHUTDOWN_MASK)
-		mask |= POLLHUP;
-
-	aq = &l2cap_pi(sk)->accept_q;
-	if (!skb_queue_empty(&sk->receive_queue) || aq->head || (sk->shutdown & RCV_SHUTDOWN))
-		mask |= POLLIN | POLLRDNORM;
-
-	if (sk->state == BT_CLOSED)
-		mask |= POLLHUP;
-
-	if (sock_writeable(sk))
-		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
-	else
-		set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
-
-	return mask;
-}
-
-static int l2cap_sock_release(struct socket *sock)
-{
-	struct sock *sk = sock->sk;
-
-	DBG("sock %p, sk %p", sock, sk);
-
-	if (!sk)
-		return 0;
-
-	sock_orphan(sk);
-
-	l2cap_sock_close(sk);
-
-	return 0;
-}
-
-/* --------- L2CAP channels --------- */
-static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
-{
-	struct sock *s;
-
-	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
-		if (l2cap_pi(s)->dcid == cid)
-			break;
-	}
-
-	return s;
-}
-
-static inline struct sock *l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
-{
-	struct sock *s;
-
-	read_lock(&l->lock);
-	s = __l2cap_get_chan_by_dcid(l, cid);
-	read_unlock(&l->lock);
-
-	return s;
-}
-
-static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
-{
-	struct sock *s;
-
-	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
-		if (l2cap_pi(s)->scid == cid)
-			break;
-	}
-
-	return s;
-}
-static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
-{
-	struct sock *s;
-
-	read_lock(&l->lock);
-	s = __l2cap_get_chan_by_scid(l, cid);
-	read_unlock(&l->lock);
-
-	return s;
-}
-
-static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, __u8 ident)
-{
-	struct sock *s;
-
-	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
-		if (l2cap_pi(s)->ident == ident)
-			break;
-	}
-
-	return s;
-}
-
-static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, __u8 ident)
-{
-	struct sock *s;
-
-	read_lock(&l->lock);
-	s = __l2cap_get_chan_by_ident(l, ident);
-	read_unlock(&l->lock);
-
-	return s;
-}
-
-static __u16 l2cap_alloc_cid(struct l2cap_chan_list *l)
-{
-	__u16 cid = 0x0040;
-
-	for (; cid < 0xffff; cid++) {
-		if(!__l2cap_get_chan_by_scid(l, cid))
-			return cid;
-	}
-
-	return 0;
-}
-
-static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk)
-{
-	sock_hold(sk);
-
-	if (l->head)
-		l2cap_pi(l->head)->prev_c = sk;
-
-	l2cap_pi(sk)->next_c = l->head;
-	l2cap_pi(sk)->prev_c = NULL;
-	l->head = sk;
-}
-
-static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk)
-{
-	struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c;
-
-	write_lock(&l->lock);
-	if (sk == l->head)
-		l->head = next;
-
-	if (next)
-		l2cap_pi(next)->prev_c = prev;
-	if (prev)
-		l2cap_pi(prev)->next_c = next;
-	write_unlock(&l->lock);
-
-	__sock_put(sk);
-}
-
-static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
-{
-	struct l2cap_chan_list *l = &conn->chan_list;
-
-	DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
-
-	l2cap_conn_clear_timer(conn);
-
-	atomic_inc(&conn->refcnt);
-	l2cap_pi(sk)->conn = conn;
-
-	if (sk->type == SOCK_SEQPACKET) {
-		/* Alloc CID for normal socket */
-		l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
-	} else {
-		/* Raw socket can send only signalling messages */
-		l2cap_pi(sk)->scid = 0x0001;
-		l2cap_pi(sk)->dcid = 0x0001;
-		l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
-	}
-
-	__l2cap_chan_link(l, sk);
-
-	if (parent)
-		l2cap_accept_queue(parent, sk);
-}
-
-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
-{
-	struct l2cap_chan_list *l = &conn->chan_list;
-
-	write_lock(&l->lock);
-	__l2cap_chan_add(conn, sk, parent);
-	write_unlock(&l->lock);
-}
-
-/* Delete channel. 
- * Must be called on the locked socket. */
-static void l2cap_chan_del(struct sock *sk, int err)
-{
-	struct l2cap_conn *conn;
-	struct sock *parent;
-
-	conn = l2cap_pi(sk)->conn;
-	parent = l2cap_pi(sk)->parent;
-
-	DBG("sk %p, conn %p, err %d", sk, conn, err);
-
-	if (parent) {
-		/* Unlink from parent accept queue */
-		bh_lock_sock(parent);
-		l2cap_accept_unlink(sk);
-		bh_unlock_sock(parent);
-	}
-
-	if (conn) { 
-		long timeout;
-
-		/* Unlink from channel list */
-		l2cap_chan_unlink(&conn->chan_list, sk);
-		l2cap_pi(sk)->conn = NULL;
-
-		if (conn->out)
-			timeout = L2CAP_DISCONN_TIMEOUT;
-		else
-			timeout = L2CAP_CONN_IDLE_TIMEOUT;
-		
-		if (atomic_dec_and_test(&conn->refcnt) && conn->state == BT_CONNECTED) {
-			/* Schedule Baseband disconnect */
-			l2cap_conn_set_timer(conn, timeout);
-		}
-	}
-
-	sk->state  = BT_CLOSED;
-	sk->err    = err;
-	sk->state_change(sk);
-
-	sk->zapped = 1;
-}
-
-static void l2cap_conn_ready(struct l2cap_conn *conn)
-{
-	struct l2cap_chan_list *l = &conn->chan_list;
-	struct sock *sk;
-
-	DBG("conn %p", conn);
-
-	read_lock(&l->lock);
-
-	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
-		bh_lock_sock(sk);
-
-		if (sk->type != SOCK_SEQPACKET) {
-			sk->state = BT_CONNECTED;
-			sk->state_change(sk);
-			l2cap_sock_clear_timer(sk);
-		} else if (sk->state == BT_CONNECT) {
-			l2cap_conn_req req;
-			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
-			req.psm  = l2cap_pi(sk)->psm;
-			l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
-
-			l2cap_sock_set_timer(sk, sk->sndtimeo);
-		}
-
-		bh_unlock_sock(sk);
-	}
-
-	read_unlock(&l->lock);
-}
-
-static void l2cap_chan_ready(struct sock *sk)
-{
-	struct sock *parent = l2cap_pi(sk)->parent;
-
-	DBG("sk %p, parent %p", sk, parent);
-
-	l2cap_pi(sk)->conf_state = 0;
-	l2cap_sock_clear_timer(sk);
-
-	if (!parent) {
-		/* Outgoing channel.
-		 * Wake up socket sleeping on connect.
-		 */
-		sk->state = BT_CONNECTED;
-		sk->state_change(sk);
-	} else {
-		/* Incomming channel.
-		 * Wake up socket sleeping on accept.
-		 */
-		parent->data_ready(parent, 1);
-	}
-}
-
-/* Copy frame to all raw sockets on that connection */
-void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
-{
-	struct l2cap_chan_list *l = &conn->chan_list;
-	struct sk_buff *nskb;
-	struct sock * sk;
-
-	DBG("conn %p", conn);
-
-	read_lock(&l->lock);
-	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
-		if (sk->type != SOCK_RAW)
-			continue;
-
-		/* Don't send frame to the socket it came from */
-		if (skb->sk == sk)
-			continue;
-
-		if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
-			continue;
-
-		skb_queue_tail(&sk->receive_queue, nskb);
-		sk->data_ready(sk, nskb->len);
-	}
-	read_unlock(&l->lock);
-}
-
-static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len)
-{
-	struct l2cap_conn *conn = l2cap_pi(sk)->conn;
-	struct sk_buff *skb, **frag;
-	int err, size, count, sent=0;
-	l2cap_hdr *lh;
-
-	/* Check outgoing MTU */
-	if (len > l2cap_pi(sk)->omtu)
-		return -EINVAL;
-
-	DBG("sk %p len %d", sk, len);
-
-	/* First fragment (with L2CAP header) */
-	count = MIN(conn->iff->mtu - L2CAP_HDR_SIZE, len);
-	size  = L2CAP_HDR_SIZE + count;
-	if (!(skb = bluez_skb_send_alloc(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)))
-		return err;
-
-	/* Create L2CAP header */
-	lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
-	lh->len = __cpu_to_le16(len);
-	lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-
-	if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
-		err = -EFAULT;
-		goto fail;
-	}
-
-	sent += count;
-	len  -= count;
-
-	/* Continuation fragments (no L2CAP header) */
-	frag = &skb_shinfo(skb)->frag_list;
-	while (len) {
-		count = MIN(conn->iff->mtu, len);
-
-		*frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
-		if (!*frag)
-			goto fail;
-		
-		if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) {
-			err = -EFAULT;
-			goto fail;
-		}
-
-		sent += count;
-		len  -= count;
-
-		frag = &(*frag)->next;
-	}
-
-	if ((err = hci_send_acl(conn->hconn, skb, 0)) < 0)
-		goto fail;
-
-	return sent;
-
-fail:
-	kfree_skb(skb);
-	return err;
-}
-
-/* --------- L2CAP signalling commands --------- */
-static inline __u8 l2cap_get_ident(struct l2cap_conn *conn)
-{
-	__u8 id;
-
-	/* Get next available identificator.
-	 *    1 - 199 are used by kernel.
-	 *  200 - 254 are used by utilities like l2ping, etc 
-	 */
-
-	spin_lock(&conn->lock);
-
-	if (++conn->tx_ident > 199)
-		conn->tx_ident = 1;
-
-	id = conn->tx_ident;
-
-	spin_unlock(&conn->lock);
-
-	return id;
-}
-
-static inline struct sk_buff *l2cap_build_cmd(__u8 code, __u8 ident, __u16 len, void *data)
-{
-	struct sk_buff *skb;
-	l2cap_cmd_hdr *cmd;
-	l2cap_hdr *lh;
-	int size;
-
-	DBG("code 0x%2.2x, ident 0x%2.2x, len %d", code, ident, len);
-
-	size = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + len;
-	if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC)))
-		return NULL;
-
-	lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
-	lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + len);
-	lh->cid = __cpu_to_le16(0x0001);
-
-	cmd = (l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
-	cmd->code  = code;
-	cmd->ident = ident;
-	cmd->len   = __cpu_to_le16(len);
-
-	if (len)
-		memcpy(skb_put(skb, len), data, len);
-
-	return skb;
-}
-
-static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data)
-{
-	struct sk_buff *skb;
-	__u8 ident;
-
-	DBG("code 0x%2.2x", code);
-
-	ident = l2cap_get_ident(conn);
-	if (!(skb = l2cap_build_cmd(code, ident, len, data)))
-		return -ENOMEM;
-	return hci_send_acl(conn->hconn, skb, 0);
-}
-
-static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data)
-{
-	struct sk_buff *skb;
-
-	DBG("code 0x%2.2x", code);
-
-	if (!(skb = l2cap_build_cmd(code, ident, len, data)))
-		return -ENOMEM;
-	return hci_send_acl(conn->hconn, skb, 0);
-}
-
-static inline int l2cap_get_conf_opt(__u8 **ptr, __u8 *type, __u32 *val)
-{
-	l2cap_conf_opt *opt = (l2cap_conf_opt *) (*ptr);
-	int len;
-
-	*type = opt->type;
-	switch (opt->len) {
-	case 1:
-		*val = *((__u8 *) opt->val);
-		break;
-
-	case 2:
-		*val = __le16_to_cpu(*((__u16 *)opt->val));
-		break;
-
-	case 4:
-		*val = __le32_to_cpu(*((__u32 *)opt->val));
-		break;
-
-	default:
-		*val = 0L;
-		break;
-	};
-
-	DBG("type 0x%2.2x len %d val 0x%8.8x", *type, opt->len, *val);
-
-	len = L2CAP_CONF_OPT_SIZE + opt->len;
-
-	*ptr += len;
-
-	return len;
-}
-
-static inline void l2cap_parse_conf_req(struct sock *sk, char *data, int len)
-{
-	__u8 type, hint; __u32 val;
-	__u8 *ptr = data;
-
-	DBG("sk %p len %d", sk, len);
-
-	while (len >= L2CAP_CONF_OPT_SIZE) {
-		len -= l2cap_get_conf_opt(&ptr, &type, &val);
-
-		hint  = type & 0x80;
-		type &= 0x7f;
-
-		switch (type) {
-		case L2CAP_CONF_MTU:
-			l2cap_pi(sk)->conf_mtu = val;
-			break;
-
-		case L2CAP_CONF_FLUSH_TO:
-			l2cap_pi(sk)->flush_to = val;
-			break;
-
-		case L2CAP_CONF_QOS:
-			break;
-		
-		default:
-			if (hint)
-				break;
-
-			/* FIXME: Reject unknon option */
-			break;
-		};
-	}
-}
-
-static inline void l2cap_add_conf_opt(__u8 **ptr, __u8 type, __u8 len, __u32 val)
-{
-	register l2cap_conf_opt *opt = (l2cap_conf_opt *) (*ptr);
-
-	DBG("type 0x%2.2x len %d val 0x%8.8x", type, len, val);
-
-	opt->type = type;
-	opt->len  = len;
-	switch (len) {
-	case 1:
-		*((__u8 *) opt->val)  = val;
-		break;
-
-	case 2:
-		*((__u16 *) opt->val) = __cpu_to_le16(val);
-		break;
-
-	case 4:
-		*((__u32 *) opt->val) = __cpu_to_le32(val);
-		break;
-	};
-
-	*ptr += L2CAP_CONF_OPT_SIZE + len;
-}
-
-static int l2cap_build_conf_req(struct sock *sk, __u8 *data)
-{
-	struct l2cap_pinfo *pi = l2cap_pi(sk);
-	l2cap_conf_req *req = (l2cap_conf_req *) data;
-	__u8 *ptr = req->data;
-
-	DBG("sk %p", sk);
-
-	if (pi->imtu != L2CAP_DEFAULT_MTU)
-		l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
-
-	/* FIXME. Need actual value of the flush timeout */
-	//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
-	//   l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
-
-	req->dcid  = __cpu_to_le16(pi->dcid);
-	req->flags = __cpu_to_le16(0);
-
-	return ptr - data;
-}
-
-static int l2cap_conf_output(struct sock *sk, __u8 **ptr)
-{
-	struct l2cap_pinfo *pi = l2cap_pi(sk);
-	int result = 0;
-
-	/* Configure output options and let other side know
-	 * which ones we don't like.
-	 */
-	if (pi->conf_mtu < pi->omtu) {
-		l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, l2cap_pi(sk)->omtu);
-		result = L2CAP_CONF_UNACCEPT;
-	} else {
-		pi->omtu = pi->conf_mtu;
-	}
-
-	DBG("sk %p result %d", sk, result);
-	return result;
-}
-
-static int l2cap_build_conf_rsp(struct sock *sk, __u8 *data, int *result)
-{
-	l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data;
-	__u8 *ptr = rsp->data;
-
-	DBG("sk %p complete %d", sk, result ? 1 : 0);
-
-	if (result)
-		*result = l2cap_conf_output(sk, &ptr);
-
-	rsp->scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
-	rsp->result = __cpu_to_le16(result ? *result : 0);
-	rsp->flags  = __cpu_to_le16(0);
-
-	return ptr - data;
-}
-
-static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	struct l2cap_chan_list *list = &conn->chan_list;
-	l2cap_conn_req *req = (l2cap_conn_req *) data;
-	l2cap_conn_rsp rsp;
-	struct sock *sk, *parent;
-
-	__u16 scid = __le16_to_cpu(req->scid);
-	__u16 psm  = req->psm;
-
-	DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
-
-	/* Check if we have socket listening on psm */
-	if (!(parent = l2cap_get_sock_listen(&conn->src, psm)))
-		goto reject;
-
-	bh_lock_sock(parent);
-	write_lock(&list->lock);
-
-	/* Check if we already have channel with that dcid */
-	if (__l2cap_get_chan_by_dcid(list, scid))
-		goto unlock;
-
-	/* Check for backlog size */
-	if (parent->ack_backlog > parent->max_ack_backlog)
-		goto unlock;
-
-	if (!(sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC)))
-		goto unlock;
-
-	l2cap_sock_init(sk, parent);
-
-	bacpy(&l2cap_pi(sk)->src, &conn->src);
-	bacpy(&l2cap_pi(sk)->dst, &conn->dst);
-	l2cap_pi(sk)->psm  = psm;
-	l2cap_pi(sk)->dcid = scid;
-
-	__l2cap_chan_add(conn, sk, parent);
-	sk->state = BT_CONFIG;
-
-	write_unlock(&list->lock);
-	bh_unlock_sock(parent);
-
-	rsp.dcid   = __cpu_to_le16(l2cap_pi(sk)->scid);
-	rsp.scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
-	rsp.result = __cpu_to_le16(0);
-	rsp.status = __cpu_to_le16(0);
-	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
-
-	return 0;
-
-unlock:
-	write_unlock(&list->lock);
-	bh_unlock_sock(parent);
-
-reject:
-	rsp.scid   = __cpu_to_le16(scid);
-	rsp.dcid   = __cpu_to_le16(0);
-	rsp.status = __cpu_to_le16(0);
-	rsp.result = __cpu_to_le16(L2CAP_CONN_NO_MEM);
-	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
-
-	return 0;
-}
-
-static inline int l2cap_connect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	l2cap_conn_rsp *rsp = (l2cap_conn_rsp *) data;
-	__u16 scid, dcid, result, status;
-	struct sock *sk;
-
-	scid   = __le16_to_cpu(rsp->scid);
-	dcid   = __le16_to_cpu(rsp->dcid);
-	result = __le16_to_cpu(rsp->result);
-	status = __le16_to_cpu(rsp->status);
-
-	DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
-		return -ENOENT;
-
-	bh_lock_sock(sk);
-
-	if (!result) {
-		char req[64];
-
-		sk->state = BT_CONFIG;
-		l2cap_pi(sk)->dcid = dcid;
-		l2cap_pi(sk)->conf_state |= CONF_REQ_SENT;
-
-		l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
-	} else {
-		l2cap_chan_del(sk, ECONNREFUSED);
-	}
-
-	bh_unlock_sock(sk);
-	return 0;
-}
-
-static inline int l2cap_config_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	l2cap_conf_req * req = (l2cap_conf_req *) data;
-	__u16 dcid, flags;
-	__u8 rsp[64];
-	struct sock *sk;
-	int result;
-
-	dcid  = __le16_to_cpu(req->dcid);
-	flags = __le16_to_cpu(req->flags);
-
-	DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
-		return -ENOENT;
-
-	bh_lock_sock(sk);
-
-	l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
-
-	if (flags & 0x01) {
-		/* Incomplete config. Send empty response. */
-		l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
-		goto unlock;
-	}
-
-	/* Complete config. */
-	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp);
-
-	if (result)
-		goto unlock;
-
-	/* Output config done */
-	l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE;
-
-	if (l2cap_pi(sk)->conf_state & CONF_INPUT_DONE) {
-		sk->state = BT_CONNECTED;
-		l2cap_chan_ready(sk);
-	} else if (!(l2cap_pi(sk)->conf_state & CONF_REQ_SENT)) {
-		char req[64];
-		l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
-	}
-
-unlock:
-	bh_unlock_sock(sk);
-
-	return 0;
-}
-
-static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data;
-	__u16 scid, flags, result;
-	struct sock *sk;
-	int err = 0;
-
-	scid   = __le16_to_cpu(rsp->scid);
-	flags  = __le16_to_cpu(rsp->flags);
-	result = __le16_to_cpu(rsp->result);
-
-	DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result);
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
-		return -ENOENT;
-
-	bh_lock_sock(sk);
-
-	if (result) {
-		l2cap_disconn_req req;
-
-		/* They didn't like our options. Well... we do not negotiate.
-		 * Close channel.
-		 */
-		sk->state = BT_DISCONN;
-
-		req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-		req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
-		l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
-
-		l2cap_sock_set_timer(sk, sk->sndtimeo);
-		goto done;
-	}
-
-	if (flags & 0x01)
-		goto done;
-
-	/* Input config done */
-	l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE;
-
-	if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) {
-		sk->state = BT_CONNECTED;
-		l2cap_chan_ready(sk);
-	}
-
-done:
-	bh_unlock_sock(sk);
-
-	return err;
-}
-
-static inline int l2cap_disconnect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	l2cap_disconn_req *req = (l2cap_disconn_req *) data;
-	l2cap_disconn_rsp rsp;
-	__u16 dcid, scid;
-	struct sock *sk;
-
-	scid = __le16_to_cpu(req->scid);
-	dcid = __le16_to_cpu(req->dcid);
-
-	DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
-		return 0;
-
-	bh_lock_sock(sk);
-
-	rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
-	rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-	l2cap_send_rsp(conn, cmd->ident, L2CAP_DISCONN_RSP, L2CAP_DISCONN_RSP_SIZE, &rsp);
-
-	l2cap_chan_del(sk, ECONNRESET);
-
-	bh_unlock_sock(sk);
-
-	l2cap_sock_kill(sk);
-
-	return 0;
-}
-
-static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	l2cap_disconn_rsp *rsp = (l2cap_disconn_rsp *) data;
-	__u16 dcid, scid;
-	struct sock *sk;
-
-	scid = __le16_to_cpu(rsp->scid);
-	dcid = __le16_to_cpu(rsp->dcid);
-
-	DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
-		return -ENOENT;
-
-	bh_lock_sock(sk);
-	l2cap_sock_clear_timer(sk);
-	l2cap_chan_del(sk, ECONNABORTED);
-	bh_unlock_sock(sk);
-
-	l2cap_sock_kill(sk);
-
-	return 0;
-}
-
-static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
-{
-	__u8 *data = skb->data;
-	int len = skb->len;
-	l2cap_cmd_hdr cmd;
-	int err = 0;
-
-	while (len >= L2CAP_CMD_HDR_SIZE) {
-		memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
-		data += L2CAP_CMD_HDR_SIZE;
-		len  -= L2CAP_CMD_HDR_SIZE;
-
-		cmd.len = __le16_to_cpu(cmd.len);
-
-		DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd.len, cmd.ident);
-
-		if (cmd.len > len || !cmd.ident) {
-			DBG("corrupted command");
-			break;
-		}
-
-		switch (cmd.code) {
-		case L2CAP_CONN_REQ:
-			err = l2cap_connect_req(conn, &cmd, data);
-			break;
-
-		case L2CAP_CONN_RSP:
-			err = l2cap_connect_rsp(conn, &cmd, data);
-			break;
-
-		case L2CAP_CONF_REQ:
-			err = l2cap_config_req(conn, &cmd, data);
-			break;
-
-		case L2CAP_CONF_RSP:
-			err = l2cap_config_rsp(conn, &cmd, data);
-			break;
-
-		case L2CAP_DISCONN_REQ:
-			err = l2cap_disconnect_req(conn, &cmd, data);
-			break;
-
-		case L2CAP_DISCONN_RSP:
-			err = l2cap_disconnect_rsp(conn, &cmd, data);
-			break;
-
-		case L2CAP_COMMAND_REJ:
-			/* FIXME: We should process this */
-			l2cap_raw_recv(conn, skb);
-			break;
-
-		case L2CAP_ECHO_REQ:
-			l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data);
-			break;
-
-		case L2CAP_ECHO_RSP:
-		case L2CAP_INFO_REQ:
-		case L2CAP_INFO_RSP:
-			l2cap_raw_recv(conn, skb);
-			break;
-
-		default:
-			ERR("Uknown signaling command 0x%2.2x", cmd.code);
-			err = -EINVAL;
-			break;
-		};
-
-		if (err) {
-			l2cap_cmd_rej rej;
-			DBG("error %d", err);
-
-			/* FIXME: Map err to a valid reason. */
-			rej.reason = __cpu_to_le16(0);
-			l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej);
-		}
-
-		data += cmd.len;
-		len  -= cmd.len;
-	}
-
-	kfree_skb(skb);
-}
-
-static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct sk_buff *skb)
-{
-	struct sock *sk;
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, cid))) {
-		DBG("unknown cid 0x%4.4x", cid);
-		goto drop;
-	}
-
-	DBG("sk %p, len %d", sk, skb->len);
-
-	if (sk->state != BT_CONNECTED)
-		goto drop;
-
-	if (l2cap_pi(sk)->imtu < skb->len)
-		goto drop;
-
-	skb_queue_tail(&sk->receive_queue, skb);
-	sk->data_ready(sk, skb->len);
-
-	return 0;
-
-drop:
-	kfree_skb(skb);
-
-	return 0;
-}
-
-static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
-{
-	l2cap_hdr *lh = (l2cap_hdr *) skb->data;
-	__u16 cid, len;
-
-	skb_pull(skb, L2CAP_HDR_SIZE);
-	cid = __le16_to_cpu(lh->cid);
-	len = __le16_to_cpu(lh->len);
-
-	DBG("len %d, cid 0x%4.4x", len, cid);
-
-	if (cid == 0x0001)
-		l2cap_sig_channel(conn, skb);
-	else	
-		l2cap_data_channel(conn, cid, skb);
-}
-
-/* ------------ L2CAP interface with lower layer (HCI) ------------- */
-static int l2cap_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
-	struct hci_dev *hdev = (struct hci_dev *) ptr;
-
-	DBG("hdev %s, event %ld", hdev->name, event);
-
-	write_lock(&l2cap_rt_lock);
-
-	switch (event) {
-	case HCI_DEV_UP:
-		l2cap_iff_add(hdev);
-		break;
-
-	case HCI_DEV_DOWN:
-		l2cap_iff_del(hdev);
-		break;
-	};
-
-	write_unlock(&l2cap_rt_lock);
-
-	return NOTIFY_DONE;
-}
-
-int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
-{
-	struct l2cap_iff *iff;
-
-	DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
-
-	if (!(iff = hdev->l2cap_data)) {
-		ERR("unknown interface");
-		return 0;
-	}
-
-	/* Always accept connection */
-	return 1;
-}
-
-int l2cap_connect_cfm(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 status, struct hci_conn *hconn)
-{
-	struct l2cap_conn *conn;
-	struct l2cap_iff *iff;
-	int err = 0;
-
-	DBG("hdev %s bdaddr %s hconn %p", hdev->name, batostr(bdaddr), hconn);
-
-	if (!(iff = hdev->l2cap_data)) {
-		ERR("unknown interface");
-		return 0;
-	}
-
-	l2cap_iff_lock(iff);
-
-	conn = l2cap_get_conn_by_addr(iff, bdaddr);
-
-	if (conn) {
-		/* Outgoing connection */
-		DBG("Outgoing connection: %s -> %s, %p, %2.2x", batostr(iff->bdaddr), batostr(bdaddr), conn, status);
-
-		if (!status && hconn) {
-			conn->state = BT_CONNECTED;
-			conn->hconn = hconn;
-
-			hconn->l2cap_data = (void *)conn;
-
-			/* Establish channels */
-			l2cap_conn_ready(conn);
-		} else {
-			l2cap_conn_del(conn, bterr(status));
-		}
-	} else {
-		/* Incomming connection */
-		DBG("Incomming connection: %s -> %s, %2.2x", batostr(iff->bdaddr), batostr(bdaddr), status);
-	
-		if (status || !hconn)
-			goto done;
-
-		if (!(conn = l2cap_conn_add(iff, bdaddr))) {
-			err = -ENOMEM;
-			goto done;
-		}
-
-		conn->hconn = hconn;
-		hconn->l2cap_data = (void *)conn;
-
-		conn->state = BT_CONNECTED;
-	}
-
-done:
-	l2cap_iff_unlock(iff);
-
-	return err;
-}
-
-int l2cap_disconn_ind(struct hci_conn *hconn, __u8 reason)
-{
-	struct l2cap_conn *conn = hconn->l2cap_data;
-
-	DBG("hconn %p reason %d", hconn, reason);
-
-	if (!conn) {
-		ERR("unknown connection");
-		return 0;
-	}
-	conn->hconn = NULL;
-
-	l2cap_iff_lock(conn->iff);
-	l2cap_conn_del(conn, bterr(reason));
-	l2cap_iff_unlock(conn->iff);
-
-	return 0;
-}
-
-int l2cap_recv_acldata(struct hci_conn *hconn, struct sk_buff *skb, __u16 flags)
-{
-	struct l2cap_conn *conn = hconn->l2cap_data;
-
-	if (!conn) {
-		ERR("unknown connection %p", hconn);
-		goto drop;
-	}
-
-	DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
-
-	if (flags & ACL_START) {
-		int flen, tlen, size;
-		l2cap_hdr *lh;
-
-		if (conn->rx_len) {
-			ERR("Unexpected start frame (len %d)", skb->len);
-			kfree_skb(conn->rx_skb); conn->rx_skb = NULL;
-			conn->rx_len = 0;
-		}
-
-		if (skb->len < L2CAP_HDR_SIZE) {
-			ERR("Frame is too small (len %d)", skb->len);
-			goto drop;
-		}
-
-		lh = (l2cap_hdr *)skb->data;
-		tlen = __le16_to_cpu(lh->len);
-		flen = skb->len - L2CAP_HDR_SIZE;
-
-		DBG("Start: total len %d, frag len %d", tlen, flen);
-
-		if (flen == tlen) {
-			/* Complete frame received */
-			l2cap_recv_frame(conn, skb);
-			return 0;
-		}
-
-		/* Allocate skb for the complete frame (with header) */
-		size = L2CAP_HDR_SIZE + tlen;
-		if (!(conn->rx_skb = bluez_skb_alloc(size, GFP_ATOMIC)))
-			goto drop;
-
-		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
-
-		conn->rx_len = tlen - flen;
-	} else {
-		DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
-
-		if (!conn->rx_len) {
-			ERR("Unexpected continuation frame (len %d)", skb->len);
-			goto drop;
-		}
-
-		if (skb->len > conn->rx_len) {
-			ERR("Fragment is too large (len %d)", skb->len);
-			kfree_skb(conn->rx_skb); conn->rx_skb = NULL;
-			goto drop;
-		}
-
-		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
-		conn->rx_len -= skb->len;
-
-		if (!conn->rx_len) {
-			/* Complete frame received */
-			l2cap_recv_frame(conn, conn->rx_skb);
-			conn->rx_skb = NULL;
-		}
-	}
-
-drop:
-	kfree_skb(skb);
-	return 0;
-}
-
-struct proto_ops l2cap_sock_ops = {
-	family:		PF_BLUETOOTH,
-	release:	l2cap_sock_release,
-	bind:		l2cap_sock_bind,
-	connect:	l2cap_sock_connect,
-	listen:		l2cap_sock_listen,
-	accept:		l2cap_sock_accept,
-	getname:	l2cap_sock_getname,
-	sendmsg:	l2cap_sock_sendmsg,
-	recvmsg:	l2cap_sock_recvmsg,
-	poll:		l2cap_sock_poll,
-	socketpair:	sock_no_socketpair,
-	ioctl:		sock_no_ioctl,
-	shutdown:	sock_no_shutdown,
-	setsockopt:	l2cap_sock_setsockopt,
-	getsockopt:	l2cap_sock_getsockopt,
-	mmap:		sock_no_mmap
-};
-
-struct net_proto_family l2cap_sock_family_ops = {
-	family:		PF_BLUETOOTH,
-	create:		l2cap_sock_create
-};
-
-struct hci_proto l2cap_hci_proto = {
-	name:		"L2CAP",
-	id:		HCI_PROTO_L2CAP,
-	connect_ind:	l2cap_connect_ind,
-	connect_cfm:	l2cap_connect_cfm,
-	disconn_ind:	l2cap_disconn_ind,
-	recv_acldata:	l2cap_recv_acldata,
-};
-
-struct notifier_block l2cap_nblock = {
-	notifier_call: l2cap_dev_event
-};
-
-int __init l2cap_init(void)
-{
-	INF("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc",
-		VERSION);
-	INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
-
-	if (bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops)) {
-		ERR("Can't register L2CAP socket");
-		return -EPROTO;
-	}
-
-	if (hci_register_proto(&l2cap_hci_proto) < 0) {
-		ERR("Can't register L2CAP protocol");
-		return -EPROTO;
-	}
-
-	hci_register_notifier(&l2cap_nblock);
-
-	l2cap_register_proc();
-
-	return 0;
-}
-
-void l2cap_cleanup(void)
-{
-	l2cap_unregister_proc();
-
-	/* Unregister socket, protocol and notifier */
-	if (bluez_sock_unregister(BTPROTO_L2CAP))
-		ERR("Can't unregister L2CAP socket");
-
-	if (hci_unregister_proto(&l2cap_hci_proto) < 0)
-		ERR("Can't unregister L2CAP protocol");
-
-	hci_unregister_notifier(&l2cap_nblock);
-
-	/* We _must_ not have any sockets and/or connections
-	 * at this stage.
-	 */
-
-	/* Free interface list and unlock HCI devices */
-	{
-		struct list_head *list = &l2cap_iff_list;
-
-		while (!list_empty(list)) {
-			struct l2cap_iff *iff;
-
-			iff = list_entry(list->next, struct l2cap_iff, list);
-			l2cap_iff_del(iff->hdev);
-		}
-	}
-}
-
-module_init(l2cap_init);
-module_exit(l2cap_cleanup);
-
-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
-MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION);
-MODULE_LICENSE("GPL");
-
diff -urN linux-2.4.18/net/bluetooth/l2cap_proc.c linux-2.4.18-mh15/net/bluetooth/l2cap_proc.c
--- linux-2.4.18/net/bluetooth/l2cap_proc.c	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/net/bluetooth/l2cap_proc.c	1970-01-01 01:00:00.000000000 +0100
@@ -1,165 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * BlueZ L2CAP proc fs support.
- *
- * $Id: l2cap_proc.c,v 1.2 2001/06/02 01:40:09 maxk Exp $
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/fcntl.h>
-#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/interrupt.h>
-#include <linux/socket.h>
-#include <linux/skbuff.h>
-#include <linux/proc_fs.h>
-#include <linux/list.h>
-#include <net/sock.h>
-
-#include <asm/system.h>
-#include <asm/uaccess.h>
-
-#include <net/bluetooth/bluez.h>
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/l2cap_core.h>
-
-#ifndef L2CAP_DEBUG
-#undef  DBG
-#define DBG( A... )
-#endif
-
-/* ----- PROC fs support ----- */
-static int l2cap_conn_dump(char *buf, struct l2cap_iff *iff)
-{
-	struct list_head *p;
-	char *ptr = buf;
-
-	list_for_each(p, &iff->conn_list) {
-		struct l2cap_conn *c;
-
-		c = list_entry(p, struct l2cap_conn, list);
-		ptr += sprintf(ptr, "    %p %d %p %p %s %s\n", 
-				c, c->state, c->iff, c->hconn, batostr(&c->src), batostr(&c->dst));
-	}
-
-	return ptr - buf;
-}
-
-static int l2cap_iff_dump(char *buf)
-{
-	struct list_head *p;
-	char *ptr = buf;
-
-	ptr += sprintf(ptr, "Interfaces:\n");
-
-	write_lock(&l2cap_rt_lock);
-
-	list_for_each(p, &l2cap_iff_list) {
-		struct l2cap_iff *iff;
-
-		iff = list_entry(p, struct l2cap_iff, list);
-
-		ptr += sprintf(ptr, "  %s %p %p\n", iff->hdev->name, iff, iff->hdev);
-		
-		l2cap_iff_lock(iff);
-		ptr += l2cap_conn_dump(ptr, iff);
-		l2cap_iff_unlock(iff);
-	}
-
-	write_unlock(&l2cap_rt_lock);
-
-	ptr += sprintf(ptr, "\n");
-
-	return ptr - buf;
-}
-
-static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
-{
-	struct l2cap_pinfo *pi;
-	struct sock *sk;
-	char *ptr = buf;
-
-	ptr += sprintf(ptr, "Sockets:\n");
-
-	write_lock(&list->lock);
-
-	for (sk = list->head; sk; sk = sk->next) {
-		pi = l2cap_pi(sk);
-		ptr += sprintf(ptr, "  %p %d %p %d %s %s 0x%4.4x 0x%4.4x %d %d\n", sk, sk->state, pi->conn, pi->psm,
-		               batostr(&pi->src), batostr(&pi->dst), pi->scid, pi->dcid, pi->imtu, pi->omtu );
-	}
-
-	write_unlock(&list->lock);
-
-	ptr += sprintf(ptr, "\n");
-
-	return ptr - buf;
-}
-
-static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
-{
-	char *ptr = buf;
-	int len;
-
-	DBG("count %d, offset %ld", count, offset);
-
-	ptr += l2cap_iff_dump(ptr);
-	ptr += l2cap_sock_dump(ptr, &l2cap_sk_list);
-	len  = ptr - buf;
-
-	if (len <= count + offset)
-		*eof = 1;
-
-	*start = buf + offset;
-	len -= offset;
-
-	if (len > count)
-		len = count;
-	if (len < 0)
-		len = 0;
-
-	return len;
-}
-
-void l2cap_register_proc(void)
-{
-	create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL);
-}
-
-void l2cap_unregister_proc(void)
-{
-	remove_proc_entry("bluetooth/l2cap", NULL);
-}
diff -urN linux-2.4.18/net/bluetooth/lib.c linux-2.4.18-mh15/net/bluetooth/lib.c
--- linux-2.4.18/net/bluetooth/lib.c	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/net/bluetooth/lib.c	2004-08-01 16:26:23.000000000 +0200
@@ -25,7 +25,7 @@
 /*
  * BlueZ kernel library.
  *
- * $Id: lib.c,v 1.3 2001/06/22 23:14:23 maxk Exp $
+ * $Id: lib.c,v 1.2 2002/06/20 19:55:08 maxk Exp $
  */
 
 #include <linux/kernel.h>
@@ -105,7 +105,7 @@
 		return EACCES;
 
 	case 0x06:
-		return EINVAL;
+		return EBADE;
 
 	case 0x07:
 		return ENOMEM;
diff -urN linux-2.4.18/net/bluetooth/Makefile linux-2.4.18-mh15/net/bluetooth/Makefile
--- linux-2.4.18/net/bluetooth/Makefile	2001-06-12 04:15:27.000000000 +0200
+++ linux-2.4.18-mh15/net/bluetooth/Makefile	2004-08-01 16:26:23.000000000 +0200
@@ -1,20 +1,40 @@
 #
-# Makefile for the Bluetooth subsystem
+# Makefile for the Linux Bluetooth subsystem
 #
-O_TARGET	:= bluetooth.o
 
-list-multi	:= hci.o l2cap.o
-export-objs	:= syms.o
-hci-objs	:= af_bluetooth.o hci_core.o hci_sock.o lib.o syms.o
-l2cap-objs	:= l2cap_core.o l2cap_proc.o
+O_TARGET    := bluetooth.o
 
-obj-$(CONFIG_BLUEZ) += hci.o
+list-multi  := bluez.o
+export-objs := syms.o l2cap.o
+
+bluez-objs  := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
+
+obj-$(CONFIG_BLUEZ) += bluez.o
 obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o
+obj-$(CONFIG_BLUEZ_SCO) += sco.o
 
-include $(TOPDIR)/Rules.make
+subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm
+subdir-$(CONFIG_BLUEZ_BNEP) += bnep
+subdir-$(CONFIG_BLUEZ_CMTP) += cmtp
+subdir-$(CONFIG_BLUEZ_HIDP) += hidp
+
+ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
+obj-y += rfcomm/rfcomm.o
+endif
 
-hci.o: $(hci-objs)
-	$(LD) -r -o $@ $(hci-objs)
+ifeq ($(CONFIG_BLUEZ_BNEP),y)
+obj-y += bnep/bnep.o
+endif
+
+ifeq ($(CONFIG_BLUEZ_CMTP),y)
+obj-y += cmtp/cmtp.o
+endif
+
+ifeq ($(CONFIG_BLUEZ_HIDP),y)
+obj-y += hidp/hidp.o
+endif
+
+include $(TOPDIR)/Rules.make
 
-l2cap.o: $(l2cap-objs)
-	$(LD) -r -o $@ $(l2cap-objs)
+bluez.o: $(bluez-objs)
+	$(LD) -r -o $@ $(bluez-objs)
diff -urN linux-2.4.18/net/bluetooth/rfcomm/Config.in linux-2.4.18-mh15/net/bluetooth/rfcomm/Config.in
--- linux-2.4.18/net/bluetooth/rfcomm/Config.in	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/rfcomm/Config.in	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,10 @@
+#
+# Bluetooth RFCOMM layer configuration
+#
+
+dep_tristate 'RFCOMM protocol support' CONFIG_BLUEZ_RFCOMM $CONFIG_BLUEZ_L2CAP
+
+if [ "$CONFIG_BLUEZ_RFCOMM" != "n" ]; then
+   bool '  RFCOMM TTY support' CONFIG_BLUEZ_RFCOMM_TTY
+fi
+
diff -urN linux-2.4.18/net/bluetooth/rfcomm/core.c linux-2.4.18-mh15/net/bluetooth/rfcomm/core.c
--- linux-2.4.18/net/bluetooth/rfcomm/core.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/rfcomm/core.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,1940 @@
+/* 
+   RFCOMM implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+   Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/* 
+   RPN support    -    Dirk Husemann <hud@zurich.ibm.com>
+*/
+
+/*
+ * RFCOMM core.
+ *
+ * $Id: core.c,v 1.46 2002/10/18 20:12:12 maxk Exp $
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/net.h>
+#include <linux/proc_fs.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/rfcomm.h>
+
+#define VERSION "1.1"
+
+#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+struct task_struct *rfcomm_thread;
+DECLARE_MUTEX(rfcomm_sem);
+unsigned long rfcomm_event;
+
+static LIST_HEAD(session_list);
+static atomic_t terminate, running;
+
+static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
+static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
+static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci);
+static int rfcomm_queue_disc(struct rfcomm_dlc *d);
+static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type);
+static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d);
+static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig);
+static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len);
+static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits);
+static void rfcomm_make_uih(struct sk_buff *skb, u8 addr);
+
+static void rfcomm_process_connect(struct rfcomm_session *s);
+
+/* ---- RFCOMM frame parsing macros ---- */
+#define __get_dlci(b)     ((b & 0xfc) >> 2)
+#define __get_channel(b)  ((b & 0xf8) >> 3)
+#define __get_dir(b)      ((b & 0x04) >> 2)
+#define __get_type(b)     ((b & 0xef))
+
+#define __test_ea(b)      ((b & 0x01))
+#define __test_cr(b)      ((b & 0x02))
+#define __test_pf(b)      ((b & 0x10))
+
+#define __addr(cr, dlci)       (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
+#define __ctrl(type, pf)       (((type & 0xef) | (pf << 4)))
+#define __dlci(dir, chn)       (((chn & 0x1f) << 1) | dir)
+#define __srv_channel(dlci)    (dlci >> 1)
+#define __dir(dlci)            (dlci & 0x01)
+
+#define __len8(len)       (((len) << 1) | 1)
+#define __len16(len)      ((len) << 1)
+
+/* MCC macros */
+#define __mcc_type(cr, type)   (((type << 2) | (cr << 1) | 0x01))
+#define __get_mcc_type(b) ((b & 0xfc) >> 2)
+#define __get_mcc_len(b)  ((b & 0xfe) >> 1)
+
+/* RPN macros */
+#define __rpn_line_settings(data, stop, parity)  ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x3) << 3))
+#define __get_rpn_data_bits(line) ((line) & 0x3)
+#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
+#define __get_rpn_parity(line)    (((line) >> 3) & 0x3)
+
+/* ---- RFCOMM FCS computation ---- */
+
+/* CRC on 2 bytes */
+#define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
+
+/* FCS on 2 bytes */ 
+static inline u8 __fcs(u8 *data)
+{
+	return (0xff - __crc(data));
+}
+
+/* FCS on 3 bytes */ 
+static inline u8 __fcs2(u8 *data)
+{
+	return (0xff - rfcomm_crc_table[__crc(data) ^ data[2]]);
+}
+
+/* Check FCS */
+static inline int __check_fcs(u8 *data, int type, u8 fcs)
+{
+	u8 f = __crc(data);
+
+	if (type != RFCOMM_UIH)
+		f = rfcomm_crc_table[f ^ data[2]];
+
+	return rfcomm_crc_table[f ^ fcs] != 0xcf;
+}
+
+/* ---- L2CAP callbacks ---- */
+static void rfcomm_l2state_change(struct sock *sk)
+{
+	BT_DBG("%p state %d", sk, sk->state);
+	rfcomm_schedule(RFCOMM_SCHED_STATE);
+}
+
+static void rfcomm_l2data_ready(struct sock *sk, int bytes)
+{
+	BT_DBG("%p bytes %d", sk, bytes);
+	rfcomm_schedule(RFCOMM_SCHED_RX);
+}
+
+static int rfcomm_l2sock_create(struct socket **sock)
+{
+	int err;
+
+	BT_DBG("");
+
+	err = sock_create(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock);
+	if (!err) {
+		struct sock *sk = (*sock)->sk;
+		sk->data_ready   = rfcomm_l2data_ready;
+		sk->state_change = rfcomm_l2state_change;
+	}
+	return err;
+}
+
+/* ---- RFCOMM DLCs ---- */
+static void rfcomm_dlc_timeout(unsigned long arg)
+{
+	struct rfcomm_dlc *d = (void *) arg;
+
+	BT_DBG("dlc %p state %ld", d, d->state);
+
+	set_bit(RFCOMM_TIMED_OUT, &d->flags);
+	rfcomm_dlc_put(d);
+	rfcomm_schedule(RFCOMM_SCHED_TIMEO);
+}
+
+static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout)
+{
+	BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout);
+
+	if (!mod_timer(&d->timer, jiffies + timeout))
+		rfcomm_dlc_hold(d);
+}
+
+static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d)
+{
+	BT_DBG("dlc %p state %ld", d, d->state);
+
+	if (timer_pending(&d->timer) && del_timer(&d->timer))
+		rfcomm_dlc_put(d);
+}
+
+static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d)
+{
+	BT_DBG("%p", d);
+
+	d->state      = BT_OPEN;
+	d->flags      = 0;
+	d->mscex      = 0;
+	d->mtu        = RFCOMM_DEFAULT_MTU;
+	d->v24_sig    = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV;
+
+	d->cfc        = RFCOMM_CFC_DISABLED;
+	d->rx_credits = RFCOMM_DEFAULT_CREDITS;
+}
+
+struct rfcomm_dlc *rfcomm_dlc_alloc(int prio)
+{
+	struct rfcomm_dlc *d = kmalloc(sizeof(*d), prio);
+	if (!d)
+		return NULL;
+	memset(d, 0, sizeof(*d));
+
+	init_timer(&d->timer);
+	d->timer.function = rfcomm_dlc_timeout;
+	d->timer.data = (unsigned long) d;
+
+	skb_queue_head_init(&d->tx_queue);
+	spin_lock_init(&d->lock);
+	atomic_set(&d->refcnt, 1);
+
+	rfcomm_dlc_clear_state(d);
+	
+	BT_DBG("%p", d);
+	return d;
+}
+
+void rfcomm_dlc_free(struct rfcomm_dlc *d)
+{
+	BT_DBG("%p", d);
+
+	skb_queue_purge(&d->tx_queue);
+	kfree(d);
+}
+
+static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
+{
+	BT_DBG("dlc %p session %p", d, s);
+
+	rfcomm_session_hold(s);
+
+	rfcomm_dlc_hold(d);
+	list_add(&d->list, &s->dlcs);
+	d->session = s;
+}
+
+static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
+{
+	struct rfcomm_session *s = d->session;
+
+	BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s);
+
+	list_del(&d->list);
+	d->session = NULL;
+	rfcomm_dlc_put(d);
+
+	rfcomm_session_put(s);
+}
+
+static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
+{
+	struct rfcomm_dlc *d;
+	struct list_head *p;
+
+	list_for_each(p, &s->dlcs) {
+		d = list_entry(p, struct rfcomm_dlc, list);
+		if (d->dlci == dlci)
+			return d;
+	}
+	return NULL;
+}
+
+static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
+{
+	struct rfcomm_session *s;
+	int err = 0;
+	u8 dlci;
+
+	BT_DBG("dlc %p state %ld %s %s channel %d", 
+			d, d->state, batostr(src), batostr(dst), channel);
+
+	if (channel < 1 || channel > 30)
+		return -EINVAL;
+
+	if (d->state != BT_OPEN && d->state != BT_CLOSED)
+		return 0;
+
+	s = rfcomm_session_get(src, dst);
+	if (!s) {
+		s = rfcomm_session_create(src, dst, &err);
+		if (!s)
+			return err;
+	}
+
+	dlci = __dlci(!s->initiator, channel);
+
+	/* Check if DLCI already exists */
+	if (rfcomm_dlc_get(s, dlci))
+		return -EBUSY;
+
+	rfcomm_dlc_clear_state(d);
+
+	d->dlci     = dlci;
+	d->addr     = __addr(s->initiator, dlci);
+	d->priority = 7;
+
+	d->state    = BT_CONFIG;
+	rfcomm_dlc_link(s, d);
+
+	d->mtu = s->mtu;
+	d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
+
+	if (s->state == BT_CONNECTED)
+		rfcomm_send_pn(s, 1, d);
+	rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
+	return 0;
+}
+
+int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
+{
+	mm_segment_t fs;
+	int r;
+
+	rfcomm_lock();
+
+	fs = get_fs(); set_fs(KERNEL_DS);
+	r = __rfcomm_dlc_open(d, src, dst, channel);
+	set_fs(fs);
+
+	rfcomm_unlock();
+	return r;
+}
+
+static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
+{
+	struct rfcomm_session *s = d->session;
+	if (!s)
+		return 0;
+
+	BT_DBG("dlc %p state %ld dlci %d err %d session %p",
+			d, d->state, d->dlci, err, s);
+
+	switch (d->state) {
+	case BT_CONNECTED:
+	case BT_CONFIG:
+	case BT_CONNECT:
+		d->state = BT_DISCONN;
+		if (skb_queue_empty(&d->tx_queue)) {
+			rfcomm_send_disc(s, d->dlci);
+			rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
+		} else {
+			rfcomm_queue_disc(d);
+			rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
+		}
+		break;
+
+	default:
+		rfcomm_dlc_clear_timer(d);
+
+		rfcomm_dlc_lock(d);
+		d->state = BT_CLOSED;
+		d->state_change(d, err);
+		rfcomm_dlc_unlock(d);
+
+		skb_queue_purge(&d->tx_queue);
+		rfcomm_dlc_unlink(d);
+	}
+
+	return 0;
+}
+
+int rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
+{
+	mm_segment_t fs;
+	int r;
+
+	rfcomm_lock();
+
+	fs = get_fs(); set_fs(KERNEL_DS);
+	r = __rfcomm_dlc_close(d, err);
+	set_fs(fs);
+
+	rfcomm_unlock();
+	return r;
+}
+
+int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
+{
+	int len = skb->len;
+
+	if (d->state != BT_CONNECTED)
+		return -ENOTCONN;
+
+	BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
+
+	if (len > d->mtu)
+		return -EINVAL;
+
+	rfcomm_make_uih(skb, d->addr);
+	skb_queue_tail(&d->tx_queue, skb);
+
+	if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags))
+		rfcomm_schedule(RFCOMM_SCHED_TX);
+	return len;
+}
+
+void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
+{
+	BT_DBG("dlc %p state %ld", d, d->state);
+
+	if (!d->cfc) {
+		d->v24_sig |= RFCOMM_V24_FC;
+		set_bit(RFCOMM_MSC_PENDING, &d->flags);
+	}
+	rfcomm_schedule(RFCOMM_SCHED_TX);
+}
+
+void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
+{
+	BT_DBG("dlc %p state %ld", d, d->state);
+
+	if (!d->cfc) {
+		d->v24_sig &= ~RFCOMM_V24_FC;
+		set_bit(RFCOMM_MSC_PENDING, &d->flags);
+	}
+	rfcomm_schedule(RFCOMM_SCHED_TX);
+}
+
+/* 
+   Set/get modem status functions use _local_ status i.e. what we report
+   to the other side.
+   Remote status is provided by dlc->modem_status() callback.
+ */
+int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig)
+{
+	BT_DBG("dlc %p state %ld v24_sig 0x%x", 
+			d, d->state, v24_sig);
+
+	if (test_bit(RFCOMM_RX_THROTTLED, &d->flags))
+		v24_sig |= RFCOMM_V24_FC;
+	else
+		v24_sig &= ~RFCOMM_V24_FC;
+	
+	d->v24_sig = v24_sig;
+
+	if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags))
+		rfcomm_schedule(RFCOMM_SCHED_TX);
+
+	return 0;
+}
+
+int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig)
+{
+	BT_DBG("dlc %p state %ld v24_sig 0x%x", 
+			d, d->state, d->v24_sig);
+
+	*v24_sig = d->v24_sig;
+	return 0;
+}
+
+/* ---- RFCOMM sessions ---- */
+struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
+{
+	struct rfcomm_session *s = kmalloc(sizeof(*s), GFP_KERNEL);
+	if (!s)
+		return NULL;
+	memset(s, 0, sizeof(*s));
+	
+	BT_DBG("session %p sock %p", s, sock);
+
+	INIT_LIST_HEAD(&s->dlcs);
+	s->state = state;
+	s->sock  = sock;
+
+	s->mtu   = RFCOMM_DEFAULT_MTU;
+	s->cfc   = RFCOMM_CFC_UNKNOWN;
+	
+	list_add(&s->list, &session_list);
+
+	/* Do not increment module usage count for listeting sessions.
+	 * Otherwise we won't be able to unload the module. */
+	if (state != BT_LISTEN)
+		MOD_INC_USE_COUNT;
+	return s;
+}
+
+void rfcomm_session_del(struct rfcomm_session *s)
+{
+	int state = s->state;
+	
+	BT_DBG("session %p state %ld", s, s->state);
+
+	list_del(&s->list);
+
+	if (state == BT_CONNECTED)
+		rfcomm_send_disc(s, 0);
+
+	sock_release(s->sock);
+	kfree(s);
+
+	if (state != BT_LISTEN)
+		MOD_DEC_USE_COUNT;
+}
+
+struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
+{
+	struct rfcomm_session *s;
+	struct list_head *p, *n;
+	struct bluez_pinfo *pi;
+	list_for_each_safe(p, n, &session_list) {
+		s = list_entry(p, struct rfcomm_session, list);
+		pi = bluez_pi(s->sock->sk); 
+
+		if ((!bacmp(src, BDADDR_ANY) || !bacmp(&pi->src, src)) &&
+				!bacmp(&pi->dst, dst))
+			return s;
+	}
+	return NULL;
+}
+
+void rfcomm_session_close(struct rfcomm_session *s, int err)
+{
+	struct rfcomm_dlc *d;
+	struct list_head *p, *n;
+
+	BT_DBG("session %p state %ld err %d", s, s->state, err);
+
+	rfcomm_session_hold(s);
+
+	s->state = BT_CLOSED;
+
+	/* Close all dlcs */
+	list_for_each_safe(p, n, &s->dlcs) {
+		d = list_entry(p, struct rfcomm_dlc, list);
+		d->state = BT_CLOSED;
+		__rfcomm_dlc_close(d, err);
+	}
+
+	rfcomm_session_put(s);
+}
+
+struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err)
+{
+	struct rfcomm_session *s = NULL;
+	struct sockaddr_l2 addr;
+	struct l2cap_options opts;
+	struct socket *sock;
+	int    size;
+
+	BT_DBG("%s %s", batostr(src), batostr(dst));
+
+	*err = rfcomm_l2sock_create(&sock);
+	if (*err < 0)
+		return NULL;
+
+	bacpy(&addr.l2_bdaddr, src);
+	addr.l2_family = AF_BLUETOOTH;
+	addr.l2_psm    = 0;
+	*err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (*err < 0)
+		goto failed;
+
+	/* Set L2CAP options */
+	size = sizeof(opts);
+	sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
+	
+	opts.imtu = RFCOMM_MAX_L2CAP_MTU;
+	sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
+
+	s = rfcomm_session_add(sock, BT_BOUND);
+	if (!s) {
+		*err = -ENOMEM;
+		goto failed;
+	}
+
+	s->initiator = 1;
+
+	bacpy(&addr.l2_bdaddr, dst);
+	addr.l2_family = AF_BLUETOOTH;
+	addr.l2_psm    = htobs(RFCOMM_PSM);
+	*err = sock->ops->connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
+	if (*err == 0 || *err == -EAGAIN)
+		return s;
+
+	rfcomm_session_del(s);
+	return NULL;
+
+failed:
+	sock_release(sock);
+	return NULL;
+}
+
+void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst)
+{
+	struct sock *sk = s->sock->sk;
+	if (src)
+		bacpy(src, &bluez_pi(sk)->src);
+	if (dst)
+		bacpy(dst, &bluez_pi(sk)->dst);
+}
+
+/* ---- RFCOMM frame sending ---- */
+static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
+{
+	struct socket *sock = s->sock;
+	struct iovec iv = { data, len };
+	struct msghdr msg;
+	int err;
+
+	BT_DBG("session %p len %d", s, len);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iovlen = 1;
+	msg.msg_iov = &iv;
+
+	err = sock->ops->sendmsg(sock, &msg, len, 0);
+	return err;
+}
+
+static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	BT_DBG("%p dlci %d", s, dlci);
+
+	cmd.addr = __addr(s->initiator, dlci);
+	cmd.ctrl = __ctrl(RFCOMM_SABM, 1);
+	cmd.len  = __len8(0);
+	cmd.fcs  = __fcs2((u8 *) &cmd);
+
+	return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+}
+
+static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	BT_DBG("%p dlci %d", s, dlci);
+
+	cmd.addr = __addr(!s->initiator, dlci);
+	cmd.ctrl = __ctrl(RFCOMM_UA, 1);
+	cmd.len  = __len8(0);
+	cmd.fcs  = __fcs2((u8 *) &cmd);
+
+	return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+}
+
+static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	BT_DBG("%p dlci %d", s, dlci);
+
+	cmd.addr = __addr(s->initiator, dlci);
+	cmd.ctrl = __ctrl(RFCOMM_DISC, 1);
+	cmd.len  = __len8(0);
+	cmd.fcs  = __fcs2((u8 *) &cmd);
+
+	return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+}
+
+static int rfcomm_queue_disc(struct rfcomm_dlc *d)
+{
+	struct rfcomm_cmd *cmd;
+	struct sk_buff *skb;
+
+	BT_DBG("dlc %p dlci %d", d, d->dlci);
+
+	skb = alloc_skb(sizeof(*cmd), GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (void *) __skb_put(skb, sizeof(*cmd));
+	cmd->addr = d->addr;
+	cmd->ctrl = __ctrl(RFCOMM_DISC, 1);
+	cmd->len  = __len8(0);
+	cmd->fcs  = __fcs2((u8 *) cmd);
+
+	skb_queue_tail(&d->tx_queue, skb);
+	rfcomm_schedule(RFCOMM_SCHED_TX);
+	return 0;
+}
+
+static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	BT_DBG("%p dlci %d", s, dlci);
+
+	cmd.addr = __addr(!s->initiator, dlci);
+	cmd.ctrl = __ctrl(RFCOMM_DM, 1);
+	cmd.len  = __len8(0);
+	cmd.fcs  = __fcs2((u8 *) &cmd);
+
+	return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+}
+
+static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d type %d", s, cr, type);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc) + 1);
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(cr, RFCOMM_NSC);
+	mcc->len  = __len8(1);
+
+	/* Type that we didn't like */
+	*ptr = __mcc_type(cr, type); ptr++;
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	struct rfcomm_pn  *pn;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc) + sizeof(*pn));
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(cr, RFCOMM_PN);
+	mcc->len  = __len8(sizeof(*pn));
+
+	pn = (void *) ptr; ptr += sizeof(*pn);
+	pn->dlci        = d->dlci;
+	pn->priority    = d->priority;
+	pn->ack_timer   = 0;
+	pn->max_retrans = 0;
+
+	if (s->cfc) {
+		pn->flow_ctrl = cr ? 0xf0 : 0xe0;
+		pn->credits = RFCOMM_DEFAULT_CREDITS;
+	} else {
+		pn->flow_ctrl = 0;
+		pn->credits   = 0;
+	}
+
+	pn->mtu = htobs(d->mtu);
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
+			   u8 bit_rate, u8 data_bits, u8 stop_bits,
+			   u8 parity, u8 flow_ctrl_settings, 
+			   u8 xon_char, u8 xoff_char, u16 param_mask)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	struct rfcomm_rpn *rpn;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x"
+	       "flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", 
+			s, cr, dlci, bit_rate, data_bits, stop_bits, parity, 
+			flow_ctrl_settings, xon_char, xoff_char, param_mask);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc) + sizeof(*rpn));
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(cr, RFCOMM_RPN);
+	mcc->len  = __len8(sizeof(*rpn));
+
+	rpn = (void *) ptr; ptr += sizeof(*rpn);
+	rpn->dlci          = __addr(1, dlci);
+	rpn->bit_rate      = bit_rate;
+	rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity);
+	rpn->flow_ctrl     = flow_ctrl_settings;
+	rpn->xon_char      = xon_char;
+	rpn->xoff_char     = xoff_char;
+	rpn->param_mask    = param_mask;
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	struct rfcomm_rls *rls;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d status 0x%x", s, cr, status);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc) + sizeof(*rls));
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(cr, RFCOMM_RLS);
+	mcc->len  = __len8(sizeof(*rls));
+
+	rls = (void *) ptr; ptr += sizeof(*rls);
+	rls->dlci   = __addr(1, dlci);
+	rls->status = status;
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	struct rfcomm_msc *msc;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc) + sizeof(*msc));
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(cr, RFCOMM_MSC);
+	mcc->len  = __len8(sizeof(*msc));
+
+	msc = (void *) ptr; ptr += sizeof(*msc);
+	msc->dlci    = __addr(1, dlci);
+	msc->v24_sig = v24_sig | 0x01;
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d", s, cr);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc));
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(cr, RFCOMM_FCOFF);
+	mcc->len  = __len8(0);
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d", s, cr);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc));
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(cr, RFCOMM_FCON);
+	mcc->len  = __len8(0);
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
+{
+	struct socket *sock = s->sock;
+	struct iovec iv[3];
+	struct msghdr msg;
+	unsigned char hdr[5], crc[1];
+
+	if (len > 125)
+		return -EINVAL;
+
+	BT_DBG("%p cr %d", s, cr);
+
+	hdr[0] = __addr(s->initiator, 0);
+	hdr[1] = __ctrl(RFCOMM_UIH, 0);
+	hdr[2] = 0x01 | ((len + 2) << 1);
+	hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2);
+	hdr[4] = 0x01 | (len << 1);
+
+	crc[0] = __fcs(hdr);
+
+	iv[0].iov_base = hdr;
+	iv[0].iov_len  = 5;
+	iv[1].iov_base = pattern;
+	iv[1].iov_len  = len;
+	iv[2].iov_base = crc;
+	iv[2].iov_len  = 1;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iovlen = 3;
+	msg.msg_iov = iv;
+	return sock->ops->sendmsg(sock, &msg, 6 + len, 0);
+}
+
+static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
+{
+	struct rfcomm_hdr *hdr;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p addr %d credits %d", s, addr, credits);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = addr;
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 1);
+	hdr->len  = __len8(0);
+
+	*ptr = credits; ptr++;
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
+{
+	struct rfcomm_hdr *hdr;
+	int len = skb->len;
+	u8 *crc;
+
+	if (len > 127) {
+		hdr = (void *) skb_push(skb, 4);
+		put_unaligned(htobs(__len16(len)), (u16 *) &hdr->len);
+	} else {
+		hdr = (void *) skb_push(skb, 3);
+		hdr->len = __len8(len);
+	}
+	hdr->addr = addr;
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+
+	crc = skb_put(skb, 1);
+	*crc = __fcs((void *) hdr);
+}
+
+/* ---- RFCOMM frame reception ---- */
+static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
+{
+	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
+
+	if (dlci) {
+		/* Data channel */
+		struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
+		if (!d) {
+			rfcomm_send_dm(s, dlci);
+			return 0;
+		}
+
+		switch (d->state) {
+		case BT_CONNECT:
+			rfcomm_dlc_clear_timer(d);
+
+			rfcomm_dlc_lock(d);
+			d->state = BT_CONNECTED;
+			d->state_change(d, 0);
+			rfcomm_dlc_unlock(d);
+
+			rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+			break;
+
+		case BT_DISCONN:
+			d->state = BT_CLOSED;
+			__rfcomm_dlc_close(d, 0);
+			break;
+		}
+	} else {
+		/* Control channel */
+		switch (s->state) {
+		case BT_CONNECT:
+			s->state = BT_CONNECTED;
+			rfcomm_process_connect(s);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci)
+{
+	int err = 0;
+
+	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
+
+	if (dlci) {
+		/* Data DLC */
+		struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
+		if (d) {
+			if (d->state == BT_CONNECT || d->state == BT_CONFIG)
+				err = ECONNREFUSED;
+			else
+				err = ECONNRESET;
+
+			d->state = BT_CLOSED;
+			__rfcomm_dlc_close(d, err);
+		}
+	} else {
+		if (s->state == BT_CONNECT)
+			err = ECONNREFUSED;
+		else
+			err = ECONNRESET;
+
+		s->state = BT_CLOSED;
+		rfcomm_session_close(s, err);
+	}
+	return 0;
+}
+
+static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
+{
+	int err = 0;
+
+	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
+
+	if (dlci) {
+		struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
+		if (d) {
+			rfcomm_send_ua(s, dlci);
+
+			if (d->state == BT_CONNECT || d->state == BT_CONFIG)
+				err = ECONNREFUSED;
+			else
+				err = ECONNRESET;
+
+			d->state = BT_CLOSED;
+			__rfcomm_dlc_close(d, err);
+		} else 
+			rfcomm_send_dm(s, dlci);
+			
+	} else {
+		rfcomm_send_ua(s, 0);
+
+		if (s->state == BT_CONNECT)
+			err = ECONNREFUSED;
+		else
+			err = ECONNRESET;
+
+		s->state = BT_CLOSED;
+		rfcomm_session_close(s, err);
+	}
+
+	return 0;
+}
+
+static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
+{
+	struct rfcomm_dlc *d;
+	u8 channel;
+
+	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
+
+	if (!dlci) {
+		rfcomm_send_ua(s, 0);
+
+		if (s->state == BT_OPEN) {
+			s->state = BT_CONNECTED;
+			rfcomm_process_connect(s);
+		}
+		return 0;
+	}
+
+	/* Check if DLC exists */
+	d = rfcomm_dlc_get(s, dlci);
+	if (d) {
+		if (d->state == BT_OPEN) {
+			/* DLC was previously opened by PN request */
+			rfcomm_send_ua(s, dlci);
+
+			rfcomm_dlc_lock(d);
+			d->state = BT_CONNECTED;
+			d->state_change(d, 0);
+			rfcomm_dlc_unlock(d);
+
+			rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+		}
+		return 0;
+	}
+
+	/* Notify socket layer about incomming connection */
+	channel = __srv_channel(dlci);
+	if (rfcomm_connect_ind(s, channel, &d)) {
+		d->dlci = dlci;
+		d->addr = __addr(s->initiator, dlci);
+		rfcomm_dlc_link(s, d);
+
+		rfcomm_send_ua(s, dlci);
+
+		rfcomm_dlc_lock(d);
+		d->state = BT_CONNECTED;
+		d->state_change(d, 0);
+		rfcomm_dlc_unlock(d);
+
+		rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+	} else {
+		rfcomm_send_dm(s, dlci);
+	}
+
+	return 0;
+}
+
+static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
+{
+	struct rfcomm_session *s = d->session;
+
+	BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", 
+			d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits);
+
+	if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) {
+		d->cfc = s->cfc = RFCOMM_CFC_ENABLED;
+		d->tx_credits = pn->credits;
+	} else {
+		d->cfc = s->cfc = RFCOMM_CFC_DISABLED;
+		set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+	}
+
+	d->priority = pn->priority;
+
+	d->mtu = s->mtu = btohs(pn->mtu);
+
+	return 0;
+}
+
+static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb)
+{
+	struct rfcomm_pn *pn = (void *) skb->data;
+	struct rfcomm_dlc *d;
+	u8 dlci = pn->dlci;
+
+	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
+
+	if (!dlci)
+		return 0;
+
+	d = rfcomm_dlc_get(s, dlci);
+	if (d) {
+		if (cr) {
+			/* PN request */
+			rfcomm_apply_pn(d, cr, pn);
+			rfcomm_send_pn(s, 0, d);
+		} else {
+			/* PN response */
+			switch (d->state) {
+			case BT_CONFIG:
+				rfcomm_apply_pn(d, cr, pn);
+
+				d->state = BT_CONNECT;
+				rfcomm_send_sabm(s, d->dlci);
+				break;
+			}
+		}
+	} else {
+		u8 channel = __srv_channel(dlci);
+
+		if (!cr)
+			return 0;
+
+		/* PN request for non existing DLC.
+		 * Assume incomming connection. */
+		if (rfcomm_connect_ind(s, channel, &d)) {
+			d->dlci = dlci;
+			d->addr = __addr(s->initiator, dlci);
+			rfcomm_dlc_link(s, d);
+
+			rfcomm_apply_pn(d, cr, pn);
+
+			d->state = BT_OPEN;
+			rfcomm_send_pn(s, 0, d);
+		} else {
+			rfcomm_send_dm(s, dlci);
+		}
+	}
+	return 0;
+}
+
+static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb)
+{
+	struct rfcomm_rpn *rpn = (void *) skb->data;
+	u8 dlci = __get_dlci(rpn->dlci);
+
+	u8 bit_rate  = 0;
+	u8 data_bits = 0;
+	u8 stop_bits = 0;
+	u8 parity    = 0;
+	u8 flow_ctrl = 0;
+	u8 xon_char  = 0;
+	u8 xoff_char = 0;
+	u16 rpn_mask = RFCOMM_RPN_PM_ALL;
+	
+	BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", 
+	       dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
+	       rpn->xon_char, rpn->xoff_char, rpn->param_mask);
+	
+	if (!cr) 
+		return 0;
+	
+	if (len == 1) {
+		/* request: return default setting */
+		bit_rate  = RFCOMM_RPN_BR_115200;
+		data_bits = RFCOMM_RPN_DATA_8;
+		stop_bits = RFCOMM_RPN_STOP_1;
+		parity    = RFCOMM_RPN_PARITY_NONE;
+		flow_ctrl = RFCOMM_RPN_FLOW_NONE;
+		xon_char  = RFCOMM_RPN_XON_CHAR;
+		xoff_char = RFCOMM_RPN_XOFF_CHAR;
+
+		goto rpn_out;
+	}
+	/* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity,
+	                          no flow control lines, normal XON/XOFF chars */
+	if (rpn->param_mask & RFCOMM_RPN_PM_BITRATE) {
+		bit_rate = rpn->bit_rate;
+		if (bit_rate != RFCOMM_RPN_BR_115200) {
+			BT_DBG("RPN bit rate mismatch 0x%x", bit_rate);
+			bit_rate = RFCOMM_RPN_BR_115200;
+			rpn_mask ^= RFCOMM_RPN_PM_BITRATE;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_DATA) {
+		data_bits = __get_rpn_data_bits(rpn->line_settings);
+		if (data_bits != RFCOMM_RPN_DATA_8) {
+			BT_DBG("RPN data bits mismatch 0x%x", data_bits);
+			data_bits = RFCOMM_RPN_DATA_8;
+			rpn_mask ^= RFCOMM_RPN_PM_DATA;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_STOP) {
+		stop_bits = __get_rpn_stop_bits(rpn->line_settings);
+		if (stop_bits != RFCOMM_RPN_STOP_1) {
+			BT_DBG("RPN stop bits mismatch 0x%x", stop_bits);
+			stop_bits = RFCOMM_RPN_STOP_1;
+			rpn_mask ^= RFCOMM_RPN_PM_STOP;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_PARITY) {
+		parity = __get_rpn_parity(rpn->line_settings);
+		if (parity != RFCOMM_RPN_PARITY_NONE) {
+			BT_DBG("RPN parity mismatch 0x%x", parity);
+			parity = RFCOMM_RPN_PARITY_NONE;
+			rpn_mask ^= RFCOMM_RPN_PM_PARITY;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
+		flow_ctrl = rpn->flow_ctrl;
+		if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
+			BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl);
+			flow_ctrl = RFCOMM_RPN_FLOW_NONE;
+			rpn_mask ^= RFCOMM_RPN_PM_FLOW;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_XON) {
+		xon_char = rpn->xon_char;
+		if (xon_char != RFCOMM_RPN_XON_CHAR) {
+			BT_DBG("RPN XON char mismatch 0x%x", xon_char);
+			xon_char = RFCOMM_RPN_XON_CHAR;
+			rpn_mask ^= RFCOMM_RPN_PM_XON;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) {
+		xoff_char = rpn->xoff_char;
+		if (xoff_char != RFCOMM_RPN_XOFF_CHAR) {
+			BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char);
+			xoff_char = RFCOMM_RPN_XOFF_CHAR;
+			rpn_mask ^= RFCOMM_RPN_PM_XOFF;
+		}
+	}
+
+rpn_out:
+	rfcomm_send_rpn(s, 0, dlci, 
+			bit_rate, data_bits, stop_bits, parity, flow_ctrl,
+			xon_char, xoff_char, rpn_mask);
+
+	return 0;
+}
+
+static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb)
+{
+	struct rfcomm_rls *rls = (void *) skb->data;
+	u8 dlci = __get_dlci(rls->dlci);
+
+	BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status);
+	
+	if (!cr)
+		return 0;
+
+	/* FIXME: We should probably do something with this
+	   information here. But for now it's sufficient just
+	   to reply -- Bluetooth 1.1 says it's mandatory to 
+	   recognise and respond to RLS */
+
+	rfcomm_send_rls(s, 0, dlci, rls->status);
+
+	return 0;
+}
+
+static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb)
+{
+	struct rfcomm_msc *msc = (void *) skb->data;
+	struct rfcomm_dlc *d;
+	u8 dlci = __get_dlci(msc->dlci);
+
+	BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
+
+	d = rfcomm_dlc_get(s, dlci);
+	if (!d)
+		return 0;
+
+	if (cr) {
+		if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc)
+			set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+		else
+			clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
+		
+		rfcomm_dlc_lock(d);
+		if (d->modem_status)
+			d->modem_status(d, msc->v24_sig);
+		rfcomm_dlc_unlock(d);
+		
+		rfcomm_send_msc(s, 0, dlci, msc->v24_sig);
+
+		d->mscex |= RFCOMM_MSCEX_RX;
+	} else 
+		d->mscex |= RFCOMM_MSCEX_TX;
+
+	return 0;
+}
+
+static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb)
+{
+	struct rfcomm_mcc *mcc = (void *) skb->data;
+	u8 type, cr, len;
+
+	cr   = __test_cr(mcc->type);
+	type = __get_mcc_type(mcc->type);
+	len  = __get_mcc_len(mcc->len);
+
+	BT_DBG("%p type 0x%x cr %d", s, type, cr);
+
+	skb_pull(skb, 2);
+
+	switch (type) {
+	case RFCOMM_PN:
+		rfcomm_recv_pn(s, cr, skb);
+		break;
+
+	case RFCOMM_RPN:
+		rfcomm_recv_rpn(s, cr, len, skb);
+		break;
+
+	case RFCOMM_RLS:
+		rfcomm_recv_rls(s, cr, skb);
+		break;
+
+	case RFCOMM_MSC:
+		rfcomm_recv_msc(s, cr, skb);
+		break;
+
+	case RFCOMM_FCOFF:
+		if (cr) {
+			set_bit(RFCOMM_TX_THROTTLED, &s->flags);
+			rfcomm_send_fcoff(s, 0);
+		}
+		break;
+
+	case RFCOMM_FCON:
+		if (cr) {
+			clear_bit(RFCOMM_TX_THROTTLED, &s->flags);
+			rfcomm_send_fcon(s, 0);
+		}
+		break;
+
+	case RFCOMM_TEST:
+		if (cr)
+			rfcomm_send_test(s, 0, skb->data, skb->len);
+		break;
+
+	case RFCOMM_NSC:
+		break;
+
+	default:
+		BT_ERR("Unknown control type 0x%02x", type);
+		rfcomm_send_nsc(s, cr, type);
+		break;
+	}
+	return 0;
+}
+
+static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk_buff *skb)
+{
+	struct rfcomm_dlc *d;
+
+	BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf);
+
+	d = rfcomm_dlc_get(s, dlci);
+	if (!d) {
+		rfcomm_send_dm(s, dlci);
+		goto drop;
+	}
+
+	if (pf && d->cfc) {
+		u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
+
+		d->tx_credits += credits;
+		if (d->tx_credits)
+			clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
+	}
+
+	if (skb->len && d->state == BT_CONNECTED) {
+		rfcomm_dlc_lock(d);
+		d->rx_credits--;
+		d->data_ready(d, skb);
+		rfcomm_dlc_unlock(d);
+		return 0;
+	}
+
+drop:
+	kfree_skb(skb);
+	return 0;
+}
+
+static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
+{
+	struct rfcomm_hdr *hdr = (void *) skb->data;
+	u8 type, dlci, fcs;
+
+	dlci = __get_dlci(hdr->addr);
+	type = __get_type(hdr->ctrl);
+
+	/* Trim FCS */
+	skb->len--; skb->tail--;
+	fcs = *(u8 *) skb->tail;
+	
+	if (__check_fcs(skb->data, type, fcs)) {
+		BT_ERR("bad checksum in packet");
+		kfree_skb(skb);
+		return -EILSEQ;
+	}
+
+	if (__test_ea(hdr->len))
+		skb_pull(skb, 3);
+	else
+		skb_pull(skb, 4);
+	
+	switch (type) {
+	case RFCOMM_SABM:
+		if (__test_pf(hdr->ctrl))
+			rfcomm_recv_sabm(s, dlci);
+		break;
+
+	case RFCOMM_DISC:
+		if (__test_pf(hdr->ctrl))
+			rfcomm_recv_disc(s, dlci);
+		break;
+
+	case RFCOMM_UA:
+		if (__test_pf(hdr->ctrl))
+			rfcomm_recv_ua(s, dlci);
+		break;
+
+	case RFCOMM_DM:
+		rfcomm_recv_dm(s, dlci);
+		break;
+
+	case RFCOMM_UIH:
+		if (dlci)
+			return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb);
+
+		rfcomm_recv_mcc(s, skb);
+		break;
+
+	default:
+		BT_ERR("Unknown packet type 0x%02x\n", type);
+		break;
+	}
+	kfree_skb(skb);
+	return 0;
+}
+
+/* ---- Connection and data processing ---- */
+
+static void rfcomm_process_connect(struct rfcomm_session *s)
+{
+	struct rfcomm_dlc *d;
+	struct list_head *p, *n;
+
+	BT_DBG("session %p state %ld", s, s->state);
+
+	list_for_each_safe(p, n, &s->dlcs) {
+		d = list_entry(p, struct rfcomm_dlc, list);
+		if (d->state == BT_CONFIG) {
+			d->mtu = s->mtu;
+			rfcomm_send_pn(s, 1, d);
+		}
+	}
+}
+
+/* Send data queued for the DLC.
+ * Return number of frames left in the queue.
+ */
+static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
+{
+	struct sk_buff *skb;
+	int err;
+
+	BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d", 
+			d, d->state, d->cfc, d->rx_credits, d->tx_credits);
+
+	/* Send pending MSC */
+	if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
+		rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
+	
+	if (d->cfc) {
+		/* CFC enabled. 
+		 * Give them some credits */
+		if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
+			       	d->rx_credits <= (d->cfc >> 2)) {
+			rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits);
+			d->rx_credits = d->cfc;
+		}
+	} else {
+		/* CFC disabled. 
+		 * Give ourselves some credits */
+		d->tx_credits = 5;
+	}
+
+	if (test_bit(RFCOMM_TX_THROTTLED, &d->flags))
+		return skb_queue_len(&d->tx_queue);
+
+	while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) {
+		err = rfcomm_send_frame(d->session, skb->data, skb->len);
+		if (err < 0) {
+			skb_queue_head(&d->tx_queue, skb);
+			break;
+		}
+		kfree_skb(skb);
+		d->tx_credits--;
+	}
+
+	if (d->cfc && !d->tx_credits) {
+		/* We're out of TX credits.
+		 * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */
+		set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+	}
+
+	return skb_queue_len(&d->tx_queue);
+}
+
+static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
+{
+	struct rfcomm_dlc *d;
+	struct list_head *p, *n;
+
+	BT_DBG("session %p state %ld", s, s->state);
+
+	list_for_each_safe(p, n, &s->dlcs) {
+		d = list_entry(p, struct rfcomm_dlc, list);
+		if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) {
+			__rfcomm_dlc_close(d, ETIMEDOUT);
+			continue;
+		}
+
+		if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
+			continue;
+
+		if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) &&
+				d->mscex == RFCOMM_MSCEX_OK)
+			rfcomm_process_tx(d);
+	}
+}
+
+static inline void rfcomm_process_rx(struct rfcomm_session *s)
+{
+	struct socket *sock = s->sock;
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+
+	BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->receive_queue));
+
+	/* Get data directly from socket receive queue without copying it. */
+	while ((skb = skb_dequeue(&sk->receive_queue))) {
+		skb_orphan(skb);
+		rfcomm_recv_frame(s, skb);
+	}
+
+	if (sk->state == BT_CLOSED) {
+		if (!s->initiator)
+			rfcomm_session_put(s);
+
+		rfcomm_session_close(s, sk->err);
+	}
+}
+
+static inline void rfcomm_accept_connection(struct rfcomm_session *s)
+{
+	struct socket *sock = s->sock, *nsock;
+	int err;
+
+	/* Fast check for a new connection.
+	 * Avoids unnesesary socket allocations. */
+	if (list_empty(&bluez_pi(sock->sk)->accept_q))
+		return;
+
+	BT_DBG("session %p", s);
+
+	nsock = sock_alloc();
+	if (!nsock)
+		return;
+
+	nsock->type = sock->type;
+	nsock->ops  = sock->ops;
+	
+	err = sock->ops->accept(sock, nsock, O_NONBLOCK);
+	if (err < 0) {
+		sock_release(nsock);
+		return;
+	}
+
+	/* Set our callbacks */
+	nsock->sk->data_ready   = rfcomm_l2data_ready;
+	nsock->sk->state_change = rfcomm_l2state_change;
+
+	s = rfcomm_session_add(nsock, BT_OPEN);
+	if (s) {
+		rfcomm_session_hold(s);
+		rfcomm_schedule(RFCOMM_SCHED_RX);
+	} else
+		sock_release(nsock);
+}
+
+static inline void rfcomm_check_connection(struct rfcomm_session *s)
+{
+	struct sock *sk = s->sock->sk;
+
+	BT_DBG("%p state %ld", s, s->state);
+
+	switch(sk->state) {
+	case BT_CONNECTED:
+		s->state = BT_CONNECT;
+
+		/* We can adjust MTU on outgoing sessions.
+		 * L2CAP MTU minus UIH header and FCS. */
+		s->mtu = min(l2cap_pi(sk)->omtu, l2cap_pi(sk)->imtu) - 5;
+
+		rfcomm_send_sabm(s, 0);
+		break;
+
+	case BT_CLOSED:
+		s->state = BT_CLOSED;
+		rfcomm_session_close(s, sk->err);
+		break;
+	}
+}
+
+static inline void rfcomm_process_sessions(void)
+{
+	struct list_head *p, *n;
+
+	rfcomm_lock();
+
+	list_for_each_safe(p, n, &session_list) {
+		struct rfcomm_session *s;
+		s = list_entry(p, struct rfcomm_session, list);
+
+		if (s->state == BT_LISTEN) {
+			rfcomm_accept_connection(s);
+			continue;
+		}
+
+		rfcomm_session_hold(s);
+
+		switch (s->state) {
+		case BT_BOUND:
+			rfcomm_check_connection(s);
+			break;
+
+		default:
+			rfcomm_process_rx(s);
+			break;
+		}
+
+		rfcomm_process_dlcs(s);
+
+		rfcomm_session_put(s);
+	}
+	
+	rfcomm_unlock();
+}
+
+static void rfcomm_worker(void)
+{
+	BT_DBG("");
+
+	daemonize(); reparent_to_init();
+	set_fs(KERNEL_DS);
+
+	while (!atomic_read(&terminate)) {
+		BT_DBG("worker loop event 0x%lx", rfcomm_event);
+
+		if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) {
+			/* No pending events. Let's sleep.
+			 * Incomming connections and data will wake us up. */
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+		}
+
+		/* Process stuff */
+		clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event);
+		rfcomm_process_sessions();
+	}
+	set_current_state(TASK_RUNNING);
+	return;
+}
+
+static int rfcomm_add_listener(bdaddr_t *ba)
+{
+	struct sockaddr_l2 addr;
+	struct l2cap_options opts;
+	struct socket *sock;
+	struct rfcomm_session *s;
+	int    size, err = 0;
+
+	/* Create socket */
+	err = rfcomm_l2sock_create(&sock);
+	if (err < 0) { 
+		BT_ERR("Create socket failed %d", err);
+		return err;
+	}
+
+	/* Bind socket */
+	bacpy(&addr.l2_bdaddr, ba);
+	addr.l2_family = AF_BLUETOOTH;
+	addr.l2_psm    = htobs(RFCOMM_PSM);
+	err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0) {
+		BT_ERR("Bind failed %d", err);
+		goto failed;
+	}
+
+	/* Set L2CAP options */
+	size = sizeof(opts);
+	sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
+
+	opts.imtu = RFCOMM_MAX_L2CAP_MTU;
+	sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
+
+	/* Start listening on the socket */
+	err = sock->ops->listen(sock, 10);
+	if (err) {
+		BT_ERR("Listen failed %d", err);
+		goto failed;
+	}
+
+	/* Add listening session */
+	s = rfcomm_session_add(sock, BT_LISTEN);
+	if (!s)
+		goto failed;
+
+	rfcomm_session_hold(s);
+	return 0;
+failed:
+	sock_release(sock);
+	return err;
+}
+
+static void rfcomm_kill_listener(void)
+{
+	struct rfcomm_session *s;
+	struct list_head *p, *n;
+
+	BT_DBG("");
+
+	list_for_each_safe(p, n, &session_list) {
+		s = list_entry(p, struct rfcomm_session, list);
+		rfcomm_session_del(s);
+	}
+}
+
+static int rfcomm_run(void *unused)
+{
+	rfcomm_thread = current;
+
+	atomic_inc(&running);
+
+	daemonize(); reparent_to_init();
+
+	sigfillset(&current->blocked);
+	set_fs(KERNEL_DS);
+
+	sprintf(current->comm, "krfcommd");
+
+	BT_DBG("");
+
+	rfcomm_add_listener(BDADDR_ANY);
+
+	rfcomm_worker();
+
+	rfcomm_kill_listener();
+
+	atomic_dec(&running);
+	return 0;
+}
+
+/* ---- Proc fs support ---- */
+static int rfcomm_dlc_dump(char *buf)
+{
+	struct rfcomm_session *s;
+	struct sock *sk;
+	struct list_head *p, *pp;
+	char *ptr = buf;
+
+	rfcomm_lock();
+
+	list_for_each(p, &session_list) {
+		s = list_entry(p, struct rfcomm_session, list);
+		sk = s->sock->sk;
+
+		list_for_each(pp, &s->dlcs) {
+		struct rfcomm_dlc *d;
+			d = list_entry(pp, struct rfcomm_dlc, list);
+
+			ptr += sprintf(ptr, "dlc %s %s %ld %d %d %d %d\n",
+				batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
+				d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits);
+		}
+	}
+	
+	rfcomm_unlock();
+
+	return ptr - buf;
+}
+
+extern int rfcomm_sock_dump(char *buf);
+
+static int rfcomm_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
+{
+	char *ptr = buf;
+	int len;
+
+	BT_DBG("count %d, offset %ld", count, offset);
+
+	ptr += rfcomm_dlc_dump(ptr);
+	ptr += rfcomm_sock_dump(ptr);
+	len  = ptr - buf;
+
+	if (len <= count + offset)
+		*eof = 1;
+
+	*start = buf + offset;
+	len -= offset;
+
+	if (len > count)
+		len = count;
+	if (len < 0)
+		len = 0;
+
+	return len;
+}
+
+/* ---- Initialization ---- */
+int __init rfcomm_init(void)
+{
+	l2cap_load();
+
+	kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+
+	rfcomm_init_sockets();
+
+#ifdef CONFIG_BLUEZ_RFCOMM_TTY
+	rfcomm_init_ttys();
+#endif
+
+	create_proc_read_entry("bluetooth/rfcomm", 0, 0, rfcomm_read_proc, NULL);
+
+	BT_INFO("BlueZ RFCOMM ver %s", VERSION);
+	BT_INFO("Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>");
+	BT_INFO("Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>");
+	return 0;
+}
+
+void rfcomm_cleanup(void)
+{
+	/* Terminate working thread.
+	 * ie. Set terminate flag and wake it up */
+	atomic_inc(&terminate);
+	rfcomm_schedule(RFCOMM_SCHED_STATE);
+
+	/* Wait until thread is running */
+	while (atomic_read(&running))
+		schedule();
+
+	remove_proc_entry("bluetooth/rfcomm", NULL);
+
+#ifdef CONFIG_BLUEZ_RFCOMM_TTY
+	rfcomm_cleanup_ttys();
+#endif
+
+	rfcomm_cleanup_sockets();
+	return;
+}
+
+module_init(rfcomm_init);
+module_exit(rfcomm_cleanup);
+
+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueZ RFCOMM ver " VERSION);
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/net/bluetooth/rfcomm/crc.c linux-2.4.18-mh15/net/bluetooth/rfcomm/crc.c
--- linux-2.4.18/net/bluetooth/rfcomm/crc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/rfcomm/crc.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,71 @@
+/* 
+   RFCOMM implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+   Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * RFCOMM FCS calculation.
+ *
+ * $Id: crc.c,v 1.2 2002/09/21 09:54:32 holtmann Exp $
+ */
+
+/* reversed, 8-bit, poly=0x07 */
+unsigned char rfcomm_crc_table[256] = { 
+	0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
+	0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
+	0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
+	0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
+
+	0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
+	0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
+	0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
+	0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
+
+	0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
+	0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
+	0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
+	0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
+
+	0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
+	0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
+	0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
+	0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
+
+	0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
+	0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
+	0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
+	0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
+
+	0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
+	0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
+	0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
+	0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
+
+	0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
+	0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
+	0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
+	0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
+
+	0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
+	0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
+	0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
+	0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
+};
diff -urN linux-2.4.18/net/bluetooth/rfcomm/Makefile linux-2.4.18-mh15/net/bluetooth/rfcomm/Makefile
--- linux-2.4.18/net/bluetooth/rfcomm/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/rfcomm/Makefile	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,11 @@
+#
+# Makefile for the Linux Bluetooth RFCOMM layer
+#
+
+O_TARGET := rfcomm.o
+
+obj-y				:= core.o sock.o crc.o
+obj-$(CONFIG_BLUEZ_RFCOMM_TTY)	+= tty.o
+obj-m				+= $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.4.18/net/bluetooth/rfcomm/sock.c linux-2.4.18-mh15/net/bluetooth/rfcomm/sock.c
--- linux-2.4.18/net/bluetooth/rfcomm/sock.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/rfcomm/sock.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,847 @@
+/* 
+   RFCOMM implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+   Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * RFCOMM sockets.
+ *
+ * $Id: sock.c,v 1.30 2002/10/18 20:12:12 maxk Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/rfcomm.h>
+
+#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+static struct proto_ops rfcomm_sock_ops;
+
+static struct bluez_sock_list rfcomm_sk_list = {
+	lock: RW_LOCK_UNLOCKED
+};
+
+static void rfcomm_sock_close(struct sock *sk);
+static void rfcomm_sock_kill(struct sock *sk);
+
+/* ---- DLC callbacks ----
+ *
+ * called under rfcomm_dlc_lock()
+ */
+static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb)
+{
+	struct sock *sk = d->owner;
+	if (!sk)
+		return;
+
+	atomic_add(skb->len, &sk->rmem_alloc);
+	skb_queue_tail(&sk->receive_queue, skb);
+	sk->data_ready(sk, skb->len);
+
+	if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf)
+		rfcomm_dlc_throttle(d);
+}
+
+static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
+{
+	struct sock *sk = d->owner, *parent;
+	if (!sk)
+		return;
+
+	BT_DBG("dlc %p state %ld err %d", d, d->state, err);
+
+	bh_lock_sock(sk);
+
+	if (err)
+		sk->err = err;
+	sk->state = d->state;
+
+	parent = bluez_pi(sk)->parent;
+	if (!parent) {
+		if (d->state == BT_CONNECTED)
+			rfcomm_session_getaddr(d->session, &bluez_pi(sk)->src, NULL);
+		sk->state_change(sk);
+	} else
+		parent->data_ready(parent, 0);
+
+	bh_unlock_sock(sk);
+}
+
+/* ---- Socket functions ---- */
+static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src)
+{
+	struct sock *sk;
+
+	for (sk = rfcomm_sk_list.head; sk; sk = sk->next) {
+		if (rfcomm_pi(sk)->channel == channel && 
+				!bacmp(&bluez_pi(sk)->src, src))
+			break;
+	}
+
+	return sk;
+}
+
+/* Find socket with channel and source bdaddr.
+ * Returns closest match.
+ */
+static struct sock *__rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src)
+{
+	struct sock *sk, *sk1 = NULL;
+
+	for (sk = rfcomm_sk_list.head; sk; sk = sk->next) {
+		if (state && sk->state != state)
+			continue;
+
+		if (rfcomm_pi(sk)->channel == channel) {
+			/* Exact match. */
+			if (!bacmp(&bluez_pi(sk)->src, src))
+				break;
+
+			/* Closest match */
+			if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
+				sk1 = sk;
+		}
+	}
+	return sk ? sk : sk1;
+}
+
+/* Find socket with given address (channel, src).
+ * Returns locked socket */
+static inline struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src)
+{
+	struct sock *s;
+	read_lock(&rfcomm_sk_list.lock);
+	s = __rfcomm_get_sock_by_channel(state, channel, src);
+	if (s) bh_lock_sock(s);
+	read_unlock(&rfcomm_sk_list.lock);
+	return s;
+}
+
+static void rfcomm_sock_destruct(struct sock *sk)
+{
+	struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+
+	BT_DBG("sk %p dlc %p", sk, d);
+
+	skb_queue_purge(&sk->receive_queue);
+	skb_queue_purge(&sk->write_queue);
+
+	rfcomm_dlc_lock(d);
+	rfcomm_pi(sk)->dlc = NULL;
+	
+	/* Detach DLC if it's owned by this socket */
+	if (d->owner == sk)
+		d->owner = NULL;
+	rfcomm_dlc_unlock(d);
+
+	rfcomm_dlc_put(d);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static void rfcomm_sock_cleanup_listen(struct sock *parent)
+{
+	struct sock *sk;
+
+	BT_DBG("parent %p", parent);
+
+	/* Close not yet accepted dlcs */
+	while ((sk = bluez_accept_dequeue(parent, NULL))) {
+		rfcomm_sock_close(sk);
+		rfcomm_sock_kill(sk);
+	}
+
+	parent->state  = BT_CLOSED;
+	parent->zapped = 1;
+}
+
+/* Kill socket (only if zapped and orphan)
+ * Must be called on unlocked socket.
+ */
+static void rfcomm_sock_kill(struct sock *sk)
+{
+	if (!sk->zapped || sk->socket)
+		return;
+
+	BT_DBG("sk %p state %d refcnt %d", sk, sk->state, atomic_read(&sk->refcnt));
+
+	/* Kill poor orphan */
+	bluez_sock_unlink(&rfcomm_sk_list, sk);
+	sk->dead = 1;
+	sock_put(sk);
+}
+
+static void __rfcomm_sock_close(struct sock *sk)
+{
+	struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+
+	BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
+
+	switch (sk->state) {
+	case BT_LISTEN:
+		rfcomm_sock_cleanup_listen(sk);
+		break;
+
+	case BT_CONNECT:
+	case BT_CONNECT2:
+	case BT_CONFIG:
+	case BT_CONNECTED:
+		rfcomm_dlc_close(d, 0);
+
+	default:
+		sk->zapped = 1;
+		break;
+	}
+}
+
+/* Close socket.
+ * Must be called on unlocked socket.
+ */
+static void rfcomm_sock_close(struct sock *sk)
+{
+	lock_sock(sk);
+	__rfcomm_sock_close(sk);
+	release_sock(sk);
+}
+
+static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
+{
+	BT_DBG("sk %p", sk);
+
+	if (parent) 
+		sk->type = parent->type;
+}
+
+static struct sock *rfcomm_sock_alloc(struct socket *sock, int proto, int prio)
+{
+	struct rfcomm_dlc *d;
+	struct sock *sk;
+
+	sk = sk_alloc(PF_BLUETOOTH, prio, 1);
+	if (!sk)
+		return NULL;
+
+	d = rfcomm_dlc_alloc(prio);
+	if (!d) {
+		sk_free(sk);
+		return NULL;
+	}
+	d->data_ready   = rfcomm_sk_data_ready;
+	d->state_change = rfcomm_sk_state_change;
+
+	rfcomm_pi(sk)->dlc = d;
+	d->owner = sk;
+
+	bluez_sock_init(sock, sk);
+
+	sk->zapped   = 0;
+
+	sk->destruct = rfcomm_sock_destruct;
+	sk->sndtimeo = RFCOMM_CONN_TIMEOUT;
+
+	sk->sndbuf   = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
+	sk->rcvbuf   = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
+
+	sk->protocol = proto;
+	sk->state    = BT_OPEN;
+
+	bluez_sock_link(&rfcomm_sk_list, sk);
+
+	BT_DBG("sk %p", sk);
+
+	MOD_INC_USE_COUNT;
+	return sk;
+}
+
+static int rfcomm_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	sock->state = SS_UNCONNECTED;
+
+	if (sock->type != SOCK_STREAM && sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	sock->ops = &rfcomm_sock_ops;
+
+	if (!(sk = rfcomm_sock_alloc(sock, protocol, GFP_KERNEL)))
+		return -ENOMEM;
+
+	rfcomm_sock_init(sk, NULL);
+	return 0;
+}
+
+static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+	struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr));
+
+	if (!addr || addr->sa_family != AF_BLUETOOTH)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_OPEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	write_lock_bh(&rfcomm_sk_list.lock);
+
+	if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) {
+		err = -EADDRINUSE;
+	} else {
+		/* Save source address */
+		bacpy(&bluez_pi(sk)->src, &sa->rc_bdaddr);
+		rfcomm_pi(sk)->channel = sa->rc_channel;
+		sk->state = BT_BOUND;
+	}
+
+	write_unlock_bh(&rfcomm_sk_list.lock);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+{
+	struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
+	struct sock *sk = sock->sk;
+	struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+	int err = 0;
+
+	BT_DBG("sk %p", sk);
+
+	if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_rc))
+		return -EINVAL;
+
+	if (sk->state != BT_OPEN && sk->state != BT_BOUND)
+		return -EBADFD;
+
+	if (sk->type != SOCK_STREAM)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	sk->state = BT_CONNECT;
+	bacpy(&bluez_pi(sk)->dst, &sa->rc_bdaddr);
+	rfcomm_pi(sk)->channel = sa->rc_channel;
+	
+	err = rfcomm_dlc_open(d, &bluez_pi(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
+	if (!err)
+		err = bluez_sock_wait_state(sk, BT_CONNECTED,
+				sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+	release_sock(sk);
+	return err;
+}
+
+int rfcomm_sock_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p backlog %d", sk, backlog);
+
+	lock_sock(sk);
+
+	if (sk->state != BT_BOUND) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	sk->max_ack_backlog = backlog;
+	sk->ack_backlog = 0;
+	sk->state = BT_LISTEN;
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct sock *sk = sock->sk, *nsk;
+	long timeo;
+	int err = 0;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_LISTEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
+
+	BT_DBG("sk %p timeo %ld", sk, timeo);
+
+	/* Wait for an incoming connection. (wake-one). */
+	add_wait_queue_exclusive(sk->sleep, &wait);
+	while (!(nsk = bluez_accept_dequeue(sk, newsock))) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!timeo) {
+			err = -EAGAIN;
+			break;
+		}
+
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+
+		if (sk->state != BT_LISTEN) {
+			err = -EBADFD;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			err = sock_intr_errno(timeo);
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+
+	if (err)
+		goto done;
+
+	newsock->state = SS_CONNECTED;
+
+	BT_DBG("new socket %p", nsk);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
+{
+	struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	sa->rc_family  = AF_BLUETOOTH;
+	sa->rc_channel = rfcomm_pi(sk)->channel;
+	if (peer)
+		bacpy(&sa->rc_bdaddr, &bluez_pi(sk)->dst);
+	else
+		bacpy(&sa->rc_bdaddr, &bluez_pi(sk)->src);
+
+	*len = sizeof(struct sockaddr_rc);
+	return 0;
+}
+
+static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+			       struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+	struct sk_buff *skb;
+	int err, size;
+	int sent = 0;
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	if (sk->shutdown & SEND_SHUTDOWN)
+		return -EPIPE;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	lock_sock(sk);
+
+	while (len) {
+		size = min_t(uint, len, d->mtu);
+		
+		skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
+				msg->msg_flags & MSG_DONTWAIT, &err);
+		if (!skb)
+			break;
+		skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
+
+		err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+		if (err) {
+			kfree_skb(skb);
+			sent = err;
+			break;
+		}
+
+		err = rfcomm_dlc_send(d, skb);
+		if (err < 0) {
+			kfree_skb(skb);
+			break;
+		}
+
+		sent += size;
+		len  -= size;
+	}
+
+	release_sock(sk);
+
+	return sent ? sent : err;
+}
+
+static long rfcomm_sock_data_wait(struct sock *sk, long timeo)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(sk->sleep, &wait);
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (skb_queue_len(&sk->receive_queue) || sk->err || (sk->shutdown & RCV_SHUTDOWN) ||
+				signal_pending(current) || !timeo)
+			break;
+
+		set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+		clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
+	}
+
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+	return timeo;
+}
+
+static int rfcomm_sock_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+			       int flags, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	int target, err = 0, copied = 0;
+	long timeo;
+
+	if (flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	msg->msg_namelen = 0;
+
+	BT_DBG("sk %p size %d", sk, size);
+
+	lock_sock(sk);
+
+	target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
+	timeo  = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
+
+	do {
+		struct sk_buff *skb;
+		int chunk;
+
+		skb = skb_dequeue(&sk->receive_queue);
+		if (!skb) {
+			if (copied >= target)
+				break;
+
+			if ((err = sock_error(sk)) != 0)
+				break;
+			if (sk->shutdown & RCV_SHUTDOWN)
+				break;
+
+			err = -EAGAIN;
+			if (!timeo)
+				break;
+
+			timeo = rfcomm_sock_data_wait(sk, timeo);
+
+			if (signal_pending(current)) {
+				err = sock_intr_errno(timeo);
+				goto out;
+			}
+			continue;
+		}
+
+		chunk = min_t(unsigned int, skb->len, size);
+		if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+			skb_queue_head(&sk->receive_queue, skb);
+			if (!copied)
+				copied = -EFAULT;
+			break;
+		}
+		copied += chunk;
+		size   -= chunk;
+
+		if (!(flags & MSG_PEEK)) {
+			atomic_sub(chunk, &sk->rmem_alloc);
+
+			skb_pull(skb, chunk);
+			if (skb->len) {
+				skb_queue_head(&sk->receive_queue, skb);
+				break;
+			}
+			kfree_skb(skb);
+
+		} else {
+			/* put message back and return */
+			skb_queue_head(&sk->receive_queue, skb);
+			break;
+		}
+	} while (size);
+
+out:
+	if (atomic_read(&sk->rmem_alloc) <= (sk->rcvbuf >> 2))
+		rfcomm_dlc_unthrottle(rfcomm_pi(sk)->dlc);
+
+	release_sock(sk);
+	return copied ? : err;
+}
+
+static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p", sk);
+
+	lock_sock(sk);
+
+	switch (optname) {
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	};
+
+	release_sock(sk);
+	return err;
+}
+
+static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+{
+	struct sock *sk = sock->sk;
+	int len, err = 0; 
+
+	BT_DBG("sk %p", sk);
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	lock_sock(sk);
+
+	switch (optname) {
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	};
+
+	release_sock(sk);
+	return err;
+}
+
+static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+	int err;
+
+	lock_sock(sk);
+
+#ifdef CONFIG_BLUEZ_RFCOMM_TTY
+	err = rfcomm_dev_ioctl(sk, cmd, arg);
+#else
+	err = -EOPNOTSUPP;
+#endif
+
+	release_sock(sk);
+
+	return err;
+}
+
+static int rfcomm_sock_shutdown(struct socket *sock, int how)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (!sk) return 0;
+
+	lock_sock(sk);
+	if (!sk->shutdown) {
+		sk->shutdown = SHUTDOWN_MASK;
+		__rfcomm_sock_close(sk);
+
+		if (sk->linger)
+			err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
+	}
+	release_sock(sk);
+	return err;
+}
+
+static int rfcomm_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	err = rfcomm_sock_shutdown(sock, 2);
+
+	sock_orphan(sk);
+	rfcomm_sock_kill(sk);
+	return err;
+}
+
+/* ---- RFCOMM core layer callbacks ---- 
+ *
+ * called under rfcomm_lock()
+ */
+int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d)
+{
+	struct sock *sk, *parent;
+	bdaddr_t src, dst;
+	int result = 0;
+
+	BT_DBG("session %p channel %d", s, channel);
+
+	rfcomm_session_getaddr(s, &src, &dst);
+
+	/* Check if we have socket listening on this channel */
+	parent = rfcomm_get_sock_by_channel(BT_LISTEN, channel, &src);
+	if (!parent)
+		return 0;
+
+	/* Check for backlog size */
+	if (parent->ack_backlog > parent->max_ack_backlog) {
+		BT_DBG("backlog full %d", parent->ack_backlog); 
+		goto done;
+	}
+
+	sk = rfcomm_sock_alloc(NULL, BTPROTO_RFCOMM, GFP_ATOMIC);
+	if (!sk)
+		goto done;
+
+	rfcomm_sock_init(sk, parent);
+	bacpy(&bluez_pi(sk)->src, &src);
+	bacpy(&bluez_pi(sk)->dst, &dst);
+	rfcomm_pi(sk)->channel = channel;
+
+	sk->state = BT_CONFIG;
+	bluez_accept_enqueue(parent, sk);
+
+	/* Accept connection and return socket DLC */
+	*d = rfcomm_pi(sk)->dlc;
+	result = 1;
+
+done:
+	bh_unlock_sock(parent);
+	return result;
+}
+
+/* ---- Proc fs support ---- */
+int rfcomm_sock_dump(char *buf)
+{
+	struct bluez_sock_list *list = &rfcomm_sk_list;
+	struct rfcomm_pinfo *pi;
+	struct sock *sk;
+	char *ptr = buf;
+
+	write_lock_bh(&list->lock);
+
+	for (sk = list->head; sk; sk = sk->next) {
+		pi = rfcomm_pi(sk);
+		ptr += sprintf(ptr, "sk  %s %s %d %d\n",
+				batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
+				sk->state, rfcomm_pi(sk)->channel);
+	}
+
+	write_unlock_bh(&list->lock);
+
+	return ptr - buf;
+}
+
+static struct proto_ops rfcomm_sock_ops = {
+	family:		PF_BLUETOOTH,
+	release:	rfcomm_sock_release,
+	bind:		rfcomm_sock_bind,
+	connect:	rfcomm_sock_connect,
+	listen:		rfcomm_sock_listen,
+	accept:		rfcomm_sock_accept,
+	getname:	rfcomm_sock_getname,
+	sendmsg:	rfcomm_sock_sendmsg,
+	recvmsg:	rfcomm_sock_recvmsg,
+	shutdown:	rfcomm_sock_shutdown,
+	setsockopt:	rfcomm_sock_setsockopt,
+	getsockopt:	rfcomm_sock_getsockopt,
+	ioctl:		rfcomm_sock_ioctl,
+	poll:		bluez_sock_poll,
+	socketpair:	sock_no_socketpair,
+	mmap:		sock_no_mmap
+};
+
+static struct net_proto_family rfcomm_sock_family_ops = {
+	family:		PF_BLUETOOTH,
+	create:		rfcomm_sock_create
+};
+
+int rfcomm_init_sockets(void)
+{
+	int err;
+
+	if ((err = bluez_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops))) {
+		BT_ERR("Can't register RFCOMM socket layer");
+		return err;
+	}
+
+	return 0;
+}
+
+void rfcomm_cleanup_sockets(void)
+{
+	int err;
+
+	/* Unregister socket, protocol and notifier */
+	if ((err = bluez_sock_unregister(BTPROTO_RFCOMM)))
+		BT_ERR("Can't unregister RFCOMM socket layer %d", err);
+}
diff -urN linux-2.4.18/net/bluetooth/rfcomm/tty.c linux-2.4.18-mh15/net/bluetooth/rfcomm/tty.c
--- linux-2.4.18/net/bluetooth/rfcomm/tty.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/rfcomm/tty.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,960 @@
+/* 
+   RFCOMM implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+   Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * RFCOMM TTY.
+ *
+ * $Id: tty.c,v 1.26 2002/10/18 20:12:12 maxk Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/rfcomm.h>
+
+#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define RFCOMM_TTY_MAGIC 0x6d02		/* magic number for rfcomm struct */
+#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV	/* whole lotta rfcomm devices */
+#define RFCOMM_TTY_MAJOR 216		/* device node major id of the usb/bluetooth.c driver */
+#define RFCOMM_TTY_MINOR 0
+
+struct rfcomm_dev {
+	struct list_head	list;
+	atomic_t		refcnt;
+
+	char			name[12];
+	int			id;
+	unsigned long		flags;
+	int			opened;
+	int			err;
+
+	bdaddr_t		src;
+	bdaddr_t		dst;
+	u8 			channel;
+
+	uint 			modem_status;
+
+	struct rfcomm_dlc	*dlc;
+	struct tty_struct	*tty;
+	wait_queue_head_t       wait;
+	struct tasklet_struct   wakeup_task;
+
+	atomic_t 		wmem_alloc;
+};
+
+static LIST_HEAD(rfcomm_dev_list);
+static rwlock_t rfcomm_dev_lock = RW_LOCK_UNLOCKED;
+
+static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
+static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
+static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);
+
+static void rfcomm_tty_wakeup(unsigned long arg);
+
+/* ---- Device functions ---- */
+static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
+{
+	struct rfcomm_dlc *dlc = dev->dlc;
+
+	BT_DBG("dev %p dlc %p", dev, dlc);
+
+	rfcomm_dlc_lock(dlc);
+	/* Detach DLC if it's owned by this dev */
+	if (dlc->owner == dev)
+		dlc->owner = NULL;
+	rfcomm_dlc_unlock(dlc);
+
+	rfcomm_dlc_put(dlc);
+	kfree(dev);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static inline void rfcomm_dev_hold(struct rfcomm_dev *dev)
+{
+	atomic_inc(&dev->refcnt);
+}
+
+static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
+{
+	/* The reason this isn't actually a race, as you no
+	   doubt have a little voice screaming at you in your
+	   head, is that the refcount should never actually
+	   reach zero unless the device has already been taken
+	   off the list, in rfcomm_dev_del(). And if that's not
+	   true, we'll hit the BUG() in rfcomm_dev_destruct()
+	   anyway. */
+	if (atomic_dec_and_test(&dev->refcnt))
+		rfcomm_dev_destruct(dev);
+}
+
+static struct rfcomm_dev *__rfcomm_dev_get(int id)
+{
+	struct rfcomm_dev *dev;
+	struct list_head  *p;
+
+	list_for_each(p, &rfcomm_dev_list) {
+		dev = list_entry(p, struct rfcomm_dev, list);
+		if (dev->id == id)
+			return dev;
+	}
+
+	return NULL;
+}
+
+static inline struct rfcomm_dev *rfcomm_dev_get(int id)
+{
+	struct rfcomm_dev *dev;
+
+	read_lock(&rfcomm_dev_lock);
+
+	dev = __rfcomm_dev_get(id);
+	if (dev)
+		rfcomm_dev_hold(dev);
+
+	read_unlock(&rfcomm_dev_lock);
+
+	return dev;
+}
+
+static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
+{
+	struct rfcomm_dev *dev;
+	struct list_head *head = &rfcomm_dev_list, *p;
+	int err = 0;
+
+	BT_DBG("id %d channel %d", req->dev_id, req->channel);
+	
+	dev = kmalloc(sizeof(struct rfcomm_dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	memset(dev, 0, sizeof(struct rfcomm_dev));
+
+	write_lock_bh(&rfcomm_dev_lock);
+
+	if (req->dev_id < 0) {
+		dev->id = 0;
+
+		list_for_each(p, &rfcomm_dev_list) {
+			if (list_entry(p, struct rfcomm_dev, list)->id != dev->id)
+				break;
+
+			dev->id++;
+			head = p;
+		}
+	} else {
+		dev->id = req->dev_id;
+
+		list_for_each(p, &rfcomm_dev_list) {
+			struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list);
+
+			if (entry->id == dev->id) {
+				err = -EADDRINUSE;
+				goto out;
+			}
+
+			if (entry->id > dev->id - 1)
+				break;
+
+			head = p;
+		}
+	}
+
+	if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) {
+		err = -ENFILE;
+		goto out;
+	}
+
+	sprintf(dev->name, "rfcomm%d", dev->id);
+
+	list_add(&dev->list, head);
+	atomic_set(&dev->refcnt, 1);
+
+	bacpy(&dev->src, &req->src);
+	bacpy(&dev->dst, &req->dst);
+	dev->channel = req->channel;
+
+	dev->flags = req->flags & 
+		((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC));
+
+	init_waitqueue_head(&dev->wait);
+	tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
+
+	rfcomm_dlc_lock(dlc);
+	dlc->data_ready   = rfcomm_dev_data_ready;
+	dlc->state_change = rfcomm_dev_state_change;
+	dlc->modem_status = rfcomm_dev_modem_status;
+
+	dlc->owner = dev;
+	dev->dlc   = dlc;
+	rfcomm_dlc_unlock(dlc);
+
+	MOD_INC_USE_COUNT;
+	
+out:
+	write_unlock_bh(&rfcomm_dev_lock);
+
+	if (err) {
+		kfree(dev);
+		return err;
+	} else
+		return dev->id;
+}
+
+static void rfcomm_dev_del(struct rfcomm_dev *dev)
+{
+	BT_DBG("dev %p", dev);
+
+	write_lock_bh(&rfcomm_dev_lock);
+	list_del_init(&dev->list);
+	write_unlock_bh(&rfcomm_dev_lock);
+
+	rfcomm_dev_put(dev);
+}
+
+/* ---- Send buffer ---- */
+
+static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
+{
+	/* We can't let it be zero, because we don't get a callback 
+	   when tx_credits becomes nonzero, hence we'd never wake up */
+	return dlc->mtu * (dlc->tx_credits?:1);
+}
+
+static void rfcomm_wfree(struct sk_buff *skb)
+{
+	struct rfcomm_dev *dev = (void *) skb->sk;
+	atomic_sub(skb->truesize, &dev->wmem_alloc);
+	if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
+		tasklet_schedule(&dev->wakeup_task);
+	rfcomm_dev_put(dev);
+}
+
+static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev)
+{
+	rfcomm_dev_hold(dev);
+	atomic_add(skb->truesize, &dev->wmem_alloc);
+	skb->sk = (void *) dev;
+	skb->destructor = rfcomm_wfree;
+}
+
+static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority)
+{
+	if (force || atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
+		struct sk_buff *skb = alloc_skb(size, priority);
+		if (skb) {
+			rfcomm_set_owner_w(skb, dev);
+			return skb;
+		}
+	}
+	return NULL;
+}
+
+/* ---- Device IOCTLs ---- */
+
+#define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP))
+
+static int rfcomm_create_dev(struct sock *sk, unsigned long arg)
+{
+	struct rfcomm_dev_req req;
+	struct rfcomm_dlc *dlc;
+	int id;
+
+	if (copy_from_user(&req, (void *) arg, sizeof(req)))
+		return -EFAULT;
+
+	BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags);
+
+	if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
+		/* Socket must be connected */
+		if (sk->state != BT_CONNECTED)
+			return -EBADFD;
+
+		dlc = rfcomm_pi(sk)->dlc;
+		rfcomm_dlc_hold(dlc);
+	} else {
+		dlc = rfcomm_dlc_alloc(GFP_KERNEL);
+		if (!dlc)
+			return -ENOMEM;
+	}
+
+	id = rfcomm_dev_add(&req, dlc);
+	if (id < 0) {
+		rfcomm_dlc_put(dlc);
+		return id;
+	}
+
+	if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
+		/* DLC is now used by device.
+		 * Socket must be disconnected */
+		sk->state = BT_CLOSED;
+	}
+
+	return id;
+}
+
+static int rfcomm_release_dev(unsigned long arg)
+{
+	struct rfcomm_dev_req req;
+	struct rfcomm_dev *dev;
+
+	if (copy_from_user(&req, (void *) arg, sizeof(req)))
+		return -EFAULT;
+
+	BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags);
+
+	if (!(dev = rfcomm_dev_get(req.dev_id)))
+		return -ENODEV;
+
+	if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) {
+		rfcomm_dev_put(dev);
+		return -EPERM;
+	}
+
+	if (req.flags & (1 << RFCOMM_HANGUP_NOW))
+		rfcomm_dlc_close(dev->dlc, 0);
+
+	rfcomm_dev_del(dev);
+	rfcomm_dev_put(dev);
+	return 0;
+}
+
+static int rfcomm_get_dev_list(unsigned long arg)
+{
+	struct rfcomm_dev_list_req *dl;
+	struct rfcomm_dev_info *di;
+	struct list_head *p;
+	int n = 0, size, err;
+	u16 dev_num;
+
+	BT_DBG("");
+
+	if (get_user(dev_num, (u16 *) arg))
+		return -EFAULT;
+
+	if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di))
+		return -EINVAL;
+
+	size = sizeof(*dl) + dev_num * sizeof(*di);
+
+	if (!(dl = kmalloc(size, GFP_KERNEL)))
+		return -ENOMEM;
+
+	di = dl->dev_info;
+
+	read_lock_bh(&rfcomm_dev_lock);
+
+	list_for_each(p, &rfcomm_dev_list) {
+		struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
+		(di + n)->id      = dev->id;
+		(di + n)->flags   = dev->flags;
+		(di + n)->state   = dev->dlc->state;
+		(di + n)->channel = dev->channel;
+		bacpy(&(di + n)->src, &dev->src);
+		bacpy(&(di + n)->dst, &dev->dst);
+		if (++n >= dev_num)
+			break;
+	}
+
+	read_unlock_bh(&rfcomm_dev_lock);
+
+	dl->dev_num = n;
+	size = sizeof(*dl) + n * sizeof(*di);
+
+	err = copy_to_user((void *) arg, dl, size);
+	kfree(dl);
+
+	return err ? -EFAULT : 0;
+}
+
+static int rfcomm_get_dev_info(unsigned long arg)
+{
+	struct rfcomm_dev *dev;
+	struct rfcomm_dev_info di;
+	int err = 0;
+
+	BT_DBG("");
+
+	if (copy_from_user(&di, (void *)arg, sizeof(di)))
+		return -EFAULT;
+
+	if (!(dev = rfcomm_dev_get(di.id)))
+		return -ENODEV;
+
+	di.flags   = dev->flags;
+	di.channel = dev->channel;
+	di.state   = dev->dlc->state;
+	bacpy(&di.src, &dev->src);
+	bacpy(&di.dst, &dev->dst);
+
+	if (copy_to_user((void *)arg, &di, sizeof(di)))
+		err = -EFAULT;
+
+	rfcomm_dev_put(dev);
+	return err;
+}
+
+int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
+{
+	BT_DBG("cmd %d arg %ld", cmd, arg);
+
+	switch (cmd) {
+	case RFCOMMCREATEDEV:
+		return rfcomm_create_dev(sk, arg);
+
+	case RFCOMMRELEASEDEV:
+		return rfcomm_release_dev(arg);
+
+	case RFCOMMGETDEVLIST:
+		return rfcomm_get_dev_list(arg);
+
+	case RFCOMMGETDEVINFO:
+		return rfcomm_get_dev_info(arg);
+	}
+
+	return -EINVAL;
+}
+
+/* ---- DLC callbacks ---- */
+static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
+{
+	struct rfcomm_dev *dev = dlc->owner;
+	struct tty_struct *tty;
+       
+	if (!dev || !(tty = dev->tty)) {
+		kfree_skb(skb);
+		return;
+	}
+
+	BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len);
+
+	if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
+		register int i;
+		for (i = 0; i < skb->len; i++) {
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+				tty_flip_buffer_push(tty);
+
+			tty_insert_flip_char(tty, skb->data[i], 0);
+		}
+		tty_flip_buffer_push(tty);
+	} else
+		tty->ldisc.receive_buf(tty, skb->data, NULL, skb->len);
+
+	kfree_skb(skb);
+}
+
+static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
+{
+	struct rfcomm_dev *dev = dlc->owner;
+	if (!dev)
+		return;
+	
+	BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
+
+	dev->err = err;
+	wake_up_interruptible(&dev->wait);
+
+	if (dlc->state == BT_CLOSED) {
+		if (!dev->tty) {
+			if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
+				rfcomm_dev_hold(dev);
+				rfcomm_dev_del(dev);
+
+				/* We have to drop DLC lock here, otherwise
+				   rfcomm_dev_put() will dead lock if it's
+				   the last reference. */
+				rfcomm_dlc_unlock(dlc);
+				rfcomm_dev_put(dev);
+				rfcomm_dlc_lock(dlc);
+			}
+		} else 
+			tty_hangup(dev->tty);
+	}
+}
+
+static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
+{
+	struct rfcomm_dev *dev = dlc->owner;
+	if (!dev)
+		return;
+	
+	BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig);
+
+	dev->modem_status = 
+		((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) |
+		((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) |
+		((v24_sig & RFCOMM_V24_IC)  ? TIOCM_RI : 0) |
+		((v24_sig & RFCOMM_V24_DV)  ? TIOCM_CD : 0);
+}
+
+/* ---- TTY functions ---- */
+static void rfcomm_tty_wakeup(unsigned long arg)
+{
+	struct rfcomm_dev *dev = (void *) arg;
+	struct tty_struct *tty = dev->tty;
+	if (!tty)
+		return;
+
+	BT_DBG("dev %p tty %p", dev, tty);
+
+	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
+                (tty->ldisc.write_wakeup)(tty);
+
+	wake_up_interruptible(&tty->write_wait);
+#ifdef SERIAL_HAVE_POLL_WAIT
+	wake_up_interruptible(&tty->poll_wait);
+#endif
+}
+
+static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct rfcomm_dev *dev;
+	struct rfcomm_dlc *dlc;
+	int err, id;
+
+	id = MINOR(tty->device) - tty->driver.minor_start;
+
+	BT_DBG("tty %p id %d", tty, id);
+
+	/* We don't leak this refcount. For reasons which are not entirely
+	   clear, the TTY layer will call our ->close() method even if the
+	   open fails. We decrease the refcount there, and decreasing it
+	   here too would cause breakage. */
+	dev = rfcomm_dev_get(id);
+	if (!dev)
+		return -ENODEV;
+
+	BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened);
+
+	if (dev->opened++ != 0)
+		return 0;
+
+	dlc = dev->dlc;
+
+	/* Attach TTY and open DLC */
+
+	rfcomm_dlc_lock(dlc);
+	tty->driver_data = dev;
+	dev->tty = tty;
+	rfcomm_dlc_unlock(dlc);
+	set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
+
+	err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel);
+	if (err < 0)
+		return err;
+
+	/* Wait for DLC to connect */
+	add_wait_queue(&dev->wait, &wait);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (dlc->state == BT_CLOSED) {
+			err = -dev->err;
+			break;
+		}
+
+		if (dlc->state == BT_CONNECTED)
+			break;
+
+		if (signal_pending(current)) {
+			err = -EINTR;
+			break;
+		}
+
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&dev->wait, &wait);
+
+	return err;
+}
+
+static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	if (!dev)
+		return;
+
+	BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened);
+
+	if (--dev->opened == 0) {
+		/* Close DLC and dettach TTY */
+		rfcomm_dlc_close(dev->dlc, 0);
+
+		clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
+		tasklet_kill(&dev->wakeup_task);
+
+		rfcomm_dlc_lock(dev->dlc);
+		tty->driver_data = NULL;
+		dev->tty = NULL;
+		rfcomm_dlc_unlock(dev->dlc);
+	}
+
+	rfcomm_dev_put(dev);
+}
+
+static int rfcomm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	struct rfcomm_dlc *dlc = dev->dlc;
+	struct sk_buff *skb;
+	int err = 0, sent = 0, size;
+
+	BT_DBG("tty %p from_user %d count %d", tty, from_user, count);
+
+	while (count) {
+		size = min_t(uint, count, dlc->mtu);
+
+		if (from_user)
+			skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL);
+		else
+			skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_ATOMIC);
+		
+		if (!skb)
+			break;
+
+		skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
+
+		if (from_user)
+			copy_from_user(skb_put(skb, size), buf + sent, size);
+		else
+			memcpy(skb_put(skb, size), buf + sent, size);
+
+		if ((err = rfcomm_dlc_send(dlc, skb)) < 0) {
+			kfree_skb(skb);
+			break;
+		}
+
+		sent  += size;
+		count -= size;
+	}
+
+	return sent ? sent : err;
+}
+
+static void rfcomm_tty_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	struct rfcomm_dlc *dlc = dev->dlc;
+	struct sk_buff *skb;
+
+	BT_DBG("tty %p char %x", tty, ch);
+
+	skb = rfcomm_wmalloc(dev, 1 + RFCOMM_SKB_RESERVE, 1, GFP_ATOMIC);
+
+	if (!skb)
+		return;
+
+	skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
+
+	*(char *)skb_put(skb, 1) = ch;
+
+	if ((rfcomm_dlc_send(dlc, skb)) < 0)
+		kfree_skb(skb);	
+}
+
+static int rfcomm_tty_write_room(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	int room;
+	
+	BT_DBG("tty %p", tty);
+
+	room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc);
+	if (room < 0)
+		room = 0;
+
+	return room;
+}
+
+static int rfcomm_tty_set_modem_status(uint cmd, struct rfcomm_dlc *dlc, uint status)
+{
+	u8 v24_sig, mask;
+
+	BT_DBG("dlc %p cmd 0x%02x", dlc, cmd);
+
+	if (cmd == TIOCMSET)
+		v24_sig = 0;
+	else
+		rfcomm_dlc_get_modem_status(dlc, &v24_sig);
+
+	mask =  ((status & TIOCM_DSR) ? RFCOMM_V24_RTC : 0) |
+		((status & TIOCM_DTR) ? RFCOMM_V24_RTC : 0) |
+		((status & TIOCM_RTS) ? RFCOMM_V24_RTR : 0) |
+		((status & TIOCM_CTS) ? RFCOMM_V24_RTR : 0) |
+		((status & TIOCM_RI)  ? RFCOMM_V24_IC  : 0) |
+		((status & TIOCM_CD)  ? RFCOMM_V24_DV  : 0);
+
+	if (cmd == TIOCMBIC)
+		v24_sig &= ~mask;
+	else
+		v24_sig |= mask;
+
+	rfcomm_dlc_set_modem_status(dlc, v24_sig);
+	return 0;
+}
+
+static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	struct rfcomm_dlc *dlc = dev->dlc;
+	uint status;
+	int err;
+
+	BT_DBG("tty %p cmd 0x%02x", tty, cmd);
+
+	switch (cmd) {
+	case TCGETS:
+		BT_DBG("TCGETS is not supported");
+		return -ENOIOCTLCMD;
+
+	case TCSETS:
+		BT_DBG("TCSETS is not supported");
+		return -ENOIOCTLCMD;
+
+	case TIOCMGET:
+		BT_DBG("TIOCMGET");
+
+		return put_user(dev->modem_status, (unsigned int *)arg);
+
+	case TIOCMSET: /* Turns on and off the lines as specified by the mask */
+	case TIOCMBIS: /* Turns on the lines as specified by the mask */
+	case TIOCMBIC: /* Turns off the lines as specified by the mask */
+		if ((err = get_user(status, (unsigned int *)arg)))
+			return err;
+		return rfcomm_tty_set_modem_status(cmd, dlc, status);
+
+	case TIOCMIWAIT:
+		BT_DBG("TIOCMIWAIT");
+		break;
+
+	case TIOCGICOUNT:
+		BT_DBG("TIOCGICOUNT");
+		break;
+
+	case TIOCGSERIAL:
+		BT_ERR("TIOCGSERIAL is not supported");
+		return -ENOIOCTLCMD;
+
+	case TIOCSSERIAL:
+		BT_ERR("TIOCSSERIAL is not supported");
+		return -ENOIOCTLCMD;
+
+	case TIOCSERGSTRUCT:
+		BT_ERR("TIOCSERGSTRUCT is not supported");
+		return -ENOIOCTLCMD;
+
+	case TIOCSERGETLSR:
+		BT_ERR("TIOCSERGETLSR is not supported");
+		return -ENOIOCTLCMD;
+
+	case TIOCSERCONFIG:
+		BT_ERR("TIOCSERCONFIG is not supported");
+		return -ENOIOCTLCMD;
+
+	default:
+		return -ENOIOCTLCMD;	/* ioctls which we must ignore */
+
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old)
+{
+	BT_DBG("tty %p", tty);
+
+	if ((tty->termios->c_cflag == old->c_cflag) &&
+		(RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag)))
+		return;
+
+	/* handle turning off CRTSCTS */
+	if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) {
+		BT_DBG("turning off CRTSCTS");
+	}
+}
+
+static void rfcomm_tty_throttle(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+
+	BT_DBG("tty %p dev %p", tty, dev);
+	
+	rfcomm_dlc_throttle(dev->dlc);
+}
+
+static void rfcomm_tty_unthrottle(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+
+	BT_DBG("tty %p dev %p", tty, dev);
+	
+	rfcomm_dlc_unthrottle(dev->dlc);
+}
+
+static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	struct rfcomm_dlc *dlc = dev->dlc;
+
+	BT_DBG("tty %p dev %p", tty, dev);
+
+	if (skb_queue_len(&dlc->tx_queue))
+		return dlc->mtu;
+
+	return 0;
+}
+
+static void rfcomm_tty_flush_buffer(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	if (!dev)
+		return;
+
+	BT_DBG("tty %p dev %p", tty, dev);
+
+	skb_queue_purge(&dev->dlc->tx_queue);
+
+	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
+		tty->ldisc.write_wakeup(tty);
+}
+
+static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch)
+{
+	BT_DBG("tty %p ch %c", tty, ch);
+}
+
+static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	BT_DBG("tty %p timeout %d", tty, timeout);
+}
+
+static void rfcomm_tty_hangup(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	if (!dev)
+		return;
+
+	BT_DBG("tty %p dev %p", tty, dev);
+
+	rfcomm_tty_flush_buffer(tty);
+
+	if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
+		rfcomm_dev_del(dev);
+}
+
+static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused)
+{
+	return 0;
+}
+
+/* ---- TTY structure ---- */
+static int    rfcomm_tty_refcount;       /* If we manage several devices */
+
+static struct tty_struct *rfcomm_tty_table[RFCOMM_TTY_PORTS];
+static struct termios *rfcomm_tty_termios[RFCOMM_TTY_PORTS];
+static struct termios *rfcomm_tty_termios_locked[RFCOMM_TTY_PORTS];
+
+static struct tty_driver rfcomm_tty_driver = {
+	magic:			TTY_DRIVER_MAGIC,
+	driver_name:		"rfcomm",
+#ifdef CONFIG_DEVFS_FS
+	name:			"bluetooth/rfcomm/%d",
+#else
+	name:			"rfcomm",
+#endif
+	major:			RFCOMM_TTY_MAJOR,
+	minor_start:		RFCOMM_TTY_MINOR,
+	num:			RFCOMM_TTY_PORTS,
+	type:			TTY_DRIVER_TYPE_SERIAL,
+	subtype:		SERIAL_TYPE_NORMAL,
+	flags:			TTY_DRIVER_REAL_RAW,
+
+	refcount:		&rfcomm_tty_refcount,
+	table:			rfcomm_tty_table,
+	termios:		rfcomm_tty_termios,
+	termios_locked:		rfcomm_tty_termios_locked,
+
+	open:			rfcomm_tty_open,
+	close:			rfcomm_tty_close,
+	put_char:		rfcomm_tty_put_char,
+	write:			rfcomm_tty_write,
+	write_room:		rfcomm_tty_write_room,
+	chars_in_buffer:	rfcomm_tty_chars_in_buffer,
+	flush_buffer:		rfcomm_tty_flush_buffer,
+	ioctl:			rfcomm_tty_ioctl,
+	throttle:		rfcomm_tty_throttle,
+	unthrottle:		rfcomm_tty_unthrottle,
+	set_termios:		rfcomm_tty_set_termios,
+	send_xchar:		rfcomm_tty_send_xchar,
+	stop:			NULL,
+	start:			NULL,
+	hangup:			rfcomm_tty_hangup,
+	wait_until_sent:	rfcomm_tty_wait_until_sent,
+	read_proc:		rfcomm_tty_read_proc,
+};
+
+int rfcomm_init_ttys(void)
+{
+	int i;
+
+	/* Initalize our global data */
+	for (i = 0; i < RFCOMM_TTY_PORTS; i++)
+		rfcomm_tty_table[i] = NULL;
+
+	/* Register the TTY driver */
+	rfcomm_tty_driver.init_termios = tty_std_termios;
+	rfcomm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	rfcomm_tty_driver.flags = TTY_DRIVER_REAL_RAW;
+
+	if (tty_register_driver(&rfcomm_tty_driver)) {
+		BT_ERR("Can't register RFCOMM TTY driver");
+		return -1;
+	}
+
+	return 0;
+}
+
+void rfcomm_cleanup_ttys(void)
+{
+	tty_unregister_driver(&rfcomm_tty_driver);
+	return;
+}
diff -urN linux-2.4.18/net/bluetooth/sco.c linux-2.4.18-mh15/net/bluetooth/sco.c
--- linux-2.4.18/net/bluetooth/sco.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.18-mh15/net/bluetooth/sco.c	2004-08-01 16:26:23.000000000 +0200
@@ -0,0 +1,1019 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * BlueZ SCO sockets.
+ *
+ * $Id: sco.c,v 1.4 2002/07/22 20:32:54 maxk Exp $
+ */
+#define VERSION "0.3"
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/sco.h>
+
+#ifndef SCO_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+static struct proto_ops sco_sock_ops;
+
+static struct bluez_sock_list sco_sk_list = {
+	lock: RW_LOCK_UNLOCKED
+};
+
+static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
+static void sco_chan_del(struct sock *sk, int err);
+static inline struct sock * sco_chan_get(struct sco_conn *conn);
+
+static int  sco_conn_del(struct hci_conn *conn, int err);
+
+static void sco_sock_close(struct sock *sk);
+static void sco_sock_kill(struct sock *sk);
+
+/* ----- SCO timers ------ */
+static void sco_sock_timeout(unsigned long arg)
+{
+	struct sock *sk = (struct sock *) arg;
+
+	BT_DBG("sock %p state %d", sk, sk->state);
+
+	bh_lock_sock(sk);
+	sk->err = ETIMEDOUT;
+	sk->state_change(sk);
+	bh_unlock_sock(sk);
+
+	sco_sock_kill(sk);
+	sock_put(sk);
+}
+
+static void sco_sock_set_timer(struct sock *sk, long timeout)
+{
+	BT_DBG("sock %p state %d timeout %ld", sk, sk->state, timeout);
+
+	if (!mod_timer(&sk->timer, jiffies + timeout))
+		sock_hold(sk);
+}
+
+static void sco_sock_clear_timer(struct sock *sk)
+{
+	BT_DBG("sock %p state %d", sk, sk->state);
+
+	if (timer_pending(&sk->timer) && del_timer(&sk->timer))
+		__sock_put(sk);
+}
+
+static void sco_sock_init_timer(struct sock *sk)
+{
+	init_timer(&sk->timer);
+	sk->timer.function = sco_sock_timeout;
+	sk->timer.data = (unsigned long)sk;
+}
+
+/* -------- SCO connections --------- */
+static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
+{
+	struct hci_dev *hdev = hcon->hdev;
+	struct sco_conn *conn;
+
+	if ((conn = hcon->sco_data))
+		return conn;
+
+	if (status)
+		return conn;
+
+	if (!(conn = kmalloc(sizeof(struct sco_conn), GFP_ATOMIC)))
+		return NULL;
+	memset(conn, 0, sizeof(struct sco_conn));
+
+	spin_lock_init(&conn->lock);
+
+	hcon->sco_data = conn;
+	conn->hcon = hcon;
+
+	conn->src = &hdev->bdaddr;
+	conn->dst = &hcon->dst;
+	
+	if (hdev->sco_mtu > 0)
+		conn->mtu = hdev->sco_mtu;
+	else
+		conn->mtu = 60;
+
+	BT_DBG("hcon %p conn %p", hcon, conn);
+
+	MOD_INC_USE_COUNT;
+	return conn;
+}
+
+static int sco_conn_del(struct hci_conn *hcon, int err)
+{
+	struct sco_conn *conn;
+	struct sock *sk;
+
+	if (!(conn = hcon->sco_data)) 
+		return 0;
+
+	BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
+
+	/* Kill socket */
+	if ((sk = sco_chan_get(conn))) {
+		bh_lock_sock(sk);
+		sco_sock_clear_timer(sk);
+		sco_chan_del(sk, err);
+		bh_unlock_sock(sk);
+		sco_sock_kill(sk);
+	}
+
+	hcon->sco_data = NULL;
+	kfree(conn);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int sco_connect(struct sock *sk)
+{
+	bdaddr_t *src = &bluez_pi(sk)->src;
+	bdaddr_t *dst = &bluez_pi(sk)->dst;
+	struct sco_conn *conn;
+	struct hci_conn *hcon;
+	struct hci_dev  *hdev;
+	int err = 0;
+
+	BT_DBG("%s -> %s", batostr(src), batostr(dst));
+
+	if (!(hdev = hci_get_route(dst, src)))
+		return -EHOSTUNREACH;
+
+	hci_dev_lock_bh(hdev);
+
+	err = -ENOMEM;
+
+	hcon = hci_connect(hdev, SCO_LINK, dst);
+	if (!hcon)
+		goto done;
+
+	conn = sco_conn_add(hcon, 0);
+	if (!conn) {
+		hci_conn_put(hcon);
+		goto done;
+	}
+
+	/* Update source addr of the socket */
+	bacpy(src, conn->src);
+
+	err = sco_chan_add(conn, sk, NULL);
+	if (err)
+		goto done;
+
+	if (hcon->state == BT_CONNECTED) {
+		sco_sock_clear_timer(sk);
+		sk->state = BT_CONNECTED;
+	} else {
+		sk->state = BT_CONNECT;
+		sco_sock_set_timer(sk, sk->sndtimeo);
+	}
+done:
+	hci_dev_unlock_bh(hdev);
+	hci_dev_put(hdev);
+	return err;
+}
+
+static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
+{
+	struct sco_conn *conn = sco_pi(sk)->conn;
+	struct sk_buff *skb;
+	int err, count;
+
+	/* Check outgoing MTU */
+	if (len > conn->mtu)
+		return -EINVAL;
+
+	BT_DBG("sk %p len %d", sk, len);
+
+	count = MIN(conn->mtu, len);
+	if (!(skb = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err)))
+		return err;
+
+	if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
+		err = -EFAULT;
+		goto fail;
+	}
+
+	if ((err = hci_send_sco(conn->hcon, skb)) < 0)
+		goto fail;
+
+	return count;
+
+fail:
+	kfree_skb(skb);
+	return err;
+}
+
+static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
+{
+	struct sock *sk = sco_chan_get(conn);
+
+	if (!sk)
+		goto drop;
+
+	BT_DBG("sk %p len %d", sk, skb->len);
+
+	if (sk->state != BT_CONNECTED)
+		goto drop;
+
+	if (!sock_queue_rcv_skb(sk, skb))
+		return;
+
+drop:
+	kfree_skb(skb);
+	return;
+}
+
+/* -------- Socket interface ---------- */
+static struct sock *__sco_get_sock_by_addr(bdaddr_t *ba)
+{
+	struct sock *sk;
+
+	for (sk = sco_sk_list.head; sk; sk = sk->next) {
+		if (!bacmp(&bluez_pi(sk)->src, ba))
+			break;
+	}
+
+	return sk;
+}
+
+/* Find socket listening on source bdaddr.
+ * Returns closest match.
+ */
+static struct sock *sco_get_sock_listen(bdaddr_t *src)
+{
+	struct sock *sk, *sk1 = NULL;
+
+	read_lock(&sco_sk_list.lock);
+
+	for (sk = sco_sk_list.head; sk; sk = sk->next) {
+		if (sk->state != BT_LISTEN)
+			continue;
+
+		/* Exact match. */
+		if (!bacmp(&bluez_pi(sk)->src, src))
+			break;
+
+		/* Closest match */
+		if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY))
+			sk1 = sk;
+	}
+
+	read_unlock(&sco_sk_list.lock);
+
+	return sk ? sk : sk1;
+}
+
+static void sco_sock_destruct(struct sock *sk)
+{
+	BT_DBG("sk %p", sk);
+
+	skb_queue_purge(&sk->receive_queue);
+	skb_queue_purge(&sk->write_queue);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static void sco_sock_cleanup_listen(struct sock *parent)
+{
+	struct sock *sk;
+
+	BT_DBG("parent %p", parent);
+
+	/* Close not yet accepted channels */
+	while ((sk = bluez_accept_dequeue(parent, NULL))) {
+		sco_sock_close(sk);
+		sco_sock_kill(sk);
+	}
+
+	parent->state  = BT_CLOSED;
+	parent->zapped = 1;
+}
+
+/* Kill socket (only if zapped and orphan)
+ * Must be called on unlocked socket.
+ */
+static void sco_sock_kill(struct sock *sk)
+{
+	if (!sk->zapped || sk->socket)
+		return;
+
+	BT_DBG("sk %p state %d", sk, sk->state);
+
+	/* Kill poor orphan */
+	bluez_sock_unlink(&sco_sk_list, sk);
+	sk->dead = 1;
+	sock_put(sk);
+}
+
+/* Close socket.
+ * Must be called on unlocked socket.
+ */
+static void sco_sock_close(struct sock *sk)
+{
+	struct sco_conn *conn;
+
+	sco_sock_clear_timer(sk);
+
+	lock_sock(sk);
+
+	conn = sco_pi(sk)->conn;
+
+	BT_DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket);
+
+	switch (sk->state) {
+	case BT_LISTEN:
+		sco_sock_cleanup_listen(sk);
+		break;
+
+	case BT_CONNECTED:
+	case BT_CONFIG:
+	case BT_CONNECT:
+	case BT_DISCONN:
+		sco_chan_del(sk, ECONNRESET);
+		break;
+
+	default:
+		sk->zapped = 1;
+		break;
+	};
+
+	release_sock(sk);
+}
+
+static void sco_sock_init(struct sock *sk, struct sock *parent)
+{
+	BT_DBG("sk %p", sk);
+
+	if (parent) 
+		sk->type = parent->type;
+}
+
+static struct sock *sco_sock_alloc(struct socket *sock, int proto, int prio)
+{
+	struct sock *sk;
+
+	if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1)))
+		return NULL;
+
+	bluez_sock_init(sock, sk);
+
+	sk->zapped   = 0;
+
+	sk->destruct = sco_sock_destruct;
+	sk->sndtimeo = SCO_CONN_TIMEOUT;
+
+	sk->protocol = proto;
+	sk->state    = BT_OPEN;
+
+	sco_sock_init_timer(sk);
+
+	bluez_sock_link(&sco_sk_list, sk);
+
+	MOD_INC_USE_COUNT;
+	return sk;
+}
+
+static int sco_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	sock->state = SS_UNCONNECTED;
+
+	if (sock->type != SOCK_SEQPACKET)
+		return -ESOCKTNOSUPPORT;
+
+	sock->ops = &sco_sock_ops;
+
+	if (!(sk = sco_sock_alloc(sock, protocol, GFP_KERNEL)))
+		return -ENOMEM;
+
+	sco_sock_init(sk, NULL);
+	return 0;
+}
+
+static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+	struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
+	struct sock *sk = sock->sk;
+	bdaddr_t *src = &sa->sco_bdaddr;
+	int err = 0;
+
+	BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr));
+
+	if (!addr || addr->sa_family != AF_BLUETOOTH)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_OPEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	write_lock_bh(&sco_sk_list.lock);
+
+	if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) {
+		err = -EADDRINUSE;
+	} else {
+		/* Save source address */
+		bacpy(&bluez_pi(sk)->src, &sa->sco_bdaddr);
+		sk->state = BT_BOUND;
+	}
+
+	write_unlock_bh(&sco_sk_list.lock);
+
+done:
+	release_sock(sk);
+
+	return err;
+}
+
+static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+{
+	struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+
+	BT_DBG("sk %p", sk);
+
+	if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_sco))
+		return -EINVAL;
+
+	if (sk->state != BT_OPEN && sk->state != BT_BOUND)
+		return -EBADFD;
+
+	if (sk->type != SOCK_SEQPACKET)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	/* Set destination address and psm */
+	bacpy(&bluez_pi(sk)->dst, &sa->sco_bdaddr);
+
+	if ((err = sco_connect(sk)))
+		goto done;
+
+	err = bluez_sock_wait_state(sk, BT_CONNECTED,
+			sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+int sco_sock_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p backlog %d", sk, backlog);
+
+	lock_sock(sk);
+
+	if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	sk->max_ack_backlog = backlog;
+	sk->ack_backlog = 0;
+	sk->state = BT_LISTEN;
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct sock *sk = sock->sk, *ch;
+	long timeo;
+	int err = 0;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_LISTEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
+
+	BT_DBG("sk %p timeo %ld", sk, timeo);
+
+	/* Wait for an incoming connection. (wake-one). */
+	add_wait_queue_exclusive(sk->sleep, &wait);
+	while (!(ch = bluez_accept_dequeue(sk, newsock))) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!timeo) {
+			err = -EAGAIN;
+			break;
+		}
+
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+
+		if (sk->state != BT_LISTEN) {
+			err = -EBADFD;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			err = sock_intr_errno(timeo);
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+
+	if (err)
+		goto done;
+
+	newsock->state = SS_CONNECTED;
+
+	BT_DBG("new socket %p", ch);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
+{
+	struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	addr->sa_family = AF_BLUETOOTH;
+	*len = sizeof(struct sockaddr_sco);
+
+	if (peer)
+		bacpy(&sa->sco_bdaddr, &bluez_pi(sk)->dst);
+	else
+		bacpy(&sa->sco_bdaddr, &bluez_pi(sk)->src);
+
+	return 0;
+}
+
+static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (sk->err)
+		return sock_error(sk);
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	lock_sock(sk);
+
+	if (sk->state == BT_CONNECTED)
+		err = sco_send_frame(sk, msg, len);
+	else
+		err = -ENOTCONN;
+
+	release_sock(sk);
+	return err;
+}
+
+int sco_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p", sk);
+
+	lock_sock(sk);
+
+	switch (optname) {
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	};
+
+	release_sock(sk);
+	return err;
+}
+
+int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+{
+	struct sock *sk = sock->sk;
+	struct sco_options opts;
+	struct sco_conninfo cinfo;
+	int len, err = 0; 
+
+	BT_DBG("sk %p", sk);
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	lock_sock(sk);
+
+	switch (optname) {
+	case SCO_OPTIONS:
+		if (sk->state != BT_CONNECTED) {
+			err = -ENOTCONN;
+			break;
+		}
+		
+		opts.mtu = sco_pi(sk)->conn->mtu;
+
+		BT_DBG("mtu %d", opts.mtu);
+
+		len = MIN(len, sizeof(opts));
+		if (copy_to_user(optval, (char *)&opts, len))
+			err = -EFAULT;
+
+		break;
+
+	case SCO_CONNINFO:
+		if (sk->state != BT_CONNECTED) {
+			err = -ENOTCONN;
+			break;
+		}
+
+		cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle;
+
+		len = MIN(len, sizeof(cinfo));
+		if (copy_to_user(optval, (char *)&cinfo, len))
+			err = -EFAULT;
+
+		break;
+
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	};
+
+	release_sock(sk);
+	return err;
+}
+
+static int sco_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	sco_sock_close(sk);
+	if (sk->linger) {
+		lock_sock(sk);
+		err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
+		release_sock(sk);
+	}
+
+	sock_orphan(sk);
+	sco_sock_kill(sk);
+	return err;
+}
+
+static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+{
+	BT_DBG("conn %p", conn);
+
+	sco_pi(sk)->conn = conn;
+	conn->sk = sk;
+
+	if (parent)
+		bluez_accept_enqueue(parent, sk);
+}
+
+static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+{
+	int err = 0;
+
+	sco_conn_lock(conn);
+	if (conn->sk) {
+		err = -EBUSY;
+	} else {
+		__sco_chan_add(conn, sk, parent);
+	}
+	sco_conn_unlock(conn);
+	return err;
+}
+
+static inline struct sock * sco_chan_get(struct sco_conn *conn)
+{
+	struct sock *sk = NULL;
+	sco_conn_lock(conn);
+	sk = conn->sk;
+	sco_conn_unlock(conn);
+	return sk;
+}
+
+/* Delete channel. 
+ * Must be called on the locked socket. */
+static void sco_chan_del(struct sock *sk, int err)
+{
+	struct sco_conn *conn;
+
+	conn = sco_pi(sk)->conn;
+
+	BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
+
+	if (conn) { 
+		sco_conn_lock(conn);
+		conn->sk = NULL;
+		sco_pi(sk)->conn = NULL;
+		sco_conn_unlock(conn);
+		hci_conn_put(conn->hcon);
+	}
+
+	sk->state = BT_CLOSED;
+	sk->err   = err;
+	sk->state_change(sk);
+
+	sk->zapped = 1;
+}
+
+static void sco_conn_ready(struct sco_conn *conn)
+{
+	struct sock *parent, *sk;
+
+	BT_DBG("conn %p", conn);
+
+	sco_conn_lock(conn);
+
+	if ((sk = conn->sk)) {
+		sco_sock_clear_timer(sk);
+		bh_lock_sock(sk);
+		sk->state = BT_CONNECTED;
+		sk->state_change(sk);
+		bh_unlock_sock(sk);
+	} else {
+		parent = sco_get_sock_listen(conn->src);
+		if (!parent)
+			goto done;
+
+		bh_lock_sock(parent);
+
+		sk = sco_sock_alloc(NULL, BTPROTO_SCO, GFP_ATOMIC);
+		if (!sk) {
+			bh_unlock_sock(parent);
+                	goto done;
+		}
+
+		sco_sock_init(sk, parent);
+
+		bacpy(&bluez_pi(sk)->src, conn->src);
+		bacpy(&bluez_pi(sk)->dst, conn->dst);
+
+		hci_conn_hold(conn->hcon);
+        	__sco_chan_add(conn, sk, parent);
+
+        	sk->state = BT_CONNECTED;
+
+		/* Wake up parent */
+		parent->data_ready(parent, 1);
+	
+        	bh_unlock_sock(parent);
+	}
+
+done:
+	sco_conn_unlock(conn);
+}
+
+/* ----- SCO interface with lower layer (HCI) ----- */
+int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
+{
+	BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
+
+	/* Always accept connection */
+	return HCI_LM_ACCEPT;
+}
+
+int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
+{
+	BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
+
+	if (hcon->type != SCO_LINK)
+		return 0;
+
+	if (!status) {
+		struct sco_conn *conn;
+
+		conn = sco_conn_add(hcon, status);
+		if (conn)
+			sco_conn_ready(conn);
+	} else 
+		sco_conn_del(hcon, bterr(status));
+	
+	return 0;
+}
+
+int sco_disconn_ind(struct hci_conn *hcon, __u8 reason)
+{
+	BT_DBG("hcon %p reason %d", hcon, reason);
+
+	if (hcon->type != SCO_LINK)
+		return 0;
+
+	sco_conn_del(hcon, bterr(reason));
+	return 0;
+}
+
+int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
+{
+	struct sco_conn *conn = hcon->sco_data;
+
+	if (!conn)
+		goto drop;
+
+	BT_DBG("conn %p len %d", conn, skb->len);
+
+	if (skb->len) {
+		sco_recv_frame(conn, skb);
+		return 0;
+	}
+
+drop:
+	kfree_skb(skb);	
+	return 0;
+}
+
+/* ----- Proc fs support ------ */
+static int sco_sock_dump(char *buf, struct bluez_sock_list *list)
+{
+	struct sco_pinfo *pi;
+	struct sock *sk;
+	char *ptr = buf;
+
+	write_lock_bh(&list->lock);
+
+	for (sk = list->head; sk; sk = sk->next) {
+		pi = sco_pi(sk);
+		ptr += sprintf(ptr, "%s %s %d\n",
+				batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst),
+				sk->state); 
+	}
+
+	write_unlock_bh(&list->lock);
+
+	ptr += sprintf(ptr, "\n");
+
+	return ptr - buf;
+}
+
+static int sco_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
+{
+	char *ptr = buf;
+	int len;
+
+	BT_DBG("count %d, offset %ld", count, offset);
+
+	ptr += sco_sock_dump(ptr, &sco_sk_list);
+	len  = ptr - buf;
+
+	if (len <= count + offset)
+		*eof = 1;
+
+	*start = buf + offset;
+	len -= offset;
+
+	if (len > count)
+		len = count;
+	if (len < 0)
+		len = 0;
+
+	return len;
+}
+
+static struct proto_ops sco_sock_ops = {
+	family:		PF_BLUETOOTH,
+	release:	sco_sock_release,
+	bind:		sco_sock_bind,
+	connect:	sco_sock_connect,
+	listen:		sco_sock_listen,
+	accept:		sco_sock_accept,
+	getname:	sco_sock_getname,
+	sendmsg:	sco_sock_sendmsg,
+	recvmsg:	bluez_sock_recvmsg,
+	poll:		bluez_sock_poll,
+	socketpair:	sock_no_socketpair,
+	ioctl:		sock_no_ioctl,
+	shutdown:	sock_no_shutdown,
+	setsockopt:	sco_sock_setsockopt,
+	getsockopt:	sco_sock_getsockopt,
+	mmap:		sock_no_mmap
+};
+
+static struct net_proto_family sco_sock_family_ops = {
+	family:		PF_BLUETOOTH,
+	create:		sco_sock_create
+};
+
+static struct hci_proto sco_hci_proto = {
+	name:		"SCO",
+	id:		HCI_PROTO_SCO,
+	connect_ind:	sco_connect_ind,
+	connect_cfm:	sco_connect_cfm,
+	disconn_ind:	sco_disconn_ind,
+	recv_scodata:	sco_recv_scodata,
+};
+
+int __init sco_init(void)
+{
+	int err;
+
+	if ((err = bluez_sock_register(BTPROTO_SCO, &sco_sock_family_ops))) {
+		BT_ERR("Can't register SCO socket layer");
+		return err;
+	}
+
+	if ((err = hci_register_proto(&sco_hci_proto))) {
+		BT_ERR("Can't register SCO protocol");
+		return err;
+	}
+
+	create_proc_read_entry("bluetooth/sco", 0, 0, sco_read_proc, NULL);
+
+	BT_INFO("BlueZ SCO ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION);
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+	return 0;
+}
+
+void sco_cleanup(void)
+{
+	int err;
+
+	remove_proc_entry("bluetooth/sco", NULL);
+
+	/* Unregister socket, protocol and notifier */
+	if ((err = bluez_sock_unregister(BTPROTO_SCO)))
+		BT_ERR("Can't unregister SCO socket layer %d", err);
+
+	if ((err = hci_unregister_proto(&sco_hci_proto)))
+		BT_ERR("Can't unregister SCO protocol %d", err);
+}
+
+module_init(sco_init);
+module_exit(sco_cleanup);
+
+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+MODULE_DESCRIPTION("BlueZ SCO ver " VERSION);
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/net/bluetooth/syms.c linux-2.4.18-mh15/net/bluetooth/syms.c
--- linux-2.4.18/net/bluetooth/syms.c	2001-09-07 18:28:38.000000000 +0200
+++ linux-2.4.18-mh15/net/bluetooth/syms.c	2004-08-01 16:26:23.000000000 +0200
@@ -25,7 +25,7 @@
 /*
  * BlueZ symbols.
  *
- * $Id: syms.c,v 1.1 2001/07/12 19:31:24 maxk Exp $
+ * $Id: syms.c,v 1.1 2002/03/08 21:06:59 maxk Exp $
  */
 
 #include <linux/config.h>
@@ -39,25 +39,28 @@
 #include <linux/socket.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
 #include <net/bluetooth/hci_core.h>
 
 /* HCI Core */
 EXPORT_SYMBOL(hci_register_dev);
 EXPORT_SYMBOL(hci_unregister_dev);
+EXPORT_SYMBOL(hci_suspend_dev);
+EXPORT_SYMBOL(hci_resume_dev);
+
 EXPORT_SYMBOL(hci_register_proto);
 EXPORT_SYMBOL(hci_unregister_proto);
-EXPORT_SYMBOL(hci_register_notifier);
-EXPORT_SYMBOL(hci_unregister_notifier);
 
+EXPORT_SYMBOL(hci_get_route);
 EXPORT_SYMBOL(hci_connect);
-EXPORT_SYMBOL(hci_disconnect);
 EXPORT_SYMBOL(hci_dev_get);
+EXPORT_SYMBOL(hci_conn_auth);
+EXPORT_SYMBOL(hci_conn_encrypt);
 
 EXPORT_SYMBOL(hci_recv_frame);
 EXPORT_SYMBOL(hci_send_acl);
 EXPORT_SYMBOL(hci_send_sco);
-EXPORT_SYMBOL(hci_send_raw);
+EXPORT_SYMBOL(hci_send_cmd);
+EXPORT_SYMBOL(hci_si_event);
 
 /* BlueZ lib */
 EXPORT_SYMBOL(bluez_dump);
@@ -68,5 +71,11 @@
 /* BlueZ sockets */
 EXPORT_SYMBOL(bluez_sock_register);
 EXPORT_SYMBOL(bluez_sock_unregister);
+EXPORT_SYMBOL(bluez_sock_init);
 EXPORT_SYMBOL(bluez_sock_link);
 EXPORT_SYMBOL(bluez_sock_unlink);
+EXPORT_SYMBOL(bluez_sock_recvmsg);
+EXPORT_SYMBOL(bluez_sock_poll);
+EXPORT_SYMBOL(bluez_accept_enqueue);
+EXPORT_SYMBOL(bluez_accept_dequeue);
+EXPORT_SYMBOL(bluez_sock_wait_state);
diff -urN linux-2.4.18/net/netsyms.c linux-2.4.18-mh15/net/netsyms.c
--- linux-2.4.18/net/netsyms.c	2002-02-25 20:38:14.000000000 +0100
+++ linux-2.4.18-mh15/net/netsyms.c	2004-08-01 16:26:23.000000000 +0200
@@ -159,6 +159,7 @@
 EXPORT_SYMBOL(put_cmsg);
 EXPORT_SYMBOL(sock_kmalloc);
 EXPORT_SYMBOL(sock_kfree_s);
+EXPORT_SYMBOL(sockfd_lookup);
 
 #ifdef CONFIG_FILTER
 EXPORT_SYMBOL(sk_run_filter);