diff options
author | Michael Lauer <mickey@vanille-media.de> | 2005-07-24 19:30:31 +0000 |
---|---|---|
committer | OpenEmbedded Project <openembedded-devel@lists.openembedded.org> | 2005-07-24 19:30:31 +0000 |
commit | fb2d31da84a0cc813f26ac55f1a4df6fdea055e1 (patch) | |
tree | 468a1235435f3a7a0b55493bb443d446da518572 /packages/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107 | |
parent | 30931c232d0aabfd61ab05195d10feddf709243f (diff) |
openzaurus-pxa27x: autoload usb host module
openzaurus-pxa: remove outdated bluetooth patch, use local WE patches
Diffstat (limited to 'packages/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107')
-rw-r--r-- | packages/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh11.patch | 31393 |
1 files changed, 0 insertions, 31393 deletions
diff --git a/packages/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh11.patch b/packages/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh11.patch deleted file mode 100644 index ff3582ab7b..0000000000 --- a/packages/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh11.patch +++ /dev/null @@ -1,31393 +0,0 @@ - -# -# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher -# - ---- linux/arch/alpha/config.in~bluetooth-2.4.18-mh11 2004-01-25 23:28:10.000000000 +0100 -+++ linux/arch/alpha/config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -372,9 +372,7 @@ - source drivers/usb/Config.in - source drivers/input/Config.in - --if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then -- source net/bluetooth/Config.in --fi -+source net/bluetooth/Config.in - - mainmenu_option next_comment - comment 'Kernel hacking' ---- linux/arch/arm/config.in~bluetooth-2.4.18-mh11 2004-01-25 23:28:31.000000000 +0100 -+++ linux/arch/arm/config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -796,9 +796,7 @@ - - source drivers/usb/Config.in - --if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then -- source net/bluetooth/Config.in --fi -+source net/bluetooth/Config.in - - mainmenu_option next_comment - comment 'Kernel hacking' ---- linux/arch/i386/config.in~bluetooth-2.4.18-mh11 2004-01-25 23:28:31.000000000 +0100 -+++ linux/arch/i386/config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -415,9 +415,7 @@ - - source drivers/usb/Config.in - --if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then -- source net/bluetooth/Config.in --fi -+source net/bluetooth/Config.in - - mainmenu_option next_comment - comment 'Kernel hacking' ---- linux/arch/ppc/config.in~bluetooth-2.4.18-mh11 2004-01-25 23:28:11.000000000 +0100 -+++ linux/arch/ppc/config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -390,9 +390,7 @@ - - source drivers/usb/Config.in - --if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then -- source net/bluetooth/Config.in --fi -+source net/bluetooth/Config.in - - mainmenu_option next_comment - comment 'Kernel hacking' ---- linux/arch/sparc/config.in~bluetooth-2.4.18-mh11 2004-01-25 23:28:11.000000000 +0100 -+++ linux/arch/sparc/config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -252,9 +252,7 @@ - - source fs/Config.in - --if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then -- source net/bluetooth/Config.in --fi -+source net/bluetooth/Config.in - - mainmenu_option next_comment - comment 'Watchdog' ---- linux/arch/sparc64/config.in~bluetooth-2.4.18-mh11 2004-01-25 23:28:11.000000000 +0100 -+++ linux/arch/sparc64/config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -284,9 +284,7 @@ - - source drivers/usb/Config.in - --if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then -- source net/bluetooth/Config.in --fi -+source net/bluetooth/Config.in - - mainmenu_option next_comment - comment 'Watchdog' ---- linux/arch/sparc64/kernel/ioctl32.c~bluetooth-2.4.18-mh11 2002-02-25 20:37:56.000000000 +0100 -+++ linux/arch/sparc64/kernel/ioctl32.c 2004-01-25 23:37:39.000000000 +0100 -@@ -92,6 +92,7 @@ - - #include <net/bluetooth/bluetooth.h> - #include <net/bluetooth/hci.h> -+#include <net/bluetooth/rfcomm.h> - - #include <linux/usb.h> - #include <linux/usbdevice_fs.h> -@@ -3822,6 +3823,15 @@ - return err; - } - -+/* Bluetooth ioctls */ -+#define HCIUARTSETPROTO _IOW('U', 200, int) -+#define HCIUARTGETPROTO _IOR('U', 201, int) -+ -+#define BNEPCONNADD _IOW('B', 200, int) -+#define BNEPCONNDEL _IOW('B', 201, int) -+#define BNEPGETCONNLIST _IOR('B', 210, int) -+#define BNEPGETCONNINFO _IOR('B', 211, int) -+ - struct mtd_oob_buf32 { - u32 start; - u32 length; -@@ -3878,6 +3888,11 @@ - return ((0 == ret) ? 0 : -EFAULT); - } - -+#define CMTPCONNADD _IOW('C', 200, int) -+#define CMTPCONNDEL _IOW('C', 201, int) -+#define CMTPGETCONNLIST _IOR('C', 210, int) -+#define CMTPGETCONNINFO _IOR('C', 211, int) -+ - struct ioctl_trans { - unsigned int cmd; - unsigned int handler; -@@ -4540,6 +4555,21 @@ - COMPATIBLE_IOCTL(HCISETSCAN) - COMPATIBLE_IOCTL(HCISETAUTH) - COMPATIBLE_IOCTL(HCIINQUIRY) -+COMPATIBLE_IOCTL(HCIUARTSETPROTO) -+COMPATIBLE_IOCTL(HCIUARTGETPROTO) -+COMPATIBLE_IOCTL(RFCOMMCREATEDEV) -+COMPATIBLE_IOCTL(RFCOMMRELEASEDEV) -+COMPATIBLE_IOCTL(RFCOMMGETDEVLIST) -+COMPATIBLE_IOCTL(RFCOMMGETDEVINFO) -+COMPATIBLE_IOCTL(RFCOMMSTEALDLC) -+COMPATIBLE_IOCTL(BNEPCONNADD) -+COMPATIBLE_IOCTL(BNEPCONNDEL) -+COMPATIBLE_IOCTL(BNEPGETCONNLIST) -+COMPATIBLE_IOCTL(BNEPGETCONNINFO) -+COMPATIBLE_IOCTL(CMTPCONNADD) -+COMPATIBLE_IOCTL(CMTPCONNDEL) -+COMPATIBLE_IOCTL(CMTPGETCONNLIST) -+COMPATIBLE_IOCTL(CMTPGETCONNINFO) - /* Misc. */ - COMPATIBLE_IOCTL(0x41545900) /* ATYIO_CLKR */ - COMPATIBLE_IOCTL(0x41545901) /* ATYIO_CLKW */ ---- linux/CREDITS~bluetooth-2.4.18-mh11 2004-01-25 23:28:29.000000000 +0100 -+++ linux/CREDITS 2004-01-25 23:37:39.000000000 +0100 -@@ -1317,6 +1317,15 @@ - S: Provo, Utah 84606-5607 - S: USA - -+N: Marcel Holtmann -+E: marcel@holtmann.org -+W: http://www.holtmann.org -+D: Maintainer of the Linux Bluetooth Subsystem -+D: Author and maintainer of the various Bluetooth HCI drivers -+D: Author and maintainer of the CAPI message transport protocol driver -+D: Various other Bluetooth related patches, cleanups and fixes -+S: Germany -+ - N: Rob W. W. Hooft - E: hooft@EMBL-Heidelberg.DE - D: Shared libs for graphics-tools and for the f2c compiler -@@ -2555,6 +2564,7 @@ - N: Aristeu Sergio Rozanski Filho - E: aris@conectiva.com.br - D: Support for EtherExpress 10 ISA (i82595) in eepro driver -+D: User level driver support for input - S: Conectiva S.A. - S: R. Tocantins, 89 - Cristo Rei - S: 80050-430 - Curitiba - Paraná ---- linux/Documentation/Configure.help~bluetooth-2.4.18-mh11 2004-01-25 23:28:31.000000000 +0100 -+++ linux/Documentation/Configure.help 2004-01-25 23:37:39.000000000 +0100 -@@ -2847,14 +2847,6 @@ - - If unsure, say N. - --HCI EMU (virtual device) driver --CONFIG_BLUEZ_HCIEMU -- Bluetooth Virtual HCI device driver. -- This driver is required if you want to use HCI Emulation software. -- -- Say Y here to compile support for Virtual HCI devices into the -- kernel or say M to compile it as module (hci_usb.o). -- - # Choice: alphatype - Alpha system type - CONFIG_ALPHA_GENERIC -@@ -11067,6 +11059,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 -@@ -12967,6 +12965,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 -@@ -19986,11 +19993,15 @@ - Bluetooth can be found at <http://www.bluetooth.com/>. - - Linux Bluetooth subsystem consist of several layers: -- HCI Core (device and connection manager, scheduler) -+ BlueZ Core (HCI device and connection manager, scheduler) - HCI Device drivers (interface to the hardware) - L2CAP Module (L2CAP protocol) -+ SCO Module (SCO links) -+ RFCOMM Module (RFCOMM protocol) -+ BNEP Module (BNEP protocol) -+ CMTP Module (CMTP protocol) - -- Say Y here to enable Linux Bluetooth support and to build HCI Core -+ Say Y here to enable Linux Bluetooth support and to build BlueZ Core - layer. - - To use Linux Bluetooth subsystem, you will need several user-space -@@ -19998,7 +20009,7 @@ - Bluetooth kernel modules are provided in the BlueZ package. - For more information, see <http://bluez.sourceforge.net/>. - -- If you want to compile HCI Core as module (hci.o) say M here. -+ If you want to compile BlueZ Core as module (bluez.o) say M here. - - L2CAP protocol support - CONFIG_BLUEZ_L2CAP -@@ -20009,15 +20020,91 @@ - Say Y here to compile L2CAP support into the kernel or say M to - compile it as module (l2cap.o). - -+SCO links support -+CONFIG_BLUEZ_SCO -+ SCO link provides voice transport over Bluetooth. SCO support is -+ required for voice applications like Headset and Audio. -+ -+ Say Y here to compile SCO support into the kernel or say M to -+ compile it as module (sco.o). -+ -+RFCOMM protocol support -+CONFIG_BLUEZ_RFCOMM -+ RFCOMM provides connection oriented stream transport. RFCOMM -+ support is required for Dialup Networking, OBEX and other Bluetooth -+ applications. -+ -+ Say Y here to compile RFCOMM support into the kernel or say M to -+ compile it as module (rfcomm.o). -+ -+RFCOMM TTY emulation support -+CONFIG_BLUEZ_RFCOMM_TTY -+ This option enables TTY emulation support for RFCOMM channels. -+ -+BNEP protocol support -+CONFIG_BLUEZ_BNEP -+ BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet -+ emulation layer on top of Bluetooth. BNEP is required for Bluetooth -+ PAN (Personal Area Network). -+ -+ To use BNEP, you will need user-space utilities provided in the -+ BlueZ-PAN package. -+ For more information, see <http://bluez.sourceforge.net>. -+ -+ Say Y here to compile BNEP support into the kernel or say M to -+ compile it as module (bnep.o). -+ -+CMTP protocol support -+CONFIG_BLUEZ_CMTP -+ CMTP (CAPI Message Transport Protocol) is a transport layer -+ for CAPI messages. CMTP is required for the Bluetooth Common -+ ISDN Access Profile. -+ -+ Say Y here to compile CMTP support into the kernel or say M to -+ compile it as module (cmtp.o). -+ -+BNEP multicast filter support -+CONFIG_BLUEZ_BNEP_MC_FILTER -+ This option enables the multicast filter support for BNEP. -+ -+BNEP protocol filter support -+CONFIG_BLUEZ_BNEP_PROTO_FILTER -+ This option enables the protocol filter support for BNEP. -+ - HCI UART driver - CONFIG_BLUEZ_HCIUART - Bluetooth HCI UART driver. - This driver is required if you want to use Bluetooth devices with -- serial port interface. -+ serial port interface. You will also need this driver if you have -+ UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card -+ adapter and BrainBoxes Bluetooth PC Card. - - Say Y here to compile support for Bluetooth UART devices into the - kernel or say M to compile it as module (hci_uart.o). - -+HCI UART (H4) protocol support -+CONFIG_BLUEZ_HCIUART_H4 -+ UART (H4) is serial protocol for communication between Bluetooth -+ device and host. This protocol is required for most Bluetooth devices -+ with UART interface, including PCMCIA and CF cards. -+ -+ Say Y here to compile support for HCI UART (H4) protocol. -+ -+HCI BCSP protocol support -+CONFIG_BLUEZ_HCIUART_BCSP -+ BCSP (BlueCore Serial Protocol) is serial protocol for communication -+ between Bluetooth device and host. This protocol is required for non -+ USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and -+ CF cards. -+ -+ Say Y here to compile support for HCI BCSP protocol. -+ -+HCI BCSP transmit CRC with every BCSP packet -+CONFIG_BLUEZ_HCIUART_BCSP_TXCRC -+ If you say Y here, a 16-bit CRC checksum will be transmitted along with -+ every BCSP (BlueCore Serial Protocol) packet sent to the Bluetooth chip. -+ This increases reliability, but slightly reduces efficiency. -+ - HCI USB driver - CONFIG_BLUEZ_HCIUSB - Bluetooth HCI USB driver. -@@ -20027,7 +20114,16 @@ - Say Y here to compile support for Bluetooth USB devices into the - kernel or say M to compile it as module (hci_usb.o). - --HCI VHCI virtual HCI device driver -+HCI USB SCO (voice) support -+CONFIG_BLUEZ_HCIUSB_SCO -+ This option enables the SCO support in the HCI USB driver. You need this -+ to transmit voice data with your Bluetooth USB device. And your device -+ must also support sending SCO data over the HCI layer, because some of -+ them sends the SCO data to an internal PCM adapter. -+ -+ Say Y here to compile support for HCI SCO data. -+ -+HCI VHCI Virtual HCI device driver - CONFIG_BLUEZ_HCIVHCI - Bluetooth Virtual HCI device driver. - This driver is required if you want to use HCI Emulation software. -@@ -20035,6 +20131,66 @@ - Say Y here to compile support for virtual HCI devices into the - kernel or say M to compile it as module (hci_vhci.o). - -+HCI BFUSB device driver -+CONFIG_BLUEZ_HCIBFUSB -+ Bluetooth HCI BlueFRITZ! USB driver. -+ This driver provides support for Bluetooth USB devices with AVM -+ interface: -+ AVM BlueFRITZ! USB -+ -+ Say Y here to compile support for HCI BFUSB devices into the -+ kernel or say M to compile it as module (bfusb.o). -+ -+HCI DTL1 (PC Card) device driver -+CONFIG_BLUEZ_HCIDTL1 -+ Bluetooth HCI DTL1 (PC Card) driver. -+ This driver provides support for Bluetooth PCMCIA devices with -+ Nokia DTL1 interface: -+ Nokia Bluetooth Card -+ Socket Bluetooth CF Card -+ -+ Say Y here to compile support for HCI DTL1 devices into the -+ kernel or say M to compile it as module (dtl1_cs.o). -+ -+HCI BT3C (PC Card) device driver -+CONFIG_BLUEZ_HCIBT3C -+ Bluetooth HCI BT3C (PC Card) driver. -+ This driver provides support for Bluetooth PCMCIA devices with -+ 3Com BT3C interface: -+ 3Com Bluetooth Card (3CRWB6096) -+ HP Bluetooth Card -+ -+ The HCI BT3C driver uses external firmware loader program provided in -+ the BlueFW package. For more information, see <http://bluez.sf.net>. -+ -+ Say Y here to compile support for HCI BT3C devices into the -+ kernel or say M to compile it as module (bt3c_cs.o). -+ -+HCI BlueCard (PC Card) device driver -+CONFIG_BLUEZ_HCIBLUECARD -+ Bluetooth HCI BlueCard (PC Card) driver. -+ This driver provides support for Bluetooth PCMCIA devices with -+ Anycom BlueCard interface: -+ Anycom Bluetooth PC Card -+ Anycom Bluetooth CF Card -+ -+ Say Y here to compile support for HCI BlueCard devices into the -+ kernel or say M to compile it as module (bluecard_cs.o). -+ -+HCI UART (PC Card) device driver -+CONFIG_BLUEZ_HCIBTUART -+ Bluetooth HCI UART (PC Card) driver. -+ This driver provides support for Bluetooth PCMCIA devices with -+ an UART interface: -+ Xircom CreditCard Bluetooth Adapter -+ Xircom RealPort2 Bluetooth Adapter -+ Sphinx PICO Card -+ H-Soft blue+Card -+ Cyber-blue Compact Flash Card -+ -+ Say Y here to compile support for HCI UART devices into the -+ kernel or say M to compile it as module (btuart_cs.o). -+ - # The following options are for Linux when running on the Hitachi - # SuperH family of RISC microprocessors. - ---- linux/Documentation/devices.txt~bluetooth-2.4.18-mh11 2001-11-07 23:46:01.000000000 +0100 -+++ linux/Documentation/devices.txt 2004-01-25 23:37:39.000000000 +0100 -@@ -419,6 +419,7 @@ - 220 = /dev/mptctl Message passing technology (MPT) control - 221 = /dev/mvista/hssdsi Montavista PICMG hot swap system driver - 222 = /dev/mvista/hasi Montavista PICMG high availability -+ 223 = /dev/input/uinput User level driver support for input - 240-255 Reserved for local use - - 11 char Raw keyboard device ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/firmware_class/firmware_sample_driver.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,121 @@ -+/* -+ * firmware_sample_driver.c - -+ * -+ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org> -+ * -+ * Sample code on how to use request_firmware() from drivers. -+ * -+ * Note that register_firmware() is currently useless. -+ * -+ */ -+ -+#include <linux/module.h> -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/string.h> -+ -+#include "linux/firmware.h" -+ -+#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE -+#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE -+char __init inkernel_firmware[] = "let's say that this is firmware\n"; -+#endif -+ -+static char ghost_device[] = "ghost0"; -+ -+static void sample_firmware_load(char *firmware, int size) -+{ -+ u8 buf[size+1]; -+ memcpy(buf, firmware, size); -+ buf[size] = '\0'; -+ printk("firmware_sample_driver: firmware: %s\n", buf); -+} -+ -+static void sample_probe_default(void) -+{ -+ /* uses the default method to get the firmware */ -+ const struct firmware *fw_entry; -+ printk("firmware_sample_driver: a ghost device got inserted :)\n"); -+ -+ if(request_firmware(&fw_entry, "sample_driver_fw", ghost_device)!=0) -+ { -+ printk(KERN_ERR -+ "firmware_sample_driver: Firmware not available\n"); -+ return; -+ } -+ -+ sample_firmware_load(fw_entry->data, fw_entry->size); -+ -+ release_firmware(fw_entry); -+ -+ /* finish setting up the device */ -+} -+static void sample_probe_specific(void) -+{ -+ /* Uses some specific hotplug support to get the firmware from -+ * userspace directly into the hardware, or via some sysfs file */ -+ -+ /* NOTE: This currently doesn't work */ -+ -+ printk("firmware_sample_driver: a ghost device got inserted :)\n"); -+ -+ if(request_firmware(NULL, "sample_driver_fw", ghost_device)!=0) -+ { -+ printk(KERN_ERR -+ "firmware_sample_driver: Firmware load failed\n"); -+ return; -+ } -+ -+ /* request_firmware blocks until userspace finished, so at -+ * this point the firmware should be already in the device */ -+ -+ /* finish setting up the device */ -+} -+static void sample_probe_async_cont(const struct firmware *fw, void *context) -+{ -+ if(!fw){ -+ printk(KERN_ERR -+ "firmware_sample_driver: firmware load failed\n"); -+ return; -+ } -+ -+ printk("firmware_sample_driver: device pointer \"%s\"\n", -+ (char *)context); -+ sample_firmware_load(fw->data, fw->size); -+} -+static void sample_probe_async(void) -+{ -+ /* Let's say that I can't sleep */ -+ int error; -+ error = request_firmware_nowait (THIS_MODULE, -+ "sample_driver_fw", ghost_device, -+ "my device pointer", -+ sample_probe_async_cont); -+ if(error){ -+ printk(KERN_ERR -+ "firmware_sample_driver:" -+ " request_firmware_nowait failed\n"); -+ } -+} -+ -+static int sample_init(void) -+{ -+#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE -+ register_firmware("sample_driver_fw", inkernel_firmware, -+ sizeof(inkernel_firmware)); -+#endif -+ /* since there is no real hardware insertion I just call the -+ * sample probe functions here */ -+ sample_probe_specific(); -+ sample_probe_default(); -+ sample_probe_async(); -+ return 0; -+} -+static void __exit sample_exit(void) -+{ -+} -+ -+module_init (sample_init); -+module_exit (sample_exit); -+ -+MODULE_LICENSE("GPL"); ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/firmware_class/hotplug-script 2004-01-25 23:37:39.000000000 +0100 -@@ -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 -+# ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/Documentation/firmware_class/README 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,58 @@ -+ -+ request_firmware() hotplug interface: -+ ------------------------------------ -+ Copyright (C) 2003 Manuel Estrada Sainz <ranty@debian.org> -+ -+ Why: -+ --- -+ -+ Today, the most extended way to use firmware in the Linux kernel is linking -+ it statically in a header file. Which has political and technical issues: -+ -+ 1) Some firmware is not legal to redistribute. -+ 2) The firmware occupies memory permanently, even though it often is just -+ used once. -+ 3) Some people, like the Debian crowd, don't consider some firmware free -+ enough and remove entire drivers (e.g.: keyspan). -+ -+ about in-kernel persistence: -+ --------------------------- -+ Under some circumstances, as explained below, it would be interesting to keep -+ firmware images in non-swappable kernel memory or even in the kernel image -+ (probably within initramfs). -+ -+ Note that this functionality has not been implemented. -+ -+ - Why OPTIONAL in-kernel persistence may be a good idea sometimes: -+ -+ - If the device that needs the firmware is needed to access the -+ filesystem. When upon some error the device has to be reset and the -+ firmware reloaded, it won't be possible to get it from userspace. -+ e.g.: -+ - A diskless client with a network card that needs firmware. -+ - The filesystem is stored in a disk behind an scsi device -+ that needs firmware. -+ - Replacing buggy DSDT/SSDT ACPI tables on boot. -+ Note: this would require the persistent objects to be included -+ within the kernel image, probably within initramfs. -+ -+ And the same device can be needed to access the filesystem or not depending -+ on the setup, so I think that the choice on what firmware to make -+ persistent should be left to userspace. -+ -+ - Why register_firmware()+__init can be useful: -+ - For boot devices needing firmware. -+ - To make the transition easier: -+ The firmware can be declared __init and register_firmware() -+ called on module_init. Then the firmware is warranted to be -+ there even if "firmware hotplug userspace" is not there yet or -+ it doesn't yet provide the needed firmware. -+ Once the firmware is widely available in userspace, it can be -+ removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE). -+ -+ In either case, if firmware hotplug support is there, it can move the -+ firmware out of kernel memory into the real filesystem for later -+ usage. -+ -+ Note: If persistence is implemented on top of initramfs, -+ register_firmware() may not be appropriate. ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/bfusb.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,781 @@ -+/* -+ * -+ * AVM BlueFRITZ! USB driver -+ * -+ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org> -+ * -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/slab.h> -+#include <linux/types.h> -+#include <linux/sched.h> -+#include <linux/errno.h> -+#include <linux/skbuff.h> -+ -+#include <linux/firmware.h> -+#include <linux/usb.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+ -+#ifndef CONFIG_BLUEZ_HCIBFUSB_DEBUG -+#undef BT_DBG -+#define BT_DBG(D...) -+#endif -+ -+#define VERSION "1.1" -+ -+static struct usb_device_id bfusb_table[] = { -+ /* AVM BlueFRITZ! USB */ -+ { USB_DEVICE(0x057c, 0x2200) }, -+ -+ { } /* Terminating entry */ -+}; -+ -+MODULE_DEVICE_TABLE(usb, bfusb_table); -+ -+ -+#define BFUSB_MAX_BLOCK_SIZE 256 -+ -+#define BFUSB_BLOCK_TIMEOUT (HZ * 3) -+ -+#define BFUSB_TX_PROCESS 1 -+#define BFUSB_TX_WAKEUP 2 -+ -+#define BFUSB_MAX_BULK_TX 1 -+#define BFUSB_MAX_BULK_RX 1 -+ -+struct bfusb { -+ struct hci_dev hdev; -+ -+ unsigned long state; -+ -+ struct usb_device *udev; -+ -+ unsigned int bulk_in_ep; -+ unsigned int bulk_out_ep; -+ unsigned int bulk_pkt_size; -+ -+ rwlock_t lock; -+ -+ struct sk_buff_head transmit_q; -+ -+ struct sk_buff *reassembly; -+ -+ atomic_t pending_tx; -+ struct sk_buff_head pending_q; -+ struct sk_buff_head completed_q; -+}; -+ -+struct bfusb_scb { -+ struct urb *urb; -+}; -+ -+static void bfusb_tx_complete(struct urb *urb); -+static void bfusb_rx_complete(struct urb *urb); -+ -+static struct urb *bfusb_get_completed(struct bfusb *bfusb) -+{ -+ struct sk_buff *skb; -+ struct urb *urb = NULL; -+ -+ BT_DBG("bfusb %p", bfusb); -+ -+ skb = skb_dequeue(&bfusb->completed_q); -+ if (skb) { -+ urb = ((struct bfusb_scb *) skb->cb)->urb; -+ kfree_skb(skb); -+ } -+ -+ return urb; -+} -+ -+static inline void bfusb_unlink_urbs(struct bfusb *bfusb) -+{ -+ struct sk_buff *skb; -+ struct urb *urb; -+ -+ BT_DBG("bfusb %p", bfusb); -+ -+ while ((skb = skb_dequeue(&bfusb->pending_q))) { -+ urb = ((struct bfusb_scb *) skb->cb)->urb; -+ usb_unlink_urb(urb); -+ skb_queue_tail(&bfusb->completed_q, skb); -+ } -+ -+ while ((urb = bfusb_get_completed(bfusb))) -+ usb_free_urb(urb); -+} -+ -+ -+static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb) -+{ -+ struct bfusb_scb *scb = (void *) skb->cb; -+ struct urb *urb = bfusb_get_completed(bfusb); -+ int err, pipe; -+ -+ BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len); -+ -+ if (!urb && !(urb = usb_alloc_urb(0))) -+ return -ENOMEM; -+ -+ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep); -+ -+ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, skb->len, -+ bfusb_tx_complete, skb); -+ -+ urb->transfer_flags = USB_QUEUE_BULK; -+ -+ scb->urb = urb; -+ -+ skb_queue_tail(&bfusb->pending_q, skb); -+ -+ err = usb_submit_urb(urb); -+ if (err) { -+ BT_ERR("%s bulk tx submit failed urb %p err %d", -+ bfusb->hdev.name, urb, err); -+ skb_unlink(skb); -+ usb_free_urb(urb); -+ } else -+ atomic_inc(&bfusb->pending_tx); -+ -+ return err; -+} -+ -+static void bfusb_tx_wakeup(struct bfusb *bfusb) -+{ -+ struct sk_buff *skb; -+ -+ BT_DBG("bfusb %p", bfusb); -+ -+ if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) { -+ set_bit(BFUSB_TX_WAKEUP, &bfusb->state); -+ return; -+ } -+ -+ do { -+ clear_bit(BFUSB_TX_WAKEUP, &bfusb->state); -+ -+ while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) && -+ (skb = skb_dequeue(&bfusb->transmit_q))) { -+ if (bfusb_send_bulk(bfusb, skb) < 0) { -+ skb_queue_head(&bfusb->transmit_q, skb); -+ break; -+ } -+ } -+ -+ } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state)); -+ -+ clear_bit(BFUSB_TX_PROCESS, &bfusb->state); -+} -+ -+static void bfusb_tx_complete(struct urb *urb) -+{ -+ struct sk_buff *skb = (struct sk_buff *) urb->context; -+ struct bfusb *bfusb = (struct bfusb *) skb->dev; -+ -+ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len); -+ -+ atomic_dec(&bfusb->pending_tx); -+ -+ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags)) -+ return; -+ -+ if (!urb->status) -+ bfusb->hdev.stat.byte_tx += skb->len; -+ else -+ bfusb->hdev.stat.err_tx++; -+ -+ read_lock(&bfusb->lock); -+ -+ skb_unlink(skb); -+ skb_queue_tail(&bfusb->completed_q, skb); -+ -+ bfusb_tx_wakeup(bfusb); -+ -+ read_unlock(&bfusb->lock); -+} -+ -+ -+static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb) -+{ -+ struct bfusb_scb *scb; -+ struct sk_buff *skb; -+ int err, pipe, size = HCI_MAX_FRAME_SIZE + 32; -+ -+ BT_DBG("bfusb %p urb %p", bfusb, urb); -+ -+ if (!urb && !(urb = usb_alloc_urb(0))) -+ return -ENOMEM; -+ -+ if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) { -+ usb_free_urb(urb); -+ return -ENOMEM; -+ } -+ -+ skb->dev = (void *) bfusb; -+ -+ scb = (struct bfusb_scb *) skb->cb; -+ scb->urb = urb; -+ -+ pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep); -+ -+ FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, size, -+ bfusb_rx_complete, skb); -+ -+ urb->transfer_flags = USB_QUEUE_BULK; -+ -+ skb_queue_tail(&bfusb->pending_q, skb); -+ -+ err = usb_submit_urb(urb); -+ if (err) { -+ BT_ERR("%s bulk rx submit failed urb %p err %d", -+ bfusb->hdev.name, urb, err); -+ skb_unlink(skb); -+ kfree_skb(skb); -+ usb_free_urb(urb); -+ } -+ -+ return err; -+} -+ -+static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len) -+{ -+ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len); -+ -+ if (hdr & 0x10) { -+ BT_ERR("%s error in block", bfusb->hdev.name); -+ if (bfusb->reassembly) -+ kfree_skb(bfusb->reassembly); -+ bfusb->reassembly = NULL; -+ return -EIO; -+ } -+ -+ if (hdr & 0x04) { -+ struct sk_buff *skb; -+ unsigned char pkt_type; -+ int pkt_len = 0; -+ -+ if (bfusb->reassembly) { -+ BT_ERR("%s unexpected start block", bfusb->hdev.name); -+ kfree_skb(bfusb->reassembly); -+ bfusb->reassembly = NULL; -+ } -+ -+ if (len < 1) { -+ BT_ERR("%s no packet type found", bfusb->hdev.name); -+ return -EPROTO; -+ } -+ -+ pkt_type = *data++; len--; -+ -+ switch (pkt_type) { -+ case HCI_EVENT_PKT: -+ if (len >= HCI_EVENT_HDR_SIZE) { -+ hci_event_hdr *hdr = (hci_event_hdr *) data; -+ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen; -+ } else { -+ BT_ERR("%s event block is too short", bfusb->hdev.name); -+ return -EILSEQ; -+ } -+ break; -+ -+ case HCI_ACLDATA_PKT: -+ if (len >= HCI_ACL_HDR_SIZE) { -+ hci_acl_hdr *hdr = (hci_acl_hdr *) data; -+ pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen); -+ } else { -+ BT_ERR("%s data block is too short", bfusb->hdev.name); -+ return -EILSEQ; -+ } -+ break; -+ -+ case HCI_SCODATA_PKT: -+ if (len >= HCI_SCO_HDR_SIZE) { -+ hci_sco_hdr *hdr = (hci_sco_hdr *) data; -+ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen; -+ } else { -+ BT_ERR("%s audio block is too short", bfusb->hdev.name); -+ return -EILSEQ; -+ } -+ break; -+ } -+ -+ skb = bluez_skb_alloc(pkt_len, GFP_ATOMIC); -+ if (!skb) { -+ BT_ERR("%s no memory for the packet", bfusb->hdev.name); -+ return -ENOMEM; -+ } -+ -+ skb->dev = (void *) &bfusb->hdev; -+ skb->pkt_type = pkt_type; -+ -+ bfusb->reassembly = skb; -+ } else { -+ if (!bfusb->reassembly) { -+ BT_ERR("%s unexpected continuation block", bfusb->hdev.name); -+ return -EIO; -+ } -+ } -+ -+ if (len > 0) -+ memcpy(skb_put(bfusb->reassembly, len), data, len); -+ -+ if (hdr & 0x08) { -+ hci_recv_frame(bfusb->reassembly); -+ bfusb->reassembly = NULL; -+ } -+ -+ return 0; -+} -+ -+static void bfusb_rx_complete(struct urb *urb) -+{ -+ struct sk_buff *skb = (struct sk_buff *) urb->context; -+ struct bfusb *bfusb = (struct bfusb *) skb->dev; -+ unsigned char *buf = urb->transfer_buffer; -+ int count = urb->actual_length; -+ int err, hdr, len; -+ -+ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len); -+ -+ if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags)) -+ return; -+ -+ read_lock(&bfusb->lock); -+ -+ if (urb->status || !count) -+ goto resubmit; -+ -+ bfusb->hdev.stat.byte_rx += count; -+ -+ skb_put(skb, count); -+ -+ while (count) { -+ hdr = buf[0] | (buf[1] << 8); -+ -+ if (hdr & 0x4000) { -+ len = 0; -+ count -= 2; -+ buf += 2; -+ } else { -+ len = (buf[2] == 0) ? 256 : buf[2]; -+ count -= 3; -+ buf += 3; -+ } -+ -+ if (count < len) { -+ BT_ERR("%s block extends over URB buffer ranges", -+ bfusb->hdev.name); -+ } -+ -+ if ((hdr & 0xe1) == 0xc1) -+ bfusb_recv_block(bfusb, hdr, buf, len); -+ -+ count -= len; -+ buf += len; -+ } -+ -+ skb_unlink(skb); -+ kfree_skb(skb); -+ -+ bfusb_rx_submit(bfusb, urb); -+ -+ read_unlock(&bfusb->lock); -+ -+ return; -+ -+resubmit: -+ urb->dev = bfusb->udev; -+ -+ err = usb_submit_urb(urb); -+ if (err) { -+ BT_ERR("%s bulk resubmit failed urb %p err %d", -+ bfusb->hdev.name, urb, err); -+ } -+ -+ read_unlock(&bfusb->lock); -+} -+ -+ -+static int bfusb_open(struct hci_dev *hdev) -+{ -+ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; -+ unsigned long flags; -+ int i, err; -+ -+ BT_DBG("hdev %p bfusb %p", hdev, bfusb); -+ -+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) -+ return 0; -+ -+ MOD_INC_USE_COUNT; -+ -+ write_lock_irqsave(&bfusb->lock, flags); -+ -+ err = bfusb_rx_submit(bfusb, NULL); -+ if (!err) { -+ for (i = 1; i < BFUSB_MAX_BULK_RX; i++) -+ bfusb_rx_submit(bfusb, NULL); -+ } else { -+ clear_bit(HCI_RUNNING, &hdev->flags); -+ MOD_DEC_USE_COUNT; -+ } -+ -+ write_unlock_irqrestore(&bfusb->lock, flags); -+ -+ return err; -+} -+ -+static int bfusb_flush(struct hci_dev *hdev) -+{ -+ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; -+ -+ BT_DBG("hdev %p bfusb %p", hdev, bfusb); -+ -+ skb_queue_purge(&bfusb->transmit_q); -+ -+ return 0; -+} -+ -+static int bfusb_close(struct hci_dev *hdev) -+{ -+ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; -+ unsigned long flags; -+ -+ BT_DBG("hdev %p bfusb %p", hdev, bfusb); -+ -+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) -+ return 0; -+ -+ write_lock_irqsave(&bfusb->lock, flags); -+ -+ bfusb_unlink_urbs(bfusb); -+ bfusb_flush(hdev); -+ -+ write_unlock_irqrestore(&bfusb->lock, flags); -+ -+ MOD_DEC_USE_COUNT; -+ -+ return 0; -+} -+ -+static int bfusb_send_frame(struct sk_buff *skb) -+{ -+ struct hci_dev *hdev = (struct hci_dev *) skb->dev; -+ struct bfusb *bfusb; -+ struct sk_buff *nskb; -+ unsigned char buf[3]; -+ int sent = 0, size, count; -+ -+ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len); -+ -+ if (!hdev) { -+ BT_ERR("Frame for unknown HCI device (hdev=NULL)"); -+ return -ENODEV; -+ } -+ -+ if (!test_bit(HCI_RUNNING, &hdev->flags)) -+ return -EBUSY; -+ -+ bfusb = (struct bfusb *) hdev->driver_data; -+ -+ switch (skb->pkt_type) { -+ case HCI_COMMAND_PKT: -+ hdev->stat.cmd_tx++; -+ break; -+ case HCI_ACLDATA_PKT: -+ hdev->stat.acl_tx++; -+ break; -+ case HCI_SCODATA_PKT: -+ hdev->stat.sco_tx++; -+ break; -+ }; -+ -+ /* Prepend skb with frame type */ -+ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1); -+ -+ count = skb->len; -+ -+ /* Max HCI frame size seems to be 1511 + 1 */ -+ if (!(nskb = bluez_skb_alloc(count + 32, GFP_ATOMIC))) { -+ BT_ERR("Can't allocate memory for new packet"); -+ return -ENOMEM; -+ } -+ -+ nskb->dev = (void *) bfusb; -+ -+ while (count) { -+ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE); -+ -+ buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0); -+ buf[1] = 0x00; -+ buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size; -+ -+ memcpy(skb_put(nskb, 3), buf, 3); -+ memcpy(skb_put(nskb, size), skb->data + sent, size); -+ -+ sent += size; -+ count -= size; -+ } -+ -+ /* Don't send frame with multiple size of bulk max packet */ -+ if ((nskb->len % bfusb->bulk_pkt_size) == 0) { -+ buf[0] = 0xdd; -+ buf[1] = 0x00; -+ memcpy(skb_put(nskb, 2), buf, 2); -+ } -+ -+ read_lock(&bfusb->lock); -+ -+ skb_queue_tail(&bfusb->transmit_q, nskb); -+ bfusb_tx_wakeup(bfusb); -+ -+ read_unlock(&bfusb->lock); -+ -+ kfree_skb(skb); -+ -+ return 0; -+} -+ -+static void bfusb_destruct(struct hci_dev *hdev) -+{ -+ struct bfusb *bfusb = (struct bfusb *) hdev->driver_data; -+ -+ BT_DBG("hdev %p bfusb %p", hdev, bfusb); -+ -+ kfree(bfusb); -+} -+ -+static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) -+{ -+ return -ENOIOCTLCMD; -+} -+ -+ -+static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count) -+{ -+ unsigned char *buf; -+ int err, pipe, len, size, sent = 0; -+ -+ BT_DBG("bfusb %p udev %p firmware %p count %d", bfusb, bfusb->udev, firmware, count); -+ -+ BT_INFO("BlueFRITZ! USB loading firmware"); -+ -+ if (usb_set_configuration(bfusb->udev, 1) < 0) { -+ BT_ERR("Can't change to loading configuration"); -+ return -EBUSY; -+ } -+ -+ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC); -+ if (!buf) { -+ BT_ERR("Can't allocate memory chunk for firmware"); -+ return -ENOMEM; -+ } -+ -+ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep); -+ -+ while (count) { -+ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3); -+ -+ memcpy(buf, firmware + sent, size); -+ -+ err = usb_bulk_msg(bfusb->udev, pipe, buf, size, -+ &len, BFUSB_BLOCK_TIMEOUT); -+ -+ if (err || (len != size)) { -+ BT_ERR("Error in firmware loading"); -+ goto error; -+ } -+ -+ sent += size; -+ count -= size; -+ } -+ -+ if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0, -+ &len, BFUSB_BLOCK_TIMEOUT)) < 0) { -+ BT_ERR("Error in null packet request"); -+ goto error; -+ } -+ -+ if ((err = usb_set_configuration(bfusb->udev, 2)) < 0) { -+ BT_ERR("Can't change to running configuration"); -+ goto error; -+ } -+ -+ BT_INFO("BlueFRITZ! USB device ready"); -+ -+ kfree(buf); -+ return 0; -+ -+error: -+ kfree(buf); -+ -+ pipe = usb_sndctrlpipe(bfusb->udev, 0); -+ -+ usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION, -+ 0, 0, 0, NULL, 0, BFUSB_BLOCK_TIMEOUT); -+ -+ return err; -+} -+ -+static void *bfusb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) -+{ -+ const struct firmware *firmware; -+ char device[16]; -+ struct usb_interface *iface; -+ struct usb_interface_descriptor *iface_desc; -+ struct usb_endpoint_descriptor *bulk_out_ep; -+ struct usb_endpoint_descriptor *bulk_in_ep; -+ struct hci_dev *hdev; -+ struct bfusb *bfusb; -+ -+ BT_DBG("udev %p ifnum %d id %p", udev, ifnum, id); -+ -+ /* Check number of endpoints */ -+ iface = &udev->actconfig->interface[0]; -+ iface_desc = &iface->altsetting[0]; -+ -+ if (iface_desc->bNumEndpoints < 2) -+ return NULL; -+ -+ bulk_out_ep = &iface_desc->endpoint[0]; -+ bulk_in_ep = &iface_desc->endpoint[1]; -+ -+ if (!bulk_out_ep || !bulk_in_ep) { -+ BT_ERR("Bulk endpoints not found"); -+ goto done; -+ } -+ -+ /* Initialize control structure and load firmware */ -+ if (!(bfusb = kmalloc(sizeof(struct bfusb), GFP_KERNEL))) { -+ BT_ERR("Can't allocate memory for control structure"); -+ goto done; -+ } -+ -+ memset(bfusb, 0, sizeof(struct bfusb)); -+ -+ bfusb->udev = udev; -+ bfusb->bulk_in_ep = bulk_in_ep->bEndpointAddress; -+ bfusb->bulk_out_ep = bulk_out_ep->bEndpointAddress; -+ bfusb->bulk_pkt_size = bulk_out_ep->wMaxPacketSize; -+ -+ bfusb->lock = RW_LOCK_UNLOCKED; -+ -+ bfusb->reassembly = NULL; -+ -+ skb_queue_head_init(&bfusb->transmit_q); -+ skb_queue_head_init(&bfusb->pending_q); -+ skb_queue_head_init(&bfusb->completed_q); -+ -+ snprintf(device, sizeof(device), "bfusb%3.3d%3.3d", udev->bus->busnum, udev->devnum); -+ -+ if (request_firmware(&firmware, "bfubase.frm", device) < 0) { -+ BT_ERR("Firmware request failed"); -+ goto error; -+ } -+ -+ if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) { -+ BT_ERR("Firmware loading failed"); -+ goto release; -+ } -+ -+ release_firmware(firmware); -+ -+ /* Initialize and register HCI device */ -+ hdev = &bfusb->hdev; -+ -+ hdev->type = HCI_USB; -+ hdev->driver_data = bfusb; -+ -+ hdev->open = bfusb_open; -+ hdev->close = bfusb_close; -+ hdev->flush = bfusb_flush; -+ hdev->send = bfusb_send_frame; -+ hdev->destruct = bfusb_destruct; -+ hdev->ioctl = bfusb_ioctl; -+ -+ if (hci_register_dev(hdev) < 0) { -+ BT_ERR("Can't register HCI device"); -+ goto error; -+ } -+ -+ return bfusb; -+ -+release: -+ release_firmware(firmware); -+ -+error: -+ kfree(bfusb); -+ -+done: -+ return NULL; -+} -+ -+static void bfusb_disconnect(struct usb_device *udev, void *ptr) -+{ -+ struct bfusb *bfusb = (struct bfusb *) ptr; -+ struct hci_dev *hdev = &bfusb->hdev; -+ -+ BT_DBG("udev %p ptr %p", udev, ptr); -+ -+ if (!hdev) -+ return; -+ -+ bfusb_close(hdev); -+ -+ if (hci_unregister_dev(hdev) < 0) -+ BT_ERR("Can't unregister HCI device %s", hdev->name); -+} -+ -+static struct usb_driver bfusb_driver = { -+ name: "bfusb", -+ probe: bfusb_probe, -+ disconnect: bfusb_disconnect, -+ id_table: bfusb_table, -+}; -+ -+static int __init bfusb_init(void) -+{ -+ int err; -+ -+ BT_INFO("BlueFRITZ! USB driver ver %s", VERSION); -+ BT_INFO("Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>"); -+ -+ if ((err = usb_register(&bfusb_driver)) < 0) -+ BT_ERR("Failed to register BlueFRITZ! USB driver"); -+ -+ return err; -+} -+ -+static void __exit bfusb_cleanup(void) -+{ -+ usb_deregister(&bfusb_driver); -+} -+ -+module_init(bfusb_init); -+module_exit(bfusb_cleanup); -+ -+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -+MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION); -+MODULE_LICENSE("GPL"); ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/bluecard_cs.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,1113 @@ -+/* -+ * -+ * Bluetooth driver for the Anycom BlueCard (LSE039/LSE041) -+ * -+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org> -+ * -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation; -+ * -+ * Software distributed under the License is distributed on an "AS -+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -+ * implied. See the License for the specific language governing -+ * rights and limitations under the License. -+ * -+ * The initial developer of the original code is David A. Hinds -+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds -+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/slab.h> -+#include <linux/types.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/errno.h> -+#include <linux/ptrace.h> -+#include <linux/ioport.h> -+#include <linux/spinlock.h> -+#include <linux/skbuff.h> -+#include <asm/io.h> -+ -+#include <pcmcia/version.h> -+#include <pcmcia/cs_types.h> -+#include <pcmcia/cs.h> -+#include <pcmcia/cistpl.h> -+#include <pcmcia/ciscode.h> -+#include <pcmcia/ds.h> -+#include <pcmcia/cisreg.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+ -+ -+ -+/* ======================== Module parameters ======================== */ -+ -+ -+/* Bit map of interrupts to choose from */ -+static u_int irq_mask = 0x86bc; -+static int irq_list[4] = { -1 }; -+ -+MODULE_PARM(irq_mask, "i"); -+MODULE_PARM(irq_list, "1-4i"); -+ -+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -+MODULE_DESCRIPTION("BlueZ driver for the Anycom BlueCard (LSE039/LSE041)"); -+MODULE_LICENSE("GPL"); -+ -+ -+ -+/* ======================== Local structures ======================== */ -+ -+ -+typedef struct bluecard_info_t { -+ dev_link_t link; -+ dev_node_t node; -+ -+ struct hci_dev hdev; -+ -+ spinlock_t lock; /* For serializing operations */ -+ struct timer_list timer; /* For LED control */ -+ -+ struct sk_buff_head txq; -+ unsigned long tx_state; -+ -+ unsigned long rx_state; -+ unsigned long rx_count; -+ struct sk_buff *rx_skb; -+ -+ unsigned char ctrl_reg; -+ unsigned long hw_state; /* Status of the hardware and LED control */ -+} bluecard_info_t; -+ -+ -+void bluecard_config(dev_link_t *link); -+void bluecard_release(u_long arg); -+int bluecard_event(event_t event, int priority, event_callback_args_t *args); -+ -+static dev_info_t dev_info = "bluecard_cs"; -+ -+dev_link_t *bluecard_attach(void); -+void bluecard_detach(dev_link_t *); -+ -+static dev_link_t *dev_list = NULL; -+ -+ -+/* Default baud rate: 57600, 115200, 230400 or 460800 */ -+#define DEFAULT_BAUD_RATE 230400 -+ -+ -+/* Hardware states */ -+#define CARD_READY 1 -+#define CARD_HAS_PCCARD_ID 4 -+#define CARD_HAS_POWER_LED 5 -+#define CARD_HAS_ACTIVITY_LED 6 -+ -+/* Transmit states */ -+#define XMIT_SENDING 1 -+#define XMIT_WAKEUP 2 -+#define XMIT_BUFFER_NUMBER 5 /* unset = buffer one, set = buffer two */ -+#define XMIT_BUF_ONE_READY 6 -+#define XMIT_BUF_TWO_READY 7 -+#define XMIT_SENDING_READY 8 -+ -+/* Receiver states */ -+#define RECV_WAIT_PACKET_TYPE 0 -+#define RECV_WAIT_EVENT_HEADER 1 -+#define RECV_WAIT_ACL_HEADER 2 -+#define RECV_WAIT_SCO_HEADER 3 -+#define RECV_WAIT_DATA 4 -+ -+/* Special packet types */ -+#define PKT_BAUD_RATE_57600 0x80 -+#define PKT_BAUD_RATE_115200 0x81 -+#define PKT_BAUD_RATE_230400 0x82 -+#define PKT_BAUD_RATE_460800 0x83 -+ -+ -+/* These are the register offsets */ -+#define REG_COMMAND 0x20 -+#define REG_INTERRUPT 0x21 -+#define REG_CONTROL 0x22 -+#define REG_RX_CONTROL 0x24 -+#define REG_CARD_RESET 0x30 -+#define REG_LED_CTRL 0x30 -+ -+/* REG_COMMAND */ -+#define REG_COMMAND_TX_BUF_ONE 0x01 -+#define REG_COMMAND_TX_BUF_TWO 0x02 -+#define REG_COMMAND_RX_BUF_ONE 0x04 -+#define REG_COMMAND_RX_BUF_TWO 0x08 -+#define REG_COMMAND_RX_WIN_ONE 0x00 -+#define REG_COMMAND_RX_WIN_TWO 0x10 -+ -+/* REG_CONTROL */ -+#define REG_CONTROL_BAUD_RATE_57600 0x00 -+#define REG_CONTROL_BAUD_RATE_115200 0x01 -+#define REG_CONTROL_BAUD_RATE_230400 0x02 -+#define REG_CONTROL_BAUD_RATE_460800 0x03 -+#define REG_CONTROL_RTS 0x04 -+#define REG_CONTROL_BT_ON 0x08 -+#define REG_CONTROL_BT_RESET 0x10 -+#define REG_CONTROL_BT_RES_PU 0x20 -+#define REG_CONTROL_INTERRUPT 0x40 -+#define REG_CONTROL_CARD_RESET 0x80 -+ -+/* REG_RX_CONTROL */ -+#define RTS_LEVEL_SHIFT_BITS 0x02 -+ -+ -+ -+/* ======================== LED handling routines ======================== */ -+ -+ -+void bluecard_activity_led_timeout(u_long arg) -+{ -+ bluecard_info_t *info = (bluecard_info_t *)arg; -+ unsigned int iobase = info->link.io.BasePort1; -+ -+ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) { -+ /* Disable activity LED */ -+ outb(0x08 | 0x20, iobase + 0x30); -+ } else { -+ /* Disable power LED */ -+ outb(0x00, iobase + 0x30); -+ } -+} -+ -+ -+static void bluecard_enable_activity_led(bluecard_info_t *info) -+{ -+ unsigned int iobase = info->link.io.BasePort1; -+ -+ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) { -+ /* Enable activity LED */ -+ outb(0x10 | 0x40, iobase + 0x30); -+ -+ /* Stop the LED after HZ/4 */ -+ mod_timer(&(info->timer), jiffies + HZ / 4); -+ } else { -+ /* Enable power LED */ -+ outb(0x08 | 0x20, iobase + 0x30); -+ -+ /* Stop the LED after HZ/2 */ -+ mod_timer(&(info->timer), jiffies + HZ / 2); -+ } -+} -+ -+ -+ -+/* ======================== Interrupt handling ======================== */ -+ -+ -+static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len) -+{ -+ int i, actual; -+ -+ actual = (len > 15) ? 15 : len; -+ -+ outb_p(actual, iobase + offset); -+ -+ for (i = 0; i < actual; i++) -+ outb_p(buf[i], iobase + offset + i + 1); -+ -+ return actual; -+} -+ -+ -+static void bluecard_write_wakeup(bluecard_info_t *info) -+{ -+ if (!info) { -+ printk(KERN_WARNING "bluecard_cs: Call of write_wakeup for unknown device.\n"); -+ return; -+ } -+ -+ if (!test_bit(XMIT_SENDING_READY, &(info->tx_state))) -+ return; -+ -+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) { -+ set_bit(XMIT_WAKEUP, &(info->tx_state)); -+ return; -+ } -+ -+ do { -+ register unsigned int iobase = info->link.io.BasePort1; -+ register unsigned int offset; -+ register unsigned char command; -+ register unsigned long ready_bit; -+ register struct sk_buff *skb; -+ register int len; -+ -+ clear_bit(XMIT_WAKEUP, &(info->tx_state)); -+ -+ if (!(info->link.state & DEV_PRESENT)) -+ return; -+ -+ if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) { -+ if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state))) -+ break; -+ offset = 0x10; -+ command = REG_COMMAND_TX_BUF_TWO; -+ ready_bit = XMIT_BUF_TWO_READY; -+ } else { -+ if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state))) -+ break; -+ offset = 0x00; -+ command = REG_COMMAND_TX_BUF_ONE; -+ ready_bit = XMIT_BUF_ONE_READY; -+ } -+ -+ if (!(skb = skb_dequeue(&(info->txq)))) -+ break; -+ -+ if (skb->pkt_type & 0x80) { -+ /* Disable RTS */ -+ info->ctrl_reg |= REG_CONTROL_RTS; -+ outb(info->ctrl_reg, iobase + REG_CONTROL); -+ } -+ -+ /* Activate LED */ -+ bluecard_enable_activity_led(info); -+ -+ /* Send frame */ -+ len = bluecard_write(iobase, offset, skb->data, skb->len); -+ -+ /* Tell the FPGA to send the data */ -+ outb_p(command, iobase + REG_COMMAND); -+ -+ /* Mark the buffer as dirty */ -+ clear_bit(ready_bit, &(info->tx_state)); -+ -+ if (skb->pkt_type & 0x80) { -+ -+ wait_queue_head_t wait; -+ unsigned char baud_reg; -+ -+ switch (skb->pkt_type) { -+ case PKT_BAUD_RATE_460800: -+ baud_reg = REG_CONTROL_BAUD_RATE_460800; -+ break; -+ case PKT_BAUD_RATE_230400: -+ baud_reg = REG_CONTROL_BAUD_RATE_230400; -+ break; -+ case PKT_BAUD_RATE_115200: -+ baud_reg = REG_CONTROL_BAUD_RATE_115200; -+ break; -+ case PKT_BAUD_RATE_57600: -+ /* Fall through... */ -+ default: -+ baud_reg = REG_CONTROL_BAUD_RATE_57600; -+ break; -+ } -+ -+ /* Wait until the command reaches the baseband */ -+ init_waitqueue_head(&wait); -+ interruptible_sleep_on_timeout(&wait, HZ / 10); -+ -+ /* Set baud on baseband */ -+ info->ctrl_reg &= ~0x03; -+ info->ctrl_reg |= baud_reg; -+ outb(info->ctrl_reg, iobase + REG_CONTROL); -+ -+ /* Enable RTS */ -+ info->ctrl_reg &= ~REG_CONTROL_RTS; -+ outb(info->ctrl_reg, iobase + REG_CONTROL); -+ -+ /* Wait before the next HCI packet can be send */ -+ interruptible_sleep_on_timeout(&wait, HZ); -+ -+ } -+ -+ if (len == skb->len) { -+ kfree_skb(skb); -+ } else { -+ skb_pull(skb, len); -+ skb_queue_head(&(info->txq), skb); -+ } -+ -+ info->hdev.stat.byte_tx += len; -+ -+ /* Change buffer */ -+ change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state)); -+ -+ } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); -+ -+ clear_bit(XMIT_SENDING, &(info->tx_state)); -+} -+ -+ -+static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size) -+{ -+ int i, n, len; -+ -+ outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND); -+ -+ len = inb(iobase + offset); -+ n = 0; -+ i = 1; -+ -+ while (n < len) { -+ -+ if (i == 16) { -+ outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND); -+ i = 0; -+ } -+ -+ buf[n] = inb(iobase + offset + i); -+ -+ n++; -+ i++; -+ -+ } -+ -+ return len; -+} -+ -+ -+static void bluecard_receive(bluecard_info_t *info, unsigned int offset) -+{ -+ unsigned int iobase; -+ unsigned char buf[31]; -+ int i, len; -+ -+ if (!info) { -+ printk(KERN_WARNING "bluecard_cs: Call of receive for unknown device.\n"); -+ return; -+ } -+ -+ iobase = info->link.io.BasePort1; -+ -+ if (test_bit(XMIT_SENDING_READY, &(info->tx_state))) -+ bluecard_enable_activity_led(info); -+ -+ len = bluecard_read(iobase, offset, buf, sizeof(buf)); -+ -+ for (i = 0; i < len; i++) { -+ -+ /* Allocate packet */ -+ if (info->rx_skb == NULL) { -+ info->rx_state = RECV_WAIT_PACKET_TYPE; -+ info->rx_count = 0; -+ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { -+ printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n"); -+ return; -+ } -+ } -+ -+ if (info->rx_state == RECV_WAIT_PACKET_TYPE) { -+ -+ info->rx_skb->dev = (void *)&(info->hdev); -+ info->rx_skb->pkt_type = buf[i]; -+ -+ switch (info->rx_skb->pkt_type) { -+ -+ case 0x00: -+ /* init packet */ -+ if (offset != 0x00) { -+ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state)); -+ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state)); -+ set_bit(XMIT_SENDING_READY, &(info->tx_state)); -+ bluecard_write_wakeup(info); -+ } -+ -+ kfree_skb(info->rx_skb); -+ info->rx_skb = NULL; -+ break; -+ -+ case HCI_EVENT_PKT: -+ info->rx_state = RECV_WAIT_EVENT_HEADER; -+ info->rx_count = HCI_EVENT_HDR_SIZE; -+ break; -+ -+ case HCI_ACLDATA_PKT: -+ info->rx_state = RECV_WAIT_ACL_HEADER; -+ info->rx_count = HCI_ACL_HDR_SIZE; -+ break; -+ -+ case HCI_SCODATA_PKT: -+ info->rx_state = RECV_WAIT_SCO_HEADER; -+ info->rx_count = HCI_SCO_HDR_SIZE; -+ break; -+ -+ default: -+ /* unknown packet */ -+ printk(KERN_WARNING "bluecard_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type); -+ info->hdev.stat.err_rx++; -+ -+ kfree_skb(info->rx_skb); -+ info->rx_skb = NULL; -+ break; -+ -+ } -+ -+ } else { -+ -+ *skb_put(info->rx_skb, 1) = buf[i]; -+ info->rx_count--; -+ -+ if (info->rx_count == 0) { -+ -+ int dlen; -+ hci_event_hdr *eh; -+ hci_acl_hdr *ah; -+ hci_sco_hdr *sh; -+ -+ switch (info->rx_state) { -+ -+ case RECV_WAIT_EVENT_HEADER: -+ eh = (hci_event_hdr *)(info->rx_skb->data); -+ info->rx_state = RECV_WAIT_DATA; -+ info->rx_count = eh->plen; -+ break; -+ -+ case RECV_WAIT_ACL_HEADER: -+ ah = (hci_acl_hdr *)(info->rx_skb->data); -+ dlen = __le16_to_cpu(ah->dlen); -+ info->rx_state = RECV_WAIT_DATA; -+ info->rx_count = dlen; -+ break; -+ -+ case RECV_WAIT_SCO_HEADER: -+ sh = (hci_sco_hdr *)(info->rx_skb->data); -+ info->rx_state = RECV_WAIT_DATA; -+ info->rx_count = sh->dlen; -+ break; -+ -+ case RECV_WAIT_DATA: -+ hci_recv_frame(info->rx_skb); -+ info->rx_skb = NULL; -+ break; -+ -+ } -+ -+ } -+ -+ } -+ -+ -+ } -+ -+ info->hdev.stat.byte_rx += len; -+} -+ -+ -+void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs) -+{ -+ bluecard_info_t *info = dev_inst; -+ unsigned int iobase; -+ unsigned char reg; -+ -+ if (!info) { -+ printk(KERN_WARNING "bluecard_cs: Call of irq %d for unknown device.\n", irq); -+ return; -+ } -+ -+ if (!test_bit(CARD_READY, &(info->hw_state))) -+ return; -+ -+ iobase = info->link.io.BasePort1; -+ -+ spin_lock(&(info->lock)); -+ -+ /* Disable interrupt */ -+ info->ctrl_reg &= ~REG_CONTROL_INTERRUPT; -+ outb(info->ctrl_reg, iobase + REG_CONTROL); -+ -+ reg = inb(iobase + REG_INTERRUPT); -+ -+ if ((reg != 0x00) && (reg != 0xff)) { -+ -+ if (reg & 0x04) { -+ bluecard_receive(info, 0x00); -+ outb(0x04, iobase + REG_INTERRUPT); -+ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND); -+ } -+ -+ if (reg & 0x08) { -+ bluecard_receive(info, 0x10); -+ outb(0x08, iobase + REG_INTERRUPT); -+ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND); -+ } -+ -+ if (reg & 0x01) { -+ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state)); -+ outb(0x01, iobase + REG_INTERRUPT); -+ bluecard_write_wakeup(info); -+ } -+ -+ if (reg & 0x02) { -+ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state)); -+ outb(0x02, iobase + REG_INTERRUPT); -+ bluecard_write_wakeup(info); -+ } -+ -+ } -+ -+ /* Enable interrupt */ -+ info->ctrl_reg |= REG_CONTROL_INTERRUPT; -+ outb(info->ctrl_reg, iobase + REG_CONTROL); -+ -+ spin_unlock(&(info->lock)); -+} -+ -+ -+ -+/* ======================== Device specific HCI commands ======================== */ -+ -+ -+static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud) -+{ -+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data); -+ struct sk_buff *skb; -+ -+ /* Ericsson baud rate command */ -+ unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 }; -+ -+ if (!(skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { -+ printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n"); -+ return -1; -+ } -+ -+ switch (baud) { -+ case 460800: -+ cmd[4] = 0x00; -+ skb->pkt_type = PKT_BAUD_RATE_460800; -+ break; -+ case 230400: -+ cmd[4] = 0x01; -+ skb->pkt_type = PKT_BAUD_RATE_230400; -+ break; -+ case 115200: -+ cmd[4] = 0x02; -+ skb->pkt_type = PKT_BAUD_RATE_115200; -+ break; -+ case 57600: -+ /* Fall through... */ -+ default: -+ cmd[4] = 0x03; -+ skb->pkt_type = PKT_BAUD_RATE_57600; -+ break; -+ } -+ -+ memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd)); -+ -+ skb_queue_tail(&(info->txq), skb); -+ -+ bluecard_write_wakeup(info); -+ -+ return 0; -+} -+ -+ -+ -+/* ======================== HCI interface ======================== */ -+ -+ -+static int bluecard_hci_flush(struct hci_dev *hdev) -+{ -+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data); -+ -+ /* Drop TX queue */ -+ skb_queue_purge(&(info->txq)); -+ -+ return 0; -+} -+ -+ -+static int bluecard_hci_open(struct hci_dev *hdev) -+{ -+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data); -+ unsigned int iobase = info->link.io.BasePort1; -+ -+ bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE); -+ -+ if (test_and_set_bit(HCI_RUNNING, &(hdev->flags))) -+ return 0; -+ -+ /* Enable LED */ -+ outb(0x08 | 0x20, iobase + 0x30); -+ -+ return 0; -+} -+ -+ -+static int bluecard_hci_close(struct hci_dev *hdev) -+{ -+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data); -+ unsigned int iobase = info->link.io.BasePort1; -+ -+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) -+ return 0; -+ -+ bluecard_hci_flush(hdev); -+ -+ /* Disable LED */ -+ outb(0x00, iobase + 0x30); -+ -+ return 0; -+} -+ -+ -+static int bluecard_hci_send_frame(struct sk_buff *skb) -+{ -+ bluecard_info_t *info; -+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev); -+ -+ if (!hdev) { -+ printk(KERN_WARNING "bluecard_cs: Frame for unknown HCI device (hdev=NULL)."); -+ return -ENODEV; -+ } -+ -+ info = (bluecard_info_t *)(hdev->driver_data); -+ -+ switch (skb->pkt_type) { -+ case HCI_COMMAND_PKT: -+ hdev->stat.cmd_tx++; -+ break; -+ case HCI_ACLDATA_PKT: -+ hdev->stat.acl_tx++; -+ break; -+ case HCI_SCODATA_PKT: -+ hdev->stat.sco_tx++; -+ break; -+ }; -+ -+ /* Prepend skb with frame type */ -+ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1); -+ skb_queue_tail(&(info->txq), skb); -+ -+ bluecard_write_wakeup(info); -+ -+ return 0; -+} -+ -+ -+static void bluecard_hci_destruct(struct hci_dev *hdev) -+{ -+} -+ -+ -+static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) -+{ -+ return -ENOIOCTLCMD; -+} -+ -+ -+ -+/* ======================== Card services HCI interaction ======================== */ -+ -+ -+int bluecard_open(bluecard_info_t *info) -+{ -+ unsigned int iobase = info->link.io.BasePort1; -+ struct hci_dev *hdev; -+ unsigned char id; -+ -+ spin_lock_init(&(info->lock)); -+ -+ init_timer(&(info->timer)); -+ info->timer.function = &bluecard_activity_led_timeout; -+ info->timer.data = (u_long)info; -+ -+ skb_queue_head_init(&(info->txq)); -+ -+ info->rx_state = RECV_WAIT_PACKET_TYPE; -+ info->rx_count = 0; -+ info->rx_skb = NULL; -+ -+ id = inb(iobase + 0x30); -+ -+ if ((id & 0x0f) == 0x02) -+ set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)); -+ -+ if (id & 0x10) -+ set_bit(CARD_HAS_POWER_LED, &(info->hw_state)); -+ -+ if (id & 0x20) -+ set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state)); -+ -+ /* Reset card */ -+ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET; -+ outb(info->ctrl_reg, iobase + REG_CONTROL); -+ -+ /* Turn FPGA off */ -+ outb(0x80, iobase + 0x30); -+ -+ /* Wait some time */ -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ / 100); -+ -+ /* Turn FPGA on */ -+ outb(0x00, iobase + 0x30); -+ -+ /* Activate card */ -+ info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU; -+ outb(info->ctrl_reg, iobase + REG_CONTROL); -+ -+ /* Enable interrupt */ -+ outb(0xff, iobase + REG_INTERRUPT); -+ info->ctrl_reg |= REG_CONTROL_INTERRUPT; -+ outb(info->ctrl_reg, iobase + REG_CONTROL); -+ -+ /* Start the RX buffers */ -+ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND); -+ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND); -+ -+ /* Signal that the hardware is ready */ -+ set_bit(CARD_READY, &(info->hw_state)); -+ -+ /* Drop TX queue */ -+ skb_queue_purge(&(info->txq)); -+ -+ /* Control the point at which RTS is enabled */ -+ outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL); -+ -+ /* Timeout before it is safe to send the first HCI packet */ -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout((HZ * 5) / 4); // or set it to 3/2 -+ -+ -+ /* Initialize and register HCI device */ -+ -+ hdev = &(info->hdev); -+ -+ hdev->type = HCI_PCCARD; -+ hdev->driver_data = info; -+ -+ hdev->open = bluecard_hci_open; -+ hdev->close = bluecard_hci_close; -+ hdev->flush = bluecard_hci_flush; -+ hdev->send = bluecard_hci_send_frame; -+ hdev->destruct = bluecard_hci_destruct; -+ hdev->ioctl = bluecard_hci_ioctl; -+ -+ if (hci_register_dev(hdev) < 0) { -+ printk(KERN_WARNING "bluecard_cs: Can't register HCI device %s.\n", hdev->name); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+ -+int bluecard_close(bluecard_info_t *info) -+{ -+ unsigned int iobase = info->link.io.BasePort1; -+ struct hci_dev *hdev = &(info->hdev); -+ -+ bluecard_hci_close(hdev); -+ -+ clear_bit(CARD_READY, &(info->hw_state)); -+ -+ /* Reset card */ -+ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET; -+ outb(info->ctrl_reg, iobase + REG_CONTROL); -+ -+ /* Turn FPGA off */ -+ outb(0x80, iobase + 0x30); -+ -+ if (hci_unregister_dev(hdev) < 0) -+ printk(KERN_WARNING "bluecard_cs: Can't unregister HCI device %s.\n", hdev->name); -+ -+ return 0; -+} -+ -+ -+ -+/* ======================== Card services ======================== */ -+ -+ -+static void cs_error(client_handle_t handle, int func, int ret) -+{ -+ error_info_t err = { func, ret }; -+ -+ CardServices(ReportError, handle, &err); -+} -+ -+ -+dev_link_t *bluecard_attach(void) -+{ -+ bluecard_info_t *info; -+ client_reg_t client_reg; -+ dev_link_t *link; -+ int i, ret; -+ -+ /* Create new info device */ -+ info = kmalloc(sizeof(*info), GFP_KERNEL); -+ if (!info) -+ return NULL; -+ memset(info, 0, sizeof(*info)); -+ -+ link = &info->link; -+ link->priv = info; -+ -+ link->release.function = &bluecard_release; -+ link->release.data = (u_long)link; -+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; -+ link->io.NumPorts1 = 8; -+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; -+ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; -+ -+ if (irq_list[0] == -1) -+ link->irq.IRQInfo2 = irq_mask; -+ else -+ for (i = 0; i < 4; i++) -+ link->irq.IRQInfo2 |= 1 << irq_list[i]; -+ -+ link->irq.Handler = bluecard_interrupt; -+ link->irq.Instance = info; -+ -+ link->conf.Attributes = CONF_ENABLE_IRQ; -+ link->conf.Vcc = 50; -+ link->conf.IntType = INT_MEMORY_AND_IO; -+ -+ /* Register with Card Services */ -+ link->next = dev_list; -+ dev_list = link; -+ client_reg.dev_info = &dev_info; -+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; -+ client_reg.EventMask = -+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | -+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | -+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; -+ client_reg.event_handler = &bluecard_event; -+ client_reg.Version = 0x0210; -+ client_reg.event_callback_args.client_data = link; -+ -+ ret = CardServices(RegisterClient, &link->handle, &client_reg); -+ if (ret != CS_SUCCESS) { -+ cs_error(link->handle, RegisterClient, ret); -+ bluecard_detach(link); -+ return NULL; -+ } -+ -+ return link; -+} -+ -+ -+void bluecard_detach(dev_link_t *link) -+{ -+ bluecard_info_t *info = link->priv; -+ dev_link_t **linkp; -+ int ret; -+ -+ /* Locate device structure */ -+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) -+ if (*linkp == link) -+ break; -+ -+ if (*linkp == NULL) -+ return; -+ -+ del_timer(&link->release); -+ if (link->state & DEV_CONFIG) -+ bluecard_release((u_long)link); -+ -+ if (link->handle) { -+ ret = CardServices(DeregisterClient, link->handle); -+ if (ret != CS_SUCCESS) -+ cs_error(link->handle, DeregisterClient, ret); -+ } -+ -+ /* Unlink device structure, free bits */ -+ *linkp = link->next; -+ -+ kfree(info); -+} -+ -+ -+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse) -+{ -+ int i; -+ -+ i = CardServices(fn, handle, tuple); -+ if (i != CS_SUCCESS) -+ return CS_NO_MORE_ITEMS; -+ -+ i = CardServices(GetTupleData, handle, tuple); -+ if (i != CS_SUCCESS) -+ return i; -+ -+ return CardServices(ParseTuple, handle, tuple, parse); -+} -+ -+ -+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) -+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) -+ -+void bluecard_config(dev_link_t *link) -+{ -+ client_handle_t handle = link->handle; -+ bluecard_info_t *info = link->priv; -+ tuple_t tuple; -+ u_short buf[256]; -+ cisparse_t parse; -+ config_info_t config; -+ int i, n, last_ret, last_fn; -+ -+ tuple.TupleData = (cisdata_t *)buf; -+ tuple.TupleOffset = 0; -+ tuple.TupleDataMax = 255; -+ tuple.Attributes = 0; -+ -+ /* Get configuration register information */ -+ tuple.DesiredTuple = CISTPL_CONFIG; -+ last_ret = first_tuple(handle, &tuple, &parse); -+ if (last_ret != CS_SUCCESS) { -+ last_fn = ParseTuple; -+ goto cs_failed; -+ } -+ link->conf.ConfigBase = parse.config.base; -+ link->conf.Present = parse.config.rmask[0]; -+ -+ /* Configure card */ -+ link->state |= DEV_CONFIG; -+ i = CardServices(GetConfigurationInfo, handle, &config); -+ link->conf.Vcc = config.Vcc; -+ -+ link->conf.ConfigIndex = 0x20; -+ link->io.NumPorts1 = 64; -+ link->io.IOAddrLines = 6; -+ -+ for (n = 0; n < 0x400; n += 0x40) { -+ link->io.BasePort1 = n ^ 0x300; -+ i = CardServices(RequestIO, link->handle, &link->io); -+ if (i == CS_SUCCESS) -+ break; -+ } -+ -+ if (i != CS_SUCCESS) { -+ cs_error(link->handle, RequestIO, i); -+ goto failed; -+ } -+ -+ i = CardServices(RequestIRQ, link->handle, &link->irq); -+ if (i != CS_SUCCESS) { -+ cs_error(link->handle, RequestIRQ, i); -+ link->irq.AssignedIRQ = 0; -+ } -+ -+ i = CardServices(RequestConfiguration, link->handle, &link->conf); -+ if (i != CS_SUCCESS) { -+ cs_error(link->handle, RequestConfiguration, i); -+ goto failed; -+ } -+ -+ MOD_INC_USE_COUNT; -+ -+ if (bluecard_open(info) != 0) -+ goto failed; -+ -+ strcpy(info->node.dev_name, info->hdev.name); -+ link->dev = &info->node; -+ link->state &= ~DEV_CONFIG_PENDING; -+ -+ return; -+ -+cs_failed: -+ cs_error(link->handle, last_fn, last_ret); -+ -+failed: -+ bluecard_release((u_long)link); -+} -+ -+ -+void bluecard_release(u_long arg) -+{ -+ dev_link_t *link = (dev_link_t *)arg; -+ bluecard_info_t *info = link->priv; -+ -+ if (link->state & DEV_PRESENT) -+ bluecard_close(info); -+ -+ MOD_DEC_USE_COUNT; -+ -+ link->dev = NULL; -+ -+ CardServices(ReleaseConfiguration, link->handle); -+ CardServices(ReleaseIO, link->handle, &link->io); -+ CardServices(ReleaseIRQ, link->handle, &link->irq); -+ -+ link->state &= ~DEV_CONFIG; -+} -+ -+ -+int bluecard_event(event_t event, int priority, event_callback_args_t *args) -+{ -+ dev_link_t *link = args->client_data; -+ bluecard_info_t *info = link->priv; -+ -+ switch (event) { -+ case CS_EVENT_CARD_REMOVAL: -+ link->state &= ~DEV_PRESENT; -+ if (link->state & DEV_CONFIG) { -+ bluecard_close(info); -+ mod_timer(&link->release, jiffies + HZ / 20); -+ } -+ break; -+ case CS_EVENT_CARD_INSERTION: -+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; -+ bluecard_config(link); -+ break; -+ case CS_EVENT_PM_SUSPEND: -+ link->state |= DEV_SUSPEND; -+ /* Fall through... */ -+ case CS_EVENT_RESET_PHYSICAL: -+ if (link->state & DEV_CONFIG) -+ CardServices(ReleaseConfiguration, link->handle); -+ break; -+ case CS_EVENT_PM_RESUME: -+ link->state &= ~DEV_SUSPEND; -+ /* Fall through... */ -+ case CS_EVENT_CARD_RESET: -+ if (DEV_OK(link)) -+ CardServices(RequestConfiguration, link->handle, &link->conf); -+ break; -+ } -+ -+ return 0; -+} -+ -+ -+ -+/* ======================== Module initialization ======================== */ -+ -+ -+int __init init_bluecard_cs(void) -+{ -+ servinfo_t serv; -+ int err; -+ -+ CardServices(GetCardServicesInfo, &serv); -+ if (serv.Revision != CS_RELEASE_CODE) { -+ printk(KERN_NOTICE "bluecard_cs: Card Services release does not match!\n"); -+ return -1; -+ } -+ -+ err = register_pccard_driver(&dev_info, &bluecard_attach, &bluecard_detach); -+ -+ return err; -+} -+ -+ -+void __exit exit_bluecard_cs(void) -+{ -+ unregister_pccard_driver(&dev_info); -+ -+ while (dev_list != NULL) -+ bluecard_detach(dev_list); -+} -+ -+ -+module_init(init_bluecard_cs); -+module_exit(exit_bluecard_cs); -+ -+EXPORT_NO_SYMBOLS; ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/bt3c_cs.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,946 @@ -+/* -+ * -+ * Driver for the 3Com Bluetooth PCMCIA card -+ * -+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org> -+ * Jose Orlando Pereira <jop@di.uminho.pt> -+ * -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation; -+ * -+ * Software distributed under the License is distributed on an "AS -+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -+ * implied. See the License for the specific language governing -+ * rights and limitations under the License. -+ * -+ * The initial developer of the original code is David A. Hinds -+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds -+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#define __KERNEL_SYSCALLS__ -+ -+#include <linux/kernel.h> -+#include <linux/kmod.h> -+#include <linux/init.h> -+#include <linux/slab.h> -+#include <linux/types.h> -+#include <linux/sched.h> -+#include <linux/delay.h> -+#include <linux/timer.h> -+#include <linux/errno.h> -+#include <linux/unistd.h> -+#include <linux/ptrace.h> -+#include <linux/ioport.h> -+#include <linux/spinlock.h> -+ -+#include <linux/skbuff.h> -+#include <linux/string.h> -+#include <linux/serial.h> -+#include <linux/serial_reg.h> -+#include <asm/system.h> -+#include <asm/bitops.h> -+#include <asm/io.h> -+ -+#include <pcmcia/version.h> -+#include <pcmcia/cs_types.h> -+#include <pcmcia/cs.h> -+#include <pcmcia/cistpl.h> -+#include <pcmcia/ciscode.h> -+#include <pcmcia/ds.h> -+#include <pcmcia/cisreg.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+ -+ -+ -+/* ======================== Module parameters ======================== */ -+ -+ -+/* Bit map of interrupts to choose from */ -+static u_int irq_mask = 0xffff; -+static int irq_list[4] = { -1 }; -+ -+MODULE_PARM(irq_mask, "i"); -+MODULE_PARM(irq_list, "1-4i"); -+ -+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>, Jose Orlando Pereira <jop@di.uminho.pt>"); -+MODULE_DESCRIPTION("BlueZ driver for the 3Com Bluetooth PCMCIA card"); -+MODULE_LICENSE("GPL"); -+ -+ -+ -+/* ======================== Local structures ======================== */ -+ -+ -+typedef struct bt3c_info_t { -+ dev_link_t link; -+ dev_node_t node; -+ -+ struct hci_dev hdev; -+ -+ spinlock_t lock; /* For serializing operations */ -+ -+ struct sk_buff_head txq; -+ unsigned long tx_state; -+ -+ unsigned long rx_state; -+ unsigned long rx_count; -+ struct sk_buff *rx_skb; -+} bt3c_info_t; -+ -+ -+void bt3c_config(dev_link_t *link); -+void bt3c_release(u_long arg); -+int bt3c_event(event_t event, int priority, event_callback_args_t *args); -+ -+static dev_info_t dev_info = "bt3c_cs"; -+ -+dev_link_t *bt3c_attach(void); -+void bt3c_detach(dev_link_t *); -+ -+static dev_link_t *dev_list = NULL; -+ -+ -+/* Transmit states */ -+#define XMIT_SENDING 1 -+#define XMIT_WAKEUP 2 -+#define XMIT_WAITING 8 -+ -+/* Receiver states */ -+#define RECV_WAIT_PACKET_TYPE 0 -+#define RECV_WAIT_EVENT_HEADER 1 -+#define RECV_WAIT_ACL_HEADER 2 -+#define RECV_WAIT_SCO_HEADER 3 -+#define RECV_WAIT_DATA 4 -+ -+ -+ -+/* ======================== Special I/O functions ======================== */ -+ -+ -+#define DATA_L 0 -+#define DATA_H 1 -+#define ADDR_L 2 -+#define ADDR_H 3 -+#define CONTROL 4 -+ -+ -+inline void bt3c_address(unsigned int iobase, unsigned short addr) -+{ -+ outb(addr & 0xff, iobase + ADDR_L); -+ outb((addr >> 8) & 0xff, iobase + ADDR_H); -+} -+ -+ -+inline void bt3c_put(unsigned int iobase, unsigned short value) -+{ -+ outb(value & 0xff, iobase + DATA_L); -+ outb((value >> 8) & 0xff, iobase + DATA_H); -+} -+ -+ -+inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value) -+{ -+ bt3c_address(iobase, addr); -+ bt3c_put(iobase, value); -+} -+ -+ -+inline unsigned short bt3c_get(unsigned int iobase) -+{ -+ unsigned short value = inb(iobase + DATA_L); -+ -+ value |= inb(iobase + DATA_H) << 8; -+ -+ return value; -+} -+ -+ -+inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr) -+{ -+ bt3c_address(iobase, addr); -+ -+ return bt3c_get(iobase); -+} -+ -+ -+ -+/* ======================== Interrupt handling ======================== */ -+ -+ -+static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) -+{ -+ int actual = 0; -+ -+ bt3c_address(iobase, 0x7080); -+ -+ /* Fill FIFO with current frame */ -+ while (actual < len) { -+ /* Transmit next byte */ -+ bt3c_put(iobase, buf[actual]); -+ actual++; -+ } -+ -+ bt3c_io_write(iobase, 0x7005, actual); -+ -+ return actual; -+} -+ -+ -+static void bt3c_write_wakeup(bt3c_info_t *info, int from) -+{ -+ unsigned long flags; -+ -+ if (!info) { -+ printk(KERN_WARNING "bt3c_cs: Call of write_wakeup for unknown device.\n"); -+ return; -+ } -+ -+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) -+ return; -+ -+ spin_lock_irqsave(&(info->lock), flags); -+ -+ do { -+ register unsigned int iobase = info->link.io.BasePort1; -+ register struct sk_buff *skb; -+ register int len; -+ -+ if (!(info->link.state & DEV_PRESENT)) -+ break; -+ -+ -+ if (!(skb = skb_dequeue(&(info->txq)))) { -+ clear_bit(XMIT_SENDING, &(info->tx_state)); -+ break; -+ } -+ -+ /* Send frame */ -+ len = bt3c_write(iobase, 256, skb->data, skb->len); -+ -+ if (len != skb->len) { -+ printk(KERN_WARNING "bt3c_cs: very strange\n"); -+ } -+ -+ kfree_skb(skb); -+ -+ info->hdev.stat.byte_tx += len; -+ -+ } while (0); -+ -+ spin_unlock_irqrestore(&(info->lock), flags); -+} -+ -+ -+static void bt3c_receive(bt3c_info_t *info) -+{ -+ unsigned int iobase; -+ int size = 0, avail; -+ -+ if (!info) { -+ printk(KERN_WARNING "bt3c_cs: Call of receive for unknown device.\n"); -+ return; -+ } -+ -+ iobase = info->link.io.BasePort1; -+ -+ avail = bt3c_read(iobase, 0x7006); -+ //printk("bt3c_cs: receiving %d bytes\n", avail); -+ -+ bt3c_address(iobase, 0x7480); -+ while (size < avail) { -+ size++; -+ info->hdev.stat.byte_rx++; -+ -+ /* Allocate packet */ -+ if (info->rx_skb == NULL) { -+ info->rx_state = RECV_WAIT_PACKET_TYPE; -+ info->rx_count = 0; -+ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { -+ printk(KERN_WARNING "bt3c_cs: Can't allocate mem for new packet.\n"); -+ return; -+ } -+ } -+ -+ -+ if (info->rx_state == RECV_WAIT_PACKET_TYPE) { -+ -+ info->rx_skb->dev = (void *)&(info->hdev); -+ info->rx_skb->pkt_type = inb(iobase + DATA_L); -+ inb(iobase + DATA_H); -+ //printk("bt3c: PACKET_TYPE=%02x\n", info->rx_skb->pkt_type); -+ -+ switch (info->rx_skb->pkt_type) { -+ -+ case HCI_EVENT_PKT: -+ info->rx_state = RECV_WAIT_EVENT_HEADER; -+ info->rx_count = HCI_EVENT_HDR_SIZE; -+ break; -+ -+ case HCI_ACLDATA_PKT: -+ info->rx_state = RECV_WAIT_ACL_HEADER; -+ info->rx_count = HCI_ACL_HDR_SIZE; -+ break; -+ -+ case HCI_SCODATA_PKT: -+ info->rx_state = RECV_WAIT_SCO_HEADER; -+ info->rx_count = HCI_SCO_HDR_SIZE; -+ break; -+ -+ default: -+ /* Unknown packet */ -+ printk(KERN_WARNING "bt3c_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type); -+ info->hdev.stat.err_rx++; -+ clear_bit(HCI_RUNNING, &(info->hdev.flags)); -+ -+ kfree_skb(info->rx_skb); -+ info->rx_skb = NULL; -+ break; -+ -+ } -+ -+ } else { -+ -+ __u8 x = inb(iobase + DATA_L); -+ -+ *skb_put(info->rx_skb, 1) = x; -+ inb(iobase + DATA_H); -+ info->rx_count--; -+ -+ if (info->rx_count == 0) { -+ -+ int dlen; -+ hci_event_hdr *eh; -+ hci_acl_hdr *ah; -+ hci_sco_hdr *sh; -+ -+ switch (info->rx_state) { -+ -+ case RECV_WAIT_EVENT_HEADER: -+ eh = (hci_event_hdr *)(info->rx_skb->data); -+ info->rx_state = RECV_WAIT_DATA; -+ info->rx_count = eh->plen; -+ break; -+ -+ case RECV_WAIT_ACL_HEADER: -+ ah = (hci_acl_hdr *)(info->rx_skb->data); -+ dlen = __le16_to_cpu(ah->dlen); -+ info->rx_state = RECV_WAIT_DATA; -+ info->rx_count = dlen; -+ break; -+ -+ case RECV_WAIT_SCO_HEADER: -+ sh = (hci_sco_hdr *)(info->rx_skb->data); -+ info->rx_state = RECV_WAIT_DATA; -+ info->rx_count = sh->dlen; -+ break; -+ -+ case RECV_WAIT_DATA: -+ hci_recv_frame(info->rx_skb); -+ info->rx_skb = NULL; -+ break; -+ -+ } -+ -+ } -+ -+ } -+ -+ } -+ -+ bt3c_io_write(iobase, 0x7006, 0x0000); -+} -+ -+ -+void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs) -+{ -+ bt3c_info_t *info = dev_inst; -+ unsigned int iobase; -+ int iir; -+ -+ if (!info) { -+ printk(KERN_WARNING "bt3c_cs: Call of irq %d for unknown device.\n", irq); -+ return; -+ } -+ -+ iobase = info->link.io.BasePort1; -+ -+ spin_lock(&(info->lock)); -+ -+ iir = inb(iobase + CONTROL); -+ if (iir & 0x80) { -+ int stat = bt3c_read(iobase, 0x7001); -+ -+ if ((stat & 0xff) == 0x7f) { -+ printk(KERN_WARNING "bt3c_cs: STRANGE stat=%04x\n", stat); -+ } else if ((stat & 0xff) != 0xff) { -+ if (stat & 0x0020) { -+ int stat = bt3c_read(iobase, 0x7002) & 0x10; -+ printk(KERN_WARNING "bt3c_cs: antena %s\n", stat ? "OUT" : "IN"); -+ } -+ if (stat & 0x0001) -+ bt3c_receive(info); -+ if (stat & 0x0002) { -+ //printk("bt3c_cs: ACK %04x\n", stat); -+ clear_bit(XMIT_SENDING, &(info->tx_state)); -+ bt3c_write_wakeup(info, 1); -+ } -+ -+ bt3c_io_write(iobase, 0x7001, 0x0000); -+ -+ outb(iir, iobase + CONTROL); -+ } -+ } -+ -+ spin_unlock(&(info->lock)); -+} -+ -+ -+ -+ -+/* ======================== HCI interface ======================== */ -+ -+ -+static int bt3c_hci_flush(struct hci_dev *hdev) -+{ -+ bt3c_info_t *info = (bt3c_info_t *)(hdev->driver_data); -+ -+ /* Drop TX queue */ -+ skb_queue_purge(&(info->txq)); -+ -+ return 0; -+} -+ -+ -+static int bt3c_hci_open(struct hci_dev *hdev) -+{ -+ set_bit(HCI_RUNNING, &(hdev->flags)); -+ -+ return 0; -+} -+ -+ -+static int bt3c_hci_close(struct hci_dev *hdev) -+{ -+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) -+ return 0; -+ -+ bt3c_hci_flush(hdev); -+ -+ return 0; -+} -+ -+ -+static int bt3c_hci_send_frame(struct sk_buff *skb) -+{ -+ bt3c_info_t *info; -+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev); -+ -+ if (!hdev) { -+ printk(KERN_WARNING "bt3c_cs: Frame for unknown HCI device (hdev=NULL)."); -+ return -ENODEV; -+ } -+ -+ info = (bt3c_info_t *) (hdev->driver_data); -+ -+ switch (skb->pkt_type) { -+ case HCI_COMMAND_PKT: -+ hdev->stat.cmd_tx++; -+ break; -+ case HCI_ACLDATA_PKT: -+ hdev->stat.acl_tx++; -+ break; -+ case HCI_SCODATA_PKT: -+ hdev->stat.sco_tx++; -+ break; -+ }; -+ -+ /* Prepend skb with frame type */ -+ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1); -+ skb_queue_tail(&(info->txq), skb); -+ -+ bt3c_write_wakeup(info, 0); -+ -+ return 0; -+} -+ -+ -+static void bt3c_hci_destruct(struct hci_dev *hdev) -+{ -+} -+ -+ -+static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) -+{ -+ return -ENOIOCTLCMD; -+} -+ -+ -+ -+/* ======================== User mode firmware loader ======================== */ -+ -+ -+#define FW_LOADER "/sbin/bluefw" -+static int errno; -+ -+ -+static int bt3c_fw_loader_exec(void *dev) -+{ -+ char *argv[] = { FW_LOADER, "pccard", dev, NULL }; -+ char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; -+ int err; -+ -+ err = exec_usermodehelper(FW_LOADER, argv, envp); -+ if (err) -+ printk(KERN_WARNING "bt3c_cs: Failed to exec \"%s pccard %s\".\n", FW_LOADER, (char *)dev); -+ -+ return err; -+} -+ -+ -+static int bt3c_firmware_load(bt3c_info_t *info) -+{ -+ sigset_t tmpsig; -+ char dev[16]; -+ pid_t pid; -+ int result; -+ -+ /* Check if root fs is mounted */ -+ if (!current->fs->root) { -+ printk(KERN_WARNING "bt3c_cs: Root filesystem is not mounted.\n"); -+ return -EPERM; -+ } -+ -+ sprintf(dev, "%04x", info->link.io.BasePort1); -+ -+ pid = kernel_thread(bt3c_fw_loader_exec, (void *)dev, 0); -+ if (pid < 0) { -+ printk(KERN_WARNING "bt3c_cs: Forking of kernel thread failed (errno=%d).\n", -pid); -+ return pid; -+ } -+ -+ /* Block signals, everything but SIGKILL/SIGSTOP */ -+ spin_lock_irq(¤t->sigmask_lock); -+ tmpsig = current->blocked; -+ siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)); -+ recalc_sigpending(current); -+ spin_unlock_irq(¤t->sigmask_lock); -+ -+ result = waitpid(pid, NULL, __WCLONE); -+ -+ /* Allow signals again */ -+ spin_lock_irq(¤t->sigmask_lock); -+ current->blocked = tmpsig; -+ recalc_sigpending(current); -+ spin_unlock_irq(¤t->sigmask_lock); -+ -+ if (result != pid) { -+ printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result); -+ return -result; -+ } -+ -+ return 0; -+} -+ -+ -+ -+/* ======================== Card services HCI interaction ======================== */ -+ -+ -+int bt3c_open(bt3c_info_t *info) -+{ -+ struct hci_dev *hdev; -+ int err; -+ -+ spin_lock_init(&(info->lock)); -+ -+ skb_queue_head_init(&(info->txq)); -+ -+ info->rx_state = RECV_WAIT_PACKET_TYPE; -+ info->rx_count = 0; -+ info->rx_skb = NULL; -+ -+ /* Load firmware */ -+ -+ if ((err = bt3c_firmware_load(info)) < 0) -+ return err; -+ -+ /* Timeout before it is safe to send the first HCI packet */ -+ -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ); -+ -+ -+ /* Initialize and register HCI device */ -+ -+ hdev = &(info->hdev); -+ -+ hdev->type = HCI_PCCARD; -+ hdev->driver_data = info; -+ -+ hdev->open = bt3c_hci_open; -+ hdev->close = bt3c_hci_close; -+ hdev->flush = bt3c_hci_flush; -+ hdev->send = bt3c_hci_send_frame; -+ hdev->destruct = bt3c_hci_destruct; -+ hdev->ioctl = bt3c_hci_ioctl; -+ -+ if (hci_register_dev(hdev) < 0) { -+ printk(KERN_WARNING "bt3c_cs: Can't register HCI device %s.\n", hdev->name); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+ -+int bt3c_close(bt3c_info_t *info) -+{ -+ struct hci_dev *hdev = &(info->hdev); -+ -+ bt3c_hci_close(hdev); -+ -+ if (hci_unregister_dev(hdev) < 0) -+ printk(KERN_WARNING "bt3c_cs: Can't unregister HCI device %s.\n", hdev->name); -+ -+ return 0; -+} -+ -+ -+ -+/* ======================== Card services ======================== */ -+ -+ -+static void cs_error(client_handle_t handle, int func, int ret) -+{ -+ error_info_t err = { func, ret }; -+ -+ CardServices(ReportError, handle, &err); -+} -+ -+ -+dev_link_t *bt3c_attach(void) -+{ -+ bt3c_info_t *info; -+ client_reg_t client_reg; -+ dev_link_t *link; -+ int i, ret; -+ -+ /* Create new info device */ -+ info = kmalloc(sizeof(*info), GFP_KERNEL); -+ if (!info) -+ return NULL; -+ memset(info, 0, sizeof(*info)); -+ -+ link = &info->link; -+ link->priv = info; -+ -+ link->release.function = &bt3c_release; -+ link->release.data = (u_long)link; -+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; -+ link->io.NumPorts1 = 8; -+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; -+ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; -+ -+ if (irq_list[0] == -1) -+ link->irq.IRQInfo2 = irq_mask; -+ else -+ for (i = 0; i < 4; i++) -+ link->irq.IRQInfo2 |= 1 << irq_list[i]; -+ -+ link->irq.Handler = bt3c_interrupt; -+ link->irq.Instance = info; -+ -+ link->conf.Attributes = CONF_ENABLE_IRQ; -+ link->conf.Vcc = 50; -+ link->conf.IntType = INT_MEMORY_AND_IO; -+ -+ /* Register with Card Services */ -+ link->next = dev_list; -+ dev_list = link; -+ client_reg.dev_info = &dev_info; -+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; -+ client_reg.EventMask = -+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | -+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | -+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; -+ client_reg.event_handler = &bt3c_event; -+ client_reg.Version = 0x0210; -+ client_reg.event_callback_args.client_data = link; -+ -+ ret = CardServices(RegisterClient, &link->handle, &client_reg); -+ if (ret != CS_SUCCESS) { -+ cs_error(link->handle, RegisterClient, ret); -+ bt3c_detach(link); -+ return NULL; -+ } -+ -+ return link; -+} -+ -+ -+void bt3c_detach(dev_link_t *link) -+{ -+ bt3c_info_t *info = link->priv; -+ dev_link_t **linkp; -+ int ret; -+ -+ /* Locate device structure */ -+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) -+ if (*linkp == link) -+ break; -+ -+ if (*linkp == NULL) -+ return; -+ -+ del_timer(&link->release); -+ -+ if (link->state & DEV_CONFIG) -+ bt3c_release((u_long)link); -+ -+ if (link->handle) { -+ ret = CardServices(DeregisterClient, link->handle); -+ if (ret != CS_SUCCESS) -+ cs_error(link->handle, DeregisterClient, ret); -+ } -+ -+ /* Unlink device structure, free bits */ -+ *linkp = link->next; -+ -+ kfree(info); -+} -+ -+ -+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse) -+{ -+ int i; -+ -+ i = CardServices(fn, handle, tuple); -+ if (i != CS_SUCCESS) -+ return CS_NO_MORE_ITEMS; -+ -+ i = CardServices(GetTupleData, handle, tuple); -+ if (i != CS_SUCCESS) -+ return i; -+ -+ return CardServices(ParseTuple, handle, tuple, parse); -+} -+ -+ -+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) -+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) -+ -+void bt3c_config(dev_link_t *link) -+{ -+ static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; -+ client_handle_t handle = link->handle; -+ bt3c_info_t *info = link->priv; -+ tuple_t tuple; -+ u_short buf[256]; -+ cisparse_t parse; -+ cistpl_cftable_entry_t *cf = &parse.cftable_entry; -+ config_info_t config; -+ int i, j, try, last_ret, last_fn; -+ -+ tuple.TupleData = (cisdata_t *)buf; -+ tuple.TupleOffset = 0; -+ tuple.TupleDataMax = 255; -+ tuple.Attributes = 0; -+ -+ /* Get configuration register information */ -+ tuple.DesiredTuple = CISTPL_CONFIG; -+ last_ret = first_tuple(handle, &tuple, &parse); -+ if (last_ret != CS_SUCCESS) { -+ last_fn = ParseTuple; -+ goto cs_failed; -+ } -+ link->conf.ConfigBase = parse.config.base; -+ link->conf.Present = parse.config.rmask[0]; -+ -+ /* Configure card */ -+ link->state |= DEV_CONFIG; -+ i = CardServices(GetConfigurationInfo, handle, &config); -+ link->conf.Vcc = config.Vcc; -+ -+ /* First pass: look for a config entry that looks normal. */ -+ tuple.TupleData = (cisdata_t *)buf; -+ tuple.TupleOffset = 0; -+ tuple.TupleDataMax = 255; -+ tuple.Attributes = 0; -+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; -+ /* Two tries: without IO aliases, then with aliases */ -+ for (try = 0; try < 2; try++) { -+ i = first_tuple(handle, &tuple, &parse); -+ while (i != CS_NO_MORE_ITEMS) { -+ if (i != CS_SUCCESS) -+ goto next_entry; -+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) -+ link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; -+ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) { -+ link->conf.ConfigIndex = cf->index; -+ link->io.BasePort1 = cf->io.win[0].base; -+ link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; -+ i = CardServices(RequestIO, link->handle, &link->io); -+ if (i == CS_SUCCESS) -+ goto found_port; -+ } -+next_entry: -+ i = next_tuple(handle, &tuple, &parse); -+ } -+ } -+ -+ /* Second pass: try to find an entry that isn't picky about -+ its base address, then try to grab any standard serial port -+ address, and finally try to get any free port. */ -+ i = first_tuple(handle, &tuple, &parse); -+ while (i != CS_NO_MORE_ITEMS) { -+ if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { -+ link->conf.ConfigIndex = cf->index; -+ for (j = 0; j < 5; j++) { -+ link->io.BasePort1 = base[j]; -+ link->io.IOAddrLines = base[j] ? 16 : 3; -+ i = CardServices(RequestIO, link->handle, &link->io); -+ if (i == CS_SUCCESS) -+ goto found_port; -+ } -+ } -+ i = next_tuple(handle, &tuple, &parse); -+ } -+ -+found_port: -+ if (i != CS_SUCCESS) { -+ printk(KERN_NOTICE "bt3c_cs: No usable port range found. Giving up.\n"); -+ cs_error(link->handle, RequestIO, i); -+ goto failed; -+ } -+ -+ i = CardServices(RequestIRQ, link->handle, &link->irq); -+ if (i != CS_SUCCESS) { -+ cs_error(link->handle, RequestIRQ, i); -+ link->irq.AssignedIRQ = 0; -+ } -+ -+ i = CardServices(RequestConfiguration, link->handle, &link->conf); -+ if (i != CS_SUCCESS) { -+ cs_error(link->handle, RequestConfiguration, i); -+ goto failed; -+ } -+ -+ MOD_INC_USE_COUNT; -+ -+ if (bt3c_open(info) != 0) -+ goto failed; -+ -+ strcpy(info->node.dev_name, info->hdev.name); -+ link->dev = &info->node; -+ link->state &= ~DEV_CONFIG_PENDING; -+ -+ return; -+ -+cs_failed: -+ cs_error(link->handle, last_fn, last_ret); -+ -+failed: -+ bt3c_release((u_long)link); -+} -+ -+ -+void bt3c_release(u_long arg) -+{ -+ dev_link_t *link = (dev_link_t *)arg; -+ bt3c_info_t *info = link->priv; -+ -+ if (link->state & DEV_PRESENT) -+ bt3c_close(info); -+ -+ MOD_DEC_USE_COUNT; -+ -+ link->dev = NULL; -+ -+ CardServices(ReleaseConfiguration, link->handle); -+ CardServices(ReleaseIO, link->handle, &link->io); -+ CardServices(ReleaseIRQ, link->handle, &link->irq); -+ -+ link->state &= ~DEV_CONFIG; -+} -+ -+ -+int bt3c_event(event_t event, int priority, event_callback_args_t *args) -+{ -+ dev_link_t *link = args->client_data; -+ bt3c_info_t *info = link->priv; -+ -+ switch (event) { -+ case CS_EVENT_CARD_REMOVAL: -+ link->state &= ~DEV_PRESENT; -+ if (link->state & DEV_CONFIG) { -+ bt3c_close(info); -+ mod_timer(&link->release, jiffies + HZ / 20); -+ } -+ break; -+ case CS_EVENT_CARD_INSERTION: -+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; -+ bt3c_config(link); -+ break; -+ case CS_EVENT_PM_SUSPEND: -+ link->state |= DEV_SUSPEND; -+ /* Fall through... */ -+ case CS_EVENT_RESET_PHYSICAL: -+ if (link->state & DEV_CONFIG) -+ CardServices(ReleaseConfiguration, link->handle); -+ break; -+ case CS_EVENT_PM_RESUME: -+ link->state &= ~DEV_SUSPEND; -+ /* Fall through... */ -+ case CS_EVENT_CARD_RESET: -+ if (DEV_OK(link)) -+ CardServices(RequestConfiguration, link->handle, &link->conf); -+ break; -+ } -+ -+ return 0; -+} -+ -+ -+ -+/* ======================== Module initialization ======================== */ -+ -+ -+int __init init_bt3c_cs(void) -+{ -+ servinfo_t serv; -+ int err; -+ -+ CardServices(GetCardServicesInfo, &serv); -+ if (serv.Revision != CS_RELEASE_CODE) { -+ printk(KERN_NOTICE "bt3c_cs: Card Services release does not match!\n"); -+ return -1; -+ } -+ -+ err = register_pccard_driver(&dev_info, &bt3c_attach, &bt3c_detach); -+ -+ return err; -+} -+ -+ -+void __exit exit_bt3c_cs(void) -+{ -+ unregister_pccard_driver(&dev_info); -+ -+ while (dev_list != NULL) -+ bt3c_detach(dev_list); -+} -+ -+ -+module_init(init_bt3c_cs); -+module_exit(exit_bt3c_cs); -+ -+EXPORT_NO_SYMBOLS; ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/btuart_cs.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,906 @@ -+/* -+ * -+ * Driver for Bluetooth PCMCIA cards with HCI UART interface -+ * -+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org> -+ * -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation; -+ * -+ * Software distributed under the License is distributed on an "AS -+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -+ * implied. See the License for the specific language governing -+ * rights and limitations under the License. -+ * -+ * The initial developer of the original code is David A. Hinds -+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds -+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/slab.h> -+#include <linux/types.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/errno.h> -+#include <linux/ptrace.h> -+#include <linux/ioport.h> -+#include <linux/spinlock.h> -+ -+#include <linux/skbuff.h> -+#include <linux/string.h> -+#include <linux/serial.h> -+#include <linux/serial_reg.h> -+#include <asm/system.h> -+#include <asm/bitops.h> -+#include <asm/io.h> -+ -+#include <pcmcia/version.h> -+#include <pcmcia/cs_types.h> -+#include <pcmcia/cs.h> -+#include <pcmcia/cistpl.h> -+#include <pcmcia/ciscode.h> -+#include <pcmcia/ds.h> -+#include <pcmcia/cisreg.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+ -+ -+ -+/* ======================== Module parameters ======================== */ -+ -+ -+/* Bit map of interrupts to choose from */ -+static u_int irq_mask = 0xffff; -+static int irq_list[4] = { -1 }; -+ -+MODULE_PARM(irq_mask, "i"); -+MODULE_PARM(irq_list, "1-4i"); -+ -+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -+MODULE_DESCRIPTION("BlueZ driver for Bluetooth PCMCIA cards with HCI UART interface"); -+MODULE_LICENSE("GPL"); -+ -+ -+ -+/* ======================== Local structures ======================== */ -+ -+ -+typedef struct btuart_info_t { -+ dev_link_t link; -+ dev_node_t node; -+ -+ struct hci_dev hdev; -+ -+ spinlock_t lock; /* For serializing operations */ -+ -+ struct sk_buff_head txq; -+ unsigned long tx_state; -+ -+ unsigned long rx_state; -+ unsigned long rx_count; -+ struct sk_buff *rx_skb; -+} btuart_info_t; -+ -+ -+void btuart_config(dev_link_t *link); -+void btuart_release(u_long arg); -+int btuart_event(event_t event, int priority, event_callback_args_t *args); -+ -+static dev_info_t dev_info = "btuart_cs"; -+ -+dev_link_t *btuart_attach(void); -+void btuart_detach(dev_link_t *); -+ -+static dev_link_t *dev_list = NULL; -+ -+ -+/* Maximum baud rate */ -+#define SPEED_MAX 115200 -+ -+/* Default baud rate: 57600, 115200, 230400 or 460800 */ -+#define DEFAULT_BAUD_RATE 115200 -+ -+ -+/* Transmit states */ -+#define XMIT_SENDING 1 -+#define XMIT_WAKEUP 2 -+#define XMIT_WAITING 8 -+ -+/* Receiver states */ -+#define RECV_WAIT_PACKET_TYPE 0 -+#define RECV_WAIT_EVENT_HEADER 1 -+#define RECV_WAIT_ACL_HEADER 2 -+#define RECV_WAIT_SCO_HEADER 3 -+#define RECV_WAIT_DATA 4 -+ -+ -+ -+/* ======================== Interrupt handling ======================== */ -+ -+ -+static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) -+{ -+ int actual = 0; -+ -+ /* Tx FIFO should be empty */ -+ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE)) -+ return 0; -+ -+ /* Fill FIFO with current frame */ -+ while ((fifo_size-- > 0) && (actual < len)) { -+ /* Transmit next byte */ -+ outb(buf[actual], iobase + UART_TX); -+ actual++; -+ } -+ -+ return actual; -+} -+ -+ -+static void btuart_write_wakeup(btuart_info_t *info) -+{ -+ if (!info) { -+ printk(KERN_WARNING "btuart_cs: Call of write_wakeup for unknown device.\n"); -+ return; -+ } -+ -+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) { -+ set_bit(XMIT_WAKEUP, &(info->tx_state)); -+ return; -+ } -+ -+ do { -+ register unsigned int iobase = info->link.io.BasePort1; -+ register struct sk_buff *skb; -+ register int len; -+ -+ clear_bit(XMIT_WAKEUP, &(info->tx_state)); -+ -+ if (!(info->link.state & DEV_PRESENT)) -+ return; -+ -+ if (!(skb = skb_dequeue(&(info->txq)))) -+ break; -+ -+ /* Send frame */ -+ len = btuart_write(iobase, 16, skb->data, skb->len); -+ set_bit(XMIT_WAKEUP, &(info->tx_state)); -+ -+ if (len == skb->len) { -+ kfree_skb(skb); -+ } else { -+ skb_pull(skb, len); -+ skb_queue_head(&(info->txq), skb); -+ } -+ -+ info->hdev.stat.byte_tx += len; -+ -+ } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); -+ -+ clear_bit(XMIT_SENDING, &(info->tx_state)); -+} -+ -+ -+static void btuart_receive(btuart_info_t *info) -+{ -+ unsigned int iobase; -+ int boguscount = 0; -+ -+ if (!info) { -+ printk(KERN_WARNING "btuart_cs: Call of receive for unknown device.\n"); -+ return; -+ } -+ -+ iobase = info->link.io.BasePort1; -+ -+ do { -+ info->hdev.stat.byte_rx++; -+ -+ /* Allocate packet */ -+ if (info->rx_skb == NULL) { -+ info->rx_state = RECV_WAIT_PACKET_TYPE; -+ info->rx_count = 0; -+ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { -+ printk(KERN_WARNING "btuart_cs: Can't allocate mem for new packet.\n"); -+ return; -+ } -+ } -+ -+ if (info->rx_state == RECV_WAIT_PACKET_TYPE) { -+ -+ info->rx_skb->dev = (void *)&(info->hdev); -+ info->rx_skb->pkt_type = inb(iobase + UART_RX); -+ -+ switch (info->rx_skb->pkt_type) { -+ -+ case HCI_EVENT_PKT: -+ info->rx_state = RECV_WAIT_EVENT_HEADER; -+ info->rx_count = HCI_EVENT_HDR_SIZE; -+ break; -+ -+ case HCI_ACLDATA_PKT: -+ info->rx_state = RECV_WAIT_ACL_HEADER; -+ info->rx_count = HCI_ACL_HDR_SIZE; -+ break; -+ -+ case HCI_SCODATA_PKT: -+ info->rx_state = RECV_WAIT_SCO_HEADER; -+ info->rx_count = HCI_SCO_HDR_SIZE; -+ break; -+ -+ default: -+ /* Unknown packet */ -+ printk(KERN_WARNING "btuart_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type); -+ info->hdev.stat.err_rx++; -+ clear_bit(HCI_RUNNING, &(info->hdev.flags)); -+ -+ kfree_skb(info->rx_skb); -+ info->rx_skb = NULL; -+ break; -+ -+ } -+ -+ } else { -+ -+ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX); -+ info->rx_count--; -+ -+ if (info->rx_count == 0) { -+ -+ int dlen; -+ hci_event_hdr *eh; -+ hci_acl_hdr *ah; -+ hci_sco_hdr *sh; -+ -+ -+ switch (info->rx_state) { -+ -+ case RECV_WAIT_EVENT_HEADER: -+ eh = (hci_event_hdr *)(info->rx_skb->data); -+ info->rx_state = RECV_WAIT_DATA; -+ info->rx_count = eh->plen; -+ break; -+ -+ case RECV_WAIT_ACL_HEADER: -+ ah = (hci_acl_hdr *)(info->rx_skb->data); -+ dlen = __le16_to_cpu(ah->dlen); -+ info->rx_state = RECV_WAIT_DATA; -+ info->rx_count = dlen; -+ break; -+ -+ case RECV_WAIT_SCO_HEADER: -+ sh = (hci_sco_hdr *)(info->rx_skb->data); -+ info->rx_state = RECV_WAIT_DATA; -+ info->rx_count = sh->dlen; -+ break; -+ -+ case RECV_WAIT_DATA: -+ hci_recv_frame(info->rx_skb); -+ info->rx_skb = NULL; -+ break; -+ -+ } -+ -+ } -+ -+ } -+ -+ /* Make sure we don't stay here to long */ -+ if (boguscount++ > 16) -+ break; -+ -+ } while (inb(iobase + UART_LSR) & UART_LSR_DR); -+} -+ -+ -+void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs) -+{ -+ btuart_info_t *info = dev_inst; -+ unsigned int iobase; -+ int boguscount = 0; -+ int iir, lsr; -+ -+ if (!info) { -+ printk(KERN_WARNING "btuart_cs: Call of irq %d for unknown device.\n", irq); -+ return; -+ } -+ -+ iobase = info->link.io.BasePort1; -+ -+ spin_lock(&(info->lock)); -+ -+ iir = inb(iobase + UART_IIR) & UART_IIR_ID; -+ while (iir) { -+ -+ /* Clear interrupt */ -+ lsr = inb(iobase + UART_LSR); -+ -+ switch (iir) { -+ case UART_IIR_RLSI: -+ printk(KERN_NOTICE "btuart_cs: RLSI\n"); -+ break; -+ case UART_IIR_RDI: -+ /* Receive interrupt */ -+ btuart_receive(info); -+ break; -+ case UART_IIR_THRI: -+ if (lsr & UART_LSR_THRE) { -+ /* Transmitter ready for data */ -+ btuart_write_wakeup(info); -+ } -+ break; -+ default: -+ printk(KERN_NOTICE "btuart_cs: Unhandled IIR=%#x\n", iir); -+ break; -+ } -+ -+ /* Make sure we don't stay here to long */ -+ if (boguscount++ > 100) -+ break; -+ -+ iir = inb(iobase + UART_IIR) & UART_IIR_ID; -+ -+ } -+ -+ spin_unlock(&(info->lock)); -+} -+ -+ -+static void btuart_change_speed(btuart_info_t *info, unsigned int speed) -+{ -+ unsigned long flags; -+ unsigned int iobase; -+ int fcr; /* FIFO control reg */ -+ int lcr; /* Line control reg */ -+ int divisor; -+ -+ if (!info) { -+ printk(KERN_WARNING "btuart_cs: Call of change speed for unknown device.\n"); -+ return; -+ } -+ -+ iobase = info->link.io.BasePort1; -+ -+ spin_lock_irqsave(&(info->lock), flags); -+ -+ /* Turn off interrupts */ -+ outb(0, iobase + UART_IER); -+ -+ divisor = SPEED_MAX / speed; -+ -+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; -+ -+ /* -+ * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and -+ * almost 1,7 ms at 19200 bps. At speeds above that we can just forget -+ * about this timeout since it will always be fast enough. -+ */ -+ -+ if (speed < 38400) -+ fcr |= UART_FCR_TRIGGER_1; -+ else -+ fcr |= UART_FCR_TRIGGER_14; -+ -+ /* Bluetooth cards use 8N1 */ -+ lcr = UART_LCR_WLEN8; -+ -+ outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */ -+ outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */ -+ outb(divisor >> 8, iobase + UART_DLM); -+ outb(lcr, iobase + UART_LCR); /* Set 8N1 */ -+ outb(fcr, iobase + UART_FCR); /* Enable FIFO's */ -+ -+ /* Turn on interrups */ -+ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); -+ -+ spin_unlock_irqrestore(&(info->lock), flags); -+} -+ -+ -+ -+/* ======================== HCI interface ======================== */ -+ -+ -+static int btuart_hci_flush(struct hci_dev *hdev) -+{ -+ btuart_info_t *info = (btuart_info_t *)(hdev->driver_data); -+ -+ /* Drop TX queue */ -+ skb_queue_purge(&(info->txq)); -+ -+ return 0; -+} -+ -+ -+static int btuart_hci_open(struct hci_dev *hdev) -+{ -+ set_bit(HCI_RUNNING, &(hdev->flags)); -+ -+ return 0; -+} -+ -+ -+static int btuart_hci_close(struct hci_dev *hdev) -+{ -+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) -+ return 0; -+ -+ btuart_hci_flush(hdev); -+ -+ return 0; -+} -+ -+ -+static int btuart_hci_send_frame(struct sk_buff *skb) -+{ -+ btuart_info_t *info; -+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev); -+ -+ if (!hdev) { -+ printk(KERN_WARNING "btuart_cs: Frame for unknown HCI device (hdev=NULL)."); -+ return -ENODEV; -+ } -+ -+ info = (btuart_info_t *)(hdev->driver_data); -+ -+ switch (skb->pkt_type) { -+ case HCI_COMMAND_PKT: -+ hdev->stat.cmd_tx++; -+ break; -+ case HCI_ACLDATA_PKT: -+ hdev->stat.acl_tx++; -+ break; -+ case HCI_SCODATA_PKT: -+ hdev->stat.sco_tx++; -+ break; -+ }; -+ -+ /* Prepend skb with frame type */ -+ memcpy(skb_push(skb, 1), &(skb->pkt_type), 1); -+ skb_queue_tail(&(info->txq), skb); -+ -+ btuart_write_wakeup(info); -+ -+ return 0; -+} -+ -+ -+static void btuart_hci_destruct(struct hci_dev *hdev) -+{ -+} -+ -+ -+static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) -+{ -+ return -ENOIOCTLCMD; -+} -+ -+ -+ -+/* ======================== Card services HCI interaction ======================== */ -+ -+ -+int btuart_open(btuart_info_t *info) -+{ -+ unsigned long flags; -+ unsigned int iobase = info->link.io.BasePort1; -+ struct hci_dev *hdev; -+ -+ spin_lock_init(&(info->lock)); -+ -+ skb_queue_head_init(&(info->txq)); -+ -+ info->rx_state = RECV_WAIT_PACKET_TYPE; -+ info->rx_count = 0; -+ info->rx_skb = NULL; -+ -+ spin_lock_irqsave(&(info->lock), flags); -+ -+ /* Reset UART */ -+ outb(0, iobase + UART_MCR); -+ -+ /* Turn off interrupts */ -+ outb(0, iobase + UART_IER); -+ -+ /* Initialize UART */ -+ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */ -+ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR); -+ -+ /* Turn on interrupts */ -+ // outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); -+ -+ spin_unlock_irqrestore(&(info->lock), flags); -+ -+ btuart_change_speed(info, DEFAULT_BAUD_RATE); -+ -+ /* Timeout before it is safe to send the first HCI packet */ -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ); -+ -+ -+ /* Initialize and register HCI device */ -+ -+ hdev = &(info->hdev); -+ -+ hdev->type = HCI_PCCARD; -+ hdev->driver_data = info; -+ -+ hdev->open = btuart_hci_open; -+ hdev->close = btuart_hci_close; -+ hdev->flush = btuart_hci_flush; -+ hdev->send = btuart_hci_send_frame; -+ hdev->destruct = btuart_hci_destruct; -+ hdev->ioctl = btuart_hci_ioctl; -+ -+ if (hci_register_dev(hdev) < 0) { -+ printk(KERN_WARNING "btuart_cs: Can't register HCI device %s.\n", hdev->name); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+ -+int btuart_close(btuart_info_t *info) -+{ -+ unsigned long flags; -+ unsigned int iobase = info->link.io.BasePort1; -+ struct hci_dev *hdev = &(info->hdev); -+ -+ btuart_hci_close(hdev); -+ -+ spin_lock_irqsave(&(info->lock), flags); -+ -+ /* Reset UART */ -+ outb(0, iobase + UART_MCR); -+ -+ /* Turn off interrupts */ -+ outb(0, iobase + UART_IER); -+ -+ spin_unlock_irqrestore(&(info->lock), flags); -+ -+ if (hci_unregister_dev(hdev) < 0) -+ printk(KERN_WARNING "btuart_cs: Can't unregister HCI device %s.\n", hdev->name); -+ -+ return 0; -+} -+ -+ -+ -+/* ======================== Card services ======================== */ -+ -+ -+static void cs_error(client_handle_t handle, int func, int ret) -+{ -+ error_info_t err = { func, ret }; -+ -+ CardServices(ReportError, handle, &err); -+} -+ -+ -+dev_link_t *btuart_attach(void) -+{ -+ btuart_info_t *info; -+ client_reg_t client_reg; -+ dev_link_t *link; -+ int i, ret; -+ -+ /* Create new info device */ -+ info = kmalloc(sizeof(*info), GFP_KERNEL); -+ if (!info) -+ return NULL; -+ memset(info, 0, sizeof(*info)); -+ -+ link = &info->link; -+ link->priv = info; -+ -+ link->release.function = &btuart_release; -+ link->release.data = (u_long)link; -+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; -+ link->io.NumPorts1 = 8; -+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; -+ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; -+ -+ if (irq_list[0] == -1) -+ link->irq.IRQInfo2 = irq_mask; -+ else -+ for (i = 0; i < 4; i++) -+ link->irq.IRQInfo2 |= 1 << irq_list[i]; -+ -+ link->irq.Handler = btuart_interrupt; -+ link->irq.Instance = info; -+ -+ link->conf.Attributes = CONF_ENABLE_IRQ; -+ link->conf.Vcc = 50; -+ link->conf.IntType = INT_MEMORY_AND_IO; -+ -+ /* Register with Card Services */ -+ link->next = dev_list; -+ dev_list = link; -+ client_reg.dev_info = &dev_info; -+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; -+ client_reg.EventMask = -+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | -+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | -+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; -+ client_reg.event_handler = &btuart_event; -+ client_reg.Version = 0x0210; -+ client_reg.event_callback_args.client_data = link; -+ -+ ret = CardServices(RegisterClient, &link->handle, &client_reg); -+ if (ret != CS_SUCCESS) { -+ cs_error(link->handle, RegisterClient, ret); -+ btuart_detach(link); -+ return NULL; -+ } -+ -+ return link; -+} -+ -+ -+void btuart_detach(dev_link_t *link) -+{ -+ btuart_info_t *info = link->priv; -+ dev_link_t **linkp; -+ int ret; -+ -+ /* Locate device structure */ -+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) -+ if (*linkp == link) -+ break; -+ -+ if (*linkp == NULL) -+ return; -+ -+ del_timer(&link->release); -+ if (link->state & DEV_CONFIG) -+ btuart_release((u_long)link); -+ -+ if (link->handle) { -+ ret = CardServices(DeregisterClient, link->handle); -+ if (ret != CS_SUCCESS) -+ cs_error(link->handle, DeregisterClient, ret); -+ } -+ -+ /* Unlink device structure, free bits */ -+ *linkp = link->next; -+ -+ kfree(info); -+} -+ -+ -+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse) -+{ -+ int i; -+ -+ i = CardServices(fn, handle, tuple); -+ if (i != CS_SUCCESS) -+ return CS_NO_MORE_ITEMS; -+ -+ i = CardServices(GetTupleData, handle, tuple); -+ if (i != CS_SUCCESS) -+ return i; -+ -+ return CardServices(ParseTuple, handle, tuple, parse); -+} -+ -+ -+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) -+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) -+ -+void btuart_config(dev_link_t *link) -+{ -+ static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; -+ client_handle_t handle = link->handle; -+ btuart_info_t *info = link->priv; -+ tuple_t tuple; -+ u_short buf[256]; -+ cisparse_t parse; -+ cistpl_cftable_entry_t *cf = &parse.cftable_entry; -+ config_info_t config; -+ int i, j, try, last_ret, last_fn; -+ -+ tuple.TupleData = (cisdata_t *)buf; -+ tuple.TupleOffset = 0; -+ tuple.TupleDataMax = 255; -+ tuple.Attributes = 0; -+ -+ /* Get configuration register information */ -+ tuple.DesiredTuple = CISTPL_CONFIG; -+ last_ret = first_tuple(handle, &tuple, &parse); -+ if (last_ret != CS_SUCCESS) { -+ last_fn = ParseTuple; -+ goto cs_failed; -+ } -+ link->conf.ConfigBase = parse.config.base; -+ link->conf.Present = parse.config.rmask[0]; -+ -+ /* Configure card */ -+ link->state |= DEV_CONFIG; -+ i = CardServices(GetConfigurationInfo, handle, &config); -+ link->conf.Vcc = config.Vcc; -+ -+ /* First pass: look for a config entry that looks normal. */ -+ tuple.TupleData = (cisdata_t *) buf; -+ tuple.TupleOffset = 0; -+ tuple.TupleDataMax = 255; -+ tuple.Attributes = 0; -+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; -+ /* Two tries: without IO aliases, then with aliases */ -+ for (try = 0; try < 2; try++) { -+ i = first_tuple(handle, &tuple, &parse); -+ while (i != CS_NO_MORE_ITEMS) { -+ if (i != CS_SUCCESS) -+ goto next_entry; -+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) -+ link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; -+ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) { -+ link->conf.ConfigIndex = cf->index; -+ link->io.BasePort1 = cf->io.win[0].base; -+ link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; -+ i = CardServices(RequestIO, link->handle, &link->io); -+ if (i == CS_SUCCESS) -+ goto found_port; -+ } -+next_entry: -+ i = next_tuple(handle, &tuple, &parse); -+ } -+ } -+ -+ /* Second pass: try to find an entry that isn't picky about -+ its base address, then try to grab any standard serial port -+ address, and finally try to get any free port. */ -+ i = first_tuple(handle, &tuple, &parse); -+ while (i != CS_NO_MORE_ITEMS) { -+ if ((i == CS_SUCCESS) && (cf->io.nwin > 0) -+ && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { -+ link->conf.ConfigIndex = cf->index; -+ for (j = 0; j < 5; j++) { -+ link->io.BasePort1 = base[j]; -+ link->io.IOAddrLines = base[j] ? 16 : 3; -+ i = CardServices(RequestIO, link->handle, &link->io); -+ if (i == CS_SUCCESS) -+ goto found_port; -+ } -+ } -+ i = next_tuple(handle, &tuple, &parse); -+ } -+ -+found_port: -+ if (i != CS_SUCCESS) { -+ printk(KERN_NOTICE "btuart_cs: No usable port range found. Giving up.\n"); -+ cs_error(link->handle, RequestIO, i); -+ goto failed; -+ } -+ -+ i = CardServices(RequestIRQ, link->handle, &link->irq); -+ if (i != CS_SUCCESS) { -+ cs_error(link->handle, RequestIRQ, i); -+ link->irq.AssignedIRQ = 0; -+ } -+ -+ i = CardServices(RequestConfiguration, link->handle, &link->conf); -+ if (i != CS_SUCCESS) { -+ cs_error(link->handle, RequestConfiguration, i); -+ goto failed; -+ } -+ -+ MOD_INC_USE_COUNT; -+ -+ if (btuart_open(info) != 0) -+ goto failed; -+ -+ strcpy(info->node.dev_name, info->hdev.name); -+ link->dev = &info->node; -+ link->state &= ~DEV_CONFIG_PENDING; -+ -+ return; -+ -+cs_failed: -+ cs_error(link->handle, last_fn, last_ret); -+ -+failed: -+ btuart_release((u_long) link); -+} -+ -+ -+void btuart_release(u_long arg) -+{ -+ dev_link_t *link = (dev_link_t *)arg; -+ btuart_info_t *info = link->priv; -+ -+ if (link->state & DEV_PRESENT) -+ btuart_close(info); -+ -+ MOD_DEC_USE_COUNT; -+ -+ link->dev = NULL; -+ -+ CardServices(ReleaseConfiguration, link->handle); -+ CardServices(ReleaseIO, link->handle, &link->io); -+ CardServices(ReleaseIRQ, link->handle, &link->irq); -+ -+ link->state &= ~DEV_CONFIG; -+} -+ -+ -+int btuart_event(event_t event, int priority, event_callback_args_t *args) -+{ -+ dev_link_t *link = args->client_data; -+ btuart_info_t *info = link->priv; -+ -+ switch (event) { -+ case CS_EVENT_CARD_REMOVAL: -+ link->state &= ~DEV_PRESENT; -+ if (link->state & DEV_CONFIG) { -+ btuart_close(info); -+ mod_timer(&link->release, jiffies + HZ / 20); -+ } -+ break; -+ case CS_EVENT_CARD_INSERTION: -+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; -+ btuart_config(link); -+ break; -+ case CS_EVENT_PM_SUSPEND: -+ link->state |= DEV_SUSPEND; -+ /* Fall through... */ -+ case CS_EVENT_RESET_PHYSICAL: -+ if (link->state & DEV_CONFIG) -+ CardServices(ReleaseConfiguration, link->handle); -+ break; -+ case CS_EVENT_PM_RESUME: -+ link->state &= ~DEV_SUSPEND; -+ /* Fall through... */ -+ case CS_EVENT_CARD_RESET: -+ if (DEV_OK(link)) -+ CardServices(RequestConfiguration, link->handle, &link->conf); -+ break; -+ } -+ -+ return 0; -+} -+ -+ -+ -+/* ======================== Module initialization ======================== */ -+ -+ -+int __init init_btuart_cs(void) -+{ -+ servinfo_t serv; -+ int err; -+ -+ CardServices(GetCardServicesInfo, &serv); -+ if (serv.Revision != CS_RELEASE_CODE) { -+ printk(KERN_NOTICE "btuart_cs: Card Services release does not match!\n"); -+ return -1; -+ } -+ -+ err = register_pccard_driver(&dev_info, &btuart_attach, &btuart_detach); -+ -+ return err; -+} -+ -+ -+void __exit exit_btuart_cs(void) -+{ -+ unregister_pccard_driver(&dev_info); -+ -+ while (dev_list != NULL) -+ btuart_detach(dev_list); -+} -+ -+ -+module_init(init_btuart_cs); -+module_exit(exit_btuart_cs); -+ -+EXPORT_NO_SYMBOLS; ---- linux/drivers/bluetooth/Config.in~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/drivers/bluetooth/Config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -1,8 +1,33 @@ -+# -+# Bluetooth HCI device drivers configuration -+# -+ - mainmenu_option next_comment - comment 'Bluetooth device drivers' - - dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB -+if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then -+ bool ' SCO (voice) support' CONFIG_BLUEZ_HCIUSB_SCO -+fi -+ - dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ --dep_tristate 'HCI VHCI virtual HCI device driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ -+if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then -+ bool ' UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4 -+ bool ' BCSP protocol support' CONFIG_BLUEZ_HCIUART_BCSP -+ dep_bool ' Transmit CRC with every BCSP packet' CONFIG_BLUEZ_HCIUART_BCSP_TXCRC $CONFIG_BLUEZ_HCIUART_BCSP -+fi -+ -+dep_tristate 'HCI BlueFRITZ! USB driver' CONFIG_BLUEZ_HCIBFUSB $CONFIG_BLUEZ $CONFIG_USB -+ -+dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ -+ -+dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ -+ -+dep_tristate 'HCI BlueCard (PC Card) driver' CONFIG_BLUEZ_HCIBLUECARD $CONFIG_PCMCIA $CONFIG_BLUEZ -+ -+dep_tristate 'HCI UART (PC Card) driver' CONFIG_BLUEZ_HCIBTUART $CONFIG_PCMCIA $CONFIG_BLUEZ -+ -+dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ - - endmenu -+ ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/dtl1_cs.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,858 @@ -+/* -+ * -+ * A driver for Nokia Connectivity Card DTL-1 devices -+ * -+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org> -+ * -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation; -+ * -+ * Software distributed under the License is distributed on an "AS -+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or -+ * implied. See the License for the specific language governing -+ * rights and limitations under the License. -+ * -+ * The initial developer of the original code is David A. Hinds -+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds -+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/slab.h> -+#include <linux/types.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/errno.h> -+#include <linux/ptrace.h> -+#include <linux/ioport.h> -+#include <linux/spinlock.h> -+ -+#include <linux/skbuff.h> -+#include <linux/string.h> -+#include <linux/serial.h> -+#include <linux/serial_reg.h> -+#include <asm/system.h> -+#include <asm/bitops.h> -+#include <asm/io.h> -+ -+#include <pcmcia/version.h> -+#include <pcmcia/cs_types.h> -+#include <pcmcia/cs.h> -+#include <pcmcia/cistpl.h> -+#include <pcmcia/ciscode.h> -+#include <pcmcia/ds.h> -+#include <pcmcia/cisreg.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+ -+ -+ -+/* ======================== Module parameters ======================== */ -+ -+ -+/* Bit map of interrupts to choose from */ -+static u_int irq_mask = 0xffff; -+static int irq_list[4] = { -1 }; -+ -+MODULE_PARM(irq_mask, "i"); -+MODULE_PARM(irq_list, "1-4i"); -+ -+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -+MODULE_DESCRIPTION("BlueZ driver for Nokia Connectivity Card DTL-1"); -+MODULE_LICENSE("GPL"); -+ -+ -+ -+/* ======================== Local structures ======================== */ -+ -+ -+typedef struct dtl1_info_t { -+ dev_link_t link; -+ dev_node_t node; -+ -+ struct hci_dev hdev; -+ -+ spinlock_t lock; /* For serializing operations */ -+ -+ unsigned long flowmask; /* HCI flow mask */ -+ int ri_latch; -+ -+ struct sk_buff_head txq; -+ unsigned long tx_state; -+ -+ unsigned long rx_state; -+ unsigned long rx_count; -+ struct sk_buff *rx_skb; -+} dtl1_info_t; -+ -+ -+void dtl1_config(dev_link_t *link); -+void dtl1_release(u_long arg); -+int dtl1_event(event_t event, int priority, event_callback_args_t *args); -+ -+static dev_info_t dev_info = "dtl1_cs"; -+ -+dev_link_t *dtl1_attach(void); -+void dtl1_detach(dev_link_t *); -+ -+static dev_link_t *dev_list = NULL; -+ -+ -+/* Transmit states */ -+#define XMIT_SENDING 1 -+#define XMIT_WAKEUP 2 -+#define XMIT_WAITING 8 -+ -+/* Receiver States */ -+#define RECV_WAIT_NSH 0 -+#define RECV_WAIT_DATA 1 -+ -+ -+typedef struct { -+ u8 type; -+ u8 zero; -+ u16 len; -+} __attribute__ ((packed)) nsh_t; /* Nokia Specific Header */ -+ -+#define NSHL 4 /* Nokia Specific Header Length */ -+ -+ -+ -+/* ======================== Interrupt handling ======================== */ -+ -+ -+static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) -+{ -+ int actual = 0; -+ -+ /* Tx FIFO should be empty */ -+ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE)) -+ return 0; -+ -+ /* Fill FIFO with current frame */ -+ while ((fifo_size-- > 0) && (actual < len)) { -+ /* Transmit next byte */ -+ outb(buf[actual], iobase + UART_TX); -+ actual++; -+ } -+ -+ return actual; -+} -+ -+ -+static void dtl1_write_wakeup(dtl1_info_t *info) -+{ -+ if (!info) { -+ printk(KERN_WARNING "dtl1_cs: Call of write_wakeup for unknown device.\n"); -+ return; -+ } -+ -+ if (test_bit(XMIT_WAITING, &(info->tx_state))) { -+ set_bit(XMIT_WAKEUP, &(info->tx_state)); -+ return; -+ } -+ -+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) { -+ set_bit(XMIT_WAKEUP, &(info->tx_state)); -+ return; -+ } -+ -+ do { -+ register unsigned int iobase = info->link.io.BasePort1; -+ register struct sk_buff *skb; -+ register int len; -+ -+ clear_bit(XMIT_WAKEUP, &(info->tx_state)); -+ -+ if (!(info->link.state & DEV_PRESENT)) -+ return; -+ -+ if (!(skb = skb_dequeue(&(info->txq)))) -+ break; -+ -+ /* Send frame */ -+ len = dtl1_write(iobase, 32, skb->data, skb->len); -+ -+ if (len == skb->len) { -+ set_bit(XMIT_WAITING, &(info->tx_state)); -+ kfree_skb(skb); -+ } else { -+ skb_pull(skb, len); -+ skb_queue_head(&(info->txq), skb); -+ } -+ -+ info->hdev.stat.byte_tx += len; -+ -+ } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); -+ -+ clear_bit(XMIT_SENDING, &(info->tx_state)); -+} -+ -+ -+static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb) -+{ -+ u8 flowmask = *(u8 *)skb->data; -+ int i; -+ -+ printk(KERN_INFO "dtl1_cs: Nokia control data = "); -+ for (i = 0; i < skb->len; i++) { -+ printk("%02x ", skb->data[i]); -+ } -+ printk("\n"); -+ -+ /* transition to active state */ -+ if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) { -+ clear_bit(XMIT_WAITING, &(info->tx_state)); -+ dtl1_write_wakeup(info); -+ } -+ -+ info->flowmask = flowmask; -+ -+ kfree_skb(skb); -+} -+ -+ -+static void dtl1_receive(dtl1_info_t *info) -+{ -+ unsigned int iobase; -+ nsh_t *nsh; -+ int boguscount = 0; -+ -+ if (!info) { -+ printk(KERN_WARNING "dtl1_cs: Call of receive for unknown device.\n"); -+ return; -+ } -+ -+ iobase = info->link.io.BasePort1; -+ -+ do { -+ info->hdev.stat.byte_rx++; -+ -+ /* Allocate packet */ -+ if (info->rx_skb == NULL) -+ if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { -+ printk(KERN_WARNING "dtl1_cs: Can't allocate mem for new packet.\n"); -+ info->rx_state = RECV_WAIT_NSH; -+ info->rx_count = NSHL; -+ return; -+ } -+ -+ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX); -+ nsh = (nsh_t *)info->rx_skb->data; -+ -+ info->rx_count--; -+ -+ if (info->rx_count == 0) { -+ -+ switch (info->rx_state) { -+ case RECV_WAIT_NSH: -+ info->rx_state = RECV_WAIT_DATA; -+ info->rx_count = nsh->len + (nsh->len & 0x0001); -+ break; -+ case RECV_WAIT_DATA: -+ info->rx_skb->pkt_type = nsh->type; -+ -+ /* remove PAD byte if it exists */ -+ if (nsh->len & 0x0001) { -+ info->rx_skb->tail--; -+ info->rx_skb->len--; -+ } -+ -+ /* remove NSH */ -+ skb_pull(info->rx_skb, NSHL); -+ -+ switch (info->rx_skb->pkt_type) { -+ case 0x80: -+ /* control data for the Nokia Card */ -+ dtl1_control(info, info->rx_skb); -+ break; -+ case 0x82: -+ case 0x83: -+ case 0x84: -+ /* send frame to the HCI layer */ -+ info->rx_skb->dev = (void *)&(info->hdev); -+ info->rx_skb->pkt_type &= 0x0f; -+ hci_recv_frame(info->rx_skb); -+ break; -+ default: -+ /* unknown packet */ -+ printk(KERN_WARNING "dtl1_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type); -+ kfree_skb(info->rx_skb); -+ break; -+ } -+ -+ info->rx_state = RECV_WAIT_NSH; -+ info->rx_count = NSHL; -+ info->rx_skb = NULL; -+ break; -+ } -+ -+ } -+ -+ /* Make sure we don't stay here to long */ -+ if (boguscount++ > 32) -+ break; -+ -+ } while (inb(iobase + UART_LSR) & UART_LSR_DR); -+} -+ -+ -+void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs) -+{ -+ dtl1_info_t *info = dev_inst; -+ unsigned int iobase; -+ unsigned char msr; -+ int boguscount = 0; -+ int iir, lsr; -+ -+ if (!info) { -+ printk(KERN_WARNING "dtl1_cs: Call of irq %d for unknown device.\n", irq); -+ return; -+ } -+ -+ iobase = info->link.io.BasePort1; -+ -+ spin_lock(&(info->lock)); -+ -+ iir = inb(iobase + UART_IIR) & UART_IIR_ID; -+ while (iir) { -+ -+ /* Clear interrupt */ -+ lsr = inb(iobase + UART_LSR); -+ -+ switch (iir) { -+ case UART_IIR_RLSI: -+ printk(KERN_NOTICE "dtl1_cs: RLSI\n"); -+ break; -+ case UART_IIR_RDI: -+ /* Receive interrupt */ -+ dtl1_receive(info); -+ break; -+ case UART_IIR_THRI: -+ if (lsr & UART_LSR_THRE) { -+ /* Transmitter ready for data */ -+ dtl1_write_wakeup(info); -+ } -+ break; -+ default: -+ printk(KERN_NOTICE "dtl1_cs: Unhandled IIR=%#x\n", iir); -+ break; -+ } -+ -+ /* Make sure we don't stay here to long */ -+ if (boguscount++ > 100) -+ break; -+ -+ iir = inb(iobase + UART_IIR) & UART_IIR_ID; -+ -+ } -+ -+ msr = inb(iobase + UART_MSR); -+ -+ if (info->ri_latch ^ (msr & UART_MSR_RI)) { -+ info->ri_latch = msr & UART_MSR_RI; -+ clear_bit(XMIT_WAITING, &(info->tx_state)); -+ dtl1_write_wakeup(info); -+ } -+ -+ spin_unlock(&(info->lock)); -+} -+ -+ -+ -+/* ======================== HCI interface ======================== */ -+ -+ -+static int dtl1_hci_open(struct hci_dev *hdev) -+{ -+ set_bit(HCI_RUNNING, &(hdev->flags)); -+ -+ return 0; -+} -+ -+ -+static int dtl1_hci_flush(struct hci_dev *hdev) -+{ -+ dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data); -+ -+ /* Drop TX queue */ -+ skb_queue_purge(&(info->txq)); -+ -+ return 0; -+} -+ -+ -+static int dtl1_hci_close(struct hci_dev *hdev) -+{ -+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) -+ return 0; -+ -+ dtl1_hci_flush(hdev); -+ -+ return 0; -+} -+ -+ -+static int dtl1_hci_send_frame(struct sk_buff *skb) -+{ -+ dtl1_info_t *info; -+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev); -+ struct sk_buff *s; -+ nsh_t nsh; -+ -+ if (!hdev) { -+ printk(KERN_WARNING "dtl1_cs: Frame for unknown HCI device (hdev=NULL)."); -+ return -ENODEV; -+ } -+ -+ info = (dtl1_info_t *)(hdev->driver_data); -+ -+ switch (skb->pkt_type) { -+ case HCI_COMMAND_PKT: -+ hdev->stat.cmd_tx++; -+ nsh.type = 0x81; -+ break; -+ case HCI_ACLDATA_PKT: -+ hdev->stat.acl_tx++; -+ nsh.type = 0x82; -+ break; -+ case HCI_SCODATA_PKT: -+ hdev->stat.sco_tx++; -+ nsh.type = 0x83; -+ break; -+ }; -+ -+ nsh.zero = 0; -+ nsh.len = skb->len; -+ -+ s = bluez_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC); -+ skb_reserve(s, NSHL); -+ memcpy(skb_put(s, skb->len), skb->data, skb->len); -+ if (skb->len & 0x0001) -+ *skb_put(s, 1) = 0; /* PAD */ -+ -+ /* Prepend skb with Nokia frame header and queue */ -+ memcpy(skb_push(s, NSHL), &nsh, NSHL); -+ skb_queue_tail(&(info->txq), s); -+ -+ dtl1_write_wakeup(info); -+ -+ kfree_skb(skb); -+ -+ return 0; -+} -+ -+ -+static void dtl1_hci_destruct(struct hci_dev *hdev) -+{ -+} -+ -+ -+static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) -+{ -+ return -ENOIOCTLCMD; -+} -+ -+ -+ -+/* ======================== Card services HCI interaction ======================== */ -+ -+ -+int dtl1_open(dtl1_info_t *info) -+{ -+ unsigned long flags; -+ unsigned int iobase = info->link.io.BasePort1; -+ struct hci_dev *hdev; -+ -+ spin_lock_init(&(info->lock)); -+ -+ skb_queue_head_init(&(info->txq)); -+ -+ info->rx_state = RECV_WAIT_NSH; -+ info->rx_count = NSHL; -+ info->rx_skb = NULL; -+ -+ set_bit(XMIT_WAITING, &(info->tx_state)); -+ -+ spin_lock_irqsave(&(info->lock), flags); -+ -+ /* Reset UART */ -+ outb(0, iobase + UART_MCR); -+ -+ /* Turn off interrupts */ -+ outb(0, iobase + UART_IER); -+ -+ /* Initialize UART */ -+ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */ -+ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR); -+ -+ info->ri_latch = inb(info->link.io.BasePort1 + UART_MSR) & UART_MSR_RI; -+ -+ /* Turn on interrupts */ -+ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); -+ -+ spin_unlock_irqrestore(&(info->lock), flags); -+ -+ /* Timeout before it is safe to send the first HCI packet */ -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ * 2); -+ -+ -+ /* Initialize and register HCI device */ -+ -+ hdev = &(info->hdev); -+ -+ hdev->type = HCI_PCCARD; -+ hdev->driver_data = info; -+ -+ hdev->open = dtl1_hci_open; -+ hdev->close = dtl1_hci_close; -+ hdev->flush = dtl1_hci_flush; -+ hdev->send = dtl1_hci_send_frame; -+ hdev->destruct = dtl1_hci_destruct; -+ hdev->ioctl = dtl1_hci_ioctl; -+ -+ if (hci_register_dev(hdev) < 0) { -+ printk(KERN_WARNING "dtl1_cs: Can't register HCI device %s.\n", hdev->name); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+ -+int dtl1_close(dtl1_info_t *info) -+{ -+ unsigned long flags; -+ unsigned int iobase = info->link.io.BasePort1; -+ struct hci_dev *hdev = &(info->hdev); -+ -+ dtl1_hci_close(hdev); -+ -+ spin_lock_irqsave(&(info->lock), flags); -+ -+ /* Reset UART */ -+ outb(0, iobase + UART_MCR); -+ -+ /* Turn off interrupts */ -+ outb(0, iobase + UART_IER); -+ -+ spin_unlock_irqrestore(&(info->lock), flags); -+ -+ if (hci_unregister_dev(hdev) < 0) -+ printk(KERN_WARNING "dtl1_cs: Can't unregister HCI device %s.\n", hdev->name); -+ -+ return 0; -+} -+ -+ -+ -+/* ======================== Card services ======================== */ -+ -+ -+static void cs_error(client_handle_t handle, int func, int ret) -+{ -+ error_info_t err = { func, ret }; -+ -+ CardServices(ReportError, handle, &err); -+} -+ -+ -+dev_link_t *dtl1_attach(void) -+{ -+ dtl1_info_t *info; -+ client_reg_t client_reg; -+ dev_link_t *link; -+ int i, ret; -+ -+ /* Create new info device */ -+ info = kmalloc(sizeof(*info), GFP_KERNEL); -+ if (!info) -+ return NULL; -+ memset(info, 0, sizeof(*info)); -+ -+ link = &info->link; -+ link->priv = info; -+ -+ link->release.function = &dtl1_release; -+ link->release.data = (u_long)link; -+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; -+ link->io.NumPorts1 = 8; -+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; -+ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; -+ -+ if (irq_list[0] == -1) -+ link->irq.IRQInfo2 = irq_mask; -+ else -+ for (i = 0; i < 4; i++) -+ link->irq.IRQInfo2 |= 1 << irq_list[i]; -+ -+ link->irq.Handler = dtl1_interrupt; -+ link->irq.Instance = info; -+ -+ link->conf.Attributes = CONF_ENABLE_IRQ; -+ link->conf.Vcc = 50; -+ link->conf.IntType = INT_MEMORY_AND_IO; -+ -+ /* Register with Card Services */ -+ link->next = dev_list; -+ dev_list = link; -+ client_reg.dev_info = &dev_info; -+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; -+ client_reg.EventMask = -+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | -+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | -+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; -+ client_reg.event_handler = &dtl1_event; -+ client_reg.Version = 0x0210; -+ client_reg.event_callback_args.client_data = link; -+ -+ ret = CardServices(RegisterClient, &link->handle, &client_reg); -+ if (ret != CS_SUCCESS) { -+ cs_error(link->handle, RegisterClient, ret); -+ dtl1_detach(link); -+ return NULL; -+ } -+ -+ return link; -+} -+ -+ -+void dtl1_detach(dev_link_t *link) -+{ -+ dtl1_info_t *info = link->priv; -+ dev_link_t **linkp; -+ int ret; -+ -+ /* Locate device structure */ -+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) -+ if (*linkp == link) -+ break; -+ -+ if (*linkp == NULL) -+ return; -+ -+ del_timer(&link->release); -+ if (link->state & DEV_CONFIG) -+ dtl1_release((u_long)link); -+ -+ if (link->handle) { -+ ret = CardServices(DeregisterClient, link->handle); -+ if (ret != CS_SUCCESS) -+ cs_error(link->handle, DeregisterClient, ret); -+ } -+ -+ /* Unlink device structure, free bits */ -+ *linkp = link->next; -+ -+ kfree(info); -+} -+ -+ -+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple, cisparse_t *parse) -+{ -+ int i; -+ -+ i = CardServices(fn, handle, tuple); -+ if (i != CS_SUCCESS) -+ return CS_NO_MORE_ITEMS; -+ -+ i = CardServices(GetTupleData, handle, tuple); -+ if (i != CS_SUCCESS) -+ return i; -+ -+ return CardServices(ParseTuple, handle, tuple, parse); -+} -+ -+ -+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) -+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c) -+ -+void dtl1_config(dev_link_t *link) -+{ -+ client_handle_t handle = link->handle; -+ dtl1_info_t *info = link->priv; -+ tuple_t tuple; -+ u_short buf[256]; -+ cisparse_t parse; -+ cistpl_cftable_entry_t *cf = &parse.cftable_entry; -+ config_info_t config; -+ int i, last_ret, last_fn; -+ -+ tuple.TupleData = (cisdata_t *)buf; -+ tuple.TupleOffset = 0; -+ tuple.TupleDataMax = 255; -+ tuple.Attributes = 0; -+ -+ /* Get configuration register information */ -+ tuple.DesiredTuple = CISTPL_CONFIG; -+ last_ret = first_tuple(handle, &tuple, &parse); -+ if (last_ret != CS_SUCCESS) { -+ last_fn = ParseTuple; -+ goto cs_failed; -+ } -+ link->conf.ConfigBase = parse.config.base; -+ link->conf.Present = parse.config.rmask[0]; -+ -+ /* Configure card */ -+ link->state |= DEV_CONFIG; -+ i = CardServices(GetConfigurationInfo, handle, &config); -+ link->conf.Vcc = config.Vcc; -+ -+ tuple.TupleData = (cisdata_t *)buf; -+ tuple.TupleOffset = 0; -+ tuple.TupleDataMax = 255; -+ tuple.Attributes = 0; -+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; -+ -+ /* Look for a generic full-sized window */ -+ link->io.NumPorts1 = 8; -+ i = first_tuple(handle, &tuple, &parse); -+ while (i != CS_NO_MORE_ITEMS) { -+ if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && (cf->io.win[0].len > 8)) { -+ link->conf.ConfigIndex = cf->index; -+ link->io.BasePort1 = cf->io.win[0].base; -+ link->io.NumPorts1 = cf->io.win[0].len; /*yo */ -+ link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; -+ i = CardServices(RequestIO, link->handle, &link->io); -+ if (i == CS_SUCCESS) -+ break; -+ } -+ i = next_tuple(handle, &tuple, &parse); -+ } -+ -+ if (i != CS_SUCCESS) { -+ cs_error(link->handle, RequestIO, i); -+ goto failed; -+ } -+ -+ i = CardServices(RequestIRQ, link->handle, &link->irq); -+ if (i != CS_SUCCESS) { -+ cs_error(link->handle, RequestIRQ, i); -+ link->irq.AssignedIRQ = 0; -+ } -+ -+ i = CardServices(RequestConfiguration, link->handle, &link->conf); -+ if (i != CS_SUCCESS) { -+ cs_error(link->handle, RequestConfiguration, i); -+ goto failed; -+ } -+ -+ MOD_INC_USE_COUNT; -+ -+ if (dtl1_open(info) != 0) -+ goto failed; -+ -+ strcpy(info->node.dev_name, info->hdev.name); -+ link->dev = &info->node; -+ link->state &= ~DEV_CONFIG_PENDING; -+ -+ return; -+ -+cs_failed: -+ cs_error(link->handle, last_fn, last_ret); -+ -+failed: -+ dtl1_release((u_long)link); -+} -+ -+ -+void dtl1_release(u_long arg) -+{ -+ dev_link_t *link = (dev_link_t *)arg; -+ dtl1_info_t *info = link->priv; -+ -+ if (link->state & DEV_PRESENT) -+ dtl1_close(info); -+ -+ MOD_DEC_USE_COUNT; -+ -+ link->dev = NULL; -+ -+ CardServices(ReleaseConfiguration, link->handle); -+ CardServices(ReleaseIO, link->handle, &link->io); -+ CardServices(ReleaseIRQ, link->handle, &link->irq); -+ -+ link->state &= ~DEV_CONFIG; -+} -+ -+ -+int dtl1_event(event_t event, int priority, event_callback_args_t *args) -+{ -+ dev_link_t *link = args->client_data; -+ dtl1_info_t *info = link->priv; -+ -+ switch (event) { -+ case CS_EVENT_CARD_REMOVAL: -+ link->state &= ~DEV_PRESENT; -+ if (link->state & DEV_CONFIG) { -+ dtl1_close(info); -+ mod_timer(&link->release, jiffies + HZ / 20); -+ } -+ break; -+ case CS_EVENT_CARD_INSERTION: -+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; -+ dtl1_config(link); -+ break; -+ case CS_EVENT_PM_SUSPEND: -+ link->state |= DEV_SUSPEND; -+ /* Fall through... */ -+ case CS_EVENT_RESET_PHYSICAL: -+ if (link->state & DEV_CONFIG) -+ CardServices(ReleaseConfiguration, link->handle); -+ break; -+ case CS_EVENT_PM_RESUME: -+ link->state &= ~DEV_SUSPEND; -+ /* Fall through... */ -+ case CS_EVENT_CARD_RESET: -+ if (DEV_OK(link)) -+ CardServices(RequestConfiguration, link->handle, &link->conf); -+ break; -+ } -+ -+ return 0; -+} -+ -+ -+ -+/* ======================== Module initialization ======================== */ -+ -+ -+int __init init_dtl1_cs(void) -+{ -+ servinfo_t serv; -+ int err; -+ -+ CardServices(GetCardServicesInfo, &serv); -+ if (serv.Revision != CS_RELEASE_CODE) { -+ printk(KERN_NOTICE "dtl1_cs: Card Services release does not match!\n"); -+ return -1; -+ } -+ -+ err = register_pccard_driver(&dev_info, &dtl1_attach, &dtl1_detach); -+ -+ return err; -+} -+ -+ -+void __exit exit_dtl1_cs(void) -+{ -+ unregister_pccard_driver(&dev_info); -+ -+ while (dev_list != NULL) -+ dtl1_detach(dev_list); -+} -+ -+ -+module_init(init_dtl1_cs); -+module_exit(exit_dtl1_cs); -+ -+EXPORT_NO_SYMBOLS; ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/hci_bcsp.c 2004-01-25 23:37:39.000000000 +0100 -@@ -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/config.h> -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/sched.h> -+#include <linux/types.h> -+#include <linux/fcntl.h> -+#include <linux/interrupt.h> -+#include <linux/ptrace.h> -+#include <linux/poll.h> -+ -+#include <linux/slab.h> -+#include <linux/tty.h> -+#include <linux/errno.h> -+#include <linux/string.h> -+#include <linux/signal.h> -+#include <linux/ioctl.h> -+#include <linux/skbuff.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+#include "hci_uart.h" -+#include "hci_bcsp.h" -+ -+#ifndef HCI_UART_DEBUG -+#undef BT_DBG -+#define BT_DBG( A... ) -+#undef BT_DMP -+#define BT_DMP( A... ) -+#endif -+ -+/* ---- BCSP CRC calculation ---- */ -+ -+/* Table for calculating CRC for polynomial 0x1021, LSB processed first, -+initial value 0xffff, bits shifted in reverse order. */ -+ -+static const u16 crc_table[] = { -+ 0x0000, 0x1081, 0x2102, 0x3183, -+ 0x4204, 0x5285, 0x6306, 0x7387, -+ 0x8408, 0x9489, 0xa50a, 0xb58b, -+ 0xc60c, 0xd68d, 0xe70e, 0xf78f -+}; -+ -+/* Initialise the crc calculator */ -+#define BCSP_CRC_INIT(x) x = 0xffff -+ -+/* -+ Update crc with next data byte -+ -+ Implementation note -+ The data byte is treated as two nibbles. The crc is generated -+ in reverse, i.e., bits are fed into the register from the top. -+*/ -+static void bcsp_crc_update(u16 *crc, u8 d) -+{ -+ u16 reg = *crc; -+ -+ reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f]; -+ reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f]; -+ -+ *crc = reg; -+} -+ -+/* -+ Get reverse of generated crc -+ -+ Implementation note -+ The crc generator (bcsp_crc_init() and bcsp_crc_update()) -+ creates a reversed crc, so it needs to be swapped back before -+ being passed on. -+*/ -+static u16 bcsp_crc_reverse(u16 crc) -+{ -+ u16 b, rev; -+ -+ for (b = 0, rev = 0; b < 16; b++) { -+ rev = rev << 1; -+ rev |= (crc & 1); -+ crc = crc >> 1; -+ } -+ return (rev); -+} -+ -+/* ---- BCSP core ---- */ -+ -+static void bcsp_slip_msgdelim(struct sk_buff *skb) -+{ -+ const char pkt_delim = 0xc0; -+ memcpy(skb_put(skb, 1), &pkt_delim, 1); -+} -+ -+static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c) -+{ -+ const char esc_c0[2] = { 0xdb, 0xdc }; -+ const char esc_db[2] = { 0xdb, 0xdd }; -+ -+ switch (c) { -+ case 0xc0: -+ memcpy(skb_put(skb, 2), &esc_c0, 2); -+ break; -+ case 0xdb: -+ memcpy(skb_put(skb, 2), &esc_db, 2); -+ break; -+ default: -+ memcpy(skb_put(skb, 1), &c, 1); -+ } -+} -+ -+static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb) -+{ -+ struct bcsp_struct *bcsp = hu->priv; -+ -+ if (skb->len > 0xFFF) { -+ BT_ERR("Packet too long"); -+ kfree_skb(skb); -+ return 0; -+ } -+ -+ switch (skb->pkt_type) { -+ case HCI_ACLDATA_PKT: -+ case HCI_COMMAND_PKT: -+ skb_queue_tail(&bcsp->rel, skb); -+ break; -+ -+ case HCI_SCODATA_PKT: -+ skb_queue_tail(&bcsp->unrel, skb); -+ break; -+ -+ default: -+ BT_ERR("Unknown packet type"); -+ kfree_skb(skb); -+ break; -+ } -+ return 0; -+} -+ -+static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, -+ int len, int pkt_type) -+{ -+ struct sk_buff *nskb; -+ u8 hdr[4], chan; -+ int rel, i; -+ -+#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC -+ u16 BCSP_CRC_INIT(bcsp_txmsg_crc); -+#endif -+ -+ switch (pkt_type) { -+ case HCI_ACLDATA_PKT: -+ chan = 6; /* BCSP ACL channel */ -+ rel = 1; /* reliable channel */ -+ break; -+ case HCI_COMMAND_PKT: -+ chan = 5; /* BCSP cmd/evt channel */ -+ rel = 1; /* reliable channel */ -+ break; -+ case HCI_SCODATA_PKT: -+ chan = 7; /* BCSP SCO channel */ -+ rel = 0; /* unreliable channel */ -+ break; -+ case BCSP_LE_PKT: -+ chan = 1; /* BCSP LE channel */ -+ rel = 0; /* unreliable channel */ -+ break; -+ case BCSP_ACK_PKT: -+ chan = 0; /* BCSP internal channel */ -+ rel = 0; /* unreliable channel */ -+ break; -+ default: -+ BT_ERR("Unknown packet type"); -+ return NULL; -+ } -+ -+ /* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2 -+ (because bytes 0xc0 and 0xdb are escaped, worst case is -+ when the packet is all made of 0xc0 and 0xdb :) ) -+ + 2 (0xc0 delimiters at start and end). */ -+ -+ nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC); -+ if (!nskb) -+ return NULL; -+ -+ nskb->pkt_type = pkt_type; -+ -+ bcsp_slip_msgdelim(nskb); -+ -+ hdr[0] = bcsp->rxseq_txack << 3; -+ bcsp->txack_req = 0; -+ BT_DBG("We request packet no %u to card", bcsp->rxseq_txack); -+ -+ if (rel) { -+ hdr[0] |= 0x80 + bcsp->msgq_txseq; -+ BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq); -+ bcsp->msgq_txseq = ++(bcsp->msgq_txseq) & 0x07; -+ } -+#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC -+ hdr[0] |= 0x40; -+#endif -+ -+ hdr[1] = (len << 4) & 0xFF; -+ hdr[1] |= chan; -+ hdr[2] = len >> 4; -+ hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]); -+ -+ /* Put BCSP header */ -+ for (i = 0; i < 4; i++) { -+ bcsp_slip_one_byte(nskb, hdr[i]); -+#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC -+ bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]); -+#endif -+ } -+ -+ /* Put payload */ -+ for (i = 0; i < len; i++) { -+ bcsp_slip_one_byte(nskb, data[i]); -+#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC -+ bcsp_crc_update(&bcsp_txmsg_crc, data[i]); -+#endif -+ } -+ -+#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC -+ /* Put CRC */ -+ bcsp_txmsg_crc = bcsp_crc_reverse(bcsp_txmsg_crc); -+ bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff)); -+ bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff)); -+#endif -+ -+ bcsp_slip_msgdelim(nskb); -+ return nskb; -+} -+ -+/* This is a rewrite of pkt_avail in ABCSP */ -+static struct sk_buff *bcsp_dequeue(struct hci_uart *hu) -+{ -+ struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv; -+ unsigned long flags; -+ struct sk_buff *skb; -+ -+ /* First of all, check for unreliable messages in the queue, -+ since they have priority */ -+ -+ if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) { -+ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type); -+ if (nskb) { -+ kfree_skb(skb); -+ return nskb; -+ } else { -+ skb_queue_head(&bcsp->unrel, skb); -+ BT_ERR("Could not dequeue pkt because alloc_skb failed"); -+ } -+ } -+ -+ /* Now, try to send a reliable pkt. We can only send a -+ reliable packet if the number of packets sent but not yet ack'ed -+ is < than the winsize */ -+ -+ spin_lock_irqsave(&bcsp->unack.lock, flags); -+ -+ if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) { -+ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type); -+ if (nskb) { -+ __skb_queue_tail(&bcsp->unack, skb); -+ mod_timer(&bcsp->tbcsp, jiffies + HZ / 4); -+ spin_unlock_irqrestore(&bcsp->unack.lock, flags); -+ return nskb; -+ } else { -+ skb_queue_head(&bcsp->rel, skb); -+ BT_ERR("Could not dequeue pkt because alloc_skb failed"); -+ } -+ } -+ -+ spin_unlock_irqrestore(&bcsp->unack.lock, flags); -+ -+ -+ /* We could not send a reliable packet, either because there are -+ none or because there are too many unack'ed pkts. Did we receive -+ any packets we have not acknowledged yet ? */ -+ -+ if (bcsp->txack_req) { -+ /* if so, craft an empty ACK pkt and send it on BCSP unreliable -+ channel 0 */ -+ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT); -+ return nskb; -+ } -+ -+ /* We have nothing to send */ -+ return NULL; -+} -+ -+static int bcsp_flush(struct hci_uart *hu) -+{ -+ BT_DBG("hu %p", hu); -+ return 0; -+} -+ -+/* Remove ack'ed packets */ -+static void bcsp_pkt_cull(struct bcsp_struct *bcsp) -+{ -+ unsigned long flags; -+ struct sk_buff *skb; -+ int i, pkts_to_be_removed; -+ u8 seqno; -+ -+ spin_lock_irqsave(&bcsp->unack.lock, flags); -+ -+ pkts_to_be_removed = bcsp->unack.qlen; -+ seqno = bcsp->msgq_txseq; -+ -+ while (pkts_to_be_removed) { -+ if (bcsp->rxack == seqno) -+ break; -+ pkts_to_be_removed--; -+ seqno = (seqno - 1) & 0x07; -+ } -+ -+ if (bcsp->rxack != seqno) -+ BT_ERR("Peer acked invalid packet"); -+ -+ BT_DBG("Removing %u pkts out of %u, up to seqno %u", -+ pkts_to_be_removed, bcsp->unack.qlen, (seqno - 1) & 0x07); -+ -+ for (i = 0, skb = ((struct sk_buff *) &bcsp->unack)->next; i < pkts_to_be_removed -+ && skb != (struct sk_buff *) &bcsp->unack; i++) { -+ struct sk_buff *nskb; -+ -+ nskb = skb->next; -+ __skb_unlink(skb, &bcsp->unack); -+ kfree_skb(skb); -+ skb = nskb; -+ } -+ if (bcsp->unack.qlen == 0) -+ del_timer(&bcsp->tbcsp); -+ spin_unlock_irqrestore(&bcsp->unack.lock, flags); -+ -+ if (i != pkts_to_be_removed) -+ BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed); -+} -+ -+/* Handle BCSP link-establishment packets. When we -+ detect a "sync" packet, symptom that the BT module has reset, -+ we do nothing :) (yet) */ -+static void bcsp_handle_le_pkt(struct hci_uart *hu) -+{ -+ struct bcsp_struct *bcsp = hu->priv; -+ u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed }; -+ u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 }; -+ u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed }; -+ -+ /* spot "conf" pkts and reply with a "conf rsp" pkt */ -+ if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 && -+ !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) { -+ struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC); -+ -+ BT_DBG("Found a LE conf pkt"); -+ if (!nskb) -+ return; -+ memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4); -+ nskb->pkt_type = BCSP_LE_PKT; -+ -+ skb_queue_head(&bcsp->unrel, nskb); -+ hci_uart_tx_wakeup(hu); -+ } -+ /* Spot "sync" pkts. If we find one...disaster! */ -+ else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 && -+ !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) { -+ BT_ERR("Found a LE sync pkt, card has reset"); -+ } -+} -+ -+static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte) -+{ -+ const u8 c0 = 0xc0, db = 0xdb; -+ -+ switch (bcsp->rx_esc_state) { -+ case BCSP_ESCSTATE_NOESC: -+ switch (byte) { -+ case 0xdb: -+ bcsp->rx_esc_state = BCSP_ESCSTATE_ESC; -+ break; -+ default: -+ memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1); -+ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && -+ bcsp->rx_state != BCSP_W4_CRC) -+ bcsp_crc_update(&bcsp->message_crc, byte); -+ bcsp->rx_count--; -+ } -+ break; -+ -+ case BCSP_ESCSTATE_ESC: -+ switch (byte) { -+ case 0xdc: -+ memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1); -+ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && -+ bcsp->rx_state != BCSP_W4_CRC) -+ bcsp_crc_update(&bcsp-> message_crc, 0xc0); -+ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; -+ bcsp->rx_count--; -+ break; -+ -+ case 0xdd: -+ memcpy(skb_put(bcsp->rx_skb, 1), &db, 1); -+ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && -+ bcsp->rx_state != BCSP_W4_CRC) -+ bcsp_crc_update(&bcsp-> message_crc, 0xdb); -+ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; -+ bcsp->rx_count--; -+ break; -+ -+ default: -+ BT_ERR ("Invalid byte %02x after esc byte", byte); -+ kfree_skb(bcsp->rx_skb); -+ bcsp->rx_skb = NULL; -+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; -+ bcsp->rx_count = 0; -+ } -+ } -+} -+ -+static inline void bcsp_complete_rx_pkt(struct hci_uart *hu) -+{ -+ struct bcsp_struct *bcsp = hu->priv; -+ int pass_up; -+ -+ if (bcsp->rx_skb->data[0] & 0x80) { /* reliable pkt */ -+ BT_DBG("Received seqno %u from card", bcsp->rxseq_txack); -+ bcsp->rxseq_txack++; -+ bcsp->rxseq_txack %= 0x8; -+ bcsp->txack_req = 1; -+ -+ /* If needed, transmit an ack pkt */ -+ hci_uart_tx_wakeup(hu); -+ } -+ -+ bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07; -+ BT_DBG("Request for pkt %u from card", bcsp->rxack); -+ -+ bcsp_pkt_cull(bcsp); -+ if ((bcsp->rx_skb->data[1] & 0x0f) == 6 && -+ bcsp->rx_skb->data[0] & 0x80) { -+ bcsp->rx_skb->pkt_type = HCI_ACLDATA_PKT; -+ pass_up = 1; -+ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 && -+ bcsp->rx_skb->data[0] & 0x80) { -+ bcsp->rx_skb->pkt_type = HCI_EVENT_PKT; -+ pass_up = 1; -+ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) { -+ bcsp->rx_skb->pkt_type = HCI_SCODATA_PKT; -+ pass_up = 1; -+ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 && -+ !(bcsp->rx_skb->data[0] & 0x80)) { -+ bcsp_handle_le_pkt(hu); -+ pass_up = 0; -+ } else -+ pass_up = 0; -+ -+ if (!pass_up) { -+ if ((bcsp->rx_skb->data[1] & 0x0f) != 0 && -+ (bcsp->rx_skb->data[1] & 0x0f) != 1) { -+ BT_ERR ("Packet for unknown channel (%u %s)", -+ bcsp->rx_skb->data[1] & 0x0f, -+ bcsp->rx_skb->data[0] & 0x80 ? -+ "reliable" : "unreliable"); -+ } -+ kfree_skb(bcsp->rx_skb); -+ } else { -+ /* Pull out BCSP hdr */ -+ skb_pull(bcsp->rx_skb, 4); -+ -+ hci_recv_frame(bcsp->rx_skb); -+ } -+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; -+ bcsp->rx_skb = NULL; -+} -+ -+/* Recv data */ -+static int bcsp_recv(struct hci_uart *hu, void *data, int count) -+{ -+ struct bcsp_struct *bcsp = hu->priv; -+ register unsigned char *ptr; -+ -+ BT_DBG("hu %p count %d rx_state %ld rx_count %ld", -+ hu, count, bcsp->rx_state, bcsp->rx_count); -+ -+ ptr = data; -+ while (count) { -+ if (bcsp->rx_count) { -+ if (*ptr == 0xc0) { -+ BT_ERR("Short BCSP packet"); -+ kfree_skb(bcsp->rx_skb); -+ bcsp->rx_state = BCSP_W4_PKT_START; -+ bcsp->rx_count = 0; -+ } else -+ bcsp_unslip_one_byte(bcsp, *ptr); -+ -+ ptr++; count--; -+ continue; -+ } -+ -+ switch (bcsp->rx_state) { -+ case BCSP_W4_BCSP_HDR: -+ if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] + -+ bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) { -+ BT_ERR("Error in BCSP hdr checksum"); -+ kfree_skb(bcsp->rx_skb); -+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; -+ bcsp->rx_count = 0; -+ continue; -+ } -+ if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */ -+ && (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) { -+ BT_ERR ("Out-of-order packet arrived, got %u expected %u", -+ bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack); -+ -+ kfree_skb(bcsp->rx_skb); -+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; -+ bcsp->rx_count = 0; -+ continue; -+ } -+ bcsp->rx_state = BCSP_W4_DATA; -+ bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) + -+ (bcsp->rx_skb->data[2] << 4); /* May be 0 */ -+ continue; -+ -+ case BCSP_W4_DATA: -+ if (bcsp->rx_skb->data[0] & 0x40) { /* pkt with crc */ -+ bcsp->rx_state = BCSP_W4_CRC; -+ bcsp->rx_count = 2; -+ } else -+ bcsp_complete_rx_pkt(hu); -+ continue; -+ -+ case BCSP_W4_CRC: -+ if (bcsp_crc_reverse(bcsp->message_crc) != -+ (bcsp->rx_skb->data[bcsp->rx_skb->len - 2] << 8) + -+ bcsp->rx_skb->data[bcsp->rx_skb->len - 1]) { -+ -+ BT_ERR ("Checksum failed: computed %04x received %04x", -+ bcsp_crc_reverse(bcsp->message_crc), -+ (bcsp->rx_skb-> data[bcsp->rx_skb->len - 2] << 8) + -+ bcsp->rx_skb->data[bcsp->rx_skb->len - 1]); -+ -+ kfree_skb(bcsp->rx_skb); -+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; -+ bcsp->rx_count = 0; -+ continue; -+ } -+ skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2); -+ bcsp_complete_rx_pkt(hu); -+ continue; -+ -+ case BCSP_W4_PKT_DELIMITER: -+ switch (*ptr) { -+ case 0xc0: -+ bcsp->rx_state = BCSP_W4_PKT_START; -+ break; -+ default: -+ /*BT_ERR("Ignoring byte %02x", *ptr);*/ -+ break; -+ } -+ ptr++; count--; -+ break; -+ -+ case BCSP_W4_PKT_START: -+ switch (*ptr) { -+ case 0xc0: -+ ptr++; count--; -+ break; -+ -+ default: -+ bcsp->rx_state = BCSP_W4_BCSP_HDR; -+ bcsp->rx_count = 4; -+ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; -+ BCSP_CRC_INIT(bcsp->message_crc); -+ -+ /* Do not increment ptr or decrement count -+ * Allocate packet. Max len of a BCSP pkt= -+ * 0xFFF (payload) +4 (header) +2 (crc) */ -+ -+ bcsp->rx_skb = bluez_skb_alloc(0x1005, GFP_ATOMIC); -+ if (!bcsp->rx_skb) { -+ BT_ERR("Can't allocate mem for new packet"); -+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; -+ bcsp->rx_count = 0; -+ return 0; -+ } -+ bcsp->rx_skb->dev = (void *) &hu->hdev; -+ break; -+ } -+ break; -+ } -+ } -+ return count; -+} -+ -+ /* Arrange to retransmit all messages in the relq. */ -+static void bcsp_timed_event(unsigned long arg) -+{ -+ struct hci_uart *hu = (struct hci_uart *) arg; -+ struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv; -+ struct sk_buff *skb; -+ unsigned long flags; -+ -+ BT_ERR("Timeout, retransmitting %u pkts", bcsp->unack.qlen); -+ spin_lock_irqsave(&bcsp->unack.lock, flags); -+ -+ while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) { -+ bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07; -+ skb_queue_head(&bcsp->rel, skb); -+ } -+ -+ spin_unlock_irqrestore(&bcsp->unack.lock, flags); -+ -+ hci_uart_tx_wakeup(hu); -+} -+ -+static int bcsp_open(struct hci_uart *hu) -+{ -+ struct bcsp_struct *bcsp; -+ -+ BT_DBG("hu %p", hu); -+ -+ bcsp = kmalloc(sizeof(*bcsp), GFP_ATOMIC); -+ if (!bcsp) -+ return -ENOMEM; -+ memset(bcsp, 0, sizeof(*bcsp)); -+ -+ hu->priv = bcsp; -+ skb_queue_head_init(&bcsp->unack); -+ skb_queue_head_init(&bcsp->rel); -+ skb_queue_head_init(&bcsp->unrel); -+ -+ init_timer(&bcsp->tbcsp); -+ bcsp->tbcsp.function = bcsp_timed_event; -+ bcsp->tbcsp.data = (u_long) hu; -+ -+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER; -+ -+ return 0; -+} -+ -+static int bcsp_close(struct hci_uart *hu) -+{ -+ struct bcsp_struct *bcsp = hu->priv; -+ hu->priv = NULL; -+ -+ BT_DBG("hu %p", hu); -+ -+ skb_queue_purge(&bcsp->unack); -+ skb_queue_purge(&bcsp->rel); -+ skb_queue_purge(&bcsp->unrel); -+ del_timer(&bcsp->tbcsp); -+ -+ kfree(bcsp); -+ return 0; -+} -+ -+static struct hci_uart_proto bcsp = { -+ id: HCI_UART_BCSP, -+ open: bcsp_open, -+ close: bcsp_close, -+ enqueue: bcsp_enqueue, -+ dequeue: bcsp_dequeue, -+ recv: bcsp_recv, -+ flush: bcsp_flush -+}; -+ -+int bcsp_init(void) -+{ -+ return hci_uart_register_proto(&bcsp); -+} -+ -+int bcsp_deinit(void) -+{ -+ return hci_uart_unregister_proto(&bcsp); -+} ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/hci_bcsp.h 2004-01-25 23:37:39.000000000 +0100 -@@ -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__ */ ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/hci_h4.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,277 @@ -+/* -+ BlueZ - Bluetooth protocol stack for Linux -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * BlueZ HCI UART(H4) protocol. -+ * -+ * $Id: hci_h4.c,v 1.3 2002/09/09 01:17:32 maxk Exp $ -+ */ -+#define VERSION "1.2" -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/version.h> -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/sched.h> -+#include <linux/types.h> -+#include <linux/fcntl.h> -+#include <linux/interrupt.h> -+#include <linux/ptrace.h> -+#include <linux/poll.h> -+ -+#include <linux/slab.h> -+#include <linux/tty.h> -+#include <linux/errno.h> -+#include <linux/string.h> -+#include <linux/signal.h> -+#include <linux/ioctl.h> -+#include <linux/skbuff.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+#include "hci_uart.h" -+#include "hci_h4.h" -+ -+#ifndef HCI_UART_DEBUG -+#undef BT_DBG -+#define BT_DBG( A... ) -+#undef BT_DMP -+#define BT_DMP( A... ) -+#endif -+ -+/* Initialize protocol */ -+static int h4_open(struct hci_uart *hu) -+{ -+ struct h4_struct *h4; -+ -+ BT_DBG("hu %p", hu); -+ -+ h4 = kmalloc(sizeof(*h4), GFP_ATOMIC); -+ if (!h4) -+ return -ENOMEM; -+ memset(h4, 0, sizeof(*h4)); -+ -+ skb_queue_head_init(&h4->txq); -+ -+ hu->priv = h4; -+ return 0; -+} -+ -+/* Flush protocol data */ -+static int h4_flush(struct hci_uart *hu) -+{ -+ struct h4_struct *h4 = hu->priv; -+ -+ BT_DBG("hu %p", hu); -+ skb_queue_purge(&h4->txq); -+ return 0; -+} -+ -+/* Close protocol */ -+static int h4_close(struct hci_uart *hu) -+{ -+ struct h4_struct *h4 = hu->priv; -+ hu->priv = NULL; -+ -+ BT_DBG("hu %p", hu); -+ -+ skb_queue_purge(&h4->txq); -+ if (h4->rx_skb) -+ kfree_skb(h4->rx_skb); -+ -+ hu->priv = NULL; -+ kfree(h4); -+ return 0; -+} -+ -+/* Enqueue frame for transmittion (padding, crc, etc) */ -+static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb) -+{ -+ struct h4_struct *h4 = hu->priv; -+ -+ BT_DBG("hu %p skb %p", hu, skb); -+ -+ /* Prepend skb with frame type */ -+ memcpy(skb_push(skb, 1), &skb->pkt_type, 1); -+ skb_queue_tail(&h4->txq, skb); -+ return 0; -+} -+ -+static inline int h4_check_data_len(struct h4_struct *h4, int len) -+{ -+ register int room = skb_tailroom(h4->rx_skb); -+ -+ BT_DBG("len %d room %d", len, room); -+ if (!len) { -+ BT_DMP(h4->rx_skb->data, h4->rx_skb->len); -+ hci_recv_frame(h4->rx_skb); -+ } else if (len > room) { -+ BT_ERR("Data length is too large"); -+ kfree_skb(h4->rx_skb); -+ } else { -+ h4->rx_state = H4_W4_DATA; -+ h4->rx_count = len; -+ return len; -+ } -+ -+ h4->rx_state = H4_W4_PACKET_TYPE; -+ h4->rx_skb = NULL; -+ h4->rx_count = 0; -+ return 0; -+} -+ -+/* Recv data */ -+static int h4_recv(struct hci_uart *hu, void *data, int count) -+{ -+ struct h4_struct *h4 = hu->priv; -+ register char *ptr; -+ hci_event_hdr *eh; -+ hci_acl_hdr *ah; -+ hci_sco_hdr *sh; -+ register int len, type, dlen; -+ -+ BT_DBG("hu %p count %d rx_state %ld rx_count %ld", -+ hu, count, h4->rx_state, h4->rx_count); -+ -+ ptr = data; -+ while (count) { -+ if (h4->rx_count) { -+ len = MIN(h4->rx_count, count); -+ memcpy(skb_put(h4->rx_skb, len), ptr, len); -+ h4->rx_count -= len; count -= len; ptr += len; -+ -+ if (h4->rx_count) -+ continue; -+ -+ switch (h4->rx_state) { -+ case H4_W4_DATA: -+ BT_DBG("Complete data"); -+ -+ BT_DMP(h4->rx_skb->data, h4->rx_skb->len); -+ -+ hci_recv_frame(h4->rx_skb); -+ -+ h4->rx_state = H4_W4_PACKET_TYPE; -+ h4->rx_skb = NULL; -+ continue; -+ -+ case H4_W4_EVENT_HDR: -+ eh = (hci_event_hdr *) h4->rx_skb->data; -+ -+ BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen); -+ -+ h4_check_data_len(h4, eh->plen); -+ continue; -+ -+ case H4_W4_ACL_HDR: -+ ah = (hci_acl_hdr *) h4->rx_skb->data; -+ dlen = __le16_to_cpu(ah->dlen); -+ -+ BT_DBG("ACL header: dlen %d", dlen); -+ -+ h4_check_data_len(h4, dlen); -+ continue; -+ -+ case H4_W4_SCO_HDR: -+ sh = (hci_sco_hdr *) h4->rx_skb->data; -+ -+ BT_DBG("SCO header: dlen %d", sh->dlen); -+ -+ h4_check_data_len(h4, sh->dlen); -+ continue; -+ } -+ } -+ -+ /* H4_W4_PACKET_TYPE */ -+ switch (*ptr) { -+ case HCI_EVENT_PKT: -+ BT_DBG("Event packet"); -+ h4->rx_state = H4_W4_EVENT_HDR; -+ h4->rx_count = HCI_EVENT_HDR_SIZE; -+ type = HCI_EVENT_PKT; -+ break; -+ -+ case HCI_ACLDATA_PKT: -+ BT_DBG("ACL packet"); -+ h4->rx_state = H4_W4_ACL_HDR; -+ h4->rx_count = HCI_ACL_HDR_SIZE; -+ type = HCI_ACLDATA_PKT; -+ break; -+ -+ case HCI_SCODATA_PKT: -+ BT_DBG("SCO packet"); -+ h4->rx_state = H4_W4_SCO_HDR; -+ h4->rx_count = HCI_SCO_HDR_SIZE; -+ type = HCI_SCODATA_PKT; -+ break; -+ -+ default: -+ BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr); -+ hu->hdev.stat.err_rx++; -+ ptr++; count--; -+ continue; -+ }; -+ ptr++; count--; -+ -+ /* Allocate packet */ -+ h4->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); -+ if (!h4->rx_skb) { -+ BT_ERR("Can't allocate mem for new packet"); -+ h4->rx_state = H4_W4_PACKET_TYPE; -+ h4->rx_count = 0; -+ return 0; -+ } -+ h4->rx_skb->dev = (void *) &hu->hdev; -+ h4->rx_skb->pkt_type = type; -+ } -+ return count; -+} -+ -+static struct sk_buff *h4_dequeue(struct hci_uart *hu) -+{ -+ struct h4_struct *h4 = hu->priv; -+ return skb_dequeue(&h4->txq); -+} -+ -+static struct hci_uart_proto h4p = { -+ id: HCI_UART_H4, -+ open: h4_open, -+ close: h4_close, -+ recv: h4_recv, -+ enqueue: h4_enqueue, -+ dequeue: h4_dequeue, -+ flush: h4_flush, -+}; -+ -+int h4_init(void) -+{ -+ return hci_uart_register_proto(&h4p); -+} -+ -+int h4_deinit(void) -+{ -+ return hci_uart_unregister_proto(&h4p); -+} ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/hci_h4.h 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,44 @@ -+/* -+ BlueZ - Bluetooth protocol stack for Linux -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * $Id: hci_h4.h,v 1.2 2002/09/09 01:17:32 maxk Exp $ -+ */ -+ -+#ifdef __KERNEL__ -+struct h4_struct { -+ unsigned long rx_state; -+ unsigned long rx_count; -+ struct sk_buff *rx_skb; -+ struct sk_buff_head txq; -+}; -+ -+/* H4 receiver States */ -+#define H4_W4_PACKET_TYPE 0 -+#define H4_W4_EVENT_HDR 1 -+#define H4_W4_ACL_HDR 2 -+#define H4_W4_SCO_HDR 3 -+#define H4_W4_DATA 4 -+ -+#endif /* __KERNEL__ */ ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/hci_ldisc.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,580 @@ -+/* -+ BlueZ - Bluetooth protocol stack for Linux -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * BlueZ HCI UART driver. -+ * -+ * $Id: hci_ldisc.c,v 1.5 2002/10/02 18:37:20 maxk Exp $ -+ */ -+#define VERSION "2.1" -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/version.h> -+#include <linux/config.h> -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/sched.h> -+#include <linux/types.h> -+#include <linux/fcntl.h> -+#include <linux/interrupt.h> -+#include <linux/ptrace.h> -+#include <linux/poll.h> -+ -+#include <linux/slab.h> -+#include <linux/tty.h> -+#include <linux/errno.h> -+#include <linux/string.h> -+#include <linux/signal.h> -+#include <linux/ioctl.h> -+#include <linux/skbuff.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+#include "hci_uart.h" -+ -+#ifndef HCI_UART_DEBUG -+#undef BT_DBG -+#define BT_DBG( A... ) -+#undef BT_DMP -+#define BT_DMP( A... ) -+#endif -+ -+static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO]; -+ -+int hci_uart_register_proto(struct hci_uart_proto *p) -+{ -+ if (p->id >= HCI_UART_MAX_PROTO) -+ return -EINVAL; -+ -+ if (hup[p->id]) -+ return -EEXIST; -+ -+ hup[p->id] = p; -+ return 0; -+} -+ -+int hci_uart_unregister_proto(struct hci_uart_proto *p) -+{ -+ if (p->id >= HCI_UART_MAX_PROTO) -+ return -EINVAL; -+ -+ if (!hup[p->id]) -+ return -EINVAL; -+ -+ hup[p->id] = NULL; -+ return 0; -+} -+ -+static struct hci_uart_proto *hci_uart_get_proto(unsigned int id) -+{ -+ if (id >= HCI_UART_MAX_PROTO) -+ return NULL; -+ return hup[id]; -+} -+ -+static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type) -+{ -+ struct hci_dev *hdev = &hu->hdev; -+ -+ /* Update HCI stat counters */ -+ switch (pkt_type) { -+ case HCI_COMMAND_PKT: -+ hdev->stat.cmd_tx++; -+ break; -+ -+ case HCI_ACLDATA_PKT: -+ hdev->stat.acl_tx++; -+ break; -+ -+ case HCI_SCODATA_PKT: -+ hdev->stat.cmd_tx++; -+ break; -+ } -+} -+ -+static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) -+{ -+ struct sk_buff *skb = hu->tx_skb; -+ if (!skb) -+ skb = hu->proto->dequeue(hu); -+ else -+ hu->tx_skb = NULL; -+ return skb; -+} -+ -+int hci_uart_tx_wakeup(struct hci_uart *hu) -+{ -+ struct tty_struct *tty = hu->tty; -+ struct hci_dev *hdev = &hu->hdev; -+ struct sk_buff *skb; -+ -+ if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) { -+ set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); -+ return 0; -+ } -+ -+ BT_DBG(""); -+ -+restart: -+ clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); -+ -+ while ((skb = hci_uart_dequeue(hu))) { -+ int len; -+ -+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); -+ len = tty->driver.write(tty, 0, skb->data, skb->len); -+ hdev->stat.byte_tx += len; -+ -+ skb_pull(skb, len); -+ if (skb->len) { -+ hu->tx_skb = skb; -+ break; -+ } -+ -+ hci_uart_tx_complete(hu, skb->pkt_type); -+ kfree_skb(skb); -+ } -+ -+ if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)) -+ goto restart; -+ -+ clear_bit(HCI_UART_SENDING, &hu->tx_state); -+ return 0; -+} -+ -+/* ------- Interface to HCI layer ------ */ -+/* Initialize device */ -+static int hci_uart_open(struct hci_dev *hdev) -+{ -+ BT_DBG("%s %p", hdev->name, hdev); -+ -+ /* Nothing to do for UART driver */ -+ -+ set_bit(HCI_RUNNING, &hdev->flags); -+ return 0; -+} -+ -+/* Reset device */ -+static int hci_uart_flush(struct hci_dev *hdev) -+{ -+ struct hci_uart *hu = (struct hci_uart *) hdev->driver_data; -+ struct tty_struct *tty = hu->tty; -+ -+ BT_DBG("hdev %p tty %p", hdev, tty); -+ -+ if (hu->tx_skb) { -+ kfree_skb(hu->tx_skb); hu->tx_skb = NULL; -+ } -+ -+ /* Flush any pending characters in the driver and discipline. */ -+ if (tty->ldisc.flush_buffer) -+ tty->ldisc.flush_buffer(tty); -+ -+ if (tty->driver.flush_buffer) -+ tty->driver.flush_buffer(tty); -+ -+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) -+ hu->proto->flush(hu); -+ -+ return 0; -+} -+ -+/* Close device */ -+static int hci_uart_close(struct hci_dev *hdev) -+{ -+ BT_DBG("hdev %p", hdev); -+ -+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) -+ return 0; -+ -+ hci_uart_flush(hdev); -+ return 0; -+} -+ -+/* Send frames from HCI layer */ -+static int hci_uart_send_frame(struct sk_buff *skb) -+{ -+ struct hci_dev* hdev = (struct hci_dev *) skb->dev; -+ struct tty_struct *tty; -+ struct hci_uart *hu; -+ -+ if (!hdev) { -+ BT_ERR("Frame for uknown device (hdev=NULL)"); -+ return -ENODEV; -+ } -+ -+ if (!test_bit(HCI_RUNNING, &hdev->flags)) -+ return -EBUSY; -+ -+ hu = (struct hci_uart *) hdev->driver_data; -+ tty = hu->tty; -+ -+ BT_DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len); -+ -+ hu->proto->enqueue(hu, skb); -+ -+ hci_uart_tx_wakeup(hu); -+ return 0; -+} -+ -+static void hci_uart_destruct(struct hci_dev *hdev) -+{ -+ struct hci_uart *hu; -+ -+ if (!hdev) return; -+ -+ BT_DBG("%s", hdev->name); -+ -+ hu = (struct hci_uart *) hdev->driver_data; -+ kfree(hu); -+ -+ MOD_DEC_USE_COUNT; -+} -+ -+/* ------ LDISC part ------ */ -+/* hci_uart_tty_open -+ * -+ * Called when line discipline changed to HCI_UART. -+ * -+ * Arguments: -+ * tty pointer to tty info structure -+ * Return Value: -+ * 0 if success, otherwise error code -+ */ -+static int hci_uart_tty_open(struct tty_struct *tty) -+{ -+ struct hci_uart *hu = (void *) tty->disc_data; -+ -+ BT_DBG("tty %p", tty); -+ -+ if (hu) -+ return -EEXIST; -+ -+ if (!(hu = kmalloc(sizeof(struct hci_uart), GFP_KERNEL))) { -+ BT_ERR("Can't allocate controll structure"); -+ return -ENFILE; -+ } -+ memset(hu, 0, sizeof(struct hci_uart)); -+ -+ tty->disc_data = hu; -+ hu->tty = tty; -+ -+ spin_lock_init(&hu->rx_lock); -+ -+ /* Flush any pending characters in the driver and line discipline */ -+ if (tty->ldisc.flush_buffer) -+ tty->ldisc.flush_buffer(tty); -+ -+ if (tty->driver.flush_buffer) -+ tty->driver.flush_buffer(tty); -+ -+ MOD_INC_USE_COUNT; -+ return 0; -+} -+ -+/* hci_uart_tty_close() -+ * -+ * Called when the line discipline is changed to something -+ * else, the tty is closed, or the tty detects a hangup. -+ */ -+static void hci_uart_tty_close(struct tty_struct *tty) -+{ -+ struct hci_uart *hu = (void *)tty->disc_data; -+ -+ BT_DBG("tty %p", tty); -+ -+ /* Detach from the tty */ -+ tty->disc_data = NULL; -+ -+ if (hu) { -+ struct hci_dev *hdev = &hu->hdev; -+ hci_uart_close(hdev); -+ -+ if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { -+ hu->proto->close(hu); -+ hci_unregister_dev(hdev); -+ } -+ -+ MOD_DEC_USE_COUNT; -+ } -+} -+ -+/* hci_uart_tty_wakeup() -+ * -+ * Callback for transmit wakeup. Called when low level -+ * device driver can accept more send data. -+ * -+ * Arguments: tty pointer to associated tty instance data -+ * Return Value: None -+ */ -+static void hci_uart_tty_wakeup(struct tty_struct *tty) -+{ -+ struct hci_uart *hu = (void *)tty->disc_data; -+ -+ BT_DBG(""); -+ -+ if (!hu) -+ return; -+ -+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); -+ -+ if (tty != hu->tty) -+ return; -+ -+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) -+ hci_uart_tx_wakeup(hu); -+} -+ -+/* hci_uart_tty_room() -+ * -+ * Callback function from tty driver. Return the amount of -+ * space left in the receiver's buffer to decide if remote -+ * transmitter is to be throttled. -+ * -+ * Arguments: tty pointer to associated tty instance data -+ * Return Value: number of bytes left in receive buffer -+ */ -+static int hci_uart_tty_room (struct tty_struct *tty) -+{ -+ return 65536; -+} -+ -+/* hci_uart_tty_receive() -+ * -+ * Called by tty low level driver when receive data is -+ * available. -+ * -+ * Arguments: tty pointer to tty isntance data -+ * data pointer to received data -+ * flags pointer to flags for data -+ * count count of received data in bytes -+ * -+ * Return Value: None -+ */ -+static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count) -+{ -+ struct hci_uart *hu = (void *)tty->disc_data; -+ -+ if (!hu || tty != hu->tty) -+ return; -+ -+ if (!test_bit(HCI_UART_PROTO_SET, &hu->flags)) -+ return; -+ -+ spin_lock(&hu->rx_lock); -+ hu->proto->recv(hu, (void *) data, count); -+ hu->hdev.stat.byte_rx += count; -+ spin_unlock(&hu->rx_lock); -+ -+ if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle) -+ tty->driver.unthrottle(tty); -+} -+ -+static int hci_uart_register_dev(struct hci_uart *hu) -+{ -+ struct hci_dev *hdev; -+ -+ BT_DBG(""); -+ -+ /* Initialize and register HCI device */ -+ hdev = &hu->hdev; -+ -+ hdev->type = HCI_UART; -+ hdev->driver_data = hu; -+ -+ hdev->open = hci_uart_open; -+ hdev->close = hci_uart_close; -+ hdev->flush = hci_uart_flush; -+ hdev->send = hci_uart_send_frame; -+ hdev->destruct = hci_uart_destruct; -+ -+ if (hci_register_dev(hdev) < 0) { -+ BT_ERR("Can't register HCI device %s", hdev->name); -+ return -ENODEV; -+ } -+ MOD_INC_USE_COUNT; -+ return 0; -+} -+ -+static int hci_uart_set_proto(struct hci_uart *hu, int id) -+{ -+ struct hci_uart_proto *p; -+ int err; -+ -+ p = hci_uart_get_proto(id); -+ if (!p) -+ return -EPROTONOSUPPORT; -+ -+ err = p->open(hu); -+ if (err) -+ return err; -+ -+ hu->proto = p; -+ -+ err = hci_uart_register_dev(hu); -+ if (err) { -+ p->close(hu); -+ return err; -+ } -+ return 0; -+} -+ -+/* hci_uart_tty_ioctl() -+ * -+ * Process IOCTL system call for the tty device. -+ * -+ * Arguments: -+ * -+ * tty pointer to tty instance data -+ * file pointer to open file object for device -+ * cmd IOCTL command code -+ * arg argument for IOCTL call (cmd dependent) -+ * -+ * Return Value: Command dependent -+ */ -+static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, -+ unsigned int cmd, unsigned long arg) -+{ -+ struct hci_uart *hu = (void *)tty->disc_data; -+ int err = 0; -+ -+ BT_DBG(""); -+ -+ /* Verify the status of the device */ -+ if (!hu) -+ return -EBADF; -+ -+ switch (cmd) { -+ case HCIUARTSETPROTO: -+ if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) { -+ err = hci_uart_set_proto(hu, arg); -+ if (err) { -+ clear_bit(HCI_UART_PROTO_SET, &hu->flags); -+ return err; -+ } -+ tty->low_latency = 1; -+ } else -+ return -EBUSY; -+ -+ case HCIUARTGETPROTO: -+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) -+ return hu->proto->id; -+ return -EUNATCH; -+ -+ default: -+ err = n_tty_ioctl(tty, file, cmd, arg); -+ break; -+ }; -+ -+ return err; -+} -+ -+/* -+ * We don't provide read/write/poll interface for user space. -+ */ -+static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr) -+{ -+ return 0; -+} -+static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count) -+{ -+ return 0; -+} -+static unsigned int hci_uart_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait) -+{ -+ return 0; -+} -+ -+#ifdef CONFIG_BLUEZ_HCIUART_H4 -+int h4_init(void); -+int h4_deinit(void); -+#endif -+#ifdef CONFIG_BLUEZ_HCIUART_BCSP -+int bcsp_init(void); -+int bcsp_deinit(void); -+#endif -+ -+int __init hci_uart_init(void) -+{ -+ static struct tty_ldisc hci_uart_ldisc; -+ int err; -+ -+ BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", -+ VERSION); -+ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); -+ -+ /* Register the tty discipline */ -+ -+ memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc)); -+ hci_uart_ldisc.magic = TTY_LDISC_MAGIC; -+ hci_uart_ldisc.name = "n_hci"; -+ hci_uart_ldisc.open = hci_uart_tty_open; -+ hci_uart_ldisc.close = hci_uart_tty_close; -+ hci_uart_ldisc.read = hci_uart_tty_read; -+ hci_uart_ldisc.write = hci_uart_tty_write; -+ hci_uart_ldisc.ioctl = hci_uart_tty_ioctl; -+ hci_uart_ldisc.poll = hci_uart_tty_poll; -+ hci_uart_ldisc.receive_room= hci_uart_tty_room; -+ hci_uart_ldisc.receive_buf = hci_uart_tty_receive; -+ hci_uart_ldisc.write_wakeup= hci_uart_tty_wakeup; -+ -+ if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) { -+ BT_ERR("Can't register HCI line discipline (%d)", err); -+ return err; -+ } -+ -+#ifdef CONFIG_BLUEZ_HCIUART_H4 -+ h4_init(); -+#endif -+#ifdef CONFIG_BLUEZ_HCIUART_BCSP -+ bcsp_init(); -+#endif -+ -+ return 0; -+} -+ -+void hci_uart_cleanup(void) -+{ -+ int err; -+ -+#ifdef CONFIG_BLUEZ_HCIUART_H4 -+ h4_deinit(); -+#endif -+#ifdef CONFIG_BLUEZ_HCIUART_BCSP -+ bcsp_deinit(); -+#endif -+ -+ /* Release tty registration of line discipline */ -+ if ((err = tty_register_ldisc(N_HCI, NULL))) -+ BT_ERR("Can't unregister HCI line discipline (%d)", err); -+} -+ -+module_init(hci_uart_init); -+module_exit(hci_uart_cleanup); -+ -+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); -+MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION); -+MODULE_LICENSE("GPL"); ---- linux/drivers/bluetooth/hci_uart.c~bluetooth-2.4.18-mh11 -+++ linux/drivers/bluetooth/hci_uart.c --/* -- BlueZ - Bluetooth protocol stack for Linux -- Copyright (C) 2000-2001 Qualcomm Incorporated -- -- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License version 2 as -- published by the Free Software Foundation; -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -- -- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -- SOFTWARE IS DISCLAIMED. --*/ -- --/* -- * BlueZ HCI UART driver. -- * -- * $Id: hci_uart.c,v 1.5 2001/07/05 18:42:44 maxk Exp $ -- */ --#define VERSION "1.0" -- --#include <linux/config.h> --#include <linux/module.h> -- --#include <linux/version.h> --#include <linux/config.h> --#include <linux/kernel.h> --#include <linux/init.h> --#include <linux/sched.h> --#include <linux/types.h> --#include <linux/fcntl.h> --#include <linux/interrupt.h> --#include <linux/ptrace.h> --#include <linux/poll.h> -- --#include <linux/slab.h> --#include <linux/tty.h> --#include <linux/errno.h> --#include <linux/string.h> --#include <linux/signal.h> --#include <linux/ioctl.h> --#include <linux/skbuff.h> -- --#include <net/bluetooth/bluetooth.h> --#include <net/bluetooth/bluez.h> --#include <net/bluetooth/hci_core.h> --#include <net/bluetooth/hci_uart.h> -- --#ifndef HCI_UART_DEBUG --#undef DBG --#define DBG( A... ) --#undef DMP --#define DMP( A... ) --#endif -- --/* ------- Interface to HCI layer ------ */ --/* Initialize device */ --int n_hci_open(struct hci_dev *hdev) --{ -- DBG("%s %p", hdev->name, hdev); -- -- /* Nothing to do for UART driver */ -- -- hdev->flags |= HCI_RUNNING; -- -- return 0; --} -- --/* Reset device */ --int n_hci_flush(struct hci_dev *hdev) --{ -- struct n_hci *n_hci = (struct n_hci *) hdev->driver_data; -- struct tty_struct *tty = n_hci->tty; -- -- DBG("hdev %p tty %p", hdev, tty); -- -- /* Drop TX queue */ -- skb_queue_purge(&n_hci->txq); -- -- /* Flush any pending characters in the driver and discipline. */ -- if (tty->ldisc.flush_buffer) -- tty->ldisc.flush_buffer(tty); -- -- if (tty->driver.flush_buffer) -- tty->driver.flush_buffer(tty); -- -- return 0; --} -- --/* Close device */ --int n_hci_close(struct hci_dev *hdev) --{ -- DBG("hdev %p", hdev); -- -- hdev->flags &= ~HCI_RUNNING; -- -- n_hci_flush(hdev); -- -- return 0; --} -- --int n_hci_tx_wakeup(struct n_hci *n_hci) --{ -- register struct tty_struct *tty = n_hci->tty; -- -- if (test_and_set_bit(TRANS_SENDING, &n_hci->tx_state)) { -- set_bit(TRANS_WAKEUP, &n_hci->tx_state); -- return 0; -- } -- -- DBG(""); -- do { -- register struct sk_buff *skb; -- register int len; -- -- clear_bit(TRANS_WAKEUP, &n_hci->tx_state); -- -- if (!(skb = skb_dequeue(&n_hci->txq))) -- break; -- -- DMP(skb->data, skb->len); -- -- /* Send frame to TTY driver */ -- tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); -- len = tty->driver.write(tty, 0, skb->data, skb->len); -- -- n_hci->hdev.stat.byte_tx += len; -- -- DBG("sent %d", len); -- -- if (len == skb->len) { -- /* Full frame was sent */ -- kfree_skb(skb); -- } else { -- /* Subtract sent part and requeue */ -- skb_pull(skb, len); -- skb_queue_head(&n_hci->txq, skb); -- } -- } while (test_bit(TRANS_WAKEUP, &n_hci->tx_state)); -- clear_bit(TRANS_SENDING, &n_hci->tx_state); -- -- return 0; --} -- --/* Send frames from HCI layer */ --int n_hci_send_frame(struct sk_buff *skb) --{ -- struct hci_dev* hdev = (struct hci_dev *) skb->dev; -- struct tty_struct *tty; -- struct n_hci *n_hci; -- -- if (!hdev) { -- ERR("Frame for uknown device (hdev=NULL)"); -- return -ENODEV; -- } -- -- if (!(hdev->flags & HCI_RUNNING)) -- return -EBUSY; -- -- n_hci = (struct n_hci *) hdev->driver_data; -- tty = n_hci2tty(n_hci); -- -- DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len); -- -- switch (skb->pkt_type) { -- case HCI_COMMAND_PKT: -- hdev->stat.cmd_tx++; -- break; -- -- case HCI_ACLDATA_PKT: -- hdev->stat.acl_tx++; -- break; -- -- case HCI_SCODATA_PKT: -- hdev->stat.cmd_tx++; -- break; -- }; -- -- /* Prepend skb with frame type and queue */ -- memcpy(skb_push(skb, 1), &skb->pkt_type, 1); -- skb_queue_tail(&n_hci->txq, skb); -- -- n_hci_tx_wakeup(n_hci); -- -- return 0; --} -- --/* ------ LDISC part ------ */ -- --/* n_hci_tty_open -- * -- * Called when line discipline changed to N_HCI. -- * -- * Arguments: -- * tty pointer to tty info structure -- * Return Value: -- * 0 if success, otherwise error code -- */ --static int n_hci_tty_open(struct tty_struct *tty) --{ -- struct n_hci *n_hci = tty2n_hci(tty); -- struct hci_dev *hdev; -- -- DBG("tty %p", tty); -- -- if (n_hci) -- return -EEXIST; -- -- if (!(n_hci = kmalloc(sizeof(struct n_hci), GFP_KERNEL))) { -- ERR("Can't allocate controll structure"); -- return -ENFILE; -- } -- memset(n_hci, 0, sizeof(struct n_hci)); -- -- /* Initialize and register HCI device */ -- hdev = &n_hci->hdev; -- -- hdev->type = HCI_UART; -- hdev->driver_data = n_hci; -- -- hdev->open = n_hci_open; -- hdev->close = n_hci_close; -- hdev->flush = n_hci_flush; -- hdev->send = n_hci_send_frame; -- -- if (hci_register_dev(hdev) < 0) { -- ERR("Can't register HCI device %s", hdev->name); -- kfree(n_hci); -- return -ENODEV; -- } -- -- tty->disc_data = n_hci; -- n_hci->tty = tty; -- -- spin_lock_init(&n_hci->rx_lock); -- n_hci->rx_state = WAIT_PACKET_TYPE; -- -- skb_queue_head_init(&n_hci->txq); -- -- MOD_INC_USE_COUNT; -- -- /* Flush any pending characters in the driver and discipline. */ -- if (tty->ldisc.flush_buffer) -- tty->ldisc.flush_buffer(tty); -- -- if (tty->driver.flush_buffer) -- tty->driver.flush_buffer(tty); -- -- return 0; --} -- --/* n_hci_tty_close() -- * -- * Called when the line discipline is changed to something -- * else, the tty is closed, or the tty detects a hangup. -- */ --static void n_hci_tty_close(struct tty_struct *tty) --{ -- struct n_hci *n_hci = tty2n_hci(tty); -- struct hci_dev *hdev = &n_hci->hdev; -- -- DBG("tty %p hdev %p", tty, hdev); -- -- if (n_hci != NULL) { -- n_hci_close(hdev); -- -- if (hci_unregister_dev(hdev) < 0) { -- ERR("Can't unregister HCI device %s",hdev->name); -- } -- -- hdev->driver_data = NULL; -- tty->disc_data = NULL; -- kfree(n_hci); -- -- MOD_DEC_USE_COUNT; -- } --} -- --/* n_hci_tty_wakeup() -- * -- * Callback for transmit wakeup. Called when low level -- * device driver can accept more send data. -- * -- * Arguments: tty pointer to associated tty instance data -- * Return Value: None -- */ --static void n_hci_tty_wakeup( struct tty_struct *tty ) --{ -- struct n_hci *n_hci = tty2n_hci(tty); -- -- DBG(""); -- -- if (!n_hci) -- return; -- -- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); -- -- if (tty != n_hci->tty) -- return; -- -- n_hci_tx_wakeup(n_hci); --} -- --/* n_hci_tty_room() -- * -- * Callback function from tty driver. Return the amount of -- * space left in the receiver's buffer to decide if remote -- * transmitter is to be throttled. -- * -- * Arguments: tty pointer to associated tty instance data -- * Return Value: number of bytes left in receive buffer -- */ --static int n_hci_tty_room (struct tty_struct *tty) --{ -- return 65536; --} -- --static inline int n_hci_check_data_len(struct n_hci *n_hci, int len) --{ -- register int room = skb_tailroom(n_hci->rx_skb); -- -- DBG("len %d room %d", len, room); -- if (!len) { -- DMP(n_hci->rx_skb->data, n_hci->rx_skb->len); -- hci_recv_frame(n_hci->rx_skb); -- } else if (len > room) { -- ERR("Data length is to large"); -- kfree_skb(n_hci->rx_skb); -- n_hci->hdev.stat.err_rx++; -- } else { -- n_hci->rx_state = WAIT_DATA; -- n_hci->rx_count = len; -- return len; -- } -- -- n_hci->rx_state = WAIT_PACKET_TYPE; -- n_hci->rx_skb = NULL; -- n_hci->rx_count = 0; -- return 0; --} -- --static inline void n_hci_rx(struct n_hci *n_hci, const __u8 * data, char *flags, int count) --{ -- register const char *ptr; -- hci_event_hdr *eh; -- hci_acl_hdr *ah; -- hci_sco_hdr *sh; -- register int len, type, dlen; -- -- DBG("count %d state %ld rx_count %ld", count, n_hci->rx_state, n_hci->rx_count); -- -- n_hci->hdev.stat.byte_rx += count; -- -- ptr = data; -- while (count) { -- if (n_hci->rx_count) { -- len = MIN(n_hci->rx_count, count); -- memcpy(skb_put(n_hci->rx_skb, len), ptr, len); -- n_hci->rx_count -= len; count -= len; ptr += len; -- -- if (n_hci->rx_count) -- continue; -- -- switch (n_hci->rx_state) { -- case WAIT_DATA: -- DBG("Complete data"); -- -- DMP(n_hci->rx_skb->data, n_hci->rx_skb->len); -- -- hci_recv_frame(n_hci->rx_skb); -- -- n_hci->rx_state = WAIT_PACKET_TYPE; -- n_hci->rx_skb = NULL; -- continue; -- -- case WAIT_EVENT_HDR: -- eh = (hci_event_hdr *) n_hci->rx_skb->data; -- -- DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen); -- -- n_hci_check_data_len(n_hci, eh->plen); -- continue; -- -- case WAIT_ACL_HDR: -- ah = (hci_acl_hdr *) n_hci->rx_skb->data; -- dlen = __le16_to_cpu(ah->dlen); -- -- DBG("ACL header: dlen %d", dlen); -- -- n_hci_check_data_len(n_hci, dlen); -- continue; -- -- case WAIT_SCO_HDR: -- sh = (hci_sco_hdr *) n_hci->rx_skb->data; -- -- DBG("SCO header: dlen %d", sh->dlen); -- -- n_hci_check_data_len(n_hci, sh->dlen); -- continue; -- }; -- } -- -- /* WAIT_PACKET_TYPE */ -- switch (*ptr) { -- case HCI_EVENT_PKT: -- DBG("Event packet"); -- n_hci->rx_state = WAIT_EVENT_HDR; -- n_hci->rx_count = HCI_EVENT_HDR_SIZE; -- type = HCI_EVENT_PKT; -- break; -- -- case HCI_ACLDATA_PKT: -- DBG("ACL packet"); -- n_hci->rx_state = WAIT_ACL_HDR; -- n_hci->rx_count = HCI_ACL_HDR_SIZE; -- type = HCI_ACLDATA_PKT; -- break; -- -- case HCI_SCODATA_PKT: -- DBG("SCO packet"); -- n_hci->rx_state = WAIT_SCO_HDR; -- n_hci->rx_count = HCI_SCO_HDR_SIZE; -- type = HCI_SCODATA_PKT; -- break; -- -- default: -- ERR("Unknown HCI packet type %2.2x", (__u8)*ptr); -- n_hci->hdev.stat.err_rx++; -- ptr++; count--; -- continue; -- }; -- ptr++; count--; -- -- /* Allocate packet */ -- if (!(n_hci->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) { -- ERR("Can't allocate mem for new packet"); -- -- n_hci->rx_state = WAIT_PACKET_TYPE; -- n_hci->rx_count = 0; -- return; -- } -- n_hci->rx_skb->dev = (void *) &n_hci->hdev; -- n_hci->rx_skb->pkt_type = type; -- } --} -- --/* n_hci_tty_receive() -- * -- * Called by tty low level driver when receive data is -- * available. -- * -- * Arguments: tty pointer to tty isntance data -- * data pointer to received data -- * flags pointer to flags for data -- * count count of received data in bytes -- * -- * Return Value: None -- */ --static void n_hci_tty_receive(struct tty_struct *tty, const __u8 * data, char *flags, int count) --{ -- struct n_hci *n_hci = tty2n_hci(tty); -- -- if (!n_hci || tty != n_hci->tty) -- return; -- -- spin_lock(&n_hci->rx_lock); -- n_hci_rx(n_hci, data, flags, count); -- spin_unlock(&n_hci->rx_lock); -- -- if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle) -- tty->driver.unthrottle(tty); --} -- --/* n_hci_tty_ioctl() -- * -- * Process IOCTL system call for the tty device. -- * -- * Arguments: -- * -- * tty pointer to tty instance data -- * file pointer to open file object for device -- * cmd IOCTL command code -- * arg argument for IOCTL call (cmd dependent) -- * -- * Return Value: Command dependent -- */ --static int n_hci_tty_ioctl (struct tty_struct *tty, struct file * file, -- unsigned int cmd, unsigned long arg) --{ -- struct n_hci *n_hci = tty2n_hci(tty); -- int error = 0; -- -- DBG(""); -- -- /* Verify the status of the device */ -- if (!n_hci) -- return -EBADF; -- -- switch (cmd) { -- default: -- error = n_tty_ioctl(tty, file, cmd, arg); -- break; -- }; -- -- return error; --} -- --/* -- * We don't provide read/write/poll interface for user space. -- */ --static ssize_t n_hci_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr) --{ -- return 0; --} --static ssize_t n_hci_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count) --{ -- return 0; --} --static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait) --{ -- return 0; --} -- --int __init n_hci_init(void) --{ -- static struct tty_ldisc n_hci_ldisc; -- int err; -- -- INF("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", -- VERSION); -- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); -- -- /* Register the tty discipline */ -- -- memset(&n_hci_ldisc, 0, sizeof (n_hci_ldisc)); -- n_hci_ldisc.magic = TTY_LDISC_MAGIC; -- n_hci_ldisc.name = "n_hci"; -- n_hci_ldisc.open = n_hci_tty_open; -- n_hci_ldisc.close = n_hci_tty_close; -- n_hci_ldisc.read = n_hci_tty_read; -- n_hci_ldisc.write = n_hci_tty_write; -- n_hci_ldisc.ioctl = n_hci_tty_ioctl; -- n_hci_ldisc.poll = n_hci_tty_poll; -- n_hci_ldisc.receive_room= n_hci_tty_room; -- n_hci_ldisc.receive_buf = n_hci_tty_receive; -- n_hci_ldisc.write_wakeup= n_hci_tty_wakeup; -- -- if ((err = tty_register_ldisc(N_HCI, &n_hci_ldisc))) { -- ERR("Can't register HCI line discipline (%d)", err); -- return err; -- } -- -- return 0; --} -- --void n_hci_cleanup(void) --{ -- int err; -- -- /* Release tty registration of line discipline */ -- if ((err = tty_register_ldisc(N_HCI, NULL))) -- ERR("Can't unregister HCI line discipline (%d)", err); --} -- --module_init(n_hci_init); --module_exit(n_hci_cleanup); -- --MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); --MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION); --MODULE_LICENSE("GPL"); ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/hci_uart.h 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,81 @@ -+/* -+ BlueZ - Bluetooth protocol stack for Linux -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * $Id: hci_uart.h,v 1.2 2002/09/09 01:17:32 maxk Exp $ -+ */ -+ -+#ifndef N_HCI -+#define N_HCI 15 -+#endif -+ -+/* Ioctls */ -+#define HCIUARTSETPROTO _IOW('U', 200, int) -+#define HCIUARTGETPROTO _IOR('U', 201, int) -+ -+/* UART protocols */ -+#define HCI_UART_MAX_PROTO 3 -+ -+#define HCI_UART_H4 0 -+#define HCI_UART_BCSP 1 -+#define HCI_UART_NCSP 2 -+ -+#ifdef __KERNEL__ -+struct hci_uart; -+ -+struct hci_uart_proto { -+ unsigned int id; -+ int (*open)(struct hci_uart *hu); -+ int (*close)(struct hci_uart *hu); -+ int (*flush)(struct hci_uart *hu); -+ int (*recv)(struct hci_uart *hu, void *data, int len); -+ int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb); -+ struct sk_buff *(*dequeue)(struct hci_uart *hu); -+}; -+ -+struct hci_uart { -+ struct tty_struct *tty; -+ struct hci_dev hdev; -+ unsigned long flags; -+ -+ struct hci_uart_proto *proto; -+ void *priv; -+ -+ struct sk_buff *tx_skb; -+ unsigned long tx_state; -+ spinlock_t rx_lock; -+}; -+ -+/* HCI_UART flag bits */ -+#define HCI_UART_PROTO_SET 0 -+ -+/* TX states */ -+#define HCI_UART_SENDING 1 -+#define HCI_UART_TX_WAKEUP 2 -+ -+int hci_uart_register_proto(struct hci_uart_proto *p); -+int hci_uart_unregister_proto(struct hci_uart_proto *p); -+int hci_uart_tx_wakeup(struct hci_uart *hu); -+ -+#endif /* __KERNEL__ */ ---- linux/drivers/bluetooth/hci_usb.c~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/drivers/bluetooth/hci_usb.c 2004-01-25 23:37:39.000000000 +0100 -@@ -1,9 +1,10 @@ - /* -- BlueZ - Bluetooth protocol stack for Linux -+ HCI USB driver for Linux Bluetooth protocol stack (BlueZ) - Copyright (C) 2000-2001 Qualcomm Incorporated -- - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> - -+ Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com> -+ - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; -@@ -23,598 +24,914 @@ - */ - - /* -- * BlueZ HCI USB driver. - * Based on original USB Bluetooth driver for Linux kernel - * Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com> - * Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu> - * -- * $Id: hci_usb.c,v 1.5 2001/07/05 18:42:44 maxk Exp $ -+ * $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $ - */ --#define VERSION "1.0" -+#define VERSION "2.4" - - #include <linux/config.h> - #include <linux/module.h> - - #include <linux/version.h> --#include <linux/config.h> - #include <linux/kernel.h> - #include <linux/init.h> - #include <linux/sched.h> -+#include <linux/unistd.h> - #include <linux/types.h> --#include <linux/fcntl.h> - #include <linux/interrupt.h> --#include <linux/ptrace.h> --#include <linux/poll.h> - - #include <linux/slab.h> --#include <linux/tty.h> - #include <linux/errno.h> - #include <linux/string.h> --#include <linux/signal.h> --#include <linux/ioctl.h> - #include <linux/skbuff.h> - - #include <linux/usb.h> - - #include <net/bluetooth/bluetooth.h> --#include <net/bluetooth/bluez.h> - #include <net/bluetooth/hci_core.h> --#include <net/bluetooth/hci_usb.h> -+ -+#include "hci_usb.h" - - #ifndef HCI_USB_DEBUG --#undef DBG --#define DBG( A... ) --#undef DMP --#define DMP( A... ) -+#undef BT_DBG -+#define BT_DBG( A... ) -+#undef BT_DMP -+#define BT_DMP( A... ) - #endif - --static struct usb_device_id usb_bluetooth_ids [] = { -+#ifndef CONFIG_BLUEZ_HCIUSB_ZERO_PACKET -+#undef USB_ZERO_PACKET -+#define USB_ZERO_PACKET 0 -+#endif -+ -+static struct usb_driver hci_usb_driver; -+ -+static struct usb_device_id bluetooth_ids[] = { -+ /* Digianswer device */ -+ { USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER }, -+ -+ /* Generic Bluetooth USB device */ - { USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) }, -+ -+ /* Ericsson with non-standard id */ -+ { USB_DEVICE(0x0bdb, 0x1002) }, -+ -+ /* ALPS Module with non-standard id */ -+ { USB_DEVICE(0x044e, 0x3002) }, -+ -+ /* Bluetooth Ultraport Module from IBM */ -+ { USB_DEVICE(0x04bf, 0x030a) }, -+ - { } /* Terminating entry */ - }; - --MODULE_DEVICE_TABLE (usb, usb_bluetooth_ids); -+MODULE_DEVICE_TABLE (usb, bluetooth_ids); - --static int hci_usb_ctrl_msg(struct hci_usb *husb, struct sk_buff *skb); --static int hci_usb_write_msg(struct hci_usb *husb, struct sk_buff *skb); -+static struct usb_device_id ignore_ids[] = { -+ /* Broadcom BCM2033 without firmware */ -+ { USB_DEVICE(0x0a5c, 0x2033) }, - --static void hci_usb_unlink_urbs(struct hci_usb *husb) --{ -- usb_unlink_urb(husb->read_urb); -- usb_unlink_urb(husb->intr_urb); -- usb_unlink_urb(husb->ctrl_urb); -- usb_unlink_urb(husb->write_urb); --} -+ { } /* Terminating entry */ -+}; - --static void hci_usb_free_bufs(struct hci_usb *husb) -+struct _urb *_urb_alloc(int isoc, int gfp) - { -- if (husb->read_urb) { -- if (husb->read_urb->transfer_buffer) -- kfree(husb->read_urb->transfer_buffer); -- usb_free_urb(husb->read_urb); -+ struct _urb *_urb = kmalloc(sizeof(struct _urb) + -+ sizeof(iso_packet_descriptor_t) * isoc, gfp); -+ if (_urb) { -+ memset(_urb, 0, sizeof(*_urb)); -+ spin_lock_init(&_urb->urb.lock); - } -+ return _urb; -+} - -- if (husb->intr_urb) { -- if (husb->intr_urb->transfer_buffer) -- kfree(husb->intr_urb->transfer_buffer); -- usb_free_urb(husb->intr_urb); -+struct _urb *_urb_dequeue(struct _urb_queue *q) -+{ -+ struct _urb *_urb = NULL; -+ unsigned long flags; -+ spin_lock_irqsave(&q->lock, flags); -+ { -+ struct list_head *head = &q->head; -+ struct list_head *next = head->next; -+ if (next != head) { -+ _urb = list_entry(next, struct _urb, list); -+ list_del(next); _urb->queue = NULL; -+ } - } -+ spin_unlock_irqrestore(&q->lock, flags); -+ return _urb; -+} - -- if (husb->ctrl_urb) -- usb_free_urb(husb->ctrl_urb); -+static void hci_usb_rx_complete(struct urb *urb); -+static void hci_usb_tx_complete(struct urb *urb); - -- if (husb->write_urb) -- usb_free_urb(husb->write_urb); -+#define __pending_tx(husb, type) (&husb->pending_tx[type-1]) -+#define __pending_q(husb, type) (&husb->pending_q[type-1]) -+#define __completed_q(husb, type) (&husb->completed_q[type-1]) -+#define __transmit_q(husb, type) (&husb->transmit_q[type-1]) -+#define __reassembly(husb, type) (husb->reassembly[type-1]) - -- if (husb->intr_skb) -- kfree_skb(husb->intr_skb); -+static inline struct _urb *__get_completed(struct hci_usb *husb, int type) -+{ -+ return _urb_dequeue(__completed_q(husb, type)); - } - --/* ------- Interface to HCI layer ------ */ --/* Initialize device */ --int hci_usb_open(struct hci_dev *hdev) -+#ifdef CONFIG_BLUEZ_HCIUSB_SCO -+static void __fill_isoc_desc(struct urb *urb, int len, int mtu) - { -- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; -- int status; -- -- DBG("%s", hdev->name); -- -- husb->read_urb->dev = husb->udev; -- if ((status = usb_submit_urb(husb->read_urb))) -- DBG("read submit failed. %d", status); -- -- husb->intr_urb->dev = husb->udev; -- if ((status = usb_submit_urb(husb->intr_urb))) -- DBG("interrupt submit failed. %d", status); -+ int offset = 0, i; - -- hdev->flags |= HCI_RUNNING; -+ BT_DBG("len %d mtu %d", len, mtu); - -- return 0; -+ for (i=0; i < HCI_MAX_ISOC_FRAMES && len >= mtu; i++, offset += mtu, len -= mtu) { -+ urb->iso_frame_desc[i].offset = offset; -+ urb->iso_frame_desc[i].length = mtu; -+ BT_DBG("desc %d offset %d len %d", i, offset, mtu); -+ } -+ if (len && i < HCI_MAX_ISOC_FRAMES) { -+ urb->iso_frame_desc[i].offset = offset; -+ urb->iso_frame_desc[i].length = len; -+ BT_DBG("desc %d offset %d len %d", i, offset, len); -+ i++; -+ } -+ urb->number_of_packets = i; - } -+#endif - --/* Reset device */ --int hci_usb_flush(struct hci_dev *hdev) -+static int hci_usb_intr_rx_submit(struct hci_usb *husb) - { -- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; -+ struct _urb *_urb; -+ struct urb *urb; -+ int err, pipe, interval, size; -+ void *buf; - -- DBG("%s", hdev->name); -+ BT_DBG("%s", husb->hdev.name); - -- /* Drop TX queues */ -- skb_queue_purge(&husb->tx_ctrl_q); -- skb_queue_purge(&husb->tx_write_q); -+ size = husb->intr_in_ep->wMaxPacketSize; - -- return 0; -+ buf = kmalloc(size, GFP_ATOMIC); -+ if (!buf) -+ return -ENOMEM; -+ -+ _urb = _urb_alloc(0, GFP_ATOMIC); -+ if (!_urb) { -+ kfree(buf); -+ return -ENOMEM; -+ } -+ _urb->type = HCI_EVENT_PKT; -+ _urb_queue_tail(__pending_q(husb, _urb->type), _urb); -+ -+ urb = &_urb->urb; -+ pipe = usb_rcvintpipe(husb->udev, husb->intr_in_ep->bEndpointAddress); -+ interval = husb->intr_in_ep->bInterval; -+ FILL_INT_URB(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb, interval); -+ -+ err = usb_submit_urb(urb); -+ if (err) { -+ BT_ERR("%s intr rx submit failed urb %p err %d", -+ husb->hdev.name, urb, err); -+ _urb_unlink(_urb); -+ _urb_free(_urb); -+ kfree(buf); -+ } -+ return err; - } - --/* Close device */ --int hci_usb_close(struct hci_dev *hdev) -+static int hci_usb_bulk_rx_submit(struct hci_usb *husb) - { -- struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; -+ struct _urb *_urb; -+ struct urb *urb; -+ int err, pipe, size = HCI_MAX_FRAME_SIZE; -+ void *buf; - -- DBG("%s", hdev->name); -+ buf = kmalloc(size, GFP_ATOMIC); -+ if (!buf) -+ return -ENOMEM; - -- hdev->flags &= ~HCI_RUNNING; -- hci_usb_unlink_urbs(husb); -+ _urb = _urb_alloc(0, GFP_ATOMIC); -+ if (!_urb) { -+ kfree(buf); -+ return -ENOMEM; -+ } -+ _urb->type = HCI_ACLDATA_PKT; -+ _urb_queue_tail(__pending_q(husb, _urb->type), _urb); - -- hci_usb_flush(hdev); -+ urb = &_urb->urb; -+ pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep->bEndpointAddress); -+ FILL_BULK_URB(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb); -+ urb->transfer_flags = USB_QUEUE_BULK; - -- return 0; -+ BT_DBG("%s urb %p", husb->hdev.name, urb); -+ -+ err = usb_submit_urb(urb); -+ if (err) { -+ BT_ERR("%s bulk rx submit failed urb %p err %d", -+ husb->hdev.name, urb, err); -+ _urb_unlink(_urb); -+ _urb_free(_urb); -+ kfree(buf); -+ } -+ return err; - } - --void hci_usb_ctrl_wakeup(struct hci_usb *husb) -+#ifdef CONFIG_BLUEZ_HCIUSB_SCO -+static int hci_usb_isoc_rx_submit(struct hci_usb *husb) - { -- struct sk_buff *skb; -- -- if (test_and_set_bit(HCI_TX_CTRL, &husb->tx_state)) -- return; -+ struct _urb *_urb; -+ struct urb *urb; -+ int err, mtu, size; -+ void *buf; - -- DBG("%s", husb->hdev.name); -+ mtu = husb->isoc_in_ep->wMaxPacketSize; -+ size = mtu * HCI_MAX_ISOC_FRAMES; - -- if (!(skb = skb_dequeue(&husb->tx_ctrl_q))) -- goto done; -+ buf = kmalloc(size, GFP_ATOMIC); -+ if (!buf) -+ return -ENOMEM; - -- if (hci_usb_ctrl_msg(husb, skb)){ -- kfree_skb(skb); -- goto done; -+ _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC); -+ if (!_urb) { -+ kfree(buf); -+ return -ENOMEM; - } -+ _urb->type = HCI_SCODATA_PKT; -+ _urb_queue_tail(__pending_q(husb, _urb->type), _urb); - -- DMP(skb->data, skb->len); -+ urb = &_urb->urb; - -- husb->hdev.stat.byte_tx += skb->len; -- return; -+ urb->context = husb; -+ urb->dev = husb->udev; -+ urb->pipe = usb_rcvisocpipe(husb->udev, husb->isoc_in_ep->bEndpointAddress); -+ urb->complete = hci_usb_rx_complete; - --done: -- clear_bit(HCI_TX_CTRL, &husb->tx_state); -- return; -+ urb->transfer_buffer_length = size; -+ urb->transfer_buffer = buf; -+ urb->transfer_flags = USB_ISO_ASAP; -+ -+ __fill_isoc_desc(urb, size, mtu); -+ -+ BT_DBG("%s urb %p", husb->hdev.name, urb); -+ -+ err = usb_submit_urb(urb); -+ if (err) { -+ BT_ERR("%s isoc rx submit failed urb %p err %d", -+ husb->hdev.name, urb, err); -+ _urb_unlink(_urb); -+ _urb_free(_urb); -+ kfree(buf); -+ } -+ return err; - } -+#endif - --void hci_usb_write_wakeup(struct hci_usb *husb) -+/* Initialize device */ -+static int hci_usb_open(struct hci_dev *hdev) - { -- struct sk_buff *skb; -+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; -+ int i, err; -+ unsigned long flags; - -- if (test_and_set_bit(HCI_TX_WRITE, &husb->tx_state)) -- return; -+ BT_DBG("%s", hdev->name); - -- DBG("%s", husb->hdev.name); -+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) -+ return 0; - -- if (!(skb = skb_dequeue(&husb->tx_write_q))) -- goto done; -+ MOD_INC_USE_COUNT; - -- if (hci_usb_write_msg(husb, skb)) { -- skb_queue_head(&husb->tx_write_q, skb); -- goto done; -+ write_lock_irqsave(&husb->completion_lock, flags); -+ -+ err = hci_usb_intr_rx_submit(husb); -+ if (!err) { -+ for (i = 0; i < HCI_MAX_BULK_RX; i++) -+ hci_usb_bulk_rx_submit(husb); -+ -+#ifdef CONFIG_BLUEZ_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; - } - -- DMP(skb->data, skb->len); -+ write_unlock_irqrestore(&husb->completion_lock, flags); -+ return err; -+} - -- husb->hdev.stat.byte_tx += skb->len; -- return; -+/* Reset device */ -+static int hci_usb_flush(struct hci_dev *hdev) -+{ -+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; -+ int i; - --done: -- clear_bit(HCI_TX_WRITE, &husb->tx_state); -- return; -+ BT_DBG("%s", hdev->name); -+ -+ for (i=0; i < 4; i++) -+ skb_queue_purge(&husb->transmit_q[i]); -+ return 0; - } - --/* Send frames from HCI layer */ --int hci_usb_send_frame(struct sk_buff *skb) -+static void hci_usb_unlink_urbs(struct hci_usb *husb) - { -- struct hci_dev *hdev = (struct hci_dev *) skb->dev; -- struct hci_usb *husb; -+ int i; - -- if (!hdev) { -- ERR("frame for uknown device (hdev=NULL)"); -- return -ENODEV; -- } -+ BT_DBG("%s", husb->hdev.name); - -- if (!(hdev->flags & HCI_RUNNING)) -- return 0; -+ for (i=0; i < 4; i++) { -+ struct _urb *_urb; -+ struct urb *urb; - -- husb = (struct hci_usb *) hdev->driver_data; -+ /* 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); -+ } - -- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); -+ /* 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); -+ } - -- switch (skb->pkt_type) { -- case HCI_COMMAND_PKT: -- skb_queue_tail(&husb->tx_ctrl_q, skb); -- hci_usb_ctrl_wakeup(husb); -- hdev->stat.cmd_tx++; -- return 0; -+ /* Release reassembly buffers */ -+ if (husb->reassembly[i]) { -+ kfree_skb(husb->reassembly[i]); -+ husb->reassembly[i] = NULL; -+ } -+ } -+} - -- case HCI_ACLDATA_PKT: -- skb_queue_tail(&husb->tx_write_q, skb); -- hci_usb_write_wakeup(husb); -- hdev->stat.acl_tx++; -- return 0; -+/* Close device */ -+static int hci_usb_close(struct hci_dev *hdev) -+{ -+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; -+ unsigned long flags; -+ -+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) -+ return 0; - -- case HCI_SCODATA_PKT: -- return -EOPNOTSUPP; -- }; -+ BT_DBG("%s", hdev->name); -+ -+ write_lock_irqsave(&husb->completion_lock, flags); -+ -+ hci_usb_unlink_urbs(husb); -+ hci_usb_flush(hdev); -+ -+ write_unlock_irqrestore(&husb->completion_lock, flags); - -+ MOD_DEC_USE_COUNT; - return 0; - } - --/* ---------- USB ------------- */ -+static int __tx_submit(struct hci_usb *husb, struct _urb *_urb) -+{ -+ struct urb *urb = &_urb->urb; -+ int err; - --static void hci_usb_ctrl(struct urb *urb) -+ 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 sk_buff *skb = (struct sk_buff *) urb->context; -- struct hci_dev *hdev; -- struct hci_usb *husb; -+ struct _urb *_urb = __get_completed(husb, skb->pkt_type); -+ devrequest *dr; -+ struct urb *urb; - -- if (!skb) -- return; -- hdev = (struct hci_dev *) skb->dev; -- husb = (struct hci_usb *) hdev->driver_data; -+ if (!_urb) { -+ _urb = _urb_alloc(0, GFP_ATOMIC); -+ if (!_urb) -+ return -ENOMEM; -+ _urb->type = skb->pkt_type; - -- DBG("%s", hdev->name); -+ dr = kmalloc(sizeof(*dr), GFP_ATOMIC); -+ if (!dr) { -+ _urb_free(_urb); -+ return -ENOMEM; -+ } -+ } else -+ dr = (void *) _urb->urb.setup_packet; - -- if (urb->status) -- DBG("%s ctrl status: %d", hdev->name, urb->status); -+ dr->requesttype = husb->ctrl_req; -+ dr->request = 0; -+ dr->index = 0; -+ dr->value = 0; -+ dr->length = __cpu_to_le16(skb->len); - -- clear_bit(HCI_TX_CTRL, &husb->tx_state); -- kfree_skb(skb); -+ urb = &_urb->urb; -+ FILL_CONTROL_URB(urb, husb->udev, usb_sndctrlpipe(husb->udev, 0), -+ (void *) dr, skb->data, skb->len, hci_usb_tx_complete, husb); - -- /* Wake up device */ -- hci_usb_ctrl_wakeup(husb); -+ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len); -+ -+ _urb->priv = skb; -+ return __tx_submit(husb, _urb); - } - --static void hci_usb_bulk_write(struct urb *urb) -+static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb) - { -- struct sk_buff *skb = (struct sk_buff *) urb->context; -- struct hci_dev *hdev; -- struct hci_usb *husb; -- -- if (!skb) -- return; -- hdev = (struct hci_dev *) skb->dev; -- husb = (struct hci_usb *) hdev->driver_data; -- -- DBG("%s", hdev->name); -+ struct _urb *_urb = __get_completed(husb, skb->pkt_type); -+ struct urb *urb; -+ int pipe; - -- if (urb->status) -- DBG("%s bulk write status: %d", hdev->name, urb->status); -+ if (!_urb) { -+ _urb = _urb_alloc(0, GFP_ATOMIC); -+ if (!_urb) -+ return -ENOMEM; -+ _urb->type = skb->pkt_type; -+ } - -- clear_bit(HCI_TX_WRITE, &husb->tx_state); -- kfree_skb(skb); -+ urb = &_urb->urb; -+ pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep->bEndpointAddress); -+ FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len, -+ hci_usb_tx_complete, husb); -+ urb->transfer_flags = USB_QUEUE_BULK | USB_ZERO_PACKET; - -- /* Wake up device */ -- hci_usb_write_wakeup(husb); -+ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len); - -- return; -+ _urb->priv = skb; -+ return __tx_submit(husb, _urb); - } - --static void hci_usb_intr(struct urb *urb) -+#ifdef CONFIG_BLUEZ_HCIUSB_SCO -+static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb) - { -- struct hci_usb *husb = (struct hci_usb *) urb->context; -- unsigned char *data = urb->transfer_buffer; -- register int count = urb->actual_length; -- register struct sk_buff *skb = husb->intr_skb; -- hci_event_hdr *eh; -- register int len; -+ struct _urb *_urb = __get_completed(husb, skb->pkt_type); -+ struct urb *urb; -+ -+ if (!_urb) { -+ _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC); -+ if (!_urb) -+ return -ENOMEM; -+ _urb->type = skb->pkt_type; -+ } - -- if (!husb) -- return; -+ BT_DBG("%s skb %p len %d", husb->hdev.name, skb, skb->len); - -- DBG("%s count %d", husb->hdev.name, count); -+ urb = &_urb->urb; -+ -+ urb->context = husb; -+ urb->dev = husb->udev; -+ urb->pipe = usb_sndisocpipe(husb->udev, husb->isoc_out_ep->bEndpointAddress); -+ urb->complete = hci_usb_tx_complete; -+ urb->transfer_flags = USB_ISO_ASAP; - -- if (urb->status || !count) { -- DBG("%s intr status %d, count %d", husb->hdev.name, urb->status, count); -- return; -- } -+ urb->transfer_buffer = skb->data; -+ urb->transfer_buffer_length = skb->len; -+ -+ __fill_isoc_desc(urb, skb->len, husb->isoc_out_ep->wMaxPacketSize); - -- /* Do we really have to handle continuations here ? */ -- if (!skb) { -- /* New frame */ -- if (count < HCI_EVENT_HDR_SIZE) { -- DBG("%s bad frame len %d", husb->hdev.name, count); -- return; -- } -+ _urb->priv = skb; -+ return __tx_submit(husb, _urb); -+} -+#endif - -- eh = (hci_event_hdr *) data; -- len = eh->plen + HCI_EVENT_HDR_SIZE; -+static void hci_usb_tx_process(struct hci_usb *husb) -+{ -+ struct sk_buff_head *q; -+ struct sk_buff *skb; - -- if (count > len) { -- DBG("%s corrupted frame, len %d", husb->hdev.name, count); -- return; -+ 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); - } - -- /* Allocate skb */ -- if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) { -- ERR("Can't allocate mem for new packet"); -- return; -+#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); - } -- skb->dev = (void *) &husb->hdev; -- skb->pkt_type = HCI_EVENT_PKT; -+#endif -+ -+ /* Process ACL queue */ -+ q = __transmit_q(husb, HCI_ACLDATA_PKT); -+ while (atomic_read(__pending_tx(husb, HCI_ACLDATA_PKT)) < HCI_MAX_BULK_TX && -+ (skb = skb_dequeue(q))) { -+ if (hci_usb_send_bulk(husb, skb) < 0) { -+ skb_queue_head(q, skb); -+ break; -+ } -+ } -+ } while(test_bit(HCI_USB_TX_WAKEUP, &husb->state)); -+} - -- husb->intr_skb = skb; -- husb->intr_count = len; -- } else { -- /* Continuation */ -- if (count > husb->intr_count) { -- ERR("%s bad frame len %d (expected %d)", husb->hdev.name, count, husb->intr_count); -+static inline void hci_usb_tx_wakeup(struct hci_usb *husb) -+{ -+ /* Serialize TX queue processing to avoid data reordering */ -+ if (!test_and_set_bit(HCI_USB_TX_PROCESS, &husb->state)) { -+ hci_usb_tx_process(husb); -+ clear_bit(HCI_USB_TX_PROCESS, &husb->state); -+ } else -+ set_bit(HCI_USB_TX_WAKEUP, &husb->state); -+} - -- kfree_skb(skb); -- husb->intr_skb = NULL; -- husb->intr_count = 0; -- return; -- } -+/* Send frames from HCI layer */ -+static int hci_usb_send_frame(struct sk_buff *skb) -+{ -+ struct hci_dev *hdev = (struct hci_dev *) skb->dev; -+ struct hci_usb *husb; -+ -+ if (!hdev) { -+ BT_ERR("frame for uknown device (hdev=NULL)"); -+ return -ENODEV; - } - -- memcpy(skb_put(skb, count), data, count); -- husb->intr_count -= count; -+ if (!test_bit(HCI_RUNNING, &hdev->flags)) -+ return -EBUSY; - -- DMP(data, count); -+ BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); - -- if (!husb->intr_count) { -- /* Got complete frame */ -+ husb = (struct hci_usb *) hdev->driver_data; - -- husb->hdev.stat.byte_rx += skb->len; -- hci_recv_frame(skb); -+ switch (skb->pkt_type) { -+ case HCI_COMMAND_PKT: -+ hdev->stat.cmd_tx++; -+ break; - -- husb->intr_skb = NULL; -- } --} -+ case HCI_ACLDATA_PKT: -+ hdev->stat.acl_tx++; -+ break; - --static void hci_usb_bulk_read(struct urb *urb) --{ -- struct hci_usb *husb = (struct hci_usb *) urb->context; -- unsigned char *data = urb->transfer_buffer; -- int count = urb->actual_length, status; -- struct sk_buff *skb; -- hci_acl_hdr *ah; -- register __u16 dlen; -+#ifdef CONFIG_BLUEZ_HCIUSB_SCO -+ case HCI_SCODATA_PKT: -+ hdev->stat.sco_tx++; -+ break; -+#endif - -- if (!husb) -- return; -+ default: -+ kfree_skb(skb); -+ return 0; -+ } - -- DBG("%s status %d, count %d, flags %x", husb->hdev.name, urb->status, count, urb->transfer_flags); -+ read_lock(&husb->completion_lock); - -- if (urb->status) { -- /* Do not re-submit URB on critical errors */ -- switch (urb->status) { -- case -ENOENT: -- return; -- default: -- goto resubmit; -- }; -- } -- if (!count) -- goto resubmit; -+ skb_queue_tail(__transmit_q(husb, skb->pkt_type), skb); -+ hci_usb_tx_wakeup(husb); - -- DMP(data, count); -+ read_unlock(&husb->completion_lock); -+ return 0; -+} - -- ah = (hci_acl_hdr *) data; -- dlen = le16_to_cpu(ah->dlen); -+static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int count) -+{ -+ BT_DBG("%s type %d data %p count %d", husb->hdev.name, type, data, count); - -- /* Verify frame len and completeness */ -- if ((count - HCI_ACL_HDR_SIZE) != dlen) { -- ERR("%s corrupted ACL packet: count %d, plen %d", husb->hdev.name, count, dlen); -- goto resubmit; -- } -+ husb->hdev.stat.byte_rx += count; - -- /* Allocate packet */ -- if (!(skb = bluez_skb_alloc(count, GFP_ATOMIC))) { -- ERR("Can't allocate mem for new packet"); -- goto resubmit; -- } -+ while (count) { -+ struct sk_buff *skb = __reassembly(husb, type); -+ struct { int expect; } *scb; -+ int len = 0; -+ -+ if (!skb) { -+ /* Start of the frame */ - -- memcpy(skb_put(skb, count), data, count); -- skb->dev = (void *) &husb->hdev; -- skb->pkt_type = HCI_ACLDATA_PKT; -+ 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; - -- husb->hdev.stat.byte_rx += skb->len; -+ 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); -+ -+ 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; - -- hci_recv_frame(skb); -+ scb = (void *) skb->cb; -+ scb->expect = len; -+ } else { -+ /* Continuation */ -+ scb = (void *) skb->cb; -+ len = scb->expect; -+ } - --resubmit: -- husb->read_urb->dev = husb->udev; -- if ((status = usb_submit_urb(husb->read_urb))) -- DBG("%s read URB submit failed %d", husb->hdev.name, status); -+ len = min(len, count); -+ -+ memcpy(skb_put(skb, len), data, len); - -- DBG("%s read URB re-submited", husb->hdev.name); -+ scb->expect -= len; -+ if (!scb->expect) { -+ /* Complete frame */ -+ __reassembly(husb, type) = NULL; -+ hci_recv_frame(skb); -+ } -+ -+ count -= len; data += len; -+ } -+ return 0; - } - --static int hci_usb_ctrl_msg(struct hci_usb *husb, struct sk_buff *skb) -+static void hci_usb_rx_complete(struct urb *urb) - { -- struct urb *urb = husb->ctrl_urb; -- devrequest *dr = &husb->dev_req; -- int pipe, status; -+ struct _urb *_urb = container_of(urb, struct _urb, urb); -+ struct hci_usb *husb = (void *) urb->context; -+ struct hci_dev *hdev = &husb->hdev; -+ int err, count = urb->actual_length; - -- DBG("%s len %d", husb->hdev.name, skb->len); -+ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb, -+ _urb->type, urb->status, count, urb->transfer_flags); - -- pipe = usb_sndctrlpipe(husb->udev, 0); -+ if (!test_bit(HCI_RUNNING, &hdev->flags)) -+ return; - -- dr->requesttype = HCI_CTRL_REQ; -- dr->request = 0; -- dr->index = 0; -- dr->value = 0; -- dr->length = cpu_to_le16(skb->len); -+ read_lock(&husb->completion_lock); - -- FILL_CONTROL_URB(urb, husb->udev, pipe, (void*)dr, skb->data, skb->len, -- hci_usb_ctrl, skb); -+ if (urb->status || !count) -+ goto resubmit; - -- if ((status = usb_submit_urb(urb))) { -- DBG("%s control URB submit failed %d", husb->hdev.name, status); -- return status; -+ if (_urb->type == HCI_SCODATA_PKT) { -+#ifdef CONFIG_BLUEZ_HCIUSB_SCO -+ int i; -+ for (i=0; i < urb->number_of_packets; i++) { -+ BT_DBG("desc %d status %d offset %d len %d", i, -+ urb->iso_frame_desc[i].status, -+ urb->iso_frame_desc[i].offset, -+ urb->iso_frame_desc[i].actual_length); -+ -+ if (!urb->iso_frame_desc[i].status) -+ __recv_frame(husb, _urb->type, -+ urb->transfer_buffer + urb->iso_frame_desc[i].offset, -+ urb->iso_frame_desc[i].actual_length); -+ } -+#else -+ ; -+#endif -+ } else { -+ err = __recv_frame(husb, _urb->type, urb->transfer_buffer, count); -+ if (err < 0) { -+ BT_ERR("%s corrupted packet: type %d count %d", -+ husb->hdev.name, _urb->type, count); -+ hdev->stat.err_rx++; -+ } - } - -- return 0; -+resubmit: -+ if (_urb->type != HCI_EVENT_PKT) { -+ urb->dev = husb->udev; -+ err = usb_submit_urb(urb); -+ BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb, -+ _urb->type, err); -+ } -+ read_unlock(&husb->completion_lock); - } - --static int hci_usb_write_msg(struct hci_usb *husb, struct sk_buff *skb) -+static void hci_usb_tx_complete(struct urb *urb) - { -- struct urb *urb = husb->write_urb; -- int pipe, status; -+ struct _urb *_urb = container_of(urb, struct _urb, urb); -+ struct hci_usb *husb = (void *) urb->context; -+ struct hci_dev *hdev = &husb->hdev; - -- DBG("%s len %d", husb->hdev.name, skb->len); -+ BT_DBG("%s urb %p status %d flags %x", hdev->name, urb, -+ urb->status, urb->transfer_flags); - -- pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep_addr); -+ atomic_dec(__pending_tx(husb, _urb->type)); - -- FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len, -- hci_usb_bulk_write, skb); -- urb->transfer_flags |= USB_QUEUE_BULK; -+ urb->transfer_buffer = NULL; -+ kfree_skb((struct sk_buff *) _urb->priv); - -- if ((status = usb_submit_urb(urb))) { -- DBG("%s write URB submit failed %d", husb->hdev.name, status); -- return status; -- } -+ if (!test_bit(HCI_RUNNING, &hdev->flags)) -+ return; - -- return 0; -+ if (!urb->status) -+ hdev->stat.byte_tx += urb->transfer_buffer_length; -+ else -+ hdev->stat.err_tx++; -+ -+ read_lock(&husb->completion_lock); -+ -+ _urb_unlink(_urb); -+ _urb_queue_tail(__completed_q(husb, _urb->type), _urb); -+ -+ hci_usb_tx_wakeup(husb); -+ -+ read_unlock(&husb->completion_lock); - } - --static void * hci_usb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) -+static void hci_usb_destruct(struct hci_dev *hdev) - { -- struct usb_endpoint_descriptor *bulk_out_ep, *intr_in_ep, *bulk_in_ep; -+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data; -+ -+ BT_DBG("%s", hdev->name); -+ -+ kfree(husb); -+} -+ -+static void *hci_usb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id) -+{ -+ struct usb_endpoint_descriptor *bulk_out_ep[HCI_MAX_IFACE_NUM]; -+ struct usb_endpoint_descriptor *isoc_out_ep[HCI_MAX_IFACE_NUM]; -+ struct usb_endpoint_descriptor *bulk_in_ep[HCI_MAX_IFACE_NUM]; -+ struct usb_endpoint_descriptor *isoc_in_ep[HCI_MAX_IFACE_NUM]; -+ struct usb_endpoint_descriptor *intr_in_ep[HCI_MAX_IFACE_NUM]; - struct usb_interface_descriptor *uif; - struct usb_endpoint_descriptor *ep; -+ struct usb_interface *iface, *isoc_iface; - struct hci_usb *husb; - struct hci_dev *hdev; -- int i, size, pipe; -- __u8 * buf; -- -- DBG("udev %p ifnum %d", udev, ifnum); -+ int i, a, e, size, ifn, isoc_ifnum, isoc_alts; - -- /* Check device signature */ -- if ((udev->descriptor.bDeviceClass != HCI_DEV_CLASS) || -- (udev->descriptor.bDeviceSubClass != HCI_DEV_SUBCLASS)|| -- (udev->descriptor.bDeviceProtocol != HCI_DEV_PROTOCOL) ) -- return NULL; -+ BT_DBG("udev %p ifnum %d", udev, ifnum); - -- MOD_INC_USE_COUNT; -+ iface = &udev->actconfig->interface[0]; - -- uif = &udev->actconfig->interface[ifnum].altsetting[0]; -+ /* Check our black list */ -+ if (usb_match_id(udev, iface, ignore_ids)) -+ return NULL; - -- if (uif->bNumEndpoints != 3) { -- DBG("Wrong number of endpoints %d", uif->bNumEndpoints); -- MOD_DEC_USE_COUNT; -+ /* Check number of endpoints */ -+ if (udev->actconfig->interface[ifnum].altsetting[0].bNumEndpoints < 3) - return NULL; -- } - -- bulk_out_ep = intr_in_ep = bulk_in_ep = NULL; -+ memset(bulk_out_ep, 0, sizeof(bulk_out_ep)); -+ memset(isoc_out_ep, 0, sizeof(isoc_out_ep)); -+ memset(bulk_in_ep, 0, sizeof(bulk_in_ep)); -+ memset(isoc_in_ep, 0, sizeof(isoc_in_ep)); -+ memset(intr_in_ep, 0, sizeof(intr_in_ep)); - -+ size = 0; -+ isoc_iface = NULL; -+ isoc_alts = isoc_ifnum = 0; -+ - /* Find endpoints that we need */ -- for ( i = 0; i < uif->bNumEndpoints; ++i) { -- ep = &uif->endpoint[i]; - -- switch (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { -- case USB_ENDPOINT_XFER_BULK: -- if (ep->bEndpointAddress & USB_DIR_IN) -- bulk_in_ep = ep; -- else -- bulk_out_ep = ep; -- break; -- -- case USB_ENDPOINT_XFER_INT: -- intr_in_ep = ep; -- break; -- }; -- } -+ ifn = MIN(udev->actconfig->bNumInterfaces, HCI_MAX_IFACE_NUM); -+ for (i = 0; i < ifn; i++) { -+ iface = &udev->actconfig->interface[i]; -+ for (a = 0; a < iface->num_altsetting; a++) { -+ uif = &iface->altsetting[a]; -+ for (e = 0; e < uif->bNumEndpoints; e++) { -+ ep = &uif->endpoint[e]; - -- if (!bulk_in_ep || !bulk_out_ep || !intr_in_ep) { -- DBG("Endpoints not found: %p %p %p", bulk_in_ep, bulk_out_ep, intr_in_ep); -- MOD_DEC_USE_COUNT; -- return NULL; -- } -+ switch (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { -+ case USB_ENDPOINT_XFER_INT: -+ if (ep->bEndpointAddress & USB_DIR_IN) -+ intr_in_ep[i] = ep; -+ break; - -- if (!(husb = kmalloc(sizeof(struct hci_usb), GFP_KERNEL))) { -- ERR("Can't allocate: control structure"); -- MOD_DEC_USE_COUNT; -- return NULL; -- } -+ case USB_ENDPOINT_XFER_BULK: -+ if (ep->bEndpointAddress & USB_DIR_IN) -+ bulk_in_ep[i] = ep; -+ else -+ bulk_out_ep[i] = ep; -+ break; - -- memset(husb, 0, sizeof(struct hci_usb)); -+#ifdef CONFIG_BLUEZ_HCIUSB_SCO -+ case USB_ENDPOINT_XFER_ISOC: -+ if (ep->wMaxPacketSize < size || a > 2) -+ break; -+ size = ep->wMaxPacketSize; - -- husb->udev = udev; -- husb->bulk_out_ep_addr = bulk_out_ep->bEndpointAddress; -+ isoc_iface = iface; -+ isoc_alts = a; -+ isoc_ifnum = i; - -- if (!(husb->ctrl_urb = usb_alloc_urb(0))) { -- ERR("Can't allocate: control URB"); -- goto probe_error; -+ if (ep->bEndpointAddress & USB_DIR_IN) -+ isoc_in_ep[i] = ep; -+ else -+ isoc_out_ep[i] = ep; -+ break; -+#endif -+ } -+ } -+ } - } - -- if (!(husb->write_urb = usb_alloc_urb(0))) { -- ERR("Can't allocate: write URB"); -- goto probe_error; -+ if (!bulk_in_ep[0] || !bulk_out_ep[0] || !intr_in_ep[0]) { -+ BT_DBG("Bulk endpoints not found"); -+ goto done; - } - -- if (!(husb->read_urb = usb_alloc_urb(0))) { -- ERR("Can't allocate: read URB"); -- goto probe_error; -+#ifdef CONFIG_BLUEZ_HCIUSB_SCO -+ if (!isoc_in_ep[1] || !isoc_out_ep[1]) { -+ BT_DBG("Isoc endpoints not found"); -+ isoc_iface = NULL; - } -+#endif - -- ep = bulk_in_ep; -- pipe = usb_rcvbulkpipe(udev, ep->bEndpointAddress); -- size = HCI_MAX_FRAME_SIZE; -- -- if (!(buf = kmalloc(size, GFP_KERNEL))) { -- ERR("Can't allocate: read buffer"); -- goto probe_error; -+ if (!(husb = kmalloc(sizeof(struct hci_usb), GFP_KERNEL))) { -+ BT_ERR("Can't allocate: control structure"); -+ goto done; - } - -- FILL_BULK_URB(husb->read_urb, udev, pipe, buf, size, hci_usb_bulk_read, husb); -- husb->read_urb->transfer_flags |= USB_QUEUE_BULK; -+ memset(husb, 0, sizeof(struct hci_usb)); - -- ep = intr_in_ep; -- pipe = usb_rcvintpipe(udev, ep->bEndpointAddress); -- size = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); -+ husb->udev = udev; -+ husb->bulk_out_ep = bulk_out_ep[0]; -+ husb->bulk_in_ep = bulk_in_ep[0]; -+ husb->intr_in_ep = intr_in_ep[0]; - -- if (!(husb->intr_urb = usb_alloc_urb(0))) { -- ERR("Can't allocate: interrupt URB"); -- goto probe_error; -- } -+ if (id->driver_info & HCI_DIGIANSWER) -+ husb->ctrl_req = HCI_DIGI_REQ; -+ else -+ husb->ctrl_req = HCI_CTRL_REQ; - -- if (!(buf = kmalloc(size, GFP_KERNEL))) { -- ERR("Can't allocate: interrupt buffer"); -- goto probe_error; -+#ifdef CONFIG_BLUEZ_HCIUSB_SCO -+ if (isoc_iface) { -+ BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts); -+ if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) { -+ BT_ERR("Can't set isoc interface settings"); -+ isoc_iface = NULL; -+ } -+ usb_driver_claim_interface(&hci_usb_driver, isoc_iface, husb); -+ husb->isoc_iface = isoc_iface; -+ husb->isoc_in_ep = isoc_in_ep[isoc_ifnum]; -+ husb->isoc_out_ep = isoc_out_ep[isoc_ifnum]; - } -+#endif -+ -+ husb->completion_lock = RW_LOCK_UNLOCKED; - -- FILL_INT_URB(husb->intr_urb, udev, pipe, buf, size, hci_usb_intr, husb, ep->bInterval); -- -- skb_queue_head_init(&husb->tx_ctrl_q); -- skb_queue_head_init(&husb->tx_write_q); -+ 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; - hdev->close = hci_usb_close; - hdev->flush = hci_usb_flush; -- hdev->send = hci_usb_send_frame; -+ hdev->send = hci_usb_send_frame; -+ hdev->destruct = hci_usb_destruct; - - if (hci_register_dev(hdev) < 0) { -- ERR("Can't register HCI device %s", hdev->name); -+ BT_ERR("Can't register HCI device"); - goto probe_error; - } - - return husb; - - probe_error: -- hci_usb_free_bufs(husb); - kfree(husb); -- MOD_DEC_USE_COUNT; -+ -+done: - return NULL; - } - -@@ -626,38 +943,34 @@ - if (!husb) - return; - -- DBG("%s", hdev->name); -+ BT_DBG("%s", hdev->name); - - hci_usb_close(hdev); - -- if (hci_unregister_dev(hdev) < 0) { -- ERR("Can't unregister HCI device %s", hdev->name); -- } -- -- hci_usb_free_bufs(husb); -- kfree(husb); -+ if (husb->isoc_iface) -+ usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface); - -- MOD_DEC_USE_COUNT; -+ if (hci_unregister_dev(hdev) < 0) -+ BT_ERR("Can't unregister HCI device %s", hdev->name); - } - --static struct usb_driver hci_usb_driver = --{ -+static struct usb_driver hci_usb_driver = { - name: "hci_usb", - probe: hci_usb_probe, - disconnect: hci_usb_disconnect, -- id_table: usb_bluetooth_ids, -+ id_table: bluetooth_ids, - }; - - int hci_usb_init(void) - { - int err; - -- INF("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", -+ BT_INFO("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", - VERSION); -- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); -+ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); - - if ((err = usb_register(&hci_usb_driver)) < 0) -- ERR("Failed to register HCI USB driver"); -+ BT_ERR("Failed to register HCI USB driver"); - - return err; - } ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/hci_usb.h 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,144 @@ -+/* -+ HCI USB driver for Linux Bluetooth protocol stack (BlueZ) -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * $Id: hci_usb.h,v 1.2 2002/03/18 19:10:04 maxk Exp $ -+ */ -+ -+#ifdef __KERNEL__ -+ -+/* Class, SubClass, and Protocol codes that describe a Bluetooth device */ -+#define HCI_DEV_CLASS 0xe0 /* Wireless class */ -+#define HCI_DEV_SUBCLASS 0x01 /* RF subclass */ -+#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */ -+ -+#define HCI_CTRL_REQ 0x20 -+#define HCI_DIGI_REQ 0x40 -+ -+#define HCI_DIGIANSWER 0x01 -+ -+#define HCI_MAX_IFACE_NUM 3 -+ -+#define HCI_MAX_BULK_TX 4 -+#define HCI_MAX_BULK_RX 1 -+ -+#define HCI_MAX_ISOC_RX 2 -+#define HCI_MAX_ISOC_TX 2 -+ -+#define HCI_MAX_ISOC_FRAMES 10 -+ -+struct _urb_queue { -+ struct list_head head; -+ spinlock_t lock; -+}; -+ -+struct _urb { -+ struct list_head list; -+ struct _urb_queue *queue; -+ int type; -+ void *priv; -+ struct urb urb; -+}; -+ -+struct _urb *_urb_alloc(int isoc, int gfp); -+ -+static inline void _urb_free(struct _urb *_urb) -+{ -+ kfree(_urb); -+} -+ -+static inline void _urb_queue_init(struct _urb_queue *q) -+{ -+ INIT_LIST_HEAD(&q->head); -+ spin_lock_init(&q->lock); -+} -+ -+static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb) -+{ -+ unsigned long flags; -+ spin_lock_irqsave(&q->lock, flags); -+ list_add(&_urb->list, &q->head); _urb->queue = q; -+ spin_unlock_irqrestore(&q->lock, flags); -+} -+ -+static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb) -+{ -+ unsigned long flags; -+ spin_lock_irqsave(&q->lock, flags); -+ list_add_tail(&_urb->list, &q->head); _urb->queue = q; -+ spin_unlock_irqrestore(&q->lock, flags); -+} -+ -+static inline void _urb_unlink(struct _urb *_urb) -+{ -+ struct _urb_queue *q = _urb->queue; -+ unsigned long flags; -+ if (q) { -+ spin_lock_irqsave(&q->lock, flags); -+ list_del(&_urb->list); _urb->queue = NULL; -+ spin_unlock_irqrestore(&q->lock, flags); -+ } -+} -+ -+struct _urb *_urb_dequeue(struct _urb_queue *q); -+ -+#ifndef container_of -+#define container_of(ptr, type, member) ({ \ -+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ -+ (type *)( (char *)__mptr - offsetof(type,member) );}) -+#endif -+ -+struct hci_usb { -+ struct hci_dev hdev; -+ -+ unsigned long state; -+ -+ struct usb_device *udev; -+ -+ struct usb_endpoint_descriptor *bulk_in_ep; -+ struct usb_endpoint_descriptor *bulk_out_ep; -+ struct usb_endpoint_descriptor *intr_in_ep; -+ -+ struct usb_interface *isoc_iface; -+ struct usb_endpoint_descriptor *isoc_out_ep; -+ struct usb_endpoint_descriptor *isoc_in_ep; -+ -+ __u8 ctrl_req; -+ -+ struct sk_buff_head transmit_q[4]; -+ struct sk_buff *reassembly[4]; // Reassembly buffers -+ -+ rwlock_t completion_lock; -+ -+ atomic_t pending_tx[4]; // Number of pending requests -+ struct _urb_queue pending_q[4]; // Pending requests -+ struct _urb_queue completed_q[4]; // Completed requests -+}; -+ -+/* States */ -+#define HCI_USB_TX_PROCESS 1 -+#define HCI_USB_TX_WAKEUP 2 -+ -+#endif /* __KERNEL__ */ ---- linux/drivers/bluetooth/hci_vhci.c~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/drivers/bluetooth/hci_vhci.c 2004-01-25 23:37:39.000000000 +0100 -@@ -25,9 +25,9 @@ - /* - * BlueZ HCI virtual device driver. - * -- * $Id: hci_vhci.c,v 1.3 2001/08/03 04:19:50 maxk Exp $ -+ * $Id: hci_vhci.c,v 1.3 2002/04/17 17:37:20 maxk Exp $ - */ --#define VERSION "1.0" -+#define VERSION "1.1" - - #include <linux/config.h> - #include <linux/module.h> -@@ -49,43 +49,56 @@ - #include <asm/uaccess.h> - - #include <net/bluetooth/bluetooth.h> --#include <net/bluetooth/bluez.h> - #include <net/bluetooth/hci_core.h> --#include <net/bluetooth/hci_vhci.h> -+#include "hci_vhci.h" - - /* HCI device part */ - --int hci_vhci_open(struct hci_dev *hdev) -+static int hci_vhci_open(struct hci_dev *hdev) - { -- hdev->flags |= HCI_RUNNING; -+ set_bit(HCI_RUNNING, &hdev->flags); - return 0; - } - --int hci_vhci_flush(struct hci_dev *hdev) -+static int hci_vhci_flush(struct hci_dev *hdev) - { - struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) hdev->driver_data; - skb_queue_purge(&hci_vhci->readq); - return 0; - } - --int hci_vhci_close(struct hci_dev *hdev) -+static int hci_vhci_close(struct hci_dev *hdev) - { -- hdev->flags &= ~HCI_RUNNING; -+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) -+ return 0; -+ - hci_vhci_flush(hdev); - return 0; - } - --int hci_vhci_send_frame(struct sk_buff *skb) -+static void hci_vhci_destruct(struct hci_dev *hdev) -+{ -+ struct hci_vhci_struct *vhci; -+ -+ if (!hdev) return; -+ -+ vhci = (struct hci_vhci_struct *) hdev->driver_data; -+ kfree(vhci); -+ -+ MOD_DEC_USE_COUNT; -+} -+ -+static int hci_vhci_send_frame(struct sk_buff *skb) - { - struct hci_dev* hdev = (struct hci_dev *) skb->dev; - struct hci_vhci_struct *hci_vhci; - - if (!hdev) { -- ERR("Frame for uknown device (hdev=NULL)"); -+ BT_ERR("Frame for uknown device (hdev=NULL)"); - return -ENODEV; - } - -- if (!(hdev->flags & HCI_RUNNING)) -+ if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; - - hci_vhci = (struct hci_vhci_struct *) hdev->driver_data; -@@ -188,7 +201,7 @@ - - add_wait_queue(&hci_vhci->read_wait, &wait); - while (count) { -- current->state = TASK_INTERRUPTIBLE; -+ set_current_state(TASK_INTERRUPTIBLE); - - /* Read frames from device queue */ - if (!(skb = skb_dequeue(&hci_vhci->readq))) { -@@ -214,8 +227,7 @@ - kfree_skb(skb); - break; - } -- -- current->state = TASK_RUNNING; -+ set_current_state(TASK_RUNNING); - remove_wait_queue(&hci_vhci->read_wait, &wait); - - return ret; -@@ -270,11 +282,13 @@ - hdev->close = hci_vhci_close; - hdev->flush = hci_vhci_flush; - hdev->send = hci_vhci_send_frame; -+ hdev->destruct = hci_vhci_destruct; - - if (hci_register_dev(hdev) < 0) { - kfree(hci_vhci); - return -EBUSY; - } -+ MOD_INC_USE_COUNT; - - file->private_data = hci_vhci; - return 0; -@@ -285,12 +299,10 @@ - struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data; - - if (hci_unregister_dev(&hci_vhci->hdev) < 0) { -- ERR("Can't unregister HCI device %s", hci_vhci->hdev.name); -+ BT_ERR("Can't unregister HCI device %s", hci_vhci->hdev.name); - } - -- kfree(hci_vhci); - file->private_data = NULL; -- - return 0; - } - -@@ -315,12 +327,12 @@ - - int __init hci_vhci_init(void) - { -- INF("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", -+ BT_INFO("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", - VERSION); -- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); -+ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); - - if (misc_register(&hci_vhci_miscdev)) { -- ERR("Can't register misc device %d\n", VHCI_MINOR); -+ BT_ERR("Can't register misc device %d\n", VHCI_MINOR); - return -EIO; - } - -@@ -337,4 +349,4 @@ - - MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); - MODULE_DESCRIPTION("BlueZ VHCI driver ver " VERSION); --MODULE_LICENSE("GPL"); -+MODULE_LICENSE("GPL"); ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/hci_vhci.h 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,50 @@ -+/* -+ BlueZ - Bluetooth protocol stack for Linux -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * $Id: hci_vhci.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $ -+ */ -+ -+#ifndef __HCI_VHCI_H -+#define __HCI_VHCI_H -+ -+#ifdef __KERNEL__ -+ -+struct hci_vhci_struct { -+ struct hci_dev hdev; -+ __u32 flags; -+ wait_queue_head_t read_wait; -+ struct sk_buff_head readq; -+ struct fasync_struct *fasync; -+}; -+ -+/* VHCI device flags */ -+#define VHCI_FASYNC 0x0010 -+ -+#endif /* __KERNEL__ */ -+ -+#define VHCI_DEV "/dev/vhci" -+#define VHCI_MINOR 250 -+ -+#endif /* __HCI_VHCI_H */ ---- linux/drivers/bluetooth/Makefile~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/drivers/bluetooth/Makefile 2004-01-25 23:37:39.000000000 +0100 -@@ -1,11 +1,27 @@ - # --# Makefile for Bluetooth HCI device drivers. -+# Makefile for the Linux Bluetooth HCI device drivers - # - - O_TARGET := bluetooth.o - -+list-multi := hci_uart.o -+ - obj-$(CONFIG_BLUEZ_HCIUSB) += hci_usb.o --obj-$(CONFIG_BLUEZ_HCIUART) += hci_uart.o - obj-$(CONFIG_BLUEZ_HCIVHCI) += hci_vhci.o - -+obj-$(CONFIG_BLUEZ_HCIUART) += hci_uart.o -+uart-y := hci_ldisc.o -+uart-$(CONFIG_BLUEZ_HCIUART_H4) += hci_h4.o -+uart-$(CONFIG_BLUEZ_HCIUART_BCSP) += hci_bcsp.o -+ -+obj-$(CONFIG_BLUEZ_HCIBFUSB) += bfusb.o -+ -+obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o -+obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o -+obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o -+obj-$(CONFIG_BLUEZ_HCIBTUART) += btuart_cs.o -+ - include $(TOPDIR)/Rules.make -+ -+hci_uart.o: $(uart-y) -+ $(LD) -r -o $@ $(uart-y) ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/bluetooth/Makefile.lib 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1 @@ -+obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o ---- linux/drivers/char/pcmcia/serial_cs.c~bluetooth-2.4.18-mh11 2004-01-25 23:28:22.000000000 +0100 -+++ linux/drivers/char/pcmcia/serial_cs.c 2004-01-25 23:37:39.000000000 +0100 -@@ -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 */ - - /*====================================================================*/ -@@ -246,18 +246,20 @@ - { - struct serial_struct serial; - int line; -- -+ - memset(&serial, 0, sizeof(serial)); - serial.port = port; - serial.irq = irq; - serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ; -+ if (buggy_uart) -+ serial.flags |= ASYNC_BUGGY_UART; - line = register_serial(&serial); - if (line < 0) { - printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx," - " irq %d failed\n", (u_long)serial.port, serial.irq); - return -1; - } -- -+ - info->line[info->ndev] = line; - sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); - info->node[info->ndev].major = TTY_MAJOR; -@@ -265,7 +267,7 @@ - if (info->ndev > 0) - info->node[info->ndev-1].next = &info->node[info->ndev]; - info->ndev++; -- -+ - return 0; - } - -@@ -316,7 +318,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; -@@ -343,7 +348,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. */ -@@ -355,8 +360,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; - } - } -@@ -368,7 +372,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); -@@ -393,8 +397,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; -@@ -436,12 +444,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); -@@ -457,14 +465,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; - } - -@@ -490,7 +511,7 @@ - int i, last_ret, last_fn; - - DEBUG(0, "serial_config(0x%p)\n", link); -- -+ - tuple.TupleData = (cisdata_t *)buf; - tuple.TupleOffset = 0; tuple.TupleDataMax = 255; - tuple.Attributes = 0; -@@ -525,7 +546,7 @@ - } - link->conf.ConfigBase = parse.config.base; - link->conf.Present = parse.config.rmask[0]; -- -+ - /* Configure card */ - link->state |= DEV_CONFIG; - -@@ -533,8 +554,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]); -@@ -589,15 +610,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, ®); -@@ -614,6 +635,7 @@ - cs_error(link->handle, last_fn, last_ret); - failed: - serial_release((u_long)link); -+ link->state &= ~DEV_CONFIG_PENDING; - - } /* serial_config */ - -@@ -621,7 +643,7 @@ - - After a card is removed, serial_release() will unregister the net - device, and release the PCMCIA configuration. -- -+ - ======================================================================*/ - - void serial_release(u_long arg) -@@ -629,7 +651,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++) { -@@ -642,7 +664,7 @@ - CardServices(ReleaseIO, link->handle, &link->io); - CardServices(ReleaseIRQ, link->handle, &link->irq); - } -- -+ - link->state &= ~DEV_CONFIG; - - } /* serial_release */ -@@ -653,7 +675,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, -@@ -661,9 +683,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; -@@ -702,7 +724,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; ---- linux/drivers/input/Config.in~bluetooth-2.4.18-mh11 2001-09-13 00:34:06.000000000 +0200 -+++ linux/drivers/input/Config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -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 ---- linux/drivers/input/keybdev.c~bluetooth-2.4.18-mh11 2001-10-11 18:14:32.000000000 +0200 -+++ linux/drivers/input/keybdev.c 2004-01-25 23:37:39.000000000 +0100 -@@ -154,16 +154,18 @@ - - static struct input_handler keybdev_handler; - -+static unsigned int ledstate = 0xff; -+ - void keybdev_ledfunc(unsigned int led) - { - struct input_handle *handle; - -- for (handle = keybdev_handler.handle; handle; handle = handle->hnext) { -+ ledstate = led; - -+ for (handle = keybdev_handler.handle; handle; handle = handle->hnext) { - input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01)); - input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02)); - input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04)); -- - } - } - -@@ -202,6 +204,12 @@ - - // printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number); - -+ if (ledstate != 0xff) { -+ input_event(dev, EV_LED, LED_SCROLLL, !!(ledstate & 0x01)); -+ input_event(dev, EV_LED, LED_NUML, !!(ledstate & 0x02)); -+ input_event(dev, EV_LED, LED_CAPSL, !!(ledstate & 0x04)); -+ } -+ - return handle; - } - ---- linux/drivers/input/Makefile~bluetooth-2.4.18-mh11 2000-12-29 23:07:22.000000000 +0100 -+++ linux/drivers/input/Makefile 2004-01-25 23:37:39.000000000 +0100 -@@ -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. - ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/drivers/input/uinput.c 2004-01-25 23:37:39.000000000 +0100 -@@ -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); -+ ---- linux/drivers/isdn/avmb1/capidrv.c~bluetooth-2.4.18-mh11 2001-12-21 18:41:54.000000000 +0100 -+++ linux/drivers/isdn/avmb1/capidrv.c 2004-01-25 23:37:39.000000000 +0100 -@@ -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; - ---- linux/drivers/isdn/avmb1/kcapi.c~bluetooth-2.4.18-mh11 2001-12-21 18:41:54.000000000 +0100 -+++ linux/drivers/isdn/avmb1/kcapi.c 2004-01-25 23:37:39.000000000 +0100 -@@ -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); - ---- linux/drivers/usb/Config.in~bluetooth-2.4.18-mh11 2004-01-25 23:28:22.000000000 +0100 -+++ linux/drivers/usb/Config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -39,7 +39,13 @@ - - comment 'USB Device Class drivers' - dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND --dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL -+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then -+ if [ "$CONFIG_BLUEZ" = "n" ]; then -+ dep_tristate ' USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB -+ else -+ comment ' USB Bluetooth can only be used with disabled Bluetooth subsystem' -+ fi -+fi - if [ "$CONFIG_SCSI" = "n" ]; then - comment ' SCSI support is needed for USB Storage' - fi ---- linux/drivers/usb/hid-core.c~bluetooth-2.4.18-mh11 2001-12-21 18:41:55.000000000 +0100 -+++ linux/drivers/usb/hid-core.c 2004-01-25 23:37:39.000000000 +0100 -@@ -217,6 +217,8 @@ - - offset = report->size; - report->size += parser->global.report_size * parser->global.report_count; -+ if (usages < parser->global.report_count) -+ usages = parser->global.report_count; - - if (usages == 0) - return 0; /* ignore padding fields */ ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/include/linux/firmware.h 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,20 @@ -+#ifndef _LINUX_FIRMWARE_H -+#define _LINUX_FIRMWARE_H -+#include <linux/module.h> -+#include <linux/types.h> -+#define FIRMWARE_NAME_MAX 30 -+struct firmware { -+ size_t size; -+ u8 *data; -+}; -+int request_firmware (const struct firmware **fw, const char *name, -+ const char *device); -+int request_firmware_nowait ( -+ struct module *module, -+ const char *name, const char *device, void *context, -+ void (*cont)(const struct firmware *fw, void *context)); -+/* On 2.5 'device' is 'struct device *' */ -+ -+void release_firmware (const struct firmware *fw); -+void register_firmware (const char *name, const u8 *data, size_t size); -+#endif ---- linux/include/linux/input.h~bluetooth-2.4.18-mh11 2001-09-13 00:34:06.000000000 +0200 -+++ linux/include/linux/input.h 2004-01-25 23:37:39.000000000 +0100 -@@ -468,6 +468,8 @@ - #define BUS_PCI 0x01 - #define BUS_ISAPNP 0x02 - #define BUS_USB 0x03 -+#define BUS_HIL 0x04 -+#define BUS_BLUETOOTH 0x05 - - #define BUS_ISA 0x10 - #define BUS_I8042 0x11 ---- linux/include/linux/kernel.h~bluetooth-2.4.18-mh11 2002-02-25 20:38:13.000000000 +0100 -+++ linux/include/linux/kernel.h 2004-01-25 23:37:39.000000000 +0100 -@@ -11,6 +11,7 @@ - #include <linux/linkage.h> - #include <linux/stddef.h> - #include <linux/types.h> -+#include <linux/compiler.h> - - /* Optimization barrier */ - /* The "volatile" is due to gcc bugs */ -@@ -181,4 +182,6 @@ - char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding: libc5 uses this.. */ - }; - --#endif -+#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) -+ -+#endif /* _LINUX_KERNEL_H */ ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/include/linux/uinput.h 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,79 @@ -+/* -+ * User level driver support for input subsystem -+ * -+ * Heavily based on evdev.c by Vojtech Pavlik -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> -+ * -+ * Changes/Revisions: -+ * 0.1 20/06/2002 -+ * - first public version -+ */ -+ -+#ifndef __UINPUT_H_ -+#define __UINPUT_H_ -+ -+#ifdef __KERNEL__ -+#define UINPUT_MINOR 223 -+#define UINPUT_NAME "uinput" -+#define UINPUT_BUFFER_SIZE 16 -+ -+/* state flags => bit index for {set|clear|test}_bit ops */ -+#define UIST_CREATED 0 -+ -+struct uinput_device { -+ struct input_dev *dev; -+ unsigned long state; -+ wait_queue_head_t waitq; -+ unsigned char ready, -+ head, -+ tail; -+ struct input_event buff[UINPUT_BUFFER_SIZE]; -+}; -+#endif /* __KERNEL__ */ -+ -+/* ioctl */ -+#define UINPUT_IOCTL_BASE 'U' -+#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1) -+#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2) -+#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int) -+#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int) -+#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int) -+#define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int) -+#define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int) -+#define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int) -+#define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int) -+#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int) -+ -+#ifndef NBITS -+#define NBITS(x) ((((x)-1)/(sizeof(long)*8))+1) -+#endif /* NBITS */ -+ -+#define UINPUT_MAX_NAME_SIZE 80 -+struct uinput_user_dev { -+ char name[UINPUT_MAX_NAME_SIZE]; -+ unsigned short idbus; -+ unsigned short idvendor; -+ unsigned short idproduct; -+ unsigned short idversion; -+ int ff_effects_max; -+ int absmax[ABS_MAX + 1]; -+ int absmin[ABS_MAX + 1]; -+ int absfuzz[ABS_MAX + 1]; -+ int absflat[ABS_MAX + 1]; -+}; -+#endif /* __UINPUT_H_ */ ---- linux/include/net/bluetooth/bluetooth.h~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/include/net/bluetooth/bluetooth.h 2004-01-25 23:37:39.000000000 +0100 -@@ -23,7 +23,7 @@ - */ - - /* -- * $Id: bluetooth.h,v 1.6 2001/08/03 04:19:49 maxk Exp $ -+ * $Id: bluetooth.h,v 1.9 2002/05/06 21:11:55 maxk Exp $ - */ - - #ifndef __BLUETOOTH_H -@@ -31,17 +31,63 @@ - - #include <asm/types.h> - #include <asm/byteorder.h> -+#include <linux/poll.h> -+#include <net/sock.h> - - #ifndef AF_BLUETOOTH - #define AF_BLUETOOTH 31 - #define PF_BLUETOOTH AF_BLUETOOTH - #endif - -+/* Reserv for core and drivers use */ -+#define BLUEZ_SKB_RESERVE 8 -+ -+#ifndef MIN -+#define MIN(a,b) ((a) < (b) ? (a) : (b)) -+#endif -+ - #define BTPROTO_L2CAP 0 - #define BTPROTO_HCI 1 -+#define BTPROTO_SCO 2 -+#define BTPROTO_RFCOMM 3 -+#define BTPROTO_BNEP 4 -+#define BTPROTO_CMTP 5 - - #define SOL_HCI 0 - #define SOL_L2CAP 6 -+#define SOL_SCO 17 -+#define SOL_RFCOMM 18 -+ -+/* Debugging */ -+#ifdef CONFIG_BLUEZ_DEBUG -+ -+#define HCI_CORE_DEBUG 1 -+#define HCI_SOCK_DEBUG 1 -+#define HCI_UART_DEBUG 1 -+#define HCI_USB_DEBUG 1 -+//#define HCI_DATA_DUMP 1 -+ -+#define L2CAP_DEBUG 1 -+#define SCO_DEBUG 1 -+#define AF_BLUETOOTH_DEBUG 1 -+ -+#endif /* CONFIG_BLUEZ_DEBUG */ -+ -+extern void bluez_dump(char *pref, __u8 *buf, int count); -+ -+#if __GNUC__ <= 2 && __GNUC_MINOR__ < 95 -+#define __func__ __FUNCTION__ -+#endif -+ -+#define BT_INFO(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg) -+#define BT_DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n" , __func__ , ## arg) -+#define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) -+ -+#ifdef HCI_DATA_DUMP -+#define BT_DMP(buf, len) bluez_dump(__func__, buf, len) -+#else -+#define BT_DMP(D...) -+#endif - - /* Connection and socket states */ - enum { -@@ -50,6 +96,7 @@ - BT_BOUND, - BT_LISTEN, - BT_CONNECT, -+ BT_CONNECT2, - BT_CONFIG, - BT_DISCONN, - BT_CLOSED -@@ -66,7 +113,8 @@ - __u8 b[6]; - } __attribute__((packed)) bdaddr_t; - --#define BDADDR_ANY ((bdaddr_t *)"\000\000\000\000\000") -+#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) -+#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) - - /* Copy, swap, convert BD Address */ - static inline int bacmp(bdaddr_t *ba1, bdaddr_t *ba2) -@@ -82,6 +130,91 @@ - char *batostr(bdaddr_t *ba); - bdaddr_t *strtoba(char *str); - -+/* Common socket structures and functions */ -+ -+#define bluez_pi(sk) ((struct bluez_pinfo *) &sk->protinfo) -+#define bluez_sk(pi) ((struct sock *) \ -+ ((void *)pi - (unsigned long)(&((struct sock *)0)->protinfo))) -+ -+struct bluez_pinfo { -+ bdaddr_t src; -+ bdaddr_t dst; -+ -+ struct list_head accept_q; -+ struct sock *parent; -+}; -+ -+struct bluez_sock_list { -+ struct sock *head; -+ rwlock_t lock; -+}; -+ -+int bluez_sock_register(int proto, struct net_proto_family *ops); -+int bluez_sock_unregister(int proto); -+void bluez_sock_init(struct socket *sock, struct sock *sk); -+void bluez_sock_link(struct bluez_sock_list *l, struct sock *s); -+void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s); -+int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm); -+uint bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait); -+int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo); -+ -+void bluez_accept_enqueue(struct sock *parent, struct sock *sk); -+struct sock * bluez_accept_dequeue(struct sock *parent, struct socket *newsock); -+ -+/* Skb helpers */ -+struct bluez_skb_cb { -+ int incomming; -+}; -+#define bluez_cb(skb) ((struct bluez_skb_cb *)(skb->cb)) -+ -+static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how) -+{ -+ struct sk_buff *skb; -+ -+ if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) { -+ skb_reserve(skb, BLUEZ_SKB_RESERVE); -+ bluez_cb(skb)->incomming = 0; -+ } -+ return skb; -+} -+ -+static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len, -+ int nb, int *err) -+{ -+ struct sk_buff *skb; -+ -+ if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) { -+ skb_reserve(skb, BLUEZ_SKB_RESERVE); -+ bluez_cb(skb)->incomming = 0; -+ } -+ -+ return skb; -+} -+ -+static inline int skb_frags_no(struct sk_buff *skb) -+{ -+ register struct sk_buff *frag = skb_shinfo(skb)->frag_list; -+ register int n = 1; -+ -+ for (; frag; frag=frag->next, n++); -+ return n; -+} -+ -+int hci_core_init(void); -+int hci_core_cleanup(void); -+int hci_sock_init(void); -+int hci_sock_cleanup(void); -+ - int bterr(__u16 code); - -+#ifndef MODULE_LICENSE -+#define MODULE_LICENSE(x) -+#endif -+ -+#ifndef list_for_each_safe -+#define list_for_each_safe(pos, n, head) \ -+ for (pos = (head)->next, n = pos->next; pos != (head); \ -+ pos = n, n = pos->next) -+#endif -+ - #endif /* __BLUETOOTH_H */ ---- linux/include/net/bluetooth/bluez.h~bluetooth-2.4.18-mh11 -+++ linux/include/net/bluetooth/bluez.h --/* -- BlueZ - Bluetooth protocol stack for Linux -- Copyright (C) 2000-2001 Qualcomm Incorporated -- -- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License version 2 as -- published by the Free Software Foundation; -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -- -- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -- SOFTWARE IS DISCLAIMED. --*/ -- --/* -- * $Id: bluez.h,v 1.4 2001/08/03 04:19:49 maxk Exp $ -- */ -- --#ifndef __IF_BLUEZ_H --#define __IF_BLUEZ_H -- --#include <net/sock.h> -- --#define BLUEZ_MAX_PROTO 2 -- --/* Reserv for core and drivers use */ --#define BLUEZ_SKB_RESERVE 8 -- --#ifndef MIN --#define MIN(a,b) ((a) < (b) ? (a) : (b)) --#endif -- --/* Debugging */ --#ifdef BLUEZ_DEBUG -- --#define HCI_CORE_DEBUG 1 --#define HCI_SOCK_DEBUG 1 --#define HCI_UART_DEBUG 1 --#define HCI_USB_DEBUG 1 --//#define HCI_DATA_DUMP 1 -- --#define L2CAP_DEBUG 1 -- --#endif /* BLUEZ_DEBUG */ -- --extern void bluez_dump(char *pref, __u8 *buf, int count); -- --#define INF(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg) --#define DBG(fmt, arg...) printk(KERN_INFO __FUNCTION__ ": " fmt "\n" , ## arg) --#define ERR(fmt, arg...) printk(KERN_ERR __FUNCTION__ ": " fmt "\n" , ## arg) -- --#ifdef HCI_DATA_DUMP --#define DMP(buf, len) bluez_dump(__FUNCTION__, buf, len) --#else --#define DMP(D...) --#endif -- --/* ----- Sockets ------ */ --struct bluez_sock_list { -- struct sock *head; -- rwlock_t lock; --}; -- --extern int bluez_sock_register(int proto, struct net_proto_family *ops); --extern int bluez_sock_unregister(int proto); -- --extern void bluez_sock_link(struct bluez_sock_list *l, struct sock *s); --extern void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s); -- --/* ----- SKB helpers ----- */ --struct bluez_skb_cb { -- int incomming; --}; --#define bluez_cb(skb) ((struct bluez_skb_cb *)(skb->cb)) -- --static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how) --{ -- struct sk_buff *skb; -- -- if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) { -- skb_reserve(skb, BLUEZ_SKB_RESERVE); -- bluez_cb(skb)->incomming = 0; -- } -- return skb; --} -- --static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len, -- int nb, int *err) --{ -- struct sk_buff *skb; -- -- if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) { -- skb_reserve(skb, BLUEZ_SKB_RESERVE); -- bluez_cb(skb)->incomming = 0; -- } -- -- return skb; --} -- --static inline int skb_frags_no(struct sk_buff *skb) --{ -- register struct sk_buff *frag = skb_shinfo(skb)->frag_list; -- register int n = 1; -- -- for (; frag; frag=frag->next, n++); -- return n; --} -- --extern int hci_core_init(void); --extern int hci_core_cleanup(void); --extern int hci_sock_init(void); --extern int hci_sock_cleanup(void); -- --#endif /* __IF_BLUEZ_H */ ---- linux/include/net/bluetooth/hci_core.h~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/include/net/bluetooth/hci_core.h 2004-01-25 23:37:39.000000000 +0100 -@@ -23,7 +23,7 @@ - */ - - /* -- * $Id: hci_core.h,v 1.11 2001/08/05 06:02:15 maxk Exp $ -+ * $Id: hci_core.h,v 1.5 2002/06/27 04:56:30 maxk Exp $ - */ - - #ifndef __HCI_CORE_H -@@ -32,14 +32,12 @@ - #include <net/bluetooth/hci.h> - - /* HCI upper protocols */ --#define HCI_MAX_PROTO 1 - #define HCI_PROTO_L2CAP 0 -+#define HCI_PROTO_SCO 1 - - #define HCI_INIT_TIMEOUT (HZ * 10) - --/* ----- Inquiry cache ----- */ --#define INQUIRY_CACHE_AGE_MAX (HZ*5) // 5 seconds --#define INQUIRY_ENTRY_AGE_MAX (HZ*60) // 60 seconds -+/* HCI Core structures */ - - struct inquiry_entry { - struct inquiry_entry *next; -@@ -53,111 +51,186 @@ - struct inquiry_entry *list; - }; - --static inline void inquiry_cache_init(struct inquiry_cache *cache) --{ -- spin_lock_init(&cache->lock); -- cache->list = NULL; --} -+struct conn_hash { -+ struct list_head list; -+ spinlock_t lock; -+ unsigned int num; -+}; - --static inline void inquiry_cache_lock(struct inquiry_cache *cache) --{ -- spin_lock(&cache->lock); --} -+struct hci_dev { -+ struct list_head list; -+ spinlock_t lock; -+ atomic_t refcnt; - --static inline void inquiry_cache_unlock(struct inquiry_cache *cache) --{ -- spin_unlock(&cache->lock); --} -+ char name[8]; -+ unsigned long flags; -+ __u16 id; -+ __u8 type; -+ bdaddr_t bdaddr; -+ __u8 features[8]; - --static inline void inquiry_cache_lock_bh(struct inquiry_cache *cache) --{ -- spin_lock_bh(&cache->lock); --} -+ __u16 pkt_type; -+ __u16 link_policy; -+ __u16 link_mode; -+ -+ atomic_t cmd_cnt; -+ unsigned int acl_cnt; -+ unsigned int sco_cnt; - --static inline void inquiry_cache_unlock_bh(struct inquiry_cache *cache) --{ -- spin_unlock_bh(&cache->lock); --} -+ unsigned int acl_mtu; -+ unsigned int sco_mtu; -+ unsigned int acl_pkts; -+ unsigned int sco_pkts; - --static inline long inquiry_cache_age(struct inquiry_cache *cache) --{ -- return jiffies - cache->timestamp; --} -+ unsigned long cmd_last_tx; -+ unsigned long acl_last_tx; -+ unsigned long sco_last_tx; -+ -+ struct tasklet_struct cmd_task; -+ struct tasklet_struct rx_task; -+ struct tasklet_struct tx_task; - --static inline long inquiry_entry_age(struct inquiry_entry *e) --{ -- return jiffies - e->timestamp; --} --extern void inquiry_cache_flush(struct inquiry_cache *cache); -+ struct sk_buff_head rx_q; -+ struct sk_buff_head raw_q; -+ struct sk_buff_head cmd_q; - --struct hci_dev; -+ struct sk_buff *sent_cmd; -+ -+ struct semaphore req_lock; -+ wait_queue_head_t req_wait_q; -+ __u32 req_status; -+ __u32 req_result; -+ -+ struct inquiry_cache inq_cache; -+ struct conn_hash conn_hash; -+ -+ struct hci_dev_stats stat; -+ -+ void *driver_data; -+ void *core_data; -+ -+ atomic_t promisc; -+ -+ int (*open)(struct hci_dev *hdev); -+ int (*close)(struct hci_dev *hdev); -+ int (*flush)(struct hci_dev *hdev); -+ int (*send)(struct sk_buff *skb); -+ void (*destruct)(struct hci_dev *hdev); -+ int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg); -+}; - --/* ----- HCI Connections ----- */ - struct hci_conn { - struct list_head list; -+ -+ atomic_t refcnt; -+ spinlock_t lock; -+ - bdaddr_t dst; - __u16 handle; -+ __u16 state; - __u8 type; -- unsigned int sent; -+ __u8 out; -+ __u32 link_mode; -+ unsigned long pend; -+ -+ unsigned int sent; -+ -+ struct sk_buff_head data_q; - -+ struct timer_list timer; -+ - struct hci_dev *hdev; - void *l2cap_data; -+ void *sco_data; - void *priv; - -- struct sk_buff_head data_q; -+ struct hci_conn *link; - }; - --struct conn_hash { -- struct list_head list; -- spinlock_t lock; -- unsigned int num; --}; -+extern struct hci_proto *hci_proto[]; -+extern struct list_head hdev_list; -+extern rwlock_t hdev_list_lock; - --static inline void conn_hash_init(struct conn_hash *h) -+/* ----- Inquiry cache ----- */ -+#define INQUIRY_CACHE_AGE_MAX (HZ*30) // 30 seconds -+#define INQUIRY_ENTRY_AGE_MAX (HZ*60) // 60 seconds -+ -+#define inquiry_cache_lock(c) spin_lock(&c->lock) -+#define inquiry_cache_unlock(c) spin_unlock(&c->lock) -+#define inquiry_cache_lock_bh(c) spin_lock_bh(&c->lock) -+#define inquiry_cache_unlock_bh(c) spin_unlock_bh(&c->lock) -+ -+static inline void inquiry_cache_init(struct hci_dev *hdev) - { -- INIT_LIST_HEAD(&h->list); -- spin_lock_init(&h->lock); -- h->num = 0; -+ struct inquiry_cache *c = &hdev->inq_cache; -+ spin_lock_init(&c->lock); -+ c->list = NULL; - } - --static inline void conn_hash_lock(struct conn_hash *h) -+static inline int inquiry_cache_empty(struct hci_dev *hdev) - { -- spin_lock(&h->lock); -+ struct inquiry_cache *c = &hdev->inq_cache; -+ return (c->list == NULL); - } - --static inline void conn_hash_unlock(struct conn_hash *h) -+static inline long inquiry_cache_age(struct hci_dev *hdev) - { -- spin_unlock(&h->lock); -+ struct inquiry_cache *c = &hdev->inq_cache; -+ return jiffies - c->timestamp; - } - --static inline void __conn_hash_add(struct conn_hash *h, __u16 handle, struct hci_conn *c) -+static inline long inquiry_entry_age(struct inquiry_entry *e) - { -- list_add(&c->list, &h->list); -- h->num++; -+ return jiffies - e->timestamp; - } - --static inline void conn_hash_add(struct conn_hash *h, __u16 handle, struct hci_conn *c) -+struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); -+void inquiry_cache_update(struct hci_dev *hdev, inquiry_info *info); -+void inquiry_cache_flush(struct hci_dev *hdev); -+int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf); -+ -+/* ----- HCI Connections ----- */ -+enum { -+ HCI_CONN_AUTH_PEND, -+ HCI_CONN_ENCRYPT_PEND -+}; -+ -+#define hci_conn_lock(c) spin_lock(&c->lock) -+#define hci_conn_unlock(c) spin_unlock(&c->lock) -+#define hci_conn_lock_bh(c) spin_lock_bh(&c->lock) -+#define hci_conn_unlock_bh(c) spin_unlock_bh(&c->lock) -+ -+#define conn_hash_lock(d) spin_lock(&d->conn_hash->lock) -+#define conn_hash_unlock(d) spin_unlock(&d->conn_hash->lock) -+#define conn_hash_lock_bh(d) spin_lock_bh(&d->conn_hash->lock) -+#define conn_hash_unlock_bh(d) spin_unlock_bh(&d->conn_hash->lock) -+ -+static inline void conn_hash_init(struct hci_dev *hdev) - { -- conn_hash_lock(h); -- __conn_hash_add(h, handle, c); -- conn_hash_unlock(h); -+ struct conn_hash *h = &hdev->conn_hash; -+ INIT_LIST_HEAD(&h->list); -+ spin_lock_init(&h->lock); -+ h->num = 0; - } - --static inline void __conn_hash_del(struct conn_hash *h, struct hci_conn *c) -+static inline void conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) - { -- list_del(&c->list); -- h->num--; -+ struct conn_hash *h = &hdev->conn_hash; -+ list_add(&c->list, &h->list); -+ h->num++; - } - --static inline void conn_hash_del(struct conn_hash *h, struct hci_conn *c) -+static inline void conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) - { -- conn_hash_lock(h); -- __conn_hash_del(h, c); -- conn_hash_unlock(h); -+ struct conn_hash *h = &hdev->conn_hash; -+ list_del(&c->list); -+ h->num--; - } - --static inline struct hci_conn *__conn_hash_lookup(struct conn_hash *h, __u16 handle) -+static inline struct hci_conn *conn_hash_lookup_handle(struct hci_dev *hdev, -+ __u16 handle) - { -+ register struct conn_hash *h = &hdev->conn_hash; - register struct list_head *p; - register struct hci_conn *c; - -@@ -169,101 +242,97 @@ - return NULL; - } - --static inline struct hci_conn *conn_hash_lookup(struct conn_hash *h, __u16 handle) -+static inline struct hci_conn *conn_hash_lookup_ba(struct hci_dev *hdev, -+ __u8 type, bdaddr_t *ba) - { -- struct hci_conn *conn; -+ register struct conn_hash *h = &hdev->conn_hash; -+ register struct list_head *p; -+ register struct hci_conn *c; - -- conn_hash_lock(h); -- conn = __conn_hash_lookup(h, handle); -- conn_hash_unlock(h); -- return conn; -+ list_for_each(p, &h->list) { -+ c = list_entry(p, struct hci_conn, list); -+ if (c->type == type && !bacmp(&c->dst, ba)) -+ return c; -+ } -+ return NULL; - } - --/* ----- HCI Devices ----- */ --struct hci_dev { -- atomic_t refcnt; -- -- char name[8]; -- __u32 flags; -- __u16 id; -- __u8 type; -- bdaddr_t bdaddr; -- __u8 features[8]; -- -- __u16 pkt_type; -- -- atomic_t cmd_cnt; -- unsigned int acl_cnt; -- unsigned int sco_cnt; -- -- unsigned int acl_mtu; -- unsigned int sco_mtu; -- unsigned int acl_max; -- unsigned int sco_max; -- -- void *driver_data; -- void *l2cap_data; -- void *priv; -- -- struct tasklet_struct cmd_task; -- struct tasklet_struct rx_task; -- struct tasklet_struct tx_task; -- -- struct sk_buff_head rx_q; -- struct sk_buff_head raw_q; -- struct sk_buff_head cmd_q; -- -- struct sk_buff *sent_cmd; -- -- struct semaphore req_lock; -- wait_queue_head_t req_wait_q; -- __u32 req_status; -- __u32 req_result; -+void hci_acl_connect(struct hci_conn *conn); -+void hci_acl_disconn(struct hci_conn *conn, __u8 reason); -+void hci_add_sco(struct hci_conn *conn, __u16 handle); - -- struct inquiry_cache inq_cache; -+struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst); -+int hci_conn_del(struct hci_conn *conn); -+void hci_conn_hash_flush(struct hci_dev *hdev); - -- struct conn_hash conn_hash; -+struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *src); -+int hci_conn_auth(struct hci_conn *conn); -+int hci_conn_encrypt(struct hci_conn *conn); - -- struct hci_dev_stats stat; -+static inline void hci_conn_set_timer(struct hci_conn *conn, long timeout) -+{ -+ mod_timer(&conn->timer, jiffies + timeout); -+} - -- int (*open)(struct hci_dev *hdev); -- int (*close)(struct hci_dev *hdev); -- int (*flush)(struct hci_dev *hdev); -- int (*send)(struct sk_buff *skb); --}; -+static inline void hci_conn_del_timer(struct hci_conn *conn) -+{ -+ del_timer(&conn->timer); -+} - --static inline void hci_dev_hold(struct hci_dev *hdev) -+static inline void hci_conn_hold(struct hci_conn *conn) - { -- atomic_inc(&hdev->refcnt); -+ atomic_inc(&conn->refcnt); -+ hci_conn_del_timer(conn); - } - --static inline void hci_dev_put(struct hci_dev *hdev) -+static inline void hci_conn_put(struct hci_conn *conn) - { -- atomic_dec(&hdev->refcnt); -+ if (atomic_dec_and_test(&conn->refcnt)) { -+ if (conn->type == ACL_LINK) { -+ unsigned long timeo = (conn->out) ? -+ HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2; -+ hci_conn_set_timer(conn, timeo); -+ } else -+ hci_conn_set_timer(conn, HZ / 100); -+ } - } - --extern struct hci_dev *hci_dev_get(int index); --extern int hci_register_dev(struct hci_dev *hdev); --extern int hci_unregister_dev(struct hci_dev *hdev); --extern int hci_dev_open(__u16 dev); --extern int hci_dev_close(__u16 dev); --extern int hci_dev_reset(__u16 dev); --extern int hci_dev_reset_stat(__u16 dev); --extern int hci_dev_info(unsigned long arg); --extern int hci_dev_list(unsigned long arg); --extern int hci_dev_setscan(unsigned long arg); --extern int hci_dev_setauth(unsigned long arg); --extern int hci_dev_setptype(unsigned long arg); --extern int hci_conn_list(unsigned long arg); --extern int hci_inquiry(unsigned long arg); -+/* ----- HCI Devices ----- */ -+static inline void hci_dev_put(struct hci_dev *d) -+{ -+ if (atomic_dec_and_test(&d->refcnt)) -+ d->destruct(d); -+} -+#define hci_dev_hold(d) atomic_inc(&d->refcnt) - --extern __u32 hci_dev_setmode(struct hci_dev *hdev, __u32 mode); --extern __u32 hci_dev_getmode(struct hci_dev *hdev); -+#define hci_dev_lock(d) spin_lock(&d->lock) -+#define hci_dev_unlock(d) spin_unlock(&d->lock) -+#define hci_dev_lock_bh(d) spin_lock_bh(&d->lock) -+#define hci_dev_unlock_bh(d) spin_unlock_bh(&d->lock) - --extern int hci_recv_frame(struct sk_buff *skb); -+struct hci_dev *hci_dev_get(int index); -+struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); -+int hci_register_dev(struct hci_dev *hdev); -+int hci_unregister_dev(struct hci_dev *hdev); -+int hci_suspend_dev(struct hci_dev *hdev); -+int hci_resume_dev(struct hci_dev *hdev); -+int hci_dev_open(__u16 dev); -+int hci_dev_close(__u16 dev); -+int hci_dev_reset(__u16 dev); -+int hci_dev_reset_stat(__u16 dev); -+int hci_dev_cmd(unsigned int cmd, unsigned long arg); -+int hci_get_dev_list(unsigned long arg); -+int hci_get_dev_info(unsigned long arg); -+int hci_get_conn_list(unsigned long arg); -+int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg); -+int hci_inquiry(unsigned long arg); -+ -+int hci_recv_frame(struct sk_buff *skb); -+void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); - - /* ----- LMP capabilities ----- */ - #define lmp_rswitch_capable(dev) (dev->features[0] & LMP_RSWITCH) -+#define lmp_encrypt_capable(dev) (dev->features[0] & LMP_ENCRYPT) - - /* ----- HCI tasks ----- */ - static inline void hci_sched_cmd(struct hci_dev *hdev) -@@ -284,43 +353,130 @@ - /* ----- HCI protocols ----- */ - struct hci_proto { - char *name; -- __u32 id; -- __u32 flags; -+ unsigned int id; -+ unsigned long flags; - - void *priv; - -- int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr); -- int (*connect_cfm) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 status, struct hci_conn *conn); -+ int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type); -+ int (*connect_cfm) (struct hci_conn *conn, __u8 status); - int (*disconn_ind) (struct hci_conn *conn, __u8 reason); -- int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb , __u16 flags); -+ int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags); - int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb); -+ int (*auth_cfm) (struct hci_conn *conn, __u8 status); -+ int (*encrypt_cfm) (struct hci_conn *conn, __u8 status); - }; - --extern int hci_register_proto(struct hci_proto *hproto); --extern int hci_unregister_proto(struct hci_proto *hproto); --extern int hci_register_notifier(struct notifier_block *nb); --extern int hci_unregister_notifier(struct notifier_block *nb); --extern int hci_connect(struct hci_dev * hdev, bdaddr_t * bdaddr); --extern int hci_disconnect(struct hci_conn *conn, __u8 reason); --extern int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void * param); --extern int hci_send_raw(struct sk_buff *skb); --extern int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags); --extern int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); -+static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type) -+{ -+ register struct hci_proto *hp; -+ int mask = 0; -+ -+ hp = hci_proto[HCI_PROTO_L2CAP]; -+ if (hp && hp->connect_ind) -+ mask |= hp->connect_ind(hdev, bdaddr, type); -+ -+ hp = hci_proto[HCI_PROTO_SCO]; -+ if (hp && hp->connect_ind) -+ mask |= hp->connect_ind(hdev, bdaddr, type); -+ -+ return mask; -+} -+ -+static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status) -+{ -+ register struct hci_proto *hp; -+ -+ hp = hci_proto[HCI_PROTO_L2CAP]; -+ if (hp && hp->connect_cfm) -+ hp->connect_cfm(conn, status); -+ -+ hp = hci_proto[HCI_PROTO_SCO]; -+ if (hp && hp->connect_cfm) -+ hp->connect_cfm(conn, status); -+} -+ -+static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason) -+{ -+ register struct hci_proto *hp; -+ -+ hp = hci_proto[HCI_PROTO_L2CAP]; -+ if (hp && hp->disconn_ind) -+ hp->disconn_ind(conn, reason); -+ -+ hp = hci_proto[HCI_PROTO_SCO]; -+ if (hp && hp->disconn_ind) -+ hp->disconn_ind(conn, reason); -+} -+ -+static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) -+{ -+ register struct hci_proto *hp; -+ -+ hp = hci_proto[HCI_PROTO_L2CAP]; -+ if (hp && hp->auth_cfm) -+ hp->auth_cfm(conn, status); -+ -+ hp = hci_proto[HCI_PROTO_SCO]; -+ if (hp && hp->auth_cfm) -+ hp->auth_cfm(conn, status); -+} -+ -+static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status) -+{ -+ register struct hci_proto *hp; -+ -+ hp = hci_proto[HCI_PROTO_L2CAP]; -+ if (hp && hp->encrypt_cfm) -+ hp->encrypt_cfm(conn, status); -+ -+ hp = hci_proto[HCI_PROTO_SCO]; -+ if (hp && hp->encrypt_cfm) -+ hp->encrypt_cfm(conn, status); -+} -+ -+int hci_register_proto(struct hci_proto *hproto); -+int hci_unregister_proto(struct hci_proto *hproto); -+int hci_register_notifier(struct notifier_block *nb); -+int hci_unregister_notifier(struct notifier_block *nb); -+ -+int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param); -+int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags); -+int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); -+ -+void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf); -+ -+void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data); - - /* ----- HCI Sockets ----- */ --extern void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); -+void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); - - /* HCI info for socket */ --#define hci_pi(sk) ((struct hci_pinfo *) &sk->protinfo) -+#define hci_pi(sk) ((struct hci_pinfo *) &sk->tp_pinfo) - struct hci_pinfo { - struct hci_dev *hdev; - struct hci_filter filter; - __u32 cmsg_mask; - }; - -+/* HCI security filter */ -+#define HCI_SFLT_MAX_OGF 5 -+ -+struct hci_sec_filter { -+ __u32 type_mask; -+ __u32 event_mask[2]; -+ __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; -+}; -+ - /* ----- HCI requests ----- */ - #define HCI_REQ_DONE 0 - #define HCI_REQ_PEND 1 - #define HCI_REQ_CANCELED 2 - -+#define hci_req_lock(d) down(&d->req_lock) -+#define hci_req_unlock(d) up(&d->req_lock) -+ -+void hci_req_complete(struct hci_dev *hdev, int result); -+void hci_req_cancel(struct hci_dev *hdev, int err); -+ - #endif /* __HCI_CORE_H */ ---- linux/include/net/bluetooth/hci.h~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/include/net/bluetooth/hci.h 2004-01-25 23:37:39.000000000 +0100 -@@ -23,59 +23,75 @@ - */ - - /* -- * $Id: hci.h,v 1.15 2001/08/05 06:02:15 maxk Exp $ -+ * $Id: hci.h,v 1.5 2002/06/27 17:29:30 maxk Exp $ - */ - - #ifndef __HCI_H - #define __HCI_H - --#include <asm/byteorder.h> -- --#define HCI_MAX_DEV 8 --#define HCI_MAX_FRAME_SIZE 2048 -+#define HCI_MAX_ACL_SIZE 1024 -+#define HCI_MAX_SCO_SIZE 255 -+#define HCI_MAX_EVENT_SIZE 260 -+#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4) - - /* HCI dev events */ - #define HCI_DEV_REG 1 - #define HCI_DEV_UNREG 2 - #define HCI_DEV_UP 3 - #define HCI_DEV_DOWN 4 -+#define HCI_DEV_SUSPEND 5 -+#define HCI_DEV_RESUME 6 - - /* HCI device types */ --#define HCI_UART 0 -+#define HCI_VHCI 0 - #define HCI_USB 1 --#define HCI_VHCI 2 -+#define HCI_PCCARD 2 -+#define HCI_UART 3 -+#define HCI_RS232 4 -+#define HCI_PCI 5 - --/* HCI device modes */ --#define HCI_NORMAL 0x0001 --#define HCI_RAW 0x0002 --#define HCI_MODE_MASK (HCI_NORMAL | HCI_RAW) --#define HCI_SOCK 0x1000 -+/* HCI device flags */ -+enum { -+ HCI_UP, -+ HCI_INIT, -+ HCI_RUNNING, - --/* HCI device states */ --#define HCI_INIT 0x0010 --#define HCI_UP 0x0020 --#define HCI_RUNNING 0x0040 -+ HCI_PSCAN, -+ HCI_ISCAN, -+ HCI_AUTH, -+ HCI_ENCRYPT, -+ HCI_INQUIRY, - --/* HCI device flags */ --#define HCI_PSCAN 0x0100 --#define HCI_ISCAN 0x0200 --#define HCI_AUTH 0x0400 -+ HCI_RAW -+}; - --/* HCI Ioctl defines */ -+/* HCI ioctl defines */ - #define HCIDEVUP _IOW('H', 201, int) - #define HCIDEVDOWN _IOW('H', 202, int) - #define HCIDEVRESET _IOW('H', 203, int) --#define HCIRESETSTAT _IOW('H', 204, int) --#define HCIGETINFO _IOR('H', 205, int) --#define HCIGETDEVLIST _IOR('H', 206, int) --#define HCISETRAW _IOW('H', 207, int) --#define HCISETSCAN _IOW('H', 208, int) --#define HCISETAUTH _IOW('H', 209, int) --#define HCIINQUIRY _IOR('H', 210, int) --#define HCISETPTYPE _IOW('H', 211, int) -+#define HCIDEVRESTAT _IOW('H', 204, int) -+ -+#define HCIGETDEVLIST _IOR('H', 210, int) -+#define HCIGETDEVINFO _IOR('H', 211, int) - #define HCIGETCONNLIST _IOR('H', 212, int) -+#define HCIGETCONNINFO _IOR('H', 213, int) - --#ifndef __NO_HCI_DEFS -+#define HCISETRAW _IOW('H', 220, int) -+#define HCISETSCAN _IOW('H', 221, int) -+#define HCISETAUTH _IOW('H', 222, int) -+#define HCISETENCRYPT _IOW('H', 223, int) -+#define HCISETPTYPE _IOW('H', 224, int) -+#define HCISETLINKPOL _IOW('H', 225, int) -+#define HCISETLINKMODE _IOW('H', 226, int) -+#define HCISETACLMTU _IOW('H', 227, int) -+#define HCISETSCOMTU _IOW('H', 228, int) -+ -+#define HCIINQUIRY _IOR('H', 240, int) -+ -+/* HCI timeouts */ -+#define HCI_CONN_TIMEOUT (HZ * 40) -+#define HCI_DISCONN_TIMEOUT (HZ * 2) -+#define HCI_CONN_IDLE_TIMEOUT (HZ * 60) - - /* HCI Packet types */ - #define HCI_COMMAND_PKT 0x01 -@@ -92,11 +108,18 @@ - #define HCI_DH3 0x0800 - #define HCI_DH5 0x8000 - -+#define HCI_HV1 0x0020 -+#define HCI_HV2 0x0040 -+#define HCI_HV3 0x0080 -+ -+#define SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) -+#define ACL_PTYPE_MASK (~SCO_PTYPE_MASK) -+ - /* ACL flags */ --#define ACL_CONT 0x0001 --#define ACL_START 0x0002 --#define ACL_ACTIVE_BCAST 0x0010 --#define ACL_PICO_BCAST 0x0020 -+#define ACL_CONT 0x01 -+#define ACL_START 0x02 -+#define ACL_ACTIVE_BCAST 0x04 -+#define ACL_PICO_BCAST 0x08 - - /* Baseband links */ - #define SCO_LINK 0x00 -@@ -125,6 +148,20 @@ - #define LMP_PSCHEME 0x02 - #define LMP_PCONTROL 0x04 - -+/* Link policies */ -+#define HCI_LP_RSWITCH 0x0001 -+#define HCI_LP_HOLD 0x0002 -+#define HCI_LP_SNIFF 0x0004 -+#define HCI_LP_PARK 0x0008 -+ -+/* Link mode */ -+#define HCI_LM_ACCEPT 0x8000 -+#define HCI_LM_MASTER 0x0001 -+#define HCI_LM_AUTH 0x0002 -+#define HCI_LM_ENCRYPT 0x0004 -+#define HCI_LM_TRUSTED 0x0008 -+#define HCI_LM_RELIABLE 0x0010 -+ - /* ----- HCI Commands ----- */ - /* OGF & OCF values */ - -@@ -137,9 +174,10 @@ - __u8 hci_ver; - __u16 hci_rev; - __u8 lmp_ver; -- __u16 man_name; -- __u16 lmp_sub; -+ __u16 manufacturer; -+ __u16 lmp_subver; - } __attribute__ ((packed)) read_local_version_rp; -+#define READ_LOCAL_VERSION_RP_SIZE 9 - - #define OCF_READ_LOCAL_FEATURES 0x0003 - typedef struct { -@@ -165,18 +203,24 @@ - /* Host Controller and Baseband */ - #define OGF_HOST_CTL 0x03 - #define OCF_RESET 0x0003 -+#define OCF_READ_AUTH_ENABLE 0x001F - #define OCF_WRITE_AUTH_ENABLE 0x0020 -- #define AUTH_DISABLED 0x00 -- #define AUTH_ENABLED 0x01 -+ #define AUTH_DISABLED 0x00 -+ #define AUTH_ENABLED 0x01 -+ -+#define OCF_READ_ENCRYPT_MODE 0x0021 -+#define OCF_WRITE_ENCRYPT_MODE 0x0022 -+ #define ENCRYPT_DISABLED 0x00 -+ #define ENCRYPT_P2P 0x01 -+ #define ENCRYPT_BOTH 0x02 - - #define OCF_WRITE_CA_TIMEOUT 0x0016 - #define OCF_WRITE_PG_TIMEOUT 0x0018 - - #define OCF_WRITE_SCAN_ENABLE 0x001A -- #define SCANS_DISABLED 0x00 -- #define IS_ENA_PS_DIS 0x01 -- #define IS_DIS_PS_ENA 0x02 -- #define IS_ENA_PS_ENA 0x03 -+ #define SCAN_DISABLED 0x00 -+ #define SCAN_INQUIRY 0x01 -+ #define SCAN_PAGE 0x02 - - #define OCF_SET_EVENT_FLT 0x0005 - typedef struct { -@@ -226,9 +270,18 @@ - } __attribute__ ((packed)) write_class_of_dev_cp; - #define WRITE_CLASS_OF_DEV_CP_SIZE 3 - -+#define OCF_HOST_BUFFER_SIZE 0x0033 -+typedef struct { -+ __u16 acl_mtu; -+ __u8 sco_mtu; -+ __u16 acl_max_pkt; -+ __u16 sco_max_pkt; -+} __attribute__ ((packed)) host_buffer_size_cp; -+#define HOST_BUFFER_SIZE_CP_SIZE 7 -+ - /* Link Control */ - #define OGF_LINK_CTL 0x01 --#define OCF_CREATE_CONN 0x0005 -+#define OCF_CREATE_CONN 0x0005 - typedef struct { - bdaddr_t bdaddr; - __u16 pkt_type; -@@ -246,6 +299,13 @@ - } __attribute__ ((packed)) accept_conn_req_cp; - #define ACCEPT_CONN_REQ_CP_SIZE 7 - -+#define OCF_REJECT_CONN_REQ 0x000a -+typedef struct { -+ bdaddr_t bdaddr; -+ __u8 reason; -+} __attribute__ ((packed)) reject_conn_req_cp; -+#define REJECT_CONN_REQ_CP_SIZE 7 -+ - #define OCF_DISCONNECT 0x0006 - typedef struct { - __u16 handle; -@@ -253,17 +313,142 @@ - } __attribute__ ((packed)) disconnect_cp; - #define DISCONNECT_CP_SIZE 3 - -+#define OCF_ADD_SCO 0x0007 -+typedef struct { -+ __u16 handle; -+ __u16 pkt_type; -+} __attribute__ ((packed)) add_sco_cp; -+#define ADD_SCO_CP_SIZE 4 -+ - #define OCF_INQUIRY 0x0001 - typedef struct { - __u8 lap[3]; -- __u8 lenght; -+ __u8 length; - __u8 num_rsp; - } __attribute__ ((packed)) inquiry_cp; - #define INQUIRY_CP_SIZE 5 - --#define OGF_LINK_POLICY 0x02 /* Link Policy */ -+typedef struct { -+ __u8 status; -+ bdaddr_t bdaddr; -+} __attribute__ ((packed)) status_bdaddr_rp; -+#define STATUS_BDADDR_RP_SIZE 7 - --/* --------- HCI Events --------- */ -+#define OCF_INQUIRY_CANCEL 0x0002 -+ -+#define OCF_LINK_KEY_REPLY 0x000B -+#define OCF_LINK_KEY_NEG_REPLY 0x000C -+typedef struct { -+ bdaddr_t bdaddr; -+ __u8 link_key[16]; -+} __attribute__ ((packed)) link_key_reply_cp; -+#define LINK_KEY_REPLY_CP_SIZE 22 -+ -+#define OCF_PIN_CODE_REPLY 0x000D -+#define OCF_PIN_CODE_NEG_REPLY 0x000E -+typedef struct { -+ bdaddr_t bdaddr; -+ __u8 pin_len; -+ __u8 pin_code[16]; -+} __attribute__ ((packed)) pin_code_reply_cp; -+#define PIN_CODE_REPLY_CP_SIZE 23 -+ -+#define OCF_CHANGE_CONN_PTYPE 0x000F -+typedef struct { -+ __u16 handle; -+ __u16 pkt_type; -+} __attribute__ ((packed)) change_conn_ptype_cp; -+#define CHANGE_CONN_PTYPE_CP_SIZE 4 -+ -+#define OCF_AUTH_REQUESTED 0x0011 -+typedef struct { -+ __u16 handle; -+} __attribute__ ((packed)) auth_requested_cp; -+#define AUTH_REQUESTED_CP_SIZE 2 -+ -+#define OCF_SET_CONN_ENCRYPT 0x0013 -+typedef struct { -+ __u16 handle; -+ __u8 encrypt; -+} __attribute__ ((packed)) set_conn_encrypt_cp; -+#define SET_CONN_ENCRYPT_CP_SIZE 3 -+ -+#define OCF_REMOTE_NAME_REQ 0x0019 -+typedef struct { -+ bdaddr_t bdaddr; -+ __u8 pscan_rep_mode; -+ __u8 pscan_mode; -+ __u16 clock_offset; -+} __attribute__ ((packed)) remote_name_req_cp; -+#define REMOTE_NAME_REQ_CP_SIZE 10 -+ -+#define OCF_READ_REMOTE_FEATURES 0x001B -+typedef struct { -+ __u16 handle; -+} __attribute__ ((packed)) read_remote_features_cp; -+#define READ_REMOTE_FEATURES_CP_SIZE 2 -+ -+#define OCF_READ_REMOTE_VERSION 0x001D -+typedef struct { -+ __u16 handle; -+} __attribute__ ((packed)) read_remote_version_cp; -+#define READ_REMOTE_VERSION_CP_SIZE 2 -+ -+/* Link Policy */ -+#define OGF_LINK_POLICY 0x02 -+#define OCF_ROLE_DISCOVERY 0x0009 -+typedef struct { -+ __u16 handle; -+} __attribute__ ((packed)) role_discovery_cp; -+#define ROLE_DISCOVERY_CP_SIZE 2 -+typedef struct { -+ __u8 status; -+ __u16 handle; -+ __u8 role; -+} __attribute__ ((packed)) role_discovery_rp; -+#define ROLE_DISCOVERY_RP_SIZE 4 -+ -+#define OCF_READ_LINK_POLICY 0x000C -+typedef struct { -+ __u16 handle; -+} __attribute__ ((packed)) read_link_policy_cp; -+#define READ_LINK_POLICY_CP_SIZE 2 -+typedef struct { -+ __u8 status; -+ __u16 handle; -+ __u16 policy; -+} __attribute__ ((packed)) read_link_policy_rp; -+#define READ_LINK_POLICY_RP_SIZE 5 -+ -+#define OCF_SWITCH_ROLE 0x000B -+typedef struct { -+ bdaddr_t bdaddr; -+ __u8 role; -+} __attribute__ ((packed)) switch_role_cp; -+#define SWITCH_ROLE_CP_SIZE 7 -+ -+#define OCF_WRITE_LINK_POLICY 0x000D -+typedef struct { -+ __u16 handle; -+ __u16 policy; -+} __attribute__ ((packed)) write_link_policy_cp; -+#define WRITE_LINK_POLICY_CP_SIZE 4 -+typedef struct { -+ __u8 status; -+ __u16 handle; -+} __attribute__ ((packed)) write_link_policy_rp; -+#define WRITE_LINK_POLICY_RP_SIZE 3 -+ -+/* Status params */ -+#define OGF_STATUS_PARAM 0x05 -+ -+/* Testing commands */ -+#define OGF_TESTING_CMD 0x3e -+ -+/* Vendor specific commands */ -+#define OGF_VENDOR_CMD 0x3f -+ -+/* ---- HCI Events ---- */ - #define EVT_INQUIRY_COMPLETE 0x01 - - #define EVT_INQUIRY_RESULT 0x02 -@@ -272,11 +457,22 @@ - __u8 pscan_rep_mode; - __u8 pscan_period_mode; - __u8 pscan_mode; -- __u8 class[3]; -+ __u8 dev_class[3]; - __u16 clock_offset; - } __attribute__ ((packed)) inquiry_info; - #define INQUIRY_INFO_SIZE 14 - -+#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22 -+typedef struct { -+ bdaddr_t bdaddr; -+ __u8 pscan_rep_mode; -+ __u8 pscan_period_mode; -+ __u8 dev_class[3]; -+ __u16 clock_offset; -+ __u8 rssi; -+} __attribute__ ((packed)) inquiry_info_with_rssi; -+#define INQUIRY_INFO_WITH_RSSI_SIZE 14 -+ - #define EVT_CONN_COMPLETE 0x03 - typedef struct { - __u8 status; -@@ -303,6 +499,44 @@ - } __attribute__ ((packed)) evt_disconn_complete; - #define EVT_DISCONN_COMPLETE_SIZE 4 - -+#define EVT_AUTH_COMPLETE 0x06 -+typedef struct { -+ __u8 status; -+ __u16 handle; -+} __attribute__ ((packed)) evt_auth_complete; -+#define EVT_AUTH_COMPLETE_SIZE 3 -+ -+#define EVT_REMOTE_NAME_REQ_COMPLETE 0x07 -+typedef struct { -+ __u8 status; -+ bdaddr_t bdaddr; -+ __u8 name[248]; -+} __attribute__ ((packed)) evt_remote_name_req_complete; -+#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255 -+ -+#define EVT_ENCRYPT_CHANGE 0x08 -+typedef struct { -+ __u8 status; -+ __u16 handle; -+ __u8 encrypt; -+} __attribute__ ((packed)) evt_encrypt_change; -+#define EVT_ENCRYPT_CHANGE_SIZE 5 -+ -+#define EVT_QOS_SETUP_COMPLETE 0x0D -+typedef struct { -+ __u8 service_type; -+ __u32 token_rate; -+ __u32 peak_bandwidth; -+ __u32 latency; -+ __u32 delay_variation; -+} __attribute__ ((packed)) hci_qos; -+typedef struct { -+ __u8 status; -+ __u16 handle; -+ hci_qos qos; -+} __attribute__ ((packed)) evt_qos_setup_complete; -+#define EVT_QOS_SETUP_COMPLETE_SIZE 20 -+ - #define EVT_CMD_COMPLETE 0x0e - typedef struct { - __u8 ncmd; -@@ -321,16 +555,78 @@ - #define EVT_NUM_COMP_PKTS 0x13 - typedef struct { - __u8 num_hndl; -- /* variable lenght part */ -+ /* variable length part */ - } __attribute__ ((packed)) evt_num_comp_pkts; - #define EVT_NUM_COMP_PKTS_SIZE 1 - --#define EVT_HCI_DEV_EVENT 0xfd -+#define EVT_ROLE_CHANGE 0x12 -+typedef struct { -+ __u8 status; -+ bdaddr_t bdaddr; -+ __u8 role; -+} __attribute__ ((packed)) evt_role_change; -+#define EVT_ROLE_CHANGE_SIZE 8 -+ -+#define EVT_PIN_CODE_REQ 0x16 -+typedef struct { -+ bdaddr_t bdaddr; -+} __attribute__ ((packed)) evt_pin_code_req; -+#define EVT_PIN_CODE_REQ_SIZE 6 -+ -+#define EVT_LINK_KEY_REQ 0x17 -+typedef struct { -+ bdaddr_t bdaddr; -+} __attribute__ ((packed)) evt_link_key_req; -+#define EVT_LINK_KEY_REQ_SIZE 6 -+ -+#define EVT_LINK_KEY_NOTIFY 0x18 -+typedef struct { -+ bdaddr_t bdaddr; -+ __u8 link_key[16]; -+ __u8 key_type; -+} __attribute__ ((packed)) evt_link_key_notify; -+#define EVT_LINK_KEY_NOTIFY_SIZE 23 -+ -+#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B -+typedef struct { -+ __u8 status; -+ __u16 handle; -+ __u8 features[8]; -+} __attribute__ ((packed)) evt_read_remote_features_complete; -+#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11 -+ -+#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C -+typedef struct { -+ __u8 status; -+ __u16 handle; -+ __u8 lmp_ver; -+ __u16 manufacturer; -+ __u16 lmp_subver; -+} __attribute__ ((packed)) evt_read_remote_version_complete; -+#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8 -+ -+/* Internal events generated by BlueZ stack */ -+#define EVT_STACK_INTERNAL 0xfd -+typedef struct { -+ __u16 type; -+ __u8 data[0]; -+} __attribute__ ((packed)) evt_stack_internal; -+#define EVT_STACK_INTERNAL_SIZE 2 -+ -+#define EVT_SI_DEVICE 0x01 -+typedef struct { -+ __u16 event; -+ __u16 dev_id; -+} __attribute__ ((packed)) evt_si_device; -+#define EVT_SI_DEVICE_SIZE 4 -+ -+#define EVT_SI_SECURITY 0x02 - typedef struct { - __u16 event; -- __u16 param; --} __attribute__ ((packed)) evt_hci_dev_event; --#define EVT_HCI_DEV_EVENT_SIZE 4 -+ __u16 proto; -+ __u16 subproto; -+ __u8 incomming; -+} __attribute__ ((packed)) evt_si_security; - - /* -------- HCI Packet structures -------- */ - #define HCI_TYPE_LEN 1 -@@ -369,14 +665,14 @@ - #define acl_handle(h) (h & 0x0fff) - #define acl_flags(h) (h >> 12) - --#endif /* _NO_HCI_DEFS */ -- - /* HCI Socket options */ --#define HCI_DATA_DIR 0x0001 --#define HCI_FILTER 0x0002 -+#define HCI_DATA_DIR 1 -+#define HCI_FILTER 2 -+#define HCI_TIME_STAMP 3 - - /* HCI CMSG flags */ - #define HCI_CMSG_DIR 0x0001 -+#define HCI_CMSG_TSTAMP 0x0002 - - struct sockaddr_hci { - sa_family_t hci_family; -@@ -387,27 +683,29 @@ - struct hci_filter { - __u32 type_mask; - __u32 event_mask[2]; -+ __u16 opcode; - }; - --struct hci_dev_req { -- __u16 dev_id; -- __u32 dev_opt; --}; -- --struct hci_dev_list_req { -- __u16 dev_num; -- struct hci_dev_req dev_req[0]; /* hci_dev_req structures */ --}; -+#define HCI_FLT_TYPE_BITS 31 -+#define HCI_FLT_EVENT_BITS 63 -+#define HCI_FLT_OGF_BITS 63 -+#define HCI_FLT_OCF_BITS 127 - --struct hci_inquiry_req { -- __u16 dev_id; -- __u16 flags; -- __u8 lap[3]; -- __u8 length; -- __u8 num_rsp; --}; --#define IREQ_CACHE_FLUSH 0x0001 -+#if BITS_PER_LONG == 64 -+static inline void hci_set_bit(int nr, void *addr) -+{ -+ *((__u32 *) addr + (nr >> 5)) |= ((__u32) 1 << (nr & 31)); -+} -+static inline int hci_test_bit(int nr, void *addr) -+{ -+ return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); -+} -+#else -+#define hci_set_bit set_bit -+#define hci_test_bit test_bit -+#endif - -+/* Ioctl requests structures */ - struct hci_dev_stats { - __u32 err_rx; - __u32 err_tx; -@@ -433,11 +731,13 @@ - __u8 features[8]; - - __u32 pkt_type; -+ __u32 link_policy; -+ __u32 link_mode; - - __u16 acl_mtu; -- __u16 acl_max; -+ __u16 acl_pkts; - __u16 sco_mtu; -- __u16 sco_max; -+ __u16 sco_pkts; - - struct hci_dev_stats stat; - }; -@@ -445,6 +745,20 @@ - struct hci_conn_info { - __u16 handle; - bdaddr_t bdaddr; -+ __u8 type; -+ __u8 out; -+ __u16 state; -+ __u32 link_mode; -+}; -+ -+struct hci_dev_req { -+ __u16 dev_id; -+ __u32 dev_opt; -+}; -+ -+struct hci_dev_list_req { -+ __u16 dev_num; -+ struct hci_dev_req dev_req[0]; /* hci_dev_req structures */ - }; - - struct hci_conn_list_req { -@@ -453,4 +767,26 @@ - struct hci_conn_info conn_info[0]; - }; - -+struct hci_conn_info_req { -+ bdaddr_t bdaddr; -+ __u8 type; -+ struct hci_conn_info conn_info[0]; -+}; -+ -+struct hci_inquiry_req { -+ __u16 dev_id; -+ __u16 flags; -+ __u8 lap[3]; -+ __u8 length; -+ __u8 num_rsp; -+}; -+#define IREQ_CACHE_FLUSH 0x0001 -+ -+struct hci_remotename_req { -+ __u16 dev_id; -+ __u16 flags; -+ bdaddr_t bdaddr; -+ __u8 name[248]; -+}; -+ - #endif /* __HCI_H */ ---- linux/include/net/bluetooth/hci_uart.h~bluetooth-2.4.18-mh11 -+++ linux/include/net/bluetooth/hci_uart.h --/* -- BlueZ - Bluetooth protocol stack for Linux -- Copyright (C) 2000-2001 Qualcomm Incorporated -- -- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License version 2 as -- published by the Free Software Foundation; -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -- -- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -- SOFTWARE IS DISCLAIMED. --*/ -- --/* -- * $Id: hci_uart.h,v 1.2 2001/06/02 01:40:08 maxk Exp $ -- */ -- --#ifndef N_HCI --#define N_HCI 15 --#endif -- --#ifdef __KERNEL__ -- --#define tty2n_hci(tty) ((struct n_hci *)((tty)->disc_data)) --#define n_hci2tty(n_hci) ((n_hci)->tty) -- --struct n_hci { -- struct tty_struct *tty; -- struct hci_dev hdev; -- -- struct sk_buff_head txq; -- unsigned long tx_state; -- -- spinlock_t rx_lock; -- unsigned long rx_state; -- unsigned long rx_count; -- struct sk_buff *rx_skb; --}; -- --/* Transmit states */ --#define TRANS_SENDING 1 --#define TRANS_WAKEUP 2 -- --/* Receiver States */ --#define WAIT_PACKET_TYPE 0 --#define WAIT_EVENT_HDR 1 --#define WAIT_ACL_HDR 2 --#define WAIT_SCO_HDR 3 --#define WAIT_DATA 4 -- --#endif /* __KERNEL__ */ ---- linux/include/net/bluetooth/hci_usb.h~bluetooth-2.4.18-mh11 -+++ linux/include/net/bluetooth/hci_usb.h --/* -- BlueZ - Bluetooth protocol stack for Linux -- Copyright (C) 2000-2001 Qualcomm Incorporated -- -- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License version 2 as -- published by the Free Software Foundation; -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -- -- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -- SOFTWARE IS DISCLAIMED. --*/ -- --/* -- * $Id: hci_usb.h,v 1.3 2001/06/02 01:40:08 maxk Exp $ -- */ -- --#ifdef __KERNEL__ -- --/* Class, SubClass, and Protocol codes that describe a Bluetooth device */ --#define HCI_DEV_CLASS 0xe0 /* Wireless class */ --#define HCI_DEV_SUBCLASS 0x01 /* RF subclass */ --#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */ -- --#define HCI_CTRL_REQ 0x20 -- --struct hci_usb { -- struct usb_device *udev; -- -- devrequest dev_req; -- struct urb *ctrl_urb; -- struct urb *intr_urb; -- struct urb *read_urb; -- struct urb *write_urb; -- -- __u8 *read_buf; -- __u8 *intr_buf; -- struct sk_buff *intr_skb; -- int intr_count; -- -- __u8 bulk_out_ep_addr; -- __u8 bulk_in_ep_addr; -- __u8 intr_in_ep_addr; -- __u8 intr_in_interval; -- -- struct hci_dev hdev; -- -- unsigned long tx_state; -- struct sk_buff_head tx_ctrl_q; -- struct sk_buff_head tx_write_q; --}; -- --/* Transmit states */ --#define HCI_TX_CTRL 1 --#define HCI_TX_WRITE 2 -- --#endif /* __KERNEL__ */ ---- linux/include/net/bluetooth/hci_vhci.h~bluetooth-2.4.18-mh11 -+++ linux/include/net/bluetooth/hci_vhci.h --/* -- BlueZ - Bluetooth protocol stack for Linux -- Copyright (C) 2000-2001 Qualcomm Incorporated -- -- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License version 2 as -- published by the Free Software Foundation; -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -- -- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -- SOFTWARE IS DISCLAIMED. --*/ -- --/* -- * $Id: hci_vhci.h,v 1.2 2001/08/01 01:02:20 maxk Exp $ -- */ -- --#ifndef __HCI_VHCI_H --#define __HCI_VHCI_H -- --#ifdef __KERNEL__ -- --struct hci_vhci_struct { -- struct hci_dev hdev; -- __u32 flags; -- wait_queue_head_t read_wait; -- struct sk_buff_head readq; -- struct fasync_struct *fasync; --}; -- --/* VHCI device flags */ --#define VHCI_FASYNC 0x0010 -- --#endif /* __KERNEL__ */ -- --#define VHCI_DEV "/dev/vhci" --#define VHCI_MINOR 250 -- --#endif /* __HCI_VHCI_H */ ---- linux/include/net/bluetooth/l2cap_core.h~bluetooth-2.4.18-mh11 -+++ linux/include/net/bluetooth/l2cap_core.h --/* -- BlueZ - Bluetooth protocol stack for Linux -- Copyright (C) 2000-2001 Qualcomm Incorporated -- -- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License version 2 as -- published by the Free Software Foundation; -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -- -- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -- SOFTWARE IS DISCLAIMED. --*/ -- --/* -- * $Id: l2cap_core.h,v 1.6 2001/08/03 04:19:49 maxk Exp $ -- */ -- --#ifndef __L2CAP_CORE_H --#define __L2CAP_CORE_H -- --#ifdef __KERNEL__ -- --/* ----- L2CAP interface ----- */ --struct l2cap_iff { -- struct list_head list; -- struct hci_dev *hdev; -- bdaddr_t *bdaddr; -- __u16 mtu; -- spinlock_t lock; -- struct list_head conn_list; --}; -- --static inline void l2cap_iff_lock(struct l2cap_iff *iff) --{ -- spin_lock(&iff->lock); --} -- --static inline void l2cap_iff_unlock(struct l2cap_iff *iff) --{ -- spin_unlock(&iff->lock); --} -- --/* ----- L2CAP connections ----- */ --struct l2cap_chan_list { -- struct sock *head; -- rwlock_t lock; -- long num; --}; -- --struct l2cap_conn { -- struct l2cap_iff *iff; -- struct list_head list; -- -- struct hci_conn *hconn; -- -- __u16 state; -- __u8 out; -- bdaddr_t src; -- bdaddr_t dst; -- -- spinlock_t lock; -- atomic_t refcnt; -- -- struct sk_buff *rx_skb; -- __u32 rx_len; -- __u8 rx_ident; -- __u8 tx_ident; -- -- struct l2cap_chan_list chan_list; -- -- struct timer_list timer; --}; -- --static inline void __l2cap_conn_link(struct l2cap_iff *iff, struct l2cap_conn *c) --{ -- list_add(&c->list, &iff->conn_list); --} -- --static inline void __l2cap_conn_unlink(struct l2cap_iff *iff, struct l2cap_conn *c) --{ -- list_del(&c->list); --} -- --/* ----- L2CAP channel and socket info ----- */ --#define l2cap_pi(sk) ((struct l2cap_pinfo *) &sk->protinfo) -- --struct l2cap_accept_q { -- struct sock *head; -- struct sock *tail; --}; -- --struct l2cap_pinfo { -- bdaddr_t src; -- bdaddr_t dst; -- __u16 psm; -- __u16 dcid; -- __u16 scid; -- __u32 flags; -- -- __u16 imtu; -- __u16 omtu; -- __u16 flush_to; -- -- __u8 conf_state; -- __u16 conf_mtu; -- -- __u8 ident; -- -- struct l2cap_conn *conn; -- struct sock *next_c; -- struct sock *prev_c; -- -- struct sock *parent; -- struct sock *next_q; -- struct sock *prev_q; -- -- struct l2cap_accept_q accept_q; --}; -- --#define CONF_REQ_SENT 0x01 --#define CONF_INPUT_DONE 0x02 --#define CONF_OUTPUT_DONE 0x04 -- --extern struct bluez_sock_list l2cap_sk_list; --extern struct list_head l2cap_iff_list; --extern rwlock_t l2cap_rt_lock; -- --extern void l2cap_register_proc(void); --extern void l2cap_unregister_proc(void); -- --#endif /* __KERNEL__ */ -- --#endif /* __L2CAP_CORE_H */ ---- linux/include/net/bluetooth/l2cap.h~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/include/net/bluetooth/l2cap.h 2004-01-25 23:37:39.000000000 +0100 -@@ -23,22 +23,17 @@ - */ - - /* -- * $Id: l2cap.h,v 1.5 2001/06/14 21:28:26 maxk Exp $ -+ * $Id: l2cap.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $ - */ - - #ifndef __L2CAP_H - #define __L2CAP_H - --#include <asm/types.h> --#include <asm/byteorder.h> -- - /* L2CAP defaults */ - #define L2CAP_DEFAULT_MTU 672 - #define L2CAP_DEFAULT_FLUSH_TO 0xFFFF - - #define L2CAP_CONN_TIMEOUT (HZ * 40) --#define L2CAP_DISCONN_TIMEOUT (HZ * 2) --#define L2CAP_CONN_IDLE_TIMEOUT (HZ * 60) - - /* L2CAP socket address */ - struct sockaddr_l2 { -@@ -47,17 +42,12 @@ - bdaddr_t l2_bdaddr; - }; - --/* set/get sockopt defines */ --#define L2CAP_OPTIONS 0x01 -+/* Socket options */ -+#define L2CAP_OPTIONS 0x01 - struct l2cap_options { - __u16 omtu; - __u16 imtu; - __u16 flush_to; -- __u32 token_rate; -- __u32 bucket_size; -- __u32 pick_band; -- __u32 latency; -- __u32 delay_var; - }; - - #define L2CAP_CONNINFO 0x02 -@@ -65,6 +55,27 @@ - __u16 hci_handle; - }; - -+#define L2CAP_LM 0x03 -+#define L2CAP_LM_MASTER 0x0001 -+#define L2CAP_LM_AUTH 0x0002 -+#define L2CAP_LM_ENCRYPT 0x0004 -+#define L2CAP_LM_TRUSTED 0x0008 -+#define L2CAP_LM_RELIABLE 0x0010 -+ -+#define L2CAP_QOS 0x04 -+struct l2cap_qos { -+ __u16 service_type; -+ __u32 token_rate; -+ __u32 token_bucket_size; -+ __u32 peak_bandwidth; -+ __u32 latency; -+ __u32 delay_variation; -+}; -+ -+#define L2CAP_SERV_NO_TRAFFIC 0x00 -+#define L2CAP_SERV_BEST_EFFORT 0x01 -+#define L2CAP_SERV_GUARANTEED 0x02 -+ - /* L2CAP command codes */ - #define L2CAP_COMMAND_REJ 0x01 - #define L2CAP_CONN_REQ 0x02 -@@ -79,7 +90,6 @@ - #define L2CAP_INFO_RSP 0x0b - - /* L2CAP structures */ -- - typedef struct { - __u16 len; - __u16 cid; -@@ -112,11 +122,17 @@ - } __attribute__ ((packed)) l2cap_conn_rsp; - #define L2CAP_CONN_RSP_SIZE 8 - --#define L2CAP_CONN_SUCCESS 0x0000 --#define L2CAP_CONN_PEND 0x0001 --#define L2CAP_CONN_BAD_PSM 0x0002 --#define L2CAP_CONN_SEC_BLOCK 0x0003 --#define L2CAP_CONN_NO_MEM 0x0004 -+/* connect result */ -+#define L2CAP_CR_SUCCESS 0x0000 -+#define L2CAP_CR_PEND 0x0001 -+#define L2CAP_CR_BAD_PSM 0x0002 -+#define L2CAP_CR_SEC_BLOCK 0x0003 -+#define L2CAP_CR_NO_MEM 0x0004 -+ -+/* connect status */ -+#define L2CAP_CS_NO_INFO 0x0000 -+#define L2CAP_CS_AUTHEN_PEND 0x0001 -+#define L2CAP_CS_AUTHOR_PEND 0x0002 - - typedef struct { - __u16 dcid; -@@ -147,6 +163,8 @@ - #define L2CAP_CONF_FLUSH_TO 0x02 - #define L2CAP_CONF_QOS 0x03 - -+#define L2CAP_CONF_MAX_SIZE 22 -+ - typedef struct { - __u16 dcid; - __u16 scid; -@@ -159,4 +177,74 @@ - } __attribute__ ((packed)) l2cap_disconn_rsp; - #define L2CAP_DISCONN_RSP_SIZE 4 - -+typedef struct { -+ __u16 type; -+ __u8 data[0]; -+} __attribute__ ((packed)) l2cap_info_req; -+#define L2CAP_INFO_REQ_SIZE 2 -+ -+typedef struct { -+ __u16 type; -+ __u16 result; -+ __u8 data[0]; -+} __attribute__ ((packed)) l2cap_info_rsp; -+#define L2CAP_INFO_RSP_SIZE 4 -+ -+/* ----- L2CAP connections ----- */ -+struct l2cap_chan_list { -+ struct sock *head; -+ rwlock_t lock; -+ long num; -+}; -+ -+struct l2cap_conn { -+ struct hci_conn *hcon; -+ -+ bdaddr_t *dst; -+ bdaddr_t *src; -+ -+ unsigned int mtu; -+ -+ spinlock_t lock; -+ -+ struct sk_buff *rx_skb; -+ __u32 rx_len; -+ __u8 rx_ident; -+ __u8 tx_ident; -+ -+ struct l2cap_chan_list chan_list; -+}; -+ -+/* ----- L2CAP channel and socket info ----- */ -+#define l2cap_pi(sk) ((struct l2cap_pinfo *) &sk->tp_pinfo) -+ -+struct l2cap_pinfo { -+ __u16 psm; -+ __u16 dcid; -+ __u16 scid; -+ -+ __u16 imtu; -+ __u16 omtu; -+ __u16 flush_to; -+ -+ __u32 link_mode; -+ -+ __u8 conf_state; -+ __u8 conf_retry; -+ __u16 conf_mtu; -+ -+ __u8 ident; -+ -+ struct l2cap_conn *conn; -+ struct sock *next_c; -+ struct sock *prev_c; -+}; -+ -+#define L2CAP_CONF_REQ_SENT 0x01 -+#define L2CAP_CONF_INPUT_DONE 0x02 -+#define L2CAP_CONF_OUTPUT_DONE 0x04 -+#define L2CAP_CONF_MAX_RETRIES 2 -+ -+void l2cap_load(void); -+ - #endif /* __L2CAP_H */ ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/include/net/bluetooth/rfcomm.h 2004-01-25 23:37:39.000000000 +0100 -@@ -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 */ ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/include/net/bluetooth/sco.h 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,81 @@ -+/* -+ BlueZ - Bluetooth protocol stack for Linux -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * $Id: sco.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $ -+ */ -+ -+#ifndef __SCO_H -+#define __SCO_H -+ -+/* SCO defaults */ -+#define SCO_DEFAULT_MTU 500 -+#define SCO_DEFAULT_FLUSH_TO 0xFFFF -+ -+#define SCO_CONN_TIMEOUT (HZ * 40) -+#define SCO_DISCONN_TIMEOUT (HZ * 2) -+#define SCO_CONN_IDLE_TIMEOUT (HZ * 60) -+ -+/* SCO socket address */ -+struct sockaddr_sco { -+ sa_family_t sco_family; -+ bdaddr_t sco_bdaddr; -+}; -+ -+/* set/get sockopt defines */ -+#define SCO_OPTIONS 0x01 -+struct sco_options { -+ __u16 mtu; -+}; -+ -+#define SCO_CONNINFO 0x02 -+struct sco_conninfo { -+ __u16 hci_handle; -+}; -+ -+/* ---- SCO connections ---- */ -+struct sco_conn { -+ struct hci_conn *hcon; -+ -+ bdaddr_t *dst; -+ bdaddr_t *src; -+ -+ spinlock_t lock; -+ struct sock *sk; -+ -+ unsigned int mtu; -+}; -+ -+#define sco_conn_lock(c) spin_lock(&c->lock); -+#define sco_conn_unlock(c) spin_unlock(&c->lock); -+ -+/* ----- SCO socket info ----- */ -+#define sco_pi(sk) ((struct sco_pinfo *) &sk->tp_pinfo) -+ -+struct sco_pinfo { -+ __u32 flags; -+ struct sco_conn *conn; -+}; -+ -+#endif /* __SCO_H */ ---- linux/include/pcmcia/ciscode.h~bluetooth-2.4.18-mh11 2001-12-21 18:42:04.000000000 +0100 -+++ linux/include/pcmcia/ciscode.h 2004-01-25 23:37:39.000000000 +0100 -@@ -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 */ ---- linux/kernel/ksyms.c~bluetooth-2.4.18-mh11 2004-01-25 23:28:30.000000000 +0100 -+++ linux/kernel/ksyms.c 2004-01-25 23:37:39.000000000 +0100 -@@ -47,6 +47,7 @@ - #include <linux/in6.h> - #include <linux/completion.h> - #include <linux/seq_file.h> -+#include <linux/firmware.h> - #include <asm/checksum.h> - - #if defined(CONFIG_PROC_FS) -@@ -543,6 +544,13 @@ - EXPORT_SYMBOL(strspn); - EXPORT_SYMBOL(strsep); - -+#ifdef CONFIG_FW_LOADER -+EXPORT_SYMBOL(release_firmware); -+EXPORT_SYMBOL(request_firmware); -+EXPORT_SYMBOL(request_firmware_nowait); -+EXPORT_SYMBOL(register_firmware); -+#endif -+ - /* software interrupts */ - EXPORT_SYMBOL(tasklet_hi_vec); - EXPORT_SYMBOL(tasklet_vec); ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/lib/Config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,12 @@ -+# -+# Library configuration -+# -+mainmenu_option next_comment -+comment 'Library routines' -+ -+if [ "$CONFIG_EXPERIMENTAL" = "y" -a \ -+ "$CONFIG_HOTPLUG" = "y" ]; then -+ tristate 'Hotplug firmware loading support (EXPERIMENTAL)' CONFIG_FW_LOADER -+fi -+ -+endmenu ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/lib/firmware_class.c 2004-01-25 23:37:39.000000000 +0100 -@@ -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 ---- linux/lib/Makefile~bluetooth-2.4.18-mh11 2001-09-18 00:31:15.000000000 +0200 -+++ linux/lib/Makefile 2004-01-25 23:37:39.000000000 +0100 -@@ -8,13 +8,17 @@ - - L_TARGET := lib.a - --export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o -+export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \ -+ firmware_class.o - - obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o bust_spinlocks.o rbtree.o - -+obj-$(CONFIG_FW_LOADER) += firmware_class.o - obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o - obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o - -+include $(TOPDIR)/drivers/bluetooth/Makefile.lib -+ - ifneq ($(CONFIG_HAVE_DEC_LOCK),y) - obj-y += dec_and_lock.o - endif ---- linux/MAINTAINERS~bluetooth-2.4.18-mh11 2004-01-25 23:28:29.000000000 +0100 -+++ linux/MAINTAINERS 2004-01-25 23:37:39.000000000 +0100 -@@ -259,7 +259,84 @@ - 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 -+S: Maintained -+ -+BLUETOOTH RFCOMM LAYER -+P: Marcel Holtmann -+M: marcel@holtmann.org -+P: Maxim Krasnyansky -+M: maxk@qualcomm.com -+W: http://bluez.sf.net -+S: Maintained -+ -+BLUETOOTH BNEP LAYER -+P: Marcel Holtmann -+M: marcel@holtmann.org -+P: Maxim Krasnyansky -+M: maxk@qualcomm.com -+W: http://bluez.sf.net -+S: Maintained -+ -+BLUETOOTH CMTP LAYER -+P: Marcel Holtmann -+M: marcel@holtmann.org -+W: http://www.holtmann.org/linux/bluetooth/ -+S: Maintained -+ -+BLUETOOTH HCI USB DRIVER -+P: Marcel Holtmann -+M: marcel@holtmann.org -+P: Maxim Krasnyansky -+M: maxk@qualcomm.com -+W: http://bluez.sf.net -+S: Maintained -+ -+BLUETOOTH HCI UART DRIVER -+P: Marcel Holtmann -+M: marcel@holtmann.org -+P: Maxim Krasnyansky -+M: maxk@qualcomm.com -+W: http://bluez.sf.net -+S: Maintained -+ -+BLUETOOTH HCI BFUSB DRIVER -+P: Marcel Holtmann -+M: marcel@holtmann.org -+W: http://www.holtmann.org/linux/bluetooth/ -+S: Maintained -+ -+BLUETOOTH HCI DTL1 DRIVER -+P: Marcel Holtmann -+M: marcel@holtmann.org -+W: http://www.holtmann.org/linux/bluetooth/ -+S: Maintained -+ -+BLUETOOTH HCI BLUECARD DRIVER -+P: Marcel Holtmann -+M: marcel@holtmann.org -+W: http://www.holtmann.org/linux/bluetooth/ -+S: Maintained -+ -+BLUETOOTH HCI BT3C DRIVER -+P: Marcel Holtmann -+M: marcel@holtmann.org -+W: http://www.holtmann.org/linux/bluetooth/ -+S: Maintained -+ -+BLUETOOTH HCI BTUART DRIVER -+P: Marcel Holtmann -+M: marcel@holtmann.org -+W: http://www.holtmann.org/linux/bluetooth/ -+S: Maintained -+ -+BLUETOOTH HCI VHCI DRIVER - P: Maxim Krasnyansky - M: maxk@qualcomm.com - W: http://bluez.sf.net ---- linux/net/bluetooth/af_bluetooth.c~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/net/bluetooth/af_bluetooth.c 2004-01-25 23:37:39.000000000 +0100 -@@ -25,14 +25,15 @@ - /* - * BlueZ Bluetooth address family and sockets. - * -- * $Id: af_bluetooth.c,v 1.4 2001/07/05 18:42:44 maxk Exp $ -+ * $Id: af_bluetooth.c,v 1.8 2002/07/22 20:32:54 maxk Exp $ - */ --#define VERSION "1.1" -+#define VERSION "2.3" - - #include <linux/config.h> - #include <linux/module.h> - - #include <linux/types.h> -+#include <linux/list.h> - #include <linux/errno.h> - #include <linux/kernel.h> - #include <linux/major.h> -@@ -40,6 +41,7 @@ - #include <linux/slab.h> - #include <linux/skbuff.h> - #include <linux/init.h> -+#include <linux/poll.h> - #include <linux/proc_fs.h> - #include <net/sock.h> - -@@ -48,70 +50,79 @@ - #endif - - #include <net/bluetooth/bluetooth.h> --#include <net/bluetooth/bluez.h> -+ -+#ifndef AF_BLUETOOTH_DEBUG -+#undef BT_DBG -+#define BT_DBG( A... ) -+#endif - - /* Bluetooth sockets */ --static struct net_proto_family *bluez_sock[BLUEZ_MAX_PROTO]; -+#define BLUEZ_MAX_PROTO 6 -+static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO]; - - int bluez_sock_register(int proto, struct net_proto_family *ops) - { -- if (proto > BLUEZ_MAX_PROTO) -+ if (proto >= BLUEZ_MAX_PROTO) - return -EINVAL; - -- if (bluez_sock[proto]) -+ if (bluez_proto[proto]) - return -EEXIST; - -- bluez_sock[proto] = ops; -+ bluez_proto[proto] = ops; - return 0; - } - - int bluez_sock_unregister(int proto) - { -- if (proto > BLUEZ_MAX_PROTO) -+ if (proto >= BLUEZ_MAX_PROTO) - return -EINVAL; - -- if (!bluez_sock[proto]) -+ if (!bluez_proto[proto]) - return -ENOENT; - -- bluez_sock[proto] = NULL; -+ bluez_proto[proto] = NULL; - return 0; - } - - static int bluez_sock_create(struct socket *sock, int proto) - { -- if (proto > BLUEZ_MAX_PROTO) -+ if (proto >= BLUEZ_MAX_PROTO) - return -EINVAL; - - #if defined(CONFIG_KMOD) -- if (!bluez_sock[proto]) { -+ if (!bluez_proto[proto]) { - char module_name[30]; - sprintf(module_name, "bt-proto-%d", proto); - request_module(module_name); - } - #endif - -- if (!bluez_sock[proto]) -+ if (!bluez_proto[proto]) - return -ENOENT; - -- return bluez_sock[proto]->create(sock, proto); -+ return bluez_proto[proto]->create(sock, proto); -+} -+ -+void bluez_sock_init(struct socket *sock, struct sock *sk) -+{ -+ sock_init_data(sock, sk); -+ INIT_LIST_HEAD(&bluez_pi(sk)->accept_q); - } - - void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk) - { -- write_lock(&l->lock); -- -+ write_lock_bh(&l->lock); - sk->next = l->head; - l->head = sk; - sock_hold(sk); -- -- write_unlock(&l->lock); -+ write_unlock_bh(&l->lock); - } - - void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk) - { - struct sock **skp; - -- write_lock(&l->lock); -+ write_lock_bh(&l->lock); - for (skp = &l->head; *skp; skp = &((*skp)->next)) { - if (*skp == sk) { - *skp = sk->next; -@@ -119,7 +130,162 @@ - break; - } - } -- write_unlock(&l->lock); -+ write_unlock_bh(&l->lock); -+} -+ -+void bluez_accept_enqueue(struct sock *parent, struct sock *sk) -+{ -+ BT_DBG("parent %p, sk %p", parent, sk); -+ -+ sock_hold(sk); -+ list_add_tail(&bluez_pi(sk)->accept_q, &bluez_pi(parent)->accept_q); -+ bluez_pi(sk)->parent = parent; -+ parent->ack_backlog++; -+} -+ -+static void bluez_accept_unlink(struct sock *sk) -+{ -+ BT_DBG("sk %p state %d", sk, sk->state); -+ -+ list_del_init(&bluez_pi(sk)->accept_q); -+ bluez_pi(sk)->parent->ack_backlog--; -+ bluez_pi(sk)->parent = NULL; -+ sock_put(sk); -+} -+ -+struct sock *bluez_accept_dequeue(struct sock *parent, struct socket *newsock) -+{ -+ struct list_head *p, *n; -+ struct bluez_pinfo *pi; -+ struct sock *sk; -+ -+ BT_DBG("parent %p", parent); -+ -+ list_for_each_safe(p, n, &bluez_pi(parent)->accept_q) { -+ pi = list_entry(p, struct bluez_pinfo, accept_q); -+ sk = bluez_sk(pi); -+ -+ lock_sock(sk); -+ if (sk->state == BT_CLOSED) { -+ release_sock(sk); -+ bluez_accept_unlink(sk); -+ continue; -+ } -+ -+ if (sk->state == BT_CONNECTED || !newsock) { -+ bluez_accept_unlink(sk); -+ if (newsock) -+ sock_graft(sk, newsock); -+ release_sock(sk); -+ return sk; -+ } -+ release_sock(sk); -+ } -+ return NULL; -+} -+ -+int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm) -+{ -+ int noblock = flags & MSG_DONTWAIT; -+ struct sock *sk = sock->sk; -+ struct sk_buff *skb; -+ int copied, err; -+ -+ BT_DBG("sock %p sk %p len %d", sock, sk, len); -+ -+ if (flags & (MSG_OOB)) -+ return -EOPNOTSUPP; -+ -+ if (!(skb = skb_recv_datagram(sk, flags, noblock, &err))) { -+ if (sk->shutdown & RCV_SHUTDOWN) -+ return 0; -+ return err; -+ } -+ -+ msg->msg_namelen = 0; -+ -+ copied = skb->len; -+ if (len < copied) { -+ msg->msg_flags |= MSG_TRUNC; -+ copied = len; -+ } -+ -+ skb->h.raw = skb->data; -+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); -+ -+ skb_free_datagram(sk, skb); -+ -+ return err ? : copied; -+} -+ -+unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait) -+{ -+ struct sock *sk = sock->sk; -+ unsigned int mask; -+ -+ BT_DBG("sock %p, sk %p", sock, sk); -+ -+ poll_wait(file, sk->sleep, wait); -+ mask = 0; -+ -+ if (sk->err || !skb_queue_empty(&sk->error_queue)) -+ mask |= POLLERR; -+ -+ if (sk->shutdown == SHUTDOWN_MASK) -+ mask |= POLLHUP; -+ -+ if (!skb_queue_empty(&sk->receive_queue) || -+ !list_empty(&bluez_pi(sk)->accept_q) || -+ (sk->shutdown & RCV_SHUTDOWN)) -+ mask |= POLLIN | POLLRDNORM; -+ -+ if (sk->state == BT_CLOSED) -+ mask |= POLLHUP; -+ -+ if (sk->state == BT_CONNECT || sk->state == BT_CONNECT2) -+ return mask; -+ -+ if (sock_writeable(sk)) -+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND; -+ else -+ set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags); -+ -+ return mask; -+} -+ -+int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo) -+{ -+ DECLARE_WAITQUEUE(wait, current); -+ int err = 0; -+ -+ BT_DBG("sk %p", sk); -+ -+ add_wait_queue(sk->sleep, &wait); -+ while (sk->state != state) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ -+ if (!timeo) { -+ err = -EAGAIN; -+ break; -+ } -+ -+ if (signal_pending(current)) { -+ err = sock_intr_errno(timeo); -+ break; -+ } -+ -+ release_sock(sk); -+ timeo = schedule_timeout(timeo); -+ lock_sock(sk); -+ -+ if (sk->err) { -+ err = sock_error(sk); -+ break; -+ } -+ } -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(sk->sleep, &wait); -+ return err; - } - - struct net_proto_family bluez_sock_family_ops = -@@ -129,9 +295,9 @@ - - int bluez_init(void) - { -- INF("BlueZ HCI Core ver %s Copyright (C) 2000,2001 Qualcomm Inc", -+ BT_INFO("BlueZ Core ver %s Copyright (C) 2000,2001 Qualcomm Inc", - VERSION); -- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); -+ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); - - proc_mkdir("bluetooth", NULL); - -@@ -164,5 +330,6 @@ - module_exit(bluez_cleanup); - - MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); --MODULE_DESCRIPTION("BlueZ HCI Core ver " VERSION); -+MODULE_DESCRIPTION("BlueZ Core ver " VERSION); -+MODULE_LICENSE("GPL"); - #endif ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/bnep/bnep.h 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,185 @@ -+/* -+ BNEP protocol definition for Linux Bluetooth stack (BlueZ). -+ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License, version 2, as -+ published by the Free Software Foundation. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program; if not, write to the Free Software -+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+*/ -+ -+/* -+ * $Id: bnep2.h,v 1.9 2002/07/14 07:09:19 maxk Exp $ -+ */ -+ -+#ifndef _BNEP_H -+#define _BNEP_H -+ -+#include <linux/types.h> -+#include <net/bluetooth/bluetooth.h> -+ -+#include "crc32.h" -+ -+// Limits -+#define BNEP_MAX_PROTO_FILTERS 5 -+#define BNEP_MAX_MULTICAST_FILTERS 20 -+ -+// UUIDs -+#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB -+#define BNEP_UUID16 0x02 -+#define BNEP_UUID32 0x04 -+#define BNEP_UUID128 0x16 -+ -+#define BNEP_SVC_PANU 0x1115 -+#define BNEP_SVC_NAP 0x1116 -+#define BNEP_SVC_GN 0x1117 -+ -+// Packet types -+#define BNEP_GENERAL 0x00 -+#define BNEP_CONTROL 0x01 -+#define BNEP_COMPRESSED 0x02 -+#define BNEP_COMPRESSED_SRC_ONLY 0x03 -+#define BNEP_COMPRESSED_DST_ONLY 0x04 -+ -+// Control types -+#define BNEP_CMD_NOT_UNDERSTOOD 0x00 -+#define BNEP_SETUP_CONN_REQ 0x01 -+#define BNEP_SETUP_CONN_RSP 0x02 -+#define BNEP_FILTER_NET_TYPE_SET 0x03 -+#define BNEP_FILTER_NET_TYPE_RSP 0x04 -+#define BNEP_FILTER_MULTI_ADDR_SET 0x05 -+#define BNEP_FILTER_MULTI_ADDR_RSP 0x06 -+ -+// Extension types -+#define BNEP_EXT_CONTROL 0x00 -+ -+// Response messages -+#define BNEP_SUCCESS 0x00 -+ -+#define BNEP_CONN_INVALID_DST 0x01 -+#define BNEP_CONN_INVALID_SRC 0x02 -+#define BNEP_CONN_INVALID_SVC 0x03 -+#define BNEP_CONN_NOT_ALLOWED 0x04 -+ -+#define BNEP_FILTER_UNSUPPORTED_REQ 0x01 -+#define BNEP_FILTER_INVALID_RANGE 0x02 -+#define BNEP_FILTER_INVALID_MCADDR 0x02 -+#define BNEP_FILTER_LIMIT_REACHED 0x03 -+#define BNEP_FILTER_DENIED_SECURITY 0x04 -+ -+// L2CAP settings -+#define BNEP_MTU 1691 -+#define BNEP_PSM 0x0f -+#define BNEP_FLUSH_TO 0xffff -+#define BNEP_CONNECT_TO 15 -+#define BNEP_FILTER_TO 15 -+ -+// Headers -+#define BNEP_TYPE_MASK 0x7f -+#define BNEP_EXT_HEADER 0x80 -+ -+struct bnep_setup_conn_req { -+ __u8 type; -+ __u8 ctrl; -+ __u8 uuid_size; -+ __u8 service[0]; -+} __attribute__((packed)); -+ -+struct bnep_set_filter_req { -+ __u8 type; -+ __u8 ctrl; -+ __u16 len; -+ __u8 list[0]; -+} __attribute__((packed)); -+ -+struct bnep_control_rsp { -+ __u8 type; -+ __u8 ctrl; -+ __u16 resp; -+} __attribute__((packed)); -+ -+struct bnep_ext_hdr { -+ __u8 type; -+ __u8 len; -+ __u8 data[0]; -+} __attribute__((packed)); -+ -+/* BNEP ioctl defines */ -+#define BNEPCONNADD _IOW('B', 200, int) -+#define BNEPCONNDEL _IOW('B', 201, int) -+#define BNEPGETCONNLIST _IOR('B', 210, int) -+#define BNEPGETCONNINFO _IOR('B', 211, int) -+ -+struct bnep_connadd_req { -+ int sock; // Connected socket -+ __u32 flags; -+ __u16 role; -+ char device[16]; // Name of the Ethernet device -+}; -+ -+struct bnep_conndel_req { -+ __u32 flags; -+ __u8 dst[ETH_ALEN]; -+}; -+ -+struct bnep_conninfo { -+ __u32 flags; -+ __u16 role; -+ __u16 state; -+ __u8 dst[ETH_ALEN]; -+ char device[16]; -+}; -+ -+struct bnep_connlist_req { -+ __u32 cnum; -+ struct bnep_conninfo *ci; -+}; -+ -+struct bnep_proto_filter { -+ __u16 start; -+ __u16 end; -+}; -+ -+int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock); -+int bnep_del_connection(struct bnep_conndel_req *req); -+int bnep_get_connlist(struct bnep_connlist_req *req); -+int bnep_get_conninfo(struct bnep_conninfo *ci); -+ -+// BNEP sessions -+struct bnep_session { -+ struct list_head list; -+ -+ unsigned int role; -+ unsigned long state; -+ unsigned long flags; -+ atomic_t killed; -+ -+ struct ethhdr eh; -+ struct msghdr msg; -+ -+ struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS]; -+ u64 mc_filter; -+ -+ struct socket *sock; -+ struct net_device dev; -+ struct net_device_stats stats; -+}; -+ -+int bnep_net_init(struct net_device *dev); -+int bnep_sock_init(void); -+int bnep_sock_cleanup(void); -+ -+static inline int bnep_mc_hash(__u8 *addr) -+{ -+ return (bnep_crc32(~0, addr, ETH_ALEN) >> 26); -+} -+ -+#endif ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/bnep/Config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,11 @@ -+# -+# Bluetooth BNEP layer configuration -+# -+ -+dep_tristate 'BNEP protocol support' CONFIG_BLUEZ_BNEP $CONFIG_BLUEZ_L2CAP -+ -+if [ "$CONFIG_BLUEZ_BNEP" != "n" ]; then -+ bool ' Multicast filter support' CONFIG_BLUEZ_BNEP_MC_FILTER -+ bool ' Protocol filter support' CONFIG_BLUEZ_BNEP_PROTO_FILTER -+fi -+ ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/bnep/core.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,708 @@ -+/* -+ BNEP implementation for Linux Bluetooth stack (BlueZ). -+ Copyright (C) 2001-2002 Inventel Systemes -+ Written 2001-2002 by -+ Clément Moreau <clement.moreau@inventel.fr> -+ David Libault <david.libault@inventel.fr> -+ -+ Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * $Id: core.c,v 1.18 2002/07/14 07:09:19 maxk Exp $ -+ */ -+ -+#define __KERNEL_SYSCALLS__ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/kernel.h> -+#include <linux/sched.h> -+#include <linux/signal.h> -+#include <linux/init.h> -+#include <linux/wait.h> -+#include <linux/errno.h> -+#include <linux/smp_lock.h> -+#include <linux/net.h> -+#include <net/sock.h> -+ -+#include <linux/socket.h> -+#include <linux/file.h> -+ -+#include <linux/netdevice.h> -+#include <linux/etherdevice.h> -+#include <linux/skbuff.h> -+ -+#include <asm/unaligned.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/l2cap.h> -+ -+#include "bnep.h" -+ -+#ifndef CONFIG_BLUEZ_BNEP_DEBUG -+#undef BT_DBG -+#define BT_DBG(D...) -+#endif -+ -+#define VERSION "1.1" -+ -+static LIST_HEAD(bnep_session_list); -+static DECLARE_RWSEM(bnep_session_sem); -+ -+static struct bnep_session *__bnep_get_session(u8 *dst) -+{ -+ struct bnep_session *s; -+ struct list_head *p; -+ -+ BT_DBG(""); -+ -+ list_for_each(p, &bnep_session_list) { -+ s = list_entry(p, struct bnep_session, list); -+ if (!memcmp(dst, s->eh.h_source, ETH_ALEN)) -+ return s; -+ } -+ return NULL; -+} -+ -+static void __bnep_link_session(struct bnep_session *s) -+{ -+ MOD_INC_USE_COUNT; -+ list_add(&s->list, &bnep_session_list); -+} -+ -+static void __bnep_unlink_session(struct bnep_session *s) -+{ -+ list_del(&s->list); -+ MOD_DEC_USE_COUNT; -+} -+ -+static int bnep_send(struct bnep_session *s, void *data, size_t len) -+{ -+ struct socket *sock = s->sock; -+ struct iovec iv = { data, len }; -+ s->msg.msg_iov = &iv; -+ s->msg.msg_iovlen = 1; -+ return sock->ops->sendmsg(sock, &s->msg, len, NULL); -+} -+ -+static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp) -+{ -+ struct bnep_control_rsp rsp; -+ rsp.type = BNEP_CONTROL; -+ rsp.ctrl = ctrl; -+ rsp.resp = htons(resp); -+ return bnep_send(s, &rsp, sizeof(rsp)); -+} -+ -+static int bnep_ctrl_set_netfilter(struct bnep_session *s, u16 *data, int len) -+{ -+ int n; -+ -+ if (len < 2) -+ return -EILSEQ; -+ -+ n = ntohs(get_unaligned(data)); -+ data++; len -= 2; -+ -+ if (len < n) -+ return -EILSEQ; -+ -+ BT_DBG("filter len %d", n); -+ -+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER -+ n /= 4; -+ if (n <= BNEP_MAX_PROTO_FILTERS) { -+ struct bnep_proto_filter *f = s->proto_filter; -+ int i; -+ -+ for (i = 0; i < n; i++) { -+ f[i].start = get_unaligned(data++); -+ f[i].end = get_unaligned(data++); -+ -+ BT_DBG("proto filter start %d end %d", -+ f[i].start, f[i].end); -+ } -+ if (i < BNEP_MAX_PROTO_FILTERS) -+ memset(f + i, 0, sizeof(*f)); -+ -+ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS); -+ } else { -+ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED); -+ } -+#else -+ bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ); -+#endif -+ return 0; -+} -+ -+static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len) -+{ -+ int n; -+ -+ if (len < 2) -+ return -EILSEQ; -+ -+ n = ntohs(get_unaligned((u16 *) data)); -+ data += 2; len -= 2; -+ -+ if (len < n) -+ return -EILSEQ; -+ -+ BT_DBG("filter len %d", n); -+ -+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER -+ n /= (ETH_ALEN * 2); -+ -+ if (n > 0) { -+ s->mc_filter = 0; -+ -+ /* Always send broadcast */ -+ set_bit(bnep_mc_hash(s->dev.broadcast), &s->mc_filter); -+ -+ /* Add address ranges to the multicast hash */ -+ for (; n > 0; n--) { -+ u8 a1[6], *a2; -+ -+ memcpy(a1, data, ETH_ALEN); data += ETH_ALEN; -+ a2 = data; data += ETH_ALEN; -+ -+ BT_DBG("mc filter %s -> %s", -+ batostr((void *) a1), batostr((void *) a2)); -+ -+ #define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); } -+ -+ /* Iterate from a1 to a2 */ -+ set_bit(bnep_mc_hash(a1), &s->mc_filter); -+ while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) { -+ INCA(a1); -+ set_bit(bnep_mc_hash(a1), &s->mc_filter); -+ } -+ } -+ } -+ -+ BT_DBG("mc filter hash 0x%llx", s->mc_filter); -+ -+ bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS); -+#else -+ bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ); -+#endif -+ return 0; -+} -+ -+static int bnep_rx_control(struct bnep_session *s, void *data, int len) -+{ -+ u8 cmd = *(u8 *)data; -+ int err = 0; -+ -+ data++; len--; -+ -+ switch (cmd) { -+ case BNEP_CMD_NOT_UNDERSTOOD: -+ case BNEP_SETUP_CONN_REQ: -+ case BNEP_SETUP_CONN_RSP: -+ case BNEP_FILTER_NET_TYPE_RSP: -+ case BNEP_FILTER_MULTI_ADDR_RSP: -+ /* Ignore these for now */ -+ break; -+ -+ case BNEP_FILTER_NET_TYPE_SET: -+ err = bnep_ctrl_set_netfilter(s, data, len); -+ break; -+ -+ case BNEP_FILTER_MULTI_ADDR_SET: -+ err = bnep_ctrl_set_mcfilter(s, data, len); -+ break; -+ -+ default: { -+ u8 pkt[3]; -+ pkt[0] = BNEP_CONTROL; -+ pkt[1] = BNEP_CMD_NOT_UNDERSTOOD; -+ pkt[2] = cmd; -+ bnep_send(s, pkt, sizeof(pkt)); -+ } -+ break; -+ } -+ -+ return err; -+} -+ -+static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb) -+{ -+ struct bnep_ext_hdr *h; -+ int err = 0; -+ -+ do { -+ h = (void *) skb->data; -+ if (!skb_pull(skb, sizeof(*h))) { -+ err = -EILSEQ; -+ break; -+ } -+ -+ BT_DBG("type 0x%x len %d", h->type, h->len); -+ -+ switch (h->type & BNEP_TYPE_MASK) { -+ case BNEP_EXT_CONTROL: -+ bnep_rx_control(s, skb->data, skb->len); -+ break; -+ -+ default: -+ /* Unknown extension, skip it. */ -+ break; -+ } -+ -+ if (!skb_pull(skb, h->len)) { -+ err = -EILSEQ; -+ break; -+ } -+ } while (!err && (h->type & BNEP_EXT_HEADER)); -+ -+ return err; -+} -+ -+static u8 __bnep_rx_hlen[] = { -+ ETH_HLEN, /* BNEP_GENERAL */ -+ 0, /* BNEP_CONTROL */ -+ 2, /* BNEP_COMPRESSED */ -+ ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */ -+ ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */ -+}; -+#define BNEP_RX_TYPES (sizeof(__bnep_rx_hlen) - 1) -+ -+static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) -+{ -+ struct net_device *dev = &s->dev; -+ struct sk_buff *nskb; -+ u8 type; -+ -+ dev->last_rx = jiffies; -+ s->stats.rx_bytes += skb->len; -+ -+ type = *(u8 *) skb->data; skb_pull(skb, 1); -+ -+ if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES) -+ goto badframe; -+ -+ if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) { -+ bnep_rx_control(s, skb->data, skb->len); -+ kfree_skb(skb); -+ return 0; -+ } -+ -+ skb->mac.raw = skb->data; -+ -+ /* Verify and pull out header */ -+ if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK])) -+ goto badframe; -+ -+ s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2)); -+ -+ if (type & BNEP_EXT_HEADER) { -+ if (bnep_rx_extension(s, skb) < 0) -+ goto badframe; -+ } -+ -+ /* Strip 802.1p header */ -+ if (ntohs(s->eh.h_proto) == 0x8100) { -+ if (!skb_pull(skb, 4)) -+ goto badframe; -+ s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2)); -+ } -+ -+ /* We have to alloc new skb and copy data here :(. Because original skb -+ * may not be modified and because of the alignment requirements. */ -+ nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL); -+ if (!nskb) { -+ s->stats.rx_dropped++; -+ kfree_skb(skb); -+ return -ENOMEM; -+ } -+ skb_reserve(nskb, 2); -+ -+ /* Decompress header and construct ether frame */ -+ switch (type & BNEP_TYPE_MASK) { -+ case BNEP_COMPRESSED: -+ memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN); -+ break; -+ -+ case BNEP_COMPRESSED_SRC_ONLY: -+ memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN); -+ memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN); -+ put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2)); -+ break; -+ -+ case BNEP_COMPRESSED_DST_ONLY: -+ memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN); -+ memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, ETH_ALEN + 2); -+ break; -+ -+ case BNEP_GENERAL: -+ memcpy(__skb_put(nskb, ETH_ALEN * 2), skb->mac.raw, ETH_ALEN * 2); -+ put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2)); -+ break; -+ } -+ -+ memcpy(__skb_put(nskb, skb->len), skb->data, skb->len); -+ kfree_skb(skb); -+ -+ s->stats.rx_packets++; -+ nskb->dev = dev; -+ nskb->ip_summed = CHECKSUM_UNNECESSARY; -+ nskb->protocol = eth_type_trans(nskb, dev); -+ netif_rx_ni(nskb); -+ return 0; -+ -+badframe: -+ s->stats.rx_errors++; -+ kfree_skb(skb); -+ return 0; -+} -+ -+static u8 __bnep_tx_types[] = { -+ BNEP_GENERAL, -+ BNEP_COMPRESSED_SRC_ONLY, -+ BNEP_COMPRESSED_DST_ONLY, -+ BNEP_COMPRESSED -+}; -+ -+static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb) -+{ -+ struct ethhdr *eh = (void *) skb->data; -+ struct socket *sock = s->sock; -+ struct iovec iv[3]; -+ int len = 0, il = 0; -+ u8 type = 0; -+ -+ BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type); -+ -+ if (!skb->dev) { -+ /* Control frame sent by us */ -+ goto send; -+ } -+ -+ iv[il++] = (struct iovec) { &type, 1 }; -+ len++; -+ -+ if (!memcmp(eh->h_dest, s->eh.h_source, ETH_ALEN)) -+ type |= 0x01; -+ -+ if (!memcmp(eh->h_source, s->eh.h_dest, ETH_ALEN)) -+ type |= 0x02; -+ -+ if (type) -+ skb_pull(skb, ETH_ALEN * 2); -+ -+ type = __bnep_tx_types[type]; -+ switch (type) { -+ case BNEP_COMPRESSED_SRC_ONLY: -+ iv[il++] = (struct iovec) { eh->h_source, ETH_ALEN }; -+ len += ETH_ALEN; -+ break; -+ -+ case BNEP_COMPRESSED_DST_ONLY: -+ iv[il++] = (struct iovec) { eh->h_dest, ETH_ALEN }; -+ len += ETH_ALEN; -+ break; -+ } -+ -+send: -+ iv[il++] = (struct iovec) { skb->data, skb->len }; -+ len += skb->len; -+ -+ /* FIXME: linearize skb */ -+ -+ s->msg.msg_iov = iv; -+ s->msg.msg_iovlen = il; -+ len = sock->ops->sendmsg(sock, &s->msg, len, NULL); -+ kfree_skb(skb); -+ -+ if (len > 0) { -+ s->stats.tx_bytes += len; -+ s->stats.tx_packets++; -+ return 0; -+ } -+ -+ return len; -+} -+ -+static int bnep_session(void *arg) -+{ -+ struct bnep_session *s = arg; -+ struct net_device *dev = &s->dev; -+ struct sock *sk = s->sock->sk; -+ struct sk_buff *skb; -+ wait_queue_t wait; -+ -+ BT_DBG(""); -+ -+ daemonize(); reparent_to_init(); -+ -+ sprintf(current->comm, "kbnepd %s", dev->name); -+ -+ sigfillset(¤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(&s->killed)) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ -+ // RX -+ while ((skb = skb_dequeue(&sk->receive_queue))) { -+ skb_orphan(skb); -+ bnep_rx_frame(s, skb); -+ } -+ -+ if (sk->state != BT_CONNECTED) -+ break; -+ -+ // TX -+ while ((skb = skb_dequeue(&sk->write_queue))) -+ if (bnep_tx_frame(s, skb)) -+ break; -+ netif_wake_queue(dev); -+ -+ schedule(); -+ } -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(sk->sleep, &wait); -+ -+ /* Cleanup session */ -+ down_write(&bnep_session_sem); -+ -+ /* Delete network device */ -+ unregister_netdev(dev); -+ -+ /* Release the socket */ -+ fput(s->sock->file); -+ -+ __bnep_unlink_session(s); -+ -+ up_write(&bnep_session_sem); -+ kfree(s); -+ return 0; -+} -+ -+int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) -+{ -+ struct net_device *dev; -+ struct bnep_session *s, *ss; -+ u8 dst[ETH_ALEN], src[ETH_ALEN]; -+ int err; -+ -+ BT_DBG(""); -+ -+ baswap((void *) dst, &bluez_pi(sock->sk)->dst); -+ baswap((void *) src, &bluez_pi(sock->sk)->src); -+ -+ s = kmalloc(sizeof(struct bnep_session), GFP_KERNEL); -+ if (!s) -+ return -ENOMEM; -+ memset(s, 0, sizeof(struct bnep_session)); -+ -+ down_write(&bnep_session_sem); -+ -+ ss = __bnep_get_session(dst); -+ if (ss && ss->state == BT_CONNECTED) { -+ err = -EEXIST; -+ goto failed; -+ } -+ -+ dev = &s->dev; -+ -+ if (*req->device) -+ strcpy(dev->name, req->device); -+ else -+ strcpy(dev->name, "bnep%d"); -+ -+ memset(dev->broadcast, 0xff, ETH_ALEN); -+ -+ /* This is rx header therefor addresses are swaped. -+ * ie eh.h_dest is our local address. */ -+ memcpy(s->eh.h_dest, &src, ETH_ALEN); -+ memcpy(s->eh.h_source, &dst, ETH_ALEN); -+ -+ s->sock = sock; -+ s->role = req->role; -+ s->state = BT_CONNECTED; -+ -+ s->msg.msg_flags = MSG_NOSIGNAL; -+ -+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER -+ /* Set default mc filter */ -+ set_bit(bnep_mc_hash(dev->broadcast), &s->mc_filter); -+#endif -+ -+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER -+ /* Set default protocol filter */ -+ -+ /* (IPv4, ARP) */ -+ s->proto_filter[0].start = htons(0x0800); -+ s->proto_filter[0].end = htons(0x0806); -+ /* (RARP, AppleTalk) */ -+ s->proto_filter[1].start = htons(0x8035); -+ s->proto_filter[1].end = htons(0x80F3); -+ /* (IPX, IPv6) */ -+ s->proto_filter[2].start = htons(0x8137); -+ s->proto_filter[2].end = htons(0x86DD); -+#endif -+ -+ dev->init = bnep_net_init; -+ dev->priv = s; -+ err = register_netdev(dev); -+ if (err) { -+ goto failed; -+ } -+ -+ __bnep_link_session(s); -+ -+ err = kernel_thread(bnep_session, s, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); -+ if (err < 0) { -+ /* Session thread start failed, gotta cleanup. */ -+ unregister_netdev(dev); -+ __bnep_unlink_session(s); -+ goto failed; -+ } -+ -+ up_write(&bnep_session_sem); -+ strcpy(req->device, dev->name); -+ return 0; -+ -+failed: -+ up_write(&bnep_session_sem); -+ kfree(s); -+ return err; -+} -+ -+int bnep_del_connection(struct bnep_conndel_req *req) -+{ -+ struct bnep_session *s; -+ int err = 0; -+ -+ BT_DBG(""); -+ -+ down_read(&bnep_session_sem); -+ -+ s = __bnep_get_session(req->dst); -+ if (s) { -+ /* Wakeup user-space which is polling for socket errors. -+ * This is temporary hack untill we have shutdown in L2CAP */ -+ s->sock->sk->err = EUNATCH; -+ -+ /* Kill session thread */ -+ atomic_inc(&s->killed); -+ wake_up_interruptible(s->sock->sk->sleep); -+ } else -+ err = -ENOENT; -+ -+ up_read(&bnep_session_sem); -+ return err; -+} -+ -+static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s) -+{ -+ memcpy(ci->dst, s->eh.h_source, ETH_ALEN); -+ strcpy(ci->device, s->dev.name); -+ ci->flags = s->flags; -+ ci->state = s->state; -+ ci->role = s->role; -+} -+ -+int bnep_get_connlist(struct bnep_connlist_req *req) -+{ -+ struct list_head *p; -+ int err = 0, n = 0; -+ -+ down_read(&bnep_session_sem); -+ -+ list_for_each(p, &bnep_session_list) { -+ struct bnep_session *s; -+ struct bnep_conninfo ci; -+ -+ s = list_entry(p, struct bnep_session, list); -+ -+ __bnep_copy_ci(&ci, s); -+ -+ if (copy_to_user(req->ci, &ci, sizeof(ci))) { -+ err = -EFAULT; -+ break; -+ } -+ -+ if (++n >= req->cnum) -+ break; -+ -+ req->ci++; -+ } -+ req->cnum = n; -+ -+ up_read(&bnep_session_sem); -+ return err; -+} -+ -+int bnep_get_conninfo(struct bnep_conninfo *ci) -+{ -+ struct bnep_session *s; -+ int err = 0; -+ -+ down_read(&bnep_session_sem); -+ -+ s = __bnep_get_session(ci->dst); -+ if (s) -+ __bnep_copy_ci(ci, s); -+ else -+ err = -ENOENT; -+ -+ up_read(&bnep_session_sem); -+ return err; -+} -+ -+static int __init bnep_init_module(void) -+{ -+ l2cap_load(); -+ -+ bnep_crc32_init(); -+ bnep_sock_init(); -+ -+ BT_INFO("BlueZ BNEP ver %s", VERSION); -+ BT_INFO("Copyright (C) 2001,2002 Inventel Systemes"); -+ BT_INFO("Written 2001,2002 by Clement Moreau <clement.moreau@inventel.fr>"); -+ BT_INFO("Written 2001,2002 by David Libault <david.libault@inventel.fr>"); -+ BT_INFO("Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>"); -+ -+ return 0; -+} -+ -+static void __exit bnep_cleanup_module(void) -+{ -+ bnep_sock_cleanup(); -+ bnep_crc32_cleanup(); -+} -+ -+module_init(bnep_init_module); -+module_exit(bnep_cleanup_module); -+ -+MODULE_DESCRIPTION("BlueZ BNEP ver " VERSION); -+MODULE_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyanskiy <maxk@qualcomm.com>"); -+MODULE_LICENSE("GPL"); ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/bnep/crc32.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,59 @@ -+/* -+ * Based on linux-2.5/lib/crc32 by Matt Domsch <Matt_Domsch@dell.com> -+ * -+ * FIXME: Remove in 2.5 -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/slab.h> -+#include <linux/init.h> -+#include <asm/atomic.h> -+ -+#include "crc32.h" -+ -+#define CRCPOLY_BE 0x04c11db7 -+#define CRC_BE_BITS 8 -+ -+static u32 *bnep_crc32_table; -+ -+/* -+ * This code is in the public domain; copyright abandoned. -+ * Liability for non-performance of this code is limited to the amount -+ * you paid for it. Since it is distributed for free, your refund will -+ * be very very small. If it breaks, you get to keep both pieces. -+ */ -+u32 bnep_crc32(u32 crc, unsigned char const *p, size_t len) -+{ -+ while (len--) -+ crc = (crc << 8) ^ bnep_crc32_table[(crc >> 24) ^ *p++]; -+ -+ return crc; -+} -+ -+int __init bnep_crc32_init(void) -+{ -+ unsigned i, j; -+ u32 crc = 0x80000000; -+ -+ bnep_crc32_table = kmalloc((1 << CRC_BE_BITS) * sizeof(u32), GFP_KERNEL); -+ if (!bnep_crc32_table) -+ return -ENOMEM; -+ -+ bnep_crc32_table[0] = 0; -+ -+ for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) { -+ crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0); -+ for (j = 0; j < i; j++) -+ bnep_crc32_table[i + j] = crc ^ bnep_crc32_table[j]; -+ } -+ return 0; -+} -+ -+void __exit bnep_crc32_cleanup(void) -+{ -+ if (bnep_crc32_table) -+ kfree(bnep_crc32_table); -+ bnep_crc32_table = NULL; -+} ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/bnep/crc32.h 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,10 @@ -+/* -+ * crc32.h -+ * See crc32.c for license and changes -+ * -+ * FIXME: Remove in 2.5 -+ */ -+ -+int bnep_crc32_init(void); -+void bnep_crc32_cleanup(void); -+u32 bnep_crc32(u32 crc, unsigned char const *p, size_t len); ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/bnep/Makefile 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,10 @@ -+# -+# Makefile for the Linux Bluetooth BNEP layer -+# -+ -+O_TARGET := bnep.o -+ -+obj-y := core.o sock.o netdev.o crc32.o -+obj-m += $(O_TARGET) -+ -+include $(TOPDIR)/Rules.make ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/bnep/netdev.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,254 @@ -+/* -+ BNEP implementation for Linux Bluetooth stack (BlueZ). -+ Copyright (C) 2001-2002 Inventel Systemes -+ Written 2001-2002 by -+ Clément Moreau <clement.moreau@inventel.fr> -+ David Libault <david.libault@inventel.fr> -+ -+ Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * $Id: netdev.c,v 1.7 2002/07/14 05:39:26 maxk Exp $ -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/socket.h> -+#include <linux/netdevice.h> -+#include <linux/etherdevice.h> -+#include <linux/skbuff.h> -+#include <linux/wait.h> -+ -+#include <asm/unaligned.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+#include <net/bluetooth/l2cap.h> -+ -+#include "bnep.h" -+ -+#ifndef CONFIG_BLUEZ_BNEP_DEBUG -+#undef BT_DBG -+#define BT_DBG( A... ) -+#endif -+ -+#define BNEP_TX_QUEUE_LEN 20 -+ -+static int bnep_net_open(struct net_device *dev) -+{ -+ netif_start_queue(dev); -+ return 0; -+} -+ -+static int bnep_net_close(struct net_device *dev) -+{ -+ netif_stop_queue(dev); -+ return 0; -+} -+ -+static struct net_device_stats *bnep_net_get_stats(struct net_device *dev) -+{ -+ struct bnep_session *s = dev->priv; -+ return &s->stats; -+} -+ -+static void bnep_net_set_mc_list(struct net_device *dev) -+{ -+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER -+ struct bnep_session *s = dev->priv; -+ struct sock *sk = s->sock->sk; -+ struct bnep_set_filter_req *r; -+ struct sk_buff *skb; -+ int size; -+ -+ BT_DBG("%s mc_count %d", dev->name, dev->mc_count); -+ -+ size = sizeof(*r) + (BNEP_MAX_MULTICAST_FILTERS + 1) * ETH_ALEN * 2; -+ skb = alloc_skb(size, GFP_ATOMIC); -+ if (!skb) { -+ BT_ERR("%s Multicast list allocation failed", dev->name); -+ return; -+ } -+ -+ r = (void *) skb->data; -+ __skb_put(skb, sizeof(*r)); -+ -+ r->type = BNEP_CONTROL; -+ r->ctrl = BNEP_FILTER_MULTI_ADDR_SET; -+ -+ if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { -+ u8 start[ETH_ALEN] = { 0x01 }; -+ -+ /* Request all addresses */ -+ memcpy(__skb_put(skb, ETH_ALEN), start, ETH_ALEN); -+ memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); -+ r->len = htons(ETH_ALEN * 2); -+ } else { -+ struct dev_mc_list *dmi = dev->mc_list; -+ int i, len = skb->len; -+ -+ if (dev->flags & IFF_BROADCAST) { -+ memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); -+ memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); -+ } -+ -+ /* FIXME: We should group addresses here. */ -+ -+ for (i = 0; i < dev->mc_count && i < BNEP_MAX_MULTICAST_FILTERS; i++) { -+ memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN); -+ memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN); -+ dmi = dmi->next; -+ } -+ r->len = htons(skb->len - len); -+ } -+ -+ skb_queue_tail(&sk->write_queue, skb); -+ wake_up_interruptible(sk->sleep); -+#endif -+} -+ -+static int bnep_net_set_mac_addr(struct net_device *dev, void *arg) -+{ -+ BT_DBG("%s", dev->name); -+ return 0; -+} -+ -+static void bnep_net_timeout(struct net_device *dev) -+{ -+ BT_DBG("net_timeout"); -+ netif_wake_queue(dev); -+} -+ -+static int bnep_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -+{ -+ return -EINVAL; -+} -+ -+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER -+static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s) -+{ -+ struct ethhdr *eh = (void *) skb->data; -+ -+ if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), &s->mc_filter)) { -+ BT_DBG("BNEP: filtered skb %p, dst %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", skb, -+ eh->h_dest[0], eh->h_dest[1], eh->h_dest[2], -+ eh->h_dest[3], eh->h_dest[4], eh->h_dest[5]); -+ return 1; -+ } -+ return 0; -+} -+#endif -+ -+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER -+/* Determine ether protocol. Based on eth_type_trans. */ -+static inline u16 bnep_net_eth_proto(struct sk_buff *skb) -+{ -+ struct ethhdr *eh = (void *) skb->data; -+ -+ if (ntohs(eh->h_proto) >= 1536) -+ return eh->h_proto; -+ -+ if (get_unaligned((u16 *) skb->data) == 0xFFFF) -+ return htons(ETH_P_802_3); -+ -+ return htons(ETH_P_802_2); -+} -+ -+static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s) -+{ -+ u16 proto = bnep_net_eth_proto(skb); -+ struct bnep_proto_filter *f = s->proto_filter; -+ int i; -+ -+ for (i = 0; i < BNEP_MAX_PROTO_FILTERS && f[i].end; i++) { -+ if (proto >= f[i].start && proto <= f[i].end) -+ return 0; -+ } -+ -+ BT_DBG("BNEP: filtered skb %p, proto 0x%.4x", skb, proto); -+ return 1; -+} -+#endif -+ -+static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct bnep_session *s = dev->priv; -+ struct sock *sk = s->sock->sk; -+ -+ BT_DBG("skb %p, dev %p", skb, dev); -+ -+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER -+ if (bnep_net_mc_filter(skb, s)) { -+ kfree_skb(skb); -+ return 0; -+ } -+#endif -+ -+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER -+ if (bnep_net_proto_filter(skb, s)) { -+ kfree_skb(skb); -+ return 0; -+ } -+#endif -+ -+ /* -+ * We cannot send L2CAP packets from here as we are potentially in a bh. -+ * So we have to queue them and wake up session thread which is sleeping -+ * on the sk->sleep. -+ */ -+ dev->trans_start = jiffies; -+ skb_queue_tail(&sk->write_queue, skb); -+ wake_up_interruptible(sk->sleep); -+ -+ if (skb_queue_len(&sk->write_queue) >= BNEP_TX_QUEUE_LEN) { -+ BT_DBG("tx queue is full"); -+ -+ /* Stop queuing. -+ * Session thread will do netif_wake_queue() */ -+ netif_stop_queue(dev); -+ } -+ -+ return 0; -+} -+ -+int bnep_net_init(struct net_device *dev) -+{ -+ struct bnep_session *s = dev->priv; -+ -+ memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN); -+ dev->addr_len = ETH_ALEN; -+ -+ ether_setup(dev); -+ -+ dev->open = bnep_net_open; -+ dev->stop = bnep_net_close; -+ dev->hard_start_xmit = bnep_net_xmit; -+ dev->get_stats = bnep_net_get_stats; -+ dev->do_ioctl = bnep_net_ioctl; -+ dev->set_mac_address = bnep_net_set_mac_addr; -+ dev->set_multicast_list = bnep_net_set_mc_list; -+ -+ dev->watchdog_timeo = HZ * 2; -+ dev->tx_timeout = bnep_net_timeout; -+ -+ return 0; -+} ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/bnep/sock.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,238 @@ -+/* -+ BNEP implementation for Linux Bluetooth stack (BlueZ). -+ Copyright (C) 2001-2002 Inventel Systemes -+ Written 2001-2002 by -+ David Libault <david.libault@inventel.fr> -+ -+ Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * $Id: sock.c,v 1.3 2002/07/10 22:59:52 maxk Exp $ -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/kernel.h> -+#include <linux/major.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/poll.h> -+#include <linux/fcntl.h> -+#include <linux/skbuff.h> -+#include <linux/socket.h> -+#include <linux/ioctl.h> -+#include <linux/file.h> -+#include <net/sock.h> -+ -+#include <asm/system.h> -+#include <asm/uaccess.h> -+ -+#include "bnep.h" -+ -+#ifndef CONFIG_BLUEZ_BNEP_DEBUG -+#undef BT_DBG -+#define BT_DBG( A... ) -+#endif -+ -+static inline struct socket *socki_lookup(struct inode *inode) -+{ -+ return &inode->u.socket_i; -+} -+ -+static struct socket *sockfd_lookup(int fd, int *err) -+{ -+ struct file *file; -+ struct inode *inode; -+ struct socket *sock; -+ -+ if (!(file = fget(fd))) { -+ *err = -EBADF; -+ return NULL; -+ } -+ -+ inode = file->f_dentry->d_inode; -+ if (!inode->i_sock || !(sock = socki_lookup(inode))) { -+ *err = -ENOTSOCK; -+ fput(file); -+ return NULL; -+ } -+ -+ if (sock->file != file) { -+ printk(KERN_ERR "socki_lookup: socket file changed!\n"); -+ sock->file = file; -+ } -+ return sock; -+} -+ -+static int bnep_sock_release(struct socket *sock) -+{ -+ struct sock *sk = sock->sk; -+ -+ BT_DBG("sock %p sk %p", sock, sk); -+ -+ if (!sk) -+ return 0; -+ -+ sock_orphan(sk); -+ sock_put(sk); -+ -+ MOD_DEC_USE_COUNT; -+ return 0; -+} -+ -+static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -+{ -+ struct bnep_connlist_req cl; -+ struct bnep_connadd_req ca; -+ struct bnep_conndel_req cd; -+ struct bnep_conninfo ci; -+ struct socket *nsock; -+ int err; -+ -+ BT_DBG("cmd %x arg %lx", cmd, arg); -+ -+ switch (cmd) { -+ case BNEPCONNADD: -+ if (!capable(CAP_NET_ADMIN)) -+ return -EACCES; -+ -+ if (copy_from_user(&ca, (void *) arg, sizeof(ca))) -+ return -EFAULT; -+ -+ nsock = sockfd_lookup(ca.sock, &err); -+ if (!nsock) -+ return err; -+ -+ if (nsock->sk->state != BT_CONNECTED) -+ return -EBADFD; -+ -+ err = bnep_add_connection(&ca, nsock); -+ if (!err) { -+ if (copy_to_user((void *) arg, &ca, sizeof(ca))) -+ err = -EFAULT; -+ } else -+ fput(nsock->file); -+ -+ return err; -+ -+ case BNEPCONNDEL: -+ if (!capable(CAP_NET_ADMIN)) -+ return -EACCES; -+ -+ if (copy_from_user(&cd, (void *) arg, sizeof(cd))) -+ return -EFAULT; -+ -+ return bnep_del_connection(&cd); -+ -+ case BNEPGETCONNLIST: -+ if (copy_from_user(&cl, (void *) arg, sizeof(cl))) -+ return -EFAULT; -+ -+ if (cl.cnum <= 0) -+ return -EINVAL; -+ -+ err = bnep_get_connlist(&cl); -+ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl))) -+ return -EFAULT; -+ -+ return err; -+ -+ case BNEPGETCONNINFO: -+ if (copy_from_user(&ci, (void *) arg, sizeof(ci))) -+ return -EFAULT; -+ -+ err = bnep_get_conninfo(&ci); -+ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci))) -+ return -EFAULT; -+ -+ return err; -+ -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static struct proto_ops bnep_sock_ops = { -+ family: PF_BLUETOOTH, -+ release: bnep_sock_release, -+ ioctl: bnep_sock_ioctl, -+ bind: sock_no_bind, -+ getname: sock_no_getname, -+ sendmsg: sock_no_sendmsg, -+ recvmsg: sock_no_recvmsg, -+ poll: sock_no_poll, -+ listen: sock_no_listen, -+ shutdown: sock_no_shutdown, -+ setsockopt: sock_no_setsockopt, -+ getsockopt: sock_no_getsockopt, -+ connect: sock_no_connect, -+ socketpair: sock_no_socketpair, -+ accept: sock_no_accept, -+ mmap: sock_no_mmap -+}; -+ -+static int bnep_sock_create(struct socket *sock, int protocol) -+{ -+ struct sock *sk; -+ -+ BT_DBG("sock %p", sock); -+ -+ if (sock->type != SOCK_RAW) -+ return -ESOCKTNOSUPPORT; -+ -+ sock->ops = &bnep_sock_ops; -+ -+ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1))) -+ return -ENOMEM; -+ -+ MOD_INC_USE_COUNT; -+ -+ sock->state = SS_UNCONNECTED; -+ sock_init_data(sock, sk); -+ -+ sk->destruct = NULL; -+ sk->protocol = protocol; -+ -+ return 0; -+} -+ -+static struct net_proto_family bnep_sock_family_ops = { -+ family: PF_BLUETOOTH, -+ create: bnep_sock_create -+}; -+ -+int bnep_sock_init(void) -+{ -+ bluez_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops); -+ return 0; -+} -+ -+int bnep_sock_cleanup(void) -+{ -+ if (bluez_sock_unregister(BTPROTO_BNEP)) -+ BT_ERR("Can't unregister BNEP socket"); -+ return 0; -+} ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/cmtp/capi.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,707 @@ -+/* -+ CMTP implementation for Linux Bluetooth stack (BlueZ). -+ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/kernel.h> -+#include <linux/major.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/poll.h> -+#include <linux/fcntl.h> -+#include <linux/skbuff.h> -+#include <linux/socket.h> -+#include <linux/ioctl.h> -+#include <linux/file.h> -+#include <net/sock.h> -+ -+#include <linux/capi.h> -+ -+#include "../drivers/isdn/avmb1/capilli.h" -+#include "../drivers/isdn/avmb1/capicmd.h" -+#include "../drivers/isdn/avmb1/capiutil.h" -+ -+#include "cmtp.h" -+ -+#ifndef CONFIG_BLUEZ_CMTP_DEBUG -+#undef BT_DBG -+#define BT_DBG(D...) -+#endif -+ -+#define REVISION "1.0" -+ -+#define CAPI_INTEROPERABILITY 0x20 -+ -+#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ) -+#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF) -+#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND) -+#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP) -+ -+#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2) -+#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4) -+#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2) -+#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2) -+ -+#define CAPI_FUNCTION_REGISTER 0 -+#define CAPI_FUNCTION_RELEASE 1 -+#define CAPI_FUNCTION_GET_PROFILE 2 -+#define CAPI_FUNCTION_GET_MANUFACTURER 3 -+#define CAPI_FUNCTION_GET_VERSION 4 -+#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5 -+#define CAPI_FUNCTION_MANUFACTURER 6 -+#define CAPI_FUNCTION_LOOPBACK 7 -+ -+static struct capi_driver_interface *di; -+ -+ -+#define CMTP_MSGNUM 1 -+#define CMTP_APPLID 2 -+#define CMTP_MAPPING 3 -+ -+static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl) -+{ -+ struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL); -+ -+ BT_DBG("session %p application %p appl %d", session, app, appl); -+ -+ if (!app) -+ return NULL; -+ -+ memset(app, 0, sizeof(*app)); -+ -+ app->state = BT_OPEN; -+ app->appl = appl; -+ -+ list_add_tail(&app->list, &session->applications); -+ -+ return app; -+} -+ -+static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app) -+{ -+ BT_DBG("session %p application %p", session, app); -+ -+ if (app) { -+ list_del(&app->list); -+ kfree(app); -+ } -+} -+ -+static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value) -+{ -+ struct cmtp_application *app; -+ struct list_head *p, *n; -+ -+ list_for_each_safe(p, n, &session->applications) { -+ app = list_entry(p, struct cmtp_application, list); -+ switch (pattern) { -+ case CMTP_MSGNUM: -+ if (app->msgnum == value) -+ return app; -+ break; -+ case CMTP_APPLID: -+ if (app->appl == value) -+ return app; -+ break; -+ case CMTP_MAPPING: -+ if (app->mapping == value) -+ return app; -+ break; -+ } -+ } -+ -+ return NULL; -+} -+ -+static int cmtp_msgnum_get(struct cmtp_session *session) -+{ -+ session->msgnum++; -+ -+ if ((session->msgnum & 0xff) > 200) -+ session->msgnum = CMTP_INITIAL_MSGNUM + 1; -+ -+ return session->msgnum; -+} -+ -+ -+static void cmtp_send_interopmsg(struct cmtp_session *session, -+ __u8 subcmd, __u16 appl, __u16 msgnum, -+ __u16 function, unsigned char *buf, int len) -+{ -+ struct sk_buff *skb; -+ unsigned char *s; -+ -+ BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum); -+ -+ if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) { -+ BT_ERR("Can't allocate memory for interoperability packet"); -+ return; -+ } -+ -+ s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len); -+ -+ capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len); -+ capimsg_setu16(s, 2, appl); -+ capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY); -+ capimsg_setu8 (s, 5, subcmd); -+ capimsg_setu16(s, 6, msgnum); -+ -+ /* Interoperability selector (Bluetooth Device Management) */ -+ capimsg_setu16(s, 8, 0x0001); -+ -+ capimsg_setu8 (s, 10, 3 + len); -+ capimsg_setu16(s, 11, function); -+ capimsg_setu8 (s, 13, len); -+ -+ if (len > 0) -+ memcpy(s + 14, buf, len); -+ -+ cmtp_send_capimsg(session, skb); -+} -+ -+static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb) -+{ -+ struct capi_ctr *ctrl = session->ctrl; -+ struct cmtp_application *application; -+ __u16 appl, msgnum, func, info; -+ __u32 controller; -+ -+ BT_DBG("session %p skb %p len %d", session, skb, skb->len); -+ -+ switch (CAPIMSG_SUBCOMMAND(skb->data)) { -+ case CAPI_CONF: -+ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5); -+ info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8); -+ -+ switch (func) { -+ case CAPI_FUNCTION_REGISTER: -+ msgnum = CAPIMSG_MSGID(skb->data); -+ -+ application = cmtp_application_get(session, CMTP_MSGNUM, msgnum); -+ if (application) { -+ application->state = BT_CONNECTED; -+ application->msgnum = 0; -+ application->mapping = CAPIMSG_APPID(skb->data); -+ wake_up_interruptible(&session->wait); -+ } -+ -+ break; -+ -+ case CAPI_FUNCTION_RELEASE: -+ appl = CAPIMSG_APPID(skb->data); -+ -+ application = cmtp_application_get(session, CMTP_MAPPING, appl); -+ if (application) { -+ application->state = BT_CLOSED; -+ application->msgnum = 0; -+ wake_up_interruptible(&session->wait); -+ } -+ -+ break; -+ -+ case CAPI_FUNCTION_GET_PROFILE: -+ controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11); -+ msgnum = CAPIMSG_MSGID(skb->data); -+ -+ if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) { -+ session->ncontroller = controller; -+ wake_up_interruptible(&session->wait); -+ break; -+ } -+ -+ if (!info && ctrl) { -+ memcpy(&ctrl->profile, -+ skb->data + CAPI_MSG_BASELEN + 11, -+ sizeof(capi_profile)); -+ session->state = BT_CONNECTED; -+ ctrl->ready(ctrl); -+ } -+ -+ break; -+ -+ case CAPI_FUNCTION_GET_MANUFACTURER: -+ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10); -+ -+ if (!info && ctrl) { -+ strncpy(ctrl->manu, -+ skb->data + CAPI_MSG_BASELEN + 15, -+ skb->data[CAPI_MSG_BASELEN + 14]); -+ } -+ -+ break; -+ -+ case CAPI_FUNCTION_GET_VERSION: -+ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); -+ -+ if (!info && ctrl) { -+ ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16); -+ ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20); -+ ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24); -+ ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28); -+ } -+ -+ break; -+ -+ case CAPI_FUNCTION_GET_SERIAL_NUMBER: -+ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); -+ -+ if (!info && ctrl) { -+ memset(ctrl->serial, 0, CAPI_SERIAL_LEN); -+ strncpy(ctrl->serial, -+ skb->data + CAPI_MSG_BASELEN + 17, -+ skb->data[CAPI_MSG_BASELEN + 16]); -+ } -+ -+ break; -+ } -+ -+ break; -+ -+ case CAPI_IND: -+ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3); -+ -+ if (func == CAPI_FUNCTION_LOOPBACK) { -+ appl = CAPIMSG_APPID(skb->data); -+ msgnum = CAPIMSG_MSGID(skb->data); -+ cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func, -+ skb->data + CAPI_MSG_BASELEN + 6, -+ skb->data[CAPI_MSG_BASELEN + 5]); -+ } -+ -+ break; -+ } -+ -+ kfree_skb(skb); -+} -+ -+void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) -+{ -+ struct capi_ctr *ctrl = session->ctrl; -+ struct cmtp_application *application; -+ __u16 cmd, appl, info; -+ __u32 ncci, contr; -+ -+ BT_DBG("session %p skb %p len %d", session, skb, skb->len); -+ -+ if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) { -+ cmtp_recv_interopmsg(session, skb); -+ return; -+ } -+ -+ if (session->flags & (1 << CMTP_LOOPBACK)) { -+ kfree_skb(skb); -+ return; -+ } -+ -+ cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data)); -+ appl = CAPIMSG_APPID(skb->data); -+ contr = CAPIMSG_CONTROL(skb->data); -+ -+ application = cmtp_application_get(session, CMTP_MAPPING, appl); -+ if (application) { -+ appl = application->appl; -+ CAPIMSG_SETAPPID(skb->data, appl); -+ } else { -+ BT_ERR("Can't find application with id %d", appl); -+ kfree_skb(skb); -+ return; -+ } -+ -+ if ((contr & 0x7f) == 0x01) { -+ contr = (contr & 0xffffff80) | session->num; -+ CAPIMSG_SETCONTROL(skb->data, contr); -+ } -+ -+ if (!ctrl) { -+ BT_ERR("Can't find controller %d for message", session->num); -+ kfree_skb(skb); -+ return; -+ } -+ -+ switch (cmd) { -+ case CAPI_CONNECT_B3_CONF: -+ ncci = CAPIMSG_NCCI(skb->data); -+ info = CAPIMSG_U16(skb->data, 12); -+ -+ BT_DBG("CONNECT_B3_CONF ncci 0x%02x info 0x%02x", ncci, info); -+ -+ if (info == 0) -+ ctrl->new_ncci(ctrl, appl, ncci, 8); -+ -+ ctrl->handle_capimsg(ctrl, appl, skb); -+ break; -+ -+ case CAPI_CONNECT_B3_IND: -+ ncci = CAPIMSG_NCCI(skb->data); -+ -+ BT_DBG("CONNECT_B3_IND ncci 0x%02x", ncci); -+ -+ ctrl->new_ncci(ctrl, appl, ncci, 8); -+ ctrl->handle_capimsg(ctrl, appl, skb); -+ break; -+ -+ case CAPI_DISCONNECT_B3_IND: -+ ncci = CAPIMSG_NCCI(skb->data); -+ -+ BT_DBG("DISCONNECT_B3_IND ncci 0x%02x", ncci); -+ -+ if (ncci == 0xffffffff) -+ BT_ERR("DISCONNECT_B3_IND with ncci 0xffffffff"); -+ -+ ctrl->handle_capimsg(ctrl, appl, skb); -+ ctrl->free_ncci(ctrl, appl, ncci); -+ break; -+ -+ default: -+ ctrl->handle_capimsg(ctrl, appl, skb); -+ break; -+ } -+} -+ -+void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb) -+{ -+ struct cmtp_scb *scb = (void *) skb->cb; -+ -+ BT_DBG("session %p skb %p len %d", session, skb, skb->len); -+ -+ scb->id = -1; -+ scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3); -+ -+ skb_queue_tail(&session->transmit, skb); -+ -+ cmtp_schedule(session); -+} -+ -+ -+static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) -+{ -+ BT_DBG("ctrl %p data %p", ctrl, data); -+ -+ return -EIO; -+} -+ -+static void cmtp_reset_ctr(struct capi_ctr *ctrl) -+{ -+ BT_DBG("ctrl %p", ctrl); -+ -+ ctrl->reseted(ctrl); -+} -+ -+static void cmtp_remove_ctr(struct capi_ctr *ctrl) -+{ -+ struct cmtp_session *session = ctrl->driverdata; -+ -+ BT_DBG("ctrl %p", ctrl); -+ -+ ctrl->suspend_output(ctrl); -+ -+ atomic_inc(&session->terminate); -+ cmtp_schedule(session); -+} -+ -+static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp) -+{ -+ DECLARE_WAITQUEUE(wait, current); -+ struct cmtp_session *session = ctrl->driverdata; -+ struct cmtp_application *application; -+ unsigned long timeo = CMTP_INTEROP_TIMEOUT; -+ unsigned char buf[8]; -+ int err = 0, nconn, want = rp->level3cnt; -+ -+ BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d", -+ ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen); -+ -+ application = cmtp_application_add(session, appl); -+ if (!application) { -+ BT_ERR("Can't allocate memory for new application"); -+ ctrl->appl_released(ctrl, appl); -+ return; -+ } -+ -+ if (want < 0) -+ nconn = ctrl->profile.nbchannel * -want; -+ else -+ nconn = want; -+ -+ if (nconn == 0) -+ nconn = ctrl->profile.nbchannel; -+ -+ capimsg_setu16(buf, 0, nconn); -+ capimsg_setu16(buf, 2, rp->datablkcnt); -+ capimsg_setu16(buf, 4, rp->datablklen); -+ -+ application->state = BT_CONFIG; -+ application->msgnum = cmtp_msgnum_get(session); -+ -+ cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum, -+ CAPI_FUNCTION_REGISTER, buf, 6); -+ -+ add_wait_queue(&session->wait, &wait); -+ while (1) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ -+ if (!timeo) { -+ err = -EAGAIN; -+ break; -+ } -+ -+ if (application->state == BT_CLOSED) { -+ err = -application->err; -+ break; -+ } -+ -+ if (application->state == BT_CONNECTED) -+ break; -+ -+ if (signal_pending(current)) { -+ err = -EINTR; -+ break; -+ } -+ -+ timeo = schedule_timeout(timeo); -+ } -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(&session->wait, &wait); -+ -+ if (err) { -+ ctrl->appl_released(ctrl, appl); -+ cmtp_application_del(session, application); -+ return; -+ } -+ -+ ctrl->appl_registered(ctrl, appl); -+} -+ -+static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl) -+{ -+ DECLARE_WAITQUEUE(wait, current); -+ struct cmtp_session *session = ctrl->driverdata; -+ struct cmtp_application *application; -+ unsigned long timeo = CMTP_INTEROP_TIMEOUT; -+ -+ BT_DBG("ctrl %p appl %d", ctrl, appl); -+ -+ application = cmtp_application_get(session, CMTP_APPLID, appl); -+ if (!application) { -+ BT_ERR("Can't find application"); -+ return; -+ } -+ -+ application->msgnum = cmtp_msgnum_get(session); -+ -+ cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum, -+ CAPI_FUNCTION_RELEASE, NULL, 0); -+ -+ add_wait_queue(&session->wait, &wait); -+ while (timeo) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ -+ if (application->state == BT_CLOSED) -+ break; -+ -+ if (signal_pending(current)) -+ break; -+ -+ timeo = schedule_timeout(timeo); -+ } -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(&session->wait, &wait); -+ -+ cmtp_application_del(session, application); -+ ctrl->appl_released(ctrl, appl); -+} -+ -+static void cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) -+{ -+ struct cmtp_session *session = ctrl->driverdata; -+ struct cmtp_application *application; -+ __u16 appl; -+ __u32 contr; -+ -+ BT_DBG("ctrl %p skb %p", ctrl, skb); -+ -+ appl = CAPIMSG_APPID(skb->data); -+ contr = CAPIMSG_CONTROL(skb->data); -+ -+ application = cmtp_application_get(session, CMTP_APPLID, appl); -+ if ((!application) || (application->state != BT_CONNECTED)) { -+ BT_ERR("Can't find application with id %d", appl); -+ kfree_skb(skb); -+ return; -+ } -+ -+ CAPIMSG_SETAPPID(skb->data, application->mapping); -+ -+ if ((contr & 0x7f) == session->num) { -+ contr = (contr & 0xffffff80) | 0x01; -+ CAPIMSG_SETCONTROL(skb->data, contr); -+ } -+ -+ cmtp_send_capimsg(session, skb); -+} -+ -+static char *cmtp_procinfo(struct capi_ctr *ctrl) -+{ -+ return "CAPI Message Transport Protocol"; -+} -+ -+static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl) -+{ -+ struct cmtp_session *session = ctrl->driverdata; -+ struct cmtp_application *app; -+ struct list_head *p, *n; -+ int len = 0; -+ -+ len += sprintf(page + len, "%s (Revision %s)\n\n", cmtp_procinfo(ctrl), REVISION); -+ len += sprintf(page + len, "addr %s\n", session->name); -+ len += sprintf(page + len, "ctrl %d\n", session->num); -+ -+ list_for_each_safe(p, n, &session->applications) { -+ app = list_entry(p, struct cmtp_application, list); -+ len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping); -+ } -+ -+ if (off + count >= len) -+ *eof = 1; -+ -+ if (len < off) -+ return 0; -+ -+ *start = page + off; -+ -+ return ((count < len - off) ? count : len - off); -+} -+ -+static struct capi_driver cmtp_driver = { -+ name: "cmtp", -+ revision: REVISION, -+ load_firmware: cmtp_load_firmware, -+ reset_ctr: cmtp_reset_ctr, -+ remove_ctr: cmtp_remove_ctr, -+ register_appl: cmtp_register_appl, -+ release_appl: cmtp_release_appl, -+ send_message: cmtp_send_message, -+ procinfo: cmtp_procinfo, -+ ctr_read_proc: cmtp_ctr_read_proc, -+ -+ driver_read_proc: 0, -+ add_card: 0, -+}; -+ -+ -+int cmtp_attach_device(struct cmtp_session *session) -+{ -+ DECLARE_WAITQUEUE(wait, current); -+ unsigned long timeo = CMTP_INTEROP_TIMEOUT; -+ unsigned char buf[4]; -+ -+ BT_DBG("session %p", session); -+ -+ capimsg_setu32(buf, 0, 0); -+ -+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM, -+ CAPI_FUNCTION_GET_PROFILE, buf, 4); -+ -+ add_wait_queue(&session->wait, &wait); -+ while (timeo) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ -+ if (session->ncontroller) -+ break; -+ -+ if (signal_pending(current)) -+ break; -+ -+ timeo = schedule_timeout(timeo); -+ } -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(&session->wait, &wait); -+ -+ BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name); -+ -+ if (!timeo) -+ return -ETIMEDOUT; -+ -+ if (!session->ncontroller) -+ return -ENODEV; -+ -+ -+ if (session->ncontroller > 1) -+ BT_INFO("Setting up only CAPI controller 1"); -+ -+ if (!(session->ctrl = di->attach_ctr(&cmtp_driver, session->name, session))) { -+ BT_ERR("Can't attach new controller"); -+ return -EBUSY; -+ } -+ -+ session->num = session->ctrl->cnr; -+ -+ BT_DBG("session %p ctrl %p num %d", session, session->ctrl, session->num); -+ -+ capimsg_setu32(buf, 0, 1); -+ -+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), -+ CAPI_FUNCTION_GET_MANUFACTURER, buf, 4); -+ -+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), -+ CAPI_FUNCTION_GET_VERSION, buf, 4); -+ -+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), -+ CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4); -+ -+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), -+ CAPI_FUNCTION_GET_PROFILE, buf, 4); -+ -+ return 0; -+} -+ -+void cmtp_detach_device(struct cmtp_session *session) -+{ -+ struct capi_ctr *ctrl = session->ctrl; -+ -+ BT_DBG("session %p ctrl %p", session, ctrl); -+ -+ if (!ctrl) -+ return; -+ -+ ctrl->reseted(ctrl); -+ -+ di->detach_ctr(ctrl); -+} -+ -+int cmtp_init_capi(void) -+{ -+ if (!(di = attach_capi_driver(&cmtp_driver))) { -+ BT_ERR("Can't attach CAPI driver"); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+void cmtp_cleanup_capi(void) -+{ -+ detach_capi_driver(&cmtp_driver); -+} ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/cmtp/cmtp.h 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,138 @@ -+/* -+ CMTP implementation for Linux Bluetooth stack (BlueZ). -+ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+#ifndef __CMTP_H -+#define __CMTP_H -+ -+#include <linux/types.h> -+#include <net/bluetooth/bluetooth.h> -+ -+#define BTNAMSIZ 18 -+ -+/* CMTP ioctl defines */ -+#define CMTPCONNADD _IOW('C', 200, int) -+#define CMTPCONNDEL _IOW('C', 201, int) -+#define CMTPGETCONNLIST _IOR('C', 210, int) -+#define CMTPGETCONNINFO _IOR('C', 211, int) -+ -+#define CMTP_LOOPBACK 0 -+ -+struct cmtp_connadd_req { -+ int sock; // Connected socket -+ __u32 flags; -+}; -+ -+struct cmtp_conndel_req { -+ bdaddr_t bdaddr; -+ __u32 flags; -+}; -+ -+struct cmtp_conninfo { -+ bdaddr_t bdaddr; -+ __u32 flags; -+ __u16 state; -+ int num; -+}; -+ -+struct cmtp_connlist_req { -+ __u32 cnum; -+ struct cmtp_conninfo *ci; -+}; -+ -+int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock); -+int cmtp_del_connection(struct cmtp_conndel_req *req); -+int cmtp_get_connlist(struct cmtp_connlist_req *req); -+int cmtp_get_conninfo(struct cmtp_conninfo *ci); -+ -+/* CMTP session defines */ -+#define CMTP_INTEROP_TIMEOUT (HZ * 5) -+#define CMTP_INITIAL_MSGNUM 0xff00 -+ -+struct cmtp_session { -+ struct list_head list; -+ -+ struct socket *sock; -+ -+ bdaddr_t bdaddr; -+ -+ unsigned long state; -+ unsigned long flags; -+ -+ uint mtu; -+ -+ char name[BTNAMSIZ]; -+ -+ atomic_t terminate; -+ -+ wait_queue_head_t wait; -+ -+ int ncontroller; -+ int num; -+ struct capi_ctr *ctrl; -+ -+ struct list_head applications; -+ -+ unsigned long blockids; -+ int msgnum; -+ -+ struct sk_buff_head transmit; -+ -+ struct sk_buff *reassembly[16]; -+}; -+ -+struct cmtp_application { -+ struct list_head list; -+ -+ unsigned long state; -+ int err; -+ -+ __u16 appl; -+ __u16 mapping; -+ -+ __u16 msgnum; -+}; -+ -+struct cmtp_scb { -+ int id; -+ int data; -+}; -+ -+int cmtp_attach_device(struct cmtp_session *session); -+void cmtp_detach_device(struct cmtp_session *session); -+ -+void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb); -+void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb); -+ -+static inline void cmtp_schedule(struct cmtp_session *session) -+{ -+ struct sock *sk = session->sock->sk; -+ -+ wake_up_interruptible(sk->sleep); -+} -+ -+/* CMTP init defines */ -+int cmtp_init_capi(void); -+int cmtp_init_sockets(void); -+void cmtp_cleanup_capi(void); -+void cmtp_cleanup_sockets(void); -+ -+#endif /* __CMTP_H */ ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/cmtp/Config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,7 @@ -+# -+# Bluetooth CMTP layer configuration -+# -+ -+if [ "$CONFIG_ISDN" = "y" -o "$CONFIG_ISDN" = "m" ]; then -+ dep_tristate 'CMTP protocol support' CONFIG_BLUEZ_CMTP $CONFIG_ISDN_CAPI $CONFIG_BLUEZ_L2CAP -+fi ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/cmtp/core.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,515 @@ -+/* -+ CMTP implementation for Linux Bluetooth stack (BlueZ). -+ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/kernel.h> -+#include <linux/major.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/poll.h> -+#include <linux/fcntl.h> -+#include <linux/skbuff.h> -+#include <linux/socket.h> -+#include <linux/ioctl.h> -+#include <linux/file.h> -+#include <linux/init.h> -+#include <net/sock.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/l2cap.h> -+ -+#include "cmtp.h" -+ -+#ifndef CONFIG_BLUEZ_CMTP_DEBUG -+#undef BT_DBG -+#define BT_DBG(D...) -+#endif -+ -+#define VERSION "1.0" -+ -+static DECLARE_RWSEM(cmtp_session_sem); -+static LIST_HEAD(cmtp_session_list); -+ -+static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) -+{ -+ struct cmtp_session *session; -+ struct list_head *p; -+ -+ BT_DBG(""); -+ -+ list_for_each(p, &cmtp_session_list) { -+ session = list_entry(p, struct cmtp_session, list); -+ if (!bacmp(bdaddr, &session->bdaddr)) -+ return session; -+ } -+ return NULL; -+} -+ -+static void __cmtp_link_session(struct cmtp_session *session) -+{ -+ MOD_INC_USE_COUNT; -+ list_add(&session->list, &cmtp_session_list); -+} -+ -+static void __cmtp_unlink_session(struct cmtp_session *session) -+{ -+ list_del(&session->list); -+ MOD_DEC_USE_COUNT; -+} -+ -+static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci) -+{ -+ bacpy(&ci->bdaddr, &session->bdaddr); -+ -+ ci->flags = session->flags; -+ ci->state = session->state; -+ -+ ci->num = session->num; -+} -+ -+ -+static inline int cmtp_alloc_block_id(struct cmtp_session *session) -+{ -+ int i, id = -1; -+ -+ for (i = 0; i < 16; i++) -+ if (!test_and_set_bit(i, &session->blockids)) { -+ id = i; -+ break; -+ } -+ -+ return id; -+} -+ -+static inline void cmtp_free_block_id(struct cmtp_session *session, int id) -+{ -+ clear_bit(id, &session->blockids); -+} -+ -+static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count) -+{ -+ struct sk_buff *skb = session->reassembly[id], *nskb; -+ int size; -+ -+ BT_DBG("session %p buf %p count %d", session, buf, count); -+ -+ size = (skb) ? skb->len + count : count; -+ -+ if (!(nskb = alloc_skb(size, GFP_ATOMIC))) { -+ BT_ERR("Can't allocate memory for CAPI message"); -+ return; -+ } -+ -+ if (skb && (skb->len > 0)) -+ memcpy(skb_put(nskb, skb->len), skb->data, skb->len); -+ -+ memcpy(skb_put(nskb, count), buf, count); -+ -+ session->reassembly[id] = nskb; -+ -+ if (skb) -+ kfree_skb(skb); -+} -+ -+static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb) -+{ -+ __u8 hdr, hdrlen, id; -+ __u16 len; -+ -+ BT_DBG("session %p skb %p len %d", session, skb, skb->len); -+ -+ while (skb->len > 0) { -+ hdr = skb->data[0]; -+ -+ switch (hdr & 0xc0) { -+ case 0x40: -+ hdrlen = 2; -+ len = skb->data[1]; -+ break; -+ case 0x80: -+ hdrlen = 3; -+ len = skb->data[1] | (skb->data[2] << 8); -+ break; -+ default: -+ hdrlen = 1; -+ len = 0; -+ break; -+ } -+ -+ id = (hdr & 0x3c) >> 2; -+ -+ BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id); -+ -+ if (hdrlen + len > skb->len) { -+ BT_ERR("Wrong size or header information in CMTP frame"); -+ break; -+ } -+ -+ if (len == 0) { -+ skb_pull(skb, hdrlen); -+ continue; -+ } -+ -+ switch (hdr & 0x03) { -+ case 0x00: -+ cmtp_add_msgpart(session, id, skb->data + hdrlen, len); -+ cmtp_recv_capimsg(session, session->reassembly[id]); -+ session->reassembly[id] = NULL; -+ break; -+ case 0x01: -+ cmtp_add_msgpart(session, id, skb->data + hdrlen, len); -+ break; -+ default: -+ if (session->reassembly[id] != NULL) -+ kfree_skb(session->reassembly[id]); -+ session->reassembly[id] = NULL; -+ break; -+ } -+ -+ skb_pull(skb, hdrlen + len); -+ } -+ -+ kfree_skb(skb); -+ return 0; -+} -+ -+static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len) -+{ -+ struct socket *sock = session->sock; -+ struct iovec iv = { data, len }; -+ struct msghdr msg; -+ int err; -+ -+ BT_DBG("session %p data %p len %d", session, data, len); -+ -+ if (!len) -+ return 0; -+ -+ memset(&msg, 0, sizeof(msg)); -+ msg.msg_iovlen = 1; -+ msg.msg_iov = &iv; -+ -+ err = sock->ops->sendmsg(sock, &msg, len, 0); -+ return err; -+} -+ -+static int cmtp_process_transmit(struct cmtp_session *session) -+{ -+ struct sk_buff *skb, *nskb; -+ unsigned char *hdr; -+ unsigned int size, tail; -+ -+ BT_DBG("session %p", session); -+ -+ if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) { -+ BT_ERR("Can't allocate memory for new frame"); -+ return -ENOMEM; -+ } -+ -+ while ((skb = skb_dequeue(&session->transmit))) { -+ struct cmtp_scb *scb = (void *) skb->cb; -+ -+ if ((tail = (session->mtu - nskb->len)) < 5) { -+ cmtp_send_frame(session, nskb->data, nskb->len); -+ skb_trim(nskb, 0); -+ tail = session->mtu; -+ } -+ -+ size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len); -+ -+ if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) { -+ skb_queue_head(&session->transmit, skb); -+ break; -+ } -+ -+ if (size < 256) { -+ hdr = skb_put(nskb, 2); -+ hdr[0] = 0x40 -+ | ((scb->id << 2) & 0x3c) -+ | ((skb->len == size) ? 0x00 : 0x01); -+ hdr[1] = size; -+ } else { -+ hdr = skb_put(nskb, 3); -+ hdr[0] = 0x80 -+ | ((scb->id << 2) & 0x3c) -+ | ((skb->len == size) ? 0x00 : 0x01); -+ hdr[1] = size & 0xff; -+ hdr[2] = size >> 8; -+ } -+ -+ memcpy(skb_put(nskb, size), skb->data, size); -+ skb_pull(skb, size); -+ -+ if (skb->len > 0) { -+ skb_queue_head(&session->transmit, skb); -+ } else { -+ cmtp_free_block_id(session, scb->id); -+ if (scb->data) { -+ cmtp_send_frame(session, nskb->data, nskb->len); -+ skb_trim(nskb, 0); -+ } -+ kfree_skb(skb); -+ } -+ } -+ -+ cmtp_send_frame(session, nskb->data, nskb->len); -+ -+ kfree_skb(nskb); -+ -+ return skb_queue_len(&session->transmit); -+} -+ -+static int cmtp_session(void *arg) -+{ -+ struct cmtp_session *session = arg; -+ struct sock *sk = session->sock->sk; -+ struct sk_buff *skb; -+ wait_queue_t wait; -+ -+ BT_DBG("session %p", session); -+ -+ daemonize(); reparent_to_init(); -+ -+ sprintf(current->comm, "kcmtpd_ctr_%d", session->num); -+ -+ sigfillset(¤t->blocked); -+ flush_signals(current); -+ -+ current->nice = -15; -+ -+ set_fs(KERNEL_DS); -+ -+ init_waitqueue_entry(&wait, current); -+ add_wait_queue(sk->sleep, &wait); -+ while (!atomic_read(&session->terminate)) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ -+ if (sk->state != BT_CONNECTED) -+ break; -+ -+ while ((skb = skb_dequeue(&sk->receive_queue))) { -+ skb_orphan(skb); -+ cmtp_recv_frame(session, skb); -+ } -+ -+ cmtp_process_transmit(session); -+ -+ schedule(); -+ } -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(sk->sleep, &wait); -+ -+ down_write(&cmtp_session_sem); -+ -+ if (!(session->flags & (1 << CMTP_LOOPBACK))) -+ cmtp_detach_device(session); -+ -+ fput(session->sock->file); -+ -+ __cmtp_unlink_session(session); -+ -+ up_write(&cmtp_session_sem); -+ -+ kfree(session); -+ return 0; -+} -+ -+int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) -+{ -+ struct cmtp_session *session, *s; -+ bdaddr_t src, dst; -+ int i, err; -+ -+ BT_DBG(""); -+ -+ baswap(&src, &bluez_pi(sock->sk)->src); -+ baswap(&dst, &bluez_pi(sock->sk)->dst); -+ -+ session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL); -+ if (!session) -+ return -ENOMEM; -+ memset(session, 0, sizeof(struct cmtp_session)); -+ -+ down_write(&cmtp_session_sem); -+ -+ s = __cmtp_get_session(&bluez_pi(sock->sk)->dst); -+ if (s && s->state == BT_CONNECTED) { -+ err = -EEXIST; -+ goto failed; -+ } -+ -+ bacpy(&session->bdaddr, &bluez_pi(sock->sk)->dst); -+ -+ session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu); -+ -+ BT_DBG("mtu %d", session->mtu); -+ -+ sprintf(session->name, "%s", batostr(&dst)); -+ -+ session->sock = sock; -+ session->state = BT_CONFIG; -+ -+ init_waitqueue_head(&session->wait); -+ -+ session->ctrl = NULL; -+ session->msgnum = CMTP_INITIAL_MSGNUM; -+ -+ INIT_LIST_HEAD(&session->applications); -+ -+ skb_queue_head_init(&session->transmit); -+ -+ for (i = 0; i < 16; i++) -+ session->reassembly[i] = NULL; -+ -+ session->flags = req->flags; -+ -+ __cmtp_link_session(session); -+ -+ err = kernel_thread(cmtp_session, session, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); -+ if (err < 0) -+ goto unlink; -+ -+ if (!(session->flags & (1 << CMTP_LOOPBACK))) { -+ err = cmtp_attach_device(session); -+ if (err < 0) -+ goto detach; -+ } -+ -+ up_write(&cmtp_session_sem); -+ return 0; -+ -+detach: -+ cmtp_detach_device(session); -+ -+unlink: -+ __cmtp_unlink_session(session); -+ -+failed: -+ up_write(&cmtp_session_sem); -+ kfree(session); -+ return err; -+} -+ -+int cmtp_del_connection(struct cmtp_conndel_req *req) -+{ -+ struct cmtp_session *session; -+ int err = 0; -+ -+ BT_DBG(""); -+ -+ down_read(&cmtp_session_sem); -+ -+ session = __cmtp_get_session(&req->bdaddr); -+ if (session) { -+ /* Flush the transmit queue */ -+ skb_queue_purge(&session->transmit); -+ -+ /* Kill session thread */ -+ atomic_inc(&session->terminate); -+ cmtp_schedule(session); -+ } else -+ err = -ENOENT; -+ -+ up_read(&cmtp_session_sem); -+ return err; -+} -+ -+int cmtp_get_connlist(struct cmtp_connlist_req *req) -+{ -+ struct list_head *p; -+ int err = 0, n = 0; -+ -+ BT_DBG(""); -+ -+ down_read(&cmtp_session_sem); -+ -+ list_for_each(p, &cmtp_session_list) { -+ struct cmtp_session *session; -+ struct cmtp_conninfo ci; -+ -+ session = list_entry(p, struct cmtp_session, list); -+ -+ __cmtp_copy_session(session, &ci); -+ -+ if (copy_to_user(req->ci, &ci, sizeof(ci))) { -+ err = -EFAULT; -+ break; -+ } -+ -+ if (++n >= req->cnum) -+ break; -+ -+ req->ci++; -+ } -+ req->cnum = n; -+ -+ up_read(&cmtp_session_sem); -+ return err; -+} -+ -+int cmtp_get_conninfo(struct cmtp_conninfo *ci) -+{ -+ struct cmtp_session *session; -+ int err = 0; -+ -+ down_read(&cmtp_session_sem); -+ -+ session = __cmtp_get_session(&ci->bdaddr); -+ if (session) -+ __cmtp_copy_session(session, ci); -+ else -+ err = -ENOENT; -+ -+ up_read(&cmtp_session_sem); -+ return err; -+} -+ -+ -+int __init init_cmtp(void) -+{ -+ l2cap_load(); -+ -+ cmtp_init_capi(); -+ cmtp_init_sockets(); -+ -+ BT_INFO("BlueZ CMTP ver %s", VERSION); -+ BT_INFO("Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>"); -+ -+ return 0; -+} -+ -+void __exit exit_cmtp(void) -+{ -+ cmtp_cleanup_sockets(); -+ cmtp_cleanup_capi(); -+} -+ -+module_init(init_cmtp); -+module_exit(exit_cmtp); -+ -+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -+MODULE_DESCRIPTION("BlueZ CMTP ver " VERSION); -+MODULE_LICENSE("GPL"); ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/cmtp/Makefile 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,10 @@ -+# -+# Makefile for the Linux Bluetooth CMTP layer -+# -+ -+O_TARGET := cmtp.o -+ -+obj-y := core.o sock.o capi.o -+obj-m += $(O_TARGET) -+ -+include $(TOPDIR)/Rules.make ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/cmtp/sock.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,236 @@ -+/* -+ CMTP implementation for Linux Bluetooth stack (BlueZ). -+ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/kernel.h> -+#include <linux/major.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/poll.h> -+#include <linux/fcntl.h> -+#include <linux/skbuff.h> -+#include <linux/socket.h> -+#include <linux/ioctl.h> -+#include <linux/file.h> -+#include <net/sock.h> -+ -+#include <asm/system.h> -+#include <asm/uaccess.h> -+ -+#include "cmtp.h" -+ -+#ifndef CONFIG_BLUEZ_CMTP_DEBUG -+#undef BT_DBG -+#define BT_DBG(D...) -+#endif -+ -+static inline struct socket *socki_lookup(struct inode *inode) -+{ -+ return &inode->u.socket_i; -+} -+ -+static struct socket *sockfd_lookup(int fd, int *err) -+{ -+ struct file *file; -+ struct inode *inode; -+ struct socket *sock; -+ -+ if (!(file = fget(fd))) { -+ *err = -EBADF; -+ return NULL; -+ } -+ -+ inode = file->f_dentry->d_inode; -+ if (!inode->i_sock || !(sock = socki_lookup(inode))) { -+ *err = -ENOTSOCK; -+ fput(file); -+ return NULL; -+ } -+ -+ if (sock->file != file) { -+ printk(KERN_ERR "socki_lookup: socket file changed!\n"); -+ sock->file = file; -+ } -+ return sock; -+} -+ -+static int cmtp_sock_release(struct socket *sock) -+{ -+ struct sock *sk = sock->sk; -+ -+ BT_DBG("sock %p sk %p", sock, sk); -+ -+ if (!sk) -+ return 0; -+ -+ sock_orphan(sk); -+ sock_put(sk); -+ -+ MOD_DEC_USE_COUNT; -+ return 0; -+} -+ -+static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -+{ -+ struct cmtp_connadd_req ca; -+ struct cmtp_conndel_req cd; -+ struct cmtp_connlist_req cl; -+ struct cmtp_conninfo ci; -+ struct socket *nsock; -+ int err; -+ -+ BT_DBG("cmd %x arg %lx", cmd, arg); -+ -+ switch (cmd) { -+ case CMTPCONNADD: -+ if (!capable(CAP_NET_ADMIN)) -+ return -EACCES; -+ -+ if (copy_from_user(&ca, (void *) arg, sizeof(ca))) -+ return -EFAULT; -+ -+ nsock = sockfd_lookup(ca.sock, &err); -+ if (!nsock) -+ return err; -+ -+ if (nsock->sk->state != BT_CONNECTED) -+ return -EBADFD; -+ -+ err = cmtp_add_connection(&ca, nsock); -+ if (!err) { -+ if (copy_to_user((void *) arg, &ca, sizeof(ca))) -+ err = -EFAULT; -+ } else -+ fput(nsock->file); -+ -+ return err; -+ -+ case CMTPCONNDEL: -+ if (!capable(CAP_NET_ADMIN)) -+ return -EACCES; -+ -+ if (copy_from_user(&cd, (void *) arg, sizeof(cd))) -+ return -EFAULT; -+ -+ return cmtp_del_connection(&cd); -+ -+ case CMTPGETCONNLIST: -+ if (copy_from_user(&cl, (void *) arg, sizeof(cl))) -+ return -EFAULT; -+ -+ if (cl.cnum <= 0) -+ return -EINVAL; -+ -+ err = cmtp_get_connlist(&cl); -+ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl))) -+ return -EFAULT; -+ -+ return err; -+ -+ case CMTPGETCONNINFO: -+ if (copy_from_user(&ci, (void *) arg, sizeof(ci))) -+ return -EFAULT; -+ -+ err = cmtp_get_conninfo(&ci); -+ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci))) -+ return -EFAULT; -+ -+ return err; -+ } -+ -+ return -EINVAL; -+} -+ -+static struct proto_ops cmtp_sock_ops = { -+ family: PF_BLUETOOTH, -+ release: cmtp_sock_release, -+ ioctl: cmtp_sock_ioctl, -+ bind: sock_no_bind, -+ getname: sock_no_getname, -+ sendmsg: sock_no_sendmsg, -+ recvmsg: sock_no_recvmsg, -+ poll: sock_no_poll, -+ listen: sock_no_listen, -+ shutdown: sock_no_shutdown, -+ setsockopt: sock_no_setsockopt, -+ getsockopt: sock_no_getsockopt, -+ connect: sock_no_connect, -+ socketpair: sock_no_socketpair, -+ accept: sock_no_accept, -+ mmap: sock_no_mmap -+}; -+ -+static int cmtp_sock_create(struct socket *sock, int protocol) -+{ -+ struct sock *sk; -+ -+ BT_DBG("sock %p", sock); -+ -+ if (sock->type != SOCK_RAW) -+ return -ESOCKTNOSUPPORT; -+ -+ sock->ops = &cmtp_sock_ops; -+ -+ if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1))) -+ return -ENOMEM; -+ -+ MOD_INC_USE_COUNT; -+ -+ sock->state = SS_UNCONNECTED; -+ sock_init_data(sock, sk); -+ -+ sk->destruct = NULL; -+ sk->protocol = protocol; -+ -+ return 0; -+} -+ -+static struct net_proto_family cmtp_sock_family_ops = { -+ family: PF_BLUETOOTH, -+ create: cmtp_sock_create -+}; -+ -+int cmtp_init_sockets(void) -+{ -+ int err; -+ -+ if ((err = bluez_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops))) { -+ BT_ERR("Can't register CMTP socket layer (%d)", err); -+ return err; -+ } -+ -+ return 0; -+} -+ -+void cmtp_cleanup_sockets(void) -+{ -+ int err; -+ -+ if ((err = bluez_sock_unregister(BTPROTO_CMTP))) -+ BT_ERR("Can't unregister CMTP socket layer (%d)", err); -+ -+ return; -+} ---- linux/net/bluetooth/Config.in~bluetooth-2.4.18-mh11 2001-06-12 04:15:27.000000000 +0200 -+++ linux/net/bluetooth/Config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -1,16 +1,22 @@ - # --# Bluetooth configuration -+# Bluetooth subsystem configuration - # - - if [ "$CONFIG_NET" != "n" ]; then -+ - mainmenu_option next_comment - comment 'Bluetooth support' - dep_tristate 'Bluetooth subsystem support' CONFIG_BLUEZ $CONFIG_NET - - if [ "$CONFIG_BLUEZ" != "n" ]; then - dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ -+ dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ -+ source net/bluetooth/rfcomm/Config.in -+ source net/bluetooth/bnep/Config.in -+ source net/bluetooth/cmtp/Config.in - source drivers/bluetooth/Config.in - fi -+ - endmenu - fi - ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/hci_conn.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,435 @@ -+/* -+ BlueZ - Bluetooth protocol stack for Linux -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * HCI Connection handling. -+ * -+ * $Id: hci_conn.c,v 1.5 2002/07/17 18:46:25 maxk Exp $ -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/kernel.h> -+#include <linux/major.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/poll.h> -+#include <linux/fcntl.h> -+#include <linux/init.h> -+#include <linux/skbuff.h> -+#include <linux/interrupt.h> -+#include <linux/notifier.h> -+#include <net/sock.h> -+ -+#include <asm/system.h> -+#include <asm/uaccess.h> -+#include <asm/unaligned.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+ -+#ifndef HCI_CORE_DEBUG -+#undef BT_DBG -+#define BT_DBG( A... ) -+#endif -+ -+void hci_acl_connect(struct hci_conn *conn) -+{ -+ struct hci_dev *hdev = conn->hdev; -+ struct inquiry_entry *ie; -+ create_conn_cp cp; -+ -+ BT_DBG("%p", conn); -+ -+ conn->state = BT_CONNECT; -+ conn->out = 1; -+ conn->link_mode = HCI_LM_MASTER; -+ -+ memset(&cp, 0, sizeof(cp)); -+ bacpy(&cp.bdaddr, &conn->dst); -+ cp.pscan_rep_mode = 0x02; -+ -+ if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) && -+ inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { -+ cp.pscan_rep_mode = ie->info.pscan_rep_mode; -+ cp.pscan_mode = ie->info.pscan_mode; -+ cp.clock_offset = ie->info.clock_offset | __cpu_to_le16(0x8000); -+ } -+ -+ cp.pkt_type = __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK); -+ if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER)) -+ cp.role_switch = 0x01; -+ else -+ cp.role_switch = 0x00; -+ -+ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN, -+ CREATE_CONN_CP_SIZE, &cp); -+} -+ -+void hci_acl_disconn(struct hci_conn *conn, __u8 reason) -+{ -+ disconnect_cp cp; -+ -+ BT_DBG("%p", conn); -+ -+ conn->state = BT_DISCONN; -+ -+ cp.handle = __cpu_to_le16(conn->handle); -+ cp.reason = reason; -+ hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT, -+ DISCONNECT_CP_SIZE, &cp); -+} -+ -+void hci_add_sco(struct hci_conn *conn, __u16 handle) -+{ -+ struct hci_dev *hdev = conn->hdev; -+ add_sco_cp cp; -+ -+ BT_DBG("%p", conn); -+ -+ conn->state = BT_CONNECT; -+ conn->out = 1; -+ -+ cp.pkt_type = __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); -+ cp.handle = __cpu_to_le16(handle); -+ -+ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ADD_SCO, ADD_SCO_CP_SIZE, &cp); -+} -+ -+static void hci_conn_timeout(unsigned long arg) -+{ -+ struct hci_conn *conn = (void *)arg; -+ struct hci_dev *hdev = conn->hdev; -+ -+ BT_DBG("conn %p state %d", conn, conn->state); -+ -+ if (atomic_read(&conn->refcnt)) -+ return; -+ -+ hci_dev_lock(hdev); -+ if (conn->state == BT_CONNECTED) -+ hci_acl_disconn(conn, 0x13); -+ else -+ conn->state = BT_CLOSED; -+ hci_dev_unlock(hdev); -+ return; -+} -+ -+static void hci_conn_init_timer(struct hci_conn *conn) -+{ -+ init_timer(&conn->timer); -+ conn->timer.function = hci_conn_timeout; -+ conn->timer.data = (unsigned long)conn; -+} -+ -+struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) -+{ -+ struct hci_conn *conn; -+ -+ BT_DBG("%s dst %s", hdev->name, batostr(dst)); -+ -+ if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC))) -+ return NULL; -+ memset(conn, 0, sizeof(struct hci_conn)); -+ -+ bacpy(&conn->dst, dst); -+ conn->type = type; -+ conn->hdev = hdev; -+ conn->state = BT_OPEN; -+ -+ skb_queue_head_init(&conn->data_q); -+ hci_conn_init_timer(conn); -+ -+ atomic_set(&conn->refcnt, 0); -+ -+ hci_dev_hold(hdev); -+ -+ tasklet_disable(&hdev->tx_task); -+ conn_hash_add(hdev, conn); -+ tasklet_enable(&hdev->tx_task); -+ -+ return conn; -+} -+ -+int hci_conn_del(struct hci_conn *conn) -+{ -+ struct hci_dev *hdev = conn->hdev; -+ -+ BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); -+ -+ hci_conn_del_timer(conn); -+ -+ if (conn->type == SCO_LINK) { -+ struct hci_conn *acl = conn->link; -+ if (acl) { -+ acl->link = NULL; -+ hci_conn_put(acl); -+ } -+ } else { -+ struct hci_conn *sco = conn->link; -+ if (sco) -+ sco->link = NULL; -+ -+ /* Unacked frames */ -+ hdev->acl_cnt += conn->sent; -+ } -+ -+ tasklet_disable(&hdev->tx_task); -+ conn_hash_del(hdev, conn); -+ tasklet_enable(&hdev->tx_task); -+ -+ skb_queue_purge(&conn->data_q); -+ -+ hci_dev_put(hdev); -+ -+ kfree(conn); -+ return 0; -+} -+ -+struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) -+{ -+ int use_src = bacmp(src, BDADDR_ANY); -+ struct hci_dev *hdev = NULL; -+ struct list_head *p; -+ -+ BT_DBG("%s -> %s", batostr(src), batostr(dst)); -+ -+ read_lock_bh(&hdev_list_lock); -+ -+ list_for_each(p, &hdev_list) { -+ struct hci_dev *d; -+ d = list_entry(p, struct hci_dev, list); -+ -+ if (!test_bit(HCI_UP, &d->flags)) -+ continue; -+ -+ /* Simple routing: -+ * No source address - find interface with bdaddr != dst -+ * Source address - find interface with bdaddr == src -+ */ -+ -+ if (use_src) { -+ if (!bacmp(&d->bdaddr, src)) { -+ hdev = d; break; -+ } -+ } else { -+ if (bacmp(&d->bdaddr, dst)) { -+ hdev = d; break; -+ } -+ } -+ } -+ -+ if (hdev) -+ hci_dev_hold(hdev); -+ -+ read_unlock_bh(&hdev_list_lock); -+ return hdev; -+} -+ -+/* Create SCO or ACL connection. -+ * Device _must_ be locked */ -+struct hci_conn * hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst) -+{ -+ struct hci_conn *acl; -+ -+ BT_DBG("%s dst %s", hdev->name, batostr(dst)); -+ -+ if (!(acl = conn_hash_lookup_ba(hdev, ACL_LINK, dst))) { -+ if (!(acl = hci_conn_add(hdev, ACL_LINK, dst))) -+ return NULL; -+ } -+ -+ hci_conn_hold(acl); -+ -+ if (acl->state == BT_OPEN || acl->state == BT_CLOSED) -+ hci_acl_connect(acl); -+ -+ if (type == SCO_LINK) { -+ struct hci_conn *sco; -+ -+ if (!(sco = conn_hash_lookup_ba(hdev, SCO_LINK, dst))) { -+ if (!(sco = hci_conn_add(hdev, SCO_LINK, dst))) { -+ hci_conn_put(acl); -+ return NULL; -+ } -+ } -+ acl->link = sco; -+ sco->link = acl; -+ -+ hci_conn_hold(sco); -+ -+ if (acl->state == BT_CONNECTED && -+ (sco->state == BT_OPEN || sco->state == BT_CLOSED)) -+ hci_add_sco(sco, acl->handle); -+ -+ return sco; -+ } else { -+ return acl; -+ } -+} -+ -+/* Authenticate remote device */ -+int hci_conn_auth(struct hci_conn *conn) -+{ -+ BT_DBG("conn %p", conn); -+ -+ if (conn->link_mode & HCI_LM_AUTH) -+ return 1; -+ -+ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { -+ auth_requested_cp ar; -+ ar.handle = __cpu_to_le16(conn->handle); -+ hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_AUTH_REQUESTED, -+ AUTH_REQUESTED_CP_SIZE, &ar); -+ } -+ return 0; -+} -+ -+/* Enable encryption */ -+int hci_conn_encrypt(struct hci_conn *conn) -+{ -+ BT_DBG("conn %p", conn); -+ -+ if (conn->link_mode & HCI_LM_ENCRYPT) -+ return 1; -+ -+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) -+ return 0; -+ -+ if (hci_conn_auth(conn)) { -+ set_conn_encrypt_cp ce; -+ ce.handle = __cpu_to_le16(conn->handle); -+ ce.encrypt = 1; -+ hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT, -+ SET_CONN_ENCRYPT_CP_SIZE, &ce); -+ } -+ return 0; -+} -+ -+/* Drop all connection on the device */ -+void hci_conn_hash_flush(struct hci_dev *hdev) -+{ -+ struct conn_hash *h = &hdev->conn_hash; -+ struct list_head *p; -+ -+ BT_DBG("hdev %s", hdev->name); -+ -+ p = h->list.next; -+ while (p != &h->list) { -+ struct hci_conn *c; -+ -+ c = list_entry(p, struct hci_conn, list); -+ p = p->next; -+ -+ c->state = BT_CLOSED; -+ -+ hci_proto_disconn_ind(c, 0x16); -+ hci_conn_del(c); -+ } -+} -+ -+int hci_get_conn_list(unsigned long arg) -+{ -+ struct hci_conn_list_req req, *cl; -+ struct hci_conn_info *ci; -+ struct hci_dev *hdev; -+ struct list_head *p; -+ int n = 0, size; -+ -+ if (copy_from_user(&req, (void *) arg, sizeof(req))) -+ return -EFAULT; -+ -+ if (!(hdev = hci_dev_get(req.dev_id))) -+ return -ENODEV; -+ -+ size = req.conn_num * sizeof(struct hci_conn_info) + sizeof(req); -+ -+ if (verify_area(VERIFY_WRITE, (void *)arg, size)) -+ return -EFAULT; -+ -+ if (!(cl = (void *) kmalloc(size, GFP_KERNEL))) -+ return -ENOMEM; -+ ci = cl->conn_info; -+ -+ hci_dev_lock_bh(hdev); -+ list_for_each(p, &hdev->conn_hash.list) { -+ register struct hci_conn *c; -+ c = list_entry(p, struct hci_conn, list); -+ -+ bacpy(&(ci + n)->bdaddr, &c->dst); -+ (ci + n)->handle = c->handle; -+ (ci + n)->type = c->type; -+ (ci + n)->out = c->out; -+ (ci + n)->state = c->state; -+ (ci + n)->link_mode = c->link_mode; -+ n++; -+ } -+ hci_dev_unlock_bh(hdev); -+ -+ cl->dev_id = hdev->id; -+ cl->conn_num = n; -+ size = n * sizeof(struct hci_conn_info) + sizeof(req); -+ -+ hci_dev_put(hdev); -+ -+ copy_to_user((void *) arg, cl, size); -+ kfree(cl); -+ -+ return 0; -+} -+ -+int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg) -+{ -+ struct hci_conn_info_req req; -+ struct hci_conn_info ci; -+ struct hci_conn *conn; -+ char *ptr = (void *) arg + sizeof(req); -+ -+ if (copy_from_user(&req, (void *) arg, sizeof(req))) -+ return -EFAULT; -+ -+ if (verify_area(VERIFY_WRITE, ptr, sizeof(ci))) -+ return -EFAULT; -+ -+ hci_dev_lock_bh(hdev); -+ conn = conn_hash_lookup_ba(hdev, req.type, &req.bdaddr); -+ if (conn) { -+ bacpy(&ci.bdaddr, &conn->dst); -+ ci.handle = conn->handle; -+ ci.type = conn->type; -+ ci.out = conn->out; -+ ci.state = conn->state; -+ ci.link_mode = conn->link_mode; -+ } -+ hci_dev_unlock_bh(hdev); -+ -+ if (!conn) -+ return -ENOENT; -+ -+ copy_to_user(ptr, &ci, sizeof(ci)); -+ return 0; -+} ---- linux/net/bluetooth/hci_core.c~bluetooth-2.4.18-mh11 2001-11-09 23:21:21.000000000 +0100 -+++ linux/net/bluetooth/hci_core.c 2004-01-25 23:37:39.000000000 +0100 -@@ -25,11 +25,12 @@ - /* - * BlueZ HCI Core. - * -- * $Id: hci_core.c,v 1.22 2001/08/03 04:19:50 maxk Exp $ -+ * $Id: hci_core.c,v 1.14 2002/08/26 16:57:57 maxk Exp $ - */ - - #include <linux/config.h> - #include <linux/module.h> -+#include <linux/kmod.h> - - #include <linux/types.h> - #include <linux/errno.h> -@@ -50,12 +51,11 @@ - #include <asm/unaligned.h> - - #include <net/bluetooth/bluetooth.h> --#include <net/bluetooth/bluez.h> - #include <net/bluetooth/hci_core.h> - - #ifndef HCI_CORE_DEBUG --#undef DBG --#define DBG( A... ) -+#undef BT_DBG -+#define BT_DBG( A... ) - #endif - - static void hci_cmd_task(unsigned long arg); -@@ -63,279 +63,69 @@ - static void hci_tx_task(unsigned long arg); - static void hci_notify(struct hci_dev *hdev, int event); - --static rwlock_t hci_task_lock = RW_LOCK_UNLOCKED; -+rwlock_t hci_task_lock = RW_LOCK_UNLOCKED; - - /* HCI device list */ --struct hci_dev *hdev_list[HCI_MAX_DEV]; --spinlock_t hdev_list_lock; --#define GET_HDEV(a) (hdev_list[a]) -+LIST_HEAD(hdev_list); -+rwlock_t hdev_list_lock = RW_LOCK_UNLOCKED; - --/* HCI protocol list */ --struct hci_proto *hproto_list[HCI_MAX_PROTO]; --#define GET_HPROTO(a) (hproto_list[a]) -+/* HCI protocols */ -+#define HCI_MAX_PROTO 2 -+struct hci_proto *hci_proto[HCI_MAX_PROTO]; - - /* HCI notifiers list */ --struct notifier_block *hci_dev_notifier; -- --/* HCI device notifications */ --int hci_register_notifier(struct notifier_block *nb) --{ -- int err, i; -- struct hci_dev *hdev; -- -- if ((err = notifier_chain_register(&hci_dev_notifier, nb))) -- return err; -- -- /* Notify about already registered devices */ -- spin_lock(&hdev_list_lock); -- for (i = 0; i < HCI_MAX_DEV; i++) { -- if (!(hdev = GET_HDEV(i))) -- continue; -- if (hdev->flags & HCI_UP) -- (*nb->notifier_call)(nb, HCI_DEV_UP, hdev); -- } -- spin_unlock(&hdev_list_lock); -- -- return 0; --} -- --int hci_unregister_notifier(struct notifier_block *nb) --{ -- return notifier_chain_unregister(&hci_dev_notifier, nb); --} -- --static inline void hci_notify(struct hci_dev *hdev, int event) --{ -- notifier_call_chain(&hci_dev_notifier, event, hdev); --} -- --/* Get HCI device by index (device is locked on return)*/ --struct hci_dev *hci_dev_get(int index) --{ -- struct hci_dev *hdev; -- DBG("%d", index); -- -- if (index < 0 || index >= HCI_MAX_DEV) -- return NULL; -- -- spin_lock(&hdev_list_lock); -- if ((hdev = GET_HDEV(index))) -- hci_dev_hold(hdev); -- spin_unlock(&hdev_list_lock); -- -- return hdev; --} -- --/* Flush inquiry cache */ --void inquiry_cache_flush(struct inquiry_cache *cache) --{ -- struct inquiry_entry *next = cache->list, *e; -- -- DBG("cache %p", cache); -- -- cache->list = NULL; -- while ((e = next)) { -- next = e->next; -- kfree(e); -- } --} -- --/* Lookup by bdaddr. -- * Cache must be locked. */ --static struct inquiry_entry * __inquiry_cache_lookup(struct inquiry_cache *cache, bdaddr_t *bdaddr) --{ -- struct inquiry_entry *e; -- -- DBG("cache %p, %s", cache, batostr(bdaddr)); -- -- for (e = cache->list; e; e = e->next) -- if (!bacmp(&e->info.bdaddr, bdaddr)) -- break; -- -- return e; --} -- --static void inquiry_cache_update(struct inquiry_cache *cache, inquiry_info *info) --{ -- struct inquiry_entry *e; -- -- DBG("cache %p, %s", cache, batostr(&info->bdaddr)); -- -- inquiry_cache_lock(cache); -- -- if (!(e = __inquiry_cache_lookup(cache, &info->bdaddr))) { -- /* Entry not in the cache. Add new one. */ -- if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC))) -- goto unlock; -- memset(e, 0, sizeof(struct inquiry_entry)); -- e->next = cache->list; -- cache->list = e; -- } -- -- memcpy(&e->info, info, sizeof(inquiry_info)); -- e->timestamp = jiffies; -- cache->timestamp = jiffies; --unlock: -- inquiry_cache_unlock(cache); --} -- --static int inquiry_cache_dump(struct inquiry_cache *cache, int num, __u8 *buf) --{ -- inquiry_info *info = (inquiry_info *) buf; -- struct inquiry_entry *e; -- int copied = 0; -- -- inquiry_cache_lock(cache); -- -- for (e = cache->list; e && copied < num; e = e->next, copied++) -- memcpy(info++, &e->info, sizeof(inquiry_info)); -+static struct notifier_block *hci_notifier; - -- inquiry_cache_unlock(cache); - -- DBG("cache %p, copied %d", cache, copied); -- return copied; --} -+/* ---- HCI notifications ---- */ - --/* --------- BaseBand connections --------- */ --static struct hci_conn *hci_conn_add(struct hci_dev *hdev, __u16 handle, __u8 type, bdaddr_t *dst) -+int hci_register_notifier(struct notifier_block *nb) - { -- struct hci_conn *conn; -- -- DBG("%s handle %d dst %s", hdev->name, handle, batostr(dst)); -- -- if ( conn_hash_lookup(&hdev->conn_hash, handle)) { -- ERR("%s handle 0x%x already exists", hdev->name, handle); -- return NULL; -- } -- -- if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC))) -- return NULL; -- memset(conn, 0, sizeof(struct hci_conn)); -- -- bacpy(&conn->dst, dst); -- conn->handle = handle; -- conn->type = type; -- conn->hdev = hdev; -- -- skb_queue_head_init(&conn->data_q); -- -- hci_dev_hold(hdev); -- conn_hash_add(&hdev->conn_hash, handle, conn); -- -- return conn; -+ return notifier_chain_register(&hci_notifier, nb); - } - --static int hci_conn_del(struct hci_dev *hdev, struct hci_conn *conn) -+int hci_unregister_notifier(struct notifier_block *nb) - { -- DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); -- -- conn_hash_del(&hdev->conn_hash, conn); -- hci_dev_put(hdev); -- -- /* Unacked frames */ -- hdev->acl_cnt += conn->sent; -- -- skb_queue_purge(&conn->data_q); -- -- kfree(conn); -- return 0; -+ return notifier_chain_unregister(&hci_notifier, nb); - } - --/* Drop all connection on the device */ --static void hci_conn_hash_flush(struct hci_dev *hdev) -+void hci_notify(struct hci_dev *hdev, int event) - { -- struct conn_hash *h = &hdev->conn_hash; -- struct hci_proto *hp; -- struct list_head *p; -- -- DBG("hdev %s", hdev->name); -- -- p = h->list.next; -- while (p != &h->list) { -- struct hci_conn *c; -- -- c = list_entry(p, struct hci_conn, list); -- p = p->next; -- -- if (c->type == ACL_LINK) { -- /* ACL link notify L2CAP layer */ -- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->disconn_ind) -- hp->disconn_ind(c, 0x16); -- } else { -- /* SCO link (no notification) */ -- } -- -- hci_conn_del(hdev, c); -- } -+ notifier_call_chain(&hci_notifier, event, hdev); - } - --int hci_connect(struct hci_dev *hdev, bdaddr_t *bdaddr) --{ -- struct inquiry_cache *cache = &hdev->inq_cache; -- struct inquiry_entry *e; -- create_conn_cp cc; -- __u16 clock_offset; -- -- DBG("%s bdaddr %s", hdev->name, batostr(bdaddr)); -- -- if (!(hdev->flags & HCI_UP)) -- return -ENODEV; -- -- inquiry_cache_lock_bh(cache); -- -- if (!(e = __inquiry_cache_lookup(cache, bdaddr)) || inquiry_entry_age(e) > INQUIRY_ENTRY_AGE_MAX) { -- cc.pscan_rep_mode = 0; -- cc.pscan_mode = 0; -- clock_offset = 0; -- } else { -- cc.pscan_rep_mode = e->info.pscan_rep_mode; -- cc.pscan_mode = e->info.pscan_mode; -- clock_offset = __le16_to_cpu(e->info.clock_offset) & 0x8000; -- } -- -- inquiry_cache_unlock_bh(cache); -- -- bacpy(&cc.bdaddr, bdaddr); -- cc.pkt_type = __cpu_to_le16(hdev->pkt_type); -- cc.clock_offset = __cpu_to_le16(clock_offset); -- -- if (lmp_rswitch_capable(hdev)) -- cc.role_switch = 0x01; -- else -- cc.role_switch = 0x00; -- -- hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN, CREATE_CONN_CP_SIZE, &cc); -+/* ---- HCI hotplug support ---- */ - -- return 0; --} -+#ifdef CONFIG_HOTPLUG - --int hci_disconnect(struct hci_conn *conn, __u8 reason) -+static int hci_run_hotplug(char *dev, char *action) - { -- disconnect_cp dc; -- -- DBG("conn %p handle %d", conn, conn->handle); -+ char *argv[3], *envp[5], dstr[20], astr[32]; - -- dc.handle = __cpu_to_le16(conn->handle); -- dc.reason = reason; -- hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT, DISCONNECT_CP_SIZE, &dc); -+ sprintf(dstr, "DEVICE=%s", dev); -+ sprintf(astr, "ACTION=%s", action); - -- return 0; --} -+ argv[0] = hotplug_path; -+ argv[1] = "bluetooth"; -+ argv[2] = NULL; - --/* --------- HCI request handling ------------ */ --static inline void hci_req_lock(struct hci_dev *hdev) --{ -- down(&hdev->req_lock); -+ envp[0] = "HOME=/"; -+ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; -+ envp[2] = dstr; -+ envp[3] = astr; -+ envp[4] = NULL; -+ -+ return call_usermodehelper(argv[0], argv, envp); - } -+#else -+#define hci_run_hotplug(A...) -+#endif - --static inline void hci_req_unlock(struct hci_dev *hdev) --{ -- up(&hdev->req_lock); --} -+/* ---- HCI requests ---- */ - --static inline void hci_req_complete(struct hci_dev *hdev, int result) -+void hci_req_complete(struct hci_dev *hdev, int result) - { -- DBG("%s result 0x%2.2x", hdev->name, result); -+ BT_DBG("%s result 0x%2.2x", hdev->name, result); - - if (hdev->req_status == HCI_REQ_PEND) { - hdev->req_result = result; -@@ -344,9 +134,9 @@ - } - } - --static inline void hci_req_cancel(struct hci_dev *hdev, int err) -+void hci_req_cancel(struct hci_dev *hdev, int err) - { -- DBG("%s err 0x%2.2x", hdev->name, err); -+ BT_DBG("%s err 0x%2.2x", hdev->name, err); - - if (hdev->req_status == HCI_REQ_PEND) { - hdev->req_result = err; -@@ -356,23 +146,22 @@ - } - - /* Execute request and wait for completion. */ --static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), -- unsigned long opt, __u32 timeout) -+static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), unsigned long opt, __u32 timeout) - { - DECLARE_WAITQUEUE(wait, current); - int err = 0; - -- DBG("%s start", hdev->name); -+ BT_DBG("%s start", hdev->name); - - hdev->req_status = HCI_REQ_PEND; - - add_wait_queue(&hdev->req_wait_q, &wait); -- current->state = TASK_INTERRUPTIBLE; -+ set_current_state(TASK_INTERRUPTIBLE); - - req(hdev, opt); - schedule_timeout(timeout); - -- current->state = TASK_RUNNING; -+ set_current_state(TASK_RUNNING); - remove_wait_queue(&hdev->req_wait_q, &wait); - - if (signal_pending(current)) -@@ -394,7 +183,7 @@ - - hdev->req_status = hdev->req_result = 0; - -- DBG("%s end: err %d", hdev->name, err); -+ BT_DBG("%s end: err %d", hdev->name, err); - - return err; - } -@@ -412,10 +201,9 @@ - return ret; - } - --/* --------- HCI requests ---------- */ - static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) - { -- DBG("%s %ld", hdev->name, opt); -+ BT_DBG("%s %ld", hdev->name, opt); - - /* Reset device */ - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL); -@@ -423,10 +211,10 @@ - - static void hci_init_req(struct hci_dev *hdev, unsigned long opt) - { -- set_event_flt_cp ec; -+ set_event_flt_cp ef; - __u16 param; - -- DBG("%s %ld", hdev->name, opt); -+ BT_DBG("%s %ld", hdev->name, opt); - - /* Mandatory initialization */ - -@@ -436,14 +224,27 @@ - /* Read Buffer Size (ACL mtu, max pkt, etc.) */ - hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE, 0, NULL); - -+#if 0 -+ /* Host buffer size */ -+ { -+ host_buffer_size_cp bs; -+ bs.acl_mtu = __cpu_to_le16(HCI_MAX_ACL_SIZE); -+ bs.sco_mtu = HCI_MAX_SCO_SIZE; -+ bs.acl_max_pkt = __cpu_to_le16(0xffff); -+ bs.sco_max_pkt = __cpu_to_le16(0xffff); -+ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_HOST_BUFFER_SIZE, -+ HOST_BUFFER_SIZE_CP_SIZE, &bs); -+ } -+#endif -+ - /* Read BD Address */ - hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BD_ADDR, 0, NULL); - - /* Optional initialization */ - - /* Clear Event Filters */ -- ec.flt_type = FLT_CLEAR_ALL; -- hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &ec); -+ ef.flt_type = FLT_CLEAR_ALL; -+ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &ef); - - /* Page timeout ~20 secs */ - param = __cpu_to_le16(0x8000); -@@ -458,7 +259,7 @@ - { - __u8 scan = opt; - -- DBG("%s %x", hdev->name, scan); -+ BT_DBG("%s %x", hdev->name, scan); - - /* Inquiry and Page scans */ - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE, 1, &scan); -@@ -468,27 +269,190 @@ - { - __u8 auth = opt; - -- DBG("%s %x", hdev->name, auth); -+ BT_DBG("%s %x", hdev->name, auth); - - /* Authentication */ - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE, 1, &auth); - } - -+static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt) -+{ -+ __u8 encrypt = opt; -+ -+ BT_DBG("%s %x", hdev->name, encrypt); -+ -+ /* Authentication */ -+ hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE, 1, &encrypt); -+} -+ -+/* Get HCI device by index. -+ * Device is locked on return. */ -+struct hci_dev *hci_dev_get(int index) -+{ -+ struct hci_dev *hdev; -+ struct list_head *p; -+ -+ BT_DBG("%d", index); -+ -+ if (index < 0) -+ return NULL; -+ -+ read_lock(&hdev_list_lock); -+ list_for_each(p, &hdev_list) { -+ hdev = list_entry(p, struct hci_dev, list); -+ if (hdev->id == index) { -+ hci_dev_hold(hdev); -+ goto done; -+ } -+ } -+ hdev = NULL; -+done: -+ read_unlock(&hdev_list_lock); -+ return hdev; -+} -+ -+/* ---- Inquiry support ---- */ -+void inquiry_cache_flush(struct hci_dev *hdev) -+{ -+ struct inquiry_cache *cache = &hdev->inq_cache; -+ struct inquiry_entry *next = cache->list, *e; -+ -+ BT_DBG("cache %p", cache); -+ -+ cache->list = NULL; -+ while ((e = next)) { -+ next = e->next; -+ kfree(e); -+ } -+} -+ -+struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) -+{ -+ struct inquiry_cache *cache = &hdev->inq_cache; -+ struct inquiry_entry *e; -+ -+ BT_DBG("cache %p, %s", cache, batostr(bdaddr)); -+ -+ for (e = cache->list; e; e = e->next) -+ if (!bacmp(&e->info.bdaddr, bdaddr)) -+ break; -+ return e; -+} -+ -+void inquiry_cache_update(struct hci_dev *hdev, inquiry_info *info) -+{ -+ struct inquiry_cache *cache = &hdev->inq_cache; -+ struct inquiry_entry *e; -+ -+ BT_DBG("cache %p, %s", cache, batostr(&info->bdaddr)); -+ -+ if (!(e = inquiry_cache_lookup(hdev, &info->bdaddr))) { -+ /* Entry not in the cache. Add new one. */ -+ if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC))) -+ return; -+ memset(e, 0, sizeof(struct inquiry_entry)); -+ e->next = cache->list; -+ cache->list = e; -+ } -+ -+ memcpy(&e->info, info, sizeof(inquiry_info)); -+ e->timestamp = jiffies; -+ cache->timestamp = jiffies; -+} -+ -+int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) -+{ -+ struct inquiry_cache *cache = &hdev->inq_cache; -+ inquiry_info *info = (inquiry_info *) buf; -+ struct inquiry_entry *e; -+ int copied = 0; -+ -+ for (e = cache->list; e && copied < num; e = e->next, copied++) -+ memcpy(info++, &e->info, sizeof(inquiry_info)); -+ -+ BT_DBG("cache %p, copied %d", cache, copied); -+ return copied; -+} -+ - static void hci_inq_req(struct hci_dev *hdev, unsigned long opt) - { - struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt; - inquiry_cp ic; - -- DBG("%s", hdev->name); -+ BT_DBG("%s", hdev->name); -+ -+ if (test_bit(HCI_INQUIRY, &hdev->flags)) -+ return; - - /* Start Inquiry */ - memcpy(&ic.lap, &ir->lap, 3); -- ic.lenght = ir->length; -+ ic.length = ir->length; - ic.num_rsp = ir->num_rsp; - hci_send_cmd(hdev, OGF_LINK_CTL, OCF_INQUIRY, INQUIRY_CP_SIZE, &ic); - } - --/* HCI ioctl helpers */ -+int hci_inquiry(unsigned long arg) -+{ -+ struct hci_inquiry_req ir; -+ struct hci_dev *hdev; -+ int err = 0, do_inquiry = 0, max_rsp; -+ long timeo; -+ __u8 *buf, *ptr; -+ -+ ptr = (void *) arg; -+ if (copy_from_user(&ir, ptr, sizeof(ir))) -+ return -EFAULT; -+ -+ if (!(hdev = hci_dev_get(ir.dev_id))) -+ return -ENODEV; -+ -+ hci_dev_lock_bh(hdev); -+ if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX || -+ inquiry_cache_empty(hdev) || -+ ir.flags & IREQ_CACHE_FLUSH) { -+ inquiry_cache_flush(hdev); -+ do_inquiry = 1; -+ } -+ hci_dev_unlock_bh(hdev); -+ -+ timeo = ir.length * 2 * HZ; -+ if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0) -+ goto done; -+ -+ /* for unlimited number of responses we will use buffer with 255 entries */ -+ max_rsp = (ir.num_rsp == 0) ? 255 : ir.num_rsp; -+ -+ /* cache_dump can't sleep. Therefore we allocate temp buffer and then -+ * copy it to the user space. -+ */ -+ if (!(buf = kmalloc(sizeof(inquiry_info) * max_rsp, GFP_KERNEL))) { -+ err = -ENOMEM; -+ goto done; -+ } -+ -+ hci_dev_lock_bh(hdev); -+ ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf); -+ hci_dev_unlock_bh(hdev); -+ -+ BT_DBG("num_rsp %d", ir.num_rsp); -+ -+ if (!verify_area(VERIFY_WRITE, ptr, sizeof(ir) + -+ (sizeof(inquiry_info) * ir.num_rsp))) { -+ copy_to_user(ptr, &ir, sizeof(ir)); -+ ptr += sizeof(ir); -+ copy_to_user(ptr, buf, sizeof(inquiry_info) * ir.num_rsp); -+ } else -+ err = -EFAULT; -+ -+ kfree(buf); -+ -+done: -+ hci_dev_put(hdev); -+ return err; -+} -+ -+/* ---- HCI ioctl helpers ---- */ -+ - int hci_dev_open(__u16 dev) - { - struct hci_dev *hdev; -@@ -497,11 +461,11 @@ - if (!(hdev = hci_dev_get(dev))) - return -ENODEV; - -- DBG("%s %p", hdev->name, hdev); -+ BT_DBG("%s %p", hdev->name, hdev); - - hci_req_lock(hdev); - -- if (hdev->flags & HCI_UP) { -+ if (test_bit(HCI_UP, &hdev->flags)) { - ret = -EALREADY; - goto done; - } -@@ -511,18 +475,18 @@ - goto done; - } - -- if (hdev->flags & HCI_NORMAL) { -+ if (!test_bit(HCI_RAW, &hdev->flags)) { - atomic_set(&hdev->cmd_cnt, 1); -- hdev->flags |= HCI_INIT; -+ set_bit(HCI_INIT, &hdev->flags); - - //__hci_request(hdev, hci_reset_req, 0, HZ); - ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT); - -- hdev->flags &= ~HCI_INIT; -+ clear_bit(HCI_INIT, &hdev->flags); - } - - if (!ret) { -- hdev->flags |= HCI_UP; -+ set_bit(HCI_UP, &hdev->flags); - hci_notify(hdev, HCI_DEV_UP); - } else { - /* Init failed, cleanup */ -@@ -542,42 +506,36 @@ - } - - hdev->close(hdev); -+ hdev->flags = 0; - } - - done: - hci_req_unlock(hdev); - hci_dev_put(hdev); -- - return ret; - } - --int hci_dev_close(__u16 dev) -+static int hci_dev_do_close(struct hci_dev *hdev) - { -- struct hci_dev *hdev; -- -- if (!(hdev = hci_dev_get(dev))) -- return -ENODEV; -- -- DBG("%s %p", hdev->name, hdev); -+ BT_DBG("%s %p", hdev->name, hdev); - - hci_req_cancel(hdev, ENODEV); - hci_req_lock(hdev); - -- if (!(hdev->flags & HCI_UP)) -- goto done; -+ if (!test_and_clear_bit(HCI_UP, &hdev->flags)) { -+ hci_req_unlock(hdev); -+ return 0; -+ } - - /* Kill RX and TX tasks */ - tasklet_kill(&hdev->rx_task); - tasklet_kill(&hdev->tx_task); - -- inquiry_cache_flush(&hdev->inq_cache); -- -+ hci_dev_lock_bh(hdev); -+ inquiry_cache_flush(hdev); - hci_conn_hash_flush(hdev); -- -- /* Clear flags */ -- hdev->flags &= HCI_SOCK; -- hdev->flags |= HCI_NORMAL; -- -+ hci_dev_unlock_bh(hdev); -+ - hci_notify(hdev, HCI_DEV_DOWN); - - if (hdev->flush) -@@ -586,9 +544,9 @@ - /* Reset device */ - skb_queue_purge(&hdev->cmd_q); - atomic_set(&hdev->cmd_cnt, 1); -- hdev->flags |= HCI_INIT; -- __hci_request(hdev, hci_reset_req, 0, HZ); -- hdev->flags &= ~HCI_INIT; -+ set_bit(HCI_INIT, &hdev->flags); -+ __hci_request(hdev, hci_reset_req, 0, HZ/4); -+ clear_bit(HCI_INIT, &hdev->flags); - - /* Kill cmd task */ - tasklet_kill(&hdev->cmd_task); -@@ -605,17 +563,28 @@ - } - - /* After this point our queues are empty -- * and no tasks are scheduled. -- */ -+ * and no tasks are scheduled. */ - hdev->close(hdev); - --done: -- hci_req_unlock(hdev); -- hci_dev_put(hdev); -+ /* Clear flags */ -+ hdev->flags = 0; - -+ hci_req_unlock(hdev); - return 0; - } - -+int hci_dev_close(__u16 dev) -+{ -+ struct hci_dev *hdev; -+ int err; -+ -+ if (!(hdev = hci_dev_get(dev))) -+ return -ENODEV; -+ err = hci_dev_do_close(hdev); -+ hci_dev_put(hdev); -+ return err; -+} -+ - int hci_dev_reset(__u16 dev) - { - struct hci_dev *hdev; -@@ -627,16 +596,17 @@ - hci_req_lock(hdev); - tasklet_disable(&hdev->tx_task); - -- if (!(hdev->flags & HCI_UP)) -+ if (!test_bit(HCI_UP, &hdev->flags)) - goto done; - - /* Drop queues */ - skb_queue_purge(&hdev->rx_q); - skb_queue_purge(&hdev->cmd_q); - -- inquiry_cache_flush(&hdev->inq_cache); -- -+ hci_dev_lock_bh(hdev); -+ inquiry_cache_flush(hdev); - hci_conn_hash_flush(hdev); -+ hci_dev_unlock_bh(hdev); - - if (hdev->flush) - hdev->flush(hdev); -@@ -650,7 +620,6 @@ - tasklet_enable(&hdev->tx_task); - hci_req_unlock(hdev); - hci_dev_put(hdev); -- - return ret; - } - -@@ -669,30 +638,11 @@ - return ret; - } - --int hci_dev_setauth(unsigned long arg) --{ -- struct hci_dev *hdev; -- struct hci_dev_req dr; -- int ret = 0; -- -- if (copy_from_user(&dr, (void *) arg, sizeof(dr))) -- return -EFAULT; -- -- if (!(hdev = hci_dev_get(dr.dev_id))) -- return -ENODEV; -- -- ret = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT); -- -- hci_dev_put(hdev); -- -- return ret; --} -- --int hci_dev_setscan(unsigned long arg) -+int hci_dev_cmd(unsigned int cmd, unsigned long arg) - { - struct hci_dev *hdev; - struct hci_dev_req dr; -- int ret = 0; -+ int err = 0; - - if (copy_from_user(&dr, (void *) arg, sizeof(dr))) - return -EFAULT; -@@ -700,48 +650,78 @@ - if (!(hdev = hci_dev_get(dr.dev_id))) - return -ENODEV; - -- ret = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT); -- -- hci_dev_put(hdev); -+ switch (cmd) { -+ case HCISETAUTH: -+ err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT); -+ break; - -- return ret; --} -+ case HCISETENCRYPT: -+ if (!lmp_encrypt_capable(hdev)) { -+ err = -EOPNOTSUPP; -+ break; -+ } - --int hci_dev_setptype(unsigned long arg) --{ -- struct hci_dev *hdev; -- struct hci_dev_req dr; -- int ret = 0; -+ if (!test_bit(HCI_AUTH, &hdev->flags)) { -+ /* Auth must be enabled first */ -+ err = hci_request(hdev, hci_auth_req, -+ dr.dev_opt, HCI_INIT_TIMEOUT); -+ if (err) -+ break; -+ } -+ -+ err = hci_request(hdev, hci_encrypt_req, -+ dr.dev_opt, HCI_INIT_TIMEOUT); -+ break; -+ -+ case HCISETSCAN: -+ err = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT); -+ break; -+ -+ case HCISETPTYPE: -+ hdev->pkt_type = (__u16) dr.dev_opt; -+ break; -+ -+ case HCISETLINKPOL: -+ hdev->link_policy = (__u16) dr.dev_opt; -+ break; - -- if (copy_from_user(&dr, (void *) arg, sizeof(dr))) -- return -EFAULT; -+ case HCISETLINKMODE: -+ hdev->link_mode = ((__u16) dr.dev_opt) & (HCI_LM_MASTER | HCI_LM_ACCEPT); -+ break; - -- if (!(hdev = hci_dev_get(dr.dev_id))) -- return -ENODEV; -+ case HCISETACLMTU: -+ hdev->acl_mtu = *((__u16 *)&dr.dev_opt + 1); -+ hdev->acl_pkts = *((__u16 *)&dr.dev_opt + 0); -+ break; - -- hdev->pkt_type = (__u16) dr.dev_opt; -+ case HCISETSCOMTU: -+ hdev->sco_mtu = *((__u16 *)&dr.dev_opt + 1); -+ hdev->sco_pkts = *((__u16 *)&dr.dev_opt + 0); -+ break; - -+ default: -+ err = -EINVAL; -+ break; -+ } - hci_dev_put(hdev); -- -- return ret; -+ return err; - } - --int hci_dev_list(unsigned long arg) -+int hci_get_dev_list(unsigned long arg) - { - struct hci_dev_list_req *dl; - struct hci_dev_req *dr; -- struct hci_dev *hdev; -- int i, n, size; -+ struct list_head *p; -+ int n = 0, size; - __u16 dev_num; - - if (get_user(dev_num, (__u16 *) arg)) - return -EFAULT; - -- /* Avoid long loop, overflow */ -- if (dev_num > 2048) -+ if (!dev_num) - return -EINVAL; - -- size = dev_num * sizeof(struct hci_dev_req) + sizeof(__u16); -+ size = dev_num * sizeof(*dr) + sizeof(*dl); - - if (verify_area(VERIFY_WRITE, (void *) arg, size)) - return -EFAULT; -@@ -750,25 +730,27 @@ - return -ENOMEM; - dr = dl->dev_req; - -- spin_lock_bh(&hdev_list_lock); -- for (i = 0, n = 0; i < HCI_MAX_DEV && n < dev_num; i++) { -- if ((hdev = hdev_list[i])) { -- (dr + n)->dev_id = hdev->id; -- (dr + n)->dev_opt = hdev->flags; -- n++; -- } -+ read_lock_bh(&hdev_list_lock); -+ list_for_each(p, &hdev_list) { -+ struct hci_dev *hdev; -+ hdev = list_entry(p, struct hci_dev, list); -+ (dr + n)->dev_id = hdev->id; -+ (dr + n)->dev_opt = hdev->flags; -+ if (++n >= dev_num) -+ break; - } -- spin_unlock_bh(&hdev_list_lock); -+ read_unlock_bh(&hdev_list_lock); - - dl->dev_num = n; -- size = n * sizeof(struct hci_dev_req) + sizeof(__u16); -+ size = n * sizeof(*dr) + sizeof(*dl); - - copy_to_user((void *) arg, dl, size); -+ kfree(dl); - - return 0; - } - --int hci_dev_info(unsigned long arg) -+int hci_get_dev_info(unsigned long arg) - { - struct hci_dev *hdev; - struct hci_dev_info di; -@@ -786,9 +768,11 @@ - di.flags = hdev->flags; - di.pkt_type = hdev->pkt_type; - di.acl_mtu = hdev->acl_mtu; -- di.acl_max = hdev->acl_max; -+ di.acl_pkts = hdev->acl_pkts; - di.sco_mtu = hdev->sco_mtu; -- di.sco_max = hdev->sco_max; -+ di.sco_pkts = hdev->sco_pkts; -+ di.link_policy = hdev->link_policy; -+ di.link_mode = hdev->link_mode; - - memcpy(&di.stat, &hdev->stat, sizeof(di.stat)); - memcpy(&di.features, &hdev->features, sizeof(di.features)); -@@ -801,258 +785,168 @@ - return err; - } - --__u32 hci_dev_setmode(struct hci_dev *hdev, __u32 mode) --{ -- __u32 omode = hdev->flags & HCI_MODE_MASK; -- -- hdev->flags &= ~HCI_MODE_MASK; -- hdev->flags |= (mode & HCI_MODE_MASK); - -- return omode; --} -- --__u32 hci_dev_getmode(struct hci_dev *hdev) --{ -- return hdev->flags & HCI_MODE_MASK; --} -+/* ---- Interface to HCI drivers ---- */ - --int hci_conn_list(unsigned long arg) -+/* Register HCI device */ -+int hci_register_dev(struct hci_dev *hdev) - { -- struct hci_conn_list_req req, *cl; -- struct hci_conn_info *ci; -- struct hci_dev *hdev; -- struct list_head *p; -- int n = 0, size; -- -- if (copy_from_user(&req, (void *) arg, sizeof(req))) -- return -EFAULT; -+ struct list_head *head = &hdev_list, *p; -+ int id = 0; - -- if (!(hdev = hci_dev_get(req.dev_id))) -- return -ENODEV; -+ BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); - -- /* Set a limit to avoid overlong loops, and also numeric overflow - AC */ -- if(req.conn_num < 2048) -+ if (!hdev->open || !hdev->close || !hdev->destruct) - return -EINVAL; -- -- size = req.conn_num * sizeof(struct hci_conn_info) + sizeof(req); - -- if (!(cl = kmalloc(size, GFP_KERNEL))) -- return -ENOMEM; -- ci = cl->conn_info; -- -- local_bh_disable(); -- conn_hash_lock(&hdev->conn_hash); -- list_for_each(p, &hdev->conn_hash.list) { -- register struct hci_conn *c; -- c = list_entry(p, struct hci_conn, list); -+ write_lock_bh(&hdev_list_lock); - -- (ci + n)->handle = c->handle; -- bacpy(&(ci + n)->bdaddr, &c->dst); -- n++; -+ /* Find first available device id */ -+ list_for_each(p, &hdev_list) { -+ if (list_entry(p, struct hci_dev, list)->id != id) -+ break; -+ head = p; id++; - } -- conn_hash_unlock(&hdev->conn_hash); -- local_bh_enable(); -- -- cl->dev_id = hdev->id; -- cl->conn_num = n; -- size = n * sizeof(struct hci_conn_info) + sizeof(req); -- -- hci_dev_put(hdev); -- -- if(copy_to_user((void *) arg, cl, size)) -- return -EFAULT; -- return 0; --} -- --int hci_inquiry(unsigned long arg) --{ -- struct inquiry_cache *cache; -- struct hci_inquiry_req ir; -- struct hci_dev *hdev; -- int err = 0, do_inquiry = 0; -- long timeo; -- __u8 *buf, *ptr; -- -- ptr = (void *) arg; -- if (copy_from_user(&ir, ptr, sizeof(ir))) -- return -EFAULT; -+ -+ sprintf(hdev->name, "hci%d", id); -+ hdev->id = id; -+ list_add(&hdev->list, head); - -- if (!(hdev = hci_dev_get(ir.dev_id))) -- return -ENODEV; -+ atomic_set(&hdev->refcnt, 1); -+ spin_lock_init(&hdev->lock); -+ -+ hdev->flags = 0; -+ hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); -+ hdev->link_mode = (HCI_LM_ACCEPT); - -- cache = &hdev->inq_cache; -+ tasklet_init(&hdev->cmd_task, hci_cmd_task,(unsigned long) hdev); -+ tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev); -+ tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev); - -- inquiry_cache_lock(cache); -- if (inquiry_cache_age(cache) > INQUIRY_CACHE_AGE_MAX || ir.flags & IREQ_CACHE_FLUSH) { -- inquiry_cache_flush(cache); -- do_inquiry = 1; -- } -- inquiry_cache_unlock(cache); -+ skb_queue_head_init(&hdev->rx_q); -+ skb_queue_head_init(&hdev->cmd_q); -+ skb_queue_head_init(&hdev->raw_q); - -- /* Limit inquiry time, also avoid overflows */ -+ init_waitqueue_head(&hdev->req_wait_q); -+ init_MUTEX(&hdev->req_lock); - -- if(ir.length > 2048 || ir.num_rsp > 2048) -- { -- err = -EINVAL; -- goto done; -- } -+ inquiry_cache_init(hdev); - -- timeo = ir.length * 2 * HZ; -- if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0) -- goto done; -+ conn_hash_init(hdev); - -- /* cache_dump can't sleep. Therefore we allocate temp buffer and then -- * copy it to the user space. -- */ -- if (!(buf = kmalloc(sizeof(inquiry_info) * ir.num_rsp, GFP_KERNEL))) { -- err = -ENOMEM; -- goto done; -- } -- ir.num_rsp = inquiry_cache_dump(cache, ir.num_rsp, buf); -+ memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); - -- DBG("num_rsp %d", ir.num_rsp); -+ atomic_set(&hdev->promisc, 0); - -- if (!verify_area(VERIFY_WRITE, ptr, sizeof(ir) + (sizeof(inquiry_info) * ir.num_rsp))) { -- copy_to_user(ptr, &ir, sizeof(ir)); -- ptr += sizeof(ir); -- copy_to_user(ptr, buf, sizeof(inquiry_info) * ir.num_rsp); -- } else -- err = -EFAULT; -+ MOD_INC_USE_COUNT; - -- kfree(buf); -+ write_unlock_bh(&hdev_list_lock); - --done: -- hci_dev_put(hdev); -+ hci_notify(hdev, HCI_DEV_REG); -+ hci_run_hotplug(hdev->name, "register"); - -- return err; -+ return id; - } - --/* Interface to HCI drivers */ -- --/* Register HCI device */ --int hci_register_dev(struct hci_dev *hdev) -+/* Unregister HCI device */ -+int hci_unregister_dev(struct hci_dev *hdev) - { -- int i; -- -- DBG("%p name %s type %d", hdev, hdev->name, hdev->type); -- -- /* Find free slot */ -- spin_lock_bh(&hdev_list_lock); -- for (i = 0; i < HCI_MAX_DEV; i++) { -- if (!hdev_list[i]) { -- hdev_list[i] = hdev; -- -- sprintf(hdev->name, "hci%d", i); -- atomic_set(&hdev->refcnt, 0); -- hdev->id = i; -- hdev->flags = HCI_NORMAL; -- -- hdev->pkt_type = (HCI_DM1 | HCI_DH1); -- -- tasklet_init(&hdev->cmd_task, hci_cmd_task, (unsigned long) hdev); -- tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev); -- tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev); -- -- skb_queue_head_init(&hdev->rx_q); -- skb_queue_head_init(&hdev->cmd_q); -- skb_queue_head_init(&hdev->raw_q); -- -- init_waitqueue_head(&hdev->req_wait_q); -- init_MUTEX(&hdev->req_lock); -- -- inquiry_cache_init(&hdev->inq_cache); -+ BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); - -- conn_hash_init(&hdev->conn_hash); -+ write_lock_bh(&hdev_list_lock); -+ list_del(&hdev->list); -+ write_unlock_bh(&hdev_list_lock); - -- memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); -+ hci_dev_do_close(hdev); - -- hci_notify(hdev, HCI_DEV_REG); -+ hci_notify(hdev, HCI_DEV_UNREG); -+ hci_run_hotplug(hdev->name, "unregister"); - -- MOD_INC_USE_COUNT; -- break; -- } -- } -- spin_unlock_bh(&hdev_list_lock); -+ hci_dev_put(hdev); - -- return (i == HCI_MAX_DEV) ? -1 : i; -+ MOD_DEC_USE_COUNT; -+ return 0; - } - --/* Unregister HCI device */ --int hci_unregister_dev(struct hci_dev *hdev) -+/* Suspend HCI device */ -+int hci_suspend_dev(struct hci_dev *hdev) - { -- int i; -+ hci_notify(hdev, HCI_DEV_SUSPEND); -+ hci_run_hotplug(hdev->name, "suspend"); -+ return 0; -+} - -- DBG("%p name %s type %d", hdev, hdev->name, hdev->type); -+/* Resume HCI device */ -+int hci_resume_dev(struct hci_dev *hdev) -+{ -+ hci_notify(hdev, HCI_DEV_RESUME); -+ hci_run_hotplug(hdev->name, "resume"); -+ return 0; -+} - -- if (hdev->flags & HCI_UP) -- hci_dev_close(hdev->id); -+/* Receive frame from HCI drivers */ -+int hci_recv_frame(struct sk_buff *skb) -+{ -+ struct hci_dev *hdev = (struct hci_dev *) skb->dev; - -- /* Find device slot */ -- spin_lock(&hdev_list_lock); -- for (i = 0; i < HCI_MAX_DEV; i++) { -- if (hdev_list[i] == hdev) { -- hdev_list[i] = NULL; -- MOD_DEC_USE_COUNT; -- break; -- } -+ if (!hdev || (!test_bit(HCI_UP, &hdev->flags) && -+ !test_bit(HCI_INIT, &hdev->flags)) ) { -+ kfree_skb(skb); -+ return -1; - } -- spin_unlock(&hdev_list_lock); - -- hci_notify(hdev, HCI_DEV_UNREG); -- -- /* Sleep while device is in use */ -- while (atomic_read(&hdev->refcnt)) { -- int sleep_cnt = 100; -+ BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); - -- DBG("%s sleeping on lock %d", hdev->name, atomic_read(&hdev->refcnt)); -+ /* Incomming skb */ -+ bluez_cb(skb)->incomming = 1; - -- sleep_on_timeout(&hdev->req_wait_q, HZ*10); -- if (!(--sleep_cnt)) -- break; -- } -+ /* Time stamp */ -+ do_gettimeofday(&skb->stamp); - -+ /* Queue frame for rx task */ -+ skb_queue_tail(&hdev->rx_q, skb); -+ hci_sched_rx(hdev); - return 0; - } - --/* Interface to upper protocols */ -+/* ---- Interface to upper protocols ---- */ - - /* Register/Unregister protocols. -- * hci_task_lock is used to ensure that no tasks are running. -- */ --int hci_register_proto(struct hci_proto *hproto) -+ * hci_task_lock is used to ensure that no tasks are running. */ -+int hci_register_proto(struct hci_proto *hp) - { - int err = 0; - -- DBG("%p name %s", hproto, hproto->name); -+ BT_DBG("%p name %s id %d", hp, hp->name, hp->id); - -- if (hproto->id >= HCI_MAX_PROTO) -+ if (hp->id >= HCI_MAX_PROTO) - return -EINVAL; - - write_lock_bh(&hci_task_lock); - -- if (!hproto_list[hproto->id]) -- hproto_list[hproto->id] = hproto; -+ if (!hci_proto[hp->id]) -+ hci_proto[hp->id] = hp; - else -- err = -1; -+ err = -EEXIST; - - write_unlock_bh(&hci_task_lock); - - return err; - } - --int hci_unregister_proto(struct hci_proto *hproto) -+int hci_unregister_proto(struct hci_proto *hp) - { - int err = 0; - -- DBG("%p name %s", hproto, hproto->name); -+ BT_DBG("%p name %s id %d", hp, hp->name, hp->id); - -- if (hproto->id > HCI_MAX_PROTO) -+ if (hp->id >= HCI_MAX_PROTO) - return -EINVAL; - - write_lock_bh(&hci_task_lock); - -- if (hproto_list[hproto->id]) -- hproto_list[hproto->id] = NULL; -+ if (hci_proto[hp->id]) -+ hci_proto[hp->id] = NULL; - else - err = -ENOENT; - -@@ -1070,10 +964,14 @@ - return -ENODEV; - } - -- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); -+ BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); -+ -+ if (atomic_read(&hdev->promisc)) { -+ /* Time stamp */ -+ do_gettimeofday(&skb->stamp); - -- if (hdev->flags & HCI_SOCK) - hci_send_to_sock(hdev, skb); -+ } - - /* Get rid of skb owner, prior to sending to the driver. */ - skb_orphan(skb); -@@ -1081,128 +979,6 @@ - return hdev->send(skb); - } - --/* Connection scheduler */ --static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote) --{ -- struct conn_hash *h = &hdev->conn_hash; -- struct hci_conn *conn = NULL; -- int num = 0, min = 0xffff; -- struct list_head *p; -- -- conn_hash_lock(h); -- list_for_each(p, &h->list) { -- register struct hci_conn *c; -- -- c = list_entry(p, struct hci_conn, list); -- -- if (c->type != type || skb_queue_empty(&c->data_q)) -- continue; -- num++; -- -- if (c->sent < min) { -- min = c->sent; -- conn = c; -- } -- } -- conn_hash_unlock(h); -- -- if (conn) { -- int q = hdev->acl_cnt / num; -- *quote = q ? q : 1; -- } else -- *quote = 0; -- -- DBG("conn %p quote %d", conn, *quote); -- -- return conn; --} -- --static inline void hci_sched_acl(struct hci_dev *hdev) --{ -- struct hci_conn *conn; -- struct sk_buff *skb; -- int quote; -- -- DBG("%s", hdev->name); -- -- while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) { -- while (quote && (skb = skb_dequeue(&conn->data_q))) { -- DBG("skb %p len %d", skb, skb->len); -- -- hci_send_frame(skb); -- -- conn->sent++; -- hdev->acl_cnt--; -- quote--; -- } -- } --} -- --/* Schedule SCO */ --static inline void hci_sched_sco(struct hci_dev *hdev) --{ -- /* FIXME: For now we queue SCO packets to the raw queue -- -- while (hdev->sco_cnt && (skb = skb_dequeue(&conn->data_q))) { -- hci_send_frame(skb); -- conn->sco_sent++; -- hdev->sco_cnt--; -- } -- */ --} -- --/* Get data from the previously sent command */ --static void * hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf) --{ -- hci_command_hdr *hc; -- -- if (!hdev->sent_cmd) -- return NULL; -- -- hc = (void *) hdev->sent_cmd->data; -- -- if (hc->opcode != __cpu_to_le16(cmd_opcode_pack(ogf, ocf))) -- return NULL; -- -- DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf); -- -- return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE; --} -- --/* Send raw HCI frame */ --int hci_send_raw(struct sk_buff *skb) --{ -- struct hci_dev *hdev = (struct hci_dev *) skb->dev; -- -- if (!hdev) { -- kfree_skb(skb); -- return -ENODEV; -- } -- -- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); -- -- if (hdev->flags & HCI_NORMAL) { -- /* Queue frame according it's type */ -- switch (skb->pkt_type) { -- case HCI_COMMAND_PKT: -- skb_queue_tail(&hdev->cmd_q, skb); -- hci_sched_cmd(hdev); -- return 0; -- -- case HCI_ACLDATA_PKT: -- case HCI_SCODATA_PKT: -- /* FIXME: -- * Check header here and queue to apropriate connection. -- */ -- break; -- } -- } -- -- skb_queue_tail(&hdev->raw_q, skb); -- hci_sched_tx(hdev); -- return 0; --} -- - /* Send HCI command */ - int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param) - { -@@ -1210,10 +986,10 @@ - hci_command_hdr *hc; - struct sk_buff *skb; - -- DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen); -+ BT_DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen); - - if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) { -- ERR("%s Can't allocate memory for HCI command", hdev->name); -+ BT_ERR("%s Can't allocate memory for HCI command", hdev->name); - return -ENOMEM; - } - -@@ -1224,7 +1000,7 @@ - if (plen) - memcpy(skb_put(skb, plen), param, plen); - -- DBG("skb len %d", skb->len); -+ BT_DBG("skb len %d", skb->len); - - skb->pkt_type = HCI_COMMAND_PKT; - skb->dev = (void *) hdev; -@@ -1234,10 +1010,28 @@ - return 0; - } - -+/* Get data from the previously sent command */ -+void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf) -+{ -+ hci_command_hdr *hc; -+ -+ if (!hdev->sent_cmd) -+ return NULL; -+ -+ hc = (void *) hdev->sent_cmd->data; -+ -+ if (hc->opcode != __cpu_to_le16(cmd_opcode_pack(ogf, ocf))) -+ return NULL; -+ -+ BT_DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf); -+ -+ return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE; -+} -+ - /* Send ACL data */ - static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags) - { -- int len = skb->len; -+ int len = skb->len; - hci_acl_hdr *ah; - - ah = (hci_acl_hdr *) skb_push(skb, HCI_ACL_HDR_SIZE); -@@ -1252,7 +1046,7 @@ - struct hci_dev *hdev = conn->hdev; - struct sk_buff *list; - -- DBG("%s conn %p flags 0x%x", hdev->name, conn, flags); -+ BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags); - - skb->dev = (void *) hdev; - skb->pkt_type = HCI_ACLDATA_PKT; -@@ -1260,12 +1054,12 @@ - - if (!(list = skb_shinfo(skb)->frag_list)) { - /* Non fragmented */ -- DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len); -+ BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len); - - skb_queue_tail(&conn->data_q, skb); - } else { - /* Fragmented */ -- DBG("%s frag %p len %d", hdev->name, skb, skb->len); -+ BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); - - skb_shinfo(skb)->frag_list = NULL; - -@@ -1280,7 +1074,7 @@ - skb->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT); - -- DBG("%s frag %p len %d", hdev->name, skb, skb->len); -+ BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); - - __skb_queue_tail(&conn->data_q, skb); - } while (list); -@@ -1298,7 +1092,7 @@ - struct hci_dev *hdev = conn->hdev; - hci_sco_hdr hs; - -- DBG("%s len %d", hdev->name, skb->len); -+ BT_DBG("%s len %d", hdev->name, skb->len); - - if (skb->len > hdev->sco_mtu) { - kfree_skb(skb); -@@ -1315,544 +1109,136 @@ - skb->pkt_type = HCI_SCODATA_PKT; - skb_queue_tail(&conn->data_q, skb); - hci_sched_tx(hdev); -- - return 0; - } - --/* Handle HCI Event packets */ -- --/* Command Complete OGF LINK_CTL */ --static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) --{ -- DBG("%s ocf 0x%x", hdev->name, ocf); -- -- switch (ocf) { -- default: -- DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf); -- break; -- }; --} -- --/* Command Complete OGF LINK_POLICY */ --static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) --{ -- DBG("%s ocf 0x%x", hdev->name, ocf); -- -- switch (ocf) { -- default: -- DBG("%s: Command complete: ogf LINK_POLICY ocf %x", hdev->name, ocf); -- break; -- }; --} -- --/* Command Complete OGF HOST_CTL */ --static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) --{ -- __u8 status, param; -- void *sent; -- -- -- DBG("%s ocf 0x%x", hdev->name, ocf); -- -- switch (ocf) { -- case OCF_RESET: -- status = *((__u8 *) skb->data); -- -- hci_req_complete(hdev, status); -- break; -- -- case OCF_SET_EVENT_FLT: -- status = *((__u8 *) skb->data); -- -- if (status) { -- DBG("%s SET_EVENT_FLT failed %d", hdev->name, status); -- } else { -- DBG("%s SET_EVENT_FLT succeseful", hdev->name); -- } -- break; -- -- case OCF_WRITE_AUTH_ENABLE: -- if (!(sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE))) -- break; -- -- status = *((__u8 *) skb->data); -- param = *((__u8 *) sent); -- -- if (!status) { -- if (param == AUTH_ENABLED) -- hdev->flags |= HCI_AUTH; -- else -- hdev->flags &= ~HCI_AUTH; -- } -- hci_req_complete(hdev, status); -- break; -- -- case OCF_WRITE_CA_TIMEOUT: -- status = *((__u8 *) skb->data); -- -- if (status) { -- DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status); -- } else { -- DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name); -- } -- break; -- -- case OCF_WRITE_PG_TIMEOUT: -- status = *((__u8 *) skb->data); -- -- if (status) { -- DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status); -- } else { -- DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name); -- } -- break; -- -- case OCF_WRITE_SCAN_ENABLE: -- if (!(sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE))) -- break; -- status = *((__u8 *) skb->data); -- param = *((__u8 *) sent); -- -- DBG("param 0x%x", param); -- -- if (!status) { -- switch (param) { -- case IS_ENA_PS_ENA: -- hdev->flags |= HCI_PSCAN | HCI_ISCAN; -- break; -- -- case IS_ENA_PS_DIS: -- hdev->flags &= ~HCI_PSCAN; -- hdev->flags |= HCI_ISCAN; -- break; -- -- case IS_DIS_PS_ENA: -- hdev->flags &= ~HCI_ISCAN; -- hdev->flags |= HCI_PSCAN; -- break; -- -- default: -- hdev->flags &= ~(HCI_ISCAN | HCI_PSCAN); -- break; -- }; -- } -- hci_req_complete(hdev, status); -- break; -- -- default: -- DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf); -- break; -- }; --} -- --/* Command Complete OGF INFO_PARAM */ --static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) --{ -- read_local_features_rp *lf; -- read_buffer_size_rp *bs; -- read_bd_addr_rp *ba; -- -- DBG("%s ocf 0x%x", hdev->name, ocf); -- -- switch (ocf) { -- case OCF_READ_LOCAL_FEATURES: -- lf = (read_local_features_rp *) skb->data; -- -- if (lf->status) { -- DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status); -- break; -- } -- -- memcpy(hdev->features, lf->features, sizeof(hdev->features)); -- -- /* Adjust default settings according to features -- * supported by device. */ -- if (hdev->features[0] & LMP_3SLOT) -- hdev->pkt_type |= (HCI_DM3 | HCI_DH3); -- -- if (hdev->features[0] & LMP_5SLOT) -- hdev->pkt_type |= (HCI_DM5 | HCI_DH5); -- -- DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]); -- -- break; -- -- case OCF_READ_BUFFER_SIZE: -- bs = (read_buffer_size_rp *) skb->data; -- -- if (bs->status) { -- DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status); -- break; -- } -- -- hdev->acl_mtu = __le16_to_cpu(bs->acl_mtu); -- hdev->sco_mtu = bs->sco_mtu; -- hdev->acl_max = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt); -- hdev->sco_max = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt); -- -- DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name, -- hdev->acl_mtu, hdev->sco_mtu, hdev->acl_max, hdev->sco_max); -- -- break; -- -- case OCF_READ_BD_ADDR: -- ba = (read_bd_addr_rp *) skb->data; -- -- if (!ba->status) { -- bacpy(&hdev->bdaddr, &ba->bdaddr); -- } else { -- DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status); -- } -- -- hci_req_complete(hdev, ba->status); -- break; -- -- default: -- DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf); -- break; -- }; --} -+/* ---- HCI TX task (outgoing data) ---- */ - --/* Command Status OGF LINK_CTL */ --static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status) -+/* HCI Connection scheduler */ -+static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote) - { -- struct hci_proto * hp; -- -- DBG("%s ocf 0x%x", hdev->name, ocf); -- -- switch (ocf) { -- case OCF_CREATE_CONN: -- if (status) { -- create_conn_cp *cc = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN); -- -- if (!cc) -- break; -+ struct conn_hash *h = &hdev->conn_hash; -+ struct hci_conn *conn = NULL; -+ int num = 0, min = ~0; -+ struct list_head *p; - -- DBG("%s Create connection error: status 0x%x %s", hdev->name, -- status, batostr(&cc->bdaddr)); -+ /* We don't have to lock device here. Connections are always -+ * added and removed with TX task disabled. */ -+ list_for_each(p, &h->list) { -+ struct hci_conn *c; -+ c = list_entry(p, struct hci_conn, list); - -- /* Notify upper protocols */ -- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_cfm) { -- tasklet_disable(&hdev->tx_task); -- hp->connect_cfm(hdev, &cc->bdaddr, status, NULL); -- tasklet_enable(&hdev->tx_task); -- } -- } -- break; -+ if (c->type != type || c->state != BT_CONNECTED -+ || skb_queue_empty(&c->data_q)) -+ continue; -+ num++; - -- case OCF_INQUIRY: -- if (status) { -- DBG("%s Inquiry error: status 0x%x", hdev->name, status); -- hci_req_complete(hdev, status); -+ if (c->sent < min) { -+ min = c->sent; -+ conn = c; - } -- break; -- -- default: -- DBG("%s Command status: ogf LINK_CTL ocf %x", hdev->name, ocf); -- break; -- }; --} -- --/* Command Status OGF LINK_POLICY */ --static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status) --{ -- DBG("%s ocf 0x%x", hdev->name, ocf); -- -- switch (ocf) { -- default: -- DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf); -- break; -- }; --} -- --/* Command Status OGF HOST_CTL */ --static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status) --{ -- DBG("%s ocf 0x%x", hdev->name, ocf); -- -- switch (ocf) { -- default: -- DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf); -- break; -- }; --} -- --/* Command Status OGF INFO_PARAM */ --static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status) --{ -- DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf); -- -- switch (ocf) { -- default: -- DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf); -- break; -- }; --} -- --/* Inquiry Complete */ --static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) --{ -- __u8 status = *((__u8 *) skb->data); -- -- DBG("%s status %d", hdev->name, status); -- -- hci_req_complete(hdev, status); --} -- --/* Inquiry Result */ --static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) --{ -- inquiry_info *info = (inquiry_info *) (skb->data + 1); -- int num_rsp = *((__u8 *) skb->data); -+ } - -- DBG("%s num_rsp %d", hdev->name, num_rsp); -+ if (conn) { -+ int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt); -+ int q = cnt / num; -+ *quote = q ? q : 1; -+ } else -+ *quote = 0; - -- for (; num_rsp; num_rsp--) -- inquiry_cache_update(&hdev->inq_cache, info++); -+ BT_DBG("conn %p quote %d", conn, *quote); -+ return conn; - } - --/* Connect Request */ --static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -+static inline void hci_acl_tx_to(struct hci_dev *hdev) - { -- evt_conn_request *cr = (evt_conn_request *) skb->data; -- struct hci_proto *hp; -- accept_conn_req_cp ac; -- int accept = 0; -+ struct conn_hash *h = &hdev->conn_hash; -+ struct list_head *p; -+ struct hci_conn *c; - -- DBG("%s Connection request: %s type 0x%x", hdev->name, batostr(&cr->bdaddr), cr->link_type); -+ BT_ERR("%s ACL tx timeout", hdev->name); - -- /* Notify upper protocols */ -- if (cr->link_type == ACL_LINK) { -- /* ACL link notify L2CAP */ -- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_ind) { -- tasklet_disable(&hdev->tx_task); -- accept = hp->connect_ind(hdev, &cr->bdaddr); -- tasklet_enable(&hdev->tx_task); -+ /* Kill stalled connections */ -+ list_for_each(p, &h->list) { -+ c = list_entry(p, struct hci_conn, list); -+ if (c->type == ACL_LINK && c->sent) { -+ BT_ERR("%s killing stalled ACL connection %s", -+ hdev->name, batostr(&c->dst)); -+ hci_acl_disconn(c, 0x13); - } -- } else { -- /* SCO link (no notification) */ -- /* FIXME: Should be accept it here or let the requester (app) accept it ? */ -- accept = 1; -- } -- -- if (accept) { -- /* Connection accepted by upper layer */ -- bacpy(&ac.bdaddr, &cr->bdaddr); -- ac.role = 0x01; /* Remain slave */ -- hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, ACCEPT_CONN_REQ_CP_SIZE, &ac); -- } else { -- /* Connection rejected by upper layer */ -- /* FIXME: -- * Should we use HCI reject here ? -- */ -- return; - } - } - --/* Connect Complete */ --static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -+static inline void hci_sched_acl(struct hci_dev *hdev) - { -- evt_conn_complete *cc = (evt_conn_complete *) skb->data; -- struct hci_conn *conn = NULL; -- struct hci_proto *hp; -+ struct hci_conn *conn; -+ struct sk_buff *skb; -+ int quote; - -- DBG("%s", hdev->name); -+ BT_DBG("%s", hdev->name); - -- tasklet_disable(&hdev->tx_task); -+ /* ACL tx timeout must be longer than maximum -+ * link supervision timeout (40.9 seconds) */ -+ if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45)) -+ hci_acl_tx_to(hdev); - -- if (!cc->status) -- conn = hci_conn_add(hdev, __le16_to_cpu(cc->handle), cc->link_type, &cc->bdaddr); -+ while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) { -+ while (quote-- && (skb = skb_dequeue(&conn->data_q))) { -+ BT_DBG("skb %p len %d", skb, skb->len); -+ hci_send_frame(skb); -+ hdev->acl_last_tx = jiffies; - -- /* Notify upper protocols */ -- if (cc->link_type == ACL_LINK) { -- /* ACL link notify L2CAP layer */ -- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_cfm) -- hp->connect_cfm(hdev, &cc->bdaddr, cc->status, conn); -- } else { -- /* SCO link (no notification) */ -+ hdev->acl_cnt--; -+ conn->sent++; -+ } - } -- -- tasklet_enable(&hdev->tx_task); - } - --/* Disconnect Complete */ --static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -+/* Schedule SCO */ -+static inline void hci_sched_sco(struct hci_dev *hdev) - { -- evt_disconn_complete *dc = (evt_disconn_complete *) skb->data; -- struct hci_conn *conn = NULL; -- struct hci_proto *hp; -- __u16 handle = __le16_to_cpu(dc->handle); -+ struct hci_conn *conn; -+ struct sk_buff *skb; -+ int quote; - -- DBG("%s", hdev->name); -+ BT_DBG("%s", hdev->name); - -- if (!dc->status && (conn = conn_hash_lookup(&hdev->conn_hash, handle))) { -- tasklet_disable(&hdev->tx_task); -+ while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) { -+ while (quote-- && (skb = skb_dequeue(&conn->data_q))) { -+ BT_DBG("skb %p len %d", skb, skb->len); -+ hci_send_frame(skb); - -- /* Notify upper protocols */ -- if (conn->type == ACL_LINK) { -- /* ACL link notify L2CAP layer */ -- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->disconn_ind) -- hp->disconn_ind(conn, dc->reason); -- } else { -- /* SCO link (no notification) */ -+ conn->sent++; -+ if (conn->sent == ~0) -+ conn->sent = 0; - } -- -- hci_conn_del(hdev, conn); -- -- tasklet_enable(&hdev->tx_task); - } - } - --/* Number of completed packets */ --static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb) -+static void hci_tx_task(unsigned long arg) - { -- evt_num_comp_pkts *nc = (evt_num_comp_pkts *) skb->data; -- __u16 *ptr; -- int i; -- -- skb_pull(skb, EVT_NUM_COMP_PKTS_SIZE); -- -- DBG("%s num_hndl %d", hdev->name, nc->num_hndl); -+ struct hci_dev *hdev = (struct hci_dev *) arg; -+ struct sk_buff *skb; - -- if (skb->len < nc->num_hndl * 4) { -- DBG("%s bad parameters", hdev->name); -- return; -- } -+ read_lock(&hci_task_lock); - -- tasklet_disable(&hdev->tx_task); -+ BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt); - -- for (i = 0, ptr = (__u16 *) skb->data; i < nc->num_hndl; i++) { -- struct hci_conn *conn; -- __u16 handle, count; -+ /* Schedule queues and send stuff to HCI driver */ - -- handle = __le16_to_cpu(get_unaligned(ptr++)); -- count = __le16_to_cpu(get_unaligned(ptr++)); -+ hci_sched_acl(hdev); - -- hdev->acl_cnt += count; -+ hci_sched_sco(hdev); - -- if ((conn = conn_hash_lookup(&hdev->conn_hash, handle))) -- conn->sent -= count; -- } -+ /* Send next queued raw (unknown type) packet */ -+ while ((skb = skb_dequeue(&hdev->raw_q))) -+ hci_send_frame(skb); - -- tasklet_enable(&hdev->tx_task); -- -- hci_sched_tx(hdev); -+ read_unlock(&hci_task_lock); - } - --static inline void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) --{ -- hci_event_hdr *he = (hci_event_hdr *) skb->data; -- evt_cmd_status *cs; -- evt_cmd_complete *ec; -- __u16 opcode, ocf, ogf; -- -- skb_pull(skb, HCI_EVENT_HDR_SIZE); -- -- DBG("%s evt 0x%x", hdev->name, he->evt); -- -- switch (he->evt) { -- case EVT_NUM_COMP_PKTS: -- hci_num_comp_pkts_evt(hdev, skb); -- break; -- -- case EVT_INQUIRY_COMPLETE: -- hci_inquiry_complete_evt(hdev, skb); -- break; -- -- case EVT_INQUIRY_RESULT: -- hci_inquiry_result_evt(hdev, skb); -- break; -- -- case EVT_CONN_REQUEST: -- hci_conn_request_evt(hdev, skb); -- break; -- -- case EVT_CONN_COMPLETE: -- hci_conn_complete_evt(hdev, skb); -- break; -- -- case EVT_DISCONN_COMPLETE: -- hci_disconn_complete_evt(hdev, skb); -- break; -- -- case EVT_CMD_STATUS: -- cs = (evt_cmd_status *) skb->data; -- skb_pull(skb, EVT_CMD_STATUS_SIZE); -- -- opcode = __le16_to_cpu(cs->opcode); -- ogf = cmd_opcode_ogf(opcode); -- ocf = cmd_opcode_ocf(opcode); -- -- switch (ogf) { -- case OGF_INFO_PARAM: -- hci_cs_info_param(hdev, ocf, cs->status); -- break; -- -- case OGF_HOST_CTL: -- hci_cs_host_ctl(hdev, ocf, cs->status); -- break; - -- case OGF_LINK_CTL: -- hci_cs_link_ctl(hdev, ocf, cs->status); -- break; -- -- case OGF_LINK_POLICY: -- hci_cs_link_policy(hdev, ocf, cs->status); -- break; -- -- default: -- DBG("%s Command Status OGF %x", hdev->name, ogf); -- break; -- }; -- -- if (cs->ncmd) { -- atomic_set(&hdev->cmd_cnt, 1); -- if (!skb_queue_empty(&hdev->cmd_q)) -- hci_sched_cmd(hdev); -- } -- break; -- -- case EVT_CMD_COMPLETE: -- ec = (evt_cmd_complete *) skb->data; -- skb_pull(skb, EVT_CMD_COMPLETE_SIZE); -- -- opcode = __le16_to_cpu(ec->opcode); -- ogf = cmd_opcode_ogf(opcode); -- ocf = cmd_opcode_ocf(opcode); -- -- switch (ogf) { -- case OGF_INFO_PARAM: -- hci_cc_info_param(hdev, ocf, skb); -- break; -- -- case OGF_HOST_CTL: -- hci_cc_host_ctl(hdev, ocf, skb); -- break; -- -- case OGF_LINK_CTL: -- hci_cc_link_ctl(hdev, ocf, skb); -- break; -- -- case OGF_LINK_POLICY: -- hci_cc_link_policy(hdev, ocf, skb); -- break; -- -- default: -- DBG("%s Command Completed OGF %x", hdev->name, ogf); -- break; -- }; -- -- if (ec->ncmd) { -- atomic_set(&hdev->cmd_cnt, 1); -- if (!skb_queue_empty(&hdev->cmd_q)) -- hci_sched_cmd(hdev); -- } -- break; -- }; -- -- kfree_skb(skb); -- hdev->stat.evt_rx++; --} -+/* ----- HCI RX task (incomming data proccessing) ----- */ - - /* ACL data packet */ - static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) -@@ -1867,51 +1253,86 @@ - flags = acl_flags(handle); - handle = acl_handle(handle); - -- DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags); -+ BT_DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags); - -- if ((conn = conn_hash_lookup(&hdev->conn_hash, handle))) { -+ hdev->stat.acl_rx++; -+ -+ hci_dev_lock(hdev); -+ conn = conn_hash_lookup_handle(hdev, handle); -+ hci_dev_unlock(hdev); -+ -+ if (conn) { - register struct hci_proto *hp; - - /* Send to upper protocol */ -- if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->recv_acldata) { -+ if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) { - hp->recv_acldata(conn, skb, flags); -- goto sent; -+ return; - } - } else { -- ERR("%s ACL packet for unknown connection handle %d", hdev->name, handle); -+ BT_ERR("%s ACL packet for unknown connection handle %d", -+ hdev->name, handle); - } - - kfree_skb(skb); --sent: -- hdev->stat.acl_rx++; - } - - /* SCO data packet */ - static inline void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb) - { -- DBG("%s len %d", hdev->name, skb->len); -+ hci_sco_hdr *sh = (void *) skb->data; -+ struct hci_conn *conn; -+ __u16 handle; -+ -+ skb_pull(skb, HCI_SCO_HDR_SIZE); -+ -+ handle = __le16_to_cpu(sh->handle); -+ -+ BT_DBG("%s len %d handle 0x%x", hdev->name, skb->len, handle); - -- kfree_skb(skb); - hdev->stat.sco_rx++; -+ -+ hci_dev_lock(hdev); -+ conn = conn_hash_lookup_handle(hdev, handle); -+ hci_dev_unlock(hdev); -+ -+ if (conn) { -+ register struct hci_proto *hp; -+ -+ /* Send to upper protocol */ -+ if ((hp = hci_proto[HCI_PROTO_SCO]) && hp->recv_scodata) { -+ hp->recv_scodata(conn, skb); -+ return; -+ } -+ } else { -+ BT_ERR("%s SCO packet for unknown connection handle %d", -+ hdev->name, handle); -+ } -+ -+ kfree_skb(skb); - } - --/* ----- HCI tasks ----- */ - void hci_rx_task(unsigned long arg) - { - struct hci_dev *hdev = (struct hci_dev *) arg; - struct sk_buff *skb; - -- DBG("%s", hdev->name); -+ BT_DBG("%s", hdev->name); - - read_lock(&hci_task_lock); - - while ((skb = skb_dequeue(&hdev->rx_q))) { -- if (hdev->flags & HCI_SOCK) { -+ if (atomic_read(&hdev->promisc)) { - /* Send copy to the sockets */ - hci_send_to_sock(hdev, skb); - } - -- if (hdev->flags & HCI_INIT) { -+ if (test_bit(HCI_RAW, &hdev->flags)) { -+ kfree_skb(skb); -+ continue; -+ } -+ -+ if (test_bit(HCI_INIT, &hdev->flags)) { - /* Don't process data packets in this states. */ - switch (skb->pkt_type) { - case HCI_ACLDATA_PKT: -@@ -1921,64 +1342,43 @@ - }; - } - -- if (hdev->flags & HCI_NORMAL) { -- /* Process frame */ -- switch (skb->pkt_type) { -- case HCI_EVENT_PKT: -- hci_event_packet(hdev, skb); -- break; -+ /* Process frame */ -+ switch (skb->pkt_type) { -+ case HCI_EVENT_PKT: -+ hci_event_packet(hdev, skb); -+ break; - -- case HCI_ACLDATA_PKT: -- DBG("%s ACL data packet", hdev->name); -- hci_acldata_packet(hdev, skb); -- break; -+ case HCI_ACLDATA_PKT: -+ BT_DBG("%s ACL data packet", hdev->name); -+ hci_acldata_packet(hdev, skb); -+ break; - -- case HCI_SCODATA_PKT: -- DBG("%s SCO data packet", hdev->name); -- hci_scodata_packet(hdev, skb); -- break; -+ case HCI_SCODATA_PKT: -+ BT_DBG("%s SCO data packet", hdev->name); -+ hci_scodata_packet(hdev, skb); -+ break; - -- default: -- kfree_skb(skb); -- break; -- }; -- } else { -+ default: - kfree_skb(skb); -+ break; - } - } - - read_unlock(&hci_task_lock); - } - --static void hci_tx_task(unsigned long arg) --{ -- struct hci_dev *hdev = (struct hci_dev *) arg; -- struct sk_buff *skb; -- -- read_lock(&hci_task_lock); -- -- DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt); -- -- /* Schedule queues and send stuff to HCI driver */ -- -- hci_sched_acl(hdev); -- -- hci_sched_sco(hdev); -- -- /* Send next queued raw (unknown type) packet */ -- while ((skb = skb_dequeue(&hdev->raw_q))) -- hci_send_frame(skb); -- -- read_unlock(&hci_task_lock); --} -- - static void hci_cmd_task(unsigned long arg) - { - struct hci_dev *hdev = (struct hci_dev *) arg; - struct sk_buff *skb; - -- DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt)); -+ BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt)); - -+ if (!atomic_read(&hdev->cmd_cnt) && (jiffies - hdev->cmd_last_tx) > HZ) { -+ BT_ERR("%s command tx timeout", hdev->name); -+ atomic_set(&hdev->cmd_cnt, 1); -+ } -+ - /* Send queued commands */ - if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) { - if (hdev->sent_cmd) -@@ -1987,6 +1387,7 @@ - if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) { - atomic_dec(&hdev->cmd_cnt); - hci_send_frame(skb); -+ hdev->cmd_last_tx = jiffies; - } else { - skb_queue_head(&hdev->cmd_q, skb); - hci_sched_cmd(hdev); -@@ -1994,33 +1395,10 @@ - } - } - --/* Receive frame from HCI drivers */ --int hci_recv_frame(struct sk_buff *skb) --{ -- struct hci_dev *hdev = (struct hci_dev *) skb->dev; -- -- if (!hdev || !(hdev->flags & (HCI_UP | HCI_INIT))) { -- kfree_skb(skb); -- return -1; -- } -- -- DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len); -- -- /* Incomming skb */ -- bluez_cb(skb)->incomming = 1; -- -- /* Queue frame for rx task */ -- skb_queue_tail(&hdev->rx_q, skb); -- hci_sched_rx(hdev); -- -- return 0; --} -+/* ---- Initialization ---- */ - - int hci_core_init(void) - { -- /* Init locks */ -- spin_lock_init(&hdev_list_lock); -- - return 0; - } - -@@ -2028,5 +1406,3 @@ - { - return 0; - } -- --MODULE_LICENSE("GPL"); ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/hci_event.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,910 @@ -+/* -+ BlueZ - Bluetooth protocol stack for Linux -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * HCI Events. -+ * -+ * $Id: hci_event.c,v 1.4 2002/07/27 18:14:38 maxk Exp $ -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/kernel.h> -+#include <linux/major.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/poll.h> -+#include <linux/fcntl.h> -+#include <linux/init.h> -+#include <linux/skbuff.h> -+#include <linux/interrupt.h> -+#include <linux/notifier.h> -+#include <net/sock.h> -+ -+#include <asm/system.h> -+#include <asm/uaccess.h> -+#include <asm/unaligned.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+ -+#ifndef HCI_CORE_DEBUG -+#undef BT_DBG -+#define BT_DBG( A... ) -+#endif -+ -+/* Handle HCI Event packets */ -+ -+/* Command Complete OGF LINK_CTL */ -+static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) -+{ -+ __u8 status; -+ -+ BT_DBG("%s ocf 0x%x", hdev->name, ocf); -+ -+ switch (ocf) { -+ case OCF_INQUIRY_CANCEL: -+ status = *((__u8 *) skb->data); -+ -+ if (status) { -+ BT_DBG("%s Inquiry cancel error: status 0x%x", hdev->name, status); -+ } else { -+ clear_bit(HCI_INQUIRY, &hdev->flags); -+ hci_req_complete(hdev, status); -+ } -+ break; -+ -+ default: -+ BT_DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf); -+ break; -+ }; -+} -+ -+/* Command Complete OGF LINK_POLICY */ -+static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) -+{ -+ struct hci_conn *conn; -+ role_discovery_rp *rd; -+ -+ BT_DBG("%s ocf 0x%x", hdev->name, ocf); -+ -+ switch (ocf) { -+ case OCF_ROLE_DISCOVERY: -+ rd = (void *) skb->data; -+ -+ if (rd->status) -+ break; -+ -+ hci_dev_lock(hdev); -+ -+ conn = conn_hash_lookup_handle(hdev, __le16_to_cpu(rd->handle)); -+ if (conn) { -+ if (rd->role) -+ conn->link_mode &= ~HCI_LM_MASTER; -+ else -+ conn->link_mode |= HCI_LM_MASTER; -+ } -+ -+ hci_dev_unlock(hdev); -+ break; -+ -+ default: -+ BT_DBG("%s: Command complete: ogf LINK_POLICY ocf %x", -+ hdev->name, ocf); -+ break; -+ }; -+} -+ -+/* Command Complete OGF HOST_CTL */ -+static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) -+{ -+ __u8 status, param; -+ void *sent; -+ -+ BT_DBG("%s ocf 0x%x", hdev->name, ocf); -+ -+ switch (ocf) { -+ case OCF_RESET: -+ status = *((__u8 *) skb->data); -+ hci_req_complete(hdev, status); -+ break; -+ -+ case OCF_SET_EVENT_FLT: -+ status = *((__u8 *) skb->data); -+ if (status) { -+ BT_DBG("%s SET_EVENT_FLT failed %d", hdev->name, status); -+ } else { -+ BT_DBG("%s SET_EVENT_FLT succeseful", hdev->name); -+ } -+ break; -+ -+ case OCF_WRITE_AUTH_ENABLE: -+ sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE); -+ if (!sent) -+ break; -+ -+ status = *((__u8 *) skb->data); -+ param = *((__u8 *) sent); -+ -+ if (!status) { -+ if (param == AUTH_ENABLED) -+ set_bit(HCI_AUTH, &hdev->flags); -+ else -+ clear_bit(HCI_AUTH, &hdev->flags); -+ } -+ hci_req_complete(hdev, status); -+ break; -+ -+ case OCF_WRITE_ENCRYPT_MODE: -+ sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE); -+ if (!sent) -+ break; -+ -+ status = *((__u8 *) skb->data); -+ param = *((__u8 *) sent); -+ -+ if (!status) { -+ if (param) -+ set_bit(HCI_ENCRYPT, &hdev->flags); -+ else -+ clear_bit(HCI_ENCRYPT, &hdev->flags); -+ } -+ hci_req_complete(hdev, status); -+ break; -+ -+ case OCF_WRITE_CA_TIMEOUT: -+ status = *((__u8 *) skb->data); -+ if (status) { -+ BT_DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status); -+ } else { -+ BT_DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name); -+ } -+ break; -+ -+ case OCF_WRITE_PG_TIMEOUT: -+ status = *((__u8 *) skb->data); -+ if (status) { -+ BT_DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status); -+ } else { -+ BT_DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name); -+ } -+ break; -+ -+ case OCF_WRITE_SCAN_ENABLE: -+ sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE); -+ if (!sent) -+ break; -+ status = *((__u8 *) skb->data); -+ param = *((__u8 *) sent); -+ -+ BT_DBG("param 0x%x", param); -+ -+ if (!status) { -+ clear_bit(HCI_PSCAN, &hdev->flags); -+ clear_bit(HCI_ISCAN, &hdev->flags); -+ if (param & SCAN_INQUIRY) -+ set_bit(HCI_ISCAN, &hdev->flags); -+ -+ if (param & SCAN_PAGE) -+ set_bit(HCI_PSCAN, &hdev->flags); -+ } -+ hci_req_complete(hdev, status); -+ break; -+ -+ case OCF_HOST_BUFFER_SIZE: -+ status = *((__u8 *) skb->data); -+ if (status) { -+ BT_DBG("%s OCF_BUFFER_SIZE failed %d", hdev->name, status); -+ hci_req_complete(hdev, status); -+ } -+ break; -+ -+ default: -+ BT_DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf); -+ break; -+ }; -+} -+ -+/* Command Complete OGF INFO_PARAM */ -+static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) -+{ -+ read_local_features_rp *lf; -+ read_buffer_size_rp *bs; -+ read_bd_addr_rp *ba; -+ -+ BT_DBG("%s ocf 0x%x", hdev->name, ocf); -+ -+ switch (ocf) { -+ case OCF_READ_LOCAL_FEATURES: -+ lf = (read_local_features_rp *) skb->data; -+ -+ if (lf->status) { -+ BT_DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status); -+ break; -+ } -+ -+ memcpy(hdev->features, lf->features, sizeof(hdev->features)); -+ -+ /* Adjust default settings according to features -+ * supported by device. */ -+ if (hdev->features[0] & LMP_3SLOT) -+ hdev->pkt_type |= (HCI_DM3 | HCI_DH3); -+ -+ if (hdev->features[0] & LMP_5SLOT) -+ hdev->pkt_type |= (HCI_DM5 | HCI_DH5); -+ -+ if (hdev->features[1] & LMP_HV2) -+ hdev->pkt_type |= (HCI_HV2); -+ -+ if (hdev->features[1] & LMP_HV3) -+ hdev->pkt_type |= (HCI_HV3); -+ -+ BT_DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]); -+ -+ break; -+ -+ case OCF_READ_BUFFER_SIZE: -+ bs = (read_buffer_size_rp *) skb->data; -+ -+ if (bs->status) { -+ BT_DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status); -+ hci_req_complete(hdev, bs->status); -+ break; -+ } -+ -+ hdev->acl_mtu = __le16_to_cpu(bs->acl_mtu); -+ hdev->sco_mtu = bs->sco_mtu ? bs->sco_mtu : 64; -+ hdev->acl_pkts = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt); -+ hdev->sco_pkts = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt); -+ -+ BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name, -+ hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts); -+ break; -+ -+ case OCF_READ_BD_ADDR: -+ ba = (read_bd_addr_rp *) skb->data; -+ -+ if (!ba->status) { -+ bacpy(&hdev->bdaddr, &ba->bdaddr); -+ } else { -+ BT_DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status); -+ } -+ -+ hci_req_complete(hdev, ba->status); -+ break; -+ -+ default: -+ BT_DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf); -+ break; -+ }; -+} -+ -+/* Command Status OGF LINK_CTL */ -+static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) -+{ -+ struct hci_conn *conn; -+ create_conn_cp *cc = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN); -+ -+ if (!cc) -+ return; -+ -+ hci_dev_lock(hdev); -+ -+ conn = conn_hash_lookup_ba(hdev, ACL_LINK, &cc->bdaddr); -+ -+ BT_DBG("%s status 0x%x bdaddr %s conn %p", hdev->name, -+ status, batostr(&cc->bdaddr), conn); -+ -+ if (status) { -+ if (conn) { -+ conn->state = BT_CLOSED; -+ hci_proto_connect_cfm(conn, status); -+ hci_conn_del(conn); -+ } -+ } else { -+ if (!conn) { -+ conn = hci_conn_add(hdev, ACL_LINK, &cc->bdaddr); -+ if (conn) { -+ conn->out = 1; -+ conn->link_mode |= HCI_LM_MASTER; -+ } else -+ BT_ERR("No memmory for new connection"); -+ } -+ } -+ -+ hci_dev_unlock(hdev); -+} -+ -+static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status) -+{ -+ BT_DBG("%s ocf 0x%x", hdev->name, ocf); -+ -+ switch (ocf) { -+ case OCF_CREATE_CONN: -+ hci_cs_create_conn(hdev, status); -+ break; -+ -+ case OCF_ADD_SCO: -+ if (status) { -+ struct hci_conn *acl, *sco; -+ add_sco_cp *cp = hci_sent_cmd_data(hdev, -+ OGF_LINK_CTL, OCF_ADD_SCO); -+ __u16 handle; -+ -+ if (!cp) -+ break; -+ -+ handle = __le16_to_cpu(cp->handle); -+ -+ BT_DBG("%s Add SCO error: handle %d status 0x%x", hdev->name, handle, status); -+ -+ hci_dev_lock(hdev); -+ -+ acl = conn_hash_lookup_handle(hdev, handle); -+ if (acl && (sco = acl->link)) { -+ sco->state = BT_CLOSED; -+ hci_proto_connect_cfm(sco, status); -+ hci_conn_del(sco); -+ } -+ -+ hci_dev_unlock(hdev); -+ } -+ break; -+ -+ case OCF_INQUIRY: -+ if (status) { -+ BT_DBG("%s Inquiry error: status 0x%x", hdev->name, status); -+ hci_req_complete(hdev, status); -+ } else { -+ set_bit(HCI_INQUIRY, &hdev->flags); -+ } -+ break; -+ -+ default: -+ BT_DBG("%s Command status: ogf LINK_CTL ocf %x status %d", -+ hdev->name, ocf, status); -+ break; -+ }; -+} -+ -+/* Command Status OGF LINK_POLICY */ -+static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status) -+{ -+ BT_DBG("%s ocf 0x%x", hdev->name, ocf); -+ -+ switch (ocf) { -+ default: -+ BT_DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf); -+ break; -+ }; -+} -+ -+/* Command Status OGF HOST_CTL */ -+static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status) -+{ -+ BT_DBG("%s ocf 0x%x", hdev->name, ocf); -+ -+ switch (ocf) { -+ default: -+ BT_DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf); -+ break; -+ }; -+} -+ -+/* Command Status OGF INFO_PARAM */ -+static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status) -+{ -+ BT_DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf); -+ -+ switch (ocf) { -+ default: -+ BT_DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf); -+ break; -+ }; -+} -+ -+/* Inquiry Complete */ -+static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ __u8 status = *((__u8 *) skb->data); -+ -+ BT_DBG("%s status %d", hdev->name, status); -+ -+ clear_bit(HCI_INQUIRY, &hdev->flags); -+ hci_req_complete(hdev, status); -+} -+ -+/* Inquiry Result */ -+static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ inquiry_info *info = (inquiry_info *) (skb->data + 1); -+ int num_rsp = *((__u8 *) skb->data); -+ -+ BT_DBG("%s num_rsp %d", hdev->name, num_rsp); -+ -+ hci_dev_lock(hdev); -+ for (; num_rsp; num_rsp--) -+ inquiry_cache_update(hdev, info++); -+ hci_dev_unlock(hdev); -+} -+ -+/* Inquiry Result With RSSI */ -+static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ inquiry_info_with_rssi *info = (inquiry_info_with_rssi *) (skb->data + 1); -+ int num_rsp = *((__u8 *) skb->data); -+ -+ BT_DBG("%s num_rsp %d", hdev->name, num_rsp); -+ -+ hci_dev_lock(hdev); -+ for (; num_rsp; num_rsp--) { -+ inquiry_info tmp; -+ bacpy(&tmp.bdaddr, &info->bdaddr); -+ tmp.pscan_rep_mode = info->pscan_rep_mode; -+ tmp.pscan_period_mode = info->pscan_period_mode; -+ tmp.pscan_mode = 0x00; -+ memcpy(tmp.dev_class, &info->dev_class, 3); -+ tmp.clock_offset = info->clock_offset; -+ info++; -+ inquiry_cache_update(hdev, &tmp); -+ } -+ hci_dev_unlock(hdev); -+} -+ -+/* Connect Request */ -+static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ evt_conn_request *cr = (evt_conn_request *) skb->data; -+ int mask = hdev->link_mode; -+ -+ BT_DBG("%s Connection request: %s type 0x%x", hdev->name, -+ batostr(&cr->bdaddr), cr->link_type); -+ -+ mask |= hci_proto_connect_ind(hdev, &cr->bdaddr, cr->link_type); -+ -+ if (mask & HCI_LM_ACCEPT) { -+ /* Connection accepted */ -+ struct hci_conn *conn; -+ accept_conn_req_cp ac; -+ -+ hci_dev_lock(hdev); -+ conn = conn_hash_lookup_ba(hdev, cr->link_type, &cr->bdaddr); -+ if (!conn) { -+ if (!(conn = hci_conn_add(hdev, cr->link_type, &cr->bdaddr))) { -+ BT_ERR("No memmory for new connection"); -+ hci_dev_unlock(hdev); -+ return; -+ } -+ } -+ conn->state = BT_CONNECT; -+ hci_dev_unlock(hdev); -+ -+ bacpy(&ac.bdaddr, &cr->bdaddr); -+ -+ if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) -+ ac.role = 0x00; /* Become master */ -+ else -+ ac.role = 0x01; /* Remain slave */ -+ -+ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, -+ ACCEPT_CONN_REQ_CP_SIZE, &ac); -+ } else { -+ /* Connection rejected */ -+ reject_conn_req_cp rc; -+ -+ bacpy(&rc.bdaddr, &cr->bdaddr); -+ rc.reason = 0x0f; -+ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_REJECT_CONN_REQ, -+ REJECT_CONN_REQ_CP_SIZE, &rc); -+ } -+} -+ -+/* Connect Complete */ -+static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ evt_conn_complete *cc = (evt_conn_complete *) skb->data; -+ struct hci_conn *conn = NULL; -+ -+ BT_DBG("%s", hdev->name); -+ -+ hci_dev_lock(hdev); -+ -+ conn = conn_hash_lookup_ba(hdev, cc->link_type, &cc->bdaddr); -+ if (!conn) { -+ hci_dev_unlock(hdev); -+ return; -+ } -+ -+ if (!cc->status) { -+ conn->handle = __le16_to_cpu(cc->handle); -+ conn->state = BT_CONNECTED; -+ -+ if (test_bit(HCI_AUTH, &hdev->flags)) -+ conn->link_mode |= HCI_LM_AUTH; -+ -+ if (test_bit(HCI_ENCRYPT, &hdev->flags)) -+ conn->link_mode |= HCI_LM_ENCRYPT; -+ -+ -+ /* Set link policy */ -+ if (conn->type == ACL_LINK && hdev->link_policy) { -+ write_link_policy_cp lp; -+ lp.handle = cc->handle; -+ lp.policy = __cpu_to_le16(hdev->link_policy); -+ hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY, -+ WRITE_LINK_POLICY_CP_SIZE, &lp); -+ } -+ -+ /* Set packet type for incomming connection */ -+ if (!conn->out) { -+ change_conn_ptype_cp cp; -+ cp.handle = cc->handle; -+ cp.pkt_type = (conn->type == ACL_LINK) ? -+ __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK): -+ __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); -+ -+ hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE, -+ CHANGE_CONN_PTYPE_CP_SIZE, &cp); -+ } -+ } else -+ conn->state = BT_CLOSED; -+ -+ if (conn->type == ACL_LINK) { -+ struct hci_conn *sco = conn->link; -+ if (sco) { -+ if (!cc->status) -+ hci_add_sco(sco, conn->handle); -+ else { -+ hci_proto_connect_cfm(sco, cc->status); -+ hci_conn_del(sco); -+ } -+ } -+ } -+ -+ hci_proto_connect_cfm(conn, cc->status); -+ if (cc->status) -+ hci_conn_del(conn); -+ -+ hci_dev_unlock(hdev); -+} -+ -+/* Disconnect Complete */ -+static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ evt_disconn_complete *dc = (evt_disconn_complete *) skb->data; -+ struct hci_conn *conn = NULL; -+ __u16 handle = __le16_to_cpu(dc->handle); -+ -+ BT_DBG("%s status %d", hdev->name, dc->status); -+ -+ if (dc->status) -+ return; -+ -+ hci_dev_lock(hdev); -+ -+ conn = conn_hash_lookup_handle(hdev, handle); -+ if (conn) { -+ conn->state = BT_CLOSED; -+ hci_proto_disconn_ind(conn, dc->reason); -+ hci_conn_del(conn); -+ } -+ -+ hci_dev_unlock(hdev); -+} -+ -+/* Number of completed packets */ -+static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ evt_num_comp_pkts *nc = (evt_num_comp_pkts *) skb->data; -+ __u16 *ptr; -+ int i; -+ -+ skb_pull(skb, EVT_NUM_COMP_PKTS_SIZE); -+ -+ BT_DBG("%s num_hndl %d", hdev->name, nc->num_hndl); -+ -+ if (skb->len < nc->num_hndl * 4) { -+ BT_DBG("%s bad parameters", hdev->name); -+ return; -+ } -+ -+ tasklet_disable(&hdev->tx_task); -+ -+ for (i = 0, ptr = (__u16 *) skb->data; i < nc->num_hndl; i++) { -+ struct hci_conn *conn; -+ __u16 handle, count; -+ -+ handle = __le16_to_cpu(get_unaligned(ptr++)); -+ count = __le16_to_cpu(get_unaligned(ptr++)); -+ -+ conn = conn_hash_lookup_handle(hdev, handle); -+ if (conn) { -+ conn->sent -= count; -+ -+ if (conn->type == SCO_LINK) { -+ if ((hdev->sco_cnt += count) > hdev->sco_pkts) -+ hdev->sco_cnt = hdev->sco_pkts; -+ } else { -+ if ((hdev->acl_cnt += count) > hdev->acl_pkts) -+ hdev->acl_cnt = hdev->acl_pkts; -+ } -+ } -+ } -+ hci_sched_tx(hdev); -+ -+ tasklet_enable(&hdev->tx_task); -+} -+ -+/* Role Change */ -+static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ evt_role_change *rc = (evt_role_change *) skb->data; -+ struct hci_conn *conn = NULL; -+ -+ BT_DBG("%s status %d", hdev->name, rc->status); -+ -+ if (rc->status) -+ return; -+ -+ hci_dev_lock(hdev); -+ -+ conn = conn_hash_lookup_ba(hdev, ACL_LINK, &rc->bdaddr); -+ if (conn) { -+ if (rc->role) -+ conn->link_mode &= ~HCI_LM_MASTER; -+ else -+ conn->link_mode |= HCI_LM_MASTER; -+ } -+ -+ hci_dev_unlock(hdev); -+} -+ -+/* Authentication Complete */ -+static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ evt_auth_complete *ac = (evt_auth_complete *) skb->data; -+ struct hci_conn *conn = NULL; -+ __u16 handle = __le16_to_cpu(ac->handle); -+ -+ BT_DBG("%s status %d", hdev->name, ac->status); -+ -+ hci_dev_lock(hdev); -+ -+ conn = conn_hash_lookup_handle(hdev, handle); -+ if (conn) { -+ if (!ac->status) -+ conn->link_mode |= HCI_LM_AUTH; -+ clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); -+ -+ hci_proto_auth_cfm(conn, ac->status); -+ -+ if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { -+ if (!ac->status) { -+ set_conn_encrypt_cp ce; -+ ce.handle = __cpu_to_le16(conn->handle); -+ ce.encrypt = 1; -+ hci_send_cmd(conn->hdev, OGF_LINK_CTL, -+ OCF_SET_CONN_ENCRYPT, -+ SET_CONN_ENCRYPT_CP_SIZE, &ce); -+ } else { -+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); -+ hci_proto_encrypt_cfm(conn, ac->status); -+ } -+ } -+ } -+ -+ hci_dev_unlock(hdev); -+} -+ -+/* Encryption Change */ -+static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ evt_encrypt_change *ec = (evt_encrypt_change *) skb->data; -+ struct hci_conn *conn = NULL; -+ __u16 handle = __le16_to_cpu(ec->handle); -+ -+ BT_DBG("%s status %d", hdev->name, ec->status); -+ -+ hci_dev_lock(hdev); -+ -+ conn = conn_hash_lookup_handle(hdev, handle); -+ if (conn) { -+ if (!ec->status) { -+ if (ec->encrypt) -+ conn->link_mode |= HCI_LM_ENCRYPT; -+ else -+ conn->link_mode &= ~HCI_LM_ENCRYPT; -+ } -+ clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); -+ -+ hci_proto_encrypt_cfm(conn, ec->status); -+ } -+ -+ hci_dev_unlock(hdev); -+} -+ -+void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) -+{ -+ hci_event_hdr *he = (hci_event_hdr *) skb->data; -+ evt_cmd_status *cs; -+ evt_cmd_complete *ec; -+ __u16 opcode, ocf, ogf; -+ -+ skb_pull(skb, HCI_EVENT_HDR_SIZE); -+ -+ BT_DBG("%s evt 0x%x", hdev->name, he->evt); -+ -+ switch (he->evt) { -+ case EVT_NUM_COMP_PKTS: -+ hci_num_comp_pkts_evt(hdev, skb); -+ break; -+ -+ case EVT_INQUIRY_COMPLETE: -+ hci_inquiry_complete_evt(hdev, skb); -+ break; -+ -+ case EVT_INQUIRY_RESULT: -+ hci_inquiry_result_evt(hdev, skb); -+ break; -+ -+ case EVT_INQUIRY_RESULT_WITH_RSSI: -+ hci_inquiry_result_with_rssi_evt(hdev, skb); -+ break; -+ -+ case EVT_CONN_REQUEST: -+ hci_conn_request_evt(hdev, skb); -+ break; -+ -+ case EVT_CONN_COMPLETE: -+ hci_conn_complete_evt(hdev, skb); -+ break; -+ -+ case EVT_DISCONN_COMPLETE: -+ hci_disconn_complete_evt(hdev, skb); -+ break; -+ -+ case EVT_ROLE_CHANGE: -+ hci_role_change_evt(hdev, skb); -+ break; -+ -+ case EVT_AUTH_COMPLETE: -+ hci_auth_complete_evt(hdev, skb); -+ break; -+ -+ case EVT_ENCRYPT_CHANGE: -+ hci_encrypt_change_evt(hdev, skb); -+ break; -+ -+ case EVT_CMD_STATUS: -+ cs = (evt_cmd_status *) skb->data; -+ skb_pull(skb, EVT_CMD_STATUS_SIZE); -+ -+ opcode = __le16_to_cpu(cs->opcode); -+ ogf = cmd_opcode_ogf(opcode); -+ ocf = cmd_opcode_ocf(opcode); -+ -+ switch (ogf) { -+ case OGF_INFO_PARAM: -+ hci_cs_info_param(hdev, ocf, cs->status); -+ break; -+ -+ case OGF_HOST_CTL: -+ hci_cs_host_ctl(hdev, ocf, cs->status); -+ break; -+ -+ case OGF_LINK_CTL: -+ hci_cs_link_ctl(hdev, ocf, cs->status); -+ break; -+ -+ case OGF_LINK_POLICY: -+ hci_cs_link_policy(hdev, ocf, cs->status); -+ break; -+ -+ default: -+ BT_DBG("%s Command Status OGF %x", hdev->name, ogf); -+ break; -+ }; -+ -+ if (cs->ncmd) { -+ atomic_set(&hdev->cmd_cnt, 1); -+ if (!skb_queue_empty(&hdev->cmd_q)) -+ hci_sched_cmd(hdev); -+ } -+ break; -+ -+ case EVT_CMD_COMPLETE: -+ ec = (evt_cmd_complete *) skb->data; -+ skb_pull(skb, EVT_CMD_COMPLETE_SIZE); -+ -+ opcode = __le16_to_cpu(ec->opcode); -+ ogf = cmd_opcode_ogf(opcode); -+ ocf = cmd_opcode_ocf(opcode); -+ -+ switch (ogf) { -+ case OGF_INFO_PARAM: -+ hci_cc_info_param(hdev, ocf, skb); -+ break; -+ -+ case OGF_HOST_CTL: -+ hci_cc_host_ctl(hdev, ocf, skb); -+ break; -+ -+ case OGF_LINK_CTL: -+ hci_cc_link_ctl(hdev, ocf, skb); -+ break; -+ -+ case OGF_LINK_POLICY: -+ hci_cc_link_policy(hdev, ocf, skb); -+ break; -+ -+ default: -+ BT_DBG("%s Command Completed OGF %x", hdev->name, ogf); -+ break; -+ }; -+ -+ if (ec->ncmd) { -+ atomic_set(&hdev->cmd_cnt, 1); -+ if (!skb_queue_empty(&hdev->cmd_q)) -+ hci_sched_cmd(hdev); -+ } -+ break; -+ }; -+ -+ kfree_skb(skb); -+ hdev->stat.evt_rx++; -+} -+ -+/* General internal stack event */ -+void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) -+{ -+ hci_event_hdr *eh; -+ evt_stack_internal *si; -+ struct sk_buff *skb; -+ int size; -+ void *ptr; -+ -+ size = HCI_EVENT_HDR_SIZE + EVT_STACK_INTERNAL_SIZE + dlen; -+ skb = bluez_skb_alloc(size, GFP_ATOMIC); -+ if (!skb) -+ return; -+ -+ ptr = skb_put(skb, size); -+ -+ eh = ptr; -+ eh->evt = EVT_STACK_INTERNAL; -+ eh->plen = EVT_STACK_INTERNAL_SIZE + dlen; -+ ptr += HCI_EVENT_HDR_SIZE; -+ -+ si = ptr; -+ si->type = type; -+ memcpy(si->data, data, dlen); -+ -+ skb->pkt_type = HCI_EVENT_PKT; -+ skb->dev = (void *) hdev; -+ hci_send_to_sock(hdev, skb); -+ kfree_skb(skb); -+} ---- linux/net/bluetooth/hci_sock.c~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/net/bluetooth/hci_sock.c 2004-01-25 23:37:39.000000000 +0100 -@@ -25,7 +25,7 @@ - /* - * BlueZ HCI socket layer. - * -- * $Id: hci_sock.c,v 1.9 2001/08/05 06:02:16 maxk Exp $ -+ * $Id: hci_sock.c,v 1.5 2002/07/22 20:32:54 maxk Exp $ - */ - - #include <linux/config.h> -@@ -49,45 +49,54 @@ - - #include <asm/system.h> - #include <asm/uaccess.h> -+#include <asm/unaligned.h> - - #include <net/bluetooth/bluetooth.h> --#include <net/bluetooth/bluez.h> - #include <net/bluetooth/hci_core.h> - - #ifndef HCI_SOCK_DEBUG --#undef DBG --#define DBG( A... ) -+#undef BT_DBG -+#define BT_DBG( A... ) - #endif - --/* HCI socket interface */ -+/* ----- HCI socket interface ----- */ -+ -+/* Security filter */ -+static struct hci_sec_filter hci_sec_filter = { -+ /* Packet types */ -+ 0x10, -+ /* Events */ -+ { 0x1000d9fe, 0x0000300c }, -+ /* Commands */ -+ { -+ { 0x0 }, -+ /* OGF_LINK_CTL */ -+ { 0xbe000006, 0x00000001, 0x0000, 0x00 }, -+ /* OGF_LINK_POLICY */ -+ { 0x00005200, 0x00000000, 0x0000, 0x00 }, -+ /* OGF_HOST_CTL */ -+ { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 }, -+ /* OGF_INFO_PARAM */ -+ { 0x000002be, 0x00000000, 0x0000, 0x00 }, -+ /* OGF_STATUS_PARAM */ -+ { 0x000000ea, 0x00000000, 0x0000, 0x00 } -+ } -+}; - - static struct bluez_sock_list hci_sk_list = { - lock: RW_LOCK_UNLOCKED - }; - --static struct sock *hci_sock_lookup(struct hci_dev *hdev) --{ -- struct sock *sk; -- -- read_lock(&hci_sk_list.lock); -- for (sk = hci_sk_list.head; sk; sk = sk->next) { -- if (hci_pi(sk)->hdev == hdev) -- break; -- } -- read_unlock(&hci_sk_list.lock); -- return sk; --} -- - /* Send frame to RAW socket */ - void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) - { - struct sock * sk; - -- DBG("hdev %p len %d", hdev, skb->len); -+ BT_DBG("hdev %p len %d", hdev, skb->len); - - read_lock(&hci_sk_list.lock); - for (sk = hci_sk_list.head; sk; sk = sk->next) { -- struct hci_filter *flt; -+ struct hci_filter *flt; - struct sk_buff *nskb; - - if (sk->state != BT_BOUND || hci_pi(sk)->hdev != hdev) -@@ -100,13 +109,19 @@ - /* Apply filter */ - flt = &hci_pi(sk)->filter; - -- if (!test_bit(skb->pkt_type, &flt->type_mask)) -+ if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask)) - continue; - - if (skb->pkt_type == HCI_EVENT_PKT) { -- register int evt = (*(__u8 *)skb->data & 63); -+ register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS); -+ -+ if (!hci_test_bit(evt, &flt->event_mask)) -+ continue; - -- if (!test_bit(evt, &flt->event_mask)) -+ if (flt->opcode && ((evt == EVT_CMD_COMPLETE && -+ flt->opcode != *(__u16 *)(skb->data + 3)) || -+ (evt == EVT_CMD_STATUS && -+ flt->opcode != *(__u16 *)(skb->data + 4)))) - continue; - } - -@@ -116,8 +131,8 @@ - /* Put type byte before the data */ - memcpy(skb_push(nskb, 1), &nskb->pkt_type, 1); - -- skb_queue_tail(&sk->receive_queue, nskb); -- sk->data_ready(sk, nskb->len); -+ if (sock_queue_rcv_skb(sk, nskb)) -+ kfree_skb(nskb); - } - read_unlock(&hci_sk_list.lock); - } -@@ -127,7 +142,7 @@ - struct sock *sk = sock->sk; - struct hci_dev *hdev = hci_pi(sk)->hdev; - -- DBG("sock %p sk %p", sock, sk); -+ BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; -@@ -135,9 +150,7 @@ - bluez_sock_unlink(&hci_sk_list, sk); - - if (hdev) { -- if (!hci_sock_lookup(hdev)) -- hdev->flags &= ~HCI_SOCK; -- -+ atomic_dec(&hdev->promisc); - hci_dev_put(hdev); - } - -@@ -149,24 +162,55 @@ - sock_put(sk); - - MOD_DEC_USE_COUNT; -- - return 0; - } - --static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -+/* Ioctls that require bound socket */ -+static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) - { -- struct sock *sk = sock->sk; - struct hci_dev *hdev = hci_pi(sk)->hdev; -- __u32 mode; - -- DBG("cmd %x arg %lx", cmd, arg); -+ if (!hdev) -+ return -EBADFD; - - switch (cmd) { -- case HCIGETINFO: -- return hci_dev_info(arg); -+ case HCISETRAW: -+ if (!capable(CAP_NET_ADMIN)) -+ return -EACCES; -+ -+ if (arg) -+ set_bit(HCI_RAW, &hdev->flags); -+ else -+ clear_bit(HCI_RAW, &hdev->flags); -+ -+ return 0; -+ -+ case HCIGETCONNINFO: -+ return hci_get_conn_info(hdev, arg); - -+ default: -+ if (hdev->ioctl) -+ return hdev->ioctl(hdev, cmd, arg); -+ return -EINVAL; -+ } -+} -+ -+static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -+{ -+ struct sock *sk = sock->sk; -+ int err; -+ -+ BT_DBG("cmd %x arg %lx", cmd, arg); -+ -+ switch (cmd) { - case HCIGETDEVLIST: -- return hci_dev_list(arg); -+ return hci_get_dev_list(arg); -+ -+ case HCIGETDEVINFO: -+ return hci_get_dev_info(arg); -+ -+ case HCIGETCONNLIST: -+ return hci_get_conn_list(arg); - - case HCIDEVUP: - if (!capable(CAP_NET_ADMIN)) -@@ -183,48 +227,31 @@ - return -EACCES; - return hci_dev_reset(arg); - -- case HCIRESETSTAT: -+ case HCIDEVRESTAT: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_reset_stat(arg); - - case HCISETSCAN: -- if (!capable(CAP_NET_ADMIN)) -- return -EACCES; -- return hci_dev_setscan(arg); -- - case HCISETAUTH: -- if (!capable(CAP_NET_ADMIN)) -- return -EACCES; -- return hci_dev_setauth(arg); -- -- case HCISETRAW: -- if (!capable(CAP_NET_ADMIN)) -- return -EACCES; -- -- if (!hdev) -- return -EBADFD; -- -- if (arg) -- mode = HCI_RAW; -- else -- mode = HCI_NORMAL; -- -- return hci_dev_setmode(hdev, mode); -- -+ case HCISETENCRYPT: - case HCISETPTYPE: -+ case HCISETLINKPOL: -+ case HCISETLINKMODE: -+ case HCISETACLMTU: -+ case HCISETSCOMTU: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; -- return hci_dev_setptype(arg); -+ return hci_dev_cmd(cmd, arg); - - case HCIINQUIRY: - return hci_inquiry(arg); - -- case HCIGETCONNLIST: -- return hci_conn_list(arg); -- - default: -- return -EINVAL; -+ lock_sock(sk); -+ err = hci_sock_bound_ioctl(sk, cmd, arg); -+ release_sock(sk); -+ return err; - }; - } - -@@ -233,28 +260,35 @@ - struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr; - struct sock *sk = sock->sk; - struct hci_dev *hdev = NULL; -+ int err = 0; - -- DBG("sock %p sk %p", sock, sk); -+ BT_DBG("sock %p sk %p", sock, sk); - - if (!haddr || haddr->hci_family != AF_BLUETOOTH) - return -EINVAL; - -+ lock_sock(sk); -+ - if (hci_pi(sk)->hdev) { -- /* Already bound */ -- return 0; -+ err = -EALREADY; -+ goto done; - } - - if (haddr->hci_dev != HCI_DEV_NONE) { -- if (!(hdev = hci_dev_get(haddr->hci_dev))) -- return -ENODEV; -+ if (!(hdev = hci_dev_get(haddr->hci_dev))) { -+ err = -ENODEV; -+ goto done; -+ } - -- hdev->flags |= HCI_SOCK; -+ atomic_inc(&hdev->promisc); - } - - hci_pi(sk)->hdev = hdev; - sk->state = BT_BOUND; - -- return 0; -+done: -+ release_sock(sk); -+ return err; - } - - static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer) -@@ -262,73 +296,44 @@ - struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr; - struct sock *sk = sock->sk; - -- DBG("sock %p sk %p", sock, sk); -+ BT_DBG("sock %p sk %p", sock, sk); -+ -+ lock_sock(sk); - - *addr_len = sizeof(*haddr); - haddr->hci_family = AF_BLUETOOTH; - haddr->hci_dev = hci_pi(sk)->hdev->id; - -+ release_sock(sk); - return 0; - } - --static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, -- struct scm_cookie *scm) --{ -- struct sock *sk = sock->sk; -- struct hci_dev *hdev = hci_pi(sk)->hdev; -- struct sk_buff *skb; -- int err; -- -- DBG("sock %p sk %p", sock, sk); -- -- if (msg->msg_flags & MSG_OOB) -- return -EOPNOTSUPP; -- -- if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) -- return -EINVAL; -- -- if (!hdev) -- return -EBADFD; -- -- if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err))) -- return err; -- -- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { -- kfree_skb(skb); -- return -EFAULT; -- } -- -- skb->dev = (void *) hdev; -- skb->pkt_type = *((unsigned char *) skb->data); -- skb_pull(skb, 1); -- -- /* Send frame to HCI core */ -- hci_send_raw(skb); -- -- return len; --} -- - static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) - { - __u32 mask = hci_pi(sk)->cmsg_mask; - - if (mask & HCI_CMSG_DIR) - put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(int), &bluez_cb(skb)->incomming); -+ -+ if (mask & HCI_CMSG_TSTAMP) -+ put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, sizeof(skb->stamp), &skb->stamp); - } - --static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, -- int flags, struct scm_cookie *scm) -+static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm) - { - int noblock = flags & MSG_DONTWAIT; - struct sock *sk = sock->sk; - struct sk_buff *skb; - int copied, err; - -- DBG("sock %p sk %p", sock, sk); -+ BT_DBG("sock %p, sk %p", sock, sk); - -- if (flags & (MSG_OOB | MSG_PEEK)) -+ if (flags & (MSG_OOB)) - return -EOPNOTSUPP; - -+ if (sk->state == BT_CLOSED) -+ return 0; -+ - if (!(skb = skb_recv_datagram(sk, flags, noblock, &err))) - return err; - -@@ -343,28 +348,107 @@ - skb->h.raw = skb->data; - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); - -- if (hci_pi(sk)->cmsg_mask) -- hci_sock_cmsg(sk, msg, skb); -- -+ hci_sock_cmsg(sk, msg, skb); -+ - skb_free_datagram(sk, skb); - - return err ? : copied; - } - -+static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, -+ struct scm_cookie *scm) -+{ -+ struct sock *sk = sock->sk; -+ struct hci_dev *hdev; -+ struct sk_buff *skb; -+ int err; -+ -+ BT_DBG("sock %p sk %p", sock, sk); -+ -+ if (msg->msg_flags & MSG_OOB) -+ return -EOPNOTSUPP; -+ -+ if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) -+ return -EINVAL; -+ -+ if (len < 4) -+ return -EINVAL; -+ -+ lock_sock(sk); -+ -+ if (!(hdev = hci_pi(sk)->hdev)) { -+ err = -EBADFD; -+ goto done; -+ } -+ -+ if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err))) -+ goto done; -+ -+ if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { -+ err = -EFAULT; -+ goto drop; -+ } -+ -+ skb->pkt_type = *((unsigned char *) skb->data); -+ skb_pull(skb, 1); -+ skb->dev = (void *) hdev; -+ -+ if (skb->pkt_type == HCI_COMMAND_PKT) { -+ u16 opcode = __le16_to_cpu(get_unaligned((u16 *)skb->data)); -+ u16 ogf = cmd_opcode_ogf(opcode); -+ u16 ocf = cmd_opcode_ocf(opcode); -+ -+ if (((ogf > HCI_SFLT_MAX_OGF) || -+ !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) && -+ !capable(CAP_NET_RAW)) { -+ err = -EPERM; -+ goto drop; -+ } -+ -+ if (test_bit(HCI_RAW, &hdev->flags) || (ogf == OGF_VENDOR_CMD)) { -+ skb_queue_tail(&hdev->raw_q, skb); -+ hci_sched_tx(hdev); -+ } else { -+ skb_queue_tail(&hdev->cmd_q, skb); -+ hci_sched_cmd(hdev); -+ } -+ } else { -+ if (!capable(CAP_NET_RAW)) { -+ err = -EPERM; -+ goto drop; -+ } -+ -+ skb_queue_tail(&hdev->raw_q, skb); -+ hci_sched_tx(hdev); -+ } -+ -+ err = len; -+ -+done: -+ release_sock(sk); -+ return err; -+ -+drop: -+ kfree_skb(skb); -+ goto done; -+} -+ - int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len) - { - struct sock *sk = sock->sk; -- struct hci_filter flt; -+ struct hci_filter flt = { opcode: 0 }; - int err = 0, opt = 0; - -- DBG("sk %p, opt %d", sk, optname); -+ BT_DBG("sk %p, opt %d", sk, optname); - - lock_sock(sk); - - switch (optname) { - case HCI_DATA_DIR: -- if (get_user(opt, (int *)optval)) -- return -EFAULT; -+ if (get_user(opt, (int *)optval)) { -+ err = -EFAULT; -+ break; -+ } - - if (opt) - hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR; -@@ -372,12 +456,31 @@ - hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR; - break; - -+ case HCI_TIME_STAMP: -+ if (get_user(opt, (int *)optval)) { -+ err = -EFAULT; -+ break; -+ } -+ -+ if (opt) -+ hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP; -+ else -+ hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP; -+ break; -+ - case HCI_FILTER: - len = MIN(len, sizeof(struct hci_filter)); - if (copy_from_user(&flt, optval, len)) { - err = -EFAULT; - break; - } -+ -+ if (!capable(CAP_NET_RAW)) { -+ flt.type_mask &= hci_sec_filter.type_mask; -+ flt.event_mask[0] &= hci_sec_filter.event_mask[0]; -+ flt.event_mask[1] &= hci_sec_filter.event_mask[1]; -+ } -+ - memcpy(&hci_pi(sk)->filter, &flt, len); - break; - -@@ -409,6 +512,16 @@ - return -EFAULT; - break; - -+ case HCI_TIME_STAMP: -+ if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP) -+ opt = 1; -+ else -+ opt = 0; -+ -+ if (put_user(opt, optval)) -+ return -EFAULT; -+ break; -+ - case HCI_FILTER: - len = MIN(len, sizeof(struct hci_filter)); - if (copy_to_user(optval, &hci_pi(sk)->filter, len)) -@@ -446,7 +559,7 @@ - { - struct sock *sk; - -- DBG("sock %p", sock); -+ BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; -@@ -464,44 +577,31 @@ - sk->protocol = protocol; - sk->state = BT_OPEN; - -- /* Initialize filter */ -- hci_pi(sk)->filter.type_mask = (1<<HCI_EVENT_PKT); -- hci_pi(sk)->filter.event_mask[0] = ~0L; -- hci_pi(sk)->filter.event_mask[1] = ~0L; -- - bluez_sock_link(&hci_sk_list, sk); - - MOD_INC_USE_COUNT; -- - return 0; - } - - static int hci_sock_dev_event(struct notifier_block *this, unsigned long event, void *ptr) - { - struct hci_dev *hdev = (struct hci_dev *) ptr; -- struct sk_buff *skb; -- -- DBG("hdev %s event %ld", hdev->name, event); -+ evt_si_device sd; -+ -+ BT_DBG("hdev %s event %ld", hdev->name, event); - - /* Send event to sockets */ -- if ((skb = bluez_skb_alloc(HCI_EVENT_HDR_SIZE + EVT_HCI_DEV_EVENT_SIZE, GFP_ATOMIC))) { -- hci_event_hdr eh = { EVT_HCI_DEV_EVENT, EVT_HCI_DEV_EVENT_SIZE }; -- evt_hci_dev_event he = { event, hdev->id }; -- -- skb->pkt_type = HCI_EVENT_PKT; -- memcpy(skb_put(skb, HCI_EVENT_HDR_SIZE), &eh, HCI_EVENT_HDR_SIZE); -- memcpy(skb_put(skb, EVT_HCI_DEV_EVENT_SIZE), &he, EVT_HCI_DEV_EVENT_SIZE); -- -- hci_send_to_sock(NULL, skb); -- kfree_skb(skb); -- } -- -+ sd.event = event; -+ sd.dev_id = hdev->id; -+ hci_si_event(NULL, EVT_SI_DEVICE, EVT_SI_DEVICE_SIZE, &sd); -+ - if (event == HCI_DEV_UNREG) { - struct sock *sk; - - /* Detach sockets from device */ - read_lock(&hci_sk_list.lock); - for (sk = hci_sk_list.head; sk; sk = sk->next) { -+ bh_lock_sock(sk); - if (hci_pi(sk)->hdev == hdev) { - hci_pi(sk)->hdev = NULL; - sk->err = EPIPE; -@@ -510,6 +610,7 @@ - - hci_dev_put(hdev); - } -+ bh_unlock_sock(sk); - } - read_unlock(&hci_sk_list.lock); - } -@@ -529,21 +630,19 @@ - int hci_sock_init(void) - { - if (bluez_sock_register(BTPROTO_HCI, &hci_sock_family_ops)) { -- ERR("Can't register HCI socket"); -+ BT_ERR("Can't register HCI socket"); - return -EPROTO; - } - - hci_register_notifier(&hci_sock_nblock); -- - return 0; - } - - int hci_sock_cleanup(void) - { - if (bluez_sock_unregister(BTPROTO_HCI)) -- ERR("Can't unregister HCI socket"); -+ BT_ERR("Can't unregister HCI socket"); - - hci_unregister_notifier(&hci_sock_nblock); -- - return 0; - } ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/l2cap.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,2187 @@ -+/* -+ BlueZ - Bluetooth protocol stack for Linux -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * BlueZ L2CAP core and sockets. -+ * -+ * $Id: l2cap.c,v 1.15 2002/09/09 01:14:52 maxk Exp $ -+ */ -+#define VERSION "2.3" -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/kernel.h> -+#include <linux/major.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/poll.h> -+#include <linux/fcntl.h> -+#include <linux/init.h> -+#include <linux/skbuff.h> -+#include <linux/interrupt.h> -+#include <linux/socket.h> -+#include <linux/skbuff.h> -+#include <linux/proc_fs.h> -+#include <linux/list.h> -+#include <net/sock.h> -+ -+#include <asm/system.h> -+#include <asm/uaccess.h> -+#include <asm/unaligned.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+#include <net/bluetooth/l2cap.h> -+ -+#ifndef L2CAP_DEBUG -+#undef BT_DBG -+#define BT_DBG( A... ) -+#endif -+ -+static struct proto_ops l2cap_sock_ops; -+ -+struct bluez_sock_list l2cap_sk_list = { -+ lock: RW_LOCK_UNLOCKED -+}; -+ -+static int l2cap_conn_del(struct hci_conn *conn, int err); -+ -+static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent); -+static void l2cap_chan_del(struct sock *sk, int err); -+static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len); -+ -+static void __l2cap_sock_close(struct sock *sk, int reason); -+static void l2cap_sock_close(struct sock *sk); -+static void l2cap_sock_kill(struct sock *sk); -+ -+static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data); -+static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data); -+ -+/* ----- L2CAP timers ------ */ -+static void l2cap_sock_timeout(unsigned long arg) -+{ -+ struct sock *sk = (struct sock *) arg; -+ -+ BT_DBG("sock %p state %d", sk, sk->state); -+ -+ bh_lock_sock(sk); -+ __l2cap_sock_close(sk, ETIMEDOUT); -+ bh_unlock_sock(sk); -+ -+ l2cap_sock_kill(sk); -+ sock_put(sk); -+} -+ -+static void l2cap_sock_set_timer(struct sock *sk, long timeout) -+{ -+ BT_DBG("sk %p state %d timeout %ld", sk, sk->state, timeout); -+ -+ if (!mod_timer(&sk->timer, jiffies + timeout)) -+ sock_hold(sk); -+} -+ -+static void l2cap_sock_clear_timer(struct sock *sk) -+{ -+ BT_DBG("sock %p state %d", sk, sk->state); -+ -+ if (timer_pending(&sk->timer) && del_timer(&sk->timer)) -+ __sock_put(sk); -+} -+ -+static void l2cap_sock_init_timer(struct sock *sk) -+{ -+ init_timer(&sk->timer); -+ sk->timer.function = l2cap_sock_timeout; -+ sk->timer.data = (unsigned long)sk; -+} -+ -+/* -------- L2CAP connections --------- */ -+static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, __u8 status) -+{ -+ struct l2cap_conn *conn; -+ -+ if ((conn = hcon->l2cap_data)) -+ return conn; -+ -+ if (status) -+ return conn; -+ -+ if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_ATOMIC))) -+ return NULL; -+ memset(conn, 0, sizeof(struct l2cap_conn)); -+ -+ hcon->l2cap_data = conn; -+ conn->hcon = hcon; -+ -+ conn->mtu = hcon->hdev->acl_mtu; -+ conn->src = &hcon->hdev->bdaddr; -+ conn->dst = &hcon->dst; -+ -+ spin_lock_init(&conn->lock); -+ conn->chan_list.lock = RW_LOCK_UNLOCKED; -+ -+ BT_DBG("hcon %p conn %p", hcon, conn); -+ -+ MOD_INC_USE_COUNT; -+ return conn; -+} -+ -+static int l2cap_conn_del(struct hci_conn *hcon, int err) -+{ -+ struct l2cap_conn *conn; -+ struct sock *sk; -+ -+ if (!(conn = hcon->l2cap_data)) -+ return 0; -+ -+ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); -+ -+ if (conn->rx_skb) -+ kfree_skb(conn->rx_skb); -+ -+ /* Kill channels */ -+ while ((sk = conn->chan_list.head)) { -+ bh_lock_sock(sk); -+ l2cap_chan_del(sk, err); -+ bh_unlock_sock(sk); -+ l2cap_sock_kill(sk); -+ } -+ -+ hcon->l2cap_data = NULL; -+ kfree(conn); -+ -+ MOD_DEC_USE_COUNT; -+ return 0; -+} -+ -+/* -------- Socket interface ---------- */ -+static struct sock *__l2cap_get_sock_by_addr(__u16 psm, bdaddr_t *src) -+{ -+ struct sock *sk; -+ for (sk = l2cap_sk_list.head; sk; sk = sk->next) { -+ if (sk->sport == psm && !bacmp(&bluez_pi(sk)->src, src)) -+ break; -+ } -+ return sk; -+} -+ -+/* Find socket with psm and source bdaddr. -+ * Returns closest match. -+ */ -+static struct sock *__l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src) -+{ -+ struct sock *sk, *sk1 = NULL; -+ -+ for (sk = l2cap_sk_list.head; sk; sk = sk->next) { -+ if (state && sk->state != state) -+ continue; -+ -+ if (l2cap_pi(sk)->psm == psm) { -+ /* Exact match. */ -+ if (!bacmp(&bluez_pi(sk)->src, src)) -+ break; -+ -+ /* Closest match */ -+ if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY)) -+ sk1 = sk; -+ } -+ } -+ return sk ? sk : sk1; -+} -+ -+/* Find socket with given address (psm, src). -+ * Returns locked socket */ -+static inline struct sock *l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src) -+{ -+ struct sock *s; -+ read_lock(&l2cap_sk_list.lock); -+ s = __l2cap_get_sock_by_psm(state, psm, src); -+ if (s) bh_lock_sock(s); -+ read_unlock(&l2cap_sk_list.lock); -+ return s; -+} -+ -+static void l2cap_sock_destruct(struct sock *sk) -+{ -+ BT_DBG("sk %p", sk); -+ -+ skb_queue_purge(&sk->receive_queue); -+ skb_queue_purge(&sk->write_queue); -+ -+ MOD_DEC_USE_COUNT; -+} -+ -+static void l2cap_sock_cleanup_listen(struct sock *parent) -+{ -+ struct sock *sk; -+ -+ BT_DBG("parent %p", parent); -+ -+ /* Close not yet accepted channels */ -+ while ((sk = bluez_accept_dequeue(parent, NULL))) -+ l2cap_sock_close(sk); -+ -+ parent->state = BT_CLOSED; -+ parent->zapped = 1; -+} -+ -+/* Kill socket (only if zapped and orphan) -+ * Must be called on unlocked socket. -+ */ -+static void l2cap_sock_kill(struct sock *sk) -+{ -+ if (!sk->zapped || sk->socket) -+ return; -+ -+ BT_DBG("sk %p state %d", sk, sk->state); -+ -+ /* Kill poor orphan */ -+ bluez_sock_unlink(&l2cap_sk_list, sk); -+ sk->dead = 1; -+ sock_put(sk); -+} -+ -+/* Close socket. -+ */ -+static void __l2cap_sock_close(struct sock *sk, int reason) -+{ -+ BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket); -+ -+ switch (sk->state) { -+ case BT_LISTEN: -+ l2cap_sock_cleanup_listen(sk); -+ break; -+ -+ case BT_CONNECTED: -+ case BT_CONFIG: -+ case BT_CONNECT2: -+ if (sk->type == SOCK_SEQPACKET) { -+ struct l2cap_conn *conn = l2cap_pi(sk)->conn; -+ l2cap_disconn_req req; -+ -+ sk->state = BT_DISCONN; -+ l2cap_sock_set_timer(sk, sk->sndtimeo); -+ -+ req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); -+ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); -+ l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req); -+ } else { -+ l2cap_chan_del(sk, reason); -+ } -+ break; -+ -+ case BT_CONNECT: -+ case BT_DISCONN: -+ l2cap_chan_del(sk, reason); -+ break; -+ -+ default: -+ sk->zapped = 1; -+ break; -+ }; -+} -+ -+/* Must be called on unlocked socket. */ -+static void l2cap_sock_close(struct sock *sk) -+{ -+ l2cap_sock_clear_timer(sk); -+ lock_sock(sk); -+ __l2cap_sock_close(sk, ECONNRESET); -+ release_sock(sk); -+ l2cap_sock_kill(sk); -+} -+ -+static void l2cap_sock_init(struct sock *sk, struct sock *parent) -+{ -+ struct l2cap_pinfo *pi = l2cap_pi(sk); -+ -+ BT_DBG("sk %p", sk); -+ -+ if (parent) { -+ sk->type = parent->type; -+ pi->imtu = l2cap_pi(parent)->imtu; -+ pi->omtu = l2cap_pi(parent)->omtu; -+ pi->link_mode = l2cap_pi(parent)->link_mode; -+ } else { -+ pi->imtu = L2CAP_DEFAULT_MTU; -+ pi->omtu = 0; -+ pi->link_mode = 0; -+ } -+ -+ /* Default config options */ -+ pi->conf_mtu = L2CAP_DEFAULT_MTU; -+ pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; -+} -+ -+static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio) -+{ -+ struct sock *sk; -+ -+ if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1))) -+ return NULL; -+ -+ bluez_sock_init(sock, sk); -+ -+ sk->zapped = 0; -+ -+ sk->destruct = l2cap_sock_destruct; -+ sk->sndtimeo = L2CAP_CONN_TIMEOUT; -+ -+ sk->protocol = proto; -+ sk->state = BT_OPEN; -+ -+ l2cap_sock_init_timer(sk); -+ -+ bluez_sock_link(&l2cap_sk_list, sk); -+ -+ MOD_INC_USE_COUNT; -+ return sk; -+} -+ -+static int l2cap_sock_create(struct socket *sock, int protocol) -+{ -+ struct sock *sk; -+ -+ BT_DBG("sock %p", sock); -+ -+ sock->state = SS_UNCONNECTED; -+ -+ if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) -+ return -ESOCKTNOSUPPORT; -+ -+ if (sock->type == SOCK_RAW && !capable(CAP_NET_RAW)) -+ return -EPERM; -+ -+ sock->ops = &l2cap_sock_ops; -+ -+ if (!(sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL))) -+ return -ENOMEM; -+ -+ l2cap_sock_init(sk, NULL); -+ return 0; -+} -+ -+static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) -+{ -+ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; -+ struct sock *sk = sock->sk; -+ int err = 0; -+ -+ BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm); -+ -+ if (!addr || addr->sa_family != AF_BLUETOOTH) -+ return -EINVAL; -+ -+ lock_sock(sk); -+ -+ if (sk->state != BT_OPEN) { -+ err = -EBADFD; -+ goto done; -+ } -+ -+ write_lock_bh(&l2cap_sk_list.lock); -+ if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) { -+ err = -EADDRINUSE; -+ } else { -+ /* Save source address */ -+ bacpy(&bluez_pi(sk)->src, &la->l2_bdaddr); -+ l2cap_pi(sk)->psm = la->l2_psm; -+ sk->sport = la->l2_psm; -+ sk->state = BT_BOUND; -+ } -+ write_unlock_bh(&l2cap_sk_list.lock); -+ -+done: -+ release_sock(sk); -+ return err; -+} -+ -+static int l2cap_do_connect(struct sock *sk) -+{ -+ bdaddr_t *src = &bluez_pi(sk)->src; -+ bdaddr_t *dst = &bluez_pi(sk)->dst; -+ struct l2cap_conn *conn; -+ struct hci_conn *hcon; -+ struct hci_dev *hdev; -+ int err = 0; -+ -+ BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm); -+ -+ if (!(hdev = hci_get_route(dst, src))) -+ return -EHOSTUNREACH; -+ -+ hci_dev_lock_bh(hdev); -+ -+ err = -ENOMEM; -+ -+ hcon = hci_connect(hdev, ACL_LINK, dst); -+ if (!hcon) -+ goto done; -+ -+ conn = l2cap_conn_add(hcon, 0); -+ if (!conn) { -+ hci_conn_put(hcon); -+ goto done; -+ } -+ -+ err = 0; -+ -+ /* Update source addr of the socket */ -+ bacpy(src, conn->src); -+ -+ l2cap_chan_add(conn, sk, NULL); -+ -+ sk->state = BT_CONNECT; -+ l2cap_sock_set_timer(sk, sk->sndtimeo); -+ -+ if (hcon->state == BT_CONNECTED) { -+ if (sk->type == SOCK_SEQPACKET) { -+ l2cap_conn_req req; -+ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); -+ req.psm = l2cap_pi(sk)->psm; -+ l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req); -+ } else { -+ l2cap_sock_clear_timer(sk); -+ sk->state = BT_CONNECTED; -+ } -+ } -+ -+done: -+ hci_dev_unlock_bh(hdev); -+ hci_dev_put(hdev); -+ return err; -+} -+ -+static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) -+{ -+ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; -+ struct sock *sk = sock->sk; -+ int err = 0; -+ -+ lock_sock(sk); -+ -+ BT_DBG("sk %p", sk); -+ -+ if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) { -+ err = -EINVAL; -+ goto done; -+ } -+ -+ if (sk->type == SOCK_SEQPACKET && !la->l2_psm) { -+ err = -EINVAL; -+ goto done; -+ } -+ -+ switch(sk->state) { -+ case BT_CONNECT: -+ case BT_CONNECT2: -+ case BT_CONFIG: -+ /* Already connecting */ -+ goto wait; -+ -+ case BT_CONNECTED: -+ /* Already connected */ -+ goto done; -+ -+ case BT_OPEN: -+ case BT_BOUND: -+ /* Can connect */ -+ break; -+ -+ default: -+ err = -EBADFD; -+ goto done; -+ } -+ -+ /* Set destination address and psm */ -+ bacpy(&bluez_pi(sk)->dst, &la->l2_bdaddr); -+ l2cap_pi(sk)->psm = la->l2_psm; -+ -+ if ((err = l2cap_do_connect(sk))) -+ goto done; -+ -+wait: -+ err = bluez_sock_wait_state(sk, BT_CONNECTED, -+ sock_sndtimeo(sk, flags & O_NONBLOCK)); -+ -+done: -+ release_sock(sk); -+ return err; -+} -+ -+int l2cap_sock_listen(struct socket *sock, int backlog) -+{ -+ struct sock *sk = sock->sk; -+ int err = 0; -+ -+ BT_DBG("sk %p backlog %d", sk, backlog); -+ -+ lock_sock(sk); -+ -+ if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) { -+ err = -EBADFD; -+ goto done; -+ } -+ -+ if (!l2cap_pi(sk)->psm) { -+ err = -EINVAL; -+ goto done; -+ } -+ -+ sk->max_ack_backlog = backlog; -+ sk->ack_backlog = 0; -+ sk->state = BT_LISTEN; -+ -+done: -+ release_sock(sk); -+ return err; -+} -+ -+int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) -+{ -+ DECLARE_WAITQUEUE(wait, current); -+ struct sock *sk = sock->sk, *nsk; -+ long timeo; -+ int err = 0; -+ -+ lock_sock(sk); -+ -+ if (sk->state != BT_LISTEN) { -+ err = -EBADFD; -+ goto done; -+ } -+ -+ timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); -+ -+ BT_DBG("sk %p timeo %ld", sk, timeo); -+ -+ /* Wait for an incoming connection. (wake-one). */ -+ add_wait_queue_exclusive(sk->sleep, &wait); -+ while (!(nsk = bluez_accept_dequeue(sk, newsock))) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ if (!timeo) { -+ err = -EAGAIN; -+ break; -+ } -+ -+ release_sock(sk); -+ timeo = schedule_timeout(timeo); -+ lock_sock(sk); -+ -+ if (sk->state != BT_LISTEN) { -+ err = -EBADFD; -+ break; -+ } -+ -+ if (signal_pending(current)) { -+ err = sock_intr_errno(timeo); -+ break; -+ } -+ } -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(sk->sleep, &wait); -+ -+ if (err) -+ goto done; -+ -+ newsock->state = SS_CONNECTED; -+ -+ BT_DBG("new socket %p", nsk); -+ -+done: -+ release_sock(sk); -+ return err; -+} -+ -+static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) -+{ -+ struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; -+ struct sock *sk = sock->sk; -+ -+ BT_DBG("sock %p, sk %p", sock, sk); -+ -+ addr->sa_family = AF_BLUETOOTH; -+ *len = sizeof(struct sockaddr_l2); -+ -+ if (peer) -+ bacpy(&la->l2_bdaddr, &bluez_pi(sk)->dst); -+ else -+ bacpy(&la->l2_bdaddr, &bluez_pi(sk)->src); -+ -+ la->l2_psm = l2cap_pi(sk)->psm; -+ return 0; -+} -+ -+static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) -+{ -+ struct sock *sk = sock->sk; -+ int err = 0; -+ -+ BT_DBG("sock %p, sk %p", sock, sk); -+ -+ if (sk->err) -+ return sock_error(sk); -+ -+ if (msg->msg_flags & MSG_OOB) -+ return -EOPNOTSUPP; -+ -+ /* Check outgoing MTU */ -+ if (len > l2cap_pi(sk)->omtu) -+ return -EINVAL; -+ -+ lock_sock(sk); -+ -+ if (sk->state == BT_CONNECTED) -+ err = l2cap_chan_send(sk, msg, len); -+ else -+ err = -ENOTCONN; -+ -+ release_sock(sk); -+ return err; -+} -+ -+static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) -+{ -+ struct sock *sk = sock->sk; -+ struct l2cap_options opts; -+ int err = 0, len; -+ __u32 opt; -+ -+ BT_DBG("sk %p", sk); -+ -+ lock_sock(sk); -+ -+ switch (optname) { -+ case L2CAP_OPTIONS: -+ len = MIN(sizeof(opts), optlen); -+ if (copy_from_user((char *)&opts, optval, len)) { -+ err = -EFAULT; -+ break; -+ } -+ l2cap_pi(sk)->imtu = opts.imtu; -+ l2cap_pi(sk)->omtu = opts.omtu; -+ break; -+ -+ case L2CAP_LM: -+ if (get_user(opt, (__u32 *)optval)) { -+ err = -EFAULT; -+ break; -+ } -+ -+ l2cap_pi(sk)->link_mode = opt; -+ break; -+ -+ default: -+ err = -ENOPROTOOPT; -+ break; -+ } -+ -+ release_sock(sk); -+ return err; -+} -+ -+static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) -+{ -+ struct sock *sk = sock->sk; -+ struct l2cap_options opts; -+ struct l2cap_conninfo cinfo; -+ int len, err = 0; -+ -+ if (get_user(len, optlen)) -+ return -EFAULT; -+ -+ lock_sock(sk); -+ -+ switch (optname) { -+ case L2CAP_OPTIONS: -+ opts.imtu = l2cap_pi(sk)->imtu; -+ opts.omtu = l2cap_pi(sk)->omtu; -+ opts.flush_to = l2cap_pi(sk)->flush_to; -+ -+ len = MIN(len, sizeof(opts)); -+ if (copy_to_user(optval, (char *)&opts, len)) -+ err = -EFAULT; -+ -+ break; -+ -+ case L2CAP_LM: -+ if (put_user(l2cap_pi(sk)->link_mode, (__u32 *)optval)) -+ err = -EFAULT; -+ break; -+ -+ case L2CAP_CONNINFO: -+ if (sk->state != BT_CONNECTED) { -+ err = -ENOTCONN; -+ break; -+ } -+ -+ cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; -+ -+ len = MIN(len, sizeof(cinfo)); -+ if (copy_to_user(optval, (char *)&cinfo, len)) -+ err = -EFAULT; -+ -+ break; -+ -+ default: -+ err = -ENOPROTOOPT; -+ break; -+ } -+ -+ release_sock(sk); -+ return err; -+} -+ -+static int l2cap_sock_shutdown(struct socket *sock, int how) -+{ -+ struct sock *sk = sock->sk; -+ int err = 0; -+ -+ BT_DBG("sock %p, sk %p", sock, sk); -+ -+ if (!sk) return 0; -+ -+ lock_sock(sk); -+ if (!sk->shutdown) { -+ sk->shutdown = SHUTDOWN_MASK; -+ l2cap_sock_clear_timer(sk); -+ __l2cap_sock_close(sk, 0); -+ -+ if (sk->linger) -+ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime); -+ } -+ release_sock(sk); -+ return err; -+} -+ -+static int l2cap_sock_release(struct socket *sock) -+{ -+ struct sock *sk = sock->sk; -+ int err; -+ -+ BT_DBG("sock %p, sk %p", sock, sk); -+ -+ if (!sk) return 0; -+ -+ err = l2cap_sock_shutdown(sock, 2); -+ -+ sock_orphan(sk); -+ l2cap_sock_kill(sk); -+ return err; -+} -+ -+/* --------- L2CAP channels --------- */ -+static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid) -+{ -+ struct sock *s; -+ for (s = l->head; s; s = l2cap_pi(s)->next_c) { -+ if (l2cap_pi(s)->dcid == cid) -+ break; -+ } -+ return s; -+} -+ -+static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid) -+{ -+ struct sock *s; -+ for (s = l->head; s; s = l2cap_pi(s)->next_c) { -+ if (l2cap_pi(s)->scid == cid) -+ break; -+ } -+ return s; -+} -+ -+/* Find channel with given SCID. -+ * Returns locked socket */ -+static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid) -+{ -+ struct sock *s; -+ read_lock(&l->lock); -+ s = __l2cap_get_chan_by_scid(l, cid); -+ if (s) bh_lock_sock(s); -+ read_unlock(&l->lock); -+ return s; -+} -+ -+static __u16 l2cap_alloc_cid(struct l2cap_chan_list *l) -+{ -+ __u16 cid = 0x0040; -+ -+ for (; cid < 0xffff; cid++) { -+ if(!__l2cap_get_chan_by_scid(l, cid)) -+ return cid; -+ } -+ -+ return 0; -+} -+ -+static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk) -+{ -+ sock_hold(sk); -+ -+ if (l->head) -+ l2cap_pi(l->head)->prev_c = sk; -+ -+ l2cap_pi(sk)->next_c = l->head; -+ l2cap_pi(sk)->prev_c = NULL; -+ l->head = sk; -+} -+ -+static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk) -+{ -+ struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c; -+ -+ write_lock(&l->lock); -+ if (sk == l->head) -+ l->head = next; -+ -+ if (next) -+ l2cap_pi(next)->prev_c = prev; -+ if (prev) -+ l2cap_pi(prev)->next_c = next; -+ write_unlock(&l->lock); -+ -+ __sock_put(sk); -+} -+ -+static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) -+{ -+ struct l2cap_chan_list *l = &conn->chan_list; -+ -+ BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid); -+ -+ l2cap_pi(sk)->conn = conn; -+ -+ if (sk->type == SOCK_SEQPACKET) { -+ /* Alloc CID for connection-oriented socket */ -+ l2cap_pi(sk)->scid = l2cap_alloc_cid(l); -+ } else if (sk->type == SOCK_DGRAM) { -+ /* Connectionless socket */ -+ l2cap_pi(sk)->scid = 0x0002; -+ l2cap_pi(sk)->dcid = 0x0002; -+ l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; -+ } else { -+ /* Raw socket can send/recv signalling messages only */ -+ l2cap_pi(sk)->scid = 0x0001; -+ l2cap_pi(sk)->dcid = 0x0001; -+ l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; -+ } -+ -+ __l2cap_chan_link(l, sk); -+ -+ if (parent) -+ bluez_accept_enqueue(parent, sk); -+} -+ -+static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) -+{ -+ struct l2cap_chan_list *l = &conn->chan_list; -+ write_lock(&l->lock); -+ __l2cap_chan_add(conn, sk, parent); -+ write_unlock(&l->lock); -+} -+ -+/* Delete channel. -+ * Must be called on the locked socket. */ -+static void l2cap_chan_del(struct sock *sk, int err) -+{ -+ struct l2cap_conn *conn = l2cap_pi(sk)->conn; -+ struct sock *parent = bluez_pi(sk)->parent; -+ -+ l2cap_sock_clear_timer(sk); -+ -+ BT_DBG("sk %p, conn %p, err %d", sk, conn, err); -+ -+ if (conn) { -+ /* Unlink from channel list */ -+ l2cap_chan_unlink(&conn->chan_list, sk); -+ l2cap_pi(sk)->conn = NULL; -+ hci_conn_put(conn->hcon); -+ } -+ -+ sk->state = BT_CLOSED; -+ sk->zapped = 1; -+ -+ if (err) -+ sk->err = err; -+ -+ if (parent) -+ parent->data_ready(parent, 0); -+ else -+ sk->state_change(sk); -+} -+ -+static void l2cap_conn_ready(struct l2cap_conn *conn) -+{ -+ struct l2cap_chan_list *l = &conn->chan_list; -+ struct sock *sk; -+ -+ BT_DBG("conn %p", conn); -+ -+ read_lock(&l->lock); -+ -+ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { -+ bh_lock_sock(sk); -+ -+ if (sk->type != SOCK_SEQPACKET) { -+ l2cap_sock_clear_timer(sk); -+ sk->state = BT_CONNECTED; -+ sk->state_change(sk); -+ } else if (sk->state == BT_CONNECT) { -+ l2cap_conn_req req; -+ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); -+ req.psm = l2cap_pi(sk)->psm; -+ l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req); -+ } -+ -+ bh_unlock_sock(sk); -+ } -+ -+ read_unlock(&l->lock); -+} -+ -+/* Notify sockets that we cannot guaranty reliability anymore */ -+static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) -+{ -+ struct l2cap_chan_list *l = &conn->chan_list; -+ struct sock *sk; -+ -+ BT_DBG("conn %p", conn); -+ -+ read_lock(&l->lock); -+ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { -+ if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE) -+ sk->err = err; -+ } -+ read_unlock(&l->lock); -+} -+ -+static void l2cap_chan_ready(struct sock *sk) -+{ -+ struct sock *parent = bluez_pi(sk)->parent; -+ -+ BT_DBG("sk %p, parent %p", sk, parent); -+ -+ l2cap_pi(sk)->conf_state = 0; -+ l2cap_sock_clear_timer(sk); -+ -+ if (!parent) { -+ /* Outgoing channel. -+ * Wake up socket sleeping on connect. -+ */ -+ sk->state = BT_CONNECTED; -+ sk->state_change(sk); -+ } else { -+ /* Incomming channel. -+ * Wake up socket sleeping on accept. -+ */ -+ parent->data_ready(parent, 0); -+ } -+} -+ -+/* Copy frame to all raw sockets on that connection */ -+void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) -+{ -+ struct l2cap_chan_list *l = &conn->chan_list; -+ struct sk_buff *nskb; -+ struct sock * sk; -+ -+ BT_DBG("conn %p", conn); -+ -+ read_lock(&l->lock); -+ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { -+ if (sk->type != SOCK_RAW) -+ continue; -+ -+ /* Don't send frame to the socket it came from */ -+ if (skb->sk == sk) -+ continue; -+ -+ if (!(nskb = skb_clone(skb, GFP_ATOMIC))) -+ continue; -+ -+ if (sock_queue_rcv_skb(sk, nskb)) -+ kfree_skb(nskb); -+ } -+ read_unlock(&l->lock); -+} -+ -+static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len) -+{ -+ struct l2cap_conn *conn = l2cap_pi(sk)->conn; -+ struct sk_buff *skb, **frag; -+ int err, hlen, count, sent=0; -+ l2cap_hdr *lh; -+ -+ BT_DBG("sk %p len %d", sk, len); -+ -+ /* First fragment (with L2CAP header) */ -+ if (sk->type == SOCK_DGRAM) -+ hlen = L2CAP_HDR_SIZE + 2; -+ else -+ hlen = L2CAP_HDR_SIZE; -+ -+ count = MIN(conn->mtu - hlen, len); -+ -+ skb = bluez_skb_send_alloc(sk, hlen + count, -+ msg->msg_flags & MSG_DONTWAIT, &err); -+ if (!skb) -+ return err; -+ -+ /* Create L2CAP header */ -+ lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); -+ lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid); -+ lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); -+ -+ if (sk->type == SOCK_DGRAM) -+ put_unaligned(l2cap_pi(sk)->psm, (__u16 *) skb_put(skb, 2)); -+ -+ if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { -+ err = -EFAULT; -+ goto fail; -+ } -+ -+ sent += count; -+ len -= count; -+ -+ /* Continuation fragments (no L2CAP header) */ -+ frag = &skb_shinfo(skb)->frag_list; -+ while (len) { -+ count = MIN(conn->mtu, len); -+ -+ *frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); -+ if (!*frag) -+ goto fail; -+ -+ if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) { -+ err = -EFAULT; -+ goto fail; -+ } -+ -+ sent += count; -+ len -= count; -+ -+ frag = &(*frag)->next; -+ } -+ -+ if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0) -+ goto fail; -+ -+ return sent; -+ -+fail: -+ kfree_skb(skb); -+ return err; -+} -+ -+/* --------- L2CAP signalling commands --------- */ -+static inline __u8 l2cap_get_ident(struct l2cap_conn *conn) -+{ -+ __u8 id; -+ -+ /* Get next available identificator. -+ * 1 - 199 are used by kernel. -+ * 200 - 254 are used by utilities like l2ping, etc -+ */ -+ -+ spin_lock(&conn->lock); -+ -+ if (++conn->tx_ident > 199) -+ conn->tx_ident = 1; -+ -+ id = conn->tx_ident; -+ -+ spin_unlock(&conn->lock); -+ -+ return id; -+} -+ -+static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, -+ __u8 code, __u8 ident, __u16 dlen, void *data) -+{ -+ struct sk_buff *skb, **frag; -+ l2cap_cmd_hdr *cmd; -+ l2cap_hdr *lh; -+ int len, count; -+ -+ BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", conn, code, ident, dlen); -+ -+ len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen; -+ count = MIN(conn->mtu, len); -+ -+ skb = bluez_skb_alloc(count, GFP_ATOMIC); -+ if (!skb) -+ return NULL; -+ -+ lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); -+ lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen); -+ lh->cid = __cpu_to_le16(0x0001); -+ -+ cmd = (l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE); -+ cmd->code = code; -+ cmd->ident = ident; -+ cmd->len = __cpu_to_le16(dlen); -+ -+ if (dlen) { -+ count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE; -+ memcpy(skb_put(skb, count), data, count); -+ data += count; -+ } -+ -+ len -= skb->len; -+ -+ /* Continuation fragments (no L2CAP header) */ -+ frag = &skb_shinfo(skb)->frag_list; -+ while (len) { -+ count = MIN(conn->mtu, len); -+ -+ *frag = bluez_skb_alloc(count, GFP_ATOMIC); -+ if (!*frag) -+ goto fail; -+ -+ memcpy(skb_put(*frag, count), data, count); -+ -+ len -= count; -+ data += count; -+ -+ frag = &(*frag)->next; -+ } -+ -+ return skb; -+ -+fail: -+ kfree_skb(skb); -+ return NULL; -+} -+ -+static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data) -+{ -+ __u8 ident = l2cap_get_ident(conn); -+ struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); -+ -+ BT_DBG("code 0x%2.2x", code); -+ -+ if (!skb) -+ return -ENOMEM; -+ return hci_send_acl(conn->hcon, skb, 0); -+} -+ -+static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data) -+{ -+ struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); -+ -+ BT_DBG("code 0x%2.2x", code); -+ -+ if (!skb) -+ return -ENOMEM; -+ return hci_send_acl(conn->hcon, skb, 0); -+} -+ -+static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val) -+{ -+ l2cap_conf_opt *opt = *ptr; -+ int len; -+ -+ len = L2CAP_CONF_OPT_SIZE + opt->len; -+ *ptr += len; -+ -+ *type = opt->type; -+ *olen = opt->len; -+ -+ switch (opt->len) { -+ case 1: -+ *val = *((__u8 *) opt->val); -+ break; -+ -+ case 2: -+ *val = __le16_to_cpu(*((__u16 *)opt->val)); -+ break; -+ -+ case 4: -+ *val = __le32_to_cpu(*((__u32 *)opt->val)); -+ break; -+ -+ default: -+ *val = (unsigned long) opt->val; -+ break; -+ }; -+ -+ BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val); -+ return len; -+} -+ -+static inline void l2cap_parse_conf_req(struct sock *sk, void *data, int len) -+{ -+ int type, hint, olen; -+ unsigned long val; -+ void *ptr = data; -+ -+ BT_DBG("sk %p len %d", sk, len); -+ -+ while (len >= L2CAP_CONF_OPT_SIZE) { -+ len -= l2cap_get_conf_opt(&ptr, &type, &olen, &val); -+ -+ hint = type & 0x80; -+ type &= 0x7f; -+ -+ switch (type) { -+ case L2CAP_CONF_MTU: -+ l2cap_pi(sk)->conf_mtu = val; -+ break; -+ -+ case L2CAP_CONF_FLUSH_TO: -+ l2cap_pi(sk)->flush_to = val; -+ break; -+ -+ case L2CAP_CONF_QOS: -+ break; -+ -+ default: -+ if (hint) -+ break; -+ -+ /* FIXME: Reject unknown option */ -+ break; -+ }; -+ } -+} -+ -+static void l2cap_add_conf_opt(void **ptr, __u8 type, __u8 len, unsigned long val) -+{ -+ register l2cap_conf_opt *opt = *ptr; -+ -+ BT_DBG("type 0x%2.2x len %d val 0x%lx", type, len, val); -+ -+ opt->type = type; -+ opt->len = len; -+ -+ switch (len) { -+ case 1: -+ *((__u8 *) opt->val) = val; -+ break; -+ -+ case 2: -+ *((__u16 *) opt->val) = __cpu_to_le16(val); -+ break; -+ -+ case 4: -+ *((__u32 *) opt->val) = __cpu_to_le32(val); -+ break; -+ -+ default: -+ memcpy(opt->val, (void *) val, len); -+ break; -+ }; -+ -+ *ptr += L2CAP_CONF_OPT_SIZE + len; -+} -+ -+static int l2cap_build_conf_req(struct sock *sk, void *data) -+{ -+ struct l2cap_pinfo *pi = l2cap_pi(sk); -+ l2cap_conf_req *req = (l2cap_conf_req *) data; -+ void *ptr = req->data; -+ -+ BT_DBG("sk %p", sk); -+ -+ if (pi->imtu != L2CAP_DEFAULT_MTU) -+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); -+ -+ /* FIXME. Need actual value of the flush timeout */ -+ //if (flush_to != L2CAP_DEFAULT_FLUSH_TO) -+ // l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to); -+ -+ req->dcid = __cpu_to_le16(pi->dcid); -+ req->flags = __cpu_to_le16(0); -+ -+ return ptr - data; -+} -+ -+static inline int l2cap_conf_output(struct sock *sk, void **ptr) -+{ -+ struct l2cap_pinfo *pi = l2cap_pi(sk); -+ int result = 0; -+ -+ /* Configure output options and let the other side know -+ * which ones we don't like. -+ */ -+ if (pi->conf_mtu < pi->omtu) { -+ l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, pi->omtu); -+ result = L2CAP_CONF_UNACCEPT; -+ } else { -+ pi->omtu = pi->conf_mtu; -+ } -+ -+ BT_DBG("sk %p result %d", sk, result); -+ return result; -+} -+ -+static int l2cap_build_conf_rsp(struct sock *sk, void *data, int *result) -+{ -+ l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data; -+ void *ptr = rsp->data; -+ u16 flags = 0; -+ -+ BT_DBG("sk %p complete %d", sk, result ? 1 : 0); -+ -+ if (result) -+ *result = l2cap_conf_output(sk, &ptr); -+ else -+ flags |= 0x0001; -+ -+ rsp->scid = __cpu_to_le16(l2cap_pi(sk)->dcid); -+ rsp->result = __cpu_to_le16(result ? *result : 0); -+ rsp->flags = __cpu_to_le16(flags); -+ -+ return ptr - data; -+} -+ -+static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) -+{ -+ struct l2cap_chan_list *list = &conn->chan_list; -+ l2cap_conn_req *req = (l2cap_conn_req *) data; -+ l2cap_conn_rsp rsp; -+ struct sock *sk, *parent; -+ int result = 0, status = 0; -+ -+ __u16 dcid = 0, scid = __le16_to_cpu(req->scid); -+ __u16 psm = req->psm; -+ -+ BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid); -+ -+ /* Check if we have socket listening on psm */ -+ parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src); -+ if (!parent) { -+ result = L2CAP_CR_BAD_PSM; -+ goto sendresp; -+ } -+ -+ result = L2CAP_CR_NO_MEM; -+ -+ /* Check for backlog size */ -+ if (parent->ack_backlog > parent->max_ack_backlog) { -+ BT_DBG("backlog full %d", parent->ack_backlog); -+ goto response; -+ } -+ -+ sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC); -+ if (!sk) -+ goto response; -+ -+ write_lock(&list->lock); -+ -+ /* Check if we already have channel with that dcid */ -+ if (__l2cap_get_chan_by_dcid(list, scid)) { -+ write_unlock(&list->lock); -+ sk->zapped = 1; -+ l2cap_sock_kill(sk); -+ goto response; -+ } -+ -+ hci_conn_hold(conn->hcon); -+ -+ l2cap_sock_init(sk, parent); -+ bacpy(&bluez_pi(sk)->src, conn->src); -+ bacpy(&bluez_pi(sk)->dst, conn->dst); -+ l2cap_pi(sk)->psm = psm; -+ l2cap_pi(sk)->dcid = scid; -+ -+ __l2cap_chan_add(conn, sk, parent); -+ dcid = l2cap_pi(sk)->scid; -+ -+ l2cap_sock_set_timer(sk, sk->sndtimeo); -+ -+ /* Service level security */ -+ result = L2CAP_CR_PEND; -+ status = L2CAP_CS_AUTHEN_PEND; -+ sk->state = BT_CONNECT2; -+ l2cap_pi(sk)->ident = cmd->ident; -+ -+ if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) { -+ if (!hci_conn_encrypt(conn->hcon)) -+ goto done; -+ } else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) { -+ if (!hci_conn_auth(conn->hcon)) -+ goto done; -+ } -+ -+ sk->state = BT_CONFIG; -+ result = status = 0; -+ -+done: -+ write_unlock(&list->lock); -+ -+response: -+ bh_unlock_sock(parent); -+ -+sendresp: -+ rsp.scid = __cpu_to_le16(scid); -+ rsp.dcid = __cpu_to_le16(dcid); -+ rsp.result = __cpu_to_le16(result); -+ rsp.status = __cpu_to_le16(status); -+ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp); -+ return 0; -+} -+ -+static inline int l2cap_connect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) -+{ -+ l2cap_conn_rsp *rsp = (l2cap_conn_rsp *) data; -+ __u16 scid, dcid, result, status; -+ struct sock *sk; -+ char req[128]; -+ -+ scid = __le16_to_cpu(rsp->scid); -+ dcid = __le16_to_cpu(rsp->dcid); -+ result = __le16_to_cpu(rsp->result); -+ status = __le16_to_cpu(rsp->status); -+ -+ BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status); -+ -+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid))) -+ return -ENOENT; -+ -+ switch (result) { -+ case L2CAP_CR_SUCCESS: -+ sk->state = BT_CONFIG; -+ l2cap_pi(sk)->dcid = dcid; -+ l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; -+ -+ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); -+ break; -+ -+ case L2CAP_CR_PEND: -+ break; -+ -+ default: -+ l2cap_chan_del(sk, ECONNREFUSED); -+ break; -+ } -+ -+ bh_unlock_sock(sk); -+ return 0; -+} -+ -+static inline int l2cap_config_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) -+{ -+ l2cap_conf_req * req = (l2cap_conf_req *) data; -+ __u16 dcid, flags; -+ __u8 rsp[64]; -+ struct sock *sk; -+ int result; -+ -+ dcid = __le16_to_cpu(req->dcid); -+ flags = __le16_to_cpu(req->flags); -+ -+ BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags); -+ -+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid))) -+ return -ENOENT; -+ -+ l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE); -+ -+ if (flags & 0x0001) { -+ /* Incomplete config. Send empty response. */ -+ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp); -+ goto unlock; -+ } -+ -+ /* Complete config. */ -+ l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp); -+ -+ if (result) -+ goto unlock; -+ -+ /* Output config done */ -+ l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE; -+ -+ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { -+ sk->state = BT_CONNECTED; -+ l2cap_chan_ready(sk); -+ } else if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) { -+ char req[64]; -+ l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); -+ } -+ -+unlock: -+ bh_unlock_sock(sk); -+ return 0; -+} -+ -+static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) -+{ -+ l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data; -+ __u16 scid, flags, result; -+ struct sock *sk; -+ int err = 0; -+ -+ scid = __le16_to_cpu(rsp->scid); -+ flags = __le16_to_cpu(rsp->flags); -+ result = __le16_to_cpu(rsp->result); -+ -+ BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result); -+ -+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid))) -+ return -ENOENT; -+ -+ switch (result) { -+ case L2CAP_CONF_SUCCESS: -+ break; -+ -+ case L2CAP_CONF_UNACCEPT: -+ if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) { -+ char req[128]; -+ /* -+ It does not make sense to adjust L2CAP parameters -+ that are currently defined in the spec. We simply -+ resend config request that we sent earlier. It is -+ stupid :) but it helps qualification testing -+ which expects at least some response from us. -+ */ -+ l2cap_send_req(conn, L2CAP_CONF_REQ, -+ l2cap_build_conf_req(sk, req), req); -+ goto done; -+ } -+ default: -+ sk->state = BT_DISCONN; -+ sk->err = ECONNRESET; -+ l2cap_sock_set_timer(sk, HZ * 5); -+ { -+ l2cap_disconn_req req; -+ req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); -+ req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); -+ l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req); -+ } -+ goto done; -+ } -+ -+ if (flags & 0x01) -+ goto done; -+ -+ /* Input config done */ -+ l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE; -+ -+ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { -+ sk->state = BT_CONNECTED; -+ l2cap_chan_ready(sk); -+ } -+ -+done: -+ bh_unlock_sock(sk); -+ return err; -+} -+ -+static inline int l2cap_disconnect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) -+{ -+ l2cap_disconn_req *req = (l2cap_disconn_req *) data; -+ l2cap_disconn_rsp rsp; -+ __u16 dcid, scid; -+ struct sock *sk; -+ -+ scid = __le16_to_cpu(req->scid); -+ dcid = __le16_to_cpu(req->dcid); -+ -+ BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid); -+ -+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid))) -+ return 0; -+ -+ rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid); -+ rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid); -+ l2cap_send_rsp(conn, cmd->ident, L2CAP_DISCONN_RSP, L2CAP_DISCONN_RSP_SIZE, &rsp); -+ -+ sk->shutdown = SHUTDOWN_MASK; -+ -+ l2cap_chan_del(sk, ECONNRESET); -+ bh_unlock_sock(sk); -+ -+ l2cap_sock_kill(sk); -+ return 0; -+} -+ -+static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) -+{ -+ l2cap_disconn_rsp *rsp = (l2cap_disconn_rsp *) data; -+ __u16 dcid, scid; -+ struct sock *sk; -+ -+ scid = __le16_to_cpu(rsp->scid); -+ dcid = __le16_to_cpu(rsp->dcid); -+ -+ BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid); -+ -+ if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid))) -+ return 0; -+ l2cap_chan_del(sk, 0); -+ bh_unlock_sock(sk); -+ -+ l2cap_sock_kill(sk); -+ return 0; -+} -+ -+static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) -+{ -+ __u8 *data = skb->data; -+ int len = skb->len; -+ l2cap_cmd_hdr cmd; -+ int err = 0; -+ -+ while (len >= L2CAP_CMD_HDR_SIZE) { -+ memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE); -+ data += L2CAP_CMD_HDR_SIZE; -+ len -= L2CAP_CMD_HDR_SIZE; -+ -+ cmd.len = __le16_to_cpu(cmd.len); -+ -+ BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd.len, cmd.ident); -+ -+ if (cmd.len > len || !cmd.ident) { -+ BT_DBG("corrupted command"); -+ break; -+ } -+ -+ switch (cmd.code) { -+ case L2CAP_CONN_REQ: -+ err = l2cap_connect_req(conn, &cmd, data); -+ break; -+ -+ case L2CAP_CONN_RSP: -+ err = l2cap_connect_rsp(conn, &cmd, data); -+ break; -+ -+ case L2CAP_CONF_REQ: -+ err = l2cap_config_req(conn, &cmd, data); -+ break; -+ -+ case L2CAP_CONF_RSP: -+ err = l2cap_config_rsp(conn, &cmd, data); -+ break; -+ -+ case L2CAP_DISCONN_REQ: -+ err = l2cap_disconnect_req(conn, &cmd, data); -+ break; -+ -+ case L2CAP_DISCONN_RSP: -+ err = l2cap_disconnect_rsp(conn, &cmd, data); -+ break; -+ -+ case L2CAP_COMMAND_REJ: -+ /* FIXME: We should process this */ -+ l2cap_raw_recv(conn, skb); -+ break; -+ -+ case L2CAP_ECHO_REQ: -+ l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data); -+ break; -+ -+ case L2CAP_ECHO_RSP: -+ case L2CAP_INFO_REQ: -+ case L2CAP_INFO_RSP: -+ l2cap_raw_recv(conn, skb); -+ break; -+ -+ default: -+ BT_ERR("Uknown signaling command 0x%2.2x", cmd.code); -+ err = -EINVAL; -+ break; -+ }; -+ -+ if (err) { -+ l2cap_cmd_rej rej; -+ BT_DBG("error %d", err); -+ -+ /* FIXME: Map err to a valid reason. */ -+ rej.reason = __cpu_to_le16(0); -+ l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej); -+ } -+ -+ data += cmd.len; -+ len -= cmd.len; -+ } -+ -+ kfree_skb(skb); -+} -+ -+static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct sk_buff *skb) -+{ -+ struct sock *sk; -+ -+ sk = l2cap_get_chan_by_scid(&conn->chan_list, cid); -+ if (!sk) { -+ BT_DBG("unknown cid 0x%4.4x", cid); -+ goto drop; -+ } -+ -+ BT_DBG("sk %p, len %d", sk, skb->len); -+ -+ if (sk->state != BT_CONNECTED) -+ goto drop; -+ -+ if (l2cap_pi(sk)->imtu < skb->len) -+ goto drop; -+ -+ /* If socket recv buffers overflows we drop data here -+ * which is *bad* because L2CAP has to be reliable. -+ * But we don't have any other choice. L2CAP doesn't -+ * provide flow control mechanism */ -+ -+ if (!sock_queue_rcv_skb(sk, skb)) -+ goto done; -+ -+drop: -+ kfree_skb(skb); -+ -+done: -+ if (sk) bh_unlock_sock(sk); -+ return 0; -+} -+ -+static inline int l2cap_conless_channel(struct l2cap_conn *conn, __u16 psm, struct sk_buff *skb) -+{ -+ struct sock *sk; -+ -+ sk = l2cap_get_sock_by_psm(0, psm, conn->src); -+ if (!sk) -+ goto drop; -+ -+ BT_DBG("sk %p, len %d", sk, skb->len); -+ -+ if (sk->state != BT_BOUND && sk->state != BT_CONNECTED) -+ goto drop; -+ -+ if (l2cap_pi(sk)->imtu < skb->len) -+ goto drop; -+ -+ if (!sock_queue_rcv_skb(sk, skb)) -+ goto done; -+ -+drop: -+ kfree_skb(skb); -+ -+done: -+ if (sk) bh_unlock_sock(sk); -+ return 0; -+} -+ -+static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) -+{ -+ l2cap_hdr *lh = (l2cap_hdr *) skb->data; -+ __u16 cid, psm, len; -+ -+ skb_pull(skb, L2CAP_HDR_SIZE); -+ cid = __le16_to_cpu(lh->cid); -+ len = __le16_to_cpu(lh->len); -+ -+ BT_DBG("len %d, cid 0x%4.4x", len, cid); -+ -+ switch (cid) { -+ case 0x0001: -+ l2cap_sig_channel(conn, skb); -+ break; -+ -+ case 0x0002: -+ psm = get_unaligned((__u16 *) skb->data); -+ skb_pull(skb, 2); -+ l2cap_conless_channel(conn, psm, skb); -+ break; -+ -+ default: -+ l2cap_data_channel(conn, cid, skb); -+ break; -+ } -+} -+ -+/* ------------ L2CAP interface with lower layer (HCI) ------------- */ -+ -+static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type) -+{ -+ int exact = 0, lm1 = 0, lm2 = 0; -+ register struct sock *sk; -+ -+ if (type != ACL_LINK) -+ return 0; -+ -+ BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); -+ -+ /* Find listening sockets and check their link_mode */ -+ read_lock(&l2cap_sk_list.lock); -+ for (sk = l2cap_sk_list.head; sk; sk = sk->next) { -+ if (sk->state != BT_LISTEN) -+ continue; -+ -+ if (!bacmp(&bluez_pi(sk)->src, &hdev->bdaddr)) { -+ lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); -+ exact++; -+ } else if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY)) -+ lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode); -+ } -+ read_unlock(&l2cap_sk_list.lock); -+ -+ return exact ? lm1 : lm2; -+} -+ -+static int l2cap_connect_cfm(struct hci_conn *hcon, __u8 status) -+{ -+ BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); -+ -+ if (hcon->type != ACL_LINK) -+ return 0; -+ -+ if (!status) { -+ struct l2cap_conn *conn; -+ -+ conn = l2cap_conn_add(hcon, status); -+ if (conn) -+ l2cap_conn_ready(conn); -+ } else -+ l2cap_conn_del(hcon, bterr(status)); -+ -+ return 0; -+} -+ -+static int l2cap_disconn_ind(struct hci_conn *hcon, __u8 reason) -+{ -+ BT_DBG("hcon %p reason %d", hcon, reason); -+ -+ if (hcon->type != ACL_LINK) -+ return 0; -+ -+ l2cap_conn_del(hcon, bterr(reason)); -+ return 0; -+} -+ -+static int l2cap_auth_cfm(struct hci_conn *hcon, __u8 status) -+{ -+ struct l2cap_chan_list *l; -+ struct l2cap_conn *conn; -+ l2cap_conn_rsp rsp; -+ struct sock *sk; -+ int result; -+ -+ if (!(conn = hcon->l2cap_data)) -+ return 0; -+ l = &conn->chan_list; -+ -+ BT_DBG("conn %p", conn); -+ -+ read_lock(&l->lock); -+ -+ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { -+ bh_lock_sock(sk); -+ -+ if (sk->state != BT_CONNECT2 || -+ (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT)) { -+ bh_unlock_sock(sk); -+ continue; -+ } -+ -+ if (!status) { -+ sk->state = BT_CONFIG; -+ result = 0; -+ } else { -+ sk->state = BT_DISCONN; -+ l2cap_sock_set_timer(sk, HZ/10); -+ result = L2CAP_CR_SEC_BLOCK; -+ } -+ -+ rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid); -+ rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid); -+ rsp.result = __cpu_to_le16(result); -+ rsp.status = __cpu_to_le16(0); -+ l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, -+ L2CAP_CONN_RSP_SIZE, &rsp); -+ -+ bh_unlock_sock(sk); -+ } -+ -+ read_unlock(&l->lock); -+ return 0; -+} -+ -+static int l2cap_encrypt_cfm(struct hci_conn *hcon, __u8 status) -+{ -+ struct l2cap_chan_list *l; -+ struct l2cap_conn *conn; -+ l2cap_conn_rsp rsp; -+ struct sock *sk; -+ int result; -+ -+ if (!(conn = hcon->l2cap_data)) -+ return 0; -+ l = &conn->chan_list; -+ -+ BT_DBG("conn %p", conn); -+ -+ read_lock(&l->lock); -+ -+ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { -+ bh_lock_sock(sk); -+ -+ if (sk->state != BT_CONNECT2) { -+ bh_unlock_sock(sk); -+ continue; -+ } -+ -+ if (!status) { -+ sk->state = BT_CONFIG; -+ result = 0; -+ } else { -+ sk->state = BT_DISCONN; -+ l2cap_sock_set_timer(sk, HZ/10); -+ result = L2CAP_CR_SEC_BLOCK; -+ } -+ -+ rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid); -+ rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid); -+ rsp.result = __cpu_to_le16(result); -+ rsp.status = __cpu_to_le16(0); -+ l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, -+ L2CAP_CONN_RSP_SIZE, &rsp); -+ -+ bh_unlock_sock(sk); -+ } -+ -+ read_unlock(&l->lock); -+ return 0; -+} -+ -+static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16 flags) -+{ -+ struct l2cap_conn *conn = hcon->l2cap_data; -+ -+ if (!conn && !(conn = l2cap_conn_add(hcon, 0))) -+ goto drop; -+ -+ BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); -+ -+ if (flags & ACL_START) { -+ l2cap_hdr *hdr; -+ int len; -+ -+ if (conn->rx_len) { -+ BT_ERR("Unexpected start frame (len %d)", skb->len); -+ kfree_skb(conn->rx_skb); -+ conn->rx_skb = NULL; -+ conn->rx_len = 0; -+ l2cap_conn_unreliable(conn, ECOMM); -+ } -+ -+ if (skb->len < 2) { -+ BT_ERR("Frame is too short (len %d)", skb->len); -+ l2cap_conn_unreliable(conn, ECOMM); -+ goto drop; -+ } -+ -+ hdr = (l2cap_hdr *) skb->data; -+ len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE; -+ -+ if (len == skb->len) { -+ /* Complete frame received */ -+ l2cap_recv_frame(conn, skb); -+ return 0; -+ } -+ -+ BT_DBG("Start: total len %d, frag len %d", len, skb->len); -+ -+ if (skb->len > len) { -+ BT_ERR("Frame is too long (len %d, expected len %d)", -+ skb->len, len); -+ l2cap_conn_unreliable(conn, ECOMM); -+ goto drop; -+ } -+ -+ /* Allocate skb for the complete frame including header */ -+ conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC); -+ if (!conn->rx_skb) -+ goto drop; -+ -+ memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len); -+ conn->rx_len = len - skb->len; -+ } else { -+ BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); -+ -+ if (!conn->rx_len) { -+ BT_ERR("Unexpected continuation frame (len %d)", skb->len); -+ l2cap_conn_unreliable(conn, ECOMM); -+ goto drop; -+ } -+ -+ if (skb->len > conn->rx_len) { -+ BT_ERR("Fragment is too long (len %d, expected %d)", -+ skb->len, conn->rx_len); -+ kfree_skb(conn->rx_skb); -+ conn->rx_skb = NULL; -+ conn->rx_len = 0; -+ l2cap_conn_unreliable(conn, ECOMM); -+ goto drop; -+ } -+ -+ memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len); -+ conn->rx_len -= skb->len; -+ -+ if (!conn->rx_len) { -+ /* Complete frame received */ -+ l2cap_recv_frame(conn, conn->rx_skb); -+ conn->rx_skb = NULL; -+ } -+ } -+ -+drop: -+ kfree_skb(skb); -+ return 0; -+} -+ -+/* ----- Proc fs support ------ */ -+static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list) -+{ -+ struct l2cap_pinfo *pi; -+ struct sock *sk; -+ char *ptr = buf; -+ -+ read_lock_bh(&list->lock); -+ -+ for (sk = list->head; sk; sk = sk->next) { -+ pi = l2cap_pi(sk); -+ ptr += sprintf(ptr, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n", -+ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst), -+ sk->state, pi->psm, pi->scid, pi->dcid, pi->imtu, pi->omtu, -+ pi->link_mode); -+ } -+ -+ read_unlock_bh(&list->lock); -+ -+ ptr += sprintf(ptr, "\n"); -+ return ptr - buf; -+} -+ -+static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv) -+{ -+ char *ptr = buf; -+ int len; -+ -+ BT_DBG("count %d, offset %ld", count, offset); -+ -+ ptr += l2cap_sock_dump(ptr, &l2cap_sk_list); -+ len = ptr - buf; -+ -+ if (len <= count + offset) -+ *eof = 1; -+ -+ *start = buf + offset; -+ len -= offset; -+ -+ if (len > count) -+ len = count; -+ if (len < 0) -+ len = 0; -+ -+ return len; -+} -+ -+static struct proto_ops l2cap_sock_ops = { -+ family: PF_BLUETOOTH, -+ release: l2cap_sock_release, -+ bind: l2cap_sock_bind, -+ connect: l2cap_sock_connect, -+ listen: l2cap_sock_listen, -+ accept: l2cap_sock_accept, -+ getname: l2cap_sock_getname, -+ sendmsg: l2cap_sock_sendmsg, -+ recvmsg: bluez_sock_recvmsg, -+ poll: bluez_sock_poll, -+ socketpair: sock_no_socketpair, -+ ioctl: sock_no_ioctl, -+ shutdown: l2cap_sock_shutdown, -+ setsockopt: l2cap_sock_setsockopt, -+ getsockopt: l2cap_sock_getsockopt, -+ mmap: sock_no_mmap -+}; -+ -+static struct net_proto_family l2cap_sock_family_ops = { -+ family: PF_BLUETOOTH, -+ create: l2cap_sock_create -+}; -+ -+static struct hci_proto l2cap_hci_proto = { -+ name: "L2CAP", -+ id: HCI_PROTO_L2CAP, -+ connect_ind: l2cap_connect_ind, -+ connect_cfm: l2cap_connect_cfm, -+ disconn_ind: l2cap_disconn_ind, -+ recv_acldata: l2cap_recv_acldata, -+ auth_cfm: l2cap_auth_cfm, -+ encrypt_cfm: l2cap_encrypt_cfm -+}; -+ -+int __init l2cap_init(void) -+{ -+ int err; -+ -+ if ((err = bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops))) { -+ BT_ERR("Can't register L2CAP socket"); -+ return err; -+ } -+ -+ if ((err = hci_register_proto(&l2cap_hci_proto))) { -+ BT_ERR("Can't register L2CAP protocol"); -+ return err; -+ } -+ -+ create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL); -+ -+ BT_INFO("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION); -+ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); -+ return 0; -+} -+ -+void l2cap_cleanup(void) -+{ -+ remove_proc_entry("bluetooth/l2cap", NULL); -+ -+ /* Unregister socket and protocol */ -+ if (bluez_sock_unregister(BTPROTO_L2CAP)) -+ BT_ERR("Can't unregister L2CAP socket"); -+ -+ if (hci_unregister_proto(&l2cap_hci_proto)) -+ BT_ERR("Can't unregister L2CAP protocol"); -+} -+ -+void l2cap_load(void) -+{ -+ /* Dummy function to trigger automatic L2CAP module loading by -+ other modules that use L2CAP sockets but do not use any other -+ symbols from it. */ -+ return; -+} -+ -+EXPORT_SYMBOL(l2cap_load); -+ -+module_init(l2cap_init); -+module_exit(l2cap_cleanup); -+ -+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); -+MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION); -+MODULE_LICENSE("GPL"); ---- linux/net/bluetooth/l2cap_core.c~bluetooth-2.4.18-mh11 -+++ linux/net/bluetooth/l2cap_core.c --/* -- BlueZ - Bluetooth protocol stack for Linux -- Copyright (C) 2000-2001 Qualcomm Incorporated -- -- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License version 2 as -- published by the Free Software Foundation; -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -- -- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -- SOFTWARE IS DISCLAIMED. --*/ -- --/* -- * BlueZ L2CAP core and sockets. -- * -- * $Id: l2cap_core.c,v 1.19 2001/08/03 04:19:50 maxk Exp $ -- */ --#define VERSION "1.1" -- --#include <linux/config.h> --#include <linux/module.h> -- --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/kernel.h> --#include <linux/major.h> --#include <linux/sched.h> --#include <linux/slab.h> --#include <linux/poll.h> --#include <linux/fcntl.h> --#include <linux/init.h> --#include <linux/skbuff.h> --#include <linux/interrupt.h> --#include <linux/socket.h> --#include <linux/skbuff.h> --#include <linux/proc_fs.h> --#include <linux/list.h> --#include <net/sock.h> -- --#include <asm/system.h> --#include <asm/uaccess.h> -- --#include <net/bluetooth/bluetooth.h> --#include <net/bluetooth/bluez.h> --#include <net/bluetooth/hci_core.h> --#include <net/bluetooth/l2cap.h> --#include <net/bluetooth/l2cap_core.h> -- --#ifndef L2CAP_DEBUG --#undef DBG --#define DBG( A... ) --#endif -- --struct proto_ops l2cap_sock_ops; -- --struct bluez_sock_list l2cap_sk_list = { -- lock: RW_LOCK_UNLOCKED --}; -- --struct list_head l2cap_iff_list = LIST_HEAD_INIT(l2cap_iff_list); --rwlock_t l2cap_rt_lock = RW_LOCK_UNLOCKED; -- --static int l2cap_conn_del(struct l2cap_conn *conn, int err); -- --static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent); --static void l2cap_chan_del(struct sock *sk, int err); --static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len); -- --static void l2cap_sock_close(struct sock *sk); --static void l2cap_sock_kill(struct sock *sk); -- --static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data); --static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data); -- --/* -------- L2CAP interfaces & routing --------- */ --/* Add/delete L2CAP interface. -- * Must be called with locked rt_lock -- */ -- --static void l2cap_iff_add(struct hci_dev *hdev) --{ -- struct l2cap_iff *iff; -- -- DBG("%s", hdev->name); -- -- DBG("iff_list %p next %p prev %p", &l2cap_iff_list, l2cap_iff_list.next, l2cap_iff_list.prev); -- -- /* Allocate new interface and lock HCI device */ -- if (!(iff = kmalloc(sizeof(struct l2cap_iff), GFP_KERNEL))) { -- ERR("Can't allocate new interface %s", hdev->name); -- return; -- } -- memset(iff, 0, sizeof(struct l2cap_iff)); -- -- hci_dev_hold(hdev); -- hdev->l2cap_data = iff; -- iff->hdev = hdev; -- iff->mtu = hdev->acl_mtu - HCI_ACL_HDR_SIZE; -- iff->bdaddr = &hdev->bdaddr; -- -- spin_lock_init(&iff->lock); -- INIT_LIST_HEAD(&iff->conn_list); -- -- list_add(&iff->list, &l2cap_iff_list); --} -- --static void l2cap_iff_del(struct hci_dev *hdev) --{ -- struct l2cap_iff *iff; -- -- if (!(iff = hdev->l2cap_data)) -- return; -- -- DBG("%s iff %p", hdev->name, iff); -- -- list_del(&iff->list); -- -- l2cap_iff_lock(iff); -- -- /* Drop connections */ -- while (!list_empty(&iff->conn_list)) { -- struct l2cap_conn *c; -- -- c = list_entry(iff->conn_list.next, struct l2cap_conn, list); -- l2cap_conn_del(c, ENODEV); -- } -- -- l2cap_iff_unlock(iff); -- -- /* Unlock HCI device */ -- hdev->l2cap_data = NULL; -- hci_dev_put(hdev); -- -- kfree(iff); --} -- --/* Get route. Returns L2CAP interface. -- * Must be called with locked rt_lock -- */ --static struct l2cap_iff *l2cap_get_route(bdaddr_t *src, bdaddr_t *dst) --{ -- struct list_head *p; -- int use_src; -- -- DBG("%s -> %s", batostr(src), batostr(dst)); -- -- use_src = bacmp(src, BDADDR_ANY) ? 0 : 1; -- -- /* Simple routing: -- * No source address - find interface with bdaddr != dst -- * Source address - find interface with bdaddr == src -- */ -- -- list_for_each(p, &l2cap_iff_list) { -- struct l2cap_iff *iff; -- -- iff = list_entry(p, struct l2cap_iff, list); -- -- if (use_src && !bacmp(iff->bdaddr, src)) -- return iff; -- else if (bacmp(iff->bdaddr, dst)) -- return iff; -- } -- return NULL; --} -- --/* ----- L2CAP timers ------ */ --static void l2cap_sock_timeout(unsigned long arg) --{ -- struct sock *sk = (struct sock *) arg; -- -- DBG("sock %p state %d", sk, sk->state); -- -- bh_lock_sock(sk); -- switch (sk->state) { -- case BT_DISCONN: -- l2cap_chan_del(sk, ETIMEDOUT); -- break; -- -- default: -- sk->err = ETIMEDOUT; -- sk->state_change(sk); -- break; -- }; -- bh_unlock_sock(sk); -- -- l2cap_sock_kill(sk); -- sock_put(sk); --} -- --static void l2cap_sock_set_timer(struct sock *sk, long timeout) --{ -- DBG("sock %p state %d timeout %ld", sk, sk->state, timeout); -- -- if (!mod_timer(&sk->timer, jiffies + timeout)) -- sock_hold(sk); --} -- --static void l2cap_sock_clear_timer(struct sock *sk) --{ -- DBG("sock %p state %d", sk, sk->state); -- -- if (timer_pending(&sk->timer) && del_timer(&sk->timer)) -- __sock_put(sk); --} -- --static void l2cap_sock_init_timer(struct sock *sk) --{ -- init_timer(&sk->timer); -- sk->timer.function = l2cap_sock_timeout; -- sk->timer.data = (unsigned long)sk; --} -- --static void l2cap_conn_timeout(unsigned long arg) --{ -- struct l2cap_conn *conn = (void *)arg; -- -- DBG("conn %p state %d", conn, conn->state); -- -- if (conn->state == BT_CONNECTED) { -- hci_disconnect(conn->hconn, 0x13); -- } -- -- return; --} -- --static void l2cap_conn_set_timer(struct l2cap_conn *conn, long timeout) --{ -- DBG("conn %p state %d timeout %ld", conn, conn->state, timeout); -- -- mod_timer(&conn->timer, jiffies + timeout); --} -- --static void l2cap_conn_clear_timer(struct l2cap_conn *conn) --{ -- DBG("conn %p state %d", conn, conn->state); -- -- del_timer(&conn->timer); --} -- --static void l2cap_conn_init_timer(struct l2cap_conn *conn) --{ -- init_timer(&conn->timer); -- conn->timer.function = l2cap_conn_timeout; -- conn->timer.data = (unsigned long)conn; --} -- --/* -------- L2CAP connections --------- */ --/* Add new connection to the interface. -- * Interface must be locked -- */ --static struct l2cap_conn *l2cap_conn_add(struct l2cap_iff *iff, bdaddr_t *dst) --{ -- struct l2cap_conn *conn; -- bdaddr_t *src = iff->bdaddr; -- -- if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_KERNEL))) -- return NULL; -- -- memset(conn, 0, sizeof(struct l2cap_conn)); -- -- conn->state = BT_OPEN; -- conn->iff = iff; -- bacpy(&conn->src, src); -- bacpy(&conn->dst, dst); -- -- spin_lock_init(&conn->lock); -- conn->chan_list.lock = RW_LOCK_UNLOCKED; -- -- l2cap_conn_init_timer(conn); -- -- __l2cap_conn_link(iff, conn); -- -- DBG("%s -> %s, %p", batostr(src), batostr(dst), conn); -- -- MOD_INC_USE_COUNT; -- -- return conn; --} -- --/* Delete connection on the interface. -- * Interface must be locked -- */ --static int l2cap_conn_del(struct l2cap_conn *conn, int err) --{ -- struct sock *sk; -- -- DBG("conn %p, state %d, err %d", conn, conn->state, err); -- -- l2cap_conn_clear_timer(conn); -- __l2cap_conn_unlink(conn->iff, conn); -- -- conn->state = BT_CLOSED; -- -- if (conn->rx_skb) -- kfree_skb(conn->rx_skb); -- -- /* Kill channels */ -- while ((sk = conn->chan_list.head)) { -- bh_lock_sock(sk); -- l2cap_sock_clear_timer(sk); -- l2cap_chan_del(sk, err); -- bh_unlock_sock(sk); -- -- l2cap_sock_kill(sk); -- } -- -- kfree(conn); -- -- MOD_DEC_USE_COUNT; -- return 0; --} -- --static inline struct l2cap_conn *l2cap_get_conn_by_addr(struct l2cap_iff *iff, bdaddr_t *dst) --{ -- struct list_head *p; -- -- list_for_each(p, &iff->conn_list) { -- struct l2cap_conn *c; -- -- c = list_entry(p, struct l2cap_conn, list); -- if (!bacmp(&c->dst, dst)) -- return c; -- } -- return NULL; --} -- --int l2cap_connect(struct sock *sk) --{ -- bdaddr_t *src = &l2cap_pi(sk)->src; -- bdaddr_t *dst = &l2cap_pi(sk)->dst; -- struct l2cap_conn *conn; -- struct l2cap_iff *iff; -- int err = 0; -- -- DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm); -- -- read_lock_bh(&l2cap_rt_lock); -- -- /* Get route to remote BD address */ -- if (!(iff = l2cap_get_route(src, dst))) { -- err = -EHOSTUNREACH; -- goto done; -- } -- -- /* Update source addr of the socket */ -- bacpy(src, iff->bdaddr); -- -- l2cap_iff_lock(iff); -- -- if (!(conn = l2cap_get_conn_by_addr(iff, dst))) { -- /* Connection doesn't exist */ -- if (!(conn = l2cap_conn_add(iff, dst))) { -- l2cap_iff_unlock(iff); -- err = -ENOMEM; -- goto done; -- } -- conn->out = 1; -- } -- -- l2cap_iff_unlock(iff); -- -- l2cap_chan_add(conn, sk, NULL); -- -- sk->state = BT_CONNECT; -- l2cap_sock_set_timer(sk, sk->sndtimeo); -- -- switch (conn->state) { -- case BT_CONNECTED: -- if (sk->type == SOCK_SEQPACKET) { -- l2cap_conn_req req; -- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); -- req.psm = l2cap_pi(sk)->psm; -- l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req); -- } else { -- l2cap_sock_clear_timer(sk); -- sk->state = BT_CONNECTED; -- } -- break; -- -- case BT_CONNECT: -- break; -- -- default: -- /* Create ACL connection */ -- conn->state = BT_CONNECT; -- hci_connect(iff->hdev, dst); -- break; -- }; -- --done: -- read_unlock_bh(&l2cap_rt_lock); -- return err; --} -- --/* ------ Channel queues for listening sockets ------ */ --void l2cap_accept_queue(struct sock *parent, struct sock *sk) --{ -- struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q; -- -- DBG("parent %p, sk %p", parent, sk); -- -- sock_hold(sk); -- l2cap_pi(sk)->parent = parent; -- l2cap_pi(sk)->next_q = NULL; -- -- if (!q->head) { -- q->head = q->tail = sk; -- } else { -- struct sock *tail = q->tail; -- -- l2cap_pi(sk)->prev_q = tail; -- l2cap_pi(tail)->next_q = sk; -- q->tail = sk; -- } -- -- parent->ack_backlog++; --} -- --void l2cap_accept_unlink(struct sock *sk) --{ -- struct sock *parent = l2cap_pi(sk)->parent; -- struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q; -- struct sock *next, *prev; -- -- DBG("sk %p", sk); -- -- next = l2cap_pi(sk)->next_q; -- prev = l2cap_pi(sk)->prev_q; -- -- if (sk == q->head) -- q->head = next; -- if (sk == q->tail) -- q->tail = prev; -- -- if (next) -- l2cap_pi(next)->prev_q = prev; -- if (prev) -- l2cap_pi(prev)->next_q = next; -- -- l2cap_pi(sk)->parent = NULL; -- -- parent->ack_backlog--; -- __sock_put(sk); --} -- --/* Get next connected channel in queue. */ --struct sock *l2cap_accept_dequeue(struct sock *parent, int state) --{ -- struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q; -- struct sock *sk; -- -- for (sk = q->head; sk; sk = l2cap_pi(sk)->next_q) { -- if (!state || sk->state == state) { -- l2cap_accept_unlink(sk); -- break; -- } -- } -- -- DBG("parent %p, sk %p", parent, sk); -- -- return sk; --} -- --/* -------- Socket interface ---------- */ --static struct sock *__l2cap_get_sock_by_addr(struct sockaddr_l2 *addr) --{ -- bdaddr_t *src = &addr->l2_bdaddr; -- __u16 psm = addr->l2_psm; -- struct sock *sk; -- -- for (sk = l2cap_sk_list.head; sk; sk = sk->next) { -- if (l2cap_pi(sk)->psm == psm && -- !bacmp(&l2cap_pi(sk)->src, src)) -- break; -- } -- -- return sk; --} -- --/* Find socket listening on psm and source bdaddr. -- * Returns closest match. -- */ --static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm) --{ -- struct sock *sk, *sk1 = NULL; -- -- read_lock(&l2cap_sk_list.lock); -- -- for (sk = l2cap_sk_list.head; sk; sk = sk->next) { -- struct l2cap_pinfo *pi; -- -- if (sk->state != BT_LISTEN) -- continue; -- -- pi = l2cap_pi(sk); -- -- if (pi->psm == psm) { -- /* Exact match. */ -- if (!bacmp(&pi->src, src)) -- break; -- -- /* Closest match */ -- if (!bacmp(&pi->src, BDADDR_ANY)) -- sk1 = sk; -- } -- } -- -- read_unlock(&l2cap_sk_list.lock); -- -- return sk ? sk : sk1; --} -- --static void l2cap_sock_destruct(struct sock *sk) --{ -- DBG("sk %p", sk); -- -- skb_queue_purge(&sk->receive_queue); -- skb_queue_purge(&sk->write_queue); -- -- MOD_DEC_USE_COUNT; --} -- --static void l2cap_sock_cleanup_listen(struct sock *parent) --{ -- struct sock *sk; -- -- DBG("parent %p", parent); -- -- /* Close not yet accepted channels */ -- while ((sk = l2cap_accept_dequeue(parent, 0))) -- l2cap_sock_close(sk); -- -- parent->state = BT_CLOSED; -- parent->zapped = 1; --} -- --/* Kill socket (only if zapped and orphan) -- * Must be called on unlocked socket. -- */ --static void l2cap_sock_kill(struct sock *sk) --{ -- if (!sk->zapped || sk->socket) -- return; -- -- DBG("sk %p state %d", sk, sk->state); -- -- /* Kill poor orphan */ -- bluez_sock_unlink(&l2cap_sk_list, sk); -- sk->dead = 1; -- sock_put(sk); --} -- --/* Close socket. -- * Must be called on unlocked socket. -- */ --static void l2cap_sock_close(struct sock *sk) --{ -- struct l2cap_conn *conn; -- -- l2cap_sock_clear_timer(sk); -- -- lock_sock(sk); -- -- conn = l2cap_pi(sk)->conn; -- -- DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket); -- -- switch (sk->state) { -- case BT_LISTEN: -- l2cap_sock_cleanup_listen(sk); -- break; -- -- case BT_CONNECTED: -- case BT_CONFIG: -- if (sk->type == SOCK_SEQPACKET) { -- l2cap_disconn_req req; -- -- sk->state = BT_DISCONN; -- -- req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); -- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); -- l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req); -- -- l2cap_sock_set_timer(sk, sk->sndtimeo); -- } else { -- l2cap_chan_del(sk, ECONNRESET); -- } -- break; -- -- case BT_CONNECT: -- case BT_DISCONN: -- l2cap_chan_del(sk, ECONNRESET); -- break; -- -- default: -- sk->zapped = 1; -- break; -- }; -- -- release_sock(sk); -- -- l2cap_sock_kill(sk); --} -- --static void l2cap_sock_init(struct sock *sk, struct sock *parent) --{ -- struct l2cap_pinfo *pi = l2cap_pi(sk); -- -- DBG("sk %p", sk); -- -- if (parent) { -- sk->type = parent->type; -- -- pi->imtu = l2cap_pi(parent)->imtu; -- pi->omtu = l2cap_pi(parent)->omtu; -- } else { -- pi->imtu = L2CAP_DEFAULT_MTU; -- pi->omtu = 0; -- } -- -- /* Default config options */ -- pi->conf_mtu = L2CAP_DEFAULT_MTU; -- pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; --} -- --static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio) --{ -- struct sock *sk; -- -- if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1))) -- return NULL; -- -- sock_init_data(sock, sk); -- -- sk->zapped = 0; -- -- sk->destruct = l2cap_sock_destruct; -- sk->sndtimeo = L2CAP_CONN_TIMEOUT; -- -- sk->protocol = proto; -- sk->state = BT_OPEN; -- -- l2cap_sock_init_timer(sk); -- -- bluez_sock_link(&l2cap_sk_list, sk); -- -- MOD_INC_USE_COUNT; -- -- return sk; --} -- --static int l2cap_sock_create(struct socket *sock, int protocol) --{ -- struct sock *sk; -- -- DBG("sock %p", sock); -- -- sock->state = SS_UNCONNECTED; -- -- if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_RAW) -- return -ESOCKTNOSUPPORT; -- -- sock->ops = &l2cap_sock_ops; -- -- if (!(sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL))) -- return -ENOMEM; -- -- l2cap_sock_init(sk, NULL); -- -- return 0; --} -- --static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) --{ -- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; -- struct sock *sk = sock->sk; -- int err = 0; -- -- DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm); -- -- if (!addr || addr->sa_family != AF_BLUETOOTH) -- return -EINVAL; -- -- lock_sock(sk); -- -- if (sk->state != BT_OPEN) { -- err = -EBADFD; -- goto done; -- } -- -- write_lock(&l2cap_sk_list.lock); -- -- if (la->l2_psm && __l2cap_get_sock_by_addr(la)) { -- err = -EADDRINUSE; -- goto unlock; -- } -- -- /* Save source address */ -- bacpy(&l2cap_pi(sk)->src, &la->l2_bdaddr); -- l2cap_pi(sk)->psm = la->l2_psm; -- sk->state = BT_BOUND; -- --unlock: -- write_unlock(&l2cap_sk_list.lock); -- --done: -- release_sock(sk); -- -- return err; --} -- --static int l2cap_sock_w4_connect(struct sock *sk, int flags) --{ -- DECLARE_WAITQUEUE(wait, current); -- long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); -- int err = 0; -- -- DBG("sk %p", sk); -- -- add_wait_queue(sk->sleep, &wait); -- current->state = TASK_INTERRUPTIBLE; -- -- while (sk->state != BT_CONNECTED) { -- if (!timeo) { -- err = -EAGAIN; -- break; -- } -- -- release_sock(sk); -- timeo = schedule_timeout(timeo); -- lock_sock(sk); -- -- err = 0; -- if (sk->state == BT_CONNECTED) -- break; -- -- if (sk->err) { -- err = sock_error(sk); -- break; -- } -- -- if (signal_pending(current)) { -- err = sock_intr_errno(timeo); -- break; -- } -- } -- current->state = TASK_RUNNING; -- remove_wait_queue(sk->sleep, &wait); -- -- return err; --} -- --static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) --{ -- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; -- struct sock *sk = sock->sk; -- int err = 0; -- -- lock_sock(sk); -- -- DBG("sk %p", sk); -- -- if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) { -- err = -EINVAL; -- goto done; -- } -- -- if (sk->state != BT_OPEN && sk->state != BT_BOUND) { -- err = -EBADFD; -- goto done; -- } -- -- if (sk->type == SOCK_SEQPACKET && !la->l2_psm) { -- err = -EINVAL; -- goto done; -- } -- -- /* Set destination address and psm */ -- bacpy(&l2cap_pi(sk)->dst, &la->l2_bdaddr); -- l2cap_pi(sk)->psm = la->l2_psm; -- -- if ((err = l2cap_connect(sk))) -- goto done; -- -- err = l2cap_sock_w4_connect(sk, flags); -- --done: -- release_sock(sk); -- return err; --} -- --int l2cap_sock_listen(struct socket *sock, int backlog) --{ -- struct sock *sk = sock->sk; -- int err = 0; -- -- DBG("sk %p backlog %d", sk, backlog); -- -- lock_sock(sk); -- -- if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) { -- err = -EBADFD; -- goto done; -- } -- -- if (!l2cap_pi(sk)->psm) { -- err = -EINVAL; -- goto done; -- } -- -- sk->max_ack_backlog = backlog; -- sk->ack_backlog = 0; -- sk->state = BT_LISTEN; -- --done: -- release_sock(sk); -- return err; --} -- --int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) --{ -- DECLARE_WAITQUEUE(wait, current); -- struct sock *sk = sock->sk, *ch; -- long timeo; -- int err = 0; -- -- lock_sock(sk); -- -- if (sk->state != BT_LISTEN) { -- err = -EBADFD; -- goto done; -- } -- -- timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); -- -- DBG("sk %p timeo %ld", sk, timeo); -- -- /* Wait for an incoming connection. (wake-one). */ -- add_wait_queue_exclusive(sk->sleep, &wait); -- current->state = TASK_INTERRUPTIBLE; -- while (!(ch = l2cap_accept_dequeue(sk, BT_CONNECTED))) { -- if (!timeo) { -- err = -EAGAIN; -- break; -- } -- -- release_sock(sk); -- timeo = schedule_timeout(timeo); -- lock_sock(sk); -- -- if (sk->state != BT_LISTEN) { -- err = -EBADFD; -- break; -- } -- -- if (signal_pending(current)) { -- err = sock_intr_errno(timeo); -- break; -- } -- } -- current->state = TASK_RUNNING; -- remove_wait_queue(sk->sleep, &wait); -- -- if (err) -- goto done; -- -- sock_graft(ch, newsock); -- newsock->state = SS_CONNECTED; -- -- DBG("new socket %p", ch); -- --done: -- release_sock(sk); -- -- return err; --} -- --static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) --{ -- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; -- struct sock *sk = sock->sk; -- -- DBG("sock %p, sk %p", sock, sk); -- -- addr->sa_family = AF_BLUETOOTH; -- *len = sizeof(struct sockaddr_l2); -- -- if (peer) -- bacpy(&la->l2_bdaddr, &l2cap_pi(sk)->dst); -- else -- bacpy(&la->l2_bdaddr, &l2cap_pi(sk)->src); -- -- la->l2_psm = l2cap_pi(sk)->psm; -- -- return 0; --} -- --static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) --{ -- struct sock *sk = sock->sk; -- int err = 0; -- -- DBG("sock %p, sk %p", sock, sk); -- -- if (sk->err) -- return sock_error(sk); -- -- if (msg->msg_flags & MSG_OOB) -- return -EOPNOTSUPP; -- -- lock_sock(sk); -- -- if (sk->state == BT_CONNECTED) -- err = l2cap_chan_send(sk, msg, len); -- else -- err = -ENOTCONN; -- -- release_sock(sk); -- return err; --} -- --static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm) --{ -- struct sock *sk = sock->sk; -- int noblock = flags & MSG_DONTWAIT; -- int copied, err; -- struct sk_buff *skb; -- -- DBG("sock %p, sk %p", sock, sk); -- -- if (flags & (MSG_OOB)) -- return -EOPNOTSUPP; -- -- if (sk->state == BT_CLOSED) -- return 0; -- -- if (!(skb = skb_recv_datagram(sk, flags, noblock, &err))) -- return err; -- -- msg->msg_namelen = 0; -- -- copied = skb->len; -- if (len < copied) { -- msg->msg_flags |= MSG_TRUNC; -- copied = len; -- } -- -- skb->h.raw = skb->data; -- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); -- -- skb_free_datagram(sk, skb); -- -- return err ? : copied; --} -- --int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) --{ -- struct sock *sk = sock->sk; -- struct l2cap_options opts; -- int err = 0; -- -- DBG("sk %p", sk); -- -- lock_sock(sk); -- -- switch (optname) { -- case L2CAP_OPTIONS: -- if (copy_from_user((char *)&opts, optval, optlen)) { -- err = -EFAULT; -- break; -- } -- l2cap_pi(sk)->imtu = opts.imtu; -- l2cap_pi(sk)->omtu = opts.omtu; -- break; -- -- default: -- err = -ENOPROTOOPT; -- break; -- }; -- -- release_sock(sk); -- return err; --} -- --int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) --{ -- struct sock *sk = sock->sk; -- struct l2cap_options opts; -- struct l2cap_conninfo cinfo; -- int len, err = 0; -- -- if (get_user(len, optlen)) -- return -EFAULT; -- -- lock_sock(sk); -- -- switch (optname) { -- case L2CAP_OPTIONS: -- opts.imtu = l2cap_pi(sk)->imtu; -- opts.omtu = l2cap_pi(sk)->omtu; -- opts.flush_to = l2cap_pi(sk)->flush_to; -- -- len = MIN(len, sizeof(opts)); -- if (copy_to_user(optval, (char *)&opts, len)) -- err = -EFAULT; -- -- break; -- -- case L2CAP_CONNINFO: -- if (sk->state != BT_CONNECTED) { -- err = -ENOTCONN; -- break; -- } -- -- cinfo.hci_handle = l2cap_pi(sk)->conn->hconn->handle; -- -- len = MIN(len, sizeof(cinfo)); -- if (copy_to_user(optval, (char *)&cinfo, len)) -- err = -EFAULT; -- -- break; -- -- default: -- err = -ENOPROTOOPT; -- break; -- }; -- -- release_sock(sk); -- return err; --} -- --static unsigned int l2cap_sock_poll(struct file * file, struct socket *sock, poll_table *wait) --{ -- struct sock *sk = sock->sk; -- struct l2cap_accept_q *aq; -- unsigned int mask; -- -- DBG("sock %p, sk %p", sock, sk); -- -- poll_wait(file, sk->sleep, wait); -- mask = 0; -- -- if (sk->err || !skb_queue_empty(&sk->error_queue)) -- mask |= POLLERR; -- -- if (sk->shutdown == SHUTDOWN_MASK) -- mask |= POLLHUP; -- -- aq = &l2cap_pi(sk)->accept_q; -- if (!skb_queue_empty(&sk->receive_queue) || aq->head || (sk->shutdown & RCV_SHUTDOWN)) -- mask |= POLLIN | POLLRDNORM; -- -- if (sk->state == BT_CLOSED) -- mask |= POLLHUP; -- -- if (sock_writeable(sk)) -- mask |= POLLOUT | POLLWRNORM | POLLWRBAND; -- else -- set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags); -- -- return mask; --} -- --static int l2cap_sock_release(struct socket *sock) --{ -- struct sock *sk = sock->sk; -- -- DBG("sock %p, sk %p", sock, sk); -- -- if (!sk) -- return 0; -- -- sock_orphan(sk); -- -- l2cap_sock_close(sk); -- -- return 0; --} -- --/* --------- L2CAP channels --------- */ --static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid) --{ -- struct sock *s; -- -- for (s = l->head; s; s = l2cap_pi(s)->next_c) { -- if (l2cap_pi(s)->dcid == cid) -- break; -- } -- -- return s; --} -- --static inline struct sock *l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid) --{ -- struct sock *s; -- -- read_lock(&l->lock); -- s = __l2cap_get_chan_by_dcid(l, cid); -- read_unlock(&l->lock); -- -- return s; --} -- --static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid) --{ -- struct sock *s; -- -- for (s = l->head; s; s = l2cap_pi(s)->next_c) { -- if (l2cap_pi(s)->scid == cid) -- break; -- } -- -- return s; --} --static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid) --{ -- struct sock *s; -- -- read_lock(&l->lock); -- s = __l2cap_get_chan_by_scid(l, cid); -- read_unlock(&l->lock); -- -- return s; --} -- --static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, __u8 ident) --{ -- struct sock *s; -- -- for (s = l->head; s; s = l2cap_pi(s)->next_c) { -- if (l2cap_pi(s)->ident == ident) -- break; -- } -- -- return s; --} -- --static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, __u8 ident) --{ -- struct sock *s; -- -- read_lock(&l->lock); -- s = __l2cap_get_chan_by_ident(l, ident); -- read_unlock(&l->lock); -- -- return s; --} -- --static __u16 l2cap_alloc_cid(struct l2cap_chan_list *l) --{ -- __u16 cid = 0x0040; -- -- for (; cid < 0xffff; cid++) { -- if(!__l2cap_get_chan_by_scid(l, cid)) -- return cid; -- } -- -- return 0; --} -- --static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk) --{ -- sock_hold(sk); -- -- if (l->head) -- l2cap_pi(l->head)->prev_c = sk; -- -- l2cap_pi(sk)->next_c = l->head; -- l2cap_pi(sk)->prev_c = NULL; -- l->head = sk; --} -- --static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk) --{ -- struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c; -- -- write_lock(&l->lock); -- if (sk == l->head) -- l->head = next; -- -- if (next) -- l2cap_pi(next)->prev_c = prev; -- if (prev) -- l2cap_pi(prev)->next_c = next; -- write_unlock(&l->lock); -- -- __sock_put(sk); --} -- --static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) --{ -- struct l2cap_chan_list *l = &conn->chan_list; -- -- DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid); -- -- l2cap_conn_clear_timer(conn); -- -- atomic_inc(&conn->refcnt); -- l2cap_pi(sk)->conn = conn; -- -- if (sk->type == SOCK_SEQPACKET) { -- /* Alloc CID for normal socket */ -- l2cap_pi(sk)->scid = l2cap_alloc_cid(l); -- } else { -- /* Raw socket can send only signalling messages */ -- l2cap_pi(sk)->scid = 0x0001; -- l2cap_pi(sk)->dcid = 0x0001; -- l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; -- } -- -- __l2cap_chan_link(l, sk); -- -- if (parent) -- l2cap_accept_queue(parent, sk); --} -- --static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) --{ -- struct l2cap_chan_list *l = &conn->chan_list; -- -- write_lock(&l->lock); -- __l2cap_chan_add(conn, sk, parent); -- write_unlock(&l->lock); --} -- --/* Delete channel. -- * Must be called on the locked socket. */ --static void l2cap_chan_del(struct sock *sk, int err) --{ -- struct l2cap_conn *conn; -- struct sock *parent; -- -- conn = l2cap_pi(sk)->conn; -- parent = l2cap_pi(sk)->parent; -- -- DBG("sk %p, conn %p, err %d", sk, conn, err); -- -- if (parent) { -- /* Unlink from parent accept queue */ -- bh_lock_sock(parent); -- l2cap_accept_unlink(sk); -- bh_unlock_sock(parent); -- } -- -- if (conn) { -- long timeout; -- -- /* Unlink from channel list */ -- l2cap_chan_unlink(&conn->chan_list, sk); -- l2cap_pi(sk)->conn = NULL; -- -- if (conn->out) -- timeout = L2CAP_DISCONN_TIMEOUT; -- else -- timeout = L2CAP_CONN_IDLE_TIMEOUT; -- -- if (atomic_dec_and_test(&conn->refcnt) && conn->state == BT_CONNECTED) { -- /* Schedule Baseband disconnect */ -- l2cap_conn_set_timer(conn, timeout); -- } -- } -- -- sk->state = BT_CLOSED; -- sk->err = err; -- sk->state_change(sk); -- -- sk->zapped = 1; --} -- --static void l2cap_conn_ready(struct l2cap_conn *conn) --{ -- struct l2cap_chan_list *l = &conn->chan_list; -- struct sock *sk; -- -- DBG("conn %p", conn); -- -- read_lock(&l->lock); -- -- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { -- bh_lock_sock(sk); -- -- if (sk->type != SOCK_SEQPACKET) { -- sk->state = BT_CONNECTED; -- sk->state_change(sk); -- l2cap_sock_clear_timer(sk); -- } else if (sk->state == BT_CONNECT) { -- l2cap_conn_req req; -- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); -- req.psm = l2cap_pi(sk)->psm; -- l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req); -- -- l2cap_sock_set_timer(sk, sk->sndtimeo); -- } -- -- bh_unlock_sock(sk); -- } -- -- read_unlock(&l->lock); --} -- --static void l2cap_chan_ready(struct sock *sk) --{ -- struct sock *parent = l2cap_pi(sk)->parent; -- -- DBG("sk %p, parent %p", sk, parent); -- -- l2cap_pi(sk)->conf_state = 0; -- l2cap_sock_clear_timer(sk); -- -- if (!parent) { -- /* Outgoing channel. -- * Wake up socket sleeping on connect. -- */ -- sk->state = BT_CONNECTED; -- sk->state_change(sk); -- } else { -- /* Incomming channel. -- * Wake up socket sleeping on accept. -- */ -- parent->data_ready(parent, 1); -- } --} -- --/* Copy frame to all raw sockets on that connection */ --void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) --{ -- struct l2cap_chan_list *l = &conn->chan_list; -- struct sk_buff *nskb; -- struct sock * sk; -- -- DBG("conn %p", conn); -- -- read_lock(&l->lock); -- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { -- if (sk->type != SOCK_RAW) -- continue; -- -- /* Don't send frame to the socket it came from */ -- if (skb->sk == sk) -- continue; -- -- if (!(nskb = skb_clone(skb, GFP_ATOMIC))) -- continue; -- -- skb_queue_tail(&sk->receive_queue, nskb); -- sk->data_ready(sk, nskb->len); -- } -- read_unlock(&l->lock); --} -- --static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len) --{ -- struct l2cap_conn *conn = l2cap_pi(sk)->conn; -- struct sk_buff *skb, **frag; -- int err, size, count, sent=0; -- l2cap_hdr *lh; -- -- /* Check outgoing MTU */ -- if (len > l2cap_pi(sk)->omtu) -- return -EINVAL; -- -- DBG("sk %p len %d", sk, len); -- -- /* First fragment (with L2CAP header) */ -- count = MIN(conn->iff->mtu - L2CAP_HDR_SIZE, len); -- size = L2CAP_HDR_SIZE + count; -- if (!(skb = bluez_skb_send_alloc(sk, size, msg->msg_flags & MSG_DONTWAIT, &err))) -- return err; -- -- /* Create L2CAP header */ -- lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); -- lh->len = __cpu_to_le16(len); -- lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid); -- -- if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { -- err = -EFAULT; -- goto fail; -- } -- -- sent += count; -- len -= count; -- -- /* Continuation fragments (no L2CAP header) */ -- frag = &skb_shinfo(skb)->frag_list; -- while (len) { -- count = MIN(conn->iff->mtu, len); -- -- *frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); -- if (!*frag) -- goto fail; -- -- if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) { -- err = -EFAULT; -- goto fail; -- } -- -- sent += count; -- len -= count; -- -- frag = &(*frag)->next; -- } -- -- if ((err = hci_send_acl(conn->hconn, skb, 0)) < 0) -- goto fail; -- -- return sent; -- --fail: -- kfree_skb(skb); -- return err; --} -- --/* --------- L2CAP signalling commands --------- */ --static inline __u8 l2cap_get_ident(struct l2cap_conn *conn) --{ -- __u8 id; -- -- /* Get next available identificator. -- * 1 - 199 are used by kernel. -- * 200 - 254 are used by utilities like l2ping, etc -- */ -- -- spin_lock(&conn->lock); -- -- if (++conn->tx_ident > 199) -- conn->tx_ident = 1; -- -- id = conn->tx_ident; -- -- spin_unlock(&conn->lock); -- -- return id; --} -- --static inline struct sk_buff *l2cap_build_cmd(__u8 code, __u8 ident, __u16 len, void *data) --{ -- struct sk_buff *skb; -- l2cap_cmd_hdr *cmd; -- l2cap_hdr *lh; -- int size; -- -- DBG("code 0x%2.2x, ident 0x%2.2x, len %d", code, ident, len); -- -- size = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + len; -- if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) -- return NULL; -- -- lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); -- lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + len); -- lh->cid = __cpu_to_le16(0x0001); -- -- cmd = (l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE); -- cmd->code = code; -- cmd->ident = ident; -- cmd->len = __cpu_to_le16(len); -- -- if (len) -- memcpy(skb_put(skb, len), data, len); -- -- return skb; --} -- --static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data) --{ -- struct sk_buff *skb; -- __u8 ident; -- -- DBG("code 0x%2.2x", code); -- -- ident = l2cap_get_ident(conn); -- if (!(skb = l2cap_build_cmd(code, ident, len, data))) -- return -ENOMEM; -- return hci_send_acl(conn->hconn, skb, 0); --} -- --static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data) --{ -- struct sk_buff *skb; -- -- DBG("code 0x%2.2x", code); -- -- if (!(skb = l2cap_build_cmd(code, ident, len, data))) -- return -ENOMEM; -- return hci_send_acl(conn->hconn, skb, 0); --} -- --static inline int l2cap_get_conf_opt(__u8 **ptr, __u8 *type, __u32 *val) --{ -- l2cap_conf_opt *opt = (l2cap_conf_opt *) (*ptr); -- int len; -- -- *type = opt->type; -- switch (opt->len) { -- case 1: -- *val = *((__u8 *) opt->val); -- break; -- -- case 2: -- *val = __le16_to_cpu(*((__u16 *)opt->val)); -- break; -- -- case 4: -- *val = __le32_to_cpu(*((__u32 *)opt->val)); -- break; -- -- default: -- *val = 0L; -- break; -- }; -- -- DBG("type 0x%2.2x len %d val 0x%8.8x", *type, opt->len, *val); -- -- len = L2CAP_CONF_OPT_SIZE + opt->len; -- -- *ptr += len; -- -- return len; --} -- --static inline void l2cap_parse_conf_req(struct sock *sk, char *data, int len) --{ -- __u8 type, hint; __u32 val; -- __u8 *ptr = data; -- -- DBG("sk %p len %d", sk, len); -- -- while (len >= L2CAP_CONF_OPT_SIZE) { -- len -= l2cap_get_conf_opt(&ptr, &type, &val); -- -- hint = type & 0x80; -- type &= 0x7f; -- -- switch (type) { -- case L2CAP_CONF_MTU: -- l2cap_pi(sk)->conf_mtu = val; -- break; -- -- case L2CAP_CONF_FLUSH_TO: -- l2cap_pi(sk)->flush_to = val; -- break; -- -- case L2CAP_CONF_QOS: -- break; -- -- default: -- if (hint) -- break; -- -- /* FIXME: Reject unknon option */ -- break; -- }; -- } --} -- --static inline void l2cap_add_conf_opt(__u8 **ptr, __u8 type, __u8 len, __u32 val) --{ -- register l2cap_conf_opt *opt = (l2cap_conf_opt *) (*ptr); -- -- DBG("type 0x%2.2x len %d val 0x%8.8x", type, len, val); -- -- opt->type = type; -- opt->len = len; -- switch (len) { -- case 1: -- *((__u8 *) opt->val) = val; -- break; -- -- case 2: -- *((__u16 *) opt->val) = __cpu_to_le16(val); -- break; -- -- case 4: -- *((__u32 *) opt->val) = __cpu_to_le32(val); -- break; -- }; -- -- *ptr += L2CAP_CONF_OPT_SIZE + len; --} -- --static int l2cap_build_conf_req(struct sock *sk, __u8 *data) --{ -- struct l2cap_pinfo *pi = l2cap_pi(sk); -- l2cap_conf_req *req = (l2cap_conf_req *) data; -- __u8 *ptr = req->data; -- -- DBG("sk %p", sk); -- -- if (pi->imtu != L2CAP_DEFAULT_MTU) -- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); -- -- /* FIXME. Need actual value of the flush timeout */ -- //if (flush_to != L2CAP_DEFAULT_FLUSH_TO) -- // l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to); -- -- req->dcid = __cpu_to_le16(pi->dcid); -- req->flags = __cpu_to_le16(0); -- -- return ptr - data; --} -- --static int l2cap_conf_output(struct sock *sk, __u8 **ptr) --{ -- struct l2cap_pinfo *pi = l2cap_pi(sk); -- int result = 0; -- -- /* Configure output options and let other side know -- * which ones we don't like. -- */ -- if (pi->conf_mtu < pi->omtu) { -- l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, l2cap_pi(sk)->omtu); -- result = L2CAP_CONF_UNACCEPT; -- } else { -- pi->omtu = pi->conf_mtu; -- } -- -- DBG("sk %p result %d", sk, result); -- return result; --} -- --static int l2cap_build_conf_rsp(struct sock *sk, __u8 *data, int *result) --{ -- l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data; -- __u8 *ptr = rsp->data; -- -- DBG("sk %p complete %d", sk, result ? 1 : 0); -- -- if (result) -- *result = l2cap_conf_output(sk, &ptr); -- -- rsp->scid = __cpu_to_le16(l2cap_pi(sk)->dcid); -- rsp->result = __cpu_to_le16(result ? *result : 0); -- rsp->flags = __cpu_to_le16(0); -- -- return ptr - data; --} -- --static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) --{ -- struct l2cap_chan_list *list = &conn->chan_list; -- l2cap_conn_req *req = (l2cap_conn_req *) data; -- l2cap_conn_rsp rsp; -- struct sock *sk, *parent; -- -- __u16 scid = __le16_to_cpu(req->scid); -- __u16 psm = req->psm; -- -- DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid); -- -- /* Check if we have socket listening on psm */ -- if (!(parent = l2cap_get_sock_listen(&conn->src, psm))) -- goto reject; -- -- bh_lock_sock(parent); -- write_lock(&list->lock); -- -- /* Check if we already have channel with that dcid */ -- if (__l2cap_get_chan_by_dcid(list, scid)) -- goto unlock; -- -- /* Check for backlog size */ -- if (parent->ack_backlog > parent->max_ack_backlog) -- goto unlock; -- -- if (!(sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC))) -- goto unlock; -- -- l2cap_sock_init(sk, parent); -- -- bacpy(&l2cap_pi(sk)->src, &conn->src); -- bacpy(&l2cap_pi(sk)->dst, &conn->dst); -- l2cap_pi(sk)->psm = psm; -- l2cap_pi(sk)->dcid = scid; -- -- __l2cap_chan_add(conn, sk, parent); -- sk->state = BT_CONFIG; -- -- write_unlock(&list->lock); -- bh_unlock_sock(parent); -- -- rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid); -- rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid); -- rsp.result = __cpu_to_le16(0); -- rsp.status = __cpu_to_le16(0); -- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp); -- -- return 0; -- --unlock: -- write_unlock(&list->lock); -- bh_unlock_sock(parent); -- --reject: -- rsp.scid = __cpu_to_le16(scid); -- rsp.dcid = __cpu_to_le16(0); -- rsp.status = __cpu_to_le16(0); -- rsp.result = __cpu_to_le16(L2CAP_CONN_NO_MEM); -- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp); -- -- return 0; --} -- --static inline int l2cap_connect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) --{ -- l2cap_conn_rsp *rsp = (l2cap_conn_rsp *) data; -- __u16 scid, dcid, result, status; -- struct sock *sk; -- -- scid = __le16_to_cpu(rsp->scid); -- dcid = __le16_to_cpu(rsp->dcid); -- result = __le16_to_cpu(rsp->result); -- status = __le16_to_cpu(rsp->status); -- -- DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status); -- -- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid))) -- return -ENOENT; -- -- bh_lock_sock(sk); -- -- if (!result) { -- char req[64]; -- -- sk->state = BT_CONFIG; -- l2cap_pi(sk)->dcid = dcid; -- l2cap_pi(sk)->conf_state |= CONF_REQ_SENT; -- -- l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); -- } else { -- l2cap_chan_del(sk, ECONNREFUSED); -- } -- -- bh_unlock_sock(sk); -- return 0; --} -- --static inline int l2cap_config_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) --{ -- l2cap_conf_req * req = (l2cap_conf_req *) data; -- __u16 dcid, flags; -- __u8 rsp[64]; -- struct sock *sk; -- int result; -- -- dcid = __le16_to_cpu(req->dcid); -- flags = __le16_to_cpu(req->flags); -- -- DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags); -- -- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid))) -- return -ENOENT; -- -- bh_lock_sock(sk); -- -- l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE); -- -- if (flags & 0x01) { -- /* Incomplete config. Send empty response. */ -- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp); -- goto unlock; -- } -- -- /* Complete config. */ -- l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp); -- -- if (result) -- goto unlock; -- -- /* Output config done */ -- l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE; -- -- if (l2cap_pi(sk)->conf_state & CONF_INPUT_DONE) { -- sk->state = BT_CONNECTED; -- l2cap_chan_ready(sk); -- } else if (!(l2cap_pi(sk)->conf_state & CONF_REQ_SENT)) { -- char req[64]; -- l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); -- } -- --unlock: -- bh_unlock_sock(sk); -- -- return 0; --} -- --static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) --{ -- l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data; -- __u16 scid, flags, result; -- struct sock *sk; -- int err = 0; -- -- scid = __le16_to_cpu(rsp->scid); -- flags = __le16_to_cpu(rsp->flags); -- result = __le16_to_cpu(rsp->result); -- -- DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result); -- -- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid))) -- return -ENOENT; -- -- bh_lock_sock(sk); -- -- if (result) { -- l2cap_disconn_req req; -- -- /* They didn't like our options. Well... we do not negotiate. -- * Close channel. -- */ -- sk->state = BT_DISCONN; -- -- req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); -- req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); -- l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req); -- -- l2cap_sock_set_timer(sk, sk->sndtimeo); -- goto done; -- } -- -- if (flags & 0x01) -- goto done; -- -- /* Input config done */ -- l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE; -- -- if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) { -- sk->state = BT_CONNECTED; -- l2cap_chan_ready(sk); -- } -- --done: -- bh_unlock_sock(sk); -- -- return err; --} -- --static inline int l2cap_disconnect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) --{ -- l2cap_disconn_req *req = (l2cap_disconn_req *) data; -- l2cap_disconn_rsp rsp; -- __u16 dcid, scid; -- struct sock *sk; -- -- scid = __le16_to_cpu(req->scid); -- dcid = __le16_to_cpu(req->dcid); -- -- DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid); -- -- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid))) -- return 0; -- -- bh_lock_sock(sk); -- -- rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid); -- rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid); -- l2cap_send_rsp(conn, cmd->ident, L2CAP_DISCONN_RSP, L2CAP_DISCONN_RSP_SIZE, &rsp); -- -- l2cap_chan_del(sk, ECONNRESET); -- -- bh_unlock_sock(sk); -- -- l2cap_sock_kill(sk); -- -- return 0; --} -- --static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data) --{ -- l2cap_disconn_rsp *rsp = (l2cap_disconn_rsp *) data; -- __u16 dcid, scid; -- struct sock *sk; -- -- scid = __le16_to_cpu(rsp->scid); -- dcid = __le16_to_cpu(rsp->dcid); -- -- DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid); -- -- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid))) -- return -ENOENT; -- -- bh_lock_sock(sk); -- l2cap_sock_clear_timer(sk); -- l2cap_chan_del(sk, ECONNABORTED); -- bh_unlock_sock(sk); -- -- l2cap_sock_kill(sk); -- -- return 0; --} -- --static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) --{ -- __u8 *data = skb->data; -- int len = skb->len; -- l2cap_cmd_hdr cmd; -- int err = 0; -- -- while (len >= L2CAP_CMD_HDR_SIZE) { -- memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE); -- data += L2CAP_CMD_HDR_SIZE; -- len -= L2CAP_CMD_HDR_SIZE; -- -- cmd.len = __le16_to_cpu(cmd.len); -- -- DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd.len, cmd.ident); -- -- if (cmd.len > len || !cmd.ident) { -- DBG("corrupted command"); -- break; -- } -- -- switch (cmd.code) { -- case L2CAP_CONN_REQ: -- err = l2cap_connect_req(conn, &cmd, data); -- break; -- -- case L2CAP_CONN_RSP: -- err = l2cap_connect_rsp(conn, &cmd, data); -- break; -- -- case L2CAP_CONF_REQ: -- err = l2cap_config_req(conn, &cmd, data); -- break; -- -- case L2CAP_CONF_RSP: -- err = l2cap_config_rsp(conn, &cmd, data); -- break; -- -- case L2CAP_DISCONN_REQ: -- err = l2cap_disconnect_req(conn, &cmd, data); -- break; -- -- case L2CAP_DISCONN_RSP: -- err = l2cap_disconnect_rsp(conn, &cmd, data); -- break; -- -- case L2CAP_COMMAND_REJ: -- /* FIXME: We should process this */ -- l2cap_raw_recv(conn, skb); -- break; -- -- case L2CAP_ECHO_REQ: -- l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data); -- break; -- -- case L2CAP_ECHO_RSP: -- case L2CAP_INFO_REQ: -- case L2CAP_INFO_RSP: -- l2cap_raw_recv(conn, skb); -- break; -- -- default: -- ERR("Uknown signaling command 0x%2.2x", cmd.code); -- err = -EINVAL; -- break; -- }; -- -- if (err) { -- l2cap_cmd_rej rej; -- DBG("error %d", err); -- -- /* FIXME: Map err to a valid reason. */ -- rej.reason = __cpu_to_le16(0); -- l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej); -- } -- -- data += cmd.len; -- len -= cmd.len; -- } -- -- kfree_skb(skb); --} -- --static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct sk_buff *skb) --{ -- struct sock *sk; -- -- if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, cid))) { -- DBG("unknown cid 0x%4.4x", cid); -- goto drop; -- } -- -- DBG("sk %p, len %d", sk, skb->len); -- -- if (sk->state != BT_CONNECTED) -- goto drop; -- -- if (l2cap_pi(sk)->imtu < skb->len) -- goto drop; -- -- skb_queue_tail(&sk->receive_queue, skb); -- sk->data_ready(sk, skb->len); -- -- return 0; -- --drop: -- kfree_skb(skb); -- -- return 0; --} -- --static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) --{ -- l2cap_hdr *lh = (l2cap_hdr *) skb->data; -- __u16 cid, len; -- -- skb_pull(skb, L2CAP_HDR_SIZE); -- cid = __le16_to_cpu(lh->cid); -- len = __le16_to_cpu(lh->len); -- -- DBG("len %d, cid 0x%4.4x", len, cid); -- -- if (cid == 0x0001) -- l2cap_sig_channel(conn, skb); -- else -- l2cap_data_channel(conn, cid, skb); --} -- --/* ------------ L2CAP interface with lower layer (HCI) ------------- */ --static int l2cap_dev_event(struct notifier_block *this, unsigned long event, void *ptr) --{ -- struct hci_dev *hdev = (struct hci_dev *) ptr; -- -- DBG("hdev %s, event %ld", hdev->name, event); -- -- write_lock(&l2cap_rt_lock); -- -- switch (event) { -- case HCI_DEV_UP: -- l2cap_iff_add(hdev); -- break; -- -- case HCI_DEV_DOWN: -- l2cap_iff_del(hdev); -- break; -- }; -- -- write_unlock(&l2cap_rt_lock); -- -- return NOTIFY_DONE; --} -- --int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) --{ -- struct l2cap_iff *iff; -- -- DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); -- -- if (!(iff = hdev->l2cap_data)) { -- ERR("unknown interface"); -- return 0; -- } -- -- /* Always accept connection */ -- return 1; --} -- --int l2cap_connect_cfm(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 status, struct hci_conn *hconn) --{ -- struct l2cap_conn *conn; -- struct l2cap_iff *iff; -- int err = 0; -- -- DBG("hdev %s bdaddr %s hconn %p", hdev->name, batostr(bdaddr), hconn); -- -- if (!(iff = hdev->l2cap_data)) { -- ERR("unknown interface"); -- return 0; -- } -- -- l2cap_iff_lock(iff); -- -- conn = l2cap_get_conn_by_addr(iff, bdaddr); -- -- if (conn) { -- /* Outgoing connection */ -- DBG("Outgoing connection: %s -> %s, %p, %2.2x", batostr(iff->bdaddr), batostr(bdaddr), conn, status); -- -- if (!status && hconn) { -- conn->state = BT_CONNECTED; -- conn->hconn = hconn; -- -- hconn->l2cap_data = (void *)conn; -- -- /* Establish channels */ -- l2cap_conn_ready(conn); -- } else { -- l2cap_conn_del(conn, bterr(status)); -- } -- } else { -- /* Incomming connection */ -- DBG("Incomming connection: %s -> %s, %2.2x", batostr(iff->bdaddr), batostr(bdaddr), status); -- -- if (status || !hconn) -- goto done; -- -- if (!(conn = l2cap_conn_add(iff, bdaddr))) { -- err = -ENOMEM; -- goto done; -- } -- -- conn->hconn = hconn; -- hconn->l2cap_data = (void *)conn; -- -- conn->state = BT_CONNECTED; -- } -- --done: -- l2cap_iff_unlock(iff); -- -- return err; --} -- --int l2cap_disconn_ind(struct hci_conn *hconn, __u8 reason) --{ -- struct l2cap_conn *conn = hconn->l2cap_data; -- -- DBG("hconn %p reason %d", hconn, reason); -- -- if (!conn) { -- ERR("unknown connection"); -- return 0; -- } -- conn->hconn = NULL; -- -- l2cap_iff_lock(conn->iff); -- l2cap_conn_del(conn, bterr(reason)); -- l2cap_iff_unlock(conn->iff); -- -- return 0; --} -- --int l2cap_recv_acldata(struct hci_conn *hconn, struct sk_buff *skb, __u16 flags) --{ -- struct l2cap_conn *conn = hconn->l2cap_data; -- -- if (!conn) { -- ERR("unknown connection %p", hconn); -- goto drop; -- } -- -- DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); -- -- if (flags & ACL_START) { -- int flen, tlen, size; -- l2cap_hdr *lh; -- -- if (conn->rx_len) { -- ERR("Unexpected start frame (len %d)", skb->len); -- kfree_skb(conn->rx_skb); conn->rx_skb = NULL; -- conn->rx_len = 0; -- } -- -- if (skb->len < L2CAP_HDR_SIZE) { -- ERR("Frame is too small (len %d)", skb->len); -- goto drop; -- } -- -- lh = (l2cap_hdr *)skb->data; -- tlen = __le16_to_cpu(lh->len); -- flen = skb->len - L2CAP_HDR_SIZE; -- -- DBG("Start: total len %d, frag len %d", tlen, flen); -- -- if (flen == tlen) { -- /* Complete frame received */ -- l2cap_recv_frame(conn, skb); -- return 0; -- } -- -- /* Allocate skb for the complete frame (with header) */ -- size = L2CAP_HDR_SIZE + tlen; -- if (!(conn->rx_skb = bluez_skb_alloc(size, GFP_ATOMIC))) -- goto drop; -- -- memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len); -- -- conn->rx_len = tlen - flen; -- } else { -- DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); -- -- if (!conn->rx_len) { -- ERR("Unexpected continuation frame (len %d)", skb->len); -- goto drop; -- } -- -- if (skb->len > conn->rx_len) { -- ERR("Fragment is too large (len %d)", skb->len); -- kfree_skb(conn->rx_skb); conn->rx_skb = NULL; -- goto drop; -- } -- -- memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len); -- conn->rx_len -= skb->len; -- -- if (!conn->rx_len) { -- /* Complete frame received */ -- l2cap_recv_frame(conn, conn->rx_skb); -- conn->rx_skb = NULL; -- } -- } -- --drop: -- kfree_skb(skb); -- return 0; --} -- --struct proto_ops l2cap_sock_ops = { -- family: PF_BLUETOOTH, -- release: l2cap_sock_release, -- bind: l2cap_sock_bind, -- connect: l2cap_sock_connect, -- listen: l2cap_sock_listen, -- accept: l2cap_sock_accept, -- getname: l2cap_sock_getname, -- sendmsg: l2cap_sock_sendmsg, -- recvmsg: l2cap_sock_recvmsg, -- poll: l2cap_sock_poll, -- socketpair: sock_no_socketpair, -- ioctl: sock_no_ioctl, -- shutdown: sock_no_shutdown, -- setsockopt: l2cap_sock_setsockopt, -- getsockopt: l2cap_sock_getsockopt, -- mmap: sock_no_mmap --}; -- --struct net_proto_family l2cap_sock_family_ops = { -- family: PF_BLUETOOTH, -- create: l2cap_sock_create --}; -- --struct hci_proto l2cap_hci_proto = { -- name: "L2CAP", -- id: HCI_PROTO_L2CAP, -- connect_ind: l2cap_connect_ind, -- connect_cfm: l2cap_connect_cfm, -- disconn_ind: l2cap_disconn_ind, -- recv_acldata: l2cap_recv_acldata, --}; -- --struct notifier_block l2cap_nblock = { -- notifier_call: l2cap_dev_event --}; -- --int __init l2cap_init(void) --{ -- INF("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc", -- VERSION); -- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); -- -- if (bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops)) { -- ERR("Can't register L2CAP socket"); -- return -EPROTO; -- } -- -- if (hci_register_proto(&l2cap_hci_proto) < 0) { -- ERR("Can't register L2CAP protocol"); -- return -EPROTO; -- } -- -- hci_register_notifier(&l2cap_nblock); -- -- l2cap_register_proc(); -- -- return 0; --} -- --void l2cap_cleanup(void) --{ -- l2cap_unregister_proc(); -- -- /* Unregister socket, protocol and notifier */ -- if (bluez_sock_unregister(BTPROTO_L2CAP)) -- ERR("Can't unregister L2CAP socket"); -- -- if (hci_unregister_proto(&l2cap_hci_proto) < 0) -- ERR("Can't unregister L2CAP protocol"); -- -- hci_unregister_notifier(&l2cap_nblock); -- -- /* We _must_ not have any sockets and/or connections -- * at this stage. -- */ -- -- /* Free interface list and unlock HCI devices */ -- { -- struct list_head *list = &l2cap_iff_list; -- -- while (!list_empty(list)) { -- struct l2cap_iff *iff; -- -- iff = list_entry(list->next, struct l2cap_iff, list); -- l2cap_iff_del(iff->hdev); -- } -- } --} -- --module_init(l2cap_init); --module_exit(l2cap_cleanup); -- --MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); --MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION); --MODULE_LICENSE("GPL"); -- ---- linux/net/bluetooth/l2cap_proc.c~bluetooth-2.4.18-mh11 -+++ linux/net/bluetooth/l2cap_proc.c --/* -- BlueZ - Bluetooth protocol stack for Linux -- Copyright (C) 2000-2001 Qualcomm Incorporated -- -- Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License version 2 as -- published by the Free Software Foundation; -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -- -- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -- SOFTWARE IS DISCLAIMED. --*/ -- --/* -- * BlueZ L2CAP proc fs support. -- * -- * $Id: l2cap_proc.c,v 1.2 2001/06/02 01:40:09 maxk Exp $ -- */ -- --#include <linux/config.h> --#include <linux/module.h> -- --#include <linux/types.h> --#include <linux/errno.h> --#include <linux/kernel.h> --#include <linux/major.h> --#include <linux/sched.h> --#include <linux/slab.h> --#include <linux/poll.h> --#include <linux/fcntl.h> --#include <linux/init.h> --#include <linux/skbuff.h> --#include <linux/interrupt.h> --#include <linux/socket.h> --#include <linux/skbuff.h> --#include <linux/proc_fs.h> --#include <linux/list.h> --#include <net/sock.h> -- --#include <asm/system.h> --#include <asm/uaccess.h> -- --#include <net/bluetooth/bluez.h> --#include <net/bluetooth/bluetooth.h> --#include <net/bluetooth/hci_core.h> --#include <net/bluetooth/l2cap_core.h> -- --#ifndef L2CAP_DEBUG --#undef DBG --#define DBG( A... ) --#endif -- --/* ----- PROC fs support ----- */ --static int l2cap_conn_dump(char *buf, struct l2cap_iff *iff) --{ -- struct list_head *p; -- char *ptr = buf; -- -- list_for_each(p, &iff->conn_list) { -- struct l2cap_conn *c; -- -- c = list_entry(p, struct l2cap_conn, list); -- ptr += sprintf(ptr, " %p %d %p %p %s %s\n", -- c, c->state, c->iff, c->hconn, batostr(&c->src), batostr(&c->dst)); -- } -- -- return ptr - buf; --} -- --static int l2cap_iff_dump(char *buf) --{ -- struct list_head *p; -- char *ptr = buf; -- -- ptr += sprintf(ptr, "Interfaces:\n"); -- -- write_lock(&l2cap_rt_lock); -- -- list_for_each(p, &l2cap_iff_list) { -- struct l2cap_iff *iff; -- -- iff = list_entry(p, struct l2cap_iff, list); -- -- ptr += sprintf(ptr, " %s %p %p\n", iff->hdev->name, iff, iff->hdev); -- -- l2cap_iff_lock(iff); -- ptr += l2cap_conn_dump(ptr, iff); -- l2cap_iff_unlock(iff); -- } -- -- write_unlock(&l2cap_rt_lock); -- -- ptr += sprintf(ptr, "\n"); -- -- return ptr - buf; --} -- --static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list) --{ -- struct l2cap_pinfo *pi; -- struct sock *sk; -- char *ptr = buf; -- -- ptr += sprintf(ptr, "Sockets:\n"); -- -- write_lock(&list->lock); -- -- for (sk = list->head; sk; sk = sk->next) { -- pi = l2cap_pi(sk); -- ptr += sprintf(ptr, " %p %d %p %d %s %s 0x%4.4x 0x%4.4x %d %d\n", sk, sk->state, pi->conn, pi->psm, -- batostr(&pi->src), batostr(&pi->dst), pi->scid, pi->dcid, pi->imtu, pi->omtu ); -- } -- -- write_unlock(&list->lock); -- -- ptr += sprintf(ptr, "\n"); -- -- return ptr - buf; --} -- --static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv) --{ -- char *ptr = buf; -- int len; -- -- DBG("count %d, offset %ld", count, offset); -- -- ptr += l2cap_iff_dump(ptr); -- ptr += l2cap_sock_dump(ptr, &l2cap_sk_list); -- len = ptr - buf; -- -- if (len <= count + offset) -- *eof = 1; -- -- *start = buf + offset; -- len -= offset; -- -- if (len > count) -- len = count; -- if (len < 0) -- len = 0; -- -- return len; --} -- --void l2cap_register_proc(void) --{ -- create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL); --} -- --void l2cap_unregister_proc(void) --{ -- remove_proc_entry("bluetooth/l2cap", NULL); --} ---- linux/net/bluetooth/lib.c~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/net/bluetooth/lib.c 2004-01-25 23:37:39.000000000 +0100 -@@ -25,7 +25,7 @@ - /* - * BlueZ kernel library. - * -- * $Id: lib.c,v 1.3 2001/06/22 23:14:23 maxk Exp $ -+ * $Id: lib.c,v 1.2 2002/06/20 19:55:08 maxk Exp $ - */ - - #include <linux/kernel.h> -@@ -105,7 +105,7 @@ - return EACCES; - - case 0x06: -- return EINVAL; -+ return EBADE; - - case 0x07: - return ENOMEM; ---- linux/net/bluetooth/Makefile~bluetooth-2.4.18-mh11 2001-06-12 04:15:27.000000000 +0200 -+++ linux/net/bluetooth/Makefile 2004-01-25 23:37:39.000000000 +0100 -@@ -1,20 +1,31 @@ - # --# Makefile for the Bluetooth subsystem -+# Makefile for the Linux Bluetooth subsystem - # --O_TARGET := bluetooth.o - --list-multi := hci.o l2cap.o --export-objs := syms.o --hci-objs := af_bluetooth.o hci_core.o hci_sock.o lib.o syms.o --l2cap-objs := l2cap_core.o l2cap_proc.o -+O_TARGET := bluetooth.o - --obj-$(CONFIG_BLUEZ) += hci.o -+list-multi := bluez.o -+export-objs := syms.o l2cap.o -+ -+bluez-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o -+ -+obj-$(CONFIG_BLUEZ) += bluez.o - obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o -+obj-$(CONFIG_BLUEZ_SCO) += sco.o - --include $(TOPDIR)/Rules.make -+subdir-$(CONFIG_BLUEZ_RFCOMM) += rfcomm -+subdir-$(CONFIG_BLUEZ_BNEP) += bnep -+subdir-$(CONFIG_BLUEZ_CMTP) += cmtp - --hci.o: $(hci-objs) -- $(LD) -r -o $@ $(hci-objs) -+ifeq ($(CONFIG_BLUEZ_RFCOMM),y) -+obj-y += rfcomm/rfcomm.o -+endif - --l2cap.o: $(l2cap-objs) -- $(LD) -r -o $@ $(l2cap-objs) -+ifeq ($(CONFIG_BLUEZ_BNEP),y) -+obj-y += bnep/bnep.o -+endif -+ -+include $(TOPDIR)/Rules.make -+ -+bluez.o: $(bluez-objs) -+ $(LD) -r -o $@ $(bluez-objs) ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/rfcomm/Config.in 2004-01-25 23:37:39.000000000 +0100 -@@ -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 -+ ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/rfcomm/core.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,1939 @@ -+/* -+ 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); -+ 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"); ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/rfcomm/crc.c 2004-01-25 23:37:39.000000000 +0100 -@@ -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 -+}; ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/rfcomm/Makefile 2004-01-25 23:37:39.000000000 +0100 -@@ -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 ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/rfcomm/sock.c 2004-01-25 23:37:39.000000000 +0100 -@@ -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); -+} ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/rfcomm/tty.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,945 @@ -+/* -+ RFCOMM implementation for Linux Bluetooth stack (BlueZ). -+ Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> -+ Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * RFCOMM TTY. -+ * -+ * $Id: 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) -+{ -+ if (atomic_dec_and_test(&dev->refcnt)) -+ rfcomm_dev_destruct(dev); -+} -+ -+static struct rfcomm_dev *__rfcomm_dev_get(int id) -+{ -+ struct rfcomm_dev *dev; -+ struct list_head *p; -+ -+ list_for_each(p, &rfcomm_dev_list) { -+ dev = list_entry(p, struct rfcomm_dev, list); -+ if (dev->id == id) -+ return dev; -+ } -+ -+ return NULL; -+} -+ -+static inline struct rfcomm_dev *rfcomm_dev_get(int id) -+{ -+ struct rfcomm_dev *dev; -+ -+ read_lock(&rfcomm_dev_lock); -+ dev = __rfcomm_dev_get(id); -+ read_unlock(&rfcomm_dev_lock); -+ -+ if (dev) rfcomm_dev_hold(dev); -+ return dev; -+} -+ -+static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) -+{ -+ struct rfcomm_dev *dev; -+ struct list_head *head = &rfcomm_dev_list, *p; -+ int err = 0; -+ -+ BT_DBG("id %d channel %d", req->dev_id, req->channel); -+ -+ dev = kmalloc(sizeof(struct rfcomm_dev), GFP_KERNEL); -+ if (!dev) -+ return -ENOMEM; -+ memset(dev, 0, sizeof(struct rfcomm_dev)); -+ -+ write_lock_bh(&rfcomm_dev_lock); -+ -+ if (req->dev_id < 0) { -+ dev->id = 0; -+ -+ list_for_each(p, &rfcomm_dev_list) { -+ if (list_entry(p, struct rfcomm_dev, list)->id != dev->id) -+ break; -+ -+ dev->id++; -+ head = p; -+ } -+ } else { -+ dev->id = req->dev_id; -+ -+ list_for_each(p, &rfcomm_dev_list) { -+ struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list); -+ -+ if (entry->id == dev->id) { -+ err = -EADDRINUSE; -+ goto out; -+ } -+ -+ if (entry->id > dev->id - 1) -+ break; -+ -+ head = p; -+ } -+ } -+ -+ if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) { -+ err = -ENFILE; -+ goto out; -+ } -+ -+ sprintf(dev->name, "rfcomm%d", dev->id); -+ -+ list_add(&dev->list, head); -+ atomic_set(&dev->refcnt, 1); -+ -+ bacpy(&dev->src, &req->src); -+ bacpy(&dev->dst, &req->dst); -+ dev->channel = req->channel; -+ -+ dev->flags = req->flags & -+ ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); -+ -+ init_waitqueue_head(&dev->wait); -+ tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); -+ -+ rfcomm_dlc_lock(dlc); -+ dlc->data_ready = rfcomm_dev_data_ready; -+ dlc->state_change = rfcomm_dev_state_change; -+ dlc->modem_status = rfcomm_dev_modem_status; -+ -+ dlc->owner = dev; -+ dev->dlc = dlc; -+ rfcomm_dlc_unlock(dlc); -+ -+ MOD_INC_USE_COUNT; -+ -+out: -+ write_unlock_bh(&rfcomm_dev_lock); -+ -+ if (err) { -+ kfree(dev); -+ return err; -+ } else -+ return dev->id; -+} -+ -+static void rfcomm_dev_del(struct rfcomm_dev *dev) -+{ -+ BT_DBG("dev %p", dev); -+ -+ write_lock_bh(&rfcomm_dev_lock); -+ list_del_init(&dev->list); -+ write_unlock_bh(&rfcomm_dev_lock); -+ -+ rfcomm_dev_put(dev); -+} -+ -+/* ---- Send buffer ---- */ -+ -+static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc) -+{ -+ /* We can't let it be zero, because we don't get a callback -+ when tx_credits becomes nonzero, hence we'd never wake up */ -+ return dlc->mtu * (dlc->tx_credits?:1); -+} -+ -+static void rfcomm_wfree(struct sk_buff *skb) -+{ -+ struct rfcomm_dev *dev = (void *) skb->sk; -+ atomic_sub(skb->truesize, &dev->wmem_alloc); -+ if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) -+ tasklet_schedule(&dev->wakeup_task); -+ rfcomm_dev_put(dev); -+} -+ -+static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev) -+{ -+ rfcomm_dev_hold(dev); -+ atomic_add(skb->truesize, &dev->wmem_alloc); -+ skb->sk = (void *) dev; -+ skb->destructor = rfcomm_wfree; -+} -+ -+static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority) -+{ -+ if (force || atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) { -+ struct sk_buff *skb = alloc_skb(size, priority); -+ if (skb) { -+ rfcomm_set_owner_w(skb, dev); -+ return skb; -+ } -+ } -+ return NULL; -+} -+ -+/* ---- Device IOCTLs ---- */ -+ -+#define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP)) -+ -+static int rfcomm_create_dev(struct sock *sk, unsigned long arg) -+{ -+ struct rfcomm_dev_req req; -+ struct rfcomm_dlc *dlc; -+ int id; -+ -+ if (copy_from_user(&req, (void *) arg, sizeof(req))) -+ return -EFAULT; -+ -+ BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags); -+ -+ if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) -+ return -EPERM; -+ -+ if (req.flags & (1 << RFCOMM_REUSE_DLC)) { -+ /* Socket must be connected */ -+ if (sk->state != BT_CONNECTED) -+ return -EBADFD; -+ -+ dlc = rfcomm_pi(sk)->dlc; -+ rfcomm_dlc_hold(dlc); -+ } else { -+ dlc = rfcomm_dlc_alloc(GFP_KERNEL); -+ if (!dlc) -+ return -ENOMEM; -+ } -+ -+ id = rfcomm_dev_add(&req, dlc); -+ if (id < 0) { -+ rfcomm_dlc_put(dlc); -+ return id; -+ } -+ -+ if (req.flags & (1 << RFCOMM_REUSE_DLC)) { -+ /* DLC is now used by device. -+ * Socket must be disconnected */ -+ sk->state = BT_CLOSED; -+ } -+ -+ return id; -+} -+ -+static int rfcomm_release_dev(unsigned long arg) -+{ -+ struct rfcomm_dev_req req; -+ struct rfcomm_dev *dev; -+ -+ if (copy_from_user(&req, (void *) arg, sizeof(req))) -+ return -EFAULT; -+ -+ BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags); -+ -+ if (!capable(CAP_NET_ADMIN)) -+ return -EPERM; -+ -+ if (!(dev = rfcomm_dev_get(req.dev_id))) -+ return -ENODEV; -+ -+ if (req.flags & (1 << RFCOMM_HANGUP_NOW)) -+ rfcomm_dlc_close(dev->dlc, 0); -+ -+ rfcomm_dev_del(dev); -+ rfcomm_dev_put(dev); -+ return 0; -+} -+ -+static int rfcomm_get_dev_list(unsigned long arg) -+{ -+ struct rfcomm_dev_list_req *dl; -+ struct rfcomm_dev_info *di; -+ struct list_head *p; -+ int n = 0, size; -+ u16 dev_num; -+ -+ BT_DBG(""); -+ -+ if (get_user(dev_num, (u16 *) arg)) -+ return -EFAULT; -+ -+ if (!dev_num) -+ return -EINVAL; -+ -+ size = sizeof(*dl) + dev_num * sizeof(*di); -+ -+ if (verify_area(VERIFY_WRITE, (void *)arg, size)) -+ return -EFAULT; -+ -+ if (!(dl = kmalloc(size, GFP_KERNEL))) -+ return -ENOMEM; -+ -+ di = dl->dev_info; -+ -+ read_lock_bh(&rfcomm_dev_lock); -+ -+ list_for_each(p, &rfcomm_dev_list) { -+ struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list); -+ (di + n)->id = dev->id; -+ (di + n)->flags = dev->flags; -+ (di + n)->state = dev->dlc->state; -+ (di + n)->channel = dev->channel; -+ bacpy(&(di + n)->src, &dev->src); -+ bacpy(&(di + n)->dst, &dev->dst); -+ if (++n >= dev_num) -+ break; -+ } -+ -+ read_unlock_bh(&rfcomm_dev_lock); -+ -+ dl->dev_num = n; -+ size = sizeof(*dl) + n * sizeof(*di); -+ -+ copy_to_user((void *) arg, dl, size); -+ kfree(dl); -+ return 0; -+} -+ -+static int rfcomm_get_dev_info(unsigned long arg) -+{ -+ struct rfcomm_dev *dev; -+ struct rfcomm_dev_info di; -+ int err = 0; -+ -+ BT_DBG(""); -+ -+ if (copy_from_user(&di, (void *)arg, sizeof(di))) -+ return -EFAULT; -+ -+ if (!(dev = rfcomm_dev_get(di.id))) -+ return -ENODEV; -+ -+ di.flags = dev->flags; -+ di.channel = dev->channel; -+ di.state = dev->dlc->state; -+ bacpy(&di.src, &dev->src); -+ bacpy(&di.dst, &dev->dst); -+ -+ if (copy_to_user((void *)arg, &di, sizeof(di))) -+ err = -EFAULT; -+ -+ rfcomm_dev_put(dev); -+ return err; -+} -+ -+int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) -+{ -+ BT_DBG("cmd %d arg %ld", cmd, arg); -+ -+ switch (cmd) { -+ case RFCOMMCREATEDEV: -+ return rfcomm_create_dev(sk, arg); -+ -+ case RFCOMMRELEASEDEV: -+ return rfcomm_release_dev(arg); -+ -+ case RFCOMMGETDEVLIST: -+ return rfcomm_get_dev_list(arg); -+ -+ case RFCOMMGETDEVINFO: -+ return rfcomm_get_dev_info(arg); -+ } -+ -+ return -EINVAL; -+} -+ -+/* ---- DLC callbacks ---- */ -+static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb) -+{ -+ struct rfcomm_dev *dev = dlc->owner; -+ struct tty_struct *tty; -+ -+ if (!dev || !(tty = dev->tty)) { -+ kfree_skb(skb); -+ return; -+ } -+ -+ BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len); -+ -+ if (test_bit(TTY_DONT_FLIP, &tty->flags)) { -+ register int i; -+ for (i = 0; i < skb->len; i++) { -+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) -+ tty_flip_buffer_push(tty); -+ -+ tty_insert_flip_char(tty, skb->data[i], 0); -+ } -+ tty_flip_buffer_push(tty); -+ } else -+ tty->ldisc.receive_buf(tty, skb->data, NULL, skb->len); -+ -+ kfree_skb(skb); -+} -+ -+static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) -+{ -+ struct rfcomm_dev *dev = dlc->owner; -+ if (!dev) -+ return; -+ -+ BT_DBG("dlc %p dev %p err %d", dlc, dev, err); -+ -+ dev->err = err; -+ wake_up_interruptible(&dev->wait); -+ -+ if (dlc->state == BT_CLOSED) { -+ if (!dev->tty) { -+ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { -+ rfcomm_dev_hold(dev); -+ rfcomm_dev_del(dev); -+ -+ /* We have to drop DLC lock here, otherwise -+ * rfcomm_dev_put() will dead lock if it's the last refference */ -+ rfcomm_dlc_unlock(dlc); -+ rfcomm_dev_put(dev); -+ rfcomm_dlc_lock(dlc); -+ } -+ } else -+ tty_hangup(dev->tty); -+ } -+} -+ -+static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) -+{ -+ struct rfcomm_dev *dev = dlc->owner; -+ if (!dev) -+ return; -+ -+ BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig); -+ -+ dev->modem_status = -+ ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) | -+ ((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) | -+ ((v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0) | -+ ((v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0); -+} -+ -+/* ---- TTY functions ---- */ -+static void rfcomm_tty_wakeup(unsigned long arg) -+{ -+ struct rfcomm_dev *dev = (void *) arg; -+ struct tty_struct *tty = dev->tty; -+ if (!tty) -+ return; -+ -+ BT_DBG("dev %p tty %p", dev, tty); -+ -+ if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) -+ (tty->ldisc.write_wakeup)(tty); -+ -+ wake_up_interruptible(&tty->write_wait); -+#ifdef SERIAL_HAVE_POLL_WAIT -+ wake_up_interruptible(&tty->poll_wait); -+#endif -+} -+ -+static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) -+{ -+ DECLARE_WAITQUEUE(wait, current); -+ struct rfcomm_dev *dev; -+ struct rfcomm_dlc *dlc; -+ int err, id; -+ -+ id = MINOR(tty->device) - tty->driver.minor_start; -+ -+ BT_DBG("tty %p id %d", tty, id); -+ -+ dev = rfcomm_dev_get(id); -+ if (!dev) -+ return -ENODEV; -+ -+ BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened); -+ -+ if (dev->opened++ != 0) -+ return 0; -+ -+ dlc = dev->dlc; -+ -+ /* Attach TTY and open DLC */ -+ -+ rfcomm_dlc_lock(dlc); -+ tty->driver_data = dev; -+ dev->tty = tty; -+ rfcomm_dlc_unlock(dlc); -+ set_bit(RFCOMM_TTY_ATTACHED, &dev->flags); -+ -+ err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel); -+ if (err < 0) -+ return err; -+ -+ /* Wait for DLC to connect */ -+ add_wait_queue(&dev->wait, &wait); -+ while (1) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ -+ if (dlc->state == BT_CLOSED) { -+ err = -dev->err; -+ break; -+ } -+ -+ if (dlc->state == BT_CONNECTED) -+ break; -+ -+ if (signal_pending(current)) { -+ err = -EINTR; -+ break; -+ } -+ -+ schedule(); -+ } -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(&dev->wait, &wait); -+ -+ return err; -+} -+ -+static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) -+{ -+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; -+ if (!dev) -+ return; -+ -+ BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened); -+ -+ if (--dev->opened == 0) { -+ /* Close DLC and dettach TTY */ -+ rfcomm_dlc_close(dev->dlc, 0); -+ -+ clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); -+ tasklet_kill(&dev->wakeup_task); -+ -+ rfcomm_dlc_lock(dev->dlc); -+ tty->driver_data = NULL; -+ dev->tty = NULL; -+ rfcomm_dlc_unlock(dev->dlc); -+ } -+ -+ rfcomm_dev_put(dev); -+} -+ -+static int rfcomm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) -+{ -+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; -+ struct rfcomm_dlc *dlc = dev->dlc; -+ struct sk_buff *skb; -+ int err = 0, sent = 0, size; -+ -+ BT_DBG("tty %p from_user %d count %d", tty, from_user, count); -+ -+ while (count) { -+ size = min_t(uint, count, dlc->mtu); -+ -+ if (from_user) -+ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL); -+ else -+ skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_ATOMIC); -+ -+ if (!skb) -+ break; -+ -+ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); -+ -+ if (from_user) -+ copy_from_user(skb_put(skb, size), buf + sent, size); -+ else -+ memcpy(skb_put(skb, size), buf + sent, size); -+ -+ if ((err = rfcomm_dlc_send(dlc, skb)) < 0) { -+ kfree_skb(skb); -+ break; -+ } -+ -+ sent += size; -+ count -= size; -+ } -+ -+ return sent ? sent : err; -+} -+ -+static void rfcomm_tty_put_char(struct tty_struct *tty, unsigned char ch) -+{ -+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; -+ struct rfcomm_dlc *dlc = dev->dlc; -+ struct sk_buff *skb; -+ -+ BT_DBG("tty %p char %x", tty, ch); -+ -+ skb = rfcomm_wmalloc(dev, 1 + RFCOMM_SKB_RESERVE, 1, GFP_ATOMIC); -+ -+ if (!skb) -+ return; -+ -+ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); -+ -+ *(char *)skb_put(skb, 1) = ch; -+ -+ if ((rfcomm_dlc_send(dlc, skb)) < 0) -+ kfree_skb(skb); -+} -+ -+static int rfcomm_tty_write_room(struct tty_struct *tty) -+{ -+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; -+ int room; -+ -+ BT_DBG("tty %p", tty); -+ -+ room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc); -+ if (room < 0) -+ room = 0; -+ -+ return room; -+} -+ -+static int rfcomm_tty_set_modem_status(uint cmd, struct rfcomm_dlc *dlc, uint status) -+{ -+ u8 v24_sig, mask; -+ -+ BT_DBG("dlc %p cmd 0x%02x", dlc, cmd); -+ -+ if (cmd == TIOCMSET) -+ v24_sig = 0; -+ else -+ rfcomm_dlc_get_modem_status(dlc, &v24_sig); -+ -+ mask = ((status & TIOCM_DSR) ? RFCOMM_V24_RTC : 0) | -+ ((status & TIOCM_DTR) ? RFCOMM_V24_RTC : 0) | -+ ((status & TIOCM_RTS) ? RFCOMM_V24_RTR : 0) | -+ ((status & TIOCM_CTS) ? RFCOMM_V24_RTR : 0) | -+ ((status & TIOCM_RI) ? RFCOMM_V24_IC : 0) | -+ ((status & TIOCM_CD) ? RFCOMM_V24_DV : 0); -+ -+ if (cmd == TIOCMBIC) -+ v24_sig &= ~mask; -+ else -+ v24_sig |= mask; -+ -+ rfcomm_dlc_set_modem_status(dlc, v24_sig); -+ return 0; -+} -+ -+static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) -+{ -+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; -+ struct rfcomm_dlc *dlc = dev->dlc; -+ uint status; -+ int err; -+ -+ BT_DBG("tty %p cmd 0x%02x", tty, cmd); -+ -+ switch (cmd) { -+ case TCGETS: -+ BT_DBG("TCGETS is not supported"); -+ return -ENOIOCTLCMD; -+ -+ case TCSETS: -+ BT_DBG("TCSETS is not supported"); -+ return -ENOIOCTLCMD; -+ -+ case TIOCMGET: -+ BT_DBG("TIOCMGET"); -+ -+ return put_user(dev->modem_status, (unsigned int *)arg); -+ -+ case TIOCMSET: /* Turns on and off the lines as specified by the mask */ -+ case TIOCMBIS: /* Turns on the lines as specified by the mask */ -+ case TIOCMBIC: /* Turns off the lines as specified by the mask */ -+ if ((err = get_user(status, (unsigned int *)arg))) -+ return err; -+ return rfcomm_tty_set_modem_status(cmd, dlc, status); -+ -+ case TIOCMIWAIT: -+ BT_DBG("TIOCMIWAIT"); -+ break; -+ -+ case TIOCGICOUNT: -+ BT_DBG("TIOCGICOUNT"); -+ break; -+ -+ case TIOCGSERIAL: -+ BT_ERR("TIOCGSERIAL is not supported"); -+ return -ENOIOCTLCMD; -+ -+ case TIOCSSERIAL: -+ BT_ERR("TIOCSSERIAL is not supported"); -+ return -ENOIOCTLCMD; -+ -+ case TIOCSERGSTRUCT: -+ BT_ERR("TIOCSERGSTRUCT is not supported"); -+ return -ENOIOCTLCMD; -+ -+ case TIOCSERGETLSR: -+ BT_ERR("TIOCSERGETLSR is not supported"); -+ return -ENOIOCTLCMD; -+ -+ case TIOCSERCONFIG: -+ BT_ERR("TIOCSERCONFIG is not supported"); -+ return -ENOIOCTLCMD; -+ -+ default: -+ return -ENOIOCTLCMD; /* ioctls which we must ignore */ -+ -+ } -+ -+ return -ENOIOCTLCMD; -+} -+ -+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) -+ -+static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old) -+{ -+ BT_DBG("tty %p", tty); -+ -+ if ((tty->termios->c_cflag == old->c_cflag) && -+ (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag))) -+ return; -+ -+ /* handle turning off CRTSCTS */ -+ if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { -+ BT_DBG("turning off CRTSCTS"); -+ } -+} -+ -+static void rfcomm_tty_throttle(struct tty_struct *tty) -+{ -+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; -+ -+ BT_DBG("tty %p dev %p", tty, dev); -+ -+ rfcomm_dlc_throttle(dev->dlc); -+} -+ -+static void rfcomm_tty_unthrottle(struct tty_struct *tty) -+{ -+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; -+ -+ BT_DBG("tty %p dev %p", tty, dev); -+ -+ rfcomm_dlc_unthrottle(dev->dlc); -+} -+ -+static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty) -+{ -+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; -+ struct rfcomm_dlc *dlc = dev->dlc; -+ -+ BT_DBG("tty %p dev %p", tty, dev); -+ -+ if (skb_queue_len(&dlc->tx_queue)) -+ return dlc->mtu; -+ -+ return 0; -+} -+ -+static void rfcomm_tty_flush_buffer(struct tty_struct *tty) -+{ -+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; -+ if (!dev) -+ return; -+ -+ BT_DBG("tty %p dev %p", tty, dev); -+ -+ skb_queue_purge(&dev->dlc->tx_queue); -+ -+ if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) -+ tty->ldisc.write_wakeup(tty); -+} -+ -+static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch) -+{ -+ BT_DBG("tty %p ch %c", tty, ch); -+} -+ -+static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) -+{ -+ BT_DBG("tty %p timeout %d", tty, timeout); -+} -+ -+static void rfcomm_tty_hangup(struct tty_struct *tty) -+{ -+ struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; -+ if (!dev) -+ return; -+ -+ BT_DBG("tty %p dev %p", tty, dev); -+ -+ rfcomm_tty_flush_buffer(tty); -+ -+ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) -+ rfcomm_dev_del(dev); -+} -+ -+static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused) -+{ -+ return 0; -+} -+ -+/* ---- TTY structure ---- */ -+static int rfcomm_tty_refcount; /* If we manage several devices */ -+ -+static struct tty_struct *rfcomm_tty_table[RFCOMM_TTY_PORTS]; -+static struct termios *rfcomm_tty_termios[RFCOMM_TTY_PORTS]; -+static struct termios *rfcomm_tty_termios_locked[RFCOMM_TTY_PORTS]; -+ -+static struct tty_driver rfcomm_tty_driver = { -+ magic: TTY_DRIVER_MAGIC, -+ driver_name: "rfcomm", -+#ifdef CONFIG_DEVFS_FS -+ name: "bluetooth/rfcomm/%d", -+#else -+ name: "rfcomm", -+#endif -+ major: RFCOMM_TTY_MAJOR, -+ minor_start: RFCOMM_TTY_MINOR, -+ num: RFCOMM_TTY_PORTS, -+ type: TTY_DRIVER_TYPE_SERIAL, -+ subtype: SERIAL_TYPE_NORMAL, -+ flags: TTY_DRIVER_REAL_RAW, -+ -+ refcount: &rfcomm_tty_refcount, -+ table: rfcomm_tty_table, -+ termios: rfcomm_tty_termios, -+ termios_locked: rfcomm_tty_termios_locked, -+ -+ open: rfcomm_tty_open, -+ close: rfcomm_tty_close, -+ put_char: rfcomm_tty_put_char, -+ write: rfcomm_tty_write, -+ write_room: rfcomm_tty_write_room, -+ chars_in_buffer: rfcomm_tty_chars_in_buffer, -+ flush_buffer: rfcomm_tty_flush_buffer, -+ ioctl: rfcomm_tty_ioctl, -+ throttle: rfcomm_tty_throttle, -+ unthrottle: rfcomm_tty_unthrottle, -+ set_termios: rfcomm_tty_set_termios, -+ send_xchar: rfcomm_tty_send_xchar, -+ stop: NULL, -+ start: NULL, -+ hangup: rfcomm_tty_hangup, -+ wait_until_sent: rfcomm_tty_wait_until_sent, -+ read_proc: rfcomm_tty_read_proc, -+}; -+ -+int rfcomm_init_ttys(void) -+{ -+ int i; -+ -+ /* Initalize our global data */ -+ for (i = 0; i < RFCOMM_TTY_PORTS; i++) -+ rfcomm_tty_table[i] = NULL; -+ -+ /* Register the TTY driver */ -+ rfcomm_tty_driver.init_termios = tty_std_termios; -+ rfcomm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; -+ rfcomm_tty_driver.flags = TTY_DRIVER_REAL_RAW; -+ -+ if (tty_register_driver(&rfcomm_tty_driver)) { -+ BT_ERR("Can't register RFCOMM TTY driver"); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+void rfcomm_cleanup_ttys(void) -+{ -+ tty_unregister_driver(&rfcomm_tty_driver); -+ return; -+} ---- /dev/null 1970-01-01 01:00:00.000000000 +0100 -+++ linux/net/bluetooth/sco.c 2004-01-25 23:37:39.000000000 +0100 -@@ -0,0 +1,1019 @@ -+/* -+ BlueZ - Bluetooth protocol stack for Linux -+ Copyright (C) 2000-2001 Qualcomm Incorporated -+ -+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License version 2 as -+ published by the Free Software Foundation; -+ -+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. -+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY -+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES -+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, -+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS -+ SOFTWARE IS DISCLAIMED. -+*/ -+ -+/* -+ * BlueZ SCO sockets. -+ * -+ * $Id: sco.c,v 1.4 2002/07/22 20:32:54 maxk Exp $ -+ */ -+#define VERSION "0.3" -+ -+#include <linux/config.h> -+#include <linux/module.h> -+ -+#include <linux/types.h> -+#include <linux/errno.h> -+#include <linux/kernel.h> -+#include <linux/major.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/poll.h> -+#include <linux/fcntl.h> -+#include <linux/init.h> -+#include <linux/skbuff.h> -+#include <linux/interrupt.h> -+#include <linux/socket.h> -+#include <linux/skbuff.h> -+#include <linux/proc_fs.h> -+#include <linux/list.h> -+#include <net/sock.h> -+ -+#include <asm/system.h> -+#include <asm/uaccess.h> -+ -+#include <net/bluetooth/bluetooth.h> -+#include <net/bluetooth/hci_core.h> -+#include <net/bluetooth/sco.h> -+ -+#ifndef SCO_DEBUG -+#undef BT_DBG -+#define BT_DBG( A... ) -+#endif -+ -+static struct proto_ops sco_sock_ops; -+ -+static struct bluez_sock_list sco_sk_list = { -+ lock: RW_LOCK_UNLOCKED -+}; -+ -+static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); -+static void sco_chan_del(struct sock *sk, int err); -+static inline struct sock * sco_chan_get(struct sco_conn *conn); -+ -+static int sco_conn_del(struct hci_conn *conn, int err); -+ -+static void sco_sock_close(struct sock *sk); -+static void sco_sock_kill(struct sock *sk); -+ -+/* ----- SCO timers ------ */ -+static void sco_sock_timeout(unsigned long arg) -+{ -+ struct sock *sk = (struct sock *) arg; -+ -+ BT_DBG("sock %p state %d", sk, sk->state); -+ -+ bh_lock_sock(sk); -+ sk->err = ETIMEDOUT; -+ sk->state_change(sk); -+ bh_unlock_sock(sk); -+ -+ sco_sock_kill(sk); -+ sock_put(sk); -+} -+ -+static void sco_sock_set_timer(struct sock *sk, long timeout) -+{ -+ BT_DBG("sock %p state %d timeout %ld", sk, sk->state, timeout); -+ -+ if (!mod_timer(&sk->timer, jiffies + timeout)) -+ sock_hold(sk); -+} -+ -+static void sco_sock_clear_timer(struct sock *sk) -+{ -+ BT_DBG("sock %p state %d", sk, sk->state); -+ -+ if (timer_pending(&sk->timer) && del_timer(&sk->timer)) -+ __sock_put(sk); -+} -+ -+static void sco_sock_init_timer(struct sock *sk) -+{ -+ init_timer(&sk->timer); -+ sk->timer.function = sco_sock_timeout; -+ sk->timer.data = (unsigned long)sk; -+} -+ -+/* -------- SCO connections --------- */ -+static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status) -+{ -+ struct hci_dev *hdev = hcon->hdev; -+ struct sco_conn *conn; -+ -+ if ((conn = hcon->sco_data)) -+ return conn; -+ -+ if (status) -+ return conn; -+ -+ if (!(conn = kmalloc(sizeof(struct sco_conn), GFP_ATOMIC))) -+ return NULL; -+ memset(conn, 0, sizeof(struct sco_conn)); -+ -+ spin_lock_init(&conn->lock); -+ -+ hcon->sco_data = conn; -+ conn->hcon = hcon; -+ -+ conn->src = &hdev->bdaddr; -+ conn->dst = &hcon->dst; -+ -+ if (hdev->sco_mtu > 0) -+ conn->mtu = hdev->sco_mtu; -+ else -+ conn->mtu = 60; -+ -+ BT_DBG("hcon %p conn %p", hcon, conn); -+ -+ MOD_INC_USE_COUNT; -+ return conn; -+} -+ -+static int sco_conn_del(struct hci_conn *hcon, int err) -+{ -+ struct sco_conn *conn; -+ struct sock *sk; -+ -+ if (!(conn = hcon->sco_data)) -+ return 0; -+ -+ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); -+ -+ /* Kill socket */ -+ if ((sk = sco_chan_get(conn))) { -+ bh_lock_sock(sk); -+ sco_sock_clear_timer(sk); -+ sco_chan_del(sk, err); -+ bh_unlock_sock(sk); -+ sco_sock_kill(sk); -+ } -+ -+ hcon->sco_data = NULL; -+ kfree(conn); -+ -+ MOD_DEC_USE_COUNT; -+ return 0; -+} -+ -+int sco_connect(struct sock *sk) -+{ -+ bdaddr_t *src = &bluez_pi(sk)->src; -+ bdaddr_t *dst = &bluez_pi(sk)->dst; -+ struct sco_conn *conn; -+ struct hci_conn *hcon; -+ struct hci_dev *hdev; -+ int err = 0; -+ -+ BT_DBG("%s -> %s", batostr(src), batostr(dst)); -+ -+ if (!(hdev = hci_get_route(dst, src))) -+ return -EHOSTUNREACH; -+ -+ hci_dev_lock_bh(hdev); -+ -+ err = -ENOMEM; -+ -+ hcon = hci_connect(hdev, SCO_LINK, dst); -+ if (!hcon) -+ goto done; -+ -+ conn = sco_conn_add(hcon, 0); -+ if (!conn) { -+ hci_conn_put(hcon); -+ goto done; -+ } -+ -+ /* Update source addr of the socket */ -+ bacpy(src, conn->src); -+ -+ err = sco_chan_add(conn, sk, NULL); -+ if (err) -+ goto done; -+ -+ if (hcon->state == BT_CONNECTED) { -+ sco_sock_clear_timer(sk); -+ sk->state = BT_CONNECTED; -+ } else { -+ sk->state = BT_CONNECT; -+ sco_sock_set_timer(sk, sk->sndtimeo); -+ } -+done: -+ hci_dev_unlock_bh(hdev); -+ hci_dev_put(hdev); -+ return err; -+} -+ -+static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len) -+{ -+ struct sco_conn *conn = sco_pi(sk)->conn; -+ struct sk_buff *skb; -+ int err, count; -+ -+ /* Check outgoing MTU */ -+ if (len > conn->mtu) -+ return -EINVAL; -+ -+ BT_DBG("sk %p len %d", sk, len); -+ -+ count = MIN(conn->mtu, len); -+ if (!(skb = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err))) -+ return err; -+ -+ if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { -+ err = -EFAULT; -+ goto fail; -+ } -+ -+ if ((err = hci_send_sco(conn->hcon, skb)) < 0) -+ goto fail; -+ -+ return count; -+ -+fail: -+ kfree_skb(skb); -+ return err; -+} -+ -+static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb) -+{ -+ struct sock *sk = sco_chan_get(conn); -+ -+ if (!sk) -+ goto drop; -+ -+ BT_DBG("sk %p len %d", sk, skb->len); -+ -+ if (sk->state != BT_CONNECTED) -+ goto drop; -+ -+ if (!sock_queue_rcv_skb(sk, skb)) -+ return; -+ -+drop: -+ kfree_skb(skb); -+ return; -+} -+ -+/* -------- Socket interface ---------- */ -+static struct sock *__sco_get_sock_by_addr(bdaddr_t *ba) -+{ -+ struct sock *sk; -+ -+ for (sk = sco_sk_list.head; sk; sk = sk->next) { -+ if (!bacmp(&bluez_pi(sk)->src, ba)) -+ break; -+ } -+ -+ return sk; -+} -+ -+/* Find socket listening on source bdaddr. -+ * Returns closest match. -+ */ -+static struct sock *sco_get_sock_listen(bdaddr_t *src) -+{ -+ struct sock *sk, *sk1 = NULL; -+ -+ read_lock(&sco_sk_list.lock); -+ -+ for (sk = sco_sk_list.head; sk; sk = sk->next) { -+ if (sk->state != BT_LISTEN) -+ continue; -+ -+ /* Exact match. */ -+ if (!bacmp(&bluez_pi(sk)->src, src)) -+ break; -+ -+ /* Closest match */ -+ if (!bacmp(&bluez_pi(sk)->src, BDADDR_ANY)) -+ sk1 = sk; -+ } -+ -+ read_unlock(&sco_sk_list.lock); -+ -+ return sk ? sk : sk1; -+} -+ -+static void sco_sock_destruct(struct sock *sk) -+{ -+ BT_DBG("sk %p", sk); -+ -+ skb_queue_purge(&sk->receive_queue); -+ skb_queue_purge(&sk->write_queue); -+ -+ MOD_DEC_USE_COUNT; -+} -+ -+static void sco_sock_cleanup_listen(struct sock *parent) -+{ -+ struct sock *sk; -+ -+ BT_DBG("parent %p", parent); -+ -+ /* Close not yet accepted channels */ -+ while ((sk = bluez_accept_dequeue(parent, NULL))) { -+ sco_sock_close(sk); -+ sco_sock_kill(sk); -+ } -+ -+ parent->state = BT_CLOSED; -+ parent->zapped = 1; -+} -+ -+/* Kill socket (only if zapped and orphan) -+ * Must be called on unlocked socket. -+ */ -+static void sco_sock_kill(struct sock *sk) -+{ -+ if (!sk->zapped || sk->socket) -+ return; -+ -+ BT_DBG("sk %p state %d", sk, sk->state); -+ -+ /* Kill poor orphan */ -+ bluez_sock_unlink(&sco_sk_list, sk); -+ sk->dead = 1; -+ sock_put(sk); -+} -+ -+/* Close socket. -+ * Must be called on unlocked socket. -+ */ -+static void sco_sock_close(struct sock *sk) -+{ -+ struct sco_conn *conn; -+ -+ sco_sock_clear_timer(sk); -+ -+ lock_sock(sk); -+ -+ conn = sco_pi(sk)->conn; -+ -+ BT_DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket); -+ -+ switch (sk->state) { -+ case BT_LISTEN: -+ sco_sock_cleanup_listen(sk); -+ break; -+ -+ case BT_CONNECTED: -+ case BT_CONFIG: -+ case BT_CONNECT: -+ case BT_DISCONN: -+ sco_chan_del(sk, ECONNRESET); -+ break; -+ -+ default: -+ sk->zapped = 1; -+ break; -+ }; -+ -+ release_sock(sk); -+} -+ -+static void sco_sock_init(struct sock *sk, struct sock *parent) -+{ -+ BT_DBG("sk %p", sk); -+ -+ if (parent) -+ sk->type = parent->type; -+} -+ -+static struct sock *sco_sock_alloc(struct socket *sock, int proto, int prio) -+{ -+ struct sock *sk; -+ -+ if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1))) -+ return NULL; -+ -+ bluez_sock_init(sock, sk); -+ -+ sk->zapped = 0; -+ -+ sk->destruct = sco_sock_destruct; -+ sk->sndtimeo = SCO_CONN_TIMEOUT; -+ -+ sk->protocol = proto; -+ sk->state = BT_OPEN; -+ -+ sco_sock_init_timer(sk); -+ -+ bluez_sock_link(&sco_sk_list, sk); -+ -+ MOD_INC_USE_COUNT; -+ return sk; -+} -+ -+static int sco_sock_create(struct socket *sock, int protocol) -+{ -+ struct sock *sk; -+ -+ BT_DBG("sock %p", sock); -+ -+ sock->state = SS_UNCONNECTED; -+ -+ if (sock->type != SOCK_SEQPACKET) -+ return -ESOCKTNOSUPPORT; -+ -+ sock->ops = &sco_sock_ops; -+ -+ if (!(sk = sco_sock_alloc(sock, protocol, GFP_KERNEL))) -+ return -ENOMEM; -+ -+ sco_sock_init(sk, NULL); -+ return 0; -+} -+ -+static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) -+{ -+ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; -+ struct sock *sk = sock->sk; -+ bdaddr_t *src = &sa->sco_bdaddr; -+ int err = 0; -+ -+ BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr)); -+ -+ if (!addr || addr->sa_family != AF_BLUETOOTH) -+ return -EINVAL; -+ -+ lock_sock(sk); -+ -+ if (sk->state != BT_OPEN) { -+ err = -EBADFD; -+ goto done; -+ } -+ -+ write_lock_bh(&sco_sk_list.lock); -+ -+ if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) { -+ err = -EADDRINUSE; -+ } else { -+ /* Save source address */ -+ bacpy(&bluez_pi(sk)->src, &sa->sco_bdaddr); -+ sk->state = BT_BOUND; -+ } -+ -+ write_unlock_bh(&sco_sk_list.lock); -+ -+done: -+ release_sock(sk); -+ -+ return err; -+} -+ -+static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) -+{ -+ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; -+ struct sock *sk = sock->sk; -+ int err = 0; -+ -+ -+ BT_DBG("sk %p", sk); -+ -+ if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_sco)) -+ return -EINVAL; -+ -+ if (sk->state != BT_OPEN && sk->state != BT_BOUND) -+ return -EBADFD; -+ -+ if (sk->type != SOCK_SEQPACKET) -+ return -EINVAL; -+ -+ lock_sock(sk); -+ -+ /* Set destination address and psm */ -+ bacpy(&bluez_pi(sk)->dst, &sa->sco_bdaddr); -+ -+ if ((err = sco_connect(sk))) -+ goto done; -+ -+ err = bluez_sock_wait_state(sk, BT_CONNECTED, -+ sock_sndtimeo(sk, flags & O_NONBLOCK)); -+ -+done: -+ release_sock(sk); -+ return err; -+} -+ -+int sco_sock_listen(struct socket *sock, int backlog) -+{ -+ struct sock *sk = sock->sk; -+ int err = 0; -+ -+ BT_DBG("sk %p backlog %d", sk, backlog); -+ -+ lock_sock(sk); -+ -+ if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) { -+ err = -EBADFD; -+ goto done; -+ } -+ -+ sk->max_ack_backlog = backlog; -+ sk->ack_backlog = 0; -+ sk->state = BT_LISTEN; -+ -+done: -+ release_sock(sk); -+ return err; -+} -+ -+int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags) -+{ -+ DECLARE_WAITQUEUE(wait, current); -+ struct sock *sk = sock->sk, *ch; -+ long timeo; -+ int err = 0; -+ -+ lock_sock(sk); -+ -+ if (sk->state != BT_LISTEN) { -+ err = -EBADFD; -+ goto done; -+ } -+ -+ timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); -+ -+ BT_DBG("sk %p timeo %ld", sk, timeo); -+ -+ /* Wait for an incoming connection. (wake-one). */ -+ add_wait_queue_exclusive(sk->sleep, &wait); -+ while (!(ch = bluez_accept_dequeue(sk, newsock))) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ if (!timeo) { -+ err = -EAGAIN; -+ break; -+ } -+ -+ release_sock(sk); -+ timeo = schedule_timeout(timeo); -+ lock_sock(sk); -+ -+ if (sk->state != BT_LISTEN) { -+ err = -EBADFD; -+ break; -+ } -+ -+ if (signal_pending(current)) { -+ err = sock_intr_errno(timeo); -+ break; -+ } -+ } -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(sk->sleep, &wait); -+ -+ if (err) -+ goto done; -+ -+ newsock->state = SS_CONNECTED; -+ -+ BT_DBG("new socket %p", ch); -+ -+done: -+ release_sock(sk); -+ return err; -+} -+ -+static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) -+{ -+ struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; -+ struct sock *sk = sock->sk; -+ -+ BT_DBG("sock %p, sk %p", sock, sk); -+ -+ addr->sa_family = AF_BLUETOOTH; -+ *len = sizeof(struct sockaddr_sco); -+ -+ if (peer) -+ bacpy(&sa->sco_bdaddr, &bluez_pi(sk)->dst); -+ else -+ bacpy(&sa->sco_bdaddr, &bluez_pi(sk)->src); -+ -+ return 0; -+} -+ -+static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) -+{ -+ struct sock *sk = sock->sk; -+ int err = 0; -+ -+ BT_DBG("sock %p, sk %p", sock, sk); -+ -+ if (sk->err) -+ return sock_error(sk); -+ -+ if (msg->msg_flags & MSG_OOB) -+ return -EOPNOTSUPP; -+ -+ lock_sock(sk); -+ -+ if (sk->state == BT_CONNECTED) -+ err = sco_send_frame(sk, msg, len); -+ else -+ err = -ENOTCONN; -+ -+ release_sock(sk); -+ return err; -+} -+ -+int sco_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) -+{ -+ struct sock *sk = sock->sk; -+ int err = 0; -+ -+ BT_DBG("sk %p", sk); -+ -+ lock_sock(sk); -+ -+ switch (optname) { -+ default: -+ err = -ENOPROTOOPT; -+ break; -+ }; -+ -+ release_sock(sk); -+ return err; -+} -+ -+int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) -+{ -+ struct sock *sk = sock->sk; -+ struct sco_options opts; -+ struct sco_conninfo cinfo; -+ int len, err = 0; -+ -+ BT_DBG("sk %p", sk); -+ -+ if (get_user(len, optlen)) -+ return -EFAULT; -+ -+ lock_sock(sk); -+ -+ switch (optname) { -+ case SCO_OPTIONS: -+ if (sk->state != BT_CONNECTED) { -+ err = -ENOTCONN; -+ break; -+ } -+ -+ opts.mtu = sco_pi(sk)->conn->mtu; -+ -+ BT_DBG("mtu %d", opts.mtu); -+ -+ len = MIN(len, sizeof(opts)); -+ if (copy_to_user(optval, (char *)&opts, len)) -+ err = -EFAULT; -+ -+ break; -+ -+ case SCO_CONNINFO: -+ if (sk->state != BT_CONNECTED) { -+ err = -ENOTCONN; -+ break; -+ } -+ -+ cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle; -+ -+ len = MIN(len, sizeof(cinfo)); -+ if (copy_to_user(optval, (char *)&cinfo, len)) -+ err = -EFAULT; -+ -+ break; -+ -+ default: -+ err = -ENOPROTOOPT; -+ break; -+ }; -+ -+ release_sock(sk); -+ return err; -+} -+ -+static int sco_sock_release(struct socket *sock) -+{ -+ struct sock *sk = sock->sk; -+ int err = 0; -+ -+ BT_DBG("sock %p, sk %p", sock, sk); -+ -+ if (!sk) -+ return 0; -+ -+ sco_sock_close(sk); -+ if (sk->linger) { -+ lock_sock(sk); -+ err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime); -+ release_sock(sk); -+ } -+ -+ sock_orphan(sk); -+ sco_sock_kill(sk); -+ return err; -+} -+ -+static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) -+{ -+ BT_DBG("conn %p", conn); -+ -+ sco_pi(sk)->conn = conn; -+ conn->sk = sk; -+ -+ if (parent) -+ bluez_accept_enqueue(parent, sk); -+} -+ -+static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) -+{ -+ int err = 0; -+ -+ sco_conn_lock(conn); -+ if (conn->sk) { -+ err = -EBUSY; -+ } else { -+ __sco_chan_add(conn, sk, parent); -+ } -+ sco_conn_unlock(conn); -+ return err; -+} -+ -+static inline struct sock * sco_chan_get(struct sco_conn *conn) -+{ -+ struct sock *sk = NULL; -+ sco_conn_lock(conn); -+ sk = conn->sk; -+ sco_conn_unlock(conn); -+ return sk; -+} -+ -+/* Delete channel. -+ * Must be called on the locked socket. */ -+static void sco_chan_del(struct sock *sk, int err) -+{ -+ struct sco_conn *conn; -+ -+ conn = sco_pi(sk)->conn; -+ -+ BT_DBG("sk %p, conn %p, err %d", sk, conn, err); -+ -+ if (conn) { -+ sco_conn_lock(conn); -+ conn->sk = NULL; -+ sco_pi(sk)->conn = NULL; -+ sco_conn_unlock(conn); -+ hci_conn_put(conn->hcon); -+ } -+ -+ sk->state = BT_CLOSED; -+ sk->err = err; -+ sk->state_change(sk); -+ -+ sk->zapped = 1; -+} -+ -+static void sco_conn_ready(struct sco_conn *conn) -+{ -+ struct sock *parent, *sk; -+ -+ BT_DBG("conn %p", conn); -+ -+ sco_conn_lock(conn); -+ -+ if ((sk = conn->sk)) { -+ sco_sock_clear_timer(sk); -+ bh_lock_sock(sk); -+ sk->state = BT_CONNECTED; -+ sk->state_change(sk); -+ bh_unlock_sock(sk); -+ } else { -+ parent = sco_get_sock_listen(conn->src); -+ if (!parent) -+ goto done; -+ -+ bh_lock_sock(parent); -+ -+ sk = sco_sock_alloc(NULL, BTPROTO_SCO, GFP_ATOMIC); -+ if (!sk) { -+ bh_unlock_sock(parent); -+ goto done; -+ } -+ -+ sco_sock_init(sk, parent); -+ -+ bacpy(&bluez_pi(sk)->src, conn->src); -+ bacpy(&bluez_pi(sk)->dst, conn->dst); -+ -+ hci_conn_hold(conn->hcon); -+ __sco_chan_add(conn, sk, parent); -+ -+ sk->state = BT_CONNECTED; -+ -+ /* Wake up parent */ -+ parent->data_ready(parent, 1); -+ -+ bh_unlock_sock(parent); -+ } -+ -+done: -+ sco_conn_unlock(conn); -+} -+ -+/* ----- SCO interface with lower layer (HCI) ----- */ -+int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type) -+{ -+ BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); -+ -+ /* Always accept connection */ -+ return HCI_LM_ACCEPT; -+} -+ -+int sco_connect_cfm(struct hci_conn *hcon, __u8 status) -+{ -+ BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); -+ -+ if (hcon->type != SCO_LINK) -+ return 0; -+ -+ if (!status) { -+ struct sco_conn *conn; -+ -+ conn = sco_conn_add(hcon, status); -+ if (conn) -+ sco_conn_ready(conn); -+ } else -+ sco_conn_del(hcon, bterr(status)); -+ -+ return 0; -+} -+ -+int sco_disconn_ind(struct hci_conn *hcon, __u8 reason) -+{ -+ BT_DBG("hcon %p reason %d", hcon, reason); -+ -+ if (hcon->type != SCO_LINK) -+ return 0; -+ -+ sco_conn_del(hcon, bterr(reason)); -+ return 0; -+} -+ -+int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb) -+{ -+ struct sco_conn *conn = hcon->sco_data; -+ -+ if (!conn) -+ goto drop; -+ -+ BT_DBG("conn %p len %d", conn, skb->len); -+ -+ if (skb->len) { -+ sco_recv_frame(conn, skb); -+ return 0; -+ } -+ -+drop: -+ kfree_skb(skb); -+ return 0; -+} -+ -+/* ----- Proc fs support ------ */ -+static int sco_sock_dump(char *buf, struct bluez_sock_list *list) -+{ -+ struct sco_pinfo *pi; -+ struct sock *sk; -+ char *ptr = buf; -+ -+ write_lock_bh(&list->lock); -+ -+ for (sk = list->head; sk; sk = sk->next) { -+ pi = sco_pi(sk); -+ ptr += sprintf(ptr, "%s %s %d\n", -+ batostr(&bluez_pi(sk)->src), batostr(&bluez_pi(sk)->dst), -+ sk->state); -+ } -+ -+ write_unlock_bh(&list->lock); -+ -+ ptr += sprintf(ptr, "\n"); -+ -+ return ptr - buf; -+} -+ -+static int sco_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv) -+{ -+ char *ptr = buf; -+ int len; -+ -+ BT_DBG("count %d, offset %ld", count, offset); -+ -+ ptr += sco_sock_dump(ptr, &sco_sk_list); -+ len = ptr - buf; -+ -+ if (len <= count + offset) -+ *eof = 1; -+ -+ *start = buf + offset; -+ len -= offset; -+ -+ if (len > count) -+ len = count; -+ if (len < 0) -+ len = 0; -+ -+ return len; -+} -+ -+static struct proto_ops sco_sock_ops = { -+ family: PF_BLUETOOTH, -+ release: sco_sock_release, -+ bind: sco_sock_bind, -+ connect: sco_sock_connect, -+ listen: sco_sock_listen, -+ accept: sco_sock_accept, -+ getname: sco_sock_getname, -+ sendmsg: sco_sock_sendmsg, -+ recvmsg: bluez_sock_recvmsg, -+ poll: bluez_sock_poll, -+ socketpair: sock_no_socketpair, -+ ioctl: sock_no_ioctl, -+ shutdown: sock_no_shutdown, -+ setsockopt: sco_sock_setsockopt, -+ getsockopt: sco_sock_getsockopt, -+ mmap: sock_no_mmap -+}; -+ -+static struct net_proto_family sco_sock_family_ops = { -+ family: PF_BLUETOOTH, -+ create: sco_sock_create -+}; -+ -+static struct hci_proto sco_hci_proto = { -+ name: "SCO", -+ id: HCI_PROTO_SCO, -+ connect_ind: sco_connect_ind, -+ connect_cfm: sco_connect_cfm, -+ disconn_ind: sco_disconn_ind, -+ recv_scodata: sco_recv_scodata, -+}; -+ -+int __init sco_init(void) -+{ -+ int err; -+ -+ if ((err = bluez_sock_register(BTPROTO_SCO, &sco_sock_family_ops))) { -+ BT_ERR("Can't register SCO socket layer"); -+ return err; -+ } -+ -+ if ((err = hci_register_proto(&sco_hci_proto))) { -+ BT_ERR("Can't register SCO protocol"); -+ return err; -+ } -+ -+ create_proc_read_entry("bluetooth/sco", 0, 0, sco_read_proc, NULL); -+ -+ BT_INFO("BlueZ SCO ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION); -+ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); -+ return 0; -+} -+ -+void sco_cleanup(void) -+{ -+ int err; -+ -+ remove_proc_entry("bluetooth/sco", NULL); -+ -+ /* Unregister socket, protocol and notifier */ -+ if ((err = bluez_sock_unregister(BTPROTO_SCO))) -+ BT_ERR("Can't unregister SCO socket layer %d", err); -+ -+ if ((err = hci_unregister_proto(&sco_hci_proto))) -+ BT_ERR("Can't unregister SCO protocol %d", err); -+} -+ -+module_init(sco_init); -+module_exit(sco_cleanup); -+ -+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); -+MODULE_DESCRIPTION("BlueZ SCO ver " VERSION); -+MODULE_LICENSE("GPL"); ---- linux/net/bluetooth/syms.c~bluetooth-2.4.18-mh11 2001-09-07 18:28:38.000000000 +0200 -+++ linux/net/bluetooth/syms.c 2004-01-25 23:37:39.000000000 +0100 -@@ -25,7 +25,7 @@ - /* - * BlueZ symbols. - * -- * $Id: syms.c,v 1.1 2001/07/12 19:31:24 maxk Exp $ -+ * $Id: syms.c,v 1.1 2002/03/08 21:06:59 maxk Exp $ - */ - - #include <linux/config.h> -@@ -39,25 +39,28 @@ - #include <linux/socket.h> - - #include <net/bluetooth/bluetooth.h> --#include <net/bluetooth/bluez.h> - #include <net/bluetooth/hci_core.h> - - /* HCI Core */ - EXPORT_SYMBOL(hci_register_dev); - EXPORT_SYMBOL(hci_unregister_dev); -+EXPORT_SYMBOL(hci_suspend_dev); -+EXPORT_SYMBOL(hci_resume_dev); -+ - EXPORT_SYMBOL(hci_register_proto); - EXPORT_SYMBOL(hci_unregister_proto); --EXPORT_SYMBOL(hci_register_notifier); --EXPORT_SYMBOL(hci_unregister_notifier); - -+EXPORT_SYMBOL(hci_get_route); - EXPORT_SYMBOL(hci_connect); --EXPORT_SYMBOL(hci_disconnect); - EXPORT_SYMBOL(hci_dev_get); -+EXPORT_SYMBOL(hci_conn_auth); -+EXPORT_SYMBOL(hci_conn_encrypt); - - EXPORT_SYMBOL(hci_recv_frame); - EXPORT_SYMBOL(hci_send_acl); - EXPORT_SYMBOL(hci_send_sco); --EXPORT_SYMBOL(hci_send_raw); -+EXPORT_SYMBOL(hci_send_cmd); -+EXPORT_SYMBOL(hci_si_event); - - /* BlueZ lib */ - EXPORT_SYMBOL(bluez_dump); -@@ -68,5 +71,11 @@ - /* BlueZ sockets */ - EXPORT_SYMBOL(bluez_sock_register); - EXPORT_SYMBOL(bluez_sock_unregister); -+EXPORT_SYMBOL(bluez_sock_init); - EXPORT_SYMBOL(bluez_sock_link); - EXPORT_SYMBOL(bluez_sock_unlink); -+EXPORT_SYMBOL(bluez_sock_recvmsg); -+EXPORT_SYMBOL(bluez_sock_poll); -+EXPORT_SYMBOL(bluez_accept_enqueue); -+EXPORT_SYMBOL(bluez_accept_dequeue); -+EXPORT_SYMBOL(bluez_sock_wait_state); |