Commit 250fbf12 authored by David S. Miller's avatar David S. Miller

Merge branch 'for-upstream' of...

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

Johan Hedberg says:

====================
pull request: bluetooth-next 2016-01-08

Here's one more bluetooth-next pull request for the 4.5 kernel:

 - Support for CRC check and promiscuous mode for CC2520
 - Fixes to btmrvl driver
 - New ACPI IDs for hci_bcm driver
 - Limited Discovery support for the Bluetooth mgmt interface
 - Minor other cleanups here and there

Please let me know if there are any issues pulling. Thanks.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a33ad9ac 8cf60cf2
...@@ -89,6 +89,7 @@ struct btmrvl_adapter { ...@@ -89,6 +89,7 @@ struct btmrvl_adapter {
wait_queue_head_t event_hs_wait_q; wait_queue_head_t event_hs_wait_q;
u8 cmd_complete; u8 cmd_complete;
bool is_suspended; bool is_suspended;
bool is_suspending;
}; };
struct btmrvl_private { struct btmrvl_private {
......
...@@ -436,6 +436,11 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -436,6 +436,11 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("type=%d, len=%d", hci_skb_pkt_type(skb), skb->len); BT_DBG("type=%d, len=%d", hci_skb_pkt_type(skb), skb->len);
if (priv->adapter->is_suspending || priv->adapter->is_suspended) {
BT_ERR("%s: Device is suspending or suspended", __func__);
return -EBUSY;
}
switch (hci_skb_pkt_type(skb)) { switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT: case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++; hdev->stat.cmd_tx++;
...@@ -452,7 +457,8 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -452,7 +457,8 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
skb_queue_tail(&priv->adapter->tx_queue, skb); skb_queue_tail(&priv->adapter->tx_queue, skb);
wake_up_interruptible(&priv->main_thread.wait_q); if (!priv->adapter->is_suspended)
wake_up_interruptible(&priv->main_thread.wait_q);
return 0; return 0;
} }
...@@ -543,7 +549,7 @@ static int btmrvl_setup(struct hci_dev *hdev) ...@@ -543,7 +549,7 @@ static int btmrvl_setup(struct hci_dev *hdev)
if (ret) if (ret)
return ret; return ret;
priv->btmrvl_dev.gpio_gap = 0xffff; priv->btmrvl_dev.gpio_gap = 0xfffe;
btmrvl_check_device_tree(priv); btmrvl_check_device_tree(priv);
...@@ -643,7 +649,8 @@ static int btmrvl_service_main_thread(void *data) ...@@ -643,7 +649,8 @@ static int btmrvl_service_main_thread(void *data)
if (adapter->ps_state == PS_SLEEP) if (adapter->ps_state == PS_SLEEP)
continue; continue;
if (!priv->btmrvl_dev.tx_dnld_rdy) if (!priv->btmrvl_dev.tx_dnld_rdy ||
priv->adapter->is_suspended)
continue; continue;
skb = skb_dequeue(&adapter->tx_queue); skb = skb_dequeue(&adapter->tx_queue);
......
...@@ -1112,7 +1112,8 @@ static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card) ...@@ -1112,7 +1112,8 @@ static int btmrvl_sdio_download_fw(struct btmrvl_sdio_card *card)
*/ */
if (btmrvl_sdio_verify_fw_download(card, pollnum)) { if (btmrvl_sdio_verify_fw_download(card, pollnum)) {
BT_ERR("FW failed to be active in time!"); BT_ERR("FW failed to be active in time!");
return -ETIMEDOUT; ret = -ETIMEDOUT;
goto done;
} }
sdio_release_host(card->func); sdio_release_host(card->func);
...@@ -1544,10 +1545,10 @@ static int btmrvl_sdio_suspend(struct device *dev) ...@@ -1544,10 +1545,10 @@ static int btmrvl_sdio_suspend(struct device *dev)
} }
priv = card->priv; priv = card->priv;
priv->adapter->is_suspending = true;
hcidev = priv->btmrvl_dev.hcidev; hcidev = priv->btmrvl_dev.hcidev;
BT_DBG("%s: SDIO suspend", hcidev->name); BT_DBG("%s: SDIO suspend", hcidev->name);
hci_suspend_dev(hcidev); hci_suspend_dev(hcidev);
skb_queue_purge(&priv->adapter->tx_queue);
if (priv->adapter->hs_state != HS_ACTIVATED) { if (priv->adapter->hs_state != HS_ACTIVATED) {
if (btmrvl_enable_hs(priv)) { if (btmrvl_enable_hs(priv)) {
...@@ -1556,6 +1557,7 @@ static int btmrvl_sdio_suspend(struct device *dev) ...@@ -1556,6 +1557,7 @@ static int btmrvl_sdio_suspend(struct device *dev)
} }
} }
priv->adapter->is_suspending = false;
priv->adapter->is_suspended = true; priv->adapter->is_suspended = true;
/* We will keep the power when hs enabled successfully */ /* We will keep the power when hs enabled successfully */
......
...@@ -814,9 +814,16 @@ static const struct hci_uart_proto bcm_proto = { ...@@ -814,9 +814,16 @@ static const struct hci_uart_proto bcm_proto = {
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static const struct acpi_device_id bcm_acpi_match[] = { static const struct acpi_device_id bcm_acpi_match[] = {
{ "BCM2E1A", 0 },
{ "BCM2E39", 0 }, { "BCM2E39", 0 },
{ "BCM2E3A", 0 },
{ "BCM2E3D", 0 },
{ "BCM2E3F", 0 },
{ "BCM2E40", 0 },
{ "BCM2E64", 0 },
{ "BCM2E65", 0 }, { "BCM2E65", 0 },
{ "BCM2E67", 0 }, { "BCM2E67", 0 },
{ "BCM2E7B", 0 },
{ }, { },
}; };
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match); MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/ieee802154.h> #include <linux/ieee802154.h>
#include <linux/crc-ccitt.h>
#include <asm/unaligned.h>
#include <net/mac802154.h> #include <net/mac802154.h>
#include <net/cfg802154.h> #include <net/cfg802154.h>
...@@ -189,6 +191,18 @@ ...@@ -189,6 +191,18 @@
#define CC2520_RXFIFOCNT 0x3E #define CC2520_RXFIFOCNT 0x3E
#define CC2520_TXFIFOCNT 0x3F #define CC2520_TXFIFOCNT 0x3F
/* CC2520_FRMFILT0 */
#define FRMFILT0_FRAME_FILTER_EN BIT(0)
#define FRMFILT0_PAN_COORDINATOR BIT(1)
/* CC2520_FRMCTRL0 */
#define FRMCTRL0_AUTOACK BIT(5)
#define FRMCTRL0_AUTOCRC BIT(6)
/* CC2520_FRMCTRL1 */
#define FRMCTRL1_SET_RXENMASK_ON_TX BIT(0)
#define FRMCTRL1_IGNORE_TX_UNDERF BIT(1)
/* Driver private information */ /* Driver private information */
struct cc2520_private { struct cc2520_private {
struct spi_device *spi; /* SPI device structure */ struct spi_device *spi; /* SPI device structure */
...@@ -201,6 +215,7 @@ struct cc2520_private { ...@@ -201,6 +215,7 @@ struct cc2520_private {
struct work_struct fifop_irqwork;/* Workqueue for FIFOP */ struct work_struct fifop_irqwork;/* Workqueue for FIFOP */
spinlock_t lock; /* Lock for is_tx*/ spinlock_t lock; /* Lock for is_tx*/
struct completion tx_complete; /* Work completion for Tx */ struct completion tx_complete; /* Work completion for Tx */
bool promiscuous; /* Flag for promiscuous mode */
}; };
/* Generic Functions */ /* Generic Functions */
...@@ -367,14 +382,14 @@ cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data) ...@@ -367,14 +382,14 @@ cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data)
} }
static int static int
cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len) cc2520_write_txfifo(struct cc2520_private *priv, u8 pkt_len, u8 *data, u8 len)
{ {
int status; int status;
/* length byte must include FCS even /* length byte must include FCS even
* if it is calculated in the hardware * if it is calculated in the hardware
*/ */
int len_byte = len + 2; int len_byte = pkt_len;
struct spi_message msg; struct spi_message msg;
...@@ -414,7 +429,7 @@ cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len) ...@@ -414,7 +429,7 @@ cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len)
} }
static int static int
cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len, u8 *lqi) cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len)
{ {
int status; int status;
struct spi_message msg; struct spi_message msg;
...@@ -470,12 +485,25 @@ cc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb) ...@@ -470,12 +485,25 @@ cc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
unsigned long flags; unsigned long flags;
int rc; int rc;
u8 status = 0; u8 status = 0;
u8 pkt_len;
/* In promiscuous mode we disable AUTOCRC so we can get the raw CRC
* values on RX. This means we need to manually add the CRC on TX.
*/
if (priv->promiscuous) {
u16 crc = crc_ccitt(0, skb->data, skb->len);
put_unaligned_le16(crc, skb_put(skb, 2));
pkt_len = skb->len;
} else {
pkt_len = skb->len + 2;
}
rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX); rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX);
if (rc) if (rc)
goto err_tx; goto err_tx;
rc = cc2520_write_txfifo(priv, skb->data, skb->len); rc = cc2520_write_txfifo(priv, pkt_len, skb->data, skb->len);
if (rc) if (rc)
goto err_tx; goto err_tx;
...@@ -518,22 +546,62 @@ static int cc2520_rx(struct cc2520_private *priv) ...@@ -518,22 +546,62 @@ static int cc2520_rx(struct cc2520_private *priv)
u8 len = 0, lqi = 0, bytes = 1; u8 len = 0, lqi = 0, bytes = 1;
struct sk_buff *skb; struct sk_buff *skb;
cc2520_read_rxfifo(priv, &len, bytes, &lqi); /* Read single length byte from the radio. */
cc2520_read_rxfifo(priv, &len, bytes);
if (len < 2 || len > IEEE802154_MTU) if (!ieee802154_is_valid_psdu_len(len)) {
return -EINVAL; /* Corrupted frame received, clear frame buffer by
* reading entire buffer.
*/
dev_dbg(&priv->spi->dev, "corrupted frame received\n");
len = IEEE802154_MTU;
}
skb = dev_alloc_skb(len); skb = dev_alloc_skb(len);
if (!skb) if (!skb)
return -ENOMEM; return -ENOMEM;
if (cc2520_read_rxfifo(priv, skb_put(skb, len), len, &lqi)) { if (cc2520_read_rxfifo(priv, skb_put(skb, len), len)) {
dev_dbg(&priv->spi->dev, "frame reception failed\n"); dev_dbg(&priv->spi->dev, "frame reception failed\n");
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
skb_trim(skb, skb->len - 2); /* In promiscuous mode, we configure the radio to include the
* CRC (AUTOCRC==0) and we pass on the packet unconditionally. If not
* in promiscuous mode, we check the CRC here, but leave the
* RSSI/LQI/CRC_OK bytes as they will get removed in the mac layer.
*/
if (!priv->promiscuous) {
bool crc_ok;
/* Check if the CRC is valid. With AUTOCRC set, the most
* significant bit of the last byte returned from the CC2520
* is CRC_OK flag. See section 20.3.4 of the datasheet.
*/
crc_ok = skb->data[len - 1] & BIT(7);
/* If we failed CRC drop the packet in the driver layer. */
if (!crc_ok) {
dev_dbg(&priv->spi->dev, "CRC check failed\n");
kfree_skb(skb);
return -EINVAL;
}
/* To calculate LQI, the lower 7 bits of the last byte (the
* correlation value provided by the radio) must be scaled to
* the range 0-255. According to section 20.6, the correlation
* value ranges from 50-110. Ideally this would be calibrated
* per hardware design, but we use roughly the datasheet values
* to get close enough while avoiding floating point.
*/
lqi = skb->data[len - 1] & 0x7f;
if (lqi < 50)
lqi = 50;
else if (lqi > 113)
lqi = 113;
lqi = (lqi - 50) * 4;
}
ieee802154_rx_irqsafe(priv->hw, skb, lqi); ieee802154_rx_irqsafe(priv->hw, skb, lqi);
...@@ -619,14 +687,19 @@ cc2520_filter(struct ieee802154_hw *hw, ...@@ -619,14 +687,19 @@ cc2520_filter(struct ieee802154_hw *hw,
} }
if (changed & IEEE802154_AFILT_PANC_CHANGED) { if (changed & IEEE802154_AFILT_PANC_CHANGED) {
u8 frmfilt0;
dev_vdbg(&priv->spi->dev, dev_vdbg(&priv->spi->dev,
"cc2520_filter called for panc change\n"); "cc2520_filter called for panc change\n");
cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0);
if (filt->pan_coord) if (filt->pan_coord)
ret = cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0 |= FRMFILT0_PAN_COORDINATOR;
0x02);
else else
ret = cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0 &= ~FRMFILT0_PAN_COORDINATOR;
0x00);
ret = cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0);
} }
return ret; return ret;
...@@ -723,6 +796,30 @@ cc2520_set_txpower(struct ieee802154_hw *hw, s32 mbm) ...@@ -723,6 +796,30 @@ cc2520_set_txpower(struct ieee802154_hw *hw, s32 mbm)
return cc2520_cc2591_set_tx_power(priv, mbm); return cc2520_cc2591_set_tx_power(priv, mbm);
} }
static int
cc2520_set_promiscuous_mode(struct ieee802154_hw *hw, bool on)
{
struct cc2520_private *priv = hw->priv;
u8 frmfilt0;
dev_dbg(&priv->spi->dev, "%s : mode %d\n", __func__, on);
priv->promiscuous = on;
cc2520_read_register(priv, CC2520_FRMFILT0, &frmfilt0);
if (on) {
/* Disable automatic ACK, automatic CRC, and frame filtering. */
cc2520_write_register(priv, CC2520_FRMCTRL0, 0);
frmfilt0 &= ~FRMFILT0_FRAME_FILTER_EN;
} else {
cc2520_write_register(priv, CC2520_FRMCTRL0, FRMCTRL0_AUTOACK |
FRMCTRL0_AUTOCRC);
frmfilt0 |= FRMFILT0_FRAME_FILTER_EN;
}
return cc2520_write_register(priv, CC2520_FRMFILT0, frmfilt0);
}
static const struct ieee802154_ops cc2520_ops = { static const struct ieee802154_ops cc2520_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.start = cc2520_start, .start = cc2520_start,
...@@ -732,6 +829,7 @@ static const struct ieee802154_ops cc2520_ops = { ...@@ -732,6 +829,7 @@ static const struct ieee802154_ops cc2520_ops = {
.set_channel = cc2520_set_channel, .set_channel = cc2520_set_channel,
.set_hw_addr_filt = cc2520_filter, .set_hw_addr_filt = cc2520_filter,
.set_txpower = cc2520_set_txpower, .set_txpower = cc2520_set_txpower,
.set_promiscuous_mode = cc2520_set_promiscuous_mode,
}; };
static int cc2520_register(struct cc2520_private *priv) static int cc2520_register(struct cc2520_private *priv)
...@@ -749,7 +847,8 @@ static int cc2520_register(struct cc2520_private *priv) ...@@ -749,7 +847,8 @@ static int cc2520_register(struct cc2520_private *priv)
/* We do support only 2.4 Ghz */ /* We do support only 2.4 Ghz */
priv->hw->phy->supported.channels[0] = 0x7FFF800; priv->hw->phy->supported.channels[0] = 0x7FFF800;
priv->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT; priv->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
IEEE802154_HW_PROMISCUOUS;
priv->hw->phy->flags = WPAN_PHY_FLAG_TXPOWER; priv->hw->phy->flags = WPAN_PHY_FLAG_TXPOWER;
...@@ -919,6 +1018,11 @@ static int cc2520_hw_init(struct cc2520_private *priv) ...@@ -919,6 +1018,11 @@ static int cc2520_hw_init(struct cc2520_private *priv)
} }
/* Registers default value: section 28.1 in Datasheet */ /* Registers default value: section 28.1 in Datasheet */
/* Set the CCA threshold to -50 dBm. This seems to have been copied
* from the TinyOS CC2520 driver and is much higher than the -84 dBm
* threshold suggested in the datasheet.
*/
ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A); ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A);
if (ret) if (ret)
goto err_ret; goto err_ret;
...@@ -955,15 +1059,10 @@ static int cc2520_hw_init(struct cc2520_private *priv) ...@@ -955,15 +1059,10 @@ static int cc2520_hw_init(struct cc2520_private *priv)
if (ret) if (ret)
goto err_ret; goto err_ret;
ret = cc2520_write_register(priv, CC2520_FRMCTRL0, 0x60); /* Configure registers correctly for this driver. */
if (ret) ret = cc2520_write_register(priv, CC2520_FRMCTRL1,
goto err_ret; FRMCTRL1_SET_RXENMASK_ON_TX |
FRMCTRL1_IGNORE_TX_UNDERF);
ret = cc2520_write_register(priv, CC2520_FRMCTRL1, 0x03);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_FRMFILT0, 0x00);
if (ret) if (ret)
goto err_ret; goto err_ret;
......
...@@ -77,6 +77,7 @@ struct discovery_state { ...@@ -77,6 +77,7 @@ struct discovery_state {
u8 last_adv_data_len; u8 last_adv_data_len;
bool report_invalid_rssi; bool report_invalid_rssi;
bool result_filtering; bool result_filtering;
bool limited;
s8 rssi; s8 rssi;
u16 uuid_count; u16 uuid_count;
u8 (*uuids)[16]; u8 (*uuids)[16];
...@@ -1283,31 +1284,41 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, ...@@ -1283,31 +1284,41 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
mutex_unlock(&hci_cb_list_lock); mutex_unlock(&hci_cb_list_lock);
} }
static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) static inline void *eir_get_data(u8 *eir, size_t eir_len, u8 type,
size_t *data_len)
{ {
size_t parsed = 0; size_t parsed = 0;
if (data_len < 2) if (eir_len < 2)
return false; return NULL;
while (parsed < data_len - 1) { while (parsed < eir_len - 1) {
u8 field_len = data[0]; u8 field_len = eir[0];
if (field_len == 0) if (field_len == 0)
break; break;
parsed += field_len + 1; parsed += field_len + 1;
if (parsed > data_len) if (parsed > eir_len)
break; break;
if (data[1] == type) if (eir[1] != type) {
return true; eir += field_len + 1;
continue;
}
/* Zero length data */
if (field_len == 1)
return NULL;
data += field_len + 1; if (data_len)
*data_len = field_len - 1;
return &eir[2];
} }
return false; return NULL;
} }
static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type) static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
......
...@@ -584,6 +584,8 @@ struct mgmt_rp_get_adv_size_info { ...@@ -584,6 +584,8 @@ struct mgmt_rp_get_adv_size_info {
__u8 max_scan_rsp_len; __u8 max_scan_rsp_len;
} __packed; } __packed;
#define MGMT_OP_START_LIMITED_DISCOVERY 0x0041
#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;
......
...@@ -337,7 +337,7 @@ struct ieee802154_mlme_ops { ...@@ -337,7 +337,7 @@ struct ieee802154_mlme_ops {
void (*get_mac_params)(struct net_device *dev, void (*get_mac_params)(struct net_device *dev,
struct ieee802154_mac_params *params); struct ieee802154_mac_params *params);
struct ieee802154_llsec_ops *llsec; const struct ieee802154_llsec_ops *llsec;
}; };
static inline struct ieee802154_mlme_ops * static inline struct ieee802154_mlme_ops *
......
...@@ -3833,9 +3833,9 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, ...@@ -3833,9 +3833,9 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
data.ssp_mode = 0x01; data.ssp_mode = 0x01;
if (hci_dev_test_flag(hdev, HCI_MGMT)) if (hci_dev_test_flag(hdev, HCI_MGMT))
name_known = eir_has_data_type(info->data, name_known = eir_get_data(info->data,
sizeof(info->data), sizeof(info->data),
EIR_NAME_COMPLETE); EIR_NAME_COMPLETE, NULL);
else else
name_known = true; name_known = true;
......
...@@ -1737,8 +1737,8 @@ static int le_scan_disable(struct hci_request *req, unsigned long opt) ...@@ -1737,8 +1737,8 @@ static int le_scan_disable(struct hci_request *req, unsigned long opt)
static int bredr_inquiry(struct hci_request *req, unsigned long opt) static int bredr_inquiry(struct hci_request *req, unsigned long opt)
{ {
u8 length = opt; u8 length = opt;
/* General inquiry access code (GIAC) */ const u8 giac[3] = { 0x33, 0x8b, 0x9e };
u8 lap[3] = { 0x33, 0x8b, 0x9e }; const u8 liac[3] = { 0x00, 0x8b, 0x9e };
struct hci_cp_inquiry cp; struct hci_cp_inquiry cp;
BT_DBG("%s", req->hdev->name); BT_DBG("%s", req->hdev->name);
...@@ -1748,7 +1748,12 @@ static int bredr_inquiry(struct hci_request *req, unsigned long opt) ...@@ -1748,7 +1748,12 @@ static int bredr_inquiry(struct hci_request *req, unsigned long opt)
hci_dev_unlock(req->hdev); hci_dev_unlock(req->hdev);
memset(&cp, 0, sizeof(cp)); memset(&cp, 0, sizeof(cp));
memcpy(&cp.lap, lap, sizeof(cp.lap));
if (req->hdev->discovery.limited)
memcpy(&cp.lap, liac, sizeof(cp.lap));
else
memcpy(&cp.lap, giac, sizeof(cp.lap));
cp.length = length; cp.length = length;
hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp); hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp);
......
...@@ -25,9 +25,8 @@ ...@@ -25,9 +25,8 @@
/* Bluetooth HCI sockets. */ /* Bluetooth HCI sockets. */
#include <linux/export.h> #include <linux/export.h>
#include <linux/utsname.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <generated/compile.h>
#include <generated/utsrelease.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
...@@ -385,17 +384,26 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event) ...@@ -385,17 +384,26 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
return skb; return skb;
} }
static void send_monitor_note(struct sock *sk, const char *text) static void __printf(2, 3)
send_monitor_note(struct sock *sk, const char *fmt, ...)
{ {
size_t len = strlen(text); size_t len;
struct hci_mon_hdr *hdr; struct hci_mon_hdr *hdr;
struct sk_buff *skb; struct sk_buff *skb;
va_list args;
va_start(args, fmt);
len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
skb = bt_skb_alloc(len + 1, GFP_ATOMIC); skb = bt_skb_alloc(len + 1, GFP_ATOMIC);
if (!skb) if (!skb)
return; return;
strcpy(skb_put(skb, len + 1), text); va_start(args, fmt);
vsprintf(skb_put(skb, len), fmt, args);
*skb_put(skb, 1) = 0;
va_end(args);
__net_timestamp(skb); __net_timestamp(skb);
...@@ -897,10 +905,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, ...@@ -897,10 +905,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
*/ */
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED); hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
send_monitor_note(sk, "Linux version " UTS_RELEASE send_monitor_note(sk, "Linux version %s (%s)",
" (" UTS_MACHINE ")"); init_utsname()->release,
send_monitor_note(sk, "Bluetooth subsystem version " init_utsname()->machine);
BT_SUBSYS_VERSION); send_monitor_note(sk, "Bluetooth subsystem version %s",
BT_SUBSYS_VERSION);
send_monitor_replay(sk); send_monitor_replay(sk);
atomic_inc(&monitor_promisc); atomic_inc(&monitor_promisc);
......
...@@ -103,6 +103,7 @@ static const u16 mgmt_commands[] = { ...@@ -103,6 +103,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_ADD_ADVERTISING, MGMT_OP_ADD_ADVERTISING,
MGMT_OP_REMOVE_ADVERTISING, MGMT_OP_REMOVE_ADVERTISING,
MGMT_OP_GET_ADV_SIZE_INFO, MGMT_OP_GET_ADV_SIZE_INFO,
MGMT_OP_START_LIMITED_DISCOVERY,
}; };
static const u16 mgmt_events[] = { static const u16 mgmt_events[] = {
...@@ -3283,6 +3284,9 @@ void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status) ...@@ -3283,6 +3284,9 @@ void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status)
if (!cmd) if (!cmd)
cmd = pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev); cmd = pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
if (!cmd)
cmd = pending_find(MGMT_OP_START_LIMITED_DISCOVERY, hdev);
if (cmd) { if (cmd) {
cmd->cmd_complete(cmd, mgmt_status(status)); cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd); mgmt_pending_remove(cmd);
...@@ -3318,8 +3322,8 @@ static bool discovery_type_is_valid(struct hci_dev *hdev, uint8_t type, ...@@ -3318,8 +3322,8 @@ static bool discovery_type_is_valid(struct hci_dev *hdev, uint8_t type,
return true; return true;
} }
static int start_discovery(struct sock *sk, struct hci_dev *hdev, static int start_discovery_internal(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len) u16 op, void *data, u16 len)
{ {
struct mgmt_cp_start_discovery *cp = data; struct mgmt_cp_start_discovery *cp = data;
struct mgmt_pending_cmd *cmd; struct mgmt_pending_cmd *cmd;
...@@ -3331,7 +3335,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, ...@@ -3331,7 +3335,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) { if (!hdev_is_powered(hdev)) {
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, err = mgmt_cmd_complete(sk, hdev->id, op,
MGMT_STATUS_NOT_POWERED, MGMT_STATUS_NOT_POWERED,
&cp->type, sizeof(cp->type)); &cp->type, sizeof(cp->type));
goto failed; goto failed;
...@@ -3339,15 +3343,14 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, ...@@ -3339,15 +3343,14 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
if (hdev->discovery.state != DISCOVERY_STOPPED || if (hdev->discovery.state != DISCOVERY_STOPPED ||
hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) { hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) {
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, err = mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_BUSY,
MGMT_STATUS_BUSY, &cp->type, &cp->type, sizeof(cp->type));
sizeof(cp->type));
goto failed; goto failed;
} }
if (!discovery_type_is_valid(hdev, cp->type, &status)) { if (!discovery_type_is_valid(hdev, cp->type, &status)) {
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, err = mgmt_cmd_complete(sk, hdev->id, op, status,
status, &cp->type, sizeof(cp->type)); &cp->type, sizeof(cp->type));
goto failed; goto failed;
} }
...@@ -3358,8 +3361,12 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, ...@@ -3358,8 +3361,12 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
hdev->discovery.type = cp->type; hdev->discovery.type = cp->type;
hdev->discovery.report_invalid_rssi = false; hdev->discovery.report_invalid_rssi = false;
if (op == MGMT_OP_START_LIMITED_DISCOVERY)
hdev->discovery.limited = true;
else
hdev->discovery.limited = false;
cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len); cmd = mgmt_pending_add(sk, op, hdev, data, len);
if (!cmd) { if (!cmd) {
err = -ENOMEM; err = -ENOMEM;
goto failed; goto failed;
...@@ -3376,6 +3383,21 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, ...@@ -3376,6 +3383,21 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
return err; return err;
} }
static int start_discovery(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
return start_discovery_internal(sk, hdev, MGMT_OP_START_DISCOVERY,
data, len);
}
static int start_limited_discovery(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
return start_discovery_internal(sk, hdev,
MGMT_OP_START_LIMITED_DISCOVERY,
data, len);
}
static int service_discovery_cmd_complete(struct mgmt_pending_cmd *cmd, static int service_discovery_cmd_complete(struct mgmt_pending_cmd *cmd,
u8 status) u8 status)
{ {
...@@ -6313,6 +6335,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { ...@@ -6313,6 +6335,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
HCI_MGMT_VAR_LEN }, HCI_MGMT_VAR_LEN },
{ remove_advertising, MGMT_REMOVE_ADVERTISING_SIZE }, { remove_advertising, MGMT_REMOVE_ADVERTISING_SIZE },
{ get_adv_size_info, MGMT_GET_ADV_SIZE_INFO_SIZE }, { get_adv_size_info, MGMT_GET_ADV_SIZE_INFO_SIZE },
{ start_limited_discovery, MGMT_START_DISCOVERY_SIZE },
}; };
void mgmt_index_added(struct hci_dev *hdev) void mgmt_index_added(struct hci_dev *hdev)
...@@ -7237,6 +7260,18 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ...@@ -7237,6 +7260,18 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
return; return;
} }
if (hdev->discovery.limited) {
/* Check for limited discoverable bit */
if (dev_class) {
if (!(dev_class[1] & 0x20))
return;
} else {
u8 *flags = eir_get_data(eir, eir_len, EIR_FLAGS, NULL);
if (!flags || !(flags[0] & LE_AD_LIMITED))
return;
}
}
/* Make sure that the buffer is big enough. The 5 extra bytes /* Make sure that the buffer is big enough. The 5 extra bytes
* are for the potential CoD field. * are for the potential CoD field.
*/ */
...@@ -7266,7 +7301,8 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ...@@ -7266,7 +7301,8 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
/* Copy EIR or advertising data into event */ /* Copy EIR or advertising data into event */
memcpy(ev->eir, eir, eir_len); memcpy(ev->eir, eir, eir_len);
if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV)) if (dev_class && !eir_get_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
NULL))
eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
dev_class, 3); dev_class, 3);
......
...@@ -126,7 +126,7 @@ static void mac802154_get_mac_params(struct net_device *dev, ...@@ -126,7 +126,7 @@ static void mac802154_get_mac_params(struct net_device *dev,
params->lbt = wpan_dev->lbt; params->lbt = wpan_dev->lbt;
} }
static struct ieee802154_llsec_ops mac802154_llsec_ops = { static const struct ieee802154_llsec_ops mac802154_llsec_ops = {
.get_params = mac802154_get_params, .get_params = mac802154_get_params,
.set_params = mac802154_set_params, .set_params = mac802154_set_params,
.add_key = mac802154_add_key, .add_key = mac802154_add_key,
......
...@@ -163,10 +163,6 @@ static int rfkill_gpio_remove(struct platform_device *pdev) ...@@ -163,10 +163,6 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static const struct acpi_device_id rfkill_acpi_match[] = { static const struct acpi_device_id rfkill_acpi_match[] = {
{ "BCM2E1A", RFKILL_TYPE_BLUETOOTH },
{ "BCM2E3D", RFKILL_TYPE_BLUETOOTH },
{ "BCM2E40", RFKILL_TYPE_BLUETOOTH },
{ "BCM2E64", RFKILL_TYPE_BLUETOOTH },
{ "BCM4752", RFKILL_TYPE_GPS }, { "BCM4752", RFKILL_TYPE_GPS },
{ "LNV4752", RFKILL_TYPE_GPS }, { "LNV4752", RFKILL_TYPE_GPS },
{ }, { },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment