summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid S. Miller <davem@nuts.ninka.net>2004-01-13 21:17:19 -0800
committerDavid S. Miller <davem@nuts.ninka.net>2004-01-13 21:17:19 -0800
commitfd5ef78663e840412189b16d53ff5a3fd8bc7f71 (patch)
tree2de76168a0534bf0e7eb12fcecf8d3b3ba91a5f9
parentaa0d8c971aec23f84a2845a8a22b0216ece44c2c (diff)
parent46fc74070ebdee51527a2ca20a1e3a1bf4a6bb5b (diff)
Merge nuts.ninka.net:/disk1/davem/BK/bluetooth-2.6
into nuts.ninka.net:/disk1/davem/BK/net-2.6
-rw-r--r--CREDITS2
-rw-r--r--MAINTAINERS29
-rw-r--r--drivers/bluetooth/Kconfig47
-rw-r--r--drivers/bluetooth/Makefile2
-rw-r--r--drivers/bluetooth/bcm203x.c309
-rw-r--r--drivers/bluetooth/bfusb.c782
-rw-r--r--drivers/bluetooth/bluecard_cs.c8
-rw-r--r--drivers/bluetooth/bt3c_cs.c6
-rw-r--r--drivers/bluetooth/btuart_cs.c6
-rw-r--r--drivers/bluetooth/dtl1_cs.c6
-rw-r--r--drivers/bluetooth/hci_usb.c62
-rw-r--r--drivers/bluetooth/hci_usb.h9
-rw-r--r--include/net/bluetooth/bluetooth.h3
-rw-r--r--include/net/bluetooth/hci.h10
-rw-r--r--include/net/bluetooth/hci_core.h14
-rw-r--r--include/net/bluetooth/rfcomm.h9
-rw-r--r--mm/mremap.c8
-rw-r--r--net/bluetooth/Kconfig3
-rw-r--r--net/bluetooth/Makefile1
-rw-r--r--net/bluetooth/af_bluetooth.c2
-rw-r--r--net/bluetooth/bnep/core.c1
-rw-r--r--net/bluetooth/cmtp/Kconfig11
-rw-r--r--net/bluetooth/cmtp/Makefile7
-rw-r--r--net/bluetooth/cmtp/capi.c630
-rw-r--r--net/bluetooth/cmtp/cmtp.h136
-rw-r--r--net/bluetooth/cmtp/core.c507
-rw-r--r--net/bluetooth/cmtp/sock.c199
-rw-r--r--net/bluetooth/hci_conn.c2
-rw-r--r--net/bluetooth/hci_core.c1
-rw-r--r--net/bluetooth/hci_event.c27
-rw-r--r--net/bluetooth/hci_sock.c12
-rw-r--r--net/bluetooth/l2cap.c1
-rw-r--r--net/bluetooth/rfcomm/core.c63
-rw-r--r--net/bluetooth/sco.c1
34 files changed, 2817 insertions, 99 deletions
diff --git a/CREDITS b/CREDITS
index 9d04104c1e1c..13e01cb8138e 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1394,7 +1394,9 @@ S: USA
N: Marcel Holtmann
E: marcel@holtmann.org
W: http://www.holtmann.org
+D: Maintainer of the Linux Bluetooth Subsystem
D: Author and maintainer of the various Bluetooth HCI drivers
+D: Author and maintainer of the CAPI message transport protocol driver
D: Various other Bluetooth related patches, cleanups and fixes
S: Germany
diff --git a/MAINTAINERS b/MAINTAINERS
index 69753c92f331..8e94a100dc70 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -338,35 +338,64 @@ L: linux-kernel@vger.kernel.org
S: Maintained
BLUETOOTH SUBSYSTEM
+P: Marcel Holtmann
+M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
+L: bluez-devel@lists.sf.net
W: http://bluez.sf.net
S: Maintained
BLUETOOTH RFCOMM LAYER
+P: Marcel Holtmann
+M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
W: http://bluez.sf.net
S: Maintained
BLUETOOTH BNEP LAYER
+P: Marcel Holtmann
+M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
W: http://bluez.sf.net
S: Maintained
+BLUETOOTH CMTP LAYER
+P: Marcel Holtmann
+M: marcel@holtmann.org
+W: http://www.holtmann.org/linux/bluetooth/
+S: Maintained
+
BLUETOOTH HCI USB DRIVER
+P: Marcel Holtmann
+M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
W: http://bluez.sf.net
S: Maintained
BLUETOOTH HCI UART DRIVER
+P: Marcel Holtmann
+M: marcel@holtmann.org
P: Maxim Krasnyansky
M: maxk@qualcomm.com
W: http://bluez.sf.net
S: Maintained
+BLUETOOTH HCI BCM203X DRIVER
+P: Marcel Holtmann
+M: marcel@holtmann.org
+W: http://www.holtmann.org/linux/bluetooth/
+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
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 30d099c33aee..5ad7ea2b6bc6 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -13,22 +13,14 @@ config BT_HCIUSB
Say Y here to compile support for Bluetooth USB devices into the
kernel or say M to compile it as module (hci_usb).
-config BT_USB_SCO
- bool "SCO over HCI USB support"
+config BT_HCIUSB_SCO
+ bool "SCO (voice) support"
depends on BT_HCIUSB
help
This option enables the SCO support in the HCI USB driver. You need this
- to transmit voice data with your Bluetooth USB device.
- Say Y here to compile support for SCO over HCI USB.
+ to transmit voice data with your Bluetooth USB device.
-config BT_USB_ZERO_PACKET
- bool "USB zero packet support"
- depends on BT_HCIUSB
- help
- 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.
+ Say Y here to compile support for SCO over HCI USB.
config BT_HCIUART
tristate "HCI UART driver"
@@ -65,13 +57,38 @@ config BT_HCIUART_BCSP
Say Y here to compile support for HCI BCSP protocol.
config BT_HCIUART_BCSP_TXCRC
- bool "Transmit CRC with every BCSP packet"
- depends on BT_HCIUART_BCSP
- help
+ bool "Transmit CRC with every BCSP packet"
+ depends on BT_HCIUART_BCSP
+ help
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.
+config BT_HCIBCM203X
+ tristate "HCI BCM203x USB driver"
+ depends on USB && BT
+ select FW_LOADER
+ help
+ Bluetooth HCI BCM203x USB driver.
+ This driver provides the firmware loading mechanism for the Broadcom
+ Blutonium based devices.
+
+ Say Y here to compile support for HCI BCM203x devices into the
+ kernel or say M to compile it as module (bcm203x).
+
+config BT_HCIBFUSB
+ tristate "HCI BlueFRITZ! USB driver"
+ depends on USB && BT
+ select FW_LOADER
+ help
+ 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).
+
config BT_HCIDTL1
tristate "HCI DTL1 (PC Card) driver"
depends on PCMCIA && BT
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 8a6af91bb52f..ec5345fac3a3 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -5,6 +5,8 @@
obj-$(CONFIG_BT_HCIUSB) += hci_usb.o
obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o
obj-$(CONFIG_BT_HCIUART) += hci_uart.o
+obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o
+obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o
obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o
obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
new file mode 100644
index 000000000000..12e44029a38a
--- /dev/null
+++ b/drivers/bluetooth/bcm203x.c
@@ -0,0 +1,309 @@
+/*
+ *
+ * Broadcom Blutonium firmware driver
+ *
+ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#ifndef CONFIG_BT_HCIBCM203X_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.0"
+
+static struct usb_device_id bcm203x_table[] = {
+ /* Broadcom Blutonium (BCM2033) */
+ { USB_DEVICE(0x0a5c, 0x2033) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bcm203x_table);
+
+
+#define BCM203X_ERROR 0
+#define BCM203X_RESET 1
+#define BCM203X_LOAD_MINIDRV 2
+#define BCM203X_SELECT_MEMORY 3
+#define BCM203X_CHECK_MEMORY 4
+#define BCM203X_LOAD_FIRMWARE 5
+#define BCM203X_CHECK_FIRMWARE 6
+
+#define BCM203X_IN_EP 0x81
+#define BCM203X_OUT_EP 0x02
+
+struct bcm203x_data {
+ struct usb_device *udev;
+
+ unsigned long state;
+
+ struct timer_list timer;
+
+ struct urb *urb;
+ unsigned char buffer[4096];
+
+ unsigned char *fw_data;
+ unsigned int fw_size;
+ unsigned int fw_sent;
+};
+
+static void bcm203x_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct bcm203x_data *data = urb->context;
+ struct usb_device *udev = urb->dev;
+ int len;
+
+ BT_DBG("udev %p urb %p", udev, urb);
+
+ if (urb->status) {
+ BT_ERR("URB failed with status %d", urb->status);
+ data->state = BCM203X_ERROR;
+ return;
+ }
+
+ switch (data->state) {
+ case BCM203X_LOAD_MINIDRV:
+ memcpy(data->buffer, "#", 1);
+
+ usb_fill_bulk_urb(urb, udev,
+ usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->buffer, 1, bcm203x_complete, data);
+
+ data->state = BCM203X_SELECT_MEMORY;
+
+ mod_timer(&data->timer, jiffies + (HZ / 10));
+ break;
+
+ case BCM203X_SELECT_MEMORY:
+ usb_fill_int_urb(urb, udev,
+ usb_rcvintpipe(udev, BCM203X_IN_EP),
+ data->buffer, 32, bcm203x_complete, data, 1);
+
+ data->state = BCM203X_CHECK_MEMORY;
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+ break;
+
+ case BCM203X_CHECK_MEMORY:
+ if (data->buffer[0] != '#') {
+ BT_ERR("Memory select failed");
+ data->state = BCM203X_ERROR;
+ break;
+ }
+
+ data->state = BCM203X_LOAD_FIRMWARE;
+
+ case BCM203X_LOAD_FIRMWARE:
+ if (data->fw_sent == data->fw_size) {
+ usb_fill_int_urb(urb, udev,
+ usb_rcvintpipe(udev, BCM203X_IN_EP),
+ data->buffer, 32,
+ bcm203x_complete, data, 1);
+
+ data->state = BCM203X_CHECK_FIRMWARE;
+ } else {
+ len = min_t(uint, data->fw_size - data->fw_sent,
+ sizeof(data->buffer));
+
+ usb_fill_bulk_urb(urb, udev,
+ usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->fw_data + data->fw_sent, len,
+ bcm203x_complete, data);
+
+ data->fw_sent += len;
+ }
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+ break;
+
+ case BCM203X_CHECK_FIRMWARE:
+ if (data->buffer[0] != '.') {
+ BT_ERR("Firmware loading failed");
+ data->state = BCM203X_ERROR;
+ break;
+ }
+
+ data->state = BCM203X_RESET;
+ break;
+ }
+}
+
+static void bcm203x_timer(unsigned long user_data)
+{
+ struct bcm203x_data *data = (struct bcm203x_data *) user_data;
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+}
+
+static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ const struct firmware *firmware;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct bcm203x_data *data;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+ if (intf->altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ BT_ERR("Can't allocate memory for data structure");
+ return -ENOMEM;
+ }
+
+ memset(data, 0, sizeof(*data));
+
+ data->udev = udev;
+ data->state = BCM203X_LOAD_MINIDRV;
+
+ data->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!data->urb) {
+ BT_ERR("Can't allocate URB");
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
+ BT_ERR("Mini driver request failed");
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -EIO;
+ }
+
+ BT_DBG("minidrv data %p size %d", firmware->data, firmware->size);
+
+ if (firmware->size > sizeof(data->buffer)) {
+ BT_ERR("Mini driver exceeds size of buffer");
+ release_firmware(firmware);
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -EIO;
+ }
+
+ memcpy(data->buffer, firmware->data, firmware->size);
+
+ usb_fill_bulk_urb(data->urb, udev,
+ usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->buffer, firmware->size, bcm203x_complete, data);
+
+ release_firmware(firmware);
+
+ if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) {
+ BT_ERR("Firmware request failed");
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -EIO;
+ }
+
+ BT_DBG("firmware data %p size %d", firmware->data, firmware->size);
+
+ data->fw_data = kmalloc(firmware->size, GFP_KERNEL);
+ if (!data->fw_data) {
+ BT_ERR("Can't allocate memory for firmware image");
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ memcpy(data->fw_data, firmware->data, firmware->size);
+ data->fw_size = firmware->size;
+ data->fw_sent = 0;
+
+ release_firmware(firmware);
+
+ init_timer(&data->timer);
+ data->timer.function = bcm203x_timer;
+ data->timer.data = (unsigned long) data;
+
+ usb_set_intfdata(intf, data);
+
+ mod_timer(&data->timer, jiffies + HZ);
+
+ return 0;
+}
+
+static void bcm203x_disconnect(struct usb_interface *intf)
+{
+ struct bcm203x_data *data = usb_get_intfdata(intf);
+
+ BT_DBG("intf %p", intf);
+
+ usb_unlink_urb(data->urb);
+
+ usb_set_intfdata(intf, NULL);
+
+ usb_free_urb(data->urb);
+ kfree(data->fw_data);
+ kfree(data);
+}
+
+static struct usb_driver bcm203x_driver = {
+ .owner = THIS_MODULE,
+ .name = "bcm203x",
+ .probe = bcm203x_probe,
+ .disconnect = bcm203x_disconnect,
+ .id_table = bcm203x_table,
+};
+
+static int __init bcm203x_init(void)
+{
+ int err;
+
+ BT_INFO("Broadcom Blutonium firmware driver ver %s", VERSION);
+
+ err = usb_register(&bcm203x_driver);
+ if (err < 0)
+ BT_ERR("Failed to register USB driver");
+
+ return err;
+}
+
+static void __exit bcm203x_cleanup(void)
+{
+ usb_deregister(&bcm203x_driver);
+}
+
+module_init(bcm203x_init);
+module_exit(bcm203x_cleanup);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
new file mode 100644
index 000000000000..0ff501d08a78
--- /dev/null
+++ b/drivers/bluetooth/bfusb.c
@@ -0,0 +1,782 @@
+/*
+ *
+ * AVM BlueFRITZ! USB driver
+ *
+ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef CONFIG_BT_HCIBFUSB_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.1"
+
+static struct usb_driver bfusb_driver;
+
+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 2
+#define BFUSB_MAX_BULK_RX 2
+
+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, struct pt_regs *regs);
+static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs);
+
+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, GFP_ATOMIC)))
+ return -ENOMEM;
+
+ pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+
+ usb_fill_bulk_urb(urb, bfusb->udev, pipe, skb->data, skb->len,
+ bfusb_tx_complete, skb);
+
+ scb->urb = urb;
+
+ skb_queue_tail(&bfusb->pending_q, skb);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ 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 pt_regs *regs)
+{
+ 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, GFP_ATOMIC)))
+ return -ENOMEM;
+
+ if (!(skb = bt_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);
+
+ usb_fill_bulk_urb(urb, bfusb->udev, pipe, skb->data, size,
+ bfusb_rx_complete, skb);
+
+ skb_queue_tail(&bfusb->pending_q, skb);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ 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) {
+ struct hci_event_hdr *hdr = (struct 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) {
+ struct hci_acl_hdr *hdr = (struct 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) {
+ struct hci_sco_hdr *hdr = (struct 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 = bt_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 pt_regs *regs)
+{
+ 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, GFP_ATOMIC);
+ 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;
+
+ 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);
+ }
+
+ 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);
+ write_unlock_irqrestore(&bfusb->lock, flags);
+
+ bfusb_unlink_urbs(bfusb);
+ bfusb_flush(hdev);
+
+ 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 = bt_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", bfusb, bfusb->udev);
+
+ BT_INFO("BlueFRITZ! USB loading firmware");
+
+ pipe = usb_sndctrlpipe(bfusb->udev, 0);
+
+ if (usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 1, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT) < 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;
+ }
+
+ pipe = usb_sndctrlpipe(bfusb->udev, 0);
+
+ if ((err = usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 2, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 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, HZ * USB_CTRL_SET_TIMEOUT);
+
+ return err;
+}
+
+static int bfusb_probe(struct usb_interface *iface, const struct usb_device_id *id)
+{
+ const struct firmware *firmware;
+ struct usb_device *udev = interface_to_usbdev(iface);
+ struct usb_host_endpoint *bulk_out_ep;
+ struct usb_host_endpoint *bulk_in_ep;
+ struct hci_dev *hdev;
+ struct bfusb *bfusb;
+
+ BT_DBG("iface %p id %p", iface, id);
+
+ /* Check number of endpoints */
+ if (iface->altsetting[0].desc.bNumEndpoints < 2)
+ return -EIO;
+
+ bulk_out_ep = &iface->altsetting[0].endpoint[0];
+ bulk_in_ep = &iface->altsetting[0].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->desc.bEndpointAddress;
+ bfusb->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress;
+ bfusb->bulk_pkt_size = bulk_out_ep->desc.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);
+
+ if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) {
+ BT_ERR("Firmware request failed");
+ goto error;
+ }
+
+ BT_DBG("firmware data %p size %d", firmware->data, firmware->size);
+
+ 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;
+
+ hdev->owner = THIS_MODULE;
+
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ goto error;
+ }
+
+ usb_set_intfdata(iface, bfusb);
+
+ return 0;
+
+release:
+ release_firmware(firmware);
+
+error:
+ kfree(bfusb);
+
+done:
+ return -EIO;
+}
+
+static void bfusb_disconnect(struct usb_interface *iface)
+{
+ struct bfusb *bfusb = usb_get_intfdata(iface);
+ struct hci_dev *hdev = &bfusb->hdev;
+
+ BT_DBG("iface %p", iface);
+
+ if (!hdev)
+ return;
+
+ usb_set_intfdata(iface, NULL);
+
+ 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 = {
+ .owner = THIS_MODULE,
+ .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);
+
+ if ((err = usb_register(&bfusb_driver)) < 0)
+ BT_ERR("Failed to register BlueFRITZ! USB driver");
+
+ return err;
+}
+
+static void __exit bfusb_cleanup(void)
+{
+ usb_deregister(&bfusb_driver);
+}
+
+module_init(bfusb_init);
+module_exit(bfusb_cleanup);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index fee12e3828cf..6de94257b12a 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -499,7 +499,7 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
}
-void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+static irqreturn_t bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
{
bluecard_info_t *info = dev_inst;
unsigned int iobase;
@@ -507,11 +507,11 @@ void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
if (!info) {
printk(KERN_WARNING "bluecard_cs: Call of irq %d for unknown device.\n", irq);
- return;
+ return IRQ_NONE;
}
if (!test_bit(CARD_READY, &(info->hw_state)))
- return;
+ return IRQ_NONE;
iobase = info->link.io.BasePort1;
@@ -556,6 +556,8 @@ void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
outb(info->ctrl_reg, iobase + REG_CONTROL);
spin_unlock(&(info->lock));
+
+ return IRQ_HANDLED;
}
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index b5a8135f9808..df16aad65de1 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -355,7 +355,7 @@ static void bt3c_receive(bt3c_info_t *info)
}
-void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+static irqreturn_t bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
{
bt3c_info_t *info = dev_inst;
unsigned int iobase;
@@ -363,7 +363,7 @@ void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
if (!info) {
printk(KERN_WARNING "bt3c_cs: Call of irq %d for unknown device.\n", irq);
- return;
+ return IRQ_NONE;
}
iobase = info->link.io.BasePort1;
@@ -396,6 +396,8 @@ void bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
}
spin_unlock(&(info->lock));
+
+ return IRQ_HANDLED;
}
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index 8fb7b29fdc3d..337230b613e2 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -301,7 +301,7 @@ static void btuart_receive(btuart_info_t *info)
}
-void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+static irqreturn_t btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
{
btuart_info_t *info = dev_inst;
unsigned int iobase;
@@ -310,7 +310,7 @@ void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
if (!info) {
printk(KERN_WARNING "btuart_cs: Call of irq %d for unknown device.\n", irq);
- return;
+ return IRQ_NONE;
}
iobase = info->link.io.BasePort1;
@@ -351,6 +351,8 @@ void btuart_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
}
spin_unlock(&(info->lock));
+
+ return IRQ_HANDLED;
}
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index 96bfe85fdfec..74d9cf9e352d 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -304,7 +304,7 @@ static void dtl1_receive(dtl1_info_t *info)
}
-void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+static irqreturn_t dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
{
dtl1_info_t *info = dev_inst;
unsigned int iobase;
@@ -314,7 +314,7 @@ void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
if (!info) {
printk(KERN_WARNING "dtl1_cs: Call of irq %d for unknown device.\n", irq);
- return;
+ return IRQ_NONE;
}
iobase = info->link.io.BasePort1;
@@ -363,6 +363,8 @@ void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
}
spin_unlock(&(info->lock));
+
+ return IRQ_HANDLED;
}
diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c
index 3bef9b14ad7d..14b8350652e4 100644
--- a/drivers/bluetooth/hci_usb.c
+++ b/drivers/bluetooth/hci_usb.c
@@ -62,7 +62,7 @@
#define BT_DMP( A... )
#endif
-#ifndef CONFIG_BT_USB_ZERO_PACKET
+#ifndef CONFIG_BT_HCIUSB_ZERO_PACKET
#undef URB_ZERO_PACKET
#define URB_ZERO_PACKET 0
#endif
@@ -70,12 +70,21 @@
static struct usb_driver hci_usb_driver;
static struct usb_device_id bluetooth_ids[] = {
+ /* Broadcom BCM2033 without firmware */
+ { USB_DEVICE(0x0a5c, 0x2033), driver_info: HCI_IGNORE },
+
+ /* Digianswer device */
+ { USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER },
+
/* Generic Bluetooth USB device */
{ USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
/* Ericsson with non-standard id */
{ USB_DEVICE(0x0bdb, 0x1002) },
+ /* ALPS Module with non-standard id */
+ { USB_DEVICE(0x044e, 0x3002) },
+
/* Bluetooth Ultraport Module from IBM */
{ USB_DEVICE(0x04bf, 0x030a) },
@@ -84,13 +93,6 @@ static struct usb_device_id bluetooth_ids[] = {
MODULE_DEVICE_TABLE (usb, bluetooth_ids);
-static struct usb_device_id ignore_ids[] = {
- /* Broadcom BCM2033 without firmware */
- { USB_DEVICE(0x0a5c, 0x2033) },
-
- { } /* Terminating entry */
-};
-
struct _urb *_urb_alloc(int isoc, int gfp)
{
struct _urb *_urb = kmalloc(sizeof(struct _urb) +
@@ -134,7 +136,7 @@ static inline struct _urb *__get_completed(struct hci_usb *husb, int type)
return _urb_dequeue(__completed_q(husb, type));
}
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
{
int offset = 0, i;
@@ -232,7 +234,7 @@ static int hci_usb_bulk_rx_submit(struct hci_usb *husb)
return err;
}
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
{
struct _urb *_urb;
@@ -301,9 +303,10 @@ static int hci_usb_open(struct hci_dev *hdev)
for (i = 0; i < HCI_MAX_BULK_RX; i++)
hci_usb_bulk_rx_submit(husb);
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
if (husb->isoc_iface)
- hci_usb_isoc_rx_submit(husb);
+ for (i = 0; i < HCI_MAX_ISOC_RX; i++)
+ hci_usb_isoc_rx_submit(husb);
#endif
} else {
clear_bit(HCI_RUNNING, &hdev->flags);
@@ -425,7 +428,7 @@ static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb)
} else
dr = (void *) _urb->urb.setup_packet;
- dr->bRequestType = HCI_CTRL_REQ;
+ dr->bRequestType = husb->ctrl_req;
dr->bRequest = 0;
dr->wIndex = 0;
dr->wValue = 0;
@@ -466,7 +469,7 @@ static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb)
return __tx_submit(husb, _urb);
}
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
{
struct _urb *_urb = __get_completed(husb, skb->pkt_type);
@@ -517,10 +520,10 @@ static void hci_usb_tx_process(struct hci_usb *husb)
skb_queue_head(q, skb);
}
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
/* Process SCO queue */
q = __transmit_q(husb, HCI_SCODATA_PKT);
- if (!atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) &&
+ if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX &&
(skb = skb_dequeue(q))) {
if (hci_usb_send_isoc(husb, skb) < 0)
skb_queue_head(q, skb);
@@ -576,7 +579,7 @@ static int hci_usb_send_frame(struct sk_buff *skb)
hdev->stat.acl_tx++;
break;
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
@@ -626,7 +629,7 @@ static inline int __recv_frame(struct hci_usb *husb, int type, void *data, int c
} else
return -EILSEQ;
break;
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
case HCI_SCODATA_PKT:
if (count >= HCI_SCO_HDR_SIZE) {
struct hci_sco_hdr *h = data;
@@ -691,7 +694,7 @@ static void hci_usb_rx_complete(struct urb *urb, struct pt_regs *regs)
goto resubmit;
if (_urb->type == HCI_SCODATA_PKT) {
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
int i;
for (i=0; i < urb->number_of_packets; i++) {
BT_DBG("desc %d status %d offset %d len %d", i,
@@ -785,9 +788,11 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
iface = udev->actconfig->interface[0];
- /* Check our black list */
- if (usb_match_id(intf, ignore_ids))
- return -EIO;
+ if (id->driver_info & HCI_IGNORE)
+ return -ENODEV;
+
+ if (intf->altsetting->desc.bInterfaceNumber > 0)
+ return -ENODEV;
/* Check number of endpoints */
if (intf->altsetting[0].desc.bNumEndpoints < 3)
@@ -826,9 +831,9 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
bulk_out_ep[i] = ep;
break;
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
case USB_ENDPOINT_XFER_ISOC:
- if (ep->desc.wMaxPacketSize < size)
+ if (ep->desc.wMaxPacketSize < size || a > 2)
break;
size = ep->desc.wMaxPacketSize;
@@ -852,7 +857,7 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
goto done;
}
-#ifdef CONFIG_BT_USB_SCO
+#ifdef CONFIG_BT_HCIUSB_SCO
if (!isoc_in_ep[1] || !isoc_out_ep[1]) {
BT_DBG("Isoc endpoints not found");
isoc_iface = NULL;
@@ -871,7 +876,12 @@ int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
husb->bulk_in_ep = bulk_in_ep[0];
husb->intr_in_ep = intr_in_ep[0];
-#ifdef CONFIG_BT_USB_SCO
+ if (id->driver_info & HCI_DIGIANSWER)
+ husb->ctrl_req = HCI_DIGI_REQ;
+ else
+ husb->ctrl_req = HCI_CTRL_REQ;
+
+#ifdef CONFIG_BT_HCIUSB_SCO
if (isoc_iface) {
BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);
if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
diff --git a/drivers/bluetooth/hci_usb.h b/drivers/bluetooth/hci_usb.h
index 1e7306286e56..e2ba50da3021 100644
--- a/drivers/bluetooth/hci_usb.h
+++ b/drivers/bluetooth/hci_usb.h
@@ -35,12 +35,19 @@
#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
#define HCI_CTRL_REQ 0x20
+#define HCI_DIGI_REQ 0x40
+
+#define HCI_IGNORE 0x01
+#define HCI_DIGIANSWER 0x02
#define HCI_MAX_IFACE_NUM 3
#define HCI_MAX_BULK_TX 4
#define HCI_MAX_BULK_RX 1
+#define HCI_MAX_ISOC_RX 2
+#define HCI_MAX_ISOC_TX 2
+
#define HCI_MAX_ISOC_FRAMES 10
struct _urb_queue {
@@ -119,6 +126,8 @@ struct hci_usb {
struct usb_host_endpoint *isoc_out_ep;
struct usb_host_endpoint *isoc_in_ep;
+ __u8 ctrl_req;
+
struct sk_buff_head transmit_q[4];
struct sk_buff *reassembly[4]; // Reassembly buffers
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 429847a82f5f..99301bd8096a 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -47,7 +47,8 @@
#define BTPROTO_HCI 1
#define BTPROTO_SCO 2
#define BTPROTO_RFCOMM 3
-#define BTPROTO_BNEP 4
+#define BTPROTO_BNEP 4
+#define BTPROTO_CMTP 5
#define SOL_HCI 0
#define SOL_L2CAP 6
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 228a44322ee0..59523c39d6fb 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -408,6 +408,16 @@ struct inquiry_info {
__u16 clock_offset;
} __attribute__ ((packed));
+#define HCI_EV_INQUIRY_RESULT_WITH_RSSI 0x22
+struct inquiry_info_with_rssi {
+ bdaddr_t bdaddr;
+ __u8 pscan_rep_mode;
+ __u8 pscan_period_mode;
+ __u8 dev_class[3];
+ __u16 clock_offset;
+ __u8 rssi;
+} __attribute__ ((packed));
+
#define HCI_EV_CONN_COMPLETE 0x03
struct hci_ev_conn_complete {
__u8 status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index fd010a9dc75e..6a429873df9c 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -176,6 +176,12 @@ static inline void inquiry_cache_init(struct hci_dev *hdev)
c->list = NULL;
}
+static inline int inquiry_cache_empty(struct hci_dev *hdev)
+{
+ struct inquiry_cache *c = &hdev->inq_cache;
+ return (c->list == NULL);
+}
+
static inline long inquiry_cache_age(struct hci_dev *hdev)
{
struct inquiry_cache *c = &hdev->inq_cache;
@@ -281,10 +287,12 @@ static inline void hci_conn_hold(struct hci_conn *conn)
static inline void hci_conn_put(struct hci_conn *conn)
{
if (atomic_dec_and_test(&conn->refcnt)) {
- if (conn->type == SCO_LINK)
+ if (conn->type == ACL_LINK) {
+ unsigned long timeo = (conn->out) ?
+ HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2;
+ hci_conn_set_timer(conn, timeo);
+ } else
hci_conn_set_timer(conn, HZ / 100);
- else if (conn->out)
- hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT);
}
}
diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h
index 7d410fed2520..e022bfbb5166 100644
--- a/include/net/bluetooth/rfcomm.h
+++ b/include/net/bluetooth/rfcomm.h
@@ -167,8 +167,8 @@ struct rfcomm_session {
int initiator;
/* Default DLC parameters */
+ int cfc;
uint mtu;
- uint credits;
struct list_head dlcs;
};
@@ -190,7 +190,7 @@ struct rfcomm_dlc {
u8 mscex;
uint mtu;
- uint credits;
+ uint cfc;
uint rx_credits;
uint tx_credits;
@@ -219,6 +219,11 @@ struct rfcomm_dlc {
#define RFCOMM_MSCEX_RX 2
#define RFCOMM_MSCEX_OK (RFCOMM_MSCEX_TX + RFCOMM_MSCEX_RX)
+/* CFC states */
+#define RFCOMM_CFC_UNKNOWN -1
+#define RFCOMM_CFC_DISABLED 0
+#define RFCOMM_CFC_ENABLED RFCOMM_MAX_CREDITS
+
extern struct task_struct *rfcomm_thread;
extern unsigned long rfcomm_event;
diff --git a/mm/mremap.c b/mm/mremap.c
index e308e6a58639..9c0850a56800 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -315,8 +315,12 @@ unsigned long do_mremap(unsigned long addr,
old_len = PAGE_ALIGN(old_len);
new_len = PAGE_ALIGN(new_len);
- /* Don't allow the degenerate cases */
- if (!old_len || !new_len)
+ /*
+ * We allow a zero old-len as a special case
+ * for DOS-emu "duplicate shm area" thing. But
+ * a zero new-len is nonsensical.
+ */
+ if (!new_len)
goto out;
/* new_addr is only valid if MREMAP_FIXED is specified */
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index a152eec283cb..4ba9e9500fee 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -21,6 +21,7 @@ config BT
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 Bluetooth Core
layer.
@@ -57,6 +58,8 @@ source "net/bluetooth/rfcomm/Kconfig"
source "net/bluetooth/bnep/Kconfig"
+source "net/bluetooth/cmtp/Kconfig"
+
source "drivers/bluetooth/Kconfig"
endmenu
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index cf5916af802a..5c4d3ab34849 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_BT_L2CAP) += l2cap.o
obj-$(CONFIG_BT_SCO) += sco.o
obj-$(CONFIG_BT_RFCOMM) += rfcomm/
obj-$(CONFIG_BT_BNEP) += bnep/
+obj-$(CONFIG_BT_CMTP) += cmtp/
bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_proc.o lib.o syms.o
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index b9b3bbf0a5e2..c17195bbcf8a 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -59,7 +59,7 @@
struct proc_dir_entry *proc_bt;
/* Bluetooth sockets */
-#define BT_MAX_PROTO 5
+#define BT_MAX_PROTO 6
static struct net_proto_family *bt_proto[BT_MAX_PROTO];
static kmem_cache_t *bt_sock_cache;
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index efa44e162233..ece601f8df1d 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -707,3 +707,4 @@ module_exit(bnep_cleanup_module);
MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
MODULE_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyanskiy <maxk@qualcomm.com>");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("bt-proto-4");
diff --git a/net/bluetooth/cmtp/Kconfig b/net/bluetooth/cmtp/Kconfig
new file mode 100644
index 000000000000..e832f20ceb14
--- /dev/null
+++ b/net/bluetooth/cmtp/Kconfig
@@ -0,0 +1,11 @@
+config BT_CMTP
+ tristate "CMTP protocol support"
+ depends on BT && BT_L2CAP && ISDN_CAPI
+ help
+ 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).
+
diff --git a/net/bluetooth/cmtp/Makefile b/net/bluetooth/cmtp/Makefile
new file mode 100644
index 000000000000..890a9a5a6861
--- /dev/null
+++ b/net/bluetooth/cmtp/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux Bluetooth CMTP layer
+#
+
+obj-$(CONFIG_BT_CMTP) += cmtp.o
+
+cmtp-objs := core.o sock.o capi.o
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
new file mode 100644
index 000000000000..7e03820c2bfb
--- /dev/null
+++ b/net/bluetooth/cmtp/capi.c
@@ -0,0 +1,630 @@
+/*
+ CMTP implementation for Linux Bluetooth stack (BlueZ).
+ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <net/sock.h>
+
+#include <linux/isdn/capilli.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+
+#include "cmtp.h"
+
+#ifndef CONFIG_BT_CMTP_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define CAPI_INTEROPERABILITY 0x20
+
+#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
+#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
+#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
+#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
+
+#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2)
+#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4)
+#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2)
+#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2)
+
+#define CAPI_FUNCTION_REGISTER 0
+#define CAPI_FUNCTION_RELEASE 1
+#define CAPI_FUNCTION_GET_PROFILE 2
+#define CAPI_FUNCTION_GET_MANUFACTURER 3
+#define CAPI_FUNCTION_GET_VERSION 4
+#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
+#define CAPI_FUNCTION_MANUFACTURER 6
+#define CAPI_FUNCTION_LOOPBACK 7
+
+
+#define CMTP_MSGNUM 1
+#define CMTP_APPLID 2
+#define CMTP_MAPPING 3
+
+static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
+{
+ struct cmtp_application *app = kmalloc(sizeof(*app), GFP_KERNEL);
+
+ BT_DBG("session %p application %p appl %d", session, app, appl);
+
+ if (!app)
+ return NULL;
+
+ memset(app, 0, sizeof(*app));
+
+ app->state = BT_OPEN;
+ app->appl = appl;
+
+ list_add_tail(&app->list, &session->applications);
+
+ return app;
+}
+
+static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
+{
+ BT_DBG("session %p application %p", session, app);
+
+ if (app) {
+ list_del(&app->list);
+ kfree(app);
+ }
+}
+
+static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
+{
+ struct cmtp_application *app;
+ struct list_head *p, *n;
+
+ list_for_each_safe(p, n, &session->applications) {
+ app = list_entry(p, struct cmtp_application, list);
+ switch (pattern) {
+ case CMTP_MSGNUM:
+ if (app->msgnum == value)
+ return app;
+ break;
+ case CMTP_APPLID:
+ if (app->appl == value)
+ return app;
+ break;
+ case CMTP_MAPPING:
+ if (app->mapping == value)
+ return app;
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+static int cmtp_msgnum_get(struct cmtp_session *session)
+{
+ session->msgnum++;
+
+ if ((session->msgnum & 0xff) > 200)
+ session->msgnum = CMTP_INITIAL_MSGNUM + 1;
+
+ return session->msgnum;
+}
+
+
+static void cmtp_send_interopmsg(struct cmtp_session *session,
+ __u8 subcmd, __u16 appl, __u16 msgnum,
+ __u16 function, unsigned char *buf, int len)
+{
+ struct sk_buff *skb;
+ unsigned char *s;
+
+ BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
+
+ if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate memory for interoperability packet");
+ return;
+ }
+
+ s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
+
+ capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
+ capimsg_setu16(s, 2, appl);
+ capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
+ capimsg_setu8 (s, 5, subcmd);
+ capimsg_setu16(s, 6, msgnum);
+
+ /* Interoperability selector (Bluetooth Device Management) */
+ capimsg_setu16(s, 8, 0x0001);
+
+ capimsg_setu8 (s, 10, 3 + len);
+ capimsg_setu16(s, 11, function);
+ capimsg_setu8 (s, 13, len);
+
+ if (len > 0)
+ memcpy(s + 14, buf, len);
+
+ cmtp_send_capimsg(session, skb);
+}
+
+static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
+{
+ struct capi_ctr *ctrl = &session->ctrl;
+ struct cmtp_application *application;
+ __u16 appl, msgnum, func, info;
+ __u32 controller;
+
+ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+ switch (CAPIMSG_SUBCOMMAND(skb->data)) {
+ case CAPI_CONF:
+ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
+ info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
+
+ switch (func) {
+ case CAPI_FUNCTION_REGISTER:
+ msgnum = CAPIMSG_MSGID(skb->data);
+
+ application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
+ if (application) {
+ application->state = BT_CONNECTED;
+ application->msgnum = 0;
+ application->mapping = CAPIMSG_APPID(skb->data);
+ wake_up_interruptible(&session->wait);
+ }
+
+ break;
+
+ case CAPI_FUNCTION_RELEASE:
+ appl = CAPIMSG_APPID(skb->data);
+
+ application = cmtp_application_get(session, CMTP_MAPPING, appl);
+ if (application) {
+ application->state = BT_CLOSED;
+ application->msgnum = 0;
+ wake_up_interruptible(&session->wait);
+ }
+
+ break;
+
+ case CAPI_FUNCTION_GET_PROFILE:
+ controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
+ msgnum = CAPIMSG_MSGID(skb->data);
+
+ if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
+ session->ncontroller = controller;
+ wake_up_interruptible(&session->wait);
+ break;
+ }
+
+ if (!info && ctrl) {
+ memcpy(&ctrl->profile,
+ skb->data + CAPI_MSG_BASELEN + 11,
+ sizeof(capi_profile));
+ session->state = BT_CONNECTED;
+ capi_ctr_ready(ctrl);
+ }
+
+ break;
+
+ case CAPI_FUNCTION_GET_MANUFACTURER:
+ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
+
+ if (!info && ctrl) {
+ strncpy(ctrl->manu,
+ skb->data + CAPI_MSG_BASELEN + 15,
+ skb->data[CAPI_MSG_BASELEN + 14]);
+ }
+
+ break;
+
+ case CAPI_FUNCTION_GET_VERSION:
+ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
+
+ if (!info && ctrl) {
+ ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
+ ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
+ ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
+ ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
+ }
+
+ break;
+
+ case CAPI_FUNCTION_GET_SERIAL_NUMBER:
+ controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
+
+ if (!info && ctrl) {
+ memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
+ strncpy(ctrl->serial,
+ skb->data + CAPI_MSG_BASELEN + 17,
+ skb->data[CAPI_MSG_BASELEN + 16]);
+ }
+
+ break;
+ }
+
+ break;
+
+ case CAPI_IND:
+ func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
+
+ if (func == CAPI_FUNCTION_LOOPBACK) {
+ appl = CAPIMSG_APPID(skb->data);
+ msgnum = CAPIMSG_MSGID(skb->data);
+ cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
+ skb->data + CAPI_MSG_BASELEN + 6,
+ skb->data[CAPI_MSG_BASELEN + 5]);
+ }
+
+ break;
+ }
+
+ kfree_skb(skb);
+}
+
+void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
+{
+ struct capi_ctr *ctrl = &session->ctrl;
+ struct cmtp_application *application;
+ __u16 cmd, appl;
+ __u32 contr;
+
+ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+ if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
+ cmtp_recv_interopmsg(session, skb);
+ return;
+ }
+
+ if (session->flags & (1 << CMTP_LOOPBACK)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
+ appl = CAPIMSG_APPID(skb->data);
+ contr = CAPIMSG_CONTROL(skb->data);
+
+ application = cmtp_application_get(session, CMTP_MAPPING, appl);
+ if (application) {
+ appl = application->appl;
+ CAPIMSG_SETAPPID(skb->data, appl);
+ } else {
+ BT_ERR("Can't find application with id %d", appl);
+ kfree_skb(skb);
+ return;
+ }
+
+ if ((contr & 0x7f) == 0x01) {
+ contr = (contr & 0xffffff80) | session->num;
+ CAPIMSG_SETCONTROL(skb->data, contr);
+ }
+
+ if (!ctrl) {
+ BT_ERR("Can't find controller %d for message", session->num);
+ kfree_skb(skb);
+ return;
+ }
+
+ capi_ctr_handle_message(ctrl, appl, skb);
+}
+
+void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
+{
+ struct cmtp_scb *scb = (void *) skb->cb;
+
+ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+ scb->id = -1;
+ scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
+
+ skb_queue_tail(&session->transmit, skb);
+
+ cmtp_schedule(session);
+}
+
+
+static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+ BT_DBG("ctrl %p data %p", ctrl, data);
+
+ return 0;
+}
+
+static void cmtp_reset_ctr(struct capi_ctr *ctrl)
+{
+ struct cmtp_session *session = ctrl->driverdata;
+
+ BT_DBG("ctrl %p", ctrl);
+
+ capi_ctr_reseted(ctrl);
+
+ atomic_inc(&session->terminate);
+ cmtp_schedule(session);
+}
+
+static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct cmtp_session *session = ctrl->driverdata;
+ struct cmtp_application *application;
+ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
+ unsigned char buf[8];
+ int err = 0, nconn, want = rp->level3cnt;
+
+ BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
+ ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
+
+ application = cmtp_application_add(session, appl);
+ if (!application) {
+ BT_ERR("Can't allocate memory for new application");
+ return;
+ }
+
+ if (want < 0)
+ nconn = ctrl->profile.nbchannel * -want;
+ else
+ nconn = want;
+
+ if (nconn == 0)
+ nconn = ctrl->profile.nbchannel;
+
+ capimsg_setu16(buf, 0, nconn);
+ capimsg_setu16(buf, 2, rp->datablkcnt);
+ capimsg_setu16(buf, 4, rp->datablklen);
+
+ application->state = BT_CONFIG;
+ application->msgnum = cmtp_msgnum_get(session);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
+ CAPI_FUNCTION_REGISTER, buf, 6);
+
+ add_wait_queue(&session->wait, &wait);
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (!timeo) {
+ err = -EAGAIN;
+ break;
+ }
+
+ if (application->state == BT_CLOSED) {
+ err = -application->err;
+ break;
+ }
+
+ if (application->state == BT_CONNECTED)
+ break;
+
+ if (signal_pending(current)) {
+ err = -EINTR;
+ break;
+ }
+
+ timeo = schedule_timeout(timeo);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&session->wait, &wait);
+
+ if (err) {
+ cmtp_application_del(session, application);
+ return;
+ }
+}
+
+static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct cmtp_session *session = ctrl->driverdata;
+ struct cmtp_application *application;
+ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
+
+ BT_DBG("ctrl %p appl %d", ctrl, appl);
+
+ application = cmtp_application_get(session, CMTP_APPLID, appl);
+ if (!application) {
+ BT_ERR("Can't find application");
+ return;
+ }
+
+ application->msgnum = cmtp_msgnum_get(session);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
+ CAPI_FUNCTION_RELEASE, NULL, 0);
+
+ add_wait_queue(&session->wait, &wait);
+ while (timeo) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (application->state == BT_CLOSED)
+ break;
+
+ if (signal_pending(current))
+ break;
+
+ timeo = schedule_timeout(timeo);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&session->wait, &wait);
+
+ cmtp_application_del(session, application);
+}
+
+static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+ struct cmtp_session *session = ctrl->driverdata;
+ struct cmtp_application *application;
+ __u16 appl;
+ __u32 contr;
+
+ BT_DBG("ctrl %p skb %p", ctrl, skb);
+
+ appl = CAPIMSG_APPID(skb->data);
+ contr = CAPIMSG_CONTROL(skb->data);
+
+ application = cmtp_application_get(session, CMTP_APPLID, appl);
+ if ((!application) || (application->state != BT_CONNECTED)) {
+ BT_ERR("Can't find application with id %d", appl);
+ kfree_skb(skb);
+ return CAPI_ILLAPPNR;
+ }
+
+ CAPIMSG_SETAPPID(skb->data, application->mapping);
+
+ if ((contr & 0x7f) == session->num) {
+ contr = (contr & 0xffffff80) | 0x01;
+ CAPIMSG_SETCONTROL(skb->data, contr);
+ }
+
+ cmtp_send_capimsg(session, skb);
+
+ return CAPI_NOERROR;
+}
+
+static char *cmtp_procinfo(struct capi_ctr *ctrl)
+{
+ return "CAPI Message Transport Protocol";
+}
+
+static int cmtp_ctr_read_proc(char *page, char **start, off_t off, int count, int *eof, struct capi_ctr *ctrl)
+{
+ struct cmtp_session *session = ctrl->driverdata;
+ struct cmtp_application *app;
+ struct list_head *p, *n;
+ int len = 0;
+
+ len += sprintf(page + len, "%s\n\n", cmtp_procinfo(ctrl));
+ len += sprintf(page + len, "addr %s\n", session->name);
+ len += sprintf(page + len, "ctrl %d\n", session->num);
+
+ list_for_each_safe(p, n, &session->applications) {
+ app = list_entry(p, struct cmtp_application, list);
+ len += sprintf(page + len, "appl %d -> %d\n", app->appl, app->mapping);
+ }
+
+ if (off + count >= len)
+ *eof = 1;
+
+ if (len < off)
+ return 0;
+
+ *start = page + off;
+
+ return ((count < len - off) ? count : len - off);
+}
+
+
+int cmtp_attach_device(struct cmtp_session *session)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long timeo = CMTP_INTEROP_TIMEOUT;
+ unsigned char buf[4];
+
+ BT_DBG("session %p", session);
+
+ capimsg_setu32(buf, 0, 0);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
+ CAPI_FUNCTION_GET_PROFILE, buf, 4);
+
+ add_wait_queue(&session->wait, &wait);
+ while (timeo) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (session->ncontroller)
+ break;
+
+ if (signal_pending(current))
+ break;
+
+ timeo = schedule_timeout(timeo);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&session->wait, &wait);
+
+ BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
+
+ if (!timeo)
+ return -ETIMEDOUT;
+
+ if (!session->ncontroller)
+ return -ENODEV;
+
+
+ if (session->ncontroller > 1)
+ BT_INFO("Setting up only CAPI controller 1");
+
+ session->ctrl.owner = THIS_MODULE;
+ session->ctrl.driverdata = session;
+ strcpy(session->ctrl.name, session->name);
+
+ session->ctrl.driver_name = "cmtp";
+ session->ctrl.load_firmware = cmtp_load_firmware;
+ session->ctrl.reset_ctr = cmtp_reset_ctr;
+ session->ctrl.register_appl = cmtp_register_appl;
+ session->ctrl.release_appl = cmtp_release_appl;
+ session->ctrl.send_message = cmtp_send_message;
+
+ session->ctrl.procinfo = cmtp_procinfo;
+ session->ctrl.ctr_read_proc = cmtp_ctr_read_proc;
+
+ if (attach_capi_ctr(&session->ctrl) < 0) {
+ BT_ERR("Can't attach new controller");
+ return -EBUSY;
+ }
+
+ session->num = session->ctrl.cnr;
+
+ BT_DBG("session %p num %d", session, session->num);
+
+ capimsg_setu32(buf, 0, 1);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+ CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+ CAPI_FUNCTION_GET_VERSION, buf, 4);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+ CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
+
+ cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
+ CAPI_FUNCTION_GET_PROFILE, buf, 4);
+
+ return 0;
+}
+
+void cmtp_detach_device(struct cmtp_session *session)
+{
+ BT_DBG("session %p", session);
+
+ detach_capi_ctr(&session->ctrl);
+}
diff --git a/net/bluetooth/cmtp/cmtp.h b/net/bluetooth/cmtp/cmtp.h
new file mode 100644
index 000000000000..0a0062607584
--- /dev/null
+++ b/net/bluetooth/cmtp/cmtp.h
@@ -0,0 +1,136 @@
+/*
+ CMTP implementation for Linux Bluetooth stack (BlueZ).
+ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+#ifndef __CMTP_H
+#define __CMTP_H
+
+#include <linux/types.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define BTNAMSIZ 18
+
+/* CMTP ioctl defines */
+#define CMTPCONNADD _IOW('C', 200, int)
+#define CMTPCONNDEL _IOW('C', 201, int)
+#define CMTPGETCONNLIST _IOR('C', 210, int)
+#define CMTPGETCONNINFO _IOR('C', 211, int)
+
+#define CMTP_LOOPBACK 0
+
+struct cmtp_connadd_req {
+ int sock; // Connected socket
+ __u32 flags;
+};
+
+struct cmtp_conndel_req {
+ bdaddr_t bdaddr;
+ __u32 flags;
+};
+
+struct cmtp_conninfo {
+ bdaddr_t bdaddr;
+ __u32 flags;
+ __u16 state;
+ int num;
+};
+
+struct cmtp_connlist_req {
+ __u32 cnum;
+ struct cmtp_conninfo *ci;
+};
+
+int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock);
+int cmtp_del_connection(struct cmtp_conndel_req *req);
+int cmtp_get_connlist(struct cmtp_connlist_req *req);
+int cmtp_get_conninfo(struct cmtp_conninfo *ci);
+
+/* CMTP session defines */
+#define CMTP_INTEROP_TIMEOUT (HZ * 5)
+#define CMTP_INITIAL_MSGNUM 0xff00
+
+struct cmtp_session {
+ struct list_head list;
+
+ struct socket *sock;
+
+ bdaddr_t bdaddr;
+
+ unsigned long state;
+ unsigned long flags;
+
+ uint mtu;
+
+ char name[BTNAMSIZ];
+
+ atomic_t terminate;
+
+ wait_queue_head_t wait;
+
+ int ncontroller;
+ int num;
+ struct capi_ctr ctrl;
+
+ struct list_head applications;
+
+ unsigned long blockids;
+ int msgnum;
+
+ struct sk_buff_head transmit;
+
+ struct sk_buff *reassembly[16];
+};
+
+struct cmtp_application {
+ struct list_head list;
+
+ unsigned long state;
+ int err;
+
+ __u16 appl;
+ __u16 mapping;
+
+ __u16 msgnum;
+};
+
+struct cmtp_scb {
+ int id;
+ int data;
+};
+
+int cmtp_attach_device(struct cmtp_session *session);
+void cmtp_detach_device(struct cmtp_session *session);
+
+void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
+void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb);
+
+static inline void cmtp_schedule(struct cmtp_session *session)
+{
+ struct sock *sk = session->sock->sk;
+
+ wake_up_interruptible(sk->sk_sleep);
+}
+
+/* CMTP init defines */
+int cmtp_init_sockets(void);
+void cmtp_cleanup_sockets(void);
+
+#endif /* __CMTP_H */
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
new file mode 100644
index 000000000000..c5243ce93e67
--- /dev/null
+++ b/net/bluetooth/cmtp/core.c
@@ -0,0 +1,507 @@
+/*
+ CMTP implementation for Linux Bluetooth stack (BlueZ).
+ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <net/sock.h>
+
+#include <linux/isdn/capilli.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "cmtp.h"
+
+#ifndef CONFIG_BT_CMTP_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.0"
+
+static DECLARE_RWSEM(cmtp_session_sem);
+static LIST_HEAD(cmtp_session_list);
+
+static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
+{
+ struct cmtp_session *session;
+ struct list_head *p;
+
+ BT_DBG("");
+
+ list_for_each(p, &cmtp_session_list) {
+ session = list_entry(p, struct cmtp_session, list);
+ if (!bacmp(bdaddr, &session->bdaddr))
+ return session;
+ }
+ return NULL;
+}
+
+static void __cmtp_link_session(struct cmtp_session *session)
+{
+ __module_get(THIS_MODULE);
+ list_add(&session->list, &cmtp_session_list);
+}
+
+static void __cmtp_unlink_session(struct cmtp_session *session)
+{
+ list_del(&session->list);
+ module_put(THIS_MODULE);
+}
+
+static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
+{
+ bacpy(&ci->bdaddr, &session->bdaddr);
+
+ ci->flags = session->flags;
+ ci->state = session->state;
+
+ ci->num = session->num;
+}
+
+
+static inline int cmtp_alloc_block_id(struct cmtp_session *session)
+{
+ int i, id = -1;
+
+ for (i = 0; i < 16; i++)
+ if (!test_and_set_bit(i, &session->blockids)) {
+ id = i;
+ break;
+ }
+
+ return id;
+}
+
+static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
+{
+ clear_bit(id, &session->blockids);
+}
+
+static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
+{
+ struct sk_buff *skb = session->reassembly[id], *nskb;
+ int size;
+
+ BT_DBG("session %p buf %p count %d", session, buf, count);
+
+ size = (skb) ? skb->len + count : count;
+
+ if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate memory for CAPI message");
+ return;
+ }
+
+ if (skb && (skb->len > 0))
+ memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
+
+ memcpy(skb_put(nskb, count), buf, count);
+
+ session->reassembly[id] = nskb;
+
+ if (skb)
+ kfree_skb(skb);
+}
+
+static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
+{
+ __u8 hdr, hdrlen, id;
+ __u16 len;
+
+ BT_DBG("session %p skb %p len %d", session, skb, skb->len);
+
+ while (skb->len > 0) {
+ hdr = skb->data[0];
+
+ switch (hdr & 0xc0) {
+ case 0x40:
+ hdrlen = 2;
+ len = skb->data[1];
+ break;
+ case 0x80:
+ hdrlen = 3;
+ len = skb->data[1] | (skb->data[2] << 8);
+ break;
+ default:
+ hdrlen = 1;
+ len = 0;
+ break;
+ }
+
+ id = (hdr & 0x3c) >> 2;
+
+ BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
+
+ if (hdrlen + len > skb->len) {
+ BT_ERR("Wrong size or header information in CMTP frame");
+ break;
+ }
+
+ if (len == 0) {
+ skb_pull(skb, hdrlen);
+ continue;
+ }
+
+ switch (hdr & 0x03) {
+ case 0x00:
+ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
+ cmtp_recv_capimsg(session, session->reassembly[id]);
+ session->reassembly[id] = NULL;
+ break;
+ case 0x01:
+ cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
+ break;
+ default:
+ if (session->reassembly[id] != NULL)
+ kfree_skb(session->reassembly[id]);
+ session->reassembly[id] = NULL;
+ break;
+ }
+
+ skb_pull(skb, hdrlen + len);
+ }
+
+ kfree_skb(skb);
+ return 0;
+}
+
+static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
+{
+ struct socket *sock = session->sock;
+ struct iovec iv = { data, len };
+ struct msghdr msg;
+
+ BT_DBG("session %p data %p len %d", session, data, len);
+
+ if (!len)
+ return 0;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iovlen = 1;
+ msg.msg_iov = &iv;
+
+ return sock_sendmsg(sock, &msg, len);
+}
+
+static int cmtp_process_transmit(struct cmtp_session *session)
+{
+ struct sk_buff *skb, *nskb;
+ unsigned char *hdr;
+ unsigned int size, tail;
+
+ BT_DBG("session %p", session);
+
+ if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate memory for new frame");
+ return -ENOMEM;
+ }
+
+ while ((skb = skb_dequeue(&session->transmit))) {
+ struct cmtp_scb *scb = (void *) skb->cb;
+
+ if ((tail = (session->mtu - nskb->len)) < 5) {
+ cmtp_send_frame(session, nskb->data, nskb->len);
+ skb_trim(nskb, 0);
+ tail = session->mtu;
+ }
+
+ size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
+
+ if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
+ skb_queue_head(&session->transmit, skb);
+ break;
+ }
+
+ if (size < 256) {
+ hdr = skb_put(nskb, 2);
+ hdr[0] = 0x40
+ | ((scb->id << 2) & 0x3c)
+ | ((skb->len == size) ? 0x00 : 0x01);
+ hdr[1] = size;
+ } else {
+ hdr = skb_put(nskb, 3);
+ hdr[0] = 0x80
+ | ((scb->id << 2) & 0x3c)
+ | ((skb->len == size) ? 0x00 : 0x01);
+ hdr[1] = size & 0xff;
+ hdr[2] = size >> 8;
+ }
+
+ memcpy(skb_put(nskb, size), skb->data, size);
+ skb_pull(skb, size);
+
+ if (skb->len > 0) {
+ skb_queue_head(&session->transmit, skb);
+ } else {
+ cmtp_free_block_id(session, scb->id);
+ if (scb->data) {
+ cmtp_send_frame(session, nskb->data, nskb->len);
+ skb_trim(nskb, 0);
+ }
+ kfree_skb(skb);
+ }
+ }
+
+ cmtp_send_frame(session, nskb->data, nskb->len);
+
+ kfree_skb(nskb);
+
+ return skb_queue_len(&session->transmit);
+}
+
+static int cmtp_session(void *arg)
+{
+ struct cmtp_session *session = arg;
+ struct sock *sk = session->sock->sk;
+ struct sk_buff *skb;
+ wait_queue_t wait;
+
+ BT_DBG("session %p", session);
+
+ daemonize("kcmtpd_ctr_%d", session->num);
+ set_user_nice(current, -15);
+ current->flags |= PF_IOTHREAD;
+
+ set_fs(KERNEL_DS);
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(sk->sk_sleep, &wait);
+ while (!atomic_read(&session->terminate)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (sk->sk_state != BT_CONNECTED)
+ break;
+
+ while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
+ skb_orphan(skb);
+ cmtp_recv_frame(session, skb);
+ }
+
+ cmtp_process_transmit(session);
+
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(sk->sk_sleep, &wait);
+
+ down_write(&cmtp_session_sem);
+
+ if (!(session->flags & (1 << CMTP_LOOPBACK)))
+ cmtp_detach_device(session);
+
+ fput(session->sock->file);
+
+ __cmtp_unlink_session(session);
+
+ up_write(&cmtp_session_sem);
+
+ kfree(session);
+ return 0;
+}
+
+int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
+{
+ struct cmtp_session *session, *s;
+ bdaddr_t src, dst;
+ int i, err;
+
+ BT_DBG("");
+
+ baswap(&src, &bt_sk(sock->sk)->src);
+ baswap(&dst, &bt_sk(sock->sk)->dst);
+
+ session = kmalloc(sizeof(struct cmtp_session), GFP_KERNEL);
+ if (!session)
+ return -ENOMEM;
+ memset(session, 0, sizeof(struct cmtp_session));
+
+ down_write(&cmtp_session_sem);
+
+ s = __cmtp_get_session(&bt_sk(sock->sk)->dst);
+ if (s && s->state == BT_CONNECTED) {
+ err = -EEXIST;
+ goto failed;
+ }
+
+ bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst);
+
+ session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
+
+ BT_DBG("mtu %d", session->mtu);
+
+ sprintf(session->name, "%s", batostr(&dst));
+
+ session->sock = sock;
+ session->state = BT_CONFIG;
+
+ init_waitqueue_head(&session->wait);
+
+ session->msgnum = CMTP_INITIAL_MSGNUM;
+
+ INIT_LIST_HEAD(&session->applications);
+
+ skb_queue_head_init(&session->transmit);
+
+ for (i = 0; i < 16; i++)
+ session->reassembly[i] = NULL;
+
+ session->flags = req->flags;
+
+ __cmtp_link_session(session);
+
+ err = kernel_thread(cmtp_session, session, CLONE_KERNEL);
+ if (err < 0)
+ goto unlink;
+
+ if (!(session->flags & (1 << CMTP_LOOPBACK))) {
+ err = cmtp_attach_device(session);
+ if (err < 0)
+ goto detach;
+ }
+
+ up_write(&cmtp_session_sem);
+ return 0;
+
+detach:
+ cmtp_detach_device(session);
+
+unlink:
+ __cmtp_unlink_session(session);
+
+failed:
+ up_write(&cmtp_session_sem);
+ kfree(session);
+ return err;
+}
+
+int cmtp_del_connection(struct cmtp_conndel_req *req)
+{
+ struct cmtp_session *session;
+ int err = 0;
+
+ BT_DBG("");
+
+ down_read(&cmtp_session_sem);
+
+ session = __cmtp_get_session(&req->bdaddr);
+ if (session) {
+ /* Flush the transmit queue */
+ skb_queue_purge(&session->transmit);
+
+ /* Kill session thread */
+ atomic_inc(&session->terminate);
+ cmtp_schedule(session);
+ } else
+ err = -ENOENT;
+
+ up_read(&cmtp_session_sem);
+ return err;
+}
+
+int cmtp_get_connlist(struct cmtp_connlist_req *req)
+{
+ struct list_head *p;
+ int err = 0, n = 0;
+
+ BT_DBG("");
+
+ down_read(&cmtp_session_sem);
+
+ list_for_each(p, &cmtp_session_list) {
+ struct cmtp_session *session;
+ struct cmtp_conninfo ci;
+
+ session = list_entry(p, struct cmtp_session, list);
+
+ __cmtp_copy_session(session, &ci);
+
+ if (copy_to_user(req->ci, &ci, sizeof(ci))) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (++n >= req->cnum)
+ break;
+
+ req->ci++;
+ }
+ req->cnum = n;
+
+ up_read(&cmtp_session_sem);
+ return err;
+}
+
+int cmtp_get_conninfo(struct cmtp_conninfo *ci)
+{
+ struct cmtp_session *session;
+ int err = 0;
+
+ down_read(&cmtp_session_sem);
+
+ session = __cmtp_get_session(&ci->bdaddr);
+ if (session)
+ __cmtp_copy_session(session, ci);
+ else
+ err = -ENOENT;
+
+ up_read(&cmtp_session_sem);
+ return err;
+}
+
+
+int __init init_cmtp(void)
+{
+ l2cap_load();
+
+ BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION);
+
+ cmtp_init_sockets();
+
+ return 0;
+}
+
+void __exit exit_cmtp(void)
+{
+ cmtp_cleanup_sockets();
+}
+
+module_init(init_cmtp);
+module_exit(exit_cmtp);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("bt-proto-5");
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
new file mode 100644
index 000000000000..fdb0eb6f8319
--- /dev/null
+++ b/net/bluetooth/cmtp/sock.c
@@ -0,0 +1,199 @@
+/*
+ CMTP implementation for Linux Bluetooth stack (BlueZ).
+ Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <net/sock.h>
+
+#include <linux/isdn/capilli.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "cmtp.h"
+
+#ifndef CONFIG_BT_CMTP_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+static int cmtp_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ BT_DBG("sock %p sk %p", sock, sk);
+
+ if (!sk)
+ return 0;
+
+ sock_orphan(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct cmtp_connadd_req ca;
+ struct cmtp_conndel_req cd;
+ struct cmtp_connlist_req cl;
+ struct cmtp_conninfo ci;
+ struct socket *nsock;
+ int err;
+
+ BT_DBG("cmd %x arg %lx", cmd, arg);
+
+ switch (cmd) {
+ case CMTPCONNADD:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
+ return -EFAULT;
+
+ nsock = sockfd_lookup(ca.sock, &err);
+ if (!nsock)
+ return err;
+
+ if (nsock->sk->sk_state != BT_CONNECTED)
+ return -EBADFD;
+
+ err = cmtp_add_connection(&ca, nsock);
+ if (!err) {
+ if (copy_to_user((void *) arg, &ca, sizeof(ca)))
+ err = -EFAULT;
+ } else
+ fput(nsock->file);
+
+ return err;
+
+ case CMTPCONNDEL:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
+ return -EFAULT;
+
+ return cmtp_del_connection(&cd);
+
+ case CMTPGETCONNLIST:
+ if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
+ return -EFAULT;
+
+ if (cl.cnum <= 0)
+ return -EINVAL;
+
+ err = cmtp_get_connlist(&cl);
+ if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
+ return -EFAULT;
+
+ return err;
+
+ case CMTPGETCONNINFO:
+ if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
+ return -EFAULT;
+
+ err = cmtp_get_conninfo(&ci);
+ if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
+ return -EFAULT;
+
+ return err;
+ }
+
+ return -EINVAL;
+}
+
+static struct proto_ops cmtp_sock_ops = {
+ .family = PF_BLUETOOTH,
+ .owner = THIS_MODULE,
+ .release = cmtp_sock_release,
+ .ioctl = cmtp_sock_ioctl,
+ .bind = sock_no_bind,
+ .getname = sock_no_getname,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .poll = sock_no_poll,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .mmap = sock_no_mmap
+};
+
+static int cmtp_sock_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+
+ BT_DBG("sock %p", sock);
+
+ if (sock->type != SOCK_RAW)
+ return -ESOCKTNOSUPPORT;
+
+ if (!(sk = bt_sock_alloc(sock, PF_BLUETOOTH, 0, GFP_KERNEL)))
+ return -ENOMEM;
+
+ sk_set_owner(sk, THIS_MODULE);
+
+ sock->ops = &cmtp_sock_ops;
+
+ sock->state = SS_UNCONNECTED;
+
+ sk->sk_destruct = NULL;
+ sk->sk_protocol = protocol;
+
+ return 0;
+}
+
+static struct net_proto_family cmtp_sock_family_ops = {
+ .family = PF_BLUETOOTH,
+ .owner = THIS_MODULE,
+ .create = cmtp_sock_create
+};
+
+int cmtp_init_sockets(void)
+{
+ bt_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops);
+
+ return 0;
+}
+
+void cmtp_cleanup_sockets(void)
+{
+ if (bt_sock_unregister(BTPROTO_CMTP))
+ BT_ERR("Can't unregister CMTP socket");
+}
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index dca73e34a16f..132ce9982783 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -71,7 +71,7 @@ void hci_acl_connect(struct hci_conn *conn)
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, &conn->dst);
- cp.pscan_rep_mode = 0x01;
+ cp.pscan_rep_mode = 0x02;
if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) &&
inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 2436a71ed563..b760910ed84e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -406,6 +406,7 @@ int hci_inquiry(unsigned long arg)
hci_dev_lock_bh(hdev);
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
+ inquiry_cache_empty(hdev) ||
ir.flags & IREQ_CACHE_FLUSH) {
inquiry_cache_flush(hdev);
do_inquiry = 1;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 9d70c8c9ff35..262571e2f521 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -452,6 +452,29 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
hci_dev_unlock(hdev);
}
+/* Inquiry Result With RSSI */
+static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct inquiry_info_with_rssi *info = (struct inquiry_info_with_rssi *) (skb->data + 1);
+ int num_rsp = *((__u8 *) skb->data);
+
+ BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
+
+ hci_dev_lock(hdev);
+ for (; num_rsp; num_rsp--) {
+ struct inquiry_info tmp;
+ bacpy(&tmp.bdaddr, &info->bdaddr);
+ tmp.pscan_rep_mode = info->pscan_rep_mode;
+ tmp.pscan_period_mode = info->pscan_period_mode;
+ tmp.pscan_mode = 0x00;
+ memcpy(tmp.dev_class, &info->dev_class, 3);
+ tmp.clock_offset = info->clock_offset;
+ info++;
+ inquiry_cache_update(hdev, &tmp);
+ }
+ hci_dev_unlock(hdev);
+}
+
/* Connect Request */
static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
@@ -744,6 +767,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_inquiry_result_evt(hdev, skb);
break;
+ case HCI_EV_INQUIRY_RESULT_WITH_RSSI:
+ hci_inquiry_result_with_rssi_evt(hdev, skb);
+ break;
+
case HCI_EV_CONN_REQUEST:
hci_conn_request_evt(hdev, skb);
break;
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 349f6540f36e..886163ece9ac 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -66,20 +66,20 @@ static struct hci_sec_filter hci_sec_filter = {
/* Packet types */
0x10,
/* Events */
- { 0xd9fe, 0x0 },
+ { 0x1000d9fe, 0x0000300c },
/* Commands */
{
{ 0x0 },
/* OGF_LINK_CTL */
- { 0x2a000002, 0x0, 0x0, 0x0 },
+ { 0xbe000006, 0x00000001, 0x0000, 0x00 },
/* OGF_LINK_POLICY */
- { 0x1200, 0x0, 0x0, 0x0 },
+ { 0x00005200, 0x00000000, 0x0000, 0x00 },
/* OGF_HOST_CTL */
- { 0x80100000, 0x202a, 0x0, 0x0 },
+ { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
/* OGF_INFO_PARAM */
- { 0x22a, 0x0, 0x0, 0x0 },
+ { 0x000002be, 0x00000000, 0x0000, 0x00 },
/* OGF_STATUS_PARAM */
- { 0x2e, 0x0, 0x0, 0x0 }
+ { 0x000000ea, 0x00000000, 0x0000, 0x00 }
}
};
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 7280fac62948..a382860e3fb8 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -2202,3 +2202,4 @@ module_exit(l2cap_cleanup);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION);
MODULE_LICENSE("GPL");
+MODULE_ALIAS("bt-proto-0");
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 148be892afe6..8d2608c39d29 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -52,7 +52,7 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/rfcomm.h>
-#define VERSION "1.0"
+#define VERSION "1.1"
#ifndef CONFIG_BT_RFCOMM_DEBUG
#undef BT_DBG
@@ -207,7 +207,7 @@ static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d)
d->mtu = RFCOMM_DEFAULT_MTU;
d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV;
- d->credits = RFCOMM_MAX_CREDITS;
+ d->cfc = RFCOMM_CFC_DISABLED;
d->rx_credits = RFCOMM_DEFAULT_CREDITS;
}
@@ -314,8 +314,8 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
d->state = BT_CONFIG;
rfcomm_dlc_link(s, d);
- d->mtu = s->mtu;
- d->credits = s->credits;
+ d->mtu = s->mtu;
+ d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
if (s->state == BT_CONNECTED)
rfcomm_send_pn(s, 1, d);
@@ -415,7 +415,7 @@ void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
{
BT_DBG("dlc %p state %ld", d, d->state);
- if (!d->credits) {
+ if (!d->cfc) {
d->v24_sig |= RFCOMM_V24_FC;
set_bit(RFCOMM_MSC_PENDING, &d->flags);
}
@@ -426,7 +426,7 @@ void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
{
BT_DBG("dlc %p state %ld", d, d->state);
- if (!d->credits) {
+ if (!d->cfc) {
d->v24_sig &= ~RFCOMM_V24_FC;
set_bit(RFCOMM_MSC_PENDING, &d->flags);
}
@@ -479,8 +479,8 @@ struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
s->state = state;
s->sock = sock;
- s->mtu = RFCOMM_DEFAULT_MTU;
- s->credits = RFCOMM_MAX_CREDITS;
+ s->mtu = RFCOMM_DEFAULT_MTU;
+ s->cfc = RFCOMM_CFC_UNKNOWN;
list_add(&s->list, &session_list);
@@ -752,7 +752,7 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d
pn->ack_timer = 0;
pn->max_retrans = 0;
- if (d->credits) {
+ if (s->cfc) {
pn->flow_ctrl = cr ? 0xf0 : 0xe0;
pn->credits = RFCOMM_DEFAULT_CREDITS;
} else {
@@ -1142,28 +1142,22 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
{
+ struct rfcomm_session *s = d->session;
+
BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d",
d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits);
- if (cr) {
- if (pn->flow_ctrl == 0xf0) {
- d->tx_credits = pn->credits;
- } else {
- set_bit(RFCOMM_TX_THROTTLED, &d->flags);
- d->credits = 0;
- }
+ if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) {
+ d->cfc = s->cfc = RFCOMM_CFC_ENABLED;
+ d->tx_credits = pn->credits;
} else {
- if (pn->flow_ctrl == 0xe0) {
- d->tx_credits = pn->credits;
- } else {
- set_bit(RFCOMM_TX_THROTTLED, &d->flags);
- d->credits = 0;
- }
+ d->cfc = s->cfc = RFCOMM_CFC_DISABLED;
+ set_bit(RFCOMM_TX_THROTTLED, &d->flags);
}
d->priority = pn->priority;
- d->mtu = btohs(pn->mtu);
+ d->mtu = s->mtu = btohs(pn->mtu);
return 0;
}
@@ -1353,7 +1347,7 @@ static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb
return 0;
if (cr) {
- if (msc->v24_sig & RFCOMM_V24_FC && !d->credits)
+ if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc)
set_bit(RFCOMM_TX_THROTTLED, &d->flags);
else
clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
@@ -1444,7 +1438,7 @@ static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk
goto drop;
}
- if (pf && d->credits) {
+ if (pf && d->cfc) {
u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
d->tx_credits += credits;
@@ -1549,20 +1543,20 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
struct sk_buff *skb;
int err;
- BT_DBG("dlc %p state %ld credits %d rx_credits %d tx_credits %d",
- d, d->state, d->credits, d->rx_credits, d->tx_credits);
+ BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d",
+ d, d->state, d->cfc, d->rx_credits, d->tx_credits);
/* Send pending MSC */
if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
- if (d->credits) {
+ if (d->cfc) {
/* CFC enabled.
* Give them some credits */
if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
- d->rx_credits <= (d->credits >> 2)) {
- rfcomm_send_credits(d->session, d->addr, d->credits - d->rx_credits);
- d->rx_credits = d->credits;
+ d->rx_credits <= (d->cfc >> 2)) {
+ rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits);
+ d->rx_credits = d->cfc;
}
} else {
/* CFC disabled.
@@ -1583,7 +1577,7 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
d->tx_credits--;
}
- if (d->credits && !d->tx_credits) {
+ if (d->cfc && !d->tx_credits) {
/* We're out of TX credits.
* Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */
set_bit(RFCOMM_TX_THROTTLED, &d->flags);
@@ -1655,7 +1649,9 @@ static inline void rfcomm_accept_connection(struct rfcomm_session *s)
nsock->type = sock->type;
nsock->ops = sock->ops;
-
+
+ __module_get(nsock->ops->owner);
+
err = sock->ops->accept(sock, nsock, O_NONBLOCK);
if (err < 0) {
sock_release(nsock);
@@ -1998,3 +1994,4 @@ module_exit(rfcomm_cleanup);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION);
MODULE_LICENSE("GPL");
+MODULE_ALIAS("bt-proto-3");
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 987a01ac4b93..27c0f9ac3007 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -1055,3 +1055,4 @@ module_exit(sco_cleanup);
MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);
MODULE_LICENSE("GPL");
+MODULE_ALIAS("bt-proto-2");