From 0736cfa8e5bb7ee1d7b7d28aabe634fd3f85cb92 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 26 Aug 2013 21:40:51 -0700 Subject: Bluetooth: Introduce user channel flag for HCI devices This patch introduces a new user channel flag that allows to give full control of a HCI device to a user application. The kernel will stay away from the device and does not allow any further modifications of the device states. The existing raw flag is not used since it has a bit of unclear meaning due to its legacy. Using a new flag makes the code clearer. A device with the user channel flag set can still be enumerate using the legacy API, but it does not longer enumerate using the new management interface used by BlueZ 5 and beyond. This is intentional to not confuse users of modern systems. Signed-off-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index aaeaf0938ec0..128157db0680 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -109,6 +109,7 @@ enum { HCI_SERVICE_CACHE, HCI_DEBUG_KEYS, HCI_UNREGISTER, + HCI_USER_CHANNEL, HCI_LE_SCAN, HCI_SSP_ENABLED, -- cgit v1.2.3 From 23500189d7e03a071f0746f43f2cce875a62c91c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 26 Aug 2013 21:40:52 -0700 Subject: Bluetooth: Introduce new HCI socket channel for user operation This patch introcuces a new HCI socket channel that allows user applications to take control over a specific HCI device. The application gains exclusive access to this device and forces the kernel to stay away and not manage it. In case of the management interface it will actually hide the device. Such operation is useful for security testing tools that need to operate underneath the Bluetooth stack and need full control over a device. The advantage here is that the kernel still provides the service of hardware abstraction and HCI level access. The use of Bluetooth drivers for hardware access also means that sniffing tools like btmon or hcidump are still working and the whole set of transaction can be traced with existing tools. With the new channel it is possible to send HCI commands, ACL and SCO data packets and receive HCI events, ACL and SCO packets from the device. The format follows the well established H:4 protocol. The new HCI user channel can only be established when a device has been through its setup routine and is currently powered down. This is enforced to not cause any problems with current operations. In addition only one user channel per HCI device is allowed. It is exclusive access for one user application. Access to this channel is limited to process with CAP_NET_RAW capability. Using this new facility does not require any external library or special ioctl or socket filters. Just create the socket and bind it. After that the file descriptor is ready to speak H:4 protocol. struct sockaddr_hci addr; int fd; fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); memset(&addr, 0, sizeof(addr)); addr.hci_family = AF_BLUETOOTH; addr.hci_dev = 0; addr.hci_channel = HCI_CHANNEL_USER; bind(fd, (struct sockaddr *) &addr, sizeof(addr)); The example shows on how to create a user channel for hci0 device. Error handling has been left out of the example. However with the limitations mentioned above it is advised to handle errors. Binding of the user cahnnel socket can fail for various reasons. Specifically if the device is currently activated by BlueZ or if the access permissions are not present. Signed-off-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_sock.c | 86 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 128157db0680..30c88b585c1b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1571,6 +1571,7 @@ struct sockaddr_hci { #define HCI_DEV_NONE 0xffff #define HCI_CHANNEL_RAW 0 +#define HCI_CHANNEL_USER 1 #define HCI_CHANNEL_MONITOR 2 #define HCI_CHANNEL_CONTROL 3 diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 59e68f199178..c09e97638065 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -126,11 +126,20 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) if (skb->sk == sk) continue; - if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) - continue; - - if (is_filtered_packet(sk, skb)) + if (hci_pi(sk)->channel == HCI_CHANNEL_RAW) { + if (is_filtered_packet(sk, skb)) + continue; + } else if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { + if (!bt_cb(skb)->incoming) + 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) { /* Create a private copy with headroom */ @@ -444,6 +453,12 @@ static int hci_sock_release(struct socket *sock) bt_sock_unlink(&hci_sk_list, sk); 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); hci_dev_put(hdev); } @@ -661,6 +676,56 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, hci_pi(sk)->hdev = hdev; 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_RAW)) { + 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: if (haddr.hci_dev != HCI_DEV_NONE) { err = -EINVAL; @@ -807,6 +872,7 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock, case HCI_CHANNEL_RAW: hci_sock_cmsg(sk, msg, skb); break; + case HCI_CHANNEL_USER: case HCI_CHANNEL_CONTROL: case HCI_CHANNEL_MONITOR: sock_recv_timestamp(msg, sk, skb); @@ -841,6 +907,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, switch (hci_pi(sk)->channel) { case HCI_CHANNEL_RAW: + case HCI_CHANNEL_USER: break; case HCI_CHANNEL_CONTROL: err = mgmt_control(sk, msg, len); @@ -877,7 +944,8 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, skb_pull(skb, 1); 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 ogf = hci_opcode_ogf(opcode); u16 ocf = hci_opcode_ocf(opcode); @@ -908,6 +976,14 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, 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); queue_work(hdev->workqueue, &hdev->tx_work); } -- cgit v1.2.3 From 0af784dcbc44e3cf21a1bda3ce14df5fcc2bfe93 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Sep 2013 13:05:12 +0300 Subject: Bluetooth: Remove unused event mask struct The struct for HCI_Set_Event_Mask is never used. Instead a local 8-byte array is used for sending this command. Therefore, remove the unnecessary struct definition. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 30c88b585c1b..52bd2488ff4a 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -694,9 +694,6 @@ struct hci_cp_sniff_subrate { } __packed; #define HCI_OP_SET_EVENT_MASK 0x0c01 -struct hci_cp_set_event_mask { - __u8 mask[8]; -} __packed; #define HCI_OP_RESET 0x0c03 -- cgit v1.2.3 From e793dcf082c847bd2b742c781252c20cbec37986 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Sep 2013 13:05:19 +0300 Subject: Bluetooth: Fix waiting for clearing of BT_SK_SUSPEND flag In the case of blocking sockets we should not proceed with sendmsg() if the socket has the BT_SK_SUSPEND flag set. So far the code was only ensuring that POLLOUT doesn't get set for non-blocking sockets using poll() but there was no code in place to ensure that blocking sockets do the right thing when writing to them. This patch adds a new bt_sock_wait_ready helper function to sleep in the sendmsg call if the BT_SK_SUSPEND flag is set, and wake up as soon as it is unset. It also updates the L2CAP and RFCOMM sendmsg callbacks to take advantage of this new helper function. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/bluetooth.h | 1 + net/bluetooth/af_bluetooth.c | 40 +++++++++++++++++++++++++++++++++++++++ net/bluetooth/l2cap_sock.c | 6 ++++++ net/bluetooth/rfcomm/sock.c | 7 ++++++- 4 files changed, 53 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 10d43d8c7037..afbc711ba37a 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -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); 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_ready(struct sock *sk, unsigned long flags); void bt_accept_enqueue(struct sock *parent, struct sock *sk); void bt_accept_unlink(struct sock *sk); diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 9096137c889c..c600631cd19e 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -525,6 +525,46 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) } 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 struct bt_seq_state { struct bt_sock_list *l; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 0098af80b213..ad95b426b09c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -777,6 +777,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, if (sk->sk_state != BT_CONNECTED) 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); err = l2cap_chan_send(chan, msg, len, sk->sk_priority); l2cap_chan_unlock(chan); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 30b3721dc6d7..072938dc527d 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -544,7 +544,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct sock *sk = sock->sk; struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; struct sk_buff *skb; - int sent = 0; + int sent; if (test_bit(RFCOMM_DEFER_SETUP, &d->flags)) return -ENOTCONN; @@ -559,6 +559,10 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, lock_sock(sk); + sent = bt_sock_wait_ready(sk, msg->msg_flags); + if (sent) + goto done; + while (len) { size_t size = min_t(size_t, len, d->mtu); int err; @@ -594,6 +598,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, len -= size; } +done: release_sock(sk); return sent; -- cgit v1.2.3 From 5d4e7e8db0544ec53025383bac49a3328affdad3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 13 Sep 2013 11:40:01 +0300 Subject: Bluetooth: Add synchronization train parameters reading support This patch adds support for reading the synchronization train parameters for controllers that support the feature. Since the feature is detectable through the local features page 2, which is retreived only in stage 3 of the HCI init sequence, there is no other option than to add a fourth stage to the init sequence. For now the patch doesn't yet add storing of the parameters, but it is nevertheless convenient to have around to see what kind of parameters various controllers use by default (analyzable e.g. with the btmon user space tool). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 2 ++ net/bluetooth/hci_core.c | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 52bd2488ff4a..f7197a0ac759 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -835,6 +835,8 @@ struct hci_cp_write_le_host_supported { __u8 simul; } __packed; +#define HCI_OP_READ_SYNC_TRAIN_PARAMS 0x0c77 + #define HCI_OP_READ_LOCAL_VERSION 0x1001 struct hci_rp_read_local_version { __u8 status; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b24d2fa02c2f..ea542e07b2e9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -648,6 +648,15 @@ 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; + + /* 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) { int err; @@ -667,7 +676,11 @@ static int __hci_init(struct hci_dev *hdev) if (err < 0) 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) -- cgit v1.2.3 From d62e6d67a776fe6a0a725e2835e4f9e16e8db512 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 13 Sep 2013 11:40:02 +0300 Subject: Bluetooth: Add event mask page 2 setting support For those controller that support the HCI_Set_Event_Mask_Page_2 command we should include it in the init sequence. This patch implements sending of the command and enables the events in it based on supported features (currently only CSB is checked). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 2 ++ net/bluetooth/hci_core.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index f7197a0ac759..22d6e664612a 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -823,6 +823,8 @@ struct hci_rp_read_inq_rsp_tx_power { __s8 tx_power; } __packed; +#define HCI_OP_SET_EVENT_MASK_PAGE_2 0x0c63 + #define HCI_OP_READ_FLOW_CONTROL_MODE 0x0c66 struct hci_rp_read_flow_control_mode { __u8 status; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ea542e07b2e9..3d9f02b2f010 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -607,6 +607,34 @@ static void hci_set_le_support(struct hci_request *req) &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) { struct hci_dev *hdev = req->hdev; @@ -652,6 +680,10 @@ 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); -- cgit v1.2.3 From 416a4ae56b7a37407ca1155805c05b638f845778 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 25 Sep 2013 13:26:08 +0300 Subject: Bluetooth: Use async request for LE enable/disable This patch updates the code to use an asynchronous request for handling the enabling and disabling of LE support. This refactoring is necessary as a preparation for adding advertising support, since when LE is disabled we should also disable advertising, and the cleanest way to do this is to perform the two respective HCI commands in the same asynchronous request. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_event.c | 11 +++---- net/bluetooth/mgmt.c | 67 ++++++++++++++++------------------------ 3 files changed, 32 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3ede820d328f..26cc9f7858cd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1168,7 +1168,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_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, 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, u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 ssp, u8 *eir, u16 eir_len); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 94aab73f89d4..48db81f8a337 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -994,20 +994,19 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, return; if (!status) { - if (sent->le) + if (sent->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; + clear_bit(HCI_LE_ENABLED, &hdev->dev_flags); + } if (sent->simul) hdev->features[1][0] |= LMP_HOST_LE_BREDR; else 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, diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 61d4b190eebf..4c3984ee1114 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1354,11 +1354,32 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) return send_settings_rsp(sk, MGMT_OP_SET_HS, hdev); } +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) { struct mgmt_mode *cp = data; struct hci_cp_write_le_host_supported hci_cp; struct pending_cmd *cmd; + struct hci_request req; int err; u8 val, enabled; @@ -1419,8 +1440,12 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_cp.simul = lmp_le_br_capable(hdev); } - err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), - &hci_cp); + hci_req_init(&req, hdev); + + 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) mgmt_pending_remove(cmd); @@ -4141,44 +4166,6 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, 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, u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 ssp, u8 *eir, u16 eir_len) -- cgit v1.2.3 From eeca6f891305a80378da978f803821c2a0b648b6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 25 Sep 2013 13:26:09 +0300 Subject: Bluetooth: Add new mgmt setting for LE advertising This patch adds a new mgmt setting for LE advertising and hooks up the necessary places in the mgmt code to operate on the HCI_LE_PERIPHERAL flag (which corresponds to this setting). This patch does not yet add any new command for enabling the setting - that is left for a subsequent patch. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/hci_event.c | 1 + net/bluetooth/mgmt.c | 21 ++++++++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 9944c3e68c5d..6cc72b69e014 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -93,6 +93,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_BREDR 0x00000080 #define MGMT_SETTING_HS 0x00000100 #define MGMT_SETTING_LE 0x00000200 +#define MGMT_SETTING_ADVERTISING 0x00000400 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 48db81f8a337..917c7c833f69 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1000,6 +1000,7 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, } else { 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) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4c3984ee1114..9a2faa310b7c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -384,8 +384,10 @@ static u32 get_supported_settings(struct hci_dev *hdev) if (enable_hs) settings |= MGMT_SETTING_HS; - if (lmp_le_capable(hdev)) + if (lmp_le_capable(hdev)) { settings |= MGMT_SETTING_LE; + settings |= MGMT_SETTING_ADVERTISING; + } return settings; } @@ -424,6 +426,9 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_HS; + if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) + settings |= MGMT_SETTING_ADVERTISING; + return settings; } @@ -1411,6 +1416,11 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) 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); if (err < 0) goto unlock; @@ -1442,6 +1452,9 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_req_init(&req, hdev); + 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); @@ -3517,6 +3530,12 @@ static int powered_update_hci(struct hci_dev *hdev) sizeof(cp), &cp); } + 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); if (link_sec != test_bit(HCI_AUTH, &hdev->flags)) hci_req_add(&req, HCI_OP_WRITE_AUTH_ENABLE, -- cgit v1.2.3 From 4375f1037d52602413142e290608d0d84671ad36 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 25 Sep 2013 13:26:10 +0300 Subject: Bluetooth: Add new mgmt_set_advertising command This patch adds a new mgmt command for enabling and disabling LE advertising. The command depends on the LE setting being enabled first and will return a "rejected" response otherwise. The patch also adds safeguards so that there will ever only be one set_le or set_advertising command pending per adapter. The response handling and new_settings event sending is done in an asynchronous request callback, meaning raw HCI access from user space to enable advertising (e.g. hciconfig leadv) will not trigger the new_settings event. This is intentional since trying to support mixed raw HCI and mgmt access would mean adding extra state tracking or new helper functions, essentially negating the benefit of using the asynchronous request framework. The HCI_LE_ENABLED and HCI_LE_PERIPHERAL flags however are updated correctly even with raw HCI access so this will not completely break subsequent access over mgmt. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/mgmt.h | 2 + net/bluetooth/mgmt.c | 97 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 6cc72b69e014..421d7633a91f 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -352,6 +352,8 @@ struct mgmt_cp_set_device_id { } __packed; #define MGMT_SET_DEVICE_ID_SIZE 8 +#define MGMT_OP_SET_ADVERTISING 0x0029 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9a2faa310b7c..1b5b10fab545 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -76,6 +76,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_BLOCK_DEVICE, MGMT_OP_UNBLOCK_DEVICE, MGMT_OP_SET_DEVICE_ID, + MGMT_OP_SET_ADVERTISING, }; static const u16 mgmt_events[] = { @@ -1431,7 +1432,8 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) 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, MGMT_STATUS_BUSY); goto unlock; @@ -3136,6 +3138,98 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, 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; + int err; + + BT_DBG("request for %s", hdev->name); + + if (!lmp_le_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, + MGMT_STATUS_NOT_SUPPORTED); + + if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, + MGMT_STATUS_REJECTED); + + 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 void fast_connectable_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; @@ -3347,6 +3441,7 @@ static const struct mgmt_handler { { block_device, false, MGMT_BLOCK_DEVICE_SIZE }, { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE }, { set_device_id, false, MGMT_SET_DEVICE_ID_SIZE }, + { set_advertising, false, MGMT_SETTING_SIZE }, }; -- cgit v1.2.3 From a59ac2f7447d8a4202f29ade1af785212d12b1d1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Oct 2013 22:59:21 -0700 Subject: Bluetooth: Replace BDADDR_LOCAL with BDADDR_NONE The BDADDR_LOCAL is a relict from userspace and has never been used within the kernel. So remove that constant and replace it with a new BDADDR_NONE that is similar to HCI_DEV_NONE with all bits set. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index afbc711ba37a..5fd510675cfa 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -197,8 +197,8 @@ static inline bool bdaddr_type_is_le(__u8 type) return false; } -#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} }) -#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} }) +#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) +#define BDADDR_NONE (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}) /* Copy, swap, convert BD Address */ static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2) -- cgit v1.2.3 From 848566b381e72b07e41beffde677955ae1498153 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Oct 2013 22:59:22 -0700 Subject: Bluetooth: Provide high speed configuration option Hiding the Bluetooth high speed support behind a module parameter is not really useful. This can be enabled and disabled at runtime via the management interface. This also has the advantage that this can now be changed per controller and not just global. This patch removes the module parameter and exposes the high speed setting of the management interface to all controllers. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/hci_core.c | 6 ------ net/bluetooth/l2cap_core.c | 35 +++++++++++++++++++---------------- net/bluetooth/l2cap_sock.c | 10 ---------- net/bluetooth/mgmt.c | 11 ++--------- 5 files changed, 22 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1a966afbbfa8..f141b5f6e4f1 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -564,6 +564,7 @@ struct l2cap_conn { __u32 feat_mask; __u8 fixed_chan_mask; + bool hs_enabled; __u8 info_state; __u8 info_ident; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index dd2528c5b6bc..750c360f96db 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1222,12 +1222,6 @@ int hci_dev_open(__u16 dev) ret = hdev->setup(hdev); 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)) set_bit(HCI_RAW, &hdev->flags); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d1f1e78d1140..6d42498e862b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1016,13 +1016,12 @@ static bool __amp_capable(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; - if (enable_hs && - hci_amp_capable() && + if (conn->hs_enabled && hci_amp_capable() && chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED && conn->fixed_chan_mask & L2CAP_FC_A2MP) return true; - else - return false; + + return false; } 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; + if (hcon->type == ACL_LINK) + conn->hs_enabled = test_bit(HCI_HS_ENABLED, + &hcon->hdev->dev_flags); + spin_lock_init(&conn->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, @@ -3135,7 +3138,7 @@ static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan, static inline void l2cap_txwin_setup(struct l2cap_chan *chan) { if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && - __l2cap_ews_supported(chan)) { + __l2cap_ews_supported(chan->conn)) { /* use extended control field */ set_bit(FLAG_EXT_CTRL, &chan->flags); 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)) break; - if (__l2cap_efs_supported(chan)) + if (__l2cap_efs_supported(chan->conn)) set_bit(FLAG_EFS_ENABLE, &chan->flags); /* fall through */ @@ -3317,7 +3320,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) break; case L2CAP_CONF_EWS: - if (!enable_hs) + if (!chan->conn->hs_enabled) return -ECONNREFUSED; 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 (__l2cap_efs_supported(chan)) + if (__l2cap_efs_supported(chan->conn)) set_bit(FLAG_EFS_ENABLE, &chan->flags); else return -ECONNREFUSED; @@ -4303,7 +4306,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, if (!disable_ertm) feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING | L2CAP_FEAT_FCS; - if (enable_hs) + if (conn->hs_enabled) feat_mask |= L2CAP_FEAT_EXT_FLOW | L2CAP_FEAT_EXT_WINDOW; @@ -4314,7 +4317,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, u8 buf[12]; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; - if (enable_hs) + if (conn->hs_enabled) l2cap_fixed_chan[0] |= L2CAP_FC_A2MP; else l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP; @@ -4411,7 +4414,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, if (cmd_len != sizeof(*req)) return -EPROTO; - if (!enable_hs) + if (!conn->hs_enabled) return -EINVAL; psm = le16_to_cpu(req->psm); @@ -4838,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); - if (!enable_hs) + if (!conn->hs_enabled) return -EINVAL; chan = l2cap_get_chan_by_dcid(conn, icid); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index c85537ca1a23..9119898ef040 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -445,11 +445,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; case BT_CHANNEL_POLICY: - if (!enable_hs) { - err = -ENOPROTOOPT; - break; - } - if (put_user(chan->chan_policy, (u32 __user *) optval)) err = -EFAULT; break; @@ -720,11 +715,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; case BT_CHANNEL_POLICY: - if (!enable_hs) { - err = -ENOPROTOOPT; - break; - } - if (get_user(opt, (u32 __user *) optval)) { err = -EFAULT; break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1b5b10fab545..dd15491f2374 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -32,8 +32,6 @@ #include #include -bool enable_hs; - #define MGMT_VERSION 1 #define MGMT_REVISION 3 @@ -380,10 +378,8 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_DISCOVERABLE; settings |= MGMT_SETTING_BREDR; settings |= MGMT_SETTING_LINK_SECURITY; - } - - if (enable_hs) settings |= MGMT_SETTING_HS; + } if (lmp_le_capable(hdev)) { settings |= MGMT_SETTING_LE; @@ -1344,7 +1340,7 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) BT_DBG("request for %s", hdev->name); - if (!enable_hs) + if (!lmp_bredr_capable(hdev)) return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, MGMT_STATUS_NOT_SUPPORTED); @@ -4396,6 +4392,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), cmd ? cmd->sk : NULL); } - -module_param(enable_hs, bool, 0644); -MODULE_PARM_DESC(enable_hs, "Enable High Speed support"); -- cgit v1.2.3 From 56f8790102f48a4959a729ecdccff332591014e1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Oct 2013 13:43:13 +0300 Subject: Bluetooth: Introduce a new HCI_BREDR_ENABLED flag To allow treating dual-mode (BR/EDR/LE) controllers as single-mode ones (LE-only) we want to introduce a new HCI_BREDR_ENABLED flag to track whether BR/EDR is enabled or not (previously we simply looked at the feature bit with lmp_bredr_enabled). This patch add the new flag and updates the relevant places to test against it instead of using lmp_bredr_enabled. The flag is by default enabled when registering an adapter and only cleared if necessary once the local features have been read during the HCI init procedure. We cannot completely block BR/EDR usage in case user space uses raw HCI sockets but the patch tries to block this in places where possible, such as the various BR/EDR specific ioctls. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_conn.c | 3 +++ net/bluetooth/hci_core.c | 21 +++++++++++++++++++-- net/bluetooth/mgmt.c | 24 +++++++++++++----------- 4 files changed, 36 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7ede2666dc75..4fa08d7b997d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -122,6 +122,7 @@ enum { HCI_LINK_SECURITY, HCI_PERIODIC_INQ, HCI_FAST_CONNECTABLE, + HCI_BREDR_ENABLED, }; /* A mask for the flags that are supposed to remain when a reset happens diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index d2380e0c7df0..514148b7a66b 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -581,6 +581,9 @@ static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, { 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); if (!acl) { acl = hci_conn_add(hdev, ACL_LINK, dst); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7cbdd33d9b38..14df032543b2 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -519,6 +519,8 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) if (lmp_bredr_capable(hdev)) bredr_setup(req); + else + clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); if (lmp_le_capable(hdev)) le_setup(req); @@ -1034,6 +1036,11 @@ int hci_inquiry(void __user *arg) goto done; } + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + err = -EOPNOTSUPP; + goto done; + } + hci_dev_lock(hdev); if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX || inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) { @@ -1101,7 +1108,7 @@ static u8 create_ad(struct hci_dev *hdev, u8 *ptr) if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) flags |= LE_AD_GENERAL; - if (!lmp_bredr_capable(hdev)) + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) flags |= LE_AD_NO_BREDR; if (lmp_le_br_capable(hdev)) @@ -1493,6 +1500,11 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) goto done; } + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + err = -EOPNOTSUPP; + goto done; + } + switch (cmd) { case HCISETAUTH: err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt, @@ -2318,8 +2330,13 @@ int hci_register_dev(struct hci_dev *hdev) 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); + /* 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); list_add(&hdev->list, &hci_dev_list); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ad3862949a2c..e1c41b0b7a75 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -408,7 +408,7 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_PAIRABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_PAIRABLE; - if (lmp_bredr_capable(hdev)) + if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_BREDR; if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) @@ -929,7 +929,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); - if (!lmp_bredr_capable(hdev)) + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_NOT_SUPPORTED); @@ -1085,7 +1085,7 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); - if (!lmp_bredr_capable(hdev)) + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, MGMT_STATUS_NOT_SUPPORTED); @@ -1208,7 +1208,7 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); - if (!lmp_bredr_capable(hdev)) + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, MGMT_STATUS_NOT_SUPPORTED); @@ -1342,7 +1342,7 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) BT_DBG("request for %s", hdev->name); - if (!lmp_bredr_capable(hdev)) + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, MGMT_STATUS_NOT_SUPPORTED); @@ -1409,7 +1409,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) MGMT_STATUS_INVALID_PARAMS); /* 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, MGMT_STATUS_REJECTED); @@ -1720,7 +1720,7 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); - if (!lmp_bredr_capable(hdev)) + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, MGMT_STATUS_NOT_SUPPORTED); @@ -2803,7 +2803,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, switch (hdev->discovery.type) { case DISCOV_TYPE_BREDR: - if (!lmp_bredr_capable(hdev)) { + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, MGMT_STATUS_NOT_SUPPORTED); mgmt_pending_remove(cmd); @@ -2835,7 +2835,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, } 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, MGMT_STATUS_NOT_SUPPORTED); mgmt_pending_remove(cmd); @@ -3282,7 +3282,8 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, 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, MGMT_STATUS_NOT_SUPPORTED); @@ -3646,7 +3647,8 @@ static int powered_update_hci(struct hci_dev *hdev) sizeof(link_sec), &link_sec); 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_name(&req); update_eir(&req); -- cgit v1.2.3 From 0663ca2a032eea12480a8f86fe08bef9d72f8faf Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Oct 2013 13:43:14 +0300 Subject: Bluetooth: Add a new mgmt_set_bredr command This patch introduces a new mgmt command for enabling/disabling BR/EDR functionality. This can be convenient when one wants to make a dual-mode controller behave like a single-mode one. The command is only available for dual-mode controllers and requires that LE is enabled before using it. The BR/EDR setting can be enabled at any point, however disabling it requires the controller to be powered off (otherwise a "rejected" response will be sent). Disabling the BR/EDR setting will automatically disable all other BR/EDR related settings. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 2 + net/bluetooth/hci_event.c | 5 ++ net/bluetooth/mgmt.c | 120 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 421d7633a91f..7347df800a2e 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -354,6 +354,8 @@ struct mgmt_cp_set_device_id { #define MGMT_OP_SET_ADVERTISING 0x0029 +#define MGMT_OP_SET_BREDR 0x002A + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d171c04bddbd..4785ab0795f5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -297,6 +297,11 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) 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_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e1c41b0b7a75..dcce0cf1d7cc 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -75,6 +75,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_UNBLOCK_DEVICE, MGMT_OP_SET_DEVICE_ID, MGMT_OP_SET_ADVERTISING, + MGMT_OP_SET_BREDR, }; static const u16 mgmt_events[] = { @@ -3337,6 +3338,121 @@ unlock: 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) { if (key->authenticated != 0x00 && key->authenticated != 0x01) @@ -3452,6 +3568,7 @@ static const struct mgmt_handler { { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE }, { set_device_id, false, MGMT_SET_DEVICE_ID_SIZE }, { set_advertising, false, MGMT_SETTING_SIZE }, + { set_bredr, false, MGMT_SETTING_SIZE }, }; @@ -3633,6 +3750,9 @@ static int powered_update_hci(struct hci_dev *hdev) cp.simul != lmp_host_le_br_capable(hdev)) hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); + + /* In case BR/EDR was toggled during the AUTO_OFF phase */ + hci_update_ad(&req); } if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) { -- cgit v1.2.3 From d13eafce2c892d57f1eb243e43dfe48b4626006d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Oct 2013 04:41:30 -0700 Subject: Bluetooth: Add management command for setting static address On dual-mode BR/EDR/LE and LE only controllers it is possible to configure a random address. There are two types or random addresses, one is static and the other private. Since the random private addresses require special privacy feature to be supported, the configuration of these two are kept separate. This command allows for setting the static random address. It is only supported on controllers with LE support. The static random address is suppose to be valid for the lifetime of the controller or at least until the next power cycle. To ensure such behavior, setting of the address is limited to when the controller is powered off. The special BDADDR_ANY address (00:00:00:00:00:00) can be used to disable the static address. This is also the default value. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 2 ++ include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt.h | 6 +++++ net/bluetooth/mgmt.c | 49 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4fa08d7b997d..d7fd825ed2ce 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -978,6 +978,8 @@ struct hci_rp_le_read_local_features { __u8 features[8]; } __packed; +#define HCI_OP_LE_SET_RANDOM_ADDR 0x2005 + #define HCI_OP_LE_READ_ADV_TX_POWER 0x2007 struct hci_rp_le_read_adv_tx_power { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 26cc9f7858cd..e09c30577b3a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -140,6 +140,7 @@ struct hci_dev { __u8 bus; __u8 dev_type; bdaddr_t bdaddr; + bdaddr_t static_addr; __u8 dev_name[HCI_MAX_NAME_LENGTH]; __u8 short_name[HCI_MAX_SHORT_NAME_LENGTH]; __u8 eir[HCI_MAX_EIR_LENGTH]; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 7347df800a2e..2ad433bb9a2e 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -356,6 +356,12 @@ struct mgmt_cp_set_device_id { #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 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4ac31695946b..b87163238c10 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -76,6 +76,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_DEVICE_ID, MGMT_OP_SET_ADVERTISING, MGMT_OP_SET_BREDR, + MGMT_OP_SET_STATIC_ADDRESS, }; static const u16 mgmt_events[] = { @@ -3247,6 +3248,46 @@ unlock: 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) { struct pending_cmd *cmd; @@ -3576,6 +3617,7 @@ static const struct mgmt_handler { { 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 }, }; @@ -3762,6 +3804,13 @@ static int powered_update_hci(struct hci_dev *hdev) 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; -- cgit v1.2.3 From d0bf75a51b172fdd9dd90bfa03c0f5de473b6c94 Mon Sep 17 00:00:00 2001 From: DoHyun Pyun Date: Wed, 2 Oct 2013 21:54:46 +0900 Subject: Bluetooth: Add the definition and structure for Set Reserved LT_ADDR The Set_Reserved_LT_ADDR command allows the host to request that the BR/EDR Controller reserve a specific LT_ADDR for Connectionless Slave Broadcast. The Core Spec Addendum 4 adds this command in part B Connectionless Slave Broadcast. Bluetooth Core Specification Addendum 4 - Page 90 "7.3.86 Set Reserved LT_ADDR Command [New Section] ... If the LT_ADDR indicated in the LT_ADDR parameter is already in use by the BR/EDR Controller, it shall return the ACL Connection Already Exists (0x0B) error code. If the LT_ADDR indicated in the LT_ADDR parameter is out of range, the controller shall return the Invalid HCI Command Parameters (0x12) error code. If the command succeeds, then the reserved LT_ADDR shall be used when issuing subsequent Set Connectionless Slave Broadcast Data and Set Connectionless Slave Broadcast commands. To ensure that the reserved LT_ADDR is not already allocated, it is recommended that this command be issued at some point after HCI_Reset is issued but before page scanning is enabled or paging is initiated." Signed-off-by: Dohyun Pyun Signed-off-by: C S Bhargava Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d7fd825ed2ce..4d88809da3f6 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -839,6 +839,15 @@ struct hci_cp_write_le_host_supported { __u8 simul; } __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_READ_SYNC_TRAIN_PARAMS 0x0c77 #define HCI_OP_READ_LOCAL_VERSION 0x1001 -- cgit v1.2.3 From 6a20eaf40419481dd55031ffe7a856e7a304ca4d Mon Sep 17 00:00:00 2001 From: DoHyun Pyun Date: Wed, 2 Oct 2013 21:54:47 +0900 Subject: Bluetooth: Add the definition and structure for Delete Reserved LT_ADDR The Delete_Reserved_LT_ADDR command requests that the BR/EDR Controller cancel the reservation for a specific LT_ADDR reserved for the purposes of Connectionless Slave Broadcast. The Core Spec Addendum 4 adds this command in part B Connectionless Slave Broadcast. Bluetooth Core Specification Addendum 4 - Page 92 "7.3.87 Delete Reserved LT_ADDR Command [New Section] ... If the LT_ADDR indicated in the LT_ADDR parameter is not reserved by the BR/EDR Controller, it shall return the Unknown Connection Identifier (0x02) error code. If connectionless slave broadcast mode is still active, then the Controller shall return the Command Disallowed (0x0C) error code." Signed-off-by: Dohyun Pyun Signed-off-by: C S Bhargava Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4d88809da3f6..9b071f162dde 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -848,6 +848,15 @@ struct hci_rp_set_reserved_lt_addr { __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_READ_SYNC_TRAIN_PARAMS 0x0c77 #define HCI_OP_READ_LOCAL_VERSION 0x1001 -- cgit v1.2.3 From 7d1dab49f645557bb0b9246f7ae87fafd2716e70 Mon Sep 17 00:00:00 2001 From: DoHyun Pyun Date: Wed, 2 Oct 2013 21:54:48 +0900 Subject: Bluetooth: Add the definition and structure for Set CSB Data The Set_Connectionless_Slave_Broadcast_Data command provides the ability for the Host to set Connectionless Slave Broadcast data in the BR/EDR Controller. The Core Spec Addendum 4 adds this command in part B Connectionless Slave Broadcast. Bluetooth Core Specification Addendum 4 - Page 93 "7.3.88 Set Connectionless Slave Broadcast Data Command [New Section] ... If connectionless slave broadcast mode is disabled, this data shall be kept by the BR/EDR Controller and used once connectionless slave broadcast mode is enabled. If connectionless slave broadcast mode is enabled, and this command is successful, this data will be sent starting with the next Connectionless Slave Broadcast instant. The Data_Length field may be zero, in which case no data needs to be provided. The Host may fragment the data using the Fragment field in the command. If the combined length of the fragments exceeds the capacity of the largest allowed packet size specified in the Set Connectionless Slave Broadcast command, all fragments associated with the data being assembled shall be discarded and the Invalid HCI Command Parameters error (0x12) shall be returned." Signed-off-by: Dohyun Pyun Signed-off-by: C S Bhargava Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 9b071f162dde..d9e0a8467ddc 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -35,6 +35,8 @@ #define HCI_MAX_AMP_ASSOC_SIZE 672 +#define HCI_MAX_CSB_DATA_SIZE 252 + /* HCI dev events */ #define HCI_DEV_REG 1 #define HCI_DEV_UNREG 2 @@ -857,6 +859,18 @@ struct hci_rp_delete_reserved_lt_addr { __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_READ_LOCAL_VERSION 0x1001 -- cgit v1.2.3 From a9b07a643f16332c4cc96259ef5cad2163f8e28a Mon Sep 17 00:00:00 2001 From: DoHyun Pyun Date: Wed, 2 Oct 2013 21:54:49 +0900 Subject: Bluetooth: Add the structure for Write Sync Train Parameters The Write_Synchronization_Train_Parameters command configures the Synchronization Train functionality in the BR/EDR Controller. The Core Spec Addendum 4 adds this command in part B Connectionless Slave Broadcast. Bluetooth Core Specification Addendum 4 - Page 97 "7.3.90 Write Synchronization Train Parameters Command [New Section] ... Note: The AFH_Channel_Map used in the Synchronization Train packets is configured by the Set_AFH_Channel_Classification command and the local channel classification in the BR/EDR Controller. Interval_Min and Interval_Max specify the allowed range of Sync_Train_Interval. Refer to [Vol. 2], Part B, section 2.7.2 for a detailed description of Sync_Train_Interval. The BR/EDR Controller shall select an interval from this range and return it in Sync_Train_Interval. If the Controller is unable to select a value from this range, it shall return the Invalid HCI Command Parameters (0x12) error code. Once started (via the Start_Synchronization_Train Command) the Synchronization Train will continue until synchronization_trainTO slots have passed or Connectionless Slave Broadcast has been disabled." Signed-off-by: Dohyun Pyun Signed-off-by: C S Bhargava Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d9e0a8467ddc..cad6ca121461 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -873,6 +873,18 @@ struct hci_rp_set_csb_data { #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 struct hci_rp_read_local_version { __u8 status; -- cgit v1.2.3 From 8c9a041be2b8d534e770594a5e8d3251cc221bd1 Mon Sep 17 00:00:00 2001 From: DoHyun Pyun Date: Wed, 2 Oct 2013 21:54:50 +0900 Subject: Bluetooth: Add the definition and structure for Set CSB he Set_Connectionless_Slave_Broadcast command controls the Connectionless Slave Broadcast functionality in the BR/EDR Controller. The Core Spec Addendum 4 adds this command in part B Connectionless Slave Broadcast. Bluetooth Core Specification Addendum 4 - Page 78 "7.1.49 Set Connectionless Slave Broadcast Command [New Section] ... The LT_ADDR indicated in the Set_Connectionless_Slave_Broadcast shall be pre-allocated using the HCI_Set_Reserved_LT_ADDR command. If the LT_ADDR has not been reserved, the Unknown Connection Identifier (0x02) error code shall be returned. If the controller is unable to reserve sufficient bandwidth for the requested activity, the Connection Rejected Due to Limited Resources (0x0D) error code shall be returned. The LPO_Allowed parameter informs the BR/EDR Controller whether it is allowed to sleep. The Packet_Type parameter specifies which packet types are allowed. The Host shall either enable BR packet types only, or shall enable EDR and DM1 packet types only. The Interval_Min and Interval_Max parameters specify the range from which the BR/EDR Controller must select the Connectionless Slave Broadcast Interval. The selected Interval is returned." Signed-off-by: Dohyun Pyun Signed-off-by: C S Bhargava Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index cad6ca121461..42d3832f0602 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -628,6 +628,22 @@ struct hci_rp_logical_link_cancel { __u8 flow_spec_id; } __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_SNIFF_MODE 0x0803 struct hci_cp_sniff_mode { __le16 handle; -- cgit v1.2.3 From cefded981960d60f7d18f6596c020390b9764aa3 Mon Sep 17 00:00:00 2001 From: DoHyun Pyun Date: Wed, 2 Oct 2013 21:54:51 +0900 Subject: Bluetooth: Add the definition for Start Synchronization Train The Start_Synchronization_Train command controls the Synchronization Train functionality in the BR/EDR Controller. The Core Spec Addendum 4 adds this command in part B Connectionless Slave Broadcast. Bluetooth Core Specification Addendum 4 - Page 86 "7.1.51 Start Synchronization Train Command [New Section] ... If connectionless slave broadcast mode is not enabled, the Command Disallowed (0x0C) error code shall be returned. After receiving this command and returning a Command Status event, the Baseband starts attempting to send synchronization train packets containing information related to the enabled Connectionless Slave Broadcast packet timing. Note: The AFH_Channel_Map used in the synchronization train packets is configured by the Set_AFH_Channel_Classification command and the local channel classification in the BR/EDR Controller. The synchronization train packets will be sent using the parameters specified by the latest Write_Synchronization_Train_Parameters command. The Synchronization Train will continue until synchronization_trainTO slots (as specified in the last Write_Synchronization_Train command) have passed or until the Host disables the Connectionless Slave Broadcast logical transport." Signed-off-by: Dohyun Pyun Signed-off-by: C S Bhargava Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 42d3832f0602..657d2b09e3fe 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -644,6 +644,8 @@ struct hci_rp_set_csb { __le16 interval; } __packed; +#define HCI_OP_START_SYNC_TRAIN 0x0443 + #define HCI_OP_SNIFF_MODE 0x0803 struct hci_cp_sniff_mode { __le16 handle; -- cgit v1.2.3 From 2b359445d5578f65cdd5301dfcbf9e0bdc358b20 Mon Sep 17 00:00:00 2001 From: DoHyun Pyun Date: Wed, 2 Oct 2013 21:54:52 +0900 Subject: Bluetooth: Add the definition and stcuture for Sync Train Complete The Synchronization Train Complete event indicates that the Start Synchronization Train command has completed. The Core Spec Addendum 4 adds this command in part B Connectionless Slave Broadcast. Bluetooth Core Specification Addendum 4 - Page 103 "7.7.67 Synchronization Train Complete Event [New Section] ... Event Parameters: Status 0x00 Start Synchronization Train command completed successfully. 0x01-0xFF Start Synchronization Train command failed. See Part D, Error Codes, for error codes and descriptions." Signed-off-by: Dohyun Pyun Signed-off-by: C S Bhargava Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 657d2b09e3fe..03f2a9126a5d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1505,6 +1505,11 @@ struct hci_ev_num_comp_blocks { struct hci_comp_blocks_info handles[0]; } __packed; +#define HCI_EV_SYNC_TRAIN_COMPLETE 0x4F +struct hci_ev_sync_train_complete { + __u8 status; +} __packed; + /* Low energy meta events */ #define LE_CONN_ROLE_MASTER 0x00 -- cgit v1.2.3 From 2ed01805ee439056f4e1fe182846c029e0a08e49 Mon Sep 17 00:00:00 2001 From: DoHyun Pyun Date: Wed, 2 Oct 2013 21:54:53 +0900 Subject: Bluetooth: Add the definition for Slave Page Response Timeout The Slave Page Response Timeout event indicates to the Host that a slave page response timeout has occurred in the BR/EDR Controller. The Core Spec Addendum 4 adds this command in part B Connectionless Slave Broadcast. Bluetooth Core Specification Addendum 4 - Page 110 "7.7.72 Slave Page Response Timeout Event [New Section] ... Note: this event will be generated if the slave BR/EDR Controller responds to a page but does not receive the master FHS packet (see Baseband, Section 8.3.3) within pagerespTO. Event Parameters: NONE" Signed-off-by: Dohyun Pyun Signed-off-by: C S Bhargava Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 03f2a9126a5d..b90eec5e9c06 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1510,6 +1510,8 @@ struct hci_ev_sync_train_complete { __u8 status; } __packed; +#define HCI_EV_SLAVE_PAGE_RESP_TIMEOUT 0x54 + /* Low energy meta events */ #define LE_CONN_ROLE_MASTER 0x00 -- cgit v1.2.3