Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/holtmann/bluetooth-next-2.6
This commit is contained in:
Коммит
b9caaabb99
|
@ -82,6 +82,8 @@ block/
|
|||
- info on the Block I/O (BIO) layer.
|
||||
blockdev/
|
||||
- info on block devices & drivers
|
||||
btmrvl.txt
|
||||
- info on Marvell Bluetooth driver usage.
|
||||
cachetlb.txt
|
||||
- describes the cache/TLB flushing interfaces Linux uses.
|
||||
cdrom/
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
=======================================================================
|
||||
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
|
|||
Say Y here to compile support for virtual HCI devices into the
|
||||
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
|
||||
|
||||
|
|
|
@ -15,6 +15,12 @@ obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o
|
|||
obj-$(CONFIG_BT_HCIBTUSB) += btusb.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-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
|
||||
hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
|
||||
|
|
|
@ -0,0 +1,432 @@
|
|||
/**
|
||||
* 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);
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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
|
|
@ -0,0 +1,624 @@
|
|||
/**
|
||||
* 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");
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* 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 @@
|
|||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#define VERSION "0.5"
|
||||
#define VERSION "0.6"
|
||||
|
||||
static int ignore_dga;
|
||||
static int ignore_csr;
|
||||
|
@ -145,6 +145,7 @@ static struct usb_device_id blacklist_table[] = {
|
|||
#define BTUSB_INTR_RUNNING 0
|
||||
#define BTUSB_BULK_RUNNING 1
|
||||
#define BTUSB_ISOC_RUNNING 2
|
||||
#define BTUSB_SUSPENDING 3
|
||||
|
||||
struct btusb_data {
|
||||
struct hci_dev *hdev;
|
||||
|
@ -157,11 +158,15 @@ struct btusb_data {
|
|||
unsigned long flags;
|
||||
|
||||
struct work_struct work;
|
||||
struct work_struct waker;
|
||||
|
||||
struct usb_anchor tx_anchor;
|
||||
struct usb_anchor intr_anchor;
|
||||
struct usb_anchor bulk_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 *bulk_tx_ep;
|
||||
|
@ -174,8 +179,23 @@ struct btusb_data {
|
|||
unsigned int sco_num;
|
||||
int isoc_altsetting;
|
||||
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)
|
||||
{
|
||||
struct hci_dev *hdev = urb->context;
|
||||
|
@ -202,6 +222,7 @@ static void btusb_intr_complete(struct urb *urb)
|
|||
if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
|
||||
return;
|
||||
|
||||
usb_mark_last_busy(data->udev);
|
||||
usb_anchor_urb(urb, &data->intr_anchor);
|
||||
|
||||
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)
|
|||
struct urb *urb;
|
||||
unsigned char *buf;
|
||||
unsigned int pipe;
|
||||
int err, size;
|
||||
int err, size = HCI_MAX_FRAME_SIZE;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
|
@ -312,8 +333,6 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
if (!urb)
|
||||
return -ENOMEM;
|
||||
|
||||
size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
|
||||
|
||||
buf = kmalloc(size, mem_flags);
|
||||
if (!buf) {
|
||||
usb_free_urb(urb);
|
||||
|
@ -327,6 +346,7 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
|||
|
||||
urb->transfer_flags |= URB_FREE_BUFFER;
|
||||
|
||||
usb_mark_last_busy(data->udev);
|
||||
usb_anchor_urb(urb, &data->bulk_anchor);
|
||||
|
||||
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)
|
|||
}
|
||||
|
||||
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 hci_dev *hdev = (struct hci_dev *) skb->dev;
|
||||
|
@ -490,11 +537,17 @@ static int btusb_open(struct hci_dev *hdev)
|
|||
|
||||
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))
|
||||
return 0;
|
||||
goto done;
|
||||
|
||||
if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
|
||||
return 0;
|
||||
goto done;
|
||||
|
||||
err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
|
||||
if (err < 0)
|
||||
|
@ -509,17 +562,28 @@ static int btusb_open(struct hci_dev *hdev)
|
|||
set_bit(BTUSB_BULK_RUNNING, &data->flags);
|
||||
btusb_submit_bulk_urb(hdev, GFP_KERNEL);
|
||||
|
||||
done:
|
||||
usb_autopm_put_interface(data->intf);
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
||||
clear_bit(HCI_RUNNING, &hdev->flags);
|
||||
usb_autopm_put_interface(data->intf);
|
||||
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)
|
||||
{
|
||||
struct btusb_data *data = hdev->driver_data;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
|
@ -529,13 +593,16 @@ static int btusb_close(struct hci_dev *hdev)
|
|||
cancel_work_sync(&data->work);
|
||||
|
||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||
usb_kill_anchored_urbs(&data->isoc_anchor);
|
||||
|
||||
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
|
||||
usb_kill_anchored_urbs(&data->bulk_anchor);
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -622,7 +689,7 @@ static int btusb_send_frame(struct sk_buff *skb)
|
|||
urb->dev = data->udev;
|
||||
urb->pipe = pipe;
|
||||
urb->context = skb;
|
||||
urb->complete = btusb_tx_complete;
|
||||
urb->complete = btusb_isoc_tx_complete;
|
||||
urb->interval = data->isoc_tx_ep->bInterval;
|
||||
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
|
@ -633,12 +700,21 @@ static int btusb_send_frame(struct sk_buff *skb)
|
|||
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
|
||||
|
||||
hdev->stat.sco_tx++;
|
||||
break;
|
||||
goto skip_waking;
|
||||
|
||||
default:
|
||||
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);
|
||||
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
|
@ -646,10 +722,13 @@ static int btusb_send_frame(struct sk_buff *skb)
|
|||
BT_ERR("%s urb %p submission failed", hdev->name, urb);
|
||||
kfree(urb->setup_packet);
|
||||
usb_unanchor_urb(urb);
|
||||
} else {
|
||||
usb_mark_last_busy(data->udev);
|
||||
}
|
||||
|
||||
usb_free_urb(urb);
|
||||
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -721,8 +800,19 @@ static void btusb_work(struct work_struct *work)
|
|||
{
|
||||
struct btusb_data *data = container_of(work, struct btusb_data, work);
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
int err;
|
||||
|
||||
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) {
|
||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||
usb_kill_anchored_urbs(&data->isoc_anchor);
|
||||
|
@ -742,9 +832,25 @@ static void btusb_work(struct work_struct *work)
|
|||
usb_kill_anchored_urbs(&data->isoc_anchor);
|
||||
|
||||
__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,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
|
@ -814,11 +920,14 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
spin_lock_init(&data->lock);
|
||||
|
||||
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->intr_anchor);
|
||||
init_usb_anchor(&data->bulk_anchor);
|
||||
init_usb_anchor(&data->isoc_anchor);
|
||||
init_usb_anchor(&data->deferred);
|
||||
|
||||
hdev = hci_alloc_dev();
|
||||
if (!hdev) {
|
||||
|
@ -943,6 +1052,7 @@ static void btusb_disconnect(struct usb_interface *intf)
|
|||
hci_free_dev(hdev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct btusb_data *data = usb_get_intfdata(intf);
|
||||
|
@ -952,22 +1062,44 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
if (data->suspend_count++)
|
||||
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);
|
||||
|
||||
btusb_stop_traffic(data);
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct btusb_data *data = usb_get_intfdata(intf);
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("intf %p", intf);
|
||||
|
||||
|
@ -975,13 +1107,13 @@ static int btusb_resume(struct usb_interface *intf)
|
|||
return 0;
|
||||
|
||||
if (!test_bit(HCI_RUNNING, &hdev->flags))
|
||||
return 0;
|
||||
goto done;
|
||||
|
||||
if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) {
|
||||
err = btusb_submit_intr_urb(hdev, GFP_NOIO);
|
||||
if (err < 0) {
|
||||
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
|
||||
return err;
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -989,9 +1121,10 @@ static int btusb_resume(struct usb_interface *intf)
|
|||
err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
|
||||
if (err < 0) {
|
||||
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
|
||||
return err;
|
||||
} else
|
||||
btusb_submit_bulk_urb(hdev, GFP_NOIO);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
btusb_submit_bulk_urb(hdev, GFP_NOIO);
|
||||
}
|
||||
|
||||
if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
|
||||
|
@ -1001,16 +1134,35 @@ static int btusb_resume(struct usb_interface *intf)
|
|||
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;
|
||||
|
||||
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 = {
|
||||
.name = "btusb",
|
||||
.probe = btusb_probe,
|
||||
.disconnect = btusb_disconnect,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = btusb_suspend,
|
||||
.resume = btusb_resume,
|
||||
#endif
|
||||
.id_table = btusb_table,
|
||||
.supports_autosuspend = 1,
|
||||
};
|
||||
|
||||
static int __init btusb_init(void)
|
||||
|
|
|
@ -373,8 +373,9 @@ static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
|
|||
|
||||
i = 0;
|
||||
skb_queue_walk_safe(&bcsp->unack, skb, tmp) {
|
||||
if (i++ >= pkts_to_be_removed)
|
||||
if (i >= pkts_to_be_removed)
|
||||
break;
|
||||
i++;
|
||||
|
||||
__skb_unlink(skb, &bcsp->unack);
|
||||
kfree_skb(skb);
|
||||
|
|
|
@ -138,8 +138,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock);
|
|||
struct bt_skb_cb {
|
||||
__u8 pkt_type;
|
||||
__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)
|
||||
{
|
||||
|
|
|
@ -117,7 +117,7 @@ struct hci_dev {
|
|||
struct sk_buff *sent_cmd;
|
||||
struct sk_buff *reassembly[3];
|
||||
|
||||
struct semaphore req_lock;
|
||||
struct mutex req_lock;
|
||||
wait_queue_head_t req_wait_q;
|
||||
__u32 req_status;
|
||||
__u32 req_result;
|
||||
|
@ -187,6 +187,7 @@ struct hci_conn {
|
|||
struct work_struct work_del;
|
||||
|
||||
struct device dev;
|
||||
atomic_t devref;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
void *l2cap_data;
|
||||
|
@ -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_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)
|
||||
{
|
||||
atomic_inc(&conn->refcnt);
|
||||
|
@ -700,8 +704,8 @@ struct hci_sec_filter {
|
|||
#define HCI_REQ_PEND 1
|
||||
#define HCI_REQ_CANCELED 2
|
||||
|
||||
#define hci_req_lock(d) down(&d->req_lock)
|
||||
#define hci_req_unlock(d) up(&d->req_lock)
|
||||
#define hci_req_lock(d) mutex_lock(&d->req_lock)
|
||||
#define hci_req_unlock(d) mutex_unlock(&d->req_lock)
|
||||
|
||||
void hci_req_complete(struct hci_dev *hdev, int result);
|
||||
|
||||
|
|
|
@ -27,12 +27,14 @@
|
|||
|
||||
/* L2CAP defaults */
|
||||
#define L2CAP_DEFAULT_MTU 672
|
||||
#define L2CAP_DEFAULT_MIN_MTU 48
|
||||
#define L2CAP_DEFAULT_FLUSH_TO 0xffff
|
||||
#define L2CAP_DEFAULT_RX_WINDOW 1
|
||||
#define L2CAP_DEFAULT_MAX_RECEIVE 1
|
||||
#define L2CAP_DEFAULT_RETRANS_TO 300 /* 300 milliseconds */
|
||||
#define L2CAP_DEFAULT_MONITOR_TO 1000 /* 1 second */
|
||||
#define L2CAP_DEFAULT_MAX_RX_APDU 0xfff7
|
||||
#define L2CAP_DEFAULT_TX_WINDOW 63
|
||||
#define L2CAP_DEFAULT_NUM_TO_ACK (L2CAP_DEFAULT_TX_WINDOW/5)
|
||||
#define L2CAP_DEFAULT_MAX_TX 3
|
||||
#define L2CAP_DEFAULT_RETRANS_TO 1000 /* 1 second */
|
||||
#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */
|
||||
#define L2CAP_DEFAULT_MAX_PDU_SIZE 672
|
||||
|
||||
#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */
|
||||
#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */
|
||||
|
@ -52,6 +54,7 @@ struct l2cap_options {
|
|||
__u16 imtu;
|
||||
__u16 flush_to;
|
||||
__u8 mode;
|
||||
__u8 fcs;
|
||||
};
|
||||
|
||||
#define L2CAP_CONNINFO 0x02
|
||||
|
@ -93,6 +96,32 @@ struct l2cap_conninfo {
|
|||
#define L2CAP_FCS_NONE 0x00
|
||||
#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 */
|
||||
struct l2cap_hdr {
|
||||
__le16 len;
|
||||
|
@ -190,7 +219,7 @@ struct l2cap_conf_rfc {
|
|||
#define L2CAP_MODE_RETRANS 0x01
|
||||
#define L2CAP_MODE_FLOWCTL 0x02
|
||||
#define L2CAP_MODE_ERTM 0x03
|
||||
#define L2CAP_MODE_STREAM 0x04
|
||||
#define L2CAP_MODE_STREAMING 0x04
|
||||
|
||||
struct l2cap_disconn_req {
|
||||
__le16 dcid;
|
||||
|
@ -261,6 +290,14 @@ struct l2cap_conn {
|
|||
|
||||
/* ----- L2CAP channel and socket info ----- */
|
||||
#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 bt_sock bt;
|
||||
|
@ -271,30 +308,97 @@ struct l2cap_pinfo {
|
|||
__u16 imtu;
|
||||
__u16 omtu;
|
||||
__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 force_reliable;
|
||||
__u8 force_reliable;
|
||||
|
||||
__u8 conf_req[64];
|
||||
__u8 conf_len;
|
||||
__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 remote_tx_win;
|
||||
__u8 remote_max_tx;
|
||||
__u16 retrans_timeout;
|
||||
__u16 monitor_timeout;
|
||||
__u16 max_pdu_size;
|
||||
|
||||
__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 sock *next_c;
|
||||
struct sock *prev_c;
|
||||
};
|
||||
|
||||
#define L2CAP_CONF_REQ_SENT 0x01
|
||||
#define L2CAP_CONF_INPUT_DONE 0x02
|
||||
#define L2CAP_CONF_OUTPUT_DONE 0x04
|
||||
#define L2CAP_CONF_CONNECT_PEND 0x80
|
||||
#define L2CAP_CONF_REQ_SENT 0x01
|
||||
#define L2CAP_CONF_INPUT_DONE 0x02
|
||||
#define L2CAP_CONF_OUTPUT_DONE 0x04
|
||||
#define L2CAP_CONF_MTU_DONE 0x08
|
||||
#define L2CAP_CONF_MODE_DONE 0x10
|
||||
#define L2CAP_CONF_CONNECT_PEND 0x20
|
||||
#define L2CAP_CONF_NO_FCS_RECV 0x40
|
||||
#define L2CAP_CONF_STATE2_DEVICE 0x80
|
||||
|
||||
#define L2CAP_CONF_MAX_RETRIES 2
|
||||
#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);
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#define RFCOMM_CONN_TIMEOUT (HZ * 30)
|
||||
#define RFCOMM_DISC_TIMEOUT (HZ * 20)
|
||||
#define RFCOMM_AUTH_TIMEOUT (HZ * 25)
|
||||
#define RFCOMM_IDLE_TIMEOUT (HZ * 2)
|
||||
|
||||
#define RFCOMM_DEFAULT_MTU 127
|
||||
#define RFCOMM_DEFAULT_CREDITS 7
|
||||
|
@ -154,6 +155,7 @@ struct rfcomm_msc {
|
|||
struct rfcomm_session {
|
||||
struct list_head list;
|
||||
struct socket *sock;
|
||||
struct timer_list timer;
|
||||
unsigned long state;
|
||||
unsigned long flags;
|
||||
atomic_t refcnt;
|
||||
|
|
|
@ -34,6 +34,7 @@ menuconfig BT
|
|||
config BT_L2CAP
|
||||
tristate "L2CAP protocol support"
|
||||
depends on BT
|
||||
select CRC16
|
||||
help
|
||||
L2CAP (Logical Link Control and Adaptation Protocol) provides
|
||||
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)
|
|||
if (hdev->notify)
|
||||
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
|
||||
|
||||
atomic_set(&conn->devref, 0);
|
||||
|
||||
hci_conn_init_sysfs(conn);
|
||||
|
||||
tasklet_enable(&hdev->tx_task);
|
||||
|
@ -288,7 +290,7 @@ int hci_conn_del(struct hci_conn *conn)
|
|||
|
||||
skb_queue_purge(&conn->data_q);
|
||||
|
||||
hci_conn_del_sysfs(conn);
|
||||
hci_conn_put_device(conn);
|
||||
|
||||
hci_dev_put(hdev);
|
||||
|
||||
|
@ -583,6 +585,19 @@ void hci_conn_check_pending(struct hci_dev *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)
|
||||
{
|
||||
struct hci_conn_list_req req, *cl;
|
||||
|
|
|
@ -911,7 +911,7 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
hdev->reassembly[i] = NULL;
|
||||
|
||||
init_waitqueue_head(&hdev->req_wait_q);
|
||||
init_MUTEX(&hdev->req_lock);
|
||||
mutex_init(&hdev->req_lock);
|
||||
|
||||
inquiry_cache_init(hdev);
|
||||
|
||||
|
|
|
@ -887,6 +887,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
|
|||
} else
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
hci_conn_hold_device(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
|
||||
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
|
|||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
hci_conn_hold_device(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
break;
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include <linux/input.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hidraw.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
@ -92,10 +93,14 @@ static void __hidp_link_session(struct hidp_session *session)
|
|||
{
|
||||
__module_get(THIS_MODULE);
|
||||
list_add(&session->list, &hidp_session_list);
|
||||
|
||||
hci_conn_hold_device(session->conn);
|
||||
}
|
||||
|
||||
static void __hidp_unlink_session(struct hidp_session *session)
|
||||
{
|
||||
hci_conn_put_device(session->conn);
|
||||
|
||||
list_del(&session->list);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
@ -374,6 +379,7 @@ static void hidp_process_hid_control(struct hidp_session *session,
|
|||
|
||||
/* Kill session thread */
|
||||
atomic_inc(&session->terminate);
|
||||
hidp_schedule(session);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,7 +579,11 @@ static int hidp_session(void *arg)
|
|||
if (session->hid) {
|
||||
if (session->hid->claimed & HID_CLAIMED_INPUT)
|
||||
hidinput_disconnect(session->hid);
|
||||
if (session->hid->claimed & HID_CLAIMED_HIDRAW)
|
||||
hidraw_disconnect(session->hid);
|
||||
|
||||
hid_destroy_device(session->hid);
|
||||
session->hid = NULL;
|
||||
}
|
||||
|
||||
/* Wakeup user-space polling for socket errors */
|
||||
|
@ -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 *dst = &bt_sk(session->ctrl_sock->sk)->dst;
|
||||
struct device *device = NULL;
|
||||
struct hci_dev *hdev;
|
||||
struct hci_conn *conn;
|
||||
|
||||
hdev = hci_get_route(dst, src);
|
||||
if (!hdev)
|
||||
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);
|
||||
|
||||
return conn ? &conn->dev : NULL;
|
||||
return device;
|
||||
}
|
||||
|
||||
static int hidp_setup_input(struct hidp_session *session,
|
||||
struct hidp_connadd_req *req)
|
||||
{
|
||||
struct input_dev *input;
|
||||
int i;
|
||||
int err, i;
|
||||
|
||||
input = input_allocate_device();
|
||||
if (!input)
|
||||
|
@ -666,7 +678,13 @@ static int hidp_setup_input(struct hidp_session *session,
|
|||
|
||||
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)
|
||||
|
@ -748,13 +766,11 @@ static int hidp_setup_hid(struct hidp_session *session,
|
|||
{
|
||||
struct hid_device *hid;
|
||||
bdaddr_t src, dst;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
hid = hid_allocate_device();
|
||||
if (IS_ERR(hid)) {
|
||||
ret = PTR_ERR(session->hid);
|
||||
goto err;
|
||||
}
|
||||
if (IS_ERR(hid))
|
||||
return PTR_ERR(session->hid);
|
||||
|
||||
session->hid = hid;
|
||||
session->req = req;
|
||||
|
@ -776,16 +792,17 @@ static int hidp_setup_hid(struct hidp_session *session,
|
|||
hid->dev.parent = hidp_get_device(session);
|
||||
hid->ll_driver = &hidp_hid_driver;
|
||||
|
||||
ret = hid_add_device(hid);
|
||||
if (ret)
|
||||
goto err_hid;
|
||||
err = hid_add_device(hid);
|
||||
if (err < 0)
|
||||
goto failed;
|
||||
|
||||
return 0;
|
||||
err_hid:
|
||||
|
||||
failed:
|
||||
hid_destroy_device(hid);
|
||||
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)
|
||||
|
@ -835,13 +852,13 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
|
|||
if (req->rd_size > 0) {
|
||||
err = hidp_setup_hid(session, req);
|
||||
if (err && err != -ENODEV)
|
||||
goto err_skb;
|
||||
goto purge;
|
||||
}
|
||||
|
||||
if (!session->hid) {
|
||||
err = hidp_setup_input(session, req);
|
||||
if (err < 0)
|
||||
goto err_skb;
|
||||
goto purge;
|
||||
}
|
||||
|
||||
__hidp_link_session(session);
|
||||
|
@ -869,13 +886,20 @@ unlink:
|
|||
|
||||
__hidp_unlink_session(session);
|
||||
|
||||
if (session->input)
|
||||
if (session->input) {
|
||||
input_unregister_device(session->input);
|
||||
if (session->hid)
|
||||
session->input = NULL;
|
||||
}
|
||||
|
||||
if (session->hid) {
|
||||
hid_destroy_device(session->hid);
|
||||
err_skb:
|
||||
session->hid = NULL;
|
||||
}
|
||||
|
||||
purge:
|
||||
skb_queue_purge(&session->ctrl_transmit);
|
||||
skb_queue_purge(&session->intr_transmit);
|
||||
|
||||
failed:
|
||||
up_write(&hidp_session_sem);
|
||||
|
||||
|
|
|
@ -126,6 +126,8 @@ int hidp_get_conninfo(struct hidp_conninfo *ci);
|
|||
struct hidp_session {
|
||||
struct list_head list;
|
||||
|
||||
struct hci_conn *conn;
|
||||
|
||||
struct socket *ctrl_sock;
|
||||
struct socket *intr_sock;
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -244,6 +244,33 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d)
|
|||
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 ---- */
|
||||
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)
|
|||
|
||||
rfcomm_session_hold(s);
|
||||
|
||||
rfcomm_session_clear_timer(s);
|
||||
rfcomm_dlc_hold(d);
|
||||
list_add(&d->list, &s->dlcs);
|
||||
d->session = s;
|
||||
|
@ -335,6 +363,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
|
|||
d->session = NULL;
|
||||
rfcomm_dlc_put(d);
|
||||
|
||||
if (list_empty(&s->dlcs))
|
||||
rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT);
|
||||
|
||||
rfcomm_session_put(s);
|
||||
}
|
||||
|
||||
|
@ -567,6 +598,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
|
|||
|
||||
BT_DBG("session %p sock %p", s, sock);
|
||||
|
||||
setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s);
|
||||
|
||||
INIT_LIST_HEAD(&s->dlcs);
|
||||
s->state = state;
|
||||
s->sock = sock;
|
||||
|
@ -598,6 +631,7 @@ static void rfcomm_session_del(struct rfcomm_session *s)
|
|||
if (state == BT_CONNECTED)
|
||||
rfcomm_send_disc(s, 0);
|
||||
|
||||
rfcomm_session_clear_timer(s);
|
||||
sock_release(s->sock);
|
||||
kfree(s);
|
||||
|
||||
|
@ -639,6 +673,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err)
|
|||
__rfcomm_dlc_close(d, err);
|
||||
}
|
||||
|
||||
rfcomm_session_clear_timer(s);
|
||||
rfcomm_session_put(s);
|
||||
}
|
||||
|
||||
|
@ -1879,6 +1914,12 @@ static inline void rfcomm_process_sessions(void)
|
|||
struct rfcomm_session *s;
|
||||
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) {
|
||||
rfcomm_accept_connection(s);
|
||||
continue;
|
||||
|
@ -2080,7 +2121,7 @@ static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL);
|
|||
/* ---- Initialization ---- */
|
||||
static int __init rfcomm_init(void)
|
||||
{
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
l2cap_load();
|
||||
|
||||
|
@ -2088,33 +2129,35 @@ static int __init rfcomm_init(void)
|
|||
|
||||
rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");
|
||||
if (IS_ERR(rfcomm_thread)) {
|
||||
ret = PTR_ERR(rfcomm_thread);
|
||||
goto out_thread;
|
||||
err = PTR_ERR(rfcomm_thread);
|
||||
goto unregister;
|
||||
}
|
||||
|
||||
if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0)
|
||||
BT_ERR("Failed to create RFCOMM info file");
|
||||
|
||||
ret = rfcomm_init_ttys();
|
||||
if (ret)
|
||||
goto out_tty;
|
||||
err = rfcomm_init_ttys();
|
||||
if (err < 0)
|
||||
goto stop;
|
||||
|
||||
ret = rfcomm_init_sockets();
|
||||
if (ret)
|
||||
goto out_sock;
|
||||
err = rfcomm_init_sockets();
|
||||
if (err < 0)
|
||||
goto cleanup;
|
||||
|
||||
BT_INFO("RFCOMM ver %s", VERSION);
|
||||
|
||||
return 0;
|
||||
|
||||
out_sock:
|
||||
cleanup:
|
||||
rfcomm_cleanup_ttys();
|
||||
out_tty:
|
||||
|
||||
stop:
|
||||
kthread_stop(rfcomm_thread);
|
||||
out_thread:
|
||||
|
||||
unregister:
|
||||
hci_unregister_cb(&rfcomm_cb);
|
||||
|
||||
return ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit rfcomm_exit(void)
|
||||
|
|
|
@ -359,20 +359,9 @@ static void sco_sock_kill(struct sock *sk)
|
|||
sock_put(sk);
|
||||
}
|
||||
|
||||
/* Close socket.
|
||||
* Must be called on unlocked socket.
|
||||
*/
|
||||
static void sco_sock_close(struct sock *sk)
|
||||
static void __sco_sock_close(struct sock *sk)
|
||||
{
|
||||
struct sco_conn *conn;
|
||||
|
||||
sco_sock_clear_timer(sk);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
conn = sco_pi(sk)->conn;
|
||||
|
||||
BT_DBG("sk %p state %d conn %p socket %p", sk, sk->sk_state, conn, sk->sk_socket);
|
||||
BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
|
||||
|
||||
switch (sk->sk_state) {
|
||||
case BT_LISTEN:
|
||||
|
@ -390,9 +379,15 @@ static void sco_sock_close(struct sock *sk)
|
|||
sock_set_flag(sk, SOCK_ZAPPED);
|
||||
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);
|
||||
|
||||
sco_sock_kill(sk);
|
||||
}
|
||||
|
||||
|
@ -748,6 +743,30 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
|
|||
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)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
@ -969,7 +988,7 @@ static const struct proto_ops sco_sock_ops = {
|
|||
.ioctl = bt_sock_ioctl,
|
||||
.mmap = sock_no_mmap,
|
||||
.socketpair = sock_no_socketpair,
|
||||
.shutdown = sock_no_shutdown,
|
||||
.shutdown = sco_sock_shutdown,
|
||||
.setsockopt = sco_sock_setsockopt,
|
||||
.getsockopt = sco_sock_getsockopt
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче