summaryrefslogtreecommitdiff
path: root/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-patch-2.4.18-mh9.diff
diff options
context:
space:
mode:
authorChris Larson <clarson@kergoth.com>2004-11-09 00:36:47 +0000
committerChris Larson <clarson@kergoth.com>2004-11-09 00:36:47 +0000
commitf96441b9faf769c9ecdd4d338b605ea3d0cc4010 (patch)
treeedb17ec2c4ea13c5acb1c7350957a249a820e28d /linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-patch-2.4.18-mh9.diff
parentb6588aa6851fb220cedc387d21c51513ef8d67f4 (diff)
Disable bk EOLN_NATIVE conversions on all files in packages FILESPATHs, to prevent it screwing up patches.
BKrev: 4190111fA4MuVozAqwE7xOSL9fr-TA
Diffstat (limited to 'linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-patch-2.4.18-mh9.diff')
-rw-r--r--linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-patch-2.4.18-mh9.diff30831
1 files changed, 30831 insertions, 0 deletions
diff --git a/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-patch-2.4.18-mh9.diff b/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-patch-2.4.18-mh9.diff
index e69de29bb2..b31c57992f 100644
--- a/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-patch-2.4.18-mh9.diff
+++ b/linux/openzaurus-sa-2.4.18-rmk7-pxa3-embedix20030509/bluetooth-patch-2.4.18-mh9.diff
@@ -0,0 +1,30831 @@
+diff -urN linux-2.4.18/CREDITS linux-2.4.18-mh9/CREDITS
+--- linux-2.4.18/CREDITS Mon Feb 25 20:37:50 2002
++++ linux-2.4.18-mh9/CREDITS Mon Aug 25 18:38:09 2003
+@@ -1317,6 +1317,14 @@
+ S: Provo, Utah 84606-5607
+ S: USA
+
++N: Marcel Holtmann
++E: marcel@holtmann.org
++W: http://www.holtmann.org
++D: Author and maintainer of the various Bluetooth HCI drivers
++D: Author and maintainer of the CAPI message transport 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
+diff -urN linux-2.4.18/Documentation/Configure.help linux-2.4.18-mh9/Documentation/Configure.help
+--- linux-2.4.18/Documentation/Configure.help Mon Feb 25 20:37:51 2002
++++ linux-2.4.18-mh9/Documentation/Configure.help Mon Aug 25 18:38:10 2003
+@@ -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
+@@ -19870,11 +19868,15 @@
+ Bluetooth can be found at <http://www.bluetooth.com/>.
+
+ Linux Bluetooth subsystem consist of several layers:
+- HCI Core (device and connection manager, scheduler)
++ BlueZ Core (HCI device and connection manager, scheduler)
+ HCI Device drivers (interface to the hardware)
+ L2CAP Module (L2CAP protocol)
++ SCO Module (SCO links)
++ RFCOMM Module (RFCOMM protocol)
++ BNEP Module (BNEP protocol)
++ CMTP Module (CMTP protocol)
+
+- Say Y here to enable Linux Bluetooth support and to build HCI Core
++ Say Y here to enable Linux Bluetooth support and to build BlueZ Core
+ layer.
+
+ To use Linux Bluetooth subsystem, you will need several user-space
+@@ -19882,7 +19884,7 @@
+ 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.
++ If you want to compile BlueZ Core as module (bluez.o) say M here.
+
+ L2CAP protocol support
+ CONFIG_BLUEZ_L2CAP
+@@ -19893,15 +19895,91 @@
+ 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).
++
++ To use BNEP, you will need user-space utilities provided in the
++ BlueZ-PAN package.
++ For more information, see <http://bluez.sourceforge.net>.
++
++ Say Y here to compile BNEP support into the kernel or say M to
++ compile it as module (bnep.o).
++
++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).
++
++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.
++
+ 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,13 +19989,90 @@
+ 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_USB_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 USB zero packet support
++CONFIG_BLUEZ_USB_ZERO_PACKET
++ This option is provided only as a work around for buggy Bluetooth USB
++ devices. Do NOT enable it unless you know for sure that your device
++ requires zero packets.
++
++ Most people should say N here.
++
++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.
+
+ 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
++
++ The HCI BT3C driver uses external firmware loader program provided in
++ the BlueFW package. For more information, see <http://bluez.sf.net>.
++
++ 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/firmware_class/README linux-2.4.18-mh9/Documentation/firmware_class/README
+--- linux-2.4.18/Documentation/firmware_class/README Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/Documentation/firmware_class/README Mon Aug 25 18:38:10 2003
+@@ -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/Documentation/firmware_class/firmware_sample_driver.c linux-2.4.18-mh9/Documentation/firmware_class/firmware_sample_driver.c
+--- linux-2.4.18/Documentation/firmware_class/firmware_sample_driver.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/Documentation/firmware_class/firmware_sample_driver.c Mon Aug 25 18:38:10 2003
+@@ -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-mh9/Documentation/firmware_class/hotplug-script
+--- linux-2.4.18/Documentation/firmware_class/hotplug-script Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/Documentation/firmware_class/hotplug-script Mon Aug 25 18:38:10 2003
+@@ -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/MAINTAINERS linux-2.4.18-mh9/MAINTAINERS
+--- linux-2.4.18/MAINTAINERS Mon Feb 25 20:37:52 2002
++++ linux-2.4.18-mh9/MAINTAINERS Mon Aug 25 18:38:10 2003
+@@ -252,7 +252,73 @@
+ L: linux-kernel@vger.kernel.org
+ S: Maintained
+
+-BLUETOOTH SUBSYSTEM (BlueZ)
++BLUETOOTH SUBSYSTEM
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++W: http://bluez.sf.net
++S: Maintained
++
++BLUETOOTH RFCOMM LAYER
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++W: http://bluez.sf.net
++S: Maintained
++
++BLUETOOTH BNEP LAYER
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++W: http://bluez.sf.net
++S: Maintained
++
++BLUETOOTH CMTP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++W: http://www.holtmann.org/linux/bluetooth/
++S: Maintained
++
++BLUETOOTH HCI USB DRIVER
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++W: http://bluez.sf.net
++S: Maintained
++
++BLUETOOTH HCI UART DRIVER
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++W: http://bluez.sf.net
++S: Maintained
++
++BLUETOOTH HCI BFUSB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++W: http://www.holtmann.org/linux/bluetooth/
++S: Maintained
++
++BLUETOOTH HCI DTL1 DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++W: http://www.holtmann.org/linux/bluetooth/
++S: Maintained
++
++BLUETOOTH HCI BLUECARD DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++W: http://www.holtmann.org/linux/bluetooth/
++S: Maintained
++
++BLUETOOTH HCI BT3C DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++W: http://www.holtmann.org/linux/bluetooth/
++S: Maintained
++
++BLUETOOTH HCI BTUART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++W: http://www.holtmann.org/linux/bluetooth/
++S: Maintained
++
++BLUETOOTH HCI VHCI DRIVER
+ P: Maxim Krasnyansky
+ M: maxk@qualcomm.com
+ W: http://bluez.sf.net
+diff -urN linux-2.4.18/arch/alpha/config.in linux-2.4.18-mh9/arch/alpha/config.in
+--- linux-2.4.18/arch/alpha/config.in Wed Nov 21 00:49:31 2001
++++ linux-2.4.18-mh9/arch/alpha/config.in Mon Aug 25 18:38:10 2003
+@@ -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-mh9/arch/arm/config.in
+--- linux-2.4.18/arch/arm/config.in Fri Nov 9 22:58:02 2001
++++ linux-2.4.18-mh9/arch/arm/config.in Mon Aug 25 18:38:10 2003
+@@ -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-mh9/arch/i386/config.in
+--- linux-2.4.18/arch/i386/config.in Mon Feb 25 20:37:52 2002
++++ linux-2.4.18-mh9/arch/i386/config.in Mon Aug 25 18:38:10 2003
+@@ -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-mh9/arch/ppc/config.in
+--- linux-2.4.18/arch/ppc/config.in Mon Feb 25 20:37:55 2002
++++ linux-2.4.18-mh9/arch/ppc/config.in Mon Aug 25 18:38:10 2003
+@@ -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-mh9/arch/sparc/config.in
+--- linux-2.4.18/arch/sparc/config.in Tue Jun 12 04:15:27 2001
++++ linux-2.4.18-mh9/arch/sparc/config.in Mon Aug 25 18:38:10 2003
+@@ -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-mh9/arch/sparc64/config.in
+--- linux-2.4.18/arch/sparc64/config.in Fri Dec 21 18:41:53 2001
++++ linux-2.4.18-mh9/arch/sparc64/config.in Mon Aug 25 18:38:10 2003
+@@ -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-mh9/arch/sparc64/kernel/ioctl32.c
+--- linux-2.4.18/arch/sparc64/kernel/ioctl32.c Mon Feb 25 20:37:56 2002
++++ linux-2.4.18-mh9/arch/sparc64/kernel/ioctl32.c Mon Aug 25 18:38:10 2003
+@@ -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,11 @@
+ 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)
++
+ struct ioctl_trans {
+ unsigned int cmd;
+ unsigned int handler;
+@@ -4540,6 +4555,21 @@
+ 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)
+ /* Misc. */
+ COMPATIBLE_IOCTL(0x41545900) /* ATYIO_CLKR */
+ COMPATIBLE_IOCTL(0x41545901) /* ATYIO_CLKW */
+diff -urN linux-2.4.18/drivers/bluetooth/Config.in linux-2.4.18-mh9/drivers/bluetooth/Config.in
+--- linux-2.4.18/drivers/bluetooth/Config.in Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/drivers/bluetooth/Config.in Mon Aug 25 18:38:10 2003
+@@ -1,8 +1,34 @@
++#
++# 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_USB_SCO
++ bool ' USB zero packet support' CONFIG_BLUEZ_USB_ZERO_PACKET
++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/Makefile linux-2.4.18-mh9/drivers/bluetooth/Makefile
+--- linux-2.4.18/drivers/bluetooth/Makefile Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/drivers/bluetooth/Makefile Mon Aug 25 18:38:10 2003
+@@ -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-mh9/drivers/bluetooth/Makefile.lib
+--- linux-2.4.18/drivers/bluetooth/Makefile.lib Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/Makefile.lib Mon Aug 25 18:38:10 2003
+@@ -0,0 +1 @@
++obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o
+diff -urN linux-2.4.18/drivers/bluetooth/bfusb.c linux-2.4.18-mh9/drivers/bluetooth/bfusb.c
+--- linux-2.4.18/drivers/bluetooth/bfusb.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/bfusb.c Mon Aug 25 18:38:10 2003
+@@ -0,0 +1,781 @@
++/*
++ *
++ * 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);
++
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ return;
++
++ read_lock(&bfusb->lock);
++
++ 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);
++ }
++
++ 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-mh9/drivers/bluetooth/bluecard_cs.c
+--- linux-2.4.18/drivers/bluetooth/bluecard_cs.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/bluecard_cs.c Mon Aug 25 18:38:10 2003
+@@ -0,0 +1,1113 @@
++/*
++ *
++ * 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);
++
++ 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-mh9/drivers/bluetooth/bt3c_cs.c
+--- linux-2.4.18/drivers/bluetooth/bt3c_cs.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/bt3c_cs.c Mon Aug 25 18:38:10 2003
+@@ -0,0 +1,946 @@
++/*
++ *
++ * 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>
++
++#define __KERNEL_SYSCALLS__
++
++#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 <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;
++}
++
++
++
++/* ======================== User mode firmware loader ======================== */
++
++
++#define FW_LOADER "/sbin/bluefw"
++static int errno;
++
++
++static int bt3c_fw_loader_exec(void *dev)
++{
++ char *argv[] = { FW_LOADER, "pccard", dev, NULL };
++ char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
++ int err;
++
++ err = exec_usermodehelper(FW_LOADER, argv, envp);
++ if (err)
++ printk(KERN_WARNING "bt3c_cs: Failed to exec \"%s pccard %s\".\n", FW_LOADER, (char *)dev);
++
++ return err;
++}
++
++
++static int bt3c_firmware_load(bt3c_info_t *info)
++{
++ sigset_t tmpsig;
++ char dev[16];
++ pid_t pid;
++ int result;
++
++ /* Check if root fs is mounted */
++ if (!current->fs->root) {
++ printk(KERN_WARNING "bt3c_cs: Root filesystem is not mounted.\n");
++ return -EPERM;
++ }
++
++ sprintf(dev, "%04x", info->link.io.BasePort1);
++
++ pid = kernel_thread(bt3c_fw_loader_exec, (void *)dev, 0);
++ if (pid < 0) {
++ printk(KERN_WARNING "bt3c_cs: Forking of kernel thread failed (errno=%d).\n", -pid);
++ return pid;
++ }
++
++ /* Block signals, everything but SIGKILL/SIGSTOP */
++ spin_lock_irq(&current->sigmask_lock);
++ tmpsig = current->blocked;
++ siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP));
++ recalc_sigpending(current);
++ spin_unlock_irq(&current->sigmask_lock);
++
++ result = waitpid(pid, NULL, __WCLONE);
++
++ /* Allow signals again */
++ spin_lock_irq(&current->sigmask_lock);
++ current->blocked = tmpsig;
++ recalc_sigpending(current);
++ spin_unlock_irq(&current->sigmask_lock);
++
++ if (result != pid) {
++ printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result);
++ return -result;
++ }
++
++ return 0;
++}
++
++
++
++/* ======================== Card services HCI interaction ======================== */
++
++
++int bt3c_open(bt3c_info_t *info)
++{
++ 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 */
++
++ if ((err = bt3c_firmware_load(info)) < 0)
++ 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);
++
++ 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-mh9/drivers/bluetooth/btuart_cs.c
+--- linux-2.4.18/drivers/bluetooth/btuart_cs.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/btuart_cs.c Mon Aug 25 18:38:10 2003
+@@ -0,0 +1,906 @@
++/*
++ *
++ * 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);
++
++ 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/dtl1_cs.c linux-2.4.18-mh9/drivers/bluetooth/dtl1_cs.c
+--- linux-2.4.18/drivers/bluetooth/dtl1_cs.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/dtl1_cs.c Mon Aug 25 18:38:10 2003
+@@ -0,0 +1,858 @@
++/*
++ *
++ * 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);
++
++ 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-mh9/drivers/bluetooth/hci_bcsp.c
+--- linux-2.4.18/drivers/bluetooth/hci_bcsp.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_bcsp.c Mon Aug 25 18:38:10 2003
+@@ -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$
++ */
++
++#define VERSION "0.1"
++
++#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/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_ERR("Timeout, retransmitting %u pkts", 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-mh9/drivers/bluetooth/hci_bcsp.h
+--- linux-2.4.18/drivers/bluetooth/hci_bcsp.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_bcsp.h Mon Aug 25 18:38:10 2003
+@@ -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$
++ */
++
++#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-mh9/drivers/bluetooth/hci_h4.c
+--- linux-2.4.18/drivers/bluetooth/hci_h4.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_h4.c Mon Aug 25 18:38:10 2003
+@@ -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$
++ */
++#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-mh9/drivers/bluetooth/hci_h4.h
+--- linux-2.4.18/drivers/bluetooth/hci_h4.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_h4.h Mon Aug 25 18:38:10 2003
+@@ -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$
++ */
++
++#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-mh9/drivers/bluetooth/hci_ldisc.c
+--- linux-2.4.18/drivers/bluetooth/hci_ldisc.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_ldisc.c Mon Aug 25 18:38:10 2003
+@@ -0,0 +1,580 @@
++/*
++ 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$
++ */
++#define VERSION "2.1"
++
++#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/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-mh9/drivers/bluetooth/hci_uart.c
+--- linux-2.4.18/drivers/bluetooth/hci_uart.c Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_uart.c Thu Jan 1 01:00:00 1970
+@@ -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$
+- */
+-#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-mh9/drivers/bluetooth/hci_uart.h
+--- linux-2.4.18/drivers/bluetooth/hci_uart.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_uart.h Mon Aug 25 18:38:10 2003
+@@ -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$
++ */
++
++#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 3
++
++#define HCI_UART_H4 0
++#define HCI_UART_BCSP 1
++#define HCI_UART_NCSP 2
++
++#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-mh9/drivers/bluetooth/hci_usb.c
+--- linux-2.4.18/drivers/bluetooth/hci_usb.c Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_usb.c Mon Aug 25 18:38:12 2003
+@@ -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,901 @@
+ */
+
+ /*
+- * 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$
++ * $Id$
+ */
+-#define VERSION "1.0"
++#define VERSION "2.4"
+
+ #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_USB_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) },
++
++ /* Ericsson with non-standard id */
++ { USB_DEVICE(0x0bdb, 0x1002) },
++
++ /* Bluetooth Ultraport Module from IBM */
++ { USB_DEVICE(0x04bf, 0x030a) },
++
+ { } /* 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 ignore_ids[] = {
++ /* Broadcom BCM2033 without firmware */
++ { USB_DEVICE(0x0a5c, 0x2033) },
+
+-static void hci_usb_unlink_urbs(struct hci_usb *husb)
+-{
+- usb_unlink_urb(husb->read_urb);
+- usb_unlink_urb(husb->intr_urb);
+- usb_unlink_urb(husb->ctrl_urb);
+- usb_unlink_urb(husb->write_urb);
+-}
++ { } /* Terminating entry */
++};
+
+-static void hci_usb_free_bufs(struct hci_usb *husb)
++struct _urb *_urb_alloc(int isoc, int gfp)
+ {
+- if (husb->read_urb) {
+- if (husb->read_urb->transfer_buffer)
+- kfree(husb->read_urb->transfer_buffer);
+- usb_free_urb(husb->read_urb);
+- }
+-
+- if (husb->intr_urb) {
+- if (husb->intr_urb->transfer_buffer)
+- kfree(husb->intr_urb->transfer_buffer);
+- usb_free_urb(husb->intr_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;
++}
+
+- if (husb->ctrl_urb)
+- usb_free_urb(husb->ctrl_urb);
++static void hci_usb_rx_complete(struct urb *urb);
++static void hci_usb_tx_complete(struct urb *urb);
+
+- if (husb->write_urb)
+- usb_free_urb(husb->write_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])
+
+- if (husb->intr_skb)
+- kfree_skb(husb->intr_skb);
++static inline struct _urb *__get_completed(struct hci_usb *husb, int type)
++{
++ return _urb_dequeue(__completed_q(husb, type));
+ }
+
+-/* ------- Interface to HCI layer ------ */
+-/* Initialize device */
+-int hci_usb_open(struct hci_dev *hdev)
++static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
+ {
+- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+- int status;
+-
+- DBG("%s", hdev->name);
+-
+- husb->read_urb->dev = husb->udev;
+- if ((status = usb_submit_urb(husb->read_urb)))
+- DBG("read submit failed. %d", status);
++ int offset = 0, i;
+
+- husb->intr_urb->dev = husb->udev;
+- if ((status = usb_submit_urb(husb->intr_urb)))
+- DBG("interrupt submit failed. %d", status);
++ BT_DBG("len %d mtu %d", len, mtu);
+
+- hdev->flags |= HCI_RUNNING;
+-
+- return 0;
++ 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;
+ }
+
+-/* Reset device */
+-int hci_usb_flush(struct hci_dev *hdev)
++static int hci_usb_intr_rx_submit(struct hci_usb *husb)
+ {
+- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ struct _urb *_urb;
++ struct urb *urb;
++ int err, pipe, interval, size;
++ void *buf;
+
+- DBG("%s", hdev->name);
++ BT_DBG("%s", husb->hdev.name);
+
+- /* Drop TX queues */
+- skb_queue_purge(&husb->tx_ctrl_q);
+- skb_queue_purge(&husb->tx_write_q);
++ size = husb->intr_in_ep->wMaxPacketSize;
+
+- return 0;
++ 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;
+ }
+
+-/* Close device */
+-int hci_usb_close(struct hci_dev *hdev)
++static int hci_usb_bulk_rx_submit(struct hci_usb *husb)
+ {
+- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ struct _urb *_urb;
++ struct urb *urb;
++ int err, pipe, size = HCI_MAX_FRAME_SIZE;
++ void *buf;
+
+- DBG("%s", hdev->name);
++ buf = kmalloc(size, GFP_ATOMIC);
++ if (!buf)
++ return -ENOMEM;
+
+- hdev->flags &= ~HCI_RUNNING;
+- hci_usb_unlink_urbs(husb);
++ _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);
+
+- hci_usb_flush(hdev);
++ 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;
+
+- return 0;
++ 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;
+ }
+
+-void hci_usb_ctrl_wakeup(struct hci_usb *husb)
++#ifdef CONFIG_BLUEZ_USB_SCO
++static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
+ {
+- struct sk_buff *skb;
+-
+- if (test_and_set_bit(HCI_TX_CTRL, &husb->tx_state))
+- return;
++ struct _urb *_urb;
++ struct urb *urb;
++ int err, mtu, size;
++ void *buf;
+
+- DBG("%s", husb->hdev.name);
++ mtu = husb->isoc_in_ep->wMaxPacketSize;
++ size = mtu * HCI_MAX_ISOC_FRAMES;
+
+- if (!(skb = skb_dequeue(&husb->tx_ctrl_q)))
+- goto done;
++ buf = kmalloc(size, GFP_ATOMIC);
++ if (!buf)
++ return -ENOMEM;
+
+- if (hci_usb_ctrl_msg(husb, skb)){
+- kfree_skb(skb);
+- goto done;
++ _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);
+
+- DMP(skb->data, skb->len);
++ urb = &_urb->urb;
+
+- husb->hdev.stat.byte_tx += skb->len;
+- return;
++ urb->context = husb;
++ urb->dev = husb->udev;
++ urb->pipe = usb_rcvisocpipe(husb->udev, husb->isoc_in_ep->bEndpointAddress);
++ urb->complete = hci_usb_rx_complete;
+
+-done:
+- clear_bit(HCI_TX_CTRL, &husb->tx_state);
+- return;
++ 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
+
+-void hci_usb_write_wakeup(struct hci_usb *husb)
++/* Initialize device */
++static int hci_usb_open(struct hci_dev *hdev)
+ {
+- struct sk_buff *skb;
++ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ int i, err;
++ unsigned long flags;
+
+- if (test_and_set_bit(HCI_TX_WRITE, &husb->tx_state))
+- return;
++ BT_DBG("%s", hdev->name);
+
+- DBG("%s", husb->hdev.name);
++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
+
+- if (!(skb = skb_dequeue(&husb->tx_write_q)))
+- goto done;
++ MOD_INC_USE_COUNT;
+
+- if (hci_usb_write_msg(husb, skb)) {
+- skb_queue_head(&husb->tx_write_q, skb);
+- goto done;
++ write_lock_irqsave(&husb->completion_lock, flags);
++
++ 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_USB_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;
+ }
+
+- DMP(skb->data, skb->len);
++ write_unlock_irqrestore(&husb->completion_lock, flags);
++ return err;
++}
++
++/* Reset device */
++static int hci_usb_flush(struct hci_dev *hdev)
++{
++ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ int i;
+
+- husb->hdev.stat.byte_tx += skb->len;
+- return;
++ BT_DBG("%s", hdev->name);
+
+-done:
+- clear_bit(HCI_TX_WRITE, &husb->tx_state);
+- return;
++ for (i=0; i < 4; i++)
++ skb_queue_purge(&husb->transmit_q[i]);
++ return 0;
+ }
+
+-/* Send frames from HCI layer */
+-int hci_usb_send_frame(struct sk_buff *skb)
++static void hci_usb_unlink_urbs(struct hci_usb *husb)
+ {
+- struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+- struct hci_usb *husb;
++ int i;
+
+- if (!hdev) {
+- ERR("frame for uknown device (hdev=NULL)");
+- return -ENODEV;
++ BT_DBG("%s", husb->hdev.name);
++
++ 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);
++ }
++
++ /* 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);
++ }
++
++ /* Release reassembly buffers */
++ if (husb->reassembly[i]) {
++ kfree_skb(husb->reassembly[i]);
++ husb->reassembly[i] = NULL;
++ }
+ }
++}
+
+- if (!(hdev->flags & HCI_RUNNING))
++/* Close device */
++static int hci_usb_close(struct hci_dev *hdev)
++{
++ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
++ unsigned long flags;
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+- husb = (struct hci_usb *) hdev->driver_data;
++ BT_DBG("%s", hdev->name);
+
+- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
++ write_lock_irqsave(&husb->completion_lock, flags);
++
++ hci_usb_unlink_urbs(husb);
++ hci_usb_flush(hdev);
+
+- 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;
+- };
++ write_unlock_irqrestore(&husb->completion_lock, flags);
+
++ MOD_DEC_USE_COUNT;
+ return 0;
+ }
+
+-/* ---------- USB ------------- */
+-
+-static void hci_usb_ctrl(struct urb *urb)
++static int __tx_submit(struct hci_usb *husb, struct _urb *_urb)
+ {
+- struct sk_buff *skb = (struct sk_buff *) urb->context;
+- struct hci_dev *hdev;
+- struct hci_usb *husb;
+-
+- if (!skb)
+- return;
+- hdev = (struct hci_dev *) skb->dev;
+- husb = (struct hci_usb *) hdev->driver_data;
++ struct urb *urb = &_urb->urb;
++ int err;
+
+- DBG("%s", hdev->name);
++ 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));
++
++ return err;
++}
++
++static inline int hci_usb_send_ctrl(struct hci_usb *husb, 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 (urb->status)
+- DBG("%s ctrl status: %d", hdev->name, urb->status);
++ dr->requesttype = HCI_CTRL_REQ;
++ dr->request = 0;
++ dr->index = 0;
++ dr->value = 0;
++ dr->length = __cpu_to_le16(skb->len);
+
+- clear_bit(HCI_TX_CTRL, &husb->tx_state);
+- kfree_skb(skb);
++ 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);
+
+- /* Wake up device */
+- hci_usb_ctrl_wakeup(husb);
++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
++
++ _urb->priv = skb;
++ return __tx_submit(husb, _urb);
+ }
+
+-static void hci_usb_bulk_write(struct urb *urb)
++static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb)
+ {
+- struct sk_buff *skb = (struct sk_buff *) urb->context;
+- struct hci_dev *hdev;
+- struct hci_usb *husb;
+-
+- if (!skb)
+- return;
+- hdev = (struct hci_dev *) skb->dev;
+- husb = (struct hci_usb *) hdev->driver_data;
++ struct _urb *_urb = __get_completed(husb, skb->pkt_type);
++ struct urb *urb;
++ int pipe;
+
+- DBG("%s", hdev->name);
+-
+- if (urb->status)
+- DBG("%s bulk write status: %d", hdev->name, urb->status);
++ if (!_urb) {
++ _urb = _urb_alloc(0, GFP_ATOMIC);
++ if (!_urb)
++ return -ENOMEM;
++ _urb->type = skb->pkt_type;
++ }
+
+- clear_bit(HCI_TX_WRITE, &husb->tx_state);
+- kfree_skb(skb);
++ 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;
+
+- /* Wake up device */
+- hci_usb_write_wakeup(husb);
++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
+
+- return;
++ _urb->priv = skb;
++ return __tx_submit(husb, _urb);
+ }
+
+-static void hci_usb_intr(struct urb *urb)
++#ifdef CONFIG_BLUEZ_USB_SCO
++static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
+ {
+- 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;
++ 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 (!husb)
+- return;
++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len);
+
+- DBG("%s count %d", husb->hdev.name, count);
++ 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;
+
+- if (urb->status || !count) {
+- DBG("%s intr status %d, count %d", husb->hdev.name, urb->status, count);
+- return;
+- }
++ urb->transfer_buffer = skb->data;
++ urb->transfer_buffer_length = skb->len;
++
++ __fill_isoc_desc(urb, skb->len, husb->isoc_out_ep->wMaxPacketSize);
+
+- /* 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;
+- }
++ _urb->priv = skb;
++ return __tx_submit(husb, _urb);
++}
++#endif
++
++static void hci_usb_tx_process(struct hci_usb *husb)
++{
++ struct sk_buff_head *q;
++ struct sk_buff *skb;
+
+- eh = (hci_event_hdr *) data;
+- len = eh->plen + HCI_EVENT_HDR_SIZE;
++ BT_DBG("%s", husb->hdev.name);
+
+- if (count > len) {
+- DBG("%s corrupted frame, len %d", husb->hdev.name, count);
+- return;
++ do {
++ clear_bit(HCI_USB_TX_WAKEUP, &husb->state);
++
++ /* 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);
+ }
+
+- /* Allocate skb */
+- if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) {
+- ERR("Can't allocate mem for new packet");
+- return;
++#ifdef CONFIG_BLUEZ_USB_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;
++ }
+ }
+- skb->dev = (void *) &husb->hdev;
+- skb->pkt_type = HCI_EVENT_PKT;
++ } while(test_bit(HCI_USB_TX_WAKEUP, &husb->state));
++}
+
+- 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);
++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);
++}
+
+- kfree_skb(skb);
+- husb->intr_skb = NULL;
+- husb->intr_count = 0;
+- return;
+- }
++/* Send frames from HCI layer */
++static int hci_usb_send_frame(struct sk_buff *skb)
++{
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++ struct hci_usb *husb;
++
++ if (!hdev) {
++ BT_ERR("frame for uknown device (hdev=NULL)");
++ return -ENODEV;
+ }
+
+- memcpy(skb_put(skb, count), data, count);
+- husb->intr_count -= count;
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
+
+- DMP(data, count);
++ BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+
+- if (!husb->intr_count) {
+- /* Got complete frame */
++ husb = (struct hci_usb *) hdev->driver_data;
+
+- husb->hdev.stat.byte_rx += skb->len;
+- hci_recv_frame(skb);
++ 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_USB_SCO
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++#endif
+
+- husb->intr_skb = NULL;
++ default:
++ kfree_skb(skb);
++ return 0;
+ }
++
++ read_lock(&husb->completion_lock);
++
++ skb_queue_tail(__transmit_q(husb, skb->pkt_type), skb);
++ hci_usb_tx_wakeup(husb);
++
++ read_unlock(&husb->completion_lock);
++ return 0;
+ }
+
+-static void hci_usb_bulk_read(struct urb *urb)
++static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int count)
+ {
+- 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;
++ BT_DBG("%s type %d data %p count %d", husb->hdev.name, type, data, count);
+
+- DBG("%s status %d, count %d, flags %x", husb->hdev.name, urb->status, count, urb->transfer_flags);
++ husb->hdev.stat.byte_rx += count;
+
+- if (urb->status) {
+- /* Do not re-submit URB on critical errors */
+- switch (urb->status) {
+- case -ENOENT:
+- return;
+- default:
+- goto resubmit;
+- };
+- }
+- if (!count)
+- goto resubmit;
++ 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;
+
+- DMP(data, count);
++ 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_USB_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);
++
++ 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;
++ }
+
+- ah = (hci_acl_hdr *) data;
+- dlen = le16_to_cpu(ah->dlen);
++ 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);
++ }
+
+- /* 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);
+- goto resubmit;
++ count -= len; data += len;
+ }
++ return 0;
++}
+
+- /* Allocate packet */
+- if (!(skb = bluez_skb_alloc(count, GFP_ATOMIC))) {
+- ERR("Can't allocate mem for new packet");
+- goto resubmit;
+- }
++static void hci_usb_rx_complete(struct urb *urb)
++{
++ 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;
+
+- memcpy(skb_put(skb, count), data, count);
+- skb->dev = (void *) &husb->hdev;
+- skb->pkt_type = HCI_ACLDATA_PKT;
++ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,
++ _urb->type, urb->status, count, urb->transfer_flags);
+
+- husb->hdev.stat.byte_rx += skb->len;
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return;
+
+- hci_recv_frame(skb);
++ read_lock(&husb->completion_lock);
+
+-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->status || !count)
++ goto resubmit;
++
++ if (_urb->type == HCI_SCODATA_PKT) {
++#ifdef CONFIG_BLUEZ_USB_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++;
++ }
++ }
+
+- DBG("%s read URB re-submited", husb->hdev.name);
++resubmit:
++ 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);
++ }
++ 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);
++ urb->transfer_buffer = NULL;
++ kfree_skb((struct sk_buff *) _urb->priv);
+
+- FILL_CONTROL_URB(urb, husb->udev, pipe, (void*)dr, skb->data, skb->len,
+- hci_usb_ctrl, skb);
+-
+- 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);
+-
+- /* Check device signature */
+- if ((udev->descriptor.bDeviceClass != HCI_DEV_CLASS) ||
+- (udev->descriptor.bDeviceSubClass != HCI_DEV_SUBCLASS)||
+- (udev->descriptor.bDeviceProtocol != HCI_DEV_PROTOCOL) )
+- return NULL;
+-
+- MOD_INC_USE_COUNT;
++ BT_DBG("udev %p ifnum %d", udev, ifnum);
+
+- uif = &udev->actconfig->interface[ifnum].altsetting[0];
++ iface = &udev->actconfig->interface[0];
+
+- if (uif->bNumEndpoints != 3) {
+- DBG("Wrong number of endpoints %d", uif->bNumEndpoints);
+- MOD_DEC_USE_COUNT;
++ /* Check our black list */
++ if (usb_match_id(udev, iface, ignore_ids))
+ 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_USB_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_USB_SCO
++ if (!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];
++
++#ifdef CONFIG_BLUEZ_USB_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 (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 +930,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;
+ }
+diff -urN linux-2.4.18/drivers/bluetooth/hci_usb.h linux-2.4.18-mh9/drivers/bluetooth/hci_usb.h
+--- linux-2.4.18/drivers/bluetooth/hci_usb.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_usb.h Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,139 @@
++/*
++ 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$
++ */
++
++#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_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;
++
++ 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-mh9/drivers/bluetooth/hci_vhci.c
+--- linux-2.4.18/drivers/bluetooth/hci_vhci.c Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_vhci.c Mon Aug 25 18:38:10 2003
+@@ -25,9 +25,9 @@
+ /*
+ * BlueZ HCI virtual device driver.
+ *
+- * $Id$
++ * $Id$
+ */
+-#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-mh9/drivers/bluetooth/hci_vhci.h
+--- linux-2.4.18/drivers/bluetooth/hci_vhci.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/drivers/bluetooth/hci_vhci.h Mon Aug 25 18:38:10 2003
+@@ -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$
++ */
++
++#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/char/pcmcia/serial_cs.c linux-2.4.18-mh9/drivers/char/pcmcia/serial_cs.c
+--- linux-2.4.18/drivers/char/pcmcia/serial_cs.c Fri Dec 21 18:41:54 2001
++++ linux-2.4.18-mh9/drivers/char/pcmcia/serial_cs.c Mon Aug 25 18:38:10 2003
+@@ -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
+@@ -28,7 +28,7 @@
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+-
++
+ ======================================================================*/
+
+ #include <linux/module.h>
+@@ -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;
+ }
+
+@@ -487,7 +508,7 @@
+ int i, last_ret, last_fn;
+
+ DEBUG(0, "serial_config(0x%p)\n", link);
+-
++
+ tuple.TupleData = (cisdata_t *)buf;
+ tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+ tuple.Attributes = 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/usb/Config.in linux-2.4.18-mh9/drivers/usb/Config.in
+--- linux-2.4.18/drivers/usb/Config.in Mon Feb 25 20:38:07 2002
++++ linux-2.4.18-mh9/drivers/usb/Config.in Mon Aug 25 18:38:10 2003
+@@ -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/include/linux/firmware.h linux-2.4.18-mh9/include/linux/firmware.h
+--- linux-2.4.18/include/linux/firmware.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/include/linux/firmware.h Mon Aug 25 18:38:10 2003
+@@ -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/kernel.h linux-2.4.18-mh9/include/linux/kernel.h
+--- linux-2.4.18/include/linux/kernel.h Mon Feb 25 20:38:13 2002
++++ linux-2.4.18-mh9/include/linux/kernel.h Mon Aug 25 18:38:11 2003
+@@ -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/net/bluetooth/bluetooth.h linux-2.4.18-mh9/include/net/bluetooth/bluetooth.h
+--- linux-2.4.18/include/net/bluetooth/bluetooth.h Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/include/net/bluetooth/bluetooth.h Mon Aug 25 18:38:11 2003
+@@ -23,7 +23,7 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #ifndef __BLUETOOTH_H
+@@ -31,17 +31,63 @@
+
+ #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 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 +96,7 @@
+ BT_BOUND,
+ BT_LISTEN,
+ BT_CONNECT,
++ BT_CONNECT2,
+ BT_CONFIG,
+ BT_DISCONN,
+ BT_CLOSED
+@@ -66,7 +113,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 +130,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-mh9/include/net/bluetooth/bluez.h
+--- linux-2.4.18/include/net/bluetooth/bluez.h Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/include/net/bluetooth/bluez.h Thu Jan 1 01:00:00 1970
+@@ -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$
+- */
+-
+-#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.h linux-2.4.18-mh9/include/net/bluetooth/hci.h
+--- linux-2.4.18/include/net/bluetooth/hci.h Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/include/net/bluetooth/hci.h Mon Aug 25 18:38:12 2003
+@@ -23,59 +23,80 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #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 notify events */
++#define HCI_NOTIFY_CONN_ADD 1
++#define HCI_NOTIFY_CONN_DEL 2
++#define HCI_NOTIFY_VOICE_SETTING 3
+
+ /* 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 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,31 @@
+ } __attribute__ ((packed)) write_class_of_dev_cp;
+ #define WRITE_CLASS_OF_DEV_CP_SIZE 3
+
++#define OCF_READ_VOICE_SETTING 0x0025
++typedef struct {
++ __u8 status;
++ __u16 voice_setting;
++} __attribute__ ((packed)) read_voice_setting_rp;
++#define READ_VOICE_SETTING_RP_SIZE 3
++
++#define OCF_WRITE_VOICE_SETTING 0x0026
++typedef struct {
++ __u16 voice_setting;
++} __attribute__ ((packed)) write_voice_setting_cp;
++#define WRITE_VOICE_SETTING_CP_SIZE 2
++
++#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 +317,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 +331,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
+
+-/* --------- HCI Events --------- */
++#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
++
++#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,7 +475,7 @@
+ __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
+@@ -303,6 +506,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 +562,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 +672,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 +690,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 +738,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,12 +752,48 @@
+ 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 {
+ __u16 dev_id;
+ __u16 conn_num;
+ 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_core.h linux-2.4.18-mh9/include/net/bluetooth/hci_core.h
+--- linux-2.4.18/include/net/bluetooth/hci_core.h Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/include/net/bluetooth/hci_core.h Mon Aug 25 18:38:12 2003
+@@ -23,7 +23,7 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #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,182 @@
+ 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];
++ __u16 voice_setting;
+
+-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);
+-}
++ atomic_t cmd_cnt;
++ unsigned int acl_cnt;
++ unsigned int sco_cnt;
+
+-static inline long inquiry_cache_age(struct inquiry_cache *cache)
+-{
+- return jiffies - cache->timestamp;
+-}
++ unsigned int acl_mtu;
++ unsigned int sco_mtu;
++ unsigned int acl_pkts;
++ unsigned int sco_pkts;
+
+-static inline long inquiry_entry_age(struct inquiry_entry *e)
+-{
+- return jiffies - e->timestamp;
+-}
+-extern void inquiry_cache_flush(struct inquiry_cache *cache);
++ 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 hci_dev;
++ 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);
++ void (*notify)(struct hci_dev *hdev, unsigned int evt, unsigned long arg);
++ 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 long inquiry_cache_age(struct hci_dev *hdev)
+ {
+- spin_lock(&h->lock);
++ struct inquiry_cache *c = &hdev->inq_cache;
++ return jiffies - c->timestamp;
+ }
+
+-static inline void conn_hash_unlock(struct conn_hash *h)
++static inline long inquiry_entry_age(struct inquiry_entry *e)
+ {
+- spin_unlock(&h->lock);
++ 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)
+ {
+- list_add(&c->list, &h->list);
+- h->num++;
++ 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_add(struct conn_hash *h, __u16 handle, struct hci_conn *c)
++static inline void conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
+ {
+- conn_hash_lock(h);
+- __conn_hash_add(h, handle, c);
+- conn_hash_unlock(h);
++ 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)
+ {
++ struct conn_hash *h = &hdev->conn_hash;
+ list_del(&c->list);
+ h->num--;
+ }
+
+-static inline void conn_hash_del(struct conn_hash *h, struct hci_conn *c)
+-{
+- conn_hash_lock(h);
+- __conn_hash_del(h, c);
+- conn_hash_unlock(h);
+-}
+-
+-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 +238,95 @@
+ 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;
+-
+- struct semaphore req_lock;
+- wait_queue_head_t req_wait_q;
+- __u32 req_status;
+- __u32 req_result;
+-
+- struct inquiry_cache inq_cache;
++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 conn_hash conn_hash;
++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 hci_dev_stats stat;
++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);
+
+- 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 == SCO_LINK)
++ hci_conn_set_timer(conn, HZ / 100);
++ else if (conn->out)
++ hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT);
++ }
++}
+
+-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 +347,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_uart.h linux-2.4.18-mh9/include/net/bluetooth/hci_uart.h
+--- linux-2.4.18/include/net/bluetooth/hci_uart.h Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/include/net/bluetooth/hci_uart.h Thu Jan 1 01:00:00 1970
+@@ -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$
+- */
+-
+-#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-mh9/include/net/bluetooth/hci_usb.h
+--- linux-2.4.18/include/net/bluetooth/hci_usb.h Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/include/net/bluetooth/hci_usb.h Thu Jan 1 01:00:00 1970
+@@ -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$
+- */
+-
+-#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-mh9/include/net/bluetooth/hci_vhci.h
+--- linux-2.4.18/include/net/bluetooth/hci_vhci.h Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/include/net/bluetooth/hci_vhci.h Thu Jan 1 01:00:00 1970
+@@ -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$
+- */
+-
+-#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.h linux-2.4.18-mh9/include/net/bluetooth/l2cap.h
+--- linux-2.4.18/include/net/bluetooth/l2cap.h Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/include/net/bluetooth/l2cap.h Mon Aug 25 18:38:12 2003
+@@ -23,22 +23,17 @@
+ */
+
+ /*
+- * $Id$
++ * $Id$
+ */
+
+ #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;
+@@ -158,5 +176,75 @@
+ __u16 scid;
+ } __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
++
++/* ----- 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/l2cap_core.h linux-2.4.18-mh9/include/net/bluetooth/l2cap_core.h
+--- linux-2.4.18/include/net/bluetooth/l2cap_core.h Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/include/net/bluetooth/l2cap_core.h Thu Jan 1 01:00:00 1970
+@@ -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$
+- */
+-
+-#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/rfcomm.h linux-2.4.18-mh9/include/net/bluetooth/rfcomm.h
+--- linux-2.4.18/include/net/bluetooth/rfcomm.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/include/net/bluetooth/rfcomm.h Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,356 @@
++/*
++ 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$
++ */
++
++#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 */
++ uint mtu;
++ uint credits;
++
++ 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 credits;
++ 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)
++
++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-mh9/include/net/bluetooth/sco.h
+--- linux-2.4.18/include/net/bluetooth/sco.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/include/net/bluetooth/sco.h Mon Aug 25 18:38:12 2003
+@@ -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$
++ */
++
++#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-mh9/include/pcmcia/ciscode.h
+--- linux-2.4.18/include/pcmcia/ciscode.h Fri Dec 21 18:42:04 2001
++++ linux-2.4.18-mh9/include/pcmcia/ciscode.h Mon Aug 25 18:38:12 2003
+@@ -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,8 +127,11 @@
+
+ #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
+
+diff -urN linux-2.4.18/lib/Config.in linux-2.4.18-mh9/lib/Config.in
+--- linux-2.4.18/lib/Config.in Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/lib/Config.in Mon Aug 25 18:38:12 2003
+@@ -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/Makefile linux-2.4.18-mh9/lib/Makefile
+--- linux-2.4.18/lib/Makefile Tue Sep 18 00:31:15 2001
++++ linux-2.4.18-mh9/lib/Makefile Mon Aug 25 18:38:12 2003
+@@ -8,12 +8,16 @@
+
+ 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
+diff -urN linux-2.4.18/lib/firmware_class.c linux-2.4.18-mh9/lib/firmware_class.c
+--- linux-2.4.18/lib/firmware_class.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/lib/firmware_class.c Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,571 @@
++/*
++ * 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);
++
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
+diff -urN linux-2.4.18/net/bluetooth/Config.in linux-2.4.18-mh9/net/bluetooth/Config.in
+--- linux-2.4.18/net/bluetooth/Config.in Tue Jun 12 04:15:27 2001
++++ linux-2.4.18-mh9/net/bluetooth/Config.in Mon Aug 25 18:38:12 2003
+@@ -1,16 +1,22 @@
+ #
+-# 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 drivers/bluetooth/Config.in
+ fi
++
+ endmenu
+ fi
+
+diff -urN linux-2.4.18/net/bluetooth/Makefile linux-2.4.18-mh9/net/bluetooth/Makefile
+--- linux-2.4.18/net/bluetooth/Makefile Tue Jun 12 04:15:27 2001
++++ linux-2.4.18-mh9/net/bluetooth/Makefile Mon Aug 25 18:38:12 2003
+@@ -1,20 +1,31 @@
+ #
+-# 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
+
+-hci.o: $(hci-objs)
+- $(LD) -r -o $@ $(hci-objs)
++ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
++obj-y += rfcomm/rfcomm.o
++endif
++
++ifeq ($(CONFIG_BLUEZ_BNEP),y)
++obj-y += bnep/bnep.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/af_bluetooth.c linux-2.4.18-mh9/net/bluetooth/af_bluetooth.c
+--- linux-2.4.18/net/bluetooth/af_bluetooth.c Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/net/bluetooth/af_bluetooth.c Mon Aug 25 18:38:12 2003
+@@ -25,14 +25,15 @@
+ /*
+ * BlueZ Bluetooth address family and sockets.
+ *
+- * $Id$
++ * $Id$
+ */
+-#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 6
++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,162 @@
+ 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;
++
++ BT_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;
++
++ 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)
++ 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 +295,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 +330,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/Config.in linux-2.4.18-mh9/net/bluetooth/bnep/Config.in
+--- linux-2.4.18/net/bluetooth/bnep/Config.in Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/bnep/Config.in Mon Aug 25 18:38:12 2003
+@@ -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/Makefile linux-2.4.18-mh9/net/bluetooth/bnep/Makefile
+--- linux-2.4.18/net/bluetooth/bnep/Makefile Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/bnep/Makefile Mon Aug 25 18:38:12 2003
+@@ -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/bnep.h linux-2.4.18-mh9/net/bluetooth/bnep/bnep.h
+--- linux-2.4.18/net/bluetooth/bnep/bnep.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/bnep/bnep.h Mon Aug 25 18:38:12 2003
+@@ -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$
++ */
++
++#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/core.c linux-2.4.18-mh9/net/bluetooth/bnep/core.c
+--- linux-2.4.18/net/bluetooth/bnep/core.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/bnep/core.c Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,708 @@
++/*
++ 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$
++ */
++
++#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.1"
++
++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));
++}
++
++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));
++
++ 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 */
++
++ /* (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
++
++ 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-mh9/net/bluetooth/bnep/crc32.c
+--- linux-2.4.18/net/bluetooth/bnep/crc32.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/bnep/crc32.c Mon Aug 25 18:38:12 2003
+@@ -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-mh9/net/bluetooth/bnep/crc32.h
+--- linux-2.4.18/net/bluetooth/bnep/crc32.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/bnep/crc32.h Mon Aug 25 18:38:12 2003
+@@ -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/netdev.c linux-2.4.18-mh9/net/bluetooth/bnep/netdev.c
+--- linux-2.4.18/net/bluetooth/bnep/netdev.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/bnep/netdev.c Mon Aug 25 18:38:12 2003
+@@ -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$
++ */
++
++#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-mh9/net/bluetooth/bnep/sock.c
+--- linux-2.4.18/net/bluetooth/bnep/sock.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/bnep/sock.c Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,238 @@
++/*
++ 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$
++ */
++
++#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 inline struct socket *socki_lookup(struct inode *inode)
++{
++ return &inode->u.socket_i;
++}
++
++static struct socket *sockfd_lookup(int fd, int *err)
++{
++ struct file *file;
++ struct inode *inode;
++ struct socket *sock;
++
++ if (!(file = fget(fd))) {
++ *err = -EBADF;
++ return NULL;
++ }
++
++ inode = file->f_dentry->d_inode;
++ if (!inode->i_sock || !(sock = socki_lookup(inode))) {
++ *err = -ENOTSOCK;
++ fput(file);
++ return NULL;
++ }
++
++ if (sock->file != file) {
++ printk(KERN_ERR "socki_lookup: socket file changed!\n");
++ sock->file = file;
++ }
++ return sock;
++}
++
++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)
++ 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/Config.in linux-2.4.18-mh9/net/bluetooth/cmtp/Config.in
+--- linux-2.4.18/net/bluetooth/cmtp/Config.in Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/cmtp/Config.in Mon Aug 25 18:38:12 2003
+@@ -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/Makefile linux-2.4.18-mh9/net/bluetooth/cmtp/Makefile
+--- linux-2.4.18/net/bluetooth/cmtp/Makefile Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/cmtp/Makefile Mon Aug 25 18:38:12 2003
+@@ -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/capi.c linux-2.4.18-mh9/net/bluetooth/cmtp/capi.c
+--- linux-2.4.18/net/bluetooth/cmtp/capi.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/cmtp/capi.c Mon Aug 25 18:38:12 2003
+@@ -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-mh9/net/bluetooth/cmtp/cmtp.h
+--- linux-2.4.18/net/bluetooth/cmtp/cmtp.h Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/cmtp/cmtp.h Mon Aug 25 18:38:12 2003
+@@ -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/core.c linux-2.4.18-mh9/net/bluetooth/cmtp/core.c
+--- linux-2.4.18/net/bluetooth/cmtp/core.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/cmtp/core.c Mon Aug 25 18:38:12 2003
+@@ -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/sock.c linux-2.4.18-mh9/net/bluetooth/cmtp/sock.c
+--- linux-2.4.18/net/bluetooth/cmtp/sock.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/cmtp/sock.c Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,236 @@
++/*
++ 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 inline struct socket *socki_lookup(struct inode *inode)
++{
++ return &inode->u.socket_i;
++}
++
++static struct socket *sockfd_lookup(int fd, int *err)
++{
++ struct file *file;
++ struct inode *inode;
++ struct socket *sock;
++
++ if (!(file = fget(fd))) {
++ *err = -EBADF;
++ return NULL;
++ }
++
++ inode = file->f_dentry->d_inode;
++ if (!inode->i_sock || !(sock = socki_lookup(inode))) {
++ *err = -ENOTSOCK;
++ fput(file);
++ return NULL;
++ }
++
++ if (sock->file != file) {
++ printk(KERN_ERR "socki_lookup: socket file changed!\n");
++ sock->file = file;
++ }
++ return sock;
++}
++
++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)
++ 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/hci_conn.c linux-2.4.18-mh9/net/bluetooth/hci_conn.c
+--- linux-2.4.18/net/bluetooth/hci_conn.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/hci_conn.c Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,441 @@
++/*
++ 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$
++ */
++
++#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 = 0x01;
++
++ 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);
++
++ if (hdev->notify)
++ hdev->notify(hdev, HCI_NOTIFY_CONN_ADD, (unsigned long) conn);
++
++ 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);
++
++ if (hdev->notify)
++ hdev->notify(hdev, HCI_NOTIFY_CONN_DEL, (unsigned long) conn);
++
++ 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;
++
++ if (copy_from_user(&req, (void *) arg, sizeof(req)))
++ return -EFAULT;
++
++ if (!(hdev = hci_dev_get(req.dev_id)))
++ return -ENODEV;
++
++ size = req.conn_num * sizeof(struct hci_conn_info) + sizeof(req);
++
++ if (verify_area(VERIFY_WRITE, (void *)arg, size))
++ return -EFAULT;
++
++ if (!(cl = (void *) kmalloc(size, GFP_KERNEL)))
++ return -ENOMEM;
++ 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;
++ n++;
++ }
++ hci_dev_unlock_bh(hdev);
++
++ cl->dev_id = hdev->id;
++ cl->conn_num = n;
++ size = n * sizeof(struct hci_conn_info) + sizeof(req);
++
++ hci_dev_put(hdev);
++
++ copy_to_user((void *) arg, cl, size);
++ kfree(cl);
++
++ return 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;
++
++ if (verify_area(VERIFY_WRITE, ptr, sizeof(ci)))
++ 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;
++
++ copy_to_user(ptr, &ci, sizeof(ci));
++ return 0;
++}
+diff -urN linux-2.4.18/net/bluetooth/hci_core.c linux-2.4.18-mh9/net/bluetooth/hci_core.c
+--- linux-2.4.18/net/bluetooth/hci_core.c Fri Nov 9 23:21:21 2001
++++ linux-2.4.18-mh9/net/bluetooth/hci_core.c Mon Aug 25 18:38:12 2003
+@@ -25,11 +25,12 @@
+ /*
+ * BlueZ HCI Core.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #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,10 +211,10 @@
+
+ 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 */
+
+@@ -436,14 +224,30 @@
+ /* 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);
+
++ /* Read Voice Setting */
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_READ_VOICE_SETTING, 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 +262,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 +272,272 @@
+ {
+ __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 ||
++ 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 +546,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 +565,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 +598,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 +622,6 @@
+ tasklet_enable(&hdev->tx_task);
+ hci_req_unlock(hdev);
+ hci_dev_put(hdev);
+-
+ return ret;
+ }
+
+@@ -669,30 +640,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,48 +652,78 @@
+ 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;
+ __u16 dev_num;
+
+ if (get_user(dev_num, (__u16 *) arg))
+ return -EFAULT;
+
+- /* Avoid long loop, overflow */
+- if (dev_num > 2048)
++ if (!dev_num)
+ return -EINVAL;
+
+- size = dev_num * sizeof(struct hci_dev_req) + sizeof(__u16);
++ size = dev_num * sizeof(*dr) + sizeof(*dl);
+
+ if (verify_area(VERIFY_WRITE, (void *) arg, size))
+ return -EFAULT;
+@@ -750,25 +732,27 @@
+ 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 = n * sizeof(*dr) + sizeof(*dl);
+
+ copy_to_user((void *) arg, dl, size);
++ kfree(dl);
+
+ return 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);
+-
+- if (!(cl = kmalloc(size, GFP_KERNEL)))
+- return -ENOMEM;
+- ci = cl->conn_info;
++ sprintf(hdev->name, "hci%d", id);
++ hdev->id = id;
++ list_add(&hdev->list, head);
+
+- 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;
+-
+- DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
+-
+- /* 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;
++ hci_notify(hdev, HCI_DEV_SUSPEND);
++ hci_run_hotplug(hdev->name, "suspend");
++ return 0;
++}
+
+- sprintf(hdev->name, "hci%d", i);
+- atomic_set(&hdev->refcnt, 0);
+- hdev->id = i;
+- hdev->flags = HCI_NORMAL;
++/* 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;
++}
+
+- 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);
++/* ---- HCI TX task (outgoing data) ---- */
+
+- 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;
+-
+- 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-mh9/net/bluetooth/hci_event.c
+--- linux-2.4.18/net/bluetooth/hci_event.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/hci_event.c Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,927 @@
++/*
++ 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$
++ */
++
++#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;
++ __u16 setting;
++ read_voice_setting_rp *vs;
++ 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_READ_VOICE_SETTING:
++ vs = (read_voice_setting_rp *) skb->data;
++
++ if (vs->status) {
++ BT_DBG("%s READ_VOICE_SETTING failed %d", hdev->name, vc->status);
++ break;
++ }
++
++ setting = __le16_to_cpu(vs->voice_setting);
++
++ if (hdev->voice_setting != setting ) {
++ hdev->voice_setting = setting;
++
++ BT_DBG("%s: voice setting 0x%04x", hdev->name, setting);
++
++ if (hdev->notify)
++ hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING, 0);
++ }
++
++ break;
++
++ case OCF_WRITE_VOICE_SETTING:
++ sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING);
++ if (!sent)
++ break;
++
++ status = *((__u8 *) skb->data);
++ setting = __le16_to_cpu(get_unaligned((__u16 *) sent));
++
++ if (!status && hdev->voice_setting != setting) {
++ hdev->voice_setting = setting;
++
++ BT_DBG("%s: voice setting 0x%04x", hdev->name, setting);
++
++ if (hdev->notify)
++ hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING, 0);
++ }
++ 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_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);
++}
++
++/* 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_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-mh9/net/bluetooth/hci_sock.c
+--- linux-2.4.18/net/bluetooth/hci_sock.c Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/net/bluetooth/hci_sock.c Mon Aug 25 18:38:12 2003
+@@ -25,7 +25,7 @@
+ /*
+ * BlueZ HCI socket layer.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #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 */
++ { 0xd9fe, 0x0 },
++ /* Commands */
++ {
++ { 0x0 },
++ /* OGF_LINK_CTL */
++ { 0x2a000002, 0x0, 0x0, 0x0 },
++ /* OGF_LINK_POLICY */
++ { 0x1200, 0x0, 0x0, 0x0 },
++ /* OGF_HOST_CTL */
++ { 0x80100000, 0x202a, 0x0, 0x0 },
++ /* OGF_INFO_PARAM */
++ { 0x22a, 0x0, 0x0, 0x0 },
++ /* OGF_STATUS_PARAM */
++ { 0x2e, 0x0, 0x0, 0x0 }
++ }
++};
+
+ 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/l2cap.c linux-2.4.18-mh9/net/bluetooth/l2cap.c
+--- linux-2.4.18/net/bluetooth/l2cap.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/l2cap.c Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,2187 @@
++/*
++ 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$
++ */
++#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 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);
++
++ 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_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:
++ BT_ERR("Uknown 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-mh9/net/bluetooth/l2cap_core.c
+--- linux-2.4.18/net/bluetooth/l2cap_core.c Sun Sep 30 21:26:08 2001
++++ linux-2.4.18-mh9/net/bluetooth/l2cap_core.c Thu Jan 1 01:00:00 1970
+@@ -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$
+- */
+-#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-mh9/net/bluetooth/l2cap_proc.c
+--- linux-2.4.18/net/bluetooth/l2cap_proc.c Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/net/bluetooth/l2cap_proc.c Thu Jan 1 01:00:00 1970
+@@ -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$
+- */
+-
+-#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-mh9/net/bluetooth/lib.c
+--- linux-2.4.18/net/bluetooth/lib.c Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/net/bluetooth/lib.c Mon Aug 25 18:38:12 2003
+@@ -25,7 +25,7 @@
+ /*
+ * BlueZ kernel library.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #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/rfcomm/Config.in linux-2.4.18-mh9/net/bluetooth/rfcomm/Config.in
+--- linux-2.4.18/net/bluetooth/rfcomm/Config.in Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/rfcomm/Config.in Mon Aug 25 18:38:12 2003
+@@ -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/Makefile linux-2.4.18-mh9/net/bluetooth/rfcomm/Makefile
+--- linux-2.4.18/net/bluetooth/rfcomm/Makefile Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/rfcomm/Makefile Mon Aug 25 18:38:12 2003
+@@ -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/core.c linux-2.4.18-mh9/net/bluetooth/rfcomm/core.c
+--- linux-2.4.18/net/bluetooth/rfcomm/core.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/rfcomm/core.c Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,1951 @@
++/*
++ 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$
++ */
++
++#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.0"
++
++#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->credits = 0;
++ 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->credits = s->credits;
++
++ 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->credits) {
++ 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->credits) {
++ 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->credits = 0;
++
++ 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 (cr || d->credits) {
++ 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 (cr) {
++ if (pn->flow_ctrl == 0xf0) {
++ s->credits = RFCOMM_MAX_CREDITS;
++ d->credits = s->credits;
++ d->tx_credits = pn->credits;
++ } else {
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
++ d->credits = 0;
++ }
++ } else {
++ if (pn->flow_ctrl == 0xe0) {
++ s->credits = RFCOMM_MAX_CREDITS;
++ d->credits = s->credits;
++ d->tx_credits = pn->credits;
++ } else {
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
++ d->credits = 0;
++ }
++ }
++
++ d->priority = pn->priority;
++
++ d->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->credits)
++ 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->credits) {
++ 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 credits %d rx_credits %d tx_credits %d",
++ d, d->state, d->credits, 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->credits) {
++ /* CFC enabled.
++ * Give them some credits */
++ if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
++ d->rx_credits <= (d->credits >> 2)) {
++ rfcomm_send_credits(d->session, d->addr, d->credits - d->rx_credits);
++ d->rx_credits = d->credits;
++ }
++ } 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->credits && !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);
++ 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-mh9/net/bluetooth/rfcomm/crc.c
+--- linux-2.4.18/net/bluetooth/rfcomm/crc.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/rfcomm/crc.c Mon Aug 25 18:38:12 2003
+@@ -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$
++ */
++
++/* 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/sock.c linux-2.4.18-mh9/net/bluetooth/rfcomm/sock.c
+--- linux-2.4.18/net/bluetooth/rfcomm/sock.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/rfcomm/sock.c Mon Aug 25 18:38:12 2003
+@@ -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$
++ */
++
++#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-mh9/net/bluetooth/rfcomm/tty.c
+--- linux-2.4.18/net/bluetooth/rfcomm/tty.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/rfcomm/tty.c Mon Aug 25 18:38:12 2003
+@@ -0,0 +1,945 @@
++/*
++ 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$
++ */
++
++#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)
++{
++ 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);
++ read_unlock(&rfcomm_dev_lock);
++
++ if (dev) rfcomm_dev_hold(dev);
++ 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 (!capable(CAP_NET_ADMIN))
++ return -EPERM;
++
++ if (!(dev = rfcomm_dev_get(req.dev_id)))
++ return -ENODEV;
++
++ 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;
++ u16 dev_num;
++
++ BT_DBG("");
++
++ if (get_user(dev_num, (u16 *) arg))
++ return -EFAULT;
++
++ if (!dev_num)
++ return -EINVAL;
++
++ size = sizeof(*dl) + dev_num * sizeof(*di);
++
++ if (verify_area(VERIFY_WRITE, (void *)arg, size))
++ return -EFAULT;
++
++ 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);
++
++ copy_to_user((void *) arg, dl, size);
++ kfree(dl);
++ return 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 refference */
++ 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);
++
++ 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-mh9/net/bluetooth/sco.c
+--- linux-2.4.18/net/bluetooth/sco.c Thu Jan 1 01:00:00 1970
++++ linux-2.4.18-mh9/net/bluetooth/sco.c Mon Aug 25 18:38:12 2003
+@@ -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$
++ */
++#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-mh9/net/bluetooth/syms.c
+--- linux-2.4.18/net/bluetooth/syms.c Fri Sep 7 18:28:38 2001
++++ linux-2.4.18-mh9/net/bluetooth/syms.c Mon Aug 25 18:38:12 2003
+@@ -25,7 +25,7 @@
+ /*
+ * BlueZ symbols.
+ *
+- * $Id$
++ * $Id$
+ */
+
+ #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);