Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
This commit is contained in:
commit
0d4f55bc37
@ -30,3 +30,5 @@ hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o
|
|||||||
hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o
|
hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o
|
||||||
hci_uart-$(CONFIG_BT_HCIUART_3WIRE) += hci_h5.o
|
hci_uart-$(CONFIG_BT_HCIUART_3WIRE) += hci_h5.o
|
||||||
hci_uart-objs := $(hci_uart-y)
|
hci_uart-objs := $(hci_uart-y)
|
||||||
|
|
||||||
|
ccflags-y += -D__CHECK_ENDIAN__
|
||||||
|
@ -85,6 +85,7 @@ static struct usb_device_id ath3k_table[] = {
|
|||||||
{ USB_DEVICE(0x04CA, 0x3008) },
|
{ USB_DEVICE(0x04CA, 0x3008) },
|
||||||
{ USB_DEVICE(0x13d3, 0x3362) },
|
{ USB_DEVICE(0x13d3, 0x3362) },
|
||||||
{ USB_DEVICE(0x0CF3, 0xE004) },
|
{ USB_DEVICE(0x0CF3, 0xE004) },
|
||||||
|
{ USB_DEVICE(0x0CF3, 0xE005) },
|
||||||
{ USB_DEVICE(0x0930, 0x0219) },
|
{ USB_DEVICE(0x0930, 0x0219) },
|
||||||
{ USB_DEVICE(0x0489, 0xe057) },
|
{ USB_DEVICE(0x0489, 0xe057) },
|
||||||
{ USB_DEVICE(0x13d3, 0x3393) },
|
{ USB_DEVICE(0x13d3, 0x3393) },
|
||||||
@ -126,6 +127,7 @@ static struct usb_device_id ath3k_blist_tbl[] = {
|
|||||||
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
|
||||||
|
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/firmware.h>
|
||||||
|
|
||||||
#define BTM_HEADER_LEN 4
|
#define BTM_HEADER_LEN 4
|
||||||
#define BTM_UPLD_SIZE 2312
|
#define BTM_UPLD_SIZE 2312
|
||||||
@ -41,6 +43,8 @@ struct btmrvl_thread {
|
|||||||
struct btmrvl_device {
|
struct btmrvl_device {
|
||||||
void *card;
|
void *card;
|
||||||
struct hci_dev *hcidev;
|
struct hci_dev *hcidev;
|
||||||
|
struct device *dev;
|
||||||
|
const char *cal_data;
|
||||||
|
|
||||||
u8 dev_type;
|
u8 dev_type;
|
||||||
|
|
||||||
@ -91,6 +95,7 @@ struct btmrvl_private {
|
|||||||
#define BT_CMD_HOST_SLEEP_CONFIG 0x59
|
#define BT_CMD_HOST_SLEEP_CONFIG 0x59
|
||||||
#define BT_CMD_HOST_SLEEP_ENABLE 0x5A
|
#define BT_CMD_HOST_SLEEP_ENABLE 0x5A
|
||||||
#define BT_CMD_MODULE_CFG_REQ 0x5B
|
#define BT_CMD_MODULE_CFG_REQ 0x5B
|
||||||
|
#define BT_CMD_LOAD_CONFIG_DATA 0x61
|
||||||
|
|
||||||
/* Sub-commands: Module Bringup/Shutdown Request/Response */
|
/* Sub-commands: Module Bringup/Shutdown Request/Response */
|
||||||
#define MODULE_BRINGUP_REQ 0xF1
|
#define MODULE_BRINGUP_REQ 0xF1
|
||||||
@ -116,11 +121,8 @@ struct btmrvl_private {
|
|||||||
#define PS_SLEEP 0x01
|
#define PS_SLEEP 0x01
|
||||||
#define PS_AWAKE 0x00
|
#define PS_AWAKE 0x00
|
||||||
|
|
||||||
struct btmrvl_cmd {
|
#define BT_CMD_DATA_SIZE 32
|
||||||
__le16 ocf_ogf;
|
#define BT_CAL_DATA_SIZE 28
|
||||||
u8 length;
|
|
||||||
u8 data[4];
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
struct btmrvl_event {
|
struct btmrvl_event {
|
||||||
u8 ec; /* event counter */
|
u8 ec; /* event counter */
|
||||||
|
@ -57,8 +57,7 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
|
|||||||
ocf = hci_opcode_ocf(opcode);
|
ocf = hci_opcode_ocf(opcode);
|
||||||
ogf = hci_opcode_ogf(opcode);
|
ogf = hci_opcode_ogf(opcode);
|
||||||
|
|
||||||
if (ocf == BT_CMD_MODULE_CFG_REQ &&
|
if (priv->btmrvl_dev.sendcmdflag) {
|
||||||
priv->btmrvl_dev.sendcmdflag) {
|
|
||||||
priv->btmrvl_dev.sendcmdflag = false;
|
priv->btmrvl_dev.sendcmdflag = false;
|
||||||
priv->adapter->cmd_complete = true;
|
priv->adapter->cmd_complete = true;
|
||||||
wake_up_interruptible(&priv->adapter->cmd_wait_q);
|
wake_up_interruptible(&priv->adapter->cmd_wait_q);
|
||||||
@ -116,7 +115,6 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
|
|||||||
adapter->hs_state = HS_ACTIVATED;
|
adapter->hs_state = HS_ACTIVATED;
|
||||||
if (adapter->psmode)
|
if (adapter->psmode)
|
||||||
adapter->ps_state = PS_SLEEP;
|
adapter->ps_state = PS_SLEEP;
|
||||||
wake_up_interruptible(&adapter->cmd_wait_q);
|
|
||||||
BT_DBG("HS ACTIVATED!");
|
BT_DBG("HS ACTIVATED!");
|
||||||
} else {
|
} else {
|
||||||
BT_DBG("HS Enable failed");
|
BT_DBG("HS Enable failed");
|
||||||
@ -168,22 +166,24 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(btmrvl_process_event);
|
EXPORT_SYMBOL_GPL(btmrvl_process_event);
|
||||||
|
|
||||||
int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
|
static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 cmd_no,
|
||||||
|
const void *param, u8 len)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
struct btmrvl_cmd *cmd;
|
struct hci_command_hdr *hdr;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
|
skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
|
||||||
if (skb == NULL) {
|
if (skb == NULL) {
|
||||||
BT_ERR("No free skb");
|
BT_ERR("No free skb");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
|
hdr = (struct hci_command_hdr *)skb_put(skb, HCI_COMMAND_HDR_SIZE);
|
||||||
cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_MODULE_CFG_REQ));
|
hdr->opcode = cpu_to_le16(hci_opcode_pack(OGF, cmd_no));
|
||||||
cmd->length = 1;
|
hdr->plen = len;
|
||||||
cmd->data[0] = subcmd;
|
|
||||||
|
if (len)
|
||||||
|
memcpy(skb_put(skb, len), param, len);
|
||||||
|
|
||||||
bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
|
bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
|
||||||
|
|
||||||
@ -194,19 +194,23 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
|
|||||||
|
|
||||||
priv->adapter->cmd_complete = false;
|
priv->adapter->cmd_complete = false;
|
||||||
|
|
||||||
BT_DBG("Queue module cfg Command");
|
|
||||||
|
|
||||||
wake_up_interruptible(&priv->main_thread.wait_q);
|
wake_up_interruptible(&priv->main_thread.wait_q);
|
||||||
|
|
||||||
if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
|
if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
|
||||||
priv->adapter->cmd_complete,
|
priv->adapter->cmd_complete,
|
||||||
msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) {
|
msecs_to_jiffies(WAIT_UNTIL_CMD_RESP)))
|
||||||
ret = -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
BT_ERR("module_cfg_cmd(%x): timeout: %d",
|
|
||||||
subcmd, priv->btmrvl_dev.sendcmdflag);
|
|
||||||
}
|
|
||||||
|
|
||||||
BT_DBG("module cfg Command done");
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1);
|
||||||
|
if (ret)
|
||||||
|
BT_ERR("module_cfg_cmd(%x) failed\n", subcmd);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -214,61 +218,36 @@ EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
|
|||||||
|
|
||||||
int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv)
|
int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
int ret;
|
||||||
struct btmrvl_cmd *cmd;
|
u8 param[2];
|
||||||
|
|
||||||
skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
|
param[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
|
||||||
if (!skb) {
|
param[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);
|
||||||
BT_ERR("No free skb");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
|
BT_DBG("Sending HSCFG Command, gpio=0x%x, gap=0x%x",
|
||||||
cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF,
|
param[0], param[1]);
|
||||||
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;
|
ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2);
|
||||||
|
if (ret)
|
||||||
|
BT_ERR("HSCFG command failed\n");
|
||||||
|
|
||||||
skb->dev = (void *) priv->btmrvl_dev.hcidev;
|
return ret;
|
||||||
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]);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd);
|
EXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd);
|
||||||
|
|
||||||
int btmrvl_enable_ps(struct btmrvl_private *priv)
|
int btmrvl_enable_ps(struct btmrvl_private *priv)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
int ret;
|
||||||
struct btmrvl_cmd *cmd;
|
u8 param;
|
||||||
|
|
||||||
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)
|
if (priv->btmrvl_dev.psmode)
|
||||||
cmd->data[0] = BT_PS_ENABLE;
|
param = BT_PS_ENABLE;
|
||||||
else
|
else
|
||||||
cmd->data[0] = BT_PS_DISABLE;
|
param = BT_PS_DISABLE;
|
||||||
|
|
||||||
bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
|
ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, ¶m, 1);
|
||||||
|
if (ret)
|
||||||
skb->dev = (void *) priv->btmrvl_dev.hcidev;
|
BT_ERR("PSMODE command failed\n");
|
||||||
skb_queue_head(&priv->adapter->tx_queue, skb);
|
|
||||||
|
|
||||||
BT_DBG("Queue PSMODE Command:%d", cmd->data[0]);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -276,37 +255,11 @@ EXPORT_SYMBOL_GPL(btmrvl_enable_ps);
|
|||||||
|
|
||||||
int btmrvl_enable_hs(struct btmrvl_private *priv)
|
int btmrvl_enable_hs(struct btmrvl_private *priv)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
int ret;
|
||||||
struct btmrvl_cmd *cmd;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
|
ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0);
|
||||||
if (skb == NULL) {
|
if (ret)
|
||||||
BT_ERR("No free skb");
|
BT_ERR("Host sleep enable command failed\n");
|
||||||
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;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -479,6 +432,137 @@ static int btmrvl_open(struct hci_dev *hdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function parses provided calibration data input. It should contain
|
||||||
|
* hex bytes separated by space or new line character. Here is an example.
|
||||||
|
* 00 1C 01 37 FF FF FF FF 02 04 7F 01
|
||||||
|
* CE BA 00 00 00 2D C6 C0 00 00 00 00
|
||||||
|
* 00 F0 00 00
|
||||||
|
*/
|
||||||
|
static int btmrvl_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 dst_size)
|
||||||
|
{
|
||||||
|
const u8 *s = src;
|
||||||
|
u8 *d = dst;
|
||||||
|
int ret;
|
||||||
|
u8 tmp[3];
|
||||||
|
|
||||||
|
tmp[2] = '\0';
|
||||||
|
while ((s - src) <= len - 2) {
|
||||||
|
if (isspace(*s)) {
|
||||||
|
s++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isxdigit(*s)) {
|
||||||
|
if ((d - dst) >= dst_size) {
|
||||||
|
BT_ERR("calibration data file too big!!!");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(tmp, s, 2);
|
||||||
|
|
||||||
|
ret = kstrtou8(tmp, 16, d++);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
s += 2;
|
||||||
|
} else {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (d == dst)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btmrvl_load_cal_data(struct btmrvl_private *priv,
|
||||||
|
u8 *config_data)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
u8 data[BT_CMD_DATA_SIZE];
|
||||||
|
|
||||||
|
data[0] = 0x00;
|
||||||
|
data[1] = 0x00;
|
||||||
|
data[2] = 0x00;
|
||||||
|
data[3] = BT_CMD_DATA_SIZE - 4;
|
||||||
|
|
||||||
|
/* Swap cal-data bytes. Each four bytes are swapped. Considering 4
|
||||||
|
* byte SDIO header offset, mapping of input and output bytes will be
|
||||||
|
* {3, 2, 1, 0} -> {0+4, 1+4, 2+4, 3+4},
|
||||||
|
* {7, 6, 5, 4} -> {4+4, 5+4, 6+4, 7+4} */
|
||||||
|
for (i = 4; i < BT_CMD_DATA_SIZE; i++)
|
||||||
|
data[i] = config_data[(i / 4) * 8 - 1 - i];
|
||||||
|
|
||||||
|
print_hex_dump_bytes("Calibration data: ",
|
||||||
|
DUMP_PREFIX_OFFSET, data, BT_CMD_DATA_SIZE);
|
||||||
|
|
||||||
|
ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data,
|
||||||
|
BT_CMD_DATA_SIZE);
|
||||||
|
if (ret)
|
||||||
|
BT_ERR("Failed to download caibration data\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
btmrvl_process_cal_cfg(struct btmrvl_private *priv, u8 *data, u32 size)
|
||||||
|
{
|
||||||
|
u8 cal_data[BT_CAL_DATA_SIZE];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = btmrvl_parse_cal_cfg(data, size, cal_data, sizeof(cal_data));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = btmrvl_load_cal_data(priv, cal_data);
|
||||||
|
if (ret) {
|
||||||
|
BT_ERR("Fail to load calibrate data");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btmrvl_cal_data_config(struct btmrvl_private *priv)
|
||||||
|
{
|
||||||
|
const struct firmware *cfg;
|
||||||
|
int ret;
|
||||||
|
const char *cal_data = priv->btmrvl_dev.cal_data;
|
||||||
|
|
||||||
|
if (!cal_data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = request_firmware(&cfg, cal_data, priv->btmrvl_dev.dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
BT_DBG("Failed to get %s file, skipping cal data download",
|
||||||
|
cal_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = btmrvl_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size);
|
||||||
|
release_firmware(cfg);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btmrvl_setup(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
struct btmrvl_private *priv = hci_get_drvdata(hdev);
|
||||||
|
|
||||||
|
btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
|
||||||
|
|
||||||
|
if (btmrvl_cal_data_config(priv))
|
||||||
|
BT_ERR("Set cal data failed");
|
||||||
|
|
||||||
|
priv->btmrvl_dev.psmode = 1;
|
||||||
|
btmrvl_enable_ps(priv);
|
||||||
|
|
||||||
|
priv->btmrvl_dev.gpio_gap = 0xffff;
|
||||||
|
btmrvl_send_hscfg_cmd(priv);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function handles the event generated by firmware, rx data
|
* This function handles the event generated by firmware, rx data
|
||||||
* received from firmware, and tx data sent from kernel.
|
* received from firmware, and tx data sent from kernel.
|
||||||
@ -572,8 +656,7 @@ int btmrvl_register_hdev(struct btmrvl_private *priv)
|
|||||||
hdev->flush = btmrvl_flush;
|
hdev->flush = btmrvl_flush;
|
||||||
hdev->send = btmrvl_send_frame;
|
hdev->send = btmrvl_send_frame;
|
||||||
hdev->ioctl = btmrvl_ioctl;
|
hdev->ioctl = btmrvl_ioctl;
|
||||||
|
hdev->setup = btmrvl_setup;
|
||||||
btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
|
|
||||||
|
|
||||||
hdev->dev_type = priv->btmrvl_dev.dev_type;
|
hdev->dev_type = priv->btmrvl_dev.dev_type;
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
* this warranty disclaimer.
|
* this warranty disclaimer.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
#include <linux/firmware.h>
|
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#include <linux/mmc/sdio_ids.h>
|
#include <linux/mmc/sdio_ids.h>
|
||||||
@ -102,6 +101,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = {
|
|||||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
|
static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
|
||||||
.helper = "mrvl/sd8688_helper.bin",
|
.helper = "mrvl/sd8688_helper.bin",
|
||||||
.firmware = "mrvl/sd8688.bin",
|
.firmware = "mrvl/sd8688.bin",
|
||||||
|
.cal_data = NULL,
|
||||||
.reg = &btmrvl_reg_8688,
|
.reg = &btmrvl_reg_8688,
|
||||||
.sd_blksz_fw_dl = 64,
|
.sd_blksz_fw_dl = 64,
|
||||||
};
|
};
|
||||||
@ -109,6 +109,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
|
|||||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
|
static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
|
||||||
.helper = NULL,
|
.helper = NULL,
|
||||||
.firmware = "mrvl/sd8787_uapsta.bin",
|
.firmware = "mrvl/sd8787_uapsta.bin",
|
||||||
|
.cal_data = NULL,
|
||||||
.reg = &btmrvl_reg_87xx,
|
.reg = &btmrvl_reg_87xx,
|
||||||
.sd_blksz_fw_dl = 256,
|
.sd_blksz_fw_dl = 256,
|
||||||
};
|
};
|
||||||
@ -116,6 +117,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
|
|||||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
|
static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
|
||||||
.helper = NULL,
|
.helper = NULL,
|
||||||
.firmware = "mrvl/sd8797_uapsta.bin",
|
.firmware = "mrvl/sd8797_uapsta.bin",
|
||||||
|
.cal_data = "mrvl/sd8797_caldata.conf",
|
||||||
.reg = &btmrvl_reg_87xx,
|
.reg = &btmrvl_reg_87xx,
|
||||||
.sd_blksz_fw_dl = 256,
|
.sd_blksz_fw_dl = 256,
|
||||||
};
|
};
|
||||||
@ -123,6 +125,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
|
|||||||
static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
|
static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
|
||||||
.helper = NULL,
|
.helper = NULL,
|
||||||
.firmware = "mrvl/sd8897_uapsta.bin",
|
.firmware = "mrvl/sd8897_uapsta.bin",
|
||||||
|
.cal_data = NULL,
|
||||||
.reg = &btmrvl_reg_88xx,
|
.reg = &btmrvl_reg_88xx,
|
||||||
.sd_blksz_fw_dl = 256,
|
.sd_blksz_fw_dl = 256,
|
||||||
};
|
};
|
||||||
@ -1006,6 +1009,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
|
|||||||
struct btmrvl_sdio_device *data = (void *) id->driver_data;
|
struct btmrvl_sdio_device *data = (void *) id->driver_data;
|
||||||
card->helper = data->helper;
|
card->helper = data->helper;
|
||||||
card->firmware = data->firmware;
|
card->firmware = data->firmware;
|
||||||
|
card->cal_data = data->cal_data;
|
||||||
card->reg = data->reg;
|
card->reg = data->reg;
|
||||||
card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
|
card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
|
||||||
}
|
}
|
||||||
@ -1034,6 +1038,8 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
|
|||||||
}
|
}
|
||||||
|
|
||||||
card->priv = priv;
|
card->priv = priv;
|
||||||
|
priv->btmrvl_dev.dev = &card->func->dev;
|
||||||
|
priv->btmrvl_dev.cal_data = card->cal_data;
|
||||||
|
|
||||||
/* Initialize the interface specific function pointers */
|
/* Initialize the interface specific function pointers */
|
||||||
priv->hw_host_to_card = btmrvl_sdio_host_to_card;
|
priv->hw_host_to_card = btmrvl_sdio_host_to_card;
|
||||||
@ -1046,12 +1052,6 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
|
|||||||
goto disable_host_int;
|
goto disable_host_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->btmrvl_dev.psmode = 1;
|
|
||||||
btmrvl_enable_ps(priv);
|
|
||||||
|
|
||||||
priv->btmrvl_dev.gpio_gap = 0xffff;
|
|
||||||
btmrvl_send_hscfg_cmd(priv);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
disable_host_int:
|
disable_host_int:
|
||||||
@ -1222,4 +1222,5 @@ MODULE_FIRMWARE("mrvl/sd8688_helper.bin");
|
|||||||
MODULE_FIRMWARE("mrvl/sd8688.bin");
|
MODULE_FIRMWARE("mrvl/sd8688.bin");
|
||||||
MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
|
MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
|
||||||
MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
|
MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
|
||||||
|
MODULE_FIRMWARE("mrvl/sd8797_caldata.conf");
|
||||||
MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
|
MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
|
||||||
|
@ -85,6 +85,7 @@ struct btmrvl_sdio_card {
|
|||||||
u32 ioport;
|
u32 ioport;
|
||||||
const char *helper;
|
const char *helper;
|
||||||
const char *firmware;
|
const char *firmware;
|
||||||
|
const char *cal_data;
|
||||||
const struct btmrvl_sdio_card_reg *reg;
|
const struct btmrvl_sdio_card_reg *reg;
|
||||||
u16 sd_blksz_fw_dl;
|
u16 sd_blksz_fw_dl;
|
||||||
u8 rx_unit;
|
u8 rx_unit;
|
||||||
@ -94,6 +95,7 @@ struct btmrvl_sdio_card {
|
|||||||
struct btmrvl_sdio_device {
|
struct btmrvl_sdio_device {
|
||||||
const char *helper;
|
const char *helper;
|
||||||
const char *firmware;
|
const char *firmware;
|
||||||
|
const char *cal_data;
|
||||||
const struct btmrvl_sdio_card_reg *reg;
|
const struct btmrvl_sdio_card_reg *reg;
|
||||||
u16 sd_blksz_fw_dl;
|
u16 sd_blksz_fw_dl;
|
||||||
};
|
};
|
||||||
|
@ -102,6 +102,7 @@ static struct usb_device_id btusb_table[] = {
|
|||||||
|
|
||||||
/* Broadcom BCM20702A0 */
|
/* Broadcom BCM20702A0 */
|
||||||
{ USB_DEVICE(0x0b05, 0x17b5) },
|
{ USB_DEVICE(0x0b05, 0x17b5) },
|
||||||
|
{ USB_DEVICE(0x0b05, 0x17cb) },
|
||||||
{ USB_DEVICE(0x04ca, 0x2003) },
|
{ USB_DEVICE(0x04ca, 0x2003) },
|
||||||
{ USB_DEVICE(0x0489, 0xe042) },
|
{ USB_DEVICE(0x0489, 0xe042) },
|
||||||
{ USB_DEVICE(0x413c, 0x8197) },
|
{ USB_DEVICE(0x413c, 0x8197) },
|
||||||
@ -112,6 +113,9 @@ static struct usb_device_id btusb_table[] = {
|
|||||||
/*Broadcom devices with vendor specific id */
|
/*Broadcom devices with vendor specific id */
|
||||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
|
||||||
|
|
||||||
|
/* Belkin F8065bf - Broadcom based */
|
||||||
|
{ USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
|
||||||
|
|
||||||
{ } /* Terminating entry */
|
{ } /* Terminating entry */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -148,6 +152,7 @@ static struct usb_device_id blacklist_table[] = {
|
|||||||
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
|
||||||
|
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
|
||||||
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
|
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
@ -39,17 +40,17 @@
|
|||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
|
|
||||||
#define VERSION "1.3"
|
#define VERSION "1.4"
|
||||||
|
|
||||||
static bool amp;
|
static bool amp;
|
||||||
|
|
||||||
struct vhci_data {
|
struct vhci_data {
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
wait_queue_head_t read_wait;
|
wait_queue_head_t read_wait;
|
||||||
struct sk_buff_head readq;
|
struct sk_buff_head readq;
|
||||||
|
|
||||||
|
struct delayed_work open_timeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int vhci_open_dev(struct hci_dev *hdev)
|
static int vhci_open_dev(struct hci_dev *hdev)
|
||||||
@ -99,16 +100,62 @@ static int vhci_send_frame(struct sk_buff *skb)
|
|||||||
skb_queue_tail(&data->readq, skb);
|
skb_queue_tail(&data->readq, skb);
|
||||||
|
|
||||||
wake_up_interruptible(&data->read_wait);
|
wake_up_interruptible(&data->read_wait);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vhci_create_device(struct vhci_data *data, __u8 dev_type)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
skb = bt_skb_alloc(4, GFP_KERNEL);
|
||||||
|
if (!skb)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
hdev = hci_alloc_dev();
|
||||||
|
if (!hdev) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->hdev = hdev;
|
||||||
|
|
||||||
|
hdev->bus = HCI_VIRTUAL;
|
||||||
|
hdev->dev_type = dev_type;
|
||||||
|
hci_set_drvdata(hdev, data);
|
||||||
|
|
||||||
|
hdev->open = vhci_open_dev;
|
||||||
|
hdev->close = vhci_close_dev;
|
||||||
|
hdev->flush = vhci_flush;
|
||||||
|
hdev->send = vhci_send_frame;
|
||||||
|
|
||||||
|
if (hci_register_dev(hdev) < 0) {
|
||||||
|
BT_ERR("Can't register HCI device");
|
||||||
|
hci_free_dev(hdev);
|
||||||
|
data->hdev = NULL;
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
|
||||||
|
|
||||||
|
*skb_put(skb, 1) = 0xff;
|
||||||
|
*skb_put(skb, 1) = dev_type;
|
||||||
|
put_unaligned_le16(hdev->id, skb_put(skb, 2));
|
||||||
|
skb_queue_tail(&data->readq, skb);
|
||||||
|
|
||||||
|
wake_up_interruptible(&data->read_wait);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline ssize_t vhci_get_user(struct vhci_data *data,
|
static inline ssize_t vhci_get_user(struct vhci_data *data,
|
||||||
const char __user *buf, size_t count)
|
const char __user *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
__u8 pkt_type, dev_type;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (count > HCI_MAX_FRAME_SIZE)
|
if (count < 2 || count > HCI_MAX_FRAME_SIZE)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
skb = bt_skb_alloc(count, GFP_KERNEL);
|
skb = bt_skb_alloc(count, GFP_KERNEL);
|
||||||
@ -120,27 +167,70 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
|
|||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
skb->dev = (void *) data->hdev;
|
pkt_type = *((__u8 *) skb->data);
|
||||||
bt_cb(skb)->pkt_type = *((__u8 *) skb->data);
|
|
||||||
skb_pull(skb, 1);
|
skb_pull(skb, 1);
|
||||||
|
|
||||||
hci_recv_frame(skb);
|
switch (pkt_type) {
|
||||||
|
case HCI_EVENT_PKT:
|
||||||
|
case HCI_ACLDATA_PKT:
|
||||||
|
case HCI_SCODATA_PKT:
|
||||||
|
if (!data->hdev) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
return count;
|
skb->dev = (void *) data->hdev;
|
||||||
|
bt_cb(skb)->pkt_type = pkt_type;
|
||||||
|
|
||||||
|
ret = hci_recv_frame(skb);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HCI_VENDOR_PKT:
|
||||||
|
if (data->hdev) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EBADFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel_delayed_work_sync(&data->open_timeout);
|
||||||
|
|
||||||
|
dev_type = *((__u8 *) skb->data);
|
||||||
|
skb_pull(skb, 1);
|
||||||
|
|
||||||
|
if (skb->len > 0) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
if (dev_type != HCI_BREDR && dev_type != HCI_AMP)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = vhci_create_device(data, dev_type);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ret < 0) ? ret : count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline ssize_t vhci_put_user(struct vhci_data *data,
|
static inline ssize_t vhci_put_user(struct vhci_data *data,
|
||||||
struct sk_buff *skb, char __user *buf, int count)
|
struct sk_buff *skb,
|
||||||
|
char __user *buf, int count)
|
||||||
{
|
{
|
||||||
char __user *ptr = buf;
|
char __user *ptr = buf;
|
||||||
int len, total = 0;
|
int len;
|
||||||
|
|
||||||
len = min_t(unsigned int, skb->len, count);
|
len = min_t(unsigned int, skb->len, count);
|
||||||
|
|
||||||
if (copy_to_user(ptr, skb->data, len))
|
if (copy_to_user(ptr, skb->data, len))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
total += len;
|
if (!data->hdev)
|
||||||
|
return len;
|
||||||
|
|
||||||
data->hdev->stat.byte_tx += len;
|
data->hdev->stat.byte_tx += len;
|
||||||
|
|
||||||
@ -148,21 +238,19 @@ static inline ssize_t vhci_put_user(struct vhci_data *data,
|
|||||||
case HCI_COMMAND_PKT:
|
case HCI_COMMAND_PKT:
|
||||||
data->hdev->stat.cmd_tx++;
|
data->hdev->stat.cmd_tx++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_ACLDATA_PKT:
|
case HCI_ACLDATA_PKT:
|
||||||
data->hdev->stat.acl_tx++;
|
data->hdev->stat.acl_tx++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_SCODATA_PKT:
|
case HCI_SCODATA_PKT:
|
||||||
data->hdev->stat.sco_tx++;
|
data->hdev->stat.sco_tx++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return total;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t vhci_read(struct file *file,
|
static ssize_t vhci_read(struct file *file,
|
||||||
char __user *buf, size_t count, loff_t *pos)
|
char __user *buf, size_t count, loff_t *pos)
|
||||||
{
|
{
|
||||||
struct vhci_data *data = file->private_data;
|
struct vhci_data *data = file->private_data;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
@ -185,7 +273,7 @@ static ssize_t vhci_read(struct file *file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = wait_event_interruptible(data->read_wait,
|
ret = wait_event_interruptible(data->read_wait,
|
||||||
!skb_queue_empty(&data->readq));
|
!skb_queue_empty(&data->readq));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -194,7 +282,7 @@ static ssize_t vhci_read(struct file *file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t vhci_write(struct file *file,
|
static ssize_t vhci_write(struct file *file,
|
||||||
const char __user *buf, size_t count, loff_t *pos)
|
const char __user *buf, size_t count, loff_t *pos)
|
||||||
{
|
{
|
||||||
struct vhci_data *data = file->private_data;
|
struct vhci_data *data = file->private_data;
|
||||||
|
|
||||||
@ -213,10 +301,17 @@ static unsigned int vhci_poll(struct file *file, poll_table *wait)
|
|||||||
return POLLOUT | POLLWRNORM;
|
return POLLOUT | POLLWRNORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vhci_open_timeout(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct vhci_data *data = container_of(work, struct vhci_data,
|
||||||
|
open_timeout.work);
|
||||||
|
|
||||||
|
vhci_create_device(data, amp ? HCI_AMP : HCI_BREDR);
|
||||||
|
}
|
||||||
|
|
||||||
static int vhci_open(struct inode *inode, struct file *file)
|
static int vhci_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct vhci_data *data;
|
struct vhci_data *data;
|
||||||
struct hci_dev *hdev;
|
|
||||||
|
|
||||||
data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
|
data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
|
||||||
if (!data)
|
if (!data)
|
||||||
@ -225,35 +320,13 @@ static int vhci_open(struct inode *inode, struct file *file)
|
|||||||
skb_queue_head_init(&data->readq);
|
skb_queue_head_init(&data->readq);
|
||||||
init_waitqueue_head(&data->read_wait);
|
init_waitqueue_head(&data->read_wait);
|
||||||
|
|
||||||
hdev = hci_alloc_dev();
|
INIT_DELAYED_WORK(&data->open_timeout, vhci_open_timeout);
|
||||||
if (!hdev) {
|
|
||||||
kfree(data);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->hdev = hdev;
|
|
||||||
|
|
||||||
hdev->bus = HCI_VIRTUAL;
|
|
||||||
hci_set_drvdata(hdev, data);
|
|
||||||
|
|
||||||
if (amp)
|
|
||||||
hdev->dev_type = HCI_AMP;
|
|
||||||
|
|
||||||
hdev->open = vhci_open_dev;
|
|
||||||
hdev->close = vhci_close_dev;
|
|
||||||
hdev->flush = vhci_flush;
|
|
||||||
hdev->send = vhci_send_frame;
|
|
||||||
|
|
||||||
if (hci_register_dev(hdev) < 0) {
|
|
||||||
BT_ERR("Can't register HCI device");
|
|
||||||
kfree(data);
|
|
||||||
hci_free_dev(hdev);
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
file->private_data = data;
|
file->private_data = data;
|
||||||
nonseekable_open(inode, file);
|
nonseekable_open(inode, file);
|
||||||
|
|
||||||
|
schedule_delayed_work(&data->open_timeout, msecs_to_jiffies(1000));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,8 +335,12 @@ static int vhci_release(struct inode *inode, struct file *file)
|
|||||||
struct vhci_data *data = file->private_data;
|
struct vhci_data *data = file->private_data;
|
||||||
struct hci_dev *hdev = data->hdev;
|
struct hci_dev *hdev = data->hdev;
|
||||||
|
|
||||||
hci_unregister_dev(hdev);
|
cancel_delayed_work_sync(&data->open_timeout);
|
||||||
hci_free_dev(hdev);
|
|
||||||
|
if (hdev) {
|
||||||
|
hci_unregister_dev(hdev);
|
||||||
|
hci_free_dev(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
file->private_data = NULL;
|
file->private_data = NULL;
|
||||||
kfree(data);
|
kfree(data);
|
||||||
@ -309,3 +386,4 @@ MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
|||||||
MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
|
MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
|
||||||
MODULE_VERSION(VERSION);
|
MODULE_VERSION(VERSION);
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("devname:vhci");
|
||||||
|
@ -197,8 +197,8 @@ static inline bool bdaddr_type_is_le(__u8 type)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} })
|
#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
|
||||||
#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} })
|
#define BDADDR_NONE (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}})
|
||||||
|
|
||||||
/* Copy, swap, convert BD Address */
|
/* Copy, swap, convert BD Address */
|
||||||
static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)
|
static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)
|
||||||
@ -249,6 +249,7 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
|
|||||||
uint bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait);
|
uint bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait);
|
||||||
int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
|
int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
|
||||||
int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
|
int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo);
|
||||||
|
int bt_sock_wait_ready(struct sock *sk, unsigned long flags);
|
||||||
|
|
||||||
void bt_accept_enqueue(struct sock *parent, struct sock *sk);
|
void bt_accept_enqueue(struct sock *parent, struct sock *sk);
|
||||||
void bt_accept_unlink(struct sock *sk);
|
void bt_accept_unlink(struct sock *sk);
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
|
|
||||||
#define HCI_MAX_AMP_ASSOC_SIZE 672
|
#define HCI_MAX_AMP_ASSOC_SIZE 672
|
||||||
|
|
||||||
|
#define HCI_MAX_CSB_DATA_SIZE 252
|
||||||
|
|
||||||
/* HCI dev events */
|
/* HCI dev events */
|
||||||
#define HCI_DEV_REG 1
|
#define HCI_DEV_REG 1
|
||||||
#define HCI_DEV_UNREG 2
|
#define HCI_DEV_UNREG 2
|
||||||
@ -104,11 +106,13 @@ enum {
|
|||||||
enum {
|
enum {
|
||||||
HCI_SETUP,
|
HCI_SETUP,
|
||||||
HCI_AUTO_OFF,
|
HCI_AUTO_OFF,
|
||||||
|
HCI_RFKILLED,
|
||||||
HCI_MGMT,
|
HCI_MGMT,
|
||||||
HCI_PAIRABLE,
|
HCI_PAIRABLE,
|
||||||
HCI_SERVICE_CACHE,
|
HCI_SERVICE_CACHE,
|
||||||
HCI_DEBUG_KEYS,
|
HCI_DEBUG_KEYS,
|
||||||
HCI_UNREGISTER,
|
HCI_UNREGISTER,
|
||||||
|
HCI_USER_CHANNEL,
|
||||||
|
|
||||||
HCI_LE_SCAN,
|
HCI_LE_SCAN,
|
||||||
HCI_SSP_ENABLED,
|
HCI_SSP_ENABLED,
|
||||||
@ -120,6 +124,7 @@ enum {
|
|||||||
HCI_LINK_SECURITY,
|
HCI_LINK_SECURITY,
|
||||||
HCI_PERIODIC_INQ,
|
HCI_PERIODIC_INQ,
|
||||||
HCI_FAST_CONNECTABLE,
|
HCI_FAST_CONNECTABLE,
|
||||||
|
HCI_BREDR_ENABLED,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A mask for the flags that are supposed to remain when a reset happens
|
/* A mask for the flags that are supposed to remain when a reset happens
|
||||||
@ -623,6 +628,24 @@ struct hci_rp_logical_link_cancel {
|
|||||||
__u8 flow_spec_id;
|
__u8 flow_spec_id;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_SET_CSB 0x0441
|
||||||
|
struct hci_cp_set_csb {
|
||||||
|
__u8 enable;
|
||||||
|
__u8 lt_addr;
|
||||||
|
__u8 lpo_allowed;
|
||||||
|
__le16 packet_type;
|
||||||
|
__le16 interval_min;
|
||||||
|
__le16 interval_max;
|
||||||
|
__le16 csb_sv_tout;
|
||||||
|
} __packed;
|
||||||
|
struct hci_rp_set_csb {
|
||||||
|
__u8 status;
|
||||||
|
__u8 lt_addr;
|
||||||
|
__le16 interval;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_START_SYNC_TRAIN 0x0443
|
||||||
|
|
||||||
#define HCI_OP_SNIFF_MODE 0x0803
|
#define HCI_OP_SNIFF_MODE 0x0803
|
||||||
struct hci_cp_sniff_mode {
|
struct hci_cp_sniff_mode {
|
||||||
__le16 handle;
|
__le16 handle;
|
||||||
@ -693,9 +716,6 @@ struct hci_cp_sniff_subrate {
|
|||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
#define HCI_OP_SET_EVENT_MASK 0x0c01
|
#define HCI_OP_SET_EVENT_MASK 0x0c01
|
||||||
struct hci_cp_set_event_mask {
|
|
||||||
__u8 mask[8];
|
|
||||||
} __packed;
|
|
||||||
|
|
||||||
#define HCI_OP_RESET 0x0c03
|
#define HCI_OP_RESET 0x0c03
|
||||||
|
|
||||||
@ -825,6 +845,8 @@ struct hci_rp_read_inq_rsp_tx_power {
|
|||||||
__s8 tx_power;
|
__s8 tx_power;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_SET_EVENT_MASK_PAGE_2 0x0c63
|
||||||
|
|
||||||
#define HCI_OP_READ_FLOW_CONTROL_MODE 0x0c66
|
#define HCI_OP_READ_FLOW_CONTROL_MODE 0x0c66
|
||||||
struct hci_rp_read_flow_control_mode {
|
struct hci_rp_read_flow_control_mode {
|
||||||
__u8 status;
|
__u8 status;
|
||||||
@ -837,6 +859,50 @@ struct hci_cp_write_le_host_supported {
|
|||||||
__u8 simul;
|
__u8 simul;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_SET_RESERVED_LT_ADDR 0x0c74
|
||||||
|
struct hci_cp_set_reserved_lt_addr {
|
||||||
|
__u8 lt_addr;
|
||||||
|
} __packed;
|
||||||
|
struct hci_rp_set_reserved_lt_addr {
|
||||||
|
__u8 status;
|
||||||
|
__u8 lt_addr;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_DELETE_RESERVED_LT_ADDR 0x0c75
|
||||||
|
struct hci_cp_delete_reserved_lt_addr {
|
||||||
|
__u8 lt_addr;
|
||||||
|
} __packed;
|
||||||
|
struct hci_rp_delete_reserved_lt_addr {
|
||||||
|
__u8 status;
|
||||||
|
__u8 lt_addr;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_SET_CSB_DATA 0x0c76
|
||||||
|
struct hci_cp_set_csb_data {
|
||||||
|
__u8 lt_addr;
|
||||||
|
__u8 fragment;
|
||||||
|
__u8 data_length;
|
||||||
|
__u8 data[HCI_MAX_CSB_DATA_SIZE];
|
||||||
|
} __packed;
|
||||||
|
struct hci_rp_set_csb_data {
|
||||||
|
__u8 status;
|
||||||
|
__u8 lt_addr;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_READ_SYNC_TRAIN_PARAMS 0x0c77
|
||||||
|
|
||||||
|
#define HCI_OP_WRITE_SYNC_TRAIN_PARAMS 0x0c78
|
||||||
|
struct hci_cp_write_sync_train_params {
|
||||||
|
__le16 interval_min;
|
||||||
|
__le16 interval_max;
|
||||||
|
__le32 sync_train_tout;
|
||||||
|
__u8 service_data;
|
||||||
|
} __packed;
|
||||||
|
struct hci_rp_write_sync_train_params {
|
||||||
|
__u8 status;
|
||||||
|
__le16 sync_train_int;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#define HCI_OP_READ_LOCAL_VERSION 0x1001
|
#define HCI_OP_READ_LOCAL_VERSION 0x1001
|
||||||
struct hci_rp_read_local_version {
|
struct hci_rp_read_local_version {
|
||||||
__u8 status;
|
__u8 status;
|
||||||
@ -974,6 +1040,8 @@ struct hci_rp_le_read_local_features {
|
|||||||
__u8 features[8];
|
__u8 features[8];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_OP_LE_SET_RANDOM_ADDR 0x2005
|
||||||
|
|
||||||
#define HCI_OP_LE_READ_ADV_TX_POWER 0x2007
|
#define HCI_OP_LE_READ_ADV_TX_POWER 0x2007
|
||||||
struct hci_rp_le_read_adv_tx_power {
|
struct hci_rp_le_read_adv_tx_power {
|
||||||
__u8 status;
|
__u8 status;
|
||||||
@ -1437,6 +1505,13 @@ struct hci_ev_num_comp_blocks {
|
|||||||
struct hci_comp_blocks_info handles[0];
|
struct hci_comp_blocks_info handles[0];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_EV_SYNC_TRAIN_COMPLETE 0x4F
|
||||||
|
struct hci_ev_sync_train_complete {
|
||||||
|
__u8 status;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define HCI_EV_SLAVE_PAGE_RESP_TIMEOUT 0x54
|
||||||
|
|
||||||
/* Low energy meta events */
|
/* Low energy meta events */
|
||||||
#define LE_CONN_ROLE_MASTER 0x00
|
#define LE_CONN_ROLE_MASTER 0x00
|
||||||
|
|
||||||
@ -1570,6 +1645,7 @@ struct sockaddr_hci {
|
|||||||
#define HCI_DEV_NONE 0xffff
|
#define HCI_DEV_NONE 0xffff
|
||||||
|
|
||||||
#define HCI_CHANNEL_RAW 0
|
#define HCI_CHANNEL_RAW 0
|
||||||
|
#define HCI_CHANNEL_USER 1
|
||||||
#define HCI_CHANNEL_MONITOR 2
|
#define HCI_CHANNEL_MONITOR 2
|
||||||
#define HCI_CHANNEL_CONTROL 3
|
#define HCI_CHANNEL_CONTROL 3
|
||||||
|
|
||||||
|
@ -140,6 +140,7 @@ struct hci_dev {
|
|||||||
__u8 bus;
|
__u8 bus;
|
||||||
__u8 dev_type;
|
__u8 dev_type;
|
||||||
bdaddr_t bdaddr;
|
bdaddr_t bdaddr;
|
||||||
|
bdaddr_t static_addr;
|
||||||
__u8 dev_name[HCI_MAX_NAME_LENGTH];
|
__u8 dev_name[HCI_MAX_NAME_LENGTH];
|
||||||
__u8 short_name[HCI_MAX_SHORT_NAME_LENGTH];
|
__u8 short_name[HCI_MAX_SHORT_NAME_LENGTH];
|
||||||
__u8 eir[HCI_MAX_EIR_LENGTH];
|
__u8 eir[HCI_MAX_EIR_LENGTH];
|
||||||
@ -1168,7 +1169,6 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
|
|||||||
int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
|
int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
|
||||||
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
|
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
|
||||||
u8 *randomizer, u8 status);
|
u8 *randomizer, u8 status);
|
||||||
int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
|
|
||||||
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||||
u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
|
u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
|
||||||
u8 ssp, u8 *eir, u16 eir_len);
|
u8 ssp, u8 *eir, u16 eir_len);
|
||||||
|
@ -564,6 +564,7 @@ struct l2cap_conn {
|
|||||||
|
|
||||||
__u32 feat_mask;
|
__u32 feat_mask;
|
||||||
__u8 fixed_chan_mask;
|
__u8 fixed_chan_mask;
|
||||||
|
bool hs_enabled;
|
||||||
|
|
||||||
__u8 info_state;
|
__u8 info_state;
|
||||||
__u8 info_ident;
|
__u8 info_ident;
|
||||||
|
@ -93,6 +93,7 @@ struct mgmt_rp_read_index_list {
|
|||||||
#define MGMT_SETTING_BREDR 0x00000080
|
#define MGMT_SETTING_BREDR 0x00000080
|
||||||
#define MGMT_SETTING_HS 0x00000100
|
#define MGMT_SETTING_HS 0x00000100
|
||||||
#define MGMT_SETTING_LE 0x00000200
|
#define MGMT_SETTING_LE 0x00000200
|
||||||
|
#define MGMT_SETTING_ADVERTISING 0x00000400
|
||||||
|
|
||||||
#define MGMT_OP_READ_INFO 0x0004
|
#define MGMT_OP_READ_INFO 0x0004
|
||||||
#define MGMT_READ_INFO_SIZE 0
|
#define MGMT_READ_INFO_SIZE 0
|
||||||
@ -351,6 +352,16 @@ struct mgmt_cp_set_device_id {
|
|||||||
} __packed;
|
} __packed;
|
||||||
#define MGMT_SET_DEVICE_ID_SIZE 8
|
#define MGMT_SET_DEVICE_ID_SIZE 8
|
||||||
|
|
||||||
|
#define MGMT_OP_SET_ADVERTISING 0x0029
|
||||||
|
|
||||||
|
#define MGMT_OP_SET_BREDR 0x002A
|
||||||
|
|
||||||
|
#define MGMT_OP_SET_STATIC_ADDRESS 0x002B
|
||||||
|
struct mgmt_cp_set_static_address {
|
||||||
|
bdaddr_t bdaddr;
|
||||||
|
} __packed;
|
||||||
|
#define MGMT_SET_STATIC_ADDRESS_SIZE 6
|
||||||
|
|
||||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||||
struct mgmt_ev_cmd_complete {
|
struct mgmt_ev_cmd_complete {
|
||||||
__le16 opcode;
|
__le16 opcode;
|
||||||
|
@ -11,3 +11,5 @@ obj-$(CONFIG_BT_HIDP) += hidp/
|
|||||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
|
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
|
||||||
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
|
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
|
||||||
a2mp.o amp.o
|
a2mp.o amp.o
|
||||||
|
|
||||||
|
subdir-ccflags-y += -D__CHECK_ENDIAN__
|
||||||
|
@ -490,6 +490,7 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(bt_sock_ioctl);
|
EXPORT_SYMBOL(bt_sock_ioctl);
|
||||||
|
|
||||||
|
/* This function expects the sk lock to be held when called */
|
||||||
int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
|
int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
|
||||||
{
|
{
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
DECLARE_WAITQUEUE(wait, current);
|
||||||
@ -525,6 +526,46 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(bt_sock_wait_state);
|
EXPORT_SYMBOL(bt_sock_wait_state);
|
||||||
|
|
||||||
|
/* This function expects the sk lock to be held when called */
|
||||||
|
int bt_sock_wait_ready(struct sock *sk, unsigned long flags)
|
||||||
|
{
|
||||||
|
DECLARE_WAITQUEUE(wait, current);
|
||||||
|
unsigned long timeo;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
BT_DBG("sk %p", sk);
|
||||||
|
|
||||||
|
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
|
||||||
|
|
||||||
|
add_wait_queue(sk_sleep(sk), &wait);
|
||||||
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
while (test_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags)) {
|
||||||
|
if (!timeo) {
|
||||||
|
err = -EAGAIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signal_pending(current)) {
|
||||||
|
err = sock_intr_errno(timeo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
release_sock(sk);
|
||||||
|
timeo = schedule_timeout(timeo);
|
||||||
|
lock_sock(sk);
|
||||||
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
|
|
||||||
|
err = sock_error(sk);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
__set_current_state(TASK_RUNNING);
|
||||||
|
remove_wait_queue(sk_sleep(sk), &wait);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(bt_sock_wait_ready);
|
||||||
|
|
||||||
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
||||||
struct bt_seq_state {
|
struct bt_seq_state {
|
||||||
struct bt_sock_list *l;
|
struct bt_sock_list *l;
|
||||||
|
@ -518,6 +518,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
|
|||||||
list_for_each_entry(d, &hci_dev_list, list) {
|
list_for_each_entry(d, &hci_dev_list, list) {
|
||||||
if (!test_bit(HCI_UP, &d->flags) ||
|
if (!test_bit(HCI_UP, &d->flags) ||
|
||||||
test_bit(HCI_RAW, &d->flags) ||
|
test_bit(HCI_RAW, &d->flags) ||
|
||||||
|
test_bit(HCI_USER_CHANNEL, &d->dev_flags) ||
|
||||||
d->dev_type != HCI_BREDR)
|
d->dev_type != HCI_BREDR)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -580,6 +581,9 @@ static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
|
|||||||
{
|
{
|
||||||
struct hci_conn *acl;
|
struct hci_conn *acl;
|
||||||
|
|
||||||
|
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
|
||||||
|
return ERR_PTR(-ENOTSUPP);
|
||||||
|
|
||||||
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
|
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
|
||||||
if (!acl) {
|
if (!acl) {
|
||||||
acl = hci_conn_add(hdev, ACL_LINK, dst);
|
acl = hci_conn_add(hdev, ACL_LINK, dst);
|
||||||
|
@ -519,6 +519,8 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
|
|||||||
|
|
||||||
if (lmp_bredr_capable(hdev))
|
if (lmp_bredr_capable(hdev))
|
||||||
bredr_setup(req);
|
bredr_setup(req);
|
||||||
|
else
|
||||||
|
clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
|
||||||
|
|
||||||
if (lmp_le_capable(hdev))
|
if (lmp_le_capable(hdev))
|
||||||
le_setup(req);
|
le_setup(req);
|
||||||
@ -607,6 +609,34 @@ static void hci_set_le_support(struct hci_request *req)
|
|||||||
&cp);
|
&cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hci_set_event_mask_page_2(struct hci_request *req)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = req->hdev;
|
||||||
|
u8 events[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
|
|
||||||
|
/* If Connectionless Slave Broadcast master role is supported
|
||||||
|
* enable all necessary events for it.
|
||||||
|
*/
|
||||||
|
if (hdev->features[2][0] & 0x01) {
|
||||||
|
events[1] |= 0x40; /* Triggered Clock Capture */
|
||||||
|
events[1] |= 0x80; /* Synchronization Train Complete */
|
||||||
|
events[2] |= 0x10; /* Slave Page Response Timeout */
|
||||||
|
events[2] |= 0x20; /* CSB Channel Map Change */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If Connectionless Slave Broadcast slave role is supported
|
||||||
|
* enable all necessary events for it.
|
||||||
|
*/
|
||||||
|
if (hdev->features[2][0] & 0x02) {
|
||||||
|
events[2] |= 0x01; /* Synchronization Train Received */
|
||||||
|
events[2] |= 0x02; /* CSB Receive */
|
||||||
|
events[2] |= 0x04; /* CSB Timeout */
|
||||||
|
events[2] |= 0x08; /* Truncated Page Complete */
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events);
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_init3_req(struct hci_request *req, unsigned long opt)
|
static void hci_init3_req(struct hci_request *req, unsigned long opt)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = req->hdev;
|
struct hci_dev *hdev = req->hdev;
|
||||||
@ -648,6 +678,19 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hci_init4_req(struct hci_request *req, unsigned long opt)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = req->hdev;
|
||||||
|
|
||||||
|
/* Set event mask page 2 if the HCI command for it is supported */
|
||||||
|
if (hdev->commands[22] & 0x04)
|
||||||
|
hci_set_event_mask_page_2(req);
|
||||||
|
|
||||||
|
/* Check for Synchronization Train support */
|
||||||
|
if (hdev->features[2][0] & 0x04)
|
||||||
|
hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static int __hci_init(struct hci_dev *hdev)
|
static int __hci_init(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@ -667,7 +710,11 @@ static int __hci_init(struct hci_dev *hdev)
|
|||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
return __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
|
err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
return __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_scan_req(struct hci_request *req, unsigned long opt)
|
static void hci_scan_req(struct hci_request *req, unsigned long opt)
|
||||||
@ -984,6 +1031,16 @@ int hci_inquiry(void __user *arg)
|
|||||||
if (!hdev)
|
if (!hdev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
|
||||||
|
err = -EBUSY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
|
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
|
||||||
inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
|
inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
|
||||||
@ -1051,14 +1108,14 @@ static u8 create_ad(struct hci_dev *hdev, u8 *ptr)
|
|||||||
if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
|
if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
|
||||||
flags |= LE_AD_GENERAL;
|
flags |= LE_AD_GENERAL;
|
||||||
|
|
||||||
if (!lmp_bredr_capable(hdev))
|
if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
|
||||||
|
if (lmp_le_br_capable(hdev))
|
||||||
|
flags |= LE_AD_SIM_LE_BREDR_CTRL;
|
||||||
|
if (lmp_host_le_br_capable(hdev))
|
||||||
|
flags |= LE_AD_SIM_LE_BREDR_HOST;
|
||||||
|
} else {
|
||||||
flags |= LE_AD_NO_BREDR;
|
flags |= LE_AD_NO_BREDR;
|
||||||
|
}
|
||||||
if (lmp_le_br_capable(hdev))
|
|
||||||
flags |= LE_AD_SIM_LE_BREDR_CTRL;
|
|
||||||
|
|
||||||
if (lmp_host_le_br_capable(hdev))
|
|
||||||
flags |= LE_AD_SIM_LE_BREDR_HOST;
|
|
||||||
|
|
||||||
if (flags) {
|
if (flags) {
|
||||||
BT_DBG("adv flags 0x%02x", flags);
|
BT_DBG("adv flags 0x%02x", flags);
|
||||||
@ -1126,17 +1183,10 @@ void hci_update_ad(struct hci_request *req)
|
|||||||
hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
|
hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- HCI ioctl helpers ---- */
|
static int hci_dev_do_open(struct hci_dev *hdev)
|
||||||
|
|
||||||
int hci_dev_open(__u16 dev)
|
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
hdev = hci_dev_get(dev);
|
|
||||||
if (!hdev)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
BT_DBG("%s %p", hdev->name, hdev);
|
BT_DBG("%s %p", hdev->name, hdev);
|
||||||
|
|
||||||
hci_req_lock(hdev);
|
hci_req_lock(hdev);
|
||||||
@ -1146,7 +1196,11 @@ int hci_dev_open(__u16 dev)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdev->rfkill && rfkill_blocked(hdev->rfkill)) {
|
/* Check for rfkill but allow the HCI setup stage to proceed
|
||||||
|
* (which in itself doesn't cause any RF activity).
|
||||||
|
*/
|
||||||
|
if (test_bit(HCI_RFKILLED, &hdev->dev_flags) &&
|
||||||
|
!test_bit(HCI_SETUP, &hdev->dev_flags)) {
|
||||||
ret = -ERFKILL;
|
ret = -ERFKILL;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
@ -1168,16 +1222,11 @@ int hci_dev_open(__u16 dev)
|
|||||||
ret = hdev->setup(hdev);
|
ret = hdev->setup(hdev);
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
/* Treat all non BR/EDR controllers as raw devices if
|
|
||||||
* enable_hs is not set.
|
|
||||||
*/
|
|
||||||
if (hdev->dev_type != HCI_BREDR && !enable_hs)
|
|
||||||
set_bit(HCI_RAW, &hdev->flags);
|
|
||||||
|
|
||||||
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
|
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
|
||||||
set_bit(HCI_RAW, &hdev->flags);
|
set_bit(HCI_RAW, &hdev->flags);
|
||||||
|
|
||||||
if (!test_bit(HCI_RAW, &hdev->flags))
|
if (!test_bit(HCI_RAW, &hdev->flags) &&
|
||||||
|
!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
|
||||||
ret = __hci_init(hdev);
|
ret = __hci_init(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1188,6 +1237,7 @@ int hci_dev_open(__u16 dev)
|
|||||||
set_bit(HCI_UP, &hdev->flags);
|
set_bit(HCI_UP, &hdev->flags);
|
||||||
hci_notify(hdev, HCI_DEV_UP);
|
hci_notify(hdev, HCI_DEV_UP);
|
||||||
if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
|
if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
|
||||||
|
!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
|
||||||
mgmt_valid_hdev(hdev)) {
|
mgmt_valid_hdev(hdev)) {
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
mgmt_powered(hdev, 1);
|
mgmt_powered(hdev, 1);
|
||||||
@ -1216,10 +1266,37 @@ int hci_dev_open(__u16 dev)
|
|||||||
|
|
||||||
done:
|
done:
|
||||||
hci_req_unlock(hdev);
|
hci_req_unlock(hdev);
|
||||||
hci_dev_put(hdev);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- HCI ioctl helpers ---- */
|
||||||
|
|
||||||
|
int hci_dev_open(__u16 dev)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
hdev = hci_dev_get(dev);
|
||||||
|
if (!hdev)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
/* We need to ensure that no other power on/off work is pending
|
||||||
|
* before proceeding to call hci_dev_do_open. This is
|
||||||
|
* particularly important if the setup procedure has not yet
|
||||||
|
* completed.
|
||||||
|
*/
|
||||||
|
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
|
||||||
|
cancel_delayed_work(&hdev->power_off);
|
||||||
|
|
||||||
|
flush_workqueue(hdev->req_workqueue);
|
||||||
|
|
||||||
|
err = hci_dev_do_open(hdev);
|
||||||
|
|
||||||
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int hci_dev_do_close(struct hci_dev *hdev)
|
static int hci_dev_do_close(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
BT_DBG("%s %p", hdev->name, hdev);
|
BT_DBG("%s %p", hdev->name, hdev);
|
||||||
@ -1324,11 +1401,17 @@ int hci_dev_close(__u16 dev)
|
|||||||
if (!hdev)
|
if (!hdev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
|
||||||
|
err = -EBUSY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
|
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
|
||||||
cancel_delayed_work(&hdev->power_off);
|
cancel_delayed_work(&hdev->power_off);
|
||||||
|
|
||||||
err = hci_dev_do_close(hdev);
|
err = hci_dev_do_close(hdev);
|
||||||
|
|
||||||
|
done:
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -1344,8 +1427,15 @@ int hci_dev_reset(__u16 dev)
|
|||||||
|
|
||||||
hci_req_lock(hdev);
|
hci_req_lock(hdev);
|
||||||
|
|
||||||
if (!test_bit(HCI_UP, &hdev->flags))
|
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||||
|
ret = -ENETDOWN;
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* Drop queues */
|
/* Drop queues */
|
||||||
skb_queue_purge(&hdev->rx_q);
|
skb_queue_purge(&hdev->rx_q);
|
||||||
@ -1380,10 +1470,15 @@ int hci_dev_reset_stat(__u16 dev)
|
|||||||
if (!hdev)
|
if (!hdev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
|
||||||
|
ret = -EBUSY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
|
memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
|
||||||
|
|
||||||
|
done:
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1400,6 +1495,16 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
|
|||||||
if (!hdev)
|
if (!hdev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
|
||||||
|
err = -EBUSY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case HCISETAUTH:
|
case HCISETAUTH:
|
||||||
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
|
err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt,
|
||||||
@ -1458,6 +1563,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -1530,7 +1636,7 @@ int hci_get_dev_info(void __user *arg)
|
|||||||
|
|
||||||
strcpy(di.name, hdev->name);
|
strcpy(di.name, hdev->name);
|
||||||
di.bdaddr = hdev->bdaddr;
|
di.bdaddr = hdev->bdaddr;
|
||||||
di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4);
|
di.type = (hdev->bus & 0x0f) | ((hdev->dev_type & 0x03) << 4);
|
||||||
di.flags = hdev->flags;
|
di.flags = hdev->flags;
|
||||||
di.pkt_type = hdev->pkt_type;
|
di.pkt_type = hdev->pkt_type;
|
||||||
if (lmp_bredr_capable(hdev)) {
|
if (lmp_bredr_capable(hdev)) {
|
||||||
@ -1566,10 +1672,16 @@ static int hci_rfkill_set_block(void *data, bool blocked)
|
|||||||
|
|
||||||
BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
|
BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
|
||||||
|
|
||||||
if (!blocked)
|
if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
|
||||||
return 0;
|
return -EBUSY;
|
||||||
|
|
||||||
hci_dev_do_close(hdev);
|
if (blocked) {
|
||||||
|
set_bit(HCI_RFKILLED, &hdev->dev_flags);
|
||||||
|
if (!test_bit(HCI_SETUP, &hdev->dev_flags))
|
||||||
|
hci_dev_do_close(hdev);
|
||||||
|
} else {
|
||||||
|
clear_bit(HCI_RFKILLED, &hdev->dev_flags);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1585,15 +1697,19 @@ static void hci_power_on(struct work_struct *work)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
err = hci_dev_open(hdev->id);
|
err = hci_dev_do_open(hdev);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
mgmt_set_powered_failed(hdev, err);
|
mgmt_set_powered_failed(hdev, err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
|
if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) {
|
||||||
|
clear_bit(HCI_AUTO_OFF, &hdev->dev_flags);
|
||||||
|
hci_dev_do_close(hdev);
|
||||||
|
} else if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
|
||||||
queue_delayed_work(hdev->req_workqueue, &hdev->power_off,
|
queue_delayed_work(hdev->req_workqueue, &hdev->power_off,
|
||||||
HCI_AUTO_OFF_TIMEOUT);
|
HCI_AUTO_OFF_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags))
|
if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags))
|
||||||
mgmt_index_added(hdev);
|
mgmt_index_added(hdev);
|
||||||
@ -2209,10 +2325,18 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hdev->rfkill && rfkill_blocked(hdev->rfkill))
|
||||||
|
set_bit(HCI_RFKILLED, &hdev->dev_flags);
|
||||||
|
|
||||||
set_bit(HCI_SETUP, &hdev->dev_flags);
|
set_bit(HCI_SETUP, &hdev->dev_flags);
|
||||||
|
|
||||||
if (hdev->dev_type != HCI_AMP)
|
if (hdev->dev_type != HCI_AMP) {
|
||||||
set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
|
set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
|
||||||
|
/* Assume BR/EDR support until proven otherwise (such as
|
||||||
|
* through reading supported features during init.
|
||||||
|
*/
|
||||||
|
set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
|
||||||
|
}
|
||||||
|
|
||||||
write_lock(&hci_dev_list_lock);
|
write_lock(&hci_dev_list_lock);
|
||||||
list_add(&hdev->list, &hci_dev_list);
|
list_add(&hdev->list, &hci_dev_list);
|
||||||
@ -3258,15 +3382,13 @@ static void hci_tx_work(struct work_struct *work)
|
|||||||
BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
|
BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
|
||||||
hdev->sco_cnt, hdev->le_cnt);
|
hdev->sco_cnt, hdev->le_cnt);
|
||||||
|
|
||||||
/* Schedule queues and send stuff to HCI driver */
|
if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
|
||||||
|
/* Schedule queues and send stuff to HCI driver */
|
||||||
hci_sched_acl(hdev);
|
hci_sched_acl(hdev);
|
||||||
|
hci_sched_sco(hdev);
|
||||||
hci_sched_sco(hdev);
|
hci_sched_esco(hdev);
|
||||||
|
hci_sched_le(hdev);
|
||||||
hci_sched_esco(hdev);
|
}
|
||||||
|
|
||||||
hci_sched_le(hdev);
|
|
||||||
|
|
||||||
/* Send next queued raw (unknown type) packet */
|
/* Send next queued raw (unknown type) packet */
|
||||||
while ((skb = skb_dequeue(&hdev->raw_q)))
|
while ((skb = skb_dequeue(&hdev->raw_q)))
|
||||||
@ -3457,7 +3579,8 @@ static void hci_rx_work(struct work_struct *work)
|
|||||||
hci_send_to_sock(hdev, skb);
|
hci_send_to_sock(hdev, skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_bit(HCI_RAW, &hdev->flags)) {
|
if (test_bit(HCI_RAW, &hdev->flags) ||
|
||||||
|
test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -3512,7 +3635,7 @@ static void hci_cmd_work(struct work_struct *work)
|
|||||||
|
|
||||||
kfree_skb(hdev->sent_cmd);
|
kfree_skb(hdev->sent_cmd);
|
||||||
|
|
||||||
hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC);
|
hdev->sent_cmd = skb_clone(skb, GFP_KERNEL);
|
||||||
if (hdev->sent_cmd) {
|
if (hdev->sent_cmd) {
|
||||||
atomic_dec(&hdev->cmd_cnt);
|
atomic_dec(&hdev->cmd_cnt);
|
||||||
hci_send_frame(skb);
|
hci_send_frame(skb);
|
||||||
|
@ -297,6 +297,11 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We need to ensure that we set this back on if someone changed
|
||||||
|
* the scan mode through a raw HCI socket.
|
||||||
|
*/
|
||||||
|
set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
|
||||||
|
|
||||||
old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
|
old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
|
||||||
old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
|
old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
|
||||||
|
|
||||||
@ -994,20 +999,20 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!status) {
|
if (!status) {
|
||||||
if (sent->le)
|
if (sent->le) {
|
||||||
hdev->features[1][0] |= LMP_HOST_LE;
|
hdev->features[1][0] |= LMP_HOST_LE;
|
||||||
else
|
set_bit(HCI_LE_ENABLED, &hdev->dev_flags);
|
||||||
|
} else {
|
||||||
hdev->features[1][0] &= ~LMP_HOST_LE;
|
hdev->features[1][0] &= ~LMP_HOST_LE;
|
||||||
|
clear_bit(HCI_LE_ENABLED, &hdev->dev_flags);
|
||||||
|
clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
|
||||||
|
}
|
||||||
|
|
||||||
if (sent->simul)
|
if (sent->simul)
|
||||||
hdev->features[1][0] |= LMP_HOST_LE_BREDR;
|
hdev->features[1][0] |= LMP_HOST_LE_BREDR;
|
||||||
else
|
else
|
||||||
hdev->features[1][0] &= ~LMP_HOST_LE_BREDR;
|
hdev->features[1][0] &= ~LMP_HOST_LE_BREDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
|
|
||||||
!test_bit(HCI_INIT, &hdev->flags))
|
|
||||||
mgmt_le_enable_complete(hdev, sent->le, status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
|
static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
|
||||||
@ -3557,7 +3562,11 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
cp.handle = cpu_to_le16(conn->handle);
|
cp.handle = cpu_to_le16(conn->handle);
|
||||||
|
|
||||||
if (ltk->authenticated)
|
if (ltk->authenticated)
|
||||||
conn->sec_level = BT_SECURITY_HIGH;
|
conn->pending_sec_level = BT_SECURITY_HIGH;
|
||||||
|
else
|
||||||
|
conn->pending_sec_level = BT_SECURITY_MEDIUM;
|
||||||
|
|
||||||
|
conn->enc_key_size = ltk->enc_size;
|
||||||
|
|
||||||
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
|
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
|
||||||
|
|
||||||
|
@ -66,6 +66,46 @@ static struct bt_sock_list hci_sk_list = {
|
|||||||
.lock = __RW_LOCK_UNLOCKED(hci_sk_list.lock)
|
.lock = __RW_LOCK_UNLOCKED(hci_sk_list.lock)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool is_filtered_packet(struct sock *sk, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct hci_filter *flt;
|
||||||
|
int flt_type, flt_event;
|
||||||
|
|
||||||
|
/* Apply filter */
|
||||||
|
flt = &hci_pi(sk)->filter;
|
||||||
|
|
||||||
|
if (bt_cb(skb)->pkt_type == HCI_VENDOR_PKT)
|
||||||
|
flt_type = 0;
|
||||||
|
else
|
||||||
|
flt_type = bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS;
|
||||||
|
|
||||||
|
if (!test_bit(flt_type, &flt->type_mask))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* Extra filter for event packets only */
|
||||||
|
if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
flt_event = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
|
||||||
|
|
||||||
|
if (!hci_test_bit(flt_event, &flt->event_mask))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* Check filter only when opcode is set */
|
||||||
|
if (!flt->opcode)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (flt_event == HCI_EV_CMD_COMPLETE &&
|
||||||
|
flt->opcode != get_unaligned((__le16 *)(skb->data + 3)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (flt_event == HCI_EV_CMD_STATUS &&
|
||||||
|
flt->opcode != get_unaligned((__le16 *)(skb->data + 4)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Send frame to RAW socket */
|
/* Send frame to RAW socket */
|
||||||
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
@ -77,7 +117,6 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
read_lock(&hci_sk_list.lock);
|
read_lock(&hci_sk_list.lock);
|
||||||
|
|
||||||
sk_for_each(sk, &hci_sk_list.head) {
|
sk_for_each(sk, &hci_sk_list.head) {
|
||||||
struct hci_filter *flt;
|
|
||||||
struct sk_buff *nskb;
|
struct sk_buff *nskb;
|
||||||
|
|
||||||
if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev)
|
if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev)
|
||||||
@ -87,31 +126,19 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
if (skb->sk == sk)
|
if (skb->sk == sk)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW)
|
if (hci_pi(sk)->channel == HCI_CHANNEL_RAW) {
|
||||||
continue;
|
if (is_filtered_packet(sk, skb))
|
||||||
|
|
||||||
/* Apply filter */
|
|
||||||
flt = &hci_pi(sk)->filter;
|
|
||||||
|
|
||||||
if (!test_bit((bt_cb(skb)->pkt_type == HCI_VENDOR_PKT) ?
|
|
||||||
0 : (bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS),
|
|
||||||
&flt->type_mask))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (bt_cb(skb)->pkt_type == HCI_EVENT_PKT) {
|
|
||||||
int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
|
|
||||||
|
|
||||||
if (!hci_test_bit(evt, &flt->event_mask))
|
|
||||||
continue;
|
continue;
|
||||||
|
} else if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
|
||||||
if (flt->opcode &&
|
if (!bt_cb(skb)->incoming)
|
||||||
((evt == HCI_EV_CMD_COMPLETE &&
|
|
||||||
flt->opcode !=
|
|
||||||
get_unaligned((__le16 *)(skb->data + 3))) ||
|
|
||||||
(evt == HCI_EV_CMD_STATUS &&
|
|
||||||
flt->opcode !=
|
|
||||||
get_unaligned((__le16 *)(skb->data + 4)))))
|
|
||||||
continue;
|
continue;
|
||||||
|
if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
|
||||||
|
bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
|
||||||
|
bt_cb(skb)->pkt_type != HCI_SCODATA_PKT)
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
/* Don't send frame to other channel types */
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skb_copy) {
|
if (!skb_copy) {
|
||||||
@ -426,6 +453,12 @@ static int hci_sock_release(struct socket *sock)
|
|||||||
bt_sock_unlink(&hci_sk_list, sk);
|
bt_sock_unlink(&hci_sk_list, sk);
|
||||||
|
|
||||||
if (hdev) {
|
if (hdev) {
|
||||||
|
if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
|
||||||
|
mgmt_index_added(hdev);
|
||||||
|
clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
|
||||||
|
hci_dev_close(hdev->id);
|
||||||
|
}
|
||||||
|
|
||||||
atomic_dec(&hdev->promisc);
|
atomic_dec(&hdev->promisc);
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
}
|
}
|
||||||
@ -482,6 +515,9 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
|
|||||||
if (!hdev)
|
if (!hdev)
|
||||||
return -EBADFD;
|
return -EBADFD;
|
||||||
|
|
||||||
|
if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case HCISETRAW:
|
case HCISETRAW:
|
||||||
if (!capable(CAP_NET_ADMIN))
|
if (!capable(CAP_NET_ADMIN))
|
||||||
@ -512,23 +548,32 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
|
|||||||
if (!capable(CAP_NET_ADMIN))
|
if (!capable(CAP_NET_ADMIN))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
return hci_sock_blacklist_del(hdev, (void __user *) arg);
|
return hci_sock_blacklist_del(hdev, (void __user *) arg);
|
||||||
|
|
||||||
default:
|
|
||||||
if (hdev->ioctl)
|
|
||||||
return hdev->ioctl(hdev, cmd, arg);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hdev->ioctl)
|
||||||
|
return hdev->ioctl(hdev, cmd, arg);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
|
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
|
||||||
unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
|
||||||
void __user *argp = (void __user *) arg;
|
void __user *argp = (void __user *) arg;
|
||||||
|
struct sock *sk = sock->sk;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("cmd %x arg %lx", cmd, arg);
|
BT_DBG("cmd %x arg %lx", cmd, arg);
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
|
||||||
|
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
|
||||||
|
err = -EBADFD;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
release_sock(sk);
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case HCIGETDEVLIST:
|
case HCIGETDEVLIST:
|
||||||
return hci_get_dev_list(argp);
|
return hci_get_dev_list(argp);
|
||||||
@ -573,13 +618,15 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
|
|||||||
|
|
||||||
case HCIINQUIRY:
|
case HCIINQUIRY:
|
||||||
return hci_inquiry(argp);
|
return hci_inquiry(argp);
|
||||||
|
|
||||||
default:
|
|
||||||
lock_sock(sk);
|
|
||||||
err = hci_sock_bound_ioctl(sk, cmd, arg);
|
|
||||||
release_sock(sk);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
|
||||||
|
err = hci_sock_bound_ioctl(sk, cmd, arg);
|
||||||
|
|
||||||
|
done:
|
||||||
|
release_sock(sk);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
||||||
@ -629,6 +676,56 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
|||||||
hci_pi(sk)->hdev = hdev;
|
hci_pi(sk)->hdev = hdev;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HCI_CHANNEL_USER:
|
||||||
|
if (hci_pi(sk)->hdev) {
|
||||||
|
err = -EALREADY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haddr.hci_dev == HCI_DEV_NONE) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!capable(CAP_NET_ADMIN)) {
|
||||||
|
err = -EPERM;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdev = hci_dev_get(haddr.hci_dev);
|
||||||
|
if (!hdev) {
|
||||||
|
err = -ENODEV;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_bit(HCI_UP, &hdev->flags) ||
|
||||||
|
test_bit(HCI_INIT, &hdev->flags) ||
|
||||||
|
test_bit(HCI_SETUP, &hdev->dev_flags)) {
|
||||||
|
err = -EBUSY;
|
||||||
|
hci_dev_put(hdev);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_and_set_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
|
||||||
|
err = -EUSERS;
|
||||||
|
hci_dev_put(hdev);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgmt_index_removed(hdev);
|
||||||
|
|
||||||
|
err = hci_dev_open(hdev->id);
|
||||||
|
if (err) {
|
||||||
|
clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
|
||||||
|
hci_dev_put(hdev);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_inc(&hdev->promisc);
|
||||||
|
|
||||||
|
hci_pi(sk)->hdev = hdev;
|
||||||
|
break;
|
||||||
|
|
||||||
case HCI_CHANNEL_CONTROL:
|
case HCI_CHANNEL_CONTROL:
|
||||||
if (haddr.hci_dev != HCI_DEV_NONE) {
|
if (haddr.hci_dev != HCI_DEV_NONE) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
@ -677,22 +774,30 @@ static int hci_sock_getname(struct socket *sock, struct sockaddr *addr,
|
|||||||
{
|
{
|
||||||
struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
|
struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
struct hci_dev *hdev = hci_pi(sk)->hdev;
|
struct hci_dev *hdev;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
BT_DBG("sock %p sk %p", sock, sk);
|
BT_DBG("sock %p sk %p", sock, sk);
|
||||||
|
|
||||||
if (!hdev)
|
if (peer)
|
||||||
return -EBADFD;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
|
||||||
|
hdev = hci_pi(sk)->hdev;
|
||||||
|
if (!hdev) {
|
||||||
|
err = -EBADFD;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
*addr_len = sizeof(*haddr);
|
*addr_len = sizeof(*haddr);
|
||||||
haddr->hci_family = AF_BLUETOOTH;
|
haddr->hci_family = AF_BLUETOOTH;
|
||||||
haddr->hci_dev = hdev->id;
|
haddr->hci_dev = hdev->id;
|
||||||
haddr->hci_channel= 0;
|
haddr->hci_channel= hci_pi(sk)->channel;
|
||||||
|
|
||||||
|
done:
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg,
|
static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg,
|
||||||
@ -767,6 +872,7 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
|||||||
case HCI_CHANNEL_RAW:
|
case HCI_CHANNEL_RAW:
|
||||||
hci_sock_cmsg(sk, msg, skb);
|
hci_sock_cmsg(sk, msg, skb);
|
||||||
break;
|
break;
|
||||||
|
case HCI_CHANNEL_USER:
|
||||||
case HCI_CHANNEL_CONTROL:
|
case HCI_CHANNEL_CONTROL:
|
||||||
case HCI_CHANNEL_MONITOR:
|
case HCI_CHANNEL_MONITOR:
|
||||||
sock_recv_timestamp(msg, sk, skb);
|
sock_recv_timestamp(msg, sk, skb);
|
||||||
@ -801,6 +907,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|||||||
|
|
||||||
switch (hci_pi(sk)->channel) {
|
switch (hci_pi(sk)->channel) {
|
||||||
case HCI_CHANNEL_RAW:
|
case HCI_CHANNEL_RAW:
|
||||||
|
case HCI_CHANNEL_USER:
|
||||||
break;
|
break;
|
||||||
case HCI_CHANNEL_CONTROL:
|
case HCI_CHANNEL_CONTROL:
|
||||||
err = mgmt_control(sk, msg, len);
|
err = mgmt_control(sk, msg, len);
|
||||||
@ -837,7 +944,8 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|||||||
skb_pull(skb, 1);
|
skb_pull(skb, 1);
|
||||||
skb->dev = (void *) hdev;
|
skb->dev = (void *) hdev;
|
||||||
|
|
||||||
if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
|
if (hci_pi(sk)->channel == HCI_CHANNEL_RAW &&
|
||||||
|
bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
|
||||||
u16 opcode = get_unaligned_le16(skb->data);
|
u16 opcode = get_unaligned_le16(skb->data);
|
||||||
u16 ogf = hci_opcode_ogf(opcode);
|
u16 ogf = hci_opcode_ogf(opcode);
|
||||||
u16 ocf = hci_opcode_ocf(opcode);
|
u16 ocf = hci_opcode_ocf(opcode);
|
||||||
@ -868,6 +976,14 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hci_pi(sk)->channel == HCI_CHANNEL_USER &&
|
||||||
|
bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
|
||||||
|
bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
|
||||||
|
bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
skb_queue_tail(&hdev->raw_q, skb);
|
skb_queue_tail(&hdev->raw_q, skb);
|
||||||
queue_work(hdev->workqueue, &hdev->tx_work);
|
queue_work(hdev->workqueue, &hdev->tx_work);
|
||||||
}
|
}
|
||||||
@ -895,7 +1011,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
|
||||||
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
|
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
|
||||||
err = -EINVAL;
|
err = -EBADFD;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -981,7 +1097,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
|
||||||
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
|
if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
|
||||||
err = -EINVAL;
|
err = -EBADFD;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1016,13 +1016,12 @@ static bool __amp_capable(struct l2cap_chan *chan)
|
|||||||
{
|
{
|
||||||
struct l2cap_conn *conn = chan->conn;
|
struct l2cap_conn *conn = chan->conn;
|
||||||
|
|
||||||
if (enable_hs &&
|
if (conn->hs_enabled && hci_amp_capable() &&
|
||||||
hci_amp_capable() &&
|
|
||||||
chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED &&
|
chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED &&
|
||||||
conn->fixed_chan_mask & L2CAP_FC_A2MP)
|
conn->fixed_chan_mask & L2CAP_FC_A2MP)
|
||||||
return true;
|
return true;
|
||||||
else
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool l2cap_check_efs(struct l2cap_chan *chan)
|
static bool l2cap_check_efs(struct l2cap_chan *chan)
|
||||||
@ -1638,6 +1637,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
|
|||||||
|
|
||||||
conn->feat_mask = 0;
|
conn->feat_mask = 0;
|
||||||
|
|
||||||
|
if (hcon->type == ACL_LINK)
|
||||||
|
conn->hs_enabled = test_bit(HCI_HS_ENABLED,
|
||||||
|
&hcon->hdev->dev_flags);
|
||||||
|
|
||||||
spin_lock_init(&conn->lock);
|
spin_lock_init(&conn->lock);
|
||||||
mutex_init(&conn->chan_lock);
|
mutex_init(&conn->chan_lock);
|
||||||
|
|
||||||
@ -3084,14 +3087,14 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool __l2cap_ews_supported(struct l2cap_chan *chan)
|
static inline bool __l2cap_ews_supported(struct l2cap_conn *conn)
|
||||||
{
|
{
|
||||||
return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
|
return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
|
static inline bool __l2cap_efs_supported(struct l2cap_conn *conn)
|
||||||
{
|
{
|
||||||
return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
|
return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
|
static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
|
||||||
@ -3135,7 +3138,7 @@ static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
|
|||||||
static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
|
static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
|
||||||
{
|
{
|
||||||
if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
|
if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
|
||||||
__l2cap_ews_supported(chan)) {
|
__l2cap_ews_supported(chan->conn)) {
|
||||||
/* use extended control field */
|
/* use extended control field */
|
||||||
set_bit(FLAG_EXT_CTRL, &chan->flags);
|
set_bit(FLAG_EXT_CTRL, &chan->flags);
|
||||||
chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
|
chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
|
||||||
@ -3165,7 +3168,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
|
|||||||
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
|
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (__l2cap_efs_supported(chan))
|
if (__l2cap_efs_supported(chan->conn))
|
||||||
set_bit(FLAG_EFS_ENABLE, &chan->flags);
|
set_bit(FLAG_EFS_ENABLE, &chan->flags);
|
||||||
|
|
||||||
/* fall through */
|
/* fall through */
|
||||||
@ -3317,7 +3320,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_CONF_EWS:
|
case L2CAP_CONF_EWS:
|
||||||
if (!enable_hs)
|
if (!chan->conn->hs_enabled)
|
||||||
return -ECONNREFUSED;
|
return -ECONNREFUSED;
|
||||||
|
|
||||||
set_bit(FLAG_EXT_CTRL, &chan->flags);
|
set_bit(FLAG_EXT_CTRL, &chan->flags);
|
||||||
@ -3349,7 +3352,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (remote_efs) {
|
if (remote_efs) {
|
||||||
if (__l2cap_efs_supported(chan))
|
if (__l2cap_efs_supported(chan->conn))
|
||||||
set_bit(FLAG_EFS_ENABLE, &chan->flags);
|
set_bit(FLAG_EFS_ENABLE, &chan->flags);
|
||||||
else
|
else
|
||||||
return -ECONNREFUSED;
|
return -ECONNREFUSED;
|
||||||
@ -3755,6 +3758,13 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
|
|||||||
|
|
||||||
sk = chan->sk;
|
sk = chan->sk;
|
||||||
|
|
||||||
|
/* For certain devices (ex: HID mouse), support for authentication,
|
||||||
|
* pairing and bonding is optional. For such devices, inorder to avoid
|
||||||
|
* the ACL alive for too long after L2CAP disconnection, reset the ACL
|
||||||
|
* disc_timeout back to HCI_DISCONN_TIMEOUT during L2CAP connect.
|
||||||
|
*/
|
||||||
|
conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||||
|
|
||||||
bacpy(&bt_sk(sk)->src, conn->src);
|
bacpy(&bt_sk(sk)->src, conn->src);
|
||||||
bacpy(&bt_sk(sk)->dst, conn->dst);
|
bacpy(&bt_sk(sk)->dst, conn->dst);
|
||||||
chan->psm = psm;
|
chan->psm = psm;
|
||||||
@ -3884,13 +3894,13 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
|
|||||||
if (scid) {
|
if (scid) {
|
||||||
chan = __l2cap_get_chan_by_scid(conn, scid);
|
chan = __l2cap_get_chan_by_scid(conn, scid);
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
err = -EFAULT;
|
err = -EBADSLT;
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
|
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
err = -EFAULT;
|
err = -EBADSLT;
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3978,7 +3988,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
|
|||||||
|
|
||||||
chan = l2cap_get_chan_by_scid(conn, dcid);
|
chan = l2cap_get_chan_by_scid(conn, dcid);
|
||||||
if (!chan)
|
if (!chan)
|
||||||
return -ENOENT;
|
return -EBADSLT;
|
||||||
|
|
||||||
if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
|
if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
|
||||||
struct l2cap_cmd_rej_cid rej;
|
struct l2cap_cmd_rej_cid rej;
|
||||||
@ -4206,7 +4216,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
|
|||||||
chan = __l2cap_get_chan_by_scid(conn, dcid);
|
chan = __l2cap_get_chan_by_scid(conn, dcid);
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
mutex_unlock(&conn->chan_lock);
|
mutex_unlock(&conn->chan_lock);
|
||||||
return 0;
|
return -EBADSLT;
|
||||||
}
|
}
|
||||||
|
|
||||||
l2cap_chan_lock(chan);
|
l2cap_chan_lock(chan);
|
||||||
@ -4296,7 +4306,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
|
|||||||
if (!disable_ertm)
|
if (!disable_ertm)
|
||||||
feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
|
feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
|
||||||
| L2CAP_FEAT_FCS;
|
| L2CAP_FEAT_FCS;
|
||||||
if (enable_hs)
|
if (conn->hs_enabled)
|
||||||
feat_mask |= L2CAP_FEAT_EXT_FLOW
|
feat_mask |= L2CAP_FEAT_EXT_FLOW
|
||||||
| L2CAP_FEAT_EXT_WINDOW;
|
| L2CAP_FEAT_EXT_WINDOW;
|
||||||
|
|
||||||
@ -4307,7 +4317,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
|
|||||||
u8 buf[12];
|
u8 buf[12];
|
||||||
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
|
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
|
||||||
|
|
||||||
if (enable_hs)
|
if (conn->hs_enabled)
|
||||||
l2cap_fixed_chan[0] |= L2CAP_FC_A2MP;
|
l2cap_fixed_chan[0] |= L2CAP_FC_A2MP;
|
||||||
else
|
else
|
||||||
l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
|
l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
|
||||||
@ -4404,7 +4414,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
|
|||||||
if (cmd_len != sizeof(*req))
|
if (cmd_len != sizeof(*req))
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
|
|
||||||
if (!enable_hs)
|
if (!conn->hs_enabled)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
psm = le16_to_cpu(req->psm);
|
psm = le16_to_cpu(req->psm);
|
||||||
@ -4438,7 +4448,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
|
|||||||
hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst);
|
hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst);
|
||||||
if (!hs_hcon) {
|
if (!hs_hcon) {
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
return -EFAULT;
|
return -EBADSLT;
|
||||||
}
|
}
|
||||||
|
|
||||||
BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
|
BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
|
||||||
@ -4462,7 +4472,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
|
|||||||
l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
|
||||||
sizeof(rsp), &rsp);
|
sizeof(rsp), &rsp);
|
||||||
|
|
||||||
return -EFAULT;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
|
static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
|
||||||
@ -4831,7 +4841,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
|
|||||||
|
|
||||||
BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
|
BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
|
||||||
|
|
||||||
if (!enable_hs)
|
if (!conn->hs_enabled)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
chan = l2cap_get_chan_by_dcid(conn, icid);
|
chan = l2cap_get_chan_by_dcid(conn, icid);
|
||||||
@ -5212,7 +5222,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
|||||||
|
|
||||||
case L2CAP_CONN_RSP:
|
case L2CAP_CONN_RSP:
|
||||||
case L2CAP_CREATE_CHAN_RSP:
|
case L2CAP_CREATE_CHAN_RSP:
|
||||||
err = l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
|
l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_CONF_REQ:
|
case L2CAP_CONF_REQ:
|
||||||
@ -5220,7 +5230,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_CONF_RSP:
|
case L2CAP_CONF_RSP:
|
||||||
err = l2cap_config_rsp(conn, cmd, cmd_len, data);
|
l2cap_config_rsp(conn, cmd, cmd_len, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_DISCONN_REQ:
|
case L2CAP_DISCONN_REQ:
|
||||||
@ -5228,7 +5238,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_DISCONN_RSP:
|
case L2CAP_DISCONN_RSP:
|
||||||
err = l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
|
l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_ECHO_REQ:
|
case L2CAP_ECHO_REQ:
|
||||||
@ -5243,7 +5253,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_INFO_RSP:
|
case L2CAP_INFO_RSP:
|
||||||
err = l2cap_information_rsp(conn, cmd, cmd_len, data);
|
l2cap_information_rsp(conn, cmd, cmd_len, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_CREATE_CHAN_REQ:
|
case L2CAP_CREATE_CHAN_REQ:
|
||||||
@ -5255,7 +5265,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_MOVE_CHAN_RSP:
|
case L2CAP_MOVE_CHAN_RSP:
|
||||||
err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
|
l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_MOVE_CHAN_CFM:
|
case L2CAP_MOVE_CHAN_CFM:
|
||||||
@ -5263,7 +5273,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_MOVE_CHAN_CFM_RSP:
|
case L2CAP_MOVE_CHAN_CFM_RSP:
|
||||||
err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
|
l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -5294,54 +5304,65 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __le16 l2cap_err_to_reason(int err)
|
||||||
|
{
|
||||||
|
switch (err) {
|
||||||
|
case -EBADSLT:
|
||||||
|
return __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
|
||||||
|
case -EMSGSIZE:
|
||||||
|
return __constant_cpu_to_le16(L2CAP_REJ_MTU_EXCEEDED);
|
||||||
|
case -EINVAL:
|
||||||
|
case -EPROTO:
|
||||||
|
default:
|
||||||
|
return __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
|
static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
u8 *data = skb->data;
|
struct hci_conn *hcon = conn->hcon;
|
||||||
int len = skb->len;
|
struct l2cap_cmd_hdr *cmd;
|
||||||
struct l2cap_cmd_hdr cmd;
|
u16 len;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
l2cap_raw_recv(conn, skb);
|
if (hcon->type != LE_LINK)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
while (len >= L2CAP_CMD_HDR_SIZE) {
|
if (skb->len < L2CAP_CMD_HDR_SIZE)
|
||||||
u16 cmd_len;
|
goto drop;
|
||||||
memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
|
|
||||||
data += L2CAP_CMD_HDR_SIZE;
|
|
||||||
len -= L2CAP_CMD_HDR_SIZE;
|
|
||||||
|
|
||||||
cmd_len = le16_to_cpu(cmd.len);
|
cmd = (void *) skb->data;
|
||||||
|
skb_pull(skb, L2CAP_CMD_HDR_SIZE);
|
||||||
|
|
||||||
BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len,
|
len = le16_to_cpu(cmd->len);
|
||||||
cmd.ident);
|
|
||||||
|
|
||||||
if (cmd_len > len || !cmd.ident) {
|
BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd->code, len, cmd->ident);
|
||||||
BT_DBG("corrupted command");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = l2cap_le_sig_cmd(conn, &cmd, data);
|
if (len != skb->len || !cmd->ident) {
|
||||||
if (err) {
|
BT_DBG("corrupted command");
|
||||||
struct l2cap_cmd_rej_unk rej;
|
goto drop;
|
||||||
|
|
||||||
BT_ERR("Wrong link type (%d)", err);
|
|
||||||
|
|
||||||
/* FIXME: Map err to a valid reason */
|
|
||||||
rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
|
|
||||||
l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
|
|
||||||
sizeof(rej), &rej);
|
|
||||||
}
|
|
||||||
|
|
||||||
data += cmd_len;
|
|
||||||
len -= cmd_len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = l2cap_le_sig_cmd(conn, cmd, skb->data);
|
||||||
|
if (err) {
|
||||||
|
struct l2cap_cmd_rej_unk rej;
|
||||||
|
|
||||||
|
BT_ERR("Wrong link type (%d)", err);
|
||||||
|
|
||||||
|
rej.reason = l2cap_err_to_reason(err);
|
||||||
|
l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
|
||||||
|
sizeof(rej), &rej);
|
||||||
|
}
|
||||||
|
|
||||||
|
drop:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
struct hci_conn *hcon = conn->hcon;
|
||||||
u8 *data = skb->data;
|
u8 *data = skb->data;
|
||||||
int len = skb->len;
|
int len = skb->len;
|
||||||
struct l2cap_cmd_hdr cmd;
|
struct l2cap_cmd_hdr cmd;
|
||||||
@ -5349,6 +5370,9 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
|||||||
|
|
||||||
l2cap_raw_recv(conn, skb);
|
l2cap_raw_recv(conn, skb);
|
||||||
|
|
||||||
|
if (hcon->type != ACL_LINK)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
while (len >= L2CAP_CMD_HDR_SIZE) {
|
while (len >= L2CAP_CMD_HDR_SIZE) {
|
||||||
u16 cmd_len;
|
u16 cmd_len;
|
||||||
memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
|
memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
|
||||||
@ -5371,8 +5395,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
|||||||
|
|
||||||
BT_ERR("Wrong link type (%d)", err);
|
BT_ERR("Wrong link type (%d)", err);
|
||||||
|
|
||||||
/* FIXME: Map err to a valid reason */
|
rej.reason = l2cap_err_to_reason(err);
|
||||||
rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
|
|
||||||
l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
|
l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
|
||||||
sizeof(rej), &rej);
|
sizeof(rej), &rej);
|
||||||
}
|
}
|
||||||
@ -5381,6 +5404,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
|||||||
len -= cmd_len;
|
len -= cmd_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5777,7 +5801,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
|
|||||||
struct sk_buff *skb, u8 event)
|
struct sk_buff *skb, u8 event)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
bool skb_in_use = 0;
|
bool skb_in_use = false;
|
||||||
|
|
||||||
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
|
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
|
||||||
event);
|
event);
|
||||||
@ -5798,7 +5822,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
|
|||||||
control->txseq);
|
control->txseq);
|
||||||
|
|
||||||
chan->buffer_seq = chan->expected_tx_seq;
|
chan->buffer_seq = chan->expected_tx_seq;
|
||||||
skb_in_use = 1;
|
skb_in_use = true;
|
||||||
|
|
||||||
err = l2cap_reassemble_sdu(chan, skb, control);
|
err = l2cap_reassemble_sdu(chan, skb, control);
|
||||||
if (err)
|
if (err)
|
||||||
@ -5834,7 +5858,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
|
|||||||
* current frame is stored for later use.
|
* current frame is stored for later use.
|
||||||
*/
|
*/
|
||||||
skb_queue_tail(&chan->srej_q, skb);
|
skb_queue_tail(&chan->srej_q, skb);
|
||||||
skb_in_use = 1;
|
skb_in_use = true;
|
||||||
BT_DBG("Queued %p (queue len %d)", skb,
|
BT_DBG("Queued %p (queue len %d)", skb,
|
||||||
skb_queue_len(&chan->srej_q));
|
skb_queue_len(&chan->srej_q));
|
||||||
|
|
||||||
@ -5912,7 +5936,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
|
|||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
u16 txseq = control->txseq;
|
u16 txseq = control->txseq;
|
||||||
bool skb_in_use = 0;
|
bool skb_in_use = false;
|
||||||
|
|
||||||
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
|
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
|
||||||
event);
|
event);
|
||||||
@ -5924,7 +5948,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
|
|||||||
/* Keep frame for reassembly later */
|
/* Keep frame for reassembly later */
|
||||||
l2cap_pass_to_tx(chan, control);
|
l2cap_pass_to_tx(chan, control);
|
||||||
skb_queue_tail(&chan->srej_q, skb);
|
skb_queue_tail(&chan->srej_q, skb);
|
||||||
skb_in_use = 1;
|
skb_in_use = true;
|
||||||
BT_DBG("Queued %p (queue len %d)", skb,
|
BT_DBG("Queued %p (queue len %d)", skb,
|
||||||
skb_queue_len(&chan->srej_q));
|
skb_queue_len(&chan->srej_q));
|
||||||
|
|
||||||
@ -5935,7 +5959,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
|
|||||||
|
|
||||||
l2cap_pass_to_tx(chan, control);
|
l2cap_pass_to_tx(chan, control);
|
||||||
skb_queue_tail(&chan->srej_q, skb);
|
skb_queue_tail(&chan->srej_q, skb);
|
||||||
skb_in_use = 1;
|
skb_in_use = true;
|
||||||
BT_DBG("Queued %p (queue len %d)", skb,
|
BT_DBG("Queued %p (queue len %d)", skb,
|
||||||
skb_queue_len(&chan->srej_q));
|
skb_queue_len(&chan->srej_q));
|
||||||
|
|
||||||
@ -5950,7 +5974,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
|
|||||||
* the missing frames.
|
* the missing frames.
|
||||||
*/
|
*/
|
||||||
skb_queue_tail(&chan->srej_q, skb);
|
skb_queue_tail(&chan->srej_q, skb);
|
||||||
skb_in_use = 1;
|
skb_in_use = true;
|
||||||
BT_DBG("Queued %p (queue len %d)", skb,
|
BT_DBG("Queued %p (queue len %d)", skb,
|
||||||
skb_queue_len(&chan->srej_q));
|
skb_queue_len(&chan->srej_q));
|
||||||
|
|
||||||
@ -5964,7 +5988,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
|
|||||||
* SREJ'd frames.
|
* SREJ'd frames.
|
||||||
*/
|
*/
|
||||||
skb_queue_tail(&chan->srej_q, skb);
|
skb_queue_tail(&chan->srej_q, skb);
|
||||||
skb_in_use = 1;
|
skb_in_use = true;
|
||||||
BT_DBG("Queued %p (queue len %d)", skb,
|
BT_DBG("Queued %p (queue len %d)", skb,
|
||||||
skb_queue_len(&chan->srej_q));
|
skb_queue_len(&chan->srej_q));
|
||||||
|
|
||||||
@ -6373,8 +6397,12 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
|
|||||||
static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
|
static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
struct hci_conn *hcon = conn->hcon;
|
||||||
struct l2cap_chan *chan;
|
struct l2cap_chan *chan;
|
||||||
|
|
||||||
|
if (hcon->type != ACL_LINK)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
chan = l2cap_global_chan_by_psm(0, psm, conn->src, conn->dst);
|
chan = l2cap_global_chan_by_psm(0, psm, conn->src, conn->dst);
|
||||||
if (!chan)
|
if (!chan)
|
||||||
goto drop;
|
goto drop;
|
||||||
@ -6397,8 +6425,12 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
|
|||||||
static void l2cap_att_channel(struct l2cap_conn *conn,
|
static void l2cap_att_channel(struct l2cap_conn *conn,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
|
struct hci_conn *hcon = conn->hcon;
|
||||||
struct l2cap_chan *chan;
|
struct l2cap_chan *chan;
|
||||||
|
|
||||||
|
if (hcon->type != LE_LINK)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
|
chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
|
||||||
conn->src, conn->dst);
|
conn->src, conn->dst);
|
||||||
if (!chan)
|
if (!chan)
|
||||||
@ -6434,9 +6466,6 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
BT_DBG("len %d, cid 0x%4.4x", len, cid);
|
BT_DBG("len %d, cid 0x%4.4x", len, cid);
|
||||||
|
|
||||||
switch (cid) {
|
switch (cid) {
|
||||||
case L2CAP_CID_LE_SIGNALING:
|
|
||||||
l2cap_le_sig_channel(conn, skb);
|
|
||||||
break;
|
|
||||||
case L2CAP_CID_SIGNALING:
|
case L2CAP_CID_SIGNALING:
|
||||||
l2cap_sig_channel(conn, skb);
|
l2cap_sig_channel(conn, skb);
|
||||||
break;
|
break;
|
||||||
@ -6451,6 +6480,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
l2cap_att_channel(conn, skb);
|
l2cap_att_channel(conn, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case L2CAP_CID_LE_SIGNALING:
|
||||||
|
l2cap_le_sig_channel(conn, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
case L2CAP_CID_SMP:
|
case L2CAP_CID_SMP:
|
||||||
if (smp_sig_channel(conn, skb))
|
if (smp_sig_channel(conn, skb))
|
||||||
l2cap_conn_del(conn->hcon, EACCES);
|
l2cap_conn_del(conn->hcon, EACCES);
|
||||||
|
@ -445,11 +445,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BT_CHANNEL_POLICY:
|
case BT_CHANNEL_POLICY:
|
||||||
if (!enable_hs) {
|
|
||||||
err = -ENOPROTOOPT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (put_user(chan->chan_policy, (u32 __user *) optval))
|
if (put_user(chan->chan_policy, (u32 __user *) optval))
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
break;
|
break;
|
||||||
@ -720,11 +715,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BT_CHANNEL_POLICY:
|
case BT_CHANNEL_POLICY:
|
||||||
if (!enable_hs) {
|
|
||||||
err = -ENOPROTOOPT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (get_user(opt, (u32 __user *) optval)) {
|
if (get_user(opt, (u32 __user *) optval)) {
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
break;
|
break;
|
||||||
@ -777,6 +767,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|||||||
if (sk->sk_state != BT_CONNECTED)
|
if (sk->sk_state != BT_CONNECTED)
|
||||||
return -ENOTCONN;
|
return -ENOTCONN;
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
err = bt_sock_wait_ready(sk, msg->msg_flags);
|
||||||
|
release_sock(sk);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
l2cap_chan_lock(chan);
|
l2cap_chan_lock(chan);
|
||||||
err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
|
err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
|
||||||
l2cap_chan_unlock(chan);
|
l2cap_chan_unlock(chan);
|
||||||
@ -799,8 +795,8 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
|
|||||||
pi->chan->state = BT_CONFIG;
|
pi->chan->state = BT_CONFIG;
|
||||||
|
|
||||||
__l2cap_connect_rsp_defer(pi->chan);
|
__l2cap_connect_rsp_defer(pi->chan);
|
||||||
release_sock(sk);
|
err = 0;
|
||||||
return 0;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
|
@ -32,10 +32,8 @@
|
|||||||
#include <net/bluetooth/mgmt.h>
|
#include <net/bluetooth/mgmt.h>
|
||||||
#include <net/bluetooth/smp.h>
|
#include <net/bluetooth/smp.h>
|
||||||
|
|
||||||
bool enable_hs;
|
|
||||||
|
|
||||||
#define MGMT_VERSION 1
|
#define MGMT_VERSION 1
|
||||||
#define MGMT_REVISION 3
|
#define MGMT_REVISION 4
|
||||||
|
|
||||||
static const u16 mgmt_commands[] = {
|
static const u16 mgmt_commands[] = {
|
||||||
MGMT_OP_READ_INDEX_LIST,
|
MGMT_OP_READ_INDEX_LIST,
|
||||||
@ -76,6 +74,9 @@ static const u16 mgmt_commands[] = {
|
|||||||
MGMT_OP_BLOCK_DEVICE,
|
MGMT_OP_BLOCK_DEVICE,
|
||||||
MGMT_OP_UNBLOCK_DEVICE,
|
MGMT_OP_UNBLOCK_DEVICE,
|
||||||
MGMT_OP_SET_DEVICE_ID,
|
MGMT_OP_SET_DEVICE_ID,
|
||||||
|
MGMT_OP_SET_ADVERTISING,
|
||||||
|
MGMT_OP_SET_BREDR,
|
||||||
|
MGMT_OP_SET_STATIC_ADDRESS,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u16 mgmt_events[] = {
|
static const u16 mgmt_events[] = {
|
||||||
@ -339,6 +340,9 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
if (test_bit(HCI_SETUP, &d->dev_flags))
|
if (test_bit(HCI_SETUP, &d->dev_flags))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (test_bit(HCI_USER_CHANNEL, &d->dev_flags))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!mgmt_valid_hdev(d))
|
if (!mgmt_valid_hdev(d))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -376,13 +380,13 @@ static u32 get_supported_settings(struct hci_dev *hdev)
|
|||||||
settings |= MGMT_SETTING_DISCOVERABLE;
|
settings |= MGMT_SETTING_DISCOVERABLE;
|
||||||
settings |= MGMT_SETTING_BREDR;
|
settings |= MGMT_SETTING_BREDR;
|
||||||
settings |= MGMT_SETTING_LINK_SECURITY;
|
settings |= MGMT_SETTING_LINK_SECURITY;
|
||||||
|
settings |= MGMT_SETTING_HS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enable_hs)
|
if (lmp_le_capable(hdev)) {
|
||||||
settings |= MGMT_SETTING_HS;
|
|
||||||
|
|
||||||
if (lmp_le_capable(hdev))
|
|
||||||
settings |= MGMT_SETTING_LE;
|
settings |= MGMT_SETTING_LE;
|
||||||
|
settings |= MGMT_SETTING_ADVERTISING;
|
||||||
|
}
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
@ -406,7 +410,7 @@ static u32 get_current_settings(struct hci_dev *hdev)
|
|||||||
if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
|
if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
|
||||||
settings |= MGMT_SETTING_PAIRABLE;
|
settings |= MGMT_SETTING_PAIRABLE;
|
||||||
|
|
||||||
if (lmp_bredr_capable(hdev))
|
if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
|
||||||
settings |= MGMT_SETTING_BREDR;
|
settings |= MGMT_SETTING_BREDR;
|
||||||
|
|
||||||
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
|
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
|
||||||
@ -421,6 +425,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
|
|||||||
if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags))
|
if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags))
|
||||||
settings |= MGMT_SETTING_HS;
|
settings |= MGMT_SETTING_HS;
|
||||||
|
|
||||||
|
if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
|
||||||
|
settings |= MGMT_SETTING_ADVERTISING;
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,6 +811,12 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
|
||||||
|
err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
|
||||||
|
MGMT_STATUS_BUSY);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
|
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
|
||||||
cancel_delayed_work(&hdev->power_off);
|
cancel_delayed_work(&hdev->power_off);
|
||||||
|
|
||||||
@ -820,12 +833,6 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
|
|
||||||
err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
|
|
||||||
MGMT_STATUS_BUSY);
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
|
cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
@ -883,20 +890,71 @@ static int new_settings(struct hci_dev *hdev, struct sock *skip)
|
|||||||
return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip);
|
return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct cmd_lookup {
|
||||||
|
struct sock *sk;
|
||||||
|
struct hci_dev *hdev;
|
||||||
|
u8 mgmt_status;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void settings_rsp(struct pending_cmd *cmd, void *data)
|
||||||
|
{
|
||||||
|
struct cmd_lookup *match = data;
|
||||||
|
|
||||||
|
send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
|
||||||
|
|
||||||
|
list_del(&cmd->list);
|
||||||
|
|
||||||
|
if (match->sk == NULL) {
|
||||||
|
match->sk = cmd->sk;
|
||||||
|
sock_hold(match->sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
mgmt_pending_free(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
|
||||||
|
{
|
||||||
|
u8 *status = data;
|
||||||
|
|
||||||
|
cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 mgmt_bredr_support(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
if (!lmp_bredr_capable(hdev))
|
||||||
|
return MGMT_STATUS_NOT_SUPPORTED;
|
||||||
|
else if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
|
||||||
|
return MGMT_STATUS_REJECTED;
|
||||||
|
else
|
||||||
|
return MGMT_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 mgmt_le_support(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
if (!lmp_le_capable(hdev))
|
||||||
|
return MGMT_STATUS_NOT_SUPPORTED;
|
||||||
|
else if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
|
||||||
|
return MGMT_STATUS_REJECTED;
|
||||||
|
else
|
||||||
|
return MGMT_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
|
static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||||
u16 len)
|
u16 len)
|
||||||
{
|
{
|
||||||
struct mgmt_cp_set_discoverable *cp = data;
|
struct mgmt_cp_set_discoverable *cp = data;
|
||||||
struct pending_cmd *cmd;
|
struct pending_cmd *cmd;
|
||||||
u16 timeout;
|
u16 timeout;
|
||||||
u8 scan;
|
u8 scan, status;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("request for %s", hdev->name);
|
BT_DBG("request for %s", hdev->name);
|
||||||
|
|
||||||
if (!lmp_bredr_capable(hdev))
|
status = mgmt_bredr_support(hdev);
|
||||||
|
if (status)
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
status);
|
||||||
|
|
||||||
if (cp->val != 0x00 && cp->val != 0x01)
|
if (cp->val != 0x00 && cp->val != 0x01)
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
|
||||||
@ -1045,14 +1103,15 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
struct mgmt_mode *cp = data;
|
struct mgmt_mode *cp = data;
|
||||||
struct pending_cmd *cmd;
|
struct pending_cmd *cmd;
|
||||||
struct hci_request req;
|
struct hci_request req;
|
||||||
u8 scan;
|
u8 scan, status;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("request for %s", hdev->name);
|
BT_DBG("request for %s", hdev->name);
|
||||||
|
|
||||||
if (!lmp_bredr_capable(hdev))
|
status = mgmt_bredr_support(hdev);
|
||||||
|
if (status)
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
status);
|
||||||
|
|
||||||
if (cp->val != 0x00 && cp->val != 0x01)
|
if (cp->val != 0x00 && cp->val != 0x01)
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
|
||||||
@ -1168,14 +1227,15 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
{
|
{
|
||||||
struct mgmt_mode *cp = data;
|
struct mgmt_mode *cp = data;
|
||||||
struct pending_cmd *cmd;
|
struct pending_cmd *cmd;
|
||||||
u8 val;
|
u8 val, status;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("request for %s", hdev->name);
|
BT_DBG("request for %s", hdev->name);
|
||||||
|
|
||||||
if (!lmp_bredr_capable(hdev))
|
status = mgmt_bredr_support(hdev);
|
||||||
|
if (status)
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
status);
|
||||||
|
|
||||||
if (cp->val != 0x00 && cp->val != 0x01)
|
if (cp->val != 0x00 && cp->val != 0x01)
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
|
||||||
@ -1236,11 +1296,15 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||||||
{
|
{
|
||||||
struct mgmt_mode *cp = data;
|
struct mgmt_mode *cp = data;
|
||||||
struct pending_cmd *cmd;
|
struct pending_cmd *cmd;
|
||||||
u8 val;
|
u8 val, status;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("request for %s", hdev->name);
|
BT_DBG("request for %s", hdev->name);
|
||||||
|
|
||||||
|
status = mgmt_bredr_support(hdev);
|
||||||
|
if (status)
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, status);
|
||||||
|
|
||||||
if (!lmp_ssp_capable(hdev))
|
if (!lmp_ssp_capable(hdev))
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
@ -1302,23 +1366,64 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||||||
static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||||
{
|
{
|
||||||
struct mgmt_mode *cp = data;
|
struct mgmt_mode *cp = data;
|
||||||
|
bool changed;
|
||||||
|
u8 status;
|
||||||
|
int err;
|
||||||
|
|
||||||
BT_DBG("request for %s", hdev->name);
|
BT_DBG("request for %s", hdev->name);
|
||||||
|
|
||||||
if (!enable_hs)
|
status = mgmt_bredr_support(hdev);
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
if (status)
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, status);
|
||||||
|
|
||||||
if (cp->val != 0x00 && cp->val != 0x01)
|
if (cp->val != 0x00 && cp->val != 0x01)
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||||
MGMT_STATUS_INVALID_PARAMS);
|
MGMT_STATUS_INVALID_PARAMS);
|
||||||
|
|
||||||
if (cp->val)
|
hci_dev_lock(hdev);
|
||||||
set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
|
|
||||||
else
|
|
||||||
clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
|
|
||||||
|
|
||||||
return send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
|
if (cp->val) {
|
||||||
|
changed = !test_and_set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
|
||||||
|
} else {
|
||||||
|
if (hdev_is_powered(hdev)) {
|
||||||
|
err = cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
|
||||||
|
MGMT_STATUS_REJECTED);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
changed = test_and_clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
|
||||||
|
if (err < 0)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
err = new_settings(hdev, sk);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void le_enable_complete(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
struct cmd_lookup match = { NULL, hdev };
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
u8 mgmt_err = mgmt_status(status);
|
||||||
|
|
||||||
|
mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp,
|
||||||
|
&mgmt_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match);
|
||||||
|
|
||||||
|
new_settings(hdev, match.sk);
|
||||||
|
|
||||||
|
if (match.sk)
|
||||||
|
sock_put(match.sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||||
@ -1326,6 +1431,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||||||
struct mgmt_mode *cp = data;
|
struct mgmt_mode *cp = data;
|
||||||
struct hci_cp_write_le_host_supported hci_cp;
|
struct hci_cp_write_le_host_supported hci_cp;
|
||||||
struct pending_cmd *cmd;
|
struct pending_cmd *cmd;
|
||||||
|
struct hci_request req;
|
||||||
int err;
|
int err;
|
||||||
u8 val, enabled;
|
u8 val, enabled;
|
||||||
|
|
||||||
@ -1340,7 +1446,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||||||
MGMT_STATUS_INVALID_PARAMS);
|
MGMT_STATUS_INVALID_PARAMS);
|
||||||
|
|
||||||
/* LE-only devices do not allow toggling LE on/off */
|
/* LE-only devices do not allow toggling LE on/off */
|
||||||
if (!lmp_bredr_capable(hdev))
|
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
|
||||||
MGMT_STATUS_REJECTED);
|
MGMT_STATUS_REJECTED);
|
||||||
|
|
||||||
@ -1357,6 +1463,11 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!val && test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
|
||||||
|
clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev);
|
err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
@ -1367,7 +1478,8 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
|
if (mgmt_pending_find(MGMT_OP_SET_LE, hdev) ||
|
||||||
|
mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev)) {
|
||||||
err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
|
err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
|
||||||
MGMT_STATUS_BUSY);
|
MGMT_STATUS_BUSY);
|
||||||
goto unlock;
|
goto unlock;
|
||||||
@ -1386,8 +1498,15 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||||||
hci_cp.simul = lmp_le_br_capable(hdev);
|
hci_cp.simul = lmp_le_br_capable(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
|
hci_req_init(&req, hdev);
|
||||||
&hci_cp);
|
|
||||||
|
if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags) && !val)
|
||||||
|
hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(val), &val);
|
||||||
|
|
||||||
|
hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
|
||||||
|
&hci_cp);
|
||||||
|
|
||||||
|
err = hci_req_run(&req, le_enable_complete);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
mgmt_pending_remove(cmd);
|
mgmt_pending_remove(cmd);
|
||||||
|
|
||||||
@ -1706,6 +1825,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
u16 key_count, expected_len;
|
u16 key_count, expected_len;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
BT_DBG("request for %s", hdev->name);
|
||||||
|
|
||||||
|
if (!lmp_bredr_capable(hdev))
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
|
||||||
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
|
|
||||||
key_count = __le16_to_cpu(cp->key_count);
|
key_count = __le16_to_cpu(cp->key_count);
|
||||||
|
|
||||||
expected_len = sizeof(*cp) + key_count *
|
expected_len = sizeof(*cp) + key_count *
|
||||||
@ -2685,6 +2810,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
|
|||||||
struct hci_request req;
|
struct hci_request req;
|
||||||
/* General inquiry access code (GIAC) */
|
/* General inquiry access code (GIAC) */
|
||||||
u8 lap[3] = { 0x33, 0x8b, 0x9e };
|
u8 lap[3] = { 0x33, 0x8b, 0x9e };
|
||||||
|
u8 status;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
@ -2721,9 +2847,10 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
|
|||||||
|
|
||||||
switch (hdev->discovery.type) {
|
switch (hdev->discovery.type) {
|
||||||
case DISCOV_TYPE_BREDR:
|
case DISCOV_TYPE_BREDR:
|
||||||
if (!lmp_bredr_capable(hdev)) {
|
status = mgmt_bredr_support(hdev);
|
||||||
|
if (status) {
|
||||||
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
status);
|
||||||
mgmt_pending_remove(cmd);
|
mgmt_pending_remove(cmd);
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
@ -2745,15 +2872,16 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
|
|||||||
|
|
||||||
case DISCOV_TYPE_LE:
|
case DISCOV_TYPE_LE:
|
||||||
case DISCOV_TYPE_INTERLEAVED:
|
case DISCOV_TYPE_INTERLEAVED:
|
||||||
if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
|
status = mgmt_le_support(hdev);
|
||||||
|
if (status) {
|
||||||
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
status);
|
||||||
mgmt_pending_remove(cmd);
|
mgmt_pending_remove(cmd);
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
|
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
|
||||||
!lmp_bredr_capable(hdev)) {
|
!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
|
||||||
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
mgmt_pending_remove(cmd);
|
mgmt_pending_remove(cmd);
|
||||||
@ -3065,6 +3193,135 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_advertising_complete(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
struct cmd_lookup match = { NULL, hdev };
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
u8 mgmt_err = mgmt_status(status);
|
||||||
|
|
||||||
|
mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev,
|
||||||
|
cmd_status_rsp, &mgmt_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp,
|
||||||
|
&match);
|
||||||
|
|
||||||
|
new_settings(hdev, match.sk);
|
||||||
|
|
||||||
|
if (match.sk)
|
||||||
|
sock_put(match.sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||||
|
{
|
||||||
|
struct mgmt_mode *cp = data;
|
||||||
|
struct pending_cmd *cmd;
|
||||||
|
struct hci_request req;
|
||||||
|
u8 val, enabled, status;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("request for %s", hdev->name);
|
||||||
|
|
||||||
|
status = mgmt_le_support(hdev);
|
||||||
|
if (status)
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
|
||||||
|
status);
|
||||||
|
|
||||||
|
if (cp->val != 0x00 && cp->val != 0x01)
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
|
||||||
|
MGMT_STATUS_INVALID_PARAMS);
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
val = !!cp->val;
|
||||||
|
enabled = test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
|
||||||
|
|
||||||
|
if (!hdev_is_powered(hdev) || val == enabled) {
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
if (val != test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
|
||||||
|
change_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = send_settings_rsp(sk, MGMT_OP_SET_ADVERTISING, hdev);
|
||||||
|
if (err < 0)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
err = new_settings(hdev, sk);
|
||||||
|
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev) ||
|
||||||
|
mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
|
||||||
|
err = cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
|
||||||
|
MGMT_STATUS_BUSY);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = mgmt_pending_add(sk, MGMT_OP_SET_ADVERTISING, hdev, data, len);
|
||||||
|
if (!cmd) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_req_init(&req, hdev);
|
||||||
|
|
||||||
|
hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(val), &val);
|
||||||
|
|
||||||
|
err = hci_req_run(&req, set_advertising_complete);
|
||||||
|
if (err < 0)
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_static_address(struct sock *sk, struct hci_dev *hdev,
|
||||||
|
void *data, u16 len)
|
||||||
|
{
|
||||||
|
struct mgmt_cp_set_static_address *cp = data;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
if (!lmp_le_capable(hdev))
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
|
||||||
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
|
|
||||||
|
if (hdev_is_powered(hdev))
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS,
|
||||||
|
MGMT_STATUS_REJECTED);
|
||||||
|
|
||||||
|
if (bacmp(&cp->bdaddr, BDADDR_ANY)) {
|
||||||
|
if (!bacmp(&cp->bdaddr, BDADDR_NONE))
|
||||||
|
return cmd_status(sk, hdev->id,
|
||||||
|
MGMT_OP_SET_STATIC_ADDRESS,
|
||||||
|
MGMT_STATUS_INVALID_PARAMS);
|
||||||
|
|
||||||
|
/* Two most significant bits shall be set */
|
||||||
|
if ((cp->bdaddr.b[5] & 0xc0) != 0xc0)
|
||||||
|
return cmd_status(sk, hdev->id,
|
||||||
|
MGMT_OP_SET_STATIC_ADDRESS,
|
||||||
|
MGMT_STATUS_INVALID_PARAMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
bacpy(&hdev->static_addr, &cp->bdaddr);
|
||||||
|
|
||||||
|
err = cmd_complete(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, 0, NULL, 0);
|
||||||
|
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static void fast_connectable_complete(struct hci_dev *hdev, u8 status)
|
static void fast_connectable_complete(struct hci_dev *hdev, u8 status)
|
||||||
{
|
{
|
||||||
struct pending_cmd *cmd;
|
struct pending_cmd *cmd;
|
||||||
@ -3108,7 +3365,8 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!lmp_bredr_capable(hdev) || hdev->hci_ver < BLUETOOTH_VER_1_2)
|
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) ||
|
||||||
|
hdev->hci_ver < BLUETOOTH_VER_1_2)
|
||||||
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
|
|
||||||
@ -3162,6 +3420,121 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_bredr_complete(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
struct pending_cmd *cmd;
|
||||||
|
|
||||||
|
BT_DBG("status 0x%02x", status);
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
cmd = mgmt_pending_find(MGMT_OP_SET_BREDR, hdev);
|
||||||
|
if (!cmd)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
u8 mgmt_err = mgmt_status(status);
|
||||||
|
|
||||||
|
/* We need to restore the flag if related HCI commands
|
||||||
|
* failed.
|
||||||
|
*/
|
||||||
|
clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
|
||||||
|
|
||||||
|
cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err);
|
||||||
|
} else {
|
||||||
|
send_settings_rsp(cmd->sk, MGMT_OP_SET_BREDR, hdev);
|
||||||
|
new_settings(hdev, cmd->sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||||
|
{
|
||||||
|
struct mgmt_mode *cp = data;
|
||||||
|
struct pending_cmd *cmd;
|
||||||
|
struct hci_request req;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("request for %s", hdev->name);
|
||||||
|
|
||||||
|
if (!lmp_bredr_capable(hdev) || !lmp_le_capable(hdev))
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
|
||||||
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
|
|
||||||
|
if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
|
||||||
|
MGMT_STATUS_REJECTED);
|
||||||
|
|
||||||
|
if (cp->val != 0x00 && cp->val != 0x01)
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
|
||||||
|
MGMT_STATUS_INVALID_PARAMS);
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
if (cp->val == test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
|
||||||
|
err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hdev_is_powered(hdev)) {
|
||||||
|
if (!cp->val) {
|
||||||
|
clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
|
||||||
|
clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
|
||||||
|
clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
|
||||||
|
clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
|
||||||
|
clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags);
|
||||||
|
clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
change_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
|
||||||
|
|
||||||
|
err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev);
|
||||||
|
if (err < 0)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
err = new_settings(hdev, sk);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reject disabling when powered on */
|
||||||
|
if (!cp->val) {
|
||||||
|
err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
|
||||||
|
MGMT_STATUS_REJECTED);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mgmt_pending_find(MGMT_OP_SET_BREDR, hdev)) {
|
||||||
|
err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR,
|
||||||
|
MGMT_STATUS_BUSY);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = mgmt_pending_add(sk, MGMT_OP_SET_BREDR, hdev, data, len);
|
||||||
|
if (!cmd) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need to flip the bit already here so that hci_update_ad
|
||||||
|
* generates the correct flags.
|
||||||
|
*/
|
||||||
|
set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
|
||||||
|
|
||||||
|
hci_req_init(&req, hdev);
|
||||||
|
hci_update_ad(&req);
|
||||||
|
err = hci_req_run(&req, set_bredr_complete);
|
||||||
|
if (err < 0)
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ltk_is_valid(struct mgmt_ltk_info *key)
|
static bool ltk_is_valid(struct mgmt_ltk_info *key)
|
||||||
{
|
{
|
||||||
if (key->authenticated != 0x00 && key->authenticated != 0x01)
|
if (key->authenticated != 0x00 && key->authenticated != 0x01)
|
||||||
@ -3180,6 +3553,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
|
|||||||
u16 key_count, expected_len;
|
u16 key_count, expected_len;
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
|
BT_DBG("request for %s", hdev->name);
|
||||||
|
|
||||||
|
if (!lmp_le_capable(hdev))
|
||||||
|
return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
|
||||||
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
|
|
||||||
key_count = __le16_to_cpu(cp->key_count);
|
key_count = __le16_to_cpu(cp->key_count);
|
||||||
|
|
||||||
expected_len = sizeof(*cp) + key_count *
|
expected_len = sizeof(*cp) + key_count *
|
||||||
@ -3276,6 +3655,9 @@ static const struct mgmt_handler {
|
|||||||
{ block_device, false, MGMT_BLOCK_DEVICE_SIZE },
|
{ block_device, false, MGMT_BLOCK_DEVICE_SIZE },
|
||||||
{ unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE },
|
{ unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE },
|
||||||
{ set_device_id, false, MGMT_SET_DEVICE_ID_SIZE },
|
{ set_device_id, false, MGMT_SET_DEVICE_ID_SIZE },
|
||||||
|
{ set_advertising, false, MGMT_SETTING_SIZE },
|
||||||
|
{ set_bredr, false, MGMT_SETTING_SIZE },
|
||||||
|
{ set_static_address, false, MGMT_SET_STATIC_ADDRESS_SIZE },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -3320,6 +3702,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
|||||||
MGMT_STATUS_INVALID_INDEX);
|
MGMT_STATUS_INVALID_INDEX);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
|
||||||
|
err = cmd_status(sk, index, opcode,
|
||||||
|
MGMT_STATUS_INVALID_INDEX);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opcode >= ARRAY_SIZE(mgmt_handlers) ||
|
if (opcode >= ARRAY_SIZE(mgmt_handlers) ||
|
||||||
@ -3365,14 +3753,6 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
|
|
||||||
{
|
|
||||||
u8 *status = data;
|
|
||||||
|
|
||||||
cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
|
|
||||||
mgmt_pending_remove(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgmt_index_added(struct hci_dev *hdev)
|
int mgmt_index_added(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
if (!mgmt_valid_hdev(hdev))
|
if (!mgmt_valid_hdev(hdev))
|
||||||
@ -3393,28 +3773,6 @@ int mgmt_index_removed(struct hci_dev *hdev)
|
|||||||
return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
|
return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cmd_lookup {
|
|
||||||
struct sock *sk;
|
|
||||||
struct hci_dev *hdev;
|
|
||||||
u8 mgmt_status;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void settings_rsp(struct pending_cmd *cmd, void *data)
|
|
||||||
{
|
|
||||||
struct cmd_lookup *match = data;
|
|
||||||
|
|
||||||
send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
|
|
||||||
|
|
||||||
list_del(&cmd->list);
|
|
||||||
|
|
||||||
if (match->sk == NULL) {
|
|
||||||
match->sk = cmd->sk;
|
|
||||||
sock_hold(match->sk);
|
|
||||||
}
|
|
||||||
|
|
||||||
mgmt_pending_free(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_bredr_scan(struct hci_request *req)
|
static void set_bredr_scan(struct hci_request *req)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = req->hdev;
|
struct hci_dev *hdev = req->hdev;
|
||||||
@ -3481,6 +3839,22 @@ static int powered_update_hci(struct hci_dev *hdev)
|
|||||||
cp.simul != lmp_host_le_br_capable(hdev))
|
cp.simul != lmp_host_le_br_capable(hdev))
|
||||||
hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
|
hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
|
||||||
sizeof(cp), &cp);
|
sizeof(cp), &cp);
|
||||||
|
|
||||||
|
/* In case BR/EDR was toggled during the AUTO_OFF phase */
|
||||||
|
hci_update_ad(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lmp_le_capable(hdev)) {
|
||||||
|
/* Set random address to static address if configured */
|
||||||
|
if (bacmp(&hdev->static_addr, BDADDR_ANY))
|
||||||
|
hci_req_add(&req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
|
||||||
|
&hdev->static_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
|
||||||
|
u8 adv = 0x01;
|
||||||
|
|
||||||
|
hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(adv), &adv);
|
||||||
}
|
}
|
||||||
|
|
||||||
link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
|
link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
|
||||||
@ -3489,7 +3863,8 @@ static int powered_update_hci(struct hci_dev *hdev)
|
|||||||
sizeof(link_sec), &link_sec);
|
sizeof(link_sec), &link_sec);
|
||||||
|
|
||||||
if (lmp_bredr_capable(hdev)) {
|
if (lmp_bredr_capable(hdev)) {
|
||||||
set_bredr_scan(&req);
|
if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
|
||||||
|
set_bredr_scan(&req);
|
||||||
update_class(&req);
|
update_class(&req);
|
||||||
update_name(&req);
|
update_name(&req);
|
||||||
update_eir(&req);
|
update_eir(&req);
|
||||||
@ -4132,44 +4507,6 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
|
|
||||||
{
|
|
||||||
struct cmd_lookup match = { NULL, hdev };
|
|
||||||
bool changed = false;
|
|
||||||
int err = 0;
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
u8 mgmt_err = mgmt_status(status);
|
|
||||||
|
|
||||||
if (enable && test_and_clear_bit(HCI_LE_ENABLED,
|
|
||||||
&hdev->dev_flags))
|
|
||||||
err = new_settings(hdev, NULL);
|
|
||||||
|
|
||||||
mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp,
|
|
||||||
&mgmt_err);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enable) {
|
|
||||||
if (!test_and_set_bit(HCI_LE_ENABLED, &hdev->dev_flags))
|
|
||||||
changed = true;
|
|
||||||
} else {
|
|
||||||
if (test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags))
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match);
|
|
||||||
|
|
||||||
if (changed)
|
|
||||||
err = new_settings(hdev, match.sk);
|
|
||||||
|
|
||||||
if (match.sk)
|
|
||||||
sock_put(match.sk);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||||
u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
|
u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8
|
||||||
ssp, u8 *eir, u16 eir_len)
|
ssp, u8 *eir, u16 eir_len)
|
||||||
@ -4286,6 +4623,3 @@ int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
|
|||||||
return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
|
return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
|
||||||
cmd ? cmd->sk : NULL);
|
cmd ? cmd->sk : NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_param(enable_hs, bool, 0644);
|
|
||||||
MODULE_PARM_DESC(enable_hs, "Enable High Speed support");
|
|
||||||
|
@ -544,7 +544,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
|
struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int sent = 0;
|
int sent;
|
||||||
|
|
||||||
if (test_bit(RFCOMM_DEFER_SETUP, &d->flags))
|
if (test_bit(RFCOMM_DEFER_SETUP, &d->flags))
|
||||||
return -ENOTCONN;
|
return -ENOTCONN;
|
||||||
@ -559,6 +559,10 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|||||||
|
|
||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
|
||||||
|
sent = bt_sock_wait_ready(sk, msg->msg_flags);
|
||||||
|
if (sent)
|
||||||
|
goto done;
|
||||||
|
|
||||||
while (len) {
|
while (len) {
|
||||||
size_t size = min_t(size_t, len, d->mtu);
|
size_t size = min_t(size_t, len, d->mtu);
|
||||||
int err;
|
int err;
|
||||||
@ -594,6 +598,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
|||||||
len -= size;
|
len -= size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
release_sock(sk);
|
release_sock(sk);
|
||||||
|
|
||||||
return sent;
|
return sent;
|
||||||
|
@ -569,7 +569,6 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
|
|||||||
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
|
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
|
||||||
{
|
{
|
||||||
struct rfcomm_dev *dev = dlc->owner;
|
struct rfcomm_dev *dev = dlc->owner;
|
||||||
struct tty_struct *tty;
|
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -581,38 +580,8 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
|
|||||||
DPM_ORDER_DEV_AFTER_PARENT);
|
DPM_ORDER_DEV_AFTER_PARENT);
|
||||||
|
|
||||||
wake_up_interruptible(&dev->port.open_wait);
|
wake_up_interruptible(&dev->port.open_wait);
|
||||||
} else if (dlc->state == BT_CLOSED) {
|
} else if (dlc->state == BT_CLOSED)
|
||||||
tty = tty_port_tty_get(&dev->port);
|
tty_port_tty_hangup(&dev->port, false);
|
||||||
if (!tty) {
|
|
||||||
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
|
|
||||||
/* Drop DLC lock here to avoid deadlock
|
|
||||||
* 1. rfcomm_dev_get will take rfcomm_dev_lock
|
|
||||||
* but in rfcomm_dev_add there's lock order:
|
|
||||||
* rfcomm_dev_lock -> dlc lock
|
|
||||||
* 2. tty_port_put will deadlock if it's
|
|
||||||
* the last reference
|
|
||||||
*
|
|
||||||
* FIXME: when we release the lock anything
|
|
||||||
* could happen to dev, even its destruction
|
|
||||||
*/
|
|
||||||
rfcomm_dlc_unlock(dlc);
|
|
||||||
if (rfcomm_dev_get(dev->id) == NULL) {
|
|
||||||
rfcomm_dlc_lock(dlc);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!test_and_set_bit(RFCOMM_TTY_RELEASED,
|
|
||||||
&dev->flags))
|
|
||||||
tty_port_put(&dev->port);
|
|
||||||
|
|
||||||
tty_port_put(&dev->port);
|
|
||||||
rfcomm_dlc_lock(dlc);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tty_hangup(tty);
|
|
||||||
tty_kref_put(tty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
|
static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
|
||||||
|
@ -847,16 +847,27 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
|
|
||||||
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
__u8 code = skb->data[0];
|
struct hci_conn *hcon = conn->hcon;
|
||||||
__u8 reason;
|
__u8 code, reason;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
if (hcon->type != LE_LINK) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skb->len < 1) {
|
||||||
|
kfree_skb(skb);
|
||||||
|
return -EILSEQ;
|
||||||
|
}
|
||||||
|
|
||||||
if (!test_bit(HCI_LE_ENABLED, &conn->hcon->hdev->dev_flags)) {
|
if (!test_bit(HCI_LE_ENABLED, &conn->hcon->hdev->dev_flags)) {
|
||||||
err = -ENOTSUPP;
|
err = -ENOTSUPP;
|
||||||
reason = SMP_PAIRING_NOTSUPP;
|
reason = SMP_PAIRING_NOTSUPP;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code = skb->data[0];
|
||||||
skb_pull(skb, sizeof(code));
|
skb_pull(skb, sizeof(code));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user