Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
This commit is contained in:
commit
375e875c69
7 changed files with 462 additions and 280 deletions
|
@ -29,20 +29,6 @@
|
||||||
struct btmrvl_debugfs_data {
|
struct btmrvl_debugfs_data {
|
||||||
struct dentry *config_dir;
|
struct dentry *config_dir;
|
||||||
struct dentry *status_dir;
|
struct dentry *status_dir;
|
||||||
|
|
||||||
/* config */
|
|
||||||
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 ssize_t btmrvl_hscfgcmd_write(struct file *file,
|
static ssize_t btmrvl_hscfgcmd_write(struct file *file,
|
||||||
|
@ -91,47 +77,6 @@ static const struct file_operations btmrvl_hscfgcmd_fops = {
|
||||||
.llseek = default_llseek,
|
.llseek = default_llseek,
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
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 = simple_open,
|
|
||||||
.llseek = default_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf,
|
static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf,
|
||||||
size_t count, loff_t *ppos)
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
|
@ -178,47 +123,6 @@ static const struct file_operations btmrvl_pscmd_fops = {
|
||||||
.llseek = default_llseek,
|
.llseek = default_llseek,
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
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 = simple_open,
|
|
||||||
.llseek = default_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf,
|
static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf,
|
||||||
size_t count, loff_t *ppos)
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
|
@ -263,119 +167,6 @@ static const struct file_operations btmrvl_hscmd_fops = {
|
||||||
.llseek = default_llseek,
|
.llseek = default_llseek,
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
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 = simple_open,
|
|
||||||
.llseek = default_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
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 = simple_open,
|
|
||||||
.llseek = default_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
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 = simple_open,
|
|
||||||
.llseek = default_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
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 = simple_open,
|
|
||||||
.llseek = default_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
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 = simple_open,
|
|
||||||
.llseek = default_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
void btmrvl_debugfs_init(struct hci_dev *hdev)
|
void btmrvl_debugfs_init(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
struct btmrvl_private *priv = hci_get_drvdata(hdev);
|
struct btmrvl_private *priv = hci_get_drvdata(hdev);
|
||||||
|
@ -394,30 +185,28 @@ void btmrvl_debugfs_init(struct hci_dev *hdev)
|
||||||
|
|
||||||
dbg->config_dir = debugfs_create_dir("config", hdev->debugfs);
|
dbg->config_dir = debugfs_create_dir("config", hdev->debugfs);
|
||||||
|
|
||||||
dbg->psmode = debugfs_create_file("psmode", 0644, dbg->config_dir,
|
debugfs_create_u8("psmode", 0644, dbg->config_dir,
|
||||||
priv, &btmrvl_psmode_fops);
|
&priv->btmrvl_dev.psmode);
|
||||||
dbg->pscmd = debugfs_create_file("pscmd", 0644, dbg->config_dir,
|
debugfs_create_file("pscmd", 0644, dbg->config_dir,
|
||||||
priv, &btmrvl_pscmd_fops);
|
priv, &btmrvl_pscmd_fops);
|
||||||
dbg->gpiogap = debugfs_create_file("gpiogap", 0644, dbg->config_dir,
|
debugfs_create_x16("gpiogap", 0644, dbg->config_dir,
|
||||||
priv, &btmrvl_gpiogap_fops);
|
&priv->btmrvl_dev.gpio_gap);
|
||||||
dbg->hsmode = debugfs_create_file("hsmode", 0644, dbg->config_dir,
|
debugfs_create_u8("hsmode", 0644, dbg->config_dir,
|
||||||
priv, &btmrvl_hsmode_fops);
|
&priv->btmrvl_dev.hsmode);
|
||||||
dbg->hscmd = debugfs_create_file("hscmd", 0644, dbg->config_dir,
|
debugfs_create_file("hscmd", 0644, dbg->config_dir,
|
||||||
priv, &btmrvl_hscmd_fops);
|
priv, &btmrvl_hscmd_fops);
|
||||||
dbg->hscfgcmd = debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
|
debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
|
||||||
priv, &btmrvl_hscfgcmd_fops);
|
priv, &btmrvl_hscfgcmd_fops);
|
||||||
|
|
||||||
dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
|
dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
|
||||||
dbg->curpsmode = debugfs_create_file("curpsmode", 0444,
|
debugfs_create_u8("curpsmode", 0444, dbg->status_dir,
|
||||||
dbg->status_dir, priv,
|
&priv->adapter->psmode);
|
||||||
&btmrvl_curpsmode_fops);
|
debugfs_create_u8("psstate", 0444, dbg->status_dir,
|
||||||
dbg->psstate = debugfs_create_file("psstate", 0444, dbg->status_dir,
|
&priv->adapter->ps_state);
|
||||||
priv, &btmrvl_psstate_fops);
|
debugfs_create_u8("hsstate", 0444, dbg->status_dir,
|
||||||
dbg->hsstate = debugfs_create_file("hsstate", 0444, dbg->status_dir,
|
&priv->adapter->hs_state);
|
||||||
priv, &btmrvl_hsstate_fops);
|
debugfs_create_u8("txdnldready", 0444, dbg->status_dir,
|
||||||
dbg->txdnldready = debugfs_create_file("txdnldready", 0444,
|
&priv->btmrvl_dev.tx_dnld_rdy);
|
||||||
dbg->status_dir, priv,
|
|
||||||
&btmrvl_txdnldready_fops);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void btmrvl_debugfs_remove(struct hci_dev *hdev)
|
void btmrvl_debugfs_remove(struct hci_dev *hdev)
|
||||||
|
@ -428,19 +217,8 @@ void btmrvl_debugfs_remove(struct hci_dev *hdev)
|
||||||
if (!dbg)
|
if (!dbg)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
debugfs_remove(dbg->psmode);
|
debugfs_remove_recursive(dbg->config_dir);
|
||||||
debugfs_remove(dbg->pscmd);
|
debugfs_remove_recursive(dbg->status_dir);
|
||||||
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);
|
|
||||||
|
|
||||||
kfree(dbg);
|
kfree(dbg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,24 +228,24 @@ failed:
|
||||||
static int btmrvl_sdio_verify_fw_download(struct btmrvl_sdio_card *card,
|
static int btmrvl_sdio_verify_fw_download(struct btmrvl_sdio_card *card,
|
||||||
int pollnum)
|
int pollnum)
|
||||||
{
|
{
|
||||||
int ret = -ETIMEDOUT;
|
|
||||||
u16 firmwarestat;
|
u16 firmwarestat;
|
||||||
unsigned int tries;
|
int tries, ret;
|
||||||
|
|
||||||
/* Wait for firmware to become ready */
|
/* Wait for firmware to become ready */
|
||||||
for (tries = 0; tries < pollnum; tries++) {
|
for (tries = 0; tries < pollnum; tries++) {
|
||||||
if (btmrvl_sdio_read_fw_status(card, &firmwarestat) < 0)
|
sdio_claim_host(card->func);
|
||||||
|
ret = btmrvl_sdio_read_fw_status(card, &firmwarestat);
|
||||||
|
sdio_release_host(card->func);
|
||||||
|
if (ret < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (firmwarestat == FIRMWARE_READY) {
|
if (firmwarestat == FIRMWARE_READY)
|
||||||
ret = 0;
|
return 0;
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
msleep(10);
|
msleep(10);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card)
|
static int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card)
|
||||||
|
@ -874,7 +874,7 @@ exit:
|
||||||
|
|
||||||
static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
|
static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret;
|
||||||
u8 fws0;
|
u8 fws0;
|
||||||
int pollnum = MAX_POLL_TRIES;
|
int pollnum = MAX_POLL_TRIES;
|
||||||
|
|
||||||
|
@ -882,13 +882,14 @@ static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
|
||||||
BT_ERR("card or function is NULL!");
|
BT_ERR("card or function is NULL!");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
sdio_claim_host(card->func);
|
|
||||||
|
|
||||||
if (!btmrvl_sdio_verify_fw_download(card, 1)) {
|
if (!btmrvl_sdio_verify_fw_download(card, 1)) {
|
||||||
BT_DBG("Firmware already downloaded!");
|
BT_DBG("Firmware already downloaded!");
|
||||||
goto done;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sdio_claim_host(card->func);
|
||||||
|
|
||||||
/* Check if other function driver is downloading the firmware */
|
/* Check if other function driver is downloading the firmware */
|
||||||
fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret);
|
fws0 = sdio_readb(card->func, card->reg->card_fw_status0, &ret);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -918,15 +919,21 @@ static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sdio_release_host(card->func);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* winner or not, with this test the FW synchronizes when the
|
||||||
|
* module can continue its initialization
|
||||||
|
*/
|
||||||
if (btmrvl_sdio_verify_fw_download(card, pollnum)) {
|
if (btmrvl_sdio_verify_fw_download(card, pollnum)) {
|
||||||
BT_ERR("FW failed to be active in time!");
|
BT_ERR("FW failed to be active in time!");
|
||||||
ret = -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
sdio_release_host(card->func);
|
sdio_release_host(card->func);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -989,8 +996,6 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
|
||||||
goto unreg_dev;
|
goto unreg_dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
msleep(100);
|
|
||||||
|
|
||||||
btmrvl_sdio_enable_host_int(card);
|
btmrvl_sdio_enable_host_int(card);
|
||||||
|
|
||||||
priv = btmrvl_add_card(card);
|
priv = btmrvl_add_card(card);
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
|
#include <linux/firmware.h>
|
||||||
|
|
||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
|
@ -47,6 +48,7 @@ static struct usb_driver btusb_driver;
|
||||||
#define BTUSB_BROKEN_ISOC 0x20
|
#define BTUSB_BROKEN_ISOC 0x20
|
||||||
#define BTUSB_WRONG_SCO_MTU 0x40
|
#define BTUSB_WRONG_SCO_MTU 0x40
|
||||||
#define BTUSB_ATH3012 0x80
|
#define BTUSB_ATH3012 0x80
|
||||||
|
#define BTUSB_INTEL 0x100
|
||||||
|
|
||||||
static struct usb_device_id btusb_table[] = {
|
static struct usb_device_id btusb_table[] = {
|
||||||
/* Generic Bluetooth USB device */
|
/* Generic Bluetooth USB device */
|
||||||
|
@ -207,6 +209,9 @@ static struct usb_device_id blacklist_table[] = {
|
||||||
/* Frontline ComProbe Bluetooth Sniffer */
|
/* Frontline ComProbe Bluetooth Sniffer */
|
||||||
{ USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
|
{ USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
|
||||||
|
|
||||||
|
/* Intel Bluetooth device */
|
||||||
|
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
|
||||||
|
|
||||||
{ } /* Terminating entry */
|
{ } /* Terminating entry */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -943,6 +948,375 @@ static int btusb_setup_bcm92035(struct hci_dev *hdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct intel_version {
|
||||||
|
u8 status;
|
||||||
|
u8 hw_platform;
|
||||||
|
u8 hw_variant;
|
||||||
|
u8 hw_revision;
|
||||||
|
u8 fw_variant;
|
||||||
|
u8 fw_revision;
|
||||||
|
u8 fw_build_num;
|
||||||
|
u8 fw_build_ww;
|
||||||
|
u8 fw_build_yy;
|
||||||
|
u8 fw_patch_num;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
|
||||||
|
struct intel_version *ver)
|
||||||
|
{
|
||||||
|
const struct firmware *fw;
|
||||||
|
char fwname[64];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
snprintf(fwname, sizeof(fwname),
|
||||||
|
"intel/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.bseq",
|
||||||
|
ver->hw_platform, ver->hw_variant, ver->hw_revision,
|
||||||
|
ver->fw_variant, ver->fw_revision, ver->fw_build_num,
|
||||||
|
ver->fw_build_ww, ver->fw_build_yy);
|
||||||
|
|
||||||
|
ret = request_firmware(&fw, fwname, &hdev->dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (ret == -EINVAL) {
|
||||||
|
BT_ERR("%s Intel firmware file request failed (%d)",
|
||||||
|
hdev->name, ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_ERR("%s failed to open Intel firmware file: %s(%d)",
|
||||||
|
hdev->name, fwname, ret);
|
||||||
|
|
||||||
|
/* If the correct firmware patch file is not found, use the
|
||||||
|
* default firmware patch file instead
|
||||||
|
*/
|
||||||
|
snprintf(fwname, sizeof(fwname), "intel/ibt-hw-%x.%x.bseq",
|
||||||
|
ver->hw_platform, ver->hw_variant);
|
||||||
|
if (request_firmware(&fw, fwname, &hdev->dev) < 0) {
|
||||||
|
BT_ERR("%s failed to open default Intel fw file: %s",
|
||||||
|
hdev->name, fwname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_INFO("%s: Intel Bluetooth firmware file: %s", hdev->name, fwname);
|
||||||
|
|
||||||
|
return fw;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btusb_setup_intel_patching(struct hci_dev *hdev,
|
||||||
|
const struct firmware *fw,
|
||||||
|
const u8 **fw_ptr, int *disable_patch)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct hci_command_hdr *cmd;
|
||||||
|
const u8 *cmd_param;
|
||||||
|
struct hci_event_hdr *evt = NULL;
|
||||||
|
const u8 *evt_param = NULL;
|
||||||
|
int remain = fw->size - (*fw_ptr - fw->data);
|
||||||
|
|
||||||
|
/* The first byte indicates the types of the patch command or event.
|
||||||
|
* 0x01 means HCI command and 0x02 is HCI event. If the first bytes
|
||||||
|
* in the current firmware buffer doesn't start with 0x01 or
|
||||||
|
* the size of remain buffer is smaller than HCI command header,
|
||||||
|
* the firmware file is corrupted and it should stop the patching
|
||||||
|
* process.
|
||||||
|
*/
|
||||||
|
if (remain > HCI_COMMAND_HDR_SIZE && *fw_ptr[0] != 0x01) {
|
||||||
|
BT_ERR("%s Intel fw corrupted: invalid cmd read", hdev->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
(*fw_ptr)++;
|
||||||
|
remain--;
|
||||||
|
|
||||||
|
cmd = (struct hci_command_hdr *)(*fw_ptr);
|
||||||
|
*fw_ptr += sizeof(*cmd);
|
||||||
|
remain -= sizeof(*cmd);
|
||||||
|
|
||||||
|
/* Ensure that the remain firmware data is long enough than the length
|
||||||
|
* of command parameter. If not, the firmware file is corrupted.
|
||||||
|
*/
|
||||||
|
if (remain < cmd->plen) {
|
||||||
|
BT_ERR("%s Intel fw corrupted: invalid cmd len", hdev->name);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there is a command that loads a patch in the firmware
|
||||||
|
* file, then enable the patch upon success, otherwise just
|
||||||
|
* disable the manufacturer mode, for example patch activation
|
||||||
|
* is not required when the default firmware patch file is used
|
||||||
|
* because there are no patch data to load.
|
||||||
|
*/
|
||||||
|
if (*disable_patch && le16_to_cpu(cmd->opcode) == 0xfc8e)
|
||||||
|
*disable_patch = 0;
|
||||||
|
|
||||||
|
cmd_param = *fw_ptr;
|
||||||
|
*fw_ptr += cmd->plen;
|
||||||
|
remain -= cmd->plen;
|
||||||
|
|
||||||
|
/* This reads the expected events when the above command is sent to the
|
||||||
|
* device. Some vendor commands expects more than one events, for
|
||||||
|
* example command status event followed by vendor specific event.
|
||||||
|
* For this case, it only keeps the last expected event. so the command
|
||||||
|
* can be sent with __hci_cmd_sync_ev() which returns the sk_buff of
|
||||||
|
* last expected event.
|
||||||
|
*/
|
||||||
|
while (remain > HCI_EVENT_HDR_SIZE && *fw_ptr[0] == 0x02) {
|
||||||
|
(*fw_ptr)++;
|
||||||
|
remain--;
|
||||||
|
|
||||||
|
evt = (struct hci_event_hdr *)(*fw_ptr);
|
||||||
|
*fw_ptr += sizeof(*evt);
|
||||||
|
remain -= sizeof(*evt);
|
||||||
|
|
||||||
|
if (remain < evt->plen) {
|
||||||
|
BT_ERR("%s Intel fw corrupted: invalid evt len",
|
||||||
|
hdev->name);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
evt_param = *fw_ptr;
|
||||||
|
*fw_ptr += evt->plen;
|
||||||
|
remain -= evt->plen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Every HCI commands in the firmware file has its correspond event.
|
||||||
|
* If event is not found or remain is smaller than zero, the firmware
|
||||||
|
* file is corrupted.
|
||||||
|
*/
|
||||||
|
if (!evt || !evt_param || remain < 0) {
|
||||||
|
BT_ERR("%s Intel fw corrupted: invalid evt read", hdev->name);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
skb = __hci_cmd_sync_ev(hdev, le16_to_cpu(cmd->opcode), cmd->plen,
|
||||||
|
cmd_param, evt->evt, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
BT_ERR("%s sending Intel patch command (0x%4.4x) failed (%ld)",
|
||||||
|
hdev->name, cmd->opcode, PTR_ERR(skb));
|
||||||
|
return -PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It ensures that the returned event matches the event data read from
|
||||||
|
* the firmware file. At fist, it checks the length and then
|
||||||
|
* the contents of the event.
|
||||||
|
*/
|
||||||
|
if (skb->len != evt->plen) {
|
||||||
|
BT_ERR("%s mismatch event length (opcode 0x%4.4x)", hdev->name,
|
||||||
|
le16_to_cpu(cmd->opcode));
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(skb->data, evt_param, evt->plen)) {
|
||||||
|
BT_ERR("%s mismatch event parameter (opcode 0x%4.4x)",
|
||||||
|
hdev->name, le16_to_cpu(cmd->opcode));
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btusb_setup_intel(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
const struct firmware *fw;
|
||||||
|
const u8 *fw_ptr;
|
||||||
|
int disable_patch;
|
||||||
|
struct intel_version *ver;
|
||||||
|
|
||||||
|
const u8 mfg_enable[] = { 0x01, 0x00 };
|
||||||
|
const u8 mfg_disable[] = { 0x00, 0x00 };
|
||||||
|
const u8 mfg_reset_deactivate[] = { 0x00, 0x01 };
|
||||||
|
const u8 mfg_reset_activate[] = { 0x00, 0x02 };
|
||||||
|
|
||||||
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
/* The controller has a bug with the first HCI command sent to it
|
||||||
|
* returning number of completed commands as zero. This would stall the
|
||||||
|
* command processing in the Bluetooth core.
|
||||||
|
*
|
||||||
|
* As a workaround, send HCI Reset command first which will reset the
|
||||||
|
* number of completed commands and allow normal command processing
|
||||||
|
* from now on.
|
||||||
|
*/
|
||||||
|
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
BT_ERR("%s sending initial HCI reset command failed (%ld)",
|
||||||
|
hdev->name, PTR_ERR(skb));
|
||||||
|
return -PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
/* Read Intel specific controller version first to allow selection of
|
||||||
|
* which firmware file to load.
|
||||||
|
*
|
||||||
|
* The returned information are hardware variant and revision plus
|
||||||
|
* firmware variant, revision and build number.
|
||||||
|
*/
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
BT_ERR("%s reading Intel fw version command failed (%ld)",
|
||||||
|
hdev->name, PTR_ERR(skb));
|
||||||
|
return -PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skb->len != sizeof(*ver)) {
|
||||||
|
BT_ERR("%s Intel version event length mismatch", hdev->name);
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
ver = (struct intel_version *)skb->data;
|
||||||
|
if (ver->status) {
|
||||||
|
BT_ERR("%s Intel fw version event failed (%02x)", hdev->name,
|
||||||
|
ver->status);
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -bt_to_errno(ver->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_INFO("%s: read Intel version: %02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||||
|
hdev->name, ver->hw_platform, ver->hw_variant,
|
||||||
|
ver->hw_revision, ver->fw_variant, ver->fw_revision,
|
||||||
|
ver->fw_build_num, ver->fw_build_ww, ver->fw_build_yy,
|
||||||
|
ver->fw_patch_num);
|
||||||
|
|
||||||
|
/* fw_patch_num indicates the version of patch the device currently
|
||||||
|
* have. If there is no patch data in the device, it is always 0x00.
|
||||||
|
* So, if it is other than 0x00, no need to patch the deivce again.
|
||||||
|
*/
|
||||||
|
if (ver->fw_patch_num) {
|
||||||
|
BT_INFO("%s: Intel device is already patched. patch num: %02x",
|
||||||
|
hdev->name, ver->fw_patch_num);
|
||||||
|
kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Opens the firmware patch file based on the firmware version read
|
||||||
|
* from the controller. If it fails to open the matching firmware
|
||||||
|
* patch file, it tries to open the default firmware patch file.
|
||||||
|
* If no patch file is found, allow the device to operate without
|
||||||
|
* a patch.
|
||||||
|
*/
|
||||||
|
fw = btusb_setup_intel_get_fw(hdev, ver);
|
||||||
|
if (!fw) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fw_ptr = fw->data;
|
||||||
|
|
||||||
|
/* This Intel specific command enables the manufacturer mode of the
|
||||||
|
* controller.
|
||||||
|
*
|
||||||
|
* Only while this mode is enabled, the driver can download the
|
||||||
|
* firmware patch data and configuration parameters.
|
||||||
|
*/
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc11, 2, mfg_enable, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
BT_ERR("%s entering Intel manufacturer mode failed (%ld)",
|
||||||
|
hdev->name, PTR_ERR(skb));
|
||||||
|
release_firmware(fw);
|
||||||
|
return -PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skb->data[0]) {
|
||||||
|
u8 evt_status = skb->data[0];
|
||||||
|
BT_ERR("%s enable Intel manufacturer mode event failed (%02x)",
|
||||||
|
hdev->name, evt_status);
|
||||||
|
kfree_skb(skb);
|
||||||
|
release_firmware(fw);
|
||||||
|
return -bt_to_errno(evt_status);
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
disable_patch = 1;
|
||||||
|
|
||||||
|
/* The firmware data file consists of list of Intel specific HCI
|
||||||
|
* commands and its expected events. The first byte indicates the
|
||||||
|
* type of the message, either HCI command or HCI event.
|
||||||
|
*
|
||||||
|
* It reads the command and its expected event from the firmware file,
|
||||||
|
* and send to the controller. Once __hci_cmd_sync_ev() returns,
|
||||||
|
* the returned event is compared with the event read from the firmware
|
||||||
|
* file and it will continue until all the messages are downloaded to
|
||||||
|
* the controller.
|
||||||
|
*
|
||||||
|
* Once the firmware patching is completed successfully,
|
||||||
|
* the manufacturer mode is disabled with reset and activating the
|
||||||
|
* downloaded patch.
|
||||||
|
*
|
||||||
|
* If the firmware patching fails, the manufacturer mode is
|
||||||
|
* disabled with reset and deactivating the patch.
|
||||||
|
*
|
||||||
|
* If the default patch file is used, no reset is done when disabling
|
||||||
|
* the manufacturer.
|
||||||
|
*/
|
||||||
|
while (fw->size > fw_ptr - fw->data) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = btusb_setup_intel_patching(hdev, fw, &fw_ptr,
|
||||||
|
&disable_patch);
|
||||||
|
if (ret < 0)
|
||||||
|
goto exit_mfg_deactivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
release_firmware(fw);
|
||||||
|
|
||||||
|
if (disable_patch)
|
||||||
|
goto exit_mfg_disable;
|
||||||
|
|
||||||
|
/* Patching completed successfully and disable the manufacturer mode
|
||||||
|
* with reset and activate the downloaded firmware patches.
|
||||||
|
*/
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_activate),
|
||||||
|
mfg_reset_activate, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
|
||||||
|
hdev->name, PTR_ERR(skb));
|
||||||
|
return -PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
|
||||||
|
hdev->name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit_mfg_disable:
|
||||||
|
/* Disable the manufacturer mode without reset */
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_disable), mfg_disable,
|
||||||
|
HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
|
||||||
|
hdev->name, PTR_ERR(skb));
|
||||||
|
return -PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit_mfg_deactivate:
|
||||||
|
release_firmware(fw);
|
||||||
|
|
||||||
|
/* Patching failed. Disable the manufacturer mode with reset and
|
||||||
|
* deactivate the downloaded firmware patches.
|
||||||
|
*/
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc11, sizeof(mfg_reset_deactivate),
|
||||||
|
mfg_reset_deactivate, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
BT_ERR("%s exiting Intel manufacturer mode failed (%ld)",
|
||||||
|
hdev->name, PTR_ERR(skb));
|
||||||
|
return -PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
|
||||||
|
hdev->name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -1048,6 +1422,9 @@ static int btusb_probe(struct usb_interface *intf,
|
||||||
if (id->driver_info & BTUSB_BCM92035)
|
if (id->driver_info & BTUSB_BCM92035)
|
||||||
hdev->setup = btusb_setup_bcm92035;
|
hdev->setup = btusb_setup_bcm92035;
|
||||||
|
|
||||||
|
if (id->driver_info & BTUSB_INTEL)
|
||||||
|
hdev->setup = btusb_setup_intel;
|
||||||
|
|
||||||
/* Interface numbers are hardcoded in the specification */
|
/* Interface numbers are hardcoded in the specification */
|
||||||
data->isoc = usb_ifnum_to_if(data->udev, 1);
|
data->isoc = usb_ifnum_to_if(data->udev, 1);
|
||||||
|
|
||||||
|
|
|
@ -1081,17 +1081,19 @@ struct hci_request {
|
||||||
|
|
||||||
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
|
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
|
||||||
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
|
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
|
||||||
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param);
|
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
|
||||||
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, void *param,
|
const void *param);
|
||||||
u8 event);
|
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
|
||||||
|
const void *param, u8 event);
|
||||||
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
|
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
|
||||||
|
|
||||||
struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
void *param, u32 timeout);
|
const void *param, u32 timeout);
|
||||||
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
void *param, u8 event, u32 timeout);
|
const void *param, u8 event, u32 timeout);
|
||||||
|
|
||||||
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param);
|
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
|
||||||
|
const void *param);
|
||||||
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
|
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
|
||||||
void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
|
void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,8 @@ static void hci_req_cancel(struct hci_dev *hdev, int err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sk_buff *hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 event)
|
static struct sk_buff *hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
|
||||||
|
u8 event)
|
||||||
{
|
{
|
||||||
struct hci_ev_cmd_complete *ev;
|
struct hci_ev_cmd_complete *ev;
|
||||||
struct hci_event_hdr *hdr;
|
struct hci_event_hdr *hdr;
|
||||||
|
@ -134,7 +135,7 @@ failed:
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
void *param, u8 event, u32 timeout)
|
const void *param, u8 event, u32 timeout)
|
||||||
{
|
{
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
DECLARE_WAITQUEUE(wait, current);
|
||||||
struct hci_request req;
|
struct hci_request req;
|
||||||
|
@ -188,7 +189,7 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
EXPORT_SYMBOL(__hci_cmd_sync_ev);
|
EXPORT_SYMBOL(__hci_cmd_sync_ev);
|
||||||
|
|
||||||
struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
void *param, u32 timeout)
|
const void *param, u32 timeout)
|
||||||
{
|
{
|
||||||
return __hci_cmd_sync_ev(hdev, opcode, plen, param, 0, timeout);
|
return __hci_cmd_sync_ev(hdev, opcode, plen, param, 0, timeout);
|
||||||
}
|
}
|
||||||
|
@ -377,6 +378,8 @@ static void bredr_setup(struct hci_request *req)
|
||||||
|
|
||||||
static void le_setup(struct hci_request *req)
|
static void le_setup(struct hci_request *req)
|
||||||
{
|
{
|
||||||
|
struct hci_dev *hdev = req->hdev;
|
||||||
|
|
||||||
/* Read LE Buffer Size */
|
/* Read LE Buffer Size */
|
||||||
hci_req_add(req, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
|
hci_req_add(req, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
|
||||||
|
|
||||||
|
@ -391,6 +394,10 @@ static void le_setup(struct hci_request *req)
|
||||||
|
|
||||||
/* Read LE Supported States */
|
/* Read LE Supported States */
|
||||||
hci_req_add(req, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL);
|
hci_req_add(req, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL);
|
||||||
|
|
||||||
|
/* LE-only controllers have LE implicitly enabled */
|
||||||
|
if (!lmp_bredr_capable(hdev))
|
||||||
|
set_bit(HCI_LE_ENABLED, &hdev->dev_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
|
static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
|
||||||
|
@ -574,6 +581,10 @@ static void hci_set_le_support(struct hci_request *req)
|
||||||
struct hci_dev *hdev = req->hdev;
|
struct hci_dev *hdev = req->hdev;
|
||||||
struct hci_cp_write_le_host_supported cp;
|
struct hci_cp_write_le_host_supported cp;
|
||||||
|
|
||||||
|
/* LE-only devices do not support explicit enablement */
|
||||||
|
if (!lmp_bredr_capable(hdev))
|
||||||
|
return;
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
|
||||||
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
|
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
|
||||||
|
@ -2602,7 +2613,7 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode,
|
static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode,
|
||||||
u32 plen, void *param)
|
u32 plen, const void *param)
|
||||||
{
|
{
|
||||||
int len = HCI_COMMAND_HDR_SIZE + plen;
|
int len = HCI_COMMAND_HDR_SIZE + plen;
|
||||||
struct hci_command_hdr *hdr;
|
struct hci_command_hdr *hdr;
|
||||||
|
@ -2628,7 +2639,8 @@ static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send HCI command */
|
/* Send HCI command */
|
||||||
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
|
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
|
||||||
|
const void *param)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
@ -2652,8 +2664,8 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Queue a command to an asynchronous HCI request */
|
/* Queue a command to an asynchronous HCI request */
|
||||||
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, void *param,
|
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
|
||||||
u8 event)
|
const void *param, u8 event)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = req->hdev;
|
struct hci_dev *hdev = req->hdev;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
@ -2682,7 +2694,8 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, void *param,
|
||||||
skb_queue_tail(&req->cmd_q, skb);
|
skb_queue_tail(&req->cmd_q, skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param)
|
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
|
||||||
|
const void *param)
|
||||||
{
|
{
|
||||||
hci_req_add_ev(req, opcode, plen, param, 0);
|
hci_req_add_ev(req, opcode, plen, param, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6314,12 +6314,13 @@ drop:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void l2cap_att_channel(struct l2cap_conn *conn, u16 cid,
|
static void l2cap_att_channel(struct l2cap_conn *conn,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct l2cap_chan *chan;
|
struct l2cap_chan *chan;
|
||||||
|
|
||||||
chan = l2cap_global_chan_by_scid(0, cid, conn->src, conn->dst);
|
chan = l2cap_global_chan_by_scid(0, L2CAP_CID_LE_DATA,
|
||||||
|
conn->src, conn->dst);
|
||||||
if (!chan)
|
if (!chan)
|
||||||
goto drop;
|
goto drop;
|
||||||
|
|
||||||
|
@ -6368,7 +6369,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_CID_LE_DATA:
|
case L2CAP_CID_LE_DATA:
|
||||||
l2cap_att_channel(conn, cid, skb);
|
l2cap_att_channel(conn, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_CID_SMP:
|
case L2CAP_CID_SMP:
|
||||||
|
|
|
@ -1351,6 +1351,11 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
|
||||||
MGMT_STATUS_INVALID_PARAMS);
|
MGMT_STATUS_INVALID_PARAMS);
|
||||||
|
|
||||||
|
/* LE-only devices do not allow toggling LE on/off */
|
||||||
|
if (!lmp_bredr_capable(hdev))
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
|
||||||
|
MGMT_STATUS_REJECTED);
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
val = !!cp->val;
|
val = !!cp->val;
|
||||||
|
@ -3347,7 +3352,8 @@ static int powered_update_hci(struct hci_dev *hdev)
|
||||||
hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
|
hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
|
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
|
||||||
|
lmp_bredr_capable(hdev)) {
|
||||||
struct hci_cp_write_le_host_supported cp;
|
struct hci_cp_write_le_host_supported cp;
|
||||||
|
|
||||||
cp.le = 1;
|
cp.le = 1;
|
||||||
|
|
Loading…
Add table
Reference in a new issue