Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next

This commit is contained in:
John W. Linville 2013-10-03 16:19:07 -04:00
commit 0d4f55bc37
25 changed files with 1393 additions and 486 deletions

View File

@ -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__

View File

@ -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 },

View File

@ -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 */

View File

@ -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 @@ exit:
} }
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, &param, 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;

View File

@ -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");

View File

@ -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;
}; };

View File

@ -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 },

View File

@ -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");

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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__

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
} }

View File

@ -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 @@ error:
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 @@ done:
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 @@ drop:
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);

View File

@ -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);

View File

@ -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 @@ failed:
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 @@ unlock:
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 @@ done:
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");

View File

@ -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;

View File

@ -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)

View File

@ -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));
/* /*