diff -urN linux-2.4.18/CREDITS linux-2.4.18-mh9/CREDITS
--- linux-2.4.18/CREDITS Mon Feb 25 20:37:50 2002
+++ linux-2.4.18-mh9/CREDITS Mon Aug 25 18:38:09 2003
@@ -1317,6 +1317,14 @@
S: Provo, Utah 84606-5607
S: USA
+N: Marcel Holtmann
+E: marcel@holtmann.org
+W: http://www.holtmann.org
+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
diff -urN linux-2.4.18/Documentation/Configure.help linux-2.4.18-mh9/Documentation/Configure.help
--- linux-2.4.18/Documentation/Configure.help Mon Feb 25 20:37:51 2002
+++ linux-2.4.18-mh9/Documentation/Configure.help Mon Aug 25 18:38:10 2003
@@ -2824,14 +2824,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
@@ -11037,6 +11029,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
@@ -19870,11 +19868,15 @@
Bluetooth can be found at .
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
@@ -19882,7 +19884,7 @@
Bluetooth kernel modules are provided in the BlueZ package.
For more information, see .
- 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
@@ -19893,15 +19895,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 .
+
+ 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.
@@ -19911,13 +19989,90 @@
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_USB_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 USB zero packet support
+CONFIG_BLUEZ_USB_ZERO_PACKET
+ This option is provided only as a work around for buggy Bluetooth USB
+ devices. Do NOT enable it unless you know for sure that your device
+ requires zero packets.
+
+ Most people should say N here.
+
+HCI VHCI Virtual HCI device driver
CONFIG_BLUEZ_HCIVHCI
Bluetooth Virtual HCI device driver.
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_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 .
+
+ 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.
diff -urN linux-2.4.18/Documentation/firmware_class/README linux-2.4.18-mh9/Documentation/firmware_class/README
--- linux-2.4.18/Documentation/firmware_class/README Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/Documentation/firmware_class/README Mon Aug 25 18:38:10 2003
@@ -0,0 +1,58 @@
+
+ request_firmware() hotplug interface:
+ ------------------------------------
+ Copyright (C) 2003 Manuel Estrada Sainz
+
+ Why:
+ ---
+
+ Today, the most extended way to use firmware in the Linux kernel is linking
+ it statically in a header file. Which has political and technical issues:
+
+ 1) Some firmware is not legal to redistribute.
+ 2) The firmware occupies memory permanently, even though it often is just
+ used once.
+ 3) Some people, like the Debian crowd, don't consider some firmware free
+ enough and remove entire drivers (e.g.: keyspan).
+
+ about in-kernel persistence:
+ ---------------------------
+ Under some circumstances, as explained below, it would be interesting to keep
+ firmware images in non-swappable kernel memory or even in the kernel image
+ (probably within initramfs).
+
+ Note that this functionality has not been implemented.
+
+ - Why OPTIONAL in-kernel persistence may be a good idea sometimes:
+
+ - If the device that needs the firmware is needed to access the
+ filesystem. When upon some error the device has to be reset and the
+ firmware reloaded, it won't be possible to get it from userspace.
+ e.g.:
+ - A diskless client with a network card that needs firmware.
+ - The filesystem is stored in a disk behind an scsi device
+ that needs firmware.
+ - Replacing buggy DSDT/SSDT ACPI tables on boot.
+ Note: this would require the persistent objects to be included
+ within the kernel image, probably within initramfs.
+
+ And the same device can be needed to access the filesystem or not depending
+ on the setup, so I think that the choice on what firmware to make
+ persistent should be left to userspace.
+
+ - Why register_firmware()+__init can be useful:
+ - For boot devices needing firmware.
+ - To make the transition easier:
+ The firmware can be declared __init and register_firmware()
+ called on module_init. Then the firmware is warranted to be
+ there even if "firmware hotplug userspace" is not there yet or
+ it doesn't yet provide the needed firmware.
+ Once the firmware is widely available in userspace, it can be
+ removed from the kernel. Or made optional (CONFIG_.*_FIRMWARE).
+
+ In either case, if firmware hotplug support is there, it can move the
+ firmware out of kernel memory into the real filesystem for later
+ usage.
+
+ Note: If persistence is implemented on top of initramfs,
+ register_firmware() may not be appropriate.
diff -urN linux-2.4.18/Documentation/firmware_class/firmware_sample_driver.c linux-2.4.18-mh9/Documentation/firmware_class/firmware_sample_driver.c
--- linux-2.4.18/Documentation/firmware_class/firmware_sample_driver.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/Documentation/firmware_class/firmware_sample_driver.c Mon Aug 25 18:38:10 2003
@@ -0,0 +1,121 @@
+/*
+ * firmware_sample_driver.c -
+ *
+ * Copyright (c) 2003 Manuel Estrada Sainz
+ *
+ * Sample code on how to use request_firmware() from drivers.
+ *
+ * Note that register_firmware() is currently useless.
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include "linux/firmware.h"
+
+#define WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
+#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
+char __init inkernel_firmware[] = "let's say that this is firmware\n";
+#endif
+
+static char ghost_device[] = "ghost0";
+
+static void sample_firmware_load(char *firmware, int size)
+{
+ u8 buf[size+1];
+ memcpy(buf, firmware, size);
+ buf[size] = '\0';
+ printk("firmware_sample_driver: firmware: %s\n", buf);
+}
+
+static void sample_probe_default(void)
+{
+ /* uses the default method to get the firmware */
+ const struct firmware *fw_entry;
+ printk("firmware_sample_driver: a ghost device got inserted :)\n");
+
+ if(request_firmware(&fw_entry, "sample_driver_fw", ghost_device)!=0)
+ {
+ printk(KERN_ERR
+ "firmware_sample_driver: Firmware not available\n");
+ return;
+ }
+
+ sample_firmware_load(fw_entry->data, fw_entry->size);
+
+ release_firmware(fw_entry);
+
+ /* finish setting up the device */
+}
+static void sample_probe_specific(void)
+{
+ /* Uses some specific hotplug support to get the firmware from
+ * userspace directly into the hardware, or via some sysfs file */
+
+ /* NOTE: This currently doesn't work */
+
+ printk("firmware_sample_driver: a ghost device got inserted :)\n");
+
+ if(request_firmware(NULL, "sample_driver_fw", ghost_device)!=0)
+ {
+ printk(KERN_ERR
+ "firmware_sample_driver: Firmware load failed\n");
+ return;
+ }
+
+ /* request_firmware blocks until userspace finished, so at
+ * this point the firmware should be already in the device */
+
+ /* finish setting up the device */
+}
+static void sample_probe_async_cont(const struct firmware *fw, void *context)
+{
+ if(!fw){
+ printk(KERN_ERR
+ "firmware_sample_driver: firmware load failed\n");
+ return;
+ }
+
+ printk("firmware_sample_driver: device pointer \"%s\"\n",
+ (char *)context);
+ sample_firmware_load(fw->data, fw->size);
+}
+static void sample_probe_async(void)
+{
+ /* Let's say that I can't sleep */
+ int error;
+ error = request_firmware_nowait (THIS_MODULE,
+ "sample_driver_fw", ghost_device,
+ "my device pointer",
+ sample_probe_async_cont);
+ if(error){
+ printk(KERN_ERR
+ "firmware_sample_driver:"
+ " request_firmware_nowait failed\n");
+ }
+}
+
+static int sample_init(void)
+{
+#ifdef WE_CAN_NEED_FIRMWARE_BEFORE_USERSPACE_IS_AVAILABLE
+ register_firmware("sample_driver_fw", inkernel_firmware,
+ sizeof(inkernel_firmware));
+#endif
+ /* since there is no real hardware insertion I just call the
+ * sample probe functions here */
+ sample_probe_specific();
+ sample_probe_default();
+ sample_probe_async();
+ return 0;
+}
+static void __exit sample_exit(void)
+{
+}
+
+module_init (sample_init);
+module_exit (sample_exit);
+
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/Documentation/firmware_class/hotplug-script linux-2.4.18-mh9/Documentation/firmware_class/hotplug-script
--- linux-2.4.18/Documentation/firmware_class/hotplug-script Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/Documentation/firmware_class/hotplug-script Mon Aug 25 18:38:10 2003
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# Simple hotplug script sample:
+#
+# Both $DEVPATH and $FIRMWARE are already provided in the environment.
+
+HOTPLUG_FW_DIR=/usr/lib/hotplug/firmware/
+
+echo 1 > /sysfs/$DEVPATH/loading
+cat $HOTPLUG_FW_DIR/$FIRMWARE > /sysfs/$DEVPATH/data
+echo 0 > /sysfs/$DEVPATH/loading
+
+# To cancel the load in case of error:
+#
+# echo -1 > /sysfs/$DEVPATH/loading
+#
diff -urN linux-2.4.18/MAINTAINERS linux-2.4.18-mh9/MAINTAINERS
--- linux-2.4.18/MAINTAINERS Mon Feb 25 20:37:52 2002
+++ linux-2.4.18-mh9/MAINTAINERS Mon Aug 25 18:38:10 2003
@@ -252,7 +252,73 @@
L: linux-kernel@vger.kernel.org
S: Maintained
-BLUETOOTH SUBSYSTEM (BlueZ)
+BLUETOOTH SUBSYSTEM
+P: Maxim Krasnyansky
+M: maxk@qualcomm.com
+W: http://bluez.sf.net
+S: Maintained
+
+BLUETOOTH RFCOMM LAYER
+P: Maxim Krasnyansky
+M: maxk@qualcomm.com
+W: http://bluez.sf.net
+S: Maintained
+
+BLUETOOTH BNEP LAYER
+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: Maxim Krasnyansky
+M: maxk@qualcomm.com
+W: http://bluez.sf.net
+S: Maintained
+
+BLUETOOTH HCI UART DRIVER
+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
diff -urN linux-2.4.18/arch/alpha/config.in linux-2.4.18-mh9/arch/alpha/config.in
--- linux-2.4.18/arch/alpha/config.in Wed Nov 21 00:49:31 2001
+++ linux-2.4.18-mh9/arch/alpha/config.in Mon Aug 25 18:38:10 2003
@@ -371,9 +371,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'
diff -urN linux-2.4.18/arch/arm/config.in linux-2.4.18-mh9/arch/arm/config.in
--- linux-2.4.18/arch/arm/config.in Fri Nov 9 22:58:02 2001
+++ linux-2.4.18-mh9/arch/arm/config.in Mon Aug 25 18:38:10 2003
@@ -584,9 +584,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'
diff -urN linux-2.4.18/arch/i386/config.in linux-2.4.18-mh9/arch/i386/config.in
--- linux-2.4.18/arch/i386/config.in Mon Feb 25 20:37:52 2002
+++ linux-2.4.18-mh9/arch/i386/config.in Mon Aug 25 18:38:10 2003
@@ -407,9 +407,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'
diff -urN linux-2.4.18/arch/ppc/config.in linux-2.4.18-mh9/arch/ppc/config.in
--- linux-2.4.18/arch/ppc/config.in Mon Feb 25 20:37:55 2002
+++ linux-2.4.18-mh9/arch/ppc/config.in Mon Aug 25 18:38:10 2003
@@ -389,9 +389,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'
diff -urN linux-2.4.18/arch/sparc/config.in linux-2.4.18-mh9/arch/sparc/config.in
--- linux-2.4.18/arch/sparc/config.in Tue Jun 12 04:15:27 2001
+++ linux-2.4.18-mh9/arch/sparc/config.in Mon Aug 25 18:38:10 2003
@@ -251,9 +251,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'
diff -urN linux-2.4.18/arch/sparc64/config.in linux-2.4.18-mh9/arch/sparc64/config.in
--- linux-2.4.18/arch/sparc64/config.in Fri Dec 21 18:41:53 2001
+++ linux-2.4.18-mh9/arch/sparc64/config.in Mon Aug 25 18:38:10 2003
@@ -283,9 +283,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'
diff -urN linux-2.4.18/arch/sparc64/kernel/ioctl32.c linux-2.4.18-mh9/arch/sparc64/kernel/ioctl32.c
--- linux-2.4.18/arch/sparc64/kernel/ioctl32.c Mon Feb 25 20:37:56 2002
+++ linux-2.4.18-mh9/arch/sparc64/kernel/ioctl32.c Mon Aug 25 18:38:10 2003
@@ -92,6 +92,7 @@
#include
#include
+#include
#include
#include
@@ -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 */
diff -urN linux-2.4.18/drivers/bluetooth/Config.in linux-2.4.18-mh9/drivers/bluetooth/Config.in
--- linux-2.4.18/drivers/bluetooth/Config.in Fri Sep 7 18:28:38 2001
+++ linux-2.4.18-mh9/drivers/bluetooth/Config.in Mon Aug 25 18:38:10 2003
@@ -1,8 +1,34 @@
+#
+# 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_USB_SCO
+ bool ' USB zero packet support' CONFIG_BLUEZ_USB_ZERO_PACKET
+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
+
diff -urN linux-2.4.18/drivers/bluetooth/Makefile linux-2.4.18-mh9/drivers/bluetooth/Makefile
--- linux-2.4.18/drivers/bluetooth/Makefile Fri Sep 7 18:28:38 2001
+++ linux-2.4.18-mh9/drivers/bluetooth/Makefile Mon Aug 25 18:38:10 2003
@@ -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)
diff -urN linux-2.4.18/drivers/bluetooth/Makefile.lib linux-2.4.18-mh9/drivers/bluetooth/Makefile.lib
--- linux-2.4.18/drivers/bluetooth/Makefile.lib Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/Makefile.lib Mon Aug 25 18:38:10 2003
@@ -0,0 +1 @@
+obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o
diff -urN linux-2.4.18/drivers/bluetooth/bfusb.c linux-2.4.18-mh9/drivers/bluetooth/bfusb.c
--- linux-2.4.18/drivers/bluetooth/bfusb.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/bfusb.c Mon Aug 25 18:38:10 2003
@@ -0,0 +1,781 @@
+/*
+ *
+ * AVM BlueFRITZ! USB driver
+ *
+ * Copyright (C) 2003 Marcel Holtmann
+ *
+ *
+ * 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
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+#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 ");
+
+ 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 ");
+MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/drivers/bluetooth/bluecard_cs.c linux-2.4.18-mh9/drivers/bluetooth/bluecard_cs.c
--- linux-2.4.18/drivers/bluetooth/bluecard_cs.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/bluecard_cs.c Mon Aug 25 18:38:10 2003
@@ -0,0 +1,1113 @@
+/*
+ *
+ * Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann
+ *
+ *
+ * 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
+ * . Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+
+
+/* ======================== 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 ");
+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;
diff -urN linux-2.4.18/drivers/bluetooth/bt3c_cs.c linux-2.4.18-mh9/drivers/bluetooth/bt3c_cs.c
--- linux-2.4.18/drivers/bluetooth/bt3c_cs.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/bt3c_cs.c Mon Aug 25 18:38:10 2003
@@ -0,0 +1,946 @@
+/*
+ *
+ * Driver for the 3Com Bluetooth PCMCIA card
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann
+ * Jose Orlando Pereira
+ *
+ *
+ * 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
+ * . Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include
+#include
+
+#define __KERNEL_SYSCALLS__
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+
+
+/* ======================== 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 , Jose Orlando Pereira ");
+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;
diff -urN linux-2.4.18/drivers/bluetooth/btuart_cs.c linux-2.4.18-mh9/drivers/bluetooth/btuart_cs.c
--- linux-2.4.18/drivers/bluetooth/btuart_cs.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/btuart_cs.c Mon Aug 25 18:38:10 2003
@@ -0,0 +1,906 @@
+/*
+ *
+ * Driver for Bluetooth PCMCIA cards with HCI UART interface
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann
+ *
+ *
+ * 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
+ * . Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+
+
+/* ======================== 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 ");
+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;
diff -urN linux-2.4.18/drivers/bluetooth/dtl1_cs.c linux-2.4.18-mh9/drivers/bluetooth/dtl1_cs.c
--- linux-2.4.18/drivers/bluetooth/dtl1_cs.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/dtl1_cs.c Mon Aug 25 18:38:10 2003
@@ -0,0 +1,858 @@
+/*
+ *
+ * A driver for Nokia Connectivity Card DTL-1 devices
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann
+ *
+ *
+ * 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
+ * . Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+
+
+/* ======================== 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 ");
+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;
diff -urN linux-2.4.18/drivers/bluetooth/hci_bcsp.c linux-2.4.18-mh9/drivers/bluetooth/hci_bcsp.c
--- linux-2.4.18/drivers/bluetooth/hci_bcsp.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/hci_bcsp.c Mon Aug 25 18:38:10 2003
@@ -0,0 +1,710 @@
+/*
+ BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
+ Copyright 2002 by Fabrizio Gennari
+
+ Based on
+ hci_h4.c by Maxim Krasnyansky
+ ABCSP by Carl Orsborn
+
+ 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
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#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);
+}
diff -urN linux-2.4.18/drivers/bluetooth/hci_bcsp.h linux-2.4.18-mh9/drivers/bluetooth/hci_bcsp.h
--- linux-2.4.18/drivers/bluetooth/hci_bcsp.h Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/hci_bcsp.h Mon Aug 25 18:38:10 2003
@@ -0,0 +1,70 @@
+/*
+ BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
+ Copyright 2002 by Fabrizio Gennari
+
+ Based on
+ hci_h4.c by Maxim Krasnyansky
+ ABCSP by Carl Orsborn
+
+ 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__ */
diff -urN linux-2.4.18/drivers/bluetooth/hci_h4.c linux-2.4.18-mh9/drivers/bluetooth/hci_h4.c
--- linux-2.4.18/drivers/bluetooth/hci_h4.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/hci_h4.c Mon Aug 25 18:38:10 2003
@@ -0,0 +1,277 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky
+
+ 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
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#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);
+}
diff -urN linux-2.4.18/drivers/bluetooth/hci_h4.h linux-2.4.18-mh9/drivers/bluetooth/hci_h4.h
--- linux-2.4.18/drivers/bluetooth/hci_h4.h Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/hci_h4.h Mon Aug 25 18:38:10 2003
@@ -0,0 +1,44 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky
+
+ 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__ */
diff -urN linux-2.4.18/drivers/bluetooth/hci_ldisc.c linux-2.4.18-mh9/drivers/bluetooth/hci_ldisc.c
--- linux-2.4.18/drivers/bluetooth/hci_ldisc.c Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/hci_ldisc.c Mon Aug 25 18:38:10 2003
@@ -0,0 +1,580 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky
+
+ 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
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#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 ");
+
+ /* 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 ");
+MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
+MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/drivers/bluetooth/hci_uart.c linux-2.4.18-mh9/drivers/bluetooth/hci_uart.c
--- linux-2.4.18/drivers/bluetooth/hci_uart.c Fri Sep 7 18:28:38 2001
+++ linux-2.4.18-mh9/drivers/bluetooth/hci_uart.c Thu Jan 1 01:00:00 1970
@@ -1,580 +0,0 @@
-/*
- BlueZ - Bluetooth protocol stack for Linux
- Copyright (C) 2000-2001 Qualcomm Incorporated
-
- Written 2000,2001 by Maxim Krasnyansky
-
- 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
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-
-#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 ");
-
- /* 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 ");
-MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
-MODULE_LICENSE("GPL");
diff -urN linux-2.4.18/drivers/bluetooth/hci_uart.h linux-2.4.18-mh9/drivers/bluetooth/hci_uart.h
--- linux-2.4.18/drivers/bluetooth/hci_uart.h Thu Jan 1 01:00:00 1970
+++ linux-2.4.18-mh9/drivers/bluetooth/hci_uart.h Mon Aug 25 18:38:10 2003
@@ -0,0 +1,81 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky
+
+ 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__ */
diff -urN linux-2.4.18/drivers/bluetooth/hci_usb.c linux-2.4.18-mh9/drivers/bluetooth/hci_usb.c
--- linux-2.4.18/drivers/bluetooth/hci_usb.c Fri Sep 7 18:28:38 2001
+++ linux-2.4.18-mh9/drivers/bluetooth/hci_usb.c Mon Aug 25 18:38:12 2003
@@ -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
+ Copyright (C) 2003 Maxim Krasnyansky