Commit fd5ef786 authored by David S. Miller's avatar David S. Miller

Merge nuts.ninka.net:/disk1/davem/BK/bluetooth-2.6

into nuts.ninka.net:/disk1/davem/BK/net-2.6
parents aa0d8c97 46fc7407
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
/*
*
* 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");
/*
*
* 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");
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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)) {
......
......@@ -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
......
......@@ -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
......
......@@ -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;
......
......@@ -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);
}
}
......
......@@ -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;
......
......@@ -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 */
......
......@@ -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
......
......@@ -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
......@@ -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;
......
......@@ -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");
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).
#
# Makefile for the Linux Bluetooth CMTP layer
#
obj-$(CONFIG_BT_CMTP) += cmtp.o
cmtp-objs := core.o sock.o capi.o
/*
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);
}
/*
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 */
/*
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");
/*
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");
}
......@@ -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) {
......
......@@ -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;
......
......@@ -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;
......
......@@ -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 }
}
};
......
......@@ -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");
......@@ -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");
......@@ -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");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment