提交 b9caaabb 编写于 作者: D David S. Miller
...@@ -82,6 +82,8 @@ block/ ...@@ -82,6 +82,8 @@ block/
- info on the Block I/O (BIO) layer. - info on the Block I/O (BIO) layer.
blockdev/ blockdev/
- info on block devices & drivers - info on block devices & drivers
btmrvl.txt
- info on Marvell Bluetooth driver usage.
cachetlb.txt cachetlb.txt
- describes the cache/TLB flushing interfaces Linux uses. - describes the cache/TLB flushing interfaces Linux uses.
cdrom/ cdrom/
......
=======================================================================
README for btmrvl driver
=======================================================================
All commands are used via debugfs interface.
=====================
Set/get driver configurations:
Path: /debug/btmrvl/config/
gpiogap=[n]
hscfgcmd
These commands are used to configure the host sleep parameters.
bit 8:0 -- Gap
bit 16:8 -- GPIO
where GPIO is the pin number of GPIO used to wake up the host.
It could be any valid GPIO pin# (e.g. 0-7) or 0xff (SDIO interface
wakeup will be used instead).
where Gap is the gap in milli seconds between wakeup signal and
wakeup event, or 0xff for special host sleep setting.
Usage:
# Use SDIO interface to wake up the host and set GAP to 0x80:
echo 0xff80 > /debug/btmrvl/config/gpiogap
echo 1 > /debug/btmrvl/config/hscfgcmd
# Use GPIO pin #3 to wake up the host and set GAP to 0xff:
echo 0x03ff > /debug/btmrvl/config/gpiogap
echo 1 > /debug/btmrvl/config/hscfgcmd
psmode=[n]
pscmd
These commands are used to enable/disable auto sleep mode
where the option is:
1 -- Enable auto sleep mode
0 -- Disable auto sleep mode
Usage:
# Enable auto sleep mode
echo 1 > /debug/btmrvl/config/psmode
echo 1 > /debug/btmrvl/config/pscmd
# Disable auto sleep mode
echo 0 > /debug/btmrvl/config/psmode
echo 1 > /debug/btmrvl/config/pscmd
hsmode=[n]
hscmd
These commands are used to enable host sleep or wake up firmware
where the option is:
1 -- Enable host sleep
0 -- Wake up firmware
Usage:
# Enable host sleep
echo 1 > /debug/btmrvl/config/hsmode
echo 1 > /debug/btmrvl/config/hscmd
# Wake up firmware
echo 0 > /debug/btmrvl/config/hsmode
echo 1 > /debug/btmrvl/config/hscmd
======================
Get driver status:
Path: /debug/btmrvl/status/
Usage:
cat /debug/btmrvl/status/<args>
where the args are:
curpsmode
This command displays current auto sleep status.
psstate
This command display the power save state.
hsstate
This command display the host sleep state.
txdnldrdy
This command displays the value of Tx download ready flag.
=====================
Use hcitool to issue raw hci command, refer to hcitool manual
Usage: Hcitool cmd <ogf> <ocf> [Parameters]
Interface Control Command
hcitool cmd 0x3f 0x5b 0xf5 0x01 0x00 --Enable All interface
hcitool cmd 0x3f 0x5b 0xf5 0x01 0x01 --Enable Wlan interface
hcitool cmd 0x3f 0x5b 0xf5 0x01 0x02 --Enable BT interface
hcitool cmd 0x3f 0x5b 0xf5 0x00 0x00 --Disable All interface
hcitool cmd 0x3f 0x5b 0xf5 0x00 0x01 --Disable Wlan interface
hcitool cmd 0x3f 0x5b 0xf5 0x00 0x02 --Disable BT interface
=======================================================================
SD8688 firmware:
/lib/firmware/sd8688_helper.bin
/lib/firmware/sd8688.bin
The images can be downloaded from:
git.infradead.org/users/dwmw2/linux-firmware.git/libertas/
...@@ -170,5 +170,30 @@ config BT_HCIVHCI ...@@ -170,5 +170,30 @@ config BT_HCIVHCI
Say Y here to compile support for virtual HCI devices into the Say Y here to compile support for virtual HCI devices into the
kernel or say M to compile it as module (hci_vhci). kernel or say M to compile it as module (hci_vhci).
config BT_MRVL
tristate "Marvell Bluetooth driver support"
help
The core driver to support Marvell Bluetooth devices.
This driver is required if you want to support
Marvell Bluetooth devices, such as 8688.
Say Y here to compile Marvell Bluetooth driver
into the kernel or say M to compile it as module.
config BT_MRVL_SDIO
tristate "Marvell BT-over-SDIO driver"
depends on BT_MRVL && MMC
select FW_LOADER
help
The driver for Marvell Bluetooth chipsets with SDIO interface.
This driver is required if you want to use Marvell Bluetooth
devices with SDIO interface. Currently only SD8688 chipset is
supported.
Say Y here to compile support for Marvell BT-over-SDIO driver
into the kernel or say M to compile it as module.
endmenu endmenu
...@@ -15,6 +15,12 @@ obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o ...@@ -15,6 +15,12 @@ obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o
obj-$(CONFIG_BT_HCIBTUSB) += btusb.o obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
hci_uart-y := hci_ldisc.o hci_uart-y := hci_ldisc.o
hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
......
/**
* Marvell Bluetooth driver: debugfs related functions
*
* Copyright (C) 2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
**/
#include <linux/debugfs.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btmrvl_drv.h"
struct btmrvl_debugfs_data {
struct dentry *root_dir, *config_dir, *status_dir;
/* config */
struct dentry *drvdbg;
struct dentry *psmode;
struct dentry *pscmd;
struct dentry *hsmode;
struct dentry *hscmd;
struct dentry *gpiogap;
struct dentry *hscfgcmd;
/* status */
struct dentry *curpsmode;
struct dentry *hsstate;
struct dentry *psstate;
struct dentry *txdnldready;
};
static int btmrvl_open_generic(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static ssize_t btmrvl_hscfgcmd_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
long result, ret;
memset(buf, 0, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
ret = strict_strtol(buf, 10, &result);
priv->btmrvl_dev.hscfgcmd = result;
if (priv->btmrvl_dev.hscfgcmd) {
btmrvl_prepare_command(priv);
wake_up_interruptible(&priv->main_thread.wait_q);
}
return count;
}
static ssize_t btmrvl_hscfgcmd_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
priv->btmrvl_dev.hscfgcmd);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_hscfgcmd_fops = {
.read = btmrvl_hscfgcmd_read,
.write = btmrvl_hscfgcmd_write,
.open = btmrvl_open_generic,
};
static ssize_t btmrvl_psmode_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
long result, ret;
memset(buf, 0, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
ret = strict_strtol(buf, 10, &result);
priv->btmrvl_dev.psmode = result;
return count;
}
static ssize_t btmrvl_psmode_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
priv->btmrvl_dev.psmode);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_psmode_fops = {
.read = btmrvl_psmode_read,
.write = btmrvl_psmode_write,
.open = btmrvl_open_generic,
};
static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
long result, ret;
memset(buf, 0, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
ret = strict_strtol(buf, 10, &result);
priv->btmrvl_dev.pscmd = result;
if (priv->btmrvl_dev.pscmd) {
btmrvl_prepare_command(priv);
wake_up_interruptible(&priv->main_thread.wait_q);
}
return count;
}
static ssize_t btmrvl_pscmd_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.pscmd);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_pscmd_fops = {
.read = btmrvl_pscmd_read,
.write = btmrvl_pscmd_write,
.open = btmrvl_open_generic,
};
static ssize_t btmrvl_gpiogap_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
long result, ret;
memset(buf, 0, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
ret = strict_strtol(buf, 16, &result);
priv->btmrvl_dev.gpio_gap = result;
return count;
}
static ssize_t btmrvl_gpiogap_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "0x%x\n",
priv->btmrvl_dev.gpio_gap);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_gpiogap_fops = {
.read = btmrvl_gpiogap_read,
.write = btmrvl_gpiogap_write,
.open = btmrvl_open_generic,
};
static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = (struct btmrvl_private *) file->private_data;
char buf[16];
long result, ret;
memset(buf, 0, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
ret = strict_strtol(buf, 10, &result);
priv->btmrvl_dev.hscmd = result;
if (priv->btmrvl_dev.hscmd) {
btmrvl_prepare_command(priv);
wake_up_interruptible(&priv->main_thread.wait_q);
}
return count;
}
static ssize_t btmrvl_hscmd_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hscmd);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_hscmd_fops = {
.read = btmrvl_hscmd_read,
.write = btmrvl_hscmd_write,
.open = btmrvl_open_generic,
};
static ssize_t btmrvl_hsmode_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
long result, ret;
memset(buf, 0, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
ret = strict_strtol(buf, 10, &result);
priv->btmrvl_dev.hsmode = result;
return count;
}
static ssize_t btmrvl_hsmode_read(struct file *file, char __user * userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hsmode);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_hsmode_fops = {
.read = btmrvl_hsmode_read,
.write = btmrvl_hsmode_write,
.open = btmrvl_open_generic,
};
static ssize_t btmrvl_curpsmode_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->psmode);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_curpsmode_fops = {
.read = btmrvl_curpsmode_read,
.open = btmrvl_open_generic,
};
static ssize_t btmrvl_psstate_read(struct file *file, char __user * userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->ps_state);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_psstate_fops = {
.read = btmrvl_psstate_read,
.open = btmrvl_open_generic,
};
static ssize_t btmrvl_hsstate_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->adapter->hs_state);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_hsstate_fops = {
.read = btmrvl_hsstate_read,
.open = btmrvl_open_generic,
};
static ssize_t btmrvl_txdnldready_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
priv->btmrvl_dev.tx_dnld_rdy);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_txdnldready_fops = {
.read = btmrvl_txdnldready_read,
.open = btmrvl_open_generic,
};
void btmrvl_debugfs_init(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hdev->driver_data;
struct btmrvl_debugfs_data *dbg;
dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
priv->debugfs_data = dbg;
if (!dbg) {
BT_ERR("Can not allocate memory for btmrvl_debugfs_data.");
return;
}
dbg->root_dir = debugfs_create_dir("btmrvl", NULL);
dbg->config_dir = debugfs_create_dir("config", dbg->root_dir);
dbg->psmode = debugfs_create_file("psmode", 0644, dbg->config_dir,
hdev->driver_data, &btmrvl_psmode_fops);
dbg->pscmd = debugfs_create_file("pscmd", 0644, dbg->config_dir,
hdev->driver_data, &btmrvl_pscmd_fops);
dbg->gpiogap = debugfs_create_file("gpiogap", 0644, dbg->config_dir,
hdev->driver_data, &btmrvl_gpiogap_fops);
dbg->hsmode = debugfs_create_file("hsmode", 0644, dbg->config_dir,
hdev->driver_data, &btmrvl_hsmode_fops);
dbg->hscmd = debugfs_create_file("hscmd", 0644, dbg->config_dir,
hdev->driver_data, &btmrvl_hscmd_fops);
dbg->hscfgcmd = debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
hdev->driver_data, &btmrvl_hscfgcmd_fops);
dbg->status_dir = debugfs_create_dir("status", dbg->root_dir);
dbg->curpsmode = debugfs_create_file("curpsmode", 0444,
dbg->status_dir,
hdev->driver_data,
&btmrvl_curpsmode_fops);
dbg->psstate = debugfs_create_file("psstate", 0444, dbg->status_dir,
hdev->driver_data, &btmrvl_psstate_fops);
dbg->hsstate = debugfs_create_file("hsstate", 0444, dbg->status_dir,
hdev->driver_data, &btmrvl_hsstate_fops);
dbg->txdnldready = debugfs_create_file("txdnldready", 0444,
dbg->status_dir,
hdev->driver_data,
&btmrvl_txdnldready_fops);
}
void btmrvl_debugfs_remove(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hdev->driver_data;
struct btmrvl_debugfs_data *dbg = priv->debugfs_data;
if (!dbg)
return;
debugfs_remove(dbg->psmode);
debugfs_remove(dbg->pscmd);
debugfs_remove(dbg->gpiogap);
debugfs_remove(dbg->hsmode);
debugfs_remove(dbg->hscmd);
debugfs_remove(dbg->hscfgcmd);
debugfs_remove(dbg->config_dir);
debugfs_remove(dbg->curpsmode);
debugfs_remove(dbg->psstate);
debugfs_remove(dbg->hsstate);
debugfs_remove(dbg->txdnldready);
debugfs_remove(dbg->status_dir);
debugfs_remove(dbg->root_dir);
kfree(dbg);
}
/*
* Marvell Bluetooth driver: global definitions & declarations
*
* Copyright (C) 2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
#include <linux/kthread.h>
#include <linux/bitops.h>
#include <net/bluetooth/bluetooth.h>
#define BTM_HEADER_LEN 4
#define BTM_UPLD_SIZE 2312
/* Time to wait until Host Sleep state change in millisecond */
#define WAIT_UNTIL_HS_STATE_CHANGED 5000
/* Time to wait for command response in millisecond */
#define WAIT_UNTIL_CMD_RESP 5000
struct btmrvl_thread {
struct task_struct *task;
wait_queue_head_t wait_q;
void *priv;
};
struct btmrvl_device {
void *card;
struct hci_dev *hcidev;
u8 tx_dnld_rdy;
u8 psmode;
u8 pscmd;
u8 hsmode;
u8 hscmd;
/* Low byte is gap, high byte is GPIO */
u16 gpio_gap;
u8 hscfgcmd;
u8 sendcmdflag;
};
struct btmrvl_adapter {
u32 int_count;
struct sk_buff_head tx_queue;
u8 psmode;
u8 ps_state;
u8 hs_state;
u8 wakeup_tries;
wait_queue_head_t cmd_wait_q;
u8 cmd_complete;
};
struct btmrvl_private {
struct btmrvl_device btmrvl_dev;
struct btmrvl_adapter *adapter;
struct btmrvl_thread main_thread;
int (*hw_host_to_card) (struct btmrvl_private *priv,
u8 *payload, u16 nb);
int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
spinlock_t driver_lock; /* spinlock used by driver */
#ifdef CONFIG_DEBUG_FS
void *debugfs_data;
#endif
};
#define MRVL_VENDOR_PKT 0xFE
/* Bluetooth commands */
#define BT_CMD_AUTO_SLEEP_MODE 0x23
#define BT_CMD_HOST_SLEEP_CONFIG 0x59
#define BT_CMD_HOST_SLEEP_ENABLE 0x5A
#define BT_CMD_MODULE_CFG_REQ 0x5B
/* Sub-commands: Module Bringup/Shutdown Request */
#define MODULE_BRINGUP_REQ 0xF1
#define MODULE_SHUTDOWN_REQ 0xF2
#define BT_EVENT_POWER_STATE 0x20
/* Bluetooth Power States */
#define BT_PS_ENABLE 0x02
#define BT_PS_DISABLE 0x03
#define BT_PS_SLEEP 0x01
#define OGF 0x3F
/* Host Sleep states */
#define HS_ACTIVATED 0x01
#define HS_DEACTIVATED 0x00
/* Power Save modes */
#define PS_SLEEP 0x01
#define PS_AWAKE 0x00
struct btmrvl_cmd {
__le16 ocf_ogf;
u8 length;
u8 data[4];
} __attribute__ ((packed));
struct btmrvl_event {
u8 ec; /* event counter */
u8 length;
u8 data[4];
} __attribute__ ((packed));
/* Prototype of global function */
struct btmrvl_private *btmrvl_add_card(void *card);
int btmrvl_remove_card(struct btmrvl_private *priv);
void btmrvl_interrupt(struct btmrvl_private *priv);
void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd);
int btmrvl_prepare_command(struct btmrvl_private *priv);
#ifdef CONFIG_DEBUG_FS
void btmrvl_debugfs_init(struct hci_dev *hdev);
void btmrvl_debugfs_remove(struct hci_dev *hdev);
#endif
/**
* Marvell Bluetooth driver
*
* Copyright (C) 2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
**/
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btmrvl_drv.h"
#define VERSION "1.0"
/*
* This function is called by interface specific interrupt handler.
* It updates Power Save & Host Sleep states, and wakes up the main
* thread.
*/
void btmrvl_interrupt(struct btmrvl_private *priv)
{
priv->adapter->ps_state = PS_AWAKE;
priv->adapter->wakeup_tries = 0;
priv->adapter->int_count++;
wake_up_interruptible(&priv->main_thread.wait_q);
}
EXPORT_SYMBOL_GPL(btmrvl_interrupt);
void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *) skb->data;
struct hci_ev_cmd_complete *ec;
u16 opcode, ocf;
if (hdr->evt == HCI_EV_CMD_COMPLETE) {
ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE);
opcode = __le16_to_cpu(ec->opcode);
ocf = hci_opcode_ocf(opcode);
if (ocf == BT_CMD_MODULE_CFG_REQ &&
priv->btmrvl_dev.sendcmdflag) {
priv->btmrvl_dev.sendcmdflag = false;
priv->adapter->cmd_complete = true;
wake_up_interruptible(&priv->adapter->cmd_wait_q);
}
}
}
EXPORT_SYMBOL_GPL(btmrvl_check_evtpkt);
int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
{
struct btmrvl_adapter *adapter = priv->adapter;
struct btmrvl_event *event;
u8 ret = 0;
event = (struct btmrvl_event *) skb->data;
if (event->ec != 0xff) {
BT_DBG("Not Marvell Event=%x", event->ec);
ret = -EINVAL;
goto exit;
}
switch (event->data[0]) {
case BT_CMD_AUTO_SLEEP_MODE:
if (!event->data[2]) {
if (event->data[1] == BT_PS_ENABLE)
adapter->psmode = 1;
else
adapter->psmode = 0;
BT_DBG("PS Mode:%s",
(adapter->psmode) ? "Enable" : "Disable");
} else {
BT_DBG("PS Mode command failed");
}
break;
case BT_CMD_HOST_SLEEP_CONFIG:
if (!event->data[3])
BT_DBG("gpio=%x, gap=%x", event->data[1],
event->data[2]);
else
BT_DBG("HSCFG command failed");
break;
case BT_CMD_HOST_SLEEP_ENABLE:
if (!event->data[1]) {
adapter->hs_state = HS_ACTIVATED;
if (adapter->psmode)
adapter->ps_state = PS_SLEEP;
wake_up_interruptible(&adapter->cmd_wait_q);
BT_DBG("HS ACTIVATED!");
} else {
BT_DBG("HS Enable failed");
}
break;
case BT_CMD_MODULE_CFG_REQ:
if (priv->btmrvl_dev.sendcmdflag &&
event->data[1] == MODULE_BRINGUP_REQ) {
BT_DBG("EVENT:%s", (event->data[2]) ?
"Bring-up failed" : "Bring-up succeed");
} else if (priv->btmrvl_dev.sendcmdflag &&
event->data[1] == MODULE_SHUTDOWN_REQ) {
BT_DBG("EVENT:%s", (event->data[2]) ?
"Shutdown failed" : "Shutdown succeed");
} else {
BT_DBG("BT_CMD_MODULE_CFG_REQ resp for APP");
ret = -EINVAL;
}
break;
case BT_EVENT_POWER_STATE:
if (event->data[1] == BT_PS_SLEEP)
adapter->ps_state = PS_SLEEP;
BT_DBG("EVENT:%s",
(adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
break;
default:
BT_DBG("Unknown Event=%d", event->data[0]);
ret = -EINVAL;
break;
}
exit:
if (!ret)
kfree_skb(skb);
return ret;
}
EXPORT_SYMBOL_GPL(btmrvl_process_event);
int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
{
struct sk_buff *skb;
struct btmrvl_cmd *cmd;
int ret = 0;
skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
if (skb == NULL) {
BT_ERR("No free skb");
return -ENOMEM;
}
cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_MODULE_CFG_REQ));
cmd->length = 1;
cmd->data[0] = subcmd;
bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
skb->dev = (void *) priv->btmrvl_dev.hcidev;
skb_queue_head(&priv->adapter->tx_queue, skb);
priv->btmrvl_dev.sendcmdflag = true;
priv->adapter->cmd_complete = false;
BT_DBG("Queue module cfg Command");
wake_up_interruptible(&priv->main_thread.wait_q);
if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
priv->adapter->cmd_complete,
msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) {
ret = -ETIMEDOUT;
BT_ERR("module_cfg_cmd(%x): timeout: %d",
subcmd, priv->btmrvl_dev.sendcmdflag);
}
BT_DBG("module cfg Command done");
return ret;
}
EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
static int btmrvl_enable_hs(struct btmrvl_private *priv)
{
struct sk_buff *skb;
struct btmrvl_cmd *cmd;
int ret = 0;
skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
if (skb == NULL) {
BT_ERR("No free skb");
return -ENOMEM;
}
cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_ENABLE));
cmd->length = 0;
bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
skb->dev = (void *) priv->btmrvl_dev.hcidev;
skb_queue_head(&priv->adapter->tx_queue, skb);
BT_DBG("Queue hs enable Command");
wake_up_interruptible(&priv->main_thread.wait_q);
if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
priv->adapter->hs_state,
msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED))) {
ret = -ETIMEDOUT;
BT_ERR("timeout: %d, %d,%d", priv->adapter->hs_state,
priv->adapter->ps_state,
priv->adapter->wakeup_tries);
}
return ret;
}
int btmrvl_prepare_command(struct btmrvl_private *priv)
{
struct sk_buff *skb = NULL;
struct btmrvl_cmd *cmd;
int ret = 0;
if (priv->btmrvl_dev.hscfgcmd) {
priv->btmrvl_dev.hscfgcmd = 0;
skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
if (skb == NULL) {
BT_ERR("No free skb");
return -ENOMEM;
}
cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_CONFIG));
cmd->length = 2;
cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);
bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
skb->dev = (void *) priv->btmrvl_dev.hcidev;
skb_queue_head(&priv->adapter->tx_queue, skb);
BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x",
cmd->data[0], cmd->data[1]);
}
if (priv->btmrvl_dev.pscmd) {
priv->btmrvl_dev.pscmd = 0;
skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
if (skb == NULL) {
BT_ERR("No free skb");
return -ENOMEM;
}
cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_AUTO_SLEEP_MODE));
cmd->length = 1;
if (priv->btmrvl_dev.psmode)
cmd->data[0] = BT_PS_ENABLE;
else
cmd->data[0] = BT_PS_DISABLE;
bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
skb->dev = (void *) priv->btmrvl_dev.hcidev;
skb_queue_head(&priv->adapter->tx_queue, skb);
BT_DBG("Queue PSMODE Command:%d", cmd->data[0]);
}
if (priv->btmrvl_dev.hscmd) {
priv->btmrvl_dev.hscmd = 0;
if (priv->btmrvl_dev.hsmode) {
ret = btmrvl_enable_hs(priv);
} else {
ret = priv->hw_wakeup_firmware(priv);
priv->adapter->hs_state = HS_DEACTIVATED;
}
}
return ret;
}
static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
{
int ret = 0;
if (!skb || !skb->data)
return -EINVAL;
if (!skb->len || ((skb->len + BTM_HEADER_LEN) > BTM_UPLD_SIZE)) {
BT_ERR("Tx Error: Bad skb length %d : %d",
skb->len, BTM_UPLD_SIZE);
return -EINVAL;
}
if (skb_headroom(skb) < BTM_HEADER_LEN) {
struct sk_buff *tmp = skb;
skb = skb_realloc_headroom(skb, BTM_HEADER_LEN);
if (!skb) {
BT_ERR("Tx Error: realloc_headroom failed %d",
BTM_HEADER_LEN);
skb = tmp;
return -EINVAL;
}
kfree_skb(tmp);
}
skb_push(skb, BTM_HEADER_LEN);
/* header type: byte[3]
* HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor
* header length: byte[2][1][0]
*/
skb->data[0] = (skb->len & 0x0000ff);
skb->data[1] = (skb->len & 0x00ff00) >> 8;
skb->data[2] = (skb->len & 0xff0000) >> 16;
skb->data[3] = bt_cb(skb)->pkt_type;
if (priv->hw_host_to_card)
ret = priv->hw_host_to_card(priv, skb->data, skb->len);
return ret;
}
static void btmrvl_init_adapter(struct btmrvl_private *priv)
{
skb_queue_head_init(&priv->adapter->tx_queue);
priv->adapter->ps_state = PS_AWAKE;
init_waitqueue_head(&priv->adapter->cmd_wait_q);
}
static void btmrvl_free_adapter(struct btmrvl_private *priv)
{
skb_queue_purge(&priv->adapter->tx_queue);
kfree(priv->adapter);
priv->adapter = NULL;
}
static int btmrvl_ioctl(struct hci_dev *hdev,
unsigned int cmd, unsigned long arg)
{
return -ENOIOCTLCMD;
}
static void btmrvl_destruct(struct hci_dev *hdev)
{
}
static int btmrvl_send_frame(struct sk_buff *skb)
{
struct hci_dev *hdev = (struct hci_dev *) skb->dev;
struct btmrvl_private *priv = NULL;
BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
if (!hdev || !hdev->driver_data) {
BT_ERR("Frame for unknown HCI device");
return -ENODEV;
}
priv = (struct btmrvl_private *) hdev->driver_data;
if (!test_bit(HCI_RUNNING, &hdev->flags)) {
BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags);
print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET,
skb->data, skb->len);
return -EBUSY;
}
switch (bt_cb(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;
}
skb_queue_tail(&priv->adapter->tx_queue, skb);
wake_up_interruptible(&priv->main_thread.wait_q);
return 0;
}
static int btmrvl_flush(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hdev->driver_data;
skb_queue_purge(&priv->adapter->tx_queue);
return 0;
}
static int btmrvl_close(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hdev->driver_data;
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0;
skb_queue_purge(&priv->adapter->tx_queue);
return 0;
}
static int btmrvl_open(struct hci_dev *hdev)
{
set_bit(HCI_RUNNING, &hdev->flags);
return 0;
}
/*
* This function handles the event generated by firmware, rx data
* received from firmware, and tx data sent from kernel.
*/
static int btmrvl_service_main_thread(void *data)
{
struct btmrvl_thread *thread = data;
struct btmrvl_private *priv = thread->priv;
struct btmrvl_adapter *adapter = priv->adapter;
wait_queue_t wait;
struct sk_buff *skb;
ulong flags;
init_waitqueue_entry(&wait, current);
current->flags |= PF_NOFREEZE;
for (;;) {
add_wait_queue(&thread->wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
if (adapter->wakeup_tries ||
((!adapter->int_count) &&
(!priv->btmrvl_dev.tx_dnld_rdy ||
skb_queue_empty(&adapter->tx_queue)))) {
BT_DBG("main_thread is sleeping...");
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&thread->wait_q, &wait);
BT_DBG("main_thread woke up");
if (kthread_should_stop()) {
BT_DBG("main_thread: break from main thread");
break;
}
spin_lock_irqsave(&priv->driver_lock, flags);
if (adapter->int_count) {
adapter->int_count = 0;
} else if (adapter->ps_state == PS_SLEEP &&
!skb_queue_empty(&adapter->tx_queue)) {
spin_unlock_irqrestore(&priv->driver_lock, flags);
adapter->wakeup_tries++;
priv->hw_wakeup_firmware(priv);
continue;
}
spin_unlock_irqrestore(&priv->driver_lock, flags);
if (adapter->ps_state == PS_SLEEP)
continue;
if (!priv->btmrvl_dev.tx_dnld_rdy)
continue;
skb = skb_dequeue(&adapter->tx_queue);
if (skb) {
if (btmrvl_tx_pkt(priv, skb))
priv->btmrvl_dev.hcidev->stat.err_tx++;
else
priv->btmrvl_dev.hcidev->stat.byte_tx += skb->len;
kfree_skb(skb);
}
}
return 0;
}
struct btmrvl_private *btmrvl_add_card(void *card)
{
struct hci_dev *hdev = NULL;
struct btmrvl_private *priv;
int ret;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
BT_ERR("Can not allocate priv");
goto err_priv;
}
priv->adapter = kzalloc(sizeof(*priv->adapter), GFP_KERNEL);
if (!priv->adapter) {
BT_ERR("Allocate buffer for btmrvl_adapter failed!");
goto err_adapter;
}
btmrvl_init_adapter(priv);
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Can not allocate HCI device");
goto err_hdev;
}
BT_DBG("Starting kthread...");
priv->main_thread.priv = priv;
spin_lock_init(&priv->driver_lock);
init_waitqueue_head(&priv->main_thread.wait_q);
priv->main_thread.task = kthread_run(btmrvl_service_main_thread,
&priv->main_thread, "btmrvl_main_service");
priv->btmrvl_dev.hcidev = hdev;
priv->btmrvl_dev.card = card;
hdev->driver_data = priv;
priv->btmrvl_dev.tx_dnld_rdy = true;
hdev->type = HCI_SDIO;
hdev->open = btmrvl_open;
hdev->close = btmrvl_close;
hdev->flush = btmrvl_flush;
hdev->send = btmrvl_send_frame;
hdev->destruct = btmrvl_destruct;
hdev->ioctl = btmrvl_ioctl;
hdev->owner = THIS_MODULE;
ret = hci_register_dev(hdev);
if (ret < 0) {
BT_ERR("Can not register HCI device");
goto err_hci_register_dev;
}
#ifdef CONFIG_DEBUG_FS
btmrvl_debugfs_init(hdev);
#endif
return priv;
err_hci_register_dev:
/* Stop the thread servicing the interrupts */
kthread_stop(priv->main_thread.task);
hci_free_dev(hdev);
err_hdev:
btmrvl_free_adapter(priv);
err_adapter:
kfree(priv);
err_priv:
return NULL;
}
EXPORT_SYMBOL_GPL(btmrvl_add_card);
int btmrvl_remove_card(struct btmrvl_private *priv)
{
struct hci_dev *hdev;
hdev = priv->btmrvl_dev.hcidev;
wake_up_interruptible(&priv->adapter->cmd_wait_q);
kthread_stop(priv->main_thread.task);
#ifdef CONFIG_DEBUG_FS
btmrvl_debugfs_remove(hdev);
#endif
hci_unregister_dev(hdev);
hci_free_dev(hdev);
priv->btmrvl_dev.hcidev = NULL;
btmrvl_free_adapter(priv);
kfree(priv);
return 0;
}
EXPORT_SYMBOL_GPL(btmrvl_remove_card);
MODULE_AUTHOR("Marvell International Ltd.");
MODULE_DESCRIPTION("Marvell Bluetooth driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL v2");
此差异已折叠。
/**
* Marvell BT-over-SDIO driver: SDIO interface related definitions
*
* Copyright (C) 2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
**/
#define SDIO_HEADER_LEN 4
/* SD block size can not bigger than 64 due to buf size limit in firmware */
/* define SD block size for data Tx/Rx */
#define SDIO_BLOCK_SIZE 64
/* Number of blocks for firmware transfer */
#define FIRMWARE_TRANSFER_NBLOCK 2
/* This is for firmware specific length */
#define FW_EXTRA_LEN 36
#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024)
#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
(HCI_MAX_FRAME_SIZE + FW_EXTRA_LEN)
#define ALLOC_BUF_SIZE (((max_t (int, MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+ SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \
* SDIO_BLOCK_SIZE)
/* The number of times to try when polling for status */
#define MAX_POLL_TRIES 100
/* Max retry number of CMD53 write */
#define MAX_WRITE_IOMEM_RETRY 2
/* Host Control Registers */
#define IO_PORT_0_REG 0x00
#define IO_PORT_1_REG 0x01
#define IO_PORT_2_REG 0x02
#define CONFIG_REG 0x03
#define HOST_POWER_UP BIT(1)
#define HOST_CMD53_FIN BIT(2)
#define HOST_INT_MASK_REG 0x04
#define HIM_DISABLE 0xff
#define HIM_ENABLE (BIT(0) | BIT(1))
#define HOST_INTSTATUS_REG 0x05
#define UP_LD_HOST_INT_STATUS BIT(0)
#define DN_LD_HOST_INT_STATUS BIT(1)
/* Card Control Registers */
#define SQ_READ_BASE_ADDRESS_A0_REG 0x10
#define SQ_READ_BASE_ADDRESS_A1_REG 0x11
#define CARD_STATUS_REG 0x20
#define DN_LD_CARD_RDY BIT(0)
#define CARD_IO_READY BIT(3)
#define CARD_FW_STATUS0_REG 0x40
#define CARD_FW_STATUS1_REG 0x41
#define FIRMWARE_READY 0xfedc
#define CARD_RX_LEN_REG 0x42
#define CARD_RX_UNIT_REG 0x43
struct btmrvl_sdio_card {
struct sdio_func *func;
u32 ioport;
const char *helper;
const char *firmware;
u8 rx_unit;
struct btmrvl_private *priv;
};
struct btmrvl_sdio_device {
const char *helper;
const char *firmware;
};
/* Platform specific DMA alignment */
#define BTSDIO_DMA_ALIGN 8
/* Macros for Data Alignment : size */
#define ALIGN_SZ(p, a) \
(((p) + ((a) - 1)) & ~((a) - 1))
/* Macros for Data Alignment : address */
#define ALIGN_ADDR(p, a) \
((((unsigned long)(p)) + (((unsigned long)(a)) - 1)) & \
~(((unsigned long)(a)) - 1))
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
#define VERSION "0.5" #define VERSION "0.6"
static int ignore_dga; static int ignore_dga;
static int ignore_csr; static int ignore_csr;
...@@ -145,6 +145,7 @@ static struct usb_device_id blacklist_table[] = { ...@@ -145,6 +145,7 @@ static struct usb_device_id blacklist_table[] = {
#define BTUSB_INTR_RUNNING 0 #define BTUSB_INTR_RUNNING 0
#define BTUSB_BULK_RUNNING 1 #define BTUSB_BULK_RUNNING 1
#define BTUSB_ISOC_RUNNING 2 #define BTUSB_ISOC_RUNNING 2
#define BTUSB_SUSPENDING 3
struct btusb_data { struct btusb_data {
struct hci_dev *hdev; struct hci_dev *hdev;
...@@ -157,11 +158,15 @@ struct btusb_data { ...@@ -157,11 +158,15 @@ struct btusb_data {
unsigned long flags; unsigned long flags;
struct work_struct work; struct work_struct work;
struct work_struct waker;
struct usb_anchor tx_anchor; struct usb_anchor tx_anchor;
struct usb_anchor intr_anchor; struct usb_anchor intr_anchor;
struct usb_anchor bulk_anchor; struct usb_anchor bulk_anchor;
struct usb_anchor isoc_anchor; struct usb_anchor isoc_anchor;
struct usb_anchor deferred;
int tx_in_flight;
spinlock_t txlock;
struct usb_endpoint_descriptor *intr_ep; struct usb_endpoint_descriptor *intr_ep;
struct usb_endpoint_descriptor *bulk_tx_ep; struct usb_endpoint_descriptor *bulk_tx_ep;
...@@ -174,8 +179,23 @@ struct btusb_data { ...@@ -174,8 +179,23 @@ struct btusb_data {
unsigned int sco_num; unsigned int sco_num;
int isoc_altsetting; int isoc_altsetting;
int suspend_count; int suspend_count;
int did_iso_resume:1;
}; };
static int inc_tx(struct btusb_data *data)
{
unsigned long flags;
int rv;
spin_lock_irqsave(&data->txlock, flags);
rv = test_bit(BTUSB_SUSPENDING, &data->flags);
if (!rv)
data->tx_in_flight++;
spin_unlock_irqrestore(&data->txlock, flags);
return rv;
}
static void btusb_intr_complete(struct urb *urb) static void btusb_intr_complete(struct urb *urb)
{ {
struct hci_dev *hdev = urb->context; struct hci_dev *hdev = urb->context;
...@@ -202,6 +222,7 @@ static void btusb_intr_complete(struct urb *urb) ...@@ -202,6 +222,7 @@ static void btusb_intr_complete(struct urb *urb)
if (!test_bit(BTUSB_INTR_RUNNING, &data->flags)) if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
return; return;
usb_mark_last_busy(data->udev);
usb_anchor_urb(urb, &data->intr_anchor); usb_anchor_urb(urb, &data->intr_anchor);
err = usb_submit_urb(urb, GFP_ATOMIC); err = usb_submit_urb(urb, GFP_ATOMIC);
...@@ -301,7 +322,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) ...@@ -301,7 +322,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
struct urb *urb; struct urb *urb;
unsigned char *buf; unsigned char *buf;
unsigned int pipe; unsigned int pipe;
int err, size; int err, size = HCI_MAX_FRAME_SIZE;
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
...@@ -312,8 +333,6 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) ...@@ -312,8 +333,6 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
if (!urb) if (!urb)
return -ENOMEM; return -ENOMEM;
size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
buf = kmalloc(size, mem_flags); buf = kmalloc(size, mem_flags);
if (!buf) { if (!buf) {
usb_free_urb(urb); usb_free_urb(urb);
...@@ -327,6 +346,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) ...@@ -327,6 +346,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
urb->transfer_flags |= URB_FREE_BUFFER; urb->transfer_flags |= URB_FREE_BUFFER;
usb_mark_last_busy(data->udev);
usb_anchor_urb(urb, &data->bulk_anchor); usb_anchor_urb(urb, &data->bulk_anchor);
err = usb_submit_urb(urb, mem_flags); err = usb_submit_urb(urb, mem_flags);
...@@ -462,6 +482,33 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags) ...@@ -462,6 +482,33 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
} }
static void btusb_tx_complete(struct urb *urb) static void btusb_tx_complete(struct urb *urb)
{
struct sk_buff *skb = urb->context;
struct hci_dev *hdev = (struct hci_dev *) skb->dev;
struct btusb_data *data = hdev->driver_data;
BT_DBG("%s urb %p status %d count %d", hdev->name,
urb, urb->status, urb->actual_length);
if (!test_bit(HCI_RUNNING, &hdev->flags))
goto done;
if (!urb->status)
hdev->stat.byte_tx += urb->transfer_buffer_length;
else
hdev->stat.err_tx++;
done:
spin_lock(&data->txlock);
data->tx_in_flight--;
spin_unlock(&data->txlock);
kfree(urb->setup_packet);
kfree_skb(skb);
}
static void btusb_isoc_tx_complete(struct urb *urb)
{ {
struct sk_buff *skb = urb->context; struct sk_buff *skb = urb->context;
struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct hci_dev *hdev = (struct hci_dev *) skb->dev;
...@@ -490,11 +537,17 @@ static int btusb_open(struct hci_dev *hdev) ...@@ -490,11 +537,17 @@ static int btusb_open(struct hci_dev *hdev)
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
err = usb_autopm_get_interface(data->intf);
if (err < 0)
return err;
data->intf->needs_remote_wakeup = 1;
if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
return 0; goto done;
if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
return 0; goto done;
err = btusb_submit_intr_urb(hdev, GFP_KERNEL); err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
if (err < 0) if (err < 0)
...@@ -509,17 +562,28 @@ static int btusb_open(struct hci_dev *hdev) ...@@ -509,17 +562,28 @@ static int btusb_open(struct hci_dev *hdev)
set_bit(BTUSB_BULK_RUNNING, &data->flags); set_bit(BTUSB_BULK_RUNNING, &data->flags);
btusb_submit_bulk_urb(hdev, GFP_KERNEL); btusb_submit_bulk_urb(hdev, GFP_KERNEL);
done:
usb_autopm_put_interface(data->intf);
return 0; return 0;
failed: failed:
clear_bit(BTUSB_INTR_RUNNING, &data->flags); clear_bit(BTUSB_INTR_RUNNING, &data->flags);
clear_bit(HCI_RUNNING, &hdev->flags); clear_bit(HCI_RUNNING, &hdev->flags);
usb_autopm_put_interface(data->intf);
return err; return err;
} }
static void btusb_stop_traffic(struct btusb_data *data)
{
usb_kill_anchored_urbs(&data->intr_anchor);
usb_kill_anchored_urbs(&data->bulk_anchor);
usb_kill_anchored_urbs(&data->isoc_anchor);
}
static int btusb_close(struct hci_dev *hdev) static int btusb_close(struct hci_dev *hdev)
{ {
struct btusb_data *data = hdev->driver_data; struct btusb_data *data = hdev->driver_data;
int err;
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
...@@ -529,13 +593,16 @@ static int btusb_close(struct hci_dev *hdev) ...@@ -529,13 +593,16 @@ static int btusb_close(struct hci_dev *hdev)
cancel_work_sync(&data->work); cancel_work_sync(&data->work);
clear_bit(BTUSB_ISOC_RUNNING, &data->flags); clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->isoc_anchor);
clear_bit(BTUSB_BULK_RUNNING, &data->flags); clear_bit(BTUSB_BULK_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->bulk_anchor);
clear_bit(BTUSB_INTR_RUNNING, &data->flags); clear_bit(BTUSB_INTR_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->intr_anchor);
btusb_stop_traffic(data);
err = usb_autopm_get_interface(data->intf);
if (err < 0)
return 0;
data->intf->needs_remote_wakeup = 0;
usb_autopm_put_interface(data->intf);
return 0; return 0;
} }
...@@ -622,7 +689,7 @@ static int btusb_send_frame(struct sk_buff *skb) ...@@ -622,7 +689,7 @@ static int btusb_send_frame(struct sk_buff *skb)
urb->dev = data->udev; urb->dev = data->udev;
urb->pipe = pipe; urb->pipe = pipe;
urb->context = skb; urb->context = skb;
urb->complete = btusb_tx_complete; urb->complete = btusb_isoc_tx_complete;
urb->interval = data->isoc_tx_ep->bInterval; urb->interval = data->isoc_tx_ep->bInterval;
urb->transfer_flags = URB_ISO_ASAP; urb->transfer_flags = URB_ISO_ASAP;
...@@ -633,12 +700,21 @@ static int btusb_send_frame(struct sk_buff *skb) ...@@ -633,12 +700,21 @@ static int btusb_send_frame(struct sk_buff *skb)
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize)); le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
hdev->stat.sco_tx++; hdev->stat.sco_tx++;
break; goto skip_waking;
default: default:
return -EILSEQ; return -EILSEQ;
} }
err = inc_tx(data);
if (err) {
usb_anchor_urb(urb, &data->deferred);
schedule_work(&data->waker);
err = 0;
goto done;
}
skip_waking:
usb_anchor_urb(urb, &data->tx_anchor); usb_anchor_urb(urb, &data->tx_anchor);
err = usb_submit_urb(urb, GFP_ATOMIC); err = usb_submit_urb(urb, GFP_ATOMIC);
...@@ -646,10 +722,13 @@ static int btusb_send_frame(struct sk_buff *skb) ...@@ -646,10 +722,13 @@ static int btusb_send_frame(struct sk_buff *skb)
BT_ERR("%s urb %p submission failed", hdev->name, urb); BT_ERR("%s urb %p submission failed", hdev->name, urb);
kfree(urb->setup_packet); kfree(urb->setup_packet);
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
} else {
usb_mark_last_busy(data->udev);
} }
usb_free_urb(urb); usb_free_urb(urb);
done:
return err; return err;
} }
...@@ -721,8 +800,19 @@ static void btusb_work(struct work_struct *work) ...@@ -721,8 +800,19 @@ static void btusb_work(struct work_struct *work)
{ {
struct btusb_data *data = container_of(work, struct btusb_data, work); struct btusb_data *data = container_of(work, struct btusb_data, work);
struct hci_dev *hdev = data->hdev; struct hci_dev *hdev = data->hdev;
int err;
if (hdev->conn_hash.sco_num > 0) { if (hdev->conn_hash.sco_num > 0) {
if (!data->did_iso_resume) {
err = usb_autopm_get_interface(data->isoc);
if (err < 0) {
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->isoc_anchor);
return;
}
data->did_iso_resume = 1;
}
if (data->isoc_altsetting != 2) { if (data->isoc_altsetting != 2) {
clear_bit(BTUSB_ISOC_RUNNING, &data->flags); clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->isoc_anchor); usb_kill_anchored_urbs(&data->isoc_anchor);
...@@ -742,9 +832,25 @@ static void btusb_work(struct work_struct *work) ...@@ -742,9 +832,25 @@ static void btusb_work(struct work_struct *work)
usb_kill_anchored_urbs(&data->isoc_anchor); usb_kill_anchored_urbs(&data->isoc_anchor);
__set_isoc_interface(hdev, 0); __set_isoc_interface(hdev, 0);
if (data->did_iso_resume) {
data->did_iso_resume = 0;
usb_autopm_put_interface(data->isoc);
}
} }
} }
static void btusb_waker(struct work_struct *work)
{
struct btusb_data *data = container_of(work, struct btusb_data, waker);
int err;
err = usb_autopm_get_interface(data->intf);
if (err < 0)
return;
usb_autopm_put_interface(data->intf);
}
static int btusb_probe(struct usb_interface *intf, static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
...@@ -814,11 +920,14 @@ static int btusb_probe(struct usb_interface *intf, ...@@ -814,11 +920,14 @@ static int btusb_probe(struct usb_interface *intf,
spin_lock_init(&data->lock); spin_lock_init(&data->lock);
INIT_WORK(&data->work, btusb_work); INIT_WORK(&data->work, btusb_work);
INIT_WORK(&data->waker, btusb_waker);
spin_lock_init(&data->txlock);
init_usb_anchor(&data->tx_anchor); init_usb_anchor(&data->tx_anchor);
init_usb_anchor(&data->intr_anchor); init_usb_anchor(&data->intr_anchor);
init_usb_anchor(&data->bulk_anchor); init_usb_anchor(&data->bulk_anchor);
init_usb_anchor(&data->isoc_anchor); init_usb_anchor(&data->isoc_anchor);
init_usb_anchor(&data->deferred);
hdev = hci_alloc_dev(); hdev = hci_alloc_dev();
if (!hdev) { if (!hdev) {
...@@ -943,6 +1052,7 @@ static void btusb_disconnect(struct usb_interface *intf) ...@@ -943,6 +1052,7 @@ static void btusb_disconnect(struct usb_interface *intf)
hci_free_dev(hdev); hci_free_dev(hdev);
} }
#ifdef CONFIG_PM
static int btusb_suspend(struct usb_interface *intf, pm_message_t message) static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
{ {
struct btusb_data *data = usb_get_intfdata(intf); struct btusb_data *data = usb_get_intfdata(intf);
...@@ -952,22 +1062,44 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -952,22 +1062,44 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
if (data->suspend_count++) if (data->suspend_count++)
return 0; return 0;
spin_lock_irq(&data->txlock);
if (!(interface_to_usbdev(intf)->auto_pm && data->tx_in_flight)) {
set_bit(BTUSB_SUSPENDING, &data->flags);
spin_unlock_irq(&data->txlock);
} else {
spin_unlock_irq(&data->txlock);
data->suspend_count--;
return -EBUSY;
}
cancel_work_sync(&data->work); cancel_work_sync(&data->work);
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor); usb_kill_anchored_urbs(&data->tx_anchor);
usb_kill_anchored_urbs(&data->isoc_anchor);
usb_kill_anchored_urbs(&data->bulk_anchor);
usb_kill_anchored_urbs(&data->intr_anchor);
return 0; return 0;
} }
static void play_deferred(struct btusb_data *data)
{
struct urb *urb;
int err;
while ((urb = usb_get_from_anchor(&data->deferred))) {
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0)
break;
data->tx_in_flight++;
}
usb_scuttle_anchored_urbs(&data->deferred);
}
static int btusb_resume(struct usb_interface *intf) static int btusb_resume(struct usb_interface *intf)
{ {
struct btusb_data *data = usb_get_intfdata(intf); struct btusb_data *data = usb_get_intfdata(intf);
struct hci_dev *hdev = data->hdev; struct hci_dev *hdev = data->hdev;
int err; int err = 0;
BT_DBG("intf %p", intf); BT_DBG("intf %p", intf);
...@@ -975,13 +1107,13 @@ static int btusb_resume(struct usb_interface *intf) ...@@ -975,13 +1107,13 @@ static int btusb_resume(struct usb_interface *intf)
return 0; return 0;
if (!test_bit(HCI_RUNNING, &hdev->flags)) if (!test_bit(HCI_RUNNING, &hdev->flags))
return 0; goto done;
if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) { if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
err = btusb_submit_intr_urb(hdev, GFP_NOIO); err = btusb_submit_intr_urb(hdev, GFP_NOIO);
if (err < 0) { if (err < 0) {
clear_bit(BTUSB_INTR_RUNNING, &data->flags); clear_bit(BTUSB_INTR_RUNNING, &data->flags);
return err; goto failed;
} }
} }
...@@ -989,9 +1121,10 @@ static int btusb_resume(struct usb_interface *intf) ...@@ -989,9 +1121,10 @@ static int btusb_resume(struct usb_interface *intf)
err = btusb_submit_bulk_urb(hdev, GFP_NOIO); err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
if (err < 0) { if (err < 0) {
clear_bit(BTUSB_BULK_RUNNING, &data->flags); clear_bit(BTUSB_BULK_RUNNING, &data->flags);
return err; goto failed;
} else }
btusb_submit_bulk_urb(hdev, GFP_NOIO);
btusb_submit_bulk_urb(hdev, GFP_NOIO);
} }
if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) { if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
...@@ -1001,16 +1134,35 @@ static int btusb_resume(struct usb_interface *intf) ...@@ -1001,16 +1134,35 @@ static int btusb_resume(struct usb_interface *intf)
btusb_submit_isoc_urb(hdev, GFP_NOIO); btusb_submit_isoc_urb(hdev, GFP_NOIO);
} }
spin_lock_irq(&data->txlock);
play_deferred(data);
clear_bit(BTUSB_SUSPENDING, &data->flags);
spin_unlock_irq(&data->txlock);
schedule_work(&data->work);
return 0; return 0;
failed:
usb_scuttle_anchored_urbs(&data->deferred);
done:
spin_lock_irq(&data->txlock);
clear_bit(BTUSB_SUSPENDING, &data->flags);
spin_unlock_irq(&data->txlock);
return err;
} }
#endif
static struct usb_driver btusb_driver = { static struct usb_driver btusb_driver = {
.name = "btusb", .name = "btusb",
.probe = btusb_probe, .probe = btusb_probe,
.disconnect = btusb_disconnect, .disconnect = btusb_disconnect,
#ifdef CONFIG_PM
.suspend = btusb_suspend, .suspend = btusb_suspend,
.resume = btusb_resume, .resume = btusb_resume,
#endif
.id_table = btusb_table, .id_table = btusb_table,
.supports_autosuspend = 1,
}; };
static int __init btusb_init(void) static int __init btusb_init(void)
......
...@@ -373,8 +373,9 @@ static void bcsp_pkt_cull(struct bcsp_struct *bcsp) ...@@ -373,8 +373,9 @@ static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
i = 0; i = 0;
skb_queue_walk_safe(&bcsp->unack, skb, tmp) { skb_queue_walk_safe(&bcsp->unack, skb, tmp) {
if (i++ >= pkts_to_be_removed) if (i >= pkts_to_be_removed)
break; break;
i++;
__skb_unlink(skb, &bcsp->unack); __skb_unlink(skb, &bcsp->unack);
kfree_skb(skb); kfree_skb(skb);
......
...@@ -138,8 +138,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock); ...@@ -138,8 +138,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock);
struct bt_skb_cb { struct bt_skb_cb {
__u8 pkt_type; __u8 pkt_type;
__u8 incoming; __u8 incoming;
__u8 tx_seq;
__u8 retries;
__u8 sar;
}; };
#define bt_cb(skb) ((struct bt_skb_cb *)(skb->cb)) #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb))
static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how) static inline struct sk_buff *bt_skb_alloc(unsigned int len, gfp_t how)
{ {
......
...@@ -117,7 +117,7 @@ struct hci_dev { ...@@ -117,7 +117,7 @@ struct hci_dev {
struct sk_buff *sent_cmd; struct sk_buff *sent_cmd;
struct sk_buff *reassembly[3]; struct sk_buff *reassembly[3];
struct semaphore req_lock; struct mutex req_lock;
wait_queue_head_t req_wait_q; wait_queue_head_t req_wait_q;
__u32 req_status; __u32 req_status;
__u32 req_result; __u32 req_result;
...@@ -187,6 +187,7 @@ struct hci_conn { ...@@ -187,6 +187,7 @@ struct hci_conn {
struct work_struct work_del; struct work_struct work_del;
struct device dev; struct device dev;
atomic_t devref;
struct hci_dev *hdev; struct hci_dev *hdev;
void *l2cap_data; void *l2cap_data;
...@@ -339,6 +340,9 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role); ...@@ -339,6 +340,9 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
void hci_conn_enter_active_mode(struct hci_conn *conn); void hci_conn_enter_active_mode(struct hci_conn *conn);
void hci_conn_enter_sniff_mode(struct hci_conn *conn); void hci_conn_enter_sniff_mode(struct hci_conn *conn);
void hci_conn_hold_device(struct hci_conn *conn);
void hci_conn_put_device(struct hci_conn *conn);
static inline void hci_conn_hold(struct hci_conn *conn) static inline void hci_conn_hold(struct hci_conn *conn)
{ {
atomic_inc(&conn->refcnt); atomic_inc(&conn->refcnt);
...@@ -700,8 +704,8 @@ struct hci_sec_filter { ...@@ -700,8 +704,8 @@ struct hci_sec_filter {
#define HCI_REQ_PEND 1 #define HCI_REQ_PEND 1
#define HCI_REQ_CANCELED 2 #define HCI_REQ_CANCELED 2
#define hci_req_lock(d) down(&d->req_lock) #define hci_req_lock(d) mutex_lock(&d->req_lock)
#define hci_req_unlock(d) up(&d->req_lock) #define hci_req_unlock(d) mutex_unlock(&d->req_lock)
void hci_req_complete(struct hci_dev *hdev, int result); void hci_req_complete(struct hci_dev *hdev, int result);
......
...@@ -27,12 +27,14 @@ ...@@ -27,12 +27,14 @@
/* L2CAP defaults */ /* L2CAP defaults */
#define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_MTU 672
#define L2CAP_DEFAULT_MIN_MTU 48
#define L2CAP_DEFAULT_FLUSH_TO 0xffff #define L2CAP_DEFAULT_FLUSH_TO 0xffff
#define L2CAP_DEFAULT_RX_WINDOW 1 #define L2CAP_DEFAULT_TX_WINDOW 63
#define L2CAP_DEFAULT_MAX_RECEIVE 1 #define L2CAP_DEFAULT_NUM_TO_ACK (L2CAP_DEFAULT_TX_WINDOW/5)
#define L2CAP_DEFAULT_RETRANS_TO 300 /* 300 milliseconds */ #define L2CAP_DEFAULT_MAX_TX 3
#define L2CAP_DEFAULT_MONITOR_TO 1000 /* 1 second */ #define L2CAP_DEFAULT_RETRANS_TO 1000 /* 1 second */
#define L2CAP_DEFAULT_MAX_RX_APDU 0xfff7 #define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */
#define L2CAP_DEFAULT_MAX_PDU_SIZE 672
#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */ #define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */
#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */ #define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */
...@@ -52,6 +54,7 @@ struct l2cap_options { ...@@ -52,6 +54,7 @@ struct l2cap_options {
__u16 imtu; __u16 imtu;
__u16 flush_to; __u16 flush_to;
__u8 mode; __u8 mode;
__u8 fcs;
}; };
#define L2CAP_CONNINFO 0x02 #define L2CAP_CONNINFO 0x02
...@@ -93,6 +96,32 @@ struct l2cap_conninfo { ...@@ -93,6 +96,32 @@ struct l2cap_conninfo {
#define L2CAP_FCS_NONE 0x00 #define L2CAP_FCS_NONE 0x00
#define L2CAP_FCS_CRC16 0x01 #define L2CAP_FCS_CRC16 0x01
/* L2CAP Control Field bit masks */
#define L2CAP_CTRL_SAR 0xC000
#define L2CAP_CTRL_REQSEQ 0x3F00
#define L2CAP_CTRL_TXSEQ 0x007E
#define L2CAP_CTRL_RETRANS 0x0080
#define L2CAP_CTRL_FINAL 0x0080
#define L2CAP_CTRL_POLL 0x0010
#define L2CAP_CTRL_SUPERVISE 0x000C
#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */
#define L2CAP_CTRL_TXSEQ_SHIFT 1
#define L2CAP_CTRL_REQSEQ_SHIFT 8
#define L2CAP_CTRL_SAR_SHIFT 14
/* L2CAP Supervisory Function */
#define L2CAP_SUPER_RCV_READY 0x0000
#define L2CAP_SUPER_REJECT 0x0004
#define L2CAP_SUPER_RCV_NOT_READY 0x0008
#define L2CAP_SUPER_SELECT_REJECT 0x000C
/* L2CAP Segmentation and Reassembly */
#define L2CAP_SDU_UNSEGMENTED 0x0000
#define L2CAP_SDU_START 0x4000
#define L2CAP_SDU_END 0x8000
#define L2CAP_SDU_CONTINUE 0xC000
/* L2CAP structures */ /* L2CAP structures */
struct l2cap_hdr { struct l2cap_hdr {
__le16 len; __le16 len;
...@@ -190,7 +219,7 @@ struct l2cap_conf_rfc { ...@@ -190,7 +219,7 @@ struct l2cap_conf_rfc {
#define L2CAP_MODE_RETRANS 0x01 #define L2CAP_MODE_RETRANS 0x01
#define L2CAP_MODE_FLOWCTL 0x02 #define L2CAP_MODE_FLOWCTL 0x02
#define L2CAP_MODE_ERTM 0x03 #define L2CAP_MODE_ERTM 0x03
#define L2CAP_MODE_STREAM 0x04 #define L2CAP_MODE_STREAMING 0x04
struct l2cap_disconn_req { struct l2cap_disconn_req {
__le16 dcid; __le16 dcid;
...@@ -261,6 +290,14 @@ struct l2cap_conn { ...@@ -261,6 +290,14 @@ struct l2cap_conn {
/* ----- L2CAP channel and socket info ----- */ /* ----- L2CAP channel and socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
#define TX_QUEUE(sk) (&l2cap_pi(sk)->tx_queue)
#define SREJ_QUEUE(sk) (&l2cap_pi(sk)->srej_queue)
#define SREJ_LIST(sk) (&l2cap_pi(sk)->srej_l.list)
struct srej_list {
__u8 tx_seq;
struct list_head list;
};
struct l2cap_pinfo { struct l2cap_pinfo {
struct bt_sock bt; struct bt_sock bt;
...@@ -271,30 +308,97 @@ struct l2cap_pinfo { ...@@ -271,30 +308,97 @@ struct l2cap_pinfo {
__u16 imtu; __u16 imtu;
__u16 omtu; __u16 omtu;
__u16 flush_to; __u16 flush_to;
__u8 sec_level; __u8 mode;
__u8 num_conf_req;
__u8 num_conf_rsp;
__u8 fcs;
__u8 sec_level;
__u8 role_switch; __u8 role_switch;
__u8 force_reliable; __u8 force_reliable;
__u8 conf_req[64]; __u8 conf_req[64];
__u8 conf_len; __u8 conf_len;
__u8 conf_state; __u8 conf_state;
__u8 conf_retry; __u8 conn_state;
__u8 next_tx_seq;
__u8 expected_ack_seq;
__u8 req_seq;
__u8 expected_tx_seq;
__u8 buffer_seq;
__u8 buffer_seq_srej;
__u8 srej_save_reqseq;
__u8 unacked_frames;
__u8 retry_count;
__u8 num_to_ack;
__u16 sdu_len;
__u16 partial_sdu_len;
struct sk_buff *sdu;
__u8 ident; __u8 ident;
__u8 remote_tx_win;
__u8 remote_max_tx;
__u16 retrans_timeout;
__u16 monitor_timeout;
__u16 max_pdu_size;
__le16 sport; __le16 sport;
struct timer_list retrans_timer;
struct timer_list monitor_timer;
struct sk_buff_head tx_queue;
struct sk_buff_head srej_queue;
struct srej_list srej_l;
struct l2cap_conn *conn; struct l2cap_conn *conn;
struct sock *next_c; struct sock *next_c;
struct sock *prev_c; struct sock *prev_c;
}; };
#define L2CAP_CONF_REQ_SENT 0x01 #define L2CAP_CONF_REQ_SENT 0x01
#define L2CAP_CONF_INPUT_DONE 0x02 #define L2CAP_CONF_INPUT_DONE 0x02
#define L2CAP_CONF_OUTPUT_DONE 0x04 #define L2CAP_CONF_OUTPUT_DONE 0x04
#define L2CAP_CONF_CONNECT_PEND 0x80 #define L2CAP_CONF_MTU_DONE 0x08
#define L2CAP_CONF_MODE_DONE 0x10
#define L2CAP_CONF_MAX_RETRIES 2 #define L2CAP_CONF_CONNECT_PEND 0x20
#define L2CAP_CONF_NO_FCS_RECV 0x40
#define L2CAP_CONF_STATE2_DEVICE 0x80
#define L2CAP_CONF_MAX_CONF_REQ 2
#define L2CAP_CONF_MAX_CONF_RSP 2
#define L2CAP_CONN_SAR_SDU 0x01
#define L2CAP_CONN_SREJ_SENT 0x02
#define L2CAP_CONN_WAIT_F 0x04
#define L2CAP_CONN_SREJ_ACT 0x08
#define L2CAP_CONN_SEND_PBIT 0x10
#define L2CAP_CONN_REMOTE_BUSY 0x20
#define L2CAP_CONN_LOCAL_BUSY 0x40
#define __mod_retrans_timer() mod_timer(&l2cap_pi(sk)->retrans_timer, \
jiffies + msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO));
#define __mod_monitor_timer() mod_timer(&l2cap_pi(sk)->monitor_timer, \
jiffies + msecs_to_jiffies(L2CAP_DEFAULT_MONITOR_TO));
static inline int l2cap_tx_window_full(struct sock *sk)
{
struct l2cap_pinfo *pi = l2cap_pi(sk);
int sub;
sub = (pi->next_tx_seq - pi->expected_ack_seq) % 64;
if (sub < 0)
sub += 64;
return (sub == pi->remote_tx_win);
}
#define __get_txseq(ctrl) ((ctrl) & L2CAP_CTRL_TXSEQ) >> 1
#define __get_reqseq(ctrl) ((ctrl) & L2CAP_CTRL_REQSEQ) >> 8
#define __is_iframe(ctrl) !((ctrl) & L2CAP_CTRL_FRAME_TYPE)
#define __is_sframe(ctrl) (ctrl) & L2CAP_CTRL_FRAME_TYPE
#define __is_sar_start(ctrl) ((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START
void l2cap_load(void); void l2cap_load(void);
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#define RFCOMM_CONN_TIMEOUT (HZ * 30) #define RFCOMM_CONN_TIMEOUT (HZ * 30)
#define RFCOMM_DISC_TIMEOUT (HZ * 20) #define RFCOMM_DISC_TIMEOUT (HZ * 20)
#define RFCOMM_AUTH_TIMEOUT (HZ * 25) #define RFCOMM_AUTH_TIMEOUT (HZ * 25)
#define RFCOMM_IDLE_TIMEOUT (HZ * 2)
#define RFCOMM_DEFAULT_MTU 127 #define RFCOMM_DEFAULT_MTU 127
#define RFCOMM_DEFAULT_CREDITS 7 #define RFCOMM_DEFAULT_CREDITS 7
...@@ -154,6 +155,7 @@ struct rfcomm_msc { ...@@ -154,6 +155,7 @@ struct rfcomm_msc {
struct rfcomm_session { struct rfcomm_session {
struct list_head list; struct list_head list;
struct socket *sock; struct socket *sock;
struct timer_list timer;
unsigned long state; unsigned long state;
unsigned long flags; unsigned long flags;
atomic_t refcnt; atomic_t refcnt;
......
...@@ -34,6 +34,7 @@ menuconfig BT ...@@ -34,6 +34,7 @@ menuconfig BT
config BT_L2CAP config BT_L2CAP
tristate "L2CAP protocol support" tristate "L2CAP protocol support"
depends on BT depends on BT
select CRC16
help help
L2CAP (Logical Link Control and Adaptation Protocol) provides L2CAP (Logical Link Control and Adaptation Protocol) provides
connection oriented and connection-less data transport. L2CAP connection oriented and connection-less data transport. L2CAP
......
...@@ -246,6 +246,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) ...@@ -246,6 +246,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
if (hdev->notify) if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
atomic_set(&conn->devref, 0);
hci_conn_init_sysfs(conn); hci_conn_init_sysfs(conn);
tasklet_enable(&hdev->tx_task); tasklet_enable(&hdev->tx_task);
...@@ -288,7 +290,7 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -288,7 +290,7 @@ int hci_conn_del(struct hci_conn *conn)
skb_queue_purge(&conn->data_q); skb_queue_purge(&conn->data_q);
hci_conn_del_sysfs(conn); hci_conn_put_device(conn);
hci_dev_put(hdev); hci_dev_put(hdev);
...@@ -583,6 +585,19 @@ void hci_conn_check_pending(struct hci_dev *hdev) ...@@ -583,6 +585,19 @@ void hci_conn_check_pending(struct hci_dev *hdev)
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
} }
void hci_conn_hold_device(struct hci_conn *conn)
{
atomic_inc(&conn->devref);
}
EXPORT_SYMBOL(hci_conn_hold_device);
void hci_conn_put_device(struct hci_conn *conn)
{
if (atomic_dec_and_test(&conn->devref))
hci_conn_del_sysfs(conn);
}
EXPORT_SYMBOL(hci_conn_put_device);
int hci_get_conn_list(void __user *arg) int hci_get_conn_list(void __user *arg)
{ {
struct hci_conn_list_req req, *cl; struct hci_conn_list_req req, *cl;
......
...@@ -911,7 +911,7 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -911,7 +911,7 @@ int hci_register_dev(struct hci_dev *hdev)
hdev->reassembly[i] = NULL; hdev->reassembly[i] = NULL;
init_waitqueue_head(&hdev->req_wait_q); init_waitqueue_head(&hdev->req_wait_q);
init_MUTEX(&hdev->req_lock); mutex_init(&hdev->req_lock);
inquiry_cache_init(hdev); inquiry_cache_init(hdev);
......
...@@ -887,6 +887,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s ...@@ -887,6 +887,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
} else } else
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
hci_conn_hold_device(conn);
hci_conn_add_sysfs(conn); hci_conn_add_sysfs(conn);
if (test_bit(HCI_AUTH, &hdev->flags)) if (test_bit(HCI_AUTH, &hdev->flags))
...@@ -1693,6 +1694,7 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu ...@@ -1693,6 +1694,7 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu
conn->handle = __le16_to_cpu(ev->handle); conn->handle = __le16_to_cpu(ev->handle);
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
hci_conn_hold_device(conn);
hci_conn_add_sysfs(conn); hci_conn_add_sysfs(conn);
break; break;
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/hidraw.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
...@@ -92,10 +93,14 @@ static void __hidp_link_session(struct hidp_session *session) ...@@ -92,10 +93,14 @@ static void __hidp_link_session(struct hidp_session *session)
{ {
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
list_add(&session->list, &hidp_session_list); list_add(&session->list, &hidp_session_list);
hci_conn_hold_device(session->conn);
} }
static void __hidp_unlink_session(struct hidp_session *session) static void __hidp_unlink_session(struct hidp_session *session)
{ {
hci_conn_put_device(session->conn);
list_del(&session->list); list_del(&session->list);
module_put(THIS_MODULE); module_put(THIS_MODULE);
} }
...@@ -374,6 +379,7 @@ static void hidp_process_hid_control(struct hidp_session *session, ...@@ -374,6 +379,7 @@ static void hidp_process_hid_control(struct hidp_session *session,
/* Kill session thread */ /* Kill session thread */
atomic_inc(&session->terminate); atomic_inc(&session->terminate);
hidp_schedule(session);
} }
} }
...@@ -573,7 +579,11 @@ static int hidp_session(void *arg) ...@@ -573,7 +579,11 @@ static int hidp_session(void *arg)
if (session->hid) { if (session->hid) {
if (session->hid->claimed & HID_CLAIMED_INPUT) if (session->hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(session->hid); hidinput_disconnect(session->hid);
if (session->hid->claimed & HID_CLAIMED_HIDRAW)
hidraw_disconnect(session->hid);
hid_destroy_device(session->hid); hid_destroy_device(session->hid);
session->hid = NULL;
} }
/* Wakeup user-space polling for socket errors */ /* Wakeup user-space polling for socket errors */
...@@ -601,25 +611,27 @@ static struct device *hidp_get_device(struct hidp_session *session) ...@@ -601,25 +611,27 @@ static struct device *hidp_get_device(struct hidp_session *session)
{ {
bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
struct device *device = NULL;
struct hci_dev *hdev; struct hci_dev *hdev;
struct hci_conn *conn;
hdev = hci_get_route(dst, src); hdev = hci_get_route(dst, src);
if (!hdev) if (!hdev)
return NULL; return NULL;
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
if (session->conn)
device = &session->conn->dev;
hci_dev_put(hdev); hci_dev_put(hdev);
return conn ? &conn->dev : NULL; return device;
} }
static int hidp_setup_input(struct hidp_session *session, static int hidp_setup_input(struct hidp_session *session,
struct hidp_connadd_req *req) struct hidp_connadd_req *req)
{ {
struct input_dev *input; struct input_dev *input;
int i; int err, i;
input = input_allocate_device(); input = input_allocate_device();
if (!input) if (!input)
...@@ -666,7 +678,13 @@ static int hidp_setup_input(struct hidp_session *session, ...@@ -666,7 +678,13 @@ static int hidp_setup_input(struct hidp_session *session,
input->event = hidp_input_event; input->event = hidp_input_event;
return input_register_device(input); err = input_register_device(input);
if (err < 0) {
hci_conn_put_device(session->conn);
return err;
}
return 0;
} }
static int hidp_open(struct hid_device *hid) static int hidp_open(struct hid_device *hid)
...@@ -748,13 +766,11 @@ static int hidp_setup_hid(struct hidp_session *session, ...@@ -748,13 +766,11 @@ static int hidp_setup_hid(struct hidp_session *session,
{ {
struct hid_device *hid; struct hid_device *hid;
bdaddr_t src, dst; bdaddr_t src, dst;
int ret; int err;
hid = hid_allocate_device(); hid = hid_allocate_device();
if (IS_ERR(hid)) { if (IS_ERR(hid))
ret = PTR_ERR(session->hid); return PTR_ERR(session->hid);
goto err;
}
session->hid = hid; session->hid = hid;
session->req = req; session->req = req;
...@@ -776,16 +792,17 @@ static int hidp_setup_hid(struct hidp_session *session, ...@@ -776,16 +792,17 @@ static int hidp_setup_hid(struct hidp_session *session,
hid->dev.parent = hidp_get_device(session); hid->dev.parent = hidp_get_device(session);
hid->ll_driver = &hidp_hid_driver; hid->ll_driver = &hidp_hid_driver;
ret = hid_add_device(hid); err = hid_add_device(hid);
if (ret) if (err < 0)
goto err_hid; goto failed;
return 0; return 0;
err_hid:
failed:
hid_destroy_device(hid); hid_destroy_device(hid);
session->hid = NULL; session->hid = NULL;
err:
return ret; return err;
} }
int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
...@@ -835,13 +852,13 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, ...@@ -835,13 +852,13 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
if (req->rd_size > 0) { if (req->rd_size > 0) {
err = hidp_setup_hid(session, req); err = hidp_setup_hid(session, req);
if (err && err != -ENODEV) if (err && err != -ENODEV)
goto err_skb; goto purge;
} }
if (!session->hid) { if (!session->hid) {
err = hidp_setup_input(session, req); err = hidp_setup_input(session, req);
if (err < 0) if (err < 0)
goto err_skb; goto purge;
} }
__hidp_link_session(session); __hidp_link_session(session);
...@@ -869,13 +886,20 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, ...@@ -869,13 +886,20 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
__hidp_unlink_session(session); __hidp_unlink_session(session);
if (session->input) if (session->input) {
input_unregister_device(session->input); input_unregister_device(session->input);
if (session->hid) session->input = NULL;
}
if (session->hid) {
hid_destroy_device(session->hid); hid_destroy_device(session->hid);
err_skb: session->hid = NULL;
}
purge:
skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit); skb_queue_purge(&session->intr_transmit);
failed: failed:
up_write(&hidp_session_sem); up_write(&hidp_session_sem);
......
...@@ -126,6 +126,8 @@ int hidp_get_conninfo(struct hidp_conninfo *ci); ...@@ -126,6 +126,8 @@ int hidp_get_conninfo(struct hidp_conninfo *ci);
struct hidp_session { struct hidp_session {
struct list_head list; struct list_head list;
struct hci_conn *conn;
struct socket *ctrl_sock; struct socket *ctrl_sock;
struct socket *intr_sock; struct socket *intr_sock;
......
此差异已折叠。
...@@ -244,6 +244,33 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d) ...@@ -244,6 +244,33 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d)
auth_type); auth_type);
} }
static void rfcomm_session_timeout(unsigned long arg)
{
struct rfcomm_session *s = (void *) arg;
BT_DBG("session %p state %ld", s, s->state);
set_bit(RFCOMM_TIMED_OUT, &s->flags);
rfcomm_session_put(s);
rfcomm_schedule(RFCOMM_SCHED_TIMEO);
}
static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout)
{
BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout);
if (!mod_timer(&s->timer, jiffies + timeout))
rfcomm_session_hold(s);
}
static void rfcomm_session_clear_timer(struct rfcomm_session *s)
{
BT_DBG("session %p state %ld", s, s->state);
if (timer_pending(&s->timer) && del_timer(&s->timer))
rfcomm_session_put(s);
}
/* ---- RFCOMM DLCs ---- */ /* ---- RFCOMM DLCs ---- */
static void rfcomm_dlc_timeout(unsigned long arg) static void rfcomm_dlc_timeout(unsigned long arg)
{ {
...@@ -320,6 +347,7 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) ...@@ -320,6 +347,7 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
rfcomm_session_hold(s); rfcomm_session_hold(s);
rfcomm_session_clear_timer(s);
rfcomm_dlc_hold(d); rfcomm_dlc_hold(d);
list_add(&d->list, &s->dlcs); list_add(&d->list, &s->dlcs);
d->session = s; d->session = s;
...@@ -335,6 +363,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) ...@@ -335,6 +363,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
d->session = NULL; d->session = NULL;
rfcomm_dlc_put(d); rfcomm_dlc_put(d);
if (list_empty(&s->dlcs))
rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT);
rfcomm_session_put(s); rfcomm_session_put(s);
} }
...@@ -567,6 +598,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) ...@@ -567,6 +598,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
BT_DBG("session %p sock %p", s, sock); BT_DBG("session %p sock %p", s, sock);
setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s);
INIT_LIST_HEAD(&s->dlcs); INIT_LIST_HEAD(&s->dlcs);
s->state = state; s->state = state;
s->sock = sock; s->sock = sock;
...@@ -598,6 +631,7 @@ static void rfcomm_session_del(struct rfcomm_session *s) ...@@ -598,6 +631,7 @@ static void rfcomm_session_del(struct rfcomm_session *s)
if (state == BT_CONNECTED) if (state == BT_CONNECTED)
rfcomm_send_disc(s, 0); rfcomm_send_disc(s, 0);
rfcomm_session_clear_timer(s);
sock_release(s->sock); sock_release(s->sock);
kfree(s); kfree(s);
...@@ -639,6 +673,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err) ...@@ -639,6 +673,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err)
__rfcomm_dlc_close(d, err); __rfcomm_dlc_close(d, err);
} }
rfcomm_session_clear_timer(s);
rfcomm_session_put(s); rfcomm_session_put(s);
} }
...@@ -1879,6 +1914,12 @@ static inline void rfcomm_process_sessions(void) ...@@ -1879,6 +1914,12 @@ static inline void rfcomm_process_sessions(void)
struct rfcomm_session *s; struct rfcomm_session *s;
s = list_entry(p, struct rfcomm_session, list); s = list_entry(p, struct rfcomm_session, list);
if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) {
s->state = BT_DISCONN;
rfcomm_send_disc(s, 0);
continue;
}
if (s->state == BT_LISTEN) { if (s->state == BT_LISTEN) {
rfcomm_accept_connection(s); rfcomm_accept_connection(s);
continue; continue;
...@@ -2080,7 +2121,7 @@ static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL); ...@@ -2080,7 +2121,7 @@ static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL);
/* ---- Initialization ---- */ /* ---- Initialization ---- */
static int __init rfcomm_init(void) static int __init rfcomm_init(void)
{ {
int ret; int err;
l2cap_load(); l2cap_load();
...@@ -2088,33 +2129,35 @@ static int __init rfcomm_init(void) ...@@ -2088,33 +2129,35 @@ static int __init rfcomm_init(void)
rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd"); rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");
if (IS_ERR(rfcomm_thread)) { if (IS_ERR(rfcomm_thread)) {
ret = PTR_ERR(rfcomm_thread); err = PTR_ERR(rfcomm_thread);
goto out_thread; goto unregister;
} }
if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0) if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0)
BT_ERR("Failed to create RFCOMM info file"); BT_ERR("Failed to create RFCOMM info file");
ret = rfcomm_init_ttys(); err = rfcomm_init_ttys();
if (ret) if (err < 0)
goto out_tty; goto stop;
ret = rfcomm_init_sockets(); err = rfcomm_init_sockets();
if (ret) if (err < 0)
goto out_sock; goto cleanup;
BT_INFO("RFCOMM ver %s", VERSION); BT_INFO("RFCOMM ver %s", VERSION);
return 0; return 0;
out_sock: cleanup:
rfcomm_cleanup_ttys(); rfcomm_cleanup_ttys();
out_tty:
stop:
kthread_stop(rfcomm_thread); kthread_stop(rfcomm_thread);
out_thread:
unregister:
hci_unregister_cb(&rfcomm_cb); hci_unregister_cb(&rfcomm_cb);
return ret; return err;
} }
static void __exit rfcomm_exit(void) static void __exit rfcomm_exit(void)
......
...@@ -359,20 +359,9 @@ static void sco_sock_kill(struct sock *sk) ...@@ -359,20 +359,9 @@ static void sco_sock_kill(struct sock *sk)
sock_put(sk); sock_put(sk);
} }
/* Close socket. static void __sco_sock_close(struct sock *sk)
* Must be called on unlocked socket.
*/
static void sco_sock_close(struct sock *sk)
{ {
struct sco_conn *conn; BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
sco_sock_clear_timer(sk);
lock_sock(sk);
conn = sco_pi(sk)->conn;
BT_DBG("sk %p state %d conn %p socket %p", sk, sk->sk_state, conn, sk->sk_socket);
switch (sk->sk_state) { switch (sk->sk_state) {
case BT_LISTEN: case BT_LISTEN:
...@@ -390,9 +379,15 @@ static void sco_sock_close(struct sock *sk) ...@@ -390,9 +379,15 @@ static void sco_sock_close(struct sock *sk)
sock_set_flag(sk, SOCK_ZAPPED); sock_set_flag(sk, SOCK_ZAPPED);
break; break;
} }
}
/* Must be called on unlocked socket. */
static void sco_sock_close(struct sock *sk)
{
sco_sock_clear_timer(sk);
lock_sock(sk);
__sco_sock_close(sk);
release_sock(sk); release_sock(sk);
sco_sock_kill(sk); sco_sock_kill(sk);
} }
...@@ -748,6 +743,30 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char ...@@ -748,6 +743,30 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
return err; return err;
} }
static int sco_sock_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
int err = 0;
BT_DBG("sock %p, sk %p", sock, sk);
if (!sk)
return 0;
lock_sock(sk);
if (!sk->sk_shutdown) {
sk->sk_shutdown = SHUTDOWN_MASK;
sco_sock_clear_timer(sk);
__sco_sock_close(sk);
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
err = bt_sock_wait_state(sk, BT_CLOSED,
sk->sk_lingertime);
}
release_sock(sk);
return err;
}
static int sco_sock_release(struct socket *sock) static int sco_sock_release(struct socket *sock)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
...@@ -969,7 +988,7 @@ static const struct proto_ops sco_sock_ops = { ...@@ -969,7 +988,7 @@ static const struct proto_ops sco_sock_ops = {
.ioctl = bt_sock_ioctl, .ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap, .mmap = sock_no_mmap,
.socketpair = sock_no_socketpair, .socketpair = sock_no_socketpair,
.shutdown = sock_no_shutdown, .shutdown = sco_sock_shutdown,
.setsockopt = sco_sock_setsockopt, .setsockopt = sco_sock_setsockopt,
.getsockopt = sco_sock_getsockopt .getsockopt = sco_sock_getsockopt
}; };
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册