summaryrefslogtreecommitdiff
path: root/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch
diff options
context:
space:
mode:
authornslu2-linux.adm@bkbits.net <nslu2-linux.adm@bkbits.net>2005-03-30 13:19:04 +0000
committernslu2-linux.adm@bkbits.net <nslu2-linux.adm@bkbits.net>2005-03-30 13:19:04 +0000
commit99c724568b2c8e6d1b9857435ebe601854615f53 (patch)
tree87f55bc20ac87dcbd88bbdafeae608e8cf837510 /packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch
parentac58f168c434bc09037c9e3b74b989c12d1a5d75 (diff)
Merge bk://oe-devel.bkbits.net/openembedded
into bkbits.net:/repos/n/nslu2-linux/openembedded 2005/03/30 14:00:48+02:00 mn-solutions.de!schurig MNCI "Ramses": newest bluez for the kernel 2005/03/30 13:59:44+02:00 mn-solutions.de!schurig bluez-utils install the binaries into /{s}bin, not /usr/{s}bin, this patch fixes the scripts 2005/03/30 13:54:17+02:00 utwente.nl!koen Merge bk://oe-devel@oe-devel.bkbits.net/openembedded into bitbake.utwente.nl:/home/koen/OE/openembedded 2005/03/30 13:53:47+02:00 utwente.nl!koen preferred-gpe-versions.inc: add working minimo CVSDATE BKrev: 424aa748Ew_6rzmEpNqOmQsr4KaxYw
Diffstat (limited to 'packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch')
-rw-r--r--packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch8135
1 files changed, 8038 insertions, 97 deletions
diff --git a/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch b/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch
index 2f80d090a2..d617ac80ce 100644
--- a/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch
+++ b/packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch
@@ -28,6 +28,7 @@
# vmalloc.patch
# usb-sl811.patch
# orinoco013e.patch
+# bluetooth.patch
# ramses.patch
# ramses-ac97.patch
# ramses-keyb.patch
@@ -49,6 +50,201 @@
# Patch managed by http://www.holgerschurig.de/patcher.html
#
+--- linux-2.4.21/CREDITS~bluetooth
++++ linux-2.4.21/CREDITS
+@@ -1348,7 +1348,11 @@
+ N: Marcel Holtmann
+ E: marcel@holtmann.org
+ W: http://www.holtmann.org
+-D: Author of the Linux Bluetooth Subsystem PC Card drivers
++D: Maintainer of the Linux Bluetooth Subsystem
++D: Author and maintainer of the various Bluetooth HCI drivers
++D: Author and maintainer of the CAPI message transport protocol driver
++D: Author and maintainer of the Bluetooth HID protocol driver
++D: Various other Bluetooth related patches, cleanups and fixes
+ S: Germany
+
+ N: Rob W. W. Hooft
+@@ -2624,6 +2628,7 @@
+ N: Aristeu Sergio Rozanski Filho
+ E: aris@conectiva.com.br
+ D: Support for EtherExpress 10 ISA (i82595) in eepro driver
++D: User level driver support for input
+ S: Conectiva S.A.
+ S: R. Tocantins, 89 - Cristo Rei
+ S: 80050-430 - Curitiba - Paraná
+--- linux-2.4.21/Documentation/Configure.help~bluetooth
++++ linux-2.4.21/Documentation/Configure.help
+@@ -14071,6 +14071,15 @@
+ accessible under char device 13:64+ - /dev/input/eventX in a generic
+ way. This is the future ...
+
++CONFIG_INPUT_UINPUT
++ Say Y here if you want to support user level drivers for input
++ subsystem accessible under char device 10:223 - /dev/input/uinput.
++
++ This driver is also available as a module ( = code which can be
++ inserted in and removed from the running kernel whenever you want).
++ The module will be called uinput.o. If you want to compile it as a
++ module, say M here and read <file:Documentation/modules.txt>.
++
+ USB Scanner support
+ CONFIG_USB_SCANNER
+ Say Y here if you want to connect a USB scanner to your computer's
+@@ -21670,21 +21679,21 @@
+
+ Linux Bluetooth subsystem consist of several layers:
+ 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)
++ HCI Device drivers (Interface to the hardware)
++ SCO Module (SCO audio links)
++ L2CAP Module (Logical Link Control and Adaptation Protocol)
++ RFCOMM Module (RFCOMM Protocol)
++ BNEP Module (Bluetooth Network Encapsulation Protocol)
++ CMTP Module (CAPI Message Transport Protocol)
++ HIDP Module (Human Interface Device Protocol)
+
+- Say Y here to enable Linux Bluetooth support and to build BlueZ Core
+- layer.
++ Say Y here to compile Bluetooth support into the kernel or say M to
++ compile it as module (bluez.o).
+
+ To use Linux Bluetooth subsystem, you will need several user-space
+ utilities like hciconfig and hcid. These utilities and updates to
+ Bluetooth kernel modules are provided in the BlueZ package.
+- For more information, see <http://bluez.sourceforge.net/>.
+-
+- If you want to compile BlueZ Core as module (bluez.o) say M here.
++ For more information, see <http://www.bluez.org/>.
+
+ L2CAP protocol support
+ CONFIG_BLUEZ_L2CAP
+@@ -21697,7 +21706,7 @@
+
+ SCO links support
+ CONFIG_BLUEZ_SCO
+- SCO link provides voice transport over Bluetooth. SCO support is
++ 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
+@@ -21705,7 +21714,7 @@
+
+ RFCOMM protocol support
+ CONFIG_BLUEZ_RFCOMM
+- RFCOMM provides connection oriented stream transport. RFCOMM
++ RFCOMM provides connection oriented stream transport. RFCOMM
+ support is required for Dialup Networking, OBEX and other Bluetooth
+ applications.
+
+@@ -21719,12 +21728,8 @@
+ 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>.
++ emulation layer on top of Bluetooth. BNEP is required for
++ Bluetooth PAN (Personal Area Network).
+
+ Say Y here to compile BNEP support into the kernel or say M to
+ compile it as module (bnep.o).
+@@ -21737,6 +21742,24 @@
+ CONFIG_BLUEZ_BNEP_PROTO_FILTER
+ This option enables the protocol filter support for BNEP.
+
++CMTP protocol support
++CONFIG_BLUEZ_CMTP
++ CMTP (CAPI Message Transport Protocol) is a transport layer
++ for CAPI messages. CMTP is required for the Bluetooth Common
++ ISDN Access Profile.
++
++ Say Y here to compile CMTP support into the kernel or say M to
++ compile it as module (cmtp.o).
++
++HIDP protocol support
++CONFIG_BLUEZ_HIDP
++ HIDP (Human Interface Device Protocol) is a transport layer
++ for HID reports. HIDP is required for the Bluetooth Human
++ Interface Device Profile.
++
++ Say Y here to compile HIDP support into the kernel or say M to
++ compile it as module (hidp.o).
++
+ HCI UART driver
+ CONFIG_BLUEZ_HCIUART
+ Bluetooth HCI UART driver.
+@@ -21781,7 +21804,7 @@
+ kernel or say M to compile it as module (hci_usb.o).
+
+ HCI USB SCO (voice) support
+-CONFIG_BLUEZ_USB_SCO
++CONFIG_BLUEZ_HCIUSB_SCO
+ This option enables the SCO support in the HCI USB driver. You need this
+ to transmit voice data with your Bluetooth USB device. And your device
+ must also support sending SCO data over the HCI layer, because some of
+@@ -21789,14 +21812,6 @@
+
+ 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.
+@@ -21805,6 +21820,16 @@
+ 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.
+@@ -21824,9 +21849,6 @@
+ 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).
+
+@@ -26815,6 +26837,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.
++
+ NatSemi SCx200 support
+ CONFIG_SCx200
+ This provides basic support for the National Semiconductor SCx200
--- /dev/null
+++ linux-2.4.21/Documentation/DocBook/librs.tmpl
@@ -0,0 +1,287 @@
@@ -1660,6 +1856,314 @@
+ </para>
+ </chapter>
+</book>
+--- linux-2.4.21/Documentation/devices.txt~bluetooth
++++ linux-2.4.21/Documentation/devices.txt
+@@ -419,6 +419,7 @@
+ 220 = /dev/mptctl Message passing technology (MPT) control
+ 221 = /dev/mvista/hssdsi Montavista PICMG hot swap system driver
+ 222 = /dev/mvista/hasi Montavista PICMG high availability
++ 223 = /dev/input/uinput User level driver support for input
+ 240-255 Reserved for local use
+
+ 11 char Raw keyboard device
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/README
+@@ -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.
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/firmware_sample_driver.c
+@@ -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");
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/hotplug-script
+@@ -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
++#
+--- linux-2.4.21/MAINTAINERS~bluetooth
++++ linux-2.4.21/MAINTAINERS
+@@ -302,16 +302,88 @@
+ L: linux-kernel@vger.kernel.org
+ S: Maintained
+
+-BLUETOOTH SUBSYSTEM (BlueZ)
++BLUETOOTH SUBSYSTEM
++P: Marcel Holtmann
++M: marcel@holtmann.org
+ P: Maxim Krasnyansky
+ M: maxk@qualcomm.com
++L: bluez-devel@lists.sf.net
+ W: http://bluez.sf.net
++W: http://www.bluez.org
++W: http://www.holtmann.org/linux/bluetooth/
+ S: Maintained
+
+-BLUETOOTH SUBSYSTEM (PC Card Drivers)
++BLUETOOTH RFCOMM LAYER
+ P: Marcel Holtmann
+ M: marcel@holtmann.org
+-W: http://www.holtmann.org/linux/bluetooth/
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH BNEP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH CMTP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HIDP LAYER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI UART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH HCI USB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
++S: Maintained
++
++BLUETOOTH HCI BCM203X DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BFUSB DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI DTL1 DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BLUECARD DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BT3C DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI BTUART DRIVER
++P: Marcel Holtmann
++M: marcel@holtmann.org
++S: Maintained
++
++BLUETOOTH HCI VHCI DRIVER
++P: Maxim Krasnyansky
++M: maxk@qualcomm.com
+ S: Maintained
+
+ BONDING DRIVER
--- linux-2.4.21/Makefile~linux-mkdep
+++ linux-2.4.21/Makefile
@@ -14,10 +14,11 @@
@@ -1783,7 +2287,7 @@
--- /dev/null
+++ linux-2.4.21/arch/arm/def-configs/ramses
-@@ -0,0 +1,1174 @@
+@@ -0,0 +1,1177 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
@@ -2422,6 +2926,7 @@
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
++CONFIG_INPUT_UINPUT=m
+# CONFIG_INPUT_MX1TS is not set
+
+#
@@ -2913,26 +3418,27 @@
+CONFIG_BLUEZ_L2CAP=m
+CONFIG_BLUEZ_SCO=m
+CONFIG_BLUEZ_RFCOMM=m
-+# CONFIG_BLUEZ_RFCOMM_TTY is not set
++CONFIG_BLUEZ_RFCOMM_TTY=y
+CONFIG_BLUEZ_BNEP=m
+CONFIG_BLUEZ_BNEP_MC_FILTER=y
-+# CONFIG_BLUEZ_BNEP_PROTO_FILTER is not set
++CONFIG_BLUEZ_BNEP_PROTO_FILTER=y
++CONFIG_BLUEZ_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BLUEZ_HCIUSB=m
-+CONFIG_BLUEZ_USB_SCO=y
-+CONFIG_BLUEZ_USB_ZERO_PACKET=y
++CONFIG_BLUEZ_HCIUSB_SCO=y
+CONFIG_BLUEZ_HCIUART=m
+CONFIG_BLUEZ_HCIUART_H4=y
+CONFIG_BLUEZ_HCIUART_BCSP=y
-+CONFIG_BLUEZ_HCIUART_BCSP_TXCRC=y
++# CONFIG_BLUEZ_HCIUART_BCSP_TXCRC is not set
++CONFIG_BLUEZ_HCIBFUSB=m
+CONFIG_BLUEZ_HCIDTL1=m
+CONFIG_BLUEZ_HCIBT3C=m
+CONFIG_BLUEZ_HCIBLUECARD=m
+CONFIG_BLUEZ_HCIBTUART=m
-+# CONFIG_BLUEZ_HCIVHCI is not set
++CONFIG_BLUEZ_HCIVHCI=m
+
+#
+# Kernel hacking
@@ -2958,6 +3464,7 @@
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+# CONFIG_REED_SOLOMON is not set
++CONFIG_FW_LOADER=m
--- linux-2.4.21/arch/arm/mach-pxa/Makefile~pm
+++ linux-2.4.21/arch/arm/mach-pxa/Makefile
@@ -14,8 +14,11 @@
@@ -4075,7 +4582,7 @@
+#endif
+#define USE_UCB
+//#define PFI_LED
-+//#define PFI_TURNOFF
++#define PFI_TURNOFF
+
+#include <asm/types.h>
+#include <asm/setup.h>
@@ -4927,6 +5434,1410 @@
pstr = pxa_usb_kmalloc_string_descriptor( "PXA USB NIC" );
if ( pstr ) {
pxa_usb_set_string_descriptor( 1, pstr );
+--- linux-2.4.21/drivers/bluetooth/Config.in~bluetooth
++++ linux-2.4.21/drivers/bluetooth/Config.in
+@@ -7,8 +7,7 @@
+
+ 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
++ bool ' SCO (voice) support' CONFIG_BLUEZ_HCIUSB_SCO
+ fi
+
+ dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
+@@ -18,6 +17,8 @@
+ 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
+--- linux-2.4.21/drivers/bluetooth/Makefile~bluetooth
++++ linux-2.4.21/drivers/bluetooth/Makefile
+@@ -14,6 +14,8 @@
+ 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
+--- /dev/null
++++ linux-2.4.21/drivers/bluetooth/Makefile.lib
+@@ -0,0 +1,2 @@
++obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o
++obj-$(CONFIG_BLUEZ_HCIBT3C) += firmware_class.o
+--- /dev/null
++++ linux-2.4.21/drivers/bluetooth/bfusb.c
+@@ -0,0 +1,782 @@
++/*
++ *
++ * AVM BlueFRITZ! USB driver
++ *
++ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
++
++#include <linux/firmware.h>
++#include <linux/usb.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
++
++#ifndef CONFIG_BLUEZ_HCIBFUSB_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.1"
++
++static struct usb_device_id bfusb_table[] = {
++ /* AVM BlueFRITZ! USB */
++ { USB_DEVICE(0x057c, 0x2200) },
++
++ { } /* Terminating entry */
++};
++
++MODULE_DEVICE_TABLE(usb, bfusb_table);
++
++
++#define BFUSB_MAX_BLOCK_SIZE 256
++
++#define BFUSB_BLOCK_TIMEOUT (HZ * 3)
++
++#define BFUSB_TX_PROCESS 1
++#define BFUSB_TX_WAKEUP 2
++
++#define BFUSB_MAX_BULK_TX 1
++#define BFUSB_MAX_BULK_RX 1
++
++struct bfusb {
++ struct hci_dev hdev;
++
++ unsigned long state;
++
++ struct usb_device *udev;
++
++ unsigned int bulk_in_ep;
++ unsigned int bulk_out_ep;
++ unsigned int bulk_pkt_size;
++
++ rwlock_t lock;
++
++ struct sk_buff_head transmit_q;
++
++ struct sk_buff *reassembly;
++
++ atomic_t pending_tx;
++ struct sk_buff_head pending_q;
++ struct sk_buff_head completed_q;
++};
++
++struct bfusb_scb {
++ struct urb *urb;
++};
++
++static void bfusb_tx_complete(struct urb *urb);
++static void bfusb_rx_complete(struct urb *urb);
++
++static struct urb *bfusb_get_completed(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++ struct urb *urb = NULL;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ skb = skb_dequeue(&bfusb->completed_q);
++ if (skb) {
++ urb = ((struct bfusb_scb *) skb->cb)->urb;
++ kfree_skb(skb);
++ }
++
++ return urb;
++}
++
++static inline void bfusb_unlink_urbs(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++ struct urb *urb;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ while ((skb = skb_dequeue(&bfusb->pending_q))) {
++ urb = ((struct bfusb_scb *) skb->cb)->urb;
++ usb_unlink_urb(urb);
++ skb_queue_tail(&bfusb->completed_q, skb);
++ }
++
++ while ((urb = bfusb_get_completed(bfusb)))
++ usb_free_urb(urb);
++}
++
++
++static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb)
++{
++ struct bfusb_scb *scb = (void *) skb->cb;
++ struct urb *urb = bfusb_get_completed(bfusb);
++ int err, pipe;
++
++ BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len);
++
++ if (!urb && !(urb = usb_alloc_urb(0)))
++ return -ENOMEM;
++
++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
++
++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, skb->len,
++ bfusb_tx_complete, skb);
++
++ urb->transfer_flags = USB_QUEUE_BULK;
++
++ scb->urb = urb;
++
++ skb_queue_tail(&bfusb->pending_q, skb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk tx submit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ skb_unlink(skb);
++ usb_free_urb(urb);
++ } else
++ atomic_inc(&bfusb->pending_tx);
++
++ return err;
++}
++
++static void bfusb_tx_wakeup(struct bfusb *bfusb)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("bfusb %p", bfusb);
++
++ if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) {
++ set_bit(BFUSB_TX_WAKEUP, &bfusb->state);
++ return;
++ }
++
++ do {
++ clear_bit(BFUSB_TX_WAKEUP, &bfusb->state);
++
++ while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) &&
++ (skb = skb_dequeue(&bfusb->transmit_q))) {
++ if (bfusb_send_bulk(bfusb, skb) < 0) {
++ skb_queue_head(&bfusb->transmit_q, skb);
++ break;
++ }
++ }
++
++ } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state));
++
++ clear_bit(BFUSB_TX_PROCESS, &bfusb->state);
++}
++
++static void bfusb_tx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = (struct sk_buff *) urb->context;
++ struct bfusb *bfusb = (struct bfusb *) skb->dev;
++
++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
++
++ atomic_dec(&bfusb->pending_tx);
++
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ return;
++
++ if (!urb->status)
++ bfusb->hdev.stat.byte_tx += skb->len;
++ else
++ bfusb->hdev.stat.err_tx++;
++
++ read_lock(&bfusb->lock);
++
++ skb_unlink(skb);
++ skb_queue_tail(&bfusb->completed_q, skb);
++
++ bfusb_tx_wakeup(bfusb);
++
++ read_unlock(&bfusb->lock);
++}
++
++
++static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
++{
++ struct bfusb_scb *scb;
++ struct sk_buff *skb;
++ int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
++
++ BT_DBG("bfusb %p urb %p", bfusb, urb);
++
++ if (!urb && !(urb = usb_alloc_urb(0)))
++ return -ENOMEM;
++
++ if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) {
++ usb_free_urb(urb);
++ return -ENOMEM;
++ }
++
++ skb->dev = (void *) bfusb;
++
++ scb = (struct bfusb_scb *) skb->cb;
++ scb->urb = urb;
++
++ pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep);
++
++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, size,
++ bfusb_rx_complete, skb);
++
++ urb->transfer_flags = USB_QUEUE_BULK;
++
++ skb_queue_tail(&bfusb->pending_q, skb);
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk rx submit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ skb_unlink(skb);
++ kfree_skb(skb);
++ usb_free_urb(urb);
++ }
++
++ return err;
++}
++
++static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len)
++{
++ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len);
++
++ if (hdr & 0x10) {
++ BT_ERR("%s error in block", bfusb->hdev.name);
++ if (bfusb->reassembly)
++ kfree_skb(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ return -EIO;
++ }
++
++ if (hdr & 0x04) {
++ struct sk_buff *skb;
++ unsigned char pkt_type;
++ int pkt_len = 0;
++
++ if (bfusb->reassembly) {
++ BT_ERR("%s unexpected start block", bfusb->hdev.name);
++ kfree_skb(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ }
++
++ if (len < 1) {
++ BT_ERR("%s no packet type found", bfusb->hdev.name);
++ return -EPROTO;
++ }
++
++ pkt_type = *data++; len--;
++
++ switch (pkt_type) {
++ case HCI_EVENT_PKT:
++ if (len >= HCI_EVENT_HDR_SIZE) {
++ hci_event_hdr *hdr = (hci_event_hdr *) data;
++ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
++ } else {
++ BT_ERR("%s event block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++
++ case HCI_ACLDATA_PKT:
++ if (len >= HCI_ACL_HDR_SIZE) {
++ hci_acl_hdr *hdr = (hci_acl_hdr *) data;
++ pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
++ } else {
++ BT_ERR("%s data block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++
++ case HCI_SCODATA_PKT:
++ if (len >= HCI_SCO_HDR_SIZE) {
++ hci_sco_hdr *hdr = (hci_sco_hdr *) data;
++ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
++ } else {
++ BT_ERR("%s audio block is too short", bfusb->hdev.name);
++ return -EILSEQ;
++ }
++ break;
++ }
++
++ skb = bluez_skb_alloc(pkt_len, GFP_ATOMIC);
++ if (!skb) {
++ BT_ERR("%s no memory for the packet", bfusb->hdev.name);
++ return -ENOMEM;
++ }
++
++ skb->dev = (void *) &bfusb->hdev;
++ skb->pkt_type = pkt_type;
++
++ bfusb->reassembly = skb;
++ } else {
++ if (!bfusb->reassembly) {
++ BT_ERR("%s unexpected continuation block", bfusb->hdev.name);
++ return -EIO;
++ }
++ }
++
++ if (len > 0)
++ memcpy(skb_put(bfusb->reassembly, len), data, len);
++
++ if (hdr & 0x08) {
++ hci_recv_frame(bfusb->reassembly);
++ bfusb->reassembly = NULL;
++ }
++
++ return 0;
++}
++
++static void bfusb_rx_complete(struct urb *urb)
++{
++ struct sk_buff *skb = (struct sk_buff *) urb->context;
++ struct bfusb *bfusb = (struct bfusb *) skb->dev;
++ unsigned char *buf = urb->transfer_buffer;
++ int count = urb->actual_length;
++ int err, hdr, len;
++
++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
++
++ read_lock(&bfusb->lock);
++
++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++ goto unlock;
++
++ if (urb->status || !count)
++ goto resubmit;
++
++ bfusb->hdev.stat.byte_rx += count;
++
++ skb_put(skb, count);
++
++ while (count) {
++ hdr = buf[0] | (buf[1] << 8);
++
++ if (hdr & 0x4000) {
++ len = 0;
++ count -= 2;
++ buf += 2;
++ } else {
++ len = (buf[2] == 0) ? 256 : buf[2];
++ count -= 3;
++ buf += 3;
++ }
++
++ if (count < len) {
++ BT_ERR("%s block extends over URB buffer ranges",
++ bfusb->hdev.name);
++ }
++
++ if ((hdr & 0xe1) == 0xc1)
++ bfusb_recv_block(bfusb, hdr, buf, len);
++
++ count -= len;
++ buf += len;
++ }
++
++ skb_unlink(skb);
++ kfree_skb(skb);
++
++ bfusb_rx_submit(bfusb, urb);
++
++ read_unlock(&bfusb->lock);
++
++ return;
++
++resubmit:
++ urb->dev = bfusb->udev;
++
++ err = usb_submit_urb(urb);
++ if (err) {
++ BT_ERR("%s bulk resubmit failed urb %p err %d",
++ bfusb->hdev.name, urb, err);
++ }
++
++unlock:
++ read_unlock(&bfusb->lock);
++}
++
++
++static int bfusb_open(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++ unsigned long flags;
++ int i, err;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ MOD_INC_USE_COUNT;
++
++ write_lock_irqsave(&bfusb->lock, flags);
++
++ err = bfusb_rx_submit(bfusb, NULL);
++ if (!err) {
++ for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
++ bfusb_rx_submit(bfusb, NULL);
++ } else {
++ clear_bit(HCI_RUNNING, &hdev->flags);
++ MOD_DEC_USE_COUNT;
++ }
++
++ write_unlock_irqrestore(&bfusb->lock, flags);
++
++ return err;
++}
++
++static int bfusb_flush(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ skb_queue_purge(&bfusb->transmit_q);
++
++ return 0;
++}
++
++static int bfusb_close(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++ unsigned long flags;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
++ return 0;
++
++ write_lock_irqsave(&bfusb->lock, flags);
++
++ bfusb_unlink_urbs(bfusb);
++ bfusb_flush(hdev);
++
++ write_unlock_irqrestore(&bfusb->lock, flags);
++
++ MOD_DEC_USE_COUNT;
++
++ return 0;
++}
++
++static int bfusb_send_frame(struct sk_buff *skb)
++{
++ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
++ struct bfusb *bfusb;
++ struct sk_buff *nskb;
++ unsigned char buf[3];
++ int sent = 0, size, count;
++
++ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len);
++
++ if (!hdev) {
++ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
++ return -ENODEV;
++ }
++
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ return -EBUSY;
++
++ bfusb = (struct bfusb *) hdev->driver_data;
++
++ switch (skb->pkt_type) {
++ case HCI_COMMAND_PKT:
++ hdev->stat.cmd_tx++;
++ break;
++ case HCI_ACLDATA_PKT:
++ hdev->stat.acl_tx++;
++ break;
++ case HCI_SCODATA_PKT:
++ hdev->stat.sco_tx++;
++ break;
++ };
++
++ /* Prepend skb with frame type */
++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
++
++ count = skb->len;
++
++ /* Max HCI frame size seems to be 1511 + 1 */
++ if (!(nskb = bluez_skb_alloc(count + 32, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new packet");
++ return -ENOMEM;
++ }
++
++ nskb->dev = (void *) bfusb;
++
++ while (count) {
++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
++
++ buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
++ buf[1] = 0x00;
++ buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
++
++ memcpy(skb_put(nskb, 3), buf, 3);
++ memcpy(skb_put(nskb, size), skb->data + sent, size);
++
++ sent += size;
++ count -= size;
++ }
++
++ /* Don't send frame with multiple size of bulk max packet */
++ if ((nskb->len % bfusb->bulk_pkt_size) == 0) {
++ buf[0] = 0xdd;
++ buf[1] = 0x00;
++ memcpy(skb_put(nskb, 2), buf, 2);
++ }
++
++ read_lock(&bfusb->lock);
++
++ skb_queue_tail(&bfusb->transmit_q, nskb);
++ bfusb_tx_wakeup(bfusb);
++
++ read_unlock(&bfusb->lock);
++
++ kfree_skb(skb);
++
++ return 0;
++}
++
++static void bfusb_destruct(struct hci_dev *hdev)
++{
++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++
++ BT_DBG("hdev %p bfusb %p", hdev, bfusb);
++
++ kfree(bfusb);
++}
++
++static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
++{
++ return -ENOIOCTLCMD;
++}
++
++
++static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count)
++{
++ unsigned char *buf;
++ int err, pipe, len, size, sent = 0;
++
++ BT_DBG("bfusb %p udev %p firmware %p count %d", bfusb, bfusb->udev, firmware, count);
++
++ BT_INFO("BlueFRITZ! USB loading firmware");
++
++ if (usb_set_configuration(bfusb->udev, 1) < 0) {
++ BT_ERR("Can't change to loading configuration");
++ return -EBUSY;
++ }
++
++ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
++ if (!buf) {
++ BT_ERR("Can't allocate memory chunk for firmware");
++ return -ENOMEM;
++ }
++
++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
++
++ while (count) {
++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
++
++ memcpy(buf, firmware + sent, size);
++
++ err = usb_bulk_msg(bfusb->udev, pipe, buf, size,
++ &len, BFUSB_BLOCK_TIMEOUT);
++
++ if (err || (len != size)) {
++ BT_ERR("Error in firmware loading");
++ goto error;
++ }
++
++ sent += size;
++ count -= size;
++ }
++
++ if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0,
++ &len, BFUSB_BLOCK_TIMEOUT)) < 0) {
++ BT_ERR("Error in null packet request");
++ goto error;
++ }
++
++ if ((err = usb_set_configuration(bfusb->udev, 2)) < 0) {
++ BT_ERR("Can't change to running configuration");
++ goto error;
++ }
++
++ BT_INFO("BlueFRITZ! USB device ready");
++
++ kfree(buf);
++ return 0;
++
++error:
++ kfree(buf);
++
++ pipe = usb_sndctrlpipe(bfusb->udev, 0);
++
++ usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
++ 0, 0, 0, NULL, 0, BFUSB_BLOCK_TIMEOUT);
++
++ return err;
++}
++
++static void *bfusb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
++{
++ const struct firmware *firmware;
++ char device[16];
++ struct usb_interface *iface;
++ struct usb_interface_descriptor *iface_desc;
++ struct usb_endpoint_descriptor *bulk_out_ep;
++ struct usb_endpoint_descriptor *bulk_in_ep;
++ struct hci_dev *hdev;
++ struct bfusb *bfusb;
++
++ BT_DBG("udev %p ifnum %d id %p", udev, ifnum, id);
++
++ /* Check number of endpoints */
++ iface = &udev->actconfig->interface[0];
++ iface_desc = &iface->altsetting[0];
++
++ if (iface_desc->bNumEndpoints < 2)
++ return NULL;
++
++ bulk_out_ep = &iface_desc->endpoint[0];
++ bulk_in_ep = &iface_desc->endpoint[1];
++
++ if (!bulk_out_ep || !bulk_in_ep) {
++ BT_ERR("Bulk endpoints not found");
++ goto done;
++ }
++
++ /* Initialize control structure and load firmware */
++ if (!(bfusb = kmalloc(sizeof(struct bfusb), GFP_KERNEL))) {
++ BT_ERR("Can't allocate memory for control structure");
++ goto done;
++ }
++
++ memset(bfusb, 0, sizeof(struct bfusb));
++
++ bfusb->udev = udev;
++ bfusb->bulk_in_ep = bulk_in_ep->bEndpointAddress;
++ bfusb->bulk_out_ep = bulk_out_ep->bEndpointAddress;
++ bfusb->bulk_pkt_size = bulk_out_ep->wMaxPacketSize;
++
++ bfusb->lock = RW_LOCK_UNLOCKED;
++
++ bfusb->reassembly = NULL;
++
++ skb_queue_head_init(&bfusb->transmit_q);
++ skb_queue_head_init(&bfusb->pending_q);
++ skb_queue_head_init(&bfusb->completed_q);
++
++ snprintf(device, sizeof(device), "bfusb%3.3d%3.3d", udev->bus->busnum, udev->devnum);
++
++ if (request_firmware(&firmware, "bfubase.frm", device) < 0) {
++ BT_ERR("Firmware request failed");
++ goto error;
++ }
++
++ if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) {
++ BT_ERR("Firmware loading failed");
++ goto release;
++ }
++
++ release_firmware(firmware);
++
++ /* Initialize and register HCI device */
++ hdev = &bfusb->hdev;
++
++ hdev->type = HCI_USB;
++ hdev->driver_data = bfusb;
++
++ hdev->open = bfusb_open;
++ hdev->close = bfusb_close;
++ hdev->flush = bfusb_flush;
++ hdev->send = bfusb_send_frame;
++ hdev->destruct = bfusb_destruct;
++ hdev->ioctl = bfusb_ioctl;
++
++ if (hci_register_dev(hdev) < 0) {
++ BT_ERR("Can't register HCI device");
++ goto error;
++ }
++
++ return bfusb;
++
++release:
++ release_firmware(firmware);
++
++error:
++ kfree(bfusb);
++
++done:
++ return NULL;
++}
++
++static void bfusb_disconnect(struct usb_device *udev, void *ptr)
++{
++ struct bfusb *bfusb = (struct bfusb *) ptr;
++ struct hci_dev *hdev = &bfusb->hdev;
++
++ BT_DBG("udev %p ptr %p", udev, ptr);
++
++ if (!hdev)
++ return;
++
++ bfusb_close(hdev);
++
++ if (hci_unregister_dev(hdev) < 0)
++ BT_ERR("Can't unregister HCI device %s", hdev->name);
++}
++
++static struct usb_driver bfusb_driver = {
++ name: "bfusb",
++ probe: bfusb_probe,
++ disconnect: bfusb_disconnect,
++ id_table: bfusb_table,
++};
++
++static int __init bfusb_init(void)
++{
++ int err;
++
++ BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
++ BT_INFO("Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>");
++
++ if ((err = usb_register(&bfusb_driver)) < 0)
++ BT_ERR("Failed to register BlueFRITZ! USB driver");
++
++ return err;
++}
++
++static void __exit bfusb_cleanup(void)
++{
++ usb_deregister(&bfusb_driver);
++}
++
++module_init(bfusb_init);
++module_exit(bfusb_cleanup);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
++MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/bluetooth/bluecard_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/bluecard_cs.c
+@@ -803,6 +803,9 @@
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ bluecard_hci_close(hdev);
+
+ clear_bit(CARD_READY, &(info->hw_state));
+--- linux-2.4.21/drivers/bluetooth/bt3c_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/bt3c_cs.c
+@@ -24,8 +24,6 @@
+ #include <linux/config.h>
+ #include <linux/module.h>
+
+-#define __KERNEL_SYSCALLS__
+-
+ #include <linux/kernel.h>
+ #include <linux/kmod.h>
+ #include <linux/init.h>
+@@ -48,6 +46,8 @@
+ #include <asm/bitops.h>
+ #include <asm/io.h>
+
++#include <linux/firmware.h>
++
+ #include <pcmcia/version.h>
+ #include <pcmcia/cs_types.h>
+ #include <pcmcia/cs.h>
+@@ -485,78 +485,101 @@
+
+
+
+-/* ======================== User mode firmware loader ======================== */
++/* ======================== Card services HCI interaction ======================== */
+
+
+-#define FW_LOADER "/sbin/bluefw"
+-static int errno;
++static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int count)
++{
++ char *ptr = (char *) firmware;
++ char b[9];
++ unsigned int iobase, size, addr, fcs, tmp;
++ int i, err = 0;
+
++ iobase = info->link.io.BasePort1;
+
+-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;
++ /* Reset */
+
+- 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);
++ bt3c_io_write(iobase, 0x8040, 0x0404);
++ bt3c_io_write(iobase, 0x8040, 0x0400);
+
+- return err;
+-}
++ udelay(1);
+
++ bt3c_io_write(iobase, 0x8040, 0x0404);
+
+-static int bt3c_firmware_load(bt3c_info_t *info)
+-{
+- sigset_t tmpsig;
+- char dev[16];
+- pid_t pid;
+- int result;
++ udelay(17);
+
+- /* Check if root fs is mounted */
+- if (!current->fs->root) {
+- printk(KERN_WARNING "bt3c_cs: Root filesystem is not mounted.\n");
+- return -EPERM;
+- }
++ /* Load */
+
+- sprintf(dev, "%04x", info->link.io.BasePort1);
++ while (count) {
++ if (ptr[0] != 'S') {
++ printk(KERN_WARNING "bt3c_cs: Bad address in firmware.\n");
++ err = -EFAULT;
++ goto error;
++ }
+
+- 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;
+- }
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + 2, 2);
++ size = simple_strtol(b, NULL, 16);
+
+- /* 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);
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + 4, 8);
++ addr = simple_strtol(b, NULL, 16);
+
+- result = waitpid(pid, NULL, __WCLONE);
++ memset(b, 0, sizeof(b));
++ memcpy(b, ptr + (size * 2) + 2, 2);
++ fcs = simple_strtol(b, NULL, 16);
+
+- /* Allow signals again */
+- spin_lock_irq(&current->sigmask_lock);
+- current->blocked = tmpsig;
+- recalc_sigpending(current);
+- spin_unlock_irq(&current->sigmask_lock);
++ memset(b, 0, sizeof(b));
++ for (tmp = 0, i = 0; i < size; i++) {
++ memcpy(b, ptr + (i * 2) + 2, 2);
++ tmp += simple_strtol(b, NULL, 16);
++ }
+
+- if (result != pid) {
+- printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result);
+- return -result;
++ if (((tmp + fcs) & 0xff) != 0xff) {
++ printk(KERN_WARNING "bt3c_cs: Checksum error in firmware.\n");
++ err = -EILSEQ;
++ goto error;
++ }
++
++ if (ptr[1] == '3') {
++ bt3c_address(iobase, addr);
++
++ memset(b, 0, sizeof(b));
++ for (i = 0; i < (size - 4) / 2; i++) {
++ memcpy(b, ptr + (i * 4) + 12, 4);
++ tmp = simple_strtol(b, NULL, 16);
++ bt3c_put(iobase, tmp);
++ }
++ }
++
++ ptr += (size * 2) + 6;
++ count -= (size * 2) + 6;
+ }
+
+- return 0;
+-}
++ udelay(17);
+
++ /* Boot */
+
++ bt3c_address(iobase, 0x3000);
++ outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
+
+-/* ======================== Card services HCI interaction ======================== */
++error:
++ udelay(17);
++
++ /* Clear */
++
++ bt3c_io_write(iobase, 0x7006, 0x0000);
++ bt3c_io_write(iobase, 0x7005, 0x0000);
++ bt3c_io_write(iobase, 0x7001, 0x0000);
++
++ return err;
++}
+
+
+ int bt3c_open(bt3c_info_t *info)
+ {
++ const struct firmware *firmware;
++ char device[16];
+ struct hci_dev *hdev;
+ int err;
+
+@@ -570,8 +593,22 @@
+
+ /* Load firmware */
+
+- if ((err = bt3c_firmware_load(info)) < 0)
++ snprintf(device, sizeof(device), "bt3c%4.4x", info->link.io.BasePort1);
++
++ err = request_firmware(&firmware, "BT3CPCC.bin", device);
++ if (err < 0) {
++ printk(KERN_WARNING "bt3c_cs: Firmware request failed.\n");
+ return err;
++ }
++
++ err = bt3c_load_firmware(info, firmware->data, firmware->size);
++
++ release_firmware(firmware);
++
++ if (err < 0) {
++ printk(KERN_WARNING "bt3c_cs: Firmware loading failed.\n");
++ return err;
++ }
+
+ /* Timeout before it is safe to send the first HCI packet */
+
+@@ -606,6 +643,9 @@
+ {
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ bt3c_hci_close(hdev);
+
+ if (hci_unregister_dev(hdev) < 0)
+--- linux-2.4.21/drivers/bluetooth/btuart_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/btuart_cs.c
+@@ -556,6 +556,9 @@
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ btuart_hci_close(hdev);
+
+ spin_lock_irqsave(&(info->lock), flags);
+--- linux-2.4.21/drivers/bluetooth/dtl1_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/dtl1_cs.c
+@@ -535,6 +535,9 @@
+ unsigned int iobase = info->link.io.BasePort1;
+ struct hci_dev *hdev = &(info->hdev);
+
++ if (info->link.state & DEV_CONFIG_PENDING)
++ return -ENODEV;
++
+ dtl1_hci_close(hdev);
+
+ spin_lock_irqsave(&(info->lock), flags);
+--- linux-2.4.21/drivers/bluetooth/hci_bcsp.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_bcsp.c
+@@ -34,7 +34,6 @@
+ #include <linux/module.h>
+
+ #include <linux/version.h>
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
+@@ -635,7 +634,8 @@
+ struct sk_buff *skb;
+ unsigned long flags;
+
+- BT_ERR("Timeout, retransmitting %u pkts", bcsp->unack.qlen);
++ BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
++
+ spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+ while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
+--- linux-2.4.21/drivers/bluetooth/hci_ldisc.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_ldisc.c
+@@ -33,7 +33,6 @@
+ #include <linux/module.h>
+
+ #include <linux/version.h>
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
+--- linux-2.4.21/drivers/bluetooth/hci_uart.h~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_uart.h
+@@ -35,11 +35,12 @@
+ #define HCIUARTGETPROTO _IOR('U', 201, int)
+
+ /* UART protocols */
+-#define HCI_UART_MAX_PROTO 3
++#define HCI_UART_MAX_PROTO 4
+
+ #define HCI_UART_H4 0
+ #define HCI_UART_BCSP 1
+-#define HCI_UART_NCSP 2
++#define HCI_UART_3WIRE 2
++#define HCI_UART_H4DS 3
+
+ #ifdef __KERNEL__
+ struct hci_uart;
+--- linux-2.4.21/drivers/bluetooth/hci_usb.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_usb.c
+@@ -30,7 +30,7 @@
+ *
+ * $Id$
+ */
+-#define VERSION "2.4"
++#define VERSION "2.7"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -62,7 +62,7 @@
+ #define BT_DMP( A... )
+ #endif
+
+-#ifndef CONFIG_BLUEZ_USB_ZERO_PACKET
++#ifndef CONFIG_BLUEZ_HCIUSB_ZERO_PACKET
+ #undef USB_ZERO_PACKET
+ #define USB_ZERO_PACKET 0
+ #endif
+@@ -73,20 +73,39 @@
+ /* 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) },
++ /* AVM BlueFRITZ! USB v2.0 */
++ { USB_DEVICE(0x057c, 0x3800) },
+
+ /* Bluetooth Ultraport Module from IBM */
+ { USB_DEVICE(0x04bf, 0x030a) },
+
++ /* ALPS Modules with non-standard id */
++ { USB_DEVICE(0x044e, 0x3001) },
++ { USB_DEVICE(0x044e, 0x3002) },
++
++ /* Ericsson with non-standard id */
++ { USB_DEVICE(0x0bdb, 0x1002) },
++
+ { } /* Terminating entry */
+ };
+
+ MODULE_DEVICE_TABLE (usb, bluetooth_ids);
+
+-static struct usb_device_id ignore_ids[] = {
++static struct usb_device_id blacklist_ids[] = {
+ /* Broadcom BCM2033 without firmware */
+- { USB_DEVICE(0x0a5c, 0x2033) },
++ { USB_DEVICE(0x0a5c, 0x2033), driver_info: HCI_IGNORE },
++
++ /* Broadcom BCM2035 */
++ { USB_DEVICE(0x0a5c, 0x200a), driver_info: HCI_RESET },
++
++ /* ISSC Bluetooth Adapter v3.1 */
++ { USB_DEVICE(0x1131, 0x1001), driver_info: HCI_RESET },
++
++ /* Digianswer device */
++ { USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER },
++
++ /* RTX Telecom based adapter with buggy SCO support */
++ { USB_DEVICE(0x0400, 0x0807), driver_info: HCI_BROKEN_ISOC },
+
+ { } /* Terminating entry */
+ };
+@@ -133,6 +152,7 @@
+ return _urb_dequeue(__completed_q(husb, type));
+ }
+
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
+ {
+ int offset = 0, i;
+@@ -152,6 +172,7 @@
+ }
+ urb->number_of_packets = i;
+ }
++#endif
+
+ static int hci_usb_intr_rx_submit(struct hci_usb *husb)
+ {
+@@ -229,7 +250,7 @@
+ return err;
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
+ {
+ struct _urb *_urb;
+@@ -300,8 +321,10 @@
+ for (i = 0; i < HCI_MAX_BULK_RX; i++)
+ hci_usb_bulk_rx_submit(husb);
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
+- hci_usb_isoc_rx_submit(husb);
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (husb->isoc_iface)
++ for (i = 0; i < HCI_MAX_ISOC_RX; i++)
++ hci_usb_isoc_rx_submit(husb);
+ #endif
+ } else {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+@@ -426,7 +449,7 @@
+ } else
+ dr = (void *) _urb->urb.setup_packet;
+
+- dr->bRequestType = HCI_CTRL_REQ;
++ dr->bRequestType = husb->ctrl_req;
+ dr->bRequest = 0;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+@@ -467,7 +490,7 @@
+ return __tx_submit(husb, _urb);
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
+ {
+ struct _urb *_urb = __get_completed(husb, skb->pkt_type);
+@@ -518,10 +541,10 @@
+ skb_queue_head(q, skb);
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ /* Process SCO queue */
+ q = __transmit_q(husb, HCI_SCODATA_PKT);
+- if (!atomic_read(__pending_tx(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);
+@@ -577,7 +600,7 @@
+ hdev->stat.acl_tx++;
+ break;
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+@@ -627,7 +650,7 @@
+ } else
+ return -EILSEQ;
+ break;
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ case HCI_SCODATA_PKT:
+ if (count >= HCI_SCO_HDR_SIZE) {
+ hci_sco_hdr *h = data;
+@@ -638,7 +661,7 @@
+ #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);
+@@ -683,16 +706,16 @@
+ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,
+ _urb->type, urb->status, count, urb->transfer_flags);
+
+- if (!test_bit(HCI_RUNNING, &hdev->flags))
+- return;
+-
+ read_lock(&husb->completion_lock);
+
++ if (!test_bit(HCI_RUNNING, &hdev->flags))
++ goto unlock;
++
+ if (urb->status || !count)
+ goto resubmit;
+
+ if (_urb->type == HCI_SCODATA_PKT) {
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ int i;
+ for (i=0; i < urb->number_of_packets; i++) {
+ BT_DBG("desc %d status %d offset %d len %d", i,
+@@ -724,6 +747,8 @@
+ BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,
+ _urb->type, err);
+ }
++
++unlock:
+ read_unlock(&husb->completion_lock);
+ }
+
+@@ -786,8 +811,14 @@
+
+ iface = &udev->actconfig->interface[0];
+
+- /* Check our black list */
+- if (usb_match_id(udev, iface, ignore_ids))
++ if (!id->driver_info) {
++ const struct usb_device_id *match;
++ match = usb_match_id(udev, iface, blacklist_ids);
++ if (match)
++ id = match;
++ }
++
++ if (id->driver_info & HCI_IGNORE)
+ return NULL;
+
+ /* Check number of endpoints */
+@@ -827,9 +858,9 @@
+ bulk_out_ep[i] = ep;
+ break;
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ case USB_ENDPOINT_XFER_ISOC:
+- if (ep->wMaxPacketSize < size)
++ if (ep->wMaxPacketSize < size || a > 2)
+ break;
+ size = ep->wMaxPacketSize;
+
+@@ -853,8 +884,8 @@
+ goto done;
+ }
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
+- if (!isoc_in_ep[1] || !isoc_out_ep[1]) {
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
++ if (id->driver_info & HCI_BROKEN_ISOC || !isoc_in_ep[1] || !isoc_out_ep[1]) {
+ BT_DBG("Isoc endpoints not found");
+ isoc_iface = NULL;
+ }
+@@ -872,7 +903,12 @@
+ husb->bulk_in_ep = bulk_in_ep[0];
+ husb->intr_in_ep = intr_in_ep[0];
+
+-#ifdef CONFIG_BLUEZ_USB_SCO
++ if (id->driver_info & HCI_DIGIANSWER)
++ husb->ctrl_req = HCI_DIGI_REQ;
++ else
++ husb->ctrl_req = HCI_CTRL_REQ;
++
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ if (isoc_iface) {
+ BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);
+ if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
+@@ -906,6 +942,9 @@
+ hdev->send = hci_usb_send_frame;
+ hdev->destruct = hci_usb_destruct;
+
++ if (id->driver_info & HCI_RESET)
++ set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
++
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ goto probe_error;
+@@ -968,6 +1007,6 @@
+ module_init(hci_usb_init);
+ module_exit(hci_usb_cleanup);
+
+-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
+ MODULE_DESCRIPTION("BlueZ HCI USB driver ver " VERSION);
+ MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/bluetooth/hci_usb.h~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_usb.h
+@@ -35,12 +35,21 @@
+ #define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
+
+ #define HCI_CTRL_REQ 0x20
++#define HCI_DIGI_REQ 0x40
++
++#define HCI_IGNORE 0x01
++#define HCI_RESET 0x02
++#define HCI_DIGIANSWER 0x04
++#define HCI_BROKEN_ISOC 0x08
+
+ #define HCI_MAX_IFACE_NUM 3
+
+ #define HCI_MAX_BULK_TX 4
+ #define HCI_MAX_BULK_RX 1
+
++#define HCI_MAX_ISOC_RX 2
++#define HCI_MAX_ISOC_TX 2
++
+ #define HCI_MAX_ISOC_FRAMES 10
+
+ struct _urb_queue {
+@@ -119,6 +128,8 @@
+ struct usb_endpoint_descriptor *isoc_out_ep;
+ struct usb_endpoint_descriptor *isoc_in_ep;
+
++ __u8 ctrl_req;
++
+ struct sk_buff_head transmit_q[4];
+ struct sk_buff *reassembly[4]; // Reassembly buffers
+
--- linux-2.4.21/drivers/char/Config.in~i2c-ds1337
+++ linux-2.4.21/drivers/char/Config.in
@@ -164,6 +164,7 @@
@@ -6736,7 +8647,7 @@
static void change_speed(struct async_struct *info, struct termios *old);
static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
-@@ -325,46 +165,82 @@
+@@ -325,46 +165,86 @@
{ 0, 0}
};
@@ -6820,12 +8731,16 @@
+ flags: ASYNC_SKIP_TEST,
+ }, {
+ type: PORT_UNKNOWN,
++ baud_base: 115200,
+ }, {
+ type: PORT_UNKNOWN,
++ baud_base: 115200,
+ }, {
+ type: PORT_UNKNOWN,
++ baud_base: 115200,
+ }, {
+ type: PORT_UNKNOWN,
++ baud_base: 115200,
+ }
};
@@ -6856,7 +8771,7 @@
#ifndef PREPARE_FUNC
#define PREPARE_FUNC(dev) (dev->prepare)
-@@ -403,39 +279,21 @@
+@@ -403,39 +283,21 @@
#endif
@@ -6905,7 +8820,7 @@
return readl((unsigned long) info->iomem_base +
(offset<<info->iomem_reg_shift));
default:
-@@ -447,17 +305,14 @@
+@@ -447,17 +309,14 @@
int value)
{
switch (info->io_type) {
@@ -6926,7 +8841,7 @@
writel(value, (unsigned long) info->iomem_base +
(offset<<info->iomem_reg_shift));
break;
-@@ -509,9 +364,6 @@
+@@ -509,9 +368,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
@@ -6936,7 +8851,7 @@
save_flags(flags); cli();
if (info->IER & UART_IER_THRI) {
info->IER &= ~UART_IER_THRI;
-@@ -529,9 +381,6 @@
+@@ -529,9 +385,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
@@ -6946,7 +8861,7 @@
save_flags(flags); cli();
if (info->xmit.head != info->xmit.tail
&& info->xmit.buf
-@@ -689,11 +538,7 @@
+@@ -689,11 +542,7 @@
#endif
*status = serial_inp(info, UART_LSR);
} while ((*status & UART_LSR_DR) && (max_count-- > 0));
@@ -6958,7 +8873,7 @@
}
static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
-@@ -758,11 +603,6 @@
+@@ -758,11 +607,6 @@
icount->dsr++;
if (status & UART_MSR_DDCD) {
icount->dcd++;
@@ -6970,7 +8885,7 @@
}
if (status & UART_MSR_DCTS)
icount->cts++;
-@@ -810,120 +650,23 @@
+@@ -810,120 +654,23 @@
}
}
@@ -7092,7 +9007,7 @@
do {
status = serial_inp(info, UART_LSR);
#ifdef SERIAL_DEBUG_INTR
-@@ -932,120 +675,23 @@
+@@ -932,120 +679,23 @@
if (status & UART_LSR_DR)
receive_chars(info, &status, regs);
check_modem_status(info);
@@ -7217,7 +9132,7 @@
/*
* -------------------------------------------------------------------
-@@ -1107,22 +753,6 @@
+@@ -1107,22 +757,6 @@
if (!info)
continue;
save_flags(flags); cli();
@@ -7240,7 +9155,7 @@
rs_interrupt_single(i, NULL, NULL);
restore_flags(flags);
}
-@@ -1132,11 +762,7 @@
+@@ -1132,11 +766,7 @@
if (IRQ_ports[0]) {
save_flags(flags); cli();
@@ -7252,7 +9167,7 @@
restore_flags(flags);
mod_timer(&serial_timer, jiffies + IRQ_timeout[0]);
-@@ -1177,50 +803,6 @@
+@@ -1177,50 +807,6 @@
IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1;
}
@@ -7303,7 +9218,7 @@
static int startup(struct async_struct * info)
{
unsigned long flags;
-@@ -1228,9 +810,6 @@
+@@ -1228,9 +814,6 @@
void (*handler)(int, void *, struct pt_regs *);
struct serial_state *state= info->state;
unsigned long page;
@@ -7313,7 +9228,7 @@
page = get_zeroed_page(GFP_KERNEL);
if (!page)
-@@ -1258,6 +837,22 @@
+@@ -1258,6 +841,22 @@
printk("starting up ttys%d (irq %d)...", info->line, state->irq);
#endif
@@ -7336,7 +9251,7 @@
if (uart_config[state->type].flags & UART_STARTECH) {
/* Wake up UART */
serial_outp(info, UART_LCR, 0xBF);
-@@ -1305,25 +900,12 @@
+@@ -1305,25 +904,12 @@
serial_outp(info, UART_LCR, 0);
}
@@ -7363,7 +9278,7 @@
case (long)&STUART: CKEN |= CKEN5_STUART; break;
}
}
-@@ -1344,6 +926,7 @@
+@@ -1344,6 +930,7 @@
/*
* Clear the interrupt registers.
*/
@@ -7371,7 +9286,7 @@
(void) serial_inp(info, UART_LSR);
(void) serial_inp(info, UART_RX);
(void) serial_inp(info, UART_IIR);
-@@ -1371,18 +954,8 @@
+@@ -1371,18 +958,8 @@
if (state->irq && (!IRQ_ports[state->irq] ||
!IRQ_ports[state->irq]->next_port)) {
if (IRQ_ports[state->irq]) {
@@ -7390,7 +9305,7 @@
} else
handler = rs_interrupt_single;
-@@ -1417,12 +990,6 @@
+@@ -1417,12 +994,6 @@
info->MCR = 0;
if (info->tty->termios->c_cflag & CBAUD)
info->MCR = UART_MCR_DTR | UART_MCR_RTS;
@@ -7403,7 +9318,7 @@
{
if (state->irq != 0)
info->MCR |= UART_MCR_OUT2;
-@@ -1437,18 +1004,9 @@
+@@ -1437,18 +1008,9 @@
*/
info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
if (pxa_port(state->type))
@@ -7423,7 +9338,7 @@
/*
* And clear the interrupt registers again for luck.
*/
-@@ -1469,7 +1027,6 @@
+@@ -1469,7 +1031,6 @@
/*
* Set up the tty->alt_speed kludge
*/
@@ -7431,7 +9346,7 @@
if (info->tty) {
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
info->tty->alt_speed = 57600;
-@@ -1480,7 +1037,6 @@
+@@ -1480,7 +1041,6 @@
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
info->tty->alt_speed = 460800;
}
@@ -7439,7 +9354,7 @@
/*
* and set the speed of the serial port
-@@ -1516,6 +1072,30 @@
+@@ -1516,6 +1076,30 @@
state->irq);
#endif
@@ -7470,7 +9385,7 @@
save_flags(flags); cli(); /* Disable interrupts */
/*
-@@ -1561,13 +1141,6 @@
+@@ -1561,13 +1145,6 @@
info->IER = 0;
serial_outp(info, UART_IER, 0x00); /* disable all intrs */
@@ -7484,7 +9399,7 @@
info->MCR &= ~UART_MCR_OUT2;
if (pxa_buggy_port(state->type))
info->MCR ^= UART_MCR_OUT2;
-@@ -1586,16 +1159,6 @@
+@@ -1586,16 +1163,6 @@
UART_FCR_CLEAR_XMIT));
serial_outp(info, UART_FCR, 0);
@@ -7501,7 +9416,7 @@
#ifdef CONFIG_ARCH_PXA
if (state->type == PORT_PXA
#ifdef CONFIG_SERIAL_CONSOLE
-@@ -1634,37 +1197,6 @@
+@@ -1634,37 +1201,6 @@
restore_flags(flags);
}
@@ -7539,7 +9454,7 @@
/*
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
-@@ -1711,12 +1243,6 @@
+@@ -1711,12 +1247,6 @@
baud = tty_get_baud_rate(info->tty);
if (!baud)
baud = 9600; /* B0 transition handled in rs_set_termios */
@@ -7552,7 +9467,7 @@
baud_base = info->state->baud_base;
if (info->state->type == PORT_16C950) {
if (baud <= baud_base)
-@@ -1778,10 +1304,6 @@
+@@ -1778,10 +1308,6 @@
if (uart_config[info->state->type].flags & UART_USE_FIFO) {
if ((info->state->baud_base / quot) < 2400)
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
@@ -7563,7 +9478,7 @@
else
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
}
-@@ -1864,9 +1386,6 @@
+@@ -1864,9 +1390,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
@@ -7573,7 +9488,7 @@
if (!tty || !info->xmit.buf)
return;
-@@ -1888,9 +1407,6 @@
+@@ -1888,9 +1411,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
@@ -7583,7 +9498,7 @@
if (info->xmit.head == info->xmit.tail
|| tty->stopped
|| tty->hw_stopped
-@@ -1900,8 +1416,6 @@
+@@ -1900,8 +1420,6 @@
save_flags(flags); cli();
info->IER |= UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
@@ -7592,7 +9507,7 @@
restore_flags(flags);
}
-@@ -1912,9 +1426,6 @@
+@@ -1912,9 +1430,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
@@ -7602,7 +9517,7 @@
if (!tty || !info->xmit.buf || !tmp_buf)
return 0;
-@@ -1978,11 +1489,6 @@
+@@ -1978,11 +1493,6 @@
&& !(info->IER & UART_IER_THRI)) {
info->IER |= UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
@@ -7614,7 +9529,7 @@
}
return ret;
}
-@@ -1991,8 +1497,6 @@
+@@ -1991,8 +1501,6 @@
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
@@ -7623,7 +9538,7 @@
return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
}
-@@ -2000,8 +1504,6 @@
+@@ -2000,8 +1508,6 @@
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
@@ -7632,7 +9547,7 @@
return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
}
-@@ -2010,8 +1512,6 @@
+@@ -2010,8 +1516,6 @@
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
@@ -7641,7 +9556,7 @@
save_flags(flags); cli();
info->xmit.head = info->xmit.tail = 0;
restore_flags(flags);
-@@ -2032,16 +1532,11 @@
+@@ -2032,16 +1536,11 @@
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
@@ -7658,7 +9573,7 @@
}
}
-@@ -2064,9 +1559,6 @@
+@@ -2064,9 +1563,6 @@
tty->ldisc.chars_in_buffer(tty));
#endif
@@ -7668,7 +9583,7 @@
if (I_IXOFF(tty))
rs_send_xchar(tty, STOP_CHAR(tty));
-@@ -2089,9 +1581,6 @@
+@@ -2089,9 +1585,6 @@
tty->ldisc.chars_in_buffer(tty));
#endif
@@ -7678,7 +9593,7 @@
if (I_IXOFF(tty)) {
if (info->x_char)
info->x_char = 0;
-@@ -2134,7 +1623,6 @@
+@@ -2134,7 +1627,6 @@
tmp.close_delay = state->close_delay;
tmp.closing_wait = state->closing_wait;
tmp.custom_divisor = state->custom_divisor;
@@ -7686,7 +9601,7 @@
tmp.io_type = state->io_type;
if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
return -EFAULT;
-@@ -2160,8 +1648,7 @@
+@@ -2160,8 +1652,7 @@
new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
change_irq = new_serial.irq != state->irq;
@@ -7696,7 +9611,7 @@
if (!capable(CAP_SYS_ADMIN)) {
if (change_irq || change_port ||
-@@ -2198,7 +1685,6 @@
+@@ -2198,7 +1689,6 @@
if (new_serial.type) {
for (i = 0 ; i < NR_PORTS; i++)
if ((state != &rs_table[i]) &&
@@ -7704,7 +9619,7 @@
(rs_table[i].port == new_port) &&
rs_table[i].type)
return -EADDRINUSE;
-@@ -2220,18 +1706,11 @@
+@@ -2220,18 +1710,11 @@
state->custom_divisor = new_serial.custom_divisor;
state->close_delay = new_serial.close_delay * HZ/100;
state->closing_wait = new_serial.closing_wait * HZ/100;
@@ -7723,7 +9638,7 @@
release_region(state->port,8);
}
state->type = new_serial.type;
-@@ -2243,31 +1722,19 @@
+@@ -2243,31 +1726,19 @@
shutdown(info);
state->irq = new_serial.irq;
info->port = state->port = new_port;
@@ -7756,7 +9671,7 @@
if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
info->tty->alt_speed = 57600;
if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-@@ -2276,7 +1743,6 @@
+@@ -2276,7 +1747,6 @@
info->tty->alt_speed = 230400;
if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
info->tty->alt_speed = 460800;
@@ -7764,7 +9679,7 @@
change_speed(info, 0);
}
} else
-@@ -2414,60 +1880,14 @@
+@@ -2414,60 +1884,14 @@
return 0;
}
@@ -7825,7 +9740,7 @@
if (!CONFIGURED_SERIAL_PORT(info))
return;
save_flags(flags); cli();
-@@ -2478,121 +1898,6 @@
+@@ -2478,121 +1902,6 @@
serial_out(info, UART_LCR, info->LCR);
restore_flags(flags);
}
@@ -7947,7 +9862,7 @@
static int rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
-@@ -2601,12 +1906,6 @@
+@@ -2601,12 +1910,6 @@
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct icount;
unsigned long flags;
@@ -7960,7 +9875,7 @@
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
-@@ -2616,45 +1915,6 @@
+@@ -2616,45 +1919,6 @@
}
switch (cmd) {
@@ -8006,7 +9921,7 @@
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
-@@ -2667,9 +1927,6 @@
+@@ -2667,9 +1931,6 @@
case TIOCSSERIAL:
return set_serial_info(info,
(struct serial_struct *) arg);
@@ -8016,7 +9931,7 @@
case TIOCSERGETLSR: /* Get line status register */
return get_lsr_info(info, (unsigned int *) arg);
-@@ -2679,15 +1936,6 @@
+@@ -2679,15 +1940,6 @@
return -EFAULT;
return 0;
@@ -8032,7 +9947,7 @@
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
-@@ -2754,6 +2002,39 @@
+@@ -2754,6 +2006,39 @@
printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
return 0;
@@ -8072,7 +9987,7 @@
default:
return -ENOIOCTLCMD;
}
-@@ -2801,18 +2082,6 @@
+@@ -2801,18 +2086,6 @@
tty->hw_stopped = 0;
rs_start(tty);
}
@@ -8091,7 +10006,7 @@
}
/*
-@@ -2831,9 +2100,6 @@
+@@ -2831,9 +2104,6 @@
struct serial_state *state;
unsigned long flags;
@@ -8101,7 +10016,7 @@
state = info->state;
save_flags(flags); cli();
-@@ -2933,10 +2199,7 @@
+@@ -2933,10 +2203,7 @@
{
struct async_struct * info = (struct async_struct *)tty->driver_data;
unsigned long orig_jiffies, char_time;
@@ -8113,7 +10028,7 @@
if (info->state->type == PORT_UNKNOWN)
return;
-@@ -2974,9 +2237,11 @@
+@@ -2974,9 +2241,11 @@
printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
printk("jiff=%lu...", jiffies);
#endif
@@ -8126,7 +10041,7 @@
#endif
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(char_time);
-@@ -2986,8 +2251,9 @@
+@@ -2986,8 +2255,9 @@
break;
}
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
@@ -8137,7 +10052,7 @@
}
/*
-@@ -2998,9 +2264,6 @@
+@@ -2998,9 +2268,6 @@
struct async_struct * info = (struct async_struct *)tty->driver_data;
struct serial_state *state = info->state;
@@ -8147,7 +10062,7 @@
state = info->state;
rs_flush_buffer(tty);
-@@ -3036,12 +2299,8 @@
+@@ -3036,12 +2303,8 @@
(info->flags & ASYNC_CLOSING)) {
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
@@ -8160,7 +10075,7 @@
}
/*
-@@ -3114,14 +2373,10 @@
+@@ -3114,14 +2377,10 @@
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(info->flags & ASYNC_INITIALIZED)) {
@@ -8175,7 +10090,7 @@
break;
}
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
-@@ -3223,16 +2478,12 @@
+@@ -3223,16 +2482,12 @@
}
tty->driver_data = info;
info->tty = tty;
@@ -8192,7 +10107,7 @@
/*
* This relies on lock_kernel() stuff so wants tidying for 2.5
-@@ -3254,12 +2505,8 @@
+@@ -3254,12 +2509,8 @@
(info->flags & ASYNC_CLOSING)) {
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
@@ -8205,7 +10120,7 @@
}
/*
-@@ -3313,17 +2560,14 @@
+@@ -3313,17 +2564,14 @@
int ret;
unsigned long flags;
@@ -8229,7 +10144,7 @@
/*
* Figure out the current RS-232 lines
-@@ -3334,7 +2578,6 @@
+@@ -3334,7 +2582,6 @@
info->magic = SERIAL_MAGIC;
info->port = state->port;
info->flags = state->flags;
@@ -8237,7 +10152,7 @@
info->io_type = state->io_type;
info->iomem_base = state->iomem_base;
info->iomem_reg_shift = state->iomem_reg_shift;
-@@ -3389,13 +2632,13 @@
+@@ -3389,13 +2636,13 @@
}
static int rs_read_proc(char *page, char **start, off_t off, int count,
@@ -8254,7 +10169,7 @@
for (i = 0; i < NR_PORTS && len < 4000; i++) {
l = line_info(page + len, &rs_table[i]);
len += l;
-@@ -3423,125 +2666,212 @@
+@@ -3423,125 +2670,212 @@
*/
/*
@@ -8573,7 +10488,7 @@
}
/*
-@@ -3894,1760 +3224,8 @@
+@@ -3894,1762 +3228,10 @@
restore_flags(flags);
}
@@ -8827,15 +10742,15 @@
- p = ioremap(pci_resource_start(dev, 0), 0x80);
- writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c);
- iounmap(p);
--
+
- if (!enable)
- pci_write_config_byte(dev, PCI_COMMAND,
- data & ~pci_config);
- return 0;
-}
--
--
--/*
+
+
+ /*
- * SIIG serial cards have an PCI interface chip which also controls
- * the UART clocking frequency. Each UART can be clocked independently
- * (except cards equiped with 4 UARTs) and initial clocking settings
@@ -9194,7 +11109,7 @@
-#endif
- { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */
- 0, 0, pci_xircom_fn },
-
+-
- { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */
- 0, 0, pci_siig10x_fn },
- { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */
@@ -9209,7 +11124,7 @@
- 0, 0, pci_siig20x_fn },
- { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */
- 0, 0, pci_siig20x_fn },
-
+-
- { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */
- 0x40, 2, NULL, 0x200 },
- { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */
@@ -10331,10 +12246,12 @@
- rs_table[i].closing_wait = req->closing_wait;
- return(0);
-}
-
- /*
+-
+-/*
* register_serial and unregister_serial allows for 16x50 serial ports to be
-@@ -5734,11 +3312,8 @@
+ * configured at run-time, to support PCMCIA modems.
+ */
+@@ -5734,11 +3316,8 @@
}
restore_flags(flags);
@@ -10347,7 +12264,7 @@
state->iomem_base ? "iomem" : "port",
state->iomem_base ? (unsigned long)state->iomem_base :
state->port, state->irq, uart_config[state->type].name);
-@@ -5746,7 +3321,7 @@
+@@ -5746,7 +3325,7 @@
serial_driver.minor_start + state->line);
tty_register_devfs(&callout_driver, 0,
callout_driver.minor_start + state->line);
@@ -10356,7 +12273,7 @@
}
/**
-@@ -5785,7 +3360,6 @@
+@@ -5785,7 +3364,6 @@
int i;
struct async_struct *info;
@@ -10364,7 +12281,7 @@
del_timer_sync(&serial_timer);
save_flags(flags); cli();
remove_bh(SERIAL_BH);
-@@ -5803,41 +3377,31 @@
+@@ -5803,41 +3381,31 @@
kfree(info);
}
if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) {
@@ -10422,7 +12339,7 @@
}
module_init(rs_init);
-@@ -5946,7 +3510,7 @@
+@@ -5946,7 +3514,7 @@
static struct async_struct *info;
struct serial_state *state;
unsigned cval;
@@ -10431,7 +12348,7 @@
int bits = 8;
int parity = 'n';
int doflow = 0;
-@@ -5954,6 +3518,8 @@
+@@ -5954,6 +3522,8 @@
int quot = 0;
char *s;
@@ -10440,7 +12357,7 @@
if (options) {
baud = simple_strtoul(options, NULL, 10);
s = options;
-@@ -6028,19 +3594,12 @@
+@@ -6028,19 +3598,12 @@
info->state = state;
info->port = state->port;
info->flags = state->flags;
@@ -10460,7 +12377,7 @@
if (cflag & PARENB)
cval |= UART_LCR_PARITY;
if (!(cflag & PARODD))
-@@ -6082,10 +3641,15 @@
+@@ -6082,10 +3645,15 @@
*/
void __init serial_console_init(void)
{
@@ -11451,7 +13368,15 @@
dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_INPUT
if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then
int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
---- linux-2.4.21/drivers/input/Makefile~ramses-keyb
+@@ -14,6 +16,7 @@
+ fi
+ dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_INPUT
+ dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_INPUT
++dep_tristate ' User level driver support' CONFIG_INPUT_UINPUT $CONFIG_INPUT
+ dep_tristate ' MX1 touchscreen support' CONFIG_INPUT_MX1TS $CONFIG_INPUT_MOUSEDEV $CONFIG_ARCH_MX1ADS
+
+ endmenu
+--- linux-2.4.21/drivers/input/Makefile~bluetooth
+++ linux-2.4.21/drivers/input/Makefile
@@ -8,7 +8,7 @@
@@ -11462,7 +13387,7 @@
# Object file lists.
-@@ -21,10 +21,12 @@
+@@ -21,10 +21,13 @@
obj-$(CONFIG_INPUT) += input.o
obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o
@@ -11470,11 +13395,48 @@
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
++obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_MX1TS) += mx1ts.o
+obj-$(CONFIG_INPUT_RAMSES_WEDGE) += wedge.o
# The global Rules.make.
+--- linux-2.4.21/drivers/input/keybdev.c~bluetooth
++++ linux-2.4.21/drivers/input/keybdev.c
+@@ -154,16 +154,18 @@
+
+ static struct input_handler keybdev_handler;
+
++static unsigned int ledstate = 0xff;
++
+ void keybdev_ledfunc(unsigned int led)
+ {
+ struct input_handle *handle;
+
+- for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
++ ledstate = led;
+
++ for (handle = keybdev_handler.handle; handle; handle = handle->hnext) {
+ input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01));
+ input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02));
+ input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04));
+-
+ }
+ }
+
+@@ -203,6 +205,12 @@
+ // printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number);
+ kbd_refresh_leds();
+
++ if (ledstate != 0xff) {
++ input_event(dev, EV_LED, LED_SCROLLL, !!(ledstate & 0x01));
++ input_event(dev, EV_LED, LED_NUML, !!(ledstate & 0x02));
++ input_event(dev, EV_LED, LED_CAPSL, !!(ledstate & 0x04));
++ }
++
+ return handle;
+ }
+
--- /dev/null
+++ linux-2.4.21/drivers/input/ramses_cellmap.h
@@ -0,0 +1,34 @@
@@ -12320,6 +14282,437 @@
+
+#endif
--- /dev/null
++++ linux-2.4.21/drivers/input/uinput.c
+@@ -0,0 +1,428 @@
++/*
++ * User level driver support for input subsystem
++ *
++ * Heavily based on evdev.c by Vojtech Pavlik
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
++ *
++ * Changes/Revisions:
++ * 0.1 20/06/2002
++ * - first public version
++ */
++
++#include <linux/poll.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/smp_lock.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/uinput.h>
++
++static int uinput_dev_open(struct input_dev *dev)
++{
++ return 0;
++}
++
++static void uinput_dev_close(struct input_dev *dev)
++{
++}
++
++static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++{
++ struct uinput_device *udev;
++
++ udev = (struct uinput_device *)dev->private;
++
++ udev->buff[udev->head].type = type;
++ udev->buff[udev->head].code = code;
++ udev->buff[udev->head].value = value;
++ do_gettimeofday(&udev->buff[udev->head].time);
++ udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
++
++ wake_up_interruptible(&udev->waitq);
++
++ return 0;
++}
++
++static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect)
++{
++ return 0;
++}
++
++static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
++{
++ return 0;
++}
++
++static int uinput_create_device(struct uinput_device *udev)
++{
++ if (!udev->dev->name) {
++ printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
++ return -EINVAL;
++ }
++
++ udev->dev->open = uinput_dev_open;
++ udev->dev->close = uinput_dev_close;
++ udev->dev->event = uinput_dev_event;
++ udev->dev->upload_effect = uinput_dev_upload_effect;
++ udev->dev->erase_effect = uinput_dev_erase_effect;
++ udev->dev->private = udev;
++
++ init_waitqueue_head(&(udev->waitq));
++
++ input_register_device(udev->dev);
++
++ set_bit(UIST_CREATED, &(udev->state));
++
++ return 0;
++}
++
++static int uinput_destroy_device(struct uinput_device *udev)
++{
++ if (!test_bit(UIST_CREATED, &(udev->state))) {
++ printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME);
++ return -EINVAL;
++ }
++
++ input_unregister_device(udev->dev);
++
++ clear_bit(UIST_CREATED, &(udev->state));
++
++ return 0;
++}
++
++static int uinput_open(struct inode *inode, struct file *file)
++{
++ struct uinput_device *newdev;
++ struct input_dev *newinput;
++
++ newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL);
++ if (!newdev)
++ goto error;
++ memset(newdev, 0, sizeof(struct uinput_device));
++
++ newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
++ if (!newinput)
++ goto cleanup;
++ memset(newinput, 0, sizeof(struct input_dev));
++
++ newdev->dev = newinput;
++
++ file->private_data = newdev;
++
++ return 0;
++cleanup:
++ kfree(newdev);
++error:
++ return -ENOMEM;
++}
++
++static int uinput_validate_absbits(struct input_dev *dev)
++{
++ unsigned int cnt;
++ int retval = 0;
++
++ for (cnt = 0; cnt < ABS_MAX; cnt++) {
++ if (!test_bit(cnt, dev->absbit))
++ continue;
++
++ if (/*!dev->absmin[cnt] || !dev->absmax[cnt] || */
++ (dev->absmax[cnt] <= dev->absmin[cnt])) {
++ printk(KERN_DEBUG
++ "%s: invalid abs[%02x] min:%d max:%d\n",
++ UINPUT_NAME, cnt,
++ dev->absmin[cnt], dev->absmax[cnt]);
++ retval = -EINVAL;
++ break;
++ }
++
++ if ((dev->absflat[cnt] < dev->absmin[cnt]) ||
++ (dev->absflat[cnt] > dev->absmax[cnt])) {
++ printk(KERN_DEBUG
++ "%s: absflat[%02x] out of range: %d "
++ "(min:%d/max:%d)\n",
++ UINPUT_NAME, cnt, dev->absflat[cnt],
++ dev->absmin[cnt], dev->absmax[cnt]);
++ retval = -EINVAL;
++ break;
++ }
++ }
++ return retval;
++}
++
++static int uinput_alloc_device(struct file *file, const char *buffer, size_t count)
++{
++ struct uinput_user_dev *user_dev;
++ struct input_dev *dev;
++ struct uinput_device *udev;
++ int size,
++ retval;
++
++ retval = count;
++
++ udev = (struct uinput_device *)file->private_data;
++ dev = udev->dev;
++
++ user_dev = kmalloc(sizeof(*user_dev), GFP_KERNEL);
++ if (!user_dev) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
++ retval = -EFAULT;
++ goto exit;
++ }
++
++ if (NULL != dev->name)
++ kfree(dev->name);
++
++ size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
++ dev->name = kmalloc(size, GFP_KERNEL);
++ if (!dev->name) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ strncpy(dev->name, user_dev->name, size);
++ dev->idbus = user_dev->idbus;
++ dev->idvendor = user_dev->idvendor;
++ dev->idproduct = user_dev->idproduct;
++ dev->idversion = user_dev->idversion;
++ dev->ff_effects_max = user_dev->ff_effects_max;
++
++ size = sizeof(int) * (ABS_MAX + 1);
++ memcpy(dev->absmax, user_dev->absmax, size);
++ memcpy(dev->absmin, user_dev->absmin, size);
++ memcpy(dev->absfuzz, user_dev->absfuzz, size);
++ memcpy(dev->absflat, user_dev->absflat, size);
++
++ /* check if absmin/absmax/absfuzz/absflat are filled as
++ * told in Documentation/input/input-programming.txt */
++ if (test_bit(EV_ABS, dev->evbit)) {
++ retval = uinput_validate_absbits(dev);
++ if (retval < 0)
++ kfree(dev->name);
++ }
++
++exit:
++ kfree(user_dev);
++ return retval;
++}
++
++static ssize_t uinput_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
++{
++ struct uinput_device *udev = file->private_data;
++
++ if (test_bit(UIST_CREATED, &(udev->state))) {
++ struct input_event ev;
++
++ if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
++ return -EFAULT;
++ input_event(udev->dev, ev.type, ev.code, ev.value);
++ }
++ else
++ count = uinput_alloc_device(file, buffer, count);
++
++ return count;
++}
++
++static ssize_t uinput_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
++{
++ struct uinput_device *udev = file->private_data;
++ int retval = 0;
++
++ if (!test_bit(UIST_CREATED, &(udev->state)))
++ return -ENODEV;
++
++ if ((udev->head == udev->tail) && (file->f_flags & O_NONBLOCK))
++ return -EAGAIN;
++
++ retval = wait_event_interruptible(udev->waitq,
++ (udev->head != udev->tail) ||
++ !test_bit(UIST_CREATED, &(udev->state)));
++
++ if (retval)
++ return retval;
++
++ if (!test_bit(UIST_CREATED, &(udev->state)))
++ return -ENODEV;
++
++ while ((udev->head != udev->tail) &&
++ (retval + sizeof(struct input_event) <= count)) {
++ if (copy_to_user(buffer + retval, &(udev->buff[udev->tail]),
++ sizeof(struct input_event))) return -EFAULT;
++ udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
++ retval += sizeof(struct input_event);
++ }
++
++ return retval;
++}
++
++static unsigned int uinput_poll(struct file *file, poll_table *wait)
++{
++ struct uinput_device *udev = file->private_data;
++
++ poll_wait(file, &udev->waitq, wait);
++
++ if (udev->head != udev->tail)
++ return POLLIN | POLLRDNORM;
++
++ return 0;
++}
++
++static int uinput_burn_device(struct uinput_device *udev)
++{
++ if (test_bit(UIST_CREATED, &(udev->state)))
++ uinput_destroy_device(udev);
++
++ kfree(udev->dev);
++ kfree(udev);
++
++ return 0;
++}
++
++static int uinput_close(struct inode *inode, struct file *file)
++{
++ return uinput_burn_device((struct uinput_device *)file->private_data);
++}
++
++static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
++{
++ int retval = 0;
++ struct uinput_device *udev;
++
++ udev = (struct uinput_device *)file->private_data;
++
++ /* device attributes can not be changed after the device is created */
++ if (cmd >= UI_SET_EVBIT && test_bit(UIST_CREATED, &(udev->state)))
++ return -EINVAL;
++
++ switch (cmd) {
++ case UI_DEV_CREATE:
++ retval = uinput_create_device(udev);
++ break;
++
++ case UI_DEV_DESTROY:
++ retval = uinput_destroy_device(udev);
++ break;
++
++ case UI_SET_EVBIT:
++ if (arg > EV_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->evbit);
++ break;
++
++ case UI_SET_KEYBIT:
++ if (arg > KEY_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->keybit);
++ break;
++
++ case UI_SET_RELBIT:
++ if (arg > REL_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->relbit);
++ break;
++
++ case UI_SET_ABSBIT:
++ if (arg > ABS_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->absbit);
++ break;
++
++ case UI_SET_MSCBIT:
++ if (arg > MSC_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->mscbit);
++ break;
++
++ case UI_SET_LEDBIT:
++ if (arg > LED_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->ledbit);
++ break;
++
++ case UI_SET_SNDBIT:
++ if (arg > SND_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->sndbit);
++ break;
++
++ case UI_SET_FFBIT:
++ if (arg > FF_MAX) {
++ retval = -EINVAL;
++ break;
++ }
++ set_bit(arg, udev->dev->ffbit);
++ break;
++
++ default:
++ retval = -EFAULT;
++ }
++ return retval;
++}
++
++struct file_operations uinput_fops = {
++ owner: THIS_MODULE,
++ open: uinput_open,
++ release: uinput_close,
++ read: uinput_read,
++ write: uinput_write,
++ poll: uinput_poll,
++ ioctl: uinput_ioctl,
++};
++
++static struct miscdevice uinput_misc = {
++ fops: &uinput_fops,
++ minor: UINPUT_MINOR,
++ name: UINPUT_NAME,
++};
++
++static int __init uinput_init(void)
++{
++ return misc_register(&uinput_misc);
++}
++
++static void __exit uinput_exit(void)
++{
++ misc_deregister(&uinput_misc);
++}
++
++MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
++MODULE_DESCRIPTION("User level driver support for input subsystem");
++MODULE_LICENSE("GPL");
++
++module_init(uinput_init);
++module_exit(uinput_exit);
++
+--- /dev/null
+++ linux-2.4.21/drivers/input/wedge.c
@@ -0,0 +1,241 @@
+/*
@@ -12563,6 +14956,106 @@
+
+MODULE_DESCRIPTION("virtual keyboard wedge");
+MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/isdn/avmb1/capidrv.c~bluetooth
++++ linux-2.4.21/drivers/isdn/avmb1/capidrv.c
+@@ -523,13 +523,25 @@
+
+ static void send_message(capidrv_contr * card, _cmsg * cmsg)
+ {
+- struct sk_buff *skb;
+- size_t len;
++ struct sk_buff *skb;
++ size_t len;
++ u16 err;
++
+ capi_cmsg2message(cmsg, cmsg->buf);
+ len = CAPIMSG_LEN(cmsg->buf);
+ skb = alloc_skb(len, GFP_ATOMIC);
++ if(!skb) {
++ printk(KERN_ERR "no skb len(%d) memory\n", len);
++ return;
++ }
+ memcpy(skb_put(skb, len), cmsg->buf, len);
+- (*capifuncs->capi_put_message) (global.appid, skb);
++ err = (*capifuncs->capi_put_message) (global.appid, skb);
++ if (err) {
++ printk(KERN_WARNING "%s: capi_put_message error: %04x\n",
++ __FUNCTION__, err);
++ kfree_skb(skb);
++ return;
++ }
+ global.nsentctlpkt++;
+ }
+
+@@ -2188,10 +2200,10 @@
+ free_ncci(card, card->bchans[card->nbchan-1].nccip);
+ if (card->bchans[card->nbchan-1].plcip)
+ free_plci(card, card->bchans[card->nbchan-1].plcip);
+- if (card->plci_list)
+- printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ card->nbchan--;
+ }
++ if (card->plci_list)
++ printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ kfree(card->bchans);
+ card->bchans = 0;
+
+--- linux-2.4.21/drivers/isdn/avmb1/kcapi.c~bluetooth
++++ linux-2.4.21/drivers/isdn/avmb1/kcapi.c
+@@ -546,7 +546,13 @@
+ static void notify_up(__u32 contr)
+ {
+ struct capi_interface_user *p;
++ __u16 appl;
+
++ for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
++ if (!VALID_APPLID(appl)) continue;
++ if (APPL(appl)->releasing) continue;
++ CARD(contr)->driver->register_appl(CARD(contr), appl, &APPL(appl)->rparam);
++ }
+ printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr);
+ spin_lock(&capi_users_lock);
+ for (p = capi_users; p; p = p->next) {
+@@ -714,12 +720,16 @@
+ nextpp = &(*pp)->next;
+ }
+ }
+- APPL(appl)->releasing--;
+- if (APPL(appl)->releasing <= 0) {
+- APPL(appl)->signal = 0;
+- APPL_MARK_FREE(appl);
+- printk(KERN_INFO "kcapi: appl %d down\n", appl);
+- }
++ if (APPL(appl)->releasing) { /* only release if the application was marked for release */
++ printk(KERN_DEBUG "kcapi: appl %d releasing(%d)\n", appl, APPL(appl)->releasing);
++ APPL(appl)->releasing--;
++ if (APPL(appl)->releasing <= 0) {
++ APPL(appl)->signal = 0;
++ APPL_MARK_FREE(appl);
++ printk(KERN_INFO "kcapi: appl %d down\n", appl);
++ }
++ } else
++ printk(KERN_WARNING "kcapi: appl %d card%d released without request\n", appl, card->cnr);
+ }
+ /*
+ * ncci management
+@@ -872,16 +882,7 @@
+
+ static void controllercb_ready(struct capi_ctr * card)
+ {
+- __u16 appl;
+-
+ card->cardstate = CARD_RUNNING;
+-
+- for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+- if (!VALID_APPLID(appl)) continue;
+- if (APPL(appl)->releasing) continue;
+- card->driver->register_appl(card, appl, &APPL(appl)->rparam);
+- }
+-
+ printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
+ CARDNR(card), card->name);
+
--- linux-2.4.21/drivers/misc/Makefile~wedge
+++ linux-2.4.21/drivers/misc/Makefile
@@ -12,7 +12,7 @@
@@ -70016,6 +72509,17 @@
/* pass ownership to the completion handler */
urb->complete (urb);
+--- linux-2.4.21/drivers/usb/hid-core.c~bluetooth
++++ linux-2.4.21/drivers/usb/hid-core.c
+@@ -211,6 +211,8 @@
+
+ offset = report->size;
+ report->size += parser->global.report_size * parser->global.report_count;
++ if (usages < parser->global.report_count)
++ usages = parser->global.report_count;
+
+ if (usages == 0)
+ return 0; /* ignore padding fields */
--- linux-2.4.21/drivers/usb/host/Config.in~usb-sl811
+++ linux-2.4.21/drivers/usb/host/Config.in
@@ -13,6 +13,9 @@
@@ -93639,6 +96143,29 @@
+
+#endif
+#endif
+--- /dev/null
++++ linux-2.4.21/include/linux/firmware.h
+@@ -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
--- linux-2.4.21/include/linux/fs.h~mtd-cvs
+++ linux-2.4.21/include/linux/fs.h
@@ -1376,6 +1376,7 @@
@@ -93670,6 +96197,18 @@
#define I2C_DRIVERID_EXP0 0xF0 /* experimental use id's */
#define I2C_DRIVERID_EXP1 0xF1
+--- linux-2.4.21/include/linux/input.h~bluetooth
++++ linux-2.4.21/include/linux/input.h
+@@ -472,7 +472,8 @@
+ #define BUS_PCI 0x01
+ #define BUS_ISAPNP 0x02
+ #define BUS_USB 0x03
+-#define BUS_HIL 0x04
++#define BUS_HIL 0x04
++#define BUS_BLUETOOTH 0x05
+
+ #define BUS_ISA 0x10
+ #define BUS_I8042 0x11
--- linux-2.4.21/include/linux/jffs2.h~mtd-cvs
+++ linux-2.4.21/include/linux/jffs2.h
@@ -1,50 +1,30 @@
@@ -97740,6 +100279,16 @@
+#endif /* CONFIG_MTD_XIP */
+
+#endif /* __LINUX_MTD_XIP_H__ */
+--- linux-2.4.21/include/linux/net.h~bluetooth
++++ linux-2.4.21/include/linux/net.h
+@@ -139,6 +139,7 @@
+ extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags);
+ extern int sock_readv_writev(int type, struct inode * inode, struct file * file,
+ const struct iovec * iov, long count, long size);
++extern struct socket *sockfd_lookup(int fd, int *err);
+
+ extern int net_ratelimit(void);
+ extern unsigned long net_random(void);
--- /dev/null
+++ linux-2.4.21/include/linux/pm-devices.h
@@ -0,0 +1,41 @@
@@ -98265,6 +100814,88 @@
/* Note: the ioctl VT_GETSTATE does not work for
consoles 16 and higher (since it returns a short) */
+--- /dev/null
++++ linux-2.4.21/include/linux/uinput.h
+@@ -0,0 +1,79 @@
++/*
++ * User level driver support for input subsystem
++ *
++ * Heavily based on evdev.c by Vojtech Pavlik
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
++ *
++ * Changes/Revisions:
++ * 0.1 20/06/2002
++ * - first public version
++ */
++
++#ifndef __UINPUT_H_
++#define __UINPUT_H_
++
++#ifdef __KERNEL__
++#define UINPUT_MINOR 223
++#define UINPUT_NAME "uinput"
++#define UINPUT_BUFFER_SIZE 16
++
++/* state flags => bit index for {set|clear|test}_bit ops */
++#define UIST_CREATED 0
++
++struct uinput_device {
++ struct input_dev *dev;
++ unsigned long state;
++ wait_queue_head_t waitq;
++ unsigned char ready,
++ head,
++ tail;
++ struct input_event buff[UINPUT_BUFFER_SIZE];
++};
++#endif /* __KERNEL__ */
++
++/* ioctl */
++#define UINPUT_IOCTL_BASE 'U'
++#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
++#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
++#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
++#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
++#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int)
++#define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int)
++#define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int)
++#define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int)
++#define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int)
++#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int)
++
++#ifndef NBITS
++#define NBITS(x) ((((x)-1)/(sizeof(long)*8))+1)
++#endif /* NBITS */
++
++#define UINPUT_MAX_NAME_SIZE 80
++struct uinput_user_dev {
++ char name[UINPUT_MAX_NAME_SIZE];
++ unsigned short idbus;
++ unsigned short idvendor;
++ unsigned short idproduct;
++ unsigned short idversion;
++ int ff_effects_max;
++ int absmax[ABS_MAX + 1];
++ int absmin[ABS_MAX + 1];
++ int absfuzz[ABS_MAX + 1];
++ int absflat[ABS_MAX + 1];
++};
++#endif /* __UINPUT_H_ */
--- linux-2.4.21/include/linux/usb.h~ramses-usb
+++ linux-2.4.21/include/linux/usb.h
@@ -1079,7 +1079,7 @@
@@ -98858,6 +101489,210 @@
+
+
+#endif /* __MTD_NFTL_USER_H__ */
+--- linux-2.4.21/include/net/bluetooth/bluetooth.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/bluetooth.h
+@@ -51,6 +51,8 @@
+ #define BTPROTO_SCO 2
+ #define BTPROTO_RFCOMM 3
+ #define BTPROTO_BNEP 4
++#define BTPROTO_CMTP 5
++#define BTPROTO_HIDP 6
+
+ #define SOL_HCI 0
+ #define SOL_L2CAP 6
+@@ -155,7 +157,7 @@
+ 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_w4_connect(struct sock *sk, int flags);
++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);
+--- linux-2.4.21/include/net/bluetooth/hci.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/hci.h
+@@ -50,6 +50,11 @@
+ #define HCI_RS232 4
+ #define HCI_PCI 5
+
++/* HCI device quirks */
++enum {
++ HCI_QUIRK_RESET_ON_INIT
++};
++
+ /* HCI device flags */
+ enum {
+ HCI_UP,
+@@ -160,6 +165,7 @@
+ #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 */
+@@ -333,6 +339,8 @@
+ } __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 {
+@@ -459,6 +467,17 @@
+ } __attribute__ ((packed)) inquiry_info;
+ #define INQUIRY_INFO_SIZE 14
+
++#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22
++typedef struct {
++ bdaddr_t bdaddr;
++ __u8 pscan_rep_mode;
++ __u8 pscan_period_mode;
++ __u8 dev_class[3];
++ __u16 clock_offset;
++ __s8 rssi;
++} __attribute__ ((packed)) inquiry_info_with_rssi;
++#define INQUIRY_INFO_WITH_RSSI_SIZE 14
++
+ #define EVT_CONN_COMPLETE 0x03
+ typedef struct {
+ __u8 status;
+--- linux-2.4.21/include/net/bluetooth/hci_core.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/hci_core.h
+@@ -72,7 +72,9 @@
+ __u16 pkt_type;
+ __u16 link_policy;
+ __u16 link_mode;
+-
++
++ unsigned long quirks;
++
+ atomic_t cmd_cnt;
+ unsigned int acl_cnt;
+ unsigned int sco_cnt;
+@@ -167,6 +169,12 @@
+ c->list = NULL;
+ }
+
++static inline int inquiry_cache_empty(struct hci_dev *hdev)
++{
++ struct inquiry_cache *c = &hdev->inq_cache;
++ return (c->list == NULL);
++}
++
+ static inline long inquiry_cache_age(struct hci_dev *hdev)
+ {
+ struct inquiry_cache *c = &hdev->inq_cache;
+@@ -282,10 +290,12 @@
+ static inline void hci_conn_put(struct hci_conn *conn)
+ {
+ if (atomic_dec_and_test(&conn->refcnt)) {
+- if (conn->type == SCO_LINK)
++ if (conn->type == ACL_LINK) {
++ unsigned long timeo = (conn->out) ?
++ HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
++ hci_conn_set_timer(conn, timeo);
++ } else
+ hci_conn_set_timer(conn, HZ / 100);
+- else if (conn->out)
+- hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT);
+ }
+ }
+
+--- linux-2.4.21/include/net/bluetooth/l2cap.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/l2cap.h
+@@ -60,6 +60,7 @@
+ #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 {
+@@ -189,6 +190,14 @@
+ } __attribute__ ((packed)) l2cap_info_rsp;
+ #define L2CAP_INFO_RSP_SIZE 4
+
++/* info type */
++#define L2CAP_IT_CL_MTU 0x0001
++#define L2CAP_IT_FEAT_MASK 0x0002
++
++/* info result */
++#define L2CAP_IR_SUCCESS 0x0000
++#define L2CAP_IR_NOTSUPP 0x0001
++
+ /* ----- L2CAP connections ----- */
+ struct l2cap_chan_list {
+ struct sock *head;
+@@ -229,6 +238,7 @@
+ __u32 link_mode;
+
+ __u8 conf_state;
++ __u8 conf_retry;
+ __u16 conf_mtu;
+
+ __u8 ident;
+@@ -238,8 +248,11 @@
+ struct sock *prev_c;
+ };
+
+-#define CONF_REQ_SENT 0x01
+-#define CONF_INPUT_DONE 0x02
+-#define CONF_OUTPUT_DONE 0x04
++#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 */
+--- linux-2.4.21/include/net/bluetooth/rfcomm.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/rfcomm.h
+@@ -167,8 +167,8 @@
+ int initiator;
+
+ /* Default DLC parameters */
++ int cfc;
+ uint mtu;
+- uint credits;
+
+ struct list_head dlcs;
+ };
+@@ -185,11 +185,12 @@
+ atomic_t refcnt;
+ u8 dlci;
+ u8 addr;
+-
+- uint mtu;
++ u8 priority;
+ u8 v24_sig;
++ u8 mscex;
+
+- uint credits;
++ uint mtu;
++ uint cfc;
+ uint rx_credits;
+ uint tx_credits;
+
+@@ -213,6 +214,16 @@
+ #define RFCOMM_SCHED_TIMEO 3
+ #define RFCOMM_SCHED_WAKEUP 31
+
++/* MSC exchange flags */
++#define RFCOMM_MSCEX_TX 1
++#define RFCOMM_MSCEX_RX 2
++#define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX)
++
++/* CFC states */
++#define RFCOMM_CFC_UNKNOWN -1
++#define RFCOMM_CFC_DISABLED 0
++#define RFCOMM_CFC_ENABLED RFCOMM_MAX_CREDITS
++
+ extern struct task_struct *rfcomm_thread;
+ extern unsigned long rfcomm_event;
+
--- linux-2.4.21/include/net/iw_handler.h~linux-iw241_we16-6
+++ linux-2.4.21/include/net/iw_handler.h
@@ -1,7 +1,7 @@
@@ -98984,9 +101819,31 @@
extern int get_filesystem_list(char * buf);
extern asmlinkage long sys_mount(char *dev_name, char *dir_name, char *type,
---- linux-2.4.21/kernel/ksyms.c~ramses
+--- linux-2.4.21/kernel/ksyms.c~bluetooth
+++ linux-2.4.21/kernel/ksyms.c
-@@ -585,6 +585,11 @@
+@@ -48,6 +48,7 @@
+ #include <linux/completion.h>
+ #include <linux/seq_file.h>
+ #include <linux/dnotify.h>
++#include <linux/firmware.h>
+ #include <asm/checksum.h>
+
+ #if defined(CONFIG_PROC_FS)
+@@ -564,6 +565,13 @@
+ EXPORT_SYMBOL(strspn);
+ EXPORT_SYMBOL(strsep);
+
++#ifdef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
++
+ /* software interrupts */
+ EXPORT_SYMBOL(tasklet_hi_vec);
+ EXPORT_SYMBOL(tasklet_vec);
+@@ -585,6 +593,11 @@
EXPORT_SYMBOL(tasklist_lock);
EXPORT_SYMBOL(pidhash);
@@ -99020,7 +101877,7 @@
return 0;
--- linux-2.4.21/lib/Config.in~mtd-cvs
+++ linux-2.4.21/lib/Config.in
-@@ -35,4 +35,20 @@
+@@ -35,4 +35,25 @@
fi
fi
@@ -99040,18 +101897,616 @@
+ fi
+fi
+
++if [ "$CONFIG_EXPERIMENTAL" = "y" -a \
++ "$CONFIG_HOTPLUG" = "y" ]; then
++ tristate 'Hotplug firmware loading support (EXPERIMENTAL)' CONFIG_FW_LOADER
++fi
++
endmenu
--- linux-2.4.21/lib/Makefile~mtd-cvs
+++ linux-2.4.21/lib/Makefile
-@@ -22,6 +22,7 @@
+@@ -8,11 +8,13 @@
+
+ L_TARGET := lib.a
+
+-export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o rbtree.o
++export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \
++ rbtree.o firmware_class.o
+
+ obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o \
+ bust_spinlocks.o rbtree.o dump_stack.o
+
++obj-$(CONFIG_FW_LOADER) += firmware_class.o
+ obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
+ obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
+
+@@ -22,6 +24,9 @@
subdir-$(CONFIG_ZLIB_INFLATE) += zlib_inflate
subdir-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate
+subdir-$(CONFIG_REED_SOLOMON) += reed_solomon
++
++include $(TOPDIR)/drivers/bluetooth/Makefile.lib
# Include the subdirs, if necessary.
obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
--- /dev/null
++++ linux-2.4.21/lib/firmware_class.c
+@@ -0,0 +1,573 @@
++/*
++ * firmware_class.c - Multi purpose firmware loading support
++ *
++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
++ *
++ * Please see Documentation/firmware_class/ for more information.
++ *
++ */
++/*
++ * Based on kernel/kmod.c and drivers/usb/usb.c
++ */
++/*
++ kernel/kmod.c
++ Kirk Petersen
++
++ Reorganized not to be a daemon by Adam Richter, with guidance
++ from Greg Zornetzer.
++
++ Modified to avoid chroot and file sharing problems.
++ Mikael Pettersson
++
++ Limit the concurrent number of kmod modprobes to catch loops from
++ "modprobe needs a service that is in a module".
++ Keith Owens <kaos@ocs.com.au> December 1999
++
++ Unblock all signals when we exec a usermode process.
++ Shuu Yamaguchi <shuu@wondernetworkresources.com> December 2000
++*/
++/*
++ * drivers/usb/usb.c
++ *
++ * (C) Copyright Linus Torvalds 1999
++ * (C) Copyright Johannes Erdfelt 1999-2001
++ * (C) Copyright Andreas Gal 1999
++ * (C) Copyright Gregory P. Smith 1999
++ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
++ * (C) Copyright Randy Dunlap 2000
++ * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id)
++ * (C) Copyright Yggdrasil Computing, Inc. 2000
++ * (usb_device_id matching changes by Adam J. Richter)
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/kmod.h>
++#include <linux/proc_fs.h>
++#include <linux/vmalloc.h>
++#include <asm/hardirq.h>
++
++#include "linux/firmware.h"
++
++MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
++MODULE_DESCRIPTION("Multi purpose firmware loading support");
++MODULE_LICENSE("GPL");
++
++#define err(format, arg...) \
++ printk(KERN_ERR "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg)
++
++static int loading_timeout = 10; /* In seconds */
++static struct proc_dir_entry *proc_dir_timeout;
++static struct proc_dir_entry *proc_dir;
++
++#ifdef CONFIG_HOTPLUG
++
++static int
++call_helper(char *verb, const char *name, const char *device)
++{
++ char *argv[3], **envp, *buf, *scratch;
++ int i = 0;
++
++ int retval = 0;
++
++ if (!hotplug_path[0])
++ return -ENOENT;
++ if (in_interrupt()) {
++ err("in_interrupt");
++ return -EFAULT;
++ }
++ if (!current->fs->root) {
++ warn("call_policy %s -- no FS yet", verb);
++ return -EPERM;
++ }
++
++ if (!(envp = (char **) kmalloc(20 * sizeof (char *), GFP_KERNEL))) {
++ err("unable to allocate envp");
++ return -ENOMEM;
++ }
++ if (!(buf = kmalloc(256, GFP_KERNEL))) {
++ kfree(envp);
++ err("unable to allocate buf");
++ return -ENOMEM;
++ }
++
++ /* only one standardized param to hotplug command: type */
++ argv[0] = hotplug_path;
++ argv[1] = "firmware";
++ argv[2] = 0;
++
++ /* minimal command environment */
++ envp[i++] = "HOME=/";
++ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++
++#ifdef DEBUG
++ /* hint that policy agent should enter no-stdout debug mode */
++ envp[i++] = "DEBUG=kernel";
++#endif
++ scratch = buf;
++
++ if (device) {
++ envp[i++] = scratch;
++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX+25,
++ "DEVPATH=/driver/firmware/%s", device) + 1;
++ }
++
++ envp[i++] = scratch;
++ scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
++
++ envp[i++] = scratch;
++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX,
++ "FIRMWARE=%s", name) + 1;
++
++ envp[i++] = 0;
++
++#ifdef DEBUG
++ dbg("firmware: %s %s %s", argv[0], argv[1], verb);
++#endif
++
++ retval = call_usermodehelper(argv[0], argv, envp);
++ if (retval) {
++ printk("call_usermodehelper return %d\n", retval);
++ }
++
++ kfree(buf);
++ kfree(envp);
++ return retval;
++}
++#else
++
++static inline int
++call_helper(char *verb, const char *name, const char *device)
++{
++ return -ENOENT;
++}
++
++#endif /* CONFIG_HOTPLUG */
++
++struct firmware_priv {
++ struct completion completion;
++ struct proc_dir_entry *proc_dir;
++ struct proc_dir_entry *attr_data;
++ struct proc_dir_entry *attr_loading;
++ struct firmware *fw;
++ int loading;
++ int abort;
++ int alloc_size;
++ struct timer_list timeout;
++};
++
++static int
++firmware_timeout_show(char *buf, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ return sprintf(buf, "%d\n", loading_timeout);
++}
++
++/**
++ * firmware_timeout_store:
++ * Description:
++ * Sets the number of seconds to wait for the firmware. Once
++ * this expires an error will be return to the driver and no
++ * firmware will be provided.
++ *
++ * Note: zero means 'wait for ever'
++ *
++ **/
++static int
++firmware_timeout_store(struct file *file, const char *buf,
++ unsigned long count, void *data)
++{
++ loading_timeout = simple_strtol(buf, NULL, 10);
++ return count;
++}
++
++static int
++firmware_loading_show(char *buf, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ return sprintf(buf, "%d\n", fw_priv->loading);
++}
++
++/**
++ * firmware_loading_store: - loading control file
++ * Description:
++ * The relevant values are:
++ *
++ * 1: Start a load, discarding any previous partial load.
++ * 0: Conclude the load and handle the data to the driver code.
++ * -1: Conclude the load with an error and discard any written data.
++ **/
++static int
++firmware_loading_store(struct file *file, const char *buf,
++ unsigned long count, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ int prev_loading = fw_priv->loading;
++
++ fw_priv->loading = simple_strtol(buf, NULL, 10);
++
++ switch (fw_priv->loading) {
++ case -1:
++ fw_priv->abort = 1;
++ wmb();
++ complete(&fw_priv->completion);
++ break;
++ case 1:
++ kfree(fw_priv->fw->data);
++ fw_priv->fw->data = NULL;
++ fw_priv->fw->size = 0;
++ fw_priv->alloc_size = 0;
++ break;
++ case 0:
++ if (prev_loading == 1)
++ complete(&fw_priv->completion);
++ break;
++ }
++
++ return count;
++}
++
++static int
++firmware_data_read(char *buffer, char **start, off_t offset,
++ int count, int *eof, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ struct firmware *fw = fw_priv->fw;
++
++ if (offset > fw->size)
++ return 0;
++ if (offset + count > fw->size)
++ count = fw->size - offset;
++
++ memcpy(buffer, fw->data + offset, count);
++ *start = (void *) ((long) count);
++ return count;
++}
++static int
++fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
++{
++ u8 *new_data;
++ int new_size;
++
++ if (min_size <= fw_priv->alloc_size)
++ return 0;
++ if((min_size % PAGE_SIZE) == 0)
++ new_size = min_size;
++ else
++ new_size = (min_size + PAGE_SIZE) & PAGE_MASK;
++ new_data = vmalloc(new_size);
++ if (!new_data) {
++ printk(KERN_ERR "%s: unable to alloc buffer\n", __FUNCTION__);
++ /* Make sure that we don't keep incomplete data */
++ fw_priv->abort = 1;
++ return -ENOMEM;
++ }
++ fw_priv->alloc_size = new_size;
++ if (fw_priv->fw->data) {
++ memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
++ vfree(fw_priv->fw->data);
++ }
++ fw_priv->fw->data = new_data;
++ BUG_ON(min_size > fw_priv->alloc_size);
++ return 0;
++}
++
++/**
++ * firmware_data_write:
++ *
++ * Description:
++ *
++ * Data written to the 'data' attribute will be later handled to
++ * the driver as a firmware image.
++ **/
++static int
++firmware_data_write(struct file *file, const char *buffer,
++ unsigned long count, void *data)
++{
++ struct firmware_priv *fw_priv = data;
++ struct firmware *fw = fw_priv->fw;
++ int offset = file->f_pos;
++ int retval;
++
++ retval = fw_realloc_buffer(fw_priv, offset + count);
++ if (retval) {
++ printk("%s: retval:%d\n", __FUNCTION__, retval);
++ return retval;
++ }
++
++ memcpy(fw->data + offset, buffer, count);
++
++ fw->size = max_t(size_t, offset + count, fw->size);
++ file->f_pos += count;
++ return count;
++}
++
++static void
++firmware_class_timeout(u_long data)
++{
++ struct firmware_priv *fw_priv = (struct firmware_priv *) data;
++ fw_priv->abort = 1;
++ wmb();
++ complete(&fw_priv->completion);
++}
++static int
++fw_setup_class_device(struct firmware_priv **fw_priv_p,
++ const char *fw_name, const char *device)
++{
++ int retval;
++ struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv),
++ GFP_KERNEL);
++ *fw_priv_p = fw_priv;
++ if (!fw_priv) {
++ retval = -ENOMEM;
++ goto out;
++ }
++ memset(fw_priv, 0, sizeof (*fw_priv));
++
++ init_completion(&fw_priv->completion);
++
++ fw_priv->timeout.function = firmware_class_timeout;
++ fw_priv->timeout.data = (u_long) fw_priv;
++ init_timer(&fw_priv->timeout);
++
++ retval = -EAGAIN;
++ fw_priv->proc_dir = create_proc_entry(device, 0644 | S_IFDIR, proc_dir);
++ if (!fw_priv->proc_dir)
++ goto err_free_fw_priv;
++
++ fw_priv->attr_data = create_proc_entry("data", 0644 | S_IFREG,
++ fw_priv->proc_dir);
++ if (!fw_priv->attr_data)
++ goto err_remove_dir;
++
++ fw_priv->attr_data->read_proc = firmware_data_read;
++ fw_priv->attr_data->write_proc = firmware_data_write;
++ fw_priv->attr_data->data = fw_priv;
++
++ fw_priv->attr_loading = create_proc_entry("loading", 0644 | S_IFREG,
++ fw_priv->proc_dir);
++ if (!fw_priv->attr_loading)
++ goto err_remove_data;
++
++ fw_priv->attr_loading->read_proc = firmware_loading_show;
++ fw_priv->attr_loading->write_proc = firmware_loading_store;
++ fw_priv->attr_loading->data = fw_priv;
++
++ retval = 0;
++ fw_priv->fw = kmalloc(sizeof (struct firmware), GFP_KERNEL);
++ if (!fw_priv->fw) {
++ printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n",
++ __FUNCTION__);
++ retval = -ENOMEM;
++ goto err_remove_loading;
++ }
++ memset(fw_priv->fw, 0, sizeof (*fw_priv->fw));
++
++ goto out;
++
++err_remove_loading:
++ remove_proc_entry("loading", fw_priv->proc_dir);
++err_remove_data:
++ remove_proc_entry("data", fw_priv->proc_dir);
++err_remove_dir:
++ remove_proc_entry(device, proc_dir);
++err_free_fw_priv:
++ kfree(fw_priv);
++out:
++ return retval;
++}
++static void
++fw_remove_class_device(struct firmware_priv *fw_priv)
++{
++ remove_proc_entry("loading", fw_priv->proc_dir);
++ remove_proc_entry("data", fw_priv->proc_dir);
++ remove_proc_entry(fw_priv->proc_dir->name, proc_dir);
++}
++
++/**
++ * request_firmware: - request firmware to hotplug and wait for it
++ * Description:
++ * @firmware will be used to return a firmware image by the name
++ * of @name for device @device.
++ *
++ * Should be called from user context where sleeping is allowed.
++ *
++ * @name will be use as $FIRMWARE in the hotplug environment and
++ * should be distinctive enough not to be confused with any other
++ * firmware image for this or any other device.
++ **/
++int
++request_firmware(const struct firmware **firmware, const char *name,
++ const char *device)
++{
++ struct firmware_priv *fw_priv;
++ int retval;
++
++ if (!firmware) {
++ retval = -EINVAL;
++ goto out;
++ }
++ *firmware = NULL;
++
++ retval = fw_setup_class_device(&fw_priv, name, device);
++ if (retval)
++ goto out;
++
++ retval = call_helper("add", name, device);
++ if (retval)
++ goto out;
++ if (loading_timeout) {
++ fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
++ add_timer(&fw_priv->timeout);
++ }
++
++ wait_for_completion(&fw_priv->completion);
++
++ del_timer(&fw_priv->timeout);
++ fw_remove_class_device(fw_priv);
++
++ if (fw_priv->fw->size && !fw_priv->abort) {
++ *firmware = fw_priv->fw;
++ } else {
++ retval = -ENOENT;
++ vfree(fw_priv->fw->data);
++ kfree(fw_priv->fw);
++ }
++out:
++ kfree(fw_priv);
++ return retval;
++}
++
++void
++release_firmware(const struct firmware *fw)
++{
++ if (fw) {
++ vfree(fw->data);
++ kfree(fw);
++ }
++}
++
++/**
++ * register_firmware: - provide a firmware image for later usage
++ *
++ * Description:
++ * Make sure that @data will be available by requesting firmware @name.
++ *
++ * Note: This will not be possible until some kind of persistence
++ * is available.
++ **/
++void
++register_firmware(const char *name, const u8 *data, size_t size)
++{
++ /* This is meaningless without firmware caching, so until we
++ * decide if firmware caching is reasonable just leave it as a
++ * noop */
++}
++
++/* Async support */
++struct firmware_work {
++ struct tq_struct work;
++ struct module *module;
++ const char *name;
++ const char *device;
++ void *context;
++ void (*cont)(const struct firmware *fw, void *context);
++};
++
++static void
++request_firmware_work_func(void *arg)
++{
++ struct firmware_work *fw_work = arg;
++ const struct firmware *fw;
++ if (!arg)
++ return;
++ request_firmware(&fw, fw_work->name, fw_work->device);
++ fw_work->cont(fw, fw_work->context);
++ release_firmware(fw);
++ __MOD_DEC_USE_COUNT(fw_work->module);
++ kfree(fw_work);
++}
++
++/**
++ * request_firmware_nowait:
++ *
++ * Description:
++ * Asynchronous variant of request_firmware() for contexts where
++ * it is not possible to sleep.
++ *
++ * @cont will be called asynchronously when the firmware request is over.
++ *
++ * @context will be passed over to @cont.
++ *
++ * @fw may be %NULL if firmware request fails.
++ *
++ **/
++int
++request_firmware_nowait(
++ struct module *module,
++ const char *name, const char *device, void *context,
++ void (*cont)(const struct firmware *fw, void *context))
++{
++ struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
++ GFP_ATOMIC);
++ if (!fw_work)
++ return -ENOMEM;
++ if (!try_inc_mod_count(module)) {
++ kfree(fw_work);
++ return -EFAULT;
++ }
++
++ *fw_work = (struct firmware_work) {
++ .module = module,
++ .name = name,
++ .device = device,
++ .context = context,
++ .cont = cont,
++ };
++ INIT_TQUEUE(&fw_work->work, request_firmware_work_func, fw_work);
++
++ schedule_task(&fw_work->work);
++ return 0;
++}
++
++static int __init
++firmware_class_init(void)
++{
++ proc_dir = create_proc_entry("driver/firmware", 0755 | S_IFDIR, NULL);
++ if (!proc_dir)
++ return -EAGAIN;
++ proc_dir_timeout = create_proc_entry("timeout",
++ 0644 | S_IFREG, proc_dir);
++ if (!proc_dir_timeout) {
++ remove_proc_entry("driver/firmware", NULL);
++ return -EAGAIN;
++ }
++ proc_dir_timeout->read_proc = firmware_timeout_show;
++ proc_dir_timeout->write_proc = firmware_timeout_store;
++ return 0;
++}
++static void __exit
++firmware_class_exit(void)
++{
++ remove_proc_entry("timeout", proc_dir);
++ remove_proc_entry("driver/firmware", NULL);
++}
++
++module_init(firmware_class_init);
++module_exit(firmware_class_exit);
++
++#ifndef CONFIG_FW_LOADER
++EXPORT_SYMBOL(release_firmware);
++EXPORT_SYMBOL(request_firmware);
++EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL(register_firmware);
++#endif
+--- /dev/null
+++ linux-2.4.21/lib/reed_solomon/Makefile
@@ -0,0 +1,12 @@
+#
@@ -99759,6 +103214,4484 @@
if (!size) {
kfree (area);
return NULL;
+--- linux-2.4.21/net/bluetooth/Config.in~bluetooth
++++ linux-2.4.21/net/bluetooth/Config.in
+@@ -13,6 +13,8 @@
+ dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ
+ source net/bluetooth/rfcomm/Config.in
+ source net/bluetooth/bnep/Config.in
++ source net/bluetooth/cmtp/Config.in
++ source net/bluetooth/hidp/Config.in
+ source drivers/bluetooth/Config.in
+ fi
+
+--- linux-2.4.21/net/bluetooth/Makefile~bluetooth
++++ linux-2.4.21/net/bluetooth/Makefile
+@@ -5,7 +5,7 @@
+ O_TARGET := bluetooth.o
+
+ list-multi := bluez.o
+-export-objs := syms.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
+
+@@ -15,6 +15,8 @@
+
+ subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm
+ subdir-$(CONFIG_BLUEZ_BNEP) += bnep
++subdir-$(CONFIG_BLUEZ_CMTP) += cmtp
++subdir-$(CONFIG_BLUEZ_HIDP) += hidp
+
+ ifeq ($(CONFIG_BLUEZ_RFCOMM),y)
+ obj-y += rfcomm/rfcomm.o
+@@ -24,6 +26,14 @@
+ obj-y += bnep/bnep.o
+ endif
+
++ifeq ($(CONFIG_BLUEZ_CMTP),y)
++obj-y += cmtp/cmtp.o
++endif
++
++ifeq ($(CONFIG_BLUEZ_HIDP),y)
++obj-y += hidp/hidp.o
++endif
++
+ include $(TOPDIR)/Rules.make
+
+ bluez.o: $(bluez-objs)
+--- linux-2.4.21/net/bluetooth/af_bluetooth.c~bluetooth
++++ linux-2.4.21/net/bluetooth/af_bluetooth.c
+@@ -27,7 +27,7 @@
+ *
+ * $Id$
+ */
+-#define VERSION "2.2"
++#define VERSION "2.4"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -57,7 +57,7 @@
+ #endif
+
+ /* Bluetooth sockets */
+-#define BLUEZ_MAX_PROTO 5
++#define BLUEZ_MAX_PROTO 7
+ static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
+
+ int bluez_sock_register(int proto, struct net_proto_family *ops)
+@@ -221,12 +221,11 @@
+ unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
+ {
+ struct sock *sk = sock->sk;
+- unsigned int mask;
++ unsigned int mask = 0;
+
+ 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;
+@@ -242,9 +241,11 @@
+ if (sk->state == BT_CLOSED)
+ mask |= POLLHUP;
+
+- if (sk->state == BT_CONNECT || sk->state == BT_CONNECT2)
++ if (sk->state == BT_CONNECT ||
++ sk->state == BT_CONNECT2 ||
++ sk->state == BT_CONFIG)
+ return mask;
+-
++
+ if (sock_writeable(sk))
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+ else
+@@ -253,39 +254,35 @@
+ return mask;
+ }
+
+-int bluez_sock_w4_connect(struct sock *sk, int flags)
++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+- long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+ int err = 0;
+
+ BT_DBG("sk %p", sk);
+
+ add_wait_queue(sk->sleep, &wait);
+- while (sk->state != BT_CONNECTED) {
++ 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);
+
+- 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;
+- }
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(sk->sleep, &wait);
+--- linux-2.4.21/net/bluetooth/bnep/core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/bnep/core.c
+@@ -63,7 +63,7 @@
+ #define BT_DBG(D...)
+ #endif
+
+-#define VERSION "1.1"
++#define VERSION "1.2"
+
+ static LIST_HEAD(bnep_session_list);
+ static DECLARE_RWSEM(bnep_session_sem);
+@@ -113,13 +113,28 @@
+ return bnep_send(s, &rsp, sizeof(rsp));
+ }
+
++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
++static inline void bnep_set_default_proto_filter(struct bnep_session *s)
++{
++ /* (IPv4, ARP) */
++ s->proto_filter[0].start = htons(0x0800);
++ s->proto_filter[0].end = htons(0x0806);
++ /* (RARP, AppleTalk) */
++ s->proto_filter[1].start = htons(0x8035);
++ s->proto_filter[1].end = htons(0x80F3);
++ /* (IPX, IPv6) */
++ s->proto_filter[2].start = htons(0x8137);
++ s->proto_filter[2].end = htons(0x86DD);
++}
++#endif
++
+ static int bnep_ctrl_set_netfilter(struct bnep_session *s, u16 *data, int len)
+ {
+ int n;
+
+ if (len < 2)
+ return -EILSEQ;
+-
++
+ n = ntohs(get_unaligned(data));
+ data++; len -= 2;
+
+@@ -141,9 +156,13 @@
+ BT_DBG("proto filter start %d end %d",
+ f[i].start, f[i].end);
+ }
++
+ if (i < BNEP_MAX_PROTO_FILTERS)
+ memset(f + i, 0, sizeof(*f));
+
++ if (n == 0)
++ bnep_set_default_proto_filter(s);
++
+ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
+ } else {
+ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
+@@ -160,7 +179,7 @@
+
+ if (len < 2)
+ return -EILSEQ;
+-
++
+ n = ntohs(get_unaligned((u16 *) data));
+ data += 2; len -= 2;
+
+@@ -214,7 +233,7 @@
+ int err = 0;
+
+ data++; len--;
+-
++
+ switch (cmd) {
+ case BNEP_CMD_NOT_UNDERSTOOD:
+ case BNEP_SETUP_CONN_REQ:
+@@ -268,13 +287,13 @@
+ /* Unknown extension, skip it. */
+ break;
+ }
+-
++
+ if (!skb_pull(skb, h->len)) {
+ err = -EILSEQ;
+ break;
+ }
+ } while (!err && (h->type & BNEP_EXT_HEADER));
+-
++
+ return err;
+ }
+
+@@ -300,7 +319,7 @@
+
+ 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);
+@@ -326,7 +345,7 @@
+ 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);
+@@ -342,7 +361,7 @@
+ 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);
+@@ -362,7 +381,7 @@
+
+ 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;
+@@ -426,9 +445,9 @@
+ 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);
+@@ -453,16 +472,16 @@
+
+ BT_DBG("");
+
+- daemonize(); reparent_to_init();
++ daemonize(); reparent_to_init();
+
+- sprintf(current->comm, "kbnepd %s", dev->name);
+-
+- sigfillset(&current->blocked);
++ sprintf(current->comm, "kbnepd %s", dev->name);
++
++ sigfillset(&current->blocked);
+ flush_signals(current);
+
+ current->nice = -15;
+
+- set_fs(KERNEL_DS);
++ set_fs(KERNEL_DS);
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(sk->sleep, &wait);
+@@ -477,13 +496,13 @@
+
+ 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);
+@@ -547,28 +566,19 @@
+ 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);
++ bnep_set_default_proto_filter(s);
+ #endif
+-
++
+ dev->init = bnep_net_init;
+ dev->priv = s;
+ err = register_netdev(dev);
+@@ -577,7 +587,7 @@
+ }
+
+ __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. */
+@@ -680,6 +690,8 @@
+
+ static int __init bnep_init_module(void)
+ {
++ l2cap_load();
++
+ bnep_crc32_init();
+ bnep_sock_init();
+
+--- linux-2.4.21/net/bluetooth/bnep/sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/bnep/sock.c
+@@ -55,36 +55,6 @@
+ #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;
+@@ -124,8 +94,10 @@
+ if (!nsock)
+ return err;
+
+- if (nsock->sk->state != BT_CONNECTED)
++ if (nsock->sk->state != BT_CONNECTED) {
++ fput(nsock->file);
+ return -EBADFD;
++ }
+
+ err = bnep_add_connection(&ca, nsock);
+ if (!err) {
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/Config.in
+@@ -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
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/Makefile
+@@ -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
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/capi.c
+@@ -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);
++}
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/cmtp.h
+@@ -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 */
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/core.c
+@@ -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");
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/sock.c
+@@ -0,0 +1,208 @@
++/*
++ CMTP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <net/sock.h>
++
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include "cmtp.h"
++
++#ifndef CONFIG_BLUEZ_CMTP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static int cmtp_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct cmtp_connadd_req ca;
++ struct cmtp_conndel_req cd;
++ struct cmtp_connlist_req cl;
++ struct cmtp_conninfo ci;
++ struct socket *nsock;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
++ case CMTPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
++
++ nsock = sockfd_lookup(ca.sock, &err);
++ if (!nsock)
++ return err;
++
++ if (nsock->sk->state != BT_CONNECTED) {
++ fput(nsock->file);
++ return -EBADFD;
++ }
++
++ err = cmtp_add_connection(&ca, nsock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else
++ fput(nsock->file);
++
++ return err;
++
++ case CMTPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
++
++ return cmtp_del_connection(&cd);
++
++ case CMTPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
++
++ if (cl.cnum <= 0)
++ return -EINVAL;
++
++ err = cmtp_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case CMTPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = cmtp_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++ }
++
++ return -EINVAL;
++}
++
++static struct proto_ops cmtp_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: cmtp_sock_release,
++ ioctl: cmtp_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
++
++static int cmtp_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &cmtp_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family cmtp_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: cmtp_sock_create
++};
++
++int cmtp_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops))) {
++ BT_ERR("Can't register CMTP socket layer (%d)", err);
++ return err;
++ }
++
++ return 0;
++}
++
++void cmtp_cleanup_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_unregister(BTPROTO_CMTP)))
++ BT_ERR("Can't unregister CMTP socket layer (%d)", err);
++
++ return;
++}
+--- linux-2.4.21/net/bluetooth/hci_core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_core.c
+@@ -218,6 +218,10 @@
+
+ /* Mandatory initialization */
+
++ /* Reset */
++ if (test_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks))
++ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL);
++
+ /* Read Local Supported Features */
+ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES, 0, NULL);
+
+@@ -395,7 +399,7 @@
+ {
+ struct hci_inquiry_req ir;
+ struct hci_dev *hdev;
+- int err = 0, do_inquiry = 0;
++ int err = 0, do_inquiry = 0, max_rsp;
+ long timeo;
+ __u8 *buf, *ptr;
+
+@@ -408,6 +412,7 @@
+
+ hci_dev_lock_bh(hdev);
+ if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
++ inquiry_cache_empty(hdev) ||
+ ir.flags & IREQ_CACHE_FLUSH) {
+ inquiry_cache_flush(hdev);
+ do_inquiry = 1;
+@@ -418,16 +423,19 @@
+ 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) * ir.num_rsp, GFP_KERNEL))) {
++ 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, ir.num_rsp, buf);
++ ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);
+ hci_dev_unlock_bh(hdev);
+
+ BT_DBG("num_rsp %d", ir.num_rsp);
+@@ -708,22 +716,20 @@
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ struct list_head *p;
+- int n = 0, size;
++ int n = 0, size, err;
+ __u16 dev_num;
+
+ if (get_user(dev_num, (__u16 *) arg))
+ return -EFAULT;
+
+- if (!dev_num)
++ if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr))
+ return -EINVAL;
+-
+- size = dev_num * sizeof(*dr) + sizeof(*dl);
+
+- if (verify_area(VERIFY_WRITE, (void *) arg, size))
+- return -EFAULT;
++ size = sizeof(*dl) + dev_num * sizeof(*dr);
+
+ if (!(dl = kmalloc(size, GFP_KERNEL)))
+ return -ENOMEM;
++
+ dr = dl->dev_req;
+
+ read_lock_bh(&hdev_list_lock);
+@@ -738,12 +744,12 @@
+ read_unlock_bh(&hdev_list_lock);
+
+ dl->dev_num = n;
+- size = n * sizeof(*dr) + sizeof(*dl);
++ size = sizeof(*dl) + n * sizeof(*dr);
+
+- copy_to_user((void *) arg, dl, size);
++ err = copy_to_user((void *) arg, dl, size);
+ kfree(dl);
+
+- return 0;
++ return err ? -EFAULT : 0;
+ }
+
+ int hci_get_dev_info(unsigned long arg)
+--- linux-2.4.21/net/bluetooth/hci_event.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_event.c
+@@ -62,9 +62,22 @@
+ /* 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;
+@@ -307,7 +320,7 @@
+ status, batostr(&cc->bdaddr), conn);
+
+ if (status) {
+- if (conn) {
++ if (conn && conn->state == BT_CONNECT) {
+ conn->state = BT_CLOSED;
+ hci_proto_connect_cfm(conn, status);
+ hci_conn_del(conn);
+@@ -439,6 +452,29 @@
+ hci_dev_unlock(hdev);
+ }
+
++/* Inquiry Result With RSSI */
++static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb)
++{
++ inquiry_info_with_rssi *info = (inquiry_info_with_rssi *) (skb->data + 1);
++ int num_rsp = *((__u8 *) skb->data);
++
++ BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
++
++ hci_dev_lock(hdev);
++ for (; num_rsp; num_rsp--) {
++ inquiry_info tmp;
++ bacpy(&tmp.bdaddr, &info->bdaddr);
++ tmp.pscan_rep_mode = info->pscan_rep_mode;
++ tmp.pscan_period_mode = info->pscan_period_mode;
++ tmp.pscan_mode = 0x00;
++ memcpy(tmp.dev_class, &info->dev_class, 3);
++ tmp.clock_offset = info->clock_offset;
++ info++;
++ inquiry_cache_update(hdev, &tmp);
++ }
++ hci_dev_unlock(hdev);
++}
++
+ /* Connect Request */
+ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
+ {
+@@ -735,6 +771,10 @@
+ hci_inquiry_result_evt(hdev, skb);
+ break;
+
++ case EVT_INQUIRY_RESULT_WITH_RSSI:
++ hci_inquiry_result_with_rssi_evt(hdev, skb);
++ break;
++
+ case EVT_CONN_REQUEST:
+ hci_conn_request_evt(hdev, skb);
+ break;
+--- linux-2.4.21/net/bluetooth/hci_sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_sock.c
+@@ -66,20 +66,20 @@
+ /* Packet types */
+ 0x10,
+ /* Events */
+- { 0xd9fe, 0x0 },
++ { 0x1000d9fe, 0x0000300c },
+ /* Commands */
+ {
+ { 0x0 },
+ /* OGF_LINK_CTL */
+- { 0x2a000002, 0x0, 0x0, 0x0 },
++ { 0xbe000006, 0x00000001, 0x0000, 0x00 },
+ /* OGF_LINK_POLICY */
+- { 0x1200, 0x0, 0x0, 0x0 },
++ { 0x00005200, 0x00000000, 0x0000, 0x00 },
+ /* OGF_HOST_CTL */
+- { 0x80100000, 0x2a, 0x0, 0x0 },
++ { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
+ /* OGF_INFO_PARAM */
+- { 0x22a, 0x0, 0x0, 0x0 },
++ { 0x000002be, 0x00000000, 0x0000, 0x00 },
+ /* OGF_STATUS_PARAM */
+- { 0x2e, 0x0, 0x0, 0x0 }
++ { 0x000000ea, 0x00000000, 0x0000, 0x00 }
+ }
+ };
+
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/Config.in
+@@ -0,0 +1,5 @@
++#
++# Bluetooth HIDP layer configuration
++#
++
++dep_tristate 'HIDP protocol support' CONFIG_BLUEZ_HIDP $CONFIG_INPUT $CONFIG_BLUEZ_L2CAP
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/Makefile
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth HIDP layer
++#
++
++O_TARGET := hidp.o
++
++obj-y := core.o sock.o
++obj-m += $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/core.c
+@@ -0,0 +1,655 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include <linux/input.h>
++
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
++
++#include "hidp.h"
++
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++#define VERSION "1.0"
++
++static DECLARE_RWSEM(hidp_session_sem);
++static LIST_HEAD(hidp_session_list);
++
++static unsigned char hidp_keycode[256] = {
++ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
++ 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
++ 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
++ 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
++ 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
++ 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
++ 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
++ 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
++ 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
++ 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
++ 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
++ 150,158,159,128,136,177,178,176,142,152,173,140
++};
++
++static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
++{
++ struct hidp_session *session;
++ struct list_head *p;
++
++ BT_DBG("");
++
++ list_for_each(p, &hidp_session_list) {
++ session = list_entry(p, struct hidp_session, list);
++ if (!bacmp(bdaddr, &session->bdaddr))
++ return session;
++ }
++ return NULL;
++}
++
++static void __hidp_link_session(struct hidp_session *session)
++{
++ MOD_INC_USE_COUNT;
++ list_add(&session->list, &hidp_session_list);
++}
++
++static void __hidp_unlink_session(struct hidp_session *session)
++{
++ list_del(&session->list);
++ MOD_DEC_USE_COUNT;
++}
++
++static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
++{
++ bacpy(&ci->bdaddr, &session->bdaddr);
++
++ ci->flags = session->flags;
++ ci->state = session->state;
++
++ ci->vendor = 0x0000;
++ ci->product = 0x0000;
++ ci->version = 0x0000;
++ memset(ci->name, 0, 128);
++
++ if (session->input) {
++ ci->vendor = session->input->idvendor;
++ ci->product = session->input->idproduct;
++ ci->version = session->input->idversion;
++ if (session->input->name)
++ strncpy(ci->name, session->input->name, 128);
++ else
++ strncpy(ci->name, "HID Boot Device", 128);
++ }
++}
++
++static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++{
++ struct hidp_session *session = dev->private;
++ struct sk_buff *skb;
++ unsigned char newleds;
++
++ BT_DBG("session %p hid %p data %p size %d", session, device, data, size);
++
++ if (type != EV_LED)
++ return -1;
++
++ newleds = (!!test_bit(LED_KANA, dev->led) << 3) |
++ (!!test_bit(LED_COMPOSE, dev->led) << 3) |
++ (!!test_bit(LED_SCROLLL, dev->led) << 2) |
++ (!!test_bit(LED_CAPSL, dev->led) << 1) |
++ (!!test_bit(LED_NUML, dev->led));
++
++ if (session->leds == newleds)
++ return 0;
++
++ session->leds = newleds;
++
++ if (!(skb = alloc_skb(3, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for new frame");
++ return -ENOMEM;
++ }
++
++ *skb_put(skb, 1) = 0xa2;
++ *skb_put(skb, 1) = 0x01;
++ *skb_put(skb, 1) = newleds;
++
++ skb_queue_tail(&session->intr_transmit, skb);
++
++ hidp_schedule(session);
++
++ return 0;
++}
++
++static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb)
++{
++ struct input_dev *dev = session->input;
++ unsigned char *keys = session->keys;
++ unsigned char *udata = skb->data + 1;
++ signed char *sdata = skb->data + 1;
++ int i, size = skb->len - 1;
++
++ switch (skb->data[0]) {
++ case 0x01: /* Keyboard report */
++ for (i = 0; i < 8; i++)
++ input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1);
++
++ for (i = 2; i < 8; i++) {
++ if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) {
++ if (hidp_keycode[keys[i]])
++ input_report_key(dev, hidp_keycode[keys[i]], 0);
++ else
++ BT_ERR("Unknown key (scancode %#x) released.", keys[i]);
++ }
++
++ if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) {
++ if (hidp_keycode[udata[i]])
++ input_report_key(dev, hidp_keycode[udata[i]], 1);
++ else
++ BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]);
++ }
++ }
++
++ memcpy(keys, udata, 8);
++ break;
++
++ case 0x02: /* Mouse report */
++ input_report_key(dev, BTN_LEFT, sdata[0] & 0x01);
++ input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02);
++ input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04);
++ input_report_key(dev, BTN_SIDE, sdata[0] & 0x08);
++ input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10);
++
++ input_report_rel(dev, REL_X, sdata[1]);
++ input_report_rel(dev, REL_Y, sdata[2]);
++
++ if (size > 3)
++ input_report_rel(dev, REL_WHEEL, sdata[3]);
++ break;
++ }
++
++ input_event(dev, EV_RST, 0, 0);
++}
++
++static void hidp_idle_timeout(unsigned long arg)
++{
++ struct hidp_session *session = (struct hidp_session *) arg;
++
++ atomic_inc(&session->terminate);
++ hidp_schedule(session);
++}
++
++static inline void hidp_set_timer(struct hidp_session *session)
++{
++ if (session->idle_to > 0)
++ mod_timer(&session->timer, jiffies + HZ * session->idle_to);
++}
++
++static inline void hidp_del_timer(struct hidp_session *session)
++{
++ if (session->idle_to > 0)
++ del_timer(&session->timer);
++}
++
++static inline void hidp_send_message(struct hidp_session *session, unsigned char hdr)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("session %p", session);
++
++ if (!(skb = alloc_skb(1, GFP_ATOMIC))) {
++ BT_ERR("Can't allocate memory for message");
++ return;
++ }
++
++ *skb_put(skb, 1) = hdr;
++
++ skb_queue_tail(&session->ctrl_transmit, skb);
++
++ hidp_schedule(session);
++}
++
++static inline int hidp_recv_frame(struct hidp_session *session, struct sk_buff *skb)
++{
++ __u8 hdr;
++
++ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
++
++ hdr = skb->data[0];
++ skb_pull(skb, 1);
++
++ if (hdr == 0xa1) {
++ hidp_set_timer(session);
++
++ if (session->input)
++ hidp_input_report(session, skb);
++ } else {
++ BT_DBG("Unsupported protocol header 0x%02x", hdr);
++ }
++
++ kfree_skb(skb);
++ return 0;
++}
++
++static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
++{
++ struct iovec iv = { data, len };
++ struct msghdr msg;
++
++ BT_DBG("sock %p data %p len %d", sock, data, len);
++
++ if (!len)
++ return 0;
++
++ memset(&msg, 0, sizeof(msg));
++ msg.msg_iovlen = 1;
++ msg.msg_iov = &iv;
++
++ return sock_sendmsg(sock, &msg, len);
++}
++
++static int hidp_process_transmit(struct hidp_session *session)
++{
++ struct sk_buff *skb;
++
++ BT_DBG("session %p", session);
++
++ while ((skb = skb_dequeue(&session->ctrl_transmit))) {
++ if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) {
++ skb_queue_head(&session->ctrl_transmit, skb);
++ break;
++ }
++
++ hidp_set_timer(session);
++ kfree_skb(skb);
++ }
++
++ while ((skb = skb_dequeue(&session->intr_transmit))) {
++ if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) {
++ skb_queue_head(&session->intr_transmit, skb);
++ break;
++ }
++
++ hidp_set_timer(session);
++ kfree_skb(skb);
++ }
++
++ return skb_queue_len(&session->ctrl_transmit) +
++ skb_queue_len(&session->intr_transmit);
++}
++
++static int hidp_session(void *arg)
++{
++ struct hidp_session *session = arg;
++ struct sock *ctrl_sk = session->ctrl_sock->sk;
++ struct sock *intr_sk = session->intr_sock->sk;
++ struct sk_buff *skb;
++ int vendor = 0x0000, product = 0x0000;
++ wait_queue_t ctrl_wait, intr_wait;
++ unsigned long timeo = HZ;
++
++ BT_DBG("session %p", session);
++
++ if (session->input) {
++ vendor = session->input->idvendor;
++ product = session->input->idproduct;
++ }
++
++ daemonize(); reparent_to_init();
++
++ sprintf(current->comm, "khidpd_%04x%04x", vendor, product);
++
++ sigfillset(&current->blocked);
++ flush_signals(current);
++
++ current->nice = -15;
++
++ set_fs(KERNEL_DS);
++
++ init_waitqueue_entry(&ctrl_wait, current);
++ init_waitqueue_entry(&intr_wait, current);
++ add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ add_wait_queue(intr_sk->sleep, &intr_wait);
++ while (!atomic_read(&session->terminate)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (ctrl_sk->state != BT_CONNECTED || intr_sk->state != BT_CONNECTED)
++ break;
++
++ while ((skb = skb_dequeue(&ctrl_sk->receive_queue))) {
++ skb_orphan(skb);
++ hidp_recv_frame(session, skb);
++ }
++
++ while ((skb = skb_dequeue(&intr_sk->receive_queue))) {
++ skb_orphan(skb);
++ hidp_recv_frame(session, skb);
++ }
++
++ hidp_process_transmit(session);
++
++ schedule();
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(intr_sk->sleep, &intr_wait);
++ remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++
++ down_write(&hidp_session_sem);
++
++ hidp_del_timer(session);
++
++ if (intr_sk->state != BT_CONNECTED) {
++ init_waitqueue_entry(&ctrl_wait, current);
++ add_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ while (timeo && ctrl_sk->state != BT_CLOSED) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(ctrl_sk->sleep, &ctrl_wait);
++ timeo = HZ;
++ }
++
++ fput(session->ctrl_sock->file);
++
++ init_waitqueue_entry(&intr_wait, current);
++ add_wait_queue(intr_sk->sleep, &intr_wait);
++ while (timeo && intr_sk->state != BT_CLOSED) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ timeo = schedule_timeout(timeo);
++ }
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(intr_sk->sleep, &intr_wait);
++
++ fput(session->intr_sock->file);
++
++ __hidp_unlink_session(session);
++
++ if (session->input) {
++ input_unregister_device(session->input);
++ kfree(session->input);
++ }
++
++ up_write(&hidp_session_sem);
++
++ kfree(session);
++ return 0;
++}
++
++static inline void hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req)
++{
++ struct input_dev *input = session->input;
++ int i;
++
++ input->private = session;
++
++ input->idbus = BUS_BLUETOOTH;
++ input->idvendor = req->vendor;
++ input->idproduct = req->product;
++ input->idversion = req->version;
++
++ if (req->subclass & 0x40) {
++ set_bit(EV_KEY, input->evbit);
++ set_bit(EV_LED, input->evbit);
++ set_bit(EV_REP, input->evbit);
++
++ set_bit(LED_NUML, input->ledbit);
++ set_bit(LED_CAPSL, input->ledbit);
++ set_bit(LED_SCROLLL, input->ledbit);
++ set_bit(LED_COMPOSE, input->ledbit);
++ set_bit(LED_KANA, input->ledbit);
++
++ for (i = 0; i < sizeof(hidp_keycode); i++)
++ set_bit(hidp_keycode[i], input->keybit);
++ clear_bit(0, input->keybit);
++ }
++
++ if (req->subclass & 0x80) {
++ input->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
++ input->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
++ input->relbit[0] = BIT(REL_X) | BIT(REL_Y);
++ input->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
++ input->relbit[0] |= BIT(REL_WHEEL);
++ }
++
++ input->event = hidp_input_event;
++
++ input_register_device(input);
++}
++
++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
++{
++ struct hidp_session *session, *s;
++ int err;
++
++ BT_DBG("");
++
++ if (bacmp(&bluez_pi(ctrl_sock->sk)->src, &bluez_pi(intr_sock->sk)->src) ||
++ bacmp(&bluez_pi(ctrl_sock->sk)->dst, &bluez_pi(intr_sock->sk)->dst))
++ return -ENOTUNIQ;
++
++ session = kmalloc(sizeof(struct hidp_session), GFP_KERNEL);
++ if (!session)
++ return -ENOMEM;
++ memset(session, 0, sizeof(struct hidp_session));
++
++ session->input = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
++ if (!session->input) {
++ kfree(session);
++ return -ENOMEM;
++ }
++ memset(session->input, 0, sizeof(struct input_dev));
++
++ down_write(&hidp_session_sem);
++
++ s = __hidp_get_session(&bluez_pi(ctrl_sock->sk)->dst);
++ if (s && s->state == BT_CONNECTED) {
++ err = -EEXIST;
++ goto failed;
++ }
++
++ bacpy(&session->bdaddr, &bluez_pi(ctrl_sock->sk)->dst);
++
++ session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->omtu, l2cap_pi(ctrl_sock->sk)->imtu);
++ session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->omtu, l2cap_pi(intr_sock->sk)->imtu);
++
++ BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu);
++
++ session->ctrl_sock = ctrl_sock;
++ session->intr_sock = intr_sock;
++ session->state = BT_CONNECTED;
++
++ init_timer(&session->timer);
++
++ session->timer.function = hidp_idle_timeout;
++ session->timer.data = (unsigned long) session;
++
++ skb_queue_head_init(&session->ctrl_transmit);
++ skb_queue_head_init(&session->intr_transmit);
++
++ session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
++ session->idle_to = req->idle_to;
++
++ if (session->input)
++ hidp_setup_input(session, req);
++
++ __hidp_link_session(session);
++
++ hidp_set_timer(session);
++
++ err = kernel_thread(hidp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
++ if (err < 0)
++ goto unlink;
++
++ if (session->input) {
++ hidp_send_message(session, 0x70);
++ session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
++
++ session->leds = 0xff;
++ hidp_input_event(session->input, EV_LED, 0, 0);
++ }
++
++ up_write(&hidp_session_sem);
++ return 0;
++
++unlink:
++ hidp_del_timer(session);
++
++ __hidp_unlink_session(session);
++
++ if (session->input)
++ input_unregister_device(session->input);
++
++failed:
++ up_write(&hidp_session_sem);
++
++ if (session->input)
++ kfree(session->input);
++
++ kfree(session);
++ return err;
++}
++
++int hidp_del_connection(struct hidp_conndel_req *req)
++{
++ struct hidp_session *session;
++ int err = 0;
++
++ BT_DBG("");
++
++ down_read(&hidp_session_sem);
++
++ session = __hidp_get_session(&req->bdaddr);
++ if (session) {
++ if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) {
++ hidp_send_message(session, 0x15);
++ } else {
++ /* Flush the transmit queues */
++ skb_queue_purge(&session->ctrl_transmit);
++ skb_queue_purge(&session->intr_transmit);
++
++ /* Kill session thread */
++ atomic_inc(&session->terminate);
++ hidp_schedule(session);
++ }
++ } else
++ err = -ENOENT;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++int hidp_get_connlist(struct hidp_connlist_req *req)
++{
++ struct list_head *p;
++ int err = 0, n = 0;
++
++ BT_DBG("");
++
++ down_read(&hidp_session_sem);
++
++ list_for_each(p, &hidp_session_list) {
++ struct hidp_session *session;
++ struct hidp_conninfo ci;
++
++ session = list_entry(p, struct hidp_session, list);
++
++ __hidp_copy_session(session, &ci);
++
++ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (++n >= req->cnum)
++ break;
++
++ req->ci++;
++ }
++ req->cnum = n;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++int hidp_get_conninfo(struct hidp_conninfo *ci)
++{
++ struct hidp_session *session;
++ int err = 0;
++
++ down_read(&hidp_session_sem);
++
++ session = __hidp_get_session(&ci->bdaddr);
++ if (session)
++ __hidp_copy_session(session, ci);
++ else
++ err = -ENOENT;
++
++ up_read(&hidp_session_sem);
++ return err;
++}
++
++static int __init hidp_init(void)
++{
++ l2cap_load();
++
++ hidp_init_sockets();
++
++ BT_INFO("BlueZ HIDP ver %s", VERSION);
++ BT_INFO("Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>");
++
++ return 0;
++}
++
++static void __exit hidp_exit(void)
++{
++ hidp_cleanup_sockets();
++}
++
++module_init(hidp_init);
++module_exit(hidp_exit);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/hidp.h
+@@ -0,0 +1,122 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#ifndef __HIDP_H
++#define __HIDP_H
++
++#include <linux/types.h>
++#include <net/bluetooth/bluetooth.h>
++
++/* HIDP ioctl defines */
++#define HIDPCONNADD _IOW('H', 200, int)
++#define HIDPCONNDEL _IOW('H', 201, int)
++#define HIDPGETCONNLIST _IOR('H', 210, int)
++#define HIDPGETCONNINFO _IOR('H', 211, int)
++
++#define HIDP_VIRTUAL_CABLE_UNPLUG 0
++#define HIDP_BOOT_PROTOCOL_MODE 1
++#define HIDP_BLUETOOTH_VENDOR_ID 9
++
++struct hidp_connadd_req {
++ int ctrl_sock; // Connected control socket
++ int intr_sock; // Connteted interrupt socket
++ __u16 parser;
++ __u16 rd_size;
++ __u8 *rd_data;
++ __u8 country;
++ __u8 subclass;
++ __u16 vendor;
++ __u16 product;
++ __u16 version;
++ __u32 flags;
++ __u32 idle_to;
++ char name[128];
++};
++
++struct hidp_conndel_req {
++ bdaddr_t bdaddr;
++ __u32 flags;
++};
++
++struct hidp_conninfo {
++ bdaddr_t bdaddr;
++ __u32 flags;
++ __u16 state;
++ __u16 vendor;
++ __u16 product;
++ __u16 version;
++ char name[128];
++};
++
++struct hidp_connlist_req {
++ __u32 cnum;
++ struct hidp_conninfo *ci;
++};
++
++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock);
++int hidp_del_connection(struct hidp_conndel_req *req);
++int hidp_get_connlist(struct hidp_connlist_req *req);
++int hidp_get_conninfo(struct hidp_conninfo *ci);
++
++/* HIDP session defines */
++struct hidp_session {
++ struct list_head list;
++
++ struct socket *ctrl_sock;
++ struct socket *intr_sock;
++
++ bdaddr_t bdaddr;
++
++ unsigned long state;
++ unsigned long flags;
++ unsigned long idle_to;
++
++ uint ctrl_mtu;
++ uint intr_mtu;
++
++ atomic_t terminate;
++
++ unsigned char keys[8];
++ unsigned char leds;
++
++ struct input_dev *input;
++
++ struct timer_list timer;
++
++ struct sk_buff_head ctrl_transmit;
++ struct sk_buff_head intr_transmit;
++};
++
++static inline void hidp_schedule(struct hidp_session *session)
++{
++ struct sock *ctrl_sk = session->ctrl_sock->sk;
++ struct sock *intr_sk = session->intr_sock->sk;
++
++ wake_up_interruptible(ctrl_sk->sleep);
++ wake_up_interruptible(intr_sk->sleep);
++}
++
++/* HIDP init defines */
++extern int __init hidp_init_sockets(void);
++extern void __exit hidp_cleanup_sockets(void);
++
++#endif /* __HIDP_H */
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/sock.c
+@@ -0,0 +1,212 @@
++/*
++ HIDP implementation for Linux Bluetooth stack (BlueZ).
++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License version 2 as
++ published by the Free Software Foundation;
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
++ SOFTWARE IS DISCLAIMED.
++*/
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/poll.h>
++#include <linux/fcntl.h>
++#include <linux/skbuff.h>
++#include <linux/socket.h>
++#include <linux/ioctl.h>
++#include <linux/file.h>
++#include <linux/init.h>
++#include <net/sock.h>
++
++#include "hidp.h"
++
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef BT_DBG
++#define BT_DBG(D...)
++#endif
++
++static int hidp_sock_release(struct socket *sock)
++{
++ struct sock *sk = sock->sk;
++
++ BT_DBG("sock %p sk %p", sock, sk);
++
++ if (!sk)
++ return 0;
++
++ sock_orphan(sk);
++ sock_put(sk);
++
++ MOD_DEC_USE_COUNT;
++ return 0;
++}
++
++static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
++{
++ struct hidp_connadd_req ca;
++ struct hidp_conndel_req cd;
++ struct hidp_connlist_req cl;
++ struct hidp_conninfo ci;
++ struct socket *csock;
++ struct socket *isock;
++ int err;
++
++ BT_DBG("cmd %x arg %lx", cmd, arg);
++
++ switch (cmd) {
++ case HIDPCONNADD:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++ return -EFAULT;
++
++ csock = sockfd_lookup(ca.ctrl_sock, &err);
++ if (!csock)
++ return err;
++
++ isock = sockfd_lookup(ca.intr_sock, &err);
++ if (!isock) {
++ fput(csock->file);
++ return err;
++ }
++
++ if (csock->sk->state != BT_CONNECTED || isock->sk->state != BT_CONNECTED) {
++ fput(csock->file);
++ fput(isock->file);
++ return -EBADFD;
++ }
++
++ err = hidp_add_connection(&ca, csock, isock);
++ if (!err) {
++ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++ err = -EFAULT;
++ } else {
++ fput(csock->file);
++ fput(isock->file);
++ }
++
++ return err;
++
++ case HIDPCONNDEL:
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++ return -EFAULT;
++
++ return hidp_del_connection(&cd);
++
++ case HIDPGETCONNLIST:
++ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++ return -EFAULT;
++
++ if (cl.cnum <= 0)
++ return -EINVAL;
++
++ err = hidp_get_connlist(&cl);
++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++ return -EFAULT;
++
++ return err;
++
++ case HIDPGETCONNINFO:
++ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++ return -EFAULT;
++
++ err = hidp_get_conninfo(&ci);
++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++ return -EFAULT;
++
++ return err;
++ }
++
++ return -EINVAL;
++}
++
++static struct proto_ops hidp_sock_ops = {
++ family: PF_BLUETOOTH,
++ release: hidp_sock_release,
++ ioctl: hidp_sock_ioctl,
++ bind: sock_no_bind,
++ getname: sock_no_getname,
++ sendmsg: sock_no_sendmsg,
++ recvmsg: sock_no_recvmsg,
++ poll: sock_no_poll,
++ listen: sock_no_listen,
++ shutdown: sock_no_shutdown,
++ setsockopt: sock_no_setsockopt,
++ getsockopt: sock_no_getsockopt,
++ connect: sock_no_connect,
++ socketpair: sock_no_socketpair,
++ accept: sock_no_accept,
++ mmap: sock_no_mmap
++};
++
++static int hidp_sock_create(struct socket *sock, int protocol)
++{
++ struct sock *sk;
++
++ BT_DBG("sock %p", sock);
++
++ if (sock->type != SOCK_RAW)
++ return -ESOCKTNOSUPPORT;
++
++ sock->ops = &hidp_sock_ops;
++
++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++ return -ENOMEM;
++
++ MOD_INC_USE_COUNT;
++
++ sock->state = SS_UNCONNECTED;
++ sock_init_data(sock, sk);
++
++ sk->destruct = NULL;
++ sk->protocol = protocol;
++
++ return 0;
++}
++
++static struct net_proto_family hidp_sock_family_ops = {
++ family: PF_BLUETOOTH,
++ create: hidp_sock_create
++};
++
++int __init hidp_init_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops)))
++ BT_ERR("Can't register HIDP socket layer (%d)", err);
++
++ return err;
++}
++
++void __exit hidp_cleanup_sockets(void)
++{
++ int err;
++
++ if ((err = bluez_sock_unregister(BTPROTO_HIDP)))
++ BT_ERR("Can't unregister HIDP socket layer (%d)", err);
++}
+--- linux-2.4.21/net/bluetooth/l2cap.c~bluetooth
++++ linux-2.4.21/net/bluetooth/l2cap.c
+@@ -27,7 +27,7 @@
+ *
+ * $Id$
+ */
+-#define VERSION "2.1"
++#define VERSION "2.3"
+
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -284,7 +284,7 @@
+ l2cap_disconn_req req;
+
+ sk->state = BT_DISCONN;
+- l2cap_sock_set_timer(sk, HZ * 5);
++ 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);
+@@ -309,11 +309,9 @@
+ 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);
+ }
+
+@@ -527,7 +525,8 @@
+ goto done;
+
+ wait:
+- err = bluez_sock_w4_connect(sk, flags);
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+ done:
+ release_sock(sk);
+@@ -760,32 +759,39 @@
+ 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;
+
+- l2cap_sock_clear_timer(sk);
+-
+ lock_sock(sk);
+- sk->shutdown = SHUTDOWN_MASK;
+- __l2cap_sock_close(sk, ECONNRESET);
+- release_sock(sk);
++ if (!sk->shutdown) {
++ sk->shutdown = SHUTDOWN_MASK;
++ l2cap_sock_clear_timer(sk);
++ __l2cap_sock_close(sk, 0);
+
+- return 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_close(sk);
+- return 0;
++ l2cap_sock_kill(sk);
++ return err;
+ }
+
+ /* --------- L2CAP channels --------- */
+@@ -917,10 +923,12 @@
+ hci_conn_put(conn->hcon);
+ }
+
+- sk->state = BT_CLOSED;
+- sk->err = err;
++ sk->state = BT_CLOSED;
+ sk->zapped = 1;
+
++ if (err)
++ sk->err = err;
++
+ if (parent)
+ parent->data_ready(parent, 0);
+ else
+@@ -956,6 +964,22 @@
+ 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;
+@@ -1320,15 +1344,18 @@
+ {
+ 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(0);
++ rsp->flags = __cpu_to_le16(flags);
+
+ return ptr - data;
+ }
+@@ -1441,7 +1468,7 @@
+ case L2CAP_CR_SUCCESS:
+ sk->state = BT_CONFIG;
+ l2cap_pi(sk)->dcid = dcid;
+- l2cap_pi(sk)->conf_state |= CONF_REQ_SENT;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+
+ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+ break;
+@@ -1476,7 +1503,7 @@
+
+ l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
+
+- if (flags & 0x01) {
++ 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;
+@@ -1489,12 +1516,12 @@
+ goto unlock;
+
+ /* Output config done */
+- l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+
+- if (l2cap_pi(sk)->conf_state & CONF_INPUT_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 & CONF_REQ_SENT)) {
++ } 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);
+ }
+@@ -1520,18 +1547,34 @@
+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+ return -ENOENT;
+
+- if (result) {
+- l2cap_disconn_req req;
++ switch (result) {
++ case L2CAP_CONF_SUCCESS:
++ break;
+
+- /* They didn't like our options. Well... we do not negotiate.
+- * Close channel.
+- */
++ 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);
+-
+- 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_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;
+ }
+
+@@ -1539,9 +1582,9 @@
+ goto done;
+
+ /* Input config done */
+- l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE;
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;
+
+- if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) {
++ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+ sk->state = BT_CONNECTED;
+ l2cap_chan_ready(sk);
+ }
+@@ -1592,13 +1635,42 @@
+
+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+ return 0;
+- l2cap_chan_del(sk, ECONNABORTED);
++ l2cap_chan_del(sk, 0);
+ bh_unlock_sock(sk);
+
+ l2cap_sock_kill(sk);
+ return 0;
+ }
+
++static inline int l2cap_information_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
++{
++ l2cap_info_req *req = (l2cap_info_req *) data;
++ l2cap_info_rsp rsp;
++ u16 type;
++
++ type = __le16_to_cpu(req->type);
++
++ BT_DBG("type 0x%4.4x", type);
++
++ rsp.type = __cpu_to_le16(type);
++ rsp.result = __cpu_to_le16(L2CAP_IR_NOTSUPP);
++ l2cap_send_rsp(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp);
++ return 0;
++}
++
++static inline int l2cap_information_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
++{
++ l2cap_info_rsp *rsp = (l2cap_info_rsp *) data;
++ u16 type, result;
++
++ type = __le16_to_cpu(rsp->type);
++ result = __le16_to_cpu(rsp->result);
++
++ BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
++
++ return 0;
++}
++
+ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+ {
+ __u8 *data = skb->data;
+@@ -1606,6 +1678,8 @@
+ l2cap_cmd_hdr cmd;
+ int err = 0;
+
++ l2cap_raw_recv(conn, skb);
++
+ while (len >= L2CAP_CMD_HDR_SIZE) {
+ memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
+ data += L2CAP_CMD_HDR_SIZE;
+@@ -1621,6 +1695,10 @@
+ }
+
+ switch (cmd.code) {
++ case L2CAP_COMMAND_REJ:
++ /* FIXME: We should process this */
++ break;
++
+ case L2CAP_CONN_REQ:
+ err = l2cap_connect_req(conn, &cmd, data);
+ break;
+@@ -1645,23 +1723,23 @@
+ 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:
++ break;
++
+ case L2CAP_INFO_REQ:
++ err = l2cap_information_req(conn, &cmd, data);
++ break;
++
+ case L2CAP_INFO_RSP:
+- l2cap_raw_recv(conn, skb);
++ err = l2cap_information_rsp(conn, &cmd, data);
+ break;
+
+ default:
+- BT_ERR("Uknown signaling command 0x%2.2x", cmd.code);
++ BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);
+ err = -EINVAL;
+ break;
+ };
+@@ -1670,7 +1748,7 @@
+ l2cap_cmd_rej rej;
+ BT_DBG("error %d", err);
+
+- /* FIXME: Map err to a valid reason. */
++ /* 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);
+ }
+@@ -1943,26 +2021,36 @@
+ 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 small (len %d)", skb->len);
++ 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;
+
+- BT_DBG("Start: total len %d, frag len %d", len, skb->len);
+-
+ if (len == skb->len) {
+ /* Complete frame received */
+ l2cap_recv_frame(conn, skb);
+ return 0;
+ }
+
+- /* Allocate skb for the complete frame (with header) */
+- if (!(conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC)))
++ 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);
+@@ -1972,15 +2060,17 @@
+
+ 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 large (len %d, expect %d)",
++ 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;
+ }
+
+@@ -2114,6 +2204,16 @@
+ 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);
+
+--- linux-2.4.21/net/bluetooth/rfcomm/core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/core.c
+@@ -51,7 +51,7 @@
+ #include <net/bluetooth/l2cap.h>
+ #include <net/bluetooth/rfcomm.h>
+
+-#define VERSION "1.0"
++#define VERSION "1.1"
+
+ #ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
+ #undef BT_DBG
+@@ -198,10 +198,11 @@
+
+ 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 = RFCOMM_MAX_CREDITS;
++ d->cfc = RFCOMM_CFC_DISABLED;
+ d->rx_credits = RFCOMM_DEFAULT_CREDITS;
+ }
+
+@@ -274,13 +275,13 @@
+ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
+ {
+ struct rfcomm_session *s;
+- u8 dlci = __dlci(0, channel);
+ int err = 0;
++ u8 dlci;
+
+- BT_DBG("dlc %p state %ld %s %s channel %d dlci %d",
+- d, d->state, batostr(src), batostr(dst), channel, dlci);
++ BT_DBG("dlc %p state %ld %s %s channel %d",
++ d, d->state, batostr(src), batostr(dst), channel);
+
+- if (dlci < 1 || dlci > 62)
++ if (channel < 1 || channel > 30)
+ return -EINVAL;
+
+ if (d->state != BT_OPEN && d->state != BT_CLOSED)
+@@ -293,20 +294,23 @@
+ 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->dlci = dlci;
++ d->addr = __addr(s->initiator, dlci);
++ d->priority = 7;
+
+- d->state = BT_CONFIG;
++ d->state = BT_CONFIG;
+ rfcomm_dlc_link(s, d);
+
+- d->mtu = s->mtu;
+- d->credits = s->credits;
++ d->mtu = s->mtu;
++ d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
+
+ if (s->state == BT_CONNECTED)
+ rfcomm_send_pn(s, 1, d);
+@@ -406,7 +410,7 @@
+ {
+ BT_DBG("dlc %p state %ld", d, d->state);
+
+- if (!d->credits) {
++ if (!d->cfc) {
+ d->v24_sig |= RFCOMM_V24_FC;
+ set_bit(RFCOMM_MSC_PENDING, &d->flags);
+ }
+@@ -417,7 +421,7 @@
+ {
+ BT_DBG("dlc %p state %ld", d, d->state);
+
+- if (!d->credits) {
++ if (!d->cfc) {
+ d->v24_sig &= ~RFCOMM_V24_FC;
+ set_bit(RFCOMM_MSC_PENDING, &d->flags);
+ }
+@@ -470,8 +474,8 @@
+ s->state = state;
+ s->sock = sock;
+
+- s->mtu = RFCOMM_DEFAULT_MTU;
+- s->credits = RFCOMM_MAX_CREDITS;
++ s->mtu = RFCOMM_DEFAULT_MTU;
++ s->cfc = RFCOMM_CFC_UNKNOWN;
+
+ list_add(&s->list, &session_list);
+
+@@ -707,7 +711,7 @@
+ hdr->len = __len8(sizeof(*mcc) + 1);
+
+ mcc = (void *) ptr; ptr += sizeof(*mcc);
+- mcc->type = __mcc_type(s->initiator, RFCOMM_NSC);
++ mcc->type = __mcc_type(cr, RFCOMM_NSC);
+ mcc->len = __len8(1);
+
+ /* Type that we didn't like */
+@@ -733,16 +737,16 @@
+ hdr->len = __len8(sizeof(*mcc) + sizeof(*pn));
+
+ mcc = (void *) ptr; ptr += sizeof(*mcc);
+- mcc->type = __mcc_type(s->initiator, RFCOMM_PN);
++ mcc->type = __mcc_type(cr, RFCOMM_PN);
+ mcc->len = __len8(sizeof(*pn));
+
+ pn = (void *) ptr; ptr += sizeof(*pn);
+ pn->dlci = d->dlci;
+- pn->priority = 0;
++ pn->priority = d->priority;
+ pn->ack_timer = 0;
+ pn->max_retrans = 0;
+
+- if (d->credits) {
++ if (s->cfc) {
+ pn->flow_ctrl = cr ? 0xf0 : 0xe0;
+ pn->credits = RFCOMM_DEFAULT_CREDITS;
+ } else {
+@@ -842,7 +846,51 @@
+
+ msc = (void *) ptr; ptr += sizeof(*msc);
+ msc->dlci = __addr(1, dlci);
+- msc->v24_sig = v24_sig;
++ 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++;
+
+@@ -1076,6 +1124,8 @@
+ 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);
+ }
+@@ -1085,29 +1135,23 @@
+
+ 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) {
+- d->tx_credits = pn->credits;
+- } else {
+- set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+- d->credits = 0;
+- }
+-
+- d->mtu = btohs(pn->mtu);
++ if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) {
++ d->cfc = s->cfc = RFCOMM_CFC_ENABLED;
++ d->tx_credits = pn->credits;
+ } else {
+- if (pn->flow_ctrl == 0xe0) {
+- d->tx_credits = pn->credits;
+- } else {
+- set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+- d->credits = 0;
+- }
+-
+- d->mtu = btohs(pn->mtu);
++ d->cfc = s->cfc = RFCOMM_CFC_DISABLED;
++ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+ }
+
++ d->priority = pn->priority;
++
++ d->mtu = s->mtu = btohs(pn->mtu);
++
+ return 0;
+ }
+
+@@ -1133,7 +1177,7 @@
+ switch (d->state) {
+ case BT_CONFIG:
+ rfcomm_apply_pn(d, cr, pn);
+-
++
+ d->state = BT_CONNECT;
+ rfcomm_send_sabm(s, d->dlci);
+ break;
+@@ -1144,7 +1188,7 @@
+
+ if (!cr)
+ return 0;
+-
++
+ /* PN request for non existing DLC.
+ * Assume incomming connection. */
+ if (rfcomm_connect_ind(s, channel, &d)) {
+@@ -1153,7 +1197,7 @@
+ rfcomm_dlc_link(s, d);
+
+ rfcomm_apply_pn(d, cr, pn);
+-
++
+ d->state = BT_OPEN;
+ rfcomm_send_pn(s, 0, d);
+ } else {
+@@ -1198,6 +1242,14 @@
+ }
+ /* 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) {
+@@ -1223,23 +1275,26 @@
+ }
+ }
+ if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
+- if (rpn->flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
+- BT_DBG("RPN flow ctrl mismatch 0x%x", rpn->flow_ctrl);
+- rpn->flow_ctrl = RFCOMM_RPN_FLOW_NONE;
++ 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) {
+- if (rpn->xon_char != RFCOMM_RPN_XON_CHAR) {
+- BT_DBG("RPN XON char mismatch 0x%x", rpn->xon_char);
+- rpn->xon_char = RFCOMM_RPN_XON_CHAR;
++ 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) {
+- if (rpn->xoff_char != RFCOMM_RPN_XOFF_CHAR) {
+- BT_DBG("RPN XOFF char mismatch 0x%x", rpn->xoff_char);
+- rpn->xoff_char = RFCOMM_RPN_XOFF_CHAR;
++ 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;
+ }
+ }
+@@ -1280,12 +1335,12 @@
+
+ BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
+
+- if (!cr)
++ d = rfcomm_dlc_get(s, dlci);
++ if (!d)
+ return 0;
+
+- d = rfcomm_dlc_get(s, dlci);
+- if (d) {
+- if (msc->v24_sig & RFCOMM_V24_FC && !d->credits)
++ if (cr) {
++ if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc)
+ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+ else
+ clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
+@@ -1296,7 +1351,11 @@
+ 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;
+ }
+
+@@ -1330,6 +1389,20 @@
+ 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);
+@@ -1358,7 +1431,7 @@
+ goto drop;
+ }
+
+- if (pf && d->credits) {
++ if (pf && d->cfc) {
+ u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
+
+ d->tx_credits += credits;
+@@ -1463,20 +1536,20 @@
+ 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);
++ BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d",
++ d, d->state, d->cfc, d->rx_credits, d->tx_credits);
+
+ /* Send pending MSC */
+ if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
+ rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
+
+- if (d->credits) {
++ if (d->cfc) {
+ /* 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;
++ d->rx_credits <= (d->cfc >> 2)) {
++ rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits);
++ d->rx_credits = d->cfc;
+ }
+ } else {
+ /* CFC disabled.
+@@ -1497,7 +1570,7 @@
+ d->tx_credits--;
+ }
+
+- if (d->credits && !d->tx_credits) {
++ if (d->cfc && !d->tx_credits) {
+ /* We're out of TX credits.
+ * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */
+ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+@@ -1520,7 +1593,11 @@
+ continue;
+ }
+
+- if (d->state == BT_CONNECTED || d->state == BT_DISCONN)
++ 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);
+ }
+ }
+@@ -1577,9 +1654,10 @@
+ nsock->sk->state_change = rfcomm_l2state_change;
+
+ s = rfcomm_session_add(nsock, BT_OPEN);
+- if (s)
++ if (s) {
+ rfcomm_session_hold(s);
+- else
++ rfcomm_schedule(RFCOMM_SCHED_RX);
++ } else
+ sock_release(nsock);
+ }
+
+@@ -1815,6 +1893,8 @@
+ /* ---- Initialization ---- */
+ int __init rfcomm_init(void)
+ {
++ l2cap_load();
++
+ kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+
+ rfcomm_init_sockets();
+--- linux-2.4.21/net/bluetooth/rfcomm/sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/sock.c
+@@ -188,8 +188,10 @@
+ BT_DBG("parent %p", parent);
+
+ /* Close not yet accepted dlcs */
+- while ((sk = bluez_accept_dequeue(parent, NULL)))
++ while ((sk = bluez_accept_dequeue(parent, NULL))) {
+ rfcomm_sock_close(sk);
++ rfcomm_sock_kill(sk);
++ }
+
+ parent->state = BT_CLOSED;
+ parent->zapped = 1;
+@@ -211,15 +213,10 @@
+ sock_put(sk);
+ }
+
+-/* Close socket.
+- * Must be called on unlocked socket.
+- */
+-static void rfcomm_sock_close(struct sock *sk)
++static void __rfcomm_sock_close(struct sock *sk)
+ {
+ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+
+- lock_sock(sk);
+-
+ BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
+
+ switch (sk->state) {
+@@ -236,11 +233,17 @@
+ 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);
+-
+- rfcomm_sock_kill(sk);
+ }
+
+ static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
+@@ -374,7 +377,8 @@
+
+ err = rfcomm_dlc_open(d, &bluez_pi(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
+ if (!err)
+- err = bluez_sock_w4_connect(sk, flags);
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+ release_sock(sk);
+ return err;
+@@ -558,9 +562,6 @@
+ int target, err = 0, copied = 0;
+ long timeo;
+
+- if (sk->state != BT_CONNECTED)
+- return -EINVAL;
+-
+ if (flags & MSG_OOB)
+ return -EOPNOTSUPP;
+
+@@ -635,23 +636,6 @@
+ return copied ? : err;
+ }
+
+-static int rfcomm_sock_shutdown(struct socket *sock, int how)
+-{
+- struct sock *sk = sock->sk;
+-
+- BT_DBG("sock %p, sk %p", sock, sk);
+-
+- if (!sk) return 0;
+-
+- lock_sock(sk);
+- sk->shutdown = SHUTDOWN_MASK;
+- if (sk->state == BT_CONNECTED)
+- rfcomm_dlc_close(rfcomm_pi(sk)->dlc, 0);
+- release_sock(sk);
+-
+- return 0;
+-}
+-
+ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+ {
+ struct sock *sk = sock->sk;
+@@ -711,19 +695,42 @@
+ 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;
+
+- sock_orphan(sk);
+- rfcomm_sock_close(sk);
++ err = rfcomm_sock_shutdown(sock, 2);
+
+- return 0;
++ sock_orphan(sk);
++ rfcomm_sock_kill(sk);
++ return err;
+ }
+
+ /* ---- RFCOMM core layer callbacks ----
+--- linux-2.4.21/net/bluetooth/rfcomm/tty.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/tty.c
+@@ -109,6 +109,13 @@
+
+ static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
+ {
++ /* The reason this isn't actually a race, as you no
++ doubt have a little voice screaming at you in your
++ head, is that the refcount should never actually
++ reach zero unless the device has already been taken
++ off the list, in rfcomm_dev_del(). And if that's not
++ true, we'll hit the BUG() in rfcomm_dev_destruct()
++ anyway. */
+ if (atomic_dec_and_test(&dev->refcnt))
+ rfcomm_dev_destruct(dev);
+ }
+@@ -132,10 +139,13 @@
+ struct rfcomm_dev *dev;
+
+ read_lock(&rfcomm_dev_lock);
++
+ dev = __rfcomm_dev_get(id);
++ if (dev)
++ rfcomm_dev_hold(dev);
++
+ read_unlock(&rfcomm_dev_lock);
+
+- if (dev) rfcomm_dev_hold(dev);
+ return dev;
+ }
+
+@@ -260,9 +270,9 @@
+ skb->destructor = rfcomm_wfree;
+ }
+
+-static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int priority)
++static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority)
+ {
+- if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
++ 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);
+@@ -328,12 +338,14 @@
+
+ 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 (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) {
++ rfcomm_dev_put(dev);
++ return -EPERM;
++ }
++
+ if (req.flags & (1 << RFCOMM_HANGUP_NOW))
+ rfcomm_dlc_close(dev->dlc, 0);
+
+@@ -347,7 +359,7 @@
+ struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di;
+ struct list_head *p;
+- int n = 0, size;
++ int n = 0, size, err;
+ u16 dev_num;
+
+ BT_DBG("");
+@@ -355,14 +367,11 @@
+ if (get_user(dev_num, (u16 *) arg))
+ return -EFAULT;
+
+- if (!dev_num)
++ if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di))
+ 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;
+
+@@ -387,9 +396,10 @@
+ dl->dev_num = n;
+ size = sizeof(*dl) + n * sizeof(*di);
+
+- copy_to_user((void *) arg, dl, size);
++ err = copy_to_user((void *) arg, dl, size);
+ kfree(dl);
+- return 0;
++
++ return err ? -EFAULT : 0;
+ }
+
+ static int rfcomm_get_dev_info(unsigned long arg)
+@@ -486,7 +496,8 @@
+ 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_dev_put() will dead lock if it's
++ the last reference. */
+ rfcomm_dlc_unlock(dlc);
+ rfcomm_dev_put(dev);
+ rfcomm_dlc_lock(dlc);
+@@ -541,6 +552,10 @@
+
+ BT_DBG("tty %p id %d", tty, id);
+
++ /* We don't leak this refcount. For reasons which are not entirely
++ clear, the TTY layer will call our ->close() method even if the
++ open fails. We decrease the refcount there, and decreasing it
++ here too would cause breakage. */
+ dev = rfcomm_dev_get(id);
+ if (!dev)
+ return -ENODEV;
+@@ -627,9 +642,9 @@
+ size = min_t(uint, count, dlc->mtu);
+
+ if (from_user)
+- skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_KERNEL);
++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL);
+ else
+- skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC);
++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_ATOMIC);
+
+ if (!skb)
+ break;
+@@ -653,6 +668,27 @@
+ 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;
+@@ -879,6 +915,7 @@
+
+ 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,
+--- linux-2.4.21/net/bluetooth/sco.c~bluetooth
++++ linux-2.4.21/net/bluetooth/sco.c
+@@ -332,8 +332,10 @@
+ BT_DBG("parent %p", parent);
+
+ /* Close not yet accepted channels */
+- while ((sk = bluez_accept_dequeue(parent, NULL)))
++ while ((sk = bluez_accept_dequeue(parent, NULL))) {
+ sco_sock_close(sk);
++ sco_sock_kill(sk);
++ }
+
+ parent->state = BT_CLOSED;
+ parent->zapped = 1;
+@@ -388,8 +390,6 @@
+ };
+
+ release_sock(sk);
+-
+- sco_sock_kill(sk);
+ }
+
+ static void sco_sock_init(struct sock *sk, struct sock *parent)
+@@ -508,7 +508,8 @@
+ if ((err = sco_connect(sk)))
+ goto done;
+
+- err = bluez_sock_w4_connect(sk, flags);
++ err = bluez_sock_wait_state(sk, BT_CONNECTED,
++ sock_sndtimeo(sk, flags & O_NONBLOCK));
+
+ done:
+ release_sock(sk);
+@@ -712,16 +713,23 @@
+ 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;
+
+- sock_orphan(sk);
+ sco_sock_close(sk);
++ if (sk->linger) {
++ lock_sock(sk);
++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++ release_sock(sk);
++ }
+
+- return 0;
++ sock_orphan(sk);
++ sco_sock_kill(sk);
++ return err;
+ }
+
+ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+--- linux-2.4.21/net/bluetooth/syms.c~bluetooth
++++ linux-2.4.21/net/bluetooth/syms.c
+@@ -78,4 +78,4 @@
+ EXPORT_SYMBOL(bluez_sock_poll);
+ EXPORT_SYMBOL(bluez_accept_enqueue);
+ EXPORT_SYMBOL(bluez_accept_dequeue);
+-EXPORT_SYMBOL(bluez_sock_w4_connect);
++EXPORT_SYMBOL(bluez_sock_wait_state);
--- linux-2.4.21/net/core/wireless.c~linux-iw241_we16-6
+++ linux-2.4.21/net/core/wireless.c
@@ -2,7 +2,7 @@
@@ -100115,7 +108048,15 @@
/* Oh, well. At least we tried. */
--- linux-2.4.21/net/netsyms.c~linux-iw241_we16-6
+++ linux-2.4.21/net/netsyms.c
-@@ -601,6 +601,11 @@
+@@ -160,6 +160,7 @@
+ EXPORT_SYMBOL(put_cmsg);
+ EXPORT_SYMBOL(sock_kmalloc);
+ EXPORT_SYMBOL(sock_kfree_s);
++EXPORT_SYMBOL(sockfd_lookup);
+
+ #ifdef CONFIG_FILTER
+ EXPORT_SYMBOL(sk_run_filter);
+@@ -601,6 +602,11 @@
#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
#include <net/iw_handler.h>
EXPORT_SYMBOL(wireless_send_event);