diff options
author | Michael Lauer <mickey@vanille-media.de> | 2005-08-22 10:30:30 +0000 |
---|---|---|
committer | OpenEmbedded Project <openembedded-devel@lists.openembedded.org> | 2005-08-22 10:30:30 +0000 |
commit | a1392aaa1d146d105ebf7a6528b91d5746dc0b89 (patch) | |
tree | 544be87e85beb17b818921c5ee851ca7c0c87863 /packages/linux/openzaurus-pxa27x-2.4.20-rmk2-embedix20050602/P11-bluetooth-mh18_041216.patch | |
parent | a98fc361734009b592044b9b73dfdce3f20caebd (diff) |
add support for the Sharp SL-C3100 (Borzoi)
Diffstat (limited to 'packages/linux/openzaurus-pxa27x-2.4.20-rmk2-embedix20050602/P11-bluetooth-mh18_041216.patch')
-rw-r--r-- | packages/linux/openzaurus-pxa27x-2.4.20-rmk2-embedix20050602/P11-bluetooth-mh18_041216.patch | 16593 |
1 files changed, 16593 insertions, 0 deletions
diff --git a/packages/linux/openzaurus-pxa27x-2.4.20-rmk2-embedix20050602/P11-bluetooth-mh18_041216.patch b/packages/linux/openzaurus-pxa27x-2.4.20-rmk2-embedix20050602/P11-bluetooth-mh18_041216.patch new file mode 100644 index 0000000000..0a2b7fbde4 --- /dev/null +++ b/packages/linux/openzaurus-pxa27x-2.4.20-rmk2-embedix20050602/P11-bluetooth-mh18_041216.patch @@ -0,0 +1,16593 @@ +diff -Nur c3000_pre/linux/CREDITS c3000_work/linux/CREDITS +--- c3000_pre/linux/CREDITS 2004-08-21 09:48:05.000000000 +0900 ++++ c3000_work/linux/CREDITS 2004-12-16 23:01:14.000000000 +0900 +@@ -1344,7 +1344,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 +@@ -2592,6 +2596,7 @@ + N: Aristeu Sergio Rozanski Filho + E: aris@conectiva.com.br + D: Support for EtherExpress 10 ISA (i82595) in eepro driver ++D: User level driver support for input + S: Conectiva S.A. + S: R. Tocantins, 89 - Cristo Rei + S: 80050-430 - Curitiba - Paraná +diff -Nur c3000_pre/linux/Documentation/Configure.help c3000_work/linux/Documentation/Configure.help +--- c3000_pre/linux/Documentation/Configure.help 2004-08-21 09:48:06.000000000 +0900 ++++ c3000_work/linux/Documentation/Configure.help 2004-12-16 23:01:14.000000000 +0900 +@@ -12238,6 +12238,12 @@ + + If unsure, say N. + ++Hotplug firmware loading support (EXPERIMENTAL) ++CONFIG_FW_LOADER ++ This option is provided for the case where no in-kernel-tree modules require ++ hotplug firmware loading support, but a module built outside the kernel tree ++ does. ++ + Use PCI shared memory for NIC registers + CONFIG_TULIP_MMIO + Use PCI shared memory for the NIC registers, rather than going through +@@ -14373,6 +14379,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 +@@ -21868,19 +21883,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) ++ 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 +@@ -21893,25 +21910,60 @@ + + 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 + compile it as module (sco.o). + ++RFCOMM protocol support ++CONFIG_BLUEZ_RFCOMM ++ RFCOMM provides connection oriented stream transport. RFCOMM ++ support is required for Dialup Networking, OBEX and other Bluetooth ++ applications. ++ ++ Say Y here to compile RFCOMM support into the kernel or say M to ++ compile it as module (rfcomm.o). ++ ++RFCOMM TTY emulation support ++CONFIG_BLUEZ_RFCOMM_TTY ++ This option enables TTY emulation support for RFCOMM channels. ++ + BNEP protocol support + CONFIG_BLUEZ_BNEP + BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet +- emulation layer on top of Bluetooth. BNEP is required for Bluetooth +- PAN (Personal Area Network). +- +- To use BNEP, you will need user-space utilities provided in the +- BlueZ-PAN package. +- For more information, see <http://bluez.sourceforge.net>. ++ emulation layer on top of Bluetooth. BNEP is required for ++ Bluetooth PAN (Personal Area Network). + + Say Y here to compile BNEP support into the kernel or say M to + compile it as module (bnep.o). + ++BNEP multicast filter support ++CONFIG_BLUEZ_BNEP_MC_FILTER ++ This option enables the multicast filter support for BNEP. ++ ++BNEP protocol filter support ++CONFIG_BLUEZ_BNEP_PROTO_FILTER ++ This option enables the protocol filter support for BNEP. ++ ++CMTP protocol support ++CONFIG_BLUEZ_CMTP ++ CMTP (CAPI Message Transport Protocol) is a transport layer ++ for CAPI messages. CMTP is required for the Bluetooth Common ++ ISDN Access Profile. ++ ++ Say Y here to compile CMTP support into the kernel or say M to ++ compile it as module (cmtp.o). ++ ++HIDP protocol support ++CONFIG_BLUEZ_HIDP ++ HIDP (Human Interface Device Protocol) is a transport layer ++ for HID reports. HIDP is required for the Bluetooth Human ++ Interface Device Profile. ++ ++ Say Y here to compile HIDP support into the kernel or say M to ++ compile it as module (hidp.o). ++ + HCI UART driver + CONFIG_BLUEZ_HCIUART + Bluetooth HCI UART driver. +@@ -21926,11 +21978,26 @@ + HCI UART (H4) protocol support + CONFIG_BLUEZ_HCIUART_H4 + UART (H4) is serial protocol for communication between Bluetooth +- device and host. This protocol is required for most UART based +- Bluetooth device (including PCMCIA and CF). ++ device and host. This protocol is required for most Bluetooth devices ++ with UART interface, including PCMCIA and CF cards. + + Say Y here to compile support for HCI UART (H4) protocol. + ++HCI BCSP protocol support ++CONFIG_BLUEZ_HCIUART_BCSP ++ BCSP (BlueCore Serial Protocol) is serial protocol for communication ++ between Bluetooth device and host. This protocol is required for non ++ USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and ++ CF cards. ++ ++ Say Y here to compile support for HCI BCSP protocol. ++ ++HCI BCSP transmit CRC with every BCSP packet ++CONFIG_BLUEZ_HCIUART_BCSP_TXCRC ++ If you say Y here, a 16-bit CRC checksum will be transmitted along with ++ every BCSP (BlueCore Serial Protocol) packet sent to the Bluetooth chip. ++ This increases reliability, but slightly reduces efficiency. ++ + HCI USB driver + CONFIG_BLUEZ_HCIUSB + Bluetooth HCI USB driver. +@@ -21940,14 +22007,15 @@ + Say Y here to compile support for Bluetooth USB devices into the + kernel or say M to compile it as module (hci_usb.o). + +-HCI USB zero packet support +-CONFIG_BLUEZ_USB_ZERO_PACKET +- Support for USB zero packets. +- 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 USB SCO (voice) support ++CONFIG_BLUEZ_HCIUSB_SCO ++ This option enables the SCO support in the HCI USB driver. You need this ++ to transmit voice data with your Bluetooth USB device. And your device ++ must also support sending SCO data over the HCI layer, because some of ++ them sends the SCO data to an internal PCM adapter. ++ ++ Say Y here to compile support for HCI SCO data. ++ + HCI VHCI Virtual HCI device driver + CONFIG_BLUEZ_HCIVHCI + Bluetooth Virtual HCI device driver. +@@ -21956,6 +22024,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. +@@ -21975,9 +22053,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). + +@@ -21992,6 +22067,20 @@ + Say Y here to compile support for HCI BlueCard devices into the + kernel or say M to compile it as module (bluecard_cs.o). + ++HCI UART (PC Card) device driver ++CONFIG_BLUEZ_HCIBTUART ++ Bluetooth HCI UART (PC Card) driver. ++ This driver provides support for Bluetooth PCMCIA devices with ++ an UART interface: ++ Xircom CreditCard Bluetooth Adapter ++ Xircom RealPort2 Bluetooth Adapter ++ Sphinx PICO Card ++ H-Soft blue+Card ++ Cyber-blue Compact Flash Card ++ ++ Say Y here to compile support for HCI UART devices into the ++ kernel or say M to compile it as module (btuart_cs.o). ++ + # The following options are for Linux when running on the Hitachi + # SuperH family of RISC microprocessors. + +diff -Nur c3000_pre/linux/Documentation/devices.txt c3000_work/linux/Documentation/devices.txt +--- c3000_pre/linux/Documentation/devices.txt 2004-08-21 09:48:06.000000000 +0900 ++++ c3000_work/linux/Documentation/devices.txt 2004-12-16 23:01:14.000000000 +0900 +@@ -419,6 +419,7 @@ + 220 = /dev/mptctl Message passing technology (MPT) control + 221 = /dev/mvista/hssdsi Montavista PICMG hot swap system driver + 222 = /dev/mvista/hasi Montavista PICMG high availability ++ 223 = /dev/input/uinput User level driver support for input + 240-255 Reserved for local use + + 11 char Raw keyboard device +diff -Nur c3000_pre/linux/Documentation/firmware_class/README c3000_work/linux/Documentation/firmware_class/README +--- c3000_pre/linux/Documentation/firmware_class/README 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/Documentation/firmware_class/README 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,58 @@ ++ ++ request_firmware() hotplug interface: ++ ------------------------------------ ++ Copyright (C) 2003 Manuel Estrada Sainz <ranty@debian.org> ++ ++ Why: ++ --- ++ ++ Today, the most extended way to use firmware in the Linux kernel is linking ++ it statically in a header file. Which has political and technical issues: ++ ++ 1) Some firmware is not legal to redistribute. ++ 2) The firmware occupies memory permanently, even though it often is just ++ used once. ++ 3) Some people, like the Debian crowd, don't consider some firmware free ++ enough and remove entire drivers (e.g.: keyspan). ++ ++ about in-kernel persistence: ++ --------------------------- ++ Under some circumstances, as explained below, it would be interesting to keep ++ firmware images in non-swappable kernel memory or even in the kernel image ++ (probably within initramfs). ++ ++ Note that this functionality has not been implemented. ++ ++ - Why OPTIONAL in-kernel persistence may be a good idea sometimes: ++ ++ - If the device that needs the firmware is needed to access the ++ filesystem. When upon some error the device has to be reset and the ++ firmware reloaded, it won't be possible to get it from userspace. ++ e.g.: ++ - A diskless client with a network card that needs firmware. ++ - The filesystem is stored in a disk behind an scsi device ++ that needs firmware. ++ - Replacing buggy DSDT/SSDT ACPI tables on boot. ++ Note: this would require the persistent objects to be included ++ within the kernel image, probably within initramfs. ++ ++ And the same device can be needed to access the filesystem or not depending ++ on the setup, so I think that the choice on what firmware to make ++ persistent should be left to userspace. ++ ++ - Why register_firmware()+__init can be useful: ++ - For boot devices needing firmware. ++ - To make the transition easier: ++ The firmware can be declared __init and register_firmware() ++ called on module_init. Then the firmware is warranted to be ++ there even if "firmware hotplug userspace" is not there yet or ++ it doesn't yet provide the needed firmware. ++ Once the firmware is widely available in userspace, it can be ++ removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE). ++ ++ In either case, if firmware hotplug support is there, it can move the ++ firmware out of kernel memory into the real filesystem for later ++ usage. ++ ++ Note: If persistence is implemented on top of initramfs, ++ register_firmware() may not be appropriate. +diff -Nur c3000_pre/linux/Documentation/firmware_class/firmware_sample_driver.c c3000_work/linux/Documentation/firmware_class/firmware_sample_driver.c +--- c3000_pre/linux/Documentation/firmware_class/firmware_sample_driver.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/Documentation/firmware_class/firmware_sample_driver.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,121 @@ ++/* ++ * firmware_sample_driver.c - ++ * ++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org> ++ * ++ * Sample code on how to use request_firmware() from drivers. ++ * ++ * Note that register_firmware() is currently useless. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/string.h> ++ ++#include "linux/firmware.h" ++ ++#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE ++#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE ++char __init inkernel_firmware[] = "let's say that this is firmware\n"; ++#endif ++ ++static char ghost_device[] = "ghost0"; ++ ++static void sample_firmware_load(char *firmware, int size) ++{ ++ u8 buf[size+1]; ++ memcpy(buf, firmware, size); ++ buf[size] = '\0'; ++ printk("firmware_sample_driver: firmware: %s\n", buf); ++} ++ ++static void sample_probe_default(void) ++{ ++ /* uses the default method to get the firmware */ ++ const struct firmware *fw_entry; ++ printk("firmware_sample_driver: a ghost device got inserted :)\n"); ++ ++ if(request_firmware(&fw_entry, "sample_driver_fw", ghost_device)!=0) ++ { ++ printk(KERN_ERR ++ "firmware_sample_driver: Firmware not available\n"); ++ return; ++ } ++ ++ sample_firmware_load(fw_entry->data, fw_entry->size); ++ ++ release_firmware(fw_entry); ++ ++ /* finish setting up the device */ ++} ++static void sample_probe_specific(void) ++{ ++ /* Uses some specific hotplug support to get the firmware from ++ * userspace directly into the hardware, or via some sysfs file */ ++ ++ /* NOTE: This currently doesn't work */ ++ ++ printk("firmware_sample_driver: a ghost device got inserted :)\n"); ++ ++ if(request_firmware(NULL, "sample_driver_fw", ghost_device)!=0) ++ { ++ printk(KERN_ERR ++ "firmware_sample_driver: Firmware load failed\n"); ++ return; ++ } ++ ++ /* request_firmware blocks until userspace finished, so at ++ * this point the firmware should be already in the device */ ++ ++ /* finish setting up the device */ ++} ++static void sample_probe_async_cont(const struct firmware *fw, void *context) ++{ ++ if(!fw){ ++ printk(KERN_ERR ++ "firmware_sample_driver: firmware load failed\n"); ++ return; ++ } ++ ++ printk("firmware_sample_driver: device pointer \"%s\"\n", ++ (char *)context); ++ sample_firmware_load(fw->data, fw->size); ++} ++static void sample_probe_async(void) ++{ ++ /* Let's say that I can't sleep */ ++ int error; ++ error = request_firmware_nowait (THIS_MODULE, ++ "sample_driver_fw", ghost_device, ++ "my device pointer", ++ sample_probe_async_cont); ++ if(error){ ++ printk(KERN_ERR ++ "firmware_sample_driver:" ++ " request_firmware_nowait failed\n"); ++ } ++} ++ ++static int sample_init(void) ++{ ++#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE ++ register_firmware("sample_driver_fw", inkernel_firmware, ++ sizeof(inkernel_firmware)); ++#endif ++ /* since there is no real hardware insertion I just call the ++ * sample probe functions here */ ++ sample_probe_specific(); ++ sample_probe_default(); ++ sample_probe_async(); ++ return 0; ++} ++static void __exit sample_exit(void) ++{ ++} ++ ++module_init (sample_init); ++module_exit (sample_exit); ++ ++MODULE_LICENSE("GPL"); +diff -Nur c3000_pre/linux/Documentation/firmware_class/hotplug-script c3000_work/linux/Documentation/firmware_class/hotplug-script +--- c3000_pre/linux/Documentation/firmware_class/hotplug-script 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/Documentation/firmware_class/hotplug-script 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,16 @@ ++#!/bin/sh ++ ++# Simple hotplug script sample: ++# ++# Both $DEVPATH and $FIRMWARE are already provided in the environment. ++ ++HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/ ++ ++echo 1 > /sysfs/$DEVPATH/loading ++cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data ++echo 0 > /sysfs/$DEVPATH/loading ++ ++# To cancel the load in case of error: ++# ++# echo -1 > /sysfs/$DEVPATH/loading ++# +diff -Nur c3000_pre/linux/MAINTAINERS c3000_work/linux/MAINTAINERS +--- c3000_pre/linux/MAINTAINERS 2004-08-21 09:48:05.000000000 +0900 ++++ c3000_work/linux/MAINTAINERS 2004-12-16 23:01:14.000000000 +0900 +@@ -267,16 +267,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 + + BTTV VIDEO4LINUX DRIVER +diff -Nur c3000_pre/linux/arch/sparc64/kernel/ioctl32.c c3000_work/linux/arch/sparc64/kernel/ioctl32.c +--- c3000_pre/linux/arch/sparc64/kernel/ioctl32.c 2004-08-21 09:48:22.000000000 +0900 ++++ c3000_work/linux/arch/sparc64/kernel/ioctl32.c 2004-12-16 23:01:14.000000000 +0900 +@@ -95,6 +95,7 @@ + + #include <net/bluetooth/bluetooth.h> + #include <net/bluetooth/hci.h> ++#include <net/bluetooth/rfcomm.h> + + #include <linux/usb.h> + #include <linux/usbdevice_fs.h> +@@ -4286,6 +4287,25 @@ + return sys_ioctl(fd, BLKGETSIZE64, arg); + } + ++/* Bluetooth ioctls */ ++#define HCIUARTSETPROTO _IOW('U', 200, int) ++#define HCIUARTGETPROTO _IOR('U', 201, int) ++ ++#define BNEPCONNADD _IOW('B', 200, int) ++#define BNEPCONNDEL _IOW('B', 201, int) ++#define BNEPGETCONNLIST _IOR('B', 210, int) ++#define BNEPGETCONNINFO _IOR('B', 211, int) ++ ++#define CMTPCONNADD _IOW('C', 200, int) ++#define CMTPCONNDEL _IOW('C', 201, int) ++#define CMTPGETCONNLIST _IOR('C', 210, int) ++#define CMTPGETCONNINFO _IOR('C', 211, int) ++ ++#define HIDPCONNADD _IOW('H', 200, int) ++#define HIDPCONNDEL _IOW('H', 201, int) ++#define HIDPGETCONNLIST _IOR('H', 210, int) ++#define HIDPGETCONNINFO _IOR('H', 211, int) ++ + struct ioctl_trans { + unsigned int cmd; + unsigned int handler; +@@ -4987,6 +5007,25 @@ + COMPATIBLE_IOCTL(HCISETACLMTU) + COMPATIBLE_IOCTL(HCISETSCOMTU) + COMPATIBLE_IOCTL(HCIINQUIRY) ++COMPATIBLE_IOCTL(HCIUARTSETPROTO) ++COMPATIBLE_IOCTL(HCIUARTGETPROTO) ++COMPATIBLE_IOCTL(RFCOMMCREATEDEV) ++COMPATIBLE_IOCTL(RFCOMMRELEASEDEV) ++COMPATIBLE_IOCTL(RFCOMMGETDEVLIST) ++COMPATIBLE_IOCTL(RFCOMMGETDEVINFO) ++COMPATIBLE_IOCTL(RFCOMMSTEALDLC) ++COMPATIBLE_IOCTL(BNEPCONNADD) ++COMPATIBLE_IOCTL(BNEPCONNDEL) ++COMPATIBLE_IOCTL(BNEPGETCONNLIST) ++COMPATIBLE_IOCTL(BNEPGETCONNINFO) ++COMPATIBLE_IOCTL(CMTPCONNADD) ++COMPATIBLE_IOCTL(CMTPCONNDEL) ++COMPATIBLE_IOCTL(CMTPGETCONNLIST) ++COMPATIBLE_IOCTL(CMTPGETCONNINFO) ++COMPATIBLE_IOCTL(HIDPCONNADD) ++COMPATIBLE_IOCTL(HIDPCONNDEL) ++COMPATIBLE_IOCTL(HIDPGETCONNLIST) ++COMPATIBLE_IOCTL(HIDPGETCONNINFO) + /* Misc. */ + COMPATIBLE_IOCTL(0x41545900) /* ATYIO_CLKR */ + COMPATIBLE_IOCTL(0x41545901) /* ATYIO_CLKW */ +diff -Nur c3000_pre/linux/drivers/bluetooth/Config.in c3000_work/linux/drivers/bluetooth/Config.in +--- c3000_pre/linux/drivers/bluetooth/Config.in 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/Config.in 2004-12-16 23:01:14.000000000 +0900 +@@ -1,22 +1,33 @@ ++# ++# Bluetooth HCI device drivers configuration ++# ++ + mainmenu_option next_comment + comment 'Bluetooth device drivers' + + dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB + if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then +- bool ' 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 + if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then + bool ' UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4 ++ bool ' BCSP protocol support' CONFIG_BLUEZ_HCIUART_BCSP ++ dep_bool ' Transmit CRC with every BCSP packet' CONFIG_BLUEZ_HCIUART_BCSP_TXCRC $CONFIG_BLUEZ_HCIUART_BCSP + fi + ++dep_tristate 'HCI BlueFRITZ! USB driver' CONFIG_BLUEZ_HCIBFUSB $CONFIG_BLUEZ $CONFIG_USB ++ + dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ + + dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ + + dep_tristate 'HCI BlueCard (PC Card) driver' CONFIG_BLUEZ_HCIBLUECARD $CONFIG_PCMCIA $CONFIG_BLUEZ + ++dep_tristate 'HCI UART (PC Card) driver' CONFIG_BLUEZ_HCIBTUART $CONFIG_PCMCIA $CONFIG_BLUEZ ++ + dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ + + endmenu ++ +diff -Nur c3000_pre/linux/drivers/bluetooth/Makefile c3000_work/linux/drivers/bluetooth/Makefile +--- c3000_pre/linux/drivers/bluetooth/Makefile 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/Makefile 2004-12-16 23:01:14.000000000 +0900 +@@ -1,5 +1,5 @@ + # +-# Makefile for Bluetooth HCI device drivers. ++# Makefile for the Linux Bluetooth HCI device drivers + # + + O_TARGET := bluetooth.o +@@ -9,13 +9,17 @@ + obj-$(CONFIG_BLUEZ_HCIUSB) += hci_usb.o + obj-$(CONFIG_BLUEZ_HCIVHCI) += hci_vhci.o + +-obj-$(CONFIG_BLUEZ_HCIUART) += hci_uart.o +-uart-y := hci_ldisc.o +-uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o ++obj-$(CONFIG_BLUEZ_HCIUART) += hci_uart.o ++uart-y := hci_ldisc.o ++uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o ++uart-$(CONFIG_BLUEZ_HCIUART_BCSP) += hci_bcsp.o ++ ++obj-$(CONFIG_BLUEZ_HCIBFUSB) += bfusb.o + + obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o + obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o + obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o ++obj-$(CONFIG_BLUEZ_HCIBTUART) += btuart_cs.o + + include $(TOPDIR)/Rules.make + +diff -Nur c3000_pre/linux/drivers/bluetooth/Makefile.lib c3000_work/linux/drivers/bluetooth/Makefile.lib +--- c3000_pre/linux/drivers/bluetooth/Makefile.lib 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/Makefile.lib 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o ++obj-$(CONFIG_BLUEZ_HCIBT3C) += firmware_class.o +diff -Nur c3000_pre/linux/drivers/bluetooth/bfusb.c c3000_work/linux/drivers/bluetooth/bfusb.c +--- c3000_pre/linux/drivers/bluetooth/bfusb.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/bfusb.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,782 @@ ++/* ++ * ++ * AVM BlueFRITZ! USB driver ++ * ++ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org> ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/types.h> ++#include <linux/sched.h> ++#include <linux/errno.h> ++#include <linux/skbuff.h> ++ ++#include <linux/firmware.h> ++#include <linux/usb.h> ++ ++#include <net/bluetooth/bluetooth.h> ++#include <net/bluetooth/hci_core.h> ++ ++#ifndef CONFIG_BLUEZ_HCIBFUSB_DEBUG ++#undef BT_DBG ++#define BT_DBG(D...) ++#endif ++ ++#define VERSION "1.1" ++ ++static struct usb_device_id bfusb_table[] = { ++ /* AVM BlueFRITZ! USB */ ++ { USB_DEVICE(0x057c, 0x2200) }, ++ ++ { } /* Terminating entry */ ++}; ++ ++MODULE_DEVICE_TABLE(usb, bfusb_table); ++ ++ ++#define BFUSB_MAX_BLOCK_SIZE 256 ++ ++#define BFUSB_BLOCK_TIMEOUT (HZ * 3) ++ ++#define BFUSB_TX_PROCESS 1 ++#define BFUSB_TX_WAKEUP 2 ++ ++#define BFUSB_MAX_BULK_TX 1 ++#define BFUSB_MAX_BULK_RX 1 ++ ++struct bfusb { ++ struct hci_dev hdev; ++ ++ unsigned long state; ++ ++ struct usb_device *udev; ++ ++ unsigned int bulk_in_ep; ++ unsigned int bulk_out_ep; ++ unsigned int bulk_pkt_size; ++ ++ rwlock_t lock; ++ ++ struct sk_buff_head transmit_q; ++ ++ struct sk_buff *reassembly; ++ ++ atomic_t pending_tx; ++ struct sk_buff_head pending_q; ++ struct sk_buff_head completed_q; ++}; ++ ++struct bfusb_scb { ++ struct urb *urb; ++}; ++ ++static void bfusb_tx_complete(struct urb *urb); ++static void bfusb_rx_complete(struct urb *urb); ++ ++static struct urb *bfusb_get_completed(struct bfusb *bfusb) ++{ ++ struct sk_buff *skb; ++ struct urb *urb = NULL; ++ ++ BT_DBG("bfusb %p", bfusb); ++ ++ skb = skb_dequeue(&bfusb->completed_q); ++ if (skb) { ++ urb = ((struct bfusb_scb *) skb->cb)->urb; ++ kfree_skb(skb); ++ } ++ ++ return urb; ++} ++ ++static inline void bfusb_unlink_urbs(struct bfusb *bfusb) ++{ ++ struct sk_buff *skb; ++ struct urb *urb; ++ ++ BT_DBG("bfusb %p", bfusb); ++ ++ while ((skb = skb_dequeue(&bfusb->pending_q))) { ++ urb = ((struct bfusb_scb *) skb->cb)->urb; ++ usb_unlink_urb(urb); ++ skb_queue_tail(&bfusb->completed_q, skb); ++ } ++ ++ while ((urb = bfusb_get_completed(bfusb))) ++ usb_free_urb(urb); ++} ++ ++ ++static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb) ++{ ++ struct bfusb_scb *scb = (void *) skb->cb; ++ struct urb *urb = bfusb_get_completed(bfusb); ++ int err, pipe; ++ ++ BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len); ++ ++ if (!urb && !(urb = usb_alloc_urb(0))) ++ return -ENOMEM; ++ ++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep); ++ ++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, skb->len, ++ bfusb_tx_complete, skb); ++ ++ urb->transfer_flags = USB_QUEUE_BULK; ++ ++ scb->urb = urb; ++ ++ skb_queue_tail(&bfusb->pending_q, skb); ++ ++ err = usb_submit_urb(urb); ++ if (err) { ++ BT_ERR("%s bulk tx submit failed urb %p err %d", ++ bfusb->hdev.name, urb, err); ++ skb_unlink(skb); ++ usb_free_urb(urb); ++ } else ++ atomic_inc(&bfusb->pending_tx); ++ ++ return err; ++} ++ ++static void bfusb_tx_wakeup(struct bfusb *bfusb) ++{ ++ struct sk_buff *skb; ++ ++ BT_DBG("bfusb %p", bfusb); ++ ++ if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) { ++ set_bit(BFUSB_TX_WAKEUP, &bfusb->state); ++ return; ++ } ++ ++ do { ++ clear_bit(BFUSB_TX_WAKEUP, &bfusb->state); ++ ++ while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) && ++ (skb = skb_dequeue(&bfusb->transmit_q))) { ++ if (bfusb_send_bulk(bfusb, skb) < 0) { ++ skb_queue_head(&bfusb->transmit_q, skb); ++ break; ++ } ++ } ++ ++ } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state)); ++ ++ clear_bit(BFUSB_TX_PROCESS, &bfusb->state); ++} ++ ++static void bfusb_tx_complete(struct urb *urb) ++{ ++ struct sk_buff *skb = (struct sk_buff *) urb->context; ++ struct bfusb *bfusb = (struct bfusb *) skb->dev; ++ ++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len); ++ ++ atomic_dec(&bfusb->pending_tx); ++ ++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags)) ++ return; ++ ++ if (!urb->status) ++ bfusb->hdev.stat.byte_tx += skb->len; ++ else ++ bfusb->hdev.stat.err_tx++; ++ ++ read_lock(&bfusb->lock); ++ ++ skb_unlink(skb); ++ skb_queue_tail(&bfusb->completed_q, skb); ++ ++ bfusb_tx_wakeup(bfusb); ++ ++ read_unlock(&bfusb->lock); ++} ++ ++ ++static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb) ++{ ++ struct bfusb_scb *scb; ++ struct sk_buff *skb; ++ int err, pipe, size = HCI_MAX_FRAME_SIZE + 32; ++ ++ BT_DBG("bfusb %p urb %p", bfusb, urb); ++ ++ if (!urb && !(urb = usb_alloc_urb(0))) ++ return -ENOMEM; ++ ++ if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) { ++ usb_free_urb(urb); ++ return -ENOMEM; ++ } ++ ++ skb->dev = (void *) bfusb; ++ ++ scb = (struct bfusb_scb *) skb->cb; ++ scb->urb = urb; ++ ++ pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep); ++ ++ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, size, ++ bfusb_rx_complete, skb); ++ ++ urb->transfer_flags = USB_QUEUE_BULK; ++ ++ skb_queue_tail(&bfusb->pending_q, skb); ++ ++ err = usb_submit_urb(urb); ++ if (err) { ++ BT_ERR("%s bulk rx submit failed urb %p err %d", ++ bfusb->hdev.name, urb, err); ++ skb_unlink(skb); ++ kfree_skb(skb); ++ usb_free_urb(urb); ++ } ++ ++ return err; ++} ++ ++static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len) ++{ ++ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len); ++ ++ if (hdr & 0x10) { ++ BT_ERR("%s error in block", bfusb->hdev.name); ++ if (bfusb->reassembly) ++ kfree_skb(bfusb->reassembly); ++ bfusb->reassembly = NULL; ++ return -EIO; ++ } ++ ++ if (hdr & 0x04) { ++ struct sk_buff *skb; ++ unsigned char pkt_type; ++ int pkt_len = 0; ++ ++ if (bfusb->reassembly) { ++ BT_ERR("%s unexpected start block", bfusb->hdev.name); ++ kfree_skb(bfusb->reassembly); ++ bfusb->reassembly = NULL; ++ } ++ ++ if (len < 1) { ++ BT_ERR("%s no packet type found", bfusb->hdev.name); ++ return -EPROTO; ++ } ++ ++ pkt_type = *data++; len--; ++ ++ switch (pkt_type) { ++ case HCI_EVENT_PKT: ++ if (len >= HCI_EVENT_HDR_SIZE) { ++ hci_event_hdr *hdr = (hci_event_hdr *) data; ++ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen; ++ } else { ++ BT_ERR("%s event block is too short", bfusb->hdev.name); ++ return -EILSEQ; ++ } ++ break; ++ ++ case HCI_ACLDATA_PKT: ++ if (len >= HCI_ACL_HDR_SIZE) { ++ hci_acl_hdr *hdr = (hci_acl_hdr *) data; ++ pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen); ++ } else { ++ BT_ERR("%s data block is too short", bfusb->hdev.name); ++ return -EILSEQ; ++ } ++ break; ++ ++ case HCI_SCODATA_PKT: ++ if (len >= HCI_SCO_HDR_SIZE) { ++ hci_sco_hdr *hdr = (hci_sco_hdr *) data; ++ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen; ++ } else { ++ BT_ERR("%s audio block is too short", bfusb->hdev.name); ++ return -EILSEQ; ++ } ++ break; ++ } ++ ++ skb = bluez_skb_alloc(pkt_len, GFP_ATOMIC); ++ if (!skb) { ++ BT_ERR("%s no memory for the packet", bfusb->hdev.name); ++ return -ENOMEM; ++ } ++ ++ skb->dev = (void *) &bfusb->hdev; ++ skb->pkt_type = pkt_type; ++ ++ bfusb->reassembly = skb; ++ } else { ++ if (!bfusb->reassembly) { ++ BT_ERR("%s unexpected continuation block", bfusb->hdev.name); ++ return -EIO; ++ } ++ } ++ ++ if (len > 0) ++ memcpy(skb_put(bfusb->reassembly, len), data, len); ++ ++ if (hdr & 0x08) { ++ hci_recv_frame(bfusb->reassembly); ++ bfusb->reassembly = NULL; ++ } ++ ++ return 0; ++} ++ ++static void bfusb_rx_complete(struct urb *urb) ++{ ++ struct sk_buff *skb = (struct sk_buff *) urb->context; ++ struct bfusb *bfusb = (struct bfusb *) skb->dev; ++ unsigned char *buf = urb->transfer_buffer; ++ int count = urb->actual_length; ++ int err, hdr, len; ++ ++ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len); ++ ++ read_lock(&bfusb->lock); ++ ++ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags)) ++ goto unlock; ++ ++ if (urb->status || !count) ++ goto resubmit; ++ ++ bfusb->hdev.stat.byte_rx += count; ++ ++ skb_put(skb, count); ++ ++ while (count) { ++ hdr = buf[0] | (buf[1] << 8); ++ ++ if (hdr & 0x4000) { ++ len = 0; ++ count -= 2; ++ buf += 2; ++ } else { ++ len = (buf[2] == 0) ? 256 : buf[2]; ++ count -= 3; ++ buf += 3; ++ } ++ ++ if (count < len) { ++ BT_ERR("%s block extends over URB buffer ranges", ++ bfusb->hdev.name); ++ } ++ ++ if ((hdr & 0xe1) == 0xc1) ++ bfusb_recv_block(bfusb, hdr, buf, len); ++ ++ count -= len; ++ buf += len; ++ } ++ ++ skb_unlink(skb); ++ kfree_skb(skb); ++ ++ bfusb_rx_submit(bfusb, urb); ++ ++ read_unlock(&bfusb->lock); ++ ++ return; ++ ++resubmit: ++ urb->dev = bfusb->udev; ++ ++ err = usb_submit_urb(urb); ++ if (err) { ++ BT_ERR("%s bulk resubmit failed urb %p err %d", ++ bfusb->hdev.name, urb, err); ++ } ++ ++unlock: ++ read_unlock(&bfusb->lock); ++} ++ ++ ++static int bfusb_open(struct hci_dev *hdev) ++{ ++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; ++ unsigned long flags; ++ int i, err; ++ ++ BT_DBG("hdev %p bfusb %p", hdev, bfusb); ++ ++ if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) ++ return 0; ++ ++ MOD_INC_USE_COUNT; ++ ++ write_lock_irqsave(&bfusb->lock, flags); ++ ++ err = bfusb_rx_submit(bfusb, NULL); ++ if (!err) { ++ for (i = 1; i < BFUSB_MAX_BULK_RX; i++) ++ bfusb_rx_submit(bfusb, NULL); ++ } else { ++ clear_bit(HCI_RUNNING, &hdev->flags); ++ MOD_DEC_USE_COUNT; ++ } ++ ++ write_unlock_irqrestore(&bfusb->lock, flags); ++ ++ return err; ++} ++ ++static int bfusb_flush(struct hci_dev *hdev) ++{ ++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; ++ ++ BT_DBG("hdev %p bfusb %p", hdev, bfusb); ++ ++ skb_queue_purge(&bfusb->transmit_q); ++ ++ return 0; ++} ++ ++static int bfusb_close(struct hci_dev *hdev) ++{ ++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; ++ unsigned long flags; ++ ++ BT_DBG("hdev %p bfusb %p", hdev, bfusb); ++ ++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) ++ return 0; ++ ++ write_lock_irqsave(&bfusb->lock, flags); ++ ++ bfusb_unlink_urbs(bfusb); ++ bfusb_flush(hdev); ++ ++ write_unlock_irqrestore(&bfusb->lock, flags); ++ ++ MOD_DEC_USE_COUNT; ++ ++ return 0; ++} ++ ++static int bfusb_send_frame(struct sk_buff *skb) ++{ ++ struct hci_dev *hdev = (struct hci_dev *) skb->dev; ++ struct bfusb *bfusb; ++ struct sk_buff *nskb; ++ unsigned char buf[3]; ++ int sent = 0, size, count; ++ ++ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len); ++ ++ if (!hdev) { ++ BT_ERR("Frame for unknown HCI device (hdev=NULL)"); ++ return -ENODEV; ++ } ++ ++ if (!test_bit(HCI_RUNNING, &hdev->flags)) ++ return -EBUSY; ++ ++ bfusb = (struct bfusb *) hdev->driver_data; ++ ++ switch (skb->pkt_type) { ++ case HCI_COMMAND_PKT: ++ hdev->stat.cmd_tx++; ++ break; ++ case HCI_ACLDATA_PKT: ++ hdev->stat.acl_tx++; ++ break; ++ case HCI_SCODATA_PKT: ++ hdev->stat.sco_tx++; ++ break; ++ }; ++ ++ /* Prepend skb with frame type */ ++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1); ++ ++ count = skb->len; ++ ++ /* Max HCI frame size seems to be 1511 + 1 */ ++ if (!(nskb = bluez_skb_alloc(count + 32, GFP_ATOMIC))) { ++ BT_ERR("Can't allocate memory for new packet"); ++ return -ENOMEM; ++ } ++ ++ nskb->dev = (void *) bfusb; ++ ++ while (count) { ++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE); ++ ++ buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0); ++ buf[1] = 0x00; ++ buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size; ++ ++ memcpy(skb_put(nskb, 3), buf, 3); ++ memcpy(skb_put(nskb, size), skb->data + sent, size); ++ ++ sent += size; ++ count -= size; ++ } ++ ++ /* Don't send frame with multiple size of bulk max packet */ ++ if ((nskb->len % bfusb->bulk_pkt_size) == 0) { ++ buf[0] = 0xdd; ++ buf[1] = 0x00; ++ memcpy(skb_put(nskb, 2), buf, 2); ++ } ++ ++ read_lock(&bfusb->lock); ++ ++ skb_queue_tail(&bfusb->transmit_q, nskb); ++ bfusb_tx_wakeup(bfusb); ++ ++ read_unlock(&bfusb->lock); ++ ++ kfree_skb(skb); ++ ++ return 0; ++} ++ ++static void bfusb_destruct(struct hci_dev *hdev) ++{ ++ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; ++ ++ BT_DBG("hdev %p bfusb %p", hdev, bfusb); ++ ++ kfree(bfusb); ++} ++ ++static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) ++{ ++ return -ENOIOCTLCMD; ++} ++ ++ ++static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count) ++{ ++ unsigned char *buf; ++ int err, pipe, len, size, sent = 0; ++ ++ BT_DBG("bfusb %p udev %p firmware %p count %d", bfusb, bfusb->udev, firmware, count); ++ ++ BT_INFO("BlueFRITZ! USB loading firmware"); ++ ++ if (usb_set_configuration(bfusb->udev, 1) < 0) { ++ BT_ERR("Can't change to loading configuration"); ++ return -EBUSY; ++ } ++ ++ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC); ++ if (!buf) { ++ BT_ERR("Can't allocate memory chunk for firmware"); ++ return -ENOMEM; ++ } ++ ++ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep); ++ ++ while (count) { ++ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3); ++ ++ memcpy(buf, firmware + sent, size); ++ ++ err = usb_bulk_msg(bfusb->udev, pipe, buf, size, ++ &len, BFUSB_BLOCK_TIMEOUT); ++ ++ if (err || (len != size)) { ++ BT_ERR("Error in firmware loading"); ++ goto error; ++ } ++ ++ sent += size; ++ count -= size; ++ } ++ ++ if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0, ++ &len, BFUSB_BLOCK_TIMEOUT)) < 0) { ++ BT_ERR("Error in null packet request"); ++ goto error; ++ } ++ ++ if ((err = usb_set_configuration(bfusb->udev, 2)) < 0) { ++ BT_ERR("Can't change to running configuration"); ++ goto error; ++ } ++ ++ BT_INFO("BlueFRITZ! USB device ready"); ++ ++ kfree(buf); ++ return 0; ++ ++error: ++ kfree(buf); ++ ++ pipe = usb_sndctrlpipe(bfusb->udev, 0); ++ ++ usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION, ++ 0, 0, 0, NULL, 0, BFUSB_BLOCK_TIMEOUT); ++ ++ return err; ++} ++ ++static void *bfusb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) ++{ ++ const struct firmware *firmware; ++ char device[16]; ++ struct usb_interface *iface; ++ struct usb_interface_descriptor *iface_desc; ++ struct usb_endpoint_descriptor *bulk_out_ep; ++ struct usb_endpoint_descriptor *bulk_in_ep; ++ struct hci_dev *hdev; ++ struct bfusb *bfusb; ++ ++ BT_DBG("udev %p ifnum %d id %p", udev, ifnum, id); ++ ++ /* Check number of endpoints */ ++ iface = &udev->actconfig->interface[0]; ++ iface_desc = &iface->altsetting[0]; ++ ++ if (iface_desc->bNumEndpoints < 2) ++ return NULL; ++ ++ bulk_out_ep = &iface_desc->endpoint[0]; ++ bulk_in_ep = &iface_desc->endpoint[1]; ++ ++ if (!bulk_out_ep || !bulk_in_ep) { ++ BT_ERR("Bulk endpoints not found"); ++ goto done; ++ } ++ ++ /* Initialize control structure and load firmware */ ++ if (!(bfusb = kmalloc(sizeof(struct bfusb), GFP_KERNEL))) { ++ BT_ERR("Can't allocate memory for control structure"); ++ goto done; ++ } ++ ++ memset(bfusb, 0, sizeof(struct bfusb)); ++ ++ bfusb->udev = udev; ++ bfusb->bulk_in_ep = bulk_in_ep->bEndpointAddress; ++ bfusb->bulk_out_ep = bulk_out_ep->bEndpointAddress; ++ bfusb->bulk_pkt_size = bulk_out_ep->wMaxPacketSize; ++ ++ bfusb->lock = RW_LOCK_UNLOCKED; ++ ++ bfusb->reassembly = NULL; ++ ++ skb_queue_head_init(&bfusb->transmit_q); ++ skb_queue_head_init(&bfusb->pending_q); ++ skb_queue_head_init(&bfusb->completed_q); ++ ++ snprintf(device, sizeof(device), "bfusb%3.3d%3.3d", udev->bus->busnum, udev->devnum); ++ ++ if (request_firmware(&firmware, "bfubase.frm", device) < 0) { ++ BT_ERR("Firmware request failed"); ++ goto error; ++ } ++ ++ if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) { ++ BT_ERR("Firmware loading failed"); ++ goto release; ++ } ++ ++ release_firmware(firmware); ++ ++ /* Initialize and register HCI device */ ++ hdev = &bfusb->hdev; ++ ++ hdev->type = HCI_USB; ++ hdev->driver_data = bfusb; ++ ++ hdev->open = bfusb_open; ++ hdev->close = bfusb_close; ++ hdev->flush = bfusb_flush; ++ hdev->send = bfusb_send_frame; ++ hdev->destruct = bfusb_destruct; ++ hdev->ioctl = bfusb_ioctl; ++ ++ if (hci_register_dev(hdev) < 0) { ++ BT_ERR("Can't register HCI device"); ++ goto error; ++ } ++ ++ return bfusb; ++ ++release: ++ release_firmware(firmware); ++ ++error: ++ kfree(bfusb); ++ ++done: ++ return NULL; ++} ++ ++static void bfusb_disconnect(struct usb_device *udev, void *ptr) ++{ ++ struct bfusb *bfusb = (struct bfusb *) ptr; ++ struct hci_dev *hdev = &bfusb->hdev; ++ ++ BT_DBG("udev %p ptr %p", udev, ptr); ++ ++ if (!hdev) ++ return; ++ ++ bfusb_close(hdev); ++ ++ if (hci_unregister_dev(hdev) < 0) ++ BT_ERR("Can't unregister HCI device %s", hdev->name); ++} ++ ++static struct usb_driver bfusb_driver = { ++ name: "bfusb", ++ probe: bfusb_probe, ++ disconnect: bfusb_disconnect, ++ id_table: bfusb_table, ++}; ++ ++static int __init bfusb_init(void) ++{ ++ int err; ++ ++ BT_INFO("BlueFRITZ! USB driver ver %s", VERSION); ++ BT_INFO("Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>"); ++ ++ if ((err = usb_register(&bfusb_driver)) < 0) ++ BT_ERR("Failed to register BlueFRITZ! USB driver"); ++ ++ return err; ++} ++ ++static void __exit bfusb_cleanup(void) ++{ ++ usb_deregister(&bfusb_driver); ++} ++ ++module_init(bfusb_init); ++module_exit(bfusb_cleanup); ++ ++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); ++MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION); ++MODULE_LICENSE("GPL"); +diff -Nur c3000_pre/linux/drivers/bluetooth/bluecard_cs.c c3000_work/linux/drivers/bluetooth/bluecard_cs.c +--- c3000_pre/linux/drivers/bluetooth/bluecard_cs.c 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/bluecard_cs.c 2004-12-16 23:01:14.000000000 +0900 +@@ -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)); +diff -Nur c3000_pre/linux/drivers/bluetooth/bt3c_cs.c c3000_work/linux/drivers/bluetooth/bt3c_cs.c +--- c3000_pre/linux/drivers/bluetooth/bt3c_cs.c 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/bt3c_cs.c 2004-12-16 23:01:14.000000000 +0900 +@@ -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); ++ ++ memset(b, 0, sizeof(b)); ++ memcpy(b, ptr + 4, 8); ++ addr = simple_strtol(b, NULL, 16); ++ ++ memset(b, 0, sizeof(b)); ++ memcpy(b, ptr + (size * 2) + 2, 2); ++ fcs = simple_strtol(b, NULL, 16); ++ ++ memset(b, 0, sizeof(b)); ++ for (tmp = 0, i = 0; i < size; i++) { ++ memcpy(b, ptr + (i * 2) + 2, 2); ++ tmp += simple_strtol(b, NULL, 16); ++ } + +- /* 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); ++ if (((tmp + fcs) & 0xff) != 0xff) { ++ printk(KERN_WARNING "bt3c_cs: Checksum error in firmware.\n"); ++ err = -EILSEQ; ++ goto error; ++ } + +- result = waitpid(pid, NULL, __WCLONE); ++ if (ptr[1] == '3') { ++ bt3c_address(iobase, addr); + +- /* 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 (i = 0; i < (size - 4) / 2; i++) { ++ memcpy(b, ptr + (i * 4) + 12, 4); ++ tmp = simple_strtol(b, NULL, 16); ++ bt3c_put(iobase, tmp); ++ } ++ } + +- if (result != pid) { +- printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result); +- return -result; ++ 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) +diff -Nur c3000_pre/linux/drivers/bluetooth/btuart_cs.c c3000_work/linux/drivers/bluetooth/btuart_cs.c +--- c3000_pre/linux/drivers/bluetooth/btuart_cs.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/btuart_cs.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,909 @@ ++/* ++ * ++ * Driver for Bluetooth PCMCIA cards with HCI UART interface ++ * ++ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org> ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation; ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * The initial developer of the original code is David A. Hinds ++ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds ++ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/types.h> ++#include <linux/sched.h> ++#include <linux/timer.h> ++#include <linux/errno.h> ++#include <linux/ptrace.h> ++#include <linux/ioport.h> ++#include <linux/spinlock.h> ++ ++#include <linux/skbuff.h> ++#include <linux/string.h> ++#include <linux/serial.h> ++#include <linux/serial_reg.h> ++#include <asm/system.h> ++#include <asm/bitops.h> ++#include <asm/io.h> ++ ++#include <pcmcia/version.h> ++#include <pcmcia/cs_types.h> ++#include <pcmcia/cs.h> ++#include <pcmcia/cistpl.h> ++#include <pcmcia/ciscode.h> ++#include <pcmcia/ds.h> ++#include <pcmcia/cisreg.h> ++ ++#include <net/bluetooth/bluetooth.h> ++#include <net/bluetooth/hci_core.h> ++ ++ ++ ++/* ======================== Module parameters ======================== */ ++ ++ ++/* Bit map of interrupts to choose from */ ++static u_int irq_mask = 0xffff; ++static int irq_list[4] = { -1 }; ++ ++MODULE_PARM(irq_mask, "i"); ++MODULE_PARM(irq_list, "1-4i"); ++ ++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); ++MODULE_DESCRIPTION("BlueZ driver for Bluetooth PCMCIA cards with HCI UART interface"); ++MODULE_LICENSE("GPL"); ++ ++ ++ ++/* ======================== Local structures ======================== */ ++ ++ ++typedef struct btuart_info_t { ++ dev_link_t link; ++ dev_node_t node; ++ ++ struct hci_dev hdev; ++ ++ spinlock_t lock; /* For serializing operations */ ++ ++ struct sk_buff_head txq; ++ unsigned long tx_state; ++ ++ unsigned long rx_state; ++ unsigned long rx_count; ++ struct sk_buff *rx_skb; ++} btuart_info_t; ++ ++ ++void btuart_config(dev_link_t *link); ++void btuart_release(u_long arg); ++int btuart_event(event_t event, int priority, event_callback_args_t *args); ++ ++static dev_info_t dev_info = "btuart_cs"; ++ ++dev_link_t *btuart_attach(void); ++void btuart_detach(dev_link_t *); ++ ++static dev_link_t *dev_list = NULL; ++ ++ ++/* Maximum baud rate */ ++#define SPEED_MAX 115200 ++ ++/* Default baud rate: 57600, 115200, 230400 or 460800 */ ++#define DEFAULT_BAUD_RATE 115200 ++ ++ ++/* Transmit states */ ++#define XMIT_SENDING 1 ++#define XMIT_WAKEUP 2 ++#define XMIT_WAITING 8 ++ ++/* Receiver states */ ++#define RECV_WAIT_PACKET_TYPE 0 ++#define RECV_WAIT_EVENT_HEADER 1 ++#define RECV_WAIT_ACL_HEADER 2 ++#define RECV_WAIT_SCO_HEADER 3 ++#define RECV_WAIT_DATA 4 ++ ++ ++ ++/* ======================== Interrupt handling ======================== */ ++ ++ ++static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) ++{ ++ int actual = 0; ++ ++ /* Tx FIFO should be empty */ ++ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE)) ++ return 0; ++ ++ /* Fill FIFO with current frame */ ++ while ((fifo_size-- > 0) && (actual < len)) { ++ /* Transmit next byte */ ++ outb(buf[actual], iobase + UART_TX); ++ actual++; ++ } ++ ++ return actual; ++} ++ ++ ++static void btuart_write_wakeup(btuart_info_t *info) ++{ ++ if (!info) { ++ printk(KERN_WARNING "btuart_cs: Call of write_wakeup for unknown device.\n"); ++ return; ++ } ++ ++ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) { ++ set_bit(XMIT_WAKEUP, &(info->tx_state)); ++ return; ++ } ++ ++ do { ++ register unsigned int iobase = info->link.io.BasePort1; ++ register struct sk_buff *skb; ++ register int len; ++ ++ clear_bit(XMIT_WAKEUP, &(info->tx_state)); ++ ++ if (!(info->link.state & DEV_PRESENT)) ++ return; ++ ++ if (!(skb = skb_dequeue(&(info->txq)))) ++ break; ++ ++ /* Send frame */ ++ len = btuart_write(iobase, 16, skb->data, skb->len); ++ set_bit(XMIT_WAKEUP, &(info->tx_state)); ++ ++ if (len == skb->len) { ++ kfree_skb(skb); ++ } else { ++ skb_pull(skb, len); ++ skb_queue_head(&(info->txq), skb); ++ } ++ ++ info->hdev.stat.byte_tx += len; ++ ++ } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); ++ ++ clear_bit(XMIT_SENDING, &(info->tx_state)); ++} ++ ++ ++static void btuart_receive(btuart_info_t *info) ++{ ++ unsigned int iobase; ++ int boguscount = 0; ++ ++ if (!info) { ++ printk(KERN_WARNING "btuart_cs: Call of receive for unknown device.\n"); ++ return; ++ } ++ ++ iobase = info->link.io.BasePort1; ++ ++ do { ++ info->hdev.stat.byte_rx++; ++ ++ /* Allocate packet */ ++ if (info->rx_skb == NULL) { ++ info->rx_state = RECV_WAIT_PACKET_TYPE; ++ info->rx_count = 0; ++ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { ++ printk(KERN_WARNING "btuart_cs: Can't allocate mem for new packet.\n"); ++ return; ++ } ++ } ++ ++ if (info->rx_state == RECV_WAIT_PACKET_TYPE) { ++ ++ info->rx_skb->dev = (void *)&(info->hdev); ++ info->rx_skb->pkt_type = inb(iobase + UART_RX); ++ ++ switch (info->rx_skb->pkt_type) { ++ ++ case HCI_EVENT_PKT: ++ info->rx_state = RECV_WAIT_EVENT_HEADER; ++ info->rx_count = HCI_EVENT_HDR_SIZE; ++ break; ++ ++ case HCI_ACLDATA_PKT: ++ info->rx_state = RECV_WAIT_ACL_HEADER; ++ info->rx_count = HCI_ACL_HDR_SIZE; ++ break; ++ ++ case HCI_SCODATA_PKT: ++ info->rx_state = RECV_WAIT_SCO_HEADER; ++ info->rx_count = HCI_SCO_HDR_SIZE; ++ break; ++ ++ default: ++ /* Unknown packet */ ++ printk(KERN_WARNING "btuart_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type); ++ info->hdev.stat.err_rx++; ++ clear_bit(HCI_RUNNING, &(info->hdev.flags)); ++ ++ kfree_skb(info->rx_skb); ++ info->rx_skb = NULL; ++ break; ++ ++ } ++ ++ } else { ++ ++ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX); ++ info->rx_count--; ++ ++ if (info->rx_count == 0) { ++ ++ int dlen; ++ hci_event_hdr *eh; ++ hci_acl_hdr *ah; ++ hci_sco_hdr *sh; ++ ++ ++ switch (info->rx_state) { ++ ++ case RECV_WAIT_EVENT_HEADER: ++ eh = (hci_event_hdr *)(info->rx_skb->data); ++ info->rx_state = RECV_WAIT_DATA; ++ info->rx_count = eh->plen; ++ break; ++ ++ case RECV_WAIT_ACL_HEADER: ++ ah = (hci_acl_hdr *)(info->rx_skb->data); ++ dlen = __le16_to_cpu(ah->dlen); ++ info->rx_state = RECV_WAIT_DATA; ++ info->rx_count = dlen; ++ break; ++ ++ case RECV_WAIT_SCO_HEADER: ++ sh = (hci_sco_hdr *)(info->rx_skb->data); ++ info->rx_state = RECV_WAIT_DATA; ++ info->rx_count = sh->dlen; ++ break; ++ ++ case RECV_WAIT_DATA: ++ hci_recv_frame(info->rx_skb); ++ info->rx_skb = NULL; ++ break; ++ ++ } ++ ++ } ++ ++ } ++ ++ /* Make sure we don't stay here to long */ ++ if (boguscount++ > 16) ++ break; ++ ++ } while (inb(iobase + UART_LSR) & UART_LSR_DR); ++} ++ ++ ++void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs) ++{ ++ btuart_info_t *info = dev_inst; ++ unsigned int iobase; ++ int boguscount = 0; ++ int iir, lsr; ++ ++ if (!info) { ++ printk(KERN_WARNING "btuart_cs: Call of irq %d for unknown device.\n", irq); ++ return; ++ } ++ ++ iobase = info->link.io.BasePort1; ++ ++ spin_lock(&(info->lock)); ++ ++ iir = inb(iobase + UART_IIR) & UART_IIR_ID; ++ while (iir) { ++ ++ /* Clear interrupt */ ++ lsr = inb(iobase + UART_LSR); ++ ++ switch (iir) { ++ case UART_IIR_RLSI: ++ printk(KERN_NOTICE "btuart_cs: RLSI\n"); ++ break; ++ case UART_IIR_RDI: ++ /* Receive interrupt */ ++ btuart_receive(info); ++ break; ++ case UART_IIR_THRI: ++ if (lsr & UART_LSR_THRE) { ++ /* Transmitter ready for data */ ++ btuart_write_wakeup(info); ++ } ++ break; ++ default: ++ printk(KERN_NOTICE "btuart_cs: Unhandled IIR=%#x\n", iir); ++ break; ++ } ++ ++ /* Make sure we don't stay here to long */ ++ if (boguscount++ > 100) ++ break; ++ ++ iir = inb(iobase + UART_IIR) & UART_IIR_ID; ++ ++ } ++ ++ spin_unlock(&(info->lock)); ++} ++ ++ ++static void btuart_change_speed(btuart_info_t *info, unsigned int speed) ++{ ++ unsigned long flags; ++ unsigned int iobase; ++ int fcr; /* FIFO control reg */ ++ int lcr; /* Line control reg */ ++ int divisor; ++ ++ if (!info) { ++ printk(KERN_WARNING "btuart_cs: Call of change speed for unknown device.\n"); ++ return; ++ } ++ ++ iobase = info->link.io.BasePort1; ++ ++ spin_lock_irqsave(&(info->lock), flags); ++ ++ /* Turn off interrupts */ ++ outb(0, iobase + UART_IER); ++ ++ divisor = SPEED_MAX / speed; ++ ++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; ++ ++ /* ++ * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and ++ * almost 1,7 ms at 19200 bps. At speeds above that we can just forget ++ * about this timeout since it will always be fast enough. ++ */ ++ ++ if (speed < 38400) ++ fcr |= UART_FCR_TRIGGER_1; ++ else ++ fcr |= UART_FCR_TRIGGER_14; ++ ++ /* Bluetooth cards use 8N1 */ ++ lcr = UART_LCR_WLEN8; ++ ++ outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */ ++ outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */ ++ outb(divisor >> 8, iobase + UART_DLM); ++ outb(lcr, iobase + UART_LCR); /* Set 8N1 */ ++ outb(fcr, iobase + UART_FCR); /* Enable FIFO's */ ++ ++ /* Turn on interrups */ ++ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); ++ ++ spin_unlock_irqrestore(&(info->lock), flags); ++} ++ ++ ++ ++/* ======================== HCI interface ======================== */ ++ ++ ++static int btuart_hci_flush(struct hci_dev *hdev) ++{ ++ btuart_info_t *info = (btuart_info_t *)(hdev->driver_data); ++ ++ /* Drop TX queue */ ++ skb_queue_purge(&(info->txq)); ++ ++ return 0; ++} ++ ++ ++static int btuart_hci_open(struct hci_dev *hdev) ++{ ++ set_bit(HCI_RUNNING, &(hdev->flags)); ++ ++ return 0; ++} ++ ++ ++static int btuart_hci_close(struct hci_dev *hdev) ++{ ++ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) ++ return 0; ++ ++ btuart_hci_flush(hdev); ++ ++ return 0; ++} ++ ++ ++static int btuart_hci_send_frame(struct sk_buff *skb) ++{ ++ btuart_info_t *info; ++ struct hci_dev *hdev = (struct hci_dev *)(skb->dev); ++ ++ if (!hdev) { ++ printk(KERN_WARNING "btuart_cs: Frame for unknown HCI device (hdev=NULL)."); ++ return -ENODEV; ++ } ++ ++ info = (btuart_info_t *)(hdev->driver_data); ++ ++ switch (skb->pkt_type) { ++ case HCI_COMMAND_PKT: ++ hdev->stat.cmd_tx++; ++ break; ++ case HCI_ACLDATA_PKT: ++ hdev->stat.acl_tx++; ++ break; ++ case HCI_SCODATA_PKT: ++ hdev->stat.sco_tx++; ++ break; ++ }; ++ ++ /* Prepend skb with frame type */ ++ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1); ++ skb_queue_tail(&(info->txq), skb); ++ ++ btuart_write_wakeup(info); ++ ++ return 0; ++} ++ ++ ++static void btuart_hci_destruct(struct hci_dev *hdev) ++{ ++} ++ ++ ++static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) ++{ ++ return -ENOIOCTLCMD; ++} ++ ++ ++ ++/* ======================== Card services HCI interaction ======================== */ ++ ++ ++int btuart_open(btuart_info_t *info) ++{ ++ unsigned long flags; ++ unsigned int iobase = info->link.io.BasePort1; ++ struct hci_dev *hdev; ++ ++ spin_lock_init(&(info->lock)); ++ ++ skb_queue_head_init(&(info->txq)); ++ ++ info->rx_state = RECV_WAIT_PACKET_TYPE; ++ info->rx_count = 0; ++ info->rx_skb = NULL; ++ ++ spin_lock_irqsave(&(info->lock), flags); ++ ++ /* Reset UART */ ++ outb(0, iobase + UART_MCR); ++ ++ /* Turn off interrupts */ ++ outb(0, iobase + UART_IER); ++ ++ /* Initialize UART */ ++ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */ ++ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR); ++ ++ /* Turn on interrupts */ ++ // outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); ++ ++ spin_unlock_irqrestore(&(info->lock), flags); ++ ++ btuart_change_speed(info, DEFAULT_BAUD_RATE); ++ ++ /* Timeout before it is safe to send the first HCI packet */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(HZ); ++ ++ ++ /* Initialize and register HCI device */ ++ ++ hdev = &(info->hdev); ++ ++ hdev->type = HCI_PCCARD; ++ hdev->driver_data = info; ++ ++ hdev->open = btuart_hci_open; ++ hdev->close = btuart_hci_close; ++ hdev->flush = btuart_hci_flush; ++ hdev->send = btuart_hci_send_frame; ++ hdev->destruct = btuart_hci_destruct; ++ hdev->ioctl = btuart_hci_ioctl; ++ ++ if (hci_register_dev(hdev) < 0) { ++ printk(KERN_WARNING "btuart_cs: Can't register HCI device %s.\n", hdev->name); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++ ++int btuart_close(btuart_info_t *info) ++{ ++ unsigned long flags; ++ unsigned int iobase = info->link.io.BasePort1; ++ struct hci_dev *hdev = &(info->hdev); ++ ++ if (info->link.state & DEV_CONFIG_PENDING) ++ return -ENODEV; ++ ++ btuart_hci_close(hdev); ++ ++ spin_lock_irqsave(&(info->lock), flags); ++ ++ /* Reset UART */ ++ outb(0, iobase + UART_MCR); ++ ++ /* Turn off interrupts */ ++ outb(0, iobase + UART_IER); ++ ++ spin_unlock_irqrestore(&(info->lock), flags); ++ ++ if (hci_unregister_dev(hdev) < 0) ++ printk(KERN_WARNING "btuart_cs: Can't unregister HCI device %s.\n", hdev->name); ++ ++ return 0; ++} ++ ++ ++ ++/* ======================== Card services ======================== */ ++ ++ ++static void cs_error(client_handle_t handle, int func, int ret) ++{ ++ error_info_t err = { func, ret }; ++ ++ CardServices(ReportError, handle, &err); ++} ++ ++ ++dev_link_t *btuart_attach(void) ++{ ++ btuart_info_t *info; ++ client_reg_t client_reg; ++ dev_link_t *link; ++ int i, ret; ++ ++ /* Create new info device */ ++ info = kmalloc(sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return NULL; ++ memset(info, 0, sizeof(*info)); ++ ++ link = &info->link; ++ link->priv = info; ++ ++ link->release.function = &btuart_release; ++ link->release.data = (u_long)link; ++ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; ++ link->io.NumPorts1 = 8; ++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; ++ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; ++ ++ if (irq_list[0] == -1) ++ link->irq.IRQInfo2 = irq_mask; ++ else ++ for (i = 0; i < 4; i++) ++ link->irq.IRQInfo2 |= 1 << irq_list[i]; ++ ++ link->irq.Handler = btuart_interrupt; ++ link->irq.Instance = info; ++ ++ link->conf.Attributes = CONF_ENABLE_IRQ; ++ link->conf.Vcc = 50; ++ link->conf.IntType = INT_MEMORY_AND_IO; ++ ++ /* Register with Card Services */ ++ link->next = dev_list; ++ dev_list = link; ++ client_reg.dev_info = &dev_info; ++ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; ++ client_reg.EventMask = ++ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | ++ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | ++ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; ++ client_reg.event_handler = &btuart_event; ++ client_reg.Version = 0x0210; ++ client_reg.event_callback_args.client_data = link; ++ ++ ret = CardServices(RegisterClient, &link->handle, &client_reg); ++ if (ret != CS_SUCCESS) { ++ cs_error(link->handle, RegisterClient, ret); ++ btuart_detach(link); ++ return NULL; ++ } ++ ++ return link; ++} ++ ++ ++void btuart_detach(dev_link_t *link) ++{ ++ btuart_info_t *info = link->priv; ++ dev_link_t **linkp; ++ int ret; ++ ++ /* Locate device structure */ ++ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) ++ if (*linkp == link) ++ break; ++ ++ if (*linkp == NULL) ++ return; ++ ++ del_timer(&link->release); ++ if (link->state & DEV_CONFIG) ++ btuart_release((u_long)link); ++ ++ if (link->handle) { ++ ret = CardServices(DeregisterClient, link->handle); ++ if (ret != CS_SUCCESS) ++ cs_error(link->handle, DeregisterClient, ret); ++ } ++ ++ /* Unlink device structure, free bits */ ++ *linkp = link->next; ++ ++ kfree(info); ++} ++ ++ ++static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse) ++{ ++ int i; ++ ++ i = CardServices(fn, handle, tuple); ++ if (i != CS_SUCCESS) ++ return CS_NO_MORE_ITEMS; ++ ++ i = CardServices(GetTupleData, handle, tuple); ++ if (i != CS_SUCCESS) ++ return i; ++ ++ return CardServices(ParseTuple, handle, tuple, parse); ++} ++ ++ ++#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) ++#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) ++ ++void btuart_config(dev_link_t *link) ++{ ++ static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; ++ client_handle_t handle = link->handle; ++ btuart_info_t *info = link->priv; ++ tuple_t tuple; ++ u_short buf[256]; ++ cisparse_t parse; ++ cistpl_cftable_entry_t *cf = &parse.cftable_entry; ++ config_info_t config; ++ int i, j, try, last_ret, last_fn; ++ ++ tuple.TupleData = (cisdata_t *)buf; ++ tuple.TupleOffset = 0; ++ tuple.TupleDataMax = 255; ++ tuple.Attributes = 0; ++ ++ /* Get configuration register information */ ++ tuple.DesiredTuple = CISTPL_CONFIG; ++ last_ret = first_tuple(handle, &tuple, &parse); ++ if (last_ret != CS_SUCCESS) { ++ last_fn = ParseTuple; ++ goto cs_failed; ++ } ++ link->conf.ConfigBase = parse.config.base; ++ link->conf.Present = parse.config.rmask[0]; ++ ++ /* Configure card */ ++ link->state |= DEV_CONFIG; ++ i = CardServices(GetConfigurationInfo, handle, &config); ++ link->conf.Vcc = config.Vcc; ++ ++ /* First pass: look for a config entry that looks normal. */ ++ tuple.TupleData = (cisdata_t *) buf; ++ tuple.TupleOffset = 0; ++ tuple.TupleDataMax = 255; ++ tuple.Attributes = 0; ++ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; ++ /* Two tries: without IO aliases, then with aliases */ ++ for (try = 0; try < 2; try++) { ++ i = first_tuple(handle, &tuple, &parse); ++ while (i != CS_NO_MORE_ITEMS) { ++ if (i != CS_SUCCESS) ++ goto next_entry; ++ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) ++ link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; ++ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) { ++ link->conf.ConfigIndex = cf->index; ++ link->io.BasePort1 = cf->io.win[0].base; ++ link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; ++ i = CardServices(RequestIO, link->handle, &link->io); ++ if (i == CS_SUCCESS) ++ goto found_port; ++ } ++next_entry: ++ i = next_tuple(handle, &tuple, &parse); ++ } ++ } ++ ++ /* Second pass: try to find an entry that isn't picky about ++ its base address, then try to grab any standard serial port ++ address, and finally try to get any free port. */ ++ i = first_tuple(handle, &tuple, &parse); ++ while (i != CS_NO_MORE_ITEMS) { ++ if ((i == CS_SUCCESS) && (cf->io.nwin > 0) ++ && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { ++ link->conf.ConfigIndex = cf->index; ++ for (j = 0; j < 5; j++) { ++ link->io.BasePort1 = base[j]; ++ link->io.IOAddrLines = base[j] ? 16 : 3; ++ i = CardServices(RequestIO, link->handle, &link->io); ++ if (i == CS_SUCCESS) ++ goto found_port; ++ } ++ } ++ i = next_tuple(handle, &tuple, &parse); ++ } ++ ++found_port: ++ if (i != CS_SUCCESS) { ++ printk(KERN_NOTICE "btuart_cs: No usable port range found. Giving up.\n"); ++ cs_error(link->handle, RequestIO, i); ++ goto failed; ++ } ++ ++ i = CardServices(RequestIRQ, link->handle, &link->irq); ++ if (i != CS_SUCCESS) { ++ cs_error(link->handle, RequestIRQ, i); ++ link->irq.AssignedIRQ = 0; ++ } ++ ++ i = CardServices(RequestConfiguration, link->handle, &link->conf); ++ if (i != CS_SUCCESS) { ++ cs_error(link->handle, RequestConfiguration, i); ++ goto failed; ++ } ++ ++ MOD_INC_USE_COUNT; ++ ++ if (btuart_open(info) != 0) ++ goto failed; ++ ++ strcpy(info->node.dev_name, info->hdev.name); ++ link->dev = &info->node; ++ link->state &= ~DEV_CONFIG_PENDING; ++ ++ return; ++ ++cs_failed: ++ cs_error(link->handle, last_fn, last_ret); ++ ++failed: ++ btuart_release((u_long) link); ++} ++ ++ ++void btuart_release(u_long arg) ++{ ++ dev_link_t *link = (dev_link_t *)arg; ++ btuart_info_t *info = link->priv; ++ ++ if (link->state & DEV_PRESENT) ++ btuart_close(info); ++ ++ MOD_DEC_USE_COUNT; ++ ++ link->dev = NULL; ++ ++ CardServices(ReleaseConfiguration, link->handle); ++ CardServices(ReleaseIO, link->handle, &link->io); ++ CardServices(ReleaseIRQ, link->handle, &link->irq); ++ ++ link->state &= ~DEV_CONFIG; ++} ++ ++ ++int btuart_event(event_t event, int priority, event_callback_args_t *args) ++{ ++ dev_link_t *link = args->client_data; ++ btuart_info_t *info = link->priv; ++ ++ switch (event) { ++ case CS_EVENT_CARD_REMOVAL: ++ link->state &= ~DEV_PRESENT; ++ if (link->state & DEV_CONFIG) { ++ btuart_close(info); ++ mod_timer(&link->release, jiffies + HZ / 20); ++ } ++ break; ++ case CS_EVENT_CARD_INSERTION: ++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; ++ btuart_config(link); ++ break; ++ case CS_EVENT_PM_SUSPEND: ++ link->state |= DEV_SUSPEND; ++ /* Fall through... */ ++ case CS_EVENT_RESET_PHYSICAL: ++ if (link->state & DEV_CONFIG) ++ CardServices(ReleaseConfiguration, link->handle); ++ break; ++ case CS_EVENT_PM_RESUME: ++ link->state &= ~DEV_SUSPEND; ++ /* Fall through... */ ++ case CS_EVENT_CARD_RESET: ++ if (DEV_OK(link)) ++ CardServices(RequestConfiguration, link->handle, &link->conf); ++ break; ++ } ++ ++ return 0; ++} ++ ++ ++ ++/* ======================== Module initialization ======================== */ ++ ++ ++int __init init_btuart_cs(void) ++{ ++ servinfo_t serv; ++ int err; ++ ++ CardServices(GetCardServicesInfo, &serv); ++ if (serv.Revision != CS_RELEASE_CODE) { ++ printk(KERN_NOTICE "btuart_cs: Card Services release does not match!\n"); ++ return -1; ++ } ++ ++ err = register_pccard_driver(&dev_info, &btuart_attach, &btuart_detach); ++ ++ return err; ++} ++ ++ ++void __exit exit_btuart_cs(void) ++{ ++ unregister_pccard_driver(&dev_info); ++ ++ while (dev_list != NULL) ++ btuart_detach(dev_list); ++} ++ ++ ++module_init(init_btuart_cs); ++module_exit(exit_btuart_cs); ++ ++EXPORT_NO_SYMBOLS; +diff -Nur c3000_pre/linux/drivers/bluetooth/dtl1_cs.c c3000_work/linux/drivers/bluetooth/dtl1_cs.c +--- c3000_pre/linux/drivers/bluetooth/dtl1_cs.c 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/dtl1_cs.c 2004-12-16 23:01:14.000000000 +0900 +@@ -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); +diff -Nur c3000_pre/linux/drivers/bluetooth/hci_bcsp.c c3000_work/linux/drivers/bluetooth/hci_bcsp.c +--- c3000_pre/linux/drivers/bluetooth/hci_bcsp.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/hci_bcsp.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,710 @@ ++/* ++ BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ). ++ Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com> ++ ++ Based on ++ hci_h4.c by Maxim Krasnyansky <maxk@qualcomm.com> ++ ABCSP by Carl Orsborn <cjo@csr.com> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++/* ++ * $Id: hci_bcsp.c,v 1.2 2002/09/26 05:05:14 maxk Exp $ ++ */ ++ ++#define VERSION "0.1" ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/sched.h> ++#include <linux/types.h> ++#include <linux/fcntl.h> ++#include <linux/interrupt.h> ++#include <linux/ptrace.h> ++#include <linux/poll.h> ++ ++#include <linux/slab.h> ++#include <linux/tty.h> ++#include <linux/errno.h> ++#include <linux/string.h> ++#include <linux/signal.h> ++#include <linux/ioctl.h> ++#include <linux/skbuff.h> ++ ++#include <net/bluetooth/bluetooth.h> ++#include <net/bluetooth/hci_core.h> ++#include "hci_uart.h" ++#include "hci_bcsp.h" ++ ++#ifndef HCI_UART_DEBUG ++#undef BT_DBG ++#define BT_DBG( A... ) ++#undef BT_DMP ++#define BT_DMP( A... ) ++#endif ++ ++/* ---- BCSP CRC calculation ---- */ ++ ++/* Table for calculating CRC for polynomial 0x1021, LSB processed first, ++initial value 0xffff, bits shifted in reverse order. */ ++ ++static const u16 crc_table[] = { ++ 0x0000, 0x1081, 0x2102, 0x3183, ++ 0x4204, 0x5285, 0x6306, 0x7387, ++ 0x8408, 0x9489, 0xa50a, 0xb58b, ++ 0xc60c, 0xd68d, 0xe70e, 0xf78f ++}; ++ ++/* Initialise the crc calculator */ ++#define BCSP_CRC_INIT(x) x = 0xffff ++ ++/* ++ Update crc with next data byte ++ ++ Implementation note ++ The data byte is treated as two nibbles. The crc is generated ++ in reverse, i.e., bits are fed into the register from the top. ++*/ ++static void bcsp_crc_update(u16 *crc, u8 d) ++{ ++ u16 reg = *crc; ++ ++ reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f]; ++ reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f]; ++ ++ *crc = reg; ++} ++ ++/* ++ Get reverse of generated crc ++ ++ Implementation note ++ The crc generator (bcsp_crc_init() and bcsp_crc_update()) ++ creates a reversed crc, so it needs to be swapped back before ++ being passed on. ++*/ ++static u16 bcsp_crc_reverse(u16 crc) ++{ ++ u16 b, rev; ++ ++ for (b = 0, rev = 0; b < 16; b++) { ++ rev = rev << 1; ++ rev |= (crc & 1); ++ crc = crc >> 1; ++ } ++ return (rev); ++} ++ ++/* ---- BCSP core ---- */ ++ ++static void bcsp_slip_msgdelim(struct sk_buff *skb) ++{ ++ const char pkt_delim = 0xc0; ++ memcpy(skb_put(skb, 1), &pkt_delim, 1); ++} ++ ++static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c) ++{ ++ const char esc_c0[2] = { 0xdb, 0xdc }; ++ const char esc_db[2] = { 0xdb, 0xdd }; ++ ++ switch (c) { ++ case 0xc0: ++ memcpy(skb_put(skb, 2), &esc_c0, 2); ++ break; ++ case 0xdb: ++ memcpy(skb_put(skb, 2), &esc_db, 2); ++ break; ++ default: ++ memcpy(skb_put(skb, 1), &c, 1); ++ } ++} ++ ++static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb) ++{ ++ struct bcsp_struct *bcsp = hu->priv; ++ ++ if (skb->len > 0xFFF) { ++ BT_ERR("Packet too long"); ++ kfree_skb(skb); ++ return 0; ++ } ++ ++ switch (skb->pkt_type) { ++ case HCI_ACLDATA_PKT: ++ case HCI_COMMAND_PKT: ++ skb_queue_tail(&bcsp->rel, skb); ++ break; ++ ++ case HCI_SCODATA_PKT: ++ skb_queue_tail(&bcsp->unrel, skb); ++ break; ++ ++ default: ++ BT_ERR("Unknown packet type"); ++ kfree_skb(skb); ++ break; ++ } ++ return 0; ++} ++ ++static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, ++ int len, int pkt_type) ++{ ++ struct sk_buff *nskb; ++ u8 hdr[4], chan; ++ int rel, i; ++ ++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC ++ u16 BCSP_CRC_INIT(bcsp_txmsg_crc); ++#endif ++ ++ switch (pkt_type) { ++ case HCI_ACLDATA_PKT: ++ chan = 6; /* BCSP ACL channel */ ++ rel = 1; /* reliable channel */ ++ break; ++ case HCI_COMMAND_PKT: ++ chan = 5; /* BCSP cmd/evt channel */ ++ rel = 1; /* reliable channel */ ++ break; ++ case HCI_SCODATA_PKT: ++ chan = 7; /* BCSP SCO channel */ ++ rel = 0; /* unreliable channel */ ++ break; ++ case BCSP_LE_PKT: ++ chan = 1; /* BCSP LE channel */ ++ rel = 0; /* unreliable channel */ ++ break; ++ case BCSP_ACK_PKT: ++ chan = 0; /* BCSP internal channel */ ++ rel = 0; /* unreliable channel */ ++ break; ++ default: ++ BT_ERR("Unknown packet type"); ++ return NULL; ++ } ++ ++ /* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2 ++ (because bytes 0xc0 and 0xdb are escaped, worst case is ++ when the packet is all made of 0xc0 and 0xdb :) ) ++ + 2 (0xc0 delimiters at start and end). */ ++ ++ nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC); ++ if (!nskb) ++ return NULL; ++ ++ nskb->pkt_type = pkt_type; ++ ++ bcsp_slip_msgdelim(nskb); ++ ++ hdr[0] = bcsp->rxseq_txack << 3; ++ bcsp->txack_req = 0; ++ BT_DBG("We request packet no %u to card", bcsp->rxseq_txack); ++ ++ if (rel) { ++ hdr[0] |= 0x80 + bcsp->msgq_txseq; ++ BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq); ++ bcsp->msgq_txseq = ++(bcsp->msgq_txseq) & 0x07; ++ } ++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC ++ hdr[0] |= 0x40; ++#endif ++ ++ hdr[1] = (len << 4) & 0xFF; ++ hdr[1] |= chan; ++ hdr[2] = len >> 4; ++ hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]); ++ ++ /* Put BCSP header */ ++ for (i = 0; i < 4; i++) { ++ bcsp_slip_one_byte(nskb, hdr[i]); ++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC ++ bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]); ++#endif ++ } ++ ++ /* Put payload */ ++ for (i = 0; i < len; i++) { ++ bcsp_slip_one_byte(nskb, data[i]); ++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC ++ bcsp_crc_update(&bcsp_txmsg_crc, data[i]); ++#endif ++ } ++ ++#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC ++ /* Put CRC */ ++ bcsp_txmsg_crc = bcsp_crc_reverse(bcsp_txmsg_crc); ++ bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff)); ++ bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff)); ++#endif ++ ++ bcsp_slip_msgdelim(nskb); ++ return nskb; ++} ++ ++/* This is a rewrite of pkt_avail in ABCSP */ ++static struct sk_buff *bcsp_dequeue(struct hci_uart *hu) ++{ ++ struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv; ++ unsigned long flags; ++ struct sk_buff *skb; ++ ++ /* First of all, check for unreliable messages in the queue, ++ since they have priority */ ++ ++ if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) { ++ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type); ++ if (nskb) { ++ kfree_skb(skb); ++ return nskb; ++ } else { ++ skb_queue_head(&bcsp->unrel, skb); ++ BT_ERR("Could not dequeue pkt because alloc_skb failed"); ++ } ++ } ++ ++ /* Now, try to send a reliable pkt. We can only send a ++ reliable packet if the number of packets sent but not yet ack'ed ++ is < than the winsize */ ++ ++ spin_lock_irqsave(&bcsp->unack.lock, flags); ++ ++ if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) { ++ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type); ++ if (nskb) { ++ __skb_queue_tail(&bcsp->unack, skb); ++ mod_timer(&bcsp->tbcsp, jiffies + HZ / 4); ++ spin_unlock_irqrestore(&bcsp->unack.lock, flags); ++ return nskb; ++ } else { ++ skb_queue_head(&bcsp->rel, skb); ++ BT_ERR("Could not dequeue pkt because alloc_skb failed"); ++ } ++ } ++ ++ spin_unlock_irqrestore(&bcsp->unack.lock, flags); ++ ++ ++ /* We could not send a reliable packet, either because there are ++ none or because there are too many unack'ed pkts. Did we receive ++ any packets we have not acknowledged yet ? */ ++ ++ if (bcsp->txack_req) { ++ /* if so, craft an empty ACK pkt and send it on BCSP unreliable ++ channel 0 */ ++ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT); ++ return nskb; ++ } ++ ++ /* We have nothing to send */ ++ return NULL; ++} ++ ++static int bcsp_flush(struct hci_uart *hu) ++{ ++ BT_DBG("hu %p", hu); ++ return 0; ++} ++ ++/* Remove ack'ed packets */ ++static void bcsp_pkt_cull(struct bcsp_struct *bcsp) ++{ ++ unsigned long flags; ++ struct sk_buff *skb; ++ int i, pkts_to_be_removed; ++ u8 seqno; ++ ++ spin_lock_irqsave(&bcsp->unack.lock, flags); ++ ++ pkts_to_be_removed = bcsp->unack.qlen; ++ seqno = bcsp->msgq_txseq; ++ ++ while (pkts_to_be_removed) { ++ if (bcsp->rxack == seqno) ++ break; ++ pkts_to_be_removed--; ++ seqno = (seqno - 1) & 0x07; ++ } ++ ++ if (bcsp->rxack != seqno) ++ BT_ERR("Peer acked invalid packet"); ++ ++ BT_DBG("Removing %u pkts out of %u, up to seqno %u", ++ pkts_to_be_removed, bcsp->unack.qlen, (seqno - 1) & 0x07); ++ ++ for (i = 0, skb = ((struct sk_buff *) &bcsp->unack)->next; i < pkts_to_be_removed ++ && skb != (struct sk_buff *) &bcsp->unack; i++) { ++ struct sk_buff *nskb; ++ ++ nskb = skb->next; ++ __skb_unlink(skb, &bcsp->unack); ++ kfree_skb(skb); ++ skb = nskb; ++ } ++ if (bcsp->unack.qlen == 0) ++ del_timer(&bcsp->tbcsp); ++ spin_unlock_irqrestore(&bcsp->unack.lock, flags); ++ ++ if (i != pkts_to_be_removed) ++ BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed); ++} ++ ++/* Handle BCSP link-establishment packets. When we ++ detect a "sync" packet, symptom that the BT module has reset, ++ we do nothing :) (yet) */ ++static void bcsp_handle_le_pkt(struct hci_uart *hu) ++{ ++ struct bcsp_struct *bcsp = hu->priv; ++ u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed }; ++ u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 }; ++ u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed }; ++ ++ /* spot "conf" pkts and reply with a "conf rsp" pkt */ ++ if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 && ++ !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) { ++ struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC); ++ ++ BT_DBG("Found a LE conf pkt"); ++ if (!nskb) ++ return; ++ memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4); ++ nskb->pkt_type = BCSP_LE_PKT; ++ ++ skb_queue_head(&bcsp->unrel, nskb); ++ hci_uart_tx_wakeup(hu); ++ } ++ /* Spot "sync" pkts. If we find one...disaster! */ ++ else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 && ++ !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) { ++ BT_ERR("Found a LE sync pkt, card has reset"); ++ } ++} ++ ++static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte) ++{ ++ const u8 c0 = 0xc0, db = 0xdb; ++ ++ switch (bcsp->rx_esc_state) { ++ case BCSP_ESCSTATE_NOESC: ++ switch (byte) { ++ case 0xdb: ++ bcsp->rx_esc_state = BCSP_ESCSTATE_ESC; ++ break; ++ default: ++ memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1); ++ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && ++ bcsp->rx_state != BCSP_W4_CRC) ++ bcsp_crc_update(&bcsp->message_crc, byte); ++ bcsp->rx_count--; ++ } ++ break; ++ ++ case BCSP_ESCSTATE_ESC: ++ switch (byte) { ++ case 0xdc: ++ memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1); ++ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && ++ bcsp->rx_state != BCSP_W4_CRC) ++ bcsp_crc_update(&bcsp-> message_crc, 0xc0); ++ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; ++ bcsp->rx_count--; ++ break; ++ ++ case 0xdd: ++ memcpy(skb_put(bcsp->rx_skb, 1), &db, 1); ++ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && ++ bcsp->rx_state != BCSP_W4_CRC) ++ bcsp_crc_update(&bcsp-> message_crc, 0xdb); ++ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; ++ bcsp->rx_count--; ++ break; ++ ++ default: ++ BT_ERR ("Invalid byte %02x after esc byte", byte); ++ kfree_skb(bcsp->rx_skb); ++ bcsp->rx_skb = NULL; ++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; ++ bcsp->rx_count = 0; ++ } ++ } ++} ++ ++static inline void bcsp_complete_rx_pkt(struct hci_uart *hu) ++{ ++ struct bcsp_struct *bcsp = hu->priv; ++ int pass_up; ++ ++ if (bcsp->rx_skb->data[0] & 0x80) { /* reliable pkt */ ++ BT_DBG("Received seqno %u from card", bcsp->rxseq_txack); ++ bcsp->rxseq_txack++; ++ bcsp->rxseq_txack %= 0x8; ++ bcsp->txack_req = 1; ++ ++ /* If needed, transmit an ack pkt */ ++ hci_uart_tx_wakeup(hu); ++ } ++ ++ bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07; ++ BT_DBG("Request for pkt %u from card", bcsp->rxack); ++ ++ bcsp_pkt_cull(bcsp); ++ if ((bcsp->rx_skb->data[1] & 0x0f) == 6 && ++ bcsp->rx_skb->data[0] & 0x80) { ++ bcsp->rx_skb->pkt_type = HCI_ACLDATA_PKT; ++ pass_up = 1; ++ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 && ++ bcsp->rx_skb->data[0] & 0x80) { ++ bcsp->rx_skb->pkt_type = HCI_EVENT_PKT; ++ pass_up = 1; ++ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) { ++ bcsp->rx_skb->pkt_type = HCI_SCODATA_PKT; ++ pass_up = 1; ++ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 && ++ !(bcsp->rx_skb->data[0] & 0x80)) { ++ bcsp_handle_le_pkt(hu); ++ pass_up = 0; ++ } else ++ pass_up = 0; ++ ++ if (!pass_up) { ++ if ((bcsp->rx_skb->data[1] & 0x0f) != 0 && ++ (bcsp->rx_skb->data[1] & 0x0f) != 1) { ++ BT_ERR ("Packet for unknown channel (%u %s)", ++ bcsp->rx_skb->data[1] & 0x0f, ++ bcsp->rx_skb->data[0] & 0x80 ? ++ "reliable" : "unreliable"); ++ } ++ kfree_skb(bcsp->rx_skb); ++ } else { ++ /* Pull out BCSP hdr */ ++ skb_pull(bcsp->rx_skb, 4); ++ ++ hci_recv_frame(bcsp->rx_skb); ++ } ++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; ++ bcsp->rx_skb = NULL; ++} ++ ++/* Recv data */ ++static int bcsp_recv(struct hci_uart *hu, void *data, int count) ++{ ++ struct bcsp_struct *bcsp = hu->priv; ++ register unsigned char *ptr; ++ ++ BT_DBG("hu %p count %d rx_state %ld rx_count %ld", ++ hu, count, bcsp->rx_state, bcsp->rx_count); ++ ++ ptr = data; ++ while (count) { ++ if (bcsp->rx_count) { ++ if (*ptr == 0xc0) { ++ BT_ERR("Short BCSP packet"); ++ kfree_skb(bcsp->rx_skb); ++ bcsp->rx_state = BCSP_W4_PKT_START; ++ bcsp->rx_count = 0; ++ } else ++ bcsp_unslip_one_byte(bcsp, *ptr); ++ ++ ptr++; count--; ++ continue; ++ } ++ ++ switch (bcsp->rx_state) { ++ case BCSP_W4_BCSP_HDR: ++ if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] + ++ bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) { ++ BT_ERR("Error in BCSP hdr checksum"); ++ kfree_skb(bcsp->rx_skb); ++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; ++ bcsp->rx_count = 0; ++ continue; ++ } ++ if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */ ++ && (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) { ++ BT_ERR ("Out-of-order packet arrived, got %u expected %u", ++ bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack); ++ ++ kfree_skb(bcsp->rx_skb); ++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; ++ bcsp->rx_count = 0; ++ continue; ++ } ++ bcsp->rx_state = BCSP_W4_DATA; ++ bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) + ++ (bcsp->rx_skb->data[2] << 4); /* May be 0 */ ++ continue; ++ ++ case BCSP_W4_DATA: ++ if (bcsp->rx_skb->data[0] & 0x40) { /* pkt with crc */ ++ bcsp->rx_state = BCSP_W4_CRC; ++ bcsp->rx_count = 2; ++ } else ++ bcsp_complete_rx_pkt(hu); ++ continue; ++ ++ case BCSP_W4_CRC: ++ if (bcsp_crc_reverse(bcsp->message_crc) != ++ (bcsp->rx_skb->data[bcsp->rx_skb->len - 2] << 8) + ++ bcsp->rx_skb->data[bcsp->rx_skb->len - 1]) { ++ ++ BT_ERR ("Checksum failed: computed %04x received %04x", ++ bcsp_crc_reverse(bcsp->message_crc), ++ (bcsp->rx_skb-> data[bcsp->rx_skb->len - 2] << 8) + ++ bcsp->rx_skb->data[bcsp->rx_skb->len - 1]); ++ ++ kfree_skb(bcsp->rx_skb); ++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; ++ bcsp->rx_count = 0; ++ continue; ++ } ++ skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2); ++ bcsp_complete_rx_pkt(hu); ++ continue; ++ ++ case BCSP_W4_PKT_DELIMITER: ++ switch (*ptr) { ++ case 0xc0: ++ bcsp->rx_state = BCSP_W4_PKT_START; ++ break; ++ default: ++ /*BT_ERR("Ignoring byte %02x", *ptr);*/ ++ break; ++ } ++ ptr++; count--; ++ break; ++ ++ case BCSP_W4_PKT_START: ++ switch (*ptr) { ++ case 0xc0: ++ ptr++; count--; ++ break; ++ ++ default: ++ bcsp->rx_state = BCSP_W4_BCSP_HDR; ++ bcsp->rx_count = 4; ++ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; ++ BCSP_CRC_INIT(bcsp->message_crc); ++ ++ /* Do not increment ptr or decrement count ++ * Allocate packet. Max len of a BCSP pkt= ++ * 0xFFF (payload) +4 (header) +2 (crc) */ ++ ++ bcsp->rx_skb = bluez_skb_alloc(0x1005, GFP_ATOMIC); ++ if (!bcsp->rx_skb) { ++ BT_ERR("Can't allocate mem for new packet"); ++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; ++ bcsp->rx_count = 0; ++ return 0; ++ } ++ bcsp->rx_skb->dev = (void *) &hu->hdev; ++ break; ++ } ++ break; ++ } ++ } ++ return count; ++} ++ ++ /* Arrange to retransmit all messages in the relq. */ ++static void bcsp_timed_event(unsigned long arg) ++{ ++ struct hci_uart *hu = (struct hci_uart *) arg; ++ struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv; ++ struct sk_buff *skb; ++ unsigned long flags; ++ ++ BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen); ++ ++ spin_lock_irqsave(&bcsp->unack.lock, flags); ++ ++ while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) { ++ bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07; ++ skb_queue_head(&bcsp->rel, skb); ++ } ++ ++ spin_unlock_irqrestore(&bcsp->unack.lock, flags); ++ ++ hci_uart_tx_wakeup(hu); ++} ++ ++static int bcsp_open(struct hci_uart *hu) ++{ ++ struct bcsp_struct *bcsp; ++ ++ BT_DBG("hu %p", hu); ++ ++ bcsp = kmalloc(sizeof(*bcsp), GFP_ATOMIC); ++ if (!bcsp) ++ return -ENOMEM; ++ memset(bcsp, 0, sizeof(*bcsp)); ++ ++ hu->priv = bcsp; ++ skb_queue_head_init(&bcsp->unack); ++ skb_queue_head_init(&bcsp->rel); ++ skb_queue_head_init(&bcsp->unrel); ++ ++ init_timer(&bcsp->tbcsp); ++ bcsp->tbcsp.function = bcsp_timed_event; ++ bcsp->tbcsp.data = (u_long) hu; ++ ++ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; ++ ++ return 0; ++} ++ ++static int bcsp_close(struct hci_uart *hu) ++{ ++ struct bcsp_struct *bcsp = hu->priv; ++ hu->priv = NULL; ++ ++ BT_DBG("hu %p", hu); ++ ++ skb_queue_purge(&bcsp->unack); ++ skb_queue_purge(&bcsp->rel); ++ skb_queue_purge(&bcsp->unrel); ++ del_timer(&bcsp->tbcsp); ++ ++ kfree(bcsp); ++ return 0; ++} ++ ++static struct hci_uart_proto bcsp = { ++ id: HCI_UART_BCSP, ++ open: bcsp_open, ++ close: bcsp_close, ++ enqueue: bcsp_enqueue, ++ dequeue: bcsp_dequeue, ++ recv: bcsp_recv, ++ flush: bcsp_flush ++}; ++ ++int bcsp_init(void) ++{ ++ return hci_uart_register_proto(&bcsp); ++} ++ ++int bcsp_deinit(void) ++{ ++ return hci_uart_unregister_proto(&bcsp); ++} +diff -Nur c3000_pre/linux/drivers/bluetooth/hci_bcsp.h c3000_work/linux/drivers/bluetooth/hci_bcsp.h +--- c3000_pre/linux/drivers/bluetooth/hci_bcsp.h 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/hci_bcsp.h 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,70 @@ ++/* ++ BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ). ++ Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com> ++ ++ Based on ++ hci_h4.c by Maxim Krasnyansky <maxk@qualcomm.com> ++ ABCSP by Carl Orsborn <cjo@csr.com> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++/* ++ * $Id: hci_bcsp.h,v 1.2 2002/09/26 05:05:14 maxk Exp $ ++ */ ++ ++#ifndef __HCI_BCSP_H__ ++#define __HCI_BCSP_H__ ++ ++#define BCSP_TXWINSIZE 4 ++ ++#define BCSP_ACK_PKT 0x05 ++#define BCSP_LE_PKT 0x06 ++ ++struct bcsp_struct { ++ struct sk_buff_head unack; /* Unack'ed packets queue */ ++ struct sk_buff_head rel; /* Reliable packets queue */ ++ struct sk_buff_head unrel; /* Unreliable packets queue */ ++ ++ unsigned long rx_count; ++ struct sk_buff *rx_skb; ++ u8 rxseq_txack; /* rxseq == txack. */ ++ u8 rxack; /* Last packet sent by us that the peer ack'ed */ ++ struct timer_list tbcsp; ++ ++ enum { ++ BCSP_W4_PKT_DELIMITER, ++ BCSP_W4_PKT_START, ++ BCSP_W4_BCSP_HDR, ++ BCSP_W4_DATA, ++ BCSP_W4_CRC ++ } rx_state; ++ ++ enum { ++ BCSP_ESCSTATE_NOESC, ++ BCSP_ESCSTATE_ESC ++ } rx_esc_state; ++ ++ u16 message_crc; ++ u8 txack_req; /* Do we need to send ack's to the peer? */ ++ ++ /* Reliable packet sequence number - used to assign seq to each rel pkt. */ ++ u8 msgq_txseq; ++}; ++ ++#endif /* __HCI_BCSP_H__ */ +diff -Nur c3000_pre/linux/drivers/bluetooth/hci_h4.c c3000_work/linux/drivers/bluetooth/hci_h4.c +--- c3000_pre/linux/drivers/bluetooth/hci_h4.c 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/hci_h4.c 2004-12-16 23:01:14.000000000 +0900 +@@ -25,15 +25,14 @@ + /* + * BlueZ HCI UART(H4) protocol. + * +- * $Id: hci_h4.c,v 1.2 2002/04/17 17:37:20 maxk Exp $ ++ * $Id: hci_h4.c,v 1.3 2002/09/09 01:17:32 maxk Exp $ + */ +-#define VERSION "1.1" ++#define VERSION "1.2" + + #include <linux/config.h> + #include <linux/module.h> + + #include <linux/version.h> +-#include <linux/config.h> + #include <linux/kernel.h> + #include <linux/init.h> + #include <linux/sched.h> +@@ -64,63 +63,61 @@ + #endif + + /* Initialize protocol */ +-static int h4_open(struct n_hci *n_hci) ++static int h4_open(struct hci_uart *hu) + { + struct h4_struct *h4; + +- BT_DBG("n_hci %p", n_hci); ++ BT_DBG("hu %p", hu); + + h4 = kmalloc(sizeof(*h4), GFP_ATOMIC); + if (!h4) + return -ENOMEM; + memset(h4, 0, sizeof(*h4)); + +- n_hci->priv = h4; ++ skb_queue_head_init(&h4->txq); ++ ++ hu->priv = h4; + return 0; + } + + /* Flush protocol data */ +-static int h4_flush(struct n_hci *n_hci) ++static int h4_flush(struct hci_uart *hu) + { +- BT_DBG("n_hci %p", n_hci); ++ struct h4_struct *h4 = hu->priv; ++ ++ BT_DBG("hu %p", hu); ++ skb_queue_purge(&h4->txq); + return 0; + } + + /* Close protocol */ +-static int h4_close(struct n_hci *n_hci) ++static int h4_close(struct hci_uart *hu) + { +- struct h4_struct *h4 = n_hci->priv; +- n_hci->priv = NULL; ++ struct h4_struct *h4 = hu->priv; ++ hu->priv = NULL; + +- BT_DBG("n_hci %p", n_hci); ++ BT_DBG("hu %p", hu); + ++ skb_queue_purge(&h4->txq); + if (h4->rx_skb) + kfree_skb(h4->rx_skb); + ++ hu->priv = NULL; + kfree(h4); + return 0; + } + +-/* Send data */ +-static int h4_send(struct n_hci *n_hci, void *data, int len) ++/* Enqueue frame for transmittion (padding, crc, etc) */ ++static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb) + { +- struct tty_struct *tty = n_hci->tty; +- +- BT_DBG("n_hci %p len %d", n_hci, len); ++ struct h4_struct *h4 = hu->priv; + +- /* Send frame to TTY driver */ +- tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); +- return tty->driver.write(tty, 0, data, len); +-} +- +-/* Init frame before queueing (padding, crc, etc) */ +-static struct sk_buff* h4_preq(struct n_hci *n_hci, struct sk_buff *skb) +-{ +- BT_DBG("n_hci %p skb %p", n_hci, skb); ++ BT_DBG("hu %p skb %p", hu, skb); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &skb->pkt_type, 1); +- return skb; ++ skb_queue_tail(&h4->txq, skb); ++ return 0; + } + + static inline int h4_check_data_len(struct h4_struct *h4, int len) +@@ -132,7 +129,7 @@ + BT_DMP(h4->rx_skb->data, h4->rx_skb->len); + hci_recv_frame(h4->rx_skb); + } else if (len > room) { +- BT_ERR("Data length is to large"); ++ BT_ERR("Data length is too large"); + kfree_skb(h4->rx_skb); + } else { + h4->rx_state = H4_W4_DATA; +@@ -147,16 +144,17 @@ + } + + /* Recv data */ +-static int h4_recv(struct n_hci *n_hci, void *data, int count) ++static int h4_recv(struct hci_uart *hu, void *data, int count) + { +- struct h4_struct *h4 = n_hci->priv; ++ struct h4_struct *h4 = hu->priv; + register char *ptr; + hci_event_hdr *eh; + hci_acl_hdr *ah; + hci_sco_hdr *sh; + register int len, type, dlen; + +- BT_DBG("n_hci %p count %d rx_state %ld rx_count %ld", n_hci, count, h4->rx_state, h4->rx_count); ++ BT_DBG("hu %p count %d rx_state %ld rx_count %ld", ++ hu, count, h4->rx_state, h4->rx_count); + + ptr = data; + while (count) { +@@ -204,7 +202,7 @@ + + h4_check_data_len(h4, sh->dlen); + continue; +- }; ++ } + } + + /* H4_W4_PACKET_TYPE */ +@@ -232,7 +230,7 @@ + + default: + BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr); +- n_hci->hdev.stat.err_rx++; ++ hu->hdev.stat.err_rx++; + ptr++; count--; + continue; + }; +@@ -246,20 +244,26 @@ + h4->rx_count = 0; + return 0; + } +- h4->rx_skb->dev = (void *) &n_hci->hdev; ++ h4->rx_skb->dev = (void *) &hu->hdev; + h4->rx_skb->pkt_type = type; + } + return count; + } + ++static struct sk_buff *h4_dequeue(struct hci_uart *hu) ++{ ++ struct h4_struct *h4 = hu->priv; ++ return skb_dequeue(&h4->txq); ++} ++ + static struct hci_uart_proto h4p = { +- id: HCI_UART_H4, +- open: h4_open, +- close: h4_close, +- send: h4_send, +- recv: h4_recv, +- preq: h4_preq, +- flush: h4_flush, ++ id: HCI_UART_H4, ++ open: h4_open, ++ close: h4_close, ++ recv: h4_recv, ++ enqueue: h4_enqueue, ++ dequeue: h4_dequeue, ++ flush: h4_flush, + }; + + int h4_init(void) +diff -Nur c3000_pre/linux/drivers/bluetooth/hci_h4.h c3000_work/linux/drivers/bluetooth/hci_h4.h +--- c3000_pre/linux/drivers/bluetooth/hci_h4.h 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/hci_h4.h 2004-12-16 23:01:14.000000000 +0900 +@@ -23,7 +23,7 @@ + */ + + /* +- * $Id: hci_h4.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $ ++ * $Id: hci_h4.h,v 1.2 2002/09/09 01:17:32 maxk Exp $ + */ + + #ifdef __KERNEL__ +@@ -31,6 +31,7 @@ + unsigned long rx_state; + unsigned long rx_count; + struct sk_buff *rx_skb; ++ struct sk_buff_head txq; + }; + + /* H4 receiver States */ +diff -Nur c3000_pre/linux/drivers/bluetooth/hci_ldisc.c c3000_work/linux/drivers/bluetooth/hci_ldisc.c +--- c3000_pre/linux/drivers/bluetooth/hci_ldisc.c 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/hci_ldisc.c 2004-12-16 23:01:14.000000000 +0900 +@@ -25,9 +25,9 @@ + /* + * BlueZ HCI UART driver. + * +- * $Id: hci_ldisc.c,v 1.2 2002/04/17 17:37:20 maxk Exp $ ++ * $Id: hci_ldisc.c,v 1.5 2002/10/02 18:37:20 maxk Exp $ + */ +-#define VERSION "2.0" ++#define VERSION "2.1" + + #include <linux/config.h> + #include <linux/module.h> +@@ -87,16 +87,86 @@ + return 0; + } + +-static struct hci_uart_proto *n_hci_get_proto(unsigned int id) ++static struct hci_uart_proto *hci_uart_get_proto(unsigned int id) + { + if (id >= HCI_UART_MAX_PROTO) + return NULL; + return hup[id]; + } + ++static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type) ++{ ++ struct hci_dev *hdev = &hu->hdev; ++ ++ /* Update HCI stat counters */ ++ switch (pkt_type) { ++ case HCI_COMMAND_PKT: ++ hdev->stat.cmd_tx++; ++ break; ++ ++ case HCI_ACLDATA_PKT: ++ hdev->stat.acl_tx++; ++ break; ++ ++ case HCI_SCODATA_PKT: ++ hdev->stat.cmd_tx++; ++ break; ++ } ++} ++ ++static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) ++{ ++ struct sk_buff *skb = hu->tx_skb; ++ if (!skb) ++ skb = hu->proto->dequeue(hu); ++ else ++ hu->tx_skb = NULL; ++ return skb; ++} ++ ++int hci_uart_tx_wakeup(struct hci_uart *hu) ++{ ++ struct tty_struct *tty = hu->tty; ++ struct hci_dev *hdev = &hu->hdev; ++ struct sk_buff *skb; ++ ++ if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) { ++ set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); ++ return 0; ++ } ++ ++ BT_DBG(""); ++ ++restart: ++ clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); ++ ++ while ((skb = hci_uart_dequeue(hu))) { ++ int len; ++ ++ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); ++ len = tty->driver.write(tty, 0, skb->data, skb->len); ++ hdev->stat.byte_tx += len; ++ ++ skb_pull(skb, len); ++ if (skb->len) { ++ hu->tx_skb = skb; ++ break; ++ } ++ ++ hci_uart_tx_complete(hu, skb->pkt_type); ++ kfree_skb(skb); ++ } ++ ++ if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)) ++ goto restart; ++ ++ clear_bit(HCI_UART_SENDING, &hu->tx_state); ++ return 0; ++} ++ + /* ------- Interface to HCI layer ------ */ + /* Initialize device */ +-static int n_hci_open(struct hci_dev *hdev) ++static int hci_uart_open(struct hci_dev *hdev) + { + BT_DBG("%s %p", hdev->name, hdev); + +@@ -107,15 +177,16 @@ + } + + /* Reset device */ +-static int n_hci_flush(struct hci_dev *hdev) ++static int hci_uart_flush(struct hci_dev *hdev) + { +- struct n_hci *n_hci = (struct n_hci *) hdev->driver_data; +- struct tty_struct *tty = n_hci->tty; ++ struct hci_uart *hu = (struct hci_uart *) hdev->driver_data; ++ struct tty_struct *tty = hu->tty; + + BT_DBG("hdev %p tty %p", hdev, tty); + +- /* Drop TX queue */ +- skb_queue_purge(&n_hci->txq); ++ if (hu->tx_skb) { ++ kfree_skb(hu->tx_skb); hu->tx_skb = NULL; ++ } + + /* Flush any pending characters in the driver and discipline. */ + if (tty->ldisc.flush_buffer) +@@ -124,80 +195,30 @@ + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + +- if (n_hci->proto->flush) +- n_hci->proto->flush(n_hci); ++ if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) ++ hu->proto->flush(hu); + + return 0; + } + + /* Close device */ +-static int n_hci_close(struct hci_dev *hdev) ++static int hci_uart_close(struct hci_dev *hdev) + { + BT_DBG("hdev %p", hdev); + + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) + return 0; + +- n_hci_flush(hdev); +- return 0; +-} +- +-static int n_hci_tx_wakeup(struct n_hci *n_hci) +-{ +- struct hci_dev *hdev = &n_hci->hdev; +- +- if (test_and_set_bit(N_HCI_SENDING, &n_hci->tx_state)) { +- set_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state); +- return 0; +- } +- +- BT_DBG(""); +- do { +- register struct sk_buff *skb; +- register int len; +- +- clear_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state); +- +- if (!(skb = skb_dequeue(&n_hci->txq))) +- break; +- +- len = n_hci->proto->send(n_hci, skb->data, skb->len); +- n_hci->hdev.stat.byte_tx += len; +- +- if (len == skb->len) { +- /* Complete frame was sent */ +- +- switch (skb->pkt_type) { +- case HCI_COMMAND_PKT: +- hdev->stat.cmd_tx++; +- break; +- +- case HCI_ACLDATA_PKT: +- hdev->stat.acl_tx++; +- break; +- +- case HCI_SCODATA_PKT: +- hdev->stat.cmd_tx++; +- break; +- }; +- +- kfree_skb(skb); +- } else { +- /* Subtract sent part and requeue */ +- skb_pull(skb, len); +- skb_queue_head(&n_hci->txq, skb); +- } +- } while (test_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state)); +- clear_bit(N_HCI_SENDING, &n_hci->tx_state); ++ hci_uart_flush(hdev); + return 0; + } + + /* Send frames from HCI layer */ +-static int n_hci_send_frame(struct sk_buff *skb) ++static int hci_uart_send_frame(struct sk_buff *skb) + { + struct hci_dev* hdev = (struct hci_dev *) skb->dev; + struct tty_struct *tty; +- struct n_hci *n_hci; ++ struct hci_uart *hu; + + if (!hdev) { + BT_ERR("Frame for uknown device (hdev=NULL)"); +@@ -207,66 +228,60 @@ + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -EBUSY; + +- n_hci = (struct n_hci *) hdev->driver_data; +- tty = n_hci->tty; ++ hu = (struct hci_uart *) hdev->driver_data; ++ tty = hu->tty; + + BT_DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len); + +- if (n_hci->proto->preq) { +- skb = n_hci->proto->preq(n_hci, skb); +- if (!skb) +- return 0; +- } +- +- skb_queue_tail(&n_hci->txq, skb); +- n_hci_tx_wakeup(n_hci); ++ hu->proto->enqueue(hu, skb); ++ ++ hci_uart_tx_wakeup(hu); + return 0; + } + +-static void n_hci_destruct(struct hci_dev *hdev) ++static void hci_uart_destruct(struct hci_dev *hdev) + { +- struct n_hci *n_hci; ++ struct hci_uart *hu; + + if (!hdev) return; + + BT_DBG("%s", hdev->name); + +- n_hci = (struct n_hci *) hdev->driver_data; +- kfree(n_hci); ++ hu = (struct hci_uart *) hdev->driver_data; ++ kfree(hu); + + MOD_DEC_USE_COUNT; + } + + /* ------ LDISC part ------ */ +-/* n_hci_tty_open ++/* hci_uart_tty_open + * +- * Called when line discipline changed to N_HCI. ++ * Called when line discipline changed to HCI_UART. + * + * Arguments: + * tty pointer to tty info structure + * Return Value: + * 0 if success, otherwise error code + */ +-static int n_hci_tty_open(struct tty_struct *tty) ++static int hci_uart_tty_open(struct tty_struct *tty) + { +- struct n_hci *n_hci = (void *)tty->disc_data; ++ struct hci_uart *hu = (void *) tty->disc_data; + + BT_DBG("tty %p", tty); + +- if (n_hci) ++ if (hu) + return -EEXIST; + +- if (!(n_hci = kmalloc(sizeof(struct n_hci), GFP_KERNEL))) { ++ if (!(hu = kmalloc(sizeof(struct hci_uart), GFP_KERNEL))) { + BT_ERR("Can't allocate controll structure"); + return -ENFILE; + } +- memset(n_hci, 0, sizeof(struct n_hci)); ++ memset(hu, 0, sizeof(struct hci_uart)); + +- tty->disc_data = n_hci; +- n_hci->tty = tty; ++ tty->disc_data = hu; ++ hu->tty = tty; + +- spin_lock_init(&n_hci->rx_lock); +- skb_queue_head_init(&n_hci->txq); ++ spin_lock_init(&hu->rx_lock); + + /* Flush any pending characters in the driver and line discipline */ + if (tty->ldisc.flush_buffer) +@@ -279,34 +294,34 @@ + return 0; + } + +-/* n_hci_tty_close() ++/* hci_uart_tty_close() + * + * Called when the line discipline is changed to something + * else, the tty is closed, or the tty detects a hangup. + */ +-static void n_hci_tty_close(struct tty_struct *tty) ++static void hci_uart_tty_close(struct tty_struct *tty) + { +- struct n_hci *n_hci = (void *)tty->disc_data; ++ struct hci_uart *hu = (void *)tty->disc_data; + + BT_DBG("tty %p", tty); + + /* Detach from the tty */ + tty->disc_data = NULL; + +- if (n_hci) { +- struct hci_dev *hdev = &n_hci->hdev; +- n_hci_close(hdev); ++ if (hu) { ++ struct hci_dev *hdev = &hu->hdev; ++ hci_uart_close(hdev); + +- if (test_and_clear_bit(N_HCI_PROTO_SET, &n_hci->flags)) { +- n_hci->proto->close(n_hci); ++ if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { ++ hu->proto->close(hu); + hci_unregister_dev(hdev); + } +- ++ + MOD_DEC_USE_COUNT; + } + } + +-/* n_hci_tty_wakeup() ++/* hci_uart_tty_wakeup() + * + * Callback for transmit wakeup. Called when low level + * device driver can accept more send data. +@@ -314,24 +329,25 @@ + * Arguments: tty pointer to associated tty instance data + * Return Value: None + */ +-static void n_hci_tty_wakeup( struct tty_struct *tty ) ++static void hci_uart_tty_wakeup(struct tty_struct *tty) + { +- struct n_hci *n_hci = (void *)tty->disc_data; ++ struct hci_uart *hu = (void *)tty->disc_data; + + BT_DBG(""); + +- if (!n_hci) ++ if (!hu) + return; + +- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); ++ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + +- if (tty != n_hci->tty) ++ if (tty != hu->tty) + return; + +- n_hci_tx_wakeup(n_hci); ++ if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) ++ hci_uart_tx_wakeup(hu); + } + +-/* n_hci_tty_room() ++/* hci_uart_tty_room() + * + * Callback function from tty driver. Return the amount of + * space left in the receiver's buffer to decide if remote +@@ -340,12 +356,12 @@ + * Arguments: tty pointer to associated tty instance data + * Return Value: number of bytes left in receive buffer + */ +-static int n_hci_tty_room (struct tty_struct *tty) ++static int hci_uart_tty_room (struct tty_struct *tty) + { + return 65536; + } + +-/* n_hci_tty_receive() ++/* hci_uart_tty_receive() + * + * Called by tty low level driver when receive data is + * available. +@@ -357,42 +373,42 @@ + * + * Return Value: None + */ +-static void n_hci_tty_receive(struct tty_struct *tty, const __u8 * data, char *flags, int count) ++static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count) + { +- struct n_hci *n_hci = (void *)tty->disc_data; ++ struct hci_uart *hu = (void *)tty->disc_data; + +- if (!n_hci || tty != n_hci->tty) ++ if (!hu || tty != hu->tty) + return; + +- if (!test_bit(N_HCI_PROTO_SET, &n_hci->flags)) ++ if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) + return; + +- spin_lock(&n_hci->rx_lock); +- n_hci->proto->recv(n_hci, (void *) data, count); +- n_hci->hdev.stat.byte_rx += count; +- spin_unlock(&n_hci->rx_lock); ++ spin_lock(&hu->rx_lock); ++ hu->proto->recv(hu, (void *) data, count); ++ hu->hdev.stat.byte_rx += count; ++ spin_unlock(&hu->rx_lock); + + if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle) + tty->driver.unthrottle(tty); + } + +-static int n_hci_register_dev(struct n_hci *n_hci) ++static int hci_uart_register_dev(struct hci_uart *hu) + { + struct hci_dev *hdev; + + BT_DBG(""); + + /* Initialize and register HCI device */ +- hdev = &n_hci->hdev; ++ hdev = &hu->hdev; + + hdev->type = HCI_UART; +- hdev->driver_data = n_hci; ++ hdev->driver_data = hu; + +- hdev->open = n_hci_open; +- hdev->close = n_hci_close; +- hdev->flush = n_hci_flush; +- hdev->send = n_hci_send_frame; +- hdev->destruct = n_hci_destruct; ++ hdev->open = hci_uart_open; ++ hdev->close = hci_uart_close; ++ hdev->flush = hci_uart_flush; ++ hdev->send = hci_uart_send_frame; ++ hdev->destruct = hci_uart_destruct; + + if (hci_register_dev(hdev) < 0) { + BT_ERR("Can't register HCI device %s", hdev->name); +@@ -402,30 +418,30 @@ + return 0; + } + +-static int n_hci_set_proto(struct n_hci *n_hci, int id) ++static int hci_uart_set_proto(struct hci_uart *hu, int id) + { + struct hci_uart_proto *p; + int err; + +- p = n_hci_get_proto(id); ++ p = hci_uart_get_proto(id); + if (!p) + return -EPROTONOSUPPORT; + +- err = p->open(n_hci); ++ err = p->open(hu); + if (err) + return err; + +- n_hci->proto = p; ++ hu->proto = p; + +- err = n_hci_register_dev(n_hci); ++ err = hci_uart_register_dev(hu); + if (err) { +- p->close(n_hci); ++ p->close(hu); + return err; + } + return 0; + } + +-/* n_hci_tty_ioctl() ++/* hci_uart_tty_ioctl() + * + * Process IOCTL system call for the tty device. + * +@@ -438,24 +454,24 @@ + * + * Return Value: Command dependent + */ +-static int n_hci_tty_ioctl(struct tty_struct *tty, struct file * file, ++static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) + { +- struct n_hci *n_hci = (void *)tty->disc_data; ++ struct hci_uart *hu = (void *)tty->disc_data; + int err = 0; + + BT_DBG(""); + + /* Verify the status of the device */ +- if (!n_hci) ++ if (!hu) + return -EBADF; + + switch (cmd) { + case HCIUARTSETPROTO: +- if (!test_and_set_bit(N_HCI_PROTO_SET, &n_hci->flags)) { +- err = n_hci_set_proto(n_hci, arg); ++ if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) { ++ err = hci_uart_set_proto(hu, arg); + if (err) { +- clear_bit(N_HCI_PROTO_SET, &n_hci->flags); ++ clear_bit(HCI_UART_PROTO_SET, &hu->flags); + return err; + } + tty->low_latency = 1; +@@ -463,8 +479,8 @@ + return -EBUSY; + + case HCIUARTGETPROTO: +- if (test_bit(N_HCI_PROTO_SET, &n_hci->flags)) +- return n_hci->proto->id; ++ if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) ++ return hu->proto->id; + return -EUNATCH; + + default: +@@ -478,15 +494,15 @@ + /* + * We don't provide read/write/poll interface for user space. + */ +-static ssize_t n_hci_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr) ++static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr) + { + return 0; + } +-static ssize_t n_hci_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count) ++static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count) + { + return 0; + } +-static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait) ++static unsigned int hci_uart_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait) + { + return 0; + } +@@ -495,10 +511,14 @@ + int h4_init(void); + int h4_deinit(void); + #endif ++#ifdef CONFIG_BLUEZ_HCIUART_BCSP ++int bcsp_init(void); ++int bcsp_deinit(void); ++#endif + +-int __init n_hci_init(void) ++int __init hci_uart_init(void) + { +- static struct tty_ldisc n_hci_ldisc; ++ static struct tty_ldisc hci_uart_ldisc; + int err; + + BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", +@@ -507,20 +527,20 @@ + + /* Register the tty discipline */ + +- memset(&n_hci_ldisc, 0, sizeof (n_hci_ldisc)); +- n_hci_ldisc.magic = TTY_LDISC_MAGIC; +- n_hci_ldisc.name = "n_hci"; +- n_hci_ldisc.open = n_hci_tty_open; +- n_hci_ldisc.close = n_hci_tty_close; +- n_hci_ldisc.read = n_hci_tty_read; +- n_hci_ldisc.write = n_hci_tty_write; +- n_hci_ldisc.ioctl = n_hci_tty_ioctl; +- n_hci_ldisc.poll = n_hci_tty_poll; +- n_hci_ldisc.receive_room= n_hci_tty_room; +- n_hci_ldisc.receive_buf = n_hci_tty_receive; +- n_hci_ldisc.write_wakeup= n_hci_tty_wakeup; ++ memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc)); ++ hci_uart_ldisc.magic = TTY_LDISC_MAGIC; ++ hci_uart_ldisc.name = "n_hci"; ++ hci_uart_ldisc.open = hci_uart_tty_open; ++ hci_uart_ldisc.close = hci_uart_tty_close; ++ hci_uart_ldisc.read = hci_uart_tty_read; ++ hci_uart_ldisc.write = hci_uart_tty_write; ++ hci_uart_ldisc.ioctl = hci_uart_tty_ioctl; ++ hci_uart_ldisc.poll = hci_uart_tty_poll; ++ hci_uart_ldisc.receive_room= hci_uart_tty_room; ++ hci_uart_ldisc.receive_buf = hci_uart_tty_receive; ++ hci_uart_ldisc.write_wakeup= hci_uart_tty_wakeup; + +- if ((err = tty_register_ldisc(N_HCI, &n_hci_ldisc))) { ++ if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) { + BT_ERR("Can't register HCI line discipline (%d)", err); + return err; + } +@@ -528,25 +548,31 @@ + #ifdef CONFIG_BLUEZ_HCIUART_H4 + h4_init(); + #endif ++#ifdef CONFIG_BLUEZ_HCIUART_BCSP ++ bcsp_init(); ++#endif + + return 0; + } + +-void n_hci_cleanup(void) ++void hci_uart_cleanup(void) + { + int err; + + #ifdef CONFIG_BLUEZ_HCIUART_H4 + h4_deinit(); + #endif ++#ifdef CONFIG_BLUEZ_HCIUART_BCSP ++ bcsp_deinit(); ++#endif + + /* Release tty registration of line discipline */ + if ((err = tty_register_ldisc(N_HCI, NULL))) + BT_ERR("Can't unregister HCI line discipline (%d)", err); + } + +-module_init(n_hci_init); +-module_exit(n_hci_cleanup); ++module_init(hci_uart_init); ++module_exit(hci_uart_cleanup); + + MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); + MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION); +diff -Nur c3000_pre/linux/drivers/bluetooth/hci_uart.h c3000_work/linux/drivers/bluetooth/hci_uart.h +--- c3000_pre/linux/drivers/bluetooth/hci_uart.h 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/hci_uart.h 2004-12-16 23:01:14.000000000 +0900 +@@ -23,10 +23,10 @@ + */ + + /* +- * $Id: hci_uart.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $ ++ * $Id: hci_uart.h,v 1.2 2002/09/09 01:17:32 maxk Exp $ + */ + +-#ifndef N_HCI ++#ifndef N_HCI + #define N_HCI 15 + #endif + +@@ -35,26 +35,27 @@ + #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 n_hci; ++struct hci_uart; + + struct hci_uart_proto { + unsigned int id; +- int (*open)(struct n_hci *n_hci); +- int (*recv)(struct n_hci *n_hci, void *data, int len); +- int (*send)(struct n_hci *n_hci, void *data, int len); +- int (*close)(struct n_hci *n_hci); +- int (*flush)(struct n_hci *n_hci); +- struct sk_buff* (*preq)(struct n_hci *n_hci, struct sk_buff *skb); ++ int (*open)(struct hci_uart *hu); ++ int (*close)(struct hci_uart *hu); ++ int (*flush)(struct hci_uart *hu); ++ int (*recv)(struct hci_uart *hu, void *data, int len); ++ int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb); ++ struct sk_buff *(*dequeue)(struct hci_uart *hu); + }; + +-struct n_hci { ++struct hci_uart { + struct tty_struct *tty; + struct hci_dev hdev; + unsigned long flags; +@@ -62,19 +63,20 @@ + struct hci_uart_proto *proto; + void *priv; + +- struct sk_buff_head txq; +- unsigned long tx_state; +- spinlock_t rx_lock; ++ struct sk_buff *tx_skb; ++ unsigned long tx_state; ++ spinlock_t rx_lock; + }; + +-/* N_HCI flag bits */ +-#define N_HCI_PROTO_SET 0x00 ++/* HCI_UART flag bits */ ++#define HCI_UART_PROTO_SET 0 + + /* TX states */ +-#define N_HCI_SENDING 1 +-#define N_HCI_TX_WAKEUP 2 ++#define HCI_UART_SENDING 1 ++#define HCI_UART_TX_WAKEUP 2 + + int hci_uart_register_proto(struct hci_uart_proto *p); + int hci_uart_unregister_proto(struct hci_uart_proto *p); ++int hci_uart_tx_wakeup(struct hci_uart *hu); + + #endif /* __KERNEL__ */ +diff -Nur c3000_pre/linux/drivers/bluetooth/hci_usb.c c3000_work/linux/drivers/bluetooth/hci_usb.c +--- c3000_pre/linux/drivers/bluetooth/hci_usb.c 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/hci_usb.c 2004-12-16 23:01:14.000000000 +0900 +@@ -1,9 +1,10 @@ + /* +- BlueZ - Bluetooth protocol stack for Linux ++ HCI USB driver for Linux Bluetooth protocol stack (BlueZ) + Copyright (C) 2000-2001 Qualcomm Incorporated +- + Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> + ++ Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com> ++ + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation; +@@ -23,20 +24,17 @@ + */ + + /* +- * BlueZ HCI USB driver. + * Based on original USB Bluetooth driver for Linux kernel + * Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com> + * Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu> + * + * $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $ + */ +-#define VERSION "2.1" ++#define VERSION "2.7" + + #include <linux/config.h> + #include <linux/module.h> + +-#define __KERNEL_SYSCALLS__ +- + #include <linux/version.h> + #include <linux/kernel.h> + #include <linux/init.h> +@@ -49,15 +47,13 @@ + #include <linux/errno.h> + #include <linux/string.h> + #include <linux/skbuff.h> +-#include <linux/kmod.h> + + #include <linux/usb.h> + + #include <net/bluetooth/bluetooth.h> + #include <net/bluetooth/hci_core.h> +-#include "hci_usb.h" + +-#define HCI_MAX_PENDING (HCI_MAX_BULK_RX + HCI_MAX_BULK_TX + 1) ++#include "hci_usb.h" + + #ifndef HCI_USB_DEBUG + #undef BT_DBG +@@ -66,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 +@@ -77,6 +73,16 @@ + /* Generic Bluetooth USB device */ + { USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) }, + ++ /* AVM BlueFRITZ! USB v2.0 */ ++ { USB_DEVICE(0x057c, 0x3800) }, ++ ++ /* Bluetooth Ultraport Module from IBM */ ++ { USB_DEVICE(0x04bf, 0x030a) }, ++ ++ /* ALPS Modules with non-standard id */ ++ { USB_DEVICE(0x044e, 0x3001) }, ++ { USB_DEVICE(0x044e, 0x3002) }, ++ + /* Ericsson with non-standard id */ + { USB_DEVICE(0x0bdb, 0x1002) }, + +@@ -85,116 +91,214 @@ + + 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 */ + }; + +-static void hci_usb_interrupt(struct urb *urb); ++struct _urb *_urb_alloc(int isoc, int gfp) ++{ ++ struct _urb *_urb = kmalloc(sizeof(struct _urb) + ++ sizeof(struct iso_packet_descriptor) * isoc, gfp); ++ if (_urb) { ++ memset(_urb, 0, sizeof(*_urb)); ++ spin_lock_init(&_urb->urb.lock); ++ } ++ return _urb; ++} ++ ++struct _urb *_urb_dequeue(struct _urb_queue *q) ++{ ++ struct _urb *_urb = NULL; ++ unsigned long flags; ++ spin_lock_irqsave(&q->lock, flags); ++ { ++ struct list_head *head = &q->head; ++ struct list_head *next = head->next; ++ if (next != head) { ++ _urb = list_entry(next, struct _urb, list); ++ list_del(next); _urb->queue = NULL; ++ } ++ } ++ spin_unlock_irqrestore(&q->lock, flags); ++ return _urb; ++} ++ + static void hci_usb_rx_complete(struct urb *urb); + static void hci_usb_tx_complete(struct urb *urb); + +-static struct urb *hci_usb_get_completed(struct hci_usb *husb) ++#define __pending_tx(husb, type) (&husb->pending_tx[type-1]) ++#define __pending_q(husb, type) (&husb->pending_q[type-1]) ++#define __completed_q(husb, type) (&husb->completed_q[type-1]) ++#define __transmit_q(husb, type) (&husb->transmit_q[type-1]) ++#define __reassembly(husb, type) (husb->reassembly[type-1]) ++ ++static inline struct _urb *__get_completed(struct hci_usb *husb, int type) + { +- struct sk_buff *skb; +- struct urb *urb = NULL; ++ return _urb_dequeue(__completed_q(husb, type)); ++} + +- skb = skb_dequeue(&husb->completed_q); +- if (skb) { +- urb = ((struct hci_usb_scb *) skb->cb)->urb; +- kfree_skb(skb); +- } ++#ifdef CONFIG_BLUEZ_HCIUSB_SCO ++static void __fill_isoc_desc(struct urb *urb, int len, int mtu) ++{ ++ int offset = 0, i; + +- BT_DBG("%s urb %p", husb->hdev.name, urb); +- return urb; ++ BT_DBG("len %d mtu %d", len, mtu); ++ ++ for (i=0; i < HCI_MAX_ISOC_FRAMES && len >= mtu; i++, offset += mtu, len -= mtu) { ++ urb->iso_frame_desc[i].offset = offset; ++ urb->iso_frame_desc[i].length = mtu; ++ BT_DBG("desc %d offset %d len %d", i, offset, mtu); ++ } ++ if (len && i < HCI_MAX_ISOC_FRAMES) { ++ urb->iso_frame_desc[i].offset = offset; ++ urb->iso_frame_desc[i].length = len; ++ BT_DBG("desc %d offset %d len %d", i, offset, len); ++ i++; ++ } ++ urb->number_of_packets = i; + } ++#endif + +-static int hci_usb_enable_intr(struct hci_usb *husb) ++static int hci_usb_intr_rx_submit(struct hci_usb *husb) + { ++ struct _urb *_urb; + struct urb *urb; +- int pipe, size; ++ int err, pipe, interval, size; + void *buf; + + BT_DBG("%s", husb->hdev.name); + +- if (!(urb = usb_alloc_urb(0))) ++ size = husb->intr_in_ep->wMaxPacketSize; ++ ++ buf = kmalloc(size, GFP_ATOMIC); ++ if (!buf) + return -ENOMEM; + +- if (!(buf = kmalloc(HCI_MAX_EVENT_SIZE, GFP_KERNEL))) { +- usb_free_urb(urb); ++ _urb = _urb_alloc(0, GFP_ATOMIC); ++ if (!_urb) { ++ kfree(buf); + return -ENOMEM; + } ++ _urb->type = HCI_EVENT_PKT; ++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb); + +- husb->intr_urb = urb; +- +- pipe = usb_rcvintpipe(husb->udev, husb->intr_ep); +- size = usb_maxpacket(husb->udev, pipe, usb_pipeout(pipe)); +- FILL_INT_URB(urb, husb->udev, pipe, buf, size, +- hci_usb_interrupt, husb, husb->intr_interval); ++ urb = &_urb->urb; ++ pipe = usb_rcvintpipe(husb->udev, husb->intr_in_ep->bEndpointAddress); ++ interval = husb->intr_in_ep->bInterval; ++ FILL_INT_URB(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb, interval); + +- return usb_submit_urb(urb); ++ err = usb_submit_urb(urb); ++ if (err) { ++ BT_ERR("%s intr rx submit failed urb %p err %d", ++ husb->hdev.name, urb, err); ++ _urb_unlink(_urb); ++ _urb_free(_urb); ++ kfree(buf); ++ } ++ return err; + } + +-static int hci_usb_disable_intr(struct hci_usb *husb) ++static int hci_usb_bulk_rx_submit(struct hci_usb *husb) + { +- struct urb *urb = husb->intr_urb; +- struct sk_buff *skb; +- +- BT_DBG("%s", husb->hdev.name); ++ struct _urb *_urb; ++ struct urb *urb; ++ int err, pipe, size = HCI_MAX_FRAME_SIZE; ++ void *buf; + +- usb_unlink_urb(urb); usb_free_urb(urb); +- husb->intr_urb = NULL; ++ buf = kmalloc(size, GFP_ATOMIC); ++ if (!buf) ++ return -ENOMEM; + +- skb = husb->intr_skb; +- if (skb) { +- husb->intr_skb = NULL; +- kfree_skb(skb); ++ _urb = _urb_alloc(0, GFP_ATOMIC); ++ if (!_urb) { ++ kfree(buf); ++ return -ENOMEM; + } ++ _urb->type = HCI_ACLDATA_PKT; ++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb); + +- return 0; ++ urb = &_urb->urb; ++ pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep->bEndpointAddress); ++ FILL_BULK_URB(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb); ++ urb->transfer_flags = USB_QUEUE_BULK; ++ ++ BT_DBG("%s urb %p", husb->hdev.name, urb); ++ ++ err = usb_submit_urb(urb); ++ if (err) { ++ BT_ERR("%s bulk rx submit failed urb %p err %d", ++ husb->hdev.name, urb, err); ++ _urb_unlink(_urb); ++ _urb_free(_urb); ++ kfree(buf); ++ } ++ return err; + } + +-static int hci_usb_rx_submit(struct hci_usb *husb, struct urb *urb) ++#ifdef CONFIG_BLUEZ_HCIUSB_SCO ++static int hci_usb_isoc_rx_submit(struct hci_usb *husb) + { +- struct hci_usb_scb *scb; +- struct sk_buff *skb; +- int pipe, size, err; ++ struct _urb *_urb; ++ struct urb *urb; ++ int err, mtu, size; ++ void *buf; + +- if (!urb && !(urb = usb_alloc_urb(0))) +- return -ENOMEM; ++ mtu = husb->isoc_in_ep->wMaxPacketSize; ++ size = mtu * HCI_MAX_ISOC_FRAMES; + +- size = HCI_MAX_FRAME_SIZE; ++ buf = kmalloc(size, GFP_ATOMIC); ++ if (!buf) ++ return -ENOMEM; + +- if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) { +- usb_free_urb(urb); ++ _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC); ++ if (!_urb) { ++ kfree(buf); + return -ENOMEM; + } +- +- BT_DBG("%s urb %p", husb->hdev.name, urb); ++ _urb->type = HCI_SCODATA_PKT; ++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb); + +- skb->dev = (void *) &husb->hdev; +- skb->pkt_type = HCI_ACLDATA_PKT; ++ urb = &_urb->urb; + +- scb = (struct hci_usb_scb *) skb->cb; +- scb->urb = urb; ++ urb->context = husb; ++ urb->dev = husb->udev; ++ urb->pipe = usb_rcvisocpipe(husb->udev, husb->isoc_in_ep->bEndpointAddress); ++ urb->complete = hci_usb_rx_complete; + +- pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep); ++ urb->transfer_buffer_length = size; ++ urb->transfer_buffer = buf; ++ urb->transfer_flags = USB_ISO_ASAP; + +- FILL_BULK_URB(urb, husb->udev, pipe, skb->data, size, hci_usb_rx_complete, skb); +- urb->transfer_flags = USB_QUEUE_BULK; ++ __fill_isoc_desc(urb, size, mtu); ++ ++ BT_DBG("%s urb %p", husb->hdev.name, urb); + +- skb_queue_tail(&husb->pending_q, skb); + err = usb_submit_urb(urb); + if (err) { +- BT_ERR("%s bulk rx submit failed urb %p err %d", ++ BT_ERR("%s isoc rx submit failed urb %p err %d", + husb->hdev.name, urb, err); +- skb_unlink(skb); +- usb_free_urb(urb); ++ _urb_unlink(_urb); ++ _urb_free(_urb); ++ kfree(buf); + } + return err; + } ++#endif + + /* Initialize device */ + static int hci_usb_open(struct hci_dev *hdev) +@@ -212,10 +316,16 @@ + + write_lock_irqsave(&husb->completion_lock, flags); + +- err = hci_usb_enable_intr(husb); ++ err = hci_usb_intr_rx_submit(husb); + if (!err) { +- for (i = 0; i < HCI_MAX_BULK_TX; i++) +- hci_usb_rx_submit(husb, NULL); ++ for (i = 0; i < HCI_MAX_BULK_RX; i++) ++ hci_usb_bulk_rx_submit(husb); ++ ++#ifdef CONFIG_BLUEZ_HCIUSB_SCO ++ if (husb->isoc_iface) ++ for (i = 0; i < HCI_MAX_ISOC_RX; i++) ++ hci_usb_isoc_rx_submit(husb); ++#endif + } else { + clear_bit(HCI_RUNNING, &hdev->flags); + MOD_DEC_USE_COUNT; +@@ -229,29 +339,52 @@ + static int hci_usb_flush(struct hci_dev *hdev) + { + struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; ++ int i; + + BT_DBG("%s", hdev->name); + +- skb_queue_purge(&husb->cmd_q); +- skb_queue_purge(&husb->acl_q); ++ for (i=0; i < 4; i++) ++ skb_queue_purge(&husb->transmit_q[i]); + return 0; + } + +-static inline void hci_usb_unlink_urbs(struct hci_usb *husb) ++static void hci_usb_unlink_urbs(struct hci_usb *husb) + { +- struct sk_buff *skb; +- struct urb *urb; ++ int i; + + BT_DBG("%s", husb->hdev.name); + +- while ((skb = skb_dequeue(&husb->pending_q))) { +- urb = ((struct hci_usb_scb *) skb->cb)->urb; +- usb_unlink_urb(urb); +- kfree_skb(skb); +- } ++ for (i=0; i < 4; i++) { ++ struct _urb *_urb; ++ struct urb *urb; ++ ++ /* Kill pending requests */ ++ while ((_urb = _urb_dequeue(&husb->pending_q[i]))) { ++ urb = &_urb->urb; ++ BT_DBG("%s unlinking _urb %p type %d urb %p", ++ husb->hdev.name, _urb, _urb->type, urb); ++ usb_unlink_urb(urb); ++ _urb_queue_tail(__completed_q(husb, _urb->type), _urb); ++ } ++ ++ /* Release completed requests */ ++ while ((_urb = _urb_dequeue(&husb->completed_q[i]))) { ++ urb = &_urb->urb; ++ BT_DBG("%s freeing _urb %p type %d urb %p", ++ husb->hdev.name, _urb, _urb->type, urb); ++ if (urb->setup_packet) ++ kfree(urb->setup_packet); ++ if (urb->transfer_buffer) ++ kfree(urb->transfer_buffer); ++ _urb_free(_urb); ++ } + +- while ((urb = hci_usb_get_completed(husb))) +- usb_free_urb(urb); ++ /* Release reassembly buffers */ ++ if (husb->reassembly[i]) { ++ kfree_skb(husb->reassembly[i]); ++ husb->reassembly[i] = NULL; ++ } ++ } + } + + /* Close device */ +@@ -267,7 +400,6 @@ + + write_lock_irqsave(&husb->completion_lock, flags); + +- hci_usb_disable_intr(husb); + hci_usb_unlink_urbs(husb); + hci_usb_flush(hdev); + +@@ -277,104 +409,157 @@ + return 0; + } + ++static int __tx_submit(struct hci_usb *husb, struct _urb *_urb) ++{ ++ struct urb *urb = &_urb->urb; ++ int err; ++ ++ BT_DBG("%s urb %p type %d", husb->hdev.name, urb, _urb->type); ++ ++ _urb_queue_tail(__pending_q(husb, _urb->type), _urb); ++ err = usb_submit_urb(urb); ++ if (err) { ++ BT_ERR("%s tx submit failed urb %p type %d err %d", ++ husb->hdev.name, urb, _urb->type, err); ++ _urb_unlink(_urb); ++ _urb_queue_tail(__completed_q(husb, _urb->type), _urb); ++ } else ++ atomic_inc(__pending_tx(husb, _urb->type)); ++ ++ return err; ++} ++ + static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb) + { +- struct hci_usb_scb *scb = (void *) skb->cb; +- struct urb *urb = hci_usb_get_completed(husb); ++ struct _urb *_urb = __get_completed(husb, skb->pkt_type); + struct usb_ctrlrequest *dr; +- int pipe, err; +- +- if (!urb && !(urb = usb_alloc_urb(0))) +- return -ENOMEM; ++ struct urb *urb; + +- if (!(dr = kmalloc(sizeof(*dr), GFP_ATOMIC))) { +- usb_free_urb(urb); +- return -ENOMEM; +- } +- +- pipe = usb_sndctrlpipe(husb->udev, 0); ++ if (!_urb) { ++ _urb = _urb_alloc(0, GFP_ATOMIC); ++ if (!_urb) ++ return -ENOMEM; ++ _urb->type = skb->pkt_type; ++ ++ dr = kmalloc(sizeof(*dr), GFP_ATOMIC); ++ if (!dr) { ++ _urb_free(_urb); ++ return -ENOMEM; ++ } ++ } else ++ dr = (void *) _urb->urb.setup_packet; + +- dr->bRequestType = HCI_CTRL_REQ; ++ dr->bRequestType = husb->ctrl_req; + dr->bRequest = 0; + dr->wIndex = 0; + dr->wValue = 0; + dr->wLength = __cpu_to_le16(skb->len); + +- FILL_CONTROL_URB(urb, husb->udev, pipe, (void *) dr, +- skb->data, skb->len, hci_usb_tx_complete, skb); +- +- BT_DBG("%s urb %p len %d", husb->hdev.name, urb, skb->len); +- +- scb->urb = urb; ++ urb = &_urb->urb; ++ FILL_CONTROL_URB(urb, husb->udev, usb_sndctrlpipe(husb->udev, 0), ++ (void *) dr, skb->data, skb->len, hci_usb_tx_complete, husb); + +- skb_queue_tail(&husb->pending_q, skb); +- err = usb_submit_urb(urb); +- if (err) { +- BT_ERR("%s ctrl tx submit failed urb %p err %d", +- husb->hdev.name, urb, err); +- skb_unlink(skb); +- usb_free_urb(urb); kfree(dr); +- } +- return err; ++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len); ++ ++ _urb->priv = skb; ++ return __tx_submit(husb, _urb); + } + + static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb) + { +- struct hci_usb_scb *scb = (void *) skb->cb; +- struct urb *urb = hci_usb_get_completed(husb); +- int pipe, err; ++ struct _urb *_urb = __get_completed(husb, skb->pkt_type); ++ struct urb *urb; ++ int pipe; + +- if (!urb && !(urb = usb_alloc_urb(0))) +- return -ENOMEM; ++ if (!_urb) { ++ _urb = _urb_alloc(0, GFP_ATOMIC); ++ if (!_urb) ++ return -ENOMEM; ++ _urb->type = skb->pkt_type; ++ } + +- pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep); +- +- FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len, +- hci_usb_tx_complete, skb); ++ urb = &_urb->urb; ++ pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep->bEndpointAddress); ++ FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len, ++ hci_usb_tx_complete, husb); + urb->transfer_flags = USB_QUEUE_BULK | USB_ZERO_PACKET; + +- BT_DBG("%s urb %p len %d", husb->hdev.name, urb, skb->len); ++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len); + +- scb->urb = urb; ++ _urb->priv = skb; ++ return __tx_submit(husb, _urb); ++} + +- skb_queue_tail(&husb->pending_q, skb); +- err = usb_submit_urb(urb); +- if (err) { +- BT_ERR("%s bulk tx submit failed urb %p err %d", +- husb->hdev.name, urb, err); +- skb_unlink(skb); +- usb_free_urb(urb); ++#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); ++ struct urb *urb; ++ ++ if (!_urb) { ++ _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC); ++ if (!_urb) ++ return -ENOMEM; ++ _urb->type = skb->pkt_type; + } +- return err; ++ ++ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len); ++ ++ urb = &_urb->urb; ++ ++ urb->context = husb; ++ urb->dev = husb->udev; ++ urb->pipe = usb_sndisocpipe(husb->udev, husb->isoc_out_ep->bEndpointAddress); ++ urb->complete = hci_usb_tx_complete; ++ urb->transfer_flags = USB_ISO_ASAP; ++ ++ urb->transfer_buffer = skb->data; ++ urb->transfer_buffer_length = skb->len; ++ ++ __fill_isoc_desc(urb, skb->len, husb->isoc_out_ep->wMaxPacketSize); ++ ++ _urb->priv = skb; ++ return __tx_submit(husb, _urb); + } ++#endif + + static void hci_usb_tx_process(struct hci_usb *husb) + { ++ struct sk_buff_head *q; + struct sk_buff *skb; + + BT_DBG("%s", husb->hdev.name); + + do { + clear_bit(HCI_USB_TX_WAKEUP, &husb->state); ++ ++ /* Process command queue */ ++ q = __transmit_q(husb, HCI_COMMAND_PKT); ++ if (!atomic_read(__pending_tx(husb, HCI_COMMAND_PKT)) && ++ (skb = skb_dequeue(q))) { ++ if (hci_usb_send_ctrl(husb, skb) < 0) ++ skb_queue_head(q, skb); ++ } ++ ++#ifdef CONFIG_BLUEZ_HCIUSB_SCO ++ /* Process SCO queue */ ++ q = __transmit_q(husb, HCI_SCODATA_PKT); ++ if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX && ++ (skb = skb_dequeue(q))) { ++ if (hci_usb_send_isoc(husb, skb) < 0) ++ skb_queue_head(q, skb); ++ } ++#endif + + /* Process ACL queue */ +- while (skb_queue_len(&husb->pending_q) < HCI_MAX_PENDING && +- (skb = skb_dequeue(&husb->acl_q))) { ++ q = __transmit_q(husb, HCI_ACLDATA_PKT); ++ while (atomic_read(__pending_tx(husb, HCI_ACLDATA_PKT)) < HCI_MAX_BULK_TX && ++ (skb = skb_dequeue(q))) { + if (hci_usb_send_bulk(husb, skb) < 0) { +- skb_queue_head(&husb->acl_q, skb); ++ skb_queue_head(q, skb); + break; + } + } +- +- /* Process command queue */ +- if (!test_bit(HCI_USB_CTRL_TX, &husb->state) && +- (skb = skb_dequeue(&husb->cmd_q)) != NULL) { +- set_bit(HCI_USB_CTRL_TX, &husb->state); +- if (hci_usb_send_ctrl(husb, skb) < 0) { +- skb_queue_head(&husb->cmd_q, skb); +- clear_bit(HCI_USB_CTRL_TX, &husb->state); +- } +- } + } while(test_bit(HCI_USB_TX_WAKEUP, &husb->state)); + } + +@@ -389,7 +574,7 @@ + } + + /* Send frames from HCI layer */ +-int hci_usb_send_frame(struct sk_buff *skb) ++static int hci_usb_send_frame(struct sk_buff *skb) + { + struct hci_dev *hdev = (struct hci_dev *) skb->dev; + struct hci_usb *husb; +@@ -402,204 +587,209 @@ + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -EBUSY; + +- husb = (struct hci_usb *) hdev->driver_data; +- + BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); + +- read_lock(&husb->completion_lock); ++ husb = (struct hci_usb *) hdev->driver_data; + + switch (skb->pkt_type) { + case HCI_COMMAND_PKT: +- skb_queue_tail(&husb->cmd_q, skb); + hdev->stat.cmd_tx++; + break; + + case HCI_ACLDATA_PKT: +- skb_queue_tail(&husb->acl_q, skb); + hdev->stat.acl_tx++; + break; + ++#ifdef CONFIG_BLUEZ_HCIUSB_SCO + case HCI_SCODATA_PKT: ++ hdev->stat.sco_tx++; ++ break; ++#endif ++ + default: + kfree_skb(skb); +- break; ++ return 0; + } ++ ++ read_lock(&husb->completion_lock); ++ ++ skb_queue_tail(__transmit_q(husb, skb->pkt_type), skb); + hci_usb_tx_wakeup(husb); + + read_unlock(&husb->completion_lock); + return 0; + } + +-static void hci_usb_interrupt(struct urb *urb) ++static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int count) + { +- struct hci_usb *husb = (void *) urb->context; +- struct hci_usb_scb *scb; +- struct sk_buff *skb; +- hci_event_hdr *eh; +- __u8 *data = urb->transfer_buffer; +- int count = urb->actual_length; +- int len = HCI_EVENT_HDR_SIZE; ++ BT_DBG("%s type %d data %p count %d", husb->hdev.name, type, data, count); + +- BT_DBG("%s urb %p count %d", husb->hdev.name, urb, count); ++ husb->hdev.stat.byte_rx += count; + +- if (!test_bit(HCI_RUNNING, &husb->hdev.flags)) +- return; ++ while (count) { ++ struct sk_buff *skb = __reassembly(husb, type); ++ struct { int expect; } *scb; ++ int len = 0; ++ ++ if (!skb) { ++ /* Start of the frame */ + +- if (urb->status || !count) { +- BT_DBG("%s intr status %d, count %d", +- husb->hdev.name, urb->status, count); +- return; +- } ++ switch (type) { ++ case HCI_EVENT_PKT: ++ if (count >= HCI_EVENT_HDR_SIZE) { ++ hci_event_hdr *h = data; ++ len = HCI_EVENT_HDR_SIZE + h->plen; ++ } else ++ return -EILSEQ; ++ break; + +- read_lock(&husb->completion_lock); +- +- husb->hdev.stat.byte_rx += count; ++ case HCI_ACLDATA_PKT: ++ if (count >= HCI_ACL_HDR_SIZE) { ++ hci_acl_hdr *h = data; ++ len = HCI_ACL_HDR_SIZE + __le16_to_cpu(h->dlen); ++ } else ++ return -EILSEQ; ++ break; ++#ifdef CONFIG_BLUEZ_HCIUSB_SCO ++ case HCI_SCODATA_PKT: ++ if (count >= HCI_SCO_HDR_SIZE) { ++ hci_sco_hdr *h = data; ++ len = HCI_SCO_HDR_SIZE + h->dlen; ++ } else ++ return -EILSEQ; ++ break; ++#endif ++ } ++ BT_DBG("new packet len %d", len); + +- if (!(skb = husb->intr_skb)) { +- /* Start of the frame */ +- if (count < HCI_EVENT_HDR_SIZE) +- goto bad_len; ++ skb = bluez_skb_alloc(len, GFP_ATOMIC); ++ if (!skb) { ++ BT_ERR("%s no memory for the packet", husb->hdev.name); ++ return -ENOMEM; ++ } ++ skb->dev = (void *) &husb->hdev; ++ skb->pkt_type = type; ++ ++ __reassembly(husb, type) = skb; + +- eh = (hci_event_hdr *) data; +- len = eh->plen + HCI_EVENT_HDR_SIZE; ++ scb = (void *) skb->cb; ++ scb->expect = len; ++ } else { ++ /* Continuation */ ++ scb = (void *) skb->cb; ++ len = scb->expect; ++ } + +- if (count > len) +- goto bad_len; ++ len = min(len, count); ++ ++ memcpy(skb_put(skb, len), data, len); + +- skb = bluez_skb_alloc(len, GFP_ATOMIC); +- if (!skb) { +- BT_ERR("%s no memory for event packet", husb->hdev.name); +- goto done; ++ scb->expect -= len; ++ if (!scb->expect) { ++ /* Complete frame */ ++ __reassembly(husb, type) = NULL; ++ hci_recv_frame(skb); + } +- scb = (void *) skb->cb; + +- skb->dev = (void *) &husb->hdev; +- skb->pkt_type = HCI_EVENT_PKT; ++ count -= len; data += len; ++ } ++ return 0; ++} ++ ++static void hci_usb_rx_complete(struct urb *urb) ++{ ++ struct _urb *_urb = container_of(urb, struct _urb, urb); ++ struct hci_usb *husb = (void *) urb->context; ++ struct hci_dev *hdev = &husb->hdev; ++ int err, count = urb->actual_length; ++ ++ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb, ++ _urb->type, urb->status, count, urb->transfer_flags); ++ ++ read_lock(&husb->completion_lock); ++ ++ if (!test_bit(HCI_RUNNING, &hdev->flags)) ++ goto unlock; ++ ++ if (urb->status || !count) ++ goto resubmit; + +- husb->intr_skb = skb; +- scb->intr_len = len; ++ if (_urb->type == HCI_SCODATA_PKT) { ++#ifdef CONFIG_BLUEZ_HCIUSB_SCO ++ int i; ++ for (i=0; i < urb->number_of_packets; i++) { ++ BT_DBG("desc %d status %d offset %d len %d", i, ++ urb->iso_frame_desc[i].status, ++ urb->iso_frame_desc[i].offset, ++ urb->iso_frame_desc[i].actual_length); ++ ++ if (!urb->iso_frame_desc[i].status) ++ __recv_frame(husb, _urb->type, ++ urb->transfer_buffer + urb->iso_frame_desc[i].offset, ++ urb->iso_frame_desc[i].actual_length); ++ } ++#else ++ ; ++#endif + } else { +- /* Continuation */ +- scb = (void *) skb->cb; +- len = scb->intr_len; +- if (count > len) { +- husb->intr_skb = NULL; +- kfree_skb(skb); +- goto bad_len; ++ err = __recv_frame(husb, _urb->type, urb->transfer_buffer, count); ++ if (err < 0) { ++ BT_ERR("%s corrupted packet: type %d count %d", ++ husb->hdev.name, _urb->type, count); ++ hdev->stat.err_rx++; + } + } + +- memcpy(skb_put(skb, count), data, count); +- scb->intr_len -= count; +- +- if (!scb->intr_len) { +- /* Complete frame */ +- husb->intr_skb = NULL; +- hci_recv_frame(skb); ++resubmit: ++ if (_urb->type != HCI_EVENT_PKT) { ++ urb->dev = husb->udev; ++ err = usb_submit_urb(urb); ++ BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb, ++ _urb->type, err); + } + +-done: +- read_unlock(&husb->completion_lock); +- return; +- +-bad_len: +- BT_ERR("%s bad frame len %d expected %d", husb->hdev.name, count, len); +- husb->hdev.stat.err_rx++; ++unlock: + read_unlock(&husb->completion_lock); + } + + static void hci_usb_tx_complete(struct urb *urb) + { +- struct sk_buff *skb = (struct sk_buff *) urb->context; +- struct hci_dev *hdev = (struct hci_dev *) skb->dev; +- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; ++ struct _urb *_urb = container_of(urb, struct _urb, urb); ++ struct hci_usb *husb = (void *) urb->context; ++ struct hci_dev *hdev = &husb->hdev; + +- BT_DBG("%s urb %p status %d flags %x", husb->hdev.name, urb, ++ BT_DBG("%s urb %p status %d flags %x", hdev->name, urb, + urb->status, urb->transfer_flags); + +- if (urb->pipe == usb_sndctrlpipe(husb->udev, 0)) { +- kfree(urb->setup_packet); +- clear_bit(HCI_USB_CTRL_TX, &husb->state); +- } ++ atomic_dec(__pending_tx(husb, _urb->type)); ++ ++ urb->transfer_buffer = NULL; ++ kfree_skb((struct sk_buff *) _urb->priv); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return; + +- read_lock(&husb->completion_lock); +- + if (!urb->status) +- husb->hdev.stat.byte_tx += skb->len; ++ hdev->stat.byte_tx += urb->transfer_buffer_length; + else +- husb->hdev.stat.err_tx++; +- +- skb_unlink(skb); +- skb_queue_tail(&husb->completed_q, skb); +- hci_usb_tx_wakeup(husb); +- +- read_unlock(&husb->completion_lock); +- return; +-} +- +-static void hci_usb_rx_complete(struct urb *urb) +-{ +- struct sk_buff *skb = (struct sk_buff *) urb->context; +- struct hci_dev *hdev = (struct hci_dev *) skb->dev; +- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; +- int status, count = urb->actual_length; +- hci_acl_hdr *ah; +- int dlen, size; +- +- BT_DBG("%s urb %p status %d count %d flags %x", husb->hdev.name, urb, +- urb->status, count, urb->transfer_flags); +- +- if (!test_bit(HCI_RUNNING, &hdev->flags)) +- return; ++ hdev->stat.err_tx++; + + read_lock(&husb->completion_lock); + +- if (urb->status || !count) +- goto resubmit; +- +- husb->hdev.stat.byte_rx += count; +- +- ah = (hci_acl_hdr *) skb->data; +- dlen = __le16_to_cpu(ah->dlen); +- size = HCI_ACL_HDR_SIZE + dlen; +- +- /* Verify frame len and completeness */ +- if (count != size) { +- BT_ERR("%s corrupted ACL packet: count %d, dlen %d", +- husb->hdev.name, count, dlen); +- bluez_dump("hci_usb", skb->data, count); +- husb->hdev.stat.err_rx++; +- goto resubmit; +- } +- +- skb_unlink(skb); +- skb_put(skb, count); +- hci_recv_frame(skb); +- +- hci_usb_rx_submit(husb, urb); ++ _urb_unlink(_urb); ++ _urb_queue_tail(__completed_q(husb, _urb->type), _urb); + +- read_unlock(&husb->completion_lock); +- return; +- +-resubmit: +- urb->dev = husb->udev; +- status = usb_submit_urb(urb); +- BT_DBG("%s URB resubmit status %d", husb->hdev.name, status); ++ hci_usb_tx_wakeup(husb); ++ + read_unlock(&husb->completion_lock); + } + + static void hci_usb_destruct(struct hci_dev *hdev) + { +- struct hci_usb *husb; +- +- if (!hdev) return; ++ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; + + BT_DBG("%s", hdev->name); + +- husb = (struct hci_usb *) hdev->driver_data; + kfree(husb); + } + +@@ -621,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 */ +@@ -662,8 +858,9 @@ + bulk_out_ep[i] = ep; + break; + ++#ifdef CONFIG_BLUEZ_HCIUSB_SCO + case USB_ENDPOINT_XFER_ISOC: +- if (ep->wMaxPacketSize < size) ++ if (ep->wMaxPacketSize < size || a > 2) + break; + size = ep->wMaxPacketSize; + +@@ -676,6 +873,7 @@ + else + isoc_out_ep[i] = ep; + break; ++#endif + } + } + } +@@ -686,10 +884,12 @@ + goto done; + } + +- 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; + } ++#endif + + if (!(husb = kmalloc(sizeof(struct hci_usb), GFP_KERNEL))) { + BT_ERR("Can't allocate: control structure"); +@@ -699,35 +899,41 @@ + memset(husb, 0, sizeof(struct hci_usb)); + + husb->udev = udev; +- husb->bulk_out_ep = bulk_out_ep[0]->bEndpointAddress; +- husb->bulk_in_ep = bulk_in_ep[0]->bEndpointAddress; ++ husb->bulk_out_ep = bulk_out_ep[0]; ++ husb->bulk_in_ep = bulk_in_ep[0]; ++ husb->intr_in_ep = intr_in_ep[0]; + +- husb->intr_ep = intr_in_ep[0]->bEndpointAddress; +- husb->intr_interval = intr_in_ep[0]->bInterval; ++ if (id->driver_info & HCI_DIGIANSWER) ++ husb->ctrl_req = HCI_DIGI_REQ; ++ else ++ husb->ctrl_req = HCI_CTRL_REQ; + ++#ifdef CONFIG_BLUEZ_HCIUSB_SCO + if (isoc_iface) { ++ BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts); + if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) { + BT_ERR("Can't set isoc interface settings"); + isoc_iface = NULL; + } + usb_driver_claim_interface(&hci_usb_driver, isoc_iface, husb); + husb->isoc_iface = isoc_iface; +- +- husb->isoc_in_ep = isoc_in_ep[1]->bEndpointAddress; +- husb->isoc_out_ep = isoc_in_ep[1]->bEndpointAddress; ++ husb->isoc_in_ep = isoc_in_ep[isoc_ifnum]; ++ husb->isoc_out_ep = isoc_out_ep[isoc_ifnum]; + } +- +- husb->completion_lock = RW_LOCK_UNLOCKED; ++#endif + +- skb_queue_head_init(&husb->acl_q); +- skb_queue_head_init(&husb->cmd_q); +- skb_queue_head_init(&husb->pending_q); +- skb_queue_head_init(&husb->completed_q); ++ husb->completion_lock = RW_LOCK_UNLOCKED; ++ ++ for (i = 0; i < 4; i++) { ++ skb_queue_head_init(&husb->transmit_q[i]); ++ _urb_queue_init(&husb->pending_q[i]); ++ _urb_queue_init(&husb->completed_q[i]); ++ } + + /* Initialize and register HCI device */ + hdev = &husb->hdev; + +- hdev->type = HCI_USB; ++ hdev->type = HCI_USB; + hdev->driver_data = husb; + + hdev->open = hci_usb_open; +@@ -736,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; +@@ -798,6 +1007,6 @@ + module_init(hci_usb_init); + module_exit(hci_usb_cleanup); + +-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); ++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>"); + MODULE_DESCRIPTION("BlueZ HCI USB driver ver " VERSION); + MODULE_LICENSE("GPL"); +diff -Nur c3000_pre/linux/drivers/bluetooth/hci_usb.h c3000_work/linux/drivers/bluetooth/hci_usb.h +--- c3000_pre/linux/drivers/bluetooth/hci_usb.h 2004-08-21 09:48:24.000000000 +0900 ++++ c3000_work/linux/drivers/bluetooth/hci_usb.h 2004-12-16 23:01:14.000000000 +0900 +@@ -1,9 +1,10 @@ + /* +- BlueZ - Bluetooth protocol stack for Linux ++ HCI USB driver for Linux Bluetooth protocol stack (BlueZ) + Copyright (C) 2000-2001 Qualcomm Incorporated +- + Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> + ++ Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com> ++ + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation; +@@ -34,46 +35,113 @@ + #define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */ + + #define HCI_CTRL_REQ 0x20 ++#define HCI_DIGI_REQ 0x40 ++ ++#define HCI_IGNORE 0x01 ++#define HCI_RESET 0x02 ++#define HCI_DIGIANSWER 0x04 ++#define HCI_BROKEN_ISOC 0x08 + + #define HCI_MAX_IFACE_NUM 3 + + #define HCI_MAX_BULK_TX 4 + #define HCI_MAX_BULK_RX 1 + ++#define HCI_MAX_ISOC_RX 2 ++#define HCI_MAX_ISOC_TX 2 ++ ++#define HCI_MAX_ISOC_FRAMES 10 ++ ++struct _urb_queue { ++ struct list_head head; ++ spinlock_t lock; ++}; ++ ++struct _urb { ++ struct list_head list; ++ struct _urb_queue *queue; ++ int type; ++ void *priv; ++ struct urb urb; ++}; ++ ++struct _urb *_urb_alloc(int isoc, int gfp); ++ ++static inline void _urb_free(struct _urb *_urb) ++{ ++ kfree(_urb); ++} ++ ++static inline void _urb_queue_init(struct _urb_queue *q) ++{ ++ INIT_LIST_HEAD(&q->head); ++ spin_lock_init(&q->lock); ++} ++ ++static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&q->lock, flags); ++ list_add(&_urb->list, &q->head); _urb->queue = q; ++ spin_unlock_irqrestore(&q->lock, flags); ++} ++ ++static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&q->lock, flags); ++ list_add_tail(&_urb->list, &q->head); _urb->queue = q; ++ spin_unlock_irqrestore(&q->lock, flags); ++} ++ ++static inline void _urb_unlink(struct _urb *_urb) ++{ ++ struct _urb_queue *q = _urb->queue; ++ unsigned long flags; ++ if (q) { ++ spin_lock_irqsave(&q->lock, flags); ++ list_del(&_urb->list); _urb->queue = NULL; ++ spin_unlock_irqrestore(&q->lock, flags); ++ } ++} ++ ++struct _urb *_urb_dequeue(struct _urb_queue *q); ++ ++#ifndef container_of ++#define container_of(ptr, type, member) ({ \ ++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ ++ (type *)( (char *)__mptr - offsetof(type,member) );}) ++#endif ++ + struct hci_usb { + struct hci_dev hdev; + + unsigned long state; + + struct usb_device *udev; +- struct usb_interface *isoc_iface; + +- __u8 bulk_out_ep; +- __u8 bulk_in_ep; +- __u8 isoc_out_ep; +- __u8 isoc_in_ep; +- +- __u8 intr_ep; +- __u8 intr_interval; +- struct urb *intr_urb; +- struct sk_buff * intr_skb; ++ struct usb_endpoint_descriptor *bulk_in_ep; ++ struct usb_endpoint_descriptor *bulk_out_ep; ++ struct usb_endpoint_descriptor *intr_in_ep; ++ ++ struct usb_interface *isoc_iface; ++ struct usb_endpoint_descriptor *isoc_out_ep; ++ struct usb_endpoint_descriptor *isoc_in_ep; ++ ++ __u8 ctrl_req; ++ ++ struct sk_buff_head transmit_q[4]; ++ struct sk_buff *reassembly[4]; // Reassembly buffers + + rwlock_t completion_lock; +- +- struct sk_buff_head cmd_q; // TX Commands +- struct sk_buff_head acl_q; // TX ACLs +- struct sk_buff_head pending_q; // Pending requests +- struct sk_buff_head completed_q; // Completed requests +-}; + +-struct hci_usb_scb { +- struct urb *urb; +- int intr_len; ++ atomic_t pending_tx[4]; // Number of pending requests ++ struct _urb_queue pending_q[4]; // Pending requests ++ struct _urb_queue completed_q[4]; // Completed requests + }; + + /* States */ + #define HCI_USB_TX_PROCESS 1 + #define HCI_USB_TX_WAKEUP 2 +-#define HCI_USB_CTRL_TX 3 + + #endif /* __KERNEL__ */ +diff -Nur c3000_pre/linux/drivers/char/pcmcia/serial_cs.c c3000_work/linux/drivers/char/pcmcia/serial_cs.c +--- c3000_pre/linux/drivers/char/pcmcia/serial_cs.c 2004-08-21 09:48:33.000000000 +0900 ++++ c3000_work/linux/drivers/char/pcmcia/serial_cs.c 2004-12-16 23:01:14.000000000 +0900 +@@ -2,7 +2,7 @@ + + A driver for PCMCIA serial devices + +- serial_cs.c 1.128 2001/10/18 12:18:35 ++ serial_cs.c 1.138 2002/10/25 06:24:52 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file +@@ -72,14 +72,14 @@ + static int irq_list[4] = { -1 }; + MODULE_PARM(irq_list, "1-4i"); + +-/* Enable the speaker? */ +-INT_MODULE_PARM(do_sound, 1); ++INT_MODULE_PARM(do_sound, 1); /* Enable the speaker? */ ++INT_MODULE_PARM(buggy_uart, 0); /* Skip strict UART tests? */ + + #ifdef PCMCIA_DEBUG + INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG); + #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) + static char *version = +-"serial_cs.c 1.128 2001/10/18 12:18:35 (David Hinds)"; ++"serial_cs.c 1.138 2002/10/25 06:24:52 (David Hinds)"; + #else + #define DEBUG(n, args...) + #endif +@@ -98,6 +98,7 @@ + { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 }, + { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 }, + { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 }, ++ { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D2, 2 }, + { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 }, + { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS422, 2 }, + { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS422, 4 }, +@@ -151,7 +152,7 @@ + client_reg_t client_reg; + dev_link_t *link; + int i, ret; +- ++ + DEBUG(0, "serial_attach()\n"); + + /* Create new serial device */ +@@ -163,7 +164,7 @@ + link->release.function = &serial_release; + link->release.data = (u_long)link; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; +- link->io.NumPorts1 = 8; ++ link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) +@@ -172,13 +173,12 @@ + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; +- link->conf.Vcc = 50; + if (do_sound) { + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + } + link->conf.IntType = INT_MEMORY_AND_IO; +- ++ + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; +@@ -197,7 +197,7 @@ + serial_detach(link); + return NULL; + } +- ++ + return link; + } /* serial_attach */ + +@@ -217,7 +217,7 @@ + int ret; + + DEBUG(0, "serial_detach(0x%p)\n", link); +- ++ + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; +@@ -227,17 +227,17 @@ + del_timer(&link->release); + if (link->state & DEV_CONFIG) + serial_release((u_long)link); +- ++ + if (link->handle) { + ret = CardServices(DeregisterClient, link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } +- ++ + /* Unlink device structure, free bits */ + *linkp = link->next; + kfree(info); +- ++ + } /* serial_detach */ + + /*====================================================================*/ +@@ -257,6 +257,8 @@ + #endif + serial.irq = irq; + serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ; ++ if (buggy_uart) ++ serial.flags |= ASYNC_BUGGY_UART; + line = register_serial(&serial); + if (line < 0) { + printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx," +@@ -271,7 +273,7 @@ + if (info->ndev > 0) + info->node[info->ndev-1].next = &info->node[info->ndev]; + info->ndev++; +- ++ + return 0; + } + +@@ -322,7 +324,10 @@ + return setup_serial(info, port, config.AssignedIRQ); + } + link->conf.Vcc = config.Vcc; +- ++ ++ link->io.NumPorts1 = 8; ++ link->io.NumPorts2 = 0; ++ + /* First pass: look for a config entry that looks normal. */ + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleOffset = 0; tuple.TupleDataMax = 255; +@@ -349,7 +354,7 @@ + i = next_tuple(handle, &tuple, &parse); + } + } +- ++ + /* Second pass: try to find an entry that isn't picky about + its base address, then try to grab any standard serial port + address, and finally try to get any free port. */ +@@ -361,8 +366,7 @@ + for (j = 0; j < 5; j++) { + link->io.BasePort1 = base[j]; + link->io.IOAddrLines = base[j] ? 16 : 3; +- i = CardServices(RequestIO, link->handle, +- &link->io); ++ i = CardServices(RequestIO, link->handle, &link->io); + if (i == CS_SUCCESS) goto found_port; + } + } +@@ -374,7 +378,7 @@ + cs_error(link->handle, RequestIO, i); + return -1; + } +- ++ + i = CardServices(RequestIRQ, link->handle, &link->irq); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIRQ, i); +@@ -399,8 +403,12 @@ + u_char buf[256]; + cisparse_t parse; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; ++ config_info_t config; + int i, base2 = 0; + ++ CardServices(GetConfigurationInfo, handle, &config); ++ link->conf.Vcc = config.Vcc; ++ + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleOffset = 0; tuple.TupleDataMax = 255; + tuple.Attributes = 0; +@@ -442,12 +450,12 @@ + i = next_tuple(handle, &tuple, &parse); + } + } +- ++ + if (i != CS_SUCCESS) { +- cs_error(link->handle, RequestIO, i); +- return -1; ++ /* At worst, try to configure as a single port */ ++ return simple_config(link); + } +- ++ + i = CardServices(RequestIRQ, link->handle, &link->irq); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIRQ, i); +@@ -463,14 +471,27 @@ + cs_error(link->handle, RequestConfiguration, i); + return -1; + } +- ++ ++ /* The Oxford Semiconductor OXCF950 cards are in fact single-port: ++ 8 registers are for the UART, the others are extra registers */ ++ if (info->manfid == MANFID_OXSEMI) { ++ if (cf->index == 1 || cf->index == 3) { ++ setup_serial(info, base2, link->irq.AssignedIRQ); ++ outb(12,link->io.BasePort1+1); ++ } else { ++ setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); ++ outb(12,base2+1); ++ } ++ return 0; ++ } ++ + setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); + /* The Nokia cards are not really multiport cards */ + if (info->manfid == MANFID_NOKIA) + return 0; + for (i = 0; i < info->multi-1; i++) + setup_serial(info, base2+(8*i), link->irq.AssignedIRQ); +- ++ + return 0; + } + +@@ -531,7 +552,7 @@ + } + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; +- ++ + /* Configure card */ + link->state |= DEV_CONFIG; + +@@ -539,8 +560,8 @@ + tuple.DesiredTuple = CISTPL_LONGLINK_MFC; + tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK; + info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS); +- +- /* Is this a multiport card? */ ++ ++ /* Scan list of known multiport card ID's */ + tuple.DesiredTuple = CISTPL_MANFID; + if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { + info->manfid = le16_to_cpu(buf[0]); +@@ -595,15 +616,15 @@ + } + #endif + } +- ++ + if (info->multi > 1) + multi_config(link); + else + simple_config(link); +- ++ + if (info->ndev == 0) + goto failed; +- ++ + if (info->manfid == MANFID_IBM) { + conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; + CS_CHECK(AccessConfigurationRegister, link->handle, ®); +@@ -620,6 +641,7 @@ + cs_error(link->handle, last_fn, last_ret); + failed: + serial_release((u_long)link); ++ link->state &= ~DEV_CONFIG_PENDING; + + } /* serial_config */ + +@@ -627,7 +649,7 @@ + + After a card is removed, serial_release() will unregister the net + device, and release the PCMCIA configuration. +- ++ + ======================================================================*/ + + void serial_release(u_long arg) +@@ -635,7 +657,7 @@ + dev_link_t *link = (dev_link_t *)arg; + serial_info_t *info = link->priv; + int i; +- ++ + DEBUG(0, "serial_release(0x%p)\n", link); + + for (i = 0; i < info->ndev; i++) { +@@ -648,7 +670,7 @@ + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + } +- ++ + link->state &= ~DEV_CONFIG; + + } /* serial_release */ +@@ -659,7 +681,7 @@ + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the serial drivers from + talking to the ports. +- ++ + ======================================================================*/ + + static int serial_event(event_t event, int priority, +@@ -667,9 +689,9 @@ + { + dev_link_t *link = args->client_data; + serial_info_t *info = link->priv; +- ++ + DEBUG(1, "serial_event(0x%06x)\n", event); +- ++ + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; +@@ -714,7 +736,7 @@ + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "serial_cs: Card Services release " + "does not match!\n"); +- return -1; ++ return -EINVAL; + } + register_pccard_driver(&dev_info, &serial_attach, &serial_detach); + return 0; +diff -Nur c3000_pre/linux/drivers/char/serial.c c3000_work/linux/drivers/char/serial.c +--- c3000_pre/linux/drivers/char/serial.c 2004-08-21 09:48:26.000000000 +0900 ++++ c3000_work/linux/drivers/char/serial.c 2004-12-16 23:01:14.000000000 +0900 +@@ -961,10 +961,15 @@ + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + check_modem_status(info); ++#ifdef CONFIG_MELAN + if ((status & UART_LSR_THRE) || + /* for buggy ELAN processors */ + ((iir & UART_IIR_ID) == UART_IIR_THRI)) + transmit_chars(info, 0); ++#else ++ if (status & UART_LSR_THRE) ++ transmit_chars(info, 0); ++#endif + + next: + info = info->next_port; +diff -Nur c3000_pre/linux/drivers/input/Config.in c3000_work/linux/drivers/input/Config.in +--- c3000_pre/linux/drivers/input/Config.in 2004-08-21 09:48:34.000000000 +0900 ++++ c3000_work/linux/drivers/input/Config.in 2004-12-16 23:01:14.000000000 +0900 +@@ -14,5 +14,6 @@ + fi + dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_INPUT + dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_INPUT ++dep_tristate ' User level driver support' CONFIG_INPUT_UINPUT $CONFIG_INPUT + + endmenu +diff -Nur c3000_pre/linux/drivers/input/Makefile c3000_work/linux/drivers/input/Makefile +--- c3000_pre/linux/drivers/input/Makefile 2004-08-21 09:48:34.000000000 +0900 ++++ c3000_work/linux/drivers/input/Makefile 2004-12-16 23:01:14.000000000 +0900 +@@ -24,6 +24,7 @@ + obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o + obj-$(CONFIG_INPUT_JOYDEV) += joydev.o + obj-$(CONFIG_INPUT_EVDEV) += evdev.o ++obj-$(CONFIG_INPUT_UINPUT) += uinput.o + + # The global Rules.make. + +diff -Nur c3000_pre/linux/drivers/input/keybdev.c c3000_work/linux/drivers/input/keybdev.c +--- c3000_pre/linux/drivers/input/keybdev.c 2004-08-21 09:48:34.000000000 +0900 ++++ c3000_work/linux/drivers/input/keybdev.c 2004-12-16 23:01:14.000000000 +0900 +@@ -166,16 +166,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)); +- + } + } + +@@ -214,6 +216,12 @@ + + // printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number); + ++ if (ledstate != 0xff) { ++ input_event(dev, EV_LED, LED_SCROLLL, !!(ledstate & 0x01)); ++ input_event(dev, EV_LED, LED_NUML, !!(ledstate & 0x02)); ++ input_event(dev, EV_LED, LED_CAPSL, !!(ledstate & 0x04)); ++ } ++ + return handle; + } + +diff -Nur c3000_pre/linux/drivers/input/uinput.c c3000_work/linux/drivers/input/uinput.c +--- c3000_pre/linux/drivers/input/uinput.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/drivers/input/uinput.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,428 @@ ++/* ++ * User level driver support for input subsystem ++ * ++ * Heavily based on evdev.c by Vojtech Pavlik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> ++ * ++ * Changes/Revisions: ++ * 0.1 20/06/2002 ++ * - first public version ++ */ ++ ++#include <linux/poll.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/smp_lock.h> ++#include <linux/fs.h> ++#include <linux/miscdevice.h> ++#include <linux/uinput.h> ++ ++static int uinput_dev_open(struct input_dev *dev) ++{ ++ return 0; ++} ++ ++static void uinput_dev_close(struct input_dev *dev) ++{ ++} ++ ++static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) ++{ ++ struct uinput_device *udev; ++ ++ udev = (struct uinput_device *)dev->private; ++ ++ udev->buff[udev->head].type = type; ++ udev->buff[udev->head].code = code; ++ udev->buff[udev->head].value = value; ++ do_gettimeofday(&udev->buff[udev->head].time); ++ udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE; ++ ++ wake_up_interruptible(&udev->waitq); ++ ++ return 0; ++} ++ ++static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect) ++{ ++ return 0; ++} ++ ++static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) ++{ ++ return 0; ++} ++ ++static int uinput_create_device(struct uinput_device *udev) ++{ ++ if (!udev->dev->name) { ++ printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); ++ return -EINVAL; ++ } ++ ++ udev->dev->open = uinput_dev_open; ++ udev->dev->close = uinput_dev_close; ++ udev->dev->event = uinput_dev_event; ++ udev->dev->upload_effect = uinput_dev_upload_effect; ++ udev->dev->erase_effect = uinput_dev_erase_effect; ++ udev->dev->private = udev; ++ ++ init_waitqueue_head(&(udev->waitq)); ++ ++ input_register_device(udev->dev); ++ ++ set_bit(UIST_CREATED, &(udev->state)); ++ ++ return 0; ++} ++ ++static int uinput_destroy_device(struct uinput_device *udev) ++{ ++ if (!test_bit(UIST_CREATED, &(udev->state))) { ++ printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME); ++ return -EINVAL; ++ } ++ ++ input_unregister_device(udev->dev); ++ ++ clear_bit(UIST_CREATED, &(udev->state)); ++ ++ return 0; ++} ++ ++static int uinput_open(struct inode *inode, struct file *file) ++{ ++ struct uinput_device *newdev; ++ struct input_dev *newinput; ++ ++ newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL); ++ if (!newdev) ++ goto error; ++ memset(newdev, 0, sizeof(struct uinput_device)); ++ ++ newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL); ++ if (!newinput) ++ goto cleanup; ++ memset(newinput, 0, sizeof(struct input_dev)); ++ ++ newdev->dev = newinput; ++ ++ file->private_data = newdev; ++ ++ return 0; ++cleanup: ++ kfree(newdev); ++error: ++ return -ENOMEM; ++} ++ ++static int uinput_validate_absbits(struct input_dev *dev) ++{ ++ unsigned int cnt; ++ int retval = 0; ++ ++ for (cnt = 0; cnt < ABS_MAX; cnt++) { ++ if (!test_bit(cnt, dev->absbit)) ++ continue; ++ ++ if (/*!dev->absmin[cnt] || !dev->absmax[cnt] || */ ++ (dev->absmax[cnt] <= dev->absmin[cnt])) { ++ printk(KERN_DEBUG ++ "%s: invalid abs[%02x] min:%d max:%d\n", ++ UINPUT_NAME, cnt, ++ dev->absmin[cnt], dev->absmax[cnt]); ++ retval = -EINVAL; ++ break; ++ } ++ ++ if ((dev->absflat[cnt] < dev->absmin[cnt]) || ++ (dev->absflat[cnt] > dev->absmax[cnt])) { ++ printk(KERN_DEBUG ++ "%s: absflat[%02x] out of range: %d " ++ "(min:%d/max:%d)\n", ++ UINPUT_NAME, cnt, dev->absflat[cnt], ++ dev->absmin[cnt], dev->absmax[cnt]); ++ retval = -EINVAL; ++ break; ++ } ++ } ++ return retval; ++} ++ ++static int uinput_alloc_device(struct file *file, const char *buffer, size_t count) ++{ ++ struct uinput_user_dev *user_dev; ++ struct input_dev *dev; ++ struct uinput_device *udev; ++ int size, ++ retval; ++ ++ retval = count; ++ ++ udev = (struct uinput_device *)file->private_data; ++ dev = udev->dev; ++ ++ user_dev = kmalloc(sizeof(*user_dev), GFP_KERNEL); ++ if (!user_dev) { ++ retval = -ENOMEM; ++ goto exit; ++ } ++ ++ if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) { ++ retval = -EFAULT; ++ goto exit; ++ } ++ ++ if (NULL != dev->name) ++ kfree(dev->name); ++ ++ size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1; ++ dev->name = kmalloc(size, GFP_KERNEL); ++ if (!dev->name) { ++ retval = -ENOMEM; ++ goto exit; ++ } ++ ++ strncpy(dev->name, user_dev->name, size); ++ dev->idbus = user_dev->idbus; ++ dev->idvendor = user_dev->idvendor; ++ dev->idproduct = user_dev->idproduct; ++ dev->idversion = user_dev->idversion; ++ dev->ff_effects_max = user_dev->ff_effects_max; ++ ++ size = sizeof(int) * (ABS_MAX + 1); ++ memcpy(dev->absmax, user_dev->absmax, size); ++ memcpy(dev->absmin, user_dev->absmin, size); ++ memcpy(dev->absfuzz, user_dev->absfuzz, size); ++ memcpy(dev->absflat, user_dev->absflat, size); ++ ++ /* check if absmin/absmax/absfuzz/absflat are filled as ++ * told in Documentation/input/input-programming.txt */ ++ if (test_bit(EV_ABS, dev->evbit)) { ++ retval = uinput_validate_absbits(dev); ++ if (retval < 0) ++ kfree(dev->name); ++ } ++ ++exit: ++ kfree(user_dev); ++ return retval; ++} ++ ++static ssize_t uinput_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) ++{ ++ struct uinput_device *udev = file->private_data; ++ ++ if (test_bit(UIST_CREATED, &(udev->state))) { ++ struct input_event ev; ++ ++ if (copy_from_user(&ev, buffer, sizeof(struct input_event))) ++ return -EFAULT; ++ input_event(udev->dev, ev.type, ev.code, ev.value); ++ } ++ else ++ count = uinput_alloc_device(file, buffer, count); ++ ++ return count; ++} ++ ++static ssize_t uinput_read(struct file *file, char *buffer, size_t count, loff_t *ppos) ++{ ++ struct uinput_device *udev = file->private_data; ++ int retval = 0; ++ ++ if (!test_bit(UIST_CREATED, &(udev->state))) ++ return -ENODEV; ++ ++ if ((udev->head == udev->tail) && (file->f_flags & O_NONBLOCK)) ++ return -EAGAIN; ++ ++ retval = wait_event_interruptible(udev->waitq, ++ (udev->head != udev->tail) || ++ !test_bit(UIST_CREATED, &(udev->state))); ++ ++ if (retval) ++ return retval; ++ ++ if (!test_bit(UIST_CREATED, &(udev->state))) ++ return -ENODEV; ++ ++ while ((udev->head != udev->tail) && ++ (retval + sizeof(struct input_event) <= count)) { ++ if (copy_to_user(buffer + retval, &(udev->buff[udev->tail]), ++ sizeof(struct input_event))) return -EFAULT; ++ udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; ++ retval += sizeof(struct input_event); ++ } ++ ++ return retval; ++} ++ ++static unsigned int uinput_poll(struct file *file, poll_table *wait) ++{ ++ struct uinput_device *udev = file->private_data; ++ ++ poll_wait(file, &udev->waitq, wait); ++ ++ if (udev->head != udev->tail) ++ return POLLIN | POLLRDNORM; ++ ++ return 0; ++} ++ ++static int uinput_burn_device(struct uinput_device *udev) ++{ ++ if (test_bit(UIST_CREATED, &(udev->state))) ++ uinput_destroy_device(udev); ++ ++ kfree(udev->dev); ++ kfree(udev); ++ ++ return 0; ++} ++ ++static int uinput_close(struct inode *inode, struct file *file) ++{ ++ return uinput_burn_device((struct uinput_device *)file->private_data); ++} ++ ++static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int retval = 0; ++ struct uinput_device *udev; ++ ++ udev = (struct uinput_device *)file->private_data; ++ ++ /* device attributes can not be changed after the device is created */ ++ if (cmd >= UI_SET_EVBIT && test_bit(UIST_CREATED, &(udev->state))) ++ return -EINVAL; ++ ++ switch (cmd) { ++ case UI_DEV_CREATE: ++ retval = uinput_create_device(udev); ++ break; ++ ++ case UI_DEV_DESTROY: ++ retval = uinput_destroy_device(udev); ++ break; ++ ++ case UI_SET_EVBIT: ++ if (arg > EV_MAX) { ++ retval = -EINVAL; ++ break; ++ } ++ set_bit(arg, udev->dev->evbit); ++ break; ++ ++ case UI_SET_KEYBIT: ++ if (arg > KEY_MAX) { ++ retval = -EINVAL; ++ break; ++ } ++ set_bit(arg, udev->dev->keybit); ++ break; ++ ++ case UI_SET_RELBIT: ++ if (arg > REL_MAX) { ++ retval = -EINVAL; ++ break; ++ } ++ set_bit(arg, udev->dev->relbit); ++ break; ++ ++ case UI_SET_ABSBIT: ++ if (arg > ABS_MAX) { ++ retval = -EINVAL; ++ break; ++ } ++ set_bit(arg, udev->dev->absbit); ++ break; ++ ++ case UI_SET_MSCBIT: ++ if (arg > MSC_MAX) { ++ retval = -EINVAL; ++ break; ++ } ++ set_bit(arg, udev->dev->mscbit); ++ break; ++ ++ case UI_SET_LEDBIT: ++ if (arg > LED_MAX) { ++ retval = -EINVAL; ++ break; ++ } ++ set_bit(arg, udev->dev->ledbit); ++ break; ++ ++ case UI_SET_SNDBIT: ++ if (arg > SND_MAX) { ++ retval = -EINVAL; ++ break; ++ } ++ set_bit(arg, udev->dev->sndbit); ++ break; ++ ++ case UI_SET_FFBIT: ++ if (arg > FF_MAX) { ++ retval = -EINVAL; ++ break; ++ } ++ set_bit(arg, udev->dev->ffbit); ++ break; ++ ++ default: ++ retval = -EFAULT; ++ } ++ return retval; ++} ++ ++struct file_operations uinput_fops = { ++ owner: THIS_MODULE, ++ open: uinput_open, ++ release: uinput_close, ++ read: uinput_read, ++ write: uinput_write, ++ poll: uinput_poll, ++ ioctl: uinput_ioctl, ++}; ++ ++static struct miscdevice uinput_misc = { ++ fops: &uinput_fops, ++ minor: UINPUT_MINOR, ++ name: UINPUT_NAME, ++}; ++ ++static int __init uinput_init(void) ++{ ++ return misc_register(&uinput_misc); ++} ++ ++static void __exit uinput_exit(void) ++{ ++ misc_deregister(&uinput_misc); ++} ++ ++MODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); ++MODULE_DESCRIPTION("User level driver support for input subsystem"); ++MODULE_LICENSE("GPL"); ++ ++module_init(uinput_init); ++module_exit(uinput_exit); ++ +diff -Nur c3000_pre/linux/drivers/isdn/avmb1/capidrv.c c3000_work/linux/drivers/isdn/avmb1/capidrv.c +--- c3000_pre/linux/drivers/isdn/avmb1/capidrv.c 2004-08-21 09:48:34.000000000 +0900 ++++ c3000_work/linux/drivers/isdn/avmb1/capidrv.c 2004-12-16 23:01:14.000000000 +0900 +@@ -514,13 +514,25 @@ + + static void send_message(capidrv_contr * card, _cmsg * cmsg) + { +- struct sk_buff *skb; +- size_t len; ++ struct sk_buff *skb; ++ size_t len; ++ u16 err; ++ + capi_cmsg2message(cmsg, cmsg->buf); + len = CAPIMSG_LEN(cmsg->buf); + skb = alloc_skb(len, GFP_ATOMIC); ++ if(!skb) { ++ printk(KERN_ERR "no skb len(%d) memory\n", len); ++ return; ++ } + memcpy(skb_put(skb, len), cmsg->buf, len); +- (*capifuncs->capi_put_message) (global.appid, skb); ++ err = (*capifuncs->capi_put_message) (global.appid, skb); ++ if (err) { ++ printk(KERN_WARNING "%s: capi_put_message error: %04x\n", ++ __FUNCTION__, err); ++ kfree_skb(skb); ++ return; ++ } + global.nsentctlpkt++; + } + +@@ -2179,10 +2191,10 @@ + free_ncci(card, card->bchans[card->nbchan-1].nccip); + if (card->bchans[card->nbchan-1].plcip) + free_plci(card, card->bchans[card->nbchan-1].plcip); +- if (card->plci_list) +- printk(KERN_ERR "capidrv: bug in free_plci()\n"); + card->nbchan--; + } ++ if (card->plci_list) ++ printk(KERN_ERR "capidrv: bug in free_plci()\n"); + kfree(card->bchans); + card->bchans = 0; + +diff -Nur c3000_pre/linux/drivers/isdn/avmb1/kcapi.c c3000_work/linux/drivers/isdn/avmb1/kcapi.c +--- c3000_pre/linux/drivers/isdn/avmb1/kcapi.c 2004-08-21 09:48:34.000000000 +0900 ++++ c3000_work/linux/drivers/isdn/avmb1/kcapi.c 2004-12-16 23:01:14.000000000 +0900 +@@ -545,7 +545,13 @@ + static void notify_up(__u32 contr) + { + struct capi_interface_user *p; ++ __u16 appl; + ++ for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { ++ if (!VALID_APPLID(appl)) continue; ++ if (APPL(appl)->releasing) continue; ++ CARD(contr)->driver->register_appl(CARD(contr), appl, &APPL(appl)->rparam); ++ } + printk(KERN_NOTICE "kcapi: notify up contr %d\n", contr); + spin_lock(&capi_users_lock); + for (p = capi_users; p; p = p->next) { +@@ -705,12 +711,16 @@ + nextpp = &(*pp)->next; + } + } +- APPL(appl)->releasing--; +- if (APPL(appl)->releasing <= 0) { +- APPL(appl)->signal = 0; +- APPL_MARK_FREE(appl); +- printk(KERN_INFO "kcapi: appl %d down\n", appl); +- } ++ if (APPL(appl)->releasing) { /* only release if the application was marked for release */ ++ printk(KERN_DEBUG "kcapi: appl %d releasing(%d)\n", appl, APPL(appl)->releasing); ++ APPL(appl)->releasing--; ++ if (APPL(appl)->releasing <= 0) { ++ APPL(appl)->signal = 0; ++ APPL_MARK_FREE(appl); ++ printk(KERN_INFO "kcapi: appl %d down\n", appl); ++ } ++ } else ++ printk(KERN_WARNING "kcapi: appl %d card%d released without request\n", appl, card->cnr); + } + /* + * ncci management +@@ -863,16 +873,7 @@ + + static void controllercb_ready(struct capi_ctr * card) + { +- __u16 appl; +- + card->cardstate = CARD_RUNNING; +- +- for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { +- if (!VALID_APPLID(appl)) continue; +- if (APPL(appl)->releasing) continue; +- card->driver->register_appl(card, appl, &APPL(appl)->rparam); +- } +- + printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n", + CARDNR(card), card->name); + +diff -Nur c3000_pre/linux/drivers/usb/Config.in c3000_work/linux/drivers/usb/Config.in +--- c3000_pre/linux/drivers/usb/Config.in 2004-08-21 09:48:56.000000000 +0900 ++++ c3000_work/linux/drivers/usb/Config.in 2004-12-16 23:01:14.000000000 +0900 +@@ -63,7 +63,13 @@ + comment 'USB Device Class drivers' + dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND + dep_tristate ' EMI 2|6 USB Audio interface support' CONFIG_USB_EMI26 $CONFIG_USB_AUDIO +- dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL ++ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then ++ if [ "$CONFIG_BLUEZ" = "n" ]; then ++ dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB ++ else ++ comment ' USB Bluetooth can only be used with disabled Bluetooth subsystem' ++ fi ++ fi + dep_tristate ' USB MIDI support' CONFIG_USB_MIDI $CONFIG_USB + if [ "$CONFIG_SCSI" = "n" ]; then + comment ' SCSI support is needed for USB Storage' +diff -Nur c3000_pre/linux/drivers/usb/hid-core.c c3000_work/linux/drivers/usb/hid-core.c +--- c3000_pre/linux/drivers/usb/hid-core.c 2004-08-21 09:48:57.000000000 +0900 ++++ c3000_work/linux/drivers/usb/hid-core.c 2004-12-16 23:01:14.000000000 +0900 +@@ -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 */ +diff -Nur c3000_pre/linux/include/linux/firmware.h c3000_work/linux/include/linux/firmware.h +--- c3000_pre/linux/include/linux/firmware.h 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/include/linux/firmware.h 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,20 @@ ++#ifndef _LINUX_FIRMWARE_H ++#define _LINUX_FIRMWARE_H ++#include <linux/module.h> ++#include <linux/types.h> ++#define FIRMWARE_NAME_MAX 30 ++struct firmware { ++ size_t size; ++ u8 *data; ++}; ++int request_firmware (const struct firmware **fw, const char *name, ++ const char *device); ++int request_firmware_nowait ( ++ struct module *module, ++ const char *name, const char *device, void *context, ++ void (*cont)(const struct firmware *fw, void *context)); ++/* On 2.5 'device' is 'struct device *' */ ++ ++void release_firmware (const struct firmware *fw); ++void register_firmware (const char *name, const u8 *data, size_t size); ++#endif +diff -Nur c3000_pre/linux/include/linux/input.h c3000_work/linux/include/linux/input.h +--- c3000_pre/linux/include/linux/input.h 2004-08-21 09:49:13.000000000 +0900 ++++ c3000_work/linux/include/linux/input.h 2004-12-16 23:01:14.000000000 +0900 +@@ -471,7 +471,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 +diff -Nur c3000_pre/linux/include/linux/net.h c3000_work/linux/include/linux/net.h +--- c3000_pre/linux/include/linux/net.h 2004-08-21 09:49:13.000000000 +0900 ++++ c3000_work/linux/include/linux/net.h 2004-12-16 23:01:14.000000000 +0900 +@@ -139,6 +139,7 @@ + extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags); + extern int sock_readv_writev(int type, struct inode * inode, struct file * file, + const struct iovec * iov, long count, long size); ++extern struct socket *sockfd_lookup(int fd, int *err); + + extern int net_ratelimit(void); + extern unsigned long net_random(void); +diff -Nur c3000_pre/linux/include/linux/uinput.h c3000_work/linux/include/linux/uinput.h +--- c3000_pre/linux/include/linux/uinput.h 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/include/linux/uinput.h 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,79 @@ ++/* ++ * User level driver support for input subsystem ++ * ++ * Heavily based on evdev.c by Vojtech Pavlik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> ++ * ++ * Changes/Revisions: ++ * 0.1 20/06/2002 ++ * - first public version ++ */ ++ ++#ifndef __UINPUT_H_ ++#define __UINPUT_H_ ++ ++#ifdef __KERNEL__ ++#define UINPUT_MINOR 223 ++#define UINPUT_NAME "uinput" ++#define UINPUT_BUFFER_SIZE 16 ++ ++/* state flags => bit index for {set|clear|test}_bit ops */ ++#define UIST_CREATED 0 ++ ++struct uinput_device { ++ struct input_dev *dev; ++ unsigned long state; ++ wait_queue_head_t waitq; ++ unsigned char ready, ++ head, ++ tail; ++ struct input_event buff[UINPUT_BUFFER_SIZE]; ++}; ++#endif /* __KERNEL__ */ ++ ++/* ioctl */ ++#define UINPUT_IOCTL_BASE 'U' ++#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1) ++#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2) ++#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int) ++#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int) ++#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int) ++#define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int) ++#define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int) ++#define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int) ++#define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int) ++#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int) ++ ++#ifndef NBITS ++#define NBITS(x) ((((x)-1)/(sizeof(long)*8))+1) ++#endif /* NBITS */ ++ ++#define UINPUT_MAX_NAME_SIZE 80 ++struct uinput_user_dev { ++ char name[UINPUT_MAX_NAME_SIZE]; ++ unsigned short idbus; ++ unsigned short idvendor; ++ unsigned short idproduct; ++ unsigned short idversion; ++ int ff_effects_max; ++ int absmax[ABS_MAX + 1]; ++ int absmin[ABS_MAX + 1]; ++ int absfuzz[ABS_MAX + 1]; ++ int absflat[ABS_MAX + 1]; ++}; ++#endif /* __UINPUT_H_ */ +diff -Nur c3000_pre/linux/include/net/bluetooth/bluetooth.h c3000_work/linux/include/net/bluetooth/bluetooth.h +--- c3000_pre/linux/include/net/bluetooth/bluetooth.h 2004-08-21 09:49:13.000000000 +0900 ++++ c3000_work/linux/include/net/bluetooth/bluetooth.h 2004-12-16 23:01:14.000000000 +0900 +@@ -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); +diff -Nur c3000_pre/linux/include/net/bluetooth/hci.h c3000_work/linux/include/net/bluetooth/hci.h +--- c3000_pre/linux/include/net/bluetooth/hci.h 2004-08-21 09:49:13.000000000 +0900 ++++ c3000_work/linux/include/net/bluetooth/hci.h 2004-12-16 23:01:14.000000000 +0900 +@@ -39,6 +39,8 @@ + #define HCI_DEV_UNREG 2 + #define HCI_DEV_UP 3 + #define HCI_DEV_DOWN 4 ++#define HCI_DEV_SUSPEND 5 ++#define HCI_DEV_RESUME 6 + + /* HCI device types */ + #define HCI_VHCI 0 +@@ -46,6 +48,12 @@ + #define HCI_PCCARD 2 + #define HCI_UART 3 + #define HCI_RS232 4 ++#define HCI_PCI 5 ++ ++/* HCI device quirks */ ++enum { ++ HCI_QUIRK_RESET_ON_INIT ++}; + + /* HCI device flags */ + enum { +@@ -157,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 */ +@@ -330,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 { +@@ -436,6 +447,12 @@ + /* Status params */ + #define OGF_STATUS_PARAM 0x05 + ++/* Testing commands */ ++#define OGF_TESTING_CMD 0x3e ++ ++/* Vendor specific commands */ ++#define OGF_VENDOR_CMD 0x3f ++ + /* ---- HCI Events ---- */ + #define EVT_INQUIRY_COMPLETE 0x01 + +@@ -450,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; +@@ -542,7 +570,7 @@ + bdaddr_t bdaddr; + __u8 role; + } __attribute__ ((packed)) evt_role_change; +-#define EVT_ROLE_CHANGE_SIZE 1 ++#define EVT_ROLE_CHANGE_SIZE 8 + + #define EVT_PIN_CODE_REQ 0x16 + typedef struct { +diff -Nur c3000_pre/linux/include/net/bluetooth/hci_core.h c3000_work/linux/include/net/bluetooth/hci_core.h +--- c3000_pre/linux/include/net/bluetooth/hci_core.h 2004-08-21 09:49:13.000000000 +0900 ++++ c3000_work/linux/include/net/bluetooth/hci_core.h 2004-12-16 23:01:14.000000000 +0900 +@@ -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; +@@ -281,8 +289,14 @@ + + static inline void hci_conn_put(struct hci_conn *conn) + { +- if (atomic_dec_and_test(&conn->refcnt) && conn->out) +- hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT); ++ if (atomic_dec_and_test(&conn->refcnt)) { ++ if (conn->type == ACL_LINK) { ++ unsigned long timeo = (conn->out) ? ++ HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2; ++ hci_conn_set_timer(conn, timeo); ++ } else ++ hci_conn_set_timer(conn, HZ / 100); ++ } + } + + /* ----- HCI Devices ----- */ +@@ -302,6 +316,8 @@ + struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); + int hci_register_dev(struct hci_dev *hdev); + int hci_unregister_dev(struct hci_dev *hdev); ++int hci_suspend_dev(struct hci_dev *hdev); ++int hci_resume_dev(struct hci_dev *hdev); + int hci_dev_open(__u16 dev); + int hci_dev_close(__u16 dev); + int hci_dev_reset(__u16 dev); +@@ -429,7 +445,6 @@ + int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param); + int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags); + int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); +-int hci_send_raw(struct sk_buff *skb); + + void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf); + +@@ -447,7 +462,7 @@ + }; + + /* HCI security filter */ +-#define HCI_SFLT_MAX_OGF 4 ++#define HCI_SFLT_MAX_OGF 5 + + struct hci_sec_filter { + __u32 type_mask; +@@ -455,7 +470,6 @@ + __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; + }; + +- + /* ----- HCI requests ----- */ + #define HCI_REQ_DONE 0 + #define HCI_REQ_PEND 1 +diff -Nur c3000_pre/linux/include/net/bluetooth/l2cap.h c3000_work/linux/include/net/bluetooth/l2cap.h +--- c3000_pre/linux/include/net/bluetooth/l2cap.h 2004-08-21 09:49:13.000000000 +0900 ++++ c3000_work/linux/include/net/bluetooth/l2cap.h 2004-12-16 23:01:14.000000000 +0900 +@@ -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 */ +diff -Nur c3000_pre/linux/include/net/bluetooth/rfcomm.h c3000_work/linux/include/net/bluetooth/rfcomm.h +--- c3000_pre/linux/include/net/bluetooth/rfcomm.h 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/include/net/bluetooth/rfcomm.h 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,361 @@ ++/* ++ RFCOMM implementation for Linux Bluetooth stack (BlueZ). ++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> ++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++/* ++ RPN support - Dirk Husemann <hud@zurich.ibm.com> ++*/ ++ ++/* ++ * $Id: rfcomm.h,v 1.31 2002/10/18 20:12:11 maxk Exp $ ++ */ ++ ++#ifndef __RFCOMM_H ++#define __RFCOMM_H ++ ++#define RFCOMM_PSM 3 ++ ++#define RFCOMM_CONN_TIMEOUT (HZ * 30) ++#define RFCOMM_DISC_TIMEOUT (HZ * 20) ++ ++#define RFCOMM_DEFAULT_MTU 127 ++#define RFCOMM_DEFAULT_CREDITS 7 ++ ++#define RFCOMM_MAX_L2CAP_MTU 1024 ++#define RFCOMM_MAX_CREDITS 40 ++ ++#define RFCOMM_SKB_HEAD_RESERVE 8 ++#define RFCOMM_SKB_TAIL_RESERVE 2 ++#define RFCOMM_SKB_RESERVE (RFCOMM_SKB_HEAD_RESERVE + RFCOMM_SKB_TAIL_RESERVE) ++ ++#define RFCOMM_SABM 0x2f ++#define RFCOMM_DISC 0x43 ++#define RFCOMM_UA 0x63 ++#define RFCOMM_DM 0x0f ++#define RFCOMM_UIH 0xef ++ ++#define RFCOMM_TEST 0x08 ++#define RFCOMM_FCON 0x28 ++#define RFCOMM_FCOFF 0x18 ++#define RFCOMM_MSC 0x38 ++#define RFCOMM_RPN 0x24 ++#define RFCOMM_RLS 0x14 ++#define RFCOMM_PN 0x20 ++#define RFCOMM_NSC 0x04 ++ ++#define RFCOMM_V24_FC 0x02 ++#define RFCOMM_V24_RTC 0x04 ++#define RFCOMM_V24_RTR 0x08 ++#define RFCOMM_V24_IC 0x40 ++#define RFCOMM_V24_DV 0x80 ++ ++#define RFCOMM_RPN_BR_2400 0x0 ++#define RFCOMM_RPN_BR_4800 0x1 ++#define RFCOMM_RPN_BR_7200 0x2 ++#define RFCOMM_RPN_BR_9600 0x3 ++#define RFCOMM_RPN_BR_19200 0x4 ++#define RFCOMM_RPN_BR_38400 0x5 ++#define RFCOMM_RPN_BR_57600 0x6 ++#define RFCOMM_RPN_BR_115200 0x7 ++#define RFCOMM_RPN_BR_230400 0x8 ++ ++#define RFCOMM_RPN_DATA_5 0x0 ++#define RFCOMM_RPN_DATA_6 0x1 ++#define RFCOMM_RPN_DATA_7 0x2 ++#define RFCOMM_RPN_DATA_8 0x3 ++ ++#define RFCOMM_RPN_STOP_1 0 ++#define RFCOMM_RPN_STOP_15 1 ++ ++#define RFCOMM_RPN_PARITY_NONE 0x0 ++#define RFCOMM_RPN_PARITY_ODD 0x4 ++#define RFCOMM_RPN_PARITY_EVEN 0x5 ++#define RFCOMM_RPN_PARITY_MARK 0x6 ++#define RFCOMM_RPN_PARITY_SPACE 0x7 ++ ++#define RFCOMM_RPN_FLOW_NONE 0x00 ++ ++#define RFCOMM_RPN_XON_CHAR 0x11 ++#define RFCOMM_RPN_XOFF_CHAR 0x13 ++ ++#define RFCOMM_RPN_PM_BITRATE 0x0001 ++#define RFCOMM_RPN_PM_DATA 0x0002 ++#define RFCOMM_RPN_PM_STOP 0x0004 ++#define RFCOMM_RPN_PM_PARITY 0x0008 ++#define RFCOMM_RPN_PM_PARITY_TYPE 0x0010 ++#define RFCOMM_RPN_PM_XON 0x0020 ++#define RFCOMM_RPN_PM_XOFF 0x0040 ++#define RFCOMM_RPN_PM_FLOW 0x3F00 ++ ++#define RFCOMM_RPN_PM_ALL 0x3F7F ++ ++struct rfcomm_hdr { ++ u8 addr; ++ u8 ctrl; ++ u8 len; // Actual size can be 2 bytes ++} __attribute__ ((packed)); ++ ++struct rfcomm_cmd { ++ u8 addr; ++ u8 ctrl; ++ u8 len; ++ u8 fcs; ++} __attribute__ ((packed)); ++ ++struct rfcomm_mcc { ++ u8 type; ++ u8 len; ++} __attribute__ ((packed)); ++ ++struct rfcomm_pn { ++ u8 dlci; ++ u8 flow_ctrl; ++ u8 priority; ++ u8 ack_timer; ++ u16 mtu; ++ u8 max_retrans; ++ u8 credits; ++} __attribute__ ((packed)); ++ ++struct rfcomm_rpn { ++ u8 dlci; ++ u8 bit_rate; ++ u8 line_settings; ++ u8 flow_ctrl; ++ u8 xon_char; ++ u8 xoff_char; ++ u16 param_mask; ++} __attribute__ ((packed)); ++ ++struct rfcomm_rls { ++ u8 dlci; ++ u8 status; ++} __attribute__ ((packed)); ++ ++struct rfcomm_msc { ++ u8 dlci; ++ u8 v24_sig; ++} __attribute__ ((packed)); ++ ++/* ---- Core structures, flags etc ---- */ ++ ++struct rfcomm_session { ++ struct list_head list; ++ struct socket *sock; ++ unsigned long state; ++ unsigned long flags; ++ atomic_t refcnt; ++ int initiator; ++ ++ /* Default DLC parameters */ ++ int cfc; ++ uint mtu; ++ ++ struct list_head dlcs; ++}; ++ ++struct rfcomm_dlc { ++ struct list_head list; ++ struct rfcomm_session *session; ++ struct sk_buff_head tx_queue; ++ struct timer_list timer; ++ ++ spinlock_t lock; ++ unsigned long state; ++ unsigned long flags; ++ atomic_t refcnt; ++ u8 dlci; ++ u8 addr; ++ u8 priority; ++ u8 v24_sig; ++ u8 mscex; ++ ++ uint mtu; ++ uint cfc; ++ uint rx_credits; ++ uint tx_credits; ++ ++ void *owner; ++ ++ void (*data_ready)(struct rfcomm_dlc *d, struct sk_buff *skb); ++ void (*state_change)(struct rfcomm_dlc *d, int err); ++ void (*modem_status)(struct rfcomm_dlc *d, u8 v24_sig); ++}; ++ ++/* DLC and session flags */ ++#define RFCOMM_RX_THROTTLED 0 ++#define RFCOMM_TX_THROTTLED 1 ++#define RFCOMM_MSC_PENDING 2 ++#define RFCOMM_TIMED_OUT 3 ++ ++/* Scheduling flags and events */ ++#define RFCOMM_SCHED_STATE 0 ++#define RFCOMM_SCHED_RX 1 ++#define RFCOMM_SCHED_TX 2 ++#define RFCOMM_SCHED_TIMEO 3 ++#define RFCOMM_SCHED_WAKEUP 31 ++ ++/* MSC exchange flags */ ++#define RFCOMM_MSCEX_TX 1 ++#define RFCOMM_MSCEX_RX 2 ++#define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX) ++ ++/* CFC states */ ++#define RFCOMM_CFC_UNKNOWN -1 ++#define RFCOMM_CFC_DISABLED 0 ++#define RFCOMM_CFC_ENABLED RFCOMM_MAX_CREDITS ++ ++extern struct task_struct *rfcomm_thread; ++extern unsigned long rfcomm_event; ++ ++static inline void rfcomm_schedule(uint event) ++{ ++ if (!rfcomm_thread) ++ return; ++ set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event); ++ wake_up_process(rfcomm_thread); ++} ++ ++extern struct semaphore rfcomm_sem; ++#define rfcomm_lock() down(&rfcomm_sem); ++#define rfcomm_unlock() up(&rfcomm_sem); ++ ++/* ---- RFCOMM DLCs (channels) ---- */ ++struct rfcomm_dlc *rfcomm_dlc_alloc(int prio); ++void rfcomm_dlc_free(struct rfcomm_dlc *d); ++int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel); ++int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason); ++int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb); ++int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig); ++int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig); ++ ++#define rfcomm_dlc_lock(d) spin_lock(&d->lock) ++#define rfcomm_dlc_unlock(d) spin_unlock(&d->lock) ++ ++static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d) ++{ ++ atomic_inc(&d->refcnt); ++} ++ ++static inline void rfcomm_dlc_put(struct rfcomm_dlc *d) ++{ ++ if (atomic_dec_and_test(&d->refcnt)) ++ rfcomm_dlc_free(d); ++} ++ ++extern void FASTCALL(__rfcomm_dlc_throttle(struct rfcomm_dlc *d)); ++extern void FASTCALL(__rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)); ++ ++static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d) ++{ ++ if (!test_and_set_bit(RFCOMM_RX_THROTTLED, &d->flags)) ++ __rfcomm_dlc_throttle(d); ++} ++ ++static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) ++{ ++ if (test_and_clear_bit(RFCOMM_RX_THROTTLED, &d->flags)) ++ __rfcomm_dlc_unthrottle(d); ++} ++ ++/* ---- RFCOMM sessions ---- */ ++struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state); ++struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst); ++struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err); ++void rfcomm_session_del(struct rfcomm_session *s); ++void rfcomm_session_close(struct rfcomm_session *s, int err); ++void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst); ++ ++static inline void rfcomm_session_hold(struct rfcomm_session *s) ++{ ++ atomic_inc(&s->refcnt); ++} ++ ++static inline void rfcomm_session_put(struct rfcomm_session *s) ++{ ++ if (atomic_dec_and_test(&s->refcnt)) ++ rfcomm_session_del(s); ++} ++ ++/* ---- RFCOMM chechsum ---- */ ++extern u8 rfcomm_crc_table[]; ++ ++/* ---- RFCOMM sockets ---- */ ++struct sockaddr_rc { ++ sa_family_t rc_family; ++ bdaddr_t rc_bdaddr; ++ u8 rc_channel; ++}; ++ ++#define rfcomm_pi(sk) ((struct rfcomm_pinfo *) &sk->tp_pinfo) ++ ++struct rfcomm_pinfo { ++ struct rfcomm_dlc *dlc; ++ u8 channel; ++}; ++ ++int rfcomm_init_sockets(void); ++void rfcomm_cleanup_sockets(void); ++ ++int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d); ++ ++/* ---- RFCOMM TTY ---- */ ++#define RFCOMM_MAX_DEV 256 ++ ++#define RFCOMMCREATEDEV _IOW('R', 200, int) ++#define RFCOMMRELEASEDEV _IOW('R', 201, int) ++#define RFCOMMGETDEVLIST _IOR('R', 210, int) ++#define RFCOMMGETDEVINFO _IOR('R', 211, int) ++#define RFCOMMSTEALDLC _IOW('R', 220, int) ++ ++#define RFCOMM_REUSE_DLC 0 ++#define RFCOMM_RELEASE_ONHUP 1 ++#define RFCOMM_HANGUP_NOW 2 ++#define RFCOMM_TTY_ATTACHED 3 ++ ++struct rfcomm_dev_req { ++ s16 dev_id; ++ u32 flags; ++ bdaddr_t src; ++ bdaddr_t dst; ++ u8 channel; ++}; ++ ++struct rfcomm_dev_info { ++ s16 id; ++ u32 flags; ++ u16 state; ++ bdaddr_t src; ++ bdaddr_t dst; ++ u8 channel; ++}; ++ ++struct rfcomm_dev_list_req { ++ u16 dev_num; ++ struct rfcomm_dev_info dev_info[0]; ++}; ++ ++int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg); ++int rfcomm_init_ttys(void); ++void rfcomm_cleanup_ttys(void); ++ ++#endif /* __RFCOMM_H */ +diff -Nur c3000_pre/linux/include/pcmcia/ciscode.h c3000_work/linux/include/pcmcia/ciscode.h +--- c3000_pre/linux/include/pcmcia/ciscode.h 2004-08-21 09:49:13.000000000 +0900 ++++ c3000_work/linux/include/pcmcia/ciscode.h 2004-12-16 23:01:14.000000000 +0900 +@@ -1,5 +1,5 @@ + /* +- * ciscode.h 1.48 2001/08/24 12:16:12 ++ * ciscode.h 1.57 2002/11/03 20:38:14 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in +@@ -60,6 +60,10 @@ + #define PRODID_INTEL_DUAL_RS232 0x0301 + #define PRODID_INTEL_2PLUS 0x8422 + ++#define MANFID_KME 0x0032 ++#define PRODID_KME_KXLC005_A 0x0704 ++#define PRODID_KME_KXLC005_B 0x2904 ++ + #define MANFID_LINKSYS 0x0143 + #define PRODID_LINKSYS_PCMLM28 0xc0ab + #define PRODID_LINKSYS_3400 0x3341 +@@ -94,6 +98,8 @@ + #define PRODID_OSITECH_JACK_336 0x0007 + #define PRODID_OSITECH_SEVEN 0x0008 + ++#define MANFID_OXSEMI 0x0279 ++ + #define MANFID_PIONEER 0x000b + + #define MANFID_PSION 0x016c +@@ -103,6 +109,7 @@ + #define PRODID_QUATECH_SPP100 0x0003 + #define PRODID_QUATECH_DUAL_RS232 0x0012 + #define PRODID_QUATECH_DUAL_RS232_D1 0x0007 ++#define PRODID_QUATECH_DUAL_RS232_D2 0x0052 + #define PRODID_QUATECH_QUAD_RS232 0x001b + #define PRODID_QUATECH_DUAL_RS422 0x000e + #define PRODID_QUATECH_QUAD_RS422 0x0045 +@@ -120,9 +127,12 @@ + + #define MANFID_TDK 0x0105 + #define PRODID_TDK_CF010 0x0900 ++#define PRODID_TDK_GN3410 0x4815 + + #define MANFID_TOSHIBA 0x0098 + ++#define MANFID_UNGERMANN 0x02c0 ++ + #define MANFID_XIRCOM 0x0105 + + #endif /* _LINUX_CISCODE_H */ +diff -Nur c3000_pre/linux/kernel/ksyms.c c3000_work/linux/kernel/ksyms.c +--- c3000_pre/linux/kernel/ksyms.c 2004-08-21 09:49:14.000000000 +0900 ++++ c3000_work/linux/kernel/ksyms.c 2004-12-16 23:01:14.000000000 +0900 +@@ -51,6 +51,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) +@@ -587,6 +588,13 @@ + EXPORT_SYMBOL(md5_pad); + EXPORT_SYMBOL(md5_result); + ++#ifdef CONFIG_FW_LOADER ++EXPORT_SYMBOL(release_firmware); ++EXPORT_SYMBOL(request_firmware); ++EXPORT_SYMBOL(request_firmware_nowait); ++EXPORT_SYMBOL(register_firmware); ++#endif ++ + /* software interrupts */ + EXPORT_SYMBOL(tasklet_hi_vec); + EXPORT_SYMBOL(tasklet_vec); +diff -Nur c3000_pre/linux/lib/Config.in c3000_work/linux/lib/Config.in +--- c3000_pre/linux/lib/Config.in 2004-08-21 09:49:14.000000000 +0900 ++++ c3000_work/linux/lib/Config.in 2004-12-16 23:01:14.000000000 +0900 +@@ -9,4 +9,9 @@ + + source crypto/Config.in + ++if [ "$CONFIG_EXPERIMENTAL" = "y" -a \ ++ "$CONFIG_HOTPLUG" = "y" ]; then ++ tristate 'Hotplug firmware loading support (EXPERIMENTAL)' CONFIG_FW_LOADER ++fi ++ + endmenu +diff -Nur c3000_pre/linux/lib/Makefile c3000_work/linux/lib/Makefile +--- c3000_pre/linux/lib/Makefile 2004-08-21 09:49:14.000000000 +0900 ++++ c3000_work/linux/lib/Makefile 2004-12-16 23:01:14.000000000 +0900 +@@ -9,10 +9,12 @@ + + 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 md5.o + ++obj-$(CONFIG_FW_LOADER) += firmware_class.o + obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o + obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o + +@@ -23,6 +25,8 @@ + subdir-$(CONFIG_ZLIB_INFLATE) += zlib_inflate + subdir-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate + ++include $(TOPDIR)/drivers/bluetooth/Makefile.lib ++ + # Include the subdirs, if necessary. + obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o)) + +diff -Nur c3000_pre/linux/lib/firmware_class.c c3000_work/linux/lib/firmware_class.c +--- c3000_pre/linux/lib/firmware_class.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/lib/firmware_class.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,573 @@ ++/* ++ * firmware_class.c - Multi purpose firmware loading support ++ * ++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org> ++ * ++ * Please see Documentation/firmware_class/ for more information. ++ * ++ */ ++/* ++ * Based on kernel/kmod.c and drivers/usb/usb.c ++ */ ++/* ++ kernel/kmod.c ++ Kirk Petersen ++ ++ Reorganized not to be a daemon by Adam Richter, with guidance ++ from Greg Zornetzer. ++ ++ Modified to avoid chroot and file sharing problems. ++ Mikael Pettersson ++ ++ Limit the concurrent number of kmod modprobes to catch loops from ++ "modprobe needs a service that is in a module". ++ Keith Owens <kaos@ocs.com.au> December 1999 ++ ++ Unblock all signals when we exec a usermode process. ++ Shuu Yamaguchi <shuu@wondernetworkresources.com> December 2000 ++*/ ++/* ++ * drivers/usb/usb.c ++ * ++ * (C) Copyright Linus Torvalds 1999 ++ * (C) Copyright Johannes Erdfelt 1999-2001 ++ * (C) Copyright Andreas Gal 1999 ++ * (C) Copyright Gregory P. Smith 1999 ++ * (C) Copyright Deti Fliegl 1999 (new USB architecture) ++ * (C) Copyright Randy Dunlap 2000 ++ * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id) ++ * (C) Copyright Yggdrasil Computing, Inc. 2000 ++ * (usb_device_id matching changes by Adam J. Richter) ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/string.h> ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/kmod.h> ++#include <linux/proc_fs.h> ++#include <linux/vmalloc.h> ++#include <asm/hardirq.h> ++ ++#include "linux/firmware.h" ++ ++MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>"); ++MODULE_DESCRIPTION("Multi purpose firmware loading support"); ++MODULE_LICENSE("GPL"); ++ ++#define err(format, arg...) \ ++ printk(KERN_ERR "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg) ++#define warn(format, arg...) \ ++ printk(KERN_WARNING "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg) ++#define dbg(format, arg...) \ ++ printk(KERN_DEBUG "%s:%s: " format "\n",__FILE__, __FUNCTION__ , ## arg) ++ ++static int loading_timeout = 10; /* In seconds */ ++static struct proc_dir_entry *proc_dir_timeout; ++static struct proc_dir_entry *proc_dir; ++ ++#ifdef CONFIG_HOTPLUG ++ ++static int ++call_helper(char *verb, const char *name, const char *device) ++{ ++ char *argv[3], **envp, *buf, *scratch; ++ int i = 0; ++ ++ int retval = 0; ++ ++ if (!hotplug_path[0]) ++ return -ENOENT; ++ if (in_interrupt()) { ++ err("in_interrupt"); ++ return -EFAULT; ++ } ++ if (!current->fs->root) { ++ warn("call_policy %s -- no FS yet", verb); ++ return -EPERM; ++ } ++ ++ if (!(envp = (char **) kmalloc(20 * sizeof (char *), GFP_KERNEL))) { ++ err("unable to allocate envp"); ++ return -ENOMEM; ++ } ++ if (!(buf = kmalloc(256, GFP_KERNEL))) { ++ kfree(envp); ++ err("unable to allocate buf"); ++ return -ENOMEM; ++ } ++ ++ /* only one standardized param to hotplug command: type */ ++ argv[0] = hotplug_path; ++ argv[1] = "firmware"; ++ argv[2] = 0; ++ ++ /* minimal command environment */ ++ envp[i++] = "HOME=/"; ++ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; ++ ++#ifdef DEBUG ++ /* hint that policy agent should enter no-stdout debug mode */ ++ envp[i++] = "DEBUG=kernel"; ++#endif ++ scratch = buf; ++ ++ if (device) { ++ envp[i++] = scratch; ++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX+25, ++ "DEVPATH=/driver/firmware/%s", device) + 1; ++ } ++ ++ envp[i++] = scratch; ++ scratch += sprintf(scratch, "ACTION=%s", verb) + 1; ++ ++ envp[i++] = scratch; ++ scratch += snprintf(scratch, FIRMWARE_NAME_MAX, ++ "FIRMWARE=%s", name) + 1; ++ ++ envp[i++] = 0; ++ ++#ifdef DEBUG ++ dbg("firmware: %s %s %s", argv[0], argv[1], verb); ++#endif ++ ++ retval = call_usermodehelper(argv[0], argv, envp); ++ if (retval) { ++ printk("call_usermodehelper return %d\n", retval); ++ } ++ ++ kfree(buf); ++ kfree(envp); ++ return retval; ++} ++#else ++ ++static inline int ++call_helper(char *verb, const char *name, const char *device) ++{ ++ return -ENOENT; ++} ++ ++#endif /* CONFIG_HOTPLUG */ ++ ++struct firmware_priv { ++ struct completion completion; ++ struct proc_dir_entry *proc_dir; ++ struct proc_dir_entry *attr_data; ++ struct proc_dir_entry *attr_loading; ++ struct firmware *fw; ++ int loading; ++ int abort; ++ int alloc_size; ++ struct timer_list timeout; ++}; ++ ++static int ++firmware_timeout_show(char *buf, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ return sprintf(buf, "%d\n", loading_timeout); ++} ++ ++/** ++ * firmware_timeout_store: ++ * Description: ++ * Sets the number of seconds to wait for the firmware. Once ++ * this expires an error will be return to the driver and no ++ * firmware will be provided. ++ * ++ * Note: zero means 'wait for ever' ++ * ++ **/ ++static int ++firmware_timeout_store(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ loading_timeout = simple_strtol(buf, NULL, 10); ++ return count; ++} ++ ++static int ++firmware_loading_show(char *buf, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ struct firmware_priv *fw_priv = data; ++ return sprintf(buf, "%d\n", fw_priv->loading); ++} ++ ++/** ++ * firmware_loading_store: - loading control file ++ * Description: ++ * The relevant values are: ++ * ++ * 1: Start a load, discarding any previous partial load. ++ * 0: Conclude the load and handle the data to the driver code. ++ * -1: Conclude the load with an error and discard any written data. ++ **/ ++static int ++firmware_loading_store(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ struct firmware_priv *fw_priv = data; ++ int prev_loading = fw_priv->loading; ++ ++ fw_priv->loading = simple_strtol(buf, NULL, 10); ++ ++ switch (fw_priv->loading) { ++ case -1: ++ fw_priv->abort = 1; ++ wmb(); ++ complete(&fw_priv->completion); ++ break; ++ case 1: ++ kfree(fw_priv->fw->data); ++ fw_priv->fw->data = NULL; ++ fw_priv->fw->size = 0; ++ fw_priv->alloc_size = 0; ++ break; ++ case 0: ++ if (prev_loading == 1) ++ complete(&fw_priv->completion); ++ break; ++ } ++ ++ return count; ++} ++ ++static int ++firmware_data_read(char *buffer, char **start, off_t offset, ++ int count, int *eof, void *data) ++{ ++ struct firmware_priv *fw_priv = data; ++ struct firmware *fw = fw_priv->fw; ++ ++ if (offset > fw->size) ++ return 0; ++ if (offset + count > fw->size) ++ count = fw->size - offset; ++ ++ memcpy(buffer, fw->data + offset, count); ++ *start = (void *) ((long) count); ++ return count; ++} ++static int ++fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) ++{ ++ u8 *new_data; ++ int new_size; ++ ++ if (min_size <= fw_priv->alloc_size) ++ return 0; ++ if((min_size % PAGE_SIZE) == 0) ++ new_size = min_size; ++ else ++ new_size = (min_size + PAGE_SIZE) & PAGE_MASK; ++ new_data = vmalloc(new_size); ++ if (!new_data) { ++ printk(KERN_ERR "%s: unable to alloc buffer\n", __FUNCTION__); ++ /* Make sure that we don't keep incomplete data */ ++ fw_priv->abort = 1; ++ return -ENOMEM; ++ } ++ fw_priv->alloc_size = new_size; ++ if (fw_priv->fw->data) { ++ memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size); ++ vfree(fw_priv->fw->data); ++ } ++ fw_priv->fw->data = new_data; ++ BUG_ON(min_size > fw_priv->alloc_size); ++ return 0; ++} ++ ++/** ++ * firmware_data_write: ++ * ++ * Description: ++ * ++ * Data written to the 'data' attribute will be later handled to ++ * the driver as a firmware image. ++ **/ ++static int ++firmware_data_write(struct file *file, const char *buffer, ++ unsigned long count, void *data) ++{ ++ struct firmware_priv *fw_priv = data; ++ struct firmware *fw = fw_priv->fw; ++ int offset = file->f_pos; ++ int retval; ++ ++ retval = fw_realloc_buffer(fw_priv, offset + count); ++ if (retval) { ++ printk("%s: retval:%d\n", __FUNCTION__, retval); ++ return retval; ++ } ++ ++ memcpy(fw->data + offset, buffer, count); ++ ++ fw->size = max_t(size_t, offset + count, fw->size); ++ file->f_pos += count; ++ return count; ++} ++ ++static void ++firmware_class_timeout(u_long data) ++{ ++ struct firmware_priv *fw_priv = (struct firmware_priv *) data; ++ fw_priv->abort = 1; ++ wmb(); ++ complete(&fw_priv->completion); ++} ++static int ++fw_setup_class_device(struct firmware_priv **fw_priv_p, ++ const char *fw_name, const char *device) ++{ ++ int retval; ++ struct firmware_priv *fw_priv = kmalloc(sizeof (struct firmware_priv), ++ GFP_KERNEL); ++ *fw_priv_p = fw_priv; ++ if (!fw_priv) { ++ retval = -ENOMEM; ++ goto out; ++ } ++ memset(fw_priv, 0, sizeof (*fw_priv)); ++ ++ init_completion(&fw_priv->completion); ++ ++ fw_priv->timeout.function = firmware_class_timeout; ++ fw_priv->timeout.data = (u_long) fw_priv; ++ init_timer(&fw_priv->timeout); ++ ++ retval = -EAGAIN; ++ fw_priv->proc_dir = create_proc_entry(device, 0644 | S_IFDIR, proc_dir); ++ if (!fw_priv->proc_dir) ++ goto err_free_fw_priv; ++ ++ fw_priv->attr_data = create_proc_entry("data", 0644 | S_IFREG, ++ fw_priv->proc_dir); ++ if (!fw_priv->attr_data) ++ goto err_remove_dir; ++ ++ fw_priv->attr_data->read_proc = firmware_data_read; ++ fw_priv->attr_data->write_proc = firmware_data_write; ++ fw_priv->attr_data->data = fw_priv; ++ ++ fw_priv->attr_loading = create_proc_entry("loading", 0644 | S_IFREG, ++ fw_priv->proc_dir); ++ if (!fw_priv->attr_loading) ++ goto err_remove_data; ++ ++ fw_priv->attr_loading->read_proc = firmware_loading_show; ++ fw_priv->attr_loading->write_proc = firmware_loading_store; ++ fw_priv->attr_loading->data = fw_priv; ++ ++ retval = 0; ++ fw_priv->fw = kmalloc(sizeof (struct firmware), GFP_KERNEL); ++ if (!fw_priv->fw) { ++ printk(KERN_ERR "%s: kmalloc(struct firmware) failed\n", ++ __FUNCTION__); ++ retval = -ENOMEM; ++ goto err_remove_loading; ++ } ++ memset(fw_priv->fw, 0, sizeof (*fw_priv->fw)); ++ ++ goto out; ++ ++err_remove_loading: ++ remove_proc_entry("loading", fw_priv->proc_dir); ++err_remove_data: ++ remove_proc_entry("data", fw_priv->proc_dir); ++err_remove_dir: ++ remove_proc_entry(device, proc_dir); ++err_free_fw_priv: ++ kfree(fw_priv); ++out: ++ return retval; ++} ++static void ++fw_remove_class_device(struct firmware_priv *fw_priv) ++{ ++ remove_proc_entry("loading", fw_priv->proc_dir); ++ remove_proc_entry("data", fw_priv->proc_dir); ++ remove_proc_entry(fw_priv->proc_dir->name, proc_dir); ++} ++ ++/** ++ * request_firmware: - request firmware to hotplug and wait for it ++ * Description: ++ * @firmware will be used to return a firmware image by the name ++ * of @name for device @device. ++ * ++ * Should be called from user context where sleeping is allowed. ++ * ++ * @name will be use as $FIRMWARE in the hotplug environment and ++ * should be distinctive enough not to be confused with any other ++ * firmware image for this or any other device. ++ **/ ++int ++request_firmware(const struct firmware **firmware, const char *name, ++ const char *device) ++{ ++ struct firmware_priv *fw_priv; ++ int retval; ++ ++ if (!firmware) { ++ retval = -EINVAL; ++ goto out; ++ } ++ *firmware = NULL; ++ ++ retval = fw_setup_class_device(&fw_priv, name, device); ++ if (retval) ++ goto out; ++ ++ retval = call_helper("add", name, device); ++ if (retval) ++ goto out; ++ if (loading_timeout) { ++ fw_priv->timeout.expires = jiffies + loading_timeout * HZ; ++ add_timer(&fw_priv->timeout); ++ } ++ ++ wait_for_completion(&fw_priv->completion); ++ ++ del_timer(&fw_priv->timeout); ++ fw_remove_class_device(fw_priv); ++ ++ if (fw_priv->fw->size && !fw_priv->abort) { ++ *firmware = fw_priv->fw; ++ } else { ++ retval = -ENOENT; ++ vfree(fw_priv->fw->data); ++ kfree(fw_priv->fw); ++ } ++out: ++ kfree(fw_priv); ++ return retval; ++} ++ ++void ++release_firmware(const struct firmware *fw) ++{ ++ if (fw) { ++ vfree(fw->data); ++ kfree(fw); ++ } ++} ++ ++/** ++ * register_firmware: - provide a firmware image for later usage ++ * ++ * Description: ++ * Make sure that @data will be available by requesting firmware @name. ++ * ++ * Note: This will not be possible until some kind of persistence ++ * is available. ++ **/ ++void ++register_firmware(const char *name, const u8 *data, size_t size) ++{ ++ /* This is meaningless without firmware caching, so until we ++ * decide if firmware caching is reasonable just leave it as a ++ * noop */ ++} ++ ++/* Async support */ ++struct firmware_work { ++ struct tq_struct work; ++ struct module *module; ++ const char *name; ++ const char *device; ++ void *context; ++ void (*cont)(const struct firmware *fw, void *context); ++}; ++ ++static void ++request_firmware_work_func(void *arg) ++{ ++ struct firmware_work *fw_work = arg; ++ const struct firmware *fw; ++ if (!arg) ++ return; ++ request_firmware(&fw, fw_work->name, fw_work->device); ++ fw_work->cont(fw, fw_work->context); ++ release_firmware(fw); ++ __MOD_DEC_USE_COUNT(fw_work->module); ++ kfree(fw_work); ++} ++ ++/** ++ * request_firmware_nowait: ++ * ++ * Description: ++ * Asynchronous variant of request_firmware() for contexts where ++ * it is not possible to sleep. ++ * ++ * @cont will be called asynchronously when the firmware request is over. ++ * ++ * @context will be passed over to @cont. ++ * ++ * @fw may be %NULL if firmware request fails. ++ * ++ **/ ++int ++request_firmware_nowait( ++ struct module *module, ++ const char *name, const char *device, void *context, ++ void (*cont)(const struct firmware *fw, void *context)) ++{ ++ struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work), ++ GFP_ATOMIC); ++ if (!fw_work) ++ return -ENOMEM; ++ if (!try_inc_mod_count(module)) { ++ kfree(fw_work); ++ return -EFAULT; ++ } ++ ++ *fw_work = (struct firmware_work) { ++ .module = module, ++ .name = name, ++ .device = device, ++ .context = context, ++ .cont = cont, ++ }; ++ INIT_TQUEUE(&fw_work->work, request_firmware_work_func, fw_work); ++ ++ schedule_task(&fw_work->work); ++ return 0; ++} ++ ++static int __init ++firmware_class_init(void) ++{ ++ proc_dir = create_proc_entry("driver/firmware", 0755 | S_IFDIR, NULL); ++ if (!proc_dir) ++ return -EAGAIN; ++ proc_dir_timeout = create_proc_entry("timeout", ++ 0644 | S_IFREG, proc_dir); ++ if (!proc_dir_timeout) { ++ remove_proc_entry("driver/firmware", NULL); ++ return -EAGAIN; ++ } ++ proc_dir_timeout->read_proc = firmware_timeout_show; ++ proc_dir_timeout->write_proc = firmware_timeout_store; ++ return 0; ++} ++static void __exit ++firmware_class_exit(void) ++{ ++ remove_proc_entry("timeout", proc_dir); ++ remove_proc_entry("driver/firmware", NULL); ++} ++ ++module_init(firmware_class_init); ++module_exit(firmware_class_exit); ++ ++#ifndef CONFIG_FW_LOADER ++EXPORT_SYMBOL(release_firmware); ++EXPORT_SYMBOL(request_firmware); ++EXPORT_SYMBOL(request_firmware_nowait); ++EXPORT_SYMBOL(register_firmware); ++#endif +diff -Nur c3000_pre/linux/net/bluetooth/Config.in c3000_work/linux/net/bluetooth/Config.in +--- c3000_pre/linux/net/bluetooth/Config.in 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/Config.in 2004-12-16 23:01:14.000000000 +0900 +@@ -1,8 +1,9 @@ + # +-# Bluetooth configuration ++# Bluetooth subsystem configuration + # + + if [ "$CONFIG_NET" != "n" ]; then ++ + mainmenu_option next_comment + comment 'Bluetooth support' + dep_tristate 'Bluetooth subsystem support' CONFIG_BLUEZ $CONFIG_NET +@@ -10,9 +11,13 @@ + if [ "$CONFIG_BLUEZ" != "n" ]; then + dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ + dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ ++ source net/bluetooth/rfcomm/Config.in + source net/bluetooth/bnep/Config.in ++ source net/bluetooth/cmtp/Config.in ++ source net/bluetooth/hidp/Config.in + source drivers/bluetooth/Config.in + fi ++ + endmenu + fi + +diff -Nur c3000_pre/linux/net/bluetooth/Makefile c3000_work/linux/net/bluetooth/Makefile +--- c3000_pre/linux/net/bluetooth/Makefile 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/Makefile 2004-12-16 23:01:14.000000000 +0900 +@@ -1,10 +1,11 @@ + # +-# Makefile for the Bluetooth subsystem ++# Makefile for the Linux Bluetooth subsystem + # ++ + 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 + +@@ -12,12 +13,27 @@ + obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o + obj-$(CONFIG_BLUEZ_SCO) += sco.o + ++subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm + subdir-$(CONFIG_BLUEZ_BNEP) += bnep ++subdir-$(CONFIG_BLUEZ_CMTP) += cmtp ++subdir-$(CONFIG_BLUEZ_HIDP) += hidp ++ ++ifeq ($(CONFIG_BLUEZ_RFCOMM),y) ++obj-y += rfcomm/rfcomm.o ++endif + + ifeq ($(CONFIG_BLUEZ_BNEP),y) + obj-y += bnep/bnep.o + endif + ++ifeq ($(CONFIG_BLUEZ_CMTP),y) ++obj-y += cmtp/cmtp.o ++endif ++ ++ifeq ($(CONFIG_BLUEZ_HIDP),y) ++obj-y += hidp/hidp.o ++endif ++ + include $(TOPDIR)/Rules.make + + bluez.o: $(bluez-objs) +diff -Nur c3000_pre/linux/net/bluetooth/af_bluetooth.c c3000_work/linux/net/bluetooth/af_bluetooth.c +--- c3000_pre/linux/net/bluetooth/af_bluetooth.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/af_bluetooth.c 2004-12-16 23:01:14.000000000 +0900 +@@ -27,7 +27,7 @@ + * + * $Id: af_bluetooth.c,v 1.8 2002/07/22 20:32:54 maxk Exp $ + */ +-#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); +diff -Nur c3000_pre/linux/net/bluetooth/bnep/Config.in c3000_work/linux/net/bluetooth/bnep/Config.in +--- c3000_pre/linux/net/bluetooth/bnep/Config.in 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/bnep/Config.in 2004-12-16 23:01:14.000000000 +0900 +@@ -1,6 +1,11 @@ ++# ++# Bluetooth BNEP layer configuration ++# + + dep_tristate 'BNEP protocol support' CONFIG_BLUEZ_BNEP $CONFIG_BLUEZ_L2CAP ++ + if [ "$CONFIG_BLUEZ_BNEP" != "n" ]; then +- bool ' Multicast filter support' CONFIG_BNEP_MC_FILTER +- bool ' Protocol filter support' CONFIG_BNEP_PROTO_FILTER ++ bool ' Multicast filter support' CONFIG_BLUEZ_BNEP_MC_FILTER ++ bool ' Protocol filter support' CONFIG_BLUEZ_BNEP_PROTO_FILTER + fi ++ +diff -Nur c3000_pre/linux/net/bluetooth/bnep/Makefile c3000_work/linux/net/bluetooth/bnep/Makefile +--- c3000_pre/linux/net/bluetooth/bnep/Makefile 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/bnep/Makefile 2004-12-16 23:01:14.000000000 +0900 +@@ -1,5 +1,5 @@ + # +-# Makefile for BNEP protocol ++# Makefile for the Linux Bluetooth BNEP layer + # + + O_TARGET := bnep.o +diff -Nur c3000_pre/linux/net/bluetooth/bnep/bnep.h c3000_work/linux/net/bluetooth/bnep/bnep.h +--- c3000_pre/linux/net/bluetooth/bnep/bnep.h 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/bnep/bnep.h 2004-12-16 23:01:14.000000000 +0900 +@@ -112,25 +112,25 @@ + __u8 data[0]; + } __attribute__((packed)); + +-// Ioctl interface +-#define BNEPCONADD 1 +-#define BNEPCONDEL 2 +-#define BNEPGETCONLIST 3 +-#define BNEPGETCONINFO 4 ++/* BNEP ioctl defines */ ++#define BNEPCONNADD _IOW('B', 200, int) ++#define BNEPCONNDEL _IOW('B', 201, int) ++#define BNEPGETCONNLIST _IOR('B', 210, int) ++#define BNEPGETCONNINFO _IOR('B', 211, int) + +-struct bnep_conadd_req { ++struct bnep_connadd_req { + int sock; // Connected socket + __u32 flags; + __u16 role; + char device[16]; // Name of the Ethernet device + }; + +-struct bnep_condel_req { ++struct bnep_conndel_req { + __u32 flags; + __u8 dst[ETH_ALEN]; + }; + +-struct bnep_coninfo { ++struct bnep_conninfo { + __u32 flags; + __u16 role; + __u16 state; +@@ -138,9 +138,9 @@ + char device[16]; + }; + +-struct bnep_conlist_req { ++struct bnep_connlist_req { + __u32 cnum; +- struct bnep_coninfo *ci; ++ struct bnep_conninfo *ci; + }; + + struct bnep_proto_filter { +@@ -148,10 +148,10 @@ + __u16 end; + }; + +-int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock); +-int bnep_del_connection(struct bnep_condel_req *req); +-int bnep_get_conlist(struct bnep_conlist_req *req); +-int bnep_get_coninfo(struct bnep_coninfo *ci); ++int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock); ++int bnep_del_connection(struct bnep_conndel_req *req); ++int bnep_get_connlist(struct bnep_connlist_req *req); ++int bnep_get_conninfo(struct bnep_conninfo *ci); + + // BNEP sessions + struct bnep_session { +diff -Nur c3000_pre/linux/net/bluetooth/bnep/core.c c3000_work/linux/net/bluetooth/bnep/core.c +--- c3000_pre/linux/net/bluetooth/bnep/core.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/bnep/core.c 2004-12-16 23:01:14.000000000 +0900 +@@ -58,17 +58,17 @@ + + #include "bnep.h" + +-#ifndef CONFIG_BNEP_DEBUG ++#ifndef CONFIG_BLUEZ_BNEP_DEBUG + #undef BT_DBG + #define BT_DBG(D...) + #endif + +-#define VERSION "1.0" ++#define VERSION "1.2" + + static LIST_HEAD(bnep_session_list); + static DECLARE_RWSEM(bnep_session_sem); + +-static struct bnep_session *__bnep_get_session(__u8 *dst) ++static struct bnep_session *__bnep_get_session(u8 *dst) + { + struct bnep_session *s; + struct list_head *p; +@@ -104,7 +104,7 @@ + return sock->ops->sendmsg(sock, &s->msg, len, NULL); + } + +-static int bnep_send_rsp(struct bnep_session *s, __u8 ctrl, __u16 resp) ++static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp) + { + struct bnep_control_rsp rsp; + rsp.type = BNEP_CONTROL; +@@ -113,23 +113,37 @@ + return bnep_send(s, &rsp, sizeof(rsp)); + } + +-static int bnep_ctrl_set_netfilter(struct bnep_session *s, struct sk_buff *skb) ++#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) + { +- __u16 *data; + int n; +- +- data = (void *) skb->data; +- if (!skb_pull(skb, 2)) ++ ++ if (len < 2) + return -EILSEQ; ++ + n = ntohs(get_unaligned(data)); ++ data++; len -= 2; + +- data = (void *) skb->data; +- if (!skb_pull(skb, n)) ++ if (len < n) + return -EILSEQ; + + BT_DBG("filter len %d", n); + +-#ifdef CONFIG_BNEP_PROTO_FILTER ++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER + n /= 4; + if (n <= BNEP_MAX_PROTO_FILTERS) { + struct bnep_proto_filter *f = s->proto_filter; +@@ -142,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); +@@ -155,23 +173,22 @@ + return 0; + } + +-static int bnep_ctrl_set_mcfilter(struct bnep_session *s, struct sk_buff *skb) ++static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len) + { +- u8 *data; + int n; +- +- data = (void *) skb->data; +- if (!skb_pull(skb, 2)) ++ ++ if (len < 2) + return -EILSEQ; +- n = ntohs(get_unaligned((u16 *) data)); + +- data = (void *) skb->data; +- if (!skb_pull(skb, n)) ++ n = ntohs(get_unaligned((u16 *) data)); ++ data += 2; len -= 2; ++ ++ if (len < n) + return -EILSEQ; + + BT_DBG("filter len %d", n); + +-#ifdef CONFIG_BNEP_MC_FILTER ++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER + n /= (ETH_ALEN * 2); + + if (n > 0) { +@@ -210,12 +227,13 @@ + return 0; + } + +-static int bnep_rx_control(struct bnep_session *s, struct sk_buff *skb) ++static int bnep_rx_control(struct bnep_session *s, void *data, int len) + { ++ u8 cmd = *(u8 *)data; + int err = 0; +- __u8 cmd = *(__u8 *) skb->data; +- skb_pull(skb, 1); +- ++ ++ data++; len--; ++ + switch (cmd) { + case BNEP_CMD_NOT_UNDERSTOOD: + case BNEP_SETUP_CONN_REQ: +@@ -226,15 +244,15 @@ + break; + + case BNEP_FILTER_NET_TYPE_SET: +- err = bnep_ctrl_set_netfilter(s, skb); ++ err = bnep_ctrl_set_netfilter(s, data, len); + break; + + case BNEP_FILTER_MULTI_ADDR_SET: +- err = bnep_ctrl_set_mcfilter(s, skb); ++ err = bnep_ctrl_set_mcfilter(s, data, len); + break; + + default: { +- __u8 pkt[3]; ++ u8 pkt[3]; + pkt[0] = BNEP_CONTROL; + pkt[1] = BNEP_CMD_NOT_UNDERSTOOD; + pkt[2] = cmd; +@@ -262,21 +280,24 @@ + + switch (h->type & BNEP_TYPE_MASK) { + case BNEP_EXT_CONTROL: +- err = bnep_rx_control(s, skb); ++ bnep_rx_control(s, skb->data, skb->len); + break; + + default: +- /* Unknown extension */ +- if (!skb_pull(skb, h->len)) +- err = -EILSEQ; ++ /* Unknown extension, skip it. */ ++ break; ++ } ++ ++ if (!skb_pull(skb, h->len)) { ++ err = -EILSEQ; + break; + } + } while (!err && (h->type & BNEP_EXT_HEADER)); +- ++ + return err; + } + +-static __u8 __bnep_rx_hlen[] = { ++static u8 __bnep_rx_hlen[] = { + ETH_HLEN, /* BNEP_GENERAL */ + 0, /* BNEP_CONTROL */ + 2, /* BNEP_COMPRESSED */ +@@ -289,18 +310,18 @@ + { + struct net_device *dev = &s->dev; + struct sk_buff *nskb; +- __u8 type; ++ u8 type; + + dev->last_rx = jiffies; + s->stats.rx_bytes += skb->len; + +- type = *(__u8 *) skb->data; skb_pull(skb, 1); ++ type = *(u8 *) skb->data; skb_pull(skb, 1); + + if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES) + goto badframe; +- ++ + if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) { +- bnep_rx_control(s, skb); ++ bnep_rx_control(s, skb->data, skb->len); + kfree_skb(skb); + return 0; + } +@@ -311,7 +332,7 @@ + if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK])) + goto badframe; + +- s->eh.h_proto = get_unaligned((__u16 *) (skb->data - 2)); ++ s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2)); + + if (type & BNEP_EXT_HEADER) { + if (bnep_rx_extension(s, skb) < 0) +@@ -322,9 +343,9 @@ + if (ntohs(s->eh.h_proto) == 0x8100) { + if (!skb_pull(skb, 4)) + goto badframe; +- s->eh.h_proto = get_unaligned((__u16 *) (skb->data - 2)); ++ 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); +@@ -340,11 +361,11 @@ + case BNEP_COMPRESSED: + memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN); + break; +- ++ + case BNEP_COMPRESSED_SRC_ONLY: + memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN); + memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN); +- put_unaligned(s->eh.h_proto, (__u16 *) __skb_put(nskb, 2)); ++ put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2)); + break; + + case BNEP_COMPRESSED_DST_ONLY: +@@ -354,13 +375,13 @@ + + case BNEP_GENERAL: + memcpy(__skb_put(nskb, ETH_ALEN * 2), skb->mac.raw, ETH_ALEN * 2); +- put_unaligned(s->eh.h_proto, (__u16 *) __skb_put(nskb, 2)); ++ put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2)); + break; + } + + memcpy(__skb_put(nskb, skb->len), skb->data, skb->len); + kfree_skb(skb); +- ++ + s->stats.rx_packets++; + nskb->dev = dev; + nskb->ip_summed = CHECKSUM_UNNECESSARY; +@@ -374,7 +395,7 @@ + return 0; + } + +-static __u8 __bnep_tx_types[] = { ++static u8 __bnep_tx_types[] = { + BNEP_GENERAL, + BNEP_COMPRESSED_SRC_ONLY, + BNEP_COMPRESSED_DST_ONLY, +@@ -387,7 +408,7 @@ + struct socket *sock = s->sock; + struct iovec iv[3]; + int len = 0, il = 0; +- __u8 type = 0; ++ u8 type = 0; + + BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type); + +@@ -424,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); +@@ -451,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); +@@ -475,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); +@@ -503,11 +524,11 @@ + return 0; + } + +-int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock) ++int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) + { + struct net_device *dev; + struct bnep_session *s, *ss; +- __u8 dst[ETH_ALEN], src[ETH_ALEN]; ++ u8 dst[ETH_ALEN], src[ETH_ALEN]; + int err; + + BT_DBG(""); +@@ -535,6 +556,8 @@ + else + strcpy(dev->name, "bnep%d"); + ++ memset(dev->broadcast, 0xff, ETH_ALEN); ++ + /* This is rx header therefor addresses are swaped. + * ie eh.h_dest is our local address. */ + memcpy(s->eh.h_dest, &src, ETH_ALEN); +@@ -543,28 +566,19 @@ + s->sock = sock; + s->role = req->role; + s->state = BT_CONNECTED; +- ++ + s->msg.msg_flags = MSG_NOSIGNAL; + +-#ifdef CONFIG_BNEP_MC_FILTER ++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER + /* Set default mc filter */ + set_bit(bnep_mc_hash(dev->broadcast), &s->mc_filter); + #endif +- +-#ifdef CONFIG_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); ++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER ++ /* Set default protocol filter */ ++ bnep_set_default_proto_filter(s); + #endif +- ++ + dev->init = bnep_net_init; + dev->priv = s; + err = register_netdev(dev); +@@ -573,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. */ +@@ -592,7 +606,7 @@ + return err; + } + +-int bnep_del_connection(struct bnep_condel_req *req) ++int bnep_del_connection(struct bnep_conndel_req *req) + { + struct bnep_session *s; + int err = 0; +@@ -617,7 +631,7 @@ + return err; + } + +-static void __bnep_copy_ci(struct bnep_coninfo *ci, struct bnep_session *s) ++static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s) + { + memcpy(ci->dst, s->eh.h_source, ETH_ALEN); + strcpy(ci->device, s->dev.name); +@@ -626,7 +640,7 @@ + ci->role = s->role; + } + +-int bnep_get_conlist(struct bnep_conlist_req *req) ++int bnep_get_connlist(struct bnep_connlist_req *req) + { + struct list_head *p; + int err = 0, n = 0; +@@ -635,7 +649,7 @@ + + list_for_each(p, &bnep_session_list) { + struct bnep_session *s; +- struct bnep_coninfo ci; ++ struct bnep_conninfo ci; + + s = list_entry(p, struct bnep_session, list); + +@@ -657,7 +671,7 @@ + return err; + } + +-int bnep_get_coninfo(struct bnep_coninfo *ci) ++int bnep_get_conninfo(struct bnep_conninfo *ci) + { + struct bnep_session *s; + int err = 0; +@@ -676,17 +690,16 @@ + + static int __init bnep_init_module(void) + { +- BT_INFO("BNEP: BNEP2 ver %s\n" +- "BNEP: Copyright (C) 2002 Inventel\n" +- "BNEP: Written 2001,2002 by\n" +- "BNEP: \tClement Moreau <clement.moreau@inventel.fr> " +- "David Libault <david.libault@inventel.fr>\n" +- "BNEP: Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>", +- VERSION); ++ l2cap_load(); + ++ bnep_crc32_init(); + bnep_sock_init(); + +- bnep_crc32_init(); ++ BT_INFO("BlueZ BNEP ver %s", VERSION); ++ BT_INFO("Copyright (C) 2001,2002 Inventel Systemes"); ++ BT_INFO("Written 2001,2002 by Clement Moreau <clement.moreau@inventel.fr>"); ++ BT_INFO("Written 2001,2002 by David Libault <david.libault@inventel.fr>"); ++ BT_INFO("Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>"); + + return 0; + } +@@ -694,13 +707,12 @@ + static void __exit bnep_cleanup_module(void) + { + bnep_sock_cleanup(); +- + bnep_crc32_cleanup(); + } + + module_init(bnep_init_module); + module_exit(bnep_cleanup_module); + +-MODULE_DESCRIPTION("BNEP2 ver " VERSION); +-MODULE_AUTHOR("David Libault <david.libault@inventel.fr> Maxim Krasnyanskiy <maxk@qualcomm.com>"); ++MODULE_DESCRIPTION("BlueZ BNEP ver " VERSION); ++MODULE_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyanskiy <maxk@qualcomm.com>"); + MODULE_LICENSE("GPL"); +diff -Nur c3000_pre/linux/net/bluetooth/bnep/netdev.c c3000_work/linux/net/bluetooth/bnep/netdev.c +--- c3000_pre/linux/net/bluetooth/bnep/netdev.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/bnep/netdev.c 2004-12-16 23:01:14.000000000 +0900 +@@ -46,7 +46,7 @@ + + #include "bnep.h" + +-#ifndef CONFIG_BNEP_DEBUG ++#ifndef CONFIG_BLUEZ_BNEP_DEBUG + #undef BT_DBG + #define BT_DBG( A... ) + #endif +@@ -73,7 +73,7 @@ + + static void bnep_net_set_mc_list(struct net_device *dev) + { +-#ifdef CONFIG_BNEP_MC_FILTER ++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER + struct bnep_session *s = dev->priv; + struct sock *sk = s->sock->sk; + struct bnep_set_filter_req *r; +@@ -143,27 +143,31 @@ + return -EINVAL; + } + +-#ifdef CONFIG_BNEP_MC_FILTER ++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER + static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s) + { + struct ethhdr *eh = (void *) skb->data; + +- if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), &s->mc_filter)) ++ if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), &s->mc_filter)) { ++ BT_DBG("BNEP: filtered skb %p, dst %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", skb, ++ eh->h_dest[0], eh->h_dest[1], eh->h_dest[2], ++ eh->h_dest[3], eh->h_dest[4], eh->h_dest[5]); + return 1; ++ } + return 0; + } + #endif + +-#ifdef CONFIG_BNEP_PROTO_FILTER ++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER + /* Determine ether protocol. Based on eth_type_trans. */ +-static inline __u16 bnep_net_eth_proto(struct sk_buff *skb) ++static inline u16 bnep_net_eth_proto(struct sk_buff *skb) + { + struct ethhdr *eh = (void *) skb->data; + + if (ntohs(eh->h_proto) >= 1536) + return eh->h_proto; + +- if (get_unaligned((__u16 *) skb->data) == 0xFFFF) ++ if (get_unaligned((u16 *) skb->data) == 0xFFFF) + return htons(ETH_P_802_3); + + return htons(ETH_P_802_2); +@@ -171,7 +175,7 @@ + + static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s) + { +- __u16 proto = bnep_net_eth_proto(skb); ++ u16 proto = bnep_net_eth_proto(skb); + struct bnep_proto_filter *f = s->proto_filter; + int i; + +@@ -192,14 +196,14 @@ + + BT_DBG("skb %p, dev %p", skb, dev); + +-#ifdef CONFIG_BNEP_MC_FILTER ++#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER + if (bnep_net_mc_filter(skb, s)) { + kfree_skb(skb); + return 0; + } + #endif + +-#ifdef CONFIG_BNEP_PROTO_FILTER ++#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER + if (bnep_net_proto_filter(skb, s)) { + kfree_skb(skb); + return 0; +diff -Nur c3000_pre/linux/net/bluetooth/bnep/sock.c c3000_work/linux/net/bluetooth/bnep/sock.c +--- c3000_pre/linux/net/bluetooth/bnep/sock.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/bnep/sock.c 2004-12-16 23:01:14.000000000 +0900 +@@ -50,41 +50,11 @@ + + #include "bnep.h" + +-#ifndef CONFIG_BNEP_DEBUG ++#ifndef CONFIG_BLUEZ_BNEP_DEBUG + #undef BT_DBG + #define BT_DBG( A... ) + #endif + +-static inline struct socket *socki_lookup(struct inode *inode) +-{ +- return &inode->u.socket_i; +-} +- +-static struct socket *sockfd_lookup(int fd, int *err) +-{ +- struct file *file; +- struct inode *inode; +- struct socket *sock; +- +- if (!(file = fget(fd))) { +- *err = -EBADF; +- return NULL; +- } +- +- inode = file->f_dentry->d_inode; +- if (!inode->i_sock || !(sock = socki_lookup(inode))) { +- *err = -ENOTSOCK; +- fput(file); +- return NULL; +- } +- +- if (sock->file != file) { +- printk(KERN_ERR "socki_lookup: socket file changed!\n"); +- sock->file = file; +- } +- return sock; +-} +- + static int bnep_sock_release(struct socket *sock) + { + struct sock *sk = sock->sk; +@@ -103,17 +73,17 @@ + + static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) + { +- struct bnep_conlist_req cl; +- struct bnep_conadd_req ca; +- struct bnep_condel_req cd; +- struct bnep_coninfo ci; ++ struct bnep_connlist_req cl; ++ struct bnep_connadd_req ca; ++ struct bnep_conndel_req cd; ++ struct bnep_conninfo ci; + struct socket *nsock; + int err; + + BT_DBG("cmd %x arg %lx", cmd, arg); + + switch (cmd) { +- case BNEPCONADD: ++ case BNEPCONNADD: + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + +@@ -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) { +@@ -136,7 +108,7 @@ + + return err; + +- case BNEPCONDEL: ++ case BNEPCONNDEL: + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + +@@ -145,24 +117,24 @@ + + return bnep_del_connection(&cd); + +- case BNEPGETCONLIST: ++ case BNEPGETCONNLIST: + if (copy_from_user(&cl, (void *) arg, sizeof(cl))) + return -EFAULT; + + if (cl.cnum <= 0) + return -EINVAL; + +- err = bnep_get_conlist(&cl); ++ err = bnep_get_connlist(&cl); + if (!err && copy_to_user((void *) arg, &cl, sizeof(cl))) + return -EFAULT; + + return err; + +- case BNEPGETCONINFO: ++ case BNEPGETCONNINFO: + if (copy_from_user(&ci, (void *) arg, sizeof(ci))) + return -EFAULT; + +- err = bnep_get_coninfo(&ci); ++ err = bnep_get_conninfo(&ci); + if (!err && copy_to_user((void *) arg, &ci, sizeof(ci))) + return -EFAULT; + +diff -Nur c3000_pre/linux/net/bluetooth/cmtp/Config.in c3000_work/linux/net/bluetooth/cmtp/Config.in +--- c3000_pre/linux/net/bluetooth/cmtp/Config.in 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/cmtp/Config.in 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,7 @@ ++# ++# Bluetooth CMTP layer configuration ++# ++ ++if [ "$CONFIG_ISDN" = "y" -o "$CONFIG_ISDN" = "m" ]; then ++ dep_tristate 'CMTP protocol support' CONFIG_BLUEZ_CMTP $CONFIG_ISDN_CAPI $CONFIG_BLUEZ_L2CAP ++fi +diff -Nur c3000_pre/linux/net/bluetooth/cmtp/Makefile c3000_work/linux/net/bluetooth/cmtp/Makefile +--- c3000_pre/linux/net/bluetooth/cmtp/Makefile 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/cmtp/Makefile 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,10 @@ ++# ++# Makefile for the Linux Bluetooth CMTP layer ++# ++ ++O_TARGET := cmtp.o ++ ++obj-y := core.o sock.o capi.o ++obj-m += $(O_TARGET) ++ ++include $(TOPDIR)/Rules.make +diff -Nur c3000_pre/linux/net/bluetooth/cmtp/capi.c c3000_work/linux/net/bluetooth/cmtp/capi.c +--- c3000_pre/linux/net/bluetooth/cmtp/capi.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/cmtp/capi.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,707 @@ ++/* ++ CMTP implementation for Linux Bluetooth stack (BlueZ). ++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/major.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/poll.h> ++#include <linux/fcntl.h> ++#include <linux/skbuff.h> ++#include <linux/socket.h> ++#include <linux/ioctl.h> ++#include <linux/file.h> ++#include <net/sock.h> ++ ++#include <linux/capi.h> ++ ++#include "../drivers/isdn/avmb1/capilli.h" ++#include "../drivers/isdn/avmb1/capicmd.h" ++#include "../drivers/isdn/avmb1/capiutil.h" ++ ++#include "cmtp.h" ++ ++#ifndef CONFIG_BLUEZ_CMTP_DEBUG ++#undef BT_DBG ++#define BT_DBG(D...) ++#endif ++ ++#define REVISION "1.0" ++ ++#define CAPI_INTEROPERABILITY 0x20 ++ ++#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ) ++#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF) ++#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND) ++#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP) ++ ++#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2) ++#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4) ++#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2) ++#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2) ++ ++#define CAPI_FUNCTION_REGISTER 0 ++#define CAPI_FUNCTION_RELEASE 1 ++#define CAPI_FUNCTION_GET_PROFILE 2 ++#define CAPI_FUNCTION_GET_MANUFACTURER 3 ++#define CAPI_FUNCTION_GET_VERSION 4 ++#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5 ++#define CAPI_FUNCTION_MANUFACTURER 6 ++#define CAPI_FUNCTION_LOOPBACK 7 ++ ++static struct capi_driver_interface *di; ++ ++ ++#define CMTP_MSGNUM 1 ++#define CMTP_APPLID 2 ++#define CMTP_MAPPING 3 ++ ++static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl) ++{ ++ struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL); ++ ++ BT_DBG("session %p application %p appl %d", session, app, appl); ++ ++ if (!app) ++ return NULL; ++ ++ memset(app, 0, sizeof(*app)); ++ ++ app->state = BT_OPEN; ++ app->appl = appl; ++ ++ list_add_tail(&app->list, &session->applications); ++ ++ return app; ++} ++ ++static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app) ++{ ++ BT_DBG("session %p application %p", session, app); ++ ++ if (app) { ++ list_del(&app->list); ++ kfree(app); ++ } ++} ++ ++static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value) ++{ ++ struct cmtp_application *app; ++ struct list_head *p, *n; ++ ++ list_for_each_safe(p, n, &session->applications) { ++ app = list_entry(p, struct cmtp_application, list); ++ switch (pattern) { ++ case CMTP_MSGNUM: ++ if (app->msgnum == value) ++ return app; ++ break; ++ case CMTP_APPLID: ++ if (app->appl == value) ++ return app; ++ break; ++ case CMTP_MAPPING: ++ if (app->mapping == value) ++ return app; ++ break; ++ } ++ } ++ ++ return NULL; ++} ++ ++static int cmtp_msgnum_get(struct cmtp_session *session) ++{ ++ session->msgnum++; ++ ++ if ((session->msgnum & 0xff) > 200) ++ session->msgnum = CMTP_INITIAL_MSGNUM + 1; ++ ++ return session->msgnum; ++} ++ ++ ++static void cmtp_send_interopmsg(struct cmtp_session *session, ++ __u8 subcmd, __u16 appl, __u16 msgnum, ++ __u16 function, unsigned char *buf, int len) ++{ ++ struct sk_buff *skb; ++ unsigned char *s; ++ ++ BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum); ++ ++ if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) { ++ BT_ERR("Can't allocate memory for interoperability packet"); ++ return; ++ } ++ ++ s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len); ++ ++ capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len); ++ capimsg_setu16(s, 2, appl); ++ capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY); ++ capimsg_setu8 (s, 5, subcmd); ++ capimsg_setu16(s, 6, msgnum); ++ ++ /* Interoperability selector (Bluetooth Device Management) */ ++ capimsg_setu16(s, 8, 0x0001); ++ ++ capimsg_setu8 (s, 10, 3 + len); ++ capimsg_setu16(s, 11, function); ++ capimsg_setu8 (s, 13, len); ++ ++ if (len > 0) ++ memcpy(s + 14, buf, len); ++ ++ cmtp_send_capimsg(session, skb); ++} ++ ++static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb) ++{ ++ struct capi_ctr *ctrl = session->ctrl; ++ struct cmtp_application *application; ++ __u16 appl, msgnum, func, info; ++ __u32 controller; ++ ++ BT_DBG("session %p skb %p len %d", session, skb, skb->len); ++ ++ switch (CAPIMSG_SUBCOMMAND(skb->data)) { ++ case CAPI_CONF: ++ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5); ++ info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8); ++ ++ switch (func) { ++ case CAPI_FUNCTION_REGISTER: ++ msgnum = CAPIMSG_MSGID(skb->data); ++ ++ application = cmtp_application_get(session, CMTP_MSGNUM, msgnum); ++ if (application) { ++ application->state = BT_CONNECTED; ++ application->msgnum = 0; ++ application->mapping = CAPIMSG_APPID(skb->data); ++ wake_up_interruptible(&session->wait); ++ } ++ ++ break; ++ ++ case CAPI_FUNCTION_RELEASE: ++ appl = CAPIMSG_APPID(skb->data); ++ ++ application = cmtp_application_get(session, CMTP_MAPPING, appl); ++ if (application) { ++ application->state = BT_CLOSED; ++ application->msgnum = 0; ++ wake_up_interruptible(&session->wait); ++ } ++ ++ break; ++ ++ case CAPI_FUNCTION_GET_PROFILE: ++ controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11); ++ msgnum = CAPIMSG_MSGID(skb->data); ++ ++ if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) { ++ session->ncontroller = controller; ++ wake_up_interruptible(&session->wait); ++ break; ++ } ++ ++ if (!info && ctrl) { ++ memcpy(&ctrl->profile, ++ skb->data + CAPI_MSG_BASELEN + 11, ++ sizeof(capi_profile)); ++ session->state = BT_CONNECTED; ++ ctrl->ready(ctrl); ++ } ++ ++ break; ++ ++ case CAPI_FUNCTION_GET_MANUFACTURER: ++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10); ++ ++ if (!info && ctrl) { ++ strncpy(ctrl->manu, ++ skb->data + CAPI_MSG_BASELEN + 15, ++ skb->data[CAPI_MSG_BASELEN + 14]); ++ } ++ ++ break; ++ ++ case CAPI_FUNCTION_GET_VERSION: ++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); ++ ++ if (!info && ctrl) { ++ ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16); ++ ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20); ++ ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24); ++ ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28); ++ } ++ ++ break; ++ ++ case CAPI_FUNCTION_GET_SERIAL_NUMBER: ++ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); ++ ++ if (!info && ctrl) { ++ memset(ctrl->serial, 0, CAPI_SERIAL_LEN); ++ strncpy(ctrl->serial, ++ skb->data + CAPI_MSG_BASELEN + 17, ++ skb->data[CAPI_MSG_BASELEN + 16]); ++ } ++ ++ break; ++ } ++ ++ break; ++ ++ case CAPI_IND: ++ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3); ++ ++ if (func == CAPI_FUNCTION_LOOPBACK) { ++ appl = CAPIMSG_APPID(skb->data); ++ msgnum = CAPIMSG_MSGID(skb->data); ++ cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func, ++ skb->data + CAPI_MSG_BASELEN + 6, ++ skb->data[CAPI_MSG_BASELEN + 5]); ++ } ++ ++ break; ++ } ++ ++ kfree_skb(skb); ++} ++ ++void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) ++{ ++ struct capi_ctr *ctrl = session->ctrl; ++ struct cmtp_application *application; ++ __u16 cmd, appl, info; ++ __u32 ncci, contr; ++ ++ BT_DBG("session %p skb %p len %d", session, skb, skb->len); ++ ++ if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) { ++ cmtp_recv_interopmsg(session, skb); ++ return; ++ } ++ ++ if (session->flags & (1 << CMTP_LOOPBACK)) { ++ kfree_skb(skb); ++ return; ++ } ++ ++ cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data)); ++ appl = CAPIMSG_APPID(skb->data); ++ contr = CAPIMSG_CONTROL(skb->data); ++ ++ application = cmtp_application_get(session, CMTP_MAPPING, appl); ++ if (application) { ++ appl = application->appl; ++ CAPIMSG_SETAPPID(skb->data, appl); ++ } else { ++ BT_ERR("Can't find application with id %d", appl); ++ kfree_skb(skb); ++ return; ++ } ++ ++ if ((contr & 0x7f) == 0x01) { ++ contr = (contr & 0xffffff80) | session->num; ++ CAPIMSG_SETCONTROL(skb->data, contr); ++ } ++ ++ if (!ctrl) { ++ BT_ERR("Can't find controller %d for message", session->num); ++ kfree_skb(skb); ++ return; ++ } ++ ++ switch (cmd) { ++ case CAPI_CONNECT_B3_CONF: ++ ncci = CAPIMSG_NCCI(skb->data); ++ info = CAPIMSG_U16(skb->data, 12); ++ ++ BT_DBG("CONNECT_B3_CONF ncci 0x%02x info 0x%02x", ncci, info); ++ ++ if (info == 0) ++ ctrl->new_ncci(ctrl, appl, ncci, 8); ++ ++ ctrl->handle_capimsg(ctrl, appl, skb); ++ break; ++ ++ case CAPI_CONNECT_B3_IND: ++ ncci = CAPIMSG_NCCI(skb->data); ++ ++ BT_DBG("CONNECT_B3_IND ncci 0x%02x", ncci); ++ ++ ctrl->new_ncci(ctrl, appl, ncci, 8); ++ ctrl->handle_capimsg(ctrl, appl, skb); ++ break; ++ ++ case CAPI_DISCONNECT_B3_IND: ++ ncci = CAPIMSG_NCCI(skb->data); ++ ++ BT_DBG("DISCONNECT_B3_IND ncci 0x%02x", ncci); ++ ++ if (ncci == 0xffffffff) ++ BT_ERR("DISCONNECT_B3_IND with ncci 0xffffffff"); ++ ++ ctrl->handle_capimsg(ctrl, appl, skb); ++ ctrl->free_ncci(ctrl, appl, ncci); ++ break; ++ ++ default: ++ ctrl->handle_capimsg(ctrl, appl, skb); ++ break; ++ } ++} ++ ++void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb) ++{ ++ struct cmtp_scb *scb = (void *) skb->cb; ++ ++ BT_DBG("session %p skb %p len %d", session, skb, skb->len); ++ ++ scb->id = -1; ++ scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3); ++ ++ skb_queue_tail(&session->transmit, skb); ++ ++ cmtp_schedule(session); ++} ++ ++ ++static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) ++{ ++ BT_DBG("ctrl %p data %p", ctrl, data); ++ ++ return -EIO; ++} ++ ++static void cmtp_reset_ctr(struct capi_ctr *ctrl) ++{ ++ BT_DBG("ctrl %p", ctrl); ++ ++ ctrl->reseted(ctrl); ++} ++ ++static void cmtp_remove_ctr(struct capi_ctr *ctrl) ++{ ++ struct cmtp_session *session = ctrl->driverdata; ++ ++ BT_DBG("ctrl %p", ctrl); ++ ++ ctrl->suspend_output(ctrl); ++ ++ atomic_inc(&session->terminate); ++ cmtp_schedule(session); ++} ++ ++static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ struct cmtp_session *session = ctrl->driverdata; ++ struct cmtp_application *application; ++ unsigned long timeo = CMTP_INTEROP_TIMEOUT; ++ unsigned char buf[8]; ++ int err = 0, nconn, want = rp->level3cnt; ++ ++ BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d", ++ ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen); ++ ++ application = cmtp_application_add(session, appl); ++ if (!application) { ++ BT_ERR("Can't allocate memory for new application"); ++ ctrl->appl_released(ctrl, appl); ++ return; ++ } ++ ++ if (want < 0) ++ nconn = ctrl->profile.nbchannel * -want; ++ else ++ nconn = want; ++ ++ if (nconn == 0) ++ nconn = ctrl->profile.nbchannel; ++ ++ capimsg_setu16(buf, 0, nconn); ++ capimsg_setu16(buf, 2, rp->datablkcnt); ++ capimsg_setu16(buf, 4, rp->datablklen); ++ ++ application->state = BT_CONFIG; ++ application->msgnum = cmtp_msgnum_get(session); ++ ++ cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum, ++ CAPI_FUNCTION_REGISTER, buf, 6); ++ ++ add_wait_queue(&session->wait, &wait); ++ while (1) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ if (!timeo) { ++ err = -EAGAIN; ++ break; ++ } ++ ++ if (application->state == BT_CLOSED) { ++ err = -application->err; ++ break; ++ } ++ ++ if (application->state == BT_CONNECTED) ++ break; ++ ++ if (signal_pending(current)) { ++ err = -EINTR; ++ break; ++ } ++ ++ timeo = schedule_timeout(timeo); ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&session->wait, &wait); ++ ++ if (err) { ++ ctrl->appl_released(ctrl, appl); ++ cmtp_application_del(session, application); ++ return; ++ } ++ ++ ctrl->appl_registered(ctrl, appl); ++} ++ ++static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ struct cmtp_session *session = ctrl->driverdata; ++ struct cmtp_application *application; ++ unsigned long timeo = CMTP_INTEROP_TIMEOUT; ++ ++ BT_DBG("ctrl %p appl %d", ctrl, appl); ++ ++ application = cmtp_application_get(session, CMTP_APPLID, appl); ++ if (!application) { ++ BT_ERR("Can't find application"); ++ return; ++ } ++ ++ application->msgnum = cmtp_msgnum_get(session); ++ ++ cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum, ++ CAPI_FUNCTION_RELEASE, NULL, 0); ++ ++ add_wait_queue(&session->wait, &wait); ++ while (timeo) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ if (application->state == BT_CLOSED) ++ break; ++ ++ if (signal_pending(current)) ++ break; ++ ++ timeo = schedule_timeout(timeo); ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&session->wait, &wait); ++ ++ cmtp_application_del(session, application); ++ ctrl->appl_released(ctrl, appl); ++} ++ ++static void cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) ++{ ++ struct cmtp_session *session = ctrl->driverdata; ++ struct cmtp_application *application; ++ __u16 appl; ++ __u32 contr; ++ ++ BT_DBG("ctrl %p skb %p", ctrl, skb); ++ ++ appl = CAPIMSG_APPID(skb->data); ++ contr = CAPIMSG_CONTROL(skb->data); ++ ++ application = cmtp_application_get(session, CMTP_APPLID, appl); ++ if ((!application) || (application->state != BT_CONNECTED)) { ++ BT_ERR("Can't find application with id %d", appl); ++ kfree_skb(skb); ++ return; ++ } ++ ++ CAPIMSG_SETAPPID(skb->data, application->mapping); ++ ++ if ((contr & 0x7f) == session->num) { ++ contr = (contr & 0xffffff80) | 0x01; ++ CAPIMSG_SETCONTROL(skb->data, contr); ++ } ++ ++ cmtp_send_capimsg(session, skb); ++} ++ ++static char *cmtp_procinfo(struct capi_ctr *ctrl) ++{ ++ return "CAPI Message Transport Protocol"; ++} ++ ++static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl) ++{ ++ struct cmtp_session *session = ctrl->driverdata; ++ struct cmtp_application *app; ++ struct list_head *p, *n; ++ int len = 0; ++ ++ len += sprintf(page + len, "%s (Revision %s)\n\n", cmtp_procinfo(ctrl), REVISION); ++ len += sprintf(page + len, "addr %s\n", session->name); ++ len += sprintf(page + len, "ctrl %d\n", session->num); ++ ++ list_for_each_safe(p, n, &session->applications) { ++ app = list_entry(p, struct cmtp_application, list); ++ len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping); ++ } ++ ++ if (off + count >= len) ++ *eof = 1; ++ ++ if (len < off) ++ return 0; ++ ++ *start = page + off; ++ ++ return ((count < len - off) ? count : len - off); ++} ++ ++static struct capi_driver cmtp_driver = { ++ name: "cmtp", ++ revision: REVISION, ++ load_firmware: cmtp_load_firmware, ++ reset_ctr: cmtp_reset_ctr, ++ remove_ctr: cmtp_remove_ctr, ++ register_appl: cmtp_register_appl, ++ release_appl: cmtp_release_appl, ++ send_message: cmtp_send_message, ++ procinfo: cmtp_procinfo, ++ ctr_read_proc: cmtp_ctr_read_proc, ++ ++ driver_read_proc: 0, ++ add_card: 0, ++}; ++ ++ ++int cmtp_attach_device(struct cmtp_session *session) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ unsigned long timeo = CMTP_INTEROP_TIMEOUT; ++ unsigned char buf[4]; ++ ++ BT_DBG("session %p", session); ++ ++ capimsg_setu32(buf, 0, 0); ++ ++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM, ++ CAPI_FUNCTION_GET_PROFILE, buf, 4); ++ ++ add_wait_queue(&session->wait, &wait); ++ while (timeo) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ if (session->ncontroller) ++ break; ++ ++ if (signal_pending(current)) ++ break; ++ ++ timeo = schedule_timeout(timeo); ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&session->wait, &wait); ++ ++ BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name); ++ ++ if (!timeo) ++ return -ETIMEDOUT; ++ ++ if (!session->ncontroller) ++ return -ENODEV; ++ ++ ++ if (session->ncontroller > 1) ++ BT_INFO("Setting up only CAPI controller 1"); ++ ++ if (!(session->ctrl = di->attach_ctr(&cmtp_driver, session->name, session))) { ++ BT_ERR("Can't attach new controller"); ++ return -EBUSY; ++ } ++ ++ session->num = session->ctrl->cnr; ++ ++ BT_DBG("session %p ctrl %p num %d", session, session->ctrl, session->num); ++ ++ capimsg_setu32(buf, 0, 1); ++ ++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), ++ CAPI_FUNCTION_GET_MANUFACTURER, buf, 4); ++ ++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), ++ CAPI_FUNCTION_GET_VERSION, buf, 4); ++ ++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), ++ CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4); ++ ++ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), ++ CAPI_FUNCTION_GET_PROFILE, buf, 4); ++ ++ return 0; ++} ++ ++void cmtp_detach_device(struct cmtp_session *session) ++{ ++ struct capi_ctr *ctrl = session->ctrl; ++ ++ BT_DBG("session %p ctrl %p", session, ctrl); ++ ++ if (!ctrl) ++ return; ++ ++ ctrl->reseted(ctrl); ++ ++ di->detach_ctr(ctrl); ++} ++ ++int cmtp_init_capi(void) ++{ ++ if (!(di = attach_capi_driver(&cmtp_driver))) { ++ BT_ERR("Can't attach CAPI driver"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++void cmtp_cleanup_capi(void) ++{ ++ detach_capi_driver(&cmtp_driver); ++} +diff -Nur c3000_pre/linux/net/bluetooth/cmtp/cmtp.h c3000_work/linux/net/bluetooth/cmtp/cmtp.h +--- c3000_pre/linux/net/bluetooth/cmtp/cmtp.h 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/cmtp/cmtp.h 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,138 @@ ++/* ++ CMTP implementation for Linux Bluetooth stack (BlueZ). ++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++#ifndef __CMTP_H ++#define __CMTP_H ++ ++#include <linux/types.h> ++#include <net/bluetooth/bluetooth.h> ++ ++#define BTNAMSIZ 18 ++ ++/* CMTP ioctl defines */ ++#define CMTPCONNADD _IOW('C', 200, int) ++#define CMTPCONNDEL _IOW('C', 201, int) ++#define CMTPGETCONNLIST _IOR('C', 210, int) ++#define CMTPGETCONNINFO _IOR('C', 211, int) ++ ++#define CMTP_LOOPBACK 0 ++ ++struct cmtp_connadd_req { ++ int sock; // Connected socket ++ __u32 flags; ++}; ++ ++struct cmtp_conndel_req { ++ bdaddr_t bdaddr; ++ __u32 flags; ++}; ++ ++struct cmtp_conninfo { ++ bdaddr_t bdaddr; ++ __u32 flags; ++ __u16 state; ++ int num; ++}; ++ ++struct cmtp_connlist_req { ++ __u32 cnum; ++ struct cmtp_conninfo *ci; ++}; ++ ++int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock); ++int cmtp_del_connection(struct cmtp_conndel_req *req); ++int cmtp_get_connlist(struct cmtp_connlist_req *req); ++int cmtp_get_conninfo(struct cmtp_conninfo *ci); ++ ++/* CMTP session defines */ ++#define CMTP_INTEROP_TIMEOUT (HZ * 5) ++#define CMTP_INITIAL_MSGNUM 0xff00 ++ ++struct cmtp_session { ++ struct list_head list; ++ ++ struct socket *sock; ++ ++ bdaddr_t bdaddr; ++ ++ unsigned long state; ++ unsigned long flags; ++ ++ uint mtu; ++ ++ char name[BTNAMSIZ]; ++ ++ atomic_t terminate; ++ ++ wait_queue_head_t wait; ++ ++ int ncontroller; ++ int num; ++ struct capi_ctr *ctrl; ++ ++ struct list_head applications; ++ ++ unsigned long blockids; ++ int msgnum; ++ ++ struct sk_buff_head transmit; ++ ++ struct sk_buff *reassembly[16]; ++}; ++ ++struct cmtp_application { ++ struct list_head list; ++ ++ unsigned long state; ++ int err; ++ ++ __u16 appl; ++ __u16 mapping; ++ ++ __u16 msgnum; ++}; ++ ++struct cmtp_scb { ++ int id; ++ int data; ++}; ++ ++int cmtp_attach_device(struct cmtp_session *session); ++void cmtp_detach_device(struct cmtp_session *session); ++ ++void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb); ++void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb); ++ ++static inline void cmtp_schedule(struct cmtp_session *session) ++{ ++ struct sock *sk = session->sock->sk; ++ ++ wake_up_interruptible(sk->sleep); ++} ++ ++/* CMTP init defines */ ++int cmtp_init_capi(void); ++int cmtp_init_sockets(void); ++void cmtp_cleanup_capi(void); ++void cmtp_cleanup_sockets(void); ++ ++#endif /* __CMTP_H */ +diff -Nur c3000_pre/linux/net/bluetooth/cmtp/core.c c3000_work/linux/net/bluetooth/cmtp/core.c +--- c3000_pre/linux/net/bluetooth/cmtp/core.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/cmtp/core.c 2004-12-16 23:01:14.000000000 +0900 +@@ -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"); +diff -Nur c3000_pre/linux/net/bluetooth/cmtp/sock.c c3000_work/linux/net/bluetooth/cmtp/sock.c +--- c3000_pre/linux/net/bluetooth/cmtp/sock.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/cmtp/sock.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,208 @@ ++/* ++ CMTP implementation for Linux Bluetooth stack (BlueZ). ++ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/major.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/poll.h> ++#include <linux/fcntl.h> ++#include <linux/skbuff.h> ++#include <linux/socket.h> ++#include <linux/ioctl.h> ++#include <linux/file.h> ++#include <net/sock.h> ++ ++#include <asm/system.h> ++#include <asm/uaccess.h> ++ ++#include "cmtp.h" ++ ++#ifndef CONFIG_BLUEZ_CMTP_DEBUG ++#undef BT_DBG ++#define BT_DBG(D...) ++#endif ++ ++static int cmtp_sock_release(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ BT_DBG("sock %p sk %p", sock, sk); ++ ++ if (!sk) ++ return 0; ++ ++ sock_orphan(sk); ++ sock_put(sk); ++ ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ++{ ++ struct cmtp_connadd_req ca; ++ struct cmtp_conndel_req cd; ++ struct cmtp_connlist_req cl; ++ struct cmtp_conninfo ci; ++ struct socket *nsock; ++ int err; ++ ++ BT_DBG("cmd %x arg %lx", cmd, arg); ++ ++ switch (cmd) { ++ case CMTPCONNADD: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EACCES; ++ ++ if (copy_from_user(&ca, (void *) arg, sizeof(ca))) ++ return -EFAULT; ++ ++ nsock = sockfd_lookup(ca.sock, &err); ++ if (!nsock) ++ return err; ++ ++ if (nsock->sk->state != BT_CONNECTED) { ++ fput(nsock->file); ++ return -EBADFD; ++ } ++ ++ err = cmtp_add_connection(&ca, nsock); ++ if (!err) { ++ if (copy_to_user((void *) arg, &ca, sizeof(ca))) ++ err = -EFAULT; ++ } else ++ fput(nsock->file); ++ ++ return err; ++ ++ case CMTPCONNDEL: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EACCES; ++ ++ if (copy_from_user(&cd, (void *) arg, sizeof(cd))) ++ return -EFAULT; ++ ++ return cmtp_del_connection(&cd); ++ ++ case CMTPGETCONNLIST: ++ if (copy_from_user(&cl, (void *) arg, sizeof(cl))) ++ return -EFAULT; ++ ++ if (cl.cnum <= 0) ++ return -EINVAL; ++ ++ err = cmtp_get_connlist(&cl); ++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl))) ++ return -EFAULT; ++ ++ return err; ++ ++ case CMTPGETCONNINFO: ++ if (copy_from_user(&ci, (void *) arg, sizeof(ci))) ++ return -EFAULT; ++ ++ err = cmtp_get_conninfo(&ci); ++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci))) ++ return -EFAULT; ++ ++ return err; ++ } ++ ++ return -EINVAL; ++} ++ ++static struct proto_ops cmtp_sock_ops = { ++ family: PF_BLUETOOTH, ++ release: cmtp_sock_release, ++ ioctl: cmtp_sock_ioctl, ++ bind: sock_no_bind, ++ getname: sock_no_getname, ++ sendmsg: sock_no_sendmsg, ++ recvmsg: sock_no_recvmsg, ++ poll: sock_no_poll, ++ listen: sock_no_listen, ++ shutdown: sock_no_shutdown, ++ setsockopt: sock_no_setsockopt, ++ getsockopt: sock_no_getsockopt, ++ connect: sock_no_connect, ++ socketpair: sock_no_socketpair, ++ accept: sock_no_accept, ++ mmap: sock_no_mmap ++}; ++ ++static int cmtp_sock_create(struct socket *sock, int protocol) ++{ ++ struct sock *sk; ++ ++ BT_DBG("sock %p", sock); ++ ++ if (sock->type != SOCK_RAW) ++ return -ESOCKTNOSUPPORT; ++ ++ sock->ops = &cmtp_sock_ops; ++ ++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1))) ++ return -ENOMEM; ++ ++ MOD_INC_USE_COUNT; ++ ++ sock->state = SS_UNCONNECTED; ++ sock_init_data(sock, sk); ++ ++ sk->destruct = NULL; ++ sk->protocol = protocol; ++ ++ return 0; ++} ++ ++static struct net_proto_family cmtp_sock_family_ops = { ++ family: PF_BLUETOOTH, ++ create: cmtp_sock_create ++}; ++ ++int cmtp_init_sockets(void) ++{ ++ int err; ++ ++ if ((err = bluez_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops))) { ++ BT_ERR("Can't register CMTP socket layer (%d)", err); ++ return err; ++ } ++ ++ return 0; ++} ++ ++void cmtp_cleanup_sockets(void) ++{ ++ int err; ++ ++ if ((err = bluez_sock_unregister(BTPROTO_CMTP))) ++ BT_ERR("Can't unregister CMTP socket layer (%d)", err); ++ ++ return; ++} +diff -Nur c3000_pre/linux/net/bluetooth/hci_conn.c c3000_work/linux/net/bluetooth/hci_conn.c +--- c3000_pre/linux/net/bluetooth/hci_conn.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/hci_conn.c 2004-12-16 23:01:14.000000000 +0900 +@@ -71,6 +71,7 @@ + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.bdaddr, &conn->dst); ++ cp.pscan_rep_mode = 0x02; + + if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) && + inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { +@@ -357,21 +358,24 @@ + struct hci_conn_info *ci; + struct hci_dev *hdev; + struct list_head *p; +- int n = 0, size; ++ int n = 0, size, err; + + if (copy_from_user(&req, (void *) arg, sizeof(req))) + return -EFAULT; + +- if (!(hdev = hci_dev_get(req.dev_id))) +- return -ENODEV; +- +- size = req.conn_num * sizeof(struct hci_conn_info) + sizeof(req); ++ if (!req.conn_num || req.conn_num > (PAGE_SIZE * 2) / sizeof(*ci)) ++ return -EINVAL; + +- if (verify_area(VERIFY_WRITE, (void *)arg, size)) +- return -EFAULT; ++ size = sizeof(req) + req.conn_num * sizeof(*ci); + + if (!(cl = (void *) kmalloc(size, GFP_KERNEL))) + return -ENOMEM; ++ ++ if (!(hdev = hci_dev_get(req.dev_id))) { ++ kfree(cl); ++ return -ENODEV; ++ } ++ + ci = cl->conn_info; + + hci_dev_lock_bh(hdev); +@@ -385,20 +389,21 @@ + (ci + n)->out = c->out; + (ci + n)->state = c->state; + (ci + n)->link_mode = c->link_mode; +- n++; ++ if (++n >= req.conn_num) ++ break; + } + hci_dev_unlock_bh(hdev); + + cl->dev_id = hdev->id; + cl->conn_num = n; +- size = n * sizeof(struct hci_conn_info) + sizeof(req); ++ size = sizeof(req) + n * sizeof(*ci); + + hci_dev_put(hdev); + +- copy_to_user((void *) arg, cl, size); ++ err = copy_to_user((void *) arg, cl, size); + kfree(cl); + +- return 0; ++ return err ? -EFAULT : 0; + } + + int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg) +@@ -411,9 +416,6 @@ + if (copy_from_user(&req, (void *) arg, sizeof(req))) + return -EFAULT; + +- if (verify_area(VERIFY_WRITE, ptr, sizeof(ci))) +- return -EFAULT; +- + hci_dev_lock_bh(hdev); + conn = conn_hash_lookup_ba(hdev, req.type, &req.bdaddr); + if (conn) { +@@ -429,6 +431,5 @@ + if (!conn) + return -ENOENT; + +- copy_to_user(ptr, &ci, sizeof(ci)); +- return 0; ++ return copy_to_user(ptr, &ci, sizeof(ci)) ? -EFAULT : 0; + } +diff -Nur c3000_pre/linux/net/bluetooth/hci_core.c c3000_work/linux/net/bluetooth/hci_core.c +--- c3000_pre/linux/net/bluetooth/hci_core.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/hci_core.c 2004-12-16 23:01:14.000000000 +0900 +@@ -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(struct hci_dev_req) + sizeof(__u16); + +- if (verify_area(VERIFY_WRITE, (void *) arg, size)) +- return -EFAULT; ++ size = sizeof(*dl) + dev_num * sizeof(*dr); + + if (!(dl = kmalloc(size, GFP_KERNEL))) + return -ENOMEM; ++ + dr = dl->dev_req; + + read_lock_bh(&hdev_list_lock); +@@ -738,12 +744,12 @@ + read_unlock_bh(&hdev_list_lock); + + dl->dev_num = n; +- size = n * sizeof(struct hci_dev_req) + sizeof(__u16); ++ size = sizeof(*dl) + n * sizeof(*dr); + +- copy_to_user((void *) arg, dl, size); ++ err = copy_to_user((void *) arg, dl, size); + kfree(dl); + +- return 0; ++ return err ? -EFAULT : 0; + } + + int hci_get_dev_info(unsigned long arg) +@@ -864,6 +870,22 @@ + return 0; + } + ++/* Suspend HCI device */ ++int hci_suspend_dev(struct hci_dev *hdev) ++{ ++ hci_notify(hdev, HCI_DEV_SUSPEND); ++ hci_run_hotplug(hdev->name, "suspend"); ++ return 0; ++} ++ ++/* Resume HCI device */ ++int hci_resume_dev(struct hci_dev *hdev) ++{ ++ hci_notify(hdev, HCI_DEV_RESUME); ++ hci_run_hotplug(hdev->name, "resume"); ++ return 0; ++} ++ + /* Receive frame from HCI drivers */ + int hci_recv_frame(struct sk_buff *skb) + { +@@ -959,40 +981,6 @@ + return hdev->send(skb); + } + +-/* Send raw HCI frame */ +-int hci_send_raw(struct sk_buff *skb) +-{ +- struct hci_dev *hdev = (struct hci_dev *) skb->dev; +- +- if (!hdev) { +- kfree_skb(skb); +- return -ENODEV; +- } +- +- BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); +- +- if (!test_bit(HCI_RAW, &hdev->flags)) { +- /* Queue frame according it's type */ +- switch (skb->pkt_type) { +- case HCI_COMMAND_PKT: +- skb_queue_tail(&hdev->cmd_q, skb); +- hci_sched_cmd(hdev); +- return 0; +- +- case HCI_ACLDATA_PKT: +- case HCI_SCODATA_PKT: +- /* FIXME: +- * Check header here and queue to apropriate connection. +- */ +- break; +- } +- } +- +- skb_queue_tail(&hdev->raw_q, skb); +- hci_sched_tx(hdev); +- return 0; +-} +- + /* Send HCI command */ + int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param) + { +diff -Nur c3000_pre/linux/net/bluetooth/hci_event.c c3000_work/linux/net/bluetooth/hci_event.c +--- c3000_pre/linux/net/bluetooth/hci_event.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/hci_event.c 2004-12-16 23:01:14.000000000 +0900 +@@ -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; +diff -Nur c3000_pre/linux/net/bluetooth/hci_sock.c c3000_work/linux/net/bluetooth/hci_sock.c +--- c3000_pre/linux/net/bluetooth/hci_sock.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/hci_sock.c 2004-12-16 23:01:14.000000000 +0900 +@@ -49,6 +49,7 @@ + + #include <asm/system.h> + #include <asm/uaccess.h> ++#include <asm/unaligned.h> + + #include <net/bluetooth/bluetooth.h> + #include <net/bluetooth/hci_core.h> +@@ -65,17 +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, 0xa, 0x0, 0x0 }, ++ { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 }, + /* OGF_INFO_PARAM */ +- { 0x22a, 0x0, 0x0, 0x0 } ++ { 0x000002be, 0x00000000, 0x0000, 0x00 }, ++ /* OGF_STATUS_PARAM */ ++ { 0x000000ea, 0x00000000, 0x0000, 0x00 } + } + }; + +@@ -387,25 +391,37 @@ + + skb->pkt_type = *((unsigned char *) skb->data); + skb_pull(skb, 1); ++ skb->dev = (void *) hdev; + +- if (!capable(CAP_NET_RAW)) { +- err = -EPERM; ++ if (skb->pkt_type == HCI_COMMAND_PKT) { ++ u16 opcode = __le16_to_cpu(get_unaligned((u16 *)skb->data)); ++ u16 ogf = cmd_opcode_ogf(opcode); ++ u16 ocf = cmd_opcode_ocf(opcode); ++ ++ if (((ogf > HCI_SFLT_MAX_OGF) || ++ !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) && ++ !capable(CAP_NET_RAW)) { ++ err = -EPERM; ++ goto drop; ++ } + +- if (skb->pkt_type == HCI_COMMAND_PKT) { +- __u16 opcode = __le16_to_cpu(*(__u16 *)skb->data); +- __u16 ogf = cmd_opcode_ogf(opcode) - 1; +- __u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS; +- +- if (ogf > HCI_SFLT_MAX_OGF || +- !hci_test_bit(ocf, &hci_sec_filter.ocf_mask[ogf])) +- goto drop; +- } else ++ if (test_bit(HCI_RAW, &hdev->flags) || (ogf == OGF_VENDOR_CMD)) { ++ skb_queue_tail(&hdev->raw_q, skb); ++ hci_sched_tx(hdev); ++ } else { ++ skb_queue_tail(&hdev->cmd_q, skb); ++ hci_sched_cmd(hdev); ++ } ++ } else { ++ if (!capable(CAP_NET_RAW)) { ++ err = -EPERM; + goto drop; ++ } ++ ++ skb_queue_tail(&hdev->raw_q, skb); ++ hci_sched_tx(hdev); + } +- +- /* Send frame to HCI core */ +- skb->dev = (void *) hdev; +- hci_send_raw(skb); ++ + err = len; + + done: +diff -Nur c3000_pre/linux/net/bluetooth/hidp/Config.in c3000_work/linux/net/bluetooth/hidp/Config.in +--- c3000_pre/linux/net/bluetooth/hidp/Config.in 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/hidp/Config.in 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,5 @@ ++# ++# Bluetooth HIDP layer configuration ++# ++ ++dep_tristate 'HIDP protocol support' CONFIG_BLUEZ_HIDP $CONFIG_INPUT $CONFIG_BLUEZ_L2CAP +diff -Nur c3000_pre/linux/net/bluetooth/hidp/Makefile c3000_work/linux/net/bluetooth/hidp/Makefile +--- c3000_pre/linux/net/bluetooth/hidp/Makefile 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/hidp/Makefile 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,10 @@ ++# ++# Makefile for the Linux Bluetooth HIDP layer ++# ++ ++O_TARGET := hidp.o ++ ++obj-y := core.o sock.o ++obj-m += $(O_TARGET) ++ ++include $(TOPDIR)/Rules.make +diff -Nur c3000_pre/linux/net/bluetooth/hidp/core.c c3000_work/linux/net/bluetooth/hidp/core.c +--- c3000_pre/linux/net/bluetooth/hidp/core.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/hidp/core.c 2004-12-16 23:01:14.000000000 +0900 +@@ -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"); +diff -Nur c3000_pre/linux/net/bluetooth/hidp/hidp.h c3000_work/linux/net/bluetooth/hidp/hidp.h +--- c3000_pre/linux/net/bluetooth/hidp/hidp.h 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/hidp/hidp.h 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,122 @@ ++/* ++ HIDP implementation for Linux Bluetooth stack (BlueZ). ++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++#ifndef __HIDP_H ++#define __HIDP_H ++ ++#include <linux/types.h> ++#include <net/bluetooth/bluetooth.h> ++ ++/* HIDP ioctl defines */ ++#define HIDPCONNADD _IOW('H', 200, int) ++#define HIDPCONNDEL _IOW('H', 201, int) ++#define HIDPGETCONNLIST _IOR('H', 210, int) ++#define HIDPGETCONNINFO _IOR('H', 211, int) ++ ++#define HIDP_VIRTUAL_CABLE_UNPLUG 0 ++#define HIDP_BOOT_PROTOCOL_MODE 1 ++#define HIDP_BLUETOOTH_VENDOR_ID 9 ++ ++struct hidp_connadd_req { ++ int ctrl_sock; // Connected control socket ++ int intr_sock; // Connteted interrupt socket ++ __u16 parser; ++ __u16 rd_size; ++ __u8 *rd_data; ++ __u8 country; ++ __u8 subclass; ++ __u16 vendor; ++ __u16 product; ++ __u16 version; ++ __u32 flags; ++ __u32 idle_to; ++ char name[128]; ++}; ++ ++struct hidp_conndel_req { ++ bdaddr_t bdaddr; ++ __u32 flags; ++}; ++ ++struct hidp_conninfo { ++ bdaddr_t bdaddr; ++ __u32 flags; ++ __u16 state; ++ __u16 vendor; ++ __u16 product; ++ __u16 version; ++ char name[128]; ++}; ++ ++struct hidp_connlist_req { ++ __u32 cnum; ++ struct hidp_conninfo *ci; ++}; ++ ++int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock); ++int hidp_del_connection(struct hidp_conndel_req *req); ++int hidp_get_connlist(struct hidp_connlist_req *req); ++int hidp_get_conninfo(struct hidp_conninfo *ci); ++ ++/* HIDP session defines */ ++struct hidp_session { ++ struct list_head list; ++ ++ struct socket *ctrl_sock; ++ struct socket *intr_sock; ++ ++ bdaddr_t bdaddr; ++ ++ unsigned long state; ++ unsigned long flags; ++ unsigned long idle_to; ++ ++ uint ctrl_mtu; ++ uint intr_mtu; ++ ++ atomic_t terminate; ++ ++ unsigned char keys[8]; ++ unsigned char leds; ++ ++ struct input_dev *input; ++ ++ struct timer_list timer; ++ ++ struct sk_buff_head ctrl_transmit; ++ struct sk_buff_head intr_transmit; ++}; ++ ++static inline void hidp_schedule(struct hidp_session *session) ++{ ++ struct sock *ctrl_sk = session->ctrl_sock->sk; ++ struct sock *intr_sk = session->intr_sock->sk; ++ ++ wake_up_interruptible(ctrl_sk->sleep); ++ wake_up_interruptible(intr_sk->sleep); ++} ++ ++/* HIDP init defines */ ++extern int __init hidp_init_sockets(void); ++extern void __exit hidp_cleanup_sockets(void); ++ ++#endif /* __HIDP_H */ +diff -Nur c3000_pre/linux/net/bluetooth/hidp/sock.c c3000_work/linux/net/bluetooth/hidp/sock.c +--- c3000_pre/linux/net/bluetooth/hidp/sock.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/hidp/sock.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,212 @@ ++/* ++ HIDP implementation for Linux Bluetooth stack (BlueZ). ++ Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/major.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/poll.h> ++#include <linux/fcntl.h> ++#include <linux/skbuff.h> ++#include <linux/socket.h> ++#include <linux/ioctl.h> ++#include <linux/file.h> ++#include <linux/init.h> ++#include <net/sock.h> ++ ++#include "hidp.h" ++ ++#ifndef CONFIG_BT_HIDP_DEBUG ++#undef BT_DBG ++#define BT_DBG(D...) ++#endif ++ ++static int hidp_sock_release(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ BT_DBG("sock %p sk %p", sock, sk); ++ ++ if (!sk) ++ return 0; ++ ++ sock_orphan(sk); ++ sock_put(sk); ++ ++ MOD_DEC_USE_COUNT; ++ return 0; ++} ++ ++static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ++{ ++ struct hidp_connadd_req ca; ++ struct hidp_conndel_req cd; ++ struct hidp_connlist_req cl; ++ struct hidp_conninfo ci; ++ struct socket *csock; ++ struct socket *isock; ++ int err; ++ ++ BT_DBG("cmd %x arg %lx", cmd, arg); ++ ++ switch (cmd) { ++ case HIDPCONNADD: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EACCES; ++ ++ if (copy_from_user(&ca, (void *) arg, sizeof(ca))) ++ return -EFAULT; ++ ++ csock = sockfd_lookup(ca.ctrl_sock, &err); ++ if (!csock) ++ return err; ++ ++ isock = sockfd_lookup(ca.intr_sock, &err); ++ if (!isock) { ++ fput(csock->file); ++ return err; ++ } ++ ++ if (csock->sk->state != BT_CONNECTED || isock->sk->state != BT_CONNECTED) { ++ fput(csock->file); ++ fput(isock->file); ++ return -EBADFD; ++ } ++ ++ err = hidp_add_connection(&ca, csock, isock); ++ if (!err) { ++ if (copy_to_user((void *) arg, &ca, sizeof(ca))) ++ err = -EFAULT; ++ } else { ++ fput(csock->file); ++ fput(isock->file); ++ } ++ ++ return err; ++ ++ case HIDPCONNDEL: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EACCES; ++ ++ if (copy_from_user(&cd, (void *) arg, sizeof(cd))) ++ return -EFAULT; ++ ++ return hidp_del_connection(&cd); ++ ++ case HIDPGETCONNLIST: ++ if (copy_from_user(&cl, (void *) arg, sizeof(cl))) ++ return -EFAULT; ++ ++ if (cl.cnum <= 0) ++ return -EINVAL; ++ ++ err = hidp_get_connlist(&cl); ++ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl))) ++ return -EFAULT; ++ ++ return err; ++ ++ case HIDPGETCONNINFO: ++ if (copy_from_user(&ci, (void *) arg, sizeof(ci))) ++ return -EFAULT; ++ ++ err = hidp_get_conninfo(&ci); ++ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci))) ++ return -EFAULT; ++ ++ return err; ++ } ++ ++ return -EINVAL; ++} ++ ++static struct proto_ops hidp_sock_ops = { ++ family: PF_BLUETOOTH, ++ release: hidp_sock_release, ++ ioctl: hidp_sock_ioctl, ++ bind: sock_no_bind, ++ getname: sock_no_getname, ++ sendmsg: sock_no_sendmsg, ++ recvmsg: sock_no_recvmsg, ++ poll: sock_no_poll, ++ listen: sock_no_listen, ++ shutdown: sock_no_shutdown, ++ setsockopt: sock_no_setsockopt, ++ getsockopt: sock_no_getsockopt, ++ connect: sock_no_connect, ++ socketpair: sock_no_socketpair, ++ accept: sock_no_accept, ++ mmap: sock_no_mmap ++}; ++ ++static int hidp_sock_create(struct socket *sock, int protocol) ++{ ++ struct sock *sk; ++ ++ BT_DBG("sock %p", sock); ++ ++ if (sock->type != SOCK_RAW) ++ return -ESOCKTNOSUPPORT; ++ ++ sock->ops = &hidp_sock_ops; ++ ++ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1))) ++ return -ENOMEM; ++ ++ MOD_INC_USE_COUNT; ++ ++ sock->state = SS_UNCONNECTED; ++ sock_init_data(sock, sk); ++ ++ sk->destruct = NULL; ++ sk->protocol = protocol; ++ ++ return 0; ++} ++ ++static struct net_proto_family hidp_sock_family_ops = { ++ family: PF_BLUETOOTH, ++ create: hidp_sock_create ++}; ++ ++int __init hidp_init_sockets(void) ++{ ++ int err; ++ ++ if ((err = bluez_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops))) ++ BT_ERR("Can't register HIDP socket layer (%d)", err); ++ ++ return err; ++} ++ ++void __exit hidp_cleanup_sockets(void) ++{ ++ int err; ++ ++ if ((err = bluez_sock_unregister(BTPROTO_HIDP))) ++ BT_ERR("Can't unregister HIDP socket layer (%d)", err); ++} +diff -Nur c3000_pre/linux/net/bluetooth/l2cap.c c3000_work/linux/net/bluetooth/l2cap.c +--- c3000_pre/linux/net/bluetooth/l2cap.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/l2cap.c 2004-12-16 23:01:14.000000000 +0900 +@@ -27,7 +27,7 @@ + * + * $Id: l2cap.c,v 1.15 2002/09/09 01:14:52 maxk Exp $ + */ +-#define VERSION "2.1" ++#define VERSION "2.3" + + #include <linux/config.h> + #include <linux/module.h> +@@ -178,69 +178,12 @@ + return 0; + } + +-int l2cap_connect(struct sock *sk) +-{ +- bdaddr_t *src = &bluez_pi(sk)->src; +- bdaddr_t *dst = &bluez_pi(sk)->dst; +- struct l2cap_conn *conn; +- struct hci_conn *hcon; +- struct hci_dev *hdev; +- int err = 0; +- +- BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm); +- +- if (!(hdev = hci_get_route(dst, src))) +- return -EHOSTUNREACH; +- +- hci_dev_lock_bh(hdev); +- +- err = -ENOMEM; +- +- hcon = hci_connect(hdev, ACL_LINK, dst); +- if (!hcon) +- goto done; +- +- conn = l2cap_conn_add(hcon, 0); +- if (!conn) { +- hci_conn_put(hcon); +- goto done; +- } +- +- err = 0; +- +- /* Update source addr of the socket */ +- bacpy(src, conn->src); +- +- l2cap_chan_add(conn, sk, NULL); +- +- sk->state = BT_CONNECT; +- l2cap_sock_set_timer(sk, sk->sndtimeo); +- +- if (hcon->state == BT_CONNECTED) { +- if (sk->type == SOCK_SEQPACKET) { +- l2cap_conn_req req; +- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); +- req.psm = l2cap_pi(sk)->psm; +- l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req); +- } else { +- l2cap_sock_clear_timer(sk); +- sk->state = BT_CONNECTED; +- } +- } +- +-done: +- hci_dev_unlock_bh(hdev); +- hci_dev_put(hdev); +- return err; +-} +- + /* -------- Socket interface ---------- */ + static struct sock *__l2cap_get_sock_by_addr(__u16 psm, bdaddr_t *src) + { + struct sock *sk; + for (sk = l2cap_sk_list.head; sk; sk = sk->next) { +- if (l2cap_pi(sk)->psm == psm && +- !bacmp(&bluez_pi(sk)->src, src)) ++ if (sk->sport == psm && !bacmp(&bluez_pi(sk)->src, src)) + break; + } + return sk; +@@ -341,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); +@@ -366,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); + } + +@@ -432,6 +373,9 @@ + if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + ++ if (sock->type == SOCK_RAW && !capable(CAP_NET_RAW)) ++ return -EPERM; ++ + sock->ops = &l2cap_sock_ops; + + if (!(sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL))) +@@ -466,9 +410,9 @@ + /* Save source address */ + bacpy(&bluez_pi(sk)->src, &la->l2_bdaddr); + l2cap_pi(sk)->psm = la->l2_psm; ++ sk->sport = la->l2_psm; + sk->state = BT_BOUND; + } +- + write_unlock_bh(&l2cap_sk_list.lock); + + done: +@@ -476,6 +420,62 @@ + return err; + } + ++static int l2cap_do_connect(struct sock *sk) ++{ ++ bdaddr_t *src = &bluez_pi(sk)->src; ++ bdaddr_t *dst = &bluez_pi(sk)->dst; ++ struct l2cap_conn *conn; ++ struct hci_conn *hcon; ++ struct hci_dev *hdev; ++ int err = 0; ++ ++ BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm); ++ ++ if (!(hdev = hci_get_route(dst, src))) ++ return -EHOSTUNREACH; ++ ++ hci_dev_lock_bh(hdev); ++ ++ err = -ENOMEM; ++ ++ hcon = hci_connect(hdev, ACL_LINK, dst); ++ if (!hcon) ++ goto done; ++ ++ conn = l2cap_conn_add(hcon, 0); ++ if (!conn) { ++ hci_conn_put(hcon); ++ goto done; ++ } ++ ++ err = 0; ++ ++ /* Update source addr of the socket */ ++ bacpy(src, conn->src); ++ ++ l2cap_chan_add(conn, sk, NULL); ++ ++ sk->state = BT_CONNECT; ++ l2cap_sock_set_timer(sk, sk->sndtimeo); ++ ++ if (hcon->state == BT_CONNECTED) { ++ if (sk->type == SOCK_SEQPACKET) { ++ l2cap_conn_req req; ++ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); ++ req.psm = l2cap_pi(sk)->psm; ++ l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req); ++ } else { ++ l2cap_sock_clear_timer(sk); ++ sk->state = BT_CONNECTED; ++ } ++ } ++ ++done: ++ hci_dev_unlock_bh(hdev); ++ hci_dev_put(hdev); ++ return err; ++} ++ + static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) + { + struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; +@@ -521,11 +521,12 @@ + bacpy(&bluez_pi(sk)->dst, &la->l2_bdaddr); + l2cap_pi(sk)->psm = la->l2_psm; + +- if ((err = l2cap_connect(sk))) ++ if ((err = l2cap_do_connect(sk))) + 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); +@@ -758,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 --------- */ +@@ -915,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 +@@ -954,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; +@@ -1318,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; + } +@@ -1439,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; +@@ -1474,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; +@@ -1487,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); + } +@@ -1518,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; + } + +@@ -1537,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); + } +@@ -1590,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; +@@ -1604,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; +@@ -1619,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; +@@ -1643,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; + }; +@@ -1668,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); + } +@@ -1787,7 +1867,7 @@ + if (sk->state != BT_LISTEN) + continue; + +- if (!bacmp(&bluez_pi(sk)->src, bdaddr)) { ++ if (!bacmp(&bluez_pi(sk)->src, &hdev->bdaddr)) { + lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); + exact++; + } else if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY)) +@@ -1941,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); +@@ -1970,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; + } + +@@ -2112,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); + +diff -Nur c3000_pre/linux/net/bluetooth/rfcomm/Config.in c3000_work/linux/net/bluetooth/rfcomm/Config.in +--- c3000_pre/linux/net/bluetooth/rfcomm/Config.in 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/rfcomm/Config.in 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,10 @@ ++# ++# Bluetooth RFCOMM layer configuration ++# ++ ++dep_tristate 'RFCOMM protocol support' CONFIG_BLUEZ_RFCOMM $CONFIG_BLUEZ_L2CAP ++ ++if [ "$CONFIG_BLUEZ_RFCOMM" != "n" ]; then ++ bool ' RFCOMM TTY support' CONFIG_BLUEZ_RFCOMM_TTY ++fi ++ +diff -Nur c3000_pre/linux/net/bluetooth/rfcomm/Makefile c3000_work/linux/net/bluetooth/rfcomm/Makefile +--- c3000_pre/linux/net/bluetooth/rfcomm/Makefile 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/rfcomm/Makefile 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,11 @@ ++# ++# Makefile for the Linux Bluetooth RFCOMM layer ++# ++ ++O_TARGET := rfcomm.o ++ ++obj-y := core.o sock.o crc.o ++obj-$(CONFIG_BLUEZ_RFCOMM_TTY) += tty.o ++obj-m += $(O_TARGET) ++ ++include $(TOPDIR)/Rules.make +diff -Nur c3000_pre/linux/net/bluetooth/rfcomm/core.c c3000_work/linux/net/bluetooth/rfcomm/core.c +--- c3000_pre/linux/net/bluetooth/rfcomm/core.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/rfcomm/core.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,1940 @@ ++/* ++ RFCOMM implementation for Linux Bluetooth stack (BlueZ). ++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> ++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++/* ++ RPN support - Dirk Husemann <hud@zurich.ibm.com> ++*/ ++ ++/* ++ * RFCOMM core. ++ * ++ * $Id: core.c,v 1.46 2002/10/18 20:12:12 maxk Exp $ ++ */ ++ ++#define __KERNEL_SYSCALLS__ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/signal.h> ++#include <linux/init.h> ++#include <linux/wait.h> ++#include <linux/net.h> ++#include <linux/proc_fs.h> ++#include <net/sock.h> ++#include <asm/uaccess.h> ++#include <asm/unaligned.h> ++ ++#include <net/bluetooth/bluetooth.h> ++#include <net/bluetooth/l2cap.h> ++#include <net/bluetooth/rfcomm.h> ++ ++#define VERSION "1.1" ++ ++#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG ++#undef BT_DBG ++#define BT_DBG(D...) ++#endif ++ ++struct task_struct *rfcomm_thread; ++DECLARE_MUTEX(rfcomm_sem); ++unsigned long rfcomm_event; ++ ++static LIST_HEAD(session_list); ++static atomic_t terminate, running; ++ ++static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len); ++static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci); ++static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci); ++static int rfcomm_queue_disc(struct rfcomm_dlc *d); ++static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type); ++static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d); ++static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig); ++static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len); ++static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits); ++static void rfcomm_make_uih(struct sk_buff *skb, u8 addr); ++ ++static void rfcomm_process_connect(struct rfcomm_session *s); ++ ++/* ---- RFCOMM frame parsing macros ---- */ ++#define __get_dlci(b) ((b & 0xfc) >> 2) ++#define __get_channel(b) ((b & 0xf8) >> 3) ++#define __get_dir(b) ((b & 0x04) >> 2) ++#define __get_type(b) ((b & 0xef)) ++ ++#define __test_ea(b) ((b & 0x01)) ++#define __test_cr(b) ((b & 0x02)) ++#define __test_pf(b) ((b & 0x10)) ++ ++#define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01) ++#define __ctrl(type, pf) (((type & 0xef) | (pf << 4))) ++#define __dlci(dir, chn) (((chn & 0x1f) << 1) | dir) ++#define __srv_channel(dlci) (dlci >> 1) ++#define __dir(dlci) (dlci & 0x01) ++ ++#define __len8(len) (((len) << 1) | 1) ++#define __len16(len) ((len) << 1) ++ ++/* MCC macros */ ++#define __mcc_type(cr, type) (((type << 2) | (cr << 1) | 0x01)) ++#define __get_mcc_type(b) ((b & 0xfc) >> 2) ++#define __get_mcc_len(b) ((b & 0xfe) >> 1) ++ ++/* RPN macros */ ++#define __rpn_line_settings(data, stop, parity) ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x3) << 3)) ++#define __get_rpn_data_bits(line) ((line) & 0x3) ++#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1) ++#define __get_rpn_parity(line) (((line) >> 3) & 0x3) ++ ++/* ---- RFCOMM FCS computation ---- */ ++ ++/* CRC on 2 bytes */ ++#define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]]) ++ ++/* FCS on 2 bytes */ ++static inline u8 __fcs(u8 *data) ++{ ++ return (0xff - __crc(data)); ++} ++ ++/* FCS on 3 bytes */ ++static inline u8 __fcs2(u8 *data) ++{ ++ return (0xff - rfcomm_crc_table[__crc(data) ^ data[2]]); ++} ++ ++/* Check FCS */ ++static inline int __check_fcs(u8 *data, int type, u8 fcs) ++{ ++ u8 f = __crc(data); ++ ++ if (type != RFCOMM_UIH) ++ f = rfcomm_crc_table[f ^ data[2]]; ++ ++ return rfcomm_crc_table[f ^ fcs] != 0xcf; ++} ++ ++/* ---- L2CAP callbacks ---- */ ++static void rfcomm_l2state_change(struct sock *sk) ++{ ++ BT_DBG("%p state %d", sk, sk->state); ++ rfcomm_schedule(RFCOMM_SCHED_STATE); ++} ++ ++static void rfcomm_l2data_ready(struct sock *sk, int bytes) ++{ ++ BT_DBG("%p bytes %d", sk, bytes); ++ rfcomm_schedule(RFCOMM_SCHED_RX); ++} ++ ++static int rfcomm_l2sock_create(struct socket **sock) ++{ ++ int err; ++ ++ BT_DBG(""); ++ ++ err = sock_create(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock); ++ if (!err) { ++ struct sock *sk = (*sock)->sk; ++ sk->data_ready = rfcomm_l2data_ready; ++ sk->state_change = rfcomm_l2state_change; ++ } ++ return err; ++} ++ ++/* ---- RFCOMM DLCs ---- */ ++static void rfcomm_dlc_timeout(unsigned long arg) ++{ ++ struct rfcomm_dlc *d = (void *) arg; ++ ++ BT_DBG("dlc %p state %ld", d, d->state); ++ ++ set_bit(RFCOMM_TIMED_OUT, &d->flags); ++ rfcomm_dlc_put(d); ++ rfcomm_schedule(RFCOMM_SCHED_TIMEO); ++} ++ ++static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout) ++{ ++ BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout); ++ ++ if (!mod_timer(&d->timer, jiffies + timeout)) ++ rfcomm_dlc_hold(d); ++} ++ ++static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d) ++{ ++ BT_DBG("dlc %p state %ld", d, d->state); ++ ++ if (timer_pending(&d->timer) && del_timer(&d->timer)) ++ rfcomm_dlc_put(d); ++} ++ ++static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d) ++{ ++ BT_DBG("%p", d); ++ ++ d->state = BT_OPEN; ++ d->flags = 0; ++ d->mscex = 0; ++ d->mtu = RFCOMM_DEFAULT_MTU; ++ d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV; ++ ++ d->cfc = RFCOMM_CFC_DISABLED; ++ d->rx_credits = RFCOMM_DEFAULT_CREDITS; ++} ++ ++struct rfcomm_dlc *rfcomm_dlc_alloc(int prio) ++{ ++ struct rfcomm_dlc *d = kmalloc(sizeof(*d), prio); ++ if (!d) ++ return NULL; ++ memset(d, 0, sizeof(*d)); ++ ++ init_timer(&d->timer); ++ d->timer.function = rfcomm_dlc_timeout; ++ d->timer.data = (unsigned long) d; ++ ++ skb_queue_head_init(&d->tx_queue); ++ spin_lock_init(&d->lock); ++ atomic_set(&d->refcnt, 1); ++ ++ rfcomm_dlc_clear_state(d); ++ ++ BT_DBG("%p", d); ++ return d; ++} ++ ++void rfcomm_dlc_free(struct rfcomm_dlc *d) ++{ ++ BT_DBG("%p", d); ++ ++ skb_queue_purge(&d->tx_queue); ++ kfree(d); ++} ++ ++static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) ++{ ++ BT_DBG("dlc %p session %p", d, s); ++ ++ rfcomm_session_hold(s); ++ ++ rfcomm_dlc_hold(d); ++ list_add(&d->list, &s->dlcs); ++ d->session = s; ++} ++ ++static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) ++{ ++ struct rfcomm_session *s = d->session; ++ ++ BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s); ++ ++ list_del(&d->list); ++ d->session = NULL; ++ rfcomm_dlc_put(d); ++ ++ rfcomm_session_put(s); ++} ++ ++static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) ++{ ++ struct rfcomm_dlc *d; ++ struct list_head *p; ++ ++ list_for_each(p, &s->dlcs) { ++ d = list_entry(p, struct rfcomm_dlc, list); ++ if (d->dlci == dlci) ++ return d; ++ } ++ return NULL; ++} ++ ++static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) ++{ ++ struct rfcomm_session *s; ++ int err = 0; ++ u8 dlci; ++ ++ BT_DBG("dlc %p state %ld %s %s channel %d", ++ d, d->state, batostr(src), batostr(dst), channel); ++ ++ if (channel < 1 || channel > 30) ++ return -EINVAL; ++ ++ if (d->state != BT_OPEN && d->state != BT_CLOSED) ++ return 0; ++ ++ s = rfcomm_session_get(src, dst); ++ if (!s) { ++ s = rfcomm_session_create(src, dst, &err); ++ if (!s) ++ return err; ++ } ++ ++ dlci = __dlci(!s->initiator, channel); ++ ++ /* Check if DLCI already exists */ ++ if (rfcomm_dlc_get(s, dlci)) ++ return -EBUSY; ++ ++ rfcomm_dlc_clear_state(d); ++ ++ d->dlci = dlci; ++ d->addr = __addr(s->initiator, dlci); ++ d->priority = 7; ++ ++ d->state = BT_CONFIG; ++ rfcomm_dlc_link(s, d); ++ ++ d->mtu = s->mtu; ++ d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc; ++ ++ if (s->state == BT_CONNECTED) ++ rfcomm_send_pn(s, 1, d); ++ rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); ++ return 0; ++} ++ ++int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) ++{ ++ mm_segment_t fs; ++ int r; ++ ++ rfcomm_lock(); ++ ++ fs = get_fs(); set_fs(KERNEL_DS); ++ r = __rfcomm_dlc_open(d, src, dst, channel); ++ set_fs(fs); ++ ++ rfcomm_unlock(); ++ return r; ++} ++ ++static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) ++{ ++ struct rfcomm_session *s = d->session; ++ if (!s) ++ return 0; ++ ++ BT_DBG("dlc %p state %ld dlci %d err %d session %p", ++ d, d->state, d->dlci, err, s); ++ ++ switch (d->state) { ++ case BT_CONNECTED: ++ case BT_CONFIG: ++ case BT_CONNECT: ++ d->state = BT_DISCONN; ++ if (skb_queue_empty(&d->tx_queue)) { ++ rfcomm_send_disc(s, d->dlci); ++ rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT); ++ } else { ++ rfcomm_queue_disc(d); ++ rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2); ++ } ++ break; ++ ++ default: ++ rfcomm_dlc_clear_timer(d); ++ ++ rfcomm_dlc_lock(d); ++ d->state = BT_CLOSED; ++ d->state_change(d, err); ++ rfcomm_dlc_unlock(d); ++ ++ skb_queue_purge(&d->tx_queue); ++ rfcomm_dlc_unlink(d); ++ } ++ ++ return 0; ++} ++ ++int rfcomm_dlc_close(struct rfcomm_dlc *d, int err) ++{ ++ mm_segment_t fs; ++ int r; ++ ++ rfcomm_lock(); ++ ++ fs = get_fs(); set_fs(KERNEL_DS); ++ r = __rfcomm_dlc_close(d, err); ++ set_fs(fs); ++ ++ rfcomm_unlock(); ++ return r; ++} ++ ++int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb) ++{ ++ int len = skb->len; ++ ++ if (d->state != BT_CONNECTED) ++ return -ENOTCONN; ++ ++ BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len); ++ ++ if (len > d->mtu) ++ return -EINVAL; ++ ++ rfcomm_make_uih(skb, d->addr); ++ skb_queue_tail(&d->tx_queue, skb); ++ ++ if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags)) ++ rfcomm_schedule(RFCOMM_SCHED_TX); ++ return len; ++} ++ ++void __rfcomm_dlc_throttle(struct rfcomm_dlc *d) ++{ ++ BT_DBG("dlc %p state %ld", d, d->state); ++ ++ if (!d->cfc) { ++ d->v24_sig |= RFCOMM_V24_FC; ++ set_bit(RFCOMM_MSC_PENDING, &d->flags); ++ } ++ rfcomm_schedule(RFCOMM_SCHED_TX); ++} ++ ++void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) ++{ ++ BT_DBG("dlc %p state %ld", d, d->state); ++ ++ if (!d->cfc) { ++ d->v24_sig &= ~RFCOMM_V24_FC; ++ set_bit(RFCOMM_MSC_PENDING, &d->flags); ++ } ++ rfcomm_schedule(RFCOMM_SCHED_TX); ++} ++ ++/* ++ Set/get modem status functions use _local_ status i.e. what we report ++ to the other side. ++ Remote status is provided by dlc->modem_status() callback. ++ */ ++int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig) ++{ ++ BT_DBG("dlc %p state %ld v24_sig 0x%x", ++ d, d->state, v24_sig); ++ ++ if (test_bit(RFCOMM_RX_THROTTLED, &d->flags)) ++ v24_sig |= RFCOMM_V24_FC; ++ else ++ v24_sig &= ~RFCOMM_V24_FC; ++ ++ d->v24_sig = v24_sig; ++ ++ if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags)) ++ rfcomm_schedule(RFCOMM_SCHED_TX); ++ ++ return 0; ++} ++ ++int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig) ++{ ++ BT_DBG("dlc %p state %ld v24_sig 0x%x", ++ d, d->state, d->v24_sig); ++ ++ *v24_sig = d->v24_sig; ++ return 0; ++} ++ ++/* ---- RFCOMM sessions ---- */ ++struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) ++{ ++ struct rfcomm_session *s = kmalloc(sizeof(*s), GFP_KERNEL); ++ if (!s) ++ return NULL; ++ memset(s, 0, sizeof(*s)); ++ ++ BT_DBG("session %p sock %p", s, sock); ++ ++ INIT_LIST_HEAD(&s->dlcs); ++ s->state = state; ++ s->sock = sock; ++ ++ s->mtu = RFCOMM_DEFAULT_MTU; ++ s->cfc = RFCOMM_CFC_UNKNOWN; ++ ++ list_add(&s->list, &session_list); ++ ++ /* Do not increment module usage count for listeting sessions. ++ * Otherwise we won't be able to unload the module. */ ++ if (state != BT_LISTEN) ++ MOD_INC_USE_COUNT; ++ return s; ++} ++ ++void rfcomm_session_del(struct rfcomm_session *s) ++{ ++ int state = s->state; ++ ++ BT_DBG("session %p state %ld", s, s->state); ++ ++ list_del(&s->list); ++ ++ if (state == BT_CONNECTED) ++ rfcomm_send_disc(s, 0); ++ ++ sock_release(s->sock); ++ kfree(s); ++ ++ if (state != BT_LISTEN) ++ MOD_DEC_USE_COUNT; ++} ++ ++struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) ++{ ++ struct rfcomm_session *s; ++ struct list_head *p, *n; ++ struct bluez_pinfo *pi; ++ list_for_each_safe(p, n, &session_list) { ++ s = list_entry(p, struct rfcomm_session, list); ++ pi = bluez_pi(s->sock->sk); ++ ++ if ((!bacmp(src, BDADDR_ANY) || !bacmp(&pi->src, src)) && ++ !bacmp(&pi->dst, dst)) ++ return s; ++ } ++ return NULL; ++} ++ ++void rfcomm_session_close(struct rfcomm_session *s, int err) ++{ ++ struct rfcomm_dlc *d; ++ struct list_head *p, *n; ++ ++ BT_DBG("session %p state %ld err %d", s, s->state, err); ++ ++ rfcomm_session_hold(s); ++ ++ s->state = BT_CLOSED; ++ ++ /* Close all dlcs */ ++ list_for_each_safe(p, n, &s->dlcs) { ++ d = list_entry(p, struct rfcomm_dlc, list); ++ d->state = BT_CLOSED; ++ __rfcomm_dlc_close(d, err); ++ } ++ ++ rfcomm_session_put(s); ++} ++ ++struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err) ++{ ++ struct rfcomm_session *s = NULL; ++ struct sockaddr_l2 addr; ++ struct l2cap_options opts; ++ struct socket *sock; ++ int size; ++ ++ BT_DBG("%s %s", batostr(src), batostr(dst)); ++ ++ *err = rfcomm_l2sock_create(&sock); ++ if (*err < 0) ++ return NULL; ++ ++ bacpy(&addr.l2_bdaddr, src); ++ addr.l2_family = AF_BLUETOOTH; ++ addr.l2_psm = 0; ++ *err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr)); ++ if (*err < 0) ++ goto failed; ++ ++ /* Set L2CAP options */ ++ size = sizeof(opts); ++ sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size); ++ ++ opts.imtu = RFCOMM_MAX_L2CAP_MTU; ++ sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size); ++ ++ s = rfcomm_session_add(sock, BT_BOUND); ++ if (!s) { ++ *err = -ENOMEM; ++ goto failed; ++ } ++ ++ s->initiator = 1; ++ ++ bacpy(&addr.l2_bdaddr, dst); ++ addr.l2_family = AF_BLUETOOTH; ++ addr.l2_psm = htobs(RFCOMM_PSM); ++ *err = sock->ops->connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK); ++ if (*err == 0 || *err == -EAGAIN) ++ return s; ++ ++ rfcomm_session_del(s); ++ return NULL; ++ ++failed: ++ sock_release(sock); ++ return NULL; ++} ++ ++void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst) ++{ ++ struct sock *sk = s->sock->sk; ++ if (src) ++ bacpy(src, &bluez_pi(sk)->src); ++ if (dst) ++ bacpy(dst, &bluez_pi(sk)->dst); ++} ++ ++/* ---- RFCOMM frame sending ---- */ ++static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len) ++{ ++ struct socket *sock = s->sock; ++ struct iovec iv = { data, len }; ++ struct msghdr msg; ++ int err; ++ ++ BT_DBG("session %p len %d", s, len); ++ ++ memset(&msg, 0, sizeof(msg)); ++ msg.msg_iovlen = 1; ++ msg.msg_iov = &iv; ++ ++ err = sock->ops->sendmsg(sock, &msg, len, 0); ++ return err; ++} ++ ++static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) ++{ ++ struct rfcomm_cmd cmd; ++ ++ BT_DBG("%p dlci %d", s, dlci); ++ ++ cmd.addr = __addr(s->initiator, dlci); ++ cmd.ctrl = __ctrl(RFCOMM_SABM, 1); ++ cmd.len = __len8(0); ++ cmd.fcs = __fcs2((u8 *) &cmd); ++ ++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); ++} ++ ++static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) ++{ ++ struct rfcomm_cmd cmd; ++ ++ BT_DBG("%p dlci %d", s, dlci); ++ ++ cmd.addr = __addr(!s->initiator, dlci); ++ cmd.ctrl = __ctrl(RFCOMM_UA, 1); ++ cmd.len = __len8(0); ++ cmd.fcs = __fcs2((u8 *) &cmd); ++ ++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); ++} ++ ++static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) ++{ ++ struct rfcomm_cmd cmd; ++ ++ BT_DBG("%p dlci %d", s, dlci); ++ ++ cmd.addr = __addr(s->initiator, dlci); ++ cmd.ctrl = __ctrl(RFCOMM_DISC, 1); ++ cmd.len = __len8(0); ++ cmd.fcs = __fcs2((u8 *) &cmd); ++ ++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); ++} ++ ++static int rfcomm_queue_disc(struct rfcomm_dlc *d) ++{ ++ struct rfcomm_cmd *cmd; ++ struct sk_buff *skb; ++ ++ BT_DBG("dlc %p dlci %d", d, d->dlci); ++ ++ skb = alloc_skb(sizeof(*cmd), GFP_KERNEL); ++ if (!skb) ++ return -ENOMEM; ++ ++ cmd = (void *) __skb_put(skb, sizeof(*cmd)); ++ cmd->addr = d->addr; ++ cmd->ctrl = __ctrl(RFCOMM_DISC, 1); ++ cmd->len = __len8(0); ++ cmd->fcs = __fcs2((u8 *) cmd); ++ ++ skb_queue_tail(&d->tx_queue, skb); ++ rfcomm_schedule(RFCOMM_SCHED_TX); ++ return 0; ++} ++ ++static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci) ++{ ++ struct rfcomm_cmd cmd; ++ ++ BT_DBG("%p dlci %d", s, dlci); ++ ++ cmd.addr = __addr(!s->initiator, dlci); ++ cmd.ctrl = __ctrl(RFCOMM_DM, 1); ++ cmd.len = __len8(0); ++ cmd.fcs = __fcs2((u8 *) &cmd); ++ ++ return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); ++} ++ ++static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) ++{ ++ struct rfcomm_hdr *hdr; ++ struct rfcomm_mcc *mcc; ++ u8 buf[16], *ptr = buf; ++ ++ BT_DBG("%p cr %d type %d", s, cr, type); ++ ++ hdr = (void *) ptr; ptr += sizeof(*hdr); ++ hdr->addr = __addr(s->initiator, 0); ++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0); ++ hdr->len = __len8(sizeof(*mcc) + 1); ++ ++ mcc = (void *) ptr; ptr += sizeof(*mcc); ++ mcc->type = __mcc_type(cr, RFCOMM_NSC); ++ mcc->len = __len8(1); ++ ++ /* Type that we didn't like */ ++ *ptr = __mcc_type(cr, type); ptr++; ++ ++ *ptr = __fcs(buf); ptr++; ++ ++ return rfcomm_send_frame(s, buf, ptr - buf); ++} ++ ++static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d) ++{ ++ struct rfcomm_hdr *hdr; ++ struct rfcomm_mcc *mcc; ++ struct rfcomm_pn *pn; ++ u8 buf[16], *ptr = buf; ++ ++ BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu); ++ ++ hdr = (void *) ptr; ptr += sizeof(*hdr); ++ hdr->addr = __addr(s->initiator, 0); ++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0); ++ hdr->len = __len8(sizeof(*mcc) + sizeof(*pn)); ++ ++ mcc = (void *) ptr; ptr += sizeof(*mcc); ++ mcc->type = __mcc_type(cr, RFCOMM_PN); ++ mcc->len = __len8(sizeof(*pn)); ++ ++ pn = (void *) ptr; ptr += sizeof(*pn); ++ pn->dlci = d->dlci; ++ pn->priority = d->priority; ++ pn->ack_timer = 0; ++ pn->max_retrans = 0; ++ ++ if (s->cfc) { ++ pn->flow_ctrl = cr ? 0xf0 : 0xe0; ++ pn->credits = RFCOMM_DEFAULT_CREDITS; ++ } else { ++ pn->flow_ctrl = 0; ++ pn->credits = 0; ++ } ++ ++ pn->mtu = htobs(d->mtu); ++ ++ *ptr = __fcs(buf); ptr++; ++ ++ return rfcomm_send_frame(s, buf, ptr - buf); ++} ++ ++static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, ++ u8 bit_rate, u8 data_bits, u8 stop_bits, ++ u8 parity, u8 flow_ctrl_settings, ++ u8 xon_char, u8 xoff_char, u16 param_mask) ++{ ++ struct rfcomm_hdr *hdr; ++ struct rfcomm_mcc *mcc; ++ struct rfcomm_rpn *rpn; ++ u8 buf[16], *ptr = buf; ++ ++ BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x" ++ "flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", ++ s, cr, dlci, bit_rate, data_bits, stop_bits, parity, ++ flow_ctrl_settings, xon_char, xoff_char, param_mask); ++ ++ hdr = (void *) ptr; ptr += sizeof(*hdr); ++ hdr->addr = __addr(s->initiator, 0); ++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0); ++ hdr->len = __len8(sizeof(*mcc) + sizeof(*rpn)); ++ ++ mcc = (void *) ptr; ptr += sizeof(*mcc); ++ mcc->type = __mcc_type(cr, RFCOMM_RPN); ++ mcc->len = __len8(sizeof(*rpn)); ++ ++ rpn = (void *) ptr; ptr += sizeof(*rpn); ++ rpn->dlci = __addr(1, dlci); ++ rpn->bit_rate = bit_rate; ++ rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity); ++ rpn->flow_ctrl = flow_ctrl_settings; ++ rpn->xon_char = xon_char; ++ rpn->xoff_char = xoff_char; ++ rpn->param_mask = param_mask; ++ ++ *ptr = __fcs(buf); ptr++; ++ ++ return rfcomm_send_frame(s, buf, ptr - buf); ++} ++ ++static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) ++{ ++ struct rfcomm_hdr *hdr; ++ struct rfcomm_mcc *mcc; ++ struct rfcomm_rls *rls; ++ u8 buf[16], *ptr = buf; ++ ++ BT_DBG("%p cr %d status 0x%x", s, cr, status); ++ ++ hdr = (void *) ptr; ptr += sizeof(*hdr); ++ hdr->addr = __addr(s->initiator, 0); ++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0); ++ hdr->len = __len8(sizeof(*mcc) + sizeof(*rls)); ++ ++ mcc = (void *) ptr; ptr += sizeof(*mcc); ++ mcc->type = __mcc_type(cr, RFCOMM_RLS); ++ mcc->len = __len8(sizeof(*rls)); ++ ++ rls = (void *) ptr; ptr += sizeof(*rls); ++ rls->dlci = __addr(1, dlci); ++ rls->status = status; ++ ++ *ptr = __fcs(buf); ptr++; ++ ++ return rfcomm_send_frame(s, buf, ptr - buf); ++} ++ ++static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig) ++{ ++ struct rfcomm_hdr *hdr; ++ struct rfcomm_mcc *mcc; ++ struct rfcomm_msc *msc; ++ u8 buf[16], *ptr = buf; ++ ++ BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig); ++ ++ hdr = (void *) ptr; ptr += sizeof(*hdr); ++ hdr->addr = __addr(s->initiator, 0); ++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0); ++ hdr->len = __len8(sizeof(*mcc) + sizeof(*msc)); ++ ++ mcc = (void *) ptr; ptr += sizeof(*mcc); ++ mcc->type = __mcc_type(cr, RFCOMM_MSC); ++ mcc->len = __len8(sizeof(*msc)); ++ ++ msc = (void *) ptr; ptr += sizeof(*msc); ++ msc->dlci = __addr(1, dlci); ++ msc->v24_sig = v24_sig | 0x01; ++ ++ *ptr = __fcs(buf); ptr++; ++ ++ return rfcomm_send_frame(s, buf, ptr - buf); ++} ++ ++static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr) ++{ ++ struct rfcomm_hdr *hdr; ++ struct rfcomm_mcc *mcc; ++ u8 buf[16], *ptr = buf; ++ ++ BT_DBG("%p cr %d", s, cr); ++ ++ hdr = (void *) ptr; ptr += sizeof(*hdr); ++ hdr->addr = __addr(s->initiator, 0); ++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0); ++ hdr->len = __len8(sizeof(*mcc)); ++ ++ mcc = (void *) ptr; ptr += sizeof(*mcc); ++ mcc->type = __mcc_type(cr, RFCOMM_FCOFF); ++ mcc->len = __len8(0); ++ ++ *ptr = __fcs(buf); ptr++; ++ ++ return rfcomm_send_frame(s, buf, ptr - buf); ++} ++ ++static int rfcomm_send_fcon(struct rfcomm_session *s, int cr) ++{ ++ struct rfcomm_hdr *hdr; ++ struct rfcomm_mcc *mcc; ++ u8 buf[16], *ptr = buf; ++ ++ BT_DBG("%p cr %d", s, cr); ++ ++ hdr = (void *) ptr; ptr += sizeof(*hdr); ++ hdr->addr = __addr(s->initiator, 0); ++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0); ++ hdr->len = __len8(sizeof(*mcc)); ++ ++ mcc = (void *) ptr; ptr += sizeof(*mcc); ++ mcc->type = __mcc_type(cr, RFCOMM_FCON); ++ mcc->len = __len8(0); ++ ++ *ptr = __fcs(buf); ptr++; ++ ++ return rfcomm_send_frame(s, buf, ptr - buf); ++} ++ ++static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len) ++{ ++ struct socket *sock = s->sock; ++ struct iovec iv[3]; ++ struct msghdr msg; ++ unsigned char hdr[5], crc[1]; ++ ++ if (len > 125) ++ return -EINVAL; ++ ++ BT_DBG("%p cr %d", s, cr); ++ ++ hdr[0] = __addr(s->initiator, 0); ++ hdr[1] = __ctrl(RFCOMM_UIH, 0); ++ hdr[2] = 0x01 | ((len + 2) << 1); ++ hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2); ++ hdr[4] = 0x01 | (len << 1); ++ ++ crc[0] = __fcs(hdr); ++ ++ iv[0].iov_base = hdr; ++ iv[0].iov_len = 5; ++ iv[1].iov_base = pattern; ++ iv[1].iov_len = len; ++ iv[2].iov_base = crc; ++ iv[2].iov_len = 1; ++ ++ memset(&msg, 0, sizeof(msg)); ++ msg.msg_iovlen = 3; ++ msg.msg_iov = iv; ++ return sock->ops->sendmsg(sock, &msg, 6 + len, 0); ++} ++ ++static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits) ++{ ++ struct rfcomm_hdr *hdr; ++ u8 buf[16], *ptr = buf; ++ ++ BT_DBG("%p addr %d credits %d", s, addr, credits); ++ ++ hdr = (void *) ptr; ptr += sizeof(*hdr); ++ hdr->addr = addr; ++ hdr->ctrl = __ctrl(RFCOMM_UIH, 1); ++ hdr->len = __len8(0); ++ ++ *ptr = credits; ptr++; ++ ++ *ptr = __fcs(buf); ptr++; ++ ++ return rfcomm_send_frame(s, buf, ptr - buf); ++} ++ ++static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) ++{ ++ struct rfcomm_hdr *hdr; ++ int len = skb->len; ++ u8 *crc; ++ ++ if (len > 127) { ++ hdr = (void *) skb_push(skb, 4); ++ put_unaligned(htobs(__len16(len)), (u16 *) &hdr->len); ++ } else { ++ hdr = (void *) skb_push(skb, 3); ++ hdr->len = __len8(len); ++ } ++ hdr->addr = addr; ++ hdr->ctrl = __ctrl(RFCOMM_UIH, 0); ++ ++ crc = skb_put(skb, 1); ++ *crc = __fcs((void *) hdr); ++} ++ ++/* ---- RFCOMM frame reception ---- */ ++static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) ++{ ++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); ++ ++ if (dlci) { ++ /* Data channel */ ++ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); ++ if (!d) { ++ rfcomm_send_dm(s, dlci); ++ return 0; ++ } ++ ++ switch (d->state) { ++ case BT_CONNECT: ++ rfcomm_dlc_clear_timer(d); ++ ++ rfcomm_dlc_lock(d); ++ d->state = BT_CONNECTED; ++ d->state_change(d, 0); ++ rfcomm_dlc_unlock(d); ++ ++ rfcomm_send_msc(s, 1, dlci, d->v24_sig); ++ break; ++ ++ case BT_DISCONN: ++ d->state = BT_CLOSED; ++ __rfcomm_dlc_close(d, 0); ++ break; ++ } ++ } else { ++ /* Control channel */ ++ switch (s->state) { ++ case BT_CONNECT: ++ s->state = BT_CONNECTED; ++ rfcomm_process_connect(s); ++ break; ++ } ++ } ++ return 0; ++} ++ ++static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) ++{ ++ int err = 0; ++ ++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); ++ ++ if (dlci) { ++ /* Data DLC */ ++ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); ++ if (d) { ++ if (d->state == BT_CONNECT || d->state == BT_CONFIG) ++ err = ECONNREFUSED; ++ else ++ err = ECONNRESET; ++ ++ d->state = BT_CLOSED; ++ __rfcomm_dlc_close(d, err); ++ } ++ } else { ++ if (s->state == BT_CONNECT) ++ err = ECONNREFUSED; ++ else ++ err = ECONNRESET; ++ ++ s->state = BT_CLOSED; ++ rfcomm_session_close(s, err); ++ } ++ return 0; ++} ++ ++static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) ++{ ++ int err = 0; ++ ++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); ++ ++ if (dlci) { ++ struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); ++ if (d) { ++ rfcomm_send_ua(s, dlci); ++ ++ if (d->state == BT_CONNECT || d->state == BT_CONFIG) ++ err = ECONNREFUSED; ++ else ++ err = ECONNRESET; ++ ++ d->state = BT_CLOSED; ++ __rfcomm_dlc_close(d, err); ++ } else ++ rfcomm_send_dm(s, dlci); ++ ++ } else { ++ rfcomm_send_ua(s, 0); ++ ++ if (s->state == BT_CONNECT) ++ err = ECONNREFUSED; ++ else ++ err = ECONNRESET; ++ ++ s->state = BT_CLOSED; ++ rfcomm_session_close(s, err); ++ } ++ ++ return 0; ++} ++ ++static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) ++{ ++ struct rfcomm_dlc *d; ++ u8 channel; ++ ++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); ++ ++ if (!dlci) { ++ rfcomm_send_ua(s, 0); ++ ++ if (s->state == BT_OPEN) { ++ s->state = BT_CONNECTED; ++ rfcomm_process_connect(s); ++ } ++ return 0; ++ } ++ ++ /* Check if DLC exists */ ++ d = rfcomm_dlc_get(s, dlci); ++ if (d) { ++ if (d->state == BT_OPEN) { ++ /* DLC was previously opened by PN request */ ++ rfcomm_send_ua(s, dlci); ++ ++ rfcomm_dlc_lock(d); ++ d->state = BT_CONNECTED; ++ d->state_change(d, 0); ++ rfcomm_dlc_unlock(d); ++ ++ rfcomm_send_msc(s, 1, dlci, d->v24_sig); ++ } ++ return 0; ++ } ++ ++ /* Notify socket layer about incomming connection */ ++ channel = __srv_channel(dlci); ++ if (rfcomm_connect_ind(s, channel, &d)) { ++ d->dlci = dlci; ++ d->addr = __addr(s->initiator, dlci); ++ rfcomm_dlc_link(s, d); ++ ++ rfcomm_send_ua(s, dlci); ++ ++ rfcomm_dlc_lock(d); ++ d->state = BT_CONNECTED; ++ d->state_change(d, 0); ++ rfcomm_dlc_unlock(d); ++ ++ rfcomm_send_msc(s, 1, dlci, d->v24_sig); ++ } else { ++ rfcomm_send_dm(s, dlci); ++ } ++ ++ return 0; ++} ++ ++static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn) ++{ ++ struct rfcomm_session *s = d->session; ++ ++ BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", ++ d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits); ++ ++ if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) { ++ d->cfc = s->cfc = RFCOMM_CFC_ENABLED; ++ d->tx_credits = pn->credits; ++ } else { ++ d->cfc = s->cfc = RFCOMM_CFC_DISABLED; ++ set_bit(RFCOMM_TX_THROTTLED, &d->flags); ++ } ++ ++ d->priority = pn->priority; ++ ++ d->mtu = s->mtu = btohs(pn->mtu); ++ ++ return 0; ++} ++ ++static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb) ++{ ++ struct rfcomm_pn *pn = (void *) skb->data; ++ struct rfcomm_dlc *d; ++ u8 dlci = pn->dlci; ++ ++ BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); ++ ++ if (!dlci) ++ return 0; ++ ++ d = rfcomm_dlc_get(s, dlci); ++ if (d) { ++ if (cr) { ++ /* PN request */ ++ rfcomm_apply_pn(d, cr, pn); ++ rfcomm_send_pn(s, 0, d); ++ } else { ++ /* PN response */ ++ switch (d->state) { ++ case BT_CONFIG: ++ rfcomm_apply_pn(d, cr, pn); ++ ++ d->state = BT_CONNECT; ++ rfcomm_send_sabm(s, d->dlci); ++ break; ++ } ++ } ++ } else { ++ u8 channel = __srv_channel(dlci); ++ ++ if (!cr) ++ return 0; ++ ++ /* PN request for non existing DLC. ++ * Assume incomming connection. */ ++ if (rfcomm_connect_ind(s, channel, &d)) { ++ d->dlci = dlci; ++ d->addr = __addr(s->initiator, dlci); ++ rfcomm_dlc_link(s, d); ++ ++ rfcomm_apply_pn(d, cr, pn); ++ ++ d->state = BT_OPEN; ++ rfcomm_send_pn(s, 0, d); ++ } else { ++ rfcomm_send_dm(s, dlci); ++ } ++ } ++ return 0; ++} ++ ++static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb) ++{ ++ struct rfcomm_rpn *rpn = (void *) skb->data; ++ u8 dlci = __get_dlci(rpn->dlci); ++ ++ u8 bit_rate = 0; ++ u8 data_bits = 0; ++ u8 stop_bits = 0; ++ u8 parity = 0; ++ u8 flow_ctrl = 0; ++ u8 xon_char = 0; ++ u8 xoff_char = 0; ++ u16 rpn_mask = RFCOMM_RPN_PM_ALL; ++ ++ BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", ++ dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl, ++ rpn->xon_char, rpn->xoff_char, rpn->param_mask); ++ ++ if (!cr) ++ return 0; ++ ++ if (len == 1) { ++ /* request: return default setting */ ++ bit_rate = RFCOMM_RPN_BR_115200; ++ data_bits = RFCOMM_RPN_DATA_8; ++ stop_bits = RFCOMM_RPN_STOP_1; ++ parity = RFCOMM_RPN_PARITY_NONE; ++ flow_ctrl = RFCOMM_RPN_FLOW_NONE; ++ xon_char = RFCOMM_RPN_XON_CHAR; ++ xoff_char = RFCOMM_RPN_XOFF_CHAR; ++ ++ goto rpn_out; ++ } ++ /* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity, ++ no flow control lines, normal XON/XOFF chars */ ++ if (rpn->param_mask & RFCOMM_RPN_PM_BITRATE) { ++ bit_rate = rpn->bit_rate; ++ if (bit_rate != RFCOMM_RPN_BR_115200) { ++ BT_DBG("RPN bit rate mismatch 0x%x", bit_rate); ++ bit_rate = RFCOMM_RPN_BR_115200; ++ rpn_mask ^= RFCOMM_RPN_PM_BITRATE; ++ } ++ } ++ if (rpn->param_mask & RFCOMM_RPN_PM_DATA) { ++ data_bits = __get_rpn_data_bits(rpn->line_settings); ++ if (data_bits != RFCOMM_RPN_DATA_8) { ++ BT_DBG("RPN data bits mismatch 0x%x", data_bits); ++ data_bits = RFCOMM_RPN_DATA_8; ++ rpn_mask ^= RFCOMM_RPN_PM_DATA; ++ } ++ } ++ if (rpn->param_mask & RFCOMM_RPN_PM_STOP) { ++ stop_bits = __get_rpn_stop_bits(rpn->line_settings); ++ if (stop_bits != RFCOMM_RPN_STOP_1) { ++ BT_DBG("RPN stop bits mismatch 0x%x", stop_bits); ++ stop_bits = RFCOMM_RPN_STOP_1; ++ rpn_mask ^= RFCOMM_RPN_PM_STOP; ++ } ++ } ++ if (rpn->param_mask & RFCOMM_RPN_PM_PARITY) { ++ parity = __get_rpn_parity(rpn->line_settings); ++ if (parity != RFCOMM_RPN_PARITY_NONE) { ++ BT_DBG("RPN parity mismatch 0x%x", parity); ++ parity = RFCOMM_RPN_PARITY_NONE; ++ rpn_mask ^= RFCOMM_RPN_PM_PARITY; ++ } ++ } ++ if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) { ++ flow_ctrl = rpn->flow_ctrl; ++ if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) { ++ BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl); ++ flow_ctrl = RFCOMM_RPN_FLOW_NONE; ++ rpn_mask ^= RFCOMM_RPN_PM_FLOW; ++ } ++ } ++ if (rpn->param_mask & RFCOMM_RPN_PM_XON) { ++ xon_char = rpn->xon_char; ++ if (xon_char != RFCOMM_RPN_XON_CHAR) { ++ BT_DBG("RPN XON char mismatch 0x%x", xon_char); ++ xon_char = RFCOMM_RPN_XON_CHAR; ++ rpn_mask ^= RFCOMM_RPN_PM_XON; ++ } ++ } ++ if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) { ++ xoff_char = rpn->xoff_char; ++ if (xoff_char != RFCOMM_RPN_XOFF_CHAR) { ++ BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char); ++ xoff_char = RFCOMM_RPN_XOFF_CHAR; ++ rpn_mask ^= RFCOMM_RPN_PM_XOFF; ++ } ++ } ++ ++rpn_out: ++ rfcomm_send_rpn(s, 0, dlci, ++ bit_rate, data_bits, stop_bits, parity, flow_ctrl, ++ xon_char, xoff_char, rpn_mask); ++ ++ return 0; ++} ++ ++static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb) ++{ ++ struct rfcomm_rls *rls = (void *) skb->data; ++ u8 dlci = __get_dlci(rls->dlci); ++ ++ BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status); ++ ++ if (!cr) ++ return 0; ++ ++ /* FIXME: We should probably do something with this ++ information here. But for now it's sufficient just ++ to reply -- Bluetooth 1.1 says it's mandatory to ++ recognise and respond to RLS */ ++ ++ rfcomm_send_rls(s, 0, dlci, rls->status); ++ ++ return 0; ++} ++ ++static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb) ++{ ++ struct rfcomm_msc *msc = (void *) skb->data; ++ struct rfcomm_dlc *d; ++ u8 dlci = __get_dlci(msc->dlci); ++ ++ BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig); ++ ++ d = rfcomm_dlc_get(s, dlci); ++ if (!d) ++ return 0; ++ ++ if (cr) { ++ if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc) ++ set_bit(RFCOMM_TX_THROTTLED, &d->flags); ++ else ++ clear_bit(RFCOMM_TX_THROTTLED, &d->flags); ++ ++ rfcomm_dlc_lock(d); ++ if (d->modem_status) ++ d->modem_status(d, msc->v24_sig); ++ rfcomm_dlc_unlock(d); ++ ++ rfcomm_send_msc(s, 0, dlci, msc->v24_sig); ++ ++ d->mscex |= RFCOMM_MSCEX_RX; ++ } else ++ d->mscex |= RFCOMM_MSCEX_TX; ++ ++ return 0; ++} ++ ++static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb) ++{ ++ struct rfcomm_mcc *mcc = (void *) skb->data; ++ u8 type, cr, len; ++ ++ cr = __test_cr(mcc->type); ++ type = __get_mcc_type(mcc->type); ++ len = __get_mcc_len(mcc->len); ++ ++ BT_DBG("%p type 0x%x cr %d", s, type, cr); ++ ++ skb_pull(skb, 2); ++ ++ switch (type) { ++ case RFCOMM_PN: ++ rfcomm_recv_pn(s, cr, skb); ++ break; ++ ++ case RFCOMM_RPN: ++ rfcomm_recv_rpn(s, cr, len, skb); ++ break; ++ ++ case RFCOMM_RLS: ++ rfcomm_recv_rls(s, cr, skb); ++ break; ++ ++ case RFCOMM_MSC: ++ rfcomm_recv_msc(s, cr, skb); ++ break; ++ ++ case RFCOMM_FCOFF: ++ if (cr) { ++ set_bit(RFCOMM_TX_THROTTLED, &s->flags); ++ rfcomm_send_fcoff(s, 0); ++ } ++ break; ++ ++ case RFCOMM_FCON: ++ if (cr) { ++ clear_bit(RFCOMM_TX_THROTTLED, &s->flags); ++ rfcomm_send_fcon(s, 0); ++ } ++ break; ++ ++ case RFCOMM_TEST: ++ if (cr) ++ rfcomm_send_test(s, 0, skb->data, skb->len); ++ break; ++ ++ case RFCOMM_NSC: ++ break; ++ ++ default: ++ BT_ERR("Unknown control type 0x%02x", type); ++ rfcomm_send_nsc(s, cr, type); ++ break; ++ } ++ return 0; ++} ++ ++static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk_buff *skb) ++{ ++ struct rfcomm_dlc *d; ++ ++ BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf); ++ ++ d = rfcomm_dlc_get(s, dlci); ++ if (!d) { ++ rfcomm_send_dm(s, dlci); ++ goto drop; ++ } ++ ++ if (pf && d->cfc) { ++ u8 credits = *(u8 *) skb->data; skb_pull(skb, 1); ++ ++ d->tx_credits += credits; ++ if (d->tx_credits) ++ clear_bit(RFCOMM_TX_THROTTLED, &d->flags); ++ } ++ ++ if (skb->len && d->state == BT_CONNECTED) { ++ rfcomm_dlc_lock(d); ++ d->rx_credits--; ++ d->data_ready(d, skb); ++ rfcomm_dlc_unlock(d); ++ return 0; ++ } ++ ++drop: ++ kfree_skb(skb); ++ return 0; ++} ++ ++static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) ++{ ++ struct rfcomm_hdr *hdr = (void *) skb->data; ++ u8 type, dlci, fcs; ++ ++ dlci = __get_dlci(hdr->addr); ++ type = __get_type(hdr->ctrl); ++ ++ /* Trim FCS */ ++ skb->len--; skb->tail--; ++ fcs = *(u8 *) skb->tail; ++ ++ if (__check_fcs(skb->data, type, fcs)) { ++ BT_ERR("bad checksum in packet"); ++ kfree_skb(skb); ++ return -EILSEQ; ++ } ++ ++ if (__test_ea(hdr->len)) ++ skb_pull(skb, 3); ++ else ++ skb_pull(skb, 4); ++ ++ switch (type) { ++ case RFCOMM_SABM: ++ if (__test_pf(hdr->ctrl)) ++ rfcomm_recv_sabm(s, dlci); ++ break; ++ ++ case RFCOMM_DISC: ++ if (__test_pf(hdr->ctrl)) ++ rfcomm_recv_disc(s, dlci); ++ break; ++ ++ case RFCOMM_UA: ++ if (__test_pf(hdr->ctrl)) ++ rfcomm_recv_ua(s, dlci); ++ break; ++ ++ case RFCOMM_DM: ++ rfcomm_recv_dm(s, dlci); ++ break; ++ ++ case RFCOMM_UIH: ++ if (dlci) ++ return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb); ++ ++ rfcomm_recv_mcc(s, skb); ++ break; ++ ++ default: ++ BT_ERR("Unknown packet type 0x%02x\n", type); ++ break; ++ } ++ kfree_skb(skb); ++ return 0; ++} ++ ++/* ---- Connection and data processing ---- */ ++ ++static void rfcomm_process_connect(struct rfcomm_session *s) ++{ ++ struct rfcomm_dlc *d; ++ struct list_head *p, *n; ++ ++ BT_DBG("session %p state %ld", s, s->state); ++ ++ list_for_each_safe(p, n, &s->dlcs) { ++ d = list_entry(p, struct rfcomm_dlc, list); ++ if (d->state == BT_CONFIG) { ++ d->mtu = s->mtu; ++ rfcomm_send_pn(s, 1, d); ++ } ++ } ++} ++ ++/* Send data queued for the DLC. ++ * Return number of frames left in the queue. ++ */ ++static inline int rfcomm_process_tx(struct rfcomm_dlc *d) ++{ ++ struct sk_buff *skb; ++ int err; ++ ++ BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d", ++ d, d->state, d->cfc, d->rx_credits, d->tx_credits); ++ ++ /* Send pending MSC */ ++ if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags)) ++ rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); ++ ++ if (d->cfc) { ++ /* CFC enabled. ++ * Give them some credits */ ++ if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) && ++ d->rx_credits <= (d->cfc >> 2)) { ++ rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits); ++ d->rx_credits = d->cfc; ++ } ++ } else { ++ /* CFC disabled. ++ * Give ourselves some credits */ ++ d->tx_credits = 5; ++ } ++ ++ if (test_bit(RFCOMM_TX_THROTTLED, &d->flags)) ++ return skb_queue_len(&d->tx_queue); ++ ++ while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) { ++ err = rfcomm_send_frame(d->session, skb->data, skb->len); ++ if (err < 0) { ++ skb_queue_head(&d->tx_queue, skb); ++ break; ++ } ++ kfree_skb(skb); ++ d->tx_credits--; ++ } ++ ++ if (d->cfc && !d->tx_credits) { ++ /* We're out of TX credits. ++ * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */ ++ set_bit(RFCOMM_TX_THROTTLED, &d->flags); ++ } ++ ++ return skb_queue_len(&d->tx_queue); ++} ++ ++static inline void rfcomm_process_dlcs(struct rfcomm_session *s) ++{ ++ struct rfcomm_dlc *d; ++ struct list_head *p, *n; ++ ++ BT_DBG("session %p state %ld", s, s->state); ++ ++ list_for_each_safe(p, n, &s->dlcs) { ++ d = list_entry(p, struct rfcomm_dlc, list); ++ if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) { ++ __rfcomm_dlc_close(d, ETIMEDOUT); ++ continue; ++ } ++ ++ if (test_bit(RFCOMM_TX_THROTTLED, &s->flags)) ++ continue; ++ ++ if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) && ++ d->mscex == RFCOMM_MSCEX_OK) ++ rfcomm_process_tx(d); ++ } ++} ++ ++static inline void rfcomm_process_rx(struct rfcomm_session *s) ++{ ++ struct socket *sock = s->sock; ++ struct sock *sk = sock->sk; ++ struct sk_buff *skb; ++ ++ BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->receive_queue)); ++ ++ /* Get data directly from socket receive queue without copying it. */ ++ while ((skb = skb_dequeue(&sk->receive_queue))) { ++ skb_orphan(skb); ++ rfcomm_recv_frame(s, skb); ++ } ++ ++ if (sk->state == BT_CLOSED) { ++ if (!s->initiator) ++ rfcomm_session_put(s); ++ ++ rfcomm_session_close(s, sk->err); ++ } ++} ++ ++static inline void rfcomm_accept_connection(struct rfcomm_session *s) ++{ ++ struct socket *sock = s->sock, *nsock; ++ int err; ++ ++ /* Fast check for a new connection. ++ * Avoids unnesesary socket allocations. */ ++ if (list_empty(&bluez_pi(sock->sk)->accept_q)) ++ return; ++ ++ BT_DBG("session %p", s); ++ ++ nsock = sock_alloc(); ++ if (!nsock) ++ return; ++ ++ nsock->type = sock->type; ++ nsock->ops = sock->ops; ++ ++ err = sock->ops->accept(sock, nsock, O_NONBLOCK); ++ if (err < 0) { ++ sock_release(nsock); ++ return; ++ } ++ ++ /* Set our callbacks */ ++ nsock->sk->data_ready = rfcomm_l2data_ready; ++ nsock->sk->state_change = rfcomm_l2state_change; ++ ++ s = rfcomm_session_add(nsock, BT_OPEN); ++ if (s) { ++ rfcomm_session_hold(s); ++ rfcomm_schedule(RFCOMM_SCHED_RX); ++ } else ++ sock_release(nsock); ++} ++ ++static inline void rfcomm_check_connection(struct rfcomm_session *s) ++{ ++ struct sock *sk = s->sock->sk; ++ ++ BT_DBG("%p state %ld", s, s->state); ++ ++ switch(sk->state) { ++ case BT_CONNECTED: ++ s->state = BT_CONNECT; ++ ++ /* We can adjust MTU on outgoing sessions. ++ * L2CAP MTU minus UIH header and FCS. */ ++ s->mtu = min(l2cap_pi(sk)->omtu, l2cap_pi(sk)->imtu) - 5; ++ ++ rfcomm_send_sabm(s, 0); ++ break; ++ ++ case BT_CLOSED: ++ s->state = BT_CLOSED; ++ rfcomm_session_close(s, sk->err); ++ break; ++ } ++} ++ ++static inline void rfcomm_process_sessions(void) ++{ ++ struct list_head *p, *n; ++ ++ rfcomm_lock(); ++ ++ list_for_each_safe(p, n, &session_list) { ++ struct rfcomm_session *s; ++ s = list_entry(p, struct rfcomm_session, list); ++ ++ if (s->state == BT_LISTEN) { ++ rfcomm_accept_connection(s); ++ continue; ++ } ++ ++ rfcomm_session_hold(s); ++ ++ switch (s->state) { ++ case BT_BOUND: ++ rfcomm_check_connection(s); ++ break; ++ ++ default: ++ rfcomm_process_rx(s); ++ break; ++ } ++ ++ rfcomm_process_dlcs(s); ++ ++ rfcomm_session_put(s); ++ } ++ ++ rfcomm_unlock(); ++} ++ ++static void rfcomm_worker(void) ++{ ++ BT_DBG(""); ++ ++ daemonize(); reparent_to_init(); ++ set_fs(KERNEL_DS); ++ ++ while (!atomic_read(&terminate)) { ++ BT_DBG("worker loop event 0x%lx", rfcomm_event); ++ ++ if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) { ++ /* No pending events. Let's sleep. ++ * Incomming connections and data will wake us up. */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule(); ++ } ++ ++ /* Process stuff */ ++ clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event); ++ rfcomm_process_sessions(); ++ } ++ set_current_state(TASK_RUNNING); ++ return; ++} ++ ++static int rfcomm_add_listener(bdaddr_t *ba) ++{ ++ struct sockaddr_l2 addr; ++ struct l2cap_options opts; ++ struct socket *sock; ++ struct rfcomm_session *s; ++ int size, err = 0; ++ ++ /* Create socket */ ++ err = rfcomm_l2sock_create(&sock); ++ if (err < 0) { ++ BT_ERR("Create socket failed %d", err); ++ return err; ++ } ++ ++ /* Bind socket */ ++ bacpy(&addr.l2_bdaddr, ba); ++ addr.l2_family = AF_BLUETOOTH; ++ addr.l2_psm = htobs(RFCOMM_PSM); ++ err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr)); ++ if (err < 0) { ++ BT_ERR("Bind failed %d", err); ++ goto failed; ++ } ++ ++ /* Set L2CAP options */ ++ size = sizeof(opts); ++ sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size); ++ ++ opts.imtu = RFCOMM_MAX_L2CAP_MTU; ++ sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size); ++ ++ /* Start listening on the socket */ ++ err = sock->ops->listen(sock, 10); ++ if (err) { ++ BT_ERR("Listen failed %d", err); ++ goto failed; ++ } ++ ++ /* Add listening session */ ++ s = rfcomm_session_add(sock, BT_LISTEN); ++ if (!s) ++ goto failed; ++ ++ rfcomm_session_hold(s); ++ return 0; ++failed: ++ sock_release(sock); ++ return err; ++} ++ ++static void rfcomm_kill_listener(void) ++{ ++ struct rfcomm_session *s; ++ struct list_head *p, *n; ++ ++ BT_DBG(""); ++ ++ list_for_each_safe(p, n, &session_list) { ++ s = list_entry(p, struct rfcomm_session, list); ++ rfcomm_session_del(s); ++ } ++} ++ ++static int rfcomm_run(void *unused) ++{ ++ rfcomm_thread = current; ++ ++ atomic_inc(&running); ++ ++ daemonize(); reparent_to_init(); ++ ++ sigfillset(¤t->blocked); ++ set_fs(KERNEL_DS); ++ ++ sprintf(current->comm, "krfcommd"); ++ ++ BT_DBG(""); ++ ++ rfcomm_add_listener(BDADDR_ANY); ++ ++ rfcomm_worker(); ++ ++ rfcomm_kill_listener(); ++ ++ atomic_dec(&running); ++ return 0; ++} ++ ++/* ---- Proc fs support ---- */ ++static int rfcomm_dlc_dump(char *buf) ++{ ++ struct rfcomm_session *s; ++ struct sock *sk; ++ struct list_head *p, *pp; ++ char *ptr = buf; ++ ++ rfcomm_lock(); ++ ++ list_for_each(p, &session_list) { ++ s = list_entry(p, struct rfcomm_session, list); ++ sk = s->sock->sk; ++ ++ list_for_each(pp, &s->dlcs) { ++ struct rfcomm_dlc *d; ++ d = list_entry(pp, struct rfcomm_dlc, list); ++ ++ ptr += sprintf(ptr, "dlc %s %s %ld %d %d %d %d\n", ++ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst), ++ d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits); ++ } ++ } ++ ++ rfcomm_unlock(); ++ ++ return ptr - buf; ++} ++ ++extern int rfcomm_sock_dump(char *buf); ++ ++static int rfcomm_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv) ++{ ++ char *ptr = buf; ++ int len; ++ ++ BT_DBG("count %d, offset %ld", count, offset); ++ ++ ptr += rfcomm_dlc_dump(ptr); ++ ptr += rfcomm_sock_dump(ptr); ++ len = ptr - buf; ++ ++ if (len <= count + offset) ++ *eof = 1; ++ ++ *start = buf + offset; ++ len -= offset; ++ ++ if (len > count) ++ len = count; ++ if (len < 0) ++ len = 0; ++ ++ return len; ++} ++ ++/* ---- Initialization ---- */ ++int __init rfcomm_init(void) ++{ ++ l2cap_load(); ++ ++ kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); ++ ++ rfcomm_init_sockets(); ++ ++#ifdef CONFIG_BLUEZ_RFCOMM_TTY ++ rfcomm_init_ttys(); ++#endif ++ ++ create_proc_read_entry("bluetooth/rfcomm", 0, 0, rfcomm_read_proc, NULL); ++ ++ BT_INFO("BlueZ RFCOMM ver %s", VERSION); ++ BT_INFO("Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>"); ++ BT_INFO("Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>"); ++ return 0; ++} ++ ++void rfcomm_cleanup(void) ++{ ++ /* Terminate working thread. ++ * ie. Set terminate flag and wake it up */ ++ atomic_inc(&terminate); ++ rfcomm_schedule(RFCOMM_SCHED_STATE); ++ ++ /* Wait until thread is running */ ++ while (atomic_read(&running)) ++ schedule(); ++ ++ remove_proc_entry("bluetooth/rfcomm", NULL); ++ ++#ifdef CONFIG_BLUEZ_RFCOMM_TTY ++ rfcomm_cleanup_ttys(); ++#endif ++ ++ rfcomm_cleanup_sockets(); ++ return; ++} ++ ++module_init(rfcomm_init); ++module_exit(rfcomm_cleanup); ++ ++MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>"); ++MODULE_DESCRIPTION("BlueZ RFCOMM ver " VERSION); ++MODULE_LICENSE("GPL"); +diff -Nur c3000_pre/linux/net/bluetooth/rfcomm/crc.c c3000_work/linux/net/bluetooth/rfcomm/crc.c +--- c3000_pre/linux/net/bluetooth/rfcomm/crc.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/rfcomm/crc.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,71 @@ ++/* ++ RFCOMM implementation for Linux Bluetooth stack (BlueZ). ++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> ++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++/* ++ * RFCOMM FCS calculation. ++ * ++ * $Id: crc.c,v 1.2 2002/09/21 09:54:32 holtmann Exp $ ++ */ ++ ++/* reversed, 8-bit, poly=0x07 */ ++unsigned char rfcomm_crc_table[256] = { ++ 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, ++ 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, ++ 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, ++ 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, ++ ++ 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, ++ 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, ++ 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, ++ 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, ++ ++ 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, ++ 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, ++ 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, ++ 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, ++ ++ 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, ++ 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, ++ 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, ++ 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, ++ ++ 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, ++ 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, ++ 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, ++ 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, ++ ++ 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, ++ 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, ++ 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, ++ 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, ++ ++ 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, ++ 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, ++ 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, ++ 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, ++ ++ 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, ++ 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, ++ 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, ++ 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf ++}; +diff -Nur c3000_pre/linux/net/bluetooth/rfcomm/sock.c c3000_work/linux/net/bluetooth/rfcomm/sock.c +--- c3000_pre/linux/net/bluetooth/rfcomm/sock.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/rfcomm/sock.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,847 @@ ++/* ++ RFCOMM implementation for Linux Bluetooth stack (BlueZ). ++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> ++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++/* ++ * RFCOMM sockets. ++ * ++ * $Id: sock.c,v 1.30 2002/10/18 20:12:12 maxk Exp $ ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/kernel.h> ++#include <linux/major.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/poll.h> ++#include <linux/fcntl.h> ++#include <linux/init.h> ++#include <linux/skbuff.h> ++#include <linux/interrupt.h> ++#include <linux/socket.h> ++#include <linux/skbuff.h> ++#include <linux/list.h> ++#include <net/sock.h> ++ ++#include <asm/system.h> ++#include <asm/uaccess.h> ++ ++#include <net/bluetooth/bluetooth.h> ++#include <net/bluetooth/rfcomm.h> ++ ++#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG ++#undef BT_DBG ++#define BT_DBG(D...) ++#endif ++ ++static struct proto_ops rfcomm_sock_ops; ++ ++static struct bluez_sock_list rfcomm_sk_list = { ++ lock: RW_LOCK_UNLOCKED ++}; ++ ++static void rfcomm_sock_close(struct sock *sk); ++static void rfcomm_sock_kill(struct sock *sk); ++ ++/* ---- DLC callbacks ---- ++ * ++ * called under rfcomm_dlc_lock() ++ */ ++static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb) ++{ ++ struct sock *sk = d->owner; ++ if (!sk) ++ return; ++ ++ atomic_add(skb->len, &sk->rmem_alloc); ++ skb_queue_tail(&sk->receive_queue, skb); ++ sk->data_ready(sk, skb->len); ++ ++ if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) ++ rfcomm_dlc_throttle(d); ++} ++ ++static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err) ++{ ++ struct sock *sk = d->owner, *parent; ++ if (!sk) ++ return; ++ ++ BT_DBG("dlc %p state %ld err %d", d, d->state, err); ++ ++ bh_lock_sock(sk); ++ ++ if (err) ++ sk->err = err; ++ sk->state = d->state; ++ ++ parent = bluez_pi(sk)->parent; ++ if (!parent) { ++ if (d->state == BT_CONNECTED) ++ rfcomm_session_getaddr(d->session, &bluez_pi(sk)->src, NULL); ++ sk->state_change(sk); ++ } else ++ parent->data_ready(parent, 0); ++ ++ bh_unlock_sock(sk); ++} ++ ++/* ---- Socket functions ---- */ ++static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src) ++{ ++ struct sock *sk; ++ ++ for (sk = rfcomm_sk_list.head; sk; sk = sk->next) { ++ if (rfcomm_pi(sk)->channel == channel && ++ !bacmp(&bluez_pi(sk)->src, src)) ++ break; ++ } ++ ++ return sk; ++} ++ ++/* Find socket with channel and source bdaddr. ++ * Returns closest match. ++ */ ++static struct sock *__rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src) ++{ ++ struct sock *sk, *sk1 = NULL; ++ ++ for (sk = rfcomm_sk_list.head; sk; sk = sk->next) { ++ if (state && sk->state != state) ++ continue; ++ ++ if (rfcomm_pi(sk)->channel == channel) { ++ /* Exact match. */ ++ if (!bacmp(&bluez_pi(sk)->src, src)) ++ break; ++ ++ /* Closest match */ ++ if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY)) ++ sk1 = sk; ++ } ++ } ++ return sk ? sk : sk1; ++} ++ ++/* Find socket with given address (channel, src). ++ * Returns locked socket */ ++static inline struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src) ++{ ++ struct sock *s; ++ read_lock(&rfcomm_sk_list.lock); ++ s = __rfcomm_get_sock_by_channel(state, channel, src); ++ if (s) bh_lock_sock(s); ++ read_unlock(&rfcomm_sk_list.lock); ++ return s; ++} ++ ++static void rfcomm_sock_destruct(struct sock *sk) ++{ ++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; ++ ++ BT_DBG("sk %p dlc %p", sk, d); ++ ++ skb_queue_purge(&sk->receive_queue); ++ skb_queue_purge(&sk->write_queue); ++ ++ rfcomm_dlc_lock(d); ++ rfcomm_pi(sk)->dlc = NULL; ++ ++ /* Detach DLC if it's owned by this socket */ ++ if (d->owner == sk) ++ d->owner = NULL; ++ rfcomm_dlc_unlock(d); ++ ++ rfcomm_dlc_put(d); ++ ++ MOD_DEC_USE_COUNT; ++} ++ ++static void rfcomm_sock_cleanup_listen(struct sock *parent) ++{ ++ struct sock *sk; ++ ++ BT_DBG("parent %p", parent); ++ ++ /* Close not yet accepted dlcs */ ++ while ((sk = bluez_accept_dequeue(parent, NULL))) { ++ rfcomm_sock_close(sk); ++ rfcomm_sock_kill(sk); ++ } ++ ++ parent->state = BT_CLOSED; ++ parent->zapped = 1; ++} ++ ++/* Kill socket (only if zapped and orphan) ++ * Must be called on unlocked socket. ++ */ ++static void rfcomm_sock_kill(struct sock *sk) ++{ ++ if (!sk->zapped || sk->socket) ++ return; ++ ++ BT_DBG("sk %p state %d refcnt %d", sk, sk->state, atomic_read(&sk->refcnt)); ++ ++ /* Kill poor orphan */ ++ bluez_sock_unlink(&rfcomm_sk_list, sk); ++ sk->dead = 1; ++ sock_put(sk); ++} ++ ++static void __rfcomm_sock_close(struct sock *sk) ++{ ++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; ++ ++ BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket); ++ ++ switch (sk->state) { ++ case BT_LISTEN: ++ rfcomm_sock_cleanup_listen(sk); ++ break; ++ ++ case BT_CONNECT: ++ case BT_CONNECT2: ++ case BT_CONFIG: ++ case BT_CONNECTED: ++ rfcomm_dlc_close(d, 0); ++ ++ default: ++ sk->zapped = 1; ++ break; ++ } ++} ++ ++/* Close socket. ++ * Must be called on unlocked socket. ++ */ ++static void rfcomm_sock_close(struct sock *sk) ++{ ++ lock_sock(sk); ++ __rfcomm_sock_close(sk); ++ release_sock(sk); ++} ++ ++static void rfcomm_sock_init(struct sock *sk, struct sock *parent) ++{ ++ BT_DBG("sk %p", sk); ++ ++ if (parent) ++ sk->type = parent->type; ++} ++ ++static struct sock *rfcomm_sock_alloc(struct socket *sock, int proto, int prio) ++{ ++ struct rfcomm_dlc *d; ++ struct sock *sk; ++ ++ sk = sk_alloc(PF_BLUETOOTH, prio, 1); ++ if (!sk) ++ return NULL; ++ ++ d = rfcomm_dlc_alloc(prio); ++ if (!d) { ++ sk_free(sk); ++ return NULL; ++ } ++ d->data_ready = rfcomm_sk_data_ready; ++ d->state_change = rfcomm_sk_state_change; ++ ++ rfcomm_pi(sk)->dlc = d; ++ d->owner = sk; ++ ++ bluez_sock_init(sock, sk); ++ ++ sk->zapped = 0; ++ ++ sk->destruct = rfcomm_sock_destruct; ++ sk->sndtimeo = RFCOMM_CONN_TIMEOUT; ++ ++ sk->sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; ++ sk->rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; ++ ++ sk->protocol = proto; ++ sk->state = BT_OPEN; ++ ++ bluez_sock_link(&rfcomm_sk_list, sk); ++ ++ BT_DBG("sk %p", sk); ++ ++ MOD_INC_USE_COUNT; ++ return sk; ++} ++ ++static int rfcomm_sock_create(struct socket *sock, int protocol) ++{ ++ struct sock *sk; ++ ++ BT_DBG("sock %p", sock); ++ ++ sock->state = SS_UNCONNECTED; ++ ++ if (sock->type != SOCK_STREAM && sock->type != SOCK_RAW) ++ return -ESOCKTNOSUPPORT; ++ ++ sock->ops = &rfcomm_sock_ops; ++ ++ if (!(sk = rfcomm_sock_alloc(sock, protocol, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ rfcomm_sock_init(sk, NULL); ++ return 0; ++} ++ ++static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) ++{ ++ struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; ++ struct sock *sk = sock->sk; ++ int err = 0; ++ ++ BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr)); ++ ++ if (!addr || addr->sa_family != AF_BLUETOOTH) ++ return -EINVAL; ++ ++ lock_sock(sk); ++ ++ if (sk->state != BT_OPEN) { ++ err = -EBADFD; ++ goto done; ++ } ++ ++ write_lock_bh(&rfcomm_sk_list.lock); ++ ++ if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) { ++ err = -EADDRINUSE; ++ } else { ++ /* Save source address */ ++ bacpy(&bluez_pi(sk)->src, &sa->rc_bdaddr); ++ rfcomm_pi(sk)->channel = sa->rc_channel; ++ sk->state = BT_BOUND; ++ } ++ ++ write_unlock_bh(&rfcomm_sk_list.lock); ++ ++done: ++ release_sock(sk); ++ return err; ++} ++ ++static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) ++{ ++ struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; ++ struct sock *sk = sock->sk; ++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; ++ int err = 0; ++ ++ BT_DBG("sk %p", sk); ++ ++ if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_rc)) ++ return -EINVAL; ++ ++ if (sk->state != BT_OPEN && sk->state != BT_BOUND) ++ return -EBADFD; ++ ++ if (sk->type != SOCK_STREAM) ++ return -EINVAL; ++ ++ lock_sock(sk); ++ ++ sk->state = BT_CONNECT; ++ bacpy(&bluez_pi(sk)->dst, &sa->rc_bdaddr); ++ rfcomm_pi(sk)->channel = sa->rc_channel; ++ ++ err = rfcomm_dlc_open(d, &bluez_pi(sk)->src, &sa->rc_bdaddr, sa->rc_channel); ++ if (!err) ++ err = bluez_sock_wait_state(sk, BT_CONNECTED, ++ sock_sndtimeo(sk, flags & O_NONBLOCK)); ++ ++ release_sock(sk); ++ return err; ++} ++ ++int rfcomm_sock_listen(struct socket *sock, int backlog) ++{ ++ struct sock *sk = sock->sk; ++ int err = 0; ++ ++ BT_DBG("sk %p backlog %d", sk, backlog); ++ ++ lock_sock(sk); ++ ++ if (sk->state != BT_BOUND) { ++ err = -EBADFD; ++ goto done; ++ } ++ ++ sk->max_ack_backlog = backlog; ++ sk->ack_backlog = 0; ++ sk->state = BT_LISTEN; ++ ++done: ++ release_sock(sk); ++ return err; ++} ++ ++int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ struct sock *sk = sock->sk, *nsk; ++ long timeo; ++ int err = 0; ++ ++ lock_sock(sk); ++ ++ if (sk->state != BT_LISTEN) { ++ err = -EBADFD; ++ goto done; ++ } ++ ++ timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); ++ ++ BT_DBG("sk %p timeo %ld", sk, timeo); ++ ++ /* Wait for an incoming connection. (wake-one). */ ++ add_wait_queue_exclusive(sk->sleep, &wait); ++ while (!(nsk = bluez_accept_dequeue(sk, newsock))) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (!timeo) { ++ err = -EAGAIN; ++ break; ++ } ++ ++ release_sock(sk); ++ timeo = schedule_timeout(timeo); ++ lock_sock(sk); ++ ++ if (sk->state != BT_LISTEN) { ++ err = -EBADFD; ++ break; ++ } ++ ++ if (signal_pending(current)) { ++ err = sock_intr_errno(timeo); ++ break; ++ } ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(sk->sleep, &wait); ++ ++ if (err) ++ goto done; ++ ++ newsock->state = SS_CONNECTED; ++ ++ BT_DBG("new socket %p", nsk); ++ ++done: ++ release_sock(sk); ++ return err; ++} ++ ++static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) ++{ ++ struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; ++ struct sock *sk = sock->sk; ++ ++ BT_DBG("sock %p, sk %p", sock, sk); ++ ++ sa->rc_family = AF_BLUETOOTH; ++ sa->rc_channel = rfcomm_pi(sk)->channel; ++ if (peer) ++ bacpy(&sa->rc_bdaddr, &bluez_pi(sk)->dst); ++ else ++ bacpy(&sa->rc_bdaddr, &bluez_pi(sk)->src); ++ ++ *len = sizeof(struct sockaddr_rc); ++ return 0; ++} ++ ++static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, ++ struct scm_cookie *scm) ++{ ++ struct sock *sk = sock->sk; ++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; ++ struct sk_buff *skb; ++ int err, size; ++ int sent = 0; ++ ++ if (msg->msg_flags & MSG_OOB) ++ return -EOPNOTSUPP; ++ ++ if (sk->shutdown & SEND_SHUTDOWN) ++ return -EPIPE; ++ ++ BT_DBG("sock %p, sk %p", sock, sk); ++ ++ lock_sock(sk); ++ ++ while (len) { ++ size = min_t(uint, len, d->mtu); ++ ++ skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE, ++ msg->msg_flags & MSG_DONTWAIT, &err); ++ if (!skb) ++ break; ++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); ++ ++ err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); ++ if (err) { ++ kfree_skb(skb); ++ sent = err; ++ break; ++ } ++ ++ err = rfcomm_dlc_send(d, skb); ++ if (err < 0) { ++ kfree_skb(skb); ++ break; ++ } ++ ++ sent += size; ++ len -= size; ++ } ++ ++ release_sock(sk); ++ ++ return sent ? sent : err; ++} ++ ++static long rfcomm_sock_data_wait(struct sock *sk, long timeo) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ ++ add_wait_queue(sk->sleep, &wait); ++ for (;;) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ if (skb_queue_len(&sk->receive_queue) || sk->err || (sk->shutdown & RCV_SHUTDOWN) || ++ signal_pending(current) || !timeo) ++ break; ++ ++ set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags); ++ release_sock(sk); ++ timeo = schedule_timeout(timeo); ++ lock_sock(sk); ++ clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags); ++ } ++ ++ __set_current_state(TASK_RUNNING); ++ remove_wait_queue(sk->sleep, &wait); ++ return timeo; ++} ++ ++static int rfcomm_sock_recvmsg(struct socket *sock, struct msghdr *msg, int size, ++ int flags, struct scm_cookie *scm) ++{ ++ struct sock *sk = sock->sk; ++ int target, err = 0, copied = 0; ++ long timeo; ++ ++ if (flags & MSG_OOB) ++ return -EOPNOTSUPP; ++ ++ msg->msg_namelen = 0; ++ ++ BT_DBG("sk %p size %d", sk, size); ++ ++ lock_sock(sk); ++ ++ target = sock_rcvlowat(sk, flags & MSG_WAITALL, size); ++ timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); ++ ++ do { ++ struct sk_buff *skb; ++ int chunk; ++ ++ skb = skb_dequeue(&sk->receive_queue); ++ if (!skb) { ++ if (copied >= target) ++ break; ++ ++ if ((err = sock_error(sk)) != 0) ++ break; ++ if (sk->shutdown & RCV_SHUTDOWN) ++ break; ++ ++ err = -EAGAIN; ++ if (!timeo) ++ break; ++ ++ timeo = rfcomm_sock_data_wait(sk, timeo); ++ ++ if (signal_pending(current)) { ++ err = sock_intr_errno(timeo); ++ goto out; ++ } ++ continue; ++ } ++ ++ chunk = min_t(unsigned int, skb->len, size); ++ if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { ++ skb_queue_head(&sk->receive_queue, skb); ++ if (!copied) ++ copied = -EFAULT; ++ break; ++ } ++ copied += chunk; ++ size -= chunk; ++ ++ if (!(flags & MSG_PEEK)) { ++ atomic_sub(chunk, &sk->rmem_alloc); ++ ++ skb_pull(skb, chunk); ++ if (skb->len) { ++ skb_queue_head(&sk->receive_queue, skb); ++ break; ++ } ++ kfree_skb(skb); ++ ++ } else { ++ /* put message back and return */ ++ skb_queue_head(&sk->receive_queue, skb); ++ break; ++ } ++ } while (size); ++ ++out: ++ if (atomic_read(&sk->rmem_alloc) <= (sk->rcvbuf >> 2)) ++ rfcomm_dlc_unthrottle(rfcomm_pi(sk)->dlc); ++ ++ release_sock(sk); ++ return copied ? : err; ++} ++ ++static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) ++{ ++ struct sock *sk = sock->sk; ++ int err = 0; ++ ++ BT_DBG("sk %p", sk); ++ ++ lock_sock(sk); ++ ++ switch (optname) { ++ default: ++ err = -ENOPROTOOPT; ++ break; ++ }; ++ ++ release_sock(sk); ++ return err; ++} ++ ++static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) ++{ ++ struct sock *sk = sock->sk; ++ int len, err = 0; ++ ++ BT_DBG("sk %p", sk); ++ ++ if (get_user(len, optlen)) ++ return -EFAULT; ++ ++ lock_sock(sk); ++ ++ switch (optname) { ++ default: ++ err = -ENOPROTOOPT; ++ break; ++ }; ++ ++ release_sock(sk); ++ return err; ++} ++ ++static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ++{ ++ struct sock *sk = sock->sk; ++ int err; ++ ++ lock_sock(sk); ++ ++#ifdef CONFIG_BLUEZ_RFCOMM_TTY ++ err = rfcomm_dev_ioctl(sk, cmd, arg); ++#else ++ err = -EOPNOTSUPP; ++#endif ++ ++ release_sock(sk); ++ ++ return err; ++} ++ ++static int rfcomm_sock_shutdown(struct socket *sock, int how) ++{ ++ struct sock *sk = sock->sk; ++ int err = 0; ++ ++ BT_DBG("sock %p, sk %p", sock, sk); ++ ++ if (!sk) return 0; ++ ++ lock_sock(sk); ++ if (!sk->shutdown) { ++ sk->shutdown = SHUTDOWN_MASK; ++ __rfcomm_sock_close(sk); ++ ++ if (sk->linger) ++ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime); ++ } ++ release_sock(sk); ++ return err; ++} ++ ++static int rfcomm_sock_release(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ int err = 0; ++ ++ BT_DBG("sock %p, sk %p", sock, sk); ++ ++ if (!sk) ++ return 0; ++ ++ err = rfcomm_sock_shutdown(sock, 2); ++ ++ sock_orphan(sk); ++ rfcomm_sock_kill(sk); ++ return err; ++} ++ ++/* ---- RFCOMM core layer callbacks ---- ++ * ++ * called under rfcomm_lock() ++ */ ++int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d) ++{ ++ struct sock *sk, *parent; ++ bdaddr_t src, dst; ++ int result = 0; ++ ++ BT_DBG("session %p channel %d", s, channel); ++ ++ rfcomm_session_getaddr(s, &src, &dst); ++ ++ /* Check if we have socket listening on this channel */ ++ parent = rfcomm_get_sock_by_channel(BT_LISTEN, channel, &src); ++ if (!parent) ++ return 0; ++ ++ /* Check for backlog size */ ++ if (parent->ack_backlog > parent->max_ack_backlog) { ++ BT_DBG("backlog full %d", parent->ack_backlog); ++ goto done; ++ } ++ ++ sk = rfcomm_sock_alloc(NULL, BTPROTO_RFCOMM, GFP_ATOMIC); ++ if (!sk) ++ goto done; ++ ++ rfcomm_sock_init(sk, parent); ++ bacpy(&bluez_pi(sk)->src, &src); ++ bacpy(&bluez_pi(sk)->dst, &dst); ++ rfcomm_pi(sk)->channel = channel; ++ ++ sk->state = BT_CONFIG; ++ bluez_accept_enqueue(parent, sk); ++ ++ /* Accept connection and return socket DLC */ ++ *d = rfcomm_pi(sk)->dlc; ++ result = 1; ++ ++done: ++ bh_unlock_sock(parent); ++ return result; ++} ++ ++/* ---- Proc fs support ---- */ ++int rfcomm_sock_dump(char *buf) ++{ ++ struct bluez_sock_list *list = &rfcomm_sk_list; ++ struct rfcomm_pinfo *pi; ++ struct sock *sk; ++ char *ptr = buf; ++ ++ write_lock_bh(&list->lock); ++ ++ for (sk = list->head; sk; sk = sk->next) { ++ pi = rfcomm_pi(sk); ++ ptr += sprintf(ptr, "sk %s %s %d %d\n", ++ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst), ++ sk->state, rfcomm_pi(sk)->channel); ++ } ++ ++ write_unlock_bh(&list->lock); ++ ++ return ptr - buf; ++} ++ ++static struct proto_ops rfcomm_sock_ops = { ++ family: PF_BLUETOOTH, ++ release: rfcomm_sock_release, ++ bind: rfcomm_sock_bind, ++ connect: rfcomm_sock_connect, ++ listen: rfcomm_sock_listen, ++ accept: rfcomm_sock_accept, ++ getname: rfcomm_sock_getname, ++ sendmsg: rfcomm_sock_sendmsg, ++ recvmsg: rfcomm_sock_recvmsg, ++ shutdown: rfcomm_sock_shutdown, ++ setsockopt: rfcomm_sock_setsockopt, ++ getsockopt: rfcomm_sock_getsockopt, ++ ioctl: rfcomm_sock_ioctl, ++ poll: bluez_sock_poll, ++ socketpair: sock_no_socketpair, ++ mmap: sock_no_mmap ++}; ++ ++static struct net_proto_family rfcomm_sock_family_ops = { ++ family: PF_BLUETOOTH, ++ create: rfcomm_sock_create ++}; ++ ++int rfcomm_init_sockets(void) ++{ ++ int err; ++ ++ if ((err = bluez_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops))) { ++ BT_ERR("Can't register RFCOMM socket layer"); ++ return err; ++ } ++ ++ return 0; ++} ++ ++void rfcomm_cleanup_sockets(void) ++{ ++ int err; ++ ++ /* Unregister socket, protocol and notifier */ ++ if ((err = bluez_sock_unregister(BTPROTO_RFCOMM))) ++ BT_ERR("Can't unregister RFCOMM socket layer %d", err); ++} +diff -Nur c3000_pre/linux/net/bluetooth/rfcomm/tty.c c3000_work/linux/net/bluetooth/rfcomm/tty.c +--- c3000_pre/linux/net/bluetooth/rfcomm/tty.c 1970-01-01 09:00:00.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/rfcomm/tty.c 2004-12-16 23:01:14.000000000 +0900 +@@ -0,0 +1,960 @@ ++/* ++ RFCOMM implementation for Linux Bluetooth stack (BlueZ). ++ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> ++ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation; ++ ++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. ++ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY ++ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES ++ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, ++ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS ++ SOFTWARE IS DISCLAIMED. ++*/ ++ ++/* ++ * RFCOMM TTY. ++ * ++ * $Id: tty.c,v 1.26 2002/10/18 20:12:12 maxk Exp $ ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include <linux/tty.h> ++#include <linux/tty_driver.h> ++#include <linux/tty_flip.h> ++ ++#include <linux/slab.h> ++#include <linux/skbuff.h> ++ ++#include <net/bluetooth/bluetooth.h> ++#include <net/bluetooth/rfcomm.h> ++ ++#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG ++#undef BT_DBG ++#define BT_DBG(D...) ++#endif ++ ++#define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */ ++#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */ ++#define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */ ++#define RFCOMM_TTY_MINOR 0 ++ ++struct rfcomm_dev { ++ struct list_head list; ++ atomic_t refcnt; ++ ++ char name[12]; ++ int id; ++ unsigned long flags; ++ int opened; ++ int err; ++ ++ bdaddr_t src; ++ bdaddr_t dst; ++ u8 channel; ++ ++ uint modem_status; ++ ++ struct rfcomm_dlc *dlc; ++ struct tty_struct *tty; ++ wait_queue_head_t wait; ++ struct tasklet_struct wakeup_task; ++ ++ atomic_t wmem_alloc; ++}; ++ ++static LIST_HEAD(rfcomm_dev_list); ++static rwlock_t rfcomm_dev_lock = RW_LOCK_UNLOCKED; ++ ++static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); ++static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); ++static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig); ++ ++static void rfcomm_tty_wakeup(unsigned long arg); ++ ++/* ---- Device functions ---- */ ++static void rfcomm_dev_destruct(struct rfcomm_dev *dev) ++{ ++ struct rfcomm_dlc *dlc = dev->dlc; ++ ++ BT_DBG("dev %p dlc %p", dev, dlc); ++ ++ rfcomm_dlc_lock(dlc); ++ /* Detach DLC if it's owned by this dev */ ++ if (dlc->owner == dev) ++ dlc->owner = NULL; ++ rfcomm_dlc_unlock(dlc); ++ ++ rfcomm_dlc_put(dlc); ++ kfree(dev); ++ ++ MOD_DEC_USE_COUNT; ++} ++ ++static inline void rfcomm_dev_hold(struct rfcomm_dev *dev) ++{ ++ atomic_inc(&dev->refcnt); ++} ++ ++static inline void rfcomm_dev_put(struct rfcomm_dev *dev) ++{ ++ /* The reason this isn't actually a race, as you no ++ doubt have a little voice screaming at you in your ++ head, is that the refcount should never actually ++ reach zero unless the device has already been taken ++ off the list, in rfcomm_dev_del(). And if that's not ++ true, we'll hit the BUG() in rfcomm_dev_destruct() ++ anyway. */ ++ if (atomic_dec_and_test(&dev->refcnt)) ++ rfcomm_dev_destruct(dev); ++} ++ ++static struct rfcomm_dev *__rfcomm_dev_get(int id) ++{ ++ struct rfcomm_dev *dev; ++ struct list_head *p; ++ ++ list_for_each(p, &rfcomm_dev_list) { ++ dev = list_entry(p, struct rfcomm_dev, list); ++ if (dev->id == id) ++ return dev; ++ } ++ ++ return NULL; ++} ++ ++static inline struct rfcomm_dev *rfcomm_dev_get(int id) ++{ ++ struct rfcomm_dev *dev; ++ ++ read_lock(&rfcomm_dev_lock); ++ ++ dev = __rfcomm_dev_get(id); ++ if (dev) ++ rfcomm_dev_hold(dev); ++ ++ read_unlock(&rfcomm_dev_lock); ++ ++ return dev; ++} ++ ++static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) ++{ ++ struct rfcomm_dev *dev; ++ struct list_head *head = &rfcomm_dev_list, *p; ++ int err = 0; ++ ++ BT_DBG("id %d channel %d", req->dev_id, req->channel); ++ ++ dev = kmalloc(sizeof(struct rfcomm_dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ memset(dev, 0, sizeof(struct rfcomm_dev)); ++ ++ write_lock_bh(&rfcomm_dev_lock); ++ ++ if (req->dev_id < 0) { ++ dev->id = 0; ++ ++ list_for_each(p, &rfcomm_dev_list) { ++ if (list_entry(p, struct rfcomm_dev, list)->id != dev->id) ++ break; ++ ++ dev->id++; ++ head = p; ++ } ++ } else { ++ dev->id = req->dev_id; ++ ++ list_for_each(p, &rfcomm_dev_list) { ++ struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list); ++ ++ if (entry->id == dev->id) { ++ err = -EADDRINUSE; ++ goto out; ++ } ++ ++ if (entry->id > dev->id - 1) ++ break; ++ ++ head = p; ++ } ++ } ++ ++ if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) { ++ err = -ENFILE; ++ goto out; ++ } ++ ++ sprintf(dev->name, "rfcomm%d", dev->id); ++ ++ list_add(&dev->list, head); ++ atomic_set(&dev->refcnt, 1); ++ ++ bacpy(&dev->src, &req->src); ++ bacpy(&dev->dst, &req->dst); ++ dev->channel = req->channel; ++ ++ dev->flags = req->flags & ++ ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); ++ ++ init_waitqueue_head(&dev->wait); ++ tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); ++ ++ rfcomm_dlc_lock(dlc); ++ dlc->data_ready = rfcomm_dev_data_ready; ++ dlc->state_change = rfcomm_dev_state_change; ++ dlc->modem_status = rfcomm_dev_modem_status; ++ ++ dlc->owner = dev; ++ dev->dlc = dlc; ++ rfcomm_dlc_unlock(dlc); ++ ++ MOD_INC_USE_COUNT; ++ ++out: ++ write_unlock_bh(&rfcomm_dev_lock); ++ ++ if (err) { ++ kfree(dev); ++ return err; ++ } else ++ return dev->id; ++} ++ ++static void rfcomm_dev_del(struct rfcomm_dev *dev) ++{ ++ BT_DBG("dev %p", dev); ++ ++ write_lock_bh(&rfcomm_dev_lock); ++ list_del_init(&dev->list); ++ write_unlock_bh(&rfcomm_dev_lock); ++ ++ rfcomm_dev_put(dev); ++} ++ ++/* ---- Send buffer ---- */ ++ ++static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc) ++{ ++ /* We can't let it be zero, because we don't get a callback ++ when tx_credits becomes nonzero, hence we'd never wake up */ ++ return dlc->mtu * (dlc->tx_credits?:1); ++} ++ ++static void rfcomm_wfree(struct sk_buff *skb) ++{ ++ struct rfcomm_dev *dev = (void *) skb->sk; ++ atomic_sub(skb->truesize, &dev->wmem_alloc); ++ if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) ++ tasklet_schedule(&dev->wakeup_task); ++ rfcomm_dev_put(dev); ++} ++ ++static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev) ++{ ++ rfcomm_dev_hold(dev); ++ atomic_add(skb->truesize, &dev->wmem_alloc); ++ skb->sk = (void *) dev; ++ skb->destructor = rfcomm_wfree; ++} ++ ++static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority) ++{ ++ if (force || atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) { ++ struct sk_buff *skb = alloc_skb(size, priority); ++ if (skb) { ++ rfcomm_set_owner_w(skb, dev); ++ return skb; ++ } ++ } ++ return NULL; ++} ++ ++/* ---- Device IOCTLs ---- */ ++ ++#define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP)) ++ ++static int rfcomm_create_dev(struct sock *sk, unsigned long arg) ++{ ++ struct rfcomm_dev_req req; ++ struct rfcomm_dlc *dlc; ++ int id; ++ ++ if (copy_from_user(&req, (void *) arg, sizeof(req))) ++ return -EFAULT; ++ ++ BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags); ++ ++ if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ ++ if (req.flags & (1 << RFCOMM_REUSE_DLC)) { ++ /* Socket must be connected */ ++ if (sk->state != BT_CONNECTED) ++ return -EBADFD; ++ ++ dlc = rfcomm_pi(sk)->dlc; ++ rfcomm_dlc_hold(dlc); ++ } else { ++ dlc = rfcomm_dlc_alloc(GFP_KERNEL); ++ if (!dlc) ++ return -ENOMEM; ++ } ++ ++ id = rfcomm_dev_add(&req, dlc); ++ if (id < 0) { ++ rfcomm_dlc_put(dlc); ++ return id; ++ } ++ ++ if (req.flags & (1 << RFCOMM_REUSE_DLC)) { ++ /* DLC is now used by device. ++ * Socket must be disconnected */ ++ sk->state = BT_CLOSED; ++ } ++ ++ return id; ++} ++ ++static int rfcomm_release_dev(unsigned long arg) ++{ ++ struct rfcomm_dev_req req; ++ struct rfcomm_dev *dev; ++ ++ if (copy_from_user(&req, (void *) arg, sizeof(req))) ++ return -EFAULT; ++ ++ BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags); ++ ++ if (!(dev = rfcomm_dev_get(req.dev_id))) ++ return -ENODEV; ++ ++ if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) { ++ rfcomm_dev_put(dev); ++ return -EPERM; ++ } ++ ++ if (req.flags & (1 << RFCOMM_HANGUP_NOW)) ++ rfcomm_dlc_close(dev->dlc, 0); ++ ++ rfcomm_dev_del(dev); ++ rfcomm_dev_put(dev); ++ return 0; ++} ++ ++static int rfcomm_get_dev_list(unsigned long arg) ++{ ++ struct rfcomm_dev_list_req *dl; ++ struct rfcomm_dev_info *di; ++ struct list_head *p; ++ int n = 0, size, err; ++ u16 dev_num; ++ ++ BT_DBG(""); ++ ++ if (get_user(dev_num, (u16 *) arg)) ++ return -EFAULT; ++ ++ if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di)) ++ return -EINVAL; ++ ++ size = sizeof(*dl) + dev_num * sizeof(*di); ++ ++ if (!(dl = kmalloc(size, GFP_KERNEL))) ++ return -ENOMEM; ++ ++ di = dl->dev_info; ++ ++ read_lock_bh(&rfcomm_dev_lock); ++ ++ list_for_each(p, &rfcomm_dev_list) { ++ struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list); ++ (di + n)->id = dev->id; ++ (di + n)->flags = dev->flags; ++ (di + n)->state = dev->dlc->state; ++ (di + n)->channel = dev->channel; ++ bacpy(&(di + n)->src, &dev->src); ++ bacpy(&(di + n)->dst, &dev->dst); ++ if (++n >= dev_num) ++ break; ++ } ++ ++ read_unlock_bh(&rfcomm_dev_lock); ++ ++ dl->dev_num = n; ++ size = sizeof(*dl) + n * sizeof(*di); ++ ++ err = copy_to_user((void *) arg, dl, size); ++ kfree(dl); ++ ++ return err ? -EFAULT : 0; ++} ++ ++static int rfcomm_get_dev_info(unsigned long arg) ++{ ++ struct rfcomm_dev *dev; ++ struct rfcomm_dev_info di; ++ int err = 0; ++ ++ BT_DBG(""); ++ ++ if (copy_from_user(&di, (void *)arg, sizeof(di))) ++ return -EFAULT; ++ ++ if (!(dev = rfcomm_dev_get(di.id))) ++ return -ENODEV; ++ ++ di.flags = dev->flags; ++ di.channel = dev->channel; ++ di.state = dev->dlc->state; ++ bacpy(&di.src, &dev->src); ++ bacpy(&di.dst, &dev->dst); ++ ++ if (copy_to_user((void *)arg, &di, sizeof(di))) ++ err = -EFAULT; ++ ++ rfcomm_dev_put(dev); ++ return err; ++} ++ ++int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) ++{ ++ BT_DBG("cmd %d arg %ld", cmd, arg); ++ ++ switch (cmd) { ++ case RFCOMMCREATEDEV: ++ return rfcomm_create_dev(sk, arg); ++ ++ case RFCOMMRELEASEDEV: ++ return rfcomm_release_dev(arg); ++ ++ case RFCOMMGETDEVLIST: ++ return rfcomm_get_dev_list(arg); ++ ++ case RFCOMMGETDEVINFO: ++ return rfcomm_get_dev_info(arg); ++ } ++ ++ return -EINVAL; ++} ++ ++/* ---- DLC callbacks ---- */ ++static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb) ++{ ++ struct rfcomm_dev *dev = dlc->owner; ++ struct tty_struct *tty; ++ ++ if (!dev || !(tty = dev->tty)) { ++ kfree_skb(skb); ++ return; ++ } ++ ++ BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len); ++ ++ if (test_bit(TTY_DONT_FLIP, &tty->flags)) { ++ register int i; ++ for (i = 0; i < skb->len; i++) { ++ if (tty->flip.count >= TTY_FLIPBUF_SIZE) ++ tty_flip_buffer_push(tty); ++ ++ tty_insert_flip_char(tty, skb->data[i], 0); ++ } ++ tty_flip_buffer_push(tty); ++ } else ++ tty->ldisc.receive_buf(tty, skb->data, NULL, skb->len); ++ ++ kfree_skb(skb); ++} ++ ++static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) ++{ ++ struct rfcomm_dev *dev = dlc->owner; ++ if (!dev) ++ return; ++ ++ BT_DBG("dlc %p dev %p err %d", dlc, dev, err); ++ ++ dev->err = err; ++ wake_up_interruptible(&dev->wait); ++ ++ if (dlc->state == BT_CLOSED) { ++ if (!dev->tty) { ++ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { ++ rfcomm_dev_hold(dev); ++ rfcomm_dev_del(dev); ++ ++ /* We have to drop DLC lock here, otherwise ++ rfcomm_dev_put() will dead lock if it's ++ the last reference. */ ++ rfcomm_dlc_unlock(dlc); ++ rfcomm_dev_put(dev); ++ rfcomm_dlc_lock(dlc); ++ } ++ } else ++ tty_hangup(dev->tty); ++ } ++} ++ ++static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) ++{ ++ struct rfcomm_dev *dev = dlc->owner; ++ if (!dev) ++ return; ++ ++ BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig); ++ ++ dev->modem_status = ++ ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) | ++ ((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) | ++ ((v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0) | ++ ((v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0); ++} ++ ++/* ---- TTY functions ---- */ ++static void rfcomm_tty_wakeup(unsigned long arg) ++{ ++ struct rfcomm_dev *dev = (void *) arg; ++ struct tty_struct *tty = dev->tty; ++ if (!tty) ++ return; ++ ++ BT_DBG("dev %p tty %p", dev, tty); ++ ++ if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) ++ (tty->ldisc.write_wakeup)(tty); ++ ++ wake_up_interruptible(&tty->write_wait); ++#ifdef SERIAL_HAVE_POLL_WAIT ++ wake_up_interruptible(&tty->poll_wait); ++#endif ++} ++ ++static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ struct rfcomm_dev *dev; ++ struct rfcomm_dlc *dlc; ++ int err, id; ++ ++ id = MINOR(tty->device) - tty->driver.minor_start; ++ ++ BT_DBG("tty %p id %d", tty, id); ++ ++ /* We don't leak this refcount. For reasons which are not entirely ++ clear, the TTY layer will call our ->close() method even if the ++ open fails. We decrease the refcount there, and decreasing it ++ here too would cause breakage. */ ++ dev = rfcomm_dev_get(id); ++ if (!dev) ++ return -ENODEV; ++ ++ BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened); ++ ++ if (dev->opened++ != 0) ++ return 0; ++ ++ dlc = dev->dlc; ++ ++ /* Attach TTY and open DLC */ ++ ++ rfcomm_dlc_lock(dlc); ++ tty->driver_data = dev; ++ dev->tty = tty; ++ rfcomm_dlc_unlock(dlc); ++ set_bit(RFCOMM_TTY_ATTACHED, &dev->flags); ++ ++ err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel); ++ if (err < 0) ++ return err; ++ ++ /* Wait for DLC to connect */ ++ add_wait_queue(&dev->wait, &wait); ++ while (1) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ if (dlc->state == BT_CLOSED) { ++ err = -dev->err; ++ break; ++ } ++ ++ if (dlc->state == BT_CONNECTED) ++ break; ++ ++ if (signal_pending(current)) { ++ err = -EINTR; ++ break; ++ } ++ ++ schedule(); ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&dev->wait, &wait); ++ ++ return err; ++} ++ ++static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) ++{ ++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; ++ if (!dev) ++ return; ++ ++ BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened); ++ ++ if (--dev->opened == 0) { ++ /* Close DLC and dettach TTY */ ++ rfcomm_dlc_close(dev->dlc, 0); ++ ++ clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); ++ tasklet_kill(&dev->wakeup_task); ++ ++ rfcomm_dlc_lock(dev->dlc); ++ tty->driver_data = NULL; ++ dev->tty = NULL; ++ rfcomm_dlc_unlock(dev->dlc); ++ } ++ ++ rfcomm_dev_put(dev); ++} ++ ++static int rfcomm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) ++{ ++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; ++ struct rfcomm_dlc *dlc = dev->dlc; ++ struct sk_buff *skb; ++ int err = 0, sent = 0, size; ++ ++ BT_DBG("tty %p from_user %d count %d", tty, from_user, count); ++ ++ while (count) { ++ size = min_t(uint, count, dlc->mtu); ++ ++ if (from_user) ++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL); ++ else ++ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_ATOMIC); ++ ++ if (!skb) ++ break; ++ ++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); ++ ++ if (from_user) ++ copy_from_user(skb_put(skb, size), buf + sent, size); ++ else ++ memcpy(skb_put(skb, size), buf + sent, size); ++ ++ if ((err = rfcomm_dlc_send(dlc, skb)) < 0) { ++ kfree_skb(skb); ++ break; ++ } ++ ++ sent += size; ++ count -= size; ++ } ++ ++ return sent ? sent : err; ++} ++ ++static void rfcomm_tty_put_char(struct tty_struct *tty, unsigned char ch) ++{ ++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; ++ struct rfcomm_dlc *dlc = dev->dlc; ++ struct sk_buff *skb; ++ ++ BT_DBG("tty %p char %x", tty, ch); ++ ++ skb = rfcomm_wmalloc(dev, 1 + RFCOMM_SKB_RESERVE, 1, GFP_ATOMIC); ++ ++ if (!skb) ++ return; ++ ++ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); ++ ++ *(char *)skb_put(skb, 1) = ch; ++ ++ if ((rfcomm_dlc_send(dlc, skb)) < 0) ++ kfree_skb(skb); ++} ++ ++static int rfcomm_tty_write_room(struct tty_struct *tty) ++{ ++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; ++ int room; ++ ++ BT_DBG("tty %p", tty); ++ ++ room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc); ++ if (room < 0) ++ room = 0; ++ ++ return room; ++} ++ ++static int rfcomm_tty_set_modem_status(uint cmd, struct rfcomm_dlc *dlc, uint status) ++{ ++ u8 v24_sig, mask; ++ ++ BT_DBG("dlc %p cmd 0x%02x", dlc, cmd); ++ ++ if (cmd == TIOCMSET) ++ v24_sig = 0; ++ else ++ rfcomm_dlc_get_modem_status(dlc, &v24_sig); ++ ++ mask = ((status & TIOCM_DSR) ? RFCOMM_V24_RTC : 0) | ++ ((status & TIOCM_DTR) ? RFCOMM_V24_RTC : 0) | ++ ((status & TIOCM_RTS) ? RFCOMM_V24_RTR : 0) | ++ ((status & TIOCM_CTS) ? RFCOMM_V24_RTR : 0) | ++ ((status & TIOCM_RI) ? RFCOMM_V24_IC : 0) | ++ ((status & TIOCM_CD) ? RFCOMM_V24_DV : 0); ++ ++ if (cmd == TIOCMBIC) ++ v24_sig &= ~mask; ++ else ++ v24_sig |= mask; ++ ++ rfcomm_dlc_set_modem_status(dlc, v24_sig); ++ return 0; ++} ++ ++static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; ++ struct rfcomm_dlc *dlc = dev->dlc; ++ uint status; ++ int err; ++ ++ BT_DBG("tty %p cmd 0x%02x", tty, cmd); ++ ++ switch (cmd) { ++ case TCGETS: ++ BT_DBG("TCGETS is not supported"); ++ return -ENOIOCTLCMD; ++ ++ case TCSETS: ++ BT_DBG("TCSETS is not supported"); ++ return -ENOIOCTLCMD; ++ ++ case TIOCMGET: ++ BT_DBG("TIOCMGET"); ++ ++ return put_user(dev->modem_status, (unsigned int *)arg); ++ ++ case TIOCMSET: /* Turns on and off the lines as specified by the mask */ ++ case TIOCMBIS: /* Turns on the lines as specified by the mask */ ++ case TIOCMBIC: /* Turns off the lines as specified by the mask */ ++ if ((err = get_user(status, (unsigned int *)arg))) ++ return err; ++ return rfcomm_tty_set_modem_status(cmd, dlc, status); ++ ++ case TIOCMIWAIT: ++ BT_DBG("TIOCMIWAIT"); ++ break; ++ ++ case TIOCGICOUNT: ++ BT_DBG("TIOCGICOUNT"); ++ break; ++ ++ case TIOCGSERIAL: ++ BT_ERR("TIOCGSERIAL is not supported"); ++ return -ENOIOCTLCMD; ++ ++ case TIOCSSERIAL: ++ BT_ERR("TIOCSSERIAL is not supported"); ++ return -ENOIOCTLCMD; ++ ++ case TIOCSERGSTRUCT: ++ BT_ERR("TIOCSERGSTRUCT is not supported"); ++ return -ENOIOCTLCMD; ++ ++ case TIOCSERGETLSR: ++ BT_ERR("TIOCSERGETLSR is not supported"); ++ return -ENOIOCTLCMD; ++ ++ case TIOCSERCONFIG: ++ BT_ERR("TIOCSERCONFIG is not supported"); ++ return -ENOIOCTLCMD; ++ ++ default: ++ return -ENOIOCTLCMD; /* ioctls which we must ignore */ ++ ++ } ++ ++ return -ENOIOCTLCMD; ++} ++ ++#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) ++ ++static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old) ++{ ++ BT_DBG("tty %p", tty); ++ ++ if ((tty->termios->c_cflag == old->c_cflag) && ++ (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag))) ++ return; ++ ++ /* handle turning off CRTSCTS */ ++ if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { ++ BT_DBG("turning off CRTSCTS"); ++ } ++} ++ ++static void rfcomm_tty_throttle(struct tty_struct *tty) ++{ ++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; ++ ++ BT_DBG("tty %p dev %p", tty, dev); ++ ++ rfcomm_dlc_throttle(dev->dlc); ++} ++ ++static void rfcomm_tty_unthrottle(struct tty_struct *tty) ++{ ++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; ++ ++ BT_DBG("tty %p dev %p", tty, dev); ++ ++ rfcomm_dlc_unthrottle(dev->dlc); ++} ++ ++static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty) ++{ ++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; ++ struct rfcomm_dlc *dlc = dev->dlc; ++ ++ BT_DBG("tty %p dev %p", tty, dev); ++ ++ if (skb_queue_len(&dlc->tx_queue)) ++ return dlc->mtu; ++ ++ return 0; ++} ++ ++static void rfcomm_tty_flush_buffer(struct tty_struct *tty) ++{ ++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; ++ if (!dev) ++ return; ++ ++ BT_DBG("tty %p dev %p", tty, dev); ++ ++ skb_queue_purge(&dev->dlc->tx_queue); ++ ++ if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) ++ tty->ldisc.write_wakeup(tty); ++} ++ ++static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch) ++{ ++ BT_DBG("tty %p ch %c", tty, ch); ++} ++ ++static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) ++{ ++ BT_DBG("tty %p timeout %d", tty, timeout); ++} ++ ++static void rfcomm_tty_hangup(struct tty_struct *tty) ++{ ++ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; ++ if (!dev) ++ return; ++ ++ BT_DBG("tty %p dev %p", tty, dev); ++ ++ rfcomm_tty_flush_buffer(tty); ++ ++ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) ++ rfcomm_dev_del(dev); ++} ++ ++static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused) ++{ ++ return 0; ++} ++ ++/* ---- TTY structure ---- */ ++static int rfcomm_tty_refcount; /* If we manage several devices */ ++ ++static struct tty_struct *rfcomm_tty_table[RFCOMM_TTY_PORTS]; ++static struct termios *rfcomm_tty_termios[RFCOMM_TTY_PORTS]; ++static struct termios *rfcomm_tty_termios_locked[RFCOMM_TTY_PORTS]; ++ ++static struct tty_driver rfcomm_tty_driver = { ++ magic: TTY_DRIVER_MAGIC, ++ driver_name: "rfcomm", ++#ifdef CONFIG_DEVFS_FS ++ name: "bluetooth/rfcomm/%d", ++#else ++ name: "rfcomm", ++#endif ++ major: RFCOMM_TTY_MAJOR, ++ minor_start: RFCOMM_TTY_MINOR, ++ num: RFCOMM_TTY_PORTS, ++ type: TTY_DRIVER_TYPE_SERIAL, ++ subtype: SERIAL_TYPE_NORMAL, ++ flags: TTY_DRIVER_REAL_RAW, ++ ++ refcount: &rfcomm_tty_refcount, ++ table: rfcomm_tty_table, ++ termios: rfcomm_tty_termios, ++ termios_locked: rfcomm_tty_termios_locked, ++ ++ open: rfcomm_tty_open, ++ close: rfcomm_tty_close, ++ put_char: rfcomm_tty_put_char, ++ write: rfcomm_tty_write, ++ write_room: rfcomm_tty_write_room, ++ chars_in_buffer: rfcomm_tty_chars_in_buffer, ++ flush_buffer: rfcomm_tty_flush_buffer, ++ ioctl: rfcomm_tty_ioctl, ++ throttle: rfcomm_tty_throttle, ++ unthrottle: rfcomm_tty_unthrottle, ++ set_termios: rfcomm_tty_set_termios, ++ send_xchar: rfcomm_tty_send_xchar, ++ stop: NULL, ++ start: NULL, ++ hangup: rfcomm_tty_hangup, ++ wait_until_sent: rfcomm_tty_wait_until_sent, ++ read_proc: rfcomm_tty_read_proc, ++}; ++ ++int rfcomm_init_ttys(void) ++{ ++ int i; ++ ++ /* Initalize our global data */ ++ for (i = 0; i < RFCOMM_TTY_PORTS; i++) ++ rfcomm_tty_table[i] = NULL; ++ ++ /* Register the TTY driver */ ++ rfcomm_tty_driver.init_termios = tty_std_termios; ++ rfcomm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; ++ rfcomm_tty_driver.flags = TTY_DRIVER_REAL_RAW; ++ ++ if (tty_register_driver(&rfcomm_tty_driver)) { ++ BT_ERR("Can't register RFCOMM TTY driver"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++void rfcomm_cleanup_ttys(void) ++{ ++ tty_unregister_driver(&rfcomm_tty_driver); ++ return; ++} +diff -Nur c3000_pre/linux/net/bluetooth/sco.c c3000_work/linux/net/bluetooth/sco.c +--- c3000_pre/linux/net/bluetooth/sco.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/sco.c 2004-12-16 23:01:14.000000000 +0900 +@@ -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); +@@ -678,7 +679,7 @@ + + opts.mtu = sco_pi(sk)->conn->mtu; + +- BT_INFO("mtu %d", opts.mtu); ++ BT_DBG("mtu %d", opts.mtu); + + len = MIN(len, sizeof(opts)); + if (copy_to_user(optval, (char *)&opts, len)) +@@ -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) +diff -Nur c3000_pre/linux/net/bluetooth/syms.c c3000_work/linux/net/bluetooth/syms.c +--- c3000_pre/linux/net/bluetooth/syms.c 2004-08-21 09:49:19.000000000 +0900 ++++ c3000_work/linux/net/bluetooth/syms.c 2004-12-16 23:01:14.000000000 +0900 +@@ -44,6 +44,9 @@ + /* HCI Core */ + EXPORT_SYMBOL(hci_register_dev); + EXPORT_SYMBOL(hci_unregister_dev); ++EXPORT_SYMBOL(hci_suspend_dev); ++EXPORT_SYMBOL(hci_resume_dev); ++ + EXPORT_SYMBOL(hci_register_proto); + EXPORT_SYMBOL(hci_unregister_proto); + +@@ -56,7 +59,7 @@ + EXPORT_SYMBOL(hci_recv_frame); + EXPORT_SYMBOL(hci_send_acl); + EXPORT_SYMBOL(hci_send_sco); +-EXPORT_SYMBOL(hci_send_raw); ++EXPORT_SYMBOL(hci_send_cmd); + EXPORT_SYMBOL(hci_si_event); + + /* BlueZ lib */ +@@ -75,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); +diff -Nur c3000_pre/linux/net/netsyms.c c3000_work/linux/net/netsyms.c +--- c3000_pre/linux/net/netsyms.c 2004-08-21 09:49:17.000000000 +0900 ++++ c3000_work/linux/net/netsyms.c 2004-12-16 23:01:14.000000000 +0900 +@@ -168,6 +168,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); |