diff options
author | Chris Larson <clarson@kergoth.com> | 2004-11-09 00:36:47 +0000 |
---|---|---|
committer | Chris Larson <clarson@kergoth.com> | 2004-11-09 00:36:47 +0000 |
commit | f96441b9faf769c9ecdd4d338b605ea3d0cc4010 (patch) | |
tree | edb17ec2c4ea13c5acb1c7350957a249a820e28d /linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107 | |
parent | b6588aa6851fb220cedc387d21c51513ef8d67f4 (diff) |
Disable bk EOLN_NATIVE conversions on all files in packages FILESPATHs, to prevent it screwing up patches.
BKrev: 4190111fA4MuVozAqwE7xOSL9fr-TA
Diffstat (limited to 'linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107')
20 files changed, 41816 insertions, 0 deletions
diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluecard_cs.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluecard_cs.patch index e69de29bb2..eacada33f5 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluecard_cs.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluecard_cs.patch @@ -0,0 +1,11 @@ +--- linux-orig/drivers/bluetooth/bluecard_cs.c 2004-02-16 10:51:46.000000000 +0300 ++++ linux/drivers/bluetooth/bluecard_cs.c 2004-02-17 03:45:31.000000000 +0300 +@@ -102,7 +102,7 @@ + + + /* Default baud rate: 57600, 115200, 230400 or 460800 */ +-#define DEFAULT_BAUD_RATE 230400 ++#define DEFAULT_BAUD_RATE 460800 + + + /* Hardware states */ diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh11.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh11.patch index e69de29bb2..f2ea0d699b 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh11.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bluetooth-2.4.18-mh11.patch @@ -0,0 +1,31393 @@ + +# +# 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$ ++ */ ++ ++#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$ ++ */ ++ ++#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$ ++ */ ++#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$ ++ */ ++ ++#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$ ++ */ ++#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$ +- */ +-#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$ ++ */ ++ ++#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$ ++ * $Id$ + */ +-#define VERSION "1.0" ++#define VERSION "2.4" + + #include <linux/config.h> + #include <linux/module.h> + + #include <linux/version.h> +-#include <linux/config.h> + #include <linux/kernel.h> + #include <linux/init.h> + #include <linux/sched.h> ++#include <linux/unistd.h> + #include <linux/types.h> +-#include <linux/fcntl.h> + #include <linux/interrupt.h> +-#include <linux/ptrace.h> +-#include <linux/poll.h> + + #include <linux/slab.h> +-#include <linux/tty.h> + #include <linux/errno.h> + #include <linux/string.h> +-#include <linux/signal.h> +-#include <linux/ioctl.h> + #include <linux/skbuff.h> + + #include <linux/usb.h> + + #include <net/bluetooth/bluetooth.h> +-#include <net/bluetooth/bluez.h> + #include <net/bluetooth/hci_core.h> +-#include <net/bluetooth/hci_usb.h> ++ ++#include "hci_usb.h" + + #ifndef HCI_USB_DEBUG +-#undef DBG +-#define DBG( A... ) +-#undef DMP +-#define DMP( A... ) ++#undef BT_DBG ++#define BT_DBG( A... ) ++#undef BT_DMP ++#define BT_DMP( A... ) + #endif + +-static struct usb_device_id usb_bluetooth_ids [] = { ++#ifndef CONFIG_BLUEZ_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$ ++ */ ++ ++#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$ ++ * $Id$ + */ +-#define VERSION "1.0" ++#define VERSION "1.1" + + #include <linux/config.h> + #include <linux/module.h> +@@ -49,43 +49,56 @@ + #include <asm/uaccess.h> + + #include <net/bluetooth/bluetooth.h> +-#include <net/bluetooth/bluez.h> + #include <net/bluetooth/hci_core.h> +-#include <net/bluetooth/hci_vhci.h> ++#include "hci_vhci.h" + + /* HCI device part */ + +-int hci_vhci_open(struct hci_dev *hdev) ++static int hci_vhci_open(struct hci_dev *hdev) + { +- hdev->flags |= HCI_RUNNING; ++ set_bit(HCI_RUNNING, &hdev->flags); + return 0; + } + +-int hci_vhci_flush(struct hci_dev *hdev) ++static int hci_vhci_flush(struct hci_dev *hdev) + { + struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) hdev->driver_data; + skb_queue_purge(&hci_vhci->readq); + return 0; + } + +-int hci_vhci_close(struct hci_dev *hdev) ++static int hci_vhci_close(struct hci_dev *hdev) + { +- hdev->flags &= ~HCI_RUNNING; ++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) ++ return 0; ++ + hci_vhci_flush(hdev); + return 0; + } + +-int hci_vhci_send_frame(struct sk_buff *skb) ++static void hci_vhci_destruct(struct hci_dev *hdev) ++{ ++ struct hci_vhci_struct *vhci; ++ ++ if (!hdev) return; ++ ++ vhci = (struct hci_vhci_struct *) hdev->driver_data; ++ kfree(vhci); ++ ++ MOD_DEC_USE_COUNT; ++} ++ ++static int hci_vhci_send_frame(struct sk_buff *skb) + { + struct hci_dev* hdev = (struct hci_dev *) skb->dev; + struct hci_vhci_struct *hci_vhci; + + if (!hdev) { +- ERR("Frame for uknown device (hdev=NULL)"); ++ BT_ERR("Frame for uknown device (hdev=NULL)"); + return -ENODEV; + } + +- if (!(hdev->flags & HCI_RUNNING)) ++ if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -EBUSY; + + hci_vhci = (struct hci_vhci_struct *) hdev->driver_data; +@@ -188,7 +201,7 @@ + + add_wait_queue(&hci_vhci->read_wait, &wait); + while (count) { +- current->state = TASK_INTERRUPTIBLE; ++ set_current_state(TASK_INTERRUPTIBLE); + + /* Read frames from device queue */ + if (!(skb = skb_dequeue(&hci_vhci->readq))) { +@@ -214,8 +227,7 @@ + kfree_skb(skb); + break; + } +- +- current->state = TASK_RUNNING; ++ set_current_state(TASK_RUNNING); + remove_wait_queue(&hci_vhci->read_wait, &wait); + + return ret; +@@ -270,11 +282,13 @@ + hdev->close = hci_vhci_close; + hdev->flush = hci_vhci_flush; + hdev->send = hci_vhci_send_frame; ++ hdev->destruct = hci_vhci_destruct; + + if (hci_register_dev(hdev) < 0) { + kfree(hci_vhci); + return -EBUSY; + } ++ MOD_INC_USE_COUNT; + + file->private_data = hci_vhci; + return 0; +@@ -285,12 +299,10 @@ + struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data; + + if (hci_unregister_dev(&hci_vhci->hdev) < 0) { +- ERR("Can't unregister HCI device %s", hci_vhci->hdev.name); ++ BT_ERR("Can't unregister HCI device %s", hci_vhci->hdev.name); + } + +- kfree(hci_vhci); + file->private_data = NULL; +- + return 0; + } + +@@ -315,12 +327,12 @@ + + int __init hci_vhci_init(void) + { +- INF("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", ++ BT_INFO("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", + VERSION); +- INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); ++ BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>"); + + if (misc_register(&hci_vhci_miscdev)) { +- ERR("Can't register misc device %d\n", VHCI_MINOR); ++ BT_ERR("Can't register misc device %d\n", VHCI_MINOR); + return -EIO; + } + +@@ -337,4 +349,4 @@ + + MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); + MODULE_DESCRIPTION("BlueZ VHCI driver ver " VERSION); +-MODULE_LICENSE("GPL"); ++MODULE_LICENSE("GPL"); +--- /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$ ++ */ ++ ++#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$ ++ * $Id$ + */ + + #ifndef __BLUETOOTH_H +@@ -31,17 +31,63 @@ + + #include <asm/types.h> + #include <asm/byteorder.h> ++#include <linux/poll.h> ++#include <net/sock.h> + + #ifndef AF_BLUETOOTH + #define AF_BLUETOOTH 31 + #define PF_BLUETOOTH AF_BLUETOOTH + #endif + ++/* Reserv for core and drivers use */ ++#define BLUEZ_SKB_RESERVE 8 ++ ++#ifndef MIN ++#define MIN(a,b) ((a) < (b) ? (a) : (b)) ++#endif ++ + #define BTPROTO_L2CAP 0 + #define BTPROTO_HCI 1 ++#define BTPROTO_SCO 2 ++#define BTPROTO_RFCOMM 3 ++#define BTPROTO_BNEP 4 ++#define BTPROTO_CMTP 5 + + #define SOL_HCI 0 + #define SOL_L2CAP 6 ++#define SOL_SCO 17 ++#define SOL_RFCOMM 18 ++ ++/* Debugging */ ++#ifdef CONFIG_BLUEZ_DEBUG ++ ++#define HCI_CORE_DEBUG 1 ++#define HCI_SOCK_DEBUG 1 ++#define HCI_UART_DEBUG 1 ++#define HCI_USB_DEBUG 1 ++//#define HCI_DATA_DUMP 1 ++ ++#define L2CAP_DEBUG 1 ++#define SCO_DEBUG 1 ++#define AF_BLUETOOTH_DEBUG 1 ++ ++#endif /* CONFIG_BLUEZ_DEBUG */ ++ ++extern void bluez_dump(char *pref, __u8 *buf, int count); ++ ++#if __GNUC__ <= 2 && __GNUC_MINOR__ < 95 ++#define __func__ __FUNCTION__ ++#endif ++ ++#define BT_INFO(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg) ++#define BT_DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n" , __func__ , ## arg) ++#define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) ++ ++#ifdef HCI_DATA_DUMP ++#define BT_DMP(buf, len) bluez_dump(__func__, buf, len) ++#else ++#define BT_DMP(D...) ++#endif + + /* Connection and socket states */ + enum { +@@ -50,6 +96,7 @@ + BT_BOUND, + BT_LISTEN, + BT_CONNECT, ++ BT_CONNECT2, + BT_CONFIG, + BT_DISCONN, + BT_CLOSED +@@ -66,7 +113,8 @@ + __u8 b[6]; + } __attribute__((packed)) bdaddr_t; + +-#define BDADDR_ANY ((bdaddr_t *)"\000\000\000\000\000") ++#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) ++#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) + + /* Copy, swap, convert BD Address */ + static inline int bacmp(bdaddr_t *ba1, bdaddr_t *ba2) +@@ -82,6 +130,91 @@ + char *batostr(bdaddr_t *ba); + bdaddr_t *strtoba(char *str); + ++/* Common socket structures and functions */ ++ ++#define bluez_pi(sk) ((struct bluez_pinfo *) &sk->protinfo) ++#define bluez_sk(pi) ((struct sock *) \ ++ ((void *)pi - (unsigned long)(&((struct sock *)0)->protinfo))) ++ ++struct bluez_pinfo { ++ bdaddr_t src; ++ bdaddr_t dst; ++ ++ struct list_head accept_q; ++ struct sock *parent; ++}; ++ ++struct bluez_sock_list { ++ struct sock *head; ++ rwlock_t lock; ++}; ++ ++int bluez_sock_register(int proto, struct net_proto_family *ops); ++int bluez_sock_unregister(int proto); ++void bluez_sock_init(struct socket *sock, struct sock *sk); ++void bluez_sock_link(struct bluez_sock_list *l, struct sock *s); ++void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s); ++int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm); ++uint bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait); ++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo); ++ ++void bluez_accept_enqueue(struct sock *parent, struct sock *sk); ++struct sock * bluez_accept_dequeue(struct sock *parent, struct socket *newsock); ++ ++/* Skb helpers */ ++struct bluez_skb_cb { ++ int incomming; ++}; ++#define bluez_cb(skb) ((struct bluez_skb_cb *)(skb->cb)) ++ ++static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how) ++{ ++ struct sk_buff *skb; ++ ++ if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) { ++ skb_reserve(skb, BLUEZ_SKB_RESERVE); ++ bluez_cb(skb)->incomming = 0; ++ } ++ return skb; ++} ++ ++static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len, ++ int nb, int *err) ++{ ++ struct sk_buff *skb; ++ ++ if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) { ++ skb_reserve(skb, BLUEZ_SKB_RESERVE); ++ bluez_cb(skb)->incomming = 0; ++ } ++ ++ return skb; ++} ++ ++static inline int skb_frags_no(struct sk_buff *skb) ++{ ++ register struct sk_buff *frag = skb_shinfo(skb)->frag_list; ++ register int n = 1; ++ ++ for (; frag; frag=frag->next, n++); ++ return n; ++} ++ ++int hci_core_init(void); ++int hci_core_cleanup(void); ++int hci_sock_init(void); ++int hci_sock_cleanup(void); ++ + int bterr(__u16 code); + ++#ifndef MODULE_LICENSE ++#define MODULE_LICENSE(x) ++#endif ++ ++#ifndef list_for_each_safe ++#define list_for_each_safe(pos, n, head) \ ++ for (pos = (head)->next, n = pos->next; pos != (head); \ ++ pos = n, n = pos->next) ++#endif ++ + #endif /* __BLUETOOTH_H */ +--- 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$ +- */ +- +-#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$ ++ * $Id$ + */ + + #ifndef __HCI_CORE_H +@@ -32,14 +32,12 @@ + #include <net/bluetooth/hci.h> + + /* HCI upper protocols */ +-#define HCI_MAX_PROTO 1 + #define HCI_PROTO_L2CAP 0 ++#define HCI_PROTO_SCO 1 + + #define HCI_INIT_TIMEOUT (HZ * 10) + +-/* ----- Inquiry cache ----- */ +-#define INQUIRY_CACHE_AGE_MAX (HZ*5) // 5 seconds +-#define INQUIRY_ENTRY_AGE_MAX (HZ*60) // 60 seconds ++/* HCI Core structures */ + + struct inquiry_entry { + struct inquiry_entry *next; +@@ -53,111 +51,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$ ++ * $Id$ + */ + + #ifndef __HCI_H + #define __HCI_H + +-#include <asm/byteorder.h> +- +-#define HCI_MAX_DEV 8 +-#define HCI_MAX_FRAME_SIZE 2048 ++#define HCI_MAX_ACL_SIZE 1024 ++#define HCI_MAX_SCO_SIZE 255 ++#define HCI_MAX_EVENT_SIZE 260 ++#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4) + + /* HCI dev events */ + #define HCI_DEV_REG 1 + #define HCI_DEV_UNREG 2 + #define HCI_DEV_UP 3 + #define HCI_DEV_DOWN 4 ++#define HCI_DEV_SUSPEND 5 ++#define HCI_DEV_RESUME 6 + + /* HCI 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$ +- */ +- +-#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$ +- */ +- +-#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$ +- */ +- +-#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$ +- */ +- +-#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$ ++ * $Id$ + */ + + #ifndef __L2CAP_H + #define __L2CAP_H + +-#include <asm/types.h> +-#include <asm/byteorder.h> +- + /* L2CAP defaults */ + #define L2CAP_DEFAULT_MTU 672 + #define L2CAP_DEFAULT_FLUSH_TO 0xFFFF + + #define L2CAP_CONN_TIMEOUT (HZ * 40) +-#define L2CAP_DISCONN_TIMEOUT (HZ * 2) +-#define L2CAP_CONN_IDLE_TIMEOUT (HZ * 60) + + /* L2CAP socket address */ + struct sockaddr_l2 { +@@ -47,17 +42,12 @@ + bdaddr_t l2_bdaddr; + }; + +-/* set/get sockopt defines */ +-#define L2CAP_OPTIONS 0x01 ++/* Socket options */ ++#define L2CAP_OPTIONS 0x01 + struct l2cap_options { + __u16 omtu; + __u16 imtu; + __u16 flush_to; +- __u32 token_rate; +- __u32 bucket_size; +- __u32 pick_band; +- __u32 latency; +- __u32 delay_var; + }; + + #define L2CAP_CONNINFO 0x02 +@@ -65,6 +55,27 @@ + __u16 hci_handle; + }; + ++#define L2CAP_LM 0x03 ++#define L2CAP_LM_MASTER 0x0001 ++#define L2CAP_LM_AUTH 0x0002 ++#define L2CAP_LM_ENCRYPT 0x0004 ++#define L2CAP_LM_TRUSTED 0x0008 ++#define L2CAP_LM_RELIABLE 0x0010 ++ ++#define L2CAP_QOS 0x04 ++struct l2cap_qos { ++ __u16 service_type; ++ __u32 token_rate; ++ __u32 token_bucket_size; ++ __u32 peak_bandwidth; ++ __u32 latency; ++ __u32 delay_variation; ++}; ++ ++#define L2CAP_SERV_NO_TRAFFIC 0x00 ++#define L2CAP_SERV_BEST_EFFORT 0x01 ++#define L2CAP_SERV_GUARANTEED 0x02 ++ + /* L2CAP command codes */ + #define L2CAP_COMMAND_REJ 0x01 + #define L2CAP_CONN_REQ 0x02 +@@ -79,7 +90,6 @@ + #define L2CAP_INFO_RSP 0x0b + + /* L2CAP structures */ +- + typedef struct { + __u16 len; + __u16 cid; +@@ -112,11 +122,17 @@ + } __attribute__ ((packed)) l2cap_conn_rsp; + #define L2CAP_CONN_RSP_SIZE 8 + +-#define L2CAP_CONN_SUCCESS 0x0000 +-#define L2CAP_CONN_PEND 0x0001 +-#define L2CAP_CONN_BAD_PSM 0x0002 +-#define L2CAP_CONN_SEC_BLOCK 0x0003 +-#define L2CAP_CONN_NO_MEM 0x0004 ++/* connect result */ ++#define L2CAP_CR_SUCCESS 0x0000 ++#define L2CAP_CR_PEND 0x0001 ++#define L2CAP_CR_BAD_PSM 0x0002 ++#define L2CAP_CR_SEC_BLOCK 0x0003 ++#define L2CAP_CR_NO_MEM 0x0004 ++ ++/* connect status */ ++#define L2CAP_CS_NO_INFO 0x0000 ++#define L2CAP_CS_AUTHEN_PEND 0x0001 ++#define L2CAP_CS_AUTHOR_PEND 0x0002 + + typedef struct { + __u16 dcid; +@@ -147,6 +163,8 @@ + #define L2CAP_CONF_FLUSH_TO 0x02 + #define L2CAP_CONF_QOS 0x03 + ++#define L2CAP_CONF_MAX_SIZE 22 ++ + typedef struct { + __u16 dcid; + __u16 scid; +@@ -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$ ++ */ ++ ++#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$ ++ */ ++ ++#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$ ++ * $Id$ + */ +-#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$ ++ */ ++ ++#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$ ++ */ ++ ++#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$ ++ */ ++ ++#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$ ++ */ ++ ++#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$ ++ */ ++ ++#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$ ++ * $Id$ + */ + + #include <linux/config.h> + #include <linux/module.h> ++#include <linux/kmod.h> + + #include <linux/types.h> + #include <linux/errno.h> +@@ -50,12 +51,11 @@ + #include <asm/unaligned.h> + + #include <net/bluetooth/bluetooth.h> +-#include <net/bluetooth/bluez.h> + #include <net/bluetooth/hci_core.h> + + #ifndef HCI_CORE_DEBUG +-#undef DBG +-#define DBG( A... ) ++#undef BT_DBG ++#define BT_DBG( A... ) + #endif + + static void hci_cmd_task(unsigned long arg); +@@ -63,279 +63,69 @@ + static void hci_tx_task(unsigned long arg); + static void hci_notify(struct hci_dev *hdev, int event); + +-static rwlock_t hci_task_lock = RW_LOCK_UNLOCKED; ++rwlock_t hci_task_lock = RW_LOCK_UNLOCKED; + + /* HCI device list */ +-struct hci_dev *hdev_list[HCI_MAX_DEV]; +-spinlock_t hdev_list_lock; +-#define GET_HDEV(a) (hdev_list[a]) ++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$ ++ */ ++ ++#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$ ++ * $Id$ + */ + + #include <linux/config.h> +@@ -49,45 +49,54 @@ + + #include <asm/system.h> + #include <asm/uaccess.h> ++#include <asm/unaligned.h> + + #include <net/bluetooth/bluetooth.h> +-#include <net/bluetooth/bluez.h> + #include <net/bluetooth/hci_core.h> + + #ifndef HCI_SOCK_DEBUG +-#undef DBG +-#define DBG( A... ) ++#undef BT_DBG ++#define BT_DBG( A... ) + #endif + +-/* HCI socket interface */ ++/* ----- HCI socket interface ----- */ ++ ++/* Security filter */ ++static struct hci_sec_filter hci_sec_filter = { ++ /* Packet types */ ++ 0x10, ++ /* Events */ ++ { 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$ ++ */ ++#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$ +- */ +-#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$ +- */ +- +-#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$ ++ * $Id$ + */ + + #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$ ++ */ ++ ++#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$ ++ */ ++ ++/* 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$ ++ */ ++ ++#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$ ++ */ ++ ++#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$ ++ */ ++#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$ ++ * $Id$ + */ + + #include <linux/config.h> +@@ -39,25 +39,28 @@ + #include <linux/socket.h> + + #include <net/bluetooth/bluetooth.h> +-#include <net/bluetooth/bluez.h> + #include <net/bluetooth/hci_core.h> + + /* HCI Core */ + EXPORT_SYMBOL(hci_register_dev); + EXPORT_SYMBOL(hci_unregister_dev); ++EXPORT_SYMBOL(hci_suspend_dev); ++EXPORT_SYMBOL(hci_resume_dev); ++ + EXPORT_SYMBOL(hci_register_proto); + EXPORT_SYMBOL(hci_unregister_proto); +-EXPORT_SYMBOL(hci_register_notifier); +-EXPORT_SYMBOL(hci_unregister_notifier); + ++EXPORT_SYMBOL(hci_get_route); + EXPORT_SYMBOL(hci_connect); +-EXPORT_SYMBOL(hci_disconnect); + EXPORT_SYMBOL(hci_dev_get); ++EXPORT_SYMBOL(hci_conn_auth); ++EXPORT_SYMBOL(hci_conn_encrypt); + + EXPORT_SYMBOL(hci_recv_frame); + EXPORT_SYMBOL(hci_send_acl); + EXPORT_SYMBOL(hci_send_sco); +-EXPORT_SYMBOL(hci_send_raw); ++EXPORT_SYMBOL(hci_send_cmd); ++EXPORT_SYMBOL(hci_si_event); + + /* BlueZ lib */ + EXPORT_SYMBOL(bluez_dump); +@@ -68,5 +71,11 @@ + /* BlueZ sockets */ + EXPORT_SYMBOL(bluez_sock_register); + EXPORT_SYMBOL(bluez_sock_unregister); ++EXPORT_SYMBOL(bluez_sock_init); + EXPORT_SYMBOL(bluez_sock_link); + EXPORT_SYMBOL(bluez_sock_unlink); ++EXPORT_SYMBOL(bluez_sock_recvmsg); ++EXPORT_SYMBOL(bluez_sock_poll); ++EXPORT_SYMBOL(bluez_accept_enqueue); ++EXPORT_SYMBOL(bluez_accept_dequeue); ++EXPORT_SYMBOL(bluez_sock_wait_state); diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bt950_cs.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bt950_cs.patch index e69de29bb2..c29f0d02f2 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bt950_cs.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/bt950_cs.patch @@ -0,0 +1,1174 @@ +diff -Nur linux-orig/drivers/bluetooth/bt950_cs.c linux/drivers/bluetooth/bt950_cs.c +--- linux-orig/drivers/bluetooth/bt950_cs.c 1970-01-01 03:00:00.000000000 +0300 ++++ linux/drivers/bluetooth/bt950_cs.c 2004-02-04 09:55:04.000000000 +0300 +@@ -0,0 +1,1133 @@ ++/* ++ * ++ * Driver for Bluetooth cards with OXCF950 UART interface ++ * ++ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org> ++ * Albert Rybalkin <albertr@iral.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; ++ * ++ * 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> ++ ++#ifdef CONFIG_MODVERSIONS ++#ifndef MODVERSIONS ++#define MODVERSIONS ++#endif ++#include <linux/modversions.h> ++#endif ++ ++#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/delay.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> ++ ++/* Default baud rate: 57600, 115200, 230400 or 460800 */ ++#define DEFAULT_BAUD_RATE 460800 ++ ++ ++/* ======================== Module parameters ======================== */ ++ ++ ++/* Bit map of interrupts to choose from */ ++static u_int irq_mask = 0x86bc; ++static int irq_list[4] = { -1 }; ++static long baud_rate = DEFAULT_BAUD_RATE; ++ ++MODULE_PARM(irq_mask, "i"); ++MODULE_PARM(irq_list, "1-4i"); ++MODULE_PARM(baud_rate, "l"); ++ ++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>, Albert Rybalkin <albertr@iral.com>"); ++MODULE_DESCRIPTION("BlueZ driver for Bluetooth cards with OXCF950 UART interface"); ++MODULE_LICENSE("GPL"); ++ ++ ++ ++/* ======================== Local structures ======================== */ ++ ++ ++typedef struct bt950_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; ++} bt950_info_t; ++ ++ ++static void bt950_config(dev_link_t *link); ++static void bt950_release(u_long arg); ++static int bt950_event(event_t event, int priority, event_callback_args_t *args); ++ ++static dev_info_t dev_info = "bt950_cs"; ++ ++static dev_link_t *bt950_attach(void); ++static void bt950_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 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 ++ ++/* 950-specific stuff */ ++#define MAX_WAIT 0xFFFF ++#define FIFO_SIZE 128 ++ ++#define TR_TX_INT 0x10 /* TTL: TX interrupt trigger level (0-127) */ ++#define TR_RX_INT 0x40 /* RTL: RX interrupt trigger level (1-127) */ ++#define TR_CTL_LO 0x08 /* FCL: auto flow control LOWER trigger level (0-127) */ ++#define TR_CTL_HI 0x60 /* FCH: auto flow control HIGH trigger level (1-127) */ ++ ++/* 950-specific registers and values we use. It should ++ * eventually go to include/linux/serial_reg.h */ ++#define UART_IER_CTS 0x80 /* enable CTS interrupt */ ++#define UART_IER_RTS 0x40 /* enable RTS interrupt */ ++#define UART_IER_SLP 0x10 /* enable sleep mode */ ++#define UART_LCR_650 0xBF /* enable 650-compatible registers access */ ++#define UART_LSR_DE 0x80 /* data error */ ++#define UART_LSR_ERR (UART_LSR_OE|UART_LSR_PE|UART_LSR_FE|UART_LSR_BI|UART_LSR_DE) ++#define UART_IIR_RXTOUT 0x0C /* RX timeout interrupt */ ++#define UART_IIR_CTSRTS 0x20 /* CTS or RTS change interrupt */ ++#define UART_IIR_RTS 0x40 ++#define UART_IIR_CTS 0x80 ++#define UART_IIR_MASK 0x3E /* interrupt mask */ ++#define UART_SRT 0x0D /* soft reset register */ ++ ++ ++ ++/* ======================== Interrupt handling ======================== */ ++ ++ ++static int bt950_write(unsigned int iobase, int fifo_size, const unsigned char *buf, int len) ++{ ++ int i, actual = 0; ++ ++ /* Activate DTR and RTS */ ++ outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, iobase + UART_MCR); ++ ++ /* Wait for CTS flow control */ ++ for (i = MAX_WAIT; i; i--) ++ if (inb(iobase + UART_MSR) & UART_MSR_CTS) ++ break; ++ ++ if (!i) { ++ printk(KERN_WARNING "bt950_cs: Timeout waiting for CTS on write.\n"); ++ return 0; ++ } ++ ++ /* The TX FIFO should be empty */ ++ for (i = MAX_WAIT; i; i--) ++ if (inb(iobase + UART_LSR) & UART_LSR_THRE) ++ break; ++ ++ if (!i) { ++ printk(KERN_WARNING "bt950_cs: Timeout waiting for empty TX FIFO on write.\n"); ++ 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 bt950_write_wakeup(bt950_info_t *info) ++{ ++ unsigned char lcr; ++ unsigned int divisor; ++ ++ if (!info) { ++ printk(KERN_WARNING "bt950_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; ++ ++ if (skb->pkt_type & 0x80) { ++ /* Disable RTS */ ++ outb((inb(iobase + UART_MCR) & ~UART_MCR_RTS), iobase + UART_MCR); ++ } ++ ++ /* Send frame */ ++ len = bt950_write(iobase, FIFO_SIZE, skb->data, skb->len); ++ ++ set_bit(XMIT_WAKEUP, &(info->tx_state)); ++ ++ if (skb->pkt_type & 0x80) { ++ ++ wait_queue_head_t wait; ++ ++ switch (skb->pkt_type) { ++ ++ case PKT_BAUD_RATE_460800: ++ divisor = 1; ++ break; ++ ++ case PKT_BAUD_RATE_230400: ++ divisor = 2; ++ break; ++ ++ case PKT_BAUD_RATE_115200: ++ divisor = 4; ++ break; ++ ++ case PKT_BAUD_RATE_57600: ++ /* Fall through... */ ++ ++ default: ++ divisor = 8; ++ break; ++ } ++ ++ /* Wait until the command reaches the baseband */ ++ init_waitqueue_head(&wait); ++ interruptible_sleep_on_timeout(&wait, HZ / 10); ++ ++ /* Set baud on baseband */ ++ /* Enable divisor latch access */ ++ lcr = inb(iobase + UART_LCR) & 0x3F; ++ outb(lcr | UART_LCR_DLAB, iobase + UART_LCR); ++ ++ /* Setup divisor latch */ ++ outb(divisor & 0x00FF, iobase + UART_DLL); /* divisor latch LOW byte */ ++ outb((divisor & 0xFF00) >> 8, iobase + UART_DLM); /* divisor latch HI byte */ ++ ++ /* Disable divisor latch access */ ++ outb(lcr, iobase + UART_LCR); ++ ++ /* Enable RTS */ ++ outb((inb(iobase + UART_MCR) | UART_MCR_RTS), iobase + UART_MCR); ++ ++ /* 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; ++ ++ } while (test_bit(XMIT_WAKEUP, &(info->tx_state))); ++ ++ clear_bit(XMIT_SENDING, &(info->tx_state)); ++} ++ ++ ++static inline void bt950_receive(bt950_info_t *info) ++{ ++ unsigned int iobase; ++ int boguscount = 0; ++ ++ if (!info) { ++ printk(KERN_ERR "bt950_cs: Call of receive for unknown device.\n"); ++ return; ++ } ++ ++ iobase = info->link.io.BasePort1; ++ ++ /* Fixme: BUG? */ ++ inb(iobase + UART_MCR); ++ ++ 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_ERR "bt950_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 "bt950_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) = 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 too long */ ++ if (boguscount++ > 16) ++ break; ++ ++ } while (inb(iobase + UART_LSR) & UART_LSR_DR); ++} ++ ++ ++static void bt950_interrupt(int irq, void *dev_inst, struct pt_regs *regs) ++{ ++ bt950_info_t *info = dev_inst; ++ unsigned int iobase; ++ int boguscount = 0; ++ int iir, lsr; ++ ++ if (!info) { ++ printk(KERN_ERR "bt950_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); ++ ++ while (!(iir & UART_IIR_NO_INT)) { ++ ++ switch (iir & UART_IIR_ID) { ++ case UART_IIR_RLSI: ++ /* Clear RLSI interrupt */ ++ lsr = inb(iobase + UART_LSR); ++ printk(KERN_NOTICE "bt950_cs: RLSI interrupt, LSR=%#x\n", lsr); ++ /* Fixme: we need to process errors ... */ ++ break; ++ case UART_IIR_RDI: ++ /* Receive interrupt */ ++ bt950_receive(info); ++ break; ++ case UART_IIR_THRI: ++ /* Transmitter ready for data */ ++ bt950_write_wakeup(info); ++ break; ++ default: ++ printk(KERN_NOTICE "bt950_cs: Unhandled IIR=%#x\n", iir); ++ break; ++ } ++ ++ /* Make sure we don't stay here too long */ ++ if (boguscount++ > 100) ++ break; ++ ++ iir = inb(iobase + UART_IIR); ++ ++ } ++ ++ spin_unlock(&(info->lock)); ++} ++ ++/* ======================== Device specific HCI commands ======================== */ ++ ++ ++static int bt950_hci_set_baud_rate(struct hci_dev *hdev, int baud) ++{ ++ bt950_info_t *info = (bt950_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 "bt950_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: ++ baud = 57600; ++ 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); ++ ++ printk(KERN_WARNING "bt950_cs: setting baud rate: %d.\n", baud); ++ ++ bt950_write_wakeup(info); ++ ++ return 0; ++} ++ ++ ++/* ======================== HCI interface ======================== */ ++ ++ ++static int bt950_hci_flush(struct hci_dev *hdev) ++{ ++ bt950_info_t *info = (bt950_info_t *)(hdev->driver_data); ++ ++ /* Drop TX queue */ ++ skb_queue_purge(&(info->txq)); ++ ++ return 0; ++} ++ ++ ++static int bt950_hci_open(struct hci_dev *hdev) ++{ ++ bt950_hci_set_baud_rate(hdev, baud_rate); ++ set_bit(HCI_RUNNING, &(hdev->flags)); ++ ++ return 0; ++} ++ ++ ++static int bt950_hci_close(struct hci_dev *hdev) ++{ ++ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) ++ return 0; ++ ++ bt950_hci_flush(hdev); ++ ++ return 0; ++} ++ ++ ++static int bt950_hci_send_frame(struct sk_buff *skb) ++{ ++ bt950_info_t *info; ++ struct hci_dev *hdev = (struct hci_dev *)(skb->dev); ++ ++ if (!hdev) { ++ printk(KERN_ERR "bt950_cs: Frame for unknown HCI device (hdev=NULL)."); ++ return -ENODEV; ++ } ++ ++ info = (bt950_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); ++ ++ bt950_write_wakeup(info); ++ ++ return 0; ++} ++ ++ ++static void bt950_hci_destruct(struct hci_dev *hdev) ++{ ++} ++ ++ ++static int bt950_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) ++{ ++ return -ENOIOCTLCMD; ++} ++ ++ ++ ++/* ======================== Card services HCI interaction ======================== */ ++ ++ ++static int bt950_setup_uart(bt950_info_t *info) ++{ ++ unsigned long flags; ++ unsigned int iobase = info->link.io.BasePort1; ++ unsigned char lcr, ier = UART_IER_RDI | UART_IER_RLSI | UART_IER_SLP; ++ unsigned int divisor = 8; /* Fixme: divisor == 0x0c ??? */ ++ unsigned char id1, id2, id3, rev; ++ register int i; ++ ++ spin_lock_irqsave(&(info->lock), flags); ++ ++ /* Disable interrupts */ ++ outb(0, iobase + UART_IER); ++ ++ /* Activate RTS and OUT2 */ ++ /* Fixme: is OUT2 used to enable interrupts? */ ++ outb(UART_MCR_RTS | UART_MCR_OUT2, iobase + UART_MCR); ++ ++ /* Setup the FIFO's */ ++ outb(0, iobase + UART_FCR); ++ inb(iobase + UART_RX); ++ outb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | ++ UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_14, iobase + UART_FCR); ++ ++ /* Disable divisor latch access */ ++ lcr = inb(iobase + UART_LCR) & 0x3F; /* mask out UART_LCR_DLAB and UART_LCR_SBC */ ++ outb(lcr, iobase + UART_LCR); ++ ++ /* Read up to 4 bytes from RX FIFO */ ++ for (i = 0; i < 4; i++) { ++ inb(iobase + UART_RX); ++ if (!(inb(iobase + UART_LSR) & UART_LSR_DR)) ++ break; ++ } ++ ++ /* Wait if CTS/DSR/DCD changing */ ++ for (i = 1; i < 0x3E8; i++) { ++ if (!(inb(iobase + UART_MSR) & UART_MSR_ANY_DELTA)) ++ break; ++ } ++ ++ /* Enable divisor latch access */ ++ outb(lcr | UART_LCR_DLAB, iobase + UART_LCR); ++ ++ /* Setup divisor latch */ ++ outb(divisor & 0x00FF, iobase + UART_DLL); /* divisor latch LOW byte */ ++ outb((divisor & 0xFF00) >> 8, iobase + UART_DLM); /* divisor latch HIGH byte */ ++ ++ /* Disable divisor latch access */ ++ outb(lcr, iobase + UART_LCR); ++ ++ /* Setup interrupts, enable sleep mode */ ++ outb(ier, iobase + UART_IER); /* we don't want to handle TX interrupts */ ++ ++ /* Skip pending interrupts */ ++ for (i = 0; i < 4; i++) { ++ if (inb(iobase + UART_IIR) & UART_IIR_NO_INT) ++ break; ++ } ++ ++ /* 8N1 */ ++ lcr = UART_LCR_WLEN8; ++ outb(lcr, iobase + UART_LCR); ++ ++ /* Setup CTS/RTS flow control and 950 enhanced mode */ ++ outb(UART_LCR_650, iobase + UART_LCR); ++ outb(UART_EFR_CTS | UART_EFR_RTS | UART_EFR_ECB, ++ iobase + UART_EFR); ++ outb(lcr, iobase + UART_LCR); ++ ++ /* Read core id and revision */ ++ outb(UART_ACR, iobase + UART_EMSR); ++ outb(UART_ACR_ICRRD, iobase + UART_LSR); /* enable ICR read access, we don't need to save the old value of ACR */ ++ ++ outb(UART_ID1, iobase + UART_EMSR); ++ id1 = inb(iobase + UART_LSR); ++ ++ outb(UART_ID2, iobase + UART_EMSR); ++ id2 = inb(iobase + UART_LSR); ++ ++ outb(UART_ID3, iobase + UART_EMSR); ++ id3 = inb(iobase + UART_LSR); ++ ++ outb(UART_REV, iobase + UART_EMSR); ++ rev = inb(iobase + UART_LSR); ++ ++ if (id1 != 0x16 || id2 != 0xC9 || id3 != 0x50) { ++ printk(KERN_ERR "bt950_cs: Unknown UART core %02X%02X%02X found.\n", id1, id2, id3); ++ spin_unlock_irqrestore(&(info->lock), flags); ++ return -ENODEV; ++ } ++ ++ ++ /* Init ICR registers */ ++ outb(UART_TTL, iobase + UART_EMSR); ++ outb(TR_TX_INT, iobase + UART_LSR); /* TX interrupt trigger level (0-127) */ ++ ++ outb(UART_RTL, iobase + UART_EMSR); ++ outb(TR_RX_INT, iobase + UART_LSR); /* RX interrupt trigger level (1-127) */ ++ ++ outb(UART_FCL, iobase + UART_EMSR); ++ outb(TR_CTL_LO, iobase + UART_LSR); /* auto flow control LOWER trigger level (0-127) */ ++ ++ outb(UART_FCH, iobase + UART_EMSR); ++ outb(TR_CTL_HI, iobase + UART_LSR); /* auto flow control HIGH trigger level (1-127) */ ++ ++ outb(UART_ACR, iobase + UART_EMSR); ++ outb(UART_ACR_TLENB, iobase + UART_LSR); /* disable ICR read access, enable trigger levels */ ++ ++ spin_unlock_irqrestore(&(info->lock), flags); ++ ++ return 0; ++} ++ ++ ++static void bt950_stop_uart(bt950_info_t *info) ++{ ++ unsigned long flags; ++ unsigned int iobase = info->link.io.BasePort1; ++ ++ spin_lock_irqsave(&(info->lock), flags); ++ ++ /* Disable interrupts */ ++ outb(0, iobase + UART_IER); ++ ++ /* Set RTS and OUT2 low */ ++ outb(0, iobase + UART_MCR); ++ ++ spin_unlock_irqrestore(&(info->lock), flags); ++} ++ ++ ++static int bt950_open(bt950_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; ++ ++ /* Setup hardware */ ++ if ((err = bt950_setup_uart(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 = bt950_hci_open; ++ hdev->close = bt950_hci_close; ++ hdev->flush = bt950_hci_flush; ++ hdev->send = bt950_hci_send_frame; ++ hdev->destruct = bt950_hci_destruct; ++ hdev->ioctl = bt950_hci_ioctl; ++ ++ if (hci_register_dev(hdev) < 0) { ++ printk(KERN_ERR "bt950_cs: Can't register HCI device %s.\n", hdev->name); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++ ++static int bt950_close(bt950_info_t *info) ++{ ++ struct hci_dev *hdev = &(info->hdev); ++ ++ bt950_hci_close(hdev); ++ ++ /* Stop hardware */ ++ bt950_stop_uart(info); ++ ++ if (hci_unregister_dev(hdev) < 0) ++ printk(KERN_ERR "bt950_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); ++} ++ ++ ++static dev_link_t *bt950_attach(void) ++{ ++ bt950_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 = &bt950_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 = bt950_interrupt; ++ link->irq.Instance = info; ++ ++ link->conf.Attributes = CONF_ENABLE_IRQ; ++ link->conf.IntType = INT_MEMORY_AND_IO; ++ link->conf.Present = ++ PRESENT_OPTION | PRESENT_STATUS | PRESENT_PIN_REPLACE | ++ PRESENT_COPY; ++ ++ /* 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 = &bt950_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); ++ bt950_detach(link); ++ return NULL; ++ } ++ ++ return link; ++} ++ ++ ++static void bt950_detach(dev_link_t *link) ++{ ++ bt950_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) ++ bt950_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) ++ ++static void bt950_config(dev_link_t *link) ++{ ++ static ioaddr_t base[4] = { 0x2f8, 0x3e8, 0x2e8, 0x0 }; ++ client_handle_t handle = link->handle; ++ bt950_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_ERR "bt950_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 (bt950_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: ++ bt950_release((u_long)link); ++ link->state &= ~DEV_CONFIG_PENDING; ++} ++ ++ ++static void bt950_release(u_long arg) ++{ ++ dev_link_t *link = (dev_link_t *) arg; ++ bt950_info_t *info = link->priv; ++ ++ if (link->state & DEV_PRESENT) ++ bt950_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; ++} ++ ++ ++static int bt950_event(event_t event, int priority, event_callback_args_t *args) ++{ ++ dev_link_t *link = args->client_data; ++ bt950_info_t *info = link->priv; ++ ++ switch (event) { ++ case CS_EVENT_CARD_REMOVAL: ++ link->state &= ~DEV_PRESENT; ++ if (link->state & DEV_CONFIG) { ++ bt950_close(info); ++ link->state |= DEV_RELEASE_PENDING; ++ mod_timer(&link->release, jiffies + HZ / 20); ++ } ++ break; ++ case CS_EVENT_CARD_INSERTION: ++ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; ++ bt950_config(link); ++ break; ++ case CS_EVENT_PM_SUSPEND: ++ link->state |= DEV_SUSPEND; ++ /* Fall through... */ ++ case CS_EVENT_RESET_PHYSICAL: ++ if (link->state & DEV_CONFIG) { ++ bt950_stop_uart(info); ++ CardServices(ReleaseConfiguration, link->handle); ++ } ++ break; ++ case CS_EVENT_PM_RESUME: ++ link->state &= ~DEV_SUSPEND; ++ /* Fall through... */ ++ case CS_EVENT_CARD_RESET: ++ if (link->state & DEV_CONFIG) { ++ CardServices(RequestConfiguration, link->handle, &link->conf); ++ bt950_setup_uart(info); ++ } ++ break; ++ } ++ ++ return 0; ++} ++ ++ ++ ++/* ======================== Module initialization ======================== */ ++ ++ ++int __init init_bt950_cs(void) ++{ ++ servinfo_t serv; ++ int err; ++ ++ CardServices(GetCardServicesInfo, &serv); ++ if (serv.Revision != CS_RELEASE_CODE) { ++ printk(KERN_NOTICE "bt950_cs: Card Services release does not match!\n"); ++// return -1; ++ } ++ ++ err = register_pccard_driver(&dev_info, &bt950_attach, &bt950_detach); ++ ++ return err; ++} ++ ++ ++void __exit exit_bt950_cs(void) ++{ ++ unregister_pccard_driver(&dev_info); ++ ++ while (dev_list != NULL) ++ bt950_detach(dev_list); ++} ++ ++ ++module_init(init_bt950_cs); ++module_exit(exit_bt950_cs); ++ ++EXPORT_NO_SYMBOLS; +diff -Nur linux-orig/drivers/bluetooth/Config.in linux/drivers/bluetooth/Config.in +--- linux-orig/drivers/bluetooth/Config.in 2004-02-16 08:45:33.000000000 +0300 ++++ linux/drivers/bluetooth/Config.in 2004-02-16 08:50:36.000000000 +0300 +@@ -21,7 +21,7 @@ + + 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 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 + +@@ -29,5 +29,7 @@ + + dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ + ++dep_tristate 'HCI BT950 (BT950 device) driver' CONFIG_BLUEZ_BT950 $CONFIG_BLUEZ ++ + endmenu + +diff -Nur linux-orig/drivers/bluetooth/Makefile linux/drivers/bluetooth/Makefile +--- linux-orig/drivers/bluetooth/Makefile 2004-02-16 08:45:33.000000000 +0300 ++++ linux/drivers/bluetooth/Makefile 2004-02-16 08:50:47.000000000 +0300 +@@ -17,10 +17,12 @@ + obj-$(CONFIG_BLUEZ_HCIBFUSB) += bfusb.o + + obj-$(CONFIG_BLUEZ_HCIDTL1) += dtl1_cs.o +-obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o ++# obj-$(CONFIG_BLUEZ_HCIBT3C) += bt3c_cs.o + obj-$(CONFIG_BLUEZ_HCIBLUECARD) += bluecard_cs.o + obj-$(CONFIG_BLUEZ_HCIBTUART) += btuart_cs.o + ++obj-$(CONFIG_BLUEZ_BT950) += bt950_cs.o ++ + include $(TOPDIR)/Rules.make + + hci_uart.o: $(uart-y) diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/buffered-fbmem.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/buffered-fbmem.patch index e69de29bb2..554f118936 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/buffered-fbmem.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/buffered-fbmem.patch @@ -0,0 +1,19 @@ + +# +# Made by http://www.mn-logistik.de/unsupported/pxa250/patcher +# + +--- linux/drivers/video/fbmem.c~buffered-fbmem 2003-09-16 00:48:18.000000000 +0200 ++++ linux/drivers/video/fbmem.c 2003-09-19 00:38:00.000000000 +0200 +@@ -650,7 +650,11 @@ + pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK; + pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED; + #elif defined(__arm__) ++#ifdef CONFIG_PXA ++ vma->vm_page_prot = pgprot_noncached_buffered(vma->vm_page_prot); ++#else + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++#endif + #ifdef CONFIG_POODLE_CONSISTENT_ALLOC + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) | L_PTE_CACHEABLE); + #endif diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/compile.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/compile.patch index e69de29bb2..1a19b85daa 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/compile.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/compile.patch @@ -0,0 +1,14 @@ +--- linux/include/linux/jffs2_fs_i.h.old 2003-02-01 00:24:48.000000000 -0600 ++++ linux/include/linux/jffs2_fs_i.h 2003-02-01 00:25:14.000000000 -0600 +@@ -48,9 +48,11 @@ + uint32_t nr_frags; + #endif + ++#ifdef KERNEL_VERSION + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) + struct inode vfs_inode; + #endif ++#endif + }; + + #endif /* _JFFS2_FS_I */ diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/deviceinfo.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/deviceinfo.patch index e69de29bb2..58343c5b55 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/deviceinfo.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/deviceinfo.patch @@ -0,0 +1,26 @@ +--- linux/arch/arm/mach-pxa/Makefile.old 2004-09-24 20:49:09.000000000 +0930 ++++ linux/arch/arm/mach-pxa/Makefile 2004-09-24 20:49:11.000000000 +0930 +@@ -94,6 +94,23 @@ + endif + endif + ++ifeq ($(CONFIG_DEVICEINFO),m) ++ obj-$(CONFIG_DEVICEINFO) += devinfo.o ++ ifeq ($(CONFIG_SABINAL_DISCOVERY),y) ++ devinfo-objs-m += deviceinfo.o ++ devinfo-objs-$(CONFIG_DISCOVERY_DEVICEINFO) += discovery_deviceinfo.o ++ endif ++ ifeq ($(CONFIG_ARCH_PXA_POODLE),y) ++ devinfo-objs-m += sharpsl_deviceinfo.o ++ endif ++ ifeq ($(CONFIG_ARCH_PXA_CORGI),y) ++ devinfo-objs-m += sharpsl_deviceinfo.o ++ endif ++ ifeq ($(CONFIG_ARCH_PXA_TOSA),y) ++ devinfo-objs-m += sharpsl_deviceinfo.o ++ endif ++endif ++ + obj-m += registers.o + + include $(TOPDIR)/Rules.make diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/disable-pcmcia-probe.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/disable-pcmcia-probe.patch index e69de29bb2..79ba036323 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/disable-pcmcia-probe.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/disable-pcmcia-probe.patch @@ -0,0 +1,17 @@ + +# +# Patch managed by http://www.mn-logistik.de/unsupported/pxa250/patcher +# + +--- linux/drivers/pcmcia/Config.in~disable-pcmcia-probe 2003-05-13 11:18:23.000000000 +0200 ++++ linux/drivers/pcmcia/Config.in 2004-05-27 13:59:50.000000000 +0200 +@@ -15,9 +15,6 @@ + tristate 'PCMCIA/CardBus support' CONFIG_PCMCIA + if [ "$CONFIG_PCMCIA" != "n" ]; then + # yes, I really mean the following... +- if [ "$CONFIG_ISA" = "y" -o "$CONFIG_ARCH_SA1100" = "y" ]; then +- define_bool CONFIG_PCMCIA_PROBE y +- fi + if [ "$CONFIG_PCI" != "n" ]; then + bool ' CardBus support' CONFIG_CARDBUS + fi diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/enable-sysrq.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/enable-sysrq.patch index e69de29bb2..ece8fd9a5d 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/enable-sysrq.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/enable-sysrq.patch @@ -0,0 +1,45 @@ +--- linux/drivers/char/corgi_keyb.c~enable-sysrq 2003-09-05 01:18:15.000000000 +0200 ++++ linux/drivers/char/corgi_keyb.c 2003-09-05 01:40:17.000000000 +0200 +@@ -23,7 +23,7 @@ + #include <linux/init.h> + #include <linux/poll.h> + #include <linux/wait.h> +-#include <asm/arch/keyboard.h> ++#include <asm/keyboard.h> + #include <asm/uaccess.h> + #include <linux/tqueue.h> + #include <linux/kbd_ll.h> +@@ -46,6 +46,18 @@ + #endif + + /* ++ * This is the KeyCode [not ScanCode!] to ASCII Code mapping table ++ */ ++ ++#ifdef CONFIG_MAGIC_SYSRQ ++static unsigned char corgi_sysrq_xlate[128] = ++ "\000abcdefghijklmno" /* 00-0f */ ++ "pqrstuvwxyz\000\000\000\000\000" /* 10-1f */ ++ " \000\000\000\000\000\000\000\0001234567" /* 20-2f */ ++ "890\000\000\000\000\000\000\000\000\000\000\000\000\000"; /* 30-3f */ ++#endif ++ ++/* + * common logical driver definition + */ + extern void sharppda_kbd_press(int keycode); +@@ -251,7 +263,13 @@ + corgi_wakeup_button_init(); + #endif // USE_WAKEUP_BUTTON + +- printk("keyboard initilaized.\n"); ++ printk("keyboard initialized.\n"); ++#ifdef CONFIG_MAGIC_SYSRQ ++ k_sysrq_key = 0x28; // KEY_HOME ++ k_sysrq_xlate = corgi_sysrq_xlate; ++ printk("magic_sysrq initialized.\n"); ++#endif ++ + } + + int corgi_kbd_translate(unsigned char scancode, unsigned char *keycode_p) diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/idecs.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/idecs.patch index e69de29bb2..62038c34e2 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/idecs.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/idecs.patch @@ -0,0 +1,77 @@ +--- linux/drivers/ide/ide-cs.c 2003-02-28 17:04:00.000000000 -0600 ++++ linux.new/drivers/ide/ide-cs.c 2003-02-28 17:18:53.000000000 -0600 +@@ -2,7 +2,7 @@ + + A driver for PCMCIA IDE/ATA disk cards + +- ide_cs.c 1.26 1999/11/16 02:10:49 ++ ide-cs.c 1.26 1999/11/16 02:10:49 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file +@@ -66,7 +66,7 @@ + MODULE_PARM(pc_debug, "i"); + #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) + static char *version = +-"ide_cs.c 1.26 1999/11/16 02:10:49 (David Hinds)"; ++"ide-cs.c 1.26 1999/11/16 02:10:49 (David Hinds)"; + #else + #define DEBUG(n, args...) + #endif +@@ -110,7 +110,7 @@ + static int ide_event(event_t event, int priority, + event_callback_args_t *args); + +-static dev_info_t dev_info = "ide_cs"; ++static dev_info_t dev_info = "ide-cs"; + + static dev_link_t *ide_attach(void); + static void ide_detach(dev_link_t *); +@@ -356,7 +356,7 @@ + } + + if (hd < 0) { +- printk(KERN_NOTICE "ide_cs: ide_register() at 0x%03x & 0x%03x" ++ printk(KERN_NOTICE "ide-cs: ide_register() at 0x%03x & 0x%03x" + ", irq %u failed\n", io_base, ctl_base, + link->irq.AssignedIRQ); + goto failed; +@@ -369,7 +369,7 @@ + info->node.minor = 0; + info->hd = hd; + link->dev = &info->node; +- printk(KERN_INFO "ide_cs: %s: Vcc = %d.%d, Vpp = %d.%d\n", ++ printk(KERN_INFO "ide-cs: %s: Vcc = %d.%d, Vpp = %d.%d\n", + info->node.dev_name, link->conf.Vcc/10, link->conf.Vcc%10, + link->conf.Vpp1/10, link->conf.Vpp1%10); + +@@ -409,9 +409,9 @@ + MOD_DEC_USE_COUNT; + } + +- request_region(link->io.BasePort1, link->io.NumPorts1,"ide_cs"); ++ request_region(link->io.BasePort1, link->io.NumPorts1,"ide-cs"); + if (link->io.NumPorts2) +- request_region(link->io.BasePort2, link->io.NumPorts2,"ide_cs"); ++ request_region(link->io.BasePort2, link->io.NumPorts2,"ide-cs"); + + info->ndev = 0; + link->dev = NULL; +@@ -508,7 +508,7 @@ + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { +- printk(KERN_NOTICE "ide_cs: Card Services release " ++ printk(KERN_NOTICE "ide-cs: Card Services release " + "does not match!\n"); + return -1; + } +@@ -518,7 +518,7 @@ + + static void __exit exit_ide_cs(void) + { +- DEBUG(0, "ide_cs: unloading\n"); ++ DEBUG(0, "ide-cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + ide_detach(dev_list); diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/initsh.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/initsh.patch index e69de29bb2..a672631194 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/initsh.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/initsh.patch @@ -0,0 +1,14 @@ +--- linux/init/main.c 2002-02-25 13:38:13.000000000 -0600 ++++ linux.new/init/main.c 2003-03-16 11:49:45.000000000 -0600 +@@ -830,8 +830,10 @@ + * trying to recover a really broken machine. + */ + +- if (execute_command) ++ if (execute_command) { ++ argv_init[0] = execute_command; + execve(execute_command,argv_init,envp_init); ++ } + execve("/sbin/init",argv_init,envp_init); + execve("/etc/init",argv_init,envp_init); + execve("/bin/init",argv_init,envp_init); diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/irda-qos.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/irda-qos.patch index e69de29bb2..559f4ad564 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/irda-qos.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/irda-qos.patch @@ -0,0 +1,108 @@ +diff -urN linux.orig/net/irda/irsysctl.c linux/net/irda/irsysctl.c +--- linux.orig/net/irda/irsysctl.c Fri Feb 27 18:28:04 2004 ++++ linux/net/irda/irsysctl.c Wed Jan 14 19:36:40 2004 +@@ -40,7 +40,8 @@ + + enum { DISCOVERY=1, DEVNAME, DEBUG, FAST_POLL, DISCOVERY_SLOTS, + DISCOVERY_TIMEOUT, SLOT_TIMEOUT, MAX_BAUD_RATE, MIN_TX_TURN_TIME, +- MAX_NOREPLY_TIME, WARN_NOREPLY_TIME, LAP_KEEPALIVE_TIME, SPECIFIC_DEV }; ++ MAX_TX_WINDOW, MAX_NOREPLY_TIME, WARN_NOREPLY_TIME, ++ LAP_KEEPALIVE_TIME, SPECIFIC_DEV }; + + extern int sysctl_discovery; + extern int sysctl_discovery_slots; +@@ -51,6 +52,7 @@ + extern char sysctl_devname[]; + extern int sysctl_max_baud_rate; + extern int sysctl_min_tx_turn_time; ++extern int sysctl_max_tx_window; + extern int sysctl_max_noreply_time; + extern int sysctl_warn_noreply_time; + extern int sysctl_lap_keepalive_time; +@@ -71,6 +73,8 @@ + static int min_max_baud_rate = 2400; + static int max_min_tx_turn_time = 10000; /* See qos.c - IrLAP spec */ + static int min_min_tx_turn_time = 0; ++static int max_max_tx_window=7; ++static int min_max_tx_window=1; + static int max_max_noreply_time = 40; /* See qos.c - IrLAP spec */ + static int min_max_noreply_time = 3; + static int max_warn_noreply_time = 3; /* 3s == standard */ +@@ -128,6 +132,9 @@ + { MIN_TX_TURN_TIME, "min_tx_turn_time", &sysctl_min_tx_turn_time, + sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, + NULL, &min_min_tx_turn_time, &max_min_tx_turn_time }, ++ { MAX_TX_WINDOW, "max_tx_window", &sysctl_max_tx_window, ++ sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, ++ NULL, &min_max_tx_window, &max_max_tx_window }, + { MAX_NOREPLY_TIME, "max_noreply_time", &sysctl_max_noreply_time, + sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, + NULL, &min_max_noreply_time, &max_max_noreply_time }, +diff -urN linux.orig/net/irda/parameters.c linux/net/irda/parameters.c +--- linux.orig/net/irda/parameters.c Sat Nov 10 01:22:17 2001 ++++ linux/net/irda/parameters.c Wed Jan 14 19:34:23 2004 +@@ -204,12 +204,14 @@ + { + irda_param_t p; + int n = 0; ++ int extract_len; + int err; + + p.pi = pi; /* In case handler needs to know */ + p.pl = buf[1]; /* Extract lenght of value */ + p.pv.i = 0; /* Clear value */ +- ++ extract_len = p.pl; ++ + /* Check if buffer is long enough for parsing */ + if (len < (2+p.pl)) { + WARNING(__FUNCTION__ "(), buffer to short for parsing! " +@@ -226,12 +228,15 @@ + "Expected %d bytes, but value had %d bytes!\n", + type & PV_MASK, p.pl); + +- /* Skip parameter */ ++ if((p.pl < (type & PV_MASK)) || (type & PV_BIG_ENDIAN)) { + return p.pl+2; ++ } else { ++ extract_len = type & PV_MASK; ++ } + } + + +- switch (p.pl) { ++ switch (extract_len) { + case 1: + n += irda_param_unpack(buf+2, "b", &p.pv.i); + break; +diff -urN linux.orig/net/irda/qos.c linux/net/irda/qos.c +--- linux.orig/net/irda/qos.c Fri Feb 27 18:28:04 2004 ++++ linux/net/irda/qos.c Wed Jan 14 18:18:20 2004 +@@ -65,6 +65,7 @@ + */ + unsigned sysctl_min_tx_turn_time = 10; + ++unsigned sysctl_max_tx_window = 7; + /* + * Specific device list limits some negotiation parameters at the connection + * with listed peer devices. +@@ -212,6 +213,9 @@ + __u16 msb = 0x8000; + int index = 15; /* Current MSB */ + ++ if(!word) ++ word=0x1; ++ + while (msb) { + if (word & msb) + break; /* Found it! */ +@@ -386,6 +390,9 @@ + qos->max_turn_time.value = 500; + } + ++ if(qos->window_size.value > sysctl_max_tx_window) ++ qos->window_size.value = sysctl_max_tx_window; ++ + /* + * The data size must be adjusted according to the baud rate and max + * turn time diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw240_we15-6.diff b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw240_we15-6.diff index e69de29bb2..2ebfd8ec12 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw240_we15-6.diff +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw240_we15-6.diff @@ -0,0 +1,399 @@ +diff -u -p linux/include/linux/wireless.14.h linux/include/linux/wireless.h +--- linux/include/linux/wireless.14.h Mon Dec 2 18:51:00 2002 ++++ linux/include/linux/wireless.h Mon Dec 2 18:53:35 2002 +@@ -1,7 +1,7 @@ + /* + * This file define a set of standard wireless extensions + * +- * Version : 14 25.1.02 ++ * Version : 15 12.7.02 + * + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> + * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. +@@ -80,7 +80,7 @@ + * (there is some stuff that will be added in the future...) + * I just plan to increment with each new version. + */ +-#define WIRELESS_EXT 14 ++#define WIRELESS_EXT 15 + + /* + * Changes : +@@ -153,17 +153,32 @@ + * - Define additional specific event numbers + * - Add "addr" and "param" fields in union iwreq_data + * - AP scanning stuff (SIOCSIWSCAN and friends) ++ * ++ * V14 to V15 ++ * ---------- ++ * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg ++ * - Make struct iw_freq signed (both m & e), add explicit padding ++ * - Add IWEVCUSTOM for driver specific event/scanning token ++ * - Add IW_MAX_GET_SPY for driver returning a lot of addresses ++ * - Add IW_TXPOW_RANGE for range of Tx Powers ++ * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points ++ * - Add IW_MODE_MONITOR for passive monitor + */ + + /**************************** CONSTANTS ****************************/ + + /* -------------------------- IOCTL LIST -------------------------- */ + +-/* Basic operations */ ++/* Wireless Identification */ + #define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ + #define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ +-#define SIOCSIWNWID 0x8B02 /* set network id (the cell) */ +-#define SIOCGIWNWID 0x8B03 /* get network id */ ++/* SIOCGIWNAME is used to verify the presence of Wireless Extensions. ++ * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... ++ * Don't put the name of your driver there, it's useless. */ ++ ++/* Basic operations */ ++#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ ++#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ + #define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ + #define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ + #define SIOCSIWMODE 0x8B06 /* set operation mode */ +@@ -178,16 +193,18 @@ + #define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ + #define SIOCSIWSTATS 0x8B0E /* Unused */ + #define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ ++/* SIOCGIWSTATS is strictly used between user space and the kernel, and ++ * is never passed to the driver (i.e. the driver will never see it). */ + +-/* Mobile IP support */ ++/* Mobile IP support (statistics per MAC address) */ + #define SIOCSIWSPY 0x8B10 /* set spy addresses */ + #define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ + + /* Access Point manipulation */ + #define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ + #define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ +-#define SIOCGIWAPLIST 0x8B17 /* get list of access point in range */ +-#define SIOCSIWSCAN 0x8B18 /* trigger scanning */ ++#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ ++#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ + #define SIOCGIWSCAN 0x8B19 /* get scanning results */ + + /* 802.11 specific support */ +@@ -197,9 +214,7 @@ + #define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ + /* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit + * within the 'iwreq' structure, so we need to use the 'data' member to +- * point to a string in user space, like it is done for RANGE... +- * The "flags" member indicate if the ESSID is active or not (promiscuous). +- */ ++ * point to a string in user space, like it is done for RANGE... */ + + /* Other parameters useful in 802.11 and some other devices */ + #define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ +@@ -257,7 +272,10 @@ + /* Most events use the same identifier as ioctl requests */ + + #define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ +-#define IWEVQUAL 0x8C01 /* Quality part of statistics */ ++#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ ++#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ ++#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ ++#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ + + #define IWEVFIRST 0x8C00 + +@@ -273,7 +291,8 @@ + #define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ + #define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ + #define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ +-#define IW_PRIV_TYPE_FLOAT 0x5000 ++#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ ++#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ + + #define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed nuber of args */ + +@@ -297,13 +316,16 @@ + + /* Maximum tx powers in the range struct */ + #define IW_MAX_TXPOWER 8 ++/* Note : if you more than 8 TXPowers, just set the max and min or ++ * a few of them in the struct iw_range. */ + + /* Maximum of address that you may set with SPY */ +-#define IW_MAX_SPY 8 ++#define IW_MAX_SPY 8 /* set */ ++#define IW_MAX_GET_SPY 64 /* get */ + + /* Maximum of address that you may get in the + list of access points in range */ +-#define IW_MAX_AP 8 ++#define IW_MAX_AP 64 + + /* Maximum size of the ESSID and NICKN strings */ + #define IW_ESSID_MAX_SIZE 32 +@@ -315,6 +337,7 @@ + #define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ + #define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ + #define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ ++#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ + + /* Maximum number of size of encoding token available + * they are listed in the range structure */ +@@ -350,8 +373,10 @@ + #define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + + /* Transmit Power flags available */ ++#define IW_TXPOW_TYPE 0x00FF /* Type of value */ + #define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ + #define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ ++#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ + + /* Retry limits and lifetime flags available */ + #define IW_RETRY_ON 0x0000 /* No details... */ +@@ -376,6 +401,9 @@ + /* Maximum size of returned data */ + #define IW_SCAN_MAX_DATA 4096 /* In bytes */ + ++/* Max number of char in custom event - use multiple of them if needed */ ++#define IW_CUSTOM_MAX 256 /* In bytes */ ++ + /****************************** TYPES ******************************/ + + /* --------------------------- SUBTYPES --------------------------- */ +@@ -411,9 +439,10 @@ struct iw_point + */ + struct iw_freq + { +- __u32 m; /* Mantissa */ +- __u16 e; /* Exponent */ ++ __s32 m; /* Mantissa */ ++ __s16 e; /* Exponent */ + __u8 i; /* List index (when in range struct) */ ++ __u8 pad; /* Unused - just for alignement */ + }; + + /* +diff -u -p linux/include/net/iw_handler.14.h linux/include/net/iw_handler.h +--- linux/include/net/iw_handler.14.h Mon Dec 2 18:51:17 2002 ++++ linux/include/net/iw_handler.h Mon Dec 2 18:54:51 2002 +@@ -1,7 +1,7 @@ + /* + * This file define the new driver API for Wireless Extensions + * +- * Version : 3 17.1.02 ++ * Version : 4 21.6.02 + * + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> + * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved. +@@ -206,7 +206,7 @@ + * will be needed... + * I just plan to increment with each new version. + */ +-#define IW_HANDLER_VERSION 3 ++#define IW_HANDLER_VERSION 4 + + /* + * Changes : +@@ -217,6 +217,9 @@ + * - Add Wireless Event support : + * o wireless_send_event() prototype + * o iwe_stream_add_event/point() inline functions ++ * V3 to V4 ++ * -------- ++ * - Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes + */ + + /**************************** CONSTANTS ****************************/ +@@ -233,10 +236,10 @@ + #define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */ + #define IW_HEADER_TYPE_UINT 4 /* __u32 */ + #define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */ +-#define IW_HEADER_TYPE_POINT 6 /* struct iw_point */ +-#define IW_HEADER_TYPE_PARAM 7 /* struct iw_param */ +-#define IW_HEADER_TYPE_ADDR 8 /* struct sockaddr */ +-#define IW_HEADER_TYPE_QUAL 9 /* struct iw_quality */ ++#define IW_HEADER_TYPE_ADDR 6 /* struct sockaddr */ ++#define IW_HEADER_TYPE_POINT 8 /* struct iw_point */ ++#define IW_HEADER_TYPE_PARAM 9 /* struct iw_param */ ++#define IW_HEADER_TYPE_QUAL 10 /* struct iw_quality */ + + /* Handling flags */ + /* Most are not implemented. I just use them as a reminder of some +diff -u -p linux/net/core/wireless.14.c linux/net/core/wireless.c +--- linux/net/core/wireless.14.c Mon Dec 2 18:51:35 2002 ++++ linux/net/core/wireless.c Mon Dec 2 18:53:10 2002 +@@ -33,8 +33,16 @@ + * o Propagate events as rtnetlink IFLA_WIRELESS option + * o Generate event on selected SET requests + * +- * v4 - 18.04.01 - Jean II ++ * v4 - 18.04.02 - Jean II + * o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1 ++ * ++ * v5 - 21.06.02 - Jean II ++ * o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup) ++ * o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes ++ * o Add IWEVCUSTOM for driver specific event/scanning token ++ * o Turn on WE_STRICT_WRITE by default + kernel warning ++ * o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num) ++ * o Fix off-by-one in test (extra_size <= IFNAMSIZ) + */ + + /***************************** INCLUDES *****************************/ +@@ -50,8 +58,9 @@ + + /**************************** CONSTANTS ****************************/ + +-/* This will be turned on later on... */ +-#undef WE_STRICT_WRITE /* Check write buffer size */ ++/* Enough lenience, let's make sure things are proper... */ ++#define WE_STRICT_WRITE /* Check write buffer size */ ++/* I'll probably drop both the define and kernel message in the next version */ + + /* Debuging stuff */ + #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ +@@ -106,7 +115,7 @@ static const struct iw_ioctl_description + /* SIOCSIWSPY */ + { IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0}, + /* SIOCGIWSPY */ +- { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0}, ++ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_GET_SPY, 0}, + /* -- hole -- */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* -- hole -- */ +@@ -176,25 +185,41 @@ static const struct iw_ioctl_description + { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, + /* IWEVQUAL */ + { IW_HEADER_TYPE_QUAL, 0, 0, 0, 0, 0}, ++ /* IWEVCUSTOM */ ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_CUSTOM_MAX, 0}, ++ /* IWEVREGISTERED */ ++ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, ++ /* IWEVEXPIRED */ ++ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, + }; + static const int standard_event_num = (sizeof(standard_event) / + sizeof(struct iw_ioctl_description)); + + /* Size (in bytes) of the various private data types */ +-static const char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 }; ++static const char priv_type_size[] = { ++ 0, /* IW_PRIV_TYPE_NONE */ ++ 1, /* IW_PRIV_TYPE_BYTE */ ++ 1, /* IW_PRIV_TYPE_CHAR */ ++ 0, /* Not defined */ ++ sizeof(__u32), /* IW_PRIV_TYPE_INT */ ++ sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ ++ sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ ++ 0, /* Not defined */ ++}; + + /* Size (in bytes) of various events */ + static const int event_type_size[] = { +- IW_EV_LCP_LEN, ++ IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ ++ 0, ++ IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ + 0, +- IW_EV_CHAR_LEN, ++ IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ ++ IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ ++ IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ + 0, +- IW_EV_UINT_LEN, +- IW_EV_FREQ_LEN, + IW_EV_POINT_LEN, /* Without variable payload */ +- IW_EV_PARAM_LEN, +- IW_EV_ADDR_LEN, +- IW_EV_QUAL_LEN, ++ IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ ++ IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ + }; + + /************************ COMMON SUBROUTINES ************************/ +@@ -440,8 +465,10 @@ static inline int ioctl_export_private(s + return -EFAULT; + #ifdef WE_STRICT_WRITE + /* Check if there is enough buffer up there */ +- if(iwr->u.data.length < (SIOCIWLASTPRIV - SIOCIWFIRSTPRIV + 1)) ++ if(iwr->u.data.length < dev->wireless_handlers->num_private_args) { ++ printk(KERN_ERR "%s (WE) : Buffer for request SIOCGIWPRIV too small (%d<%d)\n", dev->name, iwr->u.data.length, dev->wireless_handlers->num_private_args); + return -E2BIG; ++ } + #endif /* WE_STRICT_WRITE */ + + /* Set the number of available ioctls. */ +@@ -471,6 +498,7 @@ static inline int ioctl_standard_call(st + const struct iw_ioctl_description * descr; + struct iw_request_info info; + int ret = -EINVAL; ++ int user_size = 0; + + /* Get the description of the IOCTL */ + if((cmd - SIOCIWFIRST) >= standard_ioctl_num) +@@ -518,11 +546,8 @@ static inline int ioctl_standard_call(st + /* Check NULL pointer */ + if(iwr->u.data.pointer == NULL) + return -EFAULT; +-#ifdef WE_STRICT_WRITE +- /* Check if there is enough buffer up there */ +- if(iwr->u.data.length < descr->max_tokens) +- return -E2BIG; +-#endif /* WE_STRICT_WRITE */ ++ /* Save user space buffer size for checking */ ++ user_size = iwr->u.data.length; + } + + #ifdef WE_IOCTL_DEBUG +@@ -559,6 +584,15 @@ static inline int ioctl_standard_call(st + + /* If we have something to return to the user */ + if (!ret && IW_IS_GET(cmd)) { ++#ifdef WE_STRICT_WRITE ++ /* Check if there is enough buffer up there */ ++ if(user_size < iwr->u.data.length) { ++ printk(KERN_ERR "%s (WE) : Buffer for request %04X too small (%d<%d)\n", dev->name, cmd, user_size, iwr->u.data.length); ++ kfree(extra); ++ return -E2BIG; ++ } ++#endif /* WE_STRICT_WRITE */ ++ + err = copy_to_user(iwr->u.data.pointer, extra, + iwr->u.data.length * + descr->token_size); +@@ -646,12 +680,18 @@ static inline int ioctl_private_call(str + /* Compute the size of the set/get arguments */ + if(descr != NULL) { + if(IW_IS_SET(cmd)) { ++ int offset = 0; /* For sub-ioctls */ ++ /* Check for sub-ioctl handler */ ++ if(descr->name[0] == '\0') ++ /* Reserve one int for sub-ioctl index */ ++ offset = sizeof(__u32); ++ + /* Size of set arguments */ + extra_size = get_priv_size(descr->set_args); + + /* Does it fits in iwr ? */ + if((descr->set_args & IW_PRIV_SIZE_FIXED) && +- (extra_size < IFNAMSIZ)) ++ ((extra_size + offset) <= IFNAMSIZ)) + extra_size = 0; + } else { + /* Size of set arguments */ +@@ -659,7 +699,7 @@ static inline int ioctl_private_call(str + + /* Does it fits in iwr ? */ + if((descr->get_args & IW_PRIV_SIZE_FIXED) && +- (extra_size < IFNAMSIZ)) ++ (extra_size <= IFNAMSIZ)) + extra_size = 0; + } + } +@@ -925,7 +965,7 @@ void wireless_send_event(struct net_devi + * The best the driver could do is to log an error message. + * We will do it ourselves instead... + */ +- printk(KERN_ERR "%s (WE) : Invalid Wireless Event (0x%04X)\n", ++ printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n", + dev->name, cmd); + return; + } diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw_handlers.w13-5.diff b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw_handlers.w13-5.diff index e69de29bb2..a27a7654a9 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw_handlers.w13-5.diff +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw_handlers.w13-5.diff @@ -0,0 +1,1513 @@ +diff -u -p -r --new-file linux/include/linux-w12/netdevice.h linux/include/linux/netdevice.h +--- linux/include/linux-w12/netdevice.h Thu Nov 22 11:47:09 2001 ++++ linux/include/linux/netdevice.h Thu Jan 17 12:00:39 2002 +@@ -278,6 +278,10 @@ struct net_device + struct net_device_stats* (*get_stats)(struct net_device *dev); + struct iw_statistics* (*get_wireless_stats)(struct net_device *dev); + ++ /* List of functions to handle Wireless Extensions (instead of ioctl). ++ * See <net/iw_handler.h> for details. Jean II */ ++ struct iw_handler_def * wireless_handlers; ++ + /* + * This marks the end of the "visible" part of the structure. All + * fields hereafter are internal to the system, and may change at +diff -u -p -r --new-file linux/include/linux-w12/wireless.h linux/include/linux/wireless.h +--- linux/include/linux-w12/wireless.h Thu Nov 22 11:47:12 2001 ++++ linux/include/linux/wireless.h Thu Jan 17 12:04:08 2002 +@@ -1,9 +1,10 @@ + /* + * This file define a set of standard wireless extensions + * +- * Version : 12 5.10.01 ++ * Version : 13 6.12.01 + * + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> ++ * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved. + */ + + #ifndef _LINUX_WIRELESS_H +@@ -11,6 +12,8 @@ + + /************************** DOCUMENTATION **************************/ + /* ++ * Initial APIs (1996 -> onward) : ++ * ----------------------------- + * Basically, the wireless extensions are for now a set of standard ioctl + * call + /proc/net/wireless + * +@@ -27,16 +30,27 @@ + * We have the list of command plus a structure descibing the + * data exchanged... + * Note that to add these ioctl, I was obliged to modify : +- * net/core/dev.c (two place + add include) +- * net/ipv4/af_inet.c (one place + add include) ++ * # net/core/dev.c (two place + add include) ++ * # net/ipv4/af_inet.c (one place + add include) + * + * /proc/net/wireless is a copy of /proc/net/dev. + * We have a structure for data passed from the driver to /proc/net/wireless + * Too add this, I've modified : +- * net/core/dev.c (two other places) +- * include/linux/netdevice.h (one place) +- * include/linux/proc_fs.h (one place) ++ * # net/core/dev.c (two other places) ++ * # include/linux/netdevice.h (one place) ++ * # include/linux/proc_fs.h (one place) ++ * ++ * New driver API (2001 -> onward) : ++ * ------------------------------- ++ * This file is only concerned with the user space API and common definitions. ++ * The new driver API is defined and documented in : ++ * # include/net/iw_handler.h + * ++ * Note as well that /proc/net/wireless implementation has now moved in : ++ * # include/linux/wireless.c ++ * ++ * Other comments : ++ * -------------- + * Do not add here things that are redundant with other mechanisms + * (drivers init, ifconfig, /proc/net/dev, ...) and with are not + * wireless specific. +@@ -54,16 +68,14 @@ + #include <linux/socket.h> /* for "struct sockaddr" et al */ + #include <linux/if.h> /* for IFNAMSIZ and co... */ + +-/**************************** CONSTANTS ****************************/ +- +-/* --------------------------- VERSION --------------------------- */ ++/***************************** VERSION *****************************/ + /* + * This constant is used to know the availability of the wireless + * extensions and to know which version of wireless extensions it is + * (there is some stuff that will be added in the future...) + * I just plan to increment with each new version. + */ +-#define WIRELESS_EXT 12 ++#define WIRELESS_EXT 13 + + /* + * Changes : +@@ -123,12 +135,20 @@ + * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space + * - Add new statistics (frag, retry, beacon) + * - Add average quality (for user space calibration) ++ * ++ * V12 to V13 ++ * ---------- ++ * - Document creation of new driver API. ++ * - Extract union iwreq_data from struct iwreq (for new driver API). ++ * - Rename SIOCSIWNAME as SIOCSIWCOMMIT + */ + ++/**************************** CONSTANTS ****************************/ ++ + /* -------------------------- IOCTL LIST -------------------------- */ + + /* Basic operations */ +-#define SIOCSIWNAME 0x8B00 /* Unused */ ++#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ + #define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ + #define SIOCSIWNWID 0x8B02 /* set network id (the cell) */ + #define SIOCGIWNWID 0x8B03 /* get network id */ +@@ -414,13 +434,49 @@ struct iw_statistics + + /* ------------------------ IOCTL REQUEST ------------------------ */ + /* ++ * This structure defines the payload of an ioctl, and is used ++ * below. ++ * ++ * Note that this structure should fit on the memory footprint ++ * of iwreq (which is the same as ifreq), which mean a max size of ++ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... ++ * You should check this when increasing the structures defined ++ * above in this file... ++ */ ++union iwreq_data ++{ ++ /* Config - generic */ ++ char name[IFNAMSIZ]; ++ /* Name : used to verify the presence of wireless extensions. ++ * Name of the protocol/provider... */ ++ ++ struct iw_point essid; /* Extended network name */ ++ struct iw_param nwid; /* network id (or domain - the cell) */ ++ struct iw_freq freq; /* frequency or channel : ++ * 0-1000 = channel ++ * > 1000 = frequency in Hz */ ++ ++ struct iw_param sens; /* signal level threshold */ ++ struct iw_param bitrate; /* default bit rate */ ++ struct iw_param txpower; /* default transmit power */ ++ struct iw_param rts; /* RTS threshold threshold */ ++ struct iw_param frag; /* Fragmentation threshold */ ++ __u32 mode; /* Operation mode */ ++ struct iw_param retry; /* Retry limits & lifetime */ ++ ++ struct iw_point encoding; /* Encoding stuff : tokens */ ++ struct iw_param power; /* PM duration/timeout */ ++ ++ struct sockaddr ap_addr; /* Access point address */ ++ ++ struct iw_point data; /* Other large parameters */ ++}; ++ ++/* + * The structure to exchange data for ioctl. + * This structure is the same as 'struct ifreq', but (re)defined for + * convenience... +- * +- * Note that it should fit on the same memory footprint ! +- * You should check this when increasing the above structures (16 octets) +- * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... ++ * Do I need to remind you about structure size (32 octets) ? + */ + struct iwreq + { +@@ -429,35 +485,8 @@ struct iwreq + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + } ifr_ifrn; + +- /* Data part */ +- union +- { +- /* Config - generic */ +- char name[IFNAMSIZ]; +- /* Name : used to verify the presence of wireless extensions. +- * Name of the protocol/provider... */ +- +- struct iw_point essid; /* Extended network name */ +- struct iw_param nwid; /* network id (or domain - the cell) */ +- struct iw_freq freq; /* frequency or channel : +- * 0-1000 = channel +- * > 1000 = frequency in Hz */ +- +- struct iw_param sens; /* signal level threshold */ +- struct iw_param bitrate; /* default bit rate */ +- struct iw_param txpower; /* default transmit power */ +- struct iw_param rts; /* RTS threshold threshold */ +- struct iw_param frag; /* Fragmentation threshold */ +- __u32 mode; /* Operation mode */ +- struct iw_param retry; /* Retry limits & lifetime */ +- +- struct iw_point encoding; /* Encoding stuff : tokens */ +- struct iw_param power; /* PM duration/timeout */ +- +- struct sockaddr ap_addr; /* Access point address */ +- +- struct iw_point data; /* Other large parameters */ +- } u; ++ /* Data part (defined just above) */ ++ union iwreq_data u; + }; + + /* -------------------------- IOCTL DATA -------------------------- */ +diff -u -p -r --new-file linux/include/net-w12/iw_handler.h linux/include/net/iw_handler.h +--- linux/include/net-w12/iw_handler.h Wed Dec 31 16:00:00 1969 ++++ linux/include/net/iw_handler.h Thu Jan 17 12:16:46 2002 +@@ -0,0 +1,374 @@ ++/* ++ * This file define the new driver API for Wireless Extensions ++ * ++ * Version : 2 6.12.01 ++ * ++ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> ++ * Copyright (c) 2001 Jean Tourrilhes, All Rights Reserved. ++ */ ++ ++#ifndef _IW_HANDLER_H ++#define _IW_HANDLER_H ++ ++/************************** DOCUMENTATION **************************/ ++/* ++ * Initial driver API (1996 -> onward) : ++ * ----------------------------------- ++ * The initial API just sends the IOCTL request received from user space ++ * to the driver (via the driver ioctl handler). The driver has to ++ * handle all the rest... ++ * ++ * The initial API also defines a specific handler in struct net_device ++ * to handle wireless statistics. ++ * ++ * The initial APIs served us well and has proven a reasonably good design. ++ * However, there is a few shortcommings : ++ * o No events, everything is a request to the driver. ++ * o Large ioctl function in driver with gigantic switch statement ++ * (i.e. spaghetti code). ++ * o Driver has to mess up with copy_to/from_user, and in many cases ++ * does it unproperly. Common mistakes are : ++ * * buffer overflows (no checks or off by one checks) ++ * * call copy_to/from_user with irq disabled ++ * o The user space interface is tied to ioctl because of the use ++ * copy_to/from_user. ++ * ++ * New driver API (2001 -> onward) : ++ * ------------------------------- ++ * The new driver API is just a bunch of standard functions (handlers), ++ * each handling a specific Wireless Extension. The driver just export ++ * the list of handler it supports, and those will be called apropriately. ++ * ++ * I tried to keep the main advantage of the previous API (simplicity, ++ * efficiency and light weight), and also I provide a good dose of backward ++ * compatibility (most structures are the same, driver can use both API ++ * simultaneously, ...). ++ * Hopefully, I've also addressed the shortcomming of the initial API. ++ * ++ * The advantage of the new API are : ++ * o Handling of Extensions in driver broken in small contained functions ++ * o Tighter checks of ioctl before calling the driver ++ * o Flexible commit strategy (at least, the start of it) ++ * o Backward compatibility (can be mixed with old API) ++ * o Driver doesn't have to worry about memory and user-space issues ++ * The last point is important for the following reasons : ++ * o You are now able to call the new driver API from any API you ++ * want (including from within other parts of the kernel). ++ * o Common mistakes are avoided (buffer overflow, user space copy ++ * with irq disabled and so on). ++ * ++ * The Drawback of the new API are : ++ * o bloat (especially kernel) ++ * o need to migrate existing drivers to new API ++ * My initial testing shows that the new API adds around 3kB to the kernel ++ * and save between 0 and 5kB from a typical driver. ++ * Also, as all structures and data types are unchanged, the migration is ++ * quite straightforward (but tedious). ++ * ++ * --- ++ * ++ * The new driver API is defined below in this file. User space should ++ * not be aware of what's happening down there... ++ * ++ * A new kernel wrapper is in charge of validating the IOCTLs and calling ++ * the appropriate driver handler. This is implemented in : ++ * # net/core/wireless.c ++ * ++ * The driver export the list of handlers in : ++ * # include/linux/netdevice.h (one place) ++ * ++ * The new driver API is available for WIRELESS_EXT >= 13. ++ * Good luck with migration to the new API ;-) ++ */ ++ ++/* ---------------------- THE IMPLEMENTATION ---------------------- */ ++/* ++ * Some of the choice I've made are pretty controversials. Defining an ++ * API is very much weighting compromises. This goes into some of the ++ * details and the thinking behind the implementation. ++ * ++ * Implementation goals : ++ * -------------------- ++ * The implementation goals were as follow : ++ * o Obvious : you should not need a PhD to understand what's happening, ++ * the benefit is easier maintainance. ++ * o Flexible : it should accomodate a wide variety of driver ++ * implementations and be as flexible as the old API. ++ * o Lean : it should be efficient memory wise to minimise the impact ++ * on kernel footprint. ++ * o Transparent to user space : the large number of user space ++ * applications that use Wireless Extensions should not need ++ * any modifications. ++ * ++ * Array of functions versus Struct of functions ++ * --------------------------------------------- ++ * 1) Having an array of functions allow the kernel code to access the ++ * handler in a single lookup, which is much more efficient (think hash ++ * table here). ++ * 2) The only drawback is that driver writer may put their handler in ++ * the wrong slot. This is trivial to test (I set the frequency, the ++ * bitrate changes). Once the handler is in the proper slot, it will be ++ * there forever, because the array is only extended at the end. ++ * 3) Backward/forward compatibility : adding new handler just require ++ * extending the array, so you can put newer driver in older kernel ++ * without having to patch the kernel code (and vice versa). ++ * ++ * All handler are of the same generic type ++ * ---------------------------------------- ++ * That's a feature !!! ++ * 1) Having a generic handler allow to have generic code, which is more ++ * efficient. If each of the handler was individually typed I would need ++ * to add a big switch in the kernel (== more bloat). This solution is ++ * more scalable, adding new Wireless Extensions doesn't add new code. ++ * 2) You can use the same handler in different slots of the array. For ++ * hardware, it may be more efficient or logical to handle multiple ++ * Wireless Extensions with a single function, and the API allow you to ++ * do that. (An example would be a single record on the card to control ++ * both bitrate and frequency, the handler would read the old record, ++ * modify it according to info->cmd and rewrite it). ++ * ++ * Functions prototype uses union iwreq_data ++ * ----------------------------------------- ++ * Some would have prefered functions defined this way : ++ * static int mydriver_ioctl_setrate(struct net_device *dev, ++ * long rate, int auto) ++ * 1) The kernel code doesn't "validate" the content of iwreq_data, and ++ * can't do it (different hardware may have different notion of what a ++ * valid frequency is), so we don't pretend that we do it. ++ * 2) The above form is not extendable. If I want to add a flag (for ++ * example to distinguish setting max rate and basic rate), I would ++ * break the prototype. Using iwreq_data is more flexible. ++ * 3) Also, the above form is not generic (see above). ++ * 4) I don't expect driver developper using the wrong field of the ++ * union (Doh !), so static typechecking doesn't add much value. ++ * 5) Lastly, you can skip the union by doing : ++ * static int mydriver_ioctl_setrate(struct net_device *dev, ++ * struct iw_request_info *info, ++ * struct iw_param *rrq, ++ * char *extra) ++ * And then adding the handler in the array like this : ++ * (iw_handler) mydriver_ioctl_setrate, // SIOCSIWRATE ++ * ++ * Using functions and not a registry ++ * ---------------------------------- ++ * Another implementation option would have been for every instance to ++ * define a registry (a struct containing all the Wireless Extensions) ++ * and only have a function to commit the registry to the hardware. ++ * 1) This approach can be emulated by the current code, but not ++ * vice versa. ++ * 2) Some drivers don't keep any configuration in the driver, for them ++ * adding such a registry would be a significant bloat. ++ * 3) The code to translate from Wireless Extension to native format is ++ * needed anyway, so it would not reduce significantely the amount of code. ++ * 4) The current approach only selectively translate Wireless Extensions ++ * to native format and only selectively set, whereas the registry approach ++ * would require to translate all WE and set all parameters for any single ++ * change. ++ * 5) For many Wireless Extensions, the GET operation return the current ++ * dynamic value, not the value that was set. ++ * ++ * This header is <net/iw_handler.h> ++ * --------------------------------- ++ * 1) This header is kernel space only and should not be exported to ++ * user space. Headers in "include/linux/" are exported, headers in ++ * "include/net/" are not. ++ * ++ * Mixed 32/64 bit issues ++ * ---------------------- ++ * The Wireless Extensions are designed to be 64 bit clean, by using only ++ * datatypes with explicit storage size. ++ * There are some issues related to kernel and user space using different ++ * memory model, and in particular 64bit kernel with 32bit user space. ++ * The problem is related to struct iw_point, that contains a pointer ++ * that *may* need to be translated. ++ * This is quite messy. The new API doesn't solve this problem (it can't), ++ * but is a step in the right direction : ++ * 1) Meta data about each ioctl is easily available, so we know what type ++ * of translation is needed. ++ * 2) The move of data between kernel and user space is only done in a single ++ * place in the kernel, so adding specific hooks in there is possible. ++ * 3) In the long term, it allows to move away from using ioctl as the ++ * user space API. ++ * ++ * So many comments and so few code ++ * -------------------------------- ++ * That's a feature. Comments won't bloat the resulting kernel binary. ++ */ ++ ++/***************************** INCLUDES *****************************/ ++ ++#include <linux/wireless.h> /* IOCTL user space API */ ++ ++/***************************** VERSION *****************************/ ++/* ++ * This constant is used to know which version of the driver API is ++ * available. Hopefully, this will be pretty stable and no changes ++ * will be needed... ++ * I just plan to increment with each new version. ++ */ ++#define IW_HANDLER_VERSION 2 ++ ++/**************************** CONSTANTS ****************************/ ++ ++/* Special error message for the driver to indicate that we ++ * should do a commit after return from the iw_handler */ ++#define EIWCOMMIT EINPROGRESS ++ ++/* Flags available in struct iw_request_info */ ++#define IW_REQUEST_FLAG_NONE 0x0000 /* No flag so far */ ++ ++/* Type of headers we know about (basically union iwreq_data) */ ++#define IW_HEADER_TYPE_NULL 0 /* Not available */ ++#define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */ ++#define IW_HEADER_TYPE_UINT 4 /* __u32 */ ++#define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */ ++#define IW_HEADER_TYPE_POINT 6 /* struct iw_point */ ++#define IW_HEADER_TYPE_PARAM 7 /* struct iw_param */ ++#define IW_HEADER_TYPE_ADDR 8 /* struct sockaddr */ ++ ++/* Handling flags */ ++/* Most are not implemented. I just use them as a reminder of some ++ * cool features we might need one day ;-) */ ++#define IW_DESCR_FLAG_NONE 0x0000 /* Obvious */ ++/* Wrapper level flags */ ++#define IW_DESCR_FLAG_DUMP 0x0001 /* Not part of the dump command */ ++#define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */ ++#define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET request is ROOT only */ ++/* Driver level flags */ ++#define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */ ++ ++/****************************** TYPES ******************************/ ++ ++/* ----------------------- WIRELESS HANDLER ----------------------- */ ++/* ++ * A wireless handler is just a standard function, that looks like the ++ * ioctl handler. ++ * We also define there how a handler list look like... As the Wireless ++ * Extension space is quite dense, we use a simple array, which is faster ++ * (that's the perfect hash table ;-). ++ */ ++ ++/* ++ * Meta data about the request passed to the iw_handler. ++ * Most handlers can safely ignore what's in there. ++ * The 'cmd' field might come handy if you want to use the same handler ++ * for multiple command... ++ * This struct is also my long term insurance. I can add new fields here ++ * without breaking the prototype of iw_handler... ++ */ ++struct iw_request_info ++{ ++ __u16 cmd; /* Wireless Extension command */ ++ __u16 flags; /* More to come ;-) */ ++}; ++ ++/* ++ * This is how a function handling a Wireless Extension should look ++ * like (both get and set, standard and private). ++ */ ++typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info, ++ union iwreq_data *wrqu, char *extra); ++ ++/* ++ * This define all the handler that the driver export. ++ * As you need only one per driver type, please use a static const ++ * shared by all driver instances... Same for the members... ++ * This will be linked from net_device in <linux/netdevice.h> ++ */ ++struct iw_handler_def ++{ ++ /* Number of handlers defined (more precisely, index of the ++ * last defined handler + 1) */ ++ __u16 num_standard; ++ __u16 num_private; ++ /* Number of private arg description */ ++ __u16 num_private_args; ++ ++ /* Array of handlers for standard ioctls ++ * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME] ++ */ ++ iw_handler * standard; ++ ++ /* Array of handlers for private ioctls ++ * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV] ++ */ ++ iw_handler * private; ++ ++ /* Arguments of private handler. This one is just a list, so you ++ * can put it in any order you want and should not leave holes... ++ * We will automatically export that to user space... */ ++ struct iw_priv_args * private_args; ++ ++ /* In the long term, get_wireless_stats will move from ++ * 'struct net_device' to here, to minimise bloat. */ ++}; ++ ++/* ----------------------- WIRELESS EVENTS ----------------------- */ ++/* ++ * Currently we don't support events, so let's just plan for the ++ * future... ++ */ ++ ++/* ++ * A Wireless Event. ++ */ ++// How do we define short header ? We don't want a flag on length. ++// Probably a flag on event ? Highest bit to zero... ++struct iw_event ++{ ++ __u16 length; /* Lenght of this stuff */ ++ __u16 event; /* Wireless IOCTL */ ++ union iwreq_data header; /* IOCTL fixed payload */ ++ char extra[0]; /* Optional IOCTL data */ ++}; ++ ++/* ---------------------- IOCTL DESCRIPTION ---------------------- */ ++/* ++ * One of the main goal of the new interface is to deal entirely with ++ * user space/kernel space memory move. ++ * For that, we need to know : ++ * o if iwreq is a pointer or contain the full data ++ * o what is the size of the data to copy ++ * ++ * For private IOCTLs, we use the same rules as used by iwpriv and ++ * defined in struct iw_priv_args. ++ * ++ * For standard IOCTLs, things are quite different and we need to ++ * use the stuctures below. Actually, this struct is also more ++ * efficient, but that's another story... ++ */ ++ ++/* ++ * Describe how a standard IOCTL looks like. ++ */ ++struct iw_ioctl_description ++{ ++ __u8 header_type; /* NULL, iw_point or other */ ++ __u8 token_type; /* Future */ ++ __u16 token_size; /* Granularity of payload */ ++ __u16 min_tokens; /* Min acceptable token number */ ++ __u16 max_tokens; /* Max acceptable token number */ ++ __u32 flags; /* Special handling of the request */ ++}; ++ ++/* Need to think of short header translation table. Later. */ ++ ++/**************************** PROTOTYPES ****************************/ ++/* ++ * Functions part of the Wireless Extensions (defined in net/core/wireless.c). ++ * Those may be called only within the kernel. ++ */ ++ ++/* First : function strictly used inside the kernel */ ++ ++/* Handle /proc/net/wireless, called in net/code/dev.c */ ++extern int dev_get_wireless_info(char * buffer, char **start, off_t offset, ++ int length); ++ ++/* Handle IOCTLs, called in net/code/dev.c */ ++extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd); ++ ++/* Second : functions that may be called by driver modules */ ++/* None yet */ ++ ++#endif /* _LINUX_WIRELESS_H */ +diff -u -p -r --new-file linux/net/core-w12/Makefile linux/net/core/Makefile +--- linux/net/core-w12/Makefile Tue Oct 30 15:08:12 2001 ++++ linux/net/core/Makefile Thu Jan 17 11:06:07 2002 +@@ -26,5 +26,8 @@ obj-$(CONFIG_NET) += dev.o dev_mcast.o d + obj-$(CONFIG_NETFILTER) += netfilter.o + obj-$(CONFIG_NET_DIVERT) += dv.o + obj-$(CONFIG_NET_PROFILE) += profile.o ++obj-$(CONFIG_NET_RADIO) += wireless.o ++# Ugly. I wish all wireless drivers were moved in drivers/net/wireless ++obj-$(CONFIG_NET_PCMCIA_RADIO) += wireless.o + + include $(TOPDIR)/Rules.make +diff -u -p -r --new-file linux/net/core-w12/dev.c linux/net/core/dev.c +--- linux/net/core-w12/dev.c Wed Nov 7 14:39:36 2001 ++++ linux/net/core/dev.c Thu Jan 17 11:06:07 2002 +@@ -102,6 +102,7 @@ + #include <linux/module.h> + #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) + #include <linux/wireless.h> /* Note : will define WIRELESS_EXT */ ++#include <net/iw_handler.h> + #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ + #ifdef CONFIG_PLIP + extern int plip_init(void); +@@ -1796,122 +1797,6 @@ static int dev_proc_stats(char *buffer, + #endif /* CONFIG_PROC_FS */ + + +-#ifdef WIRELESS_EXT +-#ifdef CONFIG_PROC_FS +- +-/* +- * Print one entry of /proc/net/wireless +- * This is a clone of /proc/net/dev (just above) +- */ +-static int sprintf_wireless_stats(char *buffer, struct net_device *dev) +-{ +- /* Get stats from the driver */ +- struct iw_statistics *stats = (dev->get_wireless_stats ? +- dev->get_wireless_stats(dev) : +- (struct iw_statistics *) NULL); +- int size; +- +- if (stats != (struct iw_statistics *) NULL) { +- size = sprintf(buffer, +- "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d %6d %6d %6d\n", +- dev->name, +- stats->status, +- stats->qual.qual, +- stats->qual.updated & 1 ? '.' : ' ', +- stats->qual.level, +- stats->qual.updated & 2 ? '.' : ' ', +- stats->qual.noise, +- stats->qual.updated & 4 ? '.' : ' ', +- stats->discard.nwid, +- stats->discard.code, +- stats->discard.fragment, +- stats->discard.retries, +- stats->discard.misc, +- stats->miss.beacon); +- stats->qual.updated = 0; +- } +- else +- size = 0; +- +- return size; +-} +- +-/* +- * Print info for /proc/net/wireless (print all entries) +- * This is a clone of /proc/net/dev (just above) +- */ +-static int dev_get_wireless_info(char * buffer, char **start, off_t offset, +- int length) +-{ +- int len = 0; +- off_t begin = 0; +- off_t pos = 0; +- int size; +- +- struct net_device * dev; +- +- size = sprintf(buffer, +- "Inter-| sta-| Quality | Discarded packets | Missed\n" +- " face | tus | link level noise | nwid crypt frag retry misc | beacon\n" +- ); +- +- pos += size; +- len += size; +- +- read_lock(&dev_base_lock); +- for (dev = dev_base; dev != NULL; dev = dev->next) { +- size = sprintf_wireless_stats(buffer + len, dev); +- len += size; +- pos = begin + len; +- +- if (pos < offset) { +- len = 0; +- begin = pos; +- } +- if (pos > offset + length) +- break; +- } +- read_unlock(&dev_base_lock); +- +- *start = buffer + (offset - begin); /* Start of wanted data */ +- len -= (offset - begin); /* Start slop */ +- if (len > length) +- len = length; /* Ending slop */ +- if (len < 0) +- len = 0; +- +- return len; +-} +-#endif /* CONFIG_PROC_FS */ +- +-/* +- * Allow programatic access to /proc/net/wireless even if /proc +- * doesn't exist... Also more efficient... +- */ +-static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr) +-{ +- /* Get stats from the driver */ +- struct iw_statistics *stats = (dev->get_wireless_stats ? +- dev->get_wireless_stats(dev) : +- (struct iw_statistics *) NULL); +- +- if (stats != (struct iw_statistics *) NULL) { +- struct iwreq * wrq = (struct iwreq *)ifr; +- +- /* Copy statistics to the user buffer */ +- if(copy_to_user(wrq->u.data.pointer, stats, +- sizeof(struct iw_statistics))) +- return -EFAULT; +- +- /* Check if we need to clear the update flag */ +- if(wrq->u.data.flags != 0) +- stats->qual.updated = 0; +- return(0); +- } else +- return -EOPNOTSUPP; +-} +-#endif /* WIRELESS_EXT */ +- + /** + * netdev_set_master - set up master/slave pair + * @slave: slave device +@@ -2209,11 +2094,6 @@ static int dev_ifsioc(struct ifreq *ifr, + notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); + return 0; + +-#ifdef WIRELESS_EXT +- case SIOCGIWSTATS: +- return dev_iwstats(dev, ifr); +-#endif /* WIRELESS_EXT */ +- + /* + * Unknown or private ioctl + */ +@@ -2239,17 +2119,6 @@ static int dev_ifsioc(struct ifreq *ifr, + return -EOPNOTSUPP; + } + +-#ifdef WIRELESS_EXT +- if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { +- if (dev->do_ioctl) { +- if (!netif_device_present(dev)) +- return -ENODEV; +- return dev->do_ioctl(dev, ifr, cmd); +- } +- return -EOPNOTSUPP; +- } +-#endif /* WIRELESS_EXT */ +- + } + return -EINVAL; + } +@@ -2431,7 +2300,8 @@ int dev_ioctl(unsigned int cmd, void *ar + } + dev_load(ifr.ifr_name); + rtnl_lock(); +- ret = dev_ifsioc(&ifr, cmd); ++ /* Follow me in net/core/wireless.c */ ++ ret = wireless_process_ioctl(&ifr, cmd); + rtnl_unlock(); + if (!ret && IW_IS_GET(cmd) && + copy_to_user(arg, &ifr, sizeof(struct ifreq))) +@@ -2856,6 +2726,7 @@ int __init net_dev_init(void) + proc_net_create("dev", 0, dev_get_info); + create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL); + #ifdef WIRELESS_EXT ++ /* Available in net/core/wireless.c */ + proc_net_create("wireless", 0, dev_get_wireless_info); + #endif /* WIRELESS_EXT */ + #endif /* CONFIG_PROC_FS */ +diff -u -p -r --new-file linux/net/core-w12/wireless.c linux/net/core/wireless.c +--- linux/net/core-w12/wireless.c Wed Dec 31 16:00:00 1969 ++++ linux/net/core/wireless.c Mon Jan 21 11:13:23 2002 +@@ -0,0 +1,733 @@ ++/* ++ * This file implement the Wireless Extensions APIs. ++ * ++ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> ++ * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved. ++ * ++ * (As all part of the Linux kernel, this file is GPL) ++ */ ++ ++/************************** DOCUMENTATION **************************/ ++/* ++ * API definition : ++ * -------------- ++ * See <linux/wireless.h> for details of the APIs and the rest. ++ * ++ * History : ++ * ------- ++ * ++ * v1 - 5.12.01 - Jean II ++ * o Created this file. ++ * ++ * v2 - 13.12.01 - Jean II ++ * o Move /proc/net/wireless stuff from net/core/dev.c to here ++ * o Make Wireless Extension IOCTLs go through here ++ * o Added iw_handler handling ;-) ++ * o Added standard ioctl description ++ * o Initial dumb commit strategy based on orinoco.c ++ */ ++ ++/***************************** INCLUDES *****************************/ ++ ++#include <asm/uaccess.h> /* copy_to_user() */ ++#include <linux/config.h> /* Not needed ??? */ ++#include <linux/types.h> /* off_t */ ++#include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */ ++ ++#include <linux/wireless.h> /* Pretty obvious */ ++#include <net/iw_handler.h> /* New driver API */ ++ ++/**************************** CONSTANTS ****************************/ ++ ++/* This will be turned on later on... */ ++#undef WE_STRICT_WRITE /* Check write buffer size */ ++ ++/* Debuging stuff */ ++#undef WE_IOCTL_DEBUG /* Debug IOCTL API */ ++ ++/************************* GLOBAL VARIABLES *************************/ ++/* ++ * You should not use global variables, because or re-entrancy. ++ * On our case, it's only const, so it's OK... ++ */ ++static const struct iw_ioctl_description standard_ioctl[] = { ++ /* SIOCSIWCOMMIT (internal) */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* SIOCGIWNAME */ ++ { IW_HEADER_TYPE_CHAR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, ++ /* SIOCSIWNWID */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT}, ++ /* SIOCGIWNWID */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, ++ /* SIOCSIWFREQ */ ++ { IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT}, ++ /* SIOCGIWFREQ */ ++ { IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, ++ /* SIOCSIWMODE */ ++ { IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT}, ++ /* SIOCGIWMODE */ ++ { IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, ++ /* SIOCSIWSENS */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCGIWSENS */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCSIWRANGE */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* SIOCGIWRANGE */ ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_range), IW_DESCR_FLAG_DUMP}, ++ /* SIOCSIWPRIV */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* SIOCGIWPRIV (handled directly by us) */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* SIOCSIWSTATS */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* SIOCGIWSTATS (handled directly by us) */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, ++ /* SIOCSIWSPY */ ++ { IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0}, ++ /* SIOCGIWSPY */ ++ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0}, ++ /* -- hole -- */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* -- hole -- */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* SIOCSIWAP */ ++ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, ++ /* SIOCGIWAP */ ++ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, ++ /* -- hole -- */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* SIOCGIWAPLIST */ ++ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0}, ++ /* -- hole -- */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* -- hole -- */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* SIOCSIWESSID */ ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_EVENT}, ++ /* SIOCGIWESSID */ ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_DUMP}, ++ /* SIOCSIWNICKN */ ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0}, ++ /* SIOCGIWNICKN */ ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0}, ++ /* -- hole -- */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* -- hole -- */ ++ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* SIOCSIWRATE */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCGIWRATE */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCSIWRTS */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCGIWRTS */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCSIWFRAG */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCGIWFRAG */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCSIWTXPOW */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCGIWTXPOW */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCSIWRETRY */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCGIWRETRY */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCSIWENCODE */ ++ { IW_HEADER_TYPE_POINT, 4, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT}, ++ /* SIOCGIWENCODE */ ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT}, ++ /* SIOCSIWPOWER */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCGIWPOWER */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++}; ++ ++/* Size (in bytes) of the various private data types */ ++char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 }; ++ ++/************************ COMMON SUBROUTINES ************************/ ++/* ++ * Stuff that may be used in various place or doesn't fit in one ++ * of the section below. ++ */ ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Return the driver handler associated with a specific Wireless Extension. ++ * Called from various place, so make sure it remains efficient. ++ */ ++static inline iw_handler get_handler(struct net_device *dev, ++ unsigned int cmd) ++{ ++ unsigned int index; /* MUST be unsigned */ ++ ++ /* Check if we have some wireless handlers defined */ ++ if(dev->wireless_handlers == NULL) ++ return NULL; ++ ++ /* Try as a standard command */ ++ index = cmd - SIOCIWFIRST; ++ if(index < dev->wireless_handlers->num_standard) ++ return dev->wireless_handlers->standard[index]; ++ ++ /* Try as a private command */ ++ index = cmd - SIOCIWFIRSTPRIV; ++ if(index < dev->wireless_handlers->num_private) ++ return dev->wireless_handlers->private[index]; ++ ++ /* Not found */ ++ return NULL; ++} ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Get statistics out of the driver ++ */ ++static inline struct iw_statistics *get_wireless_stats(struct net_device *dev) ++{ ++ return (dev->get_wireless_stats ? ++ dev->get_wireless_stats(dev) : ++ (struct iw_statistics *) NULL); ++ /* In the future, get_wireless_stats may move from 'struct net_device' ++ * to 'struct iw_handler_def', to de-bloat struct net_device. ++ * Definitely worse a thought... */ ++} ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Call the commit handler in the driver ++ * (if exist and if conditions are right) ++ * ++ * Note : our current commit strategy is currently pretty dumb, ++ * but we will be able to improve on that... ++ * The goal is to try to agreagate as many changes as possible ++ * before doing the commit. Drivers that will define a commit handler ++ * are usually those that need a reset after changing parameters, so ++ * we want to minimise the number of reset. ++ * A cool idea is to use a timer : at each "set" command, we re-set the ++ * timer, when the timer eventually fires, we call the driver. ++ * Hopefully, more on that later. ++ * ++ * Also, I'm waiting to see how many people will complain about the ++ * netif_running(dev) test. I'm open on that one... ++ * Hopefully, the driver will remember to do a commit in "open()" ;-) ++ */ ++static inline int call_commit_handler(struct net_device * dev) ++{ ++ if((netif_running(dev)) && ++ (dev->wireless_handlers->standard[0] != NULL)) { ++ /* Call the commit handler on the driver */ ++ return dev->wireless_handlers->standard[0](dev, NULL, ++ NULL, NULL); ++ } else ++ return 0; /* Command completed successfully */ ++} ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Number of private arguments ++ */ ++static inline int get_priv_size(__u16 args) ++{ ++ int num = args & IW_PRIV_SIZE_MASK; ++ int type = (args & IW_PRIV_TYPE_MASK) >> 12; ++ ++ return num * priv_type_size[type]; ++} ++ ++ ++/******************** /proc/net/wireless SUPPORT ********************/ ++/* ++ * The /proc/net/wireless file is a human readable user-space interface ++ * exporting various wireless specific statistics from the wireless devices. ++ * This is the most popular part of the Wireless Extensions ;-) ++ * ++ * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). ++ * The content of the file is basically the content of "struct iw_statistics". ++ */ ++ ++#ifdef CONFIG_PROC_FS ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Print one entry (line) of /proc/net/wireless ++ */ ++static inline int sprintf_wireless_stats(char *buffer, struct net_device *dev) ++{ ++ /* Get stats from the driver */ ++ struct iw_statistics *stats; ++ int size; ++ ++ stats = get_wireless_stats(dev); ++ if (stats != (struct iw_statistics *) NULL) { ++ size = sprintf(buffer, ++ "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d %6d %6d %6d\n", ++ dev->name, ++ stats->status, ++ stats->qual.qual, ++ stats->qual.updated & 1 ? '.' : ' ', ++ stats->qual.level, ++ stats->qual.updated & 2 ? '.' : ' ', ++ stats->qual.noise, ++ stats->qual.updated & 4 ? '.' : ' ', ++ stats->discard.nwid, ++ stats->discard.code, ++ stats->discard.fragment, ++ stats->discard.retries, ++ stats->discard.misc, ++ stats->miss.beacon); ++ stats->qual.updated = 0; ++ } ++ else ++ size = 0; ++ ++ return size; ++} ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Print info for /proc/net/wireless (print all entries) ++ */ ++int dev_get_wireless_info(char * buffer, char **start, off_t offset, ++ int length) ++{ ++ int len = 0; ++ off_t begin = 0; ++ off_t pos = 0; ++ int size; ++ ++ struct net_device * dev; ++ ++ size = sprintf(buffer, ++ "Inter-| sta-| Quality | Discarded packets | Missed\n" ++ " face | tus | link level noise | nwid crypt frag retry misc | beacon\n" ++ ); ++ ++ pos += size; ++ len += size; ++ ++ read_lock(&dev_base_lock); ++ for (dev = dev_base; dev != NULL; dev = dev->next) { ++ size = sprintf_wireless_stats(buffer + len, dev); ++ len += size; ++ pos = begin + len; ++ ++ if (pos < offset) { ++ len = 0; ++ begin = pos; ++ } ++ if (pos > offset + length) ++ break; ++ } ++ read_unlock(&dev_base_lock); ++ ++ *start = buffer + (offset - begin); /* Start of wanted data */ ++ len -= (offset - begin); /* Start slop */ ++ if (len > length) ++ len = length; /* Ending slop */ ++ if (len < 0) ++ len = 0; ++ ++ return len; ++} ++#endif /* CONFIG_PROC_FS */ ++ ++/************************** IOCTL SUPPORT **************************/ ++/* ++ * The original user space API to configure all those Wireless Extensions ++ * is through IOCTLs. ++ * In there, we check if we need to call the new driver API (iw_handler) ++ * or just call the driver ioctl handler. ++ */ ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Allow programatic access to /proc/net/wireless even if /proc ++ * doesn't exist... Also more efficient... ++ */ ++static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr) ++{ ++ /* Get stats from the driver */ ++ struct iw_statistics *stats; ++ ++ stats = get_wireless_stats(dev); ++ if (stats != (struct iw_statistics *) NULL) { ++ struct iwreq * wrq = (struct iwreq *)ifr; ++ ++ /* Copy statistics to the user buffer */ ++ if(copy_to_user(wrq->u.data.pointer, stats, ++ sizeof(struct iw_statistics))) ++ return -EFAULT; ++ ++ /* Check if we need to clear the update flag */ ++ if(wrq->u.data.flags != 0) ++ stats->qual.updated = 0; ++ return 0; ++ } else ++ return -EOPNOTSUPP; ++} ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Export the driver private handler definition ++ * They will be picked up by tools like iwpriv... ++ */ ++static inline int ioctl_export_private(struct net_device * dev, ++ struct ifreq * ifr) ++{ ++ struct iwreq * iwr = (struct iwreq *) ifr; ++ ++ /* Check if the driver has something to export */ ++ if((dev->wireless_handlers->num_private_args == 0) || ++ (dev->wireless_handlers->private_args == NULL)) ++ return -EOPNOTSUPP; ++ ++ /* Check NULL pointer */ ++ if(iwr->u.data.pointer == NULL) ++ return -EFAULT; ++#ifdef WE_STRICT_WRITE ++ /* Check if there is enough buffer up there */ ++ if(iwr->u.data.length < (SIOCIWLASTPRIV - SIOCIWFIRSTPRIV + 1)) ++ return -E2BIG; ++#endif /* WE_STRICT_WRITE */ ++ ++ /* Set the number of available ioctls. */ ++ iwr->u.data.length = dev->wireless_handlers->num_private_args; ++ ++ /* Copy structure to the user buffer. */ ++ if (copy_to_user(iwr->u.data.pointer, ++ dev->wireless_handlers->private_args, ++ sizeof(struct iw_priv_args) * iwr->u.data.length)) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Wrapper to call a standard Wireless Extension handler. ++ * We do various checks and also take care of moving data between ++ * user space and kernel space. ++ */ ++static inline int ioctl_standard_call(struct net_device * dev, ++ struct ifreq * ifr, ++ unsigned int cmd, ++ iw_handler handler) ++{ ++ struct iwreq * iwr = (struct iwreq *) ifr; ++ const struct iw_ioctl_description * descr; ++ struct iw_request_info info; ++ int ret = -EINVAL; ++ ++ /* Get the description of the IOCTL */ ++ descr = &(standard_ioctl[cmd - SIOCIWFIRST]); ++ ++#ifdef WE_IOCTL_DEBUG ++ printk(KERN_DEBUG "%s : Found standard handler for 0x%04X\n", ++ ifr->ifr_name, cmd); ++ printk(KERN_DEBUG "Header type : %d, token type : %d, token_size : %d, max_token : %d\n", descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); ++#endif /* WE_IOCTL_DEBUG */ ++ ++ /* Prepare the call */ ++ info.cmd = cmd; ++ info.flags = 0; ++ ++ /* Check if we have a pointer to user space data or not */ ++ if(descr->header_type != IW_HEADER_TYPE_POINT) { ++ /* No extra arguments. Trivial to handle */ ++ ret = handler(dev, &info, &(iwr->u), NULL); ++ } else { ++ char * extra; ++ int err; ++ ++ /* Check what user space is giving us */ ++ if(IW_IS_SET(cmd)) { ++ /* Check NULL pointer */ ++ if((iwr->u.data.pointer == NULL) && ++ (iwr->u.data.length != 0)) ++ return -EFAULT; ++ /* Check if number of token fits within bounds */ ++ if(iwr->u.data.length > descr->max_tokens) ++ return -E2BIG; ++ if(iwr->u.data.length < descr->min_tokens) ++ return -EINVAL; ++ } else { ++ /* Check NULL pointer */ ++ if(iwr->u.data.pointer == NULL) ++ return -EFAULT; ++#ifdef WE_STRICT_WRITE ++ /* Check if there is enough buffer up there */ ++ if(iwr->u.data.length < descr->max_tokens) ++ return -E2BIG; ++#endif /* WE_STRICT_WRITE */ ++ } ++ ++#ifdef WE_IOCTL_DEBUG ++ printk(KERN_DEBUG "Malloc %d bytes\n", ++ descr->max_tokens * descr->token_size); ++#endif /* WE_IOCTL_DEBUG */ ++ ++ /* Always allocate for max space. Easier, and won't last ++ * long... */ ++ extra = kmalloc(descr->max_tokens * descr->token_size, ++ GFP_KERNEL); ++ if (extra == NULL) { ++ return -ENOMEM; ++ } ++ ++ /* If it is a SET, get all the extra data in here */ ++ if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { ++ err = copy_from_user(extra, iwr->u.data.pointer, ++ iwr->u.data.length * ++ descr->token_size); ++ if (err) { ++ kfree(extra); ++ return -EFAULT; ++ } ++#ifdef WE_IOCTL_DEBUG ++ printk(KERN_DEBUG "Got %d bytes\n", ++ iwr->u.data.length * descr->token_size); ++#endif /* WE_IOCTL_DEBUG */ ++ } ++ ++ /* Call the handler */ ++ ret = handler(dev, &info, &(iwr->u), extra); ++ ++ /* If we have something to return to the user */ ++ if (!ret && IW_IS_GET(cmd)) { ++ err = copy_to_user(iwr->u.data.pointer, extra, ++ iwr->u.data.length * ++ descr->token_size); ++ if (err) ++ ret = -EFAULT; ++#ifdef WE_IOCTL_DEBUG ++ printk(KERN_DEBUG "Wrote %d bytes\n", ++ iwr->u.data.length * descr->token_size); ++#endif /* WE_IOCTL_DEBUG */ ++ } ++ ++ /* Cleanup - I told you it wasn't that long ;-) */ ++ kfree(extra); ++ } ++ ++ /* Call commit handler if needed and defined */ ++ if(ret == -EIWCOMMIT) ++ ret = call_commit_handler(dev); ++ ++ /* Here, we will generate the appropriate event if needed */ ++ ++ return ret; ++} ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Wrapper to call a private Wireless Extension handler. ++ * We do various checks and also take care of moving data between ++ * user space and kernel space. ++ * It's not as nice and slimline as the standard wrapper. The cause ++ * is struct iw_priv_args, which was not really designed for the ++ * job we are going here. ++ * ++ * IMPORTANT : This function prevent to set and get data on the same ++ * IOCTL and enforce the SET/GET convention. Not doing it would be ++ * far too hairy... ++ * If you need to set and get data at the same time, please don't use ++ * a iw_handler but process it in your ioctl handler (i.e. use the ++ * old driver API). ++ */ ++static inline int ioctl_private_call(struct net_device * dev, ++ struct ifreq * ifr, ++ unsigned int cmd, ++ iw_handler handler) ++{ ++ struct iwreq * iwr = (struct iwreq *) ifr; ++ struct iw_priv_args * descr = NULL; ++ struct iw_request_info info; ++ int extra_size = 0; ++ int i; ++ int ret = -EINVAL; ++ ++ /* Get the description of the IOCTL */ ++ for(i = 0; i < dev->wireless_handlers->num_private_args; i++) ++ if(cmd == dev->wireless_handlers->private_args[i].cmd) { ++ descr = &(dev->wireless_handlers->private_args[i]); ++ break; ++ } ++ ++#ifdef WE_IOCTL_DEBUG ++ printk(KERN_DEBUG "%s : Found private handler for 0x%04X\n", ++ ifr->ifr_name, cmd); ++ if(descr) { ++ printk(KERN_DEBUG "Name %s, set %X, get %X\n", ++ descr->name, descr->set_args, descr->get_args); ++ } ++#endif /* WE_IOCTL_DEBUG */ ++ ++ /* Compute the size of the set/get arguments */ ++ if(descr != NULL) { ++ if(IW_IS_SET(cmd)) { ++ /* Size of set arguments */ ++ extra_size = get_priv_size(descr->set_args); ++ ++ /* Does it fits in iwr ? */ ++ if((descr->set_args & IW_PRIV_SIZE_FIXED) && ++ (extra_size < IFNAMSIZ)) ++ extra_size = 0; ++ } else { ++ /* Size of set arguments */ ++ extra_size = get_priv_size(descr->get_args); ++ ++ /* Does it fits in iwr ? */ ++ if((descr->get_args & IW_PRIV_SIZE_FIXED) && ++ (extra_size < IFNAMSIZ)) ++ extra_size = 0; ++ } ++ } ++ ++ /* Prepare the call */ ++ info.cmd = cmd; ++ info.flags = 0; ++ ++ /* Check if we have a pointer to user space data or not. */ ++ if(extra_size == 0) { ++ /* No extra arguments. Trivial to handle */ ++ ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); ++ } else { ++ char * extra; ++ int err; ++ ++ /* Check what user space is giving us */ ++ if(IW_IS_SET(cmd)) { ++ /* Check NULL pointer */ ++ if((iwr->u.data.pointer == NULL) && ++ (iwr->u.data.length != 0)) ++ return -EFAULT; ++ ++ /* Does it fits within bounds ? */ ++ if(iwr->u.data.length > (descr->set_args & ++ IW_PRIV_SIZE_MASK)) ++ return -E2BIG; ++ } else { ++ /* Check NULL pointer */ ++ if(iwr->u.data.pointer == NULL) ++ return -EFAULT; ++ } ++ ++#ifdef WE_IOCTL_DEBUG ++ printk(KERN_DEBUG "Malloc %d bytes\n", extra_size); ++#endif /* WE_IOCTL_DEBUG */ ++ ++ /* Always allocate for max space. Easier, and won't last ++ * long... */ ++ extra = kmalloc(extra_size, GFP_KERNEL); ++ if (extra == NULL) { ++ return -ENOMEM; ++ } ++ ++ /* If it is a SET, get all the extra data in here */ ++ if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { ++ err = copy_from_user(extra, iwr->u.data.pointer, ++ extra_size); ++ if (err) { ++ kfree(extra); ++ return -EFAULT; ++ } ++#ifdef WE_IOCTL_DEBUG ++ printk(KERN_DEBUG "Got %d elem\n", iwr->u.data.length); ++#endif /* WE_IOCTL_DEBUG */ ++ } ++ ++ /* Call the handler */ ++ ret = handler(dev, &info, &(iwr->u), extra); ++ ++ /* If we have something to return to the user */ ++ if (!ret && IW_IS_GET(cmd)) { ++ err = copy_to_user(iwr->u.data.pointer, extra, ++ extra_size); ++ if (err) ++ ret = -EFAULT; ++#ifdef WE_IOCTL_DEBUG ++ printk(KERN_DEBUG "Wrote %d elem\n", ++ iwr->u.data.length); ++#endif /* WE_IOCTL_DEBUG */ ++ } ++ ++ /* Cleanup - I told you it wasn't that long ;-) */ ++ kfree(extra); ++ } ++ ++ ++ /* Call commit handler if needed and defined */ ++ if(ret == -EIWCOMMIT) ++ ret = call_commit_handler(dev); ++ ++ return ret; ++} ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Main IOCTl dispatcher. Called from the main networking code ++ * (dev_ioctl() in net/core/dev.c). ++ * Check the type of IOCTL and call the appropriate wrapper... ++ */ ++int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd) ++{ ++ struct net_device *dev; ++ iw_handler handler; ++ ++ /* Permissions are already checked in dev_ioctl() before calling us. ++ * The copy_to/from_user() of ifr is also dealt with in there */ ++ ++ /* Make sure the device exist */ ++ if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL) ++ return -ENODEV; ++ ++ /* A bunch of special cases, then the generic case... ++ * Note that 'cmd' is already filtered in dev_ioctl() with ++ * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ ++ switch(cmd) ++ { ++ case SIOCGIWSTATS: ++ /* Get Wireless Stats */ ++ return dev_iwstats(dev, ifr); ++ ++ case SIOCGIWPRIV: ++ /* Check if we have some wireless handlers defined */ ++ if(dev->wireless_handlers != NULL) { ++ /* We export to user space the definition of ++ * the private handler ourselves */ ++ return ioctl_export_private(dev, ifr); ++ } ++ // ## Fall-through for old API ## ++ default: ++ /* Generic IOCTL */ ++ /* Basic check */ ++ if (!netif_device_present(dev)) ++ return -ENODEV; ++ /* New driver API : try to find the handler */ ++ handler = get_handler(dev, cmd); ++ if(handler != NULL) { ++ /* Standard and private are not the same */ ++ if(cmd < SIOCIWFIRSTPRIV) ++ return ioctl_standard_call(dev, ++ ifr, ++ cmd, ++ handler); ++ else ++ return ioctl_private_call(dev, ++ ifr, ++ cmd, ++ handler); ++ } ++ /* Old driver API : call driver ioctl handler */ ++ if (dev->do_ioctl) { ++ return dev->do_ioctl(dev, ifr, cmd); ++ } ++ return -EOPNOTSUPP; ++ } ++ /* Not reached */ ++ return -EINVAL; ++} diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw_handlers.w14-5.diff b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw_handlers.w14-5.diff index e69de29bb2..539b160068 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw_handlers.w14-5.diff +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/iw_handlers.w14-5.diff @@ -0,0 +1,838 @@ +diff -u -p -r --new-file linux/include/linux-w13/rtnetlink.h linux/include/linux/rtnetlink.h +--- linux/include/linux-w13/rtnetlink.h Thu Jun 6 14:44:08 2002 ++++ linux/include/linux/rtnetlink.h Thu Jun 6 15:47:44 2002 +@@ -440,12 +440,14 @@ enum + #define IFLA_COST IFLA_COST + IFLA_PRIORITY, + #define IFLA_PRIORITY IFLA_PRIORITY +- IFLA_MASTER ++ IFLA_MASTER, + #define IFLA_MASTER IFLA_MASTER ++ IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ ++#define IFLA_WIRELESS IFLA_WIRELESS + }; + + +-#define IFLA_MAX IFLA_MASTER ++#define IFLA_MAX IFLA_WIRELESS + + #define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) + #define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) +diff -u -p -r --new-file linux/include/linux-w13/wireless.h linux/include/linux/wireless.h +--- linux/include/linux-w13/wireless.h Thu Jun 6 15:00:28 2002 ++++ linux/include/linux/wireless.h Thu Jun 6 15:47:44 2002 +@@ -1,10 +1,10 @@ + /* + * This file define a set of standard wireless extensions + * +- * Version : 13 6.12.01 ++ * Version : 14 25.1.02 + * + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> +- * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved. ++ * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. + */ + + #ifndef _LINUX_WIRELESS_H +@@ -40,7 +40,7 @@ + * # include/linux/netdevice.h (one place) + * # include/linux/proc_fs.h (one place) + * +- * New driver API (2001 -> onward) : ++ * New driver API (2002 -> onward) : + * ------------------------------- + * This file is only concerned with the user space API and common definitions. + * The new driver API is defined and documented in : +@@ -49,6 +49,11 @@ + * Note as well that /proc/net/wireless implementation has now moved in : + * # include/linux/wireless.c + * ++ * Wireless Events (2002 -> onward) : ++ * -------------------------------- ++ * Events are defined at the end of this file, and implemented in : ++ * # include/linux/wireless.c ++ * + * Other comments : + * -------------- + * Do not add here things that are redundant with other mechanisms +@@ -75,7 +80,7 @@ + * (there is some stuff that will be added in the future...) + * I just plan to increment with each new version. + */ +-#define WIRELESS_EXT 13 ++#define WIRELESS_EXT 14 + + /* + * Changes : +@@ -141,6 +146,13 @@ + * - Document creation of new driver API. + * - Extract union iwreq_data from struct iwreq (for new driver API). + * - Rename SIOCSIWNAME as SIOCSIWCOMMIT ++ * ++ * V13 to V14 ++ * ---------- ++ * - Wireless Events support : define struct iw_event ++ * - Define additional specific event numbers ++ * - Add "addr" and "param" fields in union iwreq_data ++ * - AP scanning stuff (SIOCSIWSCAN and friends) + */ + + /**************************** CONSTANTS ****************************/ +@@ -175,6 +187,8 @@ + #define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ + #define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ + #define SIOCGIWAPLIST 0x8B17 /* get list of access point in range */ ++#define SIOCSIWSCAN 0x8B18 /* trigger scanning */ ++#define SIOCGIWSCAN 0x8B19 /* get scanning results */ + + /* 802.11 specific support */ + #define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ +@@ -238,6 +252,15 @@ + #define IW_IS_SET(cmd) (!((cmd) & 0x1)) + #define IW_IS_GET(cmd) ((cmd) & 0x1) + ++/* ----------------------- WIRELESS EVENTS ----------------------- */ ++/* Those are *NOT* ioctls, do not issue request on them !!! */ ++/* Most events use the same identifier as ioctl requests */ ++ ++#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ ++#define IWEVQUAL 0x8C01 /* Quality part of statistics */ ++ ++#define IWEVFIRST 0x8C00 ++ + /* ------------------------- PRIVATE INFO ------------------------- */ + /* + * The following is used with SIOCGIWPRIV. It allow a driver to define +@@ -340,6 +363,19 @@ + #define IW_RETRY_MAX 0x0002 /* Value is a maximum */ + #define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + ++/* Scanning request flags */ ++#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ ++#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ ++#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ ++#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ ++#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ ++#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ ++#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ ++#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ ++#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ ++/* Maximum size of returned data */ ++#define IW_SCAN_MAX_DATA 4096 /* In bytes */ ++ + /****************************** TYPES ******************************/ + + /* --------------------------- SUBTYPES --------------------------- */ +@@ -466,9 +502,12 @@ union iwreq_data + + struct iw_point encoding; /* Encoding stuff : tokens */ + struct iw_param power; /* PM duration/timeout */ ++ struct iw_quality qual; /* Quality part of statistics */ + + struct sockaddr ap_addr; /* Access point address */ ++ struct sockaddr addr; /* Destination address (hw) */ + ++ struct iw_param param; /* Other small parameters */ + struct iw_point data; /* Other large parameters */ + }; + +@@ -595,5 +634,36 @@ struct iw_priv_args + __u16 get_args; /* Type and number of args */ + char name[IFNAMSIZ]; /* Name of the extension */ + }; ++ ++/* ----------------------- WIRELESS EVENTS ----------------------- */ ++/* ++ * Wireless events are carried through the rtnetlink socket to user ++ * space. They are encapsulated in the IFLA_WIRELESS field of ++ * a RTM_NEWLINK message. ++ */ ++ ++/* ++ * A Wireless Event. Contains basically the same data as the ioctl... ++ */ ++struct iw_event ++{ ++ __u16 len; /* Real lenght of this stuff */ ++ __u16 cmd; /* Wireless IOCTL */ ++ union iwreq_data u; /* IOCTL fixed payload */ ++}; ++ ++/* Size of the Event prefix (including padding and alignement junk) */ ++#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) ++/* Size of the various events */ ++#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) ++#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) ++#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) ++#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point)) ++#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) ++#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr)) ++#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) ++ ++/* Note : in the case of iw_point, the extra data will come at the ++ * end of the event */ + + #endif /* _LINUX_WIRELESS_H */ +diff -u -p -r --new-file linux/include/net-w13/iw_handler.h linux/include/net/iw_handler.h +--- linux/include/net-w13/iw_handler.h Thu Jun 6 15:06:16 2002 ++++ linux/include/net/iw_handler.h Thu Jun 6 15:48:06 2002 +@@ -1,10 +1,10 @@ + /* + * This file define the new driver API for Wireless Extensions + * +- * Version : 2 6.12.01 ++ * Version : 3 17.1.02 + * + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> +- * Copyright (c) 2001 Jean Tourrilhes, All Rights Reserved. ++ * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved. + */ + + #ifndef _IW_HANDLER_H +@@ -33,7 +33,7 @@ + * o The user space interface is tied to ioctl because of the use + * copy_to/from_user. + * +- * New driver API (2001 -> onward) : ++ * New driver API (2002 -> onward) : + * ------------------------------- + * The new driver API is just a bunch of standard functions (handlers), + * each handling a specific Wireless Extension. The driver just export +@@ -206,7 +206,18 @@ + * will be needed... + * I just plan to increment with each new version. + */ +-#define IW_HANDLER_VERSION 2 ++#define IW_HANDLER_VERSION 3 ++ ++/* ++ * Changes : ++ * ++ * V2 to V3 ++ * -------- ++ * - Move event definition in <linux/wireless.h> ++ * - Add Wireless Event support : ++ * o wireless_send_event() prototype ++ * o iwe_stream_add_event/point() inline functions ++ */ + + /**************************** CONSTANTS ****************************/ + +@@ -225,6 +236,7 @@ + #define IW_HEADER_TYPE_POINT 6 /* struct iw_point */ + #define IW_HEADER_TYPE_PARAM 7 /* struct iw_param */ + #define IW_HEADER_TYPE_ADDR 8 /* struct sockaddr */ ++#define IW_HEADER_TYPE_QUAL 9 /* struct iw_quality */ + + /* Handling flags */ + /* Most are not implemented. I just use them as a reminder of some +@@ -233,7 +245,8 @@ + /* Wrapper level flags */ + #define IW_DESCR_FLAG_DUMP 0x0001 /* Not part of the dump command */ + #define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */ +-#define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET request is ROOT only */ ++#define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET : request is ROOT only */ ++ /* SET : Omit payload from generated iwevent */ + /* Driver level flags */ + #define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */ + +@@ -303,25 +316,6 @@ struct iw_handler_def + * 'struct net_device' to here, to minimise bloat. */ + }; + +-/* ----------------------- WIRELESS EVENTS ----------------------- */ +-/* +- * Currently we don't support events, so let's just plan for the +- * future... +- */ +- +-/* +- * A Wireless Event. +- */ +-// How do we define short header ? We don't want a flag on length. +-// Probably a flag on event ? Highest bit to zero... +-struct iw_event +-{ +- __u16 length; /* Lenght of this stuff */ +- __u16 event; /* Wireless IOCTL */ +- union iwreq_data header; /* IOCTL fixed payload */ +- char extra[0]; /* Optional IOCTL data */ +-}; +- + /* ---------------------- IOCTL DESCRIPTION ---------------------- */ + /* + * One of the main goal of the new interface is to deal entirely with +@@ -369,6 +363,88 @@ extern int dev_get_wireless_info(char * + extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd); + + /* Second : functions that may be called by driver modules */ +-/* None yet */ + +-#endif /* _LINUX_WIRELESS_H */ ++/* Send a single event to user space */ ++extern void wireless_send_event(struct net_device * dev, ++ unsigned int cmd, ++ union iwreq_data * wrqu, ++ char * extra); ++ ++/* We may need a function to send a stream of events to user space. ++ * More on that later... */ ++ ++/************************* INLINE FUNTIONS *************************/ ++/* ++ * Function that are so simple that it's more efficient inlining them ++ */ ++ ++/*------------------------------------------------------------------*/ ++/* ++ * Wrapper to add an Wireless Event to a stream of events. ++ */ ++static inline char * ++iwe_stream_add_event(char * stream, /* Stream of events */ ++ char * ends, /* End of stream */ ++ struct iw_event *iwe, /* Payload */ ++ int event_len) /* Real size of payload */ ++{ ++ /* Check if it's possible */ ++ if((stream + event_len) < ends) { ++ iwe->len = event_len; ++ memcpy(stream, (char *) iwe, event_len); ++ stream += event_len; ++ } ++ return stream; ++} ++ ++/*------------------------------------------------------------------*/ ++/* ++ * Wrapper to add an short Wireless Event containing a pointer to a ++ * stream of events. ++ */ ++static inline char * ++iwe_stream_add_point(char * stream, /* Stream of events */ ++ char * ends, /* End of stream */ ++ struct iw_event *iwe, /* Payload */ ++ char * extra) ++{ ++ int event_len = IW_EV_POINT_LEN + iwe->u.data.length; ++ /* Check if it's possible */ ++ if((stream + event_len) < ends) { ++ iwe->len = event_len; ++ memcpy(stream, (char *) iwe, IW_EV_POINT_LEN); ++ memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length); ++ stream += event_len; ++ } ++ return stream; ++} ++ ++/*------------------------------------------------------------------*/ ++/* ++ * Wrapper to add a value to a Wireless Event in a stream of events. ++ * Be careful, this one is tricky to use properly : ++ * At the first run, you need to have (value = event + IW_EV_LCP_LEN). ++ */ ++static inline char * ++iwe_stream_add_value(char * event, /* Event in the stream */ ++ char * value, /* Value in event */ ++ char * ends, /* End of stream */ ++ struct iw_event *iwe, /* Payload */ ++ int event_len) /* Real size of payload */ ++{ ++ /* Don't duplicate LCP */ ++ event_len -= IW_EV_LCP_LEN; ++ ++ /* Check if it's possible */ ++ if((value + event_len) < ends) { ++ /* Add new value */ ++ memcpy(value, (char *) iwe + IW_EV_LCP_LEN, event_len); ++ value += event_len; ++ /* Patch LCP */ ++ iwe->len = value - event; ++ memcpy(event, (char *) iwe, IW_EV_LCP_LEN); ++ } ++ return value; ++} ++ ++#endif /* _IW_HANDLER_H */ +diff -u -p -r --new-file linux/net/netsyms-w13.c linux/net/netsyms.c +--- linux/net/netsyms-w13.c Thu Jun 6 15:46:34 2002 ++++ linux/net/netsyms.c Thu Jun 6 15:47:44 2002 +@@ -588,4 +588,10 @@ EXPORT_SYMBOL(register_gifconf); + EXPORT_SYMBOL(net_call_rx_atomic); + EXPORT_SYMBOL(softnet_data); + ++#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) ++/* Don't include the whole header mess for a single function */ ++extern void wireless_send_event(struct net_device *dev, unsigned int cmd, union iwreq_data *wrqu, char *extra); ++EXPORT_SYMBOL(wireless_send_event); ++#endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ ++ + #endif /* CONFIG_NET */ +diff -u -p -r --new-file linux/net/core/wireless-w13.c linux/net/core/wireless.c +--- linux/net/core/wireless-w13.c Thu Jun 6 15:46:45 2002 ++++ linux/net/core/wireless.c Thu Jun 6 15:48:06 2002 +@@ -2,7 +2,7 @@ + * This file implement the Wireless Extensions APIs. + * + * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> +- * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved. ++ * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. + * + * (As all part of the Linux kernel, this file is GPL) + */ +@@ -25,6 +25,16 @@ + * o Added iw_handler handling ;-) + * o Added standard ioctl description + * o Initial dumb commit strategy based on orinoco.c ++ * ++ * v3 - 19.12.01 - Jean II ++ * o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call ++ * o Add event dispatcher function ++ * o Add event description ++ * o Propagate events as rtnetlink IFLA_WIRELESS option ++ * o Generate event on selected SET requests ++ * ++ * v4 - 18.04.01 - Jean II ++ * o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1 + */ + + /***************************** INCLUDES *****************************/ +@@ -33,6 +43,7 @@ + #include <linux/config.h> /* Not needed ??? */ + #include <linux/types.h> /* off_t */ + #include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */ ++#include <linux/rtnetlink.h> /* rtnetlink stuff */ + + #include <linux/wireless.h> /* Pretty obvious */ + #include <net/iw_handler.h> /* New driver API */ +@@ -44,14 +55,23 @@ + + /* Debuging stuff */ + #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ ++#undef WE_EVENT_DEBUG /* Debug Event dispatcher */ ++ ++/* Options */ ++#define WE_EVENT_NETLINK /* Propagate events using rtnetlink */ ++#define WE_SET_EVENT /* Generate an event on some set commands */ + + /************************* GLOBAL VARIABLES *************************/ + /* + * You should not use global variables, because or re-entrancy. + * On our case, it's only const, so it's OK... + */ ++/* ++ * Meta-data about all the standard Wireless Extension request we ++ * know about. ++ */ + static const struct iw_ioctl_description standard_ioctl[] = { +- /* SIOCSIWCOMMIT (internal) */ ++ /* SIOCSIWCOMMIT */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCGIWNAME */ + { IW_HEADER_TYPE_CHAR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, +@@ -99,18 +119,18 @@ static const struct iw_ioctl_description + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* SIOCGIWAPLIST */ + { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0}, +- /* -- hole -- */ +- { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, +- /* -- hole -- */ +- { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, ++ /* SIOCSIWSCAN */ ++ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, ++ /* SIOCGIWSCAN */ ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, 0}, + /* SIOCSIWESSID */ +- { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_EVENT}, ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_EVENT}, + /* SIOCGIWESSID */ +- { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_DUMP}, ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_DUMP}, + /* SIOCSIWNICKN */ +- { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0}, ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, 0}, + /* SIOCGIWNICKN */ +- { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0}, ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, 0}, + /* -- hole -- */ + { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, + /* -- hole -- */ +@@ -136,7 +156,7 @@ static const struct iw_ioctl_description + /* SIOCGIWRETRY */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + /* SIOCSIWENCODE */ +- { IW_HEADER_TYPE_POINT, 4, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT}, ++ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT}, + /* SIOCGIWENCODE */ + { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT}, + /* SIOCSIWPOWER */ +@@ -144,9 +164,38 @@ static const struct iw_ioctl_description + /* SIOCGIWPOWER */ + { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, + }; ++static const int standard_ioctl_num = (sizeof(standard_ioctl) / ++ sizeof(struct iw_ioctl_description)); ++ ++/* ++ * Meta-data about all the additional standard Wireless Extension events ++ * we know about. ++ */ ++static const struct iw_ioctl_description standard_event[] = { ++ /* IWEVTXDROP */ ++ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, ++ /* IWEVQUAL */ ++ { IW_HEADER_TYPE_QUAL, 0, 0, 0, 0, 0}, ++}; ++static const int standard_event_num = (sizeof(standard_event) / ++ sizeof(struct iw_ioctl_description)); + + /* Size (in bytes) of the various private data types */ +-char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 }; ++static const char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 }; ++ ++/* Size (in bytes) of various events */ ++static const int event_type_size[] = { ++ IW_EV_LCP_LEN, ++ 0, ++ IW_EV_CHAR_LEN, ++ 0, ++ IW_EV_UINT_LEN, ++ IW_EV_FREQ_LEN, ++ IW_EV_POINT_LEN, /* Without variable payload */ ++ IW_EV_PARAM_LEN, ++ IW_EV_ADDR_LEN, ++ IW_EV_QUAL_LEN, ++}; + + /************************ COMMON SUBROUTINES ************************/ + /* +@@ -162,7 +211,8 @@ char priv_type_size[] = { 0, 1, 1, 0, 4, + static inline iw_handler get_handler(struct net_device *dev, + unsigned int cmd) + { +- unsigned int index; /* MUST be unsigned */ ++ /* Don't "optimise" the following variable, it will crash */ ++ unsigned int index; /* *MUST* be unsigned */ + + /* Check if we have some wireless handlers defined */ + if(dev->wireless_handlers == NULL) +@@ -269,9 +319,9 @@ static inline int sprintf_wireless_stats + stats->status, + stats->qual.qual, + stats->qual.updated & 1 ? '.' : ' ', +- stats->qual.level, ++ ((__u8) stats->qual.level), + stats->qual.updated & 2 ? '.' : ' ', +- stats->qual.noise, ++ ((__u8) stats->qual.noise), + stats->qual.updated & 4 ? '.' : ' ', + stats->discard.nwid, + stats->discard.code, +@@ -423,12 +473,14 @@ static inline int ioctl_standard_call(st + int ret = -EINVAL; + + /* Get the description of the IOCTL */ ++ if((cmd - SIOCIWFIRST) >= standard_ioctl_num) ++ return -EOPNOTSUPP; + descr = &(standard_ioctl[cmd - SIOCIWFIRST]); + + #ifdef WE_IOCTL_DEBUG +- printk(KERN_DEBUG "%s : Found standard handler for 0x%04X\n", ++ printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n", + ifr->ifr_name, cmd); +- printk(KERN_DEBUG "Header type : %d, token type : %d, token_size : %d, max_token : %d\n", descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); ++ printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); + #endif /* WE_IOCTL_DEBUG */ + + /* Prepare the call */ +@@ -437,8 +489,16 @@ static inline int ioctl_standard_call(st + + /* Check if we have a pointer to user space data or not */ + if(descr->header_type != IW_HEADER_TYPE_POINT) { ++ + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), NULL); ++ ++#ifdef WE_SET_EVENT ++ /* Generate an event to notify listeners of the change */ ++ if((descr->flags & IW_DESCR_FLAG_EVENT) && ++ ((ret == 0) || (ret == -EIWCOMMIT))) ++ wireless_send_event(dev, cmd, &(iwr->u), NULL); ++#endif /* WE_SET_EVENT */ + } else { + char * extra; + int err; +@@ -466,8 +526,8 @@ static inline int ioctl_standard_call(st + } + + #ifdef WE_IOCTL_DEBUG +- printk(KERN_DEBUG "Malloc %d bytes\n", +- descr->max_tokens * descr->token_size); ++ printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", ++ dev->name, descr->max_tokens * descr->token_size); + #endif /* WE_IOCTL_DEBUG */ + + /* Always allocate for max space. Easier, and won't last +@@ -488,7 +548,8 @@ static inline int ioctl_standard_call(st + return -EFAULT; + } + #ifdef WE_IOCTL_DEBUG +- printk(KERN_DEBUG "Got %d bytes\n", ++ printk(KERN_DEBUG "%s (WE) : Got %d bytes\n", ++ dev->name, + iwr->u.data.length * descr->token_size); + #endif /* WE_IOCTL_DEBUG */ + } +@@ -504,11 +565,26 @@ static inline int ioctl_standard_call(st + if (err) + ret = -EFAULT; + #ifdef WE_IOCTL_DEBUG +- printk(KERN_DEBUG "Wrote %d bytes\n", ++ printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n", ++ dev->name, + iwr->u.data.length * descr->token_size); + #endif /* WE_IOCTL_DEBUG */ + } + ++#ifdef WE_SET_EVENT ++ /* Generate an event to notify listeners of the change */ ++ if((descr->flags & IW_DESCR_FLAG_EVENT) && ++ ((ret == 0) || (ret == -EIWCOMMIT))) { ++ if(descr->flags & IW_DESCR_FLAG_RESTRICT) ++ /* If the event is restricted, don't ++ * export the payload */ ++ wireless_send_event(dev, cmd, &(iwr->u), NULL); ++ else ++ wireless_send_event(dev, cmd, &(iwr->u), ++ extra); ++ } ++#endif /* WE_SET_EVENT */ ++ + /* Cleanup - I told you it wasn't that long ;-) */ + kfree(extra); + } +@@ -558,11 +634,12 @@ static inline int ioctl_private_call(str + } + + #ifdef WE_IOCTL_DEBUG +- printk(KERN_DEBUG "%s : Found private handler for 0x%04X\n", ++ printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n", + ifr->ifr_name, cmd); + if(descr) { +- printk(KERN_DEBUG "Name %s, set %X, get %X\n", +- descr->name, descr->set_args, descr->get_args); ++ printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n", ++ dev->name, descr->name, ++ descr->set_args, descr->get_args); + } + #endif /* WE_IOCTL_DEBUG */ + +@@ -617,7 +694,8 @@ static inline int ioctl_private_call(str + } + + #ifdef WE_IOCTL_DEBUG +- printk(KERN_DEBUG "Malloc %d bytes\n", extra_size); ++ printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", ++ dev->name, extra_size); + #endif /* WE_IOCTL_DEBUG */ + + /* Always allocate for max space. Easier, and won't last +@@ -636,7 +714,8 @@ static inline int ioctl_private_call(str + return -EFAULT; + } + #ifdef WE_IOCTL_DEBUG +- printk(KERN_DEBUG "Got %d elem\n", iwr->u.data.length); ++ printk(KERN_DEBUG "%s (WE) : Got %d elem\n", ++ dev->name, iwr->u.data.length); + #endif /* WE_IOCTL_DEBUG */ + } + +@@ -650,8 +729,8 @@ static inline int ioctl_private_call(str + if (err) + ret = -EFAULT; + #ifdef WE_IOCTL_DEBUG +- printk(KERN_DEBUG "Wrote %d elem\n", +- iwr->u.data.length); ++ printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n", ++ dev->name, iwr->u.data.length); + #endif /* WE_IOCTL_DEBUG */ + } + +@@ -730,4 +809,178 @@ int wireless_process_ioctl(struct ifreq + } + /* Not reached */ + return -EINVAL; ++} ++ ++/************************* EVENT PROCESSING *************************/ ++/* ++ * Process events generated by the wireless layer or the driver. ++ * Most often, the event will be propagated through rtnetlink ++ */ ++ ++#ifdef WE_EVENT_NETLINK ++/* "rtnl" is defined in net/core/rtnetlink.c, but we need it here. ++ * It is declared in <linux/rtnetlink.h> */ ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Fill a rtnetlink message with our event data. ++ * Note that we propage only the specified event and don't dump the ++ * current wireless config. Dumping the wireless config is far too ++ * expensive (for each parameter, the driver need to query the hardware). ++ */ ++static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb, ++ struct net_device * dev, ++ int type, ++ char * event, ++ int event_len) ++{ ++ struct ifinfomsg *r; ++ struct nlmsghdr *nlh; ++ unsigned char *b = skb->tail; ++ ++ nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r)); ++ r = NLMSG_DATA(nlh); ++ r->ifi_family = AF_UNSPEC; ++ r->ifi_type = dev->type; ++ r->ifi_index = dev->ifindex; ++ r->ifi_flags = dev->flags; ++ r->ifi_change = 0; /* Wireless changes don't affect those flags */ ++ ++ /* Add the wireless events in the netlink packet */ ++ RTA_PUT(skb, IFLA_WIRELESS, ++ event_len, event); ++ ++ nlh->nlmsg_len = skb->tail - b; ++ return skb->len; ++ ++nlmsg_failure: ++rtattr_failure: ++ skb_trim(skb, b - skb->data); ++ return -1; ++} ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Create and broadcast and send it on the standard rtnetlink socket ++ * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c ++ * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field ++ * within a RTM_NEWLINK event. ++ */ ++static inline void rtmsg_iwinfo(struct net_device * dev, ++ char * event, ++ int event_len) ++{ ++ struct sk_buff *skb; ++ int size = NLMSG_GOODSIZE; ++ ++ skb = alloc_skb(size, GFP_ATOMIC); ++ if (!skb) ++ return; ++ ++ if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, ++ event, event_len) < 0) { ++ kfree_skb(skb); ++ return; ++ } ++ NETLINK_CB(skb).dst_groups = RTMGRP_LINK; ++ netlink_broadcast(rtnl, skb, 0, RTMGRP_LINK, GFP_ATOMIC); ++} ++#endif /* WE_EVENT_NETLINK */ ++ ++/* ---------------------------------------------------------------- */ ++/* ++ * Main event dispatcher. Called from other parts and drivers. ++ * Send the event on the apropriate channels. ++ * May be called from interrupt context. ++ */ ++void wireless_send_event(struct net_device * dev, ++ unsigned int cmd, ++ union iwreq_data * wrqu, ++ char * extra) ++{ ++ const struct iw_ioctl_description * descr = NULL; ++ int extra_len = 0; ++ struct iw_event *event; /* Mallocated whole event */ ++ int event_len; /* Its size */ ++ int hdr_len; /* Size of the event header */ ++ /* Don't "optimise" the following variable, it will crash */ ++ unsigned cmd_index; /* *MUST* be unsigned */ ++ ++ /* Get the description of the IOCTL */ ++ if(cmd <= SIOCIWLAST) { ++ cmd_index = cmd - SIOCIWFIRST; ++ if(cmd_index < standard_ioctl_num) ++ descr = &(standard_ioctl[cmd_index]); ++ } else { ++ cmd_index = cmd - IWEVFIRST; ++ if(cmd_index < standard_event_num) ++ descr = &(standard_event[cmd_index]); ++ } ++ /* Don't accept unknown events */ ++ if(descr == NULL) { ++ /* Note : we don't return an error to the driver, because ++ * the driver would not know what to do about it. It can't ++ * return an error to the user, because the event is not ++ * initiated by a user request. ++ * The best the driver could do is to log an error message. ++ * We will do it ourselves instead... ++ */ ++ printk(KERN_ERR "%s (WE) : Invalid Wireless Event (0x%04X)\n", ++ dev->name, cmd); ++ return; ++ } ++#ifdef WE_EVENT_DEBUG ++ printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n", ++ dev->name, cmd); ++ printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); ++#endif /* WE_EVENT_DEBUG */ ++ ++ /* Check extra parameters and set extra_len */ ++ if(descr->header_type == IW_HEADER_TYPE_POINT) { ++ /* Check if number of token fits within bounds */ ++ if(wrqu->data.length > descr->max_tokens) { ++ printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length); ++ return; ++ } ++ if(wrqu->data.length < descr->min_tokens) { ++ printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length); ++ return; ++ } ++ /* Calculate extra_len - extra is NULL for restricted events */ ++ if(extra != NULL) ++ extra_len = wrqu->data.length * descr->token_size; ++#ifdef WE_EVENT_DEBUG ++ printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len); ++#endif /* WE_EVENT_DEBUG */ ++ } ++ ++ /* Total length of the event */ ++ hdr_len = event_type_size[descr->header_type]; ++ event_len = hdr_len + extra_len; ++ ++#ifdef WE_EVENT_DEBUG ++ printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, event_len %d\n", dev->name, cmd, hdr_len, event_len); ++#endif /* WE_EVENT_DEBUG */ ++ ++ /* Create temporary buffer to hold the event */ ++ event = kmalloc(event_len, GFP_ATOMIC); ++ if(event == NULL) ++ return; ++ ++ /* Fill event */ ++ event->len = event_len; ++ event->cmd = cmd; ++ memcpy(&event->u, wrqu, hdr_len - IW_EV_LCP_LEN); ++ if(extra != NULL) ++ memcpy(((char *) event) + hdr_len, extra, extra_len); ++ ++#ifdef WE_EVENT_NETLINK ++ /* rtnetlink event channel */ ++ rtmsg_iwinfo(dev, (char *) event, event_len); ++#endif /* WE_EVENT_NETLINK */ ++ ++ /* Cleanup */ ++ kfree(event); ++ ++ return; /* Always success, I guess ;-) */ + } diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/keyboard-ctrl+alt.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/keyboard-ctrl+alt.patch index e69de29bb2..93a80bae3c 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/keyboard-ctrl+alt.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/keyboard-ctrl+alt.patch @@ -0,0 +1,79 @@ + +# +# Made by http://www.mn-logistik.de/unsupported/pxa250/patcher +# + +--- linux/drivers/char/corgi_rawmap.h~keyboard-ctrl+alt 2003-09-15 14:30:08.000000000 +0200 ++++ linux/drivers/char/corgi_rawmap.h 2003-09-16 00:45:39.000000000 +0200 +@@ -55,7 +55,7 @@ + SLKEY_ACTIVITY, SLKEY_W, SLKEY_S, SLKEY_F, SLKEY_V, SLKEY_H, SLKEY_M, SLKEY_L, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_CONTACTS, SLKEY_A, SLKEY_D, SLKEY_C, SLKEY_B, SLKEY_N, SLKEY_PERIOD, KEY_IGN, SLKEY_ENTER, KEY_IGN, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_MAIL, SLKEY_Z, SLKEY_X, SLKEY_MINUS, SLKEY_SPACE, SLKEY_COMMA, KEY_IGN, SLKEY_UP, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, +- SLKEY_HOME, SLKEY_KANA, SLKEY_ZENHAN, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, ++ SLKEY_HOME, SLKEY_LCONTROL, SLKEY_LALT, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_OFF, SLKEY_EXSELECT, SLKEY_EXCANCEL, SLKEY_EXJOGDOWN, SLKEY_EXJOGUP, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN}; + + static unsigned char rawkeytable_table_NormalUpper[(NR_KEYCODES+1)] = { +@@ -66,7 +66,7 @@ + SLKEY_ACTIVITY, SLKEY_W, SLKEY_S, SLKEY_F, SLKEY_V, SLKEY_H, SLKEY_M, SLKEY_L, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_CONTACTS, SLKEY_A, SLKEY_D, SLKEY_C, SLKEY_B, SLKEY_N, SLKEY_PERIOD, KEY_IGN, SLKEY_ENTER, KEY_IGN, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_MAIL, SLKEY_Z, SLKEY_X, SLKEY_MINUS, SLKEY_SPACE, SLKEY_COMMA, KEY_IGN, SLKEY_UP, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, +- SLKEY_HOME, SLKEY_KANA, SLKEY_ZENHAN, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, ++ SLKEY_HOME, SLKEY_LCONTROL, SLKEY_LALT, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_OFF, SLKEY_EXSELECT, SLKEY_EXCANCEL, SLKEY_EXJOGDOWN, SLKEY_EXJOGUP, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN}; + + static unsigned char rawkeytable_table_2ndLower[(NR_KEYCODES+1)] = { +@@ -77,7 +77,7 @@ + SLKEY_ACTIVITY, SLKEY_W, SLKEY_S, SLKEY_F, SLKEY_V, SLKEY_H, SLKEY_M, SLKEY_L, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_CONTACTS, SLKEY_A, SLKEY_D, SLKEY_C, SLKEY_B, SLKEY_N, SLKEY_PERIOD, KEY_IGN, SLKEY_ENTER, KEY_IGN, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_MAIL, SLKEY_Z, SLKEY_X, SLKEY_MINUS, SLKEY_SPACE, SLKEY_COMMA, KEY_IGN, SLKEY_UP, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, +- SLKEY_HOME, SLKEY_KANA, SLKEY_ZENHAN, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, ++ SLKEY_HOME, SLKEY_LCONTROL, SLKEY_LALT, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_OFF, SLKEY_EXSELECT, SLKEY_EXCANCEL, SLKEY_EXJOGDOWN, SLKEY_EXJOGUP, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN}; + + static unsigned char rawkeytable_table_2ndUpper[(NR_KEYCODES+1)] = { +@@ -88,7 +88,7 @@ + SLKEY_ACTIVITY, SLKEY_W, SLKEY_S, SLKEY_F, SLKEY_V, SLKEY_H, SLKEY_M, SLKEY_L, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_CONTACTS, SLKEY_A, SLKEY_D, SLKEY_PRINTSCREEN, SLKEY_B, SLKEY_N, SLKEY_PERIOD, KEY_IGN, SLKEY_ENTER, KEY_IGN, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_MAIL, SLKEY_Z, SLKEY_X, SLKEY_MINUS, SLKEY_SPACE, SLKEY_COMMA, KEY_IGN, SLKEY_UP, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, +- SLKEY_HOME, SLKEY_KANA, SLKEY_ZENHAN, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, ++ SLKEY_HOME, SLKEY_LCONTROL, SLKEY_LALT, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_OFF, SLKEY_EXSELECT, SLKEY_EXCANCEL, SLKEY_EXJOGDOWN, SLKEY_EXJOGUP, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN}; + + static unsigned char rawkeytable_table_NumlockLower[(NR_KEYCODES+1)] = { +@@ -99,7 +99,7 @@ + SLKEY_ACTIVITY, SLKEY_W, SLKEY_S, SLKEY_F, SLKEY_V, SLKEY_H, SLKEY_M, SLKEY_L, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_CONTACTS, SLKEY_A, SLKEY_D, SLKEY_C, SLKEY_B, SLKEY_N, SLKEY_PERIOD, KEY_IGN, SLKEY_ENTER, KEY_IGN, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_MAIL, SLKEY_Z, SLKEY_X, SLKEY_MINUS, SLKEY_SPACE, SLKEY_COMMA, KEY_IGN, SLKEY_UP, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, +- SLKEY_HOME, SLKEY_KANA, SLKEY_ZENHAN, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, ++ SLKEY_HOME, SLKEY_LCONTROL, SLKEY_LALT, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_OFF, SLKEY_EXSELECT, SLKEY_EXCANCEL, SLKEY_EXJOGDOWN, SLKEY_EXJOGUP, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN}; + + static unsigned char rawkeytable_table_NumlockUpper[(NR_KEYCODES+1)] = { +@@ -110,7 +110,7 @@ + SLKEY_ACTIVITY, SLKEY_W, SLKEY_S, SLKEY_F, SLKEY_V, SLKEY_H, SLKEY_M, SLKEY_L, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_CONTACTS, SLKEY_A, SLKEY_D, SLKEY_C, SLKEY_B, SLKEY_N, SLKEY_PERIOD, KEY_IGN, SLKEY_ENTER, KEY_IGN, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_MAIL, SLKEY_Z, SLKEY_X, SLKEY_MINUS, SLKEY_SPACE, SLKEY_COMMA, KEY_IGN, SLKEY_UP, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, +- SLKEY_HOME, SLKEY_KANA, SLKEY_ZENHAN, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, ++ SLKEY_HOME, SLKEY_LCONTROL, SLKEY_LALT, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_OFF, SLKEY_EXSELECT, SLKEY_EXCANCEL, SLKEY_EXJOGDOWN, SLKEY_EXJOGUP, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN}; + + static unsigned char rawkeytable_table_Num2ndLower[(NR_KEYCODES+1)] = { +@@ -121,7 +121,7 @@ + SLKEY_ACTIVITY, SLKEY_W, SLKEY_S, SLKEY_F, SLKEY_V, SLKEY_H, SLKEY_M, SLKEY_L, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_CONTACTS, SLKEY_A, SLKEY_D, SLKEY_C, SLKEY_B, SLKEY_N, SLKEY_PERIOD, KEY_IGN, SLKEY_ENTER, KEY_IGN, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_MAIL, SLKEY_Z, SLKEY_X, SLKEY_MINUS, SLKEY_SPACE, SLKEY_COMMA, KEY_IGN, SLKEY_UP, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, +- SLKEY_HOME, SLKEY_KANA, SLKEY_ZENHAN, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, ++ SLKEY_HOME, SLKEY_LCONTROL, SLKEY_LALT, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_OFF, SLKEY_EXSELECT, SLKEY_EXCANCEL, SLKEY_EXJOGDOWN, SLKEY_EXJOGUP, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN}; + + static unsigned char rawkeytable_table_Num2ndUpper[(NR_KEYCODES+1)] = { +@@ -132,7 +132,7 @@ + SLKEY_ACTIVITY, SLKEY_W, SLKEY_S, SLKEY_F, SLKEY_V, SLKEY_H, SLKEY_M, SLKEY_L, KEY_IGN, SLKEY_RSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_CONTACTS, SLKEY_A, SLKEY_D, SLKEY_PRINTSCREEN, SLKEY_B, SLKEY_N, SLKEY_PERIOD, KEY_IGN, SLKEY_ENTER, KEY_IGN, SLKEY_LSHIFT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_MAIL, SLKEY_Z, SLKEY_X, SLKEY_MINUS, SLKEY_SPACE, SLKEY_COMMA, KEY_IGN, SLKEY_UP, KEY_IGN, KEY_IGN, KEY_IGN, SLKEY_2ND, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, +- SLKEY_HOME, SLKEY_KANA, SLKEY_ZENHAN, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, ++ SLKEY_HOME, SLKEY_LCONTROL, SLKEY_LALT, SLKEY_F9, SLKEY_F4, SLKEY_F2, SLKEY_LEFT, SLKEY_DOWN, SLKEY_RIGHT, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, + SLKEY_OFF, SLKEY_EXSELECT, SLKEY_EXCANCEL, SLKEY_EXJOGDOWN, SLKEY_EXJOGUP, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN, KEY_IGN}; + + diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/keymap-more-sane.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/keymap-more-sane.patch index e69de29bb2..a58dd13fbd 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/keymap-more-sane.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/keymap-more-sane.patch @@ -0,0 +1,23 @@ +--- linux/drivers/char/corgi_keymap.map~keymap-more-sane 2003-08-30 01:05:14.000000000 +0200 ++++ linux/drivers/char/corgi_keymap.map 2003-09-03 20:44:27.000000000 +0200 +@@ -71,9 +71,11 @@ + # (Cancel:34) F9 -> Escape + keycode 34 = Escape + keycode 35 = Left ++ alt keycode 35 = Decr_Console + keycode 36 = Up + keycode 37 = Down + keycode 38 = Right ++ alt keycode 38 = Incr_Console + # (OK:39) F4 -> Return + keycode 39 = Return + keycode 40 = +@@ -100,7 +102,7 @@ + keycode 60 = Shift_Lock + keycode 61 = at + keycode 62 = question +-keycode 63 = comma slash ++keycode 63 = slash comma + alt keycode 63 = less + keycode 64 = period question + alt keycode 64 = greater diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/logo.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/logo.patch index e69de29bb2..c006684f23 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/logo.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/logo.patch @@ -0,0 +1,2598 @@ +--- linux/drivers/video/fbcon.c 2003-02-27 21:47:36.000000000 -0600 ++++ linux.new/drivers/video/fbcon.c 2003-02-27 18:39:39.000000000 -0600 +@@ -126,8 +126,8 @@ + #define LOGO_H (320-16) + #define LOGO_W 240 + #else +-#define LOGO_H 80 +-#define LOGO_W 80 ++#define LOGO_H 52 ++#define LOGO_W 240 + #endif + #define LOGO_LINE (LOGO_W/8) + #if defined(CONFIG_SHARP_LOGO_SCREEN) +--- linux/include/linux/linux_logo.h 2001-06-11 21:15:27.000000000 -0500 ++++ linux.new/include/linux/linux_logo.h 2003-02-27 15:58:15.000000000 -0600 +@@ -1,4 +1,4 @@ +-/* $Id$ ++/* linux_logo.h created with fblogo, 2002/12/29 02:43:36 + * include/linux/linux_logo.h: This is a linux logo + * to be displayed on boot. + * +@@ -7,907 +7,1673 @@ + * + * You can put anything here, but: + * LINUX_LOGO_COLORS has to be less than 224 +- * image size has to be 80x80 +- * values have to start from 0x20 +- * (i.e. RGB(linux_logo_red[0], +- * linux_logo_green[0], +- * linux_logo_blue[0]) is color 0x20) +- * BW image has to be 80x80 as well, with MS bit +- * on the left +- * Serial_console ascii image can be any size, +- * but should contain %s to display the version ++ * Generated by fblogo version 0.5.0 ++ * ++ * ++ * Remember to modify drivers/video/fbcon.c: ++ * Change "#define LOGO_H 80" to "#define LOGO_H 52" ++ * Change "#define LOGO_W 80" to "#define LOGO_W 240" + */ + + #ifndef __HAVE_ARCH_LINUX_LOGO +-#define LINUX_LOGO_COLORS 187 ++#define LINUX_LOGO_COLORS 223 + #endif +- + #ifdef INCLUDE_LINUX_LOGO_DATA +- + #ifndef __HAVE_ARCH_LINUX_LOGO +- + unsigned char linux_logo_red[] __initdata = { +- 0x00, 0x06, 0x0a, 0x0e, 0x16, 0x1a, 0x1e, 0x22, +- 0x12, 0x00, 0x2a, 0x36, 0x42, 0x4e, 0x4a, 0x56, +- 0x26, 0x46, 0x2e, 0x32, 0x52, 0x3a, 0x02, 0x65, +- 0x5e, 0x3e, 0x74, 0x8a, 0xa2, 0x9a, 0x86, 0xc6, +- 0xc3, 0x65, 0xbb, 0xd2, 0xda, 0xd6, 0xe2, 0xf6, +- 0xfd, 0xae, 0x7b, 0xdd, 0xea, 0x6a, 0xaa, 0xe7, +- 0xbe, 0x5a, 0xee, 0x9e, 0x95, 0x80, 0x76, 0x79, +- 0x62, 0x36, 0x9a, 0xe2, 0xec, 0xe1, 0xb8, 0xd7, +- 0xaf, 0x25, 0xbc, 0xc0, 0xef, 0xea, 0xe8, 0xe8, +- 0xf5, 0xf1, 0xda, 0xd3, 0x79, 0xdb, 0xf4, 0xf6, +- 0xf6, 0xf6, 0xe2, 0x3d, 0xb4, 0xce, 0xe6, 0xee, +- 0xf6, 0x68, 0xd8, 0xec, 0xf5, 0xc6, 0xc8, 0x9c, +- 0x89, 0xd2, 0xee, 0xcb, 0xb9, 0xd2, 0x66, 0x5e, +- 0x8b, 0xbe, 0xa8, 0xd5, 0xca, 0xb6, 0xae, 0x9c, +- 0xc5, 0xbe, 0xbe, 0xca, 0x90, 0xb2, 0x9a, 0xa8, +- 0xb6, 0xf2, 0xce, 0xfa, 0xb2, 0x6e, 0xa6, 0x12, +- 0x4a, 0x8e, 0xf2, 0xf6, 0xf6, 0xee, 0xb5, 0xe4, +- 0xf1, 0x26, 0x9a, 0xea, 0xf6, 0xe0, 0xd2, 0x16, +- 0x9a, 0x2e, 0x70, 0xd6, 0x46, 0x7c, 0xb4, 0x62, +- 0xd6, 0xa3, 0x74, 0xa7, 0xa2, 0xca, 0xe0, 0xae, +- 0xbe, 0xce, 0xa3, 0x8e, 0x6d, 0x8e, 0x32, 0xaf, +- 0x50, 0x9e, 0x5b, 0x8a, 0x98, 0x82, 0x7a, 0x82, +- 0x56, 0x7c, 0x8a, 0x56, 0x5e, 0x86, 0x6a, 0x52, +- 0x59, 0x64, 0x5e, ++ 0x00, 0x02, 0x06, 0x0A, 0x56, 0xA6, 0xBE, 0xBA, ++ 0xB6, 0x92, 0x3E, 0x1A, 0x8A, 0xF3, 0xFB, 0xEE, ++ 0xC6, 0x62, 0x4A, 0x12, 0x26, 0x0E, 0x76, 0xDA, ++ 0x6E, 0x32, 0x8E, 0x1E, 0x2A, 0xCE, 0xE2, 0x36, ++ 0x66, 0xA2, 0x2E, 0x9E, 0xCA, 0x6A, 0x5A, 0x82, ++ 0x7E, 0xE9, 0xD2, 0xDE, 0x22, 0x72, 0xAE, 0xD6, ++ 0x86, 0x3A, 0x52, 0x9A, 0xAA, 0x16, 0x46, 0x4E, ++ 0x42, 0xC2, 0x96, 0x7A, 0x5E, 0xB2, 0x32, 0x7E, ++ 0x3A, 0x16, 0x02, 0x02, 0x06, 0x1F, 0x02, 0x02, ++ 0x06, 0x02, 0x22, 0x3A, 0x35, 0x16, 0x0A, 0x02, ++ 0x2A, 0x3E, 0x39, 0x32, 0x2A, 0x1E, 0x16, 0x06, ++ 0x22, 0x22, 0x1E, 0x16, 0x6E, 0x82, 0x6E, 0x2E, ++ 0x1E, 0x2E, 0x1E, 0x0A, 0x3A, 0xDA, 0xFE, 0xFA, ++ 0xFA, 0xE2, 0x6A, 0x3A, 0x1A, 0xFA, 0xF6, 0x9E, ++ 0x02, 0x6E, 0xFE, 0xF6, 0x3E, 0x66, 0x52, 0x1B, ++ 0xD6, 0x5A, 0xEE, 0xC6, 0x72, 0x36, 0x22, 0x1E, ++ 0x02, 0x96, 0xF6, 0xDE, 0xA2, 0xD6, 0x39, 0x0E, ++ 0xAA, 0x52, 0x5A, 0xD2, 0xDE, 0x61, 0x46, 0xDE, ++ 0x7A, 0x57, 0x9E, 0xBA, 0xB6, 0x4A, 0x5A, 0xA6, ++ 0x96, 0x23, 0x1E, 0x12, 0x9A, 0x4E, 0x76, 0xAE, ++ 0x2E, 0xBE, 0x86, 0x48, 0xA6, 0x52, 0x06, 0x2E, ++ 0x56, 0x13, 0x2A, 0x4A, 0x36, 0x56, 0x2E, 0x1E, ++ 0x46, 0x3A, 0x66, 0x02, 0x3D, 0x3E, 0x2E, 0x3E, ++ 0x4A, 0x32, 0x52, 0x72, 0x76, 0x67, 0x68, 0x5A, ++ 0x3A, 0x1A, 0x0E, 0x2E, 0x4E, 0x02, 0x3E, 0x0A, ++ 0x28, 0x42, 0x10, 0x26, 0x8E, 0x0A, 0x86, 0xC2, ++ 0x02, 0x6E, 0x92, 0x02, 0x42, 0x02, 0xBE, 0x4E, ++ 0x02, 0x22, 0x02, 0x3E, 0x3A, 0x32 + }; + + unsigned char linux_logo_green[] __initdata = { +- 0x00, 0x06, 0x0a, 0x0e, 0x16, 0x1a, 0x1e, 0x22, +- 0x12, 0x00, 0x2a, 0x36, 0x42, 0x4e, 0x4a, 0x56, +- 0x26, 0x46, 0x2e, 0x32, 0x52, 0x3a, 0x02, 0x65, +- 0x5e, 0x3e, 0x74, 0x8a, 0xa2, 0x9a, 0x86, 0xc6, +- 0xc3, 0x62, 0xbb, 0xd2, 0xda, 0xd6, 0xe2, 0xf6, +- 0xfd, 0xae, 0x7b, 0xdd, 0xea, 0x6a, 0xaa, 0xe7, +- 0xbe, 0x5a, 0xee, 0x9e, 0x95, 0x80, 0x62, 0x5c, +- 0x4e, 0x26, 0x72, 0xaa, 0xba, 0xaf, 0x90, 0xae, +- 0x92, 0x1a, 0xa4, 0x85, 0xb6, 0xbe, 0xc3, 0xc8, +- 0xcf, 0xd0, 0xc2, 0xce, 0x57, 0xa2, 0xd6, 0xda, +- 0xda, 0xd7, 0xb8, 0x2a, 0x7b, 0x91, 0xae, 0xca, +- 0xda, 0x45, 0x9e, 0xb2, 0xd7, 0x9b, 0x90, 0x76, +- 0x5c, 0xa2, 0xbe, 0xa6, 0x85, 0x96, 0x4e, 0x46, +- 0x66, 0x92, 0x7a, 0x9a, 0x96, 0x9d, 0x9a, 0x6b, +- 0x8a, 0x8e, 0xb2, 0xca, 0x90, 0xa6, 0x79, 0x7c, +- 0xb6, 0xf2, 0xce, 0xfa, 0xb2, 0x6e, 0xa6, 0x0e, +- 0x36, 0x86, 0xba, 0xbe, 0xe6, 0xcc, 0x8e, 0xb8, +- 0xc4, 0x1e, 0x8e, 0xae, 0xba, 0xb2, 0xa6, 0x12, +- 0x7a, 0x20, 0x64, 0xaa, 0x2f, 0x70, 0x85, 0x46, +- 0xa6, 0x6e, 0x51, 0x72, 0x92, 0xa2, 0xa6, 0x87, +- 0x96, 0xa2, 0x85, 0x7a, 0x6a, 0x6e, 0x22, 0x76, +- 0x36, 0x76, 0x3c, 0x6e, 0x63, 0x53, 0x66, 0x62, +- 0x42, 0x50, 0x56, 0x42, 0x56, 0x56, 0x56, 0x3e, +- 0x51, 0x52, 0x56, ++ 0x00, 0x02, 0x06, 0x0A, 0x56, 0xA6, 0xBE, 0xBA, ++ 0xB6, 0x92, 0x3E, 0x1A, 0x8A, 0xF3, 0xFB, 0xEE, ++ 0xC6, 0x62, 0x4A, 0x12, 0x26, 0x0E, 0x76, 0xDA, ++ 0x6E, 0x32, 0x8E, 0x1E, 0x2A, 0xCE, 0xE2, 0x36, ++ 0x66, 0xA2, 0x2E, 0x9E, 0xCA, 0x6A, 0x5A, 0x82, ++ 0x7E, 0xE9, 0xD2, 0xDE, 0x22, 0x72, 0xAE, 0xD6, ++ 0x86, 0x3A, 0x52, 0x9A, 0xAA, 0x16, 0x46, 0x4E, ++ 0x42, 0xC2, 0x96, 0x7A, 0x5E, 0xB2, 0x2E, 0x7E, ++ 0x3A, 0x3E, 0x6A, 0xA2, 0x92, 0x30, 0x95, 0x76, ++ 0x4E, 0x86, 0x36, 0x7E, 0x9A, 0x6E, 0x42, 0x56, ++ 0x5A, 0xC2, 0xBE, 0xB2, 0xAA, 0x9A, 0x63, 0x1E, ++ 0xA2, 0x9E, 0x96, 0x5A, 0x3A, 0x42, 0x4A, 0x52, ++ 0x91, 0x46, 0x4E, 0x62, 0x22, 0x4A, 0x6E, 0x86, ++ 0x92, 0x9A, 0x52, 0x4A, 0x76, 0x7A, 0x9E, 0x76, ++ 0x62, 0x32, 0x5E, 0xAE, 0x3E, 0x66, 0x52, 0x81, ++ 0xA6, 0x5A, 0xEE, 0xC6, 0x72, 0x5C, 0x6A, 0x8A, ++ 0x36, 0x3E, 0xBA, 0xBA, 0xA2, 0xD6, 0xAA, 0x4E, ++ 0x8A, 0x4E, 0x5A, 0xD2, 0xDE, 0x64, 0x32, 0x9A, ++ 0x5E, 0x57, 0x9E, 0xBA, 0xB6, 0x52, 0x62, 0xA6, ++ 0x9A, 0x47, 0x3E, 0x56, 0x02, 0x1A, 0x76, 0xAE, ++ 0x4A, 0x02, 0x02, 0x38, 0x02, 0x4A, 0x46, 0x86, ++ 0x1E, 0x2E, 0x7A, 0xD2, 0x5E, 0x26, 0x92, 0x62, ++ 0xCA, 0x42, 0x66, 0x26, 0x67, 0xB6, 0x62, 0x92, ++ 0x9E, 0x72, 0xB2, 0xF2, 0xFE, 0xDE, 0xFE, 0xFE, ++ 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x8A, 0x2A, ++ 0x42, 0x52, 0x6A, 0x26, 0x8E, 0x26, 0x86, 0xC2, ++ 0xE2, 0x6E, 0x92, 0xBE, 0x42, 0xC6, 0xBE, 0x4E, ++ 0xEE, 0x5A, 0xEA, 0x3E, 0x4A, 0x32 + }; + + unsigned char linux_logo_blue[] __initdata = { +- 0x00, 0x06, 0x0a, 0x0e, 0x16, 0x1a, 0x1e, 0x22, +- 0x12, 0x01, 0x2a, 0x36, 0x42, 0x4e, 0x4a, 0x56, +- 0x26, 0x46, 0x2e, 0x32, 0x52, 0x3a, 0x06, 0x65, +- 0x5e, 0x3e, 0x74, 0x8a, 0xa2, 0x9a, 0x86, 0xc6, +- 0xc3, 0x59, 0xbb, 0xd2, 0xda, 0xd6, 0xe2, 0xf6, +- 0xfd, 0xae, 0x7b, 0xdd, 0xea, 0x6a, 0xaa, 0xe7, +- 0xbe, 0x5a, 0xee, 0x9e, 0x95, 0x80, 0x2e, 0x08, +- 0x0a, 0x06, 0x0a, 0x0b, 0x0b, 0x0f, 0x0c, 0x0f, +- 0x3d, 0x09, 0x73, 0x09, 0x0d, 0x0a, 0x10, 0x1e, +- 0x2d, 0x13, 0x86, 0xba, 0x19, 0x0a, 0x36, 0x3c, +- 0x26, 0x14, 0x0d, 0x06, 0x07, 0x0a, 0x0b, 0x0f, +- 0x4a, 0x06, 0x0a, 0x0c, 0x2b, 0x0a, 0x0b, 0x0a, +- 0x06, 0x0a, 0x0a, 0x11, 0x0b, 0x0a, 0x0a, 0x1e, +- 0x0f, 0x0d, 0x0a, 0x0b, 0x22, 0x6a, 0x72, 0x0b, +- 0x0b, 0x22, 0x90, 0xca, 0x90, 0x92, 0x3c, 0x2c, +- 0xb6, 0xf2, 0xce, 0xfa, 0xb2, 0x6e, 0xa6, 0x06, +- 0x0e, 0x6a, 0x0e, 0x0e, 0xbe, 0x5b, 0x2c, 0x3e, +- 0x0e, 0x0a, 0x5a, 0x0d, 0x0e, 0x3e, 0x0a, 0x06, +- 0x2e, 0x06, 0x4e, 0x36, 0x06, 0x58, 0x24, 0x06, +- 0x3a, 0x08, 0x08, 0x07, 0x5e, 0x45, 0x0a, 0x32, +- 0x2e, 0x2a, 0x43, 0x48, 0x5f, 0x2e, 0x06, 0x06, +- 0x07, 0x24, 0x06, 0x32, 0x06, 0x06, 0x46, 0x2e, +- 0x22, 0x06, 0x06, 0x1e, 0x4c, 0x06, 0x3a, 0x22, +- 0x42, 0x34, 0x42, ++ 0x00, 0x02, 0x06, 0x0A, 0x56, 0xA6, 0xBE, 0xBA, ++ 0xB6, 0x92, 0x3E, 0x1A, 0x8A, 0xF3, 0xFB, 0xEE, ++ 0xC6, 0x62, 0x4A, 0x12, 0x26, 0x0E, 0x76, 0xDA, ++ 0x6E, 0x32, 0x8E, 0x1E, 0x2A, 0xCE, 0xE2, 0x36, ++ 0x66, 0xA2, 0x2E, 0x9E, 0xCA, 0x6A, 0x5A, 0x82, ++ 0x7E, 0xE9, 0xD2, 0xDE, 0x22, 0x72, 0xAE, 0xD6, ++ 0x86, 0x3A, 0x52, 0x9A, 0xAA, 0x16, 0x46, 0x4E, ++ 0x42, 0xC2, 0x96, 0x7A, 0x5E, 0xB2, 0x1A, 0x06, ++ 0x1A, 0x2A, 0x36, 0x52, 0x46, 0x23, 0x4D, 0x3E, ++ 0x2A, 0x42, 0x2A, 0x5A, 0x69, 0x42, 0x26, 0x2E, ++ 0x42, 0x7E, 0x7C, 0x72, 0x6A, 0x5E, 0x3B, 0x12, ++ 0x62, 0x5E, 0x5A, 0x36, 0x26, 0x1E, 0x26, 0x42, ++ 0x56, 0x3A, 0x36, 0x36, 0x1E, 0x2E, 0x2A, 0x22, ++ 0x22, 0x1A, 0x16, 0x42, 0x46, 0x26, 0x1E, 0x12, ++ 0x32, 0x2A, 0x2E, 0x16, 0x2A, 0x1A, 0x2E, 0x4E, ++ 0x12, 0x22, 0x02, 0x02, 0x12, 0x4A, 0x46, 0x52, ++ 0x1E, 0x1E, 0x16, 0x0E, 0x02, 0x02, 0x6F, 0x2E, ++ 0x0E, 0x1A, 0x06, 0x02, 0x02, 0x04, 0x22, 0x16, ++ 0x0E, 0x12, 0x02, 0x02, 0x02, 0x0A, 0x5A, 0x02, ++ 0x02, 0x1B, 0x2E, 0x32, 0x02, 0x02, 0x02, 0x02, ++ 0x16, 0x02, 0x02, 0x10, 0x02, 0x02, 0x26, 0x5A, ++ 0x02, 0x1F, 0x52, 0x8E, 0x22, 0x02, 0x5E, 0x3E, ++ 0x86, 0x0E, 0x16, 0x16, 0x52, 0x7A, 0x4A, 0x66, ++ 0x72, 0x52, 0x82, 0xB2, 0xBA, 0xA4, 0xB2, 0xAA, ++ 0x9E, 0x8E, 0x88, 0x96, 0xA6, 0x82, 0x62, 0x1A, ++ 0x34, 0x4A, 0x31, 0x16, 0x02, 0x1A, 0x02, 0x02, ++ 0x72, 0x02, 0x02, 0x5E, 0x1A, 0x62, 0x02, 0x0E, ++ 0x76, 0x1E, 0x76, 0x1E, 0x0A, 0x02 + }; + + unsigned char linux_logo[] __initdata = { +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, +- 0x22, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, +- 0x26, 0x26, 0x25, 0x28, 0x23, 0x22, 0x21, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x21, 0x23, 0x25, 0x2a, 0x2b, 0x2c, 0x2d, 0x2d, +- 0x2d, 0x2e, 0x2c, 0x2b, 0x2a, 0x25, 0x28, 0x22, +- 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, +- 0x24, 0x2a, 0x2c, 0x2f, 0x2c, 0x30, 0x30, 0x24, +- 0x25, 0x27, 0x2b, 0x2c, 0x2f, 0x31, 0x32, 0x25, +- 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x25, +- 0x33, 0x34, 0x35, 0x21, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x21, 0x2b, 0x2f, 0x2c, +- 0x30, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, 0x33, +- 0x2d, 0x27, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0x31, +- 0x2d, 0x32, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, 0x2a, 0x34, +- 0x25, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x23, 0x32, 0x27, 0x21, 0x36, +- 0x2a, 0x2d, 0x2a, 0x28, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x22, 0x26, 0x2c, 0x35, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x25, 0x2f, 0x37, 0x32, 0x22, +- 0x36, 0x35, 0x31, 0x27, 0x22, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x23, 0x2a, 0x2f, 0x22, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x26, 0x38, 0x38, 0x35, 0x25, +- 0x36, 0x21, 0x2d, 0x2b, 0x24, 0x21, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x21, 0x24, 0x39, 0x39, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x25, 0x2b, 0x30, 0x28, 0x22, +- 0x36, 0x36, 0x27, 0x34, 0x30, 0x23, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x21, 0x26, 0x2d, 0x26, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x22, 0x22, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x2d, 0x33, 0x28, 0x21, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x22, 0x30, 0x2f, 0x23, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x2b, 0x2c, 0x25, 0x21, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x23, 0x2a, 0x34, 0x36, 0x36, +- 0x36, 0x21, 0x22, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x21, 0x23, 0x22, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x28, 0x34, 0x27, 0x22, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x23, 0x32, 0x2f, 0x36, 0x36, +- 0x21, 0x21, 0x24, 0x27, 0x21, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x28, 0x27, 0x22, 0x33, 0x24, 0x36, +- 0x36, 0x36, 0x36, 0x22, 0x2f, 0x2a, 0x23, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x23, 0x32, 0x2f, 0x36, 0x36, +- 0x30, 0x3a, 0x38, 0x24, 0x24, 0x36, 0x36, 0x36, +- 0x23, 0x2f, 0x3b, 0x3c, 0x3d, 0x30, 0x25, 0x21, +- 0x36, 0x36, 0x36, 0x36, 0x2f, 0x32, 0x23, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x23, 0x32, 0x2f, 0x36, 0x23, +- 0x3e, 0x3f, 0x40, 0x3a, 0x22, 0x36, 0x36, 0x21, +- 0x41, 0x42, 0x43, 0x44, 0x45, 0x3e, 0x23, 0x21, +- 0x36, 0x36, 0x36, 0x36, 0x2f, 0x33, 0x28, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x29, 0x20, 0x29, 0x29, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x23, 0x32, 0x2f, 0x36, 0x2b, +- 0x44, 0x40, 0x46, 0x47, 0x35, 0x36, 0x36, 0x26, +- 0x43, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x2e, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x31, 0x35, 0x24, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x23, 0x32, 0x34, 0x36, 0x4d, +- 0x4e, 0x25, 0x2f, 0x46, 0x4a, 0x22, 0x23, 0x32, +- 0x4f, 0x50, 0x21, 0x31, 0x51, 0x52, 0x53, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x31, 0x35, 0x24, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x29, 0x20, 0x29, 0x29, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x23, 0x2a, 0x2f, 0x21, 0x3a, +- 0x4d, 0x21, 0x31, 0x54, 0x55, 0x28, 0x30, 0x2b, +- 0x4b, 0x4d, 0x36, 0x23, 0x32, 0x50, 0x3f, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x2e, 0x39, 0x24, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x29, 0x20, 0x29, 0x20, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x23, 0x2a, 0x38, 0x23, 0x37, +- 0x55, 0x36, 0x28, 0x3a, 0x56, 0x57, 0x57, 0x58, +- 0x3c, 0x4d, 0x36, 0x36, 0x36, 0x40, 0x40, 0x21, +- 0x36, 0x36, 0x36, 0x36, 0x2e, 0x39, 0x24, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x29, 0x29, 0x29, 0x20, 0x29, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x22, 0x30, 0x51, 0x23, 0x35, +- 0x43, 0x25, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, +- 0x5f, 0x60, 0x61, 0x36, 0x31, 0x47, 0x3b, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x31, 0x2c, 0x25, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x22, 0x30, 0x2f, 0x23, 0x22, +- 0x40, 0x62, 0x63, 0x5d, 0x64, 0x65, 0x66, 0x67, +- 0x68, 0x69, 0x66, 0x5e, 0x6a, 0x6b, 0x2a, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x33, 0x2e, 0x26, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x22, 0x27, 0x2f, 0x23, 0x36, +- 0x6c, 0x63, 0x6d, 0x64, 0x5c, 0x66, 0x69, 0x6e, +- 0x6f, 0x70, 0x71, 0x69, 0x69, 0x72, 0x6c, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x33, 0x34, 0x27, 0x22, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x22, 0x27, 0x34, 0x26, 0x73, +- 0x74, 0x75, 0x76, 0x64, 0x65, 0x77, 0x69, 0x78, +- 0x70, 0x71, 0x71, 0x71, 0x72, 0x5f, 0x5e, 0x21, +- 0x36, 0x36, 0x36, 0x36, 0x25, 0x38, 0x2a, 0x23, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x22, 0x26, 0x2d, 0x33, 0x79, +- 0x63, 0x7a, 0x7b, 0x5c, 0x66, 0x69, 0x6e, 0x7c, +- 0x71, 0x71, 0x69, 0x7d, 0x7e, 0x7a, 0x7f, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x21, 0x51, 0x2b, 0x28, +- 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x22, 0x26, 0x2d, 0x32, 0x24, +- 0x80, 0x81, 0x64, 0x82, 0x77, 0x69, 0x71, 0x71, +- 0x69, 0x83, 0x84, 0x85, 0x7a, 0x85, 0x86, 0x36, +- 0x21, 0x2b, 0x23, 0x36, 0x36, 0x39, 0x2e, 0x26, +- 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x22, 0x27, 0x2d, 0x33, 0x21, +- 0x87, 0x88, 0x89, 0x72, 0x67, 0x66, 0x5f, 0x89, +- 0x8a, 0x63, 0x85, 0x8b, 0x8c, 0x8d, 0x41, 0x36, +- 0x36, 0x2d, 0x3a, 0x35, 0x36, 0x24, 0x51, 0x32, +- 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x22, 0x30, 0x2f, 0x33, 0x21, +- 0x55, 0x8e, 0x8f, 0x8a, 0x7d, 0x5e, 0x90, 0x7e, +- 0x75, 0x75, 0x90, 0x62, 0x40, 0x3f, 0x49, 0x23, +- 0x36, 0x24, 0x3a, 0x3a, 0x24, 0x36, 0x2e, 0x31, +- 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x21, 0x28, 0x33, 0x37, 0x25, 0x22, +- 0x3b, 0x50, 0x8e, 0x8f, 0x90, 0x7e, 0x90, 0x63, +- 0x74, 0x91, 0x92, 0x42, 0x93, 0x4b, 0x45, 0x2c, +- 0x36, 0x36, 0x33, 0x39, 0x21, 0x36, 0x22, 0x51, +- 0x33, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x22, 0x27, 0x2e, 0x2e, 0x36, 0x21, +- 0x94, 0x3f, 0x50, 0x95, 0x96, 0x8f, 0x8f, 0x97, +- 0x8e, 0x42, 0x50, 0x43, 0x47, 0x48, 0x48, 0x98, +- 0x21, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x39, +- 0x2e, 0x27, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x22, 0x24, 0x2b, 0x38, 0x28, 0x36, 0x32, +- 0x4c, 0x4b, 0x50, 0x50, 0x50, 0x42, 0x42, 0x50, +- 0x50, 0x40, 0x45, 0x99, 0x48, 0x48, 0x48, 0x48, +- 0x34, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x23, +- 0x2f, 0x2b, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x21, 0x28, 0x32, 0x51, 0x32, 0x28, 0x21, 0x98, +- 0x48, 0x47, 0x9a, 0x50, 0x50, 0x50, 0x50, 0x50, +- 0x9a, 0x4f, 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x93, 0x23, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x2a, 0x2f, 0x2a, 0x28, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, +- 0x23, 0x30, 0x2e, 0x2c, 0x36, 0x21, 0x51, 0x9b, +- 0x48, 0x48, 0x52, 0x3f, 0x50, 0x50, 0x40, 0x4b, +- 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x34, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x2d, 0x31, 0x27, 0x23, 0x21, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, +- 0x27, 0x2c, 0x2d, 0x21, 0x36, 0x28, 0x44, 0x48, +- 0x48, 0x48, 0x48, 0x47, 0x46, 0x4f, 0x47, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x9c, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x28, 0x51, 0x39, 0x26, 0x22, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x25, +- 0x35, 0x51, 0x28, 0x36, 0x36, 0x9d, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x9b, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x4f, 0x28, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x28, 0x38, 0x2b, 0x25, 0x22, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, 0x33, +- 0x51, 0x25, 0x36, 0x36, 0x23, 0x40, 0x9b, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x9b, 0x99, 0x2b, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x30, 0x2f, 0x33, 0x24, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x21, 0x23, 0x30, 0x34, +- 0x27, 0x36, 0x36, 0x36, 0x2a, 0x40, 0x47, 0x48, +- 0x48, 0x48, 0x48, 0x9b, 0x99, 0x99, 0x9b, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x9b, 0x47, 0x52, +- 0x46, 0x4f, 0x37, 0x21, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x30, 0x34, 0x2a, 0x23, +- 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x22, 0x25, 0x39, 0x2c, +- 0x36, 0x36, 0x36, 0x21, 0x31, 0x4e, 0x9a, 0x4c, +- 0x47, 0x9b, 0x9b, 0x52, 0x46, 0x4f, 0x52, 0x9b, +- 0x9b, 0x9b, 0x47, 0x4f, 0x45, 0x9a, 0x93, 0x93, +- 0x3f, 0x93, 0x98, 0x28, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x39, 0x2c, 0x26, +- 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x23, 0x2a, 0x34, 0x28, +- 0x36, 0x36, 0x36, 0x22, 0x38, 0x98, 0x44, 0x99, +- 0x9b, 0x48, 0x48, 0x9b, 0x4c, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x47, 0x52, 0x46, 0x43, 0x93, +- 0x40, 0x40, 0x43, 0x53, 0x21, 0x23, 0x33, 0x23, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0x2f, 0x32, +- 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x21, 0x24, 0x2b, 0x31, 0x36, +- 0x36, 0x22, 0x36, 0x24, 0x9e, 0x4f, 0x9b, 0x48, +- 0x48, 0x48, 0x48, 0x9b, 0x99, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x47, +- 0x4f, 0x9a, 0x3f, 0x46, 0x38, 0x36, 0x21, 0x30, +- 0x26, 0x36, 0x36, 0x36, 0x36, 0x36, 0x39, 0x2c, +- 0x25, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x22, 0x26, 0x2e, 0x33, 0x36, +- 0x25, 0x25, 0x36, 0x4d, 0x52, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x47, 0x44, 0x93, 0x43, 0x23, 0x36, 0x36, +- 0x26, 0x24, 0x36, 0x36, 0x36, 0x36, 0x28, 0x2f, +- 0x2a, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x23, 0x2a, 0x51, 0x24, 0x36, +- 0x2a, 0x36, 0x28, 0x44, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x9b, 0x4b, 0x44, 0x37, 0x36, 0x23, +- 0x28, 0x30, 0x22, 0x36, 0x36, 0x36, 0x36, 0x2d, +- 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x21, 0x28, 0x2b, 0x34, 0x36, 0x25, +- 0x24, 0x36, 0x4a, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x9b, 0x52, 0x3f, 0x21, 0x30, +- 0x35, 0x25, 0x30, 0x36, 0x36, 0x36, 0x36, 0x32, +- 0x2d, 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x22, 0x26, 0x2e, 0x35, 0x36, 0x2a, +- 0x36, 0x24, 0x4f, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x9b, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x47, 0x32, 0x30, +- 0x2a, 0x23, 0x30, 0x23, 0x36, 0x36, 0x36, 0x21, +- 0x2f, 0x32, 0x23, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x21, 0x23, 0x2a, 0x51, 0x28, 0x28, 0x25, +- 0x36, 0x3a, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x9b, 0x52, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x38, 0x21, +- 0x36, 0x36, 0x22, 0x27, 0x36, 0x36, 0x36, 0x36, +- 0x2e, 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x22, 0x25, 0x2c, 0x34, 0x36, 0x30, 0x21, +- 0x23, 0x43, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x47, 0x99, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x94, 0x36, +- 0x36, 0x36, 0x36, 0x32, 0x36, 0x36, 0x36, 0x36, +- 0x2a, 0x2e, 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x21, 0x23, 0x2a, 0x51, 0x25, 0x21, 0x2a, 0x36, +- 0x2e, 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x99, 0x99, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x98, 0x36, +- 0x36, 0x36, 0x36, 0x32, 0x36, 0x36, 0x36, 0x36, +- 0x22, 0x2f, 0x30, 0x22, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x22, 0x25, 0x2c, 0x34, 0x36, 0x24, 0x28, 0x36, +- 0x54, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4c, 0x99, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x9a, 0x36, +- 0x36, 0x36, 0x36, 0x30, 0x36, 0x36, 0x36, 0x36, +- 0x21, 0x2f, 0x32, 0x23, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, +- 0x28, 0x32, 0x2f, 0x28, 0x36, 0x27, 0x22, 0x21, +- 0x43, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4c, 0x99, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4b, 0x21, +- 0x36, 0x36, 0x21, 0x26, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x34, 0x2b, 0x28, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, +- 0x25, 0x2c, 0x39, 0x36, 0x36, 0x30, 0x22, 0x25, +- 0x52, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4f, 0x52, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4f, 0x21, +- 0x36, 0x36, 0x22, 0x26, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x2c, 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, +- 0x30, 0x2d, 0x21, 0x36, 0x36, 0x32, 0x23, 0x2a, +- 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4f, 0x99, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4c, 0x22, +- 0x36, 0x36, 0x24, 0x23, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x2c, 0x39, 0x24, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, +- 0x33, 0x2e, 0x36, 0x36, 0x23, 0x31, 0x27, 0x39, +- 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4f, 0x47, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x4c, 0x23, +- 0x36, 0x36, 0x26, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x2c, 0x39, 0x24, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, +- 0x2b, 0x39, 0x36, 0x36, 0x36, 0x26, 0x32, 0x31, +- 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4f, 0x47, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x46, 0x22, +- 0x36, 0x21, 0x26, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x2c, 0x35, 0x24, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, +- 0x35, 0x39, 0x36, 0x36, 0x36, 0x36, 0x26, 0x2d, +- 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4f, 0x47, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x9a, 0x36, +- 0x24, 0x27, 0x9f, 0x24, 0x25, 0x28, 0x21, 0x36, +- 0x36, 0x34, 0x2b, 0x28, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x25, +- 0x39, 0x4d, 0xa0, 0x84, 0x81, 0x57, 0x21, 0x39, +- 0x52, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4f, 0x47, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x53, 0x28, +- 0x23, 0x36, 0x36, 0x36, 0x21, 0x28, 0x2c, 0x30, +- 0x21, 0x38, 0x33, 0x28, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x21, 0x22, 0x22, 0x28, 0x30, +- 0x2d, 0xa1, 0x7a, 0xa2, 0xa3, 0xa3, 0x7f, 0x22, +- 0x51, 0x52, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4f, 0x9b, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0xa4, 0xa5, 0xa5, 0xa6, 0x61, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x30, 0x32, +- 0x25, 0x4d, 0x2b, 0x28, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x21, 0x23, 0x24, 0x26, 0x30, 0x33, 0x31, +- 0x4d, 0x91, 0x5b, 0xa2, 0xa3, 0xa3, 0xa3, 0x5a, +- 0x21, 0x2e, 0x46, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4f, 0x9b, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0xa7, 0xa8, 0x69, 0x66, 0xa9, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0x26, 0x25, +- 0x83, 0xaa, 0x2c, 0x25, 0x21, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x21, 0x28, 0x30, 0x35, 0x2d, 0x2f, 0x37, 0x4a, +- 0x60, 0x85, 0xab, 0xac, 0xa3, 0xa3, 0xa3, 0x82, +- 0x86, 0x36, 0x32, 0x3f, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x4c, 0x99, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0xad, 0xa2, 0xa8, 0xae, 0xaf, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x21, 0x57, +- 0x77, 0x66, 0x34, 0x27, 0x22, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x23, 0x30, 0x31, 0xb0, 0x91, 0x7e, 0x90, 0x90, +- 0x8b, 0x5b, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0x5d, 0xb1, 0x36, 0x24, 0x53, 0x9b, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x9b, 0x99, 0xad, 0x64, 0x5c, 0x8b, 0xb1, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x73, 0x5d, +- 0x82, 0x5c, 0xb2, 0x2a, 0x23, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, +- 0x24, 0x2b, 0xb0, 0x8b, 0x5b, 0x76, 0x5b, 0x5b, +- 0x7b, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa8, 0x5e, 0x22, 0x36, 0x21, 0x3a, 0x99, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x4f, 0x3f, 0xb3, 0x7b, 0x7b, 0x85, 0x80, +- 0x9f, 0x36, 0x36, 0x36, 0x21, 0xb4, 0x7e, 0x7b, +- 0x64, 0x64, 0xb5, 0x35, 0x24, 0x21, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, +- 0x26, 0x31, 0xb6, 0x5b, 0x64, 0xa2, 0xa2, 0xac, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0x66, 0xb7, 0x36, 0x36, 0x36, 0x2c, 0x4b, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x9a, 0x3f, 0xb8, 0x76, 0x76, 0x7a, 0x63, +- 0xb9, 0xba, 0x86, 0xba, 0xbb, 0x90, 0x5b, 0x64, +- 0xa2, 0xa2, 0xbc, 0x2d, 0x27, 0x23, 0x21, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, +- 0x26, 0x2d, 0x91, 0x5b, 0x64, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa8, 0x83, 0xaf, 0x36, 0x36, 0x36, 0x30, +- 0x44, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x9b, 0x9a, 0x3f, 0xbd, 0x5b, 0x7b, 0xbe, 0x85, +- 0x7e, 0x90, 0x63, 0x90, 0x85, 0x5b, 0xa2, 0xa3, +- 0xa3, 0xac, 0x5d, 0xb5, 0x39, 0x26, 0x23, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, +- 0x26, 0x2d, 0xbf, 0xbe, 0x64, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa8, 0x88, 0x36, 0x36, 0x36, 0x36, +- 0x2d, 0x9b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x9b, 0x45, 0x3f, 0xc0, 0x6d, 0x7b, 0xab, 0xbe, +- 0x7a, 0x8b, 0x8b, 0x7a, 0x5b, 0x64, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa2, 0xc1, 0x37, 0x35, 0x26, 0x23, +- 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, +- 0x26, 0x2e, 0xbf, 0x7a, 0x7b, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa8, 0x72, 0x73, 0x36, 0x36, 0x36, +- 0x24, 0x52, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x46, 0x42, 0xb6, 0x7a, 0x7b, 0x64, 0x7b, +- 0x76, 0x5b, 0x5b, 0x76, 0x7b, 0xa2, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xac, 0x64, 0xc1, 0x4d, 0x2c, 0x27, +- 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, +- 0x25, 0x31, 0xc2, 0x8b, 0x7b, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa8, 0x89, 0x9f, 0x36, 0x36, +- 0x32, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x4b, 0x2f, 0x8f, 0x7a, 0x7b, 0xa2, 0xac, +- 0xa2, 0x64, 0x64, 0xa2, 0xa2, 0xac, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0x5d, 0xc3, 0x2c, +- 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, +- 0x25, 0x31, 0xc2, 0x85, 0x7b, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0x66, 0x57, 0x27, 0x4d, +- 0x4b, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x99, 0x34, 0x9f, 0xb9, 0x7a, 0x7b, 0xa2, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0xc2, +- 0x32, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, +- 0x26, 0x2d, 0xc2, 0x85, 0x7b, 0xac, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa8, 0x5f, 0x92, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x44, +- 0x35, 0x36, 0xaf, 0xbb, 0x7a, 0x7b, 0xac, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xac, 0xa2, 0xc0, +- 0x2b, 0x24, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, +- 0x30, 0x2f, 0xb6, 0x8b, 0x7b, 0xac, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x66, 0x89, 0x45, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x48, 0x9b, 0x4e, 0x25, +- 0x36, 0x36, 0x61, 0xb9, 0x6d, 0x64, 0xac, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xac, 0x7b, 0xbe, 0xc3, +- 0x32, 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, +- 0x33, 0xc4, 0x63, 0xbe, 0xa2, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0x72, 0x81, 0xc5, +- 0x46, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, +- 0x48, 0x48, 0x48, 0x48, 0x3f, 0x2c, 0x36, 0x36, +- 0x36, 0x36, 0xc6, 0x8f, 0x6d, 0x64, 0xac, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa2, 0xab, 0x8b, 0xb0, 0x2c, +- 0x26, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, +- 0x35, 0x96, 0x75, 0xab, 0xa2, 0xac, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xac, 0x7b, 0x81, 0xb9, +- 0x73, 0x3b, 0x44, 0x9b, 0x48, 0x48, 0x48, 0x9b, +- 0x99, 0x43, 0x94, 0x2c, 0x21, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x73, 0xb9, 0x7a, 0x7b, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0x64, 0x76, 0x7a, 0x91, 0xb5, 0x31, 0x30, +- 0x28, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x24, +- 0x39, 0x97, 0x75, 0xbe, 0x7b, 0x64, 0xa2, 0xa2, +- 0xac, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0x7b, 0x7a, 0xc7, +- 0xc8, 0x36, 0x21, 0x26, 0x2b, 0x39, 0x33, 0x30, +- 0x23, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x21, 0xc8, 0xbb, 0x8b, 0x7b, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, 0x64, 0x64, +- 0x76, 0x85, 0xbf, 0xb5, 0x34, 0x2b, 0x27, 0x28, +- 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x28, +- 0x33, 0xc9, 0x63, 0x7e, 0x7a, 0x6d, 0xbe, 0x5b, +- 0x76, 0x7b, 0x64, 0x64, 0xa2, 0xac, 0xa3, 0xa3, +- 0xa3, 0xa3, 0xa3, 0xa3, 0xac, 0x76, 0x85, 0xb9, +- 0x79, 0x22, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x21, 0xca, 0xbb, 0x75, 0x76, 0xa2, 0xa3, +- 0xa3, 0xa3, 0xac, 0xa2, 0x64, 0x76, 0xbe, 0x8b, +- 0xb6, 0xb5, 0x2f, 0x35, 0x30, 0x24, 0x22, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, +- 0x27, 0x31, 0xcb, 0xc9, 0xbb, 0x74, 0x63, 0x90, +- 0x7e, 0x75, 0x8b, 0x6d, 0xbe, 0x76, 0x64, 0xa2, +- 0xac, 0xac, 0xac, 0xac, 0x64, 0x7a, 0x84, 0xcc, +- 0x79, 0x9f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +- 0x36, 0x21, 0xc8, 0xcc, 0x63, 0x6d, 0x7b, 0x64, +- 0xac, 0xa2, 0x64, 0x7b, 0xbe, 0x75, 0x63, 0x96, +- 0x38, 0x39, 0x2a, 0x24, 0x23, 0x21, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, +- 0x28, 0x27, 0x35, 0x2d, 0x41, 0xb5, 0xc5, 0x8f, +- 0xb9, 0xbb, 0xc7, 0x74, 0x84, 0x90, 0x85, 0x6d, +- 0x5b, 0x7b, 0x7b, 0xab, 0x6d, 0x90, 0xb9, 0xcd, +- 0xca, 0x22, 0x36, 0x36, 0x28, 0x30, 0x30, 0x30, +- 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x25, 0x36, +- 0x36, 0x21, 0xb4, 0x80, 0xc7, 0x7e, 0x6d, 0x76, +- 0xab, 0x76, 0x6d, 0x85, 0x63, 0xb9, 0xb5, 0x34, +- 0x33, 0x26, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x21, 0x23, 0x24, 0x27, 0x2a, 0x35, 0x2e, 0x2f, +- 0x41, 0xce, 0xcf, 0x6c, 0x80, 0xcc, 0xb9, 0x74, +- 0x84, 0x90, 0x75, 0x7e, 0x74, 0x8f, 0xcd, 0x79, +- 0xc6, 0x2b, 0x9d, 0x41, 0x2f, 0x34, 0x2d, 0x2d, +- 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x34, 0x2f, 0x38, +- 0x4d, 0x37, 0xd0, 0xd1, 0x8f, 0x74, 0x63, 0x7e, +- 0x75, 0x7e, 0x63, 0xc7, 0x88, 0xc4, 0x31, 0x2a, +- 0x24, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x21, 0x22, 0x23, 0x24, 0x26, 0x30, +- 0x33, 0x39, 0x2e, 0x51, 0x41, 0xb2, 0x6c, 0xd1, +- 0x80, 0xcc, 0xcc, 0xcc, 0xd2, 0xd1, 0xb7, 0xd3, +- 0x41, 0x34, 0x35, 0x32, 0x30, 0x27, 0x27, 0x27, +- 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x30, 0x2a, +- 0x2b, 0x34, 0xd4, 0xca, 0xd5, 0x8f, 0xbb, 0xc7, +- 0xc7, 0xbb, 0xcc, 0x6c, 0x41, 0x39, 0x27, 0x28, +- 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, +- 0x28, 0x24, 0x26, 0x2a, 0x33, 0x2c, 0x2f, 0x41, +- 0xd6, 0xb7, 0x79, 0x79, 0x79, 0xca, 0xd7, 0x51, +- 0x39, 0x30, 0x24, 0x23, 0x22, 0x22, 0x22, 0x22, +- 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, +- 0x24, 0x2a, 0x31, 0xd8, 0xc8, 0x79, 0xd1, 0x80, +- 0xd5, 0xba, 0xd9, 0x2f, 0x35, 0x26, 0x23, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x21, 0x22, 0x23, 0x28, 0x25, 0x30, 0x2b, +- 0x31, 0x2f, 0xd4, 0xd8, 0xd8, 0x2f, 0x2e, 0x33, +- 0x26, 0x23, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x21, 0x28, 0x27, 0x35, 0x34, 0xd8, 0xd8, 0xd8, +- 0xda, 0xd4, 0x2e, 0x33, 0x25, 0x23, 0x21, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x23, 0x28, +- 0x26, 0x30, 0x32, 0x2b, 0x33, 0x2a, 0x26, 0x28, +- 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x21, 0x23, 0x25, 0x30, 0x33, 0x35, 0x35, +- 0x2b, 0x2a, 0x26, 0x28, 0x22, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, +- 0x21, 0x22, 0x23, 0x28, 0x28, 0x23, 0x22, 0x21, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x21, 0x23, 0x28, 0x24, 0x24, +- 0x28, 0x23, 0x22, 0x21, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x23, 0x33, 0x22, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x23, 0x61, 0x62, 0x2B, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x35, 0x61, 0x63, 0x64, 0x35, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x65, 0x66, 0x66, 0x67, 0x55, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x3B, ++ 0x3B, 0x68, 0x64, 0x69, 0x62, 0x55, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x22, 0x23, 0x23, 0x55, 0x6A, 0x6B, ++ 0x6C, 0x6D, 0x6E, 0x6F, 0x6F, 0x33, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, ++ 0x22, 0x23, 0x35, 0x33, 0x3B, 0x70, 0x71, 0x72, ++ 0x73, 0x74, 0x75, 0x76, 0x77, 0x55, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x22, 0x23, 0x23, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x23, 0x55, ++ 0x2B, 0x34, 0x34, 0x42, 0x70, 0x71, 0x73, 0x78, ++ 0x78, 0x78, 0x79, 0x7A, 0x7B, 0x34, 0x22, 0x21, ++ 0x55, 0x33, 0x23, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x22, 0x3B, 0x7C, 0x7D, 0x7E, 0x34, 0x35, ++ 0x21, 0x21, 0x21, 0x21, 0x35, 0x3B, 0x4C, 0x3C, ++ 0x42, 0x3F, 0x56, 0x7F, 0x71, 0x74, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x79, 0x80, 0x81, 0x3C, 0x82, ++ 0x83, 0x62, 0x61, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x23, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, ++ 0x3B, 0x21, 0x55, 0x55, 0x2B, 0x34, 0x39, 0x3F, ++ 0x2A, 0x57, 0x8B, 0x72, 0x74, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x75, 0x8C, 0x6F, 0x63, ++ 0x63, 0x66, 0x61, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x84, 0x85, 0x86, 0x8D, 0x88, 0x8E, 0x8F, 0x5E, ++ 0x3C, 0x34, 0x3B, 0x34, 0x3F, 0x2A, 0x58, 0x57, ++ 0x52, 0x46, 0x6C, 0x73, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x79, 0x7A, 0x6E, 0x69, ++ 0x69, 0x90, 0x34, 0x33, 0x22, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, ++ 0x91, 0x92, 0x8D, 0x87, 0x8E, 0x93, 0x8A, 0x35, ++ 0x94, 0x95, 0x96, 0x56, 0x32, 0x32, 0x52, 0x5C, ++ 0x4D, 0x82, 0x72, 0x78, 0x78, 0x78, 0x78, 0x79, ++ 0x97, 0x78, 0x78, 0x78, 0x78, 0x75, 0x6D, 0x6F, ++ 0x62, 0x65, 0x34, 0x4C, 0x33, 0x22, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x2B, ++ 0x91, 0x86, 0x87, 0x88, 0x93, 0x98, 0x60, 0x3F, ++ 0x99, 0x9A, 0x9A, 0x9B, 0x9C, 0x57, 0x5C, 0x36, ++ 0x9D, 0x9E, 0x6D, 0x78, 0x78, 0x78, 0x97, 0x6E, ++ 0x9F, 0x78, 0x78, 0x78, 0x78, 0x79, 0x80, 0xA0, ++ 0x61, 0x51, 0x51, 0x3C, 0x4C, 0x55, 0x35, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x3B, 0x22, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x4C, ++ 0xA1, 0x8D, 0x88, 0x8E, 0xA2, 0xA3, 0x94, 0x42, ++ 0x51, 0xA4, 0x9A, 0x9A, 0x9A, 0xA5, 0x9C, 0x2A, ++ 0xA6, 0x74, 0x8C, 0x75, 0x97, 0xA7, 0x76, 0x75, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x75, 0x6E, ++ 0x46, 0x52, 0x56, 0x3F, 0x42, 0x34, 0x33, 0x35, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x22, 0x22, 0x21, 0x21, 0x22, ++ 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x52, 0x3A, ++ 0x5A, 0x53, 0x53, 0x29, 0x43, 0x50, 0x53, 0x5A, ++ 0x5A, 0x29, 0x5A, 0x43, 0x53, 0x5D, 0x44, 0x2A, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x55, ++ 0x7D, 0x87, 0x8E, 0x93, 0xA8, 0xA9, 0xAA, 0x2A, ++ 0x57, 0x94, 0xA4, 0x9B, 0xAB, 0xAC, 0x9A, 0x9B, ++ 0xAD, 0x76, 0x79, 0x78, 0x75, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x75, 0x6D, ++ 0x46, 0x5C, 0x57, 0x56, 0x51, 0x42, 0x4C, 0x55, ++ 0x23, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x28, 0x27, ++ 0x29, 0x2A, 0x2B, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x38, 0x30, ++ 0x59, 0x3D, 0x3D, 0x3D, 0x27, 0x27, 0x26, 0x5D, ++ 0x27, 0x27, 0x54, 0x37, 0x2E, 0x4A, 0x36, 0x2B, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0xAE, 0x88, 0xAF, 0xB0, 0xAA, 0x2A, 0x39, 0x34, ++ 0x4C, 0x5C, 0x58, 0xB1, 0xB2, 0xB3, 0xAB, 0xA5, ++ 0x9A, 0xB4, 0xB5, 0x8C, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x80, ++ 0xB6, 0x38, 0x5C, 0x57, 0x56, 0x3F, 0x42, 0x3B, ++ 0x55, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, ++ 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x32, ++ 0x31, 0x2C, 0x26, 0x2C, 0x33, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x34, ++ 0x32, 0x2B, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, 0x33, ++ 0x33, 0x55, 0x55, 0x55, 0x23, 0x23, 0x35, 0x23, ++ 0x23, 0x35, 0x2B, 0x4E, 0x4F, 0x2A, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x55, 0x38, 0x25, ++ 0x2C, 0x52, 0x34, 0x22, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x55, 0x8A, 0x8F, 0x39, 0x34, 0x3C, 0x57, 0x32, ++ 0x46, 0x42, 0x52, 0x4A, 0x51, 0xAA, 0xB7, 0xB3, ++ 0xAB, 0xAC, 0xAC, 0xB8, 0xB9, 0x79, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x7A, ++ 0xBA, 0x70, 0x7B, 0xBB, 0x82, 0x56, 0x3F, 0x34, ++ 0x4C, 0x55, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, 0x36, ++ 0x2D, 0x37, 0x38, 0x2A, 0x2B, 0x21, 0x21, 0x21, ++ 0x21, 0x22, 0x39, 0x3A, 0x2C, 0x3B, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x33, 0x3C, 0x22, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x24, 0x3D, ++ 0x3E, 0x25, 0x3C, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x33, 0x29, 0x26, 0x2A, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x3B, 0x54, 0x3E, 0x28, ++ 0x28, 0x59, 0x59, 0x48, 0x51, 0x3F, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x2B, 0x56, 0x4D, 0x29, 0x5B, 0x34, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x55, 0x60, 0x3F, 0x42, 0x51, 0x32, 0x52, ++ 0x40, 0x38, 0x40, 0x46, 0x42, 0xBC, 0xBD, 0xBE, ++ 0xBF, 0x9B, 0xAB, 0x9B, 0xC0, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x75, ++ 0xA7, 0x63, 0x63, 0x66, 0x6F, 0x58, 0x56, 0x39, ++ 0x34, 0x2B, 0x23, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x24, 0x2F, ++ 0x3D, 0x3F, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x22, 0x40, 0x41, 0x3B, 0x21, ++ 0x21, 0x22, 0x42, 0x38, 0x43, 0x44, 0x3A, 0x45, ++ 0x34, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x46, 0x30, 0x47, ++ 0x2A, 0x48, 0x48, 0x21, 0x21, 0x21, 0x21, 0x22, ++ 0x23, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x22, 0x34, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x22, 0x4D, 0x44, 0x2A, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x22, 0x50, 0x4A, 0x57, 0x35, ++ 0x35, 0x2B, 0x56, 0x27, 0x4B, 0x5D, 0x35, 0x21, ++ 0x21, 0x21, 0x21, 0x3B, 0x52, 0x23, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x2B, ++ 0x55, 0x21, 0x21, 0x21, 0x21, 0x21, 0x3B, 0x38, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, ++ 0x3C, 0x42, 0x21, 0x33, 0x24, 0x35, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, ++ 0x2B, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x33, 0x38, 0x59, 0x37, 0x27, 0x27, 0x4A, 0x47, ++ 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x23, 0x4C, 0x4C, 0x2A, 0x56, 0x46, 0x31, ++ 0x4D, 0x36, 0x50, 0x5A, 0x35, 0xC1, 0xC1, 0xC2, ++ 0xC3, 0xB7, 0xB3, 0xBE, 0x76, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x75, ++ 0x6D, 0x62, 0x69, 0x6F, 0x58, 0x46, 0x32, 0x2A, ++ 0x39, 0x34, 0x35, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x3B, 0x44, 0x49, ++ 0x31, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x23, 0x29, 0x48, 0x22, ++ 0x42, 0x3A, 0x4A, 0x49, 0x4B, 0x2E, 0x4B, 0x3E, ++ 0x4A, 0x2C, 0x34, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x4C, 0x30, 0x40, 0x22, ++ 0x21, 0x32, 0x3A, 0x22, 0x21, 0x21, 0x21, 0x39, ++ 0x36, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, ++ 0x31, 0x4F, 0x2A, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x31, 0x59, 0x2A, 0x22, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x55, 0x27, 0x24, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x4C, 0x44, 0x27, 0x35, 0x21, ++ 0x21, 0x21, 0x21, 0x2A, 0x26, 0x2B, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x3A, ++ 0x40, 0x21, 0x21, 0x21, 0x21, 0x21, 0x51, 0x49, ++ 0x35, 0x21, 0x21, 0x21, 0x21, 0x22, 0x58, 0x25, ++ 0x5A, 0x57, 0x21, 0x4C, 0x3D, 0x34, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x38, ++ 0x47, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, ++ 0x3A, 0x4B, 0x53, 0x56, 0x55, 0x33, 0x42, 0x56, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x55, 0x4C, 0x42, 0x2A, 0x57, 0x5C, 0x38, ++ 0x36, 0x47, 0x5A, 0xB6, 0x77, 0xC4, 0xC1, 0xC1, ++ 0xC4, 0xC5, 0xB7, 0xB9, 0x79, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x75, ++ 0x97, 0xC6, 0x68, 0x57, 0x4D, 0x38, 0x46, 0x32, ++ 0x51, 0x42, 0x2B, 0x35, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x4D, 0x2D, 0x38, ++ 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x4C, 0x4E, 0x3C, ++ 0x47, 0x4F, 0x50, 0x51, 0x3F, 0x44, 0x42, 0x34, ++ 0x52, 0x53, 0x28, 0x2A, 0x22, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x48, 0x54, 0x33, 0x21, ++ 0x23, 0x3A, 0x3A, 0x22, 0x21, 0x21, 0x21, 0x2A, ++ 0x26, 0x33, 0x21, 0x21, 0x21, 0x21, 0x23, 0x45, ++ 0x37, 0x3D, 0x41, 0x22, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x31, ++ 0x59, 0x3F, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x3C, 0x28, 0x33, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x23, 0x27, 0x5B, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x3C, 0x54, 0x55, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x4C, 0x4B, ++ 0x46, 0x21, 0x21, 0x21, 0x21, 0x21, 0x3B, 0x49, ++ 0x2B, 0x21, 0x21, 0x21, 0x23, 0x40, 0x43, 0x32, ++ 0x35, 0x21, 0x21, 0x33, 0x28, 0x4C, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, 0x4F, ++ 0x5B, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, 0x50, ++ 0x44, 0x39, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x23, 0x3B, 0x34, 0x3F, 0x58, 0x46, 0x40, 0x36, ++ 0x50, 0x41, 0x4D, 0xC7, 0x65, 0xC2, 0xC1, 0xC1, ++ 0xC8, 0xBF, 0xB1, 0x97, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, ++ 0x80, 0xC9, 0x36, 0x50, 0x36, 0x38, 0x31, 0x32, ++ 0x56, 0x39, 0x34, 0x33, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x2B, 0x3D, 0x28, 0x35, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x40, 0x4D, ++ 0x55, 0x2B, 0x21, 0x21, 0x2B, 0x28, 0x3B, 0x21, ++ 0x21, 0x22, 0x52, 0x27, 0x56, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x23, 0x2A, 0x30, 0x3F, 0x21, 0x22, ++ 0x57, 0x44, 0x58, 0x21, 0x21, 0x21, 0x21, 0x56, ++ 0x37, 0x2B, 0x21, 0x21, 0x21, 0x21, 0x56, 0x3E, ++ 0x36, 0x2A, 0x59, 0x23, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, 0x50, 0x3D, ++ 0x42, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x40, 0x5A, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x56, 0x49, 0x24, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x42, 0x54, 0x33, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x4D, 0x59, ++ 0x55, 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, 0x4A, ++ 0x51, 0x21, 0x21, 0x33, 0x47, 0x47, 0x33, 0x21, ++ 0x21, 0x21, 0x21, 0x55, 0x27, 0x3B, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x32, 0x4B, ++ 0x4C, 0x21, 0x21, 0x21, 0x21, 0x21, 0x34, 0x44, ++ 0x3F, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x33, 0x4C, 0x3C, 0x51, 0x57, 0x5C, 0x38, 0x38, ++ 0x9D, 0xCA, 0x61, 0xCB, 0xCC, 0xCD, 0xC4, 0xC8, ++ 0xA4, 0x9C, 0x76, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, ++ 0x7A, 0x7F, 0x41, 0x29, 0x47, 0x38, 0x40, 0x57, ++ 0x32, 0x51, 0x42, 0x2B, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x57, 0x49, 0x32, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x57, 0x27, ++ 0x35, 0x21, 0x21, 0x21, 0x2B, 0x59, 0x2A, 0x21, ++ 0x21, 0x21, 0x21, 0x56, 0x54, 0x2B, 0x21, 0x21, ++ 0x21, 0x21, 0x4C, 0x2E, 0x5A, 0x23, 0x55, 0x5B, ++ 0x49, 0x3A, 0x22, 0x21, 0x21, 0x21, 0x21, 0x32, ++ 0x3E, 0x3B, 0x21, 0x21, 0x21, 0x35, 0x54, 0x53, ++ 0x23, 0x55, 0x4F, 0x55, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x4C, 0x25, 0x44, 0x42, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x23, 0x41, 0x46, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x23, 0x53, 0x2F, 0x2A, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x32, 0x28, 0x33, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, 0x59, 0x5B, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x54, ++ 0x57, 0x21, 0x35, 0x53, 0x47, 0x23, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x3C, 0x44, 0x3B, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x41, 0x43, ++ 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x3F, 0x29, ++ 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x55, 0x4C, 0x3F, 0x2A, 0x46, 0x31, 0x36, 0x46, ++ 0x71, 0x97, 0xCE, 0xCB, 0xCF, 0xA4, 0x9C, 0xA4, ++ 0xAD, 0x76, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x80, 0x8C, ++ 0x7A, 0xA7, 0x36, 0x50, 0x3A, 0x36, 0x38, 0x52, ++ 0x32, 0x2A, 0x3F, 0x2B, 0x22, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x22, 0x5A, 0x5A, 0x23, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x52, 0x28, ++ 0x35, 0x21, 0x21, 0x21, 0x55, 0x30, 0x56, 0x21, ++ 0x21, 0x21, 0x21, 0x22, 0x5B, 0x45, 0x21, 0x21, ++ 0x21, 0x21, 0x2B, 0x2E, 0x25, 0x47, 0x3D, 0x2D, ++ 0x25, 0x4C, 0x21, 0x21, 0x21, 0x21, 0x21, 0x39, ++ 0x44, 0x55, 0x21, 0x21, 0x21, 0x46, 0x59, 0x3B, ++ 0x21, 0x23, 0x30, 0x34, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x2A, 0x28, 0x4E, 0x42, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x3C, 0x3D, 0x42, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x51, 0x4B, 0x49, 0x39, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x5C, 0x25, 0x23, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x38, 0x4B, 0x3B, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x54, ++ 0x56, 0x21, 0x5B, 0x3A, 0x35, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x51, 0x59, 0x55, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x32, 0x2D, 0x42, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x4C, 0x3A, ++ 0x23, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x2B, 0x3C, 0x39, 0x56, 0x46, 0x40, 0x36, 0xB6, ++ 0x73, 0x82, 0xCB, 0xD0, 0x75, 0xD1, 0xD2, 0xD1, ++ 0x8C, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x74, 0xCE, ++ 0x9E, 0x6E, 0xD3, 0xC6, 0x6F, 0x81, 0x38, 0x5C, ++ 0x52, 0x58, 0x39, 0x4C, 0x35, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x4C, 0x4A, 0x40, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x36, 0x3A, ++ 0x21, 0x21, 0x21, 0x21, 0x55, 0x27, 0x3F, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x3B, 0x25, 0x3B, 0x21, ++ 0x21, 0x21, 0x42, 0x2E, 0x2D, 0x2D, 0x26, 0x46, ++ 0x35, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x51, ++ 0x44, 0x55, 0x21, 0x21, 0x35, 0x25, 0x40, 0x21, ++ 0x21, 0x22, 0x4E, 0x58, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x39, 0x59, 0x38, 0x35, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x56, 0x59, 0x23, 0x21, 0x21, 0x21, ++ 0x21, 0x4C, 0x54, 0x5B, 0x59, 0x4C, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x57, 0x3A, 0x22, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x22, 0x4D, 0x49, 0x26, 0x23, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x53, ++ 0x32, 0x51, 0x27, 0x42, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x42, 0x25, 0x35, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x22, 0x24, 0x37, 0x3E, 0x35, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x48, ++ 0x48, 0x33, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, ++ 0x3B, 0x39, 0x3F, 0x32, 0x5C, 0x38, 0x47, 0xD4, ++ 0x7A, 0xC7, 0xCB, 0xD0, 0x74, 0x80, 0x8C, 0x9F, ++ 0x80, 0x79, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x73, 0x72, 0xD5, 0xCA, 0x61, 0xD3, 0x40, 0x5C, ++ 0x46, 0x58, 0x51, 0x42, 0x33, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x39, 0x4B, 0x58, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x2A, 0x3D, 0x51, ++ 0x21, 0x21, 0x21, 0x21, 0x55, 0x28, 0x42, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x47, 0x52, 0x21, ++ 0x21, 0x21, 0x58, 0x59, 0x42, 0x3C, 0x35, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x57, ++ 0x4E, 0x23, 0x21, 0x22, 0x4D, 0x54, 0x35, 0x21, ++ 0x21, 0x21, 0x5A, 0x46, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x22, 0x56, 0x59, 0x36, 0x35, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x36, 0x5B, 0x21, 0x21, 0x21, 0x21, ++ 0x23, 0x53, 0x48, 0x3F, 0x27, 0x55, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x24, 0x47, 0x22, 0x21, 0x21, ++ 0x21, 0x21, 0x22, 0x40, 0x27, 0x29, 0x29, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x47, ++ 0x40, 0x41, 0x5C, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x39, 0x41, 0x23, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x32, 0x26, 0x47, 0x28, 0x22, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x2B, ++ 0x53, 0x28, 0x36, 0x42, 0x23, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, ++ 0x4C, 0x39, 0x58, 0x32, 0x31, 0x38, 0x47, 0xD4, ++ 0xCF, 0xD0, 0x6C, 0x9E, 0xD6, 0x6B, 0xD7, 0xD8, ++ 0xD7, 0x6B, 0xCF, 0x7B, 0x80, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x74, 0x72, 0xD0, 0xA6, 0x70, 0x51, ++ 0x46, 0x56, 0x51, 0x42, 0x55, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x5C, 0x27, 0x35, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x32, 0x37, 0x54, 0x33, ++ 0x21, 0x21, 0x21, 0x21, 0x2B, 0x44, 0x2A, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x52, 0x2C, 0x21, ++ 0x21, 0x21, 0x47, 0x36, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x52, ++ 0x54, 0x23, 0x21, 0x24, 0x4F, 0x46, 0x21, 0x21, ++ 0x21, 0x21, 0x38, 0x2C, 0x22, 0x21, 0x21, 0x21, ++ 0x21, 0x42, 0x55, 0x21, 0x21, 0x21, 0x21, 0x33, ++ 0x45, 0x54, 0x56, 0x22, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x22, 0x53, 0x24, 0x21, 0x21, 0x21, 0x21, ++ 0x46, 0x5D, 0x4C, 0x52, 0x4E, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x31, 0x48, 0x22, 0x21, 0x21, ++ 0x21, 0x22, 0x46, 0x54, 0x3B, 0x45, 0x29, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x47, ++ 0x28, 0x28, 0x55, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x51, 0x41, 0x22, 0x21, 0x21, ++ 0x21, 0x21, 0x56, 0x54, 0x39, 0x56, 0x28, 0x22, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x23, 0x34, 0x50, 0x30, 0x4E, 0x40, 0x34, 0x22, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, ++ 0x4C, 0x39, 0x58, 0x52, 0x5C, 0x38, 0x47, 0x9D, ++ 0xC9, 0xD9, 0xDA, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, ++ 0xDC, 0xDC, 0xDC, 0xDD, 0x6B, 0x7B, 0x75, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x74, 0x71, 0xD0, 0x3F, ++ 0x52, 0x57, 0x2A, 0x42, 0x2B, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x47, 0x3A, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x35, 0x45, 0x4A, 0x2C, 0x3B, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x2B, 0x30, 0x56, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x58, 0x29, 0x21, ++ 0x21, 0x21, 0x53, 0x3F, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x31, ++ 0x43, 0x22, 0x3B, 0x28, 0x48, 0x22, 0x21, 0x21, ++ 0x21, 0x21, 0x56, 0x25, 0x35, 0x21, 0x21, 0x21, ++ 0x39, 0x54, 0x34, 0x21, 0x21, 0x21, 0x35, 0x3A, ++ 0x30, 0x57, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x33, 0x39, 0x21, 0x21, ++ 0x21, 0x35, 0x25, 0x3F, 0x21, 0x21, 0x21, 0x56, ++ 0x30, 0x58, 0x21, 0x24, 0x43, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x4D, 0x29, 0x22, 0x21, 0x21, ++ 0x22, 0x32, 0x26, 0x42, 0x21, 0x46, 0x2C, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x48, ++ 0x2E, 0x36, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x56, 0x28, 0x35, 0x21, 0x21, ++ 0x21, 0x3F, 0x27, 0x32, 0x21, 0x51, 0x25, 0x22, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x22, 0x39, 0x38, 0x25, 0x54, 0x50, ++ 0x58, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, ++ 0x4C, 0x3F, 0x56, 0x52, 0x31, 0x4D, 0x47, 0x39, ++ 0xD8, 0xDC, 0xDC, 0xDC, 0xDE, 0xDF, 0xDF, 0xDF, ++ 0xDF, 0xDF, 0xDE, 0xDE, 0xDC, 0xDD, 0xD6, 0x97, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x75, 0x97, 0x56, ++ 0x46, 0x57, 0x2A, 0x42, 0x2B, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x38, 0x29, 0x22, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x35, 0x5B, 0x3D, 0x40, 0x35, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x2B, 0x59, 0x3F, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x40, 0x5A, 0x21, ++ 0x21, 0x22, 0x5D, 0x34, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x5B, ++ 0x2C, 0x33, 0x29, 0x28, 0x3B, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x34, 0x59, 0x3C, 0x21, 0x21, 0x2B, ++ 0x53, 0x36, 0x22, 0x21, 0x21, 0x34, 0x5A, 0x28, ++ 0x3F, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x40, 0x41, 0x23, 0x21, ++ 0x21, 0x3B, 0x28, 0x2B, 0x21, 0x21, 0x2A, 0x44, ++ 0x38, 0x22, 0x21, 0x24, 0x50, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x50, 0x25, 0x23, 0x21, 0x23, ++ 0x45, 0x5D, 0x52, 0x21, 0x21, 0x40, 0x50, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x50, ++ 0x2D, 0x39, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x46, 0x30, 0x33, 0x21, 0x22, ++ 0x24, 0x4E, 0x45, 0x22, 0x21, 0x58, 0x25, 0x22, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x33, 0x3C, 0x48, ++ 0x27, 0x39, 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, ++ 0x34, 0x3F, 0x58, 0x57, 0x31, 0x4D, 0x47, 0x56, ++ 0xDB, 0xDC, 0xDE, 0xE0, 0xE1, 0xE2, 0xE2, 0xE2, ++ 0xE2, 0xE2, 0xE1, 0xE3, 0xE4, 0xDE, 0xDC, 0x6B, ++ 0x8C, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x80, 0xA7, 0x38, ++ 0x46, 0x56, 0x2A, 0x39, 0x3B, 0x23, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x32, 0x44, 0x34, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, 0x3F, ++ 0x4D, 0x41, 0x29, 0x42, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x2B, 0x44, 0x46, 0x34, ++ 0x2B, 0x55, 0x55, 0x2B, 0x56, 0x59, 0x36, 0x21, ++ 0x21, 0x22, 0x59, 0x2B, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x55, 0x5C, 0x21, 0x21, 0x21, 0x40, ++ 0x50, 0x4D, 0x4F, 0x32, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x55, 0x44, 0x31, 0x22, 0x3B, 0x5A, ++ 0x41, 0x55, 0x21, 0x35, 0x57, 0x4A, 0x2E, 0x48, ++ 0x57, 0x32, 0x3F, 0x55, 0x35, 0x22, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x25, 0x47, 0x22, 0x21, ++ 0x21, 0x4C, 0x28, 0x35, 0x21, 0x34, 0x30, 0x3A, ++ 0x23, 0x21, 0x21, 0x58, 0x3A, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x4D, 0x4E, 0x35, 0x33, 0x48, ++ 0x44, 0x56, 0x21, 0x21, 0x21, 0x58, 0x41, 0x23, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x29, ++ 0x4A, 0x23, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x32, 0x44, 0x2B, 0x23, 0x45, ++ 0x3D, 0x31, 0x22, 0x21, 0x21, 0x4C, 0x27, 0x35, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, ++ 0x53, 0x53, 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, ++ 0x34, 0x3F, 0x58, 0x57, 0x40, 0x38, 0x47, 0xD4, ++ 0xDC, 0xDE, 0xE0, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE2, 0xE1, 0xDF, 0xDC, ++ 0xE6, 0x8C, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x80, 0x2A, 0x40, ++ 0x46, 0x58, 0x2A, 0x42, 0x34, 0x34, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x2B, 0x54, 0x5A, 0x34, 0x23, ++ 0x35, 0x23, 0x35, 0x33, 0x2B, 0x24, 0x54, 0x4B, ++ 0x59, 0x5C, 0x23, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x4C, 0x4B, 0x44, 0x59, ++ 0x27, 0x26, 0x26, 0x4A, 0x3E, 0x41, 0x55, 0x21, ++ 0x21, 0x22, 0x41, 0x39, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x34, 0x53, 0x4D, 0x21, 0x21, 0x21, 0x5C, ++ 0x4B, 0x4F, 0x40, 0x22, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x48, 0x30, 0x50, 0x26, 0x54, ++ 0x34, 0x21, 0x55, 0x4E, 0x2D, 0x2F, 0x4F, 0x37, ++ 0x3E, 0x4B, 0x4B, 0x30, 0x27, 0x53, 0x2C, 0x57, ++ 0x42, 0x4C, 0x55, 0x22, 0x22, 0x22, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x39, 0x4A, 0x3F, 0x21, 0x21, ++ 0x21, 0x55, 0x53, 0x55, 0x2A, 0x26, 0x54, 0x33, ++ 0x21, 0x21, 0x21, 0x51, 0x54, 0x3B, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x51, 0x3D, 0x46, 0x5A, 0x4F, ++ 0x52, 0x21, 0x21, 0x21, 0x21, 0x22, 0x27, 0x31, ++ 0x33, 0x23, 0x42, 0x4C, 0x21, 0x21, 0x21, 0x5B, ++ 0x54, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x3B, 0x44, 0x45, 0x48, 0x4B, ++ 0x38, 0x22, 0x21, 0x21, 0x21, 0x21, 0x53, 0x48, ++ 0x33, 0x23, 0x34, 0x3C, 0x22, 0x57, 0x4D, 0x33, ++ 0x21, 0x21, 0x21, 0x21, 0x22, 0x23, 0x34, 0x40, ++ 0x44, 0x31, 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, ++ 0x3B, 0x42, 0x2A, 0x57, 0x31, 0x38, 0x5C, 0xD8, ++ 0xDC, 0xE4, 0xE2, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE2, 0xE0, ++ 0xDE, 0xCA, 0x9F, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x80, 0x76, 0x57, 0x5C, ++ 0x31, 0x6E, 0x34, 0x3C, 0xC9, 0x3B, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x33, 0x5B, 0x30, 0x25, ++ 0x5D, 0x5A, 0x53, 0x27, 0x26, 0x59, 0x36, 0x56, ++ 0x55, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x3B, 0x3D, 0x2A, 0x55, ++ 0x42, 0x51, 0x56, 0x24, 0x2A, 0x35, 0x21, 0x21, ++ 0x21, 0x21, 0x45, 0x53, 0x3B, 0x22, 0x22, 0x35, ++ 0x46, 0x59, 0x54, 0x55, 0x21, 0x21, 0x21, 0x31, ++ 0x2D, 0x47, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x33, 0x50, 0x5D, 0x45, 0x55, ++ 0x21, 0x21, 0x34, 0x4F, 0x29, 0x58, 0x33, 0x4C, ++ 0x39, 0x3C, 0x2A, 0x40, 0x48, 0x54, 0x3D, 0x3D, ++ 0x37, 0x4A, 0x59, 0x29, 0x5B, 0x36, 0x52, 0x2A, ++ 0x42, 0x3C, 0x32, 0x30, 0x41, 0x35, 0x21, 0x21, ++ 0x21, 0x33, 0x41, 0x26, 0x4B, 0x5A, 0x35, 0x21, ++ 0x21, 0x21, 0x21, 0x3C, 0x3E, 0x41, 0x23, 0x21, ++ 0x21, 0x21, 0x21, 0x23, 0x41, 0x49, 0x30, 0x32, ++ 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x52, 0x4F, ++ 0x5D, 0x41, 0x27, 0x58, 0x21, 0x21, 0x21, 0x5B, ++ 0x41, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x47, 0x49, 0x4F, 0x5C, ++ 0x23, 0x21, 0x21, 0x21, 0x21, 0x21, 0x39, 0x4A, ++ 0x28, 0x43, 0x27, 0x5C, 0x35, 0x25, 0x49, 0x5B, ++ 0x58, 0x52, 0x5C, 0x38, 0x48, 0x41, 0x4A, 0x4B, ++ 0x50, 0x33, 0x21, 0x21, 0x21, 0x21, 0x21, 0x35, ++ 0x3B, 0x3C, 0x2A, 0x32, 0x5C, 0x40, 0x32, 0xDD, ++ 0xDC, 0xE3, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE1, 0xE0, 0xBB, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x80, 0xE7, 0x58, 0x52, ++ 0x5C, 0x67, 0xE7, 0x6C, 0xE8, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x23, 0x39, 0x5C, ++ 0x29, 0x5A, 0x4D, 0x5C, 0x2A, 0x2B, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x3B, 0x44, 0x42, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x33, 0x27, 0x30, 0x5A, 0x2C, 0x25, ++ 0x4B, 0x50, 0x3B, 0x21, 0x21, 0x21, 0x21, 0x33, ++ 0x2C, 0x3B, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x4C, 0x22, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x55, 0x2B, ++ 0x39, 0x32, 0x4D, 0x29, 0x26, 0x3D, 0x2D, 0x2D, ++ 0x4B, 0x4B, 0x2F, 0x30, 0x42, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x3B, 0x29, 0x2C, 0x55, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x23, 0x50, 0x4D, 0x22, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x2B, 0x32, 0x3B, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x51, ++ 0x48, 0x45, 0x34, 0x21, 0x21, 0x21, 0x21, 0x38, ++ 0x44, 0x35, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x33, 0x32, 0x34, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x3C, ++ 0x5B, 0x4D, 0x42, 0x21, 0x21, 0x34, 0x4D, 0x47, ++ 0x5A, 0x5D, 0x27, 0x4E, 0x25, 0x3A, 0x5C, 0x42, ++ 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, ++ 0x2B, 0x34, 0x51, 0x56, 0x46, 0x40, 0xE9, 0xDA, ++ 0xDD, 0xE1, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE2, 0xEA, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x76, 0xD3, 0x67, 0x39, ++ 0x3C, 0xCF, 0xD5, 0x9F, 0x3B, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x3B, 0x28, 0x2B, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x3C, 0x53, 0x44, 0x3D, 0x43, ++ 0x57, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x35, 0x55, 0x3F, 0x2A, ++ 0x57, 0x24, 0x3F, 0x3B, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x22, 0x22, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x55, ++ 0x3C, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x22, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x55, 0x4C, 0x3F, 0x58, 0x46, 0x31, 0x8B, 0xAD, ++ 0xCC, 0xE2, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xEA, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x97, 0x35, 0x6E, 0xC6, 0x82, ++ 0xA6, 0x73, 0x75, 0xBA, 0x2B, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x34, 0x44, 0x3B, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, 0x23, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x33, 0x34, 0xEB, 0x34, 0x57, 0x5C, 0x4C, 0xA4, ++ 0xEC, 0x64, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0x83, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x80, 0xED, 0xCF, 0xCE, 0x78, 0x78, ++ 0x78, 0x7A, 0x6D, 0x34, 0x22, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x55, 0x25, 0x55, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x23, 0x34, 0xEE, 0xEF, 0x5F, 0x96, 0x23, 0xB3, ++ 0xBF, 0xB5, 0xF0, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xBB, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x9F, 0x78, 0x78, 0x78, 0x78, ++ 0x80, 0x9F, 0x65, 0x33, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x3B, 0x28, 0x55, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x22, 0x4C, 0xF1, 0xA5, 0xAC, 0xAB, 0x60, 0xB3, ++ 0xB3, 0xF2, 0xEA, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xF3, 0x6D, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x80, 0x61, 0x34, 0x23, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x3B, 0x25, 0x22, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x2B, 0x5E, ++ 0x4C, 0x3B, 0xF4, 0xA5, 0x9B, 0x9B, 0xB4, 0xEF, ++ 0xEF, 0xBF, 0xAD, 0xF5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0x67, 0x75, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x80, ++ 0xA7, 0x3F, 0x55, 0x22, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x4C, 0x4E, 0x23, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x2B, 0x5F, ++ 0x9B, 0xD2, 0x3C, 0xB7, 0xEF, 0xEF, 0xEF, 0xEF, ++ 0xEF, 0xB3, 0xB8, 0xEA, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0xF0, 0xBB, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x80, 0x82, ++ 0x34, 0x3B, 0x33, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x4C, 0x5D, 0x23, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x60, ++ 0xAB, 0xA5, 0xB8, 0xB1, 0xEF, 0xEF, 0xEF, 0xEF, ++ 0xEF, 0xF6, 0xB7, 0xB5, 0xF0, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, ++ 0x67, 0x80, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x78, 0x80, 0x9F, 0xBA, 0x42, ++ 0x4C, 0x2B, 0x22, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x4C, 0x3A, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x55, ++ 0xF2, 0x9B, 0x9B, 0xB4, 0xEF, 0xEF, 0xEF, 0xEF, ++ 0xEF, 0xEF, 0xB4, 0x5F, 0x63, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xF5, ++ 0x76, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x78, 0x78, 0x80, 0x6D, 0x65, 0x3C, 0x34, ++ 0x3B, 0x35, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x2A, 0x29, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, ++ 0xF7, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, ++ 0xEF, 0xEF, 0xB3, 0xB8, 0xEA, 0xE5, 0xE5, 0xE5, ++ 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xF8, 0x68, ++ 0x7A, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x78, 0x80, 0x97, 0x82, 0x39, 0x39, 0x34, 0x3B, ++ 0x33, 0x23, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x2A, 0x29, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x3B, 0xB8, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, ++ 0xEF, 0xEF, 0xF6, 0xB2, 0xF9, 0xFA, 0xFA, 0xFA, ++ 0xFA, 0xF8, 0xF8, 0xF8, 0xF0, 0xF0, 0x6F, 0x97, ++ 0x80, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, ++ 0x8C, 0x61, 0x39, 0x2A, 0x3F, 0x3C, 0x4C, 0x55, ++ 0x33, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x4C, 0x31, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x22, 0xFB, 0xF6, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, ++ 0xEF, 0xEF, 0xB3, 0xB2, 0xF9, 0xF3, 0xF3, 0xF3, ++ 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0x90, 0x6D, 0x97, ++ 0x8C, 0x6D, 0x76, 0xBB, 0xC6, 0xB9, 0xC0, 0xFC, ++ 0xAD, 0xFB, 0x2A, 0x58, 0x42, 0x4C, 0x55, 0x33, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x33, 0xD2, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, ++ 0xEF, 0xEF, 0xB4, 0xB8, 0xEB, 0xE8, 0x7F, 0xD6, ++ 0xD6, 0xD6, 0xD6, 0x9D, 0x9D, 0x52, 0xEB, 0x5F, ++ 0x5F, 0x5F, 0xF2, 0xA4, 0xBF, 0xBF, 0xB4, 0xBF, ++ 0xEE, 0x56, 0x2A, 0x42, 0x4C, 0x2B, 0x33, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x4C, 0xBE, 0xF6, 0xEF, 0xEF, 0xEF, ++ 0xF6, 0xB4, 0xB2, 0xF1, 0x3C, 0x56, 0x46, 0x5C, ++ 0x31, 0x38, 0x40, 0x40, 0x4D, 0x4D, 0xB8, 0xAC, ++ 0xBF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xB4, ++ 0xB1, 0x39, 0x39, 0x3B, 0x2B, 0x23, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x3B, 0xAD, 0xA4, 0xBF, 0xBF, ++ 0xB7, 0xB2, 0xEE, 0x5E, 0x39, 0x51, 0x2A, 0x32, ++ 0x52, 0x46, 0x5C, 0x31, 0x31, 0x2A, 0xA5, 0xEF, ++ 0xFD, 0xB4, 0xEF, 0xF6, 0xF6, 0xEF, 0xF6, 0xB2, ++ 0x3F, 0x34, 0x2B, 0x55, 0x23, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x33, 0xEB, 0xF7, 0xAA, ++ 0xAA, 0xF7, 0xEB, 0x55, 0x3B, 0x3C, 0x39, 0x51, ++ 0x2A, 0x56, 0x32, 0x57, 0x57, 0x2A, 0x96, 0x3C, ++ 0x2A, 0xEE, 0xEF, 0x5F, 0xD2, 0xEF, 0xB4, 0xAA, ++ 0x42, 0x55, 0x35, 0x22, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x23, 0x3B, ++ 0x3B, 0x35, 0x21, 0x23, 0x33, 0x3B, 0x4C, 0x3C, ++ 0x39, 0x3F, 0x51, 0x2A, 0x51, 0x51, 0x58, 0x32, ++ 0x56, 0xF4, 0xB1, 0x42, 0x3C, 0xF2, 0x9C, 0x3F, ++ 0x33, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x33, 0x55, ++ 0x3B, 0x4C, 0x4C, 0x3C, 0x42, 0x42, 0x3C, 0x34, ++ 0x34, 0x3F, 0x4C, 0x3B, 0x4C, 0x3B, 0x3C, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x35, 0x33, 0x55, 0x33, 0x33, 0x33, 0x55, 0x2B, ++ 0x2B, 0x35, 0x35, 0x35, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, ++ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, ++ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + + #endif /* !__HAVE_ARCH_LINUX_LOGO */ +@@ -994,7 +1760,7 @@ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + #endif /* !__HAVE_ARCH_LINUX_LOGOBW */ +@@ -1401,7 +2167,7 @@ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + #endif /* !__HAVE_ARCH_LINUX_LOGO16 */ diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/mkdep.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/mkdep.patch index e69de29bb2..4daeaa11be 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/mkdep.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/mkdep.patch @@ -0,0 +1,16 @@ + +# +# Made by http://www.mn-logistik.de/unsupported/pxa250/patcher +# + +--- linux/Makefile~mkdep 2003-12-19 09:36:51.000000000 -0800 ++++ linux/Makefile 2003-12-19 09:57:44.000000000 -0800 +@@ -458,7 +458,7 @@ + + dep-files: scripts/mkdep archdep include/linux/version.h + scripts/mkdep -- init/*.c > .depend +- scripts/mkdep -- `find $(FINDHPATH) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend ++ $(foreach, dir, $(FINDHPATH), scripts/mkdep -- `find $(dir) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print` >> .hdepend) + $(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS)) _FASTDEP_ALL_SUB_DIRS="$(SUBDIRS)" + ifdef CONFIG_MODVERSIONS + $(MAKE) update-modverfile diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/swap-performance.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/swap-performance.patch index e69de29bb2..1cde717bb4 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/swap-performance.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/swap-performance.patch @@ -0,0 +1,19 @@ +*** ../linux/mm/swap.c Tue Jan 14 14:54:41 2003 +--- linux/mm/swap.c Sun Sep 28 21:34:25 2003 +*************** +*** 32,38 **** + int page_cluster; + + pager_daemon_t pager_daemon = { +! 512, /* base number for calculating the number of tries */ + SWAP_CLUSTER_MAX, /* minimum number of tries */ + 8, /* do swap I/O in clusters of this size */ + }; +--- 32,38 ---- + int page_cluster; + + pager_daemon_t pager_daemon = { +! 128, /* base number for calculating the number of tries */ + SWAP_CLUSTER_MAX, /* minimum number of tries */ + 8, /* do swap I/O in clusters of this size */ + }; diff --git a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/usb-storage.patch b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/usb-storage.patch index e69de29bb2..c9296cf224 100644 --- a/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/usb-storage.patch +++ b/linux/openzaurus-pxa-2.4.18-rmk7-pxa3-embedix20031107/usb-storage.patch @@ -0,0 +1,3433 @@ +diff -Nur linux-2.4.18/drivers/usb/device/bi/sa1100.c linux-2.4.18-usb-storage/drivers/usb/device/bi/sa1100.c +--- linux-2.4.18/drivers/usb/device/bi/sa1100.c 2003-05-13 13:18:44.000000000 +0400 ++++ linux-2.4.18-usb-storage/drivers/usb/device/bi/sa1100.c 2004-03-01 07:20:38.000000000 +0300 +@@ -440,6 +440,7 @@ + udc_interrupts, *(UDCSR), *(UDCCS0), *(UDCAR)); + + usbd_device_event (udc_device, DEVICE_RESET, 0); ++ usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED,0); + } + + if (status & UDCSR_SUSIR) { +diff -Nur linux-2.4.18/drivers/usb/device/Config.in linux-2.4.18-usb-storage/drivers/usb/device/Config.in +--- linux-2.4.18/drivers/usb/device/Config.in 2003-05-13 13:18:45.000000000 +0400 ++++ linux-2.4.18-usb-storage/drivers/usb/device/Config.in 2003-11-07 05:35:14.000000000 +0300 +@@ -34,6 +34,7 @@ + comment 'USB Device functions' + source drivers/usb/device/net_fd/Config.in + source drivers/usb/device/serial_fd/Config.in ++ source drivers/usb/device/storage_fd/Config.in + + comment 'USB Device bus interfaces' + source drivers/usb/device/bi/Config.in +diff -Nur linux-2.4.18/drivers/usb/device/Makefile linux-2.4.18-usb-storage/drivers/usb/device/Makefile +--- linux-2.4.18/drivers/usb/device/Makefile 2003-05-13 13:18:45.000000000 +0400 ++++ linux-2.4.18-usb-storage/drivers/usb/device/Makefile 2003-11-07 05:35:01.000000000 +0300 +@@ -20,6 +20,7 @@ + + subdir-$(CONFIG_USBD_NET) += net_fd + subdir-$(CONFIG_USBD_SERIAL) += serial_fd ++subdir-$(CONFIG_USBD_STORAGE) += storage_fd + + #subdir-$(CONFIG_USBD_GENERIC_BUS) += gen_bi + #subdir-$(CONFIG_USBD_L7205_BUS) += l7205_bi +diff -Nur linux-2.4.18/drivers/usb/device/storage_fd/Config.help linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/Config.help +--- linux-2.4.18/drivers/usb/device/storage_fd/Config.help 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/Config.help 2003-11-07 05:34:43.000000000 +0300 +@@ -0,0 +1,55 @@ ++CONFIG_USBD_STORAGE ++ Enable the generic mass storage function driver. This function is ++ used emulating a Linux block driver. ++ ++CONFIG_USBD_STORAGE_VENDORID ++ Optionally specify the mass storage USB Device Vendor ID. The top ++ level Vendor ID will be used if this is not specified. ++ ++CONFIG_USBD_STORAGE_PRODUCTID ++ Optionally specify the mass storage USB Device Product ID. The top ++ level Vendor ID will be used if this is not specified. ++ ++CONFIG_USBD_STORAGE_OUT_ENDPOINT ++ Specify the preferred OUT (received data) endpoint number. This is a ++ number from 0-15 and must be allowed by the bus interface device. ++ ++ Some devices such as the SA-1110 or L7205/L7210 may override this ++ value with a fixed value. ++ ++CONFIG_USBD_STORAGE_IN_ENDPOINT ++ Specify the preferred IN (transmit data) endpoint number. This is a ++ number from 0-15 and must be allowed by the bus interface device. ++ ++ Some devices such as the SA-1110 or L7205/L7210 may override this ++ value with a fixed value. ++ ++CONFIG_USBD_STORAGE_INT_ENDPOINT ++ Specify the preferred INT (interrupt) endpoint number. This is a ++ number from 0-15 and must be allowed by the bus interface device. ++ ++ Some devices such as the L7205/L7210 may override this value with a ++ fixed value. Others such as the SA-1110 do not allow an interrupt ++ value. ++ ++CONFIG_USBD_STORAGE_OUT_PKTSIZE ++ Specify the maximum packet size for the OUT endpoint. This allowable ++ values are normally 16, 32 and 64. ++ ++ Some devices such as the Linkup L7205/L7210 may override this value ++ with a lower maximum value (such as 32). ++ ++CONFIG_USBD_STORAGE_IN_PKTSIZE ++ Specify the maximum packet size for the IN endpoint. This allowable ++ values are normally 16, 32 and 64. ++ ++ Some devices such as the Linkup L7205/L7210 may override this value ++ with a lower maximum value (such as 32). ++ ++CONFIG_USBD_STORAGE_INT_PKTSIZE ++ Specify the maximum packet size for the INT endpoint. This allowable ++ values are normally 8 and 16. Some bus interface devices may not ++ support all values. ++ ++CONFIG_USBD_STORAGE_DEF_DEVICE_NAME ++ Specify the default block device name to used on mass storage. +diff -Nur linux-2.4.18/drivers/usb/device/storage_fd/Config.in linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/Config.in +--- linux-2.4.18/drivers/usb/device/storage_fd/Config.in 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/Config.in 2003-11-07 05:34:43.000000000 +0300 +@@ -0,0 +1,29 @@ ++# ++# Generic Mass Storage Function Driver ++# ++# Copyright (c) 2003 Communication Technology Inc. ++# Copyright (C) 2001 Lineo, Inc. ++# Copyright (C) 2001 Hewlett-Packard Co. ++mainmenu_option next_comment ++comment "Mass Storage Function" ++ ++dep_tristate ' Mass Storage Function Driver' CONFIG_USBD_STORAGE $CONFIG_USBD ++if [ "$CONFIG_USBD_STORAGE" = "y" -o "$CONFIG_USBD_STORAGE" = "m" ]; then ++ hex ' Overide VendorID (hex value)' CONFIG_USBD_STORAGE_VENDORID "0000" ++ hex ' Overide ProductID (hex value)' CONFIG_USBD_STORAGE_PRODUCTID "0000" ++ ++ # allow setting of endpoint configurations for some architectures ++ int ' OUT Endpoint (0-15)' CONFIG_USBD_STORAGE_OUT_ENDPOINT "1" ++ int ' OUT PacketSize (16, 32, 64)' CONFIG_USBD_STORAGE_OUT_PKTSIZE "64" ++ int ' IN Endpoint (0-15)' CONFIG_USBD_STORAGE_IN_ENDPOINT "2" ++ int ' IN PacketSize (16, 32, 64)' CONFIG_USBD_STORAGE_IN_PKTSIZE "64" ++ ++ if [ ! "$CONFIG_ARCH_SA1100" = "y" -a ! "$CONFIG_ARCH_L7200" = "y" ]; then ++ int ' INT Endpoint (0-15)' CONFIG_USBD_STORAGE_INT_ENDPOINT "3" ++ int ' INT PacketSize (8, 16)' CONFIG_USBD_STORAGE_INT_PKTSIZE "16" ++ fi ++ ++ string ' Default Mass Storage device name' CONFIG_USBD_STORAGE_DEF_DEVICE_NAME "" ++fi ++ ++endmenu +diff -Nur linux-2.4.18/drivers/usb/device/storage_fd/Makefile linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/Makefile +--- linux-2.4.18/drivers/usb/device/storage_fd/Makefile 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/Makefile 2003-11-07 05:34:43.000000000 +0300 +@@ -0,0 +1,58 @@ ++# ++# SA1100 Function driver for a network USB Device ++# ++# Copyright (C) 2001 Lineo, Inc. ++# Copyright (C) 2001 Hewlett-Packard Co. ++ ++ ++O_TARGET := storage_fd_drv.o ++list-multi := storage_fd.o ++ ++storage_fd-objs := storage-fd.o storageproto.o schedule_task.o # netproto.o crc32.o ++ ++# Object file lists. ++ ++obj-y := ++obj-m := ++obj-n := ++obj- := ++ ++# Each configuration option enables a list of files. ++ ++obj-$(CONFIG_USBD_STORAGE) += storage_fd.o ++ ++# Extract lists of the multi-part drivers. ++# The 'int-*' lists are the intermediate files used to build the multi's. ++ ++multi-y := $(filter $(list-multi), $(obj-y)) ++multi-m := $(filter $(list-multi), $(obj-m)) ++int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) ++int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) ++ ++# Files that are both resident and modular: remove from modular. ++ ++obj-m := $(filter-out $(obj-y), $(obj-m)) ++int-m := $(filter-out $(int-y), $(int-m)) ++ ++# Translate to Rules.make lists. ++ ++O_OBJS := $(filter-out $(export-objs), $(obj-y)) ++OX_OBJS := $(filter $(export-objs), $(obj-y)) ++M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) ++MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) ++MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) ++MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) ++ ++# The global Rules.make. ++ ++include $(TOPDIR)/Rules.make ++ ++# Link rules for multi-part drivers. ++ ++storage_fd.o: $(storage_fd-objs) ++ $(LD) -r -o $@ $(storage_fd-objs) ++ ++# dependencies: ++ ++storage-fd.o: ../usbd.h ../usbd-bus.h ../usbd-func.h ++ +diff -Nur linux-2.4.18/drivers/usb/device/storage_fd/schedule_task.c linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/schedule_task.c +--- linux-2.4.18/drivers/usb/device/storage_fd/schedule_task.c 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/schedule_task.c 2003-11-07 05:34:43.000000000 +0300 +@@ -0,0 +1,230 @@ ++/* ++ * linux/drivers/usb/device/storage_fd/schedule_task.c - schedule task library ++ * ++ * Copyright (c) 2003 Lineo Solutions, Inc. ++ * ++ * Written by Shunnosuke kabata ++ * ++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++/****************************************************************************** ++** Include File ++******************************************************************************/ ++#include <linux/config.h> ++#include <linux/fs.h> ++#include <linux/string.h> ++#include <linux/mm.h> ++#include <linux/dcache.h> ++#include <linux/init.h> ++#include <linux/quotaops.h> ++#include <linux/slab.h> ++#include <linux/cache.h> ++#include <linux/swap.h> ++#include <linux/swapctl.h> ++#include <linux/prefetch.h> ++#include <linux/locks.h> ++#include <asm/uaccess.h> ++ ++#include "schedule_task.h" ++ ++/****************************************************************************** ++** Macro Define ++******************************************************************************/ ++#define TASK_DESC_NUM (512) ++ ++/****************************************************************************** ++** Structure Define ++******************************************************************************/ ++ ++/************************************** ++** TASK_DESC ++**************************************/ ++typedef struct _TASK_DESC{ ++ struct _TASK_DESC* next; ++ SCHEDULE_TASK_FUNC task_entry; ++ int task_param1; ++ int task_param2; ++ int task_param3; ++ int task_param4; ++ int task_param5; ++ char* file; ++ int line; ++} TASK_DESC; ++ ++/************************************** ++** OS_WRP_TASK_DATA ++**************************************/ ++typedef struct{ ++ volatile TASK_DESC* read_desc; ++ volatile TASK_DESC* write_desc; ++ TASK_DESC desc_pool[TASK_DESC_NUM]; ++ spinlock_t spin_lock; ++ struct tq_struct task_que; ++ unsigned long use_num; ++ unsigned long max_num; ++} TASK_DATA; ++ ++/****************************************************************************** ++** Variable Declaration ++******************************************************************************/ ++static TASK_DATA TaskData; ++ ++/****************************************************************************** ++** Local Function Prototype ++******************************************************************************/ ++static void task_entry(void*); ++ ++/****************************************************************************** ++** Global Function ++******************************************************************************/ ++void schedule_task_init(void) ++{ ++ int i; ++ ++ /* 0 clear */ ++ memset(&TaskData, 0x00, sizeof(TaskData)); ++ ++ /* Read/write pointer initialize */ ++ TaskData.read_desc = TaskData.write_desc = &TaskData.desc_pool[0]; ++ ++ /* Ling buffer initialize */ ++ for(i=0; i<(TASK_DESC_NUM-1); i++){ ++ TaskData.desc_pool[i].next = &TaskData.desc_pool[i+1]; ++ } ++ TaskData.desc_pool[i].next = &TaskData.desc_pool[0]; ++ ++ /* Spin lock initialize */ ++ spin_lock_init(&TaskData.spin_lock); ++ ++ /* Task queue initialize */ ++ PREPARE_TQUEUE(&TaskData.task_que, task_entry, &TaskData); ++ ++ return; ++} ++ ++int schedule_task_register(SCHEDULE_TASK_FUNC entry, int param1, int param2, ++ int param3, int param4, int param5) ++{ ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&TaskData.spin_lock, flags); ++ ++ /* Free descriptor check */ ++ if(TaskData.write_desc->next == TaskData.read_desc){ ++ printk(KERN_INFO "storage_fd: schedule task no descriptor.\n"); ++ spin_unlock_irqrestore(&TaskData.spin_lock, flags); ++ return -1; ++ } ++ ++ /* Descriptor set */ ++ TaskData.write_desc->task_entry = entry; ++ TaskData.write_desc->task_param1 = param1; ++ TaskData.write_desc->task_param2 = param2; ++ TaskData.write_desc->task_param3 = param3; ++ TaskData.write_desc->task_param4 = param4; ++ TaskData.write_desc->task_param5 = param5; ++ ++ /* Pointer update */ ++ TaskData.write_desc = TaskData.write_desc->next; ++ ++ /* Statistics set */ ++ TaskData.use_num++; ++ if(TaskData.use_num > TaskData.max_num){ ++ TaskData.max_num = TaskData.use_num; ++ } ++ ++ spin_unlock_irqrestore(&TaskData.spin_lock, flags); ++ ++ /* Task queue register */ ++ schedule_task(&TaskData.task_que); ++ ++ return 0; ++} ++ ++void schedule_task_all_unregister(void) ++{ ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&TaskData.spin_lock, flags); ++ TaskData.read_desc = TaskData.write_desc; ++ TaskData.use_num = 0; ++ spin_unlock_irqrestore(&TaskData.spin_lock, flags); ++} ++ ++ssize_t schedule_task_proc_read(struct file* file, char* buf, size_t count, ++ loff_t* pos) ++{ ++ char string[1024]; ++ int len = 0; ++ ++ len += sprintf(string + len, "Schedule task max num:0x%d\n", ++ TASK_DESC_NUM); ++ ++ len += sprintf(string + len, "Schedule task use num:0x%ld\n", ++ TaskData.use_num); ++ ++ len += sprintf(string + len, "Schedule task max use num:0x%ld\n", ++ TaskData.max_num); ++ ++ *pos += len; ++ if(len > count){ ++ len = -EINVAL; ++ } ++ else ++ if(len > 0 && copy_to_user(buf, string, len)) { ++ len = -EFAULT; ++ } ++ ++ return len; ++} ++ ++/****************************************************************************** ++** Local Function ++******************************************************************************/ ++static void task_entry(void* data) ++{ ++ int cond; ++ unsigned long flags = 0; ++ volatile TASK_DESC* desc; ++ ++ for(;;){ ++ ++ spin_lock_irqsave(&TaskData.spin_lock, flags); ++ desc = TaskData.read_desc; ++ cond = (TaskData.read_desc == TaskData.write_desc); ++ spin_unlock_irqrestore(&TaskData.spin_lock, flags); ++ ++ if(cond) break; ++ ++ /* Task call */ ++ desc->task_entry(desc->task_param1, desc->task_param2, ++ desc->task_param3, desc->task_param4, desc->task_param5); ++ ++ spin_lock_irqsave(&TaskData.spin_lock, flags); ++ ++ /* Pointer update */ ++ TaskData.read_desc = TaskData.read_desc->next; ++ ++ /* Statistics set */ ++ TaskData.use_num--; ++ ++ spin_unlock_irqrestore(&TaskData.spin_lock, flags); ++ } ++ ++ return; ++} ++ +diff -Nur linux-2.4.18/drivers/usb/device/storage_fd/schedule_task.h linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/schedule_task.h +--- linux-2.4.18/drivers/usb/device/storage_fd/schedule_task.h 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/schedule_task.h 2003-11-07 05:34:43.000000000 +0300 +@@ -0,0 +1,41 @@ ++/* ++ * linux/drivers/usb/device/storage_fd/schedule_task.h - schedule task library header ++ * ++ * Copyright (c) 2003 Lineo Solutions, Inc. ++ * ++ * Written by Shunnosuke kabata ++ * ++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef _SCHEDULE_TASK_H_ ++#define _SCHEDULE_TASK_H_ ++ ++/****************************************************************************** ++** Macro Define ++******************************************************************************/ ++typedef int (*SCHEDULE_TASK_FUNC)(int, int, int, int, int); ++ ++/****************************************************************************** ++** Global Function Prototype ++******************************************************************************/ ++void schedule_task_init(void); ++int schedule_task_register(SCHEDULE_TASK_FUNC, int, int, int, int, int); ++void schedule_task_all_unregister(void); ++ssize_t schedule_task_proc_read(struct file*, char*, size_t, loff_t*); ++ ++#endif /* _SCHEDULE_TASK_H_ */ ++ +diff -Nur linux-2.4.18/drivers/usb/device/storage_fd/storage-fd.c linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/storage-fd.c +--- linux-2.4.18/drivers/usb/device/storage_fd/storage-fd.c 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/storage-fd.c 2003-11-07 05:34:43.000000000 +0300 +@@ -0,0 +1,865 @@ ++/* ++ * linux/drivers/usb/device/storage_fd/storage-fd.c - mass storage function driver ++ * ++ * Copyright (c) 2003 Lineo Solutions, Inc. ++ * ++ * Written by Shunnosuke kabata ++ * ++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Based on ++ * ++ * linux/drivers/usbd/net_fd/net-fd.c - network function driver ++ * ++ * Copyright (c) 2000, 2001, 2002 Lineo ++ * Copyright (c) 2001 Hewlett Packard ++ * ++ * By: ++ * Stuart Lynne <sl@lineo.com>, ++ * Tom Rushworth <tbr@lineo.com>, ++ * Bruce Balden <balden@lineo.com> ++ * ++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++/****************************************************************************** ++** Include File ++******************************************************************************/ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include "../usbd-export.h" ++#include "../usbd-build.h" ++#include "../usbd-module.h" ++ ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <linux/netdevice.h> ++#include <linux/skbuff.h> ++#include <linux/etherdevice.h> ++#include <linux/rtnetlink.h> ++#include <linux/smp_lock.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/string.h> ++#include <linux/atmdev.h> ++#include <linux/pkt_sched.h> ++#include <linux/delay.h> ++#include <asm/uaccess.h> ++#include <asm/system.h> ++#include <net/arp.h> ++ ++#include <linux/autoconf.h> ++ ++#include "../usbd.h" ++#include "../usbd-func.h" ++#include "../usbd-bus.h" ++#include "../usbd-inline.h" ++#include "../usbd-arch.h" ++#include "../hotplug.h" ++ ++#include "schedule_task.h" ++#include "storageproto.h" ++ ++/****************************************************************************** ++** Macro Define ++******************************************************************************/ ++ ++/************************************** ++** Module Information ++**************************************/ ++ ++MODULE_AUTHOR("Shunnosuke kabata"); ++MODULE_DESCRIPTION("USB Device Mass Storage Function"); ++USBD_MODULE_INFO("storage_fd 0.1"); ++ ++/************************************** ++** Configration Check ++**************************************/ ++ ++#if !defined (CONFIG_USBD_VENDORID) && !defined(CONFIG_USBD_STORAGE_VENDORID) ++#error No Vendor ID ++#endif ++#if !defined (CONFIG_USBD_PRODUCTID) && !defined(CONFIG_USBD_STORAGE_PRODUCTID) ++#error No Product ID ++#endif ++ ++#if defined(CONFIG_USBD_STORAGE_VENDORID) && (CONFIG_USBD_STORAGE_VENDORID > 0) ++#undef CONFIG_USBD_VENDORID ++#define CONFIG_USBD_VENDORID CONFIG_USBD_STORAGE_VENDORID ++#endif ++ ++#if defined(CONFIG_USBD_STORAGE_PRODUCTID) && (CONFIG_USBD_STORAGE_PRODUCTID > 0) ++#undef CONFIG_USBD_PRODUCTID ++#define CONFIG_USBD_PRODUCTID CONFIG_USBD_STORAGE_PRODUCTID ++#endif ++ ++#ifndef CONFIG_USBD_MAXPOWER ++#define CONFIG_USBD_MAXPOWER 0 ++#endif ++ ++#ifndef CONFIG_USBD_MANUFACTURER ++#define CONFIG_USBD_MANUFACTURER "Sharp" ++#endif ++ ++#define MAXTRANSFER (512) ++ ++#ifndef CONFIG_USBD_VENDORID ++#error "CONFIG_USBD_VENDORID not defined" ++#endif ++ ++#ifndef CONFIG_USBD_PRODUCTID ++#error "CONFIG_USBD_PRODUCTID not defined" ++#endif ++ ++#ifndef CONFIG_USBD_PRODUCT_NAME ++#define CONFIG_USBD_PRODUCT_NAME "Linux Mass Storage Driver" ++#endif ++ ++#ifndef CONFIG_USBD_SERIAL_NUMBER_STR ++#define CONFIG_USBD_SERIAL_NUMBER_STR "" ++#endif ++ ++/* ++ * USB 2.0 spec does not mention it, but MaxPower is expected to be at least one ++ * and is tested for in USB configuration tests. ++ */ ++ ++#ifdef CONFIG_USBD_SELFPOWERED ++#define BMATTRIBUTE BMATTRIBUTE_RESERVED | BMATTRIBUTE_SELF_POWERED ++#define BMAXPOWER 1 ++#else ++#define BMATTRIBUTE BMATTRIBUTE_RESERVED ++#define BMAXPOWER CONFIG_USBD_MAXPOWER ++#endif ++ ++/* ++ * setup some default values for pktsizes and endpoint addresses. ++ */ ++ ++#ifndef CONFIG_USBD_STORAGE_OUT_PKTSIZE ++#define CONFIG_USBD_STORAGE_OUT_PKTSIZE 64 ++#endif ++ ++#ifndef CONFIG_USBD_STORAGE_IN_PKTSIZE ++#define CONFIG_USBD_STORAGE_IN_PKTSIZE 64 ++#endif ++ ++#ifndef CONFIG_USBD_STORAGE_INT_PKTSIZE ++#define CONFIG_USBD_STORAGE_INT_PKTSIZE 16 ++#endif ++ ++#ifndef CONFIG_USBD_STORAGE_OUT_ENDPOINT ++#define CONFIG_USBD_STORAGE_OUT_ENDPOINT 1 ++#endif ++ ++#ifndef CONFIG_USBD_STORAGE_IN_ENDPOINT ++#define CONFIG_USBD_STORAGE_IN_ENDPOINT 2 ++#endif ++ ++#ifndef CONFIG_USBD_STORAGE_INT_ENDPOINT ++#define CONFIG_USBD_STORAGE_INT_ENDPOINT 3 ++#endif ++ ++/* ++ * check for architecture specific endpoint configurations ++ */ ++ ++#if defined(ABS_OUT_ADDR) ++ //#warning ++ //#warning USING ABS ENDPOINT OUT ADDRESS ++ #undef CONFIG_USBD_STORAGE_OUT_ENDPOINT ++ ++ #if ABS_OUT_ADDR > 0 ++ #define CONFIG_USBD_STORAGE_OUT_ENDPOINT ABS_OUT_ADDR ++ #endif ++ ++#elif defined(MAX_OUT_ADDR) && defined(CONFIG_USBD_STORAGE_OUT_ENDPOINT) && (CONFIG_USBD_STORAGE_OUT_ENDPOINT > MAX_OUT_ADDR) ++ //#warning ++ //#warning USING DEFAULT ENDPOINT OUT ADDRESS ++ #undef CONFIG_USBD_STORAGE_OUT_ENDPOINT ++ #define CONFIG_USBD_STORAGE_OUT_ENDPOINT DFL_OUT_ADDR ++ ++#endif /* elif */ ++ ++#if defined(ABS_IN_ADDR) ++ //#warning ++ //#warning USING ABS ENDPOINT IN ADDRESS ++ #undef CONFIG_USBD_STORAGE_IN_ENDPOINT ++ ++ #if ABS_IN_ADDR > 0 ++ #define CONFIG_USBD_STORAGE_IN_ENDPOINT ABS_IN_ADDR ++ #endif ++ ++#elif defined(MAX_IN_ADDR) && defined(CONFIG_USBD_STORAGE_IN_ENDPOINT) && (CONFIG_USBD_STORAGE_IN_ENDPOINT > MAX_IN_ADDR) ++ //#warning ++ //#warning USING DEFAULT ENDPOINT IN ADDRESS ++ #undef CONFIG_USBD_STORAGE_IN_ENDPOINT ++ #define CONFIG_USBD_STORAGE_IN_ENDPOINT DFL_IN_ADDR ++ ++#endif /* elif */ ++ ++#if defined(ABS_INT_ADDR) ++ //#warning ++ //#warning USING ABS ENDPOINT INT ADDRESS ++ #undef CONFIG_USBD_STORAGE_INT_ENDPOINT ++ ++ #if ABS_INT_ADDR ++ #define CONFIG_USBD_STORAGE_INT_ENDPOINT ABS_INT_ADDR ++ #endif ++ ++#elif defined(MAX_INT_ADDR) && defined(CONFIG_USBD_STORAGE_INT_ENDPOINT) && (CONFIG_USBD_STORAGE_INT_ENDPOINT > MAX_INT_ADDR) ++ //#warning ++ //#warning USING DEFAULT ENDPOINT INT ADDRESS ++ #undef CONFIG_USBD_STORAGE_INT_ENDPOINT ++ #define CONFIG_USBD_STORAGE_INT_ENDPOINT DFL_INT_ADDR ++ ++#endif /* elif */ ++ ++#if defined(MAX_OUT_PKTSIZE) && defined(CONFIG_USBD_STORAGE_OUT_PKTSIZE) && CONFIG_USBD_STORAGE_OUT_PKTSIZE > MAX_OUT_PKTSIZE ++ //#warning ++ //#warning OVERIDING ENDPOINT OUT PKTSIZE ++ #undef CONFIG_USBD_STORAGE_OUT_PKTSIZE ++ #define CONFIG_USBD_STORAGE_OUT_PKTSIZE MAX_OUT_PKTSIZE ++#endif ++ ++#if defined(MAX_IN_PKTSIZE) && defined(CONFIG_USBD_STORAGE_IN_PKTSIZE) && CONFIG_USBD_STORAGE_IN_PKTSIZE > MAX_IN_PKTSIZE ++ //#warning ++ //#warning OVERIDING ENDPOINT IN PKTSIZE ++ #undef CONFIG_USBD_STORAGE_IN_PKTSIZE ++ #define CONFIG_USBD_STORAGE_IN_PKTSIZE MAX_IN_PKTSIZE ++#endif ++ ++#if defined(MAX_INT_PKTSIZE) && defined(CONFIG_USBD_STORAGE_INT_PKTSIZE) && CONFIG_USBD_STORAGE_INT_PKTSIZE > MAX_INT_PKTSIZE ++ //#warning ++ //#warning OVERIDING ENDPOINT INT PKTSIZE ++ #undef CONFIG_USBD_STORAGE_INT_PKTSIZE ++ #define CONFIG_USBD_STORAGE_INT_PKTSIZE MAX_INT_PKTSIZE ++#endif ++ ++/****************************************************************************** ++** Variable Declaration ++******************************************************************************/ ++ ++/************************************** ++** Module Parameters ++**************************************/ ++ ++static u32 vendor_id; ++static u32 product_id; ++static int out_pkt_sz = CONFIG_USBD_STORAGE_OUT_PKTSIZE; ++static int in_pkt_sz = CONFIG_USBD_STORAGE_IN_PKTSIZE; ++ ++MODULE_PARM(vendor_id, "i"); ++MODULE_PARM(product_id, "i"); ++MODULE_PARM(out_pkt_sz, "i"); ++MODULE_PARM(in_pkt_sz, "i"); ++ ++MODULE_PARM_DESC(vendor_id, "vendor id"); ++MODULE_PARM_DESC(product_id, "product id"); ++ ++/************************************** ++** Mass Storage Configuration ++**************************************/ ++ ++/* ++ * Data Interface Alternate 1 endpoints ++ */ ++static __initdata struct usb_endpoint_description StorageAlt1Endpoints[] = { ++ { ++ bEndpointAddress:CONFIG_USBD_STORAGE_OUT_ENDPOINT, ++ bmAttributes:BULK, ++ wMaxPacketSize:CONFIG_USBD_STORAGE_OUT_PKTSIZE, ++ bInterval:0, ++ direction:OUT, ++ transferSize:MAXTRANSFER, ++ }, ++ ++ { ++ bEndpointAddress:CONFIG_USBD_STORAGE_IN_ENDPOINT, ++ bmAttributes:BULK, ++ wMaxPacketSize:CONFIG_USBD_STORAGE_IN_PKTSIZE, ++ bInterval:0, ++ direction:IN, ++ transferSize:MAXTRANSFER, ++ }, ++ ++#if defined(CONFIG_USBD_STORAGE_INT_ENDPOINT) && (CONFIG_USBD_STORAGE_INT_ENDPOINT > 0) ++ { ++ bEndpointAddress:CONFIG_USBD_STORAGE_INT_ENDPOINT, ++ bmAttributes:INTERRUPT, ++ wMaxPacketSize:CONFIG_USBD_STORAGE_INT_PKTSIZE, ++ bInterval:10, ++ direction:IN, ++ transferSize:CONFIG_USBD_STORAGE_INT_PKTSIZE, ++ }, ++#endif ++}; ++ ++ ++/* ++ * Data Interface Alternate description(s) ++ */ ++static __initdata struct usb_alternate_description StorageAlternateDescriptions[] = { ++ { ++ #if defined(CONFIG_USBD_STORAGE_NO_STRINGS) ++ iInterface:"", ++ #else ++ iInterface:"Mass Storage Interface", ++ #endif ++ bAlternateSetting:0, ++ classes:0, ++ class_list:NULL, ++ endpoints:sizeof (StorageAlt1Endpoints) / sizeof (struct usb_endpoint_description), ++ endpoint_list:StorageAlt1Endpoints, ++ }, ++}; ++ ++/* ++ * Interface description(s) ++ */ ++static __initdata struct usb_interface_description StorageInterfaces[] = { ++ { ++ #if defined(CONFIG_USBD_STORAGE_NO_STRINGS) ++ iInterface:"", ++ #else ++ iInterface:"Mass Storage Interface", ++ #endif ++ bInterfaceClass:MASS_STORAGE_CLASS, ++ bInterfaceSubClass:MASS_STORAGE_SUBCLASS_SCSI, ++ bInterfaceProtocol:MASS_STORAGE_PROTO_BULK_ONLY, ++ alternates:sizeof (StorageAlternateDescriptions) / sizeof (struct usb_alternate_description), ++ alternate_list:StorageAlternateDescriptions, ++ }, ++}; ++ ++/****************************************************************************** ++** USB Configuration ++******************************************************************************/ ++ ++/* ++ * Configuration description(s) ++ */ ++struct __initdata usb_configuration_description StorageDescription[] = { ++ { ++ #if defined(CONFIG_USBD_STORAGE_NO_STRINGS) ++ iConfiguration:"", ++ #else ++ iConfiguration:"Mass Storage Configuration", ++ #endif ++ bmAttributes:BMATTRIBUTE, ++ bMaxPower:BMAXPOWER, ++ interfaces:sizeof (StorageInterfaces) / sizeof (struct usb_interface_description), ++ interface_list:StorageInterfaces, ++ }, ++}; ++ ++/* ++ * Device Description ++ */ ++struct __initdata usb_device_description StorageDeviceDescription = { ++ bDeviceClass:0, ++ bDeviceSubClass:0, // XXX ++ bDeviceProtocol:0, // XXX ++ idVendor:CONFIG_USBD_VENDORID, ++ idProduct:CONFIG_USBD_PRODUCTID, ++ iManufacturer:CONFIG_USBD_MANUFACTURER, ++ iProduct:CONFIG_USBD_PRODUCT_NAME, ++ iSerialNumber:CONFIG_USBD_SERIAL_NUMBER_STR, ++}; ++ ++/************************************** ++** Other Variable ++**************************************/ ++static int storage_exit_flag = 0; ++static pid_t storage_pid = 0; ++static struct semaphore storage_sem; ++struct timer_list storage_usb_event_tim; ++ ++ ++/****************************************************************************** ++** Global Function ++******************************************************************************/ ++ ++void storage_urb_send(struct usb_device_instance* device, void* buffer, ++ int length) ++{ ++ int port = 0; ++ struct urb* urb; ++ ++ if(!(urb = usbd_alloc_urb(device, ++ device->function_instance_array + port, ++ CONFIG_USBD_STORAGE_IN_ENDPOINT, ++ length + 5 + in_pkt_sz))){ ++ printk(KERN_INFO "storage_fd: usbd_alloc_urb failed. length '%d'.\n", length); ++ return; ++ } ++ ++ if(buffer){ ++ memcpy(urb->buffer, buffer, length); ++ } ++ else{ ++ memset(urb->buffer, 0x00, length); ++ } ++ urb->actual_length = length; ++ ++ if(usbd_send_urb(urb)){ ++ printk(KERN_INFO "storage_fd: usbd_send_urb failed.\n"); ++ usbd_dealloc_urb(urb); ++ return; ++ } ++ ++ return; ++} ++ ++/****************************************************************************** ++** Local Function ++******************************************************************************/ ++ ++/************************************** ++** Called when a USB Device is created or destroyed ++**************************************/ ++ ++static int storage_thread(void *_c) ++{ ++ siginfo_t info; ++ unsigned long signr; ++ char buff[32]; ++ ++ /* current status set */ ++ daemonize(); ++ ++ /* PID set */ ++ storage_pid = current->pid; ++ ++ /* thread name set */ ++ sprintf(current->comm, STORAGE_THREAD_NAME); ++ (current)->nice = 10; ++ ++ /* signal register */ ++ spin_lock_irq(¤t->sigmask_lock); ++ siginitsetinv(¤t->blocked, ++ sigmask(SIGUSR1) | sigmask(SIGHUP) | sigmask(SIGKILL) | ++ sigmask(SIGSTOP) | sigmask(SIGCONT) | sigmask(SIGTERM) | ++ sigmask(SIGALRM)); ++ recalc_sigpending(current); ++ spin_unlock_irq(¤t->sigmask_lock); ++ ++ /* media open */ ++ schedule_task_register( ++ (SCHEDULE_TASK_FUNC)storageproto_media_status_check, CONTEXT_STORAGE, ++ 0, 0, 0, 0); ++ ++ /* thread active indicate */ ++ sprintf(buff, "%d\n", storage_pid); ++ hotplug("usbdstorage", buff, "active"); ++ ++ for(;;){ ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ if (!signal_pending(current)) { ++ schedule(); ++ continue; ++ } ++ ++ spin_lock_irq(¤t->sigmask_lock); ++ signr = dequeue_signal(¤t->blocked, &info); ++ spin_unlock_irq(¤t->sigmask_lock); ++ ++ switch(signr) { ++ case SIGHUP: ++ /* media signal indicate */ ++ schedule_task_register( ++ (SCHEDULE_TASK_FUNC)storageproto_media_status_check, CONTEXT_STORAGE, ++ 0, 0, 0, 0); ++ ++ DBG_STORAGE_FD(KERN_INFO "storage_fd: signal receive 'SIGHUP'.\n"); ++ break; ++ ++ default: ++ DBG_STORAGE_FD(KERN_INFO "storage_fd: signal receive '%ld'\n", signr); ++ break; ++ } ++ ++ if(storage_exit_flag) break; ++ } ++ /* current status set */ ++ current->state = TASK_RUNNING; ++ ++ /* PID clear */ ++ storage_pid = 0; ++ ++ /* thread inactive indicate */ ++ hotplug("usbdstorage", buff, "inactive"); ++ ++ up(&storage_sem); ++ ++ return 0; ++} ++ ++static void storage_function_init(struct usb_bus_instance* bus, ++ struct usb_device_instance* device, ++ struct usb_function_driver* function_driver) ++{ ++ /* schedule task init */ ++ schedule_task_init(); ++ ++ /* storage protocol initialize */ ++ storageproto_init(); ++ ++ /* semaphore init */ ++ sema_init(&storage_sem, 0); ++ ++ /* timer initialize */ ++ init_timer(&storage_usb_event_tim); ++ ++ /* thread create */ ++ storage_pid = kernel_thread(storage_thread, NULL, 0); ++ ++ return; ++} ++ ++static void storage_function_exit(struct usb_device_instance* device) ++{ ++ /* thread kill */ ++ storage_exit_flag = 1; ++ kill_proc(storage_pid, SIGKILL, 1); ++ down(&storage_sem); ++ ++ /* delete timer */ ++ del_timer(&storage_usb_event_tim); ++ ++ /* storage protocol exit */ ++ storageproto_exit(); ++ ++ /* schedule task delete */ ++ schedule_task_all_unregister(); ++ ++ return; ++} ++ ++ ++/************************************** ++** Called to handle USB Events ++**************************************/ ++ ++static void storage_usb_event_delay_timeout(unsigned long param) ++{ ++ /* media signal indicate */ ++ schedule_task_register( ++ (SCHEDULE_TASK_FUNC)storageproto_usb_status_check, (int)param, ++ 0, 0, 0, 0); ++ ++ return; ++} ++ ++/* ++ * storage_event - process a device event ++ * @device: usb device ++ * @event: the event that happened ++ * ++ * Called by the usb device core layer to respond to various USB events. ++ * ++ * This routine IS called at interrupt time. Please use the usual precautions. ++ * ++ */ ++void storage_event(struct usb_device_instance* device, ++ usb_device_event_t event, int data) ++{ ++#if 0 ++ static struct { ++ usb_device_event_t event; ++ char* string; ++ } eventAnal[] = { ++ {DEVICE_UNKNOWN, "DEVICE_UNKNOWN"}, ++ {DEVICE_INIT, "DEVICE_INIT"}, ++ {DEVICE_CREATE, "DEVICE_CREATE"}, ++ {DEVICE_HUB_CONFIGURED, "DEVICE_HUB_CONFIGURED"}, ++ {DEVICE_RESET, "DEVICE_RESET"}, ++ {DEVICE_ADDRESS_ASSIGNED, "DEVICE_ADDRESS_ASSIGNED"}, ++ {DEVICE_CONFIGURED, "DEVICE_CONFIGURED"}, ++ {DEVICE_SET_INTERFACE, "DEVICE_SET_INTERFACE"}, ++ {DEVICE_SET_FEATURE, "DEVICE_SET_FEATURE"}, ++ {DEVICE_CLEAR_FEATURE, "DEVICE_CLEAR_FEATURE"}, ++ {DEVICE_DE_CONFIGURED, "DEVICE_DE_CONFIGURED"}, ++ {DEVICE_BUS_INACTIVE, "DEVICE_BUS_INACTIVE"}, ++ {DEVICE_BUS_ACTIVITY, "DEVICE_BUS_ACTIVITY"}, ++ {DEVICE_POWER_INTERRUPTION, "DEVICE_POWER_INTERRUPTION"}, ++ {DEVICE_HUB_RESET, "DEVICE_HUB_RESET"}, ++ {DEVICE_DESTROY, "DEVICE_DESTROY"}, ++ {DEVICE_FUNCTION_PRIVATE, "DEVICE_FUNCTION_PRIVATE"} ++ }; ++ int i; ++ ++ for(i=0; i<(sizeof(eventAnal)/sizeof(eventAnal[0])); i++){ ++ if(event == eventAnal[i].event){ ++ DBG_STORAGE_FD(KERN_INFO "storage_fd: event receive '%s'.\n", ++ eventAnal[i].string); ++ break; ++ } ++ } ++ if(i == (sizeof(eventAnal)/sizeof(eventAnal[0]))){ ++ DBG_STORAGE_FD(KERN_INFO "storage_fd: unknown event receive.\n"); ++ } ++#endif ++ ++ switch(event){ ++ case DEVICE_ADDRESS_ASSIGNED: ++ { ++ static int Is1stCheck = 1; ++ if(Is1stCheck){ ++ Is1stCheck = 0; ++ ++ /* delay timer set */ ++ del_timer(&storage_usb_event_tim); ++ storage_usb_event_tim.expires = jiffies + ((USB_EVENT_DELAY_TIM * HZ) / 1000); ++ storage_usb_event_tim.data = USB_DISCONNECT; ++ storage_usb_event_tim.function = storage_usb_event_delay_timeout; ++ add_timer(&storage_usb_event_tim); ++ break; ++ } ++ } ++ case DEVICE_BUS_ACTIVITY: ++ /* delay timer set */ ++ del_timer(&storage_usb_event_tim); ++ storage_usb_event_tim.expires = jiffies + ((USB_EVENT_DELAY_TIM * HZ) / 1000); ++ storage_usb_event_tim.data = USB_CONNECT; ++ storage_usb_event_tim.function = storage_usb_event_delay_timeout; ++ add_timer(&storage_usb_event_tim); ++ break; ++ ++ case DEVICE_BUS_INACTIVE: ++ /* delay timer set */ ++ del_timer(&storage_usb_event_tim); ++ storage_usb_event_tim.expires = jiffies + ((USB_EVENT_DELAY_TIM * HZ) / 1000); ++ storage_usb_event_tim.data = USB_DISCONNECT; ++ storage_usb_event_tim.function = storage_usb_event_delay_timeout; ++ add_timer(&storage_usb_event_tim); ++ break; ++ ++ case DEVICE_RESET: ++ schedule_task_register( ++ (SCHEDULE_TASK_FUNC)storageproto_usb_reset_ind, ++ 0, 0, 0, 0, 0); ++ break; ++ ++ default: ++ break; ++ } ++ ++ return; ++} ++ ++/* ++ * storage_recv_setup - called with a control URB ++ * @urb - pointer to struct urb ++ * ++ * Check if this is a setup packet, process the device request, put results ++ * back into the urb and return zero or non-zero to indicate success (DATA) ++ * or failure (STALL). ++ * ++ * This routine IS called at interrupt time. Please use the usual precautions. ++ * ++ */ ++int storage_recv_setup(struct urb* urb) ++{ ++ return 0; ++} ++ ++/* ++ * storage_recv_urb - called with a received URB ++ * @urb - pointer to struct urb ++ * ++ * Return non-zero if we failed and urb is still valid (not disposed) ++ * ++ * This routine IS called at interrupt time. Please use the usual precautions. ++ * ++ */ ++int storage_recv_urb(struct urb* urb) ++{ ++ int port = 0; // XXX compound device ++ struct usb_device_instance* device; ++ struct usb_function_instance* function; ++ ++ if(!urb || !(device = urb->device) || ++ !(function = device->function_instance_array + port)){ ++ return -EINVAL; ++ } ++ ++ if(urb->status != RECV_OK){ ++ return -EINVAL; ++ } ++ ++ /* URB urb_analysis */ ++ schedule_task_register( ++ (SCHEDULE_TASK_FUNC)storageproto_urb_analysis, (int)urb, ++ 0, 0, 0, 0); ++ ++ return 0; ++} ++ ++/* ++ * storage_urb_sent - called to indicate URB transmit finished ++ * @urb: pointer to struct urb ++ * @rc: result ++ * ++ * The usb device core layer will use this to let us know when an URB has ++ * been finished with. ++ * ++ * This routine IS called at interrupt time. Please use the usual precautions. ++ * ++ */ ++int storage_urb_sent(struct urb* urb, int rc) ++{ ++ int port = 0; // XXX compound device ++ struct usb_device_instance* device; ++ struct usb_function_instance* function; ++ ++ if(!urb || !(device = urb->device) || ++ !(function = device->function_instance_array + port)){ ++ return -EINVAL; ++ } ++ ++ usbd_dealloc_urb (urb); ++ ++ return 0; ++} ++ ++/************************************** ++** Proc file system ++**************************************/ ++ ++static ssize_t storage_proc_read(struct file* file, char* buf, size_t count, ++ loff_t* pos) ++{ ++ int len = 0, ret; ++ ++ if(*pos > 0) return 0; ++ ++ if((ret = storageproto_proc_read(file, buf + len, count - len, pos)) < 0){ ++ return ret; ++ } ++ len += ret; ++ ++ if((ret = schedule_task_proc_read(file, buf + len, count - len, pos)) < 0){ ++ return ret; ++ } ++ len += ret; ++ ++ return len; ++} ++ ++static struct file_operations StorageProcOps = { ++ read:storage_proc_read, ++}; ++ ++/************************************** ++** Module init and exit ++**************************************/ ++ ++struct usb_function_operations StorageFunctionOps = { ++ event:storage_event, ++ recv_urb:storage_recv_urb, ++ recv_setup:storage_recv_setup, ++ urb_sent:storage_urb_sent, ++ function_init:storage_function_init, ++ function_exit:storage_function_exit, ++}; ++ ++struct usb_function_driver StorageFunctionDriver = { ++ name:"Mass Storage", ++ ops:&StorageFunctionOps, ++ device_description:&StorageDeviceDescription, ++ configurations:sizeof (StorageDescription) / sizeof (struct usb_configuration_description), ++ configuration_description:StorageDescription, ++ this_module:THIS_MODULE, ++}; ++ ++/* ++ * net_modinit - module init ++ * ++ */ ++static int __init net_modinit(void) ++{ ++ struct proc_dir_entry* proc_entry; ++ ++ printk(KERN_INFO "storage_fd: %s (OUT=%d,IN=%d)\n", ++ __usbd_module_info, out_pkt_sz, in_pkt_sz); ++ ++ printk(KERN_INFO "storage_fd: vendorID: %x productID: %x\n", ++ CONFIG_USBD_VENDORID, CONFIG_USBD_PRODUCTID); ++ ++ // verify pkt sizes not too small ++ if (out_pkt_sz < 3 || in_pkt_sz < 3){ ++ printk(KERN_INFO "storage_fd: Rx pkt size %d or Tx pkt size %d too small\n", ++ out_pkt_sz, in_pkt_sz); ++ return (-EINVAL); ++ } ++ ++ if(vendor_id){ ++ StorageDeviceDescription.idVendor = vendor_id; ++ } ++ if(product_id){ ++ StorageDeviceDescription.idProduct = product_id; ++ } ++ ++ // register us with the usb device support layer ++ if(usbd_register_function(&StorageFunctionDriver)){ ++ printk(KERN_INFO "storage_fd: usbd_register_function failed.\n"); ++ return -EINVAL; ++ } ++ ++ // create proc entry ++ if ((proc_entry = create_proc_entry("usb-storage", 0, 0)) == NULL) { ++ usbd_deregister_function (&StorageFunctionDriver); ++ printk(KERN_INFO "storage_fd: create_proc_entry failed.\n"); ++ return -ENOMEM; ++ } ++ proc_entry->proc_fops = &StorageProcOps; ++ ++ return 0; ++} ++ ++/* ++ * function_exit - module cleanup ++ * ++ */ ++static void __exit net_modexit (void) ++{ ++ // de-register us with the usb device support layer ++ usbd_deregister_function (&StorageFunctionDriver); ++ ++ // remove proc entry ++ remove_proc_entry("usb-storage", NULL); ++ ++ return; ++} ++ ++module_init (net_modinit); ++module_exit (net_modexit); +diff -Nur linux-2.4.18/drivers/usb/device/storage_fd/storageproto.c linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/storageproto.c +--- linux-2.4.18/drivers/usb/device/storage_fd/storageproto.c 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/storageproto.c 2003-11-07 05:34:43.000000000 +0300 +@@ -0,0 +1,1505 @@ ++/* ++ * linux/drivers/usb/device/storage_fd/storageproto.c - mass storage protocol library ++ * ++ * Copyright (c) 2003 Lineo Solutions, Inc. ++ * ++ * Written by Shunnosuke kabata ++ * ++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++/****************************************************************************** ++** Include File ++******************************************************************************/ ++#include <linux/config.h> ++#include <linux/module.h> ++ ++#include "../usbd-export.h" ++#include "../usbd-build.h" ++#include "../usbd-module.h" ++ ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <linux/netdevice.h> ++#include <linux/skbuff.h> ++#include <linux/etherdevice.h> ++#include <linux/rtnetlink.h> ++#include <linux/smp_lock.h> ++#include <linux/ctype.h> ++#include <linux/timer.h> ++#include <linux/string.h> ++#include <linux/atmdev.h> ++#include <linux/pkt_sched.h> ++#include <linux/delay.h> ++#include <linux/blkdev.h> ++#include <linux/file.h> ++#include <asm/uaccess.h> ++#include <asm/system.h> ++#include <net/arp.h> ++ ++#include <linux/autoconf.h> ++ ++#include "../usbd.h" ++#include "../usbd-func.h" ++#include "../usbd-bus.h" ++#include "../usbd-inline.h" ++#include "../usbd-arch.h" ++#include "../hotplug.h" ++ ++#include "schedule_task.h" ++#include "storageproto.h" ++ ++/****************************************************************************** ++** macro define ++******************************************************************************/ ++ ++#define DEVICE_BLOCK_SIZE 512 ++ ++#define BLOCK_BUFFER_SIZE (1024 * 64) ++ ++#define DEF_NUMBER_OF_HEADS 0x10 ++#define DEF_SECTORS_PER_TRACK 0x20 ++ ++/****************************************************************************** ++** Structure Define ++******************************************************************************/ ++ ++typedef struct{ ++ unsigned char scsi_command; ++ unsigned char* command_name; ++ void (*scsi_func)(struct usb_device_instance*, ++ COMMAND_BLOCK_WRAPPER*); ++} SCSI_ANALYSIS_TBL; ++ ++typedef struct{ ++ unsigned char scsi_command; ++ unsigned char* command_name; ++ void (*bulkout_func)(struct usb_device_instance*, ++ void*, int); ++} SCSI_BULKOUT_ANALYSIS_TBL; ++ ++/****************************************************************************** ++** Variable Declaration ++******************************************************************************/ ++ ++/************************************** ++** Module Parameters ++**************************************/ ++ ++static char* storage_device = CONFIG_USBD_STORAGE_DEF_DEVICE_NAME; ++MODULE_PARM(storage_device, "s"); ++ ++/************************************** ++** Device Information ++**************************************/ ++ ++static struct file* DeviceFile = NULL; ++static int DeviceSize = 0; ++static int DeviceBlockSize = DEVICE_BLOCK_SIZE; ++static int DeviceWrProtect = WR_PROTECT_OFF; ++ ++/************************************** ++** Status ++**************************************/ ++ ++static int StorageStatus = STORAGE_IDLE; ++static int UsbStatus = USB_DISCONNECT; ++static int MediaStatus = MEDIA_EJECT; ++static int MediaChange = MEDIA_CHANGE_OFF; ++ ++/************************************** ++** Keep Information ++**************************************/ ++ ++static SCSI_REQUEST_SENSE_DATA RequestSenseData; ++static COMMAND_BLOCK_WRAPPER KeepCBW; ++static unsigned long BulkOutLength = 0; ++static unsigned char BlockBuffer[BLOCK_BUFFER_SIZE]; ++ ++/************************************** ++** Statistics ++**************************************/ ++static unsigned long StatMaxBulkInSize = 0; ++static unsigned long StatMaxBulkOutSize = 0; ++static unsigned long StatDevWriteError = 0; ++static unsigned long StatDevReadError = 0; ++static unsigned long StatDevFlushError = 0; ++static unsigned long StatWriteTimout = 0; ++static unsigned long StatMaxWriteTime = 0; ++ ++/************************************** ++** Timer ++**************************************/ ++static struct timer_list BulkOutTim; ++static struct timer_list MediaCheckTim; ++ ++/****************************************************************************** ++** Local Function ++******************************************************************************/ ++ ++static void storage_bulkout_timeout(unsigned long param) ++{ ++ /* statistics update */ ++ StatWriteTimout++; ++ printk(KERN_INFO "storage_fd: write bulk out timeout. length '%ld/%ld'.\n", ++ BulkOutLength, KeepCBW.dCBWDataTransferLength); ++ ++ return; ++} ++ ++static void media_check_timeout(unsigned long param) ++{ ++ /* media check */ ++ schedule_task_register( ++ (SCHEDULE_TASK_FUNC)storageproto_media_status_check, CONTEXT_TIMER, ++ 0, 0, 0, 0); ++ ++ return; ++} ++ ++static void request_sense_data_set(unsigned char sense_key, unsigned char asc, ++ unsigned char ascq, unsigned long info) ++{ ++ /* ++ * set REQUEST SENSE DATA ++ */ ++ ++ memset(&RequestSenseData, 0x00, sizeof(RequestSenseData)); ++ ++ RequestSenseData.ErrorCode = 0x70; ++ RequestSenseData.Valid = (info) ? 1 : 0; ++ RequestSenseData.SenseKey = sense_key; ++ memcpy(RequestSenseData.Information, &info, sizeof(info)); ++ RequestSenseData.AdditionalSenseLength = 0x0a; ++ RequestSenseData.AdditionalSenseCode = asc; ++ RequestSenseData.AdditionalSenseCodeQualifier = ascq; ++ ++ return; ++} ++ ++static void scsi_inquiry_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ SCSI_INQUIRY_DATA data; ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned long data_len; ++ ++ /* ++ * data transport ++ */ ++ ++ memset(&data, 0x00, sizeof(data)); ++ ++ data.PeripheralDeviceType = 0x00; ++ data.RMB = 1; ++ data.ResponseDataFormat = 0x01; ++ data.AdditionalLength = 0x1f; ++ strncpy(data.VendorInformation, CONFIG_USBD_MANUFACTURER, ++ sizeof(data.VendorInformation)); ++ strncpy(data.ProductIdentification, CONFIG_USBD_PRODUCT_NAME, ++ sizeof(data.ProductIdentification)); ++ strncpy(data.ProductRevisionLevel, PRODUCT_REVISION_LEVEL, ++ sizeof(data.ProductRevisionLevel)); ++ ++ data_len = sizeof(data); ++ if(cbw->dCBWDataTransferLength < data_len){ ++ data_len = cbw->dCBWDataTransferLength; ++ } ++ ++ storage_urb_send(device, &data, data_len); ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength - data_len; ++ csw.bCSWStatus = 0; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ return; ++} ++ ++static void scsi_read_format_capacity_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ SCSI_READ_FORMAT_CAPACITY_DATA data; ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned long block_num, data_len; ++ unsigned short block_len; ++ ++ /* ++ * data transport ++ */ ++ ++ block_num = 0xffffffff; ++ block_len = (unsigned short)DeviceBlockSize; ++ block_num = htonl(block_num); ++ block_len = htons(block_len); ++ ++ memset(&data, 0x00, sizeof(data)); ++ ++ data.CapacityListHeader.CapacityListLength = ++ sizeof(data.CurrentMaximumCapacityDescriptor); ++ memcpy(data.CurrentMaximumCapacityDescriptor.NumberofBlocks, &block_num, ++ sizeof(block_num)); ++ data.CurrentMaximumCapacityDescriptor.DescriptorCode = 0x03; ++ memcpy(data.CurrentMaximumCapacityDescriptor.BlockLength + 1, &block_len, ++ sizeof(block_len)); ++ ++ data_len = sizeof(data); ++ if(cbw->dCBWDataTransferLength < data_len){ ++ data_len = cbw->dCBWDataTransferLength; ++ } ++ ++ storage_urb_send(device, &data, data_len); ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength - data_len; ++ csw.bCSWStatus = 0; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ return; ++} ++ ++static void scsi_read_capacity_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ SCSI_READ_CAPACITY_DATA data; ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned char data_len, status = 0; ++ ++ if(DeviceFile == NULL){ ++ /* 0 clear */ ++ memset(&data, 0x00, sizeof(data)); ++ ++ /* data length set */ ++ data_len = cbw->dCBWDataTransferLength; ++ ++ /* status set */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x02, 0x3a, 0x00, 0x00); ++ } ++ else ++ if(MediaChange == MEDIA_CHANGE_ON){ ++ /* 0 clear */ ++ memset(&data, 0x00, sizeof(data)); ++ ++ /* data length set */ ++ data_len = cbw->dCBWDataTransferLength; ++ ++ /* status set */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x06, 0x28, 0x00, 0x00); ++ ++ /* media change flag off */ ++ MediaChange = MEDIA_CHANGE_OFF; ++ } ++ else{ ++ unsigned long last_lba, block_len; ++ ++ /* 0 clear */ ++ memset(&data, 0x00, sizeof(data)); ++ ++ /* data set */ ++ last_lba = (DeviceSize / DeviceBlockSize) - 1; ++ block_len = DeviceBlockSize; ++ last_lba = htonl(last_lba); ++ block_len = htonl(block_len); ++ ++ memcpy(data.LastLogicalBlockAddress, &last_lba, sizeof(last_lba)); ++ memcpy(data.BlockLengthInBytes, &block_len, sizeof(block_len)); ++ ++ /* data length set */ ++ data_len = sizeof(data); ++ ++ /* status set */ ++ status = 0; ++ } ++ ++ /* ++ * data transport ++ */ ++ ++ if(cbw->dCBWDataTransferLength < data_len){ ++ data_len = cbw->dCBWDataTransferLength; ++ } ++ ++ storage_urb_send(device, &data, data_len); ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength - data_len; ++ csw.bCSWStatus = status; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ return; ++} ++ ++static void scsi_request_sense_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned long data_len; ++ ++ /* ++ * data transport ++ */ ++ ++ data_len = sizeof(RequestSenseData); ++ if(cbw->dCBWDataTransferLength < data_len){ ++ data_len = cbw->dCBWDataTransferLength; ++ } ++ ++ storage_urb_send(device, &RequestSenseData, data_len); ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength - data_len; ++ csw.bCSWStatus = 0; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ return; ++} ++ ++static void scsi_read_10_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ SCSI_READ_10_COMMAND* command = (SCSI_READ_10_COMMAND*)cbw->CBWCB; ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned char status = 0; ++ unsigned short len; ++ unsigned long lba, size, offset; ++ ++ memcpy(&lba, command->LogicalBlockAddress, sizeof(lba)); ++ memcpy(&len, command->TransferLength, sizeof(len)); ++ lba = ntohl(lba); ++ len = ntohs(len); ++ offset = lba * DeviceBlockSize; ++ size = cbw->dCBWDataTransferLength; ++ ++ if(DeviceFile == NULL){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x02, 0x3a, 0x00, 0x00); ++ ++ /* ++ * data transport ++ */ ++ ++ storage_urb_send(device, NULL, size); ++ } ++ else ++ if(MediaChange == MEDIA_CHANGE_ON){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x06, 0x28, 0x00, 0x00); ++ ++ /* media change flag off */ ++ MediaChange = MEDIA_CHANGE_OFF; ++ ++ /* ++ * data transport ++ */ ++ ++ storage_urb_send(device, NULL, size); ++ } ++ else{ ++ unsigned long count, read_size; ++ ++ /* ++ * data transport ++ */ ++ ++ /* device seek */ ++ DeviceFile->f_op->llseek(DeviceFile, offset, 0); ++ ++ /* device read */ ++ for(count = size; count; count -= read_size){ ++ read_size = (count > sizeof(BlockBuffer)) ? ++ sizeof(BlockBuffer) : count; ++ if(DeviceFile && ++ DeviceFile->f_op->read(DeviceFile, BlockBuffer, read_size, ++ &DeviceFile->f_pos) != read_size){ ++ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x02, 0x3a, 0x00, 0x00); ++ ++ /* statistics update */ ++ StatDevReadError++; ++ printk(KERN_INFO "storage_fd: device read error. length '%ld'.\n", read_size); ++ } ++ ++ storage_urb_send(device, BlockBuffer, read_size); ++ } ++ } ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength - size; ++ csw.bCSWStatus = status; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ return; ++} ++ ++static void scsi_mode_sense_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ static READ_WRITE_ERROR_RECOVERY_PAGE page_01 = { ++ PageCode:0x01, ++ PageLength:0x0A, ++ ReadRetryCount:0x03, ++ WriteRetryCount:0x80, ++ }; ++ static FLEXIBLE_DISK_PAGE page_05 = { ++ PageCode:0x05, ++ PageLength:0x1E, ++ TransferRate:{0x00, 0xFA}, ++ NumberofHeads:0xA0, ++ SectorsperTrack:0x00, ++ DataBytesperSector:{0x02, 0x00}, ++ NumberofCylinders:{0x00, 0x00}, ++ MotorOnDelay:0x05, ++ MotorOffDelay:0x1E, ++ MediumRotationRate:{0x01, 0x68}, ++ }; ++ static REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE page_1b = { ++ PageCode:0x1B, ++ PageLength:0x0A, ++ TLUN:0x01, ++ }; ++ static TIMER_AND_PROTECT_PAGE page_1c = { ++ PageCode:0x1c, ++ PageLength:0x06, ++ InactivityTimeMultiplier:0x0A, ++ }; ++ ++ ++ SCSI_MODE_SENSE_COMMAND* command = (SCSI_MODE_SENSE_COMMAND*)cbw->CBWCB; ++ SCSI_MODE_SENSE_DATA data; ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned char data_len, status = 0; ++ unsigned short cylinder, sector; ++ unsigned long size; ++ ++ /* ++ * data transport ++ */ ++ ++ memset(&data, 0x00, sizeof(data)); ++ ++ /* set write protect */ ++ data.ModeParameterHeader.WP = DeviceWrProtect; ++ ++ /* set Flexible Disk Page */ ++ if(DeviceFile == NULL){ ++ sector = (unsigned short)DeviceBlockSize; ++ cylinder = 0; ++ sector = htons(sector); ++ cylinder = htons(cylinder); ++ ++ page_05.NumberofHeads = 0; ++ page_05.SectorsperTrack = 0; ++ memcpy(page_05.DataBytesperSector, §or, sizeof(sector)); ++ memcpy(page_05.NumberofCylinders, &cylinder, sizeof(cylinder)); ++ } ++ else{ ++ sector = (unsigned short)DeviceBlockSize; ++ size = DEF_NUMBER_OF_HEADS * DEF_SECTORS_PER_TRACK * sector; ++ cylinder = DeviceSize / size; ++ sector = htons(sector); ++ cylinder = htons(cylinder); ++ ++ page_05.NumberofHeads = DEF_NUMBER_OF_HEADS; ++ page_05.SectorsperTrack = DEF_SECTORS_PER_TRACK; ++ memcpy(page_05.DataBytesperSector, §or, sizeof(sector)); ++ memcpy(page_05.NumberofCylinders, &cylinder, sizeof(cylinder)); ++ } ++ ++ if(command->PC == 0 && command->PageCode == 0x01){ ++ data_len = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_01); ++ data.ModeParameterHeader.ModeDataLength = data_len - 1; ++ memcpy(&data.ModePages, &page_01, sizeof(page_01)); ++ } ++ else ++ if(command->PC == 0 && command->PageCode == 0x05){ ++ data_len = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_05); ++ data.ModeParameterHeader.ModeDataLength = data_len - 1; ++ memcpy(&data.ModePages, &page_05, sizeof(page_05)); ++ } ++ else ++ if(command->PC == 0 && command->PageCode == 0x1b){ ++ data_len = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_1b); ++ data.ModeParameterHeader.ModeDataLength = data_len - 1; ++ memcpy(&data.ModePages, &page_1b, sizeof(page_1b)); ++ } ++ else ++ if(command->PC == 0 && command->PageCode == 0x1c){ ++ data_len = sizeof(MODE_PARAMETER_HEADER) + sizeof(page_1c); ++ data.ModeParameterHeader.ModeDataLength = data_len - 1; ++ memcpy(&data.ModePages, &page_1c, sizeof(page_1c)); ++ } ++ else ++ if(command->PC == 0 && command->PageCode == 0x3f){ ++ data_len = sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_ALL_PAGES); ++ data.ModeParameterHeader.ModeDataLength = data_len - 1; ++ memcpy(&data.ModePages.ModeAllPages.ReadWriteErrorRecoveryPage, ++ &page_01, sizeof(page_01)); ++ memcpy(&data.ModePages.ModeAllPages.FlexibleDiskPage, ++ &page_05, sizeof(page_05)); ++ memcpy(&data.ModePages.ModeAllPages.RemovableBlockAccessCapabilitiesPage, ++ &page_1b, sizeof(page_1b)); ++ memcpy(&data.ModePages.ModeAllPages.TimerAndProtectPage, ++ &page_1c, sizeof(page_1c)); ++ } ++ else{ ++ /* command fail */ ++ status = 1; ++ data_len = cbw->dCBWDataTransferLength; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x05, 0x24, 0x00, 0x00); ++ } ++ ++ if(cbw->dCBWDataTransferLength < data_len){ ++ data_len = cbw->dCBWDataTransferLength; ++ } ++ ++ storage_urb_send(device, &data, data_len); ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength - data_len; ++ csw.bCSWStatus = status; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ return; ++} ++ ++static void scsi_test_unit_ready_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned char status = 0; ++ ++ if(DeviceFile == NULL){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x02, 0x3a, 0x00, 0x00); ++ } ++ else ++ if(MediaChange == MEDIA_CHANGE_ON){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x06, 0x28, 0x00, 0x00); ++ ++ /* media change flag off */ ++ MediaChange = MEDIA_CHANGE_OFF; ++ } ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength; ++ csw.bCSWStatus = status; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ return; ++} ++ ++static void scsi_prevent_allow_medium_removal_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL_COMMAND* command = (SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL_COMMAND*)cbw->CBWCB; ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned char status = 0; ++ ++ if(command->Prevent){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x05, 0x24, 0x00, 0x00); ++ } ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength; ++ csw.bCSWStatus = status; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ return; ++} ++ ++static void scsi_start_stop_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ SCSI_START_STOP_COMMAND* command = (SCSI_START_STOP_COMMAND*)cbw->CBWCB; ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned char status = 0; ++ ++ if(DeviceFile == NULL){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x02, 0x3a, 0x00, 0x00); ++ } ++ else ++ if(MediaChange == MEDIA_CHANGE_ON){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x06, 0x28, 0x00, 0x00); ++ ++ /* media change flag off */ ++ MediaChange = MEDIA_CHANGE_OFF; ++ } ++ else ++ if(command->Start && command->LoEj){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x05, 0x24, 0x00, 0x00); ++ } ++ ++ /* device buffer flush */ ++ if(DeviceFile && DeviceFile->f_op->ioctl(DeviceFile->f_dentry->d_inode, ++ DeviceFile, BLKFLSBUF, 0) != 0){ ++ /* statistics update */ ++ StatDevFlushError++; ++ printk(KERN_INFO "storage_fd: device flush error.\n"); ++ } ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength; ++ csw.bCSWStatus = status; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ return; ++} ++ ++static void scsi_write_10_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ /* status set */ ++ BulkOutLength = 0; ++ StorageStatus = STORAGE_BULKOUT; ++ ++ /* timer set */ ++ del_timer(&BulkOutTim); ++ BulkOutTim.expires = jiffies + ((WR_BULKOUT_CHK_TIM * HZ) / 1000); ++ BulkOutTim.data = 0; ++ BulkOutTim.function = storage_bulkout_timeout; ++ add_timer(&BulkOutTim); ++ ++ return; ++} ++ ++static void scsi_verify_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned char status = 0; ++ ++ if(DeviceFile == NULL){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x02, 0x3a, 0x00, 0x00); ++ } ++ else ++ if(MediaChange == MEDIA_CHANGE_ON){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x06, 0x28, 0x00, 0x00); ++ ++ /* media change flag off */ ++ MediaChange = MEDIA_CHANGE_OFF; ++ } ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength; ++ csw.bCSWStatus = status; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ return; ++} ++ ++static void scsi_unsupport_analysis(struct usb_device_instance* device, ++ COMMAND_BLOCK_WRAPPER* cbw) ++{ ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned char data_len = 0; ++ ++ if(((cbw->bmCBWFlags & 0x80) == 0x00) && (cbw->dCBWDataTransferLength)){ ++ /* BLKOUT */ ++ ++ /* status set */ ++ BulkOutLength = 0; ++ StorageStatus = STORAGE_BULKOUT; ++ } ++ else{ ++ /* BLKIN */ ++ ++ if(cbw->dCBWDataTransferLength){ ++ data_len = cbw->dCBWDataTransferLength; ++ storage_urb_send(device, NULL, data_len); ++ } ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = cbw->dCBWTag; ++ csw.dCSWDataResidue = cbw->dCBWDataTransferLength - data_len; ++ csw.bCSWStatus = 0x01; ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ } ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x05, 0x20, 0x00, 0x00); ++ ++ return; ++} ++ ++static void scsi_bulkout_write_10_analysis(struct usb_device_instance* device, ++ void* buffer, int length) ++{ ++ COMMAND_STATUS_WRAPPER csw; ++ unsigned char status = 0; ++ unsigned long buff_used_len, buff_offset; ++ unsigned long s_tick, e_tick, wr_tick; ++ ++ buff_offset = BulkOutLength % sizeof(BlockBuffer); ++ ++ memcpy(BlockBuffer + buff_offset, buffer, length); ++ BulkOutLength += length; ++ ++ buff_used_len = BulkOutLength % sizeof(BlockBuffer); ++ if(buff_used_len == 0) buff_used_len = sizeof(BlockBuffer); ++ ++ /* delete timer */ ++ if(BulkOutLength >= KeepCBW.dCBWDataTransferLength){ ++ del_timer(&BulkOutTim); ++ } ++ ++ if(buff_used_len >= sizeof(BlockBuffer) || ++ BulkOutLength >= KeepCBW.dCBWDataTransferLength){ ++ ++ /* buffer full */ ++ SCSI_WRITE_10_COMMAND* command = (SCSI_WRITE_10_COMMAND*)&KeepCBW.CBWCB; ++ unsigned short len; ++ unsigned long lba, offset; ++ ++ memcpy(&lba, command->LogicalBlockAddress, sizeof(lba)); ++ memcpy(&len, command->TransferLength, sizeof(len)); ++ lba = ntohl(lba); ++ len = ntohs(len); ++ ++ offset = (lba * DeviceBlockSize) + ++ (sizeof(BlockBuffer) * ((BulkOutLength - 1) / sizeof(BlockBuffer))); ++ ++ /* device check */ ++ if(DeviceFile && ++ MediaChange != MEDIA_CHANGE_ON && ++ DeviceWrProtect != WR_PROTECT_ON){ ++ ++ /* write before jiffies get */ ++ s_tick = jiffies; ++ ++ /* device seek */ ++ DeviceFile->f_op->llseek(DeviceFile, offset, 0); ++ ++ /* device write */ ++ if(DeviceFile->f_op->write(DeviceFile, BlockBuffer, buff_used_len, ++ &DeviceFile->f_pos) != buff_used_len){ ++ /* statistics update */ ++ StatDevWriteError++; ++ printk(KERN_INFO "storage_fd: device write error. length '%ld'.\n", buff_used_len); ++ } ++ ++ /* write after jiffies get */ ++ e_tick = jiffies; ++ ++ /* statistics update */ ++ wr_tick = e_tick - s_tick; ++ if(wr_tick > StatMaxWriteTime){ ++ StatMaxWriteTime = wr_tick; ++ } ++ } ++ } ++ ++ if(BulkOutLength >= KeepCBW.dCBWDataTransferLength){ ++ if(DeviceFile == NULL){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x02, 0x3a, 0x00, 0x00); ++ } ++ else ++ if(MediaChange == MEDIA_CHANGE_ON){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x06, 0x28, 0x00, 0x00); ++ ++ /* media change flag off */ ++ MediaChange = MEDIA_CHANGE_OFF; ++ } ++ else ++ if(DeviceWrProtect == WR_PROTECT_ON){ ++ /* command fail */ ++ status = 1; ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x07, 0x27, 0x00, 0x00); ++ ++ /* media change flag off */ ++ MediaChange = MEDIA_CHANGE_OFF; ++ } ++ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = KeepCBW.dCBWTag; ++ csw.dCSWDataResidue = KeepCBW.dCBWDataTransferLength - BulkOutLength; ++ csw.bCSWStatus = status; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ /* status reset */ ++ BulkOutLength = 0; ++ StorageStatus = STORAGE_IDLE; ++ ++ /* flush before jiffies get */ ++ s_tick = jiffies; ++ ++ /* device buffer flush */ ++ if(DeviceFile && DeviceFile->f_op->ioctl(DeviceFile->f_dentry->d_inode, ++ DeviceFile, BLKFLSBUF, 0) != 0){ ++ /* statistics update */ ++ StatDevFlushError++; ++ printk(KERN_INFO "storage_fd: device flush error.\n"); ++ } ++ ++ /* flush after jiffies get */ ++ e_tick = jiffies; ++ ++ /* statistics update */ ++ wr_tick = e_tick - s_tick; ++ if(wr_tick > StatMaxWriteTime){ ++ StatMaxWriteTime = wr_tick; ++ } ++ ++ } ++ ++ return; ++} ++ ++static void scsi_bulkout_unsupport_analysis(struct usb_device_instance* device, ++ void* buffer, int length) ++{ ++ COMMAND_STATUS_WRAPPER csw; ++ ++ BulkOutLength += length; ++ ++ if(BulkOutLength >= KeepCBW.dCBWDataTransferLength){ ++ /* ++ * status transport ++ */ ++ ++ memset(&csw, 0x00, sizeof(csw)); ++ ++ csw.dCSWSignature = CSW_SIGNATURE; ++ csw.dCSWTag = KeepCBW.dCBWTag; ++ csw.dCSWDataResidue = KeepCBW.dCBWDataTransferLength - BulkOutLength; ++ csw.bCSWStatus = 1; ++ ++ storage_urb_send(device, &csw, sizeof(csw)); ++ ++ /* error code save REQUEST SENSE */ ++ request_sense_data_set(0x05, 0x20, 0x00, 0x00); ++ ++ /* status reset */ ++ BulkOutLength = 0; ++ StorageStatus = STORAGE_IDLE; ++ } ++ ++ return; ++} ++ ++/****************************************************************************** ++** Global Function ++******************************************************************************/ ++ ++static SCSI_ANALYSIS_TBL ScsiAnalysisTbl[] = { ++ {SCSI_FORMAT_UNT, "SCSI_FORMAT_UNT", NULL}, ++ {SCSI_INQUIRY, "SCSI_INQUIRY", scsi_inquiry_analysis}, ++ {SCSI_START_STOP, "SCSI_START_STOP", scsi_start_stop_analysis}, ++ {SCSI_MODE_SELECT, "SCSI_MODE_SELECT", NULL}, ++ {SCSI_MODE_SENSE, "SCSI_MODE_SENSE", scsi_mode_sense_analysis}, ++ {SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, "SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL", scsi_prevent_allow_medium_removal_analysis}, ++ {SCSI_READ_10, "SCSI_READ_10", scsi_read_10_analysis}, ++ {SCSI_READ_12, "SCSI_READ_12", NULL}, ++ {SCSI_READ_CAPACITY, "SCSI_READ_CAPACITY", scsi_read_capacity_analysis}, ++ {SCSI_READ_FORMAT_CAPACITY, "SCSI_READ_FORMAT_CAPACITY", scsi_read_format_capacity_analysis}, ++ {SCSI_REQUEST_SENSE, "SCSI_REQUEST_SENSE", scsi_request_sense_analysis}, ++ {SCSI_REZERO_UNIT, "SCSI_REZERO_UNIT", NULL}, ++ {SCSI_SEEK_10, "SCSI_SEEK_10", NULL}, ++ {SCSI_SEND_DIAGNOSTIC, "SCSI_SEND_DIAGNOSTIC", NULL}, ++ {SCSI_TEST_UNIT_READY, "SCSI_TEST_UNIT_READY", scsi_test_unit_ready_analysis}, ++ {SCSI_VERIFY, "SCSI_VERIFY", scsi_verify_analysis}, ++ {SCSI_WRITE_10, "SCSI_WRITE_10", scsi_write_10_analysis}, ++ {SCSI_WRITE_12, "SCSI_WRITE_12", NULL}, ++ {SCSI_WRITE_AND_VERIFY, "SCSI_WRITE_AND_VERIFY", NULL} ++}; ++ ++static SCSI_BULKOUT_ANALYSIS_TBL ScsiBlkOutAnalysisTbl[] = { ++ {SCSI_WRITE_10, "SCSI_WRITE_10", scsi_bulkout_write_10_analysis}, ++}; ++ ++void storageproto_urb_analysis(struct urb* urb) ++{ ++ COMMAND_BLOCK_WRAPPER* cbw = (COMMAND_BLOCK_WRAPPER*)urb->buffer; ++ int i; ++ ++ /* status BLKOUT check */ ++ if(StorageStatus == STORAGE_BULKOUT){ ++ for(i = 0; ++ i < sizeof(ScsiBlkOutAnalysisTbl) / sizeof(SCSI_ANALYSIS_TBL); ++ i++){ ++ if(ScsiBlkOutAnalysisTbl[i].scsi_command == KeepCBW.CBWCB[0]){ ++ if(ScsiBlkOutAnalysisTbl[i].bulkout_func){ ++ ScsiBlkOutAnalysisTbl[i].bulkout_func(urb->device, ++ urb->buffer, ++ urb->actual_length); ++ goto RETURN_LABEL; ++ } ++ break; ++ } ++ } ++ scsi_bulkout_unsupport_analysis(urb->device, urb->buffer, urb->actual_length); ++ goto RETURN_LABEL; ++ } ++ ++ /* signature check */ ++ if(cbw->dCBWSignature != CBW_SIGNATURE){ ++ printk(KERN_INFO "storage_fd: signature error. '0x%08lx'.\n", ++ cbw->dCBWSignature); ++ goto RETURN_LABEL; ++ } ++ ++ /* statistics set */ ++ if(cbw->dCBWDataTransferLength){ ++ if(((cbw->bmCBWFlags & 0x80) == 0x00) && (cbw->dCBWDataTransferLength)){ ++ /* BULK OUT */ ++ if(StatMaxBulkOutSize < cbw->dCBWDataTransferLength){ ++ StatMaxBulkOutSize = cbw->dCBWDataTransferLength; ++ } ++ } ++ else{ ++ /* BULK IN */ ++ if(StatMaxBulkInSize < cbw->dCBWDataTransferLength){ ++ StatMaxBulkInSize = cbw->dCBWDataTransferLength; ++ } ++ } ++ } ++ ++ /* save CBW and set storage status */ ++ memcpy(&KeepCBW, cbw, sizeof(KeepCBW)); ++ ++ /* UFI command analysis */ ++ for(i = 0; i < sizeof(ScsiAnalysisTbl) / sizeof(SCSI_ANALYSIS_TBL); i++){ ++ if(ScsiAnalysisTbl[i].scsi_command == cbw->CBWCB[0]){ ++ if(ScsiAnalysisTbl[i].scsi_func){ ++ ScsiAnalysisTbl[i].scsi_func(urb->device, cbw); ++ goto RETURN_LABEL; ++ } ++ break; ++ } ++ } ++ ++ scsi_unsupport_analysis(urb->device, cbw); ++ printk(KERN_INFO "storage_fd: SCSI command error. '0x%02x'.\n", ++ cbw->CBWCB[0]); ++ goto RETURN_LABEL; ++ ++RETURN_LABEL: ++ ++ /* URB free */ ++ usbd_recycle_urb(urb); ++ ++ return; ++} ++ ++int storageproto_device_open_check(void) ++{ ++ struct file* file; ++ struct inode* inode; ++ kdev_t dev; ++ int read_org; ++ ++ /* device already open check */ ++ if(DeviceFile){ ++ storageproto_device_close(); ++ } ++ ++ /* device open */ ++ file = filp_open(storage_device, O_RDWR, 0000); ++ if(IS_ERR(file)){ ++ file = filp_open(storage_device, O_RDONLY, 0000); ++ if(IS_ERR(file)){ ++ storageproto_device_close(); ++ return MEDIA_EJECT; ++ } ++ DeviceWrProtect = WR_PROTECT_ON; ++ } ++ ++ file->f_op->llseek(file, 0, 0); ++ if(file->f_op->read(file, (void*)&read_org, sizeof(read_org), &file->f_pos) ++ != sizeof(read_org)){ ++ filp_close(file, NULL); ++ storageproto_device_close(); ++ return MEDIA_EJECT; ++ } ++ ++ /* struct file pointer save */ ++ DeviceFile = file; ++ ++ /* device information */ ++ inode = file->f_dentry->d_inode; ++ dev = inode->i_rdev; ++ ++ if (blk_size[MAJOR(dev)]){ ++ DeviceSize = blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS; ++ } ++ else{ ++ DeviceSize = INT_MAX << BLOCK_SIZE_BITS; ++ } ++ ++ if(blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)]){ ++ DeviceBlockSize = DEVICE_BLOCK_SIZE; ++ } ++ else{ ++ DeviceBlockSize = DEVICE_BLOCK_SIZE; ++ } ++ ++ return MEDIA_INSERT; ++} ++ ++void storageproto_device_close(void) ++{ ++ if(DeviceFile){ ++ filp_close(DeviceFile, NULL); ++ DeviceFile = NULL; ++ DeviceSize = 0; ++ DeviceBlockSize = DEVICE_BLOCK_SIZE; ++ DeviceWrProtect = WR_PROTECT_OFF; ++ } ++ ++ return; ++} ++ ++void storageproto_usb_status_check(int status) ++{ ++ static int Is1stCheck = 1; ++ ++ /* USB status check */ ++ if(Is1stCheck){ ++ Is1stCheck = 0; ++ } ++ else{ ++ if(UsbStatus == status) goto RETURN_LABEL; ++ } ++ ++ /* set status */ ++ UsbStatus = status; ++ ++ switch(UsbStatus){ ++ case USB_CONNECT: ++ /* media status check */ ++ storageproto_media_status_check(CONTEXT_SCHEDULE); ++ ++ switch(MediaStatus){ ++ case MEDIA_EJECT: ++ break; ++ ++ case MEDIA_INSERT: ++ hotplug("usbdstorage", storage_device, "umount"); ++ break; ++ ++ default: ++ break; ++ } ++ hotplug("usbdstorage", storage_device, "connect"); ++ break; ++ ++ case USB_DISCONNECT: ++ /* device close */ ++ storageproto_device_close(); ++ ++ switch(MediaStatus){ ++ case MEDIA_EJECT: ++ break; ++ ++ case MEDIA_INSERT: ++ hotplug("usbdstorage", storage_device, "mount"); ++ break; ++ ++ default: ++ break; ++ } ++ hotplug("usbdstorage", storage_device, "disconnect"); ++ break; ++ ++ default: ++ break; ++ } ++ ++RETURN_LABEL: ++ DBG_STORAGE_FD(KERN_INFO "storage_fd: USB check '%s' '%s' 'file:%p'.\n", ++ (UsbStatus) ? "USB_CONNECT" : "USB_DISCONNECT", ++ (MediaStatus) ? "MEDIA_INSERT" : "MEDIA_EJECT", DeviceFile); ++ ++ return; ++} ++ ++void storageproto_media_status_check(int call_context) ++{ ++ static unsigned long RetryCount = 0; ++ int status; ++ ++ /* media open check */ ++ status = storageproto_device_open_check(); ++ ++ /* media status check retry */ ++ if(status == MEDIA_EJECT){ ++ switch(call_context){ ++ case CONTEXT_STORAGE: ++ ++ /* retry counter init */ ++ RetryCount = 0; ++ ++ /* timer set */ ++ del_timer(&MediaCheckTim); ++ MediaCheckTim.expires = jiffies + ((MEDIA_CHECK_TIM * HZ) / 1000); ++ MediaCheckTim.data = 0; ++ MediaCheckTim.function = media_check_timeout; ++ add_timer(&MediaCheckTim); ++ ++ break; ++ ++ case CONTEXT_TIMER: ++ ++ /* retry counter update */ ++ RetryCount++; ++ ++ /* retry counter check */ ++ if(RetryCount >= MEDIA_CHECK_RETRY) break; ++ ++ /* timer set */ ++ del_timer(&MediaCheckTim); ++ MediaCheckTim.expires = jiffies + ((MEDIA_CHECK_TIM * HZ) / 1000); ++ MediaCheckTim.data = 0; ++ MediaCheckTim.function = media_check_timeout; ++ add_timer(&MediaCheckTim); ++ ++ break; ++ ++ default: ++ break; ++ } ++ } ++ else ++ if(status == MEDIA_INSERT){ ++ /* delete timer */ ++ del_timer(&MediaCheckTim); ++ } ++ ++ /* media status check */ ++ if(status == MediaStatus){ ++ if(UsbStatus == USB_DISCONNECT){ ++ storageproto_device_close(); ++ } ++ if(MediaStatus == MEDIA_INSERT){ ++ if(call_context == CONTEXT_STORAGE || call_context == CONTEXT_TIMER){ ++ MediaChange = MEDIA_CHANGE_ON; ++ } ++ } ++ goto RETURN_LABEL; ++ } ++ ++ /* set status */ ++ MediaStatus = status; ++ ++ switch(MediaStatus){ ++ case MEDIA_INSERT: ++ /* set status */ ++ MediaChange = MEDIA_CHANGE_ON; ++ ++ switch(UsbStatus){ ++ case USB_DISCONNECT: ++ storageproto_device_close(); ++ break; ++ ++ case USB_CONNECT: ++ break; ++ ++ default: ++ break; ++ } ++ hotplug("usbdstorage", storage_device, "insert"); ++ break; ++ ++ case MEDIA_EJECT: ++ switch(UsbStatus){ ++ case USB_DISCONNECT: ++ storageproto_device_close(); ++ break; ++ ++ case USB_CONNECT: ++ break; ++ ++ default: ++ break; ++ } ++ hotplug("usbdstorage", storage_device, "eject"); ++ break; ++ ++ default: ++ break; ++ } ++ ++RETURN_LABEL: ++ DBG_STORAGE_FD(KERN_INFO "storage_fd: media check. '%s' '%s' 'file:%p' '%s'.\n", ++ (UsbStatus) ? "USB_CONNECT" : "USB_DISCONNECT", ++ (MediaStatus) ? "MEDIA_INSERT" : "MEDIA_EJECT", DeviceFile, ++ (call_context == CONTEXT_SCHEDULE) ? "CONTEXT_SCHEDULE" : ++ (call_context == CONTEXT_STORAGE) ? "CONTEXT_STORAGE" : "CONTEXT_TIMER"); ++ ++ return; ++} ++ ++void storageproto_usb_reset_ind(void) ++{ ++ /* status reset */ ++ BulkOutLength = 0; ++ StorageStatus = STORAGE_IDLE; ++ ++ DBG_STORAGE_FD(KERN_INFO "storage_fd: storage protocol reset.\n"); ++ ++ return; ++} ++ ++void storageproto_init(void) ++{ ++ /* timer init */ ++ init_timer(&BulkOutTim); ++ ++ /* timer init */ ++ init_timer(&MediaCheckTim); ++ ++ return; ++} ++ ++void storageproto_exit(void) ++{ ++ /* device close */ ++ storageproto_device_close(); ++ ++ /* delete timer */ ++ del_timer(&BulkOutTim); ++ ++ /* delete timer */ ++ del_timer(&MediaCheckTim); ++ ++ return; ++} ++ ++ssize_t storageproto_proc_read(struct file* file, char* buf, size_t count, ++ loff_t* pos) ++{ ++ char string[1024]; ++ int len = 0; ++ ++ len += sprintf(string + len, "Protocol status:%s\n", ++ (StorageStatus == STORAGE_IDLE) ? "STORAGE_IDLE" : ++ (StorageStatus == STORAGE_BULKIN) ? "STORAGE_BULKIN" : ++ "STORAGE_BULKOUT"); ++ ++ len += sprintf(string + len, "USB status:%s\n", ++ (UsbStatus == USB_DISCONNECT) ? "USB_DISCONNECT" : ++ "USB_CONNECT"); ++ ++ len += sprintf(string + len, "Media status:%s\n", ++ (MediaStatus == MEDIA_EJECT) ? "MEDIA_EJECT" : ++ "MEDIA_INSERT"); ++ ++ len += sprintf(string + len, "Media chage:%s\n", ++ (MediaChange == MEDIA_CHANGE_OFF) ? "MEDIA_CHANGE_OFF" : ++ "MEDIA_CHANGE_ON"); ++ ++ len += sprintf(string + len, "Device name:%s\n", ++ storage_device); ++ ++ len += sprintf(string + len, "Device file descriptor:0x%p\n", ++ DeviceFile); ++ ++ len += sprintf(string + len, "Device size:0x%d\n", ++ DeviceSize); ++ ++ len += sprintf(string + len, "Device block size:0x%d\n", ++ DeviceBlockSize); ++ ++ len += sprintf(string + len, "Device write protect:%s\n", ++ (DeviceWrProtect == WR_PROTECT_OFF) ? "WR_PROTECT_OFF": ++ "WR_PROTECT_ON"); ++ ++ len += sprintf(string + len, "Bulk in max size:%ld\n", ++ StatMaxBulkInSize); ++ ++ len += sprintf(string + len, "Bulk out max size:%ld\n", ++ StatMaxBulkOutSize); ++ ++ len += sprintf(string + len, "device write error:%ld\n", ++ StatDevWriteError); ++ ++ len += sprintf(string + len, "device read error:%ld\n", ++ StatDevReadError); ++ ++ len += sprintf(string + len, "device flush error:%ld\n", ++ StatDevFlushError); ++ ++ len += sprintf(string + len, "write data bulk out timout:%ld\n", ++ StatWriteTimout); ++ ++ len += sprintf(string + len, "device write max time:%ld msec\n", ++ (StatMaxWriteTime * 1000) / HZ); ++ ++ *pos += len; ++ if(len > count){ ++ len = -EINVAL; ++ } ++ else ++ if(len > 0 && copy_to_user(buf, string, len)) { ++ len = -EFAULT; ++ } ++ ++ return len; ++} ++ +diff -Nur linux-2.4.18/drivers/usb/device/storage_fd/storageproto.h linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/storageproto.h +--- linux-2.4.18/drivers/usb/device/storage_fd/storageproto.h 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.18-usb-storage/drivers/usb/device/storage_fd/storageproto.h 2003-11-07 05:34:43.000000000 +0300 +@@ -0,0 +1,585 @@ ++/* ++ * linux/drivers/usb/device/storage_fd/storageproto.h - mass storage protocol library header ++ * ++ * Copyright (c) 2003 Lineo Solutions, Inc. ++ * ++ * Written by Shunnosuke kabata ++ * ++ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef _STORAGEPROTO_H_ ++#define _STORAGEPROTO_H_ ++ ++/****************************************************************************** ++** Macro Define ++******************************************************************************/ ++ ++/************************************** ++** Class Code ++**************************************/ ++ ++/* ++ * Class ++ */ ++ ++#define MASS_STORAGE_CLASS 0x08 ++ ++/* ++ * SubClass ++ */ ++ ++#define MASS_STORAGE_SUBCLASS_RBC 0x01 ++#define MASS_STORAGE_SUBCLASS_SFF8020I 0x02 ++#define MASS_STORAGE_SUBCLASS_QIC157 0x03 ++#define MASS_STORAGE_SUBCLASS_UFI 0x04 ++#define MASS_STORAGE_SUBCLASS_SFF8070I 0x05 ++#define MASS_STORAGE_SUBCLASS_SCSI 0x06 ++ ++/* ++ * Protocol ++ */ ++ ++#define MASS_STORAGE_PROTO_CBI_WITH_COMP 0x00 ++#define MASS_STORAGE_PROTO_CBI_NO_COMP 0x01 ++#define MASS_STORAGE_PROTO_BULK_ONLY 0x50 ++ ++/************************************** ++** SCSI Command ++**************************************/ ++ ++#define SCSI_FORMAT_UNT 0x04 ++#define SCSI_INQUIRY 0x12 ++#define SCSI_START_STOP 0x1b ++#define SCSI_MODE_SELECT 0x55 ++#define SCSI_MODE_SENSE 0x1a ++#define SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e ++#define SCSI_READ_10 0x28 ++#define SCSI_READ_12 0xa8 ++#define SCSI_READ_CAPACITY 0x25 ++#define SCSI_READ_FORMAT_CAPACITY 0x23 ++#define SCSI_REQUEST_SENSE 0x03 ++#define SCSI_REZERO_UNIT 0x01 ++#define SCSI_SEEK_10 0x2b ++#define SCSI_SEND_DIAGNOSTIC 0x1d ++#define SCSI_TEST_UNIT_READY 0x00 ++#define SCSI_VERIFY 0x2f ++#define SCSI_WRITE_10 0x2a ++#define SCSI_WRITE_12 0xaa ++#define SCSI_WRITE_AND_VERIFY 0x2e ++ ++/************************************** ++** SCSI Command Parameter ++**************************************/ ++ ++#define CBW_SIGNATURE 0x43425355 /* USBC */ ++#define CSW_SIGNATURE 0x53425355 /* USBS */ ++ ++#define PRODUCT_REVISION_LEVEL "1.00" ++ ++/************************************** ++** Status ++**************************************/ ++ ++#define WR_PROTECT_OFF 0 ++#define WR_PROTECT_ON 1 ++ ++#define STORAGE_IDLE 0 ++#define STORAGE_BULKIN 1 ++#define STORAGE_BULKOUT 2 ++ ++#define USB_DISCONNECT 0 ++#define USB_CONNECT 1 ++ ++#define MEDIA_EJECT 0 ++#define MEDIA_INSERT 1 ++ ++#define MEDIA_CHANGE_OFF 0 ++#define MEDIA_CHANGE_ON 1 ++ ++/************************************** ++** Mass Storage Thread Name ++**************************************/ ++ ++#define STORAGE_THREAD_NAME "usbdstorage" ++ ++/************************************** ++** Media Signal Delay Time(ms) ++**************************************/ ++ ++#define USB_EVENT_DELAY_TIM 1000 ++ ++/************************************** ++** Write Bulk Out Check Time(ms) ++**************************************/ ++ ++#define WR_BULKOUT_CHK_TIM 1000 ++ ++/************************************** ++** Media Check Time(ms) ++**************************************/ ++ ++#define MEDIA_CHECK_TIM 3000 ++#define MEDIA_CHECK_RETRY 3 ++ ++/************************************** ++** Context ++**************************************/ ++ ++#define CONTEXT_SCHEDULE 0 ++#define CONTEXT_STORAGE 1 ++#define CONTEXT_TIMER 2 ++ ++/************************************** ++** Debug Message ++**************************************/ ++#if 0 ++#define DBG_STORAGE_FD(fmt, args...) printk(fmt, ##args) ++#else ++#define DBG_STORAGE_FD(fmt, args...) ++#endif ++ ++/****************************************************************************** ++** Structure Define ++******************************************************************************/ ++ ++/************************************** ++** Command Block Wrapper / Command Status Wrapper ++**************************************/ ++ ++/* ++ * Command Block Wrapper ++ */ ++typedef struct{ ++ unsigned long dCBWSignature; ++ unsigned long dCBWTag; ++ unsigned long dCBWDataTransferLength; ++ unsigned char bmCBWFlags; ++ unsigned char bCBWLUN:4, ++ Reserved:4; ++ unsigned char bCBWCBLength:5, ++ Reserved2:3; ++ unsigned char CBWCB[16]; ++} __attribute__((packed)) COMMAND_BLOCK_WRAPPER; ++ ++/* ++ * Command Status Wrapper ++ */ ++typedef struct{ ++ unsigned long dCSWSignature; ++ unsigned long dCSWTag; ++ unsigned long dCSWDataResidue; ++ unsigned char bCSWStatus; ++} __attribute__((packed)) COMMAND_STATUS_WRAPPER; ++ ++/************************************** ++** SCSI Command ++**************************************/ ++ ++/* ++ * INQUIRY ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char EVPD:1, ++ Reserved1:4, ++ LogicalUnitNumber:3; ++ unsigned char PageCode; ++ unsigned char Reserved2; ++ unsigned char AllocationLength; ++ unsigned char Reserved3; ++ unsigned char Reserved4; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++ unsigned char Reserved7; ++ unsigned char Reserved8; ++ unsigned char Reserved9; ++} __attribute__((packed)) SCSI_INQUIRY_COMMAND; ++ ++typedef struct{ ++ unsigned char PeripheralDeviceType:5, ++ Reserved1:3; ++ unsigned char Reserved2:7, ++ RMB:1; ++ unsigned char ANSIVersion:3, ++ ECMAVersion:3, ++ ISOVersion:2; ++ unsigned char ResponseDataFormat:4, ++ Reserved3:4; ++ unsigned char AdditionalLength; ++ unsigned char Reserved4; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++ unsigned char VendorInformation[8]; ++ unsigned char ProductIdentification[16]; ++ unsigned char ProductRevisionLevel[4]; ++} __attribute__((packed)) SCSI_INQUIRY_DATA; ++ ++/* ++ * READ FORMAT CAPACITY ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char Reserved1:5, ++ LogicalUnitNumber:3; ++ unsigned char Reserved2; ++ unsigned char Reserved3; ++ unsigned char Reserved4; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++ unsigned char AllocationLength[2]; ++ unsigned char Reserved7; ++ unsigned char Reserved8; ++ unsigned char Reserved9; ++} __attribute__((packed)) SCSI_READ_FORMAT_CAPACITY_COMMAND; ++ ++typedef struct{ ++ struct{ ++ unsigned char Reserved1; ++ unsigned char Reserved2; ++ unsigned char Reserved3; ++ unsigned char CapacityListLength; ++ } __attribute__((packed)) CapacityListHeader; ++ struct{ ++ unsigned char NumberofBlocks[4]; ++ unsigned char DescriptorCode:2, ++ Reserved1:6; ++ unsigned char BlockLength[3]; ++ } __attribute__((packed)) CurrentMaximumCapacityDescriptor; ++} __attribute__((packed)) SCSI_READ_FORMAT_CAPACITY_DATA; ++ ++/* ++ * READ FORMAT CAPACITY ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char RelAdr:1, ++ Reserved1:4, ++ LogicalUnitNumber:3; ++ unsigned char LogicalBlockAddress[4]; ++ unsigned char Reserved2; ++ unsigned char Reserved3; ++ unsigned char PMI:1, ++ Reserved4:7; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++ unsigned char Reserved7; ++} __attribute__((packed)) SCSI_READ_CAPACITY_COMMAND; ++ ++typedef struct{ ++ unsigned char LastLogicalBlockAddress[4]; ++ unsigned char BlockLengthInBytes[4]; ++} __attribute__((packed)) SCSI_READ_CAPACITY_DATA; ++ ++/* ++ * REQUEST SENSE ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char Reserved1:5, ++ LogicalUnitNumber:3; ++ unsigned char Reserved2; ++ unsigned char Reserved3; ++ unsigned char AllocationLength; ++ unsigned char Reserved4; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++ unsigned char Reserved7; ++ unsigned char Reserved8; ++ unsigned char Reserved9; ++ unsigned char Reserved10; ++} __attribute__((packed)) SCSI_REQUEST_SENSE_COMMAND; ++ ++typedef struct{ ++ unsigned char ErrorCode:7, ++ Valid:1; ++ unsigned char Reserved1; ++ unsigned char SenseKey:4, ++ Reserved2:4; ++ unsigned char Information[4]; ++ unsigned char AdditionalSenseLength; ++ unsigned char Reserved3[4]; ++ unsigned char AdditionalSenseCode; ++ unsigned char AdditionalSenseCodeQualifier; ++ unsigned char Reserved4; ++ unsigned char Reserved5[3]; ++} __attribute__((packed)) SCSI_REQUEST_SENSE_DATA; ++ ++/* ++ * READ(10) ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char RelAdr:1, ++ Reserved1:2, ++ FUA:1, ++ DPO:1, ++ LogicalUnitNumber:3; ++ unsigned char LogicalBlockAddress[4]; ++ unsigned char Reserved2; ++ unsigned char TransferLength[2]; ++ unsigned char Reserved3; ++ unsigned char Reserved4; ++ unsigned char Reserved5; ++} __attribute__((packed)) SCSI_READ_10_COMMAND; ++ ++/* ++ * MODE SENSE ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char Reserved1:3, ++ DBD:1, ++ Reserved2:1, ++ LogicalUnitNumber:3; ++ unsigned char PageCode:6, ++ PC:2; ++ unsigned char Reserved3; ++ unsigned char Reserved4; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++ unsigned char ParameterListLength[2]; ++ unsigned char Reserved7; ++ unsigned char Reserved8; ++ unsigned char Reserved9; ++} __attribute__((packed)) SCSI_MODE_SENSE_COMMAND; ++ ++typedef struct{ ++ unsigned char ModeDataLength; ++ unsigned char MediumTypeCode; ++ unsigned char Reserved1:4, ++ DPOFUA:1, ++ Reserved2:2, ++ WP:1; ++ unsigned char Reserved3; ++} __attribute__((packed)) MODE_PARAMETER_HEADER; ++ ++typedef struct{ ++ unsigned char PageCode:6, ++ Reserved1:1, ++ PS:1; ++ unsigned char PageLength; ++ unsigned char DCR:1, ++ Reserved2:1, ++ PER:1, ++ Reserved3:1, ++ RC:1, ++ Reserved4:1, ++ Reserved5:1, ++ AWRE:1; ++ unsigned char ReadRetryCount; ++ unsigned char Reserved6[4]; ++ unsigned char WriteRetryCount; ++ unsigned char Reserved7[3]; ++} __attribute__((packed)) READ_WRITE_ERROR_RECOVERY_PAGE; ++ ++typedef struct{ ++ unsigned char PageCode:6, ++ Reserved1:1, ++ PS:1; ++ unsigned char PageLength; ++ unsigned char TransferRate[2]; ++ unsigned char NumberofHeads; ++ unsigned char SectorsperTrack; ++ unsigned char DataBytesperSector[2]; ++ unsigned char NumberofCylinders[2]; ++ unsigned char Reserved2[9]; ++ unsigned char MotorOnDelay; ++ unsigned char MotorOffDelay; ++ unsigned char Reserved3[7]; ++ unsigned char MediumRotationRate[2]; ++ unsigned char Reserved4; ++ unsigned char Reserved5; ++} __attribute__((packed)) FLEXIBLE_DISK_PAGE; ++ ++typedef struct{ ++ unsigned char PageCode:6, ++ Reserved1:1, ++ PS:1; ++ unsigned char PageLength; ++ unsigned char Reserved2:6, ++ SRFP:1, ++ SFLP:1; ++ unsigned char TLUN:3, ++ Reserved3:3, ++ SML:1, ++ NCD:1; ++ unsigned char Reserved4[8]; ++} __attribute__((packed)) REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE; ++ ++typedef struct{ ++ unsigned char PageCode:6, ++ Reserved1:1, ++ PS:1; ++ unsigned char PageLength; ++ unsigned char Reserved2; ++ unsigned char InactivityTimeMultiplier:4, ++ Reserved3:4; ++ unsigned char SWPP:1, ++ DISP:1, ++ Reserved4:6; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++ unsigned char Reserved7; ++} __attribute__((packed)) TIMER_AND_PROTECT_PAGE; ++ ++typedef struct{ ++ READ_WRITE_ERROR_RECOVERY_PAGE ReadWriteErrorRecoveryPage; ++ FLEXIBLE_DISK_PAGE FlexibleDiskPage; ++ REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE RemovableBlockAccessCapabilitiesPage; ++ TIMER_AND_PROTECT_PAGE TimerAndProtectPage; ++} __attribute__((packed)) MODE_ALL_PAGES; ++ ++typedef struct{ ++ MODE_PARAMETER_HEADER ModeParameterHeader; ++ union{ ++ READ_WRITE_ERROR_RECOVERY_PAGE ReadWriteErrorRecoveryPage; ++ FLEXIBLE_DISK_PAGE FlexibleDiskPage; ++ REMOVABLE_BLOCK_ACCESS_CAPABILITIES_PAGE RemovableBlockAccessCapabilitiesPage; ++ TIMER_AND_PROTECT_PAGE TimerAndProtectPage; ++ MODE_ALL_PAGES ModeAllPages; ++ } __attribute__((packed)) ModePages; ++} __attribute__((packed)) SCSI_MODE_SENSE_DATA; ++ ++/* ++ * TEST UNIT READY ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char Reserved1:5, ++ LogicalUnitNumber:3; ++ unsigned char Reserved2; ++ unsigned char Reserved3; ++ unsigned char Reserved4; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++ unsigned char Reserved7; ++ unsigned char Reserved8; ++ unsigned char Reserved9; ++ unsigned char Reserved10; ++ unsigned char Reserved11; ++} __attribute__((packed)) SCSI_TEST_UNIT_READY_COMMAND; ++ ++/* ++ * PREVENT-ALLOW MEDIUM REMOVAL ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char Reserved1:5, ++ LogicalUnitNumber:3; ++ unsigned char Reserved2; ++ unsigned char Reserved3; ++ unsigned char Prevent:1, ++ Reserved4:7; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++ unsigned char Reserved7; ++ unsigned char Reserved8; ++ unsigned char Reserved9; ++ unsigned char Reserved10; ++ unsigned char Reserved11; ++} __attribute__((packed)) SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL_COMMAND; ++ ++/* ++ * START-STOP UNIT ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char IMMED:1, ++ Reserved1:4, ++ LogicalUnitNumber:3; ++ unsigned char Reserved2; ++ unsigned char Reserved3; ++ unsigned char Start:1, ++ LoEj:1, ++ Reserved4:6; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++ unsigned char Reserved7; ++ unsigned char Reserved8; ++ unsigned char Reserved9; ++ unsigned char Reserved10; ++ unsigned char Reserved11; ++} __attribute__((packed)) SCSI_START_STOP_COMMAND; ++ ++/* ++ * WRITE(10) ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char RelAdr:1, ++ Reserved1:2, ++ FUA:1, ++ DPO:1, ++ LogicalUnitNumber:3; ++ unsigned char LogicalBlockAddress[4]; ++ unsigned char Reserved2; ++ unsigned char TransferLength[2]; ++ unsigned char Reserved3; ++ unsigned char Reserved4; ++ unsigned char Reserved5; ++} __attribute__((packed)) SCSI_WRITE_10_COMMAND; ++ ++/* ++ * VERIFY ++ */ ++ ++typedef struct{ ++ unsigned char OperationCode; ++ unsigned char RelAdr:1, ++ ByteChk:1, ++ Reserved1:1, ++ Reserved2:1, ++ DPO:1, ++ LogicalUnitNumber:3; ++ unsigned char LogicalBlockAddress[4]; ++ unsigned char Reserved3; ++ unsigned char VerificationLength[2]; ++ unsigned char Reserved4; ++ unsigned char Reserved5; ++ unsigned char Reserved6; ++} __attribute__((packed)) SCSI_VERIFY_COMMAND; ++ ++/****************************************************************************** ++** Global Function Prototype ++******************************************************************************/ ++ ++/* storage-fd.c */ ++void storage_urb_send(struct usb_device_instance*, void*, int); ++ ++/* storageproto.c */ ++void storageproto_urb_analysis(struct urb*); ++int storageproto_device_open_check(void); ++void storageproto_device_close(void); ++void storageproto_usb_status_check(int); ++void storageproto_media_status_check(int); ++void storageproto_usb_reset_ind(void); ++ssize_t storageproto_proc_read(struct file*, char*, size_t, loff_t* pos); ++void storageproto_init(void); ++void storageproto_exit(void); ++ ++#endif /* _STORAGEPROTO_H_ */ ++ |