Commit 970282d0 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 2015-03-19

This wont the last 4.1 bluetooth-next pull request, but we've piled up
enough patches in less than a week that I wanted to save you from a
single huge "last-minute" pull somewhere closer to the merge window.

The main changes are:

 - Simultaneous LE & BR/EDR discovery support for HW that can do it
 - Complete LE OOB pairing support
 - More fine-grained mgmt-command access control (normal user can now do
   harmless read-only operations).
 - Added RF power amplifier support in cc2520 ieee802154 driver
 - Some cleanups/fixes in ieee802154 code

Please let me know if there are any issues pulling. Thanks.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c9bdc0dd ea6edfbc
...@@ -13,11 +13,15 @@ Required properties: ...@@ -13,11 +13,15 @@ Required properties:
- cca-gpio: GPIO spec for the CCA pin - cca-gpio: GPIO spec for the CCA pin
- vreg-gpio: GPIO spec for the VREG pin - vreg-gpio: GPIO spec for the VREG pin
- reset-gpio: GPIO spec for the RESET pin - reset-gpio: GPIO spec for the RESET pin
Optional properties:
- amplified: include if the CC2520 is connected to a CC2591 amplifier
Example: Example:
cc2520@0 { cc2520@0 {
compatible = "ti,cc2520"; compatible = "ti,cc2520";
reg = <0>; reg = <0>;
spi-max-frequency = <4000000>; spi-max-frequency = <4000000>;
amplified;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&cc2520_cape_pins>; pinctrl-0 = <&cc2520_cape_pins>;
fifo-gpio = <&gpio1 18 0>; fifo-gpio = <&gpio1 18 0>;
......
...@@ -215,8 +215,8 @@ static const struct usb_device_id blacklist_table[] = { ...@@ -215,8 +215,8 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
/* QCA ROME chipset */ /* QCA ROME chipset */
{ USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME}, { USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME },
{ USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME}, { USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME },
/* Broadcom BCM2035 */ /* Broadcom BCM2035 */
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 }, { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
...@@ -3019,6 +3019,7 @@ static int btusb_probe(struct usb_interface *intf, ...@@ -3019,6 +3019,7 @@ static int btusb_probe(struct usb_interface *intf,
hdev->shutdown = btusb_shutdown_intel; hdev->shutdown = btusb_shutdown_intel;
hdev->set_bdaddr = btusb_set_bdaddr_intel; hdev->set_bdaddr = btusb_set_bdaddr_intel;
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
} }
if (id->driver_info & BTUSB_INTEL_NEW) { if (id->driver_info & BTUSB_INTEL_NEW) {
...@@ -3042,6 +3043,7 @@ static int btusb_probe(struct usb_interface *intf, ...@@ -3042,6 +3043,7 @@ static int btusb_probe(struct usb_interface *intf,
if (id->driver_info & BTUSB_ATH3012) { if (id->driver_info & BTUSB_ATH3012) {
hdev->set_bdaddr = btusb_set_bdaddr_ath3012; hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
} }
...@@ -3085,6 +3087,8 @@ static int btusb_probe(struct usb_interface *intf, ...@@ -3085,6 +3087,8 @@ static int btusb_probe(struct usb_interface *intf,
/* Fake CSR devices with broken commands */ /* Fake CSR devices with broken commands */
if (bcdDevice <= 0x100) if (bcdDevice <= 0x100)
hdev->setup = btusb_setup_csr; hdev->setup = btusb_setup_csr;
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
} }
if (id->driver_info & BTUSB_SNIFFER) { if (id->driver_info & BTUSB_SNIFFER) {
......
...@@ -325,7 +325,7 @@ at86rf230_read_subreg(struct at86rf230_local *lp, ...@@ -325,7 +325,7 @@ at86rf230_read_subreg(struct at86rf230_local *lp,
int rc; int rc;
rc = __at86rf230_read(lp, addr, data); rc = __at86rf230_read(lp, addr, data);
if (rc > 0) if (!rc)
*data = (*data & mask) >> shift; *data = (*data & mask) >> shift;
return rc; return rc;
......
...@@ -714,11 +714,45 @@ static irqreturn_t cc2520_sfd_isr(int irq, void *data) ...@@ -714,11 +714,45 @@ static irqreturn_t cc2520_sfd_isr(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int cc2520_get_platform_data(struct spi_device *spi,
struct cc2520_platform_data *pdata)
{
struct device_node *np = spi->dev.of_node;
struct cc2520_private *priv = spi_get_drvdata(spi);
if (!np) {
struct cc2520_platform_data *spi_pdata = spi->dev.platform_data;
if (!spi_pdata)
return -ENOENT;
*pdata = *spi_pdata;
return 0;
}
pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0);
priv->fifo_pin = pdata->fifo;
pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0);
pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0);
pdata->cca = of_get_named_gpio(np, "cca-gpio", 0);
pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0);
pdata->reset = of_get_named_gpio(np, "reset-gpio", 0);
pdata->amplified = of_property_read_bool(np, "amplified");
return 0;
}
static int cc2520_hw_init(struct cc2520_private *priv) static int cc2520_hw_init(struct cc2520_private *priv)
{ {
u8 status = 0, state = 0xff; u8 status = 0, state = 0xff;
int ret; int ret;
int timeout = 100; int timeout = 100;
struct cc2520_platform_data pdata;
ret = cc2520_get_platform_data(priv->spi, &pdata);
if (ret)
goto err_ret;
ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state); ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state);
if (ret) if (ret)
...@@ -741,11 +775,47 @@ static int cc2520_hw_init(struct cc2520_private *priv) ...@@ -741,11 +775,47 @@ static int cc2520_hw_init(struct cc2520_private *priv)
dev_vdbg(&priv->spi->dev, "oscillator brought up\n"); dev_vdbg(&priv->spi->dev, "oscillator brought up\n");
/* Registers default value: section 28.1 in Datasheet */ /* If the CC2520 is connected to a CC2591 amplifier, we must both
* configure GPIOs on the CC2520 to correctly configure the CC2591
* and change a couple settings of the CC2520 to work with the
* amplifier. See section 8 page 17 of TI application note AN065.
* http://www.ti.com/lit/an/swra229a/swra229a.pdf
*/
if (pdata.amplified) {
ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF9);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x16);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_GPIOCTRL0, 0x46);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_GPIOCTRL5, 0x47);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_GPIOPOLARITY, 0x1e);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_TXCTRL, 0xc1);
if (ret)
goto err_ret;
} else {
ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7); ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7);
if (ret) if (ret)
goto err_ret; goto err_ret;
ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11);
if (ret)
goto err_ret;
}
/* Registers default value: section 28.1 in 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;
...@@ -770,10 +840,6 @@ static int cc2520_hw_init(struct cc2520_private *priv) ...@@ -770,10 +840,6 @@ 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_AGCCTRL1, 0x11);
if (ret)
goto err_ret;
ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10); ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10);
if (ret) if (ret)
goto err_ret; goto err_ret;
...@@ -808,40 +874,10 @@ static int cc2520_hw_init(struct cc2520_private *priv) ...@@ -808,40 +874,10 @@ static int cc2520_hw_init(struct cc2520_private *priv)
return ret; return ret;
} }
static struct cc2520_platform_data *
cc2520_get_platform_data(struct spi_device *spi)
{
struct cc2520_platform_data *pdata;
struct device_node *np = spi->dev.of_node;
struct cc2520_private *priv = spi_get_drvdata(spi);
if (!np)
return spi->dev.platform_data;
pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
goto done;
pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0);
priv->fifo_pin = pdata->fifo;
pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0);
pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0);
pdata->cca = of_get_named_gpio(np, "cca-gpio", 0);
pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0);
pdata->reset = of_get_named_gpio(np, "reset-gpio", 0);
spi->dev.platform_data = pdata;
done:
return pdata;
}
static int cc2520_probe(struct spi_device *spi) static int cc2520_probe(struct spi_device *spi)
{ {
struct cc2520_private *priv; struct cc2520_private *priv;
struct cc2520_platform_data *pdata; struct cc2520_platform_data pdata;
int ret; int ret;
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
...@@ -850,8 +886,8 @@ static int cc2520_probe(struct spi_device *spi) ...@@ -850,8 +886,8 @@ static int cc2520_probe(struct spi_device *spi)
spi_set_drvdata(spi, priv); spi_set_drvdata(spi, priv);
pdata = cc2520_get_platform_data(spi); ret = cc2520_get_platform_data(spi, &pdata);
if (!pdata) { if (ret < 0) {
dev_err(&spi->dev, "no platform data\n"); dev_err(&spi->dev, "no platform data\n");
return -EINVAL; return -EINVAL;
} }
...@@ -869,76 +905,76 @@ static int cc2520_probe(struct spi_device *spi) ...@@ -869,76 +905,76 @@ static int cc2520_probe(struct spi_device *spi)
init_completion(&priv->tx_complete); init_completion(&priv->tx_complete);
/* Request all the gpio's */ /* Request all the gpio's */
if (!gpio_is_valid(pdata->fifo)) { if (!gpio_is_valid(pdata.fifo)) {
dev_err(&spi->dev, "fifo gpio is not valid\n"); dev_err(&spi->dev, "fifo gpio is not valid\n");
ret = -EINVAL; ret = -EINVAL;
goto err_hw_init; goto err_hw_init;
} }
ret = devm_gpio_request_one(&spi->dev, pdata->fifo, ret = devm_gpio_request_one(&spi->dev, pdata.fifo,
GPIOF_IN, "fifo"); GPIOF_IN, "fifo");
if (ret) if (ret)
goto err_hw_init; goto err_hw_init;
if (!gpio_is_valid(pdata->cca)) { if (!gpio_is_valid(pdata.cca)) {
dev_err(&spi->dev, "cca gpio is not valid\n"); dev_err(&spi->dev, "cca gpio is not valid\n");
ret = -EINVAL; ret = -EINVAL;
goto err_hw_init; goto err_hw_init;
} }
ret = devm_gpio_request_one(&spi->dev, pdata->cca, ret = devm_gpio_request_one(&spi->dev, pdata.cca,
GPIOF_IN, "cca"); GPIOF_IN, "cca");
if (ret) if (ret)
goto err_hw_init; goto err_hw_init;
if (!gpio_is_valid(pdata->fifop)) { if (!gpio_is_valid(pdata.fifop)) {
dev_err(&spi->dev, "fifop gpio is not valid\n"); dev_err(&spi->dev, "fifop gpio is not valid\n");
ret = -EINVAL; ret = -EINVAL;
goto err_hw_init; goto err_hw_init;
} }
ret = devm_gpio_request_one(&spi->dev, pdata->fifop, ret = devm_gpio_request_one(&spi->dev, pdata.fifop,
GPIOF_IN, "fifop"); GPIOF_IN, "fifop");
if (ret) if (ret)
goto err_hw_init; goto err_hw_init;
if (!gpio_is_valid(pdata->sfd)) { if (!gpio_is_valid(pdata.sfd)) {
dev_err(&spi->dev, "sfd gpio is not valid\n"); dev_err(&spi->dev, "sfd gpio is not valid\n");
ret = -EINVAL; ret = -EINVAL;
goto err_hw_init; goto err_hw_init;
} }
ret = devm_gpio_request_one(&spi->dev, pdata->sfd, ret = devm_gpio_request_one(&spi->dev, pdata.sfd,
GPIOF_IN, "sfd"); GPIOF_IN, "sfd");
if (ret) if (ret)
goto err_hw_init; goto err_hw_init;
if (!gpio_is_valid(pdata->reset)) { if (!gpio_is_valid(pdata.reset)) {
dev_err(&spi->dev, "reset gpio is not valid\n"); dev_err(&spi->dev, "reset gpio is not valid\n");
ret = -EINVAL; ret = -EINVAL;
goto err_hw_init; goto err_hw_init;
} }
ret = devm_gpio_request_one(&spi->dev, pdata->reset, ret = devm_gpio_request_one(&spi->dev, pdata.reset,
GPIOF_OUT_INIT_LOW, "reset"); GPIOF_OUT_INIT_LOW, "reset");
if (ret) if (ret)
goto err_hw_init; goto err_hw_init;
if (!gpio_is_valid(pdata->vreg)) { if (!gpio_is_valid(pdata.vreg)) {
dev_err(&spi->dev, "vreg gpio is not valid\n"); dev_err(&spi->dev, "vreg gpio is not valid\n");
ret = -EINVAL; ret = -EINVAL;
goto err_hw_init; goto err_hw_init;
} }
ret = devm_gpio_request_one(&spi->dev, pdata->vreg, ret = devm_gpio_request_one(&spi->dev, pdata.vreg,
GPIOF_OUT_INIT_LOW, "vreg"); GPIOF_OUT_INIT_LOW, "vreg");
if (ret) if (ret)
goto err_hw_init; goto err_hw_init;
gpio_set_value(pdata->vreg, HIGH); gpio_set_value(pdata.vreg, HIGH);
usleep_range(100, 150); usleep_range(100, 150);
gpio_set_value(pdata->reset, HIGH); gpio_set_value(pdata.reset, HIGH);
usleep_range(200, 250); usleep_range(200, 250);
ret = cc2520_hw_init(priv); ret = cc2520_hw_init(priv);
...@@ -947,7 +983,7 @@ static int cc2520_probe(struct spi_device *spi) ...@@ -947,7 +983,7 @@ static int cc2520_probe(struct spi_device *spi)
/* Set up fifop interrupt */ /* Set up fifop interrupt */
ret = devm_request_irq(&spi->dev, ret = devm_request_irq(&spi->dev,
gpio_to_irq(pdata->fifop), gpio_to_irq(pdata.fifop),
cc2520_fifop_isr, cc2520_fifop_isr,
IRQF_TRIGGER_RISING, IRQF_TRIGGER_RISING,
dev_name(&spi->dev), dev_name(&spi->dev),
...@@ -959,7 +995,7 @@ static int cc2520_probe(struct spi_device *spi) ...@@ -959,7 +995,7 @@ static int cc2520_probe(struct spi_device *spi)
/* Set up sfd interrupt */ /* Set up sfd interrupt */
ret = devm_request_irq(&spi->dev, ret = devm_request_irq(&spi->dev,
gpio_to_irq(pdata->sfd), gpio_to_irq(pdata.sfd),
cc2520_sfd_isr, cc2520_sfd_isr,
IRQF_TRIGGER_FALLING, IRQF_TRIGGER_FALLING,
dev_name(&spi->dev), dev_name(&spi->dev),
......
...@@ -21,6 +21,7 @@ struct cc2520_platform_data { ...@@ -21,6 +21,7 @@ struct cc2520_platform_data {
int sfd; int sfd;
int reset; int reset;
int vreg; int vreg;
bool amplified;
}; };
#endif #endif
...@@ -335,6 +335,11 @@ static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk, ...@@ -335,6 +335,11 @@ static inline struct sk_buff *bt_skb_send_alloc(struct sock *sk,
int bt_to_errno(__u16 code); int bt_to_errno(__u16 code);
void hci_sock_set_flag(struct sock *sk, int nr);
void hci_sock_clear_flag(struct sock *sk, int nr);
int hci_sock_test_flag(struct sock *sk, int nr);
unsigned short hci_sock_get_channel(struct sock *sk);
int hci_sock_init(void); int hci_sock_init(void);
void hci_sock_cleanup(void); void hci_sock_cleanup(void);
......
...@@ -160,6 +160,14 @@ enum { ...@@ -160,6 +160,14 @@ enum {
* during the hdev->setup vendor callback. * during the hdev->setup vendor callback.
*/ */
HCI_QUIRK_STRICT_DUPLICATE_FILTER, HCI_QUIRK_STRICT_DUPLICATE_FILTER,
/* When this quirk is set, LE scan and BR/EDR inquiry is done
* simultaneously, otherwise it's interleaved.
*
* This quirk can be set before hci_register_dev is called or
* during the hdev->setup vendor callback.
*/
HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
}; };
/* HCI device flags */ /* HCI device flags */
...@@ -179,6 +187,16 @@ enum { ...@@ -179,6 +187,16 @@ enum {
HCI_RESET, HCI_RESET,
}; };
/* HCI socket flags */
enum {
HCI_SOCK_TRUSTED,
HCI_MGMT_INDEX_EVENTS,
HCI_MGMT_UNCONF_INDEX_EVENTS,
HCI_MGMT_EXT_INDEX_EVENTS,
HCI_MGMT_GENERIC_EVENTS,
HCI_MGMT_OOB_DATA_EVENTS,
};
/* /*
* BR/EDR and/or LE controller flags: the flags defined here should represent * BR/EDR and/or LE controller flags: the flags defined here should represent
* states from the controller. * states from the controller.
...@@ -447,6 +465,10 @@ enum { ...@@ -447,6 +465,10 @@ enum {
#define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */ #define EIR_SSP_HASH_C 0x0E /* Simple Pairing Hash C */
#define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ #define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */
#define EIR_DEVICE_ID 0x10 /* device ID */ #define EIR_DEVICE_ID 0x10 /* device ID */
#define EIR_LE_BDADDR 0x1B /* LE Bluetooth device address */
#define EIR_LE_ROLE 0x1C /* LE role */
#define EIR_LE_SC_CONFIRM 0x22 /* LE SC Confirmation Value */
#define EIR_LE_SC_RANDOM 0x23 /* LE SC Random Value */
/* Low Energy Advertising Flags */ /* Low Energy Advertising Flags */
#define LE_AD_LIMITED 0x01 /* Limited Discoverable */ #define LE_AD_LIMITED 0x01 /* Limited Discoverable */
......
...@@ -596,7 +596,6 @@ enum { ...@@ -596,7 +596,6 @@ enum {
HCI_CONN_SC_ENABLED, HCI_CONN_SC_ENABLED,
HCI_CONN_AES_CCM, HCI_CONN_AES_CCM,
HCI_CONN_POWER_SAVE, HCI_CONN_POWER_SAVE,
HCI_CONN_REMOTE_OOB,
HCI_CONN_FLUSH_KEY, HCI_CONN_FLUSH_KEY,
HCI_CONN_ENCRYPT, HCI_CONN_ENCRYPT,
HCI_CONN_AUTH, HCI_CONN_AUTH,
...@@ -1284,14 +1283,15 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); ...@@ -1284,14 +1283,15 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
/* ----- HCI Sockets ----- */ /* ----- HCI Sockets ----- */
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);
void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
struct sock *skip_sk); int flag, struct sock *skip_sk);
void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb);
void hci_sock_dev_event(struct hci_dev *hdev, int event); void hci_sock_dev_event(struct hci_dev *hdev, int event);
#define HCI_MGMT_VAR_LEN (1 << 0) #define HCI_MGMT_VAR_LEN BIT(0)
#define HCI_MGMT_NO_HDEV (1 << 1) #define HCI_MGMT_NO_HDEV BIT(1)
#define HCI_MGMT_UNCONFIGURED (1 << 2) #define HCI_MGMT_UNTRUSTED BIT(2)
#define HCI_MGMT_UNCONFIGURED BIT(3)
struct hci_mgmt_handler { struct hci_mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
...@@ -1305,6 +1305,7 @@ struct hci_mgmt_chan { ...@@ -1305,6 +1305,7 @@ struct hci_mgmt_chan {
unsigned short channel; unsigned short channel;
size_t handler_count; size_t handler_count;
const struct hci_mgmt_handler *handlers; const struct hci_mgmt_handler *handlers;
void (*hdev_init) (struct sock *sk, struct hci_dev *hdev);
}; };
int hci_mgmt_chan_register(struct hci_mgmt_chan *c); int hci_mgmt_chan_register(struct hci_mgmt_chan *c);
...@@ -1329,9 +1330,6 @@ void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c); ...@@ -1329,9 +1330,6 @@ void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c);
#define DISCOV_BREDR_INQUIRY_LEN 0x08 #define DISCOV_BREDR_INQUIRY_LEN 0x08
#define DISCOV_LE_RESTART_DELAY msecs_to_jiffies(200) /* msec */ #define DISCOV_LE_RESTART_DELAY msecs_to_jiffies(200) /* msec */
int mgmt_control(struct hci_mgmt_chan *chan, struct sock *sk,
struct msghdr *msg, size_t msglen);
int mgmt_new_settings(struct hci_dev *hdev); int mgmt_new_settings(struct hci_dev *hdev);
void mgmt_index_added(struct hci_dev *hdev); void mgmt_index_added(struct hci_dev *hdev);
void mgmt_index_removed(struct hci_dev *hdev); void mgmt_index_removed(struct hci_dev *hdev);
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#define MGMT_STATUS_INVALID_INDEX 0x11 #define MGMT_STATUS_INVALID_INDEX 0x11
#define MGMT_STATUS_RFKILLED 0x12 #define MGMT_STATUS_RFKILLED 0x12
#define MGMT_STATUS_ALREADY_PAIRED 0x13 #define MGMT_STATUS_ALREADY_PAIRED 0x13
#define MGMT_STATUS_PERMISSION_DENIED 0x14
struct mgmt_hdr { struct mgmt_hdr {
__le16 opcode; __le16 opcode;
...@@ -505,6 +506,39 @@ struct mgmt_cp_start_service_discovery { ...@@ -505,6 +506,39 @@ struct mgmt_cp_start_service_discovery {
} __packed; } __packed;
#define MGMT_START_SERVICE_DISCOVERY_SIZE 4 #define MGMT_START_SERVICE_DISCOVERY_SIZE 4
#define MGMT_OP_READ_LOCAL_OOB_EXT_DATA 0x003B
struct mgmt_cp_read_local_oob_ext_data {
__u8 type;
} __packed;
#define MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE 1
struct mgmt_rp_read_local_oob_ext_data {
__u8 type;
__le16 eir_len;
__u8 eir[0];
} __packed;
#define MGMT_OP_READ_EXT_INDEX_LIST 0x003C
#define MGMT_READ_EXT_INDEX_LIST_SIZE 0
struct mgmt_rp_read_ext_index_list {
__le16 num_controllers;
struct {
__le16 index;
__u8 type;
__u8 bus;
} entry[0];
} __packed;
#define MGMT_OP_READ_ADV_FEATURES 0x0003D
#define MGMT_READ_ADV_FEATURES_SIZE 0
struct mgmt_rp_read_adv_features {
__le32 supported_flags;
__u8 max_adv_data_len;
__u8 max_scan_rsp_len;
__u8 max_instances;
__u8 num_instances;
__u8 instance[0];
} __packed;
#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;
...@@ -692,3 +726,19 @@ struct mgmt_ev_new_conn_param { ...@@ -692,3 +726,19 @@ struct mgmt_ev_new_conn_param {
#define MGMT_EV_UNCONF_INDEX_REMOVED 0x001e #define MGMT_EV_UNCONF_INDEX_REMOVED 0x001e
#define MGMT_EV_NEW_CONFIG_OPTIONS 0x001f #define MGMT_EV_NEW_CONFIG_OPTIONS 0x001f
struct mgmt_ev_ext_index {
__u8 type;
__u8 bus;
} __packed;
#define MGMT_EV_EXT_INDEX_ADDED 0x0020
#define MGMT_EV_EXT_INDEX_REMOVED 0x0021
#define MGMT_EV_LOCAL_OOB_DATA_UPDATED 0x0022
struct mgmt_ev_local_oob_data_updated {
__u8 type;
__le16 eir_len;
__u8 eir[0];
} __packed;
...@@ -13,7 +13,7 @@ bluetooth_6lowpan-y := 6lowpan.o ...@@ -13,7 +13,7 @@ bluetooth_6lowpan-y := 6lowpan.o
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 ecc.o hci_request.o a2mp.o amp.o ecc.o hci_request.o mgmt_util.o
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
......
...@@ -2902,12 +2902,26 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status, ...@@ -2902,12 +2902,26 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status,
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
&hdev->quirks)) {
/* If we were running LE only scan, change discovery
* state. If we were running both LE and BR/EDR inquiry
* simultaneously, and BR/EDR inquiry is already
* finished, stop discovery, otherwise BR/EDR inquiry
* will stop discovery when finished.
*/
if (!test_bit(HCI_INQUIRY, &hdev->flags))
hci_discovery_set_state(hdev,
DISCOVERY_STOPPED);
} else {
hci_inquiry_cache_flush(hdev); hci_inquiry_cache_flush(hdev);
err = hci_req_run(&req, inquiry_complete); err = hci_req_run(&req, inquiry_complete);
if (err) { if (err) {
BT_ERR("Inquiry request failed: err %d", err); BT_ERR("Inquiry request failed: err %d", err);
hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_discovery_set_state(hdev,
DISCOVERY_STOPPED);
}
} }
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
......
...@@ -166,7 +166,7 @@ static int remote_oob_show(struct seq_file *f, void *ptr) ...@@ -166,7 +166,7 @@ static int remote_oob_show(struct seq_file *f, void *ptr)
seq_printf(f, "%pMR (type %u) %u %*phN %*phN %*phN %*phN\n", seq_printf(f, "%pMR (type %u) %u %*phN %*phN %*phN %*phN\n",
&data->bdaddr, data->bdaddr_type, data->present, &data->bdaddr, data->bdaddr_type, data->present,
16, data->hash192, 16, data->rand192, 16, data->hash192, 16, data->rand192,
16, data->hash256, 19, data->rand256); 16, data->hash256, 16, data->rand256);
} }
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
......
...@@ -2126,6 +2126,15 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -2126,6 +2126,15 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
goto unlock; goto unlock;
if (list_empty(&discov->resolve)) { if (list_empty(&discov->resolve)) {
/* When BR/EDR inquiry is active and no LE scanning is in
* progress, then change discovery state to indicate completion.
*
* When running LE scanning and BR/EDR inquiry simultaneously
* and the LE scan already finished, then change the discovery
* state to indicate completion.
*/
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
!test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
goto unlock; goto unlock;
} }
...@@ -2135,6 +2144,15 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -2135,6 +2144,15 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
e->name_state = NAME_PENDING; e->name_state = NAME_PENDING;
hci_discovery_set_state(hdev, DISCOVERY_RESOLVING); hci_discovery_set_state(hdev, DISCOVERY_RESOLVING);
} else { } else {
/* When BR/EDR inquiry is active and no LE scanning is in
* progress, then change discovery state to indicate completion.
*
* When running LE scanning and BR/EDR inquiry simultaneously
* and the LE scan already finished, then change the discovery
* state to indicate completion.
*/
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
!test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks))
hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
} }
...@@ -3889,7 +3907,6 @@ static u8 bredr_oob_data_present(struct hci_conn *conn) ...@@ -3889,7 +3907,6 @@ static u8 bredr_oob_data_present(struct hci_conn *conn)
if (!data) if (!data)
return 0x00; return 0x00;
if (conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)) {
if (bredr_sc_enabled(hdev)) { if (bredr_sc_enabled(hdev)) {
/* When Secure Connections is enabled, then just /* When Secure Connections is enabled, then just
* return the present value stored with the OOB * return the present value stored with the OOB
...@@ -3921,9 +3938,6 @@ static u8 bredr_oob_data_present(struct hci_conn *conn) ...@@ -3921,9 +3938,6 @@ static u8 bredr_oob_data_present(struct hci_conn *conn)
return 0x00; return 0x00;
return 0x01; return 0x01;
}
return 0x00;
} }
static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
...@@ -4010,8 +4024,6 @@ static void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -4010,8 +4024,6 @@ static void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->remote_cap = ev->capability; conn->remote_cap = ev->capability;
conn->remote_auth = ev->authentication; conn->remote_auth = ev->authentication;
if (ev->oob_data)
set_bit(HCI_CONN_REMOTE_OOB, &conn->flags);
unlock: unlock:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
......
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci_mon.h> #include <net/bluetooth/hci_mon.h>
#include <net/bluetooth/mgmt.h>
#include "mgmt_util.h"
static LIST_HEAD(mgmt_chan_list); static LIST_HEAD(mgmt_chan_list);
static DEFINE_MUTEX(mgmt_chan_list_lock); static DEFINE_MUTEX(mgmt_chan_list_lock);
...@@ -47,8 +50,29 @@ struct hci_pinfo { ...@@ -47,8 +50,29 @@ struct hci_pinfo {
struct hci_filter filter; struct hci_filter filter;
__u32 cmsg_mask; __u32 cmsg_mask;
unsigned short channel; unsigned short channel;
unsigned long flags;
}; };
void hci_sock_set_flag(struct sock *sk, int nr)
{
set_bit(nr, &hci_pi(sk)->flags);
}
void hci_sock_clear_flag(struct sock *sk, int nr)
{
clear_bit(nr, &hci_pi(sk)->flags);
}
int hci_sock_test_flag(struct sock *sk, int nr)
{
return test_bit(nr, &hci_pi(sk)->flags);
}
unsigned short hci_sock_get_channel(struct sock *sk)
{
return hci_pi(sk)->channel;
}
static inline int hci_test_bit(int nr, const void *addr) static inline int hci_test_bit(int nr, const void *addr)
{ {
return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
...@@ -188,7 +212,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -188,7 +212,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
/* Send frame to sockets with specific channel */ /* Send frame to sockets with specific channel */
void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
struct sock *skip_sk) int flag, struct sock *skip_sk)
{ {
struct sock *sk; struct sock *sk;
...@@ -199,6 +223,10 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, ...@@ -199,6 +223,10 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
sk_for_each(sk, &hci_sk_list.head) { sk_for_each(sk, &hci_sk_list.head) {
struct sk_buff *nskb; struct sk_buff *nskb;
/* Ignore socket without the flag set */
if (!hci_sock_test_flag(sk, flag))
continue;
/* Skip the original socket */ /* Skip the original socket */
if (sk == skip_sk) if (sk == skip_sk)
continue; continue;
...@@ -266,7 +294,8 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -266,7 +294,8 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
hdr->index = cpu_to_le16(hdev->id); hdr->index = cpu_to_le16(hdev->id);
hdr->len = cpu_to_le16(skb->len); hdr->len = cpu_to_le16(skb->len);
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy, NULL); hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb_copy); kfree_skb(skb_copy);
} }
...@@ -373,7 +402,8 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event) ...@@ -373,7 +402,8 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
skb = create_monitor_event(hdev, event); skb = create_monitor_event(hdev, event);
if (skb) { if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, NULL); hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb); kfree_skb(skb);
} }
} }
...@@ -752,6 +782,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, ...@@ -752,6 +782,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
goto done; goto done;
} }
/* The monitor interface is restricted to CAP_NET_RAW
* capabilities and with that implicitly trusted.
*/
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
send_monitor_replay(sk); send_monitor_replay(sk);
atomic_inc(&monitor_promisc); atomic_inc(&monitor_promisc);
...@@ -768,11 +803,29 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, ...@@ -768,11 +803,29 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
goto done; goto done;
} }
if (!capable(CAP_NET_ADMIN)) { /* Users with CAP_NET_ADMIN capabilities are allowed
err = -EPERM; * access to all management commands and events. For
goto done; * untrusted users the interface is restricted and
* also only untrusted events are sent.
*/
if (capable(CAP_NET_ADMIN))
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
/* At the moment the index and unconfigured index events
* are enabled unconditionally. Setting them on each
* socket when binding keeps this functionality. They
* however might be cleared later and then sending of these
* events will be disabled, but that is then intentional.
*
* This also enables generic events that are safe to be
* received by untrusted users. Example for such events
* are changes to settings, class of device, name etc.
*/
if (haddr.hci_channel == HCI_CHANNEL_CONTROL) {
hci_sock_set_flag(sk, HCI_MGMT_INDEX_EVENTS);
hci_sock_set_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS);
hci_sock_set_flag(sk, HCI_MGMT_GENERIC_EVENTS);
} }
break; break;
} }
...@@ -901,6 +954,117 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, ...@@ -901,6 +954,117 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
return err ? : copied; return err ? : copied;
} }
static int hci_mgmt_cmd(struct hci_mgmt_chan *chan, struct sock *sk,
struct msghdr *msg, size_t msglen)
{
void *buf;
u8 *cp;
struct mgmt_hdr *hdr;
u16 opcode, index, len;
struct hci_dev *hdev = NULL;
const struct hci_mgmt_handler *handler;
bool var_len, no_hdev;
int err;
BT_DBG("got %zu bytes", msglen);
if (msglen < sizeof(*hdr))
return -EINVAL;
buf = kmalloc(msglen, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (memcpy_from_msg(buf, msg, msglen)) {
err = -EFAULT;
goto done;
}
hdr = buf;
opcode = __le16_to_cpu(hdr->opcode);
index = __le16_to_cpu(hdr->index);
len = __le16_to_cpu(hdr->len);
if (len != msglen - sizeof(*hdr)) {
err = -EINVAL;
goto done;
}
if (opcode >= chan->handler_count ||
chan->handlers[opcode].func == NULL) {
BT_DBG("Unknown op %u", opcode);
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_UNKNOWN_COMMAND);
goto done;
}
handler = &chan->handlers[opcode];
if (!hci_sock_test_flag(sk, HCI_SOCK_TRUSTED) &&
!(handler->flags & HCI_MGMT_UNTRUSTED)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_PERMISSION_DENIED);
goto done;
}
if (index != MGMT_INDEX_NONE) {
hdev = hci_dev_get(index);
if (!hdev) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
if (hci_dev_test_flag(hdev, HCI_SETUP) ||
hci_dev_test_flag(hdev, HCI_CONFIG) ||
hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
!(handler->flags & HCI_MGMT_UNCONFIGURED)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
}
no_hdev = (handler->flags & HCI_MGMT_NO_HDEV);
if (no_hdev != !hdev) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_INDEX);
goto done;
}
var_len = (handler->flags & HCI_MGMT_VAR_LEN);
if ((var_len && len < handler->data_len) ||
(!var_len && len != handler->data_len)) {
err = mgmt_cmd_status(sk, index, opcode,
MGMT_STATUS_INVALID_PARAMS);
goto done;
}
if (hdev && chan->hdev_init)
chan->hdev_init(sk, hdev);
cp = buf + sizeof(*hdr);
err = handler->func(sk, hdev, cp, len);
if (err < 0)
goto done;
err = msglen;
done:
if (hdev)
hci_dev_put(hdev);
kfree(buf);
return err;
}
static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
size_t len) size_t len)
{ {
...@@ -934,7 +1098,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, ...@@ -934,7 +1098,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
mutex_lock(&mgmt_chan_list_lock); mutex_lock(&mgmt_chan_list_lock);
chan = __hci_mgmt_chan_find(hci_pi(sk)->channel); chan = __hci_mgmt_chan_find(hci_pi(sk)->channel);
if (chan) if (chan)
err = mgmt_control(chan, sk, msg, len); err = hci_mgmt_cmd(chan, sk, msg, len);
else else
err = -EINVAL; err = -EINVAL;
......
This diff is collapsed.
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2015 Intel Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/mgmt.h>
#include "mgmt_util.h"
int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
void *data, u16 data_len, int flag, struct sock *skip_sk)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(event);
if (hdev)
hdr->index = cpu_to_le16(hdev->id);
else
hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
hdr->len = cpu_to_le16(data_len);
if (data)
memcpy(skb_put(skb, data_len), data, data_len);
/* Time stamp */
__net_timestamp(skb);
hci_send_to_channel(channel, skb, flag, skip_sk);
kfree_skb(skb);
return 0;
}
int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_status *ev;
int err;
BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev));
ev = (void *) skb_put(skb, sizeof(*ev));
ev->status = status;
ev->opcode = cpu_to_le16(cmd);
err = sock_queue_rcv_skb(sk, skb);
if (err < 0)
kfree_skb(skb);
return err;
}
int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
void *rp, size_t rp_len)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *ev;
int err;
BT_DBG("sock %p", sk);
skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = (void *) skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
ev->opcode = cpu_to_le16(cmd);
ev->status = status;
if (rp)
memcpy(ev->data, rp, rp_len);
err = sock_queue_rcv_skb(sk, skb);
if (err < 0)
kfree_skb(skb);
return err;
}
struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
struct hci_dev *hdev)
{
struct mgmt_pending_cmd *cmd;
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
if (hci_sock_get_channel(cmd->sk) != channel)
continue;
if (cmd->opcode == opcode)
return cmd;
}
return NULL;
}
struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
u16 opcode,
struct hci_dev *hdev,
const void *data)
{
struct mgmt_pending_cmd *cmd;
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
if (cmd->user_data != data)
continue;
if (cmd->opcode == opcode)
return cmd;
}
return NULL;
}
void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
void *data)
{
struct mgmt_pending_cmd *cmd, *tmp;
list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
if (opcode > 0 && cmd->opcode != opcode)
continue;
cb(cmd, data);
}
}
struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_pending_cmd *cmd;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return NULL;
cmd->opcode = opcode;
cmd->index = hdev->id;
cmd->param = kmemdup(data, len, GFP_KERNEL);
if (!cmd->param) {
kfree(cmd);
return NULL;
}
cmd->param_len = len;
cmd->sk = sk;
sock_hold(sk);
list_add(&cmd->list, &hdev->mgmt_pending);
return cmd;
}
void mgmt_pending_free(struct mgmt_pending_cmd *cmd)
{
sock_put(cmd->sk);
kfree(cmd->param);
kfree(cmd);
}
void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
{
list_del(&cmd->list);
mgmt_pending_free(cmd);
}
/*
BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2015 Intel Coropration
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
struct mgmt_pending_cmd {
struct list_head list;
u16 opcode;
int index;
void *param;
size_t param_len;
struct sock *sk;
void *user_data;
int (*cmd_complete)(struct mgmt_pending_cmd *cmd, u8 status);
};
int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
void *data, u16 data_len, int flag, struct sock *skip_sk);
int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status);
int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
void *rp, size_t rp_len);
struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
struct hci_dev *hdev);
struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
u16 opcode,
struct hci_dev *hdev,
const void *data);
void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
void *data);
struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
struct hci_dev *hdev,
void *data, u16 len);
void mgmt_pending_free(struct mgmt_pending_cmd *cmd);
void mgmt_pending_remove(struct mgmt_pending_cmd *cmd);
This diff is collapsed.
...@@ -188,6 +188,7 @@ int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey); ...@@ -188,6 +188,7 @@ int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16], bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
const bdaddr_t *bdaddr); const bdaddr_t *bdaddr);
int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa); int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa);
int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]);
int smp_register(struct hci_dev *hdev); int smp_register(struct hci_dev *hdev);
void smp_unregister(struct hci_dev *hdev); void smp_unregister(struct hci_dev *hdev);
......
#ifndef __MAC802154_DRVIER_OPS #ifndef __MAC802154_DRIVER_OPS
#define __MAC802154_DRIVER_OPS #define __MAC802154_DRIVER_OPS
#include <linux/types.h> #include <linux/types.h>
...@@ -220,4 +220,4 @@ drv_set_promiscuous_mode(struct ieee802154_local *local, bool on) ...@@ -220,4 +220,4 @@ drv_set_promiscuous_mode(struct ieee802154_local *local, bool on)
return local->ops->set_promiscuous_mode(&local->hw, on); return local->ops->set_promiscuous_mode(&local->hw, on);
} }
#endif /* __MAC802154_DRVIER_OPS */ #endif /* __MAC802154_DRIVER_OPS */
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