diff options
author | nslu2-linux.adm@bkbits.net <nslu2-linux.adm@bkbits.net> | 2005-03-30 13:19:04 +0000 |
---|---|---|
committer | nslu2-linux.adm@bkbits.net <nslu2-linux.adm@bkbits.net> | 2005-03-30 13:19:04 +0000 |
commit | 99c724568b2c8e6d1b9857435ebe601854615f53 (patch) | |
tree | 87f55bc20ac87dcbd88bbdafeae608e8cf837510 /packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch | |
parent | ac58f168c434bc09037c9e3b74b989c12d1a5d75 (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.patch | 8135 |
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(¤t->sigmask_lock); +- tmpsig = current->blocked; +- siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)); +- recalc_sigpending(current); +- spin_unlock_irq(¤t->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(¤t->sigmask_lock); +- current->blocked = tmpsig; +- recalc_sigpending(current); +- spin_unlock_irq(¤t->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(¤t->blocked); ++ sprintf(current->comm, "kbnepd %s", dev->name); ++ ++ sigfillset(¤t->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(¤t->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(¤t->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); |